xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/sandbox2/util.cc (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "sandboxed_api/sandbox2/util.h"
16 
17 #include <sched.h>
18 #include <spawn.h>
19 #include <sys/ptrace.h>
20 #include <sys/resource.h>
21 #include <sys/uio.h>
22 #include <sys/wait.h>
23 #include <syscall.h>
24 #include <unistd.h>
25 
26 #include <cerrno>
27 #include <csetjmp>
28 #include <cstddef>
29 #include <cstdint>
30 #include <cstdlib>
31 #include <cstring>
32 #include <string>
33 #include <utility>
34 #include <vector>
35 
36 #include "absl/base/attributes.h"
37 #include "absl/base/macros.h"
38 #include "absl/base/optimization.h"
39 #include "absl/status/status.h"
40 #include "absl/status/statusor.h"
41 #include "absl/strings/ascii.h"
42 #include "absl/strings/escaping.h"
43 #include "absl/strings/str_cat.h"
44 #include "absl/strings/str_format.h"
45 #include "absl/strings/str_join.h"
46 #include "absl/strings/str_replace.h"
47 #include "absl/strings/str_split.h"
48 #include "absl/strings/string_view.h"
49 #include "sandboxed_api/config.h"
50 #include "sandboxed_api/util/file_helpers.h"
51 #include "sandboxed_api/util/fileops.h"
52 #include "sandboxed_api/util/path.h"
53 #include "sandboxed_api/util/raw_logging.h"
54 
55 namespace sandbox2::util {
56 
57 namespace file = ::sapi::file;
58 namespace file_util = ::sapi::file_util;
59 
60 namespace {
61 
ConcatenateAll(char * const * arr)62 std::string ConcatenateAll(char* const* arr) {
63   std::string result;
64   for (; *arr != nullptr; ++arr) {
65     size_t len = strlen(*arr);
66     result.append(*arr, len + 1);
67   }
68   return result;
69 }
70 
71 #ifdef __ELF__
72 extern "C" void __gcov_dump() ABSL_ATTRIBUTE_WEAK;
73 extern "C" void __gcov_flush() ABSL_ATTRIBUTE_WEAK;
74 extern "C" void __gcov_reset() ABSL_ATTRIBUTE_WEAK;
75 #endif
76 
ResetCoverageData()77 void ResetCoverageData() {
78 #ifdef __ELF__
79   if (&__gcov_reset != nullptr) {
80     __gcov_reset();
81   }
82 #endif
83 }
84 
85 }  // namespace
86 
DumpCoverageData()87 void DumpCoverageData() {
88 #ifdef __ELF__
89   if (&__gcov_dump != nullptr) {
90     SAPI_RAW_LOG(WARNING, "Flushing coverage data (dump)");
91     __gcov_dump();
92   } else if (&__gcov_flush != nullptr) {
93     SAPI_RAW_LOG(WARNING, "Flushing coverage data (flush)");
94     __gcov_flush();
95   }
96 #endif
97 }
98 
CharPtrArray(char * const * arr)99 CharPtrArray::CharPtrArray(char* const* arr) : content_(ConcatenateAll(arr)) {
100   for (auto it = content_.begin(); it != content_.end();
101        it += strlen(&*it) + 1) {
102     array_.push_back(&*it);
103   }
104   array_.push_back(nullptr);
105 }
106 
CharPtrArray(const std::vector<std::string> & vec)107 CharPtrArray::CharPtrArray(const std::vector<std::string>& vec)
108     : content_(absl::StrJoin(vec, absl::string_view("\0", 1))) {
109   size_t len = 0;
110   array_.reserve(vec.size() + 1);
111   for (const std::string& str : vec) {
112     array_.push_back(&content_[len]);
113     len += str.size() + 1;
114   }
115   array_.push_back(nullptr);
116 }
117 
FromStringVector(const std::vector<std::string> & vec)118 CharPtrArray CharPtrArray::FromStringVector(
119     const std::vector<std::string>& vec) {
120   return CharPtrArray(vec);
121 }
122 
ToStringVector() const123 std::vector<std::string> CharPtrArray::ToStringVector() const {
124   std::vector<std::string> result;
125   result.reserve(array_.size() - 1);
126   for (size_t i = 0; i < array_.size() - 1; ++i) {
127     result.push_back(array_[i]);
128   }
129   return result;
130 }
131 
GetProgName(pid_t pid)132 std::string GetProgName(pid_t pid) {
133   std::string fname = file::JoinPath("/proc", absl::StrCat(pid), "exe");
134   // Use ReadLink instead of RealPath, as for fd-based executables (e.g. created
135   // via memfd_create()) the RealPath will not work, as the destination file
136   // doesn't exist on the local file-system.
137   return file_util::fileops::Basename(file_util::fileops::ReadLink(fname));
138 }
139 
GetCmdLine(pid_t pid)140 std::string GetCmdLine(pid_t pid) {
141   std::string fname = file::JoinPath("/proc", absl::StrCat(pid), "cmdline");
142   std::string cmdline;
143   auto status =
144       sapi::file::GetContents(fname, &cmdline, sapi::file::Defaults());
145   if (!status.ok()) {
146     SAPI_RAW_LOG(WARNING, "%s", std::string(status.message()).c_str());
147     return "";
148   }
149   return absl::StrReplaceAll(cmdline, {{absl::string_view("\0", 1), " "}});
150 }
151 
GetProcStatusLine(int pid,const std::string & value)152 std::string GetProcStatusLine(int pid, const std::string& value) {
153   const std::string fname = absl::StrCat("/proc/", pid, "/status");
154   std::string procpidstatus;
155   auto status =
156       sapi::file::GetContents(fname, &procpidstatus, sapi::file::Defaults());
157   if (!status.ok()) {
158     SAPI_RAW_LOG(WARNING, "%s", std::string(status.message()).c_str());
159     return "";
160   }
161 
162   for (const auto& line : absl::StrSplit(procpidstatus, '\n')) {
163     std::pair<std::string, std::string> kv =
164         absl::StrSplit(line, absl::MaxSplits(':', 1));
165     SAPI_RAW_VLOG(3, "Key: '%s' Value: '%s'", kv.first.c_str(),
166                   kv.second.c_str());
167     if (kv.first == value) {
168       absl::StripLeadingAsciiWhitespace(&kv.second);
169       return std::move(kv.second);
170     }
171   }
172   SAPI_RAW_LOG(ERROR, "No '%s' field found in '%s'", value.c_str(),
173                fname.c_str());
174   return "";
175 }
176 
Syscall(long sys_no,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5,uintptr_t a6)177 long Syscall(long sys_no,  // NOLINT
178              uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4,
179              uintptr_t a5, uintptr_t a6) {
180   return syscall(sys_no, a1, a2, a3, a4, a5, a6);
181 }
182 
183 namespace {
184 
ChildFunc(void * arg)185 int ChildFunc(void* arg) {
186   auto* env_ptr = reinterpret_cast<jmp_buf*>(arg);
187   // Restore the old stack.
188   longjmp(*env_ptr, 1);
189 }
190 
191 // This code is inspired by base/process/launch_posix.cc in the Chromium source.
192 // There are a few things to be careful of here:
193 // - Make sure the stack_buf is below the env_ptr to please FORTIFY_SOURCE.
194 // - Make sure the stack_buf is not too far away from the real stack to please
195 // ASAN. If they are too far away, a warning is printed. This means not only
196 // that the temporary stack buffer needs to also be on the stack, but also that
197 // we need to disable ASAN for this function, to prevent it from being placed on
198 // the fake ASAN stack.
199 // - Make sure that the buffer is aligned to whatever is required by the CPU.
200 ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS
201 ABSL_ATTRIBUTE_NOINLINE
CloneAndJump(int flags,jmp_buf * env_ptr)202 pid_t CloneAndJump(int flags, jmp_buf* env_ptr) {
203   uint8_t stack_buf[PTHREAD_STACK_MIN] ABSL_CACHELINE_ALIGNED;
204   static_assert(sapi::host_cpu::IsX8664() || sapi::host_cpu::IsPPC64LE() ||
205                     sapi::host_cpu::IsArm64() || sapi::host_cpu::IsArm(),
206                 "Host CPU architecture not supported, see config.h");
207   // Stack grows down.
208   void* stack = stack_buf + sizeof(stack_buf);
209   int r = clone(&ChildFunc, stack, flags, env_ptr, nullptr, nullptr, nullptr);
210   if (r == -1) {
211     SAPI_RAW_PLOG(ERROR, "clone()");
212   }
213   return r;
214 }
215 
216 }  // namespace
217 
ForkWithFlags(int flags)218 pid_t ForkWithFlags(int flags) {
219   const int unsupported_flags = CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID |
220                                 CLONE_PARENT_SETTID | CLONE_SETTLS | CLONE_VM;
221   if (flags & unsupported_flags) {
222     SAPI_RAW_LOG(ERROR, "ForkWithFlags used with unsupported flag");
223     return -1;
224   }
225 
226   jmp_buf env;
227   if (setjmp(env) == 0) {
228     return CloneAndJump(flags, &env);
229   }
230 
231   // Child.
232   return 0;
233 }
234 
CreateMemFd(int * fd,const char * name)235 bool CreateMemFd(int* fd, const char* name) {
236   // Usually defined in linux/memfd.h. Define it here to avoid dependency on
237   // UAPI headers.
238   constexpr uintptr_t kMfdCloseOnExec = 0x0001;
239   constexpr uintptr_t kMfdAllowSealing = 0x0002;
240   int tmp_fd = Syscall(__NR_memfd_create, reinterpret_cast<uintptr_t>(name),
241                        kMfdCloseOnExec | kMfdAllowSealing);
242   if (tmp_fd < 0) {
243     if (errno == ENOSYS) {
244       SAPI_RAW_LOG(ERROR,
245                    "This system does not seem to support the memfd_create()"
246                    " syscall. Try running on a newer kernel.");
247     } else {
248       SAPI_RAW_PLOG(ERROR, "Could not create tmp file '%s'", name);
249     }
250     return false;
251   }
252   *fd = tmp_fd;
253   return true;
254 }
255 
Communicate(const std::vector<std::string> & argv,const std::vector<std::string> & envv,std::string * output)256 absl::StatusOr<int> Communicate(const std::vector<std::string>& argv,
257                                 const std::vector<std::string>& envv,
258                                 std::string* output) {
259   int cout_pipe[2];
260   posix_spawn_file_actions_t action;
261 
262   if (pipe(cout_pipe) == -1) {
263     return absl::ErrnoToStatus(errno, "creating pipe");
264   }
265   file_util::fileops::FDCloser cout_closer{cout_pipe[1]};
266 
267   posix_spawn_file_actions_init(&action);
268   struct ActionCleanup {
269     ~ActionCleanup() { posix_spawn_file_actions_destroy(action_); }
270     posix_spawn_file_actions_t* action_;
271   } action_cleanup{&action};
272 
273   // Redirect both stdout and stderr to stdout to our pipe.
274   posix_spawn_file_actions_addclose(&action, cout_pipe[0]);
275   posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1);
276   posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 2);
277   posix_spawn_file_actions_addclose(&action, cout_pipe[1]);
278 
279   CharPtrArray args = CharPtrArray::FromStringVector(argv);
280   CharPtrArray envp = CharPtrArray::FromStringVector(envv);
281 
282   pid_t pid;
283   if (posix_spawnp(&pid, args.array()[0], &action, nullptr,
284                    const_cast<char**>(args.data()),
285                    const_cast<char**>(envp.data())) != 0) {
286     return absl::ErrnoToStatus(errno, "posix_spawnp()");
287   }
288 
289   // Close child end of the pipe.
290   cout_closer.Close();
291 
292   std::string buffer(1024, '\0');
293   for (;;) {
294     int bytes_read =
295         TEMP_FAILURE_RETRY(read(cout_pipe[0], &buffer[0], buffer.length()));
296     if (bytes_read < 0) {
297       return absl::ErrnoToStatus(errno, "reading from cout pipe");
298     }
299     if (bytes_read == 0) {
300       break;  // Nothing left to read
301     }
302     absl::StrAppend(output, absl::string_view(buffer.data(), bytes_read));
303   }
304 
305   int status;
306   SAPI_RAW_PCHECK(TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)) == pid,
307                   "Waiting for subprocess");
308   return WEXITSTATUS(status);
309 }
310 
GetSignalName(int signo)311 std::string GetSignalName(int signo) {
312   constexpr absl::string_view kSignalNames[] = {
313       "SIG_0",   "SIGHUP",  "SIGINT",     "SIGQUIT", "SIGILL",    "SIGTRAP",
314       "SIGABRT", "SIGBUS",  "SIGFPE",     "SIGKILL", "SIGUSR1",   "SIGSEGV",
315       "SIGUSR2", "SIGPIPE", "SIGALRM",    "SIGTERM", "SIGSTKFLT", "SIGCHLD",
316       "SIGCONT", "SIGSTOP", "SIGTSTP",    "SIGTTIN", "SIGTTOU",   "SIGURG",
317       "SIGXCPU", "SIGXFSZ", "SIGVTALARM", "SIGPROF", "SIGWINCH",  "SIGIO",
318       "SIGPWR",  "SIGSYS"};
319 
320   if (signo >= SIGRTMIN && signo <= SIGRTMAX) {
321     return absl::StrFormat("SIGRT-%d [%d]", signo - SIGRTMIN, signo);
322   }
323   if (signo < 0 || signo >= static_cast<int>(ABSL_ARRAYSIZE(kSignalNames))) {
324     return absl::StrFormat("UNKNOWN_SIGNAL [%d]", signo);
325   }
326   return absl::StrFormat("%s [%d]", kSignalNames[signo], signo);
327 }
328 
GetRlimitName(int resource)329 std::string GetRlimitName(int resource) {
330   switch (resource) {
331     case RLIMIT_AS:
332       return "RLIMIT_AS";
333     case RLIMIT_FSIZE:
334       return "RLIMIT_FSIZE";
335     case RLIMIT_NOFILE:
336       return "RLIMIT_NOFILE";
337     case RLIMIT_CPU:
338       return "RLIMIT_CPU";
339     case RLIMIT_CORE:
340       return "RLIMIT_CORE";
341     default:
342       return absl::StrCat("UNKNOWN: ", resource);
343   }
344 }
345 
GetPtraceEventName(int event)346 std::string GetPtraceEventName(int event) {
347 #if !defined(PTRACE_EVENT_STOP)
348 #define PTRACE_EVENT_STOP 128
349 #endif
350 
351   switch (event) {
352     case PTRACE_EVENT_FORK:
353       return "PTRACE_EVENT_FORK";
354     case PTRACE_EVENT_VFORK:
355       return "PTRACE_EVENT_VFORK";
356     case PTRACE_EVENT_CLONE:
357       return "PTRACE_EVENT_CLONE";
358     case PTRACE_EVENT_EXEC:
359       return "PTRACE_EVENT_EXEC";
360     case PTRACE_EVENT_VFORK_DONE:
361       return "PTRACE_EVENT_VFORK_DONE";
362     case PTRACE_EVENT_EXIT:
363       return "PTRACE_EVENT_EXIT";
364     case PTRACE_EVENT_SECCOMP:
365       return "PTRACE_EVENT_SECCOMP";
366     case PTRACE_EVENT_STOP:
367       return "PTRACE_EVENT_STOP";
368     default:
369       return absl::StrCat("UNKNOWN: ", event);
370   }
371 }
372 
ReadCPathFromPid(pid_t pid,uintptr_t ptr)373 absl::StatusOr<std::string> ReadCPathFromPid(pid_t pid, uintptr_t ptr) {
374   std::string path(PATH_MAX, '\0');
375   iovec local_iov[] = {{&path[0], path.size()}};
376 
377   static const uintptr_t page_size = getpagesize();
378   static const uintptr_t page_mask = ~(page_size - 1);
379   // See 'man process_vm_readv' for details on how to read NUL-terminated
380   // strings with this syscall.
381   size_t len1 = ((ptr + page_size) & page_mask) - ptr;
382   len1 = (len1 > path.size()) ? path.size() : len1;
383   size_t len2 = (path.size() <= len1) ? 0UL : path.size() - len1;
384   // Second iov is wrapping around to NULL ptr.
385   if ((ptr + len1) < ptr) {
386     len2 = 0UL;
387   }
388 
389   iovec remote_iov[] = {
390       {reinterpret_cast<void*>(ptr), len1},
391       {reinterpret_cast<void*>(ptr + len1), len2},
392   };
393 
394   SAPI_RAW_VLOG(4, "ReadCPathFromPid (iovec): len1: %zu, len2: %zu", len1,
395                 len2);
396   if (process_vm_readv(pid, local_iov, ABSL_ARRAYSIZE(local_iov), remote_iov,
397                        ABSL_ARRAYSIZE(remote_iov), 0) < 0) {
398     return absl::ErrnoToStatus(
399         errno,
400         absl::StrFormat("process_vm_readv() failed for PID: %d at address: %#x",
401                         pid, reinterpret_cast<uintptr_t>(ptr)));
402   }
403 
404   // Check for whether there's a NUL byte in the buffer. If not, it's an
405   // incorrect path (or >PATH_MAX).
406   auto pos = path.find('\0');
407   if (pos == std::string::npos) {
408     return absl::FailedPreconditionError(absl::StrCat(
409         "No NUL-byte inside the C string '", absl::CHexEscape(path), "'"));
410   }
411   path.resize(pos);
412   return path;
413 }
414 
Execveat(int dirfd,const char * pathname,const char * const argv[],const char * const envp[],int flags,uintptr_t extra_arg)415 int Execveat(int dirfd, const char* pathname, const char* const argv[],
416              const char* const envp[], int flags, uintptr_t extra_arg) {
417   // Flush coverage data prior to exec.
418   if (extra_arg == 0) {
419     DumpCoverageData();
420   }
421   int res = syscall(__NR_execveat, static_cast<uintptr_t>(dirfd),
422                     reinterpret_cast<uintptr_t>(pathname),
423                     reinterpret_cast<uintptr_t>(argv),
424                     reinterpret_cast<uintptr_t>(envp),
425                     static_cast<uintptr_t>(flags), extra_arg);
426   // Reset coverage data if exec fails as the counters have been already dumped.
427   if (extra_arg == 0) {
428     ResetCoverageData();
429   }
430   return res;
431 }
432 
433 }  // namespace sandbox2::util
434