brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.1 KiB · 15a8097 Raw
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