AddressSanitizer run-time library. Not yet integrated with the compiler-rt build system, but can be built using the old makefile. See details in README.txt

llvm-svn: 145463
This commit is contained in:
Kostya Serebryany 2011-11-30 01:07:02 +00:00
parent 71944203de
commit 019b76f5fd
68 changed files with 11203 additions and 0 deletions

View File

@ -0,0 +1,10 @@
#===- lib/asan/Makefile.mk ---------------------------------*- Makefile -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
# See README.txt

View File

@ -0,0 +1,360 @@
#===- lib/asan/Makefile.old --------------------------------*- Makefile -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
SVNVERSION=$(shell svnversion)
OS=$(shell uname | tr '[A-Z]' '[a-z]')
ROOT=$(shell pwd)
MAKEFILE=Makefile.old # this file.
ifeq ($(ARCH), android)
ANDROID_CFLAGS= \
-DANDROID \
-D__WORDSIZE=32 \
-I$(ANDROID_BUILD_TOP)/external/stlport/stlport \
-I$(ANDROID_BUILD_TOP)/bionic \
-I$(ANDROID_BUILD_TOP)/bionic/libstdc++/include \
-I$(ANDROID_BUILD_TOP)/bionic/libc/arch-arm/include \
-I$(ANDROID_BUILD_TOP)/bionic/libc/include \
-I$(ANDROID_BUILD_TOP)/bionic/libc/kernel/common \
-I$(ANDROID_BUILD_TOP)/bionic/libc/kernel/arch-arm \
-I$(ANDROID_BUILD_TOP)/bionic/libm/include \
-I$(ANDROID_BUILD_TOP)/bionic/libm/include/arm \
-I$(ANDROID_BUILD_TOP)/bionic/libthread_db/include \
-L$(ANDROID_PRODUCT_OUT)/obj/lib
CLANG_FLAGS= \
-ccc-host-triple arm-linux-androideabi \
-D__compiler_offsetof=__builtin_offsetof \
-D__ELF__=1 \
-ccc-gcc-name arm-linux-androideabi-g++ \
$(ANDROID_CFLAGS)
CC=$(ANDROID_EABI_TOOLCHAIN)/arm-linux-androideabi-gcc $(ANDROID_CFLAGS)
CXX=$(ANDROID_EABI_TOOLCHAIN)/arm-linux-androideabi-g++ $(ANDROID_CFLAGS)
endif
ifeq ($(ARCH), arm)
# Example make command line:
# CROSSTOOL=$HOME/x-tools/arm-unknown-linux-gnueabi/ PATH=$CROSSTOOL/bin:$PATH make ARCH=arm asan_test
CLANG_FLAGS= \
-ccc-host-triple arm-unknown-linux-gnueabi \
-march=armv7-a -mfloat-abi=softfp -mfp=neon \
-ccc-gcc-name arm-unknown-linux-gnueabi-g++ \
-B$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4 \
-B$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/lib \
-I$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4/include \
-I$(CROSSTOOL)/arm-unknown-linux-gnueabi/include/c++/4.4.4 \
-I$(CROSSTOOL)/arm-unknown-linux-gnueabi/include/c++/4.4.4/arm-unknown-linux-gnueabi \
-I$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/include \
-I$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/include \
-L$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4 \
-L$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/lib \
-L$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/lib
CC=$(CROSSTOOL)/bin/arm-unknown-linux-gnueabi-gcc
CXX=$(CROSSTOOL)/bin/arm-unknown-linux-gnueabi-g++
endif
CLANG_FLAGS=
CLANG_BUILD=$(ROOT)/../../../../build/Release+Asserts
CLANG_CC=$(CLANG_BUILD)/bin/clang $(CLANG_FLAGS)
CLANG_CXX=$(CLANG_BUILD)/bin/clang++ $(CLANG_FLAGS)
CC=$(CLANG_CC)
CXX=$(CLANG_CXX)
CFLAGS:=-Wall -fvisibility=hidden
CLEANROOM_CXX=$(CXX) -Wall
INSTALL_DIR=../asan_clang_$(OS)
BIN=bin_$(OS)
LIBS=#-lpthread -ldl
ARCH=x86_64
ASAN_STACK=1
ASAN_GLOBALS=1
ASAN_USE_CALL=1
ASAN_SCALE=0 # default will be used
ASAN_OFFSET=-1 #default will be used
ASAN_UAR=0
ASAN_HAS_EXCEPTIONS=1
ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
ASAN_HAS_BLACKLIST=1
ASAN_NEEDS_SEGV=1
ASAN_PIE=0
ifeq ($(ARCH), i386)
BITS=32
SUFF=$(BITS)
CFLAGS:=$(CFLAGS) -m$(BITS)
endif
ifeq ($(ARCH), x86_64)
BITS=64
SUFF=$(BITS)
CFLAGS:=$(CFLAGS) -m$(BITS)
endif
ifeq ($(ARCH), arm)
BITS=32
SUFF=_arm
CFLAGS:=$(CFLAGS) -march=armv7-a
ASAN_HAS_EXCEPTIONS=0
endif
ifeq ($(ARCH), android)
BITS=32
SUFF=_android
CFLAGS:=$(CFLAGS)
ASAN_HAS_EXCEPTIONS=0
endif
PIE=
ifeq ($(ASAN_PIE), 1)
PIE=-fPIE -pie
endif
LIBASAN_INST_DIR=$(CLANG_BUILD)/lib/clang/$(OS)/$(ARCH)
LIBASAN_A=$(LIBASAN_INST_DIR)/libclang_rt.asan.a
BLACKLIST=
ifeq ($(ASAN_HAS_BLACKLIST), 1)
BLACKLIST=-mllvm -asan-blacklist=$(ROOT)/tests/asan_test.ignore
endif
COMMON_ASAN_DEFINES=\
-DASAN_UAR=$(ASAN_UAR) \
-DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \
-DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \
-DASAN_HAS_BLACKLIST=$(ASAN_HAS_BLACKLIST)
CLANG_ASAN_CXX=$(CLANG_CXX) \
-faddress-sanitizer \
-DADDRESS_SANITIZER=1 \
$(BLACKLIST) \
-mllvm -asan-stack=$(ASAN_STACK) \
-mllvm -asan-globals=$(ASAN_GLOBALS) \
-mllvm -asan-use-call=$(ASAN_USE_CALL) \
-mllvm -asan-mapping-scale=$(ASAN_SCALE) \
-mllvm -asan-mapping-offset-log=$(ASAN_OFFSET) \
-mllvm -asan-use-after-return=$(ASAN_UAR) \
$(COMMON_ASAN_DEFINES)
CLANG_ASAN_LD=$(CLANG_CXX) -faddress-sanitizer
GCC_ASAN_PATH=SET_FROM_COMMAND_LINE
GCC_ASAN_CXX=$(GCC_ASAN_PATH)/g++ \
-faddress-sanitizer \
-DADDRESS_SANITIZER=1 \
$(COMMON_ASAN_DEFINES)
GCC_ASAN_LD=$(GCC_ASAN_PATH)/g++ -ldl -lpthread
ASAN_COMPILER=clang
ifeq ($(ASAN_COMPILER), clang)
ASAN_CXX=$(CLANG_ASAN_CXX)
ASAN_LD=$(CLANG_ASAN_LD)
ASAN_LD_TAIL=
endif
ifeq ($(ASAN_COMPILER), gcc)
ASAN_CXX=$(GCC_ASAN_CXX)
ASAN_LD=$(GCC_ASAN_LD)
ASAN_LD_TAIL=$(LIBASAN_A)
endif
RTL_HDR_COMMON=asan_allocator.h \
asan_internal.h \
asan_interceptors.h \
asan_interface.h \
asan_lock.h \
asan_mapping.h \
asan_stack.h \
asan_stats.h \
asan_thread.h \
asan_thread_registry.h \
sysinfo/basictypes.h \
sysinfo/sysinfo.h
LIBASAN_OBJ_COMMON=$(BIN)/asan_rtl$(SUFF).o \
$(BIN)/asan_allocator$(SUFF).o \
$(BIN)/asan_globals$(SUFF).o \
$(BIN)/asan_interceptors$(SUFF).o \
$(BIN)/asan_poisoning$(SUFF).o \
$(BIN)/asan_printf$(SUFF).o \
$(BIN)/asan_stack$(SUFF).o \
$(BIN)/asan_stats$(SUFF).o \
$(BIN)/asan_thread$(SUFF).o \
$(BIN)/asan_thread_registry$(SUFF).o \
$(BIN)/sysinfo/sysinfo$(SUFF).o
ifeq ($(OS),Darwin)
RTL_HDR=$(RTL_HDR_COMMON) mach_override/mach_override.h asan_mac.h
LIBASAN_OBJ=$(LIBASAN_OBJ_COMMON) \
$(BIN)/asan_mac$(SUFF).o \
$(BIN)/asan_malloc_mac$(SUFF).o \
$(BIN)/mach_override/mach_override$(SUFF).o
else
RTL_HDR=$(RTL_HDR_COMMON)
LIBASAN_OBJ=$(LIBASAN_OBJ_COMMON) \
$(BIN)/asan_linux$(SUFF).o \
$(BIN)/asan_malloc_linux$(SUFF).o
endif
GTEST_ROOT=third_party/googletest
GTEST_INCLUDE=-I$(GTEST_ROOT)/include
GTEST_MAKE_DIR=$(GTEST_ROOT)/make-$(OS)$(SUFF)
GTEST_LIB=$(GTEST_MAKE_DIR)/gtest-all.o
all: b64 b32
test: t64 t32 output_tests lint
output_tests: b32 b64
cd tests && ./test_output.sh $(CLANG_CXX)
t64: b64
$(BIN)/asan_test64
t32: b32
$(BIN)/asan_test32
b64: | $(BIN)
$(MAKE) -f $(MAKEFILE) ARCH=x86_64 asan_test asan_benchmarks
b32: | $(BIN)
$(MAKE) -f $(MAKEFILE) ARCH=i386 asan_test asan_benchmarks
lib64:
$(MAKE) $(MAKEFILE) ARCH=x86_64 lib
lib32:
$(MAKE) $(MAKEFILE) ARCH=i386 lib
$(BIN):
mkdir -p $(BIN)
mkdir -p $(BIN)/sysinfo
mkdir -p $(BIN)/mach_override
clang:
cd ../ && llvm/rebuild_clang_and_asan.sh > /dev/null
install: install_clang
$(INSTALL_DIR):
mkdir -p $(INSTALL_DIR) $(INSTALL_DIR)/bin $(INSTALL_DIR)/lib
install_clang: | $(INSTALL_DIR)
cp -v $(CLANG_BUILD)/bin/clang $(INSTALL_DIR)/bin
cp -rv $(CLANG_BUILD)/lib/clang $(INSTALL_DIR)/lib
(cd $(INSTALL_DIR)/bin; ln -sf clang clang++)
#install_lib: | $(INSTALL_DIR)
# cp -v $(CLANG_BUILD)/lib/libasan*.a $(INSTALL_DIR)/lib
$(BIN)/asan_noinst_test$(SUFF).o: tests/asan_noinst_test.cc $(RTL_HDR) $(MAKEFILE)
$(CLEANROOM_CXX) $(PIE) $(CFLAGS) $(GTEST_INCLUDE) -I. -g -c $< -O2 -o $@
$(BIN)/asan_break_optimization$(SUFF).o: tests/asan_break_optimization.cc $(MAKEFILE)
$(CLEANROOM_CXX) $(PIE) $(CFLAGS) -c $< -O0 -o $@
$(BIN)/%_test$(SUFF).o: tests/%_test.cc $(RTL_HDR) $(MAKEFILE)
$(ASAN_CXX) $(GTEST_INCLUDE) -I. -g -c $< -O2 -o $@ $(PIE) $(CFLAGS)
$(BIN)/%_test$(SUFF).o: tests/%_test.mm $(RTL_HDR) $(MAKEFILE)
$(ASAN_CXX) $(GTEST_INCLUDE) -I. -g -c $< -O2 -o $@ -ObjC $(PIE) $(CFLAGS)
$(BIN)/%$(SUFF).o: %.cc $(RTL_HDR) $(MAKEFILE)
$(CXX) $(PIE) $(CFLAGS) -fPIC -c -o $@ -g $< -Ithird_party \
-DASAN_REVISION=\"$(SVNVERSION)\" \
-DASAN_USE_SYSINFO=1 \
-DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \
-DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \
-DASAN_FLEXIBLE_MAPPING_AND_OFFSET=$(ASAN_FLEXIBLE_MAPPING_AND_OFFSET) \
$(ASAN_FLAGS)
$(BIN)/%$(SUFF).o: %.c $(RTL_HDR) $(MAKEFILE)
$(CC) $(PIE) $(CFLAGS) -fPIC -c -o $@ -g $< -Ithird_party \
-DASAN_REVISION=\"$(SVNVERSION)\" \
-DASAN_USE_SYSINFO=1 \
$(ASAN_FLAGS)
ifeq ($(OS),Darwin)
LD_FLAGS=-framework Foundation
else
LD_FLAGS=
endif
lib: $(LIBASAN_A)
$(LIBASAN_A): $(BIN) $(LIBASAN_OBJ) $(MAKEFILE)
mkdir -p $(LIBASAN_INST_DIR)
ar ru $@ $(LIBASAN_OBJ)
$(CXX) -shared $(CFLAGS) $(LIBASAN_OBJ) $(LD_FLAGS) -o $(BIN)/libasan$(SUFF).so
TEST_OBJECTS_COMMON=\
$(BIN)/asan_test$(SUFF).o \
$(BIN)/asan_globals_test$(SUFF).o \
$(BIN)/asan_break_optimization$(SUFF).o \
$(BIN)/asan_noinst_test$(SUFF).o \
$(BIN)/asan_interface_test$(SUFF).o
BENCHMARK_OBJECTS=\
$(BIN)/asan_benchmarks_test$(SUFF).o \
$(BIN)/asan_break_optimization$(SUFF).o
ifeq ($(OS),Darwin)
TEST_OBJECTS=$(TEST_OBJECTS_COMMON) \
$(BIN)/asan_mac_test$(SUFF).o
else
TEST_OBJECTS=$(TEST_OBJECTS_COMMON)
endif
$(BIN)/asan_test$(SUFF): $(TEST_OBJECTS) $(LIBASAN_A) $(MAKEFILE) tests/asan_test.ignore $(GTEST_LIB)
$(ASAN_LD) $(PIE) $(CFLAGS) -g -O3 $(TEST_OBJECTS) \
$(LD_FLAGS) -o $@ $(LIBS) $(GTEST_LIB) $(ASAN_LD_TAIL)
$(BIN)/asan_benchmarks$(SUFF): $(BENCHMARK_OBJECTS) $(LIBASAN_A) $(MAKEFILE) $(GTEST_LIB)
$(ASAN_LD) $(PIE) $(CFLAGS) -g -O3 $(BENCHMARK_OBJECTS) \
$(LD_FLAGS) -o $@ $(LIBS) $(GTEST_LIB) $(ASAN_LD_TAIL)
asan_test: $(BIN)/asan_test$(SUFF)
asan_benchmarks: $(BIN)/asan_benchmarks$(SUFF)
# for now, build gtest with clang/asan even if we use a different compiler.
$(GTEST_LIB):
mkdir -p $(GTEST_MAKE_DIR) && \
cd $(GTEST_MAKE_DIR) && \
$(MAKE) -f ../make/Makefile CXXFLAGS="$(PIE) $(CFLAGS) -g -w" \
CXX="$(CLANG_ASAN_CXX)"
RTL_LINT_FITLER=-readability/casting,-readability/check,-build/include,-build/header_guard,-build/class,-legal/copyright
# TODO(kcc): remove these filters one by one
TEST_LINT_FITLER=-readability/casting,-build/include,-legal/copyright,-whitespace/newline,-runtime/sizeof,-runtime/int,-runtime/printf
LLVM_LINT_FILTER=-,+whitespace
ADDRESS_SANITIZER_CPP=../../../../lib/Transforms/Instrumentation/AddressSanitizer.cpp
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=$(TEST_LINT_FITLER) tests/*.cc
get_third_party:
rm -rf third_party
mkdir third_party
(cd third_party && \
svn co -r375 http://googletest.googlecode.com/svn/trunk googletest && \
svn co -r69 http://google-styleguide.googlecode.com/svn/trunk/cpplint cpplint \
)
clean:
rm -f *.o *.ll *.S *.a *.log asan_test64* asan_test32* a.out perf.data log
rm -rf $(BIN)
rm -rf $(GTEST_ROOT)/make-*

View File

@ -0,0 +1,26 @@
AddressSanitizer RT
================================
This directory contains sources of the AddressSanitizer (asan) run-time library.
We are in the process of integrating AddressSanitizer with LLVM, stay tuned.
Directory structre:
README.txt : This file.
Makefile.mk : Currently a stub for a proper makefile. not usable.
Makefile.old : Old out-of-tree makefile, the only usable one so far.
asan_*.{cc,h} : Sources of the asan run-time lirbary.
mach_override/* : Utility to override functions on Darwin (MIT License).
sysinfo/* : Portable utility to iterate over /proc/maps (BSD License).
scripts/* : Helper scripts.
Temporary build instructions (verified on linux):
cd lib/asan
make -f Makefile.old get_third_party # gets googletest and cpplint
make -f Makefile.old test -j 8 CLANG_BUILD=/path/to/Release+Asserts
# Optional:
# make -f Makefile.old install # installs clang and rt to lib/asan_clang_linux
For more info see http://code.google.com/p/address-sanitizer/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,156 @@
//===-- asan_allocator.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.
//
// ASan-private header for asan_allocator.cc.
//===----------------------------------------------------------------------===//
#ifndef ASAN_ALLOCATOR_H
#define ASAN_ALLOCATOR_H
#include "asan_internal.h"
#include "asan_interceptors.h"
namespace __asan {
static const size_t kNumberOfSizeClasses = 255;
class AsanChunk;
class AsanChunkFifoList {
public:
explicit AsanChunkFifoList(LinkerInitialized) { }
AsanChunkFifoList() { clear(); }
void Push(AsanChunk *n);
void PushList(AsanChunkFifoList *q);
AsanChunk *Pop();
size_t size() { return size_; }
void clear() {
first_ = last_ = NULL;
size_ = 0;
}
private:
AsanChunk *first_;
AsanChunk *last_;
size_t size_;
};
struct AsanThreadLocalMallocStorage {
explicit AsanThreadLocalMallocStorage(LinkerInitialized x)
: quarantine_(x) { }
AsanThreadLocalMallocStorage() {
CHECK(real_memset);
real_memset(this, 0, sizeof(AsanThreadLocalMallocStorage));
}
AsanChunkFifoList quarantine_;
AsanChunk *free_lists_[kNumberOfSizeClasses];
void CommitBack();
};
// Fake stack frame contains local variables of one function.
// This struct should fit into a stack redzone (32 bytes).
struct FakeFrame {
uintptr_t magic; // Modified by the instrumented code.
uintptr_t descr; // Modified by the instrumented code.
FakeFrame *next;
uint64_t real_stack : 48;
uint64_t size_minus_one : 16;
};
struct FakeFrameFifo {
public:
void FifoPush(FakeFrame *node);
FakeFrame *FifoPop();
private:
FakeFrame *first_, *last_;
};
class FakeFrameLifo {
public:
void LifoPush(FakeFrame *node) {
node->next = top_;
top_ = node;
}
void LifoPop() {
CHECK(top_);
top_ = top_->next;
}
FakeFrame *top() { return top_; }
private:
FakeFrame *top_;
};
// For each thread we create a fake stack and place stack objects on this fake
// stack instead of the real stack. The fake stack is not really a stack but
// a fast malloc-like allocator so that when a function exits the fake stack
// is not poped but remains there for quite some time until gets used again.
// So, we poison the objects on the fake stack when function returns.
// It helps us find use-after-return bugs.
// We can not rely on __asan_stack_free being called on every function exit,
// so we maintain a lifo list of all current fake frames and update it on every
// call to __asan_stack_malloc.
class FakeStack {
public:
FakeStack();
explicit FakeStack(LinkerInitialized) {}
void Init(size_t stack_size);
void Cleanup();
uintptr_t AllocateStack(size_t size, size_t real_stack);
static void OnFree(size_t ptr, size_t size, size_t real_stack);
// Return the bottom of the maped region.
uintptr_t AddrIsInFakeStack(uintptr_t addr);
private:
static const size_t kMinStackFrameSizeLog = 9; // Min frame is 512B.
static const size_t kMaxStackFrameSizeLog = 16; // Max stack frame is 64K.
static const size_t kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog;
static const size_t kNumberOfSizeClasses =
kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1;
bool AddrIsInSizeClass(uintptr_t addr, size_t size_class);
// Each size class should be large enough to hold all frames.
size_t ClassMmapSize(size_t size_class);
size_t ClassSize(size_t size_class) {
return 1UL << (size_class + kMinStackFrameSizeLog);
}
void DeallocateFrame(FakeFrame *fake_frame);
size_t ComputeSizeClass(size_t alloc_size);
void AllocateOneSizeClass(size_t size_class);
size_t stack_size_;
bool alive_;
uintptr_t allocated_size_classes_[kNumberOfSizeClasses];
FakeFrameFifo size_classes_[kNumberOfSizeClasses];
FakeFrameLifo call_stack_;
};
void *asan_memalign(size_t alignment, size_t size, AsanStackTrace *stack);
void asan_free(void *ptr, AsanStackTrace *stack);
void *asan_malloc(size_t size, AsanStackTrace *stack);
void *asan_calloc(size_t nmemb, size_t size, AsanStackTrace *stack);
void *asan_realloc(void *p, size_t size, AsanStackTrace *stack);
void *asan_valloc(size_t size, AsanStackTrace *stack);
void *asan_pvalloc(size_t size, AsanStackTrace *stack);
int asan_posix_memalign(void **memptr, size_t alignment, size_t size,
AsanStackTrace *stack);
size_t __asan_mz_size(const void *ptr);
void __asan_mz_force_lock();
void __asan_mz_force_unlock();
void DescribeHeapAddress(uintptr_t addr, size_t access_size);
} // namespace __asan
#endif // ASAN_ALLOCATOR_H

View File

@ -0,0 +1,144 @@
//===-- asan_globals.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.
//
// Handle globals.
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
#include "asan_interface.h"
#include "asan_internal.h"
#include "asan_lock.h"
#include "asan_mapping.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
#include <ctype.h>
#include <map>
namespace __asan {
typedef __asan_global Global;
static AsanLock mu_for_globals(LINKER_INITIALIZED);
typedef std::map<uintptr_t, Global> MapOfGlobals;
static MapOfGlobals *g_all_globals = NULL;
void PoisonRedZones(const Global &g) {
uintptr_t shadow = MemToShadow(g.beg);
size_t ShadowRZSize = kGlobalAndStackRedzone >> SHADOW_SCALE;
CHECK(ShadowRZSize == 1 || ShadowRZSize == 2 || ShadowRZSize == 4);
// full right redzone
uintptr_t right_rz2_offset = ShadowRZSize *
((g.size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone);
real_memset((uint8_t*)shadow + right_rz2_offset,
kAsanGlobalRedzoneMagic, ShadowRZSize);
if ((g.size % kGlobalAndStackRedzone) != 0) {
// partial right redzone
uint64_t right_rz1_offset =
ShadowRZSize * (g.size / kGlobalAndStackRedzone);
CHECK(right_rz1_offset == right_rz2_offset - ShadowRZSize);
PoisonShadowPartialRightRedzone((uint8_t*)(shadow + right_rz1_offset),
g.size % kGlobalAndStackRedzone,
kGlobalAndStackRedzone,
SHADOW_GRANULARITY,
kAsanGlobalRedzoneMagic);
}
}
static size_t GetAlignedSize(size_t size) {
return ((size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone)
* kGlobalAndStackRedzone;
}
// Check if the global is a zero-terminated ASCII string. If so, print it.
void PrintIfASCII(const Global &g) {
for (size_t p = g.beg; p < g.beg + g.size - 1; p++) {
if (!isascii(*(char*)p)) return;
}
if (*(char*)(g.beg + g.size - 1) != 0) return;
Printf(" '%s' is ascii string '%s'\n", g.name, g.beg);
}
bool DescribeAddrIfMyRedZone(const Global &g, uintptr_t addr) {
if (addr < g.beg - kGlobalAndStackRedzone) return false;
if (addr >= g.beg + g.size_with_redzone) return false;
Printf("%p is located ", addr);
if (addr < g.beg) {
Printf("%d bytes to the left", g.beg - addr);
} else if (addr >= g.beg + g.size) {
Printf("%d bytes to the right", addr - (g.beg + g.size));
} else {
Printf("%d bytes inside", addr - g.beg); // Can it happen?
}
Printf(" of global variable '%s' (0x%lx) of size %ld\n",
g.name, g.beg, g.size);
PrintIfASCII(g);
return true;
}
bool DescribeAddrIfGlobal(uintptr_t addr) {
if (!FLAG_report_globals) return false;
ScopedLock lock(&mu_for_globals);
if (!g_all_globals) return false;
bool res = false;
// Just iterate. May want to use binary search instead.
for (MapOfGlobals::iterator i = g_all_globals->begin(),
end = g_all_globals->end(); i != end; ++i) {
Global &g = i->second;
CHECK(i->first == g.beg);
if (FLAG_report_globals >= 2)
Printf("Search Global: beg=%p size=%ld name=%s\n",
g.beg, g.size, g.name);
res |= DescribeAddrIfMyRedZone(g, addr);
}
return res;
}
// Register a global variable.
// This function may be called more than once for every global
// so we store the globals in a map.
static void RegisterGlobal(const Global *g) {
CHECK(asan_inited);
if (!FLAG_report_globals) return;
ScopedLock lock(&mu_for_globals);
if (!g_all_globals)
g_all_globals = new MapOfGlobals;
CHECK(AddrIsInMem(g->beg));
if (FLAG_report_globals >= 2)
Printf("Added Global: beg=%p size=%ld name=%s\n",
g->beg, g->size, g->name);
PoisonRedZones(*g);
(*g_all_globals)[g->beg] = *g;
}
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
// Register one global with a default redzone.
void __asan_register_global(uintptr_t addr, size_t size,
const char *name) {
Global g;
g.beg = addr;
g.size = size;
g.size_with_redzone = GetAlignedSize(size) + kGlobalAndStackRedzone;
g.name = name;
RegisterGlobal(&g);
}
// Register an array of globals.
void __asan_register_globals(__asan_global *globals, size_t n) {
for (size_t i = 0; i < n; i++) {
RegisterGlobal(&globals[i]);
}
}

View File

@ -0,0 +1,304 @@
//===-- asan_interceptors.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.
//
// Intercept various libc functions to catch buggy memory accesses there.
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
#include "asan_allocator.h"
#include "asan_interface.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include <algorithm>
#include <dlfcn.h>
#include <string.h>
namespace __asan {
index_f real_index;
memcpy_f real_memcpy;
memmove_f real_memmove;
memset_f real_memset;
strchr_f real_strchr;
strcmp_f real_strcmp;
strcpy_f real_strcpy;
strdup_f real_strdup;
strlen_f real_strlen;
strncmp_f real_strncmp;
strncpy_f real_strncpy;
strnlen_f real_strnlen;
// Instruments read/write access to a single byte in memory.
// On error calls __asan_report_error, which aborts the program.
__attribute__((noinline))
static void AccessAddress(uintptr_t address, bool isWrite) {
if (__asan_address_is_poisoned((void*)address)) {
GET_BP_PC_SP;
__asan_report_error(pc, bp, sp, address, isWrite, /* access_size */ 1);
}
}
// We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE,
// and ASAN_WRITE_RANGE as macro instead of function so
// that no extra frames are created, and stack trace contains
// relevant information only.
// Instruments read/write access to a memory range.
// More complex implementation is possible, for now just
// checking the first and the last byte of a range.
#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \
if (size > 0) { \
uintptr_t ptr = (uintptr_t)(offset); \
AccessAddress(ptr, isWrite); \
AccessAddress(ptr + (size) - 1, isWrite); \
} \
} while (0);
#define ASAN_READ_RANGE(offset, size) do { \
ACCESS_MEMORY_RANGE(offset, size, false); \
} while (0);
#define ASAN_WRITE_RANGE(offset, size) do { \
ACCESS_MEMORY_RANGE(offset, size, true); \
} while (0);
// Behavior of functions like "memcpy" or "strcpy" is undefined
// if memory intervals overlap. We report error in this case.
// Macro is used to avoid creation of new frames.
static inline bool RangesOverlap(const char *offset1, const char *offset2,
size_t length) {
return !((offset1 + length <= offset2) || (offset2 + length <= offset1));
}
#define CHECK_RANGES_OVERLAP(_offset1, _offset2, length) do { \
const char *offset1 = (const char*)_offset1; \
const char *offset2 = (const char*)_offset2; \
if (RangesOverlap((const char*)offset1, (const char*)offset2, \
length)) { \
Printf("ERROR: AddressSanitizer strcpy-param-overlap: " \
"memory ranges [%p,%p) and [%p, %p) overlap\n", \
offset1, offset1 + length, offset2, offset2 + length); \
PRINT_CURRENT_STACK(); \
ShowStatsAndAbort(); \
} \
} while (0);
static inline void ensure_asan_inited() {
CHECK(!asan_init_is_running);
if (!asan_inited) {
__asan_init();
}
}
size_t internal_strlen(const char *s) {
size_t i = 0;
while (s[i]) i++;
return i;
}
size_t internal_strnlen(const char *s, size_t maxlen) {
if (real_strnlen != NULL) {
return real_strnlen(s, maxlen);
}
size_t i = 0;
while (i < maxlen && s[i]) i++;
return i;
}
void InitializeAsanInterceptors() {
#ifndef __APPLE__
INTERCEPT_FUNCTION(index);
#else
OVERRIDE_FUNCTION(index, WRAP(strchr));
#endif
#ifndef __APPLE__
INTERCEPT_FUNCTION(memcpy);
INTERCEPT_FUNCTION(memmove);
INTERCEPT_FUNCTION(memset);
#else
real_memcpy = memcpy;
real_memmove = memmove;
real_memset = memset;
#endif
INTERCEPT_FUNCTION(strchr);
INTERCEPT_FUNCTION(strcmp);
INTERCEPT_FUNCTION(strcpy); // NOLINT
INTERCEPT_FUNCTION(strdup);
INTERCEPT_FUNCTION(strlen);
INTERCEPT_FUNCTION(strncmp);
INTERCEPT_FUNCTION(strncpy);
#ifndef __APPLE__
INTERCEPT_FUNCTION(strnlen);
#endif
if (FLAG_v > 0) {
Printf("AddressSanitizer: libc interceptors initialized\n");
}
}
} // namespace __asan
// ---------------------- Wrappers ---------------- {{{1
using namespace __asan; // NOLINT
void *WRAP(memcpy)(void *to, const void *from, size_t size) {
// memcpy is called during __asan_init() from the internals
// of printf(...).
if (asan_init_is_running) {
return real_memcpy(to, from, size);
}
ensure_asan_inited();
if (FLAG_replace_intrin) {
CHECK_RANGES_OVERLAP(to, from, size);
ASAN_WRITE_RANGE(from, size);
ASAN_READ_RANGE(to, size);
}
return real_memcpy(to, from, size);
}
void *WRAP(memmove)(void *to, const void *from, size_t size) {
ensure_asan_inited();
if (FLAG_replace_intrin) {
ASAN_WRITE_RANGE(from, size);
ASAN_READ_RANGE(to, size);
}
return real_memmove(to, from, size);
}
void *WRAP(memset)(void *block, int c, size_t size) {
ensure_asan_inited();
if (FLAG_replace_intrin) {
ASAN_WRITE_RANGE(block, size);
}
return real_memset(block, c, size);
}
// Note that on Linux index and strchr are definined differently depending on
// the compiler (gcc vs clang).
// see __CORRECT_ISO_CPP_STRING_H_PROTO in /usr/include/string.h
#ifndef __APPLE__
char *WRAP(index)(const char *str, int c)
__attribute__((alias(WRAPPER_NAME(strchr))));
#endif
char *WRAP(strchr)(const char *str, int c) {
ensure_asan_inited();
char *result = real_strchr(str, c);
if (FLAG_replace_str) {
size_t bytes_read = (result ? result - str : real_strlen(str)) + 1;
ASAN_READ_RANGE(str, bytes_read);
}
return result;
}
static inline int CharCmp(unsigned char c1, unsigned char c2) {
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
}
int WRAP(strcmp)(const char *s1, const char *s2) {
// strcmp is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac.
if (asan_init_is_running) {
return real_strcmp(s1, s2);
}
unsigned char c1, c2;
size_t i;
for (i = 0; ; i++) {
c1 = (unsigned char)s1[i];
c2 = (unsigned char)s2[i];
if (c1 != c2 || c1 == '\0') break;
}
ASAN_READ_RANGE(s1, i + 1);
ASAN_READ_RANGE(s2, i + 1);
return CharCmp(c1, c2);
}
char *WRAP(strcpy)(char *to, const char *from) { // NOLINT
// strcpy is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac.
if (asan_init_is_running) {
return real_strcpy(to, from);
}
ensure_asan_inited();
if (FLAG_replace_str) {
size_t from_size = real_strlen(from) + 1;
CHECK_RANGES_OVERLAP(to, from, from_size);
ASAN_READ_RANGE(from, from_size);
ASAN_WRITE_RANGE(to, from_size);
}
return real_strcpy(to, from);
}
char *WRAP(strdup)(const char *s) {
ensure_asan_inited();
if (FLAG_replace_str) {
size_t length = real_strlen(s);
ASAN_READ_RANGE(s, length + 1);
}
return real_strdup(s);
}
size_t WRAP(strlen)(const char *s) {
// strlen is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac.
if (asan_init_is_running) {
return real_strlen(s);
}
ensure_asan_inited();
size_t length = real_strlen(s);
if (FLAG_replace_str) {
ASAN_READ_RANGE(s, length + 1);
}
return length;
}
int WRAP(strncmp)(const char *s1, const char *s2, size_t size) {
// strncmp is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac.
if (asan_init_is_running) {
return real_strncmp(s1, s2, size);
}
unsigned char c1 = 0, c2 = 0;
size_t i;
for (i = 0; i < size; i++) {
c1 = (unsigned char)s1[i];
c2 = (unsigned char)s2[i];
if (c1 != c2 || c1 == '\0') break;
}
ASAN_READ_RANGE(s1, std::min(i + 1, size));
ASAN_READ_RANGE(s2, std::min(i + 1, size));
return CharCmp(c1, c2);
}
char *WRAP(strncpy)(char *to, const char *from, size_t size) {
ensure_asan_inited();
if (FLAG_replace_str) {
size_t from_size = std::min(size, internal_strnlen(from, size) + 1);
CHECK_RANGES_OVERLAP(to, from, from_size);
ASAN_READ_RANGE(from, from_size);
ASAN_WRITE_RANGE(to, size);
}
return real_strncpy(to, from, size);
}
#ifndef __APPLE__
size_t WRAP(strnlen)(const char *s, size_t maxlen) {
ensure_asan_inited();
size_t length = real_strnlen(s, maxlen);
if (FLAG_replace_str) {
ASAN_READ_RANGE(s, std::min(length + 1, maxlen));
}
return length;
}
#endif

