Path: blob/main/contrib/llvm-project/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp
39608 views
//===-- ProcessLauncherPosixFork.cpp --------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Host/posix/ProcessLauncherPosixFork.h"9#include "lldb/Host/FileSystem.h"10#include "lldb/Host/Host.h"11#include "lldb/Host/HostProcess.h"12#include "lldb/Host/Pipe.h"13#include "lldb/Host/ProcessLaunchInfo.h"14#include "lldb/Utility/FileSpec.h"15#include "lldb/Utility/Log.h"16#include "llvm/Support/Errno.h"1718#include <climits>19#include <sys/ptrace.h>20#include <sys/wait.h>21#include <unistd.h>2223#include <sstream>24#include <csignal>2526#ifdef __ANDROID__27#include <android/api-level.h>28#define PT_TRACE_ME PTRACE_TRACEME29#endif3031#if defined(__ANDROID_API__) && __ANDROID_API__ < 1532#include <linux/personality.h>33#elif defined(__linux__)34#include <sys/personality.h>35#endif3637using namespace lldb;38using namespace lldb_private;3940// Begin code running in the child process41// NB: This code needs to be async-signal safe, since we're invoking fork from42// multithreaded contexts.4344static void write_string(int error_fd, const char *str) {45int r = write(error_fd, str, strlen(str));46(void)r;47}4849[[noreturn]] static void ExitWithError(int error_fd,50const char *operation) {51int err = errno;52write_string(error_fd, operation);53write_string(error_fd, " failed: ");54// strerror is not guaranteed to be async-signal safe, but it usually is.55write_string(error_fd, strerror(err));56_exit(1);57}5859static void DisableASLR(int error_fd) {60#if defined(__linux__)61const unsigned long personality_get_current = 0xffffffff;62int value = personality(personality_get_current);63if (value == -1)64ExitWithError(error_fd, "personality get");6566value = personality(ADDR_NO_RANDOMIZE | value);67if (value == -1)68ExitWithError(error_fd, "personality set");69#endif70}7172static void DupDescriptor(int error_fd, const char *file, int fd, int flags) {73int target_fd = FileSystem::Instance().Open(file, flags, 0666);7475if (target_fd == -1)76ExitWithError(error_fd, "DupDescriptor-open");7778if (target_fd == fd)79return;8081if (::dup2(target_fd, fd) == -1)82ExitWithError(error_fd, "DupDescriptor-dup2");8384::close(target_fd);85}8687namespace {88struct ForkFileAction {89ForkFileAction(const FileAction &act);9091FileAction::Action action;92int fd;93std::string path;94int arg;95};9697struct ForkLaunchInfo {98ForkLaunchInfo(const ProcessLaunchInfo &info);99100bool separate_process_group;101bool debug;102bool disable_aslr;103std::string wd;104const char **argv;105Environment::Envp envp;106std::vector<ForkFileAction> actions;107108bool has_action(int fd) const {109for (const ForkFileAction &action : actions) {110if (action.fd == fd)111return true;112}113return false;114}115};116} // namespace117118[[noreturn]] static void ChildFunc(int error_fd, const ForkLaunchInfo &info) {119if (info.separate_process_group) {120if (setpgid(0, 0) != 0)121ExitWithError(error_fd, "setpgid");122}123124for (const ForkFileAction &action : info.actions) {125switch (action.action) {126case FileAction::eFileActionClose:127if (close(action.fd) != 0)128ExitWithError(error_fd, "close");129break;130case FileAction::eFileActionDuplicate:131if (dup2(action.fd, action.arg) == -1)132ExitWithError(error_fd, "dup2");133break;134case FileAction::eFileActionOpen:135DupDescriptor(error_fd, action.path.c_str(), action.fd, action.arg);136break;137case FileAction::eFileActionNone:138break;139}140}141142// Change working directory143if (!info.wd.empty() && 0 != ::chdir(info.wd.c_str()))144ExitWithError(error_fd, "chdir");145146if (info.disable_aslr)147DisableASLR(error_fd);148149// Clear the signal mask to prevent the child from being affected by any150// masking done by the parent.151sigset_t set;152if (sigemptyset(&set) != 0 ||153pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0)154ExitWithError(error_fd, "pthread_sigmask");155156if (info.debug) {157// Do not inherit setgid powers.158if (setgid(getgid()) != 0)159ExitWithError(error_fd, "setgid");160161// HACK:162// Close everything besides stdin, stdout, and stderr that has no file163// action to avoid leaking. Only do this when debugging, as elsewhere we164// actually rely on passing open descriptors to child processes.165// NB: This code is not async-signal safe, but we currently do not launch166// processes for debugging from within multithreaded contexts.167168const llvm::StringRef proc_fd_path = "/proc/self/fd";169std::error_code ec;170bool result;171ec = llvm::sys::fs::is_directory(proc_fd_path, result);172if (result) {173std::vector<int> files_to_close;174// Directory iterator doesn't ensure any sequence.175for (llvm::sys::fs::directory_iterator iter(proc_fd_path, ec), file_end;176iter != file_end && !ec; iter.increment(ec)) {177int fd = std::stoi(iter->path().substr(proc_fd_path.size() + 1));178179// Don't close first three entries since they are stdin, stdout and180// stderr.181if (fd > 2 && !info.has_action(fd) && fd != error_fd)182files_to_close.push_back(fd);183}184for (int file_to_close : files_to_close)185close(file_to_close);186} else {187// Since /proc/self/fd didn't work, trying the slow way instead.188int max_fd = sysconf(_SC_OPEN_MAX);189for (int fd = 3; fd < max_fd; ++fd)190if (!info.has_action(fd) && fd != error_fd)191close(fd);192}193194// Start tracing this child that is about to exec.195if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1)196ExitWithError(error_fd, "ptrace");197}198199// Execute. We should never return...200execve(info.argv[0], const_cast<char *const *>(info.argv), info.envp);201202#if defined(__linux__)203if (errno == ETXTBSY) {204// On android M and earlier we can get this error because the adb daemon205// can hold a write handle on the executable even after it has finished206// uploading it. This state lasts only a short time and happens only when207// there are many concurrent adb commands being issued, such as when208// running the test suite. (The file remains open when someone does an "adb209// shell" command in the fork() child before it has had a chance to exec.)210// Since this state should clear up quickly, wait a while and then give it211// one more go.212usleep(50000);213execve(info.argv[0], const_cast<char *const *>(info.argv), info.envp);214}215#endif216217// ...unless exec fails. In which case we definitely need to end the child218// here.219ExitWithError(error_fd, "execve");220}221222// End of code running in the child process.223224ForkFileAction::ForkFileAction(const FileAction &act)225: action(act.GetAction()), fd(act.GetFD()), path(act.GetPath().str()),226arg(act.GetActionArgument()) {}227228static std::vector<ForkFileAction>229MakeForkActions(const ProcessLaunchInfo &info) {230std::vector<ForkFileAction> result;231for (size_t i = 0; i < info.GetNumFileActions(); ++i)232result.emplace_back(*info.GetFileActionAtIndex(i));233return result;234}235236static Environment::Envp FixupEnvironment(Environment env) {237#ifdef __ANDROID__238// If there is no PATH variable specified inside the environment then set the239// path to /system/bin. It is required because the default path used by240// execve() is wrong on android.241env.try_emplace("PATH", "/system/bin");242#endif243return env.getEnvp();244}245246ForkLaunchInfo::ForkLaunchInfo(const ProcessLaunchInfo &info)247: separate_process_group(248info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)),249debug(info.GetFlags().Test(eLaunchFlagDebug)),250disable_aslr(info.GetFlags().Test(eLaunchFlagDisableASLR)),251wd(info.GetWorkingDirectory().GetPath()),252argv(info.GetArguments().GetConstArgumentVector()),253envp(FixupEnvironment(info.GetEnvironment())),254actions(MakeForkActions(info)) {}255256HostProcess257ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,258Status &error) {259// A pipe used by the child process to report errors.260PipePosix pipe;261const bool child_processes_inherit = false;262error = pipe.CreateNew(child_processes_inherit);263if (error.Fail())264return HostProcess();265266const ForkLaunchInfo fork_launch_info(launch_info);267268::pid_t pid = ::fork();269if (pid == -1) {270// Fork failed271error.SetErrorStringWithFormatv("Fork failed with error message: {0}",272llvm::sys::StrError());273return HostProcess(LLDB_INVALID_PROCESS_ID);274}275if (pid == 0) {276// child process277pipe.CloseReadFileDescriptor();278ChildFunc(pipe.ReleaseWriteFileDescriptor(), fork_launch_info);279}280281// parent process282283pipe.CloseWriteFileDescriptor();284llvm::SmallString<0> buf;285size_t pos = 0;286ssize_t r = 0;287do {288pos += r;289buf.resize_for_overwrite(pos + 100);290r = llvm::sys::RetryAfterSignal(-1, read, pipe.GetReadFileDescriptor(),291buf.begin() + pos, buf.size() - pos);292} while (r > 0);293assert(r != -1);294295buf.resize(pos);296if (buf.empty())297return HostProcess(pid); // No error. We're done.298299error.SetErrorString(buf);300301llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0);302303return HostProcess();304}305306307