Handle S2 unwinding by trapping ptrace

PiperOrigin-RevId: 491893277
Change-Id: I427a2e485173c73fffead43e29511460c58c4f04
This commit is contained in:
Wiktor Garbacz 2022-11-30 05:59:36 -08:00 committed by Copybara-Service
parent bd5769d40a
commit ee58a410d9
9 changed files with 52 additions and 72 deletions

View File

@ -77,7 +77,7 @@ endif()
if(SAPI_DOWNLOAD_LIBUNWIND) if(SAPI_DOWNLOAD_LIBUNWIND)
include(cmake/libunwind.cmake) include(cmake/libunwind.cmake)
endif() endif()
sapi_check_target(unwind_ptrace_wrapped) sapi_check_target(unwind_ptrace)
if(SAPI_DOWNLOAD_PROTOBUF) if(SAPI_DOWNLOAD_PROTOBUF)
include(cmake/protobuf.cmake) include(cmake/protobuf.cmake)

View File

@ -110,7 +110,7 @@ elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm")
) )
endif() endif()
add_library(unwind_ptrace_wrapped STATIC add_library(unwind_ptrace STATIC
# internal_headers # internal_headers
${libunwind_SOURCE_DIR}/include/compiler.h ${libunwind_SOURCE_DIR}/include/compiler.h
${libunwind_SOURCE_DIR}/include/config.h ${libunwind_SOURCE_DIR}/include/config.h
@ -185,35 +185,22 @@ add_library(unwind_ptrace_wrapped STATIC
# source_ptrace # source_ptrace
${_unwind_ptrace_srcs} ${_unwind_ptrace_srcs}
) )
add_library(unwind::unwind_ptrace_wrapped ALIAS unwind_ptrace_wrapped) add_library(unwind::unwind_ptrace ALIAS unwind_ptrace)
target_include_directories(unwind_ptrace_wrapped PUBLIC target_include_directories(unwind_ptrace PUBLIC
${libunwind_SOURCE_DIR}/include ${libunwind_SOURCE_DIR}/include
${libunwind_SOURCE_DIR}/include/tdep ${libunwind_SOURCE_DIR}/include/tdep
${libunwind_SOURCE_DIR}/include/tdep-${_unwind_cpu} ${libunwind_SOURCE_DIR}/include/tdep-${_unwind_cpu}
${libunwind_SOURCE_DIR}/src ${libunwind_SOURCE_DIR}/src
) )
target_compile_options(unwind_ptrace_wrapped PRIVATE target_compile_options(unwind_ptrace PRIVATE
-fno-common -fno-common
-Wno-cpp -Wno-cpp
) )
target_compile_definitions(unwind_ptrace_wrapped target_compile_definitions(unwind_ptrace
PRIVATE -DHAVE_CONFIG_H PRIVATE -DHAVE_CONFIG_H
-D_GNU_SOURCE -D_GNU_SOURCE
-DNO_FRAME_POINTER -DNO_FRAME_POINTER
PUBLIC -D_UPT_accessors=_UPT_accessors_wrapped
-D_UPT_create=_UPT_create_wrapped
-D_UPT_destroy=_UPT_destroy_wrapped
-D_U${_unwind_cpu}_create_addr_space=_U${_unwind_cpu}_create_addr_space_wrapped
-D_U${_unwind_cpu}_destroy_addr_space=_U${_unwind_cpu}_destroy_addr_space_wrapped
-D_U${_unwind_cpu}_get_proc_name=_U${_unwind_cpu}_get_proc_name_wrapped
-D_U${_unwind_cpu}_get_reg=_U${_unwind_cpu}_get_reg_wrapped
-D_U${_unwind_cpu}_init_remote=_U${_unwind_cpu}_init_remote_wrapped
-D_U${_unwind_cpu}_step=_U${_unwind_cpu}_step_wrapped
-Dptrace=ptrace_wrapped
) )
target_link_libraries(unwind_ptrace_wrapped PRIVATE target_link_libraries(unwind_ptrace PRIVATE
sapi::base sapi::base
sandbox2::ptrace_hook
) )

View File

@ -106,7 +106,7 @@ filegroup(
) )
cc_library( cc_library(
name = "unwind-ptrace-wrapped", name = "unwind-ptrace",
srcs = [ srcs = [
"src/mi/Gdyn-remote.c", "src/mi/Gdyn-remote.c",
"src/ptrace/_UPT_access_fpreg.c", "src/ptrace/_UPT_access_fpreg.c",
@ -138,20 +138,9 @@ cc_library(
"-DNO_FRAME_POINTER", "-DNO_FRAME_POINTER",
"-fno-common", "-fno-common",
"-Wno-cpp", # Warning in src/ptrace/_UPT_get_dyn_info_list_addr.c "-Wno-cpp", # Warning in src/ptrace/_UPT_get_dyn_info_list_addr.c
"-D_UPT_accessors=_UPT_accessors_wrapped",
"-D_UPT_create=_UPT_create_wrapped",
"-D_UPT_destroy=_UPT_destroy_wrapped",
"-D_Ux86_64_create_addr_space=_Ux86_64_create_addr_space_wrapped",
"-D_Ux86_64_destroy_addr_space=_Ux86_64_destroy_addr_space_wrapped",
"-D_Ux86_64_get_proc_name=_Ux86_64_get_proc_name_wrapped",
"-D_Ux86_64_get_reg=_Ux86_64_get_reg_wrapped",
"-D_Ux86_64_init_remote=_Ux86_64_init_remote_wrapped",
"-D_Ux86_64_step=_Ux86_64_step_wrapped",
"-Dptrace=ptrace_wrapped",
], ],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
":included_sources", ":included_sources",
"@com_google_sandboxed_api//sandboxed_api/sandbox2/unwind:ptrace_hook",
], ],
) )