View File

@ -0,0 +1,105 @@
//===-- asan_interceptors.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.
//
// ASan-private header for asan_interceptors.cc
//===----------------------------------------------------------------------===//
#ifndef ASAN_INTERCEPTORS_H
#define ASAN_INTERCEPTORS_H
#include "asan_internal.h"
// 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().
//
// TODO(glider): mach_override_ptr() tends to spend too much time
// in allocateBranchIsland(). This should be ok for real-word
// application, but slows down our tests which fork too many children.
#ifdef __APPLE__
#include "mach_override.h"
#define WRAP(x) wrap_##x
#define WRAPPER_NAME(x) "wrap_"#x
#define OVERRIDE_FUNCTION(oldfunc, newfunc) \
CHECK(0 == mach_override_ptr((void*)(oldfunc), \
(void*)(newfunc), \
(void**)&real_##oldfunc)); \
CHECK(real_##oldfunc != NULL);
#define INTERCEPT_FUNCTION(func) \
OVERRIDE_FUNCTION(func, WRAP(func))
#else
#define WRAP(x) x
#define WRAPPER_NAME(x) #x
#define INTERCEPT_FUNCTION(func) \
CHECK((real_##func = (func##_f)dlsym(RTLD_NEXT, #func)));
#endif
#ifdef __APPLE__
void *WRAP(memcpy)(void *to, const void *from, size_t size);
void *WRAP(memmove)(void *to, const void *from, size_t size);
void *WRAP(memset)(void *block, int c, size_t size);
const char *WRAP(strchr)(const char *string, int c);
int WRAP(strcmp)(const char *s1, const char *s2);
char *WRAP(strcpy)(char *to, const char *from); // NOLINT
char *WRAP(strdup)(const char *s);
size_t WRAP(strlen)(const char *s);
int WRAP(strncmp)(const char *s1, const char *s2, size_t size);
char *WRAP(strncpy)(char *to, const char *from, size_t size);
#endif
namespace __asan {
typedef void* (*index_f)(const char *string, int c);
typedef void* (*memcpy_f)(void *to, const void *from, size_t size);
typedef void* (*memmove_f)(void *to, const void *from, size_t size);
typedef void* (*memset_f)(void *block, int c, size_t size);
typedef char* (*strchr_f)(const char *str, int c);
typedef int (*strcmp_f)(const char *s1, const char *s2);
typedef char* (*strcpy_f)(char *to, const char *from);
typedef char* (*strdup_f)(const char *s);
typedef size_t (*strlen_f)(const char *s);
typedef int (*strncmp_f)(const char *s1, const char *s2, size_t size);
typedef char* (*strncpy_f)(char *to, const char *from, size_t size);
typedef size_t (*strnlen_f)(const char *s, size_t maxlen);
// __asan::real_X() holds pointer to library implementation of X().
extern index_f real_index;
extern memcpy_f real_memcpy;
extern memmove_f real_memmove;
extern memset_f real_memset;
extern strchr_f real_strchr;
extern strcmp_f real_strcmp;
extern strcpy_f real_strcpy;
extern strdup_f real_strdup;
extern strlen_f real_strlen;
extern strncmp_f real_strncmp;
extern strncpy_f real_strncpy;
extern strnlen_f real_strnlen;
// __asan::internal_X() is the implementation of X() for use in RTL.
size_t internal_strlen(const char *s);
size_t internal_strnlen(const char *s, size_t maxlen);
// Initializes pointers to str*/mem* functions.
void InitializeAsanInterceptors();
} // namespace __asan
#endif // ASAN_INTERCEPTORS_H

View File

@ -0,0 +1,135 @@
//===-- asan_interface.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.
//
// This header can be included by the instrumented program to fetch
// data (mostly allocator statistics) from ASan runtime library.
//===----------------------------------------------------------------------===//
#ifndef ASAN_INTERFACE_H
#define ASAN_INTERFACE_H
#include <stdint.h> // for __WORDSIZE
#include <stdlib.h> // for size_t
// This header should NOT include any other headers from ASan runtime.
// All functions in this header are extern "C" and start with __asan_.
extern "C" {
// This function should be called at the very beginning of the process,
// before any instrumented code is executed and before any call to malloc.
void __asan_init()
__attribute__((visibility("default")));
// This function should be called by the instrumented code.
// 'addr' is the address of a global variable called 'name' of 'size' bytes.
void __asan_register_global(uintptr_t addr, size_t size, const char *name)
__attribute__((visibility("default")));
// This structure describes an instrumented global variable.
struct __asan_global {
size_t beg; // The address of the global.
size_t size; // The original size of the global.
size_t size_with_redzone; // The size with the redzone.
const char *name; // Name as a C string.
};
// This function should be called by the instrumented code.
// gets an array of structures describing globals.
void __asan_register_globals(__asan_global *globals, size_t n)
__attribute__((visibility("default")));
// These two functions are used by the instrumented code in the
// use-after-return mode. __asan_stack_malloc allocates size bytes of
// fake stack and __asan_stack_free poisons it. real_stack is a pointer to
// the real stack region.
size_t __asan_stack_malloc(size_t size, size_t real_stack)
__attribute__((visibility("default")));
void __asan_stack_free(size_t ptr, size_t size, size_t real_stack)
__attribute__((visibility("default")));
// Marks memory region [addr, addr+size) as unaddressable.
// This memory must be previously allocated by the user program. Accessing
// addresses in this region from instrumented code is forbidden until
// this region is unpoisoned. This function is not guaranteed to poison
// the whole region - it may poison only subregion of [addr, addr+size) due
// to ASan alignment restrictions.
// Method is NOT thread-safe in the sense that no two threads can
// (un)poison memory in the same memory region simultaneously.
void __asan_poison_memory_region(void const volatile *addr, size_t size);
// Marks memory region [addr, addr+size) as addressable.
// This memory must be previously allocated by the user program. Accessing
// addresses in this region is allowed until this region is poisoned again.
// This function may unpoison a superregion of [addr, addr+size) due to
// ASan alignment restrictions.
// Method is NOT thread-safe in the sense that no two threads can
// (un)poison memory in the same memory region simultaneously.
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
// User code should use macro instead of functions.
#ifdef ADDRESS_SANITIZER
#define ASAN_POISON_MEMORY_REGION(addr, size) \
__asan_poison_memory_region((addr), (size))
#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
__asan_unpoison_memory_region((addr), (size))
#else
#define ASAN_POISON_MEMORY_REGION(addr, size) \
((void)(addr), (void)(size))
#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
((void)(addr), (void)(size))
#endif
// Returns true iff addr is poisoned (i.e. 1-byte read/write access to this
// address will result in error report from AddressSanitizer).
bool __asan_address_is_poisoned(void const volatile *addr);
// This is an internal function that is called to report an error.
// However it is still a part of the interface because users may want to
// set a breakpoint on this function in a debugger.
void __asan_report_error(uintptr_t pc, uintptr_t bp, uintptr_t sp,
uintptr_t addr, bool is_write, size_t access_size)
__attribute__((visibility("default")));
// Sets the exit code to use when reporting an error.
// Returns the old value.
int __asan_set_error_exit_code(int exit_code);
// Returns the estimated number of bytes that will be reserved by allocator
// for request of "size" bytes. If ASan allocator can't allocate that much
// memory, returns the maximal possible allocation size, otherwise returns
// "size".
size_t __asan_get_estimated_allocated_size(size_t size);
// Returns true if p is NULL or if p was returned by the ASan allocator and
// is not yet freed.
bool __asan_get_ownership(const void *p);
// Returns the number of bytes reserved for the pointer p.
// Requires (get_ownership(p) == true).
size_t __asan_get_allocated_size(const void *p);
// Number of bytes, allocated and not yet freed by the application.
size_t __asan_get_current_allocated_bytes();
// Number of bytes, mmaped by asan allocator to fulfill allocation requests.
// Generally, for request of X bytes, allocator can reserve and add to free
// lists a large number of chunks of size X to use them for future requests.
// All these chunks count toward the heap size. Currently, allocator never
// releases memory to OS (instead, it just puts freed chunks to free lists).
size_t __asan_get_heap_size();
// Number of bytes, mmaped by asan allocator, which can be used to fulfill
// allocation requests. When a user program frees memory chunk, it can first
// fall into quarantine and will count toward __asan_get_free_bytes() later.
size_t __asan_get_free_bytes();
// Number of bytes in unmapped pages, that are released to OS. Currently,
// always returns 0.
size_t __asan_get_unmapped_bytes();
// Turns on/off statistics update. Returns the previous value.
bool __asan_enable_statistics(bool enable);
// Prints accumulated stats to stderr. Used for debugging.
void __asan_print_accumulated_stats();
} // namespace
#endif // ASAN_INTERFACE_H

View File

@ -0,0 +1,178 @@
//===-- asan_internal.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.
//
// ASan-private header which defines various general utilities.
//===----------------------------------------------------------------------===//
#ifndef ASAN_INTERNAL_H
#define ASAN_INTERNAL_H
#include <stdint.h> // for __WORDSIZE
#include <stdlib.h> // for size_t
#include <unistd.h> // for _exit
#ifdef ANDROID
#include <sys/atomics.h>
#endif
#ifdef ADDRESS_SANITIZER
# error "The AddressSanitizer run-time should not be"
" instrumented by AddressSanitizer"
#endif
// All internal functions in asan reside inside the __asan namespace
// to avoid namespace collisions with the user programs.
// Seperate namespace also makes it simpler to distinguish the asan run-time
// functions from the instrumented user code in a profile.
namespace __asan {
class AsanThread;
struct AsanStackTrace;
// asan_rtl.cc
void CheckFailed(const char *cond, const char *file, int line);
void ShowStatsAndAbort();
// asan_globals.cc
bool DescribeAddrIfGlobal(uintptr_t addr);
// asan_malloc_linux.cc / asan_malloc_mac.cc
void ReplaceSystemMalloc();
// asan_linux.cc / asan_mac.cc
void *AsanDoesNotSupportStaticLinkage();
void *asan_mmap(void *addr, size_t length, int prot, int flags,
int fd, uint64_t offset);
ssize_t asan_write(int fd, const void *buf, size_t count);
// asan_printf.cc
void RawWrite(const char *buffer);
int SNPrint(char *buffer, size_t length, const char *format, ...);
void Printf(const char *format, ...);
void Report(const char *format, ...);
extern size_t FLAG_quarantine_size;
extern int FLAG_demangle;
extern bool FLAG_symbolize;
extern int FLAG_v;
extern bool FLAG_mt;
extern size_t FLAG_redzone;
extern int FLAG_debug;
extern bool FLAG_poison_shadow;
extern int FLAG_report_globals;
extern size_t FLAG_malloc_context_size;
extern bool FLAG_stats;
extern bool FLAG_replace_str;
extern bool FLAG_replace_intrin;
extern bool FLAG_replace_cfallocator;
extern bool FLAG_fast_unwind;
extern bool FLAG_use_fake_stack;
extern size_t FLAG_max_malloc_fill_size;
extern int FLAG_exitcode;
extern bool FLAG_allow_user_poisoning;
extern int asan_inited;
// Used to avoid infinite recursion in __asan_init().
extern bool asan_init_is_running;
enum LinkerInitialized { LINKER_INITIALIZED = 0 };
#ifndef ASAN_DIE
#define ASAN_DIE _exit(FLAG_exitcode)
#endif // ASAN_DIE
#define CHECK(cond) do { if (!(cond)) { \
CheckFailed(#cond, __FILE__, __LINE__); \
}}while(0)
#define RAW_CHECK_MSG(expr, msg) do { \
if (!(expr)) { \
RawWrite(msg); \
ASAN_DIE; \
} \
} while (0)
#define RAW_CHECK(expr) RAW_CHECK_MSG(expr, #expr)
#define UNIMPLEMENTED() CHECK("unimplemented" && 0)
#define ASAN_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
const size_t kWordSize = __WORDSIZE / 8;
const size_t kWordSizeInBits = 8 * kWordSize;
const size_t kPageSizeBits = 12;
const size_t kPageSize = 1UL << kPageSizeBits;
#define GET_CALLER_PC() (uintptr_t)__builtin_return_address(0)
#define GET_CURRENT_FRAME() (uintptr_t)__builtin_frame_address(0)
#define GET_BP_PC_SP \
uintptr_t bp = GET_CURRENT_FRAME(); \
uintptr_t pc = GET_CALLER_PC(); \
uintptr_t local_stack; \
uintptr_t sp = (uintptr_t)&local_stack;
// These magic values are written to shadow for better error reporting.
const int kAsanHeapLeftRedzoneMagic = 0xfa;
const int kAsanHeapRightRedzoneMagic = 0xfb;
const int kAsanHeapFreeMagic = 0xfd;
const int kAsanStackLeftRedzoneMagic = 0xf1;
const int kAsanStackMidRedzoneMagic = 0xf2;
const int kAsanStackRightRedzoneMagic = 0xf3;
const int kAsanStackPartialRedzoneMagic = 0xf4;
const int kAsanStackAfterReturnMagic = 0xf5;
const int kAsanUserPoisonedMemoryMagic = 0xf7;
const int kAsanGlobalRedzoneMagic = 0xf9;
static const uintptr_t kCurrentStackFrameMagic = 0x41B58AB3;
static const uintptr_t kRetiredStackFrameMagic = 0x45E0360E;
// Poison the shadow memory which corresponds to 'redzone_size' bytes
// of the original memory, where first 'size' bytes are addressable.
static inline void
PoisonShadowPartialRightRedzone(unsigned char *shadow,
uintptr_t size,
uintptr_t redzone_size,
uintptr_t shadow_granularity,
unsigned char magic) {
for (uintptr_t i = 0; i < redzone_size;
i+= shadow_granularity, shadow++) {
if (i + shadow_granularity <= size) {
*shadow = 0; // fully addressable
} else if (i >= size) {
*shadow = (shadow_granularity == 128) ? 0xff : magic; // unaddressable
} else {
*shadow = size - i; // first size-i bytes are addressable
}
}
}
// -------------------------- Atomic ---------------- {{{1
static inline int AtomicInc(int *a) {
if (!FLAG_mt) return ++(*a);
#ifdef ANDROID
return __atomic_inc(a) + 1;
#else
return __sync_add_and_fetch(a, 1);
#endif
}
static inline int AtomicDec(int *a) {
if (!FLAG_mt) return --(*a);
#ifdef ANDROID
return __atomic_dec(a) - 1;
#else
return __sync_add_and_fetch(a, -1);
#endif
}
} // namespace __asan
#endif // ASAN_INTERNAL_H

View File

@ -0,0 +1,48 @@
//===-- asan_linux.cc -----------------------------------------------------===//
//
// 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 details.
//===----------------------------------------------------------------------===//
#include "asan_internal.h"
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
extern char _DYNAMIC[];
namespace __asan {
void *AsanDoesNotSupportStaticLinkage() {
// This will fail to link with -static.
return &_DYNAMIC;
}
#ifdef ANDROID
#define SYS_mmap2 __NR_mmap2
#define SYS_write __NR_write
#endif
void *asan_mmap(void *addr, size_t length, int prot, int flags,
int fd, uint64_t offset) {
# if __WORDSIZE == 64
return (void *)syscall(SYS_mmap, addr, length, prot, flags, fd, offset);
# else
return (void *)syscall(SYS_mmap2, addr, length, prot, flags, fd, offset);
# endif
}
ssize_t asan_write(int fd, const void *buf, size_t count) {
return (ssize_t)syscall(SYS_write, fd, buf, count);
}
} // namespace __asan

View File

@ -0,0 +1,100 @@
//===-- asan_lock.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.
//
// A wrapper for a simple lock.
//===----------------------------------------------------------------------===//
#ifndef ASAN_LOCK_H
#define ASAN_LOCK_H
#include "asan_internal.h"
// The locks in ASan are global objects and they are never destroyed to avoid
// at-exit races (that is, a lock is being used by other threads while the main
// thread is doing atexit destructors).
#ifdef __APPLE__
#include <pthread.h>
#include <libkern/OSAtomic.h>
namespace __asan {
class AsanLock {
public:
explicit AsanLock(LinkerInitialized) :
mu_(OS_SPINLOCK_INIT),
owner_(0),
is_locked_(false) {}
void Lock() {
CHECK(owner_ != pthread_self());
OSSpinLockLock(&mu_);
is_locked_ = true;
owner_ = pthread_self();
}
void Unlock() {
owner_ = 0;
is_locked_ = false;
OSSpinLockUnlock(&mu_);
}
bool IsLocked() {
// This is not atomic, e.g. one thread may get different values if another
// one is about to release the lock.
return is_locked_;
}
private:
OSSpinLock mu_;
volatile pthread_t owner_; // for debugging purposes
bool is_locked_; // for silly malloc_introspection_t interface
};
} // namespace __asan
#else // assume linux
#include <pthread.h>
namespace __asan {
class AsanLock {
public:
explicit AsanLock(LinkerInitialized) {
// We assume that pthread_mutex_t initialized to all zeroes is a valid
// unlocked mutex. We can not use PTHREAD_MUTEX_INITIALIZER as it triggers
// a gcc warning:
// extended initializer lists only available with -std=c++0x or -std=gnu++0x
}
void Lock() {
pthread_mutex_lock(&mu_);
// pthread_spin_lock(&mu_);
}
void Unlock() {
pthread_mutex_unlock(&mu_);
// pthread_spin_unlock(&mu_);
}
private:
pthread_mutex_t mu_;
// pthread_spinlock_t mu_;
};
} // namespace __asan
#endif
namespace __asan {
class ScopedLock {
public:
explicit ScopedLock(AsanLock *mu) : mu_(mu) {
mu_->Lock();
}
~ScopedLock() {
mu_->Unlock();
}
private:
AsanLock *mu_;
};
} // namespace __asan
#endif // ASAN_LOCK_H

View File

@ -0,0 +1,256 @@
//===-- asan_mac.cc -------------------------------------------------------===//
//
// 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 details.
//===----------------------------------------------------------------------===//
#ifndef __APPLE__
#error "This file should be used on Mac OS X only."
#endif
#include "asan_mac.h"
#include "asan_internal.h"
#include "asan_stack.h"
#include "asan_thread.h"
#include "asan_thread_registry.h"
#include <algorithm>
#include <sys/mman.h>
#include <unistd.h>
namespace __asan {
extern dispatch_async_f_f real_dispatch_async_f;
extern dispatch_sync_f_f real_dispatch_sync_f;
extern dispatch_after_f_f real_dispatch_after_f;
extern dispatch_barrier_async_f_f real_dispatch_barrier_async_f;
extern dispatch_group_async_f_f real_dispatch_group_async_f;
extern pthread_workqueue_additem_np_f real_pthread_workqueue_additem_np;
// No-op. Mac does not support static linkage anyway.
void *AsanDoesNotSupportStaticLinkage() {
return NULL;
}
void *asan_mmap(void *addr, size_t length, int prot, int flags,
int fd, uint64_t offset) {
return mmap(addr, length, prot, flags, fd, offset);
}
ssize_t asan_write(int fd, const void *buf, size_t count) {
return write(fd, buf, count);
}
// Support for the following functions from libdispatch on Mac OS:
// dispatch_async_f()
// dispatch_async()
// dispatch_sync_f()
// dispatch_sync()
// dispatch_after_f()
// dispatch_after()
// dispatch_group_async_f()
// dispatch_group_async()
// TODO(glider): libdispatch API contains other functions that we don't support
// yet.
//
// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
// they can cause jobs to run on a thread different from the current one.
// TODO(glider): if so, we need a test for this (otherwise we should remove
// them).
//
// The following functions use dispatch_barrier_async_f() (which isn't a library
// function but is exported) and are thus supported:
// dispatch_source_set_cancel_handler_f()
// dispatch_source_set_cancel_handler()
// dispatch_source_set_event_handler_f()
// dispatch_source_set_event_handler()
//
// The reference manual for Grand Central Dispatch is available at
// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
// The implementation details are at
// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
extern "C"
void asan_dispatch_call_block_and_release(void *block) {
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
asan_block_context_t *context = (asan_block_context_t*)block;
if (FLAG_v >= 2) {
Report("asan_dispatch_call_block_and_release(): "
"context: %p, pthread_self: %p\n",
block, pthread_self());
}
AsanThread *t = asanThreadRegistry().GetCurrent();
if (t) {
// We've already executed a job on this worker thread. Let's reuse the
// AsanThread object.
if (t != asanThreadRegistry().GetMain()) {
// Flush the statistics and update the current thread's tid.
asanThreadRegistry().UnregisterThread(t);
asanThreadRegistry().RegisterThread(t, context->parent_tid, &stack);
}
// Otherwise the worker is being executed on the main thread -- we are
// draining the dispatch queue.
// TODO(glider): any checks for that?
} else {
// It's incorrect to assert that the current thread is not dying: at least
// the callbacks from dispatch_sync() are sometimes called after the TSD is
// destroyed.
t = (AsanThread*)asan_malloc(sizeof(AsanThread), &stack);
new(t) AsanThread(context->parent_tid,
/*start_routine*/NULL, /*arg*/NULL, &stack);
asanThreadRegistry().SetCurrent(t);
}
// Call the original dispatcher for the block.
context->func(context->block);
asan_free(context, &stack);
}
} // namespace __asan
using namespace __asan; // NOLINT
// Wrap |ctxt| and |func| into an asan_block_context_t.
// The caller retains control of the allocated context.
extern "C"
asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
AsanStackTrace *stack) {
asan_block_context_t *asan_ctxt =
(asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
asan_ctxt->block = ctxt;
asan_ctxt->func = func;
AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
if (FLAG_debug) {
// Sometimes at Chromium teardown this assertion is violated:
// -- a task is created via dispatch_async() on the "CFMachPort"
// thread while doing _dispatch_queue_drain();
// -- a task is created via dispatch_async_f() on the
// "com.apple.root.default-overcommit-priority" thread while doing
// _dispatch_dispose().
// TODO(glider): find out what's going on.
CHECK(curr_thread || asanThreadRegistry().IsCurrentThreadDying());
}
asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne();
return asan_ctxt;
}
// TODO(glider): can we reduce code duplication by introducing a macro?
extern "C"
int WRAP(dispatch_async_f)(dispatch_queue_t dq,
void *ctxt,
dispatch_function_t func) {
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
if (FLAG_v >= 2) {
Report("dispatch_async_f(): context: %p, pthread_self: %p\n",
asan_ctxt, pthread_self());
PRINT_CURRENT_STACK();
}
return real_dispatch_async_f(dq, (void*)asan_ctxt,
asan_dispatch_call_block_and_release);
}
extern "C"
int WRAP(dispatch_sync_f)(dispatch_queue_t dq,
void *ctxt,
dispatch_function_t func) {
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
if (FLAG_v >= 2) {
Report("dispatch_sync_f(): context: %p, pthread_self: %p\n",
asan_ctxt, pthread_self());
PRINT_CURRENT_STACK();
}
return real_dispatch_sync_f(dq, (void*)asan_ctxt,
asan_dispatch_call_block_and_release);
}
extern "C"
int WRAP(dispatch_after_f)(dispatch_time_t when,
dispatch_queue_t dq,
void *ctxt,
dispatch_function_t func) {
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
if (FLAG_v >= 2) {
Report("dispatch_after_f: %p\n", asan_ctxt);
PRINT_CURRENT_STACK();
}
return real_dispatch_after_f(when, dq, (void*)asan_ctxt,
asan_dispatch_call_block_and_release);
}
extern "C"
void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq,
void *ctxt, dispatch_function_t func) {
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
if (FLAG_v >= 2) {
Report("dispatch_barrier_async_f(): context: %p, pthread_self: %p\n",
asan_ctxt, pthread_self());
PRINT_CURRENT_STACK();
}
real_dispatch_barrier_async_f(dq, (void*)asan_ctxt,
asan_dispatch_call_block_and_release);
}
extern "C"
void WRAP(dispatch_group_async_f)(dispatch_group_t group,
dispatch_queue_t dq,
void *ctxt, dispatch_function_t func) {
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
if (FLAG_v >= 2) {
Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
asan_ctxt, pthread_self());
PRINT_CURRENT_STACK();
}
real_dispatch_group_async_f(group, dq, (void*)asan_ctxt,
asan_dispatch_call_block_and_release);
}
// The following stuff has been extremely helpful while looking for the
// unhandled functions that spawned jobs on Chromium shutdown. If the verbosity
// level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to
// find the points of worker thread creation (each of such threads may be used
// to run several tasks, that's why this is not enough to support the whole
// libdispatch API.
extern "C"
void *wrap_workitem_func(void *arg) {
if (FLAG_v >= 2) {
Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self());
}
asan_block_context_t *ctxt = (asan_block_context_t*)arg;
worker_t fn = (worker_t)(ctxt->func);
void *result = fn(ctxt->block);
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
asan_free(arg, &stack);
return result;
}
extern "C"
int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq,
void *(*workitem_func)(void *), void * workitem_arg,
pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) {
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
asan_block_context_t *asan_ctxt =
(asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack);
asan_ctxt->block = workitem_arg;
asan_ctxt->func = (dispatch_function_t)workitem_func;
asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne();
if (FLAG_v >= 2) {
Report("pthread_workqueue_additem_np: %p\n", asan_ctxt);
PRINT_CURRENT_STACK();
}
return real_pthread_workqueue_additem_np(workq, wrap_workitem_func, asan_ctxt,
itemhandlep, gencountp);
}

View File

