• 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/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