Import compiler-rt r160957.

This commit is contained in:
Andrew Turner 2012-07-30 10:58:13 +00:00
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
356 changed files with 23042 additions and 5322 deletions

View File

@ -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 )

View File

@ -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()

View File

@ -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

View File

@ -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);

View File

@ -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 *);

View File

@ -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)));

View File

@ -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 *);

View File

@ -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 )

View File

@ -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 )

View File

@ -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)

View File

@ -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 )

View File

@ -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}

View File

@ -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()

View File

@ -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

View File

@ -12,10 +12,10 @@
* ===----------------------------------------------------------------------===
*/
#if __x86_64
#include "int_lib.h"
#if __x86_64
/* Returns: absolute value */
/* Effects: aborts if abs(x) < 0 */

View File

@ -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);

View File

@ -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);

View File

@ -12,10 +12,10 @@
* ===----------------------------------------------------------------------===
*/
#if __x86_64
#include "int_lib.h"
#if __x86_64
/* Returns: a + b */
/* Effects: aborts if a + b overflows */

View File

27
lib/arm/aeabi_idivmod.S Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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()

View File

@ -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=%)

View File

@ -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-*

View File

@ -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

View File

@ -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
View 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

View File

@ -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]);
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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__

View File

@ -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__

View File

@ -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__

View File

@ -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
View 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

View File

@ -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

View 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; }

View File

@ -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
View 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_

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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) { }

View File

@ -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

View File

@ -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_;

View File

@ -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;
}

View File

@ -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
View 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

View 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

View 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];
}

View 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;
}

View File

@ -1,4 +1,4 @@
//===-- asan_rtl.cc ------------*- C++ -*-===//
//===----------- dlclose-test-so.cc -----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//

View File

@ -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;
}

View File

@ -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;
}

View 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}}

View 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

View 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

View 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

View 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}}

View 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;
}

View 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}}

View File

@ -1,4 +1,4 @@
//===-- asan_rtl.cc ------------*- C++ -*-===//
//===----------- shared-lib-test-so.cc --------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//

View File

@ -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}}

View 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>}}

View File

@ -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) {

View 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}}

View 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

View File

@ -4,3 +4,6 @@ int main() {
free(x);
return x[5];
}
// CHECK: heap-use-after-free
// CHECKSLEEP: Sleeping for 1 second

View 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}}

View File

@ -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()

View File

@ -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.

View File

@ -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_

View File

@ -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));
}

View File

@ -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_ */

View 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})

View File

@ -1,4 +1,4 @@
//===-- asan_benchmarks_test.cc ------------*- C++ -*-===//
//===-- asan_benchmarks_test.cc ----------------------===//
//
// The LLVM Compiler Infrastructure
//

View File

@ -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;
}

View File

@ -1,4 +1,4 @@
//===-- asan_globals_test.cc ------------*- C++ -*-===//
//===-- asan_globals_test.cc ----------------------===//
//
// The LLVM Compiler Infrastructure
//

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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];
}

View File

@ -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]);
}

View 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);
}

View File

@ -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";

View File

@ -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

View File

@ -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);

View File

@ -1 +0,0 @@
PASS

Some files were not shown because too many files have changed in this diff Show More