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