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 <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#endif
31
32using namespace lldb;
33using namespace lldb_private;
34
35// Begin code running in the child process
36// NB: This code needs to be async-signal safe, since we're invoking fork from
37// multithreaded contexts.
38
39static void write_string(int error_fd, const char *str) {
40 int r = write(fd: error_fd, buf: str, n: strlen(s: 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, str: operation);
47 write_string(error_fd, str: " failed: ");
48 // strerror is not guaranteed to be async-signal safe, but it usually is.
49 write_string(error_fd, str: strerror(errnum: err));
50 _exit(status: 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(persona: personality_get_current);
57 if (value == -1)
58 ExitWithError(error_fd, operation: "personality get");
59
60 value = personality(persona: ADDR_NO_RANDOMIZE | value);
61 if (value == -1)
62 ExitWithError(error_fd, operation: "personality set");
63#endif
64}
65
66static void DupDescriptor(int error_fd, const char *file, int fd, int flags) {
67 int target_fd = FileSystem::Instance().Open(path: file, flags, mode: 0666);
68
69 if (target_fd == -1)
70 ExitWithError(error_fd, operation: "DupDescriptor-open");
71
72 if (target_fd == fd)
73 return;
74
75 if (::dup2(fd: target_fd, fd2: fd) == -1)
76 ExitWithError(error_fd, operation: "DupDescriptor-dup2");
77
78 ::close(fd: 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} // namespace
112
113[[noreturn]] static void ChildFunc(int error_fd, const ForkLaunchInfo &info) {
114 if (info.separate_process_group) {
115 if (setpgid(pid: 0, pgid: 0) != 0)
116 ExitWithError(error_fd, operation: "setpgid");
117 }
118
119 for (const ForkFileAction &action : info.actions) {
120 switch (action.action) {
121 case FileAction::eFileActionClose:
122 if (close(fd: action.fd) != 0)
123 ExitWithError(error_fd, operation: "close");
124 break;
125 case FileAction::eFileActionDuplicate:
126 if (action.fd != action.arg) {
127 if (dup2(fd: action.fd, fd2: action.arg) == -1)
128 ExitWithError(error_fd, operation: "dup2");
129 } else {
130 if (fcntl(fd: action.fd, F_SETFD,
131 fcntl(fd: action.fd, F_GETFD) & ~FD_CLOEXEC) == -1)
132 ExitWithError(error_fd, operation: "fcntl");
133 }
134 break;
135 case FileAction::eFileActionOpen:
136 DupDescriptor(error_fd, file: action.path.c_str(), fd: action.fd, flags: action.arg);
137 break;
138 case FileAction::eFileActionNone:
139 break;
140 }
141 }
142
143 // Change working directory
144 if (!info.wd.empty() && 0 != ::chdir(path: info.wd.c_str()))
145 ExitWithError(error_fd, operation: "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: &set) != 0 ||
154 pthread_sigmask(SIG_SETMASK, newmask: &set, oldmask: nullptr) != 0)
155 ExitWithError(error_fd, operation: "pthread_sigmask");
156
157 if (info.debug) {
158 // Do not inherit setgid powers.
159 if (setgid(getgid()) != 0)
160 ExitWithError(error_fd, operation: "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(path: 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(str: iter->path().substr(pos: 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(x: fd);
184 }
185 for (int file_to_close : files_to_close)
186 close(fd: 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: fd);
193 }
194
195 // Start tracing this child that is about to exec.
196#ifdef _AIX
197 if (ptrace64(PT_TRACE_ME, 0, 0, 0, nullptr) == -1)
198#else
199 if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1)
200#endif
201 ExitWithError(error_fd, operation: "ptrace");
202 }
203
204 // Execute. We should never return...
205 execve(path: info.executable.c_str(), argv: const_cast<char *const *>(info.argv),
206 envp: 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 daemon
211 // can hold a write handle on the executable even after it has finished
212 // uploading it. This state lasts only a short time and happens only when
213 // there are many concurrent adb commands being issued, such as when
214 // running the test suite. (The file remains open when someone does an "adb
215 // 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 it
217 // one more go.
218 usleep(useconds: 50000);
219 execve(path: info.executable.c_str(), argv: const_cast<char *const *>(info.argv),
220 envp: info.envp);
221 }
222#endif
223
224 // ...unless exec fails. In which case we definitely need to end the child
225 // here.
226 ExitWithError(error_fd, operation: "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(args: *info.GetFileActionAtIndex(idx: i));
240 return result;
241}
242
243ForkLaunchInfo::ForkLaunchInfo(const ProcessLaunchInfo &info)
244 : separate_process_group(
245 info.GetFlags().Test(bit: eLaunchFlagLaunchInSeparateProcessGroup)),
246 debug(info.GetFlags().Test(bit: eLaunchFlagDebug)),
247 disable_aslr(info.GetFlags().Test(bit: 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
253HostProcess
254ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,
255 Status &error) {
256 // A pipe used by the child process to report errors.
257 PipePosix pipe;
258 const bool child_processes_inherit = false;
259 error = pipe.CreateNew(child_process_inherit: child_processes_inherit);
260 if (error.Fail())
261 return HostProcess();
262
263 const ForkLaunchInfo fork_launch_info(launch_info);
264
265 ::pid_t pid = ::fork();
266 if (pid == -1) {
267 // Fork failed
268 error = Status::FromErrorStringWithFormatv(
269 format: "Fork failed with error message: {0}", args: llvm::sys::StrError());
270 return HostProcess(LLDB_INVALID_PROCESS_ID);
271 }
272 if (pid == 0) {
273 // child process
274 pipe.CloseReadFileDescriptor();
275 ChildFunc(error_fd: pipe.ReleaseWriteFileDescriptor(), info: fork_launch_info);
276 }
277
278 // parent process
279
280 pipe.CloseWriteFileDescriptor();
281 llvm::SmallString<0> buf;
282 size_t pos = 0;
283 ssize_t r = 0;
284 do {
285 pos += r;
286 buf.resize_for_overwrite(N: pos + 100);
287 r = llvm::sys::RetryAfterSignal(Fail: -1, F&: read, As: pipe.GetReadFileDescriptor(),
288 As: buf.begin() + pos, As: buf.size() - pos);
289 } while (r > 0);
290 assert(r != -1);
291
292 buf.resize(N: pos);
293 if (buf.empty())
294 return HostProcess(pid); // No error. We're done.
295
296 error = Status(buf.str().str());
297
298 llvm::sys::RetryAfterSignal(Fail: -1, F&: waitpid, As: pid, As: nullptr, As: 0);
299
300 return HostProcess();
301}
302

source code of lldb/source/Host/posix/ProcessLauncherPosixFork.cpp