Vendor import of compiler-rt trunk r290819:

https://llvm.org/svn/llvm-project/compiler-rt/trunk@290819
This commit is contained in:
Dimitry Andric 2017-01-02 19:18:27 +00:00
parent 0230fcf22f
commit 316d58822d
588 changed files with 17337 additions and 7478 deletions

View file

@ -1,14 +1,14 @@
# CMake build for CompilerRT.
#
# This build assumes that CompilerRT is checked out into the
# 'projects/compiler-rt' inside of an LLVM tree.
# 'projects/compiler-rt' or 'runtimes/compiler-rt' inside of an LLVM tree.
# Standalone build system for CompilerRT is not yet ready.
#
# An important constraint of the build is that it only produces libraries
# based on the ability of the host toolchain to target various platforms.
# Check if compiler-rt is built as a standalone project.
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR OR COMPILER_RT_STANDALONE_BUILD)
project(CompilerRT C CXX ASM)
set(COMPILER_RT_STANDALONE_BUILD TRUE)
endif()
@ -32,42 +32,17 @@ list(INSERT CMAKE_MODULE_PATH 0
)
include(base-config-ix)
include(CompilerRTUtils)
option(COMPILER_RT_BUILD_BUILTINS "Build builtins" ON)
mark_as_advanced(COMPILER_RT_BUILD_BUILTINS)
option(COMPILER_RT_BUILD_SANITIZERS "Build sanitizers" ON)
mark_as_advanced(COMPILER_RT_BUILD_SANITIZERS)
option(COMPILER_RT_BUILD_XRAY "Build xray" ON)
mark_as_advanced(COMPILER_RT_BUILD_XRAY)
if (COMPILER_RT_STANDALONE_BUILD)
if (NOT LLVM_CONFIG_PATH)
find_program(LLVM_CONFIG_PATH "llvm-config"
DOC "Path to llvm-config binary")
if (NOT LLVM_CONFIG_PATH)
message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH")
endif()
endif()
execute_process(
COMMAND ${LLVM_CONFIG_PATH} "--obj-root" "--bindir" "--libdir" "--src-root"
RESULT_VARIABLE HAD_ERROR
OUTPUT_VARIABLE CONFIG_OUTPUT)
if (HAD_ERROR)
message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}")
endif()
string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" CONFIG_OUTPUT ${CONFIG_OUTPUT})
list(GET CONFIG_OUTPUT 0 LLVM_BINARY_DIR)
list(GET CONFIG_OUTPUT 1 LLVM_TOOLS_BINARY_DIR)
list(GET CONFIG_OUTPUT 2 LLVM_LIBRARY_DIR)
list(GET CONFIG_OUTPUT 3 LLVM_MAIN_SRC_DIR)
# Make use of LLVM CMake modules.
file(TO_CMAKE_PATH ${LLVM_BINARY_DIR} LLVM_BINARY_DIR_CMAKE_STYLE)
set(LLVM_CMAKE_PATH "${LLVM_BINARY_DIR_CMAKE_STYLE}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm")
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}")
# Get some LLVM variables from LLVMConfig.
include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake")
set(LLVM_LIBRARY_OUTPUT_INTDIR
${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
load_llvm_config()
# Find Python interpreter.
set(Python_ADDITIONAL_VERSIONS 2.7 2.6 2.5)
@ -86,29 +61,10 @@ if (COMPILER_RT_STANDALONE_BUILD)
set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit")
endif()
set(COMPILER_RT_DEFAULT_TARGET_TRIPLE ${TARGET_TRIPLE} CACHE STRING
"Default triple for which compiler-rt runtimes will be built.")
if(DEFINED COMPILER_RT_TEST_TARGET_TRIPLE)
# Backwards compatibility: this variable used to be called
# COMPILER_RT_TEST_TARGET_TRIPLE.
set(COMPILER_RT_DEFAULT_TARGET_TRIPLE ${COMPILER_RT_TEST_TARGET_TRIPLE})
endif()
string(REPLACE "-" ";" TARGET_TRIPLE_LIST ${COMPILER_RT_DEFAULT_TARGET_TRIPLE})
list(GET TARGET_TRIPLE_LIST 0 COMPILER_RT_DEFAULT_TARGET_ARCH)
list(GET TARGET_TRIPLE_LIST 1 COMPILER_RT_DEFAULT_TARGET_OS)
list(GET TARGET_TRIPLE_LIST 2 COMPILER_RT_DEFAULT_TARGET_ABI)
# Determine if test target triple is specified explicitly, and doesn't match the
# default.
if(NOT COMPILER_RT_DEFAULT_TARGET_TRIPLE STREQUAL TARGET_TRIPLE)
set(COMPILER_RT_HAS_EXPLICIT_DEFAULT_TARGET_TRIPLE TRUE)
else()
set(COMPILER_RT_HAS_EXPLICIT_DEFAULT_TARGET_TRIPLE FALSE)
endif()
construct_compiler_rt_default_triple()
if ("${COMPILER_RT_DEFAULT_TARGET_ABI}" STREQUAL "androideabi")
set(ANDROID 1)
endif()
include(CompilerRTUtils)
set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(COMPILER_RT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
@ -129,12 +85,25 @@ option(COMPILER_RT_EXTERNALIZE_DEBUGINFO
# COMPILER_RT_DEBUG_PYBOOL is used by lit.common.configured.in.
pythonize_bool(COMPILER_RT_DEBUG)
include(config-ix)
if(APPLE AND SANITIZER_MIN_OSX_VERSION VERSION_LESS "10.9")
# Mac OS X prior to 10.9 had problems with exporting symbols from
# libc++/libc++abi.
set(use_cxxabi_default OFF)
elseif(MSVC)
set(use_cxxabi_default OFF)
else()
set(use_cxxabi_default ON)
endif()
option(SANITIZER_CAN_USE_CXXABI "Sanitizers can use cxxabi" ${use_cxxabi_default})
pythonize_bool(SANITIZER_CAN_USE_CXXABI)
#================================
# Setup Compiler Flags
#================================
include(config-ix)
if(MSVC)
# Override any existing /W flags with /W4. This is what LLVM does. Failing to
# remove other /W[0-4] flags will result in a warning about overriding a
@ -160,7 +129,9 @@ if(NOT COMPILER_RT_HAS_FUNC_SYMBOL)
endif()
# Provide some common commmandline flags for Sanitizer runtimes.
append_list_if(COMPILER_RT_HAS_FPIC_FLAG -fPIC SANITIZER_COMMON_CFLAGS)
if(NOT WIN32)
append_list_if(COMPILER_RT_HAS_FPIC_FLAG -fPIC SANITIZER_COMMON_CFLAGS)
endif()
append_list_if(COMPILER_RT_HAS_FNO_BUILTIN_FLAG -fno-builtin SANITIZER_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG -fno-exceptions SANITIZER_COMMON_CFLAGS)
if(NOT COMPILER_RT_DEBUG)
@ -194,7 +165,7 @@ if(MSVC)
# VS 2015 (version 1900) added support for thread safe static initialization.
# However, ASan interceptors run before CRT initialization, which causes the
# new thread safe code to crash. Disable this feature for now.
if (MSVC_VERSION GREATER 1899)
if (MSVC_VERSION GREATER 1899 OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
list(APPEND SANITIZER_COMMON_CFLAGS /Zc:threadSafeInit-)
endif()
endif()
@ -224,8 +195,20 @@ if(COMPILER_RT_HAS_GLINE_TABLES_ONLY_FLAG AND NOT COMPILER_RT_DEBUG)
list(APPEND SANITIZER_COMMON_CFLAGS -gline-tables-only)
elseif(COMPILER_RT_HAS_G_FLAG)
list(APPEND SANITIZER_COMMON_CFLAGS -g)
elseif(COMPILER_RT_HAS_Zi_FLAG)
list(APPEND SANITIZER_COMMON_CFLAGS /Zi)
elseif(MSVC)
# Use /Z7 instead of /Zi for the asan runtime. This avoids the LNK4099
# warning from the MS linker complaining that it can't find the 'vc140.pdb'
# file used by our object library compilations.
list(APPEND SANITIZER_COMMON_CFLAGS /Z7)
llvm_replace_compiler_option(CMAKE_CXX_FLAGS "/Z[i7I]" "/Z7")
llvm_replace_compiler_option(CMAKE_CXX_FLAGS_DEBUG "/Z[i7I]" "/Z7")
llvm_replace_compiler_option(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/Z[i7I]" "/Z7")
endif()
if(LLVM_ENABLE_MODULES)
# Sanitizers cannot be built with -fmodules. The interceptors intentionally
# don't include system headers, which is incompatible with modules.
list(APPEND SANITIZER_COMMON_CFLAGS -fno-modules)
endif()
# Turn off several warnings.
@ -242,17 +225,6 @@ append_list_if(COMPILER_RT_HAS_WD4800_FLAG /wd4800 SANITIZER_COMMON_CFLAGS)
# Warnings to turn off for all libraries, not just sanitizers.
append_string_if(COMPILER_RT_HAS_WUNUSED_PARAMETER_FLAG -Wno-unused-parameter CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
if(APPLE AND SANITIZER_MIN_OSX_VERSION VERSION_LESS "10.9")
# Mac OS X prior to 10.9 had problems with exporting symbols from
# libc++/libc++abi.
set(SANITIZER_CAN_USE_CXXABI FALSE)
elseif(MSVC)
set(SANITIZER_CAN_USE_CXXABI FALSE)
else()
set(SANITIZER_CAN_USE_CXXABI TRUE)
endif()
pythonize_bool(SANITIZER_CAN_USE_CXXABI)
add_subdirectory(include)
set(COMPILER_RT_LIBCXX_PATH ${LLVM_MAIN_SRC_DIR}/projects/libcxx)

275
Makefile
View file

@ -1,275 +0,0 @@
SubDirs := lib
# Set default rule before anything else.
all: help
include make/config.mk
include make/util.mk
# If SRCROOT is defined, assume we are doing an Apple style build. We should be
# able to use RC_XBS for this but that is unused during "make installsrc".
ifdef SRCROOT
include make/AppleBI.mk
endif
# Make sure we don't build with a missing ProjObjRoot.
ifeq ($(ProjObjRoot),)
$(error Refusing to build with empty ProjObjRoot variable)
endif
##############
###
# Rules
###
# Top level targets
# FIXME: Document the available subtargets.
help:
@echo "usage: make [{VARIABLE=VALUE}*] target"
@echo
@echo "User variables:"
@echo " VERBOSE=1: Use to show all commands [default=0]"
@echo
@echo "Available targets:"
@echo " <platform name>: build the libraries for 'platform'"
@echo " clean: clean all configurations"
@echo " test: run unit tests"
@echo
@echo " info-platforms: list available platforms"
@echo " help-devel: print additional help for developers"
@echo
help-devel: help
@echo "Development targets:"
@echo " <platform name>-<config name>:"
@echo " build the libraries for a single platform config"
@echo " <platform name>-<config name>-<arch name>:"
@echo " build the libraries for a single config and arch"
@echo " info-functions: list available compiler-rt functions"
@echo " help-hidden: print help for Makefile debugging"
@echo
help-hidden: help-devel
@echo "Debugging variables:"
@echo " DEBUGMAKE=1: enable some Makefile logging [default=]"
@echo " =2: enable more Makefile logging"
@echo
@echo "Debugging targets:"
@echo " make-print-FOO: print information on the variable 'FOO'"
@echo
info-functions:
@echo "compiler-rt Available Functions"
@echo
@echo "All Functions: $(AvailableFunctions)"
@$(foreach fn,$(AvailableFunctions),\
printf " %-20s - available in (%s)\n" $(fn)\
"$(foreach key,$(AvailableIn.$(fn)),$($(key).Dir))";)
info-platforms:
@echo "compiler-rt Available Platforms"
@echo
@echo "Platforms:"
@$(foreach key,$(PlatformKeys),\
printf " %s - from '%s'\n" $($(key).Name) $($(key).Path);\
printf " %s\n" "$($(key).Description)";\
printf " Configurations: %s\n\n" "$($(key).Configs)";)
# Provide default clean target which is extended by other templates.
.PHONY: clean
clean::
# Test
.PHONY: test
test:
cd test/Unit && ./test
###
# Directory handling magic.
# Create directories as needed, and timestamp their creation.
%/.dir:
$(Summary) " MKDIR: $*"
$(Verb) $(MKDIR) $* > /dev/null
$(Verb) echo 'Created.' > $@
# Remove directories
%/.remove:
$(Verb) $(RM) -r $*
###
# Include child makefile fragments
Dir := .
include make/subdir.mk
include make/lib_info.mk
include make/lib_util.mk
include make/lib_platforms.mk
###
# Define Platform Rules
define PerPlatform_template
$(call Set,Tmp.Key,$(1))
$(call Set,Tmp.Name,$($(Tmp.Key).Name))
$(call Set,Tmp.Configs,$($(Tmp.Key).Configs))
$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name))
# Top-Level Platform Target
$(Tmp.Name):: $(Tmp.Configs:%=$(Tmp.Name)-%)
.PHONY: $(Tmp.Name)
clean::
$(Verb) rm -rf $(Tmp.ObjPath)
# Per-Config Libraries
$(foreach config,$(Tmp.Configs),\
$(call PerPlatformConfig_template,$(config)))
endef
define PerPlatformConfig_template
$(call Set,Tmp.Config,$(1))
$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config))
$(call Set,Tmp.SHARED_LIBRARY,$(strip \
$(call GetCNAVar,SHARED_LIBRARY,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.SHARED_LIBRARY_SUFFIX,$(strip \
$(call GetCNAVar,SHARED_LIBRARY_SUFFIX,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
# Compute the library suffix.
$(if $(call streq,1,$(Tmp.SHARED_LIBRARY)),
$(call Set,Tmp.LibrarySuffix,$(Tmp.SHARED_LIBRARY_SUFFIX)),
$(call Set,Tmp.LibrarySuffix,a))
# Compute the archs to build, depending on whether this is a universal build or
# not.
$(call Set,Tmp.ArchsToBuild,\
$(if $(call IsDefined,$(Tmp.Key).UniversalArchs),\
$(strip \
$(or $($(Tmp.Key).UniversalArchs.$(Tmp.Config)),\
$($(Tmp.Key).UniversalArchs))),\
$(call VarOrDefault,$(Tmp.Key).Arch.$(Tmp.Config),$($(Tmp.Key).Arch))))
# Copy or lipo to create the per-config library.
$(call Set,Tmp.Inputs,$(Tmp.ArchsToBuild:%=$(Tmp.ObjPath)/%/libcompiler_rt.$(Tmp.LibrarySuffix)))
$(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix): $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
$(Summary) " FINAL-ARCHIVE: $(Tmp.Name)/$(Tmp.Config): $$@"
-$(Verb) $(RM) $$@
$(if $(call streq,1,$(words $(Tmp.ArchsToBuild))), \
$(Verb) $(CP) $(Tmp.Inputs) $$@, \
$(Verb) $(LIPO) -create -output $$@ $(Tmp.Inputs))
.PRECIOUS: $(Tmp.ObjPath)/.dir
# Per-Config Targets
$(Tmp.Name)-$(Tmp.Config):: $(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix)
.PHONY: $(Tmp.Name)-$(Tmp.Config)
# Per-Config-Arch Libraries
$(foreach arch,$(Tmp.ArchsToBuild),\
$(call PerPlatformConfigArch_template,$(arch)))
endef
define PerPlatformConfigArch_template
$(call Set,Tmp.Arch,$(1))
$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch))
$(call Set,Tmp.Functions,$(strip \
$(AlwaysRequiredModules) \
$(call GetCNAVar,FUNCTIONS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.Optimized,$(strip \
$(call GetCNAVar,OPTIMIZED,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.AR,$(strip \
$(call GetCNAVar,AR,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.ARFLAGS,$(strip \
$(call GetCNAVar,ARFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.CC,$(strip \
$(call GetCNAVar,CC,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.LDFLAGS,$(strip \
$(call GetCNAVar,LDFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.RANLIB,$(strip \
$(call GetCNAVar,RANLIB,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.RANLIBFLAGS,$(strip \
$(call GetCNAVar,RANLIBFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.SHARED_LIBRARY,$(strip \
$(call GetCNAVar,SHARED_LIBRARY,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
# Compute the library suffix.
$(if $(call streq,1,$(Tmp.SHARED_LIBRARY)),
$(call Set,Tmp.LibrarySuffix,$(Tmp.SHARED_LIBRARY_SUFFIX)),
$(call Set,Tmp.LibrarySuffix,a))
# Compute the object inputs for this library.
$(call Set,Tmp.Inputs,\
$(foreach fn,$(sort $(Tmp.Functions)),\
$(call Set,Tmp.FnDir,\
$(call SelectFunctionDir,$(Tmp.Config),$(Tmp.Arch),$(fn),$(Tmp.Optimized)))\
$(Tmp.ObjPath)/$(Tmp.FnDir)/$(fn).o))
$(Tmp.ObjPath)/libcompiler_rt.a: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
$(Summary) " ARCHIVE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@"
-$(Verb) $(RM) $$@
$(Verb) $(Tmp.AR) $(Tmp.ARFLAGS) $$@ $(Tmp.Inputs)
$(Verb) $(Tmp.RANLIB) $(Tmp.RANLIBFLAGS) $$@
$(Tmp.ObjPath)/libcompiler_rt.dylib: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
$(Summary) " DYLIB: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@"
$(Verb) $(Tmp.CC) -arch $(Tmp.Arch) -dynamiclib -o $$@ \
$(Tmp.Inputs) $(Tmp.LDFLAGS)
$(Tmp.ObjPath)/libcompiler_rt.so: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
$(Summary) " SO: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@"
$(Verb) $(Tmp.CC) -shared -o $$@ \
$(Tmp.Inputs) $(Tmp.LDFLAGS)
.PRECIOUS: $(Tmp.ObjPath)/.dir
# Per-Config-Arch Targets
$(Tmp.Name)-$(Tmp.Config)-$(Tmp.Arch):: $(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix)
.PHONY: $(Tmp.Name)-$(Tmp.Config)-$(Tmp.Arch)
# Per-Config-Arch-SubDir Objects
$(foreach key,$(SubDirKeys),\
$(call PerPlatformConfigArchSubDir_template,$(key)))
endef
define PerPlatformConfigArchSubDir_template
$(call Set,Tmp.SubDirKey,$(1))
$(call Set,Tmp.SubDir,$($(Tmp.SubDirKey).Dir))
$(call Set,Tmp.SrcPath,$(ProjSrcRoot)/$(Tmp.SubDir))
$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch)/$(Tmp.SubDirKey))
$(call Set,Tmp.Dependencies,$($(Tmp.SubDirKey).Dependencies))
$(call Set,Tmp.CC,$(strip \
$(call GetCNAVar,CC,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.KERNEL_USE,$(strip \
$(call GetCNAVar,KERNEL_USE,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.VISIBILITY_HIDDEN,$(strip \
$(call GetCNAVar,VISIBILITY_HIDDEN,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.CFLAGS,$(strip \
$(if $(call IsDefined,$(Tmp.Key).UniversalArchs),-arch $(Tmp.Arch),)\
$(if $(call streq,$(Tmp.VISIBILITY_HIDDEN),1),\
-fvisibility=hidden -DVISIBILITY_HIDDEN,)\
$(if $(call streq,$(Tmp.KERNEL_USE),1),\
-mkernel -DKERNEL_USE,)\
$(call GetCNAVar,CFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.s $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir
$(Summary) " ASSEMBLE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<"
$(Verb) $(Tmp.CC) $(COMMON_ASMFLAGS) $(Tmp.CFLAGS) -c -o $$@ $$<
$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.S $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir
$(Summary) " ASSEMBLE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<"
$(Verb) $(Tmp.CC) $(COMMON_ASMFLAGS) $(Tmp.CFLAGS) -c -o $$@ $$<
$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.c $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir
$(Summary) " COMPILE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<"
$(Verb) $(Tmp.CC) $(COMMON_CFLAGS) $(Tmp.CFLAGS) -c -o $$@ $$<
$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.cc $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir
$(Summary) " COMPILE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<"
$(Verb) $(Tmp.CC) $(COMMON_CXXFLAGS) $(Tmp.CFLAGS) -c -o $$@ $$<
.PRECIOUS: $(Tmp.ObjPath)/.dir
endef
# Run templates.
$(foreach key,$(PlatformKeys),\
$(eval $(call PerPlatform_template,$(key))))
###
ifneq ($(DEBUGMAKE),)
$(info MAKE: Done processing Makefile)
$(info )
endif

View file

@ -77,6 +77,15 @@ macro(format_object_libs output suffix)
endforeach()
endmacro()
function(add_compiler_rt_component name)
add_custom_target(${name})
set_target_properties(${name} PROPERTIES FOLDER "Compiler-RT Misc")
if(COMMAND runtime_register_component)
runtime_register_component(${name})
endif()
add_dependencies(compiler-rt ${name})
endfunction()
# Adds static or shared runtime for a list of architectures and operating
# systems and puts it in the proper directory in the build and install trees.
# add_compiler_rt_runtime(<name>
@ -164,6 +173,7 @@ function(add_compiler_rt_runtime name type)
-P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
set_target_properties(install-${LIB_PARENT_TARGET} PROPERTIES
FOLDER "Compiler-RT Misc")
add_dependencies(install-compiler-rt install-${LIB_PARENT_TARGET})
endif()
endif()
@ -185,8 +195,14 @@ function(add_compiler_rt_runtime name type)
set_target_properties(${libname} PROPERTIES
OUTPUT_NAME ${output_name_${libname}})
set_target_properties(${libname} PROPERTIES FOLDER "Compiler-RT Runtime")
if(LIB_LINK_LIBS AND ${type} STREQUAL "SHARED")
target_link_libraries(${libname} ${LIB_LINK_LIBS})
if(${type} STREQUAL "SHARED")
if(LIB_LINK_LIBS)
target_link_libraries(${libname} ${LIB_LINK_LIBS})
endif()
if(WIN32 AND NOT CYGWIN AND NOT MINGW)
set_target_properties(${libname} PROPERTIES IMPORT_PREFIX "")
set_target_properties(${libname} PROPERTIES IMPORT_SUFFIX ".lib")
endif()
endif()
install(TARGETS ${libname}
ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}
@ -253,12 +269,6 @@ if(MSVC)
# gtest use a lot of stuff marked as deprecated on Windows.
list(APPEND COMPILER_RT_GTEST_CFLAGS -Wno-deprecated-declarations)
# Visual Studio 2012 only supports up to 8 template parameters in
# std::tr1::tuple by default, but gtest requires 10
if(MSVC_VERSION EQUAL 1700)
list(APPEND COMPILER_RT_GTEST_CFLAGS -D_VARIADIC_MAX=10)
endif()
endif()
# Link objects into a single executable with COMPILER_RT_TEST_COMPILER,

View file

@ -1,3 +1,4 @@
include(CMakeCheckCompilerFlagCommonPatterns)
# This function takes an OS and a list of architectures and identifies the
# subset of the architectures list that the installed toolchain can target.
@ -10,7 +11,13 @@ function(try_compile_only output)
file(WRITE ${SIMPLE_C} "${ARG_SOURCE}\n")
string(REGEX MATCHALL "<[A-Za-z0-9_]*>" substitutions
${CMAKE_C_COMPILE_OBJECT})
string(REPLACE ";" " " extra_flags "${ARG_FLAGS}")
set(TRY_COMPILE_FLAGS "${ARG_FLAGS}")
if(CMAKE_C_COMPILER_ID MATCHES Clang AND CMAKE_C_COMPILER_TARGET)
list(APPEND TRY_COMPILE_FLAGS "-target ${CMAKE_C_COMPILER_TARGET}")
endif()
string(REPLACE ";" " " extra_flags "${TRY_COMPILE_FLAGS}")
set(test_compile_command "${CMAKE_C_COMPILE_OBJECT}")
foreach(substitution ${substitutions})
@ -41,7 +48,19 @@ function(try_compile_only output)
OUTPUT_VARIABLE TEST_OUTPUT
ERROR_VARIABLE TEST_ERROR
)
if(result EQUAL 0)
CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckCCompilerFlag_COMMON_PATTERNS)
set(ERRORS_FOUND OFF)
foreach(var ${_CheckCCompilerFlag_COMMON_PATTERNS})
if("${var}" STREQUAL "FAIL_REGEX")
continue()
endif()
if("${TEST_ERROR}" MATCHES "${var}" OR "${TEST_OUTPUT}" MATCHES "${var}")
set(ERRORS_FOUND ON)
endif()
endforeach()
if(result EQUAL 0 AND NOT ERRORS_FOUND)
set(${output} True PARENT_SCOPE)
else()
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log

View file

@ -7,13 +7,15 @@ function(find_darwin_sdk_dir var sdk_name)
# Let's first try the internal SDK, otherwise use the public SDK.
execute_process(
COMMAND xcodebuild -version -sdk ${sdk_name}.internal Path
RESULT_VARIABLE result_process
OUTPUT_VARIABLE var_internal
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_FILE /dev/null
)
if("" STREQUAL "${var_internal}")
if((NOT result_process EQUAL 0) OR "" STREQUAL "${var_internal}")
execute_process(
COMMAND xcodebuild -version -sdk ${sdk_name} Path
RESULT_VARIABLE result_process
OUTPUT_VARIABLE var_internal
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_FILE /dev/null
@ -21,7 +23,9 @@ function(find_darwin_sdk_dir var sdk_name)
else()
set(${var}_INTERNAL ${var_internal} PARENT_SCOPE)
endif()
set(${var} ${var_internal} PARENT_SCOPE)
if(result_process EQUAL 0)
set(${var} ${var_internal} PARENT_SCOPE)
endif()
endfunction()
# There isn't a clear mapping of what architectures are supported with a given
@ -256,30 +260,6 @@ function(darwin_filter_builtin_sources output_var exclude_or_include excluded_li
set(${output_var} ${intermediate} PARENT_SCOPE)
endfunction()
function(darwin_add_eprintf_library)
cmake_parse_arguments(LIB
""
""
"CFLAGS"
${ARGN})
add_library(clang_rt.eprintf STATIC eprintf.c)
set_target_compile_flags(clang_rt.eprintf
-isysroot ${DARWIN_osx_SYSROOT}
${DARWIN_osx_BUILTIN_MIN_VER_FLAG}
-arch i386
${LIB_CFLAGS})
set_target_properties(clang_rt.eprintf PROPERTIES
OUTPUT_NAME clang_rt.eprintf${COMPILER_RT_OS_SUFFIX})
set_target_properties(clang_rt.eprintf PROPERTIES
OSX_ARCHITECTURES i386)
add_dependencies(builtins clang_rt.eprintf)
set_target_properties(clang_rt.eprintf PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR})
install(TARGETS clang_rt.eprintf
ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR})
endfunction()
# Generates builtin libraries for all operating systems specified in ARGN. Each
# OS library is constructed by lipo-ing together single-architecture libraries.
macro(darwin_add_builtin_libraries)
@ -350,8 +330,6 @@ macro(darwin_add_builtin_libraries)
endif()
endforeach()
darwin_add_eprintf_library(CFLAGS ${CFLAGS})
# We put the x86 sim slices into the archives for their base OS
foreach (os ${ARGN})
if(NOT ${os} MATCHES ".*sim$")

View file

@ -49,7 +49,7 @@ macro(append_string_if condition value)
endmacro()
macro(append_rtti_flag polarity list)
if(polarity)
if(${polarity})
append_list_if(COMPILER_RT_HAS_FRTTI_FLAG -frtti ${list})
append_list_if(COMPILER_RT_HAS_GR_FLAG /GR ${list})
else()
@ -76,6 +76,18 @@ macro(list_intersect output input1 input2)
endforeach()
endmacro()
function(list_replace input_list old new)
set(replaced_list)
foreach(item ${${input_list}})
if(${item} STREQUAL ${old})
list(APPEND replaced_list ${new})
else()
list(APPEND replaced_list ${item})
endif()
endforeach()
set(${input_list} "${replaced_list}" PARENT_SCOPE)
endfunction()
# Takes ${ARGN} and puts only supported architectures in @out_var list.
function(filter_available_targets out_var)
set(archs ${${out_var}})
@ -88,6 +100,13 @@ function(filter_available_targets out_var)
set(${out_var} ${archs} PARENT_SCOPE)
endfunction()
# Add $arch as supported with no additional flags.
macro(add_default_target_arch arch)
set(TARGET_${arch}_CFLAGS "")
set(CAN_TARGET_${arch} 1)
list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
endmacro()
function(check_compile_definition def argstring out_var)
if("${def}" STREQUAL "")
set(${out_var} TRUE PARENT_SCOPE)
@ -113,20 +132,26 @@ macro(test_target_arch arch def)
set(argstring "${argstring} ${arg}")
endforeach()
check_compile_definition("${def}" "${argstring}" HAS_${arch}_DEF)
if(NOT HAS_${arch}_DEF)
set(CAN_TARGET_${arch} FALSE)
elseif(TEST_COMPILE_ONLY)
try_compile_only(CAN_TARGET_${arch} ${TARGET_${arch}_CFLAGS})
else()
set(argstring "${CMAKE_EXE_LINKER_FLAGS} ${argstring}")
try_compile(CAN_TARGET_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE}
COMPILE_DEFINITIONS "${TARGET_${arch}_CFLAGS}"
OUTPUT_VARIABLE TARGET_${arch}_OUTPUT
CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${argstring}")
if(NOT DEFINED CAN_TARGET_${arch})
if(NOT HAS_${arch}_DEF)
set(CAN_TARGET_${arch} FALSE)
elseif(TEST_COMPILE_ONLY)
try_compile_only(CAN_TARGET_${arch} ${TARGET_${arch}_CFLAGS})
else()
set(argstring "${CMAKE_EXE_LINKER_FLAGS} ${argstring}")
set(FLAG_NO_EXCEPTIONS "")
if(COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG)
set(FLAG_NO_EXCEPTIONS " -fno-exceptions ")
endif()
try_compile(CAN_TARGET_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE}
COMPILE_DEFINITIONS "${TARGET_${arch}_CFLAGS} ${FLAG_NO_EXCEPTIONS}"
OUTPUT_VARIABLE TARGET_${arch}_OUTPUT
CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${argstring}")
endif()
endif()
if(${CAN_TARGET_${arch}})
list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "${arch}" AND
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" STREQUAL "${arch}" AND
COMPILER_RT_HAS_EXPLICIT_DEFAULT_TARGET_TRIPLE)
# Bail out if we cannot target the architecture we plan to test.
message(FATAL_ERROR "Cannot compile for ${arch}:\n${TARGET_${arch}_OUTPUT}")
@ -166,3 +191,73 @@ macro(detect_target_arch)
add_default_target_arch(wasm64)
endif()
endmacro()
macro(load_llvm_config)
if (NOT LLVM_CONFIG_PATH)
find_program(LLVM_CONFIG_PATH "llvm-config"
DOC "Path to llvm-config binary")
if (NOT LLVM_CONFIG_PATH)
message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH")
endif()
endif()
execute_process(
COMMAND ${LLVM_CONFIG_PATH} "--obj-root" "--bindir" "--libdir" "--src-root"
RESULT_VARIABLE HAD_ERROR
OUTPUT_VARIABLE CONFIG_OUTPUT)
if (HAD_ERROR)
message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}")
endif()
string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" CONFIG_OUTPUT ${CONFIG_OUTPUT})
list(GET CONFIG_OUTPUT 0 BINARY_DIR)
list(GET CONFIG_OUTPUT 1 TOOLS_BINARY_DIR)
list(GET CONFIG_OUTPUT 2 LIBRARY_DIR)
list(GET CONFIG_OUTPUT 3 MAIN_SRC_DIR)
set(LLVM_BINARY_DIR ${BINARY_DIR} CACHE PATH "Path to LLVM build tree")
set(LLVM_TOOLS_BINARY_DIR ${TOOLS_BINARY_DIR} CACHE PATH "Path to llvm/bin")
set(LLVM_LIBRARY_DIR ${LIBRARY_DIR} CACHE PATH "Path to llvm/lib")
set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree")
# Make use of LLVM CMake modules.
file(TO_CMAKE_PATH ${LLVM_BINARY_DIR} LLVM_BINARY_DIR_CMAKE_STYLE)
set(LLVM_CMAKE_PATH "${LLVM_BINARY_DIR_CMAKE_STYLE}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm")
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}")
# Get some LLVM variables from LLVMConfig.
include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake")
set(LLVM_LIBRARY_OUTPUT_INTDIR
${LLVM_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
endmacro()
macro(construct_compiler_rt_default_triple)
if(COMPILER_RT_DEFAULT_TARGET_ONLY)
if(DEFINED COMPILER_RT_DEFAULT_TARGET_TRIPLE)
message(FATAL_ERROR "COMPILER_RT_DEFAULT_TARGET_TRIPLE isn't supported when building for default target only")
endif()
set(COMPILER_RT_DEFAULT_TARGET_TRIPLE ${CMAKE_C_COMPILER_TARGET})
else()
set(COMPILER_RT_DEFAULT_TARGET_TRIPLE ${TARGET_TRIPLE} CACHE STRING
"Default triple for which compiler-rt runtimes will be built.")
endif()
if(DEFINED COMPILER_RT_TEST_TARGET_TRIPLE)
# Backwards compatibility: this variable used to be called
# COMPILER_RT_TEST_TARGET_TRIPLE.
set(COMPILER_RT_DEFAULT_TARGET_TRIPLE ${COMPILER_RT_TEST_TARGET_TRIPLE})
endif()
string(REPLACE "-" ";" TARGET_TRIPLE_LIST ${COMPILER_RT_DEFAULT_TARGET_TRIPLE})
list(GET TARGET_TRIPLE_LIST 0 COMPILER_RT_DEFAULT_TARGET_ARCH)
list(GET TARGET_TRIPLE_LIST 1 COMPILER_RT_DEFAULT_TARGET_OS)
list(LENGTH TARGET_TRIPLE_LIST TARGET_TRIPLE_LIST_LENGTH)
if(TARGET_TRIPLE_LIST_LENGTH GREATER 2)
list(GET TARGET_TRIPLE_LIST 2 COMPILER_RT_DEFAULT_TARGET_ABI)
endif()
# Determine if test target triple is specified explicitly, and doesn't match the
# default.
if(NOT COMPILER_RT_DEFAULT_TARGET_TRIPLE STREQUAL TARGET_TRIPLE)
set(COMPILER_RT_HAS_EXPLICIT_DEFAULT_TARGET_TRIPLE TRUE)
else()
set(COMPILER_RT_HAS_EXPLICIT_DEFAULT_TARGET_TRIPLE FALSE)
endif()
endmacro()

View file

@ -8,6 +8,7 @@ check_include_file(unwind.h HAVE_UNWIND_H)
# Top level target used to build all compiler-rt libraries.
add_custom_target(compiler-rt ALL)
add_custom_target(install-compiler-rt)
set_target_properties(compiler-rt PROPERTIES FOLDER "Compiler-RT Misc")
# Setting these variables from an LLVM build is sufficient that compiler-rt can
@ -80,9 +81,20 @@ if(APPLE)
set(OSX_SYSROOT_FLAG "-isysroot${OSX_SYSROOT}")
endif()
option(COMPILER_RT_ENABLE_IOS "Enable building for iOS" Off)
option(COMPILER_RT_ENABLE_IOS "Enable building for iOS" On)
option(COMPILER_RT_ENABLE_WATCHOS "Enable building for watchOS - Experimental" Off)
option(COMPILER_RT_ENABLE_TVOS "Enable building for tvOS - Experimental" Off)
else()
option(COMPILER_RT_DEFAULT_TARGET_ONLY "Build builtins only for the default target" Off)
endif()
if(WIN32 AND NOT MINGW AND NOT CYGWIN)
set(CMAKE_SHARED_LIBRARY_PREFIX_C "")
set(CMAKE_SHARED_LIBRARY_PREFIX_CXX "")
set(CMAKE_STATIC_LIBRARY_PREFIX_C "")
set(CMAKE_STATIC_LIBRARY_PREFIX_CXX "")
set(CMAKE_STATIC_LIBRARY_SUFFIX_C ".lib")
set(CMAKE_STATIC_LIBRARY_SUFFIX_CXX ".lib")
endif()
macro(test_targets)
@ -117,7 +129,9 @@ macro(test_targets)
detect_target_arch()
set(COMPILER_RT_OS_SUFFIX "-android")
elseif(NOT APPLE) # Supported archs for Apple platforms are generated later
if("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "i[2-6]86|x86|amd64")
if(COMPILER_RT_DEFAULT_TARGET_ONLY)
add_default_target_arch(${COMPILER_RT_DEFAULT_TARGET_ARCH})
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "i[2-6]86|x86|amd64")
if(NOT MSVC)
test_target_arch(x86_64 "" "-m64")
# FIXME: We build runtimes for both i686 and i386, as "clang -m32" may
@ -153,8 +167,12 @@ macro(test_targets)
test_target_arch(mips "" "-mips32r2" "--target=mips-linux-gnu")
test_target_arch(mips64 "" "-mips64r2" "--target=mips64-linux-gnu" "-mabi=64")
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "arm")
test_target_arch(arm "" "-march=armv7-a" "-mfloat-abi=soft")
test_target_arch(armhf "" "-march=armv7-a" "-mfloat-abi=hard")
if(WIN32)
test_target_arch(arm "" "" "")
else()
test_target_arch(arm "" "-march=armv7-a" "-mfloat-abi=soft")
test_target_arch(armhf "" "-march=armv7-a" "-mfloat-abi=hard")
endif()
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "aarch32")
test_target_arch(aarch32 "" "-march=armv8-a")
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "aarch64")

View file

@ -4,18 +4,17 @@ include(CheckCSourceCompiles)
# Make all the tests only check the compiler
set(TEST_COMPILE_ONLY On)
# Check host compiler support for certain flags
builtin_check_c_compiler_flag(-fPIC COMPILER_RT_HAS_FPIC_FLAG)
builtin_check_c_compiler_flag(-fPIE COMPILER_RT_HAS_FPIE_FLAG)
builtin_check_c_compiler_flag(-fno-builtin COMPILER_RT_HAS_FNO_BUILTIN_FLAG)
builtin_check_c_compiler_flag(-std=c99 COMPILER_RT_HAS_STD_C99_FLAG)
builtin_check_c_compiler_flag(-std=c11 COMPILER_RT_HAS_STD_C11_FLAG)
builtin_check_c_compiler_flag(-fvisibility=hidden COMPILER_RT_HAS_VISIBILITY_HIDDEN_FLAG)
builtin_check_c_compiler_flag(-fomit-frame-pointer COMPILER_RT_HAS_OMIT_FRAME_POINTER_FLAG)
builtin_check_c_compiler_flag(-ffreestanding COMPILER_RT_HAS_FREESTANDING_FLAG)
builtin_check_c_compiler_flag(-mfloat-abi=soft COMPILER_RT_HAS_FLOAT_ABI_SOFT_FLAG)
builtin_check_c_compiler_flag(-mfloat-abi=hard COMPILER_RT_HAS_FLOAT_ABI_HARD_FLAG)
builtin_check_c_compiler_flag(-static COMPILER_RT_HAS_STATIC_FLAG)
builtin_check_c_compiler_flag(-fxray-instrument COMPILER_RT_HAS_XRAY_COMPILER_FLAG)
builtin_check_c_compiler_source(COMPILER_RT_SUPPORTS_ATOMIC_KEYWORD
builtin_check_c_compiler_source(COMPILER_RT_HAS_ATOMIC_KEYWORD
"
int foo(int x, int y) {
_Atomic int result = x * y;

View file

@ -29,6 +29,7 @@ check_cxx_compiler_flag(-std=c++11 COMPILER_RT_HAS_STD_CXX11_FLAG)
check_cxx_compiler_flag(-ftls-model=initial-exec COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC)
check_cxx_compiler_flag(-fno-lto COMPILER_RT_HAS_FNO_LTO_FLAG)
check_cxx_compiler_flag("-Werror -msse3" COMPILER_RT_HAS_MSSE3_FLAG)
check_cxx_compiler_flag("-Werror -msse4.2" COMPILER_RT_HAS_MSSE4_2_FLAG)
check_cxx_compiler_flag(--sysroot=. COMPILER_RT_HAS_SYSROOT_FLAG)
if(NOT WIN32 AND NOT CYGWIN)
@ -93,14 +94,7 @@ set(COMPILER_RT_SUPPORTED_ARCH)
# runtime libraries supported by our current compilers cross-compiling
# abilities.
set(SIMPLE_SOURCE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple.cc)
file(WRITE ${SIMPLE_SOURCE} "#include <stdlib.h>\n#include <limits>\nint main() {}\n")
# Add $arch as supported with no additional flags.
macro(add_default_target_arch arch)
set(TARGET_${arch}_CFLAGS "")
set(CAN_TARGET_${arch} 1)
list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
endmacro()
file(WRITE ${SIMPLE_SOURCE} "#include <stdlib.h>\n#include <stdio.h>\nint main() { printf(\"hello, world\"); }\n")
# 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.
@ -128,6 +122,24 @@ function(get_target_flags_for_arch arch out_var)
endif()
endfunction()
# Returns a compiler and CFLAGS that should be used to run tests for the
# specific architecture. When cross-compiling, this is controled via
# COMPILER_RT_TEST_COMPILER and COMPILER_RT_TEST_COMPILER_CFLAGS.
macro(get_test_cc_for_arch arch cc_out cflags_out)
if(ANDROID OR ${arch} MATCHES "arm|aarch64")
# This is only true if we are cross-compiling.
# Build all tests with host compiler and use host tools.
set(${cc_out} ${COMPILER_RT_TEST_COMPILER})
set(${cflags_out} ${COMPILER_RT_TEST_COMPILER_CFLAGS})
else()
get_target_flags_for_arch(${arch} ${cflags_out})
if(APPLE)
list(APPEND ${cflags_out} ${DARWIN_osx_CFLAGS})
endif()
string(REPLACE ";" " " ${cflags_out} "${${cflags_out}}")
endif()
endmacro()
set(ARM64 aarch64)
set(ARM32 arm armhf)
set(X86 i386 i686)
@ -148,19 +160,20 @@ endif()
set(ALL_SANITIZER_COMMON_SUPPORTED_ARCH ${X86} ${X86_64} ${PPC64}
${ARM32} ${ARM64} ${MIPS32} ${MIPS64} ${S390X})
set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64}
${MIPS32} ${MIPS64} ${PPC64})
${MIPS32} ${MIPS64} ${PPC64} ${S390X})
set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64})
set(ALL_LSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64})
set(ALL_MSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64})
set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC64}
${MIPS32} ${MIPS64})
${MIPS32} ${MIPS64} ${S390X})
set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64})
set(ALL_UBSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64}
${MIPS32} ${MIPS64} ${PPC64} ${S390X})
set(ALL_SAFESTACK_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM64} ${MIPS32} ${MIPS64})
set(ALL_CFI_SUPPORTED_ARCH ${X86} ${X86_64} ${MIPS64})
set(ALL_ESAN_SUPPORTED_ARCH ${X86_64})
set(ALL_SCUDO_SUPPORTED_ARCH ${X86_64})
set(ALL_ESAN_SUPPORTED_ARCH ${X86_64} ${MIPS64})
set(ALL_SCUDO_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32})
set(ALL_XRAY_SUPPORTED_ARCH ${X86_64} ${ARM32} ${ARM64})
if(APPLE)
include(CompilerRTDarwinUtils)
@ -173,6 +186,14 @@ if(APPLE)
find_darwin_sdk_dir(DARWIN_tvossim_SYSROOT appletvsimulator)
find_darwin_sdk_dir(DARWIN_tvos_SYSROOT appletvos)
if(NOT DARWIN_osx_SYSROOT)
if(EXISTS /usr/include)
set(DARWIN_osx_SYSROOT /)
else()
message(ERROR "Could not detect OS X Sysroot. Either install Xcode or the Apple Command Line Tools")
endif()
endif()
if(COMPILER_RT_ENABLE_IOS)
list(APPEND DARWIN_EMBEDDED_PLATFORMS ios)
set(DARWIN_ios_MIN_VER_FLAG -miphoneos-version-min)
@ -208,7 +229,11 @@ if(APPLE)
set(SANITIZER_MIN_OSX_VERSION 10.9)
endif()
if(SANITIZER_MIN_OSX_VERSION VERSION_LESS "10.7")
message(FATAL_ERROR "Too old OS X version: ${SANITIZER_MIN_OSX_VERSION}")
message(FATAL_ERROR "macOS deployment target '${SANITIZER_MIN_OSX_VERSION}' is too old.")
endif()
if(SANITIZER_MIN_OSX_VERSION VERSION_GREATER "10.9")
message(WARNING "macOS deployment target '${SANITIZER_MIN_OSX_VERSION}' is too new, setting to '10.9' instead.")
set(SANITIZER_MIN_OSX_VERSION 10.9)
endif()
endif()
@ -268,7 +293,7 @@ if(APPLE)
DARWIN_${platform}sim_ARCHS
${toolchain_arches})
message(STATUS "${platform} Simulator supported arches: ${DARWIN_${platform}sim_ARCHS}")
if(DARWIN_${platform}_ARCHS)
if(DARWIN_${platform}sim_ARCHS)
list(APPEND SANITIZER_COMMON_SUPPORTED_OS ${platform}sim)
list(APPEND PROFILE_SUPPORTED_OS ${platform}sim)
if(DARWIN_${platform}_SYSROOT_INTERNAL)
@ -350,6 +375,9 @@ if(APPLE)
list_intersect(SCUDO_SUPPORTED_ARCH
ALL_SCUDO_SUPPORTED_ARCH
SANITIZER_COMMON_SUPPORTED_ARCH)
list_intersect(XRAY_SUPPORTED_ARCH
ALL_XRAY_SUPPORTED_ARCH
SANITIZER_COMMON_SUPPORTED_ARCH)
else()
# Architectures supported by compiler-rt libraries.
filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH
@ -371,8 +399,8 @@ else()
${ALL_SAFESTACK_SUPPORTED_ARCH})
filter_available_targets(CFI_SUPPORTED_ARCH ${ALL_CFI_SUPPORTED_ARCH})
filter_available_targets(ESAN_SUPPORTED_ARCH ${ALL_ESAN_SUPPORTED_ARCH})
filter_available_targets(SCUDO_SUPPORTED_ARCH
${ALL_SCUDO_SUPPORTED_ARCH})
filter_available_targets(SCUDO_SUPPORTED_ARCH ${ALL_SCUDO_SUPPORTED_ARCH})
filter_available_targets(XRAY_SUPPORTED_ARCH ${ALL_XRAY_SUPPORTED_ARCH})
endif()
if (MSVC)
@ -387,6 +415,11 @@ else()
set(CAN_SYMBOLIZE 1)
endif()
find_program(GOLD_EXECUTABLE NAMES ${LLVM_DEFAULT_TARGET_TRIPLE}-ld.gold ld.gold ${LLVM_DEFAULT_TARGET_TRIPLE}-ld ld DOC "The gold linker")
if(COMPILER_RT_SUPPORTED_ARCH)
list(REMOVE_DUPLICATES COMPILER_RT_SUPPORTED_ARCH)
endif()
message(STATUS "Compiler-RT supported architectures: ${COMPILER_RT_SUPPORTED_ARCH}")
if(ANDROID)
@ -395,9 +428,14 @@ else()
set(OS_NAME "${CMAKE_SYSTEM_NAME}")
endif()
set(ALL_SANITIZERS asan;dfsan;msan;tsan;safestack;cfi;esan;scudo)
set(COMPILER_RT_SANITIZERS_TO_BUILD ${ALL_SANITIZERS} CACHE STRING
"sanitizers to build if supported on the target (all;${ALL_SANITIZERS})")
list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}")
if (SANITIZER_COMMON_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND
(OS_NAME MATCHES "Android|Darwin|Linux|FreeBSD" OR
(OS_NAME MATCHES "Windows" AND MSVC)))
(OS_NAME MATCHES "Windows" AND (NOT MINGW AND NOT CYGWIN))))
set(COMPILER_RT_HAS_SANITIZER_COMMON TRUE)
else()
set(COMPILER_RT_HAS_SANITIZER_COMMON FALSE)
@ -444,7 +482,7 @@ else()
set(COMPILER_RT_HAS_MSAN FALSE)
endif()
if (PROFILE_SUPPORTED_ARCH AND
if (PROFILE_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND
OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows")
set(COMPILER_RT_HAS_PROFILE TRUE)
else()
@ -493,3 +531,9 @@ else()
set(COMPILER_RT_HAS_SCUDO FALSE)
endif()
if (COMPILER_RT_HAS_SANITIZER_COMMON AND XRAY_SUPPORTED_ARCH AND
OS_NAME MATCHES "Linux")
set(COMPILER_RT_HAS_XRAY TRUE)
else()
set(COMPILER_RT_HAS_XRAY FALSE)
endif()

66
docs/TestingGuide.rst Normal file
View file

@ -0,0 +1,66 @@
========================================
Compiler-rt Testing Infrastructure Guide
========================================
.. contents::
:local:
Overview
========
This document is the reference manual for the compiler-rt modifications to the
testing infrastructure. Documentation for the infrastructure itself can be found at
:ref:`llvm_testing_guide`.
LLVM testing infrastructure organization
========================================
The compiler-rt testing infrastructure contains regression tests which are run
as part of the usual ``make check-all`` and are expected to always pass -- they
should be run before every commit.
Quick start
===========
The regressions tests are in the "compiler-rt" module and are normally checked
out in the directory ``llvm/projects/compiler-rt/test``. Use ``make check-all``
to run the regression tests after building compiler-rt.
REQUIRES, XFAIL, etc.
---------------------
Sometimes it is necessary to restrict a test to a specific target or mark it as
an "expected fail" or XFAIL. This is normally achieved using ``REQUIRES:`` or
``XFAIL:`` with a substring of LLVM's default target triple. Unfortunately, the
behaviour of this is somewhat quirky in compiler-rt. There are two main
pitfalls to avoid.
The first pitfall is that these directives perform a substring match on the
triple and as such ``XFAIL: mips`` affects more triples than expected. For
example, ``mips-linux-gnu``, ``mipsel-linux-gnu``, ``mips64-linux-gnu``, and
``mips64el-linux-gnu`` will all match a ``XFAIL: mips`` directive. Including a
trailing ``-`` such as in ``XFAIL: mips-`` can help to mitigate this quirk but
even that has issues as described below.
The second pitfall is that the default target triple is often inappropriate for
compiler-rt tests since compiler-rt tests may be compiled for multiple targets.
For example, a typical build on an ``x86_64-linux-gnu`` host will often run the
tests for both x86_64 and i386. In this situation ``XFAIL: x86_64`` will mark
both the x86_64 and i386 tests as an expected failure while ``XFAIL: i386``
will have no effect at all.
To remedy both pitfalls, compiler-rt tests provide a feature string which can
be used to specify a single target. This string is of the form
``target-is-${arch}`` where ``${arch}}`` is one of the values from the
following lines of the CMake output::
-- Compiler-RT supported architectures: x86_64;i386
-- Builtin supported architectures: i386;x86_64
So for example ``XFAIL: target-is-x86_64`` will mark a test as expected to fail
on x86_64 without also affecting the i386 test and ``XFAIL: target-is-i386``
will mark a test as expected to fail on i386 even if the default target triple
is ``x86_64-linux-gnu``. Directives that use these ``target-is-${arch}`` string
require exact matches so ``XFAIL: target-is-mips``,
``XFAIL: target-is-mipsel``, ``XFAIL: target-is-mips64``, and
``XFAIL: target-is-mips64el`` all refer to different MIPS targets.

View file

@ -10,11 +10,18 @@ set(SANITIZER_HEADERS
sanitizer/msan_interface.h
sanitizer/tsan_interface_atomic.h)
set(XRAY_HEADERS
xray/xray_interface.h)
set(COMPILER_RT_HEADERS
${SANITIZER_HEADERS}
${XRAY_HEADERS})
set(output_dir ${COMPILER_RT_OUTPUT_DIR}/include)
# Copy compiler-rt headers to the build tree.
set(out_files)
foreach( f ${SANITIZER_HEADERS} )
foreach( f ${COMPILER_RT_HEADERS} )
set( src ${CMAKE_CURRENT_SOURCE_DIR}/${f} )
set( dst ${output_dir}/${f} )
add_custom_command(OUTPUT ${dst}
@ -32,3 +39,7 @@ set_target_properties(compiler-rt-headers PROPERTIES FOLDER "Compiler-RT Misc")
install(FILES ${SANITIZER_HEADERS}
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
DESTINATION ${COMPILER_RT_INSTALL_PATH}/include/sanitizer)
# Install xray headers.
install(FILES ${XRAY_HEADERS}
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
DESTINATION ${COMPILER_RT_INSTALL_PATH}/include/xray)

View file

@ -117,6 +117,16 @@ extern "C" {
// Print the stack trace leading to this call. Useful for debugging user code.
void __sanitizer_print_stack_trace();
// Symbolizes the supplied 'pc' using the format string 'fmt'.
// Outputs at most 'out_buf_size' bytes into 'out_buf'.
// The format syntax is described in
// lib/sanitizer_common/sanitizer_stacktrace_printer.h.
void __sanitizer_symbolize_pc(void *pc, const char *fmt, char *out_buf,
size_t out_buf_size);
// Same as __sanitizer_symbolize_pc, but for data section (i.e. globals).
void __sanitizer_symbolize_global(void *data_ptr, const char *fmt,
char *out_buf, size_t out_buf_size);
// Sets the callback to be called right before death on error.
// Passing 0 will unset the callback.
void __sanitizer_set_death_callback(void (*callback)(void));
@ -169,7 +179,16 @@ extern "C" {
// use-after-return detection.
void __sanitizer_start_switch_fiber(void **fake_stack_save,
const void *bottom, size_t size);
void __sanitizer_finish_switch_fiber(void *fake_stack_save);
void __sanitizer_finish_switch_fiber(void *fake_stack_save,
const void **bottom_old,
size_t *size_old);
// Get full module name and calculate pc offset within it.
// Returns 1 if pc belongs to some module, 0 if module was not found.
int __sanitizer_get_module_and_offset_for_pc(void *pc, char *module_path,
size_t module_path_len,
void **pc_offset);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -23,6 +23,11 @@ extern "C" {
void __sanitizer_cov_init();
// Record and dump coverage info.
void __sanitizer_cov_dump();
// Dump collected coverage info. Sorts pcs by module into individual
// .sancov files.
void __sanitizer_dump_coverage(const uintptr_t *pcs, uintptr_t len);
// Open <name>.sancov.packed in the coverage directory and return the file
// descriptor. Returns -1 on failure, or if coverage dumping is disabled.
// This is intended for use by sandboxing code.
@ -41,13 +46,6 @@ extern "C" {
// Some of the entries in *data will be zero.
uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data);
// Set *data to the growing buffer with covered PCs and return the size
// of the buffer. The entries are never zero.
// When only unique pcs are collected, the size is equal to
// __sanitizer_get_total_unique_coverage.
// WARNING: EXPERIMENTAL API.
uintptr_t __sanitizer_get_coverage_pc_buffer(uintptr_t **data);
// The coverage instrumentation may optionally provide imprecise counters.
// Rather than exposing the counter values to the user we instead map
// the counters to a bitset.
@ -65,6 +63,7 @@ extern "C" {
// __sanitizer_get_number_of_counters bytes long and 8-aligned.
uintptr_t
__sanitizer_update_counter_bitset_and_clear_counters(uint8_t *bitset);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -0,0 +1,65 @@
//===-- xray_interface.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 XRay, a dynamic runtime instrumentation system.
//
// APIs for controlling XRay functionality explicitly.
//===----------------------------------------------------------------------===//
#ifndef XRAY_XRAY_INTERFACE_H
#define XRAY_XRAY_INTERFACE_H
#include <cstdint>
extern "C" {
enum XRayEntryType { ENTRY = 0, EXIT = 1, TAIL = 2 };
// Provide a function to invoke for when instrumentation points are hit. This is
// a user-visible control surface that overrides the default implementation. The
// function provided should take the following arguments:
//
// - function id: an identifier that indicates the id of a function; this id
// is generated by xray; the mapping between the function id
// and the actual function pointer is available through
// __xray_table.
// - entry type: identifies what kind of instrumentation point was encountered
// (function entry, function exit, etc.). See the enum
// XRayEntryType for more details.
//
// The user handler must handle correctly spurious calls after this handler is
// removed or replaced with another handler, because it would be too costly for
// XRay runtime to avoid spurious calls.
// To prevent circular calling, the handler function itself and all its
// direct&indirect callees must not be instrumented with XRay, which can be
// achieved by marking them all with: __attribute__((xray_never_instrument))
//
// Returns 1 on success, 0 on error.
extern int __xray_set_handler(void (*entry)(int32_t, XRayEntryType));
// This removes whatever the currently provided handler is. Returns 1 on
// success, 0 on error.
extern int __xray_remove_handler();
enum XRayPatchingStatus {
NOT_INITIALIZED = 0,
SUCCESS = 1,
ONGOING = 2,
FAILED = 3,
};
// This tells XRay to patch the instrumentation points. See XRayPatchingStatus
// for possible result values.
extern XRayPatchingStatus __xray_patch();
// Reverses the effect of __xray_patch(). See XRayPatchingStatus for possible
// result values.
extern XRayPatchingStatus __xray_unpatch();
}
#endif

View file

@ -0,0 +1,80 @@
//===-- xray_records.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 XRay, a dynamic runtime instrumentation system.
//
// This header exposes some record types useful for the XRay in-memory logging
// implementation.
//
//===----------------------------------------------------------------------===//
#ifndef XRAY_XRAY_RECORDS_H
#define XRAY_XRAY_RECORDS_H
namespace __xray {
enum FileTypes {
NAIVE_LOG = 0,
};
// This data structure is used to describe the contents of the file. We use this
// for versioning the supported XRay file formats.
struct alignas(32) XRayFileHeader {
uint16_t Version = 0;
// The type of file we're writing out. See the FileTypes enum for more
// information. This allows different implementations of the XRay logging to
// have different files for different information being stored.
uint16_t Type = 0;
// What follows are a set of flags that indicate useful things for when
// reading the data in the file.
bool ConstantTSC : 1;
bool NonstopTSC : 1;
// The frequency by which TSC increases per-second.
alignas(8) uint64_t CycleFrequency = 0;
} __attribute__((packed));
static_assert(sizeof(XRayFileHeader) == 32, "XRayFileHeader != 32 bytes");
enum RecordTypes {
NORMAL = 0,
};
struct alignas(32) XRayRecord {
// This is the type of the record being written. We use 16 bits to allow us to
// treat this as a discriminant, and so that the first 4 bytes get packed
// properly. See RecordTypes for more supported types.
uint16_t RecordType = 0;
// The CPU where the thread is running. We assume number of CPUs <= 256.
uint8_t CPU = 0;
// The type of the event. Usually either ENTER = 0 or EXIT = 1.
uint8_t Type = 0;
// The function ID for the record.
int32_t FuncId = 0;
// Get the full 8 bytes of the TSC when we get the log record.
uint64_t TSC = 0;
// The thread ID for the currently running thread.
uint32_t TId = 0;
// Use some bytes in the end of the record for buffers.
char Buffer[4] = {};
} __attribute__((packed));
static_assert(sizeof(XRayRecord) == 32, "XRayRecord != 32 bytes");
} // namespace __xray
#endif // XRAY_XRAY_RECORDS_H

View file

@ -4,56 +4,59 @@
include(AddCompilerRT)
include(SanitizerUtils)
# Hoist the building of sanitizer_common on whether we're building either the
# sanitizers or xray (or both).
#
#TODO: Refactor sanitizer_common into smaller pieces (e.g. flag parsing, utils).
if (COMPILER_RT_HAS_SANITIZER_COMMON AND
(COMPILER_RT_BUILD_SANITIZERS OR COMPILER_RT_BUILD_XRAY))
add_subdirectory(sanitizer_common)
endif()
if(COMPILER_RT_BUILD_BUILTINS)
add_subdirectory(builtins)
endif()
if(COMPILER_RT_BUILD_SANITIZERS)
if(COMPILER_RT_HAS_INTERCEPTION)
add_subdirectory(interception)
function(compiler_rt_build_runtime runtime)
string(TOUPPER ${runtime} runtime_uppercase)
if(COMPILER_RT_HAS_${runtime_uppercase})
add_subdirectory(${runtime})
foreach(directory ${ARGN})
add_subdirectory(${directory})
endforeach()
endif()
endfunction()
function(compiler_rt_build_sanitizer sanitizer)
string(TOUPPER ${sanitizer} sanitizer_uppercase)
string(TOLOWER ${sanitizer} sanitizer_lowercase)
list(FIND COMPILER_RT_SANITIZERS_TO_BUILD ${sanitizer_lowercase} result)
if(NOT ${result} EQUAL -1)
compiler_rt_build_runtime(${sanitizer} ${ARGN})
endif()
endfunction()
if(COMPILER_RT_BUILD_SANITIZERS)
compiler_rt_build_runtime(interception)
if(COMPILER_RT_HAS_SANITIZER_COMMON)
add_subdirectory(sanitizer_common)
add_subdirectory(stats)
add_subdirectory(lsan)
add_subdirectory(ubsan)
endif()
if(COMPILER_RT_HAS_ASAN)
add_subdirectory(asan)
endif()
compiler_rt_build_sanitizer(asan)
compiler_rt_build_sanitizer(dfsan)
compiler_rt_build_sanitizer(msan)
compiler_rt_build_sanitizer(tsan tsan/dd)
compiler_rt_build_sanitizer(safestack)
compiler_rt_build_sanitizer(cfi)
compiler_rt_build_sanitizer(esan)
compiler_rt_build_sanitizer(scudo)
if(COMPILER_RT_HAS_DFSAN)
add_subdirectory(dfsan)
endif()
if(COMPILER_RT_HAS_MSAN)
add_subdirectory(msan)
endif()
if(COMPILER_RT_HAS_PROFILE)
add_subdirectory(profile)
endif()
if(COMPILER_RT_HAS_TSAN)
add_subdirectory(tsan)
add_subdirectory(tsan/dd)
endif()
if(COMPILER_RT_HAS_SAFESTACK)
add_subdirectory(safestack)
endif()
if(COMPILER_RT_HAS_CFI)
add_subdirectory(cfi)
endif()
if(COMPILER_RT_HAS_ESAN)
add_subdirectory(esan)
endif()
if(COMPILER_RT_HAS_SCUDO)
add_subdirectory(scudo)
endif()
compiler_rt_build_runtime(profile)
endif()
if(COMPILER_RT_BUILD_XRAY)
compiler_rt_build_runtime(xray)
endif()

View file

@ -1,13 +0,0 @@
#===- lib/Makefile.mk --------------------------------------*- Makefile -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
SubDirs :=
# Add submodules.
SubDirs += builtins

View file

@ -4,9 +4,12 @@ set(ASAN_SOURCES
asan_allocator.cc
asan_activation.cc
asan_debugging.cc
asan_descriptions.cc
asan_errors.cc
asan_fake_stack.cc
asan_flags.cc
asan_globals.cc
asan_globals_win.cc
asan_interceptors.cc
asan_linux.cc
asan_mac.cc
@ -35,14 +38,9 @@ include_directories(..)
set(ASAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_rtti_flag(OFF ASAN_CFLAGS)
set(ASAN_COMMON_DEFINITIONS
ASAN_HAS_EXCEPTIONS=1)
set(ASAN_DYNAMIC_LINK_FLAGS)
if(ANDROID)
list(APPEND ASAN_COMMON_DEFINITIONS
ASAN_LOW_MEMORY=1)
# On Android, -z global does not do what it is documented to do.
# On Android, -z global moves the library ahead in the lookup order,
# placing it right after the LD_PRELOADs. This is used to compensate for the fact
@ -105,8 +103,7 @@ if(NOT APPLE)
endif()
# Build ASan runtimes shipped with Clang.
add_custom_target(asan)
set_target_properties(asan PROPERTIES FOLDER "Compiler-RT Misc")
add_compiler_rt_component(asan)
if(APPLE)
add_compiler_rt_runtime(clang_rt.asan
@ -207,15 +204,25 @@ else()
STATIC
ARCHS ${arch}
SOURCES asan_win_dll_thunk.cc
asan_globals_win.cc
$<TARGET_OBJECTS:RTInterception.${arch}>
CFLAGS ${ASAN_CFLAGS} -DASAN_DLL_THUNK
DEFS ${ASAN_COMMON_DEFINITIONS}
PARENT_TARGET asan)
set(DYNAMIC_RUNTIME_THUNK_CFLAGS "-DASAN_DYNAMIC_RUNTIME_THUNK")
if(MSVC)
list(APPEND DYNAMIC_RUNTIME_THUNK_CFLAGS "-Zl")
elseif(CMAKE_C_COMPILER_ID MATCHES Clang)
list(APPEND DYNAMIC_RUNTIME_THUNK_CFLAGS "-nodefaultlibs")
endif()
add_compiler_rt_runtime(clang_rt.asan_dynamic_runtime_thunk
STATIC
ARCHS ${arch}
SOURCES asan_win_dynamic_runtime_thunk.cc
CFLAGS ${ASAN_CFLAGS} -DASAN_DYNAMIC_RUNTIME_THUNK -Zl
asan_globals_win.cc
CFLAGS ${ASAN_CFLAGS} ${DYNAMIC_RUNTIME_THUNK_CFLAGS}
DEFS ${ASAN_COMMON_DEFINITIONS}
PARENT_TARGET asan)
endif()
@ -223,7 +230,6 @@ else()
endif()
add_compiler_rt_resource_file(asan_blacklist asan_blacklist.txt asan)
add_dependencies(compiler-rt asan)
add_subdirectory(scripts)

View file

@ -79,11 +79,13 @@ static struct AsanDeactivatedFlags {
Report(
"quarantine_size_mb %d, max_redzone %d, poison_heap %d, "
"malloc_context_size %d, alloc_dealloc_mismatch %d, "
"allocator_may_return_null %d, coverage %d, coverage_dir %s\n",
"allocator_may_return_null %d, coverage %d, coverage_dir %s, "
"allocator_release_to_os_interval_ms %d\n",
allocator_options.quarantine_size_mb, allocator_options.max_redzone,
poison_heap, malloc_context_size,
allocator_options.alloc_dealloc_mismatch,
allocator_options.may_return_null, coverage, coverage_dir);
allocator_options.may_return_null, coverage, coverage_dir,
allocator_options.release_to_os_interval_ms);
}
} asan_deactivated_flags;

View file

@ -33,3 +33,4 @@ COMMON_ACTIVATION_FLAG(bool, coverage)
COMMON_ACTIVATION_FLAG(const char *, coverage_dir)
COMMON_ACTIVATION_FLAG(int, verbosity)
COMMON_ACTIVATION_FLAG(bool, help)
COMMON_ACTIVATION_FLAG(s32, allocator_release_to_os_interval_ms)

View file

@ -207,25 +207,27 @@ QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) {
void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) {
quarantine_size_mb = f->quarantine_size_mb;
thread_local_quarantine_size_kb = f->thread_local_quarantine_size_kb;
min_redzone = f->redzone;
max_redzone = f->max_redzone;
may_return_null = cf->allocator_may_return_null;
alloc_dealloc_mismatch = f->alloc_dealloc_mismatch;
release_to_os_interval_ms = cf->allocator_release_to_os_interval_ms;
}
void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) {
f->quarantine_size_mb = quarantine_size_mb;
f->thread_local_quarantine_size_kb = thread_local_quarantine_size_kb;
f->redzone = min_redzone;
f->max_redzone = max_redzone;
cf->allocator_may_return_null = may_return_null;
f->alloc_dealloc_mismatch = alloc_dealloc_mismatch;
cf->allocator_release_to_os_interval_ms = release_to_os_interval_ms;
}
struct Allocator {
static const uptr kMaxAllowedMallocSize =
FIRST_32_SECOND_64(3UL << 30, 1ULL << 40);
static const uptr kMaxThreadLocalQuarantine =
FIRST_32_SECOND_64(1 << 18, 1 << 20);
AsanAllocator allocator;
AsanQuarantine quarantine;
@ -254,7 +256,7 @@ struct Allocator {
void SharedInitCode(const AllocatorOptions &options) {
CheckOptions(options);
quarantine.Init((uptr)options.quarantine_size_mb << 20,
kMaxThreadLocalQuarantine);
(uptr)options.thread_local_quarantine_size_kb << 10);
atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch,
memory_order_release);
atomic_store(&min_redzone, options.min_redzone, memory_order_release);
@ -262,22 +264,59 @@ struct Allocator {
}
void Initialize(const AllocatorOptions &options) {
allocator.Init(options.may_return_null);
allocator.Init(options.may_return_null, options.release_to_os_interval_ms);
SharedInitCode(options);
}
void RePoisonChunk(uptr chunk) {
// This could a user-facing chunk (with redzones), or some internal
// housekeeping chunk, like TransferBatch. Start by assuming the former.
AsanChunk *ac = GetAsanChunk((void *)chunk);
uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)ac);
uptr beg = ac->Beg();
uptr end = ac->Beg() + ac->UsedSize(true);
uptr chunk_end = chunk + allocated_size;
if (chunk < beg && beg < end && end <= chunk_end) {
// Looks like a valid AsanChunk. Or maybe not. Be conservative and only
// poison the redzones.
PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic);
uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY);
FastPoisonShadowPartialRightRedzone(
end_aligned_down, end - end_aligned_down,
chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic);
} else {
// This can not be an AsanChunk. Poison everything. It may be reused as
// AsanChunk later.
PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic);
}
}
void ReInitialize(const AllocatorOptions &options) {
allocator.SetMayReturnNull(options.may_return_null);
allocator.SetReleaseToOSIntervalMs(options.release_to_os_interval_ms);
SharedInitCode(options);
// Poison all existing allocation's redzones.
if (CanPoisonMemory()) {
allocator.ForceLock();
allocator.ForEachChunk(
[](uptr chunk, void *alloc) {
((Allocator *)alloc)->RePoisonChunk(chunk);
},
this);
allocator.ForceUnlock();
}
}
void GetOptions(AllocatorOptions *options) const {
options->quarantine_size_mb = quarantine.GetSize() >> 20;
options->thread_local_quarantine_size_kb = quarantine.GetCacheSize() >> 10;
options->min_redzone = atomic_load(&min_redzone, memory_order_acquire);
options->max_redzone = atomic_load(&max_redzone, memory_order_acquire);
options->may_return_null = allocator.MayReturnNull();
options->alloc_dealloc_mismatch =
atomic_load(&alloc_dealloc_mismatch, memory_order_acquire);
options->release_to_os_interval_ms = allocator.ReleaseToOSIntervalMs();
}
// -------------------- Helper methods. -------------------------
@ -356,7 +395,7 @@ struct Allocator {
if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n",
(void*)size);
return allocator.ReturnNullOrDie();
return allocator.ReturnNullOrDieOnBadRequest();
}
AsanThread *t = GetCurrentThread();
@ -373,8 +412,7 @@ struct Allocator {
allocator.Allocate(cache, needed_size, 8, false, check_rss_limit);
}
if (!allocated)
return allocator.ReturnNullOrDie();
if (!allocated) return allocator.ReturnNullOrDieOnOOM();
if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) {
// Heap poisoning is enabled, but the allocator provides an unpoisoned
@ -530,7 +568,7 @@ struct Allocator {
if (delete_size && flags()->new_delete_type_mismatch &&
delete_size != m->UsedSize()) {
ReportNewDeleteSizeMismatch(p, m->UsedSize(), delete_size, stack);
ReportNewDeleteSizeMismatch(p, delete_size, stack);
}
QuarantineChunk(m, ptr, stack, alloc_type);
@ -563,7 +601,7 @@ struct Allocator {
void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
if (CallocShouldReturnNullDueToOverflow(size, nmemb))
return allocator.ReturnNullOrDie();
return allocator.ReturnNullOrDieOnBadRequest();
void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false);
// If the memory comes from the secondary allocator no need to clear it
// as it comes directly from mmap.
@ -673,6 +711,9 @@ uptr AsanChunkView::End() { return Beg() + UsedSize(); }
uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); }
uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
AllocType AsanChunkView::GetAllocType() {
return (AllocType)chunk_->alloc_type;
}
static StackTrace GetStackTraceFromId(u32 id) {
CHECK(id);
@ -707,6 +748,9 @@ void GetAllocatorOptions(AllocatorOptions *options) {
AsanChunkView FindHeapChunkByAddress(uptr addr) {
return instance.FindHeapChunkByAddress(addr);
}
AsanChunkView FindHeapChunkByAllocBeg(uptr addr) {
return AsanChunkView(instance.GetAsanChunk(reinterpret_cast<void*>(addr)));
}
void AsanThreadLocalMallocStorage::CommitBack() {
instance.CommitBack(this);

View file

@ -33,10 +33,12 @@ struct AsanChunk;
struct AllocatorOptions {
u32 quarantine_size_mb;
u32 thread_local_quarantine_size_kb;
u16 min_redzone;
u16 max_redzone;
u8 may_return_null;
u8 alloc_dealloc_mismatch;
s32 release_to_os_interval_ms;
void SetFrom(const Flags *f, const CommonFlags *cf);
void CopyTo(Flags *f, CommonFlags *cf);
@ -62,6 +64,7 @@ class AsanChunkView {
u32 GetFreeStackId();
StackTrace GetAllocStack();
StackTrace GetFreeStack();
AllocType GetAllocType();
bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) {
if (addr >= Beg() && (addr + access_size) <= End()) {
*offset = addr - Beg();
@ -90,6 +93,7 @@ class AsanChunkView {
};
AsanChunkView FindHeapChunkByAddress(uptr address);
AsanChunkView FindHeapChunkByAllocBeg(uptr address);
// List of AsanChunks with total size.
class AsanChunkFifoList: public IntrusiveList<AsanChunk> {
@ -117,18 +121,36 @@ struct AsanMapUnmapCallback {
# if defined(__powerpc64__)
const uptr kAllocatorSpace = 0xa0000000000ULL;
const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
typedef DefaultSizeClassMap SizeClassMap;
# elif defined(__aarch64__) && SANITIZER_ANDROID
const uptr kAllocatorSpace = 0x3000000000ULL;
const uptr kAllocatorSize = 0x2000000000ULL; // 128G.
typedef VeryCompactSizeClassMap SizeClassMap;
# elif defined(__aarch64__)
// AArch64/SANITIZIER_CAN_USER_ALLOCATOR64 is only for 42-bit VMA
// AArch64/SANITIZER_CAN_USER_ALLOCATOR64 is only for 42-bit VMA
// so no need to different values for different VMA.
const uptr kAllocatorSpace = 0x10000000000ULL;
const uptr kAllocatorSize = 0x10000000000ULL; // 3T.
typedef DefaultSizeClassMap SizeClassMap;
# elif SANITIZER_WINDOWS
const uptr kAllocatorSpace = ~(uptr)0;
const uptr kAllocatorSize = 0x8000000000ULL; // 500G
typedef DefaultSizeClassMap SizeClassMap;
# else
const uptr kAllocatorSpace = 0x600000000000ULL;
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
# endif
typedef DefaultSizeClassMap SizeClassMap;
typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0 /*metadata*/,
SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
# endif
struct AP64 { // Allocator64 parameters. Deliberately using a short name.
static const uptr kSpaceBeg = kAllocatorSpace;
static const uptr kSpaceSize = kAllocatorSize;
static const uptr kMetadataSize = 0;
typedef __asan::SizeClassMap SizeClassMap;
typedef AsanMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
typedef SizeClassAllocator64<AP64> PrimaryAllocator;
#else // Fallback to SizeClassAllocator32.
static const uptr kRegionSizeLog = 20;
static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;

View file

@ -14,74 +14,39 @@
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
#include "asan_descriptions.h"
#include "asan_flags.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include "asan_report.h"
#include "asan_thread.h"
namespace __asan {
namespace {
using namespace __asan;
void GetInfoForStackVar(uptr addr, AddressDescription *descr, AsanThread *t) {
descr->name[0] = 0;
descr->region_address = 0;
descr->region_size = 0;
descr->region_kind = "stack";
AsanThread::StackFrameAccess access;
if (!t->GetStackFrameAccessByAddr(addr, &access))
return;
static void FindInfoForStackVar(uptr addr, const char *frame_descr, uptr offset,
char *name, uptr name_size,
uptr &region_address, uptr &region_size) {
InternalMmapVector<StackVarDescr> vars(16);
if (!ParseFrameDescription(access.frame_descr, &vars)) {
if (!ParseFrameDescription(frame_descr, &vars)) {
return;
}
for (uptr i = 0; i < vars.size(); i++) {
if (access.offset <= vars[i].beg + vars[i].size) {
internal_strncat(descr->name, vars[i].name_pos,
Min(descr->name_size, vars[i].name_len));
descr->region_address = addr - (access.offset - vars[i].beg);
descr->region_size = vars[i].size;
if (offset <= vars[i].beg + vars[i].size) {
// We use name_len + 1 because strlcpy will guarantee a \0 at the end, so
// if we're limiting the copy due to name_len, we add 1 to ensure we copy
// the whole name and then terminate with '\0'.
internal_strlcpy(name, vars[i].name_pos,
Min(name_size, vars[i].name_len + 1));
region_address = addr - (offset - vars[i].beg);
region_size = vars[i].size;
return;
}
}
}
void GetInfoForHeapAddress(uptr addr, AddressDescription *descr) {
AsanChunkView chunk = FindHeapChunkByAddress(addr);
descr->name[0] = 0;
descr->region_address = 0;
descr->region_size = 0;
if (!chunk.IsValid()) {
descr->region_kind = "heap-invalid";
return;
}
descr->region_address = chunk.Beg();
descr->region_size = chunk.UsedSize();
descr->region_kind = "heap";
}
void AsanLocateAddress(uptr addr, AddressDescription *descr) {
if (DescribeAddressIfShadow(addr, descr, /* print */ false)) {
return;
}
if (GetInfoForAddressIfGlobal(addr, descr)) {
return;
}
asanThreadRegistry().Lock();
AsanThread *thread = FindThreadByStackAddress(addr);
asanThreadRegistry().Unlock();
if (thread) {
GetInfoForStackVar(addr, descr, thread);
return;
}
GetInfoForHeapAddress(addr, descr);
}
static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
bool alloc_stack) {
AsanChunkView chunk = FindHeapChunkByAddress(addr);
if (!chunk.IsValid()) return 0;
@ -108,18 +73,58 @@ static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
return 0;
}
} // namespace __asan
using namespace __asan;
} // namespace
SANITIZER_INTERFACE_ATTRIBUTE
const char *__asan_locate_address(uptr addr, char *name, uptr name_size,
uptr *region_address, uptr *region_size) {
AddressDescription descr = { name, name_size, 0, 0, nullptr };
AsanLocateAddress(addr, &descr);
if (region_address) *region_address = descr.region_address;
if (region_size) *region_size = descr.region_size;
return descr.region_kind;
uptr *region_address_ptr,
uptr *region_size_ptr) {
AddressDescription descr(addr);
uptr region_address = 0;
uptr region_size = 0;
const char *region_kind = nullptr;
if (name && name_size > 0) name[0] = 0;
if (auto shadow = descr.AsShadow()) {
// region_{address,size} are already 0
switch (shadow->kind) {
case kShadowKindLow:
region_kind = "low shadow";
break;
case kShadowKindGap:
region_kind = "shadow gap";
break;
case kShadowKindHigh:
region_kind = "high shadow";
break;
}
} else if (auto heap = descr.AsHeap()) {
region_kind = "heap";
region_address = heap->chunk_access.chunk_begin;
region_size = heap->chunk_access.chunk_size;
} else if (auto stack = descr.AsStack()) {
region_kind = "stack";
if (!stack->frame_descr) {
// region_{address,size} are already 0
} else {
FindInfoForStackVar(addr, stack->frame_descr, stack->offset, name,
name_size, region_address, region_size);
}
} else if (auto global = descr.AsGlobal()) {
region_kind = "global";
auto &g = global->globals[0];
internal_strlcpy(name, g.name, name_size);
region_address = g.beg;
region_size = g.size;
} else {
// region_{address,size} are already 0
region_kind = "heap-invalid";
}
CHECK(region_kind);
if (region_address_ptr) *region_address_ptr = region_address;
if (region_size_ptr) *region_size_ptr = region_size;
return region_kind;
}
SANITIZER_INTERFACE_ATTRIBUTE

View file

@ -0,0 +1,486 @@
//===-- asan_descriptions.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.
//
// ASan functions for getting information about an address and/or printing it.
//===----------------------------------------------------------------------===//
#include "asan_descriptions.h"
#include "asan_mapping.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
namespace __asan {
// Return " (thread_name) " or an empty string if the name is empty.
const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[],
uptr buff_len) {
const char *name = t->name;
if (name[0] == '\0') return "";
buff[0] = 0;
internal_strncat(buff, " (", 3);
internal_strncat(buff, name, buff_len - 4);
internal_strncat(buff, ")", 2);
return buff;
}
const char *ThreadNameWithParenthesis(u32 tid, char buff[], uptr buff_len) {
if (tid == kInvalidTid) return "";
asanThreadRegistry().CheckLocked();
AsanThreadContext *t = GetThreadContextByTidLocked(tid);
return ThreadNameWithParenthesis(t, buff, buff_len);
}
void DescribeThread(AsanThreadContext *context) {
CHECK(context);
asanThreadRegistry().CheckLocked();
// No need to announce the main thread.
if (context->tid == 0 || context->announced) {
return;
}
context->announced = true;
char tname[128];
InternalScopedString str(1024);
str.append("Thread T%d%s", context->tid,
ThreadNameWithParenthesis(context->tid, tname, sizeof(tname)));
if (context->parent_tid == kInvalidTid) {
str.append(" created by unknown thread\n");
Printf("%s", str.data());
return;
}
str.append(
" created by T%d%s here:\n", context->parent_tid,
ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname)));
Printf("%s", str.data());
StackDepotGet(context->stack_id).Print();
// Recursively described parent thread if needed.
if (flags()->print_full_thread_history) {
AsanThreadContext *parent_context =
GetThreadContextByTidLocked(context->parent_tid);
DescribeThread(parent_context);
}
}
// Shadow descriptions
static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) {
CHECK(!AddrIsInMem(addr));
if (AddrIsInShadowGap(addr)) {
*shadow_kind = kShadowKindGap;
} else if (AddrIsInHighShadow(addr)) {
*shadow_kind = kShadowKindHigh;
} else if (AddrIsInLowShadow(addr)) {
*shadow_kind = kShadowKindLow;
} else {
CHECK(0 && "Address is not in memory and not in shadow?");
return false;
}
return true;
}
bool DescribeAddressIfShadow(uptr addr) {
ShadowAddressDescription descr;
if (!GetShadowAddressInformation(addr, &descr)) return false;
descr.Print();
return true;
}
bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr) {
if (AddrIsInMem(addr)) return false;
ShadowKind shadow_kind;
if (!GetShadowKind(addr, &shadow_kind)) return false;
if (shadow_kind != kShadowKindGap) descr->shadow_byte = *(u8 *)addr;
descr->addr = addr;
descr->kind = shadow_kind;
return true;
}
// Heap descriptions
static void GetAccessToHeapChunkInformation(ChunkAccess *descr,
AsanChunkView chunk, uptr addr,
uptr access_size) {
descr->bad_addr = addr;
if (chunk.AddrIsAtLeft(addr, access_size, &descr->offset)) {
descr->access_type = kAccessTypeLeft;
} else if (chunk.AddrIsAtRight(addr, access_size, &descr->offset)) {
descr->access_type = kAccessTypeRight;
if (descr->offset < 0) {
descr->bad_addr -= descr->offset;
descr->offset = 0;
}
} else if (chunk.AddrIsInside(addr, access_size, &descr->offset)) {
descr->access_type = kAccessTypeInside;
} else {
descr->access_type = kAccessTypeUnknown;
}
descr->chunk_begin = chunk.Beg();
descr->chunk_size = chunk.UsedSize();
descr->alloc_type = chunk.GetAllocType();
}
static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) {
Decorator d;
InternalScopedString str(4096);
str.append("%s", d.Location());
switch (descr.access_type) {
case kAccessTypeLeft:
str.append("%p is located %zd bytes to the left of",
(void *)descr.bad_addr, descr.offset);
break;
case kAccessTypeRight:
str.append("%p is located %zd bytes to the right of",
(void *)descr.bad_addr, descr.offset);
break;
case kAccessTypeInside:
str.append("%p is located %zd bytes inside of", (void *)descr.bad_addr,
descr.offset);
break;
case kAccessTypeUnknown:
str.append(
"%p is located somewhere around (this is AddressSanitizer bug!)",
(void *)descr.bad_addr);
}
str.append(" %zu-byte region [%p,%p)\n", descr.chunk_size,
(void *)descr.chunk_begin,
(void *)(descr.chunk_begin + descr.chunk_size));
str.append("%s", d.EndLocation());
Printf("%s", str.data());
}
bool GetHeapAddressInformation(uptr addr, uptr access_size,
HeapAddressDescription *descr) {
AsanChunkView chunk = FindHeapChunkByAddress(addr);
if (!chunk.IsValid()) {
return false;
}
descr->addr = addr;
GetAccessToHeapChunkInformation(&descr->chunk_access, chunk, addr,
access_size);
CHECK_NE(chunk.AllocTid(), kInvalidTid);
descr->alloc_tid = chunk.AllocTid();
descr->alloc_stack_id = chunk.GetAllocStackId();
descr->free_tid = chunk.FreeTid();
if (descr->free_tid != kInvalidTid)
descr->free_stack_id = chunk.GetFreeStackId();
return true;
}
static StackTrace GetStackTraceFromId(u32 id) {
CHECK(id);
StackTrace res = StackDepotGet(id);
CHECK(res.trace);
return res;
}
bool DescribeAddressIfHeap(uptr addr, uptr access_size) {
HeapAddressDescription descr;
if (!GetHeapAddressInformation(addr, access_size, &descr)) {
Printf(
"AddressSanitizer can not describe address in more detail "
"(wild memory access suspected).\n");
return false;
}
descr.Print();
return true;
}
// Stack descriptions
bool GetStackAddressInformation(uptr addr, uptr access_size,
StackAddressDescription *descr) {
AsanThread *t = FindThreadByStackAddress(addr);
if (!t) return false;
descr->addr = addr;
descr->tid = t->tid();
// Try to fetch precise stack frame for this access.
AsanThread::StackFrameAccess access;
if (!t->GetStackFrameAccessByAddr(addr, &access)) {
descr->frame_descr = nullptr;
return true;
}
descr->offset = access.offset;
descr->access_size = access_size;
descr->frame_pc = access.frame_pc;
descr->frame_descr = access.frame_descr;
#if SANITIZER_PPC64V1
// On PowerPC64 ELFv1, the address of a function actually points to a
// three-doubleword data structure with the first field containing
// the address of the function's code.
descr->frame_pc = *reinterpret_cast<uptr *>(descr->frame_pc);
#endif
descr->frame_pc += 16;
return true;
}
static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
uptr access_size, uptr prev_var_end,
uptr next_var_beg) {
uptr var_end = var.beg + var.size;
uptr addr_end = addr + access_size;
const char *pos_descr = nullptr;
// If the variable [var.beg, var_end) is the nearest variable to the
// current memory access, indicate it in the log.
if (addr >= var.beg) {
if (addr_end <= var_end)
pos_descr = "is inside"; // May happen if this is a use-after-return.
else if (addr < var_end)
pos_descr = "partially overflows";
else if (addr_end <= next_var_beg &&
next_var_beg - addr_end >= addr - var_end)
pos_descr = "overflows";
} else {
if (addr_end > var.beg)
pos_descr = "partially underflows";
else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end)
pos_descr = "underflows";
}
InternalScopedString str(1024);
str.append(" [%zd, %zd)", var.beg, var_end);
// Render variable name.
str.append(" '");
for (uptr i = 0; i < var.name_len; ++i) {
str.append("%c", var.name_pos[i]);
}
str.append("'");
if (pos_descr) {
Decorator d;
// FIXME: we may want to also print the size of the access here,
// but in case of accesses generated by memset it may be confusing.
str.append("%s <== Memory access at offset %zd %s this variable%s\n",
d.Location(), addr, pos_descr, d.EndLocation());
} else {
str.append("\n");
}
Printf("%s", str.data());
}
bool DescribeAddressIfStack(uptr addr, uptr access_size) {
StackAddressDescription descr;
if (!GetStackAddressInformation(addr, access_size, &descr)) return false;
descr.Print();
return true;
}
// Global descriptions
static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
const __asan_global &g) {
InternalScopedString str(4096);
Decorator d;
str.append("%s", d.Location());
if (addr < g.beg) {
str.append("%p is located %zd bytes to the left", (void *)addr,
g.beg - addr);
} else if (addr + access_size > g.beg + g.size) {
if (addr < g.beg + g.size) addr = g.beg + g.size;
str.append("%p is located %zd bytes to the right", (void *)addr,
addr - (g.beg + g.size));
} else {
// Can it happen?
str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
}
str.append(" of global variable '%s' defined in '",
MaybeDemangleGlobalName(g.name));
PrintGlobalLocation(&str, g);
str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
str.append("%s", d.EndLocation());
PrintGlobalNameIfASCII(&str, g);
Printf("%s", str.data());
}
bool GetGlobalAddressInformation(uptr addr, uptr access_size,
GlobalAddressDescription *descr) {
descr->addr = addr;
int globals_num = GetGlobalsForAddress(addr, descr->globals, descr->reg_sites,
ARRAY_SIZE(descr->globals));
descr->size = globals_num;
descr->access_size = access_size;
return globals_num != 0;
}
bool DescribeAddressIfGlobal(uptr addr, uptr access_size,
const char *bug_type) {
GlobalAddressDescription descr;
if (!GetGlobalAddressInformation(addr, access_size, &descr)) return false;
descr.Print(bug_type);
return true;
}
void ShadowAddressDescription::Print() const {
Printf("Address %p is located in the %s area.\n", addr, ShadowNames[kind]);
}
void GlobalAddressDescription::Print(const char *bug_type) const {
for (int i = 0; i < size; i++) {
DescribeAddressRelativeToGlobal(addr, access_size, globals[i]);
if (bug_type &&
0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
reg_sites[i]) {
Printf(" registered at:\n");
StackDepotGet(reg_sites[i]).Print();
}
}
}
void StackAddressDescription::Print() const {
Decorator d;
char tname[128];
Printf("%s", d.Location());
Printf("Address %p is located in stack of thread T%d%s", addr, tid,
ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
if (!frame_descr) {
Printf("%s\n", d.EndLocation());
return;
}
Printf(" at offset %zu in frame%s\n", offset, d.EndLocation());
// Now we print the frame where the alloca has happened.
// We print this frame as a stack trace with one element.
// The symbolizer may print more than one frame if inlining was involved.
// The frame numbers may be different than those in the stack trace printed
// previously. That's unfortunate, but I have no better solution,
// especially given that the alloca may be from entirely different place
// (e.g. use-after-scope, or different thread's stack).
Printf("%s", d.EndLocation());
StackTrace alloca_stack(&frame_pc, 1);
alloca_stack.Print();
InternalMmapVector<StackVarDescr> vars(16);
if (!ParseFrameDescription(frame_descr, &vars)) {
Printf(
"AddressSanitizer can't parse the stack frame "
"descriptor: |%s|\n",
frame_descr);
// 'addr' is a stack address, so return true even if we can't parse frame
return;
}
uptr n_objects = vars.size();
// Report the number of stack objects.
Printf(" This frame has %zu object(s):\n", n_objects);
// Report all objects in this frame.
for (uptr i = 0; i < n_objects; i++) {
uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;
uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);
PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end,
next_var_beg);
}
Printf(
"HINT: this may be a false positive if your program uses "
"some custom stack unwind mechanism or swapcontext\n");
if (SANITIZER_WINDOWS)
Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");
else
Printf(" (longjmp and C++ exceptions *are* supported)\n");
DescribeThread(GetThreadContextByTidLocked(tid));
}
void HeapAddressDescription::Print() const {
PrintHeapChunkAccess(addr, chunk_access);
asanThreadRegistry().CheckLocked();
AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid);
StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id);
char tname[128];
Decorator d;
AsanThreadContext *free_thread = nullptr;
if (free_tid != kInvalidTid) {
free_thread = GetThreadContextByTidLocked(free_tid);
Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
free_thread->tid,
ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
d.EndAllocation());
StackTrace free_stack = GetStackTraceFromId(free_stack_id);
free_stack.Print();
Printf("%spreviously allocated by thread T%d%s here:%s\n", d.Allocation(),
alloc_thread->tid,
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
d.EndAllocation());
} else {
Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
alloc_thread->tid,
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
d.EndAllocation());
}
alloc_stack.Print();
DescribeThread(GetCurrentThread());
if (free_thread) DescribeThread(free_thread);
DescribeThread(alloc_thread);
}
AddressDescription::AddressDescription(uptr addr, uptr access_size,
bool shouldLockThreadRegistry) {
if (GetShadowAddressInformation(addr, &data.shadow)) {
data.kind = kAddressKindShadow;
return;
}
if (GetHeapAddressInformation(addr, access_size, &data.heap)) {
data.kind = kAddressKindHeap;
return;
}
bool isStackMemory = false;
if (shouldLockThreadRegistry) {
ThreadRegistryLock l(&asanThreadRegistry());
isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
} else {
isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
}
if (isStackMemory) {
data.kind = kAddressKindStack;
return;
}
if (GetGlobalAddressInformation(addr, access_size, &data.global)) {
data.kind = kAddressKindGlobal;
return;
}
data.kind = kAddressKindWild;
addr = 0;
}
void PrintAddressDescription(uptr addr, uptr access_size,
const char *bug_type) {
ShadowAddressDescription shadow_descr;
if (GetShadowAddressInformation(addr, &shadow_descr)) {
shadow_descr.Print();
return;
}
GlobalAddressDescription global_descr;
if (GetGlobalAddressInformation(addr, access_size, &global_descr)) {
global_descr.Print(bug_type);
return;
}
StackAddressDescription stack_descr;
if (GetStackAddressInformation(addr, access_size, &stack_descr)) {
stack_descr.Print();
return;
}
HeapAddressDescription heap_descr;
if (GetHeapAddressInformation(addr, access_size, &heap_descr)) {
heap_descr.Print();
return;
}
// We exhausted our possibilities. Bail out.
Printf(
"AddressSanitizer can not describe address in more detail "
"(wild memory access suspected).\n");
}
} // namespace __asan

View file

@ -0,0 +1,253 @@
//===-- asan_descriptions.h -------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// ASan-private header for asan_descriptions.cc.
// TODO(filcab): Most struct definitions should move to the interface headers.
//===----------------------------------------------------------------------===//
#ifndef ASAN_DESCRIPTIONS_H
#define ASAN_DESCRIPTIONS_H
#include "asan_allocator.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
namespace __asan {
void DescribeThread(AsanThreadContext *context);
static inline void DescribeThread(AsanThread *t) {
if (t) DescribeThread(t->context());
}
const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[],
uptr buff_len);
const char *ThreadNameWithParenthesis(u32 tid, char buff[], uptr buff_len);
class Decorator : public __sanitizer::SanitizerCommonDecorator {
public:
Decorator() : SanitizerCommonDecorator() {}
const char *Access() { return Blue(); }
const char *EndAccess() { return Default(); }
const char *Location() { return Green(); }
const char *EndLocation() { return Default(); }
const char *Allocation() { return Magenta(); }
const char *EndAllocation() { return Default(); }
const char *ShadowByte(u8 byte) {
switch (byte) {
case kAsanHeapLeftRedzoneMagic:
case kAsanArrayCookieMagic:
return Red();
case kAsanHeapFreeMagic:
return Magenta();
case kAsanStackLeftRedzoneMagic:
case kAsanStackMidRedzoneMagic:
case kAsanStackRightRedzoneMagic:
return Red();
case kAsanStackAfterReturnMagic:
return Magenta();
case kAsanInitializationOrderMagic:
return Cyan();
case kAsanUserPoisonedMemoryMagic:
case kAsanContiguousContainerOOBMagic:
case kAsanAllocaLeftMagic:
case kAsanAllocaRightMagic:
return Blue();
case kAsanStackUseAfterScopeMagic:
return Magenta();
case kAsanGlobalRedzoneMagic:
return Red();
case kAsanInternalHeapMagic:
return Yellow();
case kAsanIntraObjectRedzone:
return Yellow();
default:
return Default();
}
}
const char *EndShadowByte() { return Default(); }
const char *MemoryByte() { return Magenta(); }
const char *EndMemoryByte() { return Default(); }
};
enum ShadowKind : u8 {
kShadowKindLow,
kShadowKindGap,
kShadowKindHigh,
};
static const char *const ShadowNames[] = {"low shadow", "shadow gap",
"high shadow"};
struct ShadowAddressDescription {
uptr addr;
ShadowKind kind;
u8 shadow_byte;
void Print() const;
};
bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr);
bool DescribeAddressIfShadow(uptr addr);
enum AccessType {
kAccessTypeLeft,
kAccessTypeRight,
kAccessTypeInside,
kAccessTypeUnknown, // This means we have an AddressSanitizer bug!
};
struct ChunkAccess {
uptr bad_addr;
sptr offset;
uptr chunk_begin;
uptr chunk_size;
u32 access_type : 2;
u32 alloc_type : 2;
};
struct HeapAddressDescription {
uptr addr;
uptr alloc_tid;
uptr free_tid;
u32 alloc_stack_id;
u32 free_stack_id;
ChunkAccess chunk_access;
void Print() const;
};
bool GetHeapAddressInformation(uptr addr, uptr access_size,
HeapAddressDescription *descr);
bool DescribeAddressIfHeap(uptr addr, uptr access_size = 1);
struct StackAddressDescription {
uptr addr;
uptr tid;
uptr offset;
uptr frame_pc;
uptr access_size;
const char *frame_descr;
void Print() const;
};
bool GetStackAddressInformation(uptr addr, uptr access_size,
StackAddressDescription *descr);
struct GlobalAddressDescription {
uptr addr;
// Assume address is close to at most four globals.
static const int kMaxGlobals = 4;
__asan_global globals[kMaxGlobals];
u32 reg_sites[kMaxGlobals];
uptr access_size;
u8 size;
void Print(const char *bug_type = "") const;
};
bool GetGlobalAddressInformation(uptr addr, uptr access_size,
GlobalAddressDescription *descr);
bool DescribeAddressIfGlobal(uptr addr, uptr access_size, const char *bug_type);
// General function to describe an address. Will try to describe the address as
// a shadow, global (variable), stack, or heap address.
// bug_type is optional and is used for checking if we're reporting an
// initialization-order-fiasco
// The proper access_size should be passed for stack, global, and heap
// addresses. Defaults to 1.
// Each of the *AddressDescription functions has its own Print() member, which
// may take access_size and bug_type parameters if needed.
void PrintAddressDescription(uptr addr, uptr access_size = 1,
const char *bug_type = "");
enum AddressKind {
kAddressKindWild,
kAddressKindShadow,
kAddressKindHeap,
kAddressKindStack,
kAddressKindGlobal,
};
class AddressDescription {
struct AddressDescriptionData {
AddressKind kind;
union {
ShadowAddressDescription shadow;
HeapAddressDescription heap;
StackAddressDescription stack;
GlobalAddressDescription global;
uptr addr;
};
};
AddressDescriptionData data;
public:
AddressDescription() = default;
// shouldLockThreadRegistry allows us to skip locking if we're sure we already
// have done it.
AddressDescription(uptr addr, bool shouldLockThreadRegistry = true)
: AddressDescription(addr, 1, shouldLockThreadRegistry) {}
AddressDescription(uptr addr, uptr access_size,
bool shouldLockThreadRegistry = true);
uptr Address() const {
switch (data.kind) {
case kAddressKindWild:
return data.addr;
case kAddressKindShadow:
return data.shadow.addr;
case kAddressKindHeap:
return data.heap.addr;
case kAddressKindStack:
return data.stack.addr;
case kAddressKindGlobal:
return data.global.addr;
}
UNREACHABLE("AddressInformation kind is invalid");
}
void Print(const char *bug_descr = nullptr) const {
switch (data.kind) {
case kAddressKindWild:
Printf("Address %p is a wild pointer.\n", data.addr);
return;
case kAddressKindShadow:
return data.shadow.Print();
case kAddressKindHeap:
return data.heap.Print();
case kAddressKindStack:
return data.stack.Print();
case kAddressKindGlobal:
// initialization-order-fiasco has a special Print()
return data.global.Print(bug_descr);
}
UNREACHABLE("AddressInformation kind is invalid");
}
void StoreTo(AddressDescriptionData *dst) const { *dst = data; }
const ShadowAddressDescription *AsShadow() const {
return data.kind == kAddressKindShadow ? &data.shadow : nullptr;
}
const HeapAddressDescription *AsHeap() const {
return data.kind == kAddressKindHeap ? &data.heap : nullptr;
}
const StackAddressDescription *AsStack() const {
return data.kind == kAddressKindStack ? &data.stack : nullptr;
}
const GlobalAddressDescription *AsGlobal() const {
return data.kind == kAddressKindGlobal ? &data.global : nullptr;
}
};
} // namespace __asan
#endif // ASAN_DESCRIPTIONS_H

503
lib/asan/asan_errors.cc Normal file
View file

@ -0,0 +1,503 @@
//===-- asan_errors.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.
//
// ASan implementation for error structures.
//===----------------------------------------------------------------------===//
#include "asan_errors.h"
#include <signal.h>
#include "asan_descriptions.h"
#include "asan_mapping.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
namespace __asan {
void ErrorStackOverflow::Print() {
Decorator d;
Printf("%s", d.Warning());
Report(
"ERROR: AddressSanitizer: %s on address %p"
" (pc %p bp %p sp %p T%d)\n", scariness.GetDescription(),
(void *)addr, (void *)pc, (void *)bp, (void *)sp, tid);
Printf("%s", d.EndWarning());
scariness.Print();
BufferedStackTrace stack;
GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context,
common_flags()->fast_unwind_on_fatal);
stack.Print();
ReportErrorSummary(scariness.GetDescription(), &stack);
}
static void MaybeDumpInstructionBytes(uptr pc) {
if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) return;
InternalScopedString str(1024);
str.append("First 16 instruction bytes at pc: ");
if (IsAccessibleMemoryRange(pc, 16)) {
for (int i = 0; i < 16; ++i) {
PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/ false, " ");
}
str.append("\n");
} else {
str.append("unaccessible\n");
}
Report("%s", str.data());
}
static void MaybeDumpRegisters(void *context) {
if (!flags()->dump_registers) return;
SignalContext::DumpAllRegisters(context);
}
void ErrorDeadlySignal::Print() {
Decorator d;
Printf("%s", d.Warning());
const char *description = DescribeSignalOrException(signo);
Report(
"ERROR: AddressSanitizer: %s on unknown address %p (pc %p bp %p sp %p "
"T%d)\n",
description, (void *)addr, (void *)pc, (void *)bp, (void *)sp, tid);
Printf("%s", d.EndWarning());
if (pc < GetPageSizeCached()) Report("Hint: pc points to the zero page.\n");
if (is_memory_access) {
const char *access_type =
write_flag == SignalContext::WRITE
? "WRITE"
: (write_flag == SignalContext::READ ? "READ" : "UNKNOWN");
Report("The signal is caused by a %s memory access.\n", access_type);
if (addr < GetPageSizeCached())
Report("Hint: address points to the zero page.\n");
}
scariness.Print();
BufferedStackTrace stack;
GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context,
common_flags()->fast_unwind_on_fatal);
stack.Print();
MaybeDumpInstructionBytes(pc);
MaybeDumpRegisters(context);
Printf("AddressSanitizer can not provide additional info.\n");
ReportErrorSummary(description, &stack);
}
void ErrorDoubleFree::Print() {
Decorator d;
Printf("%s", d.Warning());
char tname[128];
Report(
"ERROR: AddressSanitizer: attempting %s on %p in "
"thread T%d%s:\n",
scariness.GetDescription(), addr_description.addr, tid,
ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
Printf("%s", d.EndWarning());
scariness.Print();
GET_STACK_TRACE_FATAL(second_free_stack->trace[0],
second_free_stack->top_frame_bp);
stack.Print();
addr_description.Print();
ReportErrorSummary(scariness.GetDescription(), &stack);
}
void ErrorNewDeleteSizeMismatch::Print() {
Decorator d;
Printf("%s", d.Warning());
char tname[128];
Report(
"ERROR: AddressSanitizer: %s on %p in thread "
"T%d%s:\n",
scariness.GetDescription(), addr_description.addr, tid,
ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
Printf("%s object passed to delete has wrong type:\n", d.EndWarning());
Printf(
" size of the allocated type: %zd bytes;\n"
" size of the deallocated type: %zd bytes.\n",
addr_description.chunk_access.chunk_size, delete_size);
CHECK_GT(free_stack->size, 0);
scariness.Print();
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
stack.Print();
addr_description.Print();
ReportErrorSummary(scariness.GetDescription(), &stack);
Report(
"HINT: if you don't care about these errors you may set "
"ASAN_OPTIONS=new_delete_type_mismatch=0\n");
}
void ErrorFreeNotMalloced::Print() {
Decorator d;
Printf("%s", d.Warning());
char tname[128];
Report(
"ERROR: AddressSanitizer: attempting free on address "
"which was not malloc()-ed: %p in thread T%d%s\n",
addr_description.Address(), tid,
ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
Printf("%s", d.EndWarning());
CHECK_GT(free_stack->size, 0);
scariness.Print();
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
stack.Print();
addr_description.Print();
ReportErrorSummary(scariness.GetDescription(), &stack);
}
void ErrorAllocTypeMismatch::Print() {
static const char *alloc_names[] = {"INVALID", "malloc", "operator new",
"operator new []"};
static const char *dealloc_names[] = {"INVALID", "free", "operator delete",
"operator delete []"};
CHECK_NE(alloc_type, dealloc_type);
Decorator d;
Printf("%s", d.Warning());
Report("ERROR: AddressSanitizer: %s (%s vs %s) on %p\n",
scariness.GetDescription(),
alloc_names[alloc_type], dealloc_names[dealloc_type],
addr_description.addr);
Printf("%s", d.EndWarning());
CHECK_GT(dealloc_stack->size, 0);
scariness.Print();
GET_STACK_TRACE_FATAL(dealloc_stack->trace[0], dealloc_stack->top_frame_bp);
stack.Print();
addr_description.Print();
ReportErrorSummary(scariness.GetDescription(), &stack);
Report(
"HINT: if you don't care about these errors you may set "
"ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
}
void ErrorMallocUsableSizeNotOwned::Print() {
Decorator d;
Printf("%s", d.Warning());
Report(
"ERROR: AddressSanitizer: attempting to call malloc_usable_size() for "
"pointer which is not owned: %p\n",
addr_description.Address());
Printf("%s", d.EndWarning());
stack->Print();
addr_description.Print();
ReportErrorSummary(scariness.GetDescription(), stack);
}
void ErrorSanitizerGetAllocatedSizeNotOwned::Print() {
Decorator d;
Printf("%s", d.Warning());
Report(
"ERROR: AddressSanitizer: attempting to call "
"__sanitizer_get_allocated_size() for pointer which is not owned: %p\n",
addr_description.Address());
Printf("%s", d.EndWarning());
stack->Print();
addr_description.Print();
ReportErrorSummary(scariness.GetDescription(), stack);
}
void ErrorStringFunctionMemoryRangesOverlap::Print() {
Decorator d;
char bug_type[100];
internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
Printf("%s", d.Warning());
Report(
"ERROR: AddressSanitizer: %s: memory ranges [%p,%p) and [%p, %p) "
"overlap\n",
bug_type, addr1_description.Address(),
addr1_description.Address() + length1, addr2_description.Address(),
addr2_description.Address() + length2);
Printf("%s", d.EndWarning());
scariness.Print();
stack->Print();
addr1_description.Print();
addr2_description.Print();
ReportErrorSummary(bug_type, stack);
}
void ErrorStringFunctionSizeOverflow::Print() {
Decorator d;
Printf("%s", d.Warning());
Report("ERROR: AddressSanitizer: %s: (size=%zd)\n",
scariness.GetDescription(), size);
Printf("%s", d.EndWarning());
scariness.Print();
stack->Print();
addr_description.Print();
ReportErrorSummary(scariness.GetDescription(), stack);
}
void ErrorBadParamsToAnnotateContiguousContainer::Print() {
Report(
"ERROR: AddressSanitizer: bad parameters to "
"__sanitizer_annotate_contiguous_container:\n"
" beg : %p\n"
" end : %p\n"
" old_mid : %p\n"
" new_mid : %p\n",
beg, end, old_mid, new_mid);
uptr granularity = SHADOW_GRANULARITY;
if (!IsAligned(beg, granularity))
Report("ERROR: beg is not aligned by %d\n", granularity);
stack->Print();
ReportErrorSummary(scariness.GetDescription(), stack);
}
void ErrorODRViolation::Print() {
Decorator d;
Printf("%s", d.Warning());
Report("ERROR: AddressSanitizer: %s (%p):\n", scariness.GetDescription(),
global1.beg);
Printf("%s", d.EndWarning());
InternalScopedString g1_loc(256), g2_loc(256);
PrintGlobalLocation(&g1_loc, global1);
PrintGlobalLocation(&g2_loc, global2);
Printf(" [1] size=%zd '%s' %s\n", global1.size,
MaybeDemangleGlobalName(global1.name), g1_loc.data());
Printf(" [2] size=%zd '%s' %s\n", global2.size,
MaybeDemangleGlobalName(global2.name), g2_loc.data());
if (stack_id1 && stack_id2) {
Printf("These globals were registered at these points:\n");
Printf(" [1]:\n");
StackDepotGet(stack_id1).Print();
Printf(" [2]:\n");
StackDepotGet(stack_id2).Print();
}
Report(
"HINT: if you don't care about these errors you may set "
"ASAN_OPTIONS=detect_odr_violation=0\n");
InternalScopedString error_msg(256);
error_msg.append("%s: global '%s' at %s", scariness.GetDescription(),
MaybeDemangleGlobalName(global1.name), g1_loc.data());
ReportErrorSummary(error_msg.data());
}
void ErrorInvalidPointerPair::Print() {
Decorator d;
Printf("%s", d.Warning());
Report("ERROR: AddressSanitizer: %s: %p %p\n", scariness.GetDescription(),
addr1_description.Address(), addr2_description.Address());
Printf("%s", d.EndWarning());
GET_STACK_TRACE_FATAL(pc, bp);
stack.Print();
addr1_description.Print();
addr2_description.Print();
ReportErrorSummary(scariness.GetDescription(), &stack);
}
static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) {
return s[-1] > 127 && s[1] > 127;
}
ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr,
bool is_write_, uptr access_size_)
: ErrorBase(tid),
addr_description(addr, access_size_, /*shouldLockThreadRegistry=*/false),
pc(pc_),
bp(bp_),
sp(sp_),
access_size(access_size_),
is_write(is_write_),
shadow_val(0) {
scariness.Clear();
if (access_size) {
if (access_size <= 9) {
char desr[] = "?-byte";
desr[0] = '0' + access_size;
scariness.Scare(access_size + access_size / 2, desr);
} else if (access_size >= 10) {
scariness.Scare(15, "multi-byte");
}
is_write ? scariness.Scare(20, "write") : scariness.Scare(1, "read");
// Determine the error type.
bug_descr = "unknown-crash";
if (AddrIsInMem(addr)) {
u8 *shadow_addr = (u8 *)MemToShadow(addr);
// If we are accessing 16 bytes, look at the second shadow byte.
if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) shadow_addr++;
// If we are in the partial right redzone, look at the next shadow byte.
if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++;
bool far_from_bounds = false;
shadow_val = *shadow_addr;
int bug_type_score = 0;
// For use-after-frees reads are almost as bad as writes.
int read_after_free_bonus = 0;
switch (shadow_val) {
case kAsanHeapLeftRedzoneMagic:
case kAsanArrayCookieMagic:
bug_descr = "heap-buffer-overflow";
bug_type_score = 10;
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
break;
case kAsanHeapFreeMagic:
bug_descr = "heap-use-after-free";
bug_type_score = 20;
if (!is_write) read_after_free_bonus = 18;
break;
case kAsanStackLeftRedzoneMagic:
bug_descr = "stack-buffer-underflow";
bug_type_score = 25;
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
break;
case kAsanInitializationOrderMagic:
bug_descr = "initialization-order-fiasco";
bug_type_score = 1;
break;
case kAsanStackMidRedzoneMagic:
case kAsanStackRightRedzoneMagic:
bug_descr = "stack-buffer-overflow";
bug_type_score = 25;
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
break;
case kAsanStackAfterReturnMagic:
bug_descr = "stack-use-after-return";
bug_type_score = 30;
if (!is_write) read_after_free_bonus = 18;
break;
case kAsanUserPoisonedMemoryMagic:
bug_descr = "use-after-poison";
bug_type_score = 20;
break;
case kAsanContiguousContainerOOBMagic:
bug_descr = "container-overflow";
bug_type_score = 10;
break;
case kAsanStackUseAfterScopeMagic:
bug_descr = "stack-use-after-scope";
bug_type_score = 10;
break;
case kAsanGlobalRedzoneMagic:
bug_descr = "global-buffer-overflow";
bug_type_score = 10;
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
break;
case kAsanIntraObjectRedzone:
bug_descr = "intra-object-overflow";
bug_type_score = 10;
break;
case kAsanAllocaLeftMagic:
case kAsanAllocaRightMagic:
bug_descr = "dynamic-stack-buffer-overflow";
bug_type_score = 25;
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
break;
}
scariness.Scare(bug_type_score + read_after_free_bonus, bug_descr);
if (far_from_bounds) scariness.Scare(10, "far-from-bounds");
}
}
}
static void PrintContainerOverflowHint() {
Printf("HINT: if you don't care about these errors you may set "
"ASAN_OPTIONS=detect_container_overflow=0.\n"
"If you suspect a false positive see also: "
"https://github.com/google/sanitizers/wiki/"
"AddressSanitizerContainerOverflow.\n");
}
static void PrintShadowByte(InternalScopedString *str, const char *before,
u8 byte, const char *after = "\n") {
PrintMemoryByte(str, before, byte, /*in_shadow*/true, after);
}
static void PrintLegend(InternalScopedString *str) {
str->append(
"Shadow byte legend (one shadow byte represents %d "
"application bytes):\n",
(int)SHADOW_GRANULARITY);
PrintShadowByte(str, " Addressable: ", 0);
str->append(" Partially addressable: ");
for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " ");
str->append("\n");
PrintShadowByte(str, " Heap left redzone: ",
kAsanHeapLeftRedzoneMagic);
PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic);
PrintShadowByte(str, " Stack left redzone: ",
kAsanStackLeftRedzoneMagic);
PrintShadowByte(str, " Stack mid redzone: ",
kAsanStackMidRedzoneMagic);
PrintShadowByte(str, " Stack right redzone: ",
kAsanStackRightRedzoneMagic);
PrintShadowByte(str, " Stack after return: ",
kAsanStackAfterReturnMagic);
PrintShadowByte(str, " Stack use after scope: ",
kAsanStackUseAfterScopeMagic);
PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic);
PrintShadowByte(str, " Global init order: ",
kAsanInitializationOrderMagic);
PrintShadowByte(str, " Poisoned by user: ",
kAsanUserPoisonedMemoryMagic);
PrintShadowByte(str, " Container overflow: ",
kAsanContiguousContainerOOBMagic);
PrintShadowByte(str, " Array cookie: ",
kAsanArrayCookieMagic);
PrintShadowByte(str, " Intra object redzone: ",
kAsanIntraObjectRedzone);
PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic);
PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic);
}
static void PrintShadowBytes(InternalScopedString *str, const char *before,
u8 *bytes, u8 *guilty, uptr n) {
Decorator d;
if (before) str->append("%s%p:", before, bytes);
for (uptr i = 0; i < n; i++) {
u8 *p = bytes + i;
const char *before =
p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " ";
const char *after = p == guilty ? "]" : "";
PrintShadowByte(str, before, *p, after);
}
str->append("\n");
}
static void PrintShadowMemoryForAddress(uptr addr) {
if (!AddrIsInMem(addr)) return;
uptr shadow_addr = MemToShadow(addr);
const uptr n_bytes_per_row = 16;
uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
InternalScopedString str(4096 * 8);
str.append("Shadow bytes around the buggy address:\n");
for (int i = -5; i <= 5; i++) {
const char *prefix = (i == 0) ? "=>" : " ";
PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row),
(u8 *)shadow_addr, n_bytes_per_row);
}
if (flags()->print_legend) PrintLegend(&str);
Printf("%s", str.data());
}
void ErrorGeneric::Print() {
Decorator d;
Printf("%s", d.Warning());
uptr addr = addr_description.Address();
Report("ERROR: AddressSanitizer: %s on address %p at pc %p bp %p sp %p\n",
bug_descr, (void *)addr, pc, bp, sp);
Printf("%s", d.EndWarning());
char tname[128];
Printf("%s%s of size %zu at %p thread T%d%s%s\n", d.Access(),
access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", access_size,
(void *)addr, tid,
ThreadNameWithParenthesis(tid, tname, sizeof(tname)), d.EndAccess());
scariness.Print();
GET_STACK_TRACE_FATAL(pc, bp);
stack.Print();
// Pass bug_descr because we have a special case for
// initialization-order-fiasco
addr_description.Print(bug_descr);
if (shadow_val == kAsanContiguousContainerOOBMagic)
PrintContainerOverflowHint();
ReportErrorSummary(bug_descr, &stack);
PrintShadowMemoryForAddress(addr);
}
} // namespace __asan

