blob: 63178e6c8a7a72741d939c2957f7b8b83d19fec8 [file] [log] [blame]
//===-- ProcessLauncherPosixFork.cpp --------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lldb/Host/posix/ProcessLauncherPosixFork.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/HostProcess.h"
#include "lldb/Host/Pipe.h"
#include "lldb/Host/ProcessLaunchInfo.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/Log.h"
#include "llvm/Support/Errno.h"
#include "llvm/Support/FileSystem.h"
#include <climits>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sstream>
#include <csignal>
#ifdef __ANDROID__
#include <android/api-level.h>
#define PT_TRACE_ME PTRACE_TRACEME
#endif
#if defined(__ANDROID_API__) && __ANDROID_API__ < 15
#include <linux/personality.h>
#elif defined(__linux__)
#include <sys/personality.h>
#endif
using namespace lldb;
using namespace lldb_private;
static void FixupEnvironment(Environment &env) {
#ifdef __ANDROID__
// If there is no PATH variable specified inside the environment then set the
// path to /system/bin. It is required because the default path used by
// execve() is wrong on android.
env.try_emplace("PATH", "/system/bin");
#endif
}
[[noreturn]] static void ExitWithError(int error_fd,
const char *operation) {
int err = errno;
llvm::raw_fd_ostream os(error_fd, true);
os << operation << " failed: " << llvm::sys::StrError(err);
os.flush();
_exit(1);
}
static void DisableASLRIfRequested(int error_fd, const ProcessLaunchInfo &info) {
#if defined(__linux__)
if (info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)) {
const unsigned long personality_get_current = 0xffffffff;
int value = personality(personality_get_current);
if (value == -1)
ExitWithError(error_fd, "personality get");
value = personality(ADDR_NO_RANDOMIZE | value);
if (value == -1)
ExitWithError(error_fd, "personality set");
}
#endif
}
static void DupDescriptor(int error_fd, const FileSpec &file_spec, int fd,
int flags) {
int target_fd = llvm::sys::RetryAfterSignal(-1, ::open,
file_spec.GetCString(), flags, 0666);
if (target_fd == -1)
ExitWithError(error_fd, "DupDescriptor-open");
if (target_fd == fd)
return;
if (::dup2(target_fd, fd) == -1)
ExitWithError(error_fd, "DupDescriptor-dup2");
::close(target_fd);
return;
}
[[noreturn]] static void ChildFunc(int error_fd,
const ProcessLaunchInfo &info) {
if (info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)) {
if (setpgid(0, 0) != 0)
ExitWithError(error_fd, "setpgid");
}
for (size_t i = 0; i < info.GetNumFileActions(); ++i) {
const FileAction &action = *info.GetFileActionAtIndex(i);
switch (action.GetAction()) {
case FileAction::eFileActionClose:
if (close(action.GetFD()) != 0)
ExitWithError(error_fd, "close");
break;
case FileAction::eFileActionDuplicate:
if (dup2(action.GetFD(), action.GetActionArgument()) == -1)
ExitWithError(error_fd, "dup2");
break;
case FileAction::eFileActionOpen:
DupDescriptor(error_fd, action.GetFileSpec(), action.GetFD(),
action.GetActionArgument());
break;
case FileAction::eFileActionNone:
break;
}
}
const char **argv = info.GetArguments().GetConstArgumentVector();
// Change working directory
if (info.GetWorkingDirectory() &&
0 != ::chdir(info.GetWorkingDirectory().GetCString()))
ExitWithError(error_fd, "chdir");
DisableASLRIfRequested(error_fd, info);
Environment env = info.GetEnvironment();
FixupEnvironment(env);
Environment::Envp envp = env.getEnvp();
// Clear the signal mask to prevent the child from being affected by any
// masking done by the parent.
sigset_t set;
if (sigemptyset(&set) != 0 ||
pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0)
ExitWithError(error_fd, "pthread_sigmask");
if (info.GetFlags().Test(eLaunchFlagDebug)) {
// Do not inherit setgid powers.
if (setgid(getgid()) != 0)
ExitWithError(error_fd, "setgid");
// HACK:
// Close everything besides stdin, stdout, and stderr that has no file
// action to avoid leaking. Only do this when debugging, as elsewhere we
// actually rely on passing open descriptors to child processes.
const llvm::StringRef proc_fd_path = "/proc/self/fd";
std::error_code ec;
bool result;
ec = llvm::sys::fs::is_directory(proc_fd_path, result);
if (result) {
std::vector<int> files_to_close;
// Directory iterator doesn't ensure any sequence.
for (llvm::sys::fs::directory_iterator iter(proc_fd_path, ec), file_end;
iter != file_end && !ec; iter.increment(ec)) {
int fd = std::stoi(iter->path().substr(proc_fd_path.size() + 1));
// Don't close first three entries since they are stdin, stdout and
// stderr.
if (fd > 2 && !info.GetFileActionForFD(fd) && fd != error_fd)
files_to_close.push_back(fd);
}
for (int file_to_close : files_to_close)
close(file_to_close);
} else {
// Since /proc/self/fd didn't work, trying the slow way instead.
int max_fd = sysconf(_SC_OPEN_MAX);
for (int fd = 3; fd < max_fd; ++fd)
if (!info.GetFileActionForFD(fd) && fd != error_fd)
close(fd);
}
// Start tracing this child that is about to exec.
if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1)
ExitWithError(error_fd, "ptrace");
}
// Execute. We should never return...
execve(argv[0], const_cast<char *const *>(argv), envp);
#if defined(__linux__)
if (errno == ETXTBSY) {
// On android M and earlier we can get this error because the adb daemon
// can hold a write handle on the executable even after it has finished
// uploading it. This state lasts only a short time and happens only when
// there are many concurrent adb commands being issued, such as when
// running the test suite. (The file remains open when someone does an "adb
// shell" command in the fork() child before it has had a chance to exec.)
// Since this state should clear up quickly, wait a while and then give it
// one more go.
usleep(50000);
execve(argv[0], const_cast<char *const *>(argv), envp);
}
#endif
// ...unless exec fails. In which case we definitely need to end the child
// here.
ExitWithError(error_fd, "execve");
}
HostProcess
ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,
Status &error) {
char exe_path[PATH_MAX];
launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path));
// A pipe used by the child process to report errors.
PipePosix pipe;
const bool child_processes_inherit = false;
error = pipe.CreateNew(child_processes_inherit);
if (error.Fail())
return HostProcess();
::pid_t pid = ::fork();
if (pid == -1) {
// Fork failed
error.SetErrorStringWithFormatv("Fork failed with error message: {0}",
llvm::sys::StrError());
return HostProcess(LLDB_INVALID_PROCESS_ID);
}
if (pid == 0) {
// child process
pipe.CloseReadFileDescriptor();
ChildFunc(pipe.ReleaseWriteFileDescriptor(), launch_info);
}
// parent process
pipe.CloseWriteFileDescriptor();
char buf[1000];
int r = read(pipe.GetReadFileDescriptor(), buf, sizeof buf);
if (r == 0)
return HostProcess(pid); // No error. We're done.
error.SetErrorString(buf);
llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0);
return HostProcess();
}