@ -0,0 +1,83 @@
//===-- asan_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.
//
// ASan-private header for asan_mac.cc
//===----------------------------------------------------------------------===//
#ifndef ASAN_MAC_H
#define ASAN_MAC_H
#include "asan_interceptors.h"
// TODO(glider): need to check if the OS X version is 10.6 or greater.
#include <dispatch/dispatch.h>
#include <setjmp.h>
typedef void* pthread_workqueue_t;
typedef void* pthread_workitem_handle_t;
typedef void (*dispatch_function_t)(void *block);
typedef void* (*worker_t)(void *block);
typedef int (*dispatch_async_f_f)(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func);
typedef int (*dispatch_sync_f_f)(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func);
typedef int (*dispatch_after_f_f)(dispatch_time_t when,
dispatch_queue_t dq, void *ctxt,
dispatch_function_t func);
typedef void (*dispatch_barrier_async_f_f)(dispatch_queue_t dq,
void *ctxt,
dispatch_function_t func);
typedef void (*dispatch_group_async_f_f)(dispatch_group_t group,
dispatch_queue_t dq,
void *ctxt, dispatch_function_t func);
typedef int (*pthread_workqueue_additem_np_f)(pthread_workqueue_t workq,
void *(*workitem_func)(void *), void * workitem_arg,
pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
// A wrapper for the ObjC blocks used to support libdispatch.
typedef struct {
void *block;
dispatch_function_t func;
int parent_tid;
} asan_block_context_t;
extern "C" {
// 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);
// Neither is pthread_workqueue_additem_np().
int pthread_workqueue_additem_np(pthread_workqueue_t workq,
void *(*workitem_func)(void *), void * workitem_arg,
pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
int WRAP(dispatch_async_f)(dispatch_queue_t dq,
void *ctxt,
dispatch_function_t func);
int WRAP(dispatch_sync_f)(dispatch_queue_t dq,
void *ctxt,
dispatch_function_t func);
int WRAP(dispatch_after_f)(dispatch_time_t when,
dispatch_queue_t dq,
void *ctxt,
dispatch_function_t func);
void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq,
void *ctxt, dispatch_function_t func);
void WRAP(dispatch_group_async_f)(dispatch_group_t group,
dispatch_queue_t dq,
void *ctxt, dispatch_function_t func);
int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq,
void *(*workitem_func)(void *), void * workitem_arg,
pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
}
#endif // ASAN_MAC_H

View File

@ -0,0 +1,139 @@
//===-- asan_malloc_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 malloc interception.
// We simply define functions like malloc, free, realloc, etc.
// They will replace the corresponding libc functions automagically.
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stack.h"
#include <malloc.h>
#define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
#ifdef ANDROID
struct MallocDebug {
void* (*malloc)(size_t bytes);
void (*free)(void* mem);
void* (*calloc)(size_t n_elements, size_t elem_size);
void* (*realloc)(void* oldMem, size_t bytes);
void* (*memalign)(size_t alignment, size_t bytes);
};
const MallocDebug asan_malloc_dispatch __attribute__((aligned(32))) = {
malloc, free, calloc, realloc, memalign
};
extern "C" const MallocDebug* __libc_malloc_dispatch;
namespace __asan {
void ReplaceSystemMalloc() {
__libc_malloc_dispatch = &asan_malloc_dispatch;
}
} // namespace __asan
#else // ANDROID
namespace __asan {
void ReplaceSystemMalloc() {
}
} // namespace __asan
#endif // ANDROID
// ---------------------- Replacement functions ---------------- {{{1
using namespace __asan; // NOLINT
extern "C" {
INTERCEPTOR_ATTRIBUTE
void free(void *ptr) {
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
asan_free(ptr, &stack);
}
INTERCEPTOR_ATTRIBUTE
void cfree(void *ptr) {
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
asan_free(ptr, &stack);
}
INTERCEPTOR_ATTRIBUTE
void *malloc(size_t size) {
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_malloc(size, &stack);
}
INTERCEPTOR_ATTRIBUTE
void *calloc(size_t nmemb, size_t size) {
if (!asan_inited) {
// Hack: dlsym calls calloc before real_calloc is retrieved from dlsym.
const size_t kCallocPoolSize = 1024;
static uintptr_t calloc_memory_for_dlsym[kCallocPoolSize];
static size_t allocated;
size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
void *mem = (void*)&calloc_memory_for_dlsym[allocated];
allocated += size_in_words;
CHECK(allocated < kCallocPoolSize);
return mem;
}
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
INTERCEPTOR_ATTRIBUTE
void *realloc(void *ptr, size_t size) {
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_realloc(ptr, size, &stack);
}
INTERCEPTOR_ATTRIBUTE
void *memalign(size_t boundary, size_t size) {
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_memalign(boundary, size, &stack);
}
void* __libc_memalign(size_t align, size_t s)
__attribute__((alias("memalign")));
INTERCEPTOR_ATTRIBUTE
struct mallinfo mallinfo() {
struct mallinfo res;
real_memset(&res, 0, sizeof(res));
return res;
}
INTERCEPTOR_ATTRIBUTE
int mallopt(int cmd, int value) {
return -1;
}
INTERCEPTOR_ATTRIBUTE
int posix_memalign(void **memptr, size_t alignment, size_t size) {
GET_STACK_TRACE_HERE_FOR_MALLOC;
// Printf("posix_memalign: %lx %ld\n", alignment, size);
return asan_posix_memalign(memptr, alignment, size, &stack);
}
INTERCEPTOR_ATTRIBUTE
void *valloc(size_t size) {
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_valloc(size, &stack);
}
INTERCEPTOR_ATTRIBUTE
void *pvalloc(size_t size) {
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_pvalloc(size, &stack);
}
} // extern "C"

View File

@ -0,0 +1,380 @@
//===-- asan_rtl.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 malloc interception.
//===----------------------------------------------------------------------===//
#include <AvailabilityMacros.h>
#include <CoreFoundation/CFBase.h>
#include <malloc/malloc.h>
#include <setjmp.h>
#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stack.h"
// Similar code is used in Google Perftools,
// http://code.google.com/p/google-perftools.
// ---------------------- Replacement functions ---------------- {{{1
using namespace __asan; // NOLINT
// The free() implementation provided by OS X calls malloc_zone_from_ptr()
// to find the owner of |ptr|. If the result is NULL, an invalid free() is
// reported. Our implementation falls back to asan_free() in this case
// in order to print an ASan-style report.
extern "C"
void free(void *ptr) {
malloc_zone_t *zone = malloc_zone_from_ptr(ptr);
if (zone) {
#if defined(MAC_OS_X_VERSION_10_6) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
if ((zone->version >= 6) && (zone->free_definite_size)) {
zone->free_definite_size(zone, ptr, malloc_size(ptr));
} else {
malloc_zone_free(zone, ptr);
}
#else
malloc_zone_free(zone, ptr);
#endif
} else {
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
asan_free(ptr, &stack);
}
}
// TODO(glider): do we need both zones?
static malloc_zone_t *system_malloc_zone = NULL;
static malloc_zone_t *system_purgeable_zone = NULL;
// We need to provide wrappers around all the libc functions.
namespace {
// TODO(glider): the mz_* functions should be united with the Linux wrappers,
// as they are basically copied from there.
size_t mz_size(malloc_zone_t* zone, const void* ptr) {
// Fast path: check whether this pointer belongs to the original malloc zone.
// We cannot just call malloc_zone_from_ptr(), because it in turn
// calls our mz_size().
if (system_malloc_zone) {
if ((system_malloc_zone->size)(system_malloc_zone, ptr)) return 0;
}
return __asan_mz_size(ptr);
}
void *mz_malloc(malloc_zone_t *zone, size_t size) {
if (!asan_inited) {
CHECK(system_malloc_zone);
return malloc_zone_malloc(system_malloc_zone, size);
}
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_malloc(size, &stack);
}
void *cf_malloc(CFIndex size, CFOptionFlags hint, void *info) {
if (!asan_inited) {
CHECK(system_malloc_zone);
return malloc_zone_malloc(system_malloc_zone, size);
}
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_malloc(size, &stack);
}
void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
if (!asan_inited) {
// Hack: dlsym calls calloc before real_calloc is retrieved from dlsym.
const size_t kCallocPoolSize = 1024;
static uintptr_t calloc_memory_for_dlsym[kCallocPoolSize];
static size_t allocated;
size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
void *mem = (void*)&calloc_memory_for_dlsym[allocated];
allocated += size_in_words;
CHECK(allocated < kCallocPoolSize);
return mem;
}
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
void *mz_valloc(malloc_zone_t *zone, size_t size) {
if (!asan_inited) {
CHECK(system_malloc_zone);
return malloc_zone_valloc(system_malloc_zone, size);
}
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_memalign(kPageSize, size, &stack);
}
void print_zone_for_ptr(void *ptr) {
malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr);
if (orig_zone) {
if (orig_zone->zone_name) {
Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n",
ptr, orig_zone, orig_zone->zone_name);
} else {
Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n",
ptr, orig_zone);
}
} else {
Printf("malloc_zone_from_ptr(%p) = NULL\n", ptr);
}
}
// TODO(glider): the allocation callbacks need to be refactored.
void mz_free(malloc_zone_t *zone, void *ptr) {
if (!ptr) return;
malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr);
// For some reason Chromium calls mz_free() for pointers that belong to
// DefaultPurgeableMallocZone instead of asan_zone. We might want to
// fix this someday.
if (orig_zone == system_purgeable_zone) {
system_purgeable_zone->free(system_purgeable_zone, ptr);
return;
}
if (__asan_mz_size(ptr)) {
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
asan_free(ptr, &stack);
} else {
// Let us just leak this memory for now.
Printf("mz_free(%p) -- attempting to free unallocated memory.\n"
"AddressSanitizer is ignoring this error on Mac OS now.\n", ptr);
print_zone_for_ptr(ptr);
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
stack.PrintStack();
return;
}
}
void cf_free(void *ptr, void *info) {
if (!ptr) return;
malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr);
// For some reason Chromium calls mz_free() for pointers that belong to
// DefaultPurgeableMallocZone instead of asan_zone. We might want to
// fix this someday.
if (orig_zone == system_purgeable_zone) {
system_purgeable_zone->free(system_purgeable_zone, ptr);
return;
}
if (__asan_mz_size(ptr)) {
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
asan_free(ptr, &stack);
} else {
// Let us just leak this memory for now.
Printf("cf_free(%p) -- attempting to free unallocated memory.\n"
"AddressSanitizer is ignoring this error on Mac OS now.\n", ptr);
print_zone_for_ptr(ptr);
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
stack.PrintStack();
return;
}
}
void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
if (!ptr) {
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_malloc(size, &stack);
} else {
if (__asan_mz_size(ptr)) {
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_realloc(ptr, size, &stack);
} else {
// We can't recover from reallocating an unknown address, because
// this would require reading at most |size| bytes from
// potentially unaccessible memory.
Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
"This is an unrecoverable problem, exiting now.\n", ptr);
print_zone_for_ptr(ptr);
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
stack.PrintStack();
ShowStatsAndAbort();
return NULL; // unreachable
}
}
}
void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) {
if (!ptr) {
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_malloc(size, &stack);
} else {
if (__asan_mz_size(ptr)) {
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_realloc(ptr, size, &stack);
} else {
// We can't recover from reallocating an unknown address, because
// this would require reading at most |size| bytes from
// potentially unaccessible memory.
Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
"This is an unrecoverable problem, exiting now.\n", ptr);
print_zone_for_ptr(ptr);
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
stack.PrintStack();
ShowStatsAndAbort();
return NULL; // unreachable
}
}
}
void mz_destroy(malloc_zone_t* zone) {
// A no-op -- we will not be destroyed!
Printf("mz_destroy() called -- ignoring\n");
}
// from AvailabilityMacros.h
#if defined(MAC_OS_X_VERSION_10_6) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
if (!asan_inited) {
CHECK(system_malloc_zone);
return malloc_zone_memalign(system_malloc_zone, align, size);
}
GET_STACK_TRACE_HERE_FOR_MALLOC;
return asan_memalign(align, size, &stack);
}
void mz_free_definite_size(malloc_zone_t* zone, void *ptr, size_t size) {
// TODO(glider): check that |size| is valid.
UNIMPLEMENTED();
}
#endif
// malloc_introspection callbacks. I'm not clear on what all of these do.
kern_return_t mi_enumerator(task_t task, void *,
unsigned type_mask, vm_address_t zone_address,
memory_reader_t reader,
vm_range_recorder_t recorder) {
// Should enumerate all the pointers we have. Seems like a lot of work.
return KERN_FAILURE;
}
size_t mi_good_size(malloc_zone_t *zone, size_t size) {
// I think it's always safe to return size, but we maybe could do better.
return size;
}
boolean_t mi_check(malloc_zone_t *zone) {
UNIMPLEMENTED();
return true;
}
void mi_print(malloc_zone_t *zone, boolean_t verbose) {
UNIMPLEMENTED();
return;
}
void mi_log(malloc_zone_t *zone, void *address) {
// I don't think we support anything like this
}
void mi_force_lock(malloc_zone_t *zone) {
__asan_mz_force_lock();
}
void mi_force_unlock(malloc_zone_t *zone) {
__asan_mz_force_unlock();
}
void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
// TODO(csilvers): figure out how to fill these out
// TODO(glider): port this from tcmalloc when ready.
stats->blocks_in_use = 0;
stats->size_in_use = 0;
stats->max_size_in_use = 0;
stats->size_allocated = 0;
}
boolean_t mi_zone_locked(malloc_zone_t *zone) {
// UNIMPLEMENTED();
return false;
}
} // unnamed namespace
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));
asan_introspection.enumerator = &mi_enumerator;
asan_introspection.good_size = &mi_good_size;
asan_introspection.check = &mi_check;
asan_introspection.print = &mi_print;
asan_introspection.log = &mi_log;
asan_introspection.force_lock = &mi_force_lock;
asan_introspection.force_unlock = &mi_force_unlock;
static malloc_zone_t asan_zone;
__asan::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;
asan_zone.zone_name = "asan";
asan_zone.size = &mz_size;
asan_zone.malloc = &mz_malloc;
asan_zone.calloc = &mz_calloc;
asan_zone.valloc = &mz_valloc;
asan_zone.free = &mz_free;
asan_zone.realloc = &mz_realloc;
asan_zone.destroy = &mz_destroy;
asan_zone.batch_malloc = NULL;
asan_zone.batch_free = NULL;
asan_zone.introspect = &asan_introspection;
// from AvailabilityMacros.h
#if defined(MAC_OS_X_VERSION_10_6) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
// Switch to version 6 on OSX 10.6 to support memalign.
asan_zone.version = 6;
asan_zone.free_definite_size = 0;
asan_zone.memalign = &mz_memalign;
asan_introspection.zone_locked = &mi_zone_locked;
// Request the default purgable zone to force its creation. The
// current default zone is registered with the purgable zone for
// doing tiny and small allocs. Sadly, it assumes that the default
// zone is the szone implementation from OS X and will crash if it
// isn't. By creating the zone now, this will be true and changing
// the default zone won't cause a problem. (OS X 10.6 and higher.)
system_purgeable_zone = malloc_default_purgeable_zone();
#endif
// Register the ASan zone. At this point, it will not be the
// default zone.
malloc_zone_register(&asan_zone);
// Unregister and reregister the default zone. Unregistering swaps
// the specified zone with the last one registered which for the
// default zone makes the more recently registered zone the default
// zone. The default zone is then re-registered to ensure that
// allocations made from it earlier will be handled correctly.
// Things are not guaranteed to work that way, but it's how they work now.
system_malloc_zone = malloc_default_zone();
malloc_zone_unregister(system_malloc_zone);
malloc_zone_register(system_malloc_zone);
// Make sure the default allocator was replaced.
CHECK(malloc_default_zone() == &asan_zone);
if (FLAG_replace_cfallocator) {
static CFAllocatorContext asan_context =
{ /*version*/ 0, /*info*/ &asan_zone,
/*retain*/ NULL, /*release*/ NULL,
/*copyDescription*/NULL,
/*allocate*/ &cf_malloc,
/*reallocate*/ &cf_realloc,
/*deallocate*/ &cf_free,
/*preferredSize*/ NULL };
CFAllocatorRef cf_asan =
CFAllocatorCreate(kCFAllocatorUseContext, &asan_context);
CFAllocatorSetDefault(cf_asan);
}
}
} // namespace __asan

View File

@ -0,0 +1,96 @@
//===-- asan_mapping.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.
//
// Defines ASan memory mapping.
//===----------------------------------------------------------------------===//
#ifndef ASAN_MAPPING_H
#define ASAN_MAPPING_H
#include "asan_internal.h"
// The full explanation of the memory mapping could be found here:
// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm
#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1
extern __attribute__((visibility("default"))) uintptr_t __asan_mapping_scale;
extern __attribute__((visibility("default"))) uintptr_t __asan_mapping_offset;
#define SHADOW_SCALE (__asan_mapping_scale)
#define SHADOW_OFFSET (__asan_mapping_offset)
#else
#define SHADOW_SCALE (3)
#if __WORDSIZE == 32
#define SHADOW_OFFSET (1 << 29)
#else
#define SHADOW_OFFSET (1ULL << 44)
#endif
#endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET
#define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE)
#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) | (SHADOW_OFFSET))
#if __WORDSIZE == 64
static const size_t kHighMemEnd = 0x00007fffffffffffUL;
#else // __WORDSIZE == 32
static const size_t kHighMemEnd = 0xffffffff;
#endif // __WORDSIZE
#define kLowMemBeg 0
#define kLowMemEnd (SHADOW_OFFSET ? SHADOW_OFFSET - 1 : 0)
#define kLowShadowBeg SHADOW_OFFSET
#define kLowShadowEnd MEM_TO_SHADOW(kLowMemEnd)
#define kHighMemBeg (MEM_TO_SHADOW(kHighMemEnd) + 1)
#define kHighShadowBeg MEM_TO_SHADOW(kHighMemBeg)
#define kHighShadowEnd MEM_TO_SHADOW(kHighMemEnd)
#define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 : 16 * kPageSize)
#define kShadowGapEnd (kHighShadowBeg - 1)
#define kGlobalAndStackRedzone \
(SHADOW_GRANULARITY < 32 ? 32 : SHADOW_GRANULARITY)
namespace __asan {
static inline bool AddrIsInLowMem(uintptr_t a) {
return a < kLowMemEnd;
}
static inline bool AddrIsInLowShadow(uintptr_t a) {
return a >= kLowShadowBeg && a <= kLowShadowEnd;
}
static inline bool AddrIsInHighMem(uintptr_t a) {
return a >= kHighMemBeg && a <= kHighMemEnd;
}
static inline bool AddrIsInMem(uintptr_t a) {
return AddrIsInLowMem(a) || AddrIsInHighMem(a);
}
static inline uintptr_t MemToShadow(uintptr_t p) {
CHECK(AddrIsInMem(p));
return MEM_TO_SHADOW(p);
}
static inline bool AddrIsInHighShadow(uintptr_t a) {
return a >= kHighShadowBeg && a <= kHighMemEnd;
}
static inline bool AddrIsInShadow(uintptr_t a) {
return AddrIsInLowShadow(a) || AddrIsInHighShadow(a);
}
} // namespace __asan
#endif // ASAN_MAPPING_H

View File

@ -0,0 +1,134 @@
//===-- asan_poisoning.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.
//
// Memory poisoning that can be made by user application.
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
#include "asan_interface.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include <algorithm>
namespace __asan {
struct ShadowSegmentEndpoint {
uint8_t *chunk;
int8_t offset; // in [0, SHADOW_GRANULARITY)
int8_t value; // = *chunk;
explicit ShadowSegmentEndpoint(uintptr_t address) {
chunk = (uint8_t*)MemToShadow(address);
offset = address & (SHADOW_GRANULARITY - 1);
value = *chunk;
}
};
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
// Current implementation of __asan_(un)poison_memory_region doesn't check
// that user program (un)poisons the memory it owns. It poisons memory
// conservatively, and unpoisons progressively to make sure asan shadow
// mapping invariant is preserved (see detailed mapping description here:
// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm).
//
// * if user asks to poison region [left, right), the program poisons
// at least [left, AlignDown(right)).
// * if user asks to unpoison region [left, right), the program unpoisons
// at most [AlignDown(left), right).
void __asan_poison_memory_region(void const volatile *addr, size_t size) {
if (!FLAG_allow_user_poisoning || size == 0) return;
uintptr_t beg_addr = (uintptr_t)addr;
uintptr_t end_addr = beg_addr + size;
if (FLAG_v >= 1) {
Printf("Trying to poison memory region [%p, %p)\n", beg_addr, end_addr);
}
ShadowSegmentEndpoint beg(beg_addr);
ShadowSegmentEndpoint end(end_addr);
if (beg.chunk == end.chunk) {
CHECK(beg.offset < end.offset);
int8_t value = beg.value;
CHECK(value == end.value);
// We can only poison memory if the byte in end.offset is unaddressable.
// No need to re-poison memory if it is poisoned already.
if (value > 0 && value <= end.offset) {
if (beg.offset > 0) {
*beg.chunk = std::min(value, beg.offset);
} else {
*beg.chunk = kAsanUserPoisonedMemoryMagic;
}
}
return;
}
CHECK(beg.chunk < end.chunk);
if (beg.offset > 0) {
// Mark bytes from beg.offset as unaddressable.
if (beg.value == 0) {
*beg.chunk = beg.offset;
} else {
*beg.chunk = std::min(beg.value, beg.offset);
}
beg.chunk++;
}
real_memset(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk);
// Poison if byte in end.offset is unaddressable.
if (end.value > 0 && end.value <= end.offset) {
*end.chunk = kAsanUserPoisonedMemoryMagic;
}
}
void __asan_unpoison_memory_region(void const volatile *addr, size_t size) {
if (!FLAG_allow_user_poisoning || size == 0) return;
uintptr_t beg_addr = (uintptr_t)addr;
uintptr_t end_addr = beg_addr + size;
if (FLAG_v >= 1) {
Printf("Trying to unpoison memory region [%p, %p)\n", beg_addr, end_addr);
}
ShadowSegmentEndpoint beg(beg_addr);
ShadowSegmentEndpoint end(end_addr);
if (beg.chunk == end.chunk) {
CHECK(beg.offset < end.offset);
int8_t value = beg.value;
CHECK(value == end.value);
// We unpoison memory bytes up to enbytes up to end.offset if it is not
// unpoisoned already.
if (value != 0) {
*beg.chunk = std::max(value, end.offset);
}
return;
}
CHECK(beg.chunk < end.chunk);
if (beg.offset > 0) {
*beg.chunk = 0;
beg.chunk++;
}
real_memset(beg.chunk, 0, end.chunk - beg.chunk);
if (end.offset > 0 && end.value != 0) {
*end.chunk = std::max(end.value, end.offset);
}
}
bool __asan_address_is_poisoned(void const volatile *addr) {
const size_t kAccessSize = 1;
uintptr_t address = (uintptr_t)addr;
uint8_t *shadow_address = (uint8_t*)MemToShadow(address);
int8_t shadow_value = *shadow_address;
if (shadow_value) {
uint8_t last_accessed_byte = (address & (SHADOW_GRANULARITY - 1))
+ kAccessSize - 1;
return (last_accessed_byte >= shadow_value);
}
return false;
}

View File

@ -0,0 +1,181 @@
//===-- asan_printf.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.
//
// Internal printf function, used inside ASan run-time library.
// We can't use libc printf because we intercept some of the functions used
// inside it.
//===----------------------------------------------------------------------===//
#include "asan_internal.h"
#include "asan_interceptors.h"
#include <stdarg.h>
namespace __asan {
void RawWrite(const char *buffer) {
static const char *kRawWriteError = "RawWrite can't output requested buffer!";
ssize_t length = (ssize_t)internal_strlen(buffer);
if (length != asan_write(2, buffer, length)) {
asan_write(2, kRawWriteError, internal_strlen(kRawWriteError));
ASAN_DIE;
}
}
static inline int AppendChar(char **buff, const char *buff_end, char c) {
if (*buff < buff_end) {
**buff = c;
(*buff)++;
}
return 1;
}
// Appends number in a given base to buffer. If its length is less than
// "minimal_num_length", it is padded with leading zeroes.
static int AppendUnsigned(char **buff, const char *buff_end, uint64_t num,
uint8_t base, uint8_t minimal_num_length) {
size_t const kMaxLen = 30;
RAW_CHECK(base == 10 || base == 16);
RAW_CHECK(minimal_num_length < kMaxLen);
size_t num_buffer[kMaxLen];
size_t pos = 0;
do {
RAW_CHECK_MSG(pos < kMaxLen, "appendNumber buffer overflow");
num_buffer[pos++] = num % base;
num /= base;
} while (num > 0);
while (pos < minimal_num_length) num_buffer[pos++] = 0;
int result = 0;
while (pos-- > 0) {
size_t digit = num_buffer[pos];
result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit
: 'a' + digit - 10);
}
return result;
}
static inline int AppendSignedDecimal(char **buff, const char *buff_end,
int64_t num) {
int result = 0;
if (num < 0) {
result += AppendChar(buff, buff_end, '-');
num = -num;
}
result += AppendUnsigned(buff, buff_end, (uint64_t)num, 10, 0);
return result;
}
static inline int AppendString(char **buff, const char *buff_end,
const char *s) {
// Avoid library functions like stpcpy here.
RAW_CHECK(s);
int result = 0;
for (; *s; s++) {
result += AppendChar(buff, buff_end, *s);
}
return result;
}
static inline int AppendPointer(char **buff, const char *buff_end,
uint64_t ptr_value) {
int result = 0;
result += AppendString(buff, buff_end, "0x");
result += AppendUnsigned(buff, buff_end, ptr_value, 16,
(__WORDSIZE == 64) ? 12 : 8);
return result;
}
static int VSNPrintf(char *buff, int buff_length,
const char *format, va_list args) {
static const char *kPrintfFormatsHelp = "Supported Printf formats: "
"%%[l]{d,u,x}; %%p; %%s";
RAW_CHECK(format);
RAW_CHECK(buff_length > 0);
const char *buff_end = &buff[buff_length - 1];
const char *cur = format;
int result = 0;
for (; *cur; cur++) {
if (*cur == '%') {
cur++;
bool have_l = (*cur == 'l');
cur += have_l;
int64_t dval;
uint64_t uval, xval;
switch (*cur) {
case 'd': dval = have_l ? va_arg(args, intptr_t)
: va_arg(args, int);
result += AppendSignedDecimal(&buff, buff_end, dval);
break;
case 'u': uval = have_l ? va_arg(args, uintptr_t)
: va_arg(args, unsigned int);
result += AppendUnsigned(&buff, buff_end, uval, 10, 0);
break;
case 'x': xval = have_l ? va_arg(args, uintptr_t)
: va_arg(args, unsigned int);
result += AppendUnsigned(&buff, buff_end, xval, 16, 0);
break;
case 'p': RAW_CHECK_MSG(!have_l, kPrintfFormatsHelp);
result += AppendPointer(&buff, buff_end,
va_arg(args, uintptr_t));
break;
case 's': RAW_CHECK_MSG(!have_l, kPrintfFormatsHelp);
result += AppendString(&buff, buff_end, va_arg(args, char*));
break;
default: RAW_CHECK_MSG(false, kPrintfFormatsHelp);
}
} else {
result += AppendChar(&buff, buff_end, *cur);
}
}
RAW_CHECK(buff <= buff_end);
AppendChar(&buff, buff_end + 1, '\0');
return result;
}
void Printf(const char *format, ...) {
const int kLen = 1024 * 4;
char buffer[kLen];
va_list args;
va_start(args, format);
int needed_length = VSNPrintf(buffer, kLen, format, args);
va_end(args);
RAW_CHECK_MSG(needed_length < kLen, "Buffer in Printf is too short!\n");
RawWrite(buffer);
}
// Writes at most "length" symbols to "buffer" (including trailing '\0').
// Returns the number of symbols that should have been written to buffer
// (not including trailing '\0'). Thus, the string is truncated
// iff return value is not less than "length".
int SNPrintf(char *buffer, size_t length, const char *format, ...) {
va_list args;
va_start(args, format);
int needed_length = VSNPrintf(buffer, length, format, args);
va_end(args);
return needed_length;
}
// Like Printf, but prints the current PID before the output string.
void Report(const char *format, ...) {
const int kLen = 1024 * 4;
char buffer[kLen];
int needed_length = SNPrintf(buffer, kLen, "==%d== ", getpid());
RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
va_list args;
va_start(args, format);
needed_length += VSNPrintf(buffer + needed_length, kLen - needed_length,
format, args);
va_end(args);
RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
RawWrite(buffer);
}
} // namespace __asan

View File

