void* new_break = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(initial_break) + 1); int ret = brk(new_break); if (ret == -1) {
ASSERT_ERRNO(ENOMEM);
} else {
ASSERT_EQ(0, ret);
ASSERT_GE(get_brk(), new_break);
}
// Expand by a full page to force the mapping to expand
new_break = page_align(reinterpret_cast<uintptr_t>(initial_break) + sysconf(_SC_PAGE_SIZE));
ret = brk(new_break); if (ret == -1) {
ASSERT_ERRNO(ENOMEM);
} else {
ASSERT_EQ(0, ret);
ASSERT_EQ(get_brk(), new_break);
}
}
TEST(UNISTD_TEST, sbrk_ENOMEM) { #ifdefined(__BIONIC__) && !defined(__LP64__) // There is no way to guarantee that all overflow conditions can be tested // without manipulating the underlying values of the current break. externvoid* __bionic_brk;
uintptr_t cur_brk = reinterpret_cast<uintptr_t>(get_brk()); if (cur_brk < static_cast<uintptr_t>(-(SBRK_MIN+1))) { // Do the overflow test for a max negative increment.
ASSERT_EQ(reinterpret_cast<void*>(-1), sbrk(SBRK_MIN)); #ifdefined(__BIONIC__) // GLIBC does not set errno in overflow case.
ASSERT_ERRNO(ENOMEM); #endif
}
uintptr_t overflow_brk = static_cast<uintptr_t>(SBRK_MAX) + 2; if (cur_brk < overflow_brk) { // Try and move the value to PTRDIFF_MAX + 2.
cur_brk = reinterpret_cast<uintptr_t>(sbrk(overflow_brk));
} if (cur_brk >= overflow_brk) {
ASSERT_EQ(reinterpret_cast<void*>(-1), sbrk(SBRK_MAX)); #ifdefined(__BIONIC__) // GLIBC does not set errno in overflow case.
ASSERT_ERRNO(ENOMEM); #endif
} #endif
}
TEST(UNISTD_TEST, read_EBADF) { // read returns ssize_t which is 64-bits on LP64, so it's worth explicitly checking that // our syscall stubs correctly return a 64-bit -1. char buf[1];
ASSERT_ERRNO_FAILURE(EBADF, -1, read(-1, buf, sizeof(buf)));
}
TEST(UNISTD_TEST, syscall_long) { // Check that syscall(3) correctly returns long results. // https://code.google.com/p/android/issues/detail?id=73952 // We assume that the break is > 4GiB, but this is potentially flaky.
uintptr_t p = reinterpret_cast<uintptr_t>(sbrk(0));
ASSERT_EQ(p, static_cast<uintptr_t>(syscall(__NR_brk, 0)));
}
// putenv() stores the pointer directly in environ, // so later changes to the passed-in string change the environment variable. // This is a terrible idea, but we have to support it.
s[4] = '2';
ASSERT_STREQ("2", getenv("foo"));
}
TEST(UNISTD_TEST, setenv_no_mutation) { char* name = strdup("foo"); char* value = strdup("1");
ASSERT_EQ(0, setenv(name, value, 1));
// After clearenv(), environ is null...
ASSERT_EQ(nullptr, environ); // ...and pointers _in_ the old environ have been nulled too.
ASSERT_EQ(nullptr, old_environ[0]);
ASSERT_EQ(nullptr, old_environ[1]);
}
// It's safe to call clearenv() if environ is already null.
TEST(UNISTD_TEST, clearenv_null_environ) { externchar** environ;
environ = nullptr;
ASSERT_EQ(0, clearenv());
}
TEST(UNISTD_TEST, environ_concurrency) { // Number of rounds chosen by experiment to reliably ensure crashes // with implementations without locking. // 1k was flaky, so this an order of magnitude larger. // Note that this test crashes for lack of _write_ locks --- // it catches cases where putenv() and setenv() both reallocate at // the same time, but does not catch cases where getenv() is iterating // over freed memory unless you have hwasan or MTE. static constexpr size_t N = 10'000;
std::thread getenv_thread{[]() { for (size_t i = 0; i < N; ++i) { // Deliberately not one of the variables we're setting, // so getenv() is forced to traverse the whole environ array.
android::base::DoNotOptimize(getenv("FOO"));
}
}}; // Both our mutator threads use a unique variable name every time // to ensure that the existing environ array needs to be reallocated.
std::thread putenv_thread{[]() { for (size_t i = 0; i < N; ++i) { char assignment[128];
snprintf(assignment, sizeof(assignment), "PUTENV%zu=%zu", i, i);
android::base::DoNotOptimize(putenv(strdup(assignment)));
}
}};
std::thread setenv_thread{[]() { for (size_t i = 0; i < N; ++i) { char name[128];
snprintf(name, sizeof(name), "SETENV%zu", i);
android::base::DoNotOptimize(setenv(name, "123", 1));
}
}};
getenv_thread.join();
putenv_thread.join();
setenv_thread.join();
}
staticvoid TestSyncFunction(int (*fn)(int)) { int fd;
// Can't sync an invalid fd.
EXPECT_ERRNO_FAILURE(EBADF, -1, fn(-1));
// It doesn't matter whether you've opened a file for write or not.
TemporaryFile tf;
ASSERT_NE(-1, tf.fd);
// But some file systems are fussy about fsync/fdatasync...
errno = 0; int fd = open("/proc/version", O_RDONLY);
ASSERT_NE(-1, fd);
EXPECT_ERRNO_FAILURE(EINVAL, -1, fn(fd));
close(fd);
}
pid_t rc = vfork();
ASSERT_NE(-1, rc); if (rc == 0) { if (self->get_cached_pid(&cached_pid)) { constchar* error = "__get_thread()->cached_pid_ set after vfork\n";
write(STDERR_FILENO, error, strlen(error));
_exit(1);
}
if (!self->is_vforked()) { constchar* error = "__get_thread()->vforked_ not set after vfork\n";
write(STDERR_FILENO, error, strlen(error));
_exit(1);
}
staticvoid AssertGetPidCorrect() { // The loop is just to make manual testing/debugging with strace easier.
pid_t getpid_syscall_result = syscall(__NR_getpid); for (size_t i = 0; i < 128; ++i) {
ASSERT_EQ(getpid_syscall_result, getpid());
}
}
// gettid() is marked as __attribute_const__, which will have the compiler // optimize out multiple calls to gettid in the same function. This wrapper // defeats that optimization. static __attribute__((__noinline__)) pid_t GetTidForTest() {
__asm__(""); return gettid();
}
staticvoid AssertGetTidCorrect() { // The loop is just to make manual testing/debugging with strace easier.
pid_t gettid_syscall_result = syscall(__NR_gettid); for (size_t i = 0; i < 128; ++i) {
ASSERT_EQ(gettid_syscall_result, GetTidForTest());
}
}
__attribute__((noinline)) staticvoid HwasanVforkTestChild() { // Allocate a tagged region on stack and leave it there. char x[10000];
android::base::DoNotOptimize(x);
_exit(0);
}
__attribute__((noinline)) staticvoid HwasanReadMemory(constchar* p, size_t size) { // Read memory byte-by-byte. This will blow up if the pointer tag in p does not match any memory // tag in [p, p+size). char z; for (size_t i = 0; i < size; ++i) {
android::base::DoNotOptimize(z = p[i]);
}
}
__attribute__((noinline, no_sanitize("hwaddress"))) staticvoid HwasanVforkTestParent() { // Allocate a region on stack, but don't tag it (see the function attribute). // This depends on unallocated stack space at current function entry being untagged. char x[10000];
android::base::DoNotOptimize(x); // Verify that contents of x[] are untagged.
HwasanReadMemory(x, sizeof(x));
}
TEST(UNISTD_TEST, hwasan_vfork) { // Test hwasan annotation in vfork. This test is only interesting when built with hwasan, but it // is supposed to work correctly either way. if (vfork()) {
HwasanVforkTestParent();
} else {
HwasanVforkTestChild();
}
}
TEST(UNISTD_TEST, sethostname) { // The permissions check happens before the argument check, so this will // fail for a different reason if you're running as root than if you're // not, but it'll fail either way. Checking that we have the symbol is about // all we can do for sethostname(2).
ASSERT_EQ(-1, sethostname("", -1));
}
// Do we correctly detect truncation?
ASSERT_ERRNO_FAILURE(ENAMETOOLONG, -1, gethostname(hostname, strlen(hostname)));
}
TEST(UNISTD_TEST, pathconf_fpathconf) {
TemporaryFile tf; long l;
// As a file system's block size is always power of 2, the configure values // for ALLOC and XFER should be power of 2 as well.
l = pathconf(tf.path, _PC_ALLOC_SIZE_MIN);
ASSERT_TRUE(l > 0 && powerof2(l));
l = pathconf(tf.path, _PC_REC_MIN_XFER_SIZE);
ASSERT_TRUE(l > 0 && powerof2(l));
l = pathconf(tf.path, _PC_REC_XFER_ALIGN);
ASSERT_TRUE(l > 0 && powerof2(l));
l = fpathconf(tf.fd, _PC_ALLOC_SIZE_MIN);
ASSERT_TRUE(l > 0 && powerof2(l));
l = fpathconf(tf.fd, _PC_REC_MIN_XFER_SIZE);
ASSERT_TRUE(l > 0 && powerof2(l));
l = fpathconf(tf.fd, _PC_REC_XFER_ALIGN);
ASSERT_TRUE(l > 0 && powerof2(l));
// Check that the "I can't answer that, you'll have to try it and see" // cases don't set errno. int names[] = {
_PC_ASYNC_IO, _PC_PRIO_IO, _PC_REC_INCR_XFER_SIZE, _PC_REC_MAX_XFER_SIZE, _PC_SYMLINK_MAX,
_PC_SYNC_IO, -1}; for (size_t i = 0; names[i] != -1; i++) {
errno = 0;
ASSERT_EQ(-1, pathconf(tf.path, names[i])) << names[i];
ASSERT_ERRNO(0) << names[i];
ASSERT_EQ(-1, fpathconf(tf.fd, names[i])) << names[i];
ASSERT_ERRNO(0) << names[i];
}
}
TEST(UNISTD_TEST, _POSIX_constants) { // Make a tight verification of _POSIX_* / _POSIX2_* / _XOPEN_* macros, to prevent change by mistake. // Verify according to POSIX.1-2008.
EXPECT_EQ(200809L, _POSIX_VERSION);
#ifdefined(__BIONIC__) // These tests only pass on bionic, as bionic and glibc has different support on these macros. // Macros like _POSIX_ASYNCHRONOUS_IO are not supported on bionic yet.
EXPECT_EQ(-1, _POSIX_ASYNCHRONOUS_IO);
EXPECT_EQ(-1, _POSIX_MESSAGE_PASSING);
EXPECT_EQ(-1, _POSIX_PRIORITIZED_IO);
EXPECT_EQ(-1, _POSIX_SHARED_MEMORY_OBJECTS);
EXPECT_EQ(-1, _POSIX_THREAD_PRIO_INHERIT);
EXPECT_EQ(-1, _POSIX_THREAD_PRIO_PROTECT);
EXPECT_EQ(-1, _POSIX_THREAD_ROBUST_PRIO_INHERIT);
// sysconf() means unlimited when it returns -1 with errno unchanged. #define VERIFY_SYSCONF_POSITIVE(name) \
VerifySysconf(name, #name, [](long v){return (v > 0 || v == -1) && errno == 0;})
#ifdefined(__LP64__)
VERIFY_SYSCONF_UNSUPPORTED(_SC_V7_ILP32_OFF32);
VERIFY_SYSCONF_UNSUPPORTED(_SC_V7_ILP32_OFFBIG);
VERIFY_SYSCONF_POSITIVE(_SC_V7_LP64_OFF64);
VERIFY_SYSCONF_POSITIVE(_SC_V7_LPBIG_OFFBIG); #else
VERIFY_SYSCONF_POSITIVE(_SC_V7_ILP32_OFF32); #ifdefined(__BIONIC__) // bionic does not support 64 bits off_t type on 32bit machine.
VERIFY_SYSCONF_UNSUPPORTED(_SC_V7_ILP32_OFFBIG); #endif
VERIFY_SYSCONF_UNSUPPORTED(_SC_V7_LP64_OFF64);
VERIFY_SYSCONF_UNSUPPORTED(_SC_V7_LPBIG_OFFBIG); #endif
#ifdefined(__BIONIC__) // Tests can only run on bionic, as bionic and glibc have different support for these options. // Below options are not supported on bionic yet.
VERIFY_SYSCONF_UNSUPPORTED(_SC_ASYNCHRONOUS_IO);
VERIFY_SYSCONF_UNSUPPORTED(_SC_MESSAGE_PASSING);
VERIFY_SYSCONF_UNSUPPORTED(_SC_PRIORITIZED_IO);
VERIFY_SYSCONF_UNSUPPORTED(_SC_SHARED_MEMORY_OBJECTS);
VERIFY_SYSCONF_UNSUPPORTED(_SC_THREAD_ROBUST_PRIO_INHERIT);
VERIFY_SYSCONF_UNSUPPORTED(_SC_THREAD_ROBUST_PRIO_PROTECT);
// Get our current limit, and set things up so we restore the limit.
rlimit rl;
ASSERT_EQ(0, getrlimit(RLIMIT_STACK, &rl));
uint64_t original_rlim_cur = rl.rlim_cur; if (rl.rlim_cur == RLIM_INFINITY) {
rl.rlim_cur = 8 * 1024 * 1024; // Bionic reports unlimited stacks as 8MiB.
} auto guard = android::base::make_scope_guard([&rl, original_rlim_cur]() {
rl.rlim_cur = original_rlim_cur;
ASSERT_EQ(0, setrlimit(RLIMIT_STACK, &rl));
});
// _SC_ARG_MAX should be 1/4 the stack size.
EXPECT_EQ(static_cast<long>(rl.rlim_cur / 4), sysconf(_SC_ARG_MAX));
// If you have a really small stack, the kernel still guarantees a stack // expansion of 128KiB (see setup_arg_pages() in fs/exec.c).
rl.rlim_cur = 1024;
rl.rlim_max = RLIM_INFINITY;
ASSERT_EQ(0, setrlimit(RLIMIT_STACK, &rl));
// If you have a large stack, the kernel will keep the stack // expansion to 128KiB (see setup_arg_pages() in fs/exec.c).
rl.rlim_cur = 524288;
rl.rlim_max = RLIM_INFINITY;
ASSERT_EQ(0, setrlimit(RLIMIT_STACK, &rl));
[[maybe_unused]] staticvoid show_cache(constchar* name, long size, long assoc, long line_size) {
printf("%s cache size: %ld bytes, line size %ld bytes, ", name, size, line_size); if (assoc == 0) {
printf("fully");
} else {
printf("%ld-way", assoc);
}
printf(" associative\n");
}
TEST(UNISTD_TEST, sysconf_cache) { #ifdefined(ANDROID_HOST_MUSL)
GTEST_SKIP() << "musl does not have _SC_LEVEL?_?CACHE_SIZE"; #else // It's not obvious we can _test_ any of these, but we can at least // show the output for humans to inspect.
show_cache("L1D", sysconf(_SC_LEVEL1_DCACHE_SIZE), sysconf(_SC_LEVEL1_DCACHE_ASSOC), sysconf(_SC_LEVEL1_DCACHE_LINESIZE));
show_cache("L1I", sysconf(_SC_LEVEL1_ICACHE_SIZE), sysconf(_SC_LEVEL1_ICACHE_ASSOC), sysconf(_SC_LEVEL1_ICACHE_LINESIZE));
show_cache("L2", sysconf(_SC_LEVEL2_CACHE_SIZE), sysconf(_SC_LEVEL2_CACHE_ASSOC), sysconf(_SC_LEVEL2_CACHE_LINESIZE));
show_cache("L3", sysconf(_SC_LEVEL3_CACHE_SIZE), sysconf(_SC_LEVEL3_CACHE_ASSOC), sysconf(_SC_LEVEL3_CACHE_LINESIZE));
show_cache("L4", sysconf(_SC_LEVEL4_CACHE_SIZE), sysconf(_SC_LEVEL4_CACHE_ASSOC), sysconf(_SC_LEVEL4_CACHE_LINESIZE)); #endif
}
TEST(UNISTD_TEST, dup2_same) { // POSIX says of dup2: // If fildes2 is already a valid open file descriptor ... // [and] fildes is equal to fildes2 ... dup2() shall return // fildes2 without closing it. // This isn't true of dup3(2), so we need to manually implement that.
// Equal and valid. int fd = open("/proc/version", O_RDONLY);
ASSERT_TRUE(fd != -1);
ASSERT_EQ(fd, dup2(fd, fd));
ASSERT_EQ(0, close(fd)); // Check that dup2 didn't close fd.
// Equal, but invalid.
ASSERT_ERRNO_FAILURE(EBADF, -1, dup2(fd, fd));
}
// Lock everything by specifying a size of 0 (meaning "to the end, even if it changes").
ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET));
ASSERT_EQ(0, lockf64(tf.fd, F_LOCK, 0));
// Move the end.
ASSERT_EQ(0, ftruncate(tf.fd, 2*file_size));
// Check that the new section is locked too.
ASSERT_EQ(file_size, lseek64(tf.fd, file_size, SEEK_SET));
ASSERT_EQ(0, lockf64(tf.fd, F_TEST, 2*file_size));
}
// Lock everything, but specifying the range in reverse.
ASSERT_EQ(file_size, lseek64(tf.fd, file_size, SEEK_SET));
ASSERT_EQ(0, lockf64(tf.fd, F_LOCK, -file_size));
// Lock the first half of the file.
ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET));
ASSERT_EQ(0, lockf64(tf.fd, F_LOCK, file_size/2));
// Fork a child process.
pid_t pid = fork();
ASSERT_NE(-1, pid); if (pid == 0) { // Check that the child can lock the other half.
ASSERT_EQ(file_size/2, lseek64(tf.fd, file_size/2, SEEK_SET));
ASSERT_EQ(0, lockf64(tf.fd, F_TLOCK, file_size/2)); // Check that the child cannot lock the first half.
ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET));
ASSERT_ERRNO_FAILURE(EACCES, -1, lockf64(tf.fd, F_TEST, file_size/2)); // Check also that it reports itself as locked.
ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET));
ASSERT_ERRNO_FAILURE(EACCES, -1, lockf64(tf.fd, F_TEST, file_size/2));
_exit(0);
}
AssertChildExited(pid, 0);
// The second half was locked by the child, but the lock disappeared // when the process exited, so check it can be locked now.
ASSERT_EQ(file_size/2, lseek64(tf.fd, file_size/2, SEEK_SET));
ASSERT_EQ(0, lockf64(tf.fd, F_TLOCK, file_size/2));
}
#ifdefined(__BIONIC__) // bionic and glibc have different behaviors when len is too small
ASSERT_ERRNO_FAILURE(EINVAL, -1, getdomainname(buf, strlen(u.domainname))); #endif
}
TEST(UNISTD_TEST, execvpe_failure) {
ExecTestHelper eth;
errno = 0;
ASSERT_EQ(-1, execvpe("this-does-not-exist", eth.GetArgs(), eth.GetEnv())); // Running in CTS we might not even be able to search all directories in $PATH.
ASSERT_TRUE(errno == ENOENT || errno == EACCES) << strerror(errno);
}
// It's not inherently executable.
ASSERT_ERRNO_FAILURE(EACCES, -1, execvpe(basename(tf.path), eth.GetArgs(), eth.GetEnv()));
// Make it executable (and keep it writable because we're going to rewrite it below).
ASSERT_EQ(0, chmod(tf.path, 0777));
// TemporaryFile will have a writable fd, so we can test ETXTBSY while we're here...
ASSERT_ERRNO_FAILURE(ETXTBSY, -1, execvpe(basename(tf.path), eth.GetArgs(), eth.GetEnv()));
// 1. The simplest test: the kernel should handle this.
ASSERT_EQ(0, close(tf.fd));
eth.Run([&]() { execvpe(basename(tf.path), eth.GetArgs(), eth.GetEnv()); }, 0, "script\n");
// 2. Try again without a #!. We should have to handle this ourselves.
ASSERT_TRUE(android::base::WriteStringToFile("echo script\n", tf.path));
eth.Run([&]() { execvpe(basename(tf.path), eth.GetArgs(), eth.GetEnv()); }, 0, "script\n");
// 3. Again without a #!, but also with a leading '/', since that's a special case in the // implementation.
eth.Run([&]() { execvpe(tf.path, eth.GetArgs(), eth.GetEnv()); }, 0, "script\n");
}
TEST(UNISTD_TEST, exec_argv0_null) { // http://b/33276926 and http://b/227498625. // // With old kernels, bionic will see the null pointer and use "<unknown>" but // with new (5.18+) kernels, the kernel will already have substituted the // empty string, so we don't make any assertion here about what (if anything) // comes before the first ':'. // // If this ever causes trouble, we could change bionic to replace _either_ the // null pointer or the empty string. We could also use the actual name from // readlink() on /proc/self/exe if we ever had reason to disallow programs // from trying to hide like this. char* args[] = {nullptr}; char* envs[] = {nullptr};
ASSERT_EXIT(execve("/system/bin/run-as", args, envs), testing::ExitedWithCode(1), ": usage: run-as");
}
TEST(UNISTD_TEST, swab) { // POSIX: "The swab() function shall copy nbytes bytes, which are pointed to by src, // to the object pointed to by dest, exchanging adjacent bytes." char buf[BUFSIZ];
memset(buf, 'x', sizeof(buf));
swab("ehll oowlr\0d", buf, 12);
ASSERT_STREQ("hello world", buf);
}
TEST(UNISTD_TEST, swab_odd_byte_count) { // POSIX: "If nbytes is odd, swab() copies and exchanges nbytes-1 bytes and the disposition // of the last byte is unspecified." // ...but it seems unreasonable to not just leave the last byte alone. char buf[BUFSIZ];
memset(buf, 'x', sizeof(buf));
swab("012345", buf, 3);
ASSERT_EQ('1', buf[0]);
ASSERT_EQ('0', buf[1]);
ASSERT_EQ('x', buf[2]);
}
TEST(UNISTD_TEST, swab_overlap) { // POSIX: "If copying takes place between objects that overlap, the behavior is undefined." // ...but it seems unreasonable to not just do the right thing. char buf[] = "012345";
swab(buf, buf, 4);
ASSERT_EQ('1', buf[0]);
ASSERT_EQ('0', buf[1]);
ASSERT_EQ('3', buf[2]);
ASSERT_EQ('2', buf[3]);
ASSERT_EQ('4', buf[4]);
ASSERT_EQ('5', buf[5]);
ASSERT_EQ(0, buf[6]);
}
TEST(UNISTD_TEST, usleep) { auto t0 = std::chrono::steady_clock::now();
ASSERT_EQ(0, usleep(5000)); auto t1 = std::chrono::steady_clock::now();
ASSERT_GE(t1-t0, 5000us);
}
TEST(UNISTD_TEST, sleep) { auto t0 = std::chrono::steady_clock::now();
ASSERT_EQ(0U, sleep(1)); auto t1 = std::chrono::steady_clock::now();
ASSERT_GE(t1-t0, 1s);
}
TEST(UNISTD_TEST, close_range) { #ifdefined(__GLIBC__)
GTEST_SKIP() << "glibc too old"; #elifdefined(ANDROID_HOST_MUSL)
GTEST_SKIP() << "musl does not have close_range"; #else// __GLIBC__ int fd = open("/proc/version", O_RDONLY);
ASSERT_GE(fd, 0);
int rc = close_range(fd, fd, 0); if (rc == -1 && errno == ENOSYS) GTEST_SKIP() << "no close_range() in this kernel";
ASSERT_EQ(0, rc) << strerror(errno);
// Check the fd is actually closed.
ASSERT_ERRNO_FAILURE(EBADF, close(fd), -1); #endif// __GLIBC__
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.