xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/sandbox2/unwind/ptrace_hook.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/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