TEST(spawn, posix_spawnp) { ExecTestHelper eth; eth.SetArgs({"true", nullptr}); pid_t pid; ASSERT_EQ(0, posix_spawnp(&pid, eth.GetArg0(), nullptr, nullptr, eth.GetArgs(), nullptr)); AssertChildExited(pid, 0); }
TEST(UNISTD_TEST, lockf_with_child) { constexpr off64_t file_size = 32*1024LL; TemporaryFile tf; ASSERT_EQ(0, ftruncate(tf.fd, file_size)); // Lock everything. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(0, lockf64(tf.fd, F_LOCK, file_size)); // Fork a child process pid_t pid = fork(); ASSERT_NE(-1, pid); if (pid == 0) { // Check that the child cannot lock the file. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(-1, lockf64(tf.fd, F_TLOCK, file_size)); ASSERT_EQ(EAGAIN, errno); // Check also that it reports itself as locked. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(-1, lockf64(tf.fd, F_TEST, file_size)); ASSERT_EQ(EACCES, errno); _exit(0); } AssertChildExited(pid, 0); }
TEST(UNISTD_TEST, lockf_partial_with_child) { constexpr off64_t file_size = 32*1024LL; TemporaryFile tf; ASSERT_EQ(0, ftruncate(tf.fd, 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_EQ(-1, lockf64(tf.fd, F_TEST, file_size/2)); ASSERT_EQ(EACCES, errno); // Check also that it reports itself as locked. ASSERT_EQ(0, lseek64(tf.fd, 0, SEEK_SET)); ASSERT_EQ(-1, lockf64(tf.fd, F_TEST, file_size/2)); ASSERT_EQ(EACCES, errno); _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)); }
TEST(spawn, posix_spawn_file_actions) { int fds[2]; ASSERT_NE(-1, pipe(fds)); posix_spawn_file_actions_t fa; ASSERT_EQ(0, posix_spawn_file_actions_init(&fa)); ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[0])); ASSERT_EQ(0, posix_spawn_file_actions_adddup2(&fa, fds[1], 1)); ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[1])); // Check that close(2) failures are ignored by closing the same fd again. ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[1])); ASSERT_EQ(0, posix_spawn_file_actions_addopen(&fa, 56, "/proc/version", O_RDONLY, 0)); ExecTestHelper eth; eth.SetArgs({"ls", "-l", "/proc/self/fd", nullptr}); pid_t pid; ASSERT_EQ(0, posix_spawnp(&pid, eth.GetArg0(), &fa, nullptr, eth.GetArgs(), eth.GetEnv())); ASSERT_EQ(0, posix_spawn_file_actions_destroy(&fa)); ASSERT_EQ(0, close(fds[1])); std::string content; ASSERT_TRUE(android::base::ReadFdToString(fds[0], &content)); ASSERT_EQ(0, close(fds[0])); AssertChildExited(pid, 0); // We'll know the dup2 worked if we see any ls(1) output in our pipe. // The open we can check manually... bool open_to_fd_56_worked = false; for (const auto& line : android::base::Split(content, "\n")) { if (line.find(" 56 -> /proc/version") != std::string::npos) open_to_fd_56_worked = true; } ASSERT_TRUE(open_to_fd_56_worked); }
void CreateRelroFile(const char* lib, const char* relro_file) { int relro_fd = open(relro_file, O_RDWR | O_TRUNC); ASSERT_NOERROR(relro_fd); pid_t pid = fork(); if (pid == 0) { // child process extinfo_.flags |= ANDROID_DLEXT_WRITE_RELRO; extinfo_.relro_fd = relro_fd; void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo_); if (handle == nullptr) { fprintf(stderr, "in child: %s\n", dlerror()); exit(1); } exit(0); } // continuing in parent ASSERT_NOERROR(close(relro_fd)); ASSERT_NOERROR(pid); AssertChildExited(pid, 0); // reopen file for reading so it can be used relro_fd = open(relro_file, O_RDONLY); ASSERT_NOERROR(relro_fd); extinfo_.flags |= ANDROID_DLEXT_USE_RELRO; extinfo_.relro_fd = relro_fd; }
TEST(fenv, feenableexcept_fegetexcept) { #if defined(__aarch64__) || defined(__arm__) // ARM doesn't support this. They used to if you go back far enough, but it was removed in // the Cortex-A8 between r3p1 and r3p2. ASSERT_EQ(-1, feenableexcept(FE_INVALID)); ASSERT_EQ(0, fegetexcept()); ASSERT_EQ(-1, feenableexcept(FE_DIVBYZERO)); ASSERT_EQ(0, fegetexcept()); ASSERT_EQ(-1, feenableexcept(FE_OVERFLOW)); ASSERT_EQ(0, fegetexcept()); ASSERT_EQ(-1, feenableexcept(FE_UNDERFLOW)); ASSERT_EQ(0, fegetexcept()); ASSERT_EQ(-1, feenableexcept(FE_INEXACT)); ASSERT_EQ(0, fegetexcept()); ASSERT_EQ(-1, feenableexcept(FE_DENORMAL)); ASSERT_EQ(0, fegetexcept()); #else // We can't recover from SIGFPE, so sacrifice a child... pid_t pid = fork(); ASSERT_NE(-1, pid) << strerror(errno); if (pid == 0) { feclearexcept(FE_ALL_EXCEPT); ASSERT_EQ(0, fetestexcept(FE_ALL_EXCEPT)); ASSERT_EQ(0, feenableexcept(FE_INVALID)); ASSERT_EQ(FE_INVALID, fegetexcept()); ASSERT_EQ(0, feraiseexcept(FE_INVALID)); _exit(123); } AssertChildExited(pid, -SIGFPE); #endif }
TEST(spawn, posix_spawnp_not_found) { ExecTestHelper eth; eth.SetArgs({"does-not-exist", nullptr}); pid_t pid; ASSERT_EQ(0, posix_spawnp(&pid, eth.GetArg0(), nullptr, nullptr, eth.GetArgs(), nullptr)); AssertChildExited(pid, 127); }
TEST(spawn, posix_spawn_environment) { ExecTestHelper eth; eth.SetArgs({"sh", "-c", "exit $posix_spawn_environment_test", nullptr}); eth.SetEnv({"posix_spawn_environment_test=66", nullptr}); pid_t pid; ASSERT_EQ(0, posix_spawnp(&pid, eth.GetArg0(), nullptr, nullptr, eth.GetArgs(), eth.GetEnv())); AssertChildExited(pid, 66); }
TEST(pthread, pthread_atfork_with_dlclose) { ASSERT_EQ(0, pthread_atfork(AtForkPrepare1, AtForkParent1, AtForkChild1)); void* handle = dlopen("libtest_pthread_atfork.so", RTLD_NOW | RTLD_LOCAL); ASSERT_TRUE(handle != nullptr) << dlerror(); typedef int (*fn_t)(void (*)(void), void (*)(void), void (*)(void)); fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "proxy_pthread_atfork")); ASSERT_TRUE(fn != nullptr) << dlerror(); // the library registers 2 additional atfork handlers in a constructor ASSERT_EQ(0, fn(AtForkPrepare2, AtForkParent2, AtForkChild2)); ASSERT_EQ(0, fn(AtForkPrepare3, AtForkParent3, AtForkChild3)); ASSERT_EQ(0, pthread_atfork(AtForkPrepare4, AtForkParent4, AtForkChild4)); pid_t pid = fork(); ASSERT_NE(-1, pid) << strerror(errno); if (pid == 0) { ASSERT_EQ(1234, g_atfork_child_calls); _exit(0); } ASSERT_EQ(1234, g_atfork_parent_calls); ASSERT_EQ(4321, g_atfork_prepare_calls); EXPECT_EQ(0, dlclose(handle)); g_atfork_prepare_calls = g_atfork_parent_calls = g_atfork_child_calls = 0; AssertChildExited(pid, 0); pid = fork(); ASSERT_NE(-1, pid) << strerror(errno); if (pid == 0) { ASSERT_EQ(14, g_atfork_child_calls); _exit(0); } ASSERT_EQ(14, g_atfork_parent_calls); ASSERT_EQ(41, g_atfork_prepare_calls); AssertChildExited(pid, 0); }
TEST(stdlib, quick_exit) { pid_t pid = fork(); ASSERT_NE(-1, pid) << strerror(errno); if (pid == 0) { quick_exit(99); } AssertChildExited(pid, 99); }
TEST(UNISTD_TEST, _exit) { pid_t pid = fork(); ASSERT_NE(-1, pid) << strerror(errno); if (pid == 0) { _exit(99); } AssertChildExited(pid, 99); }
TEST(unistd, _Exit) { pid_t pid = fork(); ASSERT_NE(-1, pid) << strerror(errno); if (pid == 0) { _Exit(99); } AssertChildExited(pid, 99); }
TEST(spawn, signal_stress) { // Ensure that posix_spawn doesn't restore the caller's signal mask in the // child without first defaulting any caught signals (http://b/68707996). static pid_t parent = getpid(); setpgid(0, 0); signal(SIGRTMIN, SIG_IGN); pid_t pid = fork(); ASSERT_NE(-1, pid); if (pid == 0) { for (size_t i = 0; i < 1024; ++i) { kill(0, SIGRTMIN); usleep(10); } _exit(99); } // We test both with and without attributes, because they used to be // different codepaths. We also test with an empty `sigdefault` set. posix_spawnattr_t attr1; posix_spawnattr_init(&attr1); sigset_t empty_mask = {}; posix_spawnattr_t attr2; posix_spawnattr_init(&attr2); posix_spawnattr_setflags(&attr2, POSIX_SPAWN_SETSIGDEF); posix_spawnattr_setsigdefault(&attr2, &empty_mask); posix_spawnattr_t* attrs[] = { nullptr, &attr1, &attr2 }; // We use a real-time signal because that's a tricky case for LP32 // because our sigset_t was too small. ScopedSignalHandler ssh(SIGRTMIN, [](int) { ASSERT_EQ(getpid(), parent); }); const size_t pid_count = 128; pid_t spawned_pids[pid_count]; ExecTestHelper eth; eth.SetArgs({"true", nullptr}); for (size_t i = 0; i < pid_count; ++i) { pid_t spawned_pid; ASSERT_EQ(0, posix_spawn(&spawned_pid, "true", nullptr, attrs[i % 3], eth.GetArgs(), nullptr)); spawned_pids[i] = spawned_pid; } for (pid_t spawned_pid : spawned_pids) { ASSERT_EQ(spawned_pid, TEMP_FAILURE_RETRY(waitpid(spawned_pid, nullptr, 0))); } AssertChildExited(pid, 99); }
TEST(stdlib, at_quick_exit) { pid_t pid = fork(); ASSERT_NE(-1, pid) << strerror(errno); if (pid == 0) { ASSERT_EQ(at_quick_exit(quick_exit_2), 0); ASSERT_EQ(at_quick_exit(quick_exit_1), 0); atexit(not_run); quick_exit(99); } AssertChildExited(pid, 99); }
TEST(UNISTD_TEST, getpid_caching_and_clone) { pid_t parent_pid = getpid(); ASSERT_EQ(syscall(__NR_getpid), parent_pid); void* child_stack[1024]; int clone_result = clone(GetPidCachingCloneStartRoutine, &child_stack[1024], CLONE_NEWNS | SIGCHLD, NULL); if (clone_result == -1 && errno == EPERM && getuid() != 0) { GTEST_LOG_(INFO) << "This test only works if you have permission to CLONE_NEWNS; try running as root.\n"; return; } ASSERT_NE(clone_result, -1); ASSERT_EQ(parent_pid, getpid()); AssertChildExited(clone_result, 123); }
static void TestGetPidCachingWithFork(int (*fork_fn)()) { pid_t parent_pid = getpid(); ASSERT_EQ(syscall(__NR_getpid), parent_pid); pid_t fork_result = fork_fn(); ASSERT_NE(fork_result, -1); if (fork_result == 0) { // We're the child. AssertGetPidCorrect(); ASSERT_EQ(parent_pid, getppid()); _exit(123); } else { // We're the parent. ASSERT_EQ(parent_pid, getpid()); AssertChildExited(fork_result, 123); } }
TEST(pty, forkpty) { pid_t sid = getsid(0); int master; pid_t pid = forkpty(&master, NULL, NULL, NULL); ASSERT_NE(-1, pid); if (pid == 0) { // We're the child. ASSERT_NE(sid, getsid(0)); _exit(0); } ASSERT_EQ(sid, getsid(0)); AssertChildExited(pid, 0); close(master); }
static void CatFileToString(posix_spawnattr_t* sa, const char* path, std::string* content) { int fds[2]; ASSERT_NE(-1, pipe(fds)); posix_spawn_file_actions_t fa; ASSERT_EQ(0, posix_spawn_file_actions_init(&fa)); ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[0])); ASSERT_EQ(0, posix_spawn_file_actions_adddup2(&fa, fds[1], 1)); ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[1])); ExecTestHelper eth; eth.SetArgs({"cat", path, nullptr}); pid_t pid; ASSERT_EQ(0, posix_spawnp(&pid, eth.GetArg0(), &fa, sa, eth.GetArgs(), nullptr)); ASSERT_EQ(0, posix_spawn_file_actions_destroy(&fa)); ASSERT_EQ(0, close(fds[1])); ASSERT_TRUE(android::base::ReadFdToString(fds[0], content)); ASSERT_EQ(0, close(fds[0])); AssertChildExited(pid, 0); }
TEST(time, timer_create) { sigevent_t se; memset(&se, 0, sizeof(se)); se.sigev_notify = SIGEV_THREAD; se.sigev_notify_function = NoOpNotifyFunction; timer_t timer_id; ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, &se, &timer_id)); pid_t pid = fork(); ASSERT_NE(-1, pid) << strerror(errno); if (pid == 0) { // Timers are not inherited by the child. ASSERT_EQ(-1, timer_delete(timer_id)); ASSERT_EQ(EINVAL, errno); _exit(0); } AssertChildExited(pid, 0); ASSERT_EQ(0, timer_delete(timer_id)); }
void DlExtRelroSharingTest::SpawnChildrenAndMeasurePss(const char* lib, bool share_relro, size_t* pss_out) { const int CHILDREN = 20; // Create children pid_t child_pids[CHILDREN]; int childpipe[CHILDREN]; for (int i=0; i<CHILDREN; ++i) { char read_buf; int child_done_pipe[2], parent_done_pipe[2]; ASSERT_NOERROR(pipe(child_done_pipe)); ASSERT_NOERROR(pipe(parent_done_pipe)); pid_t child = fork(); if (child == 0) { // close the 'wrong' ends of the pipes in the child close(child_done_pipe[0]); close(parent_done_pipe[1]); // open the library void* handle; if (share_relro) { handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo_); } else { handle = dlopen(lib, RTLD_NOW); } if (handle == nullptr) { fprintf(stderr, "in child: %s\n", dlerror()); exit(1); } // close write end of child_done_pipe to signal the parent that we're done. close(child_done_pipe[1]); // wait for the parent to close parent_done_pipe, then exit read(parent_done_pipe[0], &read_buf, 1); exit(0); } ASSERT_NOERROR(child); // close the 'wrong' ends of the pipes in the parent close(child_done_pipe[1]); close(parent_done_pipe[0]); // wait for the child to be done read(child_done_pipe[0], &read_buf, 1); close(child_done_pipe[0]); // save the child's pid and the parent_done_pipe child_pids[i] = child; childpipe[i] = parent_done_pipe[1]; } // Sum the PSS of all the children size_t total_pss = 0; for (int i=0; i<CHILDREN; ++i) { size_t child_pss; ASSERT_NO_FATAL_FAILURE(getPss(child_pids[i], &child_pss)); total_pss += child_pss; } *pss_out = total_pss; // Close pipes and wait for children to exit for (int i=0; i<CHILDREN; ++i) { ASSERT_NOERROR(close(childpipe[i])); } for (int i = 0; i < CHILDREN; ++i) { AssertChildExited(child_pids[i], 0); } }