Import compiler-rt r160957.
This commit is contained in:
parent
864a7b98b5
commit
37dfff0574
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/vendor/compiler-rt/dist/; revision=238901 svn path=/vendor/compiler-rt/compiler-rt-r160957/; revision=238902; tag=vendor/compiler-rt/compiler-rt-r160957
@ -1,13 +0,0 @@
|
||||
PROJECT( BlocksRuntime C )
|
||||
|
||||
SET( SRCS
|
||||
runtime.c
|
||||
data.c
|
||||
)
|
||||
|
||||
ADD_LIBRARY( ${PROJECT_NAME} SHARED ${SRCS})
|
||||
SET_TARGET_PROPERTIES( ${PROJECT_NAME} PROPERTIES
|
||||
INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/lib )
|
||||
|
||||
INSTALL( TARGETS ${PROJECT_NAME} DESTINATION lib )
|
||||
INSTALL( FILES Block.h Block_private.h DESTINATION include )
|
105
CMakeLists.txt
105
CMakeLists.txt
@ -1,55 +1,68 @@
|
||||
# See docs/CMake.html for instructions about how to build Compiler-RT with CMake.
|
||||
# CMake build for CompilerRT.
|
||||
#
|
||||
# This build assumes that CompilerRT is checked out into the
|
||||
# 'projects/compiler-rt' inside of an LLVM tree, it is not a stand-alone build
|
||||
# system.
|
||||
#
|
||||
# An important constraint of the build is that it only produces libraries
|
||||
# based on the ability of the host toolchain to target various platforms.
|
||||
|
||||
PROJECT( CompilerRT C )
|
||||
CMAKE_MINIMUM_REQUIRED( VERSION 2.6 )
|
||||
include(LLVMParseArguments)
|
||||
|
||||
set(PACKAGE_NAME compiler-rt)
|
||||
set(PACKAGE_VERSION 1.0svn)
|
||||
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
|
||||
set(PACKAGE_BUGREPORT "llvmbugs@cs.uiuc.edu")
|
||||
# The CompilerRT build system requires CMake version 2.8.8 or higher in order
|
||||
# to use its support for building convenience "libraries" as a collection of
|
||||
# .o files. This is particularly useful in producing larger, more complex
|
||||
# runtime libraries.
|
||||
cmake_minimum_required(VERSION 2.8.8)
|
||||
|
||||
SET( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules )
|
||||
# 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 definitions
|
||||
include(DefineCompilerFlags)
|
||||
# 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 "")
|
||||
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")
|
||||
endif()
|
||||
|
||||
# Disallow in-source build
|
||||
INCLUDE( MacroEnsureOutOfSourceBuild )
|
||||
MACRO_ENSURE_OUT_OF_SOURCE_BUILD(
|
||||
"${PROJECT_NAME} requires an out of source build. Please create a separate build directory and run 'cmake /path/to/${PROJECT_NAME} [options]' there."
|
||||
)
|
||||
# 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}")
|
||||
|
||||
INCLUDE( ${CMAKE_SOURCE_DIR}/cmake/ConfigureChecks.cmake )
|
||||
CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/cmake/config.h.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/config.h )
|
||||
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}")
|
||||
|
||||
INCLUDE_DIRECTORIES(
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
# 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()
|
||||
|
||||
SET( Achitectures
|
||||
i386 x86_64 ppc arm
|
||||
)
|
||||
add_subdirectory(lib)
|
||||
|
||||
SET( Configurations
|
||||
Debug Release Profile
|
||||
)
|
||||
|
||||
# Only build Blocks Runtime if the compiler has enough support
|
||||
IF( WIN32 OR MSVC OR HAVE_OSATOMIC_COMPARE_AND_SWAP_INT OR HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT )
|
||||
SET(BUILD_BLOCKS_RUNTIME TRUE)
|
||||
ELSE( WIN32 OR MSVC OR HAVE_OSATOMIC_COMPARE_AND_SWAP_INT OR HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT )
|
||||
SET(BUILD_BLOCKS_RUNTIME FALSE)
|
||||
ENDIF( WIN32 OR MSVC OR HAVE_OSATOMIC_COMPARE_AND_SWAP_INT OR HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT )
|
||||
|
||||
IF( BUILD_BLOCKS_RUNTIME )
|
||||
ADD_SUBDIRECTORY( BlocksRuntime )
|
||||
ELSE( BUILD_BLOCKS_RUNTIME )
|
||||
MESSAGE(STATUS "No suitable atomic operation routines detected, skipping Blocks Runtime")
|
||||
ENDIF( BUILD_BLOCKS_RUNTIME )
|
||||
|
||||
ADD_SUBDIRECTORY( lib )
|
||||
|
||||
# Enable Test Suit:
|
||||
INCLUDE( MacroAddCheckTest )
|
||||
ADD_SUBDIRECTORY( test )
|
||||
if(LLVM_INCLUDE_TESTS)
|
||||
# Currently the tests have not been ported to CMake, so disable this
|
||||
# directory.
|
||||
#
|
||||
#add_subdirectory(test)
|
||||
endif()
|
||||
|
@ -94,5 +94,4 @@ licenses, and/or restrictions:
|
||||
|
||||
Program Directory
|
||||
------- ---------
|
||||
sysinfo lib/asan/sysinfo
|
||||
mach_override lib/asan/mach_override
|
||||
mach_override lib/interception/mach_override
|
||||
|
@ -22,6 +22,7 @@
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
void abort(void) __attribute__((__noreturn__));
|
||||
int atoi(const char *);
|
||||
void free(void *);
|
||||
char *getenv(const char *);
|
||||
void *malloc(size_t);
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
int memcmp(const void *, const void *, size_t);
|
||||
void *memcpy(void *, const void *, size_t);
|
||||
char *strcat(char *, const char *);
|
||||
char *strcpy(char *, const char *);
|
||||
char *strdup(const char *);
|
||||
|
@ -22,6 +22,7 @@
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
void abort(void) __attribute__((__nothrow__)) __attribute__((__noreturn__));
|
||||
int atoi(const char *) __attribute__((__nothrow__));
|
||||
void free(void *) __attribute__((__nothrow__));
|
||||
char *getenv(const char *) __attribute__((__nothrow__))
|
||||
__attribute__((__nonnull__(1)));
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
int memcmp(const void *, const void *, size_t);
|
||||
void *memcpy(void *, const void *, size_t);
|
||||
char *strcat(char *, const char *);
|
||||
char *strcpy(char *, const char *);
|
||||
char *strdup(const char *);
|
||||
|
@ -1,38 +0,0 @@
|
||||
INCLUDE( CheckIncludeFile )
|
||||
INCLUDE( CheckFunctionExists )
|
||||
INCLUDE( CheckSymbolExists )
|
||||
INCLUDE( CheckCSourceCompiles )
|
||||
|
||||
SET( PACKAGE ${PACKAGE_NAME} )
|
||||
SET( VERSION ${PACKAGE_VERSION} )
|
||||
|
||||
SET( BINARYDIR ${CMAKE_BINARY_DIR} )
|
||||
SET( SOURCEDIR ${CMAKE_SOURCE_DIR} )
|
||||
|
||||
# HEADER FILES
|
||||
CHECK_INCLUDE_FILE( sys/byteorder.h HAVE_SYS_BYTEORDER_H )
|
||||
CHECK_INCLUDE_FILE( AvailabilityMacros.h HAVE_AVAILABILITY_MACROS_H )
|
||||
CHECK_INCLUDE_FILE( TargetConditionals.h HAVE_TARGET_CONDITIONALS_H )
|
||||
CHECK_INCLUDE_FILE( libkern/OSAtomic.h HAVE_LIBKERN_OSATOMIC_H )
|
||||
|
||||
# FUNCTIONS
|
||||
CHECK_FUNCTION_EXISTS( sysconf HAVE_SYSCONF )
|
||||
CHECK_SYMBOL_EXISTS( OSAtomicCompareAndSwapInt libkern/OSAtomic.h HAVE_OSATOMIC_COMPARE_AND_SWAP_INT )
|
||||
CHECK_SYMBOL_EXISTS( OSAtomicCompareAndSwapLong libkern/OSAtomic.h HAVE_OSATOMIC_COMPARE_AND_SWAP_LONG )
|
||||
|
||||
# BUILTIN
|
||||
CHECK_C_SOURCE_COMPILES( "
|
||||
volatile int a;
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)__sync_bool_compare_and_swap(&a, 1, 2);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT )
|
||||
|
||||
CHECK_C_SOURCE_COMPILES( "
|
||||
volatile long a;
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)__sync_bool_compare_and_swap(&a, 1, 2);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_SYNC_BOOL_COMPARE_AND_SWAP_LONG )
|
@ -1,6 +0,0 @@
|
||||
# Define compiler flags
|
||||
|
||||
if( CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX )
|
||||
#ADD_DEFINITIONS( -Wall -W -Werror -pedantic )
|
||||
ADD_DEFINITIONS( -std=c99 -Wall -Wextra -W -pedantic -Wno-unused-parameter )
|
||||
endif( CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX )
|
@ -1,12 +0,0 @@
|
||||
# - macro_add_check_test(test_name test_source linklib1 ... linklibN)
|
||||
|
||||
ENABLE_TESTING()
|
||||
include(CTest)
|
||||
set(CMAKE_C_FLAGS_PROFILING "-g -pg")
|
||||
|
||||
macro (MACRO_ADD_CHECK_TEST _testName _testSource)
|
||||
add_executable(${_testName} ${_testSource})
|
||||
target_link_libraries(${_testName} ${ARGN})
|
||||
get_target_property(_targetLocation ${_testName} LOCATION)
|
||||
add_test(${_testName} ${_targetLocation})
|
||||
endmacro (MACRO_ADD_CHECK_TEST)
|
@ -1,18 +0,0 @@
|
||||
# MACRO_ENSURE_OUT_OF_SOURCE_BUILD(<errorMessage>)
|
||||
|
||||
macro( MACRO_ENSURE_OUT_OF_SOURCE_BUILD _errorMessage )
|
||||
|
||||
string( COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" _insource )
|
||||
if( _insource )
|
||||
message( SEND_ERROR "${_errorMessage}" )
|
||||
message( FATAL_ERROR
|
||||
"In-source builds are not allowed.
|
||||
CMake would overwrite the makefiles distributed with Compiler-RT.
|
||||
Please create a directory and run cmake from there, passing the path
|
||||
to this source directory as the last argument.
|
||||
This process created the file `CMakeCache.txt' and the directory `CMakeFiles'.
|
||||
Please delete them."
|
||||
)
|
||||
endif( _insource )
|
||||
|
||||
endmacro( MACRO_ENSURE_OUT_OF_SOURCE_BUILD )
|
@ -1,12 +0,0 @@
|
||||
#cmakedefine HAVE_SYS_BYTEORDER_H ${HAVE_SYS_BYTEORDER}
|
||||
#cmakedefine HAVE_AVAILABILITY_MACROS_H ${HAVE_AVAILABILITY_MACROS_H}
|
||||
#cmakedefine HAVE_TARGET_CONDITIONALS_H ${HAVE_TARGET_CONDITIONALS_H}
|
||||
#cmakedefine HAVE_LIBKERN_OSATOMIC_H ${HAVE_LIBKERN_OSATOMIC_H}
|
||||
|
||||
#cmakedefine HAVE_SYSCONF ${HAVE_SYSCONF}
|
||||
|
||||
#cmakedefine HAVE_OSATOMIC_COMPARE_AND_SWAP_INT ${HAVE_OSATOMIC_COMPARE_AND_SWAP_INT}
|
||||
#cmakedefine HAVE_OSATOMIC_COMPARE_AND_SWAP_LONG ${HAVE_OSATOMIC_COMPARE_AND_SWAP_LONG}
|
||||
|
||||
#cmakedefine HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT ${HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT}
|
||||
#cmakedefine HAVE_SYNC_BOOL_COMPARE_AND_SWAP_LONG ${HAVE_SYNC_BOOL_COMPARE_AND_SWAP_LONG}
|
@ -1,81 +1,197 @@
|
||||
#
|
||||
# Create a library called "CompilerRT" which includes the source files.
|
||||
# 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})
|
||||
|
||||
#INCLUDE_DIRECTORIES(
|
||||
# ${CMAKE_CURRENT_BINARY_DIR}
|
||||
#)
|
||||
# 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()
|
||||
|
||||
# Generic functions needed for each architecture
|
||||
# libcompiler_rt.Generic.a libcompiler_rt.Optimized.a
|
||||
# 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)
|
||||
|
||||
# Generic
|
||||
SET( Generic_SRCS
|
||||
absvdi2.c absvsi2.c addvdi3.c addvsi3.c ashldi3.c ashrdi3.c
|
||||
clzdi2.c clzsi2.c cmpdi2.c ctzdi2.c ctzsi2.c
|
||||
divdc3.c divdi3.c divsc3.c ffsdi2.c
|
||||
fixdfdi.c fixsfdi.c fixunsdfdi.c fixunsdfsi.c fixunssfdi.c
|
||||
fixunssfsi.c floatdidf.c floatdisf.c floatundidf.c floatundisf.c
|
||||
gcc_personality_v0.c lshrdi3.c moddi3.c muldc3.c muldi3.c
|
||||
mulsc3.c mulvdi3.c mulvsi3.c negdi2.c negvdi2.c negvsi2.c
|
||||
paritydi2.c paritysi2.c popcountdi2.c popcountsi2.c powidf2.c
|
||||
powisf2.c subvdi3.c subvsi3.c ucmpdi2.c udivdi3.c
|
||||
udivmoddi4.c umoddi3.c apple_versioning.c eprintf.c
|
||||
)
|
||||
# FIXME: Add support for the profile library.
|
||||
|
||||
# Optimized functions for each architecture
|
||||
|
||||
# Commenting out for the min until the basics are working first.
|
||||
# ADD_SUBDIRECTORY( ppc )
|
||||
# ADD_SUBDIRECTORY( x86_64 )
|
||||
# ADD_SUBDIRECTORY( i386 )
|
||||
# ADD_SUBDIRECTORY( arm )
|
||||
# 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.
|
||||
|
||||
# List of functions needed for each architecture.
|
||||
SET( i386_Functions
|
||||
divxc3.c fixunsxfdi.c fixunsxfsi.c fixxfdi.c floatdixf.c
|
||||
floatundixf.c mulxc3.c powixf2.c clear_cache.c enable_execute_stack.c
|
||||
)
|
||||
|
||||
SET( x86_64_Functions
|
||||
absvti2.c addvti3.c ashlti3.c ashrti3.c clzti2.c cmpti2.c
|
||||
ctzti2.c divti3.c divxc3.c ffsti2.c fixdfti.c fixsfti.c
|
||||
fixunsdfti.c fixunssfti.c fixunsxfdi.c fixunsxfsi.c
|
||||
fixunsxfti.c fixxfdi.c fixxfti.c floatdixf.c floattidf.c
|
||||
floattisf.c floattixf.c floatundixf.c floatuntidf.c
|
||||
floatuntisf.c floatuntixf.c lshrti3.c modti3.c multi3.c
|
||||
mulvti3.c mulxc3.c negti2.c negvti2.c parityti2.c
|
||||
popcountti2.c powixf2.c subvti3.c ucmpti2.c udivmodti4.c
|
||||
udivti3.c umodti3.c clear_cache.c enable_execute_stack.c
|
||||
)
|
||||
|
||||
SET( PPC_Functions
|
||||
divtc3.c fixtfdi.c fixunstfdi.c floatditf.c floatunditf.c
|
||||
gcc_qadd.c gcc_qdiv.c gcc_qmul.c gcc_qsub.c multc3.c
|
||||
powitf2.c restFP.c saveFP.c trampoline_setup.c
|
||||
clear_cache.c enable_execute_stack.c
|
||||
)
|
||||
|
||||
SET( ARM_Functions
|
||||
adddf3vfp.c addsf3vfp.c bswapdi2.c bswapsi2.c divdf3vfp.c
|
||||
divsf3vfp.c eqdf2vfp.c eqsf2vfp.c extendsfdf2vfp.c
|
||||
fixdfsivfp.c fixsfsivfp.c fixunsdfsivfp.c fixunssfsivfp.c
|
||||
floatsidfvfp.c floatsisfvfp.c floatunssidfvfp.c floatunssisfvfp.c
|
||||
gedf2vfp.c gesf2vfp.c gtdf2vfp.c gtsf2vfp.c
|
||||
ledf2vfp.c lesf2vfp.c ltdf2vfp.c ltsf2vfp.c
|
||||
muldf3vfp.c mulsf3vfp.c
|
||||
nedf2vfp.c negdf2vfp.c negsf2vfp.c nesf2vfp.c
|
||||
subdf3vfp.c subsf3vfp.c truncdfsf2vfp.c unorddf2vfp.c unordsf2vfp.c
|
||||
modsi3.c umodsi3.c udivsi3.c divsi3.c switch.c
|
||||
)
|
||||
|
||||
#FOREACH( LOOP_VAR ${Achitectures} )
|
||||
# See ARCHIVE_OUTPUT_DIRECTORY docs.
|
||||
#${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${LOOP_VAR}
|
||||
#ENDFOREACH
|
||||
|
||||
ADD_LIBRARY( ${PROJECT_NAME}-Generic STATIC ${Generic_SRCS} )
|
||||
#ADD_LIBRARY( ${PROJECT_NAME}-i386 STATIC ${i386_Functions} )
|
||||
|
||||
# [[debug|optimized|general]
|
||||
#TARGET_LINK_LIBRARIES( ${PROJECT_NAME} ${PROJECT_NAME}-Common optimized ${PROJECT_NAME}-i386 )
|
||||
set(GENERIC_SOURCES
|
||||
absvdi2.c
|
||||
absvsi2.c
|
||||
absvti2.c
|
||||
adddf3.c
|
||||
addsf3.c
|
||||
addvdi3.c
|
||||
addvsi3.c
|
||||
addvti3.c
|
||||
apple_versioning.c
|
||||
ashldi3.c
|
||||
ashlti3.c
|
||||
ashrdi3.c
|
||||
ashrti3.c
|
||||
clear_cache.c
|
||||
clzdi2.c
|
||||
clzsi2.c
|
||||
clzti2.c
|
||||
cmpdi2.c
|
||||
cmpti2.c
|
||||
comparedf2.c
|
||||
comparesf2.c
|
||||
ctzdi2.c
|
||||
ctzsi2.c
|
||||
ctzti2.c
|
||||
divdc3.c
|
||||
divdf3.c
|
||||
divdi3.c
|
||||
divmoddi4.c
|
||||
divmodsi4.c
|
||||
divsc3.c
|
||||
divsf3.c
|
||||
divsi3.c
|
||||
divti3.c
|
||||
divxc3.c
|
||||
enable_execute_stack.c
|
||||
eprintf.c
|
||||
extendsfdf2.c
|
||||
ffsdi2.c
|
||||
ffsti2.c
|
||||
fixdfdi.c
|
||||
fixdfsi.c
|
||||
fixdfti.c
|
||||
fixsfdi.c
|
||||
fixsfsi.c
|
||||
fixsfti.c
|
||||
fixunsdfdi.c
|
||||
fixunsdfsi.c
|
||||
fixunsdfti.c
|
||||
fixunssfdi.c
|
||||
fixunssfsi.c
|
||||
fixunssfti.c
|
||||
fixunsxfdi.c
|
||||
fixunsxfsi.c
|
||||
fixunsxfti.c
|
||||
fixxfdi.c
|
||||
fixxfti.c
|
||||
floatdidf.c
|
||||
floatdisf.c
|
||||
floatdixf.c
|
||||
floatsidf.c
|
||||
floatsisf.c
|
||||
floattidf.c
|
||||
floattisf.c
|
||||
floattixf.c
|
||||
floatundidf.c
|
||||
floatundisf.c
|
||||
floatundixf.c
|
||||
floatunsidf.c
|
||||
floatunsisf.c
|
||||
floatuntidf.c
|
||||
floatuntisf.c
|
||||
floatuntixf.c
|
||||
gcc_personality_v0.c
|
||||
int_util.c
|
||||
lshrdi3.c
|
||||
lshrti3.c
|
||||
moddi3.c
|
||||
modsi3.c
|
||||
modti3.c
|
||||
muldc3.c
|
||||
muldf3.c
|
||||
muldi3.c
|
||||
mulodi4.c
|
||||
mulosi4.c
|
||||
muloti4.c
|
||||
mulsc3.c
|
||||
mulsf3.c
|
||||
multi3.c
|
||||
mulvdi3.c
|
||||
mulvsi3.c
|
||||
mulvti3.c
|
||||
mulxc3.c
|
||||
negdf2.c
|
||||
negdi2.c
|
||||
negsf2.c
|
||||
negti2.c
|
||||
negvdi2.c
|
||||
negvsi2.c
|
||||
negvti2.c
|
||||
paritydi2.c
|
||||
paritysi2.c
|
||||
parityti2.c
|
||||
popcountdi2.c
|
||||
popcountsi2.c
|
||||
popcountti2.c
|
||||
powidf2.c
|
||||
powisf2.c
|
||||
powitf2.c
|
||||
powixf2.c
|
||||
subdf3.c
|
||||
subsf3.c
|
||||
subvdi3.c
|
||||
subvsi3.c
|
||||
subvti3.c
|
||||
trampoline_setup.c
|
||||
truncdfsf2.c
|
||||
ucmpdi2.c
|
||||
ucmpti2.c
|
||||
udivdi3.c
|
||||
udivmoddi4.c
|
||||
udivmodsi4.c
|
||||
udivmodti4.c
|
||||
udivsi3.c
|
||||
udivti3.c
|
||||
umoddi3.c
|
||||
umodsi3.c
|
||||
umodti3.c
|
||||
)
|
||||
|
||||
if(CAN_TARGET_X86_64)
|
||||
add_library(clang_rt.x86_64 STATIC
|
||||
x86_64/floatdidf.c
|
||||
x86_64/floatdisf.c
|
||||
x86_64/floatdixf.c
|
||||
x86_64/floatundidf.S
|
||||
x86_64/floatundisf.S
|
||||
x86_64/floatundixf.S
|
||||
${GENERIC_SOURCES}
|
||||
)
|
||||
set_target_properties(clang_rt.x86_64 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_X86_64_CFLAGS}")
|
||||
endif()
|
||||
if(CAN_TARGET_I386)
|
||||
add_library(clang_rt.i386 STATIC
|
||||
i386/ashldi3.S
|
||||
i386/ashrdi3.S
|
||||
i386/divdi3.S
|
||||
i386/floatdidf.S
|
||||
i386/floatdisf.S
|
||||
i386/floatdixf.S
|
||||
i386/floatundidf.S
|
||||
i386/floatundisf.S
|
||||
i386/floatundixf.S
|
||||
i386/lshrdi3.S
|
||||
i386/moddi3.S
|
||||
i386/muldi3.S
|
||||
i386/udivdi3.S
|
||||
i386/umoddi3.S
|
||||
${GENERIC_SOURCES}
|
||||
)
|
||||
set_target_properties(clang_rt.i386 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_I386_CFLAGS}")
|
||||
endif()
|
||||
|
@ -15,10 +15,18 @@ SubDirs += i386 ppc x86_64 arm
|
||||
|
||||
# Add other submodules.
|
||||
SubDirs += asan
|
||||
SubDirs += interception
|
||||
SubDirs += profile
|
||||
SubDirs += sanitizer_common
|
||||
SubDirs += tsan
|
||||
|
||||
# FIXME: We don't currently support building an atomic library, and as it must
|
||||
# be a separate library from the runtime library, we need to remove its source
|
||||
# code from the source files list.
|
||||
ExcludedSources := atomic.c
|
||||
|
||||
# Define the variables for this specific directory.
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(filter-out $(ExcludedSources),$(notdir $(file))))
|
||||
ObjNames := $(Sources:%.c=%.o)
|
||||
Implementation := Generic
|
||||
|
||||
|
@ -12,10 +12,10 @@
|
||||
* ===----------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#if __x86_64
|
||||
|
||||
#include "int_lib.h"
|
||||
|
||||
#if __x86_64
|
||||
|
||||
/* Returns: absolute value */
|
||||
|
||||
/* Effects: aborts if abs(x) < 0 */
|
||||
|
@ -15,7 +15,7 @@
|
||||
#define DOUBLE_PRECISION
|
||||
#include "fp_lib.h"
|
||||
|
||||
ARM_EABI_FNALIAS(dadd, adddf3);
|
||||
ARM_EABI_FNALIAS(dadd, adddf3)
|
||||
|
||||
COMPILER_RT_ABI fp_t
|
||||
__adddf3(fp_t a, fp_t b) {
|
||||
@ -85,7 +85,7 @@ __adddf3(fp_t a, fp_t b) {
|
||||
|
||||
// Shift the significand of b by the difference in exponents, with a sticky
|
||||
// bottom bit to get rounding correct.
|
||||
const int align = aExponent - bExponent;
|
||||
const unsigned int align = aExponent - bExponent;
|
||||
if (align) {
|
||||
if (align < typeWidth) {
|
||||
const bool sticky = bSignificand << (typeWidth - align);
|
||||
|
@ -15,7 +15,7 @@
|
||||
#define SINGLE_PRECISION
|
||||
#include "fp_lib.h"
|
||||
|
||||
ARM_EABI_FNALIAS(fadd, addsf3);
|
||||
ARM_EABI_FNALIAS(fadd, addsf3)
|
||||
|
||||
fp_t __addsf3(fp_t a, fp_t b) {
|
||||
|
||||
@ -84,7 +84,7 @@ fp_t __addsf3(fp_t a, fp_t b) {
|
||||
|
||||
// Shift the significand of b by the difference in exponents, with a sticky
|
||||
// bottom bit to get rounding correct.
|
||||
const int align = aExponent - bExponent;
|
||||
const unsigned int align = aExponent - bExponent;
|
||||
if (align) {
|
||||
if (align < typeWidth) {
|
||||
const bool sticky = bSignificand << (typeWidth - align);
|
||||
|
@ -12,10 +12,10 @@
|
||||
* ===----------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#if __x86_64
|
||||
|
||||
#include "int_lib.h"
|
||||
|
||||
#if __x86_64
|
||||
|
||||
/* Returns: a + b */
|
||||
|
||||
/* Effects: aborts if a + b overflows */
|
||||
|
27
lib/arm/aeabi_idivmod.S
Normal file
27
lib/arm/aeabi_idivmod.S
Normal file
@ -0,0 +1,27 @@
|
||||
//===-- aeabi_idivmod.S - EABI idivmod implementation ---------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../assembly.h"
|
||||
|
||||
// struct { int quot, int rem} __aeabi_idivmod(int numerator, int denominator) {
|
||||
// int rem, quot;
|
||||
// quot = __divmodsi4(numerator, denominator, &rem);
|
||||
// return {quot, rem};
|
||||
// }
|
||||
|
||||
.syntax unified
|
||||
.align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_idivmod)
|
||||
push { lr }
|
||||
sub sp, sp, #4
|
||||
mov r2, sp
|
||||
bl SYMBOL_NAME(__divmodsi4)
|
||||
ldr r1, [sp]
|
||||
add sp, sp, #4
|
||||
pop { pc }
|
30
lib/arm/aeabi_ldivmod.S
Normal file
30
lib/arm/aeabi_ldivmod.S
Normal file
@ -0,0 +1,30 @@
|
||||
//===-- aeabi_ldivmod.S - EABI ldivmod implementation ---------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../assembly.h"
|
||||
|
||||
// struct { int64_t quot, int64_t rem}
|
||||
// __aeabi_ldivmod(int64_t numerator, int64_t denominator) {
|
||||
// int64_t rem, quot;
|
||||
// quot = __divmoddi4(numerator, denominator, &rem);
|
||||
// return {quot, rem};
|
||||
// }
|
||||
|
||||
.syntax unified
|
||||
.align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_ldivmod)
|
||||
push {r11, lr}
|
||||
sub sp, sp, #16
|
||||
add r12, sp, #8
|
||||
str r12, [sp]
|
||||
bl SYMBOL_NAME(__divmoddi4)
|
||||
ldr r2, [sp, #8]
|
||||
ldr r3, [sp, #12]
|
||||
add sp, sp, #16
|
||||
pop {r11, pc}
|
19
lib/arm/aeabi_memcmp.S
Normal file
19
lib/arm/aeabi_memcmp.S
Normal file
@ -0,0 +1,19 @@
|
||||
//===-- aeabi_memcmp.S - EABI memcmp implementation -----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../assembly.h"
|
||||
|
||||
// void __aeabi_memcmp(void *dest, void *src, size_t n) { memcmp(dest, src, n); }
|
||||
|
||||
.align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_memcmp)
|
||||
b memcmp
|
||||
|
||||
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcmp4, __aeabi_memcmp)
|
||||
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcmp8, __aeabi_memcmp)
|
19
lib/arm/aeabi_memcpy.S
Normal file
19
lib/arm/aeabi_memcpy.S
Normal file
@ -0,0 +1,19 @@
|
||||
//===-- aeabi_memcpy.S - EABI memcpy implementation -----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../assembly.h"
|
||||
|
||||
// void __aeabi_memcpy(void *dest, void *src, size_t n) { memcpy(dest, src, n); }
|
||||
|
||||
.align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_memcpy)
|
||||
b memcpy
|
||||
|
||||
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcpy4, __aeabi_memcpy)
|
||||
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcpy8, __aeabi_memcpy)
|
19
lib/arm/aeabi_memmove.S
Normal file
19
lib/arm/aeabi_memmove.S
Normal file
@ -0,0 +1,19 @@
|
||||
//===-- aeabi_memmove.S - EABI memmove implementation --------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
#include "../assembly.h"
|
||||
|
||||
// void __aeabi_memmove(void *dest, void *src, size_t n) { memmove(dest, src, n); }
|
||||
|
||||
.align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_memmove)
|
||||
b memmove
|
||||
|
||||
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memmove4, __aeabi_memmove)
|
||||
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memmove8, __aeabi_memmove)
|
32
lib/arm/aeabi_memset.S
Normal file
32
lib/arm/aeabi_memset.S
Normal file
@ -0,0 +1,32 @@
|
||||
//===-- aeabi_memset.S - EABI memset implementation -----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../assembly.h"
|
||||
|
||||
// void __aeabi_memset(void *dest, size_t n, int c) { memset(dest, c, n); }
|
||||
// void __aeabi_memclr(void *dest, size_t n) { __aeabi_memset(dest, n, 0); }
|
||||
|
||||
.align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_memset)
|
||||
mov r3, r1
|
||||
mov r1, r2
|
||||
mov r2, r3
|
||||
b memset
|
||||
|
||||
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memset4, __aeabi_memset)
|
||||
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memset8, __aeabi_memset)
|
||||
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_memclr)
|
||||
mov r2, r1
|
||||
mov r1, #0
|
||||
b memset
|
||||
|
||||
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memclr4, __aeabi_memclr)
|
||||
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memclr8, __aeabi_memclr)
|
||||
|
28
lib/arm/aeabi_uidivmod.S
Normal file
28
lib/arm/aeabi_uidivmod.S
Normal file
@ -0,0 +1,28 @@
|
||||
//===-- aeabi_uidivmod.S - EABI uidivmod implementation -------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../assembly.h"
|
||||
|
||||
// struct { unsigned quot, unsigned rem}
|
||||
// __aeabi_uidivmod(unsigned numerator, unsigned denominator) {
|
||||
// unsigned rem, quot;
|
||||
// quot = __udivmodsi4(numerator, denominator, &rem);
|
||||
// return {quot, rem};
|
||||
// }
|
||||
|
||||
.syntax unified
|
||||
.align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_uidivmod)
|
||||
push { lr }
|
||||
sub sp, sp, #4
|
||||
mov r2, sp
|
||||
bl SYMBOL_NAME(__udivmodsi4)
|
||||
ldr r1, [sp]
|
||||
add sp, sp, #4
|
||||
pop { pc }
|
30
lib/arm/aeabi_uldivmod.S
Normal file
30
lib/arm/aeabi_uldivmod.S
Normal file
@ -0,0 +1,30 @@
|
||||
//===-- aeabi_uldivmod.S - EABI uldivmod implementation -------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../assembly.h"
|
||||
|
||||
// struct { uint64_t quot, uint64_t rem}
|
||||
// __aeabi_uldivmod(uint64_t numerator, uint64_t denominator) {
|
||||
// uint64_t rem, quot;
|
||||
// quot = __udivmoddi4(numerator, denominator, &rem);
|
||||
// return {quot, rem};
|
||||
// }
|
||||
|
||||
.syntax unified
|
||||
.align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_uldivmod)
|
||||
push {r11, lr}
|
||||
sub sp, sp, #16
|
||||
add r12, sp, #8
|
||||
str r12, [sp]
|
||||
bl SYMBOL_NAME(__udivmoddi4)
|
||||
ldr r2, [sp, #8]
|
||||
ldr r3, [sp, #12]
|
||||
add sp, sp, #16
|
||||
pop {r11, pc}
|
82
lib/asan/CMakeLists.txt
Normal file
82
lib/asan/CMakeLists.txt
Normal file
@ -0,0 +1,82 @@
|
||||
# Build for the AddressSanitizer runtime support library.
|
||||
|
||||
set(ASAN_SOURCES
|
||||
asan_allocator.cc
|
||||
asan_globals.cc
|
||||
asan_interceptors.cc
|
||||
asan_linux.cc
|
||||
asan_mac.cc
|
||||
asan_malloc_linux.cc
|
||||
asan_malloc_mac.cc
|
||||
asan_malloc_win.cc
|
||||
asan_new_delete.cc
|
||||
asan_poisoning.cc
|
||||
asan_posix.cc
|
||||
asan_printf.cc
|
||||
asan_rtl.cc
|
||||
asan_stack.cc
|
||||
asan_stats.cc
|
||||
asan_thread.cc
|
||||
asan_thread_registry.cc
|
||||
asan_win.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 ()
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND ASAN_CFLAGS -mmacosx-version-min=10.5)
|
||||
endif()
|
||||
|
||||
set(ASAN_COMMON_DEFINITIONS
|
||||
ASAN_HAS_EXCEPTIONS=1
|
||||
ASAN_NEEDS_SEGV=1
|
||||
)
|
||||
|
||||
# FIXME: We need to build universal binaries on OS X instead of
|
||||
# two arch-specific binaries.
|
||||
|
||||
if(CAN_TARGET_X86_64)
|
||||
add_library(clang_rt.asan-x86_64 STATIC
|
||||
${ASAN_SOURCES}
|
||||
$<TARGET_OBJECTS:RTInterception.x86_64>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.x86_64>
|
||||
)
|
||||
set_target_compile_flags(clang_rt.asan-x86_64
|
||||
${ASAN_CFLAGS}
|
||||
${TARGET_X86_64_CFLAGS}
|
||||
)
|
||||
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
|
||||
${ASAN_SOURCES}
|
||||
$<TARGET_OBJECTS:RTInterception.i386>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.i386>
|
||||
)
|
||||
set_target_compile_flags(clang_rt.asan-i386
|
||||
${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)
|
||||
endif()
|
||||
|
||||
if(LLVM_INCLUDE_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
@ -8,7 +8,7 @@
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := asan
|
||||
SubDirs := mach_override sysinfo
|
||||
SubDirs :=
|
||||
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
|
||||
ObjNames := $(Sources:%.cc=%.o)
|
||||
@ -17,6 +17,8 @@ Implementation := Generic
|
||||
|
||||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard $(Dir)/*.h)
|
||||
Dependencies += $(wildcard $(Dir)/interception/*.h)
|
||||
Dependencies += $(wildcard $(Dir)/interception/mach_override/*.h)
|
||||
|
||||
# Define a convenience variable for all the asan functions.
|
||||
AsanFunctions := $(Sources:%.cc=%)
|
||||
|
@ -58,9 +58,11 @@ ifeq ($(ARCH), arm)
|
||||
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)
|
||||
@ -77,7 +79,6 @@ ARCH=x86_64
|
||||
|
||||
ASAN_STACK=1
|
||||
ASAN_GLOBALS=1
|
||||
ASAN_USE_CALL=1
|
||||
ASAN_SCALE=0 # default will be used
|
||||
ASAN_OFFSET=-1 #default will be used
|
||||
ASAN_UAR=0
|
||||
@ -120,8 +121,8 @@ 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/3.1/lib/$(OS)/libclang_rt.asan-$(ARCH).a
|
||||
LIBASAN_INST_DIR=$(CLANG_BUILD)/lib/clang/3.1/lib/$(OS)
|
||||
# $(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=
|
||||
@ -140,7 +141,6 @@ CLANG_ASAN_CXX=$(CLANG_CXX) \
|
||||
$(BLACKLIST) \
|
||||
-mllvm -asan-stack=$(ASAN_STACK) \
|
||||
-mllvm -asan-globals=$(ASAN_GLOBALS) \
|
||||
-mllvm -asan-use-call=$(ASAN_USE_CALL) \
|
||||
-mllvm -asan-mapping-scale=$(ASAN_SCALE) \
|
||||
-mllvm -asan-mapping-offset-log=$(ASAN_OFFSET) \
|
||||
-mllvm -asan-use-after-return=$(ASAN_UAR) \
|
||||
@ -169,37 +169,25 @@ ifeq ($(ASAN_COMPILER), gcc)
|
||||
ASAN_LD_TAIL=$(LIBASAN_A)
|
||||
endif
|
||||
|
||||
RTL_HDR=asan_allocator.h \
|
||||
asan_internal.h \
|
||||
asan_interceptors.h \
|
||||
asan_interface.h \
|
||||
asan_lock.h \
|
||||
asan_mac.h \
|
||||
asan_mapping.h \
|
||||
asan_stack.h \
|
||||
asan_stats.h \
|
||||
asan_thread.h \
|
||||
asan_thread_registry.h \
|
||||
mach_override/mach_override.h \
|
||||
sysinfo/basictypes.h \
|
||||
sysinfo/sysinfo.h
|
||||
INTERCEPTION=../interception
|
||||
MACH_OVERRIDE=$(INTERCEPTION)/mach_override
|
||||
COMMON=../sanitizer_common
|
||||
|
||||
LIBASAN_OBJ=$(BIN)/asan_rtl$(SUFF).o \
|
||||
$(BIN)/asan_allocator$(SUFF).o \
|
||||
$(BIN)/asan_globals$(SUFF).o \
|
||||
$(BIN)/asan_interceptors$(SUFF).o \
|
||||
$(BIN)/asan_linux$(SUFF).o \
|
||||
$(BIN)/asan_mac$(SUFF).o \
|
||||
$(BIN)/asan_malloc_linux$(SUFF).o \
|
||||
$(BIN)/asan_malloc_mac$(SUFF).o \
|
||||
$(BIN)/asan_poisoning$(SUFF).o \
|
||||
$(BIN)/asan_printf$(SUFF).o \
|
||||
$(BIN)/asan_stack$(SUFF).o \
|
||||
$(BIN)/asan_stats$(SUFF).o \
|
||||
$(BIN)/asan_thread$(SUFF).o \
|
||||
$(BIN)/asan_thread_registry$(SUFF).o \
|
||||
$(BIN)/mach_override/mach_override$(SUFF).o \
|
||||
$(BIN)/sysinfo/sysinfo$(SUFF).o
|
||||
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
|
||||
@ -209,29 +197,28 @@ 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 tests && ./test_output.sh $(CLANG_CXX) $(CLANG_CC)
|
||||
cd output_tests && ./test_output.sh $(CLANG_CXX) $(CLANG_CC) $(FILE_CHECK)
|
||||
|
||||
t64: b64
|
||||
$(BIN)/asan_test64
|
||||
t32: b32
|
||||
$(BIN)/asan_test32
|
||||
|
||||
b64: | $(BIN)
|
||||
b64: | mk_bin_dir
|
||||
$(MAKE) -f $(MAKEFILE) ARCH=x86_64 asan_test asan_benchmarks
|
||||
b32: | $(BIN)
|
||||
b32: | mk_bin_dir
|
||||
$(MAKE) -f $(MAKEFILE) ARCH=i386 asan_test asan_benchmarks
|
||||
|
||||
lib64:
|
||||
$(MAKE) $(MAKEFILE) ARCH=x86_64 lib
|
||||
$(MAKE) -f $(MAKEFILE) ARCH=x86_64 lib
|
||||
lib32:
|
||||
$(MAKE) $(MAKEFILE) ARCH=i386 lib
|
||||
$(MAKE) -f $(MAKEFILE) ARCH=i386 lib
|
||||
|
||||
$(BIN):
|
||||
mk_bin_dir:
|
||||
mkdir -p $(BIN)
|
||||
mkdir -p $(BIN)/sysinfo
|
||||
mkdir -p $(BIN)/mach_override
|
||||
|
||||
clang:
|
||||
cd ../ && llvm/rebuild_clang_and_asan.sh > /dev/null
|
||||
@ -250,29 +237,37 @@ install_clang: | $(INSTALL_DIR)
|
||||
# cp -v $(CLANG_BUILD)/lib/libasan*.a $(INSTALL_DIR)/lib
|
||||
|
||||
$(BIN)/asan_noinst_test$(SUFF).o: tests/asan_noinst_test.cc $(RTL_HDR) $(MAKEFILE)
|
||||
$(CLEANROOM_CXX) $(PIE) $(CFLAGS) $(GTEST_INCLUDE) -I. -g -c $< -O2 -o $@
|
||||
$(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. -g -c $< -O2 -o $@ $(PIE) $(CFLAGS)
|
||||
$(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. -g -c $< -O2 -o $@ -ObjC $(PIE) $(CFLAGS)
|
||||
$(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) $(PIE) $(CFLAGS) -fPIC -c -O2 -fno-exceptions -funwind-tables \
|
||||
-o $@ -g $< -Ithird_party \
|
||||
-DASAN_USE_SYSINFO=1 \
|
||||
$(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) \
|
||||
$(ASAN_FLAGS)
|
||||
-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 \
|
||||
-DASAN_USE_SYSINFO=1 \
|
||||
$(ASAN_FLAGS)
|
||||
|
||||
ifeq ($(OS),darwin)
|
||||
@ -283,17 +278,16 @@ endif
|
||||
|
||||
lib: $(LIBASAN_A)
|
||||
|
||||
$(LIBASAN_A): $(BIN) $(LIBASAN_OBJ) $(MAKEFILE)
|
||||
$(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_test$(SUFF).o \
|
||||
$(BIN)/asan_globals_test$(SUFF).o \
|
||||
$(BIN)/asan_break_optimization$(SUFF).o \
|
||||
$(BIN)/asan_noinst_test$(SUFF).o \
|
||||
$(BIN)/asan_interface_test$(SUFF).o
|
||||
$(BIN)/asan_test$(SUFF).o
|
||||
|
||||
BENCHMARK_OBJECTS=\
|
||||
$(BIN)/asan_benchmarks_test$(SUFF).o \
|
||||
@ -323,11 +317,11 @@ $(GTEST_LIB):
|
||||
mkdir -p $(GTEST_MAKE_DIR) && \
|
||||
cd $(GTEST_MAKE_DIR) && \
|
||||
$(MAKE) -f ../make/Makefile CXXFLAGS="$(PIE) $(CFLAGS) -g -w" \
|
||||
CXX="$(CLANG_ASAN_CXX)"
|
||||
CXX="$(CLANG_CXX)"
|
||||
|
||||
RTL_LINT_FITLER=-readability/casting,-readability/check,-build/include,-build/header_guard,-build/class,-legal/copyright
|
||||
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_FITLER=-readability/casting,-build/include,-legal/copyright,-whitespace/newline,-runtime/sizeof,-runtime/int,-runtime/printf
|
||||
TEST_LINT_FILTER=-readability/casting,-build/include,-legal/copyright,-whitespace/newline,-runtime/sizeof,-runtime/int,-runtime/printf
|
||||
|
||||
LLVM_LINT_FILTER=-,+whitespace
|
||||
|
||||
@ -335,8 +329,10 @@ ADDRESS_SANITIZER_CPP=../../../../lib/Transforms/Instrumentation/AddressSanitize
|
||||
|
||||
lint:
|
||||
third_party/cpplint/cpplint.py --filter=$(LLVM_LINT_FILTER) $(ADDRESS_SANITIZER_CPP)
|
||||
third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FITLER) asan_*.cc asan_*.h
|
||||
third_party/cpplint/cpplint.py --filter=$(TEST_LINT_FITLER) tests/*.cc
|
||||
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
|
||||
@ -348,5 +344,6 @@ get_third_party:
|
||||
|
||||
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-*
|
||||
|
@ -10,7 +10,6 @@ Makefile.mk : Currently a stub for a proper makefile. not usable.
|
||||
Makefile.old : Old out-of-tree makefile, the only usable one so far.
|
||||
asan_*.{cc,h} : Sources of the asan run-time lirbary.
|
||||
mach_override/* : Utility to override functions on Darwin (MIT License).
|
||||
sysinfo/* : Portable utility to iterate over /proc/maps (BSD License).
|
||||
scripts/* : Helper scripts.
|
||||
|
||||
Temporary build instructions (verified on linux):
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -20,7 +20,7 @@
|
||||
|
||||
namespace __asan {
|
||||
|
||||
static const size_t kNumberOfSizeClasses = 255;
|
||||
static const uptr kNumberOfSizeClasses = 255;
|
||||
struct AsanChunk;
|
||||
|
||||
class AsanChunkFifoList {
|
||||
@ -30,23 +30,23 @@ class AsanChunkFifoList {
|
||||
void Push(AsanChunk *n);
|
||||
void PushList(AsanChunkFifoList *q);
|
||||
AsanChunk *Pop();
|
||||
size_t size() { return size_; }
|
||||
uptr size() { return size_; }
|
||||
void clear() {
|
||||
first_ = last_ = NULL;
|
||||
first_ = last_ = 0;
|
||||
size_ = 0;
|
||||
}
|
||||
private:
|
||||
AsanChunk *first_;
|
||||
AsanChunk *last_;
|
||||
size_t size_;
|
||||
uptr size_;
|
||||
};
|
||||
|
||||
struct AsanThreadLocalMallocStorage {
|
||||
explicit AsanThreadLocalMallocStorage(LinkerInitialized x)
|
||||
: quarantine_(x) { }
|
||||
AsanThreadLocalMallocStorage() {
|
||||
CHECK(real_memset);
|
||||
real_memset(this, 0, sizeof(AsanThreadLocalMallocStorage));
|
||||
CHECK(REAL(memset));
|
||||
REAL(memset)(this, 0, sizeof(AsanThreadLocalMallocStorage));
|
||||
}
|
||||
|
||||
AsanChunkFifoList quarantine_;
|
||||
@ -57,11 +57,11 @@ struct AsanThreadLocalMallocStorage {
|
||||
// Fake stack frame contains local variables of one function.
|
||||
// This struct should fit into a stack redzone (32 bytes).
|
||||
struct FakeFrame {
|
||||
uintptr_t magic; // Modified by the instrumented code.
|
||||
uintptr_t descr; // Modified by the instrumented code.
|
||||
uptr magic; // Modified by the instrumented code.
|
||||
uptr descr; // Modified by the instrumented code.
|
||||
FakeFrame *next;
|
||||
uint64_t real_stack : 48;
|
||||
uint64_t size_minus_one : 16;
|
||||
u64 real_stack : 48;
|
||||
u64 size_minus_one : 16;
|
||||
};
|
||||
|
||||
struct FakeFrameFifo {
|
||||
@ -100,58 +100,60 @@ class FakeStack {
|
||||
public:
|
||||
FakeStack();
|
||||
explicit FakeStack(LinkerInitialized) {}
|
||||
void Init(size_t stack_size);
|
||||
void Init(uptr stack_size);
|
||||
void StopUsingFakeStack() { alive_ = false; }
|
||||
void Cleanup();
|
||||
uintptr_t AllocateStack(size_t size, size_t real_stack);
|
||||
static void OnFree(size_t ptr, size_t size, size_t real_stack);
|
||||
uptr AllocateStack(uptr size, uptr real_stack);
|
||||
static void OnFree(uptr ptr, uptr size, uptr real_stack);
|
||||
// Return the bottom of the maped region.
|
||||
uintptr_t AddrIsInFakeStack(uintptr_t addr);
|
||||
uptr AddrIsInFakeStack(uptr addr);
|
||||
bool StackSize() { return stack_size_; }
|
||||
private:
|
||||
static const size_t kMinStackFrameSizeLog = 9; // Min frame is 512B.
|
||||
static const size_t kMaxStackFrameSizeLog = 16; // Max stack frame is 64K.
|
||||
static const size_t kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog;
|
||||
static const size_t kNumberOfSizeClasses =
|
||||
static const uptr kMinStackFrameSizeLog = 9; // Min frame is 512B.
|
||||
static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K.
|
||||
static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog;
|
||||
static const uptr kNumberOfSizeClasses =
|
||||
kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1;
|
||||
|
||||
bool AddrIsInSizeClass(uintptr_t addr, size_t size_class);
|
||||
bool AddrIsInSizeClass(uptr addr, uptr size_class);
|
||||
|
||||
// Each size class should be large enough to hold all frames.
|
||||
size_t ClassMmapSize(size_t size_class);
|
||||
uptr ClassMmapSize(uptr size_class);
|
||||
|
||||
size_t ClassSize(size_t size_class) {
|
||||
uptr ClassSize(uptr size_class) {
|
||||
return 1UL << (size_class + kMinStackFrameSizeLog);
|
||||
}
|
||||
|
||||
void DeallocateFrame(FakeFrame *fake_frame);
|
||||
|
||||
size_t ComputeSizeClass(size_t alloc_size);
|
||||
void AllocateOneSizeClass(size_t size_class);
|
||||
uptr ComputeSizeClass(uptr alloc_size);
|
||||
void AllocateOneSizeClass(uptr size_class);
|
||||
|
||||
size_t stack_size_;
|
||||
uptr stack_size_;
|
||||
bool alive_;
|
||||
|
||||
uintptr_t allocated_size_classes_[kNumberOfSizeClasses];
|
||||
uptr allocated_size_classes_[kNumberOfSizeClasses];
|
||||
FakeFrameFifo size_classes_[kNumberOfSizeClasses];
|
||||
FakeFrameLifo call_stack_;
|
||||
};
|
||||
|
||||
void *asan_memalign(size_t alignment, size_t size, AsanStackTrace *stack);
|
||||
void *asan_memalign(uptr alignment, uptr size, AsanStackTrace *stack);
|
||||
void asan_free(void *ptr, AsanStackTrace *stack);
|
||||
|
||||
void *asan_malloc(size_t size, AsanStackTrace *stack);
|
||||
void *asan_calloc(size_t nmemb, size_t size, AsanStackTrace *stack);
|
||||
void *asan_realloc(void *p, size_t size, AsanStackTrace *stack);
|
||||
void *asan_valloc(size_t size, AsanStackTrace *stack);
|
||||
void *asan_pvalloc(size_t size, AsanStackTrace *stack);
|
||||
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);
|
||||
|
||||
int asan_posix_memalign(void **memptr, size_t alignment, size_t size,
|
||||
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
|
||||
AsanStackTrace *stack);
|
||||
uptr asan_malloc_usable_size(void *ptr, AsanStackTrace *stack);
|
||||
|
||||
size_t __asan_mz_size(const void *ptr);
|
||||
void __asan_mz_force_lock();
|
||||
void __asan_mz_force_unlock();
|
||||
void DescribeHeapAddress(uintptr_t addr, size_t access_size);
|
||||
uptr asan_mz_size(const void *ptr);
|
||||
void asan_mz_force_lock();
|
||||
void asan_mz_force_unlock();
|
||||
void DescribeHeapAddress(uptr addr, uptr access_size);
|
||||
|
||||
} // namespace __asan
|
||||
#endif // ASAN_ALLOCATOR_H
|
||||
|
97
lib/asan/asan_flags.h
Normal file
97
lib/asan/asan_flags.h
Normal file
@ -0,0 +1,97 @@
|
||||
//===-- asan_flags.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 runtime flags.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ASAN_FLAGS_H
|
||||
#define ASAN_FLAGS_H
|
||||
|
||||
#include "sanitizer_common/sanitizer_interface_defs.h"
|
||||
|
||||
// ASan flag values can be defined in three ways:
|
||||
// 1) initialized with default values at startup.
|
||||
// 2) overriden from string returned by user-specified function
|
||||
// __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 {
|
||||
// Size (in bytes) of quarantine used to detect use-after-free errors.
|
||||
// Lower value may reduce memory usage but increase the chance of
|
||||
// false negatives.
|
||||
int quarantine_size;
|
||||
// If set, uses in-process symbolizer from common sanitizer runtime.
|
||||
bool symbolize;
|
||||
// Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).
|
||||
int verbosity;
|
||||
// Size (in bytes) of redzones around heap objects.
|
||||
// Requirement: redzone >= 32, is a power of two.
|
||||
int redzone;
|
||||
// If set, prints some debugging information and does additional checks.
|
||||
bool debug;
|
||||
// Controls the way to handle globals (0 - don't detect buffer overflow
|
||||
// on globals, 1 - detect buffer overflow, 2 - print data about registered
|
||||
// globals).
|
||||
int report_globals;
|
||||
// Max number of stack frames kept for each allocation.
|
||||
int malloc_context_size;
|
||||
// If set, uses custom wrappers and replacements for libc string functions
|
||||
// to find more errors.
|
||||
bool replace_str;
|
||||
// If set, uses custom wrappers for memset/memcpy/memmove intinsics.
|
||||
bool replace_intrin;
|
||||
// Used on Mac only. See comments in asan_mac.cc and asan_malloc_mac.cc.
|
||||
bool replace_cfallocator;
|
||||
// Used on Mac only.
|
||||
bool mac_ignore_invalid_free;
|
||||
// ASan allocator flag. See asan_allocator.cc.
|
||||
bool use_fake_stack;
|
||||
// ASan allocator flag. Sets the maximal size of allocation request
|
||||
// that would return memory filled with zero bytes.
|
||||
int max_malloc_fill_size;
|
||||
// Override exit status if something was reported.
|
||||
int exitcode;
|
||||
// If set, user may manually mark memory regions as poisoned or unpoisoned.
|
||||
bool allow_user_poisoning;
|
||||
// Number of seconds to sleep between printing an error report and
|
||||
// terminating application. Useful for debug purposes (when one needs
|
||||
// to attach gdb, for example).
|
||||
int sleep_before_dying;
|
||||
// If set, registers ASan custom segv handler.
|
||||
bool handle_segv;
|
||||
// If set, uses alternate stack for signal handling.
|
||||
bool use_sigaltstack;
|
||||
// Allow the users to work around the bug in Nvidia drivers prior to 295.*.
|
||||
bool check_malloc_usable_size;
|
||||
// If set, explicitly unmaps (huge) shadow at exit.
|
||||
bool unmap_shadow_on_exit;
|
||||
// If set, calls abort() instead of _exit() after printing an error report.
|
||||
bool abort_on_error;
|
||||
// If set, prints ASan exit stats even after program terminates successfully.
|
||||
bool atexit;
|
||||
// By default, disable core dumper on 64-bit - it makes little sense
|
||||
// to dump 16T+ core.
|
||||
bool disable_core;
|
||||
};
|
||||
|
||||
Flags *flags();
|
||||
void InitializeFlags(Flags *f, const char *env);
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // ASAN_FLAGS_H
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_globals.cc -----------------------------------------*- C++ -*-===//
|
||||
//===-- asan_globals.cc ---------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -36,16 +36,16 @@ static ListOfGlobals *list_of_globals;
|
||||
static LowLevelAllocator allocator_for_globals(LINKER_INITIALIZED);
|
||||
|
||||
void PoisonRedZones(const Global &g) {
|
||||
size_t shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE;
|
||||
uptr shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE;
|
||||
CHECK(shadow_rz_size == 1 || shadow_rz_size == 2 || shadow_rz_size == 4);
|
||||
// full right redzone
|
||||
size_t g_aligned_size = kGlobalAndStackRedzone *
|
||||
uptr g_aligned_size = kGlobalAndStackRedzone *
|
||||
((g.size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone);
|
||||
PoisonShadow(g.beg + g_aligned_size,
|
||||
kGlobalAndStackRedzone, kAsanGlobalRedzoneMagic);
|
||||
if ((g.size % kGlobalAndStackRedzone) != 0) {
|
||||
// partial right redzone
|
||||
uint64_t g_aligned_down_size = kGlobalAndStackRedzone *
|
||||
u64 g_aligned_down_size = kGlobalAndStackRedzone *
|
||||
(g.size / kGlobalAndStackRedzone);
|
||||
CHECK(g_aligned_down_size == g_aligned_size - kGlobalAndStackRedzone);
|
||||
PoisonShadowPartialRightRedzone(g.beg + g_aligned_down_size,
|
||||
@ -55,47 +55,47 @@ void PoisonRedZones(const Global &g) {
|
||||
}
|
||||
}
|
||||
|
||||
static size_t GetAlignedSize(size_t size) {
|
||||
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 (size_t p = g.beg; p < g.beg + g.size - 1; p++) {
|
||||
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;
|
||||
Printf(" '%s' is ascii string '%s'\n", g.name, g.beg);
|
||||
AsanPrintf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg);
|
||||
}
|
||||
|
||||
bool DescribeAddrIfMyRedZone(const Global &g, uintptr_t addr) {
|
||||
bool DescribeAddrIfMyRedZone(const Global &g, uptr addr) {
|
||||
if (addr < g.beg - kGlobalAndStackRedzone) return false;
|
||||
if (addr >= g.beg + g.size_with_redzone) return false;
|
||||
Printf("%p is located ", addr);
|
||||
AsanPrintf("%p is located ", (void*)addr);
|
||||
if (addr < g.beg) {
|
||||
Printf("%d bytes to the left", g.beg - addr);
|
||||
AsanPrintf("%zd bytes to the left", g.beg - addr);
|
||||
} else if (addr >= g.beg + g.size) {
|
||||
Printf("%d bytes to the right", addr - (g.beg + g.size));
|
||||
AsanPrintf("%zd bytes to the right", addr - (g.beg + g.size));
|
||||
} else {
|
||||
Printf("%d bytes inside", addr - g.beg); // Can it happen?
|
||||
AsanPrintf("%zd bytes inside", addr - g.beg); // Can it happen?
|
||||
}
|
||||
Printf(" of global variable '%s' (0x%lx) of size %ld\n",
|
||||
g.name, g.beg, g.size);
|
||||
AsanPrintf(" of global variable '%s' (0x%zx) of size %zu\n",
|
||||
g.name, g.beg, g.size);
|
||||
PrintIfASCII(g);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DescribeAddrIfGlobal(uintptr_t addr) {
|
||||
if (!FLAG_report_globals) return false;
|
||||
bool DescribeAddrIfGlobal(uptr addr) {
|
||||
if (!flags()->report_globals) return false;
|
||||
ScopedLock lock(&mu_for_globals);
|
||||
bool res = false;
|
||||
for (ListOfGlobals *l = list_of_globals; l; l = l->next) {
|
||||
const Global &g = *l->g;
|
||||
if (FLAG_report_globals >= 2)
|
||||
Printf("Search Global: beg=%p size=%ld name=%s\n",
|
||||
g.beg, g.size, g.name);
|
||||
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);
|
||||
}
|
||||
return res;
|
||||
@ -106,7 +106,7 @@ bool DescribeAddrIfGlobal(uintptr_t addr) {
|
||||
// so we store the globals in a map.
|
||||
static void RegisterGlobal(const Global *g) {
|
||||
CHECK(asan_inited);
|
||||
CHECK(FLAG_report_globals);
|
||||
CHECK(flags()->report_globals);
|
||||
CHECK(AddrIsInMem(g->beg));
|
||||
CHECK(AddrIsAlignedByGranularity(g->beg));
|
||||
CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
|
||||
@ -116,14 +116,14 @@ static void RegisterGlobal(const Global *g) {
|
||||
l->g = g;
|
||||
l->next = list_of_globals;
|
||||
list_of_globals = l;
|
||||
if (FLAG_report_globals >= 2)
|
||||
Report("Added Global: beg=%p size=%ld name=%s\n",
|
||||
g->beg, g->size, g->name);
|
||||
if (flags()->report_globals >= 2)
|
||||
Report("Added Global: beg=%p size=%zu name=%s\n",
|
||||
(void*)g->beg, g->size, g->name);
|
||||
}
|
||||
|
||||
static void UnregisterGlobal(const Global *g) {
|
||||
CHECK(asan_inited);
|
||||
CHECK(FLAG_report_globals);
|
||||
CHECK(flags()->report_globals);
|
||||
CHECK(AddrIsInMem(g->beg));
|
||||
CHECK(AddrIsAlignedByGranularity(g->beg));
|
||||
CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
|
||||
@ -139,9 +139,9 @@ static void UnregisterGlobal(const Global *g) {
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
// Register one global with a default redzone.
|
||||
void __asan_register_global(uintptr_t addr, size_t size,
|
||||
void __asan_register_global(uptr addr, uptr size,
|
||||
const char *name) {
|
||||
if (!FLAG_report_globals) return;
|
||||
if (!flags()->report_globals) return;
|
||||
ScopedLock lock(&mu_for_globals);
|
||||
Global *g = (Global *)allocator_for_globals.Allocate(sizeof(Global));
|
||||
g->beg = addr;
|
||||
@ -152,20 +152,20 @@ void __asan_register_global(uintptr_t addr, size_t size,
|
||||
}
|
||||
|
||||
// Register an array of globals.
|
||||
void __asan_register_globals(__asan_global *globals, size_t n) {
|
||||
if (!FLAG_report_globals) return;
|
||||
void __asan_register_globals(__asan_global *globals, uptr n) {
|
||||
if (!flags()->report_globals) return;
|
||||
ScopedLock lock(&mu_for_globals);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
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.
|
||||
void __asan_unregister_globals(__asan_global *globals, size_t n) {
|
||||
if (!FLAG_report_globals) return;
|
||||
void __asan_unregister_globals(__asan_global *globals, uptr n) {
|
||||
if (!flags()->report_globals) return;
|
||||
ScopedLock lock(&mu_for_globals);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
UnregisterGlobal(&globals[i]);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_interceptors.cc ------------------------------------*- C++ -*-===//
|
||||
//===-- asan_interceptors.cc ----------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -9,7 +9,7 @@
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Intercept various libc functions to catch buggy memory accesses there.
|
||||
// Intercept various libc functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_interceptors.h"
|
||||
|
||||
@ -19,40 +19,107 @@
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_stats.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "interception/interception.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dlfcn.h>
|
||||
#include <string.h>
|
||||
#include <strings.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 {
|
||||
|
||||
index_f real_index;
|
||||
memcmp_f real_memcmp;
|
||||
memcpy_f real_memcpy;
|
||||
memmove_f real_memmove;
|
||||
memset_f real_memset;
|
||||
strcasecmp_f real_strcasecmp;
|
||||
strcat_f real_strcat;
|
||||
strchr_f real_strchr;
|
||||
strcmp_f real_strcmp;
|
||||
strcpy_f real_strcpy;
|
||||
strdup_f real_strdup;
|
||||
strlen_f real_strlen;
|
||||
strncasecmp_f real_strncasecmp;
|
||||
strncmp_f real_strncmp;
|
||||
strncpy_f real_strncpy;
|
||||
strnlen_f real_strnlen;
|
||||
|
||||
// Instruments read/write access to a single byte in memory.
|
||||
// On error calls __asan_report_error, which aborts the program.
|
||||
__attribute__((noinline))
|
||||
static void AccessAddress(uintptr_t address, bool isWrite) {
|
||||
if (__asan_address_is_poisoned((void*)address)) {
|
||||
GET_BP_PC_SP;
|
||||
__asan_report_error(pc, bp, sp, address, isWrite, /* access_size */ 1);
|
||||
}
|
||||
}
|
||||
#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
|
||||
@ -64,9 +131,9 @@ static void AccessAddress(uintptr_t address, bool isWrite) {
|
||||
// checking the first and the last byte of a range.
|
||||
#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \
|
||||
if (size > 0) { \
|
||||
uintptr_t ptr = (uintptr_t)(offset); \
|
||||
AccessAddress(ptr, isWrite); \
|
||||
AccessAddress(ptr + (size) - 1, isWrite); \
|
||||
uptr ptr = (uptr)(offset); \
|
||||
ACCESS_ADDRESS(ptr, isWrite); \
|
||||
ACCESS_ADDRESS(ptr + (size) - 1, isWrite); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@ -81,17 +148,17 @@ static void AccessAddress(uintptr_t address, bool isWrite) {
|
||||
// Behavior of functions like "memcpy" or "strcpy" is undefined
|
||||
// if memory intervals overlap. We report error in this case.
|
||||
// Macro is used to avoid creation of new frames.
|
||||
static inline bool RangesOverlap(const char *offset1, size_t length1,
|
||||
const char *offset2, size_t length2) {
|
||||
static inline bool RangesOverlap(const char *offset1, uptr length1,
|
||||
const char *offset2, uptr length2) {
|
||||
return !((offset1 + length1 <= offset2) || (offset2 + length2 <= offset1));
|
||||
}
|
||||
#define CHECK_RANGES_OVERLAP(name, _offset1, length1, _offset2, length2) do { \
|
||||
const char *offset1 = (const char*)_offset1; \
|
||||
const char *offset2 = (const char*)_offset2; \
|
||||
if (RangesOverlap(offset1, length1, offset2, length2)) { \
|
||||
Report("ERROR: AddressSanitizer %s-param-overlap: " \
|
||||
"memory ranges [%p,%p) and [%p, %p) overlap\n", \
|
||||
name, offset1, offset1 + length1, offset2, 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(); \
|
||||
} \
|
||||
@ -104,64 +171,13 @@ static inline bool RangesOverlap(const char *offset1, size_t length1,
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
size_t internal_strlen(const char *s) {
|
||||
size_t i = 0;
|
||||
while (s[i]) i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
size_t internal_strnlen(const char *s, size_t maxlen) {
|
||||
if (real_strnlen != NULL) {
|
||||
return real_strnlen(s, maxlen);
|
||||
static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
|
||||
#if ASAN_INTERCEPT_STRNLEN
|
||||
if (REAL(strnlen) != 0) {
|
||||
return REAL(strnlen)(s, maxlen);
|
||||
}
|
||||
size_t i = 0;
|
||||
while (i < maxlen && s[i]) i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
void* internal_memchr(const void* s, int c, size_t n) {
|
||||
const char* t = (char*)s;
|
||||
for (size_t i = 0; i < n; ++i, ++t)
|
||||
if (*t == c)
|
||||
return (void*)t;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int internal_memcmp(const void* s1, const void* s2, size_t n) {
|
||||
const char* t1 = (char*)s1;
|
||||
const char* t2 = (char*)s2;
|
||||
for (size_t i = 0; i < n; ++i, ++t1, ++t2)
|
||||
if (*t1 != *t2)
|
||||
return *t1 < *t2 ? -1 : 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InitializeAsanInterceptors() {
|
||||
#ifndef __APPLE__
|
||||
INTERCEPT_FUNCTION(index);
|
||||
#else
|
||||
OVERRIDE_FUNCTION(index, WRAP(strchr));
|
||||
#endif
|
||||
INTERCEPT_FUNCTION(memcmp);
|
||||
INTERCEPT_FUNCTION(memcpy);
|
||||
INTERCEPT_FUNCTION(memmove);
|
||||
INTERCEPT_FUNCTION(memset);
|
||||
INTERCEPT_FUNCTION(strcasecmp);
|
||||
INTERCEPT_FUNCTION(strcat); // NOLINT
|
||||
INTERCEPT_FUNCTION(strchr);
|
||||
INTERCEPT_FUNCTION(strcmp);
|
||||
INTERCEPT_FUNCTION(strcpy); // NOLINT
|
||||
INTERCEPT_FUNCTION(strdup);
|
||||
INTERCEPT_FUNCTION(strlen);
|
||||
INTERCEPT_FUNCTION(strncasecmp);
|
||||
INTERCEPT_FUNCTION(strncmp);
|
||||
INTERCEPT_FUNCTION(strncpy);
|
||||
#ifndef __APPLE__
|
||||
INTERCEPT_FUNCTION(strnlen);
|
||||
#endif
|
||||
if (FLAG_v > 0) {
|
||||
Printf("AddressSanitizer: libc interceptors initialized\n");
|
||||
}
|
||||
return internal_strnlen(s, maxlen);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
@ -169,22 +185,125 @@ void InitializeAsanInterceptors() {
|
||||
// ---------------------- Wrappers ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
|
||||
AsanThread *t = (AsanThread*)arg;
|
||||
asanThreadRegistry().SetCurrent(t);
|
||||
return t->ThreadStart();
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
INTERCEPTOR(int, pthread_create, void *thread,
|
||||
void *attr, void *(*start_routine)(void*), void *arg) {
|
||||
GET_STACK_TRACE_HERE(kStackTraceMax);
|
||||
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
|
||||
|
||||
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
|
||||
INTERCEPTOR(void*, signal, int signum, void *handler) {
|
||||
if (!AsanInterceptsSignal(signum)) {
|
||||
return REAL(signal)(signum, handler);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact) {
|
||||
if (!AsanInterceptsSignal(signum)) {
|
||||
return REAL(sigaction)(signum, act, oldact);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#elif ASAN_POSIX
|
||||
// We need to have defined REAL(sigaction) on posix systems.
|
||||
DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact);
|
||||
#endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
|
||||
|
||||
INTERCEPTOR(void, longjmp, void *env, int val) {
|
||||
__asan_handle_no_return();
|
||||
REAL(longjmp)(env, val);
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
INTERCEPTOR(void, _longjmp, void *env, int val) {
|
||||
__asan_handle_no_return();
|
||||
REAL(_longjmp)(env, val);
|
||||
}
|
||||
|
||||
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__
|
||||
|
||||
INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
|
||||
CHECK(REAL(__cxa_throw));
|
||||
__asan_handle_no_return();
|
||||
REAL(__cxa_throw)(a, b, c);
|
||||
}
|
||||
#endif
|
||||
|
||||
// intercept mlock and friends.
|
||||
// Since asan maps 16T of RAM, mlock is completely unfriendly to asan.
|
||||
// All functions return 0 (success).
|
||||
static void MlockIsUnsupported() {
|
||||
static bool printed = 0;
|
||||
if (printed) return;
|
||||
printed = true;
|
||||
Printf("INFO: AddressSanitizer ignores mlock/mlockall/munlock/munlockall\n");
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
int mlock(const void *addr, uptr len) {
|
||||
MlockIsUnsupported();
|
||||
return 0;
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
int munlock(const void *addr, uptr len) {
|
||||
MlockIsUnsupported();
|
||||
return 0;
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
int mlockall(int flags) {
|
||||
MlockIsUnsupported();
|
||||
return 0;
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
int munlockall(void) {
|
||||
MlockIsUnsupported();
|
||||
return 0;
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
static inline int CharCmp(unsigned char c1, unsigned char c2) {
|
||||
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
|
||||
}
|
||||
|
||||
static inline int CharCaseCmp(unsigned char c1, unsigned char c2) {
|
||||
int c1_low = tolower(c1);
|
||||
int c2_low = tolower(c2);
|
||||
int c1_low = ToLower(c1);
|
||||
int c2_low = ToLower(c2);
|
||||
return c1_low - c2_low;
|
||||
}
|
||||
|
||||
int WRAP(memcmp)(const void *a1, const void *a2, size_t size) {
|
||||
INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
|
||||
ENSURE_ASAN_INITED();
|
||||
unsigned char c1 = 0, c2 = 0;
|
||||
const unsigned char *s1 = (const unsigned char*)a1;
|
||||
const unsigned char *s2 = (const unsigned char*)a2;
|
||||
size_t i;
|
||||
uptr i;
|
||||
for (i = 0; i < size; i++) {
|
||||
c1 = s1[i];
|
||||
c2 = s2[i];
|
||||
@ -195,65 +314,70 @@ int WRAP(memcmp)(const void *a1, const void *a2, size_t size) {
|
||||
return CharCmp(c1, c2);
|
||||
}
|
||||
|
||||
void *WRAP(memcpy)(void *to, const void *from, size_t size) {
|
||||
INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
|
||||
// memcpy is called during __asan_init() from the internals
|
||||
// of printf(...).
|
||||
if (asan_init_is_running) {
|
||||
return real_memcpy(to, from, size);
|
||||
return REAL(memcpy)(to, from, size);
|
||||
}
|
||||
ENSURE_ASAN_INITED();
|
||||
if (FLAG_replace_intrin) {
|
||||
CHECK_RANGES_OVERLAP("memcpy", to, size, from, size);
|
||||
if (flags()->replace_intrin) {
|
||||
if (to != from) {
|
||||
// We do not treat memcpy with to==from as a bug.
|
||||
// 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);
|
||||
}
|
||||
return real_memcpy(to, from, size);
|
||||
return REAL(memcpy)(to, from, size);
|
||||
}
|
||||
|
||||
void *WRAP(memmove)(void *to, const void *from, size_t size) {
|
||||
ENSURE_ASAN_INITED();
|
||||
if (FLAG_replace_intrin) {
|
||||
ASAN_WRITE_RANGE(from, size);
|
||||
ASAN_READ_RANGE(to, size);
|
||||
}
|
||||
return real_memmove(to, from, size);
|
||||
}
|
||||
|
||||
void *WRAP(memset)(void *block, int c, size_t size) {
|
||||
// memset is called inside INTERCEPT_FUNCTION on Mac.
|
||||
INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) {
|
||||
if (asan_init_is_running) {
|
||||
return real_memset(block, c, size);
|
||||
return REAL(memmove)(to, from, size);
|
||||
}
|
||||
ENSURE_ASAN_INITED();
|
||||
if (FLAG_replace_intrin) {
|
||||
if (flags()->replace_intrin) {
|
||||
ASAN_WRITE_RANGE(from, size);
|
||||
ASAN_READ_RANGE(to, size);
|
||||
}
|
||||
return REAL(memmove)(to, from, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, memset, void *block, int c, uptr size) {
|
||||
// memset is called inside Printf.
|
||||
if (asan_init_is_running) {
|
||||
return REAL(memset)(block, c, size);
|
||||
}
|
||||
ENSURE_ASAN_INITED();
|
||||
if (flags()->replace_intrin) {
|
||||
ASAN_WRITE_RANGE(block, size);
|
||||
}
|
||||
return real_memset(block, c, size);
|
||||
return REAL(memset)(block, c, size);
|
||||
}
|
||||
|
||||
// Note that on Linux index and strchr are definined differently depending on
|
||||
// the compiler (gcc vs clang).
|
||||
// see __CORRECT_ISO_CPP_STRING_H_PROTO in /usr/include/string.h
|
||||
|
||||
#ifndef __APPLE__
|
||||
char *WRAP(index)(const char *str, int c)
|
||||
__attribute__((alias(WRAPPER_NAME(strchr))));
|
||||
#endif
|
||||
|
||||
char *WRAP(strchr)(const char *str, int c) {
|
||||
INTERCEPTOR(char*, strchr, const char *str, int c) {
|
||||
ENSURE_ASAN_INITED();
|
||||
char *result = real_strchr(str, c);
|
||||
if (FLAG_replace_str) {
|
||||
size_t bytes_read = (result ? result - str : real_strlen(str)) + 1;
|
||||
char *result = REAL(strchr)(str, c);
|
||||
if (flags()->replace_str) {
|
||||
uptr bytes_read = (result ? result - str : REAL(strlen)(str)) + 1;
|
||||
ASAN_READ_RANGE(str, bytes_read);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int WRAP(strcasecmp)(const char *s1, const char *s2) {
|
||||
#ifdef __linux__
|
||||
INTERCEPTOR(char*, index, const char *string, int c)
|
||||
ALIAS(WRAPPER_NAME(strchr));
|
||||
#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;
|
||||
size_t i;
|
||||
uptr i;
|
||||
for (i = 0; ; i++) {
|
||||
c1 = (unsigned char)s1[i];
|
||||
c2 = (unsigned char)s2[i];
|
||||
@ -264,29 +388,44 @@ int WRAP(strcasecmp)(const char *s1, const char *s2) {
|
||||
return CharCaseCmp(c1, c2);
|
||||
}
|
||||
|
||||
char *WRAP(strcat)(char *to, const char *from) { // NOLINT
|
||||
INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT
|
||||
ENSURE_ASAN_INITED();
|
||||
if (FLAG_replace_str) {
|
||||
size_t from_length = real_strlen(from);
|
||||
if (flags()->replace_str) {
|
||||
uptr from_length = REAL(strlen)(from);
|
||||
ASAN_READ_RANGE(from, from_length + 1);
|
||||
if (from_length > 0) {
|
||||
size_t to_length = real_strlen(to);
|
||||
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);
|
||||
}
|
||||
}
|
||||
return real_strcat(to, from);
|
||||
return REAL(strcat)(to, from); // NOLINT
|
||||
}
|
||||
|
||||
int WRAP(strcmp)(const char *s1, const char *s2) {
|
||||
// strcmp is called from malloc_default_purgeable_zone()
|
||||
// in __asan::ReplaceSystemAlloc() on Mac.
|
||||
if (asan_init_is_running) {
|
||||
return real_strcmp(s1, s2);
|
||||
INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
|
||||
ENSURE_ASAN_INITED();
|
||||
if (flags()->replace_str && size > 0) {
|
||||
uptr from_length = MaybeRealStrnlen(from, size);
|
||||
ASAN_READ_RANGE(from, Min(size, 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 (from_length > 0) {
|
||||
CHECK_RANGES_OVERLAP("strncat", to, to_length + 1,
|
||||
from, Min(size, from_length + 1));
|
||||
}
|
||||
}
|
||||
return REAL(strncat)(to, from, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
|
||||
if (!asan_inited) {
|
||||
return internal_strcmp(s1, s2);
|
||||
}
|
||||
ENSURE_ASAN_INITED();
|
||||
unsigned char c1, c2;
|
||||
size_t i;
|
||||
uptr i;
|
||||
for (i = 0; ; i++) {
|
||||
c1 = (unsigned char)s1[i];
|
||||
c2 = (unsigned char)s2[i];
|
||||
@ -297,67 +436,68 @@ int WRAP(strcmp)(const char *s1, const char *s2) {
|
||||
return CharCmp(c1, c2);
|
||||
}
|
||||
|
||||
char *WRAP(strcpy)(char *to, const char *from) { // NOLINT
|
||||
INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
|
||||
// strcpy is called from malloc_default_purgeable_zone()
|
||||
// in __asan::ReplaceSystemAlloc() on Mac.
|
||||
if (asan_init_is_running) {
|
||||
return real_strcpy(to, from);
|
||||
return REAL(strcpy)(to, from); // NOLINT
|
||||
}
|
||||
ENSURE_ASAN_INITED();
|
||||
if (FLAG_replace_str) {
|
||||
size_t from_size = real_strlen(from) + 1;
|
||||
if (flags()->replace_str) {
|
||||
uptr from_size = REAL(strlen)(from) + 1;
|
||||
CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size);
|
||||
ASAN_READ_RANGE(from, from_size);
|
||||
ASAN_WRITE_RANGE(to, from_size);
|
||||
}
|
||||
return real_strcpy(to, from);
|
||||
return REAL(strcpy)(to, from); // NOLINT
|
||||
}
|
||||
|
||||
char *WRAP(strdup)(const char *s) {
|
||||
INTERCEPTOR(char*, strdup, const char *s) {
|
||||
ENSURE_ASAN_INITED();
|
||||
if (FLAG_replace_str) {
|
||||
size_t length = real_strlen(s);
|
||||
if (flags()->replace_str) {
|
||||
uptr length = REAL(strlen)(s);
|
||||
ASAN_READ_RANGE(s, length + 1);
|
||||
}
|
||||
return real_strdup(s);
|
||||
return REAL(strdup)(s);
|
||||
}
|
||||
|
||||
size_t WRAP(strlen)(const char *s) {
|
||||
INTERCEPTOR(uptr, strlen, const char *s) {
|
||||
// strlen is called from malloc_default_purgeable_zone()
|
||||
// in __asan::ReplaceSystemAlloc() on Mac.
|
||||
if (asan_init_is_running) {
|
||||
return real_strlen(s);
|
||||
return REAL(strlen)(s);
|
||||
}
|
||||
ENSURE_ASAN_INITED();
|
||||
size_t length = real_strlen(s);
|
||||
if (FLAG_replace_str) {
|
||||
uptr length = REAL(strlen)(s);
|
||||
if (flags()->replace_str) {
|
||||
ASAN_READ_RANGE(s, length + 1);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
int WRAP(strncasecmp)(const char *s1, const char *s2, size_t size) {
|
||||
INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, uptr n) {
|
||||
ENSURE_ASAN_INITED();
|
||||
unsigned char c1 = 0, c2 = 0;
|
||||
size_t i;
|
||||
for (i = 0; i < size; i++) {
|
||||
uptr i;
|
||||
for (i = 0; i < n; i++) {
|
||||
c1 = (unsigned char)s1[i];
|
||||
c2 = (unsigned char)s2[i];
|
||||
if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
|
||||
}
|
||||
ASAN_READ_RANGE(s1, Min(i + 1, size));
|
||||
ASAN_READ_RANGE(s2, Min(i + 1, size));
|
||||
ASAN_READ_RANGE(s1, Min(i + 1, n));
|
||||
ASAN_READ_RANGE(s2, Min(i + 1, n));
|
||||
return CharCaseCmp(c1, c2);
|
||||
}
|
||||
|
||||
int WRAP(strncmp)(const char *s1, const char *s2, size_t size) {
|
||||
INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) {
|
||||
// strncmp is called from malloc_default_purgeable_zone()
|
||||
// in __asan::ReplaceSystemAlloc() on Mac.
|
||||
if (asan_init_is_running) {
|
||||
return real_strncmp(s1, s2, size);
|
||||
return REAL(strncmp)(s1, s2, size);
|
||||
}
|
||||
ENSURE_ASAN_INITED();
|
||||
unsigned char c1 = 0, c2 = 0;
|
||||
size_t i;
|
||||
uptr i;
|
||||
for (i = 0; i < size; i++) {
|
||||
c1 = (unsigned char)s1[i];
|
||||
c2 = (unsigned char)s2[i];
|
||||
@ -368,24 +508,234 @@ int WRAP(strncmp)(const char *s1, const char *s2, size_t size) {
|
||||
return CharCmp(c1, c2);
|
||||
}
|
||||
|
||||
char *WRAP(strncpy)(char *to, const char *from, size_t size) {
|
||||
INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) {
|
||||
ENSURE_ASAN_INITED();
|
||||
if (FLAG_replace_str) {
|
||||
size_t from_size = Min(size, internal_strnlen(from, size) + 1);
|
||||
if (flags()->replace_str) {
|
||||
uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1);
|
||||
CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size);
|
||||
ASAN_READ_RANGE(from, from_size);
|
||||
ASAN_WRITE_RANGE(to, size);
|
||||
}
|
||||
return real_strncpy(to, from, size);
|
||||
return REAL(strncpy)(to, from, size);
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
size_t WRAP(strnlen)(const char *s, size_t maxlen) {
|
||||
#if ASAN_INTERCEPT_STRNLEN
|
||||
INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) {
|
||||
ENSURE_ASAN_INITED();
|
||||
size_t length = real_strnlen(s, maxlen);
|
||||
if (FLAG_replace_str) {
|
||||
uptr length = REAL(strnlen)(s, maxlen);
|
||||
if (flags()->replace_str) {
|
||||
ASAN_READ_RANGE(s, Min(length + 1, maxlen));
|
||||
}
|
||||
return length;
|
||||
}
|
||||
#endif // ASAN_INTERCEPT_STRNLEN
|
||||
|
||||
static inline bool IsValidStrtolBase(int base) {
|
||||
return (base == 0) || (2 <= base && base <= 36);
|
||||
}
|
||||
|
||||
static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) {
|
||||
CHECK(endptr != 0);
|
||||
if (nptr == *endptr) {
|
||||
// No digits were found at strtol call, we need to find out the last
|
||||
// symbol accessed by strtoll on our own.
|
||||
// We get this symbol by skipping leading blanks and optional +/- sign.
|
||||
while (IsSpace(*nptr)) nptr++;
|
||||
if (*nptr == '+' || *nptr == '-') nptr++;
|
||||
*endptr = (char*)nptr;
|
||||
}
|
||||
CHECK(*endptr >= nptr);
|
||||
}
|
||||
|
||||
INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
|
||||
char **endptr, int base) {
|
||||
ENSURE_ASAN_INITED();
|
||||
if (!flags()->replace_str) {
|
||||
return REAL(strtol)(nptr, endptr, base);
|
||||
}
|
||||
char *real_endptr;
|
||||
long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT
|
||||
if (endptr != 0) {
|
||||
*endptr = real_endptr;
|
||||
}
|
||||
if (IsValidStrtolBase(base)) {
|
||||
FixRealStrtolEndptr(nptr, &real_endptr);
|
||||
ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, atoi, const char *nptr) {
|
||||
ENSURE_ASAN_INITED();
|
||||
if (!flags()->replace_str) {
|
||||
return REAL(atoi)(nptr);
|
||||
}
|
||||
char *real_endptr;
|
||||
// "man atoi" tells that behavior of atoi(nptr) is the same as
|
||||
// strtol(nptr, 0, 10), i.e. it sets errno to ERANGE if the
|
||||
// parsed integer can't be stored in *long* type (even if it's
|
||||
// different from int). So, we just imitate this behavior.
|
||||
int result = REAL(strtol)(nptr, &real_endptr, 10);
|
||||
FixRealStrtolEndptr(nptr, &real_endptr);
|
||||
ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERCEPTOR(long, atol, const char *nptr) { // NOLINT
|
||||
ENSURE_ASAN_INITED();
|
||||
if (!flags()->replace_str) {
|
||||
return REAL(atol)(nptr);
|
||||
}
|
||||
char *real_endptr;
|
||||
long result = REAL(strtol)(nptr, &real_endptr, 10); // NOLINT
|
||||
FixRealStrtolEndptr(nptr, &real_endptr);
|
||||
ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
|
||||
INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT
|
||||
char **endptr, int base) {
|
||||
ENSURE_ASAN_INITED();
|
||||
if (!flags()->replace_str) {
|
||||
return REAL(strtoll)(nptr, endptr, base);
|
||||
}
|
||||
char *real_endptr;
|
||||
long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT
|
||||
if (endptr != 0) {
|
||||
*endptr = real_endptr;
|
||||
}
|
||||
// If base has unsupported value, strtoll can exit with EINVAL
|
||||
// without reading any characters. So do additional checks only
|
||||
// if base is valid.
|
||||
if (IsValidStrtolBase(base)) {
|
||||
FixRealStrtolEndptr(nptr, &real_endptr);
|
||||
ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT
|
||||
ENSURE_ASAN_INITED();
|
||||
if (!flags()->replace_str) {
|
||||
return REAL(atoll)(nptr);
|
||||
}
|
||||
char *real_endptr;
|
||||
long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT
|
||||
FixRealStrtolEndptr(nptr, &real_endptr);
|
||||
ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1);
|
||||
return result;
|
||||
}
|
||||
#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL
|
||||
|
||||
#define ASAN_INTERCEPT_FUNC(name) do { \
|
||||
if (!INTERCEPT_FUNCTION(name) && flags()->verbosity > 0) \
|
||||
Report("AddressSanitizer: failed to intercept '" #name "'\n"); \
|
||||
} while (0)
|
||||
|
||||
#if defined(_WIN32)
|
||||
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);
|
||||
u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
|
||||
AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack);
|
||||
asanThreadRegistry().RegisterThread(t);
|
||||
return REAL(CreateThread)(security, stack_size,
|
||||
asan_thread_start, t, flags, tid);
|
||||
}
|
||||
|
||||
namespace __asan {
|
||||
void InitializeWindowsInterceptors() {
|
||||
ASAN_INTERCEPT_FUNC(CreateThread);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
#endif
|
||||
|
||||
// ---------------------- InitializeAsanInterceptors ---------------- {{{1
|
||||
namespace __asan {
|
||||
void InitializeAsanInterceptors() {
|
||||
static bool was_called_once;
|
||||
CHECK(was_called_once == false);
|
||||
was_called_once = true;
|
||||
// Intercept mem* functions.
|
||||
ASAN_INTERCEPT_FUNC(memcmp);
|
||||
ASAN_INTERCEPT_FUNC(memmove);
|
||||
ASAN_INTERCEPT_FUNC(memset);
|
||||
if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) {
|
||||
ASAN_INTERCEPT_FUNC(memcpy);
|
||||
} else {
|
||||
REAL(memcpy) = REAL(memmove);
|
||||
}
|
||||
|
||||
// Intercept str* functions.
|
||||
ASAN_INTERCEPT_FUNC(strcat); // NOLINT
|
||||
ASAN_INTERCEPT_FUNC(strchr);
|
||||
ASAN_INTERCEPT_FUNC(strcmp);
|
||||
ASAN_INTERCEPT_FUNC(strcpy); // NOLINT
|
||||
ASAN_INTERCEPT_FUNC(strlen);
|
||||
ASAN_INTERCEPT_FUNC(strncat);
|
||||
ASAN_INTERCEPT_FUNC(strncmp);
|
||||
ASAN_INTERCEPT_FUNC(strncpy);
|
||||
#if !defined(_WIN32)
|
||||
ASAN_INTERCEPT_FUNC(strcasecmp);
|
||||
ASAN_INTERCEPT_FUNC(strdup);
|
||||
ASAN_INTERCEPT_FUNC(strncasecmp);
|
||||
# ifndef __APPLE__
|
||||
ASAN_INTERCEPT_FUNC(index);
|
||||
# else
|
||||
CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr)));
|
||||
# endif
|
||||
#endif
|
||||
#if ASAN_INTERCEPT_STRNLEN
|
||||
ASAN_INTERCEPT_FUNC(strnlen);
|
||||
#endif
|
||||
|
||||
ASAN_INTERCEPT_FUNC(atoi);
|
||||
ASAN_INTERCEPT_FUNC(atol);
|
||||
ASAN_INTERCEPT_FUNC(strtol);
|
||||
#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
|
||||
ASAN_INTERCEPT_FUNC(atoll);
|
||||
ASAN_INTERCEPT_FUNC(strtoll);
|
||||
#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)
|
||||
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.
|
||||
ASAN_INTERCEPT_FUNC(siglongjmp);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Intercept threading-related functions
|
||||
#if !defined(_WIN32)
|
||||
ASAN_INTERCEPT_FUNC(pthread_create);
|
||||
#endif
|
||||
|
||||
// Some Windows-specific interceptors.
|
||||
#if defined(_WIN32)
|
||||
InitializeWindowsInterceptors();
|
||||
#endif
|
||||
|
||||
// Some Mac-specific interceptors.
|
||||
#if defined(__APPLE__)
|
||||
InitializeMacInterceptors();
|
||||
#endif
|
||||
|
||||
if (flags()->verbosity > 0) {
|
||||
Report("AddressSanitizer: libc interceptors initialized\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
@ -15,119 +15,25 @@
|
||||
#define ASAN_INTERCEPTORS_H
|
||||
|
||||
#include "asan_internal.h"
|
||||
#include "interception/interception.h"
|
||||
|
||||
// To replace weak system functions on Linux we just need to declare functions
|
||||
// with same names in our library and then obtain the real function pointers
|
||||
// using dlsym(). This is not so on Mac OS, where the two-level namespace makes
|
||||
// our replacement functions invisible to other libraries. This may be overcomed
|
||||
// using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared
|
||||
// libraries in Chromium were noticed when doing so.
|
||||
// Instead we use mach_override, a handy framework for patching functions at
|
||||
// runtime. To avoid possible name clashes, our replacement functions have
|
||||
// the "wrap_" prefix on Mac.
|
||||
//
|
||||
// After interception, the calls to system functions will be substituted by
|
||||
// calls to our interceptors. We store pointers to system function f()
|
||||
// in __asan::real_f().
|
||||
//
|
||||
// TODO(glider): mach_override_ptr() tends to spend too much time
|
||||
// in allocateBranchIsland(). This should be ok for real-word
|
||||
// application, but slows down our tests which fork too many children.
|
||||
#ifdef __APPLE__
|
||||
#include "mach_override/mach_override.h"
|
||||
#define WRAP(x) wrap_##x
|
||||
#define WRAPPER_NAME(x) "wrap_"#x
|
||||
|
||||
#define OVERRIDE_FUNCTION(oldfunc, newfunc) \
|
||||
CHECK(0 == __asan_mach_override_ptr((void*)(oldfunc), \
|
||||
(void*)(newfunc), \
|
||||
(void**)&real_##oldfunc)); \
|
||||
CHECK(real_##oldfunc != NULL);
|
||||
|
||||
#define OVERRIDE_FUNCTION_IF_EXISTS(oldfunc, newfunc) \
|
||||
do { __asan_mach_override_ptr((void*)(oldfunc), \
|
||||
(void*)(newfunc), \
|
||||
(void**)&real_##oldfunc); } while (0)
|
||||
|
||||
#define INTERCEPT_FUNCTION(func) \
|
||||
OVERRIDE_FUNCTION(func, WRAP(func))
|
||||
|
||||
#define INTERCEPT_FUNCTION_IF_EXISTS(func) \
|
||||
OVERRIDE_FUNCTION_IF_EXISTS(func, WRAP(func))
|
||||
|
||||
#else // __linux__
|
||||
#define WRAP(x) x
|
||||
#define WRAPPER_NAME(x) #x
|
||||
|
||||
#define INTERCEPT_FUNCTION(func) \
|
||||
CHECK((real_##func = (func##_f)dlsym(RTLD_NEXT, #func)));
|
||||
|
||||
#define INTERCEPT_FUNCTION_IF_EXISTS(func) \
|
||||
do { real_##func = (func##_f)dlsym(RTLD_NEXT, #func); } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
int WRAP(memcmp)(const void *a1, const void *a2, size_t size);
|
||||
void *WRAP(memcpy)(void *to, const void *from, size_t size);
|
||||
void *WRAP(memmove)(void *to, const void *from, size_t size);
|
||||
void *WRAP(memset)(void *block, int c, size_t size);
|
||||
int WRAP(strcasecmp)(const char *s1, const char *s2);
|
||||
char *WRAP(strcat)(char *to, const char *from); // NOLINT
|
||||
char *WRAP(strchr)(const char *string, int c);
|
||||
int WRAP(strcmp)(const char *s1, const char *s2);
|
||||
char *WRAP(strcpy)(char *to, const char *from); // NOLINT
|
||||
char *WRAP(strdup)(const char *s);
|
||||
size_t WRAP(strlen)(const char *s);
|
||||
int WRAP(strncasecmp)(const char *s1, const char *s2, size_t n);
|
||||
int WRAP(strncmp)(const char *s1, const char *s2, size_t size);
|
||||
char *WRAP(strncpy)(char *to, const char *from, size_t size);
|
||||
#endif
|
||||
DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size)
|
||||
DECLARE_REAL(void*, memcpy, void *to, const void *from, uptr size)
|
||||
DECLARE_REAL(void*, memset, void *block, int c, uptr size)
|
||||
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)
|
||||
struct sigaction;
|
||||
DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact)
|
||||
|
||||
namespace __asan {
|
||||
|
||||
typedef void* (*index_f)(const char *string, int c);
|
||||
typedef int (*memcmp_f)(const void *a1, const void *a2, size_t size);
|
||||
typedef void* (*memcpy_f)(void *to, const void *from, size_t size);
|
||||
typedef void* (*memmove_f)(void *to, const void *from, size_t size);
|
||||
typedef void* (*memset_f)(void *block, int c, size_t size);
|
||||
typedef int (*strcasecmp_f)(const char *s1, const char *s2);
|
||||
typedef char* (*strcat_f)(char *to, const char *from);
|
||||
typedef char* (*strchr_f)(const char *str, int c);
|
||||
typedef int (*strcmp_f)(const char *s1, const char *s2);
|
||||
typedef char* (*strcpy_f)(char *to, const char *from);
|
||||
typedef char* (*strdup_f)(const char *s);
|
||||
typedef size_t (*strlen_f)(const char *s);
|
||||
typedef int (*strncasecmp_f)(const char *s1, const char *s2, size_t n);
|
||||
typedef int (*strncmp_f)(const char *s1, const char *s2, size_t size);
|
||||
typedef char* (*strncpy_f)(char *to, const char *from, size_t size);
|
||||
typedef size_t (*strnlen_f)(const char *s, size_t maxlen);
|
||||
|
||||
// __asan::real_X() holds pointer to library implementation of X().
|
||||
extern index_f real_index;
|
||||
extern memcmp_f real_memcmp;
|
||||
extern memcpy_f real_memcpy;
|
||||
extern memmove_f real_memmove;
|
||||
extern memset_f real_memset;
|
||||
extern strcasecmp_f real_strcasecmp;
|
||||
extern strcat_f real_strcat;
|
||||
extern strchr_f real_strchr;
|
||||
extern strcmp_f real_strcmp;
|
||||
extern strcpy_f real_strcpy;
|
||||
extern strdup_f real_strdup;
|
||||
extern strlen_f real_strlen;
|
||||
extern strncasecmp_f real_strncasecmp;
|
||||
extern strncmp_f real_strncmp;
|
||||
extern strncpy_f real_strncpy;
|
||||
extern strnlen_f real_strnlen;
|
||||
|
||||
// __asan::internal_X() is the implementation of X() for use in RTL.
|
||||
size_t internal_strlen(const char *s);
|
||||
size_t internal_strnlen(const char *s, size_t maxlen);
|
||||
void* internal_memchr(const void* s, int c, size_t n);
|
||||
int internal_memcmp(const void* s1, const void* s2, size_t n);
|
||||
|
||||
// Initializes pointers to str*/mem* functions.
|
||||
void InitializeAsanInterceptors();
|
||||
#if defined(__APPLE__)
|
||||
void InitializeMacInterceptors();
|
||||
#endif // __APPLE__
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
|
@ -15,46 +15,46 @@
|
||||
#ifndef ASAN_INTERFACE_H
|
||||
#define ASAN_INTERFACE_H
|
||||
|
||||
#include <stdint.h> // for __WORDSIZE
|
||||
#include <stdlib.h> // for size_t
|
||||
|
||||
#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_.
|
||||
|
||||
using __sanitizer::uptr;
|
||||
|
||||
extern "C" {
|
||||
// This function should be called at the very beginning of the process,
|
||||
// before any instrumented code is executed and before any call to malloc.
|
||||
void __asan_init()
|
||||
__attribute__((visibility("default")));
|
||||
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(uintptr_t addr, size_t size, const char *name)
|
||||
__attribute__((visibility("default")));
|
||||
void __asan_register_global(uptr addr, uptr size, const char *name)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
|
||||
// This structure describes an instrumented global variable.
|
||||
struct __asan_global {
|
||||
size_t beg; // The address of the global.
|
||||
size_t size; // The original size of the global.
|
||||
size_t size_with_redzone; // The size with the redzone.
|
||||
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.
|
||||
};
|
||||
|
||||
// These two functions should be called by the instrumented code.
|
||||
// 'globals' is an array of structures describing 'n' globals.
|
||||
void __asan_register_globals(__asan_global *globals, size_t n)
|
||||
__attribute__((visibility("default")));
|
||||
void __asan_unregister_globals(__asan_global *globals, size_t n)
|
||||
__attribute__((visibility("default")));
|
||||
void __asan_register_globals(__asan_global *globals, uptr n)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
void __asan_unregister_globals(__asan_global *globals, uptr n)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
|
||||
// These two functions are used by the instrumented code in the
|
||||
// use-after-return mode. __asan_stack_malloc allocates size bytes of
|
||||
// fake stack and __asan_stack_free poisons it. real_stack is a pointer to
|
||||
// the real stack region.
|
||||
size_t __asan_stack_malloc(size_t size, size_t real_stack)
|
||||
__attribute__((visibility("default")));
|
||||
void __asan_stack_free(size_t ptr, size_t size, size_t real_stack)
|
||||
__attribute__((visibility("default")));
|
||||
uptr __asan_stack_malloc(uptr size, uptr real_stack)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
void __asan_stack_free(uptr ptr, uptr size, uptr real_stack)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
|
||||
// Marks memory region [addr, addr+size) as unaddressable.
|
||||
// This memory must be previously allocated by the user program. Accessing
|
||||
@ -64,7 +64,8 @@ extern "C" {
|
||||
// to ASan alignment restrictions.
|
||||
// Method is NOT thread-safe in the sense that no two threads can
|
||||
// (un)poison memory in the same memory region simultaneously.
|
||||
void __asan_poison_memory_region(void const volatile *addr, size_t size);
|
||||
void __asan_poison_memory_region(void const volatile *addr, uptr size)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
// Marks memory region [addr, addr+size) as addressable.
|
||||
// This memory must be previously allocated by the user program. Accessing
|
||||
// addresses in this region is allowed until this region is poisoned again.
|
||||
@ -72,10 +73,15 @@ extern "C" {
|
||||
// ASan alignment restrictions.
|
||||
// Method is NOT thread-safe in the sense that no two threads can
|
||||
// (un)poison memory in the same memory region simultaneously.
|
||||
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
|
||||
void __asan_unpoison_memory_region(void const volatile *addr, uptr size)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
|
||||
// Performs cleanup before a NoReturn function. Must be called before things
|
||||
// like _exit and execl to avoid false positives on stack.
|
||||
void __asan_handle_no_return() SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
|
||||
// User code should use macro instead of functions.
|
||||
#if defined(__has_feature) && __has_feature(address_sanitizer)
|
||||
#if __has_feature(address_sanitizer)
|
||||
#define ASAN_POISON_MEMORY_REGION(addr, size) \
|
||||
__asan_poison_memory_region((addr), (size))
|
||||
#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
|
||||
@ -89,47 +95,65 @@ extern "C" {
|
||||
|
||||
// Returns true iff addr is poisoned (i.e. 1-byte read/write access to this
|
||||
// address will result in error report from AddressSanitizer).
|
||||
bool __asan_address_is_poisoned(void const volatile *addr);
|
||||
bool __asan_address_is_poisoned(void const volatile *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.
|
||||
void __asan_report_error(uintptr_t pc, uintptr_t bp, uintptr_t sp,
|
||||
uintptr_t addr, bool is_write, size_t access_size)
|
||||
__attribute__((visibility("default")));
|
||||
void __asan_report_error(uptr pc, uptr bp, uptr sp,
|
||||
uptr addr, bool is_write, uptr access_size)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
|
||||
// Sets the exit code to use when reporting an error.
|
||||
// Returns the old value.
|
||||
int __asan_set_error_exit_code(int exit_code);
|
||||
int __asan_set_error_exit_code(int exit_code)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
|
||||
// Sets the callback to be called right before death on error.
|
||||
// Passing 0 will unset the callback.
|
||||
void __asan_set_death_callback(void (*callback)(void))
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
|
||||
void __asan_set_error_report_callback(void (*callback)(const char*))
|
||||
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
|
||||
// "size".
|
||||
size_t __asan_get_estimated_allocated_size(size_t size);
|
||||
// Returns true if p is NULL or if p was returned by the ASan allocator and
|
||||
uptr __asan_get_estimated_allocated_size(uptr size)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
// Returns true if p was returned by the ASan allocator and
|
||||
// is not yet freed.
|
||||
bool __asan_get_ownership(const void *p);
|
||||
bool __asan_get_ownership(const void *p)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
// Returns the number of bytes reserved for the pointer p.
|
||||
// Requires (get_ownership(p) == true).
|
||||
size_t __asan_get_allocated_size(const void *p);
|
||||
// Requires (get_ownership(p) == true) or (p == 0).
|
||||
uptr __asan_get_allocated_size(const void *p)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
// Number of bytes, allocated and not yet freed by the application.
|
||||
size_t __asan_get_current_allocated_bytes();
|
||||
uptr __asan_get_current_allocated_bytes()
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
// Number of bytes, mmaped by asan allocator to fulfill allocation requests.
|
||||
// Generally, for request of X bytes, allocator can reserve and add to free
|
||||
// lists a large number of chunks of size X to use them for future requests.
|
||||
// All these chunks count toward the heap size. Currently, allocator never
|
||||
// releases memory to OS (instead, it just puts freed chunks to free lists).
|
||||
size_t __asan_get_heap_size();
|
||||
uptr __asan_get_heap_size()
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
// Number of bytes, mmaped by asan allocator, which can be used to fulfill
|
||||
// allocation requests. When a user program frees memory chunk, it can first
|
||||
// fall into quarantine and will count toward __asan_get_free_bytes() later.
|
||||
size_t __asan_get_free_bytes();
|
||||
uptr __asan_get_free_bytes()
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
// Number of bytes in unmapped pages, that are released to OS. Currently,
|
||||
// always returns 0.
|
||||
size_t __asan_get_unmapped_bytes();
|
||||
uptr __asan_get_unmapped_bytes()
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
// Prints accumulated stats to stderr. Used for debugging.
|
||||
void __asan_print_accumulated_stats();
|
||||
void __asan_print_accumulated_stats()
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
} // namespace
|
||||
|
||||
#endif // ASAN_INTERFACE_H
|
||||
|
@ -14,40 +14,49 @@
|
||||
#ifndef ASAN_INTERNAL_H
|
||||
#define ASAN_INTERNAL_H
|
||||
|
||||
#if !defined(__linux__) && !defined(__APPLE__)
|
||||
#include "asan_flags.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
|
||||
# error "This operating system is not supported by AddressSanitizer"
|
||||
#endif
|
||||
|
||||
#include <stdint.h> // for __WORDSIZE
|
||||
#include <stdlib.h> // for size_t
|
||||
#include <unistd.h> // for _exit
|
||||
#if defined(_WIN32)
|
||||
extern "C" void* _ReturnAddress(void);
|
||||
# pragma intrinsic(_ReturnAddress)
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
// If __WORDSIZE was undefined by the platform, define it in terms of the
|
||||
// compiler built-in __LP64__.
|
||||
#ifndef __WORDSIZE
|
||||
#if __LP64__
|
||||
#define __WORDSIZE 64
|
||||
#define ASAN_DEFAULT_FAILURE_EXITCODE 1
|
||||
|
||||
#if defined(__linux__)
|
||||
# define ASAN_LINUX 1
|
||||
#else
|
||||
#define __WORDSIZE 32
|
||||
#endif
|
||||
# define ASAN_LINUX 0
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <sys/atomics.h>
|
||||
#if defined(__APPLE__)
|
||||
# define ASAN_MAC 1
|
||||
#else
|
||||
# define ASAN_MAC 0
|
||||
#endif
|
||||
|
||||
#if defined(__has_feature) && __has_feature(address_sanitizer)
|
||||
#if defined(_WIN32)
|
||||
# define ASAN_WINDOWS 1
|
||||
#else
|
||||
# define ASAN_WINDOWS 0
|
||||
#endif
|
||||
|
||||
#define ASAN_POSIX (ASAN_LINUX || ASAN_MAC)
|
||||
|
||||
#if __has_feature(address_sanitizer)
|
||||
# error "The AddressSanitizer run-time should not be"
|
||||
" instrumented by AddressSanitizer"
|
||||
#endif
|
||||
|
||||
// Build-time configuration options.
|
||||
|
||||
// If set, sysinfo/sysinfo.h will be used to iterate over /proc/maps.
|
||||
#ifndef ASAN_USE_SYSINFO
|
||||
# define ASAN_USE_SYSINFO 1
|
||||
#endif
|
||||
|
||||
// If set, asan will install its own SEGV signal handler.
|
||||
#ifndef ASAN_NEEDS_SEGV
|
||||
# define ASAN_NEEDS_SEGV 1
|
||||
@ -64,6 +73,12 @@
|
||||
# define ASAN_FLEXIBLE_MAPPING_AND_OFFSET 0
|
||||
#endif
|
||||
|
||||
// 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
|
||||
#endif
|
||||
|
||||
// All internal functions in asan reside inside the __asan namespace
|
||||
// to avoid namespace collisions with the user programs.
|
||||
// Seperate namespace also makes it simpler to distinguish the asan run-time
|
||||
@ -74,109 +89,81 @@ class AsanThread;
|
||||
struct AsanStackTrace;
|
||||
|
||||
// asan_rtl.cc
|
||||
void CheckFailed(const char *cond, const char *file, int line);
|
||||
void ShowStatsAndAbort();
|
||||
void NORETURN ShowStatsAndAbort();
|
||||
|
||||
// asan_globals.cc
|
||||
bool DescribeAddrIfGlobal(uintptr_t addr);
|
||||
bool DescribeAddrIfGlobal(uptr addr);
|
||||
|
||||
void ReplaceOperatorsNewAndDelete();
|
||||
// asan_malloc_linux.cc / asan_malloc_mac.cc
|
||||
void ReplaceSystemMalloc();
|
||||
|
||||
void OutOfMemoryMessageAndDie(const char *mem_type, size_t size);
|
||||
|
||||
// asan_linux.cc / asan_mac.cc
|
||||
// asan_linux.cc / asan_mac.cc / asan_win.cc
|
||||
void *AsanDoesNotSupportStaticLinkage();
|
||||
int AsanOpenReadonly(const char* filename);
|
||||
|
||||
void *AsanMmapFixedNoReserve(uintptr_t fixed_addr, size_t size);
|
||||
void *AsanMmapFixedReserve(uintptr_t fixed_addr, size_t size);
|
||||
void *AsanMprotect(uintptr_t fixed_addr, size_t size);
|
||||
void *AsanMmapSomewhereOrDie(size_t size, const char *where);
|
||||
void AsanUnmapOrDie(void *ptr, size_t size);
|
||||
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
|
||||
|
||||
ssize_t AsanRead(int fd, void *buf, size_t count);
|
||||
ssize_t AsanWrite(int fd, const void *buf, size_t count);
|
||||
int AsanClose(int fd);
|
||||
bool AsanInterceptsSignal(int signum);
|
||||
void SetAlternateSignalStack();
|
||||
void UnsetAlternateSignalStack();
|
||||
void InstallSignalHandlers();
|
||||
void AsanPlatformThreadInit();
|
||||
|
||||
// Wrapper for TLS/TSD.
|
||||
void AsanTSDInit(void (*destructor)(void *tsd));
|
||||
void *AsanTSDGet();
|
||||
void AsanTSDSet(void *tsd);
|
||||
|
||||
void AppendToErrorMessageBuffer(const char *buffer);
|
||||
// asan_printf.cc
|
||||
void RawWrite(const char *buffer);
|
||||
int SNPrint(char *buffer, size_t length, const char *format, ...);
|
||||
void Printf(const char *format, ...);
|
||||
void Report(const char *format, ...);
|
||||
|
||||
// Don't use std::min and std::max, to minimize dependency on libstdc++.
|
||||
template<class T> T Min(T a, T b) { return a < b ? a : b; }
|
||||
template<class T> T Max(T a, T b) { return a > b ? a : b; }
|
||||
void AsanPrintf(const char *format, ...);
|
||||
void AsanReport(const char *format, ...);
|
||||
|
||||
// asan_poisoning.cc
|
||||
// Poisons the shadow memory for "size" bytes starting from "addr".
|
||||
void PoisonShadow(uintptr_t addr, size_t size, uint8_t value);
|
||||
void PoisonShadow(uptr addr, uptr size, u8 value);
|
||||
// Poisons the shadow memory for "redzone_size" bytes starting from
|
||||
// "addr + size".
|
||||
void PoisonShadowPartialRightRedzone(uintptr_t addr,
|
||||
uintptr_t size,
|
||||
uintptr_t redzone_size,
|
||||
uint8_t value);
|
||||
void PoisonShadowPartialRightRedzone(uptr addr,
|
||||
uptr size,
|
||||
uptr redzone_size,
|
||||
u8 value);
|
||||
|
||||
extern size_t FLAG_quarantine_size;
|
||||
extern int FLAG_demangle;
|
||||
extern bool FLAG_symbolize;
|
||||
extern int FLAG_v;
|
||||
extern size_t FLAG_redzone;
|
||||
extern int FLAG_debug;
|
||||
extern bool FLAG_poison_shadow;
|
||||
extern int FLAG_report_globals;
|
||||
extern size_t FLAG_malloc_context_size;
|
||||
extern bool FLAG_replace_str;
|
||||
extern bool FLAG_replace_intrin;
|
||||
extern bool FLAG_replace_cfallocator;
|
||||
extern bool FLAG_fast_unwind;
|
||||
extern bool FLAG_use_fake_stack;
|
||||
extern size_t FLAG_max_malloc_fill_size;
|
||||
extern int FLAG_exitcode;
|
||||
extern bool FLAG_allow_user_poisoning;
|
||||
// Platfrom-specific options.
|
||||
#ifdef __APPLE__
|
||||
bool PlatformHasDifferentMemcpyAndMemmove();
|
||||
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \
|
||||
(PlatformHasDifferentMemcpyAndMemmove())
|
||||
#else
|
||||
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true
|
||||
#endif // __APPLE__
|
||||
|
||||
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 };
|
||||
|
||||
#ifndef ASAN_DIE
|
||||
#define ASAN_DIE _exit(FLAG_exitcode)
|
||||
#endif // ASAN_DIE
|
||||
|
||||
#define CHECK(cond) do { if (!(cond)) { \
|
||||
CheckFailed(#cond, __FILE__, __LINE__); \
|
||||
}}while(0)
|
||||
|
||||
#define RAW_CHECK_MSG(expr, msg) do { \
|
||||
if (!(expr)) { \
|
||||
RawWrite(msg); \
|
||||
ASAN_DIE; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RAW_CHECK(expr) RAW_CHECK_MSG(expr, #expr)
|
||||
|
||||
#define UNIMPLEMENTED() CHECK("unimplemented" && 0)
|
||||
|
||||
#define ASAN_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
|
||||
|
||||
const size_t kWordSize = __WORDSIZE / 8;
|
||||
const size_t kWordSizeInBits = 8 * kWordSize;
|
||||
const size_t kPageSizeBits = 12;
|
||||
const size_t kPageSize = 1UL << kPageSizeBits;
|
||||
#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
|
||||
|
||||
#define GET_CALLER_PC() (uintptr_t)__builtin_return_address(0)
|
||||
#define GET_CURRENT_FRAME() (uintptr_t)__builtin_frame_address(0)
|
||||
|
||||
#define GET_BP_PC_SP \
|
||||
uintptr_t bp = GET_CURRENT_FRAME(); \
|
||||
uintptr_t pc = GET_CALLER_PC(); \
|
||||
uintptr_t local_stack; \
|
||||
uintptr_t sp = (uintptr_t)&local_stack;
|
||||
#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;
|
||||
@ -191,18 +178,8 @@ const int kAsanUserPoisonedMemoryMagic = 0xf7;
|
||||
const int kAsanGlobalRedzoneMagic = 0xf9;
|
||||
const int kAsanInternalHeapMagic = 0xfe;
|
||||
|
||||
static const uintptr_t kCurrentStackFrameMagic = 0x41B58AB3;
|
||||
static const uintptr_t kRetiredStackFrameMagic = 0x45E0360E;
|
||||
|
||||
// --------------------------- Bit twiddling ------- {{{1
|
||||
inline bool IsPowerOfTwo(size_t x) {
|
||||
return (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
inline size_t RoundUpTo(size_t size, size_t boundary) {
|
||||
CHECK(IsPowerOfTwo(boundary));
|
||||
return (size + boundary - 1) & ~(boundary - 1);
|
||||
}
|
||||
static const uptr kCurrentStackFrameMagic = 0x41B58AB3;
|
||||
static const uptr kRetiredStackFrameMagic = 0x45E0360E;
|
||||
|
||||
// -------------------------- LowLevelAllocator ----- {{{1
|
||||
// A simple low-level memory allocator for internal use.
|
||||
@ -211,29 +188,12 @@ class LowLevelAllocator {
|
||||
explicit LowLevelAllocator(LinkerInitialized) {}
|
||||
// 'size' must be a power of two.
|
||||
// Requires an external lock.
|
||||
void *Allocate(size_t size);
|
||||
void *Allocate(uptr size);
|
||||
private:
|
||||
char *allocated_end_;
|
||||
char *allocated_current_;
|
||||
};
|
||||
|
||||
// -------------------------- Atomic ---------------- {{{1
|
||||
static inline int AtomicInc(int *a) {
|
||||
#ifdef ANDROID
|
||||
return __atomic_inc(a) + 1;
|
||||
#else
|
||||
return __sync_add_and_fetch(a, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int AtomicDec(int *a) {
|
||||
#ifdef ANDROID
|
||||
return __atomic_dec(a) - 1;
|
||||
#else
|
||||
return __sync_add_and_fetch(a, -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // ASAN_INTERNAL_H
|
||||
|
@ -13,87 +13,131 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifdef __linux__
|
||||
|
||||
#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_procmaps.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <unwind.h>
|
||||
|
||||
extern char _DYNAMIC[];
|
||||
#ifndef ANDROID
|
||||
// FIXME: where to get ucontext on Android?
|
||||
#include <sys/ucontext.h>
|
||||
#endif
|
||||
|
||||
extern "C" void* _DYNAMIC;
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void *AsanDoesNotSupportStaticLinkage() {
|
||||
// This will fail to link with -static.
|
||||
return &_DYNAMIC;
|
||||
return &_DYNAMIC; // defined in link.h
|
||||
}
|
||||
|
||||
static void *asan_mmap(void *addr, size_t length, int prot, int flags,
|
||||
int fd, uint64_t offset) {
|
||||
# if __WORDSIZE == 64
|
||||
return (void *)syscall(__NR_mmap, addr, length, prot, flags, fd, offset);
|
||||
# else
|
||||
return (void *)syscall(__NR_mmap2, addr, length, prot, flags, fd, offset);
|
||||
# endif
|
||||
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
||||
#ifdef ANDROID
|
||||
*pc = *sp = *bp = 0;
|
||||
#elif defined(__arm__)
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.arm_pc;
|
||||
*bp = ucontext->uc_mcontext.arm_fp;
|
||||
*sp = ucontext->uc_mcontext.arm_sp;
|
||||
# elif defined(__x86_64__)
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.gregs[REG_RIP];
|
||||
*bp = ucontext->uc_mcontext.gregs[REG_RBP];
|
||||
*sp = ucontext->uc_mcontext.gregs[REG_RSP];
|
||||
# elif defined(__i386__)
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.gregs[REG_EIP];
|
||||
*bp = ucontext->uc_mcontext.gregs[REG_EBP];
|
||||
*sp = ucontext->uc_mcontext.gregs[REG_ESP];
|
||||
#else
|
||||
# error "Unsupported arch"
|
||||
#endif
|
||||
}
|
||||
|
||||
void *AsanMmapSomewhereOrDie(size_t size, const char *mem_type) {
|
||||
size = RoundUpTo(size, kPageSize);
|
||||
void *res = asan_mmap(0, size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
if (res == (void*)-1) {
|
||||
OutOfMemoryMessageAndDie(mem_type, size);
|
||||
bool AsanInterceptsSignal(int signum) {
|
||||
return signum == SIGSEGV && flags()->handle_segv;
|
||||
}
|
||||
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void *AsanMmapFixedNoReserve(uintptr_t fixed_addr, size_t size) {
|
||||
return asan_mmap((void*)fixed_addr, size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
void *AsanMmapFixedReserve(uintptr_t fixed_addr, size_t size) {
|
||||
return asan_mmap((void*)fixed_addr, size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
void *AsanMprotect(uintptr_t fixed_addr, size_t size) {
|
||||
return asan_mmap((void*)fixed_addr, size,
|
||||
PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
void AsanUnmapOrDie(void *addr, size_t size) {
|
||||
if (!addr || !size) return;
|
||||
int res = syscall(__NR_munmap, addr, size);
|
||||
if (res != 0) {
|
||||
Report("Failed to unmap\n");
|
||||
ASAN_DIE;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t AsanWrite(int fd, const void *buf, size_t count) {
|
||||
return (ssize_t)syscall(__NR_write, fd, buf, count);
|
||||
}
|
||||
|
||||
int AsanOpenReadonly(const char* filename) {
|
||||
return open(filename, O_RDONLY);
|
||||
}
|
||||
|
||||
ssize_t AsanRead(int fd, void *buf, size_t count) {
|
||||
return (ssize_t)syscall(__NR_read, fd, buf, count);
|
||||
}
|
||||
|
||||
int AsanClose(int fd) {
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
@ -14,86 +14,28 @@
|
||||
#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.
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <pthread.h>
|
||||
|
||||
#include <libkern/OSAtomic.h>
|
||||
namespace __asan {
|
||||
|
||||
class AsanLock {
|
||||
public:
|
||||
explicit AsanLock(LinkerInitialized) :
|
||||
mu_(OS_SPINLOCK_INIT),
|
||||
owner_(0),
|
||||
is_locked_(false) {}
|
||||
|
||||
void Lock() {
|
||||
CHECK(owner_ != pthread_self());
|
||||
OSSpinLockLock(&mu_);
|
||||
is_locked_ = true;
|
||||
owner_ = pthread_self();
|
||||
}
|
||||
void Unlock() {
|
||||
owner_ = 0;
|
||||
is_locked_ = false;
|
||||
OSSpinLockUnlock(&mu_);
|
||||
}
|
||||
|
||||
bool IsLocked() {
|
||||
// This is not atomic, e.g. one thread may get different values if another
|
||||
// one is about to release the lock.
|
||||
return is_locked_;
|
||||
}
|
||||
explicit AsanLock(LinkerInitialized);
|
||||
void Lock();
|
||||
void Unlock();
|
||||
bool IsLocked() { return owner_ != 0; }
|
||||
private:
|
||||
OSSpinLock mu_;
|
||||
volatile pthread_t owner_; // for debugging purposes
|
||||
bool is_locked_; // for silly malloc_introspection_t interface
|
||||
uptr opaque_storage_[10];
|
||||
uptr owner_; // for debugging and for malloc_introspection_t interface
|
||||
};
|
||||
} // namespace __asan
|
||||
|
||||
#else // assume linux
|
||||
#include <pthread.h>
|
||||
namespace __asan {
|
||||
class AsanLock {
|
||||
public:
|
||||
explicit AsanLock(LinkerInitialized) {
|
||||
// We assume that pthread_mutex_t initialized to all zeroes is a valid
|
||||
// unlocked mutex. We can not use PTHREAD_MUTEX_INITIALIZER as it triggers
|
||||
// a gcc warning:
|
||||
// extended initializer lists only available with -std=c++0x or -std=gnu++0x
|
||||
}
|
||||
void Lock() {
|
||||
pthread_mutex_lock(&mu_);
|
||||
// pthread_spin_lock(&mu_);
|
||||
}
|
||||
void Unlock() {
|
||||
pthread_mutex_unlock(&mu_);
|
||||
// pthread_spin_unlock(&mu_);
|
||||
}
|
||||
private:
|
||||
pthread_mutex_t mu_;
|
||||
// pthread_spinlock_t mu_;
|
||||
};
|
||||
} // namespace __asan
|
||||
#endif
|
||||
|
||||
namespace __asan {
|
||||
class ScopedLock {
|
||||
public:
|
||||
explicit ScopedLock(AsanLock *mu) : mu_(mu) {
|
||||
mu_->Lock();
|
||||
}
|
||||
~ScopedLock() {
|
||||
mu_->Unlock();
|
||||
}
|
||||
private:
|
||||
AsanLock *mu_;
|
||||
};
|
||||
typedef GenericScopedLock<AsanLock> ScopedLock;
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
|
@ -14,93 +14,165 @@
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include "asan_mac.h"
|
||||
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_mac.h"
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
#include <crt_externs.h> // for _NSGetEnviron
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h> // for free()
|
||||
#include <unistd.h>
|
||||
|
||||
#include <new>
|
||||
#include <libkern/OSAtomic.h>
|
||||
#include <CoreFoundation/CFString.h>
|
||||
|
||||
namespace __asan {
|
||||
|
||||
extern dispatch_async_f_f real_dispatch_async_f;
|
||||
extern dispatch_sync_f_f real_dispatch_sync_f;
|
||||
extern dispatch_after_f_f real_dispatch_after_f;
|
||||
extern dispatch_barrier_async_f_f real_dispatch_barrier_async_f;
|
||||
extern dispatch_group_async_f_f real_dispatch_group_async_f;
|
||||
extern pthread_workqueue_additem_np_f real_pthread_workqueue_additem_np;
|
||||
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
# if __WORDSIZE == 64
|
||||
*pc = ucontext->uc_mcontext->__ss.__rip;
|
||||
*bp = ucontext->uc_mcontext->__ss.__rbp;
|
||||
*sp = ucontext->uc_mcontext->__ss.__rsp;
|
||||
# else
|
||||
*pc = ucontext->uc_mcontext->__ss.__eip;
|
||||
*bp = ucontext->uc_mcontext->__ss.__ebp;
|
||||
*sp = ucontext->uc_mcontext->__ss.__esp;
|
||||
# endif // __WORDSIZE
|
||||
}
|
||||
|
||||
int GetMacosVersion() {
|
||||
int mib[2] = { CTL_KERN, KERN_OSRELEASE };
|
||||
char version[100];
|
||||
uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]);
|
||||
for (uptr i = 0; i < maxlen; i++) version[i] = '\0';
|
||||
// Get the version length.
|
||||
CHECK(sysctl(mib, 2, 0, &len, 0, 0) != -1);
|
||||
CHECK(len < maxlen);
|
||||
CHECK(sysctl(mib, 2, version, &len, 0, 0) != -1);
|
||||
switch (version[0]) {
|
||||
case '9': return MACOS_VERSION_LEOPARD;
|
||||
case '1': {
|
||||
switch (version[1]) {
|
||||
case '0': return MACOS_VERSION_SNOW_LEOPARD;
|
||||
case '1': return MACOS_VERSION_LION;
|
||||
default: return MACOS_VERSION_UNKNOWN;
|
||||
}
|
||||
}
|
||||
default: return MACOS_VERSION_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
bool PlatformHasDifferentMemcpyAndMemmove() {
|
||||
// On OS X 10.7 memcpy() and memmove() are both resolved
|
||||
// into memmove$VARIANT$sse42.
|
||||
// See also http://code.google.com/p/address-sanitizer/issues/detail?id=34.
|
||||
// TODO(glider): need to check dynamically that memcpy() and memmove() are
|
||||
// actually the same function.
|
||||
return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD;
|
||||
}
|
||||
|
||||
// No-op. Mac does not support static linkage anyway.
|
||||
void *AsanDoesNotSupportStaticLinkage() {
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *asan_mmap(void *addr, size_t length, int prot, int flags,
|
||||
int fd, uint64_t offset) {
|
||||
return mmap(addr, length, prot, flags, fd, offset);
|
||||
bool AsanInterceptsSignal(int signum) {
|
||||
return (signum == SIGSEGV || signum == SIGBUS) && flags()->handle_segv;
|
||||
}
|
||||
|
||||
ssize_t AsanWrite(int fd, const void *buf, size_t count) {
|
||||
return write(fd, buf, count);
|
||||
void AsanPlatformThreadInit() {
|
||||
ReplaceCFAllocator();
|
||||
}
|
||||
|
||||
void *AsanMmapSomewhereOrDie(size_t size, const char *mem_type) {
|
||||
size = RoundUpTo(size, kPageSize);
|
||||
void *res = asan_mmap(0, size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
if (res == (void*)-1) {
|
||||
OutOfMemoryMessageAndDie(mem_type, size);
|
||||
}
|
||||
return res;
|
||||
AsanLock::AsanLock(LinkerInitialized) {
|
||||
// We assume that OS_SPINLOCK_INIT is zero
|
||||
}
|
||||
|
||||
void *AsanMmapFixedNoReserve(uintptr_t fixed_addr, size_t size) {
|
||||
return asan_mmap((void*)fixed_addr, size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
|
||||
0, 0);
|
||||
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 *AsanMmapFixedReserve(uintptr_t fixed_addr, size_t size) {
|
||||
return asan_mmap((void*)fixed_addr, size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED,
|
||||
0, 0);
|
||||
void AsanLock::Unlock() {
|
||||
CHECK(owner_ == (uptr)pthread_self());
|
||||
owner_ = 0;
|
||||
OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
|
||||
}
|
||||
|
||||
void *AsanMprotect(uintptr_t fixed_addr, size_t size) {
|
||||
return asan_mmap((void*)fixed_addr, size,
|
||||
PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
void AsanUnmapOrDie(void *addr, size_t size) {
|
||||
if (!addr || !size) return;
|
||||
int res = munmap(addr, size);
|
||||
if (res != 0) {
|
||||
Report("Failed to unmap\n");
|
||||
ASAN_DIE;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int AsanOpenReadonly(const char* filename) {
|
||||
return open(filename, O_RDONLY);
|
||||
// The range of pages to be used for escape islands.
|
||||
// TODO(glider): instead of mapping a fixed range we must find a range of
|
||||
// unmapped pages in vmmap and take them.
|
||||
// These constants were chosen empirically and may not work if the shadow
|
||||
// memory layout changes. Unfortunately they do necessarily depend on
|
||||
// kHighMemBeg or kHighMemEnd.
|
||||
static void *island_allocator_pos = 0;
|
||||
|
||||
#if __WORDSIZE == 32
|
||||
# define kIslandEnd (0xffdf0000 - kPageSize)
|
||||
# define kIslandBeg (kIslandEnd - 256 * kPageSize)
|
||||
#else
|
||||
# define kIslandEnd (0x7fffffdf0000 - kPageSize)
|
||||
# define kIslandBeg (kIslandEnd - 256 * kPageSize)
|
||||
#endif
|
||||
|
||||
extern "C"
|
||||
mach_error_t __interception_allocate_island(void **ptr,
|
||||
uptr unused_size,
|
||||
void *unused_hint) {
|
||||
if (!island_allocator_pos) {
|
||||
island_allocator_pos =
|
||||
internal_mmap((void*)kIslandBeg, kIslandEnd - kIslandBeg,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED,
|
||||
-1, 0);
|
||||
if (island_allocator_pos != (void*)kIslandBeg) {
|
||||
return KERN_NO_SPACE;
|
||||
}
|
||||
if (flags()->verbosity) {
|
||||
Report("Mapped pages %p--%p for branch islands.\n",
|
||||
(void*)kIslandBeg, (void*)kIslandEnd);
|
||||
}
|
||||
// Should not be very performance-critical.
|
||||
internal_memset(island_allocator_pos, 0xCC, kIslandEnd - kIslandBeg);
|
||||
};
|
||||
*ptr = island_allocator_pos;
|
||||
island_allocator_pos = (char*)island_allocator_pos + kPageSize;
|
||||
if (flags()->verbosity) {
|
||||
Report("Branch island allocated at %p\n", *ptr);
|
||||
}
|
||||
return err_none;
|
||||
}
|
||||
|
||||
ssize_t AsanRead(int fd, void *buf, size_t count) {
|
||||
return read(fd, buf, count);
|
||||
}
|
||||
|
||||
int AsanClose(int fd) {
|
||||
return close(fd);
|
||||
extern "C"
|
||||
mach_error_t __interception_deallocate_island(void *ptr) {
|
||||
// Do nothing.
|
||||
// TODO(glider): allow to free and reuse the island memory.
|
||||
return err_none;
|
||||
}
|
||||
|
||||
// Support for the following functions from libdispatch on Mac OS:
|
||||
@ -132,34 +204,56 @@ int AsanClose(int fd) {
|
||||
// The implementation details are at
|
||||
// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
|
||||
|
||||
typedef void* pthread_workqueue_t;
|
||||
typedef void* pthread_workitem_handle_t;
|
||||
|
||||
typedef void* dispatch_group_t;
|
||||
typedef void* dispatch_queue_t;
|
||||
typedef u64 dispatch_time_t;
|
||||
typedef void (*dispatch_function_t)(void *block);
|
||||
typedef void* (*worker_t)(void *block);
|
||||
|
||||
// A wrapper for the ObjC blocks used to support libdispatch.
|
||||
typedef struct {
|
||||
void *block;
|
||||
dispatch_function_t func;
|
||||
u32 parent_tid;
|
||||
} asan_block_context_t;
|
||||
|
||||
// We use extern declarations of libdispatch functions here instead
|
||||
// of including <dispatch/dispatch.h>. This header is not present on
|
||||
// Mac OS X Leopard and eariler, and although we don't expect ASan to
|
||||
// work on legacy systems, it's bad to break the build of
|
||||
// LLVM compiler-rt there.
|
||||
extern "C" {
|
||||
void dispatch_async_f(dispatch_queue_t dq, void *ctxt,
|
||||
dispatch_function_t func);
|
||||
void dispatch_sync_f(dispatch_queue_t dq, void *ctxt,
|
||||
dispatch_function_t func);
|
||||
void dispatch_after_f(dispatch_time_t when, dispatch_queue_t dq, void *ctxt,
|
||||
dispatch_function_t func);
|
||||
void dispatch_barrier_async_f(dispatch_queue_t dq, void *ctxt,
|
||||
dispatch_function_t func);
|
||||
void dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
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"
|
||||
|
||||
extern "C"
|
||||
void asan_dispatch_call_block_and_release(void *block) {
|
||||
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
|
||||
GET_STACK_TRACE_HERE(kStackTraceMax);
|
||||
asan_block_context_t *context = (asan_block_context_t*)block;
|
||||
if (FLAG_v >= 2) {
|
||||
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) {
|
||||
// We've already executed a job on this worker thread. Let's reuse the
|
||||
// AsanThread object.
|
||||
if (t != asanThreadRegistry().GetMain()) {
|
||||
// Flush the statistics and update the current thread's tid.
|
||||
asanThreadRegistry().UnregisterThread(t);
|
||||
asanThreadRegistry().RegisterThread(t, context->parent_tid, &stack);
|
||||
}
|
||||
// Otherwise the worker is being executed on the main thread -- we are
|
||||
// draining the dispatch queue.
|
||||
// TODO(glider): any checks for that?
|
||||
} else {
|
||||
// It's incorrect to assert that the current thread is not dying: at least
|
||||
// the callbacks from dispatch_sync() are sometimes called after the TSD is
|
||||
// destroyed.
|
||||
t = (AsanThread*)asan_malloc(sizeof(AsanThread), &stack);
|
||||
new(t) AsanThread(context->parent_tid,
|
||||
/*start_routine*/NULL, /*arg*/NULL, &stack);
|
||||
if (!t) {
|
||||
t = AsanThread::Create(context->parent_tid, 0, 0, &stack);
|
||||
asanThreadRegistry().RegisterThread(t);
|
||||
t->Init();
|
||||
asanThreadRegistry().SetCurrent(t);
|
||||
}
|
||||
@ -181,94 +275,75 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
|
||||
(asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
|
||||
asan_ctxt->block = ctxt;
|
||||
asan_ctxt->func = func;
|
||||
AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
|
||||
if (FLAG_debug) {
|
||||
// Sometimes at Chromium teardown this assertion is violated:
|
||||
// -- a task is created via dispatch_async() on the "CFMachPort"
|
||||
// thread while doing _dispatch_queue_drain();
|
||||
// -- a task is created via dispatch_async_f() on the
|
||||
// "com.apple.root.default-overcommit-priority" thread while doing
|
||||
// _dispatch_dispose().
|
||||
// TODO(glider): find out what's going on.
|
||||
CHECK(curr_thread || asanThreadRegistry().IsCurrentThreadDying());
|
||||
}
|
||||
asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne();
|
||||
asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
|
||||
return asan_ctxt;
|
||||
}
|
||||
|
||||
// TODO(glider): can we reduce code duplication by introducing a macro?
|
||||
extern "C"
|
||||
int WRAP(dispatch_async_f)(dispatch_queue_t dq,
|
||||
void *ctxt,
|
||||
dispatch_function_t func) {
|
||||
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
|
||||
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 (FLAG_v >= 2) {
|
||||
if (flags()->verbosity >= 2) {
|
||||
Report("dispatch_async_f(): context: %p, pthread_self: %p\n",
|
||||
asan_ctxt, pthread_self());
|
||||
PRINT_CURRENT_STACK();
|
||||
}
|
||||
return real_dispatch_async_f(dq, (void*)asan_ctxt,
|
||||
asan_dispatch_call_block_and_release);
|
||||
return REAL(dispatch_async_f)(dq, (void*)asan_ctxt,
|
||||
asan_dispatch_call_block_and_release);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
int WRAP(dispatch_sync_f)(dispatch_queue_t dq,
|
||||
void *ctxt,
|
||||
dispatch_function_t func) {
|
||||
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
|
||||
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 (FLAG_v >= 2) {
|
||||
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);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
int WRAP(dispatch_after_f)(dispatch_time_t when,
|
||||
dispatch_queue_t dq,
|
||||
void *ctxt,
|
||||
dispatch_function_t func) {
|
||||
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
|
||||
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
|
||||
if (FLAG_v >= 2) {
|
||||
Report("dispatch_after_f: %p\n", asan_ctxt);
|
||||
PRINT_CURRENT_STACK();
|
||||
}
|
||||
return real_dispatch_after_f(when, dq, (void*)asan_ctxt,
|
||||
return REAL(dispatch_sync_f)(dq, (void*)asan_ctxt,
|
||||
asan_dispatch_call_block_and_release);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func) {
|
||||
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
|
||||
INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
|
||||
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 (FLAG_v >= 2) {
|
||||
if (flags()->verbosity >= 2) {
|
||||
Report("dispatch_after_f: %p\n", asan_ctxt);
|
||||
PRINT_CURRENT_STACK();
|
||||
}
|
||||
return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt,
|
||||
asan_dispatch_call_block_and_release);
|
||||
}
|
||||
|
||||
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);
|
||||
REAL(dispatch_barrier_async_f)(dq, (void*)asan_ctxt,
|
||||
asan_dispatch_call_block_and_release);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
void WRAP(dispatch_group_async_f)(dispatch_group_t group,
|
||||
dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func) {
|
||||
GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false);
|
||||
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);
|
||||
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
|
||||
if (FLAG_v >= 2) {
|
||||
if (flags()->verbosity >= 2) {
|
||||
Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
|
||||
asan_ctxt, pthread_self());
|
||||
PRINT_CURRENT_STACK();
|
||||
}
|
||||
real_dispatch_group_async_f(group, dq, (void*)asan_ctxt,
|
||||
asan_dispatch_call_block_and_release);
|
||||
REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt,
|
||||
asan_dispatch_call_block_and_release);
|
||||
}
|
||||
|
||||
// The following stuff has been extremely helpful while looking for the
|
||||
@ -279,33 +354,90 @@ void WRAP(dispatch_group_async_f)(dispatch_group_t group,
|
||||
// libdispatch API.
|
||||
extern "C"
|
||||
void *wrap_workitem_func(void *arg) {
|
||||
if (FLAG_v >= 2) {
|
||||
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, /*fast_unwind*/false);
|
||||
GET_STACK_TRACE_HERE(kStackTraceMax);
|
||||
asan_free(arg, &stack);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq,
|
||||
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, /*fast_unwind*/false);
|
||||
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().GetCurrentTidOrMinusOne();
|
||||
if (FLAG_v >= 2) {
|
||||
asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
|
||||
if (flags()->verbosity >= 2) {
|
||||
Report("pthread_workqueue_additem_np: %p\n", asan_ctxt);
|
||||
PRINT_CURRENT_STACK();
|
||||
}
|
||||
return real_pthread_workqueue_additem_np(workq, wrap_workitem_func, asan_ctxt,
|
||||
itemhandlep, gencountp);
|
||||
return REAL(pthread_workqueue_additem_np)(workq, wrap_workitem_func,
|
||||
asan_ctxt, itemhandlep,
|
||||
gencountp);
|
||||
}
|
||||
|
||||
// See http://opensource.apple.com/source/CF/CF-635.15/CFString.c
|
||||
int __CFStrIsConstant(CFStringRef str) {
|
||||
CFRuntimeBase *base = (CFRuntimeBase*)str;
|
||||
#if __LP64__
|
||||
return base->_rc == 0;
|
||||
#else
|
||||
return (base->_cfinfo[CF_RC_BITS]) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
INTERCEPTOR(CFStringRef, CFStringCreateCopy, CFAllocatorRef alloc,
|
||||
CFStringRef str) {
|
||||
if (__CFStrIsConstant(str)) {
|
||||
return str;
|
||||
} else {
|
||||
return REAL(CFStringCreateCopy)(alloc, str);
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
|
||||
|
||||
extern "C"
|
||||
void __CFInitialize();
|
||||
DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize)
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void InitializeMacInterceptors() {
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_async_f));
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_sync_f));
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_after_f));
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f));
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f));
|
||||
// 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
|
||||
// Chromium and WebKit, see
|
||||
// http://code.google.com/p/address-sanitizer/issues/detail?id=10
|
||||
// Until this problem is fixed we need to check that the string is
|
||||
// non-constant before calling CFStringCreateCopy.
|
||||
CHECK(INTERCEPT_FUNCTION(CFStringCreateCopy));
|
||||
// Some of the library functions call free() directly, so we have to
|
||||
// intercept it.
|
||||
CHECK(INTERCEPT_FUNCTION(free));
|
||||
if (flags()->replace_cfallocator) {
|
||||
CHECK(INTERCEPT_FUNCTION(__CFInitialize));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // __APPLE__
|
||||
|
@ -9,79 +9,45 @@
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// ASan-private header for asan_mac.cc
|
||||
// Mac-specific ASan definitions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifdef __APPLE__
|
||||
|
||||
#ifndef ASAN_MAC_H
|
||||
#define ASAN_MAC_H
|
||||
|
||||
#include "asan_interceptors.h"
|
||||
// CF_RC_BITS, the layout of CFRuntimeBase and __CFStrIsConstant are internal
|
||||
// and subject to change in further CoreFoundation versions. Apple does not
|
||||
// guarantee any binary compatibility from release to release.
|
||||
|
||||
// TODO(glider): need to check if the OS X version is 10.6 or greater.
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <setjmp.h>
|
||||
// See http://opensource.apple.com/source/CF/CF-635.15/CFInternal.h
|
||||
#if defined(__BIG_ENDIAN__)
|
||||
#define CF_RC_BITS 0
|
||||
#endif
|
||||
|
||||
typedef void* pthread_workqueue_t;
|
||||
typedef void* pthread_workitem_handle_t;
|
||||
#if defined(__LITTLE_ENDIAN__)
|
||||
#define CF_RC_BITS 3
|
||||
#endif
|
||||
|
||||
typedef void (*dispatch_function_t)(void *block);
|
||||
typedef void* (*worker_t)(void *block);
|
||||
typedef int (*dispatch_async_f_f)(dispatch_queue_t dq, void *ctxt,
|
||||
dispatch_function_t func);
|
||||
typedef int (*dispatch_sync_f_f)(dispatch_queue_t dq, void *ctxt,
|
||||
dispatch_function_t func);
|
||||
typedef int (*dispatch_after_f_f)(dispatch_time_t when,
|
||||
dispatch_queue_t dq, void *ctxt,
|
||||
dispatch_function_t func);
|
||||
typedef void (*dispatch_barrier_async_f_f)(dispatch_queue_t dq,
|
||||
void *ctxt,
|
||||
dispatch_function_t func);
|
||||
typedef void (*dispatch_group_async_f_f)(dispatch_group_t group,
|
||||
dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
typedef int (*pthread_workqueue_additem_np_f)(pthread_workqueue_t workq,
|
||||
void *(*workitem_func)(void *), void * workitem_arg,
|
||||
pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
|
||||
// See http://opensource.apple.com/source/CF/CF-635.15/CFRuntime.h
|
||||
typedef struct __CFRuntimeBase {
|
||||
uptr _cfisa;
|
||||
u8 _cfinfo[4];
|
||||
#if __LP64__
|
||||
u32 _rc;
|
||||
#endif
|
||||
} CFRuntimeBase;
|
||||
|
||||
enum {
|
||||
MACOS_VERSION_UNKNOWN = 0,
|
||||
MACOS_VERSION_LEOPARD,
|
||||
MACOS_VERSION_SNOW_LEOPARD,
|
||||
MACOS_VERSION_LION
|
||||
};
|
||||
|
||||
// A wrapper for the ObjC blocks used to support libdispatch.
|
||||
typedef struct {
|
||||
void *block;
|
||||
dispatch_function_t func;
|
||||
int parent_tid;
|
||||
} asan_block_context_t;
|
||||
namespace __asan {
|
||||
|
||||
int GetMacosVersion();
|
||||
void ReplaceCFAllocator();
|
||||
|
||||
extern "C" {
|
||||
// dispatch_barrier_async_f() is not declared in <dispatch/dispatch.h>.
|
||||
void dispatch_barrier_async_f(dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
// Neither is pthread_workqueue_additem_np().
|
||||
int pthread_workqueue_additem_np(pthread_workqueue_t workq,
|
||||
void *(*workitem_func)(void *), void * workitem_arg,
|
||||
pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
|
||||
|
||||
int WRAP(dispatch_async_f)(dispatch_queue_t dq,
|
||||
void *ctxt,
|
||||
dispatch_function_t func);
|
||||
int WRAP(dispatch_sync_f)(dispatch_queue_t dq,
|
||||
void *ctxt,
|
||||
dispatch_function_t func);
|
||||
int WRAP(dispatch_after_f)(dispatch_time_t when,
|
||||
dispatch_queue_t dq,
|
||||
void *ctxt,
|
||||
dispatch_function_t func);
|
||||
void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
void WRAP(dispatch_group_async_f)(dispatch_group_t group,
|
||||
dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq,
|
||||
void *(*workitem_func)(void *), void * workitem_arg,
|
||||
pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
|
||||
}
|
||||
} // namespace __asan
|
||||
|
||||
#endif // ASAN_MAC_H
|
||||
|
||||
#endif // __APPLE__
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_malloc_linux.cc ------------------------------------*- C++ -*-===//
|
||||
//===-- asan_malloc_linux.cc ----------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -20,20 +20,16 @@
|
||||
#include "asan_internal.h"
|
||||
#include "asan_stack.h"
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
#define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
|
||||
|
||||
#ifdef ANDROID
|
||||
struct MallocDebug {
|
||||
void* (*malloc)(size_t bytes);
|
||||
void* (*malloc)(uptr bytes);
|
||||
void (*free)(void* mem);
|
||||
void* (*calloc)(size_t n_elements, size_t elem_size);
|
||||
void* (*realloc)(void* oldMem, size_t bytes);
|
||||
void* (*memalign)(size_t alignment, size_t bytes);
|
||||
void* (*calloc)(uptr n_elements, uptr elem_size);
|
||||
void* (*realloc)(void* oldMem, uptr bytes);
|
||||
void* (*memalign)(uptr alignment, uptr bytes);
|
||||
};
|
||||
|
||||
const MallocDebug asan_malloc_dispatch __attribute__((aligned(32))) = {
|
||||
const MallocDebug asan_malloc_dispatch ALIGNED(32) = {
|
||||
malloc, free, calloc, realloc, memalign
|
||||
};
|
||||
|
||||
@ -56,33 +52,28 @@ void ReplaceSystemMalloc() {
|
||||
// ---------------------- Replacement functions ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
extern "C" {
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
void free(void *ptr) {
|
||||
INTERCEPTOR(void, free, void *ptr) {
|
||||
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
|
||||
asan_free(ptr, &stack);
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
void cfree(void *ptr) {
|
||||
INTERCEPTOR(void, cfree, void *ptr) {
|
||||
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
|
||||
asan_free(ptr, &stack);
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
void *malloc(size_t size) {
|
||||
INTERCEPTOR(void*, malloc, uptr size) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_malloc(size, &stack);
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
void *calloc(size_t nmemb, size_t size) {
|
||||
INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
|
||||
if (!asan_inited) {
|
||||
// Hack: dlsym calls calloc before real_calloc is retrieved from dlsym.
|
||||
const size_t kCallocPoolSize = 1024;
|
||||
static uintptr_t calloc_memory_for_dlsym[kCallocPoolSize];
|
||||
static size_t allocated;
|
||||
size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
|
||||
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
|
||||
const uptr kCallocPoolSize = 1024;
|
||||
static uptr calloc_memory_for_dlsym[kCallocPoolSize];
|
||||
static uptr allocated;
|
||||
uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
|
||||
void *mem = (void*)&calloc_memory_for_dlsym[allocated];
|
||||
allocated += size_in_words;
|
||||
CHECK(allocated < kCallocPoolSize);
|
||||
@ -92,51 +83,56 @@ void *calloc(size_t nmemb, size_t size) {
|
||||
return asan_calloc(nmemb, size, &stack);
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
void *realloc(void *ptr, size_t size) {
|
||||
INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_realloc(ptr, size, &stack);
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
void *memalign(size_t boundary, size_t size) {
|
||||
INTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_memalign(boundary, size, &stack);
|
||||
}
|
||||
|
||||
void* __libc_memalign(size_t align, size_t s)
|
||||
__attribute__((alias("memalign")));
|
||||
INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s)
|
||||
ALIAS("memalign");
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
struct mallinfo mallinfo() {
|
||||
struct mallinfo res;
|
||||
real_memset(&res, 0, sizeof(res));
|
||||
INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_malloc_usable_size(ptr, &stack);
|
||||
}
|
||||
|
||||
// We avoid including malloc.h for portability reasons.
|
||||
// man mallinfo says the fields are "long", but the implementation uses int.
|
||||
// It doesn't matter much -- we just need to make sure that the libc's mallinfo
|
||||
// is not called.
|
||||
struct fake_mallinfo {
|
||||
int x[10];
|
||||
};
|
||||
|
||||
INTERCEPTOR(struct fake_mallinfo, mallinfo, void) {
|
||||
struct fake_mallinfo res;
|
||||
REAL(memset)(&res, 0, sizeof(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
int mallopt(int cmd, int value) {
|
||||
INTERCEPTOR(int, mallopt, int cmd, int value) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
int posix_memalign(void **memptr, size_t alignment, size_t size) {
|
||||
INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
// Printf("posix_memalign: %lx %ld\n", alignment, size);
|
||||
// Printf("posix_memalign: %zx %zu\n", alignment, size);
|
||||
return asan_posix_memalign(memptr, alignment, size, &stack);
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
void *valloc(size_t size) {
|
||||
INTERCEPTOR(void*, valloc, uptr size) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_valloc(size, &stack);
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
void *pvalloc(size_t size) {
|
||||
INTERCEPTOR(void*, pvalloc, uptr size) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_pvalloc(size, &stack);
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
#endif // __linux__
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_rtl.cc ---------------------------------------------*- C++ -*-===//
|
||||
//===-- asan_rtl.cc -------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -16,12 +16,14 @@
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
#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_stack.h"
|
||||
|
||||
// Similar code is used in Google Perftools,
|
||||
@ -30,12 +32,26 @@
|
||||
// ---------------------- Replacement functions ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
// TODO(glider): do we need both zones?
|
||||
static malloc_zone_t *system_malloc_zone = 0;
|
||||
static malloc_zone_t *system_purgeable_zone = 0;
|
||||
static malloc_zone_t asan_zone;
|
||||
CFAllocatorRef cf_asan = 0;
|
||||
|
||||
// The free() implementation provided by OS X calls malloc_zone_from_ptr()
|
||||
// to find the owner of |ptr|. If the result is NULL, an invalid free() is
|
||||
// to find the owner of |ptr|. If the result is 0, an invalid free() is
|
||||
// reported. Our implementation falls back to asan_free() in this case
|
||||
// in order to print an ASan-style report.
|
||||
extern "C"
|
||||
void free(void *ptr) {
|
||||
//
|
||||
// For the objects created by _CFRuntimeCreateInstance a CFAllocatorRef is
|
||||
// placed at the beginning of the allocated chunk and the pointer returned by
|
||||
// our allocator is off by sizeof(CFAllocatorRef). This pointer can be then
|
||||
// passed directly to free(), which will lead to errors.
|
||||
// To overcome this we're checking whether |ptr-sizeof(CFAllocatorRef)|
|
||||
// contains a pointer to our CFAllocator (assuming no other allocator is used).
|
||||
// See http://code.google.com/p/address-sanitizer/issues/detail?id=70 for more
|
||||
// info.
|
||||
INTERCEPTOR(void, free, void *ptr) {
|
||||
malloc_zone_t *zone = malloc_zone_from_ptr(ptr);
|
||||
if (zone) {
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && \
|
||||
@ -49,27 +65,48 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(glider): do we need both zones?
|
||||
static malloc_zone_t *system_malloc_zone = NULL;
|
||||
static malloc_zone_t *system_purgeable_zone = NULL;
|
||||
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
|
||||
// crash the program. Instead we wait for the allocator to initialize and jump
|
||||
// in just after __CFInitialize(). Nobody is going to allocate memory using
|
||||
// CFAllocators before that, so we won't miss anything.
|
||||
//
|
||||
// See http://code.google.com/p/address-sanitizer/issues/detail?id=87
|
||||
// and http://opensource.apple.com/source/CF/CF-550.43/CFRuntime.c
|
||||
INTERCEPTOR(void, __CFInitialize) {
|
||||
CHECK(flags()->replace_cfallocator);
|
||||
CHECK(asan_inited);
|
||||
REAL(__CFInitialize)();
|
||||
if (!cf_asan) ReplaceCFAllocator();
|
||||
}
|
||||
|
||||
// We need to provide wrappers around all the libc functions.
|
||||
namespace {
|
||||
|
||||
// TODO(glider): the mz_* functions should be united with the Linux wrappers,
|
||||
// as they are basically copied from there.
|
||||
size_t mz_size(malloc_zone_t* zone, const void* ptr) {
|
||||
// Fast path: check whether this pointer belongs to the original malloc zone.
|
||||
// We cannot just call malloc_zone_from_ptr(), because it in turn
|
||||
// calls our mz_size().
|
||||
if (system_malloc_zone) {
|
||||
if ((system_malloc_zone->size)(system_malloc_zone, ptr)) return 0;
|
||||
}
|
||||
return __asan_mz_size(ptr);
|
||||
return asan_mz_size(ptr);
|
||||
}
|
||||
|
||||
void *mz_malloc(malloc_zone_t *zone, size_t size) {
|
||||
@ -92,9 +129,9 @@ void *cf_malloc(CFIndex size, CFOptionFlags hint, void *info) {
|
||||
|
||||
void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
|
||||
if (!asan_inited) {
|
||||
// Hack: dlsym calls calloc before real_calloc is retrieved from dlsym.
|
||||
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
|
||||
const size_t kCallocPoolSize = 1024;
|
||||
static uintptr_t calloc_memory_for_dlsym[kCallocPoolSize];
|
||||
static uptr calloc_memory_for_dlsym[kCallocPoolSize];
|
||||
static size_t allocated;
|
||||
size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
|
||||
void *mem = (void*)&calloc_memory_for_dlsym[allocated];
|
||||
@ -119,64 +156,41 @@ void print_zone_for_ptr(void *ptr) {
|
||||
malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr);
|
||||
if (orig_zone) {
|
||||
if (orig_zone->zone_name) {
|
||||
Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n",
|
||||
ptr, orig_zone, orig_zone->zone_name);
|
||||
AsanPrintf("malloc_zone_from_ptr(%p) = %p, which is %s\n",
|
||||
ptr, orig_zone, orig_zone->zone_name);
|
||||
} else {
|
||||
Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n",
|
||||
ptr, orig_zone);
|
||||
AsanPrintf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n",
|
||||
ptr, orig_zone);
|
||||
}
|
||||
} else {
|
||||
Printf("malloc_zone_from_ptr(%p) = NULL\n", ptr);
|
||||
AsanPrintf("malloc_zone_from_ptr(%p) = 0\n", ptr);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(glider): the allocation callbacks need to be refactored.
|
||||
void mz_free(malloc_zone_t *zone, void *ptr) {
|
||||
if (!ptr) return;
|
||||
malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr);
|
||||
// For some reason Chromium calls mz_free() for pointers that belong to
|
||||
// DefaultPurgeableMallocZone instead of asan_zone. We might want to
|
||||
// fix this someday.
|
||||
if (orig_zone == system_purgeable_zone) {
|
||||
system_purgeable_zone->free(system_purgeable_zone, ptr);
|
||||
return;
|
||||
}
|
||||
if (__asan_mz_size(ptr)) {
|
||||
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
|
||||
asan_free(ptr, &stack);
|
||||
} else {
|
||||
// Let us just leak this memory for now.
|
||||
Printf("mz_free(%p) -- attempting to free unallocated memory.\n"
|
||||
"AddressSanitizer is ignoring this error on Mac OS now.\n", ptr);
|
||||
print_zone_for_ptr(ptr);
|
||||
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
|
||||
stack.PrintStack();
|
||||
return;
|
||||
}
|
||||
free_common(zone, ptr);
|
||||
}
|
||||
|
||||
void cf_free(void *ptr, void *info) {
|
||||
if (!ptr) return;
|
||||
malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr);
|
||||
// For some reason Chromium calls mz_free() for pointers that belong to
|
||||
// DefaultPurgeableMallocZone instead of asan_zone. We might want to
|
||||
// fix this someday.
|
||||
if (orig_zone == system_purgeable_zone) {
|
||||
system_purgeable_zone->free(system_purgeable_zone, ptr);
|
||||
return;
|
||||
}
|
||||
if (__asan_mz_size(ptr)) {
|
||||
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
|
||||
asan_free(ptr, &stack);
|
||||
} else {
|
||||
// Let us just leak this memory for now.
|
||||
Printf("cf_free(%p) -- attempting to free unallocated memory.\n"
|
||||
"AddressSanitizer is ignoring this error on Mac OS now.\n", ptr);
|
||||
print_zone_for_ptr(ptr);
|
||||
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
|
||||
stack.PrintStack();
|
||||
return;
|
||||
}
|
||||
free_common(info, ptr);
|
||||
}
|
||||
|
||||
void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
|
||||
@ -184,20 +198,21 @@ void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_malloc(size, &stack);
|
||||
} else {
|
||||
if (__asan_mz_size(ptr)) {
|
||||
if (asan_mz_size(ptr)) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_realloc(ptr, size, &stack);
|
||||
} else {
|
||||
// We can't recover from reallocating an unknown address, because
|
||||
// this would require reading at most |size| bytes from
|
||||
// potentially unaccessible memory.
|
||||
Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
|
||||
"This is an unrecoverable problem, exiting now.\n", ptr);
|
||||
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 NULL; // unreachable
|
||||
return 0; // unreachable
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,27 +222,28 @@ void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_malloc(size, &stack);
|
||||
} else {
|
||||
if (__asan_mz_size(ptr)) {
|
||||
if (asan_mz_size(ptr)) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_realloc(ptr, size, &stack);
|
||||
} else {
|
||||
// We can't recover from reallocating an unknown address, because
|
||||
// this would require reading at most |size| bytes from
|
||||
// potentially unaccessible memory.
|
||||
Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
|
||||
"This is an unrecoverable problem, exiting now.\n", ptr);
|
||||
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 NULL; // unreachable
|
||||
return 0; // unreachable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mz_destroy(malloc_zone_t* zone) {
|
||||
// A no-op -- we will not be destroyed!
|
||||
Printf("mz_destroy() called -- ignoring\n");
|
||||
AsanPrintf("mz_destroy() called -- ignoring\n");
|
||||
}
|
||||
// from AvailabilityMacros.h
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && \
|
||||
@ -279,11 +295,11 @@ void mi_log(malloc_zone_t *zone, void *address) {
|
||||
}
|
||||
|
||||
void mi_force_lock(malloc_zone_t *zone) {
|
||||
__asan_mz_force_lock();
|
||||
asan_mz_force_lock();
|
||||
}
|
||||
|
||||
void mi_force_unlock(malloc_zone_t *zone) {
|
||||
__asan_mz_force_unlock();
|
||||
asan_mz_force_unlock();
|
||||
}
|
||||
|
||||
// This function is currently unused, and we build with -Werror.
|
||||
@ -298,19 +314,38 @@ void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && \
|
||||
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
|
||||
boolean_t mi_zone_locked(malloc_zone_t *zone) {
|
||||
// UNIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
extern bool kCFUseCollectableAllocator; // is GC on?
|
||||
extern int __CFRuntimeClassTableSize;
|
||||
|
||||
namespace __asan {
|
||||
void ReplaceCFAllocator() {
|
||||
static CFAllocatorContext asan_context = {
|
||||
/*version*/ 0, /*info*/ &asan_zone,
|
||||
/*retain*/ 0, /*release*/ 0,
|
||||
/*copyDescription*/0,
|
||||
/*allocate*/ &cf_malloc,
|
||||
/*reallocate*/ &cf_realloc,
|
||||
/*deallocate*/ &cf_free,
|
||||
/*preferredSize*/ 0 };
|
||||
if (!cf_asan)
|
||||
cf_asan = CFAllocatorCreate(kCFAllocatorUseContext, &asan_context);
|
||||
if (CFAllocatorGetDefault() != cf_asan)
|
||||
CFAllocatorSetDefault(cf_asan);
|
||||
}
|
||||
|
||||
void ReplaceSystemMalloc() {
|
||||
static malloc_introspection_t asan_introspection;
|
||||
__asan::real_memset(&asan_introspection, 0, sizeof(asan_introspection));
|
||||
// Ok to use internal_memset, these places are not performance-critical.
|
||||
internal_memset(&asan_introspection, 0, sizeof(asan_introspection));
|
||||
|
||||
asan_introspection.enumerator = &mi_enumerator;
|
||||
asan_introspection.good_size = &mi_good_size;
|
||||
@ -320,8 +355,7 @@ void ReplaceSystemMalloc() {
|
||||
asan_introspection.force_lock = &mi_force_lock;
|
||||
asan_introspection.force_unlock = &mi_force_unlock;
|
||||
|
||||
static malloc_zone_t asan_zone;
|
||||
__asan::real_memset(&asan_zone, 0, sizeof(malloc_zone_t));
|
||||
internal_memset(&asan_zone, 0, sizeof(malloc_zone_t));
|
||||
|
||||
// Start with a version 4 zone which is used for OS X 10.4 and 10.5.
|
||||
asan_zone.version = 4;
|
||||
@ -333,8 +367,8 @@ void ReplaceSystemMalloc() {
|
||||
asan_zone.free = &mz_free;
|
||||
asan_zone.realloc = &mz_realloc;
|
||||
asan_zone.destroy = &mz_destroy;
|
||||
asan_zone.batch_malloc = NULL;
|
||||
asan_zone.batch_free = NULL;
|
||||
asan_zone.batch_malloc = 0;
|
||||
asan_zone.batch_free = 0;
|
||||
asan_zone.introspect = &asan_introspection;
|
||||
|
||||
// from AvailabilityMacros.h
|
||||
@ -371,18 +405,16 @@ void ReplaceSystemMalloc() {
|
||||
// Make sure the default allocator was replaced.
|
||||
CHECK(malloc_default_zone() == &asan_zone);
|
||||
|
||||
if (FLAG_replace_cfallocator) {
|
||||
static CFAllocatorContext asan_context =
|
||||
{ /*version*/ 0, /*info*/ &asan_zone,
|
||||
/*retain*/ NULL, /*release*/ NULL,
|
||||
/*copyDescription*/NULL,
|
||||
/*allocate*/ &cf_malloc,
|
||||
/*reallocate*/ &cf_realloc,
|
||||
/*deallocate*/ &cf_free,
|
||||
/*preferredSize*/ NULL };
|
||||
CFAllocatorRef cf_asan =
|
||||
CFAllocatorCreate(kCFAllocatorUseContext, &asan_context);
|
||||
CFAllocatorSetDefault(cf_asan);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace __asan
|
||||
|
141
lib/asan/asan_malloc_win.cc
Normal file
141
lib/asan/asan_malloc_win.cc
Normal file
@ -0,0 +1,141 @@
|
||||
//===-- asan_malloc_win.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.
|
||||
//
|
||||
// Windows-specific malloc interception.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_stack.h"
|
||||
|
||||
#include "interception/interception.h"
|
||||
|
||||
// ---------------------- Replacement functions ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
// FIXME: Simply defining functions with the same signature in *.obj
|
||||
// files overrides the standard functions in *.lib
|
||||
// This works well for simple helloworld-like tests but might need to be
|
||||
// revisited in the future.
|
||||
|
||||
extern "C" {
|
||||
void free(void *ptr) {
|
||||
GET_STACK_TRACE_HERE_FOR_FREE(ptr);
|
||||
return asan_free(ptr, &stack);
|
||||
}
|
||||
|
||||
void _free_dbg(void* ptr, int) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void cfree(void *ptr) {
|
||||
CHECK(!"cfree() should not be used on Windows?");
|
||||
}
|
||||
|
||||
void *malloc(size_t size) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_malloc(size, &stack);
|
||||
}
|
||||
|
||||
void* _malloc_dbg(size_t size, int , const char*, int) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void *calloc(size_t nmemb, size_t size) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_calloc(nmemb, size, &stack);
|
||||
}
|
||||
|
||||
void* _calloc_dbg(size_t n, size_t size, int, const char*, int) {
|
||||
return calloc(n, size);
|
||||
}
|
||||
|
||||
void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
|
||||
return calloc(nmemb, size);
|
||||
}
|
||||
|
||||
void *realloc(void *ptr, size_t size) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_realloc(ptr, size, &stack);
|
||||
}
|
||||
|
||||
void *_realloc_dbg(void *ptr, size_t size, int) {
|
||||
CHECK(!"_realloc_dbg should not exist!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* _recalloc(void* p, size_t n, size_t elem_size) {
|
||||
if (!p)
|
||||
return calloc(n, elem_size);
|
||||
const size_t size = n * elem_size;
|
||||
if (elem_size != 0 && size / elem_size != n)
|
||||
return 0;
|
||||
return realloc(p, size);
|
||||
}
|
||||
|
||||
size_t _msize(void *ptr) {
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;
|
||||
return asan_malloc_usable_size(ptr, &stack);
|
||||
}
|
||||
|
||||
int _CrtDbgReport(int, const char*, int,
|
||||
const char*, const char*, ...) {
|
||||
ShowStatsAndAbort();
|
||||
}
|
||||
|
||||
int _CrtDbgReportW(int reportType, const wchar_t*, int,
|
||||
const wchar_t*, const wchar_t*, ...) {
|
||||
ShowStatsAndAbort();
|
||||
}
|
||||
|
||||
int _CrtSetReportMode(int, int) {
|
||||
return 0;
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
using __interception::GetRealFunctionAddress;
|
||||
|
||||
// We don't want to include "windows.h" in this file to avoid extra attributes
|
||||
// set on malloc/free etc (e.g. dllimport), so declare a few things manually:
|
||||
extern "C" int __stdcall VirtualProtect(void* addr, size_t size,
|
||||
DWORD prot, DWORD *old_prot);
|
||||
const int PAGE_EXECUTE_READWRITE = 0x40;
|
||||
|
||||
namespace __asan {
|
||||
void ReplaceSystemMalloc() {
|
||||
#if defined(_DLL)
|
||||
# ifdef _WIN64
|
||||
# error ReplaceSystemMalloc was not tested on x64
|
||||
# endif
|
||||
char *crt_malloc;
|
||||
if (GetRealFunctionAddress("malloc", (void**)&crt_malloc)) {
|
||||
// Replace malloc in the CRT dll with a jump to our malloc.
|
||||
DWORD old_prot, unused;
|
||||
CHECK(VirtualProtect(crt_malloc, 16, PAGE_EXECUTE_READWRITE, &old_prot));
|
||||
REAL(memset)(crt_malloc, 0xCC /* int 3 */, 16); // just in case.
|
||||
|
||||
ptrdiff_t jmp_offset = (char*)malloc - (char*)crt_malloc - 5;
|
||||
crt_malloc[0] = 0xE9; // jmp, should be followed by an offset.
|
||||
REAL(memcpy)(crt_malloc + 1, &jmp_offset, sizeof(jmp_offset));
|
||||
|
||||
CHECK(VirtualProtect(crt_malloc, 16, old_prot, &unused));
|
||||
|
||||
// FYI: FlushInstructionCache is needed on Itanium etc but not on x86/x64.
|
||||
}
|
||||
|
||||
// FIXME: investigate whether anything else is needed.
|
||||
#endif
|
||||
}
|
||||
} // namespace __asan
|
||||
|
||||
#endif // _WIN32
|
@ -20,26 +20,32 @@
|
||||
// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm
|
||||
|
||||
#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1
|
||||
extern __attribute__((visibility("default"))) uintptr_t __asan_mapping_scale;
|
||||
extern __attribute__((visibility("default"))) uintptr_t __asan_mapping_offset;
|
||||
#define SHADOW_SCALE (__asan_mapping_scale)
|
||||
#define SHADOW_OFFSET (__asan_mapping_offset)
|
||||
extern __attribute__((visibility("default"))) uptr __asan_mapping_scale;
|
||||
extern __attribute__((visibility("default"))) uptr __asan_mapping_offset;
|
||||
# define SHADOW_SCALE (__asan_mapping_scale)
|
||||
# define SHADOW_OFFSET (__asan_mapping_offset)
|
||||
#else
|
||||
#define SHADOW_SCALE (3)
|
||||
#if __WORDSIZE == 32
|
||||
#define SHADOW_OFFSET (1 << 29)
|
||||
#else
|
||||
#define SHADOW_OFFSET (1ULL << 44)
|
||||
#endif
|
||||
# ifdef ANDROID
|
||||
# define SHADOW_SCALE (3)
|
||||
# define SHADOW_OFFSET (0)
|
||||
# else
|
||||
# define SHADOW_SCALE (3)
|
||||
# if __WORDSIZE == 32
|
||||
# define SHADOW_OFFSET (1 << 29)
|
||||
# else
|
||||
# define SHADOW_OFFSET (1ULL << 44)
|
||||
# endif
|
||||
# endif
|
||||
#endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET
|
||||
|
||||
#define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE)
|
||||
#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) | (SHADOW_OFFSET))
|
||||
#define SHADOW_TO_MEM(shadow) (((shadow) - SHADOW_OFFSET) << SHADOW_SCALE)
|
||||
|
||||
#if __WORDSIZE == 64
|
||||
static const size_t kHighMemEnd = 0x00007fffffffffffUL;
|
||||
static const uptr kHighMemEnd = 0x00007fffffffffffUL;
|
||||
#else // __WORDSIZE == 32
|
||||
static const size_t kHighMemEnd = 0xffffffff;
|
||||
static const uptr kHighMemEnd = 0xffffffff;
|
||||
#endif // __WORDSIZE
|
||||
|
||||
|
||||
@ -62,39 +68,55 @@ extern __attribute__((visibility("default"))) uintptr_t __asan_mapping_offset;
|
||||
|
||||
namespace __asan {
|
||||
|
||||
static inline bool AddrIsInLowMem(uintptr_t a) {
|
||||
static inline bool AddrIsInLowMem(uptr a) {
|
||||
return a < kLowMemEnd;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInLowShadow(uintptr_t a) {
|
||||
static inline bool AddrIsInLowShadow(uptr a) {
|
||||
return a >= kLowShadowBeg && a <= kLowShadowEnd;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInHighMem(uintptr_t a) {
|
||||
static inline bool AddrIsInHighMem(uptr a) {
|
||||
return a >= kHighMemBeg && a <= kHighMemEnd;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInMem(uintptr_t a) {
|
||||
static inline bool AddrIsInMem(uptr a) {
|
||||
return AddrIsInLowMem(a) || AddrIsInHighMem(a);
|
||||
}
|
||||
|
||||
static inline uintptr_t MemToShadow(uintptr_t p) {
|
||||
static inline uptr MemToShadow(uptr p) {
|
||||
CHECK(AddrIsInMem(p));
|
||||
return MEM_TO_SHADOW(p);
|
||||
}
|
||||
|
||||
static inline bool AddrIsInHighShadow(uintptr_t a) {
|
||||
static inline bool AddrIsInHighShadow(uptr a) {
|
||||
return a >= kHighShadowBeg && a <= kHighMemEnd;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInShadow(uintptr_t a) {
|
||||
static inline bool AddrIsInShadow(uptr a) {
|
||||
return AddrIsInLowShadow(a) || AddrIsInHighShadow(a);
|
||||
}
|
||||
|
||||
static inline bool AddrIsAlignedByGranularity(uintptr_t a) {
|
||||
static inline bool AddrIsInShadowGap(uptr a) {
|
||||
return a >= kShadowGapBeg && a <= kShadowGapEnd;
|
||||
}
|
||||
|
||||
static inline bool AddrIsAlignedByGranularity(uptr a) {
|
||||
return (a & (SHADOW_GRANULARITY - 1)) == 0;
|
||||
}
|
||||
|
||||
static inline bool AddressIsPoisoned(uptr a) {
|
||||
const uptr kAccessSize = 1;
|
||||
u8 *shadow_address = (u8*)MemToShadow(a);
|
||||
s8 shadow_value = *shadow_address;
|
||||
if (shadow_value) {
|
||||
u8 last_accessed_byte = (a & (SHADOW_GRANULARITY - 1))
|
||||
+ kAccessSize - 1;
|
||||
return (last_accessed_byte >= shadow_value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // ASAN_MAPPING_H
|
||||
|
56
lib/asan/asan_new_delete.cc
Normal file
56
lib/asan/asan_new_delete.cc
Normal file
@ -0,0 +1,56 @@
|
||||
//===-- asan_interceptors.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.
|
||||
//
|
||||
// Interceptors for operators new and delete.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_internal.h"
|
||||
#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
|
||||
// with our replacements will actually be loaded from static ASan
|
||||
// run-time library at link-time.
|
||||
void ReplaceOperatorsNewAndDelete() { }
|
||||
}
|
||||
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
#define OPERATOR_NEW_BODY \
|
||||
GET_STACK_TRACE_HERE_FOR_MALLOC;\
|
||||
return asan_memalign(0, size, &stack);
|
||||
|
||||
#ifdef ANDROID
|
||||
void *operator new(size_t size) { OPERATOR_NEW_BODY; }
|
||||
void *operator new[](size_t size) { OPERATOR_NEW_BODY; }
|
||||
#else
|
||||
void *operator new(size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; }
|
||||
void *operator new[](size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; }
|
||||
void *operator new(size_t size, std::nothrow_t const&) throw()
|
||||
{ OPERATOR_NEW_BODY; }
|
||||
void *operator new[](size_t size, std::nothrow_t const&) throw()
|
||||
{ OPERATOR_NEW_BODY; }
|
||||
#endif
|
||||
|
||||
#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; }
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_poisoning.cc ---------------------------------------*- C++ -*-===//
|
||||
//===-- asan_poisoning.cc -------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -19,21 +19,22 @@
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void PoisonShadow(uintptr_t addr, size_t size, uint8_t value) {
|
||||
void PoisonShadow(uptr addr, uptr size, u8 value) {
|
||||
CHECK(AddrIsAlignedByGranularity(addr));
|
||||
CHECK(AddrIsAlignedByGranularity(addr + size));
|
||||
uintptr_t shadow_beg = MemToShadow(addr);
|
||||
uintptr_t shadow_end = MemToShadow(addr + size);
|
||||
real_memset((void*)shadow_beg, value, shadow_end - shadow_beg);
|
||||
uptr shadow_beg = MemToShadow(addr);
|
||||
uptr shadow_end = MemToShadow(addr + size);
|
||||
CHECK(REAL(memset) != 0);
|
||||
REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
|
||||
}
|
||||
|
||||
void PoisonShadowPartialRightRedzone(uintptr_t addr,
|
||||
uintptr_t size,
|
||||
uintptr_t redzone_size,
|
||||
uint8_t value) {
|
||||
void PoisonShadowPartialRightRedzone(uptr addr,
|
||||
uptr size,
|
||||
uptr redzone_size,
|
||||
u8 value) {
|
||||
CHECK(AddrIsAlignedByGranularity(addr));
|
||||
uint8_t *shadow = (uint8_t*)MemToShadow(addr);
|
||||
for (uintptr_t i = 0; i < redzone_size;
|
||||
u8 *shadow = (u8*)MemToShadow(addr);
|
||||
for (uptr i = 0; i < redzone_size;
|
||||
i += SHADOW_GRANULARITY, shadow++) {
|
||||
if (i + SHADOW_GRANULARITY <= size) {
|
||||
*shadow = 0; // fully addressable
|
||||
@ -47,12 +48,12 @@ void PoisonShadowPartialRightRedzone(uintptr_t addr,
|
||||
|
||||
|
||||
struct ShadowSegmentEndpoint {
|
||||
uint8_t *chunk;
|
||||
int8_t offset; // in [0, SHADOW_GRANULARITY)
|
||||
int8_t value; // = *chunk;
|
||||
u8 *chunk;
|
||||
s8 offset; // in [0, SHADOW_GRANULARITY)
|
||||
s8 value; // = *chunk;
|
||||
|
||||
explicit ShadowSegmentEndpoint(uintptr_t address) {
|
||||
chunk = (uint8_t*)MemToShadow(address);
|
||||
explicit ShadowSegmentEndpoint(uptr address) {
|
||||
chunk = (u8*)MemToShadow(address);
|
||||
offset = address & (SHADOW_GRANULARITY - 1);
|
||||
value = *chunk;
|
||||
}
|
||||
@ -73,18 +74,19 @@ using namespace __asan; // NOLINT
|
||||
// at least [left, AlignDown(right)).
|
||||
// * if user asks to unpoison region [left, right), the program unpoisons
|
||||
// at most [AlignDown(left), right).
|
||||
void __asan_poison_memory_region(void const volatile *addr, size_t size) {
|
||||
if (!FLAG_allow_user_poisoning || size == 0) return;
|
||||
uintptr_t beg_addr = (uintptr_t)addr;
|
||||
uintptr_t end_addr = beg_addr + size;
|
||||
if (FLAG_v >= 1) {
|
||||
Printf("Trying to poison memory region [%p, %p)\n", beg_addr, end_addr);
|
||||
void __asan_poison_memory_region(void const volatile *addr, uptr size) {
|
||||
if (!flags()->allow_user_poisoning || size == 0) return;
|
||||
uptr beg_addr = (uptr)addr;
|
||||
uptr end_addr = beg_addr + size;
|
||||
if (flags()->verbosity >= 1) {
|
||||
Printf("Trying to poison memory region [%p, %p)\n",
|
||||
(void*)beg_addr, (void*)end_addr);
|
||||
}
|
||||
ShadowSegmentEndpoint beg(beg_addr);
|
||||
ShadowSegmentEndpoint end(end_addr);
|
||||
if (beg.chunk == end.chunk) {
|
||||
CHECK(beg.offset < end.offset);
|
||||
int8_t value = beg.value;
|
||||
s8 value = beg.value;
|
||||
CHECK(value == end.value);
|
||||
// We can only poison memory if the byte in end.offset is unaddressable.
|
||||
// No need to re-poison memory if it is poisoned already.
|
||||
@ -107,25 +109,26 @@ void __asan_poison_memory_region(void const volatile *addr, size_t size) {
|
||||
}
|
||||
beg.chunk++;
|
||||
}
|
||||
real_memset(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk);
|
||||
REAL(memset)(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk);
|
||||
// Poison if byte in end.offset is unaddressable.
|
||||
if (end.value > 0 && end.value <= end.offset) {
|
||||
*end.chunk = kAsanUserPoisonedMemoryMagic;
|
||||
}
|
||||
}
|
||||
|
||||
void __asan_unpoison_memory_region(void const volatile *addr, size_t size) {
|
||||
if (!FLAG_allow_user_poisoning || size == 0) return;
|
||||
uintptr_t beg_addr = (uintptr_t)addr;
|
||||
uintptr_t end_addr = beg_addr + size;
|
||||
if (FLAG_v >= 1) {
|
||||
Printf("Trying to unpoison memory region [%p, %p)\n", beg_addr, end_addr);
|
||||
void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
|
||||
if (!flags()->allow_user_poisoning || size == 0) return;
|
||||
uptr beg_addr = (uptr)addr;
|
||||
uptr end_addr = beg_addr + size;
|
||||
if (flags()->verbosity >= 1) {
|
||||
Printf("Trying to unpoison memory region [%p, %p)\n",
|
||||
(void*)beg_addr, (void*)end_addr);
|
||||
}
|
||||
ShadowSegmentEndpoint beg(beg_addr);
|
||||
ShadowSegmentEndpoint end(end_addr);
|
||||
if (beg.chunk == end.chunk) {
|
||||
CHECK(beg.offset < end.offset);
|
||||
int8_t value = beg.value;
|
||||
s8 value = beg.value;
|
||||
CHECK(value == end.value);
|
||||
// We unpoison memory bytes up to enbytes up to end.offset if it is not
|
||||
// unpoisoned already.
|
||||
@ -139,21 +142,12 @@ void __asan_unpoison_memory_region(void const volatile *addr, size_t size) {
|
||||
*beg.chunk = 0;
|
||||
beg.chunk++;
|
||||
}
|
||||
real_memset(beg.chunk, 0, end.chunk - beg.chunk);
|
||||
REAL(memset)(beg.chunk, 0, end.chunk - beg.chunk);
|
||||
if (end.offset > 0 && end.value != 0) {
|
||||
*end.chunk = Max(end.value, end.offset);
|
||||
}
|
||||
}
|
||||
|
||||
bool __asan_address_is_poisoned(void const volatile *addr) {
|
||||
const size_t kAccessSize = 1;
|
||||
uintptr_t address = (uintptr_t)addr;
|
||||
uint8_t *shadow_address = (uint8_t*)MemToShadow(address);
|
||||
int8_t shadow_value = *shadow_address;
|
||||
if (shadow_value) {
|
||||
uint8_t last_accessed_byte = (address & (SHADOW_GRANULARITY - 1))
|
||||
+ kAccessSize - 1;
|
||||
return (last_accessed_byte >= shadow_value);
|
||||
}
|
||||
return false;
|
||||
return __asan::AddressIsPoisoned((uptr)addr);
|
||||
}
|
||||
|
126
lib/asan/asan_posix.cc
Normal file
126
lib/asan/asan_posix.cc
Normal file
@ -0,0 +1,126 @@
|
||||
//===-- asan_linux.cc -----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Posix-specific details.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
|
||||
#include "asan_internal.h"
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_procmaps.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough.
|
||||
|
||||
namespace __asan {
|
||||
|
||||
static void MaybeInstallSigaction(int signum,
|
||||
void (*handler)(int, siginfo_t *, void *)) {
|
||||
if (!AsanInterceptsSignal(signum))
|
||||
return;
|
||||
struct sigaction sigact;
|
||||
REAL(memset)(&sigact, 0, sizeof(sigact));
|
||||
sigact.sa_sigaction = handler;
|
||||
sigact.sa_flags = SA_SIGINFO;
|
||||
if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
|
||||
CHECK(0 == REAL(sigaction)(signum, &sigact, 0));
|
||||
if (flags()->verbosity >= 1) {
|
||||
Report("Installed the sigaction for signal %d\n", signum);
|
||||
}
|
||||
}
|
||||
|
||||
static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) {
|
||||
uptr addr = (uptr)siginfo->si_addr;
|
||||
// Write the first message using the bullet-proof write.
|
||||
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();
|
||||
}
|
||||
|
||||
void SetAlternateSignalStack() {
|
||||
stack_t altstack, oldstack;
|
||||
CHECK(0 == sigaltstack(0, &oldstack));
|
||||
// If the alternate stack is already in place, do nothing.
|
||||
if ((oldstack.ss_flags & SS_DISABLE) == 0) return;
|
||||
// TODO(glider): the mapped stack should have the MAP_STACK flag in the
|
||||
// future. It is not required by man 2 sigaltstack now (they're using
|
||||
// malloc()).
|
||||
void* base = MmapOrDie(kAltStackSize, __FUNCTION__);
|
||||
altstack.ss_sp = base;
|
||||
altstack.ss_flags = 0;
|
||||
altstack.ss_size = kAltStackSize;
|
||||
CHECK(0 == sigaltstack(&altstack, 0));
|
||||
if (flags()->verbosity > 0) {
|
||||
Report("Alternative stack for T%d set: [%p,%p)\n",
|
||||
asanThreadRegistry().GetCurrentTidOrInvalid(),
|
||||
altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size);
|
||||
}
|
||||
}
|
||||
|
||||
void UnsetAlternateSignalStack() {
|
||||
stack_t altstack, oldstack;
|
||||
altstack.ss_sp = 0;
|
||||
altstack.ss_flags = SS_DISABLE;
|
||||
altstack.ss_size = 0;
|
||||
CHECK(0 == sigaltstack(&altstack, &oldstack));
|
||||
UnmapOrDie(oldstack.ss_sp, oldstack.ss_size);
|
||||
}
|
||||
|
||||
void InstallSignalHandlers() {
|
||||
// Set the alternate signal stack for the main thread.
|
||||
// This will cause SetAlternateSignalStack to be called twice, but the stack
|
||||
// will be actually set only once.
|
||||
if (flags()->use_sigaltstack) SetAlternateSignalStack();
|
||||
MaybeInstallSigaction(SIGSEGV, ASAN_OnSIGSEGV);
|
||||
MaybeInstallSigaction(SIGBUS, ASAN_OnSIGSEGV);
|
||||
}
|
||||
|
||||
// ---------------------- TSD ---------------- {{{1
|
||||
|
||||
static pthread_key_t tsd_key;
|
||||
static bool tsd_key_inited = false;
|
||||
void AsanTSDInit(void (*destructor)(void *tsd)) {
|
||||
CHECK(!tsd_key_inited);
|
||||
tsd_key_inited = true;
|
||||
CHECK(0 == pthread_key_create(&tsd_key, destructor));
|
||||
}
|
||||
|
||||
void *AsanTSDGet() {
|
||||
CHECK(tsd_key_inited);
|
||||
return pthread_getspecific(tsd_key);
|
||||
}
|
||||
|
||||
void AsanTSDSet(void *tsd) {
|
||||
CHECK(tsd_key_inited);
|
||||
pthread_setspecific(tsd_key, tsd);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // __linux__ || __APPLE_
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_printf.cc ------------------------------------------*- C++ -*-===//
|
||||
//===-- asan_printf.cc ----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -16,131 +16,19 @@
|
||||
|
||||
#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 RawWrite(const char *buffer) {
|
||||
static const char *kRawWriteError = "RawWrite can't output requested buffer!";
|
||||
ssize_t length = (ssize_t)internal_strlen(buffer);
|
||||
if (length != AsanWrite(2, buffer, length)) {
|
||||
AsanWrite(2, kRawWriteError, internal_strlen(kRawWriteError));
|
||||
ASAN_DIE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int AppendChar(char **buff, const char *buff_end, char c) {
|
||||
if (*buff < buff_end) {
|
||||
**buff = c;
|
||||
(*buff)++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Appends number in a given base to buffer. If its length is less than
|
||||
// "minimal_num_length", it is padded with leading zeroes.
|
||||
static int AppendUnsigned(char **buff, const char *buff_end, uint64_t num,
|
||||
uint8_t base, uint8_t minimal_num_length) {
|
||||
size_t const kMaxLen = 30;
|
||||
RAW_CHECK(base == 10 || base == 16);
|
||||
RAW_CHECK(minimal_num_length < kMaxLen);
|
||||
size_t num_buffer[kMaxLen];
|
||||
size_t pos = 0;
|
||||
do {
|
||||
RAW_CHECK_MSG(pos < kMaxLen, "appendNumber buffer overflow");
|
||||
num_buffer[pos++] = num % base;
|
||||
num /= base;
|
||||
} while (num > 0);
|
||||
while (pos < minimal_num_length) num_buffer[pos++] = 0;
|
||||
int result = 0;
|
||||
while (pos-- > 0) {
|
||||
size_t digit = num_buffer[pos];
|
||||
result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit
|
||||
: 'a' + digit - 10);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int AppendSignedDecimal(char **buff, const char *buff_end,
|
||||
int64_t num) {
|
||||
int result = 0;
|
||||
if (num < 0) {
|
||||
result += AppendChar(buff, buff_end, '-');
|
||||
num = -num;
|
||||
}
|
||||
result += AppendUnsigned(buff, buff_end, (uint64_t)num, 10, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int AppendString(char **buff, const char *buff_end,
|
||||
const char *s) {
|
||||
// Avoid library functions like stpcpy here.
|
||||
RAW_CHECK(s);
|
||||
int result = 0;
|
||||
for (; *s; s++) {
|
||||
result += AppendChar(buff, buff_end, *s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int AppendPointer(char **buff, const char *buff_end,
|
||||
uint64_t ptr_value) {
|
||||
int result = 0;
|
||||
result += AppendString(buff, buff_end, "0x");
|
||||
result += AppendUnsigned(buff, buff_end, ptr_value, 16,
|
||||
(__WORDSIZE == 64) ? 12 : 8);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int VSNPrintf(char *buff, int buff_length,
|
||||
const char *format, va_list args) {
|
||||
static const char *kPrintfFormatsHelp = "Supported Printf formats: "
|
||||
"%%[l]{d,u,x}; %%p; %%s";
|
||||
RAW_CHECK(format);
|
||||
RAW_CHECK(buff_length > 0);
|
||||
const char *buff_end = &buff[buff_length - 1];
|
||||
const char *cur = format;
|
||||
int result = 0;
|
||||
for (; *cur; cur++) {
|
||||
if (*cur == '%') {
|
||||
cur++;
|
||||
bool have_l = (*cur == 'l');
|
||||
cur += have_l;
|
||||
int64_t dval;
|
||||
uint64_t uval, xval;
|
||||
switch (*cur) {
|
||||
case 'd': dval = have_l ? va_arg(args, intptr_t)
|
||||
: va_arg(args, int);
|
||||
result += AppendSignedDecimal(&buff, buff_end, dval);
|
||||
break;
|
||||
case 'u': uval = have_l ? va_arg(args, uintptr_t)
|
||||
: va_arg(args, unsigned int);
|
||||
result += AppendUnsigned(&buff, buff_end, uval, 10, 0);
|
||||
break;
|
||||
case 'x': xval = have_l ? va_arg(args, uintptr_t)
|
||||
: va_arg(args, unsigned int);
|
||||
result += AppendUnsigned(&buff, buff_end, xval, 16, 0);
|
||||
break;
|
||||
case 'p': RAW_CHECK_MSG(!have_l, kPrintfFormatsHelp);
|
||||
result += AppendPointer(&buff, buff_end,
|
||||
va_arg(args, uintptr_t));
|
||||
break;
|
||||
case 's': RAW_CHECK_MSG(!have_l, kPrintfFormatsHelp);
|
||||
result += AppendString(&buff, buff_end, va_arg(args, char*));
|
||||
break;
|
||||
default: RAW_CHECK_MSG(false, kPrintfFormatsHelp);
|
||||
}
|
||||
} else {
|
||||
result += AppendChar(&buff, buff_end, *cur);
|
||||
}
|
||||
}
|
||||
RAW_CHECK(buff <= buff_end);
|
||||
AppendChar(&buff, buff_end + 1, '\0');
|
||||
return result;
|
||||
}
|
||||
|
||||
void Printf(const char *format, ...) {
|
||||
void AsanPrintf(const char *format, ...) {
|
||||
const int kLen = 1024 * 4;
|
||||
char buffer[kLen];
|
||||
va_list args;
|
||||
@ -149,25 +37,14 @@ void Printf(const char *format, ...) {
|
||||
va_end(args);
|
||||
RAW_CHECK_MSG(needed_length < kLen, "Buffer in Printf is too short!\n");
|
||||
RawWrite(buffer);
|
||||
AppendToErrorMessageBuffer(buffer);
|
||||
}
|
||||
|
||||
// Writes at most "length" symbols to "buffer" (including trailing '\0').
|
||||
// Returns the number of symbols that should have been written to buffer
|
||||
// (not including trailing '\0'). Thus, the string is truncated
|
||||
// iff return value is not less than "length".
|
||||
int SNPrintf(char *buffer, size_t length, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int needed_length = VSNPrintf(buffer, length, format, args);
|
||||
va_end(args);
|
||||
return needed_length;
|
||||
}
|
||||
|
||||
// Like Printf, but prints the current PID before the output string.
|
||||
void Report(const char *format, ...) {
|
||||
// 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 = SNPrintf(buffer, kLen, "==%d== ", getpid());
|
||||
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);
|
||||
@ -176,6 +53,7 @@ void Report(const char *format, ...) {
|
||||
va_end(args);
|
||||
RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
|
||||
RawWrite(buffer);
|
||||
AppendToErrorMessageBuffer(buffer);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
//===-- asan_stack.cc -------------------------------------------*- C++ -*-===//
|
||||
//===-- asan_stack.cc -----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -16,12 +16,8 @@
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if ASAN_USE_SYSINFO == 1
|
||||
#include "sysinfo/sysinfo.h"
|
||||
#endif
|
||||
#include "sanitizer_common/sanitizer_procmaps.h"
|
||||
#include "sanitizer_common/sanitizer_symbolizer.h"
|
||||
|
||||
#ifdef ASAN_USE_EXTERNAL_SYMBOLIZER
|
||||
extern bool
|
||||
@ -30,183 +26,139 @@ ASAN_USE_EXTERNAL_SYMBOLIZER(const void *pc, char *out, int out_size);
|
||||
|
||||
namespace __asan {
|
||||
|
||||
// ----------------------- ProcSelfMaps ----------------------------- {{{1
|
||||
#if ASAN_USE_SYSINFO == 1
|
||||
class ProcSelfMaps {
|
||||
public:
|
||||
void Init() {
|
||||
ScopedLock lock(&mu_);
|
||||
if (map_size_ != 0) return; // already inited
|
||||
if (FLAG_v >= 2) {
|
||||
Printf("ProcSelfMaps::Init()\n");
|
||||
}
|
||||
ProcMapsIterator it(0, &proc_self_maps_); // 0 means "current pid"
|
||||
|
||||
uint64 start, end, offset;
|
||||
int64 inode;
|
||||
char *flags, *filename;
|
||||
CHECK(map_size_ == 0);
|
||||
while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
|
||||
CHECK(map_size_ < kMaxProcSelfMapsSize);
|
||||
Mapping &mapping = memory_map[map_size_];
|
||||
mapping.beg = start;
|
||||
mapping.end = end;
|
||||
mapping.offset = offset;
|
||||
real_strncpy(mapping.name,
|
||||
filename, ASAN_ARRAY_SIZE(mapping.name));
|
||||
mapping.name[ASAN_ARRAY_SIZE(mapping.name) - 1] = 0;
|
||||
if (FLAG_v >= 2) {
|
||||
Printf("[%ld] [%p,%p] off %p %s\n", map_size_,
|
||||
mapping.beg, mapping.end, mapping.offset, mapping.name);
|
||||
}
|
||||
map_size_++;
|
||||
}
|
||||
}
|
||||
|
||||
void Print() {
|
||||
Printf("%s\n", proc_self_maps_);
|
||||
}
|
||||
|
||||
void PrintPc(uintptr_t pc, int idx) {
|
||||
for (size_t i = 0; i < map_size_; i++) {
|
||||
Mapping &m = memory_map[i];
|
||||
if (pc >= m.beg && pc < m.end) {
|
||||
uintptr_t offset = pc - m.beg;
|
||||
if (i == 0) offset = pc;
|
||||
Printf(" #%d 0x%lx (%s+0x%lx)\n", idx, pc, m.name, offset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Printf(" #%d 0x%lx\n", idx, pc);
|
||||
}
|
||||
|
||||
private:
|
||||
void copy_until_new_line(const char *str, char *dest, size_t max_size) {
|
||||
size_t i = 0;
|
||||
for (; str[i] && str[i] != '\n' && i < max_size - 1; i++) {
|
||||
dest[i] = str[i];
|
||||
}
|
||||
dest[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
struct Mapping {
|
||||
uintptr_t beg, end, offset;
|
||||
char name[1000];
|
||||
};
|
||||
static const size_t kMaxNumMapEntries = 4096;
|
||||
static const size_t kMaxProcSelfMapsSize = 1 << 20;
|
||||
ProcMapsIterator::Buffer proc_self_maps_;
|
||||
size_t map_size_;
|
||||
Mapping memory_map[kMaxNumMapEntries];
|
||||
|
||||
static AsanLock mu_;
|
||||
};
|
||||
|
||||
static ProcSelfMaps proc_self_maps;
|
||||
AsanLock ProcSelfMaps::mu_(LINKER_INITIALIZED);
|
||||
|
||||
|
||||
void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
|
||||
proc_self_maps.Init();
|
||||
for (size_t i = 0; i < size && addr[i]; i++) {
|
||||
uintptr_t pc = addr[i];
|
||||
// int line;
|
||||
proc_self_maps.PrintPc(pc, i);
|
||||
// Printf(" #%ld 0x%lx %s\n", i, pc, rtn.c_str());
|
||||
}
|
||||
// ----------------------- 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;
|
||||
}
|
||||
#elif defined(ASAN_USE_EXTERNAL_SYMBOLIZER)
|
||||
void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
|
||||
for (size_t i = 0; i < size && addr[i]; i++) {
|
||||
uintptr_t pc = addr[i];
|
||||
|
||||
#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));
|
||||
Printf(" #%ld 0x%lx %s\n", i, pc, buff);
|
||||
AsanPrintf(" #%zu 0x%zx %s\n", i, pc, buff);
|
||||
}
|
||||
}
|
||||
|
||||
#else // ASAN_USE_SYSINFO
|
||||
void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
|
||||
for (size_t i = 0; i < size && addr[i]; i++) {
|
||||
uintptr_t pc = addr[i];
|
||||
Printf(" #%ld 0x%lx\n", i, pc);
|
||||
#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_SYSINFO
|
||||
#endif // ASAN_USE_EXTERNAL_SYMBOLIZER
|
||||
|
||||
#ifdef __arm__
|
||||
#define UNWIND_STOP _URC_END_OF_STACK
|
||||
#define UNWIND_CONTINUE _URC_OK
|
||||
#else
|
||||
#define UNWIND_STOP _URC_NORMAL_STOP
|
||||
#define UNWIND_CONTINUE _URC_NO_REASON
|
||||
#endif
|
||||
|
||||
// ----------------------- AsanStackTrace ----------------------------- {{{1
|
||||
uintptr_t AsanStackTrace::GetCurrentPc() {
|
||||
uptr AsanStackTrace::GetCurrentPc() {
|
||||
return GET_CALLER_PC();
|
||||
}
|
||||
|
||||
void AsanStackTrace::FastUnwindStack(uintptr_t pc, uintptr_t bp) {
|
||||
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;
|
||||
uintptr_t *frame = (uintptr_t*)bp;
|
||||
uintptr_t *prev_frame = frame;
|
||||
uintptr_t *top = (uintptr_t*)t->stack_top();
|
||||
uintptr_t *bottom = (uintptr_t*)t->stack_bottom();
|
||||
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 &&
|
||||
frame < top - 2 &&
|
||||
frame > bottom &&
|
||||
size < max_size) {
|
||||
uintptr_t pc1 = frame[1];
|
||||
uptr pc1 = frame[1];
|
||||
if (pc1 != pc) {
|
||||
trace[size++] = pc1;
|
||||
}
|
||||
prev_frame = frame;
|
||||
frame = (uintptr_t*)frame[0];
|
||||
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.
|
||||
size_t AsanStackTrace::CompressStack(AsanStackTrace *stack,
|
||||
uint32_t *compressed, size_t size) {
|
||||
uptr AsanStackTrace::CompressStack(AsanStackTrace *stack,
|
||||
u32 *compressed, uptr size) {
|
||||
#if __WORDSIZE == 32
|
||||
// Don't compress, just copy.
|
||||
size_t res = 0;
|
||||
for (size_t i = 0; i < stack->size && i < size; i++) {
|
||||
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.
|
||||
uintptr_t prev_pc = 0;
|
||||
const uintptr_t kMaxOffset = (1ULL << 30) - 1;
|
||||
uintptr_t c_index = 0;
|
||||
size_t res = 0;
|
||||
for (size_t i = 0, n = stack->size; i < n; i++) {
|
||||
uintptr_t pc = stack->trace[i];
|
||||
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 ((int64_t)pc < 0) break;
|
||||
// Printf("C pc[%ld] %lx\n", i, pc);
|
||||
if ((s64)pc < 0) break;
|
||||
// Printf("C pc[%zu] %zx\n", i, pc);
|
||||
if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) {
|
||||
uintptr_t offset = (int64_t)(pc - prev_pc);
|
||||
uptr offset = (s64)(pc - prev_pc);
|
||||
offset |= (1U << 31);
|
||||
if (c_index >= size) break;
|
||||
// Printf("C co[%ld] offset %lx\n", i, offset);
|
||||
// Printf("C co[%zu] offset %zx\n", i, offset);
|
||||
compressed[c_index++] = offset;
|
||||
} else {
|
||||
uintptr_t hi = pc >> 32;
|
||||
uintptr_t lo = (pc << 32) >> 32;
|
||||
uptr hi = pc >> 32;
|
||||
uptr lo = (pc << 32) >> 32;
|
||||
CHECK((hi & (1 << 31)) == 0);
|
||||
if (c_index + 1 >= size) break;
|
||||
// Printf("C co[%ld] hi/lo: %lx %lx\n", c_index, hi, lo);
|
||||
// Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo);
|
||||
compressed[c_index++] = hi;
|
||||
compressed[c_index++] = lo;
|
||||
}
|
||||
@ -224,53 +176,53 @@ size_t AsanStackTrace::CompressStack(AsanStackTrace *stack,
|
||||
AsanStackTrace check_stack;
|
||||
UncompressStack(&check_stack, compressed, size);
|
||||
if (res < check_stack.size) {
|
||||
Printf("res %ld check_stack.size %ld; c_size %ld\n", res,
|
||||
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(uintptr_t)));
|
||||
CHECK(0 == REAL(memcmp)(check_stack.trace, stack->trace,
|
||||
check_stack.size * sizeof(uptr)));
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void AsanStackTrace::UncompressStack(AsanStackTrace *stack,
|
||||
uint32_t *compressed, size_t size) {
|
||||
u32 *compressed, uptr size) {
|
||||
#if __WORDSIZE == 32
|
||||
// Don't uncompress, just copy.
|
||||
stack->size = 0;
|
||||
for (size_t i = 0; i < size && i < kStackTraceMax; i++) {
|
||||
for (uptr i = 0; i < size && i < kStackTraceMax; i++) {
|
||||
if (!compressed[i]) break;
|
||||
stack->size++;
|
||||
stack->trace[i] = compressed[i];
|
||||
}
|
||||
#else // 64 bits, uncompress
|
||||
uintptr_t prev_pc = 0;
|
||||
uptr prev_pc = 0;
|
||||
stack->size = 0;
|
||||
for (size_t i = 0; i < size && stack->size < kStackTraceMax; i++) {
|
||||
uint32_t x = compressed[i];
|
||||
uintptr_t pc = 0;
|
||||
for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) {
|
||||
u32 x = compressed[i];
|
||||
uptr pc = 0;
|
||||
if (x & (1U << 31)) {
|
||||
// Printf("U co[%ld] offset: %x\n", i, x);
|
||||
// Printf("U co[%zu] offset: %x\n", i, x);
|
||||
// this is an offset
|
||||
int32_t offset = x;
|
||||
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;
|
||||
uintptr_t hi = x;
|
||||
uintptr_t lo = compressed[i+1];
|
||||
// Printf("U co[%ld] hi/lo: %lx %lx\n", i, hi, lo);
|
||||
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[%ld] %lx\n", stack->size, pc);
|
||||
// Printf("U pc[%zu] %zx\n", stack->size, pc);
|
||||
stack->trace[stack->size++] = pc;
|
||||
prev_pc = pc;
|
||||
}
|
||||
|
@ -18,77 +18,87 @@
|
||||
|
||||
namespace __asan {
|
||||
|
||||
static const size_t kStackTraceMax = 64;
|
||||
static const uptr kStackTraceMax = 64;
|
||||
|
||||
struct AsanStackTrace {
|
||||
size_t size;
|
||||
size_t max_size;
|
||||
uintptr_t trace[kStackTraceMax];
|
||||
static void PrintStack(uintptr_t *addr, size_t size);
|
||||
uptr size;
|
||||
uptr max_size;
|
||||
uptr trace[kStackTraceMax];
|
||||
static void PrintStack(uptr *addr, uptr size);
|
||||
void PrintStack() {
|
||||
PrintStack(this->trace, this->size);
|
||||
}
|
||||
void CopyTo(uintptr_t *dst, size_t dst_size) {
|
||||
for (size_t i = 0; i < size && i < dst_size; i++)
|
||||
void CopyTo(uptr *dst, uptr dst_size) {
|
||||
for (uptr i = 0; i < size && i < dst_size; i++)
|
||||
dst[i] = trace[i];
|
||||
for (size_t i = size; i < dst_size; i++)
|
||||
for (uptr i = size; i < dst_size; i++)
|
||||
dst[i] = 0;
|
||||
}
|
||||
|
||||
void CopyFrom(uintptr_t *src, size_t src_size) {
|
||||
void CopyFrom(uptr *src, uptr src_size) {
|
||||
size = src_size;
|
||||
if (size > kStackTraceMax) size = kStackTraceMax;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
for (uptr i = 0; i < size; i++) {
|
||||
trace[i] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
void FastUnwindStack(uintptr_t pc, uintptr_t bp);
|
||||
// static _Unwind_Reason_Code Unwind_Trace(
|
||||
// struct _Unwind_Context *ctx, void *param);
|
||||
static uintptr_t GetCurrentPc();
|
||||
void GetStackTrace(uptr max_s, uptr pc, uptr bp);
|
||||
|
||||
static size_t CompressStack(AsanStackTrace *stack,
|
||||
uint32_t *compressed, size_t size);
|
||||
void FastUnwindStack(uptr pc, uptr bp);
|
||||
|
||||
static uptr GetCurrentPc();
|
||||
|
||||
static uptr CompressStack(AsanStackTrace *stack,
|
||||
u32 *compressed, uptr size);
|
||||
static void UncompressStack(AsanStackTrace *stack,
|
||||
uint32_t *compressed, size_t size);
|
||||
size_t full_frame_count;
|
||||
u32 *compressed, uptr size);
|
||||
};
|
||||
|
||||
} // 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, fast_unwind, pc, bp) \
|
||||
AsanStackTrace stack; \
|
||||
{ \
|
||||
uintptr_t saved_pc = pc; \
|
||||
uintptr_t saved_bp = bp; \
|
||||
stack.size = 0; \
|
||||
stack.full_frame_count = 0; \
|
||||
stack.trace[0] = saved_pc; \
|
||||
if ((max_s) > 1) { \
|
||||
stack.max_size = max_s; \
|
||||
stack.FastUnwindStack(saved_pc, saved_bp); \
|
||||
} \
|
||||
} \
|
||||
#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp) \
|
||||
AsanStackTrace stack; \
|
||||
stack.GetStackTrace(max_s, pc, bp)
|
||||
|
||||
#define GET_STACK_TRACE_HERE(max_size, fast_unwind) \
|
||||
GET_STACK_TRACE_WITH_PC_AND_BP(max_size, fast_unwind, \
|
||||
AsanStackTrace::GetCurrentPc(), GET_CURRENT_FRAME()) \
|
||||
// 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_FOR_MALLOC \
|
||||
GET_STACK_TRACE_HERE(FLAG_malloc_context_size, FLAG_fast_unwind)
|
||||
#define GET_STACK_TRACE_HERE(max_size) \
|
||||
GET_STACK_TRACE_WITH_PC_AND_BP(max_size, \
|
||||
AsanStackTrace::GetCurrentPc(), GET_CURRENT_FRAME())
|
||||
|
||||
#define GET_STACK_TRACE_HERE_FOR_FREE(ptr) \
|
||||
GET_STACK_TRACE_HERE(FLAG_malloc_context_size, FLAG_fast_unwind)
|
||||
#define GET_STACK_TRACE_HERE_FOR_MALLOC \
|
||||
GET_STACK_TRACE_HERE(flags()->malloc_context_size)
|
||||
|
||||
#define GET_STACK_TRACE_HERE_FOR_FREE(ptr) \
|
||||
GET_STACK_TRACE_HERE(flags()->malloc_context_size)
|
||||
|
||||
#define PRINT_CURRENT_STACK() \
|
||||
{ \
|
||||
GET_STACK_TRACE_HERE(kStackTraceMax, false); \
|
||||
GET_STACK_TRACE_HERE(kStackTraceMax); \
|
||||
stack.PrintStack(); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif // ASAN_STACK_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_stats.cc -------------------------------------------*- C++ -*-===//
|
||||
//===-- asan_stats.cc -----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -21,36 +21,36 @@
|
||||
namespace __asan {
|
||||
|
||||
AsanStats::AsanStats() {
|
||||
CHECK(real_memset != NULL);
|
||||
real_memset(this, 0, sizeof(AsanStats));
|
||||
CHECK(REAL(memset) != 0);
|
||||
REAL(memset)(this, 0, sizeof(AsanStats));
|
||||
}
|
||||
|
||||
static void PrintMallocStatsArray(const char *prefix,
|
||||
size_t (&array)[kNumberOfSizeClasses]) {
|
||||
Printf("%s", prefix);
|
||||
for (size_t i = 0; i < kNumberOfSizeClasses; i++) {
|
||||
uptr (&array)[kNumberOfSizeClasses]) {
|
||||
AsanPrintf("%s", prefix);
|
||||
for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
|
||||
if (!array[i]) continue;
|
||||
Printf("%ld:%ld; ", i, array[i]);
|
||||
AsanPrintf("%zu:%zu; ", i, array[i]);
|
||||
}
|
||||
Printf("\n");
|
||||
AsanPrintf("\n");
|
||||
}
|
||||
|
||||
void AsanStats::Print() {
|
||||
Printf("Stats: %ldM malloced (%ldM for red zones) by %ld calls\n",
|
||||
malloced>>20, malloced_redzones>>20, mallocs);
|
||||
Printf("Stats: %ldM realloced by %ld calls\n", realloced>>20, reallocs);
|
||||
Printf("Stats: %ldM freed by %ld calls\n", freed>>20, frees);
|
||||
Printf("Stats: %ldM really freed by %ld calls\n",
|
||||
really_freed>>20, real_frees);
|
||||
Printf("Stats: %ldM (%ld full pages) mmaped in %ld calls\n",
|
||||
mmaped>>20, mmaped / kPageSize, mmaps);
|
||||
AsanPrintf("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",
|
||||
really_freed>>20, real_frees);
|
||||
AsanPrintf("Stats: %zuM (%zu full pages) mmaped in %zu calls\n",
|
||||
mmaped>>20, mmaped / kPageSize, mmaps);
|
||||
|
||||
PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size);
|
||||
PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size);
|
||||
PrintMallocStatsArray(" frees by size class: ", freed_by_size);
|
||||
PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size);
|
||||
Printf("Stats: malloc large: %ld small slow: %ld\n",
|
||||
malloc_large, malloc_small_slow);
|
||||
AsanPrintf("Stats: malloc large: %zu small slow: %zu\n",
|
||||
malloc_large, malloc_small_slow);
|
||||
}
|
||||
|
||||
static AsanLock print_lock(LINKER_INITIALIZED);
|
||||
@ -67,19 +67,19 @@ static void PrintAccumulatedStats() {
|
||||
// ---------------------- Interface ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
size_t __asan_get_current_allocated_bytes() {
|
||||
uptr __asan_get_current_allocated_bytes() {
|
||||
return asanThreadRegistry().GetCurrentAllocatedBytes();
|
||||
}
|
||||
|
||||
size_t __asan_get_heap_size() {
|
||||
uptr __asan_get_heap_size() {
|
||||
return asanThreadRegistry().GetHeapSize();
|
||||
}
|
||||
|
||||
size_t __asan_get_free_bytes() {
|
||||
uptr __asan_get_free_bytes() {
|
||||
return asanThreadRegistry().GetFreeBytes();
|
||||
}
|
||||
|
||||
size_t __asan_get_unmapped_bytes() {
|
||||
uptr __asan_get_unmapped_bytes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -23,27 +23,27 @@ namespace __asan {
|
||||
// Each AsanThread has its own AsanStats, which are sometimes flushed
|
||||
// to the accumulated AsanStats.
|
||||
struct AsanStats {
|
||||
// AsanStats must be a struct consisting of size_t fields only.
|
||||
// When merging two AsanStats structs, we treat them as arrays of size_t.
|
||||
size_t mallocs;
|
||||
size_t malloced;
|
||||
size_t malloced_redzones;
|
||||
size_t frees;
|
||||
size_t freed;
|
||||
size_t real_frees;
|
||||
size_t really_freed;
|
||||
size_t really_freed_redzones;
|
||||
size_t reallocs;
|
||||
size_t realloced;
|
||||
size_t mmaps;
|
||||
size_t mmaped;
|
||||
size_t mmaped_by_size[kNumberOfSizeClasses];
|
||||
size_t malloced_by_size[kNumberOfSizeClasses];
|
||||
size_t freed_by_size[kNumberOfSizeClasses];
|
||||
size_t really_freed_by_size[kNumberOfSizeClasses];
|
||||
// AsanStats must be a struct consisting of uptr fields only.
|
||||
// When merging two AsanStats structs, we treat them as arrays of uptr.
|
||||
uptr mallocs;
|
||||
uptr malloced;
|
||||
uptr malloced_redzones;
|
||||
uptr frees;
|
||||
uptr freed;
|
||||
uptr real_frees;
|
||||
uptr really_freed;
|
||||
uptr really_freed_redzones;
|
||||
uptr reallocs;
|
||||
uptr realloced;
|
||||
uptr mmaps;
|
||||
uptr mmaped;
|
||||
uptr mmaped_by_size[kNumberOfSizeClasses];
|
||||
uptr malloced_by_size[kNumberOfSizeClasses];
|
||||
uptr freed_by_size[kNumberOfSizeClasses];
|
||||
uptr really_freed_by_size[kNumberOfSizeClasses];
|
||||
|
||||
size_t malloc_large;
|
||||
size_t malloc_small_slow;
|
||||
uptr malloc_large;
|
||||
uptr malloc_small_slow;
|
||||
|
||||
// Ctor for global AsanStats (accumulated stats and main thread stats).
|
||||
explicit AsanStats(LinkerInitialized) { }
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_thread.cc ------------------------------------------*- C++ -*-===//
|
||||
//===-- asan_thread.cc ----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -13,19 +13,11 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
#include "asan_mapping.h"
|
||||
|
||||
#if ASAN_USE_SYSINFO == 1
|
||||
#include "sysinfo/sysinfo.h"
|
||||
#endif
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
@ -34,67 +26,105 @@ AsanThread::AsanThread(LinkerInitialized x)
|
||||
malloc_storage_(x),
|
||||
stats_(x) { }
|
||||
|
||||
AsanThread::AsanThread(int parent_tid, void *(*start_routine) (void *),
|
||||
void *arg, AsanStackTrace *stack)
|
||||
: start_routine_(start_routine),
|
||||
arg_(arg) {
|
||||
asanThreadRegistry().RegisterThread(this, parent_tid, stack);
|
||||
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);
|
||||
AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__);
|
||||
thread->start_routine_ = start_routine;
|
||||
thread->arg_ = arg;
|
||||
|
||||
const uptr kSummaryAllocSize = 1024;
|
||||
CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize);
|
||||
AsanThreadSummary *summary;
|
||||
{
|
||||
ScopedLock lock(&mu_for_thread_summary);
|
||||
summary = (AsanThreadSummary*)
|
||||
allocator_for_thread_summary.Allocate(kSummaryAllocSize);
|
||||
}
|
||||
summary->Init(parent_tid, stack);
|
||||
summary->set_thread(thread);
|
||||
thread->set_summary(summary);
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
AsanThread::~AsanThread() {
|
||||
void AsanThreadSummary::TSDDtor(void *tsd) {
|
||||
AsanThreadSummary *summary = (AsanThreadSummary*)tsd;
|
||||
if (flags()->verbosity >= 1) {
|
||||
Report("T%d TSDDtor\n", summary->tid());
|
||||
}
|
||||
if (summary->thread()) {
|
||||
summary->thread()->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void AsanThread::Destroy() {
|
||||
if (flags()->verbosity >= 1) {
|
||||
Report("T%d exited\n", tid());
|
||||
}
|
||||
|
||||
asanThreadRegistry().UnregisterThread(this);
|
||||
fake_stack().Cleanup();
|
||||
CHECK(summary()->thread() == 0);
|
||||
// We also clear the shadow on thread destruction because
|
||||
// some code may still be executing in later TSD destructors
|
||||
// and we don't want it to have any poisoned stack.
|
||||
ClearShadowForThreadStack();
|
||||
}
|
||||
|
||||
void AsanThread::ClearShadowForThreadStack() {
|
||||
uintptr_t shadow_bot = MemToShadow(stack_bottom_);
|
||||
uintptr_t shadow_top = MemToShadow(stack_top_);
|
||||
real_memset((void*)shadow_bot, 0, shadow_top - shadow_bot);
|
||||
fake_stack().Cleanup();
|
||||
uptr size = RoundUpTo(sizeof(AsanThread), kPageSize);
|
||||
UnmapOrDie(this, size);
|
||||
}
|
||||
|
||||
void AsanThread::Init() {
|
||||
SetThreadStackTopAndBottom();
|
||||
fake_stack_.Init(stack_size());
|
||||
if (FLAG_v >= 1) {
|
||||
int local = 0;
|
||||
Report("T%d: stack [%p,%p) size 0x%lx; local=%p, pthread_self=%p\n",
|
||||
tid(), stack_bottom_, stack_top_,
|
||||
stack_top_ - stack_bottom_, &local, pthread_self());
|
||||
}
|
||||
|
||||
CHECK(AddrIsInMem(stack_bottom_));
|
||||
CHECK(AddrIsInMem(stack_top_));
|
||||
|
||||
ClearShadowForThreadStack();
|
||||
if (flags()->verbosity >= 1) {
|
||||
int local = 0;
|
||||
Report("T%d: stack [%p,%p) size 0x%zx; local=%p\n",
|
||||
tid(), (void*)stack_bottom_, (void*)stack_top_,
|
||||
stack_top_ - stack_bottom_, &local);
|
||||
}
|
||||
fake_stack_.Init(stack_size());
|
||||
AsanPlatformThreadInit();
|
||||
}
|
||||
|
||||
void *AsanThread::ThreadStart() {
|
||||
thread_return_t AsanThread::ThreadStart() {
|
||||
Init();
|
||||
if (flags()->use_sigaltstack) SetAlternateSignalStack();
|
||||
|
||||
if (!start_routine_) {
|
||||
// start_routine_ == NULL if we're on the main thread or on one of the
|
||||
// start_routine_ == 0 if we're on the main thread or on one of the
|
||||
// OS X libdispatch worker threads. But nobody is supposed to call
|
||||
// ThreadStart() for the worker threads.
|
||||
CHECK(tid() == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *res = start_routine_(arg_);
|
||||
thread_return_t res = start_routine_(arg_);
|
||||
malloc_storage().CommitBack();
|
||||
if (flags()->use_sigaltstack) UnsetAlternateSignalStack();
|
||||
|
||||
if (FLAG_v >= 1) {
|
||||
Report("T%d exited\n", tid());
|
||||
}
|
||||
this->Destroy();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const char *AsanThread::GetFrameNameByAddr(uintptr_t addr, uintptr_t *offset) {
|
||||
uintptr_t bottom = 0;
|
||||
void AsanThread::SetThreadStackTopAndBottom() {
|
||||
GetThreadStackTopAndBottom(tid() == 0, &stack_top_, &stack_bottom_);
|
||||
int local;
|
||||
CHECK(AddrIsInStack((uptr)&local));
|
||||
}
|
||||
|
||||
void AsanThread::ClearShadowForThreadStack() {
|
||||
PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0);
|
||||
}
|
||||
|
||||
const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) {
|
||||
uptr bottom = 0;
|
||||
bool is_fake_stack = false;
|
||||
if (AddrIsInStack(addr)) {
|
||||
bottom = stack_bottom();
|
||||
@ -103,76 +133,30 @@ const char *AsanThread::GetFrameNameByAddr(uintptr_t addr, uintptr_t *offset) {
|
||||
CHECK(bottom);
|
||||
is_fake_stack = true;
|
||||
}
|
||||
uintptr_t aligned_addr = addr & ~(__WORDSIZE/8 - 1); // align addr.
|
||||
uintptr_t *ptr = (uintptr_t*)aligned_addr;
|
||||
while (ptr >= (uintptr_t*)bottom) {
|
||||
if (ptr[0] == kCurrentStackFrameMagic ||
|
||||
(is_fake_stack && ptr[0] == kRetiredStackFrameMagic)) {
|
||||
*offset = addr - (uintptr_t)ptr;
|
||||
return (const char*)ptr[1];
|
||||
}
|
||||
ptr--;
|
||||
uptr aligned_addr = addr & ~(__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--;
|
||||
}
|
||||
*offset = 0;
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
void AsanThread::SetThreadStackTopAndBottom() {
|
||||
#ifdef __APPLE__
|
||||
size_t stacksize = pthread_get_stacksize_np(pthread_self());
|
||||
void *stackaddr = pthread_get_stackaddr_np(pthread_self());
|
||||
stack_top_ = (uintptr_t)stackaddr;
|
||||
stack_bottom_ = stack_top_ - stacksize;
|
||||
int local;
|
||||
CHECK(AddrIsInStack((uintptr_t)&local));
|
||||
#else
|
||||
#if ASAN_USE_SYSINFO == 1
|
||||
if (tid() == 0) {
|
||||
// This is the main thread. Libpthread may not be initialized yet.
|
||||
struct rlimit rl;
|
||||
CHECK(getrlimit(RLIMIT_STACK, &rl) == 0);
|
||||
|
||||
// Find the mapping that contains a stack variable.
|
||||
ProcMapsIterator it(0);
|
||||
uint64_t start, end;
|
||||
uint64_t prev_end = 0;
|
||||
while (it.Next(&start, &end, NULL, NULL, NULL, NULL)) {
|
||||
if ((uintptr_t)&rl < end)
|
||||
break;
|
||||
prev_end = end;
|
||||
}
|
||||
CHECK((uintptr_t)&rl >= start && (uintptr_t)&rl < end);
|
||||
|
||||
// Get stacksize from rlimit, but clip it so that it does not overlap
|
||||
// with other mappings.
|
||||
size_t stacksize = rl.rlim_cur;
|
||||
if (stacksize > end - prev_end)
|
||||
stacksize = end - prev_end;
|
||||
if (stacksize > kMaxThreadStackSize)
|
||||
stacksize = kMaxThreadStackSize;
|
||||
stack_top_ = end;
|
||||
stack_bottom_ = end - stacksize;
|
||||
CHECK(AddrIsInStack((uintptr_t)&rl));
|
||||
return;
|
||||
while (shadow_ptr >= shadow_bottom &&
|
||||
*shadow_ptr == kAsanStackLeftRedzoneMagic) {
|
||||
shadow_ptr--;
|
||||
}
|
||||
#endif
|
||||
pthread_attr_t attr;
|
||||
CHECK(pthread_getattr_np(pthread_self(), &attr) == 0);
|
||||
size_t stacksize = 0;
|
||||
void *stackaddr = NULL;
|
||||
pthread_attr_getstack(&attr, &stackaddr, &stacksize);
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
stack_top_ = (uintptr_t)stackaddr + stacksize;
|
||||
stack_bottom_ = (uintptr_t)stackaddr;
|
||||
// When running with unlimited stack size, we still want to set some limit.
|
||||
// The unlimited stack size is caused by 'ulimit -s unlimited'.
|
||||
// Also, for some reason, GNU make spawns subrocesses with unlimited stack.
|
||||
if (stacksize > kMaxThreadStackSize) {
|
||||
stack_bottom_ = stack_top_ - kMaxThreadStackSize;
|
||||
if (shadow_ptr < shadow_bottom) {
|
||||
*offset = 0;
|
||||
return "UNKNOWN";
|
||||
}
|
||||
CHECK(AddrIsInStack((uintptr_t)&attr));
|
||||
#endif
|
||||
|
||||
uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1));
|
||||
CHECK((ptr[0] == kCurrentStackFrameMagic) ||
|
||||
(is_fake_stack && ptr[0] == kRetiredStackFrameMagic));
|
||||
*offset = addr - (uptr)ptr;
|
||||
return (const char*)ptr[1];
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
@ -18,10 +18,11 @@
|
||||
#include "asan_internal.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_stats.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
const size_t kMaxThreadStackSize = 16 * (1 << 20); // 16M
|
||||
const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits.
|
||||
|
||||
class AsanThread;
|
||||
|
||||
@ -30,12 +31,12 @@ class AsanThread;
|
||||
class AsanThreadSummary {
|
||||
public:
|
||||
explicit AsanThreadSummary(LinkerInitialized) { } // for T0.
|
||||
AsanThreadSummary(int tid, int parent_tid, AsanStackTrace *stack)
|
||||
: tid_(tid),
|
||||
parent_tid_(parent_tid),
|
||||
announced_(false) {
|
||||
void Init(u32 parent_tid, AsanStackTrace *stack) {
|
||||
parent_tid_ = parent_tid;
|
||||
announced_ = false;
|
||||
tid_ = kInvalidTid;
|
||||
if (stack) {
|
||||
stack_ = *stack;
|
||||
internal_memcpy(&stack_, stack, sizeof(*stack));
|
||||
}
|
||||
thread_ = 0;
|
||||
}
|
||||
@ -43,16 +44,19 @@ class AsanThreadSummary {
|
||||
if (tid_ == 0) return; // no need to announce the main thread.
|
||||
if (!announced_) {
|
||||
announced_ = true;
|
||||
Printf("Thread T%d created by T%d here:\n", tid_, parent_tid_);
|
||||
AsanPrintf("Thread T%d created by T%d here:\n", tid_, parent_tid_);
|
||||
stack_.PrintStack();
|
||||
}
|
||||
}
|
||||
int tid() { return tid_; }
|
||||
u32 tid() { return tid_; }
|
||||
void set_tid(u32 tid) { tid_ = tid; }
|
||||
AsanThread *thread() { return thread_; }
|
||||
void set_thread(AsanThread *thread) { thread_ = thread; }
|
||||
static void TSDDtor(void *tsd);
|
||||
|
||||
private:
|
||||
int tid_;
|
||||
int parent_tid_;
|
||||
u32 tid_;
|
||||
u32 parent_tid_;
|
||||
bool announced_;
|
||||
AsanStackTrace stack_;
|
||||
AsanThread *thread_;
|
||||
@ -62,23 +66,23 @@ class AsanThreadSummary {
|
||||
class AsanThread {
|
||||
public:
|
||||
explicit AsanThread(LinkerInitialized); // for T0.
|
||||
AsanThread(int parent_tid, void *(*start_routine) (void *),
|
||||
void *arg, AsanStackTrace *stack);
|
||||
~AsanThread();
|
||||
static AsanThread *Create(u32 parent_tid, thread_callback_t start_routine,
|
||||
void *arg, AsanStackTrace *stack);
|
||||
void Destroy();
|
||||
|
||||
void Init(); // Should be called from the thread itself.
|
||||
void *ThreadStart();
|
||||
thread_return_t ThreadStart();
|
||||
|
||||
uintptr_t stack_top() { return stack_top_; }
|
||||
uintptr_t stack_bottom() { return stack_bottom_; }
|
||||
size_t stack_size() { return stack_top_ - stack_bottom_; }
|
||||
int tid() { return summary_->tid(); }
|
||||
uptr stack_top() { return stack_top_; }
|
||||
uptr stack_bottom() { return stack_bottom_; }
|
||||
uptr stack_size() { return stack_top_ - stack_bottom_; }
|
||||
u32 tid() { return summary_->tid(); }
|
||||
AsanThreadSummary *summary() { return summary_; }
|
||||
void set_summary(AsanThreadSummary *summary) { summary_ = summary; }
|
||||
|
||||
const char *GetFrameNameByAddr(uintptr_t addr, uintptr_t *offset);
|
||||
const char *GetFrameNameByAddr(uptr addr, uptr *offset);
|
||||
|
||||
bool AddrIsInStack(uintptr_t addr) {
|
||||
bool AddrIsInStack(uptr addr) {
|
||||
return addr >= stack_bottom_ && addr < stack_top_;
|
||||
}
|
||||
|
||||
@ -86,17 +90,15 @@ class AsanThread {
|
||||
AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; }
|
||||
AsanStats &stats() { return stats_; }
|
||||
|
||||
static const int kInvalidTid = -1;
|
||||
|
||||
private:
|
||||
|
||||
void SetThreadStackTopAndBottom();
|
||||
void ClearShadowForThreadStack();
|
||||
AsanThreadSummary *summary_;
|
||||
void *(*start_routine_) (void *param);
|
||||
thread_callback_t start_routine_;
|
||||
void *arg_;
|
||||
uintptr_t stack_top_;
|
||||
uintptr_t stack_bottom_;
|
||||
uptr stack_top_;
|
||||
uptr stack_bottom_;
|
||||
|
||||
FakeStack fake_stack_;
|
||||
AsanThreadLocalMallocStorage malloc_storage_;
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_thread_registry.cc ---------------------------------*- C++ -*-===//
|
||||
//===-- asan_thread_registry.cc -------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -16,8 +16,7 @@
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread.h"
|
||||
#include "asan_thread_registry.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
@ -27,48 +26,6 @@ AsanThreadRegistry &asanThreadRegistry() {
|
||||
return asan_thread_registry;
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
#ifndef PTHREAD_DESTRUCTOR_ITERATIONS
|
||||
#define PTHREAD_DESTRUCTOR_ITERATIONS 4
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Dark magic below. In order to be able to notice that we're not handling
|
||||
// some thread creation routines (e.g. on Mac OS) we want to distinguish the
|
||||
// thread that used to have a corresponding AsanThread object from the thread
|
||||
// that never had one. That's why upon AsanThread destruction we set the
|
||||
// pthread_key value to some odd number (that's not a valid pointer), instead
|
||||
// of NULL.
|
||||
// Because the TSD destructor for a non-NULL key value is called iteratively,
|
||||
// we increase the value by two, keeping it an invalid pointer.
|
||||
// Because the TSD implementations are allowed to call such a destructor
|
||||
// infinitely (see
|
||||
// http://pubs.opengroup.org/onlinepubs/009604499/functions/pthread_key_create.html
|
||||
// ), we exit the program after a certain number of iterations.
|
||||
static void DestroyAsanTsd(void *tsd) {
|
||||
intptr_t iter = (intptr_t)tsd;
|
||||
if (iter % 2 == 0) {
|
||||
// The pointer is valid.
|
||||
AsanThread *t = (AsanThread*)tsd;
|
||||
if (t != asanThreadRegistry().GetMain()) {
|
||||
delete t;
|
||||
}
|
||||
iter = 1;
|
||||
} else {
|
||||
// The pointer is invalid -- we've already destroyed the TSD before.
|
||||
// If |iter| is too big, we're in the infinite loop. This should be
|
||||
// impossible on the systems AddressSanitizer was tested on.
|
||||
CHECK(iter < 4 * PTHREAD_DESTRUCTOR_ITERATIONS);
|
||||
iter += 2;
|
||||
}
|
||||
CHECK(0 == pthread_setspecific(asanThreadRegistry().GetTlsKey(),
|
||||
(void*)iter));
|
||||
if (FLAG_v >= 2) {
|
||||
Report("DestroyAsanTsd: writing %p to the TSD slot of thread %p\n",
|
||||
(void*)iter, pthread_self());
|
||||
}
|
||||
}
|
||||
|
||||
AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x)
|
||||
: main_thread_(x),
|
||||
main_thread_summary_(x),
|
||||
@ -76,26 +33,25 @@ AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x)
|
||||
mu_(x) { }
|
||||
|
||||
void AsanThreadRegistry::Init() {
|
||||
CHECK(0 == pthread_key_create(&tls_key_, DestroyAsanTsd));
|
||||
tls_key_created_ = true;
|
||||
SetCurrent(&main_thread_);
|
||||
AsanTSDInit(AsanThreadSummary::TSDDtor);
|
||||
main_thread_.set_summary(&main_thread_summary_);
|
||||
main_thread_summary_.set_thread(&main_thread_);
|
||||
thread_summaries_[0] = &main_thread_summary_;
|
||||
n_threads_ = 1;
|
||||
RegisterThread(&main_thread_);
|
||||
SetCurrent(&main_thread_);
|
||||
// At this point only one thread exists.
|
||||
inited_ = true;
|
||||
}
|
||||
|
||||
void AsanThreadRegistry::RegisterThread(AsanThread *thread, int parent_tid,
|
||||
AsanStackTrace *stack) {
|
||||
void AsanThreadRegistry::RegisterThread(AsanThread *thread) {
|
||||
ScopedLock lock(&mu_);
|
||||
CHECK(n_threads_ > 0);
|
||||
int tid = n_threads_;
|
||||
u32 tid = n_threads_;
|
||||
n_threads_++;
|
||||
CHECK(n_threads_ < kMaxNumberOfThreads);
|
||||
AsanThreadSummary *summary = new AsanThreadSummary(tid, parent_tid, stack);
|
||||
summary->set_thread(thread);
|
||||
|
||||
AsanThreadSummary *summary = thread->summary();
|
||||
CHECK(summary != 0);
|
||||
summary->set_tid(tid);
|
||||
thread_summaries_[tid] = summary;
|
||||
thread->set_summary(summary);
|
||||
}
|
||||
|
||||
void AsanThreadRegistry::UnregisterThread(AsanThread *thread) {
|
||||
@ -103,7 +59,7 @@ void AsanThreadRegistry::UnregisterThread(AsanThread *thread) {
|
||||
FlushToAccumulatedStatsUnlocked(&thread->stats());
|
||||
AsanThreadSummary *summary = thread->summary();
|
||||
CHECK(summary);
|
||||
summary->set_thread(NULL);
|
||||
summary->set_thread(0);
|
||||
}
|
||||
|
||||
AsanThread *AsanThreadRegistry::GetMain() {
|
||||
@ -111,45 +67,35 @@ AsanThread *AsanThreadRegistry::GetMain() {
|
||||
}
|
||||
|
||||
AsanThread *AsanThreadRegistry::GetCurrent() {
|
||||
CHECK(tls_key_created_);
|
||||
AsanThread *thread = (AsanThread*)pthread_getspecific(tls_key_);
|
||||
if ((!thread || (intptr_t)thread % 2) && FLAG_v >= 2) {
|
||||
Report("GetCurrent: %p for thread %p\n", thread, pthread_self());
|
||||
}
|
||||
if ((intptr_t)thread % 2) {
|
||||
// Invalid pointer -- we've deleted the AsanThread already. Return NULL as
|
||||
// if the TSD was empty.
|
||||
// TODO(glider): if the code in the client TSD destructor calls
|
||||
// pthread_create(), we'll set the parent tid of the spawned thread to NULL,
|
||||
// although the creation stack will belong to the current thread. This may
|
||||
// confuse the user, but is quite unlikely.
|
||||
return NULL;
|
||||
} else {
|
||||
// NULL or valid pointer to AsanThread.
|
||||
return thread;
|
||||
AsanThreadSummary *summary = (AsanThreadSummary *)AsanTSDGet();
|
||||
if (!summary) {
|
||||
#ifdef ANDROID
|
||||
// On Android, libc constructor is called _after_ asan_init, and cleans up
|
||||
// TSD. Try to figure out if this is still the main thread by the stack
|
||||
// address. We are not entirely sure that we have correct main thread
|
||||
// limits, so only do this magic on Android, and only if the found thread is
|
||||
// the main thread.
|
||||
AsanThread* thread = FindThreadByStackAddress((uptr)&summary);
|
||||
if (thread && thread->tid() == 0) {
|
||||
SetCurrent(thread);
|
||||
return thread;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
return summary->thread();
|
||||
}
|
||||
|
||||
void AsanThreadRegistry::SetCurrent(AsanThread *t) {
|
||||
if (FLAG_v >=2) {
|
||||
Report("SetCurrent: %p for thread %p\n", t, pthread_self());
|
||||
CHECK(t->summary());
|
||||
if (flags()->verbosity >= 2) {
|
||||
Report("SetCurrent: %p for thread %p\n",
|
||||
t->summary(), (void*)GetThreadSelf());
|
||||
}
|
||||
// Make sure we do not reset the current AsanThread.
|
||||
intptr_t old_key = (intptr_t)pthread_getspecific(tls_key_);
|
||||
CHECK(!old_key || old_key % 2);
|
||||
CHECK(0 == pthread_setspecific(tls_key_, t));
|
||||
CHECK(pthread_getspecific(tls_key_) == t);
|
||||
}
|
||||
|
||||
pthread_key_t AsanThreadRegistry::GetTlsKey() {
|
||||
return tls_key_;
|
||||
}
|
||||
|
||||
// Returns true iff DestroyAsanTsd() was already called for this thread.
|
||||
bool AsanThreadRegistry::IsCurrentThreadDying() {
|
||||
CHECK(tls_key_created_);
|
||||
intptr_t thread = (intptr_t)pthread_getspecific(tls_key_);
|
||||
return (bool)(thread % 2);
|
||||
CHECK(AsanTSDGet() == 0);
|
||||
AsanTSDSet(t->summary());
|
||||
CHECK(AsanTSDGet() == t->summary());
|
||||
}
|
||||
|
||||
AsanStats &AsanThreadRegistry::GetCurrentThreadStats() {
|
||||
@ -163,19 +109,19 @@ AsanStats AsanThreadRegistry::GetAccumulatedStats() {
|
||||
return accumulated_stats_;
|
||||
}
|
||||
|
||||
size_t AsanThreadRegistry::GetCurrentAllocatedBytes() {
|
||||
uptr AsanThreadRegistry::GetCurrentAllocatedBytes() {
|
||||
ScopedLock lock(&mu_);
|
||||
UpdateAccumulatedStatsUnlocked();
|
||||
return accumulated_stats_.malloced - accumulated_stats_.freed;
|
||||
}
|
||||
|
||||
size_t AsanThreadRegistry::GetHeapSize() {
|
||||
uptr AsanThreadRegistry::GetHeapSize() {
|
||||
ScopedLock lock(&mu_);
|
||||
UpdateAccumulatedStatsUnlocked();
|
||||
return accumulated_stats_.mmaped;
|
||||
}
|
||||
|
||||
size_t AsanThreadRegistry::GetFreeBytes() {
|
||||
uptr AsanThreadRegistry::GetFreeBytes() {
|
||||
ScopedLock lock(&mu_);
|
||||
UpdateAccumulatedStatsUnlocked();
|
||||
return accumulated_stats_.mmaped
|
||||
@ -185,18 +131,17 @@ size_t AsanThreadRegistry::GetFreeBytes() {
|
||||
+ accumulated_stats_.really_freed_redzones;
|
||||
}
|
||||
|
||||
AsanThreadSummary *AsanThreadRegistry::FindByTid(int tid) {
|
||||
CHECK(tid >= 0);
|
||||
AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) {
|
||||
CHECK(tid < n_threads_);
|
||||
CHECK(thread_summaries_[tid]);
|
||||
return thread_summaries_[tid];
|
||||
}
|
||||
|
||||
AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uintptr_t addr) {
|
||||
AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) {
|
||||
ScopedLock lock(&mu_);
|
||||
for (int tid = 0; tid < n_threads_; tid++) {
|
||||
for (u32 tid = 0; tid < n_threads_; tid++) {
|
||||
AsanThread *t = thread_summaries_[tid]->thread();
|
||||
if (!t) continue;
|
||||
if (!t || !(t->fake_stack().StackSize())) continue;
|
||||
if (t->fake_stack().AddrIsInFakeStack(addr) || t->AddrIsInStack(addr)) {
|
||||
return t;
|
||||
}
|
||||
@ -205,20 +150,20 @@ AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uintptr_t addr) {
|
||||
}
|
||||
|
||||
void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() {
|
||||
for (int tid = 0; tid < n_threads_; tid++) {
|
||||
for (u32 tid = 0; tid < n_threads_; tid++) {
|
||||
AsanThread *t = thread_summaries_[tid]->thread();
|
||||
if (t != NULL) {
|
||||
if (t != 0) {
|
||||
FlushToAccumulatedStatsUnlocked(&t->stats());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AsanThreadRegistry::FlushToAccumulatedStatsUnlocked(AsanStats *stats) {
|
||||
// AsanStats consists of variables of type size_t only.
|
||||
size_t *dst = (size_t*)&accumulated_stats_;
|
||||
size_t *src = (size_t*)stats;
|
||||
size_t num_fields = sizeof(AsanStats) / sizeof(size_t);
|
||||
for (size_t i = 0; i < num_fields; i++) {
|
||||
// AsanStats consists of variables of type uptr only.
|
||||
uptr *dst = (uptr*)&accumulated_stats_;
|
||||
uptr *src = (uptr*)stats;
|
||||
uptr num_fields = sizeof(AsanStats) / sizeof(uptr);
|
||||
for (uptr i = 0; i < num_fields; i++) {
|
||||
dst[i] += src[i];
|
||||
src[i] = 0;
|
||||
}
|
||||
|
@ -30,34 +30,32 @@ class AsanThreadRegistry {
|
||||
public:
|
||||
explicit AsanThreadRegistry(LinkerInitialized);
|
||||
void Init();
|
||||
void RegisterThread(AsanThread *thread, int parent_tid,
|
||||
AsanStackTrace *stack);
|
||||
void RegisterThread(AsanThread *thread);
|
||||
void UnregisterThread(AsanThread *thread);
|
||||
|
||||
AsanThread *GetMain();
|
||||
// Get the current thread. May return NULL.
|
||||
// Get the current thread. May return 0.
|
||||
AsanThread *GetCurrent();
|
||||
void SetCurrent(AsanThread *t);
|
||||
pthread_key_t GetTlsKey();
|
||||
bool IsCurrentThreadDying();
|
||||
|
||||
int GetCurrentTidOrMinusOne() {
|
||||
u32 GetCurrentTidOrInvalid() {
|
||||
if (!inited_) return 0;
|
||||
AsanThread *t = GetCurrent();
|
||||
return t ? t->tid() : -1;
|
||||
return t ? t->tid() : kInvalidTid;
|
||||
}
|
||||
|
||||
// Returns stats for GetCurrent(), or stats for
|
||||
// T0 if GetCurrent() returns NULL.
|
||||
// T0 if GetCurrent() returns 0.
|
||||
AsanStats &GetCurrentThreadStats();
|
||||
// Flushes all thread-local stats to accumulated stats, and returns
|
||||
// a copy of accumulated stats.
|
||||
AsanStats GetAccumulatedStats();
|
||||
size_t GetCurrentAllocatedBytes();
|
||||
size_t GetHeapSize();
|
||||
size_t GetFreeBytes();
|
||||
uptr GetCurrentAllocatedBytes();
|
||||
uptr GetHeapSize();
|
||||
uptr GetFreeBytes();
|
||||
|
||||
AsanThreadSummary *FindByTid(int tid);
|
||||
AsanThread *FindThreadByStackAddress(uintptr_t addr);
|
||||
AsanThreadSummary *FindByTid(u32 tid);
|
||||
AsanThread *FindThreadByStackAddress(uptr addr);
|
||||
|
||||
private:
|
||||
void UpdateAccumulatedStatsUnlocked();
|
||||
@ -65,19 +63,14 @@ class AsanThreadRegistry {
|
||||
// and fills "stats" with zeroes.
|
||||
void FlushToAccumulatedStatsUnlocked(AsanStats *stats);
|
||||
|
||||
static const int kMaxNumberOfThreads = (1 << 22); // 4M
|
||||
static const u32 kMaxNumberOfThreads = (1 << 22); // 4M
|
||||
AsanThreadSummary *thread_summaries_[kMaxNumberOfThreads];
|
||||
AsanThread main_thread_;
|
||||
AsanThreadSummary main_thread_summary_;
|
||||
AsanStats accumulated_stats_;
|
||||
int n_threads_;
|
||||
u32 n_threads_;
|
||||
AsanLock mu_;
|
||||
// For each thread tls_key_ stores the pointer to the corresponding
|
||||
// AsanThread.
|
||||
pthread_key_t tls_key_;
|
||||
// This flag is updated only once at program startup, and then read
|
||||
// by concurrent threads.
|
||||
bool tls_key_created_;
|
||||
bool inited_;
|
||||
};
|
||||
|
||||
// Returns a single instance of registry.
|
||||
|
181
lib/asan/asan_win.cc
Normal file
181
lib/asan/asan_win.cc
Normal file
@ -0,0 +1,181 @@
|
||||
//===-- asan_win.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.
|
||||
//
|
||||
// Windows-specific details.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
#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"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
// ---------------------- Stacktraces, symbols, etc. ---------------- {{{1
|
||||
static AsanLock 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 *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;
|
||||
// Skip the RTL frames by searching for the PC in the stacktrace.
|
||||
// FIXME: this doesn't work well for the malloc/free stacks yet.
|
||||
for (uptr i = 0; i < cs_ret; i++) {
|
||||
if (pc != (uptr)tmp[i])
|
||||
continue;
|
||||
offset = i;
|
||||
break;
|
||||
}
|
||||
|
||||
size = cs_ret - offset;
|
||||
for (uptr i = 0; i < size; i++)
|
||||
trace[i] = (uptr)tmp[i + offset];
|
||||
}
|
||||
|
||||
bool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size) {
|
||||
ScopedLock lock(&dbghelp_lock);
|
||||
if (!dbghelp_initialized) {
|
||||
SymSetOptions(SYMOPT_DEFERRED_LOADS |
|
||||
SYMOPT_UNDNAME |
|
||||
SYMOPT_LOAD_LINES);
|
||||
CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE));
|
||||
// FIXME: We don't call SymCleanup() on exit yet - should we?
|
||||
dbghelp_initialized = true;
|
||||
}
|
||||
|
||||
// See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
|
||||
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
|
||||
PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
DWORD64 offset = 0;
|
||||
BOOL got_objname = SymFromAddr(GetCurrentProcess(),
|
||||
(DWORD64)addr, &offset, symbol);
|
||||
if (!got_objname)
|
||||
return false;
|
||||
|
||||
DWORD unused;
|
||||
IMAGEHLP_LINE64 info;
|
||||
info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(),
|
||||
(DWORD64)addr, &unused, &info);
|
||||
int written = 0;
|
||||
out_buffer[0] = '\0';
|
||||
// FIXME: it might be useful to print out 'obj' or 'obj+offset' info too.
|
||||
if (got_fileline) {
|
||||
written += internal_snprintf(out_buffer + written, buffer_size - written,
|
||||
" %s %s:%d", symbol->Name,
|
||||
info.FileName, info.LineNumber);
|
||||
} else {
|
||||
written += internal_snprintf(out_buffer + written, buffer_size - written,
|
||||
" %s+0x%p", symbol->Name, offset);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------- AsanLock ---------------- {{{1
|
||||
enum LockState {
|
||||
LOCK_UNINITIALIZED = 0,
|
||||
LOCK_READY = -1,
|
||||
};
|
||||
|
||||
AsanLock::AsanLock(LinkerInitialized li) {
|
||||
// FIXME: see comments in AsanLock::Lock() for the details.
|
||||
CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);
|
||||
|
||||
CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
|
||||
InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
|
||||
owner_ = LOCK_READY;
|
||||
}
|
||||
|
||||
void AsanLock::Lock() {
|
||||
if (owner_ == LOCK_UNINITIALIZED) {
|
||||
// FIXME: hm, global AsanLock objects are not initialized?!?
|
||||
// This might be a side effect of the clang+cl+link Frankenbuild...
|
||||
new(this) AsanLock((LinkerInitialized)(LINKER_INITIALIZED + 1));
|
||||
|
||||
// FIXME: If it turns out the linker doesn't invoke our
|
||||
// constructors, we should probably manually Lock/Unlock all the global
|
||||
// locks while we're starting in one thread to avoid double-init races.
|
||||
}
|
||||
EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
|
||||
CHECK(owner_ == LOCK_READY);
|
||||
owner_ = GetThreadSelf();
|
||||
}
|
||||
|
||||
void AsanLock::Unlock() {
|
||||
CHECK(owner_ == GetThreadSelf());
|
||||
owner_ = LOCK_READY;
|
||||
LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
|
||||
}
|
||||
|
||||
// ---------------------- 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
|
34
lib/asan/output_tests/clone_test.cc
Normal file
34
lib/asan/output_tests/clone_test.cc
Normal file
@ -0,0 +1,34 @@
|
||||
#ifdef __linux__
|
||||
#include <stdio.h>
|
||||
#include <sched.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int Child(void *arg) {
|
||||
char x[32] = {0}; // Stack gets poisoned.
|
||||
printf("Child: %p\n", x);
|
||||
_exit(1); // NoReturn, stack will remain unpoisoned unless we do something.
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const int kStackSize = 1 << 20;
|
||||
char child_stack[kStackSize + 1];
|
||||
char *sp = child_stack + kStackSize; // Stack grows down.
|
||||
printf("Parent: %p\n", sp);
|
||||
pid_t clone_pid = clone(Child, sp, CLONE_FILES | CLONE_VM, NULL, 0, 0, 0);
|
||||
waitpid(clone_pid, NULL, 0);
|
||||
for (int i = 0; i < kStackSize; i++)
|
||||
child_stack[i] = i;
|
||||
int ret = child_stack[argc - 1];
|
||||
printf("PASSED\n");
|
||||
return ret;
|
||||
}
|
||||
#else // not __linux__
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
printf("PASSED\n");
|
||||
// Check-Common: PASSED
|
||||
}
|
||||
#endif
|
15
lib/asan/output_tests/deep_tail_call.cc
Normal file
15
lib/asan/output_tests/deep_tail_call.cc
Normal file
@ -0,0 +1,15 @@
|
||||
// Check-Common: AddressSanitizer global-buffer-overflow
|
||||
int global[10];
|
||||
// Check-Common: {{#0.*call4}}
|
||||
void __attribute__((noinline)) call4(int i) { global[i+10]++; }
|
||||
// Check-Common: {{#1.*call3}}
|
||||
void __attribute__((noinline)) call3(int i) { call4(i); }
|
||||
// Check-Common: {{#2.*call2}}
|
||||
void __attribute__((noinline)) call2(int i) { call3(i); }
|
||||
// Check-Common: {{#3.*call1}}
|
||||
void __attribute__((noinline)) call1(int i) { call2(i); }
|
||||
// Check-Common: {{#4.*main}}
|
||||
int main(int argc, char **argv) {
|
||||
call1(argc);
|
||||
return global[0];
|
||||
}
|
12
lib/asan/output_tests/default_options.cc
Normal file
12
lib/asan/output_tests/default_options.cc
Normal file
@ -0,0 +1,12 @@
|
||||
const char *kAsanDefaultOptions="verbosity=1 foo=bar";
|
||||
|
||||
extern "C"
|
||||
__attribute__((no_address_safety_analysis))
|
||||
const char *__asan_default_options() {
|
||||
return kAsanDefaultOptions;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Check-Common: foo=bar
|
||||
return 0;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_rtl.cc ------------*- C++ -*-===//
|
||||
//===----------- dlclose-test-so.cc -----------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_rtl.cc ------------*- C++ -*-===//
|
||||
//===----------- dlclose-test.cc --------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -69,5 +69,6 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
addr[1] = 2; // BOOM (if the bug is not fixed).
|
||||
printf("PASS\n");
|
||||
// Check-Common: PASS
|
||||
return 0;
|
||||
}
|
@ -7,6 +7,10 @@ int main(int argc, char **argv) {
|
||||
memset(YYY, 0, 10);
|
||||
memset(ZZZ, 0, 10);
|
||||
int res = YYY[argc * 10]; // BOOOM
|
||||
// Check-Common: {{READ of size 1 at 0x.* thread T0}}
|
||||
// Check-Common: {{ #0 0x.* in main .*global-overflow.cc:9}}
|
||||
// Check-Common: {{0x.* is located 0 bytes to the right of global variable}}
|
||||
// Check-Common: {{.*YYY.* of size 10}}
|
||||
res += XXX[argc] + ZZZ[argc];
|
||||
return res;
|
||||
}
|
22
lib/asan/output_tests/heap-overflow.cc
Normal file
22
lib/asan/output_tests/heap-overflow.cc
Normal file
@ -0,0 +1,22 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
int main(int argc, char **argv) {
|
||||
char *x = (char*)malloc(10 * sizeof(char));
|
||||
memset(x, 0, 10);
|
||||
int res = x[argc * 10]; // BOOOM
|
||||
free(x);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Check-Common: {{READ of size 1 at 0x.* thread T0}}
|
||||
// Check-Common: {{ #0 0x.* in main .*heap-overflow.cc:6}}
|
||||
// Check-Common: {{0x.* is located 0 bytes to the right of 10-byte region}}
|
||||
// Check-Common: {{allocated by thread T0 here:}}
|
||||
|
||||
// Check-Linux: {{ #0 0x.* in .*malloc}}
|
||||
// Check-Linux: {{ #1 0x.* in main .*heap-overflow.cc:4}}
|
||||
|
||||
// Check-Darwin: {{ #0 0x.* in .*mz_malloc.*}}
|
||||
// Check-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}}
|
||||
// Check-Darwin: {{ #2 0x.* in malloc.*}}
|
||||
// Check-Darwin: {{ #3 0x.* in main heap-overflow.cc:4}}
|
17
lib/asan/output_tests/interception_failure_test-linux.cc
Normal file
17
lib/asan/output_tests/interception_failure_test-linux.cc
Normal file
@ -0,0 +1,17 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" long strtol(const char *nptr, char **endptr, int base) {
|
||||
fprintf(stderr, "my_strtol_interceptor\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
char *x = (char*)malloc(10 * sizeof(char));
|
||||
free(x);
|
||||
return (int)strtol(x, 0, 10);
|
||||
}
|
||||
|
||||
// Check-Common: my_strtol_interceptor
|
||||
// CHECK-NOT: heap-use-after-free
|
||||
|
19
lib/asan/output_tests/interception_malloc_test-linux.cc
Normal file
19
lib/asan/output_tests/interception_malloc_test-linux.cc
Normal file
@ -0,0 +1,19 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" void *__interceptor_malloc(size_t size);
|
||||
extern "C" void *malloc(size_t size) {
|
||||
write(2, "malloc call\n", sizeof("malloc call\n") - 1);
|
||||
return __interceptor_malloc(size);
|
||||
}
|
||||
|
||||
int main() {
|
||||
char *x = (char*)malloc(10 * sizeof(char));
|
||||
free(x);
|
||||
return (int)strtol(x, 0, 10);
|
||||
}
|
||||
|
||||
// Check-Common: malloc call
|
||||
// Check-Common: heap-use-after-free
|
||||
|
18
lib/asan/output_tests/interception_test-linux.cc
Normal file
18
lib/asan/output_tests/interception_test-linux.cc
Normal file
@ -0,0 +1,18 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base);
|
||||
extern "C" long strtol(const char *nptr, char **endptr, int base) {
|
||||
fprintf(stderr, "my_strtol_interceptor\n");
|
||||
return __interceptor_strtol(nptr, endptr, base);
|
||||
}
|
||||
|
||||
int main() {
|
||||
char *x = (char*)malloc(10 * sizeof(char));
|
||||
free(x);
|
||||
return (int)strtol(x, 0, 10);
|
||||
}
|
||||
|
||||
// Check-Common: my_strtol_interceptor
|
||||
// Check-Common: heap-use-after-free
|
||||
|
48
lib/asan/output_tests/large_func_test.cc
Normal file
48
lib/asan/output_tests/large_func_test.cc
Normal file
@ -0,0 +1,48 @@
|
||||
#include <stdlib.h>
|
||||
__attribute__((noinline))
|
||||
static void LargeFunction(int *x, int zero) {
|
||||
x[0]++;
|
||||
x[1]++;
|
||||
x[2]++;
|
||||
x[3]++;
|
||||
x[4]++;
|
||||
x[5]++;
|
||||
x[6]++;
|
||||
x[7]++;
|
||||
x[8]++;
|
||||
x[9]++;
|
||||
|
||||
x[zero + 111]++; // we should report this exact line
|
||||
|
||||
x[10]++;
|
||||
x[11]++;
|
||||
x[12]++;
|
||||
x[13]++;
|
||||
x[14]++;
|
||||
x[15]++;
|
||||
x[16]++;
|
||||
x[17]++;
|
||||
x[18]++;
|
||||
x[19]++;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int *x = new int[100];
|
||||
LargeFunction(x, argc - 1);
|
||||
delete x;
|
||||
}
|
||||
|
||||
// Check-Common: {{.*ERROR: AddressSanitizer heap-buffer-overflow on address}}
|
||||
// Check-Common: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}}
|
||||
// Check-Common: {{READ of size 4 at 0x.* thread T0}}
|
||||
|
||||
// atos incorrectly extracts the symbol name for the static functions on
|
||||
// Darwin.
|
||||
// Check-Linux: {{ #0 0x.* in LargeFunction.*large_func_test.cc:15}}
|
||||
// Check-Darwin: {{ #0 0x.* in .*LargeFunction.*large_func_test.cc:15}}
|
||||
|
||||
// Check-Common: {{ #1 0x.* in main .*large_func_test.cc:31}}
|
||||
// Check-Common: {{0x.* is located 44 bytes to the right of 400-byte region}}
|
||||
// Check-Common: {{allocated by thread T0 here:}}
|
||||
// Check-Common: {{ #0 0x.* in operator new.*}}
|
||||
// Check-Common: {{ #1 0x.* in main .*large_func_test.cc:30}}
|
10
lib/asan/output_tests/memcmp_test.cc
Normal file
10
lib/asan/output_tests/memcmp_test.cc
Normal file
@ -0,0 +1,10 @@
|
||||
#include <string.h>
|
||||
int main(int argc, char **argv) {
|
||||
char a1[] = {argc, 2, 3, 4};
|
||||
char a2[] = {1, 2*argc, 3, 4};
|
||||
// Check-Common: AddressSanitizer stack-buffer-overflow
|
||||
// Check-Common: {{#0.*memcmp}}
|
||||
// Check-Common: {{#1.*main}}
|
||||
int res = memcmp(a1, a2, 4 + argc); // BOOM
|
||||
return res;
|
||||
}
|
17
lib/asan/output_tests/null_deref.cc
Normal file
17
lib/asan/output_tests/null_deref.cc
Normal file
@ -0,0 +1,17 @@
|
||||
__attribute__((noinline))
|
||||
static void NullDeref(int *ptr) {
|
||||
ptr[10]++;
|
||||
}
|
||||
int main() {
|
||||
NullDeref((int*)0);
|
||||
}
|
||||
|
||||
// Check-Common: {{.*ERROR: AddressSanitizer crashed on unknown address}}
|
||||
// Check-Common: {{0x0*00028 .*pc 0x.*}}
|
||||
// Check-Common: {{AddressSanitizer can not provide additional info. ABORTING}}
|
||||
|
||||
// atos on Mac cannot extract the symbol name correctly.
|
||||
// Check-Linux: {{ #0 0x.* in NullDeref.*null_deref.cc:3}}
|
||||
// Check-Darwin: {{ #0 0x.* in .*NullDeref.*null_deref.cc:3}}
|
||||
|
||||
// Check-Common: {{ #1 0x.* in main.*null_deref.cc:6}}
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_rtl.cc ------------*- C++ -*-===//
|
||||
//===----------- shared-lib-test-so.cc --------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_rtl.cc ------------*- C++ -*-===//
|
||||
//===----------- shared-lib-test.cc -----------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -32,6 +32,11 @@ int main(int argc, char *argv[]) {
|
||||
if (!inc) return 1;
|
||||
printf("ok\n");
|
||||
inc(1);
|
||||
inc(-1);
|
||||
inc(-1); // BOOM
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check-Common: {{.*ERROR: AddressSanitizer global-buffer-overflow}}
|
||||
// Check-Common: {{READ of size 4 at 0x.* thread T0}}
|
||||
// Check-Common: {{ #0 0x.*}}
|
||||
// Check-Common: {{ #1 0x.* in main .*shared-lib-test.cc:35}}
|
11
lib/asan/output_tests/stack-overflow.cc
Normal file
11
lib/asan/output_tests/stack-overflow.cc
Normal file
@ -0,0 +1,11 @@
|
||||
#include <string.h>
|
||||
int main(int argc, char **argv) {
|
||||
char x[10];
|
||||
memset(x, 0, 10);
|
||||
int res = x[argc * 10]; // BOOOM
|
||||
return res;
|
||||
}
|
||||
|
||||
// Check-Common: {{READ of size 1 at 0x.* thread T0}}
|
||||
// Check-Common: {{ #0 0x.* in main .*stack-overflow.cc:5}}
|
||||
// Check-Common: {{Address 0x.* is .* frame <main>}}
|
@ -16,6 +16,9 @@ __attribute__((noinline))
|
||||
void Func2(char *x) {
|
||||
fprintf(stderr, "2: %p\n", x);
|
||||
*x = 1;
|
||||
// Check-Common: {{WRITE of size 1 .* thread T0}}
|
||||
// Check-Common: {{ #0.*Func2.*stack-use-after-return.cc:18}}
|
||||
// Check-Common: {{is located in frame <.*Func1.*> of T0's stack}}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
24
lib/asan/output_tests/strncpy-overflow.cc
Normal file
24
lib/asan/output_tests/strncpy-overflow.cc
Normal file
@ -0,0 +1,24 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
int main(int argc, char **argv) {
|
||||
char *hello = (char*)malloc(6);
|
||||
strcpy(hello, "hello");
|
||||
char *short_buffer = (char*)malloc(9);
|
||||
strncpy(short_buffer, hello, 10); // BOOM
|
||||
return short_buffer[8];
|
||||
}
|
||||
|
||||
// Check-Common: {{WRITE of size 1 at 0x.* thread T0}}
|
||||
// Check-Linux: {{ #0 0x.* in .*strncpy}}
|
||||
// Check-Darwin: {{ #0 0x.* in wrap_strncpy}}
|
||||
// Check-Common: {{ #1 0x.* in main .*strncpy-overflow.cc:7}}
|
||||
// Check-Common: {{0x.* is located 0 bytes to the right of 9-byte region}}
|
||||
// Check-Common: {{allocated by thread T0 here:}}
|
||||
|
||||
// Check-Linux: {{ #0 0x.* in .*malloc}}
|
||||
// Check-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:6}}
|
||||
|
||||
// Check-Darwin: {{ #0 0x.* in .*mz_malloc.*}}
|
||||
// Check-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}}
|
||||
// Check-Darwin: {{ #2 0x.* in malloc.*}}
|
||||
// Check-Darwin: {{ #3 0x.* in main .*strncpy-overflow.cc:6}}
|
79
lib/asan/output_tests/test_output.sh
Executable file
79
lib/asan/output_tests/test_output.sh
Executable file
@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e # fail on any error
|
||||
|
||||
OS=`uname`
|
||||
CXX=$1
|
||||
CC=$2
|
||||
FILE_CHECK=$3
|
||||
CXXFLAGS="-mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -g"
|
||||
SYMBOLIZER=../scripts/asan_symbolize.py
|
||||
TMP_ASAN_REPORT=asan_report.tmp
|
||||
|
||||
run_program() {
|
||||
./$1 2>&1 | $SYMBOLIZER 2> /dev/null | c++filt > $TMP_ASAN_REPORT
|
||||
}
|
||||
|
||||
# check_program exe_file source_file check_prefixf
|
||||
check_program() {
|
||||
run_program $1
|
||||
$FILE_CHECK $2 --check-prefix=$3 < $TMP_ASAN_REPORT
|
||||
rm -f $TMP_ASAN_REPORT
|
||||
}
|
||||
|
||||
C_TEST=use-after-free
|
||||
echo "Sanity checking a test in pure C"
|
||||
$CC -g -faddress-sanitizer -O2 $C_TEST.c
|
||||
check_program a.out $C_TEST.c CHECK
|
||||
rm ./a.out
|
||||
|
||||
echo "Sanity checking a test in pure C with -pie"
|
||||
$CC -g -faddress-sanitizer -O2 $C_TEST.c -pie
|
||||
check_program a.out $C_TEST.c CHECK
|
||||
rm ./a.out
|
||||
|
||||
echo "Testing sleep_before_dying"
|
||||
$CC -g -faddress-sanitizer -O2 $C_TEST.c
|
||||
export ASAN_OPTIONS="sleep_before_dying=1"
|
||||
check_program a.out $C_TEST.c CHECKSLEEP
|
||||
export ASAN_OPTIONS=""
|
||||
rm ./a.out
|
||||
|
||||
# FIXME: some tests do not need to be ran for all the combinations of arch
|
||||
# and optimization mode.
|
||||
for t in *.cc; do
|
||||
for b in 32 64; do
|
||||
for O in 0 1 2 3; do
|
||||
c=`basename $t .cc`
|
||||
if [[ "$c" == *"-so" ]]; then
|
||||
continue
|
||||
fi
|
||||
if [[ "$c" == *"-linux" ]]; then
|
||||
if [[ "$OS" != "Linux" ]]; then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
c_so=$c-so
|
||||
exe=$c.$b.O$O
|
||||
so=$c.$b.O$O-so.so
|
||||
echo testing $exe
|
||||
build_command="$CXX $CXXFLAGS -m$b -faddress-sanitizer -O$O $c.cc -o $exe"
|
||||
[ "$DEBUG" == "1" ] && echo $build_command
|
||||
$build_command
|
||||
[ -e "$c_so.cc" ] && $CXX $CXXFLAGS -m$b -faddress-sanitizer -O$O $c_so.cc -fPIC -shared -o $so
|
||||
run_program $exe
|
||||
# Check common expected lines for OS.
|
||||
$FILE_CHECK $c.cc --check-prefix="Check-Common" < $TMP_ASAN_REPORT
|
||||
# Check OS-specific lines.
|
||||
if [ `grep -c "Check-$OS" $c.cc` -gt 0 ]
|
||||
then
|
||||
$FILE_CHECK $c.cc --check-prefix="Check-$OS" < $TMP_ASAN_REPORT
|
||||
fi
|
||||
rm ./$exe
|
||||
rm ./$TMP_ASAN_REPORT
|
||||
[ -e "$so" ] && rm ./$so
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
exit 0
|
@ -4,3 +4,6 @@ int main() {
|
||||
free(x);
|
||||
return x[5];
|
||||
}
|
||||
|
||||
// CHECK: heap-use-after-free
|
||||
// CHECKSLEEP: Sleeping for 1 second
|
31
lib/asan/output_tests/use-after-free.cc
Normal file
31
lib/asan/output_tests/use-after-free.cc
Normal file
@ -0,0 +1,31 @@
|
||||
#include <stdlib.h>
|
||||
int main() {
|
||||
char *x = (char*)malloc(10 * sizeof(char));
|
||||
free(x);
|
||||
return x[5];
|
||||
}
|
||||
|
||||
// Check-Common: {{.*ERROR: AddressSanitizer heap-use-after-free on address}}
|
||||
// Check-Common: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}}
|
||||
// Check-Common: {{READ of size 1 at 0x.* thread T0}}
|
||||
// Check-Common: {{ #0 0x.* in main .*use-after-free.cc:5}}
|
||||
// Check-Common: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}}
|
||||
// Check-Common: {{freed by thread T0 here:}}
|
||||
|
||||
// Check-Linux: {{ #0 0x.* in .*free}}
|
||||
// Check-Linux: {{ #1 0x.* in main .*use-after-free.cc:4}}
|
||||
|
||||
// Check-Darwin: {{ #0 0x.* in .*mz_free.*}}
|
||||
// We override free() on Darwin, thus no malloc_zone_free
|
||||
// Check-Darwin: {{ #1 0x.* in wrap_free}}
|
||||
// Check-Darwin: {{ #2 0x.* in main .*use-after-free.cc:4}}
|
||||
|
||||
// Check-Common: {{previously allocated by thread T0 here:}}
|
||||
|
||||
// Check-Linux: {{ #0 0x.* in .*malloc}}
|
||||
// Check-Linux: {{ #1 0x.* in main .*use-after-free.cc:3}}
|
||||
|
||||
// Check-Darwin: {{ #0 0x.* in .*mz_malloc.*}}
|
||||
// Check-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}}
|
||||
// Check-Darwin: {{ #2 0x.* in malloc.*}}
|
||||
// Check-Darwin: {{ #3 0x.* in main .*use-after-free.cc:3}}
|
@ -14,21 +14,16 @@
|
||||
import subprocess
|
||||
|
||||
pipes = {}
|
||||
filetypes = {}
|
||||
DEBUG=False
|
||||
|
||||
def fix_filename(file_name):
|
||||
for path_to_cut in sys.argv[1:]:
|
||||
file_name = re.sub(".*" + path_to_cut, "", file_name)
|
||||
file_name = re.sub(".*asan_[a-z_]*.cc:[0-9]*", "_asan_rtl_", file_name)
|
||||
file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
|
||||
return file_name
|
||||
|
||||
def patch_address(frameno, addr_s):
|
||||
''' Subtracts 1 or 2 from the top frame's address.
|
||||
Top frame is normally the return address from asan_report*
|
||||
call, which is not expected to return at all. Because of that, this
|
||||
address often belongs to the next source code line, or even to a different
|
||||
function. '''
|
||||
if frameno == '0':
|
||||
addr = int(addr_s, 16)
|
||||
if os.uname()[4].startswith('arm'):
|
||||
# Cancel the Thumb bit
|
||||
addr = addr & (~1)
|
||||
addr -= 1
|
||||
return hex(addr)
|
||||
return addr_s
|
||||
|
||||
# TODO(glider): need some refactoring here
|
||||
def symbolize_addr2line(line):
|
||||
@ -38,7 +33,6 @@ def symbolize_addr2line(line):
|
||||
frameno = match.group(2)
|
||||
binary = match.group(3)
|
||||
addr = match.group(4)
|
||||
addr = patch_address(frameno, addr)
|
||||
if not pipes.has_key(binary):
|
||||
pipes[binary] = subprocess.Popen(["addr2line", "-f", "-e", binary],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
@ -50,15 +44,25 @@ def symbolize_addr2line(line):
|
||||
except:
|
||||
function_name = ""
|
||||
file_name = ""
|
||||
for path_to_cut in sys.argv[1:]:
|
||||
file_name = re.sub(".*" + path_to_cut, "", file_name)
|
||||
file_name = re.sub(".*asan_[a-z_]*.cc:[0-9]*", "_asan_rtl_", file_name)
|
||||
file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
|
||||
file_name = fix_filename(file_name)
|
||||
|
||||
print match.group(1), "in", function_name, file_name
|
||||
else:
|
||||
print line.rstrip()
|
||||
|
||||
|
||||
def get_macho_filetype(binary):
|
||||
if not filetypes.has_key(binary):
|
||||
otool_pipe = subprocess.Popen(["otool", "-Vh", binary],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
otool_line = "".join(otool_pipe.stdout.readlines())
|
||||
for t in ["DYLIB", "EXECUTE"]:
|
||||
if t in otool_line:
|
||||
filetypes[binary] = t
|
||||
otool_pipe.stdin.close()
|
||||
return filetypes[binary]
|
||||
|
||||
|
||||
def symbolize_atos(line):
|
||||
#0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
|
||||
match = re.match('^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line)
|
||||
@ -66,27 +70,49 @@ def symbolize_atos(line):
|
||||
#print line
|
||||
prefix = match.group(1)
|
||||
frameno = match.group(2)
|
||||
addr = match.group(3)
|
||||
orig_addr = match.group(3)
|
||||
binary = match.group(4)
|
||||
offset = match.group(5)
|
||||
addr = patch_address(frameno, addr)
|
||||
load_addr = int(addr, 16) - int(offset, 16)
|
||||
addr = orig_addr
|
||||
load_addr = hex(int(orig_addr, 16) - int(offset, 16))
|
||||
filetype = get_macho_filetype(binary)
|
||||
|
||||
if not pipes.has_key(binary):
|
||||
#print "atos -o %s -l %s" % (binary, hex(load_addr))
|
||||
pipes[binary] = subprocess.Popen(["atos", "-o", binary],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE,)
|
||||
# Guess which arch we're running. 10 = len("0x") + 8 hex digits.
|
||||
if len(addr) > 10:
|
||||
arch = "x86_64"
|
||||
else:
|
||||
arch = "i386"
|
||||
|
||||
if filetype == "DYLIB":
|
||||
load_addr = "0x0"
|
||||
if DEBUG:
|
||||
print "atos -o %s -arch %s -l %s" % (binary, arch, load_addr)
|
||||
cmd = ["atos", "-o", binary, "-arch", arch, "-l", load_addr]
|
||||
pipes[binary] = subprocess.Popen(cmd,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
p = pipes[binary]
|
||||
# TODO(glider): how to tell if the address is absolute?
|
||||
if ".app/" in binary and not ".framework" in binary:
|
||||
print >>p.stdin, "%s" % addr
|
||||
else:
|
||||
if filetype == "DYLIB":
|
||||
print >>p.stdin, "%s" % offset
|
||||
else:
|
||||
print >>p.stdin, "%s" % addr
|
||||
# TODO(glider): it's more efficient to make a batch atos run for each binary.
|
||||
p.stdin.close()
|
||||
atos_line = p.stdout.readline().rstrip()
|
||||
# A well-formed atos response looks like this:
|
||||
# foo(type1, type2) (in object.name) (filename.cc:80)
|
||||
match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line)
|
||||
#print "atos_line: ", atos_line
|
||||
if match:
|
||||
function_name = match.group(1)
|
||||
function_name = re.sub("\(.*?\)", "", function_name)
|
||||
file_name = fix_filename(match.group(3))
|
||||
print "%s%s in %s %s" % (prefix, addr, function_name, file_name)
|
||||
else:
|
||||
print "%s%s in %s" % (prefix, addr, atos_line)
|
||||
del pipes[binary]
|
||||
|
||||
print "%s%s in %s" % (prefix, addr, atos_line)
|
||||
else:
|
||||
print line.rstrip()
|
||||
|
||||
|
@ -1,29 +0,0 @@
|
||||
Copyright (c) 2005, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
@ -1,321 +0,0 @@
|
||||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef _BASICTYPES_H_
|
||||
#define _BASICTYPES_H_
|
||||
|
||||
#include <inttypes.h> // uint16_t might be here; PRId64 too.
|
||||
#include <stdint.h> // to get uint16_t (ISO naming madness)
|
||||
#include <sys/types.h> // our last best hope for uint16_t
|
||||
|
||||
// Standard typedefs
|
||||
// All Google code is compiled with -funsigned-char to make "char"
|
||||
// unsigned. Google code therefore doesn't need a "uchar" type.
|
||||
// TODO(csilvers): how do we make sure unsigned-char works on non-gcc systems?
|
||||
typedef signed char schar;
|
||||
typedef int8_t int8;
|
||||
typedef int16_t int16;
|
||||
typedef int32_t int32;
|
||||
typedef int64_t int64;
|
||||
|
||||
// NOTE: unsigned types are DANGEROUS in loops and other arithmetical
|
||||
// places. Use the signed types unless your variable represents a bit
|
||||
// pattern (eg a hash value) or you really need the extra bit. Do NOT
|
||||
// use 'unsigned' to express "this value should always be positive";
|
||||
// use assertions for this.
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
const uint16 kuint16max = ( (uint16) 0xFFFF);
|
||||
const uint32 kuint32max = ( (uint32) 0xFFFFFFFF);
|
||||
const uint64 kuint64max = ( (((uint64) kuint32max) << 32) | kuint32max );
|
||||
|
||||
const int8 kint8max = ( ( int8) 0x7F);
|
||||
const int16 kint16max = ( ( int16) 0x7FFF);
|
||||
const int32 kint32max = ( ( int32) 0x7FFFFFFF);
|
||||
const int64 kint64max = ( ((( int64) kint32max) << 32) | kuint32max );
|
||||
|
||||
const int8 kint8min = ( ( int8) 0x80);
|
||||
const int16 kint16min = ( ( int16) 0x8000);
|
||||
const int32 kint32min = ( ( int32) 0x80000000);
|
||||
const int64 kint64min = ( ((( int64) kint32min) << 32) | 0 );
|
||||
|
||||
// Define the "portable" printf and scanf macros, if they're not
|
||||
// already there (via the inttypes.h we #included above, hopefully).
|
||||
// Mostly it's old systems that don't support inttypes.h, so we assume
|
||||
// they're 32 bit.
|
||||
#ifndef PRIx64
|
||||
#define PRIx64 "llx"
|
||||
#endif
|
||||
#ifndef SCNx64
|
||||
#define SCNx64 "llx"
|
||||
#endif
|
||||
#ifndef PRId64
|
||||
#define PRId64 "lld"
|
||||
#endif
|
||||
#ifndef SCNd64
|
||||
#define SCNd64 "lld"
|
||||
#endif
|
||||
#ifndef PRIu64
|
||||
#define PRIu64 "llu"
|
||||
#endif
|
||||
#ifndef PRIxPTR
|
||||
#define PRIxPTR "lx"
|
||||
#endif
|
||||
|
||||
// Also allow for printing of a pthread_t.
|
||||
#define GPRIuPTHREAD "lu"
|
||||
#define GPRIxPTHREAD "lx"
|
||||
#if defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#define PRINTABLE_PTHREAD(pthreadt) reinterpret_cast<uintptr_t>(pthreadt)
|
||||
#else
|
||||
#define PRINTABLE_PTHREAD(pthreadt) pthreadt
|
||||
#endif
|
||||
|
||||
// A macro to disallow the evil copy constructor and operator= functions
|
||||
// This should be used in the private: declarations for a class
|
||||
#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
|
||||
// An alternate name that leaves out the moral judgment... :-)
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) DISALLOW_EVIL_CONSTRUCTORS(TypeName)
|
||||
|
||||
// The COMPILE_ASSERT macro can be used to verify that a compile time
|
||||
// expression is true. For example, you could use it to verify the
|
||||
// size of a static array:
|
||||
//
|
||||
// COMPILE_ASSERT(sizeof(num_content_type_names) == sizeof(int),
|
||||
// content_type_names_incorrect_size);
|
||||
//
|
||||
// or to make sure a struct is smaller than a certain size:
|
||||
//
|
||||
// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
|
||||
//
|
||||
// The second argument to the macro is the name of the variable. If
|
||||
// the expression is false, most compilers will issue a warning/error
|
||||
// containing the name of the variable.
|
||||
//
|
||||
// Implementation details of COMPILE_ASSERT:
|
||||
//
|
||||
// - COMPILE_ASSERT works by defining an array type that has -1
|
||||
// elements (and thus is invalid) when the expression is false.
|
||||
//
|
||||
// - The simpler definition
|
||||
//
|
||||
// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1]
|
||||
//
|
||||
// does not work, as gcc supports variable-length arrays whose sizes
|
||||
// are determined at run-time (this is gcc's extension and not part
|
||||
// of the C++ standard). As a result, gcc fails to reject the
|
||||
// following code with the simple definition:
|
||||
//
|
||||
// int foo;
|
||||
// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is
|
||||
// // not a compile-time constant.
|
||||
//
|
||||
// - By using the type CompileAssert<(bool(expr))>, we ensures that
|
||||
// expr is a compile-time constant. (Template arguments must be
|
||||
// determined at compile-time.)
|
||||
//
|
||||
// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
|
||||
// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
|
||||
//
|
||||
// CompileAssert<bool(expr)>
|
||||
//
|
||||
// instead, these compilers will refuse to compile
|
||||
//
|
||||
// COMPILE_ASSERT(5 > 0, some_message);
|
||||
//
|
||||
// (They seem to think the ">" in "5 > 0" marks the end of the
|
||||
// template argument list.)
|
||||
//
|
||||
// - The array size is (bool(expr) ? 1 : -1), instead of simply
|
||||
//
|
||||
// ((expr) ? 1 : -1).
|
||||
//
|
||||
// This is to avoid running into a bug in MS VC 7.1, which
|
||||
// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
|
||||
|
||||
template <bool>
|
||||
struct CompileAssert {
|
||||
};
|
||||
|
||||
#define COMPILE_ASSERT(expr, msg) \
|
||||
typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
|
||||
|
||||
#define arraysize(a) (sizeof(a) / sizeof(*(a)))
|
||||
|
||||
#define OFFSETOF_MEMBER(strct, field) \
|
||||
(reinterpret_cast<char*>(&reinterpret_cast<strct*>(16)->field) - \
|
||||
reinterpret_cast<char*>(16))
|
||||
|
||||
#ifdef HAVE___ATTRIBUTE__
|
||||
# define ATTRIBUTE_WEAK __attribute__((weak))
|
||||
# define ATTRIBUTE_NOINLINE __attribute__((noinline))
|
||||
#else
|
||||
# define ATTRIBUTE_WEAK
|
||||
# define ATTRIBUTE_NOINLINE
|
||||
#endif
|
||||
|
||||
// Section attributes are supported for both ELF and Mach-O, but in
|
||||
// very different ways. Here's the API we provide:
|
||||
// 1) ATTRIBUTE_SECTION: put this with the declaration of all functions
|
||||
// you want to be in the same linker section
|
||||
// 2) DEFINE_ATTRIBUTE_SECTION_VARS: must be called once per unique
|
||||
// name. You want to make sure this is executed before any
|
||||
// DECLARE_ATTRIBUTE_SECTION_VARS; the easiest way is to put them
|
||||
// in the same .cc file. Put this call at the global level.
|
||||
// 3) INIT_ATTRIBUTE_SECTION_VARS: you can scatter calls to this in
|
||||
// multiple places to help ensure execution before any
|
||||
// DECLARE_ATTRIBUTE_SECTION_VARS. You must have at least one
|
||||
// DEFINE, but you can have many INITs. Put each in its own scope.
|
||||
// 4) DECLARE_ATTRIBUTE_SECTION_VARS: must be called before using
|
||||
// ATTRIBUTE_SECTION_START or ATTRIBUTE_SECTION_STOP on a name.
|
||||
// Put this call at the global level.
|
||||
// 5) ATTRIBUTE_SECTION_START/ATTRIBUTE_SECTION_STOP: call this to say
|
||||
// where in memory a given section is. All functions declared with
|
||||
// ATTRIBUTE_SECTION are guaranteed to be between START and STOP.
|
||||
|
||||
#if defined(HAVE___ATTRIBUTE__) && defined(__ELF__)
|
||||
# define ATTRIBUTE_SECTION(name) __attribute__ ((section (#name)))
|
||||
|
||||
// Weak section declaration to be used as a global declaration
|
||||
// for ATTRIBUTE_SECTION_START|STOP(name) to compile and link
|
||||
// even without functions with ATTRIBUTE_SECTION(name).
|
||||
# define DECLARE_ATTRIBUTE_SECTION_VARS(name) \
|
||||
extern char __start_##name[] ATTRIBUTE_WEAK; \
|
||||
extern char __stop_##name[] ATTRIBUTE_WEAK
|
||||
# define INIT_ATTRIBUTE_SECTION_VARS(name) // no-op for ELF
|
||||
# define DEFINE_ATTRIBUTE_SECTION_VARS(name) // no-op for ELF
|
||||
|
||||
// Return void* pointers to start/end of a section of code with functions
|
||||
// having ATTRIBUTE_SECTION(name), or 0 if no such function exists.
|
||||
// One must DECLARE_ATTRIBUTE_SECTION(name) for this to compile and link.
|
||||
# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(__start_##name))
|
||||
# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(__stop_##name))
|
||||
# define HAVE_ATTRIBUTE_SECTION_START 1
|
||||
|
||||
#elif defined(HAVE___ATTRIBUTE__) && defined(__MACH__)
|
||||
# define ATTRIBUTE_SECTION(name) __attribute__ ((section ("__TEXT, " #name)))
|
||||
|
||||
#include <mach-o/getsect.h>
|
||||
#include <mach-o/dyld.h>
|
||||
class AssignAttributeStartEnd {
|
||||
public:
|
||||
AssignAttributeStartEnd(const char* name, char** pstart, char** pend) {
|
||||
// Find out what dynamic library name is defined in
|
||||
if (_dyld_present()) {
|
||||
for (int i = _dyld_image_count() - 1; i >= 0; --i) {
|
||||
const mach_header* hdr = _dyld_get_image_header(i);
|
||||
#ifdef MH_MAGIC_64
|
||||
if (hdr->magic == MH_MAGIC_64) {
|
||||
uint64_t len;
|
||||
*pstart = getsectdatafromheader_64((mach_header_64*)hdr,
|
||||
"__TEXT", name, &len);
|
||||
if (*pstart) { // NULL if not defined in this dynamic library
|
||||
*pstart += _dyld_get_image_vmaddr_slide(i); // correct for reloc
|
||||
*pend = *pstart + len;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (hdr->magic == MH_MAGIC) {
|
||||
uint32_t len;
|
||||
*pstart = getsectdatafromheader(hdr, "__TEXT", name, &len);
|
||||
if (*pstart) { // NULL if not defined in this dynamic library
|
||||
*pstart += _dyld_get_image_vmaddr_slide(i); // correct for reloc
|
||||
*pend = *pstart + len;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we get here, not defined in a dll at all. See if defined statically.
|
||||
unsigned long len; // don't ask me why this type isn't uint32_t too...
|
||||
*pstart = getsectdata("__TEXT", name, &len);
|
||||
*pend = *pstart + len;
|
||||
}
|
||||
};
|
||||
|
||||
#define DECLARE_ATTRIBUTE_SECTION_VARS(name) \
|
||||
extern char* __start_##name; \
|
||||
extern char* __stop_##name
|
||||
|
||||
#define INIT_ATTRIBUTE_SECTION_VARS(name) \
|
||||
DECLARE_ATTRIBUTE_SECTION_VARS(name); \
|
||||
static const AssignAttributeStartEnd __assign_##name( \
|
||||
#name, &__start_##name, &__stop_##name)
|
||||
|
||||
#define DEFINE_ATTRIBUTE_SECTION_VARS(name) \
|
||||
char* __start_##name, *__stop_##name; \
|
||||
INIT_ATTRIBUTE_SECTION_VARS(name)
|
||||
|
||||
# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(__start_##name))
|
||||
# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(__stop_##name))
|
||||
# define HAVE_ATTRIBUTE_SECTION_START 1
|
||||
|
||||
#else // not HAVE___ATTRIBUTE__ && __ELF__, nor HAVE___ATTRIBUTE__ && __MACH__
|
||||
# define ATTRIBUTE_SECTION(name)
|
||||
# define DECLARE_ATTRIBUTE_SECTION_VARS(name)
|
||||
# define INIT_ATTRIBUTE_SECTION_VARS(name)
|
||||
# define DEFINE_ATTRIBUTE_SECTION_VARS(name)
|
||||
# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(0))
|
||||
# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(0))
|
||||
|
||||
#endif // HAVE___ATTRIBUTE__ and __ELF__ or __MACH__
|
||||
|
||||
#if defined(HAVE___ATTRIBUTE__) && (defined(__i386__) || defined(__x86_64__))
|
||||
# define CACHELINE_SIZE 64
|
||||
# define CACHELINE_ALIGNED __attribute__((aligned(CACHELINE_SIZE)))
|
||||
#else
|
||||
# define CACHELINE_ALIGNED
|
||||
#endif // defined(HAVE___ATTRIBUTE__) && (__i386__ || __x86_64__)
|
||||
|
||||
|
||||
// The following enum should be used only as a constructor argument to indicate
|
||||
// that the variable has static storage class, and that the constructor should
|
||||
// do nothing to its state. It indicates to the reader that it is legal to
|
||||
// declare a static nistance of the class, provided the constructor is given
|
||||
// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a
|
||||
// static variable that has a constructor or a destructor because invocation
|
||||
// order is undefined. However, IF the type can be initialized by filling with
|
||||
// zeroes (which the loader does for static variables), AND the destructor also
|
||||
// does nothing to the storage, then a constructor declared as
|
||||
// explicit MyClass(base::LinkerInitialized x) {}
|
||||
// and invoked as
|
||||
// static MyClass my_variable_name(base::LINKER_INITIALIZED);
|
||||
namespace base {
|
||||
enum LinkerInitialized { LINKER_INITIALIZED };
|
||||
}
|
||||
|
||||
#endif // _BASICTYPES_H_
|
@ -1,617 +0,0 @@
|
||||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h> // for getenv()
|
||||
#include <stdio.h> // for snprintf(), sscanf()
|
||||
#include <string.h> // for memmove(), memchr(), etc.
|
||||
#include <fcntl.h> // for open()
|
||||
#include <errno.h> // for errno
|
||||
#include <unistd.h> // for read()
|
||||
#if defined __MACH__ // Mac OS X, almost certainly
|
||||
#include <mach-o/dyld.h> // for iterating over dll's in ProcMapsIter
|
||||
#include <mach-o/loader.h> // for iterating over dll's in ProcMapsIter
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h> // how we figure out numcpu's on OS X
|
||||
#elif defined __FreeBSD__
|
||||
#include <sys/sysctl.h>
|
||||
#elif defined __sun__ // Solaris
|
||||
#include <procfs.h> // for, e.g., prmap_t
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
#include <process.h> // for getpid() (actually, _getpid())
|
||||
#include <shlwapi.h> // for SHGetValueA()
|
||||
#include <tlhelp32.h> // for Module32First()
|
||||
#endif
|
||||
#include "sysinfo.h"
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
#ifdef MODULEENTRY32
|
||||
// In a change from the usual W-A pattern, there is no A variant of
|
||||
// MODULEENTRY32. Tlhelp32.h #defines the W variant, but not the A.
|
||||
// In unicode mode, tlhelp32.h #defines MODULEENTRY32 to be
|
||||
// MODULEENTRY32W. These #undefs are the only way I see to get back
|
||||
// access to the original, ascii struct (and related functions).
|
||||
#undef MODULEENTRY32
|
||||
#undef Module32First
|
||||
#undef Module32Next
|
||||
#undef PMODULEENTRY32
|
||||
#undef LPMODULEENTRY32
|
||||
#endif /* MODULEENTRY32 */
|
||||
// MinGW doesn't seem to define this, perhaps some windowsen don't either.
|
||||
#ifndef TH32CS_SNAPMODULE32
|
||||
#define TH32CS_SNAPMODULE32 0
|
||||
#endif /* TH32CS_SNAPMODULE32 */
|
||||
#endif /* PLATFORM_WINDOWS */
|
||||
|
||||
// Re-run fn until it doesn't cause EINTR.
|
||||
#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
|
||||
|
||||
// open/read/close can set errno, which may be illegal at this
|
||||
// time, so prefer making the syscalls directly if we can.
|
||||
#ifdef HAVE_SYS_SYSCALL_H
|
||||
# include <sys/syscall.h>
|
||||
# define safeopen(filename, mode) syscall(SYS_open, filename, mode)
|
||||
# define saferead(fd, buffer, size) syscall(SYS_read, fd, buffer, size)
|
||||
# define safeclose(fd) syscall(SYS_close, fd)
|
||||
#else
|
||||
# define safeopen(filename, mode) open(filename, mode)
|
||||
# define saferead(fd, buffer, size) read(fd, buffer, size)
|
||||
# define safeclose(fd) close(fd)
|
||||
#endif
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// HasPosixThreads()
|
||||
// Return true if we're running POSIX (e.g., NPTL on Linux)
|
||||
// threads, as opposed to a non-POSIX thread libary. The thing
|
||||
// that we care about is whether a thread's pid is the same as
|
||||
// the thread that spawned it. If so, this function returns
|
||||
// true.
|
||||
// ----------------------------------------------------------------------
|
||||
bool HasPosixThreads() {
|
||||
#if defined(__linux__) and !defined(ANDROID)
|
||||
#ifndef _CS_GNU_LIBPTHREAD_VERSION
|
||||
#define _CS_GNU_LIBPTHREAD_VERSION 3
|
||||
#endif
|
||||
char buf[32];
|
||||
// We assume that, if confstr() doesn't know about this name, then
|
||||
// the same glibc is providing LinuxThreads.
|
||||
if (confstr(_CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf)) == 0)
|
||||
return false;
|
||||
return strncmp(buf, "NPTL", 4) == 0;
|
||||
#elif defined(PLATFORM_WINDOWS) || defined(__CYGWIN__) || defined(__CYGWIN32__)
|
||||
return false;
|
||||
#else // other OS
|
||||
return true; // Assume that everything else has Posix
|
||||
#endif // else OS_LINUX
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#define CHECK_LT(x, y) do { assert((x) < (y)); } while (0)
|
||||
|
||||
#if defined __linux__ || defined __FreeBSD__ || defined __sun__ || defined __CYGWIN__ || defined __CYGWIN32__
|
||||
static void ConstructFilename(const char* spec, pid_t pid,
|
||||
char* buf, int buf_size) {
|
||||
CHECK_LT(snprintf(buf, buf_size,
|
||||
spec,
|
||||
static_cast<int>(pid ? pid : getpid())), buf_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
// A templatized helper function instantiated for Mach (OS X) only.
|
||||
// It can handle finding info for both 32 bits and 64 bits.
|
||||
// Returns true if it successfully handled the hdr, false else.
|
||||
#ifdef __MACH__ // Mac OS X, almost certainly
|
||||
template<uint32_t kMagic, uint32_t kLCSegment,
|
||||
typename MachHeader, typename SegmentCommand>
|
||||
static bool NextExtMachHelper(const mach_header* hdr,
|
||||
int current_image, int current_load_cmd,
|
||||
uint64 *start, uint64 *end, char **flags,
|
||||
uint64 *offset, int64 *inode, char **filename,
|
||||
uint64 *file_mapping, uint64 *file_pages,
|
||||
uint64 *anon_mapping, uint64 *anon_pages,
|
||||
dev_t *dev) {
|
||||
static char kDefaultPerms[5] = "r-xp";
|
||||
if (hdr->magic != kMagic)
|
||||
return false;
|
||||
const char* lc = (const char *)hdr + sizeof(MachHeader);
|
||||
// TODO(csilvers): make this not-quadradic (increment and hold state)
|
||||
for (int j = 0; j < current_load_cmd; j++) // advance to *our* load_cmd
|
||||
lc += ((const load_command *)lc)->cmdsize;
|
||||
if (((const load_command *)lc)->cmd == kLCSegment) {
|
||||
const intptr_t dlloff = _dyld_get_image_vmaddr_slide(current_image);
|
||||
const SegmentCommand* sc = (const SegmentCommand *)lc;
|
||||
if (start) *start = sc->vmaddr + dlloff;
|
||||
if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
|
||||
if (flags) *flags = kDefaultPerms; // can we do better?
|
||||
if (offset) *offset = sc->fileoff;
|
||||
if (inode) *inode = 0;
|
||||
if (filename)
|
||||
*filename = const_cast<char*>(_dyld_get_image_name(current_image));
|
||||
if (file_mapping) *file_mapping = 0;
|
||||
if (file_pages) *file_pages = 0; // could we use sc->filesize?
|
||||
if (anon_mapping) *anon_mapping = 0;
|
||||
if (anon_pages) *anon_pages = 0;
|
||||
if (dev) *dev = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
ProcMapsIterator::ProcMapsIterator(pid_t pid) {
|
||||
Init(pid, NULL, false);
|
||||
}
|
||||
|
||||
ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer) {
|
||||
Init(pid, buffer, false);
|
||||
}
|
||||
|
||||
ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer,
|
||||
bool use_maps_backing) {
|
||||
Init(pid, buffer, use_maps_backing);
|
||||
}
|
||||
|
||||
void ProcMapsIterator::Init(pid_t pid, Buffer *buffer,
|
||||
bool use_maps_backing) {
|
||||
pid_ = pid;
|
||||
using_maps_backing_ = use_maps_backing;
|
||||
dynamic_buffer_ = NULL;
|
||||
if (!buffer) {
|
||||
// If the user didn't pass in any buffer storage, allocate it
|
||||
// now. This is the normal case; the signal handler passes in a
|
||||
// static buffer.
|
||||
buffer = dynamic_buffer_ = new Buffer;
|
||||
} else {
|
||||
dynamic_buffer_ = NULL;
|
||||
}
|
||||
|
||||
ibuf_ = buffer->buf_;
|
||||
|
||||
stext_ = etext_ = nextline_ = ibuf_;
|
||||
ebuf_ = ibuf_ + Buffer::kBufSize - 1;
|
||||
nextline_ = ibuf_;
|
||||
|
||||
#if defined(__linux__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
|
||||
if (use_maps_backing) { // don't bother with clever "self" stuff in this case
|
||||
ConstructFilename("/proc/%d/maps_backing", pid, ibuf_, Buffer::kBufSize);
|
||||
} else if (pid == 0) {
|
||||
// We have to kludge a bit to deal with the args ConstructFilename
|
||||
// expects. The 1 is never used -- it's only impt. that it's not 0.
|
||||
ConstructFilename("/proc/self/maps", 1, ibuf_, Buffer::kBufSize);
|
||||
} else {
|
||||
ConstructFilename("/proc/%d/maps", pid, ibuf_, Buffer::kBufSize);
|
||||
}
|
||||
// No error logging since this can be called from the crash dump
|
||||
// handler at awkward moments. Users should call Valid() before
|
||||
// using.
|
||||
NO_INTR(fd_ = open(ibuf_, O_RDONLY));
|
||||
#elif defined(__FreeBSD__)
|
||||
// We don't support maps_backing on freebsd
|
||||
if (pid == 0) {
|
||||
ConstructFilename("/proc/curproc/map", 1, ibuf_, Buffer::kBufSize);
|
||||
} else {
|
||||
ConstructFilename("/proc/%d/map", pid, ibuf_, Buffer::kBufSize);
|
||||
}
|
||||
NO_INTR(fd_ = open(ibuf_, O_RDONLY));
|
||||
#elif defined(__sun__)
|
||||
if (pid == 0) {
|
||||
ConstructFilename("/proc/self/map", 1, ibuf_, Buffer::kBufSize);
|
||||
} else {
|
||||
ConstructFilename("/proc/%d/map", pid, ibuf_, Buffer::kBufSize);
|
||||
}
|
||||
NO_INTR(fd_ = open(ibuf_, O_RDONLY));
|
||||
#elif defined(__MACH__)
|
||||
current_image_ = _dyld_image_count(); // count down from the top
|
||||
current_load_cmd_ = -1;
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE |
|
||||
TH32CS_SNAPMODULE32,
|
||||
GetCurrentProcessId());
|
||||
memset(&module_, 0, sizeof(module_));
|
||||
#else
|
||||
fd_ = -1; // so Valid() is always false
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
ProcMapsIterator::~ProcMapsIterator() {
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
if (snapshot_ != INVALID_HANDLE_VALUE) CloseHandle(snapshot_);
|
||||
#elif defined(__MACH__)
|
||||
// no cleanup necessary!
|
||||
#else
|
||||
if (fd_ >= 0) NO_INTR(close(fd_));
|
||||
#endif
|
||||
delete dynamic_buffer_;
|
||||
}
|
||||
|
||||
bool ProcMapsIterator::Valid() const {
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
return snapshot_ != INVALID_HANDLE_VALUE;
|
||||
#elif defined(__MACH__)
|
||||
return 1;
|
||||
#else
|
||||
return fd_ != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ProcMapsIterator::Next(uint64 *start, uint64 *end, char **flags,
|
||||
uint64 *offset, int64 *inode, char **filename) {
|
||||
return NextExt(start, end, flags, offset, inode, filename, NULL, NULL,
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
// This has too many arguments. It should really be building
|
||||
// a map object and returning it. The problem is that this is called
|
||||
// when the memory allocator state is undefined, hence the arguments.
|
||||
bool ProcMapsIterator::NextExt(uint64 *start, uint64 *end, char **flags,
|
||||
uint64 *offset, int64 *inode, char **filename,
|
||||
uint64 *file_mapping, uint64 *file_pages,
|
||||
uint64 *anon_mapping, uint64 *anon_pages,
|
||||
dev_t *dev) {
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
|
||||
do {
|
||||
// Advance to the start of the next line
|
||||
stext_ = nextline_;
|
||||
|
||||
// See if we have a complete line in the buffer already
|
||||
nextline_ = static_cast<char *>(memchr (stext_, '\n', etext_ - stext_));
|
||||
if (!nextline_) {
|
||||
// Shift/fill the buffer so we do have a line
|
||||
int count = etext_ - stext_;
|
||||
|
||||
// Move the current text to the start of the buffer
|
||||
memmove(ibuf_, stext_, count);
|
||||
stext_ = ibuf_;
|
||||
etext_ = ibuf_ + count;
|
||||
|
||||
int nread = 0; // fill up buffer with text
|
||||
while (etext_ < ebuf_) {
|
||||
NO_INTR(nread = read(fd_, etext_, ebuf_ - etext_));
|
||||
if (nread > 0)
|
||||
etext_ += nread;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Zero out remaining characters in buffer at EOF to avoid returning
|
||||
// garbage from subsequent calls.
|
||||
if (etext_ != ebuf_ && nread == 0) {
|
||||
memset(etext_, 0, ebuf_ - etext_);
|
||||
}
|
||||
*etext_ = '\n'; // sentinel; safe because ibuf extends 1 char beyond ebuf
|
||||
nextline_ = static_cast<char *>(memchr (stext_, '\n', etext_ + 1 - stext_));
|
||||
}
|
||||
*nextline_ = 0; // turn newline into nul
|
||||
nextline_ += ((nextline_ < etext_)? 1 : 0); // skip nul if not end of text
|
||||
// stext_ now points at a nul-terminated line
|
||||
uint64 tmpstart, tmpend, tmpoffset;
|
||||
int64 tmpinode;
|
||||
int major, minor;
|
||||
unsigned filename_offset = 0;
|
||||
#if defined(__linux__)
|
||||
// for now, assume all linuxes have the same format
|
||||
if (sscanf(stext_, "%"SCNx64"-%"SCNx64" %4s %"SCNx64" %x:%x %"SCNd64" %n",
|
||||
(unsigned long long *)(start ? start : &tmpstart),
|
||||
(unsigned long long *)(end ? end : &tmpend),
|
||||
flags_,
|
||||
(unsigned long long *)(offset ? offset : &tmpoffset),
|
||||
&major, &minor,
|
||||
(unsigned long long *)(inode ? inode : &tmpinode),
|
||||
&filename_offset) != 7) continue;
|
||||
#elif defined(__CYGWIN__) || defined(__CYGWIN32__)
|
||||
// cygwin is like linux, except the third field is the "entry point"
|
||||
// rather than the offset (see format_process_maps at
|
||||
// http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/fhandler_process.cc?rev=1.89&content-type=text/x-cvsweb-markup&cvsroot=src
|
||||
// Offset is always be 0 on cygwin: cygwin implements an mmap
|
||||
// by loading the whole file and then calling NtMapViewOfSection.
|
||||
// Cygwin also seems to set its flags kinda randomly; use windows default.
|
||||
char tmpflags[5];
|
||||
if (offset)
|
||||
*offset = 0;
|
||||
strcpy(flags_, "r-xp");
|
||||
if (sscanf(stext_, "%llx-%llx %4s %llx %x:%x %lld %n",
|
||||
start ? start : &tmpstart,
|
||||
end ? end : &tmpend,
|
||||
tmpflags,
|
||||
&tmpoffset,
|
||||
&major, &minor,
|
||||
inode ? inode : &tmpinode, &filename_offset) != 7) continue;
|
||||
#elif defined(__FreeBSD__)
|
||||
// For the format, see http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/fs/procfs/procfs_map.c?rev=1.31&content-type=text/x-cvsweb-markup
|
||||
tmpstart = tmpend = tmpoffset = 0;
|
||||
tmpinode = 0;
|
||||
major = minor = 0; // can't get this info in freebsd
|
||||
if (inode)
|
||||
*inode = 0; // nor this
|
||||
if (offset)
|
||||
*offset = 0; // seems like this should be in there, but maybe not
|
||||
// start end resident privateresident obj(?) prot refcnt shadowcnt
|
||||
// flags copy_on_write needs_copy type filename:
|
||||
// 0x8048000 0x804a000 2 0 0xc104ce70 r-x 1 0 0x0 COW NC vnode /bin/cat
|
||||
if (sscanf(stext_, "0x%"SCNx64" 0x%"SCNx64" %*d %*d %*p %3s %*d %*d 0x%*x %*s %*s %*s %n",
|
||||
start ? start : &tmpstart,
|
||||
end ? end : &tmpend,
|
||||
flags_,
|
||||
&filename_offset) != 3) continue;
|
||||
#endif
|
||||
|
||||
// Depending on the Linux kernel being used, there may or may not be a space
|
||||
// after the inode if there is no filename. sscanf will in such situations
|
||||
// nondeterministically either fill in filename_offset or not (the results
|
||||
// differ on multiple calls in the same run even with identical arguments).
|
||||
// We don't want to wander off somewhere beyond the end of the string.
|
||||
size_t stext_length = strlen(stext_);
|
||||
if (filename_offset == 0 || filename_offset > stext_length)
|
||||
filename_offset = stext_length;
|
||||
|
||||
// We found an entry
|
||||
if (flags) *flags = flags_;
|
||||
if (filename) *filename = stext_ + filename_offset;
|
||||
if (dev) *dev = minor | (major << 8);
|
||||
|
||||
if (using_maps_backing_) {
|
||||
// Extract and parse physical page backing info.
|
||||
char *backing_ptr = stext_ + filename_offset +
|
||||
strlen(stext_+filename_offset);
|
||||
|
||||
// find the second '('
|
||||
int paren_count = 0;
|
||||
while (--backing_ptr > stext_) {
|
||||
if (*backing_ptr == '(') {
|
||||
++paren_count;
|
||||
if (paren_count >= 2) {
|
||||
uint64 tmp_file_mapping;
|
||||
uint64 tmp_file_pages;
|
||||
uint64 tmp_anon_mapping;
|
||||
uint64 tmp_anon_pages;
|
||||
|
||||
sscanf(backing_ptr+1, "F %"SCNx64" %"SCNd64") (A %"SCNx64" %"SCNd64")",
|
||||
(unsigned long long *)(file_mapping ?
|
||||
file_mapping : &tmp_file_mapping),
|
||||
(unsigned long long *)(file_pages ?
|
||||
file_pages : &tmp_file_pages),
|
||||
(unsigned long long *)(anon_mapping
|
||||
? anon_mapping : &tmp_anon_mapping),
|
||||
(unsigned long long *)(anon_pages
|
||||
? anon_pages : &tmp_anon_pages));
|
||||
// null terminate the file name (there is a space
|
||||
// before the first (.
|
||||
backing_ptr[-1] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} while (etext_ > ibuf_);
|
||||
#elif defined(__sun__)
|
||||
// This is based on MA_READ == 4, MA_WRITE == 2, MA_EXEC == 1
|
||||
static char kPerms[8][4] = { "---", "--x", "-w-", "-wx",
|
||||
"r--", "r-x", "rw-", "rwx" };
|
||||
COMPILE_ASSERT(MA_READ == 4, solaris_ma_read_must_equal_4);
|
||||
COMPILE_ASSERT(MA_WRITE == 2, solaris_ma_write_must_equal_2);
|
||||
COMPILE_ASSERT(MA_EXEC == 1, solaris_ma_exec_must_equal_1);
|
||||
Buffer object_path;
|
||||
int nread = 0; // fill up buffer with text
|
||||
NO_INTR(nread = read(fd_, ibuf_, sizeof(prmap_t)));
|
||||
if (nread == sizeof(prmap_t)) {
|
||||
long inode_from_mapname = 0;
|
||||
prmap_t* mapinfo = reinterpret_cast<prmap_t*>(ibuf_);
|
||||
// Best-effort attempt to get the inode from the filename. I think the
|
||||
// two middle ints are major and minor device numbers, but I'm not sure.
|
||||
sscanf(mapinfo->pr_mapname, "ufs.%*d.%*d.%ld", &inode_from_mapname);
|
||||
|
||||
if (pid_ == 0) {
|
||||
CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize,
|
||||
"/proc/self/path/%s", mapinfo->pr_mapname),
|
||||
Buffer::kBufSize);
|
||||
} else {
|
||||
CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize,
|
||||
"/proc/%d/path/%s",
|
||||
static_cast<int>(pid_), mapinfo->pr_mapname),
|
||||
Buffer::kBufSize);
|
||||
}
|
||||
ssize_t len = readlink(object_path.buf_, current_filename_, PATH_MAX);
|
||||
CHECK_LT(len, PATH_MAX);
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
current_filename_[len] = '\0';
|
||||
|
||||
if (start) *start = mapinfo->pr_vaddr;
|
||||
if (end) *end = mapinfo->pr_vaddr + mapinfo->pr_size;
|
||||
if (flags) *flags = kPerms[mapinfo->pr_mflags & 7];
|
||||
if (offset) *offset = mapinfo->pr_offset;
|
||||
if (inode) *inode = inode_from_mapname;
|
||||
if (filename) *filename = current_filename_;
|
||||
if (file_mapping) *file_mapping = 0;
|
||||
if (file_pages) *file_pages = 0;
|
||||
if (anon_mapping) *anon_mapping = 0;
|
||||
if (anon_pages) *anon_pages = 0;
|
||||
if (dev) *dev = 0;
|
||||
return true;
|
||||
}
|
||||
#elif defined(__MACH__)
|
||||
// We return a separate entry for each segment in the DLL. (TODO(csilvers):
|
||||
// can we do better?) A DLL ("image") has load-commands, some of which
|
||||
// talk about segment boundaries.
|
||||
// cf image_for_address from http://svn.digium.com/view/asterisk/team/oej/minivoicemail/dlfcn.c?revision=53912
|
||||
for (; current_image_ >= 0; current_image_--) {
|
||||
const mach_header* hdr = _dyld_get_image_header(current_image_);
|
||||
if (!hdr) continue;
|
||||
if (current_load_cmd_ < 0) // set up for this image
|
||||
current_load_cmd_ = hdr->ncmds; // again, go from the top down
|
||||
|
||||
// We start with the next load command (we've already looked at this one).
|
||||
for (current_load_cmd_--; current_load_cmd_ >= 0; current_load_cmd_--) {
|
||||
#ifdef MH_MAGIC_64
|
||||
if (NextExtMachHelper<MH_MAGIC_64, LC_SEGMENT_64,
|
||||
struct mach_header_64, struct segment_command_64>(
|
||||
hdr, current_image_, current_load_cmd_,
|
||||
start, end, flags, offset, inode, filename,
|
||||
file_mapping, file_pages, anon_mapping,
|
||||
anon_pages, dev)) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (NextExtMachHelper<MH_MAGIC, LC_SEGMENT,
|
||||
struct mach_header, struct segment_command>(
|
||||
hdr, current_image_, current_load_cmd_,
|
||||
start, end, flags, offset, inode, filename,
|
||||
file_mapping, file_pages, anon_mapping,
|
||||
anon_pages, dev)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// If we get here, no more load_cmd's in this image talk about
|
||||
// segments. Go on to the next image.
|
||||
}
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
static char kDefaultPerms[5] = "r-xp";
|
||||
BOOL ok;
|
||||
if (module_.dwSize == 0) { // only possible before first call
|
||||
module_.dwSize = sizeof(module_);
|
||||
ok = Module32First(snapshot_, &module_);
|
||||
} else {
|
||||
ok = Module32Next(snapshot_, &module_);
|
||||
}
|
||||
if (ok) {
|
||||
uint64 base_addr = reinterpret_cast<DWORD_PTR>(module_.modBaseAddr);
|
||||
if (start) *start = base_addr;
|
||||
if (end) *end = base_addr + module_.modBaseSize;
|
||||
if (flags) *flags = kDefaultPerms;
|
||||
if (offset) *offset = 0;
|
||||
if (inode) *inode = 0;
|
||||
if (filename) *filename = module_.szExePath;
|
||||
if (file_mapping) *file_mapping = 0;
|
||||
if (file_pages) *file_pages = 0;
|
||||
if (anon_mapping) *anon_mapping = 0;
|
||||
if (anon_pages) *anon_pages = 0;
|
||||
if (dev) *dev = 0;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// We didn't find anything
|
||||
return false;
|
||||
}
|
||||
|
||||
int ProcMapsIterator::FormatLine(char* buffer, int bufsize,
|
||||
uint64 start, uint64 end, const char *flags,
|
||||
uint64 offset, int64 inode,
|
||||
const char *filename, dev_t dev) {
|
||||
// We assume 'flags' looks like 'rwxp' or 'rwx'.
|
||||
char r = (flags && flags[0] == 'r') ? 'r' : '-';
|
||||
char w = (flags && flags[0] && flags[1] == 'w') ? 'w' : '-';
|
||||
char x = (flags && flags[0] && flags[1] && flags[2] == 'x') ? 'x' : '-';
|
||||
// p always seems set on linux, so we set the default to 'p', not '-'
|
||||
char p = (flags && flags[0] && flags[1] && flags[2] && flags[3] != 'p')
|
||||
? '-' : 'p';
|
||||
|
||||
const int rc = snprintf(buffer, bufsize,
|
||||
"%08"PRIx64"-%08"PRIx64" %c%c%c%c %08"PRIx64" %02x:%02x %-11"PRId64" %s\n",
|
||||
(unsigned long long)start, (unsigned long long)end, r,w,x,p,
|
||||
(unsigned long long)offset,
|
||||
static_cast<int>(dev/256), static_cast<int>(dev%256),
|
||||
(unsigned long long)inode, filename);
|
||||
return (rc < 0 || rc >= bufsize) ? 0 : rc;
|
||||
}
|
||||
|
||||
// Helper to add the list of mapped shared libraries to a profile.
|
||||
// Fill formatted "/proc/self/maps" contents into buffer 'buf' of size 'size'
|
||||
// and return the actual size occupied in 'buf'. We fill wrote_all to true
|
||||
// if we successfully wrote all proc lines to buf, false else.
|
||||
// We do not provision for 0-terminating 'buf'.
|
||||
int FillProcSelfMaps(char buf[], int size, bool* wrote_all) {
|
||||
ProcMapsIterator::Buffer iterbuf;
|
||||
ProcMapsIterator it(0, &iterbuf); // 0 means "current pid"
|
||||
|
||||
uint64 start, end, offset;
|
||||
int64 inode;
|
||||
char *flags, *filename;
|
||||
int bytes_written = 0;
|
||||
*wrote_all = true;
|
||||
while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
|
||||
const int line_length = it.FormatLine(buf + bytes_written,
|
||||
size - bytes_written,
|
||||
start, end, flags, offset,
|
||||
inode, filename, 0);
|
||||
if (line_length == 0)
|
||||
*wrote_all = false; // failed to write this line out
|
||||
else
|
||||
bytes_written += line_length;
|
||||
|
||||
}
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
// Dump the same data as FillProcSelfMaps reads to fd.
|
||||
// It seems easier to repeat parts of FillProcSelfMaps here than to
|
||||
// reuse it via a call.
|
||||
void DumpProcSelfMaps(RawFD fd) {
|
||||
ProcMapsIterator::Buffer iterbuf;
|
||||
ProcMapsIterator it(0, &iterbuf); // 0 means "current pid"
|
||||
|
||||
uint64 start, end, offset;
|
||||
int64 inode;
|
||||
char *flags, *filename;
|
||||
ProcMapsIterator::Buffer linebuf;
|
||||
while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
|
||||
int written = it.FormatLine(linebuf.buf_, sizeof(linebuf.buf_),
|
||||
start, end, flags, offset, inode, filename,
|
||||
0);
|
||||
RawWrite(fd, linebuf.buf_, written);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-run fn until it doesn't cause EINTR.
|
||||
#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
|
||||
|
||||
RawFD RawOpenForWriting(const char* filename) {
|
||||
return open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664);
|
||||
}
|
||||
|
||||
void RawWrite(RawFD fd, const char* buf, size_t len) {
|
||||
while (len > 0) {
|
||||
ssize_t r;
|
||||
NO_INTR(r = write(fd, buf, len));
|
||||
if (r <= 0) break;
|
||||
buf += r;
|
||||
len -= r;
|
||||
}
|
||||
}
|
||||
|
||||
void RawClose(RawFD fd) {
|
||||
NO_INTR(close(fd));
|
||||
}
|
@ -1,234 +0,0 @@
|
||||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// All functions here are thread-hostile due to file caching unless
|
||||
// commented otherwise.
|
||||
|
||||
#ifndef _SYSINFO_H_
|
||||
#define _SYSINFO_H_
|
||||
|
||||
#include <time.h>
|
||||
#if (defined(_WIN32) || defined(__MINGW32__)) && (!defined(__CYGWIN__) && !defined(__CYGWIN32__))
|
||||
#include <windows.h> // for DWORD
|
||||
#include <TlHelp32.h> // for CreateToolhelp32Snapshot
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h> // for pid_t
|
||||
#endif
|
||||
#include <stddef.h> // for size_t
|
||||
#include <limits.h> // for PATH_MAX
|
||||
#include "basictypes.h"
|
||||
|
||||
// This getenv function is safe to call before the C runtime is initialized.
|
||||
// On Windows, it utilizes GetEnvironmentVariable() and on unix it uses
|
||||
// /proc/self/environ instead calling getenv(). It's intended to be used in
|
||||
// routines that run before main(), when the state required for getenv() may
|
||||
// not be set up yet. In particular, errno isn't set up until relatively late
|
||||
// (after the pthreads library has a chance to make it threadsafe), and
|
||||
// getenv() doesn't work until then.
|
||||
// On some platforms, this call will utilize the same, static buffer for
|
||||
// repeated GetenvBeforeMain() calls. Callers should not expect pointers from
|
||||
// this routine to be long lived.
|
||||
// Note that on unix, /proc only has the environment at the time the
|
||||
// application was started, so this routine ignores setenv() calls/etc. Also
|
||||
// note it only reads the first 16K of the environment.
|
||||
extern const char* GetenvBeforeMain(const char* name);
|
||||
|
||||
// This takes as an argument an environment-variable name (like
|
||||
// CPUPROFILE) whose value is supposed to be a file-path, and sets
|
||||
// path to that path, and returns true. Non-trivial for surprising
|
||||
// reasons, as documented in sysinfo.cc. path must have space PATH_MAX.
|
||||
extern bool GetUniquePathFromEnv(const char* env_name, char* path);
|
||||
|
||||
extern int NumCPUs();
|
||||
|
||||
// processor cycles per second of each processor. Thread-safe.
|
||||
extern double CyclesPerSecond(void);
|
||||
|
||||
|
||||
// Return true if we're running POSIX (e.g., NPTL on Linux) threads,
|
||||
// as opposed to a non-POSIX thread libary. The thing that we care
|
||||
// about is whether a thread's pid is the same as the thread that
|
||||
// spawned it. If so, this function returns true.
|
||||
// Thread-safe.
|
||||
// Note: We consider false negatives to be OK.
|
||||
bool HasPosixThreads();
|
||||
|
||||
#ifndef SWIG // SWIG doesn't like struct Buffer and variable arguments.
|
||||
|
||||
// A ProcMapsIterator abstracts access to /proc/maps for a given
|
||||
// process. Needs to be stack-allocatable and avoid using stdio/malloc
|
||||
// so it can be used in the google stack dumper, heap-profiler, etc.
|
||||
//
|
||||
// On Windows and Mac OS X, this iterator iterates *only* over DLLs
|
||||
// mapped into this process space. For Linux, FreeBSD, and Solaris,
|
||||
// it iterates over *all* mapped memory regions, including anonymous
|
||||
// mmaps. For other O/Ss, it is unlikely to work at all, and Valid()
|
||||
// will always return false. Also note: this routine only works on
|
||||
// FreeBSD if procfs is mounted: make sure this is in your /etc/fstab:
|
||||
// proc /proc procfs rw 0 0
|
||||
class ProcMapsIterator {
|
||||
public:
|
||||
struct Buffer {
|
||||
#ifdef __FreeBSD__
|
||||
// FreeBSD requires us to read all of the maps file at once, so
|
||||
// we have to make a buffer that's "always" big enough
|
||||
static const size_t kBufSize = 102400;
|
||||
#else // a one-line buffer is good enough
|
||||
static const size_t kBufSize = PATH_MAX + 1024;
|
||||
#endif
|
||||
char buf_[kBufSize];
|
||||
};
|
||||
|
||||
|
||||
// Create a new iterator for the specified pid. pid can be 0 for "self".
|
||||
explicit ProcMapsIterator(pid_t pid);
|
||||
|
||||
// Create an iterator with specified storage (for use in signal
|
||||
// handler). "buffer" should point to a ProcMapsIterator::Buffer
|
||||
// buffer can be NULL in which case a bufer will be allocated.
|
||||
ProcMapsIterator(pid_t pid, Buffer *buffer);
|
||||
|
||||
// Iterate through maps_backing instead of maps if use_maps_backing
|
||||
// is true. Otherwise the same as above. buffer can be NULL and
|
||||
// it will allocate a buffer itself.
|
||||
ProcMapsIterator(pid_t pid, Buffer *buffer,
|
||||
bool use_maps_backing);
|
||||
|
||||
// Returns true if the iterator successfully initialized;
|
||||
bool Valid() const;
|
||||
|
||||
// Returns a pointer to the most recently parsed line. Only valid
|
||||
// after Next() returns true, and until the iterator is destroyed or
|
||||
// Next() is called again. This may give strange results on non-Linux
|
||||
// systems. Prefer FormatLine() if that may be a concern.
|
||||
const char *CurrentLine() const { return stext_; }
|
||||
|
||||
// Writes the "canonical" form of the /proc/xxx/maps info for a single
|
||||
// line to the passed-in buffer. Returns the number of bytes written,
|
||||
// or 0 if it was not able to write the complete line. (To guarantee
|
||||
// success, buffer should have size at least Buffer::kBufSize.)
|
||||
// Takes as arguments values set via a call to Next(). The
|
||||
// "canonical" form of the line (taken from linux's /proc/xxx/maps):
|
||||
// <start_addr(hex)>-<end_addr(hex)> <perms(rwxp)> <offset(hex)> +
|
||||
// <major_dev(hex)>:<minor_dev(hex)> <inode> <filename> Note: the
|
||||
// eg
|
||||
// 08048000-0804c000 r-xp 00000000 03:01 3793678 /bin/cat
|
||||
// If you don't have the dev_t (dev), feel free to pass in 0.
|
||||
// (Next() doesn't return a dev_t, though NextExt does.)
|
||||
//
|
||||
// Note: if filename and flags were obtained via a call to Next(),
|
||||
// then the output of this function is only valid if Next() returned
|
||||
// true, and only until the iterator is destroyed or Next() is
|
||||
// called again. (Since filename, at least, points into CurrentLine.)
|
||||
static int FormatLine(char* buffer, int bufsize,
|
||||
uint64 start, uint64 end, const char *flags,
|
||||
uint64 offset, int64 inode, const char *filename,
|
||||
dev_t dev);
|
||||
|
||||
// Find the next entry in /proc/maps; return true if found or false
|
||||
// if at the end of the file.
|
||||
//
|
||||
// Any of the result pointers can be NULL if you're not interested
|
||||
// in those values.
|
||||
//
|
||||
// If "flags" and "filename" are passed, they end up pointing to
|
||||
// storage within the ProcMapsIterator that is valid only until the
|
||||
// iterator is destroyed or Next() is called again. The caller may
|
||||
// modify the contents of these strings (up as far as the first NUL,
|
||||
// and only until the subsequent call to Next()) if desired.
|
||||
|
||||
// The offsets are all uint64 in order to handle the case of a
|
||||
// 32-bit process running on a 64-bit kernel
|
||||
//
|
||||
// IMPORTANT NOTE: see top-of-class notes for details about what
|
||||
// mapped regions Next() iterates over, depending on O/S.
|
||||
// TODO(csilvers): make flags and filename const.
|
||||
bool Next(uint64 *start, uint64 *end, char **flags,
|
||||
uint64 *offset, int64 *inode, char **filename);
|
||||
|
||||
bool NextExt(uint64 *start, uint64 *end, char **flags,
|
||||
uint64 *offset, int64 *inode, char **filename,
|
||||
uint64 *file_mapping, uint64 *file_pages,
|
||||
uint64 *anon_mapping, uint64 *anon_pages,
|
||||
dev_t *dev);
|
||||
|
||||
~ProcMapsIterator();
|
||||
|
||||
private:
|
||||
void Init(pid_t pid, Buffer *buffer, bool use_maps_backing);
|
||||
|
||||
char *ibuf_; // input buffer
|
||||
char *stext_; // start of text
|
||||
char *etext_; // end of text
|
||||
char *nextline_; // start of next line
|
||||
char *ebuf_; // end of buffer (1 char for a nul)
|
||||
#if (defined(_WIN32) || defined(__MINGW32__)) && (!defined(__CYGWIN__) && !defined(__CYGWIN32__))
|
||||
HANDLE snapshot_; // filehandle on dll info
|
||||
// In a change from the usual W-A pattern, there is no A variant of
|
||||
// MODULEENTRY32. Tlhelp32.h #defines the W variant, but not the A.
|
||||
// We want the original A variants, and this #undef is the only
|
||||
// way I see to get them. Redefining it when we're done prevents us
|
||||
// from affecting other .cc files.
|
||||
# ifdef MODULEENTRY32 // Alias of W
|
||||
# undef MODULEENTRY32
|
||||
MODULEENTRY32 module_; // info about current dll (and dll iterator)
|
||||
# define MODULEENTRY32 MODULEENTRY32W
|
||||
# else // It's the ascii, the one we want.
|
||||
MODULEENTRY32 module_; // info about current dll (and dll iterator)
|
||||
# endif
|
||||
#elif defined(__MACH__)
|
||||
int current_image_; // dll's are called "images" in macos parlance
|
||||
int current_load_cmd_; // the segment of this dll we're examining
|
||||
#elif defined(__sun__) // Solaris
|
||||
int fd_;
|
||||
char current_filename_[PATH_MAX];
|
||||
#else
|
||||
int fd_; // filehandle on /proc/*/maps
|
||||
#endif
|
||||
pid_t pid_;
|
||||
char flags_[10];
|
||||
Buffer* dynamic_buffer_; // dynamically-allocated Buffer
|
||||
bool using_maps_backing_; // true if we are looking at maps_backing instead of maps.
|
||||
};
|
||||
|
||||
#endif /* #ifndef SWIG */
|
||||
|
||||
// Helper routines
|
||||
typedef int RawFD;
|
||||
const RawFD kIllegalRawFD = -1; // what open returns if it fails
|
||||
|
||||
RawFD RawOpenForWriting(const char* filename); // uses default permissions
|
||||
void RawWrite(RawFD fd, const char* buf, size_t len);
|
||||
void RawClose(RawFD fd);
|
||||
|
||||
int FillProcSelfMaps(char buf[], int size, bool* wrote_all);
|
||||
void DumpProcSelfMaps(RawFD fd);
|
||||
|
||||
#endif /* #ifndef _SYSINFO_H_ */
|
118
lib/asan/tests/CMakeLists.txt
Normal file
118
lib/asan/tests/CMakeLists.txt
Normal file
@ -0,0 +1,118 @@
|
||||
# Testing rules for AddressSanitizer.
|
||||
#
|
||||
# These are broken into two buckets. One set of tests directly interacts with
|
||||
# the runtime library and checks its functionality. These are the
|
||||
# no-instrumentation tests.
|
||||
#
|
||||
# Another group of tests relies upon the ability to compile the test with
|
||||
# address sanitizer instrumentation pass. These tests form "integration" tests
|
||||
# and have some elements of version skew -- they test the *host* compiler's
|
||||
# instrumentation against the just-built runtime library.
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(../..)
|
||||
|
||||
set(ASAN_UNITTEST_COMMON_CFLAGS
|
||||
-Wall
|
||||
-Wno-format
|
||||
-fvisibility=hidden
|
||||
)
|
||||
# Support 64-bit and 32-bit builds.
|
||||
if(LLVM_BUILD_32_BITS)
|
||||
list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -m32)
|
||||
else()
|
||||
list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -m64)
|
||||
endif()
|
||||
|
||||
set(ASAN_GTEST_INCLUDE_CFLAGS
|
||||
-I${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include
|
||||
-I${LLVM_MAIN_SRC_DIR}/include
|
||||
-I${LLVM_BINARY_DIR}/include
|
||||
-D__STDC_CONSTANT_MACROS
|
||||
-D__STDC_LIMIT_MACROS
|
||||
)
|
||||
|
||||
set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS
|
||||
${ASAN_UNITTEST_COMMON_CFLAGS}
|
||||
${ASAN_GTEST_INCLUDE_CFLAGS}
|
||||
-faddress-sanitizer
|
||||
-O2
|
||||
-g
|
||||
-mllvm "-asan-blacklist=${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore"
|
||||
-DASAN_HAS_BLACKLIST=1
|
||||
-DASAN_HAS_EXCEPTIONS=1
|
||||
-DASAN_NEEDS_SEGV=1
|
||||
-DASAN_UAR=0
|
||||
)
|
||||
|
||||
add_custom_target(AsanTests)
|
||||
set_target_properties(AsanTests PROPERTIES FOLDER "ASan tests")
|
||||
function(add_asan_test testname)
|
||||
add_unittest(AsanTests ${testname} ${ARGN})
|
||||
if(LLVM_BUILD_32_BITS)
|
||||
target_link_libraries(${testname} clang_rt.asan-i386)
|
||||
else()
|
||||
target_link_libraries(${testname} clang_rt.asan-x86_64)
|
||||
endif()
|
||||
if (APPLE)
|
||||
# Darwin-specific linker flags.
|
||||
set_property(TARGET ${testname} APPEND PROPERTY
|
||||
LINK_FLAGS "-framework Foundation")
|
||||
elseif (UNIX)
|
||||
# Linux-specific linker flags.
|
||||
set_property(TARGET ${testname} APPEND PROPERTY
|
||||
LINK_FLAGS "-lpthread -ldl -export-dynamic")
|
||||
endif()
|
||||
set(add_compile_flags "")
|
||||
get_property(compile_flags TARGET ${testname} PROPERTY COMPILE_FLAGS)
|
||||
foreach(arg ${ASAN_UNITTEST_COMMON_CFLAGS})
|
||||
set(add_compile_flags "${add_compile_flags} ${arg}")
|
||||
endforeach(arg ${ASAN_UNITTEST_COMMON_CFLAGS})
|
||||
set_property(TARGET ${testname} PROPERTY COMPILE_FLAGS
|
||||
"${compile_flags} ${add_compile_flags}")
|
||||
endfunction()
|
||||
|
||||
set(ASAN_NOINST_TEST_SOURCES
|
||||
asan_noinst_test.cc
|
||||
asan_break_optimization.cc
|
||||
)
|
||||
|
||||
set(ASAN_INST_TEST_OBJECTS)
|
||||
|
||||
# We only support building instrumented tests when we're not cross compiling
|
||||
# and targeting a unix-like system where we can predict viable compilation and
|
||||
# linking strategies.
|
||||
if("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND UNIX)
|
||||
|
||||
# This function is a custom routine to manage manually compiling source files
|
||||
# for unit tests with the just-built Clang binary, using the ASan
|
||||
# instrumentation, and linking them into a test executable.
|
||||
function(add_asan_compile_command source extra_cflags)
|
||||
set(output_obj "${source}.asan.o")
|
||||
add_custom_command(
|
||||
OUTPUT ${output_obj}
|
||||
COMMAND clang
|
||||
${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}
|
||||
${extra_cflags}
|
||||
-c -o "${output_obj}"
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${source}
|
||||
MAIN_DEPENDENCY ${source}
|
||||
DEPENDS clang clang_rt.asan-i386 clang_rt.asan-x86_64 ${ARGN}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
add_asan_compile_command(asan_globals_test.cc "")
|
||||
add_asan_compile_command(asan_test.cc "")
|
||||
list(APPEND ASAN_INST_TEST_OBJECTS asan_globals_test.cc.asan.o
|
||||
asan_test.cc.asan.o)
|
||||
if (APPLE)
|
||||
add_asan_compile_command(asan_mac_test.mm "-ObjC")
|
||||
list(APPEND ASAN_INST_TEST_OBJECTS asan_mac_test.mm.asan.o)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
add_asan_test(AsanTest ${ASAN_NOINST_TEST_SOURCES}
|
||||
${ASAN_INST_TEST_OBJECTS})
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_benchmarks_test.cc ------------*- C++ -*-===//
|
||||
//===-- asan_benchmarks_test.cc ----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_break_optimization.cc ------------*- C++ -*-===//
|
||||
//===-- asan_break_optimization.cc ----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -15,4 +15,5 @@
|
||||
// Have this function in a separate file to avoid inlining.
|
||||
// (Yes, we know about cross-file inlining, but let's assume we don't use it).
|
||||
extern "C" void break_optimization(void *x) {
|
||||
(void)x;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_globals_test.cc ------------*- C++ -*-===//
|
||||
//===-- asan_globals_test.cc ----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
|
@ -1,334 +0,0 @@
|
||||
//===-- asan_interface_test.cc ------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asan_test_config.h"
|
||||
#include "asan_test_utils.h"
|
||||
#include "asan_interface.h"
|
||||
|
||||
TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) {
|
||||
EXPECT_EQ(1, __asan_get_estimated_allocated_size(0));
|
||||
const size_t sizes[] = { 1, 30, 1<<30 };
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
EXPECT_EQ(sizes[i], __asan_get_estimated_allocated_size(sizes[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kGetAllocatedSizeErrorMsg =
|
||||
"__asan_get_allocated_size failed";
|
||||
|
||||
TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) {
|
||||
const size_t kArraySize = 100;
|
||||
char *array = Ident((char*)malloc(kArraySize));
|
||||
int *int_ptr = Ident(new int);
|
||||
|
||||
// Allocated memory is owned by allocator. Allocated size should be
|
||||
// equal to requested size.
|
||||
EXPECT_EQ(true, __asan_get_ownership(array));
|
||||
EXPECT_EQ(kArraySize, __asan_get_allocated_size(array));
|
||||
EXPECT_EQ(true, __asan_get_ownership(int_ptr));
|
||||
EXPECT_EQ(sizeof(int), __asan_get_allocated_size(int_ptr));
|
||||
|
||||
// We cannot call GetAllocatedSize from the memory we didn't map,
|
||||
// and from the interior pointers (not returned by previous malloc).
|
||||
void *wild_addr = (void*)0x1;
|
||||
EXPECT_EQ(false, __asan_get_ownership(wild_addr));
|
||||
EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg);
|
||||
EXPECT_EQ(false, __asan_get_ownership(array + kArraySize / 2));
|
||||
EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2),
|
||||
kGetAllocatedSizeErrorMsg);
|
||||
|
||||
// NULL is a valid argument and is owned.
|
||||
EXPECT_EQ(true, __asan_get_ownership(NULL));
|
||||
EXPECT_EQ(0, __asan_get_allocated_size(NULL));
|
||||
|
||||
// When memory is freed, it's not owned, and call to GetAllocatedSize
|
||||
// is forbidden.
|
||||
free(array);
|
||||
EXPECT_EQ(false, __asan_get_ownership(array));
|
||||
EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg);
|
||||
|
||||
delete int_ptr;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) {
|
||||
size_t before_malloc, after_malloc, after_free;
|
||||
char *array;
|
||||
const size_t kMallocSize = 100;
|
||||
before_malloc = __asan_get_current_allocated_bytes();
|
||||
|
||||
array = Ident((char*)malloc(kMallocSize));
|
||||
after_malloc = __asan_get_current_allocated_bytes();
|
||||
EXPECT_EQ(before_malloc + kMallocSize, after_malloc);
|
||||
|
||||
free(array);
|
||||
after_free = __asan_get_current_allocated_bytes();
|
||||
EXPECT_EQ(before_malloc, after_free);
|
||||
}
|
||||
|
||||
static void DoDoubleFree() {
|
||||
int *x = Ident(new int);
|
||||
delete Ident(x);
|
||||
delete Ident(x);
|
||||
}
|
||||
|
||||
// This test is run in a separate process, so that large malloced
|
||||
// chunk won't remain in the free lists after the test.
|
||||
// Note: use ASSERT_* instead of EXPECT_* here.
|
||||
static void RunGetHeapSizeTestAndDie() {
|
||||
size_t old_heap_size, new_heap_size, heap_growth;
|
||||
// We unlikely have have chunk of this size in free list.
|
||||
static const size_t kLargeMallocSize = 1 << 29; // 512M
|
||||
old_heap_size = __asan_get_heap_size();
|
||||
fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
|
||||
free(Ident(malloc(kLargeMallocSize)));
|
||||
new_heap_size = __asan_get_heap_size();
|
||||
heap_growth = new_heap_size - old_heap_size;
|
||||
fprintf(stderr, "heap growth after first malloc: %zu\n", heap_growth);
|
||||
ASSERT_GE(heap_growth, kLargeMallocSize);
|
||||
ASSERT_LE(heap_growth, 2 * kLargeMallocSize);
|
||||
|
||||
// Now large chunk should fall into free list, and can be
|
||||
// allocated without increasing heap size.
|
||||
old_heap_size = new_heap_size;
|
||||
free(Ident(malloc(kLargeMallocSize)));
|
||||
heap_growth = __asan_get_heap_size() - old_heap_size;
|
||||
fprintf(stderr, "heap growth after second malloc: %zu\n", heap_growth);
|
||||
ASSERT_LT(heap_growth, kLargeMallocSize);
|
||||
|
||||
// Test passed. Now die with expected double-free.
|
||||
DoDoubleFree();
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, GetHeapSizeTest) {
|
||||
EXPECT_DEATH(RunGetHeapSizeTestAndDie(), "double-free");
|
||||
}
|
||||
|
||||
// Note: use ASSERT_* instead of EXPECT_* here.
|
||||
static void DoLargeMallocForGetFreeBytesTestAndDie() {
|
||||
size_t old_free_bytes, new_free_bytes;
|
||||
static const size_t kLargeMallocSize = 1 << 29; // 512M
|
||||
// If we malloc and free a large memory chunk, it will not fall
|
||||
// into quarantine and will be available for future requests.
|
||||
old_free_bytes = __asan_get_free_bytes();
|
||||
fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
|
||||
fprintf(stderr, "free bytes before malloc: %zu\n", old_free_bytes);
|
||||
free(Ident(malloc(kLargeMallocSize)));
|
||||
new_free_bytes = __asan_get_free_bytes();
|
||||
fprintf(stderr, "free bytes after malloc and free: %zu\n", new_free_bytes);
|
||||
ASSERT_GE(new_free_bytes, old_free_bytes + kLargeMallocSize);
|
||||
// Test passed.
|
||||
DoDoubleFree();
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, GetFreeBytesTest) {
|
||||
static const size_t kNumOfChunks = 100;
|
||||
static const size_t kChunkSize = 100;
|
||||
char *chunks[kNumOfChunks];
|
||||
size_t i;
|
||||
size_t old_free_bytes, new_free_bytes;
|
||||
// Allocate a small chunk. Now allocator probably has a lot of these
|
||||
// chunks to fulfill future requests. So, future requests will decrease
|
||||
// the number of free bytes.
|
||||
chunks[0] = Ident((char*)malloc(kChunkSize));
|
||||
old_free_bytes = __asan_get_free_bytes();
|
||||
for (i = 1; i < kNumOfChunks; i++) {
|
||||
chunks[i] = Ident((char*)malloc(kChunkSize));
|
||||
new_free_bytes = __asan_get_free_bytes();
|
||||
EXPECT_LT(new_free_bytes, old_free_bytes);
|
||||
old_free_bytes = new_free_bytes;
|
||||
}
|
||||
// Deleting these chunks will move them to quarantine, number of free
|
||||
// bytes won't increase.
|
||||
for (i = 0; i < kNumOfChunks; i++) {
|
||||
free(chunks[i]);
|
||||
EXPECT_EQ(old_free_bytes, __asan_get_free_bytes());
|
||||
}
|
||||
EXPECT_DEATH(DoLargeMallocForGetFreeBytesTestAndDie(), "double-free");
|
||||
}
|
||||
|
||||
static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<20, 357};
|
||||
static const size_t kManyThreadsIterations = 250;
|
||||
static const size_t kManyThreadsNumThreads = 200;
|
||||
|
||||
void *ManyThreadsWithStatsWorker(void *arg) {
|
||||
for (size_t iter = 0; iter < kManyThreadsIterations; iter++) {
|
||||
for (size_t size_index = 0; size_index < 4; size_index++) {
|
||||
free(Ident(malloc(kManyThreadsMallocSizes[size_index])));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) {
|
||||
size_t before_test, after_test, i;
|
||||
pthread_t threads[kManyThreadsNumThreads];
|
||||
before_test = __asan_get_current_allocated_bytes();
|
||||
for (i = 0; i < kManyThreadsNumThreads; i++) {
|
||||
pthread_create(&threads[i], 0,
|
||||
(void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i);
|
||||
}
|
||||
for (i = 0; i < kManyThreadsNumThreads; i++) {
|
||||
pthread_join(threads[i], 0);
|
||||
}
|
||||
after_test = __asan_get_current_allocated_bytes();
|
||||
// ASan stats also reflect memory usage of internal ASan RTL structs,
|
||||
// so we can't check for equality here.
|
||||
EXPECT_LT(after_test, before_test + (1UL<<20));
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, ExitCode) {
|
||||
int original_exit_code = __asan_set_error_exit_code(7);
|
||||
EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), "");
|
||||
EXPECT_EQ(7, __asan_set_error_exit_code(8));
|
||||
EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), "");
|
||||
EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code));
|
||||
EXPECT_EXIT(DoDoubleFree(),
|
||||
::testing::ExitedWithCode(original_exit_code), "");
|
||||
}
|
||||
|
||||
static const char* kUseAfterPoisonErrorMessage = "use-after-poison";
|
||||
|
||||
#define ACCESS(ptr, offset) Ident(*(ptr + offset))
|
||||
|
||||
#define DIE_ON_ACCESS(ptr, offset) \
|
||||
EXPECT_DEATH(Ident(*(ptr + offset)), kUseAfterPoisonErrorMessage)
|
||||
|
||||
TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) {
|
||||
char *array = Ident((char*)malloc(120));
|
||||
// poison array[40..80)
|
||||
ASAN_POISON_MEMORY_REGION(array + 40, 40);
|
||||
ACCESS(array, 39);
|
||||
ACCESS(array, 80);
|
||||
DIE_ON_ACCESS(array, 40);
|
||||
DIE_ON_ACCESS(array, 60);
|
||||
DIE_ON_ACCESS(array, 79);
|
||||
ASAN_UNPOISON_MEMORY_REGION(array + 40, 40);
|
||||
// access previously poisoned memory.
|
||||
ACCESS(array, 40);
|
||||
ACCESS(array, 79);
|
||||
free(array);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) {
|
||||
char *array = Ident((char*)malloc(120));
|
||||
// Poison [0..40) and [80..120)
|
||||
ASAN_POISON_MEMORY_REGION(array, 40);
|
||||
ASAN_POISON_MEMORY_REGION(array + 80, 40);
|
||||
DIE_ON_ACCESS(array, 20);
|
||||
ACCESS(array, 60);
|
||||
DIE_ON_ACCESS(array, 100);
|
||||
// Poison whole array - [0..120)
|
||||
ASAN_POISON_MEMORY_REGION(array, 120);
|
||||
DIE_ON_ACCESS(array, 60);
|
||||
// Unpoison [24..96)
|
||||
ASAN_UNPOISON_MEMORY_REGION(array + 24, 72);
|
||||
DIE_ON_ACCESS(array, 23);
|
||||
ACCESS(array, 24);
|
||||
ACCESS(array, 60);
|
||||
ACCESS(array, 95);
|
||||
DIE_ON_ACCESS(array, 96);
|
||||
free(array);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) {
|
||||
// Vector of capacity 20
|
||||
char *vec = Ident((char*)malloc(20));
|
||||
ASAN_POISON_MEMORY_REGION(vec, 20);
|
||||
for (size_t i = 0; i < 7; i++) {
|
||||
// Simulate push_back.
|
||||
ASAN_UNPOISON_MEMORY_REGION(vec + i, 1);
|
||||
ACCESS(vec, i);
|
||||
DIE_ON_ACCESS(vec, i + 1);
|
||||
}
|
||||
for (size_t i = 7; i > 0; i--) {
|
||||
// Simulate pop_back.
|
||||
ASAN_POISON_MEMORY_REGION(vec + i - 1, 1);
|
||||
DIE_ON_ACCESS(vec, i - 1);
|
||||
if (i > 1) ACCESS(vec, i - 2);
|
||||
}
|
||||
free(vec);
|
||||
}
|
||||
|
||||
// Make sure that each aligned block of size "2^granularity" doesn't have
|
||||
// "true" value before "false" value.
|
||||
static void MakeShadowValid(bool *shadow, int length, int granularity) {
|
||||
bool can_be_poisoned = true;
|
||||
for (int i = length - 1; i >= 0; i--) {
|
||||
can_be_poisoned &= shadow[i];
|
||||
shadow[i] &= can_be_poisoned;
|
||||
if (i % (1 << granularity) == 0) {
|
||||
can_be_poisoned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, PoisoningStressTest) {
|
||||
const size_t kSize = 24;
|
||||
bool expected[kSize];
|
||||
char *arr = Ident((char*)malloc(kSize));
|
||||
for (size_t l1 = 0; l1 < kSize; l1++) {
|
||||
for (size_t s1 = 1; l1 + s1 <= kSize; s1++) {
|
||||
for (size_t l2 = 0; l2 < kSize; l2++) {
|
||||
for (size_t s2 = 1; l2 + s2 <= kSize; s2++) {
|
||||
// Poison [l1, l1+s1), [l2, l2+s2) and check result.
|
||||
ASAN_UNPOISON_MEMORY_REGION(arr, kSize);
|
||||
ASAN_POISON_MEMORY_REGION(arr + l1, s1);
|
||||
ASAN_POISON_MEMORY_REGION(arr + l2, s2);
|
||||
memset(expected, false, kSize);
|
||||
memset(expected + l1, true, s1);
|
||||
MakeShadowValid(expected, 24, /*granularity*/ 3);
|
||||
memset(expected + l2, true, s2);
|
||||
MakeShadowValid(expected, 24, /*granularity*/ 3);
|
||||
for (size_t i = 0; i < kSize; i++) {
|
||||
ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i));
|
||||
}
|
||||
// Unpoison [l1, l1+s1) and [l2, l2+s2) and check result.
|
||||
ASAN_POISON_MEMORY_REGION(arr, kSize);
|
||||
ASAN_UNPOISON_MEMORY_REGION(arr + l1, s1);
|
||||
ASAN_UNPOISON_MEMORY_REGION(arr + l2, s2);
|
||||
memset(expected, true, kSize);
|
||||
memset(expected + l1, false, s1);
|
||||
MakeShadowValid(expected, 24, /*granularity*/ 3);
|
||||
memset(expected + l2, false, s2);
|
||||
MakeShadowValid(expected, 24, /*granularity*/ 3);
|
||||
for (size_t i = 0; i < kSize; i++) {
|
||||
ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *kInvalidPoisonMessage = "invalid-poison-memory-range";
|
||||
static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range";
|
||||
|
||||
TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) {
|
||||
char *array = Ident((char*)malloc(120));
|
||||
ASAN_UNPOISON_MEMORY_REGION(array, 120);
|
||||
// Try to unpoison not owned memory
|
||||
EXPECT_DEATH(ASAN_UNPOISON_MEMORY_REGION(array, 121),
|
||||
kInvalidUnpoisonMessage);
|
||||
EXPECT_DEATH(ASAN_UNPOISON_MEMORY_REGION(array - 1, 120),
|
||||
kInvalidUnpoisonMessage);
|
||||
|
||||
ASAN_POISON_MEMORY_REGION(array, 120);
|
||||
// Try to poison not owned memory.
|
||||
EXPECT_DEATH(ASAN_POISON_MEMORY_REGION(array, 121), kInvalidPoisonMessage);
|
||||
EXPECT_DEATH(ASAN_POISON_MEMORY_REGION(array - 1, 120),
|
||||
kInvalidPoisonMessage);
|
||||
free(array);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
extern "C" {
|
||||
void CFAllocatorDefaultDoubleFree();
|
||||
void *CFAllocatorDefaultDoubleFree(void *unused);
|
||||
void CFAllocatorSystemDefaultDoubleFree();
|
||||
void CFAllocatorMallocDoubleFree();
|
||||
void CFAllocatorMallocZoneDoubleFree();
|
||||
@ -13,4 +13,7 @@ extern "C" {
|
||||
void TestGCDSourceEvent();
|
||||
void TestGCDSourceCancel();
|
||||
void TestGCDGroupAsync();
|
||||
void TestOOBNSObjects();
|
||||
void TestNSURLDeallocation();
|
||||
void TestPassCFMemoryToAnotherThread();
|
||||
}
|
||||
|
@ -7,11 +7,14 @@
|
||||
|
||||
#import <CoreFoundation/CFBase.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
|
||||
void CFAllocatorDefaultDoubleFree() {
|
||||
// This is a (void*)(void*) function so it can be passed to pthread_create.
|
||||
void *CFAllocatorDefaultDoubleFree(void *unused) {
|
||||
void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0);
|
||||
CFAllocatorDeallocate(kCFAllocatorDefault, mem);
|
||||
CFAllocatorDeallocate(kCFAllocatorDefault, mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CFAllocatorSystemDefaultDoubleFree() {
|
||||
@ -32,6 +35,10 @@ void CFAllocatorMallocZoneDoubleFree() {
|
||||
CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
void access_memory(char *a) {
|
||||
*a = 0;
|
||||
}
|
||||
|
||||
// Test the +load instrumentation.
|
||||
// Because the +load methods are invoked before anything else is initialized,
|
||||
@ -51,7 +58,7 @@ char kStartupStr[] =
|
||||
|
||||
+(void) load {
|
||||
for (int i = 0; i < strlen(kStartupStr); i++) {
|
||||
volatile char ch = kStartupStr[i]; // make sure no optimizations occur.
|
||||
access_memory(&kStartupStr[i]); // make sure no optimizations occur.
|
||||
}
|
||||
// Don't print anything here not to interfere with the death tests.
|
||||
}
|
||||
@ -66,7 +73,7 @@ void worker_do_alloc(int size) {
|
||||
|
||||
void worker_do_crash(int size) {
|
||||
char * volatile mem = malloc(size);
|
||||
mem[size] = 0; // BOOM
|
||||
access_memory(&mem[size]); // BOOM
|
||||
free(mem);
|
||||
}
|
||||
|
||||
@ -162,7 +169,7 @@ void TestGCDSourceEvent() {
|
||||
dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
|
||||
char * volatile mem = malloc(10);
|
||||
dispatch_source_set_event_handler(timer, ^{
|
||||
mem[10] = 1;
|
||||
access_memory(&mem[10]);
|
||||
});
|
||||
dispatch_resume(timer);
|
||||
sleep(2);
|
||||
@ -186,7 +193,7 @@ void TestGCDSourceCancel() {
|
||||
dispatch_source_cancel(timer);
|
||||
});
|
||||
dispatch_source_set_cancel_handler(timer, ^{
|
||||
mem[10] = 1;
|
||||
access_memory(&mem[10]);
|
||||
});
|
||||
dispatch_resume(timer);
|
||||
sleep(2);
|
||||
@ -197,7 +204,34 @@ void TestGCDGroupAsync() {
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
char * volatile mem = malloc(10);
|
||||
dispatch_group_async(group, queue, ^{
|
||||
mem[10] = 1;
|
||||
access_memory(&mem[10]);
|
||||
});
|
||||
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
||||
}
|
||||
|
||||
@interface FixedArray : NSObject {
|
||||
int items[10];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation FixedArray
|
||||
-(int) access: (int)index {
|
||||
return items[index];
|
||||
}
|
||||
@end
|
||||
|
||||
void TestOOBNSObjects() {
|
||||
id anObject = [FixedArray new];
|
||||
[anObject access:1];
|
||||
[anObject access:11];
|
||||
[anObject release];
|
||||
}
|
||||
|
||||
void TestNSURLDeallocation() {
|
||||
NSURL *base =
|
||||
[[NSURL alloc] initWithString:@"file://localhost/Users/glider/Library/"];
|
||||
volatile NSURL *u =
|
||||
[[NSURL alloc] initWithString:@"Saved Application State"
|
||||
relativeToURL:base];
|
||||
[u release];
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_noinst_test.cc ------------*- C++ -*-===//
|
||||
//===-- asan_noinst_test.cc ----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -21,17 +21,18 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
#include <string.h> // for memset()
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
// Simple stand-alone pseudorandom number generator.
|
||||
// Current algorithm is ANSI C linear congruential PRNG.
|
||||
static inline uint32_t my_rand(uint32_t* state) {
|
||||
static inline u32 my_rand(u32* state) {
|
||||
return (*state = *state * 1103515245 + 12345) >> 16;
|
||||
}
|
||||
|
||||
static uint32_t global_seed = 0;
|
||||
static u32 global_seed = 0;
|
||||
|
||||
|
||||
TEST(AddressSanitizer, InternalSimpleDeathTest) {
|
||||
@ -39,7 +40,7 @@ TEST(AddressSanitizer, InternalSimpleDeathTest) {
|
||||
}
|
||||
|
||||
static void MallocStress(size_t n) {
|
||||
uint32_t seed = my_rand(&global_seed);
|
||||
u32 seed = my_rand(&global_seed);
|
||||
__asan::AsanStackTrace stack1;
|
||||
stack1.trace[0] = 0xa123;
|
||||
stack1.trace[1] = 0xa456;
|
||||
@ -92,16 +93,16 @@ TEST(AddressSanitizer, NoInstMallocTest) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void PrintShadow(const char *tag, uintptr_t ptr, size_t size) {
|
||||
static void PrintShadow(const char *tag, uptr ptr, size_t size) {
|
||||
fprintf(stderr, "%s shadow: %lx size % 3ld: ", tag, (long)ptr, (long)size);
|
||||
uintptr_t prev_shadow = 0;
|
||||
for (intptr_t i = -32; i < (intptr_t)size + 32; i++) {
|
||||
uintptr_t shadow = __asan::MemToShadow(ptr + i);
|
||||
if (i == 0 || i == (intptr_t)size)
|
||||
uptr prev_shadow = 0;
|
||||
for (sptr i = -32; i < (sptr)size + 32; i++) {
|
||||
uptr shadow = __asan::MemToShadow(ptr + i);
|
||||
if (i == 0 || i == (sptr)size)
|
||||
fprintf(stderr, ".");
|
||||
if (shadow != prev_shadow) {
|
||||
prev_shadow = shadow;
|
||||
fprintf(stderr, "%02x", (int)*(uint8_t*)shadow);
|
||||
fprintf(stderr, "%02x", (int)*(u8*)shadow);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
@ -110,13 +111,13 @@ static void PrintShadow(const char *tag, uintptr_t ptr, size_t size) {
|
||||
TEST(AddressSanitizer, DISABLED_InternalPrintShadow) {
|
||||
for (size_t size = 1; size <= 513; size++) {
|
||||
char *ptr = new char[size];
|
||||
PrintShadow("m", (uintptr_t)ptr, size);
|
||||
PrintShadow("m", (uptr)ptr, size);
|
||||
delete [] ptr;
|
||||
PrintShadow("f", (uintptr_t)ptr, size);
|
||||
PrintShadow("f", (uptr)ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
static uintptr_t pc_array[] = {
|
||||
static uptr pc_array[] = {
|
||||
#if __WORDSIZE == 64
|
||||
0x7effbf756068ULL,
|
||||
0x7effbf75e5abULL,
|
||||
@ -207,19 +208,20 @@ static uintptr_t pc_array[] = {
|
||||
};
|
||||
|
||||
void CompressStackTraceTest(size_t n_iter) {
|
||||
uint32_t seed = my_rand(&global_seed);
|
||||
u32 seed = my_rand(&global_seed);
|
||||
const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array);
|
||||
uint32_t compressed[2 * kNumPcs];
|
||||
u32 compressed[2 * kNumPcs];
|
||||
|
||||
for (size_t iter = 0; iter < n_iter; iter++) {
|
||||
std::random_shuffle(pc_array, pc_array + kNumPcs);
|
||||
__asan::AsanStackTrace stack0, stack1;
|
||||
stack0.CopyFrom(pc_array, kNumPcs);
|
||||
stack0.size = std::max((size_t)1, (size_t)my_rand(&seed) % stack0.size);
|
||||
stack0.size = std::max((size_t)1, (size_t)(my_rand(&seed) % stack0.size));
|
||||
size_t compress_size =
|
||||
std::max((size_t)2, (size_t)my_rand(&seed) % (2 * kNumPcs));
|
||||
size_t n_frames =
|
||||
__asan::AsanStackTrace::CompressStack(&stack0, compressed, compress_size);
|
||||
Ident(n_frames);
|
||||
assert(n_frames <= stack0.size);
|
||||
__asan::AsanStackTrace::UncompressStack(&stack1, compressed, compress_size);
|
||||
assert(stack1.size == n_frames);
|
||||
@ -235,7 +237,7 @@ TEST(AddressSanitizer, CompressStackTraceTest) {
|
||||
|
||||
void CompressStackTraceBenchmark(size_t n_iter) {
|
||||
const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array);
|
||||
uint32_t compressed[2 * kNumPcs];
|
||||
u32 compressed[2 * kNumPcs];
|
||||
std::random_shuffle(pc_array, pc_array + kNumPcs);
|
||||
|
||||
__asan::AsanStackTrace stack0;
|
||||
@ -274,7 +276,8 @@ TEST(AddressSanitizer, QuarantineTest) {
|
||||
}
|
||||
|
||||
void *ThreadedQuarantineTestWorker(void *unused) {
|
||||
uint32_t seed = my_rand(&global_seed);
|
||||
(void)unused;
|
||||
u32 seed = my_rand(&global_seed);
|
||||
__asan::AsanStackTrace stack;
|
||||
stack.trace[0] = 0x890;
|
||||
stack.size = 1;
|
||||
@ -301,6 +304,7 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) {
|
||||
}
|
||||
|
||||
void *ThreadedOneSizeMallocStress(void *unused) {
|
||||
(void)unused;
|
||||
__asan::AsanStackTrace stack;
|
||||
stack.trace[0] = 0x890;
|
||||
stack.size = 1;
|
||||
@ -327,3 +331,371 @@ TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) {
|
||||
pthread_join(t[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, MemsetWildAddressTest) {
|
||||
typedef void*(*memset_p)(void*, int, size_t);
|
||||
// Prevent inlining of memset().
|
||||
volatile memset_p libc_memset = (memset_p)memset;
|
||||
EXPECT_DEATH(libc_memset((void*)(kLowShadowBeg + kPageSize), 0, 100),
|
||||
"unknown-crash.*low shadow");
|
||||
EXPECT_DEATH(libc_memset((void*)(kShadowGapBeg + kPageSize), 0, 100),
|
||||
"unknown-crash.*shadow gap");
|
||||
EXPECT_DEATH(libc_memset((void*)(kHighShadowBeg + kPageSize), 0, 100),
|
||||
"unknown-crash.*high shadow");
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) {
|
||||
EXPECT_EQ(1U, __asan_get_estimated_allocated_size(0));
|
||||
const size_t sizes[] = { 1, 30, 1<<30 };
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
EXPECT_EQ(sizes[i], __asan_get_estimated_allocated_size(sizes[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kGetAllocatedSizeErrorMsg =
|
||||
"attempting to call __asan_get_allocated_size()";
|
||||
|
||||
TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) {
|
||||
const size_t kArraySize = 100;
|
||||
char *array = Ident((char*)malloc(kArraySize));
|
||||
int *int_ptr = Ident(new int);
|
||||
|
||||
// Allocated memory is owned by allocator. Allocated size should be
|
||||
// equal to requested size.
|
||||
EXPECT_EQ(true, __asan_get_ownership(array));
|
||||
EXPECT_EQ(kArraySize, __asan_get_allocated_size(array));
|
||||
EXPECT_EQ(true, __asan_get_ownership(int_ptr));
|
||||
EXPECT_EQ(sizeof(int), __asan_get_allocated_size(int_ptr));
|
||||
|
||||
// We cannot call GetAllocatedSize from the memory we didn't map,
|
||||
// and from the interior pointers (not returned by previous malloc).
|
||||
void *wild_addr = (void*)0x1;
|
||||
EXPECT_EQ(false, __asan_get_ownership(wild_addr));
|
||||
EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg);
|
||||
EXPECT_EQ(false, __asan_get_ownership(array + kArraySize / 2));
|
||||
EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2),
|
||||
kGetAllocatedSizeErrorMsg);
|
||||
|
||||
// NULL is not owned, but is a valid argument for __asan_get_allocated_size().
|
||||
EXPECT_EQ(false, __asan_get_ownership(NULL));
|
||||
EXPECT_EQ(0U, __asan_get_allocated_size(NULL));
|
||||
|
||||
// When memory is freed, it's not owned, and call to GetAllocatedSize
|
||||
// is forbidden.
|
||||
free(array);
|
||||
EXPECT_EQ(false, __asan_get_ownership(array));
|
||||
EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg);
|
||||
|
||||
delete int_ptr;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) {
|
||||
size_t before_malloc, after_malloc, after_free;
|
||||
char *array;
|
||||
const size_t kMallocSize = 100;
|
||||
before_malloc = __asan_get_current_allocated_bytes();
|
||||
|
||||
array = Ident((char*)malloc(kMallocSize));
|
||||
after_malloc = __asan_get_current_allocated_bytes();
|
||||
EXPECT_EQ(before_malloc + kMallocSize, after_malloc);
|
||||
|
||||
free(array);
|
||||
after_free = __asan_get_current_allocated_bytes();
|
||||
EXPECT_EQ(before_malloc, after_free);
|
||||
}
|
||||
|
||||
static void DoDoubleFree() {
|
||||
int *x = Ident(new int);
|
||||
delete Ident(x);
|
||||
delete Ident(x);
|
||||
}
|
||||
|
||||
// This test is run in a separate process, so that large malloced
|
||||
// chunk won't remain in the free lists after the test.
|
||||
// Note: use ASSERT_* instead of EXPECT_* here.
|
||||
static void RunGetHeapSizeTestAndDie() {
|
||||
size_t old_heap_size, new_heap_size, heap_growth;
|
||||
// We unlikely have have chunk of this size in free list.
|
||||
static const size_t kLargeMallocSize = 1 << 29; // 512M
|
||||
old_heap_size = __asan_get_heap_size();
|
||||
fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
|
||||
free(Ident(malloc(kLargeMallocSize)));
|
||||
new_heap_size = __asan_get_heap_size();
|
||||
heap_growth = new_heap_size - old_heap_size;
|
||||
fprintf(stderr, "heap growth after first malloc: %zu\n", heap_growth);
|
||||
ASSERT_GE(heap_growth, kLargeMallocSize);
|
||||
ASSERT_LE(heap_growth, 2 * kLargeMallocSize);
|
||||
|
||||
// Now large chunk should fall into free list, and can be
|
||||
// allocated without increasing heap size.
|
||||
old_heap_size = new_heap_size;
|
||||
free(Ident(malloc(kLargeMallocSize)));
|
||||
heap_growth = __asan_get_heap_size() - old_heap_size;
|
||||
fprintf(stderr, "heap growth after second malloc: %zu\n", heap_growth);
|
||||
ASSERT_LT(heap_growth, kLargeMallocSize);
|
||||
|
||||
// Test passed. Now die with expected double-free.
|
||||
DoDoubleFree();
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, GetHeapSizeTest) {
|
||||
EXPECT_DEATH(RunGetHeapSizeTestAndDie(), "double-free");
|
||||
}
|
||||
|
||||
// Note: use ASSERT_* instead of EXPECT_* here.
|
||||
static void DoLargeMallocForGetFreeBytesTestAndDie() {
|
||||
size_t old_free_bytes, new_free_bytes;
|
||||
static const size_t kLargeMallocSize = 1 << 29; // 512M
|
||||
// If we malloc and free a large memory chunk, it will not fall
|
||||
// into quarantine and will be available for future requests.
|
||||
old_free_bytes = __asan_get_free_bytes();
|
||||
fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
|
||||
fprintf(stderr, "free bytes before malloc: %zu\n", old_free_bytes);
|
||||
free(Ident(malloc(kLargeMallocSize)));
|
||||
new_free_bytes = __asan_get_free_bytes();
|
||||
fprintf(stderr, "free bytes after malloc and free: %zu\n", new_free_bytes);
|
||||
ASSERT_GE(new_free_bytes, old_free_bytes + kLargeMallocSize);
|
||||
// Test passed.
|
||||
DoDoubleFree();
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, GetFreeBytesTest) {
|
||||
static const size_t kNumOfChunks = 100;
|
||||
static const size_t kChunkSize = 100;
|
||||
char *chunks[kNumOfChunks];
|
||||
size_t i;
|
||||
size_t old_free_bytes, new_free_bytes;
|
||||
// Allocate a small chunk. Now allocator probably has a lot of these
|
||||
// chunks to fulfill future requests. So, future requests will decrease
|
||||
// the number of free bytes.
|
||||
chunks[0] = Ident((char*)malloc(kChunkSize));
|
||||
old_free_bytes = __asan_get_free_bytes();
|
||||
for (i = 1; i < kNumOfChunks; i++) {
|
||||
chunks[i] = Ident((char*)malloc(kChunkSize));
|
||||
new_free_bytes = __asan_get_free_bytes();
|
||||
EXPECT_LT(new_free_bytes, old_free_bytes);
|
||||
old_free_bytes = new_free_bytes;
|
||||
}
|
||||
EXPECT_DEATH(DoLargeMallocForGetFreeBytesTestAndDie(), "double-free");
|
||||
}
|
||||
|
||||
static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<20, 357};
|
||||
static const size_t kManyThreadsIterations = 250;
|
||||
static const size_t kManyThreadsNumThreads = (__WORDSIZE == 32) ? 40 : 200;
|
||||
|
||||
void *ManyThreadsWithStatsWorker(void *arg) {
|
||||
(void)arg;
|
||||
for (size_t iter = 0; iter < kManyThreadsIterations; iter++) {
|
||||
for (size_t size_index = 0; size_index < 4; size_index++) {
|
||||
free(Ident(malloc(kManyThreadsMallocSizes[size_index])));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) {
|
||||
size_t before_test, after_test, i;
|
||||
pthread_t threads[kManyThreadsNumThreads];
|
||||
before_test = __asan_get_current_allocated_bytes();
|
||||
for (i = 0; i < kManyThreadsNumThreads; i++) {
|
||||
pthread_create(&threads[i], 0,
|
||||
(void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i);
|
||||
}
|
||||
for (i = 0; i < kManyThreadsNumThreads; i++) {
|
||||
pthread_join(threads[i], 0);
|
||||
}
|
||||
after_test = __asan_get_current_allocated_bytes();
|
||||
// ASan stats also reflect memory usage of internal ASan RTL structs,
|
||||
// so we can't check for equality here.
|
||||
EXPECT_LT(after_test, before_test + (1UL<<20));
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, ExitCode) {
|
||||
int original_exit_code = __asan_set_error_exit_code(7);
|
||||
EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), "");
|
||||
EXPECT_EQ(7, __asan_set_error_exit_code(8));
|
||||
EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), "");
|
||||
EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code));
|
||||
EXPECT_EXIT(DoDoubleFree(),
|
||||
::testing::ExitedWithCode(original_exit_code), "");
|
||||
}
|
||||
|
||||
static void MyDeathCallback() {
|
||||
fprintf(stderr, "MyDeathCallback\n");
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, DeathCallbackTest) {
|
||||
__asan_set_death_callback(MyDeathCallback);
|
||||
EXPECT_DEATH(DoDoubleFree(), "MyDeathCallback");
|
||||
__asan_set_death_callback(NULL);
|
||||
}
|
||||
|
||||
static const char* kUseAfterPoisonErrorMessage = "use-after-poison";
|
||||
|
||||
#define GOOD_ACCESS(ptr, offset) \
|
||||
EXPECT_FALSE(__asan::AddressIsPoisoned((uptr)(ptr + offset)))
|
||||
|
||||
#define BAD_ACCESS(ptr, offset) \
|
||||
EXPECT_TRUE(__asan::AddressIsPoisoned((uptr)(ptr + offset)))
|
||||
|
||||
TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) {
|
||||
char *array = Ident((char*)malloc(120));
|
||||
// poison array[40..80)
|
||||
__asan_poison_memory_region(array + 40, 40);
|
||||
GOOD_ACCESS(array, 39);
|
||||
GOOD_ACCESS(array, 80);
|
||||
BAD_ACCESS(array, 40);
|
||||
BAD_ACCESS(array, 60);
|
||||
BAD_ACCESS(array, 79);
|
||||
EXPECT_DEATH(__asan_report_error(0, 0, 0, (uptr)(array + 40), true, 1),
|
||||
kUseAfterPoisonErrorMessage);
|
||||
__asan_unpoison_memory_region(array + 40, 40);
|
||||
// access previously poisoned memory.
|
||||
GOOD_ACCESS(array, 40);
|
||||
GOOD_ACCESS(array, 79);
|
||||
free(array);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) {
|
||||
char *array = Ident((char*)malloc(120));
|
||||
// Poison [0..40) and [80..120)
|
||||
__asan_poison_memory_region(array, 40);
|
||||
__asan_poison_memory_region(array + 80, 40);
|
||||
BAD_ACCESS(array, 20);
|
||||
GOOD_ACCESS(array, 60);
|
||||
BAD_ACCESS(array, 100);
|
||||
// Poison whole array - [0..120)
|
||||
__asan_poison_memory_region(array, 120);
|
||||
BAD_ACCESS(array, 60);
|
||||
// Unpoison [24..96)
|
||||
__asan_unpoison_memory_region(array + 24, 72);
|
||||
BAD_ACCESS(array, 23);
|
||||
GOOD_ACCESS(array, 24);
|
||||
GOOD_ACCESS(array, 60);
|
||||
GOOD_ACCESS(array, 95);
|
||||
BAD_ACCESS(array, 96);
|
||||
free(array);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) {
|
||||
// Vector of capacity 20
|
||||
char *vec = Ident((char*)malloc(20));
|
||||
__asan_poison_memory_region(vec, 20);
|
||||
for (size_t i = 0; i < 7; i++) {
|
||||
// Simulate push_back.
|
||||
__asan_unpoison_memory_region(vec + i, 1);
|
||||
GOOD_ACCESS(vec, i);
|
||||
BAD_ACCESS(vec, i + 1);
|
||||
}
|
||||
for (size_t i = 7; i > 0; i--) {
|
||||
// Simulate pop_back.
|
||||
__asan_poison_memory_region(vec + i - 1, 1);
|
||||
BAD_ACCESS(vec, i - 1);
|
||||
if (i > 1) GOOD_ACCESS(vec, i - 2);
|
||||
}
|
||||
free(vec);
|
||||
}
|
||||
|
||||
// Make sure that each aligned block of size "2^granularity" doesn't have
|
||||
// "true" value before "false" value.
|
||||
static void MakeShadowValid(bool *shadow, int length, int granularity) {
|
||||
bool can_be_poisoned = true;
|
||||
for (int i = length - 1; i >= 0; i--) {
|
||||
if (!shadow[i])
|
||||
can_be_poisoned = false;
|
||||
if (!can_be_poisoned)
|
||||
shadow[i] = false;
|
||||
if (i % (1 << granularity) == 0) {
|
||||
can_be_poisoned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, PoisoningStressTest) {
|
||||
const size_t kSize = 24;
|
||||
bool expected[kSize];
|
||||
char *arr = Ident((char*)malloc(kSize));
|
||||
for (size_t l1 = 0; l1 < kSize; l1++) {
|
||||
for (size_t s1 = 1; l1 + s1 <= kSize; s1++) {
|
||||
for (size_t l2 = 0; l2 < kSize; l2++) {
|
||||
for (size_t s2 = 1; l2 + s2 <= kSize; s2++) {
|
||||
// Poison [l1, l1+s1), [l2, l2+s2) and check result.
|
||||
__asan_unpoison_memory_region(arr, kSize);
|
||||
__asan_poison_memory_region(arr + l1, s1);
|
||||
__asan_poison_memory_region(arr + l2, s2);
|
||||
memset(expected, false, kSize);
|
||||
memset(expected + l1, true, s1);
|
||||
MakeShadowValid(expected, kSize, /*granularity*/ 3);
|
||||
memset(expected + l2, true, s2);
|
||||
MakeShadowValid(expected, kSize, /*granularity*/ 3);
|
||||
for (size_t i = 0; i < kSize; i++) {
|
||||
ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i));
|
||||
}
|
||||
// Unpoison [l1, l1+s1) and [l2, l2+s2) and check result.
|
||||
__asan_poison_memory_region(arr, kSize);
|
||||
__asan_unpoison_memory_region(arr + l1, s1);
|
||||
__asan_unpoison_memory_region(arr + l2, s2);
|
||||
memset(expected, true, kSize);
|
||||
memset(expected + l1, false, s1);
|
||||
MakeShadowValid(expected, kSize, /*granularity*/ 3);
|
||||
memset(expected + l2, false, s2);
|
||||
MakeShadowValid(expected, kSize, /*granularity*/ 3);
|
||||
for (size_t i = 0; i < kSize; i++) {
|
||||
ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *kInvalidPoisonMessage = "invalid-poison-memory-range";
|
||||
static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range";
|
||||
|
||||
TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) {
|
||||
char *array = Ident((char*)malloc(120));
|
||||
__asan_unpoison_memory_region(array, 120);
|
||||
// Try to unpoison not owned memory
|
||||
EXPECT_DEATH(__asan_unpoison_memory_region(array, 121),
|
||||
kInvalidUnpoisonMessage);
|
||||
EXPECT_DEATH(__asan_unpoison_memory_region(array - 1, 120),
|
||||
kInvalidUnpoisonMessage);
|
||||
|
||||
__asan_poison_memory_region(array, 120);
|
||||
// Try to poison not owned memory.
|
||||
EXPECT_DEATH(__asan_poison_memory_region(array, 121), kInvalidPoisonMessage);
|
||||
EXPECT_DEATH(__asan_poison_memory_region(array - 1, 120),
|
||||
kInvalidPoisonMessage);
|
||||
free(array);
|
||||
}
|
||||
|
||||
static void ErrorReportCallbackOneToZ(const char *report) {
|
||||
write(2, "ABCDEF", 6);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, SetErrorReportCallbackTest) {
|
||||
__asan_set_error_report_callback(ErrorReportCallbackOneToZ);
|
||||
EXPECT_DEATH(__asan_report_error(0, 0, 0, 0, true, 1), "ABCDEF");
|
||||
__asan_set_error_report_callback(NULL);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, GetOwnershipStressTest) {
|
||||
std::vector<char *> pointers;
|
||||
std::vector<size_t> sizes;
|
||||
const size_t kNumMallocs =
|
||||
(__WORDSIZE <= 32 || ASAN_LOW_MEMORY) ? 1 << 10 : 1 << 14;
|
||||
for (size_t i = 0; i < kNumMallocs; i++) {
|
||||
size_t size = i * 100 + 1;
|
||||
pointers.push_back((char*)malloc(size));
|
||||
sizes.push_back(size);
|
||||
}
|
||||
for (size_t i = 0; i < 4000000; i++) {
|
||||
EXPECT_FALSE(__asan_get_ownership(&pointers));
|
||||
EXPECT_FALSE(__asan_get_ownership((void*)0x1234));
|
||||
size_t idx = i % kNumMallocs;
|
||||
EXPECT_TRUE(__asan_get_ownership(pointers[idx]));
|
||||
EXPECT_EQ(sizes[idx], __asan_get_allocated_size(pointers[idx]));
|
||||
}
|
||||
for (size_t i = 0, n = pointers.size(); i < n; i++)
|
||||
free(pointers[i]);
|
||||
}
|
||||
|
32
lib/asan/tests/asan_racy_double_free_test.cc
Normal file
32
lib/asan/tests/asan_racy_double_free_test.cc
Normal file
@ -0,0 +1,32 @@
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
const int N = 1000;
|
||||
void *x[N];
|
||||
|
||||
void *Thread1(void *unused) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
fprintf(stderr, "%s %d\n", __FUNCTION__, i);
|
||||
free(x[i]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *Thread2(void *unused) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
fprintf(stderr, "%s %d\n", __FUNCTION__, i);
|
||||
free(x[i]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
for (int i = 0; i < N; i++)
|
||||
x[i] = malloc(128);
|
||||
pthread_t t[2];
|
||||
pthread_create(&t[0], 0, Thread1, 0);
|
||||
pthread_create(&t[1], 0, Thread2, 0);
|
||||
pthread_join(t[0], 0);
|
||||
pthread_join(t[1], 0);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_test.cc ------------*- C++ -*-===//
|
||||
//===-- asan_test.cc ----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -20,7 +20,7 @@
|
||||
#include <setjmp.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(__i386__) or defined(__x86_64__)
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
@ -29,13 +29,10 @@
|
||||
|
||||
#ifndef __APPLE__
|
||||
#include <malloc.h>
|
||||
#endif // __APPLE__
|
||||
|
||||
#ifdef __APPLE__
|
||||
static bool APPLE = true;
|
||||
#else
|
||||
static bool APPLE = false;
|
||||
#endif
|
||||
#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_*
|
||||
#include <CoreFoundation/CFString.h>
|
||||
#endif // __APPLE__
|
||||
|
||||
#if ASAN_HAS_EXCEPTIONS
|
||||
# define ASAN_THROW(x) throw (x)
|
||||
@ -61,99 +58,14 @@ static inline uint32_t my_rand(uint32_t* state) {
|
||||
|
||||
static uint32_t global_seed = 0;
|
||||
|
||||
class ObjdumpOfMyself {
|
||||
public:
|
||||
explicit ObjdumpOfMyself(const string &binary) {
|
||||
is_correct = true;
|
||||
string objdump_name = APPLE ? "gobjdump" : "objdump";
|
||||
string prog = objdump_name + " -d " + binary;
|
||||
// TODO(glider): popen() succeeds even if the file does not exist.
|
||||
FILE *pipe = popen(prog.c_str(), "r");
|
||||
string objdump;
|
||||
if (pipe) {
|
||||
const int kBuffSize = 4096;
|
||||
char buff[kBuffSize+1];
|
||||
int read_bytes;
|
||||
while ((read_bytes = fread(buff, 1, kBuffSize, pipe)) > 0) {
|
||||
buff[read_bytes] = 0;
|
||||
objdump.append(buff);
|
||||
}
|
||||
pclose(pipe);
|
||||
} else {
|
||||
is_correct = false;
|
||||
}
|
||||
// cut the objdump into functions
|
||||
string fn, next_fn;
|
||||
size_t next_start;
|
||||
for (size_t start = fn_start(objdump, 0, &fn);
|
||||
start != string::npos;
|
||||
start = next_start, fn = next_fn) {
|
||||
next_start = fn_start(objdump, start, &next_fn);
|
||||
// fprintf(stderr, "start: %d next_start = %d fn: %s\n",
|
||||
// (int)start, (int)next_start, fn.c_str());
|
||||
// Mac OS adds the "_" prefix to function names.
|
||||
if (fn.find(APPLE ? "_Disasm" : "Disasm") == string::npos) {
|
||||
continue;
|
||||
}
|
||||
string fn_body = objdump.substr(start, next_start - start);
|
||||
// fprintf(stderr, "%s:\n%s", fn.c_str(), fn_body.c_str());
|
||||
functions_[fn] = fn_body;
|
||||
}
|
||||
}
|
||||
|
||||
string &GetFuncDisasm(const string &fn) {
|
||||
return functions_[fn];
|
||||
}
|
||||
|
||||
int CountInsnInFunc(const string &fn, const vector<string> &insns) {
|
||||
// Mac OS adds the "_" prefix to function names.
|
||||
string fn_ref = APPLE ? "_" + fn : fn;
|
||||
const string &disasm = GetFuncDisasm(fn_ref);
|
||||
if (disasm.empty()) return -1;
|
||||
size_t counter = 0;
|
||||
for (size_t i = 0; i < insns.size(); i++) {
|
||||
size_t pos = 0;
|
||||
while ((pos = disasm.find(insns[i], pos)) != string::npos) {
|
||||
counter++;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
bool IsCorrect() { return is_correct; }
|
||||
|
||||
private:
|
||||
size_t fn_start(const string &objdump, size_t start_pos, string *fn) {
|
||||
size_t pos = objdump.find(">:\n", start_pos);
|
||||
if (pos == string::npos)
|
||||
return string::npos;
|
||||
size_t beg = pos;
|
||||
while (beg > 0 && objdump[beg - 1] != '<')
|
||||
beg--;
|
||||
*fn = objdump.substr(beg, pos - beg);
|
||||
return pos + 3;
|
||||
}
|
||||
|
||||
map<string, string> functions_;
|
||||
bool is_correct;
|
||||
};
|
||||
|
||||
static ObjdumpOfMyself *objdump_of_myself() {
|
||||
static ObjdumpOfMyself *o = new ObjdumpOfMyself(progname);
|
||||
return o;
|
||||
}
|
||||
|
||||
const size_t kLargeMalloc = 1 << 24;
|
||||
|
||||
template<class T>
|
||||
__attribute__((noinline))
|
||||
void asan_write(T *a) {
|
||||
template<typename T>
|
||||
NOINLINE void asan_write(T *a) {
|
||||
*a = 0;
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
void asan_write_sized_aligned(uint8_t *p, size_t size) {
|
||||
NOINLINE void asan_write_sized_aligned(uint8_t *p, size_t size) {
|
||||
EXPECT_EQ(0, ((uintptr_t)p % size));
|
||||
if (size == 1) asan_write((uint8_t*)p);
|
||||
else if (size == 2) asan_write((uint16_t*)p);
|
||||
@ -161,45 +73,41 @@ void asan_write_sized_aligned(uint8_t *p, size_t size) {
|
||||
else if (size == 8) asan_write((uint64_t*)p);
|
||||
}
|
||||
|
||||
__attribute__((noinline)) void *malloc_fff(size_t size) {
|
||||
NOINLINE void *malloc_fff(size_t size) {
|
||||
void *res = malloc/**/(size); break_optimization(0); return res;}
|
||||
__attribute__((noinline)) void *malloc_eee(size_t size) {
|
||||
NOINLINE void *malloc_eee(size_t size) {
|
||||
void *res = malloc_fff(size); break_optimization(0); return res;}
|
||||
__attribute__((noinline)) void *malloc_ddd(size_t size) {
|
||||
NOINLINE void *malloc_ddd(size_t size) {
|
||||
void *res = malloc_eee(size); break_optimization(0); return res;}
|
||||
__attribute__((noinline)) void *malloc_ccc(size_t size) {
|
||||
NOINLINE void *malloc_ccc(size_t size) {
|
||||
void *res = malloc_ddd(size); break_optimization(0); return res;}
|
||||
__attribute__((noinline)) void *malloc_bbb(size_t size) {
|
||||
NOINLINE void *malloc_bbb(size_t size) {
|
||||
void *res = malloc_ccc(size); break_optimization(0); return res;}
|
||||
__attribute__((noinline)) void *malloc_aaa(size_t size) {
|
||||
NOINLINE void *malloc_aaa(size_t size) {
|
||||
void *res = malloc_bbb(size); break_optimization(0); return res;}
|
||||
|
||||
#ifndef __APPLE__
|
||||
__attribute__((noinline)) void *memalign_fff(size_t alignment, size_t size) {
|
||||
NOINLINE void *memalign_fff(size_t alignment, size_t size) {
|
||||
void *res = memalign/**/(alignment, size); break_optimization(0); return res;}
|
||||
__attribute__((noinline)) void *memalign_eee(size_t alignment, size_t size) {
|
||||
NOINLINE void *memalign_eee(size_t alignment, size_t size) {
|
||||
void *res = memalign_fff(alignment, size); break_optimization(0); return res;}
|
||||
__attribute__((noinline)) void *memalign_ddd(size_t alignment, size_t size) {
|
||||
NOINLINE void *memalign_ddd(size_t alignment, size_t size) {
|
||||
void *res = memalign_eee(alignment, size); break_optimization(0); return res;}
|
||||
__attribute__((noinline)) void *memalign_ccc(size_t alignment, size_t size) {
|
||||
NOINLINE void *memalign_ccc(size_t alignment, size_t size) {
|
||||
void *res = memalign_ddd(alignment, size); break_optimization(0); return res;}
|
||||
__attribute__((noinline)) void *memalign_bbb(size_t alignment, size_t size) {
|
||||
NOINLINE void *memalign_bbb(size_t alignment, size_t size) {
|
||||
void *res = memalign_ccc(alignment, size); break_optimization(0); return res;}
|
||||
__attribute__((noinline)) void *memalign_aaa(size_t alignment, size_t size) {
|
||||
NOINLINE void *memalign_aaa(size_t alignment, size_t size) {
|
||||
void *res = memalign_bbb(alignment, size); break_optimization(0); return res;}
|
||||
#endif // __APPLE__
|
||||
|
||||
|
||||
__attribute__((noinline))
|
||||
void free_ccc(void *p) { free(p); break_optimization(0);}
|
||||
__attribute__((noinline))
|
||||
void free_bbb(void *p) { free_ccc(p); break_optimization(0);}
|
||||
__attribute__((noinline))
|
||||
void free_aaa(void *p) { free_bbb(p); break_optimization(0);}
|
||||
NOINLINE void free_ccc(void *p) { free(p); break_optimization(0);}
|
||||
NOINLINE void free_bbb(void *p) { free_ccc(p); break_optimization(0);}
|
||||
NOINLINE void free_aaa(void *p) { free_bbb(p); break_optimization(0);}
|
||||
|
||||
template<class T>
|
||||
__attribute__((noinline))
|
||||
void oob_test(int size, int off) {
|
||||
template<typename T>
|
||||
NOINLINE void oob_test(int size, int off) {
|
||||
char *p = (char*)malloc_aaa(size);
|
||||
// fprintf(stderr, "writing %d byte(s) into [%p,%p) with offset %d\n",
|
||||
// sizeof(T), p, p + size, off);
|
||||
@ -208,9 +116,8 @@ void oob_test(int size, int off) {
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
__attribute__((noinline))
|
||||
void uaf_test(int size, int off) {
|
||||
template<typename T>
|
||||
NOINLINE void uaf_test(int size, int off) {
|
||||
char *p = (char *)malloc_aaa(size);
|
||||
free_aaa(p);
|
||||
for (int i = 1; i < 100; i++)
|
||||
@ -255,13 +162,15 @@ TEST(AddressSanitizer, VariousMallocsTest) {
|
||||
*c = 0;
|
||||
delete c;
|
||||
|
||||
#ifndef __APPLE__
|
||||
#if !defined(__APPLE__) && !defined(ANDROID)
|
||||
// fprintf(stderr, "posix_memalign\n");
|
||||
int *pm;
|
||||
int pm_res = posix_memalign((void**)&pm, kPageSize, kPageSize);
|
||||
EXPECT_EQ(0, pm_res);
|
||||
free(pm);
|
||||
#endif
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
int *ma = (int*)memalign(kPageSize, kPageSize);
|
||||
EXPECT_EQ(0, (uintptr_t)ma % kPageSize);
|
||||
ma[123] = 0;
|
||||
@ -295,48 +204,6 @@ TEST(AddressSanitizer, PvallocTest) {
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
void NoOpSignalHandler(int unused) {
|
||||
fprintf(stderr, "NoOpSignalHandler (should not happen). Aborting\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
void NoOpSigaction(int, siginfo_t *siginfo, void *context) {
|
||||
fprintf(stderr, "NoOpSigaction (should not happen). Aborting\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, SignalTest) {
|
||||
signal(SIGSEGV, NoOpSignalHandler);
|
||||
signal(SIGILL, NoOpSignalHandler);
|
||||
// If asan did not intercept sigaction NoOpSigaction will fire.
|
||||
char *x = Ident((char*)malloc(5));
|
||||
EXPECT_DEATH(x[6]++, "is located 1 bytes to the right");
|
||||
free(Ident(x));
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, SigactionTest) {
|
||||
{
|
||||
struct sigaction sigact;
|
||||
memset(&sigact, 0, sizeof(sigact));
|
||||
sigact.sa_sigaction = NoOpSigaction;;
|
||||
sigact.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGSEGV, &sigact, 0);
|
||||
}
|
||||
|
||||
{
|
||||
struct sigaction sigact;
|
||||
memset(&sigact, 0, sizeof(sigact));
|
||||
sigact.sa_sigaction = NoOpSigaction;;
|
||||
sigact.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGILL, &sigact, 0);
|
||||
}
|
||||
|
||||
// If asan did not intercept sigaction NoOpSigaction will fire.
|
||||
char *x = Ident((char*)malloc(5));
|
||||
EXPECT_DEATH(x[6]++, "is located 1 bytes to the right");
|
||||
free(Ident(x));
|
||||
}
|
||||
|
||||
void *TSDWorker(void *test_key) {
|
||||
if (test_key) {
|
||||
pthread_setspecific(*(pthread_key_t*)test_key, (void*)0xfeedface);
|
||||
@ -367,7 +234,7 @@ TEST(AddressSanitizer, DISABLED_TSDTest) {
|
||||
pthread_key_delete(test_key);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
template<typename T>
|
||||
void OOBTest() {
|
||||
char expected_str[100];
|
||||
for (int size = sizeof(T); size < 20; size += 5) {
|
||||
@ -531,7 +398,7 @@ static void MallocStress(size_t n) {
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, MallocStressTest) {
|
||||
MallocStress(200000);
|
||||
MallocStress((ASAN_LOW_MEMORY) ? 20000 : 200000);
|
||||
}
|
||||
|
||||
static void TestLargeMalloc(size_t size) {
|
||||
@ -546,24 +413,29 @@ TEST(AddressSanitizer, LargeMallocTest) {
|
||||
}
|
||||
}
|
||||
|
||||
#if ASAN_LOW_MEMORY != 1
|
||||
TEST(AddressSanitizer, HugeMallocTest) {
|
||||
#ifdef __APPLE__
|
||||
// It was empirically found out that 1215 megabytes is the maximum amount of
|
||||
// memory available to the process under AddressSanitizer on Darwin.
|
||||
// memory available to the process under AddressSanitizer on 32-bit Mac 10.6.
|
||||
// 32-bit Mac 10.7 gives even less (< 1G).
|
||||
// (the libSystem malloc() allows allocating up to 2300 megabytes without
|
||||
// ASan).
|
||||
size_t n_megs = __WORDSIZE == 32 ? 1200 : 4100;
|
||||
size_t n_megs = __WORDSIZE == 32 ? 500 : 4100;
|
||||
#else
|
||||
size_t n_megs = __WORDSIZE == 32 ? 2600 : 4100;
|
||||
#endif
|
||||
TestLargeMalloc(n_megs << 20);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(AddressSanitizer, ThreadedMallocStressTest) {
|
||||
const int kNumThreads = 4;
|
||||
const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000;
|
||||
pthread_t t[kNumThreads];
|
||||
for (int i = 0; i < kNumThreads; i++) {
|
||||
pthread_create(&t[i], 0, (void* (*)(void *x))MallocStress, (void*)100000);
|
||||
pthread_create(&t[i], 0, (void* (*)(void *x))MallocStress,
|
||||
(void*)kNumIterations);
|
||||
}
|
||||
for (int i = 0; i < kNumThreads; i++) {
|
||||
pthread_join(t[i], 0);
|
||||
@ -601,6 +473,25 @@ TEST(AddressSanitizer, ReallocTest) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
static const char *kMallocUsableSizeErrorMsg =
|
||||
"AddressSanitizer attempting to call malloc_usable_size()";
|
||||
|
||||
TEST(AddressSanitizer, MallocUsableSizeTest) {
|
||||
const size_t kArraySize = 100;
|
||||
char *array = Ident((char*)malloc(kArraySize));
|
||||
int *int_ptr = Ident(new int);
|
||||
EXPECT_EQ(0, malloc_usable_size(NULL));
|
||||
EXPECT_EQ(kArraySize, malloc_usable_size(array));
|
||||
EXPECT_EQ(sizeof(int), malloc_usable_size(int_ptr));
|
||||
EXPECT_DEATH(malloc_usable_size((void*)0x123), kMallocUsableSizeErrorMsg);
|
||||
EXPECT_DEATH(malloc_usable_size(array + kArraySize / 2),
|
||||
kMallocUsableSizeErrorMsg);
|
||||
free(array);
|
||||
EXPECT_DEATH(malloc_usable_size(array), kMallocUsableSizeErrorMsg);
|
||||
}
|
||||
#endif
|
||||
|
||||
void WrongFree() {
|
||||
int *x = (int*)malloc(100 * sizeof(int));
|
||||
// Use the allocated memory, otherwise Clang will optimize it out.
|
||||
@ -623,12 +514,15 @@ void DoubleFree() {
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, DoubleFreeTest) {
|
||||
EXPECT_DEATH(DoubleFree(), "ERROR: AddressSanitizer attempting double-free");
|
||||
EXPECT_DEATH(DoubleFree(), ASAN_PCRE_DOTALL
|
||||
"ERROR: AddressSanitizer attempting double-free"
|
||||
".*is located 0 bytes inside of 400-byte region"
|
||||
".*freed by thread T0 here"
|
||||
".*previously allocated by thread T0 here");
|
||||
}
|
||||
|
||||
template<int kSize>
|
||||
__attribute__((noinline))
|
||||
void SizedStackTest() {
|
||||
NOINLINE void SizedStackTest() {
|
||||
char a[kSize];
|
||||
char *A = Ident((char*)&a);
|
||||
for (size_t i = 0; i < kSize; i++)
|
||||
@ -669,8 +563,7 @@ TEST(AddressSanitizer, ManyStackObjectsTest) {
|
||||
EXPECT_DEATH(Ident(ZZZ)[-1] = 0, ASAN_PCRE_DOTALL "XXX.*YYY.*ZZZ");
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
static void Frame0(int frame, char *a, char *b, char *c) {
|
||||
NOINLINE static void Frame0(int frame, char *a, char *b, char *c) {
|
||||
char d[4] = {0};
|
||||
char *D = Ident(d);
|
||||
switch (frame) {
|
||||
@ -680,15 +573,15 @@ static void Frame0(int frame, char *a, char *b, char *c) {
|
||||
case 0: D[5]++; break;
|
||||
}
|
||||
}
|
||||
__attribute__((noinline)) static void Frame1(int frame, char *a, char *b) {
|
||||
NOINLINE static void Frame1(int frame, char *a, char *b) {
|
||||
char c[4] = {0}; Frame0(frame, a, b, c);
|
||||
break_optimization(0);
|
||||
}
|
||||
__attribute__((noinline)) static void Frame2(int frame, char *a) {
|
||||
NOINLINE static void Frame2(int frame, char *a) {
|
||||
char b[4] = {0}; Frame1(frame, a, b);
|
||||
break_optimization(0);
|
||||
}
|
||||
__attribute__((noinline)) static void Frame3(int frame) {
|
||||
NOINLINE static void Frame3(int frame) {
|
||||
char a[4] = {0}; Frame2(frame, a);
|
||||
break_optimization(0);
|
||||
}
|
||||
@ -706,8 +599,7 @@ TEST(AddressSanitizer, GuiltyStackFrame3Test) {
|
||||
EXPECT_DEATH(Frame3(3), "located .*in frame <.*Frame3");
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
void LongJmpFunc1(jmp_buf buf) {
|
||||
NOINLINE void LongJmpFunc1(jmp_buf buf) {
|
||||
// create three red zones for these two stack objects.
|
||||
int a;
|
||||
int b;
|
||||
@ -718,8 +610,7 @@ void LongJmpFunc1(jmp_buf buf) {
|
||||
longjmp(buf, 1);
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
void UnderscopeLongJmpFunc1(jmp_buf buf) {
|
||||
NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) {
|
||||
// create three red zones for these two stack objects.
|
||||
int a;
|
||||
int b;
|
||||
@ -730,8 +621,7 @@ void UnderscopeLongJmpFunc1(jmp_buf buf) {
|
||||
_longjmp(buf, 1);
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
void SigLongJmpFunc1(sigjmp_buf buf) {
|
||||
NOINLINE void SigLongJmpFunc1(sigjmp_buf buf) {
|
||||
// create three red zones for these two stack objects.
|
||||
int a;
|
||||
int b;
|
||||
@ -743,8 +633,7 @@ void SigLongJmpFunc1(sigjmp_buf buf) {
|
||||
}
|
||||
|
||||
|
||||
__attribute__((noinline))
|
||||
void TouchStackFunc() {
|
||||
NOINLINE void TouchStackFunc() {
|
||||
int a[100]; // long array will intersect with redzones from LongJmpFunc1.
|
||||
int *A = Ident(a);
|
||||
for (int i = 0; i < 100; i++)
|
||||
@ -780,8 +669,7 @@ TEST(AddressSanitizer, SigLongJmpTest) {
|
||||
}
|
||||
|
||||
#ifdef __EXCEPTIONS
|
||||
__attribute__((noinline))
|
||||
void ThrowFunc() {
|
||||
NOINLINE void ThrowFunc() {
|
||||
// create three red zones for these two stack objects.
|
||||
int a;
|
||||
int b;
|
||||
@ -828,7 +716,7 @@ TEST(AddressSanitizer, ThreadStackReuseTest) {
|
||||
pthread_join(t, 0);
|
||||
}
|
||||
|
||||
#if defined(__i386__) or defined(__x86_64__)
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
TEST(AddressSanitizer, Store128Test) {
|
||||
char *a = Ident((char*)malloc(Ident(12)));
|
||||
char *p = a;
|
||||
@ -860,7 +748,7 @@ static string LeftOOBErrorMessage(int oob_distance) {
|
||||
return string(expected_str);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
template<typename T>
|
||||
void MemSetOOBTestTemplate(size_t length) {
|
||||
if (length == 0) return;
|
||||
size_t size = Ident(sizeof(T) * length);
|
||||
@ -917,7 +805,7 @@ TEST(AddressSanitizer, MemSetOOBTest) {
|
||||
}
|
||||
|
||||
// Same test for memcpy and memmove functions
|
||||
template <class T, class M>
|
||||
template <typename T, class M>
|
||||
void MemTransferOOBTestTemplate(size_t length) {
|
||||
if (length == 0) return;
|
||||
size_t size = Ident(sizeof(T) * length);
|
||||
@ -1051,11 +939,14 @@ TEST(AddressSanitizer, StrLenOOBTest) {
|
||||
free(heap_string);
|
||||
}
|
||||
|
||||
static inline char* MallocAndMemsetString(size_t size) {
|
||||
static inline char* MallocAndMemsetString(size_t size, char ch) {
|
||||
char *s = Ident((char*)malloc(size));
|
||||
memset(s, 'z', size);
|
||||
memset(s, ch, size);
|
||||
return s;
|
||||
}
|
||||
static inline char* MallocAndMemsetString(size_t size) {
|
||||
return MallocAndMemsetString(size, 'z');
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
TEST(AddressSanitizer, StrNLenOOBTest) {
|
||||
@ -1355,6 +1246,47 @@ TEST(AddressSanitizer, StrCatOOBTest) {
|
||||
EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0));
|
||||
// length of "to" is just enough.
|
||||
strcat(to, from + 1);
|
||||
|
||||
free(to);
|
||||
free(from);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, StrNCatOOBTest) {
|
||||
size_t to_size = Ident(100);
|
||||
char *to = MallocAndMemsetString(to_size);
|
||||
to[0] = '\0';
|
||||
size_t from_size = Ident(20);
|
||||
char *from = MallocAndMemsetString(from_size);
|
||||
// Normal strncat calls.
|
||||
strncat(to, from, 0);
|
||||
strncat(to, from, from_size);
|
||||
from[from_size - 1] = '\0';
|
||||
strncat(to, from, 2 * from_size);
|
||||
// Catenating empty string is not an error.
|
||||
strncat(to - 1, from, 0);
|
||||
strncat(to, from + from_size - 1, 10);
|
||||
// One of arguments points to not allocated memory.
|
||||
EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBErrorMessage(1));
|
||||
EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBErrorMessage(1));
|
||||
EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBErrorMessage(0));
|
||||
EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBErrorMessage(0));
|
||||
|
||||
memset(from, 'z', from_size);
|
||||
memset(to, 'z', to_size);
|
||||
to[0] = '\0';
|
||||
// "from" is too short.
|
||||
EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBErrorMessage(0));
|
||||
// "to" is not zero-terminated.
|
||||
EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBErrorMessage(0));
|
||||
// "to" is too short to fit "from".
|
||||
to[0] = 'z';
|
||||
to[to_size - from_size + 1] = '\0';
|
||||
EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBErrorMessage(0));
|
||||
// "to" is just enough.
|
||||
strncat(to, from, from_size - 2);
|
||||
|
||||
free(to);
|
||||
free(from);
|
||||
}
|
||||
|
||||
static string OverlapErrorMessage(const string &func) {
|
||||
@ -1365,14 +1297,22 @@ TEST(AddressSanitizer, StrArgsOverlapTest) {
|
||||
size_t size = Ident(100);
|
||||
char *str = Ident((char*)malloc(size));
|
||||
|
||||
// Do not check memcpy() on OS X 10.7 and later, where it actually aliases
|
||||
// memmove().
|
||||
#if !defined(__APPLE__) || !defined(MAC_OS_X_VERSION_10_7) || \
|
||||
(MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
|
||||
// Check "memcpy". Use Ident() to avoid inlining.
|
||||
memset(str, 'z', size);
|
||||
Ident(memcpy)(str + 1, str + 11, 10);
|
||||
Ident(memcpy)(str, str, 0);
|
||||
EXPECT_DEATH(Ident(memcpy)(str, str + 14, 15), OverlapErrorMessage("memcpy"));
|
||||
EXPECT_DEATH(Ident(memcpy)(str + 14, str, 15), OverlapErrorMessage("memcpy"));
|
||||
EXPECT_DEATH(Ident(memcpy)(str + 20, str + 20, 1),
|
||||
OverlapErrorMessage("memcpy"));
|
||||
#endif
|
||||
|
||||
// We do not treat memcpy with to==from as a bug.
|
||||
// See http://llvm.org/bugs/show_bug.cgi?id=11763.
|
||||
// EXPECT_DEATH(Ident(memcpy)(str + 20, str + 20, 1),
|
||||
// OverlapErrorMessage("memcpy"));
|
||||
|
||||
// Check "strcpy".
|
||||
memset(str, 'z', size);
|
||||
@ -1403,9 +1343,117 @@ TEST(AddressSanitizer, StrArgsOverlapTest) {
|
||||
EXPECT_DEATH(strcat(str + 9, str), OverlapErrorMessage("strcat"));
|
||||
EXPECT_DEATH(strcat(str + 10, str), OverlapErrorMessage("strcat"));
|
||||
|
||||
// Check "strncat".
|
||||
memset(str, 'z', size);
|
||||
str[10] = '\0';
|
||||
strncat(str, str + 10, 10); // from is empty
|
||||
strncat(str, str + 11, 10);
|
||||
str[10] = '\0';
|
||||
str[20] = '\0';
|
||||
strncat(str + 5, str, 5);
|
||||
str[10] = '\0';
|
||||
EXPECT_DEATH(strncat(str + 5, str, 6), OverlapErrorMessage("strncat"));
|
||||
EXPECT_DEATH(strncat(str, str + 9, 10), OverlapErrorMessage("strncat"));
|
||||
|
||||
free(str);
|
||||
}
|
||||
|
||||
void CallAtoi(const char *nptr) {
|
||||
Ident(atoi(nptr));
|
||||
}
|
||||
void CallAtol(const char *nptr) {
|
||||
Ident(atol(nptr));
|
||||
}
|
||||
void CallAtoll(const char *nptr) {
|
||||
Ident(atoll(nptr));
|
||||
}
|
||||
typedef void(*PointerToCallAtoi)(const char*);
|
||||
|
||||
void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
|
||||
char *array = MallocAndMemsetString(10, '1');
|
||||
// Invalid pointer to the string.
|
||||
EXPECT_DEATH(Atoi(array + 11), RightOOBErrorMessage(1));
|
||||
EXPECT_DEATH(Atoi(array - 1), LeftOOBErrorMessage(1));
|
||||
// Die if a buffer doesn't have terminating NULL.
|
||||
EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0));
|
||||
// Make last symbol a terminating NULL or other non-digit.
|
||||
array[9] = '\0';
|
||||
Atoi(array);
|
||||
array[9] = 'a';
|
||||
Atoi(array);
|
||||
Atoi(array + 9);
|
||||
// Sometimes we need to detect overflow if no digits are found.
|
||||
memset(array, ' ', 10);
|
||||
EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0));
|
||||
array[9] = '-';
|
||||
EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0));
|
||||
EXPECT_DEATH(Atoi(array + 9), RightOOBErrorMessage(0));
|
||||
array[8] = '-';
|
||||
Atoi(array);
|
||||
delete array;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, AtoiAndFriendsOOBTest) {
|
||||
RunAtoiOOBTest(&CallAtoi);
|
||||
RunAtoiOOBTest(&CallAtol);
|
||||
RunAtoiOOBTest(&CallAtoll);
|
||||
}
|
||||
|
||||
void CallStrtol(const char *nptr, char **endptr, int base) {
|
||||
Ident(strtol(nptr, endptr, base));
|
||||
}
|
||||
void CallStrtoll(const char *nptr, char **endptr, int base) {
|
||||
Ident(strtoll(nptr, endptr, base));
|
||||
}
|
||||
typedef void(*PointerToCallStrtol)(const char*, char**, int);
|
||||
|
||||
void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
|
||||
char *array = MallocAndMemsetString(3);
|
||||
char *endptr = NULL;
|
||||
array[0] = '1';
|
||||
array[1] = '2';
|
||||
array[2] = '3';
|
||||
// Invalid pointer to the string.
|
||||
EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBErrorMessage(0));
|
||||
EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBErrorMessage(1));
|
||||
// Buffer overflow if there is no terminating null (depends on base).
|
||||
Strtol(array, &endptr, 3);
|
||||
EXPECT_EQ(array + 2, endptr);
|
||||
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
|
||||
array[2] = 'z';
|
||||
Strtol(array, &endptr, 35);
|
||||
EXPECT_EQ(array + 2, endptr);
|
||||
EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBErrorMessage(0));
|
||||
// Add terminating zero to get rid of overflow.
|
||||
array[2] = '\0';
|
||||
Strtol(array, NULL, 36);
|
||||
// Don't check for overflow if base is invalid.
|
||||
Strtol(array - 1, NULL, -1);
|
||||
Strtol(array + 3, NULL, 1);
|
||||
// Sometimes we need to detect overflow if no digits are found.
|
||||
array[0] = array[1] = array[2] = ' ';
|
||||
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
|
||||
array[2] = '+';
|
||||
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
|
||||
array[2] = '-';
|
||||
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
|
||||
array[1] = '+';
|
||||
Strtol(array, NULL, 0);
|
||||
array[1] = array[2] = 'z';
|
||||
Strtol(array, &endptr, 0);
|
||||
EXPECT_EQ(array, endptr);
|
||||
Strtol(array + 2, NULL, 0);
|
||||
EXPECT_EQ(array, endptr);
|
||||
delete array;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, StrtollOOBTest) {
|
||||
RunStrtolOOBTest(&CallStrtoll);
|
||||
}
|
||||
TEST(AddressSanitizer, StrtolOOBTest) {
|
||||
RunStrtolOOBTest(&CallStrtol);
|
||||
}
|
||||
|
||||
// At the moment we instrument memcpy/memove/memset calls at compile time so we
|
||||
// can't handle OOB error if these functions are called by pointer, see disabled
|
||||
// MemIntrinsicCallByPointerTest below
|
||||
@ -1446,8 +1494,7 @@ TEST(AddressSanitizer, DISABLED_MemIntrinsicUnalignedAccessTest) {
|
||||
// TODO(samsonov): Add a test with malloc(0)
|
||||
// TODO(samsonov): Add tests for str* and mem* functions.
|
||||
|
||||
__attribute__((noinline))
|
||||
static int LargeFunction(bool do_bad_access) {
|
||||
NOINLINE static int LargeFunction(bool do_bad_access) {
|
||||
int *x = new int[100];
|
||||
x[0]++;
|
||||
x[1]++;
|
||||
@ -1544,8 +1591,7 @@ TEST(AddressSanitizer, ShadowGapTest) {
|
||||
#endif // ASAN_NEEDS_SEGV
|
||||
|
||||
extern "C" {
|
||||
__attribute__((noinline))
|
||||
static void UseThenFreeThenUse() {
|
||||
NOINLINE static void UseThenFreeThenUse() {
|
||||
char *x = Ident((char*)malloc(8));
|
||||
*x = 1;
|
||||
free_aaa(x);
|
||||
@ -1561,76 +1607,6 @@ TEST(AddressSanitizer, StrDupTest) {
|
||||
free(strdup(Ident("123")));
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, ObjdumpTest) {
|
||||
ObjdumpOfMyself *o = objdump_of_myself();
|
||||
EXPECT_TRUE(o->IsCorrect());
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
__attribute__((noinline))
|
||||
static void DisasmSimple() {
|
||||
Ident(0);
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
static void DisasmParamWrite(int *a) {
|
||||
*a = 1;
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
static void DisasmParamInc(int *a) {
|
||||
(*a)++;
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
static void DisasmParamReadIfWrite(int *a) {
|
||||
if (*a)
|
||||
*a = 1;
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
static int DisasmParamIfReadWrite(int *a, int cond) {
|
||||
int res = 0;
|
||||
if (cond)
|
||||
res = *a;
|
||||
*a = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int GLOBAL;
|
||||
|
||||
__attribute__((noinline))
|
||||
static void DisasmWriteGlob() {
|
||||
GLOBAL = 1;
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
TEST(AddressSanitizer, DisasmTest) {
|
||||
int a;
|
||||
DisasmSimple();
|
||||
DisasmParamWrite(&a);
|
||||
DisasmParamInc(&a);
|
||||
Ident(DisasmWriteGlob)();
|
||||
DisasmParamReadIfWrite(&a);
|
||||
|
||||
a = 7;
|
||||
EXPECT_EQ(7, DisasmParamIfReadWrite(&a, Ident(1)));
|
||||
EXPECT_EQ(0, a);
|
||||
|
||||
ObjdumpOfMyself *o = objdump_of_myself();
|
||||
vector<string> insns;
|
||||
insns.push_back("ud2");
|
||||
insns.push_back("__asan_report_");
|
||||
EXPECT_EQ(0, o->CountInsnInFunc("DisasmSimple", insns));
|
||||
EXPECT_EQ(1, o->CountInsnInFunc("DisasmParamWrite", insns));
|
||||
EXPECT_EQ(1, o->CountInsnInFunc("DisasmParamInc", insns));
|
||||
EXPECT_EQ(0, o->CountInsnInFunc("DisasmWriteGlob", insns));
|
||||
|
||||
// TODO(kcc): implement these (needs just one __asan_report).
|
||||
EXPECT_EQ(2, o->CountInsnInFunc("DisasmParamReadIfWrite", insns));
|
||||
EXPECT_EQ(2, o->CountInsnInFunc("DisasmParamIfReadWrite", insns));
|
||||
}
|
||||
|
||||
// Currently we create and poison redzone at right of global variables.
|
||||
char glob5[5];
|
||||
static char static110[110];
|
||||
@ -1718,8 +1694,7 @@ TEST(AddressSanitizer, LocalReferenceReturnTest) {
|
||||
#endif
|
||||
|
||||
template <int kSize>
|
||||
__attribute__((noinline))
|
||||
static void FuncWithStack() {
|
||||
NOINLINE static void FuncWithStack() {
|
||||
char x[kSize];
|
||||
Ident(x)[0] = 0;
|
||||
Ident(x)[kSize-1] = 0;
|
||||
@ -1758,9 +1733,21 @@ TEST(AddressSanitizer, ThreadedStressStackReuseTest) {
|
||||
}
|
||||
}
|
||||
|
||||
static void *PthreadExit(void *a) {
|
||||
pthread_exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, PthreadExitTest) {
|
||||
pthread_t t;
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
pthread_create(&t, 0, PthreadExit, 0);
|
||||
pthread_join(t, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __EXCEPTIONS
|
||||
__attribute__((noinline))
|
||||
static void StackReuseAndException() {
|
||||
NOINLINE static void StackReuseAndException() {
|
||||
int large_stack[1000];
|
||||
Ident(large_stack);
|
||||
ASAN_THROW(1);
|
||||
@ -1784,6 +1771,28 @@ TEST(AddressSanitizer, MlockTest) {
|
||||
EXPECT_EQ(0, munlock((void*)0x987, 0x654));
|
||||
}
|
||||
|
||||
struct LargeStruct {
|
||||
int foo[100];
|
||||
};
|
||||
|
||||
// Test for bug http://llvm.org/bugs/show_bug.cgi?id=11763.
|
||||
// Struct copy should not cause asan warning even if lhs == rhs.
|
||||
TEST(AddressSanitizer, LargeStructCopyTest) {
|
||||
LargeStruct a;
|
||||
*Ident(&a) = *Ident(&a);
|
||||
}
|
||||
|
||||
__attribute__((no_address_safety_analysis))
|
||||
static void NoAddressSafety() {
|
||||
char *foo = new char[10];
|
||||
Ident(foo)[10] = 0;
|
||||
delete [] foo;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, AttributeNoAddressSafetyTest) {
|
||||
Ident(NoAddressSafety)();
|
||||
}
|
||||
|
||||
// ------------------ demo tests; run each one-by-one -------------
|
||||
// e.g. --gtest_filter=*DemoOOBLeftHigh --gtest_also_run_disabled_tests
|
||||
TEST(AddressSanitizer, DISABLED_DemoThreadedTest) {
|
||||
@ -1873,23 +1882,75 @@ TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) {
|
||||
}
|
||||
}
|
||||
|
||||
// http://code.google.com/p/address-sanitizer/issues/detail?id=66
|
||||
TEST(AddressSanitizer, BufferOverflowAfterManyFrees) {
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
delete [] (Ident(new char [8644]));
|
||||
}
|
||||
char *x = new char[8192];
|
||||
EXPECT_DEATH(x[Ident(8192)] = 0, "AddressSanitizer heap-buffer-overflow");
|
||||
delete [] Ident(x);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "asan_mac_test.h"
|
||||
// TODO(glider): figure out whether we still need these tests. Is it correct
|
||||
// to intercept CFAllocator?
|
||||
TEST(AddressSanitizerMac, DISABLED_CFAllocatorDefaultDoubleFree) {
|
||||
TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) {
|
||||
EXPECT_DEATH(
|
||||
CFAllocatorDefaultDoubleFree(),
|
||||
CFAllocatorDefaultDoubleFree(NULL),
|
||||
"attempting double-free");
|
||||
}
|
||||
|
||||
void CFAllocator_DoubleFreeOnPthread() {
|
||||
pthread_t child;
|
||||
pthread_create(&child, NULL, CFAllocatorDefaultDoubleFree, NULL);
|
||||
pthread_join(child, NULL); // Shouldn't be reached.
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) {
|
||||
EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free");
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void *GLOB;
|
||||
|
||||
void *CFAllocatorAllocateToGlob(void *unused) {
|
||||
GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *CFAllocatorDeallocateFromGlob(void *unused) {
|
||||
char *p = (char*)GLOB;
|
||||
p[100] = 'A'; // ASan should report an error here.
|
||||
CFAllocatorDeallocate(NULL, GLOB);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CFAllocator_PassMemoryToAnotherThread() {
|
||||
pthread_t th1, th2;
|
||||
pthread_create(&th1, NULL, CFAllocatorAllocateToGlob, NULL);
|
||||
pthread_join(th1, NULL);
|
||||
pthread_create(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL);
|
||||
pthread_join(th2, NULL);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) {
|
||||
EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(),
|
||||
"heap-buffer-overflow");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO(glider): figure out whether we still need these tests. Is it correct
|
||||
// to intercept the non-default CFAllocators?
|
||||
TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) {
|
||||
EXPECT_DEATH(
|
||||
CFAllocatorSystemDefaultDoubleFree(),
|
||||
"attempting double-free");
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocDoubleFree) {
|
||||
// We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan.
|
||||
TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) {
|
||||
EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free");
|
||||
}
|
||||
|
||||
@ -2012,8 +2073,37 @@ TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) {
|
||||
pthread_join(th, NULL);
|
||||
pthread_key_delete(test_key);
|
||||
}
|
||||
|
||||
// Test that CFStringCreateCopy does not copy constant strings.
|
||||
TEST(AddressSanitizerMac, CFStringCreateCopy) {
|
||||
CFStringRef str = CFSTR("Hello world!\n");
|
||||
CFStringRef str2 = CFStringCreateCopy(0, str);
|
||||
EXPECT_EQ(str, str2);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, NSObjectOOB) {
|
||||
// Make sure that our allocators are used for NSObjects.
|
||||
EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow");
|
||||
}
|
||||
|
||||
// Make sure that correct pointer is passed to free() when deallocating a
|
||||
// NSURL object.
|
||||
// See http://code.google.com/p/address-sanitizer/issues/detail?id=70.
|
||||
TEST(AddressSanitizerMac, NSURLDeallocation) {
|
||||
TestNSURLDeallocation();
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
// Test that instrumentation of stack allocations takes into account
|
||||
// AllocSize of a type, and not its StoreSize (16 vs 10 bytes for long double).
|
||||
// See http://llvm.org/bugs/show_bug.cgi?id=12047 for more details.
|
||||
TEST(AddressSanitizer, LongDoubleNegativeTest) {
|
||||
long double a, b;
|
||||
static long double c;
|
||||
memcpy(Ident(&a), Ident(&b), sizeof(long double));
|
||||
memcpy(Ident(&c), Ident(&b), sizeof(long double));
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
progname = argv[0];
|
||||
testing::GTEST_FLAG(death_test_style) = "threadsafe";
|
||||
|
@ -39,6 +39,10 @@ using std::map;
|
||||
# error "please define ASAN_NEEDS_SEGV"
|
||||
#endif
|
||||
|
||||
#ifndef ASAN_LOW_MEMORY
|
||||
#define ASAN_LOW_MEMORY 0
|
||||
#endif
|
||||
|
||||
#define ASAN_PCRE_DOTALL ""
|
||||
|
||||
#endif // ASAN_TEST_CONFIG_H
|
||||
|
@ -14,13 +14,39 @@
|
||||
#ifndef ASAN_TEST_UTILS_H
|
||||
#define ASAN_TEST_UTILS_H
|
||||
|
||||
#if defined(_WIN32)
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
typedef __int8 int8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef __int64 int64_t;
|
||||
# define NOINLINE __declspec(noinline)
|
||||
#else // defined(_WIN32)
|
||||
# define NOINLINE __attribute__((noinline))
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#if !defined(__has_feature)
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __WORDSIZE
|
||||
#if __LP64__ || defined(_WIN64)
|
||||
#define __WORDSIZE 64
|
||||
#else
|
||||
#define __WORDSIZE 32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Make the compiler think that something is going on there.
|
||||
extern "C" void break_optimization(void *);
|
||||
|
||||
// This function returns its parameter but in such a way that compiler
|
||||
// can not prove it.
|
||||
template<class T>
|
||||
__attribute__((noinline))
|
||||
NOINLINE
|
||||
static T Ident(T t) {
|
||||
T ret = t;
|
||||
break_optimization(&ret);
|
||||
|
@ -1 +0,0 @@
|
||||
PASS
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user