@ -0,0 +1,760 @@
//===-- asan_rtl.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.
//
// Main file of the ASan run-time library.
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_interface.h"
#include "asan_internal.h"
#include "asan_lock.h"
#ifdef __APPLE__
#include "asan_mac.h"
#endif
#include "asan_mapping.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
#include "asan_thread_registry.h"
#include <algorithm>
#include <map>
#include <dlfcn.h>
#include <execinfo.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ucontext.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
// must not include <setjmp.h> on Linux
#ifndef ASAN_NEEDS_SEGV
# define ASAN_NEEDS_SEGV 1
#endif
namespace __asan {
// -------------------------- Flags ------------------------- {{{1
static const size_t kMallocContextSize = 30;
static int FLAG_atexit;
bool FLAG_fast_unwind = true;
size_t FLAG_redzone; // power of two, >= 32
bool FLAG_mt; // set to 0 if you have only one thread.
size_t FLAG_quarantine_size;
int FLAG_demangle;
bool FLAG_symbolize;
int FLAG_v;
int FLAG_debug;
bool FLAG_poison_shadow;
int FLAG_report_globals;
size_t FLAG_malloc_context_size = kMallocContextSize;
uintptr_t FLAG_large_malloc;
bool FLAG_lazy_shadow;
bool FLAG_handle_segv;
bool FLAG_handle_sigill;
bool FLAG_replace_str;
bool FLAG_replace_intrin;
bool FLAG_replace_cfallocator; // Used on Mac only.
bool FLAG_stats;
size_t FLAG_max_malloc_fill_size = 0;
bool FLAG_use_fake_stack;
int FLAG_exitcode = EXIT_FAILURE;
bool FLAG_allow_user_poisoning;
// -------------------------- Globals --------------------- {{{1
int asan_inited;
bool asan_init_is_running;
// -------------------------- Interceptors ---------------- {{{1
typedef int (*sigaction_f)(int signum, const struct sigaction *act,
struct sigaction *oldact);
typedef sig_t (*signal_f)(int signum, sig_t handler);
typedef void (*longjmp_f)(void *env, int val);
typedef longjmp_f _longjmp_f;
typedef longjmp_f siglongjmp_f;
typedef void (*__cxa_throw_f)(void *, void *, void *);
typedef int (*pthread_create_f)(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
#ifdef __APPLE__
dispatch_async_f_f real_dispatch_async_f;
dispatch_sync_f_f real_dispatch_sync_f;
dispatch_after_f_f real_dispatch_after_f;
dispatch_barrier_async_f_f real_dispatch_barrier_async_f;
dispatch_group_async_f_f real_dispatch_group_async_f;
pthread_workqueue_additem_np_f real_pthread_workqueue_additem_np;
#endif
sigaction_f real_sigaction;
signal_f real_signal;
longjmp_f real_longjmp;
_longjmp_f real__longjmp;
siglongjmp_f real_siglongjmp;
__cxa_throw_f real___cxa_throw;
pthread_create_f real_pthread_create;
// -------------------------- Misc ---------------- {{{1
void ShowStatsAndAbort() {
__asan_print_accumulated_stats();
ASAN_DIE;
}
static void PrintBytes(const char *before, uintptr_t *a) {
uint8_t *bytes = (uint8_t*)a;
size_t byte_num = (__WORDSIZE) / 8;
Printf("%s%p:", before, (uintptr_t)a);
for (size_t i = 0; i < byte_num; i++) {
Printf(" %lx%lx", bytes[i] >> 4, bytes[i] & 15);
}
Printf("\n");
}
// ---------------------- Thread ------------------------- {{{1
static void *asan_thread_start(void *arg) {
AsanThread *t= (AsanThread*)arg;
asanThreadRegistry().SetCurrent(t);
return t->ThreadStart();
}
// ---------------------- mmap -------------------- {{{1
static void OutOfMemoryMessage(const char *mem_type, size_t size) {
Report("ERROR: AddressSanitizer failed to allocate "
"0x%lx (%ld) bytes of %s\n",
size, size, mem_type);
}
static char *mmap_pages(size_t start_page, size_t n_pages, const char *mem_type,
bool abort_on_failure = true) {
void *res = asan_mmap((void*)start_page, kPageSize * n_pages,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, 0, 0);
// Printf("%p => %p\n", (void*)start_page, res);
char *ch = (char*)res;
if (res == (void*)-1L && abort_on_failure) {
OutOfMemoryMessage(mem_type, n_pages * kPageSize);
ShowStatsAndAbort();
}
CHECK(res == (void*)start_page || res == (void*)-1L);
return ch;
}
// mmap range [beg, end]
static char *mmap_range(uintptr_t beg, uintptr_t end, const char *mem_type) {
CHECK((beg % kPageSize) == 0);
CHECK(((end + 1) % kPageSize) == 0);
// Printf("mmap_range %p %p %ld\n", beg, end, (end - beg) / kPageSize);
return mmap_pages(beg, (end - beg + 1) / kPageSize, mem_type);
}
// protect range [beg, end]
static void protect_range(uintptr_t beg, uintptr_t end) {
CHECK((beg % kPageSize) == 0);
CHECK(((end+1) % kPageSize) == 0);
// Printf("protect_range %p %p %ld\n", beg, end, (end - beg) / kPageSize);
void *res = asan_mmap((void*)beg, end - beg + 1,
PROT_NONE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, 0, 0);
CHECK(res == (void*)beg);
}
// ---------------------- DescribeAddress -------------------- {{{1
static bool DescribeStackAddress(uintptr_t addr, uintptr_t access_size) {
AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr);
if (!t) return false;
const intptr_t kBufSize = 4095;
char buf[kBufSize];
uintptr_t offset = 0;
const char *frame_descr = t->GetFrameNameByAddr(addr, &offset);
// This string is created by the compiler and has the following form:
// "FunctioName n alloc_1 alloc_2 ... alloc_n"
// where alloc_i looks like "offset size len ObjectName ".
CHECK(frame_descr);
// Report the function name and the offset.
const char *name_end = real_strchr(frame_descr, ' ');
CHECK(name_end);
buf[0] = 0;
strncat(buf, frame_descr,
std::min(kBufSize, static_cast<intptr_t>(name_end - frame_descr)));
Printf("Address %p is located at offset %ld "
"in frame <%s> of T%d's stack:\n",
addr, offset, buf, t->tid());
// Report the number of stack objects.
char *p;
size_t n_objects = strtol(name_end, &p, 10);
CHECK(n_objects > 0);
Printf(" This frame has %ld object(s):\n", n_objects);
// Report all objects in this frame.
for (size_t i = 0; i < n_objects; i++) {
size_t beg, size;
intptr_t len;
beg = strtol(p, &p, 10);
size = strtol(p, &p, 10);
len = strtol(p, &p, 10);
if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') {
Printf("AddressSanitizer can't parse the stack frame descriptor: |%s|\n",
frame_descr);
break;
}
p++;
buf[0] = 0;
strncat(buf, p, std::min(kBufSize, len));
p += len;
Printf(" [%ld, %ld) '%s'\n", beg, beg + size, buf);
}
Printf("HINT: this may be a false positive if your program uses "
"some custom stack unwind mechanism\n"
" (longjmp and C++ exceptions *are* supported)\n");
t->summary()->Announce();
return true;
}
__attribute__((noinline))
static void DescribeAddress(uintptr_t addr, uintptr_t access_size) {
// Check if this is a global.
if (DescribeAddrIfGlobal(addr))
return;
if (DescribeStackAddress(addr, access_size))
return;
// finally, check if this is a heap.
DescribeHeapAddress(addr, access_size);
}
// -------------------------- Run-time entry ------------------- {{{1
void GetPcSpBpAx(void *context,
uintptr_t *pc, uintptr_t *sp, uintptr_t *bp, uintptr_t *ax) {
ucontext_t *ucontext = (ucontext_t*)context;
#ifdef __APPLE__
# if __WORDSIZE == 64
*pc = ucontext->uc_mcontext->__ss.__rip;
*bp = ucontext->uc_mcontext->__ss.__rbp;
*sp = ucontext->uc_mcontext->__ss.__rsp;
*ax = ucontext->uc_mcontext->__ss.__rax;
# else
*pc = ucontext->uc_mcontext->__ss.__eip;
*bp = ucontext->uc_mcontext->__ss.__ebp;
*sp = ucontext->uc_mcontext->__ss.__esp;
*ax = ucontext->uc_mcontext->__ss.__eax;
# endif // __WORDSIZE
#else // assume linux
# if defined(__arm__)
*pc = ucontext->uc_mcontext.arm_pc;
*bp = ucontext->uc_mcontext.arm_fp;
*sp = ucontext->uc_mcontext.arm_sp;
*ax = ucontext->uc_mcontext.arm_r0;
# elif __WORDSIZE == 64
*pc = ucontext->uc_mcontext.gregs[REG_RIP];
*bp = ucontext->uc_mcontext.gregs[REG_RBP];
*sp = ucontext->uc_mcontext.gregs[REG_RSP];
*ax = ucontext->uc_mcontext.gregs[REG_RAX];
# else
*pc = ucontext->uc_mcontext.gregs[REG_EIP];
*bp = ucontext->uc_mcontext.gregs[REG_EBP];
*sp = ucontext->uc_mcontext.gregs[REG_ESP];
*ax = ucontext->uc_mcontext.gregs[REG_EAX];
# endif // __WORDSIZE
#endif
}
static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) {
uintptr_t addr = (uintptr_t)siginfo->si_addr;
if (AddrIsInShadow(addr) && FLAG_lazy_shadow) {
// We traped on access to a shadow address. Just map a large chunk around
// this address.
const uintptr_t chunk_size = kPageSize << 10; // 4M
uintptr_t chunk = addr & ~(chunk_size - 1);
asan_mmap((void*)chunk, chunk_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0);
return;
}
// Write the first message using the bullet-proof write.
if (13 != asan_write(2, "ASAN:SIGSEGV\n", 13)) ASAN_DIE;
uintptr_t pc, sp, bp, ax;
GetPcSpBpAx(context, &pc, &sp, &bp, &ax);
Report("ERROR: AddressSanitizer crashed on unknown address %p"
" (pc %p sp %p bp %p ax %p T%d)\n",
addr, pc, sp, bp, ax,
asanThreadRegistry().GetCurrentTidOrMinusOne());
Printf("AddressSanitizer can not provide additional info. ABORTING\n");
GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, false, pc, bp);
stack.PrintStack();
ShowStatsAndAbort();
}
static void ASAN_OnSIGILL(int, siginfo_t *siginfo, void *context) {
// Write the first message using the bullet-proof write.
if (12 != asan_write(2, "ASAN:SIGILL\n", 12)) ASAN_DIE;
uintptr_t pc, sp, bp, ax;
GetPcSpBpAx(context, &pc, &sp, &bp, &ax);
uintptr_t addr = ax;
uint8_t *insn = (uint8_t*)pc;
CHECK(insn[0] == 0x0f && insn[1] == 0x0b); // ud2
unsigned access_size_and_type = insn[2] - 0x50;
CHECK(access_size_and_type < 16);
bool is_write = access_size_and_type & 8;
int access_size = 1 << (access_size_and_type & 7);
__asan_report_error(pc, bp, sp, addr, is_write, access_size);
}
// exported functions
#define ASAN_REPORT_ERROR(type, is_write, size) \
extern "C" void __asan_report_ ## type ## size(uintptr_t addr) \
__attribute__((visibility("default"))); \
extern "C" void __asan_report_ ## type ## size(uintptr_t addr) { \
GET_BP_PC_SP; \
__asan_report_error(pc, bp, sp, addr, is_write, size); \
}
ASAN_REPORT_ERROR(load, false, 1)
ASAN_REPORT_ERROR(load, false, 2)
ASAN_REPORT_ERROR(load, false, 4)
ASAN_REPORT_ERROR(load, false, 8)
ASAN_REPORT_ERROR(load, false, 16)
ASAN_REPORT_ERROR(store, true, 1)
ASAN_REPORT_ERROR(store, true, 2)
ASAN_REPORT_ERROR(store, true, 4)
ASAN_REPORT_ERROR(store, true, 8)
ASAN_REPORT_ERROR(store, true, 16)
// Force the linker to keep the symbols for various ASan interface functions.
// We want to keep those in the executable in order to let the instrumented
// dynamic libraries access the symbol even if it is not used by the executable
// itself. This should help if the build system is removing dead code at link
// time.
extern "C"
void __asan_force_interface_symbols() {
volatile int fake_condition = 0; // prevent dead condition elimination.
if (fake_condition) {
__asan_report_load1(NULL);
__asan_report_load2(NULL);
__asan_report_load4(NULL);
__asan_report_load8(NULL);
__asan_report_load16(NULL);
__asan_report_store1(NULL);
__asan_report_store2(NULL);
__asan_report_store4(NULL);
__asan_report_store8(NULL);
__asan_report_store16(NULL);
__asan_register_global(0, 0, NULL);
__asan_register_globals(NULL, 0);
}
}
// -------------------------- Init ------------------- {{{1
static int64_t IntFlagValue(const char *flags, const char *flag,
int64_t default_val) {
if (!flags) return default_val;
const char *str = strstr(flags, flag);
if (!str) return default_val;
return atoll(str + internal_strlen(flag));
}
static void asan_atexit() {
Printf("AddressSanitizer exit stats:\n");
__asan_print_accumulated_stats();
}
void CheckFailed(const char *cond, const char *file, int line) {
Report("CHECK failed: %s at %s:%d, pthread_self=%p\n",
cond, file, line, pthread_self());
PRINT_CURRENT_STACK();
ShowStatsAndAbort();
}
} // namespace __asan
// -------------------------- Interceptors ------------------- {{{1
using namespace __asan; // NOLINT
#define OPERATOR_NEW_BODY \
GET_STACK_TRACE_HERE_FOR_MALLOC;\
return asan_memalign(0, size, &stack);
void *operator new(size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; }
void *operator new[](size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; }
void *operator new(size_t size, std::nothrow_t const&) throw()
{ OPERATOR_NEW_BODY; }
void *operator new[](size_t size, std::nothrow_t const&) throw()
{ OPERATOR_NEW_BODY; }
#define OPERATOR_DELETE_BODY \
GET_STACK_TRACE_HERE_FOR_FREE(ptr);\
asan_free(ptr, &stack);
void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; }
void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; }
void operator delete(void *ptr, std::nothrow_t const&) throw()
{ OPERATOR_DELETE_BODY; }
void operator delete[](void *ptr, std::nothrow_t const&) throw()
{ OPERATOR_DELETE_BODY;}
extern "C"
#ifndef __APPLE__
__attribute__((visibility("default")))
#endif
int WRAP(pthread_create)(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg) {
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
AsanThread *t = (AsanThread*)asan_malloc(sizeof(AsanThread), &stack);
AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
CHECK(curr_thread || asanThreadRegistry().IsCurrentThreadDying());
new(t) AsanThread(asanThreadRegistry().GetCurrentTidOrMinusOne(),
start_routine, arg, &stack);
return real_pthread_create(thread, attr, asan_thread_start, t);
}
static bool MySignal(int signum) {
if (FLAG_handle_sigill && signum == SIGILL) return true;
if (FLAG_handle_segv && signum == SIGSEGV) return true;
#ifdef __APPLE__
if (FLAG_handle_segv && signum == SIGBUS) return true;
#endif
return false;
}
static void MaybeInstallSigaction(int signum,
void (*handler)(int, siginfo_t *, void *)) {
if (!MySignal(signum))
return;
struct sigaction sigact;
real_memset(&sigact, 0, sizeof(sigact));
sigact.sa_sigaction = handler;
sigact.sa_flags = SA_SIGINFO;
CHECK(0 == real_sigaction(signum, &sigact, 0));
}
extern "C"
sig_t WRAP(signal)(int signum, sig_t handler) {
if (!MySignal(signum)) {
return real_signal(signum, handler);
}
return NULL;
}
extern "C"
int WRAP(sigaction)(int signum, const struct sigaction *act,
struct sigaction *oldact) {
if (!MySignal(signum)) {
return real_sigaction(signum, act, oldact);
}
return 0;
}
static void UnpoisonStackFromHereToTop() {
int local_stack;
AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
CHECK(curr_thread);
uintptr_t top = curr_thread->stack_top();
uintptr_t bottom = ((uintptr_t)&local_stack - kPageSize) & ~(kPageSize-1);
uintptr_t top_shadow = MemToShadow(top);
uintptr_t bot_shadow = MemToShadow(bottom);
real_memset((void*)bot_shadow, 0, top_shadow - bot_shadow);
}
extern "C" void WRAP(longjmp)(void *env, int val) {
UnpoisonStackFromHereToTop();
real_longjmp(env, val);
}
extern "C" void WRAP(_longjmp)(void *env, int val) {
UnpoisonStackFromHereToTop();
real__longjmp(env, val);
}
extern "C" void WRAP(siglongjmp)(void *env, int val) {
UnpoisonStackFromHereToTop();
real_siglongjmp(env, val);
}
extern "C" void __cxa_throw(void *a, void *b, void *c);
#if ASAN_HAS_EXCEPTIONS
extern "C" void WRAP(__cxa_throw)(void *a, void *b, void *c) {
UnpoisonStackFromHereToTop();
real___cxa_throw(a, b, c);
}
#endif
extern "C" {
// intercept mlock and friends.
// Since asan maps 16T of RAM, mlock is completely unfriendly to asan.
// All functions return 0 (success).
static void MlockIsUnsupported() {
static bool printed = 0;
if (printed) return;
printed = true;
Printf("INFO: AddressSanitizer ignores mlock/mlockall/munlock/munlockall\n");
}
int mlock(const void *addr, size_t len) {
MlockIsUnsupported();
return 0;
}
int munlock(const void *addr, size_t len) {
MlockIsUnsupported();
return 0;
}
int mlockall(int flags) {
MlockIsUnsupported();
return 0;
}
int munlockall(void) {
MlockIsUnsupported();
return 0;
}
} // extern "C"
// ---------------------- Interface ---------------- {{{1
int __asan_set_error_exit_code(int exit_code) {
int old = FLAG_exitcode;
FLAG_exitcode = exit_code;
return old;
}
void __asan_report_error(uintptr_t pc, uintptr_t bp, uintptr_t sp,
uintptr_t addr, bool is_write, size_t access_size) {
// Do not print more than one report, otherwise they will mix up.
static int num_calls = 0;
if (AtomicInc(&num_calls) > 1) return;
Printf("=================================================================\n");
const char *bug_descr = "unknown-crash";
if (AddrIsInMem(addr)) {
uint8_t *shadow_addr = (uint8_t*)MemToShadow(addr);
uint8_t shadow_byte = shadow_addr[0];
if (shadow_byte > 0 && shadow_byte < 128) {
// we are in the partial right redzone, look at the next shadow byte.
shadow_byte = shadow_addr[1];
}
switch (shadow_byte) {
case kAsanHeapLeftRedzoneMagic:
case kAsanHeapRightRedzoneMagic:
bug_descr = "heap-buffer-overflow";
break;
case kAsanHeapFreeMagic:
bug_descr = "heap-use-after-free";
break;
case kAsanStackLeftRedzoneMagic:
bug_descr = "stack-buffer-underflow";
break;
case kAsanStackMidRedzoneMagic:
case kAsanStackRightRedzoneMagic:
case kAsanStackPartialRedzoneMagic:
bug_descr = "stack-buffer-overflow";
break;
case kAsanStackAfterReturnMagic:
bug_descr = "stack-use-after-return";
break;
case kAsanUserPoisonedMemoryMagic:
bug_descr = "use-after-poison";
break;
case kAsanGlobalRedzoneMagic:
bug_descr = "global-buffer-overflow";
break;
}
}
Report("ERROR: AddressSanitizer %s on address "
"%p at pc 0x%lx bp 0x%lx sp 0x%lx\n",
bug_descr, addr, pc, bp, sp);
Printf("%s of size %d at %p thread T%d\n",
access_size ? (is_write ? "WRITE" : "READ") : "ACCESS",
access_size, addr, asanThreadRegistry().GetCurrentTidOrMinusOne());
if (FLAG_debug) {
PrintBytes("PC: ", (uintptr_t*)pc);
}
GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax,
false, // FLAG_fast_unwind,
pc, bp);
stack.PrintStack();
CHECK(AddrIsInMem(addr));
DescribeAddress(addr, access_size);
uintptr_t shadow_addr = MemToShadow(addr);
Report("ABORTING\n");
__asan_print_accumulated_stats();
Printf("Shadow byte and word:\n");
Printf(" %p: %x\n", shadow_addr, *(unsigned char*)shadow_addr);
uintptr_t aligned_shadow = shadow_addr & ~(kWordSize - 1);
PrintBytes(" ", (uintptr_t*)(aligned_shadow));
Printf("More shadow bytes:\n");
PrintBytes(" ", (uintptr_t*)(aligned_shadow-4*kWordSize));
PrintBytes(" ", (uintptr_t*)(aligned_shadow-3*kWordSize));
PrintBytes(" ", (uintptr_t*)(aligned_shadow-2*kWordSize));
PrintBytes(" ", (uintptr_t*)(aligned_shadow-1*kWordSize));
PrintBytes("=>", (uintptr_t*)(aligned_shadow+0*kWordSize));
PrintBytes(" ", (uintptr_t*)(aligned_shadow+1*kWordSize));
PrintBytes(" ", (uintptr_t*)(aligned_shadow+2*kWordSize));
PrintBytes(" ", (uintptr_t*)(aligned_shadow+3*kWordSize));
PrintBytes(" ", (uintptr_t*)(aligned_shadow+4*kWordSize));
ASAN_DIE;
}
void __asan_init() {
if (asan_inited) return;
asan_init_is_running = true;
// Make sure we are not statically linked.
AsanDoesNotSupportStaticLinkage();
// flags
const char *options = getenv("ASAN_OPTIONS");
FLAG_malloc_context_size =
IntFlagValue(options, "malloc_context_size=", kMallocContextSize);
CHECK(FLAG_malloc_context_size <= kMallocContextSize);
FLAG_max_malloc_fill_size =
IntFlagValue(options, "max_malloc_fill_size=", 0);
FLAG_v = IntFlagValue(options, "verbosity=", 0);
FLAG_redzone = IntFlagValue(options, "redzone=", 128);
CHECK(FLAG_redzone >= 32);
CHECK((FLAG_redzone & (FLAG_redzone - 1)) == 0);
FLAG_atexit = IntFlagValue(options, "atexit=", 0);
FLAG_poison_shadow = IntFlagValue(options, "poison_shadow=", 1);
FLAG_report_globals = IntFlagValue(options, "report_globals=", 1);
FLAG_lazy_shadow = IntFlagValue(options, "lazy_shadow=", 0);
FLAG_handle_segv = IntFlagValue(options, "handle_segv=",
ASAN_NEEDS_SEGV);
FLAG_handle_sigill = IntFlagValue(options, "handle_sigill=", 0);
FLAG_stats = IntFlagValue(options, "stats=", 0);
FLAG_symbolize = IntFlagValue(options, "symbolize=", 1);
FLAG_demangle = IntFlagValue(options, "demangle=", 1);
FLAG_debug = IntFlagValue(options, "debug=", 0);
FLAG_replace_cfallocator = IntFlagValue(options, "replace_cfallocator=", 1);
FLAG_fast_unwind = IntFlagValue(options, "fast_unwind=", 1);
FLAG_mt = IntFlagValue(options, "mt=", 1);
FLAG_replace_str = IntFlagValue(options, "replace_str=", 1);
FLAG_replace_intrin = IntFlagValue(options, "replace_intrin=", 0);
FLAG_use_fake_stack = IntFlagValue(options, "use_fake_stack=", 1);
FLAG_exitcode = IntFlagValue(options, "exitcode=", EXIT_FAILURE);
FLAG_allow_user_poisoning = IntFlagValue(options,
"allow_user_poisoning=", 1);
if (FLAG_atexit) {
atexit(asan_atexit);
}
FLAG_quarantine_size =
IntFlagValue(options, "quarantine_size=", 1UL << 28);
// interceptors
InitializeAsanInterceptors();
ReplaceSystemMalloc();
INTERCEPT_FUNCTION(sigaction);
INTERCEPT_FUNCTION(signal);
INTERCEPT_FUNCTION(longjmp);
INTERCEPT_FUNCTION(_longjmp);
INTERCEPT_FUNCTION(__cxa_throw);
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);
// 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);
}
#else
// On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it
// there.
INTERCEPT_FUNCTION(siglongjmp);
#endif
MaybeInstallSigaction(SIGSEGV, ASAN_OnSIGSEGV);
MaybeInstallSigaction(SIGBUS, ASAN_OnSIGSEGV);
MaybeInstallSigaction(SIGILL, ASAN_OnSIGILL);
if (FLAG_v) {
Printf("|| `[%p, %p]` || HighMem ||\n", kHighMemBeg, kHighMemEnd);
Printf("|| `[%p, %p]` || HighShadow ||\n",
kHighShadowBeg, kHighShadowEnd);
Printf("|| `[%p, %p]` || ShadowGap ||\n",
kShadowGapBeg, kShadowGapEnd);
Printf("|| `[%p, %p]` || LowShadow ||\n",
kLowShadowBeg, kLowShadowEnd);
Printf("|| `[%p, %p]` || LowMem ||\n", kLowMemBeg, kLowMemEnd);
Printf("MemToShadow(shadow): %p %p %p %p\n",
MEM_TO_SHADOW(kLowShadowBeg),
MEM_TO_SHADOW(kLowShadowEnd),
MEM_TO_SHADOW(kHighShadowBeg),
MEM_TO_SHADOW(kHighShadowEnd));
Printf("red_zone=%ld\n", FLAG_redzone);
Printf("malloc_context_size=%ld\n", (int)FLAG_malloc_context_size);
Printf("fast_unwind=%d\n", (int)FLAG_fast_unwind);
Printf("SHADOW_SCALE: %lx\n", SHADOW_SCALE);
Printf("SHADOW_GRANULARITY: %lx\n", SHADOW_GRANULARITY);
Printf("SHADOW_OFFSET: %lx\n", SHADOW_OFFSET);
CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7);
}
if (__WORDSIZE == 64) {
// Disable core dumper -- it makes little sense to dump 16T+ core.
struct rlimit nocore;
nocore.rlim_cur = 0;
nocore.rlim_max = 0;
setrlimit(RLIMIT_CORE, &nocore);
}
{
if (!FLAG_lazy_shadow) {
if (kLowShadowBeg != kLowShadowEnd) {
// mmap the low shadow plus one page.
mmap_range(kLowShadowBeg - kPageSize, kLowShadowEnd, "LowShadow");
}
// mmap the high shadow.
mmap_range(kHighShadowBeg, kHighShadowEnd, "HighShadow");
}
// protect the gap
protect_range(kShadowGapBeg, kShadowGapEnd);
}
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
// should be set to 1 prior to initializing the threads.
asan_inited = 1;
asan_init_is_running = false;
asanThreadRegistry().Init();
asanThreadRegistry().GetMain()->ThreadStart();
__asan_force_interface_symbols(); // no-op.
if (FLAG_v) {
Report("AddressSanitizer r%s Init done ***\n", ASAN_REVISION);
}
}

View File

@ -0,0 +1,280 @@
//===-- asan_stack.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.
//
// Code for ASan stack trace.
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
#include "asan_lock.h"
#include "asan_stack.h"
#include "asan_thread.h"
#include "asan_thread_registry.h"
#include <string.h>
#ifdef ASAN_USE_SYSINFO
#include "sysinfo/sysinfo.h"
#endif
#ifdef ASAN_USE_EXTERNAL_SYMBOLIZER
extern bool
ASAN_USE_EXTERNAL_SYMBOLIZER(const void *pc, char *out, int out_size);
#endif
namespace __asan {
// ----------------------- ProcSelfMaps ----------------------------- {{{1
#ifdef ASAN_USE_SYSINFO
class ProcSelfMaps {
public:
void Init() {
ScopedLock lock(&mu_);
if (map_size_ != 0) return; // already inited
if (FLAG_v >= 2) {
Printf("ProcSelfMaps::Init()\n");
}
ProcMapsIterator it(0, &proc_self_maps_); // 0 means "current pid"
uint64 start, end, offset;
int64 inode;
char *flags, *filename;
CHECK(map_size_ == 0);
while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
CHECK(map_size_ < kMaxProcSelfMapsSize);
Mapping &mapping = memory_map[map_size_];
mapping.beg = start;
mapping.end = end;
mapping.offset = offset;
real_strncpy(mapping.name,
filename, ASAN_ARRAY_SIZE(mapping.name));
mapping.name[ASAN_ARRAY_SIZE(mapping.name) - 1] = 0;
if (FLAG_v >= 2) {
Printf("[%ld] [%p,%p] off %p %s\n", map_size_,
mapping.beg, mapping.end, mapping.offset, mapping.name);
}
map_size_++;
}
}
void Print() {
Printf("%s\n", proc_self_maps_);
}
void PrintPc(uintptr_t pc, int idx) {
for (size_t i = 0; i < map_size_; i++) {
Mapping &m = memory_map[i];
if (pc >= m.beg && pc < m.end) {
uintptr_t offset = pc - m.beg;
if (i == 0) offset = pc;
Printf(" #%d 0x%lx (%s+0x%lx)\n", idx, pc, m.name, offset);
return;
}
}
Printf(" #%d 0x%lx\n", idx, pc);
}
private:
void copy_until_new_line(const char *str, char *dest, size_t max_size) {
size_t i = 0;
for (; str[i] && str[i] != '\n' && i < max_size - 1; i++) {
dest[i] = str[i];
}
dest[i] = 0;
}
struct Mapping {
uintptr_t beg, end, offset;
char name[1000];
};
static const size_t kMaxNumMapEntries = 4096;
static const size_t kMaxProcSelfMapsSize = 1 << 20;
ProcMapsIterator::Buffer proc_self_maps_;
size_t map_size_;
Mapping memory_map[kMaxNumMapEntries];
static AsanLock mu_;
};
static ProcSelfMaps proc_self_maps;
AsanLock ProcSelfMaps::mu_(LINKER_INITIALIZED);
void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
proc_self_maps.Init();
for (size_t i = 0; i < size && addr[i]; i++) {
uintptr_t pc = addr[i];
// int line;
proc_self_maps.PrintPc(pc, i);
// Printf(" #%ld 0x%lx %s\n", i, pc, rtn.c_str());
}
}
#elif defined(ASAN_USE_EXTERNAL_SYMBOLIZER)
void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
for (size_t i = 0; i < size && addr[i]; i++) {
uintptr_t pc = addr[i];
char buff[4096];
ASAN_USE_EXTERNAL_SYMBOLIZER((void*)pc, buff, sizeof(buff));
Printf(" #%ld 0x%lx %s\n", i, pc, buff);
}
}
#else // ASAN_USE_SYSINFO
void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
for (size_t i = 0; i < size && addr[i]; i++) {
uintptr_t pc = addr[i];
Printf(" #%ld 0x%lx\n", i, pc);
}
}
#endif // ASAN_USE_SYSINFO
#ifdef __arm__
#define UNWIND_STOP _URC_END_OF_STACK
#define UNWIND_CONTINUE _URC_OK
#else
#define UNWIND_STOP _URC_NORMAL_STOP
#define UNWIND_CONTINUE _URC_NO_REASON
#endif
// ----------------------- AsanStackTrace ----------------------------- {{{1
uintptr_t AsanStackTrace::GetCurrentPc() {
return GET_CALLER_PC();
}
void AsanStackTrace::FastUnwindStack(uintptr_t pc, uintptr_t bp) {
CHECK(size == 0 && trace[0] == pc);
size = 1;
if (!asan_inited) return;
AsanThread *t = asanThreadRegistry().GetCurrent();
if (!t) return;
uintptr_t *frame = (uintptr_t*)bp;
uintptr_t *prev_frame = frame;
uintptr_t *top = (uintptr_t*)t->stack_top();
uintptr_t *bottom = (uintptr_t*)t->stack_bottom();
while (frame >= prev_frame &&
frame < top &&
frame > bottom &&
size < max_size) {
uintptr_t pc1 = frame[1];
if (pc1 != pc) {
trace[size++] = pc1;
}
prev_frame = frame;
frame = (uintptr_t*)frame[0];
}
}
// On 32-bits we don't compress stack traces.
// On 64-bits we compress stack traces: if a given pc differes slightly from
// the previous one, we record a 31-bit offset instead of the full pc.
size_t AsanStackTrace::CompressStack(AsanStackTrace *stack,
uint32_t *compressed, size_t size) {
#if __WORDSIZE == 32
// Don't compress, just copy.
size_t res = 0;
for (size_t i = 0; i < stack->size && i < size; i++) {
compressed[i] = stack->trace[i];
res++;
}
for (size_t i = stack->size; i < size; i++) {
compressed[i] = 0;
}
#else // 64 bits, compress.
uintptr_t prev_pc = 0;
const uintptr_t kMaxOffset = (1ULL << 30) - 1;
uintptr_t c_index = 0;
size_t res = 0;
for (size_t i = 0, n = stack->size; i < n; i++) {
uintptr_t pc = stack->trace[i];
if (!pc) break;
if ((int64_t)pc < 0) break;
// Printf("C pc[%ld] %lx\n", i, pc);
if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) {
uintptr_t offset = (int64_t)(pc - prev_pc);
offset |= (1U << 31);
if (c_index >= size) break;
// Printf("C co[%ld] offset %lx\n", i, offset);
compressed[c_index++] = offset;
} else {
uintptr_t hi = pc >> 32;
uintptr_t lo = (pc << 32) >> 32;
CHECK((hi & (1 << 31)) == 0);
if (c_index + 1 >= size) break;
// Printf("C co[%ld] hi/lo: %lx %lx\n", c_index, hi, lo);
compressed[c_index++] = hi;
compressed[c_index++] = lo;
}
res++;
prev_pc = pc;
}
for (size_t i = c_index; i < size; i++) {
compressed[i] = 0;
}
#endif // __WORDSIZE
// debug-only code
#if 0
AsanStackTrace check_stack;
UncompressStack(&check_stack, compressed, size);
if (res < check_stack.size) {
Printf("res %ld check_stack.size %ld; c_size %ld\n", res,
check_stack.size, size);
}
// |res| may be greater than check_stack.size, because
// UncompressStack(CompressStack(stack)) eliminates the 0x0 frames.
CHECK(res >= check_stack.size);
CHECK(0 == memcmp(check_stack.trace, stack->trace,
check_stack.size * sizeof(uintptr_t)));
#endif
return res;
}
void AsanStackTrace::UncompressStack(AsanStackTrace *stack,
uint32_t *compressed, size_t size) {
#if __WORDSIZE == 32
// Don't uncompress, just copy.
stack->size = 0;
for (size_t i = 0; i < size && i < kStackTraceMax; i++) {
if (!compressed[i]) break;
stack->size++;
stack->trace[i] = compressed[i];
}
#else // 64 bits, uncompress
uintptr_t prev_pc = 0;
stack->size = 0;
for (size_t i = 0; i < size && stack->size < kStackTraceMax; i++) {
uint32_t x = compressed[i];
uintptr_t pc = 0;
if (x & (1U << 31)) {
// Printf("U co[%ld] offset: %x\n", i, x);
// this is an offset
int32_t offset = x;
offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend.
pc = prev_pc + offset;
CHECK(pc);
} else {
// CHECK(i + 1 < size);
if (i + 1 >= size) break;
uintptr_t hi = x;
uintptr_t lo = compressed[i+1];
// Printf("U co[%ld] hi/lo: %lx %lx\n", i, hi, lo);
i++;
pc = (hi << 32) | lo;
if (!pc) break;
}
// Printf("U pc[%ld] %lx\n", stack->size, pc);
stack->trace[stack->size++] = pc;
prev_pc = pc;
}
#endif // __WORDSIZE
}
} // namespace __asan

View File

@ -0,0 +1,94 @@
//===-- asan_stack.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.
//
// ASan-private header for asan_stack.cc.
//===----------------------------------------------------------------------===//
#ifndef ASAN_STACK_H
#define ASAN_STACK_H
#include "asan_internal.h"
namespace __asan {
static const size_t kStackTraceMax = 64;
struct AsanStackTrace {
size_t size;
size_t max_size;
uintptr_t trace[kStackTraceMax];
static void PrintStack(uintptr_t *addr, size_t size);
void PrintStack() {
PrintStack(this->trace, this->size);
}
void CopyTo(uintptr_t *dst, size_t dst_size) {
for (size_t i = 0; i < size && i < dst_size; i++)
dst[i] = trace[i];
for (size_t i = size; i < dst_size; i++)
dst[i] = 0;
}
void CopyFrom(uintptr_t *src, size_t src_size) {
size = src_size;
if (size > kStackTraceMax) size = kStackTraceMax;
for (size_t i = 0; i < size; i++) {
trace[i] = src[i];
}
}
void FastUnwindStack(uintptr_t pc, uintptr_t bp);
// static _Unwind_Reason_Code Unwind_Trace(
// struct _Unwind_Context *ctx, void *param);
static uintptr_t GetCurrentPc();
static size_t CompressStack(AsanStackTrace *stack,
uint32_t *compressed, size_t size);
static void UncompressStack(AsanStackTrace *stack,
uint32_t *compressed, size_t size);
size_t full_frame_count;
};
} // namespace __asan
// Get the stack trace with the given pc and bp.
// The pc will be in the position 0 of the resulting stack trace.
// The bp may refer to the current frame or to the caller's frame.
// fast_unwind is currently unused.
#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, fast_unwind, pc, bp) \
AsanStackTrace stack; \
{ \
uintptr_t saved_pc = pc; \
uintptr_t saved_bp = bp; \
stack.size = 0; \
stack.full_frame_count = 0; \
stack.trace[0] = saved_pc; \
if ((max_s) > 1) { \
stack.max_size = max_s; \
stack.FastUnwindStack(saved_pc, saved_bp); \
} \
} \
#define GET_STACK_TRACE_HERE(max_size, fast_unwind) \
GET_STACK_TRACE_WITH_PC_AND_BP(max_size, fast_unwind, \
AsanStackTrace::GetCurrentPc(), GET_CURRENT_FRAME()) \
#define GET_STACK_TRACE_HERE_FOR_MALLOC \
GET_STACK_TRACE_HERE(FLAG_malloc_context_size, FLAG_fast_unwind)
#define GET_STACK_TRACE_HERE_FOR_FREE(ptr) \
GET_STACK_TRACE_HERE(FLAG_malloc_context_size, FLAG_fast_unwind)
#define PRINT_CURRENT_STACK() \
{ \
GET_STACK_TRACE_HERE(kStackTraceMax, false); \
stack.PrintStack(); \
} \
#endif // ASAN_STACK_H

View File

@ -0,0 +1,94 @@
//===-- asan_stats.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.
//
// Code related to statistics collected by AddressSanitizer.
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
#include "asan_interface.h"
#include "asan_internal.h"
#include "asan_lock.h"
#include "asan_stats.h"
#include "asan_thread_registry.h"
namespace __asan {
AsanStats::AsanStats() {
CHECK(real_memset != NULL);
real_memset(this, 0, sizeof(AsanStats));
}
static void PrintMallocStatsArray(const char *prefix,
size_t (&array)[kNumberOfSizeClasses]) {
Printf("%s", prefix);
for (size_t i = 0; i < kNumberOfSizeClasses; i++) {
if (!array[i]) continue;
Printf("%ld:%ld; ", i, array[i]);
}
Printf("\n");
}
void AsanStats::Print() {
Printf("Stats: %ldM malloced (%ldM for red zones) by %ld calls\n",
malloced>>20, malloced_redzones>>20, mallocs);
Printf("Stats: %ldM realloced by %ld calls\n", realloced>>20, reallocs);
Printf("Stats: %ldM freed by %ld calls\n", freed>>20, frees);
Printf("Stats: %ldM really freed by %ld calls\n",
really_freed>>20, real_frees);
Printf("Stats: %ldM (%ld full pages) mmaped in %ld calls\n",
mmaped>>20, mmaped / kPageSize, mmaps);
PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size);
PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size);
PrintMallocStatsArray(" frees by size class: ", freed_by_size);
PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size);
Printf("Stats: malloc large: %ld small slow: %ld\n",
malloc_large, malloc_small_slow);
}
static void PrintAccumulatedStats() {
if (!FLAG_stats) return;
AsanStats stats = asanThreadRegistry().GetAccumulatedStats();
// Use lock to keep reports from mixing up.
static AsanLock print_lock(LINKER_INITIALIZED);
ScopedLock lock(&print_lock);
stats.Print();
}
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
size_t __asan_get_current_allocated_bytes() {
return asanThreadRegistry().GetCurrentAllocatedBytes();
}
size_t __asan_get_heap_size() {
return asanThreadRegistry().GetHeapSize();
}
size_t __asan_get_free_bytes() {
return asanThreadRegistry().GetFreeBytes();
}
size_t __asan_get_unmapped_bytes() {
return 0;
}
bool __asan_enable_statistics(bool enable) {
bool old_flag = FLAG_stats;
FLAG_stats = enable;
return old_flag;
}
void __asan_print_accumulated_stats() {
PrintAccumulatedStats();
}

