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(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); }
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(dl, exec_without_ld_preload) { #if defined(__BIONIC__) std::string helper = get_testlib_root() + "/ld_preload_test_helper/ld_preload_test_helper"; chmod(helper.c_str(), 0755); ExecTestHelper eth; eth.SetArgs({ helper.c_str(), nullptr }); eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "12345"); #endif }
TEST(dl, preinit_system_calls) { #if defined(__BIONIC__) std::string helper = get_testlib_root() + "/preinit_syscall_test_helper/preinit_syscall_test_helper"; chmod(helper.c_str(), 0755); // TODO: "x" lost in CTS, b/34945607 ExecTestHelper eth; eth.SetArgs({ helper.c_str(), nullptr }); eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, nullptr); #endif }
// ld_config_test_helper must fail because it is depending on a lib which is not // in the search path // // Call sequence is... // _helper -- (get_value_from_lib()) --> // _lib1.so -- (get_value_from_another_lib()) --> // _lib2.so (returns 12345) // The two libs are in ns2/ subdir. TEST(dl, exec_without_ld_config_file) { #if defined(__BIONIC__) std::string error_message = "CANNOT LINK EXECUTABLE \"" + get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper\": library \"ld_config_test_helper_lib1.so\" not found\n"; std::string helper = get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper"; chmod(helper.c_str(), 0755); ExecTestHelper eth; eth.SetArgs({ helper.c_str(), nullptr }); eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, EXIT_FAILURE, error_message.c_str()); #endif }
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(dl, exec_linker) { #if defined(__BIONIC__) #if defined(__LP64__) static constexpr const char* kPathToLinker = "/system/bin/linker64"; #else static constexpr const char* kPathToLinker = "/system/bin/linker"; #endif ExecTestHelper eth; std::string expected_output = std::string("This is ") + kPathToLinker + ", the helper program for dynamic executables.\n"; eth.SetArgs( { kPathToLinker, nullptr }); eth.Run([&]() { execve(kPathToLinker, eth.GetArgs(), eth.GetEnv()); }, 0, expected_output.c_str()); #endif }
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); }
// ensures that LD_CONFIG_FILE env var does not work for production builds. // The test input is the same as exec_with_ld_config_file, but it must fail in // this case. TEST(dl, disable_ld_config_file) { #if defined(__BIONIC__) if (getuid() == 0) { // when executed from the shell (e.g. not as part of CTS), skip the test. // This test is only for CTS. return; } if (is_debuggable_build()) { // Skip the test for non production devices return; } std::string error_message = "CANNOT LINK EXECUTABLE \"" + get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper\": library \"ld_config_test_helper_lib1.so\" not found\n"; std::string helper = get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper"; TemporaryFile config_file; create_ld_config_file(config_file.filename); std::string env = std::string("LD_CONFIG_FILE=") + config_file.filename; chmod(helper.c_str(), 0755); ExecTestHelper eth; eth.SetArgs({ helper.c_str(), nullptr }); eth.SetEnv({ env.c_str(), nullptr }); eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, EXIT_FAILURE, error_message.c_str()); #endif }
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(dl, exec_with_ld_preload) { #if defined(__BIONIC__) std::string helper = get_testlib_root() + "/ld_preload_test_helper/ld_preload_test_helper"; std::string env = std::string("LD_PRELOAD=") + get_testlib_root() + "/ld_preload_test_helper_lib2.so"; chmod(helper.c_str(), 0755); ExecTestHelper eth; eth.SetArgs({ helper.c_str(), nullptr }); eth.SetEnv({ env.c_str(), nullptr }); // ld_preload_test_helper calls get_value_from_lib() and returns the value. // The symbol is defined by two libs: ld_preload_test_helper_lib.so and // ld_preloaded_lib.so. The former is DT_NEEDED and the latter is LD_PRELOADED // via this execution. The main executable is linked to the LD_PRELOADED lib // and the value given from the lib is returned. eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "54321"); #endif }
// _lib1.so and _lib2.so are now searchable by having another namespace 'ns2' // whose search paths include the 'ns2/' subdir. TEST(dl, exec_with_ld_config_file) { #if defined(__BIONIC__) if (!is_debuggable_build()) { // LD_CONFIG_FILE is not supported on user build return; } std::string helper = get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper"; TemporaryFile config_file; create_ld_config_file(config_file.filename); std::string env = std::string("LD_CONFIG_FILE=") + config_file.filename; chmod(helper.c_str(), 0755); ExecTestHelper eth; eth.SetArgs({ helper.c_str(), nullptr }); eth.SetEnv({ env.c_str(), nullptr }); eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "12345"); #endif }