301 lines · cpp
1//===-- ProcessLauncherPosixFork.cpp --------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Host/posix/ProcessLauncherPosixFork.h"10#include "lldb/Host/FileSystem.h"11#include "lldb/Host/Host.h"12#include "lldb/Host/HostProcess.h"13#include "lldb/Host/Pipe.h"14#include "lldb/Host/ProcessLaunchInfo.h"15#include "lldb/Utility/FileSpec.h"16#include "lldb/Utility/Log.h"17#include "llvm/Support/Errno.h"18 19#include <climits>20#include <fcntl.h>21#include <sys/ptrace.h>22#include <sys/wait.h>23#include <unistd.h>24 25#include <csignal>26#include <sstream>27 28#if defined(__linux__)29#include <sys/personality.h>30#endif31 32using namespace lldb;33using namespace lldb_private;34 35// Begin code running in the child process36// NB: This code needs to be async-signal safe, since we're invoking fork from37// multithreaded contexts.38 39static void write_string(int error_fd, const char *str) {40 int r = write(error_fd, str, strlen(str));41 (void)r;42}43 44[[noreturn]] static void ExitWithError(int error_fd, const char *operation) {45 int err = errno;46 write_string(error_fd, operation);47 write_string(error_fd, " failed: ");48 // strerror is not guaranteed to be async-signal safe, but it usually is.49 write_string(error_fd, strerror(err));50 _exit(1);51}52 53static void DisableASLR(int error_fd) {54#if defined(__linux__)55 const unsigned long personality_get_current = 0xffffffff;56 int value = personality(personality_get_current);57 if (value == -1)58 ExitWithError(error_fd, "personality get");59 60 value = personality(ADDR_NO_RANDOMIZE | value);61 if (value == -1)62 ExitWithError(error_fd, "personality set");63#endif64}65 66static void DupDescriptor(int error_fd, const char *file, int fd, int flags) {67 int target_fd = FileSystem::Instance().Open(file, flags, 0666);68 69 if (target_fd == -1)70 ExitWithError(error_fd, "DupDescriptor-open");71 72 if (target_fd == fd)73 return;74 75 if (::dup2(target_fd, fd) == -1)76 ExitWithError(error_fd, "DupDescriptor-dup2");77 78 ::close(target_fd);79}80 81namespace {82struct ForkFileAction {83 ForkFileAction(const FileAction &act);84 85 FileAction::Action action;86 int fd;87 std::string path;88 int arg;89};90 91struct ForkLaunchInfo {92 ForkLaunchInfo(const ProcessLaunchInfo &info);93 94 bool separate_process_group;95 bool debug;96 bool disable_aslr;97 std::string wd;98 std::string executable;99 const char **argv;100 Environment::Envp envp;101 std::vector<ForkFileAction> actions;102 103 bool has_action(int fd) const {104 for (const ForkFileAction &action : actions) {105 if (action.fd == fd)106 return true;107 }108 return false;109 }110};111} // namespace112 113[[noreturn]] static void ChildFunc(int error_fd, const ForkLaunchInfo &info) {114 if (info.separate_process_group) {115 if (setpgid(0, 0) != 0)116 ExitWithError(error_fd, "setpgid");117 }118 119 for (const ForkFileAction &action : info.actions) {120 switch (action.action) {121 case FileAction::eFileActionClose:122 if (close(action.fd) != 0)123 ExitWithError(error_fd, "close");124 break;125 case FileAction::eFileActionDuplicate:126 if (action.fd != action.arg) {127 if (dup2(action.fd, action.arg) == -1)128 ExitWithError(error_fd, "dup2");129 } else {130 if (fcntl(action.fd, F_SETFD,131 fcntl(action.fd, F_GETFD) & ~FD_CLOEXEC) == -1)132 ExitWithError(error_fd, "fcntl");133 }134 break;135 case FileAction::eFileActionOpen:136 DupDescriptor(error_fd, action.path.c_str(), action.fd, action.arg);137 break;138 case FileAction::eFileActionNone:139 break;140 }141 }142 143 // Change working directory144 if (!info.wd.empty() && 0 != ::chdir(info.wd.c_str()))145 ExitWithError(error_fd, "chdir");146 147 if (info.disable_aslr)148 DisableASLR(error_fd);149 150 // Clear the signal mask to prevent the child from being affected by any151 // masking done by the parent.152 sigset_t set;153 if (sigemptyset(&set) != 0 ||154 pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0)155 ExitWithError(error_fd, "pthread_sigmask");156 157 if (info.debug) {158 // Do not inherit setgid powers.159 if (setgid(getgid()) != 0)160 ExitWithError(error_fd, "setgid");161 162 // HACK:163 // Close everything besides stdin, stdout, and stderr that has no file164 // action to avoid leaking. Only do this when debugging, as elsewhere we165 // actually rely on passing open descriptors to child processes.166 // NB: This code is not async-signal safe, but we currently do not launch167 // processes for debugging from within multithreaded contexts.168 169 const llvm::StringRef proc_fd_path = "/proc/self/fd";170 std::error_code ec;171 bool result;172 ec = llvm::sys::fs::is_directory(proc_fd_path, result);173 if (result) {174 std::vector<int> files_to_close;175 // Directory iterator doesn't ensure any sequence.176 for (llvm::sys::fs::directory_iterator iter(proc_fd_path, ec), file_end;177 iter != file_end && !ec; iter.increment(ec)) {178 int fd = std::stoi(iter->path().substr(proc_fd_path.size() + 1));179 180 // Don't close first three entries since they are stdin, stdout and181 // stderr.182 if (fd > 2 && !info.has_action(fd) && fd != error_fd)183 files_to_close.push_back(fd);184 }185 for (int file_to_close : files_to_close)186 close(file_to_close);187 } else {188 // Since /proc/self/fd didn't work, trying the slow way instead.189 int max_fd = sysconf(_SC_OPEN_MAX);190 for (int fd = 3; fd < max_fd; ++fd)191 if (!info.has_action(fd) && fd != error_fd)192 close(fd);193 }194 195 // Start tracing this child that is about to exec.196#ifdef _AIX197 if (ptrace64(PT_TRACE_ME, 0, 0, 0, nullptr) == -1)198#else199 if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1)200#endif201 ExitWithError(error_fd, "ptrace");202 }203 204 // Execute. We should never return...205 execve(info.executable.c_str(), const_cast<char *const *>(info.argv),206 info.envp);207 208#if defined(__linux__)209 if (errno == ETXTBSY) {210 // On android M and earlier we can get this error because the adb daemon211 // can hold a write handle on the executable even after it has finished212 // uploading it. This state lasts only a short time and happens only when213 // there are many concurrent adb commands being issued, such as when214 // running the test suite. (The file remains open when someone does an "adb215 // shell" command in the fork() child before it has had a chance to exec.)216 // Since this state should clear up quickly, wait a while and then give it217 // one more go.218 usleep(50000);219 execve(info.executable.c_str(), const_cast<char *const *>(info.argv),220 info.envp);221 }222#endif223 224 // ...unless exec fails. In which case we definitely need to end the child225 // here.226 ExitWithError(error_fd, "execve");227}228 229// End of code running in the child process.230 231ForkFileAction::ForkFileAction(const FileAction &act)232 : action(act.GetAction()), fd(act.GetFD()), path(act.GetPath().str()),233 arg(act.GetActionArgument()) {}234 235static std::vector<ForkFileAction>236MakeForkActions(const ProcessLaunchInfo &info) {237 std::vector<ForkFileAction> result;238 for (size_t i = 0; i < info.GetNumFileActions(); ++i)239 result.emplace_back(*info.GetFileActionAtIndex(i));240 return result;241}242 243ForkLaunchInfo::ForkLaunchInfo(const ProcessLaunchInfo &info)244 : separate_process_group(245 info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)),246 debug(info.GetFlags().Test(eLaunchFlagDebug)),247 disable_aslr(info.GetFlags().Test(eLaunchFlagDisableASLR)),248 wd(info.GetWorkingDirectory().GetPath()),249 executable(info.GetExecutableFile().GetPath()),250 argv(info.GetArguments().GetConstArgumentVector()),251 envp(info.GetEnvironment().getEnvp()), actions(MakeForkActions(info)) {}252 253HostProcess254ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,255 Status &error) {256 // A pipe used by the child process to report errors.257 PipePosix pipe;258 error = pipe.CreateNew();259 if (error.Fail())260 return HostProcess();261 262 const ForkLaunchInfo fork_launch_info(launch_info);263 264 ::pid_t pid = ::fork();265 if (pid == -1) {266 // Fork failed267 error = Status::FromErrorStringWithFormatv(268 "Fork failed with error message: {0}", llvm::sys::StrError());269 return HostProcess(LLDB_INVALID_PROCESS_ID);270 }271 if (pid == 0) {272 // child process273 pipe.CloseReadFileDescriptor();274 ChildFunc(pipe.ReleaseWriteFileDescriptor(), fork_launch_info);275 }276 277 // parent process278 279 pipe.CloseWriteFileDescriptor();280 llvm::SmallString<0> buf;281 size_t pos = 0;282 ssize_t r = 0;283 do {284 pos += r;285 buf.resize_for_overwrite(pos + 100);286 r = llvm::sys::RetryAfterSignal(-1, read, pipe.GetReadFileDescriptor(),287 buf.begin() + pos, buf.size() - pos);288 } while (r > 0);289 assert(r != -1);290 291 buf.resize(pos);292 if (buf.empty())293 return HostProcess(pid); // No error. We're done.294 295 error = Status(buf.str().str());296 297 llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0);298 299 return HostProcess();300}301