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