forked from OSchip/llvm-project
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:
parent
71944203de
commit
019b76f5fd
|
@ -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
|
|
@ -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-*
|
|
@ -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
|
@ -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
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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"
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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_ */
|
|
@ -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();
|
||||
}
|
|
@ -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) {
|
||||
}
|
|
@ -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];
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -0,0 +1,2 @@
|
|||
fun:*IgnoreTest*
|
||||
fun:*SomeOtherFunc*
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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]
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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)
|
|
@ -0,0 +1,7 @@
|
|||
__attribute__((noinline))
|
||||
static void NullDeref(int *ptr) {
|
||||
ptr[10]++;
|
||||
}
|
||||
int main() {
|
||||
NullDeref((int*)0);
|
||||
}
|
|
@ -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]
|
|
@ -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]++;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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];
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
|||
#include <stdlib.h>
|
||||
int main() {
|
||||
char *x = (char*)malloc(10 * sizeof(char));
|
||||
free(x);
|
||||
return x[5];
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#include <stdlib.h>
|
||||
int main() {
|
||||
char *x = (char*)malloc(10 * sizeof(char));
|
||||
free(x);
|
||||
return x[5];
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue