Simplify ptrace emulation and code style fixes

PiperOrigin-RevId: 369862187
Change-Id: Ia0759c320cde1c9e3798f0df5c2a0d50ca20fd71
This commit is contained in:
Christian Blichmann 2021-04-22 06:56:22 -07:00 committed by Copybara-Service
parent d9824dff16
commit ab7943abdc
5 changed files with 56 additions and 60 deletions

View File

@ -27,6 +27,7 @@ cc_library(
hdrs = ["ptrace_hook.h"], hdrs = ["ptrace_hook.h"],
copts = sapi_platform_copts(), copts = sapi_platform_copts(),
visibility = ["@org_gnu_libunwind//:__subpackages__"], visibility = ["@org_gnu_libunwind//:__subpackages__"],
deps = ["@com_google_absl//absl/strings"],
) )
cc_library( cc_library(

View File

@ -18,8 +18,9 @@ add_library(sandbox2_ptrace_hook STATIC
ptrace_hook.h ptrace_hook.h
) )
add_library(sandbox2::ptrace_hook ALIAS sandbox2_ptrace_hook) add_library(sandbox2::ptrace_hook ALIAS sandbox2_ptrace_hook)
target_link_libraries(sandbox2_ptrace_hook PRIVATE target_link_libraries(sandbox2_ptrace_hook
sapi::base PRIVATE sapi::base
PUBLIC absl::strings
) )
# sandboxed_api/sandbox2/unwind:unwind # sandboxed_api/sandbox2/unwind:unwind

View File

@ -21,80 +21,75 @@
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <vector>
// Maximum register struct size: 128 u64. namespace sandbox2 {
constexpr size_t kRegisterBufferSize = 128 * 8; namespace {
// Contains the register values in a ptrace specified format.
// This format is pretty opaque which is why we just forward
// the raw bytes (up to a certain limit).
static unsigned char register_values[kRegisterBufferSize];
static size_t n_register_values_bytes_used = 0;
// It should not be necessary to put this in a thread local storage as we // Register size is `long` for the supported architectures according to the
// do not support setting up the forkserver when there is more than one thread. // kernel.
// However there might be some edge-cases, so we do this just in case. using RegType = long; // NOLINT
thread_local bool emulate_ptrace = false; constexpr size_t kRegSize = sizeof(RegType);
void ArmPtraceEmulation() { emulate_ptrace = true; } // Contains the register values in a ptrace specified format. This format is
// pretty opaque which is why we just forward the raw bytes (up to a certain
// limit).
auto* g_registers = new std::vector<RegType>();
void InstallUserRegs(const char *ptr, size_t size) { // Whether ptrace() emulation is in effect. This can only be enabled (per
if (sizeof(register_values) < size) { // thread), never disabled.
fprintf(stderr, "install_user_regs: Got more bytes than supported (%lu)\n", thread_local bool g_emulate_ptrace = false;
size);
} else { } // namespace
memcpy(&register_values, ptr, size);
n_register_values_bytes_used = size; void EnablePtraceEmulationWithUserRegs(absl::string_view regs) {
} g_registers->resize((regs.size() + 1) / kRegSize);
memcpy(&g_registers->front(), regs.data(), regs.size());
g_emulate_ptrace = true;
} }
// Replaces the libc version of ptrace. // Replaces the libc version of ptrace.
// This wrapper makes use of process_vm_readv to read process memory instead of // This wrapper makes use of process_vm_readv to read process memory instead of
// issuing ptrace syscalls. Accesses to registers will be emulated, for this the // issuing ptrace syscalls. Accesses to registers will be emulated, for this the
// register values should be set via install_user_regs(). // register values should be set via EnablePtraceEmulationWithUserRegs().
// The emulation can be switched on using arm_ptrace_emulation().
extern "C" long int ptrace_wrapped( // NOLINT extern "C" long int ptrace_wrapped( // NOLINT
enum __ptrace_request request, pid_t pid, void *addr, void *data) { enum __ptrace_request request, pid_t pid, void* addr, void* data) {
// Register size is `long` for the supported architectures according to the if (!g_emulate_ptrace) {
// kernel.
using reg_type = long; // NOLINT
constexpr size_t reg_size = sizeof(reg_type);
if (!emulate_ptrace) {
return ptrace(request, pid, addr, data); return ptrace(request, pid, addr, data);
} }
switch (request) { switch (request) {
case PTRACE_PEEKDATA: { case PTRACE_PEEKDATA: {
long int read_data; // NOLINT long int read_data; // NOLINT
struct iovec local = {
.iov_base = &read_data,
.iov_len = sizeof(long int), // NOLINT
};
struct iovec remote = {
.iov_base = addr,
.iov_len = sizeof(long int), // NOLINT
};
struct iovec local, remote; if (process_vm_readv(pid, &local, 1, &remote, 1, 0) <= 0) {
local.iov_len = sizeof(long int); // NOLINT
local.iov_base = &read_data;
remote.iov_len = sizeof(long int); // NOLINT
remote.iov_base = addr;
if (process_vm_readv(pid, &local, 1, &remote, 1, 0) > 0) {
return read_data;
} else {
return -1; return -1;
} }
} break; return read_data;
case PTRACE_PEEKUSER: { }
uintptr_t next_offset = reinterpret_cast<uintptr_t>(addr) + reg_size; case PTRACE_PEEKUSER:
// Make sure read is in-bounds and aligned. // Make sure read is in-bounds and aligned.
if (next_offset <= n_register_values_bytes_used && if (uintptr_t offset = reinterpret_cast<uintptr_t>(addr);
reinterpret_cast<uintptr_t>(addr) % reg_size == 0) { offset + kRegSize > g_registers->size() * kRegSize ||
return reinterpret_cast<reg_type *>( offset % kRegSize != 0) {
&register_values)[reinterpret_cast<uintptr_t>(addr) / reg_size];
} else {
return -1; return -1;
} else {
return (*g_registers)[offset / kRegSize];
} }
} break; default:
default: { fprintf(stderr, "ptrace_wrapped(): operation not permitted: %d\n",
fprintf(stderr, "ptrace-wrapper: forbidden operation invoked: %d\n",
request); request);
_exit(1); _exit(1);
}
} }
return 0; return 0;
} }
} // namespace sandbox2

View File

@ -15,12 +15,13 @@
#ifndef SANDBOXED_API_SANDBOX2_UNWIND_PTRACE_HOOK_H_ #ifndef SANDBOXED_API_SANDBOX2_UNWIND_PTRACE_HOOK_H_
#define SANDBOXED_API_SANDBOX2_UNWIND_PTRACE_HOOK_H_ #define SANDBOXED_API_SANDBOX2_UNWIND_PTRACE_HOOK_H_
#include <cstddef> #include "absl/strings/string_view.h"
namespace sandbox2 {
// Sets the register values that the ptrace emulation will return. // Sets the register values that the ptrace emulation will return.
void InstallUserRegs(const char* ptr, size_t size); void EnablePtraceEmulationWithUserRegs(absl::string_view regs);
// Enables the ptrace emulation. } // namespace sandbox2
void ArmPtraceEmulation();
#endif // SANDBOXED_API_SANDBOX2_UNWIND_PTRACE_HOOK_H_ #endif // SANDBOXED_API_SANDBOX2_UNWIND_PTRACE_HOOK_H_

View File

@ -123,9 +123,7 @@ bool RunLibUnwindAndSymbolizer(Comms* comms) {
return false; return false;
} }
const std::string& data = setup.regs(); EnablePtraceEmulationWithUserRegs(setup.regs());
InstallUserRegs(data.c_str(), data.length());
ArmPtraceEmulation();
std::vector<uintptr_t> ips; std::vector<uintptr_t> ips;
std::vector<std::string> stack_trace = std::vector<std::string> stack_trace =