390
lib/asan/asan_errors.h Normal file
View file

@ -0,0 +1,390 @@
//===-- asan_errors.h -------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// ASan-private header for error structures.
//===----------------------------------------------------------------------===//
#ifndef ASAN_ERRORS_H
#define ASAN_ERRORS_H
#include "asan_descriptions.h"
#include "asan_scariness_score.h"
#include "sanitizer_common/sanitizer_common.h"
namespace __asan {
struct ErrorBase {
ErrorBase() = default;
explicit ErrorBase(u32 tid_) : tid(tid_) {}
ScarinessScoreBase scariness;
u32 tid;
};
struct ErrorStackOverflow : ErrorBase {
uptr addr, pc, bp, sp;
// ErrorStackOverflow never owns the context.
void *context;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorStackOverflow() = default;
ErrorStackOverflow(u32 tid, const SignalContext &sig)
: ErrorBase(tid),
addr(sig.addr),
pc(sig.pc),
bp(sig.bp),
sp(sig.sp),
context(sig.context) {
scariness.Clear();
scariness.Scare(10, "stack-overflow");
}
void Print();
};
struct ErrorDeadlySignal : ErrorBase {
uptr addr, pc, bp, sp;
// ErrorDeadlySignal never owns the context.
void *context;
int signo;
SignalContext::WriteFlag write_flag;
bool is_memory_access;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorDeadlySignal() = default;
ErrorDeadlySignal(u32 tid, const SignalContext &sig, int signo_)
: ErrorBase(tid),
addr(sig.addr),
pc(sig.pc),
bp(sig.bp),
sp(sig.sp),
context(sig.context),
signo(signo_),
write_flag(sig.write_flag),
is_memory_access(sig.is_memory_access) {
scariness.Clear();
if (is_memory_access) {
if (addr < GetPageSizeCached()) {
scariness.Scare(10, "null-deref");
} else if (addr == pc) {
scariness.Scare(60, "wild-jump");
} else if (write_flag == SignalContext::WRITE) {
scariness.Scare(30, "wild-addr-write");
} else if (write_flag == SignalContext::READ) {
scariness.Scare(20, "wild-addr-read");
} else {
scariness.Scare(25, "wild-addr");
}
} else {
scariness.Scare(10, "signal");
}
}
void Print();
};
struct ErrorDoubleFree : ErrorBase {
// ErrorDoubleFree doesn't own the stack trace.
const BufferedStackTrace *second_free_stack;
HeapAddressDescription addr_description;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorDoubleFree() = default;
ErrorDoubleFree(u32 tid, BufferedStackTrace *stack, uptr addr)
: ErrorBase(tid), second_free_stack(stack) {
CHECK_GT(second_free_stack->size, 0);
GetHeapAddressInformation(addr, 1, &addr_description);
scariness.Clear();
scariness.Scare(42, "double-free");
}
void Print();
};
struct ErrorNewDeleteSizeMismatch : ErrorBase {
// ErrorNewDeleteSizeMismatch doesn't own the stack trace.
const BufferedStackTrace *free_stack;
HeapAddressDescription addr_description;
uptr delete_size;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorNewDeleteSizeMismatch() = default;
ErrorNewDeleteSizeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr,
uptr delete_size_)
: ErrorBase(tid), free_stack(stack), delete_size(delete_size_) {
GetHeapAddressInformation(addr, 1, &addr_description);
scariness.Clear();
scariness.Scare(10, "new-delete-type-mismatch");
}
void Print();
};
struct ErrorFreeNotMalloced : ErrorBase {
// ErrorFreeNotMalloced doesn't own the stack trace.
const BufferedStackTrace *free_stack;
AddressDescription addr_description;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorFreeNotMalloced() = default;
ErrorFreeNotMalloced(u32 tid, BufferedStackTrace *stack, uptr addr)
: ErrorBase(tid),
free_stack(stack),
addr_description(addr, /*shouldLockThreadRegistry=*/false) {
scariness.Clear();
scariness.Scare(40, "bad-free");
}
void Print();
};
struct ErrorAllocTypeMismatch : ErrorBase {
// ErrorAllocTypeMismatch doesn't own the stack trace.
const BufferedStackTrace *dealloc_stack;
HeapAddressDescription addr_description;
AllocType alloc_type, dealloc_type;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorAllocTypeMismatch() = default;
ErrorAllocTypeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr,
AllocType alloc_type_, AllocType dealloc_type_)
: ErrorBase(tid),
dealloc_stack(stack),
alloc_type(alloc_type_),
dealloc_type(dealloc_type_) {
GetHeapAddressInformation(addr, 1, &addr_description);
scariness.Clear();
scariness.Scare(10, "alloc-dealloc-mismatch");
};
void Print();
};
struct ErrorMallocUsableSizeNotOwned : ErrorBase {
// ErrorMallocUsableSizeNotOwned doesn't own the stack trace.
const BufferedStackTrace *stack;
AddressDescription addr_description;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorMallocUsableSizeNotOwned() = default;
ErrorMallocUsableSizeNotOwned(u32 tid, BufferedStackTrace *stack_, uptr addr)
: ErrorBase(tid),
stack(stack_),
addr_description(addr, /*shouldLockThreadRegistry=*/false) {
scariness.Clear();
scariness.Scare(10, "bad-malloc_usable_size");
}
void Print();
};
struct ErrorSanitizerGetAllocatedSizeNotOwned : ErrorBase {
// ErrorSanitizerGetAllocatedSizeNotOwned doesn't own the stack trace.
const BufferedStackTrace *stack;
AddressDescription addr_description;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorSanitizerGetAllocatedSizeNotOwned() = default;
ErrorSanitizerGetAllocatedSizeNotOwned(u32 tid, BufferedStackTrace *stack_,
uptr addr)
: ErrorBase(tid),
stack(stack_),
addr_description(addr, /*shouldLockThreadRegistry=*/false) {
scariness.Clear();
scariness.Scare(10, "bad-__sanitizer_get_allocated_size");
}
void Print();
};
struct ErrorStringFunctionMemoryRangesOverlap : ErrorBase {
// ErrorStringFunctionMemoryRangesOverlap doesn't own the stack trace.
const BufferedStackTrace *stack;
uptr length1, length2;
AddressDescription addr1_description;
AddressDescription addr2_description;
const char *function;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorStringFunctionMemoryRangesOverlap() = default;
ErrorStringFunctionMemoryRangesOverlap(u32 tid, BufferedStackTrace *stack_,
uptr addr1, uptr length1_, uptr addr2,
uptr length2_, const char *function_)
: ErrorBase(tid),
stack(stack_),
length1(length1_),
length2(length2_),
addr1_description(addr1, length1, /*shouldLockThreadRegistry=*/false),
addr2_description(addr2, length2, /*shouldLockThreadRegistry=*/false),
function(function_) {
char bug_type[100];
internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
scariness.Clear();
scariness.Scare(10, bug_type);
}
void Print();
};
struct ErrorStringFunctionSizeOverflow : ErrorBase {
// ErrorStringFunctionSizeOverflow doesn't own the stack trace.
const BufferedStackTrace *stack;
AddressDescription addr_description;
uptr size;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorStringFunctionSizeOverflow() = default;
ErrorStringFunctionSizeOverflow(u32 tid, BufferedStackTrace *stack_,
uptr addr, uptr size_)
: ErrorBase(tid),
stack(stack_),
addr_description(addr, /*shouldLockThreadRegistry=*/false),
size(size_) {
scariness.Clear();
scariness.Scare(10, "negative-size-param");
}
void Print();
};
struct ErrorBadParamsToAnnotateContiguousContainer : ErrorBase {
// ErrorBadParamsToAnnotateContiguousContainer doesn't own the stack trace.
const BufferedStackTrace *stack;
uptr beg, end, old_mid, new_mid;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorBadParamsToAnnotateContiguousContainer() = default;
// PS4: Do we want an AddressDescription for beg?
ErrorBadParamsToAnnotateContiguousContainer(u32 tid,
BufferedStackTrace *stack_,
uptr beg_, uptr end_,
uptr old_mid_, uptr new_mid_)
: ErrorBase(tid),
stack(stack_),
beg(beg_),
end(end_),
old_mid(old_mid_),
new_mid(new_mid_) {
scariness.Clear();
scariness.Scare(10, "bad-__sanitizer_annotate_contiguous_container");
}
void Print();
};
struct ErrorODRViolation : ErrorBase {
__asan_global global1, global2;
u32 stack_id1, stack_id2;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorODRViolation() = default;
ErrorODRViolation(u32 tid, const __asan_global *g1, u32 stack_id1_,
const __asan_global *g2, u32 stack_id2_)
: ErrorBase(tid),
global1(*g1),
global2(*g2),
stack_id1(stack_id1_),
stack_id2(stack_id2_) {
scariness.Clear();
scariness.Scare(10, "odr-violation");
}
void Print();
};
struct ErrorInvalidPointerPair : ErrorBase {
uptr pc, bp, sp;
AddressDescription addr1_description;
AddressDescription addr2_description;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorInvalidPointerPair() = default;
ErrorInvalidPointerPair(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr p1,
uptr p2)
: ErrorBase(tid),
pc(pc_),
bp(bp_),
sp(sp_),
addr1_description(p1, 1, /*shouldLockThreadRegistry=*/false),
addr2_description(p2, 1, /*shouldLockThreadRegistry=*/false) {
scariness.Clear();
scariness.Scare(10, "invalid-pointer-pair");
}
void Print();
};
struct ErrorGeneric : ErrorBase {
AddressDescription addr_description;
uptr pc, bp, sp;
uptr access_size;
const char *bug_descr;
bool is_write;
u8 shadow_val;
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
// constructor
ErrorGeneric() = default;
ErrorGeneric(u32 tid, uptr addr, uptr pc_, uptr bp_, uptr sp_, bool is_write_,
uptr access_size_);
void Print();
};
// clang-format off
#define ASAN_FOR_EACH_ERROR_KIND(macro) \
macro(StackOverflow) \
macro(DeadlySignal) \
macro(DoubleFree) \
macro(NewDeleteSizeMismatch) \
macro(FreeNotMalloced) \
macro(AllocTypeMismatch) \
macro(MallocUsableSizeNotOwned) \
macro(SanitizerGetAllocatedSizeNotOwned) \
macro(StringFunctionMemoryRangesOverlap) \
macro(StringFunctionSizeOverflow) \
macro(BadParamsToAnnotateContiguousContainer) \
macro(ODRViolation) \
macro(InvalidPointerPair) \
macro(Generic)
// clang-format on
#define ASAN_DEFINE_ERROR_KIND(name) kErrorKind##name,
#define ASAN_ERROR_DESCRIPTION_MEMBER(name) Error##name name;
#define ASAN_ERROR_DESCRIPTION_CONSTRUCTOR(name) \
ErrorDescription(Error##name const &e) : kind(kErrorKind##name), name(e) {}
#define ASAN_ERROR_DESCRIPTION_PRINT(name) \
case kErrorKind##name: \
return name.Print();
enum ErrorKind {
kErrorKindInvalid = 0,
ASAN_FOR_EACH_ERROR_KIND(ASAN_DEFINE_ERROR_KIND)
};
struct ErrorDescription {
ErrorKind kind;
// We're using a tagged union because it allows us to have a trivially
// copiable type and use the same structures as the public interface.
//
// We can add a wrapper around it to make it "more c++-like", but that would
// add a lot of code and the benefit wouldn't be that big.
union {
ErrorBase Base;
ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_MEMBER)
};
ErrorDescription() { internal_memset(this, 0, sizeof(*this)); }
ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_CONSTRUCTOR)
bool IsValid() { return kind != kErrorKindInvalid; }
void Print() {
switch (kind) {
ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_PRINT)
case kErrorKindInvalid:
CHECK(0);
}
CHECK(0);
}
};
#undef ASAN_FOR_EACH_ERROR_KIND
#undef ASAN_DEFINE_ERROR_KIND
#undef ASAN_ERROR_DESCRIPTION_MEMBER
#undef ASAN_ERROR_DESCRIPTION_CONSTRUCTOR
#undef ASAN_ERROR_DESCRIPTION_PRINT
} // namespace __asan
#endif // ASAN_ERRORS_H