View File

@ -0,0 +1,59 @@
//===-- asan_stats.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.
//
// ASan-private header for statistics.
//===----------------------------------------------------------------------===//
#ifndef ASAN_STATS_H
#define ASAN_STATS_H
#include "asan_allocator.h"
#include "asan_internal.h"
namespace __asan {
// AsanStats struct is NOT thread-safe.
// Each AsanThread has its own AsanStats, which are sometimes flushed
// to the accumulated AsanStats.
struct AsanStats {
// AsanStats must be a struct consisting of size_t fields only.
// When merging two AsanStats structs, we treat them as arrays of size_t.
size_t mallocs;
size_t malloced;
size_t malloced_redzones;
size_t frees;
size_t freed;
size_t real_frees;
size_t really_freed;
size_t really_freed_redzones;
size_t reallocs;
size_t realloced;
size_t mmaps;
size_t mmaped;
size_t mmaped_by_size[kNumberOfSizeClasses];
size_t malloced_by_size[kNumberOfSizeClasses];
size_t freed_by_size[kNumberOfSizeClasses];
size_t really_freed_by_size[kNumberOfSizeClasses];
size_t malloc_large;
size_t malloc_small_slow;
// Ctor for global AsanStats (accumulated stats and main thread stats).
explicit AsanStats(LinkerInitialized) { }
// Default ctor for thread-local stats.
AsanStats();
// Prints formatted stats to stderr.
void Print();
};
} // namespace __asan
#endif // ASAN_STATS_H

View File

@ -0,0 +1,139 @@
//===-- asan_thread.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.
//
// Thread-related code.
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_thread.h"
#include "asan_thread_registry.h"
#include "asan_mapping.h"
#include <sys/mman.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
namespace __asan {
AsanThread::AsanThread(LinkerInitialized x)
: fake_stack_(x),
malloc_storage_(x),
stats_(x) { }
AsanThread::AsanThread(int parent_tid, void *(*start_routine) (void *),
void *arg, AsanStackTrace *stack)
: start_routine_(start_routine),
arg_(arg) {
asanThreadRegistry().RegisterThread(this, parent_tid, stack);
}
AsanThread::~AsanThread() {
asanThreadRegistry().UnregisterThread(this);
fake_stack().Cleanup();
// We also clear the shadow on thread destruction because
// some code may still be executing in later TSD destructors
// and we don't want it to have any poisoned stack.
ClearShadowForThreadStack();
}
void AsanThread::ClearShadowForThreadStack() {
uintptr_t shadow_bot = MemToShadow(stack_bottom_);
uintptr_t shadow_top = MemToShadow(stack_top_);
real_memset((void*)shadow_bot, 0, shadow_top - shadow_bot);
}
void *AsanThread::ThreadStart() {
SetThreadStackTopAndBottom();
fake_stack_.Init(stack_size());
if (FLAG_v >= 1) {
int local = 0;
Report("T%d: stack [%p,%p) size 0x%lx; local=%p, pthread_self=%p\n",
tid(), stack_bottom_, stack_top_,
stack_top_ - stack_bottom_, &local, pthread_self());
}
CHECK(AddrIsInMem(stack_bottom_));
CHECK(AddrIsInMem(stack_top_));
ClearShadowForThreadStack();
if (!start_routine_) {
// start_routine_ == NULL if we're on the main thread or on one of the
// OS X libdispatch worker threads. But nobody is supposed to call
// ThreadStart() for the worker threads.
CHECK(tid() == 0);
return 0;
}
void *res = start_routine_(arg_);
malloc_storage().CommitBack();
if (FLAG_v >= 1) {
Report("T%d exited\n", tid());
}
return res;
}
const char *AsanThread::GetFrameNameByAddr(uintptr_t addr, uintptr_t *offset) {
uintptr_t bottom = 0;
bool is_fake_stack = false;
if (AddrIsInStack(addr)) {
bottom = stack_bottom();
} else {
bottom = fake_stack().AddrIsInFakeStack(addr);
CHECK(bottom);
is_fake_stack = true;
}
uintptr_t aligned_addr = addr & ~(__WORDSIZE/8 - 1); // align addr.
uintptr_t *ptr = (uintptr_t*)aligned_addr;
while (ptr >= (uintptr_t*)bottom) {
if (ptr[0] == kCurrentStackFrameMagic ||
(is_fake_stack && ptr[0] == kRetiredStackFrameMagic)) {
*offset = addr - (uintptr_t)ptr;
return (const char*)ptr[1];
}
ptr--;
}
*offset = 0;
return "UNKNOWN";
}
void AsanThread::SetThreadStackTopAndBottom() {
#ifdef __APPLE__
size_t stacksize = pthread_get_stacksize_np(pthread_self());
void *stackaddr = pthread_get_stackaddr_np(pthread_self());
stack_top_ = (uintptr_t)stackaddr;
stack_bottom_ = stack_top_ - stacksize;
int local;
CHECK(AddrIsInStack((uintptr_t)&local));
#else
pthread_attr_t attr;
CHECK(pthread_getattr_np(pthread_self(), &attr) == 0);
size_t stacksize = 0;
void *stackaddr = NULL;
pthread_attr_getstack(&attr, &stackaddr, &stacksize);
pthread_attr_destroy(&attr);
stack_top_ = (uintptr_t)stackaddr + stacksize;
stack_bottom_ = (uintptr_t)stackaddr;
// When running with unlimited stack size, we still want to set some limit.
// The unlimited stack size is caused by 'ulimit -s unlimited'.
// Also, for some reason, GNU make spawns subrocesses with unlimited stack.
if (stacksize > kMaxThreadStackSize) {
stack_bottom_ = stack_top_ - kMaxThreadStackSize;
}
CHECK(AddrIsInStack((uintptr_t)&attr));
#endif
}
} // namespace __asan

View File

@ -0,0 +1,107 @@
//===-- asan_thread.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.
//
// ASan-private header for asan_thread.cc.
//===----------------------------------------------------------------------===//
#ifndef ASAN_THREAD_H
#define ASAN_THREAD_H
#include "asan_allocator.h"
#include "asan_internal.h"
#include "asan_stack.h"
#include "asan_stats.h"
namespace __asan {
const size_t kMaxThreadStackSize = 16 * (1 << 20); // 16M
class AsanThread;
// These objects are created for every thread and are never deleted,
// so we can find them by tid even if the thread is long dead.
class AsanThreadSummary {
public:
explicit AsanThreadSummary(LinkerInitialized) { } // for T0.
AsanThreadSummary(int tid, int parent_tid, AsanStackTrace *stack)
: tid_(tid),
parent_tid_(parent_tid),
announced_(false) {
if (stack) {
stack_ = *stack;
}
thread_ = 0;
}
void Announce() {
if (tid_ == 0) return; // no need to announce the main thread.
if (!announced_) {
announced_ = true;
Printf("Thread T%d created by T%d here:\n", tid_, parent_tid_);
stack_.PrintStack();
}
}
int tid() { return tid_; }
AsanThread *thread() { return thread_; }
void set_thread(AsanThread *thread) { thread_ = thread; }
private:
int tid_;
int parent_tid_;
bool announced_;
AsanStackTrace stack_;
AsanThread *thread_;
};
// AsanThread are stored in TSD and destroyed when the thread dies.
class AsanThread {
public:
explicit AsanThread(LinkerInitialized); // for T0.
AsanThread(int parent_tid, void *(*start_routine) (void *),
void *arg, AsanStackTrace *stack);
~AsanThread();
void *ThreadStart();
uintptr_t stack_top() { return stack_top_; }
uintptr_t stack_bottom() { return stack_bottom_; }
size_t stack_size() { return stack_top_ - stack_bottom_; }
int tid() { return summary_->tid(); }
AsanThreadSummary *summary() { return summary_; }
void set_summary(AsanThreadSummary *summary) { summary_ = summary; }
const char *GetFrameNameByAddr(uintptr_t addr, uintptr_t *offset);
bool AddrIsInStack(uintptr_t addr) {
return addr >= stack_bottom_ && addr < stack_top_;
}
FakeStack &fake_stack() { return fake_stack_; }
AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; }
AsanStats &stats() { return stats_; }
static const int kInvalidTid = -1;
private:
void SetThreadStackTopAndBottom();
void ClearShadowForThreadStack();
AsanThreadSummary *summary_;
void *(*start_routine_) (void *param);
void *arg_;
uintptr_t stack_top_;
uintptr_t stack_bottom_;
FakeStack fake_stack_;
AsanThreadLocalMallocStorage malloc_storage_;
AsanStats stats_;
};
} // namespace __asan
#endif // ASAN_THREAD_H

View File

@ -0,0 +1,221 @@
//===-- asan_thread_registry.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.
//
// AsanThreadRegistry-related code. AsanThreadRegistry is a container
// for summaries of all created threads.
//===----------------------------------------------------------------------===//
#include "asan_stack.h"
#include "asan_thread.h"
#include "asan_thread_registry.h"
#include <limits.h>
namespace __asan {
static AsanThreadRegistry asan_thread_registry(__asan::LINKER_INITIALIZED);
AsanThreadRegistry &asanThreadRegistry() {
return asan_thread_registry;
}
// Dark magic below. In order to be able to notice that we're not handling
// some thread creation routines (e.g. on Mac OS) we want to distinguish the
// thread that used to have a corresponding AsanThread object from the thread
// that never had one. That's why upon AsanThread destruction we set the
// pthread_key value to some odd number (that's not a valid pointer), instead
// of NULL.
// Because the TSD destructor for a non-NULL key value is called iteratively,
// we increase the value by two, keeping it an invalid pointer.
// Because the TSD implementations are allowed to call such a destructor
// infinitely (see
// http://pubs.opengroup.org/onlinepubs/009604499/functions/pthread_key_create.html
// ), we exit the program after a certain number of iterations.
static void DestroyAsanTsd(void *tsd) {
intptr_t iter = (intptr_t)tsd;
if (iter % 2 == 0) {
// The pointer is valid.
AsanThread *t = (AsanThread*)tsd;
if (t != asanThreadRegistry().GetMain()) {
delete t;
}
iter = 1;
} else {
// The pointer is invalid -- we've already destroyed the TSD before.
// If |iter| is too big, we're in the infinite loop. This should be
// impossible on the systems AddressSanitizer was tested on.
CHECK(iter < 4 * PTHREAD_DESTRUCTOR_ITERATIONS);
iter += 2;
}
CHECK(0 == pthread_setspecific(asanThreadRegistry().GetTlsKey(),
(void*)iter));
if (FLAG_v >= 2) {
Report("DestroyAsanTsd: writing %p to the TSD slot of thread %p\n",
(void*)iter, pthread_self());
}
}
AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x)
: main_thread_(x),
main_thread_summary_(x),
accumulated_stats_(x),
mu_(x) { }
void AsanThreadRegistry::Init() {
CHECK(0 == pthread_key_create(&tls_key_, DestroyAsanTsd));
tls_key_created_ = true;
SetCurrent(&main_thread_);
main_thread_.set_summary(&main_thread_summary_);
main_thread_summary_.set_thread(&main_thread_);
thread_summaries_[0] = &main_thread_summary_;
n_threads_ = 1;
}
void AsanThreadRegistry::RegisterThread(AsanThread *thread, int parent_tid,
AsanStackTrace *stack) {
ScopedLock lock(&mu_);
CHECK(n_threads_ > 0);
int tid = n_threads_;
n_threads_++;
CHECK(n_threads_ < kMaxNumberOfThreads);
AsanThreadSummary *summary = new AsanThreadSummary(tid, parent_tid, stack);
summary->set_thread(thread);
thread_summaries_[tid] = summary;
thread->set_summary(summary);
}
void AsanThreadRegistry::UnregisterThread(AsanThread *thread) {
ScopedLock lock(&mu_);
FlushToAccumulatedStatsUnlocked(&thread->stats());
AsanThreadSummary *summary = thread->summary();
CHECK(summary);
summary->set_thread(NULL);
}
AsanThread *AsanThreadRegistry::GetMain() {
return &main_thread_;
}
AsanThread *AsanThreadRegistry::GetCurrent() {
CHECK(tls_key_created_);
AsanThread *thread = (AsanThread*)pthread_getspecific(tls_key_);
if ((!thread || (intptr_t)thread % 2) && FLAG_v >= 2) {
Report("GetCurrent: %p for thread %p\n", thread, pthread_self());
}
if ((intptr_t)thread % 2) {
// Invalid pointer -- we've deleted the AsanThread already. Return NULL as
// if the TSD was empty.
// TODO(glider): if the code in the client TSD destructor calls
// pthread_create(), we'll set the parent tid of the spawned thread to NULL,
// although the creation stack will belong to the current thread. This may
// confuse the user, but is quite unlikely.
return NULL;
} else {
// NULL or valid pointer to AsanThread.
return thread;
}
}
void AsanThreadRegistry::SetCurrent(AsanThread *t) {
if (FLAG_v >=2) {
Report("SetCurrent: %p for thread %p\n", t, pthread_self());
}
// Make sure we do not reset the current AsanThread.
intptr_t old_key = (intptr_t)pthread_getspecific(tls_key_);
CHECK(!old_key || old_key % 2);
CHECK(0 == pthread_setspecific(tls_key_, t));
CHECK(pthread_getspecific(tls_key_) == t);
}
pthread_key_t AsanThreadRegistry::GetTlsKey() {
return tls_key_;
}
// Returns true iff DestroyAsanTsd() was already called for this thread.
bool AsanThreadRegistry::IsCurrentThreadDying() {
CHECK(tls_key_created_);
intptr_t thread = (intptr_t)pthread_getspecific(tls_key_);
return (bool)(thread % 2);
}
AsanStats &AsanThreadRegistry::GetCurrentThreadStats() {
AsanThread *t = GetCurrent();
return (t) ? t->stats() : main_thread_.stats();
}
AsanStats AsanThreadRegistry::GetAccumulatedStats() {
ScopedLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
return accumulated_stats_;
}
size_t AsanThreadRegistry::GetCurrentAllocatedBytes() {
ScopedLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
return accumulated_stats_.malloced - accumulated_stats_.freed;
}
size_t AsanThreadRegistry::GetHeapSize() {
ScopedLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
return accumulated_stats_.mmaped;
}
size_t AsanThreadRegistry::GetFreeBytes() {
ScopedLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
return accumulated_stats_.mmaped
- accumulated_stats_.malloced
- accumulated_stats_.malloced_redzones
+ accumulated_stats_.really_freed
+ accumulated_stats_.really_freed_redzones;
}
AsanThreadSummary *AsanThreadRegistry::FindByTid(int tid) {
CHECK(tid >= 0);
CHECK(tid < n_threads_);
CHECK(thread_summaries_[tid]);
return thread_summaries_[tid];
}
AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uintptr_t addr) {
ScopedLock lock(&mu_);
for (int tid = 0; tid < n_threads_; tid++) {
AsanThread *t = thread_summaries_[tid]->thread();
if (!t) continue;
if (t->fake_stack().AddrIsInFakeStack(addr) || t->AddrIsInStack(addr)) {
return t;
}
}
return 0;
}
void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() {
for (int tid = 0; tid < n_threads_; tid++) {
AsanThread *t = thread_summaries_[tid]->thread();
if (t != NULL) {
FlushToAccumulatedStatsUnlocked(&t->stats());
}
}
}
void AsanThreadRegistry::FlushToAccumulatedStatsUnlocked(AsanStats *stats) {
// AsanStats consists of variables of type size_t only.
size_t *dst = (size_t*)&accumulated_stats_;
size_t *src = (size_t*)stats;
size_t num_fields = sizeof(AsanStats) / sizeof(size_t);
for (size_t i = 0; i < num_fields; i++) {
dst[i] += src[i];
src[i] = 0;
}
}
} // namespace __asan

View File

@ -0,0 +1,88 @@
//===-- asan_thread_registry.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.
//
// ASan-private header for asan_thread_registry.cc
//===----------------------------------------------------------------------===//
#ifndef ASAN_THREAD_REGISTRY_H
#define ASAN_THREAD_REGISTRY_H
#include "asan_lock.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
namespace __asan {
// Stores summaries of all created threads, returns current thread,
// thread by tid, thread by stack address. There is a single instance
// of AsanThreadRegistry for the whole program.
// AsanThreadRegistry is thread-safe.
class AsanThreadRegistry {
public:
explicit AsanThreadRegistry(LinkerInitialized);
void Init();
void RegisterThread(AsanThread *thread, int parent_tid,
AsanStackTrace *stack);
void UnregisterThread(AsanThread *thread);
AsanThread *GetMain();
// Get the current thread. May return NULL.
AsanThread *GetCurrent();
void SetCurrent(AsanThread *t);
pthread_key_t GetTlsKey();
bool IsCurrentThreadDying();
int GetCurrentTidOrMinusOne() {
AsanThread *t = GetCurrent();
return t ? t->tid() : -1;
}
// Returns stats for GetCurrent(), or stats for
// T0 if GetCurrent() returns NULL.
AsanStats &GetCurrentThreadStats();
// Flushes all thread-local stats to accumulated stats, and returns
// a copy of accumulated stats.
AsanStats GetAccumulatedStats();
size_t GetCurrentAllocatedBytes();
size_t GetHeapSize();
size_t GetFreeBytes();
AsanThreadSummary *FindByTid(int tid);
AsanThread *FindThreadByStackAddress(uintptr_t addr);
private:
void UpdateAccumulatedStatsUnlocked();
// Adds values of all counters in "stats" to accumulated stats,
// and fills "stats" with zeroes.
void FlushToAccumulatedStatsUnlocked(AsanStats *stats);
static const int kMaxNumberOfThreads = (1 << 22); // 4M
AsanThreadSummary *thread_summaries_[kMaxNumberOfThreads];
AsanThread main_thread_;
AsanThreadSummary main_thread_summary_;
AsanStats accumulated_stats_;
int n_threads_;
AsanLock mu_;
// For each thread tls_key_ stores the pointer to the corresponding
// AsanThread.
pthread_key_t tls_key_;
// This flag is updated only once at program startup, and then read
// by concurrent threads.
bool tls_key_created_;
};
// Returns a single instance of registry.
AsanThreadRegistry &asanThreadRegistry();
} // namespace __asan
#endif // ASAN_THREAD_REGISTRY_H

View File

