1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
|
//===-- 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/FileSystem.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 <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;
// Begin code running in the child process
// NB: This code needs to be async-signal safe, since we're invoking fork from
// multithreaded contexts.
static void write_string(int error_fd, const char *str) {
int r = write(error_fd, str, strlen(str));
(void)r;
}
[[noreturn]] static void ExitWithError(int error_fd,
const char *operation) {
int err = errno;
write_string(error_fd, operation);
write_string(error_fd, " failed: ");
// strerror is not guaranteed to be async-signal safe, but it usually is.
write_string(error_fd, strerror(err));
_exit(1);
}
static void DisableASLR(int error_fd) {
#if defined(__linux__)
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 char *file, int fd, int flags) {
int target_fd = FileSystem::Instance().Open(file, 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);
}
namespace {
struct ForkFileAction {
ForkFileAction(const FileAction &act);
FileAction::Action action;
int fd;
std::string path;
int arg;
};
struct ForkLaunchInfo {
ForkLaunchInfo(const ProcessLaunchInfo &info);
bool separate_process_group;
bool debug;
bool disable_aslr;
std::string wd;
const char **argv;
Environment::Envp envp;
std::vector<ForkFileAction> actions;
bool has_action(int fd) const {
for (const ForkFileAction &action : actions) {
if (action.fd == fd)
return true;
}
return false;
}
};
} // namespace
[[noreturn]] static void ChildFunc(int error_fd, const ForkLaunchInfo &info) {
if (info.separate_process_group) {
if (setpgid(0, 0) != 0)
ExitWithError(error_fd, "setpgid");
}
for (const ForkFileAction &action : info.actions) {
switch (action.action) {
case FileAction::eFileActionClose:
if (close(action.fd) != 0)
ExitWithError(error_fd, "close");
break;
case FileAction::eFileActionDuplicate:
if (dup2(action.fd, action.arg) == -1)
ExitWithError(error_fd, "dup2");
break;
case FileAction::eFileActionOpen:
DupDescriptor(error_fd, action.path.c_str(), action.fd, action.arg);
break;
case FileAction::eFileActionNone:
break;
}
}
// Change working directory
if (!info.wd.empty() && 0 != ::chdir(info.wd.c_str()))
ExitWithError(error_fd, "chdir");
if (info.disable_aslr)
DisableASLR(error_fd);
// 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.debug) {
// 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.
// NB: This code is not async-signal safe, but we currently do not launch
// processes for debugging from within multithreaded contexts.
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.has_action(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.has_action(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(info.argv[0], const_cast<char *const *>(info.argv), info.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(info.argv[0], const_cast<char *const *>(info.argv), info.envp);
}
#endif
// ...unless exec fails. In which case we definitely need to end the child
// here.
ExitWithError(error_fd, "execve");
}
// End of code running in the child process.
ForkFileAction::ForkFileAction(const FileAction &act)
: action(act.GetAction()), fd(act.GetFD()), path(act.GetPath().str()),
arg(act.GetActionArgument()) {}
static std::vector<ForkFileAction>
MakeForkActions(const ProcessLaunchInfo &info) {
std::vector<ForkFileAction> result;
for (size_t i = 0; i < info.GetNumFileActions(); ++i)
result.emplace_back(*info.GetFileActionAtIndex(i));
return result;
}
static Environment::Envp 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
return env.getEnvp();
}
ForkLaunchInfo::ForkLaunchInfo(const ProcessLaunchInfo &info)
: separate_process_group(
info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)),
debug(info.GetFlags().Test(eLaunchFlagDebug)),
disable_aslr(info.GetFlags().Test(eLaunchFlagDisableASLR)),
wd(info.GetWorkingDirectory().GetPath()),
argv(info.GetArguments().GetConstArgumentVector()),
envp(FixupEnvironment(info.GetEnvironment())),
actions(MakeForkActions(info)) {}
HostProcess
ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,
Status &error) {
// 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();
const ForkLaunchInfo fork_launch_info(launch_info);
::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(), fork_launch_info);
}
// parent process
pipe.CloseWriteFileDescriptor();
llvm::SmallString<0> buf;
size_t pos = 0;
ssize_t r = 0;
do {
pos += r;
buf.resize_for_overwrite(pos + 100);
r = llvm::sys::RetryAfterSignal(-1, read, pipe.GetReadFileDescriptor(),
buf.begin() + pos, buf.size() - pos);
} while (r > 0);
assert(r != -1);
buf.resize(pos);
if (buf.empty())
return HostProcess(pid); // No error. We're done.
error.SetErrorString(buf);
llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0);
return HostProcess();
}
|