View file

@ -100,7 +100,7 @@ FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id,
// if the signal arrives between checking and setting flags[pos], the
// signal handler's fake stack will start from a different hint_position
// and so will not touch this particular byte. So, it is safe to do this
// with regular non-atimic load and store (at least I was not able to make
// with regular non-atomic load and store (at least I was not able to make
// this code crash).
if (flags[pos]) continue;
flags[pos] = 1;

View file

@ -52,7 +52,7 @@ struct FakeFrame {
// Allocate() flips the appropriate allocation flag atomically, thus achieving
// async-signal safety.
// This allocator does not have quarantine per se, but it tries to allocate the
// frames in round robin fasion to maximize the delay between a deallocation
// frames in round robin fashion to maximize the delay between a deallocation
// and the next allocation.
class FakeStack {
static const uptr kMinStackFrameSizeLog = 6; // Min frame is 64B.
@ -99,12 +99,12 @@ class FakeStack {
return ((uptr)1) << (stack_size_log - kMinStackFrameSizeLog - class_id);
}
// Divide n by the numbe of frames in size class.
// Divide n by the number of frames in size class.
static uptr ModuloNumberOfFrames(uptr stack_size_log, uptr class_id, uptr n) {
return n & (NumberOfFrames(stack_size_log, class_id) - 1);
}
// The the pointer to the flags of the given class_id.
// The pointer to the flags of the given class_id.
u8 *GetFlags(uptr stack_size_log, uptr class_id) {
return reinterpret_cast<u8 *>(this) + kFlagsOffset +
FlagsOffset(stack_size_log, class_id);

View file

@ -156,9 +156,19 @@ void InitializeFlags() {
f->quarantine_size_mb = f->quarantine_size >> 20;
if (f->quarantine_size_mb < 0) {
const int kDefaultQuarantineSizeMb =
(ASAN_LOW_MEMORY) ? 1UL << 6 : 1UL << 8;
(ASAN_LOW_MEMORY) ? 1UL << 4 : 1UL << 8;
f->quarantine_size_mb = kDefaultQuarantineSizeMb;
}
if (f->thread_local_quarantine_size_kb < 0) {
const u32 kDefaultThreadLocalQuarantineSizeKb =
// It is not advised to go lower than 64Kb, otherwise quarantine batches
// pushed from thread local quarantine to global one will create too
// much overhead. One quarantine batch size is 8Kb and it holds up to
// 1021 chunk, which amounts to 1/8 memory overhead per batch when
// thread local quarantine is set to 64Kb.
(ASAN_LOW_MEMORY) ? 1 << 6 : FIRST_32_SECOND_64(1 << 8, 1 << 10);
f->thread_local_quarantine_size_kb = kDefaultThreadLocalQuarantineSizeKb;
}
if (!f->replace_str && common_flags()->intercept_strlen) {
Report("WARNING: strlen interceptor is enabled even though replace_str=0. "
"Use intercept_strlen=0 to disable it.");

View file

@ -23,6 +23,12 @@ ASAN_FLAG(int, quarantine_size_mb, -1,
"Size (in Mb) of quarantine used to detect use-after-free "
"errors. Lower value may reduce memory usage but increase the "
"chance of false negatives.")
ASAN_FLAG(int, thread_local_quarantine_size_kb, -1,
"Size (in Kb) of thread local quarantine used to detect "
"use-after-free errors. Lower value may reduce memory usage but "
"increase the chance of false negatives. It is not advised to go "
"lower than 64Kb, otherwise frequent transfers to global quarantine "
"might affect performance.")
ASAN_FLAG(int, redzone, 16,
"Minimal size (in bytes) of redzones around heap objects. "
"Requirement: redzone >= 16, is a power of two.")
@ -102,7 +108,7 @@ ASAN_FLAG(bool, poison_array_cookie, true,
// https://github.com/google/sanitizers/issues/309
// TODO(glider,timurrrr): Fix known issues and enable this back.
ASAN_FLAG(bool, alloc_dealloc_mismatch,
(SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0),
!SANITIZER_MAC && !SANITIZER_WINDOWS && !SANITIZER_ANDROID,
"Report errors on malloc/delete, new/free, new/delete[], etc.")
ASAN_FLAG(bool, new_delete_type_mismatch, true,
@ -133,6 +139,9 @@ ASAN_FLAG(int, detect_odr_violation, 2,
"have different sizes")
ASAN_FLAG(bool, dump_instruction_bytes, false,
"If true, dump 16 bytes starting at the instruction that caused SEGV")
ASAN_FLAG(bool, dump_registers, true,
"If true, dump values of CPU registers when SEGV happens. Only "
"available on OS X for now.")
ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
ASAN_FLAG(bool, halt_on_error, true,
"Crash the program after printing the first error report "

View file

@ -25,6 +25,7 @@
#include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
namespace __asan {
@ -123,18 +124,6 @@ int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites,
return res;
}
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
Global g = {};
if (GetGlobalsForAddress(addr, &g, nullptr, 1)) {
internal_strncpy(descr->name, g.name, descr->name_size);
descr->region_address = g.beg;
descr->region_size = g.size;
descr->region_kind = "global";
return true;
}
return false;
}
enum GlobalSymbolState {
UNREGISTERED = 0,
REGISTERED = 1
@ -279,6 +268,46 @@ void StopInitOrderChecking() {
}
}
static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; }
const char *MaybeDemangleGlobalName(const char *name) {
// We can spoil names of globals with C linkage, so use an heuristic
// approach to check if the name should be demangled.
bool should_demangle = false;
if (name[0] == '_' && name[1] == 'Z')
should_demangle = true;
else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
should_demangle = true;
return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
}
// Check if the global is a zero-terminated ASCII string. If so, print it.
void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) {
for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
unsigned char c = *(unsigned char *)p;
if (c == '\0' || !IsASCII(c)) return;
}
if (*(char *)(g.beg + g.size - 1) != '\0') return;
str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name),
(char *)g.beg);
}
static const char *GlobalFilename(const __asan_global &g) {
const char *res = g.module_name;
// Prefer the filename from source location, if is available.
if (g.location) res = g.location->filename;
CHECK(res);
return res;
}
void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g) {
str->append("%s", GlobalFilename(g));
if (!g.location) return;
if (g.location->line_no) str->append(":%d", g.location->line_no);
if (g.location->column_no) str->append(":%d", g.location->column_no);
}
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
@ -319,6 +348,20 @@ void __asan_register_globals(__asan_global *globals, uptr n) {
Printf("=== ID %d; %p %p\n", stack_id, &globals[0], &globals[n - 1]);
}
for (uptr i = 0; i < n; i++) {
if (SANITIZER_WINDOWS && globals[i].beg == 0) {
// The MSVC incremental linker may pad globals out to 256 bytes. As long
// as __asan_global is less than 256 bytes large and its size is a power
// of two, we can skip over the padding.
static_assert(
sizeof(__asan_global) < 256 &&
(sizeof(__asan_global) & (sizeof(__asan_global) - 1)) == 0,
"sizeof(__asan_global) incompatible with incremental linker padding");
// If these are padding bytes, the rest of the global should be zero.
CHECK(globals[i].size == 0 && globals[i].size_with_redzone == 0 &&
globals[i].name == nullptr && globals[i].module_name == nullptr &&
globals[i].odr_indicator == 0);
continue;
}
RegisterGlobal(&globals[i]);
}
}
@ -329,6 +372,11 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return;
BlockingMutexLock lock(&mu_for_globals);
for (uptr i = 0; i < n; i++) {
if (SANITIZER_WINDOWS && globals[i].beg == 0) {
// Skip globals that look like padding from the MSVC incremental linker.
// See comment in __asan_register_globals.
continue;
}
UnregisterGlobal(&globals[i]);
}
}
@ -339,10 +387,10 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) {
// initializer can only touch global variables in the same TU.
void __asan_before_dynamic_init(const char *module_name) {
if (!flags()->check_initialization_order ||
!CanPoisonMemory())
!CanPoisonMemory() ||
!dynamic_init_globals)
return;
bool strict_init_order = flags()->strict_init_order;
CHECK(dynamic_init_globals);
CHECK(module_name);
CHECK(asan_inited);
BlockingMutexLock lock(&mu_for_globals);
@ -365,7 +413,8 @@ void __asan_before_dynamic_init(const char *module_name) {
// TU are poisoned. It simply unpoisons all dynamically initialized globals.
void __asan_after_dynamic_init() {
if (!flags()->check_initialization_order ||
!CanPoisonMemory())
!CanPoisonMemory() ||
!dynamic_init_globals)
return;
CHECK(asan_inited);
BlockingMutexLock lock(&mu_for_globals);

View file

@ -0,0 +1,62 @@
//===-- asan_globals_win.cc -----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Global registration code that is linked into every Windows DLL and EXE.
//
//===----------------------------------------------------------------------===//
#include "asan_interface_internal.h"
#if SANITIZER_WINDOWS
namespace __asan {
#pragma section(".ASAN$GA", read, write) // NOLINT
#pragma section(".ASAN$GZ", read, write) // NOLINT
extern "C" __declspec(allocate(".ASAN$GA"))
__asan_global __asan_globals_start = {};
extern "C" __declspec(allocate(".ASAN$GZ"))
__asan_global __asan_globals_end = {};
#pragma comment(linker, "/merge:.ASAN=.data")
static void call_on_globals(void (*hook)(__asan_global *, uptr)) {
__asan_global *start = &__asan_globals_start + 1;
__asan_global *end = &__asan_globals_end;
uptr bytediff = (uptr)end - (uptr)start;
if (bytediff % sizeof(__asan_global) != 0) {
#ifdef ASAN_DLL_THUNK
__debugbreak();
#else
CHECK("corrupt asan global array");
#endif
}
// We know end >= start because the linker sorts the portion after the dollar
// sign alphabetically.
uptr n = end - start;
hook(start, n);
}
static void register_dso_globals() {
call_on_globals(&__asan_register_globals);
}
static void unregister_dso_globals() {
call_on_globals(&__asan_unregister_globals);
}
// Register globals
#pragma section(".CRT$XCU", long, read) // NOLINT
#pragma section(".CRT$XTX", long, read) // NOLINT
extern "C" __declspec(allocate(".CRT$XCU"))
void (*const __asan_dso_reg_hook)() = &register_dso_globals;
extern "C" __declspec(allocate(".CRT$XTX"))
void (*const __asan_dso_unreg_hook)() = &unregister_dso_globals;
} // namespace __asan
#endif // SANITIZER_WINDOWS

View file

@ -0,0 +1,34 @@
//===-- asan_globals_win.h --------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Interface to the Windows-specific global management code. Separated into a
// standalone header to allow inclusion from asan_win_dynamic_runtime_thunk,
// which defines symbols that clash with other sanitizer headers.
//
//===----------------------------------------------------------------------===//
#ifndef ASAN_GLOBALS_WIN_H
#define ASAN_GLOBALS_WIN_H
#if !defined(_MSC_VER)
#error "this file is Windows-only, and uses MSVC pragmas"
#endif
#if defined(_WIN64)
#define SANITIZER_SYM_PREFIX
#else
#define SANITIZER_SYM_PREFIX "_"
#endif
// Use this macro to force linking asan_globals_win.cc into the DSO.
#define ASAN_LINK_GLOBALS_WIN() \
__pragma( \
comment(linker, "/include:" SANITIZER_SYM_PREFIX "__asan_dso_reg_hook"))
#endif // ASAN_GLOBALS_WIN_H

View file

@ -81,6 +81,51 @@ struct AsanInterceptorContext {
} \
} while (0)
// memcpy is called during __asan_init() from the internals of printf(...).
// We do not treat memcpy with to==from as a bug.
// See http://llvm.org/bugs/show_bug.cgi?id=11763.
#define ASAN_MEMCPY_IMPL(ctx, to, from, size) \
do { \
if (UNLIKELY(!asan_inited)) return internal_memcpy(to, from, size); \
if (asan_init_is_running) { \
return REAL(memcpy)(to, from, size); \
} \
ENSURE_ASAN_INITED(); \
if (flags()->replace_intrin) { \
if (to != from) { \
CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); \
} \
ASAN_READ_RANGE(ctx, from, size); \
ASAN_WRITE_RANGE(ctx, to, size); \
} \
return REAL(memcpy)(to, from, size); \
} while (0)
// memset is called inside Printf.
#define ASAN_MEMSET_IMPL(ctx, block, c, size) \
do { \
if (UNLIKELY(!asan_inited)) return internal_memset(block, c, size); \
if (asan_init_is_running) { \
return REAL(memset)(block, c, size); \
} \
ENSURE_ASAN_INITED(); \
if (flags()->replace_intrin) { \
ASAN_WRITE_RANGE(ctx, block, size); \
} \
return REAL(memset)(block, c, size); \
} while (0)
#define ASAN_MEMMOVE_IMPL(ctx, to, from, size) \
do { \
if (UNLIKELY(!asan_inited)) return internal_memmove(to, from, size); \
ENSURE_ASAN_INITED(); \
if (flags()->replace_intrin) { \
ASAN_READ_RANGE(ctx, from, size); \
ASAN_WRITE_RANGE(ctx, to, size); \
} \
return internal_memmove(to, from, size); \
} while (0)
#define ASAN_READ_RANGE(ctx, offset, size) \
ACCESS_MEMORY_RANGE(ctx, offset, size, false)
#define ASAN_WRITE_RANGE(ctx, offset, size) \
@ -198,10 +243,25 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
} else { \
*begin = *end = 0; \
}
// Asan needs custom handling of these:
#undef SANITIZER_INTERCEPT_MEMSET
#undef SANITIZER_INTERCEPT_MEMMOVE
#undef SANITIZER_INTERCEPT_MEMCPY
#define COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size) \
do { \
ASAN_INTERCEPTOR_ENTER(ctx, memmove); \
ASAN_MEMMOVE_IMPL(ctx, to, from, size); \
} while (false)
#define COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size) \
do { \
ASAN_INTERCEPTOR_ENTER(ctx, memcpy); \
ASAN_MEMCPY_IMPL(ctx, to, from, size); \
} while (false)
#define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size) \
do { \
ASAN_INTERCEPTOR_ENTER(ctx, memset); \
ASAN_MEMSET_IMPL(ctx, block, c, size); \
} while (false)
#include "sanitizer_common/sanitizer_common_interceptors.inc"
// Syscall interceptors don't have contexts, we don't support suppressions
@ -389,90 +449,18 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
}
#endif
// memcpy is called during __asan_init() from the internals of printf(...).
// We do not treat memcpy with to==from as a bug.
// See http://llvm.org/bugs/show_bug.cgi?id=11763.
#define ASAN_MEMCPY_IMPL(ctx, to, from, size) do { \
if (UNLIKELY(!asan_inited)) return internal_memcpy(to, from, size); \
if (asan_init_is_running) { \
return REAL(memcpy)(to, from, size); \
} \
ENSURE_ASAN_INITED(); \
if (flags()->replace_intrin) { \
if (to != from) { \
CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); \
} \
ASAN_READ_RANGE(ctx, from, size); \
ASAN_WRITE_RANGE(ctx, to, size); \
} \
return REAL(memcpy)(to, from, size); \
} while (0)
void *__asan_memcpy(void *to, const void *from, uptr size) {
ASAN_MEMCPY_IMPL(nullptr, to, from, size);
}
// memset is called inside Printf.
#define ASAN_MEMSET_IMPL(ctx, block, c, size) do { \
if (UNLIKELY(!asan_inited)) return internal_memset(block, c, size); \
if (asan_init_is_running) { \
return REAL(memset)(block, c, size); \
} \
ENSURE_ASAN_INITED(); \
if (flags()->replace_intrin) { \
ASAN_WRITE_RANGE(ctx, block, size); \
} \
return REAL(memset)(block, c, size); \
} while (0)
void *__asan_memset(void *block, int c, uptr size) {
ASAN_MEMSET_IMPL(nullptr, block, c, size);
}
#define ASAN_MEMMOVE_IMPL(ctx, to, from, size) do { \
if (UNLIKELY(!asan_inited)) \
return internal_memmove(to, from, size); \
ENSURE_ASAN_INITED(); \
if (flags()->replace_intrin) { \
ASAN_READ_RANGE(ctx, from, size); \
ASAN_WRITE_RANGE(ctx, to, size); \
} \
return internal_memmove(to, from, size); \
} while (0)
void *__asan_memmove(void *to, const void *from, uptr size) {
ASAN_MEMMOVE_IMPL(nullptr, to, from, size);
}
INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, memmove);
ASAN_MEMMOVE_IMPL(ctx, to, from, size);
}
INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, memcpy);
#if !SANITIZER_MAC
ASAN_MEMCPY_IMPL(ctx, to, from, size);
#else
// At least on 10.7 and 10.8 both memcpy() and memmove() are being replaced
// with WRAP(memcpy). As a result, false positives are reported for memmove()
// calls. If we just disable error reporting with
// ASAN_OPTIONS=replace_intrin=0, memmove() is still replaced with
// internal_memcpy(), which may lead to crashes, see
// http://llvm.org/bugs/show_bug.cgi?id=16362.
ASAN_MEMMOVE_IMPL(ctx, to, from, size);
#endif // !SANITIZER_MAC
}
INTERCEPTOR(void*, memset, void *block, int c, uptr size) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, memset);
ASAN_MEMSET_IMPL(ctx, block, c, size);
}
#if ASAN_INTERCEPT_INDEX
# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
INTERCEPTOR(char*, index, const char *string, int c)
@ -720,19 +708,10 @@ INTERCEPTOR(int, fork, void) {
namespace __asan {
void InitializeAsanInterceptors() {
static bool was_called_once;
CHECK(was_called_once == false);
CHECK(!was_called_once);
was_called_once = true;
InitializeCommonInterceptors();
// Intercept mem* functions.
ASAN_INTERCEPT_FUNC(memcpy);
ASAN_INTERCEPT_FUNC(memset);
if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) {
// In asan, REAL(memmove) is not used, but it is used in msan.
ASAN_INTERCEPT_FUNC(memmove);
}
CHECK(REAL(memcpy));
// Intercept str* functions.
ASAN_INTERCEPT_FUNC(strcat); // NOLINT
ASAN_INTERCEPT_FUNC(strcpy); // NOLINT

View file

@ -23,6 +23,8 @@
#include "asan_init_version.h"
using __sanitizer::uptr;
using __sanitizer::u64;
using __sanitizer::u32;
extern "C" {
// This function should be called at the very beginning of the process,
@ -79,6 +81,20 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_after_dynamic_init();
// Sets bytes of the given range of the shadow memory into specific value.
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_set_shadow_00(uptr addr, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_set_shadow_f1(uptr addr, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_set_shadow_f2(uptr addr, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_set_shadow_f3(uptr addr, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_set_shadow_f5(uptr addr, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_set_shadow_f8(uptr addr, uptr size);
// These two functions are used by instrumented code in the
// use-after-scope mode. They mark memory for local variables as
// unaddressable when they leave scope and addressable before the
@ -156,6 +172,9 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
/* OPTIONAL */ const char* __asan_default_options();
SANITIZER_INTERFACE_ATTRIBUTE
extern uptr __asan_shadow_memory_dynamic_address;
// Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return
SANITIZER_INTERFACE_ATTRIBUTE
extern int __asan_option_detect_stack_use_after_return;

View file

@ -36,7 +36,7 @@
// 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
# if SANITIZER_IOS || (SANITIZER_WORDSIZE == 32)
# if SANITIZER_IOS || SANITIZER_ANDROID
# define ASAN_LOW_MEMORY 1
# else
# define ASAN_LOW_MEMORY 0
@ -65,6 +65,9 @@ void AsanInitFromRtl();
// asan_win.cc
void InitializePlatformExceptionHandlers();
// asan_win.cc / asan_posix.cc
const char *DescribeSignalOrException(int signo);
// asan_rtl.cc
void NORETURN ShowStatsAndAbort();
@ -100,17 +103,6 @@ void *AsanDlSymNext(const char *sym);
void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name);
// Platform-specific options.
#if SANITIZER_MAC
bool PlatformHasDifferentMemcpyAndMemmove();
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \
(PlatformHasDifferentMemcpyAndMemmove())
#elif SANITIZER_WINDOWS64
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE false
#else
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true
#endif // SANITIZER_MAC
// Add convenient macro for interface functions that may be represented as
// weak hooks.
#define ASAN_MALLOC_HOOK(ptr, size) \
@ -132,12 +124,10 @@ extern bool asan_init_is_running;
extern void (*death_callback)(void);
// These magic values are written to shadow for better error reporting.
const int kAsanHeapLeftRedzoneMagic = 0xfa;
const int kAsanHeapRightRedzoneMagic = 0xfb;
const int kAsanHeapFreeMagic = 0xfd;
const int kAsanStackLeftRedzoneMagic = 0xf1;
const int kAsanStackMidRedzoneMagic = 0xf2;
const int kAsanStackRightRedzoneMagic = 0xf3;
const int kAsanStackPartialRedzoneMagic = 0xf4;
const int kAsanStackAfterReturnMagic = 0xf5;
const int kAsanInitializationOrderMagic = 0xf6;
const int kAsanUserPoisonedMemoryMagic = 0xf7;

View file

@ -49,15 +49,6 @@ namespace __asan {
void InitializePlatformInterceptors() {}
void InitializePlatformExceptionHandlers() {}
bool PlatformHasDifferentMemcpyAndMemmove() {
// On OS X 10.7 memcpy() and memmove() are both resolved
// into memmove$VARIANT$sse42.
// See also https://github.com/google/sanitizers/issues/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 0;

View file

@ -78,7 +78,13 @@ INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
void *new_ptr = asan_malloc(size, &stack);
void *new_ptr;
if (UNLIKELY(!asan_inited)) {
new_ptr = AllocateFromLocalPool(size);
} else {
copy_size = size;
new_ptr = asan_malloc(copy_size, &stack);
}
internal_memcpy(new_ptr, ptr, copy_size);
return new_ptr;
}

View file

@ -124,6 +124,11 @@ void *_recalloc(void *p, size_t n, size_t elem_size) {
return realloc(p, size);
}
ALLOCATION_FUNCTION_ATTRIBUTE
void *_recalloc_base(void *p, size_t n, size_t elem_size) {
return _recalloc(p, n, elem_size);
}
ALLOCATION_FUNCTION_ATTRIBUTE
size_t _msize(const void *ptr) {
GET_CURRENT_PC_BP_SP;
@ -223,6 +228,7 @@ void ReplaceSystemMalloc() {
TryToOverrideFunction("_realloc_base", (uptr)realloc);
TryToOverrideFunction("_realloc_crt", (uptr)realloc);
TryToOverrideFunction("_recalloc", (uptr)_recalloc);
TryToOverrideFunction("_recalloc_base", (uptr)_recalloc);
TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc);
TryToOverrideFunction("_msize", (uptr)_msize);
TryToOverrideFunction("_expand", (uptr)_expand);

View file

@ -125,6 +125,7 @@
// || `[0x00000000, 0x2fffffff]` || LowMem ||
static const u64 kDefaultShadowScale = 3;
static const u64 kDefaultShadowSentinel = ~(uptr)0;
static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000
static const u64 kDefaultShadowOffset64 = 1ULL << 44;
static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G.
@ -140,7 +141,6 @@ static const u64 kSystemZ_ShadowOffset64 = 1ULL << 52;
static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000
static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000
static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
static const u64 kWindowsShadowOffset64 = 1ULL << 45; // 32TB
#define SHADOW_SCALE kDefaultShadowScale
@ -168,7 +168,7 @@ static const u64 kWindowsShadowOffset64 = 1ULL << 45; // 32TB
# if SANITIZER_IOSSIM
# define SHADOW_OFFSET kIosSimShadowOffset64
# else
# define SHADOW_OFFSET kIosShadowOffset64
# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address
# endif
# elif defined(__aarch64__)
# define SHADOW_OFFSET kAArch64_ShadowOffset64
@ -183,7 +183,7 @@ static const u64 kWindowsShadowOffset64 = 1ULL << 45; // 32TB
# elif defined(__mips64)
# define SHADOW_OFFSET kMIPS64_ShadowOffset64
# elif SANITIZER_WINDOWS64
# define SHADOW_OFFSET kWindowsShadowOffset64
# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address
# else
# define SHADOW_OFFSET kDefaultShort64bitShadowOffset
# endif
@ -269,9 +269,25 @@ static inline bool AddrIsInMidMem(uptr a) {
return kMidMemBeg && a >= kMidMemBeg && a <= kMidMemEnd;
}
static inline bool AddrIsInShadowGap(uptr a) {
PROFILE_ASAN_MAPPING();
if (kMidMemBeg) {
if (a <= kShadowGapEnd)
return SHADOW_OFFSET == 0 || a >= kShadowGapBeg;
return (a >= kShadowGap2Beg && a <= kShadowGap2End) ||
(a >= kShadowGap3Beg && a <= kShadowGap3End);
}
// In zero-based shadow mode we treat addresses near zero as addresses
// in shadow gap as well.
if (SHADOW_OFFSET == 0)
return a <= kShadowGapEnd;
return a >= kShadowGapBeg && a <= kShadowGapEnd;
}
static inline bool AddrIsInMem(uptr a) {
PROFILE_ASAN_MAPPING();
return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a);
return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a) ||
(flags()->protect_shadow_gap == 0 && AddrIsInShadowGap(a));
}
static inline uptr MemToShadow(uptr p) {
@ -295,21 +311,6 @@ static inline bool AddrIsInShadow(uptr a) {
return AddrIsInLowShadow(a) || AddrIsInMidShadow(a) || AddrIsInHighShadow(a);
}
static inline bool AddrIsInShadowGap(uptr a) {
PROFILE_ASAN_MAPPING();
if (kMidMemBeg) {
if (a <= kShadowGapEnd)
return SHADOW_OFFSET == 0 || a >= kShadowGapBeg;
return (a >= kShadowGap2Beg && a <= kShadowGap2End) ||
(a >= kShadowGap3Beg && a <= kShadowGap3End);
}
// In zero-based shadow mode we treat addresses near zero as addresses
// in shadow gap as well.
if (SHADOW_OFFSET == 0)
return a <= kShadowGapEnd;
return a >= kShadowGapBeg && a <= kShadowGapEnd;
}
static inline bool AddrIsAlignedByGranularity(uptr a) {
PROFILE_ASAN_MAPPING();
return (a & (SHADOW_GRANULARITY - 1)) == 0;

View file

@ -74,7 +74,7 @@ class HeapProfile {
static void ChunkCallback(uptr chunk, void *arg) {
HeapProfile *hp = reinterpret_cast<HeapProfile*>(arg);
AsanChunkView cv = FindHeapChunkByAddress(chunk);
AsanChunkView cv = FindHeapChunkByAllocBeg(chunk);
if (!cv.IsAllocated()) return;
u32 id = cv.GetAllocStackId();
if (!id) return;

View file

@ -45,26 +45,6 @@
using namespace __asan; // NOLINT
// This code has issues on OSX.
// See https://github.com/google/sanitizers/issues/131.
// Fake std::nothrow_t to avoid including <new>.
namespace std {
struct nothrow_t {};
} // namespace std
#define OPERATOR_NEW_BODY(type) \
GET_STACK_TRACE_MALLOC;\
return asan_memalign(0, size, &stack, type);
// On OS X it's not enough to just provide our own 'operator new' and
// 'operator delete' implementations, because they're going to be in the
// runtime dylib, and the main executable will depend on both the runtime
// dylib and libstdc++, each of those'll have its implementation of new and
// delete.
// To make sure that C++ allocation/deallocation operators are overridden on
// OS X we need to intercept them using their mangled names.
#if !SANITIZER_MAC
// FreeBSD prior v9.2 have wrong definition of 'size_t'.
// http://svnweb.freebsd.org/base?view=revision&revision=232261
#if SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
@ -74,6 +54,30 @@ struct nothrow_t {};
#endif // __FreeBSD_version
#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
// This code has issues on OSX.
// See https://github.com/google/sanitizers/issues/131.
// Fake std::nothrow_t and std::align_val_t to avoid including <new>.
namespace std {
struct nothrow_t {};
enum class align_val_t: size_t {};
} // namespace std
#define OPERATOR_NEW_BODY(type) \
GET_STACK_TRACE_MALLOC;\
return asan_memalign(0, size, &stack, type);
#define OPERATOR_NEW_BODY_ALIGN(type) \
GET_STACK_TRACE_MALLOC;\
return asan_memalign((uptr)align, size, &stack, type);
// On OS X it's not enough to just provide our own 'operator new' and
// 'operator delete' implementations, because they're going to be in the
// runtime dylib, and the main executable will depend on both the runtime
// dylib and libstdc++, each of those'll have its implementation of new and
// delete.
// To make sure that C++ allocation/deallocation operators are overridden on
// OS X we need to intercept them using their mangled names.
#if !SANITIZER_MAC
CXX_OPERATOR_ATTRIBUTE
void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); }
CXX_OPERATOR_ATTRIBUTE
@ -84,6 +88,18 @@ void *operator new(size_t size, std::nothrow_t const&)
CXX_OPERATOR_ATTRIBUTE
void *operator new[](size_t size, std::nothrow_t const&)
{ OPERATOR_NEW_BODY(FROM_NEW_BR); }
CXX_OPERATOR_ATTRIBUTE
void *operator new(size_t size, std::align_val_t align)
{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW); }
CXX_OPERATOR_ATTRIBUTE
void *operator new[](size_t size, std::align_val_t align)
{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR); }
CXX_OPERATOR_ATTRIBUTE
void *operator new(size_t size, std::align_val_t align, std::nothrow_t const&)
{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW); }
CXX_OPERATOR_ATTRIBUTE
void *operator new[](size_t size, std::align_val_t align, std::nothrow_t const&)
{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR); }
#else // SANITIZER_MAC
INTERCEPTOR(void *, _Znwm, size_t size) {
@ -131,6 +147,32 @@ void operator delete[](void *ptr, size_t size) NOEXCEPT {
GET_STACK_TRACE_FREE;
asan_sized_free(ptr, size, &stack, FROM_NEW_BR);
}
CXX_OPERATOR_ATTRIBUTE
void operator delete(void *ptr, std::align_val_t) NOEXCEPT {
OPERATOR_DELETE_BODY(FROM_NEW);
}
CXX_OPERATOR_ATTRIBUTE
void operator delete[](void *ptr, std::align_val_t) NOEXCEPT {
OPERATOR_DELETE_BODY(FROM_NEW_BR);
}
CXX_OPERATOR_ATTRIBUTE
void operator delete(void *ptr, std::align_val_t, std::nothrow_t const&) {
OPERATOR_DELETE_BODY(FROM_NEW);
}
CXX_OPERATOR_ATTRIBUTE
void operator delete[](void *ptr, std::align_val_t, std::nothrow_t const&) {
OPERATOR_DELETE_BODY(FROM_NEW_BR);
}
CXX_OPERATOR_ATTRIBUTE
void operator delete(void *ptr, size_t size, std::align_val_t) NOEXCEPT {
GET_STACK_TRACE_FREE;
asan_sized_free(ptr, size, &stack, FROM_NEW);
}
CXX_OPERATOR_ATTRIBUTE
void operator delete[](void *ptr, size_t size, std::align_val_t) NOEXCEPT {
GET_STACK_TRACE_FREE;
asan_sized_free(ptr, size, &stack, FROM_NEW_BR);
}
#else // SANITIZER_MAC
INTERCEPTOR(void, _ZdlPv, void *ptr) {

View file

@ -64,12 +64,9 @@ struct ShadowSegmentEndpoint {
};
void FlushUnneededASanShadowMemory(uptr p, uptr size) {
// Since asan's mapping is compacting, the shadow chunk may be
// not page-aligned, so we only flush the page-aligned portion.
uptr page_size = GetPageSizeCached();
uptr shadow_beg = RoundUpTo(MemToShadow(p), page_size);
uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size);
FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
// Since asan's mapping is compacting, the shadow chunk may be
// not page-aligned, so we only flush the page-aligned portion.
ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size));
}
void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) {
@ -117,9 +114,9 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) {
ShadowSegmentEndpoint beg(beg_addr);
ShadowSegmentEndpoint end(end_addr);
if (beg.chunk == end.chunk) {
CHECK(beg.offset < end.offset);
CHECK_LT(beg.offset, end.offset);
s8 value = beg.value;
CHECK(value == end.value);
CHECK_EQ(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.
if (value > 0 && value <= end.offset) {
@ -131,7 +128,7 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) {
}
return;
}
CHECK(beg.chunk < end.chunk);
CHECK_LT(beg.chunk, end.chunk);
if (beg.offset > 0) {
// Mark bytes from beg.offset as unaddressable.
if (beg.value == 0) {
@ -157,9 +154,9 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
ShadowSegmentEndpoint beg(beg_addr);
ShadowSegmentEndpoint end(end_addr);
if (beg.chunk == end.chunk) {
CHECK(beg.offset < end.offset);
CHECK_LT(beg.offset, end.offset);
s8 value = beg.value;
CHECK(value == end.value);
CHECK_EQ(value, end.value);
// We unpoison memory bytes up to enbytes up to end.offset if it is not
// unpoisoned already.
if (value != 0) {
@ -167,7 +164,7 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
}
return;
}
CHECK(beg.chunk < end.chunk);
CHECK_LT(beg.chunk, end.chunk);
if (beg.offset > 0) {
*beg.chunk = 0;
beg.chunk++;
@ -314,6 +311,30 @@ static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
}
}
void __asan_set_shadow_00(uptr addr, uptr size) {
REAL(memset)((void *)addr, 0, size);
}
void __asan_set_shadow_f1(uptr addr, uptr size) {
REAL(memset)((void *)addr, 0xf1, size);
}
void __asan_set_shadow_f2(uptr addr, uptr size) {
REAL(memset)((void *)addr, 0xf2, size);
}
void __asan_set_shadow_f3(uptr addr, uptr size) {
REAL(memset)((void *)addr, 0xf3, size);
}
void __asan_set_shadow_f5(uptr addr, uptr size) {
REAL(memset)((void *)addr, 0xf5, size);
}
void __asan_set_shadow_f8(uptr addr, uptr size) {
REAL(memset)((void *)addr, 0xf8, size);
}
void __asan_poison_stack_memory(uptr addr, uptr size) {
VReport(1, "poisoning: %p %zx\n", (void *)addr, size);
PoisonAlignedStackMemory(addr, size, true);
@ -388,7 +409,7 @@ const void *__sanitizer_contiguous_container_find_bad_address(
// ending with end.
uptr kMaxRangeToCheck = 32;
uptr r1_beg = beg;
uptr r1_end = Min(end + kMaxRangeToCheck, mid);
uptr r1_end = Min(beg + kMaxRangeToCheck, mid);
uptr r2_beg = Max(beg, mid - kMaxRangeToCheck);
uptr r2_end = Min(end, mid + kMaxRangeToCheck);
uptr r3_beg = Max(end - kMaxRangeToCheck, mid);

View file

@ -86,8 +86,8 @@ ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone(
}
}
// Calls __sanitizer::FlushUnneededShadowMemory() on
// [MemToShadow(p), MemToShadow(p+size)] with proper rounding.
// Calls __sanitizer::ReleaseMemoryPagesToOS() on
// [MemToShadow(p), MemToShadow(p+size)].
void FlushUnneededASanShadowMemory(uptr p, uptr size);
} // namespace __asan

View file

@ -33,6 +33,19 @@
namespace __asan {
const char *DescribeSignalOrException(int signo) {
switch (signo) {
case SIGFPE:
return "FPE";
case SIGILL:
return "ILL";
case SIGABRT:
return "ABRT";
default:
return "SEGV";
}
}
void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
ScopedDeadlySignal signal_scope(GetCurrentThread());
int code = (int)((siginfo_t*)siginfo)->si_code;
@ -84,12 +97,8 @@ void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
// unaligned memory access.
if (IsStackAccess && (code == si_SEGV_MAPERR || code == si_SEGV_ACCERR))
ReportStackOverflow(sig);
else if (signo == SIGFPE)
ReportDeadlySignal("FPE", sig);
else if (signo == SIGILL)
ReportDeadlySignal("ILL", sig);
else
ReportDeadlySignal("SEGV", sig);
ReportDeadlySignal(signo, sig);
}
// ---------------------- TSD ---------------- {{{1

File diff suppressed because it is too large Load diff

View file

@ -25,35 +25,29 @@ struct StackVarDescr {
uptr name_len;
};
struct AddressDescription {
char *name;
uptr name_size;
uptr region_address;
uptr region_size;
const char *region_kind;
};
// Returns the number of globals close to the provided address and copies
// them to "globals" array.
int GetGlobalsForAddress(uptr addr, __asan_global *globals, u32 *reg_sites,
int max_globals);
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr);
const char *MaybeDemangleGlobalName(const char *name);
void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g);
void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g);
void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte,
bool in_shadow, const char *after = "\n");
// The following functions prints address description depending
// on the memory type (shadow/heap/stack/global).
void DescribeHeapAddress(uptr addr, uptr access_size);
bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr = nullptr,
bool print = true);
bool ParseFrameDescription(const char *frame_descr,
InternalMmapVector<StackVarDescr> *vars);
bool DescribeAddressIfStack(uptr addr, uptr access_size);
void DescribeThread(AsanThreadContext *context);
// Different kinds of error reports.
void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
uptr access_size, u32 exp, bool fatal);
void ReportStackOverflow(const SignalContext &sig);
void ReportDeadlySignal(const char *description, const SignalContext &sig);
void ReportNewDeleteSizeMismatch(uptr addr, uptr alloc_size, uptr delete_size,
void ReportDeadlySignal(int signo, const SignalContext &sig);
void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
BufferedStackTrace *free_stack);
void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);

