• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <fcntl.h>
18 #include <linux/limits.h>
19 #include <sched.h>
20 #include <spawn.h>
21 #include <sys/ptrace.h>
22 #include <sys/resource.h>
23 #include <sys/socket.h>
24 #include <sys/uio.h>
25 #include <sys/wait.h>
26 #include <syscall.h>
27 #include <unistd.h>
28 
29 #include <algorithm>
30 #include <cerrno>
31 #include <csetjmp>
32 #include <cstddef>
33 #include <cstdint>
34 #include <cstdlib>
35 #include <cstring>
36 #include <string>
37 #include <utility>
38 #include <vector>
39 
40 #include "absl/algorithm/container.h"
41 #include "absl/base/attributes.h"
42 #include "absl/base/macros.h"
43 #include "absl/base/optimization.h"
44 #include "absl/status/status.h"
45 #include "absl/status/statusor.h"
46 #include "absl/strings/ascii.h"
47 #include "absl/strings/escaping.h"
48 #include "absl/strings/str_cat.h"
49 #include "absl/strings/str_format.h"
50 #include "absl/strings/str_join.h"
51 #include "absl/strings/str_replace.h"
52 #include "absl/strings/str_split.h"
53 #include "absl/strings/string_view.h"
54 #include "absl/types/span.h"
55 #include "sandboxed_api/config.h"
56 #include "sandboxed_api/util/file_helpers.h"
57 #include "sandboxed_api/util/fileops.h"
58 #include "sandboxed_api/util/path.h"
59 #include "sandboxed_api/util/raw_logging.h"
60 #include "sandboxed_api/util/status_macros.h"
61 
62 namespace sandbox2 {
63 namespace util {
64 
65 namespace file = ::sapi::file;
66 namespace file_util = ::sapi::file_util;
67 
68 namespace {
69 
ConcatenateAll(char * const * arr)70 std::string ConcatenateAll(char* const* arr) {
71   std::string result;
72   for (; *arr != nullptr; ++arr) {
73     size_t len = strlen(*arr);
74     result.append(*arr, len + 1);
75   }
76   return result;
77 }
78 
79 #ifdef __ELF__
80 extern "C" void __gcov_dump() ABSL_ATTRIBUTE_WEAK;
81 extern "C" void __gcov_flush() ABSL_ATTRIBUTE_WEAK;
82 extern "C" void __gcov_reset() ABSL_ATTRIBUTE_WEAK;
83 #endif
84 
ResetCoverageData()85 void ResetCoverageData() {
86 #ifdef __ELF__
87   if (&__gcov_reset != nullptr) {
88     __gcov_reset();
89   }
90 #endif
91 }
92 
93 }  // namespace
94 
DumpCoverageData()95 void DumpCoverageData() {
96 #ifdef __ELF__
97   if (&__gcov_dump != nullptr) {
98     SAPI_RAW_LOG(WARNING, "Flushing coverage data (dump)");
99     __gcov_dump();
100   } else if (&__gcov_flush != nullptr) {
101     SAPI_RAW_LOG(WARNING, "Flushing coverage data (flush)");
102     __gcov_flush();
103   }
104 #endif
105 }
106 
CharPtrArray(char * const * arr)107 CharPtrArray::CharPtrArray(char* const* arr) : content_(ConcatenateAll(arr)) {
108   for (auto it = content_.begin(); it != content_.end();
109        it += strlen(&*it) + 1) {
110     array_.push_back(&*it);
111   }
112   array_.push_back(nullptr);
113 }
114 
CharPtrArray(const std::vector<std::string> & vec)115 CharPtrArray::CharPtrArray(const std::vector<std::string>& vec)
116     : content_(absl::StrJoin(vec, absl::string_view("\0", 1))) {
117   size_t len = 0;
118   array_.reserve(vec.size() + 1);
119   for (const std::string& str : vec) {
120     array_.push_back(&content_[len]);
121     len += str.size() + 1;
122   }
123   array_.push_back(nullptr);
124 }
125 
FromStringVector(const std::vector<std::string> & vec)126 CharPtrArray CharPtrArray::FromStringVector(
127     const std::vector<std::string>& vec) {
128   return CharPtrArray(vec);
129 }
130 
ToStringVector() const131 std::vector<std::string> CharPtrArray::ToStringVector() const {
132   std::vector<std::string> result;
133   result.reserve(array_.size() - 1);
134   for (size_t i = 0; i < array_.size() - 1; ++i) {
135     result.push_back(array_[i]);
136   }
137   return result;
138 }
139 
GetProgName(pid_t pid)140 std::string GetProgName(pid_t pid) {
141   std::string fname = file::JoinPath("/proc", absl::StrCat(pid), "exe");
142   // Use ReadLink instead of RealPath, as for fd-based executables (e.g. created
143   // via memfd_create()) the RealPath will not work, as the destination file
144   // doesn't exist on the local file-system.
145   return file_util::fileops::Basename(file_util::fileops::ReadLink(fname));
146 }
147 
GetResolvedFdLink(pid_t pid,uint32_t fd)148 absl::StatusOr<std::string> GetResolvedFdLink(pid_t pid, uint32_t fd) {
149   // The proc/PID/fd directory contains links for all of that process' file
150   // descriptors. They'll show up as more informative strings (paths, sockets).
151   std::string fd_path = absl::StrFormat("/proc/%u/fd/%u", pid, fd);
152   std::string result(PATH_MAX, '\0');
153   ssize_t size = readlink(fd_path.c_str(), &result[0], PATH_MAX);
154   if (size < 0) {
155     return absl::ErrnoToStatus(size, "failed to read link");
156   }
157   result.resize(size);
158   return result;
159 }
160 
GetCmdLine(pid_t pid)161 std::string GetCmdLine(pid_t pid) {
162   std::string fname = file::JoinPath("/proc", absl::StrCat(pid), "cmdline");
163   std::string cmdline;
164   auto status =
165       sapi::file::GetContents(fname, &cmdline, sapi::file::Defaults());
166   if (!status.ok()) {
167     SAPI_RAW_LOG(WARNING, "%s", std::string(status.message()).c_str());
168     return "";
169   }
170   return absl::StrReplaceAll(cmdline, {{absl::string_view("\0", 1), " "}});
171 }
172 
GetProcStatusLine(int pid,const std::string & value)173 std::string GetProcStatusLine(int pid, const std::string& value) {
174   const std::string fname = absl::StrCat("/proc/", pid, "/status");
175   std::string procpidstatus;
176   auto status =
177       sapi::file::GetContents(fname, &procpidstatus, sapi::file::Defaults());
178   if (!status.ok()) {
179     SAPI_RAW_LOG(WARNING, "%s", std::string(status.message()).c_str());
180     return "";
181   }
182 
183   for (const auto& line : absl::StrSplit(procpidstatus, '\n')) {
184     std::pair<std::string, std::string> kv =
185         absl::StrSplit(line, absl::MaxSplits(':', 1));
186     SAPI_RAW_VLOG(3, "Key: '%s' Value: '%s'", kv.first.c_str(),
187                   kv.second.c_str());
188     if (kv.first == value) {
189       absl::StripLeadingAsciiWhitespace(&kv.second);
190       return std::move(kv.second);
191     }
192   }
193   SAPI_RAW_LOG(ERROR, "No '%s' field found in '%s'", value.c_str(),
194                fname.c_str());
195   return "";
196 }
197 
Syscall(long sys_no,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5,uintptr_t a6)198 long Syscall(long sys_no,  // NOLINT
199              uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4,
200              uintptr_t a5, uintptr_t a6) {
201   return syscall(sys_no, a1, a2, a3, a4, a5, a6);
202 }
203 
204 namespace {
205 
ChildFunc(void * arg)206 int ChildFunc(void* arg) {
207   auto* env_ptr = reinterpret_cast<jmp_buf*>(arg);
208   // Restore the old stack.
209   longjmp(*env_ptr, 1);
210 }
211 
212 // This code is inspired by base/process/launch_posix.cc in the Chromium source.
213 // There are a few things to be careful of here:
214 // - Make sure the stack_buf is below the env_ptr to please FORTIFY_SOURCE.
215 // - Make sure the stack_buf is not too far away from the real stack to please
216 // ASAN. If they are too far away, a warning is printed. This means not only
217 // that the temporary stack buffer needs to also be on the stack, but also that
218 // we need to disable ASAN for this function, to prevent it from being placed on
219 // the fake ASAN stack.
220 // - Make sure that the buffer is aligned to whatever is required by the CPU.
221 ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS
222 ABSL_ATTRIBUTE_NOINLINE
CloneAndJump(int flags,jmp_buf * env_ptr)223 pid_t CloneAndJump(int flags, jmp_buf* env_ptr) {
224   uint8_t stack_buf[PTHREAD_STACK_MIN] ABSL_CACHELINE_ALIGNED;
225   static_assert(sapi::host_cpu::IsX8664() || sapi::host_cpu::IsPPC64LE() ||
226                     sapi::host_cpu::IsArm64() || sapi::host_cpu::IsArm(),
227                 "Host CPU architecture not supported, see config.h");
228   // Stack grows down.
229   void* stack = stack_buf + sizeof(stack_buf);
230   int r = clone(&ChildFunc, stack, flags, env_ptr, nullptr, nullptr, nullptr);
231   if (r == -1) {
232     SAPI_RAW_PLOG(ERROR, "clone()");
233   }
234   return r;
235 }
236 
237 }  // namespace
238 
ForkWithFlags(int flags)239 pid_t ForkWithFlags(int flags) {
240   const int unsupported_flags = CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID |
241                                 CLONE_PARENT_SETTID | CLONE_SETTLS | CLONE_VM;
242   if (flags & unsupported_flags) {
243     SAPI_RAW_LOG(ERROR, "ForkWithFlags used with unsupported flag");
244     return -1;
245   }
246 
247   jmp_buf env;
248   if (setjmp(env) == 0) {
249     return CloneAndJump(flags, &env);
250   }
251 
252   // Child.
253   return 0;
254 }
255 
CreateMemFd(int * fd,const char * name)256 bool CreateMemFd(int* fd, const char* name) {
257   // Usually defined in linux/memfd.h. Define it here to avoid dependency on
258   // UAPI headers.
259   constexpr uintptr_t kMfdCloseOnExec = 0x0001;
260   constexpr uintptr_t kMfdAllowSealing = 0x0002;
261   int tmp_fd = Syscall(__NR_memfd_create, reinterpret_cast<uintptr_t>(name),
262                        kMfdCloseOnExec | kMfdAllowSealing);
263   if (tmp_fd < 0) {
264     if (errno == ENOSYS) {
265       SAPI_RAW_LOG(ERROR,
266                    "This system does not seem to support the memfd_create()"
267                    " syscall. Try running on a newer kernel.");
268     } else {
269       SAPI_RAW_PLOG(ERROR, "Could not create tmp file '%s'", name);
270     }
271     return false;
272   }
273   *fd = tmp_fd;
274   return true;
275 }
276 
Communicate(const std::vector<std::string> & argv,const std::vector<std::string> & envv,std::string * output)277 absl::StatusOr<int> Communicate(const std::vector<std::string>& argv,
278                                 const std::vector<std::string>& envv,
279                                 std::string* output) {
280   int cout_pipe[2];
281   posix_spawn_file_actions_t action;
282 
283   if (pipe(cout_pipe) == -1) {
284     return absl::ErrnoToStatus(errno, "creating pipe");
285   }
286   file_util::fileops::FDCloser cout_closer{cout_pipe[1]};
287 
288   posix_spawn_file_actions_init(&action);
289   struct ActionCleanup {
290     ~ActionCleanup() { posix_spawn_file_actions_destroy(action_); }
291     posix_spawn_file_actions_t* action_;
292   } action_cleanup{&action};
293 
294   // Redirect both stdout and stderr to stdout to our pipe.
295   posix_spawn_file_actions_addclose(&action, cout_pipe[0]);
296   posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1);
297   posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 2);
298   posix_spawn_file_actions_addclose(&action, cout_pipe[1]);
299 
300   CharPtrArray args = CharPtrArray::FromStringVector(argv);
301   CharPtrArray envp = CharPtrArray::FromStringVector(envv);
302 
303   pid_t pid;
304   if (posix_spawnp(&pid, args.array()[0], &action, nullptr,
305                    const_cast<char**>(args.data()),
306                    const_cast<char**>(envp.data())) != 0) {
307     return absl::ErrnoToStatus(errno, "posix_spawnp()");
308   }
309 
310   // Close child end of the pipe.
311   cout_closer.Close();
312 
313   std::string buffer(1024, '\0');
314   for (;;) {
315     int bytes_read =
316         TEMP_FAILURE_RETRY(read(cout_pipe[0], &buffer[0], buffer.length()));
317     if (bytes_read < 0) {
318       return absl::ErrnoToStatus(errno, "reading from cout pipe");
319     }
320     if (bytes_read == 0) {
321       break;  // Nothing left to read
322     }
323     absl::StrAppend(output, absl::string_view(buffer.data(), bytes_read));
324   }
325 
326   int status;
327   SAPI_RAW_PCHECK(TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)) == pid,
328                   "Waiting for subprocess");
329   return WEXITSTATUS(status);
330 }
331 
GetSignalName(int signo)332 std::string GetSignalName(int signo) {
333   constexpr absl::string_view kSignalNames[] = {
334       "SIG_0",   "SIGHUP",  "SIGINT",     "SIGQUIT", "SIGILL",    "SIGTRAP",
335       "SIGABRT", "SIGBUS",  "SIGFPE",     "SIGKILL", "SIGUSR1",   "SIGSEGV",
336       "SIGUSR2", "SIGPIPE", "SIGALRM",    "SIGTERM", "SIGSTKFLT", "SIGCHLD",
337       "SIGCONT", "SIGSTOP", "SIGTSTP",    "SIGTTIN", "SIGTTOU",   "SIGURG",
338       "SIGXCPU", "SIGXFSZ", "SIGVTALARM", "SIGPROF", "SIGWINCH",  "SIGIO",
339       "SIGPWR",  "SIGSYS"};
340 
341   if (signo >= SIGRTMIN && signo <= SIGRTMAX) {
342     return absl::StrFormat("SIGRT-%d [%d]", signo - SIGRTMIN, signo);
343   }
344   if (signo < 0 || signo >= static_cast<int>(ABSL_ARRAYSIZE(kSignalNames))) {
345     return absl::StrFormat("UNKNOWN_SIGNAL [%d]", signo);
346   }
347   return absl::StrFormat("%s [%d]", kSignalNames[signo], signo);
348 }
349 
GetAddressFamily(int addr_family)350 std::string GetAddressFamily(int addr_family) {
351   // Taken from definitions in `socket.h`. Each family's index in the array is
352   // also its integer value.
353   constexpr absl::string_view kAddressFamilies[] = {
354       "AF_UNSPEC",     "AF_UNIX",      "AF_INET",     "AF_AX25",
355       "AF_IPX",        "AF_APPLETALK", "AF_NETROM",   "AF_BRIDGE",
356       "AF_ATMPVC",     "AF_X25",       "AF_INET6",    "AF_ROSE",
357       "AF_DECnet",     "AF_NETBEUI",   "AF_SECURITY", "AF_KEY",
358       "AF_NETLINK",    "AF_PACKET",    "AF_ASH",      "AF_ECONET",
359       "AF_ATMSVC",     "AF_RDS",       "AF_SNA",      "AF_IRDA",
360       "AF_PPPOX",      "AF_WANPIPE",   "AF_LLC",      "AF_IB",
361       "AF_MPLS",       "AF_CAN",       "AF_TIPC",     "AF_BLUETOOTH",
362       "AF_IUCV",       "AF_RXRPC",     "AF_ISDN",     "AF_PHONET",
363       "AF_IEEE802154", "AF_CAIF",      "AF_ALG",      "AF_NFC",
364       "AF_VSOCK",      "AF_KCM",       "AF_QIPCRTR",  "AF_SMC",
365       "AF_XDP",        "AF_MCTP"};
366 
367   if (addr_family < 0 && addr_family >= ABSL_ARRAYSIZE(kAddressFamilies)) {
368     return absl::StrFormat("UNKNOWN_ADDRESS_FAMILY [%d]", addr_family);
369   }
370   return std::string(kAddressFamilies[addr_family]);
371 }
372 
GetRlimitName(int resource)373 std::string GetRlimitName(int resource) {
374   switch (resource) {
375     case RLIMIT_AS:
376       return "RLIMIT_AS";
377     case RLIMIT_FSIZE:
378       return "RLIMIT_FSIZE";
379     case RLIMIT_NOFILE:
380       return "RLIMIT_NOFILE";
381     case RLIMIT_CPU:
382       return "RLIMIT_CPU";
383     case RLIMIT_CORE:
384       return "RLIMIT_CORE";
385     default:
386       return absl::StrCat("UNKNOWN: ", resource);
387   }
388 }
389 
GetPtraceEventName(int event)390 std::string GetPtraceEventName(int event) {
391 #if !defined(PTRACE_EVENT_STOP)
392 #define PTRACE_EVENT_STOP 128
393 #endif
394 
395   switch (event) {
396     case PTRACE_EVENT_FORK:
397       return "PTRACE_EVENT_FORK";
398     case PTRACE_EVENT_VFORK:
399       return "PTRACE_EVENT_VFORK";
400     case PTRACE_EVENT_CLONE:
401       return "PTRACE_EVENT_CLONE";
402     case PTRACE_EVENT_EXEC:
403       return "PTRACE_EVENT_EXEC";
404     case PTRACE_EVENT_VFORK_DONE:
405       return "PTRACE_EVENT_VFORK_DONE";
406     case PTRACE_EVENT_EXIT:
407       return "PTRACE_EVENT_EXIT";
408     case PTRACE_EVENT_SECCOMP:
409       return "PTRACE_EVENT_SECCOMP";
410     case PTRACE_EVENT_STOP:
411       return "PTRACE_EVENT_STOP";
412     default:
413       return absl::StrCat("UNKNOWN: ", event);
414   }
415 }
416 
417 namespace {
418 
419 // Transfer memory via process_vm_readv/process_vm_writev in page-aligned
420 // chunks.
ProcessVmTransfer(bool is_read,pid_t pid,uintptr_t ptr,absl::Span<char> data)421 absl::StatusOr<size_t> ProcessVmTransfer(bool is_read, pid_t pid, uintptr_t ptr,
422                                          absl::Span<char> data) {
423   // Input sanity checks.
424   if (data.empty()) {
425     return 0;
426   }
427 
428   size_t total_bytes_transferred = 0;
429   while (!data.empty()) {
430     iovec local_iov = {data.data(), data.size()};
431     iovec remote_iov = {reinterpret_cast<void*>(ptr), data.size()};
432     ssize_t bytes_transferred =
433         is_read ? process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0)
434                 : process_vm_writev(pid, &local_iov, 1, &remote_iov, 1, 0);
435     if (bytes_transferred == 0) {
436       if (total_bytes_transferred > 0) {
437         return total_bytes_transferred;
438       }
439       return absl::NotFoundError(absl::StrFormat(
440           "Transfer was unsuccessful for PID: %d at address: %#x", pid, ptr));
441     } else if (bytes_transferred < 0) {
442       if (total_bytes_transferred > 0) {
443         return total_bytes_transferred;
444       }
445       return absl::ErrnoToStatus(
446           errno,
447           absl::StrFormat("transfer() failed for PID: %d at address: %#x", pid,
448                           ptr));
449     }
450     ptr += bytes_transferred;
451     data = data.subspan(bytes_transferred, data.size() - bytes_transferred);
452     total_bytes_transferred += bytes_transferred;
453   }
454   return total_bytes_transferred;
455 }
456 
457 // Transfer memory via process_vm_readv.
ProcessVmReadInSplitChunks(pid_t pid,uintptr_t ptr,absl::Span<char> data)458 absl::StatusOr<size_t> ProcessVmReadInSplitChunks(pid_t pid, uintptr_t ptr,
459                                                   absl::Span<char> data) {
460   static const uintptr_t page_size = getpagesize();
461   static const uintptr_t page_mask = page_size - 1;
462 
463   // Input sanity checks.
464   if (data.empty()) {
465     return 0;
466   }
467 
468   // Repeatedly call process_vm_readv/writev in IOV_MAX iovec chunks.
469   size_t total_bytes_transferred = 0;
470   while (!data.empty()) {
471     // Stores all the necessary iovecs to move memory.
472     iovec local_iov = {data.data(), 0};
473     // Stores all the necessary iovecs to move memory.
474     std::vector<iovec> remote_iov;
475     // Each iovec should be contained to a single page.
476     while (!data.empty() && remote_iov.size() < IOV_MAX) {
477       size_t size_in_page = page_size - (ptr & page_mask);
478       size_t chunk_size = std::min(data.size(), size_in_page);
479       remote_iov.push_back({reinterpret_cast<void*>(ptr), chunk_size});
480       local_iov.iov_len += chunk_size;
481       ptr += chunk_size;
482       data = data.subspan(chunk_size, data.size() - chunk_size);
483     }
484     ssize_t bytes_transferred = process_vm_readv(
485         pid, &local_iov, 1, remote_iov.data(), remote_iov.size(), 0);
486     if (bytes_transferred == 0) {
487       if (total_bytes_transferred == 0) {
488         return absl::NotFoundError(absl::StrFormat(
489             "Transfer was unsuccessful for PID: %d at address: %#x", pid, ptr));
490       }
491       break;
492     } else if (bytes_transferred < 0) {
493       return absl::ErrnoToStatus(
494           errno,
495           absl::StrFormat("transfer() failed for PID: %d at address: %#x", pid,
496                           ptr));
497     }
498     total_bytes_transferred += bytes_transferred;
499     if (bytes_transferred < local_iov.iov_len) {
500       // Read to end of a mapped region (short of full transfer).
501       break;
502     }
503   }
504   return total_bytes_transferred;
505 }
506 
507 // Open /proc/pid/mem file descriptor.
OpenProcMem(pid_t pid,bool is_read)508 absl::StatusOr<file_util::fileops::FDCloser> OpenProcMem(pid_t pid,
509                                                          bool is_read) {
510   auto path = absl::StrFormat("/proc/%d/mem", pid);
511   auto closer = file_util::fileops::FDCloser(
512       open(path.c_str(), is_read ? O_RDONLY : O_WRONLY));
513   if (closer.get() == -1) {
514     return absl::ErrnoToStatus(
515         errno, absl::StrFormat("open() failed for PID: %d", pid));
516   }
517   return closer;
518 }
519 
ProcMemTransfer(bool is_read,pid_t pid,uintptr_t ptr,absl::Span<char> data)520 absl::StatusOr<size_t> ProcMemTransfer(bool is_read, pid_t pid, uintptr_t ptr,
521                                        absl::Span<char> data) {
522   if (data.empty()) {
523     return 0;
524   }
525 
526   SAPI_ASSIGN_OR_RETURN(file_util::fileops::FDCloser fd_closer,
527                         OpenProcMem(pid, is_read));
528   size_t total_bytes_transferred = 0;
529   while (!data.empty()) {
530     ssize_t bytes_transfered =
531         is_read ? pread(fd_closer.get(), data.data(), data.size(), ptr)
532                 : pwrite(fd_closer.get(), data.data(), data.size(), ptr);
533     if (bytes_transfered == 0) {
534       if (total_bytes_transferred == 0) {
535         return absl::NotFoundError(absl::StrFormat(
536             "Transfer was unsuccessful for PID: %d at address: %#x", pid, ptr));
537       }
538       break;
539     } else if (bytes_transfered < 0) {
540       if (total_bytes_transferred > 0) {
541         // Return number of bytes transferred until this error or end.
542         break;
543       }
544       // pread/write of /proc/<pid>mem returns EIO when ptr is unmapped.
545       if (errno == EIO) {
546         // Emulate returned error code from process_vm_readv.
547         errno = EFAULT;
548       }
549       return absl::ErrnoToStatus(
550           errno,
551           absl::StrFormat("transfer() failed for PID: %d at address: %#x", pid,
552                           ptr));
553     }
554     ptr += bytes_transfered;
555     data = data.subspan(bytes_transfered, data.size() - bytes_transfered);
556     total_bytes_transferred += bytes_transfered;
557   }
558   return total_bytes_transferred;
559 }
560 
CheckIfProcessVmTransferWorks()561 bool CheckIfProcessVmTransferWorks() {
562   // Fall-back to pread("/proc/$pid/mem") if process_vm_readv is unavailable.
563   static bool process_vm_transfer_works = []() {
564     constexpr char kMagic = 42;
565     char src = kMagic;
566     char dst = 0;
567     absl::StatusOr<size_t> read = internal::ReadBytesFromPidWithReadv(
568         getpid(), reinterpret_cast<uintptr_t>(&src), absl::MakeSpan(&dst, 1));
569     if (!read.ok() || *read != 1 || dst != kMagic) {
570       SAPI_RAW_LOG(WARNING,
571                    "This system does not seem to support the process_vm_readv()"
572                    " or process_vm_writev syscall. Falling back to transfers"
573                    " via /proc/pid/mem.");
574       return false;
575     }
576     return true;
577   }();
578   return process_vm_transfer_works;
579 }
580 
581 }  // namespace
582 
583 namespace internal {
584 
ReadBytesFromPidWithReadv(pid_t pid,uintptr_t ptr,absl::Span<char> data)585 absl::StatusOr<size_t> ReadBytesFromPidWithReadv(pid_t pid, uintptr_t ptr,
586                                                  absl::Span<char> data) {
587   return ProcessVmTransfer(true, pid, ptr, data);
588 }
589 
WriteBytesToPidWithWritev(pid_t pid,uintptr_t ptr,absl::Span<const char> data)590 absl::StatusOr<size_t> WriteBytesToPidWithWritev(pid_t pid, uintptr_t ptr,
591                                                  absl::Span<const char> data) {
592   return ProcessVmTransfer(
593       false, pid, ptr,
594       absl::MakeSpan(const_cast<char*>(data.data()), data.size()));
595 }
596 
ReadBytesFromPidWithProcMem(pid_t pid,uintptr_t ptr,absl::Span<char> data)597 absl::StatusOr<size_t> ReadBytesFromPidWithProcMem(pid_t pid, uintptr_t ptr,
598                                                    absl::Span<char> data) {
599   return ProcMemTransfer(true, pid, ptr, data);
600 }
601 
ReadBytesFromPidWithReadvInSplitChunks(pid_t pid,uintptr_t ptr,absl::Span<char> data)602 absl::StatusOr<size_t> ReadBytesFromPidWithReadvInSplitChunks(
603     pid_t pid, uintptr_t ptr, absl::Span<char> data) {
604   return ProcessVmReadInSplitChunks(pid, ptr, data);
605 }
606 
WriteBytesToPidWithProcMem(pid_t pid,uintptr_t ptr,absl::Span<const char> data)607 absl::StatusOr<size_t> WriteBytesToPidWithProcMem(pid_t pid, uintptr_t ptr,
608                                                   absl::Span<const char> data) {
609   return ProcMemTransfer(
610       false, pid, ptr,
611       absl::MakeSpan(const_cast<char*>(data.data()), data.size()));
612 }
613 
614 }  // namespace internal
615 
ReadBytesFromPidInto(pid_t pid,uintptr_t ptr,absl::Span<char> data)616 absl::StatusOr<size_t> ReadBytesFromPidInto(pid_t pid, uintptr_t ptr,
617                                             absl::Span<char> data) {
618   if (CheckIfProcessVmTransferWorks()) {
619     return internal::ReadBytesFromPidWithReadv(pid, ptr, data);
620   } else {
621     return internal::ReadBytesFromPidWithProcMem(pid, ptr, data);
622   }
623 }
624 
WriteBytesToPidFrom(pid_t pid,uintptr_t ptr,absl::Span<const char> data)625 absl::StatusOr<size_t> WriteBytesToPidFrom(pid_t pid, uintptr_t ptr,
626                                            absl::Span<const char> data) {
627   if (CheckIfProcessVmTransferWorks()) {
628     return internal::WriteBytesToPidWithWritev(pid, ptr, data);
629   } else {
630     return internal::WriteBytesToPidWithProcMem(pid, ptr, data);
631   }
632 }
633 
ReadBytesFromPid(pid_t pid,uintptr_t ptr,size_t size)634 absl::StatusOr<std::vector<uint8_t>> ReadBytesFromPid(pid_t pid, uintptr_t ptr,
635                                                       size_t size) {
636   // Allocate enough bytes to hold the entire size.
637   std::vector<uint8_t> bytes(size, 0);
638   size_t result;
639   if (CheckIfProcessVmTransferWorks()) {
640     SAPI_ASSIGN_OR_RETURN(
641         result,
642         ProcessVmReadInSplitChunks(
643             pid, ptr,
644             absl::MakeSpan(reinterpret_cast<char*>(bytes.data()), size)));
645   } else {
646     SAPI_ASSIGN_OR_RETURN(
647         result,
648         internal::ReadBytesFromPidWithProcMem(
649             pid, ptr,
650             absl::MakeSpan(reinterpret_cast<char*>(bytes.data()), size)));
651   }
652   // Ensure only successfully read bytes are returned.
653   bytes.resize(result);
654   return bytes;
655 }
656 
ReadCPathFromPid(pid_t pid,uintptr_t ptr)657 absl::StatusOr<std::string> ReadCPathFromPid(pid_t pid, uintptr_t ptr) {
658   SAPI_ASSIGN_OR_RETURN(std::vector<uint8_t> bytes,
659                         ReadBytesFromPid(pid, ptr, PATH_MAX));
660   auto null_pos = absl::c_find(bytes, '\0');
661   std::string path(bytes.begin(), null_pos);
662   if (null_pos == bytes.end()) {
663     return absl::FailedPreconditionError(
664         absl::StrFormat("path '%s' is too long", absl::CHexEscape(path)));
665   }
666   return path;
667 }
668 
Execveat(int dirfd,const char * pathname,const char * const argv[],const char * const envp[],int flags,uintptr_t extra_arg)669 int Execveat(int dirfd, const char* pathname, const char* const argv[],
670              const char* const envp[], int flags, uintptr_t extra_arg) {
671   // Flush coverage data prior to exec.
672   if (extra_arg == 0) {
673     DumpCoverageData();
674   }
675   int res = syscall(__NR_execveat, static_cast<uintptr_t>(dirfd),
676                     reinterpret_cast<uintptr_t>(pathname),
677                     reinterpret_cast<uintptr_t>(argv),
678                     reinterpret_cast<uintptr_t>(envp),
679                     static_cast<uintptr_t>(flags), extra_arg);
680   // Reset coverage data if exec fails as the counters have been already dumped.
681   if (extra_arg == 0) {
682     ResetCoverageData();
683   }
684   return res;
685 }
686 
IsRunningInSandbox2()687 absl::StatusOr<bool> IsRunningInSandbox2() {
688   // Check if the kMagicSyscallNo syscall is available.
689   int result = Syscall(sandbox2::internal::kMagicSyscallNo);
690   if (result == 0) {
691     // If this happens, then someone has implemented the kMagicSyscallNo syscall
692     // and it is returning 0.
693     return absl::InternalError(
694         "kMagicSyscallNo syscall succeeded unexpectedly");
695   }
696 
697   // The caller is not running under a sandbox2.
698   if (errno == ENOSYS) {
699     return false;
700   }
701 
702   // The caller is running under a sandbox2.
703   if (errno == sandbox2::internal::kMagicSyscallErr) {
704     return true;
705   }
706 
707   // An unexpected errno was returned.
708   return absl::InternalError(absl::StrFormat(
709       "Unexpected errno for syscall kMagicSyscallNo: %d", errno));
710 }
711 
712 }  // namespace util
713 }  // namespace sandbox2
714