Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp
39608 views
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-exception
6
//
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 <sys/ptrace.h>
21
#include <sys/wait.h>
22
#include <unistd.h>
23
24
#include <sstream>
25
#include <csignal>
26
27
#ifdef __ANDROID__
28
#include <android/api-level.h>
29
#define PT_TRACE_ME PTRACE_TRACEME
30
#endif
31
32
#if defined(__ANDROID_API__) && __ANDROID_API__ < 15
33
#include <linux/personality.h>
34
#elif defined(__linux__)
35
#include <sys/personality.h>
36
#endif
37
38
using namespace lldb;
39
using namespace lldb_private;
40
41
// Begin code running in the child process
42
// NB: This code needs to be async-signal safe, since we're invoking fork from
43
// multithreaded contexts.
44
45
static void write_string(int error_fd, const char *str) {
46
int r = write(error_fd, str, strlen(str));
47
(void)r;
48
}
49
50
[[noreturn]] static void ExitWithError(int error_fd,
51
const char *operation) {
52
int err = errno;
53
write_string(error_fd, operation);
54
write_string(error_fd, " failed: ");
55
// strerror is not guaranteed to be async-signal safe, but it usually is.
56
write_string(error_fd, strerror(err));
57
_exit(1);
58
}
59
60
static void DisableASLR(int error_fd) {
61
#if defined(__linux__)
62
const unsigned long personality_get_current = 0xffffffff;
63
int value = personality(personality_get_current);
64
if (value == -1)
65
ExitWithError(error_fd, "personality get");
66
67
value = personality(ADDR_NO_RANDOMIZE | value);
68
if (value == -1)
69
ExitWithError(error_fd, "personality set");
70
#endif
71
}
72
73
static void DupDescriptor(int error_fd, const char *file, int fd, int flags) {
74
int target_fd = FileSystem::Instance().Open(file, flags, 0666);
75
76
if (target_fd == -1)
77
ExitWithError(error_fd, "DupDescriptor-open");
78
79
if (target_fd == fd)
80
return;
81
82
if (::dup2(target_fd, fd) == -1)
83
ExitWithError(error_fd, "DupDescriptor-dup2");
84
85
::close(target_fd);
86
}
87
88
namespace {
89
struct ForkFileAction {
90
ForkFileAction(const FileAction &act);
91
92
FileAction::Action action;
93
int fd;
94
std::string path;
95
int arg;
96
};
97
98
struct ForkLaunchInfo {
99
ForkLaunchInfo(const ProcessLaunchInfo &info);
100
101
bool separate_process_group;
102
bool debug;
103
bool disable_aslr;
104
std::string wd;
105
const char **argv;
106
Environment::Envp envp;
107
std::vector<ForkFileAction> actions;
108
109
bool has_action(int fd) const {
110
for (const ForkFileAction &action : actions) {
111
if (action.fd == fd)
112
return true;
113
}
114
return false;
115
}
116
};
117
} // namespace
118
119
[[noreturn]] static void ChildFunc(int error_fd, const ForkLaunchInfo &info) {
120
if (info.separate_process_group) {
121
if (setpgid(0, 0) != 0)
122
ExitWithError(error_fd, "setpgid");
123
}
124
125
for (const ForkFileAction &action : info.actions) {
126
switch (action.action) {
127
case FileAction::eFileActionClose:
128
if (close(action.fd) != 0)
129
ExitWithError(error_fd, "close");
130
break;
131
case FileAction::eFileActionDuplicate:
132
if (dup2(action.fd, action.arg) == -1)
133
ExitWithError(error_fd, "dup2");
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 directory
144
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 any
151
// 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 file
164
// action to avoid leaking. Only do this when debugging, as elsewhere we
165
// actually rely on passing open descriptors to child processes.
166
// NB: This code is not async-signal safe, but we currently do not launch
167
// 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 and
181
// 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
if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1)
197
ExitWithError(error_fd, "ptrace");
198
}
199
200
// Execute. We should never return...
201
execve(info.argv[0], const_cast<char *const *>(info.argv), info.envp);
202
203
#if defined(__linux__)
204
if (errno == ETXTBSY) {
205
// On android M and earlier we can get this error because the adb daemon
206
// can hold a write handle on the executable even after it has finished
207
// uploading it. This state lasts only a short time and happens only when
208
// there are many concurrent adb commands being issued, such as when
209
// running the test suite. (The file remains open when someone does an "adb
210
// shell" command in the fork() child before it has had a chance to exec.)
211
// Since this state should clear up quickly, wait a while and then give it
212
// one more go.
213
usleep(50000);
214
execve(info.argv[0], const_cast<char *const *>(info.argv), info.envp);
215
}
216
#endif
217
218
// ...unless exec fails. In which case we definitely need to end the child
219
// here.
220
ExitWithError(error_fd, "execve");
221
}
222
223
// End of code running in the child process.
224
225
ForkFileAction::ForkFileAction(const FileAction &act)
226
: action(act.GetAction()), fd(act.GetFD()), path(act.GetPath().str()),
227
arg(act.GetActionArgument()) {}
228
229
static std::vector<ForkFileAction>
230
MakeForkActions(const ProcessLaunchInfo &info) {
231
std::vector<ForkFileAction> result;
232
for (size_t i = 0; i < info.GetNumFileActions(); ++i)
233
result.emplace_back(*info.GetFileActionAtIndex(i));
234
return result;
235
}
236
237
static Environment::Envp FixupEnvironment(Environment env) {
238
#ifdef __ANDROID__
239
// If there is no PATH variable specified inside the environment then set the
240
// path to /system/bin. It is required because the default path used by
241
// execve() is wrong on android.
242
env.try_emplace("PATH", "/system/bin");
243
#endif
244
return env.getEnvp();
245
}
246
247
ForkLaunchInfo::ForkLaunchInfo(const ProcessLaunchInfo &info)
248
: separate_process_group(
249
info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)),
250
debug(info.GetFlags().Test(eLaunchFlagDebug)),
251
disable_aslr(info.GetFlags().Test(eLaunchFlagDisableASLR)),
252
wd(info.GetWorkingDirectory().GetPath()),
253
argv(info.GetArguments().GetConstArgumentVector()),
254
envp(FixupEnvironment(info.GetEnvironment())),
255
actions(MakeForkActions(info)) {}
256
257
HostProcess
258
ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,
259
Status &error) {
260
// A pipe used by the child process to report errors.
261
PipePosix pipe;
262
const bool child_processes_inherit = false;
263
error = pipe.CreateNew(child_processes_inherit);
264
if (error.Fail())
265
return HostProcess();
266
267
const ForkLaunchInfo fork_launch_info(launch_info);
268
269
::pid_t pid = ::fork();
270
if (pid == -1) {
271
// Fork failed
272
error.SetErrorStringWithFormatv("Fork failed with error message: {0}",
273
llvm::sys::StrError());
274
return HostProcess(LLDB_INVALID_PROCESS_ID);
275
}
276
if (pid == 0) {
277
// child process
278
pipe.CloseReadFileDescriptor();
279
ChildFunc(pipe.ReleaseWriteFileDescriptor(), fork_launch_info);
280
}
281
282
// parent process
283
284
pipe.CloseWriteFileDescriptor();
285
llvm::SmallString<0> buf;
286
size_t pos = 0;
287
ssize_t r = 0;
288
do {
289
pos += r;
290
buf.resize_for_overwrite(pos + 100);
291
r = llvm::sys::RetryAfterSignal(-1, read, pipe.GetReadFileDescriptor(),
292
buf.begin() + pos, buf.size() - pos);
293
} while (r > 0);
294
assert(r != -1);
295
296
buf.resize(pos);
297
if (buf.empty())
298
return HostProcess(pid); // No error. We're done.
299
300
error.SetErrorString(buf);
301
302
llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0);
303
304
return HostProcess();
305
}
306
307