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 // Implementation of the sandbox2::Regs class.
16
17 #include "sandboxed_api/sandbox2/regs.h"
18
19 #include <elf.h> // IWYU pragma: keep // used for NT_PRSTATUS inside an ifdef
20 #include <sys/ptrace.h>
21 #include <sys/uio.h> // IWYU pragma: keep // used for iovec
22
23 #include <cerrno>
24 #include <cstdint>
25
26 #include "absl/base/optimization.h"
27 #include "absl/status/status.h"
28 #include "absl/strings/str_cat.h"
29 #include "sandboxed_api/config.h"
30
31 namespace sandbox2 {
32
33 #ifndef NT_ARM_SYSTEM_CALL
34 #define NT_ARM_SYSTEM_CALL 0x404
35 #endif
36
Fetch()37 absl::Status Regs::Fetch() {
38 #ifdef SAPI_X86_64
39 if (ptrace(PTRACE_GETREGS, pid_, 0, &user_regs_) == -1L) {
40 return absl::ErrnoToStatus(
41 errno, absl::StrCat("ptrace(PTRACE_GETREGS, pid=", pid_, ") failed"));
42 }
43 #endif
44 if constexpr (sapi::host_cpu::IsPPC64LE() || sapi::host_cpu::IsArm64() ||
45 sapi::host_cpu::IsArm()) {
46 iovec pt_iov = {&user_regs_, sizeof(user_regs_)};
47
48 if (ptrace(PTRACE_GETREGSET, pid_, NT_PRSTATUS, &pt_iov) == -1L) {
49 return absl::ErrnoToStatus(
50 errno,
51 absl::StrCat("ptrace(PTRACE_GETREGSET, pid=", pid_, ") failed"));
52 }
53 if (pt_iov.iov_len != sizeof(user_regs_)) {
54 return absl::InternalError(absl::StrCat(
55 "ptrace(PTRACE_GETREGSET, pid=", pid_,
56 ") size returned: ", pt_iov.iov_len,
57 " different than sizeof(user_regs_): ", sizeof(user_regs_)));
58 }
59
60 // On AArch64, we are not done yet. Read the syscall number.
61 if constexpr (sapi::host_cpu::IsArm64()) {
62 iovec sys_iov = {&syscall_number_, sizeof(syscall_number_)};
63
64 if (ptrace(PTRACE_GETREGSET, pid_, NT_ARM_SYSTEM_CALL, &sys_iov) == -1L) {
65 return absl::ErrnoToStatus(
66 errno, absl::StrCat("ptrace(PTRACE_GETREGSET, pid=", pid_,
67 ", NT_ARM_SYSTEM_CALL)"));
68 }
69 if (sys_iov.iov_len != sizeof(syscall_number_)) {
70 return absl::InternalError(absl::StrCat(
71 "ptrace(PTRACE_GETREGSET, pid=", pid_,
72 ", NT_ARM_SYSTEM_CALL) size returned: ", sys_iov.iov_len,
73 " different than sizeof(syscall_number_): ",
74 sizeof(syscall_number_)));
75 }
76 }
77 }
78 return absl::OkStatus();
79 }
80
Store()81 absl::Status Regs::Store() {
82 #ifdef SAPI_X86_64
83 if (ptrace(PTRACE_SETREGS, pid_, 0, &user_regs_) == -1) {
84 return absl::ErrnoToStatus(
85 errno, absl::StrCat("ptrace(PTRACE_SETREGS, pid=", pid_, ")"));
86 }
87 #endif
88 if constexpr (sapi::host_cpu::IsPPC64LE() || sapi::host_cpu::IsArm64() ||
89 sapi::host_cpu::IsArm()) {
90 iovec pt_iov = {&user_regs_, sizeof(user_regs_)};
91
92 if (ptrace(PTRACE_SETREGSET, pid_, NT_PRSTATUS, &pt_iov) == -1L) {
93 return absl::ErrnoToStatus(
94 errno,
95 absl::StrCat("ptrace(PTRACE_SETREGSET, pid=", pid_, ") failed"));
96 }
97
98 // Store syscall number on AArch64.
99 if constexpr (sapi::host_cpu::IsArm64()) {
100 iovec sys_iov = {&syscall_number_, sizeof(syscall_number_)};
101
102 if (ptrace(PTRACE_SETREGSET, pid_, NT_ARM_SYSTEM_CALL, &sys_iov) == -1L) {
103 return absl::ErrnoToStatus(
104 errno, absl::StrCat("ptrace(PTRACE_SETREGSET, pid=", pid_,
105 ", NT_ARM_SYSTEM_CALL) failed"));
106 }
107 }
108 }
109 return absl::OkStatus();
110 }
111
SkipSyscallReturnValue(uintptr_t value)112 absl::Status Regs::SkipSyscallReturnValue(uintptr_t value) {
113 #if defined(SAPI_X86_64)
114 user_regs_.orig_rax = -1;
115 user_regs_.rax = value;
116 #elif defined(SAPI_PPC64_LE)
117 user_regs_.gpr[0] = -1;
118 user_regs_.gpr[3] = value;
119 #elif defined(SAPI_ARM64)
120 syscall_number_ = -1;
121 user_regs_.regs[0] = value;
122 #elif defined(SAPI_ARM)
123 user_regs_.orig_x0 = -1;
124 user_regs_.regs[7] = value;
125 #endif
126 return Store();
127 }
128
ToSyscall(sapi::cpu::Architecture syscall_arch) const129 Syscall Regs::ToSyscall(sapi::cpu::Architecture syscall_arch) const {
130 #if defined(SAPI_X86_64)
131 if (ABSL_PREDICT_TRUE(syscall_arch == sapi::cpu::kX8664)) {
132 auto syscall = user_regs_.orig_rax;
133 Syscall::Args args = {user_regs_.rdi, user_regs_.rsi, user_regs_.rdx,
134 user_regs_.r10, user_regs_.r8, user_regs_.r9};
135 auto sp = user_regs_.rsp;
136 auto ip = user_regs_.rip;
137 return Syscall(syscall_arch, syscall, args, pid_, sp, ip);
138 }
139 if (syscall_arch == sapi::cpu::kX86) {
140 auto syscall = user_regs_.orig_rax & 0xFFFFFFFF;
141 Syscall::Args args = {
142 user_regs_.rbx & 0xFFFFFFFF, user_regs_.rcx & 0xFFFFFFFF,
143 user_regs_.rdx & 0xFFFFFFFF, user_regs_.rsi & 0xFFFFFFFF,
144 user_regs_.rdi & 0xFFFFFFFF, user_regs_.rbp & 0xFFFFFFFF};
145 auto sp = user_regs_.rsp & 0xFFFFFFFF;
146 auto ip = user_regs_.rip & 0xFFFFFFFF;
147 return Syscall(syscall_arch, syscall, args, pid_, sp, ip);
148 }
149 #elif defined(SAPI_PPC64_LE)
150 if (ABSL_PREDICT_TRUE(syscall_arch == sapi::cpu::kPPC64LE)) {
151 auto syscall = user_regs_.gpr[0];
152 Syscall::Args args = {user_regs_.orig_gpr3, user_regs_.gpr[4],
153 user_regs_.gpr[5], user_regs_.gpr[6],
154 user_regs_.gpr[7], user_regs_.gpr[8]};
155 auto sp = user_regs_.gpr[1];
156 auto ip = user_regs_.nip;
157 return Syscall(syscall_arch, syscall, args, pid_, sp, ip);
158 }
159 #elif defined(SAPI_ARM64)
160 if (ABSL_PREDICT_TRUE(syscall_arch == sapi::cpu::kArm64)) {
161 Syscall::Args args = {
162 // First argument should be orig_x0, which is not available to ptrace on
163 // AArch64 (see
164 // https://undo.io/resources/arm64-vs-arm32-whats-different-linux-programmers/),
165 // as it will have been overwritten. For our use case, though, using
166 // regs[0] is fine, as we are always called on syscall entry and never
167 // on exit.
168 user_regs_.regs[0], user_regs_.regs[1], user_regs_.regs[2],
169 user_regs_.regs[3], user_regs_.regs[4], user_regs_.regs[5],
170 };
171 auto sp = user_regs_.sp;
172 auto ip = user_regs_.pc;
173 return Syscall(syscall_arch, syscall_number_, args, pid_, sp, ip);
174 }
175 #elif defined(SAPI_ARM)
176 if (ABSL_PREDICT_TRUE(syscall_arch == sapi::cpu::kArm)) {
177 Syscall::Args args = {
178 user_regs_.orig_x0, user_regs_.regs[1], user_regs_.regs[2],
179 user_regs_.regs[3], user_regs_.regs[4], user_regs_.regs[5],
180 };
181 auto sp = user_regs_.regs[13];
182 auto ip = user_regs_.pc;
183 return Syscall(syscall_arch, user_regs_.regs[7], args, pid_, sp, ip);
184 }
185 #endif
186 return Syscall(pid_);
187 }
188
GetReturnValue(sapi::cpu::Architecture syscall_arch) const189 int64_t Regs::GetReturnValue(sapi::cpu::Architecture syscall_arch) const {
190 #if defined(SAPI_X86_64)
191 if (ABSL_PREDICT_TRUE(syscall_arch == sapi::cpu::kX8664)) {
192 return static_cast<int64_t>(user_regs_.rax);
193 }
194 if (syscall_arch == sapi::cpu::kX86) {
195 return static_cast<int32_t>(user_regs_.rax & 0xFFFFFFFF);
196 }
197 #elif defined(SAPI_PPC64_LE)
198 if (ABSL_PREDICT_TRUE(syscall_arch == sapi::cpu::kPPC64LE)) {
199 return static_cast<int64_t>(user_regs_.gpr[3]);
200 }
201 #elif defined(SAPI_ARM64)
202 if (ABSL_PREDICT_TRUE(syscall_arch == sapi::cpu::kArm64)) {
203 return static_cast<int64_t>(user_regs_.regs[0]);
204 }
205 #elif defined(SAPI_ARM)
206 if (ABSL_PREDICT_TRUE(syscall_arch == sapi::cpu::kArm)) {
207 return static_cast<int32_t>(user_regs_.regs[0]);
208 }
209 #endif
210 return -1;
211 }
212
213 } // namespace sandbox2
214