View file

@ -32,6 +32,7 @@
#include "ubsan/ubsan_init.h"
#include "ubsan/ubsan_platform.h"
uptr __asan_shadow_memory_dynamic_address; // Global interface symbol.
int __asan_option_detect_stack_use_after_return; // Global interface symbol.
uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan.
@ -263,6 +264,7 @@ static NOINLINE void force_interface_symbols() {
volatile int fake_condition = 0; // prevent dead condition elimination.
// __asan_report_* functions are noreturn, so we need a switch to prevent
// the compiler from removing any of them.
// clang-format off
switch (fake_condition) {
case 1: __asan_report_load1(0); break;
case 2: __asan_report_load2(0); break;
@ -302,7 +304,14 @@ static NOINLINE void force_interface_symbols() {
case 37: __asan_unpoison_stack_memory(0, 0); break;
case 38: __asan_region_is_poisoned(0, 0); break;
case 39: __asan_describe_address(0); break;
case 40: __asan_set_shadow_00(0, 0); break;
case 41: __asan_set_shadow_f1(0, 0); break;
case 42: __asan_set_shadow_f2(0, 0); break;
case 43: __asan_set_shadow_f3(0, 0); break;
case 44: __asan_set_shadow_f5(0, 0); break;
case 45: __asan_set_shadow_f8(0, 0); break;
}
// clang-format on
}
static void asan_atexit() {
@ -326,8 +335,21 @@ static void InitializeHighMemEnd() {
}
static void ProtectGap(uptr addr, uptr size) {
if (!flags()->protect_shadow_gap)
if (!flags()->protect_shadow_gap) {
// The shadow gap is unprotected, so there is a chance that someone
// is actually using this memory. Which means it needs a shadow...
uptr GapShadowBeg = RoundDownTo(MEM_TO_SHADOW(addr), GetPageSizeCached());
uptr GapShadowEnd =
RoundUpTo(MEM_TO_SHADOW(addr + size), GetPageSizeCached()) - 1;
if (Verbosity())
Printf("protect_shadow_gap=0:"
" not protecting shadow gap, allocating gap's shadow\n"
"|| `[%p, %p]` || ShadowGap's shadow ||\n", GapShadowBeg,
GapShadowEnd);
ReserveShadowMemoryRange(GapShadowBeg, GapShadowEnd,
"unprotected gap shadow");
return;
}
void *res = MmapFixedNoAccess(addr, size, "shadow gap");
if (addr == (uptr)res)
return;
@ -388,6 +410,8 @@ static void PrintAddressSpaceLayout() {
Printf("redzone=%zu\n", (uptr)flags()->redzone);
Printf("max_redzone=%zu\n", (uptr)flags()->max_redzone);
Printf("quarantine_size_mb=%zuM\n", (uptr)flags()->quarantine_size_mb);
Printf("thread_local_quarantine_size_kb=%zuK\n",
(uptr)flags()->thread_local_quarantine_size_kb);
Printf("malloc_context_size=%zu\n",
(uptr)common_flags()->malloc_context_size);
@ -401,6 +425,79 @@ static void PrintAddressSpaceLayout() {
kHighShadowBeg > kMidMemEnd);
}
static void InitializeShadowMemory() {
// Set the shadow memory address to uninitialized.
__asan_shadow_memory_dynamic_address = kDefaultShadowSentinel;
uptr shadow_start = kLowShadowBeg;
// Detect if a dynamic shadow address must used and find a available location
// when necessary. When dynamic address is used, the macro |kLowShadowBeg|
// expands to |__asan_shadow_memory_dynamic_address| which is
// |kDefaultShadowSentinel|.
if (shadow_start == kDefaultShadowSentinel) {
__asan_shadow_memory_dynamic_address = 0;
CHECK_EQ(0, kLowShadowBeg);
uptr granularity = GetMmapGranularity();
uptr alignment = 8 * granularity;
uptr left_padding = granularity;
uptr space_size = kHighShadowEnd + left_padding;
shadow_start = FindAvailableMemoryRange(space_size, alignment, granularity);
CHECK_NE((uptr)0, shadow_start);
CHECK(IsAligned(shadow_start, alignment));
}
// Update the shadow memory address (potentially) used by instrumentation.
__asan_shadow_memory_dynamic_address = shadow_start;
if (kLowShadowBeg)
shadow_start -= GetMmapGranularity();
bool full_shadow_is_available =
MemoryRangeIsAvailable(shadow_start, kHighShadowEnd);
#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \
!ASAN_FIXED_MAPPING
if (!full_shadow_is_available) {
kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
}
#endif
if (Verbosity()) PrintAddressSpaceLayout();
if (full_shadow_is_available) {
// mmap the low shadow plus at least one page at the left.
if (kLowShadowBeg)
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
// mmap the high shadow.
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
// protect the gap.
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1);
} else if (kMidMemBeg &&
MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) &&
MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) {
CHECK(kLowShadowBeg != kLowShadowEnd);
// mmap the low shadow plus at least one page at the left.
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
// mmap the mid shadow.
ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow");
// mmap the high shadow.
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
// protect the gaps.
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1);
} else {
Report("Shadow memory range interleaves with an existing memory mapping. "
"ASan cannot proceed correctly. ABORTING.\n");
Report("ASan shadow was supposed to be located in the [%p-%p] range.\n",
shadow_start, kHighShadowEnd);
DumpProcessMap();
Die();
}
}
static void AsanInitInternal() {
if (LIKELY(asan_inited)) return;
SanitizerToolName = "AddressSanitizer";
@ -434,7 +531,6 @@ static void AsanInitInternal() {
__sanitizer_set_report_path(common_flags()->log_path);
// Enable UAR detection, if required.
__asan_option_detect_stack_use_after_return =
flags()->detect_stack_use_after_return;
@ -453,61 +549,9 @@ static void AsanInitInternal() {
ReplaceSystemMalloc();
uptr shadow_start = kLowShadowBeg;
if (kLowShadowBeg)
shadow_start -= GetMmapGranularity();
bool full_shadow_is_available =
MemoryRangeIsAvailable(shadow_start, kHighShadowEnd);
#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \
!ASAN_FIXED_MAPPING
if (!full_shadow_is_available) {
kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
}
#elif SANITIZER_WINDOWS64
// Disable the "mid mem" shadow layout.
if (!full_shadow_is_available) {
kMidMemBeg = 0;
kMidMemEnd = 0;
}
#endif
if (Verbosity()) PrintAddressSpaceLayout();
DisableCoreDumperIfNecessary();
if (full_shadow_is_available) {
// mmap the low shadow plus at least one page at the left.
if (kLowShadowBeg)
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
// mmap the high shadow.
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
// protect the gap.
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1);
} else if (kMidMemBeg &&
MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) &&
MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) {
CHECK(kLowShadowBeg != kLowShadowEnd);
// mmap the low shadow plus at least one page at the left.
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
// mmap the mid shadow.
ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow");
// mmap the high shadow.
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
// protect the gaps.
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1);
} else {
Report("Shadow memory range interleaves with an existing memory mapping. "
"ASan cannot proceed correctly. ABORTING.\n");
Report("ASan shadow was supposed to be located in the [%p-%p] range.\n",
shadow_start, kHighShadowEnd);
DumpProcessMap();
Die();
}
InitializeShadowMemory();
AsanTSDInit(PlatformTSDDtor);
InstallDeadlySignalHandlers(AsanOnDeadlySignal);
@ -599,6 +643,9 @@ static AsanInitializer asan_initializer;
using namespace __asan; // NOLINT
void NOINLINE __asan_handle_no_return() {
if (asan_init_is_running)
return;
int local_stack;
AsanThread *curr_thread = GetCurrentThread();
uptr PageSize = GetPageSizeCached();

View file

@ -34,10 +34,10 @@
#include "sanitizer_common/sanitizer_libc.h"
namespace __asan {
class ScarinessScore {
public:
ScarinessScore() {
struct ScarinessScoreBase {
void Clear() {
descr[0] = 0;
score = 0;
}
void Scare(int add_to_score, const char *reason) {
if (descr[0])
@ -52,16 +52,23 @@ class ScarinessScore {
Printf("SCARINESS: %d (%s)\n", score, descr);
}
static void PrintSimple(int score, const char *descr) {
ScarinessScore SS;
SS.Scare(score, descr);
SS.Print();
ScarinessScoreBase SSB;
SSB.Clear();
SSB.Scare(score, descr);
SSB.Print();
}
private:
int score = 0;
int score;
char descr[1024];
};
struct ScarinessScore : ScarinessScoreBase {
ScarinessScore() {
Clear();
}
};
} // namespace __asan
#endif // ASAN_SCARINESS_SCORE_H

View file

@ -141,7 +141,9 @@ void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom,
current_fake_stack->Destroy(this->tid());
}
void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save) {
void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save,
uptr *bottom_old,
uptr *size_old) {
if (!atomic_load(&stack_switching_, memory_order_relaxed)) {
Report("ERROR: finishing a fiber switch that has not started\n");
Die();
@ -152,6 +154,10 @@ void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save) {
fake_stack_ = fake_stack_save;
}
if (bottom_old)
*bottom_old = stack_bottom_;
if (size_old)
*size_old = stack_top_ - stack_bottom_;
stack_bottom_ = next_stack_bottom_;
stack_top_ = next_stack_top_;
atomic_store(&stack_switching_, 0, memory_order_release);
@ -345,7 +351,7 @@ AsanThread *GetCurrentThread() {
// limits, so only do this magic on Android, and only if the found thread
// is the main thread.
AsanThreadContext *tctx = GetThreadContextByTidLocked(0);
if (ThreadStackContainsAddress(tctx, &context)) {
if (tctx && ThreadStackContainsAddress(tctx, &context)) {
SetCurrentThread(tctx->thread);
return tctx->thread;
}
@ -447,12 +453,16 @@ void __sanitizer_start_switch_fiber(void **fakestacksave, const void *bottom,
}
SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_finish_switch_fiber(void* fakestack) {
void __sanitizer_finish_switch_fiber(void* fakestack,
const void **bottom_old,
uptr *size_old) {
AsanThread *t = GetCurrentThread();
if (!t) {
VReport(1, "__asan_finish_switch_fiber called from unknown thread\n");
return;
}
t->FinishSwitchFiber((FakeStack*)fakestack);
t->FinishSwitchFiber((FakeStack*)fakestack,
(uptr*)bottom_old,
(uptr*)size_old);
}
}

View file

@ -94,7 +94,8 @@ class AsanThread {
}
void StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom, uptr size);
void FinishSwitchFiber(FakeStack *fake_stack_save);
void FinishSwitchFiber(FakeStack *fake_stack_save, uptr *bottom_old,
uptr *size_old);
bool has_fake_stack() {
return !atomic_load(&stack_switching_, memory_order_relaxed) &&

View file

@ -19,6 +19,7 @@
#include <stdlib.h>
#include "asan_globals_win.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_report.h"
@ -37,7 +38,13 @@ int __asan_should_detect_stack_use_after_return() {
return __asan_option_detect_stack_use_after_return;
}
// -------------------- A workaround for the abscence of weak symbols ----- {{{
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_get_shadow_memory_dynamic_address() {
__asan_init();
return __asan_shadow_memory_dynamic_address;
}
// -------------------- A workaround for the absence of weak symbols ----- {{{
// We don't have a direct equivalent of weak symbols when using MSVC, but we can
// use the /alternatename directive to tell the linker to default a specific
// symbol to a specific value, which works nicely for allocator hooks and
@ -64,14 +71,22 @@ void __asan_default_on_error() {}
// }}}
} // extern "C"
// ---------------------- Windows-specific inteceptors ---------------- {{{
// ---------------------- Windows-specific interceptors ---------------- {{{
INTERCEPTOR_WINAPI(void, RtlRaiseException, EXCEPTION_RECORD *ExceptionRecord) {
CHECK(REAL(RtlRaiseException));
// This is a noreturn function, unless it's one of the exceptions raised to
// communicate with the debugger, such as the one from OutputDebugString.
if (ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C)
__asan_handle_no_return();
REAL(RtlRaiseException)(ExceptionRecord);
}
INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
CHECK(REAL(RaiseException));
__asan_handle_no_return();
REAL(RaiseException)(a, b, c, d);
}
#ifdef _WIN64
INTERCEPTOR_WINAPI(int, __C_specific_handler, void *a, void *b, void *c, void *d) { // NOLINT
@ -123,44 +138,12 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread,
asan_thread_start, t, thr_flags, tid);
}
namespace {
BlockingMutex mu_for_thread_tracking(LINKER_INITIALIZED);
void EnsureWorkerThreadRegistered() {
// FIXME: GetCurrentThread relies on TSD, which might not play well with
// system thread pools. We might want to use something like reference
// counting to zero out GetCurrentThread() underlying storage when the last
// work item finishes? Or can we disable reclaiming of threads in the pool?
BlockingMutexLock l(&mu_for_thread_tracking);
if (__asan::GetCurrentThread())
return;
AsanThread *t = AsanThread::Create(
/* start_routine */ nullptr, /* arg */ nullptr,
/* parent_tid */ -1, /* stack */ nullptr, /* detached */ true);
t->Init();
asanThreadRegistry().StartThread(t->tid(), 0, 0);
SetCurrentThread(t);
}
} // namespace
INTERCEPTOR_WINAPI(DWORD, NtWaitForWorkViaWorkerFactory, DWORD a, DWORD b) {
// NtWaitForWorkViaWorkerFactory is called from system worker pool threads to
// query work scheduled by BindIoCompletionCallback, QueueUserWorkItem, etc.
// System worker pool threads are created at arbitraty point in time and
// without using CreateThread, so we wrap NtWaitForWorkViaWorkerFactory
// instead and don't register a specific parent_tid/stack.
EnsureWorkerThreadRegistered();
return REAL(NtWaitForWorkViaWorkerFactory)(a, b);
}
// }}}
namespace __asan {
void InitializePlatformInterceptors() {
ASAN_INTERCEPT_FUNC(CreateThread);
ASAN_INTERCEPT_FUNC(RaiseException);
#ifdef _WIN64
ASAN_INTERCEPT_FUNC(__C_specific_handler);
@ -169,11 +152,15 @@ void InitializePlatformInterceptors() {
ASAN_INTERCEPT_FUNC(_except_handler4);
#endif
// NtWaitForWorkViaWorkerFactory is always linked dynamically.
CHECK(::__interception::OverrideFunction(
"NtWaitForWorkViaWorkerFactory",
(uptr)WRAP(NtWaitForWorkViaWorkerFactory),
(uptr *)&REAL(NtWaitForWorkViaWorkerFactory)));
// Try to intercept kernel32!RaiseException, and if that fails, intercept
// ntdll!RtlRaiseException instead.
if (!::__interception::OverrideFunction("RaiseException",
(uptr)WRAP(RaiseException),
(uptr *)&REAL(RaiseException))) {
CHECK(::__interception::OverrideFunction("RtlRaiseException",
(uptr)WRAP(RtlRaiseException),
(uptr *)&REAL(RtlRaiseException)));
}
}
void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
@ -229,8 +216,7 @@ void AsanOnDeadlySignal(int, void *siginfo, void *context) {
// Exception handler for dealing with shadow memory.
static LONG CALLBACK
ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
static uptr page_size = GetPageSizeCached();
static uptr alloc_granularity = GetMmapGranularity();
uptr page_size = GetPageSizeCached();
// Only handle access violations.
if (exception_pointers->ExceptionRecord->ExceptionCode !=
EXCEPTION_ACCESS_VIOLATION) {
@ -276,22 +262,57 @@ void InitializePlatformExceptionHandlers() {
static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;
static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
// Check based on flags if we should report this exception.
static bool ShouldReportDeadlyException(unsigned code) {
switch (code) {
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_IN_PAGE_ERROR:
return common_flags()->handle_segv;
case EXCEPTION_BREAKPOINT:
case EXCEPTION_ILLEGAL_INSTRUCTION: {
return common_flags()->handle_sigill;
}
}
return false;
}
// Return the textual name for this exception.
const char *DescribeSignalOrException(int signo) {
unsigned code = signo;
// Get the string description of the exception if this is a known deadly
// exception.
switch (code) {
case EXCEPTION_ACCESS_VIOLATION:
return "access-violation";
case EXCEPTION_IN_PAGE_ERROR:
return "in-page-error";
case EXCEPTION_BREAKPOINT:
return "breakpoint";
case EXCEPTION_ILLEGAL_INSTRUCTION:
return "illegal-instruction";
}
return nullptr;
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
long __asan_unhandled_exception_filter(EXCEPTION_POINTERS *info) {
EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
CONTEXT *context = info->ContextRecord;
if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
exception_record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) {
const char *description =
(exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
? "access-violation"
: "in-page-error";
SignalContext sig = SignalContext::Create(exception_record, context);
ReportDeadlySignal(description, sig);
}
// Continue the search if the signal wasn't deadly.
if (!ShouldReportDeadlyException(exception_record->ExceptionCode))
return EXCEPTION_CONTINUE_SEARCH;
// FIXME: Handle EXCEPTION_STACK_OVERFLOW here.
SignalContext sig = SignalContext::Create(exception_record, context);
ReportDeadlySignal(exception_record->ExceptionCode, sig);
UNREACHABLE("returned from reporting deadly signal");
}
static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
__asan_unhandled_exception_filter(info);
// Bubble out to the default exception filter.
return default_seh_handler(info);
}
@ -331,10 +352,25 @@ int __asan_set_seh_filter() {
// immediately after the CRT runs. This way, our exception filter is called
// first and we can delegate to their filter if appropriate.
#pragma section(".CRT$XCAB", long, read) // NOLINT
__declspec(allocate(".CRT$XCAB"))
int (*__intercept_seh)() = __asan_set_seh_filter;
__declspec(allocate(".CRT$XCAB")) int (*__intercept_seh)() =
__asan_set_seh_filter;
// Piggyback on the TLS initialization callback directory to initialize asan as
// early as possible. Initializers in .CRT$XL* are called directly by ntdll,
// which run before the CRT. Users also add code to .CRT$XLC, so it's important
// to run our initializers first.
static void NTAPI asan_thread_init(void *module, DWORD reason, void *reserved) {
if (reason == DLL_PROCESS_ATTACH) __asan_init();
}
#pragma section(".CRT$XLAB", long, read) // NOLINT
__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *,
unsigned long, void *) = asan_thread_init;
#endif
ASAN_LINK_GLOBALS_WIN()
// }}}
} // namespace __asan
#endif // _WIN32
#endif // SANITIZER_WINDOWS

View file

@ -15,21 +15,30 @@
// See https://github.com/google/sanitizers/issues/209 for the details.
//===----------------------------------------------------------------------===//
// Only compile this code when buidling asan_dll_thunk.lib
// Only compile this code when building asan_dll_thunk.lib
// Using #ifdef rather than relying on Makefiles etc.
// simplifies the build procedure.
#ifdef ASAN_DLL_THUNK
#include "asan_init_version.h"
#include "asan_globals_win.h"
#include "interception/interception.h"
#include "sanitizer_common/sanitizer_platform_interceptors.h"
#ifdef _M_IX86
#define WINAPI __stdcall
#else
#define WINAPI
#endif
// ---------- Function interception helper functions and macros ----------- {{{1
extern "C" {
void *__stdcall GetModuleHandleA(const char *module_name);
void *__stdcall GetProcAddress(void *module, const char *proc_name);
void *WINAPI GetModuleHandleA(const char *module_name);
void *WINAPI GetProcAddress(void *module, const char *proc_name);
void abort();
}
using namespace __sanitizer;
static uptr getRealProcAddressOrDie(const char *name) {
uptr ret =
__interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name);
@ -105,7 +114,7 @@ static void InterceptHooks();
// ---------- Function wrapping helpers ----------------------------------- {{{1
#define WRAP_V_V(name) \
extern "C" void name() { \
typedef void (*fntype)(); \
typedef decltype(name) *fntype; \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
fn(); \
} \
@ -113,7 +122,7 @@ static void InterceptHooks();
#define WRAP_V_W(name) \
extern "C" void name(void *arg) { \
typedef void (*fntype)(void *arg); \
typedef decltype(name) *fntype; \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
fn(arg); \
} \
@ -121,7 +130,7 @@ static void InterceptHooks();
#define WRAP_V_WW(name) \
extern "C" void name(void *arg1, void *arg2) { \
typedef void (*fntype)(void *, void *); \
typedef decltype(name) *fntype; \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
fn(arg1, arg2); \
} \
@ -129,7 +138,7 @@ static void InterceptHooks();
#define WRAP_V_WWW(name) \
extern "C" void name(void *arg1, void *arg2, void *arg3) { \
typedef void *(*fntype)(void *, void *, void *); \
typedef decltype(name) *fntype; \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
fn(arg1, arg2, arg3); \
} \
@ -137,7 +146,7 @@ static void InterceptHooks();
#define WRAP_W_V(name) \
extern "C" void *name() { \
typedef void *(*fntype)(); \
typedef decltype(name) *fntype; \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(); \
} \
@ -145,7 +154,7 @@ static void InterceptHooks();
#define WRAP_W_W(name) \
extern "C" void *name(void *arg) { \
typedef void *(*fntype)(void *arg); \
typedef decltype(name) *fntype; \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(arg); \
} \
@ -153,7 +162,7 @@ static void InterceptHooks();
#define WRAP_W_WW(name) \
extern "C" void *name(void *arg1, void *arg2) { \
typedef void *(*fntype)(void *, void *); \
typedef decltype(name) *fntype; \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(arg1, arg2); \
} \
@ -161,7 +170,7 @@ static void InterceptHooks();
#define WRAP_W_WWW(name) \
extern "C" void *name(void *arg1, void *arg2, void *arg3) { \
typedef void *(*fntype)(void *, void *, void *); \
typedef decltype(name) *fntype; \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(arg1, arg2, arg3); \
} \
@ -169,7 +178,7 @@ static void InterceptHooks();
#define WRAP_W_WWWW(name) \
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \
typedef void *(*fntype)(void *, void *, void *, void *); \
typedef decltype(name) *fntype; \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(arg1, arg2, arg3, arg4); \
} \
@ -178,7 +187,7 @@ static void InterceptHooks();
#define WRAP_W_WWWWW(name) \
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
void *arg5) { \
typedef void *(*fntype)(void *, void *, void *, void *, void *); \
typedef decltype(name) *fntype; \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(arg1, arg2, arg3, arg4, arg5); \
} \
@ -187,7 +196,7 @@ static void InterceptHooks();
#define WRAP_W_WWWWWW(name) \
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
void *arg5, void *arg6) { \
typedef void *(*fntype)(void *, void *, void *, void *, void *, void *); \
typedef decltype(name) *fntype; \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(arg1, arg2, arg3, arg4, arg5, arg6); \
} \
@ -198,9 +207,11 @@ static void InterceptHooks();
// Don't use the INTERFACE_FUNCTION machinery for this function as we actually
// want to call it in the __asan_init interceptor.
WRAP_W_V(__asan_should_detect_stack_use_after_return)
WRAP_W_V(__asan_get_shadow_memory_dynamic_address)
extern "C" {
int __asan_option_detect_stack_use_after_return;
uptr __asan_shadow_memory_dynamic_address;
// Manually wrap __asan_init as we need to initialize
// __asan_option_detect_stack_use_after_return afterwards.
@ -214,7 +225,8 @@ extern "C" {
fn();
__asan_option_detect_stack_use_after_return =
(__asan_should_detect_stack_use_after_return() != 0);
__asan_shadow_memory_dynamic_address =
(uptr)__asan_get_shadow_memory_dynamic_address();
InterceptHooks();
}
}
@ -224,6 +236,7 @@ extern "C" void __asan_version_mismatch_check() {
}
INTERFACE_FUNCTION(__asan_handle_no_return)
INTERFACE_FUNCTION(__asan_unhandled_exception_filter)
INTERFACE_FUNCTION(__asan_report_store1)
INTERFACE_FUNCTION(__asan_report_store2)
@ -257,6 +270,13 @@ INTERFACE_FUNCTION(__asan_memcpy);
INTERFACE_FUNCTION(__asan_memset);
INTERFACE_FUNCTION(__asan_memmove);
INTERFACE_FUNCTION(__asan_set_shadow_00);
INTERFACE_FUNCTION(__asan_set_shadow_f1);
INTERFACE_FUNCTION(__asan_set_shadow_f2);
INTERFACE_FUNCTION(__asan_set_shadow_f3);
INTERFACE_FUNCTION(__asan_set_shadow_f5);
INTERFACE_FUNCTION(__asan_set_shadow_f8);
INTERFACE_FUNCTION(__asan_alloca_poison);
INTERFACE_FUNCTION(__asan_allocas_unpoison);
@ -306,17 +326,18 @@ INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
INTERFACE_FUNCTION(__sanitizer_cov)
INTERFACE_FUNCTION(__sanitizer_cov_dump)
INTERFACE_FUNCTION(__sanitizer_dump_coverage)
INTERFACE_FUNCTION(__sanitizer_dump_trace_pc_guard_coverage)
INTERFACE_FUNCTION(__sanitizer_cov_indir_call16)
INTERFACE_FUNCTION(__sanitizer_cov_init)
INTERFACE_FUNCTION(__sanitizer_cov_module_init)
INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block)
INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter)
INTERFACE_FUNCTION(__sanitizer_cov_trace_cmp)
INTERFACE_FUNCTION(__sanitizer_cov_trace_switch)
INTERFACE_FUNCTION(__sanitizer_cov_trace_pc_guard)
INTERFACE_FUNCTION(__sanitizer_cov_trace_pc_guard_init)
INTERFACE_FUNCTION(__sanitizer_cov_with_check)
INTERFACE_FUNCTION(__sanitizer_get_allocated_size)
INTERFACE_FUNCTION(__sanitizer_get_coverage_guards)
INTERFACE_FUNCTION(__sanitizer_get_coverage_pc_buffer)
INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes)
INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size)
INTERFACE_FUNCTION(__sanitizer_get_free_bytes)
@ -327,6 +348,8 @@ INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage)
INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes)
INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file)
INTERFACE_FUNCTION(__sanitizer_print_stack_trace)
INTERFACE_FUNCTION(__sanitizer_symbolize_pc)
INTERFACE_FUNCTION(__sanitizer_symbolize_global)
INTERFACE_FUNCTION(__sanitizer_ptr_cmp)
INTERFACE_FUNCTION(__sanitizer_ptr_sub)
INTERFACE_FUNCTION(__sanitizer_report_error_summary)
@ -347,6 +370,7 @@ INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container)
INTERFACE_FUNCTION(__sanitizer_install_malloc_and_free_hooks)
INTERFACE_FUNCTION(__sanitizer_start_switch_fiber)
INTERFACE_FUNCTION(__sanitizer_finish_switch_fiber)
INTERFACE_FUNCTION(__sanitizer_get_module_and_offset_for_pc)
// TODO(timurrrr): Add more interface functions on the as-needed basis.
@ -368,6 +392,7 @@ WRAP_W_WW(realloc)
WRAP_W_WW(_realloc_base)
WRAP_W_WWW(_realloc_dbg)
WRAP_W_WWW(_recalloc)
WRAP_W_WWW(_recalloc_base)
WRAP_W_W(_msize)
WRAP_W_W(_expand)
@ -444,4 +469,15 @@ static int call_asan_init() {
#pragma section(".CRT$XIB", long, read) // NOLINT
__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = call_asan_init;
static void WINAPI asan_thread_init(void *mod, unsigned long reason,
void *reserved) {
if (reason == /*DLL_PROCESS_ATTACH=*/1) __asan_init();
}
#pragma section(".CRT$XLAB", long, read) // NOLINT
__declspec(allocate(".CRT$XLAB")) void (WINAPI *__asan_tls_init)(void *,
unsigned long, void *) = asan_thread_init;
ASAN_LINK_GLOBALS_WIN()
#endif // ASAN_DLL_THUNK

View file

@ -1,4 +1,4 @@
//===-- asan_win_uar_thunk.cc ---------------------------------------------===//
//===-- asan_win_dynamic_runtime_thunk.cc ---------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@ -16,22 +16,25 @@
// This includes:
// - forwarding the detect_stack_use_after_return runtime option
// - working around deficiencies of the MD runtime
// - installing a custom SEH handlerx
// - installing a custom SEH handler
//
//===----------------------------------------------------------------------===//
// Only compile this code when buidling asan_dynamic_runtime_thunk.lib
// Only compile this code when building asan_dynamic_runtime_thunk.lib
// Using #ifdef rather than relying on Makefiles etc.
// simplifies the build procedure.
#ifdef ASAN_DYNAMIC_RUNTIME_THUNK
#include "asan_globals_win.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// First, declare CRT sections we'll be using in this file
#pragma section(".CRT$XIB", long, read) // NOLINT
#pragma section(".CRT$XID", long, read) // NOLINT
#pragma section(".CRT$XCAB", long, read) // NOLINT
#pragma section(".CRT$XTW", long, read) // NOLINT
#pragma section(".CRT$XTY", long, read) // NOLINT
#pragma section(".CRT$XLAB", long, read) // NOLINT
////////////////////////////////////////////////////////////////////////////////
// Define a copy of __asan_option_detect_stack_use_after_return that should be
@ -42,14 +45,37 @@
// attribute adds __imp_ prefix to the symbol name of a variable.
// Since in general we don't know if a given TU is going to be used
// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows
// just to work around this issue, let's clone the a variable that is
// constant after initialization anyways.
// just to work around this issue, let's clone the variable that is constant
// after initialization anyways.
extern "C" {
__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
int __asan_option_detect_stack_use_after_return =
__asan_should_detect_stack_use_after_return();
int __asan_option_detect_stack_use_after_return;
__declspec(dllimport) void* __asan_get_shadow_memory_dynamic_address();
void* __asan_shadow_memory_dynamic_address;
}
static int InitializeClonedVariables() {
__asan_option_detect_stack_use_after_return =
__asan_should_detect_stack_use_after_return();
__asan_shadow_memory_dynamic_address =
__asan_get_shadow_memory_dynamic_address();
return 0;
}
static void NTAPI asan_thread_init(void *mod, unsigned long reason,
void *reserved) {
if (reason == DLL_PROCESS_ATTACH) InitializeClonedVariables();
}
// Our cloned variables must be initialized before C/C++ constructors. If TLS
// is used, our .CRT$XLAB initializer will run first. If not, our .CRT$XIB
// initializer is needed as a backup.
__declspec(allocate(".CRT$XIB")) int (*__asan_initialize_cloned_variables)() =
InitializeClonedVariables;
__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *,
unsigned long, void *) = asan_thread_init;
////////////////////////////////////////////////////////////////////////////////
// For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL
// unload or on exit. ASan relies on LLVM global_dtors to call
@ -73,6 +99,7 @@ void UnregisterGlobals() {
int ScheduleUnregisterGlobals() {
return atexit(UnregisterGlobals);
}
} // namespace
// We need to call 'atexit(UnregisterGlobals);' as early as possible, but after
// atexit() is initialized (.CRT$XIC). As this is executed before C++
@ -81,8 +108,6 @@ int ScheduleUnregisterGlobals() {
__declspec(allocate(".CRT$XID"))
int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
} // namespace
////////////////////////////////////////////////////////////////////////////////
// ASan SEH handling.
// We need to set the ASan-specific SEH handler at the end of CRT initialization
@ -97,4 +122,6 @@ __declspec(allocate(".CRT$XCAB")) int (*__asan_seh_interceptor)() =
SetSEHFilter;
}
ASAN_LINK_GLOBALS_WIN()
#endif // ASAN_DYNAMIC_RUNTIME_THUNK

