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,6 +40,8 @@ sanitizer_tls_get_addr.cc sanitizer_thread_registry.cc sanitizer_type_traits.cc + sanitizer_vm_read_context_mac.cc + sanitizer_vm_read_context_stubs.cc sanitizer_win.cc ) @@ -165,6 +168,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 +193,8 @@ sanitizer_thread_registry.h sanitizer_tls_get_addr.h sanitizer_vector.h + sanitizer_vm_read_context.h + sanitizer_vm_read_context_config.h sanitizer_win.h sanitizer_win_defs.h sanitizer_win_dll_thunk.h Index: lib/sanitizer_common/sanitizer_remote_address_space_view.h =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_remote_address_space_view.h @@ -0,0 +1,115 @@ +//===-- 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_atomic.h" +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_vm_read_context.h" + +namespace __sanitizer { + +class RemoteAddressSpaceView { + private: + static VMReadContext* 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(); + } + + // Set the `VMReadContext` object. Note once this function is called + // from a thread, all subsequent uses of RemoteAddressSpaceView + // must be performed by that same thread. + static void set_context(VMReadContext* ctx) { + tid_t current_tid = GetTid(); + static_assert(sizeof(atomic_uint64_t) == sizeof(tid_t), "Size mismatch"); + tid_t owning_tid = atomic_load_relaxed(&owning_tid_); + DCHECK_NE(current_tid, 0); + // We assume `owning_tid_` is zero initialized and that thread ID 0 is not a + // valid thread ID. This assumption lets us skip the check the first time + // `set_context(...)` is called, but have the check performed for all + // subsequent calls. + if (owning_tid) + CHECK_EQ(owning_tid, current_tid); + vmrc_ = ctx; + atomic_store_relaxed(&owning_tid_, current_tid); + } + + public: + static VMReadContext* get_context() { + CHECK_EQ(atomic_load_relaxed(&owning_tid_), GetTid()); + return 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(VMReadContextConfig* config) : vmrc(config) { + RemoteAddressSpaceView::set_context(&vmrc); + } + ScopedContext() { 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 { + +VMReadContext* RemoteAddressSpaceView::vmrc_ = nullptr; +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,74 @@ +//===-- 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 { + +// Foward declare to avoid header dependency. +class VMReadContextConfig; + +class VMReadContext { + private: + uptr local_address_ = 0; + VMReadContextConfig* config_ = 0; + + public: + // Out-of-process mode constructor + // + // `config` - A pointer to platform specific data needed for set up. + VMReadContext(VMReadContextConfig* config); + + // In-process mode constructor + VMReadContext() : VMReadContext(nullptr) {} + + // Prevent moving/copying. + VMReadContext(const VMReadContext&) = delete; + VMReadContext(const VMReadContext&&) = delete; + VMReadContext& operator=(const VMReadContext&) = delete; + VMReadContext& operator=(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 reinterpret_cast(local_address_); + } + + // Return a pointer to used configuration object. May return + // `nullptr` if there is no configuration object. + const VMReadContextConfig* GetConfig() const { return config_; } + + // 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_config.h =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_vm_read_context_config.h @@ -0,0 +1,54 @@ +//===-- sanitizer_vm_read_context_config.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 +// +//===----------------------------------------------------------------------===// +// +// `VMReadContextConfig` is a simple platform specific class containing +// arguments for `VMReadContext`. This is defined in a separate header from +// the `VMReadContext` can be included without knowing how `VMReadContextConfig` +// is defined. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_VM_READ_CONTEXT_CONFIG_H +#define SANITIZER_VM_READ_CONTEXT_CONFIG_H +#include "sanitizer_internal_defs.h" +#include "sanitizer_platform.h" + +namespace __sanitizer { + +#if SANITIZER_MAC + +class VMReadContextConfig { + public: + // Task port for target process. + task_t target_process = 0; + // 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); + // Function pointer to memory reader function. Typically this provided + // by the Symbolication framework on Darwin. + memory_reader_t* reader = nullptr; + + VMReadContextConfig(task_t target_process, memory_reader_t* reader) + : target_process(target_process), reader(reader) {} +}; + +#else + +// Stub implementation doesn't need arguments because it can +// only do in-process reads. +class VMReadContextConfig {}; + +#endif + +// Check that `VMReadContextConfig` is always defined. +static_assert(sizeof(VMReadContextConfig) > 0, + "Problem with VMReadContextConfig declaration"); +} // 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,56 @@ +//===-- 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" +#include "sanitizer_vm_read_context_config.h" + +namespace __sanitizer { + +VMReadContext::VMReadContext(VMReadContextConfig* config) : config_(config) { + if (!IsLocal()) + CHECK_NE(config_->reader, nullptr); +} + +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; + kern_return_t result = + config_->reader(config_->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 { + return config_ == nullptr || mach_task_self() == config_->target_process; +} + +} // 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,41 @@ +//===-- 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" +#include "sanitizer_vm_read_context_config.h" + +namespace __sanitizer { + +VMReadContext::VMReadContext(VMReadContextConfig* config) : config_(config) { + // Only in-process reading is allowed in + // stub implementation. + CHECK_EQ(config_, nullptr); +} + +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