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 | |
32 | using namespace lldb; |
33 | using 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 | |
39 | static 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 | |
53 | static 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 | |
66 | static 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 | |
81 | namespace { |
82 | struct ForkFileAction { |
83 | ForkFileAction(const FileAction &act); |
84 | |
85 | FileAction::Action action; |
86 | int fd; |
87 | std::string path; |
88 | int arg; |
89 | }; |
90 | |
91 | struct 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 | |
231 | ForkFileAction::ForkFileAction(const FileAction &act) |
232 | : action(act.GetAction()), fd(act.GetFD()), path(act.GetPath().str()), |
233 | arg(act.GetActionArgument()) {} |
234 | |
235 | static std::vector<ForkFileAction> |
236 | MakeForkActions(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 | |
243 | ForkLaunchInfo::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 | |
253 | HostProcess |
254 | ProcessLauncherPosixFork::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 | |