Import compiler-rt r172839.
This commit is contained in:
parent
37dfff0574
commit
58aabf08b7
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/vendor/compiler-rt/dist/; revision=245614 svn path=/vendor/compiler-rt/compiler-rt-r172839/; revision=245615; tag=vendor/compiler-rt/compiler-rt-r172839
4
.arcconfig
Normal file
4
.arcconfig
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"project_id" : "compiler-rt",
|
||||
"conduit_uri" : "http://llvm-reviews.chandlerc.com/"
|
||||
}
|
150
CMakeLists.txt
150
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}")
|
||||
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}")
|
||||
# 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)
|
||||
|
@ -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
|
||||
|
40
Makefile
40
Makefile
@ -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
|
||||
|
@ -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__ */
|
||||
|
@ -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__ */
|
||||
|
49
cmake/Modules/AddCompilerRT.cmake
Normal file
49
cmake/Modules/AddCompilerRT.cmake
Normal file
@ -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()
|
16
cmake/Modules/CompilerRTCompile.cmake
Normal file
16
cmake/Modules/CompilerRTCompile.cmake
Normal file
@ -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()
|
14
cmake/Modules/CompilerRTLink.cmake
Normal file
14
cmake/Modules/CompilerRTLink.cmake
Normal file
@ -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()
|
17
cmake/Modules/CompilerRTUtils.cmake
Normal file
17
cmake/Modules/CompilerRTUtils.cmake
Normal file
@ -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()
|
||||
|
@ -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
|
@ -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
|
124
include/sanitizer/msan_interface.h
Normal file
124
include/sanitizer/msan_interface.h
Normal file
@ -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
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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)))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
$<TARGET_OBJECTS:RTInterception.osx>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.osx>
|
||||
)
|
||||
set_target_compile_flags(clang_rt.asan-x86_64
|
||||
${ASAN_CFLAGS}
|
||||
${TARGET_X86_64_CFLAGS}
|
||||
)
|
||||
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)
|
||||
|
@ -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=%)
|
||||
|
@ -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-*
|
@ -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
|
||||
|
@ -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,51 +161,24 @@ 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;
|
||||
}
|
||||
|
||||
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 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));
|
||||
}
|
||||
};
|
||||
|
||||
uptr AsanChunkView::Beg() { return chunk_->Beg(); }
|
||||
uptr AsanChunkView::End() { return Beg() + UsedSize(); }
|
||||
uptr AsanChunkView::UsedSize() { return chunk_->used_size; }
|
||||
uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
|
||||
uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
|
||||
|
||||
void AsanChunkView::GetAllocStack(StackTrace *stack) {
|
||||
StackTrace::UncompressStack(stack, chunk_->compressed_alloc_stack(),
|
||||
chunk_->compressed_alloc_stack_size());
|
||||
}
|
||||
|
||||
void AsanChunkView::GetFreeStack(StackTrace *stack) {
|
||||
StackTrace::UncompressStack(stack, chunk_->compressed_free_stack(),
|
||||
chunk_->compressed_free_stack_size());
|
||||
}
|
||||
|
||||
static AsanChunk *PtrToChunk(uptr ptr) {
|
||||
AsanChunk *m = (AsanChunk*)(ptr - REDZONE);
|
||||
if (m->chunk_state == CHUNK_MEMALIGN) {
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
710
lib/asan/asan_allocator2.cc
Normal file
710
lib/asan/asan_allocator2.cc
Normal file
@ -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
|
182
lib/asan/asan_fake_stack.cc
Normal file
182
lib/asan/asan_fake_stack.cc
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
260
lib/asan/asan_intercepted_functions.h
Normal file
260
lib/asan/asan_intercepted_functions.h
Normal file
@ -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
|
@ -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,18 +690,22 @@ 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);
|
||||
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,70 +101,36 @@ 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);
|
||||
#endif
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
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);
|
||||
void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
|
||||
#if defined(__arm__) || \
|
||||
defined(__powerpc__) || defined(__powerpc64__) || \
|
||||
defined(__sparc__)
|
||||
fast = false;
|
||||
#endif
|
||||
if (!fast)
|
||||
return stack->SlowUnwindStack(pc, max_s);
|
||||
stack->size = 0;
|
||||
stack->trace[0] = pc;
|
||||
if (max_s > 1) {
|
||||
stack->max_size = max_s;
|
||||
if (!asan_inited) return;
|
||||
if (AsanThread *t = asanThreadRegistry().GetCurrent())
|
||||
stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom());
|
||||
}
|
||||
}
|
||||
|
||||
#if !ASAN_ANDROID
|
||||
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;
|
||||
}
|
||||
#else
|
||||
void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // __linux__
|
||||
|
@ -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
|
@ -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_);
|
||||
}
|
||||
|
||||
void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
|
||||
size = 0;
|
||||
trace[0] = pc;
|
||||
if ((max_s) > 1) {
|
||||
max_size = max_s;
|
||||
FastUnwindStack(pc, bp);
|
||||
// For the first program thread, we can't replace the allocator before
|
||||
// __CFInitialize() has been called. If it hasn't, we'll call
|
||||
// MaybeReplaceCFAllocator() later on this thread.
|
||||
// For other threads __CFInitialize() has been called before their creation.
|
||||
// See also asan_malloc_mac.cc.
|
||||
if (((CFRuntimeBase*)kCFAllocatorSystemDefault)->_cfisa) {
|
||||
MaybeReplaceCFAllocator();
|
||||
}
|
||||
}
|
||||
|
||||
void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
|
||||
(void)fast;
|
||||
stack->size = 0;
|
||||
stack->trace[0] = pc;
|
||||
if ((max_s) > 1) {
|
||||
stack->max_size = max_s;
|
||||
if (!asan_inited) return;
|
||||
if (AsanThread *t = asanThreadRegistry().GetCurrent())
|
||||
stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom());
|
||||
}
|
||||
}
|
||||
|
||||
void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
// The range of pages to be used for escape islands.
|
||||
// TODO(glider): instead of mapping a fixed range we must find a range of
|
||||
// unmapped pages in vmmap and take them.
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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__
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
// 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);
|
||||
|
||||
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); }
|
||||
|
||||
#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); }
|
||||
|
||||
#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
|
||||
|
||||
#define OPERATOR_DELETE_BODY \
|
||||
GET_STACK_TRACE_HERE_FOR_FREE(ptr);\
|
||||
asan_free(ptr, &stack);
|
||||
|
||||
void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; }
|
||||
void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; }
|
||||
void operator delete(void *ptr, std::nothrow_t const&) throw()
|
||||
{ OPERATOR_DELETE_BODY; }
|
||||
void operator delete[](void *ptr, std::nothrow_t const&) throw()
|
||||
{ OPERATOR_DELETE_BODY; }
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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
|
681
lib/asan/asan_report.cc
Normal file
681
lib/asan/asan_report.cc
Normal file
@ -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
|
57
lib/asan/asan_report.h
Normal file
57
lib/asan/asan_report.h
Normal file
@ -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
|
@ -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");
|
||||
if (res != (void*)beg) {
|
||||
Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
|
||||
"Perhaps you're using ulimit -v\n", size);
|
||||
Abort();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 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;
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
uptr AsanStackTrace::GetCurrentPc() {
|
||||
return GET_CALLER_PC();
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
void PrintStack(StackTrace *stack) {
|
||||
stack->PrintStack(stack->trace, stack->size, flags()->symbolize,
|
||||
flags()->strip_path_prefix, MaybeCallAsanSymbolize);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
// ------------------ Interface -------------- {{{1
|
||||
|
||||
// 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;
|
||||
}
|
||||
#endif
|
||||
|
@ -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_HERE_FOR_MALLOC \
|
||||
GET_STACK_TRACE_HERE(flags()->malloc_context_size)
|
||||
#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_HERE_FOR_FREE(ptr) \
|
||||
GET_STACK_TRACE_HERE(flags()->malloc_context_size)
|
||||
#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_MALLOC \
|
||||
GET_STACK_TRACE(flags()->malloc_context_size, \
|
||||
flags()->fast_unwind_on_malloc)
|
||||
|
||||
#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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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) {
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
@ -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,13 +49,72 @@ 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];
|
||||
stack->size = cs_ret - offset;
|
||||
for (uptr i = 0; i < stack->size; i++)
|
||||
stack->trace[i] = (uptr)tmp[i + offset];
|
||||
}
|
||||
|
||||
bool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size) {
|
||||
ScopedLock lock(&dbghelp_lock);
|
||||
// ---------------------- TSD ---------------- {{{1
|
||||
static bool tsd_key_inited = false;
|
||||
|
||||
static __declspec(thread) void *fake_tsd = 0;
|
||||
|
||||
void AsanTSDInit(void (*destructor)(void *tsd)) {
|
||||
// FIXME: we're ignoring the destructor for now.
|
||||
tsd_key_inited = true;
|
||||
}
|
||||
|
||||
void *AsanTSDGet() {
|
||||
CHECK(tsd_key_inited);
|
||||
return fake_tsd;
|
||||
}
|
||||
|
||||
void AsanTSDSet(void *tsd) {
|
||||
CHECK(tsd_key_inited);
|
||||
fake_tsd = 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
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SetAlternateSignalStack() {
|
||||
// FIXME: Decide what to do on Windows.
|
||||
}
|
||||
|
||||
void UnsetAlternateSignalStack() {
|
||||
// FIXME: Decide what to do on Windows.
|
||||
}
|
||||
|
||||
void InstallSignalHandlers() {
|
||||
// FIXME: Decide what to do on Windows.
|
||||
}
|
||||
|
||||
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 |
|
||||
@ -95,87 +153,7 @@ bool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
// ---------------------- 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_);
|
||||
}
|
||||
|
||||
// ---------------------- TSD ---------------- {{{1
|
||||
static bool tsd_key_inited = false;
|
||||
|
||||
static __declspec(thread) void *fake_tsd = 0;
|
||||
|
||||
void AsanTSDInit(void (*destructor)(void *tsd)) {
|
||||
// FIXME: we're ignoring the destructor for now.
|
||||
tsd_key_inited = true;
|
||||
}
|
||||
|
||||
void *AsanTSDGet() {
|
||||
CHECK(tsd_key_inited);
|
||||
return fake_tsd;
|
||||
}
|
||||
|
||||
void AsanTSDSet(void *tsd) {
|
||||
CHECK(tsd_key_inited);
|
||||
fake_tsd = tsd;
|
||||
}
|
||||
|
||||
// ---------------------- Various stuff ---------------- {{{1
|
||||
void *AsanDoesNotSupportStaticLinkage() {
|
||||
#if defined(_DEBUG)
|
||||
#error Please build the runtime with a non-debug CRT: /MD or /MT
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SetAlternateSignalStack() {
|
||||
// FIXME: Decide what to do on Windows.
|
||||
}
|
||||
|
||||
void UnsetAlternateSignalStack() {
|
||||
// FIXME: Decide what to do on Windows.
|
||||
}
|
||||
|
||||
void InstallSignalHandlers() {
|
||||
// FIXME: Decide what to do on Windows.
|
||||
}
|
||||
|
||||
void AsanPlatformThreadInit() {
|
||||
// Nothing here for now.
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // _WIN32
|
||||
|
25
lib/asan/dynamic/Makefile.mk
Normal file
25
lib/asan/dynamic/Makefile.mk
Normal file
@ -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=%)
|
111
lib/asan/dynamic/asan_interceptors_dynamic.cc
Normal file
111
lib/asan/dynamic/asan_interceptors_dynamic.cc
Normal file
@ -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__
|
32
lib/asan/lit_tests/CMakeLists.txt
Normal file
32
lib/asan/lit_tests/CMakeLists.txt
Normal file
@ -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()
|
5
lib/asan/lit_tests/Helpers/blacklist-extra.cc
Normal file
5
lib/asan/lit_tests/Helpers/blacklist-extra.cc
Normal file
@ -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
|
||||
}
|
15
lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc
Normal file
15
lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc
Normal file
@ -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; }
|
2
lib/asan/lit_tests/Helpers/initialization-blacklist.txt
Normal file
2
lib/asan/lit_tests/Helpers/initialization-blacklist.txt
Normal file
@ -0,0 +1,2 @@
|
||||
global-init:*badGlobal*
|
||||
global-init-type:*badNamespace::BadClass*
|
5
lib/asan/lit_tests/Helpers/initialization-bug-extra.cc
Normal file
5
lib/asan/lit_tests/Helpers/initialization-bug-extra.cc
Normal file
@ -0,0 +1,5 @@
|
||||
// This file simply declares a dynamically initialized var by the name of 'y'.
|
||||
int initY() {
|
||||
return 5;
|
||||
}
|
||||
int y = initY();
|
6
lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc
Normal file
6
lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc
Normal file
@ -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();
|
9
lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc
Normal file
9
lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc
Normal file
@ -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();
|
3
lib/asan/lit_tests/Helpers/lit.local.cfg
Normal file
3
lib/asan/lit_tests/Helpers/lit.local.cfg
Normal file
@ -0,0 +1,3 @@
|
||||
# Sources in this directory are helper files for tests which test functionality
|
||||
# involving multiple translation units.
|
||||
config.suffixes = []
|
48
lib/asan/lit_tests/Linux/clone_test.cc
Normal file
48
lib/asan/lit_tests/Linux/clone_test.cc
Normal file
@ -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;
|
||||
}
|
37
lib/asan/lit_tests/Linux/initialization-bug-any-order.cc
Normal file
37
lib/asan/lit_tests/Linux/initialization-bug-any-order.cc
Normal file
@ -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;
|
||||
}
|
26
lib/asan/lit_tests/Linux/interception_failure_test.cc
Normal file
26
lib/asan/lit_tests/Linux/interception_failure_test.cc
Normal file
@ -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
|
||||
}
|
27
lib/asan/lit_tests/Linux/interception_malloc_test.cc
Normal file
27
lib/asan/lit_tests/Linux/interception_malloc_test.cc
Normal file
@ -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
|
||||
}
|
26
lib/asan/lit_tests/Linux/interception_test.cc
Normal file
26
lib/asan/lit_tests/Linux/interception_test.cc
Normal file
@ -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
|
||||
}
|
9
lib/asan/lit_tests/Linux/lit.local.cfg
Normal file
9
lib/asan/lit_tests/Linux/lit.local.cfg
Normal file
@ -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
|
50
lib/asan/lit_tests/Linux/malloc-in-qsort.cc
Normal file
50
lib/asan/lit_tests/Linux/malloc-in-qsort.cc
Normal file
@ -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
|
47
lib/asan/lit_tests/Linux/overflow-in-qsort.cc
Normal file
47
lib/asan/lit_tests/Linux/overflow-in-qsort.cc
Normal file
@ -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
|
16
lib/asan/lit_tests/Linux/rlimit_mmap_test.cc
Normal file
16
lib/asan/lit_tests/Linux/rlimit_mmap_test.cc
Normal file
@ -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;
|
||||
}
|
66
lib/asan/lit_tests/Linux/swapcontext_test.cc
Normal file
66
lib/asan/lit_tests/Linux/swapcontext_test.cc
Normal file
@ -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;
|
||||
}
|
4
lib/asan/lit_tests/SharedLibs/lit.local.cfg
Normal file
4
lib/asan/lit_tests/SharedLibs/lit.local.cfg
Normal file
@ -0,0 +1,4 @@
|
||||
# Sources in this directory are compiled as shared libraries and used by
|
||||
# tests in parent directory.
|
||||
|
||||
config.suffixes = []
|
27
lib/asan/lit_tests/Unit/lit.cfg
Normal file
27
lib/asan/lit_tests/Unit/lit.cfg
Normal file
@ -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
|
10
lib/asan/lit_tests/Unit/lit.site.cfg.in
Normal file
10
lib/asan/lit_tests/Unit/lit.site.cfg.in
Normal file
@ -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")
|
44
lib/asan/lit_tests/blacklist.cc
Normal file
44
lib/asan/lit_tests/blacklist.cc
Normal file
@ -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;
|
||||
}
|
36
lib/asan/lit_tests/deep_stack_uaf.cc
Normal file
36
lib/asan/lit_tests/deep_stack_uaf.cc
Normal file
@ -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>
|
||||
}
|
24
lib/asan/lit_tests/deep_tail_call.cc
Normal file
24
lib/asan/lit_tests/deep_tail_call.cc
Normal file
@ -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];
|
||||
}
|
61
lib/asan/lit_tests/deep_thread_stack.cc
Normal file
61
lib/asan/lit_tests/deep_thread_stack.cc
Normal file
@ -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:
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
14
lib/asan/lit_tests/force_inline_opt0.cc
Normal file
14
lib/asan/lit_tests/force_inline_opt0.cc
Normal file
@ -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;
|
||||
}
|
25
lib/asan/lit_tests/global-overflow.cc
Normal file
25
lib/asan/lit_tests/global-overflow.cc
Normal file
@ -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;
|
||||
}
|
38
lib/asan/lit_tests/heap-overflow.cc
Normal file
38
lib/asan/lit_tests/heap-overflow.cc
Normal file
@ -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;
|
||||
}
|
32
lib/asan/lit_tests/initialization-blacklist.cc
Normal file
32
lib/asan/lit_tests/initialization-blacklist.cc
Normal file
@ -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;
|
||||
}
|
46
lib/asan/lit_tests/initialization-bug.cc
Normal file
46
lib/asan/lit_tests/initialization-bug.cc
Normal file
@ -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;
|
||||
}
|
67
lib/asan/lit_tests/initialization-nobug.cc
Normal file
67
lib/asan/lit_tests/initialization-nobug.cc
Normal file
@ -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; }
|
28
lib/asan/lit_tests/interface_symbols.c
Normal file
28
lib/asan/lit_tests/interface_symbols.c
Normal file
@ -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; }
|
62
lib/asan/lit_tests/large_func_test.cc
Normal file
62
lib/asan/lit_tests/large_func_test.cc
Normal file
@ -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;
|
||||
}
|
97
lib/asan/lit_tests/lit.cfg
Normal file
97
lib/asan/lit_tests/lit.cfg
Normal file
@ -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
|
20
lib/asan/lit_tests/lit.site.cfg.in
Normal file
20
lib/asan/lit_tests/lit.site.cfg.in
Normal file
@ -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")
|
39
lib/asan/lit_tests/log-path_test.cc
Normal file
39
lib/asan/lit_tests/log-path_test.cc
Normal file
@ -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
|
22
lib/asan/lit_tests/log_path_fork_test.cc
Normal file
22
lib/asan/lit_tests/log_path_fork_test.cc
Normal file
@ -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
|
26
lib/asan/lit_tests/malloc_delete_mismatch.cc
Normal file
26
lib/asan/lit_tests/malloc_delete_mismatch.cc
Normal file
@ -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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user