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/unwind/ptrace_hook.h"
16
17 #include <elf.h> // For NT_PRSTATUS
18 #include <sys/ptrace.h>
19 #include <sys/uio.h>
20 #include <syscall.h>
21 #include <unistd.h>
22
23 #include <cstdint>
24 #include <cstdio>
25 #include <cstdlib>
26 #include <cstring>
27 #include <vector>
28
29 #include "absl/strings/string_view.h"
30 #include "sandboxed_api/sandbox2/util/syscall_trap.h"
31
32 using PtraceRequest = __ptrace_request;
33
34 namespace sandbox2 {
35 namespace {
36
37 // Register size is `long` for the supported architectures according to the
38 // kernel.
39 using RegType = long; // NOLINT
40 constexpr size_t kRegSize = sizeof(RegType);
41
42 // Contains the register values in a ptrace specified format. This format is
43 // pretty opaque which is why we just forward the raw bytes (up to a certain
44 // limit).
45 auto* g_registers = new std::vector<RegType>();
46 pid_t g_pid;
47 int g_mem_fd;
48
49 // Hooks ptrace.
50 // This wrapper makes use of process_vm_readv to read process memory instead of
51 // issuing ptrace syscalls. Accesses to registers will be emulated, for this the
52 // register values should be set via EnablePtraceEmulationWithUserRegs().
ptrace_hook(PtraceRequest request,pid_t pid,void * addr,void * data)53 long int ptrace_hook( // NOLINT
54 PtraceRequest request, pid_t pid, void* addr, void* data) {
55 switch (request) {
56 case PTRACE_PEEKDATA: {
57 if (pid != g_pid) {
58 return -1;
59 }
60 RegType read_data;
61 if (pread(g_mem_fd, &read_data, sizeof(read_data),
62 reinterpret_cast<uintptr_t>(addr)) != sizeof(read_data)) {
63 return -1;
64 }
65 *reinterpret_cast<RegType*>(data) = read_data;
66 break;
67 }
68 case PTRACE_PEEKUSER: {
69 // Make sure read is in-bounds and aligned.
70 auto offset = reinterpret_cast<uintptr_t>(addr);
71 if (offset + kRegSize > g_registers->size() * kRegSize ||
72 offset % kRegSize != 0) {
73 return -1;
74 }
75 *reinterpret_cast<RegType*>(data) = (*g_registers)[offset / kRegSize];
76 break;
77 }
78 case PTRACE_GETREGSET: {
79 // Only return general-purpose registers.
80 if (auto kind = reinterpret_cast<uintptr_t>(addr); kind != NT_PRSTATUS) {
81 return -1;
82 }
83 auto reg_set = reinterpret_cast<iovec*>(data);
84 if (reg_set->iov_len > g_registers->size() * kRegSize) {
85 return -1;
86 }
87 memcpy(reg_set->iov_base, g_registers->data(), reg_set->iov_len);
88 break;
89 }
90 default:
91 fprintf(stderr, "ptrace_hook(): operation not permitted: %d\n", request);
92 abort();
93 }
94 return 0;
95 }
96
97 } // namespace
98
EnablePtraceEmulationWithUserRegs(pid_t pid,absl::string_view regs,int mem_fd)99 void EnablePtraceEmulationWithUserRegs(pid_t pid, absl::string_view regs,
100 int mem_fd) {
101 g_pid = pid;
102 g_mem_fd = mem_fd;
103 g_registers->resize((regs.size() + 1) / kRegSize);
104 memcpy(&g_registers->front(), regs.data(), regs.size());
105 SyscallTrap::Install([](int nr, SyscallTrap::Args args, uintptr_t* rv) {
106 if (nr != __NR_ptrace) {
107 return false;
108 }
109 *rv = ptrace_hook(
110 static_cast<PtraceRequest>(args[0]), static_cast<pid_t>(args[1]),
111 reinterpret_cast<void*>(args[2]), reinterpret_cast<void*>(args[3]));
112 return true;
113 });
114 }
115
116 } // namespace sandbox2
117