Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -32,6 +32,7 @@ sanitizer_procmaps_linux.cc sanitizer_procmaps_mac.cc sanitizer_procmaps_solaris.cc + sanitizer_remote_address_space_view.cc sanitizer_rtems.cc sanitizer_solaris.cc sanitizer_stoptheworld_mac.cc @@ -39,8 +40,9 @@ sanitizer_tls_get_addr.cc sanitizer_thread_registry.cc sanitizer_type_traits.cc - sanitizer_win.cc - ) + sanitizer_vm_read_context_mac.cc + sanitizer_vm_read_context_stubs.cc + sanitizer_win.cc) if(UNIX AND NOT APPLE AND NOT OS_NAME MATCHES "SunOS") list(APPEND SANITIZER_SOURCES_NOTERMINATION @@ -165,6 +167,7 @@ sanitizer_posix.h sanitizer_procmaps.h sanitizer_quarantine.h + sanitizer_remote_address_space_view.h sanitizer_report_decorator.h sanitizer_ring_buffer.h sanitizer_rtems.h @@ -189,6 +192,7 @@ sanitizer_thread_registry.h sanitizer_tls_get_addr.h sanitizer_vector.h + sanitizer_vm_read_context.h sanitizer_win.h sanitizer_win_defs.h sanitizer_win_dll_thunk.h Index: lib/sanitizer_common/sanitizer_internal_defs.h =================================================================== --- lib/sanitizer_common/sanitizer_internal_defs.h +++ lib/sanitizer_common/sanitizer_internal_defs.h @@ -169,6 +169,16 @@ typedef int pid_t; #endif +// process_vm_read_handle_t is a platform specific type used by a platform to +// read memory from another process. +#if SANITIZER_MAC +typedef unsigned int task_t; +typedef task_t process_vm_read_handle_t; +#else +// Stub value for other platforms. +typedef int process_vm_read_handle_t; +#endif + #if SANITIZER_FREEBSD || SANITIZER_NETBSD || \ SANITIZER_OPENBSD || SANITIZER_MAC || \ (SANITIZER_SOLARIS && (defined(_LP64) || _FILE_OFFSET_BITS == 64)) || \ Index: lib/sanitizer_common/sanitizer_remote_address_space_view.h =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_remote_address_space_view.h @@ -0,0 +1,105 @@ +//===-- sanitizer_remote_address_space_view.h -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// `RemoteAddressSpaceView` provides a remote implementation of the +// `AddressSpaceView` interface which provides a simple interface to load memory +// from another process (i.e. out-of-process). +// +// This implementation relies on an `VMReadContext` object to do the actual +// work. +// +// Note that `RemoteAddressSpaceView` assumes it used exclusively from a +// a single thread. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_REMOTE_ADDRES_SPACE_VIEW_H +#define SANITIZER_REMOTE_ADDRES_SPACE_VIEW_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_atomic.h" +#include "sanitizer_common.h" +#include "sanitizer_vm_read_context.h" + +namespace __sanitizer { + +class RemoteAddressSpaceView { + private: + static atomic_uintptr_t vmrc_; + static atomic_uint64_t owning_tid_; + static void* internal_read(uptr target_address, uptr size) { + VMReadContext* vmrc = get_context(); + CHECK_NE(vmrc, nullptr); + if (size == 0) { + // Don't error out because this code path can be hit + // in legitimate cases. Just return a nullptr and assume + // that any attempt to read from this address (or nearby it) + // will trigger a segfault. + return 0; + } + bool success = vmrc->Read(target_address, size); + CHECK(success); + return vmrc->GetLocalAddress(); + } + + static void set_context(VMReadContext* ctx) { + atomic_store_relaxed(&owning_tid_, GetTid()); + atomic_store_relaxed(&vmrc_, reinterpret_cast(ctx)); + } + + public: + static VMReadContext* get_context() { + CHECK_EQ(atomic_load_relaxed(&owning_tid_), GetTid()); + return reinterpret_cast(atomic_load_relaxed(&vmrc_)); + } + + // TODO(dliew): `Load` and `LoadWritable` use same implementation for now + // because all VMReadContext implementations currently give back writable + // memory. In the future some platforms may require us to make the distinction + // between writable and read-only memory. We might also be able to make use of + // this information to optimize memory accesses on some platforms. + + // See LocalAddressSpaceView for a description of the semantics of this + // function. + template + static const T* Load(const T* target_address, uptr num_elements = 1) { + return reinterpret_cast(internal_read( + reinterpret_cast(target_address), sizeof(T) * num_elements)); + } + + // See LocalAddressSpaceView for a description of the semantics of this + // function. + template + static T* LoadWritable(T* target_address, uptr num_elements = 1) { + return reinterpret_cast(internal_read( + reinterpret_cast(target_address), sizeof(T) * num_elements)); + } + + // RAII style wrapper that holds the storage for a VMContext object + // and whilst alive has `RemoteAddressSpaceView` use that VMContext + // object. + class ScopedContext { + private: + VMReadContext vmrc; + + public: + ScopedContext(void* config, VMReadContext::ProcessHandle target_process) + : vmrc(config, target_process) { + RemoteAddressSpaceView::set_context(&vmrc); + } + ScopedContext() : vmrc() { + RemoteAddressSpaceView::set_context(&vmrc); + } + ~ScopedContext() { + CHECK_EQ(RemoteAddressSpaceView::get_context(), &vmrc); + RemoteAddressSpaceView::set_context(nullptr); + } + }; +}; + +} // namespace __sanitizer + +#endif Index: lib/sanitizer_common/sanitizer_remote_address_space_view.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_remote_address_space_view.cc @@ -0,0 +1,18 @@ +//===-- sanitizer_remote_address_space_view.cc ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// +#include "sanitizer_remote_address_space_view.h" + +namespace __sanitizer { + +atomic_uintptr_t RemoteAddressSpaceView::vmrc_; +atomic_uint64_t RemoteAddressSpaceView::owning_tid_; + +} Index: lib/sanitizer_common/sanitizer_vm_read_context.h =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_vm_read_context.h @@ -0,0 +1,69 @@ +//===-- sanitizer_vm_read_context.h -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// `VMReadContext` is a low-level abstraction the allows clients to read the +// memory of a target process. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_VM_READ_CONTEXT_H +#define SANITIZER_VM_READ_CONTEXT_H +#include "sanitizer_internal_defs.h" +#include "sanitizer_platform.h" + +namespace __sanitizer { + +class VMReadContext { + public: + typedef __sanitizer::process_vm_read_handle_t ProcessHandle; + + private: + uptr local_address_ = 0; + void* config_ = 0; + ProcessHandle target_process_; + + public: + // Out-of-process mode constructor + // + // `config` - A pointer to platform specific data needed for set up. + // `target_process` - A platform specific handle to the target process. + VMReadContext(void* config, ProcessHandle target_process); + + // In-process mode constructor + VMReadContext(); + + VMReadContext(const VMReadContext&) = delete; + + // Return the address to memory in the current process that + // is a copy of memory from the target process. + // + // Returns `nullptr` if no successful read has been performed. + void* GetLocalAddress() const { return (void*)local_address_; } + + // Return the platform specific handle to the target process. + ProcessHandle GetTargetProcess() { return target_process_; } + + // Read memory from a remote process. The memory will be accessible (only if + // the read operation succeeds) at the address pointed to by + // `GetLocalAddress()`. This memory can be assumed to be writable + // but writes made in the local process will not propagate to the + // target process. + // + // The lifetime of the memory allocated by the `Read()` can be assumed + // to be valid for the lifetime of the VMReadContext object. + // + // Returns `true` on a sucessful read and `false` otherwise. + bool Read(uptr target_address, u64 size); + + // Returns true iff the `VMReadContext` object is configured + // to read memory from the local process (i.e. target and local + // process are the same). + bool IsLocal() const; +}; +} // namespace __sanitizer +#endif Index: lib/sanitizer_common/sanitizer_vm_read_context_mac.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_vm_read_context_mac.cc @@ -0,0 +1,62 @@ +//===-- sanitizer_vm_read_context_mac.cc ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implementation of `VMReadContext` for Apple platforms. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_platform.h" +#if SANITIZER_MAC +#include +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_vm_read_context.h" + +// Taken from +typedef kern_return_t memory_reader_t(task_t remote_task, + vm_address_t remote_address, + vm_size_t size, void** local_memory); + +namespace __sanitizer { + +VMReadContext::VMReadContext(void* config, ProcessHandle target_process) + : config_(config), target_process_(target_process) {} + +VMReadContext::VMReadContext() : VMReadContext(nullptr, mach_task_self()) {} + +bool VMReadContext::Read(uptr target_address, u64 size) { + if (IsLocal()) { + // The remote is the current process, copy isn't necessary. + local_address_ = target_address; + return true; + } + CHECK_NE(config_, nullptr); + CHECK_GT(size, 0); + + uptr local_address = 0; + memory_reader_t* native_reader = (memory_reader_t*)config_; + kern_return_t result = native_reader(target_process_, target_address, size, + (void**)(&(local_address))); + if (result == KERN_SUCCESS) { + CHECK_NE(local_address, nullptr); + local_address_ = local_address; + return true; + } + Report("Failed to read %p of size %d from remote process. Error code: %d\n", + target_address, size, result); + return false; +} + +bool VMReadContext::IsLocal() const { + if (config_ == nullptr || mach_task_self() == target_process_) + return true; + return false; +} + +} // namespace __sanitizer + +#endif Index: lib/sanitizer_common/sanitizer_vm_read_context_stubs.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_vm_read_context_stubs.cc @@ -0,0 +1,44 @@ +//===-- sanitizer_vm_read_context_stubs.cc --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Stub implementation of `VMReadContext` for non-Apple platforms. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_platform.h" +#if !SANITIZER_MAC +#include "sanitizer_common.h" +#include "sanitizer_vm_read_context.h" + +namespace __sanitizer { + +VMReadContext::VMReadContext(void* config, ProcessHandle target_process) + : config_(config), target_process_(target_process) { + // Only in-process reading is allowed in + // stub implementation. + CHECK_EQ(config_, nullptr); + CHECK_EQ(target_process_, 0); +} + +VMReadContext::VMReadContext() : VMReadContext(nullptr, 0) {} + +bool VMReadContext::Read(uptr target_address, u64 size) { + // Stub implementation should only ever + // do in-process read. + CHECK(isLocal()); + local_address_ = target_address; + return true; +} + +bool VMReadContext::IsLocal() const { + // Stub implementation only supports local (in-process) reads. + return true; +} + +} // namespace __sanitizer + +#endif