sandboxed-api/sandboxed_api/sandbox2/regs.cc
Christian Blichmann dbaf95c724 Move utility code into sandboxed_api/util
This change should make it less confusing where utility code comes from.
Having it in two places made sense when we were debating whether to publish
Sandbox2 separately, but not any longer.

Follow-up changes will move `sandbox2/util.h` and rename the remaining
`sandbox2/util` folder.

PiperOrigin-RevId: 351601640
Change-Id: I6256845261f610e590c25e2c59851cc51da2d778
2021-01-13 09:25:52 -08:00

263 lines
9.3 KiB
C++

// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Implementation of the sandbox2::Regs class.
#include "sandboxed_api/sandbox2/regs.h"
#include <elf.h> // IWYU pragma: keep // used for NT_PRSTATUS inside an ifdef
#include <linux/audit.h>
#include <sys/ptrace.h>
#include <sys/uio.h> // IWYU pragma: keep // used for iovec
#include <cerrno>
#include "absl/base/macros.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "sandboxed_api/config.h"
#include "sandboxed_api/util/strerror.h"
namespace sandbox2 {
using ::sapi::StrError;
#ifndef NT_ARM_SYSTEM_CALL
#define NT_ARM_SYSTEM_CALL 0x404
#endif
absl::Status Regs::Fetch() {
#ifdef SAPI_X86_64
if (ptrace(PTRACE_GETREGS, pid_, 0, &user_regs_) == -1L) {
return absl::InternalError(absl::StrCat("ptrace(PTRACE_GETREGS, pid=", pid_,
") failed: ", StrError(errno)));
}
#endif
if constexpr (sapi::host_cpu::IsPPC64LE() || sapi::host_cpu::IsArm64() ||
sapi::host_cpu::IsArm()) {
iovec pt_iov = {&user_regs_, sizeof(user_regs_)};
if (ptrace(PTRACE_GETREGSET, pid_, NT_PRSTATUS, &pt_iov) == -1L) {
return absl::InternalError(
absl::StrCat("ptrace(PTRACE_GETREGSET, pid=", pid_,
") failed: ", StrError(errno)));
}
if (pt_iov.iov_len != sizeof(user_regs_)) {
return absl::InternalError(absl::StrCat(
"ptrace(PTRACE_GETREGSET, pid=", pid_,
") size returned: ", pt_iov.iov_len,
" different than sizeof(user_regs_): ", sizeof(user_regs_)));
}
// On AArch64, we are not done yet. Read the syscall number.
if constexpr (sapi::host_cpu::IsArm64()) {
iovec sys_iov = {&syscall_number_, sizeof(syscall_number_)};
if (ptrace(PTRACE_GETREGSET, pid_, NT_ARM_SYSTEM_CALL, &sys_iov) == -1L) {
return absl::InternalError(
absl::StrCat("ptrace(PTRACE_GETREGSET, pid=", pid_,
", NT_ARM_SYSTEM_CALL) failed: ", StrError(errno)));
}
if (sys_iov.iov_len != sizeof(syscall_number_)) {
return absl::InternalError(absl::StrCat(
"ptrace(PTRACE_GETREGSET, pid=", pid_,
", NT_ARM_SYSTEM_CALL) size returned: ", sys_iov.iov_len,
" different than sizeof(syscall_number_): ",
sizeof(syscall_number_)));
}
}
}
return absl::OkStatus();
}
absl::Status Regs::Store() {
#ifdef SAPI_X86_64
if (ptrace(PTRACE_SETREGS, pid_, 0, &user_regs_) == -1) {
return absl::InternalError(absl::StrCat("ptrace(PTRACE_SETREGS, pid=", pid_,
") failed: ", StrError(errno)));
}
#endif
if constexpr (sapi::host_cpu::IsPPC64LE() || sapi::host_cpu::IsArm64() ||
sapi::host_cpu::IsArm()) {
iovec pt_iov = {&user_regs_, sizeof(user_regs_)};
if (ptrace(PTRACE_SETREGSET, pid_, NT_PRSTATUS, &pt_iov) == -1L) {
return absl::InternalError(
absl::StrCat("ptrace(PTRACE_SETREGSET, pid=", pid_,
") failed: ", StrError(errno)));
}
// Store syscall number on AArch64.
if constexpr (sapi::host_cpu::IsArm64()) {
iovec sys_iov = {&syscall_number_, sizeof(syscall_number_)};
if (ptrace(PTRACE_SETREGSET, pid_, NT_ARM_SYSTEM_CALL, &sys_iov) == -1L) {
return absl::InternalError(
absl::StrCat("ptrace(PTRACE_SETREGSET, pid=", pid_,
", NT_ARM_SYSTEM_CALL) failed: ", StrError(errno)));
}
}
}
return absl::OkStatus();
}
absl::Status Regs::SkipSyscallReturnValue(uintptr_t value) {
#if defined(SAPI_X86_64)
user_regs_.orig_rax = -1;
user_regs_.rax = value;
#elif defined(SAPI_PPC64_LE)
user_regs_.gpr[0] = -1;
user_regs_.gpr[3] = value;
#elif defined(SAPI_ARM64)
user_regs_.regs[0] = -1;
syscall_number_ = value;
#elif defined(SAPI_ARM)
user_regs_.orig_x0 = -1;
user_regs_.regs[7] = value;
#endif
return Store();
}
Syscall Regs::ToSyscall(sapi::cpu::Architecture syscall_arch) const {
#if defined(SAPI_X86_64)
if (ABSL_PREDICT_TRUE(syscall_arch == sapi::cpu::kX8664)) {
auto syscall = user_regs_.orig_rax;
Syscall::Args args = {user_regs_.rdi, user_regs_.rsi, user_regs_.rdx,
user_regs_.r10, user_regs_.r8, user_regs_.r9};
auto sp = user_regs_.rsp;
auto ip = user_regs_.rip;
return Syscall(syscall_arch, syscall, args, pid_, sp, ip);
}
if (syscall_arch == sapi::cpu::kX86) {
auto syscall = user_regs_.orig_rax & 0xFFFFFFFF;
Syscall::Args args = {
user_regs_.rbx & 0xFFFFFFFF, user_regs_.rcx & 0xFFFFFFFF,
user_regs_.rdx & 0xFFFFFFFF, user_regs_.rsi & 0xFFFFFFFF,
user_regs_.rdi & 0xFFFFFFFF, user_regs_.rbp & 0xFFFFFFFF};
auto sp = user_regs_.rsp & 0xFFFFFFFF;
auto ip = user_regs_.rip & 0xFFFFFFFF;
return Syscall(syscall_arch, syscall, args, pid_, sp, ip);
}
#elif defined(SAPI_PPC64_LE)
if (ABSL_PREDICT_TRUE(syscall_arch == sapi::cpu::kPPC64LE)) {
auto syscall = user_regs_.gpr[0];
Syscall::Args args = {user_regs_.orig_gpr3, user_regs_.gpr[4],
user_regs_.gpr[5], user_regs_.gpr[6],
user_regs_.gpr[7], user_regs_.gpr[8]};
auto sp = user_regs_.gpr[1];
auto ip = user_regs_.nip;
return Syscall(syscall_arch, syscall, args, pid_, sp, ip);
}
#elif defined(SAPI_ARM64)
if (ABSL_PREDICT_TRUE(syscall_arch == sapi::cpu::kArm64)) {
Syscall::Args args = {
// First argument should be orig_x0, which is not available to ptrace on
// AArch64 (see
// https://undo.io/resources/arm64-vs-arm32-whats-different-linux-programmers/),
// as it will have been overwritten. For our use case, though, using
// regs[0] is fine, as we are always called on syscall entry and never
// on exit.
user_regs_.regs[0], user_regs_.regs[1], user_regs_.regs[2],
user_regs_.regs[3], user_regs_.regs[4], user_regs_.regs[5],
};
auto sp = user_regs_.sp;
auto ip = user_regs_.pc;
return Syscall(syscall_arch, syscall_number_, args, pid_, sp, ip);
}
#elif defined(SAPI_ARM)
if (ABSL_PREDICT_TRUE(syscall_arch == sapi::cpu::kArm)) {
Syscall::Args args = {
user_regs_.orig_x0, user_regs_.regs[1], user_regs_.regs[2],
user_regs_.regs[3], user_regs_.regs[4], user_regs_.regs[5],
};
auto sp = user_regs_.regs[13];
auto ip = user_regs_.pc;
return Syscall(syscall_arch, user_regs_.regs[7], args, pid_, sp, ip);
}
#endif
return Syscall(pid_);
}
void Regs::StoreRegisterValuesInProtobuf(RegisterValues* values) const {
#if defined(SAPI_X86_64)
RegisterX8664* regs = values->mutable_register_x86_64();
regs->set_r15(user_regs_.r15);
regs->set_r14(user_regs_.r14);
regs->set_r13(user_regs_.r13);
regs->set_r12(user_regs_.r12);
regs->set_rbp(user_regs_.rbp);
regs->set_rbx(user_regs_.rbx);
regs->set_r11(user_regs_.r11);
regs->set_r10(user_regs_.r10);
regs->set_r9(user_regs_.r9);
regs->set_r8(user_regs_.r8);
regs->set_rax(user_regs_.rax);
regs->set_rcx(user_regs_.rcx);
regs->set_rdx(user_regs_.rdx);
regs->set_rsi(user_regs_.rsi);
regs->set_rdi(user_regs_.rdi);
regs->set_orig_rax(user_regs_.orig_rax);
regs->set_rip(user_regs_.rip);
regs->set_cs(user_regs_.cs);
regs->set_eflags(user_regs_.eflags);
regs->set_rsp(user_regs_.rsp);
regs->set_ss(user_regs_.ss);
regs->set_fs_base(user_regs_.fs_base);
regs->set_gs_base(user_regs_.gs_base);
regs->set_ds(user_regs_.ds);
regs->set_es(user_regs_.es);
regs->set_fs(user_regs_.fs);
regs->set_gs(user_regs_.gs);
#elif defined(SAPI_PPC64_LE)
RegisterPowerpc64* regs = values->mutable_register_powerpc64();
for (int i = 0; i < ABSL_ARRAYSIZE(user_regs_.gpr); ++i) {
regs->add_gpr(user_regs_.gpr[i]);
}
regs->set_nip(user_regs_.nip);
regs->set_msr(user_regs_.msr);
regs->set_orig_gpr3(user_regs_.orig_gpr3);
regs->set_ctr(user_regs_.ctr);
regs->set_link(user_regs_.link);
regs->set_xer(user_regs_.xer);
regs->set_ccr(user_regs_.ccr);
regs->set_softe(user_regs_.softe);
regs->set_trap(user_regs_.trap);
regs->set_dar(user_regs_.dar);
regs->set_dsisr(user_regs_.dsisr);
regs->set_result(user_regs_.result);
regs->set_zero0(user_regs_.zero0);
regs->set_zero1(user_regs_.zero1);
regs->set_zero2(user_regs_.zero2);
regs->set_zero3(user_regs_.zero3);
#elif defined(SAPI_ARM64)
RegisterAarch64* regs = values->mutable_register_aarch64();
for (int i = 0; i < ABSL_ARRAYSIZE(user_regs_.regs); ++i) {
regs->add_regs(user_regs_.regs[i]);
}
regs->set_sp(user_regs_.sp);
regs->set_pc(user_regs_.pc);
regs->set_pstate(user_regs_.pstate);
#elif defined(SAPI_ARM)
RegisterArm* regs = values->mutable_register_arm();
for (int i = 0; i < ABSL_ARRAYSIZE(user_regs_.regs); ++i) {
regs->add_regs(user_regs_.regs[i]);
}
regs->set_pc(user_regs_.pc);
regs->set_cpsr(user_regs_.cpsr);
regs->set_orig_x0(user_regs_.orig_x0);
#endif
}
} // namespace sandbox2