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) if(UNIX AND NOT APPLE AND NOT OS_NAME MATCHES "SunOS") @@ -161,6 +164,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 @@ -185,6 +189,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 @@ -170,6 +170,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,88 @@ +//===-- sanitizer_remote_address_space_view.h -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// `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. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_REMOTE_ADDRES_SPACE_VIEW_H +#define SANITIZER_REMOTE_ADDRES_SPACE_VIEW_H +#include "sanitizer_internal_defs.h" +#include "sanitizer_vm_read_context.h" + +namespace __sanitizer { + +struct RemoteAddressSpaceView { + private: + static VMReadContext* vmrc_; + static void* internal_read(uptr target_address, uptr size, bool writable) { + CHECK_NE(get_context(), 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 = get_context()->Read(target_address, size, writable); + CHECK(success); + return get_context()->GetLocalAddress(); + } + static void set_context(VMReadContext* ctx) { vmrc_ = ctx; } + + public: + static VMReadContext* get_context() { return vmrc_; } + + // 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, /*writable=*/false)); + } + + // 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, /*writable=*/true)); + } + + // RAII style wrapper that holds the storage for a VMContext object + // and whilst alive has `RemoteAddressSpaceView` use that VMContext + // object. + struct ScopedContext { + public: + VMReadContext vmrc; + ScopedContext(void* config, VMReadContext::ProcessHandle target_process) + : vmrc(config, target_process) { + CHECK_EQ(RemoteAddressSpaceView::get_context(), nullptr); + RemoteAddressSpaceView::set_context(&vmrc); + } + ScopedContext() : vmrc() { + CHECK_EQ(RemoteAddressSpaceView::get_context(), nullptr); + 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,17 @@ +//===-- sanitizer_remote_address_space_view.cc ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// +#include "sanitizer_remote_address_space_view.h" + +namespace __sanitizer { + +VMReadContext* RemoteAddressSpaceView::vmrc_ = nullptr; +} Index: lib/sanitizer_common/sanitizer_vm_read_context.h =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_vm_read_context.h @@ -0,0 +1,89 @@ +//===-- sanitizer_vm_read_context.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// `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; + uptr target_address_ = 0; + u64 size_ = 0; + void* config_ = 0; + bool is_local_ = false; + 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 address provided to the last successful + // `Read()` call. + // + // Returns `0` if no successful read has been performed. + uptr GetTargetAddress() const { return target_address_; } + + // Return the size of the memory chunk requested in the last + // successful `Read()` call. + // + // Returns `0` if no successful read has been performed. + u64 GetSize() { return size_; } + + // Returns true iff `VMReadContext` is set up to read memory from the same + // process in which it is being called from. + bool IsLocal() const { return is_local_; } + + // 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()`. + // + // The lifetime of the memory allocated by the `Read()` can be assumed + // to be valid for the lifetime of the VMReadContext object. + // + // If `writable` is true then if `Read(...)` is successful then the memory + // returned by `GetLocalAddress()` must be writable. Note the changes to this + // memory will not propagate to the target process. If `writable` is false + // then then the memory returned by `GetLocalAddress()` does not need to be + // writable (i.e. it is implementation defined whether or not the memory is + // writable). + // + // Returns `true` on a sucessful read and `false` otherwise. + bool Read(uptr target_address, u64 size, bool writable = true); +}; +} // 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,67 @@ +//===-- sanitizer_vm_read_context_mac.cc ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// 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), is_local_(false), target_process_(target_process) { + if (config_ == nullptr || mach_task_self() == target_process) { + is_local_ = true; + } +} + +VMReadContext::VMReadContext() : VMReadContext(nullptr, mach_task_self()) {} + +bool VMReadContext::Read(uptr target_address, u64 size, bool writable) { + // TODO(dliew): Investigate if `writable` is useful for optimization. For + // now we just ignore this argument and assume that we are compliant. + + if (is_local_) { + // The remote is the current process, copy isn't necessary. + local_address_ = target_address; + size_ = size; + 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); + target_address_ = target_address; + local_address_ = local_address; + size_ = size; + return true; + } + Report("Failed to read %p of size %d from remote process. Error code: %d\n", + target_address_, size_, result); + 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++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// 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), is_local_(true), 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, bool writable) { + // Stub implementation should only ever + // do in-process read. + // For now ignore `writable` and just assume that we are compliant. + CHECK(isLocal()); + local_address_ = target_address; + target_address_ = target_address; + size_ = size; + return true; +} + +} // namespace __sanitizer + +#endif