diff options
author | Andrew Turner <andrew@FreeBSD.org> | 2013-01-18 20:06:45 +0000 |
---|---|---|
committer | Andrew Turner <andrew@FreeBSD.org> | 2013-01-18 20:06:45 +0000 |
commit | 58aabf08b77d221489f10e274812ec60917c21a8 (patch) | |
tree | b946f82269be87d83f086167c762c362e734c5bb | |
parent | 37dfff057418e02f8e5322da12684dd927e3d881 (diff) | |
download | src-vendor/compiler-rt/compiler-rt-r172839.tar.gz src-vendor/compiler-rt/compiler-rt-r172839.zip |
Import compiler-rt r172839.vendor/compiler-rt/compiler-rt-r172839
437 files changed, 25435 insertions, 6095 deletions
diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 000000000000..413b70b05f7b --- /dev/null +++ b/.arcconfig @@ -0,0 +1,4 @@ +{ + "project_id" : "compiler-rt", + "conduit_uri" : "http://llvm-reviews.chandlerc.com/" +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 97835a1e945e..04d6e9763bf8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,49 +15,157 @@ include(LLVMParseArguments) # runtime libraries. cmake_minimum_required(VERSION 2.8.8) -# 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. +# Add path for custom modules +set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" + ) +include(AddCompilerRT) + +set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) # 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. -if(CMAKE_SIZEOF_VOID_P EQUAL 4) - set(TARGET_X86_64_CFLAGS "-m64") - set(TARGET_I386_CFLAGS "") +if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR LLVM_BUILD_32_BITS) + set(TARGET_64_BIT_CFLAGS "-m64") + set(TARGET_32_BIT_CFLAGS "") else() if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8) message(FATAL_ERROR "Please use a sane architecture with 4 or 8 byte pointers.") endif() - set(TARGET_X86_64_CFLAGS "") - set(TARGET_I386_CFLAGS "-m32") + set(TARGET_64_BIT_CFLAGS "") + 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) + +function(get_target_flags_for_arch arch out_var) + list(FIND COMPILER_RT_SUPPORTED_ARCH ${arch} ARCH_INDEX) + if(ARCH_INDEX EQUAL -1) + message(FATAL_ERROR "Unsupported architecture: ${arch}") + else() + set(${out_var} ${TARGET_${arch}_CFLAGS} PARENT_SCOPE) + endif() +endfunction() + # Try to compile a very simple source file to ensure we can target the given # 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}") +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}") - -# Because compiler-rt spends a lot of time setting up custom compile flags, -# define a handy helper function for it. The compile flags setting in CMake -# has serious issues that make its syntax challenging at best. -function(set_target_compile_flags target) - foreach(arg ${ARGN}) - set(argstring "${argstring} ${arg}") +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}") + +# 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) +else() + set(COMPILER_RT_CAN_EXECUTE_TESTS FALSE) +endif() + +function(filter_available_targets out_var) + set(archs) + foreach(arch ${ARGN}) + list(FIND COMPILER_RT_SUPPORTED_ARCH ${arch} ARCH_INDEX) + if(NOT (ARCH_INDEX EQUAL -1) AND CAN_TARGET_${arch}) + list(APPEND archs ${arch}) + endif() endforeach() - set_property(TARGET ${target} PROPERTY COMPILE_FLAGS "${argstring}") + set(${out_var} ${archs} PARENT_SCOPE) endfunction() +# Provide some common commmandline flags for Sanitizer runtimes. +set(SANITIZER_COMMON_CFLAGS + -fPIC + -fno-builtin + -fno-exceptions + -fomit-frame-pointer + -funwind-tables + -O3 + ) +if(NOT WIN32) + list(APPEND SANITIZER_COMMON_CFLAGS -fvisibility=hidden) +endif() +# Build sanitizer runtimes with debug info. +check_cxx_compiler_flag(-gline-tables-only SUPPORTS_GLINE_TABLES_ONLY_FLAG) +if(SUPPORTS_GLINE_TABLES_ONLY_FLAG) + list(APPEND SANITIZER_COMMON_CFLAGS -gline-tables-only) +else() + list(APPEND SANITIZER_COMMON_CFLAGS -g) +endif() +# Warnings suppressions. +check_cxx_compiler_flag(-Wno-variadic-macros SUPPORTS_NO_VARIADIC_MACROS_FLAG) +if(SUPPORTS_NO_VARIADIC_MACROS_FLAG) + list(APPEND SANITIZER_COMMON_CFLAGS -Wno-variadic-macros) +endif() +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() +if(APPLE) + list(APPEND SANITIZER_COMMON_CFLAGS -mmacosx-version-min=10.5) +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) + +# Add the public header's directory to the includes for all of compiler-rt. +include_directories(include) + add_subdirectory(lib) if(LLVM_INCLUDE_TESTS) diff --git a/LICENSE.TXT b/LICENSE.TXT index f7179425605e..6aab1f694cc2 100644 --- a/LICENSE.TXT +++ b/LICENSE.TXT @@ -14,7 +14,7 @@ Full text of the relevant licenses is included below. University of Illinois/NCSA Open Source License -Copyright (c) 2009-2012 by the contributors listed in CREDITS.TXT +Copyright (c) 2009-2013 by the contributors listed in CREDITS.TXT All rights reserved. @@ -55,7 +55,7 @@ SOFTWARE. ============================================================================== -Copyright (c) 2009-2012 by the contributors listed in CREDITS.TXT +Copyright (c) 2009-2013 by the contributors listed in CREDITS.TXT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -92,7 +92,7 @@ test: %/.dir: $(Summary) " MKDIR: $*" $(Verb) $(MKDIR) $* > /dev/null - $(Verb) $(DATE) > $@ + $(Verb) echo 'Created.' > $@ # Remove directories %/.remove: @@ -117,7 +117,7 @@ $(call Set,Tmp.Configs,$($(Tmp.Key).Configs)) $(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)) # Top-Level Platform Target -$(Tmp.Name):: $(Tmp.Configs:%=$(Tmp.ObjPath)/%/libcompiler_rt.a) +$(Tmp.Name):: $(Tmp.Configs:%=$(Tmp.Name)-%) .PHONY: $(Tmp.Name) clean:: @@ -131,6 +131,15 @@ endef define PerPlatformConfig_template $(call Set,Tmp.Config,$(1)) $(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config)) +$(call Set,Tmp.SHARED_LIBRARY,$(strip \ + $(call GetCNAVar,SHARED_LIBRARY,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.SHARED_LIBRARY_SUFFIX,$(strip \ + $(call GetCNAVar,SHARED_LIBRARY_SUFFIX,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) + +# Compute the library suffix. +$(if $(call streq,1,$(Tmp.SHARED_LIBRARY)), + $(call Set,Tmp.LibrarySuffix,$(Tmp.SHARED_LIBRARY_SUFFIX)), + $(call Set,Tmp.LibrarySuffix,a)) # Compute the archs to build, depending on whether this is a universal build or # not. @@ -142,8 +151,8 @@ $(call Set,Tmp.ArchsToBuild,\ $(call VarOrDefault,$(Tmp.Key).Arch.$(Tmp.Config),$($(Tmp.Key).Arch)))) # Copy or lipo to create the per-config library. -$(call Set,Tmp.Inputs,$(Tmp.ArchsToBuild:%=$(Tmp.ObjPath)/%/libcompiler_rt.a)) -$(Tmp.ObjPath)/libcompiler_rt.a: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir +$(call Set,Tmp.Inputs,$(Tmp.ArchsToBuild:%=$(Tmp.ObjPath)/%/libcompiler_rt.$(Tmp.LibrarySuffix))) +$(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix): $(Tmp.Inputs) $(Tmp.ObjPath)/.dir $(Summary) " FINAL-ARCHIVE: $(Tmp.Name)/$(Tmp.Config): $$@" -$(Verb) $(RM) $$@ $(if $(call streq,1,$(words $(Tmp.ArchsToBuild))), \ @@ -152,7 +161,7 @@ $(Tmp.ObjPath)/libcompiler_rt.a: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir .PRECIOUS: $(Tmp.ObjPath)/.dir # Per-Config Targets -$(Tmp.Name)-$(Tmp.Config):: $(Tmp.ObjPath)/libcompiler_rt.a +$(Tmp.Name)-$(Tmp.Config):: $(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix) .PHONY: $(Tmp.Name)-$(Tmp.Config) # Per-Config-Arch Libraries @@ -172,10 +181,21 @@ $(call Set,Tmp.AR,$(strip \ $(call GetCNAVar,AR,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) $(call Set,Tmp.ARFLAGS,$(strip \ $(call GetCNAVar,ARFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.CC,$(strip \ + $(call GetCNAVar,CC,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.LDFLAGS,$(strip \ + $(call GetCNAVar,LDFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) $(call Set,Tmp.RANLIB,$(strip \ $(call GetCNAVar,RANLIB,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) $(call Set,Tmp.RANLIBFLAGS,$(strip \ $(call GetCNAVar,RANLIBFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.SHARED_LIBRARY,$(strip \ + $(call GetCNAVar,SHARED_LIBRARY,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) + +# Compute the library suffix. +$(if $(call streq,1,$(Tmp.SHARED_LIBRARY)), + $(call Set,Tmp.LibrarySuffix,$(Tmp.SHARED_LIBRARY_SUFFIX)), + $(call Set,Tmp.LibrarySuffix,a)) # Compute the object inputs for this library. $(call Set,Tmp.Inputs,\ @@ -188,10 +208,18 @@ $(Tmp.ObjPath)/libcompiler_rt.a: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir -$(Verb) $(RM) $$@ $(Verb) $(Tmp.AR) $(Tmp.ARFLAGS) $$@ $(Tmp.Inputs) $(Verb) $(Tmp.RANLIB) $(Tmp.RANLIBFLAGS) $$@ +$(Tmp.ObjPath)/libcompiler_rt.dylib: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir + $(Summary) " DYLIB: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@" + $(Verb) $(Tmp.CC) -arch $(Tmp.Arch) -dynamiclib -o $$@ \ + $(Tmp.Inputs) $(Tmp.LDFLAGS) +$(Tmp.ObjPath)/libcompiler_rt.so: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir + $(Summary) " SO: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@" + $(Verb) $(Tmp.CC) -shared -o $$@ \ + $(Tmp.Inputs) $(Tmp.LDFLAGS) .PRECIOUS: $(Tmp.ObjPath)/.dir # Per-Config-Arch Targets -$(Tmp.Name)-$(Tmp.Config)-$(Tmp.Arch):: $(Tmp.ObjPath)/libcompiler_rt.a +$(Tmp.Name)-$(Tmp.Config)-$(Tmp.Arch):: $(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix) .PHONY: $(Tmp.Name)-$(Tmp.Config)-$(Tmp.Arch) # Per-Config-Arch-SubDir Objects diff --git a/SDKs/darwin/usr/include/stdio.h b/SDKs/darwin/usr/include/stdio.h index 3b560369f326..63b10a86b632 100644 --- a/SDKs/darwin/usr/include/stdio.h +++ b/SDKs/darwin/usr/include/stdio.h @@ -17,6 +17,10 @@ #ifndef __STDIO_H__ #define __STDIO_H__ +#if defined(__cplusplus) +extern "C" { +#endif + typedef struct __sFILE FILE; typedef __SIZE_TYPE__ size_t; @@ -51,11 +55,30 @@ typedef __SIZE_TYPE__ size_t; # define stderr __stderrp extern FILE *__stderrp; +#ifndef SEEK_SET +#define SEEK_SET 0 /* set file offset to offset */ +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 /* set file offset to current plus offset */ +#endif +#ifndef SEEK_END +#define SEEK_END 2 /* set file offset to EOF plus offset */ +#endif + int fclose(FILE *); int fflush(FILE *); -FILE *fopen(const char * restrict, const char * restrict) __asm(__FOPEN_NAME); -int fprintf(FILE * restrict, const char * restrict, ...); -size_t fwrite(const void * restrict, size_t, size_t, FILE * restrict) +FILE *fopen(const char * __restrict, const char * __restrict) __asm(__FOPEN_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) +} +#endif #endif /* __STDIO_H__ */ diff --git a/SDKs/linux/usr/include/stdio.h b/SDKs/linux/usr/include/stdio.h index ddfe75548793..7c258d2aca17 100644 --- a/SDKs/linux/usr/include/stdio.h +++ b/SDKs/linux/usr/include/stdio.h @@ -26,10 +26,17 @@ extern struct _IO_FILE *stdin; extern struct _IO_FILE *stdout; extern struct _IO_FILE *stderr; +#define SEEK_SET 0 /* set file offset to offset */ +#define SEEK_CUR 1 /* set file offset to current plus offset */ +#define SEEK_END 2 /* set file offset to EOF plus offset */ + extern int fclose(FILE *); extern int fflush(FILE *); extern FILE *fopen(const char * restrict, 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); +extern long ftell(FILE *); +extern int fseek(FILE *, long, int); #endif /* __STDIO_H__ */ diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake new file mode 100644 index 000000000000..e90253fdfa1e --- /dev/null +++ b/cmake/Modules/AddCompilerRT.cmake @@ -0,0 +1,49 @@ +include(AddLLVM) +include(LLVMParseArguments) +include(CompilerRTUtils) + +# Tries to add "object library" target for a given architecture +# with name "<name>.<arch>" if architecture can be targeted. +# add_compiler_rt_object_library(<name> <arch> +# SOURCES <source files> +# CFLAGS <compile flags>) +macro(add_compiler_rt_object_library name arch) + if(CAN_TARGET_${arch}) + parse_arguments(LIB "SOURCES;CFLAGS" "" ${ARGN}) + add_library(${name}.${arch} OBJECT ${LIB_SOURCES}) + set_target_compile_flags(${name}.${arch} + ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) + else() + message(FATAL_ERROR "Archtecture ${arch} can't be targeted") + endif() +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) +set(COMPILER_RT_GTEST_INCLUDE_CFLAGS + -DGTEST_NO_LLVM_RAW_OSTREAM=1 + -I${COMPILER_RT_GTEST_PATH}/include +) + +# Use Clang to link objects into a single executable with just-built +# Clang, using specific link flags. Make executable a part of provided +# test_suite. +# add_compiler_rt_test(<test_suite> <test_name> +# OBJECTS <object files> +# DEPS <deps (e.g. runtime libs)> +# 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} + 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() diff --git a/cmake/Modules/CompilerRTCompile.cmake b/cmake/Modules/CompilerRTCompile.cmake new file mode 100644 index 000000000000..2794cabe59c5 --- /dev/null +++ b/cmake/Modules/CompilerRTCompile.cmake @@ -0,0 +1,16 @@ +include(LLVMParseArguments) + +# Compile a source into an object file with just-built Clang using +# a provided compile flags and dependenices. +# clang_compile(<object> <source> +# CFLAGS <list of compile flags> +# DEPS <list of dependencies>) +macro(clang_compile object_file source) + parse_arguments(SOURCE "CFLAGS;DEPS" "" ${ARGN}) + get_filename_component(source_rpath ${source} REALPATH) + add_custom_command( + OUTPUT ${object_file} + COMMAND clang ${SOURCE_CFLAGS} -c -o "${object_file}" ${source_rpath} + MAIN_DEPENDENCY ${source} + DEPENDS clang ${SOURCE_DEPS}) +endmacro() diff --git a/cmake/Modules/CompilerRTLink.cmake b/cmake/Modules/CompilerRTLink.cmake new file mode 100644 index 000000000000..85030a725e19 --- /dev/null +++ b/cmake/Modules/CompilerRTLink.cmake @@ -0,0 +1,14 @@ +include(LLVMParseArguments) + +# Link a shared library with just-built Clang. +# clang_link_shared(<output.so> +# OBJECTS <list of input objects> +# LINKFLAGS <list of link flags> +# DEPS <list of dependencies>) +macro(clang_link_shared so_file) + parse_arguments(SOURCE "OBJECTS;LINKFLAGS;DEPS" "" ${ARGN}) + add_custom_command( + OUTPUT ${so_file} + COMMAND clang -o "${so_file}" -shared ${SOURCE_LINKFLAGS} ${SOURCE_OBJECTS} + DEPENDS clang ${SOURCE_DEPS}) +endmacro() diff --git a/cmake/Modules/CompilerRTUtils.cmake b/cmake/Modules/CompilerRTUtils.cmake new file mode 100644 index 000000000000..50f068091e64 --- /dev/null +++ b/cmake/Modules/CompilerRTUtils.cmake @@ -0,0 +1,17 @@ +# Because compiler-rt spends a lot of time setting up custom compile flags, +# define a handy helper function for it. The compile flags setting in CMake +# has serious issues that make its syntax challenging at best. +function(set_target_compile_flags target) + foreach(arg ${ARGN}) + set(argstring "${argstring} ${arg}") + endforeach() + set_property(TARGET ${target} PROPERTY COMPILE_FLAGS "${argstring}") +endfunction() + +function(set_target_link_flags target) + foreach(arg ${ARGN}) + set(argstring "${argstring} ${arg}") + endforeach() + set_property(TARGET ${target} PROPERTY LINK_FLAGS "${argstring}") +endfunction() + diff --git a/lib/asan/asan_interface.h b/include/sanitizer/asan_interface.h index c625a6217c0c..6afc3800f4e7 100644 --- a/lib/asan/asan_interface.h +++ b/include/sanitizer/asan_interface.h @@ -1,4 +1,4 @@ -//===-- asan_interface.h ----------------------------------------*- C++ -*-===// +//===-- sanitizer/asan_interface.h ------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -12,10 +12,11 @@ // This header can be included by the instrumented program to fetch // data (mostly allocator statistics) from ASan runtime library. //===----------------------------------------------------------------------===// -#ifndef ASAN_INTERFACE_H -#define ASAN_INTERFACE_H +#ifndef SANITIZER_ASAN_INTERFACE_H +#define SANITIZER_ASAN_INTERFACE_H + +#include <sanitizer/common_interface_defs.h> -#include "sanitizer_common/sanitizer_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_. @@ -27,17 +28,13 @@ extern "C" { // before any instrumented code is executed and before any call to malloc. void __asan_init() SANITIZER_INTERFACE_ATTRIBUTE; - // This function should be called by the instrumented code. - // 'addr' is the address of a global variable called 'name' of 'size' bytes. - void __asan_register_global(uptr addr, uptr size, const char *name) - 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. + 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. @@ -47,6 +44,14 @@ extern "C" { 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 @@ -56,6 +61,15 @@ extern "C" { 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; + // 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 @@ -98,6 +112,15 @@ extern "C" { bool __asan_address_is_poisoned(void const volatile *addr) SANITIZER_INTERFACE_ATTRIBUTE; + // 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; + + // Print the description of addr (useful when debugging in gdb). + void __asan_describe_address(uptr addr) + SANITIZER_INTERFACE_ATTRIBUTE; + // 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. @@ -118,6 +141,21 @@ extern "C" { void __asan_set_error_report_callback(void (*callback)(const char*)) SANITIZER_INTERFACE_ATTRIBUTE; + // 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; + + // 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; + // 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 @@ -154,6 +192,21 @@ extern "C" { // Prints accumulated stats to stderr. Used for debugging. void __asan_print_accumulated_stats() SANITIZER_INTERFACE_ATTRIBUTE; -} // namespace -#endif // ASAN_INTERFACE_H + // 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; + + // 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; +} // extern "C" + +#endif // SANITIZER_ASAN_INTERFACE_H diff --git a/lib/sanitizer_common/sanitizer_interface_defs.h b/include/sanitizer/common_interface_defs.h index 2395ea505657..9d8fa5582b67 100644 --- a/lib/sanitizer_common/sanitizer_interface_defs.h +++ b/include/sanitizer/common_interface_defs.h @@ -1,4 +1,4 @@ -//===-- sanitizer_interface_defs.h -----------------------------*- C++ -*-===// +//===-- sanitizer/common_interface_defs.h -----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -12,8 +12,8 @@ // NOTE: This file may be included into user code. //===----------------------------------------------------------------------===// -#ifndef SANITIZER_INTERFACE_DEFS_H -#define SANITIZER_INTERFACE_DEFS_H +#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. @@ -30,6 +30,12 @@ # 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 #if !defined(__has_feature) # define __has_feature(x) 0 @@ -40,8 +46,21 @@ // 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; @@ -53,4 +72,21 @@ typedef signed long long s64; // NOLINT } // namespace __sanitizer -#endif // SANITIZER_INTERFACE_DEFS_H +extern "C" { + // Tell the tools to write their reports to "path.<pid>" instead of stderr. + void __sanitizer_set_report_path(const char *path) + SANITIZER_INTERFACE_ATTRIBUTE; + + // Tell the tools to write their reports to given file descriptor instead of + // stderr. + void __sanitizer_set_report_fd(int fd) + SANITIZER_INTERFACE_ATTRIBUTE; + + // 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; +} // extern "C" + +#endif // SANITIZER_COMMON_INTERFACE_DEFS_H diff --git a/include/sanitizer/msan_interface.h b/include/sanitizer/msan_interface.h new file mode 100644 index 000000000000..1a76dd60599f --- /dev/null +++ b/include/sanitizer/msan_interface.h @@ -0,0 +1,124 @@ +//===-- msan_interface.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 MemorySanitizer. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef MSAN_INTERFACE_H +#define MSAN_INTERFACE_H + +#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); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 67019655332a..fa6d8abc65e6 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,34 +1,23 @@ -# 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}) - -# Call add_clang_runtime_static_library(<target_library>) to make -# sure that static <target_library> is built in the directory -# where Clang driver expects to find it. -if (APPLE) - set(CLANG_RUNTIME_LIB_DIR - ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/lib/darwin) -elseif (UNIX) - # Assume Linux. - set(CLANG_RUNTIME_LIB_DIR - ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/lib/linux) -endif() -function(add_clang_runtime_static_library target_name) - set_target_properties(${target_name} PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY ${CLANG_RUNTIME_LIB_DIR}) -endfunction() - # First, add the subdirectories which contain feature-based runtime libraries # and several convenience helper libraries. -add_subdirectory(asan) -add_subdirectory(interception) -add_subdirectory(sanitizer_common) +if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux") + # AddressSanitizer is supported on Linux and Mac OS X. + # Windows support is work in progress. + add_subdirectory(asan) + add_subdirectory(interception) + add_subdirectory(sanitizer_common) + if(NOT ANDROID) + add_subdirectory(ubsan) + endif() +endif() +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND NOT ANDROID) + # ThreadSanitizer and MemorySanitizer are supported on Linux only. + add_subdirectory(tsan) + add_subdirectory(msan) +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. @@ -163,7 +152,7 @@ set(GENERIC_SOURCES umodti3.c ) -if(CAN_TARGET_X86_64) +if(CAN_TARGET_x86_64) add_library(clang_rt.x86_64 STATIC x86_64/floatdidf.c x86_64/floatdisf.c @@ -173,9 +162,10 @@ if(CAN_TARGET_X86_64) x86_64/floatundixf.S ${GENERIC_SOURCES} ) - set_target_properties(clang_rt.x86_64 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_X86_64_CFLAGS}") + 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) +if(CAN_TARGET_i386) add_library(clang_rt.i386 STATIC i386/ashldi3.S i386/ashrdi3.S @@ -193,5 +183,6 @@ if(CAN_TARGET_I386) i386/umoddi3.S ${GENERIC_SOURCES} ) - set_target_properties(clang_rt.i386 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_I386_CFLAGS}") + set_target_properties(clang_rt.i386 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_i386_CFLAGS}") + add_clang_compiler_rt_libraries(clang_rt.i386) endif() diff --git a/lib/Makefile.mk b/lib/Makefile.mk index 791921a80068..ea471e01b1e6 100644 --- a/lib/Makefile.mk +++ b/lib/Makefile.mk @@ -19,6 +19,7 @@ SubDirs += interception SubDirs += profile SubDirs += sanitizer_common SubDirs += tsan +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 diff --git a/lib/arm/Makefile.mk b/lib/arm/Makefile.mk index e7bbd7b615d5..04dec88a9714 100644 --- a/lib/arm/Makefile.mk +++ b/lib/arm/Makefile.mk @@ -9,7 +9,7 @@ ModuleName := builtins SubDirs := -OnlyArchs := armv5 armv6 armv7 +OnlyArchs := armv5 armv6 armv7 armv7f armv7k armv7s AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file))) Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file))) diff --git a/lib/arm/divsi3.S b/lib/arm/divsi3.S index 00e61815ab4f..e76fe31bb782 100644 --- a/lib/arm/divsi3.S +++ b/lib/arm/divsi3.S @@ -25,7 +25,16 @@ // Ok, APCS and AAPCS agree on 32 bit args, so it's safe to use the same routine. DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_idiv, __divsi3) DEFINE_COMPILERRT_FUNCTION(__divsi3) - ESTABLISH_FRAME +#if __ARM_ARCH_7S__ + tst r1,r1 + beq LOCAL_LABEL(divzero) + sdiv r0, r0, r1 + bx lr +LOCAL_LABEL(divzero): + mov r0,#0 + bx lr +#else +ESTABLISH_FRAME // Set aside the sign of the quotient. eor r4, r0, r1 // Take absolute value of a and b via abs(x) = (x^(x >> 31)) - (x >> 31). @@ -39,3 +48,4 @@ DEFINE_COMPILERRT_FUNCTION(__divsi3) eor r0, r0, r4, asr #31 sub r0, r0, r4, asr #31 CLEAR_FRAME_AND_RETURN +#endif diff --git a/lib/arm/udivsi3.S b/lib/arm/udivsi3.S index 6d8966539c7f..28979fee4bdc 100644 --- a/lib/arm/udivsi3.S +++ b/lib/arm/udivsi3.S @@ -33,6 +33,15 @@ // Ok, APCS and AAPCS agree on 32 bit args, so it's safe to use the same routine. DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_uidiv, __udivsi3) DEFINE_COMPILERRT_FUNCTION(__udivsi3) +#if __ARM_ARCH_7S__ + tst r1,r1 + beq LOCAL_LABEL(divzero) + udiv r0, r0, r1 + 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 +87,4 @@ LOCAL_LABEL(return): // Move the quotient to r0 and return. mov r0, q CLEAR_FRAME_AND_RETURN +#endif diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt index ce985f528172..92cba6dee622 100644 --- a/lib/asan/CMakeLists.txt +++ b/lib/asan/CMakeLists.txt @@ -2,6 +2,8 @@ set(ASAN_SOURCES asan_allocator.cc + asan_allocator2.cc + asan_fake_stack.cc asan_globals.cc asan_interceptors.cc asan_linux.cc @@ -12,7 +14,7 @@ set(ASAN_SOURCES asan_new_delete.cc asan_poisoning.cc asan_posix.cc - asan_printf.cc + asan_report.cc asan_rtl.cc asan_stack.cc asan_stats.cc @@ -21,62 +23,99 @@ set(ASAN_SOURCES asan_win.cc ) +set(ASAN_DYLIB_SOURCES + ${ASAN_SOURCES} + dynamic/asan_interceptors_dynamic.cc + ) + include_directories(..) -set(ASAN_CFLAGS - -fPIC - -fno-exceptions - -funwind-tables - -fvisibility=hidden - -fno-builtin - -fomit-frame-pointer - -O3 - ) -if (SUPPORTS_NO_VARIADIC_MACROS_FLAG) - list(APPEND ASAN_CFLAGS -Wno-variadic-macros) -endif () +set(ASAN_CFLAGS ${SANITIZER_COMMON_CFLAGS}) -if (APPLE) - list(APPEND ASAN_CFLAGS -mmacosx-version-min=10.5) +if(ANDROID) + set(ASAN_COMMON_DEFINITIONS + ASAN_HAS_EXCEPTIONS=1 + ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 + ASAN_NEEDS_SEGV=0 + ASAN_LOW_MEMORY=1 + ) +else() + set(ASAN_COMMON_DEFINITIONS + ASAN_HAS_EXCEPTIONS=1 + ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 + ASAN_NEEDS_SEGV=1 + ) endif() -set(ASAN_COMMON_DEFINITIONS - ASAN_HAS_EXCEPTIONS=1 - ASAN_NEEDS_SEGV=1 +set(ASAN_DYLIB_DEFINITIONS + ${ASAN_COMMON_DEFINITIONS} + MAC_INTERPOSE_FUNCTIONS=1 ) -# FIXME: We need to build universal binaries on OS X instead of -# two arch-specific binaries. +# Architectures supported by ASan. +filter_available_targets(ASAN_SUPPORTED_ARCH + x86_64 i386) -if(CAN_TARGET_X86_64) - add_library(clang_rt.asan-x86_64 STATIC +set(ASAN_RUNTIME_LIBRARIES) +if(APPLE) + # Build universal binary on APPLE. + add_library(clang_rt.asan_osx STATIC ${ASAN_SOURCES} - $<TARGET_OBJECTS:RTInterception.x86_64> - $<TARGET_OBJECTS:RTSanitizerCommon.x86_64> - ) - set_target_compile_flags(clang_rt.asan-x86_64 - ${ASAN_CFLAGS} - ${TARGET_X86_64_CFLAGS} + $<TARGET_OBJECTS:RTInterception.osx> + $<TARGET_OBJECTS:RTSanitizerCommon.osx> ) - set_property(TARGET clang_rt.asan-x86_64 APPEND PROPERTY COMPILE_DEFINITIONS - ${ASAN_COMMON_DEFINITIONS}) - add_clang_runtime_static_library(clang_rt.asan-x86_64) -endif() -if(CAN_TARGET_I386) - add_library(clang_rt.asan-i386 STATIC + 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) +elseif(ANDROID) + add_library(clang_rt.asan-arm-android SHARED ${ASAN_SOURCES} - $<TARGET_OBJECTS:RTInterception.i386> - $<TARGET_OBJECTS:RTSanitizerCommon.i386> + $<TARGET_OBJECTS:RTInterception.arm.android> + $<TARGET_OBJECTS:RTSanitizerCommon.arm.android> ) - set_target_compile_flags(clang_rt.asan-i386 + set_target_compile_flags(clang_rt.asan-arm-android ${ASAN_CFLAGS} - ${TARGET_I386_CFLAGS} ) - set_property(TARGET clang_rt.asan-i386 APPEND PROPERTY COMPILE_DEFINITIONS - ${ASAN_COMMON_DEFINITIONS}) - add_clang_runtime_static_library(clang_rt.asan-i386) + 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}) + 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}) + + if(LLVM_INCLUDE_TESTS) add_subdirectory(tests) endif() + +add_subdirectory(lit_tests) diff --git a/lib/asan/Makefile.mk b/lib/asan/Makefile.mk index 9d1a2e8a9a28..af9602e8b242 100644 --- a/lib/asan/Makefile.mk +++ b/lib/asan/Makefile.mk @@ -8,7 +8,7 @@ #===------------------------------------------------------------------------===# ModuleName := asan -SubDirs := +SubDirs := dynamic Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) ObjNames := $(Sources:%.cc=%.o) @@ -17,8 +17,9 @@ 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)/../interception/*.h) +Dependencies += $(wildcard $(Dir)/../interception/mach_override/*.h) +Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) # Define a convenience variable for all the asan functions. AsanFunctions := $(Sources:%.cc=%) diff --git a/lib/asan/Makefile.old b/lib/asan/Makefile.old deleted file mode 100644 index 4ab80e20bcb1..000000000000 --- a/lib/asan/Makefile.old +++ /dev/null @@ -1,349 +0,0 @@ -#===- lib/asan/Makefile.old --------------------------------*- Makefile -*--===# -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -#===------------------------------------------------------------------------===# - -OS=$(shell uname | tr '[A-Z]' '[a-z]') -ROOT=$(shell pwd) -MAKEFILE=Makefile.old # this file. - -ifeq ($(ARCH), android) - ANDROID_CFLAGS= \ - -DANDROID \ - -D__WORDSIZE=32 \ - -I$(ANDROID_BUILD_TOP)/external/stlport/stlport \ - -I$(ANDROID_BUILD_TOP)/bionic \ - -I$(ANDROID_BUILD_TOP)/bionic/libstdc++/include \ - -I$(ANDROID_BUILD_TOP)/bionic/libc/arch-arm/include \ - -I$(ANDROID_BUILD_TOP)/bionic/libc/include \ - -I$(ANDROID_BUILD_TOP)/bionic/libc/kernel/common \ - -I$(ANDROID_BUILD_TOP)/bionic/libc/kernel/arch-arm \ - -I$(ANDROID_BUILD_TOP)/bionic/libm/include \ - -I$(ANDROID_BUILD_TOP)/bionic/libm/include/arm \ - -I$(ANDROID_BUILD_TOP)/bionic/libthread_db/include \ - -L$(ANDROID_PRODUCT_OUT)/obj/lib - CLANG_FLAGS= \ - -ccc-host-triple arm-linux-androideabi \ - -D__compiler_offsetof=__builtin_offsetof \ - -D__ELF__=1 \ - -ccc-gcc-name arm-linux-androideabi-g++ \ - $(ANDROID_CFLAGS) - CC=$(ANDROID_EABI_TOOLCHAIN)/arm-linux-androideabi-gcc $(ANDROID_CFLAGS) - CXX=$(ANDROID_EABI_TOOLCHAIN)/arm-linux-androideabi-g++ $(ANDROID_CFLAGS) -endif - -ifeq ($(ARCH), arm) - # Example make command line: - # CROSSTOOL=$HOME/x-tools/arm-unknown-linux-gnueabi/ PATH=$CROSSTOOL/bin:$PATH make ARCH=arm asan_test - CLANG_FLAGS= \ - -ccc-host-triple arm-unknown-linux-gnueabi \ - -march=armv7-a -mfloat-abi=softfp -mfp=neon \ - -ccc-gcc-name arm-unknown-linux-gnueabi-g++ \ - -B$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4 \ - -B$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/lib \ - -I$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4/include \ - -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/include/c++/4.4.4 \ - -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/include/c++/4.4.4/arm-unknown-linux-gnueabi \ - -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/include \ - -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/include \ - -L$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4 \ - -L$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/lib \ - -L$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/lib - CC=$(CROSSTOOL)/bin/arm-unknown-linux-gnueabi-gcc - CXX=$(CROSSTOOL)/bin/arm-unknown-linux-gnueabi-g++ -endif - -CLANG_FLAGS= -CLANG_VERSION=3.2 -CLANG_BUILD=$(ROOT)/../../../../build/Release+Asserts -CLANG_CC=$(CLANG_BUILD)/bin/clang $(CLANG_FLAGS) -CLANG_CXX=$(CLANG_BUILD)/bin/clang++ $(CLANG_FLAGS) -FILE_CHECK=$(CLANG_BUILD)/bin/FileCheck - -CC=$(CLANG_CC) -CXX=$(CLANG_CXX) - -CFLAGS:=-Wall -fvisibility=hidden - -CLEANROOM_CXX=$(CXX) -Wall - -INSTALL_DIR=../asan_clang_$(OS) -BIN=bin_$(OS) - -LIBS=#-lpthread -ldl -ARCH=x86_64 - -ASAN_STACK=1 -ASAN_GLOBALS=1 -ASAN_SCALE=0 # default will be used -ASAN_OFFSET=-1 #default will be used -ASAN_UAR=0 -ASAN_HAS_EXCEPTIONS=1 -ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 -ASAN_HAS_BLACKLIST=1 -ASAN_NEEDS_SEGV=1 -ASAN_PIE=0 - -ifeq ($(ARCH), i386) -BITS=32 -SUFF=$(BITS) -CFLAGS:=$(CFLAGS) -m$(BITS) -endif - -ifeq ($(ARCH), x86_64) -BITS=64 -SUFF=$(BITS) -CFLAGS:=$(CFLAGS) -m$(BITS) -endif - -ifeq ($(ARCH), arm) -BITS=32 -SUFF=_arm -CFLAGS:=$(CFLAGS) -march=armv7-a -ASAN_HAS_EXCEPTIONS=0 -endif - -ifeq ($(ARCH), android) -BITS=32 -SUFF=_android -CFLAGS:=$(CFLAGS) -ASAN_HAS_EXCEPTIONS=0 -endif - -PIE= -ifeq ($(ASAN_PIE), 1) - PIE=-fPIE -pie -endif - -# This will build libasan on linux for both x86_64 and i386 in the -# desired location. The Mac library is already build by the clang's make. -# $(CLANG_BUILD)/lib/clang/$(CLANG_VERSION)/lib/$(OS)/libclang_rt.asan-$(ARCH).a -LIBASAN_INST_DIR=$(CLANG_BUILD)/lib/clang/$(CLANG_VERSION)/lib/$(OS) -LIBASAN_A=$(LIBASAN_INST_DIR)/libclang_rt.asan-$(ARCH).a - -BLACKLIST= -ifeq ($(ASAN_HAS_BLACKLIST), 1) - BLACKLIST=-mllvm -asan-blacklist=$(ROOT)/tests/asan_test.ignore -endif - -COMMON_ASAN_DEFINES=\ - -DASAN_UAR=$(ASAN_UAR) \ - -DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \ - -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \ - -DASAN_HAS_BLACKLIST=$(ASAN_HAS_BLACKLIST) - -CLANG_ASAN_CXX=$(CLANG_CXX) \ - -faddress-sanitizer \ - $(BLACKLIST) \ - -mllvm -asan-stack=$(ASAN_STACK) \ - -mllvm -asan-globals=$(ASAN_GLOBALS) \ - -mllvm -asan-mapping-scale=$(ASAN_SCALE) \ - -mllvm -asan-mapping-offset-log=$(ASAN_OFFSET) \ - -mllvm -asan-use-after-return=$(ASAN_UAR) \ - $(COMMON_ASAN_DEFINES) - -CLANG_ASAN_LD=$(CLANG_CXX) -faddress-sanitizer - -GCC_ASAN_PATH=SET_FROM_COMMAND_LINE -GCC_ASAN_CXX=$(GCC_ASAN_PATH)/g++ \ - -faddress-sanitizer \ - $(COMMON_ASAN_DEFINES) - -GCC_ASAN_LD=$(GCC_ASAN_PATH)/g++ -ldl -lpthread - -ASAN_COMPILER=clang - -ifeq ($(ASAN_COMPILER), clang) - ASAN_CXX=$(CLANG_ASAN_CXX) - ASAN_LD=$(CLANG_ASAN_LD) - ASAN_LD_TAIL= -endif - -ifeq ($(ASAN_COMPILER), gcc) - ASAN_CXX=$(GCC_ASAN_CXX) - ASAN_LD=$(GCC_ASAN_LD) - ASAN_LD_TAIL=$(LIBASAN_A) -endif - -INTERCEPTION=../interception -MACH_OVERRIDE=$(INTERCEPTION)/mach_override -COMMON=../sanitizer_common - -RTL_HDR=$(wildcard *.h) \ - $(wildcard $(INTERCEPTION)/*.h) \ - $(wildcard $(MACH_OVERRIDE)/*.h) \ - $(wildcard $(COMMON)/*.h) - -LIBTSAN_SRC=$(wildcard *.cc) -INTERCEPTION_SRC=$(wildcard $(INTERCEPTION)/*.cc) -MACH_OVERRIDE_SRC=$(wildcard $(MACH_OVERRIDE)/*.c) -COMMON_SRC=$(wildcard $(COMMON)/*.cc) - - -LIBASAN_OBJ=$(patsubst %.cc,$(BIN)/%$(SUFF).o,$(LIBTSAN_SRC)) \ - $(patsubst $(INTERCEPTION)/%.cc,$(BIN)/%$(SUFF).o,$(INTERCEPTION_SRC)) \ - $(patsubst $(COMMON)/%.cc,$(BIN)/%$(SUFF).o,$(COMMON_SRC)) \ - $(patsubst $(MACH_OVERRIDE)/%.c,$(BIN)/%$(SUFF).o,$(MACH_OVERRIDE_SRC)) - -GTEST_ROOT=third_party/googletest -GTEST_INCLUDE=-I$(GTEST_ROOT)/include -GTEST_MAKE_DIR=$(GTEST_ROOT)/make-$(OS)$(SUFF) -GTEST_LIB=$(GTEST_MAKE_DIR)/gtest-all.o - -all: b64 b32 - -test: t64 t32 output_tests lint - @echo "ALL TESTS PASSED" - -output_tests: b32 b64 - cd output_tests && ./test_output.sh $(CLANG_CXX) $(CLANG_CC) $(FILE_CHECK) - -t64: b64 - $(BIN)/asan_test64 -t32: b32 - $(BIN)/asan_test32 - -b64: | mk_bin_dir - $(MAKE) -f $(MAKEFILE) ARCH=x86_64 asan_test asan_benchmarks -b32: | mk_bin_dir - $(MAKE) -f $(MAKEFILE) ARCH=i386 asan_test asan_benchmarks - -lib64: - $(MAKE) -f $(MAKEFILE) ARCH=x86_64 lib -lib32: - $(MAKE) -f $(MAKEFILE) ARCH=i386 lib - -mk_bin_dir: - mkdir -p $(BIN) - -clang: - cd ../ && llvm/rebuild_clang_and_asan.sh > /dev/null - -install: install_clang - -$(INSTALL_DIR): - mkdir -p $(INSTALL_DIR) $(INSTALL_DIR)/bin $(INSTALL_DIR)/lib - -install_clang: | $(INSTALL_DIR) - cp -v $(CLANG_BUILD)/bin/clang $(INSTALL_DIR)/bin - cp -rv $(CLANG_BUILD)/lib/clang $(INSTALL_DIR)/lib - (cd $(INSTALL_DIR)/bin; ln -sf clang clang++) - -#install_lib: | $(INSTALL_DIR) -# cp -v $(CLANG_BUILD)/lib/libasan*.a $(INSTALL_DIR)/lib - -$(BIN)/asan_noinst_test$(SUFF).o: tests/asan_noinst_test.cc $(RTL_HDR) $(MAKEFILE) - $(CLEANROOM_CXX) $(PIE) $(CFLAGS) $(GTEST_INCLUDE) -I. -I.. -g -c $< -O2 -o $@ - -$(BIN)/asan_break_optimization$(SUFF).o: tests/asan_break_optimization.cc $(MAKEFILE) - $(CLEANROOM_CXX) $(PIE) $(CFLAGS) -c $< -O0 -o $@ - -$(BIN)/%_test$(SUFF).o: tests/%_test.cc $(RTL_HDR) $(MAKEFILE) - $(ASAN_CXX) $(GTEST_INCLUDE) -I. -I.. -g -c $< -O2 -o $@ $(PIE) $(CFLAGS) - -$(BIN)/%_test$(SUFF).o: tests/%_test.mm $(RTL_HDR) $(MAKEFILE) - $(ASAN_CXX) $(GTEST_INCLUDE) -I. -I.. -g -c $< -O2 -o $@ -ObjC $(PIE) $(CFLAGS) - -RTL_COMMON_FLAGS=$(PIE) $(CFLAGS) -fPIC -c -O2 -fno-exceptions -funwind-tables \ - -Ithird_party -I.. $(ASAN_FLAGS) - -$(BIN)/%$(SUFF).o: $(INTERCEPTION)/%.cc $(RTL_HDR) $(MAKEFILE) - $(CXX) $(RTL_COMMON_FLAGS) -o $@ -g $< - -$(BIN)/%$(SUFF).o: $(COMMON)/%.cc $(RTL_HDR) $(MAKEFILE) - $(CXX) $(RTL_COMMON_FLAGS) -o $@ -g $< - -$(BIN)/%$(SUFF).o: $(MACH_OVERRIDE)/%.c $(RTL_HDR) $(MAKEFILE) - $(CC) $(RTL_COMMON_FLAGS) -o $@ -g $< - -$(BIN)/%$(SUFF).o: %.cc $(RTL_HDR) $(MAKEFILE) - $(CXX) $(RTL_COMMON_FLAGS) -o $@ -g $< \ - -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \ - -DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \ - -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=$(ASAN_FLEXIBLE_MAPPING_AND_OFFSET) - -$(BIN)/%$(SUFF).o: %.c $(RTL_HDR) $(MAKEFILE) - $(CC) $(PIE) $(CFLAGS) -fPIC -c -O2 -o $@ -g $< -Ithird_party \ - $(ASAN_FLAGS) - -ifeq ($(OS),darwin) -LD_FLAGS=-framework Foundation -else -LD_FLAGS= -endif - -lib: $(LIBASAN_A) - -$(LIBASAN_A): mk_bin_dir $(LIBASAN_OBJ) $(MAKEFILE) - mkdir -p $(LIBASAN_INST_DIR) - ar ru $@ $(LIBASAN_OBJ) - $(CXX) -shared $(CFLAGS) $(LIBASAN_OBJ) $(LD_FLAGS) -o $(BIN)/libasan$(SUFF).so - -TEST_OBJECTS_COMMON=\ - $(BIN)/asan_globals_test$(SUFF).o \ - $(BIN)/asan_break_optimization$(SUFF).o \ - $(BIN)/asan_noinst_test$(SUFF).o \ - $(BIN)/asan_test$(SUFF).o - -BENCHMARK_OBJECTS=\ - $(BIN)/asan_benchmarks_test$(SUFF).o \ - $(BIN)/asan_break_optimization$(SUFF).o - -ifeq ($(OS),darwin) -TEST_OBJECTS=$(TEST_OBJECTS_COMMON) \ - $(BIN)/asan_mac_test$(SUFF).o -else -TEST_OBJECTS=$(TEST_OBJECTS_COMMON) -endif - -$(BIN)/asan_test$(SUFF): $(TEST_OBJECTS) $(LIBASAN_A) $(MAKEFILE) tests/asan_test.ignore $(GTEST_LIB) - $(ASAN_LD) $(PIE) $(CFLAGS) -g -O3 $(TEST_OBJECTS) \ - $(LD_FLAGS) -o $@ $(LIBS) $(GTEST_LIB) $(ASAN_LD_TAIL) - -$(BIN)/asan_benchmarks$(SUFF): $(BENCHMARK_OBJECTS) $(LIBASAN_A) $(MAKEFILE) $(GTEST_LIB) - $(ASAN_LD) $(PIE) $(CFLAGS) -g -O3 $(BENCHMARK_OBJECTS) \ - $(LD_FLAGS) -o $@ $(LIBS) $(GTEST_LIB) $(ASAN_LD_TAIL) - -asan_test: $(BIN)/asan_test$(SUFF) - -asan_benchmarks: $(BIN)/asan_benchmarks$(SUFF) - -# for now, build gtest with clang/asan even if we use a different compiler. -$(GTEST_LIB): - mkdir -p $(GTEST_MAKE_DIR) && \ - cd $(GTEST_MAKE_DIR) && \ - $(MAKE) -f ../make/Makefile CXXFLAGS="$(PIE) $(CFLAGS) -g -w" \ - CXX="$(CLANG_CXX)" - -RTL_LINT_FILTER=-readability/casting,-readability/check,-build/include,-build/header_guard,-build/class,-legal/copyright,-build/namespaces -# TODO(kcc): remove these filters one by one -TEST_LINT_FILTER=-readability/casting,-build/include,-legal/copyright,-whitespace/newline,-runtime/sizeof,-runtime/int,-runtime/printf - -LLVM_LINT_FILTER=-,+whitespace - -ADDRESS_SANITIZER_CPP=../../../../lib/Transforms/Instrumentation/AddressSanitizer.cpp - -lint: - third_party/cpplint/cpplint.py --filter=$(LLVM_LINT_FILTER) $(ADDRESS_SANITIZER_CPP) - third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FILTER) asan_*.cc asan_*.h - third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FILTER) $(INTERCEPTION)/interception*.h $(INTERCEPTION)/interception*.cc - third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FILTER) $(COMMON)/sanitizer_*.h $(COMMON)/sanitizer_*.cc - third_party/cpplint/cpplint.py --filter=$(TEST_LINT_FILTER) tests/*.cc output_tests/*.cc - -get_third_party: - rm -rf third_party - mkdir third_party - (cd third_party && \ - svn co -r375 http://googletest.googlecode.com/svn/trunk googletest && \ - svn co -r69 http://google-styleguide.googlecode.com/svn/trunk/cpplint cpplint \ - ) - -clean: - rm -f *.o *.ll *.S *.a *.log asan_test64* asan_test32* a.out perf.data log - rm -f $(LIBASAN_INST_DIR)/libclang_rt.asan-*.a - rm -rf $(BIN) - rm -rf $(GTEST_ROOT)/make-* diff --git a/lib/asan/README.txt b/lib/asan/README.txt index 5e6600489a69..e4f4961c5d4f 100644 --- a/lib/asan/README.txt +++ b/lib/asan/README.txt @@ -4,22 +4,26 @@ This directory contains sources of the AddressSanitizer (asan) run-time library. We are in the process of integrating AddressSanitizer with LLVM, stay tuned. Directory structre: - README.txt : This file. -Makefile.mk : Currently a stub for a proper makefile. not usable. -Makefile.old : Old out-of-tree makefile, the only usable one so far. +Makefile.mk : File for make-based build. +CMakeLists.txt : File for cmake-based build. asan_*.{cc,h} : Sources of the asan run-time lirbary. -mach_override/* : Utility to override functions on Darwin (MIT License). scripts/* : Helper scripts. +tests/* : ASan unit tests. +lit_tests/* : ASan output tests. -Temporary build instructions (verified on linux): +Also ASan runtime needs the following libraries: +lib/interception/ : Machinery used to intercept function calls. +lib/sanitizer_common/ : Code shared between ASan and TSan. -cd lib/asan -make -f Makefile.old get_third_party # gets googletest and cpplint -make -f Makefile.old test -j 8 CLANG_BUILD=/path/to/Release+Asserts -# Optional: -# make -f Makefile.old install # installs clang and rt to lib/asan_clang_linux +Currently ASan runtime can be built by both make and cmake build systems. +(see compiler-rt/make and files Makefile.mk for make-based build and +files CMakeLists.txt for cmake-based build). -For more info see http://code.google.com/p/address-sanitizer/ +ASan unit and output tests work only with cmake. You may run this +command from the root of your cmake build tree: +make check-asan +For more instructions see: +http://code.google.com/p/address-sanitizer/wiki/HowToBuild diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc index 352cce00fbee..30dd4ceddd88 100644 --- a/lib/asan/asan_allocator.cc +++ b/lib/asan/asan_allocator.cc @@ -24,21 +24,19 @@ // 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_interface.h" #include "asan_internal.h" -#include "asan_lock.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" - -#if defined(_WIN32) && !defined(__clang__) -#include <intrin.h> -#endif +#include "sanitizer_common/sanitizer_mutex.h" namespace __asan { @@ -57,43 +55,7 @@ static const uptr kMallocSizeClassStepLog = 26; static const uptr kMallocSizeClassStep = 1UL << kMallocSizeClassStepLog; static const uptr kMaxAllowedMallocSize = - (__WORDSIZE == 32) ? 3UL << 30 : 8UL << 30; - -static inline bool IsAligned(uptr a, uptr alignment) { - return (a & (alignment - 1)) == 0; -} - -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 = __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); -} + (SANITIZER_WORDSIZE == 32) ? 3UL << 30 : 8UL << 30; static inline uptr SizeClassToSize(u8 size_class) { CHECK(size_class < kNumberOfSizeClasses); @@ -131,7 +93,7 @@ static void PoisonHeapPartialRightRedzone(uptr mem, uptr size) { } static u8 *MmapNewPagesAndPoisonShadow(uptr size) { - CHECK(IsAligned(size, kPageSize)); + CHECK(IsAligned(size, GetPageSizeCached())); u8 *res = (u8*)MmapOrDie(size, __FUNCTION__); PoisonShadow((uptr)res, size, kAsanHeapLeftRedzoneMagic); if (flags()->debug) { @@ -166,7 +128,8 @@ struct ChunkBase { // Second 8 bytes. uptr alignment_log : 8; - uptr used_size : FIRST_32_SECOND_64(32, 56); // Size requested by the user. + 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. @@ -198,50 +161,23 @@ struct AsanChunk: public ChunkBase { if (REDZONE < sizeof(ChunkBase)) return 0; return (REDZONE) / sizeof(u32); } +}; - bool AddrIsInside(uptr addr, uptr access_size, uptr *offset) { - if (addr >= Beg() && (addr + access_size) <= (Beg() + used_size)) { - *offset = addr - Beg(); - return true; - } - return false; - } - - bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) { - if (addr < Beg()) { - *offset = Beg() - addr; - return true; - } - return false; - } +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; } - bool AddrIsAtRight(uptr addr, uptr access_size, uptr *offset) { - if (addr + access_size >= Beg() + used_size) { - if (addr <= Beg() + used_size) - *offset = 0; - else - *offset = addr - (Beg() + used_size); - return true; - } - return false; - } +void AsanChunkView::GetAllocStack(StackTrace *stack) { + StackTrace::UncompressStack(stack, chunk_->compressed_alloc_stack(), + chunk_->compressed_alloc_stack_size()); +} - void DescribeAddress(uptr addr, uptr access_size) { - uptr offset; - AsanPrintf("%p is located ", (void*)addr); - if (AddrIsInside(addr, access_size, &offset)) { - AsanPrintf("%zu bytes inside of", offset); - } else if (AddrIsAtLeft(addr, access_size, &offset)) { - AsanPrintf("%zu bytes to the left of", offset); - } else if (AddrIsAtRight(addr, access_size, &offset)) { - AsanPrintf("%zu bytes to the right of", offset); - } else { - AsanPrintf(" somewhere around (this is AddressSanitizer bug!)"); - } - AsanPrintf(" %zu-byte region [%p,%p)\n", - used_size, (void*)Beg(), (void*)(Beg() + used_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); @@ -251,37 +187,15 @@ static AsanChunk *PtrToChunk(uptr ptr) { return m; } - void AsanChunkFifoList::PushList(AsanChunkFifoList *q) { CHECK(q->size() > 0); - if (last_) { - CHECK(first_); - CHECK(!last_->next); - last_->next = q->first_; - last_ = q->last_; - } else { - CHECK(!first_); - last_ = q->last_; - first_ = q->first_; - CHECK(first_); - } - CHECK(last_); - CHECK(!last_->next); size_ += q->size(); + append_back(q); q->clear(); } void AsanChunkFifoList::Push(AsanChunk *n) { - CHECK(n->next == 0); - if (last_) { - CHECK(first_); - CHECK(!last_->next); - last_->next = n; - last_ = n; - } else { - CHECK(!first_); - last_ = first_ = n; - } + push_back(n); size_ += n->Size(); } @@ -290,15 +204,9 @@ void AsanChunkFifoList::Push(AsanChunk *n) { // ago. Not sure if we can or want to do anything with this. AsanChunk *AsanChunkFifoList::Pop() { CHECK(first_); - AsanChunk *res = first_; - first_ = first_->next; - if (first_ == 0) - last_ = 0; - CHECK(size_ >= res->Size()); + AsanChunk *res = front(); size_ -= res->Size(); - if (last_) { - CHECK(!last_->next); - } + pop_front(); return res; } @@ -315,14 +223,13 @@ struct PageGroup { 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]; { - ScopedLock lock(&mu_); + BlockingMutexLock lock(&mu_); for (uptr i = 0; i < n_chunks; i++) { if (!(*fl)) { *fl = GetNewChunks(size_class); @@ -340,7 +247,7 @@ class MallocInfo { void SwallowThreadLocalMallocStorage(AsanThreadLocalMallocStorage *x, bool eat_free_lists) { CHECK(flags()->quarantine_size > 0); - ScopedLock lock(&mu_); + BlockingMutexLock lock(&mu_); AsanChunkFifoList *q = &x->quarantine_; if (q->size() > 0) { quarantine_.PushList(q); @@ -364,23 +271,24 @@ class MallocInfo { } void BypassThreadLocalQuarantine(AsanChunk *chunk) { - ScopedLock lock(&mu_); + BlockingMutexLock lock(&mu_); quarantine_.Push(chunk); } - AsanChunk *FindMallocedOrFreed(uptr addr, uptr access_size) { - ScopedLock lock(&mu_); - return FindChunkByAddr(addr); + AsanChunk *FindChunkByAddr(uptr addr) { + BlockingMutexLock lock(&mu_); + return FindChunkByAddrUnlocked(addr); } uptr AllocationSize(uptr ptr) { if (!ptr) return 0; - ScopedLock lock(&mu_); + 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; - // first, check if this is our memory - PageGroup *g = FindPageGroupUnlocked(ptr); - if (!g) return 0; - AsanChunk *m = PtrToChunk(ptr); if (m->chunk_state == CHUNK_ALLOCATED) { return m->used_size; } else { @@ -397,7 +305,7 @@ class MallocInfo { } void PrintStatus() { - ScopedLock lock(&mu_); + BlockingMutexLock lock(&mu_); uptr malloced = 0; Printf(" MallocInfo: in quarantine: %zu malloced: %zu; ", @@ -415,7 +323,7 @@ class MallocInfo { } PageGroup *FindPageGroup(uptr addr) { - ScopedLock lock(&mu_); + BlockingMutexLock lock(&mu_); return FindPageGroupUnlocked(addr); } @@ -462,14 +370,14 @@ class MallocInfo { return left_chunk; // Choose based on offset. uptr l_offset = 0, r_offset = 0; - CHECK(left_chunk->AddrIsAtRight(addr, 1, &l_offset)); - CHECK(right_chunk->AddrIsAtLeft(addr, 1, &r_offset)); + 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 *FindChunkByAddr(uptr addr) { + AsanChunk *FindChunkByAddrUnlocked(uptr addr) { PageGroup *g = FindPageGroupUnlocked(addr); if (!g) return 0; CHECK(g->size_of_chunk); @@ -482,17 +390,18 @@ class MallocInfo { m->chunk_state == CHUNK_AVAILABLE || m->chunk_state == CHUNK_QUARANTINE); uptr offset = 0; - if (m->AddrIsInside(addr, 1, &offset)) + AsanChunkView m_view(m); + if (m_view.AddrIsInside(addr, 1, &offset)) return m; - if (m->AddrIsAtRight(addr, 1, &offset)) { + 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->AddrIsAtLeft(addr, 1, &offset)); + 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; @@ -533,12 +442,13 @@ class MallocInfo { uptr mmap_size = Max(size, kMinMmapSize); uptr n_chunks = mmap_size / size; CHECK(n_chunks * size == mmap_size); - if (size < kPageSize) { + 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 += kPageSize; + mmap_size += PageSize; } CHECK(n_chunks > 0); u8 *mem = MmapNewPagesAndPoisonShadow(mmap_size); @@ -564,14 +474,14 @@ class MallocInfo { 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)ASAN_ARRAY_SIZE(page_groups_)); + CHECK(idx < (int)ARRAY_SIZE(page_groups_)); page_groups_[idx] = pg; return res; } AsanChunk *free_lists_[kNumberOfSizeClasses]; AsanChunkFifoList quarantine_; - AsanLock mu_; + BlockingMutex mu_; PageGroup *page_groups_[kMaxAvailableRam / kMinMmapSize]; atomic_uint32_t n_page_groups_; @@ -584,42 +494,12 @@ void AsanThreadLocalMallocStorage::CommitBack() { malloc_info.SwallowThreadLocalMallocStorage(this, true); } -static void Describe(uptr addr, uptr access_size) { - AsanChunk *m = malloc_info.FindMallocedOrFreed(addr, access_size); - if (!m) return; - m->DescribeAddress(addr, access_size); - CHECK(m->alloc_tid >= 0); - AsanThreadSummary *alloc_thread = - asanThreadRegistry().FindByTid(m->alloc_tid); - AsanStackTrace alloc_stack; - AsanStackTrace::UncompressStack(&alloc_stack, m->compressed_alloc_stack(), - m->compressed_alloc_stack_size()); - AsanThread *t = asanThreadRegistry().GetCurrent(); - CHECK(t); - if (m->free_tid != kInvalidTid) { - AsanThreadSummary *free_thread = - asanThreadRegistry().FindByTid(m->free_tid); - AsanPrintf("freed by thread T%d here:\n", free_thread->tid()); - AsanStackTrace free_stack; - AsanStackTrace::UncompressStack(&free_stack, m->compressed_free_stack(), - m->compressed_free_stack_size()); - free_stack.PrintStack(); - AsanPrintf("previously allocated by thread T%d here:\n", - alloc_thread->tid()); - - alloc_stack.PrintStack(); - t->summary()->Announce(); - free_thread->Announce(); - alloc_thread->Announce(); - } else { - AsanPrintf("allocated by thread T%d here:\n", alloc_thread->tid()); - alloc_stack.PrintStack(); - t->summary()->Announce(); - alloc_thread->Announce(); - } +AsanChunkView FindHeapChunkByAddress(uptr address) { + return AsanChunkView(malloc_info.FindChunkByAddr(address)); } -static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) { +static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack, + AllocType alloc_type) { __asan_init(); CHECK(stack); if (size == 0) { @@ -676,6 +556,7 @@ static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) { 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; @@ -697,7 +578,7 @@ static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) { CHECK(m->Beg() == addr); m->alloc_tid = t ? t->tid() : 0; m->free_tid = kInvalidTid; - AsanStackTrace::CompressStack(stack, m->compressed_alloc_stack(), + StackTrace::CompressStack(stack, m->compressed_alloc_stack(), m->compressed_alloc_stack_size()); PoisonShadow(addr, rounded_size, 0); if (size < rounded_size) { @@ -710,7 +591,7 @@ static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) { return (u8*)addr; } -static void Deallocate(u8 *ptr, AsanStackTrace *stack) { +static void Deallocate(u8 *ptr, StackTrace *stack, AllocType alloc_type) { if (!ptr) return; CHECK(stack); @@ -726,24 +607,21 @@ static void Deallocate(u8 *ptr, AsanStackTrace *stack) { memory_order_acq_rel); if (old_chunk_state == CHUNK_QUARANTINE) { - AsanReport("ERROR: AddressSanitizer attempting double-free on %p:\n", ptr); - stack->PrintStack(); - Describe((uptr)ptr, 1); - ShowStatsAndAbort(); + ReportDoubleFree((uptr)ptr, stack); } else if (old_chunk_state != CHUNK_ALLOCATED) { - AsanReport("ERROR: AddressSanitizer attempting free on address " - "which was not malloc()-ed: %p\n", ptr); - stack->PrintStack(); - ShowStatsAndAbort(); + 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; - AsanStackTrace::CompressStack(stack, m->compressed_free_stack(), + 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); @@ -769,7 +647,7 @@ static void Deallocate(u8 *ptr, AsanStackTrace *stack) { } static u8 *Reallocate(u8 *old_ptr, uptr new_size, - AsanStackTrace *stack) { + StackTrace *stack) { CHECK(old_ptr && new_size); // Statistics. @@ -781,114 +659,112 @@ static u8 *Reallocate(u8 *old_ptr, uptr new_size, 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); + 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); + Deallocate(old_ptr, stack, FROM_MALLOC); } return new_ptr; } } // namespace __asan -// Malloc hooks declaration. -// ASAN_NEW_HOOK(ptr, size) is called immediately after -// allocation of "size" bytes, which returned "ptr". -// ASAN_DELETE_HOOK(ptr) is called immediately before -// deallocation of "ptr". -// If ASAN_NEW_HOOK or ASAN_DELETE_HOOK is defined, user -// program must provide implementation of this hook. -// If macro is undefined, the hook is no-op. -#ifdef ASAN_NEW_HOOK -extern "C" void ASAN_NEW_HOOK(void *ptr, uptr size); -#else -static inline void ASAN_NEW_HOOK(void *ptr, uptr size) { } -#endif - -#ifdef ASAN_DELETE_HOOK -extern "C" void ASAN_DELETE_HOOK(void *ptr); -#else -static inline void ASAN_DELETE_HOOK(void *ptr) { } +#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 *asan_memalign(uptr alignment, uptr size, AsanStackTrace *stack) { - void *ptr = (void*)Allocate(alignment, size, stack); - ASAN_NEW_HOOK(ptr, size); +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; } -void asan_free(void *ptr, AsanStackTrace *stack) { - ASAN_DELETE_HOOK(ptr); - Deallocate((u8*)ptr, stack); +SANITIZER_INTERFACE_ATTRIBUTE +void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { + ASAN_FREE_HOOK(ptr); + Deallocate((u8*)ptr, stack, alloc_type); } -void *asan_malloc(uptr size, AsanStackTrace *stack) { - void *ptr = (void*)Allocate(0, size, stack); - ASAN_NEW_HOOK(ptr, size); +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, AsanStackTrace *stack) { - void *ptr = (void*)Allocate(0, nmemb * size, stack); +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_NEW_HOOK(ptr, nmemb * size); + ASAN_MALLOC_HOOK(ptr, size); return ptr; } -void *asan_realloc(void *p, uptr size, AsanStackTrace *stack) { +void *asan_realloc(void *p, uptr size, StackTrace *stack) { if (p == 0) { - void *ptr = (void*)Allocate(0, size, stack); - ASAN_NEW_HOOK(ptr, size); + void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC); + ASAN_MALLOC_HOOK(ptr, size); return ptr; } else if (size == 0) { - ASAN_DELETE_HOOK(p); - Deallocate((u8*)p, stack); + ASAN_FREE_HOOK(p); + Deallocate((u8*)p, stack, FROM_MALLOC); return 0; } return Reallocate((u8*)p, size, stack); } -void *asan_valloc(uptr size, AsanStackTrace *stack) { - void *ptr = (void*)Allocate(kPageSize, size, stack); - ASAN_NEW_HOOK(ptr, size); +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, AsanStackTrace *stack) { - size = RoundUpTo(size, kPageSize); +void *asan_pvalloc(uptr size, StackTrace *stack) { + uptr PageSize = GetPageSizeCached(); + size = RoundUpTo(size, PageSize); if (size == 0) { // pvalloc(0) should allocate one page. - size = kPageSize; + size = PageSize; } - void *ptr = (void*)Allocate(kPageSize, size, stack); - ASAN_NEW_HOOK(ptr, size); + 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, - AsanStackTrace *stack) { - void *ptr = Allocate(alignment, size, stack); + StackTrace *stack) { + void *ptr = Allocate(alignment, size, stack, FROM_MALLOC); CHECK(IsAligned((uptr)ptr, alignment)); - ASAN_NEW_HOOK(ptr, size); + ASAN_MALLOC_HOOK(ptr, size); *memptr = ptr; return 0; } -uptr asan_malloc_usable_size(void *ptr, AsanStackTrace *stack) { +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)) { - AsanReport("ERROR: AddressSanitizer attempting to call " - "malloc_usable_size() for pointer which is " - "not owned: %p\n", ptr); - stack->PrintStack(); - Describe((uptr)ptr, 1); - ShowStatsAndAbort(); + ReportMallocUsableSizeNotOwned((uptr)ptr, stack); } return usable_size; } @@ -897,10 +773,6 @@ uptr asan_mz_size(const void *ptr) { return malloc_info.AllocationSize((uptr)ptr); } -void DescribeHeapAddress(uptr addr, uptr access_size) { - Describe(addr, access_size); -} - void asan_mz_force_lock() { malloc_info.ForceLock(); } @@ -909,170 +781,11 @@ void asan_mz_force_unlock() { malloc_info.ForceUnlock(); } -// ---------------------- Fake stack-------------------- {{{1 -FakeStack::FakeStack() { - CHECK(REAL(memset) != 0); - REAL(memset)(this, 0, sizeof(*this)); -} - -bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) { - uptr mem = allocated_size_classes_[size_class]; - uptr size = ClassMmapSize(size_class); - bool res = mem && addr >= mem && addr < mem + size; - return res; -} - -uptr FakeStack::AddrIsInFakeStack(uptr addr) { - for (uptr i = 0; i < kNumberOfSizeClasses; i++) { - if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i]; - } - return 0; -} - -// We may want to compute this during compilation. -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))); - uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog; - CHECK(res < kNumberOfSizeClasses); - CHECK(ClassSize(res) >= rounded_size); - return res; -} - -void FakeFrameFifo::FifoPush(FakeFrame *node) { - CHECK(node); - node->next = 0; - if (first_ == 0 && last_ == 0) { - first_ = last_ = node; - } else { - CHECK(first_); - CHECK(last_); - last_->next = node; - last_ = node; - } -} - -FakeFrame *FakeFrameFifo::FifoPop() { - CHECK(first_ && last_ && "Exhausted fake stack"); - FakeFrame *res = 0; - if (first_ == last_) { - res = first_; - first_ = last_ = 0; - } else { - res = first_; - first_ = first_->next; - } - return res; -} - -void FakeStack::Init(uptr stack_size) { - stack_size_ = stack_size; - alive_ = true; -} - -void FakeStack::Cleanup() { - alive_ = false; - for (uptr i = 0; i < kNumberOfSizeClasses; i++) { - uptr mem = allocated_size_classes_[i]; - if (mem) { - PoisonShadow(mem, ClassMmapSize(i), 0); - allocated_size_classes_[i] = 0; - UnmapOrDie((void*)mem, ClassMmapSize(i)); - } - } -} - -uptr FakeStack::ClassMmapSize(uptr size_class) { - return RoundUpToPowerOfTwo(stack_size_); -} - -void FakeStack::AllocateOneSizeClass(uptr size_class) { - CHECK(ClassMmapSize(size_class) >= kPageSize); - uptr new_mem = (uptr)MmapOrDie( - ClassMmapSize(size_class), __FUNCTION__); - // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n", - // asanThreadRegistry().GetCurrent()->tid(), - // size_class, new_mem, new_mem + ClassMmapSize(size_class), - // ClassMmapSize(size_class)); - uptr i; - for (i = 0; i < ClassMmapSize(size_class); - i += ClassSize(size_class)) { - size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i)); - } - CHECK(i == ClassMmapSize(size_class)); - allocated_size_classes_[size_class] = new_mem; -} - -uptr FakeStack::AllocateStack(uptr size, uptr real_stack) { - if (!alive_) return real_stack; - CHECK(size <= kMaxStackMallocSize && size > 1); - uptr size_class = ComputeSizeClass(size); - if (!allocated_size_classes_[size_class]) { - AllocateOneSizeClass(size_class); - } - FakeFrame *fake_frame = size_classes_[size_class].FifoPop(); - CHECK(fake_frame); - fake_frame->size_minus_one = size - 1; - fake_frame->real_stack = real_stack; - while (FakeFrame *top = call_stack_.top()) { - if (top->real_stack > real_stack) break; - call_stack_.LifoPop(); - DeallocateFrame(top); - } - call_stack_.LifoPush(fake_frame); - uptr ptr = (uptr)fake_frame; - PoisonShadow(ptr, size, 0); - return ptr; -} - -void FakeStack::DeallocateFrame(FakeFrame *fake_frame) { - CHECK(alive_); - uptr size = fake_frame->size_minus_one + 1; - uptr size_class = ComputeSizeClass(size); - CHECK(allocated_size_classes_[size_class]); - uptr ptr = (uptr)fake_frame; - CHECK(AddrIsInSizeClass(ptr, size_class)); - CHECK(AddrIsInSizeClass(ptr + size - 1, size_class)); - size_classes_[size_class].FifoPush(fake_frame); -} - -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); - PoisonShadow(ptr, size, kAsanStackAfterReturnMagic); -} - } // namespace __asan // ---------------------- Interface ---------------- {{{1 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(); - if (!t) { - // TSD is gone, use the real stack. - return real_stack; - } - uptr ptr = t->fake_stack().AllocateStack(size, real_stack); - // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack); - return ptr; -} - -void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) { - if (!flags()->use_fake_stack) return; - if (ptr != real_stack) { - FakeStack::OnFree(ptr, size, real_stack); - } -} - // ASan allocator doesn't reserve extra bytes, so normally we would // just return "size". uptr __asan_get_estimated_allocated_size(uptr size) { @@ -1089,12 +802,9 @@ uptr __asan_get_allocated_size(const void *p) { uptr allocated_size = malloc_info.AllocationSize((uptr)p); // Die if p is not malloced or if it is already freed. if (allocated_size == 0) { - AsanReport("ERROR: AddressSanitizer attempting to call " - "__asan_get_allocated_size() for pointer which is " - "not owned: %p\n", p); - PRINT_CURRENT_STACK(); - Describe((uptr)p, 1); - ShowStatsAndAbort(); + 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 2aed59853868..cca24edad81f 100644 --- a/lib/asan/asan_allocator.h +++ b/lib/asan/asan_allocator.h @@ -17,13 +17,76 @@ #include "asan_internal.h" #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 { + FROM_MALLOC = 1, // Memory block came from malloc, calloc, realloc, etc. + FROM_NEW = 2, // Memory block came from operator new. + FROM_NEW_BR = 3 // Memory block came from operator new [ ] +}; + static const uptr kNumberOfSizeClasses = 255; struct AsanChunk; -class AsanChunkFifoList { +class AsanChunkView { + public: + explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {} + bool IsValid() { return chunk_ != 0; } + uptr Beg(); // first byte of user memory. + uptr End(); // last byte of user memory. + uptr UsedSize(); // size requested by the user. + uptr AllocTid(); + uptr FreeTid(); + void GetAllocStack(StackTrace *stack); + void GetFreeStack(StackTrace *stack); + bool AddrIsInside(uptr addr, uptr access_size, uptr *offset) { + if (addr >= Beg() && (addr + access_size) <= End()) { + *offset = addr - Beg(); + return true; + } + return false; + } + bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) { + (void)access_size; + if (addr < Beg()) { + *offset = Beg() - addr; + return true; + } + 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(); + return true; + } + return false; + } + + private: + AsanChunk *const chunk_; +}; + +AsanChunkView FindHeapChunkByAddress(uptr address); + +// List of AsanChunks with total size. +class AsanChunkFifoList: public IntrusiveList<AsanChunk> { public: explicit AsanChunkFifoList(LinkerInitialized) { } AsanChunkFifoList() { clear(); } @@ -32,25 +95,31 @@ class AsanChunkFifoList { AsanChunk *Pop(); uptr size() { return size_; } void clear() { - first_ = last_ = 0; + IntrusiveList<AsanChunk>::clear(); size_ = 0; } private: - AsanChunk *first_; - AsanChunk *last_; uptr size_; }; struct AsanThreadLocalMallocStorage { explicit AsanThreadLocalMallocStorage(LinkerInitialized x) - : quarantine_(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(); }; @@ -108,6 +177,7 @@ class FakeStack { // 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. @@ -137,23 +207,70 @@ class FakeStack { FakeFrameLifo call_stack_; }; -void *asan_memalign(uptr alignment, uptr size, AsanStackTrace *stack); -void asan_free(void *ptr, AsanStackTrace *stack); +void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, + AllocType alloc_type); +void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type); -void *asan_malloc(uptr size, AsanStackTrace *stack); -void *asan_calloc(uptr nmemb, uptr size, AsanStackTrace *stack); -void *asan_realloc(void *p, uptr size, AsanStackTrace *stack); -void *asan_valloc(uptr size, AsanStackTrace *stack); -void *asan_pvalloc(uptr size, AsanStackTrace *stack); +void *asan_malloc(uptr size, StackTrace *stack); +void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack); +void *asan_realloc(void *p, uptr size, StackTrace *stack); +void *asan_valloc(uptr size, StackTrace *stack); +void *asan_pvalloc(uptr size, StackTrace *stack); int asan_posix_memalign(void **memptr, uptr alignment, uptr size, - AsanStackTrace *stack); -uptr asan_malloc_usable_size(void *ptr, AsanStackTrace *stack); + StackTrace *stack); +uptr asan_malloc_usable_size(void *ptr, StackTrace *stack); uptr asan_mz_size(const void *ptr); void asan_mz_force_lock(); void asan_mz_force_unlock(); -void DescribeHeapAddress(uptr addr, uptr access_size); + +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 new file mode 100644 index 000000000000..42d8b29afd6b --- /dev/null +++ b/lib/asan/asan_allocator2.cc @@ -0,0 +1,710 @@ +//===-- asan_allocator2.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, 2-nd version. +// 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_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_internal_defs.h" +#include "sanitizer_common/sanitizer_list.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_quarantine.h" + +namespace __asan { + +struct AsanMapUnmapCallback { + void OnMap(uptr p, uptr size) const { + PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); + // Statistics. + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.mmaps++; + thread_stats.mmaped += size; + } + void OnUnmap(uptr p, uptr size) const { + PoisonShadow(p, size, 0); + // We are about to unmap a chunk of user memory. + // Mark the corresponding shadow memory as not needed. + // Since asan's mapping is compacting, the shadow chunk may be + // not page-aligned, so we only flush the page-aligned portion. + uptr page_size = GetPageSizeCached(); + uptr shadow_beg = RoundUpTo(MemToShadow(p), page_size); + uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size); + FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg); + // Statistics. + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.munmaps++; + thread_stats.munmaped += size; + } +}; + +#if SANITIZER_WORDSIZE == 64 +const uptr kAllocatorSpace = 0x600000000000ULL; +const uptr kAllocatorSize = 0x10000000000ULL; // 1T. +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; +typedef SizeClassAllocator32<0, kAddressSpaceSize, 16, + SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator; +#endif + +typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; +typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator; +typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, + SecondaryAllocator> Allocator; + +// We can not use THREADLOCAL because it is not supported on some of the +// platforms we care about (OSX 10.6, Android). +// static THREADLOCAL AllocatorCache cache; +AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) { + CHECK(ms); + CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator2_cache)); + return reinterpret_cast<AllocatorCache *>(ms->allocator2_cache); +} + +static Allocator allocator; + +static const uptr kMaxAllowedMallocSize = + FIRST_32_SECOND_64(3UL << 30, 8UL << 30); + +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. +// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone. +enum { + CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it. + CHUNK_ALLOCATED = 2, + CHUNK_QUARANTINE = 3 +}; + +// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits. +// We use adaptive redzones: for larger allocation larger redzones are used. +static u32 RZLog2Size(u32 rz_log) { + CHECK_LT(rz_log, 8); + return 16 << rz_log; +} + +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; + CHECK_EQ(rz_size, RZLog2Size(res)); + return res; +} + +static uptr ComputeRZLog(uptr user_requested_size) { + u32 rz_log = + user_requested_size <= 64 - 16 ? 0 : + user_requested_size <= 128 - 32 ? 1 : + user_requested_size <= 512 - 64 ? 2 : + user_requested_size <= 4096 - 128 ? 3 : + user_requested_size <= (1 << 14) - 256 ? 4 : + user_requested_size <= (1 << 15) - 512 ? 5 : + user_requested_size <= (1 << 16) - 1024 ? 6 : 7; + return Max(rz_log, RZSize2Log(flags()->redzone)); +} + +// The memory chunk allocated from the underlying allocator looks like this: +// L L L L L L H H U U U U U U R R +// L -- left redzone words (0 or more bytes) +// H -- ChunkHeader (16 bytes), which is also a part of the left redzone. +// U -- user memory. +// R -- right redzone (0 or more bytes) +// ChunkBase consists of ChunkHeader and other bytes that overlap with user +// memory. + +// If a memory chunk is allocated by memalign and we had to increase the +// allocation size to achieve the proper alignment, then we store this magic +// value in the first uptr word of the memory block and store the address of +// ChunkBase in the next uptr. +// M B ? ? ? L L L L L L H H U U U U U U +// M -- magic value kMemalignMagic +// B -- address of ChunkHeader pointing to the first 'H' +static const uptr kMemalignMagic = 0xCC6E96B9; + +struct ChunkHeader { + // 1-st 8 bytes. + u32 chunk_state : 8; // Must be first. + u32 alloc_tid : 24; + + u32 free_tid : 24; + u32 from_memalign : 1; + u32 alloc_type : 2; + u32 rz_log : 3; + // 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 + // SecondaryAllocator's metadata. + u32 user_requested_size; + u32 alloc_context_id; +}; + +struct ChunkBase : ChunkHeader { + // Header2, intersects with user memory. + AsanChunk *next; + u32 free_context_id; +}; + +static const uptr kChunkHeaderSize = sizeof(ChunkHeader); +static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize; +COMPILER_CHECK(kChunkHeaderSize == 16); +COMPILER_CHECK(kChunkHeader2Size <= 16); + +struct AsanChunk: ChunkBase { + uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; } + uptr UsedSize() { + if (user_requested_size != SizeClassMap::kMaxSize) + return user_requested_size; + return *reinterpret_cast<uptr *>(allocator.GetMetaData(AllocBeg())); + } + void *AllocBeg() { + if (from_memalign) + 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. + u32 *AllocStackBeg() { + return (u32*)(Beg() - RZLog2Size(rz_log)); + } + uptr AllocStackSize() { + CHECK_LE(RZLog2Size(rz_log), kChunkHeaderSize); + return (RZLog2Size(rz_log) - kChunkHeaderSize) / sizeof(u32); + } + u32 *FreeStackBeg() { + return (u32*)(Beg() + kChunkHeader2Size); + } + uptr FreeStackSize() { + if (user_requested_size < kChunkHeader2Size) return 0; + uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY); + return (available - kChunkHeader2Size) / sizeof(u32); + } +}; + +uptr AsanChunkView::Beg() { return chunk_->Beg(); } +uptr AsanChunkView::End() { return Beg() + UsedSize(); } +uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); } +uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; } +uptr AsanChunkView::FreeTid() { return chunk_->free_tid; } + +static void GetStackTraceFromId(u32 id, StackTrace *stack) { + CHECK(id); + uptr size = 0; + const uptr *trace = StackDepotGet(id, &size); + CHECK_LT(size, kStackTraceMax); + internal_memcpy(stack->trace, trace, sizeof(uptr) * size); + stack->size = size; +} + +void AsanChunkView::GetAllocStack(StackTrace *stack) { + if (flags()->use_stack_depot) + GetStackTraceFromId(chunk_->alloc_context_id, stack); + else + StackTrace::UncompressStack(stack, chunk_->AllocStackBeg(), + chunk_->AllocStackSize()); +} + +void AsanChunkView::GetFreeStack(StackTrace *stack) { + if (flags()->use_stack_depot) + GetStackTraceFromId(chunk_->free_context_id, stack); + else + StackTrace::UncompressStack(stack, chunk_->FreeStackBeg(), + chunk_->FreeStackSize()); +} + +struct QuarantineCallback; +typedef Quarantine<QuarantineCallback, AsanChunk> AsanQuarantine; +typedef AsanQuarantine::Cache QuarantineCache; +static AsanQuarantine quarantine(LINKER_INITIALIZED); +static QuarantineCache fallback_quarantine_cache(LINKER_INITIALIZED); +static AllocatorCache fallback_allocator_cache; +static SpinMutex fallback_mutex; + +QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) { + CHECK(ms); + CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache)); + return reinterpret_cast<QuarantineCache *>(ms->quarantine_cache); +} + +struct QuarantineCallback { + explicit QuarantineCallback(AllocatorCache *cache) + : cache_(cache) { + } + + void Recycle(AsanChunk *m) { + CHECK(m->chunk_state == CHUNK_QUARANTINE); + m->chunk_state = CHUNK_AVAILABLE; + CHECK_NE(m->alloc_tid, kInvalidTid); + CHECK_NE(m->free_tid, kInvalidTid); + PoisonShadow(m->Beg(), + RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), + kAsanHeapLeftRedzoneMagic); + void *p = reinterpret_cast<void *>(m->AllocBeg()); + if (m->from_memalign) { + uptr *memalign_magic = reinterpret_cast<uptr *>(p); + CHECK_EQ(memalign_magic[0], kMemalignMagic); + CHECK_EQ(memalign_magic[1], reinterpret_cast<uptr>(m)); + } + + // Statistics. + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.real_frees++; + thread_stats.really_freed += m->UsedSize(); + + allocator.Deallocate(cache_, p); + } + + void *Allocate(uptr size) { + return allocator.Allocate(cache_, size, 1, false); + } + + void Deallocate(void *p) { + allocator.Deallocate(cache_, p); + } + + AllocatorCache *cache_; +}; + +static void Init() { + static int inited = 0; + if (inited) return; + __asan_init(); + inited = true; // this must happen before any threads are created. + allocator.Init(); + quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine); +} + +static void *Allocate(uptr size, uptr alignment, StackTrace *stack, + AllocType alloc_type) { + Init(); + 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. + } + 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 needed_size = rounded_size + rz_size; + if (alignment > min_alignment) + needed_size += alignment; + bool using_primary_allocator = true; + // If we are allocating from the secondary allocator, there will be no + // automatic right redzone, so add the right redzone manually. + if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) { + needed_size += rz_size; + using_primary_allocator = false; + } + CHECK(IsAligned(needed_size, min_alignment)); + if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { + Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", + (void*)size); + return 0; + } + + AsanThread *t = asanThreadRegistry().GetCurrent(); + void *allocated; + if (t) { + AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); + allocated = allocator.Allocate(cache, needed_size, 8, false); + } else { + SpinMutexLock l(&fallback_mutex); + AllocatorCache *cache = &fallback_allocator_cache; + allocated = allocator.Allocate(cache, needed_size, 8, false); + } + uptr alloc_beg = reinterpret_cast<uptr>(allocated); + // Clear the first allocated word (an old kMemalignMagic may still be there). + reinterpret_cast<uptr *>(alloc_beg)[0] = 0; + uptr alloc_end = alloc_beg + needed_size; + uptr beg_plus_redzone = alloc_beg + rz_size; + uptr user_beg = beg_plus_redzone; + if (!IsAligned(user_beg, alignment)) + user_beg = RoundUpTo(user_beg, alignment); + uptr user_end = user_beg + size; + 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; + m->alloc_tid = alloc_tid; + CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield? + m->free_tid = kInvalidTid; + m->from_memalign = user_beg != beg_plus_redzone; + if (m->from_memalign) { + CHECK_LE(beg_plus_redzone + 2 * sizeof(uptr), user_beg); + uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg); + memalign_magic[0] = kMemalignMagic; + memalign_magic[1] = chunk_beg; + } + if (using_primary_allocator) { + CHECK(size); + m->user_requested_size = size; + CHECK(allocator.FromPrimary(allocated)); + } else { + CHECK(!allocator.FromPrimary(allocated)); + m->user_requested_size = SizeClassMap::kMaxSize; + uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(allocated)); + meta[0] = size; + meta[1] = chunk_beg; + } + + if (flags()->use_stack_depot) { + m->alloc_context_id = StackDepotPut(stack->trace, stack->size); + } else { + m->alloc_context_id = 0; + StackTrace::CompressStack(stack, m->AllocStackBeg(), m->AllocStackSize()); + } + + uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY); + // Unpoison the bulk of the memory region. + 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) { + u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity); + *shadow = size & (SHADOW_GRANULARITY - 1); + } + + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.mallocs++; + thread_stats.malloced += size; + thread_stats.malloced_redzones += needed_size - size; + uptr class_id = Min(kNumberOfSizeClasses, SizeClassMap::ClassID(needed_size)); + thread_stats.malloced_by_size[class_id]++; + if (needed_size > SizeClassMap::kMaxSize) + thread_stats.malloc_large++; + + void *res = reinterpret_cast<void *>(user_beg); + 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); + + // 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 (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch) + ReportAllocTypeMismatch((uptr)ptr, stack, + (AllocType)m->alloc_type, (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(); + m->free_tid = t ? t->tid() : 0; + if (flags()->use_stack_depot) { + m->free_context_id = StackDepotPut(stack->trace, stack->size); + } else { + 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(); + thread_stats.frees++; + thread_stats.freed += m->UsedSize(); + + // Push into quarantine. + if (t) { + AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); + AllocatorCache *ac = GetAllocatorCache(ms); + quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac), + m, m->UsedSize()); + } else { + SpinMutexLock l(&fallback_mutex); + AllocatorCache *ac = &fallback_allocator_cache; + quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac), + m, m->UsedSize()); + } + + ASAN_FREE_HOOK(ptr); +} + +static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) { + CHECK(old_ptr && new_size); + uptr p = reinterpret_cast<uptr>(old_ptr); + uptr chunk_beg = p - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); + + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.reallocs++; + thread_stats.realloced += new_size; + + CHECK(m->chunk_state == CHUNK_ALLOCATED); + uptr old_size = m->UsedSize(); + uptr memcpy_size = Min(new_size, old_size); + void *new_ptr = Allocate(new_size, 8, 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; +} + +static AsanChunk *GetAsanChunkByAddr(uptr p) { + void *ptr = reinterpret_cast<void *>(p); + uptr alloc_beg = reinterpret_cast<uptr>(allocator.GetBlockBegin(ptr)); + if (!alloc_beg) return 0; + uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg); + if (memalign_magic[0] == kMemalignMagic) { + AsanChunk *m = reinterpret_cast<AsanChunk *>(memalign_magic[1]); + CHECK(m->from_memalign); + return m; + } + if (!allocator.FromPrimary(ptr)) { + uptr *meta = reinterpret_cast<uptr *>( + allocator.GetMetaData(reinterpret_cast<void *>(alloc_beg))); + AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]); + return m; + } + uptr actual_size = allocator.GetActuallyAllocatedSize(ptr); + CHECK_LE(actual_size, SizeClassMap::kMaxSize); + // We know the actually allocted size, but we don't know the redzone size. + // Just try all possible redzone sizes. + for (u32 rz_log = 0; rz_log < 8; rz_log++) { + u32 rz_size = RZLog2Size(rz_log); + uptr max_possible_size = actual_size - rz_size; + if (ComputeRZLog(max_possible_size) != rz_log) + continue; + return reinterpret_cast<AsanChunk *>( + alloc_beg + rz_size - kChunkHeaderSize); + } + return 0; +} + +static uptr AllocationSize(uptr p) { + AsanChunk *m = GetAsanChunkByAddr(p); + if (!m) return 0; + if (m->chunk_state != CHUNK_ALLOCATED) return 0; + if (m->Beg() != p) return 0; + return m->UsedSize(); +} + +// 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 over freed chunk and freed chunk + // over available chunk. + if (left_chunk->chunk_state != right_chunk->chunk_state) { + if (left_chunk->chunk_state == CHUNK_ALLOCATED) + return left_chunk; + if (right_chunk->chunk_state == CHUNK_ALLOCATED) + return right_chunk; + if (left_chunk->chunk_state == CHUNK_QUARANTINE) + return left_chunk; + if (right_chunk->chunk_state == CHUNK_QUARANTINE) + return right_chunk; + } + // Same chunk_state: 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; +} + +AsanChunkView FindHeapChunkByAddress(uptr addr) { + AsanChunk *m1 = GetAsanChunkByAddr(addr); + if (!m1) return AsanChunkView(m1); + uptr 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. + // Search a bit to the left to see if there is another chunk. + AsanChunk *m2 = 0; + for (uptr l = 1; l < GetPageSizeCached(); l++) { + m2 = GetAsanChunkByAddr(addr - l); + if (m2 == m1) continue; // Still the same chunk. + break; + } + if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset)) + m1 = ChooseChunk(addr, m2, m1); + } + return AsanChunkView(m1); +} + +void AsanThreadLocalMallocStorage::CommitBack() { + AllocatorCache *ac = GetAllocatorCache(this); + quarantine.Drain(GetQuarantineCache(this), QuarantineCallback(ac)); + allocator.SwallowCache(GetAllocatorCache(this)); +} + +void PrintInternalAllocatorStats() { + allocator.PrintStats(); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, + AllocType alloc_type) { + return Allocate(size, alignment, stack, alloc_type); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { + Deallocate(ptr, stack, alloc_type); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *asan_malloc(uptr size, StackTrace *stack) { + return Allocate(size, 8, stack, FROM_MALLOC); +} + +void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { + void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC); + if (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); + if (size == 0) { + Deallocate(p, stack, FROM_MALLOC); + return 0; + } + return Reallocate(p, size, stack); +} + +void *asan_valloc(uptr size, StackTrace *stack) { + return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC); +} + +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; + } + return Allocate(size, PageSize, stack, FROM_MALLOC); +} + +int asan_posix_memalign(void **memptr, uptr alignment, uptr size, + StackTrace *stack) { + void *ptr = Allocate(size, alignment, stack, FROM_MALLOC); + CHECK(IsAligned((uptr)ptr, alignment)); + *memptr = ptr; + return 0; +} + +uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) { + CHECK(stack); + if (ptr == 0) return 0; + uptr usable_size = AllocationSize(reinterpret_cast<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) { + UNIMPLEMENTED(); + return 0; +} + +void asan_mz_force_lock() { + UNIMPLEMENTED(); +} + +void asan_mz_force_unlock() { + UNIMPLEMENTED(); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +// ASan allocator doesn't reserve extra bytes, so normally we would +// just return "size". We don't want to expose our redzone sizes, etc here. +uptr __asan_get_estimated_allocated_size(uptr size) { + return size; +} + +bool __asan_get_ownership(const void *p) { + uptr ptr = reinterpret_cast<uptr>(p); + return (ptr == kReturnOnZeroMalloc) || (AllocationSize(ptr) > 0); +} + +uptr __asan_get_allocated_size(const void *p) { + if (p == 0) return 0; + 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) { + GET_STACK_TRACE_FATAL_HERE; + ReportAsanGetAllocatedSizeNotOwned(ptr, &stack); + } + return allocated_size; +} + +#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 + + +#endif // ASAN_ALLOCATOR_VERSION diff --git a/lib/asan/asan_fake_stack.cc b/lib/asan/asan_fake_stack.cc new file mode 100644 index 000000000000..7c5a16312d46 --- /dev/null +++ b/lib/asan/asan_fake_stack.cc @@ -0,0 +1,182 @@ +//===-- asan_fake_stack.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. +// +// FakeStack is used to detect use-after-return bugs. +//===----------------------------------------------------------------------===// +#include "asan_allocator.h" +#include "asan_thread.h" +#include "asan_thread_registry.h" +#include "sanitizer/asan_interface.h" + +namespace __asan { + +FakeStack::FakeStack() { + CHECK(REAL(memset) != 0); + REAL(memset)(this, 0, sizeof(*this)); +} + +bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) { + uptr mem = allocated_size_classes_[size_class]; + uptr size = ClassMmapSize(size_class); + bool res = mem && addr >= mem && addr < mem + size; + return res; +} + +uptr FakeStack::AddrIsInFakeStack(uptr addr) { + for (uptr i = 0; i < kNumberOfSizeClasses; i++) { + if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i]; + } + return 0; +} + +// We may want to compute this during compilation. +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))); + uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog; + CHECK(res < kNumberOfSizeClasses); + CHECK(ClassSize(res) >= rounded_size); + return res; +} + +void FakeFrameFifo::FifoPush(FakeFrame *node) { + CHECK(node); + node->next = 0; + if (first_ == 0 && last_ == 0) { + first_ = last_ = node; + } else { + CHECK(first_); + CHECK(last_); + last_->next = node; + last_ = node; + } +} + +FakeFrame *FakeFrameFifo::FifoPop() { + CHECK(first_ && last_ && "Exhausted fake stack"); + FakeFrame *res = 0; + if (first_ == last_) { + res = first_; + first_ = last_ = 0; + } else { + res = first_; + first_ = first_->next; + } + return res; +} + +void FakeStack::Init(uptr stack_size) { + stack_size_ = stack_size; + alive_ = true; +} + +void FakeStack::Cleanup() { + alive_ = false; + for (uptr i = 0; i < kNumberOfSizeClasses; i++) { + uptr mem = allocated_size_classes_[i]; + if (mem) { + PoisonShadow(mem, ClassMmapSize(i), 0); + allocated_size_classes_[i] = 0; + UnmapOrDie((void*)mem, ClassMmapSize(i)); + } + } +} + +uptr FakeStack::ClassMmapSize(uptr size_class) { + return RoundUpToPowerOfTwo(stack_size_); +} + +void FakeStack::AllocateOneSizeClass(uptr size_class) { + CHECK(ClassMmapSize(size_class) >= GetPageSizeCached()); + uptr new_mem = (uptr)MmapOrDie( + ClassMmapSize(size_class), __FUNCTION__); + // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n", + // asanThreadRegistry().GetCurrent()->tid(), + // size_class, new_mem, new_mem + ClassMmapSize(size_class), + // ClassMmapSize(size_class)); + uptr i; + for (i = 0; i < ClassMmapSize(size_class); + i += ClassSize(size_class)) { + size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i)); + } + CHECK(i == ClassMmapSize(size_class)); + allocated_size_classes_[size_class] = new_mem; +} + +uptr FakeStack::AllocateStack(uptr size, uptr real_stack) { + if (!alive_) return real_stack; + CHECK(size <= kMaxStackMallocSize && size > 1); + uptr size_class = ComputeSizeClass(size); + if (!allocated_size_classes_[size_class]) { + AllocateOneSizeClass(size_class); + } + FakeFrame *fake_frame = size_classes_[size_class].FifoPop(); + CHECK(fake_frame); + fake_frame->size_minus_one = size - 1; + fake_frame->real_stack = real_stack; + while (FakeFrame *top = call_stack_.top()) { + if (top->real_stack > real_stack) break; + call_stack_.LifoPop(); + DeallocateFrame(top); + } + call_stack_.LifoPush(fake_frame); + uptr ptr = (uptr)fake_frame; + PoisonShadow(ptr, size, 0); + return ptr; +} + +void FakeStack::DeallocateFrame(FakeFrame *fake_frame) { + CHECK(alive_); + uptr size = fake_frame->size_minus_one + 1; + uptr size_class = ComputeSizeClass(size); + CHECK(allocated_size_classes_[size_class]); + uptr ptr = (uptr)fake_frame; + CHECK(AddrIsInSizeClass(ptr, size_class)); + CHECK(AddrIsInSizeClass(ptr + size - 1, size_class)); + size_classes_[size_class].FifoPush(fake_frame); +} + +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); + PoisonShadow(ptr, size, kAsanStackAfterReturnMagic); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +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(); + if (!t) { + // TSD is gone, use the real stack. + return real_stack; + } + uptr ptr = t->fake_stack().AllocateStack(size, real_stack); + // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack); + return ptr; +} + +void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) { + if (!flags()->use_fake_stack) return; + if (ptr != real_stack) { + FakeStack::OnFree(ptr, size, real_stack); + } +} diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h index ca9cf84ba6c5..d7b21ea4a45f 100644 --- a/lib/asan/asan_flags.h +++ b/lib/asan/asan_flags.h @@ -15,7 +15,7 @@ #ifndef ASAN_FLAGS_H #define ASAN_FLAGS_H -#include "sanitizer_common/sanitizer_interface_defs.h" +#include "sanitizer/common_interface_defs.h" // ASan flag values can be defined in three ways: // 1) initialized with default values at startup. @@ -23,11 +23,6 @@ // __asan_default_options(). // 3) overriden from env variable ASAN_OPTIONS. -extern "C" { -// Can be overriden by user. -const char *__asan_default_options() SANITIZER_WEAK_ATTRIBUTE; -} // extern "C" - namespace __asan { struct Flags { @@ -48,7 +43,9 @@ struct Flags { // on globals, 1 - detect buffer overflow, 2 - print data about registered // globals). int report_globals; - // Max number of stack frames kept for each allocation. + // 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. @@ -87,6 +84,28 @@ struct Flags { // By default, disable core dumper on 64-bit - it makes little sense // to dump 16T+ core. bool disable_core; + // 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; + // Report errors on malloc/delete, new/free, new/delete[], etc. + bool alloc_dealloc_mismatch; + // Use stack depot instead of storing stacks in the redzones. + bool use_stack_depot; }; Flags *flags(); diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc index f8c4040b8e86..4e18bb8e2355 100644 --- a/lib/asan/asan_globals.cc +++ b/lib/asan/asan_globals.cc @@ -12,15 +12,14 @@ // Handle globals. //===----------------------------------------------------------------------===// #include "asan_interceptors.h" -#include "asan_interface.h" #include "asan_internal.h" -#include "asan_lock.h" #include "asan_mapping.h" +#include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" #include "asan_thread.h" - -#include <ctype.h> +#include "sanitizer/asan_interface.h" +#include "sanitizer_common/sanitizer_mutex.h" namespace __asan { @@ -31,9 +30,10 @@ struct ListOfGlobals { ListOfGlobals *next; }; -static AsanLock mu_for_globals(LINKER_INITIALIZED); -static ListOfGlobals *list_of_globals; -static LowLevelAllocator allocator_for_globals(LINKER_INITIALIZED); +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; @@ -55,48 +55,16 @@ void PoisonRedZones(const Global &g) { } } -static uptr GetAlignedSize(uptr size) { - return ((size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone) - * kGlobalAndStackRedzone; -} - - // Check if the global is a zero-terminated ASCII string. If so, print it. -void PrintIfASCII(const Global &g) { - for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { - if (!isascii(*(char*)p)) return; - } - if (*(char*)(g.beg + g.size - 1) != 0) return; - AsanPrintf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg); -} - -bool DescribeAddrIfMyRedZone(const Global &g, uptr addr) { - if (addr < g.beg - kGlobalAndStackRedzone) return false; - if (addr >= g.beg + g.size_with_redzone) return false; - AsanPrintf("%p is located ", (void*)addr); - if (addr < g.beg) { - AsanPrintf("%zd bytes to the left", g.beg - addr); - } else if (addr >= g.beg + g.size) { - AsanPrintf("%zd bytes to the right", addr - (g.beg + g.size)); - } else { - AsanPrintf("%zd bytes inside", addr - g.beg); // Can it happen? - } - AsanPrintf(" of global variable '%s' (0x%zx) of size %zu\n", - g.name, g.beg, g.size); - PrintIfASCII(g); - return true; -} - - -bool DescribeAddrIfGlobal(uptr addr) { +bool DescribeAddressIfGlobal(uptr addr) { if (!flags()->report_globals) return false; - ScopedLock lock(&mu_for_globals); + BlockingMutexLock lock(&mu_for_globals); bool res = false; - for (ListOfGlobals *l = list_of_globals; l; l = l->next) { + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { const Global &g = *l->g; if (flags()->report_globals >= 2) - AsanPrintf("Search Global: beg=%p size=%zu name=%s\n", - (void*)g.beg, g.size, (char*)g.name); - res |= DescribeAddrIfMyRedZone(g, addr); + Report("Search Global: beg=%p size=%zu name=%s\n", + (void*)g.beg, g.size, (char*)g.name); + res |= DescribeAddressRelativeToGlobal(addr, g); } return res; } @@ -106,6 +74,10 @@ bool DescribeAddrIfGlobal(uptr addr) { // so we store the globals in a map. 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); CHECK(flags()->report_globals); CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); @@ -114,11 +86,14 @@ static void RegisterGlobal(const Global *g) { ListOfGlobals *l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); l->g = g; - l->next = list_of_globals; - list_of_globals = l; - if (flags()->report_globals >= 2) - Report("Added Global: beg=%p size=%zu name=%s\n", - (void*)g->beg, g->size, g->name); + 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; + } } static void UnregisterGlobal(const Global *g) { @@ -133,39 +108,83 @@ static void UnregisterGlobal(const Global *g) { // 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); +} + } // namespace __asan // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT -// Register one global with a default redzone. -void __asan_register_global(uptr addr, uptr size, - const char *name) { - if (!flags()->report_globals) return; - ScopedLock lock(&mu_for_globals); - Global *g = (Global *)allocator_for_globals.Allocate(sizeof(Global)); - g->beg = addr; - g->size = size; - g->size_with_redzone = GetAlignedSize(size) + kGlobalAndStackRedzone; - g->name = name; - RegisterGlobal(g); -} - // Register an array of globals. void __asan_register_globals(__asan_global *globals, uptr n) { if (!flags()->report_globals) return; - ScopedLock lock(&mu_for_globals); + BlockingMutexLock lock(&mu_for_globals); for (uptr i = 0; i < n; i++) { RegisterGlobal(&globals[i]); } } // Unregister an array of globals. -// We must do it when a shared objects gets dlclosed. +// We must do this when a shared objects gets dlclosed. void __asan_unregister_globals(__asan_global *globals, uptr n) { if (!flags()->report_globals) return; - ScopedLock lock(&mu_for_globals); + BlockingMutexLock lock(&mu_for_globals); for (uptr i = 0; i < n; i++) { UnregisterGlobal(&globals[i]); } } + +// This method runs immediately prior to dynamic initialization in each TU, +// 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); + 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; + } + 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; + BlockingMutexLock lock(&mu_for_globals); + for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) + UnpoisonGlobal(l->g); +} diff --git a/lib/asan/asan_intercepted_functions.h b/lib/asan/asan_intercepted_functions.h new file mode 100644 index 000000000000..a1faf713c130 --- /dev/null +++ b/lib/asan/asan_intercepted_functions.h @@ -0,0 +1,260 @@ +//===-- asan_intercepted_functions.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 containing prototypes for wrapper functions and wrappers +//===----------------------------------------------------------------------===// +#ifndef ASAN_INTERCEPTED_FUNCTIONS_H +#define ASAN_INTERCEPTED_FUNCTIONS_H + +#include "asan_internal.h" +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_platform_interceptors.h" + +#include <stdarg.h> + +using __sanitizer::uptr; + +// Use macro to describe if specific function should be +// intercepted on a given platform. +#if !defined(_WIN32) +# 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 +#else +# 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__) +# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1 +#else +# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0 +#endif + +#if !defined(__APPLE__) +# define ASAN_INTERCEPT_STRNLEN 1 +#else +# define ASAN_INTERCEPT_STRNLEN 0 +#endif + +#if defined(__linux__) && !defined(ANDROID) +# define ASAN_INTERCEPT_SWAPCONTEXT 1 +#else +# define ASAN_INTERCEPT_SWAPCONTEXT 0 +#endif + +#if !defined(ANDROID) && !defined(_WIN32) +# 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) +# define ASAN_INTERCEPT_SIGLONGJMP 1 +#else +# define ASAN_INTERCEPT_SIGLONGJMP 0 +#endif + +#if ASAN_HAS_EXCEPTIONS && !defined(_WIN32) +# 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); +#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 + +// 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 + +#endif // ASAN_INTERCEPTED_FUNCTIONS_H diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc index 2ce5826e1495..6170974d6f5e 100644 --- a/lib/asan/asan_interceptors.cc +++ b/lib/asan/asan_interceptors.cc @@ -14,136 +14,33 @@ #include "asan_interceptors.h" #include "asan_allocator.h" -#include "asan_interface.h" +#include "asan_intercepted_functions.h" #include "asan_internal.h" #include "asan_mapping.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" -// Use macro to describe if specific function should be -// intercepted on a given platform. -#if !defined(_WIN32) -# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1 -#else -# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0 -#endif - -#if !defined(__APPLE__) -# define ASAN_INTERCEPT_STRNLEN 1 -#else -# define ASAN_INTERCEPT_STRNLEN 0 -#endif - -#if defined(ANDROID) || defined(_WIN32) -# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0 -#else -# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1 -#endif - -// 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; -int sigaction(int sig, const struct sigaction *act, - struct sigaction *oldact); -void *signal(int signum, void *handler); -# endif - -// setjmp.h -void longjmp(void* env, int value); -# if !defined(_WIN32) -void _longjmp(void *env, int value); -# endif - -// string.h / strings.h -int memcmp(const void *a1, const void *a2, uptr size); -void* memmove(void *to, const void *from, uptr size); -void* memcpy(void *to, const void *from, uptr size); -void* memset(void *block, int c, uptr size); -char* strchr(const char *str, int c); -# if defined(__APPLE__) -char* index(const char *string, int c); -# endif -char* strcat(char *to, const char* from); // NOLINT -char *strncat(char *to, const char* from, uptr size); -char* strcpy(char *to, const char* from); // NOLINT -char* strncpy(char *to, const char* from, uptr size); -int strcmp(const char *s1, const char* s2); -int strncmp(const char *s1, const char* s2, uptr size); -# if !defined(_WIN32) -int strcasecmp(const char *s1, const char *s2); -int strncasecmp(const char *s1, const char *s2, uptr n); -char* strdup(const char *s); -# endif -uptr strlen(const char *s); -# if ASAN_INTERCEPT_STRNLEN -uptr strnlen(const char *s, uptr maxlen); -# endif - -// stdlib.h -int atoi(const char *nptr); -long atol(const char *nptr); // NOLINT -long strtol(const char *nptr, char **endptr, int base); // NOLINT -# if ASAN_INTERCEPT_ATOLL_AND_STRTOLL -long long atoll(const char *nptr); // NOLINT -long long strtoll(const char *nptr, char **endptr, int base); // NOLINT -# endif - -// 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 !defined(_WIN32) -int pthread_create(void *thread, void *attr, void *(*start_routine)(void*), - void *arg); -# endif -} // extern "C" -#endif - namespace __asan { -// Instruments read/write access to a single byte in memory. -// On error calls __asan_report_error, which aborts the program. -#define ACCESS_ADDRESS(address, isWrite) do { \ - if (!AddrIsInMem(address) || AddressIsPoisoned(address)) { \ - GET_CURRENT_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, address, isWrite, /* access_size */ 1); \ - } \ -} while (0) - // We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE, // and ASAN_WRITE_RANGE as macro instead of function so // that no extra frames are created, and stack trace contains // relevant information only. - -// Instruments read/write access to a memory range. -// More complex implementation is possible, for now just -// checking the first and the last byte of a range. -#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \ - if (size > 0) { \ - uptr ptr = (uptr)(offset); \ - ACCESS_ADDRESS(ptr, isWrite); \ - ACCESS_ADDRESS(ptr + (size) - 1, isWrite); \ - } \ +// 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 ASAN_READ_RANGE(offset, size) do { \ - ACCESS_MEMORY_RANGE(offset, size, false); \ -} while (0) - -#define ASAN_WRITE_RANGE(offset, size) do { \ - ACCESS_MEMORY_RANGE(offset, size, true); \ -} while (0) +#define ASAN_READ_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, false) +#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true); // Behavior of functions like "memcpy" or "strcpy" is undefined // if memory intervals overlap. We report error in this case. @@ -156,11 +53,9 @@ static inline bool RangesOverlap(const char *offset1, uptr length1, const char *offset1 = (const char*)_offset1; \ const char *offset2 = (const char*)_offset2; \ if (RangesOverlap(offset1, length1, offset2, length2)) { \ - AsanReport("ERROR: AddressSanitizer %s-param-overlap: " \ - "memory ranges [%p,%p) and [%p, %p) overlap\n", \ - name, offset1, offset1 + length1, offset2, offset2 + length2); \ - PRINT_CURRENT_STACK(); \ - ShowStatsAndAbort(); \ + GET_STACK_TRACE_FATAL_HERE; \ + ReportStringFunctionMemoryRangesOverlap(name, offset1, length1, \ + offset2, length2, &stack); \ } \ } while (0) @@ -180,27 +75,47 @@ static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { return internal_strnlen(s, maxlen); } +void SetThreadName(const char *name) { + AsanThread *t = asanThreadRegistry().GetCurrent(); + if (t) + t->summary()->set_name(name); +} + } // namespace __asan // ---------------------- Wrappers ---------------- {{{1 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(); \ + } 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" + static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { AsanThread *t = (AsanThread*)arg; asanThreadRegistry().SetCurrent(t); return t->ThreadStart(); } -#ifndef _WIN32 +#if ASAN_INTERCEPT_PTHREAD_CREATE INTERCEPTOR(int, pthread_create, void *thread, void *attr, void *(*start_routine)(void*), void *arg) { - GET_STACK_TRACE_HERE(kStackTraceMax); + GET_STACK_TRACE_THREAD; u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack); asanThreadRegistry().RegisterThread(t); return REAL(pthread_create)(thread, attr, asan_thread_start, t); } -#endif // !_WIN32 +#endif // ASAN_INTERCEPT_PTHREAD_CREATE #if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION INTERCEPTOR(void*, signal, int signum, void *handler) { @@ -223,28 +138,62 @@ DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact); #endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION +#if ASAN_INTERCEPT_SWAPCONTEXT +static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) { + // Align to page size. + uptr PageSize = GetPageSizeCached(); + uptr bottom = stack & ~(PageSize - 1); + ssize += stack - bottom; + ssize = RoundUpTo(ssize, PageSize); + static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb + if (ssize && ssize <= kMaxSaneContextStackSize) { + PoisonShadow(bottom, ssize, 0); + } +} + +INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp, + struct ucontext_t *ucp) { + static bool reported_warning = false; + if (!reported_warning) { + Report("WARNING: ASan doesn't fully support makecontext/swapcontext " + "functions and may produce false positives in some cases!\n"); + reported_warning = true; + } + // Clear shadow memory for new context (it may share stack + // with current context). + uptr stack, ssize; + ReadContextStack(ucp, &stack, &ssize); + ClearShadowMemoryForContextStack(stack, ssize); + int res = REAL(swapcontext)(oucp, ucp); + // swapcontext technically does not return, but program may swap context to + // "oucp" later, that would look as if swapcontext() returned 0. + // We need to clear shadow for ucp once again, as it may be in arbitrary + // state. + ClearShadowMemoryForContextStack(stack, ssize); + return res; +} +#endif // ASAN_INTERCEPT_SWAPCONTEXT + INTERCEPTOR(void, longjmp, void *env, int val) { __asan_handle_no_return(); REAL(longjmp)(env, val); } -#if !defined(_WIN32) +#if ASAN_INTERCEPT__LONGJMP INTERCEPTOR(void, _longjmp, void *env, int val) { __asan_handle_no_return(); REAL(_longjmp)(env, val); } +#endif +#if ASAN_INTERCEPT_SIGLONGJMP INTERCEPTOR(void, siglongjmp, void *env, int val) { __asan_handle_no_return(); REAL(siglongjmp)(env, val); } #endif -#if ASAN_HAS_EXCEPTIONS == 1 -#ifdef __APPLE__ -extern "C" void __cxa_throw(void *a, void *b, void *c); -#endif // __APPLE__ - +#if ASAN_INTERCEPT___CXA_THROW INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) { CHECK(REAL(__cxa_throw)); __asan_handle_no_return(); @@ -263,26 +212,22 @@ static void MlockIsUnsupported() { } extern "C" { -INTERCEPTOR_ATTRIBUTE -int mlock(const void *addr, uptr len) { +INTERCEPTOR(int, mlock, const void *addr, uptr len) { MlockIsUnsupported(); return 0; } -INTERCEPTOR_ATTRIBUTE -int munlock(const void *addr, uptr len) { +INTERCEPTOR(int, munlock, const void *addr, uptr len) { MlockIsUnsupported(); return 0; } -INTERCEPTOR_ATTRIBUTE -int mlockall(int flags) { +INTERCEPTOR(int, mlockall, int flags) { MlockIsUnsupported(); return 0; } -INTERCEPTOR_ATTRIBUTE -int munlockall(void) { +INTERCEPTOR(int, munlockall, void) { MlockIsUnsupported(); return 0; } @@ -299,6 +244,7 @@ static inline int CharCaseCmp(unsigned char c1, unsigned char c2) { } 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; @@ -315,6 +261,7 @@ INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { } INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { + if (!asan_inited) return internal_memcpy(to, from, size); // memcpy is called during __asan_init() from the internals // of printf(...). if (asan_init_is_running) { @@ -327,25 +274,39 @@ INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { // See http://llvm.org/bugs/show_bug.cgi?id=11763. CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); } - ASAN_WRITE_RANGE(from, size); - ASAN_READ_RANGE(to, 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) { + if (!asan_inited) return internal_memmove(to, from, size); if (asan_init_is_running) { return REAL(memmove)(to, from, size); } ENSURE_ASAN_INITED(); if (flags()->replace_intrin) { - ASAN_WRITE_RANGE(from, size); - ASAN_READ_RANGE(to, 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) { + if (!asan_inited) return internal_memset(block, c, size); // memset is called inside Printf. if (asan_init_is_running) { return REAL(memset)(block, c, size); @@ -358,6 +319,12 @@ INTERCEPTOR(void*, memset, void *block, int c, uptr size) { } INTERCEPTOR(char*, strchr, const char *str, int c) { + if (!asan_inited) return internal_strchr(str, c); + // strchr is called inside create_purgeable_zone() when MallocGuardEdges=1 is + // used. + if (asan_init_is_running) { + return REAL(strchr)(str, c); + } ENSURE_ASAN_INITED(); char *result = REAL(strchr)(str, c); if (flags()->replace_str) { @@ -367,37 +334,31 @@ INTERCEPTOR(char*, strchr, const char *str, int c) { return result; } -#ifdef __linux__ +#if ASAN_INTERCEPT_INDEX +# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX INTERCEPTOR(char*, index, const char *string, int c) ALIAS(WRAPPER_NAME(strchr)); -#else +# else DEFINE_REAL(char*, index, const char *string, int c) -#endif - -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); -} +# endif +#endif // ASAN_INTERCEPT_INDEX +// For both strcat() and strncat() we need to check the validity of |to| +// argument irrespective of the |from| length. INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT ENSURE_ASAN_INITED(); if (flags()->replace_str) { uptr from_length = REAL(strlen)(from); ASAN_READ_RANGE(from, from_length + 1); + uptr to_length = REAL(strlen)(to); + ASAN_READ_RANGE(to, to_length); + ASAN_WRITE_RANGE(to + to_length, from_length + 1); + // If the copying actually happens, the |from| string should not overlap + // with the resulting string starting at |to|, which has a length of + // to_length + from_length + 1. if (from_length > 0) { - uptr to_length = REAL(strlen)(to); - ASAN_READ_RANGE(to, to_length); - ASAN_WRITE_RANGE(to + to_length, from_length + 1); - CHECK_RANGES_OVERLAP("strcat", to, to_length + 1, from, from_length + 1); + CHECK_RANGES_OVERLAP("strcat", to, from_length + to_length + 1, + from, from_length + 1); } } return REAL(strcat)(to, from); // NOLINT @@ -405,23 +366,25 @@ INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { ENSURE_ASAN_INITED(); - if (flags()->replace_str && size > 0) { + if (flags()->replace_str) { uptr from_length = MaybeRealStrnlen(from, size); - ASAN_READ_RANGE(from, Min(size, from_length + 1)); + uptr copy_length = Min(size, from_length + 1); + ASAN_READ_RANGE(from, copy_length); uptr to_length = REAL(strlen)(to); ASAN_READ_RANGE(to, to_length); ASAN_WRITE_RANGE(to + to_length, from_length + 1); if (from_length > 0) { - CHECK_RANGES_OVERLAP("strncat", to, to_length + 1, - from, Min(size, from_length + 1)); + CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1, + from, copy_length); } } return REAL(strncat)(to, from, size); } INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { - if (!asan_inited) { - return internal_strcmp(s1, s2); + if (!asan_inited) return internal_strcmp(s1, s2); + if (asan_init_is_running) { + return REAL(strcmp)(s1, s2); } ENSURE_ASAN_INITED(); unsigned char c1, c2; @@ -437,6 +400,9 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { } INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT +#if MAC_INTERPOSE_FUNCTIONS + if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT +#endif // strcpy is called from malloc_default_purgeable_zone() // in __asan::ReplaceSystemAlloc() on Mac. if (asan_init_is_running) { @@ -452,7 +418,17 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT return REAL(strcpy)(to, from); // NOLINT } +#if ASAN_INTERCEPT_STRDUP INTERCEPTOR(char*, strdup, const char *s) { +#if MAC_INTERPOSE_FUNCTIONS + // 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 + // starts using mmap() instead. + // See also http://code.google.com/p/address-sanitizer/issues/detail?id=123. + if (!asan_inited) return REAL(strdup)(s); +#endif + if (!asan_inited) return internal_strdup(s); ENSURE_ASAN_INITED(); if (flags()->replace_str) { uptr length = REAL(strlen)(s); @@ -460,8 +436,10 @@ INTERCEPTOR(char*, strdup, const char *s) { } return REAL(strdup)(s); } +#endif INTERCEPTOR(uptr, strlen, const char *s) { + if (!asan_inited) return internal_strlen(s); // strlen is called from malloc_default_purgeable_zone() // in __asan::ReplaceSystemAlloc() on Mac. if (asan_init_is_running) { @@ -475,6 +453,21 @@ 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; @@ -488,8 +481,10 @@ INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, uptr 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() // in __asan::ReplaceSystemAlloc() on Mac. if (asan_init_is_running) { @@ -566,6 +561,9 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT } INTERCEPTOR(int, atoi, const char *nptr) { +#if MAC_INTERPOSE_FUNCTIONS + if (!asan_inited) return REAL(atoi)(nptr); +#endif ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(atoi)(nptr); @@ -582,6 +580,9 @@ INTERCEPTOR(int, atoi, const char *nptr) { } INTERCEPTOR(long, atol, const char *nptr) { // NOLINT +#if MAC_INTERPOSE_FUNCTIONS + if (!asan_inited) return REAL(atol)(nptr); +#endif ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(atol)(nptr); @@ -638,7 +639,7 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread, void* security, uptr stack_size, DWORD (__stdcall *start_routine)(void*), void* arg, DWORD flags, void* tid) { - GET_STACK_TRACE_HERE(kStackTraceMax); + GET_STACK_TRACE_THREAD; u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack); asanThreadRegistry().RegisterThread(t); @@ -660,6 +661,12 @@ void InitializeAsanInterceptors() { static bool was_called_once; CHECK(was_called_once == false); was_called_once = true; +#if MAC_INTERPOSE_FUNCTIONS + return; +#endif + + SANITIZER_COMMON_INTERCEPTORS_INIT; + // Intercept mem* functions. ASAN_INTERCEPT_FUNC(memcmp); ASAN_INTERCEPT_FUNC(memmove); @@ -667,7 +674,11 @@ void InitializeAsanInterceptors() { if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { ASAN_INTERCEPT_FUNC(memcpy); } else { - REAL(memcpy) = REAL(memmove); +#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. @@ -679,19 +690,23 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(strncat); ASAN_INTERCEPT_FUNC(strncmp); ASAN_INTERCEPT_FUNC(strncpy); -#if !defined(_WIN32) +#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP ASAN_INTERCEPT_FUNC(strcasecmp); - ASAN_INTERCEPT_FUNC(strdup); ASAN_INTERCEPT_FUNC(strncasecmp); -# ifndef __APPLE__ +#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 ASAN_INTERCEPT_FUNC(index); # else CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr))); # endif #endif -#if ASAN_INTERCEPT_STRNLEN - ASAN_INTERCEPT_FUNC(strnlen); -#endif ASAN_INTERCEPT_FUNC(atoi); ASAN_INTERCEPT_FUNC(atol); @@ -701,25 +716,37 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(strtoll); #endif +#if ASAN_INTERCEPT_MLOCKX + // Intercept mlock/munlock. + ASAN_INTERCEPT_FUNC(mlock); + ASAN_INTERCEPT_FUNC(munlock); + ASAN_INTERCEPT_FUNC(mlockall); + ASAN_INTERCEPT_FUNC(munlockall); +#endif + // Intecept signal- and jump-related functions. ASAN_INTERCEPT_FUNC(longjmp); #if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION ASAN_INTERCEPT_FUNC(sigaction); ASAN_INTERCEPT_FUNC(signal); #endif - -#if !defined(_WIN32) +#if ASAN_INTERCEPT_SWAPCONTEXT + ASAN_INTERCEPT_FUNC(swapcontext); +#endif +#if ASAN_INTERCEPT__LONGJMP ASAN_INTERCEPT_FUNC(_longjmp); - INTERCEPT_FUNCTION(__cxa_throw); -# if !defined(__APPLE__) - // On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it - // there. +#endif +#if ASAN_INTERCEPT_SIGLONGJMP ASAN_INTERCEPT_FUNC(siglongjmp); -# endif +#endif + + // Intercept exception handling functions. +#if ASAN_INTERCEPT___CXA_THROW + INTERCEPT_FUNCTION(__cxa_throw); #endif // Intercept threading-related functions -#if !defined(_WIN32) +#if ASAN_INTERCEPT_PTHREAD_CREATE ASAN_INTERCEPT_FUNC(pthread_create); #endif diff --git a/lib/asan/asan_interceptors.h b/lib/asan/asan_interceptors.h index 32816920f7a3..3b3e90ef93ff 100644 --- a/lib/asan/asan_interceptors.h +++ b/lib/asan/asan_interceptors.h @@ -24,6 +24,7 @@ DECLARE_REAL(char*, strchr, const char *str, int c) DECLARE_REAL(uptr, strlen, const char *s) DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size) DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen) +DECLARE_REAL(char*, strstr, const char *s1, const char *s2) struct sigaction; DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact) diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h index 8c1f32028f2c..5d3bffa814da 100644 --- a/lib/asan/asan_internal.h +++ b/lib/asan/asan_internal.h @@ -17,17 +17,13 @@ #include "asan_flags.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 -#if defined(_WIN32) -extern "C" void* _ReturnAddress(void); -# pragma intrinsic(_ReturnAddress) -#endif // defined(_WIN32) - #define ASAN_DEFAULT_FAILURE_EXITCODE 1 #if defined(__linux__) @@ -48,6 +44,13 @@ extern "C" void* _ReturnAddress(void); # 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) @@ -59,7 +62,11 @@ extern "C" void* _ReturnAddress(void); // If set, asan will install its own SEGV signal handler. #ifndef ASAN_NEEDS_SEGV -# define ASAN_NEEDS_SEGV 1 +# if ASAN_ANDROID == 1 +# define ASAN_NEEDS_SEGV 0 +# else +# define ASAN_NEEDS_SEGV 1 +# endif #endif // If set, asan will intercept C++ exception api call(s). @@ -76,7 +83,11 @@ extern "C" void* _ReturnAddress(void); // If set, values like allocator chunk size, as well as defaults for some flags // will be changed towards less memory overhead. #ifndef ASAN_LOW_MEMORY -# define ASAN_LOW_MEMORY 0 +#if SANITIZER_WORDSIZE == 32 +# define ASAN_LOW_MEMORY 1 +#else +# define ASAN_LOW_MEMORY 0 +# endif #endif // All internal functions in asan reside inside the __asan namespace @@ -86,14 +97,11 @@ extern "C" void* _ReturnAddress(void); namespace __asan { class AsanThread; -struct AsanStackTrace; +using __sanitizer::StackTrace; // asan_rtl.cc void NORETURN ShowStatsAndAbort(); -// asan_globals.cc -bool DescribeAddrIfGlobal(uptr addr); - void ReplaceOperatorsNewAndDelete(); // asan_malloc_linux.cc / asan_malloc_mac.cc void ReplaceSystemMalloc(); @@ -103,10 +111,12 @@ void *AsanDoesNotSupportStaticLinkage(); void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); +void MaybeReexec(); bool AsanInterceptsSignal(int signum); void SetAlternateSignalStack(); void UnsetAlternateSignalStack(); void InstallSignalHandlers(); +void ReadContextStack(void *context, uptr *stack, uptr *ssize); void AsanPlatformThreadInit(); // Wrapper for TLS/TSD. @@ -115,9 +125,6 @@ void *AsanTSDGet(); void AsanTSDSet(void *tsd); void AppendToErrorMessageBuffer(const char *buffer); -// asan_printf.cc -void AsanPrintf(const char *format, ...); -void AsanReport(const char *format, ...); // asan_poisoning.cc // Poisons the shadow memory for "size" bytes starting from "addr". @@ -138,33 +145,20 @@ bool PlatformHasDifferentMemcpyAndMemmove(); # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true #endif // __APPLE__ +// Add convenient macro for interface functions that may be represented as +// weak hooks. +#define ASAN_MALLOC_HOOK(ptr, size) \ + if (&__asan_malloc_hook) __asan_malloc_hook(ptr, size) +#define ASAN_FREE_HOOK(ptr) \ + if (&__asan_free_hook) __asan_free_hook(ptr) +#define ASAN_ON_ERROR() \ + if (&__asan_on_error) __asan_on_error() + extern int asan_inited; // Used to avoid infinite recursion in __asan_init(). extern bool asan_init_is_running; extern void (*death_callback)(void); -enum LinkerInitialized { LINKER_INITIALIZED = 0 }; - -#define ASAN_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) - -#if !defined(_WIN32) || defined(__clang__) -# define GET_CALLER_PC() (uptr)__builtin_return_address(0) -# define GET_CURRENT_FRAME() (uptr)__builtin_frame_address(0) -#else -# define GET_CALLER_PC() (uptr)_ReturnAddress() -// CaptureStackBackTrace doesn't need to know BP on Windows. -// FIXME: This macro is still used when printing error reports though it's not -// clear if the BP value is needed in the ASan reports on Windows. -# define GET_CURRENT_FRAME() (uptr)0xDEADBEEF -#endif - -#ifdef _WIN32 -# ifndef ASAN_USE_EXTERNAL_SYMBOLIZER -# define ASAN_USE_EXTERNAL_SYMBOLIZER __asan_WinSymbolize -bool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size); -# endif -#endif // _WIN32 - // These magic values are written to shadow for better error reporting. const int kAsanHeapLeftRedzoneMagic = 0xfa; const int kAsanHeapRightRedzoneMagic = 0xfb; @@ -174,26 +168,15 @@ const int kAsanStackMidRedzoneMagic = 0xf2; const int kAsanStackRightRedzoneMagic = 0xf3; const int kAsanStackPartialRedzoneMagic = 0xf4; const int kAsanStackAfterReturnMagic = 0xf5; +const int kAsanInitializationOrderMagic = 0xf6; const int kAsanUserPoisonedMemoryMagic = 0xf7; +const int kAsanStackUseAfterScopeMagic = 0xf8; const int kAsanGlobalRedzoneMagic = 0xf9; const int kAsanInternalHeapMagic = 0xfe; static const uptr kCurrentStackFrameMagic = 0x41B58AB3; static const uptr kRetiredStackFrameMagic = 0x45E0360E; -// -------------------------- LowLevelAllocator ----- {{{1 -// A simple low-level memory allocator for internal use. -class LowLevelAllocator { - public: - explicit LowLevelAllocator(LinkerInitialized) {} - // 'size' must be a power of two. - // Requires an external lock. - void *Allocate(uptr size); - private: - char *allocated_end_; - char *allocated_current_; -}; - } // namespace __asan #endif // ASAN_INTERNAL_H diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc index 9a3d6bdb15a6..845493de0956 100644 --- a/lib/asan/asan_linux.cc +++ b/lib/asan/asan_linux.cc @@ -15,8 +15,8 @@ #include "asan_interceptors.h" #include "asan_internal.h" -#include "asan_lock.h" #include "asan_thread.h" +#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" @@ -31,7 +31,7 @@ #include <unistd.h> #include <unwind.h> -#ifndef ANDROID +#if !ASAN_ANDROID // FIXME: where to get ucontext on Android? #include <sys/ucontext.h> #endif @@ -40,13 +40,17 @@ extern "C" void* _DYNAMIC; namespace __asan { +void MaybeReexec() { + // No need to re-exec on Linux. +} + void *AsanDoesNotSupportStaticLinkage() { // This will fail to link with -static. return &_DYNAMIC; // defined in link.h } void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { -#ifdef ANDROID +#if ASAN_ANDROID *pc = *sp = *bp = 0; #elif defined(__arm__) ucontext_t *ucontext = (ucontext_t*)context; @@ -63,6 +67,27 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { *pc = ucontext->uc_mcontext.gregs[REG_EIP]; *bp = ucontext->uc_mcontext.gregs[REG_EBP]; *sp = ucontext->uc_mcontext.gregs[REG_ESP]; +# elif defined(__powerpc__) || defined(__powerpc64__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.regs->nip; + *sp = ucontext->uc_mcontext.regs->gpr[PT_R1]; + // The powerpc{,64}-linux ABIs do not specify r31 as the frame + // pointer, but GCC always uses r31 when we need a frame pointer. + *bp = ucontext->uc_mcontext.regs->gpr[PT_R31]; +# elif defined(__sparc__) + ucontext_t *ucontext = (ucontext_t*)context; + uptr *stk_ptr; +# if defined (__arch64__) + *pc = ucontext->uc_mcontext.mc_gregs[MC_PC]; + *sp = ucontext->uc_mcontext.mc_gregs[MC_O6]; + stk_ptr = (uptr *) (*sp + 2047); + *bp = stk_ptr[15]; +# else + *pc = ucontext->uc_mcontext.gregs[REG_PC]; + *sp = ucontext->uc_mcontext.gregs[REG_O6]; + stk_ptr = (uptr *) *sp; + *bp = stk_ptr[15]; +# endif #else # error "Unsupported arch" #endif @@ -76,69 +101,35 @@ void AsanPlatformThreadInit() { // Nothing here for now. } -AsanLock::AsanLock(LinkerInitialized) { - // We assume that pthread_mutex_t initialized to all zeroes is a valid - // unlocked mutex. We can not use PTHREAD_MUTEX_INITIALIZER as it triggers - // a gcc warning: - // extended initializer lists only available with -std=c++0x or -std=gnu++0x -} - -void AsanLock::Lock() { - CHECK(sizeof(pthread_mutex_t) <= sizeof(opaque_storage_)); - pthread_mutex_lock((pthread_mutex_t*)&opaque_storage_); - CHECK(!owner_); - owner_ = (uptr)pthread_self(); -} - -void AsanLock::Unlock() { - CHECK(owner_ == (uptr)pthread_self()); - owner_ = 0; - pthread_mutex_unlock((pthread_mutex_t*)&opaque_storage_); -} - -#ifdef __arm__ -#define UNWIND_STOP _URC_END_OF_STACK -#define UNWIND_CONTINUE _URC_NO_REASON -#else -#define UNWIND_STOP _URC_NORMAL_STOP -#define UNWIND_CONTINUE _URC_NO_REASON -#endif - -uptr Unwind_GetIP(struct _Unwind_Context *ctx) { -#ifdef __arm__ - uptr val; - _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE, - 15 /* r15 = PC */, _UVRSD_UINT32, &val); - CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed"); - // Clear the Thumb bit. - return val & ~(uptr)1; -#else - return _Unwind_GetIP(ctx); +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()); + } } -_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, - void *param) { - AsanStackTrace *b = (AsanStackTrace*)param; - CHECK(b->size < b->max_size); - uptr pc = Unwind_GetIP(ctx); - b->trace[b->size++] = pc; - if (b->size == b->max_size) return UNWIND_STOP; - return UNWIND_CONTINUE; +#if !ASAN_ANDROID +void ReadContextStack(void *context, uptr *stack, uptr *ssize) { + ucontext_t *ucp = (ucontext_t*)context; + *stack = (uptr)ucp->uc_stack.ss_sp; + *ssize = ucp->uc_stack.ss_size; } - -void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) { - size = 0; - trace[0] = pc; - if ((max_s) > 1) { - max_size = max_s; -#ifdef __arm__ - _Unwind_Backtrace(Unwind_Trace, this); #else - FastUnwindStack(pc, bp); -#endif - } +void ReadContextStack(void *context, uptr *stack, uptr *ssize) { + UNIMPLEMENTED(); } +#endif } // namespace __asan diff --git a/lib/asan/asan_lock.h b/lib/asan/asan_lock.h index edee49adf6a7..e69de29bb2d1 100644 --- a/lib/asan/asan_lock.h +++ b/lib/asan/asan_lock.h @@ -1,42 +0,0 @@ -//===-- asan_lock.h ---------------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// A wrapper for a simple lock. -//===----------------------------------------------------------------------===// -#ifndef ASAN_LOCK_H -#define ASAN_LOCK_H - -#include "sanitizer_common/sanitizer_mutex.h" -#include "asan_internal.h" - -// The locks in ASan are global objects and they are never destroyed to avoid -// at-exit races (that is, a lock is being used by other threads while the main -// thread is doing atexit destructors). -// We define the class using opaque storage to avoid including system headers. - -namespace __asan { - -class AsanLock { - public: - explicit AsanLock(LinkerInitialized); - void Lock(); - void Unlock(); - bool IsLocked() { return owner_ != 0; } - private: - uptr opaque_storage_[10]; - uptr owner_; // for debugging and for malloc_introspection_t interface -}; - -typedef GenericScopedLock<AsanLock> ScopedLock; - -} // namespace __asan - -#endif // ASAN_LOCK_H diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc index a3d39e7ae05c..3ed9e06eeb6f 100644 --- a/lib/asan/asan_mac.cc +++ b/lib/asan/asan_mac.cc @@ -23,7 +23,8 @@ #include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_libc.h" -#include <crt_externs.h> // for _NSGetEnviron +#include <crt_externs.h> // for _NSGetArgv +#include <dlfcn.h> // for dladdr() #include <mach-o/dyld.h> #include <mach-o/loader.h> #include <sys/mman.h> @@ -41,7 +42,7 @@ namespace __asan { void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { ucontext_t *ucontext = (ucontext_t*)context; -# if __WORDSIZE == 64 +# if SANITIZER_WORDSIZE == 64 *pc = ucontext->uc_mcontext->__ss.__rip; *bp = ucontext->uc_mcontext->__ss.__rbp; *sp = ucontext->uc_mcontext->__ss.__rsp; @@ -49,7 +50,7 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { *pc = ucontext->uc_mcontext->__ss.__eip; *bp = ucontext->uc_mcontext->__ss.__ebp; *sp = ucontext->uc_mcontext->__ss.__esp; -# endif // __WORDSIZE +# endif // SANITIZER_WORDSIZE } int GetMacosVersion() { @@ -67,6 +68,7 @@ int GetMacosVersion() { switch (version[1]) { case '0': return MACOS_VERSION_SNOW_LEOPARD; case '1': return MACOS_VERSION_LION; + case '2': return MACOS_VERSION_MOUNTAIN_LION; default: return MACOS_VERSION_UNKNOWN; } } @@ -83,6 +85,42 @@ bool PlatformHasDifferentMemcpyAndMemmove() { return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD; } +extern "C" +void __asan_init(); + +static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES"; + +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. + Dl_info info; + CHECK(dladdr((void*)((uptr)__asan_init), &info)); + const char *dyld_insert_libraries = GetEnv(kDyldInsertLibraries); + if (!dyld_insert_libraries || + !REAL(strstr)(dyld_insert_libraries, info.dli_fname)) { + // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime + // library. + 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); + if (flags()->verbosity >= 1) { + Report("exec()-ing the program with\n"); + Report("%s=%s\n", kDyldInsertLibraries, info.dli_fname); + Report("to enable ASan wrappers.\n"); + Report("Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n"); + } + execv(program_name, *_NSGetArgv()); + } +#endif // MAC_INTERPOSE_FUNCTIONS + // If we're not using the dynamic runtime, do nothing. +} + // No-op. Mac does not support static linkage anyway. void *AsanDoesNotSupportStaticLinkage() { return 0; @@ -93,37 +131,32 @@ bool AsanInterceptsSignal(int signum) { } void AsanPlatformThreadInit() { - ReplaceCFAllocator(); -} - -AsanLock::AsanLock(LinkerInitialized) { - // We assume that OS_SPINLOCK_INIT is zero -} - -void AsanLock::Lock() { - CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_)); - CHECK(OS_SPINLOCK_INIT == 0); - CHECK(owner_ != (uptr)pthread_self()); - OSSpinLockLock((OSSpinLock*)&opaque_storage_); - CHECK(!owner_); - owner_ = (uptr)pthread_self(); -} - -void AsanLock::Unlock() { - CHECK(owner_ == (uptr)pthread_self()); - owner_ = 0; - OSSpinLockUnlock((OSSpinLock*)&opaque_storage_); + // 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 AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) { - size = 0; - trace[0] = pc; +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) { - max_size = max_s; - FastUnwindStack(pc, bp); + 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. @@ -132,12 +165,12 @@ void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) { // kHighMemBeg or kHighMemEnd. static void *island_allocator_pos = 0; -#if __WORDSIZE == 32 -# define kIslandEnd (0xffdf0000 - kPageSize) -# define kIslandBeg (kIslandEnd - 256 * kPageSize) +#if SANITIZER_WORDSIZE == 32 +# define kIslandEnd (0xffdf0000 - GetPageSizeCached()) +# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached()) #else -# define kIslandEnd (0x7fffffdf0000 - kPageSize) -# define kIslandBeg (kIslandEnd - 256 * kPageSize) +# define kIslandEnd (0x7fffffdf0000 - GetPageSizeCached()) +# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached()) #endif extern "C" @@ -161,7 +194,7 @@ mach_error_t __interception_allocate_island(void **ptr, internal_memset(island_allocator_pos, 0xCC, kIslandEnd - kIslandBeg); }; *ptr = island_allocator_pos; - island_allocator_pos = (char*)island_allocator_pos + kPageSize; + island_allocator_pos = (char*)island_allocator_pos + GetPageSizeCached(); if (flags()->verbosity) { Report("Branch island allocated at %p\n", *ptr); } @@ -209,6 +242,7 @@ 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); @@ -236,30 +270,34 @@ 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); -int pthread_workqueue_additem_np(pthread_workqueue_t workq, - void *(*workitem_func)(void *), void * workitem_arg, - pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp); } // extern "C" +static ALWAYS_INLINE +void asan_register_worker_thread(int parent_tid, StackTrace *stack) { + AsanThread *t = asanThreadRegistry().GetCurrent(); + if (!t) { + t = AsanThread::Create(parent_tid, 0, 0, stack); + asanThreadRegistry().RegisterThread(t); + t->Init(); + asanThreadRegistry().SetCurrent(t); + } +} + +// For use by only those functions that allocated the context via +// alloc_asan_context(). extern "C" void asan_dispatch_call_block_and_release(void *block) { - GET_STACK_TRACE_HERE(kStackTraceMax); + GET_STACK_TRACE_THREAD; asan_block_context_t *context = (asan_block_context_t*)block; if (flags()->verbosity >= 2) { Report("asan_dispatch_call_block_and_release(): " "context: %p, pthread_self: %p\n", block, pthread_self()); } - AsanThread *t = asanThreadRegistry().GetCurrent(); - if (!t) { - t = AsanThread::Create(context->parent_tid, 0, 0, &stack); - asanThreadRegistry().RegisterThread(t); - t->Init(); - asanThreadRegistry().SetCurrent(t); - } + asan_register_worker_thread(context->parent_tid, &stack); // Call the original dispatcher for the block. context->func(context->block); - asan_free(context, &stack); + asan_free(context, &stack, FROM_MALLOC); } } // namespace __asan @@ -270,7 +308,7 @@ using namespace __asan; // NOLINT // The caller retains control of the allocated context. extern "C" asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, - AsanStackTrace *stack) { + StackTrace *stack) { asan_block_context_t *asan_ctxt = (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); asan_ctxt->block = ctxt; @@ -279,37 +317,30 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, return asan_ctxt; } -// TODO(glider): can we reduce code duplication by introducing a macro? -INTERCEPTOR(void, dispatch_async_f, dispatch_queue_t dq, void *ctxt, - dispatch_function_t func) { - GET_STACK_TRACE_HERE(kStackTraceMax); - asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (flags()->verbosity >= 2) { - Report("dispatch_async_f(): context: %p, pthread_self: %p\n", - asan_ctxt, pthread_self()); - PRINT_CURRENT_STACK(); +// Define interceptor for dispatch_*_f function with the three most common +// parameters: dispatch_queue_t, context, dispatch_function_t. +#define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \ + INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \ + dispatch_function_t func) { \ + GET_STACK_TRACE_THREAD; \ + asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \ + if (flags()->verbosity >= 2) { \ + Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \ + asan_ctxt, pthread_self()); \ + PRINT_CURRENT_STACK(); \ + } \ + return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \ + asan_dispatch_call_block_and_release); \ } - return REAL(dispatch_async_f)(dq, (void*)asan_ctxt, - asan_dispatch_call_block_and_release); -} -INTERCEPTOR(void, dispatch_sync_f, dispatch_queue_t dq, void *ctxt, - dispatch_function_t func) { - GET_STACK_TRACE_HERE(kStackTraceMax); - asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (flags()->verbosity >= 2) { - Report("dispatch_sync_f(): context: %p, pthread_self: %p\n", - asan_ctxt, pthread_self()); - PRINT_CURRENT_STACK(); - } - return REAL(dispatch_sync_f)(dq, (void*)asan_ctxt, - asan_dispatch_call_block_and_release); -} +INTERCEPT_DISPATCH_X_F_3(dispatch_async_f) +INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f) +INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f) INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, dispatch_queue_t dq, void *ctxt, dispatch_function_t func) { - GET_STACK_TRACE_HERE(kStackTraceMax); + GET_STACK_TRACE_THREAD; asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); if (flags()->verbosity >= 2) { Report("dispatch_after_f: %p\n", asan_ctxt); @@ -319,23 +350,10 @@ INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, asan_dispatch_call_block_and_release); } -INTERCEPTOR(void, dispatch_barrier_async_f, dispatch_queue_t dq, void *ctxt, - dispatch_function_t func) { - GET_STACK_TRACE_HERE(kStackTraceMax); - asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (flags()->verbosity >= 2) { - Report("dispatch_barrier_async_f(): context: %p, pthread_self: %p\n", - asan_ctxt, pthread_self()); - PRINT_CURRENT_STACK(); - } - REAL(dispatch_barrier_async_f)(dq, (void*)asan_ctxt, - asan_dispatch_call_block_and_release); -} - INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, dispatch_queue_t dq, void *ctxt, dispatch_function_t func) { - GET_STACK_TRACE_HERE(kStackTraceMax); + GET_STACK_TRACE_THREAD; asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); if (flags()->verbosity >= 2) { Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", @@ -346,43 +364,66 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, asan_dispatch_call_block_and_release); } -// The following stuff has been extremely helpful while looking for the -// unhandled functions that spawned jobs on Chromium shutdown. If the verbosity -// level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to -// find the points of worker thread creation (each of such threads may be used -// to run several tasks, that's why this is not enough to support the whole -// libdispatch API. -extern "C" -void *wrap_workitem_func(void *arg) { - if (flags()->verbosity >= 2) { - Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self()); - } - asan_block_context_t *ctxt = (asan_block_context_t*)arg; - worker_t fn = (worker_t)(ctxt->func); - void *result = fn(ctxt->block); - GET_STACK_TRACE_HERE(kStackTraceMax); - asan_free(arg, &stack); - return result; +#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. + +extern "C" { +// FIXME: consolidate these declarations with asan_intercepted_functions.h. +void dispatch_async(dispatch_queue_t dq, void(^work)(void)); +void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, + void(^work)(void)); +void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, + void(^work)(void)); +void dispatch_source_set_cancel_handler(dispatch_source_t ds, + void(^work)(void)); +void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void)); } -INTERCEPTOR(int, pthread_workqueue_additem_np, pthread_workqueue_t workq, - void *(*workitem_func)(void *), void * workitem_arg, - pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) { - GET_STACK_TRACE_HERE(kStackTraceMax); - asan_block_context_t *asan_ctxt = - (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack); - asan_ctxt->block = workitem_arg; - asan_ctxt->func = (dispatch_function_t)workitem_func; - asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); - if (flags()->verbosity >= 2) { - Report("pthread_workqueue_additem_np: %p\n", asan_ctxt); - PRINT_CURRENT_STACK(); +#define GET_ASAN_BLOCK(work) \ + void (^asan_block)(void); \ + int parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); \ + asan_block = ^(void) { \ + GET_STACK_TRACE_THREAD; \ + asan_register_worker_thread(parent_tid, &stack); \ + work(); \ } - return REAL(pthread_workqueue_additem_np)(workq, wrap_workitem_func, - asan_ctxt, itemhandlep, - gencountp); + +INTERCEPTOR(void, dispatch_async, + dispatch_queue_t dq, void(^work)(void)) { + GET_ASAN_BLOCK(work); + REAL(dispatch_async)(dq, asan_block); +} + +INTERCEPTOR(void, dispatch_group_async, + dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) { + GET_ASAN_BLOCK(work); + REAL(dispatch_group_async)(dg, dq, asan_block); +} + +INTERCEPTOR(void, dispatch_after, + dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) { + GET_ASAN_BLOCK(work); + REAL(dispatch_after)(when, queue, asan_block); } +INTERCEPTOR(void, dispatch_source_set_cancel_handler, + dispatch_source_t ds, void(^work)(void)) { + GET_ASAN_BLOCK(work); + REAL(dispatch_source_set_cancel_handler)(ds, asan_block); +} + +INTERCEPTOR(void, dispatch_source_set_event_handler, + dispatch_source_t ds, void(^work)(void)) { + GET_ASAN_BLOCK(work); + REAL(dispatch_source_set_event_handler)(ds, asan_block); +} +#endif + // See http://opensource.apple.com/source/CF/CF-635.15/CFString.c int __CFStrIsConstant(CFStringRef str) { CFRuntimeBase *base = (CFRuntimeBase*)str; @@ -404,9 +445,7 @@ INTERCEPTOR(CFStringRef, CFStringCreateCopy, CFAllocatorRef alloc, DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr) -extern "C" -void __CFInitialize(); -DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize) +DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize, void) namespace __asan { @@ -416,12 +455,6 @@ void InitializeMacInterceptors() { CHECK(INTERCEPT_FUNCTION(dispatch_after_f)); CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f)); CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f)); - // We don't need to intercept pthread_workqueue_additem_np() to support the - // libdispatch API, but it helps us to debug the unsupported functions. Let's - // intercept it only during verbose runs. - if (flags()->verbosity >= 2) { - CHECK(INTERCEPT_FUNCTION(pthread_workqueue_additem_np)); - } // Normally CFStringCreateCopy should not copy constant CF strings. // Replacing the default CFAllocator causes constant strings to be copied // rather than just returned, which leads to bugs in big applications like diff --git a/lib/asan/asan_mac.h b/lib/asan/asan_mac.h index 6c65765a804c..be913865c440 100644 --- a/lib/asan/asan_mac.h +++ b/lib/asan/asan_mac.h @@ -40,13 +40,17 @@ enum { MACOS_VERSION_UNKNOWN = 0, MACOS_VERSION_LEOPARD, MACOS_VERSION_SNOW_LEOPARD, - MACOS_VERSION_LION + MACOS_VERSION_LION, + MACOS_VERSION_MOUNTAIN_LION }; +// Used by asan_malloc_mac.cc and asan_mac.cc +extern "C" void __CFInitialize(); + namespace __asan { int GetMacosVersion(); -void ReplaceCFAllocator(); +void MaybeReplaceCFAllocator(); } // namespace __asan diff --git a/lib/asan/asan_malloc_linux.cc b/lib/asan/asan_malloc_linux.cc index 1046f4c843a8..b95cfe3149b7 100644 --- a/lib/asan/asan_malloc_linux.cc +++ b/lib/asan/asan_malloc_linux.cc @@ -19,8 +19,16 @@ #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 +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) +DECLARE_REAL_AND_INTERCEPTOR(void*, realloc, void *ptr, uptr size) +DECLARE_REAL_AND_INTERCEPTOR(void*, memalign, uptr boundary, uptr size) -#ifdef ANDROID struct MallocDebug { void* (*malloc)(uptr bytes); void (*free)(void* mem); @@ -30,7 +38,7 @@ struct MallocDebug { }; const MallocDebug asan_malloc_dispatch ALIGNED(32) = { - malloc, free, calloc, realloc, memalign + WRAP(malloc), WRAP(free), WRAP(calloc), WRAP(realloc), WRAP(memalign) }; extern "C" const MallocDebug* __libc_malloc_dispatch; @@ -53,17 +61,17 @@ void ReplaceSystemMalloc() { using namespace __asan; // NOLINT INTERCEPTOR(void, free, void *ptr) { - GET_STACK_TRACE_HERE_FOR_FREE(ptr); - asan_free(ptr, &stack); + GET_STACK_TRACE_FREE; + asan_free(ptr, &stack, FROM_MALLOC); } INTERCEPTOR(void, cfree, void *ptr) { - GET_STACK_TRACE_HERE_FOR_FREE(ptr); - asan_free(ptr, &stack); + GET_STACK_TRACE_FREE; + asan_free(ptr, &stack, FROM_MALLOC); } INTERCEPTOR(void*, malloc, uptr size) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } @@ -79,25 +87,25 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { CHECK(allocated < kCallocPoolSize); return mem; } - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_calloc(nmemb, size, &stack); } INTERCEPTOR(void*, realloc, void *ptr, uptr size) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_realloc(ptr, size, &stack); } INTERCEPTOR(void*, memalign, uptr boundary, uptr size) { - GET_STACK_TRACE_HERE_FOR_MALLOC; - return asan_memalign(boundary, size, &stack); + GET_STACK_TRACE_MALLOC; + return asan_memalign(boundary, size, &stack, FROM_MALLOC); } INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s) ALIAS("memalign"); INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_malloc_usable_size(ptr, &stack); } @@ -120,19 +128,23 @@ INTERCEPTOR(int, mallopt, int cmd, int value) { } INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; // Printf("posix_memalign: %zx %zu\n", alignment, size); return asan_posix_memalign(memptr, alignment, size, &stack); } INTERCEPTOR(void*, valloc, uptr size) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_valloc(size, &stack); } INTERCEPTOR(void*, pvalloc, uptr size) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_pvalloc(size, &stack); } +INTERCEPTOR(void, malloc_stats, void) { + __asan_print_accumulated_stats(); +} + #endif // __linux__ diff --git a/lib/asan/asan_malloc_mac.cc b/lib/asan/asan_malloc_mac.cc index 1a6c84052a0a..545ede2debe7 100644 --- a/lib/asan/asan_malloc_mac.cc +++ b/lib/asan/asan_malloc_mac.cc @@ -1,4 +1,4 @@ -//===-- asan_rtl.cc -------------------------------------------------------===// +//===-- asan_malloc_mac.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -18,13 +18,15 @@ #include <CoreFoundation/CFBase.h> #include <dlfcn.h> #include <malloc/malloc.h> -#include <setjmp.h> #include "asan_allocator.h" #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_mac.h" +#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. @@ -38,6 +40,30 @@ 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; + } + } + return ptr; +} + // 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 @@ -65,26 +91,12 @@ INTERCEPTOR(void, free, void *ptr) { malloc_zone_free(zone, ptr); #endif } else { - 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; - } - } - GET_STACK_TRACE_HERE_FOR_FREE(ptr); - asan_free(ptr, &stack); + if (!asan_mz_size(ptr)) ptr = get_saved_cfallocator_ref(ptr); + GET_STACK_TRACE_FREE; + asan_free(ptr, &stack, FROM_MALLOC); } } -namespace __asan { - void ReplaceCFAllocator(); -} - // 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 @@ -94,11 +106,15 @@ namespace __asan { // // 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) { +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) ReplaceCFAllocator(); + if (!cf_asan && asan_inited) MaybeReplaceCFAllocator(); } namespace { @@ -114,7 +130,7 @@ void *mz_malloc(malloc_zone_t *zone, size_t size) { CHECK(system_malloc_zone); return malloc_zone_malloc(system_malloc_zone, size); } - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } @@ -123,7 +139,7 @@ void *cf_malloc(CFIndex size, CFOptionFlags hint, void *info) { CHECK(system_malloc_zone); return malloc_zone_malloc(system_malloc_zone, size); } - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } @@ -139,7 +155,7 @@ void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { CHECK(allocated < kCallocPoolSize); return mem; } - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_calloc(nmemb, size, &stack); } @@ -148,39 +164,41 @@ void *mz_valloc(malloc_zone_t *zone, size_t size) { CHECK(system_malloc_zone); return malloc_zone_valloc(system_malloc_zone, size); } - GET_STACK_TRACE_HERE_FOR_MALLOC; - return asan_memalign(kPageSize, size, &stack); + GET_STACK_TRACE_MALLOC; + return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); } -void print_zone_for_ptr(void *ptr) { - malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr); - if (orig_zone) { - if (orig_zone->zone_name) { - AsanPrintf("malloc_zone_from_ptr(%p) = %p, which is %s\n", - ptr, orig_zone, orig_zone->zone_name); - } else { - AsanPrintf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n", - ptr, orig_zone); - } - } else { - AsanPrintf("malloc_zone_from_ptr(%p) = 0\n", ptr); - } -} +#define GET_ZONE_FOR_PTR(ptr) \ + malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \ + const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name void ALWAYS_INLINE free_common(void *context, void *ptr) { if (!ptr) return; - if (!flags()->mac_ignore_invalid_free || asan_mz_size(ptr)) { - GET_STACK_TRACE_HERE_FOR_FREE(ptr); - asan_free(ptr, &stack); + if (asan_mz_size(ptr)) { + GET_STACK_TRACE_FREE; + asan_free(ptr, &stack, FROM_MALLOC); } else { - // Let us just leak this memory for now. - AsanPrintf("free_common(%p) -- attempting to free unallocated memory.\n" - "AddressSanitizer is ignoring this error on Mac OS now.\n", - ptr); - print_zone_for_ptr(ptr); - GET_STACK_TRACE_HERE_FOR_FREE(ptr); - stack.PrintStack(); - return; + // 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; + } + } } } @@ -195,55 +213,45 @@ void cf_free(void *ptr, void *info) { void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { if (!ptr) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } else { if (asan_mz_size(ptr)) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + 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. - AsanPrintf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n" - "This is an unrecoverable problem, exiting now.\n", - ptr); - print_zone_for_ptr(ptr); - GET_STACK_TRACE_HERE_FOR_FREE(ptr); - stack.PrintStack(); - ShowStatsAndAbort(); - return 0; // unreachable + GET_STACK_TRACE_FREE; + GET_ZONE_FOR_PTR(ptr); + ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); } } } void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) { if (!ptr) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } else { if (asan_mz_size(ptr)) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + 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. - AsanPrintf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n" - "This is an unrecoverable problem, exiting now.\n", - ptr); - print_zone_for_ptr(ptr); - GET_STACK_TRACE_HERE_FOR_FREE(ptr); - stack.PrintStack(); - ShowStatsAndAbort(); - return 0; // unreachable + 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! - AsanPrintf("mz_destroy() called -- ignoring\n"); + Printf("mz_destroy() called -- ignoring\n"); } // from AvailabilityMacros.h #if defined(MAC_OS_X_VERSION_10_6) && \ @@ -253,8 +261,8 @@ void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { CHECK(system_malloc_zone); return malloc_zone_memalign(system_malloc_zone, align, size); } - GET_STACK_TRACE_HERE_FOR_MALLOC; - return asan_memalign(align, size, &stack); + GET_STACK_TRACE_MALLOC; + return asan_memalign(align, size, &stack, FROM_MALLOC); } // This function is currently unused, and we build with -Werror. @@ -266,7 +274,6 @@ void mz_free_definite_size(malloc_zone_t* zone, void *ptr, size_t size) { #endif #endif -// malloc_introspection callbacks. I'm not clear on what all of these do. kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader, @@ -282,12 +289,10 @@ size_t mi_good_size(malloc_zone_t *zone, size_t size) { boolean_t mi_check(malloc_zone_t *zone) { UNIMPLEMENTED(); - return true; } void mi_print(malloc_zone_t *zone, boolean_t verbose) { UNIMPLEMENTED(); - return; } void mi_log(malloc_zone_t *zone, void *address) { @@ -302,17 +307,12 @@ void mi_force_unlock(malloc_zone_t *zone) { asan_mz_force_unlock(); } -// This function is currently unused, and we build with -Werror. -#if 0 void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { - // TODO(csilvers): figure out how to fill these out - // TODO(glider): port this from tcmalloc when ready. - stats->blocks_in_use = 0; - stats->size_in_use = 0; - stats->max_size_in_use = 0; - stats->size_allocated = 0; + AsanMallocStats malloc_stats; + asanThreadRegistry().FillMallocStatistics(&malloc_stats); + CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); + internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); } -#endif #if defined(MAC_OS_X_VERSION_10_6) && \ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 @@ -327,7 +327,7 @@ boolean_t mi_zone_locked(malloc_zone_t *zone) { extern int __CFRuntimeClassTableSize; namespace __asan { -void ReplaceCFAllocator() { +void MaybeReplaceCFAllocator() { static CFAllocatorContext asan_context = { /*version*/ 0, /*info*/ &asan_zone, /*retain*/ 0, /*release*/ 0, @@ -338,7 +338,7 @@ void ReplaceCFAllocator() { /*preferredSize*/ 0 }; if (!cf_asan) cf_asan = CFAllocatorCreate(kCFAllocatorUseContext, &asan_context); - if (CFAllocatorGetDefault() != cf_asan) + if (flags()->replace_cfallocator && CFAllocatorGetDefault() != cf_asan) CFAllocatorSetDefault(cf_asan); } @@ -354,6 +354,7 @@ void ReplaceSystemMalloc() { asan_introspection.log = &mi_log; asan_introspection.force_lock = &mi_force_lock; asan_introspection.force_unlock = &mi_force_unlock; + asan_introspection.statistics = &mi_statistics; internal_memset(&asan_zone, 0, sizeof(malloc_zone_t)); @@ -405,16 +406,14 @@ void ReplaceSystemMalloc() { // Make sure the default allocator was replaced. CHECK(malloc_default_zone() == &asan_zone); - if (flags()->replace_cfallocator) { - // 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) { - ReplaceCFAllocator(); - } + // 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 diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc index 6c00e77caae8..9fcfea56384f 100644 --- a/lib/asan/asan_malloc_win.cc +++ b/lib/asan/asan_malloc_win.cc @@ -17,9 +17,10 @@ #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_stack.h" - #include "interception/interception.h" +#include <stddef.h> + // ---------------------- Replacement functions ---------------- {{{1 using namespace __asan; // NOLINT @@ -30,8 +31,8 @@ using namespace __asan; // NOLINT extern "C" { void free(void *ptr) { - GET_STACK_TRACE_HERE_FOR_FREE(ptr); - return asan_free(ptr, &stack); + GET_STACK_TRACE_FREE; + return asan_free(ptr, &stack, FROM_MALLOC); } void _free_dbg(void* ptr, int) { @@ -43,7 +44,7 @@ void cfree(void *ptr) { } void *malloc(size_t size) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } @@ -52,7 +53,7 @@ void* _malloc_dbg(size_t size, int , const char*, int) { } void *calloc(size_t nmemb, size_t size) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_calloc(nmemb, size, &stack); } @@ -65,7 +66,7 @@ void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { } void *realloc(void *ptr, size_t size) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_realloc(ptr, size, &stack); } @@ -84,7 +85,7 @@ void* _recalloc(void* p, size_t n, size_t elem_size) { } size_t _msize(void *ptr) { - GET_STACK_TRACE_HERE_FOR_MALLOC; + GET_STACK_TRACE_MALLOC; return asan_malloc_usable_size(ptr, &stack); } diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h index 8e0c6ec5db20..5e3067031f4a 100644 --- a/lib/asan/asan_mapping.h +++ b/lib/asan/asan_mapping.h @@ -20,20 +20,24 @@ // http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm #if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1 -extern __attribute__((visibility("default"))) uptr __asan_mapping_scale; -extern __attribute__((visibility("default"))) uptr __asan_mapping_offset; +extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale; +extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset; # define SHADOW_SCALE (__asan_mapping_scale) # define SHADOW_OFFSET (__asan_mapping_offset) #else -# ifdef ANDROID +# if ASAN_ANDROID # define SHADOW_SCALE (3) # define SHADOW_OFFSET (0) # else # define SHADOW_SCALE (3) -# if __WORDSIZE == 32 +# if SANITIZER_WORDSIZE == 32 # define SHADOW_OFFSET (1 << 29) # else -# define SHADOW_OFFSET (1ULL << 44) +# if defined(__powerpc64__) +# define SHADOW_OFFSET (1ULL << 41) +# else +# define SHADOW_OFFSET (1ULL << 44) +# endif # endif # endif #endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET @@ -42,11 +46,15 @@ extern __attribute__((visibility("default"))) uptr __asan_mapping_offset; #define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) | (SHADOW_OFFSET)) #define SHADOW_TO_MEM(shadow) (((shadow) - SHADOW_OFFSET) << SHADOW_SCALE) -#if __WORDSIZE == 64 +#if SANITIZER_WORDSIZE == 64 +# if defined(__powerpc64__) + static const uptr kHighMemEnd = 0x00000fffffffffffUL; +# else static const uptr kHighMemEnd = 0x00007fffffffffffUL; -#else // __WORDSIZE == 32 +# endif +#else // SANITIZER_WORDSIZE == 32 static const uptr kHighMemEnd = 0xffffffff; -#endif // __WORDSIZE +#endif // SANITIZER_WORDSIZE #define kLowMemBeg 0 @@ -60,7 +68,12 @@ extern __attribute__((visibility("default"))) uptr __asan_mapping_offset; #define kHighShadowBeg MEM_TO_SHADOW(kHighMemBeg) #define kHighShadowEnd MEM_TO_SHADOW(kHighMemEnd) -#define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 : 16 * kPageSize) +// 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 kGlobalAndStackRedzone \ diff --git a/lib/asan/asan_new_delete.cc b/lib/asan/asan_new_delete.cc index 4a7275842f08..5d1f23c542e6 100644 --- a/lib/asan/asan_new_delete.cc +++ b/lib/asan/asan_new_delete.cc @@ -17,7 +17,6 @@ #include "asan_stack.h" #include <stddef.h> -#include <new> namespace __asan { // This function is a no-op. We need it to make sure that object file @@ -28,29 +27,42 @@ void ReplaceOperatorsNewAndDelete() { } using namespace __asan; // NOLINT -#define OPERATOR_NEW_BODY \ - GET_STACK_TRACE_HERE_FOR_MALLOC;\ - return asan_memalign(0, size, &stack); - -#ifdef ANDROID -void *operator new(size_t size) { OPERATOR_NEW_BODY; } -void *operator new[](size_t size) { OPERATOR_NEW_BODY; } -#else -void *operator new(size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; } -void *operator new[](size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; } -void *operator new(size_t size, std::nothrow_t const&) throw() -{ OPERATOR_NEW_BODY; } -void *operator new[](size_t size, std::nothrow_t const&) throw() -{ OPERATOR_NEW_BODY; } -#endif +// On Android new() goes through malloc interceptors. +#if !ASAN_ANDROID + +// Fake std::nothrow_t to avoid including <new>. +namespace std { +struct nothrow_t {}; +} // namespace std + +#define OPERATOR_NEW_BODY(type) \ + GET_STACK_TRACE_MALLOC;\ + return asan_memalign(0, size, &stack, type); -#define OPERATOR_DELETE_BODY \ - GET_STACK_TRACE_HERE_FOR_FREE(ptr);\ - asan_free(ptr, &stack); +INTERCEPTOR_ATTRIBUTE +void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); } +INTERCEPTOR_ATTRIBUTE +void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); } +INTERCEPTOR_ATTRIBUTE +void *operator new(size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(FROM_NEW); } +INTERCEPTOR_ATTRIBUTE +void *operator new[](size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(FROM_NEW_BR); } -void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; } -void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; } -void operator delete(void *ptr, std::nothrow_t const&) throw() -{ OPERATOR_DELETE_BODY; } -void operator delete[](void *ptr, std::nothrow_t const&) throw() -{ OPERATOR_DELETE_BODY; } +#define OPERATOR_DELETE_BODY(type) \ + GET_STACK_TRACE_FREE;\ + asan_free(ptr, &stack, type); + +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW); } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW_BR); } +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FROM_NEW); } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } + +#endif diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc index 3b9d9f6795ca..dc5749243569 100644 --- a/lib/asan/asan_poisoning.cc +++ b/lib/asan/asan_poisoning.cc @@ -13,17 +13,19 @@ //===----------------------------------------------------------------------===// #include "asan_interceptors.h" -#include "asan_interface.h" #include "asan_internal.h" #include "asan_mapping.h" +#include "sanitizer/asan_interface.h" +#include "sanitizer_common/sanitizer_libc.h" namespace __asan { void PoisonShadow(uptr addr, uptr size, u8 value) { + if (!flags()->poison_heap) return; CHECK(AddrIsAlignedByGranularity(addr)); CHECK(AddrIsAlignedByGranularity(addr + size)); uptr shadow_beg = MemToShadow(addr); - uptr shadow_end = MemToShadow(addr + size); + uptr shadow_end = MemToShadow(addr + size - SHADOW_GRANULARITY) + 1; CHECK(REAL(memset) != 0); REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); } @@ -32,6 +34,7 @@ void PoisonShadowPartialRightRedzone(uptr addr, uptr size, uptr redzone_size, u8 value) { + if (!flags()->poison_heap) return; CHECK(AddrIsAlignedByGranularity(addr)); u8 *shadow = (u8*)MemToShadow(addr); for (uptr i = 0; i < redzone_size; @@ -151,3 +154,67 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) { bool __asan_address_is_poisoned(void const volatile *addr) { return __asan::AddressIsPoisoned((uptr)addr); } + +uptr __asan_region_is_poisoned(uptr beg, uptr size) { + if (!size) return 0; + uptr end = beg + size; + if (!AddrIsInMem(beg)) return beg; + if (!AddrIsInMem(end)) return end; + uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY); + uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY); + uptr shadow_beg = MemToShadow(aligned_b); + uptr shadow_end = MemToShadow(aligned_e); + // First check the first and the last application bytes, + // then check the SHADOW_GRANULARITY-aligned region by calling + // mem_is_zero on the corresponding shadow. + if (!__asan::AddressIsPoisoned(beg) && + !__asan::AddressIsPoisoned(end - 1) && + (shadow_end <= shadow_beg || + __sanitizer::mem_is_zero((const char *)shadow_beg, + shadow_end - shadow_beg))) + return 0; + // The fast check failed, so we have a poisoned byte somewhere. + // Find it slowly. + for (; beg < end; beg++) + if (__asan::AddressIsPoisoned(beg)) + return beg; + UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found"); + return 0; +} + +// 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) { + if (size == 0) return; + uptr aligned_size = size & ~(SHADOW_GRANULARITY - 1); + PoisonShadow(addr, aligned_size, + do_poison ? kAsanStackUseAfterScopeMagic : 0); + if (size == aligned_size) + return; + s8 end_offset = (s8)(size - aligned_size); + s8* shadow_end = (s8*)MemToShadow(addr + aligned_size); + s8 end_value = *shadow_end; + if (do_poison) { + // If possible, mark all the bytes mapping to last shadow byte as + // unaddressable. + if (end_value > 0 && end_value <= end_offset) + *shadow_end = (s8)kAsanStackUseAfterScopeMagic; + } else { + // If necessary, mark few first bytes mapping to last shadow byte + // as addressable + if (end_value != 0) + *shadow_end = Max(end_value, end_offset); + } +} + +void __asan_poison_stack_memory(uptr addr, uptr size) { + if (flags()->verbosity > 0) + Report("poisoning: %p %zx\n", (void*)addr, size); + PoisonAlignedStackMemory(addr, size, true); +} + +void __asan_unpoison_stack_memory(uptr addr, uptr size) { + if (flags()->verbosity > 0) + Report("unpoisoning: %p %zx\n", (void*)addr, size); + PoisonAlignedStackMemory(addr, size, false); +} diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc index 061bb193034d..ceaf120fc803 100644 --- a/lib/asan/asan_posix.cc +++ b/lib/asan/asan_posix.cc @@ -16,6 +16,7 @@ #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" @@ -53,14 +54,7 @@ static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) { if (13 != internal_write(2, "ASAN:SIGSEGV\n", 13)) Die(); uptr pc, sp, bp; GetPcSpBp(context, &pc, &sp, &bp); - AsanReport("ERROR: AddressSanitizer crashed on unknown address %p" - " (pc %p sp %p bp %p T%d)\n", - (void*)addr, (void*)pc, (void*)sp, (void*)bp, - asanThreadRegistry().GetCurrentTidOrInvalid()); - AsanPrintf("AddressSanitizer can not provide additional info. ABORTING\n"); - GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp); - stack.PrintStack(); - ShowStatsAndAbort(); + ReportSIGSEGV(pc, sp, bp, addr); } void SetAlternateSignalStack() { diff --git a/lib/asan/asan_printf.cc b/lib/asan/asan_printf.cc deleted file mode 100644 index e1304f0fbe3f..000000000000 --- a/lib/asan/asan_printf.cc +++ /dev/null @@ -1,59 +0,0 @@ -//===-- asan_printf.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. -// -// Internal printf function, used inside ASan run-time library. -// We can't use libc printf because we intercept some of the functions used -// inside it. -//===----------------------------------------------------------------------===// - -#include "asan_internal.h" -#include "asan_interceptors.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_common.h" - -#include <stdarg.h> -#include <stdio.h> - -namespace __sanitizer { -int VSNPrintf(char *buff, int buff_length, const char *format, va_list args); -} // namespace __sanitizer - -namespace __asan { - -void AsanPrintf(const char *format, ...) { - const int kLen = 1024 * 4; - char buffer[kLen]; - va_list args; - va_start(args, format); - int needed_length = VSNPrintf(buffer, kLen, format, args); - va_end(args); - RAW_CHECK_MSG(needed_length < kLen, "Buffer in Printf is too short!\n"); - RawWrite(buffer); - AppendToErrorMessageBuffer(buffer); -} - -// Like AsanPrintf, but prints the current PID before the output string. -void AsanReport(const char *format, ...) { - const int kLen = 1024 * 4; - char buffer[kLen]; - int needed_length = internal_snprintf(buffer, kLen, "==%d== ", GetPid()); - RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); - va_list args; - va_start(args, format); - needed_length += VSNPrintf(buffer + needed_length, kLen - needed_length, - format, args); - va_end(args); - RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); - RawWrite(buffer); - AppendToErrorMessageBuffer(buffer); -} - -} // namespace __asan diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc new file mode 100644 index 000000000000..35ab9cabde67 --- /dev/null +++ b/lib/asan/asan_report.cc @@ -0,0 +1,681 @@ +//===-- asan_report.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. +// +// This file contains error reporting code. +//===----------------------------------------------------------------------===// +#include "asan_flags.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#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_report_decorator.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +namespace __asan { + +// -------------------- User-specified callbacks ----------------- {{{1 +static void (*error_report_callback)(const char*); +static char *error_message_buffer = 0; +static uptr error_message_buffer_pos = 0; +static uptr error_message_buffer_size = 0; + +void AppendToErrorMessageBuffer(const char *buffer) { + if (error_message_buffer) { + uptr length = internal_strlen(buffer); + CHECK_GE(error_message_buffer_size, error_message_buffer_pos); + uptr remaining = error_message_buffer_size - error_message_buffer_pos; + internal_strncpy(error_message_buffer + error_message_buffer_pos, + buffer, remaining); + error_message_buffer[error_message_buffer_size - 1] = '\0'; + // FIXME: reallocate the buffer instead of truncating the message. + error_message_buffer_pos += remaining > length ? length : remaining; + } +} + +// ---------------------- Decorator ------------------------------ {{{1 +bool PrintsToTtyCached() { + static int cached = 0; + static bool prints_to_tty; + if (!cached) { // Ok wrt threads since we are printing only from one thread. + prints_to_tty = PrintsToTty(); + cached = 1; + } + return prints_to_tty; +} +class Decorator: private __sanitizer::AnsiColorDecorator { + public: + Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + const char *Warning() { return Red(); } + const char *EndWarning() { return Default(); } + const char *Access() { return Blue(); } + const char *EndAccess() { return Default(); } + const char *Location() { return Green(); } + const char *EndLocation() { return Default(); } + const char *Allocation() { return Magenta(); } + const char *EndAllocation() { return Default(); } + + const char *ShadowByte(u8 byte) { + switch (byte) { + case kAsanHeapLeftRedzoneMagic: + case kAsanHeapRightRedzoneMagic: + return Red(); + case kAsanHeapFreeMagic: + return Magenta(); + case kAsanStackLeftRedzoneMagic: + case kAsanStackMidRedzoneMagic: + case kAsanStackRightRedzoneMagic: + case kAsanStackPartialRedzoneMagic: + return Red(); + case kAsanStackAfterReturnMagic: + return Magenta(); + case kAsanInitializationOrderMagic: + return Cyan(); + case kAsanUserPoisonedMemoryMagic: + return Blue(); + case kAsanStackUseAfterScopeMagic: + return Magenta(); + case kAsanGlobalRedzoneMagic: + return Red(); + case kAsanInternalHeapMagic: + return Yellow(); + default: + return Default(); + } + } + const char *EndShadowByte() { return Default(); } +}; + +// ---------------------- Helper functions ----------------------- {{{1 + +static void PrintShadowByte(const char *before, u8 byte, + const char *after = "\n") { + Decorator d; + Printf("%s%s%x%x%s%s", before, + d.ShadowByte(byte), byte >> 4, byte & 15, d.EndShadowByte(), after); +} + +static void PrintShadowBytes(const char *before, u8 *bytes, + u8 *guilty, uptr n) { + Decorator d; + if (before) + Printf("%s%p:", before, bytes); + for (uptr i = 0; i < n; i++) { + u8 *p = bytes + i; + const char *before = p == guilty ? "[" : + p - 1 == guilty ? "" : " "; + const char *after = p == guilty ? "]" : ""; + PrintShadowByte(before, *p, after); + } + 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); + } + Printf("Shadow byte legend (one shadow byte represents %d " + "application bytes):\n", (int)SHADOW_GRANULARITY); + PrintShadowByte(" Addressable: ", 0); + Printf(" Partially addressable: "); + for (uptr i = 1; i < SHADOW_GRANULARITY; i++) + PrintShadowByte("", i, " "); + Printf("\n"); + PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic); + PrintShadowByte(" Heap righ redzone: ", kAsanHeapRightRedzoneMagic); + PrintShadowByte(" Freed Heap region: ", kAsanHeapFreeMagic); + PrintShadowByte(" Stack left redzone: ", kAsanStackLeftRedzoneMagic); + PrintShadowByte(" Stack mid redzone: ", kAsanStackMidRedzoneMagic); + PrintShadowByte(" Stack right redzone: ", kAsanStackRightRedzoneMagic); + PrintShadowByte(" Stack partial redzone: ", kAsanStackPartialRedzoneMagic); + PrintShadowByte(" Stack after return: ", kAsanStackAfterReturnMagic); + PrintShadowByte(" Stack use after scope: ", kAsanStackUseAfterScopeMagic); + PrintShadowByte(" Global redzone: ", kAsanGlobalRedzoneMagic); + PrintShadowByte(" Global init order: ", kAsanInitializationOrderMagic); + PrintShadowByte(" Poisoned by user: ", kAsanUserPoisonedMemoryMagic); + PrintShadowByte(" ASan internal: ", kAsanInternalHeapMagic); +} + +static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, + const char *zone_name) { + if (zone_ptr) { + if (zone_name) { + Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n", + ptr, zone_ptr, zone_name); + } else { + Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n", + ptr, zone_ptr); + } + } else { + Printf("malloc_zone_from_ptr(%p) = 0\n", ptr); + } +} + +// ---------------------- Address Descriptions ------------------- {{{1 + +static bool IsASCII(unsigned char c) { + return /*0x00 <= c &&*/ c <= 0x7F; +} + +// 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; + } + if (*(char*)(g.beg + g.size - 1) != 0) return; + Printf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg); +} + +bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g) { + if (addr < g.beg - kGlobalAndStackRedzone) 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)); + } else { + Printf("%zd bytes inside", addr - g.beg); // Can it happen? + } + Printf(" of global variable '%s' (0x%zx) of size %zu\n", + g.name, g.beg, g.size); + Printf("%s", d.EndLocation()); + PrintGlobalNameIfASCII(g); + return true; +} + +bool DescribeAddressIfShadow(uptr addr) { + if (AddrIsInMem(addr)) + return false; + static const char kAddrInShadowReport[] = + "Address %p is located in the %s.\n"; + if (AddrIsInShadowGap(addr)) { + Printf(kAddrInShadowReport, addr, "shadow gap area"); + return true; + } + if (AddrIsInHighShadow(addr)) { + Printf(kAddrInShadowReport, addr, "high shadow area"); + return true; + } + if (AddrIsInLowShadow(addr)) { + Printf(kAddrInShadowReport, addr, "low shadow area"); + return true; + } + CHECK(0 && "Address is not in memory and not in shadow?"); + return false; +} + +bool DescribeAddressIfStack(uptr addr, uptr access_size) { + AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr); + if (!t) return false; + const sptr kBufSize = 4095; + char buf[kBufSize]; + uptr offset = 0; + const char *frame_descr = t->GetFrameNameByAddr(addr, &offset); + // This string is created by the compiler and has the following form: + // "FunctioName n alloc_1 alloc_2 ... alloc_n" + // where alloc_i looks like "offset size len ObjectName ". + CHECK(frame_descr); + // Report the function name and the offset. + const char *name_end = 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("%s", d.EndLocation()); + // Report the number of stack objects. + char *p; + uptr n_objects = internal_simple_strtoll(name_end, &p, 10); + CHECK(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++) { + uptr beg, size; + sptr len; + beg = internal_simple_strtoll(p, &p, 10); + size = internal_simple_strtoll(p, &p, 10); + len = internal_simple_strtoll(p, &p, 10); + if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') { + Printf("AddressSanitizer can't parse the stack frame " + "descriptor: |%s|\n", frame_descr); + break; + } + p++; + buf[0] = 0; + internal_strncat(buf, p, Min(kBufSize, len)); + p += len; + Printf(" [%zu, %zu) '%s'\n", beg, beg + size, buf); + } + 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()); + return true; +} + +static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, + uptr access_size) { + uptr 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); + } else if (chunk.AddrIsAtRight(addr, access_size, &offset)) { + Printf("%zu bytes to the right of", offset); + } else { + Printf(" somewhere around (this is AddressSanitizer bug!)"); + } + 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()); + StackTrace alloc_stack; + chunk.GetAllocStack(&alloc_stack); + AsanThread *t = asanThreadRegistry().GetCurrent(); + CHECK(t); + char tname[128]; + Decorator d; + if (chunk.FreeTid() != kInvalidTid) { + AsanThreadSummary *free_thread = + asanThreadRegistry().FindByTid(chunk.FreeTid()); + Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), + 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(), + ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), + d.EndAllocation()); + PrintStack(&alloc_stack); + DescribeThread(t->summary()); + DescribeThread(free_thread); + DescribeThread(alloc_thread); + } else { + Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(), + alloc_thread->tid(), + ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), + d.EndAllocation()); + PrintStack(&alloc_stack); + DescribeThread(t->summary()); + DescribeThread(alloc_thread); + } +} + +void DescribeAddress(uptr addr, uptr access_size) { + // Check if this is shadow or shadow gap. + if (DescribeAddressIfShadow(addr)) + return; + CHECK(AddrIsInMem(addr)); + if (DescribeAddressIfGlobal(addr)) + return; + if (DescribeAddressIfStack(addr, access_size)) + return; + // Assume it is a heap address. + DescribeHeapAddress(addr, access_size); +} + +// ------------------- Thread description -------------------- {{{1 + +void DescribeThread(AsanThreadSummary *summary) { + CHECK(summary); + // No need to announce the main thread. + if (summary->tid() == 0 || summary->announced()) { + return; + } + summary->set_announced(true); + char tname[128]; + Printf("Thread T%d%s", summary->tid(), + ThreadNameWithParenthesis(summary->tid(), tname, sizeof(tname))); + Printf(" created by T%d%s here:\n", + summary->parent_tid(), + ThreadNameWithParenthesis(summary->parent_tid(), + tname, sizeof(tname))); + PrintStack(summary->stack()); + // Recursively described parent thread if needed. + if (flags()->print_full_thread_history) { + AsanThreadSummary *parent_summary = + asanThreadRegistry().FindByTid(summary->parent_tid()); + DescribeThread(parent_summary); + } +} + +// -------------------- Different kinds of reports ----------------- {{{1 + +// Use ScopedInErrorReport to run common actions just before and +// immediately after printing error report. +class ScopedInErrorReport { + public: + ScopedInErrorReport() { + static atomic_uint32_t num_calls; + static u32 reporting_thread_tid; + if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { + // Do not print more than one report, otherwise they will mix up. + // Error reporting functions shouldn't return at this situation, as + // they are defined as no-return. + Report("AddressSanitizer: while reporting a bug found another one." + "Ignoring.\n"); + u32 current_tid = asanThreadRegistry().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 + // Die() to bypass any additional checks. + Exit(flags()->exitcode); + } + ASAN_ON_ERROR(); + reporting_thread_tid = asanThreadRegistry().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(); + CHECK(curr_thread); + curr_thread->fake_stack().StopUsingFakeStack(); + } + } + // Destructor is NORETURN, as functions that report errors are. + NORETURN ~ScopedInErrorReport() { + // Make sure the current thread is announced. + AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); + if (curr_thread) { + DescribeThread(curr_thread->summary()); + } + // Print memory stats. + __asan_print_accumulated_stats(); + if (error_report_callback) { + error_report_callback(error_message_buffer); + } + Report("ABORTING\n"); + Die(); + } +}; + +void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + 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()); + Printf("%s", d.EndWarning()); + Printf("AddressSanitizer can not provide additional info.\n"); + GET_STACK_TRACE_FATAL(pc, bp); + PrintStack(&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); + Printf("%s", d.EndWarning()); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: attempting free on address " + "which was not malloc()-ed: %p\n", addr); + Printf("%s", d.EndWarning()); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportAllocTypeMismatch(uptr addr, StackTrace *stack, + AllocType alloc_type, + AllocType dealloc_type) { + static const char *alloc_names[] = + {"INVALID", "malloc", "operator new", "operator new []"}; + static const char *dealloc_names[] = + {"INVALID", "free", "operator delete", "operator delete []"}; + CHECK_NE(alloc_type, dealloc_type); + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n", + alloc_names[alloc_type], dealloc_names[dealloc_type], addr); + Printf("%s", d.EndWarning()); + PrintStack(stack); + DescribeHeapAddress(addr, 1); + Report("HINT: if you don't care about these warnings you may set " + "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); +} + +void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) { + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: attempting to call " + "malloc_usable_size() for pointer which is " + "not owned: %p\n", addr); + Printf("%s", d.EndWarning()); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: attempting to call " + "__asan_get_allocated_size() for pointer which is " + "not owned: %p\n", addr); + Printf("%s", d.EndWarning()); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportStringFunctionMemoryRangesOverlap( + const char *function, const char *offset1, uptr length1, + const char *offset2, uptr length2, StackTrace *stack) { + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: %s-param-overlap: " + "memory ranges [%p,%p) and [%p, %p) overlap\n", \ + function, offset1, offset1 + length1, offset2, offset2 + length2); + Printf("%s", d.EndWarning()); + PrintStack(stack); + DescribeAddress((uptr)offset1, length1); + DescribeAddress((uptr)offset2, length2); +} + +// ----------------------- Mac-specific reports ----------------- {{{1 + +void WarnMacFreeUnallocated( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) { + // Just print a warning here. + Printf("free_common(%p) -- attempting to free unallocated memory.\n" + "AddressSanitizer is ignoring this error on Mac OS now.\n", + addr); + PrintZoneForPointer(addr, zone_ptr, zone_name); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportMacMzReallocUnknown( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) { + ScopedInErrorReport in_report; + Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n" + "This is an unrecoverable problem, exiting now.\n", + addr); + PrintZoneForPointer(addr, zone_ptr, zone_name); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportMacCfReallocUnknown( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) { + ScopedInErrorReport in_report; + Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n" + "This is an unrecoverable problem, exiting now.\n", + addr); + PrintZoneForPointer(addr, zone_ptr, zone_name); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +} // namespace __asan + +// --------------------------- Interface --------------------- {{{1 +using namespace __asan; // NOLINT + +void __asan_report_error(uptr pc, uptr bp, uptr sp, + uptr addr, bool is_write, uptr access_size) { + ScopedInErrorReport in_report; + + // Determine the error type. + const char *bug_descr = "unknown-crash"; + if (AddrIsInMem(addr)) { + u8 *shadow_addr = (u8*)MemToShadow(addr); + // If we are accessing 16 bytes, look at the second shadow byte. + if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) + shadow_addr++; + // If we are in the partial right redzone, look at the next shadow byte. + if (*shadow_addr > 0 && *shadow_addr < 128) + shadow_addr++; + switch (*shadow_addr) { + case kAsanHeapLeftRedzoneMagic: + case kAsanHeapRightRedzoneMagic: + bug_descr = "heap-buffer-overflow"; + break; + case kAsanHeapFreeMagic: + bug_descr = "heap-use-after-free"; + break; + case kAsanStackLeftRedzoneMagic: + bug_descr = "stack-buffer-underflow"; + break; + case kAsanInitializationOrderMagic: + bug_descr = "initialization-order-fiasco"; + break; + case kAsanStackMidRedzoneMagic: + case kAsanStackRightRedzoneMagic: + case kAsanStackPartialRedzoneMagic: + bug_descr = "stack-buffer-overflow"; + break; + case kAsanStackAfterReturnMagic: + bug_descr = "stack-use-after-return"; + break; + case kAsanUserPoisonedMemoryMagic: + bug_descr = "use-after-poison"; + break; + case kAsanStackUseAfterScopeMagic: + bug_descr = "stack-use-after-scope"; + break; + case kAsanGlobalRedzoneMagic: + bug_descr = "global-buffer-overflow"; + break; + } + } + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: %s on address " + "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n", + bug_descr, (void*)addr, pc, bp, sp); + Printf("%s", d.EndWarning()); + + u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + char tname[128]; + Printf("%s%s of size %zu at %p thread T%d%s%s\n", + d.Access(), + access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", + access_size, (void*)addr, curr_tid, + ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)), + d.EndAccess()); + + GET_STACK_TRACE_FATAL(pc, bp); + PrintStack(&stack); + + DescribeAddress(addr, access_size); + + PrintShadowMemoryForAddress(addr); +} + +void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { + error_report_callback = callback; + if (callback) { + error_message_buffer_size = 1 << 16; + error_message_buffer = + (char*)MmapOrDie(error_message_buffer_size, __FUNCTION__); + error_message_buffer_pos = 0; + } +} + +void __asan_describe_address(uptr addr) { + DescribeAddress(addr, 1); +} + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +// Provide default implementation of __asan_on_error that does nothing +// and may be overriden by user. +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +void __asan_on_error() {} +#endif diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h new file mode 100644 index 000000000000..f0617f91970e --- /dev/null +++ b/lib/asan/asan_report.h @@ -0,0 +1,57 @@ +//===-- asan_report.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 error reporting functions. +//===----------------------------------------------------------------------===// + +#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 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); + +// Different kinds of error reports. +void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr); +void NORETURN ReportDoubleFree(uptr addr, StackTrace *stack); +void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *stack); +void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *stack, + AllocType alloc_type, + AllocType dealloc_type); +void NORETURN ReportMallocUsableSizeNotOwned(uptr addr, + StackTrace *stack); +void NORETURN ReportAsanGetAllocatedSizeNotOwned(uptr addr, + StackTrace *stack); +void NORETURN ReportStringFunctionMemoryRangesOverlap( + const char *function, const char *offset1, uptr length1, + const char *offset2, uptr length2, StackTrace *stack); + +// Mac-specific errors and warnings. +void WarnMacFreeUnallocated( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack); +void NORETURN ReportMacMzReallocUnknown( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack); +void NORETURN ReportMacCfReallocUnknown( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack); + +} // namespace __asan diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index 34324fa16d08..11adbee5bdea 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -13,29 +13,29 @@ //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_interceptors.h" -#include "asan_interface.h" #include "asan_internal.h" -#include "asan_lock.h" #include "asan_mapping.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" -namespace __sanitizer { -using namespace __asan; +namespace __asan { -void Die() { +static void AsanDie() { static atomic_uint32_t num_calls; if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { // Don't die twice - run a busy loop. while (1) { } } if (flags()->sleep_before_dying) { - Report("Sleeping for %zd second(s)\n", flags()->sleep_before_dying); + Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying); SleepForSeconds(flags()->sleep_before_dying); } if (flags()->unmap_shadow_on_exit) @@ -47,19 +47,17 @@ void Die() { Exit(flags()->exitcode); } -void CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) { - AsanReport("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", +static void AsanCheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2) { + Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file, line, cond, (uptr)v1, (uptr)v2); + // FIXME: check for infinite recursion without a thread-local counter here. PRINT_CURRENT_STACK(); - ShowStatsAndAbort(); + Die(); } -} // namespace __sanitizer - -namespace __asan { - // -------------------------- Flags ------------------------- {{{1 -static const int kMallocContextSize = 30; +static const int kDeafultMallocContextSize = 30; static Flags asan_flags; @@ -67,6 +65,10 @@ Flags *flags() { return &asan_flags; } +static const char *MaybeCallAsanDefaultOptions() { + return (&__asan_default_options) ? __asan_default_options() : ""; +} + static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->quarantine_size, "quarantine_size"); ParseFlag(str, &f->symbolize, "symbolize"); @@ -77,8 +79,9 @@ static void ParseFlagsFromString(Flags *f, const char *str) { 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(f->malloc_context_size <= kMallocContextSize); + CHECK((uptr)f->malloc_context_size <= kStackTraceMax); ParseFlag(str, &f->replace_str, "replace_str"); ParseFlag(str, &f->replace_intrin, "replace_intrin"); @@ -96,22 +99,28 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->abort_on_error, "abort_on_error"); 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"); } -extern "C" { -const char* WEAK __asan_default_options() { return ""; } -} // extern "C" - void InitializeFlags(Flags *f, const char *env) { internal_memset(f, 0, sizeof(*f)); - f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 24 : 1UL << 28; + f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28; f->symbolize = false; f->verbosity = 0; - f->redzone = (ASAN_LOW_MEMORY) ? 64 : 128; + f->redzone = ASAN_ALLOCATOR_VERSION == 2 ? 16 : (ASAN_LOW_MEMORY) ? 64 : 128; f->debug = false; f->report_globals = 1; - f->malloc_context_size = kMallocContextSize; + f->check_initialization_order = true; + f->malloc_context_size = kDeafultMallocContextSize; f->replace_str = true; f->replace_intrin = true; f->replace_cfallocator = true; @@ -127,13 +136,24 @@ void InitializeFlags(Flags *f, const char *env) { f->unmap_shadow_on_exit = false; f->abort_on_error = false; f->atexit = false; - f->disable_core = (__WORDSIZE == 64); + 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. // Override from user-specified string. - ParseFlagsFromString(f, __asan_default_options()); + ParseFlagsFromString(f, MaybeCallAsanDefaultOptions()); if (flags()->verbosity) { Report("Using the defaults from __asan_default_options: %s\n", - __asan_default_options()); + MaybeCallAsanDefaultOptions()); } // Override from command line. @@ -144,10 +164,6 @@ void InitializeFlags(Flags *f, const char *env) { int asan_inited; bool asan_init_is_running; void (*death_callback)(void); -static void (*error_report_callback)(const char*); -char *error_message_buffer = 0; -uptr error_message_buffer_pos = 0; -uptr error_message_buffer_size = 0; // -------------------------- Misc ---------------- {{{1 void ShowStatsAndAbort() { @@ -155,146 +171,23 @@ void ShowStatsAndAbort() { Die(); } -static void PrintBytes(const char *before, uptr *a) { - u8 *bytes = (u8*)a; - uptr byte_num = (__WORDSIZE) / 8; - AsanPrintf("%s%p:", before, (void*)a); - for (uptr i = 0; i < byte_num; i++) { - AsanPrintf(" %x%x", bytes[i] >> 4, bytes[i] & 15); - } - AsanPrintf("\n"); -} - -void AppendToErrorMessageBuffer(const char *buffer) { - if (error_message_buffer) { - uptr length = internal_strlen(buffer); - CHECK_GE(error_message_buffer_size, error_message_buffer_pos); - uptr remaining = error_message_buffer_size - error_message_buffer_pos; - internal_strncpy(error_message_buffer + error_message_buffer_pos, - buffer, remaining); - error_message_buffer[error_message_buffer_size - 1] = '\0'; - // FIXME: reallocate the buffer instead of truncating the message. - error_message_buffer_pos += remaining > length ? length : remaining; - } -} - // ---------------------- mmap -------------------- {{{1 // Reserve memory range [beg, end]. static void ReserveShadowMemoryRange(uptr beg, uptr end) { - CHECK((beg % kPageSize) == 0); - CHECK(((end + 1) % kPageSize) == 0); + CHECK((beg % GetPageSizeCached()) == 0); + CHECK(((end + 1) % GetPageSizeCached()) == 0); uptr size = end - beg + 1; void *res = MmapFixedNoReserve(beg, size); - CHECK(res == (void*)beg && "ReserveShadowMemoryRange failed"); -} - -// ---------------------- LowLevelAllocator ------------- {{{1 -void *LowLevelAllocator::Allocate(uptr size) { - CHECK((size & (size - 1)) == 0 && "size must be a power of two"); - if (allocated_end_ - allocated_current_ < (sptr)size) { - uptr size_to_allocate = Max(size, kPageSize); - allocated_current_ = - (char*)MmapOrDie(size_to_allocate, __FUNCTION__); - allocated_end_ = allocated_current_ + size_to_allocate; - PoisonShadow((uptr)allocated_current_, size_to_allocate, - kAsanInternalHeapMagic); - } - CHECK(allocated_end_ - allocated_current_ >= (sptr)size); - void *res = allocated_current_; - allocated_current_ += size; - return res; -} - -// ---------------------- DescribeAddress -------------------- {{{1 -static bool DescribeStackAddress(uptr addr, uptr access_size) { - AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr); - if (!t) return false; - const sptr kBufSize = 4095; - char buf[kBufSize]; - uptr offset = 0; - const char *frame_descr = t->GetFrameNameByAddr(addr, &offset); - // This string is created by the compiler and has the following form: - // "FunctioName n alloc_1 alloc_2 ... alloc_n" - // where alloc_i looks like "offset size len ObjectName ". - CHECK(frame_descr); - // Report the function name and the offset. - const char *name_end = internal_strchr(frame_descr, ' '); - CHECK(name_end); - buf[0] = 0; - internal_strncat(buf, frame_descr, - Min(kBufSize, - static_cast<sptr>(name_end - frame_descr))); - AsanPrintf("Address %p is located at offset %zu " - "in frame <%s> of T%d's stack:\n", - (void*)addr, offset, buf, t->tid()); - // Report the number of stack objects. - char *p; - uptr n_objects = internal_simple_strtoll(name_end, &p, 10); - CHECK(n_objects > 0); - AsanPrintf(" This frame has %zu object(s):\n", n_objects); - // Report all objects in this frame. - for (uptr i = 0; i < n_objects; i++) { - uptr beg, size; - sptr len; - beg = internal_simple_strtoll(p, &p, 10); - size = internal_simple_strtoll(p, &p, 10); - len = internal_simple_strtoll(p, &p, 10); - if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') { - AsanPrintf("AddressSanitizer can't parse the stack frame " - "descriptor: |%s|\n", frame_descr); - break; - } - p++; - buf[0] = 0; - internal_strncat(buf, p, Min(kBufSize, len)); - p += len; - AsanPrintf(" [%zu, %zu) '%s'\n", beg, beg + size, buf); - } - AsanPrintf("HINT: this may be a false positive if your program uses " - "some custom stack unwind mechanism\n" - " (longjmp and C++ exceptions *are* supported)\n"); - t->summary()->Announce(); - return true; -} - -static bool DescribeAddrIfShadow(uptr addr) { - if (AddrIsInMem(addr)) - return false; - static const char kAddrInShadowReport[] = - "Address %p is located in the %s.\n"; - if (AddrIsInShadowGap(addr)) { - AsanPrintf(kAddrInShadowReport, addr, "shadow gap area"); - return true; - } - if (AddrIsInHighShadow(addr)) { - AsanPrintf(kAddrInShadowReport, addr, "high shadow area"); - return true; - } - if (AddrIsInLowShadow(addr)) { - AsanPrintf(kAddrInShadowReport, addr, "low shadow area"); - return true; + if (res != (void*)beg) { + Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " + "Perhaps you're using ulimit -v\n", size); + Abort(); } - - CHECK(0); // Unreachable. - return false; } -static NOINLINE void DescribeAddress(uptr addr, uptr access_size) { - // Check if this is shadow or shadow gap. - if (DescribeAddrIfShadow(addr)) - return; - - CHECK(AddrIsInMem(addr)); - - // Check if this is a global. - if (DescribeAddrIfGlobal(addr)) - return; - - if (DescribeStackAddress(addr, access_size)) - return; - - // finally, check if this is a heap. - DescribeHeapAddress(addr, access_size); +// --------------- LowLevelAllocateCallbac ---------- {{{1 +static void OnLowLevelAllocate(uptr ptr, uptr size) { + PoisonShadow(ptr, size, kAsanInternalHeapMagic); } // -------------------------- Run-time entry ------------------- {{{1 @@ -325,29 +218,48 @@ ASAN_REPORT_ERROR(store, true, 16) // time. static NOINLINE void force_interface_symbols() { volatile int fake_condition = 0; // prevent dead condition elimination. - if (fake_condition) { - __asan_report_load1(0); - __asan_report_load2(0); - __asan_report_load4(0); - __asan_report_load8(0); - __asan_report_load16(0); - __asan_report_store1(0); - __asan_report_store2(0); - __asan_report_store4(0); - __asan_report_store8(0); - __asan_report_store16(0); - __asan_register_global(0, 0, 0); - __asan_register_globals(0, 0); - __asan_unregister_globals(0, 0); - __asan_set_death_callback(0); - __asan_set_error_report_callback(0); - __asan_handle_no_return(); + // __asan_report_* functions are noreturn, so we need a switch to prevent + // the compiler from removing any of them. + switch (fake_condition) { + case 1: __asan_report_load1(0); break; + case 2: __asan_report_load2(0); break; + case 3: __asan_report_load4(0); break; + case 4: __asan_report_load8(0); break; + case 5: __asan_report_load16(0); break; + case 6: __asan_report_store1(0); break; + case 7: __asan_report_store2(0); break; + case 8: __asan_report_store4(0); break; + case 9: __asan_report_store8(0); break; + case 10: __asan_report_store16(0); break; + case 12: __asan_register_globals(0, 0); break; + case 13: __asan_unregister_globals(0, 0); break; + case 14: __asan_set_death_callback(0); break; + case 15: __asan_set_error_report_callback(0); break; + case 16: __asan_handle_no_return(); break; + case 17: __asan_address_is_poisoned(0); break; + case 18: __asan_get_allocated_size(0); break; + case 19: __asan_get_current_allocated_bytes(); break; + case 20: __asan_get_estimated_allocated_size(0); break; + case 21: __asan_get_free_bytes(); break; + case 22: __asan_get_heap_size(); break; + case 23: __asan_get_ownership(0); break; + case 24: __asan_get_unmapped_bytes(); break; + case 25: __asan_poison_memory_region(0, 0); break; + case 26: __asan_unpoison_memory_region(0, 0); break; + 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 31: __asan_after_dynamic_init(); break; + case 32: __asan_poison_stack_memory(0, 0); break; + case 33: __asan_unpoison_stack_memory(0, 0); break; + case 34: __asan_region_is_poisoned(0, 0); break; + case 35: __asan_describe_address(0); break; } } -// -------------------------- Init ------------------- {{{1 static void asan_atexit() { - AsanPrintf("AddressSanitizer exit stats:\n"); + Printf("AddressSanitizer exit stats:\n"); __asan_print_accumulated_stats(); } @@ -356,7 +268,14 @@ static void asan_atexit() { // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT -int __asan_set_error_exit_code(int exit_code) { +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +extern "C" { +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +const char* __asan_default_options() { return ""; } +} // extern "C" +#endif + +int NOINLINE __asan_set_error_exit_code(int exit_code) { int old = flags()->exitcode; flags()->exitcode = exit_code; return old; @@ -366,8 +285,9 @@ void NOINLINE __asan_handle_no_return() { int local_stack; AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); CHECK(curr_thread); + uptr PageSize = GetPageSizeCached(); uptr top = curr_thread->stack_top(); - uptr bottom = ((uptr)&local_stack - kPageSize) & ~(kPageSize-1); + uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1); PoisonShadow(bottom, top - bottom, 0); } @@ -375,134 +295,35 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) { death_callback = callback; } -void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { - error_report_callback = callback; - if (callback) { - error_message_buffer_size = 1 << 16; - error_message_buffer = - (char*)MmapOrDie(error_message_buffer_size, __FUNCTION__); - error_message_buffer_pos = 0; - } -} - -void __asan_report_error(uptr pc, uptr bp, uptr sp, - uptr addr, bool is_write, uptr access_size) { - static atomic_uint32_t num_calls; - if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { - // Do not print more than one report, otherwise they will mix up. - // We can not return here because the function is marked as never-return. - AsanPrintf("AddressSanitizer: while reporting a bug found another one." - "Ignoring.\n"); - SleepForSeconds(5); - Die(); - } - - AsanPrintf("====================================================" - "=============\n"); - const char *bug_descr = "unknown-crash"; - if (AddrIsInMem(addr)) { - u8 *shadow_addr = (u8*)MemToShadow(addr); - // If we are accessing 16 bytes, look at the second shadow byte. - if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) - shadow_addr++; - // If we are in the partial right redzone, look at the next shadow byte. - if (*shadow_addr > 0 && *shadow_addr < 128) - shadow_addr++; - switch (*shadow_addr) { - case kAsanHeapLeftRedzoneMagic: - case kAsanHeapRightRedzoneMagic: - bug_descr = "heap-buffer-overflow"; - break; - case kAsanHeapFreeMagic: - bug_descr = "heap-use-after-free"; - break; - case kAsanStackLeftRedzoneMagic: - bug_descr = "stack-buffer-underflow"; - break; - case kAsanStackMidRedzoneMagic: - case kAsanStackRightRedzoneMagic: - case kAsanStackPartialRedzoneMagic: - bug_descr = "stack-buffer-overflow"; - break; - case kAsanStackAfterReturnMagic: - bug_descr = "stack-use-after-return"; - break; - case kAsanUserPoisonedMemoryMagic: - bug_descr = "use-after-poison"; - break; - case kAsanGlobalRedzoneMagic: - bug_descr = "global-buffer-overflow"; - break; - } - } - - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); - u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); - - if (curr_thread) { - // We started reporting an error message. Stop using the fake stack - // in case we will call an instrumented function from a symbolizer. - curr_thread->fake_stack().StopUsingFakeStack(); - } - - AsanReport("ERROR: AddressSanitizer %s on address " - "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n", - bug_descr, (void*)addr, pc, bp, sp); - - AsanPrintf("%s of size %zu at %p thread T%d\n", - access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", - access_size, (void*)addr, curr_tid); - - if (flags()->debug) { - PrintBytes("PC: ", (uptr*)pc); - } - - GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp); - stack.PrintStack(); - - DescribeAddress(addr, access_size); - - if (AddrIsInMem(addr)) { - uptr shadow_addr = MemToShadow(addr); - AsanReport("ABORTING\n"); - __asan_print_accumulated_stats(); - AsanPrintf("Shadow byte and word:\n"); - AsanPrintf(" %p: %x\n", (void*)shadow_addr, *(unsigned char*)shadow_addr); - uptr aligned_shadow = shadow_addr & ~(kWordSize - 1); - PrintBytes(" ", (uptr*)(aligned_shadow)); - AsanPrintf("More shadow bytes:\n"); - PrintBytes(" ", (uptr*)(aligned_shadow-4*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow-3*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow-2*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow-1*kWordSize)); - PrintBytes("=>", (uptr*)(aligned_shadow+0*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow+1*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow+2*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow+3*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow+4*kWordSize)); - } - if (error_report_callback) { - error_report_callback(error_message_buffer); - } - Die(); -} - - void __asan_init() { if (asan_inited) return; + CHECK(!asan_init_is_running && "ASan init calls itself!"); asan_init_is_running = true; // Make sure we are not statically linked. AsanDoesNotSupportStaticLinkage(); - // Initialize flags. + // Install tool-specific callbacks in sanitizer_common. + SetDieCallback(AsanDie); + SetCheckFailedCallback(AsanCheckFailed); + SetPrintfAndReportCallback(AppendToErrorMessageBuffer); + + // Initialize flags. This must be done early, because most of the + // initialization steps look at flags(). const char *options = GetEnv("ASAN_OPTIONS"); InitializeFlags(flags(), options); + __sanitizer_set_report_path(flags()->log_path); if (flags()->verbosity && options) { Report("Parsed ASAN_OPTIONS: %s\n", options); } + // Re-exec ourselves if we need to set additional env or command line args. + MaybeReexec(); + + // Setup internal allocator callback. + SetLowLevelAllocateCallback(OnLowLevelAllocate); + if (flags()->atexit) { Atexit(asan_atexit); } @@ -543,12 +364,13 @@ void __asan_init() { } uptr shadow_start = kLowShadowBeg; - if (kLowShadowBeg > 0) shadow_start -= kMmapGranularity; + 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 - kMmapGranularity, kLowShadowEnd); + ReserveShadowMemoryRange(kLowShadowBeg - GetMmapGranularity(), + kLowShadowEnd); } // mmap the high shadow. ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); @@ -563,6 +385,13 @@ 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); + } + } // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited // should be set to 1 prior to initializing the threads. diff --git a/lib/asan/asan_stack.cc b/lib/asan/asan_stack.cc index d6103c2c98fa..ebf22fd34ca1 100644 --- a/lib/asan/asan_stack.cc +++ b/lib/asan/asan_stack.cc @@ -11,222 +11,33 @@ // // Code for ASan stack trace. //===----------------------------------------------------------------------===// -#include "asan_interceptors.h" -#include "asan_lock.h" +#include "asan_flags.h" #include "asan_stack.h" -#include "asan_thread.h" -#include "asan_thread_registry.h" -#include "sanitizer_common/sanitizer_procmaps.h" -#include "sanitizer_common/sanitizer_symbolizer.h" - -#ifdef ASAN_USE_EXTERNAL_SYMBOLIZER -extern bool -ASAN_USE_EXTERNAL_SYMBOLIZER(const void *pc, char *out, int out_size); -#endif +#include "sanitizer/asan_interface.h" namespace __asan { -// ----------------------- AsanStackTrace ----------------------------- {{{1 -// PCs in stack traces are actually the return addresses, that is, -// addresses of the next instructions after the call. That's why we -// decrement them. -static uptr patch_pc(uptr pc) { -#ifdef __arm__ - // Cancel Thumb bit. - pc = pc & (~1); -#endif - return pc - 1; +static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer, + int out_size) { + return (&__asan_symbolize) ? __asan_symbolize(pc, out_buffer, out_size) + : false; } -#if defined(ASAN_USE_EXTERNAL_SYMBOLIZER) -void AsanStackTrace::PrintStack(uptr *addr, uptr size) { - for (uptr i = 0; i < size && addr[i]; i++) { - uptr pc = addr[i]; - if (i < size - 1 && addr[i + 1]) - pc = patch_pc(pc); - char buff[4096]; - ASAN_USE_EXTERNAL_SYMBOLIZER((void*)pc, buff, sizeof(buff)); - AsanPrintf(" #%zu 0x%zx %s\n", i, pc, buff); - } +void PrintStack(StackTrace *stack) { + stack->PrintStack(stack->trace, stack->size, flags()->symbolize, + flags()->strip_path_prefix, MaybeCallAsanSymbolize); } -#else // ASAN_USE_EXTERNAL_SYMBOLIZER -void AsanStackTrace::PrintStack(uptr *addr, uptr size) { - ProcessMaps proc_maps; - uptr frame_num = 0; - for (uptr i = 0; i < size && addr[i]; i++) { - uptr pc = addr[i]; - if (i < size - 1 && addr[i + 1]) - pc = patch_pc(pc); - AddressInfo addr_frames[64]; - uptr addr_frames_num = 0; - if (flags()->symbolize) { - addr_frames_num = SymbolizeCode(pc, addr_frames, - ASAN_ARRAY_SIZE(addr_frames)); - } - if (addr_frames_num > 0) { - for (uptr j = 0; j < addr_frames_num; j++) { - AddressInfo &info = addr_frames[j]; - AsanPrintf(" #%zu 0x%zx", frame_num, pc); - if (info.function) { - AsanPrintf(" in %s", info.function); - } - if (info.file) { - AsanPrintf(" %s:%d:%d", info.file, info.line, info.column); - } else if (info.module) { - AsanPrintf(" (%s+0x%zx)", info.module, info.module_offset); - } - AsanPrintf("\n"); - info.Clear(); - frame_num++; - } - } else { - uptr offset; - char filename[4096]; - if (proc_maps.GetObjectNameAndOffset(pc, &offset, - filename, sizeof(filename))) { - AsanPrintf(" #%zu 0x%zx (%s+0x%zx)\n", frame_num, pc, filename, - offset); - } else { - AsanPrintf(" #%zu 0x%zx\n", frame_num, pc); - } - frame_num++; - } - } -} -#endif // ASAN_USE_EXTERNAL_SYMBOLIZER +} // namespace __asan -uptr AsanStackTrace::GetCurrentPc() { - return GET_CALLER_PC(); -} +// ------------------ Interface -------------- {{{1 -void AsanStackTrace::FastUnwindStack(uptr pc, uptr bp) { - CHECK(size == 0 && trace[0] == pc); - size = 1; - if (!asan_inited) return; - AsanThread *t = asanThreadRegistry().GetCurrent(); - if (!t) return; - uptr *frame = (uptr*)bp; - uptr *prev_frame = frame; - uptr *top = (uptr*)t->stack_top(); - uptr *bottom = (uptr*)t->stack_bottom(); - while (frame >= prev_frame && - frame < top - 2 && - frame > bottom && - size < max_size) { - uptr pc1 = frame[1]; - if (pc1 != pc) { - trace[size++] = pc1; - } - prev_frame = frame; - frame = (uptr*)frame[0]; - } +// 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 +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) { + return false; } - -// On 32-bits we don't compress stack traces. -// On 64-bits we compress stack traces: if a given pc differes slightly from -// the previous one, we record a 31-bit offset instead of the full pc. -uptr AsanStackTrace::CompressStack(AsanStackTrace *stack, - u32 *compressed, uptr size) { -#if __WORDSIZE == 32 - // Don't compress, just copy. - uptr res = 0; - for (uptr i = 0; i < stack->size && i < size; i++) { - compressed[i] = stack->trace[i]; - res++; - } - if (stack->size < size) - compressed[stack->size] = 0; -#else // 64 bits, compress. - uptr prev_pc = 0; - const uptr kMaxOffset = (1ULL << 30) - 1; - uptr c_index = 0; - uptr res = 0; - for (uptr i = 0, n = stack->size; i < n; i++) { - uptr pc = stack->trace[i]; - if (!pc) break; - if ((s64)pc < 0) break; - // Printf("C pc[%zu] %zx\n", i, pc); - if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) { - uptr offset = (s64)(pc - prev_pc); - offset |= (1U << 31); - if (c_index >= size) break; - // Printf("C co[%zu] offset %zx\n", i, offset); - compressed[c_index++] = offset; - } else { - uptr hi = pc >> 32; - uptr lo = (pc << 32) >> 32; - CHECK((hi & (1 << 31)) == 0); - if (c_index + 1 >= size) break; - // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo); - compressed[c_index++] = hi; - compressed[c_index++] = lo; - } - res++; - prev_pc = pc; - } - if (c_index < size) - compressed[c_index] = 0; - if (c_index + 1 < size) - compressed[c_index + 1] = 0; -#endif // __WORDSIZE - - // debug-only code -#if 0 - AsanStackTrace check_stack; - UncompressStack(&check_stack, compressed, size); - if (res < check_stack.size) { - Printf("res %zu check_stack.size %zu; c_size %zu\n", res, - check_stack.size, size); - } - // |res| may be greater than check_stack.size, because - // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames. - CHECK(res >= check_stack.size); - CHECK(0 == REAL(memcmp)(check_stack.trace, stack->trace, - check_stack.size * sizeof(uptr))); #endif - - return res; -} - -void AsanStackTrace::UncompressStack(AsanStackTrace *stack, - u32 *compressed, uptr size) { -#if __WORDSIZE == 32 - // Don't uncompress, just copy. - stack->size = 0; - for (uptr i = 0; i < size && i < kStackTraceMax; i++) { - if (!compressed[i]) break; - stack->size++; - stack->trace[i] = compressed[i]; - } -#else // 64 bits, uncompress - uptr prev_pc = 0; - stack->size = 0; - for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) { - u32 x = compressed[i]; - uptr pc = 0; - if (x & (1U << 31)) { - // Printf("U co[%zu] offset: %x\n", i, x); - // this is an offset - s32 offset = x; - offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend. - pc = prev_pc + offset; - CHECK(pc); - } else { - // CHECK(i + 1 < size); - if (i + 1 >= size) break; - uptr hi = x; - uptr lo = compressed[i+1]; - // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo); - i++; - pc = (hi << 32) | lo; - if (!pc) break; - } - // Printf("U pc[%zu] %zx\n", stack->size, pc); - stack->trace[stack->size++] = pc; - prev_pc = pc; - } -#endif // __WORDSIZE -} - -} // namespace __asan diff --git a/lib/asan/asan_stack.h b/lib/asan/asan_stack.h index 6ca9a0b28bfc..46c9f3408725 100644 --- a/lib/asan/asan_stack.h +++ b/lib/asan/asan_stack.h @@ -14,91 +14,53 @@ #ifndef ASAN_STACK_H #define ASAN_STACK_H -#include "asan_internal.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "asan_flags.h" namespace __asan { -static const uptr kStackTraceMax = 64; - -struct AsanStackTrace { - uptr size; - uptr max_size; - uptr trace[kStackTraceMax]; - static void PrintStack(uptr *addr, uptr size); - void PrintStack() { - PrintStack(this->trace, this->size); - } - void CopyTo(uptr *dst, uptr dst_size) { - for (uptr i = 0; i < size && i < dst_size; i++) - dst[i] = trace[i]; - for (uptr i = size; i < dst_size; i++) - dst[i] = 0; - } - - void CopyFrom(uptr *src, uptr src_size) { - size = src_size; - if (size > kStackTraceMax) size = kStackTraceMax; - for (uptr i = 0; i < size; i++) { - trace[i] = src[i]; - } - } - - void GetStackTrace(uptr max_s, uptr pc, uptr bp); - - void FastUnwindStack(uptr pc, uptr bp); - - static uptr GetCurrentPc(); - - static uptr CompressStack(AsanStackTrace *stack, - u32 *compressed, uptr size); - static void UncompressStack(AsanStackTrace *stack, - u32 *compressed, uptr size); -}; +void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast); +void PrintStack(StackTrace *stack); } // namespace __asan -// Use this macro if you want to print stack trace with the caller -// of the current function in the top frame. -#define GET_CALLER_PC_BP_SP \ - uptr bp = GET_CURRENT_FRAME(); \ - uptr pc = GET_CALLER_PC(); \ - uptr local_stack; \ - uptr sp = (uptr)&local_stack - -// Use this macro if you want to print stack trace with the current -// function in the top frame. -#define GET_CURRENT_PC_BP_SP \ - uptr bp = GET_CURRENT_FRAME(); \ - uptr pc = AsanStackTrace::GetCurrentPc(); \ - uptr local_stack; \ - uptr sp = (uptr)&local_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. -#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp) \ - AsanStackTrace stack; \ - stack.GetStackTrace(max_s, pc, bp) +#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ + StackTrace stack; \ + GetStackTrace(&stack, max_s, pc, bp, fast) // 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 // don't want stack trace to contain functions from ASan internals. -#define GET_STACK_TRACE_HERE(max_size) \ +#define GET_STACK_TRACE(max_size, fast) \ GET_STACK_TRACE_WITH_PC_AND_BP(max_size, \ - AsanStackTrace::GetCurrentPc(), GET_CURRENT_FRAME()) + StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), fast) + +#define GET_STACK_TRACE_FATAL(pc, bp) \ + GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp, \ + flags()->fast_unwind_on_fatal) + +#define GET_STACK_TRACE_FATAL_HERE \ + GET_STACK_TRACE(kStackTraceMax, flags()->fast_unwind_on_fatal) + +#define GET_STACK_TRACE_THREAD \ + GET_STACK_TRACE(kStackTraceMax, true) -#define GET_STACK_TRACE_HERE_FOR_MALLOC \ - GET_STACK_TRACE_HERE(flags()->malloc_context_size) +#define GET_STACK_TRACE_MALLOC \ + GET_STACK_TRACE(flags()->malloc_context_size, \ + flags()->fast_unwind_on_malloc) -#define GET_STACK_TRACE_HERE_FOR_FREE(ptr) \ - GET_STACK_TRACE_HERE(flags()->malloc_context_size) +#define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC #define PRINT_CURRENT_STACK() \ { \ - GET_STACK_TRACE_HERE(kStackTraceMax); \ - stack.PrintStack(); \ + GET_STACK_TRACE(kStackTraceMax, \ + flags()->fast_unwind_on_fatal); \ + PrintStack(&stack); \ } #endif // ASAN_STACK_H diff --git a/lib/asan/asan_stats.cc b/lib/asan/asan_stats.cc index ef5e53a8bf33..c57c8cc61aed 100644 --- a/lib/asan/asan_stats.cc +++ b/lib/asan/asan_stats.cc @@ -12,11 +12,11 @@ // Code related to statistics collected by AddressSanitizer. //===----------------------------------------------------------------------===// #include "asan_interceptors.h" -#include "asan_interface.h" #include "asan_internal.h" -#include "asan_lock.h" #include "asan_stats.h" #include "asan_thread_registry.h" +#include "sanitizer/asan_interface.h" +#include "sanitizer_common/sanitizer_stackdepot.h" namespace __asan { @@ -27,39 +27,45 @@ AsanStats::AsanStats() { static void PrintMallocStatsArray(const char *prefix, uptr (&array)[kNumberOfSizeClasses]) { - AsanPrintf("%s", prefix); + Printf("%s", prefix); for (uptr i = 0; i < kNumberOfSizeClasses; i++) { if (!array[i]) continue; - AsanPrintf("%zu:%zu; ", i, array[i]); + Printf("%zu:%zu; ", i, array[i]); } - AsanPrintf("\n"); + Printf("\n"); } void AsanStats::Print() { - AsanPrintf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n", + Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n", malloced>>20, malloced_redzones>>20, mallocs); - AsanPrintf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs); - AsanPrintf("Stats: %zuM freed by %zu calls\n", freed>>20, frees); - AsanPrintf("Stats: %zuM really freed by %zu calls\n", + Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs); + Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees); + Printf("Stats: %zuM really freed by %zu calls\n", really_freed>>20, real_frees); - AsanPrintf("Stats: %zuM (%zu full pages) mmaped in %zu calls\n", - mmaped>>20, mmaped / kPageSize, mmaps); + Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n", + (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20, + mmaps, munmaps); PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size); PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size); PrintMallocStatsArray(" frees by size class: ", freed_by_size); PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size); - AsanPrintf("Stats: malloc large: %zu small slow: %zu\n", + Printf("Stats: malloc large: %zu small slow: %zu\n", malloc_large, malloc_small_slow); } -static AsanLock print_lock(LINKER_INITIALIZED); +static BlockingMutex print_lock(LINKER_INITIALIZED); static void PrintAccumulatedStats() { - AsanStats stats = asanThreadRegistry().GetAccumulatedStats(); + AsanStats stats; + asanThreadRegistry().GetAccumulatedStats(&stats); // Use lock to keep reports from mixing up. - ScopedLock lock(&print_lock); + BlockingMutexLock lock(&print_lock); stats.Print(); + StackDepotStats *stack_depot_stats = StackDepotGetStats(); + Printf("Stats: StackDepot: %zd ids; %zdM mapped\n", + stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20); + PrintInternalAllocatorStats(); } } // namespace __asan diff --git a/lib/asan/asan_stats.h b/lib/asan/asan_stats.h index b4c63f44fc68..37846bc92ad2 100644 --- a/lib/asan/asan_stats.h +++ b/lib/asan/asan_stats.h @@ -37,6 +37,8 @@ struct AsanStats { uptr realloced; uptr mmaps; uptr mmaped; + uptr munmaps; + uptr munmaped; uptr mmaped_by_size[kNumberOfSizeClasses]; uptr malloced_by_size[kNumberOfSizeClasses]; uptr freed_by_size[kNumberOfSizeClasses]; @@ -54,6 +56,14 @@ struct AsanStats { void Print(); }; +// A cross-platform equivalent of malloc_statistics_t on Mac OS. +struct AsanMallocStats { + uptr blocks_in_use; + uptr size_in_use; + uptr max_size_in_use; + uptr size_allocated; +}; + } // namespace __asan #endif // ASAN_STATS_H diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc index 05a41ea9685a..778e91932ed5 100644 --- a/lib/asan/asan_thread.cc +++ b/lib/asan/asan_thread.cc @@ -26,24 +26,18 @@ AsanThread::AsanThread(LinkerInitialized x) malloc_storage_(x), stats_(x) { } -static AsanLock mu_for_thread_summary(LINKER_INITIALIZED); -static LowLevelAllocator allocator_for_thread_summary(LINKER_INITIALIZED); - AsanThread *AsanThread::Create(u32 parent_tid, thread_callback_t start_routine, - void *arg, AsanStackTrace *stack) { - uptr size = RoundUpTo(sizeof(AsanThread), kPageSize); + void *arg, StackTrace *stack) { + 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 = 1024; + const uptr kSummaryAllocSize = PageSize; CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize); - AsanThreadSummary *summary; - { - ScopedLock lock(&mu_for_thread_summary); - summary = (AsanThreadSummary*) - allocator_for_thread_summary.Allocate(kSummaryAllocSize); - } + AsanThreadSummary *summary = + (AsanThreadSummary*)MmapOrDie(PageSize, "AsanThreadSummary"); summary->Init(parent_tid, stack); summary->set_thread(thread); thread->set_summary(summary); @@ -73,14 +67,14 @@ void AsanThread::Destroy() { // and we don't want it to have any poisoned stack. ClearShadowForThreadStack(); fake_stack().Cleanup(); - uptr size = RoundUpTo(sizeof(AsanThread), kPageSize); + uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached()); UnmapOrDie(this, size); } void AsanThread::Init() { SetThreadStackTopAndBottom(); CHECK(AddrIsInMem(stack_bottom_)); - CHECK(AddrIsInMem(stack_top_)); + CHECK(AddrIsInMem(stack_top_ - 1)); ClearShadowForThreadStack(); if (flags()->verbosity >= 1) { int local = 0; @@ -125,25 +119,25 @@ void AsanThread::ClearShadowForThreadStack() { const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { uptr bottom = 0; - bool is_fake_stack = false; if (AddrIsInStack(addr)) { bottom = stack_bottom(); } else { bottom = fake_stack().AddrIsInFakeStack(addr); CHECK(bottom); - is_fake_stack = true; + *offset = addr - bottom; + return (const char *)((uptr*)bottom)[1]; } - uptr aligned_addr = addr & ~(__WORDSIZE/8 - 1); // align addr. + uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr. u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); u8 *shadow_bottom = (u8*)MemToShadow(bottom); while (shadow_ptr >= shadow_bottom && - *shadow_ptr != kAsanStackLeftRedzoneMagic) { + *shadow_ptr != kAsanStackLeftRedzoneMagic) { shadow_ptr--; } while (shadow_ptr >= shadow_bottom && - *shadow_ptr == kAsanStackLeftRedzoneMagic) { + *shadow_ptr == kAsanStackLeftRedzoneMagic) { shadow_ptr--; } @@ -153,8 +147,7 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { } uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1)); - CHECK((ptr[0] == kCurrentStackFrameMagic) || - (is_fake_stack && ptr[0] == kRetiredStackFrameMagic)); + CHECK(ptr[0] == kCurrentStackFrameMagic); *offset = addr - (uptr)ptr; return (const char*)ptr[1]; } diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h index 9a032fe3e66c..acc27e52e224 100644 --- a/lib/asan/asan_thread.h +++ b/lib/asan/asan_thread.h @@ -31,7 +31,7 @@ class AsanThread; class AsanThreadSummary { public: explicit AsanThreadSummary(LinkerInitialized) { } // for T0. - void Init(u32 parent_tid, AsanStackTrace *stack) { + void Init(u32 parent_tid, StackTrace *stack) { parent_tid_ = parent_tid; announced_ = false; tid_ = kInvalidTid; @@ -39,35 +39,40 @@ class AsanThreadSummary { internal_memcpy(&stack_, stack, sizeof(*stack)); } thread_ = 0; - } - void Announce() { - if (tid_ == 0) return; // no need to announce the main thread. - if (!announced_) { - announced_ = true; - AsanPrintf("Thread T%d created by T%d here:\n", tid_, parent_tid_); - stack_.PrintStack(); - } + name_[0] = 0; } 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_; } private: u32 tid_; u32 parent_tid_; bool announced_; - AsanStackTrace stack_; + StackTrace stack_; AsanThread *thread_; + char name_[128]; }; +// AsanThreadSummary objects are never freed, so we need many of them. +COMPILER_CHECK(sizeof(AsanThreadSummary) <= 4094); + // 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, AsanStackTrace *stack); + void *arg, StackTrace *stack); void Destroy(); void Init(); // Should be called from the thread itself. @@ -91,7 +96,6 @@ class AsanThread { AsanStats &stats() { return stats_; } private: - void SetThreadStackTopAndBottom(); void ClearShadowForThreadStack(); AsanThreadSummary *summary_; diff --git a/lib/asan/asan_thread_registry.cc b/lib/asan/asan_thread_registry.cc index 4540d589c552..80675405fbd5 100644 --- a/lib/asan/asan_thread_registry.cc +++ b/lib/asan/asan_thread_registry.cc @@ -20,7 +20,7 @@ namespace __asan { -static AsanThreadRegistry asan_thread_registry(__asan::LINKER_INITIALIZED); +static AsanThreadRegistry asan_thread_registry(LINKER_INITIALIZED); AsanThreadRegistry &asanThreadRegistry() { return asan_thread_registry; @@ -30,6 +30,7 @@ AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x) : main_thread_(x), main_thread_summary_(x), accumulated_stats_(x), + max_malloced_memory_(x), mu_(x) { } void AsanThreadRegistry::Init() { @@ -43,7 +44,7 @@ void AsanThreadRegistry::Init() { } void AsanThreadRegistry::RegisterThread(AsanThread *thread) { - ScopedLock lock(&mu_); + BlockingMutexLock lock(&mu_); u32 tid = n_threads_; n_threads_++; CHECK(n_threads_ < kMaxNumberOfThreads); @@ -55,7 +56,7 @@ void AsanThreadRegistry::RegisterThread(AsanThread *thread) { } void AsanThreadRegistry::UnregisterThread(AsanThread *thread) { - ScopedLock lock(&mu_); + BlockingMutexLock lock(&mu_); FlushToAccumulatedStatsUnlocked(&thread->stats()); AsanThreadSummary *summary = thread->summary(); CHECK(summary); @@ -69,7 +70,7 @@ AsanThread *AsanThreadRegistry::GetMain() { AsanThread *AsanThreadRegistry::GetCurrent() { AsanThreadSummary *summary = (AsanThreadSummary *)AsanTSDGet(); if (!summary) { -#ifdef ANDROID +#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 @@ -103,32 +104,51 @@ AsanStats &AsanThreadRegistry::GetCurrentThreadStats() { return (t) ? t->stats() : main_thread_.stats(); } -AsanStats AsanThreadRegistry::GetAccumulatedStats() { - ScopedLock lock(&mu_); +void AsanThreadRegistry::GetAccumulatedStats(AsanStats *stats) { + BlockingMutexLock lock(&mu_); UpdateAccumulatedStatsUnlocked(); - return accumulated_stats_; + internal_memcpy(stats, &accumulated_stats_, sizeof(accumulated_stats_)); } uptr AsanThreadRegistry::GetCurrentAllocatedBytes() { - ScopedLock lock(&mu_); + BlockingMutexLock lock(&mu_); UpdateAccumulatedStatsUnlocked(); - return accumulated_stats_.malloced - accumulated_stats_.freed; + 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() { - ScopedLock lock(&mu_); + BlockingMutexLock lock(&mu_); UpdateAccumulatedStatsUnlocked(); - return accumulated_stats_.mmaped; + return accumulated_stats_.mmaped - accumulated_stats_.munmaped; } uptr AsanThreadRegistry::GetFreeBytes() { - ScopedLock lock(&mu_); + BlockingMutexLock lock(&mu_); UpdateAccumulatedStatsUnlocked(); - return accumulated_stats_.mmaped - - accumulated_stats_.malloced - - accumulated_stats_.malloced_redzones - + accumulated_stats_.really_freed - + accumulated_stats_.really_freed_redzones; + 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) { @@ -138,7 +158,7 @@ AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) { } AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) { - ScopedLock lock(&mu_); + BlockingMutexLock lock(&mu_); for (u32 tid = 0; tid < n_threads_; tid++) { AsanThread *t = thread_summaries_[tid]->thread(); if (!t || !(t->fake_stack().StackSize())) continue; @@ -156,6 +176,12 @@ void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() { 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) { diff --git a/lib/asan/asan_thread_registry.h b/lib/asan/asan_thread_registry.h index 7037b9edc161..adb1a6d4f32d 100644 --- a/lib/asan/asan_thread_registry.h +++ b/lib/asan/asan_thread_registry.h @@ -15,10 +15,10 @@ #ifndef ASAN_THREAD_REGISTRY_H #define ASAN_THREAD_REGISTRY_H -#include "asan_lock.h" #include "asan_stack.h" #include "asan_stats.h" #include "asan_thread.h" +#include "sanitizer_common/sanitizer_mutex.h" namespace __asan { @@ -47,12 +47,13 @@ class AsanThreadRegistry { // Returns stats for GetCurrent(), or stats for // T0 if GetCurrent() returns 0. AsanStats &GetCurrentThreadStats(); - // Flushes all thread-local stats to accumulated stats, and returns + // Flushes all thread-local stats to accumulated stats, and makes // a copy of accumulated stats. - AsanStats GetAccumulatedStats(); + void GetAccumulatedStats(AsanStats *stats); uptr GetCurrentAllocatedBytes(); uptr GetHeapSize(); uptr GetFreeBytes(); + void FillMallocStatistics(AsanMallocStats *malloc_stats); AsanThreadSummary *FindByTid(u32 tid); AsanThread *FindThreadByStackAddress(uptr addr); @@ -68,8 +69,11 @@ class AsanThreadRegistry { 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_; - AsanLock mu_; + BlockingMutex mu_; bool inited_; }; diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc index 9e899d5865fa..d8ce050641bc 100644 --- a/lib/asan/asan_win.cc +++ b/lib/asan/asan_win.cc @@ -17,30 +17,29 @@ #include <dbghelp.h> #include <stdlib.h> -#include <new> // FIXME: temporarily needed for placement new in AsanLock. - #include "asan_interceptors.h" #include "asan_internal.h" -#include "asan_lock.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_mutex.h" namespace __asan { // ---------------------- Stacktraces, symbols, etc. ---------------- {{{1 -static AsanLock dbghelp_lock(LINKER_INITIALIZED); +static BlockingMutex dbghelp_lock(LINKER_INITIALIZED); static bool dbghelp_initialized = false; #pragma comment(lib, "dbghelp.lib") -void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) { - max_size = max_s; +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, max_size, tmp, 0), - offset = 0; + 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++) { @@ -50,86 +49,9 @@ void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) { break; } - size = cs_ret - offset; - for (uptr i = 0; i < size; i++) - trace[i] = (uptr)tmp[i + offset]; -} - -bool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size) { - ScopedLock lock(&dbghelp_lock); - if (!dbghelp_initialized) { - SymSetOptions(SYMOPT_DEFERRED_LOADS | - SYMOPT_UNDNAME | - SYMOPT_LOAD_LINES); - CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE)); - // FIXME: We don't call SymCleanup() on exit yet - should we? - dbghelp_initialized = true; - } - - // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx - char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; - PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; - symbol->SizeOfStruct = sizeof(SYMBOL_INFO); - symbol->MaxNameLen = MAX_SYM_NAME; - DWORD64 offset = 0; - BOOL got_objname = SymFromAddr(GetCurrentProcess(), - (DWORD64)addr, &offset, symbol); - if (!got_objname) - return false; - - DWORD unused; - IMAGEHLP_LINE64 info; - info.SizeOfStruct = sizeof(IMAGEHLP_LINE64); - BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), - (DWORD64)addr, &unused, &info); - int written = 0; - out_buffer[0] = '\0'; - // FIXME: it might be useful to print out 'obj' or 'obj+offset' info too. - if (got_fileline) { - written += internal_snprintf(out_buffer + written, buffer_size - written, - " %s %s:%d", symbol->Name, - info.FileName, info.LineNumber); - } else { - written += internal_snprintf(out_buffer + written, buffer_size - written, - " %s+0x%p", symbol->Name, offset); - } - return true; -} - -// ---------------------- AsanLock ---------------- {{{1 -enum LockState { - LOCK_UNINITIALIZED = 0, - LOCK_READY = -1, -}; - -AsanLock::AsanLock(LinkerInitialized li) { - // FIXME: see comments in AsanLock::Lock() for the details. - CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED); - - CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); - InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); - owner_ = LOCK_READY; -} - -void AsanLock::Lock() { - if (owner_ == LOCK_UNINITIALIZED) { - // FIXME: hm, global AsanLock objects are not initialized?!? - // This might be a side effect of the clang+cl+link Frankenbuild... - new(this) AsanLock((LinkerInitialized)(LINKER_INITIALIZED + 1)); - - // FIXME: If it turns out the linker doesn't invoke our - // constructors, we should probably manually Lock/Unlock all the global - // locks while we're starting in one thread to avoid double-init races. - } - EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_); - CHECK(owner_ == LOCK_READY); - owner_ = GetThreadSelf(); -} - -void AsanLock::Unlock() { - CHECK(owner_ == GetThreadSelf()); - owner_ = LOCK_READY; - LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_); + stack->size = cs_ret - offset; + for (uptr i = 0; i < stack->size; i++) + stack->trace[i] = (uptr)tmp[i + offset]; } // ---------------------- TSD ---------------- {{{1 @@ -153,6 +75,10 @@ void AsanTSDSet(void *tsd) { } // ---------------------- Various stuff ---------------- {{{1 +void MaybeReexec() { + // No need to re-exec on Windows. +} + void *AsanDoesNotSupportStaticLinkage() { #if defined(_DEBUG) #error Please build the runtime with a non-debug CRT: /MD or /MT @@ -176,6 +102,58 @@ void AsanPlatformThreadInit() { // Nothing here for now. } +void ReadContextStack(void *context, uptr *stack, uptr *ssize) { + UNIMPLEMENTED(); +} + } // namespace __asan +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +bool __asan_symbolize(const void *addr, char *out_buffer, int buffer_size) { + BlockingMutexLock lock(&dbghelp_lock); + if (!dbghelp_initialized) { + SymSetOptions(SYMOPT_DEFERRED_LOADS | + SYMOPT_UNDNAME | + SYMOPT_LOAD_LINES); + CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE)); + // FIXME: We don't call SymCleanup() on exit yet - should we? + dbghelp_initialized = true; + } + + // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + DWORD64 offset = 0; + BOOL got_objname = SymFromAddr(GetCurrentProcess(), + (DWORD64)addr, &offset, symbol); + if (!got_objname) + return false; + + DWORD unused; + IMAGEHLP_LINE64 info; + info.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), + (DWORD64)addr, &unused, &info); + int written = 0; + out_buffer[0] = '\0'; + // FIXME: it might be useful to print out 'obj' or 'obj+offset' info too. + if (got_fileline) { + written += internal_snprintf(out_buffer + written, buffer_size - written, + " %s %s:%d", symbol->Name, + info.FileName, info.LineNumber); + } else { + written += internal_snprintf(out_buffer + written, buffer_size - written, + " %s+0x%p", symbol->Name, offset); + } + return true; +} +} // extern "C" + + #endif // _WIN32 diff --git a/lib/asan/dynamic/Makefile.mk b/lib/asan/dynamic/Makefile.mk new file mode 100644 index 000000000000..897844e7eed4 --- /dev/null +++ b/lib/asan/dynamic/Makefile.mk @@ -0,0 +1,25 @@ +#===- lib/asan/dynamic/Makefile.mk -------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := asan_dynamic +SubDirs := + +Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) +ObjNames := $(Sources:%.cc=%.o) + +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 the asan dynamic functions. +AsanDynamicFunctions := $(Sources:%.cc=%) diff --git a/lib/asan/dynamic/asan_interceptors_dynamic.cc b/lib/asan/dynamic/asan_interceptors_dynamic.cc new file mode 100644 index 000000000000..4f0f7bd2d5f8 --- /dev/null +++ b/lib/asan/dynamic/asan_interceptors_dynamic.cc @@ -0,0 +1,111 @@ +//===-- 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 new file mode 100644 index 000000000000..1609032d4670 --- /dev/null +++ b/lib/asan/lit_tests/CMakeLists.txt @@ -0,0 +1,32 @@ +set(ASAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) +set(ASAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/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 + ${ASAN_RUNTIME_LIBRARIES} + ) + set(ASAN_TEST_PARAMS + asan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) + if(LLVM_INCLUDE_TESTS) + list(APPEND ASAN_TEST_DEPS AsanUnitTests) + endif() + add_lit_testsuite(check-asan "Running the AddressSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR} + PARAMS ${ASAN_TEST_PARAMS} + DEPENDS ${ASAN_TEST_DEPS} + ) + set_target_properties(check-asan PROPERTIES FOLDER "ASan tests") +endif() diff --git a/lib/asan/lit_tests/Helpers/blacklist-extra.cc b/lib/asan/lit_tests/Helpers/blacklist-extra.cc new file mode 100644 index 000000000000..627115cdda2b --- /dev/null +++ b/lib/asan/lit_tests/Helpers/blacklist-extra.cc @@ -0,0 +1,5 @@ +// This function is broken, but this file is blacklisted +int externalBrokenFunction(int argc) { + char x[10] = {0}; + return x[argc * 10]; // BOOM +} diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc b/lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc new file mode 100644 index 000000000000..09aed2112d5e --- /dev/null +++ b/lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc @@ -0,0 +1,15 @@ +int zero_init() { return 0; } +int badGlobal = zero_init(); +int readBadGlobal() { return badGlobal; } + +namespace badNamespace { +class BadClass { + public: + BadClass() { value = 0; } + int value; +}; +// Global object with non-trivial constructor. +BadClass bad_object; +} // namespace badNamespace + +int accessBadObject() { return badNamespace::bad_object.value; } diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist.txt b/lib/asan/lit_tests/Helpers/initialization-blacklist.txt new file mode 100644 index 000000000000..c5f6610937f0 --- /dev/null +++ b/lib/asan/lit_tests/Helpers/initialization-blacklist.txt @@ -0,0 +1,2 @@ +global-init:*badGlobal* +global-init-type:*badNamespace::BadClass* diff --git a/lib/asan/lit_tests/Helpers/initialization-bug-extra.cc b/lib/asan/lit_tests/Helpers/initialization-bug-extra.cc new file mode 100644 index 000000000000..3c4cb411defa --- /dev/null +++ b/lib/asan/lit_tests/Helpers/initialization-bug-extra.cc @@ -0,0 +1,5 @@ +// This file simply declares a dynamically initialized var by the name of 'y'. +int initY() { + return 5; +} +int y = initY(); diff --git a/lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc b/lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc new file mode 100644 index 000000000000..a3d8f190e58b --- /dev/null +++ b/lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc @@ -0,0 +1,6 @@ +// 'z' is dynamically initialized global from different TU. +extern int z; +int __attribute__((noinline)) initY() { + return z + 1; +} +int y = initY(); diff --git a/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc b/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc new file mode 100644 index 000000000000..490b3339054a --- /dev/null +++ b/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc @@ -0,0 +1,9 @@ +// Linker initialized: +int getAB(); +static int ab = getAB(); +// Function local statics: +int countCalls(); +static int one = countCalls(); +// Constexpr: +int getCoolestInteger(); +static int coolest_integer = getCoolestInteger(); diff --git a/lib/asan/lit_tests/Helpers/lit.local.cfg b/lib/asan/lit_tests/Helpers/lit.local.cfg new file mode 100644 index 000000000000..2fc4d99456b0 --- /dev/null +++ b/lib/asan/lit_tests/Helpers/lit.local.cfg @@ -0,0 +1,3 @@ +# Sources in this directory are helper files for tests which test functionality +# involving multiple translation units. +config.suffixes = [] diff --git a/lib/asan/lit_tests/Linux/clone_test.cc b/lib/asan/lit_tests/Linux/clone_test.cc new file mode 100644 index 000000000000..ca13b22425f2 --- /dev/null +++ b/lib/asan/lit_tests/Linux/clone_test.cc @@ -0,0 +1,48 @@ +// Regression test for: +// http://code.google.com/p/address-sanitizer/issues/detail?id=37 + +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t | FileCheck %s + +#include <stdio.h> +#include <sched.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +int Child(void *arg) { + char x[32] = {0}; // Stack gets poisoned. + printf("Child: %p\n", x); + _exit(1); // NoReturn, stack will remain unpoisoned unless we do something. +} + +int main(int argc, char **argv) { + const int kStackSize = 1 << 20; + char child_stack[kStackSize + 1]; + char *sp = child_stack + kStackSize; // Stack grows down. + printf("Parent: %p\n", sp); + pid_t clone_pid = clone(Child, sp, CLONE_FILES | CLONE_VM, NULL, 0, 0, 0); + int status; + pid_t wait_result = waitpid(clone_pid, &status, __WCLONE); + if (wait_result < 0) { + perror("waitpid"); + return 0; + } + if (wait_result == clone_pid && WIFEXITED(status)) { + // Make sure the child stack was indeed unpoisoned. + for (int i = 0; i < kStackSize; i++) + child_stack[i] = i; + int ret = child_stack[argc - 1]; + printf("PASSED\n"); + // CHECK: PASSED + return ret; + } + 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 new file mode 100644 index 000000000000..645fe1c85ed4 --- /dev/null +++ b/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc @@ -0,0 +1,37 @@ +// 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. + +// RUN: %clangxx_asan -m64 -O0 %s %p/../Helpers/initialization-bug-extra.cc\ +// RUN: -fsanitize=init-order -o %t && %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: | %symbolize | FileCheck %s + +// Do not test with optimization -- the error may be optimized away. + +#include <cstdio> + +// 'y' is a dynamically initialized global residing in a different TU. This +// dynamic initializer will read the value of 'y' before main starts. The +// result is undefined behavior, which should be caught by initialization order +// checking. +extern int y; +int __attribute__((noinline)) initX() { + return y + 1; + // CHECK: {{AddressSanitizer: initialization-order-fiasco}} + // CHECK: {{READ of size .* at 0x.* thread T0}} + // CHECK: {{#0 0x.* in .*initX.* .*initialization-bug-any-order.cc:}}[[@LINE-3]] + // CHECK: {{0x.* is located 0 bytes inside of global variable .*y.*}} +} + +// This initializer begins our initialization order problems. +static int x = initX(); + +int main() { + // ASan should have caused an exit before main runs. + printf("PASS\n"); + // CHECK-NOT: PASS + return 0; +} diff --git a/lib/asan/lit_tests/Linux/interception_failure_test.cc b/lib/asan/lit_tests/Linux/interception_failure_test.cc new file mode 100644 index 000000000000..dfad909f528c --- /dev/null +++ b/lib/asan/lit_tests/Linux/interception_failure_test.cc @@ -0,0 +1,26 @@ +// If user provides his own libc functions, ASan doesn't +// intercept these functions. + +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// 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 +#include <stdlib.h> +#include <stdio.h> + +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return 0; +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: my_strtol_interceptor + // CHECK-NOT: heap-use-after-free +} diff --git a/lib/asan/lit_tests/Linux/interception_malloc_test.cc b/lib/asan/lit_tests/Linux/interception_malloc_test.cc new file mode 100644 index 000000000000..8f66788e9a82 --- /dev/null +++ b/lib/asan/lit_tests/Linux/interception_malloc_test.cc @@ -0,0 +1,27 @@ +// ASan interceptor can be accessed with __interceptor_ prefix. + +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// 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 +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" void *__interceptor_malloc(size_t size); +extern "C" void *malloc(size_t size) { + write(2, "malloc call\n", sizeof("malloc call\n") - 1); + return __interceptor_malloc(size); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: malloc call + // CHECK: heap-use-after-free +} diff --git a/lib/asan/lit_tests/Linux/interception_test.cc b/lib/asan/lit_tests/Linux/interception_test.cc new file mode 100644 index 000000000000..94fb499f2f87 --- /dev/null +++ b/lib/asan/lit_tests/Linux/interception_test.cc @@ -0,0 +1,26 @@ +// ASan interceptor can be accessed with __interceptor_ prefix. + +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// 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 +#include <stdlib.h> +#include <stdio.h> + +extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base); +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return __interceptor_strtol(nptr, endptr, base); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: my_strtol_interceptor + // CHECK: heap-use-after-free +} diff --git a/lib/asan/lit_tests/Linux/lit.local.cfg b/lib/asan/lit_tests/Linux/lit.local.cfg new file mode 100644 index 000000000000..57271b8078a4 --- /dev/null +++ b/lib/asan/lit_tests/Linux/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 ['Linux']: + config.unsupported = True diff --git a/lib/asan/lit_tests/Linux/malloc-in-qsort.cc b/lib/asan/lit_tests/Linux/malloc-in-qsort.cc new file mode 100644 index 000000000000..a3fa255b186d --- /dev/null +++ b/lib/asan/lit_tests/Linux/malloc-in-qsort.cc @@ -0,0 +1,50 @@ +// RUN: %clangxx_asan -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 +#include <stdlib.h> +#include <stdio.h> + +int *GlobalPtr; + +extern "C" { +int QsortCallback(const void *a, const void *b) { + char *x = (char*)a; + char *y = (char*)b; + printf("Calling QsortCallback\n"); + GlobalPtr = new int[10]; + return (int)*x - (int)*y; +} + +__attribute__((noinline)) +void MyQsort(char *a, size_t size) { + printf("Calling qsort\n"); + qsort(a, size, sizeof(char), QsortCallback); + printf("Done\n"); // Avoid tail call. +} +} // extern "C" + +int main() { + char a[2] = {1, 2}; + MyQsort(a, 2); + return GlobalPtr[10]; +} + +// Fast unwind: can not unwind through qsort. +// FIXME: this test does not properly work with slow unwind yet. + +// CHECK-FAST: ERROR: AddressSanitizer: heap-buffer-overflow +// CHECK-FAST: is located 0 bytes to the right +// CHECK-FAST: #0{{.*}}operator new +// CHECK-FAST-NEXT: #1{{.*}}QsortCallback +// CHECK-FAST-NOT: MyQsort +// +// CHECK-SLOW: ERROR: AddressSanitizer: heap-buffer-overflow +// CHECK-SLOW: is located 0 bytes to the right +// CHECK-SLOW: #0{{.*}}operator new +// CHECK-SLOW-NEXT: #1{{.*}}QsortCallback +// CHECK-SLOW: #{{.*}}MyQsort +// CHECK-SLOW-NEXT: #{{.*}}main diff --git a/lib/asan/lit_tests/Linux/overflow-in-qsort.cc b/lib/asan/lit_tests/Linux/overflow-in-qsort.cc new file mode 100644 index 000000000000..c298991a8348 --- /dev/null +++ b/lib/asan/lit_tests/Linux/overflow-in-qsort.cc @@ -0,0 +1,47 @@ +// RUN: %clangxx_asan -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 +#include <stdlib.h> +#include <stdio.h> + +int global_array[10]; +volatile int one = 1; + +extern "C" { +int QsortCallback(const void *a, const void *b) { + char *x = (char*)a; + char *y = (char*)b; + printf("Calling QsortCallback\n"); + global_array[one * 10] = 0; // BOOM + return (int)*x - (int)*y; +} + +__attribute__((noinline)) +void MyQsort(char *a, size_t size) { + printf("Calling qsort\n"); + qsort(a, size, sizeof(char), QsortCallback); + printf("Done\n"); // Avoid tail call. +} +} // extern "C" + +int main() { + char a[2] = {1, 2}; + MyQsort(a, 2); +} + +// Fast unwind: can not unwind through qsort. + +// CHECK-FAST: ERROR: AddressSanitizer: global-buffer-overflow +// CHECK-FAST: #0{{.*}} in QsortCallback +// CHECK-FAST-NOT: MyQsort +// CHECK-FAST: is located 0 bytes to the right of global variable 'global_array + +// CHECK-SLOW: ERROR: AddressSanitizer: global-buffer-overflow +// CHECK-SLOW: #0{{.*}} in QsortCallback +// CHECK-SLOW: #{{.*}} in MyQsort +// CHECK-SLOW: #{{.*}} in main +// CHECK-SLOW: is located 0 bytes to the right of global variable 'global_array diff --git a/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc b/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc new file mode 100644 index 000000000000..5026e24e424d --- /dev/null +++ b/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc @@ -0,0 +1,16 @@ +// Check that we properly report mmap failure. +// RUN: %clangxx_asan %s -o %t && %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <assert.h> +#include <sys/time.h> +#include <sys/resource.h> + +static volatile void *x; + +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 + return 0; +} diff --git a/lib/asan/lit_tests/Linux/swapcontext_test.cc b/lib/asan/lit_tests/Linux/swapcontext_test.cc new file mode 100644 index 000000000000..0404b4f602bd --- /dev/null +++ b/lib/asan/lit_tests/Linux/swapcontext_test.cc @@ -0,0 +1,66 @@ +// Check that ASan plays well with easy cases of makecontext/swapcontext. + +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// 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 + +#include <stdio.h> +#include <ucontext.h> +#include <unistd.h> + +ucontext_t orig_context; +ucontext_t child_context; + +void Child(int mode) { + char x[32] = {0}; // Stack gets poisoned. + printf("Child: %p\n", x); + // (a) Do nothing, just return to parent function. + // (b) Jump into the original function. Stack remains poisoned unless we do + // something. + if (mode == 1) { + if (swapcontext(&child_context, &orig_context) < 0) { + perror("swapcontext"); + _exit(0); + } + } +} + +int Run(int arg, int mode) { + const int kStackSize = 1 << 20; + char child_stack[kStackSize + 1]; + printf("Child stack: %p\n", child_stack); + // Setup child context. + getcontext(&child_context); + child_context.uc_stack.ss_sp = child_stack; + child_context.uc_stack.ss_size = kStackSize / 2; + if (mode == 0) { + child_context.uc_link = &orig_context; + } + makecontext(&child_context, (void (*)())Child, 1, mode); + if (swapcontext(&orig_context, &child_context) < 0) { + perror("swapcontext"); + return 0; + } + // Touch childs's stack to make sure it's unpoisoned. + for (int i = 0; i < kStackSize; i++) { + child_stack[i] = i; + } + return child_stack[arg]; +} + +int main(int argc, char **argv) { + // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext + int ret = 0; + ret += Run(argc - 1, 0); + printf("Test1 passed\n"); + // CHECK: Test1 passed + ret += Run(argc - 1, 1); + printf("Test2 passed\n"); + // CHECK: Test2 passed + return ret; +} diff --git a/lib/asan/output_tests/dlclose-test-so.cc b/lib/asan/lit_tests/SharedLibs/dlclose-test-so.cc index 73e00507358a..73e00507358a 100644 --- a/lib/asan/output_tests/dlclose-test-so.cc +++ b/lib/asan/lit_tests/SharedLibs/dlclose-test-so.cc diff --git a/lib/asan/lit_tests/SharedLibs/lit.local.cfg b/lib/asan/lit_tests/SharedLibs/lit.local.cfg new file mode 100644 index 000000000000..b3677c17a0f2 --- /dev/null +++ b/lib/asan/lit_tests/SharedLibs/lit.local.cfg @@ -0,0 +1,4 @@ +# Sources in this directory are compiled as shared libraries and used by +# tests in parent directory. + +config.suffixes = [] diff --git a/lib/asan/output_tests/shared-lib-test-so.cc b/lib/asan/lit_tests/SharedLibs/shared-lib-test-so.cc index 686a24578082..686a24578082 100644 --- a/lib/asan/output_tests/shared-lib-test-so.cc +++ b/lib/asan/lit_tests/SharedLibs/shared-lib-test-so.cc diff --git a/lib/asan/lit_tests/Unit/lit.cfg b/lib/asan/lit_tests/Unit/lit.cfg new file mode 100644 index 000000000000..243eb7fbeec0 --- /dev/null +++ b/lib/asan/lit_tests/Unit/lit.cfg @@ -0,0 +1,27 @@ +# -*- Python -*- + +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 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", + "lit.common.unit.cfg") +lit.load_config(config, compiler_rt_lit_unit_cfg) + +# Setup config name. +config.name = 'AddressSanitizer-Unit' + +# Setup test source and exec root. For unit tests, we define +# it as build directory with ASan unit tests. +asan_binary_dir = get_required_attr(config, "asan_binary_dir") +config.test_exec_root = os.path.join(asan_binary_dir, "tests") +config.test_source_root = config.test_exec_root diff --git a/lib/asan/lit_tests/Unit/lit.site.cfg.in b/lib/asan/lit_tests/Unit/lit.site.cfg.in new file mode 100644 index 000000000000..401c3a8cc2eb --- /dev/null +++ b/lib/asan/lit_tests/Unit/lit.site.cfg.in @@ -0,0 +1,10 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +config.target_triple = "@TARGET_TRIPLE@" +config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.build_type = "@CMAKE_BUILD_TYPE@" +config.asan_binary_dir = "@ASAN_BINARY_DIR@" + +# 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/blacklist.cc b/lib/asan/lit_tests/blacklist.cc new file mode 100644 index 000000000000..6cfc1500c503 --- /dev/null +++ b/lib/asan/lit_tests/blacklist.cc @@ -0,0 +1,44 @@ +// Test the blacklist functionality of ASan + +// RUN: echo "fun:*brokenFunction*" > %tmp +// RUN: echo "global:*badGlobal*" >> %tmp +// RUN: echo "src:*blacklist-extra.cc" >> %tmp +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O0 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O1 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O2 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O3 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O0 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O1 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O2 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O3 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 + +// badGlobal is accessed improperly, but we blacklisted it. +int badGlobal; +int readBadGlobal() { + return (&badGlobal)[1]; +} + +// A function which is broken, but excluded in the blacklist. +int brokenFunction(int argc) { + char x[10] = {0}; + return x[argc * 10]; // BOOM +} + +// This function is defined in Helpers/blacklist-extra.cc, a source file which +// is blacklisted by name +int externalBrokenFunction(int x); + +int main(int argc, char **argv) { + brokenFunction(argc); + int x = readBadGlobal(); + externalBrokenFunction(argc); + return 0; +} diff --git a/lib/asan/lit_tests/deep_stack_uaf.cc b/lib/asan/lit_tests/deep_stack_uaf.cc new file mode 100644 index 000000000000..7b32798fefcc --- /dev/null +++ b/lib/asan/lit_tests/deep_stack_uaf.cc @@ -0,0 +1,36 @@ +// Check that we can store lots of stack frames if asked to. + +// RUN: %clangxx_asan -m64 -O0 %s -o %t 2>&1 +// RUN: ASAN_OPTIONS=malloc_context_size=120:redzone=512 %t 2>&1 | \ +// RUN: %symbolize | FileCheck %s + +// RUN: %clangxx_asan -m32 -O0 %s -o %t 2>&1 +// RUN: ASAN_OPTIONS=malloc_context_size=120:redzone=512 %t 2>&1 | \ +// RUN: %symbolize | FileCheck %s +#include <stdlib.h> +#include <stdio.h> + +template <int depth> +struct DeepFree { + static void free(char *x) { + DeepFree<depth - 1>::free(x); + } +}; + +template<> +struct DeepFree<0> { + static void free(char *x) { + ::free(x); + } +}; + +int main() { + char *x = (char*)malloc(10); + // deep_free(x); + DeepFree<200>::free(x); + return x[5]; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: DeepFree<36> + // CHECK: DeepFree<98> + // CHECK: DeepFree<115> +} diff --git a/lib/asan/lit_tests/deep_tail_call.cc b/lib/asan/lit_tests/deep_tail_call.cc new file mode 100644 index 000000000000..6aa15e81f6ec --- /dev/null +++ b/lib/asan/lit_tests/deep_tail_call.cc @@ -0,0 +1,24 @@ +// 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 + +// CHECK: AddressSanitizer: global-buffer-overflow +int global[10]; +// CHECK: {{#0.*call4}} +void __attribute__((noinline)) call4(int i) { global[i+10]++; } +// CHECK: {{#1.*call3}} +void __attribute__((noinline)) call3(int i) { call4(i); } +// CHECK: {{#2.*call2}} +void __attribute__((noinline)) call2(int i) { call3(i); } +// CHECK: {{#3.*call1}} +void __attribute__((noinline)) call1(int i) { call2(i); } +// CHECK: {{#4.*main}} +int main(int argc, char **argv) { + call1(argc); + return global[0]; +} diff --git a/lib/asan/lit_tests/deep_thread_stack.cc b/lib/asan/lit_tests/deep_thread_stack.cc new file mode 100644 index 000000000000..781508d61616 --- /dev/null +++ b/lib/asan/lit_tests/deep_thread_stack.cc @@ -0,0 +1,61 @@ +// 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 <pthread.h> + +int *x; + +void *AllocThread(void *arg) { + x = new int; + *x = 42; + return NULL; +} + +void *FreeThread(void *arg) { + delete x; + return NULL; +} + +void *AccessThread(void *arg) { + *x = 43; // BOOM + return NULL; +} + +typedef void* (*callback_type)(void* arg); + +void *RunnerThread(void *function) { + pthread_t thread; + pthread_create(&thread, NULL, (callback_type)function, NULL); + pthread_join(thread, NULL); + return NULL; +} + +void RunThread(callback_type function) { + pthread_t runner; + pthread_create(&runner, NULL, RunnerThread, (void*)function); + pthread_join(runner, NULL); +} + +int main(int argc, char *argv[]) { + RunThread(AllocThread); + RunThread(FreeThread); + RunThread(AccessThread); + return (x != 0); +} + +// CHECK: AddressSanitizer: heap-use-after-free +// CHECK: WRITE of size 4 at 0x{{.*}} thread T[[ACCESS_THREAD:[0-9]+]] +// CHECK: freed by thread T[[FREE_THREAD:[0-9]+]] here: +// CHECK: previously allocated by thread T[[ALLOC_THREAD:[0-9]+]] here: +// CHECK: Thread T[[ACCESS_THREAD]] created by T[[ACCESS_RUNNER:[0-9]+]] here: +// CHECK: Thread T[[ACCESS_RUNNER]] created by T0 here: +// CHECK: Thread T[[FREE_THREAD]] created by T[[FREE_RUNNER:[0-9]+]] here: +// CHECK: Thread T[[FREE_RUNNER]] created by T0 here: +// CHECK: Thread T[[ALLOC_THREAD]] created by T[[ALLOC_RUNNER:[0-9]+]] here: +// CHECK: Thread T[[ALLOC_RUNNER]] created by T0 here: diff --git a/lib/asan/output_tests/default_options.cc b/lib/asan/lit_tests/default_options.cc index d6c70291e6ff..950a7d879194 100644 --- a/lib/asan/output_tests/default_options.cc +++ b/lib/asan/lit_tests/default_options.cc @@ -1,12 +1,15 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s + const char *kAsanDefaultOptions="verbosity=1 foo=bar"; extern "C" __attribute__((no_address_safety_analysis)) const char *__asan_default_options() { + // CHECK: Using the defaults from __asan_default_options: {{.*}} foo=bar return kAsanDefaultOptions; } int main() { - // Check-Common: foo=bar return 0; } diff --git a/lib/asan/output_tests/dlclose-test.cc b/lib/asan/lit_tests/dlclose-test.cc index 16126eb9042f..229f508294bf 100644 --- a/lib/asan/output_tests/dlclose-test.cc +++ b/lib/asan/lit_tests/dlclose-test.cc @@ -1,14 +1,3 @@ -//===----------- dlclose-test.cc --------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// // Regression test for // http://code.google.com/p/address-sanitizer/issues/detail?id=19 // Bug description: @@ -19,7 +8,32 @@ // 5. application starts using this mmaped memory, but asan still thinks there // are globals. // 6. BOOM -//===----------------------------------------------------------------------===// + +// 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 +// RUN: %clangxx_asan -m64 -O1 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O2 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O3 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O0 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O1 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O2 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O3 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s + #include <assert.h> #include <dlfcn.h> #include <stdio.h> @@ -69,6 +83,6 @@ int main(int argc, char *argv[]) { } addr[1] = 2; // BOOM (if the bug is not fixed). printf("PASS\n"); - // Check-Common: PASS + // CHECK: PASS return 0; } diff --git a/lib/asan/lit_tests/force_inline_opt0.cc b/lib/asan/lit_tests/force_inline_opt0.cc new file mode 100644 index 000000000000..955ce38156fb --- /dev/null +++ b/lib/asan/lit_tests/force_inline_opt0.cc @@ -0,0 +1,14 @@ +// This test checks that we are no instrumenting a memory access twice +// (before and after inlining) +// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t +__attribute__((always_inline)) +void foo(int *x) { + *x = 0; +} + +int main() { + int x; + foo(&x); + return x; +} diff --git a/lib/asan/lit_tests/global-overflow.cc b/lib/asan/lit_tests/global-overflow.cc new file mode 100644 index 000000000000..6a2f12e106fe --- /dev/null +++ b/lib/asan/lit_tests/global-overflow.cc @@ -0,0 +1,25 @@ +// 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 <string.h> +int main(int argc, char **argv) { + static char XXX[10]; + static char YYY[10]; + static char ZZZ[10]; + memset(XXX, 0, 10); + memset(YYY, 0, 10); + memset(ZZZ, 0, 10); + int res = YYY[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in _?main .*global-overflow.cc:}}[[@LINE-2]] + // CHECK: {{0x.* is located 0 bytes to the right of global variable}} + // CHECK: {{.*YYY.* of size 10}} + res += XXX[argc] + ZZZ[argc]; + return res; +} diff --git a/lib/asan/lit_tests/heap-overflow.cc b/lib/asan/lit_tests/heap-overflow.cc new file mode 100644 index 000000000000..2648ec7e5f1f --- /dev/null +++ b/lib/asan/lit_tests/heap-overflow.cc @@ -0,0 +1,38 @@ +// 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 + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char *x = (char*)malloc(10 * sizeof(char)); + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in _?main .*heap-overflow.cc:}}[[@LINE-2]] + // CHECK: {{0x.* is located 0 bytes to the right of 10-byte region}} + // CHECK: {{allocated by thread T0 here:}} + + // 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}} + free(x); + return res; +} diff --git a/lib/asan/lit_tests/initialization-blacklist.cc b/lib/asan/lit_tests/initialization-blacklist.cc new file mode 100644 index 000000000000..f8df24c68ea6 --- /dev/null +++ b/lib/asan/lit_tests/initialization-blacklist.cc @@ -0,0 +1,32 @@ +// Test for blacklist functionality of initialization-order checker. + +// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ +// RUN: -fsanitize=init-order -o %t && %t 2>&1 +// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ +// RUN: -fsanitize=init-order -o %t && %t 2>&1 +// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ +// RUN: -fsanitize=init-order -o %t && %t 2>&1 +// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ +// RUN: -fsanitize=init-order -o %t && %t 2>&1 +// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ +// RUN: -fsanitize=init-order -o %t && %t 2>&1 +// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ +// RUN: -fsanitize=init-order -o %t && %t 2>&1 + +// Function is defined in another TU. +int readBadGlobal(); +int x = readBadGlobal(); // init-order bug. + +// Function is defined in another TU. +int accessBadObject(); +int y = accessBadObject(); // init-order bug. + +int main(int argc, char **argv) { + return argc + x + y - 1; +} diff --git a/lib/asan/lit_tests/initialization-bug.cc b/lib/asan/lit_tests/initialization-bug.cc new file mode 100644 index 000000000000..8f4e33ef5a35 --- /dev/null +++ b/lib/asan/lit_tests/initialization-bug.cc @@ -0,0 +1,46 @@ +// 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: | %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: | %symbolize | FileCheck %s + +// Do not test with optimization -- the error may be optimized away. + +#include <cstdio> + +// The structure of the test is: +// "x", "y", "z" are dynamically initialized globals. +// Value of "x" depends on "y", value of "y" depends on "z". +// "x" and "z" are defined in this TU, "y" is defined in another one. +// Thus we shoud stably report initialization order fiasco independently of +// the translation unit order. + +int initZ() { + return 5; +} +int z = initZ(); + +// 'y' is a dynamically initialized global residing in a different TU. This +// dynamic initializer will read the value of 'y' before main starts. The +// result is undefined behavior, which should be caught by initialization order +// checking. +extern int y; +int __attribute__((noinline)) initX() { + return y + 1; + // CHECK: {{AddressSanitizer: initialization-order-fiasco}} + // CHECK: {{READ of size .* at 0x.* thread T0}} + // CHECK: {{0x.* is located 0 bytes inside of global variable .*(y|z).*}} +} + +// This initializer begins our initialization order problems. +static int x = initX(); + +int main() { + // ASan should have caused an exit before main runs. + printf("PASS\n"); + // CHECK-NOT: PASS + return 0; +} diff --git a/lib/asan/lit_tests/initialization-nobug.cc b/lib/asan/lit_tests/initialization-nobug.cc new file mode 100644 index 000000000000..1b8961606811 --- /dev/null +++ b/lib/asan/lit_tests/initialization-nobug.cc @@ -0,0 +1,67 @@ +// 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 + +// Simple access: +// Make sure that accessing a global in the same TU is safe + +bool condition = true; +int initializeSameTU() { + return condition ? 0x2a : 052; +} +int sameTU = initializeSameTU(); + +// Linker initialized: +// Check that access to linker initialized globals originating from a different +// TU's initializer is safe. + +int A = (1 << 1) + (1 << 3) + (1 << 5), B; +int getAB() { + return A * B; +} + +// Function local statics: +// Check that access to function local statics originating from a different +// TU's initializer is safe. + +int countCalls() { + static int calls; + 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: + 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/interface_symbols.c b/lib/asan/lit_tests/interface_symbols.c new file mode 100644 index 000000000000..f3167f562922 --- /dev/null +++ b/lib/asan/lit_tests/interface_symbols.c @@ -0,0 +1,28 @@ +// 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: | 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: | 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: cat %t.interface | sort -u | diff %t.symbols - + +int main() { return 0; } diff --git a/lib/asan/lit_tests/large_func_test.cc b/lib/asan/lit_tests/large_func_test.cc new file mode 100644 index 000000000000..a74828811f74 --- /dev/null +++ b/lib/asan/lit_tests/large_func_test.cc @@ -0,0 +1,62 @@ +// 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 + +#include <stdlib.h> +__attribute__((noinline)) +static void LargeFunction(int *x, int zero) { + x[0]++; + x[1]++; + x[2]++; + x[3]++; + x[4]++; + x[5]++; + x[6]++; + x[7]++; + x[8]++; + x[9]++; + + // 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 + // atos incorrectly extracts the symbol name for the static functions on + // Darwin. + // CHECK-Linux: {{#0 0x.* in LargeFunction.*large_func_test.cc:}}[[@LINE-3]] + // CHECK-Darwin: {{#0 0x.* in .*LargeFunction.*large_func_test.cc}}:[[@LINE-4]] + + x[10]++; + x[11]++; + x[12]++; + x[13]++; + x[14]++; + x[15]++; + x[16]++; + x[17]++; + x[18]++; + x[19]++; +} + +int main(int argc, char **argv) { + int *x = new int[100]; + LargeFunction(x, argc - 1); + // 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: {{allocated by thread T0 here:}} + // CHECK: {{ #0 0x.* in operator new.*}} + // CHECK: {{ #1 0x.* in _?main .*large_func_test.cc:}}[[@LINE-6]] + delete x; +} diff --git a/lib/asan/lit_tests/lit.cfg b/lib/asan/lit_tests/lit.cfg new file mode 100644 index 000000000000..7875281b1f2f --- /dev/null +++ b/lib/asan/lit_tests/lit.cfg @@ -0,0 +1,97 @@ +# -*- Python -*- + +import os + +# Setup config name. +config.name = 'AddressSanitizer' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +def DisplayNoConfigMessage(): + lit.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-asan") + +# Figure out LLVM source root. +llvm_src_root = getattr(config, 'llvm_src_root', None) +if llvm_src_root is None: + # We probably haven't loaded the site-specific configuration: the user + # is likely trying to run a test file directly, and the site configuration + # wasn't created by the build system. + asan_site_cfg = lit.params.get('asan_site_config', None) + if (asan_site_cfg) and (os.path.exists(asan_site_cfg)): + lit.load_config(config, asan_site_cfg) + raise SystemExit + + # Try to guess the location of site-specific configuration using llvm-config + # util that can point where the build tree is. + llvm_config = lit.util.which("llvm-config", config.environment["PATH"]) + 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", + "lib", "asan", "lit_tests", "lit.site.cfg") + if (not asan_site_cfg) or (not os.path.exists(asan_site_cfg)): + DisplayNoConfigMessage() + + lit.load_config(config, asan_site_cfg) + 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") +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) +lit.load_config(config, compiler_rt_lit_cfg) + +# Setup default compiler flags used with -fsanitize=address option. +# FIXME: Review the set of required flags and check if it can be reduced. +clang_asan_cxxflags = ("-ccc-cxx " + + "-fsanitize=address " + + "-mno-omit-leaf-frame-pointer " + + "-fno-omit-frame-pointer " + + "-fno-optimize-sibling-calls " + + "-g") +config.substitutions.append( ("%clangxx_asan ", (" " + config.clang + " " + + clang_asan_cxxflags + " ")) ) + +# Setup path to external LLVM symbolizer to run AddressSanitizer output tests. +llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) +if llvm_tools_dir: + config.environment['LLVM_SYMBOLIZER_PATH'] = os.path.join( + llvm_tools_dir, "llvm-symbolizer") + +# Setup path to symbolizer script. +# FIXME: Instead we should copy this script to the build tree and point +# at it there. +asan_source_dir = os.path.join(config.test_source_root, "..") +symbolizer = os.path.join(asan_source_dir, + 'scripts', 'asan_symbolize.py') +if not os.path.exists(symbolizer): + lit.fatal("Can't find symbolizer script on path %r" % symbolizer) +# Define %symbolize substitution that filters output through +# symbolizer and c++filt (for demangling). +config.substitutions.append( ("%symbolize ", (" " + symbolizer + + " | c++filt " ))) + +# Define CHECK-%os to check for OS-dependent output. +config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os))) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# AddressSanitizer tests are currently supported on Linux and Darwin only. +if config.host_os not in ['Linux', 'Darwin']: + config.unsupported = True diff --git a/lib/asan/lit_tests/lit.site.cfg.in b/lib/asan/lit_tests/lit.site.cfg.in new file mode 100644 index 000000000000..cf439309c6ad --- /dev/null +++ b/lib/asan/lit_tests/lit.site.cfg.in @@ -0,0 +1,20 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +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.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.clang = "@LLVM_BINARY_DIR@/bin/clang" + +# LLVM tools dir can be passed in lit parameters, so try to +# apply substitution. +try: + config.llvm_tools_dir = config.llvm_tools_dir % 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/lit.cfg") diff --git a/lib/asan/lit_tests/log-path_test.cc b/lib/asan/lit_tests/log-path_test.cc new file mode 100644 index 000000000000..1072670fbff4 --- /dev/null +++ b/lib/asan/lit_tests/log-path_test.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_asan %s -o %t + +// Regular run. +// RUN: not %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.out + +// Good log_path. +// RUN: rm -f %t.log.* +// RUN: ASAN_OPTIONS=log_path=%t.log not %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.log.* + +// Invalid log_path. +// RUN: ASAN_OPTIONS=log_path=/INVALID not %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-INVALID < %t.out + +// Too long log_path. +// RUN: ASAN_OPTIONS=log_path=`for((i=0;i<10000;i++)); do echo -n $i; done` \ +// RUN: not %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-LONG < %t.out + +// Run w/o errors should not produce any log. +// RUN: rm -f %t.log.* +// RUN: ASAN_OPTIONS=log_path=%t.log %t ARG ARG ARG +// RUN: not cat %t.log.* + + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + if (argc > 2) return 0; + char *x = (char*)malloc(10); + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + free(x); + return res; +} +// CHECK-ERROR: ERROR: AddressSanitizer +// CHECK-INVALID: ERROR: Can't open file: /INVALID +// CHECK-LONG: ERROR: Path is too long: 01234 diff --git a/lib/asan/lit_tests/log_path_fork_test.cc b/lib/asan/lit_tests/log_path_fork_test.cc new file mode 100644 index 000000000000..c6c1b49e994d --- /dev/null +++ b/lib/asan/lit_tests/log_path_fork_test.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_asan %s -o %t +// RUN: rm -f %t.log.* +// Set verbosity to 1 so that the log files are opened prior to fork(). +// RUN: ASAN_OPTIONS="log_path=%t.log verbosity=1" not %t 2> %t.out +// RUN: for f in %t.log.* ; do FileCheck %s < $f; done +// RUN: [ `ls %t.log.* | wc -l` == 2 ] + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int main(int argc, char **argv) { + void *x = malloc(10); + free(x); + if (fork() == -1) return 1; + // There are two processes at this point, thus there should be two distinct + // error logs. + free(x); + return 0; +} + +// CHECK: ERROR: AddressSanitizer diff --git a/lib/asan/lit_tests/malloc_delete_mismatch.cc b/lib/asan/lit_tests/malloc_delete_mismatch.cc new file mode 100644 index 000000000000..f34b33a38fb3 --- /dev/null +++ b/lib/asan/lit_tests/malloc_delete_mismatch.cc @@ -0,0 +1,26 @@ +// Check that we detect malloc/delete mismatch only if the approptiate flag +// is set. + +// RUN: %clangxx_asan -g %s -o %t 2>&1 +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1 %t 2>&1 | \ +// RUN: %symbolize | FileCheck %s + +// No error here. +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=0 %t +#include <stdlib.h> + +static volatile char *x; + +int main() { + x = (char*)malloc(10); + x[0] = 0; + delete x; +} +// CHECK: ERROR: AddressSanitizer: alloc-dealloc-mismatch (malloc vs operator delete) on 0x +// CHECK-NEXT: #0{{.*}}operator delete +// CHECK: #{{.*}}main +// CHECK: is located 0 bytes inside of 10-byte region +// CHECK-NEXT: allocated by thread T0 here: +// CHECK-NEXT: #0{{.*}}malloc +// CHECK: #{{.*}}main +// CHECK: HINT: {{.*}} you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0 diff --git a/lib/asan/lit_tests/malloc_hook.cc b/lib/asan/lit_tests/malloc_hook.cc new file mode 100644 index 000000000000..6435d105ee26 --- /dev/null +++ b/lib/asan/lit_tests/malloc_hook.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <unistd.h> + +extern "C" { +// Note: avoid calling functions that allocate memory in malloc/free +// to avoid infinite recursion. +void __asan_malloc_hook(void *ptr, size_t sz) { + write(1, "MallocHook\n", sizeof("MallocHook\n")); +} +void __asan_free_hook(void *ptr) { + write(1, "FreeHook\n", sizeof("FreeHook\n")); +} +} // extern "C" + +int main() { + volatile int *x = new int; + // CHECK: MallocHook + *x = 0; + delete x; + // CHECK: FreeHook + return 0; +} diff --git a/lib/asan/lit_tests/memcmp_test.cc b/lib/asan/lit_tests/memcmp_test.cc new file mode 100644 index 000000000000..ac3f7f32ea75 --- /dev/null +++ b/lib/asan/lit_tests/memcmp_test.cc @@ -0,0 +1,19 @@ +// 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 <string.h> +int main(int argc, char **argv) { + char a1[] = {argc, 2, 3, 4}; + char a2[] = {1, 2*argc, 3, 4}; + int res = memcmp(a1, a2, 4 + argc); // BOOM + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: {{#0.*memcmp}} + // CHECK: {{#1.*main}} + return res; +} diff --git a/lib/asan/lit_tests/null_deref.cc b/lib/asan/lit_tests/null_deref.cc new file mode 100644 index 000000000000..60a521d1c210 --- /dev/null +++ b/lib/asan/lit_tests/null_deref.cc @@ -0,0 +1,31 @@ +// 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 + +__attribute__((noinline)) +static void NullDeref(int *ptr) { + // CHECK: ERROR: AddressSanitizer: SEGV on unknown address + // CHECK: {{0x0*00028 .*pc 0x.*}} + // CHECK: {{AddressSanitizer can not provide additional info.}} + ptr[10]++; // BOOM + // atos on Mac cannot extract the symbol name correctly. + // CHECK-Linux: {{ #0 0x.* in NullDeref.*null_deref.cc:}}[[@LINE-2]] + // CHECK-Darwin: {{ #0 0x.* in .*NullDeref.*null_deref.cc:}}[[@LINE-3]] +} +int main() { + NullDeref((int*)0); + // CHECK: {{ #1 0x.* in _?main.*null_deref.cc:}}[[@LINE-1]] +} diff --git a/lib/asan/lit_tests/on_error_callback.cc b/lib/asan/lit_tests/on_error_callback.cc new file mode 100644 index 000000000000..bb94d9fb579b --- /dev/null +++ b/lib/asan/lit_tests/on_error_callback.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +extern "C" +void __asan_on_error() { + fprintf(stderr, "__asan_on_error called"); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: __asan_on_error called +} diff --git a/lib/asan/lit_tests/sanity_check_pure_c.c b/lib/asan/lit_tests/sanity_check_pure_c.c new file mode 100644 index 000000000000..3d830653e33e --- /dev/null +++ b/lib/asan/lit_tests/sanity_check_pure_c.c @@ -0,0 +1,19 @@ +// Sanity checking a test in pure C. +// RUN: %clang -g -fsanitize=address -O2 %s -o %t +// RUN: %t 2>&1 | %symbolize | FileCheck %s + +// Sanity checking a test in pure C with -pie. +// RUN: %clang -g -fsanitize=address -O2 %s -pie -o %t +// RUN: %t 2>&1 | %symbolize | FileCheck %s + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: heap-use-after-free + // CHECK: free + // CHECK: main{{.*}}sanity_check_pure_c.c:[[@LINE-4]] + // CHECK: malloc + // CHECK: main{{.*}}sanity_check_pure_c.c:[[@LINE-7]] +} diff --git a/lib/asan/lit_tests/shared-lib-test.cc b/lib/asan/lit_tests/shared-lib-test.cc new file mode 100644 index 000000000000..05bf3ecdf4f9 --- /dev/null +++ b/lib/asan/lit_tests/shared-lib-test.cc @@ -0,0 +1,54 @@ +// RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m64 -O1 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m64 -O2 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m64 -O3 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m32 -O0 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m32 -O1 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m32 -O2 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m32 -O3 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s + +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> + +#include <string> + +using std::string; + +typedef void (fun_t)(int x); + +int main(int argc, char *argv[]) { + string path = string(argv[0]) + "-so.so"; + printf("opening %s ... \n", path.c_str()); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + fun_t *inc = (fun_t*)dlsym(lib, "inc"); + if (!inc) return 1; + printf("ok\n"); + inc(1); + inc(-1); // BOOM + // CHECK: {{.*ERROR: AddressSanitizer: global-buffer-overflow}} + // CHECK: {{READ of size 4 at 0x.* thread T0}} + // CHECK: {{ #0 0x.*}} + // CHECK: {{ #1 0x.* in _?main .*shared-lib-test.cc:}}[[@LINE-4]] + return 0; +} diff --git a/lib/asan/lit_tests/sleep_before_dying.c b/lib/asan/lit_tests/sleep_before_dying.c new file mode 100644 index 000000000000..df9eba276039 --- /dev/null +++ b/lib/asan/lit_tests/sleep_before_dying.c @@ -0,0 +1,10 @@ +// RUN: %clang -g -fsanitize=address -O2 %s -o %t +// RUN: ASAN_OPTIONS="sleep_before_dying=1" %t 2>&1 | FileCheck %s + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: Sleeping for 1 second +} diff --git a/lib/asan/lit_tests/stack-frame-demangle.cc b/lib/asan/lit_tests/stack-frame-demangle.cc new file mode 100644 index 000000000000..7f4d59fc5838 --- /dev/null +++ b/lib/asan/lit_tests/stack-frame-demangle.cc @@ -0,0 +1,24 @@ +// 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 + +#include <string.h> + +namespace XXX { +struct YYY { + static int ZZZ(int x) { + 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(.*)>}} + } +}; +}; + +int main(int argc, char **argv) { + int res = XXX::YYY::ZZZ(argc + 10); + return res; +} diff --git a/lib/asan/lit_tests/stack-overflow.cc b/lib/asan/lit_tests/stack-overflow.cc new file mode 100644 index 000000000000..3deb1e91de6c --- /dev/null +++ b/lib/asan/lit_tests/stack-overflow.cc @@ -0,0 +1,19 @@ +// 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 <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 .*stack-overflow.cc:}}[[@LINE-2]] + // CHECK: {{Address 0x.* is .* frame <main>}} + return res; +} diff --git a/lib/asan/lit_tests/stack-use-after-return.cc b/lib/asan/lit_tests/stack-use-after-return.cc new file mode 100644 index 000000000000..f8d8a1a2ae39 --- /dev/null +++ b/lib/asan/lit_tests/stack-use-after-return.cc @@ -0,0 +1,45 @@ +// XFAIL: * +// 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 -O1 %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 -m64 -O3 %s -o %t && \ +// RUN: %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O0 %s -o %t && \ +// RUN: %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O1 %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 +// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O3 %s -o %t && \ +// RUN: %t 2>&1 | %symbolize | FileCheck %s + +#include <stdio.h> + +__attribute__((noinline)) +char *Ident(char *x) { + fprintf(stderr, "1: %p\n", x); + return x; +} + +__attribute__((noinline)) +char *Func1() { + char local; + return Ident(&local); +} + +__attribute__((noinline)) +void Func2(char *x) { + fprintf(stderr, "2: %p\n", x); + *x = 1; + // CHECK: WRITE of size 1 {{.*}} thread T0 + // CHECK: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-2]] + // CHECK: is located {{.*}} in frame <{{.*}}Func1{{.*}}> of T0's stack +} + +int main(int argc, char **argv) { + Func2(Func1()); + return 0; +} diff --git a/lib/asan/lit_tests/strip_path_prefix.c b/lib/asan/lit_tests/strip_path_prefix.c new file mode 100644 index 000000000000..ef7bf98ab3c2 --- /dev/null +++ b/lib/asan/lit_tests/strip_path_prefix.c @@ -0,0 +1,12 @@ +// RUN: %clang -g -fsanitize=address -O2 %s -o %t +// RUN: ASAN_OPTIONS="strip_path_prefix='/'" %t 2>&1 | FileCheck %s + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // Check that paths in error report don't start with slash. + // CHECK: heap-use-after-free + // CHECK-NOT: #0 0x{{.*}} ({{[/].*}}) +} diff --git a/lib/asan/lit_tests/strncpy-overflow.cc b/lib/asan/lit_tests/strncpy-overflow.cc new file mode 100644 index 000000000000..18711843c4c8 --- /dev/null +++ b/lib/asan/lit_tests/strncpy-overflow.cc @@ -0,0 +1,40 @@ +// 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 + +#include <string.h> +#include <stdlib.h> +int main(int argc, char **argv) { + char *hello = (char*)malloc(6); + strcpy(hello, "hello"); + char *short_buffer = (char*)malloc(9); + strncpy(short_buffer, hello, 10); // BOOM + // CHECK: {{WRITE of size 1 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]] + // CHECK: {{0x.* is located 0 bytes to the right of 9-byte region}} + // CHECK: {{allocated by thread T0 here:}} + + // 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]] + return short_buffer[8]; +} diff --git a/lib/asan/lit_tests/symbolize_callback.cc b/lib/asan/lit_tests/symbolize_callback.cc new file mode 100644 index 000000000000..0691d501e24d --- /dev/null +++ b/lib/asan/lit_tests/symbolize_callback.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +extern "C" +bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) { + snprintf(out_buffer, out_size, "MySymbolizer"); + return true; +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: MySymbolizer +} diff --git a/lib/asan/lit_tests/use-after-free.cc b/lib/asan/lit_tests/use-after-free.cc new file mode 100644 index 000000000000..24d5a2a54807 --- /dev/null +++ b/lib/asan/lit_tests/use-after-free.cc @@ -0,0 +1,48 @@ +// 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 + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in _?main .*use-after-free.cc:22}} + // CHECK: {{0x.* is located 5 bytes inside of 10-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.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: {{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}} +} diff --git a/lib/asan/lit_tests/use-after-scope-inlined.cc b/lib/asan/lit_tests/use-after-scope-inlined.cc new file mode 100644 index 000000000000..3d730de6ab35 --- /dev/null +++ b/lib/asan/lit_tests/use-after-scope-inlined.cc @@ -0,0 +1,29 @@ +// Test with "-O2" only to make sure inlining (leading to use-after-scope) +// happens. "always_inline" is not enough, as Clang doesn't emit +// llvm.lifetime intrinsics at -O0. +// +// RUN: %clangxx_asan -m64 -O2 -fsanitize=use-after-scope %s -o %t && \ +// RUN: %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -m32 -O2 -fsanitize=use-after-scope %s -o %t && \ +// RUN: %t 2>&1 | %symbolize | FileCheck %s + +int *arr; + +__attribute__((always_inline)) +void inlined(int arg) { + int x[5]; + for (int i = 0; i < arg; i++) x[i] = i; + arr = x; +} + +int main(int argc, char *argv[]) { + inlined(argc); + return arr[argc - 1]; // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // 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: {{\[}}[[OFFSET]], {{.*}}) 'x.i' +} diff --git a/lib/asan/output_tests/clone_test.cc b/lib/asan/output_tests/clone_test.cc deleted file mode 100644 index b18d2550bc7c..000000000000 --- a/lib/asan/output_tests/clone_test.cc +++ /dev/null @@ -1,34 +0,0 @@ -#ifdef __linux__ -#include <stdio.h> -#include <sched.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -int Child(void *arg) { - char x[32] = {0}; // Stack gets poisoned. - printf("Child: %p\n", x); - _exit(1); // NoReturn, stack will remain unpoisoned unless we do something. -} - -int main(int argc, char **argv) { - const int kStackSize = 1 << 20; - char child_stack[kStackSize + 1]; - char *sp = child_stack + kStackSize; // Stack grows down. - printf("Parent: %p\n", sp); - pid_t clone_pid = clone(Child, sp, CLONE_FILES | CLONE_VM, NULL, 0, 0, 0); - waitpid(clone_pid, NULL, 0); - for (int i = 0; i < kStackSize; i++) - child_stack[i] = i; - int ret = child_stack[argc - 1]; - printf("PASSED\n"); - return ret; -} -#else // not __linux__ -#include <stdio.h> -int main() { - printf("PASSED\n"); - // Check-Common: PASSED -} -#endif diff --git a/lib/asan/output_tests/deep_tail_call.cc b/lib/asan/output_tests/deep_tail_call.cc deleted file mode 100644 index cb69e8925197..000000000000 --- a/lib/asan/output_tests/deep_tail_call.cc +++ /dev/null @@ -1,15 +0,0 @@ -// Check-Common: AddressSanitizer global-buffer-overflow -int global[10]; -// Check-Common: {{#0.*call4}} -void __attribute__((noinline)) call4(int i) { global[i+10]++; } -// Check-Common: {{#1.*call3}} -void __attribute__((noinline)) call3(int i) { call4(i); } -// Check-Common: {{#2.*call2}} -void __attribute__((noinline)) call2(int i) { call3(i); } -// Check-Common: {{#3.*call1}} -void __attribute__((noinline)) call1(int i) { call2(i); } -// Check-Common: {{#4.*main}} -int main(int argc, char **argv) { - call1(argc); - return global[0]; -} diff --git a/lib/asan/output_tests/global-overflow.cc b/lib/asan/output_tests/global-overflow.cc deleted file mode 100644 index a63eb733365f..000000000000 --- a/lib/asan/output_tests/global-overflow.cc +++ /dev/null @@ -1,16 +0,0 @@ -#include <string.h> -int main(int argc, char **argv) { - static char XXX[10]; - static char YYY[10]; - static char ZZZ[10]; - memset(XXX, 0, 10); - memset(YYY, 0, 10); - memset(ZZZ, 0, 10); - int res = YYY[argc * 10]; // BOOOM - // Check-Common: {{READ of size 1 at 0x.* thread T0}} - // Check-Common: {{ #0 0x.* in main .*global-overflow.cc:9}} - // Check-Common: {{0x.* is located 0 bytes to the right of global variable}} - // Check-Common: {{.*YYY.* of size 10}} - res += XXX[argc] + ZZZ[argc]; - return res; -} diff --git a/lib/asan/output_tests/heap-overflow.cc b/lib/asan/output_tests/heap-overflow.cc deleted file mode 100644 index 534fbe00b355..000000000000 --- a/lib/asan/output_tests/heap-overflow.cc +++ /dev/null @@ -1,22 +0,0 @@ -#include <stdlib.h> -#include <string.h> -int main(int argc, char **argv) { - char *x = (char*)malloc(10 * sizeof(char)); - memset(x, 0, 10); - int res = x[argc * 10]; // BOOOM - free(x); - return res; -} - -// Check-Common: {{READ of size 1 at 0x.* thread T0}} -// Check-Common: {{ #0 0x.* in main .*heap-overflow.cc:6}} -// Check-Common: {{0x.* is located 0 bytes to the right of 10-byte region}} -// Check-Common: {{allocated by thread T0 here:}} - -// Check-Linux: {{ #0 0x.* in .*malloc}} -// Check-Linux: {{ #1 0x.* in main .*heap-overflow.cc:4}} - -// 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:4}} diff --git a/lib/asan/output_tests/interception_failure_test-linux.cc b/lib/asan/output_tests/interception_failure_test-linux.cc deleted file mode 100644 index 9e8b7536906b..000000000000 --- a/lib/asan/output_tests/interception_failure_test-linux.cc +++ /dev/null @@ -1,17 +0,0 @@ -#include <stdlib.h> -#include <stdio.h> - -extern "C" long strtol(const char *nptr, char **endptr, int base) { - fprintf(stderr, "my_strtol_interceptor\n"); - return 0; -} - -int main() { - char *x = (char*)malloc(10 * sizeof(char)); - free(x); - return (int)strtol(x, 0, 10); -} - -// Check-Common: my_strtol_interceptor -// CHECK-NOT: heap-use-after-free - diff --git a/lib/asan/output_tests/interception_malloc_test-linux.cc b/lib/asan/output_tests/interception_malloc_test-linux.cc deleted file mode 100644 index 4bb3bd66de33..000000000000 --- a/lib/asan/output_tests/interception_malloc_test-linux.cc +++ /dev/null @@ -1,19 +0,0 @@ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> - -extern "C" void *__interceptor_malloc(size_t size); -extern "C" void *malloc(size_t size) { - write(2, "malloc call\n", sizeof("malloc call\n") - 1); - return __interceptor_malloc(size); -} - -int main() { - char *x = (char*)malloc(10 * sizeof(char)); - free(x); - return (int)strtol(x, 0, 10); -} - -// Check-Common: malloc call -// Check-Common: heap-use-after-free - diff --git a/lib/asan/output_tests/interception_test-linux.cc b/lib/asan/output_tests/interception_test-linux.cc deleted file mode 100644 index 0523510465a1..000000000000 --- a/lib/asan/output_tests/interception_test-linux.cc +++ /dev/null @@ -1,18 +0,0 @@ -#include <stdlib.h> -#include <stdio.h> - -extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base); -extern "C" long strtol(const char *nptr, char **endptr, int base) { - fprintf(stderr, "my_strtol_interceptor\n"); - return __interceptor_strtol(nptr, endptr, base); -} - -int main() { - char *x = (char*)malloc(10 * sizeof(char)); - free(x); - return (int)strtol(x, 0, 10); -} - -// Check-Common: my_strtol_interceptor -// Check-Common: heap-use-after-free - diff --git a/lib/asan/output_tests/large_func_test.cc b/lib/asan/output_tests/large_func_test.cc deleted file mode 100644 index 49751b39277a..000000000000 --- a/lib/asan/output_tests/large_func_test.cc +++ /dev/null @@ -1,48 +0,0 @@ -#include <stdlib.h> -__attribute__((noinline)) -static void LargeFunction(int *x, int zero) { - x[0]++; - x[1]++; - x[2]++; - x[3]++; - x[4]++; - x[5]++; - x[6]++; - x[7]++; - x[8]++; - x[9]++; - - x[zero + 111]++; // we should report this exact line - - x[10]++; - x[11]++; - x[12]++; - x[13]++; - x[14]++; - x[15]++; - x[16]++; - x[17]++; - x[18]++; - x[19]++; -} - -int main(int argc, char **argv) { - int *x = new int[100]; - LargeFunction(x, argc - 1); - delete x; -} - -// Check-Common: {{.*ERROR: AddressSanitizer heap-buffer-overflow on address}} -// Check-Common: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} -// Check-Common: {{READ of size 4 at 0x.* thread T0}} - -// atos incorrectly extracts the symbol name for the static functions on -// Darwin. -// Check-Linux: {{ #0 0x.* in LargeFunction.*large_func_test.cc:15}} -// Check-Darwin: {{ #0 0x.* in .*LargeFunction.*large_func_test.cc:15}} - -// Check-Common: {{ #1 0x.* in main .*large_func_test.cc:31}} -// Check-Common: {{0x.* is located 44 bytes to the right of 400-byte region}} -// Check-Common: {{allocated by thread T0 here:}} -// Check-Common: {{ #0 0x.* in operator new.*}} -// Check-Common: {{ #1 0x.* in main .*large_func_test.cc:30}} diff --git a/lib/asan/output_tests/memcmp_test.cc b/lib/asan/output_tests/memcmp_test.cc deleted file mode 100644 index d0e5a43b4355..000000000000 --- a/lib/asan/output_tests/memcmp_test.cc +++ /dev/null @@ -1,10 +0,0 @@ -#include <string.h> -int main(int argc, char **argv) { - char a1[] = {argc, 2, 3, 4}; - char a2[] = {1, 2*argc, 3, 4}; -// Check-Common: AddressSanitizer stack-buffer-overflow -// Check-Common: {{#0.*memcmp}} -// Check-Common: {{#1.*main}} - int res = memcmp(a1, a2, 4 + argc); // BOOM - return res; -} diff --git a/lib/asan/output_tests/null_deref.cc b/lib/asan/output_tests/null_deref.cc deleted file mode 100644 index c152a4202e33..000000000000 --- a/lib/asan/output_tests/null_deref.cc +++ /dev/null @@ -1,17 +0,0 @@ -__attribute__((noinline)) -static void NullDeref(int *ptr) { - ptr[10]++; -} -int main() { - NullDeref((int*)0); -} - -// Check-Common: {{.*ERROR: AddressSanitizer crashed on unknown address}} -// Check-Common: {{0x0*00028 .*pc 0x.*}} -// Check-Common: {{AddressSanitizer can not provide additional info. ABORTING}} - -// atos on Mac cannot extract the symbol name correctly. -// Check-Linux: {{ #0 0x.* in NullDeref.*null_deref.cc:3}} -// Check-Darwin: {{ #0 0x.* in .*NullDeref.*null_deref.cc:3}} - -// Check-Common: {{ #1 0x.* in main.*null_deref.cc:6}} diff --git a/lib/asan/output_tests/shared-lib-test.cc b/lib/asan/output_tests/shared-lib-test.cc deleted file mode 100644 index 060fcde35f0d..000000000000 --- a/lib/asan/output_tests/shared-lib-test.cc +++ /dev/null @@ -1,42 +0,0 @@ -//===----------- shared-lib-test.cc -----------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -//===----------------------------------------------------------------------===// -#include <dlfcn.h> -#include <stdio.h> -#include <string.h> - -#include <string> - -using std::string; - -typedef void (fun_t)(int x); - -int main(int argc, char *argv[]) { - string path = string(argv[0]) + "-so.so"; - printf("opening %s ... \n", path.c_str()); - void *lib = dlopen(path.c_str(), RTLD_NOW); - if (!lib) { - printf("error in dlopen(): %s\n", dlerror()); - return 1; - } - fun_t *inc = (fun_t*)dlsym(lib, "inc"); - if (!inc) return 1; - printf("ok\n"); - inc(1); - inc(-1); // BOOM - return 0; -} - -// Check-Common: {{.*ERROR: AddressSanitizer global-buffer-overflow}} -// Check-Common: {{READ of size 4 at 0x.* thread T0}} -// Check-Common: {{ #0 0x.*}} -// Check-Common: {{ #1 0x.* in main .*shared-lib-test.cc:35}} diff --git a/lib/asan/output_tests/stack-overflow.cc b/lib/asan/output_tests/stack-overflow.cc deleted file mode 100644 index 35fa8a66c34c..000000000000 --- a/lib/asan/output_tests/stack-overflow.cc +++ /dev/null @@ -1,11 +0,0 @@ -#include <string.h> -int main(int argc, char **argv) { - char x[10]; - memset(x, 0, 10); - int res = x[argc * 10]; // BOOOM - return res; -} - -// Check-Common: {{READ of size 1 at 0x.* thread T0}} -// Check-Common: {{ #0 0x.* in main .*stack-overflow.cc:5}} -// Check-Common: {{Address 0x.* is .* frame <main>}} diff --git a/lib/asan/output_tests/stack-use-after-return.cc.disabled b/lib/asan/output_tests/stack-use-after-return.cc.disabled deleted file mode 100644 index f49715737430..000000000000 --- a/lib/asan/output_tests/stack-use-after-return.cc.disabled +++ /dev/null @@ -1,27 +0,0 @@ -#include <stdio.h> - -__attribute__((noinline)) -char *Ident(char *x) { - fprintf(stderr, "1: %p\n", x); - return x; -} - -__attribute__((noinline)) -char *Func1() { - char local; - return Ident(&local); -} - -__attribute__((noinline)) -void Func2(char *x) { - fprintf(stderr, "2: %p\n", x); - *x = 1; - // Check-Common: {{WRITE of size 1 .* thread T0}} - // Check-Common: {{ #0.*Func2.*stack-use-after-return.cc:18}} - // Check-Common: {{is located in frame <.*Func1.*> of T0's stack}} -} - -int main(int argc, char **argv) { - Func2(Func1()); - return 0; -} diff --git a/lib/asan/output_tests/strncpy-overflow.cc b/lib/asan/output_tests/strncpy-overflow.cc deleted file mode 100644 index 66d5810b7040..000000000000 --- a/lib/asan/output_tests/strncpy-overflow.cc +++ /dev/null @@ -1,24 +0,0 @@ -#include <string.h> -#include <stdlib.h> -int main(int argc, char **argv) { - char *hello = (char*)malloc(6); - strcpy(hello, "hello"); - char *short_buffer = (char*)malloc(9); - strncpy(short_buffer, hello, 10); // BOOM - return short_buffer[8]; -} - -// Check-Common: {{WRITE of size 1 at 0x.* thread T0}} -// Check-Linux: {{ #0 0x.* in .*strncpy}} -// Check-Darwin: {{ #0 0x.* in wrap_strncpy}} -// Check-Common: {{ #1 0x.* in main .*strncpy-overflow.cc:7}} -// Check-Common: {{0x.* is located 0 bytes to the right of 9-byte region}} -// Check-Common: {{allocated by thread T0 here:}} - -// Check-Linux: {{ #0 0x.* in .*malloc}} -// Check-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:6}} - -// 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:6}} diff --git a/lib/asan/output_tests/test_output.sh b/lib/asan/output_tests/test_output.sh deleted file mode 100755 index 6510043396e4..000000000000 --- a/lib/asan/output_tests/test_output.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -set -e # fail on any error - -OS=`uname` -CXX=$1 -CC=$2 -FILE_CHECK=$3 -CXXFLAGS="-mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -g" -SYMBOLIZER=../scripts/asan_symbolize.py -TMP_ASAN_REPORT=asan_report.tmp - -run_program() { - ./$1 2>&1 | $SYMBOLIZER 2> /dev/null | c++filt > $TMP_ASAN_REPORT -} - -# check_program exe_file source_file check_prefixf -check_program() { - run_program $1 - $FILE_CHECK $2 --check-prefix=$3 < $TMP_ASAN_REPORT - rm -f $TMP_ASAN_REPORT -} - -C_TEST=use-after-free -echo "Sanity checking a test in pure C" -$CC -g -faddress-sanitizer -O2 $C_TEST.c -check_program a.out $C_TEST.c CHECK -rm ./a.out - -echo "Sanity checking a test in pure C with -pie" -$CC -g -faddress-sanitizer -O2 $C_TEST.c -pie -check_program a.out $C_TEST.c CHECK -rm ./a.out - -echo "Testing sleep_before_dying" -$CC -g -faddress-sanitizer -O2 $C_TEST.c -export ASAN_OPTIONS="sleep_before_dying=1" -check_program a.out $C_TEST.c CHECKSLEEP -export ASAN_OPTIONS="" -rm ./a.out - -# FIXME: some tests do not need to be ran for all the combinations of arch -# and optimization mode. -for t in *.cc; do - for b in 32 64; do - for O in 0 1 2 3; do - c=`basename $t .cc` - if [[ "$c" == *"-so" ]]; then - continue - fi - if [[ "$c" == *"-linux" ]]; then - if [[ "$OS" != "Linux" ]]; then - continue - fi - fi - c_so=$c-so - exe=$c.$b.O$O - so=$c.$b.O$O-so.so - echo testing $exe - build_command="$CXX $CXXFLAGS -m$b -faddress-sanitizer -O$O $c.cc -o $exe" - [ "$DEBUG" == "1" ] && echo $build_command - $build_command - [ -e "$c_so.cc" ] && $CXX $CXXFLAGS -m$b -faddress-sanitizer -O$O $c_so.cc -fPIC -shared -o $so - run_program $exe - # Check common expected lines for OS. - $FILE_CHECK $c.cc --check-prefix="Check-Common" < $TMP_ASAN_REPORT - # Check OS-specific lines. - if [ `grep -c "Check-$OS" $c.cc` -gt 0 ] - then - $FILE_CHECK $c.cc --check-prefix="Check-$OS" < $TMP_ASAN_REPORT - fi - rm ./$exe - rm ./$TMP_ASAN_REPORT - [ -e "$so" ] && rm ./$so - done - done -done - -exit 0 diff --git a/lib/asan/output_tests/use-after-free.c b/lib/asan/output_tests/use-after-free.c deleted file mode 100644 index 801d3f68a466..000000000000 --- a/lib/asan/output_tests/use-after-free.c +++ /dev/null @@ -1,9 +0,0 @@ -#include <stdlib.h> -int main() { - char *x = (char*)malloc(10 * sizeof(char)); - free(x); - return x[5]; -} - -// CHECK: heap-use-after-free -// CHECKSLEEP: Sleeping for 1 second diff --git a/lib/asan/output_tests/use-after-free.cc b/lib/asan/output_tests/use-after-free.cc deleted file mode 100644 index c3e9dbe6db75..000000000000 --- a/lib/asan/output_tests/use-after-free.cc +++ /dev/null @@ -1,31 +0,0 @@ -#include <stdlib.h> -int main() { - char *x = (char*)malloc(10 * sizeof(char)); - free(x); - return x[5]; -} - -// Check-Common: {{.*ERROR: AddressSanitizer heap-use-after-free on address}} -// Check-Common: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} -// Check-Common: {{READ of size 1 at 0x.* thread T0}} -// Check-Common: {{ #0 0x.* in main .*use-after-free.cc:5}} -// Check-Common: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}} -// Check-Common: {{freed by thread T0 here:}} - -// Check-Linux: {{ #0 0x.* in .*free}} -// Check-Linux: {{ #1 0x.* in main .*use-after-free.cc:4}} - -// Check-Darwin: {{ #0 0x.* in .*mz_free.*}} -// We override free() on Darwin, thus no malloc_zone_free -// Check-Darwin: {{ #1 0x.* in wrap_free}} -// Check-Darwin: {{ #2 0x.* in main .*use-after-free.cc:4}} - -// Check-Common: {{previously allocated by thread T0 here:}} - -// Check-Linux: {{ #0 0x.* in .*malloc}} -// Check-Linux: {{ #1 0x.* in main .*use-after-free.cc:3}} - -// 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:3}} diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py index e4897d0c7649..7b30bb55914e 100755 --- a/lib/asan/scripts/asan_symbolize.py +++ b/lib/asan/scripts/asan_symbolize.py @@ -7,121 +7,350 @@ # License. See LICENSE.TXT for details. # #===------------------------------------------------------------------------===# +import bisect import os import re -import sys -import string import subprocess +import sys -pipes = {} +llvm_symbolizer = None +symbolizers = {} filetypes = {} -DEBUG=False +vmaddrs = {} +DEBUG = False + +# FIXME: merge the code that calls fix_filename(). def fix_filename(file_name): for path_to_cut in sys.argv[1:]: - file_name = re.sub(".*" + path_to_cut, "", file_name) - file_name = re.sub(".*asan_[a-z_]*.cc:[0-9]*", "_asan_rtl_", file_name) - file_name = re.sub(".*crtstuff.c:0", "???:0", file_name) + file_name = re.sub('.*' + path_to_cut, '', file_name) + file_name = re.sub('.*asan_[a-z_]*.cc:[0-9]*', '_asan_rtl_', file_name) + file_name = re.sub('.*crtstuff.c:0', '???:0', file_name) return file_name -# TODO(glider): need some refactoring here -def symbolize_addr2line(line): - #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) - match = re.match('^( *#([0-9]+) *0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line) - if match: - frameno = match.group(2) - binary = match.group(3) - addr = match.group(4) - if not pipes.has_key(binary): - pipes[binary] = subprocess.Popen(["addr2line", "-f", "-e", binary], - stdin=subprocess.PIPE, stdout=subprocess.PIPE) - p = pipes[binary] +class Symbolizer(object): + def __init__(self): + pass + + def symbolize(self, addr, binary, offset): + """Symbolize the given address (pair of binary and offset). + + Overriden in subclasses. + Args: + addr: virtual address of an instruction. + binary: path to executable/shared object containing this instruction. + offset: instruction offset in the @binary. + Returns: + list of strings (one string for each inlined frame) describing + the code locations for this instruction (that is, function name, file + name, line and column numbers). + """ + return None + + +class LLVMSymbolizer(Symbolizer): + def __init__(self, symbolizer_path): + super(LLVMSymbolizer, self).__init__() + self.symbolizer_path = symbolizer_path + self.pipe = self.open_llvm_symbolizer() + + def open_llvm_symbolizer(self): + if not os.path.exists(self.symbolizer_path): + return None + cmd = [self.symbolizer_path, + '--use-symbol-table=true', + '--demangle=false', + '--functions=true', + '--inlining=true'] + if DEBUG: + print ' '.join(cmd) + return subprocess.Popen(cmd, stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + + def symbolize(self, addr, binary, offset): + """Overrides Symbolizer.symbolize.""" + if not self.pipe: + return None + result = [] try: - print >>p.stdin, addr - function_name = p.stdout.readline().rstrip() - file_name = p.stdout.readline().rstrip() - except: - function_name = "" - file_name = "" - file_name = fix_filename(file_name) + symbolizer_input = '%s %s' % (binary, offset) + if DEBUG: + print symbolizer_input + print >> self.pipe.stdin, symbolizer_input + while True: + function_name = self.pipe.stdout.readline().rstrip() + if not function_name: + break + file_name = self.pipe.stdout.readline().rstrip() + file_name = fix_filename(file_name) + if (not function_name.startswith('??') and + not file_name.startswith('??')): + # Append only valid frames. + result.append('%s in %s %s' % (addr, function_name, + file_name)) + except Exception: + result = [] + if not result: + result = None + return result - print match.group(1), "in", function_name, file_name - else: - print line.rstrip() - - -def get_macho_filetype(binary): - if not filetypes.has_key(binary): - otool_pipe = subprocess.Popen(["otool", "-Vh", binary], - stdin=subprocess.PIPE, stdout=subprocess.PIPE) - otool_line = "".join(otool_pipe.stdout.readlines()) - for t in ["DYLIB", "EXECUTE"]: - if t in otool_line: - filetypes[binary] = t - otool_pipe.stdin.close() - return filetypes[binary] - - -def symbolize_atos(line): - #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) - match = re.match('^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line) - if match: - #print line - prefix = match.group(1) - frameno = match.group(2) - orig_addr = match.group(3) - binary = match.group(4) - offset = match.group(5) - addr = orig_addr - load_addr = hex(int(orig_addr, 16) - int(offset, 16)) - filetype = get_macho_filetype(binary) - - if not pipes.has_key(binary): - # Guess which arch we're running. 10 = len("0x") + 8 hex digits. - if len(addr) > 10: - arch = "x86_64" - else: - arch = "i386" - if filetype == "DYLIB": - load_addr = "0x0" +def LLVMSymbolizerFactory(system): + symbolizer_path = os.getenv('LLVM_SYMBOLIZER_PATH') + if not symbolizer_path: + # Assume llvm-symbolizer is in PATH. + symbolizer_path = 'llvm-symbolizer' + return LLVMSymbolizer(symbolizer_path) + + +class Addr2LineSymbolizer(Symbolizer): + def __init__(self, binary): + super(Addr2LineSymbolizer, self).__init__() + self.binary = binary + self.pipe = self.open_addr2line() + + def open_addr2line(self): + cmd = ['addr2line', '-f', '-e', self.binary] if DEBUG: - print "atos -o %s -arch %s -l %s" % (binary, arch, load_addr) - cmd = ["atos", "-o", binary, "-arch", arch, "-l", load_addr] - pipes[binary] = subprocess.Popen(cmd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - p = pipes[binary] - if filetype == "DYLIB": - print >>p.stdin, "%s" % offset + print ' '.join(cmd) + return subprocess.Popen(cmd, + stdin=subprocess.PIPE, stdout=subprocess.PIPE) + + def symbolize(self, addr, binary, offset): + """Overrides Symbolizer.symbolize.""" + if self.binary != binary: + return None + try: + print >> self.pipe.stdin, offset + function_name = self.pipe.stdout.readline().rstrip() + file_name = self.pipe.stdout.readline().rstrip() + except Exception: + function_name = '' + file_name = '' + file_name = fix_filename(file_name) + return ['%s in %s %s' % (addr, function_name, file_name)] + + +class DarwinSymbolizer(Symbolizer): + def __init__(self, addr, binary): + super(DarwinSymbolizer, self).__init__() + self.binary = binary + # Guess which arch we're running. 10 = len('0x') + 8 hex digits. + if len(addr) > 10: + self.arch = 'x86_64' else: - print >>p.stdin, "%s" % addr - # TODO(glider): it's more efficient to make a batch atos run for each binary. - p.stdin.close() - atos_line = p.stdout.readline().rstrip() + self.arch = 'i386' + self.vmaddr = None + self.pipe = None + + def write_addr_to_pipe(self, offset): + print >> self.pipe.stdin, '0x%x' % int(offset, 16) + + def open_atos(self): + if DEBUG: + print 'atos -o %s -arch %s' % (self.binary, self.arch) + cmdline = ['atos', '-o', self.binary, '-arch', self.arch] + self.pipe = subprocess.Popen(cmdline, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + def symbolize(self, addr, binary, offset): + """Overrides Symbolizer.symbolize.""" + if self.binary != binary: + return None + self.open_atos() + self.write_addr_to_pipe(offset) + self.pipe.stdin.close() + atos_line = self.pipe.stdout.readline().rstrip() # A well-formed atos response looks like this: # foo(type1, type2) (in object.name) (filename.cc:80) match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line) - #print "atos_line: ", atos_line + if DEBUG: + print 'atos_line: ', atos_line if match: function_name = match.group(1) - function_name = re.sub("\(.*?\)", "", function_name) + function_name = re.sub('\(.*?\)', '', function_name) file_name = fix_filename(match.group(3)) - print "%s%s in %s %s" % (prefix, addr, function_name, file_name) + return ['%s in %s %s' % (addr, function_name, file_name)] + else: + return ['%s in %s' % (addr, atos_line)] + + +# Chain several symbolizers so that if one symbolizer fails, we fall back +# to the next symbolizer in chain. +class ChainSymbolizer(Symbolizer): + def __init__(self, symbolizer_list): + super(ChainSymbolizer, self).__init__() + self.symbolizer_list = symbolizer_list + + def symbolize(self, addr, binary, offset): + """Overrides Symbolizer.symbolize.""" + for symbolizer in self.symbolizer_list: + if symbolizer: + result = symbolizer.symbolize(addr, binary, offset) + if result: + return result + return None + + def append_symbolizer(self, symbolizer): + self.symbolizer_list.append(symbolizer) + + +def BreakpadSymbolizerFactory(binary): + suffix = os.getenv('BREAKPAD_SUFFIX') + if suffix: + filename = binary + suffix + if os.access(filename, os.F_OK): + return BreakpadSymbolizer(filename) + return None + + +def SystemSymbolizerFactory(system, addr, binary): + if system == 'Darwin': + return DarwinSymbolizer(addr, binary) + elif system == 'Linux': + return Addr2LineSymbolizer(binary) + + +class BreakpadSymbolizer(Symbolizer): + def __init__(self, filename): + super(BreakpadSymbolizer, self).__init__() + self.filename = filename + lines = file(filename).readlines() + self.files = [] + self.symbols = {} + self.address_list = [] + self.addresses = {} + # MODULE mac x86_64 A7001116478B33F18FF9BEDE9F615F190 t + fragments = lines[0].rstrip().split() + self.arch = fragments[2] + self.debug_id = fragments[3] + self.binary = ' '.join(fragments[4:]) + self.parse_lines(lines[1:]) + + def parse_lines(self, lines): + cur_function_addr = '' + for line in lines: + fragments = line.split() + if fragments[0] == 'FILE': + assert int(fragments[1]) == len(self.files) + self.files.append(' '.join(fragments[2:])) + elif fragments[0] == 'PUBLIC': + self.symbols[int(fragments[1], 16)] = ' '.join(fragments[3:]) + elif fragments[0] in ['CFI', 'STACK']: + pass + elif fragments[0] == 'FUNC': + cur_function_addr = int(fragments[1], 16) + if not cur_function_addr in self.symbols.keys(): + self.symbols[cur_function_addr] = ' '.join(fragments[4:]) + else: + # Line starting with an address. + addr = int(fragments[0], 16) + self.address_list.append(addr) + # Tuple of symbol address, size, line, file number. + self.addresses[addr] = (cur_function_addr, + int(fragments[1], 16), + int(fragments[2]), + int(fragments[3])) + self.address_list.sort() + + def get_sym_file_line(self, addr): + key = None + if addr in self.addresses.keys(): + key = addr else: - print "%s%s in %s" % (prefix, addr, atos_line) - del pipes[binary] - else: - print line.rstrip() - -system = os.uname()[0] -if system in ['Linux', 'Darwin']: - for line in sys.stdin: - if system == 'Linux': - symbolize_addr2line(line) - elif system == 'Darwin': - symbolize_atos(line) -else: - print 'Unknown system: ', system + index = bisect.bisect_left(self.address_list, addr) + if index == 0: + return None + else: + key = self.address_list[index - 1] + sym_id, size, line_no, file_no = self.addresses[key] + symbol = self.symbols[sym_id] + filename = self.files[file_no] + if addr < key + size: + return symbol, filename, line_no + else: + return None + + def symbolize(self, addr, binary, offset): + if self.binary != binary: + return None + res = self.get_sym_file_line(int(offset, 16)) + if res: + function_name, file_name, line_no = res + result = ['%s in %s %s:%d' % ( + addr, function_name, file_name, line_no)] + print result + return result + else: + return None + + +class SymbolizationLoop(object): + def __init__(self, binary_name_filter=None): + # Used by clients who may want to supply a different binary name. + # E.g. in Chrome several binaries may share a single .dSYM. + self.binary_name_filter = binary_name_filter + self.system = os.uname()[0] + if self.system in ['Linux', 'Darwin']: + self.llvm_symbolizer = LLVMSymbolizerFactory(self.system) + else: + raise Exception('Unknown system') + + def symbolize_address(self, addr, binary, offset): + # Use the chain of symbolizers: + # Breakpad symbolizer -> LLVM symbolizer -> addr2line/atos + # (fall back to next symbolizer if the previous one fails). + if not binary in symbolizers: + symbolizers[binary] = ChainSymbolizer( + [BreakpadSymbolizerFactory(binary), self.llvm_symbolizer]) + result = symbolizers[binary].symbolize(addr, binary, offset) + if result is None: + # Initialize system symbolizer only if other symbolizers failed. + symbolizers[binary].append_symbolizer( + SystemSymbolizerFactory(self.system, addr, binary)) + result = symbolizers[binary].symbolize(addr, binary, offset) + # The system symbolizer must produce some result. + assert result + return result + + def print_symbolized_lines(self, symbolized_lines): + if not symbolized_lines: + print self.current_line + else: + for symbolized_frame in symbolized_lines: + print ' #' + str(self.frame_no) + ' ' + symbolized_frame.rstrip() + self.frame_no += 1 + + def process_stdin(self): + self.frame_no = 0 + for line in sys.stdin: + self.current_line = line.rstrip() + #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) + stack_trace_line_format = ( + '^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)') + match = re.match(stack_trace_line_format, line) + if not match: + print self.current_line + continue + if DEBUG: + print line + _, frameno_str, addr, binary, offset = match.groups() + if frameno_str == '0': + # Assume that frame #0 is the first frame of new stack trace. + self.frame_no = 0 + original_binary = binary + if self.binary_name_filter: + binary = self.binary_name_filter(binary) + symbolized_line = self.symbolize_address(addr, binary, offset) + if not symbolized_line: + if original_binary != binary: + symbolized_line = self.symbolize_address(addr, binary, offset) + self.print_symbolized_lines(symbolized_line) + + +if __name__ == '__main__': + loop = SymbolizationLoop() + loop.process_stdin() diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt index d409d50b995e..272950bc5450 100644 --- a/lib/asan/tests/CMakeLists.txt +++ b/lib/asan/tests/CMakeLists.txt @@ -10,109 +10,178 @@ # instrumentation against the just-built runtime library. include(CheckCXXCompilerFlag) +include(CompilerRTCompile) include_directories(..) include_directories(../..) +set(ASAN_UNITTEST_HEADERS + asan_mac_test.h + asan_test_config.h + asan_test_utils.h) + set(ASAN_UNITTEST_COMMON_CFLAGS + ${COMPILER_RT_GTEST_INCLUDE_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/include + -I${COMPILER_RT_SOURCE_DIR}/lib + -I${COMPILER_RT_SOURCE_DIR}/lib/asan -Wall -Wno-format - -fvisibility=hidden + -Werror + -g + -O2 ) -# Support 64-bit and 32-bit builds. -if(LLVM_BUILD_32_BITS) - list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -m32) + +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. +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 + ) else() - list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -m64) + list(APPEND ASAN_UNITTEST_COMMON_CFLAGS + -DASAN_HAS_BLACKLIST=1 + -DASAN_HAS_EXCEPTIONS=1 + -DASAN_NEEDS_SEGV=1 + -DASAN_UAR=0 + ) endif() -set(ASAN_GTEST_INCLUDE_CFLAGS - -I${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include - -I${LLVM_MAIN_SRC_DIR}/include - -I${LLVM_BINARY_DIR}/include - -D__STDC_CONSTANT_MACROS - -D__STDC_LIMIT_MACROS -) +set(ASAN_LINK_FLAGS) +if(ANDROID) + # On Android, we link with ASan runtime manually + list(APPEND ASAN_LINK_FLAGS -pie) +else() + # On other platforms, we depend on Clang driver behavior, + # passing -fsanitize=address flag. + list(APPEND ASAN_LINK_FLAGS -fsanitize=address) +endif() + +# Unit tests on Mac depend on Foundation. +if(APPLE) + list(APPEND ASAN_LINK_FLAGS -framework Foundation) +endif() +# Unit tests require libstdc++. +list(APPEND ASAN_LINK_FLAGS -lstdc++) + +set(ASAN_BLACKLIST_FILE "${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore") set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS ${ASAN_UNITTEST_COMMON_CFLAGS} - ${ASAN_GTEST_INCLUDE_CFLAGS} - -faddress-sanitizer - -O2 - -g - -mllvm "-asan-blacklist=${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore" - -DASAN_HAS_BLACKLIST=1 - -DASAN_HAS_EXCEPTIONS=1 - -DASAN_NEEDS_SEGV=1 - -DASAN_UAR=0 + -fsanitize=address + -mllvm "-asan-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 ) -add_custom_target(AsanTests) -set_target_properties(AsanTests PROPERTIES FOLDER "ASan tests") -function(add_asan_test testname) - add_unittest(AsanTests ${testname} ${ARGN}) - if(LLVM_BUILD_32_BITS) - target_link_libraries(${testname} clang_rt.asan-i386) - else() - target_link_libraries(${testname} clang_rt.asan-x86_64) - endif() - if (APPLE) - # Darwin-specific linker flags. - set_property(TARGET ${testname} APPEND PROPERTY - LINK_FLAGS "-framework Foundation") - elseif (UNIX) - # Linux-specific linker flags. - set_property(TARGET ${testname} APPEND PROPERTY - LINK_FLAGS "-lpthread -ldl -export-dynamic") - endif() - set(add_compile_flags "") - get_property(compile_flags TARGET ${testname} PROPERTY COMPILE_FLAGS) - foreach(arg ${ASAN_UNITTEST_COMMON_CFLAGS}) - set(add_compile_flags "${add_compile_flags} ${arg}") - endforeach(arg ${ASAN_UNITTEST_COMMON_CFLAGS}) - set_property(TARGET ${testname} PROPERTY COMPILE_FLAGS - "${compile_flags} ${add_compile_flags}") -endfunction() - -set(ASAN_NOINST_TEST_SOURCES - asan_noinst_test.cc - asan_break_optimization.cc -) +# Compile source for the given architecture, using compiler +# options in ${ARGN}, and add it to the object list. +macro(asan_compile obj_list source arch) + get_filename_component(basename ${source} NAME) + set(output_obj "${basename}.${arch}.o") + get_target_flags_for_arch(${arch} TARGET_CFLAGS) + clang_compile(${output_obj} ${source} + CFLAGS ${ARGN} ${TARGET_CFLAGS} + DEPS gtest ${ASAN_RUNTIME_LIBRARIES} + ${ASAN_UNITTEST_HEADERS} + ${ASAN_BLACKLIST_FILE}) + list(APPEND ${obj_list} ${output_obj}) +endmacro() -set(ASAN_INST_TEST_OBJECTS) - -# We only support building instrumented tests when we're not cross compiling -# and targeting a unix-like system where we can predict viable compilation and -# linking strategies. -if("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND UNIX) - - # This function is a custom routine to manage manually compiling source files - # for unit tests with the just-built Clang binary, using the ASan - # instrumentation, and linking them into a test executable. - function(add_asan_compile_command source extra_cflags) - set(output_obj "${source}.asan.o") - add_custom_command( - OUTPUT ${output_obj} - COMMAND clang - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} - ${extra_cflags} - -c -o "${output_obj}" - ${CMAKE_CURRENT_SOURCE_DIR}/${source} - MAIN_DEPENDENCY ${source} - DEPENDS clang clang_rt.asan-i386 clang_rt.asan-x86_64 ${ARGN} - ) - endfunction() - - add_asan_compile_command(asan_globals_test.cc "") - add_asan_compile_command(asan_test.cc "") - list(APPEND ASAN_INST_TEST_OBJECTS asan_globals_test.cc.asan.o - asan_test.cc.asan.o) +# Link ASan unit test for a given architecture from a set +# of objects in ${ARGN}. +macro(add_asan_test test_suite test_name arch) + get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + add_compiler_rt_test(${test_suite} ${test_name} + OBJECTS ${ARGN} + DEPS ${ASAN_RUNTIME_LIBRARIES} ${ARGN} + LINK_FLAGS ${ASAN_LINK_FLAGS} + ${TARGET_LINK_FLAGS}) +endmacro() + +# Main AddressSanitizer unit tests. +add_custom_target(AsanUnitTests) +set_target_properties(AsanUnitTests PROPERTIES FOLDER "ASan unit tests") +# ASan benchmarks (not actively used now). +add_custom_target(AsanBenchmarks) +set_target_properties(AsanBenchmarks PROPERTIES FOLDER "Asan benchmarks") + +# Adds ASan unit tests and benchmarks for architecture. +macro(add_asan_tests_for_arch arch) + # Build gtest instrumented with ASan. + set(ASAN_INST_GTEST) + asan_compile(ASAN_INST_GTEST ${COMPILER_RT_GTEST_SOURCE} ${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}) if (APPLE) - add_asan_compile_command(asan_mac_test.mm "-ObjC") - list(APPEND ASAN_INST_TEST_OBJECTS asan_mac_test.mm.asan.o) + asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test.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}) + # Link everything together. + add_asan_test(AsanUnitTests "Asan-${arch}-Test" ${arch} + ${ASAN_NOINST_TEST_OBJECTS} + ${ASAN_INST_TEST_OBJECTS} ${ASAN_INST_GTEST}) + + # Instrumented benchmarks. + set(ASAN_BENCHMARKS_OBJECTS) + asan_compile(ASAN_BENCHMARKS_OBJECTS asan_benchmarks_test.cc ${arch} + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + # Link benchmarks. + add_asan_test(AsanBenchmarks "Asan-${arch}-Benchmark" ${arch} + ${ASAN_BENCHMARKS_OBJECTS} ${ASAN_INST_GTEST}) +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() endif() -add_asan_test(AsanTest ${ASAN_NOINST_TEST_SOURCES} - ${ASAN_INST_TEST_OBJECTS}) +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 + ${ASAN_INST_TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}) + set_target_compile_flags(asan_inst_test ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + add_executable(AsanTest + $<TARGET_OBJECTS:asan_noinst_test> + $<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}) + set_target_link_flags(AsanTest ${ASAN_LINK_FLAGS}) + target_link_libraries(AsanTest clang_rt.asan-arm-android) + # Add unit test to test suite. + add_dependencies(AsanUnitTests AsanTest) +endif() diff --git a/lib/asan/tests/asan_benchmarks_test.cc b/lib/asan/tests/asan_benchmarks_test.cc index a142fd23e1b8..fc522de475fa 100644 --- a/lib/asan/tests/asan_benchmarks_test.cc +++ b/lib/asan/tests/asan_benchmarks_test.cc @@ -12,7 +12,6 @@ // Some benchmarks for the instrumented code. //===----------------------------------------------------------------------===// -#include "asan_test_config.h" #include "asan_test_utils.h" template<class T> diff --git a/lib/asan/tests/asan_globals_test.cc b/lib/asan/tests/asan_globals_test.cc index 6467524ca32c..dc2e9bbb0530 100644 --- a/lib/asan/tests/asan_globals_test.cc +++ b/lib/asan/tests/asan_globals_test.cc @@ -1,4 +1,4 @@ -//===-- asan_globals_test.cc ----------------------===// +//===-- asan_globals_test.cc ----------------------------------------------===// // // The LLVM Compiler Infrastructure // diff --git a/lib/asan/tests/asan_mac_test.mm b/lib/asan/tests/asan_mac_test.mm index 4e5873b74485..4cbd2bb247fd 100644 --- a/lib/asan/tests/asan_mac_test.mm +++ b/lib/asan/tests/asan_mac_test.mm @@ -57,7 +57,7 @@ char kStartupStr[] = @implementation LoadSomething +(void) load { - for (int i = 0; i < strlen(kStartupStr); i++) { + for (size_t i = 0; i < strlen(kStartupStr); i++) { access_memory(&kStartupStr[i]); // make sure no optimizations occur. } // Don't print anything here not to interfere with the death tests. @@ -66,13 +66,13 @@ char kStartupStr[] = @end void worker_do_alloc(int size) { - char * volatile mem = malloc(size); + char * volatile mem = (char * volatile)malloc(size); mem[0] = 0; // Ok free(mem); } void worker_do_crash(int size) { - char * volatile mem = malloc(size); + char * volatile mem = (char * volatile)malloc(size); access_memory(&mem[size]); // BOOM free(mem); } @@ -167,7 +167,7 @@ void TestGCDSourceEvent() { dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0); - char * volatile mem = malloc(10); + char * volatile mem = (char * volatile)malloc(10); dispatch_source_set_event_handler(timer, ^{ access_memory(&mem[10]); }); @@ -184,7 +184,7 @@ void TestGCDSourceCancel() { dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0); - char * volatile mem = malloc(10); + char * volatile mem = (char * volatile)malloc(10); // Both dispatch_source_set_cancel_handler() and // dispatch_source_set_event_handler() use dispatch_barrier_async_f(). // It's tricky to test dispatch_source_set_cancel_handler() separately, @@ -202,7 +202,7 @@ void TestGCDSourceCancel() { void TestGCDGroupAsync() { dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_group_t group = dispatch_group_create(); - char * volatile mem = malloc(10); + char * volatile mem = (char * volatile)malloc(10); dispatch_group_async(group, queue, ^{ access_memory(&mem[10]); }); diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc index 44d4c3c845b2..576312bf319f 100644 --- a/lib/asan/tests/asan_noinst_test.cc +++ b/lib/asan/tests/asan_noinst_test.cc @@ -1,4 +1,4 @@ -//===-- asan_noinst_test.cc ----------------------===// +//===-- asan_noinst_test.cc -----------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -11,12 +11,13 @@ // // This test file should be compiled w/o asan instrumentation. //===----------------------------------------------------------------------===// + #include "asan_allocator.h" -#include "asan_interface.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_stack.h" #include "asan_test_utils.h" +#include "sanitizer/asan_interface.h" #include <assert.h> #include <stdio.h> @@ -24,15 +25,6 @@ #include <string.h> // for memset() #include <algorithm> #include <vector> -#include "gtest/gtest.h" - -// Simple stand-alone pseudorandom number generator. -// Current algorithm is ANSI C linear congruential PRNG. -static inline u32 my_rand(u32* state) { - return (*state = *state * 1103515245 + 12345) >> 16; -} - -static u32 global_seed = 0; TEST(AddressSanitizer, InternalSimpleDeathTest) { @@ -40,18 +32,18 @@ TEST(AddressSanitizer, InternalSimpleDeathTest) { } static void MallocStress(size_t n) { - u32 seed = my_rand(&global_seed); - __asan::AsanStackTrace stack1; + u32 seed = my_rand(); + __asan::StackTrace stack1; stack1.trace[0] = 0xa123; stack1.trace[1] = 0xa456; stack1.size = 2; - __asan::AsanStackTrace stack2; + __asan::StackTrace stack2; stack2.trace[0] = 0xb123; stack2.trace[1] = 0xb456; stack2.size = 2; - __asan::AsanStackTrace stack3; + __asan::StackTrace stack3; stack3.trace[0] = 0xc123; stack3.trace[1] = 0xc456; stack3.size = 2; @@ -60,20 +52,21 @@ static void MallocStress(size_t n) { for (size_t i = 0; i < n; i++) { if ((i % 3) == 0) { if (vec.empty()) continue; - size_t idx = my_rand(&seed) % vec.size(); + size_t idx = my_rand_r(&seed) % vec.size(); void *ptr = vec[idx]; vec[idx] = vec.back(); vec.pop_back(); - __asan::asan_free(ptr, &stack1); + __asan::asan_free(ptr, &stack1, __asan::FROM_MALLOC); } else { - size_t size = my_rand(&seed) % 1000 + 1; - switch ((my_rand(&seed) % 128)) { + size_t size = my_rand_r(&seed) % 1000 + 1; + switch ((my_rand_r(&seed) % 128)) { case 0: size += 1024; break; case 1: size += 2048; break; case 2: size += 4096; break; } - size_t alignment = 1 << (my_rand(&seed) % 10 + 1); - char *ptr = (char*)__asan::asan_memalign(alignment, size, &stack2); + size_t alignment = 1 << (my_rand_r(&seed) % 10 + 1); + char *ptr = (char*)__asan::asan_memalign(alignment, size, + &stack2, __asan::FROM_MALLOC); vec.push_back(ptr); ptr[0] = 0; ptr[size-1] = 0; @@ -81,7 +74,7 @@ static void MallocStress(size_t n) { } } for (size_t i = 0; i < vec.size(); i++) - __asan::asan_free(vec[i], &stack3); + __asan::asan_free(vec[i], &stack3, __asan::FROM_MALLOC); } @@ -118,7 +111,7 @@ TEST(AddressSanitizer, DISABLED_InternalPrintShadow) { } static uptr pc_array[] = { -#if __WORDSIZE == 64 +#if SANITIZER_WORDSIZE == 64 0x7effbf756068ULL, 0x7effbf75e5abULL, 0x7effc0625b7cULL, @@ -164,7 +157,7 @@ static uptr pc_array[] = { 0x7effbcc3e726ULL, 0x7effbcc40852ULL, 0x7effb681ec4dULL, -#endif // __WORDSIZE +#endif // SANITIZER_WORDSIZE 0xB0B5E768, 0x7B682EC1, 0x367F9918, @@ -208,22 +201,22 @@ static uptr pc_array[] = { }; void CompressStackTraceTest(size_t n_iter) { - u32 seed = my_rand(&global_seed); - const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array); + u32 seed = my_rand(); + const size_t kNumPcs = ARRAY_SIZE(pc_array); u32 compressed[2 * kNumPcs]; for (size_t iter = 0; iter < n_iter; iter++) { std::random_shuffle(pc_array, pc_array + kNumPcs); - __asan::AsanStackTrace stack0, stack1; + __asan::StackTrace stack0, stack1; stack0.CopyFrom(pc_array, kNumPcs); - stack0.size = std::max((size_t)1, (size_t)(my_rand(&seed) % stack0.size)); + 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(&seed) % (2 * kNumPcs)); + std::max((size_t)2, (size_t)my_rand_r(&seed) % (2 * kNumPcs)); size_t n_frames = - __asan::AsanStackTrace::CompressStack(&stack0, compressed, compress_size); + __asan::StackTrace::CompressStack(&stack0, compressed, compress_size); Ident(n_frames); assert(n_frames <= stack0.size); - __asan::AsanStackTrace::UncompressStack(&stack1, compressed, compress_size); + __asan::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]); @@ -236,17 +229,17 @@ TEST(AddressSanitizer, CompressStackTraceTest) { } void CompressStackTraceBenchmark(size_t n_iter) { - const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array); + const size_t kNumPcs = ARRAY_SIZE(pc_array); u32 compressed[2 * kNumPcs]; std::random_shuffle(pc_array, pc_array + kNumPcs); - __asan::AsanStackTrace stack0; + __asan::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::AsanStackTrace::CompressStack(&stack0, compressed, compress_size); + __asan::StackTrace::CompressStack(&stack0, compressed, compress_size); Ident(n_frames); } } @@ -256,18 +249,18 @@ TEST(AddressSanitizer, CompressStackTraceBenchmark) { } TEST(AddressSanitizer, QuarantineTest) { - __asan::AsanStackTrace stack; + __asan::StackTrace stack; stack.trace[0] = 0x890; stack.size = 1; const int size = 32; void *p = __asan::asan_malloc(size, &stack); - __asan::asan_free(p, &stack); + __asan::asan_free(p, &stack, __asan::FROM_MALLOC); size_t i; size_t max_i = 1 << 30; for (i = 0; i < max_i; i++) { void *p1 = __asan::asan_malloc(size, &stack); - __asan::asan_free(p1, &stack); + __asan::asan_free(p1, &stack, __asan::FROM_MALLOC); if (p1 == p) break; } // fprintf(stderr, "i=%ld\n", i); @@ -277,14 +270,14 @@ TEST(AddressSanitizer, QuarantineTest) { void *ThreadedQuarantineTestWorker(void *unused) { (void)unused; - u32 seed = my_rand(&global_seed); - __asan::AsanStackTrace stack; + u32 seed = my_rand(); + __asan::StackTrace stack; stack.trace[0] = 0x890; stack.size = 1; for (size_t i = 0; i < 1000; i++) { - void *p = __asan::asan_malloc(1 + (my_rand(&seed) % 4000), &stack); - __asan::asan_free(p, &stack); + void *p = __asan::asan_malloc(1 + (my_rand_r(&seed) % 4000), &stack); + __asan::asan_free(p, &stack, __asan::FROM_MALLOC); } return NULL; } @@ -296,8 +289,8 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) { size_t mmaped1 = __asan_get_heap_size(); for (int i = 0; i < n_threads; i++) { pthread_t t; - pthread_create(&t, NULL, ThreadedQuarantineTestWorker, 0); - pthread_join(t, 0); + PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0); + PTHREAD_JOIN(t, 0); size_t mmaped2 = __asan_get_heap_size(); EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20)); } @@ -305,7 +298,7 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) { void *ThreadedOneSizeMallocStress(void *unused) { (void)unused; - __asan::AsanStackTrace stack; + __asan::StackTrace stack; stack.trace[0] = 0x890; stack.size = 1; const size_t kNumMallocs = 1000; @@ -315,7 +308,7 @@ void *ThreadedOneSizeMallocStress(void *unused) { p[i] = __asan::asan_malloc(32, &stack); } for (size_t i = 0; i < kNumMallocs; i++) { - __asan::asan_free(p[i], &stack); + __asan::asan_free(p[i], &stack, __asan::FROM_MALLOC); } } return NULL; @@ -325,10 +318,10 @@ TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) { const int kNumThreads = 4; pthread_t t[kNumThreads]; for (int i = 0; i < kNumThreads; i++) { - pthread_create(&t[i], 0, ThreadedOneSizeMallocStress, 0); + PTHREAD_CREATE(&t[i], 0, ThreadedOneSizeMallocStress, 0); } for (int i = 0; i < kNumThreads; i++) { - pthread_join(t[i], 0); + PTHREAD_JOIN(t[i], 0); } } @@ -336,16 +329,20 @@ TEST(AddressSanitizer, MemsetWildAddressTest) { 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 + kPageSize), 0, 100), + EXPECT_DEATH(libc_memset((void*)(kLowShadowBeg + 200), 0, 100), "unknown-crash.*low shadow"); - EXPECT_DEATH(libc_memset((void*)(kShadowGapBeg + kPageSize), 0, 100), + EXPECT_DEATH(libc_memset((void*)(kShadowGapBeg + 200), 0, 100), "unknown-crash.*shadow gap"); - EXPECT_DEATH(libc_memset((void*)(kHighShadowBeg + kPageSize), 0, 100), + EXPECT_DEATH(libc_memset((void*)(kHighShadowBeg + 200), 0, 100), "unknown-crash.*high shadow"); } 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])); @@ -370,23 +367,32 @@ TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) { // We cannot call GetAllocatedSize from the memory we didn't map, // and from the interior pointers (not returned by previous malloc). void *wild_addr = (void*)0x1; - EXPECT_EQ(false, __asan_get_ownership(wild_addr)); + EXPECT_FALSE(__asan_get_ownership(wild_addr)); EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg); - EXPECT_EQ(false, __asan_get_ownership(array + kArraySize / 2)); + EXPECT_FALSE(__asan_get_ownership(array + kArraySize / 2)); EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2), kGetAllocatedSizeErrorMsg); // NULL is not owned, but is a valid argument for __asan_get_allocated_size(). - EXPECT_EQ(false, __asan_get_ownership(NULL)); + EXPECT_FALSE(__asan_get_ownership(NULL)); EXPECT_EQ(0U, __asan_get_allocated_size(NULL)); // When memory is freed, it's not owned, and call to GetAllocatedSize // is forbidden. free(array); - EXPECT_EQ(false, __asan_get_ownership(array)); + EXPECT_FALSE(__asan_get_ownership(array)); EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg); - delete int_ptr; + + void *zero_alloc = Ident(malloc(0)); + if (zero_alloc != 0) { + // If malloc(0) is not null, this pointer is owned and should have valid + // allocated size. + EXPECT_TRUE(__asan_get_ownership(zero_alloc)); + // Allocated size is 0 or 1 depending on the allocator used. + EXPECT_LT(__asan_get_allocated_size(zero_alloc), 2U); + } + free(zero_alloc); } TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) { @@ -410,6 +416,7 @@ 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. @@ -441,9 +448,26 @@ static void RunGetHeapSizeTestAndDie() { 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 + uptr old_heap_size = __asan_get_heap_size(); + for (int i = 0; i < 3; i++) { + // fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); + free(Ident(malloc(kLargeMallocSize))); + 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 @@ -455,33 +479,42 @@ static void DoLargeMallocForGetFreeBytesTestAndDie() { 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) { - 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; +#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. - 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; + // 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<<20, 357}; +static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357}; static const size_t kManyThreadsIterations = 250; -static const size_t kManyThreadsNumThreads = (__WORDSIZE == 32) ? 40 : 200; +static const size_t kManyThreadsNumThreads = + (SANITIZER_WORDSIZE == 32) ? 40 : 200; void *ManyThreadsWithStatsWorker(void *arg) { (void)arg; @@ -490,6 +523,8 @@ void *ManyThreadsWithStatsWorker(void *arg) { free(Ident(malloc(kManyThreadsMallocSizes[size_index]))); } } + // Just one large allocation. + free(Ident(malloc(1 << 20))); return 0; } @@ -498,11 +533,11 @@ TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) { pthread_t threads[kManyThreadsNumThreads]; before_test = __asan_get_current_allocated_bytes(); for (i = 0; i < kManyThreadsNumThreads; i++) { - pthread_create(&threads[i], 0, + PTHREAD_CREATE(&threads[i], 0, (void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i); } for (i = 0; i < kManyThreadsNumThreads; i++) { - pthread_join(threads[i], 0); + PTHREAD_JOIN(threads[i], 0); } after_test = __asan_get_current_allocated_bytes(); // ASan stats also reflect memory usage of internal ASan RTL structs, @@ -649,6 +684,45 @@ TEST(AddressSanitizerInterface, PoisoningStressTest) { } } +TEST(AddressSanitizerInterface, PoisonedRegion) { + size_t rz = 16; + for (size_t size = 1; size <= 64; size++) { + char *p = new char[size]; + uptr x = reinterpret_cast<uptr>(p); + for (size_t beg = 0; beg < size + rz; beg++) { + for (size_t end = beg; end < size + rz; end++) { + uptr first_poisoned = __asan_region_is_poisoned(x + beg, end - beg); + if (beg == end) { + EXPECT_FALSE(first_poisoned); + } else if (beg < size && end <= size) { + EXPECT_FALSE(first_poisoned); + } else if (beg >= size) { + EXPECT_EQ(x + beg, first_poisoned); + } else { + EXPECT_GT(end, size); + EXPECT_EQ(x + size, first_poisoned); + } + } + } + delete [] p; + } +} + +// This is a performance benchmark for manual runs. +// asan's memset interceptor calls mem_is_zero for the entire shadow region. +// the profile should look like this: +// 89.10% [.] __memset_sse2 +// 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) { + size_t size = 1 << 20; + char *x = new char[size]; + for (int i = 0; i < 100000; i++) + Ident(memset)(x, 0, size); + delete [] x; +} + static const char *kInvalidPoisonMessage = "invalid-poison-memory-range"; static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range"; @@ -670,20 +744,29 @@ TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) { } static void ErrorReportCallbackOneToZ(const char *report) { - write(2, "ABCDEF", 6); + int report_len = strlen(report); + ASSERT_EQ(6, write(2, "ABCDEF", 6)); + ASSERT_EQ(report_len, write(2, report, report_len)); + ASSERT_EQ(6, write(2, "ABCDEF", 6)); + _exit(1); } TEST(AddressSanitizerInterface, SetErrorReportCallbackTest) { __asan_set_error_report_callback(ErrorReportCallbackOneToZ); - EXPECT_DEATH(__asan_report_error(0, 0, 0, 0, true, 1), "ABCDEF"); + EXPECT_DEATH(__asan_report_error(0, 0, 0, 0, true, 1), + ASAN_PCRE_DOTALL "ABCDEF.*AddressSanitizer.*WRITE.*ABCDEF"); __asan_set_error_report_callback(NULL); } TEST(AddressSanitizerInterface, GetOwnershipStressTest) { std::vector<char *> pointers; std::vector<size_t> sizes; +#if ASAN_ALLOCATOR_VERSION == 1 const size_t kNumMallocs = - (__WORDSIZE <= 32 || ASAN_LOW_MEMORY) ? 1 << 10 : 1 << 14; + (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)); diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc index 8e967e929899..5fa65b2af5dc 100644 --- a/lib/asan/tests/asan_test.cc +++ b/lib/asan/tests/asan_test.cc @@ -1,4 +1,4 @@ -//===-- asan_test.cc ----------------------===// +//===-- asan_test.cc ------------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -19,17 +19,26 @@ #include <stdint.h> #include <setjmp.h> #include <assert.h> +#include <algorithm> + +#ifdef __linux__ +# include <sys/prctl.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <fcntl.h> +#include <unistd.h> +#endif #if defined(__i386__) || defined(__x86_64__) #include <emmintrin.h> #endif -#include "asan_test_config.h" #include "asan_test_utils.h" #ifndef __APPLE__ #include <malloc.h> #else +#include <malloc/malloc.h> #include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_* #include <CoreFoundation/CFString.h> #endif // __APPLE__ @@ -47,17 +56,8 @@ typedef uint16_t U2; typedef uint32_t U4; typedef uint64_t U8; -static const char *progname; static const int kPageSize = 4096; -// Simple stand-alone pseudorandom number generator. -// Current algorithm is ANSI C linear congruential PRNG. -static inline uint32_t my_rand(uint32_t* state) { - return (*state = *state * 1103515245 + 12345) >> 16; -} - -static uint32_t global_seed = 0; - const size_t kLargeMalloc = 1 << 24; template<typename T> @@ -66,7 +66,7 @@ NOINLINE void asan_write(T *a) { } NOINLINE void asan_write_sized_aligned(uint8_t *p, size_t size) { - EXPECT_EQ(0, ((uintptr_t)p % 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); @@ -130,6 +130,8 @@ NOINLINE void uaf_test(int size, int off) { TEST(AddressSanitizer, HasFeatureAddressSanitizerTest) { #if defined(__has_feature) && __has_feature(address_sanitizer) bool asan = 1; +#elif defined(__SANITIZE_ADDRESS__) + bool asan = 1; #else bool asan = 0; #endif @@ -141,29 +143,24 @@ TEST(AddressSanitizer, SimpleDeathTest) { } TEST(AddressSanitizer, VariousMallocsTest) { - // fprintf(stderr, "malloc:\n"); int *a = (int*)malloc(100 * sizeof(int)); a[50] = 0; free(a); - // fprintf(stderr, "realloc:\n"); int *r = (int*)malloc(10); r = (int*)realloc(r, 2000 * sizeof(int)); r[1000] = 0; free(r); - // fprintf(stderr, "operator new []\n"); int *b = new int[100]; b[50] = 0; delete [] b; - // fprintf(stderr, "operator new\n"); int *c = new int; *c = 0; delete c; -#if !defined(__APPLE__) && !defined(ANDROID) - // fprintf(stderr, "posix_memalign\n"); +#if !defined(__APPLE__) && !defined(ANDROID) && !defined(__ANDROID__) int *pm; int pm_res = posix_memalign((void**)&pm, kPageSize, kPageSize); EXPECT_EQ(0, pm_res); @@ -172,7 +169,7 @@ TEST(AddressSanitizer, VariousMallocsTest) { #if !defined(__APPLE__) int *ma = (int*)memalign(kPageSize, kPageSize); - EXPECT_EQ(0, (uintptr_t)ma % kPageSize); + EXPECT_EQ(0U, (uintptr_t)ma % kPageSize); ma[123] = 0; free(ma); #endif // __APPLE__ @@ -186,19 +183,19 @@ TEST(AddressSanitizer, CallocTest) { TEST(AddressSanitizer, VallocTest) { void *a = valloc(100); - EXPECT_EQ(0, (uintptr_t)a % kPageSize); + EXPECT_EQ(0U, (uintptr_t)a % kPageSize); free(a); } #ifndef __APPLE__ TEST(AddressSanitizer, PvallocTest) { char *a = (char*)pvalloc(kPageSize + 100); - EXPECT_EQ(0, (uintptr_t)a % kPageSize); + EXPECT_EQ(0U, (uintptr_t)a % kPageSize); a[kPageSize + 101] = 1; // we should not report an error here. free(a); a = (char*)pvalloc(0); // pvalloc(0) should allocate at least one page. - EXPECT_EQ(0, (uintptr_t)a % kPageSize); + EXPECT_EQ(0U, (uintptr_t)a % kPageSize); a[101] = 1; // we should not report an error here. free(a); } @@ -214,8 +211,8 @@ void *TSDWorker(void *test_key) { void TSDDestructor(void *tsd) { // Spawning a thread will check that the current thread id is not -1. pthread_t th; - pthread_create(&th, NULL, TSDWorker, NULL); - pthread_join(th, NULL); + PTHREAD_CREATE(&th, NULL, TSDWorker, NULL); + PTHREAD_JOIN(th, NULL); } // This tests triggers the thread-specific data destruction fiasco which occurs @@ -229,8 +226,8 @@ TEST(AddressSanitizer, DISABLED_TSDTest) { pthread_t th; pthread_key_t test_key; pthread_key_create(&test_key, TSDDestructor); - pthread_create(&th, NULL, TSDWorker, &test_key); - pthread_join(th, NULL); + PTHREAD_CREATE(&th, NULL, TSDWorker, &test_key); + PTHREAD_JOIN(th, NULL); pthread_key_delete(test_key); } @@ -245,10 +242,10 @@ void OOBTest() { EXPECT_DEATH(oob_test<T>(size, i), expected_str); } - for (int i = 0; i < size - sizeof(T) + 1; i++) + for (int i = 0; i < (int)(size - sizeof(T) + 1); i++) oob_test<T>(size, i); - for (int i = size - sizeof(T) + 1; i <= size + 3 * sizeof(T); 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; @@ -304,8 +301,20 @@ TEST(AddressSanitizer, OOBRightTest) { } } +#if ASAN_ALLOCATOR_VERSION == 2 // Broken with the asan_allocator1 +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; + } +} +#endif // ASAN_ALLOCATOR_VERSION == 2 + TEST(AddressSanitizer, UAF_char) { - const char *uaf_string = "AddressSanitizer.*heap-use-after-free"; + const char *uaf_string = "AddressSanitizer:.*heap-use-after-free"; EXPECT_DEATH(uaf_test<U1>(1, 0), uaf_string); EXPECT_DEATH(uaf_test<U1>(10, 0), uaf_string); EXPECT_DEATH(uaf_test<U1>(10, 10), uaf_string); @@ -335,7 +344,7 @@ TEST(AddressSanitizer, BitFieldPositiveTest) { EXPECT_DEATH(x->bf2 = 0, "use-after-free"); EXPECT_DEATH(x->bf3 = 0, "use-after-free"); EXPECT_DEATH(x->bf4 = 0, "use-after-free"); -}; +} struct StructWithBitFields_8_24 { int a:8; @@ -350,7 +359,7 @@ TEST(AddressSanitizer, BitFieldNegativeTest) { } TEST(AddressSanitizer, OutOfMemoryTest) { - size_t size = __WORDSIZE == 64 ? (size_t)(1ULL << 48) : (0xf0000000); + size_t size = SANITIZER_WORDSIZE == 64 ? (size_t)(1ULL << 48) : (0xf0000000); EXPECT_EQ(0, realloc(0, size)); EXPECT_EQ(0, realloc(0, ~Ident(0))); EXPECT_EQ(0, malloc(size)); @@ -360,28 +369,61 @@ TEST(AddressSanitizer, OutOfMemoryTest) { } #if ASAN_NEEDS_SEGV +namespace { + +const char kUnknownCrash[] = "AddressSanitizer: SEGV on unknown address"; +const char kOverriddenHandler[] = "ASan signal handler has been overridden\n"; + TEST(AddressSanitizer, WildAddressTest) { char *c = (char*)0x123; - EXPECT_DEATH(*c = 0, "AddressSanitizer crashed on unknown address"); + EXPECT_DEATH(*c = 0, kUnknownCrash); +} + +void my_sigaction_sighandler(int, siginfo_t*, void*) { + fprintf(stderr, kOverriddenHandler); + exit(1); } + +void my_signal_sighandler(int signum) { + fprintf(stderr, kOverriddenHandler); + exit(1); +} + +TEST(AddressSanitizer, SignalTest) { + struct sigaction sigact; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_sigaction = my_sigaction_sighandler; + sigact.sa_flags = SA_SIGINFO; + // ASan should silently ignore sigaction()... + EXPECT_EQ(0, sigaction(SIGSEGV, &sigact, 0)); +#ifdef __APPLE__ + EXPECT_EQ(0, sigaction(SIGBUS, &sigact, 0)); +#endif + char *c = (char*)0x123; + EXPECT_DEATH(*c = 0, kUnknownCrash); + // ... and signal(). + EXPECT_EQ(0, signal(SIGSEGV, my_signal_sighandler)); + EXPECT_DEATH(*c = 0, kUnknownCrash); +} +} // namespace #endif static void MallocStress(size_t n) { - uint32_t seed = my_rand(&global_seed); + uint32_t seed = my_rand(); for (size_t iter = 0; iter < 10; iter++) { vector<void *> vec; for (size_t i = 0; i < n; i++) { if ((i % 3) == 0) { if (vec.empty()) continue; - size_t idx = my_rand(&seed) % vec.size(); + size_t idx = my_rand_r(&seed) % vec.size(); void *ptr = vec[idx]; vec[idx] = vec.back(); vec.pop_back(); free_aaa(ptr); } else { - size_t size = my_rand(&seed) % 1000 + 1; + size_t size = my_rand_r(&seed) % 1000 + 1; #ifndef __APPLE__ - size_t alignment = 1 << (my_rand(&seed) % 7 + 3); + size_t alignment = 1 << (my_rand_r(&seed) % 7 + 3); char *ptr = (char*)memalign_aaa(alignment, size); #else char *ptr = (char*) malloc_aaa(size); @@ -421,24 +463,42 @@ TEST(AddressSanitizer, HugeMallocTest) { // 32-bit Mac 10.7 gives even less (< 1G). // (the libSystem malloc() allows allocating up to 2300 megabytes without // ASan). - size_t n_megs = __WORDSIZE == 32 ? 500 : 4100; + size_t n_megs = SANITIZER_WORDSIZE == 32 ? 500 : 4100; #else - size_t n_megs = __WORDSIZE == 32 ? 2600 : 4100; + size_t n_megs = SANITIZER_WORDSIZE == 32 ? 2600 : 4100; #endif TestLargeMalloc(n_megs << 20); } #endif +#ifndef __APPLE__ +void MemalignRun(size_t align, size_t size, int idx) { + char *p = (char *)memalign(align, size); + Ident(p)[idx] = 0; + free(p); +} + +TEST(AddressSanitizer, memalign) { + for (int align = 16; align <= (1 << 23); align *= 2) { + size_t size = align * 5; + EXPECT_DEATH(MemalignRun(align, size, -1), + "is located 1 bytes to the left"); + EXPECT_DEATH(MemalignRun(align, size, size + 1), + "is located 1 bytes to the right"); + } +} +#endif + 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, + PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))MallocStress, (void*)kNumIterations); } for (int i = 0; i < kNumThreads; i++) { - pthread_join(t[i], 0); + PTHREAD_JOIN(t[i], 0); } } @@ -452,13 +512,14 @@ void *ManyThreadsWorker(void *a) { } TEST(AddressSanitizer, ManyThreadsTest) { - const size_t kNumThreads = __WORDSIZE == 32 ? 30 : 1000; + const size_t kNumThreads = + (SANITIZER_WORDSIZE == 32 || ASAN_AVOID_EXPENSIVE_TESTS) ? 30 : 1000; pthread_t t[kNumThreads]; for (size_t i = 0; i < kNumThreads; i++) { - pthread_create(&t[i], 0, (void* (*)(void *x))ManyThreadsWorker, (void*)i); + PTHREAD_CREATE(&t[i], 0, ManyThreadsWorker, (void*)i); } for (size_t i = 0; i < kNumThreads; i++) { - pthread_join(t[i], 0); + PTHREAD_JOIN(t[i], 0); } } @@ -468,20 +529,20 @@ TEST(AddressSanitizer, ReallocTest) { ptr[3] = 3; for (int i = 0; i < 10000; i++) { ptr = (int*)realloc(ptr, - (my_rand(&global_seed) % 1000 + kMinElem) * sizeof(int)); + (my_rand() % 1000 + kMinElem) * sizeof(int)); EXPECT_EQ(3, ptr[3]); } } #ifndef __APPLE__ static const char *kMallocUsableSizeErrorMsg = - "AddressSanitizer attempting to call malloc_usable_size()"; + "AddressSanitizer: attempting to call malloc_usable_size()"; TEST(AddressSanitizer, MallocUsableSizeTest) { const size_t kArraySize = 100; char *array = Ident((char*)malloc(kArraySize)); int *int_ptr = Ident(new int); - EXPECT_EQ(0, malloc_usable_size(NULL)); + EXPECT_EQ(0U, malloc_usable_size(NULL)); EXPECT_EQ(kArraySize, malloc_usable_size(array)); EXPECT_EQ(sizeof(int), malloc_usable_size(int_ptr)); EXPECT_DEATH(malloc_usable_size((void*)0x123), kMallocUsableSizeErrorMsg); @@ -501,7 +562,7 @@ void WrongFree() { TEST(AddressSanitizer, WrongFreeTest) { EXPECT_DEATH(WrongFree(), - "ERROR: AddressSanitizer attempting free.*not malloc"); + "ERROR: AddressSanitizer: attempting free.*not malloc"); } void DoubleFree() { @@ -515,7 +576,7 @@ void DoubleFree() { TEST(AddressSanitizer, DoubleFreeTest) { EXPECT_DEATH(DoubleFree(), ASAN_PCRE_DOTALL - "ERROR: AddressSanitizer attempting double-free" + "ERROR: AddressSanitizer: attempting double-free" ".*is located 0 bytes inside of 400-byte region" ".*freed by thread T0 here" ".*previously allocated by thread T0 here"); @@ -610,6 +671,17 @@ NOINLINE void LongJmpFunc1(jmp_buf buf) { longjmp(buf, 1); } +NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) { + // create three red zones for these two stack objects. + int a; + int b; + + int *A = Ident(&a); + int *B = Ident(&b); + *A = *B; + __builtin_longjmp((void**)buf, 1); +} + NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) { // create three red zones for these two stack objects. int a; @@ -650,6 +722,17 @@ TEST(AddressSanitizer, LongJmpTest) { } } +#if not defined(__ANDROID__) +TEST(AddressSanitizer, BuiltinLongJmpTest) { + static jmp_buf buf; + if (!__builtin_setjmp((void**)buf)) { + BuiltinLongJmpFunc1(buf); + } else { + TouchStackFunc(); + } +} +#endif // not defined(__ANDROID__) + TEST(AddressSanitizer, UnderscopeLongJmpTest) { static jmp_buf buf; if (!_setjmp(buf)) { @@ -683,7 +766,7 @@ NOINLINE void ThrowFunc() { TEST(AddressSanitizer, CxxExceptionTest) { if (ASAN_UAR) return; // TODO(kcc): this test crashes on 32-bit for some reason... - if (__WORDSIZE == 32) return; + if (SANITIZER_WORDSIZE == 32) return; try { ThrowFunc(); } catch(...) {} @@ -710,10 +793,10 @@ void *ThreadStackReuseFunc2(void *unused) { TEST(AddressSanitizer, ThreadStackReuseTest) { pthread_t t; - pthread_create(&t, 0, ThreadStackReuseFunc1, 0); - pthread_join(t, 0); - pthread_create(&t, 0, ThreadStackReuseFunc2, 0); - pthread_join(t, 0); + PTHREAD_CREATE(&t, 0, ThreadStackReuseFunc1, 0); + PTHREAD_JOIN(t, 0); + PTHREAD_CREATE(&t, 0, ThreadStackReuseFunc2, 0); + PTHREAD_JOIN(t, 0); } #if defined(__i386__) || defined(__x86_64__) @@ -725,7 +808,7 @@ TEST(AddressSanitizer, Store128Test) { assert(((uintptr_t)p % 16) == 0); __m128i value_wide = _mm_set1_epi16(0x1234); EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide), - "AddressSanitizer heap-buffer-overflow"); + "AddressSanitizer: heap-buffer-overflow"); EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide), "WRITE of size 16"); EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide), @@ -734,14 +817,39 @@ TEST(AddressSanitizer, Store128Test) { } #endif -static string RightOOBErrorMessage(int oob_distance) { +static string RightOOBErrorMessage(int oob_distance, bool is_write) { assert(oob_distance >= 0); char expected_str[100]; - sprintf(expected_str, "located %d bytes to the right", oob_distance); + sprintf(expected_str, ASAN_PCRE_DOTALL "%s.*located %d bytes to the right", + is_write ? "WRITE" : "READ", oob_distance); return string(expected_str); } -static string LeftOOBErrorMessage(int oob_distance) { +static string RightOOBWriteMessage(int oob_distance) { + return RightOOBErrorMessage(oob_distance, /*is_write*/true); +} + +static string RightOOBReadMessage(int oob_distance) { + return RightOOBErrorMessage(oob_distance, /*is_write*/false); +} + +static string LeftOOBErrorMessage(int oob_distance, bool is_write) { + assert(oob_distance > 0); + char expected_str[100]; + sprintf(expected_str, ASAN_PCRE_DOTALL "%s.*located %d bytes to the left", + is_write ? "WRITE" : "READ", oob_distance); + return string(expected_str); +} + +static string LeftOOBWriteMessage(int oob_distance) { + return LeftOOBErrorMessage(oob_distance, /*is_write*/true); +} + +static string LeftOOBReadMessage(int oob_distance) { + return LeftOOBErrorMessage(oob_distance, /*is_write*/false); +} + +static string LeftOOBAccessMessage(int oob_distance) { assert(oob_distance > 0); char expected_str[100]; sprintf(expected_str, "located %d bytes to the left", oob_distance); @@ -755,44 +863,48 @@ void MemSetOOBTestTemplate(size_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(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); + 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), - RightOOBErrorMessage(0)); - EXPECT_DEATH(memset((char*)(array + length) - 1, element, 6), - RightOOBErrorMessage(4)); - EXPECT_DEATH(memset(array + 1, element, size + sizeof(T)), - RightOOBErrorMessage(2 * sizeof(T) - 1)); + 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), - RightOOBErrorMessage(sizeof(T))); + 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), - LeftOOBErrorMessage(1)); - EXPECT_DEATH(memset((char*)array - 5, 0, 6), - LeftOOBErrorMessage(5)); - EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)), - LeftOOBErrorMessage(5 * sizeof(T))); + 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)), - LeftOOBErrorMessage(2 * sizeof(T))); + 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), - LeftOOBErrorMessage(2)); + EXPECT_DEATH(MEMSET((char*)array - 2, element, size + 4), + LeftOOBWriteMessage(2)); free(array); } @@ -804,6 +916,51 @@ TEST(AddressSanitizer, MemSetOOBTest) { // 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) { @@ -827,27 +984,27 @@ void MemTransferOOBTestTemplate(size_t length) { // try to change mem to the right of dest EXPECT_DEATH(M::transfer(dest + 1, src, size), - RightOOBErrorMessage(sizeof(T) - 1)); + RightOOBWriteMessage(0)); EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5), - RightOOBErrorMessage(3)); + RightOOBWriteMessage(0)); // try to change mem to the left of dest EXPECT_DEATH(M::transfer(dest - 2, src, size), - LeftOOBErrorMessage(2 * sizeof(T))); + LeftOOBWriteMessage(2 * sizeof(T))); EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4), - LeftOOBErrorMessage(3)); + LeftOOBWriteMessage(3)); // try to access mem to the right of src EXPECT_DEATH(M::transfer(dest, src + 2, size), - RightOOBErrorMessage(2 * sizeof(T) - 1)); + RightOOBReadMessage(0)); EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6), - RightOOBErrorMessage(2)); + RightOOBReadMessage(0)); // try to access mem to the left of src EXPECT_DEATH(M::transfer(dest, src - 1, size), - LeftOOBErrorMessage(sizeof(T))); + LeftOOBReadMessage(sizeof(T))); EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7), - LeftOOBErrorMessage(6)); + LeftOOBReadMessage(6)); // Generally we don't need to test cases where both accessing src and writing // to dest address to poisoned memory. @@ -856,10 +1013,10 @@ void MemTransferOOBTestTemplate(size_t length) { 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), - LeftOOBErrorMessage(sizeof(T))); + LeftOOBWriteMessage(sizeof(T))); // try to access mem to both sides of src EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2), - LeftOOBErrorMessage(2 * sizeof(T))); + LeftOOBReadMessage(2 * sizeof(T))); free(src); free(dest); @@ -870,7 +1027,7 @@ void MemTransferOOBTestTemplate(size_t length) { class MemCpyWrapper { public: static void* transfer(void *to, const void *from, size_t size) { - return memcpy(to, from, size); + return Ident(memcpy)(to, from, size); } }; TEST(AddressSanitizer, MemCpyOOBTest) { @@ -881,7 +1038,7 @@ TEST(AddressSanitizer, MemCpyOOBTest) { class MemMoveWrapper { public: static void* transfer(void *to, const void *from, size_t size) { - return memmove(to, from, size); + return Ident(memmove)(to, from, size); } }; TEST(AddressSanitizer, MemMoveOOBTest) { @@ -902,21 +1059,21 @@ void StrLenOOBTestTemplate(char *str, size_t length, bool is_global) { // Normal strlen calls EXPECT_EQ(strlen(str), length); if (length > 0) { - EXPECT_EQ(strlen(str + 1), length - 1); - EXPECT_EQ(strlen(str + 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)), LeftOOBErrorMessage(1)); - EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBErrorMessage(5)); + EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBReadMessage(5)); } - EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBErrorMessage(0)); + 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)), RightOOBErrorMessage(0)); - EXPECT_DEATH(Ident(strlen(str + length)), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(strlen(str)), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(strlen(str + length)), RightOOBReadMessage(0)); // Restore terminator str[length] = 0; } @@ -925,7 +1082,8 @@ TEST(AddressSanitizer, StrLenOOBTest) { size_t length = Ident(10); char *heap_string = Ident((char*)malloc(length + 1)); char stack_string[10 + 1]; - for (int i = 0; i < length; i++) { + break_optimization(&stack_string); + for (size_t i = 0; i < length; i++) { heap_string[i] = 'a'; stack_string[i] = 'b'; } @@ -959,11 +1117,11 @@ TEST(AddressSanitizer, StrNLenOOBTest) { str[size - 1] = '\0'; Ident(strnlen(str, 2 * size)); // Argument points to not allocated memory. - EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBErrorMessage(1)); - EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBReadMessage(0)); // Overwrite the terminating '\0' and hit unallocated memory. str[size - 1] = 'z'; - EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBReadMessage(0)); free(str); } #endif @@ -979,11 +1137,11 @@ TEST(AddressSanitizer, StrDupOOBTest) { new_str = strdup(str + size - 1); free(new_str); // Argument points to not allocated memory. - EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBErrorMessage(1)); - EXPECT_DEATH(Ident(strdup(str + size)), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(strdup(str + size)), RightOOBReadMessage(0)); // Overwrite the terminating '\0' and hit unallocated memory. str[size - 1] = 'z'; - EXPECT_DEATH(Ident(strdup(str)), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(strdup(str)), RightOOBReadMessage(0)); free(str); } @@ -997,15 +1155,15 @@ TEST(AddressSanitizer, StrCpyOOBTest) { strcpy(to, from); strcpy(to + to_size - from_size, from); // Length of "from" is too small. - EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBWriteMessage(0)); // "to" or "from" points to not allocated memory. - EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBErrorMessage(1)); - EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBErrorMessage(1)); - EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBErrorMessage(0)); - EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBWriteMessage(1)); + EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBWriteMessage(0)); // Overwrite the terminating '\0' character and hit unallocated memory. from[from_size - 1] = '!'; - EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBReadMessage(0)); free(to); free(from); } @@ -1027,31 +1185,37 @@ TEST(AddressSanitizer, StrNCpyOOBTest) { strncpy(to + to_size - 1, from, 1); // One of {to, from} points to not allocated memory EXPECT_DEATH(Ident(strncpy(to, from - 1, from_size)), - LeftOOBErrorMessage(1)); + LeftOOBReadMessage(1)); EXPECT_DEATH(Ident(strncpy(to - 1, from, from_size)), - LeftOOBErrorMessage(1)); + LeftOOBWriteMessage(1)); EXPECT_DEATH(Ident(strncpy(to, from + from_size, 1)), - RightOOBErrorMessage(0)); + RightOOBReadMessage(0)); EXPECT_DEATH(Ident(strncpy(to + to_size, from, 1)), - RightOOBErrorMessage(0)); + RightOOBWriteMessage(0)); // Length of "to" is too small EXPECT_DEATH(Ident(strncpy(to + to_size - from_size + 1, from, from_size)), - RightOOBErrorMessage(0)); + RightOOBWriteMessage(0)); EXPECT_DEATH(Ident(strncpy(to + 1, from, to_size)), - RightOOBErrorMessage(0)); + RightOOBWriteMessage(0)); // Overwrite terminator in from from[from_size - 1] = '!'; // normal strncpy call strncpy(to, from, from_size); // Length of "from" is too small EXPECT_DEATH(Ident(strncpy(to, from, to_size)), - RightOOBErrorMessage(0)); + RightOOBReadMessage(0)); free(to); free(from); } -typedef char*(*PointerToStrChr)(const char*, int); -void RunStrChrTest(PointerToStrChr StrChr) { +// Users may have different definitions of "strchr" and "index", so provide +// function pointer typedefs and overload RunStrChrTest implementation. +// We can't use macro for RunStrChrTest body here, as this macro would +// confuse EXPECT_DEATH gtest macro. +typedef char*(*PointerToStrChr1)(const char*, int); +typedef char*(*PointerToStrChr2)(char*, int); + +USED static void RunStrChrTest(PointerToStrChr1 StrChr) { size_t size = Ident(100); char *str = MallocAndMemsetString(size); str[10] = 'q'; @@ -1060,13 +1224,30 @@ void RunStrChrTest(PointerToStrChr StrChr) { EXPECT_EQ(str + 10, StrChr(str, 'q')); EXPECT_EQ(NULL, StrChr(str, 'a')); // StrChr argument points to not allocated memory. - EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBErrorMessage(1)); - EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0)); // Overwrite the terminator and hit not allocated memory. str[11] = 'z'; - EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0)); free(str); } +USED static void RunStrChrTest(PointerToStrChr2 StrChr) { + size_t size = Ident(100); + char *str = MallocAndMemsetString(size); + str[10] = 'q'; + str[11] = '\0'; + EXPECT_EQ(str, StrChr(str, 'z')); + EXPECT_EQ(str + 10, StrChr(str, 'q')); + EXPECT_EQ(NULL, StrChr(str, 'a')); + // StrChr argument points to not allocated memory. + EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0)); + // Overwrite the terminator and hit not allocated memory. + str[11] = 'z'; + EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0)); + free(str); +} + TEST(AddressSanitizer, StrChrAndIndexOOBTest) { RunStrChrTest(&strchr); RunStrChrTest(&index); @@ -1124,8 +1305,9 @@ TEST(AddressSanitizer, StrCmpAndFriendsLogicTest) { typedef int(*PointerToStrCmp)(const char*, const char*); void RunStrCmpTest(PointerToStrCmp StrCmp) { size_t size = Ident(100); - char *s1 = MallocAndMemsetString(size); - char *s2 = MallocAndMemsetString(size); + int fill = 'o'; + char *s1 = MallocAndMemsetString(size, fill); + char *s2 = MallocAndMemsetString(size, fill); s1[size - 1] = '\0'; s2[size - 1] = '\0'; // Normal StrCmp calls @@ -1136,14 +1318,14 @@ void RunStrCmpTest(PointerToStrCmp StrCmp) { s2[size - 1] = 'x'; Ident(StrCmp(s1, s2)); // One of arguments points to not allocated memory. - EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBErrorMessage(1)); - EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBErrorMessage(1)); - EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBErrorMessage(0)); - EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBReadMessage(0)); // Hit unallocated memory and die. - s2[size - 1] = 'z'; - EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBErrorMessage(0)); - EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBErrorMessage(0)); + s1[size - 1] = fill; + EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBReadMessage(0)); free(s1); free(s2); } @@ -1172,13 +1354,13 @@ void RunStrNCmpTest(PointerToStrNCmp StrNCmp) { Ident(StrNCmp(s1 - 1, s2 - 1, 0)); Ident(StrNCmp(s1 + size - 1, s2 + size - 1, 1)); // One of arguments points to not allocated memory. - EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBErrorMessage(1)); - EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBErrorMessage(1)); - EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBErrorMessage(0)); - EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBReadMessage(0)); // Hit unallocated memory and die. - EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBErrorMessage(0)); - EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0)); free(s1); free(s2); } @@ -1200,22 +1382,23 @@ TEST(AddressSanitizer, MemCmpOOBTest) { 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), LeftOOBErrorMessage(1)); - EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBErrorMessage(1)); - EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBErrorMessage(0)); - EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBErrorMessage(0)); + 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), RightOOBErrorMessage(0)); - EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBErrorMessage(0)); + 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), RightOOBErrorMessage(0)); + EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0)); free(s1); free(s2); } TEST(AddressSanitizer, StrCatOOBTest) { + // strcat() reads strlen(to) bytes from |to| before concatenating. size_t to_size = Ident(100); char *to = MallocAndMemsetString(to_size); to[0] = '\0'; @@ -1226,24 +1409,25 @@ TEST(AddressSanitizer, StrCatOOBTest) { strcat(to, from); strcat(to, from); strcat(to + from_size, from + from_size - 2); - // Catenate empty string is not always an error. - strcat(to - 1, from + from_size - 1); + // Passing an invalid pointer is an error even when concatenating an empty + // string. + EXPECT_DEATH(strcat(to - 1, from + from_size - 1), LeftOOBAccessMessage(1)); // One of arguments points to not allocated memory. - EXPECT_DEATH(strcat(to - 1, from), LeftOOBErrorMessage(1)); - EXPECT_DEATH(strcat(to, from - 1), LeftOOBErrorMessage(1)); - EXPECT_DEATH(strcat(to + to_size, from), RightOOBErrorMessage(0)); - EXPECT_DEATH(strcat(to, from + from_size), RightOOBErrorMessage(0)); + EXPECT_DEATH(strcat(to - 1, from), LeftOOBAccessMessage(1)); + EXPECT_DEATH(strcat(to, from - 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(strcat(to + to_size, from), RightOOBWriteMessage(0)); + EXPECT_DEATH(strcat(to, from + from_size), RightOOBReadMessage(0)); // "from" is not zero-terminated. from[from_size - 1] = 'z'; - EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0)); + EXPECT_DEATH(strcat(to, from), RightOOBReadMessage(0)); from[from_size - 1] = '\0'; // "to" is not zero-terminated. memset(to, 'z', to_size); - EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0)); + EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0)); // "to" is too short to fit "from". to[to_size - from_size + 1] = '\0'; - EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0)); + EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0)); // length of "to" is just enough. strcat(to, from + 1); @@ -1252,6 +1436,7 @@ TEST(AddressSanitizer, StrCatOOBTest) { } TEST(AddressSanitizer, StrNCatOOBTest) { + // strncat() reads strlen(to) bytes from |to| before concatenating. size_t to_size = Ident(100); char *to = MallocAndMemsetString(to_size); to[0] = '\0'; @@ -1262,26 +1447,26 @@ TEST(AddressSanitizer, StrNCatOOBTest) { strncat(to, from, from_size); from[from_size - 1] = '\0'; strncat(to, from, 2 * from_size); - // Catenating empty string is not an error. - strncat(to - 1, from, 0); + // Catenating empty string with an invalid string is still an error. + EXPECT_DEATH(strncat(to - 1, from, 0), LeftOOBAccessMessage(1)); strncat(to, from + from_size - 1, 10); // One of arguments points to not allocated memory. - EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBErrorMessage(1)); - EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBErrorMessage(1)); - EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBErrorMessage(0)); - EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBErrorMessage(0)); + EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1)); + EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBReadMessage(1)); + EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBWriteMessage(0)); + EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0)); memset(from, 'z', from_size); memset(to, 'z', to_size); to[0] = '\0'; // "from" is too short. - EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBErrorMessage(0)); + EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0)); // "to" is not zero-terminated. - EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBErrorMessage(0)); + EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBWriteMessage(0)); // "to" is too short to fit "from". to[0] = 'z'; to[to_size - from_size + 1] = '\0'; - EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBErrorMessage(0)); + EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBWriteMessage(0)); // "to" is just enough. strncat(to, from, from_size - 2); @@ -1336,7 +1521,7 @@ TEST(AddressSanitizer, StrArgsOverlapTest) { str[10] = '\0'; str[20] = '\0'; strcat(str, str + 10); - strcat(str, str + 11); + EXPECT_DEATH(strcat(str, str + 11), OverlapErrorMessage("strcat")); str[10] = '\0'; strcat(str + 11, str); EXPECT_DEATH(strcat(str, str + 9), OverlapErrorMessage("strcat")); @@ -1347,7 +1532,7 @@ TEST(AddressSanitizer, StrArgsOverlapTest) { memset(str, 'z', size); str[10] = '\0'; strncat(str, str + 10, 10); // from is empty - strnca |