diff options
author | Ed Schouten <ed@FreeBSD.org> | 2013-05-27 18:27:12 +0000 |
---|---|---|
committer | Ed Schouten <ed@FreeBSD.org> | 2013-05-27 18:27:12 +0000 |
commit | 11023dc647fd8f41418da90d59db138400d0f334 (patch) | |
tree | 50f0ab80515576749ef638dd0766b70a65904bfa | |
parent | 58aabf08b77d221489f10e274812ec60917c21a8 (diff) | |
download | src-11023dc647fd8f41418da90d59db138400d0f334.tar.gz src-11023dc647fd8f41418da90d59db138400d0f334.zip |
Import compiler-rt r182741.vendor/compiler-rt/compiler-rt-r182741
Notes
Notes:
svn path=/vendor/compiler-rt/dist/; revision=251034
svn path=/vendor/compiler-rt/compiler-rt-r182741/; revision=251036; tag=vendor/compiler-rt/compiler-rt-r182741
434 files changed, 22298 insertions, 8908 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 04d6e9763bf8..a57751ce6f61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,19 @@ include(LLVMParseArguments) # runtime libraries. cmake_minimum_required(VERSION 2.8.8) +# Compute the Clang version from the LLVM version. +# FIXME: We should be able to reuse CLANG_VERSION variable calculated +# in Clang cmake files, instead of copying the rules here. +string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" CLANG_VERSION + ${PACKAGE_VERSION}) +# Setup the paths where compiler-rt runtimes and headers should be stored. +set(LIBCLANG_INSTALL_PATH lib${LLVM_LIBDIR_SUFFIX}/clang/${CLANG_VERSION}) +string(TOLOWER ${CMAKE_SYSTEM_NAME} LIBCLANG_OS_DIR) +set(CLANG_RESOURCE_DIR ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}) +set(COMPILER_RT_LIBRARY_OUTPUT_DIR ${CLANG_RESOURCE_DIR}/lib/${LIBCLANG_OS_DIR}) +set(COMPILER_RT_LIBRARY_INSTALL_DIR + ${LIBCLANG_INSTALL_PATH}/lib/${LIBCLANG_OS_DIR}) + # Add path for custom modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} @@ -23,6 +36,9 @@ set(CMAKE_MODULE_PATH include(AddCompilerRT) set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +# Setup custom SDK sysroots. +set(COMPILER_RT_DARWIN_SDK_SYSROOT ${COMPILER_RT_SOURCE_DIR}/SDKs/darwin) +set(COMPILER_RT_LINUX_SDK_SYSROOT ${COMPILER_RT_SOURCE_DIR}/SDKs/linux) # Detect whether the current target platform is 32-bit or 64-bit, and setup # the correct commandline flags needed to attempt to target 32-bit and 64-bit. @@ -37,15 +53,8 @@ else() set(TARGET_32_BIT_CFLAGS "-m32") endif() -# FIXME: Below we assume that the target build of LLVM/Clang is x86, which is -# not at all valid. Much of this can be fixed just by switching to use -# a just-built-clang binary for the compiles. - -set(TARGET_x86_64_CFLAGS ${TARGET_64_BIT_CFLAGS}) -set(TARGET_i386_CFLAGS ${TARGET_32_BIT_CFLAGS}) - -set(COMPILER_RT_SUPPORTED_ARCH - x86_64 i386) +# List of architectures we can target. +set(COMPILER_RT_SUPPORTED_ARCH) function(get_target_flags_for_arch arch out_var) list(FIND COMPILER_RT_SUPPORTED_ARCH ${arch} ARCH_INDEX) @@ -60,27 +69,45 @@ endfunction() # platform. We use the results of these tests to build only the various target # runtime libraries supported by our current compilers cross-compiling # abilities. -set(SIMPLE_SOURCE64 ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple64.c) -file(WRITE ${SIMPLE_SOURCE64} "#include <stdlib.h>\nint main() {}") -try_compile(CAN_TARGET_x86_64 ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE64} - COMPILE_DEFINITIONS "${TARGET_x86_64_CFLAGS}" - CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${TARGET_x86_64_CFLAGS}") - -set(SIMPLE_SOURCE32 ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple32.c) -file(WRITE ${SIMPLE_SOURCE32} "#include <stdlib.h>\nint main() {}") -try_compile(CAN_TARGET_i386 ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE32} - COMPILE_DEFINITIONS "${TARGET_i386_CFLAGS}" - CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${TARGET_i386_CFLAGS}") +set(SIMPLE_SOURCE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple.c) +file(WRITE ${SIMPLE_SOURCE} "#include <stdlib.h>\nint main() {}") + +# test_target_arch(<arch> <target flags...>) +# Sets the target flags for a given architecture and determines if this +# architecture is supported by trying to build a simple file. +macro(test_target_arch arch) + set(TARGET_${arch}_CFLAGS ${ARGN}) + try_compile(CAN_TARGET_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE} + COMPILE_DEFINITIONS "${TARGET_${arch}_CFLAGS}" + CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${TARGET_${arch}_CFLAGS}") + if(${CAN_TARGET_${arch}}) + list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch}) + endif() +endmacro() + +if("${LLVM_NATIVE_ARCH}" STREQUAL "X86") + test_target_arch(x86_64 ${TARGET_64_BIT_CFLAGS}) + test_target_arch(i386 ${TARGET_32_BIT_CFLAGS}) +elseif("${LLVM_NATIVE_ARCH}" STREQUAL "PowerPC") + # Explicitly set -m flag on powerpc, because on ppc64 defaults for gcc and + # clang are different. + test_target_arch(powerpc64 "-m64") + test_target_arch(powerpc "-m32") +endif() # We only support running instrumented tests when we're not cross compiling # and target a unix-like system. On Android we define the rules for building # unit tests, but don't execute them. if("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND UNIX AND NOT ANDROID) - set(COMPILER_RT_CAN_EXECUTE_TESTS TRUE) + option(COMPILER_RT_CAN_EXECUTE_TESTS "Can we execute instrumented tests" ON) else() - set(COMPILER_RT_CAN_EXECUTE_TESTS FALSE) + option(COMPILER_RT_CAN_EXECUTE_TESTS "Can we execute instrumented tests" OFF) endif() - + +# Check if compiler-rt is built with libc++. +find_flag_in_string("${CMAKE_CXX_FLAGS}" "-stdlib=libc++" + COMPILER_RT_USES_LIBCXX) + function(filter_available_targets out_var) set(archs) foreach(arch ${ARGN}) @@ -99,6 +126,8 @@ set(SANITIZER_COMMON_CFLAGS -fno-exceptions -fomit-frame-pointer -funwind-tables + -fno-stack-protector + -Wno-gnu # Variadic macros with 0 arguments for ... -O3 ) if(NOT WIN32) @@ -120,51 +149,36 @@ check_cxx_compiler_flag(-Wno-c99-extensions SUPPORTS_NO_C99_EXTENSIONS_FLAG) if(SUPPORTS_NO_C99_EXTENSIONS_FLAG) list(APPEND SANITIZER_COMMON_CFLAGS -Wno-c99-extensions) endif() +# Sanitizer may not have libstdc++, so we can have problems with virtual +# destructors. +check_cxx_compiler_flag(-Wno-non-virtual-dtor SUPPORTS_NO_NON_VIRTUAL_DTOR_FLAG) +if (SUPPORTS_NO_NON_VIRTUAL_DTOR_FLAG) + list(APPEND SANITIZER_COMMON_CFLAGS -Wno-non-virtual-dtor) +endif() + +# Setup min Mac OS X version. if(APPLE) - list(APPEND SANITIZER_COMMON_CFLAGS -mmacosx-version-min=10.5) + if(COMPILER_RT_USES_LIBCXX) + set(SANITIZER_MIN_OSX_VERSION 10.7) + else() + set(SANITIZER_MIN_OSX_VERSION 10.5) + endif() + list(APPEND SANITIZER_COMMON_CFLAGS + -mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION}) endif() # Architectures supported by Sanitizer runtimes. Specific sanitizers may # support only subset of these (e.g. TSan works on x86_64 only). filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH - x86_64 i386) - -# Compute the Clang version from the LLVM version. -# FIXME: We should be able to reuse CLANG_VERSION variable calculated -# in Clang cmake files, instead of copying the rules here. -string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" CLANG_VERSION - ${PACKAGE_VERSION}) -# Setup the paths where compiler-rt runtimes and headers should be stored. -set(LIBCLANG_INSTALL_PATH lib${LLVM_LIBDIR_SUFFIX}/clang/${CLANG_VERSION}) -string(TOLOWER ${CMAKE_SYSTEM_NAME} LIBCLANG_OS_DIR) - -# Install compiler-rt headers. -install(DIRECTORY include/ - DESTINATION ${LIBCLANG_INSTALL_PATH}/include - FILES_MATCHING - PATTERN "*.h" - PATTERN ".svn" EXCLUDE - ) - -# Call add_clang_compiler_rt_libraries to make sure that targets are built -# and installed in the directories where Clang driver expects to find them. -macro(add_clang_compiler_rt_libraries) - # Setup output directories so that clang in build tree works. - set_target_properties(${ARGN} PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY - ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/lib/${LIBCLANG_OS_DIR} - LIBRARY_OUTPUT_DIRECTORY - ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/lib/${LIBCLANG_OS_DIR} - ) - # Add installation command. - install(TARGETS ${ARGN} - ARCHIVE DESTINATION ${LIBCLANG_INSTALL_PATH}/lib/${LIBCLANG_OS_DIR} - LIBRARY DESTINATION ${LIBCLANG_INSTALL_PATH}/lib/${LIBCLANG_OS_DIR} - ) -endmacro(add_clang_compiler_rt_libraries) + x86_64 i386 powerpc64 powerpc) # Add the public header's directory to the includes for all of compiler-rt. include_directories(include) +add_subdirectory(include) + +set(SANITIZER_COMMON_LIT_TEST_DEPS + clang clang-headers FileCheck count not llvm-nm llvm-symbolizer + compiler-rt-headers) add_subdirectory(lib) @@ -255,10 +255,10 @@ $(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.S $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir $(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c -o $$@ $$< $(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.c $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir $(Summary) " COMPILE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<" - $(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c $(COMMON_CFLAGS) -o $$@ $$< + $(Verb) $(Tmp.CC) $(COMMON_CFLAGS) $(Tmp.CFLAGS) -c -o $$@ $$< $(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.cc $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir $(Summary) " COMPILE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<" - $(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c $(COMMON_CXXFLAGS) -o $$@ $$< + $(Verb) $(Tmp.CC) $(COMMON_CXXFLAGS) $(Tmp.CFLAGS) -c -o $$@ $$< .PRECIOUS: $(Tmp.ObjPath)/.dir endef diff --git a/SDKs/darwin/usr/include/fcntl.h b/SDKs/darwin/usr/include/fcntl.h new file mode 100644 index 000000000000..a5f91e3a5bc6 --- /dev/null +++ b/SDKs/darwin/usr/include/fcntl.h @@ -0,0 +1,17 @@ +/* ===-- fcntl.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#include <sys/fcntl.h> diff --git a/SDKs/darwin/usr/include/stdio.h b/SDKs/darwin/usr/include/stdio.h index 63b10a86b632..006652fb9b88 100644 --- a/SDKs/darwin/usr/include/stdio.h +++ b/SDKs/darwin/usr/include/stdio.h @@ -24,15 +24,18 @@ extern "C" { typedef struct __sFILE FILE; typedef __SIZE_TYPE__ size_t; -/* Determine the appropriate fopen() and fwrite() functions. */ +/* Determine the appropriate fdopen, fopen(), and fwrite() functions. */ #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) # if defined(__i386) +# define __FDOPEN_NAME "_fdopen$UNIX2003" # define __FOPEN_NAME "_fopen$UNIX2003" # define __FWRITE_NAME "_fwrite$UNIX2003" # elif defined(__x86_64__) +# define __FDOPEN_NAME "_fdopen" # define __FOPEN_NAME "_fopen" # define __FWRITE_NAME "_fwrite" # elif defined(__arm) +# define __FDOPEN_NAME "_fdopen" # define __FOPEN_NAME "_fopen" # define __FWRITE_NAME "_fwrite" # else @@ -40,9 +43,11 @@ typedef __SIZE_TYPE__ size_t; # endif #elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) # if defined(__i386) || defined (__x86_64) +# define __FDOPEN_NAME "_fdopen" # define __FOPEN_NAME "_fopen" # define __FWRITE_NAME "_fwrite" # elif defined(__arm) +# define __FDOPEN_NAME "_fdopen" # define __FOPEN_NAME "_fopen" # define __FWRITE_NAME "_fwrite" # else @@ -68,13 +73,13 @@ extern FILE *__stderrp; int fclose(FILE *); int fflush(FILE *); FILE *fopen(const char * __restrict, const char * __restrict) __asm(__FOPEN_NAME); +FILE *fdopen(int, const char *) __asm(__FDOPEN_NAME); int fprintf(FILE * __restrict, const char * __restrict, ...); size_t fwrite(const void * __restrict, size_t, size_t, FILE * __restrict) __asm(__FWRITE_NAME); size_t fread(void * __restrict, size_t, size_t, FILE * __restrict); long ftell(FILE *); int fseek(FILE *, long, int); - int snprintf(char * __restrict, size_t, const char * __restrict, ...); #if defined(__cplusplus) diff --git a/SDKs/darwin/usr/include/stdlib.h b/SDKs/darwin/usr/include/stdlib.h index c18c2e49a329..b6d3171cff49 100644 --- a/SDKs/darwin/usr/include/stdlib.h +++ b/SDKs/darwin/usr/include/stdlib.h @@ -22,9 +22,11 @@ typedef __SIZE_TYPE__ size_t; void abort(void) __attribute__((__noreturn__)); +int atexit(void (*)(void)); int atoi(const char *); void free(void *); char *getenv(const char *); void *malloc(size_t); +void *realloc(void *, size_t); #endif /* __STDLIB_H__ */ diff --git a/SDKs/darwin/usr/include/string.h b/SDKs/darwin/usr/include/string.h index bee9d46cddc4..c7da1f57ba57 100644 --- a/SDKs/darwin/usr/include/string.h +++ b/SDKs/darwin/usr/include/string.h @@ -21,6 +21,7 @@ typedef __SIZE_TYPE__ size_t; int memcmp(const void *, const void *, size_t); void *memcpy(void *, const void *, size_t); +void *memset(void *, int, size_t); char *strcat(char *, const char *); char *strcpy(char *, const char *); char *strdup(const char *); diff --git a/SDKs/darwin/usr/include/sys/fcntl.h b/SDKs/darwin/usr/include/sys/fcntl.h new file mode 100644 index 000000000000..b71706bf453b --- /dev/null +++ b/SDKs/darwin/usr/include/sys/fcntl.h @@ -0,0 +1,52 @@ +/* ===-- fcntl.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef _SYS_FCNTL_H_ +#define _SYS_FCNTL_H_ + +/* Determine the appropriate open function. */ +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) +# if defined(__i386) +# define __OPEN_NAME "_open$UNIX2003" +# elif defined(__x86_64__) +# define __OPEN_NAME "_open" +# elif defined(__arm) +# define __OPEN_NAME "_open" +# else +# error "unrecognized architecture for targetting OS X" +# endif +#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) +# if defined(__i386) || defined (__x86_64) +# define __OPEN_NAME "_open" +# elif defined(__arm) +# define __OPEN_NAME "_open" +# else +# error "unrecognized architecture for targetting iOS" +# endif +#else +# error "unrecognized architecture for targetting Darwin" +#endif + +#define O_RDONLY 0x0000 /* open for reading only */ +#define O_WRONLY 0x0001 /* open for writing only */ +#define O_RDWR 0x0002 /* open for reading and writing */ +#define O_ACCMODE 0x0003 /* mask for above modes */ + +#define O_CREAT 0x0200 /* create if nonexistant */ + +int open(const char *, int, ...) __asm(__OPEN_NAME); + +#endif /* !_SYS_FCNTL_H_ */ diff --git a/SDKs/darwin/usr/include/sys/mman.h b/SDKs/darwin/usr/include/sys/mman.h new file mode 100644 index 000000000000..84561f1b6abd --- /dev/null +++ b/SDKs/darwin/usr/include/sys/mman.h @@ -0,0 +1,42 @@ +/* ===-- mman.h - stub SDK header for compiler-rt ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __SYS_MMAN_H__ +#define __SYS_MMAN_H__ + +typedef __SIZE_TYPE__ size_t; + +#define PROT_NONE 0x00 +#define PROT_READ 0x01 +#define PROT_WRITE 0x02 +#define PROT_EXEC 0x04 + +#define MAP_SHARED 0x0001 +#define MAP_PRIVATE 0x0002 + +#define MAP_FILE 0x0000 +#define MAP_ANON 0x1000 + +#define MS_ASYNC 0x0001 +#define MS_INVALIDATE 0x0002 +#define MS_SYNC 0x0010 + +void *mmap(void *addr, size_t len, int prot, int flags, int fd, + long long offset); +int munmap(void *addr, size_t len); +int msync(void *addr, size_t len, int flags); + +#endif /* __SYS_MMAN_H__ */ diff --git a/SDKs/linux/usr/include/fcntl.h b/SDKs/linux/usr/include/fcntl.h new file mode 100644 index 000000000000..a5f91e3a5bc6 --- /dev/null +++ b/SDKs/linux/usr/include/fcntl.h @@ -0,0 +1,17 @@ +/* ===-- fcntl.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#include <sys/fcntl.h> diff --git a/SDKs/linux/usr/include/stdio.h b/SDKs/linux/usr/include/stdio.h index 7c258d2aca17..fba593640c36 100644 --- a/SDKs/linux/usr/include/stdio.h +++ b/SDKs/linux/usr/include/stdio.h @@ -33,6 +33,7 @@ extern struct _IO_FILE *stderr; extern int fclose(FILE *); extern int fflush(FILE *); extern FILE *fopen(const char * restrict, const char * restrict); +extern FILE *fdopen(int, const char * restrict); extern int fprintf(FILE * restrict, const char * restrict, ...); extern size_t fwrite(const void * restrict, size_t, size_t, FILE * restrict); extern size_t fread(void * restrict, size_t, size_t, FILE * restrict); diff --git a/SDKs/linux/usr/include/stdlib.h b/SDKs/linux/usr/include/stdlib.h index 2a6617ae3cf1..966b29db6e10 100644 --- a/SDKs/linux/usr/include/stdlib.h +++ b/SDKs/linux/usr/include/stdlib.h @@ -22,6 +22,7 @@ typedef __SIZE_TYPE__ size_t; void abort(void) __attribute__((__nothrow__)) __attribute__((__noreturn__)); +int atexit(void (*)(void)) __attribute__((__nothrow__)); int atoi(const char *) __attribute__((__nothrow__)); void free(void *) __attribute__((__nothrow__)); char *getenv(const char *) __attribute__((__nothrow__)) @@ -29,5 +30,7 @@ char *getenv(const char *) __attribute__((__nothrow__)) __attribute__((__warn_unused_result__)); void *malloc(size_t) __attribute__((__nothrow__)) __attribute((__malloc__)) __attribute__((__warn_unused_result__)); +void *realloc(void *, size_t) __attribute__((__nothrow__)) __attribute((__malloc__)) + __attribute__((__warn_unused_result__)); #endif /* __STDLIB_H__ */ diff --git a/SDKs/linux/usr/include/string.h b/SDKs/linux/usr/include/string.h index bee9d46cddc4..c7da1f57ba57 100644 --- a/SDKs/linux/usr/include/string.h +++ b/SDKs/linux/usr/include/string.h @@ -21,6 +21,7 @@ typedef __SIZE_TYPE__ size_t; int memcmp(const void *, const void *, size_t); void *memcpy(void *, const void *, size_t); +void *memset(void *, int, size_t); char *strcat(char *, const char *); char *strcpy(char *, const char *); char *strdup(const char *); diff --git a/SDKs/linux/usr/include/sys/fcntl.h b/SDKs/linux/usr/include/sys/fcntl.h new file mode 100644 index 000000000000..1512bf9b4e55 --- /dev/null +++ b/SDKs/linux/usr/include/sys/fcntl.h @@ -0,0 +1,29 @@ +/* ===-- fcntl.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef _SYS_FCNTL_H_ +#define _SYS_FCNTL_H_ + +#define O_RDONLY 0x0000 +#define O_WRONLY 0x0001 +#define O_RDWR 0x0002 +#define O_ACCMODE 0x0003 + +#define O_CREAT 0x0200 + +int open(const char *, int, ...); + +#endif /* _SYS_FCNTL_H_ */ diff --git a/SDKs/linux/usr/include/sys/mman.h b/SDKs/linux/usr/include/sys/mman.h index 7c4d05181f54..bfb7f8bb02de 100644 --- a/SDKs/linux/usr/include/sys/mman.h +++ b/SDKs/linux/usr/include/sys/mman.h @@ -19,10 +19,28 @@ typedef __SIZE_TYPE__ size_t; -#define PROT_READ 0x1 -#define PROT_WRITE 0x2 -#define PROT_EXEC 0x4 +#define PROT_NONE 0x00 +#define PROT_READ 0x01 +#define PROT_WRITE 0x02 +#define PROT_EXEC 0x04 +#define MAP_SHARED 0x0001 +#define MAP_PRIVATE 0x0002 + +#define MAP_FILE 0x0000 +#define MAP_ANON 0x1000 + +#define MS_ASYNC 0x0001 +#define MS_INVALIDATE 0x0002 +#define MS_SYNC 0x0010 + +extern void *mmap(void *addr, size_t len, int prot, int flags, int fd, + long long offset) + __attribute__((__nothrow__)); +extern int munmap(void *addr, size_t len) + __attribute__((__nothrow__)); +extern int msync(void *addr, size_t len, int flags) + __attribute__((__nothrow__)); extern int mprotect (void *__addr, size_t __len, int __prot) __attribute__((__nothrow__)); diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake index e90253fdfa1e..bf114a401ef0 100644 --- a/cmake/Modules/AddCompilerRT.cmake +++ b/cmake/Modules/AddCompilerRT.cmake @@ -18,6 +18,92 @@ macro(add_compiler_rt_object_library name arch) endif() endmacro() +# Same as above, but adds universal osx library with name "<name>.osx" +# targeting multiple architectures. +# add_compiler_rt_osx_object_library(<name> ARCH <architectures> +# SOURCES <source files> +# CFLAGS <compile flags>) +macro(add_compiler_rt_osx_object_library name) + parse_arguments(LIB "ARCH;SOURCES;CFLAGS" "" ${ARGN}) + set(libname "${name}.osx") + add_library(${libname} OBJECT ${LIB_SOURCES}) + set_target_compile_flags(${libname} ${LIB_CFLAGS}) + set_target_properties(${libname} PROPERTIES OSX_ARCHITECTURES "${LIB_ARCH}") +endmacro() + +# Adds static runtime for a given architecture and puts it in the proper +# directory in the build and install trees. +# add_compiler_rt_static_runtime(<name> <arch> +# SOURCES <source files> +# CFLAGS <compile flags> +# DEFS <compile definitions> +# SYMS <symbols file>) +macro(add_compiler_rt_static_runtime name arch) + if(CAN_TARGET_${arch}) + parse_arguments(LIB "SOURCES;CFLAGS;DEFS;SYMS" "" ${ARGN}) + add_library(${name} STATIC ${LIB_SOURCES}) + # Setup compile flags and definitions. + set_target_compile_flags(${name} + ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) + set_property(TARGET ${name} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) + # Setup correct output directory in the build tree. + set_target_properties(${name} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) + # Add installation command. + install(TARGETS ${name} + ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) + # Generate the .syms file if possible. + if(LIB_SYMS) + get_target_property(libfile ${name} LOCATION) + configure_file(${LIB_SYMS} ${libfile}.syms) + install(FILES ${libfile}.syms + DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) + endif(LIB_SYMS) + else() + message(FATAL_ERROR "Archtecture ${arch} can't be targeted") + endif() +endmacro() + +# Same as add_compiler_rt_static_runtime, but creates a universal library +# for several architectures. +# add_compiler_rt_osx_static_runtime(<name> ARCH <architectures> +# SOURCES <source files> +# CFLAGS <compile flags> +# DEFS <compile definitions>) +macro(add_compiler_rt_osx_static_runtime name) + parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS" "" ${ARGN}) + add_library(${name} STATIC ${LIB_SOURCES}) + set_target_compile_flags(${name} ${LIB_CFLAGS}) + set_property(TARGET ${name} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) + set_target_properties(${name} PROPERTIES + OSX_ARCHITECTURES "${LIB_ARCH}" + ARCHIVE_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) + install(TARGETS ${name} + ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) +endmacro() + +# Adds dynamic runtime library on osx, which supports multiple architectures. +# add_compiler_rt_osx_dynamic_runtime(<name> ARCH <architectures> +# SOURCES <source files> +# CFLAGS <compile flags> +# DEFS <compile definitions> +# LINKFLAGS <link flags>) +macro(add_compiler_rt_osx_dynamic_runtime name) + parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS;LINKFLAGS" "" ${ARGN}) + add_library(${name} SHARED ${LIB_SOURCES}) + set_target_compile_flags(${name} ${LIB_CFLAGS}) + set_target_link_flags(${name} ${LIB_LINKFLAGS}) + set_property(TARGET ${name} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) + set_target_properties(${name} PROPERTIES + OSX_ARCHITECTURES "${LIB_ARCH}" + LIBRARY_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) + install(TARGETS ${name} + LIBRARY DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) +endmacro() + # Unittests support. set(COMPILER_RT_GTEST_PATH ${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest) set(COMPILER_RT_GTEST_SOURCE ${COMPILER_RT_GTEST_PATH}/gtest-all.cc) @@ -35,15 +121,21 @@ set(COMPILER_RT_GTEST_INCLUDE_CFLAGS # LINK_FLAGS <link flags>) macro(add_compiler_rt_test test_suite test_name) parse_arguments(TEST "OBJECTS;DEPS;LINK_FLAGS" "" ${ARGN}) - get_unittest_directory(OUTPUT_DIR) - file(MAKE_DIRECTORY ${OUTPUT_DIR}) - set(output_bin "${OUTPUT_DIR}/${test_name}") - add_custom_command( - OUTPUT ${output_bin} + set(output_bin "${CMAKE_CURRENT_BINARY_DIR}/${test_name}") + add_custom_target(${test_name} COMMAND clang ${TEST_OBJECTS} -o "${output_bin}" ${TEST_LINK_FLAGS} DEPENDS clang ${TEST_DEPS}) - add_custom_target(${test_name} DEPENDS ${output_bin}) # Make the test suite depend on the binary. add_dependencies(${test_suite} ${test_name}) endmacro() + +macro(add_compiler_rt_resource_file target_name file_name) + set(src_file "${CMAKE_CURRENT_SOURCE_DIR}/${file_name}") + set(dst_file "${CLANG_RESOURCE_DIR}/${file_name}") + add_custom_target(${target_name} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src_file} ${dst_file} + DEPENDS ${file_name}) + # Install in Clang resource directory. + install(FILES ${file_name} DESTINATION ${LIBCLANG_INSTALL_PATH}) +endmacro() diff --git a/cmake/Modules/CompilerRTUtils.cmake b/cmake/Modules/CompilerRTUtils.cmake index 50f068091e64..f9760f40dbd5 100644 --- a/cmake/Modules/CompilerRTUtils.cmake +++ b/cmake/Modules/CompilerRTUtils.cmake @@ -15,3 +15,14 @@ function(set_target_link_flags target) set_property(TARGET ${target} PROPERTY LINK_FLAGS "${argstring}") endfunction() +# Check if a given flag is present in a space-separated flag_string. +# Store the result in out_var. +function(find_flag_in_string flag_string flag out_var) + string(REPLACE " " ";" flag_list ${flag_string}) + list(FIND flag_list ${flag} flag_pos) + if(NOT flag_pos EQUAL -1) + set(${out_var} TRUE PARENT_SCOPE) + else() + set(${out_var} FALSE PARENT_SCOPE) + endif() +endfunction() diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 000000000000..700b5326b06c --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,39 @@ +set(SANITIZER_HEADERS + sanitizer/asan_interface.h + sanitizer/common_interface_defs.h + sanitizer/linux_syscall_hooks.h + sanitizer/msan_interface.h) + +set(output_dir ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/include) + +if(MSVC_IDE OR XCODE) + set(other_output_dir ${LLVM_BINARY_DIR}/bin/lib/clang/${CLANG_VERSION}/include) +endif() + +# Copy compiler-rt headers to the build tree. +set(out_files) +foreach( f ${SANITIZER_HEADERS} ) + set( src ${CMAKE_CURRENT_SOURCE_DIR}/${f} ) + set( dst ${output_dir}/${f} ) + add_custom_command(OUTPUT ${dst} + DEPENDS ${src} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dst} + COMMENT "Copying compiler-rt's ${f}...") + list(APPEND out_files ${dst}) + + if(other_output_dir) + set(other_dst ${other_output_dir}/${f}) + add_custom_command(OUTPUT ${other_dst} + DEPENDS ${src} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${other_dst} + COMMENT "Copying compiler-rt's ${f}...") + list(APPEND out_files ${other_dst}) + endif() +endforeach( f ) + +add_custom_target(compiler-rt-headers ALL DEPENDS ${out_files}) + +# Install sanitizer headers. +install(FILES ${SANITIZER_HEADERS} + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + DESTINATION ${LIBCLANG_INSTALL_PATH}/include/sanitizer) diff --git a/include/sanitizer/asan_interface.h b/include/sanitizer/asan_interface.h index 6afc3800f4e7..8adf3f17f24b 100644 --- a/include/sanitizer/asan_interface.h +++ b/include/sanitizer/asan_interface.h @@ -7,69 +7,18 @@ // //===----------------------------------------------------------------------===// // -// This file is a part of AddressSanitizer, an address sanity checker. +// This file is a part of AddressSanitizer. // -// This header can be included by the instrumented program to fetch -// data (mostly allocator statistics) from ASan runtime library. +// Public interface header. //===----------------------------------------------------------------------===// #ifndef SANITIZER_ASAN_INTERFACE_H #define SANITIZER_ASAN_INTERFACE_H #include <sanitizer/common_interface_defs.h> -// ----------- ATTENTION ------------- -// This header should NOT include any other headers from ASan runtime. -// All functions in this header are extern "C" and start with __asan_. - -using __sanitizer::uptr; - +#ifdef __cplusplus 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() SANITIZER_INTERFACE_ATTRIBUTE; - - // This structure describes an instrumented global variable. - struct __asan_global { - uptr beg; // The address of the global. - uptr size; // The original size of the global. - uptr size_with_redzone; // The size with the redzone. - const char *name; // Name as a C string. - uptr has_dynamic_init; // Non-zero if the global has dynamic initializer. - }; - - // These two functions should be called by the instrumented code. - // 'globals' is an array of structures describing 'n' globals. - void __asan_register_globals(__asan_global *globals, uptr n) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unregister_globals(__asan_global *globals, uptr n) - SANITIZER_INTERFACE_ATTRIBUTE; - - // These two functions should be called before and after dynamic initializers - // run, respectively. They should be called with parameters describing all - // dynamically initialized globals defined in the calling TU. - void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_after_dynamic_init() - SANITIZER_INTERFACE_ATTRIBUTE; - - // 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. - uptr __asan_stack_malloc(uptr size, uptr real_stack) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) - SANITIZER_INTERFACE_ATTRIBUTE; - - // These two functions are used by instrumented code in the - // use-after-scope mode. They mark memory for local variables as - // unaddressable when they leave scope and addressable before the - // function exits. - void __asan_poison_stack_memory(uptr addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unpoison_stack_memory(uptr addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - +#endif // 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 @@ -78,8 +27,7 @@ extern "C" { // 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, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + 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. @@ -87,15 +35,10 @@ extern "C" { // 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, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - - // Performs cleanup before a NoReturn function. Must be called before things - // like _exit and execl to avoid false positives on stack. - void __asan_handle_no_return() SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_unpoison_memory_region(void const volatile *addr, size_t size); -// User code should use macro instead of functions. -#if __has_feature(address_sanitizer) +// User code should use macros instead of functions. +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) #define ASAN_POISON_MEMORY_REGION(addr, size) \ __asan_poison_memory_region((addr), (size)) #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ @@ -109,104 +52,86 @@ extern "C" { // 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) - SANITIZER_INTERFACE_ATTRIBUTE; + bool __asan_address_is_poisoned(void const volatile *addr); // If at least on byte in [beg, beg+size) is poisoned, return the address // of the first such byte. Otherwise return 0. - uptr __asan_region_is_poisoned(uptr beg, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + void *__asan_region_is_poisoned(void *beg, size_t size); // Print the description of addr (useful when debugging in gdb). - void __asan_describe_address(uptr addr) - SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_describe_address(void *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(uptr pc, uptr bp, uptr sp, - uptr addr, bool is_write, uptr access_size) - SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_report_error(void *pc, void *bp, void *sp, + void *addr, bool is_write, size_t access_size); // Sets the exit code to use when reporting an error. // Returns the old value. - int __asan_set_error_exit_code(int exit_code) - SANITIZER_INTERFACE_ATTRIBUTE; + int __asan_set_error_exit_code(int exit_code); // Sets the callback to be called right before death on error. // Passing 0 will unset the callback. - void __asan_set_death_callback(void (*callback)(void)) - SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_set_death_callback(void (*callback)(void)); - void __asan_set_error_report_callback(void (*callback)(const char*)) - SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_set_error_report_callback(void (*callback)(const char*)); // User may provide function that would be called right when ASan detects // an error. This can be used to notice cases when ASan detects an error, but // the program crashes before ASan report is printed. - /* OPTIONAL */ void __asan_on_error() - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_on_error(); // User may provide its own implementation for symbolization function. // It should print the description of instruction at address "pc" to // "out_buffer". Description should be at most "out_size" bytes long. // User-specified function should return true if symbolization was // successful. - /* OPTIONAL */ bool __asan_symbolize(const void *pc, char *out_buffer, - int out_size) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + bool __asan_symbolize(const void *pc, char *out_buffer, + int out_size); // 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". - uptr __asan_get_estimated_allocated_size(uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + size_t __asan_get_estimated_allocated_size(size_t size); // Returns true if p was returned by the ASan allocator and // is not yet freed. - bool __asan_get_ownership(const void *p) - SANITIZER_INTERFACE_ATTRIBUTE; + bool __asan_get_ownership(const void *p); // Returns the number of bytes reserved for the pointer p. // Requires (get_ownership(p) == true) or (p == 0). - uptr __asan_get_allocated_size(const void *p) - SANITIZER_INTERFACE_ATTRIBUTE; + size_t __asan_get_allocated_size(const void *p); // Number of bytes, allocated and not yet freed by the application. - uptr __asan_get_current_allocated_bytes() - SANITIZER_INTERFACE_ATTRIBUTE; + 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). - uptr __asan_get_heap_size() - SANITIZER_INTERFACE_ATTRIBUTE; + 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. - uptr __asan_get_free_bytes() - SANITIZER_INTERFACE_ATTRIBUTE; + size_t __asan_get_free_bytes(); // Number of bytes in unmapped pages, that are released to OS. Currently, // always returns 0. - uptr __asan_get_unmapped_bytes() - SANITIZER_INTERFACE_ATTRIBUTE; + size_t __asan_get_unmapped_bytes(); // Prints accumulated stats to stderr. Used for debugging. - void __asan_print_accumulated_stats() - SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_print_accumulated_stats(); // This function may be optionally provided by user and should return // a string containing ASan runtime options. See asan_flags.h for details. - /* OPTIONAL */ const char* __asan_default_options() - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + const char* __asan_default_options(); // Malloc hooks that may be optionally provided by user. // __asan_malloc_hook(ptr, size) is called immediately after // allocation of "size" bytes, which returned "ptr". // __asan_free_hook(ptr) is called immediately before // deallocation of "ptr". - /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; - /* OPTIONAL */ void __asan_free_hook(void *ptr) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_malloc_hook(void *ptr, size_t size); + void __asan_free_hook(void *ptr); +#ifdef __cplusplus } // extern "C" +#endif #endif // SANITIZER_ASAN_INTERFACE_H diff --git a/include/sanitizer/common_interface_defs.h b/include/sanitizer/common_interface_defs.h index 9d8fa5582b67..31d0dea5484b 100644 --- a/include/sanitizer/common_interface_defs.h +++ b/include/sanitizer/common_interface_defs.h @@ -7,86 +7,52 @@ // //===----------------------------------------------------------------------===// // -// This file is shared between AddressSanitizer and ThreadSanitizer. -// It contains basic macro and types. -// NOTE: This file may be included into user code. +// Common part of the public sanitizer interface. //===----------------------------------------------------------------------===// #ifndef SANITIZER_COMMON_INTERFACE_DEFS_H #define SANITIZER_COMMON_INTERFACE_DEFS_H -// ----------- ATTENTION ------------- -// This header should NOT include any other headers to avoid portability issues. +#include <stddef.h> +#include <stdint.h> -#if defined(_WIN32) -// FIXME find out what we need on Windows. __declspec(dllexport) ? -# define SANITIZER_INTERFACE_ATTRIBUTE -# define SANITIZER_WEAK_ATTRIBUTE -#elif defined(SANITIZER_GO) -# define SANITIZER_INTERFACE_ATTRIBUTE -# define SANITIZER_WEAK_ATTRIBUTE -#else -# define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default"))) -# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak)) -#endif - -#ifdef __linux__ -# define SANITIZER_SUPPORTS_WEAK_HOOKS 1 -#else -# define SANITIZER_SUPPORTS_WEAK_HOOKS 0 -#endif - -// __has_feature +// GCC does not understand __has_feature. #if !defined(__has_feature) # define __has_feature(x) 0 #endif -// For portability reasons we do not include stddef.h, stdint.h or any other -// system header, but we do need some basic types that are not defined -// in a portable way by the language itself. -namespace __sanitizer { - -#if defined(_WIN64) -// 64-bit Windows uses LLP64 data model. -typedef unsigned long long uptr; // NOLINT -typedef signed long long sptr; // NOLINT -#else -typedef unsigned long uptr; // NOLINT -typedef signed long sptr; // NOLINT -#endif // defined(_WIN64) -#if defined(__x86_64__) -// Since x32 uses ILP32 data model in 64-bit hardware mode, we must use -// 64-bit pointer to unwind stack frame. -typedef unsigned long long uhwptr; // NOLINT -#else -typedef uptr uhwptr; // NOLINT -#endif -typedef unsigned char u8; -typedef unsigned short u16; // NOLINT -typedef unsigned int u32; -typedef unsigned long long u64; // NOLINT -typedef signed char s8; -typedef signed short s16; // NOLINT -typedef signed int s32; -typedef signed long long s64; // NOLINT - -} // namespace __sanitizer - +#ifdef __cplusplus extern "C" { +#endif // Tell the tools to write their reports to "path.<pid>" instead of stderr. - void __sanitizer_set_report_path(const char *path) - SANITIZER_INTERFACE_ATTRIBUTE; + void __sanitizer_set_report_path(const char *path); // Tell the tools to write their reports to given file descriptor instead of // stderr. - void __sanitizer_set_report_fd(int fd) - SANITIZER_INTERFACE_ATTRIBUTE; + void __sanitizer_set_report_fd(int fd); // Notify the tools that the sandbox is going to be turned on. The reserved // parameter will be used in the future to hold a structure with functions // that the tools may call to bypass the sandbox. - void __sanitizer_sandbox_on_notify(void *reserved) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + void __sanitizer_sandbox_on_notify(void *reserved); + + // This function is called by the tool when it has just finished reporting + // an error. 'error_summary' is a one-line string that summarizes + // the error message. This function can be overridden by the client. + void __sanitizer_report_error_summary(const char *error_summary); + + // Some of the sanitizers (e.g. asan/tsan) may miss bugs that happen + // in unaligned loads/stores. In order to find such bugs reliably one needs + // to replace plain unaligned loads/stores with these calls. + uint16_t __sanitizer_unaligned_load16(const void *p); + uint32_t __sanitizer_unaligned_load32(const void *p); + uint64_t __sanitizer_unaligned_load64(const void *p); + void __sanitizer_unaligned_store16(void *p, uint16_t x); + void __sanitizer_unaligned_store32(void *p, uint32_t x); + void __sanitizer_unaligned_store64(void *p, uint64_t x); + +#ifdef __cplusplus } // extern "C" +#endif #endif // SANITIZER_COMMON_INTERFACE_DEFS_H diff --git a/include/sanitizer/linux_syscall_hooks.h b/include/sanitizer/linux_syscall_hooks.h new file mode 100644 index 000000000000..894d5c2bebff --- /dev/null +++ b/include/sanitizer/linux_syscall_hooks.h @@ -0,0 +1,802 @@ +//===-- linux_syscall_hooks.h ---------------------------------------------===// +// +// 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 public sanitizer interface. +// +// System call handlers. +// +// Interface methods declared in this header implement pre- and post- syscall +// actions for the active sanitizer. +// Usage: +// __sanitizer_syscall_pre_getfoo(...args...); +// int res = syscall(__NR_getfoo, ...args...); +// __sanitizer_syscall_post_getfoo(res, ...args...); +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LINUX_SYSCALL_HOOKS_H +#define SANITIZER_LINUX_SYSCALL_HOOKS_H + +#ifdef __cplusplus +extern "C" { +#endif + +void __sanitizer_syscall_pre_rt_sigpending(void *p, size_t s); +void __sanitizer_syscall_pre_getdents(int fd, void *dirp, int count); +void __sanitizer_syscall_pre_getdents64(int fd, void *dirp, int count); +void __sanitizer_syscall_pre_recvmsg(int sockfd, void *msg, int flags); +void __sanitizer_syscall_pre_wait4(int pid, int *status, int options, void *r); +void __sanitizer_syscall_pre_waitpid(int pid, int *status, int options); + +void __sanitizer_syscall_post_rt_sigpending(long res, void *p, size_t s); +void __sanitizer_syscall_post_getdents(long res, int fd, void *dirp, int count); +void __sanitizer_syscall_post_getdents64(long res, int fd, void *dirp, + int count); +void __sanitizer_syscall_post_recvmsg(long res, int sockfd, void *msg, + int flags); +void __sanitizer_syscall_post_wait4(long res, int pid, int *status, int options, + void *r); +void __sanitizer_syscall_post_waitpid(long res, int pid, int *status, + int options); + +// And now a few syscalls we don't handle yet. + +#define __sanitizer_syscall_pre_accept(...) +#define __sanitizer_syscall_pre_accept4(...) +#define __sanitizer_syscall_pre_access(...) +#define __sanitizer_syscall_pre_acct(...) +#define __sanitizer_syscall_pre_add_key(...) +#define __sanitizer_syscall_pre_adjtimex(...) +#define __sanitizer_syscall_pre_afs_syscall(...) +#define __sanitizer_syscall_pre_alarm(...) +#define __sanitizer_syscall_pre_arch_prctl(...) +#define __sanitizer_syscall_pre_bdflush(...) +#define __sanitizer_syscall_pre_bind(...) +#define __sanitizer_syscall_pre_break(...) +#define __sanitizer_syscall_pre_brk(...) +#define __sanitizer_syscall_pre_capget(...) +#define __sanitizer_syscall_pre_capset(...) +#define __sanitizer_syscall_pre_chdir(...) +#define __sanitizer_syscall_pre_chmod(...) +#define __sanitizer_syscall_pre_chown(...) +#define __sanitizer_syscall_pre_chown32(...) +#define __sanitizer_syscall_pre_chroot(...) +#define __sanitizer_syscall_pre_clock_adjtime(...) +#define __sanitizer_syscall_pre_clock_getres(...) +#define __sanitizer_syscall_pre_clock_gettime(...) +#define __sanitizer_syscall_pre_clock_nanosleep(...) +#define __sanitizer_syscall_pre_clock_settime(...) +#define __sanitizer_syscall_pre_clone(...) +#define __sanitizer_syscall_pre_close(...) +#define __sanitizer_syscall_pre_connect(...) +#define __sanitizer_syscall_pre_creat(...) +#define __sanitizer_syscall_pre_create_module(...) +#define __sanitizer_syscall_pre_delete_module(...) +#define __sanitizer_syscall_pre_dup(...) +#define __sanitizer_syscall_pre_dup2(...) +#define __sanitizer_syscall_pre_dup3(...) +#define __sanitizer_syscall_pre_epoll_create(...) +#define __sanitizer_syscall_pre_epoll_create1(...) +#define __sanitizer_syscall_pre_epoll_ctl(...) +#define __sanitizer_syscall_pre_epoll_ctl_old(...) +#define __sanitizer_syscall_pre_epoll_pwait(...) +#define __sanitizer_syscall_pre_epoll_wait(...) +#define __sanitizer_syscall_pre_epoll_wait_old(...) +#define __sanitizer_syscall_pre_eventfd(...) +#define __sanitizer_syscall_pre_eventfd2(...) +#define __sanitizer_syscall_pre_execve(...) +#define __sanitizer_syscall_pre_exit(...) +#define __sanitizer_syscall_pre_exit_group(...) +#define __sanitizer_syscall_pre_faccessat(...) +#define __sanitizer_syscall_pre_fadvise64(...) +#define __sanitizer_syscall_pre_fadvise64_64(...) +#define __sanitizer_syscall_pre_fallocate(...) +#define __sanitizer_syscall_pre_fanotify_init(...) +#define __sanitizer_syscall_pre_fanotify_mark(...) +#define __sanitizer_syscall_pre_fchdir(...) +#define __sanitizer_syscall_pre_fchmod(...) +#define __sanitizer_syscall_pre_fchmodat(...) +#define __sanitizer_syscall_pre_fchown(...) +#define __sanitizer_syscall_pre_fchown32(...) +#define __sanitizer_syscall_pre_fchownat(...) +#define __sanitizer_syscall_pre_fcntl(...) +#define __sanitizer_syscall_pre_fcntl64(...) +#define __sanitizer_syscall_pre_fdatasync(...) +#define __sanitizer_syscall_pre_fgetxattr(...) +#define __sanitizer_syscall_pre_flistxattr(...) +#define __sanitizer_syscall_pre_flock(...) +#define __sanitizer_syscall_pre_fork(...) +#define __sanitizer_syscall_pre_fremovexattr(...) +#define __sanitizer_syscall_pre_fsetxattr(...) +#define __sanitizer_syscall_pre_fstat(...) +#define __sanitizer_syscall_pre_fstat64(...) +#define __sanitizer_syscall_pre_fstatat64(...) +#define __sanitizer_syscall_pre_fstatfs(...) +#define __sanitizer_syscall_pre_fstatfs64(...) +#define __sanitizer_syscall_pre_fsync(...) +#define __sanitizer_syscall_pre_ftime(...) +#define __sanitizer_syscall_pre_ftruncate(...) +#define __sanitizer_syscall_pre_ftruncate64(...) +#define __sanitizer_syscall_pre_futex(...) +#define __sanitizer_syscall_pre_futimesat(...) +#define __sanitizer_syscall_pre_getcpu(...) +#define __sanitizer_syscall_pre_getcwd(...) +#define __sanitizer_syscall_pre_getegid(...) +#define __sanitizer_syscall_pre_getegid32(...) +#define __sanitizer_syscall_pre_geteuid(...) +#define __sanitizer_syscall_pre_geteuid32(...) +#define __sanitizer_syscall_pre_getgid(...) +#define __sanitizer_syscall_pre_getgid32(...) +#define __sanitizer_syscall_pre_getgroups(...) +#define __sanitizer_syscall_pre_getgroups32(...) +#define __sanitizer_syscall_pre_getitimer(...) +#define __sanitizer_syscall_pre_get_kernel_syms(...) +#define __sanitizer_syscall_pre_get_mempolicy(...) +#define __sanitizer_syscall_pre_getpeername(...) +#define __sanitizer_syscall_pre_getpgid(...) +#define __sanitizer_syscall_pre_getpgrp(...) +#define __sanitizer_syscall_pre_getpid(...) +#define __sanitizer_syscall_pre_getpmsg(...) +#define __sanitizer_syscall_pre_getppid(...) +#define __sanitizer_syscall_pre_getpriority(...) +#define __sanitizer_syscall_pre_getresgid(...) +#define __sanitizer_syscall_pre_getresgid32(...) +#define __sanitizer_syscall_pre_getresuid(...) +#define __sanitizer_syscall_pre_getresuid32(...) +#define __sanitizer_syscall_pre_getrlimit(...) +#define __sanitizer_syscall_pre_get_robust_list(...) +#define __sanitizer_syscall_pre_getrusage(...) +#define __sanitizer_syscall_pre_getsid(...) +#define __sanitizer_syscall_pre_getsockname(...) +#define __sanitizer_syscall_pre_getsockopt(...) +#define __sanitizer_syscall_pre_get_thread_area(...) +#define __sanitizer_syscall_pre_gettid(...) +#define __sanitizer_syscall_pre_gettimeofday(...) +#define __sanitizer_syscall_pre_getuid(...) +#define __sanitizer_syscall_pre_getuid32(...) +#define __sanitizer_syscall_pre_getxattr(...) +#define __sanitizer_syscall_pre_gtty(...) +#define __sanitizer_syscall_pre_idle(...) +#define __sanitizer_syscall_pre_init_module(...) +#define __sanitizer_syscall_pre_inotify_add_watch(...) +#define __sanitizer_syscall_pre_inotify_init(...) +#define __sanitizer_syscall_pre_inotify_init1(...) +#define __sanitizer_syscall_pre_inotify_rm_watch(...) +#define __sanitizer_syscall_pre_io_cancel(...) +#define __sanitizer_syscall_pre_ioctl(...) +#define __sanitizer_syscall_pre_io_destroy(...) +#define __sanitizer_syscall_pre_io_getevents(...) +#define __sanitizer_syscall_pre_ioperm(...) +#define __sanitizer_syscall_pre_iopl(...) +#define __sanitizer_syscall_pre_ioprio_get(...) +#define __sanitizer_syscall_pre_ioprio_set(...) +#define __sanitizer_syscall_pre_io_setup(...) +#define __sanitizer_syscall_pre_io_submit(...) +#define __sanitizer_syscall_pre_ipc(...) +#define __sanitizer_syscall_pre_kexec_load(...) +#define __sanitizer_syscall_pre_keyctl(...) +#define __sanitizer_syscall_pre_kill(...) +#define __sanitizer_syscall_pre_lchown(...) +#define __sanitizer_syscall_pre_lchown32(...) +#define __sanitizer_syscall_pre_lgetxattr(...) +#define __sanitizer_syscall_pre_link(...) +#define __sanitizer_syscall_pre_linkat(...) +#define __sanitizer_syscall_pre_listen(...) +#define __sanitizer_syscall_pre_listxattr(...) +#define __sanitizer_syscall_pre_llistxattr(...) +#define __sanitizer_syscall_pre__llseek(...) +#define __sanitizer_syscall_pre_lock(...) +#define __sanitizer_syscall_pre_lookup_dcookie(...) +#define __sanitizer_syscall_pre_lremovexattr(...) +#define __sanitizer_syscall_pre_lseek(...) +#define __sanitizer_syscall_pre_lsetxattr(...) +#define __sanitizer_syscall_pre_lstat(...) +#define __sanitizer_syscall_pre_lstat64(...) +#define __sanitizer_syscall_pre_madvise(...) +#define __sanitizer_syscall_pre_madvise1(...) +#define __sanitizer_syscall_pre_mbind(...) +#define __sanitizer_syscall_pre_migrate_pages(...) +#define __sanitizer_syscall_pre_mincore(...) +#define __sanitizer_syscall_pre_mkdir(...) +#define __sanitizer_syscall_pre_mkdirat(...) +#define __sanitizer_syscall_pre_mknod(...) +#define __sanitizer_syscall_pre_mknodat(...) +#define __sanitizer_syscall_pre_mlock(...) +#define __sanitizer_syscall_pre_mlockall(...) +#define __sanitizer_syscall_pre_mmap(...) +#define __sanitizer_syscall_pre_mmap2(...) +#define __sanitizer_syscall_pre_modify_ldt(...) +#define __sanitizer_syscall_pre_mount(...) +#define __sanitizer_syscall_pre_move_pages(...) +#define __sanitizer_syscall_pre_mprotect(...) +#define __sanitizer_syscall_pre_mpx(...) +#define __sanitizer_syscall_pre_mq_getsetattr(...) +#define __sanitizer_syscall_pre_mq_notify(...) +#define __sanitizer_syscall_pre_mq_open(...) +#define __sanitizer_syscall_pre_mq_timedreceive(...) +#define __sanitizer_syscall_pre_mq_timedsend(...) +#define __sanitizer_syscall_pre_mq_unlink(...) +#define __sanitizer_syscall_pre_mremap(...) +#define __sanitizer_syscall_pre_msgctl(...) +#define __sanitizer_syscall_pre_msgget(...) +#define __sanitizer_syscall_pre_msgrcv(...) +#define __sanitizer_syscall_pre_msgsnd(...) +#define __sanitizer_syscall_pre_msync(...) +#define __sanitizer_syscall_pre_munlock(...) +#define __sanitizer_syscall_pre_munlockall(...) +#define __sanitizer_syscall_pre_munmap(...) +#define __sanitizer_syscall_pre_name_to_handle_at(...) +#define __sanitizer_syscall_pre_nanosleep(...) +#define __sanitizer_syscall_pre_newfstatat(...) +#define __sanitizer_syscall_pre__newselect(...) +#define __sanitizer_syscall_pre_nfsservctl(...) +#define __sanitizer_syscall_pre_nice(...) +#define __sanitizer_syscall_pre_oldfstat(...) +#define __sanitizer_syscall_pre_oldlstat(...) +#define __sanitizer_syscall_pre_oldolduname(...) +#define __sanitizer_syscall_pre_oldstat(...) +#define __sanitizer_syscall_pre_olduname(...) +#define __sanitizer_syscall_pre_open(...) +#define __sanitizer_syscall_pre_openat(...) +#define __sanitizer_syscall_pre_open_by_handle_at(...) +#define __sanitizer_syscall_pre_pause(...) +#define __sanitizer_syscall_pre_perf_event_open(...) +#define __sanitizer_syscall_pre_personality(...) +#define __sanitizer_syscall_pre_pipe(...) +#define __sanitizer_syscall_pre_pipe2(...) +#define __sanitizer_syscall_pre_pivot_root(...) +#define __sanitizer_syscall_pre_poll(...) +#define __sanitizer_syscall_pre_ppoll(...) +#define __sanitizer_syscall_pre_prctl(...) +#define __sanitizer_syscall_pre_pread64(...) +#define __sanitizer_syscall_pre_preadv(...) +#define __sanitizer_syscall_pre_prlimit64(...) +#define __sanitizer_syscall_pre_process_vm_readv(...) +#define __sanitizer_syscall_pre_process_vm_writev(...) +#define __sanitizer_syscall_pre_prof(...) +#define __sanitizer_syscall_pre_profil(...) +#define __sanitizer_syscall_pre_pselect6(...) +#define __sanitizer_syscall_pre_ptrace(...) +#define __sanitizer_syscall_pre_putpmsg(...) +#define __sanitizer_syscall_pre_pwrite64(...) +#define __sanitizer_syscall_pre_pwritev(...) +#define __sanitizer_syscall_pre_query_module(...) +#define __sanitizer_syscall_pre_quotactl(...) +#define __sanitizer_syscall_pre_read(...) +#define __sanitizer_syscall_pre_readahead(...) +#define __sanitizer_syscall_pre_readdir(...) +#define __sanitizer_syscall_pre_readlink(...) +#define __sanitizer_syscall_pre_readlinkat(...) +#define __sanitizer_syscall_pre_readv(...) +#define __sanitizer_syscall_pre_reboot(...) +#define __sanitizer_syscall_pre_recvfrom(...) +#define __sanitizer_syscall_pre_recvmmsg(...) +#define __sanitizer_syscall_pre_remap_file_pages(...) +#define __sanitizer_syscall_pre_removexattr(...) +#define __sanitizer_syscall_pre_rename(...) +#define __sanitizer_syscall_pre_renameat(...) +#define __sanitizer_syscall_pre_request_key(...) +#define __sanitizer_syscall_pre_restart_syscall(...) +#define __sanitizer_syscall_pre_rmdir(...) +#define __sanitizer_syscall_pre_rt_sigaction(...) +#define __sanitizer_syscall_pre_rt_sigprocmask(...) +#define __sanitizer_syscall_pre_rt_sigqueueinfo(...) +#define __sanitizer_syscall_pre_rt_sigreturn(...) +#define __sanitizer_syscall_pre_rt_sigsuspend(...) +#define __sanitizer_syscall_pre_rt_sigtimedwait(...) +#define __sanitizer_syscall_pre_rt_tgsigqueueinfo(...) +#define __sanitizer_syscall_pre_sched_getaffinity(...) +#define __sanitizer_syscall_pre_sched_getparam(...) +#define __sanitizer_syscall_pre_sched_get_priority_max(...) +#define __sanitizer_syscall_pre_sched_get_priority_min(...) +#define __sanitizer_syscall_pre_sched_getscheduler(...) +#define __sanitizer_syscall_pre_sched_rr_get_interval(...) +#define __sanitizer_syscall_pre_sched_setaffinity(...) +#define __sanitizer_syscall_pre_sched_setparam(...) +#define __sanitizer_syscall_pre_sched_setscheduler(...) +#define __sanitizer_syscall_pre_sched_yield(...) +#define __sanitizer_syscall_pre_security(...) +#define __sanitizer_syscall_pre_select(...) +#define __sanitizer_syscall_pre_semctl(...) +#define __sanitizer_syscall_pre_semget(...) +#define __sanitizer_syscall_pre_semop(...) +#define __sanitizer_syscall_pre_semtimedop(...) +#define __sanitizer_syscall_pre_sendfile(...) +#define __sanitizer_syscall_pre_sendfile64(...) +#define __sanitizer_syscall_pre_sendmmsg(...) +#define __sanitizer_syscall_pre_sendmsg(...) +#define __sanitizer_syscall_pre_sendto(...) +#define __sanitizer_syscall_pre_setdomainname(...) +#define __sanitizer_syscall_pre_setfsgid(...) +#define __sanitizer_syscall_pre_setfsgid32(...) +#define __sanitizer_syscall_pre_setfsuid(...) +#define __sanitizer_syscall_pre_setfsuid32(...) +#define __sanitizer_syscall_pre_setgid(...) +#define __sanitizer_syscall_pre_setgid32(...) +#define __sanitizer_syscall_pre_setgroups(...) +#define __sanitizer_syscall_pre_setgroups32(...) +#define __sanitizer_syscall_pre_sethostname(...) +#define __sanitizer_syscall_pre_setitimer(...) +#define __sanitizer_syscall_pre_set_mempolicy(...) +#define __sanitizer_syscall_pre_setns(...) +#define __sanitizer_syscall_pre_setpgid(...) +#define __sanitizer_syscall_pre_setpriority(...) +#define __sanitizer_syscall_pre_setregid(...) +#define __sanitizer_syscall_pre_setregid32(...) +#define __sanitizer_syscall_pre_setresgid(...) +#define __sanitizer_syscall_pre_setresgid32(...) +#define __sanitizer_syscall_pre_setresuid(...) +#define __sanitizer_syscall_pre_setresuid32(...) +#define __sanitizer_syscall_pre_setreuid(...) +#define __sanitizer_syscall_pre_setreuid32(...) +#define __sanitizer_syscall_pre_setrlimit(...) +#define __sanitizer_syscall_pre_set_robust_list(...) +#define __sanitizer_syscall_pre_setsid(...) +#define __sanitizer_syscall_pre_setsockopt(...) +#define __sanitizer_syscall_pre_set_thread_area(...) +#define __sanitizer_syscall_pre_set_tid_address(...) +#define __sanitizer_syscall_pre_settimeofday(...) +#define __sanitizer_syscall_pre_setuid(...) +#define __sanitizer_syscall_pre_setuid32(...) +#define __sanitizer_syscall_pre_setxattr(...) +#define __sanitizer_syscall_pre_sgetmask(...) +#define __sanitizer_syscall_pre_shmat(...) +#define __sanitizer_syscall_pre_shmctl(...) +#define __sanitizer_syscall_pre_shmdt(...) +#define __sanitizer_syscall_pre_shmget(...) +#define __sanitizer_syscall_pre_shutdown(...) +#define __sanitizer_syscall_pre_sigaction(...) +#define __sanitizer_syscall_pre_sigaltstack(...) +#define __sanitizer_syscall_pre_signal(...) +#define __sanitizer_syscall_pre_signalfd(...) +#define __sanitizer_syscall_pre_signalfd4(...) +#define __sanitizer_syscall_pre_sigpending(...) +#define __sanitizer_syscall_pre_sigprocmask(...) +#define __sanitizer_syscall_pre_sigreturn(...) +#define __sanitizer_syscall_pre_sigsuspend(...) +#define __sanitizer_syscall_pre_socket(...) +#define __sanitizer_syscall_pre_socketcall(...) +#define __sanitizer_syscall_pre_socketpair(...) +#define __sanitizer_syscall_pre_splice(...) +#define __sanitizer_syscall_pre_ssetmask(...) +#define __sanitizer_syscall_pre_stat(...) +#define __sanitizer_syscall_pre_stat64(...) +#define __sanitizer_syscall_pre_statfs(...) +#define __sanitizer_syscall_pre_statfs64(...) +#define __sanitizer_syscall_pre_stime(...) +#define __sanitizer_syscall_pre_stty(...) +#define __sanitizer_syscall_pre_swapoff(...) +#define __sanitizer_syscall_pre_swapon(...) +#define __sanitizer_syscall_pre_symlink(...) +#define __sanitizer_syscall_pre_symlinkat(...) +#define __sanitizer_syscall_pre_sync(...) +#define __sanitizer_syscall_pre_sync_file_range(...) +#define __sanitizer_syscall_pre_syncfs(...) +#define __sanitizer_syscall_pre__sysctl(...) +#define __sanitizer_syscall_pre_sysfs(...) +#define __sanitizer_syscall_pre_sysinfo(...) +#define __sanitizer_syscall_pre_syslog(...) +#define __sanitizer_syscall_pre_tee(...) +#define __sanitizer_syscall_pre_tgkill(...) +#define __sanitizer_syscall_pre_time(...) +#define __sanitizer_syscall_pre_timer_create(...) +#define __sanitizer_syscall_pre_timer_delete(...) +#define __sanitizer_syscall_pre_timerfd_create(...) +#define __sanitizer_syscall_pre_timerfd_gettime(...) +#define __sanitizer_syscall_pre_timerfd_settime(...) +#define __sanitizer_syscall_pre_timer_getoverrun(...) +#define __sanitizer_syscall_pre_timer_gettime(...) +#define __sanitizer_syscall_pre_timer_settime(...) +#define __sanitizer_syscall_pre_times(...) +#define __sanitizer_syscall_pre_tkill(...) +#define __sanitizer_syscall_pre_truncate(...) +#define __sanitizer_syscall_pre_truncate64(...) +#define __sanitizer_syscall_pre_tuxcall(...) +#define __sanitizer_syscall_pre_ugetrlimit(...) +#define __sanitizer_syscall_pre_ulimit(...) +#define __sanitizer_syscall_pre_umask(...) +#define __sanitizer_syscall_pre_umount(...) +#define __sanitizer_syscall_pre_umount2(...) +#define __sanitizer_syscall_pre_uname(...) +#define __sanitizer_syscall_pre_unlink(...) +#define __sanitizer_syscall_pre_unlinkat(...) +#define __sanitizer_syscall_pre_unshare(...) +#define __sanitizer_syscall_pre_uselib(...) +#define __sanitizer_syscall_pre_ustat(...) +#define __sanitizer_syscall_pre_utime(...) +#define __sanitizer_syscall_pre_utimensat(...) +#define __sanitizer_syscall_pre_utimes(...) +#define __sanitizer_syscall_pre_vfork(...) +#define __sanitizer_syscall_pre_vhangup(...) +#define __sanitizer_syscall_pre_vm86(...) +#define __sanitizer_syscall_pre_vm86old(...) +#define __sanitizer_syscall_pre_vmsplice(...) +#define __sanitizer_syscall_pre_vserver(...) +#define __sanitizer_syscall_pre_waitid(...) +#define __sanitizer_syscall_pre_write(...) +#define __sanitizer_syscall_pre_writev(...) + +#define __sanitizer_syscall_post_accept4(res, ...) +#define __sanitizer_syscall_post_accept(res, ...) +#define __sanitizer_syscall_post_access(res, ...) +#define __sanitizer_syscall_post_acct(res, ...) +#define __sanitizer_syscall_post_add_key(res, ...) +#define __sanitizer_syscall_post_adjtimex(res, ...) +#define __sanitizer_syscall_post_afs_syscall(res, ...) +#define __sanitizer_syscall_post_alarm(res, ...) +#define __sanitizer_syscall_post_arch_prctl(res, ...) +#define __sanitizer_syscall_post_bdflush(res, ...) +#define __sanitizer_syscall_post_bind(res, ...) +#define __sanitizer_syscall_post_break(res, ...) +#define __sanitizer_syscall_post_brk(res, ...) +#define __sanitizer_syscall_post_capget(res, ...) +#define __sanitizer_syscall_post_capset(res, ...) +#define __sanitizer_syscall_post_chdir(res, ...) +#define __sanitizer_syscall_post_chmod(res, ...) +#define __sanitizer_syscall_post_chown32(res, ...) +#define __sanitizer_syscall_post_chown(res, ...) +#define __sanitizer_syscall_post_chroot(res, ...) +#define __sanitizer_syscall_post_clock_adjtime(res, ...) +#define __sanitizer_syscall_post_clock_getres(res, ...) +#define __sanitizer_syscall_post_clock_gettime(res, ...) +#define __sanitizer_syscall_post_clock_nanosleep(res, ...) +#define __sanitizer_syscall_post_clock_settime(res, ...) +#define __sanitizer_syscall_post_clone(res, ...) +#define __sanitizer_syscall_post_close(res, ...) +#define __sanitizer_syscall_post_connect(res, ...) +#define __sanitizer_syscall_post_create_module(res, ...) +#define __sanitizer_syscall_post_creat(res, ...) +#define __sanitizer_syscall_post_delete_module(res, ...) +#define __sanitizer_syscall_post_dup2(res, ...) +#define __sanitizer_syscall_post_dup3(res, ...) +#define __sanitizer_syscall_post_dup(res, ...) +#define __sanitizer_syscall_post_epoll_create1(res, ...) +#define __sanitizer_syscall_post_epoll_create(res, ...) +#define __sanitizer_syscall_post_epoll_ctl_old(res, ...) +#define __sanitizer_syscall_post_epoll_ctl(res, ...) +#define __sanitizer_syscall_post_epoll_pwait(res, ...) +#define __sanitizer_syscall_post_epoll_wait_old(res, ...) +#define __sanitizer_syscall_post_epoll_wait(res, ...) +#define __sanitizer_syscall_post_eventfd2(res, ...) +#define __sanitizer_syscall_post_eventfd(res, ...) +#define __sanitizer_syscall_post_execve(res, ...) +#define __sanitizer_syscall_post_exit_group(res, ...) +#define __sanitizer_syscall_post_exit(res, ...) +#define __sanitizer_syscall_post_faccessat(res, ...) +#define __sanitizer_syscall_post_fadvise64_64(res, ...) +#define __sanitizer_syscall_post_fadvise64(res, ...) +#define __sanitizer_syscall_post_fallocate(res, ...) +#define __sanitizer_syscall_post_fanotify_init(res, ...) +#define __sanitizer_syscall_post_fanotify_mark(res, ...) +#define __sanitizer_syscall_post_fchdir(res, ...) +#define __sanitizer_syscall_post_fchmodat(res, ...) +#define __sanitizer_syscall_post_fchmod(res, ...) +#define __sanitizer_syscall_post_fchown32(res, ...) +#define __sanitizer_syscall_post_fchownat(res, ...) +#define __sanitizer_syscall_post_fchown(res, ...) +#define __sanitizer_syscall_post_fcntl64(res, ...) +#define __sanitizer_syscall_post_fcntl(res, ...) +#define __sanitizer_syscall_post_fdatasync(res, ...) +#define __sanitizer_syscall_post_fgetxattr(res, ...) +#define __sanitizer_syscall_post_flistxattr(res, ...) +#define __sanitizer_syscall_post_flock(res, ...) +#define __sanitizer_syscall_post_fork(res, ...) +#define __sanitizer_syscall_post_fremovexattr(res, ...) +#define __sanitizer_syscall_post_fsetxattr(res, ...) +#define __sanitizer_syscall_post_fstat64(res, ...) +#define __sanitizer_syscall_post_fstatat64(res, ...) +#define __sanitizer_syscall_post_fstatfs64(res, ...) +#define __sanitizer_syscall_post_fstatfs(res, ...) +#define __sanitizer_syscall_post_fstat(res, ...) +#define __sanitizer_syscall_post_fsync(res, ...) +#define __sanitizer_syscall_post_ftime(res, ...) +#define __sanitizer_syscall_post_ftruncate64(res, ...) +#define __sanitizer_syscall_post_ftruncate(res, ...) +#define __sanitizer_syscall_post_futex(res, ...) +#define __sanitizer_syscall_post_futimesat(res, ...) +#define __sanitizer_syscall_post_getcpu(res, ...) +#define __sanitizer_syscall_post_getcwd(res, ...) +#define __sanitizer_syscall_post_getegid32(res, ...) +#define __sanitizer_syscall_post_getegid(res, ...) +#define __sanitizer_syscall_post_geteuid32(res, ...) +#define __sanitizer_syscall_post_geteuid(res, ...) +#define __sanitizer_syscall_post_getgid32(res, ...) +#define __sanitizer_syscall_post_getgid(res, ...) +#define __sanitizer_syscall_post_getgroups32(res, ...) +#define __sanitizer_syscall_post_getgroups(res, ...) +#define __sanitizer_syscall_post_getitimer(res, ...) +#define __sanitizer_syscall_post_get_kernel_syms(res, ...) +#define __sanitizer_syscall_post_get_mempolicy(res, ...) +#define __sanitizer_syscall_post_getpeername(res, ...) +#define __sanitizer_syscall_post_getpgid(res, ...) +#define __sanitizer_syscall_post_getpgrp(res, ...) +#define __sanitizer_syscall_post_getpid(res, ...) +#define __sanitizer_syscall_post_getpmsg(res, ...) +#define __sanitizer_syscall_post_getppid(res, ...) +#define __sanitizer_syscall_post_getpriority(res, ...) +#define __sanitizer_syscall_post_getresgid32(res, ...) +#define __sanitizer_syscall_post_getresgid(res, ...) +#define __sanitizer_syscall_post_getresuid32(res, ...) +#define __sanitizer_syscall_post_getresuid(res, ...) +#define __sanitizer_syscall_post_getrlimit(res, ...) +#define __sanitizer_syscall_post_get_robust_list(res, ...) +#define __sanitizer_syscall_post_getrusage(res, ...) +#define __sanitizer_syscall_post_getsid(res, ...) +#define __sanitizer_syscall_post_getsockname(res, ...) +#define __sanitizer_syscall_post_getsockopt(res, ...) +#define __sanitizer_syscall_post_get_thread_area(res, ...) +#define __sanitizer_syscall_post_gettid(res, ...) +#define __sanitizer_syscall_post_gettimeofday(res, ...) +#define __sanitizer_syscall_post_getuid32(res, ...) +#define __sanitizer_syscall_post_getuid(res, ...) +#define __sanitizer_syscall_post_getxattr(res, ...) +#define __sanitizer_syscall_post_gtty(res, ...) +#define __sanitizer_syscall_post_idle(res, ...) +#define __sanitizer_syscall_post_init_module(res, ...) +#define __sanitizer_syscall_post_inotify_add_watch(res, ...) +#define __sanitizer_syscall_post_inotify_init1(res, ...) +#define __sanitizer_syscall_post_inotify_init(res, ...) +#define __sanitizer_syscall_post_inotify_rm_watch(res, ...) +#define __sanitizer_syscall_post_io_cancel(res, ...) +#define __sanitizer_syscall_post_ioctl(res, ...) +#define __sanitizer_syscall_post_io_destroy(res, ...) +#define __sanitizer_syscall_post_io_getevents(res, ...) +#define __sanitizer_syscall_post_ioperm(res, ...) +#define __sanitizer_syscall_post_iopl(res, ...) +#define __sanitizer_syscall_post_ioprio_get(res, ...) +#define __sanitizer_syscall_post_ioprio_set(res, ...) +#define __sanitizer_syscall_post_io_setup(res, ...) +#define __sanitizer_syscall_post_io_submit(res, ...) +#define __sanitizer_syscall_post_ipc(res, ...) +#define __sanitizer_syscall_post_kexec_load(res, ...) +#define __sanitizer_syscall_post_keyctl(res, ...) +#define __sanitizer_syscall_post_kill(res, ...) +#define __sanitizer_syscall_post_lchown32(res, ...) +#define __sanitizer_syscall_post_lchown(res, ...) +#define __sanitizer_syscall_post_lgetxattr(res, ...) +#define __sanitizer_syscall_post_linkat(res, ...) +#define __sanitizer_syscall_post_link(res, ...) +#define __sanitizer_syscall_post_listen(res, ...) +#define __sanitizer_syscall_post_listxattr(res, ...) +#define __sanitizer_syscall_post_llistxattr(res, ...) +#define __sanitizer_syscall_post__llseek(res, ...) +#define __sanitizer_syscall_post_lock(res, ...) +#define __sanitizer_syscall_post_lookup_dcookie(res, ...) +#define __sanitizer_syscall_post_lremovexattr(res, ...) +#define __sanitizer_syscall_post_lseek(res, ...) +#define __sanitizer_syscall_post_lsetxattr(res, ...) +#define __sanitizer_syscall_post_lstat64(res, ...) +#define __sanitizer_syscall_post_lstat(res, ...) +#define __sanitizer_syscall_post_madvise1(res, ...) +#define __sanitizer_syscall_post_madvise(res, ...) +#define __sanitizer_syscall_post_mbind(res, ...) +#define __sanitizer_syscall_post_migrate_pages(res, ...) +#define __sanitizer_syscall_post_mincore(res, ...) +#define __sanitizer_syscall_post_mkdirat(res, ...) +#define __sanitizer_syscall_post_mkdir(res, ...) +#define __sanitizer_syscall_post_mknodat(res, ...) +#define __sanitizer_syscall_post_mknod(res, ...) +#define __sanitizer_syscall_post_mlockall(res, ...) +#define __sanitizer_syscall_post_mlock(res, ...) +#define __sanitizer_syscall_post_mmap2(res, ...) +#define __sanitizer_syscall_post_mmap(res, ...) +#define __sanitizer_syscall_post_modify_ldt(res, ...) +#define __sanitizer_syscall_post_mount(res, ...) +#define __sanitizer_syscall_post_move_pages(res, ...) +#define __sanitizer_syscall_post_mprotect(res, ...) +#define __sanitizer_syscall_post_mpx(res, ...) +#define __sanitizer_syscall_post_mq_getsetattr(res, ...) +#define __sanitizer_syscall_post_mq_notify(res, ...) +#define __sanitizer_syscall_post_mq_open(res, ...) +#define __sanitizer_syscall_post_mq_timedreceive(res, ...) +#define __sanitizer_syscall_post_mq_timedsend(res, ...) +#define __sanitizer_syscall_post_mq_unlink(res, ...) +#define __sanitizer_syscall_post_mremap(res, ...) +#define __sanitizer_syscall_post_msgctl(res, ...) +#define __sanitizer_syscall_post_msgget(res, ...) +#define __sanitizer_syscall_post_msgrcv(res, ...) +#define __sanitizer_syscall_post_msgsnd(res, ...) +#define __sanitizer_syscall_post_msync(res, ...) +#define __sanitizer_syscall_post_munlockall(res, ...) +#define __sanitizer_syscall_post_munlock(res, ...) +#define __sanitizer_syscall_post_munmap(res, ...) +#define __sanitizer_syscall_post_name_to_handle_at(res, ...) +#define __sanitizer_syscall_post_nanosleep(res, ...) +#define __sanitizer_syscall_post_newfstatat(res, ...) +#define __sanitizer_syscall_post__newselect(res, ...) +#define __sanitizer_syscall_post_nfsservctl(res, ...) +#define __sanitizer_syscall_post_nice(res, ...) +#define __sanitizer_syscall_post_oldfstat(res, ...) +#define __sanitizer_syscall_post_oldlstat(res, ...) +#define __sanitizer_syscall_post_oldolduname(res, ...) +#define __sanitizer_syscall_post_oldstat(res, ...) +#define __sanitizer_syscall_post_olduname(res, ...) +#define __sanitizer_syscall_post_openat(res, ...) +#define __sanitizer_syscall_post_open_by_handle_at(res, ...) +#define __sanitizer_syscall_post_open(res, ...) +#define __sanitizer_syscall_post_pause(res, ...) +#define __sanitizer_syscall_post_perf_event_open(res, ...) +#define __sanitizer_syscall_post_personality(res, ...) +#define __sanitizer_syscall_post_pipe2(res, ...) +#define __sanitizer_syscall_post_pipe(res, ...) +#define __sanitizer_syscall_post_pivot_root(res, ...) +#define __sanitizer_syscall_post_poll(res, ...) +#define __sanitizer_syscall_post_ppoll(res, ...) +#define __sanitizer_syscall_post_prctl(res, ...) +#define __sanitizer_syscall_post_pread64(res, ...) +#define __sanitizer_syscall_post_preadv(res, ...) +#define __sanitizer_syscall_post_prlimit64(res, ...) +#define __sanitizer_syscall_post_process_vm_readv(res, ...) +#define __sanitizer_syscall_post_process_vm_writev(res, ...) +#define __sanitizer_syscall_post_profil(res, ...) +#define __sanitizer_syscall_post_prof(res, ...) +#define __sanitizer_syscall_post_pselect6(res, ...) +#define __sanitizer_syscall_post_ptrace(res, ...) +#define __sanitizer_syscall_post_putpmsg(res, ...) +#define __sanitizer_syscall_post_pwrite64(res, ...) +#define __sanitizer_syscall_post_pwritev(res, ...) +#define __sanitizer_syscall_post_query_module(res, ...) +#define __sanitizer_syscall_post_quotactl(res, ...) +#define __sanitizer_syscall_post_readahead(res, ...) +#define __sanitizer_syscall_post_readdir(res, ...) +#define __sanitizer_syscall_post_readlinkat(res, ...) +#define __sanitizer_syscall_post_readlink(res, ...) +#define __sanitizer_syscall_post_read(res, ...) +#define __sanitizer_syscall_post_readv(res, ...) +#define __sanitizer_syscall_post_reboot(res, ...) +#define __sanitizer_syscall_post_recvfrom(res, ...) +#define __sanitizer_syscall_post_recvmmsg(res, ...) +#define __sanitizer_syscall_post_remap_file_pages(res, ...) +#define __sanitizer_syscall_post_removexattr(res, ...) +#define __sanitizer_syscall_post_renameat(res, ...) +#define __sanitizer_syscall_post_rename(res, ...) +#define __sanitizer_syscall_post_request_key(res, ...) +#define __sanitizer_syscall_post_restart_syscall(res, ...) +#define __sanitizer_syscall_post_rmdir(res, ...) +#define __sanitizer_syscall_post_rt_sigaction(res, ...) +#define __sanitizer_syscall_post_rt_sigprocmask(res, ...) +#define __sanitizer_syscall_post_rt_sigqueueinfo(res, ...) +#define __sanitizer_syscall_post_rt_sigreturn(res, ...) +#define __sanitizer_syscall_post_rt_sigsuspend(res, ...) +#define __sanitizer_syscall_post_rt_sigtimedwait(res, ...) +#define __sanitizer_syscall_post_rt_tgsigqueueinfo(res, ...) +#define __sanitizer_syscall_post_sched_getaffinity(res, ...) +#define __sanitizer_syscall_post_sched_getparam(res, ...) +#define __sanitizer_syscall_post_sched_get_priority_max(res, ...) +#define __sanitizer_syscall_post_sched_get_priority_min(res, ...) +#define __sanitizer_syscall_post_sched_getscheduler(res, ...) +#define __sanitizer_syscall_post_sched_rr_get_interval(res, ...) +#define __sanitizer_syscall_post_sched_setaffinity(res, ...) +#define __sanitizer_syscall_post_sched_setparam(res, ...) +#define __sanitizer_syscall_post_sched_setscheduler(res, ...) +#define __sanitizer_syscall_post_sched_yield(res, ...) +#define __sanitizer_syscall_post_security(res, ...) +#define __sanitizer_syscall_post_select(res, ...) +#define __sanitizer_syscall_post_semctl(res, ...) +#define __sanitizer_syscall_post_semget(res, ...) +#define __sanitizer_syscall_post_semop(res, ...) +#define __sanitizer_syscall_post_semtimedop(res, ...) +#define __sanitizer_syscall_post_sendfile64(res, ...) +#define __sanitizer_syscall_post_sendfile(res, ...) +#define __sanitizer_syscall_post_sendmmsg(res, ...) +#define __sanitizer_syscall_post_sendmsg(res, ...) +#define __sanitizer_syscall_post_sendto(res, ...) +#define __sanitizer_syscall_post_setdomainname(res, ...) +#define __sanitizer_syscall_post_setfsgid32(res, ...) +#define __sanitizer_syscall_post_setfsgid(res, ...) +#define __sanitizer_syscall_post_setfsuid32(res, ...) +#define __sanitizer_syscall_post_setfsuid(res, ...) +#define __sanitizer_syscall_post_setgid32(res, ...) +#define __sanitizer_syscall_post_setgid(res, ...) +#define __sanitizer_syscall_post_setgroups32(res, ...) +#define __sanitizer_syscall_post_setgroups(res, ...) +#define __sanitizer_syscall_post_sethostname(res, ...) +#define __sanitizer_syscall_post_setitimer(res, ...) +#define __sanitizer_syscall_post_set_mempolicy(res, ...) +#define __sanitizer_syscall_post_setns(res, ...) +#define __sanitizer_syscall_post_setpgid(res, ...) +#define __sanitizer_syscall_post_setpriority(res, ...) +#define __sanitizer_syscall_post_setregid32(res, ...) +#define __sanitizer_syscall_post_setregid(res, ...) +#define __sanitizer_syscall_post_setresgid32(res, ...) +#define __sanitizer_syscall_post_setresgid(res, ...) +#define __sanitizer_syscall_post_setresuid32(res, ...) +#define __sanitizer_syscall_post_setresuid(res, ...) +#define __sanitizer_syscall_post_setreuid32(res, ...) +#define __sanitizer_syscall_post_setreuid(res, ...) +#define __sanitizer_syscall_post_setrlimit(res, ...) +#define __sanitizer_syscall_post_set_robust_list(res, ...) +#define __sanitizer_syscall_post_setsid(res, ...) +#define __sanitizer_syscall_post_setsockopt(res, ...) +#define __sanitizer_syscall_post_set_thread_area(res, ...) +#define __sanitizer_syscall_post_set_tid_address(res, ...) +#define __sanitizer_syscall_post_settimeofday(res, ...) +#define __sanitizer_syscall_post_setuid32(res, ...) +#define __sanitizer_syscall_post_setuid(res, ...) +#define __sanitizer_syscall_post_setxattr(res, ...) +#define __sanitizer_syscall_post_sgetmask(res, ...) +#define __sanitizer_syscall_post_shmat(res, ...) +#define __sanitizer_syscall_post_shmctl(res, ...) +#define __sanitizer_syscall_post_shmdt(res, ...) +#define __sanitizer_syscall_post_shmget(res, ...) +#define __sanitizer_syscall_post_shutdown(res, ...) +#define __sanitizer_syscall_post_sigaction(res, ...) +#define __sanitizer_syscall_post_sigaltstack(res, ...) +#define __sanitizer_syscall_post_signalfd4(res, ...) +#define __sanitizer_syscall_post_signalfd(res, ...) +#define __sanitizer_syscall_post_signal(res, ...) +#define __sanitizer_syscall_post_sigpending(res, ...) +#define __sanitizer_syscall_post_sigprocmask(res, ...) +#define __sanitizer_syscall_post_sigreturn(res, ...) +#define __sanitizer_syscall_post_sigsuspend(res, ...) +#define __sanitizer_syscall_post_socketcall(res, ...) +#define __sanitizer_syscall_post_socketpair(res, ...) +#define __sanitizer_syscall_post_socket(res, ...) +#define __sanitizer_syscall_post_splice(res, ...) +#define __sanitizer_syscall_post_ssetmask(res, ...) +#define __sanitizer_syscall_post_stat64(res, ...) +#define __sanitizer_syscall_post_statfs64(res, ...) +#define __sanitizer_syscall_post_statfs(res, ...) +#define __sanitizer_syscall_post_stat(res, ...) +#define __sanitizer_syscall_post_stime(res, ...) +#define __sanitizer_syscall_post_stty(res, ...) +#define __sanitizer_syscall_post_swapoff(res, ...) +#define __sanitizer_syscall_post_swapon(res, ...) +#define __sanitizer_syscall_post_symlinkat(res, ...) +#define __sanitizer_syscall_post_symlink(res, ...) +#define __sanitizer_syscall_post_sync_file_range(res, ...) +#define __sanitizer_syscall_post_syncfs(res, ...) +#define __sanitizer_syscall_post_sync(res, ...) +#define __sanitizer_syscall_post__sysctl(res, ...) +#define __sanitizer_syscall_post_sysfs(res, ...) +#define __sanitizer_syscall_post_sysinfo(res, ...) +#define __sanitizer_syscall_post_syslog(res, ...) +#define __sanitizer_syscall_post_tee(res, ...) +#define __sanitizer_syscall_post_tgkill(res, ...) +#define __sanitizer_syscall_post_timer_create(res, ...) +#define __sanitizer_syscall_post_timer_delete(res, ...) +#define __sanitizer_syscall_post_time(res, ...) +#define __sanitizer_syscall_post_timerfd_create(res, ...) +#define __sanitizer_syscall_post_timerfd_gettime(res, ...) +#define __sanitizer_syscall_post_timerfd_settime(res, ...) +#define __sanitizer_syscall_post_timer_getoverrun(res, ...) +#define __sanitizer_syscall_post_timer_gettime(res, ...) +#define __sanitizer_syscall_post_timer_settime(res, ...) +#define __sanitizer_syscall_post_times(res, ...) +#define __sanitizer_syscall_post_tkill(res, ...) +#define __sanitizer_syscall_post_truncate64(res, ...) +#define __sanitizer_syscall_post_truncate(res, ...) +#define __sanitizer_syscall_post_tuxcall(res, ...) +#define __sanitizer_syscall_post_ugetrlimit(res, ...) +#define __sanitizer_syscall_post_ulimit(res, ...) +#define __sanitizer_syscall_post_umask(res, ...) +#define __sanitizer_syscall_post_umount2(res, ...) +#define __sanitizer_syscall_post_umount(res, ...) +#define __sanitizer_syscall_post_uname(res, ...) +#define __sanitizer_syscall_post_unlinkat(res, ...) +#define __sanitizer_syscall_post_unlink(res, ...) +#define __sanitizer_syscall_post_unshare(res, ...) +#define __sanitizer_syscall_post_uselib(res, ...) +#define __sanitizer_syscall_post_ustat(res, ...) +#define __sanitizer_syscall_post_utimensat(res, ...) +#define __sanitizer_syscall_post_utime(res, ...) +#define __sanitizer_syscall_post_utimes(res, ...) +#define __sanitizer_syscall_post_vfork(res, ...) +#define __sanitizer_syscall_post_vhangup(res, ...) +#define __sanitizer_syscall_post_vm86old(res, ...) +#define __sanitizer_syscall_post_vm86(res, ...) +#define __sanitizer_syscall_post_vmsplice(res, ...) +#define __sanitizer_syscall_post_vserver(res, ...) +#define __sanitizer_syscall_post_waitid(res, ...) +#define __sanitizer_syscall_post_write(res, ...) +#define __sanitizer_syscall_post_writev(res, ...) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_LINUX_SYSCALL_HOOKS_H diff --git a/include/sanitizer/msan_interface.h b/include/sanitizer/msan_interface.h index 1a76dd60599f..9eff7b597b69 100644 --- a/include/sanitizer/msan_interface.h +++ b/include/sanitizer/msan_interface.h @@ -16,106 +16,87 @@ #include <sanitizer/common_interface_defs.h> -using __sanitizer::uptr; -using __sanitizer::sptr; -using __sanitizer::u32; - #ifdef __cplusplus extern "C" { #endif -// FIXME: document all interface functions. - -SANITIZER_INTERFACE_ATTRIBUTE -int __msan_get_track_origins(); - -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_init(); - -// Print a warning and maybe return. -// This function can die based on flags()->exit_code. -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_warning(); - -// Print a warning and die. -// Intrumentation inserts calls to this function when building in "fast" mode -// (i.e. -mllvm -msan-keep-going) -SANITIZER_INTERFACE_ATTRIBUTE __attribute__((noreturn)) -void __msan_warning_noreturn(); - -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_unpoison(void *a, uptr size); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_clear_and_unpoison(void *a, uptr size); -SANITIZER_INTERFACE_ATTRIBUTE -void* __msan_memcpy(void *dst, const void *src, uptr size); -SANITIZER_INTERFACE_ATTRIBUTE -void* __msan_memset(void *s, int c, uptr n); -SANITIZER_INTERFACE_ATTRIBUTE -void* __msan_memmove(void* dest, const void* src, uptr n); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_copy_poison(void *dst, const void *src, uptr size); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_copy_origin(void *dst, const void *src, uptr size); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_move_poison(void *dst, const void *src, uptr size); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_poison(void *a, uptr size); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_poison_stack(void *a, uptr size); - -// Copy size bytes from src to dst and unpoison the result. -// Useful to implement unsafe loads. -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_load_unpoisoned(void *src, uptr size, void *dst); - -// Returns the offset of the first (at least partially) poisoned byte, -// or -1 if the whole range is good. -SANITIZER_INTERFACE_ATTRIBUTE -sptr __msan_test_shadow(const void *x, uptr size); - -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_set_origin(void *a, uptr size, u32 origin); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_set_alloca_origin(void *a, uptr size, const char *descr); -SANITIZER_INTERFACE_ATTRIBUTE -u32 __msan_get_origin(void *a); - -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_clear_on_return(); - -// Default: -1 (don't exit on error). -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_set_exit_code(int exit_code); - -SANITIZER_INTERFACE_ATTRIBUTE -int __msan_set_poison_in_malloc(int do_poison); - -// For testing. -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_set_expect_umr(int expect_umr); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_break_optimization(void *x); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_print_shadow(const void *x, uptr size); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_print_param_shadow(); -SANITIZER_INTERFACE_ATTRIBUTE -int __msan_has_dynamic_component(); - -// Returns x such that %fs:x is the first byte of __msan_retval_tls. -SANITIZER_INTERFACE_ATTRIBUTE -int __msan_get_retval_tls_offset(); -SANITIZER_INTERFACE_ATTRIBUTE -int __msan_get_param_tls_offset(); - -// For testing. -SANITIZER_INTERFACE_ATTRIBUTE -u32 __msan_get_origin_tls(); -SANITIZER_INTERFACE_ATTRIBUTE -const char *__msan_get_origin_descr_if_stack(u32 id); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_partial_poison(void* data, void* shadow, uptr size); +#if __has_feature(memory_sanitizer) + /* Returns a string describing a stack origin. + Return NULL if the origin is invalid, or is not a stack origin. */ + const char *__msan_get_origin_descr_if_stack(uint32_t id); + + + /* Set raw origin for the memory range. */ + void __msan_set_origin(const void *a, size_t size, uint32_t origin); + + /* Get raw origin for an address. */ + uint32_t __msan_get_origin(const void *a); + + /* Returns non-zero if tracking origins. */ + int __msan_get_track_origins(); + + /* Returns the origin id of the latest UMR in the calling thread. */ + uint32_t __msan_get_umr_origin(); + + /* Make memory region fully initialized (without changing its contents). */ + void __msan_unpoison(const void *a, size_t size); + + /* Make memory region fully uninitialized (without changing its contents). */ + void __msan_poison(const void *a, size_t size); + + /* Make memory region partially uninitialized (without changing its contents). + */ + void __msan_partial_poison(const void* data, void* shadow, size_t size); + + /* Returns the offset of the first (at least partially) poisoned byte in the + memory range, or -1 if the whole range is good. */ + intptr_t __msan_test_shadow(const void *x, size_t size); + + /* Set exit code when error(s) were detected. + Value of 0 means don't change the program exit code. */ + void __msan_set_exit_code(int exit_code); + + /* For testing: + __msan_set_expect_umr(1); + ... some buggy code ... + __msan_set_expect_umr(0); + The last line will verify that a UMR happened. */ + void __msan_set_expect_umr(int expect_umr); + + /* Print shadow and origin for the memory range to stdout in a human-readable + format. */ + void __msan_print_shadow(const void *x, size_t size); + + /* Print current function arguments shadow and origin to stdout in a + human-readable format. */ + void __msan_print_param_shadow(); + + /* Returns true if running under a dynamic tool (DynamoRio-based). */ + int __msan_has_dynamic_component(); + + /* Tell MSan about newly allocated memory (ex.: custom allocator). + Memory will be marked uninitialized, with origin at the call site. */ + void __msan_allocated_memory(const void* data, size_t size); + +#else // __has_feature(memory_sanitizer) + +#define __msan_get_origin_descr_if_stack(id) ((const char*)0) +#define __msan_set_origin(a, size, origin) +#define __msan_get_origin(a) ((uint32_t)-1) +#define __msan_get_track_origins() (0) +#define __msan_get_umr_origin() ((uint32_t)-1) +#define __msan_unpoison(a, size) +#define __msan_poison(a, size) +#define __msan_partial_poison(data, shadow, size) +#define __msan_test_shadow(x, size) ((intptr_t)-1) +#define __msan_set_exit_code(exit_code) +#define __msan_set_expect_umr(expect_umr) +#define __msan_print_shadow(x, size) +#define __msan_print_param_shadow() +#define __msan_has_dynamic_component() (0) +#define __msan_allocated_memory(data, size) + +#endif // __has_feature(memory_sanitizer) #ifdef __cplusplus } // extern "C" diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index fa6d8abc65e6..f07ab1e1872b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -7,6 +7,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux") add_subdirectory(interception) add_subdirectory(sanitizer_common) if(NOT ANDROID) + add_subdirectory(profile) add_subdirectory(ubsan) endif() endif() @@ -14,10 +15,10 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND NOT ANDROID) # ThreadSanitizer and MemorySanitizer are supported on Linux only. add_subdirectory(tsan) add_subdirectory(msan) + add_subdirectory(msandr) + add_subdirectory(lsan) endif() -# FIXME: Add support for the profile library. - # The top-level lib directory contains a large amount of C code which provides # generic implementations of the core runtime library along with optimized # architecture-specific code in various subdirectories. @@ -36,6 +37,8 @@ set(GENERIC_SOURCES ashlti3.c ashrdi3.c ashrti3.c + # FIXME: atomic.c may only be compiled if host compiler understands _Atomic + # atomic.c clear_cache.c clzdi2.c clzsi2.c @@ -152,37 +155,36 @@ set(GENERIC_SOURCES umodti3.c ) -if(CAN_TARGET_x86_64) - add_library(clang_rt.x86_64 STATIC - x86_64/floatdidf.c - x86_64/floatdisf.c - x86_64/floatdixf.c - x86_64/floatundidf.S - x86_64/floatundisf.S - x86_64/floatundixf.S - ${GENERIC_SOURCES} - ) - set_target_properties(clang_rt.x86_64 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_x86_64_CFLAGS}") - add_clang_compiler_rt_libraries(clang_rt.x86_64) -endif() -if(CAN_TARGET_i386) - add_library(clang_rt.i386 STATIC - i386/ashldi3.S - i386/ashrdi3.S - i386/divdi3.S - i386/floatdidf.S - i386/floatdisf.S - i386/floatdixf.S - i386/floatundidf.S - i386/floatundisf.S - i386/floatundixf.S - i386/lshrdi3.S - i386/moddi3.S - i386/muldi3.S - i386/udivdi3.S - i386/umoddi3.S - ${GENERIC_SOURCES} - ) - set_target_properties(clang_rt.i386 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_i386_CFLAGS}") - add_clang_compiler_rt_libraries(clang_rt.i386) -endif() +set(x86_64_SOURCES + x86_64/floatdidf.c + x86_64/floatdisf.c + x86_64/floatdixf.c + x86_64/floatundidf.S + x86_64/floatundisf.S + x86_64/floatundixf.S + ${GENERIC_SOURCES}) + +set(i386_SOURCES + i386/ashldi3.S + i386/ashrdi3.S + i386/divdi3.S + i386/floatdidf.S + i386/floatdisf.S + i386/floatdixf.S + i386/floatundidf.S + i386/floatundisf.S + i386/floatundixf.S + i386/lshrdi3.S + i386/moddi3.S + i386/muldi3.S + i386/udivdi3.S + i386/umoddi3.S + ${GENERIC_SOURCES}) + +foreach(arch x86_64 i386) + if(CAN_TARGET_${arch}) + add_compiler_rt_static_runtime(clang_rt.${arch} ${arch} + SOURCES ${${arch}_SOURCES} + CFLAGS "-std=c99") + endif() +endforeach() diff --git a/lib/Makefile.mk b/lib/Makefile.mk index ea471e01b1e6..8054c35aa362 100644 --- a/lib/Makefile.mk +++ b/lib/Makefile.mk @@ -19,15 +19,12 @@ SubDirs += interception SubDirs += profile SubDirs += sanitizer_common SubDirs += tsan +SubDirs += msan SubDirs += ubsan - -# FIXME: We don't currently support building an atomic library, and as it must -# be a separate library from the runtime library, we need to remove its source -# code from the source files list. -ExcludedSources := atomic.c +SubDirs += lsan # Define the variables for this specific directory. -Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(filter-out $(ExcludedSources),$(notdir $(file)))) +Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file))) ObjNames := $(Sources:%.c=%.o) Implementation := Generic diff --git a/lib/apple_versioning.c b/lib/apple_versioning.c index e838d726fbb6..09f149f14cf7 100644 --- a/lib/apple_versioning.c +++ b/lib/apple_versioning.c @@ -13,6 +13,7 @@ #if __APPLE__ #if __arm__ #define NOT_HERE_BEFORE_10_6(sym) + #define NOT_HERE_IN_10_8_AND_EARLIER(sym) #elif __ppc__ #define NOT_HERE_BEFORE_10_6(sym) \ extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); \ @@ -27,6 +28,13 @@ __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \ __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NOT_HERE_IN_10_8_AND_EARLIER(sym) \ + extern const char sym##_tmp8 __asm("$ld$hide$os10.8$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp8 = 0; \ + extern const char sym##_tmp7 __asm("$ld$hide$os10.7$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp7 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp6 = 0; #endif /* __ppc__ */ @@ -143,6 +151,56 @@ NOT_HERE_BEFORE_10_6(__gcc_qsub) NOT_HERE_BEFORE_10_6(__trampoline_setup) #endif /* __ppc__ */ +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_compare_exchange) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_compare_exchange_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_compare_exchange_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_compare_exchange_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_compare_exchange_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_exchange) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_exchange_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_exchange_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_exchange_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_exchange_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_add_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_add_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_add_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_add_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_and_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_and_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_and_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_and_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_or_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_or_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_or_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_or_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_sub_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_sub_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_sub_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_sub_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_xor_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_xor_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_xor_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_xor_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_load) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_load_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_load_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_load_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_load_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_store) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_store_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_store_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_store_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_store_8) + + #if __arm__ && __DYNAMIC__ #define NOT_HERE_UNTIL_AFTER_4_3(sym) \ extern const char sym##_tmp1 __asm("$ld$hide$os3.0$_" #sym ); \ diff --git a/lib/arm/aeabi_dcmp.S b/lib/arm/aeabi_dcmp.S new file mode 100644 index 000000000000..c4d07727a380 --- /dev/null +++ b/lib/arm/aeabi_dcmp.S @@ -0,0 +1,39 @@ +//===-- aeabi_dcmp.S - EABI dcmp* implementation ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// int __aeabi_dcmp{eq,lt,le,ge,gt}(double a, double b) { +// int result = __{eq,lt,le,ge,gt}df2(a, b); +// if (result {==,<,<=,>=,>} 0) { +// return 1; +// } else { +// return 0; +// } +// } + +#define DEFINE_AEABI_DCMP(cond) \ + .syntax unified SEPARATOR \ + .align 2 SEPARATOR \ +DEFINE_COMPILERRT_FUNCTION(__aeabi_dcmp ## cond) \ + push { r4, lr } SEPARATOR \ + bl SYMBOL_NAME(__ ## cond ## df2) SEPARATOR \ + cmp r0, #0 SEPARATOR \ + b ## cond 1f SEPARATOR \ + mov r0, #0 SEPARATOR \ + pop { r4, pc } SEPARATOR \ +1: SEPARATOR \ + mov r0, #1 SEPARATOR \ + pop { r4, pc } + +DEFINE_AEABI_DCMP(eq) +DEFINE_AEABI_DCMP(lt) +DEFINE_AEABI_DCMP(le) +DEFINE_AEABI_DCMP(ge) +DEFINE_AEABI_DCMP(gt) diff --git a/lib/arm/aeabi_fcmp.S b/lib/arm/aeabi_fcmp.S new file mode 100644 index 000000000000..576a33f25542 --- /dev/null +++ b/lib/arm/aeabi_fcmp.S @@ -0,0 +1,39 @@ +//===-- aeabi_fcmp.S - EABI fcmp* implementation ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// int __aeabi_fcmp{eq,lt,le,ge,gt}(float a, float b) { +// int result = __{eq,lt,le,ge,gt}sf2(a, b); +// if (result {==,<,<=,>=,>} 0) { +// return 1; +// } else { +// return 0; +// } +// } + +#define DEFINE_AEABI_FCMP(cond) \ + .syntax unified SEPARATOR \ + .align 2 SEPARATOR \ +DEFINE_COMPILERRT_FUNCTION(__aeabi_fcmp ## cond) \ + push { r4, lr } SEPARATOR \ + bl SYMBOL_NAME(__ ## cond ## sf2) SEPARATOR \ + cmp r0, #0 SEPARATOR \ + b ## cond 1f SEPARATOR \ + mov r0, #0 SEPARATOR \ + pop { r4, pc } SEPARATOR \ +1: SEPARATOR \ + mov r0, #1 SEPARATOR \ + pop { r4, pc } + +DEFINE_AEABI_FCMP(eq) +DEFINE_AEABI_FCMP(lt) +DEFINE_AEABI_FCMP(le) +DEFINE_AEABI_FCMP(ge) +DEFINE_AEABI_FCMP(gt) diff --git a/lib/arm/divmodsi4.S b/lib/arm/divmodsi4.S index cec39a7926f9..d31e510c8f38 100644 --- a/lib/arm/divmodsi4.S +++ b/lib/arm/divmodsi4.S @@ -24,6 +24,18 @@ .syntax unified .align 3 DEFINE_COMPILERRT_FUNCTION(__divmodsi4) +#if __ARM_ARCH_7S__ + tst r1, r1 + beq LOCAL_LABEL(divzero) + mov r3, r0 + sdiv r0, r3, r1 + mls r1, r0, r1, r3 + str r1, [r2] + bx lr +LOCAL_LABEL(divzero): + mov r0, #0 + bx lr +#else ESTABLISH_FRAME // Set aside the sign of the quotient and modulus, and the address for the // modulus. @@ -45,3 +57,4 @@ DEFINE_COMPILERRT_FUNCTION(__divmodsi4) sub r1, r1, r5, asr #31 str r1, [r6] CLEAR_FRAME_AND_RETURN +#endif diff --git a/lib/arm/modsi3.S b/lib/arm/modsi3.S index a4cd2ee54e7b..04595011d0ec 100644 --- a/lib/arm/modsi3.S +++ b/lib/arm/modsi3.S @@ -23,6 +23,16 @@ .syntax unified .align 3 DEFINE_COMPILERRT_FUNCTION(__modsi3) +#if __ARM_ARCH_7S__ + tst r1, r1 + beq LOCAL_LABEL(divzero) + sdiv r2, r0, r1 + mls r0, r2, r1, r0 + bx lr +LOCAL_LABEL(divzero): + mov r0, #0 + bx lr +#else ESTABLISH_FRAME // Set aside the sign of the dividend. mov r4, r0 @@ -37,3 +47,4 @@ DEFINE_COMPILERRT_FUNCTION(__modsi3) eor r0, r0, r4, asr #31 sub r0, r0, r4, asr #31 CLEAR_FRAME_AND_RETURN +#endif diff --git a/lib/arm/udivmodsi4.S b/lib/arm/udivmodsi4.S index d164a751d089..9956cd48442f 100644 --- a/lib/arm/udivmodsi4.S +++ b/lib/arm/udivmodsi4.S @@ -31,6 +31,18 @@ .syntax unified .align 3 DEFINE_COMPILERRT_FUNCTION(__udivmodsi4) +#if __ARM_ARCH_7S__ + tst r1, r1 + beq LOCAL_LABEL(divzero) + mov r3, r0 + udiv r0, r3, r1 + mls r1, r0, r1, r3 + str r1, [r2] + bx lr +LOCAL_LABEL(divzero): + mov r0, #0 + bx lr +#else // We use a simple digit by digit algorithm; before we get into the actual // divide loop, we must calculate the left-shift amount necessary to align // the MSB of the divisor with that of the dividend (If this shift is @@ -78,3 +90,4 @@ LOCAL_LABEL(return): str a, [r2] mov r0, q CLEAR_FRAME_AND_RETURN +#endif diff --git a/lib/arm/umodsi3.S b/lib/arm/umodsi3.S index 3a2ab2b87751..328e7054b857 100644 --- a/lib/arm/umodsi3.S +++ b/lib/arm/umodsi3.S @@ -23,6 +23,16 @@ .syntax unified .align 3 DEFINE_COMPILERRT_FUNCTION(__umodsi3) +#if __ARM_ARCH_7S__ + tst r1, r1 + beq LOCAL_LABEL(divzero) + udiv r2, r0, r1 + mls r0, r2, r1, r0 + bx lr +LOCAL_LABEL(divzero): + mov r0, #0 + bx lr +#else // We use a simple digit by digit algorithm; before we get into the actual // divide loop, we must calculate the left-shift amount necessary to align // the MSB of the divisor with that of the dividend. @@ -56,3 +66,4 @@ LOCAL_LABEL(mainLoop): subs r, a, b movhs a, r bx lr +#endif diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt index 92cba6dee622..a567a4d3e970 100644 --- a/lib/asan/CMakeLists.txt +++ b/lib/asan/CMakeLists.txt @@ -1,7 +1,6 @@ # Build for the AddressSanitizer runtime support library. set(ASAN_SOURCES - asan_allocator.cc asan_allocator2.cc asan_fake_stack.cc asan_globals.cc @@ -14,60 +13,58 @@ set(ASAN_SOURCES asan_new_delete.cc asan_poisoning.cc asan_posix.cc + asan_preinit.cc asan_report.cc asan_rtl.cc asan_stack.cc asan_stats.cc asan_thread.cc - asan_thread_registry.cc asan_win.cc ) set(ASAN_DYLIB_SOURCES ${ASAN_SOURCES} - dynamic/asan_interceptors_dynamic.cc ) include_directories(..) -set(ASAN_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +set(ASAN_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + -fno-rtti) + +set(ASAN_COMMON_DEFINITIONS + ASAN_HAS_EXCEPTIONS=1) if(ANDROID) - set(ASAN_COMMON_DEFINITIONS - ASAN_HAS_EXCEPTIONS=1 + list(APPEND ASAN_COMMON_DEFINITIONS ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 ASAN_NEEDS_SEGV=0 - ASAN_LOW_MEMORY=1 - ) + ASAN_LOW_MEMORY=1) else() - set(ASAN_COMMON_DEFINITIONS - ASAN_HAS_EXCEPTIONS=1 - ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 - ASAN_NEEDS_SEGV=1 - ) + list(APPEND ASAN_COMMON_DEFINITIONS + ASAN_FLEXIBLE_MAPPING_AND_OFFSET=1 + ASAN_NEEDS_SEGV=1) endif() -set(ASAN_DYLIB_DEFINITIONS - ${ASAN_COMMON_DEFINITIONS} - MAC_INTERPOSE_FUNCTIONS=1 - ) - # Architectures supported by ASan. filter_available_targets(ASAN_SUPPORTED_ARCH - x86_64 i386) + x86_64 i386 powerpc64 powerpc) set(ASAN_RUNTIME_LIBRARIES) if(APPLE) # Build universal binary on APPLE. - add_library(clang_rt.asan_osx STATIC - ${ASAN_SOURCES} - $<TARGET_OBJECTS:RTInterception.osx> - $<TARGET_OBJECTS:RTSanitizerCommon.osx> - ) - set_target_compile_flags(clang_rt.asan_osx ${ASAN_CFLAGS}) - set_target_properties(clang_rt.asan_osx PROPERTIES - OSX_ARCHITECTURES "${ASAN_SUPPORTED_ARCH}") - list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_osx) + add_compiler_rt_osx_dynamic_runtime(clang_rt.asan_osx_dynamic + ARCH ${ASAN_SUPPORTED_ARCH} + SOURCES ${ASAN_DYLIB_SOURCES} + $<TARGET_OBJECTS:RTInterception.osx> + $<TARGET_OBJECTS:RTSanitizerCommon.osx> + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS} + # Dynamic lookup is needed because shadow scale and offset are + # provided by the instrumented modules. + LINKFLAGS "-framework Foundation" + "-undefined dynamic_lookup") + list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_osx_dynamic) elseif(ANDROID) add_library(clang_rt.asan-arm-android SHARED ${ASAN_SOURCES} @@ -75,44 +72,28 @@ elseif(ANDROID) $<TARGET_OBJECTS:RTSanitizerCommon.arm.android> ) set_target_compile_flags(clang_rt.asan-arm-android - ${ASAN_CFLAGS} - ) + ${ASAN_CFLAGS}) + set_property(TARGET clang_rt.asan-arm-android APPEND PROPERTY + COMPILE_DEFINITIONS ${ASAN_COMMON_DEFINITIONS}) target_link_libraries(clang_rt.asan-arm-android dl) list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-arm-android) else() # Otherwise, build separate libraries for each target. foreach(arch ${ASAN_SUPPORTED_ARCH}) - add_library(clang_rt.asan-${arch} STATIC - ${ASAN_SOURCES} - $<TARGET_OBJECTS:RTInterception.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>) - set_target_compile_flags(clang_rt.asan-${arch} - ${ASAN_CFLAGS} ${TARGET_${arch}_CFLAGS}) + add_compiler_rt_static_runtime(clang_rt.asan-${arch} ${arch} + SOURCES ${ASAN_SOURCES} + $<TARGET_OBJECTS:RTInterception.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + $<TARGET_OBJECTS:RTLSanCommon.${arch}> + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS} + SYMS asan.syms) list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-${arch}) endforeach() endif() -set_property(TARGET ${ASAN_RUNTIME_LIBRARIES} APPEND PROPERTY - COMPILE_DEFINITIONS ${ASAN_COMMON_DEFINITIONS}) -add_clang_compiler_rt_libraries(${ASAN_RUNTIME_LIBRARIES}) - -set(ASAN_DYNAMIC_RUNTIME_LIBRARIES) -if(APPLE) - # Build universal binary on APPLE. - add_library(clang_rt.asan_osx_dynamic SHARED - ${ASAN_DYLIB_SOURCES} - $<TARGET_OBJECTS:RTInterception.osx> - $<TARGET_OBJECTS:RTSanitizerCommon.osx> - ) - set_target_compile_flags(clang_rt.asan_osx_dynamic ${ASAN_CFLAGS}) - set_target_properties(clang_rt.asan_osx_dynamic PROPERTIES - COMPILE_DEFINITIONS "${ASAN_DYLIB_DEFINITIONS}" - OSX_ARCHITECTURES "${ASAN_SUPPORTED_ARCH}" - LINK_FLAGS "-framework Foundation") - list(APPEND ASAN_DYNAMIC_RUNTIME_LIBRARIES clang_rt.asan_osx_dynamic) -endif() -add_clang_compiler_rt_libraries(${ASAN_DYNAMIC_RUNTIME_LIBRARIES}) - +add_compiler_rt_resource_file(asan_blacklist asan_blacklist.txt) if(LLVM_INCLUDE_TESTS) add_subdirectory(tests) diff --git a/lib/asan/Makefile.mk b/lib/asan/Makefile.mk index af9602e8b242..97da64bec573 100644 --- a/lib/asan/Makefile.mk +++ b/lib/asan/Makefile.mk @@ -8,7 +8,7 @@ #===------------------------------------------------------------------------===# ModuleName := asan -SubDirs := dynamic +SubDirs := Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) ObjNames := $(Sources:%.cc=%.o) @@ -18,7 +18,6 @@ Implementation := Generic # FIXME: use automatic dependencies? Dependencies := $(wildcard $(Dir)/*.h) Dependencies += $(wildcard $(Dir)/../interception/*.h) -Dependencies += $(wildcard $(Dir)/../interception/mach_override/*.h) Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) # Define a convenience variable for all the asan functions. diff --git a/lib/asan/asan.syms b/lib/asan/asan.syms new file mode 100644 index 000000000000..fce367314093 --- /dev/null +++ b/lib/asan/asan.syms @@ -0,0 +1,5 @@ +{ + __asan_*; + __sanitizer_syscall_pre_*; + __sanitizer_syscall_post_*; +}; diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc deleted file mode 100644 index 30dd4ceddd88..000000000000 --- a/lib/asan/asan_allocator.cc +++ /dev/null @@ -1,810 +0,0 @@ -//===-- asan_allocator.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. -// -// Implementation of ASan's memory allocator. -// Evey piece of memory (AsanChunk) allocated by the allocator -// has a left redzone of REDZONE bytes and -// a right redzone such that the end of the chunk is aligned by REDZONE -// (i.e. the right redzone is between 0 and REDZONE-1). -// The left redzone is always poisoned. -// The right redzone is poisoned on malloc, the body is poisoned on free. -// Once freed, a chunk is moved to a quarantine (fifo list). -// After quarantine, a chunk is returned to freelists. -// -// The left redzone contains ASan's internal data and the stack trace of -// the malloc call. -// Once freed, the body of the chunk contains the stack trace of the free call. -// -//===----------------------------------------------------------------------===// -#include "asan_allocator.h" - -#if ASAN_ALLOCATOR_VERSION == 1 -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_mapping.h" -#include "asan_stats.h" -#include "asan_report.h" -#include "asan_thread.h" -#include "asan_thread_registry.h" -#include "sanitizer/asan_interface.h" -#include "sanitizer_common/sanitizer_atomic.h" -#include "sanitizer_common/sanitizer_mutex.h" - -namespace __asan { - -#define REDZONE ((uptr)(flags()->redzone)) -static const uptr kMinAllocSize = REDZONE * 2; -static const u64 kMaxAvailableRam = 128ULL << 30; // 128G -static const uptr kMaxThreadLocalQuarantine = 1 << 20; // 1M - -static const uptr kMinMmapSize = (ASAN_LOW_MEMORY) ? 4UL << 17 : 4UL << 20; -static const uptr kMaxSizeForThreadLocalFreeList = - (ASAN_LOW_MEMORY) ? 1 << 15 : 1 << 17; - -// Size classes less than kMallocSizeClassStep are powers of two. -// All other size classes are multiples of kMallocSizeClassStep. -static const uptr kMallocSizeClassStepLog = 26; -static const uptr kMallocSizeClassStep = 1UL << kMallocSizeClassStepLog; - -static const uptr kMaxAllowedMallocSize = - (SANITIZER_WORDSIZE == 32) ? 3UL << 30 : 8UL << 30; - -static inline uptr SizeClassToSize(u8 size_class) { - CHECK(size_class < kNumberOfSizeClasses); - if (size_class <= kMallocSizeClassStepLog) { - return 1UL << size_class; - } else { - return (size_class - kMallocSizeClassStepLog) * kMallocSizeClassStep; - } -} - -static inline u8 SizeToSizeClass(uptr size) { - u8 res = 0; - if (size <= kMallocSizeClassStep) { - uptr rounded = RoundUpToPowerOfTwo(size); - res = Log2(rounded); - } else { - res = ((size + kMallocSizeClassStep - 1) / kMallocSizeClassStep) - + kMallocSizeClassStepLog; - } - CHECK(res < kNumberOfSizeClasses); - CHECK(size <= SizeClassToSize(res)); - return res; -} - -// Given REDZONE bytes, we need to mark first size bytes -// as addressable and the rest REDZONE-size bytes as unaddressable. -static void PoisonHeapPartialRightRedzone(uptr mem, uptr size) { - CHECK(size <= REDZONE); - CHECK(IsAligned(mem, REDZONE)); - CHECK(IsPowerOfTwo(SHADOW_GRANULARITY)); - CHECK(IsPowerOfTwo(REDZONE)); - CHECK(REDZONE >= SHADOW_GRANULARITY); - PoisonShadowPartialRightRedzone(mem, size, REDZONE, - kAsanHeapRightRedzoneMagic); -} - -static u8 *MmapNewPagesAndPoisonShadow(uptr size) { - CHECK(IsAligned(size, GetPageSizeCached())); - u8 *res = (u8*)MmapOrDie(size, __FUNCTION__); - PoisonShadow((uptr)res, size, kAsanHeapLeftRedzoneMagic); - if (flags()->debug) { - Printf("ASAN_MMAP: [%p, %p)\n", res, res + size); - } - return res; -} - -// Every chunk of memory allocated by this allocator can be in one of 3 states: -// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated. -// CHUNK_ALLOCATED: the chunk is allocated and not yet freed. -// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone. -// -// The pseudo state CHUNK_MEMALIGN is used to mark that the address is not -// the beginning of a AsanChunk (in which the actual chunk resides at -// this - this->used_size). -// -// The magic numbers for the enum values are taken randomly. -enum { - CHUNK_AVAILABLE = 0x57, - CHUNK_ALLOCATED = 0x32, - CHUNK_QUARANTINE = 0x19, - CHUNK_MEMALIGN = 0xDC -}; - -struct ChunkBase { - // First 8 bytes. - uptr chunk_state : 8; - uptr alloc_tid : 24; - uptr size_class : 8; - uptr free_tid : 24; - - // Second 8 bytes. - uptr alignment_log : 8; - uptr alloc_type : 2; - uptr used_size : FIRST_32_SECOND_64(32, 54); // Size requested by the user. - - // This field may overlap with the user area and thus should not - // be used while the chunk is in CHUNK_ALLOCATED state. - AsanChunk *next; - - // Typically the beginning of the user-accessible memory is 'this'+REDZONE - // and is also aligned by REDZONE. However, if the memory is allocated - // by memalign, the alignment might be higher and the user-accessible memory - // starts at the first properly aligned address after 'this'. - uptr Beg() { return RoundUpTo((uptr)this + 1, 1 << alignment_log); } - uptr Size() { return SizeClassToSize(size_class); } - u8 SizeClass() { return size_class; } -}; - -struct AsanChunk: public ChunkBase { - u32 *compressed_alloc_stack() { - return (u32*)((uptr)this + sizeof(ChunkBase)); - } - u32 *compressed_free_stack() { - return (u32*)((uptr)this + Max((uptr)REDZONE, (uptr)sizeof(ChunkBase))); - } - - // The left redzone after the ChunkBase is given to the alloc stack trace. - uptr compressed_alloc_stack_size() { - if (REDZONE < sizeof(ChunkBase)) return 0; - return (REDZONE - sizeof(ChunkBase)) / sizeof(u32); - } - uptr compressed_free_stack_size() { - if (REDZONE < sizeof(ChunkBase)) return 0; - return (REDZONE) / sizeof(u32); - } -}; - -uptr AsanChunkView::Beg() { return chunk_->Beg(); } -uptr AsanChunkView::End() { return Beg() + UsedSize(); } -uptr AsanChunkView::UsedSize() { return chunk_->used_size; } -uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; } -uptr AsanChunkView::FreeTid() { return chunk_->free_tid; } - -void AsanChunkView::GetAllocStack(StackTrace *stack) { - StackTrace::UncompressStack(stack, chunk_->compressed_alloc_stack(), - chunk_->compressed_alloc_stack_size()); -} - -void AsanChunkView::GetFreeStack(StackTrace *stack) { - StackTrace::UncompressStack(stack, chunk_->compressed_free_stack(), - chunk_->compressed_free_stack_size()); -} - -static AsanChunk *PtrToChunk(uptr ptr) { - AsanChunk *m = (AsanChunk*)(ptr - REDZONE); - if (m->chunk_state == CHUNK_MEMALIGN) { - m = (AsanChunk*)((uptr)m - m->used_size); - } - return m; -} - -void AsanChunkFifoList::PushList(AsanChunkFifoList *q) { - CHECK(q->size() > 0); - size_ += q->size(); - append_back(q); - q->clear(); -} - -void AsanChunkFifoList::Push(AsanChunk *n) { - push_back(n); - size_ += n->Size(); -} - -// Interesting performance observation: this function takes up to 15% of overal -// allocator time. That's because *first_ has been evicted from cache long time -// ago. Not sure if we can or want to do anything with this. -AsanChunk *AsanChunkFifoList::Pop() { - CHECK(first_); - AsanChunk *res = front(); - size_ -= res->Size(); - pop_front(); - return res; -} - -// All pages we ever allocated. -struct PageGroup { - uptr beg; - uptr end; - uptr size_of_chunk; - uptr last_chunk; - bool InRange(uptr addr) { - return addr >= beg && addr < end; - } -}; - -class MallocInfo { - public: - explicit MallocInfo(LinkerInitialized x) : mu_(x) { } - - AsanChunk *AllocateChunks(u8 size_class, uptr n_chunks) { - AsanChunk *m = 0; - AsanChunk **fl = &free_lists_[size_class]; - { - BlockingMutexLock lock(&mu_); - for (uptr i = 0; i < n_chunks; i++) { - if (!(*fl)) { - *fl = GetNewChunks(size_class); - } - AsanChunk *t = *fl; - *fl = t->next; - t->next = m; - CHECK(t->chunk_state == CHUNK_AVAILABLE); - m = t; - } - } - return m; - } - - void SwallowThreadLocalMallocStorage(AsanThreadLocalMallocStorage *x, - bool eat_free_lists) { - CHECK(flags()->quarantine_size > 0); - BlockingMutexLock lock(&mu_); - AsanChunkFifoList *q = &x->quarantine_; - if (q->size() > 0) { - quarantine_.PushList(q); - while (quarantine_.size() > (uptr)flags()->quarantine_size) { - QuarantinePop(); - } - } - if (eat_free_lists) { - for (uptr size_class = 0; size_class < kNumberOfSizeClasses; - size_class++) { - AsanChunk *m = x->free_lists_[size_class]; - while (m) { - AsanChunk *t = m->next; - m->next = free_lists_[size_class]; - free_lists_[size_class] = m; - m = t; - } - x->free_lists_[size_class] = 0; - } - } - } - - void BypassThreadLocalQuarantine(AsanChunk *chunk) { - BlockingMutexLock lock(&mu_); - quarantine_.Push(chunk); - } - - AsanChunk *FindChunkByAddr(uptr addr) { - BlockingMutexLock lock(&mu_); - return FindChunkByAddrUnlocked(addr); - } - - uptr AllocationSize(uptr ptr) { - if (!ptr) return 0; - BlockingMutexLock lock(&mu_); - - // Make sure this is our chunk and |ptr| actually points to the beginning - // of the allocated memory. - AsanChunk *m = FindChunkByAddrUnlocked(ptr); - if (!m || m->Beg() != ptr) return 0; - - if (m->chunk_state == CHUNK_ALLOCATED) { - return m->used_size; - } else { - return 0; - } - } - - void ForceLock() { - mu_.Lock(); - } - - void ForceUnlock() { - mu_.Unlock(); - } - - void PrintStatus() { - BlockingMutexLock lock(&mu_); - uptr malloced = 0; - - Printf(" MallocInfo: in quarantine: %zu malloced: %zu; ", - quarantine_.size() >> 20, malloced >> 20); - for (uptr j = 1; j < kNumberOfSizeClasses; j++) { - AsanChunk *i = free_lists_[j]; - if (!i) continue; - uptr t = 0; - for (; i; i = i->next) { - t += i->Size(); - } - Printf("%zu:%zu ", j, t >> 20); - } - Printf("\n"); - } - - PageGroup *FindPageGroup(uptr addr) { - BlockingMutexLock lock(&mu_); - return FindPageGroupUnlocked(addr); - } - - private: - PageGroup *FindPageGroupUnlocked(uptr addr) { - int n = atomic_load(&n_page_groups_, memory_order_relaxed); - // If the page groups are not sorted yet, sort them. - if (n_sorted_page_groups_ < n) { - SortArray((uptr*)page_groups_, n); - n_sorted_page_groups_ = n; - } - // Binary search over the page groups. - int beg = 0, end = n; - while (beg < end) { - int med = (beg + end) / 2; - uptr g = (uptr)page_groups_[med]; - if (addr > g) { - // 'g' points to the end of the group, so 'addr' - // may not belong to page_groups_[med] or any previous group. - beg = med + 1; - } else { - // 'addr' may belong to page_groups_[med] or a previous group. - end = med; - } - } - if (beg >= n) - return 0; - PageGroup *g = page_groups_[beg]; - CHECK(g); - if (g->InRange(addr)) - return g; - return 0; - } - - // We have an address between two chunks, and we want to report just one. - AsanChunk *ChooseChunk(uptr addr, - AsanChunk *left_chunk, AsanChunk *right_chunk) { - // Prefer an allocated chunk or a chunk from quarantine. - if (left_chunk->chunk_state == CHUNK_AVAILABLE && - right_chunk->chunk_state != CHUNK_AVAILABLE) - return right_chunk; - if (right_chunk->chunk_state == CHUNK_AVAILABLE && - left_chunk->chunk_state != CHUNK_AVAILABLE) - return left_chunk; - // Choose based on offset. - uptr l_offset = 0, r_offset = 0; - CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); - CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); - if (l_offset < r_offset) - return left_chunk; - return right_chunk; - } - - AsanChunk *FindChunkByAddrUnlocked(uptr addr) { - PageGroup *g = FindPageGroupUnlocked(addr); - if (!g) return 0; - CHECK(g->size_of_chunk); - uptr offset_from_beg = addr - g->beg; - uptr this_chunk_addr = g->beg + - (offset_from_beg / g->size_of_chunk) * g->size_of_chunk; - CHECK(g->InRange(this_chunk_addr)); - AsanChunk *m = (AsanChunk*)this_chunk_addr; - CHECK(m->chunk_state == CHUNK_ALLOCATED || - m->chunk_state == CHUNK_AVAILABLE || - m->chunk_state == CHUNK_QUARANTINE); - uptr offset = 0; - AsanChunkView m_view(m); - if (m_view.AddrIsInside(addr, 1, &offset)) - return m; - - if (m_view.AddrIsAtRight(addr, 1, &offset)) { - if (this_chunk_addr == g->last_chunk) // rightmost chunk - return m; - uptr right_chunk_addr = this_chunk_addr + g->size_of_chunk; - CHECK(g->InRange(right_chunk_addr)); - return ChooseChunk(addr, m, (AsanChunk*)right_chunk_addr); - } else { - CHECK(m_view.AddrIsAtLeft(addr, 1, &offset)); - if (this_chunk_addr == g->beg) // leftmost chunk - return m; - uptr left_chunk_addr = this_chunk_addr - g->size_of_chunk; - CHECK(g->InRange(left_chunk_addr)); - return ChooseChunk(addr, (AsanChunk*)left_chunk_addr, m); - } - } - - void QuarantinePop() { - CHECK(quarantine_.size() > 0); - AsanChunk *m = quarantine_.Pop(); - CHECK(m); - // if (F_v >= 2) Printf("MallocInfo::pop %p\n", m); - - CHECK(m->chunk_state == CHUNK_QUARANTINE); - m->chunk_state = CHUNK_AVAILABLE; - PoisonShadow((uptr)m, m->Size(), kAsanHeapLeftRedzoneMagic); - CHECK(m->alloc_tid >= 0); - CHECK(m->free_tid >= 0); - - uptr size_class = m->SizeClass(); - m->next = free_lists_[size_class]; - free_lists_[size_class] = m; - - // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); - thread_stats.real_frees++; - thread_stats.really_freed += m->used_size; - thread_stats.really_freed_redzones += m->Size() - m->used_size; - thread_stats.really_freed_by_size[m->SizeClass()]++; - } - - // Get a list of newly allocated chunks. - AsanChunk *GetNewChunks(u8 size_class) { - uptr size = SizeClassToSize(size_class); - CHECK(IsPowerOfTwo(kMinMmapSize)); - CHECK(size < kMinMmapSize || (size % kMinMmapSize) == 0); - uptr mmap_size = Max(size, kMinMmapSize); - uptr n_chunks = mmap_size / size; - CHECK(n_chunks * size == mmap_size); - uptr PageSize = GetPageSizeCached(); - if (size < PageSize) { - // Size is small, just poison the last chunk. - n_chunks--; - } else { - // Size is large, allocate an extra page at right and poison it. - mmap_size += PageSize; - } - CHECK(n_chunks > 0); - u8 *mem = MmapNewPagesAndPoisonShadow(mmap_size); - - // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); - thread_stats.mmaps++; - thread_stats.mmaped += mmap_size; - thread_stats.mmaped_by_size[size_class] += n_chunks; - - AsanChunk *res = 0; - for (uptr i = 0; i < n_chunks; i++) { - AsanChunk *m = (AsanChunk*)(mem + i * size); - m->chunk_state = CHUNK_AVAILABLE; - m->size_class = size_class; - m->next = res; - res = m; - } - PageGroup *pg = (PageGroup*)(mem + n_chunks * size); - // This memory is already poisoned, no need to poison it again. - pg->beg = (uptr)mem; - pg->end = pg->beg + mmap_size; - pg->size_of_chunk = size; - pg->last_chunk = (uptr)(mem + size * (n_chunks - 1)); - int idx = atomic_fetch_add(&n_page_groups_, 1, memory_order_relaxed); - CHECK(idx < (int)ARRAY_SIZE(page_groups_)); - page_groups_[idx] = pg; - return res; - } - - AsanChunk *free_lists_[kNumberOfSizeClasses]; - AsanChunkFifoList quarantine_; - BlockingMutex mu_; - - PageGroup *page_groups_[kMaxAvailableRam / kMinMmapSize]; - atomic_uint32_t n_page_groups_; - int n_sorted_page_groups_; -}; - -static MallocInfo malloc_info(LINKER_INITIALIZED); - -void AsanThreadLocalMallocStorage::CommitBack() { - malloc_info.SwallowThreadLocalMallocStorage(this, true); -} - -AsanChunkView FindHeapChunkByAddress(uptr address) { - return AsanChunkView(malloc_info.FindChunkByAddr(address)); -} - -static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack, - AllocType alloc_type) { - __asan_init(); - CHECK(stack); - if (size == 0) { - size = 1; // TODO(kcc): do something smarter - } - CHECK(IsPowerOfTwo(alignment)); - uptr rounded_size = RoundUpTo(size, REDZONE); - uptr needed_size = rounded_size + REDZONE; - if (alignment > REDZONE) { - needed_size += alignment; - } - CHECK(IsAligned(needed_size, REDZONE)); - if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { - Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", - (void*)size); - return 0; - } - - u8 size_class = SizeToSizeClass(needed_size); - uptr size_to_allocate = SizeClassToSize(size_class); - CHECK(size_to_allocate >= kMinAllocSize); - CHECK(size_to_allocate >= needed_size); - CHECK(IsAligned(size_to_allocate, REDZONE)); - - if (flags()->verbosity >= 3) { - Printf("Allocate align: %zu size: %zu class: %u real: %zu\n", - alignment, size, size_class, size_to_allocate); - } - - AsanThread *t = asanThreadRegistry().GetCurrent(); - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); - // Statistics - thread_stats.mallocs++; - thread_stats.malloced += size; - thread_stats.malloced_redzones += size_to_allocate - size; - thread_stats.malloced_by_size[size_class]++; - - AsanChunk *m = 0; - if (!t || size_to_allocate >= kMaxSizeForThreadLocalFreeList) { - // get directly from global storage. - m = malloc_info.AllocateChunks(size_class, 1); - thread_stats.malloc_large++; - } else { - // get from the thread-local storage. - AsanChunk **fl = &t->malloc_storage().free_lists_[size_class]; - if (!*fl) { - uptr n_new_chunks = kMaxSizeForThreadLocalFreeList / size_to_allocate; - *fl = malloc_info.AllocateChunks(size_class, n_new_chunks); - thread_stats.malloc_small_slow++; - } - m = *fl; - *fl = (*fl)->next; - } - CHECK(m); - CHECK(m->chunk_state == CHUNK_AVAILABLE); - m->chunk_state = CHUNK_ALLOCATED; - m->alloc_type = alloc_type; - m->next = 0; - CHECK(m->Size() == size_to_allocate); - uptr addr = (uptr)m + REDZONE; - CHECK(addr <= (uptr)m->compressed_free_stack()); - - if (alignment > REDZONE && (addr & (alignment - 1))) { - addr = RoundUpTo(addr, alignment); - CHECK((addr & (alignment - 1)) == 0); - AsanChunk *p = (AsanChunk*)(addr - REDZONE); - p->chunk_state = CHUNK_MEMALIGN; - p->used_size = (uptr)p - (uptr)m; - m->alignment_log = Log2(alignment); - CHECK(m->Beg() == addr); - } else { - m->alignment_log = Log2(REDZONE); - } - CHECK(m == PtrToChunk(addr)); - m->used_size = size; - CHECK(m->Beg() == addr); - m->alloc_tid = t ? t->tid() : 0; - m->free_tid = kInvalidTid; - StackTrace::CompressStack(stack, m->compressed_alloc_stack(), - m->compressed_alloc_stack_size()); - PoisonShadow(addr, rounded_size, 0); - if (size < rounded_size) { - PoisonHeapPartialRightRedzone(addr + rounded_size - REDZONE, - size & (REDZONE - 1)); - } - if (size <= (uptr)(flags()->max_malloc_fill_size)) { - REAL(memset)((void*)addr, 0, rounded_size); - } - return (u8*)addr; -} - -static void Deallocate(u8 *ptr, StackTrace *stack, AllocType alloc_type) { - if (!ptr) return; - CHECK(stack); - - if (flags()->debug) { - CHECK(malloc_info.FindPageGroup((uptr)ptr)); - } - - // Printf("Deallocate %p\n", ptr); - AsanChunk *m = PtrToChunk((uptr)ptr); - - // Flip the chunk_state atomically to avoid race on double-free. - u8 old_chunk_state = atomic_exchange((atomic_uint8_t*)m, CHUNK_QUARANTINE, - memory_order_acq_rel); - - if (old_chunk_state == CHUNK_QUARANTINE) { - ReportDoubleFree((uptr)ptr, stack); - } else if (old_chunk_state != CHUNK_ALLOCATED) { - ReportFreeNotMalloced((uptr)ptr, stack); - } - CHECK(old_chunk_state == CHUNK_ALLOCATED); - if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch) - ReportAllocTypeMismatch((uptr)ptr, stack, - (AllocType)m->alloc_type, (AllocType)alloc_type); - // With REDZONE==16 m->next is in the user area, otherwise it should be 0. - CHECK(REDZONE <= 16 || !m->next); - CHECK(m->free_tid == kInvalidTid); - CHECK(m->alloc_tid >= 0); - AsanThread *t = asanThreadRegistry().GetCurrent(); - m->free_tid = t ? t->tid() : 0; - StackTrace::CompressStack(stack, m->compressed_free_stack(), - m->compressed_free_stack_size()); - uptr rounded_size = RoundUpTo(m->used_size, REDZONE); - PoisonShadow((uptr)ptr, rounded_size, kAsanHeapFreeMagic); - - // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); - thread_stats.frees++; - thread_stats.freed += m->used_size; - thread_stats.freed_by_size[m->SizeClass()]++; - - CHECK(m->chunk_state == CHUNK_QUARANTINE); - - if (t) { - AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); - ms->quarantine_.Push(m); - - if (ms->quarantine_.size() > kMaxThreadLocalQuarantine) { - malloc_info.SwallowThreadLocalMallocStorage(ms, false); - } - } else { - malloc_info.BypassThreadLocalQuarantine(m); - } -} - -static u8 *Reallocate(u8 *old_ptr, uptr new_size, - StackTrace *stack) { - CHECK(old_ptr && new_size); - - // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); - thread_stats.reallocs++; - thread_stats.realloced += new_size; - - AsanChunk *m = PtrToChunk((uptr)old_ptr); - CHECK(m->chunk_state == CHUNK_ALLOCATED); - uptr old_size = m->used_size; - uptr memcpy_size = Min(new_size, old_size); - u8 *new_ptr = Allocate(0, new_size, stack, FROM_MALLOC); - if (new_ptr) { - CHECK(REAL(memcpy) != 0); - REAL(memcpy)(new_ptr, old_ptr, memcpy_size); - Deallocate(old_ptr, stack, FROM_MALLOC); - } - return new_ptr; -} - -} // namespace __asan - -#if !SANITIZER_SUPPORTS_WEAK_HOOKS -// Provide default (no-op) implementation of malloc hooks. -extern "C" { -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE -void __asan_malloc_hook(void *ptr, uptr size) { - (void)ptr; - (void)size; -} -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE -void __asan_free_hook(void *ptr) { - (void)ptr; -} -} // extern "C" -#endif - -namespace __asan { - -void PrintInternalAllocatorStats() { -} - -SANITIZER_INTERFACE_ATTRIBUTE -void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, - AllocType alloc_type) { - void *ptr = (void*)Allocate(alignment, size, stack, alloc_type); - ASAN_MALLOC_HOOK(ptr, size); - return ptr; -} - -SANITIZER_INTERFACE_ATTRIBUTE -void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { - ASAN_FREE_HOOK(ptr); - Deallocate((u8*)ptr, stack, alloc_type); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void *asan_malloc(uptr size, StackTrace *stack) { - void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC); - ASAN_MALLOC_HOOK(ptr, size); - return ptr; -} - -void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { - void *ptr = (void*)Allocate(0, nmemb * size, stack, FROM_MALLOC); - if (ptr) - REAL(memset)(ptr, 0, nmemb * size); - ASAN_MALLOC_HOOK(ptr, size); - return ptr; -} - -void *asan_realloc(void *p, uptr size, StackTrace *stack) { - if (p == 0) { - void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC); - ASAN_MALLOC_HOOK(ptr, size); - return ptr; - } else if (size == 0) { - ASAN_FREE_HOOK(p); - Deallocate((u8*)p, stack, FROM_MALLOC); - return 0; - } - return Reallocate((u8*)p, size, stack); -} - -void *asan_valloc(uptr size, StackTrace *stack) { - void *ptr = (void*)Allocate(GetPageSizeCached(), size, stack, FROM_MALLOC); - ASAN_MALLOC_HOOK(ptr, size); - return ptr; -} - -void *asan_pvalloc(uptr size, StackTrace *stack) { - uptr PageSize = GetPageSizeCached(); - size = RoundUpTo(size, PageSize); - if (size == 0) { - // pvalloc(0) should allocate one page. - size = PageSize; - } - void *ptr = (void*)Allocate(PageSize, size, stack, FROM_MALLOC); - ASAN_MALLOC_HOOK(ptr, size); - return ptr; -} - -int asan_posix_memalign(void **memptr, uptr alignment, uptr size, - StackTrace *stack) { - void *ptr = Allocate(alignment, size, stack, FROM_MALLOC); - CHECK(IsAligned((uptr)ptr, alignment)); - ASAN_MALLOC_HOOK(ptr, size); - *memptr = ptr; - return 0; -} - -uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) { - CHECK(stack); - if (ptr == 0) return 0; - uptr usable_size = malloc_info.AllocationSize((uptr)ptr); - if (flags()->check_malloc_usable_size && (usable_size == 0)) { - ReportMallocUsableSizeNotOwned((uptr)ptr, stack); - } - return usable_size; -} - -uptr asan_mz_size(const void *ptr) { - return malloc_info.AllocationSize((uptr)ptr); -} - -void asan_mz_force_lock() { - malloc_info.ForceLock(); -} - -void asan_mz_force_unlock() { - malloc_info.ForceUnlock(); -} - -} // namespace __asan - -// ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - -// ASan allocator doesn't reserve extra bytes, so normally we would -// just return "size". -uptr __asan_get_estimated_allocated_size(uptr size) { - if (size == 0) return 1; - return Min(size, kMaxAllowedMallocSize); -} - -bool __asan_get_ownership(const void *p) { - return malloc_info.AllocationSize((uptr)p) > 0; -} - -uptr __asan_get_allocated_size(const void *p) { - if (p == 0) return 0; - uptr allocated_size = malloc_info.AllocationSize((uptr)p); - // Die if p is not malloced or if it is already freed. - if (allocated_size == 0) { - GET_STACK_TRACE_FATAL_HERE; - ReportAsanGetAllocatedSizeNotOwned((uptr)p, &stack); - } - return allocated_size; -} -#endif // ASAN_ALLOCATOR_VERSION diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h index cca24edad81f..f817ce352ee2 100644 --- a/lib/asan/asan_allocator.h +++ b/lib/asan/asan_allocator.h @@ -9,7 +9,7 @@ // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_allocator.cc. +// ASan-private header for asan_allocator2.cc. //===----------------------------------------------------------------------===// #ifndef ASAN_ALLOCATOR_H @@ -19,18 +19,6 @@ #include "asan_interceptors.h" #include "sanitizer_common/sanitizer_list.h" -// We are in the process of transitioning from the old allocator (version 1) -// to a new one (version 2). The change is quite intrusive so both allocators -// will co-exist in the source base for a while. The actual allocator is chosen -// at build time by redefining this macro. -#ifndef ASAN_ALLOCATOR_VERSION -# if ASAN_LINUX && !ASAN_ANDROID -# define ASAN_ALLOCATOR_VERSION 2 -# else -# define ASAN_ALLOCATOR_VERSION 1 -# endif -#endif // ASAN_ALLOCATOR_VERSION - namespace __asan { enum AllocType { @@ -42,6 +30,8 @@ enum AllocType { static const uptr kNumberOfSizeClasses = 255; struct AsanChunk; +void InitializeAllocator(); + class AsanChunkView { public: explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {} @@ -53,14 +43,14 @@ class AsanChunkView { uptr FreeTid(); void GetAllocStack(StackTrace *stack); void GetFreeStack(StackTrace *stack); - bool AddrIsInside(uptr addr, uptr access_size, uptr *offset) { + bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) { if (addr >= Beg() && (addr + access_size) <= End()) { *offset = addr - Beg(); return true; } return false; } - bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) { + bool AddrIsAtLeft(uptr addr, uptr access_size, sptr *offset) { (void)access_size; if (addr < Beg()) { *offset = Beg() - addr; @@ -68,12 +58,9 @@ class AsanChunkView { } return false; } - bool AddrIsAtRight(uptr addr, uptr access_size, uptr *offset) { - if (addr + access_size >= End()) { - if (addr <= End()) - *offset = 0; - else - *offset = addr - End(); + bool AddrIsAtRight(uptr addr, uptr access_size, sptr *offset) { + if (addr + access_size > End()) { + *offset = addr - End(); return true; } return false; @@ -104,109 +91,17 @@ class AsanChunkFifoList: public IntrusiveList<AsanChunk> { struct AsanThreadLocalMallocStorage { explicit AsanThreadLocalMallocStorage(LinkerInitialized x) -#if ASAN_ALLOCATOR_VERSION == 1 - : quarantine_(x) -#endif { } AsanThreadLocalMallocStorage() { CHECK(REAL(memset)); REAL(memset)(this, 0, sizeof(AsanThreadLocalMallocStorage)); } -#if ASAN_ALLOCATOR_VERSION == 1 - AsanChunkFifoList quarantine_; - AsanChunk *free_lists_[kNumberOfSizeClasses]; -#else uptr quarantine_cache[16]; uptr allocator2_cache[96 * (512 * 8 + 16)]; // Opaque. -#endif void CommitBack(); }; -// Fake stack frame contains local variables of one function. -// This struct should fit into a stack redzone (32 bytes). -struct FakeFrame { - uptr magic; // Modified by the instrumented code. - uptr descr; // Modified by the instrumented code. - FakeFrame *next; - u64 real_stack : 48; - u64 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(uptr stack_size); - void StopUsingFakeStack() { alive_ = false; } - void Cleanup(); - uptr AllocateStack(uptr size, uptr real_stack); - static void OnFree(uptr ptr, uptr size, uptr real_stack); - // Return the bottom of the maped region. - uptr AddrIsInFakeStack(uptr addr); - bool StackSize() { return stack_size_; } - - private: - static const uptr kMinStackFrameSizeLog = 9; // Min frame is 512B. - static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K. - static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; - static const uptr kNumberOfSizeClasses = - kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1; - - bool AddrIsInSizeClass(uptr addr, uptr size_class); - - // Each size class should be large enough to hold all frames. - uptr ClassMmapSize(uptr size_class); - - uptr ClassSize(uptr size_class) { - return 1UL << (size_class + kMinStackFrameSizeLog); - } - - void DeallocateFrame(FakeFrame *fake_frame); - - uptr ComputeSizeClass(uptr alloc_size); - void AllocateOneSizeClass(uptr size_class); - - uptr stack_size_; - bool alive_; - - uptr allocated_size_classes_[kNumberOfSizeClasses]; - FakeFrameFifo size_classes_[kNumberOfSizeClasses]; - FakeFrameLifo call_stack_; -}; - void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, AllocType alloc_type); void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type); @@ -227,50 +122,5 @@ void asan_mz_force_unlock(); void PrintInternalAllocatorStats(); -// Log2 and RoundUpToPowerOfTwo should be inlined for performance. -#if defined(_WIN32) && !defined(__clang__) -extern "C" { -unsigned char _BitScanForward(unsigned long *index, unsigned long mask); // NOLINT -unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); // NOLINT -#if defined(_WIN64) -unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask); // NOLINT -unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask); // NOLINT -#endif -} -#endif - -static inline uptr Log2(uptr x) { - CHECK(IsPowerOfTwo(x)); -#if !defined(_WIN32) || defined(__clang__) - return __builtin_ctzl(x); -#elif defined(_WIN64) - unsigned long ret; // NOLINT - _BitScanForward64(&ret, x); - return ret; -#else - unsigned long ret; // NOLINT - _BitScanForward(&ret, x); - return ret; -#endif -} - -static inline uptr RoundUpToPowerOfTwo(uptr size) { - CHECK(size); - if (IsPowerOfTwo(size)) return size; - - unsigned long up; // NOLINT -#if !defined(_WIN32) || defined(__clang__) - up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(size); -#elif defined(_WIN64) - _BitScanReverse64(&up, size); -#else - _BitScanReverse(&up, size); -#endif - CHECK(size < (1ULL << (up + 1))); - CHECK(size > (1ULL << up)); - return 1UL << (up + 1); -} - - } // namespace __asan #endif // ASAN_ALLOCATOR_H diff --git a/lib/asan/asan_allocator2.cc b/lib/asan/asan_allocator2.cc index 42d8b29afd6b..d74aa553a288 100644 --- a/lib/asan/asan_allocator2.cc +++ b/lib/asan/asan_allocator2.cc @@ -13,21 +13,20 @@ // This variant uses the allocator from sanitizer_common, i.e. the one shared // with ThreadSanitizer and MemorySanitizer. // -// Status: under development, not enabled by default yet. //===----------------------------------------------------------------------===// #include "asan_allocator.h" -#if ASAN_ALLOCATOR_VERSION == 2 #include "asan_mapping.h" +#include "asan_poisoning.h" #include "asan_report.h" #include "asan_thread.h" -#include "asan_thread_registry.h" -#include "sanitizer/asan_interface.h" #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_list.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_quarantine.h" +#include "lsan/lsan_common.h" namespace __asan { @@ -35,7 +34,7 @@ struct AsanMapUnmapCallback { void OnMap(uptr p, uptr size) const { PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.mmaps++; thread_stats.mmaped += size; } @@ -50,23 +49,32 @@ struct AsanMapUnmapCallback { uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size); FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg); // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.munmaps++; thread_stats.munmaped += size; } }; #if SANITIZER_WORDSIZE == 64 +#if defined(__powerpc64__) +const uptr kAllocatorSpace = 0xa0000000000ULL; +const uptr kAllocatorSize = 0x20000000000ULL; // 2T. +#else const uptr kAllocatorSpace = 0x600000000000ULL; -const uptr kAllocatorSize = 0x10000000000ULL; // 1T. +const uptr kAllocatorSize = 0x40000000000ULL; // 4T. +#endif typedef DefaultSizeClassMap SizeClassMap; typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0 /*metadata*/, SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator; #elif SANITIZER_WORDSIZE == 32 static const u64 kAddressSpaceSize = 1ULL << 32; typedef CompactSizeClassMap SizeClassMap; +static const uptr kRegionSizeLog = 20; +static const uptr kFlatByteMapSize = kAddressSpaceSize >> kRegionSizeLog; typedef SizeClassAllocator32<0, kAddressSpaceSize, 16, - SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator; + SizeClassMap, kRegionSizeLog, + FlatByteMap<kFlatByteMapSize>, + AsanMapUnmapCallback> PrimaryAllocator; #endif typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; @@ -91,8 +99,6 @@ static const uptr kMaxAllowedMallocSize = static const uptr kMaxThreadLocalQuarantine = FIRST_32_SECOND_64(1 << 18, 1 << 20); -static const uptr kReturnOnZeroMalloc = 2048; // Zero page is protected. - // Every chunk of memory allocated by this allocator can be in one of 3 states: // CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated. // CHUNK_ALLOCATED: the chunk is allocated and not yet freed. @@ -114,7 +120,7 @@ static u32 RZSize2Log(u32 rz_size) { CHECK_GE(rz_size, 16); CHECK_LE(rz_size, 2048); CHECK(IsPowerOfTwo(rz_size)); - u32 res = __builtin_ctz(rz_size) - 4; + u32 res = Log2(rz_size) - 4; CHECK_EQ(rz_size, RZLog2Size(res)); return res; } @@ -158,6 +164,7 @@ struct ChunkHeader { u32 from_memalign : 1; u32 alloc_type : 2; u32 rz_log : 3; + u32 lsan_tag : 2; // 2-nd 8 bytes // This field is used for small sizes. For large sizes it is equal to // SizeClassMap::kMaxSize and the actual size is stored in the @@ -168,7 +175,6 @@ struct ChunkHeader { struct ChunkBase : ChunkHeader { // Header2, intersects with user memory. - AsanChunk *next; u32 free_context_id; }; @@ -189,7 +195,8 @@ struct AsanChunk: ChunkBase { return allocator.GetBlockBegin(reinterpret_cast<void *>(this)); return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log)); } - // We store the alloc/free stack traces in the chunk itself. + // If we don't use stack depot, we store the alloc/free stack traces + // in the chunk itself. u32 *AllocStackBeg() { return (u32*)(Beg() - RZLog2Size(rz_log)); } @@ -205,6 +212,9 @@ struct AsanChunk: ChunkBase { uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY); return (available - kChunkHeader2Size) / sizeof(u32); } + bool AddrIsInside(uptr addr) { + return (addr >= Beg()) && (addr < Beg() + UsedSize()); + } }; uptr AsanChunkView::Beg() { return chunk_->Beg(); } @@ -258,8 +268,8 @@ struct QuarantineCallback { } void Recycle(AsanChunk *m) { - CHECK(m->chunk_state == CHUNK_QUARANTINE); - m->chunk_state = CHUNK_AVAILABLE; + CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); + atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed); CHECK_NE(m->alloc_tid, kInvalidTid); CHECK_NE(m->free_tid, kInvalidTid); PoisonShadow(m->Beg(), @@ -273,7 +283,7 @@ struct QuarantineCallback { } // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.real_frees++; thread_stats.really_freed += m->UsedSize(); @@ -291,34 +301,32 @@ struct QuarantineCallback { AllocatorCache *cache_; }; -static void Init() { - static int inited = 0; - if (inited) return; - __asan_init(); - inited = true; // this must happen before any threads are created. +void InitializeAllocator() { allocator.Init(); quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine); } static void *Allocate(uptr size, uptr alignment, StackTrace *stack, - AllocType alloc_type) { - Init(); + AllocType alloc_type, bool can_fill) { + if (!asan_inited) + __asan_init(); + Flags &fl = *flags(); CHECK(stack); const uptr min_alignment = SHADOW_GRANULARITY; if (alignment < min_alignment) alignment = min_alignment; if (size == 0) { - if (alignment <= kReturnOnZeroMalloc) - return reinterpret_cast<void *>(kReturnOnZeroMalloc); - else - return 0; // 0 bytes with large alignment requested. Just return 0. + // We'd be happy to avoid allocating memory for zero-size requests, but + // some programs/tests depend on this behavior and assume that malloc would + // not return NULL even for zero-size allocations. Moreover, it looks like + // operator new should never return NULL, and results of consecutive "new" + // calls must be different even if the allocated size is zero. + size = 1; } CHECK(IsPowerOfTwo(alignment)); uptr rz_log = ComputeRZLog(size); uptr rz_size = RZLog2Size(rz_log); - uptr rounded_size = RoundUpTo(size, alignment); - if (rounded_size < kChunkHeader2Size) - rounded_size = kChunkHeader2Size; + uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment); uptr needed_size = rounded_size + rz_size; if (alignment > min_alignment) needed_size += alignment; @@ -336,7 +344,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, return 0; } - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); void *allocated; if (t) { AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); @@ -358,7 +366,6 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, CHECK_LE(user_end, alloc_end); uptr chunk_beg = user_beg - kChunkHeaderSize; AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); - m->chunk_state = CHUNK_ALLOCATED; m->alloc_type = alloc_type; m->rz_log = rz_log; u32 alloc_tid = t ? t->tid() : 0; @@ -384,7 +391,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, meta[1] = chunk_beg; } - if (flags()->use_stack_depot) { + if (fl.use_stack_depot) { m->alloc_context_id = StackDepotPut(stack->trace, stack->size); } else { m->alloc_context_id = 0; @@ -396,12 +403,12 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, if (size_rounded_down_to_granularity) PoisonShadow(user_beg, size_rounded_down_to_granularity, 0); // Deal with the end of the region if size is not aligned to granularity. - if (size != size_rounded_down_to_granularity && flags()->poison_heap) { + if (size != size_rounded_down_to_granularity && fl.poison_heap) { u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity); *shadow = size & (SHADOW_GRANULARITY - 1); } - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.mallocs++; thread_stats.malloced += size; thread_stats.malloced_redzones += needed_size - size; @@ -411,25 +418,42 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, thread_stats.malloc_large++; void *res = reinterpret_cast<void *>(user_beg); + if (can_fill && fl.max_malloc_fill_size) { + uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size); + REAL(memset)(res, fl.malloc_fill_byte, fill_size); + } + // Must be the last mutation of metadata in this function. + atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release); ASAN_MALLOC_HOOK(res, size); return res; } -static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { - uptr p = reinterpret_cast<uptr>(ptr); - if (p == 0 || p == kReturnOnZeroMalloc) return; - uptr chunk_beg = p - kChunkHeaderSize; - AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); - +static void AtomicallySetQuarantineFlag(AsanChunk *m, + void *ptr, StackTrace *stack) { + u8 old_chunk_state = CHUNK_ALLOCATED; // Flip the chunk_state atomically to avoid race on double-free. - u8 old_chunk_state = atomic_exchange((atomic_uint8_t*)m, CHUNK_QUARANTINE, - memory_order_relaxed); - - if (old_chunk_state == CHUNK_QUARANTINE) - ReportDoubleFree((uptr)ptr, stack); - else if (old_chunk_state != CHUNK_ALLOCATED) - ReportFreeNotMalloced((uptr)ptr, stack); - CHECK(old_chunk_state == CHUNK_ALLOCATED); + if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state, + CHUNK_QUARANTINE, memory_order_acquire)) { + if (old_chunk_state == CHUNK_QUARANTINE) + ReportDoubleFree((uptr)ptr, stack); + else + ReportFreeNotMalloced((uptr)ptr, stack); + } + CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state); +} + +// Expects the chunk to already be marked as quarantined by using +// AtomicallySetQuarantineFlag. +static void QuarantineChunk(AsanChunk *m, void *ptr, + StackTrace *stack, AllocType alloc_type) { + CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); + + // FIXME: if the free hook produces an ASan report (e.g. due to a bug), + // printing the report may crash as the AsanChunk free-related fields have not + // been updated yet. We might need to introduce yet another chunk state to + // handle this correctly, but don't want to yet. + ASAN_FREE_HOOK(ptr); + if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch) ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type, (AllocType)alloc_type); @@ -437,7 +461,7 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { CHECK_GE(m->alloc_tid, 0); if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area. CHECK_EQ(m->free_tid, kInvalidTid); - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); m->free_tid = t ? t->tid() : 0; if (flags()->use_stack_depot) { m->free_context_id = StackDepotPut(stack->trace, stack->size); @@ -445,13 +469,12 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { m->free_context_id = 0; StackTrace::CompressStack(stack, m->FreeStackBeg(), m->FreeStackSize()); } - CHECK(m->chunk_state == CHUNK_QUARANTINE); // Poison the region. PoisonShadow(m->Beg(), RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), kAsanHeapFreeMagic); - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.frees++; thread_stats.freed += m->UsedSize(); @@ -467,8 +490,17 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac), m, m->UsedSize()); } +} - ASAN_FREE_HOOK(ptr); +static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { + uptr p = reinterpret_cast<uptr>(ptr); + if (p == 0) return; + + uptr chunk_beg = p - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); + // Must mark the chunk as quarantined before any changes to its metadata. + AtomicallySetQuarantineFlag(m, ptr, stack); + QuarantineChunk(m, ptr, stack, alloc_type); } static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) { @@ -477,18 +509,21 @@ static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) { uptr chunk_beg = p - kChunkHeaderSize; AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.reallocs++; thread_stats.realloced += new_size; - CHECK(m->chunk_state == CHUNK_ALLOCATED); + // Must mark the chunk as quarantined before any changes to its metadata. + // This also ensures that other threads can't deallocate it in the meantime. + AtomicallySetQuarantineFlag(m, old_ptr, stack); + uptr old_size = m->UsedSize(); uptr memcpy_size = Min(new_size, old_size); - void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC); + void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); if (new_ptr) { - CHECK(REAL(memcpy) != 0); + CHECK_NE(REAL(memcpy), (void*)0); REAL(memcpy)(new_ptr, old_ptr, memcpy_size); - Deallocate(old_ptr, stack, FROM_MALLOC); + QuarantineChunk(m, old_ptr, stack, FROM_MALLOC); } return new_ptr; } @@ -548,7 +583,7 @@ AsanChunk *ChooseChunk(uptr addr, return right_chunk; } // Same chunk_state: choose based on offset. - uptr l_offset = 0, r_offset = 0; + sptr l_offset = 0, r_offset = 0; CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); if (l_offset < r_offset) @@ -559,7 +594,7 @@ AsanChunk *ChooseChunk(uptr addr, AsanChunkView FindHeapChunkByAddress(uptr addr) { AsanChunk *m1 = GetAsanChunkByAddr(addr); if (!m1) return AsanChunkView(m1); - uptr offset = 0; + sptr offset = 0; if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) { // The address is in the chunk's left redzone, so maybe it is actually // a right buffer overflow from the other chunk to the left. @@ -589,7 +624,7 @@ void PrintInternalAllocatorStats() { SANITIZER_INTERFACE_ATTRIBUTE void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, AllocType alloc_type) { - return Allocate(size, alignment, stack, alloc_type); + return Allocate(size, alignment, stack, alloc_type, true); } SANITIZER_INTERFACE_ATTRIBUTE @@ -599,19 +634,22 @@ void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { SANITIZER_INTERFACE_ATTRIBUTE void *asan_malloc(uptr size, StackTrace *stack) { - return Allocate(size, 8, stack, FROM_MALLOC); + return Allocate(size, 8, stack, FROM_MALLOC, true); } void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { - void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC); - if (ptr) + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; + void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); + // If the memory comes from the secondary allocator no need to clear it + // as it comes directly from mmap. + if (ptr && allocator.FromPrimary(ptr)) REAL(memset)(ptr, 0, nmemb * size); return ptr; } void *asan_realloc(void *p, uptr size, StackTrace *stack) { if (p == 0) - return Allocate(size, 8, stack, FROM_MALLOC); + return Allocate(size, 8, stack, FROM_MALLOC, true); if (size == 0) { Deallocate(p, stack, FROM_MALLOC); return 0; @@ -620,7 +658,7 @@ void *asan_realloc(void *p, uptr size, StackTrace *stack) { } void *asan_valloc(uptr size, StackTrace *stack) { - return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC); + return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true); } void *asan_pvalloc(uptr size, StackTrace *stack) { @@ -630,12 +668,12 @@ void *asan_pvalloc(uptr size, StackTrace *stack) { // pvalloc(0) should allocate one page. size = PageSize; } - return Allocate(size, PageSize, stack, FROM_MALLOC); + return Allocate(size, PageSize, stack, FROM_MALLOC, true); } int asan_posix_memalign(void **memptr, uptr alignment, uptr size, StackTrace *stack) { - void *ptr = Allocate(size, alignment, stack, FROM_MALLOC); + void *ptr = Allocate(size, alignment, stack, FROM_MALLOC, true); CHECK(IsAligned((uptr)ptr, alignment)); *memptr = ptr; return 0; @@ -651,20 +689,96 @@ uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) { } uptr asan_mz_size(const void *ptr) { - UNIMPLEMENTED(); - return 0; + return AllocationSize(reinterpret_cast<uptr>(ptr)); } void asan_mz_force_lock() { - UNIMPLEMENTED(); + allocator.ForceLock(); + fallback_mutex.Lock(); } void asan_mz_force_unlock() { - UNIMPLEMENTED(); + fallback_mutex.Unlock(); + allocator.ForceUnlock(); } } // namespace __asan +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +void LockAllocator() { + __asan::allocator.ForceLock(); +} + +void UnlockAllocator() { + __asan::allocator.ForceUnlock(); +} + +void GetAllocatorGlobalRange(uptr *begin, uptr *end) { + *begin = (uptr)&__asan::allocator; + *end = *begin + sizeof(__asan::allocator); +} + +void *PointsIntoChunk(void* p) { + uptr addr = reinterpret_cast<uptr>(p); + __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(addr); + if (!m) return 0; + uptr chunk = m->Beg(); + if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) + return reinterpret_cast<void *>(chunk); + return 0; +} + +void *GetUserBegin(void *p) { + __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(reinterpret_cast<uptr>(p)); + CHECK(m); + return reinterpret_cast<void *>(m->Beg()); +} + +LsanMetadata::LsanMetadata(void *chunk) { + uptr addr = reinterpret_cast<uptr>(chunk); + metadata_ = reinterpret_cast<void *>(addr - __asan::kChunkHeaderSize); +} + +bool LsanMetadata::allocated() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->chunk_state == __asan::CHUNK_ALLOCATED; +} + +ChunkTag LsanMetadata::tag() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return static_cast<ChunkTag>(m->lsan_tag); +} + +void LsanMetadata::set_tag(ChunkTag value) { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + m->lsan_tag = value; +} + +uptr LsanMetadata::requested_size() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->UsedSize(); +} + +u32 LsanMetadata::stack_trace_id() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->alloc_context_id; +} + +template <typename Callable> void ForEachChunk(Callable const &callback) { + __asan::allocator.ForEachChunk(callback); +} +#if CAN_SANITIZE_LEAKS +template void ForEachChunk<ProcessPlatformSpecificAllocationsCb>( + ProcessPlatformSpecificAllocationsCb const &callback); +template void ForEachChunk<PrintLeakedCb>(PrintLeakedCb const &callback); +template void ForEachChunk<CollectLeaksCb>(CollectLeaksCb const &callback); +template void ForEachChunk<MarkIndirectlyLeakedCb>( + MarkIndirectlyLeakedCb const &callback); +template void ForEachChunk<ClearTagCb>(ClearTagCb const &callback); +#endif // CAN_SANITIZE_LEAKS +} // namespace __lsan + // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT @@ -676,7 +790,7 @@ uptr __asan_get_estimated_allocated_size(uptr size) { bool __asan_get_ownership(const void *p) { uptr ptr = reinterpret_cast<uptr>(p); - return (ptr == kReturnOnZeroMalloc) || (AllocationSize(ptr) > 0); + return (AllocationSize(ptr) > 0); } uptr __asan_get_allocated_size(const void *p) { @@ -684,7 +798,7 @@ uptr __asan_get_allocated_size(const void *p) { uptr ptr = reinterpret_cast<uptr>(p); uptr allocated_size = AllocationSize(ptr); // Die if p is not malloced or if it is already freed. - if (allocated_size == 0 && ptr != kReturnOnZeroMalloc) { + if (allocated_size == 0) { GET_STACK_TRACE_FATAL_HERE; ReportAsanGetAllocatedSizeNotOwned(ptr, &stack); } @@ -705,6 +819,3 @@ void __asan_free_hook(void *ptr) { } } // extern "C" #endif - - -#endif // ASAN_ALLOCATOR_VERSION diff --git a/lib/asan/asan_blacklist.txt b/lib/asan/asan_blacklist.txt new file mode 100644 index 000000000000..03da08598d23 --- /dev/null +++ b/lib/asan/asan_blacklist.txt @@ -0,0 +1,10 @@ +# Blacklist for AddressSanitizer. Turns off instrumentation of particular +# functions or sources. Use with care. You may set location of blacklist +# at compile-time using -fsanitize-blacklist=<path> flag. + +# Example usage: +# fun:*bad_function_name* +# src:file_with_tricky_code.cc +# global:*global_with_bad_access_or_initialization* +# global-init:*global_with_initialization_issues* +# global-init-type:*Namespace::ClassName* diff --git a/lib/asan/asan_fake_stack.cc b/lib/asan/asan_fake_stack.cc index 7c5a16312d46..23eebe64e612 100644 --- a/lib/asan/asan_fake_stack.cc +++ b/lib/asan/asan_fake_stack.cc @@ -12,14 +12,13 @@ // FakeStack is used to detect use-after-return bugs. //===----------------------------------------------------------------------===// #include "asan_allocator.h" +#include "asan_poisoning.h" #include "asan_thread.h" -#include "asan_thread_registry.h" -#include "sanitizer/asan_interface.h" namespace __asan { FakeStack::FakeStack() { - CHECK(REAL(memset) != 0); + CHECK(REAL(memset)); REAL(memset)(this, 0, sizeof(*this)); } @@ -31,24 +30,26 @@ bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) { } uptr FakeStack::AddrIsInFakeStack(uptr addr) { - for (uptr i = 0; i < kNumberOfSizeClasses; i++) { - if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i]; + for (uptr size_class = 0; size_class < kNumberOfSizeClasses; size_class++) { + if (!AddrIsInSizeClass(addr, size_class)) continue; + uptr size_class_first_ptr = allocated_size_classes_[size_class]; + uptr size = ClassSize(size_class); + CHECK_LE(size_class_first_ptr, addr); + CHECK_GT(size_class_first_ptr + ClassMmapSize(size_class), addr); + return size_class_first_ptr + ((addr - size_class_first_ptr) / size) * size; } return 0; } // We may want to compute this during compilation. -inline uptr FakeStack::ComputeSizeClass(uptr alloc_size) { +ALWAYS_INLINE uptr FakeStack::ComputeSizeClass(uptr alloc_size) { uptr rounded_size = RoundUpToPowerOfTwo(alloc_size); uptr log = Log2(rounded_size); - CHECK(alloc_size <= (1UL << log)); - if (!(alloc_size > (1UL << (log-1)))) { - Printf("alloc_size %zu log %zu\n", alloc_size, log); - } - CHECK(alloc_size > (1UL << (log-1))); + CHECK_LE(alloc_size, (1UL << log)); + CHECK_GT(alloc_size, (1UL << (log-1))); uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog; - CHECK(res < kNumberOfSizeClasses); - CHECK(ClassSize(res) >= rounded_size); + CHECK_LT(res, kNumberOfSizeClasses); + CHECK_GE(ClassSize(res), rounded_size); return res; } @@ -104,7 +105,7 @@ void FakeStack::AllocateOneSizeClass(uptr size_class) { uptr new_mem = (uptr)MmapOrDie( ClassMmapSize(size_class), __FUNCTION__); // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n", - // asanThreadRegistry().GetCurrent()->tid(), + // GetCurrentThread()->tid(), // size_class, new_mem, new_mem + ClassMmapSize(size_class), // ClassMmapSize(size_class)); uptr i; @@ -116,7 +117,7 @@ void FakeStack::AllocateOneSizeClass(uptr size_class) { allocated_size_classes_[size_class] = new_mem; } -uptr FakeStack::AllocateStack(uptr size, uptr real_stack) { +ALWAYS_INLINE uptr FakeStack::AllocateStack(uptr size, uptr real_stack) { if (!alive_) return real_stack; CHECK(size <= kMaxStackMallocSize && size > 1); uptr size_class = ComputeSizeClass(size); @@ -138,7 +139,7 @@ uptr FakeStack::AllocateStack(uptr size, uptr real_stack) { return ptr; } -void FakeStack::DeallocateFrame(FakeFrame *fake_frame) { +ALWAYS_INLINE void FakeStack::DeallocateFrame(FakeFrame *fake_frame) { CHECK(alive_); uptr size = fake_frame->size_minus_one + 1; uptr size_class = ComputeSizeClass(size); @@ -149,11 +150,11 @@ void FakeStack::DeallocateFrame(FakeFrame *fake_frame) { size_classes_[size_class].FifoPush(fake_frame); } -void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) { +ALWAYS_INLINE void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) { FakeFrame *fake_frame = (FakeFrame*)ptr; - CHECK(fake_frame->magic = kRetiredStackFrameMagic); - CHECK(fake_frame->descr != 0); - CHECK(fake_frame->size_minus_one == size - 1); + CHECK_EQ(fake_frame->magic, kRetiredStackFrameMagic); + CHECK_NE(fake_frame->descr, 0); + CHECK_EQ(fake_frame->size_minus_one, size - 1); PoisonShadow(ptr, size, kAsanStackAfterReturnMagic); } @@ -164,7 +165,7 @@ using namespace __asan; // NOLINT uptr __asan_stack_malloc(uptr size, uptr real_stack) { if (!flags()->use_fake_stack) return real_stack; - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); if (!t) { // TSD is gone, use the real stack. return real_stack; diff --git a/lib/asan/asan_fake_stack.h b/lib/asan/asan_fake_stack.h new file mode 100644 index 000000000000..308b4c571832 --- /dev/null +++ b/lib/asan/asan_fake_stack.h @@ -0,0 +1,117 @@ +//===-- asan_fake_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_fake_stack.cc +//===----------------------------------------------------------------------===// + +#ifndef ASAN_FAKE_STACK_H +#define ASAN_FAKE_STACK_H + +namespace __asan { + +// Fake stack frame contains local variables of one function. +struct FakeFrame { + uptr magic; // Modified by the instrumented code. + uptr descr; // Modified by the instrumented code. + uptr pc; // Modified by the instrumented code. + u64 real_stack : 48; + u64 size_minus_one : 16; + // End of the first 32 bytes. + // The rest should not be used when the frame is active. + FakeFrame *next; +}; + +struct FakeFrameFifo { + public: + void FifoPush(FakeFrame *node); + FakeFrame *FifoPop(); + private: + FakeFrame *first_, *last_; +}; + +template<uptr kMaxNumberOfFrames> +class FakeFrameLifo { + public: + explicit FakeFrameLifo(LinkerInitialized) {} + FakeFrameLifo() : n_frames_(0) {} + void LifoPush(FakeFrame *node) { + CHECK_LT(n_frames_, kMaxNumberOfFrames); + frames_[n_frames_++] = node; + } + void LifoPop() { + CHECK(n_frames_); + n_frames_--; + } + FakeFrame *top() { + if (n_frames_ == 0) + return 0; + return frames_[n_frames_ - 1]; + } + private: + uptr n_frames_; + FakeFrame *frames_[kMaxNumberOfFrames]; +}; + +// 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 x) : call_stack_(x) {} + void Init(uptr stack_size); + void StopUsingFakeStack() { alive_ = false; } + void Cleanup(); + uptr AllocateStack(uptr size, uptr real_stack); + static void OnFree(uptr ptr, uptr size, uptr real_stack); + // Return the bottom of the maped region. + uptr AddrIsInFakeStack(uptr addr); + bool StackSize() { return stack_size_; } + + private: + static const uptr kMinStackFrameSizeLog = 9; // Min frame is 512B. + static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K. + static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; + static const uptr kNumberOfSizeClasses = + kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1; + static const uptr kMaxRecursionDepth = 1023; + + bool AddrIsInSizeClass(uptr addr, uptr size_class); + + // Each size class should be large enough to hold all frames. + uptr ClassMmapSize(uptr size_class); + + uptr ClassSize(uptr size_class) { + return 1UL << (size_class + kMinStackFrameSizeLog); + } + + void DeallocateFrame(FakeFrame *fake_frame); + + uptr ComputeSizeClass(uptr alloc_size); + void AllocateOneSizeClass(uptr size_class); + + uptr stack_size_; + bool alive_; + + uptr allocated_size_classes_[kNumberOfSizeClasses]; + FakeFrameFifo size_classes_[kNumberOfSizeClasses]; + FakeFrameLifo<kMaxRecursionDepth> call_stack_; +}; + +} // namespace __asan + +#endif // ASAN_FAKE_STACK_H diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h index d7b21ea4a45f..2f3bc9051ae1 100644 --- a/lib/asan/asan_flags.h +++ b/lib/asan/asan_flags.h @@ -15,13 +15,15 @@ #ifndef ASAN_FLAGS_H #define ASAN_FLAGS_H -#include "sanitizer/common_interface_defs.h" +#include "sanitizer_common/sanitizer_internal_defs.h" -// ASan flag values can be defined in three ways: +// ASan flag values can be defined in four ways: // 1) initialized with default values at startup. -// 2) overriden from string returned by user-specified function +// 2) overriden during compilation of ASan runtime by providing +// compile definition ASAN_DEFAULT_OPTIONS. +// 3) overriden from string returned by user-specified function // __asan_default_options(). -// 3) overriden from env variable ASAN_OPTIONS. +// 4) overriden from env variable ASAN_OPTIONS. namespace __asan { @@ -30,8 +32,6 @@ struct Flags { // Lower value may reduce memory usage but increase the chance of // false negatives. int quarantine_size; - // If set, uses in-process symbolizer from common sanitizer runtime. - bool symbolize; // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). int verbosity; // Size (in bytes) of redzones around heap objects. @@ -45,22 +45,18 @@ struct Flags { int report_globals; // If set, attempts to catch initialization order issues. bool check_initialization_order; - // Max number of stack frames kept for each allocation/deallocation. - int malloc_context_size; // If set, uses custom wrappers and replacements for libc string functions // to find more errors. bool replace_str; // If set, uses custom wrappers for memset/memcpy/memmove intinsics. bool replace_intrin; - // Used on Mac only. See comments in asan_mac.cc and asan_malloc_mac.cc. - bool replace_cfallocator; // Used on Mac only. bool mac_ignore_invalid_free; - // ASan allocator flag. See asan_allocator.cc. + // ASan allocator flag. bool use_fake_stack; - // ASan allocator flag. Sets the maximal size of allocation request - // that would return memory filled with zero bytes. - int max_malloc_fill_size; + // ASan allocator flag. max_malloc_fill_size is the maximal amount of bytes + // that will be filled with malloc_fill_byte on malloc. + int max_malloc_fill_size, malloc_fill_byte; // Override exit status if something was reported. int exitcode; // If set, user may manually mark memory regions as poisoned or unpoisoned. @@ -71,6 +67,8 @@ struct Flags { int sleep_before_dying; // If set, registers ASan custom segv handler. bool handle_segv; + // If set, allows user register segv handler even if ASan registers one. + bool allow_user_segv_handler; // If set, uses alternate stack for signal handling. bool use_sigaltstack; // Allow the users to work around the bug in Nvidia drivers prior to 295.*. @@ -79,6 +77,10 @@ struct Flags { bool unmap_shadow_on_exit; // If set, calls abort() instead of _exit() after printing an error report. bool abort_on_error; + // Print various statistics after printing an error message or if atexit=1. + bool print_stats; + // Print the legend for the shadow bytes. + bool print_legend; // If set, prints ASan exit stats even after program terminates successfully. bool atexit; // By default, disable core dumper on 64-bit - it makes little sense @@ -87,18 +89,12 @@ struct Flags { // Allow the tool to re-exec the program. This may interfere badly with the // debugger. bool allow_reexec; - // Strips this prefix from file paths in error reports. - const char *strip_path_prefix; // If set, prints not only thread creation stacks for threads in error report, // but also thread creation stacks for threads that created those threads, // etc. up to main thread. bool print_full_thread_history; // ASan will write logs to "log_path.pid" instead of stderr. const char *log_path; - // Use fast (frame-pointer-based) unwinder on fatal errors (if available). - bool fast_unwind_on_fatal; - // Use fast (frame-pointer-based) unwinder on malloc/free (if available). - bool fast_unwind_on_malloc; // Poison (or not) the heap memory on [de]allocation. Zero value is useful // for benchmarking the allocator or instrumentator. bool poison_heap; @@ -106,9 +102,20 @@ struct Flags { bool alloc_dealloc_mismatch; // Use stack depot instead of storing stacks in the redzones. bool use_stack_depot; + // If true, assume that memcmp(p1, p2, n) always reads n bytes before + // comparing p1 and p2. + bool strict_memcmp; + // If true, assume that dynamic initializers can never access globals from + // other modules, even if the latter are already initialized. + bool strict_init_order; + // Invoke LeakSanitizer at process exit. + bool detect_leaks; }; -Flags *flags(); +extern Flags asan_flags_dont_use_directly; +inline Flags *flags() { + return &asan_flags_dont_use_directly; +} void InitializeFlags(Flags *f, const char *env); } // namespace __asan diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc index 4e18bb8e2355..301ea44f2ca5 100644 --- a/lib/asan/asan_globals.cc +++ b/lib/asan/asan_globals.cc @@ -14,12 +14,14 @@ #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_mapping.h" +#include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" #include "asan_thread.h" -#include "sanitizer/asan_interface.h" +#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_placement_new.h" namespace __asan { @@ -33,38 +35,48 @@ struct ListOfGlobals { static BlockingMutex mu_for_globals(LINKER_INITIALIZED); static LowLevelAllocator allocator_for_globals; static ListOfGlobals *list_of_all_globals; -static ListOfGlobals *list_of_dynamic_init_globals; - -void PoisonRedZones(const Global &g) { - uptr shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE; - CHECK(shadow_rz_size == 1 || shadow_rz_size == 2 || shadow_rz_size == 4); - // full right redzone - uptr g_aligned_size = kGlobalAndStackRedzone * - ((g.size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone); - PoisonShadow(g.beg + g_aligned_size, - kGlobalAndStackRedzone, kAsanGlobalRedzoneMagic); - if ((g.size % kGlobalAndStackRedzone) != 0) { - // partial right redzone - u64 g_aligned_down_size = kGlobalAndStackRedzone * - (g.size / kGlobalAndStackRedzone); - CHECK(g_aligned_down_size == g_aligned_size - kGlobalAndStackRedzone); - PoisonShadowPartialRightRedzone(g.beg + g_aligned_down_size, - g.size % kGlobalAndStackRedzone, - kGlobalAndStackRedzone, - kAsanGlobalRedzoneMagic); + +static const int kDynamicInitGlobalsInitialCapacity = 512; +struct DynInitGlobal { + Global g; + bool initialized; +}; +typedef InternalVector<DynInitGlobal> VectorOfGlobals; +// Lazy-initialized and never deleted. +static VectorOfGlobals *dynamic_init_globals; + +ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { + FastPoisonShadow(g->beg, g->size_with_redzone, value); +} + +ALWAYS_INLINE void PoisonRedZones(const Global &g) { + uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY); + FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, + kAsanGlobalRedzoneMagic); + if (g.size != aligned_size) { + FastPoisonShadowPartialRightRedzone( + g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY), + g.size % SHADOW_GRANULARITY, + SHADOW_GRANULARITY, + kAsanGlobalRedzoneMagic); } } -bool DescribeAddressIfGlobal(uptr addr) { +static void ReportGlobal(const Global &g, const char *prefix) { + Report("%s Global: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n", + prefix, (void*)g.beg, g.size, g.size_with_redzone, g.name, + g.module_name, g.has_dynamic_init); +} + +bool DescribeAddressIfGlobal(uptr addr, uptr size) { if (!flags()->report_globals) return false; BlockingMutexLock lock(&mu_for_globals); bool res = false; for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { const Global &g = *l->g; if (flags()->report_globals >= 2) - Report("Search Global: beg=%p size=%zu name=%s\n", - (void*)g.beg, g.size, (char*)g.name); - res |= DescribeAddressRelativeToGlobal(addr, g); + ReportGlobal(g, "Search"); + res |= DescribeAddressRelativeToGlobal(addr, size, g); } return res; } @@ -75,24 +87,26 @@ bool DescribeAddressIfGlobal(uptr addr) { static void RegisterGlobal(const Global *g) { CHECK(asan_inited); if (flags()->report_globals >= 2) - Report("Added Global: beg=%p size=%zu/%zu name=%s dyn.init=%zu\n", - (void*)g->beg, g->size, g->size_with_redzone, g->name, - g->has_dynamic_init); + ReportGlobal(*g, "Added"); CHECK(flags()->report_globals); CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - PoisonRedZones(*g); + if (flags()->poison_heap) + PoisonRedZones(*g); ListOfGlobals *l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); l->g = g; l->next = list_of_all_globals; list_of_all_globals = l; if (g->has_dynamic_init) { - l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); - l->g = g; - l->next = list_of_dynamic_init_globals; - list_of_dynamic_init_globals = l; + if (dynamic_init_globals == 0) { + void *mem = allocator_for_globals.Allocate(sizeof(VectorOfGlobals)); + dynamic_init_globals = new(mem) + VectorOfGlobals(kDynamicInitGlobalsInitialCapacity); + } + DynInitGlobal dyn_global = { *g, false }; + dynamic_init_globals->push_back(dyn_global); } } @@ -102,34 +116,26 @@ static void UnregisterGlobal(const Global *g) { CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - PoisonShadow(g->beg, g->size_with_redzone, 0); + if (flags()->poison_heap) + PoisonShadowForGlobal(g, 0); // We unpoison the shadow memory for the global but we do not remove it from // the list because that would require O(n^2) time with the current list // implementation. It might not be worth doing anyway. } -// Poison all shadow memory for a single global. -static void PoisonGlobalAndRedzones(const Global *g) { - CHECK(asan_inited); - CHECK(flags()->check_initialization_order); - CHECK(AddrIsInMem(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - if (flags()->report_globals >= 3) - Printf("DynInitPoison : %s\n", g->name); - PoisonShadow(g->beg, g->size_with_redzone, kAsanInitializationOrderMagic); -} - -static void UnpoisonGlobal(const Global *g) { - CHECK(asan_inited); - CHECK(flags()->check_initialization_order); - CHECK(AddrIsInMem(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - if (flags()->report_globals >= 3) - Printf("DynInitUnpoison: %s\n", g->name); - PoisonShadow(g->beg, g->size_with_redzone, 0); - PoisonRedZones(*g); +void StopInitOrderChecking() { + BlockingMutexLock lock(&mu_for_globals); + if (!flags()->check_initialization_order || !dynamic_init_globals) + return; + flags()->check_initialization_order = false; + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + } } } // namespace __asan @@ -160,31 +166,47 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) { // when all dynamically initialized globals are unpoisoned. This method // poisons all global variables not defined in this TU, so that a dynamic // initializer can only touch global variables in the same TU. -void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) { - if (!flags()->check_initialization_order) return; - CHECK(list_of_dynamic_init_globals); +void __asan_before_dynamic_init(const char *module_name) { + if (!flags()->check_initialization_order || + !flags()->poison_heap) + return; + bool strict_init_order = flags()->strict_init_order; + CHECK(dynamic_init_globals); + CHECK(module_name); + CHECK(asan_inited); BlockingMutexLock lock(&mu_for_globals); - bool from_current_tu = false; - // The list looks like: - // a => ... => b => last_addr => ... => first_addr => c => ... - // The globals of the current TU reside between last_addr and first_addr. - for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) { - if (l->g->beg == last_addr) - from_current_tu = true; - if (!from_current_tu) - PoisonGlobalAndRedzones(l->g); - if (l->g->beg == first_addr) - from_current_tu = false; + if (flags()->report_globals >= 3) + Printf("DynInitPoison module: %s\n", module_name); + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + if (dyn_g.initialized) + continue; + if (g->module_name != module_name) + PoisonShadowForGlobal(g, kAsanInitializationOrderMagic); + else if (!strict_init_order) + dyn_g.initialized = true; } - CHECK(!from_current_tu); } // This method runs immediately after dynamic initialization in each TU, when // all dynamically initialized globals except for those defined in the current // TU are poisoned. It simply unpoisons all dynamically initialized globals. void __asan_after_dynamic_init() { - if (!flags()->check_initialization_order) return; + if (!flags()->check_initialization_order || + !flags()->poison_heap) + return; + CHECK(asan_inited); BlockingMutexLock lock(&mu_for_globals); - for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) - UnpoisonGlobal(l->g); + // FIXME: Optionally report that we're unpoisoning globals from a module. + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + if (!dyn_g.initialized) { + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + } + } } diff --git a/lib/asan/asan_intercepted_functions.h b/lib/asan/asan_intercepted_functions.h index a1faf713c130..842781cdb17f 100644 --- a/lib/asan/asan_intercepted_functions.h +++ b/lib/asan/asan_intercepted_functions.h @@ -19,16 +19,16 @@ #include "sanitizer_common/sanitizer_platform_interceptors.h" #include <stdarg.h> +#include <stddef.h> using __sanitizer::uptr; // Use macro to describe if specific function should be // intercepted on a given platform. -#if !defined(_WIN32) +#if !SANITIZER_WINDOWS # define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1 # define ASAN_INTERCEPT__LONGJMP 1 # define ASAN_INTERCEPT_STRDUP 1 -# define ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP 1 # define ASAN_INTERCEPT_INDEX 1 # define ASAN_INTERCEPT_PTHREAD_CREATE 1 # define ASAN_INTERCEPT_MLOCKX 1 @@ -36,225 +36,79 @@ using __sanitizer::uptr; # define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0 # define ASAN_INTERCEPT__LONGJMP 0 # define ASAN_INTERCEPT_STRDUP 0 -# define ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP 0 # define ASAN_INTERCEPT_INDEX 0 # define ASAN_INTERCEPT_PTHREAD_CREATE 0 # define ASAN_INTERCEPT_MLOCKX 0 #endif -#if defined(__linux__) +#if SANITIZER_LINUX # define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1 #else # define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0 #endif -#if !defined(__APPLE__) +#if !SANITIZER_MAC # define ASAN_INTERCEPT_STRNLEN 1 #else # define ASAN_INTERCEPT_STRNLEN 0 #endif -#if defined(__linux__) && !defined(ANDROID) +#if SANITIZER_LINUX && !SANITIZER_ANDROID # define ASAN_INTERCEPT_SWAPCONTEXT 1 #else # define ASAN_INTERCEPT_SWAPCONTEXT 0 #endif -#if !defined(ANDROID) && !defined(_WIN32) +#if !SANITIZER_ANDROID && !SANITIZER_WINDOWS # define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1 #else # define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0 #endif -// On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it -// there. -#if !defined(_WIN32) && (!defined(__APPLE__) || MAC_INTERPOSE_FUNCTIONS) +#if !SANITIZER_WINDOWS # define ASAN_INTERCEPT_SIGLONGJMP 1 #else # define ASAN_INTERCEPT_SIGLONGJMP 0 #endif -#if ASAN_HAS_EXCEPTIONS && !defined(_WIN32) +#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS # define ASAN_INTERCEPT___CXA_THROW 1 #else # define ASAN_INTERCEPT___CXA_THROW 0 #endif -#define DECLARE_FUNCTION_AND_WRAPPER(ret_type, func, ...) \ - ret_type func(__VA_ARGS__); \ - ret_type WRAP(func)(__VA_ARGS__) - -// Use extern declarations of intercepted functions on Mac and Windows -// to avoid including system headers. -#if defined(__APPLE__) || (defined(_WIN32) && !defined(_DLL)) -extern "C" { -// signal.h -# if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION -struct sigaction; -DECLARE_FUNCTION_AND_WRAPPER(int, sigaction, int sig, - const struct sigaction *act, - struct sigaction *oldact); -DECLARE_FUNCTION_AND_WRAPPER(void*, signal, int signum, void *handler); -# endif - -// setjmp.h -DECLARE_FUNCTION_AND_WRAPPER(void, longjmp, void *env, int value); -# if ASAN_INTERCEPT__LONGJMP -DECLARE_FUNCTION_AND_WRAPPER(void, _longjmp, void *env, int value); -# endif -# if ASAN_INTERCEPT_SIGLONGJMP -DECLARE_FUNCTION_AND_WRAPPER(void, siglongjmp, void *env, int value); -# endif -# if ASAN_INTERCEPT___CXA_THROW -DECLARE_FUNCTION_AND_WRAPPER(void, __cxa_throw, void *a, void *b, void *c); -#endif - -// string.h / strings.h -DECLARE_FUNCTION_AND_WRAPPER(int, memcmp, - const void *a1, const void *a2, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(void*, memmove, - void *to, const void *from, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(void*, memcpy, - void *to, const void *from, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(void*, memset, void *block, int c, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(char*, strchr, const char *str, int c); -DECLARE_FUNCTION_AND_WRAPPER(char*, strcat, /* NOLINT */ - char *to, const char* from); -DECLARE_FUNCTION_AND_WRAPPER(char*, strncat, - char *to, const char* from, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(char*, strcpy, /* NOLINT */ - char *to, const char* from); -DECLARE_FUNCTION_AND_WRAPPER(char*, strncpy, - char *to, const char* from, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(int, strcmp, const char *s1, const char* s2); -DECLARE_FUNCTION_AND_WRAPPER(int, strncmp, - const char *s1, const char* s2, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(uptr, strlen, const char *s); -# if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP -DECLARE_FUNCTION_AND_WRAPPER(int, strcasecmp, const char *s1, const char *s2); -DECLARE_FUNCTION_AND_WRAPPER(int, strncasecmp, - const char *s1, const char *s2, uptr n); -# endif -# if ASAN_INTERCEPT_STRDUP -DECLARE_FUNCTION_AND_WRAPPER(char*, strdup, const char *s); -# endif -# if ASAN_INTERCEPT_STRNLEN -DECLARE_FUNCTION_AND_WRAPPER(uptr, strnlen, const char *s, uptr maxlen); -# endif -#if ASAN_INTERCEPT_INDEX -DECLARE_FUNCTION_AND_WRAPPER(char*, index, const char *string, int c); -#endif - -// stdlib.h -DECLARE_FUNCTION_AND_WRAPPER(int, atoi, const char *nptr); -DECLARE_FUNCTION_AND_WRAPPER(long, atol, const char *nptr); // NOLINT -DECLARE_FUNCTION_AND_WRAPPER(long, strtol, const char *nptr, char **endptr, int base); // NOLINT -# if ASAN_INTERCEPT_ATOLL_AND_STRTOLL -DECLARE_FUNCTION_AND_WRAPPER(long long, atoll, const char *nptr); // NOLINT -DECLARE_FUNCTION_AND_WRAPPER(long long, strtoll, const char *nptr, char **endptr, int base); // NOLINT -# endif - -// unistd.h -# if SANITIZER_INTERCEPT_READ -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, read, int fd, void *buf, SIZE_T count); -# endif -# if SANITIZER_INTERCEPT_PREAD -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread, int fd, void *buf, - SIZE_T count, OFF_T offset); -# endif -# if SANITIZER_INTERCEPT_PREAD64 -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread64, int fd, void *buf, - SIZE_T count, OFF64_T offset); -# endif - -#if SANITIZER_INTERCEPT_WRITE -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, write, int fd, void *ptr, SIZE_T count); -#endif -#if SANITIZER_INTERCEPT_PWRITE -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count); +#if !SANITIZER_WINDOWS +# define ASAN_INTERCEPT___CXA_ATEXIT 1 +#else +# define ASAN_INTERCEPT___CXA_ATEXIT 0 #endif -# if ASAN_INTERCEPT_MLOCKX -// mlock/munlock -DECLARE_FUNCTION_AND_WRAPPER(int, mlock, const void *addr, SIZE_T len); -DECLARE_FUNCTION_AND_WRAPPER(int, munlock, const void *addr, SIZE_T len); -DECLARE_FUNCTION_AND_WRAPPER(int, mlockall, int flags); -DECLARE_FUNCTION_AND_WRAPPER(int, munlockall, void); -# endif - +# if SANITIZER_WINDOWS +extern "C" { // Windows threads. -# if defined(_WIN32) __declspec(dllimport) void* __stdcall CreateThread(void *sec, uptr st, void* start, void *arg, DWORD fl, DWORD *id); -# endif -// Posix threads. -# if ASAN_INTERCEPT_PTHREAD_CREATE -DECLARE_FUNCTION_AND_WRAPPER(int, pthread_create, - void *thread, void *attr, - void *(*start_routine)(void*), void *arg); -# endif - -#if defined(__APPLE__) -typedef void* pthread_workqueue_t; -typedef void* pthread_workitem_handle_t; - -typedef void* dispatch_group_t; -typedef void* dispatch_queue_t; -typedef void* dispatch_source_t; -typedef u64 dispatch_time_t; -typedef void (*dispatch_function_t)(void *block); -typedef void* (*worker_t)(void *block); -typedef void* CFStringRef; -typedef void* CFAllocatorRef; - -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async_f, - dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_sync_f, - dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after_f, - dispatch_time_t when, dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_barrier_async_f, - dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async_f, - dispatch_group_t group, dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, __CFInitialize, void); -DECLARE_FUNCTION_AND_WRAPPER(CFStringRef, CFStringCreateCopy, - CFAllocatorRef alloc, CFStringRef str); -DECLARE_FUNCTION_AND_WRAPPER(void, free, void* ptr); - -DECLARE_FUNCTION_AND_WRAPPER(int, vscanf, const char *format, va_list ap); -DECLARE_FUNCTION_AND_WRAPPER(int, vsscanf, const char *str, const char *format, - va_list ap); -DECLARE_FUNCTION_AND_WRAPPER(int, vfscanf, void *stream, const char *format, - va_list ap); -DECLARE_FUNCTION_AND_WRAPPER(int, scanf, const char *format, ...); -DECLARE_FUNCTION_AND_WRAPPER(int, fscanf, - void* stream, const char *format, ...); -DECLARE_FUNCTION_AND_WRAPPER(int, sscanf, // NOLINT - const char *str, const char *format, ...); - -#if MAC_INTERPOSE_FUNCTIONS && !defined(MISSING_BLOCKS_SUPPORT) -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async, - dispatch_group_t dg, - dispatch_queue_t dq, void (^work)(void)); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async, - dispatch_queue_t dq, void (^work)(void)); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after, - dispatch_queue_t dq, void (^work)(void)); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_event_handler, - dispatch_source_t ds, void (^work)(void)); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_cancel_handler, - dispatch_source_t ds, void (^work)(void)); -#endif // MAC_INTERPOSE_FUNCTIONS -#endif // __APPLE__ -} // extern "C" -#endif +int memcmp(const void *a1, const void *a2, uptr size); +void memmove(void *to, const void *from, uptr size); +void* memset(void *block, int c, uptr size); +void* memcpy(void *to, const void *from, uptr size); +char* strcat(char *to, const char* from); // NOLINT +char* strchr(const char *str, int c); +int strcmp(const char *s1, const char* s2); +char* strcpy(char *to, const char* from); // NOLINT +uptr strlen(const char *s); +char* strncat(char *to, const char* from, uptr size); +int strncmp(const char *s1, const char* s2, uptr size); +char* strncpy(char *to, const char* from, uptr size); +uptr strnlen(const char *s, uptr maxlen); +int atoi(const char *nptr); +long atol(const char *nptr); // NOLINT +long strtol(const char *nptr, char **endptr, int base); // NOLINT +void longjmp(void *env, int value); +double frexp(double x, int *expptr); +} +# endif #endif // ASAN_INTERCEPTED_FUNCTIONS_H diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc index 6170974d6f5e..7e7deea29634 100644 --- a/lib/asan/asan_interceptors.cc +++ b/lib/asan/asan_interceptors.cc @@ -17,27 +17,40 @@ #include "asan_intercepted_functions.h" #include "asan_internal.h" #include "asan_mapping.h" +#include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" -#include "asan_thread_registry.h" #include "interception/interception.h" -#include "sanitizer/asan_interface.h" #include "sanitizer_common/sanitizer_libc.h" namespace __asan { +// Return true if we can quickly decide that the region is unpoisoned. +static inline bool QuickCheckForUnpoisonedRegion(uptr beg, uptr size) { + if (size == 0) return true; + if (size <= 32) + return !AddressIsPoisoned(beg) && + !AddressIsPoisoned(beg + size - 1) && + !AddressIsPoisoned(beg + size / 2); + return false; +} + // 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. // We check all shadow bytes. -#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \ - if (uptr __ptr = __asan_region_is_poisoned((uptr)(offset), size)) { \ - GET_CURRENT_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, __ptr, isWrite, /* access_size */1); \ - } \ -} while (0) +#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \ + uptr __offset = (uptr)(offset); \ + uptr __size = (uptr)(size); \ + uptr __bad = 0; \ + if (!QuickCheckForUnpoisonedRegion(__offset, __size) && \ + (__bad = __asan_region_is_poisoned(__offset, __size))) { \ + GET_CURRENT_PC_BP_SP; \ + __asan_report_error(pc, bp, sp, __bad, isWrite, __size); \ + } \ + } while (0) #define ASAN_READ_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, false) #define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true); @@ -76,9 +89,14 @@ static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { } void SetThreadName(const char *name) { - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); if (t) - t->summary()->set_name(name); + asanThreadRegistry().SetThreadName(t->tid(), name); +} + +static void DisableStrictInitOrderChecker() { + if (flags()->strict_init_order) + flags()->check_initialization_order = false; } } // namespace __asan @@ -89,37 +107,54 @@ using namespace __asan; // NOLINT #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ ASAN_WRITE_RANGE(ptr, size) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) ASAN_READ_RANGE(ptr, size) -#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ - do { \ - ctx = 0; \ - (void)ctx; \ - ENSURE_ASAN_INITED(); \ +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + do { \ + if (asan_init_is_running) \ + return REAL(func)(__VA_ARGS__); \ + ctx = 0; \ + (void)ctx; \ + ENSURE_ASAN_INITED(); \ } while (false) #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) do { } while (false) #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) do { } while (false) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name) #include "sanitizer_common/sanitizer_common_interceptors.inc" +#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(p, s) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) +#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) +#include "sanitizer_common/sanitizer_common_syscalls.inc" + static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { AsanThread *t = (AsanThread*)arg; - asanThreadRegistry().SetCurrent(t); - return t->ThreadStart(); + SetCurrentThread(t); + return t->ThreadStart(GetTid()); } #if ASAN_INTERCEPT_PTHREAD_CREATE +extern "C" int pthread_attr_getdetachstate(void *attr, int *v); + INTERCEPTOR(int, pthread_create, void *thread, void *attr, void *(*start_routine)(void*), void *arg) { + // Strict init-order checking in thread-hostile. + DisableStrictInitOrderChecker(); GET_STACK_TRACE_THREAD; - u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); - AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack); - asanThreadRegistry().RegisterThread(t); + int detached = 0; + if (attr != 0) + pthread_attr_getdetachstate(attr, &detached); + + u32 current_tid = GetCurrentTidOrInvalid(); + AsanThread *t = AsanThread::Create(start_routine, arg); + CreateThreadContextArgs args = { t, &stack }; + asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args); return REAL(pthread_create)(thread, attr, asan_thread_start, t); } #endif // ASAN_INTERCEPT_PTHREAD_CREATE #if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION INTERCEPTOR(void*, signal, int signum, void *handler) { - if (!AsanInterceptsSignal(signum)) { + if (!AsanInterceptsSignal(signum) || flags()->allow_user_segv_handler) { return REAL(signal)(signum, handler); } return 0; @@ -127,12 +162,12 @@ INTERCEPTOR(void*, signal, int signum, void *handler) { INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact) { - if (!AsanInterceptsSignal(signum)) { + if (!AsanInterceptsSignal(signum) || flags()->allow_user_segv_handler) { return REAL(sigaction)(signum, act, oldact); } return 0; } -#elif ASAN_POSIX +#elif SANITIZER_POSIX // We need to have defined REAL(sigaction) on posix systems. DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact); @@ -237,27 +272,32 @@ static inline int CharCmp(unsigned char c1, unsigned char c2) { return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; } -static inline int CharCaseCmp(unsigned char c1, unsigned char c2) { - int c1_low = ToLower(c1); - int c2_low = ToLower(c2); - return c1_low - c2_low; -} - INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { if (!asan_inited) return internal_memcmp(a1, a2, size); ENSURE_ASAN_INITED(); - unsigned char c1 = 0, c2 = 0; - const unsigned char *s1 = (const unsigned char*)a1; - const unsigned char *s2 = (const unsigned char*)a2; - uptr i; - for (i = 0; i < size; i++) { - c1 = s1[i]; - c2 = s2[i]; - if (c1 != c2) break; + if (flags()->replace_intrin) { + if (flags()->strict_memcmp) { + // Check the entire regions even if the first bytes of the buffers are + // different. + ASAN_READ_RANGE(a1, size); + ASAN_READ_RANGE(a2, size); + // Fallthrough to REAL(memcmp) below. + } else { + unsigned char c1 = 0, c2 = 0; + const unsigned char *s1 = (const unsigned char*)a1; + const unsigned char *s2 = (const unsigned char*)a2; + uptr i; + for (i = 0; i < size; i++) { + c1 = s1[i]; + c2 = s2[i]; + if (c1 != c2) break; + } + ASAN_READ_RANGE(s1, Min(i + 1, size)); + ASAN_READ_RANGE(s2, Min(i + 1, size)); + return CharCmp(c1, c2); + } } - ASAN_READ_RANGE(s1, Min(i + 1, size)); - ASAN_READ_RANGE(s2, Min(i + 1, size)); - return CharCmp(c1, c2); + return REAL(memcmp(a1, a2, size)); } INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { @@ -277,13 +317,9 @@ INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { ASAN_READ_RANGE(from, size); ASAN_WRITE_RANGE(to, size); } -#if MAC_INTERPOSE_FUNCTIONS // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8. // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116. return internal_memcpy(to, from, size); -#else - return REAL(memcpy)(to, from, size); -#endif } INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) { @@ -296,13 +332,9 @@ INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) { ASAN_READ_RANGE(from, size); ASAN_WRITE_RANGE(to, size); } -#if MAC_INTERPOSE_FUNCTIONS // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8. // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116. return internal_memmove(to, from, size); -#else - return REAL(memmove)(to, from, size); -#endif } INTERCEPTOR(void*, memset, void *block, int c, uptr size) { @@ -339,7 +371,12 @@ INTERCEPTOR(char*, strchr, const char *str, int c) { INTERCEPTOR(char*, index, const char *string, int c) ALIAS(WRAPPER_NAME(strchr)); # else -DEFINE_REAL(char*, index, const char *string, int c) +# if SANITIZER_MAC +DECLARE_REAL(char*, index, const char *string, int c) +OVERRIDE_FUNCTION(index, strchr); +# else +DEFINE_REAL(char*, index, const char *string, int c); +# endif # endif #endif // ASAN_INTERCEPT_INDEX @@ -400,7 +437,7 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { } INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT -#if MAC_INTERPOSE_FUNCTIONS +#if SANITIZER_MAC if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT #endif // strcpy is called from malloc_default_purgeable_zone() @@ -420,7 +457,7 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT #if ASAN_INTERCEPT_STRDUP INTERCEPTOR(char*, strdup, const char *s) { -#if MAC_INTERPOSE_FUNCTIONS +#if SANITIZER_MAC // FIXME: because internal_strdup() uses InternalAlloc(), which currently // just calls malloc() on Mac, we can't use internal_strdup() with the // dynamic runtime. We can remove the call to REAL(strdup) once InternalAlloc @@ -453,36 +490,6 @@ INTERCEPTOR(uptr, strlen, const char *s) { return length; } -#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP -INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { - ENSURE_ASAN_INITED(); - unsigned char c1, c2; - uptr i; - for (i = 0; ; i++) { - c1 = (unsigned char)s1[i]; - c2 = (unsigned char)s2[i]; - if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; - } - ASAN_READ_RANGE(s1, i + 1); - ASAN_READ_RANGE(s2, i + 1); - return CharCaseCmp(c1, c2); -} - -INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, uptr n) { - ENSURE_ASAN_INITED(); - unsigned char c1 = 0, c2 = 0; - uptr i; - for (i = 0; i < n; i++) { - c1 = (unsigned char)s1[i]; - c2 = (unsigned char)s2[i]; - if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; - } - ASAN_READ_RANGE(s1, Min(i + 1, n)); - ASAN_READ_RANGE(s2, Min(i + 1, n)); - return CharCaseCmp(c1, c2); -} -#endif // ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP - INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { if (!asan_inited) return internal_strncmp(s1, s2, size); // strncmp is called from malloc_default_purgeable_zone() @@ -530,7 +537,7 @@ static inline bool IsValidStrtolBase(int base) { } static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { - CHECK(endptr != 0); + CHECK(endptr); if (nptr == *endptr) { // No digits were found at strtol call, we need to find out the last // symbol accessed by strtoll on our own. @@ -561,7 +568,7 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT } INTERCEPTOR(int, atoi, const char *nptr) { -#if MAC_INTERPOSE_FUNCTIONS +#if SANITIZER_MAC if (!asan_inited) return REAL(atoi)(nptr); #endif ENSURE_ASAN_INITED(); @@ -580,7 +587,7 @@ INTERCEPTOR(int, atoi, const char *nptr) { } INTERCEPTOR(long, atol, const char *nptr) { // NOLINT -#if MAC_INTERPOSE_FUNCTIONS +#if SANITIZER_MAC if (!asan_inited) return REAL(atol)(nptr); #endif ENSURE_ASAN_INITED(); @@ -629,20 +636,39 @@ INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT } #endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL +static void AtCxaAtexit(void *unused) { + (void)unused; + StopInitOrderChecking(); +} + +#if ASAN_INTERCEPT___CXA_ATEXIT +INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, + void *dso_handle) { + ENSURE_ASAN_INITED(); + int res = REAL(__cxa_atexit)(func, arg, dso_handle); + REAL(__cxa_atexit)(AtCxaAtexit, 0, 0); + return res; +} +#endif // ASAN_INTERCEPT___CXA_ATEXIT + #define ASAN_INTERCEPT_FUNC(name) do { \ if (!INTERCEPT_FUNCTION(name) && flags()->verbosity > 0) \ Report("AddressSanitizer: failed to intercept '" #name "'\n"); \ } while (0) -#if defined(_WIN32) +#if SANITIZER_WINDOWS INTERCEPTOR_WINAPI(DWORD, CreateThread, void* security, uptr stack_size, DWORD (__stdcall *start_routine)(void*), void* arg, DWORD flags, void* tid) { + // Strict init-order checking in thread-hostile. + DisableStrictInitOrderChecker(); GET_STACK_TRACE_THREAD; - u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); - AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack); - asanThreadRegistry().RegisterThread(t); + u32 current_tid = GetCurrentTidOrInvalid(); + AsanThread *t = AsanThread::Create(start_routine, arg); + CreateThreadContextArgs args = { t, &stack }; + int detached = 0; // FIXME: how can we determine it on Windows? + asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args); return REAL(CreateThread)(security, stack_size, asan_thread_start, t, flags, tid); } @@ -661,10 +687,9 @@ void InitializeAsanInterceptors() { static bool was_called_once; CHECK(was_called_once == false); was_called_once = true; -#if MAC_INTERPOSE_FUNCTIONS +#if SANITIZER_MAC return; -#endif - +#else SANITIZER_COMMON_INTERCEPTORS_INIT; // Intercept mem* functions. @@ -673,12 +698,6 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(memset); if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { ASAN_INTERCEPT_FUNC(memcpy); - } else { -#if !MAC_INTERPOSE_FUNCTIONS - // If we're using dynamic interceptors on Mac, these two are just plain - // functions. - internal_memcpy(&REAL(memcpy), &REAL(memmove), sizeof(REAL(memmove))); -#endif } // Intercept str* functions. @@ -690,22 +709,14 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(strncat); ASAN_INTERCEPT_FUNC(strncmp); ASAN_INTERCEPT_FUNC(strncpy); -#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP - ASAN_INTERCEPT_FUNC(strcasecmp); - ASAN_INTERCEPT_FUNC(strncasecmp); -#endif #if ASAN_INTERCEPT_STRDUP ASAN_INTERCEPT_FUNC(strdup); #endif #if ASAN_INTERCEPT_STRNLEN ASAN_INTERCEPT_FUNC(strnlen); #endif -#if ASAN_INTERCEPT_INDEX -# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX +#if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX ASAN_INTERCEPT_FUNC(index); -# else - CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr))); -# endif #endif ASAN_INTERCEPT_FUNC(atoi); @@ -750,19 +761,20 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(pthread_create); #endif - // Some Windows-specific interceptors. -#if defined(_WIN32) - InitializeWindowsInterceptors(); + // Intercept atexit function. +#if ASAN_INTERCEPT___CXA_ATEXIT + ASAN_INTERCEPT_FUNC(__cxa_atexit); #endif - // Some Mac-specific interceptors. -#if defined(__APPLE__) - InitializeMacInterceptors(); + // Some Windows-specific interceptors. +#if SANITIZER_WINDOWS + InitializeWindowsInterceptors(); #endif if (flags()->verbosity > 0) { Report("AddressSanitizer: libc interceptors initialized\n"); } +#endif // SANITIZER_MAC } } // namespace __asan diff --git a/lib/asan/asan_interceptors.h b/lib/asan/asan_interceptors.h index 3b3e90ef93ff..91830aa145a9 100644 --- a/lib/asan/asan_interceptors.h +++ b/lib/asan/asan_interceptors.h @@ -32,9 +32,6 @@ DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act, namespace __asan { void InitializeAsanInterceptors(); -#if defined(__APPLE__) -void InitializeMacInterceptors(); -#endif // __APPLE__ } // namespace __asan diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h new file mode 100644 index 000000000000..24f76253bccd --- /dev/null +++ b/lib/asan/asan_interface_internal.h @@ -0,0 +1,141 @@ +//===-- asan_interface_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. +// +// This header can be included by the instrumented program to fetch +// data (mostly allocator statistics) from ASan runtime library. +//===----------------------------------------------------------------------===// +#ifndef ASAN_INTERFACE_INTERNAL_H +#define ASAN_INTERFACE_INTERNAL_H + +#include "sanitizer_common/sanitizer_internal_defs.h" + +using __sanitizer::uptr; + +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. + // Everytime the asan ABI changes we also change the version number in this + // name. Objects build with incompatible asan ABI version + // will not link with run-time. + // Changes between ABI versions: + // v1=>v2: added 'module_name' to __asan_global + // v2=>v3: stack frame description (created by the compiler) + // contains the function PC as the 3-rd field (see + // DescribeAddressIfStack). + void __asan_init_v3() SANITIZER_INTERFACE_ATTRIBUTE; + #define __asan_init __asan_init_v3 + + // This structure describes an instrumented global variable. + struct __asan_global { + uptr beg; // The address of the global. + uptr size; // The original size of the global. + uptr size_with_redzone; // The size with the redzone. + const char *name; // Name as a C string. + const char *module_name; // Module name as a C string. This pointer is a + // unique identifier of a module. + uptr has_dynamic_init; // Non-zero if the global has dynamic initializer. + }; + + // These two functions should be called by the instrumented code. + // 'globals' is an array of structures describing 'n' globals. + void __asan_register_globals(__asan_global *globals, uptr n) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_unregister_globals(__asan_global *globals, uptr n) + SANITIZER_INTERFACE_ATTRIBUTE; + + // These two functions should be called before and after dynamic initializers + // of a single module run, respectively. + void __asan_before_dynamic_init(const char *module_name) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_after_dynamic_init() + SANITIZER_INTERFACE_ATTRIBUTE; + + // 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. + uptr __asan_stack_malloc(uptr size, uptr real_stack) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) + SANITIZER_INTERFACE_ATTRIBUTE; + + // These two functions are used by instrumented code in the + // use-after-scope mode. They mark memory for local variables as + // unaddressable when they leave scope and addressable before the + // function exits. + void __asan_poison_stack_memory(uptr addr, uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_unpoison_stack_memory(uptr addr, uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + + // Performs cleanup before a NoReturn function. Must be called before things + // like _exit and execl to avoid false positives on stack. + void __asan_handle_no_return() SANITIZER_INTERFACE_ATTRIBUTE; + + void __asan_poison_memory_region(void const volatile *addr, uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_unpoison_memory_region(void const volatile *addr, uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + + bool __asan_address_is_poisoned(void const volatile *addr) + SANITIZER_INTERFACE_ATTRIBUTE; + + uptr __asan_region_is_poisoned(uptr beg, uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + + void __asan_describe_address(uptr addr) + SANITIZER_INTERFACE_ATTRIBUTE; + + void __asan_report_error(uptr pc, uptr bp, uptr sp, + uptr addr, bool is_write, uptr access_size) + SANITIZER_INTERFACE_ATTRIBUTE; + + int __asan_set_error_exit_code(int exit_code) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_set_death_callback(void (*callback)(void)) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_set_error_report_callback(void (*callback)(const char*)) + SANITIZER_INTERFACE_ATTRIBUTE; + + /* OPTIONAL */ void __asan_on_error() + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + + /* OPTIONAL */ bool __asan_symbolize(const void *pc, char *out_buffer, + int out_size) + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + + uptr __asan_get_estimated_allocated_size(uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + bool __asan_get_ownership(const void *p) + SANITIZER_INTERFACE_ATTRIBUTE; + uptr __asan_get_allocated_size(const void *p) + SANITIZER_INTERFACE_ATTRIBUTE; + uptr __asan_get_current_allocated_bytes() + SANITIZER_INTERFACE_ATTRIBUTE; + uptr __asan_get_heap_size() + SANITIZER_INTERFACE_ATTRIBUTE; + uptr __asan_get_free_bytes() + SANITIZER_INTERFACE_ATTRIBUTE; + uptr __asan_get_unmapped_bytes() + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_print_accumulated_stats() + SANITIZER_INTERFACE_ATTRIBUTE; + + /* OPTIONAL */ const char* __asan_default_options() + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + + /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size) + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + /* OPTIONAL */ void __asan_free_hook(void *ptr) + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; +} // extern "C" + +#endif // ASAN_INTERFACE_INTERNAL_H diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h index 5d3bffa814da..7a4d74472bcd 100644 --- a/lib/asan/asan_internal.h +++ b/lib/asan/asan_internal.h @@ -15,45 +15,15 @@ #define ASAN_INTERNAL_H #include "asan_flags.h" +#include "asan_interface_internal.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_libc.h" -#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32) -# error "This operating system is not supported by AddressSanitizer" -#endif - #define ASAN_DEFAULT_FAILURE_EXITCODE 1 -#if defined(__linux__) -# define ASAN_LINUX 1 -#else -# define ASAN_LINUX 0 -#endif - -#if defined(__APPLE__) -# define ASAN_MAC 1 -#else -# define ASAN_MAC 0 -#endif - -#if defined(_WIN32) -# define ASAN_WINDOWS 1 -#else -# define ASAN_WINDOWS 0 -#endif - -#if defined(__ANDROID__) || defined(ANDROID) -# define ASAN_ANDROID 1 -#else -# define ASAN_ANDROID 0 -#endif - - -#define ASAN_POSIX (ASAN_LINUX || ASAN_MAC) - -#if __has_feature(address_sanitizer) +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) # error "The AddressSanitizer run-time should not be" " instrumented by AddressSanitizer" #endif @@ -62,7 +32,7 @@ // If set, asan will install its own SEGV signal handler. #ifndef ASAN_NEEDS_SEGV -# if ASAN_ANDROID == 1 +# if SANITIZER_ANDROID == 1 # define ASAN_NEEDS_SEGV 0 # else # define ASAN_NEEDS_SEGV 1 @@ -90,6 +60,10 @@ # endif #endif +#ifndef ASAN_USE_PREINIT_ARRAY +# define ASAN_USE_PREINIT_ARRAY (SANITIZER_LINUX && !SANITIZER_ANDROID) +#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 @@ -118,6 +92,7 @@ void UnsetAlternateSignalStack(); void InstallSignalHandlers(); void ReadContextStack(void *context, uptr *stack, uptr *ssize); void AsanPlatformThreadInit(); +void StopInitOrderChecking(); // Wrapper for TLS/TSD. void AsanTSDInit(void (*destructor)(void *tsd)); @@ -126,24 +101,14 @@ void AsanTSDSet(void *tsd); void AppendToErrorMessageBuffer(const char *buffer); -// asan_poisoning.cc -// Poisons the shadow memory for "size" bytes starting from "addr". -void PoisonShadow(uptr addr, uptr size, u8 value); -// Poisons the shadow memory for "redzone_size" bytes starting from -// "addr + size". -void PoisonShadowPartialRightRedzone(uptr addr, - uptr size, - uptr redzone_size, - u8 value); - // Platfrom-specific options. -#ifdef __APPLE__ +#if SANITIZER_MAC bool PlatformHasDifferentMemcpyAndMemmove(); # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \ (PlatformHasDifferentMemcpyAndMemmove()) #else # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true -#endif // __APPLE__ +#endif // SANITIZER_MAC // Add convenient macro for interface functions that may be represented as // weak hooks. diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc index 845493de0956..17bb4ca5f01c 100644 --- a/lib/asan/asan_linux.cc +++ b/lib/asan/asan_linux.cc @@ -11,12 +11,13 @@ // // Linux-specific details. //===----------------------------------------------------------------------===// -#ifdef __linux__ + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_thread.h" -#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" @@ -31,7 +32,7 @@ #include <unistd.h> #include <unwind.h> -#if !ASAN_ANDROID +#if !SANITIZER_ANDROID // FIXME: where to get ucontext on Android? #include <sys/ucontext.h> #endif @@ -50,7 +51,7 @@ void *AsanDoesNotSupportStaticLinkage() { } void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { -#if ASAN_ANDROID +#if SANITIZER_ANDROID *pc = *sp = *bp = 0; #elif defined(__arm__) ucontext_t *ucontext = (ucontext_t*)context; @@ -101,25 +102,7 @@ void AsanPlatformThreadInit() { // Nothing here for now. } -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) { -#if defined(__arm__) || \ - defined(__powerpc__) || defined(__powerpc64__) || \ - defined(__sparc__) - fast = false; -#endif - if (!fast) - return stack->SlowUnwindStack(pc, max_s); - stack->size = 0; - stack->trace[0] = pc; - if (max_s > 1) { - stack->max_size = max_s; - if (!asan_inited) return; - if (AsanThread *t = asanThreadRegistry().GetCurrent()) - stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom()); - } -} - -#if !ASAN_ANDROID +#if !SANITIZER_ANDROID void ReadContextStack(void *context, uptr *stack, uptr *ssize) { ucontext_t *ucp = (ucontext_t*)context; *stack = (uptr)ucp->uc_stack.ss_sp; @@ -133,4 +116,4 @@ void ReadContextStack(void *context, uptr *stack, uptr *ssize) { } // namespace __asan -#endif // __linux__ +#endif // SANITIZER_LINUX diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc index 3ed9e06eeb6f..4313534008e7 100644 --- a/lib/asan/asan_mac.cc +++ b/lib/asan/asan_mac.cc @@ -12,7 +12,8 @@ // Mac-specific details. //===----------------------------------------------------------------------===// -#ifdef __APPLE__ +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC #include "asan_interceptors.h" #include "asan_internal.h" @@ -20,7 +21,6 @@ #include "asan_mapping.h" #include "asan_stack.h" #include "asan_thread.h" -#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_libc.h" #include <crt_externs.h> // for _NSGetArgv @@ -36,7 +36,6 @@ #include <stdlib.h> // for free() #include <unistd.h> #include <libkern/OSAtomic.h> -#include <CoreFoundation/CFString.h> namespace __asan { @@ -59,9 +58,9 @@ int GetMacosVersion() { uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]); for (uptr i = 0; i < maxlen; i++) version[i] = '\0'; // Get the version length. - CHECK(sysctl(mib, 2, 0, &len, 0, 0) != -1); - CHECK(len < maxlen); - CHECK(sysctl(mib, 2, version, &len, 0, 0) != -1); + CHECK_NE(sysctl(mib, 2, 0, &len, 0, 0), -1); + CHECK_LT(len, maxlen); + CHECK_NE(sysctl(mib, 2, version, &len, 0, 0), -1); switch (version[0]) { case '9': return MACOS_VERSION_LEOPARD; case '1': { @@ -89,16 +88,52 @@ extern "C" void __asan_init(); static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES"; +LowLevelAllocator allocator_for_env; + +// Change the value of the env var |name|, leaking the original value. +// If |name_value| is NULL, the variable is deleted from the environment, +// otherwise the corresponding "NAME=value" string is replaced with +// |name_value|. +void LeakyResetEnv(const char *name, const char *name_value) { + char ***env_ptr = _NSGetEnviron(); + CHECK(env_ptr); + char **environ = *env_ptr; + CHECK(environ); + uptr name_len = internal_strlen(name); + while (*environ != 0) { + uptr len = internal_strlen(*environ); + if (len > name_len) { + const char *p = *environ; + if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') { + // Match. + if (name_value) { + // Replace the old value with the new one. + *environ = const_cast<char*>(name_value); + } else { + // Shift the subsequent pointers back. + char **del = environ; + do { + del[0] = del[1]; + } while (*del++); + } + } + } + environ++; + } +} void MaybeReexec() { if (!flags()->allow_reexec) return; -#if MAC_INTERPOSE_FUNCTIONS - // If the program is linked with the dynamic ASan runtime library, make sure - // the library is preloaded so that the wrappers work. If it is not, set - // DYLD_INSERT_LIBRARIES and re-exec ourselves. + // Make sure the dynamic ASan runtime library is preloaded so that the + // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec + // ourselves. Dl_info info; CHECK(dladdr((void*)((uptr)__asan_init), &info)); - const char *dyld_insert_libraries = GetEnv(kDyldInsertLibraries); + char *dyld_insert_libraries = + const_cast<char*>(GetEnv(kDyldInsertLibraries)); + uptr old_env_len = dyld_insert_libraries ? + internal_strlen(dyld_insert_libraries) : 0; + uptr fname_len = internal_strlen(info.dli_fname); if (!dyld_insert_libraries || !REAL(strstr)(dyld_insert_libraries, info.dli_fname)) { // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime @@ -106,19 +141,80 @@ void MaybeReexec() { char program_name[1024]; uint32_t buf_size = sizeof(program_name); _NSGetExecutablePath(program_name, &buf_size); - // Ok to use setenv() since the wrappers don't depend on the value of - // asan_inited. - setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); + char *new_env = const_cast<char*>(info.dli_fname); + if (dyld_insert_libraries) { + // Append the runtime dylib name to the existing value of + // DYLD_INSERT_LIBRARIES. + new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2); + internal_strncpy(new_env, dyld_insert_libraries, old_env_len); + new_env[old_env_len] = ':'; + // Copy fname_len and add a trailing zero. + internal_strncpy(new_env + old_env_len + 1, info.dli_fname, + fname_len + 1); + // Ok to use setenv() since the wrappers don't depend on the value of + // asan_inited. + setenv(kDyldInsertLibraries, new_env, /*overwrite*/1); + } else { + // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name. + setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); + } if (flags()->verbosity >= 1) { Report("exec()-ing the program with\n"); - Report("%s=%s\n", kDyldInsertLibraries, info.dli_fname); + Report("%s=%s\n", kDyldInsertLibraries, new_env); Report("to enable ASan wrappers.\n"); Report("Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n"); } execv(program_name, *_NSGetArgv()); + } else { + // DYLD_INSERT_LIBRARIES is set and contains the runtime library. + if (old_env_len == fname_len) { + // It's just the runtime library name - fine to unset the variable. + LeakyResetEnv(kDyldInsertLibraries, NULL); + } else { + uptr env_name_len = internal_strlen(kDyldInsertLibraries); + // Allocate memory to hold the previous env var name, its value, the '=' + // sign and the '\0' char. + char *new_env = (char*)allocator_for_env.Allocate( + old_env_len + 2 + env_name_len); + CHECK(new_env); + internal_memset(new_env, '\0', old_env_len + 2 + env_name_len); + internal_strncpy(new_env, kDyldInsertLibraries, env_name_len); + new_env[env_name_len] = '='; + char *new_env_pos = new_env + env_name_len + 1; + + // Iterate over colon-separated pieces of |dyld_insert_libraries|. + char *piece_start = dyld_insert_libraries; + char *piece_end = NULL; + char *old_env_end = dyld_insert_libraries + old_env_len; + do { + if (piece_start[0] == ':') piece_start++; + piece_end = REAL(strchr)(piece_start, ':'); + if (!piece_end) piece_end = dyld_insert_libraries + old_env_len; + if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break; + uptr piece_len = piece_end - piece_start; + + // If the current piece isn't the runtime library name, + // append it to new_env. + if ((piece_len != fname_len) || + (internal_strncmp(piece_start, info.dli_fname, fname_len) != 0)) { + if (new_env_pos != new_env + env_name_len + 1) { + new_env_pos[0] = ':'; + new_env_pos++; + } + internal_strncpy(new_env_pos, piece_start, piece_len); + } + // Move on to the next piece. + new_env_pos += piece_len; + piece_start = piece_end; + } while (piece_start < old_env_end); + + // Can't use setenv() here, because it requires the allocator to be + // initialized. + // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in + // a separate function called after InitializeAllocator(). + LeakyResetEnv(kDyldInsertLibraries, new_env); + } } -#endif // MAC_INTERPOSE_FUNCTIONS - // If we're not using the dynamic runtime, do nothing. } // No-op. Mac does not support static linkage anyway. @@ -131,83 +227,12 @@ bool AsanInterceptsSignal(int signum) { } void AsanPlatformThreadInit() { - // For the first program thread, we can't replace the allocator before - // __CFInitialize() has been called. If it hasn't, we'll call - // MaybeReplaceCFAllocator() later on this thread. - // For other threads __CFInitialize() has been called before their creation. - // See also asan_malloc_mac.cc. - if (((CFRuntimeBase*)kCFAllocatorSystemDefault)->_cfisa) { - MaybeReplaceCFAllocator(); - } -} - -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) { - (void)fast; - stack->size = 0; - stack->trace[0] = pc; - if ((max_s) > 1) { - stack->max_size = max_s; - if (!asan_inited) return; - if (AsanThread *t = asanThreadRegistry().GetCurrent()) - stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom()); - } } void ReadContextStack(void *context, uptr *stack, uptr *ssize) { UNIMPLEMENTED(); } -// The range of pages to be used for escape islands. -// TODO(glider): instead of mapping a fixed range we must find a range of -// unmapped pages in vmmap and take them. -// These constants were chosen empirically and may not work if the shadow -// memory layout changes. Unfortunately they do necessarily depend on -// kHighMemBeg or kHighMemEnd. -static void *island_allocator_pos = 0; - -#if SANITIZER_WORDSIZE == 32 -# define kIslandEnd (0xffdf0000 - GetPageSizeCached()) -# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached()) -#else -# define kIslandEnd (0x7fffffdf0000 - GetPageSizeCached()) -# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached()) -#endif - -extern "C" -mach_error_t __interception_allocate_island(void **ptr, - uptr unused_size, - void *unused_hint) { - if (!island_allocator_pos) { - island_allocator_pos = - internal_mmap((void*)kIslandBeg, kIslandEnd - kIslandBeg, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - -1, 0); - if (island_allocator_pos != (void*)kIslandBeg) { - return KERN_NO_SPACE; - } - if (flags()->verbosity) { - Report("Mapped pages %p--%p for branch islands.\n", - (void*)kIslandBeg, (void*)kIslandEnd); - } - // Should not be very performance-critical. - internal_memset(island_allocator_pos, 0xCC, kIslandEnd - kIslandBeg); - }; - *ptr = island_allocator_pos; - island_allocator_pos = (char*)island_allocator_pos + GetPageSizeCached(); - if (flags()->verbosity) { - Report("Branch island allocated at %p\n", *ptr); - } - return err_none; -} - -extern "C" -mach_error_t __interception_deallocate_island(void *ptr) { - // Do nothing. - // TODO(glider): allow to free and reuse the island memory. - return err_none; -} - // Support for the following functions from libdispatch on Mac OS: // dispatch_async_f() // dispatch_async() @@ -237,9 +262,6 @@ mach_error_t __interception_deallocate_island(void *ptr) { // The implementation details are at // http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c -typedef void* pthread_workqueue_t; -typedef void* pthread_workitem_handle_t; - typedef void* dispatch_group_t; typedef void* dispatch_queue_t; typedef void* dispatch_source_t; @@ -254,32 +276,16 @@ typedef struct { u32 parent_tid; } asan_block_context_t; -// We use extern declarations of libdispatch functions here instead -// of including <dispatch/dispatch.h>. This header is not present on -// Mac OS X Leopard and eariler, and although we don't expect ASan to -// work on legacy systems, it's bad to break the build of -// LLVM compiler-rt there. -extern "C" { -void dispatch_async_f(dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -void dispatch_sync_f(dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -void dispatch_after_f(dispatch_time_t when, dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -void dispatch_barrier_async_f(dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -void dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -} // extern "C" - -static ALWAYS_INLINE +ALWAYS_INLINE void asan_register_worker_thread(int parent_tid, StackTrace *stack) { - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); if (!t) { - t = AsanThread::Create(parent_tid, 0, 0, stack); - asanThreadRegistry().RegisterThread(t); + t = AsanThread::Create(0, 0); + CreateThreadContextArgs args = { t, stack }; + asanThreadRegistry().CreateThread(*(uptr*)t, true, parent_tid, &args); t->Init(); - asanThreadRegistry().SetCurrent(t); + asanThreadRegistry().StartThread(t->tid(), 0, 0); + SetCurrentThread(t); } } @@ -313,7 +319,7 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); asan_ctxt->block = ctxt; asan_ctxt->func = func; - asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + asan_ctxt->parent_tid = GetCurrentTidOrInvalid(); return asan_ctxt; } @@ -364,14 +370,7 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, asan_dispatch_call_block_and_release); } -#if MAC_INTERPOSE_FUNCTIONS && !defined(MISSING_BLOCKS_SUPPORT) -// dispatch_async, dispatch_group_async and others tailcall the corresponding -// dispatch_*_f functions. When wrapping functions with mach_override, those -// dispatch_*_f are intercepted automatically. But with dylib interposition -// this does not work, because the calls within the same library are not -// interposed. -// Therefore we need to re-implement dispatch_async and friends. - +#if !defined(MISSING_BLOCKS_SUPPORT) extern "C" { // FIXME: consolidate these declarations with asan_intercepted_functions.h. void dispatch_async(dispatch_queue_t dq, void(^work)(void)); @@ -386,7 +385,7 @@ void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void)); #define GET_ASAN_BLOCK(work) \ void (^asan_block)(void); \ - int parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); \ + int parent_tid = GetCurrentTidOrInvalid(); \ asan_block = ^(void) { \ GET_STACK_TRACE_THREAD; \ asan_register_worker_thread(parent_tid, &stack); \ @@ -424,53 +423,4 @@ INTERCEPTOR(void, dispatch_source_set_event_handler, } #endif -// See http://opensource.apple.com/source/CF/CF-635.15/CFString.c -int __CFStrIsConstant(CFStringRef str) { - CFRuntimeBase *base = (CFRuntimeBase*)str; -#if __LP64__ - return base->_rc == 0; -#else - return (base->_cfinfo[CF_RC_BITS]) == 0; -#endif -} - -INTERCEPTOR(CFStringRef, CFStringCreateCopy, CFAllocatorRef alloc, - CFStringRef str) { - if (__CFStrIsConstant(str)) { - return str; - } else { - return REAL(CFStringCreateCopy)(alloc, str); - } -} - -DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr) - -DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize, void) - -namespace __asan { - -void InitializeMacInterceptors() { - CHECK(INTERCEPT_FUNCTION(dispatch_async_f)); - CHECK(INTERCEPT_FUNCTION(dispatch_sync_f)); - CHECK(INTERCEPT_FUNCTION(dispatch_after_f)); - CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f)); - CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f)); - // Normally CFStringCreateCopy should not copy constant CF strings. - // Replacing the default CFAllocator causes constant strings to be copied - // rather than just returned, which leads to bugs in big applications like - // Chromium and WebKit, see - // http://code.google.com/p/address-sanitizer/issues/detail?id=10 - // Until this problem is fixed we need to check that the string is - // non-constant before calling CFStringCreateCopy. - CHECK(INTERCEPT_FUNCTION(CFStringCreateCopy)); - // Some of the library functions call free() directly, so we have to - // intercept it. - CHECK(INTERCEPT_FUNCTION(free)); - if (flags()->replace_cfallocator) { - CHECK(INTERCEPT_FUNCTION(__CFInitialize)); - } -} - -} // namespace __asan - -#endif // __APPLE__ +#endif // SANITIZER_MAC diff --git a/lib/asan/asan_mac.h b/lib/asan/asan_mac.h index be913865c440..b1a1966dbc6e 100644 --- a/lib/asan/asan_mac.h +++ b/lib/asan/asan_mac.h @@ -12,7 +12,7 @@ // Mac-specific ASan definitions. //===----------------------------------------------------------------------===// #ifndef ASAN_MAC_H -#define ASAN_MAC_H +#define ASAN__MAC_H // CF_RC_BITS, the layout of CFRuntimeBase and __CFStrIsConstant are internal // and subject to change in further CoreFoundation versions. Apple does not diff --git a/lib/asan/asan_malloc_linux.cc b/lib/asan/asan_malloc_linux.cc index b95cfe3149b7..20e636b9b3c0 100644 --- a/lib/asan/asan_malloc_linux.cc +++ b/lib/asan/asan_malloc_linux.cc @@ -13,16 +13,16 @@ // We simply define functions like malloc, free, realloc, etc. // They will replace the corresponding libc functions automagically. //===----------------------------------------------------------------------===// -#ifdef __linux__ + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX #include "asan_allocator.h" #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_stack.h" -#include "asan_thread_registry.h" -#include "sanitizer/asan_interface.h" -#if ASAN_ANDROID +#if SANITIZER_ANDROID DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size) DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr) DECLARE_REAL_AND_INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) @@ -147,4 +147,4 @@ INTERCEPTOR(void, malloc_stats, void) { __asan_print_accumulated_stats(); } -#endif // __linux__ +#endif // SANITIZER_LINUX diff --git a/lib/asan/asan_malloc_mac.cc b/lib/asan/asan_malloc_mac.cc index 545ede2debe7..4f353cb99ca7 100644 --- a/lib/asan/asan_malloc_mac.cc +++ b/lib/asan/asan_malloc_mac.cc @@ -12,7 +12,8 @@ // Mac-specific malloc interception. //===----------------------------------------------------------------------===// -#ifdef __APPLE__ +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC #include <AvailabilityMacros.h> #include <CoreFoundation/CFBase.h> @@ -26,7 +27,6 @@ #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" -#include "asan_thread_registry.h" // Similar code is used in Google Perftools, // http://code.google.com/p/google-perftools. @@ -36,85 +36,108 @@ using namespace __asan; // NOLINT // TODO(glider): do we need both zones? static malloc_zone_t *system_malloc_zone = 0; -static malloc_zone_t *system_purgeable_zone = 0; static malloc_zone_t asan_zone; -CFAllocatorRef cf_asan = 0; - -// _CFRuntimeCreateInstance() checks whether the supplied allocator is -// kCFAllocatorSystemDefault and, if it is not, stores the allocator reference -// at the beginning of the allocated memory and returns the pointer to the -// allocated memory plus sizeof(CFAllocatorRef). See -// http://www.opensource.apple.com/source/CF/CF-635.21/CFRuntime.c -// Pointers returned by _CFRuntimeCreateInstance() can then be passed directly -// to free() or CFAllocatorDeallocate(), which leads to false invalid free -// reports. -// The corresponding rdar bug is http://openradar.appspot.com/radar?id=1796404. -void* ALWAYS_INLINE get_saved_cfallocator_ref(void *ptr) { - if (flags()->replace_cfallocator) { - // Make sure we're not hitting the previous page. This may be incorrect - // if ASan's malloc returns an address ending with 0xFF8, which will be - // then padded to a page boundary with a CFAllocatorRef. - uptr arith_ptr = (uptr)ptr; - if ((arith_ptr & 0xFFF) > sizeof(CFAllocatorRef)) { - CFAllocatorRef *saved = - (CFAllocatorRef*)(arith_ptr - sizeof(CFAllocatorRef)); - if ((*saved == cf_asan) && asan_mz_size(saved)) ptr = (void*)saved; - } + +INTERCEPTOR(malloc_zone_t *, malloc_create_zone, + vm_size_t start_size, unsigned zone_flags) { + if (!asan_inited) __asan_init(); + GET_STACK_TRACE_MALLOC; + malloc_zone_t *new_zone = + (malloc_zone_t*)asan_malloc(sizeof(asan_zone), &stack); + internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone)); + new_zone->zone_name = NULL; // The name will be changed anyway. + return new_zone; +} + +INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) { + if (!asan_inited) __asan_init(); + return &asan_zone; +} + +INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) { + // FIXME: ASan should support purgeable allocations. + // https://code.google.com/p/address-sanitizer/issues/detail?id=139 + if (!asan_inited) __asan_init(); + return &asan_zone; +} + +INTERCEPTOR(void, malloc_make_purgeable, void *ptr) { + // FIXME: ASan should support purgeable allocations. Ignoring them is fine + // for now. + if (!asan_inited) __asan_init(); +} + +INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) { + // FIXME: ASan should support purgeable allocations. Ignoring them is fine + // for now. + if (!asan_inited) __asan_init(); + // Must return 0 if the contents were not purged since the last call to + // malloc_make_purgeable(). + return 0; +} + +INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) { + if (!asan_inited) __asan_init(); + // Allocate |strlen("asan-") + 1 + internal_strlen(name)| bytes. + size_t buflen = 6 + (name ? internal_strlen(name) : 0); + InternalScopedBuffer<char> new_name(buflen); + if (name && zone->introspect == asan_zone.introspect) { + internal_snprintf(new_name.data(), buflen, "asan-%s", name); + name = new_name.data(); } - return ptr; + + // Call the system malloc's implementation for both external and our zones, + // since that appropriately changes VM region protections on the zone. + REAL(malloc_set_zone_name)(zone, name); +} + +INTERCEPTOR(void *, malloc, size_t size) { + if (!asan_inited) __asan_init(); + GET_STACK_TRACE_MALLOC; + void *res = asan_malloc(size, &stack); + return res; } -// The free() implementation provided by OS X calls malloc_zone_from_ptr() -// to find the owner of |ptr|. If the result is 0, an invalid free() is -// reported. Our implementation falls back to asan_free() in this case -// in order to print an ASan-style report. -// -// For the objects created by _CFRuntimeCreateInstance a CFAllocatorRef is -// placed at the beginning of the allocated chunk and the pointer returned by -// our allocator is off by sizeof(CFAllocatorRef). This pointer can be then -// passed directly to free(), which will lead to errors. -// To overcome this we're checking whether |ptr-sizeof(CFAllocatorRef)| -// contains a pointer to our CFAllocator (assuming no other allocator is used). -// See http://code.google.com/p/address-sanitizer/issues/detail?id=70 for more -// info. INTERCEPTOR(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 { - if (!asan_mz_size(ptr)) ptr = get_saved_cfallocator_ref(ptr); - GET_STACK_TRACE_FREE; - asan_free(ptr, &stack, FROM_MALLOC); - } + if (!asan_inited) __asan_init(); + if (!ptr) return; + GET_STACK_TRACE_FREE; + asan_free(ptr, &stack, FROM_MALLOC); } -// We can't always replace the default CFAllocator with cf_asan right in -// ReplaceSystemMalloc(), because it is sometimes called before -// __CFInitialize(), when the default allocator is invalid and replacing it may -// crash the program. Instead we wait for the allocator to initialize and jump -// in just after __CFInitialize(). Nobody is going to allocate memory using -// CFAllocators before that, so we won't miss anything. -// -// See http://code.google.com/p/address-sanitizer/issues/detail?id=87 -// and http://opensource.apple.com/source/CF/CF-550.43/CFRuntime.c -INTERCEPTOR(void, __CFInitialize, void) { - // If the runtime is built as dynamic library, __CFInitialize wrapper may be - // called before __asan_init. -#if !MAC_INTERPOSE_FUNCTIONS - CHECK(flags()->replace_cfallocator); - CHECK(asan_inited); -#endif - REAL(__CFInitialize)(); - if (!cf_asan && asan_inited) MaybeReplaceCFAllocator(); +INTERCEPTOR(void *, realloc, void *ptr, size_t size) { + if (!asan_inited) __asan_init(); + GET_STACK_TRACE_MALLOC; + return asan_realloc(ptr, size, &stack); +} + +INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) { + if (!asan_inited) __asan_init(); + GET_STACK_TRACE_MALLOC; + return asan_calloc(nmemb, size, &stack); +} + +INTERCEPTOR(void *, valloc, size_t size) { + if (!asan_inited) __asan_init(); + GET_STACK_TRACE_MALLOC; + return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); +} + +INTERCEPTOR(size_t, malloc_good_size, size_t size) { + if (!asan_inited) __asan_init(); + return asan_zone.introspect->good_size(&asan_zone, size); +} + +INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) { + if (!asan_inited) __asan_init(); + CHECK(memptr); + GET_STACK_TRACE_MALLOC; + void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC); + if (result) { + *memptr = result; + return 0; + } + return -1; } namespace { @@ -134,15 +157,6 @@ void *mz_malloc(malloc_zone_t *zone, size_t size) { 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_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. @@ -174,31 +188,14 @@ void *mz_valloc(malloc_zone_t *zone, size_t size) { void ALWAYS_INLINE free_common(void *context, void *ptr) { if (!ptr) return; - if (asan_mz_size(ptr)) { - GET_STACK_TRACE_FREE; + GET_STACK_TRACE_FREE; + // FIXME: need to retire this flag. + if (!flags()->mac_ignore_invalid_free) { asan_free(ptr, &stack, FROM_MALLOC); } else { - // If the pointer does not belong to any of the zones, use one of the - // fallback methods to free memory. - malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); - if (zone_ptr == system_purgeable_zone) { - // allocations from malloc_default_purgeable_zone() done before - // __asan_init() may be occasionally freed via free_common(). - // see http://code.google.com/p/address-sanitizer/issues/detail?id=99. - malloc_zone_free(zone_ptr, ptr); - } else { - // If the memory chunk pointer was moved to store additional - // CFAllocatorRef, fix it back. - ptr = get_saved_cfallocator_ref(ptr); - GET_STACK_TRACE_FREE; - if (!flags()->mac_ignore_invalid_free) { - asan_free(ptr, &stack, FROM_MALLOC); - } else { - GET_ZONE_FOR_PTR(ptr); - WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); - return; - } - } + GET_ZONE_FOR_PTR(ptr); + WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); + return; } } @@ -207,10 +204,6 @@ void mz_free(malloc_zone_t *zone, void *ptr) { free_common(zone, ptr); } -void cf_free(void *ptr, void *info) { - free_common(info, ptr); -} - void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { if (!ptr) { GET_STACK_TRACE_MALLOC; @@ -230,29 +223,11 @@ void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { } } -void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) { - if (!ptr) { - GET_STACK_TRACE_MALLOC; - return asan_malloc(size, &stack); - } else { - if (asan_mz_size(ptr)) { - GET_STACK_TRACE_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. - GET_STACK_TRACE_FREE; - GET_ZONE_FOR_PTR(ptr); - ReportMacCfReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); - } - } -} - void mz_destroy(malloc_zone_t* zone) { // A no-op -- we will not be destroyed! - Printf("mz_destroy() called -- ignoring\n"); + Report("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 @@ -309,7 +284,7 @@ void mi_force_unlock(malloc_zone_t *zone) { void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { AsanMallocStats malloc_stats; - asanThreadRegistry().FillMallocStatistics(&malloc_stats); + FillMallocStatistics(&malloc_stats); CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); } @@ -324,23 +299,7 @@ boolean_t mi_zone_locked(malloc_zone_t *zone) { } // unnamed namespace -extern int __CFRuntimeClassTableSize; - namespace __asan { -void MaybeReplaceCFAllocator() { - static CFAllocatorContext asan_context = { - /*version*/ 0, /*info*/ &asan_zone, - /*retain*/ 0, /*release*/ 0, - /*copyDescription*/0, - /*allocate*/ &cf_malloc, - /*reallocate*/ &cf_realloc, - /*deallocate*/ &cf_free, - /*preferredSize*/ 0 }; - if (!cf_asan) - cf_asan = CFAllocatorCreate(kCFAllocatorUseContext, &asan_context); - if (flags()->replace_cfallocator && CFAllocatorGetDefault() != cf_asan) - CFAllocatorSetDefault(cf_asan); -} void ReplaceSystemMalloc() { static malloc_introspection_t asan_introspection; @@ -380,42 +339,11 @@ void ReplaceSystemMalloc() { 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. + // Register the ASan 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 __CFInitialize() hasn't been called yet, cf_asan will be created and - // installed as the default allocator after __CFInitialize() finishes (see - // the interceptor for __CFInitialize() above). Otherwise install cf_asan - // right now. On both Snow Leopard and Lion __CFInitialize() calls - // __CFAllocatorInitialize(), which initializes the _base._cfisa field of - // the default allocators we check here. - if (((CFRuntimeBase*)kCFAllocatorSystemDefault)->_cfisa) { - MaybeReplaceCFAllocator(); - } } } // namespace __asan -#endif // __APPLE__ +#endif // SANITIZER_MAC diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc index 9fcfea56384f..31fb777c7045 100644 --- a/lib/asan/asan_malloc_win.cc +++ b/lib/asan/asan_malloc_win.cc @@ -11,7 +11,9 @@ // // Windows-specific malloc interception. //===----------------------------------------------------------------------===// -#ifdef _WIN32 + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_WINDOWS #include "asan_allocator.h" #include "asan_interceptors.h" diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h index 5e3067031f4a..f04629222419 100644 --- a/lib/asan/asan_mapping.h +++ b/lib/asan/asan_mapping.h @@ -18,6 +18,37 @@ // The full explanation of the memory mapping could be found here: // http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm +// +// Typical shadow mapping on Linux/x86_64 with SHADOW_OFFSET == 0x00007fff8000: +// || `[0x10007fff8000, 0x7fffffffffff]` || HighMem || +// || `[0x02008fff7000, 0x10007fff7fff]` || HighShadow || +// || `[0x00008fff7000, 0x02008fff6fff]` || ShadowGap || +// || `[0x00007fff8000, 0x00008fff6fff]` || LowShadow || +// || `[0x000000000000, 0x00007fff7fff]` || LowMem || +// +// When SHADOW_OFFSET is zero (-pie): +// || `[0x100000000000, 0x7fffffffffff]` || HighMem || +// || `[0x020000000000, 0x0fffffffffff]` || HighShadow || +// || `[0x000000040000, 0x01ffffffffff]` || ShadowGap || +// +// Special case when something is already mapped between +// 0x003000000000 and 0x005000000000 (e.g. when prelink is installed): +// || `[0x10007fff8000, 0x7fffffffffff]` || HighMem || +// || `[0x02008fff7000, 0x10007fff7fff]` || HighShadow || +// || `[0x005000000000, 0x02008fff6fff]` || ShadowGap3 || +// || `[0x003000000000, 0x004fffffffff]` || MidMem || +// || `[0x000a7fff8000, 0x002fffffffff]` || ShadowGap2 || +// || `[0x00067fff8000, 0x000a7fff7fff]` || MidShadow || +// || `[0x00008fff7000, 0x00067fff7fff]` || ShadowGap || +// || `[0x00007fff8000, 0x00008fff6fff]` || LowShadow || +// || `[0x000000000000, 0x00007fff7fff]` || LowMem || +// +// Default Linux/i386 mapping: +// || `[0x40000000, 0xffffffff]` || HighMem || +// || `[0x28000000, 0x3fffffff]` || HighShadow || +// || `[0x24000000, 0x27ffffff]` || ShadowGap || +// || `[0x20000000, 0x23ffffff]` || LowShadow || +// || `[0x00000000, 0x1fffffff]` || LowMem || #if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1 extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale; @@ -25,7 +56,7 @@ extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset; # define SHADOW_SCALE (__asan_mapping_scale) # define SHADOW_OFFSET (__asan_mapping_offset) #else -# if ASAN_ANDROID +# if SANITIZER_ANDROID # define SHADOW_SCALE (3) # define SHADOW_OFFSET (0) # else @@ -36,27 +67,20 @@ extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset; # if defined(__powerpc64__) # define SHADOW_OFFSET (1ULL << 41) # else -# define SHADOW_OFFSET (1ULL << 44) +# if SANITIZER_MAC +# define SHADOW_OFFSET (1ULL << 44) +# else +# define SHADOW_OFFSET 0x7fff8000ULL +# endif # endif # endif # endif #endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET #define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE) -#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) | (SHADOW_OFFSET)) +#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) + (SHADOW_OFFSET)) #define SHADOW_TO_MEM(shadow) (((shadow) - SHADOW_OFFSET) << SHADOW_SCALE) -#if SANITIZER_WORDSIZE == 64 -# if defined(__powerpc64__) - static const uptr kHighMemEnd = 0x00000fffffffffffUL; -# else - static const uptr kHighMemEnd = 0x00007fffffffffffUL; -# endif -#else // SANITIZER_WORDSIZE == 32 - static const uptr kHighMemEnd = 0xffffffff; -#endif // SANITIZER_WORDSIZE - - #define kLowMemBeg 0 #define kLowMemEnd (SHADOW_OFFSET ? SHADOW_OFFSET - 1 : 0) @@ -68,59 +92,121 @@ extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset; #define kHighShadowBeg MEM_TO_SHADOW(kHighMemBeg) #define kHighShadowEnd MEM_TO_SHADOW(kHighMemEnd) +# define kMidShadowBeg MEM_TO_SHADOW(kMidMemBeg) +# define kMidShadowEnd MEM_TO_SHADOW(kMidMemEnd) + // With the zero shadow base we can not actually map pages starting from 0. // This constant is somewhat arbitrary. #define kZeroBaseShadowStart (1 << 18) #define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 \ : kZeroBaseShadowStart) -#define kShadowGapEnd (kHighShadowBeg - 1) +#define kShadowGapEnd ((kMidMemBeg ? kMidShadowBeg : kHighShadowBeg) - 1) -#define kGlobalAndStackRedzone \ - (SHADOW_GRANULARITY < 32 ? 32 : SHADOW_GRANULARITY) +#define kShadowGap2Beg (kMidMemBeg ? kMidShadowEnd + 1 : 0) +#define kShadowGap2End (kMidMemBeg ? kMidMemBeg - 1 : 0) + +#define kShadowGap3Beg (kMidMemBeg ? kMidMemEnd + 1 : 0) +#define kShadowGap3End (kMidMemBeg ? kHighShadowBeg - 1 : 0) + +#define DO_ASAN_MAPPING_PROFILE 0 // Set to 1 to profile the functions below. + +#if DO_ASAN_MAPPING_PROFILE +# define PROFILE_ASAN_MAPPING() AsanMappingProfile[__LINE__]++; +#else +# define PROFILE_ASAN_MAPPING() +#endif + +// If 1, all shadow boundaries are constants. +// Don't set to 1 other than for testing. +#define ASAN_FIXED_MAPPING 0 namespace __asan { +extern uptr AsanMappingProfile[]; + +#if ASAN_FIXED_MAPPING +// Fixed mapping for 64-bit Linux. Mostly used for performance comparison +// with non-fixed mapping. As of r175253 (Feb 2013) the performance +// difference between fixed and non-fixed mapping is below the noise level. +static uptr kHighMemEnd = 0x7fffffffffffULL; +static uptr kMidMemBeg = 0x3000000000ULL; +static uptr kMidMemEnd = 0x4fffffffffULL; +#else +SANITIZER_INTERFACE_ATTRIBUTE +extern uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; // Initialized in __asan_init. +#endif + static inline bool AddrIsInLowMem(uptr a) { + PROFILE_ASAN_MAPPING(); return a < kLowMemEnd; } static inline bool AddrIsInLowShadow(uptr a) { + PROFILE_ASAN_MAPPING(); return a >= kLowShadowBeg && a <= kLowShadowEnd; } static inline bool AddrIsInHighMem(uptr a) { + PROFILE_ASAN_MAPPING(); return a >= kHighMemBeg && a <= kHighMemEnd; } +static inline bool AddrIsInMidMem(uptr a) { + PROFILE_ASAN_MAPPING(); + return kMidMemBeg && a >= kMidMemBeg && a <= kMidMemEnd; +} + static inline bool AddrIsInMem(uptr a) { - return AddrIsInLowMem(a) || AddrIsInHighMem(a); + PROFILE_ASAN_MAPPING(); + return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a); } static inline uptr MemToShadow(uptr p) { + PROFILE_ASAN_MAPPING(); CHECK(AddrIsInMem(p)); return MEM_TO_SHADOW(p); } static inline bool AddrIsInHighShadow(uptr a) { - return a >= kHighShadowBeg && a <= kHighMemEnd; + PROFILE_ASAN_MAPPING(); + return a >= kHighShadowBeg && a <= kHighMemEnd; +} + +static inline bool AddrIsInMidShadow(uptr a) { + PROFILE_ASAN_MAPPING(); + return kMidMemBeg && a >= kMidShadowBeg && a <= kMidMemEnd; } static inline bool AddrIsInShadow(uptr a) { - return AddrIsInLowShadow(a) || AddrIsInHighShadow(a); + PROFILE_ASAN_MAPPING(); + return AddrIsInLowShadow(a) || AddrIsInMidShadow(a) || AddrIsInHighShadow(a); } static inline bool AddrIsInShadowGap(uptr a) { + PROFILE_ASAN_MAPPING(); + if (kMidMemBeg) { + if (a <= kShadowGapEnd) + return SHADOW_OFFSET == 0 || a >= kShadowGapBeg; + return (a >= kShadowGap2Beg && a <= kShadowGap2End) || + (a >= kShadowGap3Beg && a <= kShadowGap3End); + } + // In zero-based shadow mode we treat addresses near zero as addresses + // in shadow gap as well. + if (SHADOW_OFFSET == 0) + return a <= kShadowGapEnd; return a >= kShadowGapBeg && a <= kShadowGapEnd; } static inline bool AddrIsAlignedByGranularity(uptr a) { + PROFILE_ASAN_MAPPING(); return (a & (SHADOW_GRANULARITY - 1)) == 0; } static inline bool AddressIsPoisoned(uptr a) { + PROFILE_ASAN_MAPPING(); const uptr kAccessSize = 1; - u8 *shadow_address = (u8*)MemToShadow(a); + u8 *shadow_address = (u8*)MEM_TO_SHADOW(a); s8 shadow_value = *shadow_address; if (shadow_value) { u8 last_accessed_byte = (a & (SHADOW_GRANULARITY - 1)) @@ -130,6 +216,9 @@ static inline bool AddressIsPoisoned(uptr a) { return false; } +// Must be after all calls to PROFILE_ASAN_MAPPING(). +static const uptr kAsanMappingProfileSize = __LINE__; + } // namespace __asan #endif // ASAN_MAPPING_H diff --git a/lib/asan/asan_new_delete.cc b/lib/asan/asan_new_delete.cc index 5d1f23c542e6..d5eb6eca9321 100644 --- a/lib/asan/asan_new_delete.cc +++ b/lib/asan/asan_new_delete.cc @@ -28,7 +28,8 @@ void ReplaceOperatorsNewAndDelete() { } using namespace __asan; // NOLINT // On Android new() goes through malloc interceptors. -#if !ASAN_ANDROID +// See also https://code.google.com/p/address-sanitizer/issues/detail?id=131. +#if !SANITIZER_ANDROID // Fake std::nothrow_t to avoid including <new>. namespace std { @@ -39,6 +40,14 @@ struct nothrow_t {}; GET_STACK_TRACE_MALLOC;\ return asan_memalign(0, size, &stack, type); +// On OS X it's not enough to just provide our own 'operator new' and +// 'operator delete' implementations, because they're going to be in the +// runtime dylib, and the main executable will depend on both the runtime +// dylib and libstdc++, each of those'll have its implementation of new and +// delete. +// To make sure that C++ allocation/deallocation operators are overridden on +// OS X we need to intercept them using their mangled names. +#if !SANITIZER_MAC INTERCEPTOR_ATTRIBUTE void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); } INTERCEPTOR_ATTRIBUTE @@ -50,10 +59,26 @@ INTERCEPTOR_ATTRIBUTE void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY(FROM_NEW_BR); } +#else // SANITIZER_MAC +INTERCEPTOR(void *, _Znwm, size_t size) { + OPERATOR_NEW_BODY(FROM_NEW); +} +INTERCEPTOR(void *, _Znam, size_t size) { + OPERATOR_NEW_BODY(FROM_NEW_BR); +} +INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(FROM_NEW); +} +INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(FROM_NEW_BR); +} +#endif + #define OPERATOR_DELETE_BODY(type) \ GET_STACK_TRACE_FREE;\ asan_free(ptr, &stack, type); +#if !SANITIZER_MAC INTERCEPTOR_ATTRIBUTE void operator delete(void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW); } INTERCEPTOR_ATTRIBUTE @@ -65,4 +90,19 @@ INTERCEPTOR_ATTRIBUTE void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(FROM_NEW_BR); } +#else // SANITIZER_MAC +INTERCEPTOR(void, _ZdlPv, void *ptr) { + OPERATOR_DELETE_BODY(FROM_NEW); +} +INTERCEPTOR(void, _ZdaPv, void *ptr) { + OPERATOR_DELETE_BODY(FROM_NEW_BR); +} +INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY(FROM_NEW); +} +INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY(FROM_NEW_BR); +} +#endif + #endif diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc index dc5749243569..772b5e64b027 100644 --- a/lib/asan/asan_poisoning.cc +++ b/lib/asan/asan_poisoning.cc @@ -12,10 +12,7 @@ // Shadow memory poisoning by ASan RTL and by user application. //===----------------------------------------------------------------------===// -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_mapping.h" -#include "sanitizer/asan_interface.h" +#include "asan_poisoning.h" #include "sanitizer_common/sanitizer_libc.h" namespace __asan { @@ -23,11 +20,11 @@ namespace __asan { void PoisonShadow(uptr addr, uptr size, u8 value) { if (!flags()->poison_heap) return; CHECK(AddrIsAlignedByGranularity(addr)); + CHECK(AddrIsInMem(addr)); CHECK(AddrIsAlignedByGranularity(addr + size)); - uptr shadow_beg = MemToShadow(addr); - uptr shadow_end = MemToShadow(addr + size - SHADOW_GRANULARITY) + 1; - CHECK(REAL(memset) != 0); - REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); + CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY)); + CHECK(REAL(memset)); + FastPoisonShadow(addr, size, value); } void PoisonShadowPartialRightRedzone(uptr addr, @@ -36,20 +33,10 @@ void PoisonShadowPartialRightRedzone(uptr addr, u8 value) { if (!flags()->poison_heap) return; CHECK(AddrIsAlignedByGranularity(addr)); - u8 *shadow = (u8*)MemToShadow(addr); - for (uptr 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 : value; // unaddressable - } else { - *shadow = size - i; // first size-i bytes are addressable - } - } + CHECK(AddrIsInMem(addr)); + FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value); } - struct ShadowSegmentEndpoint { u8 *chunk; s8 offset; // in [0, SHADOW_GRANULARITY) @@ -182,6 +169,55 @@ uptr __asan_region_is_poisoned(uptr beg, uptr size) { return 0; } +#define CHECK_SMALL_REGION(p, size, isWrite) \ + do { \ + uptr __p = reinterpret_cast<uptr>(p); \ + uptr __size = size; \ + if (UNLIKELY(__asan::AddressIsPoisoned(__p) || \ + __asan::AddressIsPoisoned(__p + __size - 1))) { \ + GET_CURRENT_PC_BP_SP; \ + uptr __bad = __asan_region_is_poisoned(__p, __size); \ + __asan_report_error(pc, bp, sp, __bad, isWrite, __size);\ + } \ + } while (false); \ + + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u16 __sanitizer_unaligned_load16(const u16 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u32 __sanitizer_unaligned_load32(const u32 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u64 __sanitizer_unaligned_load64(const u64 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store16(u16 *p, u16 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store32(u32 *p, u32 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store64(u64 *p, u64 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + // This is a simplified version of __asan_(un)poison_memory_region, which // assumes that left border of region to be poisoned is properly aligned. static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) { diff --git a/lib/asan/asan_poisoning.h b/lib/asan/asan_poisoning.h new file mode 100644 index 000000000000..86f81e5d0ae5 --- /dev/null +++ b/lib/asan/asan_poisoning.h @@ -0,0 +1,58 @@ +//===-- asan_poisoning.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. +// +// Shadow memory poisoning by ASan RTL and by user application. +//===----------------------------------------------------------------------===// + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mapping.h" + +namespace __asan { + +// Poisons the shadow memory for "size" bytes starting from "addr". +void PoisonShadow(uptr addr, uptr size, u8 value); + +// Poisons the shadow memory for "redzone_size" bytes starting from +// "addr + size". +void PoisonShadowPartialRightRedzone(uptr addr, + uptr size, + uptr redzone_size, + u8 value); + +// Fast versions of PoisonShadow and PoisonShadowPartialRightRedzone that +// assume that memory addresses are properly aligned. Use in +// performance-critical code with care. +ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, + u8 value) { + DCHECK(flags()->poison_heap); + uptr shadow_beg = MEM_TO_SHADOW(aligned_beg); + uptr shadow_end = MEM_TO_SHADOW( + aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1; + REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); +} + +ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone( + uptr aligned_addr, uptr size, uptr redzone_size, u8 value) { + DCHECK(flags()->poison_heap); + u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr); + for (uptr 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 : value; // unaddressable + } else { + *shadow = size - i; // first size-i bytes are addressable + } + } +} + +} // namespace __asan diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc index ceaf120fc803..5126a756d1c8 100644 --- a/lib/asan/asan_posix.cc +++ b/lib/asan/asan_posix.cc @@ -11,14 +11,15 @@ // // Posix-specific details. //===----------------------------------------------------------------------===// -#if defined(__linux__) || defined(__APPLE__) + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX || SANITIZER_MAC #include "asan_internal.h" #include "asan_interceptors.h" #include "asan_mapping.h" #include "asan_report.h" #include "asan_stack.h" -#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" @@ -42,7 +43,7 @@ static void MaybeInstallSigaction(int signum, sigact.sa_sigaction = handler; sigact.sa_flags = SA_SIGINFO; if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; - CHECK(0 == REAL(sigaction)(signum, &sigact, 0)); + CHECK_EQ(0, REAL(sigaction)(signum, &sigact, 0)); if (flags()->verbosity >= 1) { Report("Installed the sigaction for signal %d\n", signum); } @@ -59,7 +60,7 @@ static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) { void SetAlternateSignalStack() { stack_t altstack, oldstack; - CHECK(0 == sigaltstack(0, &oldstack)); + CHECK_EQ(0, sigaltstack(0, &oldstack)); // If the alternate stack is already in place, do nothing. if ((oldstack.ss_flags & SS_DISABLE) == 0) return; // TODO(glider): the mapped stack should have the MAP_STACK flag in the @@ -69,10 +70,10 @@ void SetAlternateSignalStack() { altstack.ss_sp = base; altstack.ss_flags = 0; altstack.ss_size = kAltStackSize; - CHECK(0 == sigaltstack(&altstack, 0)); + CHECK_EQ(0, sigaltstack(&altstack, 0)); if (flags()->verbosity > 0) { Report("Alternative stack for T%d set: [%p,%p)\n", - asanThreadRegistry().GetCurrentTidOrInvalid(), + GetCurrentTidOrInvalid(), altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size); } } @@ -82,7 +83,7 @@ void UnsetAlternateSignalStack() { altstack.ss_sp = 0; altstack.ss_flags = SS_DISABLE; altstack.ss_size = 0; - CHECK(0 == sigaltstack(&altstack, &oldstack)); + CHECK_EQ(0, sigaltstack(&altstack, &oldstack)); UnmapOrDie(oldstack.ss_sp, oldstack.ss_size); } @@ -102,7 +103,7 @@ static bool tsd_key_inited = false; void AsanTSDInit(void (*destructor)(void *tsd)) { CHECK(!tsd_key_inited); tsd_key_inited = true; - CHECK(0 == pthread_key_create(&tsd_key, destructor)); + CHECK_EQ(0, pthread_key_create(&tsd_key, destructor)); } void *AsanTSDGet() { @@ -117,4 +118,4 @@ void AsanTSDSet(void *tsd) { } // namespace __asan -#endif // __linux__ || __APPLE_ +#endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/lib/asan/asan_preinit.cc b/lib/asan/asan_preinit.cc new file mode 100644 index 000000000000..586f551c23c3 --- /dev/null +++ b/lib/asan/asan_preinit.cc @@ -0,0 +1,31 @@ +//===-- asan_preinit.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. +// +// Call __asan_init at the very early stage of process startup. +// On Linux we use .preinit_array section (unless PIC macro is defined). +//===----------------------------------------------------------------------===// +#include "asan_internal.h" + +#if ASAN_USE_PREINIT_ARRAY && !defined(PIC) + // On Linux, we force __asan_init to be called before anyone else + // by placing it into .preinit_array section. + // FIXME: do we have anything like this on Mac? + // The symbol is called __local_asan_preinit, because it's not intended to be + // exported. + __attribute__((section(".preinit_array"), used)) + void (*__local_asan_preinit)(void) = __asan_init; +#elif SANITIZER_WINDOWS && defined(_DLL) + // On Windows, when using dynamic CRT (/MD), we can put a pointer + // to __asan_init into the global list of C initializers. + // See crt0dat.c in the CRT sources for the details. + #pragma section(".CRT$XIB", long, read) // NOLINT + __declspec(allocate(".CRT$XIB")) void (*__asan_preinit)() = __asan_init; +#endif diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc index 35ab9cabde67..aeeebf452ca8 100644 --- a/lib/asan/asan_report.cc +++ b/lib/asan/asan_report.cc @@ -17,8 +17,8 @@ #include "asan_report.h" #include "asan_stack.h" #include "asan_thread.h" -#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_symbolizer.h" @@ -120,19 +120,7 @@ static void PrintShadowBytes(const char *before, u8 *bytes, Printf("\n"); } -static void PrintShadowMemoryForAddress(uptr addr) { - if (!AddrIsInMem(addr)) - return; - uptr shadow_addr = MemToShadow(addr); - const uptr n_bytes_per_row = 16; - uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1); - Printf("Shadow bytes around the buggy address:\n"); - for (int i = -5; i <= 5; i++) { - const char *prefix = (i == 0) ? "=>" : " "; - PrintShadowBytes(prefix, - (u8*)(aligned_shadow + i * n_bytes_per_row), - (u8*)shadow_addr, n_bytes_per_row); - } +static void PrintLegend() { Printf("Shadow byte legend (one shadow byte represents %d " "application bytes):\n", (int)SHADOW_GRANULARITY); PrintShadowByte(" Addressable: ", 0); @@ -141,8 +129,8 @@ static void PrintShadowMemoryForAddress(uptr addr) { PrintShadowByte("", i, " "); Printf("\n"); PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic); - PrintShadowByte(" Heap righ redzone: ", kAsanHeapRightRedzoneMagic); - PrintShadowByte(" Freed Heap region: ", kAsanHeapFreeMagic); + PrintShadowByte(" Heap right redzone: ", kAsanHeapRightRedzoneMagic); + PrintShadowByte(" Freed heap region: ", kAsanHeapFreeMagic); PrintShadowByte(" Stack left redzone: ", kAsanStackLeftRedzoneMagic); PrintShadowByte(" Stack mid redzone: ", kAsanStackMidRedzoneMagic); PrintShadowByte(" Stack right redzone: ", kAsanStackRightRedzoneMagic); @@ -155,6 +143,23 @@ static void PrintShadowMemoryForAddress(uptr addr) { PrintShadowByte(" ASan internal: ", kAsanInternalHeapMagic); } +static void PrintShadowMemoryForAddress(uptr addr) { + if (!AddrIsInMem(addr)) + return; + uptr shadow_addr = MemToShadow(addr); + const uptr n_bytes_per_row = 16; + uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1); + Printf("Shadow bytes around the buggy address:\n"); + for (int i = -5; i <= 5; i++) { + const char *prefix = (i == 0) ? "=>" : " "; + PrintShadowBytes(prefix, + (u8*)(aligned_shadow + i * n_bytes_per_row), + (u8*)shadow_addr, n_bytes_per_row); + } + if (flags()->print_legend) + PrintLegend(); +} + static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, const char *zone_name) { if (zone_ptr) { @@ -176,30 +181,43 @@ static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; } +static const char *MaybeDemangleGlobalName(const char *name) { + // We can spoil names of globals with C linkage, so use an heuristic + // approach to check if the name should be demangled. + return (name[0] == '_' && name[1] == 'Z') ? Demangle(name) : name; +} + // Check if the global is a zero-terminated ASCII string. If so, print it. static void PrintGlobalNameIfASCII(const __asan_global &g) { for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { - if (!IsASCII(*(unsigned char*)p)) return; + unsigned char c = *(unsigned char*)p; + if (c == '\0' || !IsASCII(c)) return; } - if (*(char*)(g.beg + g.size - 1) != 0) return; - Printf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg); + if (*(char*)(g.beg + g.size - 1) != '\0') return; + Printf(" '%s' is ascii string '%s'\n", + MaybeDemangleGlobalName(g.name), (char*)g.beg); } -bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g) { - if (addr < g.beg - kGlobalAndStackRedzone) return false; +bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, + const __asan_global &g) { + static const uptr kMinimalDistanceFromAnotherGlobal = 64; + if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false; if (addr >= g.beg + g.size_with_redzone) return false; Decorator d; Printf("%s", d.Location()); - Printf("%p is located ", (void*)addr); if (addr < g.beg) { - Printf("%zd bytes to the left", g.beg - addr); - } else if (addr >= g.beg + g.size) { - Printf("%zd bytes to the right", addr - (g.beg + g.size)); + Printf("%p is located %zd bytes to the left", (void*)addr, g.beg - addr); + } else if (addr + size > g.beg + g.size) { + if (addr < g.beg + g.size) + addr = g.beg + g.size; + Printf("%p is located %zd bytes to the right", (void*)addr, + addr - (g.beg + g.size)); } else { - Printf("%zd bytes inside", addr - g.beg); // Can it happen? + // Can it happen? + Printf("%p is located %zd bytes inside", (void*)addr, addr - g.beg); } - Printf(" of global variable '%s' (0x%zx) of size %zu\n", - g.name, g.beg, g.size); + Printf(" of global variable '%s' from '%s' (0x%zx) of size %zu\n", + MaybeDemangleGlobalName(g.name), g.module_name, g.beg, g.size); Printf("%s", d.EndLocation()); PrintGlobalNameIfASCII(g); return true; @@ -226,34 +244,70 @@ bool DescribeAddressIfShadow(uptr addr) { return false; } +// Return " (thread_name) " or an empty string if the name is empty. +const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[], + uptr buff_len) { + const char *name = t->name; + if (name[0] == '\0') return ""; + buff[0] = 0; + internal_strncat(buff, " (", 3); + internal_strncat(buff, name, buff_len - 4); + internal_strncat(buff, ")", 2); + return buff; +} + +const char *ThreadNameWithParenthesis(u32 tid, char buff[], + uptr buff_len) { + if (tid == kInvalidTid) return ""; + asanThreadRegistry().CheckLocked(); + AsanThreadContext *t = GetThreadContextByTidLocked(tid); + return ThreadNameWithParenthesis(t, buff, buff_len); +} + bool DescribeAddressIfStack(uptr addr, uptr access_size) { - AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr); + AsanThread *t = FindThreadByStackAddress(addr); if (!t) return false; const sptr kBufSize = 4095; char buf[kBufSize]; uptr offset = 0; - const char *frame_descr = t->GetFrameNameByAddr(addr, &offset); + uptr frame_pc = 0; + char tname[128]; + const char *frame_descr = t->GetFrameNameByAddr(addr, &offset, &frame_pc); + +#ifdef __powerpc64__ + // On PowerPC64, the address of a function actually points to a + // three-doubleword data structure with the first field containing + // the address of the function's code. + frame_pc = *reinterpret_cast<uptr *>(frame_pc); +#endif + // This string is created by the compiler and has the following form: - // "FunctioName n alloc_1 alloc_2 ... alloc_n" + // "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 = internal_strchr(frame_descr, ' '); - CHECK(name_end); - buf[0] = 0; - internal_strncat(buf, frame_descr, - Min(kBufSize, - static_cast<sptr>(name_end - frame_descr))); Decorator d; Printf("%s", d.Location()); - Printf("Address %p is located at offset %zu " - "in frame <%s> of T%d's stack:\n", - (void*)addr, offset, Demangle(buf), t->tid()); + Printf("Address %p is located in stack of thread T%d%s " + "at offset %zu in frame\n", + addr, t->tid(), + ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)), + offset); + // Now we print the frame where the alloca has happened. + // We print this frame as a stack trace with one element. + // The symbolizer may print more than one frame if inlining was involved. + // The frame numbers may be different than those in the stack trace printed + // previously. That's unfortunate, but I have no better solution, + // especially given that the alloca may be from entirely different place + // (e.g. use-after-scope, or different thread's stack). + StackTrace alloca_stack; + alloca_stack.trace[0] = frame_pc + 16; + alloca_stack.size = 1; Printf("%s", d.EndLocation()); + PrintStack(&alloca_stack); // Report the number of stack objects. char *p; - uptr n_objects = internal_simple_strtoll(name_end, &p, 10); - CHECK(n_objects > 0); + uptr n_objects = internal_simple_strtoll(frame_descr, &p, 10); + CHECK_GT(n_objects, 0); Printf(" This frame has %zu object(s):\n", n_objects); // Report all objects in this frame. for (uptr i = 0; i < n_objects; i++) { @@ -276,87 +330,73 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) { Printf("HINT: this may be a false positive if your program uses " "some custom stack unwind mechanism or swapcontext\n" " (longjmp and C++ exceptions *are* supported)\n"); - DescribeThread(t->summary()); + DescribeThread(t->context()); return true; } static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, uptr access_size) { - uptr offset; + sptr offset; Decorator d; Printf("%s", d.Location()); - Printf("%p is located ", (void*)addr); - if (chunk.AddrIsInside(addr, access_size, &offset)) { - Printf("%zu bytes inside of", offset); - } else if (chunk.AddrIsAtLeft(addr, access_size, &offset)) { - Printf("%zu bytes to the left of", offset); + if (chunk.AddrIsAtLeft(addr, access_size, &offset)) { + Printf("%p is located %zd bytes to the left of", (void*)addr, offset); } else if (chunk.AddrIsAtRight(addr, access_size, &offset)) { - Printf("%zu bytes to the right of", offset); + if (offset < 0) { + addr -= offset; + offset = 0; + } + Printf("%p is located %zd bytes to the right of", (void*)addr, offset); + } else if (chunk.AddrIsInside(addr, access_size, &offset)) { + Printf("%p is located %zd bytes inside of", (void*)addr, offset); } else { - Printf(" somewhere around (this is AddressSanitizer bug!)"); + Printf("%p is located somewhere around (this is AddressSanitizer bug!)", + (void*)addr); } Printf(" %zu-byte region [%p,%p)\n", chunk.UsedSize(), (void*)(chunk.Beg()), (void*)(chunk.End())); Printf("%s", d.EndLocation()); } -// Return " (thread_name) " or an empty string if the name is empty. -const char *ThreadNameWithParenthesis(AsanThreadSummary *t, char buff[], - uptr buff_len) { - const char *name = t->name(); - if (*name == 0) return ""; - buff[0] = 0; - internal_strncat(buff, " (", 3); - internal_strncat(buff, name, buff_len - 4); - internal_strncat(buff, ")", 2); - return buff; -} - -const char *ThreadNameWithParenthesis(u32 tid, char buff[], - uptr buff_len) { - if (tid == kInvalidTid) return ""; - AsanThreadSummary *t = asanThreadRegistry().FindByTid(tid); - return ThreadNameWithParenthesis(t, buff, buff_len); -} - void DescribeHeapAddress(uptr addr, uptr access_size) { AsanChunkView chunk = FindHeapChunkByAddress(addr); if (!chunk.IsValid()) return; DescribeAccessToHeapChunk(chunk, addr, access_size); CHECK(chunk.AllocTid() != kInvalidTid); - AsanThreadSummary *alloc_thread = - asanThreadRegistry().FindByTid(chunk.AllocTid()); + asanThreadRegistry().CheckLocked(); + AsanThreadContext *alloc_thread = + GetThreadContextByTidLocked(chunk.AllocTid()); StackTrace alloc_stack; chunk.GetAllocStack(&alloc_stack); - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); CHECK(t); char tname[128]; Decorator d; if (chunk.FreeTid() != kInvalidTid) { - AsanThreadSummary *free_thread = - asanThreadRegistry().FindByTid(chunk.FreeTid()); + AsanThreadContext *free_thread = + GetThreadContextByTidLocked(chunk.FreeTid()); Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), - free_thread->tid(), + free_thread->tid, ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)), d.EndAllocation()); StackTrace free_stack; chunk.GetFreeStack(&free_stack); PrintStack(&free_stack); Printf("%spreviously allocated by thread T%d%s here:%s\n", - d.Allocation(), alloc_thread->tid(), + d.Allocation(), alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), d.EndAllocation()); PrintStack(&alloc_stack); - DescribeThread(t->summary()); + DescribeThread(t->context()); DescribeThread(free_thread); DescribeThread(alloc_thread); } else { Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(), - alloc_thread->tid(), + alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), d.EndAllocation()); PrintStack(&alloc_stack); - DescribeThread(t->summary()); + DescribeThread(t->context()); DescribeThread(alloc_thread); } } @@ -366,7 +406,7 @@ void DescribeAddress(uptr addr, uptr access_size) { if (DescribeAddressIfShadow(addr)) return; CHECK(AddrIsInMem(addr)); - if (DescribeAddressIfGlobal(addr)) + if (DescribeAddressIfGlobal(addr, access_size)) return; if (DescribeAddressIfStack(addr, access_size)) return; @@ -376,26 +416,27 @@ void DescribeAddress(uptr addr, uptr access_size) { // ------------------- Thread description -------------------- {{{1 -void DescribeThread(AsanThreadSummary *summary) { - CHECK(summary); +void DescribeThread(AsanThreadContext *context) { + CHECK(context); + asanThreadRegistry().CheckLocked(); // No need to announce the main thread. - if (summary->tid() == 0 || summary->announced()) { + if (context->tid == 0 || context->announced) { return; } - summary->set_announced(true); + context->announced = true; char tname[128]; - Printf("Thread T%d%s", summary->tid(), - ThreadNameWithParenthesis(summary->tid(), tname, sizeof(tname))); + Printf("Thread T%d%s", context->tid, + ThreadNameWithParenthesis(context->tid, tname, sizeof(tname))); Printf(" created by T%d%s here:\n", - summary->parent_tid(), - ThreadNameWithParenthesis(summary->parent_tid(), + context->parent_tid, + ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname))); - PrintStack(summary->stack()); + PrintStack(&context->stack); // Recursively described parent thread if needed. if (flags()->print_full_thread_history) { - AsanThreadSummary *parent_summary = - asanThreadRegistry().FindByTid(summary->parent_tid()); - DescribeThread(parent_summary); + AsanThreadContext *parent_context = + GetThreadContextByTidLocked(context->parent_tid); + DescribeThread(parent_context); } } @@ -414,25 +455,31 @@ class ScopedInErrorReport { // they are defined as no-return. Report("AddressSanitizer: while reporting a bug found another one." "Ignoring.\n"); - u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + u32 current_tid = GetCurrentTidOrInvalid(); if (current_tid != reporting_thread_tid) { // ASan found two bugs in different threads simultaneously. Sleep // long enough to make sure that the thread which started to print // an error report will finish doing it. SleepForSeconds(Max(100, flags()->sleep_before_dying + 1)); } - // If we're still not dead for some reason, use raw Exit() instead of + // If we're still not dead for some reason, use raw _exit() instead of // Die() to bypass any additional checks. - Exit(flags()->exitcode); + internal__exit(flags()->exitcode); } ASAN_ON_ERROR(); - reporting_thread_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + // Make sure the registry and sanitizer report mutexes are locked while + // we're printing an error report. + // We can lock them only here to avoid self-deadlock in case of + // recursive reports. + asanThreadRegistry().Lock(); + CommonSanitizerReportMutex.Lock(); + reporting_thread_tid = GetCurrentTidOrInvalid(); Printf("====================================================" "=============\n"); if (reporting_thread_tid != kInvalidTid) { // We started reporting an error message. Stop using the fake stack // in case we call an instrumented function from a symbolizer. - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); + AsanThread *curr_thread = GetCurrentThread(); CHECK(curr_thread); curr_thread->fake_stack().StopUsingFakeStack(); } @@ -440,12 +487,13 @@ class ScopedInErrorReport { // Destructor is NORETURN, as functions that report errors are. NORETURN ~ScopedInErrorReport() { // Make sure the current thread is announced. - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); + AsanThread *curr_thread = GetCurrentThread(); if (curr_thread) { - DescribeThread(curr_thread->summary()); + DescribeThread(curr_thread->context()); } // Print memory stats. - __asan_print_accumulated_stats(); + if (flags()->print_stats) + __asan_print_accumulated_stats(); if (error_report_callback) { error_report_callback(error_message_buffer); } @@ -454,6 +502,22 @@ class ScopedInErrorReport { } }; +static void ReportSummary(const char *error_type, StackTrace *stack) { + if (!stack->size) return; + if (IsSymbolizerAvailable()) { + AddressInfo ai; + // Currently, we include the first stack frame into the report summary. + // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). + uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); + SymbolizeCode(pc, &ai, 1); + ReportErrorSummary(error_type, + StripPathPrefix(ai.file, + common_flags()->strip_path_prefix), + ai.line, ai.function); + } + // FIXME: do we need to print anything at all if there is no symbolizer? +} + void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { ScopedInErrorReport in_report; Decorator d; @@ -461,32 +525,44 @@ void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { Report("ERROR: AddressSanitizer: SEGV on unknown address %p" " (pc %p sp %p bp %p T%d)\n", (void*)addr, (void*)pc, (void*)sp, (void*)bp, - asanThreadRegistry().GetCurrentTidOrInvalid()); + GetCurrentTidOrInvalid()); Printf("%s", d.EndWarning()); Printf("AddressSanitizer can not provide additional info.\n"); GET_STACK_TRACE_FATAL(pc, bp); PrintStack(&stack); + ReportSummary("SEGV", &stack); } void ReportDoubleFree(uptr addr, StackTrace *stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: attempting double-free on %p:\n", addr); + char tname[128]; + u32 curr_tid = GetCurrentTidOrInvalid(); + Report("ERROR: AddressSanitizer: attempting double-free on %p in " + "thread T%d%s:\n", + addr, curr_tid, + ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); + Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); + ReportSummary("double-free", stack); } void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); + char tname[128]; + u32 curr_tid = GetCurrentTidOrInvalid(); Report("ERROR: AddressSanitizer: attempting free on address " - "which was not malloc()-ed: %p\n", addr); + "which was not malloc()-ed: %p in thread T%d%s\n", addr, + curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); + ReportSummary("bad-free", stack); } void ReportAllocTypeMismatch(uptr addr, StackTrace *stack, @@ -505,6 +581,7 @@ void ReportAllocTypeMismatch(uptr addr, StackTrace *stack, Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); + ReportSummary("alloc-dealloc-mismatch", stack); Report("HINT: if you don't care about these warnings you may set " "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); } @@ -519,6 +596,7 @@ void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) { Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); + ReportSummary("bad-malloc_usable_size", stack); } void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { @@ -531,6 +609,7 @@ void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); + ReportSummary("bad-__asan_get_allocated_size", stack); } void ReportStringFunctionMemoryRangesOverlap( @@ -538,14 +617,17 @@ void ReportStringFunctionMemoryRangesOverlap( const char *offset2, uptr length2, StackTrace *stack) { ScopedInErrorReport in_report; Decorator d; + char bug_type[100]; + internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function); Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: %s-param-overlap: " + Report("ERROR: AddressSanitizer: %s: " "memory ranges [%p,%p) and [%p, %p) overlap\n", \ - function, offset1, offset1 + length1, offset2, offset2 + length2); + bug_type, offset1, offset1 + length1, offset2, offset2 + length2); Printf("%s", d.EndWarning()); PrintStack(stack); DescribeAddress((uptr)offset1, length1); DescribeAddress((uptr)offset2, length2); + ReportSummary(bug_type, stack); } // ----------------------- Mac-specific reports ----------------- {{{1 @@ -642,7 +724,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, bug_descr, (void*)addr, pc, bp, sp); Printf("%s", d.EndWarning()); - u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + u32 curr_tid = GetCurrentTidOrInvalid(); char tname[128]; Printf("%s%s of size %zu at %p thread T%d%s%s\n", d.Access(), @@ -655,7 +737,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, PrintStack(&stack); DescribeAddress(addr, access_size); - + ReportSummary(bug_descr, &stack); PrintShadowMemoryForAddress(addr); } diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h index f0617f91970e..db271fc10e97 100644 --- a/lib/asan/asan_report.h +++ b/lib/asan/asan_report.h @@ -15,21 +15,21 @@ #include "asan_allocator.h" #include "asan_internal.h" #include "asan_thread.h" -#include "sanitizer/asan_interface.h" namespace __asan { // The following functions prints address description depending // on the memory type (shadow/heap/stack/global). void DescribeHeapAddress(uptr addr, uptr access_size); -bool DescribeAddressIfGlobal(uptr addr); -bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g); +bool DescribeAddressIfGlobal(uptr addr, uptr access_size); +bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, + const __asan_global &g); bool DescribeAddressIfShadow(uptr addr); bool DescribeAddressIfStack(uptr addr, uptr access_size); // Determines memory type on its own. void DescribeAddress(uptr addr, uptr access_size); -void DescribeThread(AsanThreadSummary *summary); +void DescribeThread(AsanThreadContext *context); // Different kinds of error reports. void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr); diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index 11adbee5bdea..f989c5c0d2a5 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -15,19 +15,21 @@ #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_mapping.h" +#include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" #include "asan_thread.h" -#include "asan_thread_registry.h" -#include "sanitizer/asan_interface.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_symbolizer.h" +#include "lsan/lsan_common.h" namespace __asan { +uptr AsanMappingProfile[kAsanMappingProfileSize]; + static void AsanDie() { static atomic_uint32_t num_calls; if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { @@ -38,13 +40,19 @@ static void AsanDie() { Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying); SleepForSeconds(flags()->sleep_before_dying); } - if (flags()->unmap_shadow_on_exit) - UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg); + if (flags()->unmap_shadow_on_exit) { + if (kMidMemBeg) { + UnmapOrDie((void*)kLowShadowBeg, kMidMemBeg - kLowShadowBeg); + UnmapOrDie((void*)kMidMemEnd, kHighShadowEnd - kMidMemEnd); + } else { + UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg); + } + } if (death_callback) death_callback(); if (flags()->abort_on_error) Abort(); - Exit(flags()->exitcode); + internal__exit(flags()->exitcode); } static void AsanCheckFailed(const char *file, int line, const char *cond, @@ -57,97 +65,118 @@ static void AsanCheckFailed(const char *file, int line, const char *cond, } // -------------------------- Flags ------------------------- {{{1 -static const int kDeafultMallocContextSize = 30; - -static Flags asan_flags; +static const int kDefaultMallocContextSize = 30; -Flags *flags() { - return &asan_flags; -} +Flags asan_flags_dont_use_directly; // use via flags(). static const char *MaybeCallAsanDefaultOptions() { return (&__asan_default_options) ? __asan_default_options() : ""; } +static const char *MaybeUseAsanDefaultOptionsCompileDefiniton() { +#ifdef ASAN_DEFAULT_OPTIONS +// Stringize the macro value. +# define ASAN_STRINGIZE(x) #x +# define ASAN_STRINGIZE_OPTIONS(options) ASAN_STRINGIZE(options) + return ASAN_STRINGIZE_OPTIONS(ASAN_DEFAULT_OPTIONS); +#else + return ""; +#endif +} + static void ParseFlagsFromString(Flags *f, const char *str) { + ParseCommonFlagsFromString(str); + CHECK((uptr)common_flags()->malloc_context_size <= kStackTraceMax); + ParseFlag(str, &f->quarantine_size, "quarantine_size"); - ParseFlag(str, &f->symbolize, "symbolize"); ParseFlag(str, &f->verbosity, "verbosity"); ParseFlag(str, &f->redzone, "redzone"); - CHECK(f->redzone >= 16); + CHECK_GE(f->redzone, 16); CHECK(IsPowerOfTwo(f->redzone)); ParseFlag(str, &f->debug, "debug"); ParseFlag(str, &f->report_globals, "report_globals"); - ParseFlag(str, &f->check_initialization_order, "initialization_order"); - ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); - CHECK((uptr)f->malloc_context_size <= kStackTraceMax); + ParseFlag(str, &f->check_initialization_order, "check_initialization_order"); ParseFlag(str, &f->replace_str, "replace_str"); ParseFlag(str, &f->replace_intrin, "replace_intrin"); - ParseFlag(str, &f->replace_cfallocator, "replace_cfallocator"); ParseFlag(str, &f->mac_ignore_invalid_free, "mac_ignore_invalid_free"); ParseFlag(str, &f->use_fake_stack, "use_fake_stack"); ParseFlag(str, &f->max_malloc_fill_size, "max_malloc_fill_size"); + ParseFlag(str, &f->malloc_fill_byte, "malloc_fill_byte"); ParseFlag(str, &f->exitcode, "exitcode"); ParseFlag(str, &f->allow_user_poisoning, "allow_user_poisoning"); ParseFlag(str, &f->sleep_before_dying, "sleep_before_dying"); ParseFlag(str, &f->handle_segv, "handle_segv"); + ParseFlag(str, &f->allow_user_segv_handler, "allow_user_segv_handler"); ParseFlag(str, &f->use_sigaltstack, "use_sigaltstack"); ParseFlag(str, &f->check_malloc_usable_size, "check_malloc_usable_size"); ParseFlag(str, &f->unmap_shadow_on_exit, "unmap_shadow_on_exit"); ParseFlag(str, &f->abort_on_error, "abort_on_error"); + ParseFlag(str, &f->print_stats, "print_stats"); + ParseFlag(str, &f->print_legend, "print_legend"); ParseFlag(str, &f->atexit, "atexit"); ParseFlag(str, &f->disable_core, "disable_core"); - ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix"); ParseFlag(str, &f->allow_reexec, "allow_reexec"); ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history"); ParseFlag(str, &f->log_path, "log_path"); - ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal"); - ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc"); ParseFlag(str, &f->poison_heap, "poison_heap"); ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch"); ParseFlag(str, &f->use_stack_depot, "use_stack_depot"); + ParseFlag(str, &f->strict_memcmp, "strict_memcmp"); + ParseFlag(str, &f->strict_init_order, "strict_init_order"); + ParseFlag(str, &f->detect_leaks, "detect_leaks"); } void InitializeFlags(Flags *f, const char *env) { - internal_memset(f, 0, sizeof(*f)); + CommonFlags *cf = common_flags(); + cf->external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); + cf->symbolize = true; + cf->malloc_context_size = kDefaultMallocContextSize; + cf->fast_unwind_on_fatal = false; + cf->fast_unwind_on_malloc = true; + cf->strip_path_prefix = ""; + internal_memset(f, 0, sizeof(*f)); f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28; - f->symbolize = false; f->verbosity = 0; - f->redzone = ASAN_ALLOCATOR_VERSION == 2 ? 16 : (ASAN_LOW_MEMORY) ? 64 : 128; + f->redzone = 16; f->debug = false; f->report_globals = 1; - f->check_initialization_order = true; - f->malloc_context_size = kDeafultMallocContextSize; + f->check_initialization_order = false; f->replace_str = true; f->replace_intrin = true; - f->replace_cfallocator = true; f->mac_ignore_invalid_free = false; f->use_fake_stack = true; - f->max_malloc_fill_size = 0; + f->max_malloc_fill_size = 0x1000; // By default, fill only the first 4K. + f->malloc_fill_byte = 0xbe; f->exitcode = ASAN_DEFAULT_FAILURE_EXITCODE; f->allow_user_poisoning = true; f->sleep_before_dying = 0; f->handle_segv = ASAN_NEEDS_SEGV; + f->allow_user_segv_handler = false; f->use_sigaltstack = false; f->check_malloc_usable_size = true; f->unmap_shadow_on_exit = false; f->abort_on_error = false; + f->print_stats = false; + f->print_legend = true; f->atexit = false; f->disable_core = (SANITIZER_WORDSIZE == 64); - f->strip_path_prefix = ""; f->allow_reexec = true; f->print_full_thread_history = true; f->log_path = 0; - f->fast_unwind_on_fatal = false; - f->fast_unwind_on_malloc = true; f->poison_heap = true; // Turn off alloc/dealloc mismatch checker on Mac for now. // TODO(glider): Fix known issues and enable this back. - f->alloc_dealloc_mismatch = (ASAN_MAC == 0); - f->use_stack_depot = true; // Only affects allocator2. + f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0);; + f->use_stack_depot = true; + f->strict_memcmp = true; + f->strict_init_order = false; + f->detect_leaks = false; + + // Override from compile definition. + ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton()); // Override from user-specified string. ParseFlagsFromString(f, MaybeCallAsanDefaultOptions()); @@ -158,6 +187,20 @@ void InitializeFlags(Flags *f, const char *env) { // Override from command line. ParseFlagsFromString(f, env); + +#if !CAN_SANITIZE_LEAKS + if (f->detect_leaks) { + Report("%s: detect_leaks is not supported on this platform.\n", + SanitizerToolName); + f->detect_leaks = false; + } +#endif + + if (f->detect_leaks && !f->use_stack_depot) { + Report("%s: detect_leaks is ignored (requires use_stack_depot).\n", + SanitizerToolName); + f->detect_leaks = false; + } } // -------------------------- Globals --------------------- {{{1 @@ -165,6 +208,10 @@ int asan_inited; bool asan_init_is_running; void (*death_callback)(void); +#if !ASAN_FIXED_MAPPING +uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; +#endif + // -------------------------- Misc ---------------- {{{1 void ShowStatsAndAbort() { __asan_print_accumulated_stats(); @@ -174,8 +221,8 @@ void ShowStatsAndAbort() { // ---------------------- mmap -------------------- {{{1 // Reserve memory range [beg, end]. static void ReserveShadowMemoryRange(uptr beg, uptr end) { - CHECK((beg % GetPageSizeCached()) == 0); - CHECK(((end + 1) % GetPageSizeCached()) == 0); + CHECK_EQ((beg % GetPageSizeCached()), 0); + CHECK_EQ(((end + 1) % GetPageSizeCached()), 0); uptr size = end - beg + 1; void *res = MmapFixedNoReserve(beg, size); if (res != (void*)beg) { @@ -211,6 +258,17 @@ ASAN_REPORT_ERROR(store, true, 4) ASAN_REPORT_ERROR(store, true, 8) ASAN_REPORT_ERROR(store, true, 16) +#define ASAN_REPORT_ERROR_N(type, is_write) \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## _n(uptr addr, uptr size); \ +void __asan_report_ ## type ## _n(uptr addr, uptr size) { \ + GET_CALLER_PC_BP_SP; \ + __asan_report_error(pc, bp, sp, addr, is_write, size); \ +} + +ASAN_REPORT_ERROR_N(load, false) +ASAN_REPORT_ERROR_N(store, true) + // 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 @@ -249,7 +307,7 @@ static NOINLINE void force_interface_symbols() { case 27: __asan_set_error_exit_code(0); break; case 28: __asan_stack_free(0, 0, 0); break; case 29: __asan_stack_malloc(0, 0); break; - case 30: __asan_before_dynamic_init(0, 0); break; + case 30: __asan_before_dynamic_init(0); break; case 31: __asan_after_dynamic_init(); break; case 32: __asan_poison_stack_memory(0, 0); break; case 33: __asan_unpoison_stack_memory(0, 0); break; @@ -261,6 +319,83 @@ static NOINLINE void force_interface_symbols() { static void asan_atexit() { Printf("AddressSanitizer exit stats:\n"); __asan_print_accumulated_stats(); + // Print AsanMappingProfile. + for (uptr i = 0; i < kAsanMappingProfileSize; i++) { + if (AsanMappingProfile[i] == 0) continue; + Printf("asan_mapping.h:%zd -- %zd\n", i, AsanMappingProfile[i]); + } +} + +static void InitializeHighMemEnd() { +#if !ASAN_FIXED_MAPPING +#if SANITIZER_WORDSIZE == 64 +# if defined(__powerpc64__) + // FIXME: + // On PowerPC64 we have two different address space layouts: 44- and 46-bit. + // We somehow need to figure our which one we are using now and choose + // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. + // Note that with 'ulimit -s unlimited' the stack is moved away from the top + // of the address space, so simply checking the stack address is not enough. + kHighMemEnd = (1ULL << 44) - 1; // 0x00000fffffffffffUL +# else + kHighMemEnd = (1ULL << 47) - 1; // 0x00007fffffffffffUL; +# endif +#else // SANITIZER_WORDSIZE == 32 + kHighMemEnd = (1ULL << 32) - 1; // 0xffffffff; +#endif // SANITIZER_WORDSIZE +#endif // !ASAN_FIXED_MAPPING +} + +static void ProtectGap(uptr a, uptr size) { + CHECK_EQ(a, (uptr)Mprotect(a, size)); +} + +static void PrintAddressSpaceLayout() { + Printf("|| `[%p, %p]` || HighMem ||\n", + (void*)kHighMemBeg, (void*)kHighMemEnd); + Printf("|| `[%p, %p]` || HighShadow ||\n", + (void*)kHighShadowBeg, (void*)kHighShadowEnd); + if (kMidMemBeg) { + Printf("|| `[%p, %p]` || ShadowGap3 ||\n", + (void*)kShadowGap3Beg, (void*)kShadowGap3End); + Printf("|| `[%p, %p]` || MidMem ||\n", + (void*)kMidMemBeg, (void*)kMidMemEnd); + Printf("|| `[%p, %p]` || ShadowGap2 ||\n", + (void*)kShadowGap2Beg, (void*)kShadowGap2End); + Printf("|| `[%p, %p]` || MidShadow ||\n", + (void*)kMidShadowBeg, (void*)kMidShadowEnd); + } + Printf("|| `[%p, %p]` || ShadowGap ||\n", + (void*)kShadowGapBeg, (void*)kShadowGapEnd); + if (kLowShadowBeg) { + Printf("|| `[%p, %p]` || LowShadow ||\n", + (void*)kLowShadowBeg, (void*)kLowShadowEnd); + Printf("|| `[%p, %p]` || LowMem ||\n", + (void*)kLowMemBeg, (void*)kLowMemEnd); + } + Printf("MemToShadow(shadow): %p %p %p %p", + (void*)MEM_TO_SHADOW(kLowShadowBeg), + (void*)MEM_TO_SHADOW(kLowShadowEnd), + (void*)MEM_TO_SHADOW(kHighShadowBeg), + (void*)MEM_TO_SHADOW(kHighShadowEnd)); + if (kMidMemBeg) { + Printf(" %p %p", + (void*)MEM_TO_SHADOW(kMidShadowBeg), + (void*)MEM_TO_SHADOW(kMidShadowEnd)); + } + Printf("\n"); + Printf("red_zone=%zu\n", (uptr)flags()->redzone); + Printf("malloc_context_size=%zu\n", + (uptr)common_flags()->malloc_context_size); + + Printf("SHADOW_SCALE: %zx\n", (uptr)SHADOW_SCALE); + Printf("SHADOW_GRANULARITY: %zx\n", (uptr)SHADOW_GRANULARITY); + Printf("SHADOW_OFFSET: %zx\n", (uptr)SHADOW_OFFSET); + CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7); + if (kMidMemBeg) + CHECK(kMidShadowBeg > kLowShadowEnd && + kMidMemBeg > kMidShadowEnd && + kHighShadowBeg > kMidMemEnd); } } // namespace __asan @@ -283,11 +418,25 @@ int NOINLINE __asan_set_error_exit_code(int exit_code) { void NOINLINE __asan_handle_no_return() { int local_stack; - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); + AsanThread *curr_thread = GetCurrentThread(); CHECK(curr_thread); uptr PageSize = GetPageSizeCached(); uptr top = curr_thread->stack_top(); uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1); + static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M + if (top - bottom > kMaxExpectedCleanupSize) { + static bool reported_warning = false; + if (reported_warning) + return; + reported_warning = true; + Report("WARNING: ASan is ignoring requested __asan_handle_no_return: " + "stack top: %p; bottom %p; size: %p (%zd)\n" + "False positive error reports may follow\n" + "For details see " + "http://code.google.com/p/address-sanitizer/issues/detail?id=189\n", + top, bottom, top - bottom, top - bottom); + return; + } PoisonShadow(bottom, top - bottom, 0); } @@ -297,8 +446,10 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) { void __asan_init() { if (asan_inited) return; + SanitizerToolName = "AddressSanitizer"; CHECK(!asan_init_is_running && "ASan init calls itself!"); asan_init_is_running = true; + InitializeHighMemEnd(); // Make sure we are not statically linked. AsanDoesNotSupportStaticLinkage(); @@ -334,49 +485,48 @@ void __asan_init() { ReplaceSystemMalloc(); ReplaceOperatorsNewAndDelete(); - if (flags()->verbosity) { - Printf("|| `[%p, %p]` || HighMem ||\n", - (void*)kHighMemBeg, (void*)kHighMemEnd); - Printf("|| `[%p, %p]` || HighShadow ||\n", - (void*)kHighShadowBeg, (void*)kHighShadowEnd); - Printf("|| `[%p, %p]` || ShadowGap ||\n", - (void*)kShadowGapBeg, (void*)kShadowGapEnd); - Printf("|| `[%p, %p]` || LowShadow ||\n", - (void*)kLowShadowBeg, (void*)kLowShadowEnd); - Printf("|| `[%p, %p]` || LowMem ||\n", - (void*)kLowMemBeg, (void*)kLowMemEnd); - Printf("MemToShadow(shadow): %p %p %p %p\n", - (void*)MEM_TO_SHADOW(kLowShadowBeg), - (void*)MEM_TO_SHADOW(kLowShadowEnd), - (void*)MEM_TO_SHADOW(kHighShadowBeg), - (void*)MEM_TO_SHADOW(kHighShadowEnd)); - Printf("red_zone=%zu\n", (uptr)flags()->redzone); - Printf("malloc_context_size=%zu\n", (uptr)flags()->malloc_context_size); - - Printf("SHADOW_SCALE: %zx\n", (uptr)SHADOW_SCALE); - Printf("SHADOW_GRANULARITY: %zx\n", (uptr)SHADOW_GRANULARITY); - Printf("SHADOW_OFFSET: %zx\n", (uptr)SHADOW_OFFSET); - CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7); + uptr shadow_start = kLowShadowBeg; + if (kLowShadowBeg) shadow_start -= GetMmapGranularity(); + uptr shadow_end = kHighShadowEnd; + bool full_shadow_is_available = + MemoryRangeIsAvailable(shadow_start, shadow_end); + +#if SANITIZER_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING + if (!full_shadow_is_available) { + kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0; + kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0; } +#endif + + if (flags()->verbosity) + PrintAddressSpaceLayout(); if (flags()->disable_core) { DisableCoreDumper(); } - uptr shadow_start = kLowShadowBeg; - if (kLowShadowBeg > 0) shadow_start -= GetMmapGranularity(); - uptr shadow_end = kHighShadowEnd; - if (MemoryRangeIsAvailable(shadow_start, shadow_end)) { - if (kLowShadowBeg != kLowShadowEnd) { - // mmap the low shadow plus at least one page. - ReserveShadowMemoryRange(kLowShadowBeg - GetMmapGranularity(), - kLowShadowEnd); - } + if (full_shadow_is_available) { + // mmap the low shadow plus at least one page at the left. + if (kLowShadowBeg) + ReserveShadowMemoryRange(shadow_start, kLowShadowEnd); + // mmap the high shadow. + ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); + // protect the gap. + ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); + } else if (kMidMemBeg && + MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) && + MemoryRangeIsAvailable(kMidMemEnd + 1, shadow_end)) { + CHECK(kLowShadowBeg != kLowShadowEnd); + // mmap the low shadow plus at least one page at the left. + ReserveShadowMemoryRange(shadow_start, kLowShadowEnd); + // mmap the mid shadow. + ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd); // mmap the high shadow. ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); - // protect the gap - void *prot = Mprotect(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); - CHECK(prot == (void*)kShadowGapBeg); + // protect the gaps. + ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); + ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1); + ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1); } else { Report("Shadow memory range interleaves with an existing memory mapping. " "ASan cannot proceed correctly. ABORTING.\n"); @@ -386,11 +536,10 @@ void __asan_init() { InstallSignalHandlers(); // Start symbolizer process if necessary. - if (flags()->symbolize) { - const char *external_symbolizer = GetEnv("ASAN_SYMBOLIZER_PATH"); - if (external_symbolizer) { - InitializeExternalSymbolizer(external_symbolizer); - } + const char* external_symbolizer = common_flags()->external_symbolizer_path; + if (common_flags()->symbolize && external_symbolizer && + external_symbolizer[0]) { + InitializeExternalSymbolizer(external_symbolizer); } // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited @@ -398,25 +547,27 @@ void __asan_init() { asan_inited = 1; asan_init_is_running = false; - asanThreadRegistry().Init(); - asanThreadRegistry().GetMain()->ThreadStart(); + // Create main thread. + AsanTSDInit(AsanThread::TSDDtor); + AsanThread *main_thread = AsanThread::Create(0, 0); + CreateThreadContextArgs create_main_args = { main_thread, 0 }; + u32 main_tid = asanThreadRegistry().CreateThread( + 0, true, 0, &create_main_args); + CHECK_EQ(0, main_tid); + SetCurrentThread(main_thread); + main_thread->ThreadStart(internal_getpid()); force_interface_symbols(); // no-op. + InitializeAllocator(); + +#if CAN_SANITIZE_LEAKS + __lsan::InitCommonLsan(); + if (flags()->detect_leaks) { + Atexit(__lsan::DoLeakCheck); + } +#endif // CAN_SANITIZE_LEAKS + if (flags()->verbosity) { Report("AddressSanitizer Init done\n"); } } - -#if defined(ASAN_USE_PREINIT_ARRAY) - // On Linux, we force __asan_init to be called before anyone else - // by placing it into .preinit_array section. - // FIXME: do we have anything like this on Mac? - __attribute__((section(".preinit_array"))) - typeof(__asan_init) *__asan_preinit =__asan_init; -#elif defined(_WIN32) && defined(_DLL) - // On Windows, when using dynamic CRT (/MD), we can put a pointer - // to __asan_init into the global list of C initializers. - // See crt0dat.c in the CRT sources for the details. - #pragma section(".CRT$XIB", long, read) // NOLINT - __declspec(allocate(".CRT$XIB")) void (*__asan_preinit)() = __asan_init; -#endif diff --git a/lib/asan/asan_stack.cc b/lib/asan/asan_stack.cc index ebf22fd34ca1..21dae7df096a 100644 --- a/lib/asan/asan_stack.cc +++ b/lib/asan/asan_stack.cc @@ -11,9 +11,10 @@ // // Code for ASan stack trace. //===----------------------------------------------------------------------===// +#include "asan_internal.h" #include "asan_flags.h" #include "asan_stack.h" -#include "sanitizer/asan_interface.h" +#include "sanitizer_common/sanitizer_flags.h" namespace __asan { @@ -24,8 +25,8 @@ static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer, } void PrintStack(StackTrace *stack) { - stack->PrintStack(stack->trace, stack->size, flags()->symbolize, - flags()->strip_path_prefix, MaybeCallAsanSymbolize); + stack->PrintStack(stack->trace, stack->size, common_flags()->symbolize, + common_flags()->strip_path_prefix, MaybeCallAsanSymbolize); } } // namespace __asan @@ -35,7 +36,7 @@ void PrintStack(StackTrace *stack) { // Provide default implementation of __asan_symbolize that does nothing // and may be overriden by user if he wants to use his own symbolization. // ASan on Windows has its own implementation of this. -#if !defined(_WIN32) && !SANITIZER_SUPPORTS_WEAK_HOOKS +#if !SANITIZER_WINDOWS && !SANITIZER_SUPPORTS_WEAK_HOOKS SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) { return false; diff --git a/lib/asan/asan_stack.h b/lib/asan/asan_stack.h index 46c9f3408725..176aa183c93b 100644 --- a/lib/asan/asan_stack.h +++ b/lib/asan/asan_stack.h @@ -14,12 +14,13 @@ #ifndef ASAN_STACK_H #define ASAN_STACK_H -#include "sanitizer_common/sanitizer_stacktrace.h" #include "asan_flags.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_stacktrace.h" namespace __asan { -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast); void PrintStack(StackTrace *stack); } // namespace __asan @@ -27,10 +28,24 @@ void PrintStack(StackTrace *stack); // 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. +#if SANITIZER_WINDOWS #define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ StackTrace stack; \ - GetStackTrace(&stack, max_s, pc, bp, fast) + GetStackTrace(&stack, max_s, pc, bp, 0, 0, fast) +#else +#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ + StackTrace stack; \ + { \ + uptr stack_top = 0, stack_bottom = 0; \ + AsanThread *t; \ + if (asan_inited && (t = GetCurrentThread())) { \ + stack_top = t->stack_top(); \ + stack_bottom = t->stack_bottom(); \ + } \ + GetStackTrace(&stack, max_s, pc, bp, \ + stack_top, stack_bottom, fast); \ + } +#endif // SANITIZER_WINDOWS // NOTE: A Rule of thumb is to retrieve stack trace in the interceptors // as early as possible (in functions exposed to the user), as we generally @@ -42,24 +57,24 @@ void PrintStack(StackTrace *stack); #define GET_STACK_TRACE_FATAL(pc, bp) \ GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp, \ - flags()->fast_unwind_on_fatal) + common_flags()->fast_unwind_on_fatal) -#define GET_STACK_TRACE_FATAL_HERE \ - GET_STACK_TRACE(kStackTraceMax, flags()->fast_unwind_on_fatal) +#define GET_STACK_TRACE_FATAL_HERE \ + GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal) -#define GET_STACK_TRACE_THREAD \ +#define GET_STACK_TRACE_THREAD \ GET_STACK_TRACE(kStackTraceMax, true) -#define GET_STACK_TRACE_MALLOC \ - GET_STACK_TRACE(flags()->malloc_context_size, \ - flags()->fast_unwind_on_malloc) +#define GET_STACK_TRACE_MALLOC \ + GET_STACK_TRACE(common_flags()->malloc_context_size, \ + common_flags()->fast_unwind_on_malloc) #define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC #define PRINT_CURRENT_STACK() \ { \ GET_STACK_TRACE(kStackTraceMax, \ - flags()->fast_unwind_on_fatal); \ + common_flags()->fast_unwind_on_fatal); \ PrintStack(&stack); \ } diff --git a/lib/asan/asan_stats.cc b/lib/asan/asan_stats.cc index c57c8cc61aed..ba7c1ab6e91a 100644 --- a/lib/asan/asan_stats.cc +++ b/lib/asan/asan_stats.cc @@ -14,14 +14,14 @@ #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_stats.h" -#include "asan_thread_registry.h" -#include "sanitizer/asan_interface.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_stackdepot.h" namespace __asan { AsanStats::AsanStats() { - CHECK(REAL(memset) != 0); + CHECK(REAL(memset)); REAL(memset)(this, 0, sizeof(AsanStats)); } @@ -58,7 +58,7 @@ static BlockingMutex print_lock(LINKER_INITIALIZED); static void PrintAccumulatedStats() { AsanStats stats; - asanThreadRegistry().GetAccumulatedStats(&stats); + GetAccumulatedStats(&stats); // Use lock to keep reports from mixing up. BlockingMutexLock lock(&print_lock); stats.Print(); @@ -68,21 +68,103 @@ static void PrintAccumulatedStats() { PrintInternalAllocatorStats(); } +static AsanStats unknown_thread_stats(LINKER_INITIALIZED); +static AsanStats accumulated_stats(LINKER_INITIALIZED); +// Required for malloc_zone_statistics() on OS X. This can't be stored in +// per-thread AsanStats. +static uptr max_malloced_memory; +static BlockingMutex acc_stats_lock(LINKER_INITIALIZED); + +static void FlushToAccumulatedStatsUnlocked(AsanStats *stats) { + acc_stats_lock.CheckLocked(); + uptr *dst = (uptr*)&accumulated_stats; + uptr *src = (uptr*)stats; + uptr num_fields = sizeof(*stats) / sizeof(uptr); + for (uptr i = 0; i < num_fields; i++) { + dst[i] += src[i]; + src[i] = 0; + } +} + +static void FlushThreadStats(ThreadContextBase *tctx_base, void *arg) { + AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base); + if (AsanThread *t = tctx->thread) + FlushToAccumulatedStatsUnlocked(&t->stats()); +} + +static void UpdateAccumulatedStatsUnlocked() { + acc_stats_lock.CheckLocked(); + { + ThreadRegistryLock l(&asanThreadRegistry()); + asanThreadRegistry().RunCallbackForEachThreadLocked(FlushThreadStats, 0); + } + FlushToAccumulatedStatsUnlocked(&unknown_thread_stats); + // This is not very accurate: we may miss allocation peaks that happen + // between two updates of accumulated_stats_. For more accurate bookkeeping + // the maximum should be updated on every malloc(), which is unacceptable. + if (max_malloced_memory < accumulated_stats.malloced) { + max_malloced_memory = accumulated_stats.malloced; + } +} + +void FlushToAccumulatedStats(AsanStats *stats) { + BlockingMutexLock lock(&acc_stats_lock); + FlushToAccumulatedStatsUnlocked(stats); +} + +void GetAccumulatedStats(AsanStats *stats) { + BlockingMutexLock lock(&acc_stats_lock); + UpdateAccumulatedStatsUnlocked(); + internal_memcpy(stats, &accumulated_stats, sizeof(accumulated_stats)); +} + +void FillMallocStatistics(AsanMallocStats *malloc_stats) { + BlockingMutexLock lock(&acc_stats_lock); + UpdateAccumulatedStatsUnlocked(); + malloc_stats->blocks_in_use = accumulated_stats.mallocs; + malloc_stats->size_in_use = accumulated_stats.malloced; + malloc_stats->max_size_in_use = max_malloced_memory; + malloc_stats->size_allocated = accumulated_stats.mmaped; +} + +AsanStats &GetCurrentThreadStats() { + AsanThread *t = GetCurrentThread(); + return (t) ? t->stats() : unknown_thread_stats; +} + } // namespace __asan // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT uptr __asan_get_current_allocated_bytes() { - return asanThreadRegistry().GetCurrentAllocatedBytes(); + BlockingMutexLock lock(&acc_stats_lock); + UpdateAccumulatedStatsUnlocked(); + uptr malloced = accumulated_stats.malloced; + uptr freed = accumulated_stats.freed; + // Return sane value if malloced < freed due to racy + // way we update accumulated stats. + return (malloced > freed) ? malloced - freed : 1; } uptr __asan_get_heap_size() { - return asanThreadRegistry().GetHeapSize(); + BlockingMutexLock lock(&acc_stats_lock); + UpdateAccumulatedStatsUnlocked(); + return accumulated_stats.mmaped - accumulated_stats.munmaped; } uptr __asan_get_free_bytes() { - return asanThreadRegistry().GetFreeBytes(); + BlockingMutexLock lock(&acc_stats_lock); + UpdateAccumulatedStatsUnlocked(); + uptr total_free = accumulated_stats.mmaped + - accumulated_stats.munmaped + + accumulated_stats.really_freed + + accumulated_stats.really_freed_redzones; + uptr total_used = accumulated_stats.malloced + + accumulated_stats.malloced_redzones; + // Return sane value if total_free < total_used due to racy + // way we update accumulated stats. + return (total_free > total_used) ? total_free - total_used : 1; } uptr __asan_get_unmapped_bytes() { diff --git a/lib/asan/asan_stats.h b/lib/asan/asan_stats.h index 37846bc92ad2..68495fb33f95 100644 --- a/lib/asan/asan_stats.h +++ b/lib/asan/asan_stats.h @@ -56,6 +56,15 @@ struct AsanStats { void Print(); }; +// Returns stats for GetCurrentThread(), or stats for fake "unknown thread" +// if GetCurrentThread() returns 0. +AsanStats &GetCurrentThreadStats(); +// Flushes all thread-local stats to accumulated stats, and makes +// a copy of accumulated stats. +void GetAccumulatedStats(AsanStats *stats); +// Flushes a given stats into accumulated stats. +void FlushToAccumulatedStats(AsanStats *stats); + // A cross-platform equivalent of malloc_statistics_t on Mac OS. struct AsanMallocStats { uptr blocks_in_use; @@ -64,6 +73,8 @@ struct AsanMallocStats { uptr size_allocated; }; +void FillMallocStatistics(AsanMallocStats *malloc_stats); + } // namespace __asan #endif // ASAN_STATS_H diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc index 778e91932ed5..da28381031a5 100644 --- a/lib/asan/asan_thread.cc +++ b/lib/asan/asan_thread.cc @@ -13,46 +13,81 @@ //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_interceptors.h" +#include "asan_poisoning.h" #include "asan_stack.h" #include "asan_thread.h" -#include "asan_thread_registry.h" #include "asan_mapping.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "lsan/lsan_common.h" namespace __asan { -AsanThread::AsanThread(LinkerInitialized x) - : fake_stack_(x), - malloc_storage_(x), - stats_(x) { } +// AsanThreadContext implementation. -AsanThread *AsanThread::Create(u32 parent_tid, thread_callback_t start_routine, - void *arg, StackTrace *stack) { +void AsanThreadContext::OnCreated(void *arg) { + CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg); + if (args->stack) { + internal_memcpy(&stack, args->stack, sizeof(stack)); + } + thread = args->thread; + thread->set_context(this); +} + +void AsanThreadContext::OnFinished() { + // Drop the link to the AsanThread object. + thread = 0; +} + +static char thread_registry_placeholder[sizeof(ThreadRegistry)]; +static ThreadRegistry *asan_thread_registry; + +static ThreadContextBase *GetAsanThreadContext(u32 tid) { + void *mem = MmapOrDie(sizeof(AsanThreadContext), "AsanThreadContext"); + return new(mem) AsanThreadContext(tid); +} + +ThreadRegistry &asanThreadRegistry() { + static bool initialized; + // Don't worry about thread_safety - this should be called when there is + // a single thread. + if (!initialized) { + // Never reuse ASan threads: we store pointer to AsanThreadContext + // in TSD and can't reliably tell when no more TSD destructors will + // be called. It would be wrong to reuse AsanThreadContext for another + // thread before all TSD destructors will be called for it. + asan_thread_registry = new(thread_registry_placeholder) ThreadRegistry( + GetAsanThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads); + initialized = true; + } + return *asan_thread_registry; +} + +AsanThreadContext *GetThreadContextByTidLocked(u32 tid) { + return static_cast<AsanThreadContext *>( + asanThreadRegistry().GetThreadLocked(tid)); +} + +// AsanThread implementation. + +AsanThread *AsanThread::Create(thread_callback_t start_routine, + void *arg) { uptr PageSize = GetPageSizeCached(); uptr size = RoundUpTo(sizeof(AsanThread), PageSize); AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__); thread->start_routine_ = start_routine; thread->arg_ = arg; - - const uptr kSummaryAllocSize = PageSize; - CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize); - AsanThreadSummary *summary = - (AsanThreadSummary*)MmapOrDie(PageSize, "AsanThreadSummary"); - summary->Init(parent_tid, stack); - summary->set_thread(thread); - thread->set_summary(summary); + thread->context_ = 0; return thread; } -void AsanThreadSummary::TSDDtor(void *tsd) { - AsanThreadSummary *summary = (AsanThreadSummary*)tsd; - if (flags()->verbosity >= 1) { - Report("T%d TSDDtor\n", summary->tid()); - } - if (summary->thread()) { - summary->thread()->Destroy(); - } +void AsanThread::TSDDtor(void *tsd) { + AsanThreadContext *context = (AsanThreadContext*)tsd; + if (flags()->verbosity >= 1) + Report("T%d TSDDtor\n", context->tid); + if (context->thread) + context->thread->Destroy(); } void AsanThread::Destroy() { @@ -60,8 +95,8 @@ void AsanThread::Destroy() { Report("T%d exited\n", tid()); } - asanThreadRegistry().UnregisterThread(this); - CHECK(summary()->thread() == 0); + asanThreadRegistry().FinishThread(tid()); + FlushToAccumulatedStats(&stats_); // 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. @@ -86,15 +121,16 @@ void AsanThread::Init() { AsanPlatformThreadInit(); } -thread_return_t AsanThread::ThreadStart() { +thread_return_t AsanThread::ThreadStart(uptr os_id) { Init(); + asanThreadRegistry().StartThread(tid(), os_id, 0); if (flags()->use_sigaltstack) SetAlternateSignalStack(); if (!start_routine_) { // start_routine_ == 0 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); + CHECK_EQ(tid(), 0); return 0; } @@ -117,7 +153,8 @@ void AsanThread::ClearShadowForThreadStack() { PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); } -const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { +const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset, + uptr *frame_pc) { uptr bottom = 0; if (AddrIsInStack(addr)) { bottom = stack_bottom(); @@ -125,6 +162,7 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { bottom = fake_stack().AddrIsInFakeStack(addr); CHECK(bottom); *offset = addr - bottom; + *frame_pc = ((uptr*)bottom)[2]; return (const char *)((uptr*)bottom)[1]; } uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr. @@ -149,7 +187,79 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1)); CHECK(ptr[0] == kCurrentStackFrameMagic); *offset = addr - (uptr)ptr; + *frame_pc = ptr[2]; return (const char*)ptr[1]; } +static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base, + void *addr) { + AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base); + AsanThread *t = tctx->thread; + return (t && t->fake_stack().StackSize() && + (t->fake_stack().AddrIsInFakeStack((uptr)addr) || + t->AddrIsInStack((uptr)addr))); +} + +AsanThread *GetCurrentThread() { + AsanThreadContext *context = (AsanThreadContext*)AsanTSDGet(); + if (!context) { + if (SANITIZER_ANDROID) { + // On Android, libc constructor is called _after_ asan_init, and cleans up + // TSD. Try to figure out if this is still the main thread by the stack + // address. We are not entirely sure that we have correct main thread + // limits, so only do this magic on Android, and only if the found thread + // is the main thread. + AsanThreadContext *tctx = GetThreadContextByTidLocked(0); + if (ThreadStackContainsAddress(tctx, &context)) { + SetCurrentThread(tctx->thread); + return tctx->thread; + } + } + return 0; + } + return context->thread; +} + +void SetCurrentThread(AsanThread *t) { + CHECK(t->context()); + if (flags()->verbosity >= 2) { + Report("SetCurrentThread: %p for thread %p\n", + t->context(), (void*)GetThreadSelf()); + } + // Make sure we do not reset the current AsanThread. + CHECK_EQ(0, AsanTSDGet()); + AsanTSDSet(t->context()); + CHECK_EQ(t->context(), AsanTSDGet()); +} + +u32 GetCurrentTidOrInvalid() { + AsanThread *t = GetCurrentThread(); + return t ? t->tid() : kInvalidTid; +} + +AsanThread *FindThreadByStackAddress(uptr addr) { + asanThreadRegistry().CheckLocked(); + AsanThreadContext *tctx = static_cast<AsanThreadContext *>( + asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress, + (void *)addr)); + return tctx ? tctx->thread : 0; +} } // namespace __asan + +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, + uptr *tls_begin, uptr *tls_end, + uptr *cache_begin, uptr *cache_end) { + // FIXME: Stub. + return false; +} + +void LockThreadRegistry() { + __asan::asanThreadRegistry().Lock(); +} + +void UnlockThreadRegistry() { + __asan::asanThreadRegistry().Unlock(); +} +} // namespace __lsan diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h index acc27e52e224..14062b62f751 100644 --- a/lib/asan/asan_thread.h +++ b/lib/asan/asan_thread.h @@ -16,76 +16,58 @@ #include "asan_allocator.h" #include "asan_internal.h" +#include "asan_fake_stack.h" #include "asan_stack.h" #include "asan_stats.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_thread_registry.h" namespace __asan { const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits. +const u32 kMaxNumberOfThreads = (1 << 22); // 4M 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 { +class AsanThreadContext : public ThreadContextBase { public: - explicit AsanThreadSummary(LinkerInitialized) { } // for T0. - void Init(u32 parent_tid, StackTrace *stack) { - parent_tid_ = parent_tid; - announced_ = false; - tid_ = kInvalidTid; - if (stack) { - internal_memcpy(&stack_, stack, sizeof(*stack)); - } - thread_ = 0; - name_[0] = 0; + explicit AsanThreadContext(int tid) + : ThreadContextBase(tid), + announced(false), + thread(0) { + internal_memset(&stack, 0, sizeof(stack)); } - u32 tid() { return tid_; } - void set_tid(u32 tid) { tid_ = tid; } - u32 parent_tid() { return parent_tid_; } - bool announced() { return announced_; } - void set_announced(bool announced) { announced_ = announced; } - StackTrace *stack() { return &stack_; } - AsanThread *thread() { return thread_; } - void set_thread(AsanThread *thread) { thread_ = thread; } - static void TSDDtor(void *tsd); - void set_name(const char *name) { - internal_strncpy(name_, name, sizeof(name_) - 1); - } - const char *name() { return name_; } + bool announced; + StackTrace stack; + AsanThread *thread; - private: - u32 tid_; - u32 parent_tid_; - bool announced_; - StackTrace stack_; - AsanThread *thread_; - char name_[128]; + void OnCreated(void *arg); + void OnFinished(); }; -// AsanThreadSummary objects are never freed, so we need many of them. -COMPILER_CHECK(sizeof(AsanThreadSummary) <= 4094); +// AsanThreadContext objects are never freed, so we need many of them. +COMPILER_CHECK(sizeof(AsanThreadContext) <= 4096); // AsanThread are stored in TSD and destroyed when the thread dies. class AsanThread { public: - explicit AsanThread(LinkerInitialized); // for T0. - static AsanThread *Create(u32 parent_tid, thread_callback_t start_routine, - void *arg, StackTrace *stack); + static AsanThread *Create(thread_callback_t start_routine, void *arg); + static void TSDDtor(void *tsd); void Destroy(); void Init(); // Should be called from the thread itself. - thread_return_t ThreadStart(); + thread_return_t ThreadStart(uptr os_id); uptr stack_top() { return stack_top_; } uptr stack_bottom() { return stack_bottom_; } uptr stack_size() { return stack_top_ - stack_bottom_; } - u32 tid() { return summary_->tid(); } - AsanThreadSummary *summary() { return summary_; } - void set_summary(AsanThreadSummary *summary) { summary_ = summary; } + u32 tid() { return context_->tid; } + AsanThreadContext *context() { return context_; } + void set_context(AsanThreadContext *context) { context_ = context; } - const char *GetFrameNameByAddr(uptr addr, uptr *offset); + const char *GetFrameNameByAddr(uptr addr, uptr *offset, uptr *frame_pc); bool AddrIsInStack(uptr addr) { return addr >= stack_bottom_ && addr < stack_top_; @@ -96,9 +78,10 @@ class AsanThread { AsanStats &stats() { return stats_; } private: + AsanThread() {} void SetThreadStackTopAndBottom(); void ClearShadowForThreadStack(); - AsanThreadSummary *summary_; + AsanThreadContext *context_; thread_callback_t start_routine_; void *arg_; uptr stack_top_; @@ -109,6 +92,23 @@ class AsanThread { AsanStats stats_; }; +struct CreateThreadContextArgs { + AsanThread *thread; + StackTrace *stack; +}; + +// Returns a single instance of registry. +ThreadRegistry &asanThreadRegistry(); + +// Must be called under ThreadRegistryLock. +AsanThreadContext *GetThreadContextByTidLocked(u32 tid); + +// Get the current thread. May return 0. +AsanThread *GetCurrentThread(); +void SetCurrentThread(AsanThread *t); +u32 GetCurrentTidOrInvalid(); +AsanThread *FindThreadByStackAddress(uptr addr); + } // namespace __asan #endif // ASAN_THREAD_H diff --git a/lib/asan/asan_thread_registry.cc b/lib/asan/asan_thread_registry.cc deleted file mode 100644 index 80675405fbd5..000000000000 --- a/lib/asan/asan_thread_registry.cc +++ /dev/null @@ -1,198 +0,0 @@ -//===-- asan_thread_registry.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. -// -// 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 "sanitizer_common/sanitizer_common.h" - -namespace __asan { - -static AsanThreadRegistry asan_thread_registry(LINKER_INITIALIZED); - -AsanThreadRegistry &asanThreadRegistry() { - return asan_thread_registry; -} - -AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x) - : main_thread_(x), - main_thread_summary_(x), - accumulated_stats_(x), - max_malloced_memory_(x), - mu_(x) { } - -void AsanThreadRegistry::Init() { - AsanTSDInit(AsanThreadSummary::TSDDtor); - main_thread_.set_summary(&main_thread_summary_); - main_thread_summary_.set_thread(&main_thread_); - RegisterThread(&main_thread_); - SetCurrent(&main_thread_); - // At this point only one thread exists. - inited_ = true; -} - -void AsanThreadRegistry::RegisterThread(AsanThread *thread) { - BlockingMutexLock lock(&mu_); - u32 tid = n_threads_; - n_threads_++; - CHECK(n_threads_ < kMaxNumberOfThreads); - - AsanThreadSummary *summary = thread->summary(); - CHECK(summary != 0); - summary->set_tid(tid); - thread_summaries_[tid] = summary; -} - -void AsanThreadRegistry::UnregisterThread(AsanThread *thread) { - BlockingMutexLock lock(&mu_); - FlushToAccumulatedStatsUnlocked(&thread->stats()); - AsanThreadSummary *summary = thread->summary(); - CHECK(summary); - summary->set_thread(0); -} - -AsanThread *AsanThreadRegistry::GetMain() { - return &main_thread_; -} - -AsanThread *AsanThreadRegistry::GetCurrent() { - AsanThreadSummary *summary = (AsanThreadSummary *)AsanTSDGet(); - if (!summary) { -#if ASAN_ANDROID - // On Android, libc constructor is called _after_ asan_init, and cleans up - // TSD. Try to figure out if this is still the main thread by the stack - // address. We are not entirely sure that we have correct main thread - // limits, so only do this magic on Android, and only if the found thread is - // the main thread. - AsanThread* thread = FindThreadByStackAddress((uptr)&summary); - if (thread && thread->tid() == 0) { - SetCurrent(thread); - return thread; - } -#endif - return 0; - } - return summary->thread(); -} - -void AsanThreadRegistry::SetCurrent(AsanThread *t) { - CHECK(t->summary()); - if (flags()->verbosity >= 2) { - Report("SetCurrent: %p for thread %p\n", - t->summary(), (void*)GetThreadSelf()); - } - // Make sure we do not reset the current AsanThread. - CHECK(AsanTSDGet() == 0); - AsanTSDSet(t->summary()); - CHECK(AsanTSDGet() == t->summary()); -} - -AsanStats &AsanThreadRegistry::GetCurrentThreadStats() { - AsanThread *t = GetCurrent(); - return (t) ? t->stats() : main_thread_.stats(); -} - -void AsanThreadRegistry::GetAccumulatedStats(AsanStats *stats) { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - internal_memcpy(stats, &accumulated_stats_, sizeof(accumulated_stats_)); -} - -uptr AsanThreadRegistry::GetCurrentAllocatedBytes() { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - uptr malloced = accumulated_stats_.malloced; - uptr freed = accumulated_stats_.freed; - // Return sane value if malloced < freed due to racy - // way we update accumulated stats. - return (malloced > freed) ? malloced - freed : 1; -} - -uptr AsanThreadRegistry::GetHeapSize() { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - return accumulated_stats_.mmaped - accumulated_stats_.munmaped; -} - -uptr AsanThreadRegistry::GetFreeBytes() { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - uptr total_free = accumulated_stats_.mmaped - - accumulated_stats_.munmaped - + accumulated_stats_.really_freed - + accumulated_stats_.really_freed_redzones; - uptr total_used = accumulated_stats_.malloced - + accumulated_stats_.malloced_redzones; - // Return sane value if total_free < total_used due to racy - // way we update accumulated stats. - return (total_free > total_used) ? total_free - total_used : 1; -} - -// Return several stats counters with a single call to -// UpdateAccumulatedStatsUnlocked(). -void AsanThreadRegistry::FillMallocStatistics(AsanMallocStats *malloc_stats) { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - malloc_stats->blocks_in_use = accumulated_stats_.mallocs; - malloc_stats->size_in_use = accumulated_stats_.malloced; - malloc_stats->max_size_in_use = max_malloced_memory_; - malloc_stats->size_allocated = accumulated_stats_.mmaped; -} - -AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) { - CHECK(tid < n_threads_); - CHECK(thread_summaries_[tid]); - return thread_summaries_[tid]; -} - -AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) { - BlockingMutexLock lock(&mu_); - for (u32 tid = 0; tid < n_threads_; tid++) { - AsanThread *t = thread_summaries_[tid]->thread(); - if (!t || !(t->fake_stack().StackSize())) continue; - if (t->fake_stack().AddrIsInFakeStack(addr) || t->AddrIsInStack(addr)) { - return t; - } - } - return 0; -} - -void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() { - for (u32 tid = 0; tid < n_threads_; tid++) { - AsanThread *t = thread_summaries_[tid]->thread(); - if (t != 0) { - FlushToAccumulatedStatsUnlocked(&t->stats()); - } - } - // This is not very accurate: we may miss allocation peaks that happen - // between two updates of accumulated_stats_. For more accurate bookkeeping - // the maximum should be updated on every malloc(), which is unacceptable. - if (max_malloced_memory_ < accumulated_stats_.malloced) { - max_malloced_memory_ = accumulated_stats_.malloced; - } -} - -void AsanThreadRegistry::FlushToAccumulatedStatsUnlocked(AsanStats *stats) { - // AsanStats consists of variables of type uptr only. - uptr *dst = (uptr*)&accumulated_stats_; - uptr *src = (uptr*)stats; - uptr num_fields = sizeof(AsanStats) / sizeof(uptr); - for (uptr i = 0; i < num_fields; i++) { - dst[i] += src[i]; - src[i] = 0; - } -} - -} // namespace __asan diff --git a/lib/asan/asan_thread_registry.h b/lib/asan/asan_thread_registry.h deleted file mode 100644 index adb1a6d4f32d..000000000000 --- a/lib/asan/asan_thread_registry.h +++ /dev/null @@ -1,85 +0,0 @@ -//===-- 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_stack.h" -#include "asan_stats.h" -#include "asan_thread.h" -#include "sanitizer_common/sanitizer_mutex.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); - void UnregisterThread(AsanThread *thread); - - AsanThread *GetMain(); - // Get the current thread. May return 0. - AsanThread *GetCurrent(); - void SetCurrent(AsanThread *t); - - u32 GetCurrentTidOrInvalid() { - if (!inited_) return 0; - AsanThread *t = GetCurrent(); - return t ? t->tid() : kInvalidTid; - } - - // Returns stats for GetCurrent(), or stats for - // T0 if GetCurrent() returns 0. - AsanStats &GetCurrentThreadStats(); - // Flushes all thread-local stats to accumulated stats, and makes - // a copy of accumulated stats. - void GetAccumulatedStats(AsanStats *stats); - uptr GetCurrentAllocatedBytes(); - uptr GetHeapSize(); - uptr GetFreeBytes(); - void FillMallocStatistics(AsanMallocStats *malloc_stats); - - AsanThreadSummary *FindByTid(u32 tid); - AsanThread *FindThreadByStackAddress(uptr 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 u32 kMaxNumberOfThreads = (1 << 22); // 4M - AsanThreadSummary *thread_summaries_[kMaxNumberOfThreads]; - AsanThread main_thread_; - AsanThreadSummary main_thread_summary_; - AsanStats accumulated_stats_; - // Required for malloc_zone_statistics() on OS X. This can't be stored in - // per-thread AsanStats. - uptr max_malloced_memory_; - u32 n_threads_; - BlockingMutex mu_; - bool inited_; -}; - -// Returns a single instance of registry. -AsanThreadRegistry &asanThreadRegistry(); - -} // namespace __asan - -#endif // ASAN_THREAD_REGISTRY_H diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc index d8ce050641bc..f74de7227ed2 100644 --- a/lib/asan/asan_win.cc +++ b/lib/asan/asan_win.cc @@ -11,7 +11,9 @@ // // Windows-specific details. //===----------------------------------------------------------------------===// -#ifdef _WIN32 + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_WINDOWS #include <windows.h> #include <dbghelp.h> @@ -30,30 +32,6 @@ static BlockingMutex dbghelp_lock(LINKER_INITIALIZED); static bool dbghelp_initialized = false; #pragma comment(lib, "dbghelp.lib") -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) { - (void)fast; - stack->max_size = max_s; - void *tmp[kStackTraceMax]; - - // FIXME: CaptureStackBackTrace might be too slow for us. - // FIXME: Compare with StackWalk64. - // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc - uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0); - uptr offset = 0; - // Skip the RTL frames by searching for the PC in the stacktrace. - // FIXME: this doesn't work well for the malloc/free stacks yet. - for (uptr i = 0; i < cs_ret; i++) { - if (pc != (uptr)tmp[i]) - continue; - offset = i; - break; - } - - stack->size = cs_ret - offset; - for (uptr i = 0; i < stack->size; i++) - stack->trace[i] = (uptr)tmp[i + offset]; -} - // ---------------------- TSD ---------------- {{{1 static bool tsd_key_inited = false; diff --git a/lib/asan/dynamic/asan_interceptors_dynamic.cc b/lib/asan/dynamic/asan_interceptors_dynamic.cc deleted file mode 100644 index 4f0f7bd2d5f8..000000000000 --- a/lib/asan/dynamic/asan_interceptors_dynamic.cc +++ /dev/null @@ -1,111 +0,0 @@ -//===-- asan_interceptors_dynamic.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. -// -// __DATA,__interpose section of the dynamic runtime library for Mac OS. -//===----------------------------------------------------------------------===// - -#if defined(__APPLE__) - -#include "../asan_interceptors.h" -#include "../asan_intercepted_functions.h" - -namespace __asan { - -#if !MAC_INTERPOSE_FUNCTIONS -# error \ - Dynamic interposing library should be built with -DMAC_INTERPOSE_FUNCTIONS -#endif - -#define INTERPOSE_FUNCTION(function) \ - { reinterpret_cast<const uptr>(WRAP(function)), \ - reinterpret_cast<const uptr>(function) } - -#define INTERPOSE_FUNCTION_2(function, wrapper) \ - { reinterpret_cast<const uptr>(wrapper), \ - reinterpret_cast<const uptr>(function) } - -struct interpose_substitution { - const uptr replacement; - const uptr original; -}; - -__attribute__((used)) -const interpose_substitution substitutions[] - __attribute__((section("__DATA, __interpose"))) = { - INTERPOSE_FUNCTION(strlen), - INTERPOSE_FUNCTION(memcmp), - INTERPOSE_FUNCTION(memcpy), - INTERPOSE_FUNCTION(memmove), - INTERPOSE_FUNCTION(memset), - INTERPOSE_FUNCTION(strchr), - INTERPOSE_FUNCTION(strcat), - INTERPOSE_FUNCTION(strncat), - INTERPOSE_FUNCTION(strcpy), - INTERPOSE_FUNCTION(strncpy), - INTERPOSE_FUNCTION(pthread_create), - INTERPOSE_FUNCTION(longjmp), -#if ASAN_INTERCEPT__LONGJMP - INTERPOSE_FUNCTION(_longjmp), -#endif -#if ASAN_INTERCEPT_SIGLONGJMP - INTERPOSE_FUNCTION(siglongjmp), -#endif -#if ASAN_INTERCEPT_STRDUP - INTERPOSE_FUNCTION(strdup), -#endif -#if ASAN_INTERCEPT_STRNLEN - INTERPOSE_FUNCTION(strnlen), -#endif -#if ASAN_INTERCEPT_INDEX - INTERPOSE_FUNCTION_2(index, WRAP(strchr)), -#endif - INTERPOSE_FUNCTION(strcmp), - INTERPOSE_FUNCTION(strncmp), -#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP - INTERPOSE_FUNCTION(strcasecmp), - INTERPOSE_FUNCTION(strncasecmp), -#endif - INTERPOSE_FUNCTION(atoi), - INTERPOSE_FUNCTION(atol), - INTERPOSE_FUNCTION(strtol), -#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL - INTERPOSE_FUNCTION(atoll), - INTERPOSE_FUNCTION(strtoll), -#endif -#if ASAN_INTERCEPT_MLOCKX - INTERPOSE_FUNCTION(mlock), - INTERPOSE_FUNCTION(munlock), - INTERPOSE_FUNCTION(mlockall), - INTERPOSE_FUNCTION(munlockall), -#endif - INTERPOSE_FUNCTION(dispatch_async_f), - INTERPOSE_FUNCTION(dispatch_sync_f), - INTERPOSE_FUNCTION(dispatch_after_f), - INTERPOSE_FUNCTION(dispatch_barrier_async_f), - INTERPOSE_FUNCTION(dispatch_group_async_f), -#ifndef MISSING_BLOCKS_SUPPORT - INTERPOSE_FUNCTION(dispatch_group_async), - INTERPOSE_FUNCTION(dispatch_async), - INTERPOSE_FUNCTION(dispatch_after), - INTERPOSE_FUNCTION(dispatch_source_set_event_handler), - INTERPOSE_FUNCTION(dispatch_source_set_cancel_handler), -#endif - INTERPOSE_FUNCTION(signal), - INTERPOSE_FUNCTION(sigaction), - - INTERPOSE_FUNCTION(__CFInitialize), - INTERPOSE_FUNCTION(CFStringCreateCopy), - INTERPOSE_FUNCTION(free), -}; - -} // namespace __asan - -#endif // __APPLE__ diff --git a/lib/asan/lit_tests/CMakeLists.txt b/lib/asan/lit_tests/CMakeLists.txt index 1609032d4670..d2420b50da83 100644 --- a/lib/asan/lit_tests/CMakeLists.txt +++ b/lib/asan/lit_tests/CMakeLists.txt @@ -14,9 +14,9 @@ configure_lit_site_cfg( if(COMPILER_RT_CAN_EXECUTE_TESTS) # Run ASan tests only if we're sure we may produce working binaries. set(ASAN_TEST_DEPS - clang clang-headers FileCheck count not llvm-nm llvm-symbolizer + ${SANITIZER_COMMON_LIT_TEST_DEPS} ${ASAN_RUNTIME_LIBRARIES} - ) + asan_blacklist) set(ASAN_TEST_PARAMS asan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg ) diff --git a/lib/asan/lit_tests/Darwin/interface_symbols_darwin.c b/lib/asan/lit_tests/Darwin/interface_symbols_darwin.c new file mode 100644 index 000000000000..3fca6e915324 --- /dev/null +++ b/lib/asan/lit_tests/Darwin/interface_symbols_darwin.c @@ -0,0 +1,39 @@ +// Check the presense of interface symbols in the ASan runtime dylib. +// If you're changing this file, please also change +// ../Linux/interface_symbols.c + +// RUN: %clang -fsanitize=address -dead_strip -O2 %s -o %t.exe +// RUN: rm -f %t.symbols %t.interface + +// RUN: nm -g `otool -L %t.exe | grep "asan_osx_dynamic.dylib" | \ +// RUN: tr -d '\011' | \ +// RUN: sed "s/.dylib.*/.dylib/"` \ +// RUN: | grep " T " | sed "s/.* T //" \ +// RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \ +// RUN: | grep -v "__asan_malloc_hook" \ +// RUN: | grep -v "__asan_free_hook" \ +// RUN: | grep -v "__asan_symbolize" \ +// RUN: | grep -v "__asan_default_options" \ +// RUN: | grep -v "__asan_on_error" > %t.symbols + +// RUN: cat %p/../../asan_interface_internal.h \ +// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \ +// RUN: | grep -v "OPTIONAL" \ +// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \ +// RUN: > %t.interface +// RUN: echo __asan_report_load1 >> %t.interface +// RUN: echo __asan_report_load2 >> %t.interface +// RUN: echo __asan_report_load4 >> %t.interface +// RUN: echo __asan_report_load8 >> %t.interface +// RUN: echo __asan_report_load16 >> %t.interface +// RUN: echo __asan_report_store1 >> %t.interface +// RUN: echo __asan_report_store2 >> %t.interface +// RUN: echo __asan_report_store4 >> %t.interface +// RUN: echo __asan_report_store8 >> %t.interface +// RUN: echo __asan_report_store16 >> %t.interface +// RUN: echo __asan_report_load_n >> %t.interface +// RUN: echo __asan_report_store_n >> %t.interface + +// RUN: cat %t.interface | sort -u | diff %t.symbols - + +int main() { return 0; } diff --git a/lib/asan/lit_tests/Darwin/lit.local.cfg b/lib/asan/lit_tests/Darwin/lit.local.cfg new file mode 100644 index 000000000000..a85dfcd24c08 --- /dev/null +++ b/lib/asan/lit_tests/Darwin/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os not in ['Darwin']: + config.unsupported = True diff --git a/lib/asan/lit_tests/Darwin/reexec-insert-libraries-env.cc b/lib/asan/lit_tests/Darwin/reexec-insert-libraries-env.cc new file mode 100644 index 000000000000..40a459fd84db --- /dev/null +++ b/lib/asan/lit_tests/Darwin/reexec-insert-libraries-env.cc @@ -0,0 +1,20 @@ +// Make sure ASan doesn't hang in an exec loop if DYLD_INSERT_LIBRARIES is set. +// This is a regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=159 + +// RUN: %clangxx_asan -m64 %s -o %t +// RUN: %clangxx -m64 %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \ +// RUN: -dynamiclib -o darwin-dummy-shared-lib-so.dylib + +// FIXME: the following command line may hang in the case of a regression. +// RUN: DYLD_INSERT_LIBRARIES=darwin-dummy-shared-lib-so.dylib \ +// RUN: %t 2>&1 | FileCheck %s || exit 1 +#include <stdio.h> +#include <stdlib.h> + +int main() { + const char kEnvName[] = "DYLD_INSERT_LIBRARIES"; + printf("%s=%s\n", kEnvName, getenv(kEnvName)); + // CHECK: {{DYLD_INSERT_LIBRARIES=.*darwin-dummy-shared-lib-so.dylib.*}} + return 0; +} diff --git a/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc b/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc new file mode 100644 index 000000000000..cf89949cf942 --- /dev/null +++ b/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc @@ -0,0 +1,20 @@ +// Make sure ASan removes the runtime library from DYLD_INSERT_LIBRARIES before +// executing other programs. + +// RUN: %clangxx_asan -m64 %s -o %t +// RUN: %clangxx -m64 %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \ +// RUN: -dynamiclib -o darwin-dummy-shared-lib-so.dylib + +// Make sure DYLD_INSERT_LIBRARIES doesn't contain the runtime library before +// execl(). + +// RUN: %t >/dev/null 2>&1 +// RUN: DYLD_INSERT_LIBRARIES=darwin-dummy-shared-lib-so.dylib \ +// RUN: %t 2>&1 | FileCheck %s || exit 1 +#include <unistd.h> +int main() { + execl("/bin/bash", "/bin/bash", "-c", + "echo DYLD_INSERT_LIBRARIES=$DYLD_INSERT_LIBRARIES", NULL); + // CHECK: {{DYLD_INSERT_LIBRARIES=.*darwin-dummy-shared-lib-so.dylib.*}} + return 0; +} diff --git a/lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc b/lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc new file mode 100644 index 000000000000..e4189d19d099 --- /dev/null +++ b/lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc @@ -0,0 +1,16 @@ +#include <stdio.h> + +class C { + public: + C() { value = 42; } + ~C() { } + int value; +}; + +C c; + +void AccessC() { + printf("C value: %d\n", c.value); +} + +int main() { return 0; } diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist-extra2.cc b/lib/asan/lit_tests/Helpers/initialization-blacklist-extra2.cc new file mode 100644 index 000000000000..69455a0a6fc9 --- /dev/null +++ b/lib/asan/lit_tests/Helpers/initialization-blacklist-extra2.cc @@ -0,0 +1,4 @@ +int zero_init(); +int badSrcGlobal = zero_init(); +int readBadSrcGlobal() { return badSrcGlobal; } + diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist.txt b/lib/asan/lit_tests/Helpers/initialization-blacklist.txt index c5f6610937f0..fa4a83667f4b 100644 --- a/lib/asan/lit_tests/Helpers/initialization-blacklist.txt +++ b/lib/asan/lit_tests/Helpers/initialization-blacklist.txt @@ -1,2 +1,3 @@ global-init:*badGlobal* global-init-type:*badNamespace::BadClass* +global-init-src:*initialization-blacklist-extra2.cc diff --git a/lib/asan/lit_tests/Helpers/initialization-constexpr-extra.cc b/lib/asan/lit_tests/Helpers/initialization-constexpr-extra.cc new file mode 100644 index 000000000000..b32466a981b3 --- /dev/null +++ b/lib/asan/lit_tests/Helpers/initialization-constexpr-extra.cc @@ -0,0 +1,3 @@ +// Constexpr: +int getCoolestInteger(); +static int coolest_integer = getCoolestInteger(); diff --git a/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc b/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc index 490b3339054a..886165affd76 100644 --- a/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc +++ b/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc @@ -4,6 +4,6 @@ static int ab = getAB(); // Function local statics: int countCalls(); static int one = countCalls(); -// Constexpr: -int getCoolestInteger(); -static int coolest_integer = getCoolestInteger(); +// Trivial constructor, non-trivial destructor: +int getStructWithDtorValue(); +static int val = getStructWithDtorValue(); diff --git a/lib/asan/lit_tests/Linux/asan_prelink_test.cc b/lib/asan/lit_tests/Linux/asan_prelink_test.cc new file mode 100644 index 000000000000..c209c39c8c42 --- /dev/null +++ b/lib/asan/lit_tests/Linux/asan_prelink_test.cc @@ -0,0 +1,28 @@ +// Test if asan works with prelink. +// It does not actually use prelink, but relies on ld's flag -Ttext-segment +// or gold's flag -Ttext (we try the first flag first, if that fails we +// try the second flag). +// +// RUN: %clangxx_asan -m64 -c %s -o %t.o +// RUN: %clangxx_asan -m64 -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext-segment=0x3600000000 ||\ +// RUN: %clangxx_asan -m64 -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext=0x3600000000 +// RUN: %clangxx_asan -m64 %t.o %t.so -Wl,-R. -o %t +// RUN: ASAN_OPTIONS=verbosity=1 %t 2>&1 | FileCheck %s + +// REQUIRES: x86_64-supported-target +#if BUILD_SO +int G; +int *getG() { + return &G; +} +#else +#include <stdio.h> +extern int *getG(); +int main(int argc, char **argv) { + long p = (long)getG(); + printf("SO mapped at %lx\n", p & ~0xffffffffUL); + *getG() = 0; +} +#endif +// CHECK: 0x003000000000, 0x004fffffffff{{.*}} MidMem +// CHECK: SO mapped at 3600000000 diff --git a/lib/asan/lit_tests/Linux/glob.cc b/lib/asan/lit_tests/Linux/glob.cc new file mode 100644 index 000000000000..e05228ff39e3 --- /dev/null +++ b/lib/asan/lit_tests/Linux/glob.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <string> + + +int main(int argc, char *argv[]) { + std::string path = argv[1]; + std::string pattern = path + "/glob_test_root/*a"; + printf("pattern: %s\n", pattern.c_str()); + + glob_t globbuf; + int res = glob(pattern.c_str(), 0, 0, &globbuf); + + printf("%d %s\n", errno, strerror(errno)); + assert(res == 0); + assert(globbuf.gl_pathc == 2); + printf("%zu\n", strlen(globbuf.gl_pathv[0])); + printf("%zu\n", strlen(globbuf.gl_pathv[1])); + printf("PASS\n"); + // CHECK: PASS + return 0; +} diff --git a/lib/asan/lit_tests/Linux/glob_test_root/aa b/lib/asan/lit_tests/Linux/glob_test_root/aa new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/lib/asan/lit_tests/Linux/glob_test_root/aa diff --git a/lib/asan/lit_tests/Linux/glob_test_root/ab b/lib/asan/lit_tests/Linux/glob_test_root/ab new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/lib/asan/lit_tests/Linux/glob_test_root/ab diff --git a/lib/asan/lit_tests/Linux/glob_test_root/ba b/lib/asan/lit_tests/Linux/glob_test_root/ba new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/lib/asan/lit_tests/Linux/glob_test_root/ba diff --git a/lib/asan/lit_tests/Linux/heavy_uar_test.cc b/lib/asan/lit_tests/Linux/heavy_uar_test.cc new file mode 100644 index 000000000000..c0f4560fb4e7 --- /dev/null +++ b/lib/asan/lit_tests/Linux/heavy_uar_test.cc @@ -0,0 +1,55 @@ +// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O0 %s -o %t && \ +// RUN: %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O2 %s -o %t && \ +// RUN: %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O2 %s -o %t && \ +// RUN: %t 2>&1 | %symbolize | FileCheck %s + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +__attribute__((noinline)) +char *pretend_to_do_something(char *x) { + __asm__ __volatile__("" : : "r" (x) : "memory"); + return x; +} + +__attribute__((noinline)) +char *LeakStack() { + char x[1024]; + memset(x, 0, sizeof(x)); + return pretend_to_do_something(x); +} + +template<size_t kFrameSize> +__attribute__((noinline)) +void RecuriveFunctionWithStackFrame(int depth) { + if (depth <= 0) return; + char x[kFrameSize]; + x[0] = depth; + pretend_to_do_something(x); + RecuriveFunctionWithStackFrame<kFrameSize>(depth - 1); +} + +int main(int argc, char **argv) { + int n_iter = argc >= 2 ? atoi(argv[1]) : 1000; + int depth = argc >= 3 ? atoi(argv[2]) : 500; + for (int i = 0; i < n_iter; i++) { + RecuriveFunctionWithStackFrame<10>(depth); + RecuriveFunctionWithStackFrame<100>(depth); + RecuriveFunctionWithStackFrame<500>(depth); + RecuriveFunctionWithStackFrame<1024>(depth); + RecuriveFunctionWithStackFrame<2000>(depth); + RecuriveFunctionWithStackFrame<5000>(depth); + RecuriveFunctionWithStackFrame<10000>(depth); + } + char *stale_stack = LeakStack(); + RecuriveFunctionWithStackFrame<1024>(10); + stale_stack[100]++; + // CHECK: ERROR: AddressSanitizer: stack-use-after-return on address + // CHECK: is located in stack of thread T0 at offset 132 in frame + // CHECK: in LeakStack(){{.*}}heavy_uar_test.cc: + // CHECK: [32, 1056) 'x' + return 0; +} diff --git a/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc b/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc index 645fe1c85ed4..4f41dda18128 100644 --- a/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc +++ b/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc @@ -1,12 +1,13 @@ // Test to make sure basic initialization order errors are caught. // Check that on Linux initialization order bugs are caught -// independently on order in which we list source files. +// independently on order in which we list source files (if we specify +// strict init-order checking). -// RUN: %clangxx_asan -m64 -O0 %s %p/../Helpers/initialization-bug-extra.cc\ -// RUN: -fsanitize=init-order -o %t && %t 2>&1 \ +// RUN: %clangxx_asan -m64 -O0 %s %p/../Helpers/initialization-bug-extra.cc -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 \ // RUN: | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O0 %p/../Helpers/initialization-bug-extra.cc %s\ -// RUN: -fsanitize=init-order -o %t && %t 2>&1 \ +// RUN: %clangxx_asan -m64 -O0 %p/../Helpers/initialization-bug-extra.cc %s -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 \ // RUN: | %symbolize | FileCheck %s // Do not test with optimization -- the error may be optimized away. diff --git a/lib/asan/lit_tests/interface_symbols.c b/lib/asan/lit_tests/Linux/interface_symbols_linux.c index f3167f562922..4134c8744043 100644 --- a/lib/asan/lit_tests/interface_symbols.c +++ b/lib/asan/lit_tests/Linux/interface_symbols_linux.c @@ -1,14 +1,14 @@ // Check the presense of interface symbols in compiled file. -// RUN: %clang -fsanitize=address -dead_strip -O2 %s -o %t.exe -// RUN: nm %t.exe | grep " T " | sed "s/.* T //" \ +// RUN: %clang -fsanitize=address -O2 %s -o %t.exe +// RUN: nm -D %t.exe | grep " T " | sed "s/.* T //" \ // RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \ // RUN: | grep -v "__asan_malloc_hook" \ // RUN: | grep -v "__asan_free_hook" \ // RUN: | grep -v "__asan_symbolize" \ // RUN: | grep -v "__asan_default_options" \ // RUN: | grep -v "__asan_on_error" > %t.symbols -// RUN: cat %p/../../../include/sanitizer/asan_interface.h \ +// RUN: cat %p/../../asan_interface_internal.h \ // RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \ // RUN: | grep -v "OPTIONAL" \ // RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \ @@ -23,6 +23,12 @@ // RUN: echo __asan_report_store4 >> %t.interface // RUN: echo __asan_report_store8 >> %t.interface // RUN: echo __asan_report_store16 >> %t.interface +// RUN: echo __asan_report_load_n >> %t.interface +// RUN: echo __asan_report_store_n >> %t.interface // RUN: cat %t.interface | sort -u | diff %t.symbols - +// FIXME: nm -D on powerpc somewhy shows ASan interface symbols residing +// in "initialized data section". +// REQUIRES: x86_64-supported-target,i386-supported-target + int main() { return 0; } diff --git a/lib/asan/lit_tests/Linux/malloc-in-qsort.cc b/lib/asan/lit_tests/Linux/malloc-in-qsort.cc index a3fa255b186d..ee2e81f0d2ab 100644 --- a/lib/asan/lit_tests/Linux/malloc-in-qsort.cc +++ b/lib/asan/lit_tests/Linux/malloc-in-qsort.cc @@ -1,10 +1,14 @@ -// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %clangxx_asan -m64 -O2 %s -o %t // RUN: ASAN_OPTIONS=fast_unwind_on_malloc=1 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-FAST // RUN: ASAN_OPTIONS=fast_unwind_on_malloc=0 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-SLOW // Test how well we unwind in presence of qsort in the stack // (i.e. if we can unwind through a function compiled w/o frame pointers). // https://code.google.com/p/address-sanitizer/issues/detail?id=137 + +// Fast unwinder is only avaliable on x86_64 and i386. +// REQUIRES: x86_64-supported-target + #include <stdlib.h> #include <stdio.h> diff --git a/lib/asan/lit_tests/malloc_delete_mismatch.cc b/lib/asan/lit_tests/Linux/malloc_delete_mismatch.cc index f34b33a38fb3..f34b33a38fb3 100644 --- a/lib/asan/lit_tests/malloc_delete_mismatch.cc +++ b/lib/asan/lit_tests/Linux/malloc_delete_mismatch.cc diff --git a/lib/asan/lit_tests/Linux/overflow-in-qsort.cc b/lib/asan/lit_tests/Linux/overflow-in-qsort.cc index c298991a8348..8bc43ca0a5c3 100644 --- a/lib/asan/lit_tests/Linux/overflow-in-qsort.cc +++ b/lib/asan/lit_tests/Linux/overflow-in-qsort.cc @@ -1,10 +1,14 @@ -// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %clangxx_asan -m64 -O2 %s -o %t // RUN: ASAN_OPTIONS=fast_unwind_on_fatal=1 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-FAST // RUN: ASAN_OPTIONS=fast_unwind_on_fatal=0 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-SLOW // Test how well we unwind in presence of qsort in the stack // (i.e. if we can unwind through a function compiled w/o frame pointers). // https://code.google.com/p/address-sanitizer/issues/detail?id=137 + +// Fast unwinder is only avaliable on x86_64 and i386. +// REQUIRES: x86_64-supported-target + #include <stdlib.h> #include <stdio.h> diff --git a/lib/asan/lit_tests/Linux/preinit_test.cc b/lib/asan/lit_tests/Linux/preinit_test.cc new file mode 100644 index 000000000000..28e509472c0c --- /dev/null +++ b/lib/asan/lit_tests/Linux/preinit_test.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx -DFUNC=zzzz %s -shared -o %t.so -fPIC +// RUN: %clangxx_asan -DFUNC=main %s -o %t -Wl,-R. %t.so +// RUN: %t + +// This test ensures that we call __asan_init early enough. +// We build a shared library w/o asan instrumentation +// and the binary with asan instrumentation. +// Both files include the same header (emulated by -DFUNC here) +// with C++ template magic which runs global initializer at library load time. +// The function get() is instrumented with asan, but called +// before the usual constructors are run. +// So, we must make sure that __asan_init is executed even earlier. +// +// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56393 + +struct A { + int foo() const { return 0; } +}; +A get () { return A(); } +template <class> struct O { + static A const e; +}; +template <class T> A const O <T>::e = get(); +int FUNC() { + return O<int>::e.foo(); +} + diff --git a/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc b/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc index 5026e24e424d..86794756c76f 100644 --- a/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc +++ b/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc @@ -11,6 +11,6 @@ int main(int argc, char **argv) { struct rlimit mmap_resource_limit = { 0, 0 }; assert(0 == setrlimit(RLIMIT_AS, &mmap_resource_limit)); x = malloc(10000000); -// CHECK: AddressSanitizer is unable to mmap +// CHECK: ERROR: Failed to mmap return 0; } diff --git a/lib/asan/lit_tests/Linux/swapcontext_test.cc b/lib/asan/lit_tests/Linux/swapcontext_test.cc index 0404b4f602bd..47a8d9891f51 100644 --- a/lib/asan/lit_tests/Linux/swapcontext_test.cc +++ b/lib/asan/lit_tests/Linux/swapcontext_test.cc @@ -8,6 +8,9 @@ // RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s // RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s // RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s +// +// This test is too sublte to try on non-x86 arch for now. +// REQUIRES: x86_64-supported-target,i386-supported-target #include <stdio.h> #include <ucontext.h> @@ -16,9 +19,26 @@ ucontext_t orig_context; ucontext_t child_context; +const int kStackSize = 1 << 20; + +__attribute__((noinline)) +void Throw() { + throw 1; +} + +__attribute__((noinline)) +void ThrowAndCatch() { + try { + Throw(); + } catch(int a) { + printf("ThrowAndCatch: %d\n", a); + } +} + void Child(int mode) { char x[32] = {0}; // Stack gets poisoned. printf("Child: %p\n", x); + ThrowAndCatch(); // Simulate __asan_handle_no_return(). // (a) Do nothing, just return to parent function. // (b) Jump into the original function. Stack remains poisoned unless we do // something. @@ -30,9 +50,7 @@ void Child(int mode) { } } -int Run(int arg, int mode) { - const int kStackSize = 1 << 20; - char child_stack[kStackSize + 1]; +int Run(int arg, int mode, char *child_stack) { printf("Child stack: %p\n", child_stack); // Setup child context. getcontext(&child_context); @@ -54,13 +72,23 @@ int Run(int arg, int mode) { } int main(int argc, char **argv) { + char stack[kStackSize + 1]; // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext int ret = 0; - ret += Run(argc - 1, 0); + ret += Run(argc - 1, 0, stack); printf("Test1 passed\n"); // CHECK: Test1 passed - ret += Run(argc - 1, 1); + ret += Run(argc - 1, 1, stack); printf("Test2 passed\n"); // CHECK: Test2 passed + char *heap = new char[kStackSize + 1]; + ret += Run(argc - 1, 0, heap); + printf("Test3 passed\n"); + // CHECK: Test3 passed + ret += Run(argc - 1, 1, heap); + printf("Test4 passed\n"); + // CHECK: Test4 passed + + delete [] heap; return ret; } diff --git a/lib/asan/lit_tests/Linux/syscalls.cc b/lib/asan/lit_tests/Linux/syscalls.cc new file mode 100644 index 000000000000..b2edcfb92375 --- /dev/null +++ b/lib/asan/lit_tests/Linux/syscalls.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + +#include <assert.h> +#include <errno.h> +#include <glob.h> +#include <stdio.h> +#include <string.h> + +#include <sanitizer/linux_syscall_hooks.h> + +/* Test the presence of __sanitizer_syscall_ in the tool runtime, and general + sanity of their behaviour. */ + +int main(int argc, char *argv[]) { + char buf[1000]; + __sanitizer_syscall_pre_recvmsg(0, buf - 1, 0); + // CHECK: AddressSanitizer: stack-buffer-{{.*}}erflow + // CHECK: READ of size {{.*}} at {{.*}} thread T0 + // CHECK: #0 {{.*}} in __sanitizer_syscall_pre_recvmsg + return 0; +} diff --git a/lib/asan/lit_tests/Linux/time_null_regtest.cc b/lib/asan/lit_tests/Linux/time_null_regtest.cc new file mode 100644 index 000000000000..975bca3d105a --- /dev/null +++ b/lib/asan/lit_tests/Linux/time_null_regtest.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_asan -m64 -O0 %s -fsanitize-address-zero-base-shadow -pie -o %t && %t 2>&1 | %symbolize | FileCheck %s + +// Zero-base shadow only works on x86_64 and i386. +// REQUIRES: x86_64-supported-target + +// A regression test for time(NULL), which caused ASan to crash in the +// zero-based shadow mode on Linux. +// FIXME: this test does not work on Darwin, because the code pages of the +// executable interleave with the zero-based shadow. + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +int main() { + time_t t = time(NULL); + fprintf(stderr, "Time: %s\n", ctime(&t)); // NOLINT + // CHECK: {{Time: .* .* .*}} + return 0; +} diff --git a/lib/asan/lit_tests/Linux/zero-base-shadow.cc b/lib/asan/lit_tests/Linux/zero-base-shadow.cc new file mode 100644 index 000000000000..682e7e8d7e59 --- /dev/null +++ b/lib/asan/lit_tests/Linux/zero-base-shadow.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_asan -m64 -O0 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-64 < %t.out +// RUN: %clangxx_asan -m64 -O1 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-64 < %t.out +// RUN: %clangxx_asan -m64 -O2 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-64 < %t.out +// RUN: %clangxx_asan -m32 -O0 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-32 < %t.out +// RUN: %clangxx_asan -m32 -O1 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-32 < %t.out +// RUN: %clangxx_asan -m32 -O2 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-32 < %t.out + +// Zero-base shadow only works on x86_64 and i386. +// REQUIRES: x86_64-supported-target,i386-supported-target + +#include <string.h> +int main(int argc, char **argv) { + char x[10]; + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in _?main .*zero-base-shadow.cc:}}[[@LINE-2]] + // CHECK: {{Address 0x.* is .* frame}} + // CHECK: main + + // Check that shadow for stack memory occupies lower part of address space. + // CHECK-64: =>0x0f + // CHECK-32: =>0x1 + return res; +} diff --git a/lib/asan/lit_tests/SharedLibs/darwin-dummy-shared-lib-so.cc b/lib/asan/lit_tests/SharedLibs/darwin-dummy-shared-lib-so.cc new file mode 100644 index 000000000000..5d939991476e --- /dev/null +++ b/lib/asan/lit_tests/SharedLibs/darwin-dummy-shared-lib-so.cc @@ -0,0 +1,13 @@ +//===----------- darwin-dummy-shared-lib-so.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. +// +//===----------------------------------------------------------------------===// +void foo() {} diff --git a/lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc b/lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc new file mode 100644 index 000000000000..20ef2d8a00bb --- /dev/null +++ b/lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <unistd.h> + +void inc_global(); + +int slow_init() { + sleep(1); + inc_global(); + return 42; +} + +int slowly_init_glob = slow_init(); diff --git a/lib/asan/lit_tests/Unit/lit.cfg b/lib/asan/lit_tests/Unit/lit.cfg index 243eb7fbeec0..e24361b014e9 100644 --- a/lib/asan/lit_tests/Unit/lit.cfg +++ b/lib/asan/lit_tests/Unit/lit.cfg @@ -11,9 +11,8 @@ def get_required_attr(config, attr_name): return attr_value # Setup attributes common for all compiler-rt projects. -llvm_src_root = get_required_attr(config, 'llvm_src_root') -compiler_rt_lit_unit_cfg = os.path.join(llvm_src_root, "projects", - "compiler-rt", "lib", +compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') +compiler_rt_lit_unit_cfg = os.path.join(compiler_rt_src_root, "lib", "lit.common.unit.cfg") lit.load_config(config, compiler_rt_lit_unit_cfg) diff --git a/lib/asan/lit_tests/Unit/lit.site.cfg.in b/lib/asan/lit_tests/Unit/lit.site.cfg.in index 401c3a8cc2eb..315d24d1ed09 100644 --- a/lib/asan/lit_tests/Unit/lit.site.cfg.in +++ b/lib/asan/lit_tests/Unit/lit.site.cfg.in @@ -3,8 +3,15 @@ config.target_triple = "@TARGET_TRIPLE@" config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.build_type = "@CMAKE_BUILD_TYPE@" +config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" +config.llvm_build_mode = "@LLVM_BUILD_MODE@" config.asan_binary_dir = "@ASAN_BINARY_DIR@" +try: + config.llvm_build_mode = config.llvm_build_mode % lit.params +except KeyError,e: + key, = e.args + lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) + # Let the main config do the real work. lit.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/Unit/lit.cfg") diff --git a/lib/asan/lit_tests/allow_user_segv.cc b/lib/asan/lit_tests/allow_user_segv.cc new file mode 100644 index 000000000000..f8aed0d4ca80 --- /dev/null +++ b/lib/asan/lit_tests/allow_user_segv.cc @@ -0,0 +1,50 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=180 + +// RUN: %clangxx_asan -m64 -O0 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O2 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O0 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O2 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true %t 2>&1 | FileCheck %s + +#include <signal.h> +#include <stdio.h> + +struct sigaction user_sigaction; +struct sigaction original_sigaction; + +void User_OnSIGSEGV(int signum, siginfo_t *siginfo, void *context) { + fprintf(stderr, "User sigaction called\n"); + if (original_sigaction.sa_flags | SA_SIGINFO) + original_sigaction.sa_sigaction(signum, siginfo, context); + else + original_sigaction.sa_handler(signum); +} + +int DoSEGV() { + volatile int *x = 0; + return *x; +} + +int main() { + user_sigaction.sa_sigaction = User_OnSIGSEGV; + user_sigaction.sa_flags = SA_SIGINFO; +#if defined(__APPLE__) && !defined(__LP64__) + // On 32-bit Darwin KERN_PROTECTION_FAILURE (SIGBUS) is delivered. + int signum = SIGBUS; +#else + // On 64-bit Darwin KERN_INVALID_ADDRESS (SIGSEGV) is delivered. + // On Linux SIGSEGV is delivered as well. + int signum = SIGSEGV; +#endif + if (sigaction(signum, &user_sigaction, &original_sigaction)) { + perror("sigaction"); + return 1; + } + fprintf(stderr, "User sigaction installed\n"); + return DoSEGV(); +} + +// CHECK: User sigaction installed +// CHECK-NEXT: User sigaction called +// CHECK-NEXT: ASAN:SIGSEGV +// CHECK: AddressSanitizer: SEGV on unknown address diff --git a/lib/asan/lit_tests/default_blacklist.cc b/lib/asan/lit_tests/default_blacklist.cc new file mode 100644 index 000000000000..25a1ae1752b0 --- /dev/null +++ b/lib/asan/lit_tests/default_blacklist.cc @@ -0,0 +1,3 @@ +// Test that ASan uses the default blacklist from resource directory. +// RUN: %clangxx_asan -### %s 2>&1 | FileCheck %s +// CHECK: fsanitize-blacklist={{.*}}asan_blacklist.txt diff --git a/lib/asan/lit_tests/default_options.cc b/lib/asan/lit_tests/default_options.cc index 950a7d879194..84b80557b852 100644 --- a/lib/asan/lit_tests/default_options.cc +++ b/lib/asan/lit_tests/default_options.cc @@ -4,7 +4,7 @@ const char *kAsanDefaultOptions="verbosity=1 foo=bar"; extern "C" -__attribute__((no_address_safety_analysis)) +__attribute__((no_sanitize_address)) const char *__asan_default_options() { // CHECK: Using the defaults from __asan_default_options: {{.*}} foo=bar return kAsanDefaultOptions; diff --git a/lib/asan/lit_tests/dlclose-test.cc b/lib/asan/lit_tests/dlclose-test.cc index 229f508294bf..b15895bf3579 100644 --- a/lib/asan/lit_tests/dlclose-test.cc +++ b/lib/asan/lit_tests/dlclose-test.cc @@ -9,6 +9,11 @@ // are globals. // 6. BOOM +// This sublte test assumes that after a foo.so is dlclose-d +// we can mmap the region of memory that has been occupied by the library. +// It works on i368/x86_64 Linux, but not necessary anywhere else. +// REQUIRES: x86_64-supported-target,i386-supported-target + // RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/dlclose-test-so.cc \ // RUN: -fPIC -shared -o %t-so.so // RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s @@ -39,17 +44,17 @@ #include <stdio.h> #include <string.h> #include <sys/mman.h> +#include <unistd.h> #include <string> using std::string; -static const int kPageSize = 4096; - typedef int *(fun_t)(); int main(int argc, char *argv[]) { string path = string(argv[0]) + "-so.so"; + size_t PageSize = sysconf(_SC_PAGESIZE); printf("opening %s ... \n", path.c_str()); void *lib = dlopen(path.c_str(), RTLD_NOW); if (!lib) { @@ -73,8 +78,8 @@ int main(int argc, char *argv[]) { return 1; } // Now, the page where 'addr' is unmapped. Map it. - size_t page_beg = ((size_t)addr) & ~(kPageSize - 1); - void *res = mmap((void*)(page_beg), kPageSize, + size_t page_beg = ((size_t)addr) & ~(PageSize - 1); + void *res = mmap((void*)(page_beg), PageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, 0, 0); if (res == (char*)-1L) { diff --git a/lib/asan/lit_tests/double-free.cc b/lib/asan/lit_tests/double-free.cc new file mode 100644 index 000000000000..9e201117c563 --- /dev/null +++ b/lib/asan/lit_tests/double-free.cc @@ -0,0 +1,18 @@ +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + +#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]; + free(x); + free(x + argc - 1); // BOOM + // CHECK: AddressSanitizer: attempting double-free{{.*}}in thread T0 + // CHECK: double-free.cc:[[@LINE-2]] + // CHECK: freed by thread T0 here: + // CHECK: double-free.cc:[[@LINE-5]] + // CHECK: allocated by thread T0 here: + // CHECK: double-free.cc:[[@LINE-10]] + return res; +} diff --git a/lib/asan/lit_tests/global-demangle.cc b/lib/asan/lit_tests/global-demangle.cc new file mode 100644 index 000000000000..5696a38a7705 --- /dev/null +++ b/lib/asan/lit_tests/global-demangle.cc @@ -0,0 +1,18 @@ +// Don't run through %symbolize to avoid c++filt demangling. +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s + +namespace XXX { +class YYY { + public: + static char ZZZ[]; +}; +char YYY::ZZZ[] = "abc"; +} + +int main(int argc, char **argv) { + return (int)XXX::YYY::ZZZ[argc + 5]; // BOOM + // CHECK: {{READ of size 1 at 0x.*}} + // CHECK: {{0x.* is located 2 bytes to the right of global variable}} + // CHECK: 'XXX::YYY::ZZZ' {{.*}} of size 4 + // CHECK: 'XXX::YYY::ZZZ' is ascii string 'abc' +} diff --git a/lib/asan/lit_tests/heap-overflow.cc b/lib/asan/lit_tests/heap-overflow.cc index 2648ec7e5f1f..f1d719cd0b20 100644 --- a/lib/asan/lit_tests/heap-overflow.cc +++ b/lib/asan/lit_tests/heap-overflow.cc @@ -29,10 +29,8 @@ int main(int argc, char **argv) { // CHECK-Linux: {{ #0 0x.* in .*malloc}} // CHECK-Linux: {{ #1 0x.* in main .*heap-overflow.cc:21}} - // CHECK-Darwin: {{ #0 0x.* in .*mz_malloc.*}} - // CHECK-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}} - // CHECK-Darwin: {{ #2 0x.* in malloc.*}} - // CHECK-Darwin: {{ #3 0x.* in _?main .*heap-overflow.cc:21}} + // CHECK-Darwin: {{ #0 0x.* in _?wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in _?main .*heap-overflow.cc:21}} free(x); return res; } diff --git a/lib/asan/lit_tests/huge_negative_hea_oob.cc b/lib/asan/lit_tests/huge_negative_hea_oob.cc new file mode 100644 index 000000000000..a09e3bf87d60 --- /dev/null +++ b/lib/asan/lit_tests/huge_negative_hea_oob.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan -m64 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O %s -o %t && %t 2>&1 | FileCheck %s +// Check that we can find huge buffer overflows to the left. +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char *x = (char*)malloc(1 << 20); + memset(x, 0, 10); + int res = x[-argc * 4000]; // BOOOM + // CHECK: is located 4000 bytes to the left of + free(x); + return res; +} diff --git a/lib/asan/lit_tests/init-order-atexit.cc b/lib/asan/lit_tests/init-order-atexit.cc new file mode 100644 index 000000000000..45f4f17c0cb0 --- /dev/null +++ b/lib/asan/lit_tests/init-order-atexit.cc @@ -0,0 +1,31 @@ +// Test for the following situation: +// (1) global A is constructed. +// (2) exit() is called during construction of global B. +// (3) destructor of A reads uninitialized global C from another module. +// We do *not* want to report init-order bug in this case. + +// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/init-order-atexit-extra.cc -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +void AccessC(); + +class A { + public: + A() { } + ~A() { AccessC(); printf("PASSED\n"); } + // CHECK-NOT: AddressSanitizer + // CHECK: PASSED +}; + +A a; + +class B { + public: + B() { exit(1); } + ~B() { } +}; + +B b; diff --git a/lib/asan/lit_tests/init-order-dlopen.cc b/lib/asan/lit_tests/init-order-dlopen.cc new file mode 100644 index 000000000000..228f44204c99 --- /dev/null +++ b/lib/asan/lit_tests/init-order-dlopen.cc @@ -0,0 +1,52 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=178 + +// RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/init-order-dlopen-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// If the linker doesn't support --export-dynamic (which is ELF-specific), +// try to link without that option. +// FIXME: find a better solution. +// RUN: %clangxx_asan -m64 -O0 %s -o %t -Wl,--export-dynamic || \ +// RUN: %clangxx_asan -m64 -O0 %s -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 | FileCheck %s +#include <dlfcn.h> +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +#include <string> + +using std::string; + +int foo() { + return 42; +} +int global = foo(); + +__attribute__((visibility("default"))) +void inc_global() { + global++; +} + +void *global_poller(void *arg) { + while (true) { + if (global != 42) + break; + usleep(100); + } + return 0; +} + +int main(int argc, char *argv[]) { + pthread_t p; + pthread_create(&p, 0, global_poller, 0); + string path = string(argv[0]) + "-so.so"; + if (0 == dlopen(path.c_str(), RTLD_NOW)) { + fprintf(stderr, "dlerror: %s\n", dlerror()); + return 1; + } + pthread_join(p, 0); + printf("PASSED\n"); + // CHECK: PASSED + return 0; +} diff --git a/lib/asan/lit_tests/initialization-blacklist.cc b/lib/asan/lit_tests/initialization-blacklist.cc index f8df24c68ea6..12fbc49ed91b 100644 --- a/lib/asan/lit_tests/initialization-blacklist.cc +++ b/lib/asan/lit_tests/initialization-blacklist.cc @@ -1,23 +1,35 @@ // Test for blacklist functionality of initialization-order checker. // RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ // RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ -// RUN: -fsanitize=init-order -o %t && %t 2>&1 +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 // RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ // RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ -// RUN: -fsanitize=init-order -o %t && %t 2>&1 +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 // RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ // RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ -// RUN: -fsanitize=init-order -o %t && %t 2>&1 +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 // RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ // RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ -// RUN: -fsanitize=init-order -o %t && %t 2>&1 +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 // RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ // RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ -// RUN: -fsanitize=init-order -o %t && %t 2>&1 +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 // RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ // RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ -// RUN: -fsanitize=init-order -o %t && %t 2>&1 +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 // Function is defined in another TU. int readBadGlobal(); @@ -27,6 +39,9 @@ int x = readBadGlobal(); // init-order bug. int accessBadObject(); int y = accessBadObject(); // init-order bug. +int readBadSrcGlobal(); +int z = readBadSrcGlobal(); // init-order bug. + int main(int argc, char **argv) { - return argc + x + y - 1; + return argc + x + y + z - 1; } diff --git a/lib/asan/lit_tests/initialization-bug.cc b/lib/asan/lit_tests/initialization-bug.cc index 8f4e33ef5a35..ee2c725f0b13 100644 --- a/lib/asan/lit_tests/initialization-bug.cc +++ b/lib/asan/lit_tests/initialization-bug.cc @@ -1,14 +1,17 @@ // Test to make sure basic initialization order errors are caught. -// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-bug-extra2.cc\ -// RUN: -fsanitize=init-order -o %t && %t 2>&1 \ +// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 \ // RUN: | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-bug-extra2.cc\ -// RUN: -fsanitize=init-order -o %t && %t 2>&1 \ +// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 \ // RUN: | %symbolize | FileCheck %s // Do not test with optimization -- the error may be optimized away. +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=186 +// XFAIL: darwin + #include <cstdio> // The structure of the test is: diff --git a/lib/asan/lit_tests/initialization-constexpr.cc b/lib/asan/lit_tests/initialization-constexpr.cc new file mode 100644 index 000000000000..ba5410674f76 --- /dev/null +++ b/lib/asan/lit_tests/initialization-constexpr.cc @@ -0,0 +1,43 @@ +// Constexpr: +// We need to check that a global variable initialized with a constexpr +// constructor can be accessed during dynamic initialization (as a constexpr +// constructor implies that it was initialized during constant initialization, +// not dynamic initialization). + +// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m64 -O3 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m32 -O3 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 + +class Integer { + private: + int value; + + public: + constexpr Integer(int x = 0) : value(x) {} + int getValue() {return value;} +}; +Integer coolestInteger(42); +int getCoolestInteger() { return coolestInteger.getValue(); } + +int main() { return 0; } diff --git a/lib/asan/lit_tests/initialization-nobug.cc b/lib/asan/lit_tests/initialization-nobug.cc index 1b8961606811..407226e29a1b 100644 --- a/lib/asan/lit_tests/initialization-nobug.cc +++ b/lib/asan/lit_tests/initialization-nobug.cc @@ -1,24 +1,22 @@ // A collection of various initializers which shouldn't trip up initialization // order checking. If successful, this will just return 0. -// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-nobug-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1 -// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-nobug-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1 -// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-nobug-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1 -// RUN: %clangxx_asan -m64 -O3 %s %p/Helpers/initialization-nobug-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1 -// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-nobug-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1 -// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-nobug-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1 -// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-nobug-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1 -// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-nobug-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1 -// RUN: %clangxx_asan -m32 -O3 %s %p/Helpers/initialization-nobug-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1 +// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m64 -O3 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -m32 -O3 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 // Simple access: // Make sure that accessing a global in the same TU is safe @@ -47,21 +45,12 @@ int countCalls() { return ++calls; } -// Constexpr: -// We need to check that a global variable initialized with a constexpr -// constructor can be accessed during dynamic initialization (as a constexpr -// constructor implies that it was initialized during constant initialization, -// not dynamic initialization). - -class Integer { - private: +// Trivial constructor, non-trivial destructor. +struct StructWithDtor { + ~StructWithDtor() { } int value; - - public: - constexpr Integer(int x = 0) : value(x) {} - int getValue() {return value;} }; -Integer coolestInteger(42); -int getCoolestInteger() { return coolestInteger.getValue(); } +StructWithDtor struct_with_dtor; +int getStructWithDtorValue() { return struct_with_dtor.value; } int main() { return 0; } diff --git a/lib/asan/lit_tests/interface_test.cc b/lib/asan/lit_tests/interface_test.cc new file mode 100644 index 000000000000..428a109fe70d --- /dev/null +++ b/lib/asan/lit_tests/interface_test.cc @@ -0,0 +1,8 @@ +// Check that user may include ASan interface header. +// RUN: %clang -fsanitize=address -I %p/../../../include %s -o %t && %t +// RUN: %clang -I %p/../../../include %s -o %t && %t +#include <sanitizer/asan_interface.h> + +int main() { + return 0; +} diff --git a/lib/asan/lit_tests/invalid-free.cc b/lib/asan/lit_tests/invalid-free.cc new file mode 100644 index 000000000000..0ef064056b63 --- /dev/null +++ b/lib/asan/lit_tests/invalid-free.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + +#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]; + free(x + 5); // BOOM + // CHECK: AddressSanitizer: attempting free on address{{.*}}in thread T0 + // CHECK: invalid-free.cc:[[@LINE-2]] + // CHECK: is located 5 bytes inside of 10-byte region + // CHECK: allocated by thread T0 here: + // CHECK: invalid-free.cc:[[@LINE-8]] + return res; +} diff --git a/lib/asan/lit_tests/large_func_test.cc b/lib/asan/lit_tests/large_func_test.cc index a74828811f74..ceecc29b7b0a 100644 --- a/lib/asan/lit_tests/large_func_test.cc +++ b/lib/asan/lit_tests/large_func_test.cc @@ -32,7 +32,7 @@ static void LargeFunction(int *x, int zero) { // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} // CHECK: {{READ of size 4 at 0x.* thread T0}} - x[zero + 111]++; // we should report this exact line + x[zero + 103]++; // we should report this exact line // atos incorrectly extracts the symbol name for the static functions on // Darwin. // CHECK-Linux: {{#0 0x.* in LargeFunction.*large_func_test.cc:}}[[@LINE-3]] @@ -54,9 +54,10 @@ int main(int argc, char **argv) { int *x = new int[100]; LargeFunction(x, argc - 1); // CHECK: {{ #1 0x.* in _?main .*large_func_test.cc:}}[[@LINE-1]] - // CHECK: {{0x.* is located 44 bytes to the right of 400-byte region}} + // CHECK: {{0x.* is located 12 bytes to the right of 400-byte region}} // CHECK: {{allocated by thread T0 here:}} - // CHECK: {{ #0 0x.* in operator new.*}} - // CHECK: {{ #1 0x.* in _?main .*large_func_test.cc:}}[[@LINE-6]] + // CHECK-Linux: {{ #0 0x.* in operator new.*}} + // CHECK-Darwin: {{ #0 0x.* in .*_Zna.*}} + // CHECK: {{ #1 0x.* in _?main .*large_func_test.cc:}}[[@LINE-7]] delete x; } diff --git a/lib/asan/lit_tests/lit.cfg b/lib/asan/lit_tests/lit.cfg index 7875281b1f2f..5daecd9e557d 100644 --- a/lib/asan/lit_tests/lit.cfg +++ b/lib/asan/lit_tests/lit.cfg @@ -2,6 +2,14 @@ import os +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit.fatal("No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + # Setup config name. config.name = 'AddressSanitizer' @@ -30,14 +38,6 @@ if llvm_src_root is None: if not llvm_config: DisplayNoConfigMessage() - # Validate that llvm-config points to the same source tree. - llvm_src_root = lit.util.capture(["llvm-config", "--src-root"]).strip() - asan_test_src_root = os.path.join(llvm_src_root, "projects", "compiler-rt", - "lib", "asan", "lit_tests") - if (os.path.realpath(asan_test_src_root) != - os.path.realpath(config.test_source_root)): - DisplayNoConfigMessage() - # Find out the presumed location of generated site config. llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip() asan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt", @@ -49,8 +49,9 @@ if llvm_src_root is None: raise SystemExit # Setup attributes common for all compiler-rt projects. -compiler_rt_lit_cfg = os.path.join(llvm_src_root, "projects", "compiler-rt", - "lib", "lit.common.cfg") +compiler_rt_src_root = get_required_attr(config, "compiler_rt_src_root") +compiler_rt_lit_cfg = os.path.join(compiler_rt_src_root, "lib", + "lit.common.cfg") if (not compiler_rt_lit_cfg) or (not os.path.exists(compiler_rt_lit_cfg)): lit.fatal("Can't find common compiler-rt lit config at: %r" % compiler_rt_lit_cfg) diff --git a/lib/asan/lit_tests/lit.site.cfg.in b/lib/asan/lit_tests/lit.site.cfg.in index cf439309c6ad..08546cdabe02 100644 --- a/lib/asan/lit_tests/lit.site.cfg.in +++ b/lib/asan/lit_tests/lit.site.cfg.in @@ -5,8 +5,10 @@ config.target_triple = "@TARGET_TRIPLE@" config.host_os = "@HOST_OS@" config.llvm_src_root = "@LLVM_SOURCE_DIR@" config.llvm_obj_root = "@LLVM_BINARY_DIR@" +config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.clang = "@LLVM_BINARY_DIR@/bin/clang" +config.compiler_rt_arch = "@COMPILER_RT_SUPPORTED_ARCH@" # LLVM tools dir can be passed in lit parameters, so try to # apply substitution. diff --git a/lib/asan/lit_tests/log_path_fork_test.cc b/lib/asan/lit_tests/log_path_fork_test.cc.disabled index c6c1b49e994d..c6c1b49e994d 100644 --- a/lib/asan/lit_tests/log_path_fork_test.cc +++ b/lib/asan/lit_tests/log_path_fork_test.cc.disabled diff --git a/lib/asan/lit_tests/malloc_fill.cc b/lib/asan/lit_tests/malloc_fill.cc new file mode 100644 index 000000000000..c23516b33299 --- /dev/null +++ b/lib/asan/lit_tests/malloc_fill.cc @@ -0,0 +1,22 @@ +// Check that we fill malloc-ed memory correctly. +// RUN: %clangxx_asan -m64 %s -o %t +// RUN: %t | FileCheck %s +// RUN: ASAN_OPTIONS=max_malloc_fill_size=10:malloc_fill_byte=8 %t | FileCheck %s --check-prefix=CHECK-10-8 +// RUN: ASAN_OPTIONS=max_malloc_fill_size=20:malloc_fill_byte=171 %t | FileCheck %s --check-prefix=CHECK-20-ab + +#include <stdio.h> +int main(int argc, char **argv) { + // With asan allocator this makes sure we get memory from mmap. + static const int kSize = 1 << 25; + unsigned char *x = new unsigned char[kSize]; + printf("-"); + for (int i = 0; i <= 32; i++) { + printf("%02x", x[i]); + } + printf("-\n"); + delete [] x; +} + +// CHECK: -bebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebe- +// CHECK-10-8: -080808080808080808080000000000000000000000000000000000000000000000- +// CHECK-20-ab: -abababababababababababababababababababab00000000000000000000000000- diff --git a/lib/asan/lit_tests/memcmp_strict_test.cc b/lib/asan/lit_tests/memcmp_strict_test.cc new file mode 100644 index 000000000000..00bf921c744a --- /dev/null +++ b/lib/asan/lit_tests/memcmp_strict_test.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -m64 -O0 %s -o %t && ASAN_OPTIONS=strict_memcmp=0 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-nonstrict +// RUN: %clangxx_asan -m64 -O0 %s -o %t && ASAN_OPTIONS=strict_memcmp=1 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-strict +// Default to strict_memcmp=1. +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-strict + +#include <stdio.h> +#include <string.h> +int main() { + char kFoo[] = "foo"; + char kFubar[] = "fubar"; + int res = memcmp(kFoo, kFubar, strlen(kFubar)); + printf("res: %d\n", res); + // CHECK-nonstrict: {{res: -1}} + // CHECK-strict: AddressSanitizer: stack-buffer-overflow + return 0; +} diff --git a/lib/asan/lit_tests/partial_right.cc b/lib/asan/lit_tests/partial_right.cc new file mode 100644 index 000000000000..c579262726f9 --- /dev/null +++ b/lib/asan/lit_tests/partial_right.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + +#include <stdlib.h> +int main(int argc, char **argv) { + volatile int *x = (int*)malloc(2*sizeof(int) + 2); + int res = x[2]; // BOOOM + // CHECK: {{READ of size 4 at 0x.* thread T0}} + // CHECK: [[ADDR:0x[01-9a-fa-f]+]] is located 0 bytes to the right of {{.*}}-byte region [{{.*}},{{.*}}[[ADDR]]) + return res; +} diff --git a/lib/asan/lit_tests/stack-frame-demangle.cc b/lib/asan/lit_tests/stack-frame-demangle.cc index 7f4d59fc5838..bb8de16b2b8a 100644 --- a/lib/asan/lit_tests/stack-frame-demangle.cc +++ b/lib/asan/lit_tests/stack-frame-demangle.cc @@ -1,7 +1,4 @@ -// Check that ASan is able to print demangled frame name even w/o -// symbolization. - -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s #include <string.h> @@ -11,12 +8,13 @@ struct YYY { char array[10]; memset(array, 0, 10); return array[x]; // BOOOM - // CHECK: {{ERROR: AddressSanitizer: stack-buffer-overflow}} - // CHECK: {{READ of size 1 at 0x.* thread T0}} - // CHECK: {{Address 0x.* is .* frame <XXX::YYY::ZZZ(.*)>}} + // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow + // CHECK: READ of size 1 at + // CHECK: is located in stack of thread T0 at offset + // CHECK: XXX::YYY::ZZZ } }; -}; +} // namespace XXX int main(int argc, char **argv) { int res = XXX::YYY::ZZZ(argc + 10); diff --git a/lib/asan/lit_tests/stack-oob-frames.cc b/lib/asan/lit_tests/stack-oob-frames.cc new file mode 100644 index 000000000000..0395522252e8 --- /dev/null +++ b/lib/asan/lit_tests/stack-oob-frames.cc @@ -0,0 +1,59 @@ +// RUN: %clangxx_asan -m64 -O1 %s -o %t +// RUN: %t 0 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK0 +// RUN: %t 1 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK1 +// RUN: %t 2 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK2 +// RUN: %t 3 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK3 + +#define NOINLINE __attribute__((noinline)) +inline void break_optimization(void *arg) { + __asm__ __volatile__("" : : "r" (arg) : "memory"); +} + +NOINLINE static void Frame0(int frame, char *a, char *b, char *c) { + char s[4] = {0}; + char *d = s; + break_optimization(&d); + switch (frame) { + case 3: a[5]++; break; + case 2: b[5]++; break; + case 1: c[5]++; break; + case 0: d[5]++; break; + } +} +NOINLINE static void Frame1(int frame, char *a, char *b) { + char c[4] = {0}; Frame0(frame, a, b, c); + break_optimization(0); +} +NOINLINE static void Frame2(int frame, char *a) { + char b[4] = {0}; Frame1(frame, a, b); + break_optimization(0); +} +NOINLINE static void Frame3(int frame) { + char a[4] = {0}; Frame2(frame, a); + break_optimization(0); +} + +int main(int argc, char **argv) { + if (argc != 2) return 1; + Frame3(argv[1][0] - '0'); +} + +// CHECK0: AddressSanitizer: stack-buffer-overflow +// CHECK0: #0{{.*}}Frame0 +// CHECK0: #1{{.*}}Frame1 +// CHECK0: #2{{.*}}Frame2 +// CHECK0: #3{{.*}}Frame3 +// CHECK0: is located in stack of thread T0 at offset +// CHECK0-NEXT: #0{{.*}}Frame0 +// +// CHECK1: AddressSanitizer: stack-buffer-overflow +// CHECK1: is located in stack of thread T0 at offset +// CHECK1-NEXT: #0{{.*}}Frame1 +// +// CHECK2: AddressSanitizer: stack-buffer-overflow +// CHECK2: is located in stack of thread T0 at offset +// CHECK2-NEXT: #0{{.*}}Frame2 +// +// CHECK3: AddressSanitizer: stack-buffer-overflow +// CHECK3: is located in stack of thread T0 at offset +// CHECK3-NEXT: #0{{.*}}Frame3 diff --git a/lib/asan/lit_tests/stack-overflow.cc b/lib/asan/lit_tests/stack-overflow.cc index 3deb1e91de6c..25ea43af48a4 100644 --- a/lib/asan/lit_tests/stack-overflow.cc +++ b/lib/asan/lit_tests/stack-overflow.cc @@ -14,6 +14,7 @@ int main(int argc, char **argv) { int res = x[argc * 10]; // BOOOM // CHECK: {{READ of size 1 at 0x.* thread T0}} // CHECK: {{ #0 0x.* in _?main .*stack-overflow.cc:}}[[@LINE-2]] - // CHECK: {{Address 0x.* is .* frame <main>}} + // CHECK: {{Address 0x.* is located in stack of thread T0 at offset}} + // CHECK-NEXT: in{{.*}}main{{.*}}stack-overflow.cc return res; } diff --git a/lib/asan/lit_tests/strncpy-overflow.cc b/lib/asan/lit_tests/strncpy-overflow.cc index 18711843c4c8..5133b5c1653e 100644 --- a/lib/asan/lit_tests/strncpy-overflow.cc +++ b/lib/asan/lit_tests/strncpy-overflow.cc @@ -22,7 +22,7 @@ int main(int argc, char **argv) { strcpy(hello, "hello"); char *short_buffer = (char*)malloc(9); strncpy(short_buffer, hello, 10); // BOOM - // CHECK: {{WRITE of size 1 at 0x.* thread T0}} + // CHECK: {{WRITE of size 10 at 0x.* thread T0}} // CHECK-Linux: {{ #0 0x.* in .*strncpy}} // CHECK-Darwin: {{ #0 0x.* in _?wrap_strncpy}} // CHECK: {{ #1 0x.* in _?main .*strncpy-overflow.cc:}}[[@LINE-4]] @@ -32,9 +32,7 @@ int main(int argc, char **argv) { // CHECK-Linux: {{ #0 0x.* in .*malloc}} // CHECK-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-10]] - // CHECK-Darwin: {{ #0 0x.* in .*mz_malloc.*}} - // CHECK-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}} - // CHECK-Darwin: {{ #2 0x.* in malloc.*}} - // CHECK-Darwin: {{ #3 0x.* in _?main .*strncpy-overflow.cc:}}[[@LINE-15]] + // CHECK-Darwin: {{ #0 0x.* in _?wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in _?main .*strncpy-overflow.cc:}}[[@LINE-13]] return short_buffer[8]; } diff --git a/lib/asan/lit_tests/throw_call_test.cc b/lib/asan/lit_tests/throw_call_test.cc new file mode 100644 index 000000000000..974bc51d97c5 --- /dev/null +++ b/lib/asan/lit_tests/throw_call_test.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_asan %s -o %t && %t +// http://code.google.com/p/address-sanitizer/issues/detail?id=147 (not fixed). +// BROKEN: %clangxx_asan %s -o %t -static-libstdc++ && %t +#include <stdio.h> +static volatile int zero = 0; +inline void pretend_to_do_something(void *x) { + __asm__ __volatile__("" : : "r" (x) : "memory"); +} + +__attribute__((noinline, no_sanitize_address)) +void ReallyThrow() { + fprintf(stderr, "ReallyThrow\n"); + if (zero == 0) + throw 42; +} + +__attribute__((noinline)) +void Throw() { + int a, b, c, d, e; + pretend_to_do_something(&a); + pretend_to_do_something(&b); + pretend_to_do_something(&c); + pretend_to_do_something(&d); + pretend_to_do_something(&e); + fprintf(stderr, "Throw stack = %p\n", &a); + ReallyThrow(); +} + +__attribute__((noinline)) +void CheckStack() { + int ar[100]; + pretend_to_do_something(ar); + for (int i = 0; i < 100; i++) + ar[i] = i; + fprintf(stderr, "CheckStack stack = %p, %p\n", ar, ar + 100); +} + +int main(int argc, char** argv) { + try { + Throw(); + } catch(int a) { + fprintf(stderr, "a = %d\n", a); + } + CheckStack(); +} diff --git a/lib/asan/lit_tests/throw_invoke_test.cc b/lib/asan/lit_tests/throw_invoke_test.cc new file mode 100644 index 000000000000..077a940e8d19 --- /dev/null +++ b/lib/asan/lit_tests/throw_invoke_test.cc @@ -0,0 +1,50 @@ +// RUN: %clangxx_asan %s -o %t && %t +// RUN: %clangxx_asan %s -o %t -static-libstdc++ && %t +#include <stdio.h> +static volatile int zero = 0; +inline void pretend_to_do_something(void *x) { + __asm__ __volatile__("" : : "r" (x) : "memory"); +} + +__attribute__((noinline)) +void ReallyThrow() { + fprintf(stderr, "ReallyThrow\n"); + try { + if (zero == 0) + throw 42; + else if (zero == 1) + throw 1.; + } catch(double x) { + } +} + +__attribute__((noinline)) +void Throw() { + int a, b, c, d, e; + pretend_to_do_something(&a); + pretend_to_do_something(&b); + pretend_to_do_something(&c); + pretend_to_do_something(&d); + pretend_to_do_something(&e); + fprintf(stderr, "Throw stack = %p\n", &a); + ReallyThrow(); +} + +__attribute__((noinline)) +void CheckStack() { + int ar[100]; + pretend_to_do_something(ar); + for (int i = 0; i < 100; i++) + ar[i] = i; + fprintf(stderr, "CheckStack stack = %p, %p\n", ar, ar + 100); +} + +int main(int argc, char** argv) { + try { + Throw(); + } catch(int a) { + fprintf(stderr, "a = %d\n", a); + } + CheckStack(); +} + diff --git a/lib/asan/lit_tests/time_interceptor.cc b/lib/asan/lit_tests/time_interceptor.cc new file mode 100644 index 000000000000..f5f2ad62b815 --- /dev/null +++ b/lib/asan/lit_tests/time_interceptor.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + +// Test the time() interceptor. + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +int main() { + time_t *tm = (time_t*)malloc(sizeof(time_t)); + free(tm); + time_t t = time(tm); + printf("Time: %s\n", ctime(&t)); // NOLINT + // CHECK: use-after-free + return 0; +} diff --git a/lib/asan/lit_tests/unaligned_loads_and_stores.cc b/lib/asan/lit_tests/unaligned_loads_and_stores.cc new file mode 100644 index 000000000000..bcae089b427b --- /dev/null +++ b/lib/asan/lit_tests/unaligned_loads_and_stores.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_asan -O0 -I %p/../../../include %s -o %t +// RUN: %t A 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-A %s +// RUN: %t B 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-B %s +// RUN: %t C 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-C %s +// RUN: %t D 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-D %s +// RUN: %t E 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-E %s + +// RUN: %t K 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-K %s +// RUN: %t L 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-L %s +// RUN: %t M 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-M %s +// RUN: %t N 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-N %s +// RUN: %t O 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-O %s + +#include <sanitizer/asan_interface.h> + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + if (argc != 2) return 1; + char *x = new char[16]; + memset(x, 0xab, 16); + int res = 1; + switch (argv[1][0]) { + case 'A': res = __sanitizer_unaligned_load16(x + 15); break; +// CHECK-A ERROR: AddressSanitizer: heap-buffer-overflow on address +// CHECK-A: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-2]] +// CHECK-A: is located 0 bytes to the right of 16-byte region + case 'B': res = __sanitizer_unaligned_load32(x + 14); break; +// CHECK-B: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'C': res = __sanitizer_unaligned_load32(x + 13); break; +// CHECK-C: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'D': res = __sanitizer_unaligned_load64(x + 15); break; +// CHECK-D: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'E': res = __sanitizer_unaligned_load64(x + 9); break; +// CHECK-E: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + + case 'K': __sanitizer_unaligned_store16(x + 15, 0); break; +// CHECK-K ERROR: AddressSanitizer: heap-buffer-overflow on address +// CHECK-K: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-2]] +// CHECK-K: is located 0 bytes to the right of 16-byte region + case 'L': __sanitizer_unaligned_store32(x + 15, 0); break; +// CHECK-L: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'M': __sanitizer_unaligned_store32(x + 13, 0); break; +// CHECK-M: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'N': __sanitizer_unaligned_store64(x + 10, 0); break; +// CHECK-N: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'O': __sanitizer_unaligned_store64(x + 14, 0); break; +// CHECK-O: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + } + delete x; + return res; +} diff --git a/lib/asan/lit_tests/use-after-free-right.cc b/lib/asan/lit_tests/use-after-free-right.cc new file mode 100644 index 000000000000..b0de07b04a08 --- /dev/null +++ b/lib/asan/lit_tests/use-after-free-right.cc @@ -0,0 +1,46 @@ +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out +// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out +// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out +// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out +// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out +// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out +// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out +// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out + +// Test use-after-free report in the case when access is at the right border of +// the allocation. + +#include <stdlib.h> +int main() { + volatile char *x = (char*)malloc(sizeof(char)); + free((void*)x); + *x = 42; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{WRITE of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in _?main .*use-after-free-right.cc:25}} + // CHECK: {{0x.* is located 0 bytes inside of 1-byte region .0x.*,0x.*}} + // CHECK: {{freed by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*free}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:24}} + + // CHECK-Darwin: {{ #0 0x.* in _?wrap_free}} + // CHECK-Darwin: {{ #1 0x.* in _?main .*use-after-free-right.cc:24}} + + // CHECK: {{previously allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:23}} + + // CHECK-Darwin: {{ #0 0x.* in _?wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in _?main .*use-after-free-right.cc:23}} +} diff --git a/lib/asan/lit_tests/use-after-free.cc b/lib/asan/lit_tests/use-after-free.cc index 24d5a2a54807..aee185dc4518 100644 --- a/lib/asan/lit_tests/use-after-free.cc +++ b/lib/asan/lit_tests/use-after-free.cc @@ -30,19 +30,14 @@ int main() { // CHECK-Linux: {{ #0 0x.* in .*free}} // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:21}} - // CHECK-Darwin: {{ #0 0x.* in .*free_common.*}} - // CHECK-Darwin: {{ #1 0x.* in .*mz_free.*}} - // We override free() on Darwin, thus no malloc_zone_free - // CHECK-Darwin: {{ #2 0x.* in _?wrap_free}} - // CHECK-Darwin: {{ #3 0x.* in _?main .*use-after-free.cc:21}} + // CHECK-Darwin: {{ #0 0x.* in _?wrap_free}} + // CHECK-Darwin: {{ #1 0x.* in _?main .*use-after-free.cc:21}} // CHECK: {{previously allocated by thread T0 here:}} // CHECK-Linux: {{ #0 0x.* in .*malloc}} // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:20}} - // CHECK-Darwin: {{ #0 0x.* in .*mz_malloc.*}} - // CHECK-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}} - // CHECK-Darwin: {{ #2 0x.* in malloc.*}} - // CHECK-Darwin: {{ #3 0x.* in _?main .*use-after-free.cc:20}} + // CHECK-Darwin: {{ #0 0x.* in _?wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in _?main .*use-after-free.cc:20}} } diff --git a/lib/asan/lit_tests/use-after-poison.cc b/lib/asan/lit_tests/use-after-poison.cc new file mode 100644 index 000000000000..d87342900245 --- /dev/null +++ b/lib/asan/lit_tests/use-after-poison.cc @@ -0,0 +1,20 @@ +// Check that __asan_poison_memory_region works. +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// +// Check that we can disable it +// RUN: ASAN_OPTIONS=allow_user_poisoning=0 %t + +#include <stdlib.h> + +extern "C" void __asan_poison_memory_region(void *, size_t); + +int main(int argc, char **argv) { + char *x = new char[16]; + x[10] = 0; + __asan_poison_memory_region(x, 16); + int res = x[argc * 10]; // BOOOM + // CHECK: ERROR: AddressSanitizer: use-after-poison + // CHECK: main{{.*}}use-after-poison.cc:[[@LINE-2]] + delete [] x; + return res; +} diff --git a/lib/asan/lit_tests/use-after-scope-inlined.cc b/lib/asan/lit_tests/use-after-scope-inlined.cc index 3d730de6ab35..5c121ea187eb 100644 --- a/lib/asan/lit_tests/use-after-scope-inlined.cc +++ b/lib/asan/lit_tests/use-after-scope-inlined.cc @@ -23,7 +23,8 @@ int main(int argc, char *argv[]) { // CHECK: READ of size 4 at 0x{{.*}} thread T0 // CHECK: #0 0x{{.*}} in {{_?}}main // CHECK: {{.*}}use-after-scope-inlined.cc:[[@LINE-4]] - // CHECK: Address 0x{{.*}} is located at offset - // CHECK: [[OFFSET:[^ ]*]] in frame <main> of T0{{.*}}: + // CHECK: Address 0x{{.*}} is located in stack of thread T0 at offset + // CHECK: [[OFFSET:[^ ]*]] in frame + // CHECK: main // CHECK: {{\[}}[[OFFSET]], {{.*}}) 'x.i' } diff --git a/lib/asan/lit_tests/wait.cc b/lib/asan/lit_tests/wait.cc new file mode 100644 index 000000000000..88fbb17176fa --- /dev/null +++ b/lib/asan/lit_tests/wait.cc @@ -0,0 +1,77 @@ +// RUN: %clangxx_asan -DWAIT -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + +// RUN: %clangxx_asan -DWAITPID -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAITPID -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAITPID -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAITPID -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + +// RUN: %clangxx_asan -DWAITID -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAITID -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAITID -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAITID -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + +// RUN: %clangxx_asan -DWAIT3 -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT3 -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT3 -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT3 -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + +// RUN: %clangxx_asan -DWAIT4 -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT4 -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT4 -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT4 -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + +// RUN: %clangxx_asan -DWAIT3_RUSAGE -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT3_RUSAGE -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT3_RUSAGE -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT3_RUSAGE -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + +// RUN: %clangxx_asan -DWAIT4_RUSAGE -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT4_RUSAGE -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT4_RUSAGE -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -DWAIT4_RUSAGE -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + + +#include <assert.h> +#include <sys/wait.h> +#include <unistd.h> + +int main(int argc, char **argv) { + pid_t pid = fork(); + if (pid) { // parent + int x[3]; + int *status = x + argc * 3; + int res; +#if defined(WAIT) + res = wait(status); +#elif defined(WAITPID) + res = waitpid(pid, status, WNOHANG); +#elif defined(WAITID) + siginfo_t *si = (siginfo_t*)(x + argc * 3); + res = waitid(P_ALL, 0, si, WEXITED | WNOHANG); +#elif defined(WAIT3) + res = wait3(status, WNOHANG, NULL); +#elif defined(WAIT4) + res = wait4(pid, status, WNOHANG, NULL); +#elif defined(WAIT3_RUSAGE) || defined(WAIT4_RUSAGE) + struct rusage *ru = (struct rusage*)(x + argc * 3); + int good_status; +# if defined(WAIT3_RUSAGE) + res = wait3(&good_status, WNOHANG, ru); +# elif defined(WAIT4_RUSAGE) + res = wait4(pid, &good_status, WNOHANG, ru); +# endif +#endif + // CHECK: stack-buffer-overflow + // CHECK: {{WRITE of size .* at 0x.* thread T0}} + // CHECK: {{in .*wait}} + // CHECK: {{in _?main .*wait.cc:}} + // CHECK: is located in stack of thread T0 at offset + // CHECK: {{in _?main}} + return res != -1; + } + // child + return 0; +} diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py index 7b30bb55914e..bd3bf1e9b53e 100755 --- a/lib/asan/scripts/asan_symbolize.py +++ b/lib/asan/scripts/asan_symbolize.py @@ -8,6 +8,7 @@ # #===------------------------------------------------------------------------===# import bisect +import getopt import os import re import subprocess @@ -18,6 +19,7 @@ symbolizers = {} filetypes = {} vmaddrs = {} DEBUG = False +demangle = False; # FIXME: merge the code that calls fix_filename(). @@ -60,7 +62,7 @@ class LLVMSymbolizer(Symbolizer): return None cmd = [self.symbolizer_path, '--use-symbol-table=true', - '--demangle=false', + '--demangle=%s' % demangle, '--functions=true', '--inlining=true'] if DEBUG: @@ -111,7 +113,10 @@ class Addr2LineSymbolizer(Symbolizer): self.pipe = self.open_addr2line() def open_addr2line(self): - cmd = ['addr2line', '-f', '-e', self.binary] + cmd = ['addr2line', '-f'] + if demangle: + cmd += ['--demangle'] + cmd += ['-e', self.binary] if DEBUG: print ' '.join(cmd) return subprocess.Popen(cmd, @@ -352,5 +357,9 @@ class SymbolizationLoop(object): if __name__ == '__main__': + opts, args = getopt.getopt(sys.argv[1:], "d", ["demangle"]) + for o, a in opts: + if o in ("-d", "--demangle"): + demangle = True; loop = SymbolizationLoop() loop.process_stdin() diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt index 272950bc5450..80d6f5d67aad 100644 --- a/lib/asan/tests/CMakeLists.txt +++ b/lib/asan/tests/CMakeLists.txt @@ -15,6 +15,13 @@ include(CompilerRTCompile) include_directories(..) include_directories(../..) +# Use zero-based shadow on Android. +if(ANDROID) + set(ASAN_TESTS_USE_ZERO_BASE_SHADOW TRUE) +else() + set(ASAN_TESTS_USE_ZERO_BASE_SHADOW FALSE) +endif() + set(ASAN_UNITTEST_HEADERS asan_mac_test.h asan_test_config.h @@ -25,6 +32,7 @@ set(ASAN_UNITTEST_COMMON_CFLAGS -I${COMPILER_RT_SOURCE_DIR}/include -I${COMPILER_RT_SOURCE_DIR}/lib -I${COMPILER_RT_SOURCE_DIR}/lib/asan + -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/tests -Wall -Wno-format -Werror @@ -32,39 +40,37 @@ set(ASAN_UNITTEST_COMMON_CFLAGS -O2 ) +if(ASAN_TESTS_USE_ZERO_BASE_SHADOW) + list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -fPIE) +endif() if(SUPPORTS_NO_VARIADIC_MACROS_FLAG) list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -Wno-variadic-macros) endif() # Use -D instead of definitions to please custom compile command. +list(APPEND ASAN_UNITTEST_COMMON_CFLAGS + -DASAN_HAS_BLACKLIST=1 + -DASAN_HAS_EXCEPTIONS=1 + -DASAN_UAR=0) if(ANDROID) list(APPEND ASAN_UNITTEST_COMMON_CFLAGS - -DASAN_LOW_MEMORY=1 - -DASAN_HAS_BLACKLIST=1 - -DASAN_HAS_EXCEPTIONS=1 - -DASAN_NEEDS_SEGV=0 - -DASAN_UAR=0 - -fPIE - ) + -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 + -DASAN_NEEDS_SEGV=0) else() list(APPEND ASAN_UNITTEST_COMMON_CFLAGS - -DASAN_HAS_BLACKLIST=1 - -DASAN_HAS_EXCEPTIONS=1 - -DASAN_NEEDS_SEGV=1 - -DASAN_UAR=0 - ) + -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=1 + -DASAN_NEEDS_SEGV=1) endif() set(ASAN_LINK_FLAGS) -if(ANDROID) - # On Android, we link with ASan runtime manually +if(ASAN_TESTS_USE_ZERO_BASE_SHADOW) list(APPEND ASAN_LINK_FLAGS -pie) -else() - # On other platforms, we depend on Clang driver behavior, - # passing -fsanitize=address flag. +endif() +# On Android, we link with ASan runtime manually. On other platforms we depend +# on Clang driver behavior, passing -fsanitize=address flag. +if(NOT ANDROID) list(APPEND ASAN_LINK_FLAGS -fsanitize=address) endif() - # Unit tests on Mac depend on Foundation. if(APPLE) list(APPEND ASAN_LINK_FLAGS -framework Foundation) @@ -77,13 +83,17 @@ set(ASAN_BLACKLIST_FILE "${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore") set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS ${ASAN_UNITTEST_COMMON_CFLAGS} -fsanitize=address - -mllvm "-asan-blacklist=${ASAN_BLACKLIST_FILE}" + "-fsanitize-blacklist=${ASAN_BLACKLIST_FILE}" -mllvm -asan-stack=1 -mllvm -asan-globals=1 -mllvm -asan-mapping-scale=0 # default will be used -mllvm -asan-mapping-offset-log=-1 # default will be used -mllvm -asan-use-after-return=0 ) +if(ASAN_TESTS_USE_ZERO_BASE_SHADOW) + list(APPEND ASAN_UNITTEST_INSTRUMENTED_CFLAGS + -fsanitize-address-zero-base-shadow) +endif() # Compile source for the given architecture, using compiler # options in ${ARGN}, and add it to the object list. @@ -117,6 +127,16 @@ set_target_properties(AsanUnitTests PROPERTIES FOLDER "ASan unit tests") add_custom_target(AsanBenchmarks) set_target_properties(AsanBenchmarks PROPERTIES FOLDER "Asan benchmarks") +set(ASAN_NOINST_TEST_SOURCES + asan_noinst_test.cc + asan_test_main.cc) +set(ASAN_INST_TEST_SOURCES + asan_globals_test.cc + asan_test.cc + asan_oob_test.cc + asan_mem_test.cc + asan_str_test.cc) + # Adds ASan unit tests and benchmarks for architecture. macro(add_asan_tests_for_arch arch) # Build gtest instrumented with ASan. @@ -125,20 +145,23 @@ macro(add_asan_tests_for_arch arch) ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) # Instrumented tests. set(ASAN_INST_TEST_OBJECTS) - asan_compile(ASAN_INST_TEST_OBJECTS asan_globals_test.cc ${arch} - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) - asan_compile(ASAN_INST_TEST_OBJECTS asan_test.cc ${arch} - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + foreach(src ${ASAN_INST_TEST_SOURCES}) + asan_compile(ASAN_INST_TEST_OBJECTS ${src} ${arch} + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + endforeach() + # Add Mac-specific tests. if (APPLE) - asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test.mm ${arch} + asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test.cc ${arch} + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test_helpers.mm ${arch} ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -ObjC) endif() # Uninstrumented tests. set(ASAN_NOINST_TEST_OBJECTS) - asan_compile(ASAN_NOINST_TEST_OBJECTS asan_noinst_test.cc ${arch} - ${ASAN_UNITTEST_COMMON_CFLAGS}) - asan_compile(ASAN_NOINST_TEST_OBJECTS asan_test_main.cc ${arch} - ${ASAN_UNITTEST_COMMON_CFLAGS}) + foreach(src ${ASAN_NOINST_TEST_SOURCES}) + asan_compile(ASAN_NOINST_TEST_OBJECTS ${src} ${arch} + ${ASAN_UNITTEST_COMMON_CFLAGS}) + endforeach() # Link everything together. add_asan_test(AsanUnitTests "Asan-${arch}-Test" ${arch} ${ASAN_NOINST_TEST_OBJECTS} @@ -154,19 +177,14 @@ macro(add_asan_tests_for_arch arch) endmacro() if(COMPILER_RT_CAN_EXECUTE_TESTS) - if(CAN_TARGET_x86_64) - add_asan_tests_for_arch(x86_64) - endif() - if(CAN_TARGET_i386) - add_asan_tests_for_arch(i386) - endif() + foreach(arch ${ASAN_SUPPORTED_ARCH}) + add_asan_tests_for_arch(${arch}) + endforeach() endif() if(ANDROID) # We assume that unit tests on Android are built in a build # tree with fresh Clang as a host compiler. - set(ASAN_NOINST_TEST_SOURCES asan_noinst_test.cc asan_test_main.cc) - set(ASAN_INST_TEST_SOURCES asan_globals_test.cc asan_test.cc) add_library(asan_noinst_test OBJECT ${ASAN_NOINST_TEST_SOURCES}) set_target_compile_flags(asan_noinst_test ${ASAN_UNITTEST_COMMON_CFLAGS}) add_library(asan_inst_test OBJECT @@ -177,9 +195,8 @@ if(ANDROID) $<TARGET_OBJECTS:asan_inst_test> ) # Setup correct output directory and link flags. - get_unittest_directory(OUTPUT_DIR) set_target_properties(AsanTest PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR}) + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) set_target_link_flags(AsanTest ${ASAN_LINK_FLAGS}) target_link_libraries(AsanTest clang_rt.asan-arm-android) # Add unit test to test suite. diff --git a/lib/asan/tests/asan_globals_test.cc b/lib/asan/tests/asan_globals_test.cc index dc2e9bbb0530..5042ef07378d 100644 --- a/lib/asan/tests/asan_globals_test.cc +++ b/lib/asan/tests/asan_globals_test.cc @@ -11,8 +11,29 @@ // // Some globals in a separate file. //===----------------------------------------------------------------------===// +#include "asan_test_utils.h" + +char glob1[1]; +char glob2[2]; +char glob3[3]; +char glob4[4]; +char glob5[5]; +char glob6[6]; +char glob7[7]; +char glob8[8]; +char glob9[9]; +char glob10[10]; +char glob11[11]; +char glob12[12]; +char glob13[13]; +char glob14[14]; +char glob15[15]; +char glob16[16]; +char glob17[17]; +char glob1000[1000]; +char glob10000[10000]; +char glob100000[100000]; -extern char glob5[5]; static char static10[10]; int GlobalsTest(int zero) { diff --git a/lib/asan/tests/asan_mac_test.cc b/lib/asan/tests/asan_mac_test.cc new file mode 100644 index 000000000000..cabdfd711ea2 --- /dev/null +++ b/lib/asan/tests/asan_mac_test.cc @@ -0,0 +1,236 @@ +//===-- asan_test_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. +// +//===----------------------------------------------------------------------===// + +#include "asan_test_utils.h" + +#include "asan_mac_test.h" + +#include <malloc/malloc.h> +#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_* +#include <CoreFoundation/CFString.h> + +TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) { + EXPECT_DEATH( + CFAllocatorDefaultDoubleFree(NULL), + "attempting double-free"); +} + +void CFAllocator_DoubleFreeOnPthread() { + pthread_t child; + PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL); + PTHREAD_JOIN(child, NULL); // Shouldn't be reached. +} + +TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) { + EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free"); +} + +namespace { + +void *GLOB; + +void *CFAllocatorAllocateToGlob(void *unused) { + GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0); + return NULL; +} + +void *CFAllocatorDeallocateFromGlob(void *unused) { + char *p = (char*)GLOB; + p[100] = 'A'; // ASan should report an error here. + CFAllocatorDeallocate(NULL, GLOB); + return NULL; +} + +void CFAllocator_PassMemoryToAnotherThread() { + pthread_t th1, th2; + PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL); + PTHREAD_JOIN(th1, NULL); + PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL); + PTHREAD_JOIN(th2, NULL); +} + +TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) { + EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(), + "heap-buffer-overflow"); +} + +} // namespace + +// TODO(glider): figure out whether we still need these tests. Is it correct +// to intercept the non-default CFAllocators? +TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) { + EXPECT_DEATH( + CFAllocatorSystemDefaultDoubleFree(), + "attempting double-free"); +} + +// We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan. +TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) { + EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free"); +} + +TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) { + EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free"); +} + +// For libdispatch tests below we check that ASan got to the shadow byte +// legend, i.e. managed to print the thread stacks (this almost certainly +// means that the libdispatch task creation has been intercepted correctly). +TEST(AddressSanitizerMac, GCDDispatchAsync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDDispatchSync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend"); +} + + +TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDDispatchAfter) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDSourceEvent) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDSourceCancel) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDGroupAsync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend"); +} + +void *MallocIntrospectionLockWorker(void *_) { + const int kNumPointers = 100; + int i; + void *pointers[kNumPointers]; + for (i = 0; i < kNumPointers; i++) { + pointers[i] = malloc(i + 1); + } + for (i = 0; i < kNumPointers; i++) { + free(pointers[i]); + } + + return NULL; +} + +void *MallocIntrospectionLockForker(void *_) { + pid_t result = fork(); + if (result == -1) { + perror("fork"); + } + assert(result != -1); + if (result == 0) { + // Call malloc in the child process to make sure we won't deadlock. + void *ptr = malloc(42); + free(ptr); + exit(0); + } else { + // Return in the parent process. + return NULL; + } +} + +TEST(AddressSanitizerMac, MallocIntrospectionLock) { + // Incorrect implementation of force_lock and force_unlock in our malloc zone + // will cause forked processes to deadlock. + // TODO(glider): need to detect that none of the child processes deadlocked. + const int kNumWorkers = 5, kNumIterations = 100; + int i, iter; + for (iter = 0; iter < kNumIterations; iter++) { + pthread_t workers[kNumWorkers], forker; + for (i = 0; i < kNumWorkers; i++) { + PTHREAD_CREATE(&workers[i], 0, MallocIntrospectionLockWorker, 0); + } + PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0); + for (i = 0; i < kNumWorkers; i++) { + PTHREAD_JOIN(workers[i], 0); + } + PTHREAD_JOIN(forker, 0); + } +} + +void *TSDAllocWorker(void *test_key) { + if (test_key) { + void *mem = malloc(10); + pthread_setspecific(*(pthread_key_t*)test_key, mem); + } + return NULL; +} + +TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) { + pthread_t th; + pthread_key_t test_key; + pthread_key_create(&test_key, CallFreeOnWorkqueue); + PTHREAD_CREATE(&th, NULL, TSDAllocWorker, &test_key); + PTHREAD_JOIN(th, NULL); + pthread_key_delete(test_key); +} + +// Test that CFStringCreateCopy does not copy constant strings. +TEST(AddressSanitizerMac, CFStringCreateCopy) { + CFStringRef str = CFSTR("Hello world!\n"); + CFStringRef str2 = CFStringCreateCopy(0, str); + EXPECT_EQ(str, str2); +} + +TEST(AddressSanitizerMac, NSObjectOOB) { + // Make sure that our allocators are used for NSObjects. + EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow"); +} + +// Make sure that correct pointer is passed to free() when deallocating a +// NSURL object. +// See http://code.google.com/p/address-sanitizer/issues/detail?id=70. +TEST(AddressSanitizerMac, NSURLDeallocation) { + TestNSURLDeallocation(); +} + +// See http://code.google.com/p/address-sanitizer/issues/detail?id=109. +TEST(AddressSanitizerMac, Mstats) { + malloc_statistics_t stats1, stats2; + malloc_zone_statistics(/*all zones*/NULL, &stats1); + const size_t kMallocSize = 100000; + void *alloc = Ident(malloc(kMallocSize)); + malloc_zone_statistics(/*all zones*/NULL, &stats2); + EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use); + EXPECT_GE(stats2.size_in_use - stats1.size_in_use, kMallocSize); + free(alloc); + // Even the default OSX allocator may not change the stats after free(). +} + diff --git a/lib/asan/tests/asan_mac_test.mm b/lib/asan/tests/asan_mac_test_helpers.mm index 4cbd2bb247fd..4cbd2bb247fd 100644 --- a/lib/asan/tests/asan_mac_test.mm +++ b/lib/asan/tests/asan_mac_test_helpers.mm diff --git a/lib/asan/tests/asan_mem_test.cc b/lib/asan/tests/asan_mem_test.cc new file mode 100644 index 000000000000..60f5cd4cf760 --- /dev/null +++ b/lib/asan/tests/asan_mem_test.cc @@ -0,0 +1,240 @@ +//===-- asan_mem_test.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. +// +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" + +template<typename T> +void MemSetOOBTestTemplate(size_t length) { + if (length == 0) return; + size_t size = Ident(sizeof(T) * length); + T *array = Ident((T*)malloc(size)); + int element = Ident(42); + int zero = Ident(0); + void *(*MEMSET)(void *s, int c, size_t n) = Ident(memset); + // memset interval inside array + MEMSET(array, element, size); + MEMSET(array, element, size - 1); + MEMSET(array + length - 1, element, sizeof(T)); + MEMSET(array, element, 1); + + // memset 0 bytes + MEMSET(array - 10, element, zero); + MEMSET(array - 1, element, zero); + MEMSET(array, element, zero); + MEMSET(array + length, 0, zero); + MEMSET(array + length + 1, 0, zero); + + // try to memset bytes to the right of array + EXPECT_DEATH(MEMSET(array, 0, size + 1), + RightOOBWriteMessage(0)); + EXPECT_DEATH(MEMSET((char*)(array + length) - 1, element, 6), + RightOOBWriteMessage(0)); + EXPECT_DEATH(MEMSET(array + 1, element, size + sizeof(T)), + RightOOBWriteMessage(0)); + // whole interval is to the right + EXPECT_DEATH(MEMSET(array + length + 1, 0, 10), + RightOOBWriteMessage(sizeof(T))); + + // try to memset bytes to the left of array + EXPECT_DEATH(MEMSET((char*)array - 1, element, size), + LeftOOBWriteMessage(1)); + EXPECT_DEATH(MEMSET((char*)array - 5, 0, 6), + LeftOOBWriteMessage(5)); + if (length >= 100) { + // Large OOB, we find it only if the redzone is large enough. + EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)), + LeftOOBWriteMessage(5 * sizeof(T))); + } + // whole interval is to the left + EXPECT_DEATH(MEMSET(array - 2, 0, sizeof(T)), + LeftOOBWriteMessage(2 * sizeof(T))); + + // try to memset bytes both to the left & to the right + EXPECT_DEATH(MEMSET((char*)array - 2, element, size + 4), + LeftOOBWriteMessage(2)); + + free(array); +} + +TEST(AddressSanitizer, MemSetOOBTest) { + MemSetOOBTestTemplate<char>(100); + MemSetOOBTestTemplate<int>(5); + MemSetOOBTestTemplate<double>(256); + // We can test arrays of structres/classes here, but what for? +} + +// Try to allocate two arrays of 'size' bytes that are near each other. +// Strictly speaking we are not guaranteed to find such two pointers, +// but given the structure of asan's allocator we will. +static bool AllocateTwoAdjacentArrays(char **x1, char **x2, size_t size) { + vector<char *> v; + bool res = false; + for (size_t i = 0; i < 1000U && !res; i++) { + v.push_back(new char[size]); + if (i == 0) continue; + sort(v.begin(), v.end()); + for (size_t j = 1; j < v.size(); j++) { + assert(v[j] > v[j-1]); + if ((size_t)(v[j] - v[j-1]) < size * 2) { + *x2 = v[j]; + *x1 = v[j-1]; + res = true; + break; + } + } + } + + for (size_t i = 0; i < v.size(); i++) { + if (res && v[i] == *x1) continue; + if (res && v[i] == *x2) continue; + delete [] v[i]; + } + return res; +} + +TEST(AddressSanitizer, LargeOOBInMemset) { + for (size_t size = 200; size < 100000; size += size / 2) { + char *x1, *x2; + if (!Ident(AllocateTwoAdjacentArrays)(&x1, &x2, size)) + continue; + // fprintf(stderr, " large oob memset: %p %p %zd\n", x1, x2, size); + // Do a memset on x1 with huge out-of-bound access that will end up in x2. + EXPECT_DEATH(Ident(memset)(x1, 0, size * 2), + "is located 0 bytes to the right"); + delete [] x1; + delete [] x2; + return; + } + assert(0 && "Did not find two adjacent malloc-ed pointers"); +} + +// Same test for memcpy and memmove functions +template <typename T, class M> +void MemTransferOOBTestTemplate(size_t length) { + if (length == 0) return; + size_t size = Ident(sizeof(T) * length); + T *src = Ident((T*)malloc(size)); + T *dest = Ident((T*)malloc(size)); + int zero = Ident(0); + + // valid transfer of bytes between arrays + M::transfer(dest, src, size); + M::transfer(dest + 1, src, size - sizeof(T)); + M::transfer(dest, src + length - 1, sizeof(T)); + M::transfer(dest, src, 1); + + // transfer zero bytes + M::transfer(dest - 1, src, 0); + M::transfer(dest + length, src, zero); + M::transfer(dest, src - 1, zero); + M::transfer(dest, src, zero); + + // try to change mem to the right of dest + EXPECT_DEATH(M::transfer(dest + 1, src, size), + RightOOBWriteMessage(0)); + EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5), + RightOOBWriteMessage(0)); + + // try to change mem to the left of dest + EXPECT_DEATH(M::transfer(dest - 2, src, size), + LeftOOBWriteMessage(2 * sizeof(T))); + EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4), + LeftOOBWriteMessage(3)); + + // try to access mem to the right of src + EXPECT_DEATH(M::transfer(dest, src + 2, size), + RightOOBReadMessage(0)); + EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6), + RightOOBReadMessage(0)); + + // try to access mem to the left of src + EXPECT_DEATH(M::transfer(dest, src - 1, size), + LeftOOBReadMessage(sizeof(T))); + EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7), + LeftOOBReadMessage(6)); + + // Generally we don't need to test cases where both accessing src and writing + // to dest address to poisoned memory. + + T *big_src = Ident((T*)malloc(size * 2)); + T *big_dest = Ident((T*)malloc(size * 2)); + // try to change mem to both sides of dest + EXPECT_DEATH(M::transfer(dest - 1, big_src, size * 2), + LeftOOBWriteMessage(sizeof(T))); + // try to access mem to both sides of src + EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2), + LeftOOBReadMessage(2 * sizeof(T))); + + free(src); + free(dest); + free(big_src); + free(big_dest); +} + +class MemCpyWrapper { + public: + static void* transfer(void *to, const void *from, size_t size) { + return Ident(memcpy)(to, from, size); + } +}; + +TEST(AddressSanitizer, MemCpyOOBTest) { + MemTransferOOBTestTemplate<char, MemCpyWrapper>(100); + MemTransferOOBTestTemplate<int, MemCpyWrapper>(1024); +} + +class MemMoveWrapper { + public: + static void* transfer(void *to, const void *from, size_t size) { + return Ident(memmove)(to, from, size); + } +}; + +TEST(AddressSanitizer, MemMoveOOBTest) { + MemTransferOOBTestTemplate<char, MemMoveWrapper>(100); + MemTransferOOBTestTemplate<int, MemMoveWrapper>(1024); +} + + +TEST(AddressSanitizer, MemCmpOOBTest) { + size_t size = Ident(100); + char *s1 = MallocAndMemsetString(size); + char *s2 = MallocAndMemsetString(size); + // Normal memcmp calls. + Ident(memcmp(s1, s2, size)); + Ident(memcmp(s1 + size - 1, s2 + size - 1, 1)); + Ident(memcmp(s1 - 1, s2 - 1, 0)); + // One of arguments points to not allocated memory. + EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBReadMessage(0)); + // Hit unallocated memory and die. + EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0)); + // Zero bytes are not terminators and don't prevent from OOB. + s1[size - 1] = '\0'; + s2[size - 1] = '\0'; + EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0)); + + // Even if the buffers differ in the first byte, we still assume that + // memcmp may access the whole buffer and thus reporting the overflow here: + s1[0] = 1; + s2[0] = 123; + EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0)); + + free(s1); + free(s2); +} + + + diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc index 576312bf319f..54fdd1979b8e 100644 --- a/lib/asan/tests/asan_noinst_test.cc +++ b/lib/asan/tests/asan_noinst_test.cc @@ -15,9 +15,7 @@ #include "asan_allocator.h" #include "asan_internal.h" #include "asan_mapping.h" -#include "asan_stack.h" #include "asan_test_utils.h" -#include "sanitizer/asan_interface.h" #include <assert.h> #include <stdio.h> @@ -25,6 +23,7 @@ #include <string.h> // for memset() #include <algorithm> #include <vector> +#include <limits> TEST(AddressSanitizer, InternalSimpleDeathTest) { @@ -33,17 +32,17 @@ TEST(AddressSanitizer, InternalSimpleDeathTest) { static void MallocStress(size_t n) { u32 seed = my_rand(); - __asan::StackTrace stack1; + StackTrace stack1; stack1.trace[0] = 0xa123; stack1.trace[1] = 0xa456; stack1.size = 2; - __asan::StackTrace stack2; + StackTrace stack2; stack2.trace[0] = 0xb123; stack2.trace[1] = 0xb456; stack2.size = 2; - __asan::StackTrace stack3; + StackTrace stack3; stack3.trace[0] = 0xc123; stack3.trace[1] = 0xc456; stack3.size = 2; @@ -79,11 +78,20 @@ static void MallocStress(size_t n) { TEST(AddressSanitizer, NoInstMallocTest) { -#ifdef __arm__ - MallocStress(300000); -#else - MallocStress(1000000); -#endif + MallocStress(ASAN_LOW_MEMORY ? 300000 : 1000000); +} + +TEST(AddressSanitizer, ThreadedMallocStressTest) { + const int kNumThreads = 4; + const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000; + pthread_t t[kNumThreads]; + for (int i = 0; i < kNumThreads; i++) { + PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))MallocStress, + (void*)kNumIterations); + } + for (int i = 0; i < kNumThreads; i++) { + PTHREAD_JOIN(t[i], 0); + } } static void PrintShadow(const char *tag, uptr ptr, size_t size) { @@ -207,16 +215,16 @@ void CompressStackTraceTest(size_t n_iter) { for (size_t iter = 0; iter < n_iter; iter++) { std::random_shuffle(pc_array, pc_array + kNumPcs); - __asan::StackTrace stack0, stack1; + StackTrace stack0, stack1; stack0.CopyFrom(pc_array, kNumPcs); stack0.size = std::max((size_t)1, (size_t)(my_rand_r(&seed) % stack0.size)); size_t compress_size = std::max((size_t)2, (size_t)my_rand_r(&seed) % (2 * kNumPcs)); size_t n_frames = - __asan::StackTrace::CompressStack(&stack0, compressed, compress_size); + StackTrace::CompressStack(&stack0, compressed, compress_size); Ident(n_frames); assert(n_frames <= stack0.size); - __asan::StackTrace::UncompressStack(&stack1, compressed, compress_size); + StackTrace::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]); @@ -233,13 +241,13 @@ void CompressStackTraceBenchmark(size_t n_iter) { u32 compressed[2 * kNumPcs]; std::random_shuffle(pc_array, pc_array + kNumPcs); - __asan::StackTrace stack0; + StackTrace 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::StackTrace::CompressStack(&stack0, compressed, compress_size); + StackTrace::CompressStack(&stack0, compressed, compress_size); Ident(n_frames); } } @@ -249,11 +257,11 @@ TEST(AddressSanitizer, CompressStackTraceBenchmark) { } TEST(AddressSanitizer, QuarantineTest) { - __asan::StackTrace stack; + StackTrace stack; stack.trace[0] = 0x890; stack.size = 1; - const int size = 32; + const int size = 1024; void *p = __asan::asan_malloc(size, &stack); __asan::asan_free(p, &stack, __asan::FROM_MALLOC); size_t i; @@ -263,15 +271,14 @@ TEST(AddressSanitizer, QuarantineTest) { __asan::asan_free(p1, &stack, __asan::FROM_MALLOC); if (p1 == p) break; } - // fprintf(stderr, "i=%ld\n", i); - EXPECT_GE(i, 100000U); + EXPECT_GE(i, 10000U); EXPECT_LT(i, max_i); } void *ThreadedQuarantineTestWorker(void *unused) { (void)unused; u32 seed = my_rand(); - __asan::StackTrace stack; + StackTrace stack; stack.trace[0] = 0x890; stack.size = 1; @@ -298,7 +305,7 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) { void *ThreadedOneSizeMallocStress(void *unused) { (void)unused; - __asan::StackTrace stack; + StackTrace stack; stack.trace[0] = 0x890; stack.size = 1; const size_t kNumMallocs = 1000; @@ -326,11 +333,13 @@ TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) { } TEST(AddressSanitizer, MemsetWildAddressTest) { + using __asan::kHighMemEnd; typedef void*(*memset_p)(void*, int, size_t); // Prevent inlining of memset(). volatile memset_p libc_memset = (memset_p)memset; EXPECT_DEATH(libc_memset((void*)(kLowShadowBeg + 200), 0, 100), - "unknown-crash.*low shadow"); + (kLowShadowEnd == 0) ? "unknown-crash.*shadow gap" + : "unknown-crash.*low shadow"); EXPECT_DEATH(libc_memset((void*)(kShadowGapBeg + 200), 0, 100), "unknown-crash.*shadow gap"); EXPECT_DEATH(libc_memset((void*)(kHighShadowBeg + 200), 0, 100), @@ -338,11 +347,7 @@ TEST(AddressSanitizer, MemsetWildAddressTest) { } TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) { -#if ASAN_ALLOCATOR_VERSION == 1 - EXPECT_EQ(1U, __asan_get_estimated_allocated_size(0)); -#elif ASAN_ALLOCATOR_VERSION == 2 EXPECT_EQ(0U, __asan_get_estimated_allocated_size(0)); -#endif 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])); @@ -416,44 +421,12 @@ static void DoDoubleFree() { delete Ident(x); } -#if ASAN_ALLOCATOR_VERSION == 1 -// 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 - 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"); -} -#elif ASAN_ALLOCATOR_VERSION == 2 TEST(AddressSanitizerInterface, GetHeapSizeTest) { // asan_allocator2 does not keep huge chunks in free list, but unmaps them. // The chunk should be greater than the quarantine size, // otherwise it will be stuck in quarantine instead of being unmaped. - static const size_t kLargeMallocSize = 1 << 29; // 512M + static const size_t kLargeMallocSize = (1 << 28) + 1; // 256M + free(Ident(malloc(kLargeMallocSize))); // Drain quarantine. uptr old_heap_size = __asan_get_heap_size(); for (int i = 0; i < 3; i++) { // fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); @@ -461,55 +434,6 @@ TEST(AddressSanitizerInterface, GetHeapSizeTest) { EXPECT_EQ(old_heap_size, __asan_get_heap_size()); } } -#endif - -// Note: use ASSERT_* instead of EXPECT_* here. -static void DoLargeMallocForGetFreeBytesTestAndDie() { -#if ASAN_ALLOCATOR_VERSION == 1 - // asan_allocator2 does not keep large chunks in free_lists, so this test - // will not work. - size_t old_free_bytes, new_free_bytes; - static const size_t kLargeMallocSize = 1 << 29; // 512M - // 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); -#endif // ASAN_ALLOCATOR_VERSION - // Test passed. - DoDoubleFree(); -} - -TEST(AddressSanitizerInterface, GetFreeBytesTest) { -#if ASAN_ALLOCATOR_VERSION == 1 - // 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. Do this only on systems where there - // is enough memory for such assumptions. - if (SANITIZER_WORDSIZE == 64 && !ASAN_LOW_MEMORY) { - 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; - 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; - } - for (i = 0; i < kNumOfChunks; i++) - free(chunks[i]); - } -#endif - EXPECT_DEATH(DoLargeMallocForGetFreeBytesTestAndDie(), "double-free"); -} static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357}; static const size_t kManyThreadsIterations = 250; @@ -631,6 +555,53 @@ TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { free(vec); } +TEST(AddressSanitizerInterface, GlobalRedzones) { + GOOD_ACCESS(glob1, 1 - 1); + GOOD_ACCESS(glob2, 2 - 1); + GOOD_ACCESS(glob3, 3 - 1); + GOOD_ACCESS(glob4, 4 - 1); + GOOD_ACCESS(glob5, 5 - 1); + GOOD_ACCESS(glob6, 6 - 1); + GOOD_ACCESS(glob7, 7 - 1); + GOOD_ACCESS(glob8, 8 - 1); + GOOD_ACCESS(glob9, 9 - 1); + GOOD_ACCESS(glob10, 10 - 1); + GOOD_ACCESS(glob11, 11 - 1); + GOOD_ACCESS(glob12, 12 - 1); + GOOD_ACCESS(glob13, 13 - 1); + GOOD_ACCESS(glob14, 14 - 1); + GOOD_ACCESS(glob15, 15 - 1); + GOOD_ACCESS(glob16, 16 - 1); + GOOD_ACCESS(glob17, 17 - 1); + GOOD_ACCESS(glob1000, 1000 - 1); + GOOD_ACCESS(glob10000, 10000 - 1); + GOOD_ACCESS(glob100000, 100000 - 1); + + BAD_ACCESS(glob1, 1); + BAD_ACCESS(glob2, 2); + BAD_ACCESS(glob3, 3); + BAD_ACCESS(glob4, 4); + BAD_ACCESS(glob5, 5); + BAD_ACCESS(glob6, 6); + BAD_ACCESS(glob7, 7); + BAD_ACCESS(glob8, 8); + BAD_ACCESS(glob9, 9); + BAD_ACCESS(glob10, 10); + BAD_ACCESS(glob11, 11); + BAD_ACCESS(glob12, 12); + BAD_ACCESS(glob13, 13); + BAD_ACCESS(glob14, 14); + BAD_ACCESS(glob15, 15); + BAD_ACCESS(glob16, 16); + BAD_ACCESS(glob17, 17); + BAD_ACCESS(glob1000, 1000); + BAD_ACCESS(glob1000, 1100); // Redzone is at least 101 bytes. + BAD_ACCESS(glob10000, 10000); + BAD_ACCESS(glob10000, 11000); // Redzone is at least 1001 bytes. + BAD_ACCESS(glob100000, 100000); + BAD_ACCESS(glob100000, 110000); // Redzone is at least 10001 bytes. +} + // 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) { @@ -715,7 +686,7 @@ TEST(AddressSanitizerInterface, PoisonedRegion) { // 10.50% [.] __sanitizer::mem_is_zero // I.e. mem_is_zero should consume ~ SHADOW_GRANULARITY less CPU cycles // than memset itself. -TEST(AddressSanitizerInterface, DISABLED_Stress_memset) { +TEST(AddressSanitizerInterface, DISABLED_StressLargeMemset) { size_t size = 1 << 20; char *x = new char[size]; for (int i = 0; i < 100000; i++) @@ -723,6 +694,15 @@ TEST(AddressSanitizerInterface, DISABLED_Stress_memset) { delete [] x; } +// Same here, but we run memset with small sizes. +TEST(AddressSanitizerInterface, DISABLED_StressSmallMemset) { + size_t size = 32; + char *x = new char[size]; + for (int i = 0; i < 100000000; i++) + Ident(memset)(x, 0, size); + delete [] x; +} + static const char *kInvalidPoisonMessage = "invalid-poison-memory-range"; static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range"; @@ -761,12 +741,7 @@ TEST(AddressSanitizerInterface, SetErrorReportCallbackTest) { TEST(AddressSanitizerInterface, GetOwnershipStressTest) { std::vector<char *> pointers; std::vector<size_t> sizes; -#if ASAN_ALLOCATOR_VERSION == 1 - const size_t kNumMallocs = - (SANITIZER_WORDSIZE <= 32 || ASAN_LOW_MEMORY) ? 1 << 10 : 1 << 14; -#elif ASAN_ALLOCATOR_VERSION == 2 // too slow with asan_allocator2. :( const size_t kNumMallocs = 1 << 9; -#endif for (size_t i = 0; i < kNumMallocs; i++) { size_t size = i * 100 + 1; pointers.push_back((char*)malloc(size)); @@ -782,3 +757,38 @@ TEST(AddressSanitizerInterface, GetOwnershipStressTest) { for (size_t i = 0, n = pointers.size(); i < n; i++) free(pointers[i]); } + +TEST(AddressSanitizerInterface, CallocOverflow) { + size_t kArraySize = 4096; + volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + void *p = calloc(kArraySize, kArraySize2); // Should return 0. + EXPECT_EQ(0L, Ident(p)); +} + +TEST(AddressSanitizerInterface, CallocOverflow2) { +#if SANITIZER_WORDSIZE == 32 + size_t kArraySize = 112; + volatile size_t kArraySize2 = 43878406; + void *p = calloc(kArraySize, kArraySize2); // Should return 0. + EXPECT_EQ(0L, Ident(p)); +#endif +} + +TEST(AddressSanitizerInterface, CallocReturnsZeroMem) { + size_t sizes[] = {16, 1000, 10000, 100000, 2100000}; + for (size_t s = 0; s < ARRAY_SIZE(sizes); s++) { + size_t size = sizes[s]; + for (size_t iter = 0; iter < 5; iter++) { + char *x = Ident((char*)calloc(1, size)); + EXPECT_EQ(x[0], 0); + EXPECT_EQ(x[size - 1], 0); + EXPECT_EQ(x[size / 2], 0); + EXPECT_EQ(x[size / 3], 0); + EXPECT_EQ(x[size / 4], 0); + memset(x, 0x42, size); + free(Ident(x)); + free(Ident(malloc(Ident(1 << 27)))); // Try to drain the quarantine. + } + } +} diff --git a/lib/asan/tests/asan_oob_test.cc b/lib/asan/tests/asan_oob_test.cc new file mode 100644 index 000000000000..f8343f19cfcb --- /dev/null +++ b/lib/asan/tests/asan_oob_test.cc @@ -0,0 +1,126 @@ +//===-- asan_oob_test.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. +// +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" + +NOINLINE void asan_write_sized_aligned(uint8_t *p, size_t size) { + EXPECT_EQ(0U, ((uintptr_t)p % size)); + if (size == 1) asan_write((uint8_t*)p); + else if (size == 2) asan_write((uint16_t*)p); + else if (size == 4) asan_write((uint32_t*)p); + else if (size == 8) asan_write((uint64_t*)p); +} + +template<typename T> +NOINLINE void oob_test(int size, int off) { + char *p = (char*)malloc_aaa(size); + // fprintf(stderr, "writing %d byte(s) into [%p,%p) with offset %d\n", + // sizeof(T), p, p + size, off); + asan_write((T*)(p + off)); + free_aaa(p); +} + +template<typename T> +void OOBTest() { + char expected_str[100]; + for (int size = sizeof(T); size < 20; size += 5) { + for (int i = -5; i < 0; i++) { + const char *str = + "is located.*%d byte.*to the left"; + sprintf(expected_str, str, abs(i)); + EXPECT_DEATH(oob_test<T>(size, i), expected_str); + } + + for (int i = 0; i < (int)(size - sizeof(T) + 1); i++) + oob_test<T>(size, i); + + for (int i = size - sizeof(T) + 1; i <= (int)(size + 2 * sizeof(T)); i++) { + const char *str = + "is located.*%d byte.*to the right"; + int off = i >= size ? (i - size) : 0; + // we don't catch unaligned partially OOB accesses. + if (i % sizeof(T)) continue; + sprintf(expected_str, str, off); + EXPECT_DEATH(oob_test<T>(size, i), expected_str); + } + } + + EXPECT_DEATH(oob_test<T>(kLargeMalloc, -1), + "is located.*1 byte.*to the left"); + EXPECT_DEATH(oob_test<T>(kLargeMalloc, kLargeMalloc), + "is located.*0 byte.*to the right"); +} + +// TODO(glider): the following tests are EXTREMELY slow on Darwin: +// AddressSanitizer.OOB_char (125503 ms) +// AddressSanitizer.OOB_int (126890 ms) +// AddressSanitizer.OOBRightTest (315605 ms) +// AddressSanitizer.SimpleStackTest (366559 ms) + +TEST(AddressSanitizer, OOB_char) { + OOBTest<U1>(); +} + +TEST(AddressSanitizer, OOB_int) { + OOBTest<U4>(); +} + +TEST(AddressSanitizer, OOBRightTest) { + for (size_t access_size = 1; access_size <= 8; access_size *= 2) { + for (size_t alloc_size = 1; alloc_size <= 8; alloc_size++) { + for (size_t offset = 0; offset <= 8; offset += access_size) { + void *p = malloc(alloc_size); + // allocated: [p, p + alloc_size) + // accessed: [p + offset, p + offset + access_size) + uint8_t *addr = (uint8_t*)p + offset; + if (offset + access_size <= alloc_size) { + asan_write_sized_aligned(addr, access_size); + } else { + int outside_bytes = offset > alloc_size ? (offset - alloc_size) : 0; + const char *str = + "is located.%d *byte.*to the right"; + char expected_str[100]; + sprintf(expected_str, str, outside_bytes); + EXPECT_DEATH(asan_write_sized_aligned(addr, access_size), + expected_str); + } + free(p); + } + } + } +} + +TEST(AddressSanitizer, LargeOOBRightTest) { + size_t large_power_of_two = 1 << 19; + for (size_t i = 16; i <= 256; i *= 2) { + size_t size = large_power_of_two - i; + char *p = Ident(new char[size]); + EXPECT_DEATH(p[size] = 0, "is located 0 bytes to the right"); + delete [] p; + } +} + +TEST(AddressSanitizer, DISABLED_DemoOOBLeftLow) { + oob_test<U1>(10, -1); +} + +TEST(AddressSanitizer, DISABLED_DemoOOBLeftHigh) { + oob_test<U1>(kLargeMalloc, -1); +} + +TEST(AddressSanitizer, DISABLED_DemoOOBRightLow) { + oob_test<U1>(10, 10); +} + +TEST(AddressSanitizer, DISABLED_DemoOOBRightHigh) { + oob_test<U1>(kLargeMalloc, kLargeMalloc); +} diff --git a/lib/asan/tests/asan_str_test.cc b/lib/asan/tests/asan_str_test.cc new file mode 100644 index 000000000000..128fb61c25a9 --- /dev/null +++ b/lib/asan/tests/asan_str_test.cc @@ -0,0 +1,572 @@ +//=-- asan_str_test.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. +// +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" + +// Used for string functions tests +static char global_string[] = "global"; +static size_t global_string_length = 6; + +// Input to a test is a zero-terminated string str with given length +// Accesses to the bytes to the left and to the right of str +// are presumed to produce OOB errors +void StrLenOOBTestTemplate(char *str, size_t length, bool is_global) { + // Normal strlen calls + EXPECT_EQ(strlen(str), length); + if (length > 0) { + EXPECT_EQ(length - 1, strlen(str + 1)); + EXPECT_EQ(0U, strlen(str + length)); + } + // Arg of strlen is not malloced, OOB access + if (!is_global) { + // We don't insert RedZones to the left of global variables + EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBReadMessage(5)); + } + EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBReadMessage(0)); + // Overwrite terminator + str[length] = 'a'; + // String is not zero-terminated, strlen will lead to OOB access + EXPECT_DEATH(Ident(strlen(str)), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(strlen(str + length)), RightOOBReadMessage(0)); + // Restore terminator + str[length] = 0; +} +TEST(AddressSanitizer, StrLenOOBTest) { + // Check heap-allocated string + size_t length = Ident(10); + char *heap_string = Ident((char*)malloc(length + 1)); + char stack_string[10 + 1]; + break_optimization(&stack_string); + for (size_t i = |