@ -0,0 +1,771 @@
/*******************************************************************************
mach_override.c
Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
Some rights reserved: <http://opensource.org/licenses/mit-license.php>
***************************************************************************/
#include "mach_override.h"
#include <mach-o/dyld.h>
#include <mach/mach_host.h>
#include <mach/mach_init.h>
#include <mach/vm_map.h>
#include <sys/mman.h>
#include <CoreServices/CoreServices.h>
//#define DEBUG_DISASM 1
#undef DEBUG_DISASM
/**************************
*
* Constants
*
**************************/
#pragma mark -
#pragma mark (Constants)
#if defined(__ppc__) || defined(__POWERPC__)
long kIslandTemplate[] = {
0x9001FFFC, // stw r0,-4(SP)
0x3C00DEAD, // lis r0,0xDEAD
0x6000BEEF, // ori r0,r0,0xBEEF
0x7C0903A6, // mtctr r0
0x8001FFFC, // lwz r0,-4(SP)
0x60000000, // nop ; optionally replaced
0x4E800420 // bctr
};
#define kAddressHi 3
#define kAddressLo 5
#define kInstructionHi 10
#define kInstructionLo 11
#elif defined(__i386__)
#define kOriginalInstructionsSize 16
char kIslandTemplate[] = {
// kOriginalInstructionsSize nop instructions so that we
// should have enough space to host original instructions
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
// Now the real jump instruction
0xE9, 0xEF, 0xBE, 0xAD, 0xDE
};
#define kInstructions 0
#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
#elif defined(__x86_64__)
#define kOriginalInstructionsSize 32
#define kJumpAddress kOriginalInstructionsSize + 6
char kIslandTemplate[] = {
// kOriginalInstructionsSize nop instructions so that we
// should have enough space to host original instructions
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
// Now the real jump instruction
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
#endif
#define kAllocateHigh 1
#define kAllocateNormal 0
/**************************
*
* Data Types
*
**************************/
#pragma mark -
#pragma mark (Data Types)
typedef struct {
char instructions[sizeof(kIslandTemplate)];
int allocatedHigh;
} BranchIsland;
/**************************
*
* Funky Protos
*
**************************/
#pragma mark -
#pragma mark (Funky Protos)
mach_error_t
allocateBranchIsland(
BranchIsland **island,
int allocateHigh,
void *originalFunctionAddress);
mach_error_t
freeBranchIsland(
BranchIsland *island );
#if defined(__ppc__) || defined(__POWERPC__)
mach_error_t
setBranchIslandTarget(
BranchIsland *island,
const void *branchTo,
long instruction );
#endif
#if defined(__i386__) || defined(__x86_64__)
mach_error_t
setBranchIslandTarget_i386(
BranchIsland *island,
const void *branchTo,
char* instructions );
void
atomic_mov64(
uint64_t *targetAddress,
uint64_t value );
static Boolean
eatKnownInstructions(
unsigned char *code,
uint64_t *newInstruction,
int *howManyEaten,
char *originalInstructions );
#endif
/*******************************************************************************
*
* Interface
*
*******************************************************************************/
#pragma mark -
#pragma mark (Interface)
#if defined(__x86_64__)
mach_error_t makeIslandExecutable(void *address) {
mach_error_t err = err_none;
vm_size_t pageSize;
host_page_size( mach_host_self(), &pageSize );
uint64_t page = (uint64_t)address & ~(uint64_t)(pageSize-1);
int e = err_none;
e |= mprotect((void *)page, pageSize, PROT_EXEC | PROT_READ | PROT_WRITE);
e |= msync((void *)page, pageSize, MS_INVALIDATE );
if (e) {
err = err_cannot_override;
}
return err;
}
#endif
mach_error_t
mach_override_ptr(
void *originalFunctionAddress,
const void *overrideFunctionAddress,
void **originalFunctionReentryIsland )
{
assert( originalFunctionAddress );
assert( overrideFunctionAddress );
long *originalFunctionPtr = (long*) originalFunctionAddress;
#ifdef DEBUG_DISASM
{
fprintf(stderr, "Replacing function at %p\n", originalFunctionAddress);
fprintf(stderr, "First 16 bytes of the function: ");
unsigned char *orig = (unsigned char *)originalFunctionAddress;
int i;
for (i = 0; i < 16; i++) {
fprintf(stderr, "%x ", (unsigned int) orig[i]);
}
fprintf(stderr, "\n");
fprintf(stderr,
"To disassemble, save the following function as disas.c"
" and run:\n gcc -c disas.c && gobjdump -d disas.o\n"
"The first 16 bytes of the original function will start"
" after four nop instructions.\n");
fprintf(stderr, "\nvoid foo() {\n asm volatile(\"nop;nop;nop;nop;\");\n");
int j = 0;
for (j = 0; j < 2; j++) {
fprintf(stderr, " asm volatile(\".byte ");
for (i = 8 * j; i < 8 * (j+1) - 1; i++) {
fprintf(stderr, "0x%x, ", (unsigned int) orig[i]);
}
fprintf(stderr, "0x%x;\");\n", (unsigned int) orig[8 * (j+1) - 1]);
}
fprintf(stderr, "}\n\n");
}
#endif
mach_error_t err = err_none;
#if defined(__ppc__) || defined(__POWERPC__)
// Ensure first instruction isn't 'mfctr'.
#define kMFCTRMask 0xfc1fffff
#define kMFCTRInstruction 0x7c0903a6
long originalInstruction = *originalFunctionPtr;
if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) )
err = err_cannot_override;
#elif defined(__i386__) || defined(__x86_64__)
int eatenCount = 0;
char originalInstructions[kOriginalInstructionsSize];
uint64_t jumpRelativeInstruction = 0; // JMP
Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr,
&jumpRelativeInstruction, &eatenCount, originalInstructions);
#ifdef DEBUG_DISASM
if (!overridePossible) fprintf(stderr, "overridePossible = false @%d\n", __LINE__);
#endif
if (eatenCount > kOriginalInstructionsSize) {
#ifdef DEBUG_DISASM
fprintf(stderr, "Too many instructions eaten\n");
#endif
overridePossible = false;
}
if (!overridePossible) err = err_cannot_override;
if (err) printf("err = %x %d\n", err, __LINE__);
#endif
// Make the original function implementation writable.
if( !err ) {
err = vm_protect( mach_task_self(),
(vm_address_t) originalFunctionPtr,
sizeof(long), false, (VM_PROT_ALL | VM_PROT_COPY) );
if( err )
err = vm_protect( mach_task_self(),
(vm_address_t) originalFunctionPtr, sizeof(long), false,
(VM_PROT_DEFAULT | VM_PROT_COPY) );
}
if (err) printf("err = %x %d\n", err, __LINE__);
// Allocate and target the escape island to the overriding function.
BranchIsland *escapeIsland = NULL;
if( !err )
err = allocateBranchIsland( &escapeIsland, kAllocateHigh, originalFunctionAddress );
if (err) printf("err = %x %d\n", err, __LINE__);
#if defined(__ppc__) || defined(__POWERPC__)
if( !err )
err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
// Build the branch absolute instruction to the escape island.
long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning.
if( !err ) {
long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF;
branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress;
}
#elif defined(__i386__) || defined(__x86_64__)
if (err) printf("err = %x %d\n", err, __LINE__);
if( !err )
err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 );
if (err) printf("err = %x %d\n", err, __LINE__);
// Build the jump relative instruction to the escape island
#endif
#if defined(__i386__) || defined(__x86_64__)
if (!err) {
uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5);
addressOffset = OSSwapInt32(addressOffset);
jumpRelativeInstruction |= 0xE900000000000000LL;
jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24;
jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction);
}
#endif
// Optionally allocate & return the reentry island.
BranchIsland *reentryIsland = NULL;
if( !err && originalFunctionReentryIsland ) {
err = allocateBranchIsland( &reentryIsland, kAllocateNormal, NULL);
if( !err )
*originalFunctionReentryIsland = reentryIsland;
}
#if defined(__ppc__) || defined(__POWERPC__)
// Atomically:
// o If the reentry island was allocated:
// o Insert the original instruction into the reentry island.
// o Target the reentry island at the 2nd instruction of the
// original function.
// o Replace the original instruction with the branch absolute.
if( !err ) {
int escapeIslandEngaged = false;
do {
if( reentryIsland )
err = setBranchIslandTarget( reentryIsland,
(void*) (originalFunctionPtr+1), originalInstruction );
if( !err ) {
escapeIslandEngaged = CompareAndSwap( originalInstruction,
branchAbsoluteInstruction,
(UInt32*)originalFunctionPtr );
if( !escapeIslandEngaged ) {
// Someone replaced the instruction out from under us,
// re-read the instruction, make sure it's still not
// 'mfctr' and try again.
originalInstruction = *originalFunctionPtr;
if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
err = err_cannot_override;
}
}
} while( !err && !escapeIslandEngaged );
}
#elif defined(__i386__) || defined(__x86_64__)
// Atomically:
// o If the reentry island was allocated:
// o Insert the original instructions into the reentry island.
// o Target the reentry island at the first non-replaced
// instruction of the original function.
// o Replace the original first instructions with the jump relative.
//
// Note that on i386, we do not support someone else changing the code under our feet
if ( !err ) {
if( reentryIsland )
err = setBranchIslandTarget_i386( reentryIsland,
(void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions );
if ( !err )
atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction);
}
#endif
// Clean up on error.
if( err ) {
if( reentryIsland )
freeBranchIsland( reentryIsland );
if( escapeIsland )
freeBranchIsland( escapeIsland );
}
#if defined(__x86_64__)
err = makeIslandExecutable(escapeIsland);
err = makeIslandExecutable(reentryIsland);
#endif
#ifdef DEBUG_DISASM
{
fprintf(stderr, "First 16 bytes of the function after slicing: ");
unsigned char *orig = (unsigned char *)originalFunctionAddress;
int i;
for (i = 0; i < 16; i++) {
fprintf(stderr, "%x ", (unsigned int) orig[i]);
}
fprintf(stderr, "\n");
}
#endif
return err;
}
/*******************************************************************************
*
* Implementation
*
*******************************************************************************/
#pragma mark -
#pragma mark (Implementation)
/***************************************************************************//**
Implementation: Allocates memory for a branch island.
@param island <- The allocated island.
@param allocateHigh -> Whether to allocate the island at the end of the
address space (for use with the branch absolute
instruction).
@result <- mach_error_t
***************************************************************************/
mach_error_t
allocateBranchIsland(
BranchIsland **island,
int allocateHigh,
void *originalFunctionAddress)
{
assert( island );
mach_error_t err = err_none;
if( allocateHigh ) {
vm_size_t pageSize;
err = host_page_size( mach_host_self(), &pageSize );
if( !err ) {
assert( sizeof( BranchIsland ) <= pageSize );
#if defined(__x86_64__)
vm_address_t first = (uint64_t)originalFunctionAddress & ~(uint64_t)(((uint64_t)1 << 31) - 1) | ((uint64_t)1 << 31); // start in the middle of the page?
vm_address_t last = 0x0;
#else
vm_address_t first = 0xfeffffff;
vm_address_t last = 0xfe000000 + pageSize;
#endif
vm_address_t page = first;
int allocated = 0;
vm_map_t task_self = mach_task_self();
while( !err && !allocated && page != last ) {
err = vm_allocate( task_self, &page, pageSize, 0 );
if( err == err_none )
allocated = 1;
else if( err == KERN_NO_SPACE ) {
#if defined(__x86_64__)
page -= pageSize;
#else
page += pageSize;
#endif
err = err_none;
}
}
if( allocated )
*island = (BranchIsland*) page;
else if( !allocated && !err )
err = KERN_NO_SPACE;
}
} else {
void *block = malloc( sizeof( BranchIsland ) );
if( block )
*island = (BranchIsland*)block;
else
err = KERN_NO_SPACE;
}
if( !err )
(**island).allocatedHigh = allocateHigh;
return err;
}
/***************************************************************************//**
Implementation: Deallocates memory for a branch island.
@param island -> The island to deallocate.
@result <- mach_error_t
***************************************************************************/
mach_error_t
freeBranchIsland(
BranchIsland *island )
{
assert( island );
assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] );
assert( island->allocatedHigh );
mach_error_t err = err_none;
if( island->allocatedHigh ) {
vm_size_t pageSize;
err = host_page_size( mach_host_self(), &pageSize );
if( !err ) {
assert( sizeof( BranchIsland ) <= pageSize );
err = vm_deallocate(
mach_task_self(),
(vm_address_t) island, pageSize );
}
} else {
free( island );
}
return err;
}
/***************************************************************************//**
Implementation: Sets the branch island's target, with an optional
instruction.
@param island -> The branch island to insert target into.
@param branchTo -> The address of the target.
@param instruction -> Optional instruction to execute prior to branch. Set
to zero for nop.
@result <- mach_error_t
***************************************************************************/
#if defined(__ppc__) || defined(__POWERPC__)
mach_error_t
setBranchIslandTarget(
BranchIsland *island,
const void *branchTo,
long instruction )
{
// Copy over the template code.
bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
// Fill in the address.
((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
((short*)island->instructions)[kAddressHi]
= (((long) branchTo) >> 16) & 0x0000FFFF;
// Fill in the (optional) instuction.
if( instruction != 0 ) {
((short*)island->instructions)[kInstructionLo]
= instruction & 0x0000FFFF;
((short*)island->instructions)[kInstructionHi]
= (instruction >> 16) & 0x0000FFFF;
}
//MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
return err_none;
}
#endif
#if defined(__i386__)
mach_error_t
setBranchIslandTarget_i386(
BranchIsland *island,
const void *branchTo,
char* instructions )
{
// Copy over the template code.
bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
// copy original instructions
if (instructions) {
bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize);
}
// Fill in the address.
int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4);
*((int32_t *)(island->instructions + kJumpAddress)) = addressOffset;
msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
return err_none;
}
#elif defined(__x86_64__)
mach_error_t
setBranchIslandTarget_i386(
BranchIsland *island,
const void *branchTo,
char* instructions )
{
// Copy over the template code.
bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
// Copy original instructions.
if (instructions) {
bcopy (instructions, island->instructions, kOriginalInstructionsSize);
}
// Fill in the address.
*((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo;
msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
return err_none;
}
#endif
#if defined(__i386__) || defined(__x86_64__)
// simplistic instruction matching
typedef struct {
unsigned int length; // max 15
unsigned char mask[15]; // sequence of bytes in memory order
unsigned char constraint[15]; // sequence of bytes in memory order
} AsmInstructionMatch;
#if defined(__i386__)
static AsmInstructionMatch possibleInstructions[] = {
{ 0x1, {0xFF}, {0x90} }, // nop
{ 0x1, {0xFF}, {0x55} }, // push %esp
{ 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp
{ 0x3, {0xFF, 0xFF, 0xFF}, {0x89, 0x1C, 0x24} }, // mov %ebx,(%esp)
{ 0x1, {0xFF}, {0x53} }, // push %ebx
{ 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp
{ 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate
{ 0x1, {0xFF}, {0x57} }, // push %edi
{ 0x1, {0xFF}, {0x56} }, // push %esi
{ 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
{ 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg
{ 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg
{ 0x4, {0xFF, 0x00, 0x00, 0x00}, {0x8B, 0x00, 0x00, 0x00} }, // mov r16,r/m16 or r32,r/m32
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB9, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %ecx
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x66, 0x0F, 0xEF, 0x00} }, // pxor xmm2/128, xmm1
{ 0x2, {0xFF, 0xFF}, {0xDB, 0xE3} }, // fninit
{ 0x0 }
};
#elif defined(__x86_64__)
// TODO(glider): disassembling the "0x48, 0x89" sequences is trickier than it's done below.
// If it stops working, refer to http://ref.x86asm.net/geek.html#modrm_byte_32_64 to do it
// more accurately.
static AsmInstructionMatch possibleInstructions[] = {
{ 0x1, {0xFF}, {0x90} }, // nop
{ 0x1, {0xF8}, {0x50} }, // push %rX
{ 0x1, {0xFF}, {0x65} }, // GS prefix
{ 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp
{ 0x4, {0xFB, 0xFF, 0x07, 0x00}, {0x48, 0x89, 0x05, 0x00} }, // move onto rbp
{ 0x3, {0xFB, 0xFF, 0x00}, {0x48, 0x89, 0x00} }, // mov %reg, %reg
{ 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX
{ 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX
{ 0x2, {0xFF, 0x00}, {0x77, 0x00} }, // ja $i8
{ 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0x25, 0x00, 0x00, 0x00, 0x00} }, // and $imm, %eax
{ 0x8, {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00},
{0x48, 0x8B, 0x34, 0x25, 0x00, 0x00, 0x00, 0x00}, }, // mov $imm, %rsi
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xFA, 0x00}, }, // cmp $i8, %rdx
{ 0xa, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %rax
{ 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00},
{0x81, 0xE6, 0x00, 0x00, 0x00, 0x00} }, // and $imm, %esi
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x66, 0x0F, 0xEF, 0x00} }, // pxor xmm2/128, xmm1
{ 0x2, {0xFF, 0x00}, {0x89, 0x00} }, // mov r/m32,r32 or r/m16,r16
{ 0x3, {0xFF, 0xFF, 0xFF}, {0x49, 0x89, 0xF8} }, // mov %rdi,%r8
{ 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi)
{ 0x2, {0xFF, 0xFF}, {0xDB, 0xE3} }, // fninit
{ 0x0 }
};
#endif
static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction)
{
Boolean match = true;
size_t i;
assert(instruction);
#ifdef DEBUG_DISASM
fprintf(stderr, "Matching: ");
#endif
for (i=0; i<instruction->length; i++) {
unsigned char mask = instruction->mask[i];
unsigned char constraint = instruction->constraint[i];
unsigned char codeValue = code[i];
#ifdef DEBUG_DISASM
fprintf(stderr, "%x ", (unsigned)codeValue);
#endif
match = ((codeValue & mask) == constraint);
if (!match) break;
}
#ifdef DEBUG_DISASM
if (match) {
fprintf(stderr, " OK\n");
} else {
fprintf(stderr, " FAIL\n");
}
#endif
return match;
}
#if defined(__i386__) || defined(__x86_64__)
static Boolean
eatKnownInstructions(
unsigned char *code,
uint64_t* newInstruction,
int* howManyEaten,
char* originalInstructions )
{
Boolean allInstructionsKnown = true;
int totalEaten = 0;
unsigned char* ptr = code;
int remainsToEat = 5; // a JMP instruction takes 5 bytes
if (howManyEaten) *howManyEaten = 0;
while (remainsToEat > 0) {
Boolean curInstructionKnown = false;
// See if instruction matches one we know
AsmInstructionMatch* curInstr = possibleInstructions;
do {
if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break;
curInstr++;
} while (curInstr->length > 0);
// if all instruction matches failed, we don't know current instruction then, stop here
if (!curInstructionKnown) {
allInstructionsKnown = false;
break;
}
// At this point, we've matched curInstr
int eaten = curInstr->length;
ptr += eaten;
remainsToEat -= eaten;
totalEaten += eaten;
}
if (howManyEaten) *howManyEaten = totalEaten;
if (originalInstructions) {
Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize);
if (enoughSpaceForOriginalInstructions) {
memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP
bcopy(code, originalInstructions, totalEaten);
} else {
#ifdef DEBUG_DISASM
fprintf (stderr, "Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
#endif
return false;
}
}
if (allInstructionsKnown) {
// save last 3 bytes of first 64bits of codre we'll replace
uint64_t currentFirst64BitsOfCode = *((uint64_t *)code);
currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation
currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL;
// keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
*newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes
*newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes
}
return allInstructionsKnown;
}
#endif
#if defined(__i386__)
__asm(
".text;"
".align 2, 0x90;"
"_atomic_mov64:;"
" pushl %ebp;"
" movl %esp, %ebp;"
" pushl %esi;"
" pushl %ebx;"
" pushl %ecx;"
" pushl %eax;"
" pushl %edx;"
// atomic push of value to an address
// we use cmpxchg8b, which compares content of an address with
// edx:eax. If they are equal, it atomically puts 64bit value
// ecx:ebx in address.
// We thus put contents of address in edx:eax to force ecx:ebx
// in address
" mov 8(%ebp), %esi;" // esi contains target address
" mov 12(%ebp), %ebx;"
" mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
" mov (%esi), %eax;"
" mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
" lock; cmpxchg8b (%esi);" // atomic move.
// restore registers
" popl %edx;"
" popl %eax;"
" popl %ecx;"
" popl %ebx;"
" popl %esi;"
" popl %ebp;"
" ret"
);
#elif defined(__x86_64__)
void atomic_mov64(
uint64_t *targetAddress,
uint64_t value )
{
*targetAddress = value;
}
#endif
#endif

View File

@ -0,0 +1,121 @@
/*******************************************************************************
mach_override.h
Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
Some rights reserved: <http://opensource.org/licenses/mit-license.php>
***************************************************************************/
/***************************************************************************//**
@mainpage mach_override
@author Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
This package, coded in C to the Mach API, allows you to override ("patch")
program- and system-supplied functions at runtime. You can fully replace
functions with your implementations, or merely head- or tail-patch the
original implementations.
Use it by #include'ing mach_override.h from your .c, .m or .mm file(s).
@todo Discontinue use of Carbon's MakeDataExecutable() and
CompareAndSwap() calls and start using the Mach equivalents, if they
exist. If they don't, write them and roll them in. That way, this
code will be pure Mach, which will make it easier to use everywhere.
Update: MakeDataExecutable() has been replaced by
msync(MS_INVALIDATE). There is an OSCompareAndSwap in libkern, but
I'm currently unsure if I can link against it. May have to roll in
my own version...
@todo Stop using an entire 4K high-allocated VM page per 28-byte escape
branch island. Done right, this will dramatically speed up escape
island allocations when they number over 250. Then again, if you're
overriding more than 250 functions, maybe speed isn't your main
concern...
@todo Add detection of: b, bl, bla, bc, bcl, bcla, bcctrl, bclrl
first-instructions. Initially, we should refuse to override
functions beginning with these instructions. Eventually, we should
dynamically rewrite them to make them position-independent.
@todo Write mach_unoverride(), which would remove an override placed on a
function. Must be multiple-override aware, which means an almost
complete rewrite under the covers, because the target address can't
be spread across two load instructions like it is now since it will
need to be atomically updatable.
@todo Add non-rentry variants of overrides to test_mach_override.
***************************************************************************/
#ifndef _mach_override_
#define _mach_override_
#include <sys/types.h>
#include <mach/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Returned if the function to be overrided begins with a 'mfctr' instruction.
*/
#define err_cannot_override (err_local|1)
/************************************************************************************//**
Dynamically overrides the function implementation referenced by
originalFunctionAddress with the implentation pointed to by overrideFunctionAddress.
Optionally returns a pointer to a "reentry island" which, if jumped to, will resume
the original implementation.
@param originalFunctionAddress -> Required address of the function to
override (with overrideFunctionAddress).
@param overrideFunctionAddress -> Required address to the overriding
function.
@param originalFunctionReentryIsland <- Optional pointer to pointer to the
reentry island. Can be NULL.
@result <- err_cannot_override if the original
function's implementation begins with
the 'mfctr' instruction.
************************************************************************************/
mach_error_t
mach_override_ptr(
void *originalFunctionAddress,
const void *overrideFunctionAddress,
void **originalFunctionReentryIsland );
/************************************************************************************//**
************************************************************************************/
#ifdef __cplusplus
#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \
{ \
static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \
static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \
class mach_override_class__##ORIGINAL_FUNCTION_NAME { \
public: \
static kern_return_t override(void *originalFunctionPtr) { \
kern_return_t result = err_none; \
if (!ORIGINAL_FUNCTION_NAME##_overriden) { \
ORIGINAL_FUNCTION_NAME##_overriden = true; \
result = mach_override_ptr( (void*)originalFunctionPtr, \
(void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \
(void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \
} \
return result; \
} \
static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS {
#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \
} \
}; \
\
err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \
}
#endif
#ifdef __cplusplus
}
#endif
#endif // _mach_override_

View File

@ -0,0 +1,101 @@
#!/usr/bin/env python
#===- lib/asan/scripts/asan_symbolize.py -----------------------------------===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
import os
import re
import sys
import string
import subprocess
pipes = {}
def patch_address(frameno, addr_s):
''' Subtracts 1 or 2 from the top frame's address.
Top frame is normally the return address from asan_report*
call, which is not expected to return at all. Because of that, this
address often belongs to the next source code line, or even to a different
function. '''
if frameno == '0':
addr = int(addr_s, 16)
if os.uname()[4].startswith('arm'):
# Cancel the Thumb bit
addr = addr & (~1)
addr -= 1
return hex(addr)
return addr_s
# TODO(glider): need some refactoring here
def symbolize_addr2line(line):
#0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
match = re.match('^( *#([0-9]+) *0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line)
if match:
frameno = match.group(2)
binary = match.group(3)
addr = match.group(4)
addr = patch_address(frameno, addr)
if not pipes.has_key(binary):
pipes[binary] = subprocess.Popen(["addr2line", "-f", "-e", binary],
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p = pipes[binary]
try:
print >>p.stdin, addr
function_name = p.stdout.readline().rstrip()
file_name = p.stdout.readline().rstrip()
except:
function_name = ""
file_name = ""
for path_to_cut in sys.argv[1:]:
file_name = re.sub(".*" + path_to_cut, "", file_name)
file_name = re.sub(".*asan_[a-z_]*.cc:[0-9]*", "_asan_rtl_", file_name)
file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
print match.group(1), "in", function_name, file_name
else:
print line.rstrip()
def symbolize_atos(line):
#0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
match = re.match('^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line)
if match:
#print line
prefix = match.group(1)
frameno = match.group(2)
addr = match.group(3)
binary = match.group(4)
offset = match.group(5)
addr = patch_address(frameno, addr)
load_addr = int(addr, 16) - int(offset, 16)
if not pipes.has_key(binary):
#print "atos -o %s -l %s" % (binary, hex(load_addr))
pipes[binary] = subprocess.Popen(["atos", "-o", binary],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,)
p = pipes[binary]
# TODO(glider): how to tell if the address is absolute?
if ".app/" in binary and not ".framework" in binary:
print >>p.stdin, "%s" % addr
else:
print >>p.stdin, "%s" % offset
# TODO(glider): it's more efficient to make a batch atos run for each binary.
p.stdin.close()
atos_line = p.stdout.readline().rstrip()
del pipes[binary]
print "%s%s in %s" % (prefix, addr, atos_line)
else:
print line.rstrip()
system = os.uname()[0]
if system in ['Linux', 'Darwin']:
for line in sys.stdin:
if system == 'Linux':
symbolize_addr2line(line)
elif system == 'Darwin':
symbolize_atos(line)
else:
print 'Unknown system: ', system

View File

@ -0,0 +1,321 @@
// Copyright (c) 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef _BASICTYPES_H_
#define _BASICTYPES_H_
#include <inttypes.h> // uint16_t might be here; PRId64 too.
#include <stdint.h> // to get uint16_t (ISO naming madness)
#include <sys/types.h> // our last best hope for uint16_t
// Standard typedefs
// All Google code is compiled with -funsigned-char to make "char"
// unsigned. Google code therefore doesn't need a "uchar" type.
// TODO(csilvers): how do we make sure unsigned-char works on non-gcc systems?
typedef signed char schar;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
// NOTE: unsigned types are DANGEROUS in loops and other arithmetical
// places. Use the signed types unless your variable represents a bit
// pattern (eg a hash value) or you really need the extra bit. Do NOT
// use 'unsigned' to express "this value should always be positive";
// use assertions for this.
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
const uint16 kuint16max = ( (uint16) 0xFFFF);
const uint32 kuint32max = ( (uint32) 0xFFFFFFFF);
const uint64 kuint64max = ( (((uint64) kuint32max) << 32) | kuint32max );
const int8 kint8max = ( ( int8) 0x7F);
const int16 kint16max = ( ( int16) 0x7FFF);
const int32 kint32max = ( ( int32) 0x7FFFFFFF);
const int64 kint64max = ( ((( int64) kint32max) << 32) | kuint32max );
const int8 kint8min = ( ( int8) 0x80);
const int16 kint16min = ( ( int16) 0x8000);
const int32 kint32min = ( ( int32) 0x80000000);
const int64 kint64min = ( ((( int64) kint32min) << 32) | 0 );
// Define the "portable" printf and scanf macros, if they're not
// already there (via the inttypes.h we #included above, hopefully).
// Mostly it's old systems that don't support inttypes.h, so we assume
// they're 32 bit.
#ifndef PRIx64
#define PRIx64 "llx"
#endif
#ifndef SCNx64
#define SCNx64 "llx"
#endif
#ifndef PRId64
#define PRId64 "lld"
#endif
#ifndef SCNd64
#define SCNd64 "lld"
#endif
#ifndef PRIu64
#define PRIu64 "llu"
#endif
#ifndef PRIxPTR
#define PRIxPTR "lx"
#endif
// Also allow for printing of a pthread_t.
#define GPRIuPTHREAD "lu"
#define GPRIxPTHREAD "lx"
#if defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__)
#define PRINTABLE_PTHREAD(pthreadt) reinterpret_cast<uintptr_t>(pthreadt)
#else
#define PRINTABLE_PTHREAD(pthreadt) pthreadt
#endif
// A macro to disallow the evil copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
// An alternate name that leaves out the moral judgment... :-)
#define DISALLOW_COPY_AND_ASSIGN(TypeName) DISALLOW_EVIL_CONSTRUCTORS(TypeName)
// The COMPILE_ASSERT macro can be used to verify that a compile time
// expression is true. For example, you could use it to verify the
// size of a static array:
//
// COMPILE_ASSERT(sizeof(num_content_type_names) == sizeof(int),
// content_type_names_incorrect_size);
//
// or to make sure a struct is smaller than a certain size:
//
// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
//
// The second argument to the macro is the name of the variable. If
// the expression is false, most compilers will issue a warning/error
// containing the name of the variable.
//
// Implementation details of COMPILE_ASSERT:
//
// - COMPILE_ASSERT works by defining an array type that has -1
// elements (and thus is invalid) when the expression is false.
//
// - The simpler definition
//
// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1]
//
// does not work, as gcc supports variable-length arrays whose sizes
// are determined at run-time (this is gcc's extension and not part
// of the C++ standard). As a result, gcc fails to reject the
// following code with the simple definition:
//
// int foo;
// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is
// // not a compile-time constant.
//
// - By using the type CompileAssert<(bool(expr))>, we ensures that
// expr is a compile-time constant. (Template arguments must be
// determined at compile-time.)
//
// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
//
// CompileAssert<bool(expr)>
//
// instead, these compilers will refuse to compile
//
// COMPILE_ASSERT(5 > 0, some_message);
//
// (They seem to think the ">" in "5 > 0" marks the end of the
// template argument list.)
//
// - The array size is (bool(expr) ? 1 : -1), instead of simply
//
// ((expr) ? 1 : -1).
//
// This is to avoid running into a bug in MS VC 7.1, which
// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
template <bool>
struct CompileAssert {
};
#define COMPILE_ASSERT(expr, msg) \
typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
#define arraysize(a) (sizeof(a) / sizeof(*(a)))
#define OFFSETOF_MEMBER(strct, field) \
(reinterpret_cast<char*>(&reinterpret_cast<strct*>(16)->field) - \
reinterpret_cast<char*>(16))
#ifdef HAVE___ATTRIBUTE__
# define ATTRIBUTE_WEAK __attribute__((weak))
# define ATTRIBUTE_NOINLINE __attribute__((noinline))
#else
# define ATTRIBUTE_WEAK
# define ATTRIBUTE_NOINLINE
#endif
// Section attributes are supported for both ELF and Mach-O, but in
// very different ways. Here's the API we provide:
// 1) ATTRIBUTE_SECTION: put this with the declaration of all functions
// you want to be in the same linker section
// 2) DEFINE_ATTRIBUTE_SECTION_VARS: must be called once per unique
// name. You want to make sure this is executed before any
// DECLARE_ATTRIBUTE_SECTION_VARS; the easiest way is to put them
// in the same .cc file. Put this call at the global level.
// 3) INIT_ATTRIBUTE_SECTION_VARS: you can scatter calls to this in
// multiple places to help ensure execution before any
// DECLARE_ATTRIBUTE_SECTION_VARS. You must have at least one
// DEFINE, but you can have many INITs. Put each in its own scope.
// 4) DECLARE_ATTRIBUTE_SECTION_VARS: must be called before using
// ATTRIBUTE_SECTION_START or ATTRIBUTE_SECTION_STOP on a name.
// Put this call at the global level.
// 5) ATTRIBUTE_SECTION_START/ATTRIBUTE_SECTION_STOP: call this to say
// where in memory a given section is. All functions declared with
// ATTRIBUTE_SECTION are guaranteed to be between START and STOP.
#if defined(HAVE___ATTRIBUTE__) && defined(__ELF__)
# define ATTRIBUTE_SECTION(name) __attribute__ ((section (#name)))
// Weak section declaration to be used as a global declaration
// for ATTRIBUTE_SECTION_START|STOP(name) to compile and link
// even without functions with ATTRIBUTE_SECTION(name).
# define DECLARE_ATTRIBUTE_SECTION_VARS(name) \
extern char __start_##name[] ATTRIBUTE_WEAK; \
extern char __stop_##name[] ATTRIBUTE_WEAK
# define INIT_ATTRIBUTE_SECTION_VARS(name) // no-op for ELF
# define DEFINE_ATTRIBUTE_SECTION_VARS(name) // no-op for ELF
// Return void* pointers to start/end of a section of code with functions
// having ATTRIBUTE_SECTION(name), or 0 if no such function exists.
// One must DECLARE_ATTRIBUTE_SECTION(name) for this to compile and link.
# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(__start_##name))
# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(__stop_##name))
# define HAVE_ATTRIBUTE_SECTION_START 1
#elif defined(HAVE___ATTRIBUTE__) && defined(__MACH__)
# define ATTRIBUTE_SECTION(name) __attribute__ ((section ("__TEXT, " #name)))
#include <mach-o/getsect.h>
#include <mach-o/dyld.h>
class AssignAttributeStartEnd {
public:
AssignAttributeStartEnd(const char* name, char** pstart, char** pend) {
// Find out what dynamic library name is defined in
if (_dyld_present()) {
for (int i = _dyld_image_count() - 1; i >= 0; --i) {
const mach_header* hdr = _dyld_get_image_header(i);
#ifdef MH_MAGIC_64
if (hdr->magic == MH_MAGIC_64) {
uint64_t len;
*pstart = getsectdatafromheader_64((mach_header_64*)hdr,
"__TEXT", name, &len);
if (*pstart) { // NULL if not defined in this dynamic library
*pstart += _dyld_get_image_vmaddr_slide(i); // correct for reloc
*pend = *pstart + len;
return;
}
}
#endif
if (hdr->magic == MH_MAGIC) {
uint32_t len;
*pstart = getsectdatafromheader(hdr, "__TEXT", name, &len);
if (*pstart) { // NULL if not defined in this dynamic library
*pstart += _dyld_get_image_vmaddr_slide(i); // correct for reloc
*pend = *pstart + len;
return;
}
}
}
}
// If we get here, not defined in a dll at all. See if defined statically.
unsigned long len; // don't ask me why this type isn't uint32_t too...
*pstart = getsectdata("__TEXT", name, &len);
*pend = *pstart + len;
}
};
#define DECLARE_ATTRIBUTE_SECTION_VARS(name) \
extern char* __start_##name; \
extern char* __stop_##name
#define INIT_ATTRIBUTE_SECTION_VARS(name) \
DECLARE_ATTRIBUTE_SECTION_VARS(name); \
static const AssignAttributeStartEnd __assign_##name( \
#name, &__start_##name, &__stop_##name)
#define DEFINE_ATTRIBUTE_SECTION_VARS(name) \
char* __start_##name, *__stop_##name; \
INIT_ATTRIBUTE_SECTION_VARS(name)
# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(__start_##name))
# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(__stop_##name))
# define HAVE_ATTRIBUTE_SECTION_START 1
#else // not HAVE___ATTRIBUTE__ && __ELF__, nor HAVE___ATTRIBUTE__ && __MACH__
# define ATTRIBUTE_SECTION(name)
# define DECLARE_ATTRIBUTE_SECTION_VARS(name)
# define INIT_ATTRIBUTE_SECTION_VARS(name)
# define DEFINE_ATTRIBUTE_SECTION_VARS(name)
# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(0))
# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(0))
#endif // HAVE___ATTRIBUTE__ and __ELF__ or __MACH__
#if defined(HAVE___ATTRIBUTE__) && (defined(__i386__) || defined(__x86_64__))
# define CACHELINE_SIZE 64
# define CACHELINE_ALIGNED __attribute__((aligned(CACHELINE_SIZE)))
#else
# define CACHELINE_ALIGNED
#endif // defined(HAVE___ATTRIBUTE__) && (__i386__ || __x86_64__)
// The following enum should be used only as a constructor argument to indicate
// that the variable has static storage class, and that the constructor should
// do nothing to its state. It indicates to the reader that it is legal to
// declare a static nistance of the class, provided the constructor is given
// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a
// static variable that has a constructor or a destructor because invocation
// order is undefined. However, IF the type can be initialized by filling with
// zeroes (which the loader does for static variables), AND the destructor also
// does nothing to the storage, then a constructor declared as
// explicit MyClass(base::LinkerInitialized x) {}
// and invoked as
// static MyClass my_variable_name(base::LINKER_INITIALIZED);
namespace base {
enum LinkerInitialized { LINKER_INITIALIZED };
}
#endif // _BASICTYPES_H_

View File

@ -0,0 +1,612 @@
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
#include <stdlib.h> // for getenv()
#include <stdio.h> // for snprintf(), sscanf()
#include <string.h> // for memmove(), memchr(), etc.
#include <fcntl.h> // for open()
#include <errno.h> // for errno
#include <unistd.h> // for read()
#if defined __MACH__ // Mac OS X, almost certainly
#include <mach-o/dyld.h> // for iterating over dll's in ProcMapsIter
#include <mach-o/loader.h> // for iterating over dll's in ProcMapsIter
#include <sys/types.h>
#include <sys/sysctl.h> // how we figure out numcpu's on OS X
#elif defined __FreeBSD__
#include <sys/sysctl.h>
#elif defined __sun__ // Solaris
#include <procfs.h> // for, e.g., prmap_t
#elif defined(PLATFORM_WINDOWS)
#include <process.h> // for getpid() (actually, _getpid())
#include <shlwapi.h> // for SHGetValueA()
#include <tlhelp32.h> // for Module32First()
#endif
#include "sysinfo.h"
#ifdef PLATFORM_WINDOWS
#ifdef MODULEENTRY32
// In a change from the usual W-A pattern, there is no A variant of
// MODULEENTRY32. Tlhelp32.h #defines the W variant, but not the A.
// In unicode mode, tlhelp32.h #defines MODULEENTRY32 to be
// MODULEENTRY32W. These #undefs are the only way I see to get back
// access to the original, ascii struct (and related functions).
#undef MODULEENTRY32
#undef Module32First
#undef Module32Next
#undef PMODULEENTRY32
#undef LPMODULEENTRY32
#endif /* MODULEENTRY32 */
// MinGW doesn't seem to define this, perhaps some windowsen don't either.
#ifndef TH32CS_SNAPMODULE32
#define TH32CS_SNAPMODULE32 0
#endif /* TH32CS_SNAPMODULE32 */
#endif /* PLATFORM_WINDOWS */
// Re-run fn until it doesn't cause EINTR.
#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
// open/read/close can set errno, which may be illegal at this
// time, so prefer making the syscalls directly if we can.
#ifdef HAVE_SYS_SYSCALL_H
# include <sys/syscall.h>
# define safeopen(filename, mode) syscall(SYS_open, filename, mode)
# define saferead(fd, buffer, size) syscall(SYS_read, fd, buffer, size)
# define safeclose(fd) syscall(SYS_close, fd)
#else
# define safeopen(filename, mode) open(filename, mode)
# define saferead(fd, buffer, size) read(fd, buffer, size)
# define safeclose(fd) close(fd)
#endif
// ----------------------------------------------------------------------
// HasPosixThreads()
// Return true if we're running POSIX (e.g., NPTL on Linux)
// threads, as opposed to a non-POSIX thread libary. The thing
// that we care about is whether a thread's pid is the same as
// the thread that spawned it. If so, this function returns
// true.
// ----------------------------------------------------------------------
bool HasPosixThreads() {
#if defined(__linux__) and !defined(ANDROID)
#ifndef _CS_GNU_LIBPTHREAD_VERSION
#define _CS_GNU_LIBPTHREAD_VERSION 3
#endif
char buf[32];
// We assume that, if confstr() doesn't know about this name, then
// the same glibc is providing LinuxThreads.
if (confstr(_CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf)) == 0)
return false;
return strncmp(buf, "NPTL", 4) == 0;
#elif defined(PLATFORM_WINDOWS) || defined(__CYGWIN__) || defined(__CYGWIN32__)
return false;
#else // other OS
return true; // Assume that everything else has Posix
#endif // else OS_LINUX
}
// ----------------------------------------------------------------------
#define CHECK_LT(x, y) do { assert((x) < (y)); } while (0)
#if defined __linux__ || defined __FreeBSD__ || defined __sun__ || defined __CYGWIN__ || defined __CYGWIN32__
static void ConstructFilename(const char* spec, pid_t pid,
char* buf, int buf_size) {
CHECK_LT(snprintf(buf, buf_size,
spec,
static_cast<int>(pid ? pid : getpid())), buf_size);
}
#endif
// A templatized helper function instantiated for Mach (OS X) only.
// It can handle finding info for both 32 bits and 64 bits.
// Returns true if it successfully handled the hdr, false else.
#ifdef __MACH__ // Mac OS X, almost certainly
template<uint32_t kMagic, uint32_t kLCSegment,
typename MachHeader, typename SegmentCommand>
static bool NextExtMachHelper(const mach_header* hdr,
int current_image, int current_load_cmd,
uint64 *start, uint64 *end, char **flags,
uint64 *offset, int64 *inode, char **filename,
uint64 *file_mapping, uint64 *file_pages,
uint64 *anon_mapping, uint64 *anon_pages,
dev_t *dev) {
static char kDefaultPerms[5] = "r-xp";
if (hdr->magic != kMagic)
return false;
const char* lc = (const char *)hdr + sizeof(MachHeader);
// TODO(csilvers): make this not-quadradic (increment and hold state)
for (int j = 0; j < current_load_cmd; j++) // advance to *our* load_cmd
lc += ((const load_command *)lc)->cmdsize;
if (((const load_command *)lc)->cmd == kLCSegment) {
const intptr_t dlloff = _dyld_get_image_vmaddr_slide(current_image);
const SegmentCommand* sc = (const SegmentCommand *)lc;
if (start) *start = sc->vmaddr + dlloff;
if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
if (flags) *flags = kDefaultPerms; // can we do better?
if (offset) *offset = sc->fileoff;
if (inode) *inode = 0;
if (filename)
*filename = const_cast<char*>(_dyld_get_image_name(current_image));
if (file_mapping) *file_mapping = 0;
if (file_pages) *file_pages = 0; // could we use sc->filesize?
if (anon_mapping) *anon_mapping = 0;
if (anon_pages) *anon_pages = 0;
if (dev) *dev = 0;
return true;
}
return false;
}
#endif
ProcMapsIterator::ProcMapsIterator(pid_t pid) {
Init(pid, NULL, false);
}
ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer) {
Init(pid, buffer, false);
}
ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer,
bool use_maps_backing) {
Init(pid, buffer, use_maps_backing);
}
void ProcMapsIterator::Init(pid_t pid, Buffer *buffer,
bool use_maps_backing) {
pid_ = pid;
using_maps_backing_ = use_maps_backing;
dynamic_buffer_ = NULL;
if (!buffer) {
// If the user didn't pass in any buffer storage, allocate it
// now. This is the normal case; the signal handler passes in a
// static buffer.
buffer = dynamic_buffer_ = new Buffer;
} else {
dynamic_buffer_ = NULL;
}
ibuf_ = buffer->buf_;
stext_ = etext_ = nextline_ = ibuf_;
ebuf_ = ibuf_ + Buffer::kBufSize - 1;
nextline_ = ibuf_;
#if defined(__linux__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
if (use_maps_backing) { // don't bother with clever "self" stuff in this case
ConstructFilename("/proc/%d/maps_backing", pid, ibuf_, Buffer::kBufSize);
} else if (pid == 0) {
// We have to kludge a bit to deal with the args ConstructFilename
// expects. The 1 is never used -- it's only impt. that it's not 0.
ConstructFilename("/proc/self/maps", 1, ibuf_, Buffer::kBufSize);
} else {
ConstructFilename("/proc/%d/maps", pid, ibuf_, Buffer::kBufSize);
}
// No error logging since this can be called from the crash dump
// handler at awkward moments. Users should call Valid() before
// using.
NO_INTR(fd_ = open(ibuf_, O_RDONLY));
#elif defined(__FreeBSD__)
// We don't support maps_backing on freebsd
if (pid == 0) {
ConstructFilename("/proc/curproc/map", 1, ibuf_, Buffer::kBufSize);
} else {
ConstructFilename("/proc/%d/map", pid, ibuf_, Buffer::kBufSize);
}
NO_INTR(fd_ = open(ibuf_, O_RDONLY));
#elif defined(__sun__)
if (pid == 0) {
ConstructFilename("/proc/self/map", 1, ibuf_, Buffer::kBufSize);
} else {
ConstructFilename("/proc/%d/map", pid, ibuf_, Buffer::kBufSize);
}
NO_INTR(fd_ = open(ibuf_, O_RDONLY));
#elif defined(__MACH__)
current_image_ = _dyld_image_count(); // count down from the top
current_load_cmd_ = -1;
#elif defined(PLATFORM_WINDOWS)
snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE |
TH32CS_SNAPMODULE32,
GetCurrentProcessId());
memset(&module_, 0, sizeof(module_));
#else
fd_ = -1; // so Valid() is always false
#endif
}
ProcMapsIterator::~ProcMapsIterator() {
#if defined(PLATFORM_WINDOWS)
if (snapshot_ != INVALID_HANDLE_VALUE) CloseHandle(snapshot_);
#elif defined(__MACH__)
// no cleanup necessary!
#else
if (fd_ >= 0) NO_INTR(close(fd_));
#endif
delete dynamic_buffer_;
}
bool ProcMapsIterator::Valid() const {
#if defined(PLATFORM_WINDOWS)
return snapshot_ != INVALID_HANDLE_VALUE;
#elif defined(__MACH__)
return 1;
#else
return fd_ != -1;
#endif
}
bool ProcMapsIterator::Next(uint64 *start, uint64 *end, char **flags,
uint64 *offset, int64 *inode, char **filename) {
return NextExt(start, end, flags, offset, inode, filename, NULL, NULL,
NULL, NULL, NULL);
}
// This has too many arguments. It should really be building
// a map object and returning it. The problem is that this is called
// when the memory allocator state is undefined, hence the arguments.
bool ProcMapsIterator::NextExt(uint64 *start, uint64 *end, char **flags,
uint64 *offset, int64 *inode, char **filename,
uint64 *file_mapping, uint64 *file_pages,
uint64 *anon_mapping, uint64 *anon_pages,
dev_t *dev) {
#if defined(__linux__) || defined(__FreeBSD__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
do {
// Advance to the start of the next line
stext_ = nextline_;
// See if we have a complete line in the buffer already
nextline_ = static_cast<char *>(memchr (stext_, '\n', etext_ - stext_));
if (!nextline_) {
// Shift/fill the buffer so we do have a line
int count = etext_ - stext_;
// Move the current text to the start of the buffer
memmove(ibuf_, stext_, count);
stext_ = ibuf_;
etext_ = ibuf_ + count;
int nread = 0; // fill up buffer with text
while (etext_ < ebuf_) {
NO_INTR(nread = read(fd_, etext_, ebuf_ - etext_));
if (nread > 0)
etext_ += nread;
else
break;
}
// Zero out remaining characters in buffer at EOF to avoid returning
// garbage from subsequent calls.
if (etext_ != ebuf_ && nread == 0) {
memset(etext_, 0, ebuf_ - etext_);
}
*etext_ = '\n'; // sentinel; safe because ibuf extends 1 char beyond ebuf
nextline_ = static_cast<char *>(memchr (stext_, '\n', etext_ + 1 - stext_));
}
*nextline_ = 0; // turn newline into nul
nextline_ += ((nextline_ < etext_)? 1 : 0); // skip nul if not end of text
// stext_ now points at a nul-terminated line
uint64 tmpstart, tmpend, tmpoffset;
int64 tmpinode;
int major, minor;
unsigned filename_offset = 0;
#if defined(__linux__)
// for now, assume all linuxes have the same format
if (sscanf(stext_, "%"SCNx64"-%"SCNx64" %4s %"SCNx64" %x:%x %"SCNd64" %n",
start ? start : &tmpstart,
end ? end : &tmpend,
flags_,
offset ? offset : &tmpoffset,
&major, &minor,
inode ? inode : &tmpinode, &filename_offset) != 7) continue;
#elif defined(__CYGWIN__) || defined(__CYGWIN32__)
// cygwin is like linux, except the third field is the "entry point"
// rather than the offset (see format_process_maps at
// http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/fhandler_process.cc?rev=1.89&content-type=text/x-cvsweb-markup&cvsroot=src
// Offset is always be 0 on cygwin: cygwin implements an mmap
// by loading the whole file and then calling NtMapViewOfSection.
// Cygwin also seems to set its flags kinda randomly; use windows default.
char tmpflags[5];
if (offset)
*offset = 0;
strcpy(flags_, "r-xp");
if (sscanf(stext_, "%llx-%llx %4s %llx %x:%x %lld %n",
start ? start : &tmpstart,
end ? end : &tmpend,
tmpflags,
&tmpoffset,
&major, &minor,
inode ? inode : &tmpinode, &filename_offset) != 7) continue;
#elif defined(__FreeBSD__)
// For the format, see http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/fs/procfs/procfs_map.c?rev=1.31&content-type=text/x-cvsweb-markup
tmpstart = tmpend = tmpoffset = 0;
tmpinode = 0;
major = minor = 0; // can't get this info in freebsd
if (inode)
*inode = 0; // nor this
if (offset)
*offset = 0; // seems like this should be in there, but maybe not
// start end resident privateresident obj(?) prot refcnt shadowcnt
// flags copy_on_write needs_copy type filename:
// 0x8048000 0x804a000 2 0 0xc104ce70 r-x 1 0 0x0 COW NC vnode /bin/cat
if (sscanf(stext_, "0x%"SCNx64" 0x%"SCNx64" %*d %*d %*p %3s %*d %*d 0x%*x %*s %*s %*s %n",
start ? start : &tmpstart,
end ? end : &tmpend,
flags_,
&filename_offset) != 3) continue;
#endif
// Depending on the Linux kernel being used, there may or may not be a space
// after the inode if there is no filename. sscanf will in such situations
// nondeterministically either fill in filename_offset or not (the results
// differ on multiple calls in the same run even with identical arguments).
// We don't want to wander off somewhere beyond the end of the string.
size_t stext_length = strlen(stext_);
if (filename_offset == 0 || filename_offset > stext_length)
filename_offset = stext_length;
// We found an entry
if (flags) *flags = flags_;
if (filename) *filename = stext_ + filename_offset;
if (dev) *dev = minor | (major << 8);
if (using_maps_backing_) {
// Extract and parse physical page backing info.
char *backing_ptr = stext_ + filename_offset +
strlen(stext_+filename_offset);
// find the second '('
int paren_count = 0;
while (--backing_ptr > stext_) {
if (*backing_ptr == '(') {
++paren_count;
if (paren_count >= 2) {
uint64 tmp_file_mapping;
uint64 tmp_file_pages;
uint64 tmp_anon_mapping;
uint64 tmp_anon_pages;
sscanf(backing_ptr+1, "F %"SCNx64" %"SCNd64") (A %"SCNx64" %"SCNd64")",
file_mapping ? file_mapping : &tmp_file_mapping,
file_pages ? file_pages : &tmp_file_pages,
anon_mapping ? anon_mapping : &tmp_anon_mapping,
anon_pages ? anon_pages : &tmp_anon_pages);
// null terminate the file name (there is a space
// before the first (.
backing_ptr[-1] = 0;
break;
}
}
}
}
return true;
} while (etext_ > ibuf_);
#elif defined(__sun__)
// This is based on MA_READ == 4, MA_WRITE == 2, MA_EXEC == 1
static char kPerms[8][4] = { "---", "--x", "-w-", "-wx",
"r--", "r-x", "rw-", "rwx" };
COMPILE_ASSERT(MA_READ == 4, solaris_ma_read_must_equal_4);
COMPILE_ASSERT(MA_WRITE == 2, solaris_ma_write_must_equal_2);
COMPILE_ASSERT(MA_EXEC == 1, solaris_ma_exec_must_equal_1);
Buffer object_path;
int nread = 0; // fill up buffer with text
NO_INTR(nread = read(fd_, ibuf_, sizeof(prmap_t)));
if (nread == sizeof(prmap_t)) {
long inode_from_mapname = 0;
prmap_t* mapinfo = reinterpret_cast<prmap_t*>(ibuf_);
// Best-effort attempt to get the inode from the filename. I think the
// two middle ints are major and minor device numbers, but I'm not sure.
sscanf(mapinfo->pr_mapname, "ufs.%*d.%*d.%ld", &inode_from_mapname);
if (pid_ == 0) {
CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize,
"/proc/self/path/%s", mapinfo->pr_mapname),
Buffer::kBufSize);
} else {
CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize,
"/proc/%d/path/%s",
static_cast<int>(pid_), mapinfo->pr_mapname),
Buffer::kBufSize);
}
ssize_t len = readlink(object_path.buf_, current_filename_, PATH_MAX);
CHECK_LT(len, PATH_MAX);
if (len < 0)
len = 0;
current_filename_[len] = '\0';
if (start) *start = mapinfo->pr_vaddr;
if (end) *end = mapinfo->pr_vaddr + mapinfo->pr_size;
if (flags) *flags = kPerms[mapinfo->pr_mflags & 7];
if (offset) *offset = mapinfo->pr_offset;
if (inode) *inode = inode_from_mapname;
if (filename) *filename = current_filename_;
if (file_mapping) *file_mapping = 0;
if (file_pages) *file_pages = 0;
if (anon_mapping) *anon_mapping = 0;
if (anon_pages) *anon_pages = 0;
if (dev) *dev = 0;
return true;
}
#elif defined(__MACH__)
// We return a separate entry for each segment in the DLL. (TODO(csilvers):
// can we do better?) A DLL ("image") has load-commands, some of which
// talk about segment boundaries.
// cf image_for_address from http://svn.digium.com/view/asterisk/team/oej/minivoicemail/dlfcn.c?revision=53912
for (; current_image_ >= 0; current_image_--) {
const mach_header* hdr = _dyld_get_image_header(current_image_);
if (!hdr) continue;
if (current_load_cmd_ < 0) // set up for this image
current_load_cmd_ = hdr->ncmds; // again, go from the top down
// We start with the next load command (we've already looked at this one).
for (current_load_cmd_--; current_load_cmd_ >= 0; current_load_cmd_--) {
#ifdef MH_MAGIC_64
if (NextExtMachHelper<MH_MAGIC_64, LC_SEGMENT_64,
struct mach_header_64, struct segment_command_64>(
hdr, current_image_, current_load_cmd_,
start, end, flags, offset, inode, filename,
file_mapping, file_pages, anon_mapping,
anon_pages, dev)) {
return true;
}
#endif
if (NextExtMachHelper<MH_MAGIC, LC_SEGMENT,
struct mach_header, struct segment_command>(
hdr, current_image_, current_load_cmd_,
start, end, flags, offset, inode, filename,
file_mapping, file_pages, anon_mapping,
anon_pages, dev)) {
return true;
}
}
// If we get here, no more load_cmd's in this image talk about
// segments. Go on to the next image.
}
#elif defined(PLATFORM_WINDOWS)
static char kDefaultPerms[5] = "r-xp";
BOOL ok;
if (module_.dwSize == 0) { // only possible before first call
module_.dwSize = sizeof(module_);
ok = Module32First(snapshot_, &module_);
} else {
ok = Module32Next(snapshot_, &module_);
}
if (ok) {
uint64 base_addr = reinterpret_cast<DWORD_PTR>(module_.modBaseAddr);
if (start) *start = base_addr;
if (end) *end = base_addr + module_.modBaseSize;
if (flags) *flags = kDefaultPerms;
if (offset) *offset = 0;
if (inode) *inode = 0;
if (filename) *filename = module_.szExePath;
if (file_mapping) *file_mapping = 0;
if (file_pages) *file_pages = 0;
if (anon_mapping) *anon_mapping = 0;
if (anon_pages) *anon_pages = 0;
if (dev) *dev = 0;
return true;
}
#endif
// We didn't find anything
return false;
}
int ProcMapsIterator::FormatLine(char* buffer, int bufsize,
uint64 start, uint64 end, const char *flags,
uint64 offset, int64 inode,
const char *filename, dev_t dev) {
// We assume 'flags' looks like 'rwxp' or 'rwx'.
char r = (flags && flags[0] == 'r') ? 'r' : '-';
char w = (flags && flags[0] && flags[1] == 'w') ? 'w' : '-';
char x = (flags && flags[0] && flags[1] && flags[2] == 'x') ? 'x' : '-';
// p always seems set on linux, so we set the default to 'p', not '-'
char p = (flags && flags[0] && flags[1] && flags[2] && flags[3] != 'p')
? '-' : 'p';
const int rc = snprintf(buffer, bufsize,
"%08"PRIx64"-%08"PRIx64" %c%c%c%c %08"PRIx64" %02x:%02x %-11"PRId64" %s\n",
start, end, r,w,x,p, offset,
static_cast<int>(dev/256), static_cast<int>(dev%256),
inode, filename);
return (rc < 0 || rc >= bufsize) ? 0 : rc;
}
// Helper to add the list of mapped shared libraries to a profile.
// Fill formatted "/proc/self/maps" contents into buffer 'buf' of size 'size'
// and return the actual size occupied in 'buf'. We fill wrote_all to true
// if we successfully wrote all proc lines to buf, false else.
// We do not provision for 0-terminating 'buf'.
int FillProcSelfMaps(char buf[], int size, bool* wrote_all) {
ProcMapsIterator::Buffer iterbuf;
ProcMapsIterator it(0, &iterbuf); // 0 means "current pid"
uint64 start, end, offset;
int64 inode;
char *flags, *filename;
int bytes_written = 0;
*wrote_all = true;
while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
const int line_length = it.FormatLine(buf + bytes_written,
size - bytes_written,
start, end, flags, offset,
inode, filename, 0);
if (line_length == 0)
*wrote_all = false; // failed to write this line out
else
bytes_written += line_length;
}
return bytes_written;
}
// Dump the same data as FillProcSelfMaps reads to fd.
// It seems easier to repeat parts of FillProcSelfMaps here than to
// reuse it via a call.
void DumpProcSelfMaps(RawFD fd) {
ProcMapsIterator::Buffer iterbuf;
ProcMapsIterator it(0, &iterbuf); // 0 means "current pid"
uint64 start, end, offset;
int64 inode;
char *flags, *filename;
ProcMapsIterator::Buffer linebuf;
while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
int written = it.FormatLine(linebuf.buf_, sizeof(linebuf.buf_),
start, end, flags, offset, inode, filename,
0);
RawWrite(fd, linebuf.buf_, written);
}
}
// Re-run fn until it doesn't cause EINTR.
#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
RawFD RawOpenForWriting(const char* filename) {
return open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664);
}
void RawWrite(RawFD fd, const char* buf, size_t len) {
while (len > 0) {
ssize_t r;
NO_INTR(r = write(fd, buf, len));
if (r <= 0) break;
buf += r;
len -= r;
}
}
void RawClose(RawFD fd) {
NO_INTR(close(fd));
}

View File

@ -0,0 +1,234 @@
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// All functions here are thread-hostile due to file caching unless
// commented otherwise.
#ifndef _SYSINFO_H_
#define _SYSINFO_H_
#include <time.h>
#if (defined(_WIN32) || defined(__MINGW32__)) && (!defined(__CYGWIN__) && !defined(__CYGWIN32__))
#include <windows.h> // for DWORD
#include <TlHelp32.h> // for CreateToolhelp32Snapshot
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for pid_t
#endif
#include <stddef.h> // for size_t
#include <limits.h> // for PATH_MAX
#include "basictypes.h"
// This getenv function is safe to call before the C runtime is initialized.
// On Windows, it utilizes GetEnvironmentVariable() and on unix it uses
// /proc/self/environ instead calling getenv(). It's intended to be used in
// routines that run before main(), when the state required for getenv() may
// not be set up yet. In particular, errno isn't set up until relatively late
// (after the pthreads library has a chance to make it threadsafe), and
// getenv() doesn't work until then.
// On some platforms, this call will utilize the same, static buffer for
// repeated GetenvBeforeMain() calls. Callers should not expect pointers from
// this routine to be long lived.
// Note that on unix, /proc only has the environment at the time the
// application was started, so this routine ignores setenv() calls/etc. Also
// note it only reads the first 16K of the environment.
extern const char* GetenvBeforeMain(const char* name);
// This takes as an argument an environment-variable name (like
// CPUPROFILE) whose value is supposed to be a file-path, and sets
// path to that path, and returns true. Non-trivial for surprising
// reasons, as documented in sysinfo.cc. path must have space PATH_MAX.
extern bool GetUniquePathFromEnv(const char* env_name, char* path);
extern int NumCPUs();
// processor cycles per second of each processor. Thread-safe.
extern double CyclesPerSecond(void);
// Return true if we're running POSIX (e.g., NPTL on Linux) threads,
// as opposed to a non-POSIX thread libary. The thing that we care
// about is whether a thread's pid is the same as the thread that
// spawned it. If so, this function returns true.
// Thread-safe.
// Note: We consider false negatives to be OK.
bool HasPosixThreads();
#ifndef SWIG // SWIG doesn't like struct Buffer and variable arguments.
// A ProcMapsIterator abstracts access to /proc/maps for a given
// process. Needs to be stack-allocatable and avoid using stdio/malloc
// so it can be used in the google stack dumper, heap-profiler, etc.
//
// On Windows and Mac OS X, this iterator iterates *only* over DLLs
// mapped into this process space. For Linux, FreeBSD, and Solaris,
// it iterates over *all* mapped memory regions, including anonymous
// mmaps. For other O/Ss, it is unlikely to work at all, and Valid()
// will always return false. Also note: this routine only works on
// FreeBSD if procfs is mounted: make sure this is in your /etc/fstab:
// proc /proc procfs rw 0 0
class ProcMapsIterator {
public:
struct Buffer {
#ifdef __FreeBSD__
// FreeBSD requires us to read all of the maps file at once, so
// we have to make a buffer that's "always" big enough
static const size_t kBufSize = 102400;
#else // a one-line buffer is good enough
static const size_t kBufSize = PATH_MAX + 1024;
#endif
char buf_[kBufSize];
};
// Create a new iterator for the specified pid. pid can be 0 for "self".
explicit ProcMapsIterator(pid_t pid);
// Create an iterator with specified storage (for use in signal
// handler). "buffer" should point to a ProcMapsIterator::Buffer
// buffer can be NULL in which case a bufer will be allocated.
ProcMapsIterator(pid_t pid, Buffer *buffer);
// Iterate through maps_backing instead of maps if use_maps_backing
// is true. Otherwise the same as above. buffer can be NULL and
// it will allocate a buffer itself.
ProcMapsIterator(pid_t pid, Buffer *buffer,
bool use_maps_backing);
// Returns true if the iterator successfully initialized;
bool Valid() const;
// Returns a pointer to the most recently parsed line. Only valid
// after Next() returns true, and until the iterator is destroyed or
// Next() is called again. This may give strange results on non-Linux
// systems. Prefer FormatLine() if that may be a concern.
const char *CurrentLine() const { return stext_; }
// Writes the "canonical" form of the /proc/xxx/maps info for a single
// line to the passed-in buffer. Returns the number of bytes written,
// or 0 if it was not able to write the complete line. (To guarantee
// success, buffer should have size at least Buffer::kBufSize.)
// Takes as arguments values set via a call to Next(). The
// "canonical" form of the line (taken from linux's /proc/xxx/maps):
// <start_addr(hex)>-<end_addr(hex)> <perms(rwxp)> <offset(hex)> +
// <major_dev(hex)>:<minor_dev(hex)> <inode> <filename> Note: the
// eg
// 08048000-0804c000 r-xp 00000000 03:01 3793678 /bin/cat
// If you don't have the dev_t (dev), feel free to pass in 0.
// (Next() doesn't return a dev_t, though NextExt does.)
//
// Note: if filename and flags were obtained via a call to Next(),
// then the output of this function is only valid if Next() returned
// true, and only until the iterator is destroyed or Next() is
// called again. (Since filename, at least, points into CurrentLine.)
static int FormatLine(char* buffer, int bufsize,
uint64 start, uint64 end, const char *flags,
uint64 offset, int64 inode, const char *filename,
dev_t dev);
// Find the next entry in /proc/maps; return true if found or false
// if at the end of the file.
//
// Any of the result pointers can be NULL if you're not interested
// in those values.
//
// If "flags" and "filename" are passed, they end up pointing to
// storage within the ProcMapsIterator that is valid only until the
// iterator is destroyed or Next() is called again. The caller may
// modify the contents of these strings (up as far as the first NUL,
// and only until the subsequent call to Next()) if desired.
// The offsets are all uint64 in order to handle the case of a
// 32-bit process running on a 64-bit kernel
//
// IMPORTANT NOTE: see top-of-class notes for details about what
// mapped regions Next() iterates over, depending on O/S.
// TODO(csilvers): make flags and filename const.
bool Next(uint64 *start, uint64 *end, char **flags,
uint64 *offset, int64 *inode, char **filename);
bool NextExt(uint64 *start, uint64 *end, char **flags,
uint64 *offset, int64 *inode, char **filename,
uint64 *file_mapping, uint64 *file_pages,
uint64 *anon_mapping, uint64 *anon_pages,
dev_t *dev);
~ProcMapsIterator();
private:
void Init(pid_t pid, Buffer *buffer, bool use_maps_backing);
char *ibuf_; // input buffer
char *stext_; // start of text
char *etext_; // end of text
char *nextline_; // start of next line
char *ebuf_; // end of buffer (1 char for a nul)
#if (defined(_WIN32) || defined(__MINGW32__)) && (!defined(__CYGWIN__) && !defined(__CYGWIN32__))
HANDLE snapshot_; // filehandle on dll info
// In a change from the usual W-A pattern, there is no A variant of
// MODULEENTRY32. Tlhelp32.h #defines the W variant, but not the A.
// We want the original A variants, and this #undef is the only
// way I see to get them. Redefining it when we're done prevents us
// from affecting other .cc files.
# ifdef MODULEENTRY32 // Alias of W
# undef MODULEENTRY32
MODULEENTRY32 module_; // info about current dll (and dll iterator)
# define MODULEENTRY32 MODULEENTRY32W
# else // It's the ascii, the one we want.
MODULEENTRY32 module_; // info about current dll (and dll iterator)
# endif
#elif defined(__MACH__)
int current_image_; // dll's are called "images" in macos parlance
int current_load_cmd_; // the segment of this dll we're examining
#elif defined(__sun__) // Solaris
int fd_;
char current_filename_[PATH_MAX];
#else
int fd_; // filehandle on /proc/*/maps
#endif
pid_t pid_;
char flags_[10];
Buffer* dynamic_buffer_; // dynamically-allocated Buffer
bool using_maps_backing_; // true if we are looking at maps_backing instead of maps.
};
#endif /* #ifndef SWIG */
// Helper routines
typedef int RawFD;
const RawFD kIllegalRawFD = -1; // what open returns if it fails
RawFD RawOpenForWriting(const char* filename); // uses default permissions
void RawWrite(RawFD fd, const char* buf, size_t len);
void RawClose(RawFD fd);
int FillProcSelfMaps(char buf[], int size, bool* wrote_all);
void DumpProcSelfMaps(RawFD fd);
#endif /* #ifndef _SYSINFO_H_ */

View File

@ -0,0 +1,77 @@
//===-- asan_benchmarks_test.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.
//
// Some benchmarks for the instrumented code.
//===----------------------------------------------------------------------===//
#include "asan_test_config.h"
#include "asan_test_utils.h"
template<class T>
__attribute__((noinline))
static void ManyAccessFunc(T *x, size_t n_elements, size_t n_iter) {
for (size_t iter = 0; iter < n_iter; iter++) {
break_optimization(0);
// hand unroll the loop to stress the reg alloc.
for (size_t i = 0; i <= n_elements - 16; i += 16) {
x[i + 0] = i;
x[i + 1] = i;
x[i + 2] = i;
x[i + 3] = i;
x[i + 4] = i;
x[i + 5] = i;
x[i + 6] = i;
x[i + 7] = i;
x[i + 8] = i;
x[i + 9] = i;
x[i + 10] = i;
x[i + 11] = i;
x[i + 12] = i;
x[i + 13] = i;
x[i + 14] = i;
x[i + 15] = i;
}
}
}
TEST(AddressSanitizer, ManyAccessBenchmark) {
size_t kLen = 1024;
int *int_array = new int[kLen];
ManyAccessFunc(int_array, kLen, 1 << 24);
delete [] int_array;
}
// access 7 char elements in a 7 byte array (i.e. on the border).
__attribute__((noinline))
static void BorderAccessFunc(char *x, size_t n_iter) {
for (size_t iter = 0; iter < n_iter; iter++) {
break_optimization(x);
x[0] = 0;
x[1] = 0;
x[2] = 0;
x[3] = 0;
x[4] = 0;
x[5] = 0;
x[6] = 0;
}
}
TEST(AddressSanitizer, BorderAccessBenchmark) {
char *char_7_array = new char[7];
BorderAccessFunc(char_7_array, 1 << 30);
delete [] char_7_array;
}
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,18 @@
//===-- asan_break_optimization.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.
//
//===----------------------------------------------------------------------===//
#include "asan_test_utils.h"
// Have this function in a separate file to avoid inlining.
// (Yes, we know about cross-file inlining, but let's assume we don't use it).
extern "C" void break_optimization(void *x) {
}

View File

@ -0,0 +1,24 @@
//===-- asan_globals_test.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.
//
// Some globals in a separate file.
//===----------------------------------------------------------------------===//
extern char glob5[5];
static char static10[10];
int GlobalsTest(int zero) {
static char func_static15[15];
glob5[zero] = 0;
static10[zero] = 0;
func_static15[zero] = 0;
return glob5[1] + func_static15[2];
}

View File

@ -0,0 +1,355 @@
//===-- asan_interface_test.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.
//
//===----------------------------------------------------------------------===//
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include "asan_test_config.h"
#include "asan_test_utils.h"
#include "asan_interface.h"
TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) {
EXPECT_EQ(1, __asan_get_estimated_allocated_size(0));
const size_t sizes[] = { 1, 30, 1<<30 };
for (size_t i = 0; i < 3; i++) {
EXPECT_EQ(sizes[i], __asan_get_estimated_allocated_size(sizes[i]));
}
}
static const char* kGetAllocatedSizeErrorMsg =
"__asan_get_allocated_size failed";
TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) {
const size_t kArraySize = 100;
char *array = Ident((char*)malloc(kArraySize));
int *int_ptr = Ident(new int);
// Allocated memory is owned by allocator. Allocated size should be
// equal to requested size.
EXPECT_EQ(true, __asan_get_ownership(array));
EXPECT_EQ(kArraySize, __asan_get_allocated_size(array));
EXPECT_EQ(true, __asan_get_ownership(int_ptr));
EXPECT_EQ(sizeof(int), __asan_get_allocated_size(int_ptr));
// We cannot call GetAllocatedSize from the memory we didn't map,
// and from the interior pointers (not returned by previous malloc).
void *wild_addr = (void*)0x1;
EXPECT_EQ(false, __asan_get_ownership(wild_addr));
EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg);
EXPECT_EQ(false, __asan_get_ownership(array + kArraySize / 2));
EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2),
kGetAllocatedSizeErrorMsg);
// NULL is a valid argument and is owned.
EXPECT_EQ(true, __asan_get_ownership(NULL));
EXPECT_EQ(0, __asan_get_allocated_size(NULL));
// When memory is freed, it's not owned, and call to GetAllocatedSize
// is forbidden.
free(array);
EXPECT_EQ(false, __asan_get_ownership(array));
EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg);
delete int_ptr;
}
TEST(AddressSanitizerInterface, EnableStatisticsTest) {
bool old_stats_value = __asan_enable_statistics(true);
EXPECT_EQ(true, __asan_enable_statistics(false));
EXPECT_EQ(false, __asan_enable_statistics(old_stats_value));
}
TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) {
size_t before_malloc, after_malloc, after_free;
char *array;
const size_t kMallocSize = 100;
bool old_stats_value = __asan_enable_statistics(true);
before_malloc = __asan_get_current_allocated_bytes();
array = Ident((char*)malloc(kMallocSize));
after_malloc = __asan_get_current_allocated_bytes();
EXPECT_EQ(before_malloc + kMallocSize, after_malloc);
free(array);
after_free = __asan_get_current_allocated_bytes();
EXPECT_EQ(before_malloc, after_free);
__asan_enable_statistics(false);
array = Ident((char*)malloc(kMallocSize));
after_malloc = __asan_get_current_allocated_bytes();
EXPECT_EQ(before_malloc, after_malloc);
free(array);
__asan_enable_statistics(old_stats_value);
}
static void DoDoubleFree() {
int *x = Ident(new int);
delete Ident(x);
delete Ident(x);
}
// This test is run in a separate process, so that large malloced
// chunk won't remain in the free lists after the test.
// Note: use ASSERT_* instead of EXPECT_* here.
static void RunGetHeapSizeTestAndDie() {
size_t old_heap_size, new_heap_size, heap_growth;
// We unlikely have have chunk of this size in free list.
static const size_t kLargeMallocSize = 1 << 29; // 512M
__asan_enable_statistics(true);
old_heap_size = __asan_get_heap_size();
fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
free(Ident(malloc(kLargeMallocSize)));
new_heap_size = __asan_get_heap_size();
heap_growth = new_heap_size - old_heap_size;
fprintf(stderr, "heap growth after first malloc: %zu\n", heap_growth);
ASSERT_GE(heap_growth, kLargeMallocSize);
ASSERT_LE(heap_growth, 2 * kLargeMallocSize);
// Now large chunk should fall into free list, and can be
// allocated without increasing heap size.
old_heap_size = new_heap_size;
free(Ident(malloc(kLargeMallocSize)));
heap_growth = __asan_get_heap_size() - old_heap_size;
fprintf(stderr, "heap growth after second malloc: %zu\n", heap_growth);
ASSERT_LT(heap_growth, kLargeMallocSize);
// Test passed. Now die with expected double-free.
DoDoubleFree();
}
TEST(AddressSanitizerInterface, GetHeapSizeTest) {
EXPECT_DEATH(RunGetHeapSizeTestAndDie(), "double-free");
}
// Note: use ASSERT_* instead of EXPECT_* here.
static void DoLargeMallocForGetFreeBytesTestAndDie() {
size_t old_free_bytes, new_free_bytes;
static const size_t kLargeMallocSize = 1 << 29; // 512M
__asan_enable_statistics(true);
// If we malloc and free a large memory chunk, it will not fall
// into quarantine and will be available for future requests.
old_free_bytes = __asan_get_free_bytes();
fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
fprintf(stderr, "free bytes before malloc: %zu\n", old_free_bytes);
free(Ident(malloc(kLargeMallocSize)));
new_free_bytes = __asan_get_free_bytes();
fprintf(stderr, "free bytes after malloc and free: %zu\n", new_free_bytes);
ASSERT_GE(new_free_bytes, old_free_bytes + kLargeMallocSize);
// Test passed.
DoDoubleFree();
}
TEST(AddressSanitizerInterface, GetFreeBytesTest) {
static const size_t kNumOfChunks = 100;
static const size_t kChunkSize = 100;
char *chunks[kNumOfChunks];
size_t i;
size_t old_free_bytes, new_free_bytes;
bool old_stats_value = __asan_enable_statistics(true);
// Allocate a small chunk. Now allocator probably has a lot of these
// chunks to fulfill future requests. So, future requests will decrease
// the number of free bytes.
chunks[0] = Ident((char*)malloc(kChunkSize));
old_free_bytes = __asan_get_free_bytes();
for (i = 1; i < kNumOfChunks; i++) {
chunks[i] = Ident((char*)malloc(kChunkSize));
new_free_bytes = __asan_get_free_bytes();
EXPECT_LT(new_free_bytes, old_free_bytes);
old_free_bytes = new_free_bytes;
}
// Deleting these chunks will move them to quarantine, number of free
// bytes won't increase.
for (i = 0; i < kNumOfChunks; i++) {
free(chunks[i]);
EXPECT_EQ(old_free_bytes, __asan_get_free_bytes());
}
EXPECT_DEATH(DoLargeMallocForGetFreeBytesTestAndDie(), "double-free");
__asan_enable_statistics(old_stats_value);
}
static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<20, 357};
static const size_t kManyThreadsIterations = 250;
static const size_t kManyThreadsNumThreads = 200;
void *ManyThreadsWithStatsWorker(void *arg) {
for (size_t iter = 0; iter < kManyThreadsIterations; iter++) {
for (size_t size_index = 0; size_index < 4; size_index++) {
free(Ident(malloc(kManyThreadsMallocSizes[size_index])));
}
}
return 0;
}
TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) {
size_t before_test, after_test, i;
pthread_t threads[kManyThreadsNumThreads];
bool old_stats_value = __asan_enable_statistics(true);
before_test = __asan_get_current_allocated_bytes();
for (i = 0; i < kManyThreadsNumThreads; i++) {
pthread_create(&threads[i], 0,
(void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i);
}
for (i = 0; i < kManyThreadsNumThreads; i++) {
pthread_join(threads[i], 0);
}
after_test = __asan_get_current_allocated_bytes();
// ASan stats also reflect memory usage of internal ASan RTL structs,
// so we can't check for equality here.
EXPECT_LT(after_test, before_test + (1UL<<20));
__asan_enable_statistics(old_stats_value);
}
TEST(AddressSanitizerInterface, ExitCode) {
int original_exit_code = __asan_set_error_exit_code(7);
EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), "");
EXPECT_EQ(7, __asan_set_error_exit_code(8));
EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), "");
EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code));
EXPECT_EXIT(DoDoubleFree(),
::testing::ExitedWithCode(original_exit_code), "");
}
static const char* kUseAfterPoisonErrorMessage = "use-after-poison";
#define ACCESS(ptr, offset) Ident(*(ptr + offset))
#define DIE_ON_ACCESS(ptr, offset) \
EXPECT_DEATH(Ident(*(ptr + offset)), kUseAfterPoisonErrorMessage)
TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) {
char *array = Ident((char*)malloc(120));
// poison array[40..80)
ASAN_POISON_MEMORY_REGION(array + 40, 40);
ACCESS(array, 39);
ACCESS(array, 80);
DIE_ON_ACCESS(array, 40);
DIE_ON_ACCESS(array, 60);
DIE_ON_ACCESS(array, 79);
ASAN_UNPOISON_MEMORY_REGION(array + 40, 40);
// access previously poisoned memory.
ACCESS(array, 40);
ACCESS(array, 79);
free(array);
}
TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) {
char *array = Ident((char*)malloc(120));
// Poison [0..40) and [80..120)
ASAN_POISON_MEMORY_REGION(array, 40);
ASAN_POISON_MEMORY_REGION(array + 80, 40);
DIE_ON_ACCESS(array, 20);
ACCESS(array, 60);
DIE_ON_ACCESS(array, 100);
// Poison whole array - [0..120)
ASAN_POISON_MEMORY_REGION(array, 120);
DIE_ON_ACCESS(array, 60);
// Unpoison [24..96)
ASAN_UNPOISON_MEMORY_REGION(array + 24, 72);
DIE_ON_ACCESS(array, 23);
ACCESS(array, 24);
ACCESS(array, 60);
ACCESS(array, 95);
DIE_ON_ACCESS(array, 96);
free(array);
}
TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) {
// Vector of capacity 20
char *vec = Ident((char*)malloc(20));
ASAN_POISON_MEMORY_REGION(vec, 20);
for (size_t i = 0; i < 7; i++) {
// Simulate push_back.
ASAN_UNPOISON_MEMORY_REGION(vec + i, 1);
ACCESS(vec, i);
DIE_ON_ACCESS(vec, i + 1);
}
for (size_t i = 7; i > 0; i--) {
// Simulate pop_back.
ASAN_POISON_MEMORY_REGION(vec + i - 1, 1);
DIE_ON_ACCESS(vec, i - 1);
if (i > 1) ACCESS(vec, i - 2);
}
free(vec);
}
// Make sure that each aligned block of size "2^granularity" doesn't have
// "true" value before "false" value.
static void MakeShadowValid(bool *shadow, int length, int granularity) {
bool can_be_poisoned = true;
for (int i = length - 1; i >= 0; i--) {
can_be_poisoned &= shadow[i];
shadow[i] &= can_be_poisoned;
if (i % (1 << granularity) == 0) {
can_be_poisoned = true;
}
}
}
TEST(AddressSanitizerInterface, PoisoningStressTest) {
const size_t kSize = 24;
bool expected[kSize];
char *arr = Ident((char*)malloc(kSize));
for (size_t l1 = 0; l1 < kSize; l1++) {
for (size_t s1 = 1; l1 + s1 <= kSize; s1++) {
for (size_t l2 = 0; l2 < kSize; l2++) {
for (size_t s2 = 1; l2 + s2 <= kSize; s2++) {
// Poison [l1, l1+s1), [l2, l2+s2) and check result.
ASAN_UNPOISON_MEMORY_REGION(arr, kSize);
ASAN_POISON_MEMORY_REGION(arr + l1, s1);
ASAN_POISON_MEMORY_REGION(arr + l2, s2);
memset(expected, false, kSize);
memset(expected + l1, true, s1);
MakeShadowValid(expected, 24, /*granularity*/ 3);
memset(expected + l2, true, s2);
MakeShadowValid(expected, 24, /*granularity*/ 3);
for (size_t i = 0; i < kSize; i++) {
ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i));
}
// Unpoison [l1, l1+s1) and [l2, l2+s2) and check result.
ASAN_POISON_MEMORY_REGION(arr, kSize);
ASAN_UNPOISON_MEMORY_REGION(arr + l1, s1);
ASAN_UNPOISON_MEMORY_REGION(arr + l2, s2);
memset(expected, true, kSize);
memset(expected + l1, false, s1);
MakeShadowValid(expected, 24, /*granularity*/ 3);
memset(expected + l2, false, s2);
MakeShadowValid(expected, 24, /*granularity*/ 3);
for (size_t i = 0; i < kSize; i++) {
ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i));
}
}
}
}
}
}
static const char *kInvalidPoisonMessage = "invalid-poison-memory-range";
static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range";
TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) {
char *array = Ident((char*)malloc(120));
ASAN_UNPOISON_MEMORY_REGION(array, 120);
// Try to unpoison not owned memory
EXPECT_DEATH(ASAN_UNPOISON_MEMORY_REGION(array, 121),
kInvalidUnpoisonMessage);
EXPECT_DEATH(ASAN_UNPOISON_MEMORY_REGION(array - 1, 120),
kInvalidUnpoisonMessage);
ASAN_POISON_MEMORY_REGION(array, 120);
// Try to poison not owned memory.
EXPECT_DEATH(ASAN_POISON_MEMORY_REGION(array, 121), kInvalidPoisonMessage);
EXPECT_DEATH(ASAN_POISON_MEMORY_REGION(array - 1, 120),
kInvalidPoisonMessage);
free(array);
}

View File

@ -0,0 +1,16 @@
extern "C" {
void CFAllocatorDefaultDoubleFree();
void CFAllocatorSystemDefaultDoubleFree();
void CFAllocatorMallocDoubleFree();
void CFAllocatorMallocZoneDoubleFree();
void CallFreeOnWorkqueue(void *mem);
void TestGCDDispatchAsync();
void TestGCDDispatchSync();
void TestGCDReuseWqthreadsAsync();
void TestGCDReuseWqthreadsSync();
void TestGCDDispatchAfter();
void TestGCDInTSDDestructor();
void TestGCDSourceEvent();
void TestGCDSourceCancel();
void TestGCDGroupAsync();
}

View File

@ -0,0 +1,203 @@
// Mac OS X 10.6 or higher only.
#include <dispatch/dispatch.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#import <CoreFoundation/CFBase.h>
#import <Foundation/NSObject.h>
void CFAllocatorDefaultDoubleFree() {
void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0);
CFAllocatorDeallocate(kCFAllocatorDefault, mem);
CFAllocatorDeallocate(kCFAllocatorDefault, mem);
}
void CFAllocatorSystemDefaultDoubleFree() {
void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
}
void CFAllocatorMallocDoubleFree() {
void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0);
CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
}
void CFAllocatorMallocZoneDoubleFree() {
void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0);
CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
}
// Test the +load instrumentation.
// Because the +load methods are invoked before anything else is initialized,
// it makes little sense to wrap the code below into a gTest test case.
// If AddressSanitizer doesn't instrument the +load method below correctly,
// everything will just crash.
char kStartupStr[] =
"If your test didn't crash, AddressSanitizer is instrumenting "
"the +load methods correctly.";
@interface LoadSomething : NSObject {
}
@end
@implementation LoadSomething
+(void) load {
for (int i = 0; i < strlen(kStartupStr); i++) {
volatile char ch = kStartupStr[i]; // make sure no optimizations occur.
}
// Don't print anything here not to interfere with the death tests.
}
@end
void worker_do_alloc(int size) {
char *mem = malloc(size);
mem[0] = 0; // Ok
free(mem);
}
void worker_do_crash(int size) {
char *mem = malloc(size);
mem[size] = 0; // BOOM
free(mem);
}
// Tests for the Grand Central Dispatch. See
// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
// for the reference.
void TestGCDDispatchAsync() {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_block_t block = ^{ worker_do_crash(1024); };
// dispatch_async() runs the task on a worker thread that does not go through
// pthread_create(). We need to verify that AddressSanitizer notices that the
// thread has started.
dispatch_async(queue, block);
// TODO(glider): this is hacky. Need to wait for the worker instead.
sleep(1);
}
void TestGCDDispatchSync() {
dispatch_queue_t queue = dispatch_get_global_queue(2, 0);
dispatch_block_t block = ^{ worker_do_crash(1024); };
// dispatch_sync() runs the task on a worker thread that does not go through
// pthread_create(). We need to verify that AddressSanitizer notices that the
// thread has started.
dispatch_sync(queue, block);
// TODO(glider): this is hacky. Need to wait for the worker instead.
sleep(1);
}
// libdispatch spawns a rather small number of threads and reuses them. We need
// to make sure AddressSanitizer handles the reusing correctly.
void TestGCDReuseWqthreadsAsync() {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
for (int i = 0; i < 100; i++) {
dispatch_async(queue, block_alloc);
}
dispatch_async(queue, block_crash);
// TODO(glider): this is hacky. Need to wait for the workers instead.
sleep(1);
}
// Try to trigger abnormal behaviour of dispatch_sync() being unhandled by us.
void TestGCDReuseWqthreadsSync() {
dispatch_queue_t queue[4];
queue[0] = dispatch_get_global_queue(2, 0);
queue[1] = dispatch_get_global_queue(0, 0);
queue[2] = dispatch_get_global_queue(-2, 0);
queue[3] = dispatch_queue_create("my_queue", NULL);
dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
for (int i = 0; i < 1000; i++) {
dispatch_sync(queue[i % 4], block_alloc);
}
dispatch_sync(queue[3], block_crash);
// TODO(glider): this is hacky. Need to wait for the workers instead.
sleep(1);
}
void TestGCDDispatchAfter() {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
// Schedule the event one second from the current time.
dispatch_time_t milestone =
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
dispatch_after(milestone, queue, block_crash);
// Let's wait for a bit longer now.
// TODO(glider): this is still hacky.
sleep(2);
}
void worker_do_deallocate(void *ptr) {
free(ptr);
}
void CallFreeOnWorkqueue(void *tsd) {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_block_t block_dealloc = ^{ worker_do_deallocate(tsd); };
dispatch_async(queue, block_dealloc);
// Do not wait for the worker to free the memory -- nobody is going to touch
// it.
}
void TestGCDSourceEvent() {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_source_t timer =
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// Schedule the timer one second from the current time.
dispatch_time_t milestone =
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
char *mem = malloc(10);
dispatch_source_set_event_handler(timer, ^{
mem[10] = 1;
});
dispatch_resume(timer);
sleep(2);
}
void TestGCDSourceCancel() {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_source_t timer =
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// Schedule the timer one second from the current time.
dispatch_time_t milestone =
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
char *mem = malloc(10);
// Both dispatch_source_set_cancel_handler() and
// dispatch_source_set_event_handler() use dispatch_barrier_async_f().
// It's tricky to test dispatch_source_set_cancel_handler() separately,
// so we test both here.
dispatch_source_set_event_handler(timer, ^{
dispatch_source_cancel(timer);
});
dispatch_source_set_cancel_handler(timer, ^{
mem[10] = 1;
});
dispatch_resume(timer);
sleep(2);
}
void TestGCDGroupAsync() {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
char *mem = malloc(10);
dispatch_group_async(group, queue, ^{
mem[10] = 1;
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}

View File

@ -0,0 +1,331 @@
//===-- asan_noinst_test.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.
//
// This test file should be compiled w/o asan instrumentation.
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
#include "asan_interface.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include "asan_stack.h"
#include "asan_test_utils.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include "gtest/gtest.h"
// Simple stand-alone pseudorandom number generator.
// Current algorithm is ANSI C linear congruential PRNG.
static inline uint32_t my_rand(uint32_t* state) {
return (*state = *state * 1103515245 + 12345) >> 16;
}
static uint32_t global_seed = 0;
TEST(AddressSanitizer, InternalSimpleDeathTest) {
EXPECT_DEATH(exit(1), "");
}
static void MallocStress(size_t n) {
uint32_t seed = my_rand(&global_seed);
__asan::AsanStackTrace stack1;
stack1.trace[0] = 0xa123;
stack1.trace[1] = 0xa456;
stack1.size = 2;
__asan::AsanStackTrace stack2;
stack2.trace[0] = 0xb123;
stack2.trace[1] = 0xb456;
stack2.size = 2;
__asan::AsanStackTrace stack3;
stack3.trace[0] = 0xc123;
stack3.trace[1] = 0xc456;
stack3.size = 2;
std::vector<void *> vec;
for (size_t i = 0; i < n; i++) {
if ((i % 3) == 0) {
if (vec.empty()) continue;
size_t idx = my_rand(&seed) % vec.size();
void *ptr = vec[idx];
vec[idx] = vec.back();
vec.pop_back();
__asan::asan_free(ptr, &stack1);
} else {
size_t size = my_rand(&seed) % 1000 + 1;
switch ((my_rand(&seed) % 128)) {
case 0: size += 1024; break;
case 1: size += 2048; break;
case 2: size += 4096; break;
}
size_t alignment = 1 << (my_rand(&seed) % 10 + 1);
char *ptr = (char*)__asan::asan_memalign(alignment, size, &stack2);
vec.push_back(ptr);
ptr[0] = 0;
ptr[size-1] = 0;
ptr[size/2] = 0;
}
}
for (size_t i = 0; i < vec.size(); i++)
__asan::asan_free(vec[i], &stack3);
}
TEST(AddressSanitizer, NoInstMallocTest) {
#ifdef __arm__
MallocStress(300000);
#else
MallocStress(1000000);
#endif
}
static void PrintShadow(const char *tag, uintptr_t ptr, size_t size) {
fprintf(stderr, "%s shadow: %lx size % 3ld: ", tag, (long)ptr, (long)size);
uintptr_t prev_shadow = 0;
for (intptr_t i = -32; i < (intptr_t)size + 32; i++) {
uintptr_t shadow = __asan::MemToShadow(ptr + i);
if (i == 0 || i == (intptr_t)size)
fprintf(stderr, ".");
if (shadow != prev_shadow) {
prev_shadow = shadow;
fprintf(stderr, "%02x", (int)*(uint8_t*)shadow);
}
}
fprintf(stderr, "\n");
}
TEST(AddressSanitizer, DISABLED_InternalPrintShadow) {
for (size_t size = 1; size <= 513; size++) {
char *ptr = new char[size];
PrintShadow("m", (uintptr_t)ptr, size);
delete [] ptr;
PrintShadow("f", (uintptr_t)ptr, size);
}
}
static uintptr_t pc_array[] = {
#if __WORDSIZE == 64
0x7effbf756068ULL,
0x7effbf75e5abULL,
0x7effc0625b7cULL,
0x7effc05b8997ULL,
0x7effbf990577ULL,
0x7effbf990c56ULL,
0x7effbf992f3cULL,
0x7effbf950c22ULL,
0x7effc036dba0ULL,
0x7effc03638a3ULL,
0x7effc035be4aULL,
0x7effc0539c45ULL,
0x7effc0539a65ULL,
0x7effc03db9b3ULL,
0x7effc03db100ULL,
0x7effc037c7b8ULL,
0x7effc037bfffULL,
0x7effc038b777ULL,
0x7effc038021cULL,
0x7effc037c7d1ULL,
0x7effc037bfffULL,
0x7effc038b777ULL,
0x7effc038021cULL,
0x7effc037c7d1ULL,
0x7effc037bfffULL,
0x7effc038b777ULL,
0x7effc038021cULL,
0x7effc037c7d1ULL,
0x7effc037bfffULL,
0x7effc0520d26ULL,
0x7effc009ddffULL,
0x7effbf90bb50ULL,
0x7effbdddfa69ULL,
0x7effbdde1fe2ULL,
0x7effbdde2424ULL,
0x7effbdde27b3ULL,
0x7effbddee53bULL,
0x7effbdde1988ULL,
0x7effbdde0904ULL,
0x7effc106ce0dULL,
0x7effbcc3fa04ULL,
0x7effbcc3f6a4ULL,
0x7effbcc3e726ULL,
0x7effbcc40852ULL,
0x7effb681ec4dULL,
#endif // __WORDSIZE
0xB0B5E768,
0x7B682EC1,
0x367F9918,
0xAE34E13,
0xBA0C6C6,
0x13250F46,
0xA0D6A8AB,
0x2B07C1A8,
0x6C844F4A,
0x2321B53,
0x1F3D4F8F,
0x3FE2924B,
0xB7A2F568,
0xBD23950A,
0x61020930,
0x33E7970C,
0x405998A1,
0x59F3551D,
0x350E3028,
0xBC55A28D,
0x361F3AED,
0xBEAD0F73,
0xAEF28479,
0x757E971F,
0xAEBA450,
0x43AD22F5,
0x8C2C50C4,
0x7AD8A2E1,
0x69EE4EE8,
0xC08DFF,
0x4BA6538,
0x3708AB2,
0xC24B6475,
0x7C8890D7,
0x6662495F,
0x9B641689,
0xD3596B,
0xA1049569,
0x44CBC16,
0x4D39C39F
};
void CompressStackTraceTest(size_t n_iter) {
uint32_t seed = my_rand(&global_seed);
const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array);
uint32_t compressed[2 * kNumPcs];
for (size_t iter = 0; iter < n_iter; iter++) {
std::random_shuffle(pc_array, pc_array + kNumPcs);
__asan::AsanStackTrace stack0, stack1;
stack0.CopyFrom(pc_array, kNumPcs);
stack0.size = std::max((size_t)1, (size_t)my_rand(&seed) % stack0.size);
size_t compress_size =
std::max((size_t)2, (size_t)my_rand(&seed) % (2 * kNumPcs));
size_t n_frames =
__asan::AsanStackTrace::CompressStack(&stack0, compressed, compress_size);
assert(n_frames <= stack0.size);
__asan::AsanStackTrace::UncompressStack(&stack1, compressed, compress_size);
assert(stack1.size == n_frames);
for (size_t i = 0; i < stack1.size; i++) {
assert(stack0.trace[i] == stack1.trace[i]);
}
}
}
TEST(AddressSanitizer, CompressStackTraceTest) {
CompressStackTraceTest(10000);
}
void CompressStackTraceBenchmark(size_t n_iter) {
const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array);
uint32_t compressed[2 * kNumPcs];
std::random_shuffle(pc_array, pc_array + kNumPcs);
__asan::AsanStackTrace stack0;
stack0.CopyFrom(pc_array, kNumPcs);
stack0.size = kNumPcs;
for (size_t iter = 0; iter < n_iter; iter++) {
size_t compress_size = kNumPcs;
size_t n_frames =
__asan::AsanStackTrace::CompressStack(&stack0, compressed, compress_size);
Ident(n_frames);
}
}
TEST(AddressSanitizer, CompressStackTraceBenchmark) {
CompressStackTraceBenchmark(1 << 24);
}
TEST(AddressSanitizer, QuarantineTest) {
__asan::AsanStackTrace stack;
stack.trace[0] = 0x890;
stack.size = 1;
const int size = 32;
void *p = __asan::asan_malloc(size, &stack);
__asan::asan_free(p, &stack);
size_t i;
size_t max_i = 1 << 30;
for (i = 0; i < max_i; i++) {
void *p1 = __asan::asan_malloc(size, &stack);
__asan::asan_free(p1, &stack);
if (p1 == p) break;
}
// fprintf(stderr, "i=%ld\n", i);
EXPECT_GE(i, 100000U);
EXPECT_LT(i, max_i);
}
void *ThreadedQuarantineTestWorker(void *unused) {
uint32_t seed = my_rand(&global_seed);
__asan::AsanStackTrace stack;
stack.trace[0] = 0x890;
stack.size = 1;
for (size_t i = 0; i < 1000; i++) {
void *p = __asan::asan_malloc(1 + (my_rand(&seed) % 4000), &stack);
__asan::asan_free(p, &stack);
}
return NULL;
}
// Check that the thread local allocators are flushed when threads are
// destroyed.
TEST(AddressSanitizer, ThreadedQuarantineTest) {
const int n_threads = 3000;
bool old_flag_stats = __asan_enable_statistics(true);
size_t mmaped1 = __asan_get_heap_size();
for (int i = 0; i < n_threads; i++) {
pthread_t t;
pthread_create(&t, NULL, ThreadedQuarantineTestWorker, 0);
pthread_join(t, 0);
size_t mmaped2 = __asan_get_heap_size();
EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20));
}
__asan_enable_statistics(old_flag_stats);
}
void *ThreadedOneSizeMallocStress(void *unused) {
__asan::AsanStackTrace stack;
stack.trace[0] = 0x890;
stack.size = 1;
const size_t kNumMallocs = 1000;
for (int iter = 0; iter < 1000; iter++) {
void *p[kNumMallocs];
for (size_t i = 0; i < kNumMallocs; i++) {
p[i] = __asan::asan_malloc(32, &stack);
}
for (size_t i = 0; i < kNumMallocs; i++) {
__asan::asan_free(p[i], &stack);
}
}
return NULL;
}
TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) {
const int kNumThreads = 4;
pthread_t t[kNumThreads];
for (int i = 0; i < kNumThreads; i++) {
pthread_create(&t[i], 0, ThreadedOneSizeMallocStress, 0);
}
for (int i = 0; i < kNumThreads; i++) {
pthread_join(t[i], 0);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
fun:*IgnoreTest*
fun:*SomeOtherFunc*

View File

@ -0,0 +1,44 @@
//===-- asan_test_config.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.
//
//===----------------------------------------------------------------------===//
#ifndef ASAN_TEST_CONFIG_H
#define ASAN_TEST_CONFIG_H
#include <vector>
#include <string>
#include <map>
#include "gtest/gtest.h"
using std::string;
using std::vector;
using std::map;
#ifndef ASAN_UAR
# error "please define ASAN_UAR"
#endif
#ifndef ASAN_HAS_EXCEPTIONS
# error "please define ASAN_HAS_EXCEPTIONS"
#endif
#ifndef ASAN_HAS_BLACKLIST
# error "please define ASAN_HAS_BLACKLIST"
#endif
#ifndef ASAN_NEEDS_SEGV
# error "please define ASAN_NEEDS_SEGV"
#endif
#define ASAN_PCRE_DOTALL ""
#endif // ASAN_TEST_CONFIG_H

View File

@ -0,0 +1,30 @@
//===-- asan_test_utils.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.
//
//===----------------------------------------------------------------------===//
#ifndef ASAN_TEST_UTILS_H
#define ASAN_TEST_UTILS_H
// Make the compiler think that something is going on there.
extern "C" void break_optimization(void *);
// This function returns its parameter but in such a way that compiler
// can not prove it.
template<class T>
__attribute__((noinline))
static T Ident(T t) {
T ret = t;
break_optimization(&ret);
return ret;
}
#endif // ASAN_TEST_UTILS_H

View File

@ -0,0 +1,12 @@
#include <string.h>
int main(int argc, char **argv) {
static char XXX[10];
static char YYY[10];
static char ZZZ[10];
memset(XXX, 0, 10);
memset(YYY, 0, 10);
memset(ZZZ, 0, 10);
int res = YYY[argc * 10]; // BOOOM
res += XXX[argc] + ZZZ[argc];
return res;
}

View File

@ -0,0 +1,3 @@
READ of size 1 at 0x.* thread T0
#0 0x.* in main .*global-overflow.cc:9
0x.* is located 0 bytes to the right of global variable .*YYY.* of size 10

View File

@ -0,0 +1,9 @@
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
char *x = (char*)malloc(10 * sizeof(char));
memset(x, 0, 10);
int res = x[argc * 10]; // BOOOM
free(x);
return res;
}

View File

@ -0,0 +1,6 @@
READ of size 1 at 0x.* thread T0
#0 0x.* in main .*heap-overflow.cc:6
0x.* is located 0 bytes to the right of 10-byte region
allocated by thread T0 here:
#0 0x.* in malloc _asan_rtl_
#1 0x.* in main .*heap-overflow.cc:[45]

View File

@ -0,0 +1,8 @@
READ of size 1 at 0x.* thread T0
#0 0x.* in main .*heap-overflow.cc:6
0x.* is located 0 bytes to the right of 10-byte region
allocated by thread T0 here:
#0 0x.* in .*mz_malloc.* _asan_rtl_
#1 0x.* in malloc_zone_malloc.*
#2 0x.* in malloc.*
#3 0x.* in main heap-overflow.cc:4

View File

@ -0,0 +1,33 @@
#include <stdlib.h>
__attribute__((noinline))
static void LargeFunction(int *x, int zero) {
x[0]++;
x[1]++;
x[2]++;
x[3]++;
x[4]++;
x[5]++;
x[6]++;
x[7]++;
x[8]++;
x[9]++;
x[zero + 111]++; // we should report this exact line
x[10]++;
x[11]++;
x[12]++;
x[13]++;
x[14]++;
x[15]++;
x[16]++;
x[17]++;
x[18]++;
x[19]++;
}
int main(int argc, char **argv) {
int *x = new int[100];
LargeFunction(x, argc - 1);
delete x;
}

View File

@ -0,0 +1,8 @@
.*ERROR: AddressSanitizer heap-buffer-overflow on address 0x.* at pc 0x.* bp 0x.* sp 0x.*
READ of size 4 at 0x.* thread T0
#0 0x.* in LargeFunction .*large_func_test.cc:15
#1 0x.* in main .*large_func_test.cc:3[012]
0x.* is located 44 bytes to the right of 400-byte region
allocated by thread T0 here:
#0 0x.* in operator new.*
#1 0x.* in main .*large_func_test.cc:30

View File

@ -0,0 +1,35 @@
#!/usr/bin/python
import re
import sys
def matchFile(f, f_re):
for line_re in f_re:
line_re = line_re.rstrip()
if not line_re:
continue
if line_re[0] == '#':
continue
match = False
for line in f:
line = line.rstrip()
# print line
if re.search(line_re, line):
match = True
#print 'match: %s =~ %s' % (line, line_re)
break
if not match:
print 'no match for: %s' % (line_re)
return False
return True
if len(sys.argv) != 2:
print >>sys.stderr, 'Usage: %s <template file>'
sys.exit(1)
f = sys.stdin
f_re = open(sys.argv[1])
if not matchFile(f, f_re):
print >>sys.stderr, 'File does not match the template'
sys.exit(1)

View File

@ -0,0 +1,7 @@
__attribute__((noinline))
static void NullDeref(int *ptr) {
ptr[10]++;
}
int main() {
NullDeref((int*)0);
}

View File

@ -0,0 +1,4 @@
.*ERROR: AddressSanitizer crashed on unknown address 0x0*00028 .*pc 0x.*
AddressSanitizer can not provide additional info. ABORTING
#0 0x.* in NullDeref.*null_deref.cc:3
#1 0x.* in main.*null_deref.cc:[67]

View File

@ -0,0 +1,21 @@
//===-- asan_rtl.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.
//
//===----------------------------------------------------------------------===//
#include <stdio.h>
int pad[10];
int GLOB[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
extern "C"
void inc(int index) {
GLOB[index]++;
}

View File

@ -0,0 +1,44 @@
//===-- asan_rtl.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.
//
//===----------------------------------------------------------------------===//
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <string>
using std::string;
const char kMyName[] = "shared-lib-test";
const char kSoName[] = "shared-lib-test-so";
typedef void (fun_t)(int x);
int main(int argc, char *argv[]) {
string path = strdup(argv[0]);
size_t start = path.find(kMyName);
if (start == string::npos) return 1;
path.replace(start, strlen(kMyName), kSoName);
path += ".so";
// printf("opening %s ... ", path.c_str());
void *lib = dlopen(path.c_str(), RTLD_NOW);
if (!lib) {
printf("error in dlopen(): %s\n", dlerror());
return 1;
}
fun_t *inc = (fun_t*)dlsym(lib, "inc");
if (!inc) return 1;
// printf("ok\n");
inc(1);
inc(-1);
return 0;
}

View File

@ -0,0 +1,7 @@
#.*ERROR: AddressSanitizer global-buffer-overflow on address 0x.* at pc 0x.* bp 0x.* sp 0x.*
#READ of size 4 at 0x.* thread T0
# #0 0x.* in inc .*shared-lib-test-so.cc:11
# #1 0x.* in main .*shared-lib-test.cc:33
# #2 0x.* in __libc_start_main.*
#0x.* is located 4 bytes to the left of global variable 'GLOB' (.*) of size 40
#0x.* is located 52 bytes to the right of global variable 'pad' (.*) of size 40

View File

@ -0,0 +1,7 @@
#include <string.h>
int main(int argc, char **argv) {
char x[10];
memset(x, 0, 10);
int res = x[argc * 10]; // BOOOM
return res;
}

View File

@ -0,0 +1,3 @@
READ of size 1 at 0x.* thread T0
#0 0x.* in main .*stack-overflow.cc:5
Address 0x.* is .* frame <main>

View File

@ -0,0 +1,24 @@
#include <stdio.h>
__attribute__((noinline))
char *Ident(char *x) {
fprintf(stderr, "1: %p\n", x);
return x;
}
__attribute__((noinline))
char *Func1() {
char local;
return Ident(&local);
}
__attribute__((noinline))
void Func2(char *x) {
fprintf(stderr, "2: %p\n", x);
*x = 1;
}
int main(int argc, char **argv) {
Func2(Func1());
return 0;
}

View File

@ -0,0 +1,3 @@
WRITE of size 1 .* thread T0
#0.*Func2.*stack-use-after-return.cc:18
is located in frame <.*Func1.*> of T0's stack

View File

@ -0,0 +1,9 @@
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv) {
char *hello = (char*)malloc(6);
strcpy(hello, "hello");
char *short_buffer = (char*)malloc(9);
strncpy(short_buffer, hello, 10); // BOOM
return short_buffer[8];
}

View File

@ -0,0 +1,7 @@
WRITE of size 1 at 0x.* thread T0
#0 0x.* in strncpy _asan_rtl_
#1 0x.* in main .*strncpy-overflow.cc:[78]
0x.* is located 0 bytes to the right of 9-byte region
allocated by thread T0 here:
#0 0x.* in malloc _asan_rtl_
#1 0x.* in main .*strncpy-overflow.cc:6

View File

@ -0,0 +1,33 @@
#!/bin/bash
OS=`uname`
CXX=$1
CXXFLAGS="-mno-omit-leaf-frame-pointer"
SYMBOLIZER=../scripts/asan_symbolize.py
for t in *.tmpl; do
for b in 32 64; do
for O in 1 2 3; do
# TODO: reinstate -O0, if that's really needed.
c=`basename $t .tmpl`
c_so=$c-so
exe=$c.$b.O$O
so=$c_so.$b.O$O.so
$CXX $CXXFLAGS -g -m$b -faddress-sanitizer -O$O $c.cc -o $exe
[ -e "$c_so.cc" ] && $CXX $CXXFLAGS -g -m$b -faddress-sanitizer -O$O $c_so.cc -fPIC -shared -o $so
# If there's an OS-specific template, use it.
# Please minimize the use of OS-specific templates.
if [ -e "$t.$OS" ]
then
actual_t="$t.$OS"
else
actual_t="$t"
fi
./$exe 2>&1 | $SYMBOLIZER 2> /dev/null | c++filt | ./match_output.py $actual_t || exit 1
echo $exe
rm ./$exe
[ -e "$so" ] && rm ./$so
done
done
done
exit 0

View File

@ -0,0 +1,6 @@
#include <stdlib.h>
int main() {
char *x = (char*)malloc(10 * sizeof(char));
free(x);
return x[5];
}

View File

@ -0,0 +1,6 @@
#include <stdlib.h>
int main() {
char *x = (char*)malloc(10 * sizeof(char));
free(x);
return x[5];
}

View File

@ -0,0 +1,10 @@
.*ERROR: AddressSanitizer heap-use-after-free on address 0x.* at pc 0x.* bp 0x.* sp 0x.*
READ of size 1 at 0x.* thread T0
#0 0x.* in main .*use-after-free.cc:5
0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*
freed by thread T0 here:
#0 0x.* in free _asan_rtl_
#1 0x.* in main .*use-after-free.cc:[45]
previously allocated by thread T0 here:
#0 0x.* in malloc _asan_rtl_
#1 0x.* in main .*use-after-free.cc:3