View File

@ -1246,6 +1246,12 @@ PolicyBuilder& PolicyBuilder::AddNetworkProxyHandlerPolicy() {
return *this; return *this;
} }
PolicyBuilder& PolicyBuilder::TrapPtrace() {
AddPolicyOnSyscall(__NR_ptrace, {TRAP(0)});
user_policy_handles_ptrace_ = true;
return *this;
}
PolicyBuilder& PolicyBuilder::SetRootWritable() { PolicyBuilder& PolicyBuilder::SetRootWritable() {
EnableNamespaces(); // NOLINT(clang-diagnostic-deprecated-declarations) EnableNamespaces(); // NOLINT(clang-diagnostic-deprecated-declarations)
mounts_.SetRootWritable(); mounts_.SetRootWritable();

View File

@ -605,6 +605,9 @@ class PolicyBuilder final {
// Allows a limited version of madvise // Allows a limited version of madvise
PolicyBuilder& AllowLimitedMadvise(); PolicyBuilder& AllowLimitedMadvise();
// Traps instead of denying ptrace.
PolicyBuilder& TrapPtrace();
// Appends code to block a specific syscall and setting errno at the end of // Appends code to block a specific syscall and setting errno at the end of
// the policy - decision taken by user policy take precedence. // the policy - decision taken by user policy take precedence.
PolicyBuilder& OverridableBlockSyscallWithErrno(uint32_t num, int error); PolicyBuilder& OverridableBlockSyscallWithErrno(uint32_t num, int error);

View File

@ -120,6 +120,7 @@ absl::StatusOr<std::unique_ptr<Policy>> StackTracePeer::GetPolicy(
.AllowSyscall(__NR_madvise) .AllowSyscall(__NR_madvise)
// Required for our ptrace replacement. // Required for our ptrace replacement.
.TrapPtrace()
.AddPolicyOnSyscall( .AddPolicyOnSyscall(
__NR_process_vm_readv, __NR_process_vm_readv,
{ {

View File

@ -28,8 +28,10 @@ cc_library(
srcs = ["ptrace_hook.cc"], srcs = ["ptrace_hook.cc"],
hdrs = ["ptrace_hook.h"], hdrs = ["ptrace_hook.h"],
copts = sapi_platform_copts(), copts = sapi_platform_copts(),
visibility = ["@org_gnu_libunwind//:__subpackages__"], deps = [
deps = ["@com_google_absl//absl/strings"], "//sandboxed_api/sandbox2/util:syscall_trap",
"@com_google_absl//absl/strings",
],
) )
cc_library( cc_library(
@ -39,19 +41,6 @@ cc_library(
copts = sapi_platform_copts([ copts = sapi_platform_copts([
# TODO(cblichmann): Remove this, fix bazel/external/libunwind.BUILD # TODO(cblichmann): Remove this, fix bazel/external/libunwind.BUILD
"-Iexternal/org_gnu_libunwind/include", "-Iexternal/org_gnu_libunwind/include",
] + [
"-D{symbol}={symbol}_wrapped".format(symbol = symbol)
for symbol in [
"_UPT_accessors",
"_UPT_create",
"_UPT_destroy",
"_Ux86_64_create_addr_space",
"_Ux86_64_destroy_addr_space",
"_Ux86_64_get_proc_name",
"_Ux86_64_get_reg",
"_Ux86_64_init_remote",
"_Ux86_64_step",
]
]), ]),
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
@ -68,7 +57,7 @@ cc_library(
"@com_google_absl//absl/cleanup", "@com_google_absl//absl/cleanup",
"@com_google_absl//absl/status:statusor", "@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings", "@com_google_absl//absl/strings",
"@org_gnu_libunwind//:unwind-ptrace-wrapped", "@org_gnu_libunwind//:unwind-ptrace",
], ],
) )

View File

@ -20,6 +20,7 @@ add_library(sandbox2_ptrace_hook STATIC
add_library(sandbox2::ptrace_hook ALIAS sandbox2_ptrace_hook) add_library(sandbox2::ptrace_hook ALIAS sandbox2_ptrace_hook)
target_link_libraries(sandbox2_ptrace_hook target_link_libraries(sandbox2_ptrace_hook
PRIVATE sapi::base PRIVATE sapi::base
sandbox2::syscall_trap
PUBLIC absl::strings PUBLIC absl::strings
) )
@ -44,7 +45,7 @@ target_link_libraries(sandbox2_unwind PRIVATE
sapi::file_helpers sapi::file_helpers
sapi::raw_logging sapi::raw_logging
sapi::status sapi::status
unwind::unwind_ptrace_wrapped unwind::unwind_ptrace
) )
# sandboxed_api/sandbox2/unwind:unwind_proto # sandboxed_api/sandbox2/unwind:unwind_proto

View File

@ -17,6 +17,7 @@
#include <elf.h> // For NT_PRSTATUS #include <elf.h> // For NT_PRSTATUS
#include <sys/ptrace.h> #include <sys/ptrace.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <syscall.h>
#include <unistd.h> #include <unistd.h>
#include <cstdint> #include <cstdint>
@ -25,6 +26,8 @@
#include <cstring> #include <cstring>
#include <vector> #include <vector>
#include "sandboxed_api/sandbox2/util/syscall_trap.h"
// Android doesn't use an enum for __ptrace_request, use int instead. // Android doesn't use an enum for __ptrace_request, use int instead.
#if defined(__ANDROID__) #if defined(__ANDROID__)
using PtraceRequest = int; using PtraceRequest = int;
@ -45,38 +48,23 @@ constexpr size_t kRegSize = sizeof(RegType);
// limit). // limit).
auto* g_registers = new std::vector<RegType>(); auto* g_registers = new std::vector<RegType>();
// Whether ptrace() emulation is in effect. This can only be enabled (per // Hooks ptrace.
// thread), never disabled.
thread_local bool g_emulate_ptrace = false;
} // namespace
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.
// 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 EnablePtraceEmulationWithUserRegs(). // register values should be set via EnablePtraceEmulationWithUserRegs().
extern "C" long int ptrace_wrapped( // NOLINT long int ptrace_hook( // NOLINT
PtraceRequest request, pid_t pid, void* addr, void* data) { PtraceRequest request, pid_t pid, void* addr, void* data) {
if (!g_emulate_ptrace) {
return ptrace(request, pid, addr, data);
}
switch (request) { switch (request) {
case PTRACE_PEEKDATA: { case PTRACE_PEEKDATA: {
long int read_data; // NOLINT RegType read_data;
iovec local = {.iov_base = &read_data, .iov_len = sizeof(read_data)}; iovec local = {.iov_base = &read_data, .iov_len = sizeof(read_data)};
iovec remote = {.iov_base = addr, .iov_len = sizeof(read_data)}; iovec remote = {.iov_base = addr, .iov_len = sizeof(read_data)};
if (process_vm_readv(pid, &local, 1, &remote, 1, 0) <= 0) { if (process_vm_readv(pid, &local, 1, &remote, 1, 0) <= 0) {
return -1; return -1;
} }
return read_data; *reinterpret_cast<RegType*>(data) = read_data;
break;
} }
case PTRACE_PEEKUSER: { case PTRACE_PEEKUSER: {
// Make sure read is in-bounds and aligned. // Make sure read is in-bounds and aligned.
@ -85,7 +73,8 @@ extern "C" long int ptrace_wrapped( // NOLINT
offset % kRegSize != 0) { offset % kRegSize != 0) {
return -1; return -1;
} }
return (*g_registers)[offset / kRegSize]; *reinterpret_cast<RegType*>(data) = (*g_registers)[offset / kRegSize];
break;
} }
case PTRACE_GETREGSET: { case PTRACE_GETREGSET: {
// Only return general-purpose registers. // Only return general-purpose registers.
@ -100,11 +89,26 @@ extern "C" long int ptrace_wrapped( // NOLINT
break; break;
} }
default: default:
fprintf(stderr, "ptrace_wrapped(): operation not permitted: %d\n", fprintf(stderr, "ptrace_hook(): operation not permitted: %d\n", request);
request);
abort(); abort();
} }
return 0; return 0;
} }
} // namespace
void EnablePtraceEmulationWithUserRegs(absl::string_view regs) {
g_registers->resize((regs.size() + 1) / kRegSize);
memcpy(&g_registers->front(), regs.data(), regs.size());
SyscallTrap::Install([](int nr, SyscallTrap::Args args, uintptr_t* rv) {
if (nr != __NR_ptrace) {
return false;
}
*rv = ptrace_hook(
static_cast<PtraceRequest>(args[0]), static_cast<pid_t>(args[1]),
reinterpret_cast<void*>(args[2]), reinterpret_cast<void*>(args[3]));
return true;
});
}
} // namespace sandbox2 } // namespace sandbox2