forked from OSchip/llvm-project
AddressSanitizer: start factoring out interception machinery
llvm-svn: 150083
This commit is contained in:
parent
27c8f620ee
commit
8489f2a564
|
@ -8,7 +8,7 @@
|
|||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := asan
|
||||
SubDirs := mach_override
|
||||
SubDirs := interception mach_override
|
||||
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
|
||||
ObjNames := $(Sources:%.cc=%.o)
|
||||
|
@ -17,6 +17,8 @@ Implementation := Generic
|
|||
|
||||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard $(Dir)/*.h)
|
||||
Dependencies += $(wildcard $(Dir)/interception/*.h)
|
||||
Dependencies += $(wildcard $(Dir)/mach_override/*.h)
|
||||
|
||||
# Define a convenience variable for all the asan functions.
|
||||
AsanFunctions := $(Sources:%.cc=%)
|
||||
|
|
|
@ -178,6 +178,9 @@ RTL_HDR=asan_allocator.h \
|
|||
asan_stats.h \
|
||||
asan_thread.h \
|
||||
asan_thread_registry.h \
|
||||
interception/interception.h \
|
||||
interception/interception_linux.h \
|
||||
interception/interception_mac.h \
|
||||
mach_override/mach_override.h
|
||||
|
||||
LIBASAN_OBJ=$(BIN)/asan_rtl$(SUFF).o \
|
||||
|
@ -195,6 +198,8 @@ LIBASAN_OBJ=$(BIN)/asan_rtl$(SUFF).o \
|
|||
$(BIN)/asan_stats$(SUFF).o \
|
||||
$(BIN)/asan_thread$(SUFF).o \
|
||||
$(BIN)/asan_thread_registry$(SUFF).o \
|
||||
$(BIN)/interception/interception_linux$(SUFF).o \
|
||||
$(BIN)/interception/interception_mac$(SUFF).o \
|
||||
$(BIN)/mach_override/mach_override$(SUFF).o
|
||||
|
||||
GTEST_ROOT=third_party/googletest
|
||||
|
@ -215,9 +220,9 @@ t64: b64
|
|||
t32: b32
|
||||
$(BIN)/asan_test32
|
||||
|
||||
b64: | $(BIN)
|
||||
b64: | mk_bin_dir
|
||||
$(MAKE) -f $(MAKEFILE) ARCH=x86_64 asan_test asan_benchmarks
|
||||
b32: | $(BIN)
|
||||
b32: | mk_bin_dir
|
||||
$(MAKE) -f $(MAKEFILE) ARCH=i386 asan_test asan_benchmarks
|
||||
|
||||
lib64:
|
||||
|
@ -225,8 +230,9 @@ lib64:
|
|||
lib32:
|
||||
$(MAKE) $(MAKEFILE) ARCH=i386 lib
|
||||
|
||||
$(BIN):
|
||||
mk_bin_dir:
|
||||
mkdir -p $(BIN)
|
||||
mkdir -p $(BIN)/interception
|
||||
mkdir -p $(BIN)/mach_override
|
||||
|
||||
clang:
|
||||
|
@ -330,6 +336,7 @@ ADDRESS_SANITIZER_CPP=../../../../lib/Transforms/Instrumentation/AddressSanitize
|
|||
lint:
|
||||
third_party/cpplint/cpplint.py --filter=$(LLVM_LINT_FILTER) $(ADDRESS_SANITIZER_CPP)
|
||||
third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FITLER) asan_*.cc asan_*.h
|
||||
third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FITLER) interception/interception*.h interception/interception*.cc
|
||||
third_party/cpplint/cpplint.py --filter=$(TEST_LINT_FITLER) tests/*.cc
|
||||
|
||||
get_third_party:
|
||||
|
|
|
@ -21,74 +21,22 @@
|
|||
#include "asan_stack.h"
|
||||
#include "asan_stats.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "interception/interception.h"
|
||||
|
||||
#include <new>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#endif // _WIN32
|
||||
|
||||
// To replace weak system functions on Linux we just need to declare functions
|
||||
// with same names in our library and then obtain the real function pointers
|
||||
// using dlsym(). This is not so on Mac OS, where the two-level namespace makes
|
||||
// our replacement functions invisible to other libraries. This may be overcomed
|
||||
// using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared
|
||||
// libraries in Chromium were noticed when doing so.
|
||||
// Instead we use mach_override, a handy framework for patching functions at
|
||||
// runtime. To avoid possible name clashes, our replacement functions have
|
||||
// the "wrap_" prefix on Mac.
|
||||
//
|
||||
// After interception, the calls to system functions will be substituted by
|
||||
// calls to our interceptors. We store pointers to system function f()
|
||||
// in __asan::real_f().
|
||||
#if defined(__APPLE__)
|
||||
// Include the declarations of the original functions.
|
||||
// FIXME(samsonov): Gradually replace system headers with declarations of
|
||||
// intercepted functions.
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "mach_override/mach_override.h"
|
||||
|
||||
#define OVERRIDE_FUNCTION(oldfunc, newfunc) \
|
||||
do {CHECK(0 == __asan_mach_override_ptr_custom((void*)(oldfunc), \
|
||||
(void*)(newfunc), \
|
||||
(void**)&real_##oldfunc, \
|
||||
__asan_allocate_island, \
|
||||
__asan_deallocate_island)); \
|
||||
CHECK(real_##oldfunc != NULL); } while (0)
|
||||
|
||||
#define OVERRIDE_FUNCTION_IF_EXISTS(oldfunc, newfunc) \
|
||||
do { __asan_mach_override_ptr_custom((void*)(oldfunc), \
|
||||
(void*)(newfunc), \
|
||||
(void**)&real_##oldfunc, \
|
||||
__asan_allocate_island, \
|
||||
__asan_deallocate_island); \
|
||||
} while (0)
|
||||
|
||||
#define INTERCEPT_FUNCTION(func) \
|
||||
OVERRIDE_FUNCTION(func, WRAP(func))
|
||||
|
||||
#define INTERCEPT_FUNCTION_IF_EXISTS(func) \
|
||||
OVERRIDE_FUNCTION_IF_EXISTS(func, WRAP(func))
|
||||
|
||||
#elif defined(_WIN32)
|
||||
// TODO(timurrrr): change these macros once we decide how to intercept
|
||||
// functions on Windows.
|
||||
#define INTERCEPT_FUNCTION(func) \
|
||||
do { } while (0)
|
||||
|
||||
#define INTERCEPT_FUNCTION_IF_EXISTS(func) \
|
||||
do { } while (0)
|
||||
|
||||
#else // __linux__
|
||||
#define INTERCEPT_FUNCTION(func) \
|
||||
CHECK((real_##func = (func##_f)dlsym(RTLD_NEXT, #func)));
|
||||
|
||||
#define INTERCEPT_FUNCTION_IF_EXISTS(func) \
|
||||
do { real_##func = (func##_f)dlsym(RTLD_NEXT, #func); } while (0)
|
||||
#endif
|
||||
#endif // __APPLE__
|
||||
|
||||
namespace __asan {
|
||||
|
||||
|
@ -581,12 +529,12 @@ INTERCEPTOR(size_t, strnlen, const char *s, size_t maxlen) {
|
|||
namespace __asan {
|
||||
void InitializeAsanInterceptors() {
|
||||
#ifndef __APPLE__
|
||||
INTERCEPT_FUNCTION(index);
|
||||
CHECK(INTERCEPT_FUNCTION(index));
|
||||
#else
|
||||
OVERRIDE_FUNCTION(index, WRAP(strchr));
|
||||
CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr)));
|
||||
#endif
|
||||
INTERCEPT_FUNCTION(memcmp);
|
||||
INTERCEPT_FUNCTION(memmove);
|
||||
CHECK(INTERCEPT_FUNCTION(memcmp));
|
||||
CHECK(INTERCEPT_FUNCTION(memmove));
|
||||
#ifdef __APPLE__
|
||||
// Wrap memcpy() on OS X 10.6 only, because on 10.7 memcpy() and memmove()
|
||||
// are resolved into memmove$VARIANT$sse42.
|
||||
|
@ -594,44 +542,44 @@ void InitializeAsanInterceptors() {
|
|||
// TODO(glider): need to check dynamically that memcpy() and memmove() are
|
||||
// actually the same function.
|
||||
if (GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD) {
|
||||
INTERCEPT_FUNCTION(memcpy);
|
||||
CHECK(INTERCEPT_FUNCTION(memcpy));
|
||||
} else {
|
||||
REAL(memcpy) = REAL(memmove);
|
||||
}
|
||||
#else
|
||||
// Always wrap memcpy() on non-Darwin platforms.
|
||||
INTERCEPT_FUNCTION(memcpy);
|
||||
CHECK(INTERCEPT_FUNCTION(memcpy));
|
||||
#endif
|
||||
INTERCEPT_FUNCTION(memset);
|
||||
INTERCEPT_FUNCTION(strcasecmp);
|
||||
INTERCEPT_FUNCTION(strcat); // NOLINT
|
||||
INTERCEPT_FUNCTION(strchr);
|
||||
INTERCEPT_FUNCTION(strcmp);
|
||||
INTERCEPT_FUNCTION(strcpy); // NOLINT
|
||||
INTERCEPT_FUNCTION(strdup);
|
||||
INTERCEPT_FUNCTION(strlen);
|
||||
INTERCEPT_FUNCTION(strncasecmp);
|
||||
INTERCEPT_FUNCTION(strncmp);
|
||||
INTERCEPT_FUNCTION(strncpy);
|
||||
CHECK(INTERCEPT_FUNCTION(memset));
|
||||
CHECK(INTERCEPT_FUNCTION(strcasecmp));
|
||||
CHECK(INTERCEPT_FUNCTION(strcat)); // NOLINT
|
||||
CHECK(INTERCEPT_FUNCTION(strchr));
|
||||
CHECK(INTERCEPT_FUNCTION(strcmp));
|
||||
CHECK(INTERCEPT_FUNCTION(strcpy)); // NOLINT
|
||||
CHECK(INTERCEPT_FUNCTION(strdup));
|
||||
CHECK(INTERCEPT_FUNCTION(strlen));
|
||||
CHECK(INTERCEPT_FUNCTION(strncasecmp));
|
||||
CHECK(INTERCEPT_FUNCTION(strncmp));
|
||||
CHECK(INTERCEPT_FUNCTION(strncpy));
|
||||
|
||||
INTERCEPT_FUNCTION(sigaction);
|
||||
INTERCEPT_FUNCTION(signal);
|
||||
INTERCEPT_FUNCTION(longjmp);
|
||||
INTERCEPT_FUNCTION(_longjmp);
|
||||
INTERCEPT_FUNCTION_IF_EXISTS(__cxa_throw);
|
||||
INTERCEPT_FUNCTION(pthread_create);
|
||||
CHECK(INTERCEPT_FUNCTION(sigaction));
|
||||
CHECK(INTERCEPT_FUNCTION(signal));
|
||||
CHECK(INTERCEPT_FUNCTION(longjmp));
|
||||
CHECK(INTERCEPT_FUNCTION(_longjmp));
|
||||
INTERCEPT_FUNCTION(__cxa_throw);
|
||||
CHECK(INTERCEPT_FUNCTION(pthread_create));
|
||||
|
||||
#ifdef __APPLE__
|
||||
INTERCEPT_FUNCTION(dispatch_async_f);
|
||||
INTERCEPT_FUNCTION(dispatch_sync_f);
|
||||
INTERCEPT_FUNCTION(dispatch_after_f);
|
||||
INTERCEPT_FUNCTION(dispatch_barrier_async_f);
|
||||
INTERCEPT_FUNCTION(dispatch_group_async_f);
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_async_f));
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_sync_f));
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_after_f));
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f));
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f));
|
||||
// We don't need to intercept pthread_workqueue_additem_np() to support the
|
||||
// libdispatch API, but it helps us to debug the unsupported functions. Let's
|
||||
// intercept it only during verbose runs.
|
||||
if (FLAG_v >= 2) {
|
||||
INTERCEPT_FUNCTION(pthread_workqueue_additem_np);
|
||||
CHECK(INTERCEPT_FUNCTION(pthread_workqueue_additem_np));
|
||||
}
|
||||
// Normally CFStringCreateCopy should not copy constant CF strings.
|
||||
// Replacing the default CFAllocator causes constant strings to be copied
|
||||
|
@ -640,15 +588,15 @@ void InitializeAsanInterceptors() {
|
|||
// http://code.google.com/p/address-sanitizer/issues/detail?id=10
|
||||
// Until this problem is fixed we need to check that the string is
|
||||
// non-constant before calling CFStringCreateCopy.
|
||||
INTERCEPT_FUNCTION(CFStringCreateCopy);
|
||||
CHECK(INTERCEPT_FUNCTION(CFStringCreateCopy));
|
||||
#else
|
||||
// On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it
|
||||
// there.
|
||||
INTERCEPT_FUNCTION(siglongjmp);
|
||||
CHECK(INTERCEPT_FUNCTION(siglongjmp));
|
||||
#endif
|
||||
|
||||
#ifndef __APPLE__
|
||||
INTERCEPT_FUNCTION(strnlen);
|
||||
CHECK(INTERCEPT_FUNCTION(strnlen));
|
||||
#endif
|
||||
if (FLAG_v > 0) {
|
||||
Printf("AddressSanitizer: libc interceptors initialized\n");
|
||||
|
|
|
@ -15,66 +15,7 @@
|
|||
#define ASAN_INTERCEPTORS_H
|
||||
|
||||
#include "asan_internal.h"
|
||||
|
||||
// Suppose you need to wrap/replace system function (generally, from libc):
|
||||
// int foo(const char *bar, double baz);
|
||||
// You'll need to:
|
||||
// 1) define INTERCEPT(int, foo, const char *bar, double baz) { ... }
|
||||
// 2) add a line "INTERCEPT_FUNCTION(foo)" to InitializeAsanInterceptors()
|
||||
// You can access original function by calling __asan::real_foo(bar, baz).
|
||||
// By defualt, real_foo will be visible only inside your interceptor, and if
|
||||
// you want to use it in other parts of RTL, you'll need to:
|
||||
// 3a) add DECLARE_REAL(int, foo, const char*, double); to a
|
||||
// header file.
|
||||
// However, if you want to implement your interceptor somewhere outside
|
||||
// asan_interceptors.cc, you'll instead need to:
|
||||
// 3b) add DECLARE_REAL_AND_INTERCEPTOR(int, foo, const char*, double);
|
||||
// to a header.
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# define WRAP(x) wrap_##x
|
||||
# define WRAPPER_NAME(x) "wrap_"#x
|
||||
# define INTERCEPTOR_ATTRIBUTE
|
||||
#elif defined(_WIN32)
|
||||
// TODO(timurrrr): we're likely to use something else later on Windows.
|
||||
# define WRAP(x) wrap_##x
|
||||
# define WRAPPER_NAME(x) #x
|
||||
# define INTERCEPTOR_ATTRIBUTE
|
||||
#else
|
||||
# define WRAP(x) x
|
||||
# define WRAPPER_NAME(x) #x
|
||||
# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#define REAL(x) real_##x
|
||||
#define FUNC_TYPE(x) x##_f
|
||||
|
||||
#define DECLARE_REAL(ret_type, func, ...); \
|
||||
typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
|
||||
namespace __asan { \
|
||||
extern FUNC_TYPE(func) REAL(func); \
|
||||
}
|
||||
|
||||
#define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...); \
|
||||
DECLARE_REAL(ret_type, func, ##__VA_ARGS__); \
|
||||
extern "C" \
|
||||
ret_type WRAP(func)(__VA_ARGS__);
|
||||
|
||||
// Generally, you don't need to use DEFINE_REAL by itself, as INTERCEPTOR
|
||||
// macros does its job. In exceptional cases you may need to call REAL(foo)
|
||||
// without defining INTERCEPTOR(..., foo, ...). For example, if you override
|
||||
// foo with interceptor for other function.
|
||||
#define DEFINE_REAL(ret_type, func, ...); \
|
||||
typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
|
||||
namespace __asan { \
|
||||
FUNC_TYPE(func) REAL(func); \
|
||||
}
|
||||
|
||||
#define INTERCEPTOR(ret_type, func, ...); \
|
||||
DEFINE_REAL(ret_type, func, __VA_ARGS__); \
|
||||
extern "C" \
|
||||
INTERCEPTOR_ATTRIBUTE \
|
||||
ret_type WRAP(func)(__VA_ARGS__)
|
||||
#include "interception/interception.h"
|
||||
|
||||
DECLARE_REAL(int, memcmp, const void *a1, const void *a2, size_t size);
|
||||
DECLARE_REAL(void*, memcpy, void *to, const void *from, size_t size);
|
||||
|
|
|
@ -36,8 +36,6 @@
|
|||
|
||||
namespace __asan {
|
||||
|
||||
void *island_allocator_pos = NULL;
|
||||
|
||||
void GetPcSpBp(void *context, uintptr_t *pc, uintptr_t *sp, uintptr_t *bp) {
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
# if __WORDSIZE == 64
|
||||
|
@ -314,25 +312,26 @@ void AsanStackTrace::GetStackTrace(size_t max_s, uintptr_t pc, uintptr_t bp) {
|
|||
}
|
||||
}
|
||||
|
||||
// The range of pages to be used by __asan_mach_override_ptr for escape
|
||||
// islands.
|
||||
// The range of pages to be used for escape islands.
|
||||
// TODO(glider): instead of mapping a fixed range we must find a range of
|
||||
// unmapped pages in vmmap and take them.
|
||||
// These constants were chosen empirically and may not work if the shadow
|
||||
// memory layout changes. Unfortunately they do necessarily depend on
|
||||
// kHighMemBeg or kHighMemEnd.
|
||||
static void *island_allocator_pos = NULL;
|
||||
|
||||
#if __WORDSIZE == 32
|
||||
#define kIslandEnd (0xffdf0000 - kPageSize)
|
||||
#define kIslandBeg (kIslandEnd - 256 * kPageSize)
|
||||
# define kIslandEnd (0xffdf0000 - kPageSize)
|
||||
# define kIslandBeg (kIslandEnd - 256 * kPageSize)
|
||||
#else
|
||||
#define kIslandEnd (0x7fffffdf0000 - kPageSize)
|
||||
#define kIslandBeg (kIslandEnd - 256 * kPageSize)
|
||||
# define kIslandEnd (0x7fffffdf0000 - kPageSize)
|
||||
# define kIslandBeg (kIslandEnd - 256 * kPageSize)
|
||||
#endif
|
||||
|
||||
extern "C"
|
||||
mach_error_t __asan_allocate_island(void **ptr,
|
||||
size_t unused_size,
|
||||
void *unused_hint) {
|
||||
mach_error_t __interception_allocate_island(void **ptr,
|
||||
size_t unused_size,
|
||||
void *unused_hint) {
|
||||
if (!island_allocator_pos) {
|
||||
island_allocator_pos =
|
||||
asan_mmap((void*)kIslandBeg, kIslandEnd - kIslandBeg,
|
||||
|
@ -349,7 +348,7 @@ mach_error_t __asan_allocate_island(void **ptr,
|
|||
}
|
||||
|
||||
extern "C"
|
||||
mach_error_t __asan_deallocate_island(void *ptr) {
|
||||
mach_error_t __interception_deallocate_island(void *ptr) {
|
||||
// Do nothing.
|
||||
// TODO(glider): allow to free and reuse the island memory.
|
||||
return err_none;
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
// TODO(glider): need to check if the OS X version is 10.6 or greater.
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <mach/mach_error.h>
|
||||
#include <setjmp.h>
|
||||
#include <CoreFoundation/CFString.h>
|
||||
|
||||
|
@ -79,15 +78,6 @@ typedef struct {
|
|||
|
||||
|
||||
extern "C" {
|
||||
// Allocate memory for the escape island. This cannot be moved to
|
||||
// mach_override, because the allocator needs to know about the ASan shadow
|
||||
// mappings.
|
||||
// TODO(glider): in order to place a relative jump the allocated memory should
|
||||
// be within 2 Gb from the hint address.
|
||||
mach_error_t __asan_allocate_island(void **ptr, size_t unused_size,
|
||||
void *unused_hint);
|
||||
mach_error_t __asan_deallocate_island(void *ptr);
|
||||
|
||||
// dispatch_barrier_async_f() is not declared in <dispatch/dispatch.h>.
|
||||
void dispatch_barrier_async_f(dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
|
|
|
@ -310,7 +310,7 @@ extern bool kCFUseCollectableAllocator; // is GC on?
|
|||
namespace __asan {
|
||||
void ReplaceSystemMalloc() {
|
||||
static malloc_introspection_t asan_introspection;
|
||||
__asan::REAL(memset)(&asan_introspection, 0, sizeof(asan_introspection));
|
||||
REAL(memset)(&asan_introspection, 0, sizeof(asan_introspection));
|
||||
|
||||
asan_introspection.enumerator = &mi_enumerator;
|
||||
asan_introspection.good_size = &mi_good_size;
|
||||
|
@ -321,7 +321,7 @@ void ReplaceSystemMalloc() {
|
|||
asan_introspection.force_unlock = &mi_force_unlock;
|
||||
|
||||
static malloc_zone_t asan_zone;
|
||||
__asan::REAL(memset)(&asan_zone, 0, sizeof(malloc_zone_t));
|
||||
REAL(memset)(&asan_zone, 0, sizeof(malloc_zone_t));
|
||||
|
||||
// Start with a version 4 zone which is used for OS X 10.4 and 10.5.
|
||||
asan_zone.version = 4;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
#===- lib/asan/interception/Makefile.mk --------------------*- Makefile -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := asan
|
||||
SubDirs :=
|
||||
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
|
||||
ObjNames := $(Sources:%.cc=%.o)
|
||||
|
||||
Implementation := Generic
|
||||
|
||||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard $(Dir)/*.h)
|
||||
|
||||
# Define a convenience variable for all the asan functions.
|
||||
AsanFunctions += $(Sources:%.cc=%)
|
|
@ -0,0 +1,136 @@
|
|||
//===-- interception.h ------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Machinery for providing replacements/wrappers for system functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef INTERCEPTION_H
|
||||
#define INTERCEPTION_H
|
||||
|
||||
#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
|
||||
# error "Interception doesn't work on this operating system."
|
||||
#endif
|
||||
|
||||
// How to use this library:
|
||||
// 1) Include this header to define your own interceptors
|
||||
// (see details below).
|
||||
// 2) Build all *.cc files and link against them.
|
||||
// On Mac you will also need to:
|
||||
// 3) Provide your own implementation for the following functions:
|
||||
// mach_error_t __interception::allocate_island(void **ptr,
|
||||
// size_t size,
|
||||
// void *hint);
|
||||
// mach_error_t __interception::deallocate_island(void *ptr);
|
||||
// See "interception_mac.h" for more details.
|
||||
|
||||
// How to add an interceptor:
|
||||
// Suppose you need to wrap/replace system function (generally, from libc):
|
||||
// int foo(const char *bar, double baz);
|
||||
// You'll need to:
|
||||
// 1) define INTERCEPTOR(int, foo, const char *bar, double baz) { ... } in
|
||||
// your source file.
|
||||
// 2) Call "INTERCEPT_FUNCTION(foo)" prior to the first call of "foo".
|
||||
// INTERCEPT_FUNCTION(foo) evaluates to "true" iff the function was
|
||||
// intercepted successfully.
|
||||
// You can access original function by calling REAL(foo)(bar, baz).
|
||||
// By default, REAL(foo) will be visible only inside your interceptor, and if
|
||||
// you want to use it in other parts of RTL, you'll need to:
|
||||
// 3a) add DECLARE_REAL(int, foo, const char*, double); to a
|
||||
// header file.
|
||||
// However, if the call "INTERCEPT_FUNCTION(foo)" and definition for
|
||||
// INTERCEPTOR(..., foo, ...) are in different files, you'll instead need to:
|
||||
// 3b) add DECLARE_REAL_AND_INTERCEPTOR(int, foo, const char*, double);
|
||||
// to a header file.
|
||||
|
||||
// Notes: 1. Things may not work properly if macro INTERCEPT(...) {...} or
|
||||
// DECLARE_REAL(...); are located inside namespaces.
|
||||
// 2. On Mac you can also use: "OVERRIDE_FUNCTION(foo, zoo);" to
|
||||
// effectively redirect calls from "foo" to "zoo". In this case
|
||||
// you aren't required to implement
|
||||
// INTERCEPTOR(int, foo, const char *bar, double baz);
|
||||
// but instead you'll have to add
|
||||
// DEFINE_REAL(int, foo, const char *bar, double baz); in your
|
||||
// source file (to define a pointer to overriden function).
|
||||
|
||||
// How it works:
|
||||
// To replace weak system functions on Linux we just need to declare functions
|
||||
// with same names in our library and then obtain the real function pointers
|
||||
// using dlsym(). This is not so on Mac OS, where the two-level namespace makes
|
||||
// our replacement functions invisible to other libraries. This may be overcomed
|
||||
// using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared
|
||||
// libraries in Chromium were noticed when doing so.
|
||||
// Instead we use mach_override, a handy framework for patching functions at
|
||||
// runtime. To avoid possible name clashes, our replacement functions have
|
||||
// the "wrap_" prefix on Mac.
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# define WRAP(x) wrap_##x
|
||||
# define WRAPPER_NAME(x) "wrap_"#x
|
||||
# define INTERCEPTOR_ATTRIBUTE
|
||||
#elif defined(_WIN32)
|
||||
// TODO(timurrrr): we're likely to use something else later on Windows.
|
||||
# define WRAP(x) wrap_##x
|
||||
# define WRAPPER_NAME(x) #x
|
||||
# define INTERCEPTOR_ATTRIBUTE
|
||||
#else
|
||||
# define WRAP(x) x
|
||||
# define WRAPPER_NAME(x) #x
|
||||
# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#define PTR_TO_REAL(x) real_##x
|
||||
#define REAL(x) __interception::PTR_TO_REAL(x)
|
||||
#define FUNC_TYPE(x) x##_f
|
||||
|
||||
#define DECLARE_REAL(ret_type, func, ...); \
|
||||
typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
|
||||
namespace __interception { \
|
||||
extern FUNC_TYPE(func) PTR_TO_REAL(func); \
|
||||
}
|
||||
|
||||
#define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...); \
|
||||
DECLARE_REAL(ret_type, func, ##__VA_ARGS__); \
|
||||
extern "C" ret_type WRAP(func)(__VA_ARGS__);
|
||||
|
||||
// Generally, you don't need to use DEFINE_REAL by itself, as INTERCEPTOR
|
||||
// macros does its job. In exceptional cases you may need to call REAL(foo)
|
||||
// without defining INTERCEPTOR(..., foo, ...). For example, if you override
|
||||
// foo with an interceptor for other function.
|
||||
#define DEFINE_REAL(ret_type, func, ...); \
|
||||
typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
|
||||
namespace __interception { \
|
||||
FUNC_TYPE(func) PTR_TO_REAL(func); \
|
||||
}
|
||||
|
||||
#define INTERCEPTOR(ret_type, func, ...) \
|
||||
DEFINE_REAL(ret_type, func, __VA_ARGS__); \
|
||||
extern "C" \
|
||||
INTERCEPTOR_ATTRIBUTE \
|
||||
ret_type WRAP(func)(__VA_ARGS__)
|
||||
|
||||
#define INCLUDED_FROM_INTERCEPTION_LIB
|
||||
|
||||
#if defined(__linux__)
|
||||
# include "interception_linux.h"
|
||||
# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX(func)
|
||||
#elif defined(__APPLE__)
|
||||
# include "interception_mac.h"
|
||||
# define OVERRIDE_FUNCTION(old_func, new_func) \
|
||||
OVERRIDE_FUNCTION_MAC(old_func, new_func)
|
||||
# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func)
|
||||
#else // defined(_WIN32)
|
||||
// FIXME: deal with interception on Win.
|
||||
# define INTERCEPT_FUNCTON(func) true
|
||||
#endif
|
||||
|
||||
#undef INCLUDED_FROM_INTERCEPTION_LIB
|
||||
|
||||
#endif // INTERCEPTION_H
|
|
@ -0,0 +1,27 @@
|
|||
//===-- interception_linux.cc -----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Linux-specific interception methods.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <dlfcn.h> // for dlsym
|
||||
|
||||
namespace __interception {
|
||||
bool GetRealFunctionAddress(const char *func_name, void **func_addr) {
|
||||
*func_addr = dlsym(RTLD_NEXT, func_name);
|
||||
return (*func_addr != NULL);
|
||||
}
|
||||
} // namespace __interception
|
||||
|
||||
|
||||
#endif // __linux__
|
|
@ -0,0 +1,33 @@
|
|||
//===-- interception_linux.h ------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Linux-specific interception methods.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#if !defined(INCLUDED_FROM_INTERCEPTION_LIB)
|
||||
# error "interception_mac.h should be included from interception library only"
|
||||
#endif
|
||||
|
||||
#ifndef INTERCEPTION_LINUX_H
|
||||
#define INTERCEPTION_LINUX_H
|
||||
|
||||
namespace __interception {
|
||||
// returns true if a function with the given name was found.
|
||||
bool GetRealFunctionAddress(const char *func_name, void **func_addr);
|
||||
} // namespace __interception
|
||||
|
||||
#define INTERCEPT_FUNCTION_LINUX(func) \
|
||||
::__interception::GetRealFunctionAddress(#func, (void**)&REAL(func))
|
||||
|
||||
#endif // INTERCEPTION_LINUX_H
|
||||
#endif // __linux__
|
|
@ -0,0 +1,34 @@
|
|||
//===-- interception_mac.cc -------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Mac-specific interception methods.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#define INCLUDED_FROM_INTERCEPTION_LIB
|
||||
#include "interception_mac.h"
|
||||
#undef INCLUDED_FROM_INTERCEPTION_LIB
|
||||
// FIXME(samsonov): Put mach_override/ under interception/
|
||||
#include "../mach_override/mach_override.h"
|
||||
|
||||
namespace __interception {
|
||||
bool OverrideFunction(void *old_func, void *new_func, void **orig_old_func) {
|
||||
*orig_old_func = NULL;
|
||||
int res = __asan_mach_override_ptr_custom(old_func, new_func,
|
||||
orig_old_func,
|
||||
__interception_allocate_island,
|
||||
__interception_deallocate_island);
|
||||
return (res == 0) && (*orig_old_func != NULL);
|
||||
}
|
||||
} // namespace __interception
|
||||
|
||||
#endif // __APPLE__
|
|
@ -0,0 +1,47 @@
|
|||
//===-- interception_mac.h --------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Mac-specific interception methods.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#if !defined(INCLUDED_FROM_INTERCEPTION_LIB)
|
||||
# error "interception_mac.h should be included from interception.h only"
|
||||
#endif
|
||||
|
||||
#ifndef INTERCEPTION_MAC_H
|
||||
#define INTERCEPTION_MAC_H
|
||||
|
||||
#include <mach/mach_error.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Allocate memory for the escape island. This cannot be moved to
|
||||
// mach_override, because each user of interceptors may specify its
|
||||
// own memory range for escape islands.
|
||||
extern "C" {
|
||||
mach_error_t __interception_allocate_island(void **ptr, size_t unused_size,
|
||||
void *unused_hint);
|
||||
mach_error_t __interception_deallocate_island(void *ptr);
|
||||
} // extern "C"
|
||||
|
||||
namespace __interception {
|
||||
// returns true if the old function existed.
|
||||
bool OverrideFunction(void *old_func, void *new_func, void **orig_old_func);
|
||||
} // namespace __interception
|
||||
|
||||
# define OVERRIDE_FUNCTION_MAC(old_func, new_func) \
|
||||
::__interception::OverrideFunction((void*)old_func, (void*)new_func, \
|
||||
(void**)&REAL(old_func))
|
||||
# define INTERCEPT_FUNCTION_MAC(func) OVERRIDE_FUNCTION_MAC(func, WRAP(func))
|
||||
|
||||
#endif // INTERCEPTION_MAC_H
|
||||
#endif // __APPLE__
|
Loading…
Reference in New Issue