Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -32,12 +32,16 @@ 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 sanitizer_suppressions.cc sanitizer_tls_get_addr.cc sanitizer_thread_registry.cc + sanitizer_vm_read_context_common.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") @@ -160,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 @@ -184,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,89 @@ +//===-- 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(); + } + + public: + static void set_context(VMReadContext* ctx) { vmrc = ctx; } + 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 ScopedRemoteAddressSpaceViewContext { + public: + VMReadContext vmrc; + ScopedRemoteAddressSpaceViewContext( + void* config, VMReadContext::ProcessHandle target_process) + : vmrc(config, target_process) { + CHECK_EQ(RemoteAddressSpaceView::get_context(), nullptr); + RemoteAddressSpaceView::set_context(&vmrc); + } + ScopedRemoteAddressSpaceViewContext() : vmrc() { + CHECK_EQ(RemoteAddressSpaceView::get_context(), nullptr); + RemoteAddressSpaceView::set_context(&vmrc); + } + ~ScopedRemoteAddressSpaceViewContext() { + 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++ -*-===// +// +// 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,95 @@ +//===-- 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; + uptr target_address; + u64 size; + void* config; + ProcessHandle target_process; + bool is_local; + + 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; } + + // Reset the state of `VMReadContext` to the state it was in + // after calling the `VMReadContext` constructor. + // + // The state of the memory returned by previous calls + // to `GetLocalAddress()` should be assumed to be invalid. + void Reset(); + + // 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 successfully 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. + // + // 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_common.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_vm_read_context_common.cc @@ -0,0 +1,25 @@ +//===-- sanitizer_vm_read_context_common.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` methods common to all +// platforms. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common.h" +#include "sanitizer_platform.h" +#include "sanitizer_vm_read_context.h" + +namespace __sanitizer { + +void VMReadContext::Reset() { + local_address = 0; + target_address = 0; + size = 0; +} +} // namespace __sanitizer Index: lib/sanitizer_common/sanitizer_vm_read_context_mac.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_vm_read_context_mac.cc @@ -0,0 +1,71 @@ +//===-- 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) + : local_address(0), + target_address(0), + size(0), + config(config), + target_process(target_process), + is_local(false) { + 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; + this->size = size; + return true; + } + CHECK_NE(config, nullptr); + CHECK_GT(size, 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) { + this->target_address = target_address; + this->size = size; + return true; + } + Report("Failed to read %p of size %d from remote process. Error code: %d\n", + target_address, size, result); + // Reset the fields. + Reset(); + 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,50 @@ +//===-- 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) + : target_address(0), + local_address(0), + size(0), + config(0), + target_process(0), + is_local(true) { + // 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_EQ(reader, nullptr); + CHECK_EQ(remote, 0); + this->local_address = target_address; + this->target_address = target_address; + this->size = size; + return true; +} + +} // namespace __sanitizer + +#endif