View file

@ -300,20 +300,22 @@ if [[ -n "$ASAN_RT64" ]]; then
cp "$ASAN_RT_PATH/$ASAN_RT64" "$TMPDIR/"
fi
# FIXME: alloc_dealloc_mismatch=0 prevents a failure in libdvm startup,
# which may or may not be a real bug (probably not).
ASAN_OPTIONS=start_deactivated=1,alloc_dealloc_mismatch=0,malloc_context_size=0
ASAN_OPTIONS=start_deactivated=1,malloc_context_size=0
function generate_zygote_wrapper { # from, to, asan_rt
# The name of a symlink to libclang_rt.asan-$ARCH-android.so used in LD_PRELOAD.
# The idea is to have the same name in lib and lib64 to keep it from falling
# apart when a 64-bit process spawns a 32-bit one, inheriting the environment.
ASAN_RT_SYMLINK=symlink-to-libclang_rt.asan
function generate_zygote_wrapper { # from, to
local _from=$1
local _to=$2
local _asan_rt=$3
if [[ PRE_L -eq 0 ]]; then
# LD_PRELOAD parsing is broken in N if it starts with ":". Luckily, it is
# unset in the system environment since L.
local _ld_preload=$_asan_rt
local _ld_preload=$ASAN_RT_SYMLINK
else
local _ld_preload=\$LD_PRELOAD:$_asan_rt
local _ld_preload=\$LD_PRELOAD:$ASAN_RT_SYMLINK
fi
cat <<EOF >"$TMPDIR/$_from"
#!/system/bin/sh-from-zygote
@ -342,18 +344,18 @@ if [[ -f "$TMPDIR/app_process64" ]]; then
mv "$TMPDIR/app_process32" "$TMPDIR/app_process32.real"
mv "$TMPDIR/app_process64" "$TMPDIR/app_process64.real"
fi
generate_zygote_wrapper "app_process32" "/system/bin/app_process32.real" "$ASAN_RT"
generate_zygote_wrapper "app_process64" "/system/bin/app_process64.real" "$ASAN_RT64"
generate_zygote_wrapper "app_process32" "/system/bin/app_process32.real"
generate_zygote_wrapper "app_process64" "/system/bin/app_process64.real"
else
# A 32-bit device.
generate_zygote_wrapper "app_process.wrap" "/system/bin/app_process32" "$ASAN_RT"
generate_zygote_wrapper "app_process.wrap" "/system/bin/app_process32"
fi
# General command-line tool wrapper (use for anything that's not started as
# zygote).
cat <<EOF >"$TMPDIR/asanwrapper"
#!/system/bin/sh
LD_PRELOAD=$ASAN_RT \\
LD_PRELOAD=$ASAN_RT_SYMLINK \\
exec \$@
EOF
@ -361,7 +363,7 @@ EOF
if [[ -n "$ASAN_RT64" ]]; then
cat <<EOF >"$TMPDIR/asanwrapper64"
#!/system/bin/sh
LD_PRELOAD=$ASAN_RT64 \\
LD_PRELOAD=$ASAN_RT_SYMLINK \\
exec \$@
EOF
@ -412,12 +414,17 @@ if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
install "$TMPDIR/app_process64.real" /system/bin 755 $CTX
install "$TMPDIR/asanwrapper" /system/bin 755
install "$TMPDIR/asanwrapper64" /system/bin 755
adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK
adb_shell ln -s $ASAN_RT64 /system/lib64/$ASAN_RT_SYMLINK
else
install "$TMPDIR/$ASAN_RT" /system/lib 644
install "$TMPDIR/app_process32" /system/bin 755 $CTX
install "$TMPDIR/app_process.wrap" /system/bin 755 $CTX
install "$TMPDIR/asanwrapper" /system/bin 755 $CTX
adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK
adb_shell rm /system/bin/app_process
adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process
fi

View file

@ -106,12 +106,13 @@ set(ASAN_UNITTEST_INSTRUMENTED_LIBS)
append_list_if(ANDROID atomic ASAN_UNITTEST_INSTRUMENTED_LIBS)
set(ASAN_UNITTEST_NOINST_LINKFLAGS ${ASAN_UNITTEST_COMMON_LINKFLAGS})
append_list_if(COMPILER_RT_HAS_LIBM -lm ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBDL -ldl ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBRT -lrt ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread
ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS)
if(NOT APPLE)
append_list_if(COMPILER_RT_HAS_LIBM -lm ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBDL -ldl ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBRT -lrt ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS)
endif()
# TODO(eugenis): move all -l flags above to _LIBS?
set(ASAN_UNITTEST_NOINST_LIBS)
@ -193,6 +194,7 @@ set(ASAN_INST_TEST_SOURCES
asan_asm_test.cc
asan_globals_test.cc
asan_interface_test.cc
asan_internal_interface_test.cc
asan_test.cc
asan_oob_test.cc
asan_mem_test.cc
@ -220,6 +222,23 @@ macro(add_asan_tests_for_arch_and_kind arch kind)
${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -ObjC ${ARGN})
endif()
if (MSVC)
# With the MSVC CRT, the choice between static and dynamic CRT is made at
# compile time with a macro. Simulate the effect of passing /MD to clang-cl.
set(ASAN_INST_DYNAMIC_TEST_OBJECTS)
foreach(src ${ASAN_INST_TEST_SOURCES})
asan_compile(ASAN_INST_DYNAMIC_TEST_OBJECTS ${src} ${arch} ${kind}
${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -D_MT -D_DLL ${ARGN})
endforeach()
# Clang links the static CRT by default. Override that to use the dynamic
# CRT.
set(ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS
${ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS}
-Wl,-nodefaultlib:libcmt,-defaultlib:msvcrt,-defaultlib:oldnames)
else()
set(ASAN_INST_DYNAMIC_TEST_OBJECTS ${ASAN_INST_TEST_OBJECTS})
endif()
# Create the 'default' folder where ASAN tests are produced.
if(CMAKE_CONFIGURATION_TYPES)
foreach(build_mode ${CMAKE_CONFIGURATION_TYPES})
@ -245,7 +264,7 @@ macro(add_asan_tests_for_arch_and_kind arch kind)
add_asan_test(AsanDynamicUnitTests "Asan-${arch}${kind}-Dynamic-Test"
${arch} ${kind} SUBDIR "dynamic"
OBJECTS ${ASAN_INST_TEST_OBJECTS}
OBJECTS ${ASAN_INST_DYNAMIC_TEST_OBJECTS}
LINKFLAGS ${ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS})
endif()

View file

@ -57,12 +57,13 @@ template<> Type asm_read<Type>(Type *ptr) { \
return res; \
}
#define DECLARE_ASM_REP_MOVS(Type, Movs) \
template <> void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \
__asm__("rep " Movs " \n\t" \
: \
: "D"(dst), "S"(src), "c"(size) \
: "rsi", "rdi", "rcx", "memory"); \
#define DECLARE_ASM_REP_MOVS(Type, Movs) \
template <> \
void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \
__asm__("rep " Movs " \n\t" \
: "+D"(dst), "+S"(src), "+c"(size) \
: \
: "memory"); \
}
DECLARE_ASM_WRITE(U8, "8", "movq", "r");
@ -99,12 +100,13 @@ template<> Type asm_read<Type>(Type *ptr) { \
return res; \
}
#define DECLARE_ASM_REP_MOVS(Type, Movs) \
template <> void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \
__asm__("rep " Movs " \n\t" \
: \
: "D"(dst), "S"(src), "c"(size) \
: "esi", "edi", "ecx", "memory"); \
#define DECLARE_ASM_REP_MOVS(Type, Movs) \
template <> \
void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \
__asm__("rep " Movs " \n\t" \
: "+D"(dst), "+S"(src), "+c"(size) \
: \
: "memory"); \
}
} // End of anonymous namespace

View file

@ -100,6 +100,9 @@ TEST(AddressSanitizerInterface, GetHeapSizeTest) {
}
}
#ifndef __powerpc64__
// FIXME: This has not reliably worked on powerpc since r279664. Re-enable
// this once the problem is tracked down and fixed.
static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357};
static const size_t kManyThreadsIterations = 250;
static const size_t kManyThreadsNumThreads =
@ -133,6 +136,7 @@ TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) {
// so we can't check for equality here.
EXPECT_LT(after_test, before_test + (1UL<<20));
}
#endif
static void DoDoubleFree() {
int *x = Ident(new int);

View file

@ -0,0 +1,36 @@
//===-- asan_internal_interface_test.cc -----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
//===----------------------------------------------------------------------===//
#include "asan_interface_internal.h"
#include "asan_test_utils.h"
TEST(AddressSanitizerInternalInterface, SetShadow) {
std::vector<char> buffer(17, 0xff);
__asan_set_shadow_00((uptr)buffer.data(), buffer.size());
EXPECT_EQ(std::vector<char>(buffer.size(), 0x00), buffer);
__asan_set_shadow_f1((uptr)buffer.data(), buffer.size());
EXPECT_EQ(std::vector<char>(buffer.size(), 0xf1), buffer);
__asan_set_shadow_f2((uptr)buffer.data(), buffer.size());
EXPECT_EQ(std::vector<char>(buffer.size(), 0xf2), buffer);
__asan_set_shadow_f3((uptr)buffer.data(), buffer.size());
EXPECT_EQ(std::vector<char>(buffer.size(), 0xf3), buffer);
__asan_set_shadow_f5((uptr)buffer.data(), buffer.size());
EXPECT_EQ(std::vector<char>(buffer.size(), 0xf5), buffer);
__asan_set_shadow_f8((uptr)buffer.data(), buffer.size());
EXPECT_EQ(std::vector<char>(buffer.size(), 0xf8), buffer);
}

View file

@ -26,6 +26,8 @@
#include <vector>
#include <limits>
using namespace __sanitizer;
// ATTENTION!
// Please don't call intercepted functions (including malloc() and friends)
// in this test. The static runtime library is linked explicitly (without
@ -168,6 +170,12 @@ void *ThreadedQuarantineTestWorker(void *unused) {
// Check that the thread local allocators are flushed when threads are
// destroyed.
TEST(AddressSanitizer, ThreadedQuarantineTest) {
// Run the routine once to warm up ASAN internal structures to get more
// predictable incremental memory changes.
pthread_t t;
PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0);
PTHREAD_JOIN(t, 0);
const int n_threads = 3000;
size_t mmaped1 = __sanitizer_get_heap_size();
for (int i = 0; i < n_threads; i++) {
@ -175,6 +183,7 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) {
PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0);
PTHREAD_JOIN(t, 0);
size_t mmaped2 = __sanitizer_get_heap_size();
// Figure out why this much memory is required.
EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20));
}
}

View file

@ -127,7 +127,15 @@ TEST(AddressSanitizer, StrNLenOOBTest) {
}
#endif // SANITIZER_TEST_HAS_STRNLEN
TEST(AddressSanitizer, StrDupOOBTest) {
// This test fails with the WinASan dynamic runtime because we fail to intercept
// strdup.
#if defined(_MSC_VER) && defined(_DLL)
#define MAYBE_StrDupOOBTest DISABLED_StrDupOOBTest
#else
#define MAYBE_StrDupOOBTest StrDupOOBTest
#endif
TEST(AddressSanitizer, MAYBE_StrDupOOBTest) {
size_t size = Ident(42);
char *str = MallocAndMemsetString(size);
char *new_str;
@ -457,11 +465,13 @@ TEST(AddressSanitizer, StrArgsOverlapTest) {
#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.
#if PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE
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"));
#endif
#endif
// We do not treat memcpy with to==from as a bug.

View file

@ -692,7 +692,7 @@ TEST(AddressSanitizer, ThreadStackReuseTest) {
PTHREAD_JOIN(t, 0);
}
#if defined(__i686__) || defined(__x86_64__)
#if defined(__SSE2__)
#include <emmintrin.h>
TEST(AddressSanitizer, Store128Test) {
char *a = Ident((char*)malloc(Ident(12)));

View file

@ -28,9 +28,18 @@ extern "C" const char* __asan_default_options() {
namespace __sanitizer {
bool ReexecDisabled() {
#if __has_feature(address_sanitizer) && SANITIZER_MAC
// Allow re-exec in instrumented unit tests on Darwin. Technically, we only
// need this for 10.10 and below, where re-exec is required for the
// interceptors to work, but to avoid duplicating the version detection logic,
// let's just allow re-exec for all Darwin versions. On newer OS versions,
// returning 'false' doesn't do anything anyway, because we don't re-exec.
return false;
#else
return true;
#endif
}
}
} // namespace __sanitizer
int main(int argc, char **argv) {
testing::GTEST_FLAG(death_test_style) = "threadsafe";

View file

@ -62,7 +62,9 @@ typedef uint64_t U8;
static const int kPageSize = 4096;
const size_t kLargeMalloc = 1 << 24;
// Big enough to be handled by secondary allocator and small enough to fit into
// quarantine for all configurations.
const size_t kLargeMalloc = 1 << 22;
extern void free_aaa(void *p);
extern void *malloc_aaa(size_t size);

View file

@ -13,6 +13,10 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
"${CMAKE_SOURCE_DIR}/../../cmake/Modules")
include(base-config-ix)
include(CompilerRTUtils)
load_llvm_config()
construct_compiler_rt_default_triple()
if(APPLE)
include(CompilerRTDarwinUtils)
endif()
@ -160,7 +164,11 @@ set(GENERIC_SOURCES
umodsi3.c
umodti3.c)
if(COMPILER_RT_SUPPORTS_ATOMIC_KEYWORD)
option(COMPILER_RT_EXCLUDE_ATOMIC_BUILTIN
"Skip the atomic builtin (this may be needed if system headers are unavailable)"
Off)
if(COMPILER_RT_HAS_ATOMIC_KEYWORD AND NOT COMPILER_RT_EXCLUDE_ATOMIC_BUILTIN)
set(GENERIC_SOURCES
${GENERIC_SOURCES}
atomic.c)
@ -261,68 +269,14 @@ else () # MSVC
endif () # if (NOT MSVC)
set(arm_SOURCES
arm/adddf3vfp.S
arm/addsf3vfp.S
arm/aeabi_cdcmp.S
arm/aeabi_cdcmpeq_check_nan.c
arm/aeabi_cfcmp.S
arm/aeabi_cfcmpeq_check_nan.c
arm/aeabi_dcmp.S
arm/aeabi_div0.c
arm/aeabi_drsub.c
arm/aeabi_fcmp.S
arm/aeabi_frsub.c
arm/aeabi_idivmod.S
arm/aeabi_ldivmod.S
arm/aeabi_memcmp.S
arm/aeabi_memcpy.S
arm/aeabi_memmove.S
arm/aeabi_memset.S
arm/aeabi_uidivmod.S
arm/aeabi_uldivmod.S
arm/bswapdi2.S
arm/bswapsi2.S
arm/clzdi2.S
arm/clzsi2.S
arm/comparesf2.S
arm/divdf3vfp.S
arm/divmodsi4.S
arm/divsf3vfp.S
arm/divsi3.S
arm/eqdf2vfp.S
arm/eqsf2vfp.S
arm/extendsfdf2vfp.S
arm/fixdfsivfp.S
arm/fixsfsivfp.S
arm/fixunsdfsivfp.S
arm/fixunssfsivfp.S
arm/floatsidfvfp.S
arm/floatsisfvfp.S
arm/floatunssidfvfp.S
arm/floatunssisfvfp.S
arm/gedf2vfp.S
arm/gesf2vfp.S
arm/gtdf2vfp.S
arm/gtsf2vfp.S
arm/ledf2vfp.S
arm/lesf2vfp.S
arm/ltdf2vfp.S
arm/ltsf2vfp.S
arm/modsi3.S
arm/muldf3vfp.S
arm/mulsf3vfp.S
arm/nedf2vfp.S
arm/negdf2vfp.S
arm/negsf2vfp.S
arm/nesf2vfp.S
arm/restore_vfp_d8_d15_regs.S
arm/save_vfp_d8_d15_regs.S
arm/subdf3vfp.S
arm/subsf3vfp.S
arm/switch16.S
arm/switch32.S
arm/switch8.S
arm/switchu8.S
arm/sync_fetch_and_add_4.S
arm/sync_fetch_and_add_8.S
arm/sync_fetch_and_and_4.S
@ -343,15 +297,112 @@ set(arm_SOURCES
arm/sync_fetch_and_umin_8.S
arm/sync_fetch_and_xor_4.S
arm/sync_fetch_and_xor_8.S
arm/sync_synchronize.S
arm/truncdfsf2vfp.S
arm/udivmodsi4.S
arm/udivsi3.S
arm/umodsi3.S
arm/unorddf2vfp.S
arm/unordsf2vfp.S
${GENERIC_SOURCES})
set(arm_EABI_SOURCES
arm/aeabi_cdcmp.S
arm/aeabi_cdcmpeq_check_nan.c
arm/aeabi_cfcmp.S
arm/aeabi_cfcmpeq_check_nan.c
arm/aeabi_dcmp.S
arm/aeabi_div0.c
arm/aeabi_drsub.c
arm/aeabi_fcmp.S
arm/aeabi_frsub.c
arm/aeabi_idivmod.S
arm/aeabi_ldivmod.S
arm/aeabi_memcmp.S
arm/aeabi_memcpy.S
arm/aeabi_memmove.S
arm/aeabi_memset.S
arm/aeabi_uidivmod.S
arm/aeabi_uldivmod.S)
set(arm_Thumb1_JT_SOURCES
arm/switch16.S
arm/switch32.S
arm/switch8.S
arm/switchu8.S)
set(arm_Thumb1_SjLj_EH_SOURCES
arm/restore_vfp_d8_d15_regs.S
arm/save_vfp_d8_d15_regs.S)
set(arm_Thumb1_VFPv2_SOURCES
arm/adddf3vfp.S
arm/addsf3vfp.S
arm/divdf3vfp.S
arm/divsf3vfp.S
arm/eqdf2vfp.S
arm/eqsf2vfp.S
arm/extendsfdf2vfp.S
arm/fixdfsivfp.S
arm/fixsfsivfp.S
arm/fixunsdfsivfp.S
arm/fixunssfsivfp.S
arm/floatsidfvfp.S
arm/floatsisfvfp.S
arm/floatunssidfvfp.S
arm/floatunssisfvfp.S
arm/gedf2vfp.S
arm/gesf2vfp.S
arm/gtdf2vfp.S
arm/gtsf2vfp.S
arm/ledf2vfp.S
arm/lesf2vfp.S
arm/ltdf2vfp.S
arm/ltsf2vfp.S
arm/muldf3vfp.S
arm/mulsf3vfp.S
arm/nedf2vfp.S
arm/negdf2vfp.S
arm/negsf2vfp.S
arm/nesf2vfp.S
arm/subdf3vfp.S
arm/subsf3vfp.S
arm/truncdfsf2vfp.S
arm/unorddf2vfp.S
arm/unordsf2vfp.S)
set(arm_Thumb1_icache_SOURCES
arm/sync_synchronize.S)
set(arm_Thumb1_SOURCES
${arm_Thumb1_JT_SOURCES}
${arm_Thumb1_SjLj_EH_SOURCES}
${arm_Thumb1_VFPv2_SOURCES}
${arm_Thumb1_icache_SOURCES})
if(MINGW)
set(arm_SOURCES
arm/aeabi_idivmod.S
arm/aeabi_ldivmod.S
arm/aeabi_uidivmod.S
arm/aeabi_uldivmod.S
divmoddi4.c
divmodsi4.c
divdi3.c
divsi3.c
fixdfdi.c
fixsfdi.c
fixunsdfdi.c
fixunssfdi.c
floatdidf.c
floatdisf.c
floatundidf.c
floatundisf.c
mingw_fixfloat.c
moddi3.c
udivmoddi4.c
udivmodsi4.c
udivsi3.c
umoddi3.c)
elseif(NOT WIN32)
# TODO the EABI sources should only be added to EABI targets
set(arm_SOURCES
${arm_SOURCES}
${arm_EABI_SOURCES}
${arm_Thumb1_SOURCES})
endif()
set(aarch64_SOURCES
comparetf2.c
extenddftf2.c
@ -398,7 +449,24 @@ if (APPLE)
add_subdirectory(macho_embedded)
darwin_add_builtin_libraries(${BUILTIN_SUPPORTED_OS})
else ()
append_string_if(COMPILER_RT_HAS_STD_C99_FLAG -std=gnu99 maybe_stdc99)
set(BUILTIN_CFLAGS "")
append_list_if(COMPILER_RT_HAS_STD_C11_FLAG -std=c11 BUILTIN_CFLAGS)
# These flags would normally be added to CMAKE_C_FLAGS by the llvm
# cmake step. Add them manually if this is a standalone build.
if(COMPILER_RT_STANDALONE_BUILD)
append_list_if(COMPILER_RT_HAS_FPIC_FLAG -fPIC BUILTIN_CFLAGS)
append_list_if(COMPILER_RT_HAS_FNO_BUILTIN_FLAG -fno-builtin BUILTIN_CFLAGS)
append_list_if(COMPILER_RT_HAS_VISIBILITY_HIDDEN_FLAG -fvisibility=hidden BUILTIN_CFLAGS)
if(NOT COMPILER_RT_DEBUG)
append_list_if(COMPILER_RT_HAS_OMIT_FRAME_POINTER_FLAG -fomit-frame-pointer BUILTIN_CFLAGS)
endif()
endif()
set(BUILTIN_DEFS "")
append_list_if(COMPILER_RT_HAS_VISIBILITY_HIDDEN_FLAG VISIBILITY_HIDDEN BUILTIN_DEFS)
foreach (arch ${BUILTIN_SUPPORTED_ARCH})
if (CAN_TARGET_${arch})
@ -413,11 +481,18 @@ else ()
endif ()
endforeach ()
# Needed for clear_cache on debug mode, due to r7's usage in inline asm.
# Release mode already sets it via -O2/3, Debug mode doesn't.
if (${arch} STREQUAL "armhf")
list(APPEND BUILTIN_CFLAGS -fomit-frame-pointer)
endif()
add_compiler_rt_runtime(clang_rt.builtins
STATIC
ARCHS ${arch}
SOURCES ${${arch}_SOURCES}
CFLAGS ${maybe_stdc99}
DEFS ${BUILTIN_DEFS}
CFLAGS ${BUILTIN_CFLAGS}
PARENT_TARGET builtins)
endif ()
endforeach ()

View file

@ -1,25 +0,0 @@
#===- lib/builtins/Makefile.mk -----------------------------*- Makefile -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
ModuleName := builtins
SubDirs :=
# Add arch specific optimized implementations.
SubDirs += i386 ppc x86_64 arm armv6m
# Add ARM64 dir.
SubDirs += arm64
# Define the variables for this specific directory.
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
ObjNames := $(Sources:%.c=%.o)
Implementation := Generic
# FIXME: use automatic dependencies?
Dependencies := $(wildcard $(Dir)/*.h)

View file

@ -1,20 +0,0 @@
#===- lib/builtins/arm/Makefile.mk -------------------------*- Makefile -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
ModuleName := builtins
SubDirs :=
OnlyArchs := armv5 armv6 armv7 armv7k armv7m armv7em armv7s
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o)
Implementation := Optimized
# FIXME: use automatic dependencies?
Dependencies := $(wildcard lib/*.h $(Dir)/*.h)

View file

@ -15,16 +15,34 @@
// return {quot, rem};
// }
#if defined(__MINGW32__)
#define __aeabi_idivmod __rt_sdiv
#endif
.syntax unified
.p2align 2
DEFINE_COMPILERRT_FUNCTION(__aeabi_idivmod)
#if __ARM_ARCH_ISA_THUMB == 1
push {r0, r1, lr}
bl SYMBOL_NAME(__divsi3)
pop {r1, r2, r3} // now r0 = quot, r1 = num, r2 = denom
muls r2, r2, r0 // r2 = quot * denom
subs r1, r1, r2
JMP (r3)
#else
push { lr }
sub sp, sp, #4
mov r2, sp
#if defined(__MINGW32__)
mov r3, r0
mov r0, r1
mov r1, r3
#endif
bl SYMBOL_NAME(__divmodsi4)
ldr r1, [sp]
add sp, sp, #4
pop { pc }
#endif // __ARM_ARCH_ISA_THUMB == 1
END_COMPILERRT_FUNCTION(__aeabi_idivmod)
NO_EXEC_STACK_DIRECTIVE

View file

@ -16,6 +16,10 @@
// return {quot, rem};
// }
#if defined(__MINGW32__)
#define __aeabi_ldivmod __rt_sdiv64
#endif
.syntax unified
.p2align 2
DEFINE_COMPILERRT_FUNCTION(__aeabi_ldivmod)
@ -23,6 +27,14 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_ldivmod)
sub sp, sp, #16
add r12, sp, #8
str r12, [sp]
#if defined(__MINGW32__)
mov r12, r0
mov r0, r2
mov r2, r12
mov r12, r1
mov r1, r3
mov r3, r12
#endif
bl SYMBOL_NAME(__divmoddi4)
ldr r2, [sp, #8]
ldr r3, [sp, #12]

View file

@ -16,16 +16,40 @@
// return {quot, rem};
// }
#if defined(__MINGW32__)
#define __aeabi_uidivmod __rt_udiv
#endif
.syntax unified
.p2align 2
DEFINE_COMPILERRT_FUNCTION(__aeabi_uidivmod)
#if __ARM_ARCH_ISA_THUMB == 1
cmp r0, r1
bcc LOCAL_LABEL(case_denom_larger)
push {r0, r1, lr}
bl SYMBOL_NAME(__aeabi_uidiv)
pop {r1, r2, r3}
muls r2, r2, r0 // r2 = quot * denom
subs r1, r1, r2
JMP (r3)
LOCAL_LABEL(case_denom_larger):
movs r1, r0
movs r0, #0
JMP (lr)
#else
push { lr }
sub sp, sp, #4
mov r2, sp
#if defined(__MINGW32__)
mov r3, r0
mov r0, r1
mov r1, r3
#endif
bl SYMBOL_NAME(__udivmodsi4)
ldr r1, [sp]
add sp, sp, #4
pop { pc }
#endif
END_COMPILERRT_FUNCTION(__aeabi_uidivmod)
NO_EXEC_STACK_DIRECTIVE

View file

@ -16,6 +16,10 @@
// return {quot, rem};
// }
#if defined(__MINGW32__)
#define __aeabi_uldivmod __rt_udiv64
#endif
.syntax unified
.p2align 2
DEFINE_COMPILERRT_FUNCTION(__aeabi_uldivmod)
@ -23,6 +27,14 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_uldivmod)
sub sp, sp, #16
add r12, sp, #8
str r12, [sp]
#if defined(__MINGW32__)
mov r12, r0
mov r0, r2
mov r2, r12
mov r12, r1
mov r1, r3
mov r3, r12
#endif
bl SYMBOL_NAME(__udivmoddi4)
ldr r2, [sp, #8]
ldr r3, [sp, #12]

View file

@ -39,6 +39,9 @@
#include "../assembly.h"
.syntax unified
#if __ARM_ARCH_ISA_THUMB == 2
.thumb
#endif
.p2align 2
DEFINE_COMPILERRT_FUNCTION(__eqsf2)

View file

@ -49,17 +49,37 @@ LOCAL_LABEL(divzero):
#else
ESTABLISH_FRAME
// Set aside the sign of the quotient.
# if __ARM_ARCH_ISA_THUMB == 1
movs r4, r0
eors r4, r1
# else
eor r4, r0, r1
# endif
// Take absolute value of a and b via abs(x) = (x^(x >> 31)) - (x >> 31).
# if __ARM_ARCH_ISA_THUMB == 1
asrs r2, r0, #31
asrs r3, r1, #31
eors r0, r2
eors r1, r3
subs r0, r0, r2
subs r1, r1, r3
# else
eor r2, r0, r0, asr #31
eor r3, r1, r1, asr #31
sub r0, r2, r0, asr #31
sub r1, r3, r1, asr #31
# endif
// abs(a) / abs(b)
bl SYMBOL_NAME(__udivsi3)
// Apply sign of quotient to result and return.
# if __ARM_ARCH_ISA_THUMB == 1
asrs r4, #31
eors r0, r4
subs r0, r0, r4
# else
eor r0, r0, r4, asr #31
sub r0, r0, r4, asr #31
# endif
CLEAR_FRAME_AND_RETURN
#endif
END_COMPILERRT_FUNCTION(__divsi3)

View file

@ -40,12 +40,26 @@ DEFINE_COMPILERRT_FUNCTION(__udivsi3)
#else
cmp r1, #1
bcc LOCAL_LABEL(divby0)
#if __ARM_ARCH_ISA_THUMB == 1
bne LOCAL_LABEL(num_neq_denom)
JMP(lr)
LOCAL_LABEL(num_neq_denom):
#else
IT(eq)
JMPc(lr, eq)
#endif
cmp r0, r1
#if __ARM_ARCH_ISA_THUMB == 1
bhs LOCAL_LABEL(num_ge_denom)
movs r0, #0
JMP(lr)
LOCAL_LABEL(num_ge_denom):
#else
ITT(cc)
movcc r0, #0
JMPc(lr, cc)
#endif
/*
* Implement division using binary long division algorithm.
*
@ -62,7 +76,7 @@ DEFINE_COMPILERRT_FUNCTION(__udivsi3)
* that (r0 << shift) < 2 * r1. The quotient is stored in r3.
*/
# ifdef __ARM_FEATURE_CLZ
# if defined(__ARM_FEATURE_CLZ)
clz ip, r0
clz r3, r1
/* r0 >= r1 implies clz(r0) <= clz(r1), so ip <= r3. */
@ -77,49 +91,128 @@ DEFINE_COMPILERRT_FUNCTION(__udivsi3)
sub ip, ip, r3, lsl #3
mov r3, #0
bx ip
# else
# else /* No CLZ Feature */
# if __ARM_ARCH_ISA_THUMB == 2
# error THUMB mode requires CLZ or UDIV
# endif
# if __ARM_ARCH_ISA_THUMB == 1
# define BLOCK_SIZE 10
# else
# define BLOCK_SIZE 12
# endif
mov r2, r0
# if __ARM_ARCH_ISA_THUMB == 1
mov ip, r0
adr r0, LOCAL_LABEL(div0block)
adds r0, #1
# else
adr ip, LOCAL_LABEL(div0block)
lsr r3, r2, #16
# endif
lsrs r3, r2, #16
cmp r3, r1
# if __ARM_ARCH_ISA_THUMB == 1
blo LOCAL_LABEL(skip_16)
movs r2, r3
subs r0, r0, #(16 * BLOCK_SIZE)
LOCAL_LABEL(skip_16):
# else
movhs r2, r3
subhs ip, ip, #(16 * 12)
subhs ip, ip, #(16 * BLOCK_SIZE)
# endif
lsr r3, r2, #8
lsrs r3, r2, #8
cmp r3, r1
# if __ARM_ARCH_ISA_THUMB == 1
blo LOCAL_LABEL(skip_8)
movs r2, r3
subs r0, r0, #(8 * BLOCK_SIZE)
LOCAL_LABEL(skip_8):
# else
movhs r2, r3
subhs ip, ip, #(8 * 12)
subhs ip, ip, #(8 * BLOCK_SIZE)
# endif
lsr r3, r2, #4
lsrs r3, r2, #4
cmp r3, r1
# if __ARM_ARCH_ISA_THUMB == 1
blo LOCAL_LABEL(skip_4)
movs r2, r3
subs r0, r0, #(4 * BLOCK_SIZE)
LOCAL_LABEL(skip_4):
# else
movhs r2, r3
subhs ip, #(4 * 12)
subhs ip, #(4 * BLOCK_SIZE)
# endif
lsr r3, r2, #2
lsrs r3, r2, #2
cmp r3, r1
# if __ARM_ARCH_ISA_THUMB == 1
blo LOCAL_LABEL(skip_2)
movs r2, r3
subs r0, r0, #(2 * BLOCK_SIZE)
LOCAL_LABEL(skip_2):
# else
movhs r2, r3
subhs ip, ip, #(2 * 12)
subhs ip, ip, #(2 * BLOCK_SIZE)
# endif
/* Last block, no need to update r2 or r3. */
cmp r1, r2, lsr #1
subls ip, ip, #(1 * 12)
# if __ARM_ARCH_ISA_THUMB == 1
lsrs r3, r2, #1
cmp r3, r1
blo LOCAL_LABEL(skip_1)
subs r0, r0, #(1 * BLOCK_SIZE)
LOCAL_LABEL(skip_1):
movs r2, r0
mov r0, ip
movs r3, #0
JMP (r2)
mov r3, #0
# else
cmp r1, r2, lsr #1
subls ip, ip, #(1 * BLOCK_SIZE)
movs r3, #0
JMP(ip)
# endif
# endif
# endif /* __ARM_FEATURE_CLZ */
#define IMM #
/* due to the range limit of branch in Thumb1, we have to place the
block closer */
LOCAL_LABEL(divby0):
movs r0, #0
# if defined(__ARM_EABI__)
bl __aeabi_idiv0 // due to relocation limit, can't use b.
# endif
JMP(lr)
#if __ARM_ARCH_ISA_THUMB == 1
#define block(shift) \
lsls r2, r1, IMM shift; \
cmp r0, r2; \
blo LOCAL_LABEL(block_skip_##shift); \
subs r0, r0, r2; \
LOCAL_LABEL(block_skip_##shift) :; \
adcs r3, r3 /* same as ((r3 << 1) | Carry). Carry is set if r0 >= r2. */
/* TODO: if current location counter is not not word aligned, we don't
need the .p2align and nop */
/* Label div0block must be word-aligned. First align block 31 */
.p2align 2
nop /* Padding to align div0block as 31 blocks = 310 bytes */
#else
#define block(shift) \
cmp r0, r1, lsl IMM shift; \
ITT(hs); \
WIDE(addhs) r3, r3, IMM (1 << shift); \
WIDE(subhs) r0, r0, r1, lsl IMM shift
#endif
block(31)
block(30)
@ -159,12 +252,14 @@ LOCAL_LABEL(div0block):
JMP(lr)
#endif /* __ARM_ARCH_EXT_IDIV__ */
#if __ARM_ARCH_EXT_IDIV__
LOCAL_LABEL(divby0):
mov r0, #0
#ifdef __ARM_EABI__
b __aeabi_idiv0
#else
JMP(lr)
mov r0, #0
# ifdef __ARM_EABI__
b __aeabi_idiv0
# else
JMP(lr)
# endif
#endif
END_COMPILERRT_FUNCTION(__udivsi3)

View file

@ -1,20 +0,0 @@
#===- lib/builtins/arm64/Makefile.mk -----------------------*- Makefile -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
ModuleName := builtins
SubDirs :=
OnlyArchs := arm64
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o)
Implementation := Optimized
# FIXME: use automatic dependencies?
Dependencies := $(wildcard lib/*.h $(Dir)/*.h)

View file

@ -1,20 +0,0 @@
#===- lib/builtins/arm/Makefile.mk -------------------------*- Makefile -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
ModuleName := builtins
SubDirs :=
OnlyArchs := armv6m
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o)
Implementation := Optimized
# FIXME: use automatic dependencies?
Dependencies := $(wildcard lib/*.h $(Dir)/*.h)

View file

@ -70,7 +70,7 @@
#if defined(__ARM_ARCH_4T__) || __ARM_ARCH >= 5
#define ARM_HAS_BX
#endif
#if !defined(__ARM_FEATURE_CLZ) && \
#if !defined(__ARM_FEATURE_CLZ) && __ARM_ARCH_ISA_THUMB != 1 && \
(__ARM_ARCH >= 6 || (__ARM_ARCH == 5 && !defined(__ARM_ARCH_5__)))
#define __ARM_FEATURE_CLZ
#endif
@ -149,6 +149,7 @@
#define DEFINE_COMPILERRT_FUNCTION_ALIAS(name, target) \
.globl SYMBOL_NAME(name) SEPARATOR \
SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \
DECLARE_SYMBOL_VISIBILITY(SYMBOL_NAME(name)) SEPARATOR \
.set SYMBOL_NAME(name), SYMBOL_NAME(target) SEPARATOR
#if defined(__ARM_EABI__)

View file

@ -229,13 +229,20 @@ void __atomic_exchange_c(int size, void *ptr, void *val, void *old, int model) {
// Where the size is known at compile time, the compiler may emit calls to
// specialised versions of the above functions.
////////////////////////////////////////////////////////////////////////////////
#ifdef __SIZEOF_INT128__
#define OPTIMISED_CASES\
OPTIMISED_CASE(1, IS_LOCK_FREE_1, uint8_t)\
OPTIMISED_CASE(2, IS_LOCK_FREE_2, uint16_t)\
OPTIMISED_CASE(4, IS_LOCK_FREE_4, uint32_t)\
OPTIMISED_CASE(8, IS_LOCK_FREE_8, uint64_t)\
/* FIXME: __uint128_t isn't available on 32 bit platforms.
OPTIMISED_CASE(16, IS_LOCK_FREE_16, __uint128_t)*/\
OPTIMISED_CASE(16, IS_LOCK_FREE_16, __uint128_t)
#else
#define OPTIMISED_CASES\
OPTIMISED_CASE(1, IS_LOCK_FREE_1, uint8_t)\
OPTIMISED_CASE(2, IS_LOCK_FREE_2, uint16_t)\
OPTIMISED_CASE(4, IS_LOCK_FREE_4, uint32_t)\
OPTIMISED_CASE(8, IS_LOCK_FREE_8, uint64_t)
#endif
#define OPTIMISED_CASE(n, lockfree, type)\
type __atomic_load_##n(type *src, int model) {\

View file

@ -110,10 +110,12 @@ void __clear_cache(void *start, void *end) {
#elif defined(__linux__)
register int start_reg __asm("r0") = (int) (intptr_t) start;
const register int end_reg __asm("r1") = (int) (intptr_t) end;
const register int flags __asm("r2") = 0;
const register int syscall_nr __asm("r7") = __ARM_NR_cacheflush;
__asm __volatile("svc 0x0"
: "=r"(start_reg)
: "r"(syscall_nr), "r"(start_reg), "r"(end_reg));
: "r"(syscall_nr), "r"(start_reg), "r"(end_reg),
"r"(flags));
if (start_reg != 0) {
compilerrt_abort();
}

View file

@ -1,20 +0,0 @@
#===- lib/builtins/i386/Makefile.mk ------------------------*- Makefile -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
ModuleName := builtins
SubDirs :=
OnlyArchs := i386
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o)
Implementation := Optimized
# FIXME: use automatic dependencies?
Dependencies := $(wildcard lib/*.h $(Dir)/*.h)

View file

@ -91,14 +91,14 @@ COMPILER_RT_ABI tu_int __udivmodti4(tu_int a, tu_int b, tu_int* rem);
#include <intrin.h>
uint32_t __inline __builtin_ctz(uint32_t value) {
uint32_t trailing_zero = 0;
unsigned long trailing_zero = 0;
if (_BitScanForward(&trailing_zero, value))
return trailing_zero;
return 32;
}
uint32_t __inline __builtin_clz(uint32_t value) {
uint32_t leading_zero = 0;
unsigned long leading_zero = 0;
if (_BitScanReverse(&leading_zero, value))
return 31 - leading_zero;
return 32;
@ -106,7 +106,7 @@ uint32_t __inline __builtin_clz(uint32_t value) {
#if defined(_M_ARM) || defined(_M_X64)
uint32_t __inline __builtin_clzll(uint64_t value) {
uint32_t leading_zero = 0;
unsigned long leading_zero = 0;
if (_BitScanReverse64(&leading_zero, value))
return 63 - leading_zero;
return 64;

View file

@ -0,0 +1,36 @@
/* ===-- mingw_fixfloat.c - Wrap int/float conversions for arm/windows -----===
*
* 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 "int_lib.h"
COMPILER_RT_ABI di_int __fixdfdi(double a);
COMPILER_RT_ABI di_int __fixsfdi(float a);
COMPILER_RT_ABI du_int __fixunsdfdi(double a);
COMPILER_RT_ABI du_int __fixunssfdi(float a);
COMPILER_RT_ABI double __floatdidf(di_int a);
COMPILER_RT_ABI float __floatdisf(di_int a);
COMPILER_RT_ABI double __floatundidf(du_int a);
COMPILER_RT_ABI float __floatundisf(du_int a);
COMPILER_RT_ABI di_int __dtoi64(double a) { return __fixdfdi(a); }
COMPILER_RT_ABI di_int __stoi64(float a) { return __fixsfdi(a); }
COMPILER_RT_ABI du_int __dtou64(double a) { return __fixunsdfdi(a); }
COMPILER_RT_ABI du_int __stou64(float a) { return __fixunssfdi(a); }
COMPILER_RT_ABI double __i64tod(di_int a) { return __floatdidf(a); }
COMPILER_RT_ABI float __i64tos(di_int a) { return __floatdisf(a); }
COMPILER_RT_ABI double __u64tod(du_int a) { return __floatundidf(a); }
COMPILER_RT_ABI float __u64tos(du_int a) { return __floatundisf(a); }

View file

@ -1,20 +0,0 @@
#===- lib/builtins/ppc/Makefile.mk -------------------------*- Makefile -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
ModuleName := builtins
SubDirs :=
OnlyArchs := ppc
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o)
Implementation := Optimized
# FIXME: use automatic dependencies?
Dependencies := $(wildcard lib/*.h $(Dir)/*.h)

View file

@ -1,20 +0,0 @@
#===- lib/builtins/x86_64/Makefile.mk ----------------------*- Makefile -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
ModuleName := builtins
SubDirs :=
OnlyArchs := x86_64 x86_64h
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o)
Implementation := Optimized
# FIXME: use automatic dependencies?
Dependencies := $(wildcard lib/*.h $(Dir)/*.h)

View file

@ -1,5 +1,4 @@
add_custom_target(cfi)
set_target_properties(cfi PROPERTIES FOLDER "Compiler-RT Misc")
add_compiler_rt_component(cfi)
set(CFI_SOURCES cfi.cc)
@ -36,4 +35,3 @@ foreach(arch ${CFI_SUPPORTED_ARCH})
endforeach()
add_compiler_rt_resource_file(cfi_blacklist cfi_blacklist.txt cfi)
add_dependencies(compiler-rt cfi)

View file

@ -30,6 +30,8 @@ typedef ElfW(Ehdr) Elf_Ehdr;
#include "ubsan/ubsan_handlers.h"
#endif
using namespace __sanitizer;
namespace __cfi {
#define kCfiShadowLimitsStorageSize 4096 // 1 page

View file

@ -11,8 +11,7 @@ append_rtti_flag(OFF DFSAN_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding DFSAN_COMMON_CFLAGS)
# Static runtime library.
add_custom_target(dfsan)
set_target_properties(dfsan PROPERTIES FOLDER "Compiler-RT Misc")
add_compiler_rt_component(dfsan)
foreach(arch ${DFSAN_SUPPORTED_ARCH})
set(DFSAN_CFLAGS ${DFSAN_COMMON_CFLAGS})
@ -46,5 +45,3 @@ add_custom_command(OUTPUT ${dfsan_abilist_filename}
add_dependencies(dfsan dfsan_abilist)
install(FILES ${dfsan_abilist_filename}
DESTINATION ${COMPILER_RT_INSTALL_PATH})
add_dependencies(compiler-rt dfsan)

View file

@ -114,6 +114,26 @@ SANITIZER_INTERFACE_ATTRIBUTE uptr __dfsan_shadow_ptr_mask;
// | reserved by kernel |
// +--------------------+ 0x0000000000
// On Linux/AArch64 (48-bit VMA), memory is laid out as follow:
//
// +--------------------+ 0x1000000000000 (top of memory)
// | application memory |
// +--------------------+ 0xffff00008000 (kAppAddr)
// | unused |
// +--------------------+ 0xaaaab0000000 (top of PIE address)
// | application PIE |
// +--------------------+ 0xaaaaa0000000 (top of PIE address)
// | |
// | unused |
// | |
// +--------------------+ 0x1200000000 (kUnusedAddr)
// | union table |
// +--------------------+ 0x8000000000 (kUnionTableAddr)
// | shadow memory |
// +--------------------+ 0x0000010000 (kShadowAddr)
// | reserved by kernel |
// +--------------------+ 0x0000000000
typedef atomic_dfsan_label dfsan_union_table_t[kNumLabels][kNumLabels];
#ifdef DFSAN_RUNTIME_VMA
@ -372,11 +392,12 @@ static void InitializePlatformEarly() {
#ifdef DFSAN_RUNTIME_VMA
__dfsan::vmaSize =
(MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
if (__dfsan::vmaSize == 39 || __dfsan::vmaSize == 42) {
if (__dfsan::vmaSize == 39 || __dfsan::vmaSize == 42 ||
__dfsan::vmaSize == 48) {
__dfsan_shadow_ptr_mask = ShadowMask();
} else {
Printf("FATAL: DataFlowSanitizer: unsupported VMA range\n");
Printf("FATAL: Found %d - Supported 39 and 42\n", __dfsan::vmaSize);
Printf("FATAL: Found %d - Supported 39, 42, and 48\n", __dfsan::vmaSize);
Die();
}
#endif

View file

@ -18,6 +18,9 @@
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "dfsan_platform.h"
using __sanitizer::uptr;
using __sanitizer::u16;
// Copy declarations from public sanitizer/dfsan_interface.h header here.
typedef u16 dfsan_label;

View file

@ -16,6 +16,8 @@
#include "interception/interception.h"
#include "sanitizer_common/sanitizer_common.h"
using namespace __sanitizer;
INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags,
int fd, OFF_T offset) {
void *res = REAL(mmap)(addr, length, prot, flags, fd, offset);

View file

@ -46,6 +46,13 @@ struct Mapping42 {
static const uptr kShadowMask = ~0x3c000000000;
};
struct Mapping48 {
static const uptr kShadowAddr = 0x10000;
static const uptr kUnionTableAddr = 0x8000000000;
static const uptr kAppAddr = 0xffff00008000;
static const uptr kShadowMask = ~0xfffff0000000;
};
extern int vmaSize;
# define DFSAN_RUNTIME_VMA 1
#else
@ -72,11 +79,13 @@ uptr MappingImpl(void) {
template<int Type>
uptr MappingArchImpl(void) {
#ifdef __aarch64__
if (vmaSize == 39)
return MappingImpl<Mapping39, Type>();
else
return MappingImpl<Mapping42, Type>();
switch (vmaSize) {
case 39: return MappingImpl<Mapping39, Type>();
case 42: return MappingImpl<Mapping42, Type>();
case 48: return MappingImpl<Mapping48, Type>();
}
DCHECK(0);
return 0;
#else
return MappingImpl<Mapping, Type>();
#endif

View file

@ -266,6 +266,14 @@ fun:reflect.makeFuncStub=discard
# Replaces __sanitizer_cov_trace_cmp with __dfsw___sanitizer_cov_trace_cmp
fun:__sanitizer_cov_trace_cmp=custom
fun:__sanitizer_cov_trace_cmp=uninstrumented
fun:__sanitizer_cov_trace_cmp1=custom
fun:__sanitizer_cov_trace_cmp1=uninstrumented
fun:__sanitizer_cov_trace_cmp2=custom
fun:__sanitizer_cov_trace_cmp2=uninstrumented
fun:__sanitizer_cov_trace_cmp4=custom
fun:__sanitizer_cov_trace_cmp4=uninstrumented
fun:__sanitizer_cov_trace_cmp8=custom
fun:__sanitizer_cov_trace_cmp8=uninstrumented
# Similar for __sanitizer_cov_trace_switch
fun:__sanitizer_cov_trace_switch=custom
fun:__sanitizer_cov_trace_switch=uninstrumented

View file

@ -1,7 +1,6 @@
# Build for the EfficiencySanitizer runtime support library.
add_custom_target(esan)
set_target_properties(esan PROPERTIES FOLDER "Compiler-RT Misc")
add_compiler_rt_component(esan)
set(ESAN_RTL_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_rtti_flag(OFF ESAN_RTL_CFLAGS)
@ -36,8 +35,6 @@ foreach (arch ${ESAN_SUPPORTED_ARCH})
clang_rt.esan-${arch}-symbols)
endforeach()
add_dependencies(compiler-rt esan)
if (COMPILER_RT_INCLUDE_TESTS)
# TODO(bruening): add tests via add_subdirectory(tests)
endif()

View file

@ -94,8 +94,8 @@ static void reportStructCounter(StructHashMap::Handle &Handle) {
type = "struct";
start = &Struct->StructName[7];
}
// Remove the suffixes with '#' during print.
end = strchr(start, '#');
// Remove the suffixes with '$' during print.
end = strchr(start, '$');
CHECK(end != nullptr);
Report(" %s %.*s\n", type, end - start, start);
Report(" size = %u, count = %llu, ratio = %llu, array access = %llu\n",

View file

@ -141,9 +141,17 @@ static bool verifyShadowScheme() {
}
#endif
uptr VmaSize;
static void initializeShadow() {
verifyAddressSpace();
// This is based on the assumption that the intial stack is always allocated
// in the topmost segment of the user address space and the assumption
// holds true on all the platforms currently supported.
VmaSize =
(MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
DCHECK(verifyShadowScheme());
Mapping.initialize(ShadowScale[__esan_which_tool]);

View file

@ -34,6 +34,7 @@ namespace __esan {
extern bool EsanIsInitialized;
extern bool EsanDuringInit;
extern uptr VmaSize;
void initializeLibrary(ToolType Tool);
int finalizeLibrary();

View file

@ -17,6 +17,8 @@
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_flags.h"
using namespace __sanitizer;
namespace __esan {
static const char EsanOptsEnv[] = "ESAN_OPTIONS";

381
lib/esan/esan_hashtable.h Normal file
View file

@ -0,0 +1,381 @@
//===-- esan_hashtable.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 EfficiencySanitizer, a family of performance tuners.
//
// Generic resizing hashtable.
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_mutex.h"
#include <stddef.h>
namespace __esan {
//===----------------------------------------------------------------------===//
// Default hash and comparison functions
//===----------------------------------------------------------------------===//
template <typename T> struct DefaultHash {
size_t operator()(const T &Key) const {
return (size_t)Key;
}
};
template <typename T> struct DefaultEqual {
bool operator()(const T &Key1, const T &Key2) const {
return Key1 == Key2;
}
};
//===----------------------------------------------------------------------===//
// HashTable declaration
//===----------------------------------------------------------------------===//
// A simple resizing and mutex-locked hashtable.
//
// If the default hash functor is used, KeyTy must have an operator size_t().
// If the default comparison functor is used, KeyTy must have an operator ==.
//
// By default all operations are internally-synchronized with a mutex, with no
// synchronization for payloads once hashtable functions return. If
// ExternalLock is set to true, the caller should call the lock() and unlock()
// routines around all hashtable operations and subsequent manipulation of
// payloads.
template <typename KeyTy, typename DataTy, bool ExternalLock = false,
typename HashFuncTy = DefaultHash<KeyTy>,
typename EqualFuncTy = DefaultEqual<KeyTy> >
class HashTable {
public:
// InitialCapacity must be a power of 2.
// ResizeFactor must be between 1 and 99 and indicates the
// maximum percentage full that the table should ever be.
HashTable(u32 InitialCapacity = 2048, u32 ResizeFactor = 70);
~HashTable();
bool lookup(const KeyTy &Key, DataTy &Payload); // Const except for Mutex.
bool add(const KeyTy &Key, const DataTy &Payload);
bool remove(const KeyTy &Key);
u32 size(); // Const except for Mutex.
// If the table is internally-synchronized, this lock must not be held
// while a hashtable function is called as it will deadlock: the lock
// is not recursive. This is meant for use with externally-synchronized
// tables or with an iterator.
void lock();
void unlock();
private:
struct HashEntry {
KeyTy Key;
DataTy Payload;
HashEntry *Next;
};
public:
struct HashPair {
HashPair(KeyTy Key, DataTy Data) : Key(Key), Data(Data) {}
KeyTy Key;
DataTy Data;
};
// This iterator does not perform any synchronization.
// It expects the caller to lock the table across the whole iteration.
// Calling HashTable functions while using the iterator is not supported.
// The iterator returns copies of the keys and data.
class iterator {
public:
iterator(
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table);
iterator(const iterator &Src) = default;
iterator &operator=(const iterator &Src) = default;
HashPair operator*();
iterator &operator++();
iterator &operator++(int);
bool operator==(const iterator &Cmp) const;
bool operator!=(const iterator &Cmp) const;
private:
iterator(
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table,
int Idx);
friend HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>;
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table;
int Idx;
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::HashEntry
*Entry;
};
// No erase or insert iterator supported
iterator begin();
iterator end();
private:
void resize();
HashEntry **Table;
u32 Capacity;
u32 Entries;
const u32 ResizeFactor;
BlockingMutex Mutex;
const HashFuncTy HashFunc;
const EqualFuncTy EqualFunc;
};
//===----------------------------------------------------------------------===//
// Hashtable implementation
//===----------------------------------------------------------------------===//
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::HashTable(
u32 InitialCapacity, u32 ResizeFactor)
: Capacity(InitialCapacity), Entries(0), ResizeFactor(ResizeFactor),
HashFunc(HashFuncTy()), EqualFunc(EqualFuncTy()) {
CHECK(IsPowerOfTwo(Capacity));
CHECK(ResizeFactor >= 1 && ResizeFactor <= 99);
Table = (HashEntry **)InternalAlloc(Capacity * sizeof(HashEntry *));
internal_memset(Table, 0, Capacity * sizeof(HashEntry *));
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::~HashTable() {
for (u32 i = 0; i < Capacity; ++i) {
HashEntry *Entry = Table[i];
while (Entry != nullptr) {
HashEntry *Next = Entry->Next;
Entry->Payload.~DataTy();
InternalFree(Entry);
Entry = Next;
}
}
InternalFree(Table);
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
u32 HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::size() {
u32 Res;
if (!ExternalLock)
Mutex.Lock();
Res = Entries;
if (!ExternalLock)
Mutex.Unlock();
return Res;
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::lookup(
const KeyTy &Key, DataTy &Payload) {
if (!ExternalLock)
Mutex.Lock();
bool Found = false;
size_t Hash = HashFunc(Key) % Capacity;
HashEntry *Entry = Table[Hash];
for (; Entry != nullptr; Entry = Entry->Next) {
if (EqualFunc(Entry->Key, Key)) {
Payload = Entry->Payload;
Found = true;
break;
}
}
if (!ExternalLock)
Mutex.Unlock();
return Found;
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
void HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::resize() {
if (!ExternalLock)
Mutex.CheckLocked();
size_t OldCapacity = Capacity;
HashEntry **OldTable = Table;
Capacity *= 2;
Table = (HashEntry **)InternalAlloc(Capacity * sizeof(HashEntry *));
internal_memset(Table, 0, Capacity * sizeof(HashEntry *));
// Re-hash
for (u32 i = 0; i < OldCapacity; ++i) {
HashEntry *OldEntry = OldTable[i];
while (OldEntry != nullptr) {
HashEntry *Next = OldEntry->Next;
size_t Hash = HashFunc(OldEntry->Key) % Capacity;
OldEntry->Next = Table[Hash];
Table[Hash] = OldEntry;
OldEntry = Next;
}
}
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::add(
const KeyTy &Key, const DataTy &Payload) {
if (!ExternalLock)
Mutex.Lock();
bool Exists = false;
size_t Hash = HashFunc(Key) % Capacity;
HashEntry *Entry = Table[Hash];
for (; Entry != nullptr; Entry = Entry->Next) {
if (EqualFunc(Entry->Key, Key)) {
Exists = true;
break;
}
}
if (!Exists) {
Entries++;
if (Entries * 100 >= Capacity * ResizeFactor) {
resize();
Hash = HashFunc(Key) % Capacity;
}
HashEntry *Add = (HashEntry *)InternalAlloc(sizeof(*Add));
Add->Key = Key;
Add->Payload = Payload;
Add->Next = Table[Hash];
Table[Hash] = Add;
}
if (!ExternalLock)
Mutex.Unlock();
return !Exists;
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::remove(
const KeyTy &Key) {
if (!ExternalLock)
Mutex.Lock();
bool Found = false;
size_t Hash = HashFunc(Key) % Capacity;
HashEntry *Entry = Table[Hash];
HashEntry *Prev = nullptr;
for (; Entry != nullptr; Prev = Entry, Entry = Entry->Next) {
if (EqualFunc(Entry->Key, Key)) {
Found = true;
Entries--;
if (Prev == nullptr)
Table[Hash] = Entry->Next;
else
Prev->Next = Entry->Next;
Entry->Payload.~DataTy();
InternalFree(Entry);
break;
}
}
if (!ExternalLock)
Mutex.Unlock();
return Found;
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
void HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::lock() {
Mutex.Lock();
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
void HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::unlock() {
Mutex.Unlock();
}
//===----------------------------------------------------------------------===//
// Iterator implementation
//===----------------------------------------------------------------------===//
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
iterator(
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table)
: Table(Table), Idx(-1), Entry(nullptr) {
operator++();
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
iterator(
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table,
int Idx)
: Table(Table), Idx(Idx), Entry(nullptr) {
CHECK(Idx >= (int)Table->Capacity); // Only used to create end().
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
EqualFuncTy>::HashPair
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
operator*() {
CHECK(Idx >= 0 && Idx < (int)Table->Capacity);
CHECK(Entry != nullptr);
return HashPair(Entry->Key, Entry->Payload);
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
EqualFuncTy>::iterator &
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
operator++() {
if (Entry != nullptr)
Entry = Entry->Next;
while (Entry == nullptr) {
++Idx;
if (Idx >= (int)Table->Capacity)
break; // At end().
Entry = Table->Table[Idx];
}
return *this;
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
EqualFuncTy>::iterator &
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
operator++(int) {
iterator Temp(*this);
operator++();
return Temp;
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
operator==(const iterator &Cmp) const {
return Cmp.Table == Table && Cmp.Idx == Idx && Cmp.Entry == Entry;
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
operator!=(const iterator &Cmp) const {
return Cmp.Table != Table || Cmp.Idx != Idx || Cmp.Entry != Entry;
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
EqualFuncTy>::iterator
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::begin() {
return iterator(this);
}
template <typename KeyTy, typename DataTy, bool ExternalLock,
typename HashFuncTy, typename EqualFuncTy>
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
EqualFuncTy>::iterator
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::end() {
return iterator(this, Capacity);
}
} // namespace __esan

View file

@ -461,28 +461,35 @@ INTERCEPTOR(int, pthread_sigmask, int how, __sanitizer_sigset_t *set,
// Malloc interceptors
//===----------------------------------------------------------------------===//
static char early_alloc_buf[128];
static bool used_early_alloc_buf;
static const uptr early_alloc_buf_size = 4096;
static uptr allocated_bytes;
static char early_alloc_buf[early_alloc_buf_size];
static bool isInEarlyAllocBuf(const void *ptr) {
return ((uptr)ptr >= (uptr)early_alloc_buf &&
((uptr)ptr - (uptr)early_alloc_buf) < sizeof(early_alloc_buf));
}
static void *handleEarlyAlloc(uptr size) {
// If esan is initialized during an interceptor (which happens with some
// tcmalloc implementations that call pthread_mutex_lock), the call from
// dlsym to calloc will deadlock. There is only one such calloc (dlsym
// allocates a single pthread key), so we work around it by using a
// static buffer for the calloc request. The loader currently needs
// 32 bytes but we size at 128 to allow for future changes.
// dlsym to calloc will deadlock.
// dlsym may also call malloc before REAL(malloc) is retrieved from dlsym.
// We work around it by using a static buffer for the early malloc/calloc
// requests.
// This solution will also allow us to deliberately intercept malloc & family
// in the future (to perform tool actions on each allocation, without
// replacing the allocator), as it also solves the problem of intercepting
// calloc when it will itself be called before its REAL pointer is
// initialized.
CHECK(!used_early_alloc_buf && size < sizeof(early_alloc_buf));
// We do not handle multiple threads here. This only happens at process init
// time, and while it's possible for a shared library to create early threads
// that race here, we consider that to be a corner case extreme enough that
// it's not worth the effort to handle.
used_early_alloc_buf = true;
return (void *)early_alloc_buf;
void *mem = (void *)&early_alloc_buf[allocated_bytes];
allocated_bytes += size;
CHECK_LT(allocated_bytes, early_alloc_buf_size);
return mem;
}
INTERCEPTOR(void*, calloc, uptr size, uptr n) {
@ -496,14 +503,20 @@ INTERCEPTOR(void*, calloc, uptr size, uptr n) {
return res;
}
INTERCEPTOR(void*, malloc, uptr size) {
if (EsanDuringInit && REAL(malloc) == nullptr)
return handleEarlyAlloc(size);
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, malloc, size);
return REAL(malloc)(size);
}
INTERCEPTOR(void, free, void *p) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, free, p);
if (p == (void *)early_alloc_buf) {
// We expect just a singleton use but we clear this for cleanliness.
used_early_alloc_buf = false;
// There are only a few early allocation requests, so we simply skip the free.
if (isInEarlyAllocBuf(p))
return;
}
COMMON_INTERCEPTOR_ENTER(ctx, free, p);
REAL(free)(p);
}
@ -534,6 +547,7 @@ void initializeInterceptors() {
ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK;
INTERCEPT_FUNCTION(calloc);
INTERCEPT_FUNCTION(malloc);
INTERCEPT_FUNCTION(free);
// TODO(bruening): intercept routines that other sanitizers intercept that

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