Import compiler-rt release_34 branch r197381.
https://llvm.org/svn/llvm-project/compiler-rt/branches/release_34@197381
This commit is contained in:
parent
11023dc647
commit
8ef50bf3d1
116
CMakeLists.txt
116
CMakeLists.txt
@ -15,6 +15,9 @@ include(LLVMParseArguments)
|
||||
# runtime libraries.
|
||||
cmake_minimum_required(VERSION 2.8.8)
|
||||
|
||||
# Top level target used to build all compiler-rt libraries.
|
||||
add_custom_target(compiler-rt)
|
||||
|
||||
# Compute the Clang version from the LLVM version.
|
||||
# FIXME: We should be able to reuse CLANG_VERSION variable calculated
|
||||
# in Clang cmake files, instead of copying the rules here.
|
||||
@ -36,21 +39,24 @@ set(CMAKE_MODULE_PATH
|
||||
include(AddCompilerRT)
|
||||
|
||||
set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(COMPILER_RT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
# Setup custom SDK sysroots.
|
||||
set(COMPILER_RT_DARWIN_SDK_SYSROOT ${COMPILER_RT_SOURCE_DIR}/SDKs/darwin)
|
||||
set(COMPILER_RT_LINUX_SDK_SYSROOT ${COMPILER_RT_SOURCE_DIR}/SDKs/linux)
|
||||
include(SanitizerUtils)
|
||||
|
||||
# Detect whether the current target platform is 32-bit or 64-bit, and setup
|
||||
# the correct commandline flags needed to attempt to target 32-bit and 64-bit.
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR LLVM_BUILD_32_BITS)
|
||||
if (NOT CMAKE_SIZEOF_VOID_P EQUAL 4 AND
|
||||
NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
message(FATAL_ERROR "Please use architecture with 4 or 8 byte pointers.")
|
||||
endif()
|
||||
if (NOT MSVC)
|
||||
set(TARGET_64_BIT_CFLAGS "-m64")
|
||||
set(TARGET_32_BIT_CFLAGS "")
|
||||
else()
|
||||
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
message(FATAL_ERROR "Please use a sane architecture with 4 or 8 byte pointers.")
|
||||
endif()
|
||||
set(TARGET_64_BIT_CFLAGS "")
|
||||
set(TARGET_32_BIT_CFLAGS "-m32")
|
||||
else()
|
||||
set(TARGET_64_BIT_CFLAGS "")
|
||||
set(TARGET_32_BIT_CFLAGS "")
|
||||
endif()
|
||||
|
||||
# List of architectures we can target.
|
||||
@ -86,13 +92,12 @@ macro(test_target_arch arch)
|
||||
endmacro()
|
||||
|
||||
if("${LLVM_NATIVE_ARCH}" STREQUAL "X86")
|
||||
test_target_arch(x86_64 ${TARGET_64_BIT_CFLAGS})
|
||||
if (NOT MSVC)
|
||||
test_target_arch(x86_64 ${TARGET_64_BIT_CFLAGS})
|
||||
endif()
|
||||
test_target_arch(i386 ${TARGET_32_BIT_CFLAGS})
|
||||
elseif("${LLVM_NATIVE_ARCH}" STREQUAL "PowerPC")
|
||||
# Explicitly set -m flag on powerpc, because on ppc64 defaults for gcc and
|
||||
# clang are different.
|
||||
test_target_arch(powerpc64 "-m64")
|
||||
test_target_arch(powerpc "-m32")
|
||||
test_target_arch(powerpc64 ${TARGET_64_BIT_CFLAGS})
|
||||
endif()
|
||||
|
||||
# We only support running instrumented tests when we're not cross compiling
|
||||
@ -119,26 +124,43 @@ function(filter_available_targets out_var)
|
||||
set(${out_var} ${archs} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
option(COMPILER_RT_DEBUG "Build runtimes with full debug info" OFF)
|
||||
|
||||
# COMPILER_RT_DEBUG_PYBOOL is used by lit.common.configured.in.
|
||||
pythonize_bool(COMPILER_RT_DEBUG)
|
||||
|
||||
# Provide some common commmandline flags for Sanitizer runtimes.
|
||||
set(SANITIZER_COMMON_CFLAGS
|
||||
-fPIC
|
||||
-fno-builtin
|
||||
-fno-exceptions
|
||||
-fomit-frame-pointer
|
||||
-funwind-tables
|
||||
-fno-stack-protector
|
||||
-Wno-gnu # Variadic macros with 0 arguments for ...
|
||||
-O3
|
||||
)
|
||||
if(NOT WIN32)
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS -fvisibility=hidden)
|
||||
endif()
|
||||
# Build sanitizer runtimes with debug info.
|
||||
check_cxx_compiler_flag(-gline-tables-only SUPPORTS_GLINE_TABLES_ONLY_FLAG)
|
||||
if(SUPPORTS_GLINE_TABLES_ONLY_FLAG)
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS -gline-tables-only)
|
||||
if (NOT MSVC)
|
||||
set(SANITIZER_COMMON_CFLAGS
|
||||
-fPIC
|
||||
-fno-builtin
|
||||
-fno-exceptions
|
||||
-fomit-frame-pointer
|
||||
-funwind-tables
|
||||
-fno-stack-protector
|
||||
-Wno-gnu # Variadic macros with 0 arguments for ...
|
||||
-fvisibility=hidden
|
||||
)
|
||||
if (NOT COMPILER_RT_DEBUG)
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS -O3)
|
||||
endif()
|
||||
else()
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS -g)
|
||||
set(SANITIZER_COMMON_CFLAGS
|
||||
/MT
|
||||
/Zi
|
||||
/Oy-
|
||||
/GS-
|
||||
/wd4722
|
||||
)
|
||||
endif()
|
||||
# Build sanitizer runtimes with debug info. (MSVC gets /Zi above)
|
||||
if (NOT MSVC)
|
||||
check_cxx_compiler_flag(-gline-tables-only SUPPORTS_GLINE_TABLES_ONLY_FLAG)
|
||||
if(SUPPORTS_GLINE_TABLES_ONLY_FLAG AND NOT COMPILER_RT_DEBUG)
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS -gline-tables-only)
|
||||
else()
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS -g)
|
||||
endif()
|
||||
endif()
|
||||
# Warnings suppressions.
|
||||
check_cxx_compiler_flag(-Wno-variadic-macros SUPPORTS_NO_VARIADIC_MACROS_FLAG)
|
||||
@ -155,30 +177,50 @@ check_cxx_compiler_flag(-Wno-non-virtual-dtor SUPPORTS_NO_NON_VIRTUAL_DTOR_FLAG)
|
||||
if (SUPPORTS_NO_NON_VIRTUAL_DTOR_FLAG)
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS -Wno-non-virtual-dtor)
|
||||
endif()
|
||||
check_cxx_compiler_flag(-Wglobal-constructors SUPPORTS_GLOBAL_CONSTRUCTORS_FLAG)
|
||||
# Not all sanitizers forbid global constructors.
|
||||
|
||||
# Setup min Mac OS X version.
|
||||
if(APPLE)
|
||||
# Obtain the iOS Simulator SDK path from xcodebuild.
|
||||
execute_process(
|
||||
COMMAND xcodebuild -version -sdk iphonesimulator Path
|
||||
OUTPUT_VARIABLE IOSSIM_SDK_DIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
set(SANITIZER_COMMON_SUPPORTED_DARWIN_OS osx)
|
||||
if (IOSSIM_SDK_DIR)
|
||||
list(APPEND SANITIZER_COMMON_SUPPORTED_DARWIN_OS iossim)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_USES_LIBCXX)
|
||||
set(SANITIZER_MIN_OSX_VERSION 10.7)
|
||||
else()
|
||||
set(SANITIZER_MIN_OSX_VERSION 10.5)
|
||||
set(SANITIZER_MIN_OSX_VERSION 10.6)
|
||||
endif()
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS
|
||||
-mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION})
|
||||
set(DARWIN_osx_CFLAGS -mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION})
|
||||
set(DARWIN_iossim_CFLAGS
|
||||
-mios-simulator-version-min=7.0 -isysroot ${IOSSIM_SDK_DIR})
|
||||
set(DARWIN_osx_LINKFLAGS)
|
||||
set(DARWIN_iossim_LINKFLAGS
|
||||
-Wl,-ios_simulator_version_min,7.0.0
|
||||
-mios-simulator-version-min=7.0
|
||||
-isysroot ${IOSSIM_SDK_DIR})
|
||||
endif()
|
||||
|
||||
# Architectures supported by Sanitizer runtimes. Specific sanitizers may
|
||||
# support only subset of these (e.g. TSan works on x86_64 only).
|
||||
filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH
|
||||
x86_64 i386 powerpc64 powerpc)
|
||||
x86_64 i386 powerpc64)
|
||||
|
||||
# Add the public header's directory to the includes for all of compiler-rt.
|
||||
include_directories(include)
|
||||
add_subdirectory(include)
|
||||
|
||||
set(SANITIZER_COMMON_LIT_TEST_DEPS
|
||||
clang clang-headers FileCheck count not llvm-nm llvm-symbolizer
|
||||
compiler-rt-headers)
|
||||
# Check code style when running lit tests for sanitizers.
|
||||
if(UNIX)
|
||||
list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS SanitizerLintCheck)
|
||||
endif()
|
||||
|
||||
add_subdirectory(lib)
|
||||
|
||||
|
17
SDKs/darwin/usr/include/errno.h
Normal file
17
SDKs/darwin/usr/include/errno.h
Normal file
@ -0,0 +1,17 @@
|
||||
/* ===-- errno.h - stub SDK header for compiler-rt --------------------------===
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ===-----------------------------------------------------------------------===
|
||||
*
|
||||
* This is a stub SDK header file. This file is not part of the interface of
|
||||
* this library nor an official version of the appropriate SDK header. It is
|
||||
* intended only to stub the features of this header required by compiler-rt.
|
||||
*
|
||||
* ===-----------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#include <sys/errno.h>
|
@ -28,4 +28,25 @@ char *strdup(const char *);
|
||||
size_t strlen(const char *);
|
||||
char *strncpy(char *, const char *, size_t);
|
||||
|
||||
/* Determine the appropriate strerror() function. */
|
||||
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
|
||||
# if defined(__i386)
|
||||
# define __STRERROR_NAME "_strerror$UNIX2003"
|
||||
# elif defined(__x86_64__) || defined(__arm)
|
||||
# define __STRERROR_NAME "_strerror"
|
||||
# else
|
||||
# error "unrecognized architecture for targetting OS X"
|
||||
# endif
|
||||
#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
|
||||
# if defined(__i386) || defined (__x86_64) || defined(__arm)
|
||||
# define __STRERROR_NAME "_strerror"
|
||||
# else
|
||||
# error "unrecognized architecture for targetting iOS"
|
||||
# endif
|
||||
#else
|
||||
# error "unrecognized architecture for targetting Darwin"
|
||||
#endif
|
||||
|
||||
char *strerror(int) __asm(__STRERROR_NAME);
|
||||
|
||||
#endif /* __STRING_H__ */
|
||||
|
31
SDKs/darwin/usr/include/sys/errno.h
Normal file
31
SDKs/darwin/usr/include/sys/errno.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* ===-- errno.h - stub SDK header for compiler-rt --------------------------===
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ===-----------------------------------------------------------------------===
|
||||
*
|
||||
* This is a stub SDK header file. This file is not part of the interface of
|
||||
* this library nor an official version of the appropriate SDK header. It is
|
||||
* intended only to stub the features of this header required by compiler-rt.
|
||||
*
|
||||
* ===-----------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#ifndef _SYS_ERRNO_H_
|
||||
#define _SYS_ERRNO_H_
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int *__error(void);
|
||||
#define errno (*__error())
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -6,29 +6,35 @@ include(CompilerRTUtils)
|
||||
# with name "<name>.<arch>" if architecture can be targeted.
|
||||
# add_compiler_rt_object_library(<name> <arch>
|
||||
# SOURCES <source files>
|
||||
# CFLAGS <compile flags>)
|
||||
# CFLAGS <compile flags>
|
||||
# DEFS <compile definitions>)
|
||||
macro(add_compiler_rt_object_library name arch)
|
||||
if(CAN_TARGET_${arch})
|
||||
parse_arguments(LIB "SOURCES;CFLAGS" "" ${ARGN})
|
||||
parse_arguments(LIB "SOURCES;CFLAGS;DEFS" "" ${ARGN})
|
||||
add_library(${name}.${arch} OBJECT ${LIB_SOURCES})
|
||||
set_target_compile_flags(${name}.${arch}
|
||||
${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS})
|
||||
set_property(TARGET ${name}.${arch} APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS ${LIB_DEFS})
|
||||
else()
|
||||
message(FATAL_ERROR "Archtecture ${arch} can't be targeted")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# Same as above, but adds universal osx library with name "<name>.osx"
|
||||
# targeting multiple architectures.
|
||||
# add_compiler_rt_osx_object_library(<name> ARCH <architectures>
|
||||
# SOURCES <source files>
|
||||
# CFLAGS <compile flags>)
|
||||
macro(add_compiler_rt_osx_object_library name)
|
||||
parse_arguments(LIB "ARCH;SOURCES;CFLAGS" "" ${ARGN})
|
||||
set(libname "${name}.osx")
|
||||
# Same as above, but adds universal osx library for either OSX or iOS simulator
|
||||
# with name "<name>.<os>" targeting multiple architectures.
|
||||
# add_compiler_rt_darwin_object_library(<name> <os> ARCH <architectures>
|
||||
# SOURCES <source files>
|
||||
# CFLAGS <compile flags>
|
||||
# DEFS <compile definitions>)
|
||||
macro(add_compiler_rt_darwin_object_library name os)
|
||||
parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS" "" ${ARGN})
|
||||
set(libname "${name}.${os}")
|
||||
add_library(${libname} OBJECT ${LIB_SOURCES})
|
||||
set_target_compile_flags(${libname} ${LIB_CFLAGS})
|
||||
set_target_compile_flags(${libname} ${LIB_CFLAGS} ${DARWIN_${os}_CFLAGS})
|
||||
set_target_properties(${libname} PROPERTIES OSX_ARCHITECTURES "${LIB_ARCH}")
|
||||
set_property(TARGET ${libname} APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS ${LIB_DEFS})
|
||||
endmacro()
|
||||
|
||||
# Adds static runtime for a given architecture and puts it in the proper
|
||||
@ -36,11 +42,10 @@ endmacro()
|
||||
# add_compiler_rt_static_runtime(<name> <arch>
|
||||
# SOURCES <source files>
|
||||
# CFLAGS <compile flags>
|
||||
# DEFS <compile definitions>
|
||||
# SYMS <symbols file>)
|
||||
# DEFS <compile definitions>)
|
||||
macro(add_compiler_rt_static_runtime name arch)
|
||||
if(CAN_TARGET_${arch})
|
||||
parse_arguments(LIB "SOURCES;CFLAGS;DEFS;SYMS" "" ${ARGN})
|
||||
parse_arguments(LIB "SOURCES;CFLAGS;DEFS" "" ${ARGN})
|
||||
add_library(${name} STATIC ${LIB_SOURCES})
|
||||
# Setup compile flags and definitions.
|
||||
set_target_compile_flags(${name}
|
||||
@ -53,13 +58,7 @@ macro(add_compiler_rt_static_runtime name arch)
|
||||
# Add installation command.
|
||||
install(TARGETS ${name}
|
||||
ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR})
|
||||
# Generate the .syms file if possible.
|
||||
if(LIB_SYMS)
|
||||
get_target_property(libfile ${name} LOCATION)
|
||||
configure_file(${LIB_SYMS} ${libfile}.syms)
|
||||
install(FILES ${libfile}.syms
|
||||
DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR})
|
||||
endif(LIB_SYMS)
|
||||
add_dependencies(compiler-rt ${name})
|
||||
else()
|
||||
message(FATAL_ERROR "Archtecture ${arch} can't be targeted")
|
||||
endif()
|
||||
@ -82,19 +81,22 @@ macro(add_compiler_rt_osx_static_runtime name)
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR})
|
||||
install(TARGETS ${name}
|
||||
ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR})
|
||||
add_dependencies(compiler-rt ${name})
|
||||
endmacro()
|
||||
|
||||
# Adds dynamic runtime library on osx, which supports multiple architectures.
|
||||
# add_compiler_rt_osx_dynamic_runtime(<name> ARCH <architectures>
|
||||
# SOURCES <source files>
|
||||
# CFLAGS <compile flags>
|
||||
# DEFS <compile definitions>
|
||||
# LINKFLAGS <link flags>)
|
||||
macro(add_compiler_rt_osx_dynamic_runtime name)
|
||||
# Adds dynamic runtime library on osx/iossim, which supports multiple
|
||||
# architectures.
|
||||
# add_compiler_rt_darwin_dynamic_runtime(<name> <os>
|
||||
# ARCH <architectures>
|
||||
# SOURCES <source files>
|
||||
# CFLAGS <compile flags>
|
||||
# DEFS <compile definitions>
|
||||
# LINKFLAGS <link flags>)
|
||||
macro(add_compiler_rt_darwin_dynamic_runtime name os)
|
||||
parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS;LINKFLAGS" "" ${ARGN})
|
||||
add_library(${name} SHARED ${LIB_SOURCES})
|
||||
set_target_compile_flags(${name} ${LIB_CFLAGS})
|
||||
set_target_link_flags(${name} ${LIB_LINKFLAGS})
|
||||
set_target_compile_flags(${name} ${LIB_CFLAGS} ${DARWIN_${os}_CFLAGS})
|
||||
set_target_link_flags(${name} ${LIB_LINKFLAGS} ${DARWIN_${os}_LINKFLAGS})
|
||||
set_property(TARGET ${name} APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS ${LIB_DEFS})
|
||||
set_target_properties(${name} PROPERTIES
|
||||
@ -102,14 +104,16 @@ macro(add_compiler_rt_osx_dynamic_runtime name)
|
||||
LIBRARY_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR})
|
||||
install(TARGETS ${name}
|
||||
LIBRARY DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR})
|
||||
add_dependencies(compiler-rt ${name})
|
||||
endmacro()
|
||||
|
||||
# Unittests support.
|
||||
set(COMPILER_RT_GTEST_PATH ${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest)
|
||||
set(COMPILER_RT_GTEST_SOURCE ${COMPILER_RT_GTEST_PATH}/gtest-all.cc)
|
||||
set(COMPILER_RT_GTEST_SOURCE ${COMPILER_RT_GTEST_PATH}/src/gtest-all.cc)
|
||||
set(COMPILER_RT_GTEST_INCLUDE_CFLAGS
|
||||
-DGTEST_NO_LLVM_RAW_OSTREAM=1
|
||||
-I${COMPILER_RT_GTEST_PATH}/include
|
||||
-I${COMPILER_RT_GTEST_PATH}
|
||||
)
|
||||
|
||||
# Use Clang to link objects into a single executable with just-built
|
||||
|
@ -26,3 +26,13 @@ function(find_flag_in_string flag_string flag out_var)
|
||||
set(${out_var} FALSE PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Set the variable var_PYBOOL to True if var holds a true-ish string,
|
||||
# otherwise set it to False.
|
||||
macro(pythonize_bool var)
|
||||
if (${var})
|
||||
set(${var}_PYBOOL True)
|
||||
else()
|
||||
set(${var}_PYBOOL False)
|
||||
endif()
|
||||
endmacro()
|
||||
|
42
cmake/Modules/SanitizerUtils.cmake
Normal file
42
cmake/Modules/SanitizerUtils.cmake
Normal file
@ -0,0 +1,42 @@
|
||||
include(LLVMParseArguments)
|
||||
|
||||
set(SANITIZER_GEN_DYNAMIC_LIST
|
||||
${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/scripts/gen_dynamic_list.py)
|
||||
|
||||
set(SANITIZER_LINT_SCRIPT
|
||||
${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/scripts/check_lint.sh)
|
||||
|
||||
# Create a target "<name>-symbols" that would generate the list of symbols
|
||||
# that need to be exported from sanitizer runtime "<name>". Function
|
||||
# interceptors are exported automatically, user can also provide files with
|
||||
# symbol names that should be exported as well.
|
||||
# add_sanitizer_rt_symbols(<name> <files with extra symbols to export>)
|
||||
macro(add_sanitizer_rt_symbols name)
|
||||
get_target_property(libfile ${name} LOCATION)
|
||||
set(symsfile "${libfile}.syms")
|
||||
add_custom_command(OUTPUT ${symsfile}
|
||||
COMMAND ${PYTHON_EXECUTABLE}
|
||||
${SANITIZER_GEN_DYNAMIC_LIST} ${libfile} ${ARGN}
|
||||
> ${symsfile}
|
||||
DEPENDS ${name} ${SANITIZER_GEN_DYNAMIC_LIST} ${ARGN}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Generating exported symbols for ${name}"
|
||||
VERBATIM)
|
||||
add_custom_target(${name}-symbols ALL
|
||||
DEPENDS ${symsfile}
|
||||
SOURCES ${SANITIZER_GEN_DYNAMIC_LIST} ${ARGN})
|
||||
install(FILES ${symsfile} DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR})
|
||||
add_dependencies(compiler-rt ${name}-symbols)
|
||||
endmacro()
|
||||
|
||||
# Add target to check code style for sanitizer runtimes.
|
||||
if(UNIX)
|
||||
add_custom_target(SanitizerLintCheck
|
||||
COMMAND LLVM_CHECKOUT=${LLVM_MAIN_SRC_DIR} SILENT=1 TMPDIR=
|
||||
PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}
|
||||
${SANITIZER_LINT_SCRIPT}
|
||||
DEPENDS ${SANITIZER_LINT_SCRIPT}
|
||||
COMMENT "Running lint check for sanitizer sources..."
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
@ -1,7 +1,9 @@
|
||||
set(SANITIZER_HEADERS
|
||||
sanitizer/asan_interface.h
|
||||
sanitizer/common_interface_defs.h
|
||||
sanitizer/dfsan_interface.h
|
||||
sanitizer/linux_syscall_hooks.h
|
||||
sanitizer/lsan_interface.h
|
||||
sanitizer/msan_interface.h)
|
||||
|
||||
set(output_dir ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/include)
|
||||
@ -32,6 +34,7 @@ foreach( f ${SANITIZER_HEADERS} )
|
||||
endforeach( f )
|
||||
|
||||
add_custom_target(compiler-rt-headers ALL DEPENDS ${out_files})
|
||||
add_dependencies(compiler-rt compiler-rt-headers)
|
||||
|
||||
# Install sanitizer headers.
|
||||
install(FILES ${SANITIZER_HEADERS}
|
||||
|
@ -27,10 +27,6 @@ extern "C" {
|
||||
// Tell the tools to write their reports to "path.<pid>" instead of stderr.
|
||||
void __sanitizer_set_report_path(const char *path);
|
||||
|
||||
// Tell the tools to write their reports to given file descriptor instead of
|
||||
// stderr.
|
||||
void __sanitizer_set_report_fd(int fd);
|
||||
|
||||
// Notify the tools that the sandbox is going to be turned on. The reserved
|
||||
// parameter will be used in the future to hold a structure with functions
|
||||
// that the tools may call to bypass the sandbox.
|
||||
@ -51,6 +47,33 @@ extern "C" {
|
||||
void __sanitizer_unaligned_store32(void *p, uint32_t x);
|
||||
void __sanitizer_unaligned_store64(void *p, uint64_t x);
|
||||
|
||||
// Record and dump coverage info.
|
||||
void __sanitizer_cov_dump();
|
||||
|
||||
// Annotate the current state of a contiguous container, such as
|
||||
// std::vector, std::string or similar.
|
||||
// A contiguous container is a container that keeps all of its elements
|
||||
// in a contiguous region of memory. The container owns the region of memory
|
||||
// [beg, end); the memory [beg, mid) is used to store the current elements
|
||||
// and the memory [mid, end) is reserved for future elements;
|
||||
// end <= mid <= end. For example, in "std::vector<> v"
|
||||
// beg = &v[0];
|
||||
// end = beg + v.capacity() * sizeof(v[0]);
|
||||
// mid = beg + v.size() * sizeof(v[0]);
|
||||
//
|
||||
// This annotation tells the Sanitizer tool about the current state of the
|
||||
// container so that the tool can report errors when memory from [mid, end)
|
||||
// is accessed. Insert this annotation into methods like push_back/pop_back.
|
||||
// Supply the old and the new values of mid (old_mid/new_mid).
|
||||
// In the initial state mid == end and so should be the final
|
||||
// state when the container is destroyed or when it reallocates the storage.
|
||||
//
|
||||
// Use with caution and don't use for anything other than vector-like classes.
|
||||
//
|
||||
// For AddressSanitizer, 'beg' should be 8-aligned.
|
||||
void __sanitizer_annotate_contiguous_container(void *beg, void *end,
|
||||
void *old_mid, void *new_mid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
87
include/sanitizer/dfsan_interface.h
Normal file
87
include/sanitizer/dfsan_interface.h
Normal file
@ -0,0 +1,87 @@
|
||||
//===-- dfsan_interface.h -------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of DataFlowSanitizer.
|
||||
//
|
||||
// Public interface header.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef DFSAN_INTERFACE_H
|
||||
#define DFSAN_INTERFACE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sanitizer/common_interface_defs.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef uint16_t dfsan_label;
|
||||
|
||||
/// Stores information associated with a specific label identifier. A label
|
||||
/// may be a base label created using dfsan_create_label, with associated
|
||||
/// text description and user data, or an automatically created union label,
|
||||
/// which represents the union of two label identifiers (which may themselves
|
||||
/// be base or union labels).
|
||||
struct dfsan_label_info {
|
||||
// Fields for union labels, set to 0 for base labels.
|
||||
dfsan_label l1;
|
||||
dfsan_label l2;
|
||||
|
||||
// Fields for base labels.
|
||||
const char *desc;
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
/// Computes the union of \c l1 and \c l2, possibly creating a union label in
|
||||
/// the process.
|
||||
dfsan_label dfsan_union(dfsan_label l1, dfsan_label l2);
|
||||
|
||||
/// Creates and returns a base label with the given description and user data.
|
||||
dfsan_label dfsan_create_label(const char *desc, void *userdata);
|
||||
|
||||
/// Sets the label for each address in [addr,addr+size) to \c label.
|
||||
void dfsan_set_label(dfsan_label label, void *addr, size_t size);
|
||||
|
||||
/// Sets the label for each address in [addr,addr+size) to the union of the
|
||||
/// current label for that address and \c label.
|
||||
void dfsan_add_label(dfsan_label label, void *addr, size_t size);
|
||||
|
||||
/// Retrieves the label associated with the given data.
|
||||
///
|
||||
/// The type of 'data' is arbitrary. The function accepts a value of any type,
|
||||
/// which can be truncated or extended (implicitly or explicitly) as necessary.
|
||||
/// The truncation/extension operations will preserve the label of the original
|
||||
/// value.
|
||||
dfsan_label dfsan_get_label(long data);
|
||||
|
||||
/// Retrieves the label associated with the data at the given address.
|
||||
dfsan_label dfsan_read_label(const void *addr, size_t size);
|
||||
|
||||
/// Retrieves a pointer to the dfsan_label_info struct for the given label.
|
||||
const struct dfsan_label_info *dfsan_get_label_info(dfsan_label label);
|
||||
|
||||
/// Returns whether the given label label contains the label elem.
|
||||
int dfsan_has_label(dfsan_label label, dfsan_label elem);
|
||||
|
||||
/// If the given label label contains a label with the description desc, returns
|
||||
/// that label, else returns 0.
|
||||
dfsan_label dfsan_has_label_with_desc(dfsan_label label, const char *desc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
template <typename T>
|
||||
void dfsan_set_label(dfsan_label label, T &data) { // NOLINT
|
||||
dfsan_set_label(label, (void *)&data, sizeof(T));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // DFSAN_INTERFACE_H
|
File diff suppressed because it is too large
Load Diff
52
include/sanitizer/lsan_interface.h
Normal file
52
include/sanitizer/lsan_interface.h
Normal file
@ -0,0 +1,52 @@
|
||||
//===-- sanitizer/lsan_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 LeakSanitizer.
|
||||
//
|
||||
// Public interface header.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_LSAN_INTERFACE_H
|
||||
#define SANITIZER_LSAN_INTERFACE_H
|
||||
|
||||
#include <sanitizer/common_interface_defs.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// Allocations made between calls to __lsan_disable() and __lsan_enable() will
|
||||
// be treated as non-leaks. Disable/enable pairs may be nested.
|
||||
void __lsan_disable();
|
||||
void __lsan_enable();
|
||||
// The heap object into which p points will be treated as a non-leak.
|
||||
void __lsan_ignore_object(const void *p);
|
||||
// The user may optionally provide this function to disallow leak checking
|
||||
// for the program it is linked into (if the return value is non-zero). This
|
||||
// function must be defined as returning a constant value; any behavior beyond
|
||||
// that is unsupported.
|
||||
int __lsan_is_turned_off();
|
||||
// Calling this function makes LSan enter the leak checking phase immediately.
|
||||
// Use this if normal end-of-process leak checking happens too late (e.g. if
|
||||
// you have intentional memory leaks in your shutdown code). Calling this
|
||||
// function overrides end-of-process leak checking; it must be called at
|
||||
// most once per process. This function will terminate the process if there
|
||||
// are memory leaks and the exit_code flag is non-zero.
|
||||
void __lsan_do_leak_check();
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
namespace __lsan {
|
||||
class ScopedDisabler {
|
||||
public:
|
||||
ScopedDisabler() { __lsan_disable(); }
|
||||
~ScopedDisabler() { __lsan_enable(); }
|
||||
};
|
||||
} // namespace __lsan
|
||||
#endif
|
||||
|
||||
#endif // SANITIZER_LSAN_INTERFACE_H
|
@ -27,10 +27,10 @@ extern "C" {
|
||||
|
||||
|
||||
/* Set raw origin for the memory range. */
|
||||
void __msan_set_origin(const void *a, size_t size, uint32_t origin);
|
||||
void __msan_set_origin(const volatile void *a, size_t size, uint32_t origin);
|
||||
|
||||
/* Get raw origin for an address. */
|
||||
uint32_t __msan_get_origin(const void *a);
|
||||
uint32_t __msan_get_origin(const volatile void *a);
|
||||
|
||||
/* Returns non-zero if tracking origins. */
|
||||
int __msan_get_track_origins();
|
||||
@ -39,18 +39,19 @@ extern "C" {
|
||||
uint32_t __msan_get_umr_origin();
|
||||
|
||||
/* Make memory region fully initialized (without changing its contents). */
|
||||
void __msan_unpoison(const void *a, size_t size);
|
||||
void __msan_unpoison(const volatile void *a, size_t size);
|
||||
|
||||
/* Make memory region fully uninitialized (without changing its contents). */
|
||||
void __msan_poison(const void *a, size_t size);
|
||||
void __msan_poison(const volatile void *a, size_t size);
|
||||
|
||||
/* Make memory region partially uninitialized (without changing its contents).
|
||||
*/
|
||||
void __msan_partial_poison(const void* data, void* shadow, size_t size);
|
||||
void __msan_partial_poison(const volatile void *data, void *shadow,
|
||||
size_t size);
|
||||
|
||||
/* Returns the offset of the first (at least partially) poisoned byte in the
|
||||
memory range, or -1 if the whole range is good. */
|
||||
intptr_t __msan_test_shadow(const void *x, size_t size);
|
||||
intptr_t __msan_test_shadow(const volatile void *x, size_t size);
|
||||
|
||||
/* Set exit code when error(s) were detected.
|
||||
Value of 0 means don't change the program exit code. */
|
||||
@ -63,9 +64,14 @@ extern "C" {
|
||||
The last line will verify that a UMR happened. */
|
||||
void __msan_set_expect_umr(int expect_umr);
|
||||
|
||||
/* Change the value of keep_going flag. Non-zero value means don't terminate
|
||||
program execution when an error is detected. This will not affect error in
|
||||
modules that were compiled without the corresponding compiler flag. */
|
||||
void __msan_set_keep_going(int keep_going);
|
||||
|
||||
/* Print shadow and origin for the memory range to stdout in a human-readable
|
||||
format. */
|
||||
void __msan_print_shadow(const void *x, size_t size);
|
||||
void __msan_print_shadow(const volatile void *x, size_t size);
|
||||
|
||||
/* Print current function arguments shadow and origin to stdout in a
|
||||
human-readable format. */
|
||||
@ -76,7 +82,58 @@ extern "C" {
|
||||
|
||||
/* Tell MSan about newly allocated memory (ex.: custom allocator).
|
||||
Memory will be marked uninitialized, with origin at the call site. */
|
||||
void __msan_allocated_memory(const void* data, size_t size);
|
||||
void __msan_allocated_memory(const volatile void* data, size_t size);
|
||||
|
||||
/* This function may be optionally provided by user and should return
|
||||
a string containing Msan runtime options. See msan_flags.h for details. */
|
||||
const char* __msan_default_options();
|
||||
|
||||
|
||||
/***********************************/
|
||||
/* Allocator statistics interface. */
|
||||
|
||||
/* Returns the estimated number of bytes that will be reserved by allocator
|
||||
for request of "size" bytes. If Msan allocator can't allocate that much
|
||||
memory, returns the maximal possible allocation size, otherwise returns
|
||||
"size". */
|
||||
size_t __msan_get_estimated_allocated_size(size_t size);
|
||||
|
||||
/* Returns true if p was returned by the Msan allocator and
|
||||
is not yet freed. */
|
||||
int __msan_get_ownership(const volatile void *p);
|
||||
|
||||
/* Returns the number of bytes reserved for the pointer p.
|
||||
Requires (get_ownership(p) == true) or (p == 0). */
|
||||
size_t __msan_get_allocated_size(const volatile void *p);
|
||||
|
||||
/* Number of bytes, allocated and not yet freed by the application. */
|
||||
size_t __msan_get_current_allocated_bytes();
|
||||
|
||||
/* Number of bytes, mmaped by msan allocator to fulfill allocation requests.
|
||||
Generally, for request of X bytes, allocator can reserve and add to free
|
||||
lists a large number of chunks of size X to use them for future requests.
|
||||
All these chunks count toward the heap size. Currently, allocator never
|
||||
releases memory to OS (instead, it just puts freed chunks to free
|
||||
lists). */
|
||||
size_t __msan_get_heap_size();
|
||||
|
||||
/* Number of bytes, mmaped by msan allocator, which can be used to fulfill
|
||||
allocation requests. When a user program frees memory chunk, it can first
|
||||
fall into quarantine and will count toward __msan_get_free_bytes()
|
||||
later. */
|
||||
size_t __msan_get_free_bytes();
|
||||
|
||||
/* Number of bytes in unmapped pages, that are released to OS. Currently,
|
||||
always returns 0. */
|
||||
size_t __msan_get_unmapped_bytes();
|
||||
|
||||
/* Malloc hooks that may be optionally provided by user.
|
||||
__msan_malloc_hook(ptr, size) is called immediately after
|
||||
allocation of "size" bytes, which returned "ptr".
|
||||
__msan_free_hook(ptr) is called immediately before
|
||||
deallocation of "ptr". */
|
||||
void __msan_malloc_hook(const volatile void *ptr, size_t size);
|
||||
void __msan_free_hook(const volatile void *ptr);
|
||||
|
||||
#else // __has_feature(memory_sanitizer)
|
||||
|
||||
|
@ -1,22 +1,36 @@
|
||||
# First, add the subdirectories which contain feature-based runtime libraries
|
||||
# and several convenience helper libraries.
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux")
|
||||
|
||||
# Don't build sanitizers in the bootstrap build.
|
||||
if(LLVM_USE_SANITIZER STREQUAL "")
|
||||
# AddressSanitizer is supported on Linux and Mac OS X.
|
||||
# Windows support is work in progress.
|
||||
add_subdirectory(asan)
|
||||
add_subdirectory(interception)
|
||||
add_subdirectory(sanitizer_common)
|
||||
if(NOT ANDROID)
|
||||
# 32-bit Windows support is experimental.
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux")
|
||||
set(SUPPORTS_BUILDING_ASAN TRUE)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows"
|
||||
AND MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(SUPPORTS_BUILDING_ASAN TRUE)
|
||||
else()
|
||||
set(SUPPORTS_BUILDING_ASAN FALSE)
|
||||
endif()
|
||||
if(SUPPORTS_BUILDING_ASAN)
|
||||
add_subdirectory(asan)
|
||||
add_subdirectory(interception)
|
||||
add_subdirectory(sanitizer_common)
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux" AND NOT ANDROID)
|
||||
# LSan, UBsan and profile can be built on Mac OS and Linux.
|
||||
add_subdirectory(lsan)
|
||||
add_subdirectory(profile)
|
||||
add_subdirectory(ubsan)
|
||||
endif()
|
||||
endif()
|
||||
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND NOT ANDROID)
|
||||
# ThreadSanitizer and MemorySanitizer are supported on Linux only.
|
||||
add_subdirectory(tsan)
|
||||
add_subdirectory(msan)
|
||||
add_subdirectory(msandr)
|
||||
add_subdirectory(lsan)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT ANDROID)
|
||||
# ThreadSanitizer and MemorySanitizer are supported on Linux only.
|
||||
add_subdirectory(tsan)
|
||||
add_subdirectory(msan)
|
||||
add_subdirectory(msandr)
|
||||
add_subdirectory(dfsan)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# The top-level lib directory contains a large amount of C code which provides
|
||||
@ -181,10 +195,22 @@ set(i386_SOURCES
|
||||
i386/umoddi3.S
|
||||
${GENERIC_SOURCES})
|
||||
|
||||
foreach(arch x86_64 i386)
|
||||
if(CAN_TARGET_${arch})
|
||||
add_compiler_rt_static_runtime(clang_rt.${arch} ${arch}
|
||||
SOURCES ${${arch}_SOURCES}
|
||||
CFLAGS "-std=c99")
|
||||
endif()
|
||||
endforeach()
|
||||
if (NOT WIN32)
|
||||
foreach(arch x86_64 i386)
|
||||
if(CAN_TARGET_${arch})
|
||||
add_compiler_rt_static_runtime(clang_rt.${arch} ${arch}
|
||||
SOURCES ${${arch}_SOURCES}
|
||||
CFLAGS "-std=c99")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Generate configs for running lit and unit tests.
|
||||
configure_lit_site_cfg(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lit.common.configured.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/lit.common.configured)
|
||||
|
||||
configure_lit_site_cfg(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lit.common.unit.configured.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/lit.common.unit.configured)
|
||||
|
||||
|
@ -22,6 +22,7 @@ SubDirs += tsan
|
||||
SubDirs += msan
|
||||
SubDirs += ubsan
|
||||
SubDirs += lsan
|
||||
SubDirs += dfsan
|
||||
|
||||
# Define the variables for this specific directory.
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
|
||||
|
@ -9,19 +9,20 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#if __APPLE__
|
||||
#if __arm__
|
||||
#include <Availability.h>
|
||||
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED
|
||||
#define NOT_HERE_BEFORE_10_6(sym)
|
||||
#define NOT_HERE_IN_10_8_AND_EARLIER(sym)
|
||||
#elif __ppc__
|
||||
#define NOT_HERE_BEFORE_10_6(sym) \
|
||||
extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp3 = 0; \
|
||||
extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
|
||||
extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp5 = 0;
|
||||
#define NOT_HERE_IN_10_8_AND_EARLIER(sym) \
|
||||
extern const char sym##_tmp61 __asm("$ld$hide$os6.1$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp61 = 0; \
|
||||
extern const char sym##_tmp60 __asm("$ld$hide$os6.0$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp60 = 0; \
|
||||
extern const char sym##_tmp51 __asm("$ld$hide$os5.1$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp51 = 0; \
|
||||
extern const char sym##_tmp50 __asm("$ld$hide$os5.0$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp50 = 0;
|
||||
#else
|
||||
#define NOT_HERE_BEFORE_10_6(sym) \
|
||||
extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \
|
||||
@ -35,7 +36,7 @@
|
||||
__attribute__((visibility("default"))) const char sym##_tmp7 = 0; \
|
||||
extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp6 = 0;
|
||||
#endif /* __ppc__ */
|
||||
#endif
|
||||
|
||||
|
||||
/* Symbols in libSystem.dylib in 10.6 and later,
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
ModuleName := builtins
|
||||
SubDirs :=
|
||||
OnlyArchs := armv5 armv6 armv7 armv7f armv7k armv7s
|
||||
OnlyArchs := armv5 armv6 armv7 armv7f armv7k armv7m armv7em armv7s
|
||||
|
||||
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
|
||||
|
@ -59,12 +59,14 @@ DEFINE_COMPILERRT_FUNCTION(__nesf2)
|
||||
|
||||
// Next, we check if a and b have the same or different signs. If they have
|
||||
// opposite signs, this eor will set the N flag.
|
||||
it ne
|
||||
eorsne r12, r0, r1
|
||||
|
||||
// If a and b are equal (either both zeros or bit identical; again, we're
|
||||
// ignoring NaNs for now), this subtract will zero out r0. If they have the
|
||||
// same sign, the flags are updated as they would be for a comparison of the
|
||||
// absolute values of a and b.
|
||||
it pl
|
||||
subspl r0, r2, r3
|
||||
|
||||
// If a is smaller in magnitude than b and both have the same sign, place
|
||||
@ -77,23 +79,27 @@ DEFINE_COMPILERRT_FUNCTION(__nesf2)
|
||||
// still clear from the shift argument in orrs; if a is positive and b
|
||||
// negative, this places 0 in r0; if a is negative and b positive, -1 is
|
||||
// placed in r0.
|
||||
it lo
|
||||
mvnlo r0, r1, asr #31
|
||||
|
||||
// If a is greater in magnitude than b and both have the same sign, place
|
||||
// the sign of b in r0. Thus, if both are negative and a < b, -1 is placed
|
||||
// in r0, which is the desired result. Conversely, if both are positive
|
||||
// and a > b, zero is placed in r0.
|
||||
it hi
|
||||
movhi r0, r1, asr #31
|
||||
|
||||
// If you've been keeping track, at this point r0 contains -1 if a < b and
|
||||
// 0 if a >= b. All that remains to be done is to set it to 1 if a > b.
|
||||
// If a == b, then the Z flag is set, so we can get the correct final value
|
||||
// into r0 by simply or'ing with 1 if Z is clear.
|
||||
orrne r0, r0, #1
|
||||
it ne
|
||||
orrne r0, r0, #1
|
||||
|
||||
// Finally, we need to deal with NaNs. If either argument is NaN, replace
|
||||
// the value in r0 with 1.
|
||||
cmp r2, #0xff000000
|
||||
ite ls
|
||||
cmpls r3, #0xff000000
|
||||
movhi r0, #1
|
||||
bx lr
|
||||
@ -108,12 +114,18 @@ DEFINE_COMPILERRT_FUNCTION(__gtsf2)
|
||||
mov r2, r0, lsl #1
|
||||
mov r3, r1, lsl #1
|
||||
orrs r12, r2, r3, lsr #1
|
||||
it ne
|
||||
eorsne r12, r0, r1
|
||||
it pl
|
||||
subspl r0, r2, r3
|
||||
it lo
|
||||
mvnlo r0, r1, asr #31
|
||||
it hi
|
||||
movhi r0, r1, asr #31
|
||||
orrne r0, r0, #1
|
||||
it ne
|
||||
orrne r0, r0, #1
|
||||
cmp r2, #0xff000000
|
||||
ite ls
|
||||
cmpls r3, #0xff000000
|
||||
movhi r0, #-1
|
||||
bx lr
|
||||
@ -125,6 +137,7 @@ DEFINE_COMPILERRT_FUNCTION(__unordsf2)
|
||||
mov r3, r1, lsl #1
|
||||
mov r0, #0
|
||||
cmp r2, #0xff000000
|
||||
ite ls
|
||||
cmpls r3, #0xff000000
|
||||
movhi r0, #1
|
||||
bx lr
|
||||
|
@ -24,7 +24,7 @@
|
||||
.syntax unified
|
||||
.align 3
|
||||
DEFINE_COMPILERRT_FUNCTION(__divmodsi4)
|
||||
#if __ARM_ARCH_7S__
|
||||
#if __ARM_ARCH_EXT_IDIV__
|
||||
tst r1, r1
|
||||
beq LOCAL_LABEL(divzero)
|
||||
mov r3, r0
|
||||
|
@ -25,7 +25,7 @@
|
||||
// Ok, APCS and AAPCS agree on 32 bit args, so it's safe to use the same routine.
|
||||
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_idiv, __divsi3)
|
||||
DEFINE_COMPILERRT_FUNCTION(__divsi3)
|
||||
#if __ARM_ARCH_7S__
|
||||
#if __ARM_ARCH_EXT_IDIV__
|
||||
tst r1,r1
|
||||
beq LOCAL_LABEL(divzero)
|
||||
sdiv r0, r0, r1
|
||||
|
@ -23,7 +23,7 @@
|
||||
.syntax unified
|
||||
.align 3
|
||||
DEFINE_COMPILERRT_FUNCTION(__modsi3)
|
||||
#if __ARM_ARCH_7S__
|
||||
#if __ARM_ARCH_EXT_IDIV__
|
||||
tst r1, r1
|
||||
beq LOCAL_LABEL(divzero)
|
||||
sdiv r2, r0, r1
|
||||
|
@ -34,8 +34,9 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switch16)
|
||||
ldrh ip, [lr, #-1] // get first 16-bit word in table
|
||||
cmp r0, ip // compare with index
|
||||
add r0, lr, r0, lsl #1 // compute address of element in table
|
||||
ldrshcc r0, [r0, #1] // load 16-bit element if r0 is in range
|
||||
add ip, lr, ip, lsl #1 // compute address of last element in table
|
||||
ite lo
|
||||
ldrshlo r0, [r0, #1] // load 16-bit element if r0 is in range
|
||||
ldrshhs r0, [ip, #1] // load 16-bit element if r0 out of range
|
||||
add ip, lr, r0, lsl #1 // compute label = lr + element*2
|
||||
bx ip // jump to computed label
|
||||
|
@ -34,9 +34,10 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switch32)
|
||||
ldr ip, [lr, #-1] // get first 32-bit word in table
|
||||
cmp r0, ip // compare with index
|
||||
add r0, lr, r0, lsl #2 // compute address of element in table
|
||||
ldrcc r0, [r0, #3] // load 32-bit element if r0 is in range
|
||||
add ip, lr, ip, lsl #2 // compute address of last element in table
|
||||
ldrcs r0, [ip, #3] // load 32-bit element if r0 out of range
|
||||
ite lo
|
||||
ldrlo r0, [r0, #3] // load 32-bit element if r0 is in range
|
||||
ldrhs r0, [ip, #3] // load 32-bit element if r0 out of range
|
||||
add ip, lr, r0 // compute label = lr + element
|
||||
bx ip // jump to computed label
|
||||
|
||||
|
@ -33,7 +33,8 @@
|
||||
DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switch8)
|
||||
ldrb ip, [lr, #-1] // get first byte in table
|
||||
cmp r0, ip // signed compare with index
|
||||
ldrsbcc r0, [lr, r0] // get indexed byte out of table
|
||||
ite lo
|
||||
ldrsblo r0, [lr, r0] // get indexed byte out of table
|
||||
ldrsbhs r0, [lr, ip] // if out of range, use last entry in table
|
||||
add ip, lr, r0, lsl #1 // compute label = lr + element*2
|
||||
bx ip // jump to computed label
|
||||
|
@ -33,7 +33,8 @@
|
||||
DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switchu8)
|
||||
ldrb ip, [lr, #-1] // get first byte in table
|
||||
cmp r0, ip // compare with index
|
||||
ldrbcc r0, [lr, r0] // get indexed byte out of table
|
||||
ite lo
|
||||
ldrblo r0, [lr, r0] // get indexed byte out of table
|
||||
ldrbhs r0, [lr, ip] // if out of range, use last entry in table
|
||||
add ip, lr, r0, lsl #1 // compute label = lr + element*2
|
||||
bx ip // jump to computed label
|
||||
|
@ -31,7 +31,7 @@
|
||||
.syntax unified
|
||||
.align 3
|
||||
DEFINE_COMPILERRT_FUNCTION(__udivmodsi4)
|
||||
#if __ARM_ARCH_7S__
|
||||
#if __ARM_ARCH_EXT_IDIV__
|
||||
tst r1, r1
|
||||
beq LOCAL_LABEL(divzero)
|
||||
mov r3, r0
|
||||
@ -74,14 +74,17 @@ LOCAL_LABEL(mainLoop):
|
||||
// this way, we can merge the two branches which is a substantial win for
|
||||
// such a tight loop on current ARM architectures.
|
||||
subs r, a, b, lsl i
|
||||
itt hs
|
||||
orrhs q, q,one, lsl i
|
||||
movhs a, r
|
||||
it ne
|
||||
subsne i, i, #1
|
||||
bhi LOCAL_LABEL(mainLoop)
|
||||
|
||||
// Do the final test subtraction and update of quotient (i == 0), as it is
|
||||
// not performed in the main loop.
|
||||
subs r, a, b
|
||||
itt hs
|
||||
orrhs q, #1
|
||||
movhs a, r
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
||||
// Ok, APCS and AAPCS agree on 32 bit args, so it's safe to use the same routine.
|
||||
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_uidiv, __udivsi3)
|
||||
DEFINE_COMPILERRT_FUNCTION(__udivsi3)
|
||||
#if __ARM_ARCH_7S__
|
||||
#if __ARM_ARCH_EXT_IDIV__
|
||||
tst r1,r1
|
||||
beq LOCAL_LABEL(divzero)
|
||||
udiv r0, r0, r1
|
||||
@ -73,14 +73,17 @@ LOCAL_LABEL(mainLoop):
|
||||
// this way, we can merge the two branches which is a substantial win for
|
||||
// such a tight loop on current ARM architectures.
|
||||
subs r, a, b, lsl i
|
||||
itt hs
|
||||
orrhs q, q,one, lsl i
|
||||
movhs a, r
|
||||
it ne
|
||||
subsne i, i, #1
|
||||
bhi LOCAL_LABEL(mainLoop)
|
||||
|
||||
// Do the final test subtraction and update of quotient (i == 0), as it is
|
||||
// not performed in the main loop.
|
||||
subs r, a, b
|
||||
it hs
|
||||
orrhs q, #1
|
||||
|
||||
LOCAL_LABEL(return):
|
||||
|
@ -23,7 +23,7 @@
|
||||
.syntax unified
|
||||
.align 3
|
||||
DEFINE_COMPILERRT_FUNCTION(__umodsi3)
|
||||
#if __ARM_ARCH_7S__
|
||||
#if __ARM_ARCH_EXT_IDIV__
|
||||
tst r1, r1
|
||||
beq LOCAL_LABEL(divzero)
|
||||
udiv r2, r0, r1
|
||||
@ -57,13 +57,16 @@ LOCAL_LABEL(mainLoop):
|
||||
// this way, we can merge the two branches which is a substantial win for
|
||||
// such a tight loop on current ARM architectures.
|
||||
subs r, a, b, lsl i
|
||||
it hs
|
||||
movhs a, r
|
||||
it ne
|
||||
subsne i, i, #1
|
||||
bhi LOCAL_LABEL(mainLoop)
|
||||
|
||||
// Do the final test subtraction and update of remainder (i == 0), as it is
|
||||
// not performed in the main loop.
|
||||
subs r, a, b
|
||||
it hs
|
||||
movhs a, r
|
||||
bx lr
|
||||
#endif
|
||||
|
@ -19,18 +19,19 @@ set(ASAN_SOURCES
|
||||
asan_stack.cc
|
||||
asan_stats.cc
|
||||
asan_thread.cc
|
||||
asan_win.cc
|
||||
)
|
||||
|
||||
set(ASAN_DYLIB_SOURCES
|
||||
${ASAN_SOURCES}
|
||||
)
|
||||
asan_win.cc)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
set(ASAN_CFLAGS
|
||||
${SANITIZER_COMMON_CFLAGS}
|
||||
-fno-rtti)
|
||||
if (NOT MSVC)
|
||||
set(ASAN_CFLAGS
|
||||
${SANITIZER_COMMON_CFLAGS}
|
||||
-fno-rtti)
|
||||
else()
|
||||
set(ASAN_CFLAGS
|
||||
${SANITIZER_COMMON_CFLAGS}
|
||||
/GR-)
|
||||
endif()
|
||||
|
||||
set(ASAN_COMMON_DEFINITIONS
|
||||
ASAN_HAS_EXCEPTIONS=1)
|
||||
@ -40,6 +41,10 @@ if(ANDROID)
|
||||
ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
|
||||
ASAN_NEEDS_SEGV=0
|
||||
ASAN_LOW_MEMORY=1)
|
||||
elseif(MSVC)
|
||||
list(APPEND ASAN_COMMON_DEFINITIONS
|
||||
ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
|
||||
ASAN_NEEDS_SEGV=0)
|
||||
else()
|
||||
list(APPEND ASAN_COMMON_DEFINITIONS
|
||||
ASAN_FLEXIBLE_MAPPING_AND_OFFSET=1
|
||||
@ -48,29 +53,55 @@ endif()
|
||||
|
||||
# Architectures supported by ASan.
|
||||
filter_available_targets(ASAN_SUPPORTED_ARCH
|
||||
x86_64 i386 powerpc64 powerpc)
|
||||
x86_64 i386 powerpc64)
|
||||
|
||||
# Compile ASan sources into an object library.
|
||||
if(APPLE)
|
||||
foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS})
|
||||
add_compiler_rt_darwin_object_library(RTAsan ${os}
|
||||
ARCH ${ASAN_SUPPORTED_ARCH}
|
||||
SOURCES ${ASAN_SOURCES}
|
||||
CFLAGS ${ASAN_CFLAGS}
|
||||
DEFS ${ASAN_COMMON_DEFINITIONS})
|
||||
endforeach()
|
||||
elseif(ANDROID)
|
||||
add_library(RTAsan.arm.android OBJECT ${ASAN_SOURCES})
|
||||
set_target_compile_flags(RTAsan.arm.android ${ASAN_CFLAGS})
|
||||
set_property(TARGET RTAsan.arm.android APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS ${ASAN_COMMON_DEFINITIONS})
|
||||
else()
|
||||
foreach(arch ${ASAN_SUPPORTED_ARCH})
|
||||
add_compiler_rt_object_library(RTAsan ${arch}
|
||||
SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_CFLAGS}
|
||||
DEFS ${ASAN_COMMON_DEFINITIONS})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Build ASan runtimes shipped with Clang.
|
||||
set(ASAN_RUNTIME_LIBRARIES)
|
||||
if(APPLE)
|
||||
# Build universal binary on APPLE.
|
||||
add_compiler_rt_osx_dynamic_runtime(clang_rt.asan_osx_dynamic
|
||||
ARCH ${ASAN_SUPPORTED_ARCH}
|
||||
SOURCES ${ASAN_DYLIB_SOURCES}
|
||||
$<TARGET_OBJECTS:RTInterception.osx>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.osx>
|
||||
CFLAGS ${ASAN_CFLAGS}
|
||||
DEFS ${ASAN_COMMON_DEFINITIONS}
|
||||
foreach (os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS})
|
||||
# Dynamic lookup is needed because shadow scale and offset are
|
||||
# provided by the instrumented modules.
|
||||
LINKFLAGS "-framework Foundation"
|
||||
"-undefined dynamic_lookup")
|
||||
list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_osx_dynamic)
|
||||
set(ASAN_RUNTIME_LDFLAGS
|
||||
"-undefined dynamic_lookup")
|
||||
add_compiler_rt_darwin_dynamic_runtime(clang_rt.asan_${os}_dynamic ${os}
|
||||
ARCH ${ASAN_SUPPORTED_ARCH}
|
||||
SOURCES $<TARGET_OBJECTS:RTAsan.${os}>
|
||||
$<TARGET_OBJECTS:RTInterception.${os}>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.${os}>
|
||||
$<TARGET_OBJECTS:RTLSanCommon.${os}>
|
||||
CFLAGS ${ASAN_CFLAGS}
|
||||
DEFS ${ASAN_COMMON_DEFINITIONS}
|
||||
LINKFLAGS ${ASAN_RUNTIME_LDFLAGS})
|
||||
list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_${os}_dynamic)
|
||||
endforeach()
|
||||
|
||||
elseif(ANDROID)
|
||||
add_library(clang_rt.asan-arm-android SHARED
|
||||
${ASAN_SOURCES}
|
||||
$<TARGET_OBJECTS:RTAsan.arm.android>
|
||||
$<TARGET_OBJECTS:RTInterception.arm.android>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.arm.android>
|
||||
)
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.arm.android>)
|
||||
set_target_compile_flags(clang_rt.asan-arm-android
|
||||
${ASAN_CFLAGS})
|
||||
set_property(TARGET clang_rt.asan-arm-android APPEND PROPERTY
|
||||
@ -78,23 +109,44 @@ elseif(ANDROID)
|
||||
target_link_libraries(clang_rt.asan-arm-android dl)
|
||||
list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-arm-android)
|
||||
else()
|
||||
# Otherwise, build separate libraries for each target.
|
||||
# Build separate libraries for each target.
|
||||
foreach(arch ${ASAN_SUPPORTED_ARCH})
|
||||
set(ASAN_RUNTIME_OBJECTS
|
||||
$<TARGET_OBJECTS:RTAsan.${arch}>
|
||||
$<TARGET_OBJECTS:RTInterception.${arch}>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>)
|
||||
if (NOT WIN32)
|
||||
# We can't build Leak Sanitizer on Windows yet.
|
||||
list(APPEND ASAN_RUNTIME_OBJECTS $<TARGET_OBJECTS:RTLSanCommon.${arch}>)
|
||||
endif()
|
||||
|
||||
add_compiler_rt_static_runtime(clang_rt.asan-${arch} ${arch}
|
||||
SOURCES ${ASAN_SOURCES}
|
||||
$<TARGET_OBJECTS:RTInterception.${arch}>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
|
||||
$<TARGET_OBJECTS:RTLSanCommon.${arch}>
|
||||
SOURCES ${ASAN_RUNTIME_OBJECTS}
|
||||
CFLAGS ${ASAN_CFLAGS}
|
||||
DEFS ${ASAN_COMMON_DEFINITIONS}
|
||||
SYMS asan.syms)
|
||||
DEFS ${ASAN_COMMON_DEFINITIONS})
|
||||
list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-${arch})
|
||||
if (UNIX AND NOT ${arch} STREQUAL "i386")
|
||||
add_sanitizer_rt_symbols(clang_rt.asan-${arch} asan.syms.extra)
|
||||
list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-${arch}-symbols)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
add_compiler_rt_static_runtime(clang_rt.asan_dll_thunk-${arch} ${arch}
|
||||
SOURCES asan_dll_thunk.cc
|
||||
CFLAGS ${ASAN_CFLAGS} -DASAN_DLL_THUNK
|
||||
DEFS ${ASAN_COMMON_DEFINITIONS})
|
||||
list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_dll_thunk-${arch})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
add_compiler_rt_resource_file(asan_blacklist asan_blacklist.txt)
|
||||
|
||||
# All ASan runtime dependencies.
|
||||
add_custom_target(asan_runtime_libraries
|
||||
DEPENDS asan_blacklist ${ASAN_RUNTIME_LIBRARIES})
|
||||
|
||||
if(LLVM_INCLUDE_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
__asan_*;
|
||||
__sanitizer_syscall_pre_*;
|
||||
__sanitizer_syscall_post_*;
|
||||
};
|
3
lib/asan/asan.syms.extra
Normal file
3
lib/asan/asan.syms.extra
Normal file
@ -0,0 +1,3 @@
|
||||
__asan_*
|
||||
__lsan_*
|
||||
__ubsan_*
|
@ -35,10 +35,11 @@ void InitializeAllocator();
|
||||
class AsanChunkView {
|
||||
public:
|
||||
explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {}
|
||||
bool IsValid() { return chunk_ != 0; }
|
||||
uptr Beg(); // first byte of user memory.
|
||||
uptr End(); // last byte of user memory.
|
||||
uptr UsedSize(); // size requested by the user.
|
||||
bool IsValid(); // Checks if AsanChunkView points to a valid allocated
|
||||
// or quarantined chunk.
|
||||
uptr Beg(); // First byte of user memory.
|
||||
uptr End(); // Last byte of user memory.
|
||||
uptr UsedSize(); // Size requested by the user.
|
||||
uptr AllocTid();
|
||||
uptr FreeTid();
|
||||
void GetAllocStack(StackTrace *stack);
|
||||
@ -114,7 +115,7 @@ void *asan_pvalloc(uptr size, StackTrace *stack);
|
||||
|
||||
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
|
||||
StackTrace *stack);
|
||||
uptr asan_malloc_usable_size(void *ptr, StackTrace *stack);
|
||||
uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp);
|
||||
|
||||
uptr asan_mz_size(const void *ptr);
|
||||
void asan_mz_force_lock();
|
||||
|
@ -94,7 +94,7 @@ AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) {
|
||||
static Allocator allocator;
|
||||
|
||||
static const uptr kMaxAllowedMallocSize =
|
||||
FIRST_32_SECOND_64(3UL << 30, 8UL << 30);
|
||||
FIRST_32_SECOND_64(3UL << 30, 64UL << 30);
|
||||
|
||||
static const uptr kMaxThreadLocalQuarantine =
|
||||
FIRST_32_SECOND_64(1 << 18, 1 << 20);
|
||||
@ -146,14 +146,15 @@ static uptr ComputeRZLog(uptr user_requested_size) {
|
||||
// ChunkBase consists of ChunkHeader and other bytes that overlap with user
|
||||
// memory.
|
||||
|
||||
// If a memory chunk is allocated by memalign and we had to increase the
|
||||
// allocation size to achieve the proper alignment, then we store this magic
|
||||
// If the left redzone is greater than the ChunkHeader size we store a magic
|
||||
// value in the first uptr word of the memory block and store the address of
|
||||
// ChunkBase in the next uptr.
|
||||
// M B ? ? ? L L L L L L H H U U U U U U
|
||||
// M -- magic value kMemalignMagic
|
||||
// M B L L L L L L L L L H H U U U U U U
|
||||
// | ^
|
||||
// ---------------------|
|
||||
// M -- magic value kAllocBegMagic
|
||||
// B -- address of ChunkHeader pointing to the first 'H'
|
||||
static const uptr kMemalignMagic = 0xCC6E96B9;
|
||||
static const uptr kAllocBegMagic = 0xCC6E96B9;
|
||||
|
||||
struct ChunkHeader {
|
||||
// 1-st 8 bytes.
|
||||
@ -185,14 +186,19 @@ COMPILER_CHECK(kChunkHeader2Size <= 16);
|
||||
|
||||
struct AsanChunk: ChunkBase {
|
||||
uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; }
|
||||
uptr UsedSize() {
|
||||
uptr UsedSize(bool locked_version = false) {
|
||||
if (user_requested_size != SizeClassMap::kMaxSize)
|
||||
return user_requested_size;
|
||||
return *reinterpret_cast<uptr *>(allocator.GetMetaData(AllocBeg()));
|
||||
return *reinterpret_cast<uptr *>(
|
||||
allocator.GetMetaData(AllocBeg(locked_version)));
|
||||
}
|
||||
void *AllocBeg() {
|
||||
if (from_memalign)
|
||||
void *AllocBeg(bool locked_version = false) {
|
||||
if (from_memalign) {
|
||||
if (locked_version)
|
||||
return allocator.GetBlockBeginFastLocked(
|
||||
reinterpret_cast<void *>(this));
|
||||
return allocator.GetBlockBegin(reinterpret_cast<void *>(this));
|
||||
}
|
||||
return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
|
||||
}
|
||||
// If we don't use stack depot, we store the alloc/free stack traces
|
||||
@ -212,11 +218,14 @@ struct AsanChunk: ChunkBase {
|
||||
uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY);
|
||||
return (available - kChunkHeader2Size) / sizeof(u32);
|
||||
}
|
||||
bool AddrIsInside(uptr addr) {
|
||||
return (addr >= Beg()) && (addr < Beg() + UsedSize());
|
||||
bool AddrIsInside(uptr addr, bool locked_version = false) {
|
||||
return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version));
|
||||
}
|
||||
};
|
||||
|
||||
bool AsanChunkView::IsValid() {
|
||||
return chunk_ != 0 && chunk_->chunk_state != CHUNK_AVAILABLE;
|
||||
}
|
||||
uptr AsanChunkView::Beg() { return chunk_->Beg(); }
|
||||
uptr AsanChunkView::End() { return Beg() + UsedSize(); }
|
||||
uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); }
|
||||
@ -227,25 +236,16 @@ static void GetStackTraceFromId(u32 id, StackTrace *stack) {
|
||||
CHECK(id);
|
||||
uptr size = 0;
|
||||
const uptr *trace = StackDepotGet(id, &size);
|
||||
CHECK_LT(size, kStackTraceMax);
|
||||
internal_memcpy(stack->trace, trace, sizeof(uptr) * size);
|
||||
stack->size = size;
|
||||
CHECK(trace);
|
||||
stack->CopyFrom(trace, size);
|
||||
}
|
||||
|
||||
void AsanChunkView::GetAllocStack(StackTrace *stack) {
|
||||
if (flags()->use_stack_depot)
|
||||
GetStackTraceFromId(chunk_->alloc_context_id, stack);
|
||||
else
|
||||
StackTrace::UncompressStack(stack, chunk_->AllocStackBeg(),
|
||||
chunk_->AllocStackSize());
|
||||
GetStackTraceFromId(chunk_->alloc_context_id, stack);
|
||||
}
|
||||
|
||||
void AsanChunkView::GetFreeStack(StackTrace *stack) {
|
||||
if (flags()->use_stack_depot)
|
||||
GetStackTraceFromId(chunk_->free_context_id, stack);
|
||||
else
|
||||
StackTrace::UncompressStack(stack, chunk_->FreeStackBeg(),
|
||||
chunk_->FreeStackSize());
|
||||
GetStackTraceFromId(chunk_->free_context_id, stack);
|
||||
}
|
||||
|
||||
struct QuarantineCallback;
|
||||
@ -276,10 +276,13 @@ struct QuarantineCallback {
|
||||
RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
|
||||
kAsanHeapLeftRedzoneMagic);
|
||||
void *p = reinterpret_cast<void *>(m->AllocBeg());
|
||||
if (m->from_memalign) {
|
||||
uptr *memalign_magic = reinterpret_cast<uptr *>(p);
|
||||
CHECK_EQ(memalign_magic[0], kMemalignMagic);
|
||||
CHECK_EQ(memalign_magic[1], reinterpret_cast<uptr>(m));
|
||||
if (p != m) {
|
||||
uptr *alloc_magic = reinterpret_cast<uptr *>(p);
|
||||
CHECK_EQ(alloc_magic[0], kAllocBegMagic);
|
||||
// Clear the magic value, as allocator internals may overwrite the
|
||||
// contents of deallocated chunk, confusing GetAsanChunk lookup.
|
||||
alloc_magic[0] = 0;
|
||||
CHECK_EQ(alloc_magic[1], reinterpret_cast<uptr>(m));
|
||||
}
|
||||
|
||||
// Statistics.
|
||||
@ -341,7 +344,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
|
||||
if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
|
||||
Report("WARNING: AddressSanitizer failed to allocate %p bytes\n",
|
||||
(void*)size);
|
||||
return 0;
|
||||
return AllocatorReturnNull();
|
||||
}
|
||||
|
||||
AsanThread *t = GetCurrentThread();
|
||||
@ -355,8 +358,6 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
|
||||
allocated = allocator.Allocate(cache, needed_size, 8, false);
|
||||
}
|
||||
uptr alloc_beg = reinterpret_cast<uptr>(allocated);
|
||||
// Clear the first allocated word (an old kMemalignMagic may still be there).
|
||||
reinterpret_cast<uptr *>(alloc_beg)[0] = 0;
|
||||
uptr alloc_end = alloc_beg + needed_size;
|
||||
uptr beg_plus_redzone = alloc_beg + rz_size;
|
||||
uptr user_beg = beg_plus_redzone;
|
||||
@ -373,11 +374,10 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
|
||||
CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield?
|
||||
m->free_tid = kInvalidTid;
|
||||
m->from_memalign = user_beg != beg_plus_redzone;
|
||||
if (m->from_memalign) {
|
||||
CHECK_LE(beg_plus_redzone + 2 * sizeof(uptr), user_beg);
|
||||
uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg);
|
||||
memalign_magic[0] = kMemalignMagic;
|
||||
memalign_magic[1] = chunk_beg;
|
||||
if (alloc_beg != chunk_beg) {
|
||||
CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg);
|
||||
reinterpret_cast<uptr *>(alloc_beg)[0] = kAllocBegMagic;
|
||||
reinterpret_cast<uptr *>(alloc_beg)[1] = chunk_beg;
|
||||
}
|
||||
if (using_primary_allocator) {
|
||||
CHECK(size);
|
||||
@ -391,12 +391,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
|
||||
meta[1] = chunk_beg;
|
||||
}
|
||||
|
||||
if (fl.use_stack_depot) {
|
||||
m->alloc_context_id = StackDepotPut(stack->trace, stack->size);
|
||||
} else {
|
||||
m->alloc_context_id = 0;
|
||||
StackTrace::CompressStack(stack, m->AllocStackBeg(), m->AllocStackSize());
|
||||
}
|
||||
m->alloc_context_id = StackDepotPut(stack->trace, stack->size);
|
||||
|
||||
uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY);
|
||||
// Unpoison the bulk of the memory region.
|
||||
@ -405,7 +400,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
|
||||
// Deal with the end of the region if size is not aligned to granularity.
|
||||
if (size != size_rounded_down_to_granularity && fl.poison_heap) {
|
||||
u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity);
|
||||
*shadow = size & (SHADOW_GRANULARITY - 1);
|
||||
*shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0;
|
||||
}
|
||||
|
||||
AsanStats &thread_stats = GetCurrentThreadStats();
|
||||
@ -422,23 +417,30 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
|
||||
uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size);
|
||||
REAL(memset)(res, fl.malloc_fill_byte, fill_size);
|
||||
}
|
||||
#if CAN_SANITIZE_LEAKS
|
||||
m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored
|
||||
: __lsan::kDirectlyLeaked;
|
||||
#endif
|
||||
// Must be the last mutation of metadata in this function.
|
||||
atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release);
|
||||
ASAN_MALLOC_HOOK(res, size);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void ReportInvalidFree(void *ptr, u8 chunk_state, StackTrace *stack) {
|
||||
if (chunk_state == CHUNK_QUARANTINE)
|
||||
ReportDoubleFree((uptr)ptr, stack);
|
||||
else
|
||||
ReportFreeNotMalloced((uptr)ptr, stack);
|
||||
}
|
||||
|
||||
static void AtomicallySetQuarantineFlag(AsanChunk *m,
|
||||
void *ptr, StackTrace *stack) {
|
||||
u8 old_chunk_state = CHUNK_ALLOCATED;
|
||||
// Flip the chunk_state atomically to avoid race on double-free.
|
||||
if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state,
|
||||
CHUNK_QUARANTINE, memory_order_acquire)) {
|
||||
if (old_chunk_state == CHUNK_QUARANTINE)
|
||||
ReportDoubleFree((uptr)ptr, stack);
|
||||
else
|
||||
ReportFreeNotMalloced((uptr)ptr, stack);
|
||||
}
|
||||
CHUNK_QUARANTINE, memory_order_acquire))
|
||||
ReportInvalidFree(ptr, old_chunk_state, stack);
|
||||
CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state);
|
||||
}
|
||||
|
||||
@ -448,12 +450,6 @@ static void QuarantineChunk(AsanChunk *m, void *ptr,
|
||||
StackTrace *stack, AllocType alloc_type) {
|
||||
CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
|
||||
|
||||
// FIXME: if the free hook produces an ASan report (e.g. due to a bug),
|
||||
// printing the report may crash as the AsanChunk free-related fields have not
|
||||
// been updated yet. We might need to introduce yet another chunk state to
|
||||
// handle this correctly, but don't want to yet.
|
||||
ASAN_FREE_HOOK(ptr);
|
||||
|
||||
if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch)
|
||||
ReportAllocTypeMismatch((uptr)ptr, stack,
|
||||
(AllocType)m->alloc_type, (AllocType)alloc_type);
|
||||
@ -463,12 +459,7 @@ static void QuarantineChunk(AsanChunk *m, void *ptr,
|
||||
CHECK_EQ(m->free_tid, kInvalidTid);
|
||||
AsanThread *t = GetCurrentThread();
|
||||
m->free_tid = t ? t->tid() : 0;
|
||||
if (flags()->use_stack_depot) {
|
||||
m->free_context_id = StackDepotPut(stack->trace, stack->size);
|
||||
} else {
|
||||
m->free_context_id = 0;
|
||||
StackTrace::CompressStack(stack, m->FreeStackBeg(), m->FreeStackSize());
|
||||
}
|
||||
m->free_context_id = StackDepotPut(stack->trace, stack->size);
|
||||
// Poison the region.
|
||||
PoisonShadow(m->Beg(),
|
||||
RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
|
||||
@ -498,6 +489,7 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
|
||||
|
||||
uptr chunk_beg = p - kChunkHeaderSize;
|
||||
AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
|
||||
ASAN_FREE_HOOK(ptr);
|
||||
// Must mark the chunk as quarantined before any changes to its metadata.
|
||||
AtomicallySetQuarantineFlag(m, ptr, stack);
|
||||
QuarantineChunk(m, ptr, stack, alloc_type);
|
||||
@ -513,50 +505,45 @@ static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) {
|
||||
thread_stats.reallocs++;
|
||||
thread_stats.realloced += new_size;
|
||||
|
||||
// Must mark the chunk as quarantined before any changes to its metadata.
|
||||
// This also ensures that other threads can't deallocate it in the meantime.
|
||||
AtomicallySetQuarantineFlag(m, old_ptr, stack);
|
||||
|
||||
uptr old_size = m->UsedSize();
|
||||
uptr memcpy_size = Min(new_size, old_size);
|
||||
void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true);
|
||||
if (new_ptr) {
|
||||
u8 chunk_state = m->chunk_state;
|
||||
if (chunk_state != CHUNK_ALLOCATED)
|
||||
ReportInvalidFree(old_ptr, chunk_state, stack);
|
||||
CHECK_NE(REAL(memcpy), (void*)0);
|
||||
uptr memcpy_size = Min(new_size, m->UsedSize());
|
||||
// If realloc() races with free(), we may start copying freed memory.
|
||||
// However, we will report racy double-free later anyway.
|
||||
REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
|
||||
QuarantineChunk(m, old_ptr, stack, FROM_MALLOC);
|
||||
Deallocate(old_ptr, stack, FROM_MALLOC);
|
||||
}
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
static AsanChunk *GetAsanChunkByAddr(uptr p) {
|
||||
void *ptr = reinterpret_cast<void *>(p);
|
||||
uptr alloc_beg = reinterpret_cast<uptr>(allocator.GetBlockBegin(ptr));
|
||||
// Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg).
|
||||
static AsanChunk *GetAsanChunk(void *alloc_beg) {
|
||||
if (!alloc_beg) return 0;
|
||||
uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg);
|
||||
if (memalign_magic[0] == kMemalignMagic) {
|
||||
AsanChunk *m = reinterpret_cast<AsanChunk *>(memalign_magic[1]);
|
||||
CHECK(m->from_memalign);
|
||||
return m;
|
||||
}
|
||||
if (!allocator.FromPrimary(ptr)) {
|
||||
uptr *meta = reinterpret_cast<uptr *>(
|
||||
allocator.GetMetaData(reinterpret_cast<void *>(alloc_beg)));
|
||||
if (!allocator.FromPrimary(alloc_beg)) {
|
||||
uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(alloc_beg));
|
||||
AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]);
|
||||
return m;
|
||||
}
|
||||
uptr actual_size = allocator.GetActuallyAllocatedSize(ptr);
|
||||
CHECK_LE(actual_size, SizeClassMap::kMaxSize);
|
||||
// We know the actually allocted size, but we don't know the redzone size.
|
||||
// Just try all possible redzone sizes.
|
||||
for (u32 rz_log = 0; rz_log < 8; rz_log++) {
|
||||
u32 rz_size = RZLog2Size(rz_log);
|
||||
uptr max_possible_size = actual_size - rz_size;
|
||||
if (ComputeRZLog(max_possible_size) != rz_log)
|
||||
continue;
|
||||
return reinterpret_cast<AsanChunk *>(
|
||||
alloc_beg + rz_size - kChunkHeaderSize);
|
||||
}
|
||||
return 0;
|
||||
uptr *alloc_magic = reinterpret_cast<uptr *>(alloc_beg);
|
||||
if (alloc_magic[0] == kAllocBegMagic)
|
||||
return reinterpret_cast<AsanChunk *>(alloc_magic[1]);
|
||||
return reinterpret_cast<AsanChunk *>(alloc_beg);
|
||||
}
|
||||
|
||||
static AsanChunk *GetAsanChunkByAddr(uptr p) {
|
||||
void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast<void *>(p));
|
||||
return GetAsanChunk(alloc_beg);
|
||||
}
|
||||
|
||||
// Allocator must be locked when this function is called.
|
||||
static AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) {
|
||||
void *alloc_beg =
|
||||
allocator.GetBlockBeginFastLocked(reinterpret_cast<void *>(p));
|
||||
return GetAsanChunk(alloc_beg);
|
||||
}
|
||||
|
||||
static uptr AllocationSize(uptr p) {
|
||||
@ -621,24 +608,22 @@ void PrintInternalAllocatorStats() {
|
||||
allocator.PrintStats();
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
|
||||
AllocType alloc_type) {
|
||||
return Allocate(size, alignment, stack, alloc_type, true);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
|
||||
Deallocate(ptr, stack, alloc_type);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *asan_malloc(uptr size, StackTrace *stack) {
|
||||
return Allocate(size, 8, stack, FROM_MALLOC, true);
|
||||
}
|
||||
|
||||
void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
|
||||
if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0;
|
||||
if (CallocShouldReturnNullDueToOverflow(size, nmemb))
|
||||
return AllocatorReturnNull();
|
||||
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.
|
||||
@ -679,12 +664,13 @@ int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) {
|
||||
CHECK(stack);
|
||||
uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) {
|
||||
if (ptr == 0) return 0;
|
||||
uptr usable_size = AllocationSize(reinterpret_cast<uptr>(ptr));
|
||||
if (flags()->check_malloc_usable_size && (usable_size == 0))
|
||||
ReportMallocUsableSizeNotOwned((uptr)ptr, stack);
|
||||
if (flags()->check_malloc_usable_size && (usable_size == 0)) {
|
||||
GET_STACK_TRACE_FATAL(pc, bp);
|
||||
ReportMallocUsableSizeNotOwned((uptr)ptr, &stack);
|
||||
}
|
||||
return usable_size;
|
||||
}
|
||||
|
||||
@ -719,25 +705,26 @@ void GetAllocatorGlobalRange(uptr *begin, uptr *end) {
|
||||
*end = *begin + sizeof(__asan::allocator);
|
||||
}
|
||||
|
||||
void *PointsIntoChunk(void* p) {
|
||||
uptr PointsIntoChunk(void* p) {
|
||||
uptr addr = reinterpret_cast<uptr>(p);
|
||||
__asan::AsanChunk *m = __asan::GetAsanChunkByAddr(addr);
|
||||
__asan::AsanChunk *m = __asan::GetAsanChunkByAddrFastLocked(addr);
|
||||
if (!m) return 0;
|
||||
uptr chunk = m->Beg();
|
||||
if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr))
|
||||
return reinterpret_cast<void *>(chunk);
|
||||
if ((m->chunk_state == __asan::CHUNK_ALLOCATED) &&
|
||||
m->AddrIsInside(addr, /*locked_version=*/true))
|
||||
return chunk;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *GetUserBegin(void *p) {
|
||||
__asan::AsanChunk *m = __asan::GetAsanChunkByAddr(reinterpret_cast<uptr>(p));
|
||||
uptr GetUserBegin(uptr chunk) {
|
||||
__asan::AsanChunk *m =
|
||||
__asan::GetAsanChunkByAddrFastLocked(chunk);
|
||||
CHECK(m);
|
||||
return reinterpret_cast<void *>(m->Beg());
|
||||
return m->Beg();
|
||||
}
|
||||
|
||||
LsanMetadata::LsanMetadata(void *chunk) {
|
||||
uptr addr = reinterpret_cast<uptr>(chunk);
|
||||
metadata_ = reinterpret_cast<void *>(addr - __asan::kChunkHeaderSize);
|
||||
LsanMetadata::LsanMetadata(uptr chunk) {
|
||||
metadata_ = reinterpret_cast<void *>(chunk - __asan::kChunkHeaderSize);
|
||||
}
|
||||
|
||||
bool LsanMetadata::allocated() const {
|
||||
@ -757,7 +744,7 @@ void LsanMetadata::set_tag(ChunkTag value) {
|
||||
|
||||
uptr LsanMetadata::requested_size() const {
|
||||
__asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
|
||||
return m->UsedSize();
|
||||
return m->UsedSize(/*locked_version=*/true);
|
||||
}
|
||||
|
||||
u32 LsanMetadata::stack_trace_id() const {
|
||||
@ -765,18 +752,23 @@ u32 LsanMetadata::stack_trace_id() const {
|
||||
return m->alloc_context_id;
|
||||
}
|
||||
|
||||
template <typename Callable> void ForEachChunk(Callable const &callback) {
|
||||
__asan::allocator.ForEachChunk(callback);
|
||||
void ForEachChunk(ForEachChunkCallback callback, void *arg) {
|
||||
__asan::allocator.ForEachChunk(callback, arg);
|
||||
}
|
||||
|
||||
IgnoreObjectResult IgnoreObjectLocked(const void *p) {
|
||||
uptr addr = reinterpret_cast<uptr>(p);
|
||||
__asan::AsanChunk *m = __asan::GetAsanChunkByAddr(addr);
|
||||
if (!m) return kIgnoreObjectInvalid;
|
||||
if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) {
|
||||
if (m->lsan_tag == kIgnored)
|
||||
return kIgnoreObjectAlreadyIgnored;
|
||||
m->lsan_tag = __lsan::kIgnored;
|
||||
return kIgnoreObjectSuccess;
|
||||
} else {
|
||||
return kIgnoreObjectInvalid;
|
||||
}
|
||||
}
|
||||
#if CAN_SANITIZE_LEAKS
|
||||
template void ForEachChunk<ProcessPlatformSpecificAllocationsCb>(
|
||||
ProcessPlatformSpecificAllocationsCb const &callback);
|
||||
template void ForEachChunk<PrintLeakedCb>(PrintLeakedCb const &callback);
|
||||
template void ForEachChunk<CollectLeaksCb>(CollectLeaksCb const &callback);
|
||||
template void ForEachChunk<MarkIndirectlyLeakedCb>(
|
||||
MarkIndirectlyLeakedCb const &callback);
|
||||
template void ForEachChunk<ClearTagCb>(ClearTagCb const &callback);
|
||||
#endif // CAN_SANITIZE_LEAKS
|
||||
} // namespace __lsan
|
||||
|
||||
// ---------------------- Interface ---------------- {{{1
|
||||
@ -808,12 +800,12 @@ uptr __asan_get_allocated_size(const void *p) {
|
||||
#if !SANITIZER_SUPPORTS_WEAK_HOOKS
|
||||
// Provide default (no-op) implementation of malloc hooks.
|
||||
extern "C" {
|
||||
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __asan_malloc_hook(void *ptr, uptr size) {
|
||||
(void)ptr;
|
||||
(void)size;
|
||||
}
|
||||
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __asan_free_hook(void *ptr) {
|
||||
(void)ptr;
|
||||
}
|
||||
|
@ -6,5 +6,5 @@
|
||||
# fun:*bad_function_name*
|
||||
# src:file_with_tricky_code.cc
|
||||
# global:*global_with_bad_access_or_initialization*
|
||||
# global-init:*global_with_initialization_issues*
|
||||
# global-init-type:*Namespace::ClassName*
|
||||
# global:*global_with_initialization_issues*=init
|
||||
# type:*Namespace::ClassName*=init
|
||||
|
196
lib/asan/asan_dll_thunk.cc
Normal file
196
lib/asan/asan_dll_thunk.cc
Normal file
@ -0,0 +1,196 @@
|
||||
//===-- asan_dll_thunk.cc -------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// This file defines a family of thunks that should be statically linked into
|
||||
// the DLLs that have ASan instrumentation in order to delegate the calls to the
|
||||
// shared runtime that lives in the main binary.
|
||||
// See https://code.google.com/p/address-sanitizer/issues/detail?id=209 for the
|
||||
// details.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Only compile this code when buidling asan_dll_thunk.lib
|
||||
// Using #ifdef rather than relying on Makefiles etc.
|
||||
// simplifies the build procedure.
|
||||
#ifdef ASAN_DLL_THUNK
|
||||
|
||||
// ----------------- Helper functions and macros --------------------- {{{1
|
||||
extern "C" {
|
||||
void *__stdcall GetModuleHandleA(const char *module_name);
|
||||
void *__stdcall GetProcAddress(void *module, const char *proc_name);
|
||||
void abort();
|
||||
}
|
||||
|
||||
static void *getRealProcAddressOrDie(const char *name) {
|
||||
void *ret = GetProcAddress(GetModuleHandleA(0), name);
|
||||
if (!ret)
|
||||
abort();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define WRAP_V_V(name) \
|
||||
extern "C" void name() { \
|
||||
typedef void (*fntype)(); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
fn(); \
|
||||
}
|
||||
|
||||
#define WRAP_V_W(name) \
|
||||
extern "C" void name(void *arg) { \
|
||||
typedef void (*fntype)(void *arg); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
fn(arg); \
|
||||
}
|
||||
|
||||
#define WRAP_V_WW(name) \
|
||||
extern "C" void name(void *arg1, void *arg2) { \
|
||||
typedef void (*fntype)(void *, void *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
fn(arg1, arg2); \
|
||||
}
|
||||
|
||||
#define WRAP_V_WWW(name) \
|
||||
extern "C" void name(void *arg1, void *arg2, void *arg3) { \
|
||||
typedef void *(*fntype)(void *, void *, void *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
fn(arg1, arg2, arg3); \
|
||||
}
|
||||
|
||||
#define WRAP_W_V(name) \
|
||||
extern "C" void *name() { \
|
||||
typedef void *(*fntype)(); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(); \
|
||||
}
|
||||
|
||||
#define WRAP_W_W(name) \
|
||||
extern "C" void *name(void *arg) { \
|
||||
typedef void *(*fntype)(void *arg); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg); \
|
||||
}
|
||||
|
||||
#define WRAP_W_WW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2) { \
|
||||
typedef void *(*fntype)(void *, void *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2); \
|
||||
}
|
||||
|
||||
#define WRAP_W_WWW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2, void *arg3) { \
|
||||
typedef void *(*fntype)(void *, void *, void *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3); \
|
||||
}
|
||||
|
||||
#define WRAP_W_WWWW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \
|
||||
typedef void *(*fntype)(void *, void *, void *, void *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3, arg4); \
|
||||
}
|
||||
|
||||
#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 *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3, arg4, arg5); \
|
||||
}
|
||||
|
||||
#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 *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3, arg4, arg5, arg6); \
|
||||
}
|
||||
// }}}
|
||||
|
||||
// ----------------- ASan own interface functions --------------------
|
||||
WRAP_W_V(__asan_should_detect_stack_use_after_return)
|
||||
|
||||
extern "C" {
|
||||
int __asan_option_detect_stack_use_after_return;
|
||||
|
||||
// Manually wrap __asan_init as we need to initialize
|
||||
// __asan_option_detect_stack_use_after_return afterwards.
|
||||
void __asan_init_v3() {
|
||||
typedef void (*fntype)();
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie("__asan_init_v3");
|
||||
fn();
|
||||
__asan_option_detect_stack_use_after_return =
|
||||
(__asan_should_detect_stack_use_after_return() != 0);
|
||||
}
|
||||
}
|
||||
|
||||
WRAP_V_W(__asan_report_store1)
|
||||
WRAP_V_W(__asan_report_store2)
|
||||
WRAP_V_W(__asan_report_store4)
|
||||
WRAP_V_W(__asan_report_store8)
|
||||
WRAP_V_W(__asan_report_store16)
|
||||
WRAP_V_WW(__asan_report_store_n)
|
||||
|
||||
WRAP_V_W(__asan_report_load1)
|
||||
WRAP_V_W(__asan_report_load2)
|
||||
WRAP_V_W(__asan_report_load4)
|
||||
WRAP_V_W(__asan_report_load8)
|
||||
WRAP_V_W(__asan_report_load16)
|
||||
WRAP_V_WW(__asan_report_load_n)
|
||||
|
||||
WRAP_V_WW(__asan_register_globals)
|
||||
WRAP_V_WW(__asan_unregister_globals)
|
||||
|
||||
WRAP_W_WW(__asan_stack_malloc_0)
|
||||
WRAP_W_WW(__asan_stack_malloc_1)
|
||||
WRAP_W_WW(__asan_stack_malloc_2)
|
||||
WRAP_W_WW(__asan_stack_malloc_3)
|
||||
WRAP_W_WW(__asan_stack_malloc_4)
|
||||
WRAP_W_WW(__asan_stack_malloc_5)
|
||||
WRAP_W_WW(__asan_stack_malloc_6)
|
||||
WRAP_W_WW(__asan_stack_malloc_7)
|
||||
WRAP_W_WW(__asan_stack_malloc_8)
|
||||
WRAP_W_WW(__asan_stack_malloc_9)
|
||||
WRAP_W_WW(__asan_stack_malloc_10)
|
||||
|
||||
WRAP_V_WWW(__asan_stack_free_0)
|
||||
WRAP_V_WWW(__asan_stack_free_1)
|
||||
WRAP_V_WWW(__asan_stack_free_2)
|
||||
WRAP_V_WWW(__asan_stack_free_4)
|
||||
WRAP_V_WWW(__asan_stack_free_5)
|
||||
WRAP_V_WWW(__asan_stack_free_6)
|
||||
WRAP_V_WWW(__asan_stack_free_7)
|
||||
WRAP_V_WWW(__asan_stack_free_8)
|
||||
WRAP_V_WWW(__asan_stack_free_9)
|
||||
WRAP_V_WWW(__asan_stack_free_10)
|
||||
|
||||
// TODO(timurrrr): Add more interface functions on the as-needed basis.
|
||||
|
||||
// ----------------- Memory allocation functions ---------------------
|
||||
WRAP_V_W(free)
|
||||
WRAP_V_WW(_free_dbg)
|
||||
|
||||
WRAP_W_W(malloc)
|
||||
WRAP_W_WWWW(_malloc_dbg)
|
||||
|
||||
WRAP_W_WW(calloc)
|
||||
WRAP_W_WWWWW(_calloc_dbg)
|
||||
WRAP_W_WWW(_calloc_impl)
|
||||
|
||||
WRAP_W_WW(realloc)
|
||||
WRAP_W_WWW(_realloc_dbg)
|
||||
WRAP_W_WWW(_recalloc)
|
||||
|
||||
WRAP_W_W(_msize)
|
||||
|
||||
// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc).
|
||||
|
||||
#endif // ASAN_DLL_THUNK
|
@ -17,167 +17,204 @@
|
||||
|
||||
namespace __asan {
|
||||
|
||||
FakeStack::FakeStack() {
|
||||
CHECK(REAL(memset));
|
||||
REAL(memset)(this, 0, sizeof(*this));
|
||||
}
|
||||
static const u64 kMagic1 = kAsanStackAfterReturnMagic;
|
||||
static const u64 kMagic2 = (kMagic1 << 8) | kMagic1;
|
||||
static const u64 kMagic4 = (kMagic2 << 16) | kMagic2;
|
||||
static const u64 kMagic8 = (kMagic4 << 32) | kMagic4;
|
||||
|
||||
bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) {
|
||||
uptr mem = allocated_size_classes_[size_class];
|
||||
uptr size = ClassMmapSize(size_class);
|
||||
bool res = mem && addr >= mem && addr < mem + size;
|
||||
return res;
|
||||
}
|
||||
|
||||
uptr FakeStack::AddrIsInFakeStack(uptr addr) {
|
||||
for (uptr size_class = 0; size_class < kNumberOfSizeClasses; size_class++) {
|
||||
if (!AddrIsInSizeClass(addr, size_class)) continue;
|
||||
uptr size_class_first_ptr = allocated_size_classes_[size_class];
|
||||
uptr size = ClassSize(size_class);
|
||||
CHECK_LE(size_class_first_ptr, addr);
|
||||
CHECK_GT(size_class_first_ptr + ClassMmapSize(size_class), addr);
|
||||
return size_class_first_ptr + ((addr - size_class_first_ptr) / size) * size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We may want to compute this during compilation.
|
||||
ALWAYS_INLINE uptr FakeStack::ComputeSizeClass(uptr alloc_size) {
|
||||
uptr rounded_size = RoundUpToPowerOfTwo(alloc_size);
|
||||
uptr log = Log2(rounded_size);
|
||||
CHECK_LE(alloc_size, (1UL << log));
|
||||
CHECK_GT(alloc_size, (1UL << (log-1)));
|
||||
uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog;
|
||||
CHECK_LT(res, kNumberOfSizeClasses);
|
||||
CHECK_GE(ClassSize(res), rounded_size);
|
||||
return res;
|
||||
}
|
||||
|
||||
void FakeFrameFifo::FifoPush(FakeFrame *node) {
|
||||
CHECK(node);
|
||||
node->next = 0;
|
||||
if (first_ == 0 && last_ == 0) {
|
||||
first_ = last_ = node;
|
||||
// For small size classes inline PoisonShadow for better performance.
|
||||
ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) {
|
||||
CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3.
|
||||
u64 *shadow = reinterpret_cast<u64*>(MemToShadow(ptr));
|
||||
if (class_id <= 6) {
|
||||
for (uptr i = 0; i < (1U << class_id); i++)
|
||||
shadow[i] = magic;
|
||||
} else {
|
||||
CHECK(first_);
|
||||
CHECK(last_);
|
||||
last_->next = node;
|
||||
last_ = node;
|
||||
// The size class is too big, it's cheaper to poison only size bytes.
|
||||
PoisonShadow(ptr, size, static_cast<u8>(magic));
|
||||
}
|
||||
}
|
||||
|
||||
FakeFrame *FakeFrameFifo::FifoPop() {
|
||||
CHECK(first_ && last_ && "Exhausted fake stack");
|
||||
FakeFrame *res = 0;
|
||||
if (first_ == last_) {
|
||||
res = first_;
|
||||
first_ = last_ = 0;
|
||||
} else {
|
||||
res = first_;
|
||||
first_ = first_->next;
|
||||
FakeStack *FakeStack::Create(uptr stack_size_log) {
|
||||
static uptr kMinStackSizeLog = 16;
|
||||
static uptr kMaxStackSizeLog = FIRST_32_SECOND_64(24, 28);
|
||||
if (stack_size_log < kMinStackSizeLog)
|
||||
stack_size_log = kMinStackSizeLog;
|
||||
if (stack_size_log > kMaxStackSizeLog)
|
||||
stack_size_log = kMaxStackSizeLog;
|
||||
FakeStack *res = reinterpret_cast<FakeStack *>(
|
||||
MmapOrDie(RequiredSize(stack_size_log), "FakeStack"));
|
||||
res->stack_size_log_ = stack_size_log;
|
||||
if (common_flags()->verbosity) {
|
||||
u8 *p = reinterpret_cast<u8 *>(res);
|
||||
Report("T%d: FakeStack created: %p -- %p stack_size_log: %zd \n",
|
||||
GetCurrentTidOrInvalid(), p,
|
||||
p + FakeStack::RequiredSize(stack_size_log), stack_size_log);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void FakeStack::Init(uptr stack_size) {
|
||||
stack_size_ = stack_size;
|
||||
alive_ = true;
|
||||
void FakeStack::Destroy() {
|
||||
PoisonAll(0);
|
||||
UnmapOrDie(this, RequiredSize(stack_size_log_));
|
||||
}
|
||||
|
||||
void FakeStack::Cleanup() {
|
||||
alive_ = false;
|
||||
for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
|
||||
uptr mem = allocated_size_classes_[i];
|
||||
if (mem) {
|
||||
PoisonShadow(mem, ClassMmapSize(i), 0);
|
||||
allocated_size_classes_[i] = 0;
|
||||
UnmapOrDie((void*)mem, ClassMmapSize(i));
|
||||
void FakeStack::PoisonAll(u8 magic) {
|
||||
PoisonShadow(reinterpret_cast<uptr>(this), RequiredSize(stack_size_log()),
|
||||
magic);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE USED
|
||||
FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id,
|
||||
uptr real_stack) {
|
||||
CHECK_LT(class_id, kNumberOfSizeClasses);
|
||||
if (needs_gc_)
|
||||
GC(real_stack);
|
||||
uptr &hint_position = hint_position_[class_id];
|
||||
const int num_iter = NumberOfFrames(stack_size_log, class_id);
|
||||
u8 *flags = GetFlags(stack_size_log, class_id);
|
||||
for (int i = 0; i < num_iter; i++) {
|
||||
uptr pos = ModuloNumberOfFrames(stack_size_log, class_id, hint_position++);
|
||||
// This part is tricky. On one hand, checking and setting flags[pos]
|
||||
// should be atomic to ensure async-signal safety. But on the other hand,
|
||||
// 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
|
||||
// this code crash).
|
||||
if (flags[pos]) continue;
|
||||
flags[pos] = 1;
|
||||
FakeFrame *res = reinterpret_cast<FakeFrame *>(
|
||||
GetFrame(stack_size_log, class_id, pos));
|
||||
res->real_stack = real_stack;
|
||||
*SavedFlagPtr(reinterpret_cast<uptr>(res), class_id) = &flags[pos];
|
||||
return res;
|
||||
}
|
||||
return 0; // We are out of fake stack.
|
||||
}
|
||||
|
||||
uptr FakeStack::AddrIsInFakeStack(uptr ptr) {
|
||||
uptr stack_size_log = this->stack_size_log();
|
||||
uptr beg = reinterpret_cast<uptr>(GetFrame(stack_size_log, 0, 0));
|
||||
uptr end = reinterpret_cast<uptr>(this) + RequiredSize(stack_size_log);
|
||||
if (ptr < beg || ptr >= end) return 0;
|
||||
uptr class_id = (ptr - beg) >> stack_size_log;
|
||||
uptr base = beg + (class_id << stack_size_log);
|
||||
CHECK_LE(base, ptr);
|
||||
CHECK_LT(ptr, base + (1UL << stack_size_log));
|
||||
uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id);
|
||||
return base + pos * BytesInSizeClass(class_id);
|
||||
}
|
||||
|
||||
void FakeStack::HandleNoReturn() {
|
||||
needs_gc_ = true;
|
||||
}
|
||||
|
||||
// When throw, longjmp or some such happens we don't call OnFree() and
|
||||
// as the result may leak one or more fake frames, but the good news is that
|
||||
// we are notified about all such events by HandleNoReturn().
|
||||
// If we recently had such no-return event we need to collect garbage frames.
|
||||
// We do it based on their 'real_stack' values -- everything that is lower
|
||||
// than the current real_stack is garbage.
|
||||
NOINLINE void FakeStack::GC(uptr real_stack) {
|
||||
uptr collected = 0;
|
||||
for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) {
|
||||
u8 *flags = GetFlags(stack_size_log(), class_id);
|
||||
for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n;
|
||||
i++) {
|
||||
if (flags[i] == 0) continue; // not allocated.
|
||||
FakeFrame *ff = reinterpret_cast<FakeFrame *>(
|
||||
GetFrame(stack_size_log(), class_id, i));
|
||||
if (ff->real_stack < real_stack) {
|
||||
flags[i] = 0;
|
||||
collected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
needs_gc_ = false;
|
||||
}
|
||||
|
||||
void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void *arg) {
|
||||
for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) {
|
||||
u8 *flags = GetFlags(stack_size_log(), class_id);
|
||||
for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n;
|
||||
i++) {
|
||||
if (flags[i] == 0) continue; // not allocated.
|
||||
FakeFrame *ff = reinterpret_cast<FakeFrame *>(
|
||||
GetFrame(stack_size_log(), class_id, i));
|
||||
uptr begin = reinterpret_cast<uptr>(ff);
|
||||
callback(begin, begin + FakeStack::BytesInSizeClass(class_id), arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uptr FakeStack::ClassMmapSize(uptr size_class) {
|
||||
return RoundUpToPowerOfTwo(stack_size_);
|
||||
#if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
static THREADLOCAL FakeStack *fake_stack_tls;
|
||||
|
||||
FakeStack *GetTLSFakeStack() {
|
||||
return fake_stack_tls;
|
||||
}
|
||||
void SetTLSFakeStack(FakeStack *fs) {
|
||||
fake_stack_tls = fs;
|
||||
}
|
||||
#else
|
||||
FakeStack *GetTLSFakeStack() { return 0; }
|
||||
void SetTLSFakeStack(FakeStack *fs) { }
|
||||
#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
|
||||
static FakeStack *GetFakeStack() {
|
||||
AsanThread *t = GetCurrentThread();
|
||||
if (!t) return 0;
|
||||
return t->fake_stack();
|
||||
}
|
||||
|
||||
void FakeStack::AllocateOneSizeClass(uptr size_class) {
|
||||
CHECK(ClassMmapSize(size_class) >= GetPageSizeCached());
|
||||
uptr new_mem = (uptr)MmapOrDie(
|
||||
ClassMmapSize(size_class), __FUNCTION__);
|
||||
// Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n",
|
||||
// GetCurrentThread()->tid(),
|
||||
// size_class, new_mem, new_mem + ClassMmapSize(size_class),
|
||||
// ClassMmapSize(size_class));
|
||||
uptr i;
|
||||
for (i = 0; i < ClassMmapSize(size_class);
|
||||
i += ClassSize(size_class)) {
|
||||
size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i));
|
||||
}
|
||||
CHECK(i == ClassMmapSize(size_class));
|
||||
allocated_size_classes_[size_class] = new_mem;
|
||||
static FakeStack *GetFakeStackFast() {
|
||||
if (FakeStack *fs = GetTLSFakeStack())
|
||||
return fs;
|
||||
if (!__asan_option_detect_stack_use_after_return)
|
||||
return 0;
|
||||
return GetFakeStack();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
|
||||
if (!alive_) return real_stack;
|
||||
CHECK(size <= kMaxStackMallocSize && size > 1);
|
||||
uptr size_class = ComputeSizeClass(size);
|
||||
if (!allocated_size_classes_[size_class]) {
|
||||
AllocateOneSizeClass(size_class);
|
||||
}
|
||||
FakeFrame *fake_frame = size_classes_[size_class].FifoPop();
|
||||
CHECK(fake_frame);
|
||||
fake_frame->size_minus_one = size - 1;
|
||||
fake_frame->real_stack = real_stack;
|
||||
while (FakeFrame *top = call_stack_.top()) {
|
||||
if (top->real_stack > real_stack) break;
|
||||
call_stack_.LifoPop();
|
||||
DeallocateFrame(top);
|
||||
}
|
||||
call_stack_.LifoPush(fake_frame);
|
||||
uptr ptr = (uptr)fake_frame;
|
||||
PoisonShadow(ptr, size, 0);
|
||||
ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size, uptr real_stack) {
|
||||
FakeStack *fs = GetFakeStackFast();
|
||||
if (!fs) return real_stack;
|
||||
FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack);
|
||||
if (!ff)
|
||||
return real_stack; // Out of fake stack, return the real one.
|
||||
uptr ptr = reinterpret_cast<uptr>(ff);
|
||||
SetShadow(ptr, size, class_id, 0);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
|
||||
CHECK(alive_);
|
||||
uptr size = fake_frame->size_minus_one + 1;
|
||||
uptr size_class = ComputeSizeClass(size);
|
||||
CHECK(allocated_size_classes_[size_class]);
|
||||
uptr ptr = (uptr)fake_frame;
|
||||
CHECK(AddrIsInSizeClass(ptr, size_class));
|
||||
CHECK(AddrIsInSizeClass(ptr + size - 1, size_class));
|
||||
size_classes_[size_class].FifoPush(fake_frame);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) {
|
||||
FakeFrame *fake_frame = (FakeFrame*)ptr;
|
||||
CHECK_EQ(fake_frame->magic, kRetiredStackFrameMagic);
|
||||
CHECK_NE(fake_frame->descr, 0);
|
||||
CHECK_EQ(fake_frame->size_minus_one, size - 1);
|
||||
PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
|
||||
ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size, uptr real_stack) {
|
||||
if (ptr == real_stack)
|
||||
return;
|
||||
FakeStack::Deallocate(ptr, class_id);
|
||||
SetShadow(ptr, size, class_id, kMagic8);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
// ---------------------- Interface ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
uptr __asan_stack_malloc(uptr size, uptr real_stack) {
|
||||
if (!flags()->use_fake_stack) return real_stack;
|
||||
AsanThread *t = GetCurrentThread();
|
||||
if (!t) {
|
||||
// TSD is gone, use the real stack.
|
||||
return real_stack;
|
||||
#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \
|
||||
__asan_stack_malloc_##class_id(uptr size, uptr real_stack) { \
|
||||
return __asan::OnMalloc(class_id, size, real_stack); \
|
||||
} \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \
|
||||
uptr ptr, uptr size, uptr real_stack) { \
|
||||
__asan::OnFree(ptr, class_id, size, real_stack); \
|
||||
}
|
||||
uptr ptr = t->fake_stack().AllocateStack(size, real_stack);
|
||||
// Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) {
|
||||
if (!flags()->use_fake_stack) return;
|
||||
if (ptr != real_stack) {
|
||||
FakeStack::OnFree(ptr, size, real_stack);
|
||||
}
|
||||
}
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10)
|
||||
|
@ -9,12 +9,14 @@
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// ASan-private header for asan_fake_stack.cc
|
||||
// ASan-private header for asan_fake_stack.cc, implements FakeStack.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ASAN_FAKE_STACK_H
|
||||
#define ASAN_FAKE_STACK_H
|
||||
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
// Fake stack frame contains local variables of one function.
|
||||
@ -22,96 +24,148 @@ struct FakeFrame {
|
||||
uptr magic; // Modified by the instrumented code.
|
||||
uptr descr; // Modified by the instrumented code.
|
||||
uptr pc; // Modified by the instrumented code.
|
||||
u64 real_stack : 48;
|
||||
u64 size_minus_one : 16;
|
||||
// End of the first 32 bytes.
|
||||
// The rest should not be used when the frame is active.
|
||||
FakeFrame *next;
|
||||
};
|
||||
|
||||
struct FakeFrameFifo {
|
||||
public:
|
||||
void FifoPush(FakeFrame *node);
|
||||
FakeFrame *FifoPop();
|
||||
private:
|
||||
FakeFrame *first_, *last_;
|
||||
};
|
||||
|
||||
template<uptr kMaxNumberOfFrames>
|
||||
class FakeFrameLifo {
|
||||
public:
|
||||
explicit FakeFrameLifo(LinkerInitialized) {}
|
||||
FakeFrameLifo() : n_frames_(0) {}
|
||||
void LifoPush(FakeFrame *node) {
|
||||
CHECK_LT(n_frames_, kMaxNumberOfFrames);
|
||||
frames_[n_frames_++] = node;
|
||||
}
|
||||
void LifoPop() {
|
||||
CHECK(n_frames_);
|
||||
n_frames_--;
|
||||
}
|
||||
FakeFrame *top() {
|
||||
if (n_frames_ == 0)
|
||||
return 0;
|
||||
return frames_[n_frames_ - 1];
|
||||
}
|
||||
private:
|
||||
uptr n_frames_;
|
||||
FakeFrame *frames_[kMaxNumberOfFrames];
|
||||
uptr real_stack;
|
||||
};
|
||||
|
||||
// For each thread we create a fake stack and place stack objects on this fake
|
||||
// stack instead of the real stack. The fake stack is not really a stack but
|
||||
// a fast malloc-like allocator so that when a function exits the fake stack
|
||||
// is not poped but remains there for quite some time until gets used again.
|
||||
// is not popped but remains there for quite some time until gets used again.
|
||||
// So, we poison the objects on the fake stack when function returns.
|
||||
// It helps us find use-after-return bugs.
|
||||
// We can not rely on __asan_stack_free being called on every function exit,
|
||||
// so we maintain a lifo list of all current fake frames and update it on every
|
||||
// call to __asan_stack_malloc.
|
||||
//
|
||||
// The FakeStack objects is allocated by a single mmap call and has no other
|
||||
// pointers. The size of the fake stack depends on the actual thread stack size
|
||||
// and thus can not be a constant.
|
||||
// stack_size is a power of two greater or equal to the thread's stack size;
|
||||
// we store it as its logarithm (stack_size_log).
|
||||
// FakeStack has kNumberOfSizeClasses (11) size classes, each size class
|
||||
// is a power of two, starting from 64 bytes. Each size class occupies
|
||||
// stack_size bytes and thus can allocate
|
||||
// NumberOfFrames=(stack_size/BytesInSizeClass) fake frames (also a power of 2).
|
||||
// For each size class we have NumberOfFrames allocation flags,
|
||||
// each flag indicates whether the given frame is currently allocated.
|
||||
// All flags for size classes 0 .. 10 are stored in a single contiguous region
|
||||
// followed by another contiguous region which contains the actual memory for
|
||||
// size classes. The addresses are computed by GetFlags and GetFrame without
|
||||
// any memory accesses solely based on 'this' and stack_size_log.
|
||||
// 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
|
||||
// and the next allocation.
|
||||
class FakeStack {
|
||||
public:
|
||||
FakeStack();
|
||||
explicit FakeStack(LinkerInitialized x) : call_stack_(x) {}
|
||||
void Init(uptr stack_size);
|
||||
void StopUsingFakeStack() { alive_ = false; }
|
||||
void Cleanup();
|
||||
uptr AllocateStack(uptr size, uptr real_stack);
|
||||
static void OnFree(uptr ptr, uptr size, uptr real_stack);
|
||||
// Return the bottom of the maped region.
|
||||
uptr AddrIsInFakeStack(uptr addr);
|
||||
bool StackSize() { return stack_size_; }
|
||||
|
||||
private:
|
||||
static const uptr kMinStackFrameSizeLog = 9; // Min frame is 512B.
|
||||
static const uptr kMinStackFrameSizeLog = 6; // Min frame is 64B.
|
||||
static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K.
|
||||
static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog;
|
||||
|
||||
public:
|
||||
static const uptr kNumberOfSizeClasses =
|
||||
kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1;
|
||||
static const uptr kMaxRecursionDepth = 1023;
|
||||
kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1;
|
||||
|
||||
bool AddrIsInSizeClass(uptr addr, uptr size_class);
|
||||
// CTOR: create the FakeStack as a single mmap-ed object.
|
||||
static FakeStack *Create(uptr stack_size_log);
|
||||
|
||||
// Each size class should be large enough to hold all frames.
|
||||
uptr ClassMmapSize(uptr size_class);
|
||||
void Destroy();
|
||||
|
||||
uptr ClassSize(uptr size_class) {
|
||||
return 1UL << (size_class + kMinStackFrameSizeLog);
|
||||
// stack_size_log is at least 15 (stack_size >= 32K).
|
||||
static uptr SizeRequiredForFlags(uptr stack_size_log) {
|
||||
return 1UL << (stack_size_log + 1 - kMinStackFrameSizeLog);
|
||||
}
|
||||
|
||||
void DeallocateFrame(FakeFrame *fake_frame);
|
||||
// Each size class occupies stack_size bytes.
|
||||
static uptr SizeRequiredForFrames(uptr stack_size_log) {
|
||||
return (1ULL << stack_size_log) * kNumberOfSizeClasses;
|
||||
}
|
||||
|
||||
uptr ComputeSizeClass(uptr alloc_size);
|
||||
void AllocateOneSizeClass(uptr size_class);
|
||||
// Number of bytes requires for the whole object.
|
||||
static uptr RequiredSize(uptr stack_size_log) {
|
||||
return kFlagsOffset + SizeRequiredForFlags(stack_size_log) +
|
||||
SizeRequiredForFrames(stack_size_log);
|
||||
}
|
||||
|
||||
uptr stack_size_;
|
||||
bool alive_;
|
||||
// Offset of the given flag from the first flag.
|
||||
// The flags for class 0 begin at offset 000000000
|
||||
// The flags for class 1 begin at offset 100000000
|
||||
// ....................2................ 110000000
|
||||
// ....................3................ 111000000
|
||||
// and so on.
|
||||
static uptr FlagsOffset(uptr stack_size_log, uptr class_id) {
|
||||
uptr t = kNumberOfSizeClasses - 1 - class_id;
|
||||
const uptr all_ones = (1 << (kNumberOfSizeClasses - 1)) - 1;
|
||||
return ((all_ones >> t) << t) << (stack_size_log - 15);
|
||||
}
|
||||
|
||||
uptr allocated_size_classes_[kNumberOfSizeClasses];
|
||||
FakeFrameFifo size_classes_[kNumberOfSizeClasses];
|
||||
FakeFrameLifo<kMaxRecursionDepth> call_stack_;
|
||||
static uptr NumberOfFrames(uptr stack_size_log, uptr class_id) {
|
||||
return 1UL << (stack_size_log - kMinStackFrameSizeLog - class_id);
|
||||
}
|
||||
|
||||
// Divide n by the numbe 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.
|
||||
u8 *GetFlags(uptr stack_size_log, uptr class_id) {
|
||||
return reinterpret_cast<u8 *>(this) + kFlagsOffset +
|
||||
FlagsOffset(stack_size_log, class_id);
|
||||
}
|
||||
|
||||
// Get frame by class_id and pos.
|
||||
u8 *GetFrame(uptr stack_size_log, uptr class_id, uptr pos) {
|
||||
return reinterpret_cast<u8 *>(this) + kFlagsOffset +
|
||||
SizeRequiredForFlags(stack_size_log) +
|
||||
(1 << stack_size_log) * class_id + BytesInSizeClass(class_id) * pos;
|
||||
}
|
||||
|
||||
// Allocate the fake frame.
|
||||
FakeFrame *Allocate(uptr stack_size_log, uptr class_id, uptr real_stack);
|
||||
|
||||
// Deallocate the fake frame: read the saved flag address and write 0 there.
|
||||
static void Deallocate(uptr x, uptr class_id) {
|
||||
**SavedFlagPtr(x, class_id) = 0;
|
||||
}
|
||||
|
||||
// Poison the entire FakeStack's shadow with the magic value.
|
||||
void PoisonAll(u8 magic);
|
||||
|
||||
// Return the beginning of the FakeFrame or 0 if the address is not ours.
|
||||
uptr AddrIsInFakeStack(uptr addr);
|
||||
|
||||
// Number of bytes in a fake frame of this size class.
|
||||
static uptr BytesInSizeClass(uptr class_id) {
|
||||
return 1UL << (class_id + kMinStackFrameSizeLog);
|
||||
}
|
||||
|
||||
// The fake frame is guaranteed to have a right redzone.
|
||||
// We use the last word of that redzone to store the address of the flag
|
||||
// that corresponds to the current frame to make faster deallocation.
|
||||
static u8 **SavedFlagPtr(uptr x, uptr class_id) {
|
||||
return reinterpret_cast<u8 **>(x + BytesInSizeClass(class_id) - sizeof(x));
|
||||
}
|
||||
|
||||
uptr stack_size_log() const { return stack_size_log_; }
|
||||
|
||||
void HandleNoReturn();
|
||||
void GC(uptr real_stack);
|
||||
|
||||
void ForEachFakeFrame(RangeIteratorCallback callback, void *arg);
|
||||
|
||||
private:
|
||||
FakeStack() { }
|
||||
static const uptr kFlagsOffset = 4096; // This is were the flags begin.
|
||||
// Must match the number of uses of DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID
|
||||
COMPILER_CHECK(kNumberOfSizeClasses == 11);
|
||||
static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog;
|
||||
|
||||
uptr hint_position_[kNumberOfSizeClasses];
|
||||
uptr stack_size_log_;
|
||||
// a bit is set if something was allocated from the corresponding size class.
|
||||
bool needs_gc_;
|
||||
};
|
||||
|
||||
FakeStack *GetTLSFakeStack();
|
||||
void SetTLSFakeStack(FakeStack *fs);
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // ASAN_FAKE_STACK_H
|
||||
|
@ -32,8 +32,6 @@ struct Flags {
|
||||
// Lower value may reduce memory usage but increase the chance of
|
||||
// false negatives.
|
||||
int quarantine_size;
|
||||
// Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).
|
||||
int verbosity;
|
||||
// Size (in bytes) of redzones around heap objects.
|
||||
// Requirement: redzone >= 32, is a power of two.
|
||||
int redzone;
|
||||
@ -52,8 +50,10 @@ struct Flags {
|
||||
bool replace_intrin;
|
||||
// Used on Mac only.
|
||||
bool mac_ignore_invalid_free;
|
||||
// ASan allocator flag.
|
||||
bool use_fake_stack;
|
||||
// Enables stack-use-after-return checking at run-time.
|
||||
bool detect_stack_use_after_return;
|
||||
// The minimal fake stack size log.
|
||||
int uar_stack_size_log;
|
||||
// ASan allocator flag. max_malloc_fill_size is the maximal amount of bytes
|
||||
// that will be filled with malloc_fill_byte on malloc.
|
||||
int max_malloc_fill_size, malloc_fill_byte;
|
||||
@ -83,6 +83,9 @@ struct Flags {
|
||||
bool print_legend;
|
||||
// If set, prints ASan exit stats even after program terminates successfully.
|
||||
bool atexit;
|
||||
// If set, coverage information will be dumped at shutdown time if the
|
||||
// appropriate instrumentation was enabled.
|
||||
bool coverage;
|
||||
// By default, disable core dumper on 64-bit - it makes little sense
|
||||
// to dump 16T+ core.
|
||||
bool disable_core;
|
||||
@ -93,23 +96,20 @@ struct Flags {
|
||||
// but also thread creation stacks for threads that created those threads,
|
||||
// etc. up to main thread.
|
||||
bool print_full_thread_history;
|
||||
// ASan will write logs to "log_path.pid" instead of stderr.
|
||||
const char *log_path;
|
||||
// Poison (or not) the heap memory on [de]allocation. Zero value is useful
|
||||
// for benchmarking the allocator or instrumentator.
|
||||
bool poison_heap;
|
||||
// If true, poison partially addressable 8-byte aligned words (default=true).
|
||||
// This flag affects heap and global buffers, but not stack buffers.
|
||||
bool poison_partial;
|
||||
// Report errors on malloc/delete, new/free, new/delete[], etc.
|
||||
bool alloc_dealloc_mismatch;
|
||||
// Use stack depot instead of storing stacks in the redzones.
|
||||
bool use_stack_depot;
|
||||
// If true, assume that memcmp(p1, p2, n) always reads n bytes before
|
||||
// comparing p1 and p2.
|
||||
bool strict_memcmp;
|
||||
// If true, assume that dynamic initializers can never access globals from
|
||||
// other modules, even if the latter are already initialized.
|
||||
bool strict_init_order;
|
||||
// Invoke LeakSanitizer at process exit.
|
||||
bool detect_leaks;
|
||||
};
|
||||
|
||||
extern Flags asan_flags_dont_use_directly;
|
||||
|
@ -41,7 +41,7 @@ struct DynInitGlobal {
|
||||
Global g;
|
||||
bool initialized;
|
||||
};
|
||||
typedef InternalVector<DynInitGlobal> VectorOfGlobals;
|
||||
typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals;
|
||||
// Lazy-initialized and never deleted.
|
||||
static VectorOfGlobals *dynamic_init_globals;
|
||||
|
||||
@ -94,15 +94,13 @@ static void RegisterGlobal(const Global *g) {
|
||||
CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
|
||||
if (flags()->poison_heap)
|
||||
PoisonRedZones(*g);
|
||||
ListOfGlobals *l =
|
||||
(ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
|
||||
ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals;
|
||||
l->g = g;
|
||||
l->next = list_of_all_globals;
|
||||
list_of_all_globals = l;
|
||||
if (g->has_dynamic_init) {
|
||||
if (dynamic_init_globals == 0) {
|
||||
void *mem = allocator_for_globals.Allocate(sizeof(VectorOfGlobals));
|
||||
dynamic_init_globals = new(mem)
|
||||
dynamic_init_globals = new(allocator_for_globals)
|
||||
VectorOfGlobals(kDynamicInitGlobalsInitialCapacity);
|
||||
}
|
||||
DynInitGlobal dyn_global = { *g, false };
|
||||
|
@ -14,15 +14,8 @@
|
||||
#ifndef ASAN_INTERCEPTED_FUNCTIONS_H
|
||||
#define ASAN_INTERCEPTED_FUNCTIONS_H
|
||||
|
||||
#include "asan_internal.h"
|
||||
#include "interception/interception.h"
|
||||
#include "sanitizer_common/sanitizer_platform_interceptors.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
using __sanitizer::uptr;
|
||||
|
||||
// Use macro to describe if specific function should be
|
||||
// intercepted on a given platform.
|
||||
#if !SANITIZER_WINDOWS
|
||||
@ -83,32 +76,4 @@ using __sanitizer::uptr;
|
||||
# define ASAN_INTERCEPT___CXA_ATEXIT 0
|
||||
#endif
|
||||
|
||||
# if SANITIZER_WINDOWS
|
||||
extern "C" {
|
||||
// Windows threads.
|
||||
__declspec(dllimport)
|
||||
void* __stdcall CreateThread(void *sec, uptr st, void* start,
|
||||
void *arg, DWORD fl, DWORD *id);
|
||||
|
||||
int memcmp(const void *a1, const void *a2, uptr size);
|
||||
void memmove(void *to, const void *from, uptr size);
|
||||
void* memset(void *block, int c, uptr size);
|
||||
void* memcpy(void *to, const void *from, uptr size);
|
||||
char* strcat(char *to, const char* from); // NOLINT
|
||||
char* strchr(const char *str, int c);
|
||||
int strcmp(const char *s1, const char* s2);
|
||||
char* strcpy(char *to, const char* from); // NOLINT
|
||||
uptr strlen(const char *s);
|
||||
char* strncat(char *to, const char* from, uptr size);
|
||||
int strncmp(const char *s1, const char* s2, uptr size);
|
||||
char* strncpy(char *to, const char* from, uptr size);
|
||||
uptr strnlen(const char *s, uptr maxlen);
|
||||
int atoi(const char *nptr);
|
||||
long atol(const char *nptr); // NOLINT
|
||||
long strtol(const char *nptr, char **endptr, int base); // NOLINT
|
||||
void longjmp(void *env, int value);
|
||||
double frexp(double x, int *expptr);
|
||||
}
|
||||
# endif
|
||||
|
||||
#endif // ASAN_INTERCEPTED_FUNCTIONS_H
|
||||
|
@ -53,7 +53,7 @@ static inline bool QuickCheckForUnpoisonedRegion(uptr beg, uptr size) {
|
||||
} while (0)
|
||||
|
||||
#define ASAN_READ_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, false)
|
||||
#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true);
|
||||
#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true)
|
||||
|
||||
// Behavior of functions like "memcpy" or "strcpy" is undefined
|
||||
// if memory intervals overlap. We report error in this case.
|
||||
@ -94,9 +94,9 @@ void SetThreadName(const char *name) {
|
||||
asanThreadRegistry().SetThreadName(t->tid(), name);
|
||||
}
|
||||
|
||||
static void DisableStrictInitOrderChecker() {
|
||||
if (flags()->strict_init_order)
|
||||
flags()->check_initialization_order = false;
|
||||
int OnExit() {
|
||||
// FIXME: ask frontend whether we need to return failure.
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
@ -104,26 +104,69 @@ static void DisableStrictInitOrderChecker() {
|
||||
// ---------------------- Wrappers ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr)
|
||||
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
|
||||
|
||||
#if !SANITIZER_MAC
|
||||
#define ASAN_INTERCEPT_FUNC(name) \
|
||||
do { \
|
||||
if ((!INTERCEPT_FUNCTION(name) || !REAL(name)) && \
|
||||
common_flags()->verbosity > 0) \
|
||||
Report("AddressSanitizer: failed to intercept '" #name "'\n"); \
|
||||
} while (0)
|
||||
#else
|
||||
// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.
|
||||
#define ASAN_INTERCEPT_FUNC(name)
|
||||
#endif // SANITIZER_MAC
|
||||
|
||||
#define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name)
|
||||
#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \
|
||||
do { \
|
||||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
|
||||
ASAN_WRITE_RANGE(ptr, size)
|
||||
#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) ASAN_READ_RANGE(ptr, size)
|
||||
#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
|
||||
do { \
|
||||
if (asan_init_is_running) \
|
||||
return REAL(func)(__VA_ARGS__); \
|
||||
ctx = 0; \
|
||||
(void)ctx; \
|
||||
ENSURE_ASAN_INITED(); \
|
||||
#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
|
||||
do { \
|
||||
if (asan_init_is_running) return REAL(func)(__VA_ARGS__); \
|
||||
ctx = 0; \
|
||||
(void) ctx; \
|
||||
if (SANITIZER_MAC && !asan_inited) return REAL(func)(__VA_ARGS__); \
|
||||
ENSURE_ASAN_INITED(); \
|
||||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
|
||||
do { \
|
||||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
|
||||
do { \
|
||||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
|
||||
do { \
|
||||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) do { } while (false)
|
||||
#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) do { } while (false)
|
||||
#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name)
|
||||
// Should be asanThreadRegistry().SetThreadNameByUserId(thread, name)
|
||||
// But asan does not remember UserId's for threads (pthread_t);
|
||||
// and remembers all ever existed threads, so the linear search by UserId
|
||||
// can be slow.
|
||||
#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
|
||||
do { \
|
||||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
|
||||
#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
|
||||
#include "sanitizer_common/sanitizer_common_interceptors.inc"
|
||||
|
||||
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s)
|
||||
#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(p, s)
|
||||
#define COMMON_SYSCALL_POST_READ_RANGE(p, s)
|
||||
#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s)
|
||||
#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
|
||||
do { \
|
||||
(void)(p); \
|
||||
(void)(s); \
|
||||
} while (false)
|
||||
#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
|
||||
do { \
|
||||
(void)(p); \
|
||||
(void)(s); \
|
||||
} while (false)
|
||||
#include "sanitizer_common/sanitizer_common_syscalls.inc"
|
||||
|
||||
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
|
||||
@ -133,16 +176,16 @@ static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
|
||||
}
|
||||
|
||||
#if ASAN_INTERCEPT_PTHREAD_CREATE
|
||||
extern "C" int pthread_attr_getdetachstate(void *attr, int *v);
|
||||
|
||||
INTERCEPTOR(int, pthread_create, void *thread,
|
||||
void *attr, void *(*start_routine)(void*), void *arg) {
|
||||
EnsureMainThreadIDIsCorrect();
|
||||
// Strict init-order checking in thread-hostile.
|
||||
DisableStrictInitOrderChecker();
|
||||
if (flags()->strict_init_order)
|
||||
StopInitOrderChecking();
|
||||
GET_STACK_TRACE_THREAD;
|
||||
int detached = 0;
|
||||
if (attr != 0)
|
||||
pthread_attr_getdetachstate(attr, &detached);
|
||||
REAL(pthread_attr_getdetachstate)(attr, &detached);
|
||||
|
||||
u32 current_tid = GetCurrentTidOrInvalid();
|
||||
AsanThread *t = AsanThread::Create(start_routine, arg);
|
||||
@ -170,7 +213,7 @@ INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
|
||||
#elif SANITIZER_POSIX
|
||||
// We need to have defined REAL(sigaction) on posix systems.
|
||||
DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact);
|
||||
struct sigaction *oldact)
|
||||
#endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
|
||||
|
||||
#if ASAN_INTERCEPT_SWAPCONTEXT
|
||||
@ -240,13 +283,15 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
|
||||
// Since asan maps 16T of RAM, mlock is completely unfriendly to asan.
|
||||
// All functions return 0 (success).
|
||||
static void MlockIsUnsupported() {
|
||||
static bool printed = 0;
|
||||
static bool printed = false;
|
||||
if (printed) return;
|
||||
printed = true;
|
||||
Printf("INFO: AddressSanitizer ignores mlock/mlockall/munlock/munlockall\n");
|
||||
if (common_flags()->verbosity > 0) {
|
||||
Printf("INFO: AddressSanitizer ignores "
|
||||
"mlock/mlockall/munlock/munlockall\n");
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
INTERCEPTOR(int, mlock, const void *addr, uptr len) {
|
||||
MlockIsUnsupported();
|
||||
return 0;
|
||||
@ -266,7 +311,6 @@ INTERCEPTOR(int, munlockall, void) {
|
||||
MlockIsUnsupported();
|
||||
return 0;
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
static inline int CharCmp(unsigned char c1, unsigned char c2) {
|
||||
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
|
||||
@ -300,7 +344,23 @@ INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
|
||||
return REAL(memcmp(a1, a2, size));
|
||||
}
|
||||
|
||||
#define MEMMOVE_BODY { \
|
||||
if (!asan_inited) return internal_memmove(to, from, size); \
|
||||
if (asan_init_is_running) { \
|
||||
return REAL(memmove)(to, from, size); \
|
||||
} \
|
||||
ENSURE_ASAN_INITED(); \
|
||||
if (flags()->replace_intrin) { \
|
||||
ASAN_READ_RANGE(from, size); \
|
||||
ASAN_WRITE_RANGE(to, size); \
|
||||
} \
|
||||
return internal_memmove(to, from, size); \
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) MEMMOVE_BODY
|
||||
|
||||
INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
|
||||
#if !SANITIZER_MAC
|
||||
if (!asan_inited) return internal_memcpy(to, from, size);
|
||||
// memcpy is called during __asan_init() from the internals
|
||||
// of printf(...).
|
||||
@ -317,24 +377,19 @@ INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
|
||||
ASAN_READ_RANGE(from, size);
|
||||
ASAN_WRITE_RANGE(to, size);
|
||||
}
|
||||
// Interposing of resolver functions is broken on Mac OS 10.7 and 10.8.
|
||||
// Interposing of resolver functions is broken on Mac OS 10.7 and 10.8, so
|
||||
// calling REAL(memcpy) here leads to infinite recursion.
|
||||
// See also http://code.google.com/p/address-sanitizer/issues/detail?id=116.
|
||||
return internal_memcpy(to, from, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) {
|
||||
if (!asan_inited) return internal_memmove(to, from, size);
|
||||
if (asan_init_is_running) {
|
||||
return REAL(memmove)(to, from, size);
|
||||
}
|
||||
ENSURE_ASAN_INITED();
|
||||
if (flags()->replace_intrin) {
|
||||
ASAN_READ_RANGE(from, size);
|
||||
ASAN_WRITE_RANGE(to, size);
|
||||
}
|
||||
// Interposing of resolver functions is broken on Mac OS 10.7 and 10.8.
|
||||
// See also http://code.google.com/p/address-sanitizer/issues/detail?id=116.
|
||||
return internal_memmove(to, from, size);
|
||||
#else
|
||||
// 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.
|
||||
MEMMOVE_BODY
|
||||
#endif // !SANITIZER_MAC
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, memset, void *block, int c, uptr size) {
|
||||
@ -375,7 +430,7 @@ INTERCEPTOR(char*, index, const char *string, int c)
|
||||
DECLARE_REAL(char*, index, const char *string, int c)
|
||||
OVERRIDE_FUNCTION(index, strchr);
|
||||
# else
|
||||
DEFINE_REAL(char*, index, const char *string, int c);
|
||||
DEFINE_REAL(char*, index, const char *string, int c)
|
||||
# endif
|
||||
# endif
|
||||
#endif // ASAN_INTERCEPT_INDEX
|
||||
@ -418,24 +473,6 @@ INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
|
||||
return REAL(strncat)(to, from, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
|
||||
if (!asan_inited) return internal_strcmp(s1, s2);
|
||||
if (asan_init_is_running) {
|
||||
return REAL(strcmp)(s1, s2);
|
||||
}
|
||||
ENSURE_ASAN_INITED();
|
||||
unsigned char c1, c2;
|
||||
uptr i;
|
||||
for (i = 0; ; i++) {
|
||||
c1 = (unsigned char)s1[i];
|
||||
c2 = (unsigned char)s2[i];
|
||||
if (c1 != c2 || c1 == '\0') break;
|
||||
}
|
||||
ASAN_READ_RANGE(s1, i + 1);
|
||||
ASAN_READ_RANGE(s2, i + 1);
|
||||
return CharCmp(c1, c2);
|
||||
}
|
||||
|
||||
INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
|
||||
#if SANITIZER_MAC
|
||||
if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT
|
||||
@ -457,21 +494,16 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
|
||||
|
||||
#if ASAN_INTERCEPT_STRDUP
|
||||
INTERCEPTOR(char*, strdup, const char *s) {
|
||||
#if SANITIZER_MAC
|
||||
// FIXME: because internal_strdup() uses InternalAlloc(), which currently
|
||||
// just calls malloc() on Mac, we can't use internal_strdup() with the
|
||||
// dynamic runtime. We can remove the call to REAL(strdup) once InternalAlloc
|
||||
// starts using mmap() instead.
|
||||
// See also http://code.google.com/p/address-sanitizer/issues/detail?id=123.
|
||||
if (!asan_inited) return REAL(strdup)(s);
|
||||
#endif
|
||||
if (!asan_inited) return internal_strdup(s);
|
||||
ENSURE_ASAN_INITED();
|
||||
uptr length = REAL(strlen)(s);
|
||||
if (flags()->replace_str) {
|
||||
uptr length = REAL(strlen)(s);
|
||||
ASAN_READ_RANGE(s, length + 1);
|
||||
}
|
||||
return REAL(strdup)(s);
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
void *new_mem = asan_malloc(length + 1, &stack);
|
||||
REAL(memcpy)(new_mem, s, length + 1);
|
||||
return reinterpret_cast<char*>(new_mem);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -490,24 +522,13 @@ INTERCEPTOR(uptr, strlen, const char *s) {
|
||||
return length;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) {
|
||||
if (!asan_inited) return internal_strncmp(s1, s2, size);
|
||||
// strncmp is called from malloc_default_purgeable_zone()
|
||||
// in __asan::ReplaceSystemAlloc() on Mac.
|
||||
if (asan_init_is_running) {
|
||||
return REAL(strncmp)(s1, s2, size);
|
||||
INTERCEPTOR(uptr, wcslen, const wchar_t *s) {
|
||||
uptr length = REAL(wcslen)(s);
|
||||
if (!asan_init_is_running) {
|
||||
ENSURE_ASAN_INITED();
|
||||
ASAN_READ_RANGE(s, (length + 1) * sizeof(wchar_t));
|
||||
}
|
||||
ENSURE_ASAN_INITED();
|
||||
unsigned char c1 = 0, c2 = 0;
|
||||
uptr i;
|
||||
for (i = 0; i < size; i++) {
|
||||
c1 = (unsigned char)s1[i];
|
||||
c2 = (unsigned char)s2[i];
|
||||
if (c1 != c2 || c1 == '\0') break;
|
||||
}
|
||||
ASAN_READ_RANGE(s1, Min(i + 1, size));
|
||||
ASAN_READ_RANGE(s2, Min(i + 1, size));
|
||||
return CharCmp(c1, c2);
|
||||
return length;
|
||||
}
|
||||
|
||||
INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) {
|
||||
@ -644,6 +665,9 @@ static void AtCxaAtexit(void *unused) {
|
||||
#if ASAN_INTERCEPT___CXA_ATEXIT
|
||||
INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
|
||||
void *dso_handle) {
|
||||
#if SANITIZER_MAC
|
||||
if (!asan_inited) return REAL(__cxa_atexit)(func, arg, dso_handle);
|
||||
#endif
|
||||
ENSURE_ASAN_INITED();
|
||||
int res = REAL(__cxa_atexit)(func, arg, dso_handle);
|
||||
REAL(__cxa_atexit)(AtCxaAtexit, 0, 0);
|
||||
@ -651,26 +675,22 @@ INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
|
||||
}
|
||||
#endif // ASAN_INTERCEPT___CXA_ATEXIT
|
||||
|
||||
#define ASAN_INTERCEPT_FUNC(name) do { \
|
||||
if (!INTERCEPT_FUNCTION(name) && flags()->verbosity > 0) \
|
||||
Report("AddressSanitizer: failed to intercept '" #name "'\n"); \
|
||||
} while (0)
|
||||
|
||||
#if SANITIZER_WINDOWS
|
||||
INTERCEPTOR_WINAPI(DWORD, CreateThread,
|
||||
void* security, uptr stack_size,
|
||||
DWORD (__stdcall *start_routine)(void*), void* arg,
|
||||
DWORD flags, void* tid) {
|
||||
DWORD thr_flags, void* tid) {
|
||||
// Strict init-order checking in thread-hostile.
|
||||
DisableStrictInitOrderChecker();
|
||||
if (flags()->strict_init_order)
|
||||
StopInitOrderChecking();
|
||||
GET_STACK_TRACE_THREAD;
|
||||
u32 current_tid = GetCurrentTidOrInvalid();
|
||||
AsanThread *t = AsanThread::Create(start_routine, arg);
|
||||
CreateThreadContextArgs args = { t, &stack };
|
||||
int detached = 0; // FIXME: how can we determine it on Windows?
|
||||
bool detached = false; // FIXME: how can we determine it on Windows?
|
||||
asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args);
|
||||
return REAL(CreateThread)(security, stack_size,
|
||||
asan_thread_start, t, flags, tid);
|
||||
asan_thread_start, t, thr_flags, tid);
|
||||
}
|
||||
|
||||
namespace __asan {
|
||||
@ -687,9 +707,6 @@ void InitializeAsanInterceptors() {
|
||||
static bool was_called_once;
|
||||
CHECK(was_called_once == false);
|
||||
was_called_once = true;
|
||||
#if SANITIZER_MAC
|
||||
return;
|
||||
#else
|
||||
SANITIZER_COMMON_INTERCEPTORS_INIT;
|
||||
|
||||
// Intercept mem* functions.
|
||||
@ -703,11 +720,10 @@ void InitializeAsanInterceptors() {
|
||||
// Intercept str* functions.
|
||||
ASAN_INTERCEPT_FUNC(strcat); // NOLINT
|
||||
ASAN_INTERCEPT_FUNC(strchr);
|
||||
ASAN_INTERCEPT_FUNC(strcmp);
|
||||
ASAN_INTERCEPT_FUNC(strcpy); // NOLINT
|
||||
ASAN_INTERCEPT_FUNC(strlen);
|
||||
ASAN_INTERCEPT_FUNC(wcslen);
|
||||
ASAN_INTERCEPT_FUNC(strncat);
|
||||
ASAN_INTERCEPT_FUNC(strncmp);
|
||||
ASAN_INTERCEPT_FUNC(strncpy);
|
||||
#if ASAN_INTERCEPT_STRDUP
|
||||
ASAN_INTERCEPT_FUNC(strdup);
|
||||
@ -771,10 +787,9 @@ void InitializeAsanInterceptors() {
|
||||
InitializeWindowsInterceptors();
|
||||
#endif
|
||||
|
||||
if (flags()->verbosity > 0) {
|
||||
if (common_flags()->verbosity > 0) {
|
||||
Report("AddressSanitizer: libc interceptors initialized\n");
|
||||
}
|
||||
#endif // SANITIZER_MAC
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
@ -30,7 +30,7 @@ extern "C" {
|
||||
// v2=>v3: stack frame description (created by the compiler)
|
||||
// contains the function PC as the 3-rd field (see
|
||||
// DescribeAddressIfStack).
|
||||
void __asan_init_v3() SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_init_v3();
|
||||
#define __asan_init __asan_init_v3
|
||||
|
||||
// This structure describes an instrumented global variable.
|
||||
@ -46,96 +46,85 @@ extern "C" {
|
||||
|
||||
// These two functions should be called by the instrumented code.
|
||||
// 'globals' is an array of structures describing 'n' globals.
|
||||
void __asan_register_globals(__asan_global *globals, uptr n)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
void __asan_unregister_globals(__asan_global *globals, uptr n)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_register_globals(__asan_global *globals, uptr n);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_unregister_globals(__asan_global *globals, uptr n);
|
||||
|
||||
// These two functions should be called before and after dynamic initializers
|
||||
// of a single module run, respectively.
|
||||
void __asan_before_dynamic_init(const char *module_name)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
void __asan_after_dynamic_init()
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
|
||||
// These two functions are used by the instrumented code in the
|
||||
// use-after-return mode. __asan_stack_malloc allocates size bytes of
|
||||
// fake stack and __asan_stack_free poisons it. real_stack is a pointer to
|
||||
// the real stack region.
|
||||
uptr __asan_stack_malloc(uptr size, uptr real_stack)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
void __asan_stack_free(uptr ptr, uptr size, uptr real_stack)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_before_dynamic_init(const char *module_name);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_after_dynamic_init();
|
||||
|
||||
// These two functions are used by instrumented code in the
|
||||
// use-after-scope mode. They mark memory for local variables as
|
||||
// unaddressable when they leave scope and addressable before the
|
||||
// function exits.
|
||||
void __asan_poison_stack_memory(uptr addr, uptr size)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
void __asan_unpoison_stack_memory(uptr addr, uptr size)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_poison_stack_memory(uptr addr, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_unpoison_stack_memory(uptr addr, uptr size);
|
||||
|
||||
// Performs cleanup before a NoReturn function. Must be called before things
|
||||
// like _exit and execl to avoid false positives on stack.
|
||||
void __asan_handle_no_return() SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_handle_no_return();
|
||||
|
||||
void __asan_poison_memory_region(void const volatile *addr, uptr size)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
void __asan_unpoison_memory_region(void const volatile *addr, uptr size)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_poison_memory_region(void const volatile *addr, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_unpoison_memory_region(void const volatile *addr, uptr size);
|
||||
|
||||
bool __asan_address_is_poisoned(void const volatile *addr)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
bool __asan_address_is_poisoned(void const volatile *addr);
|
||||
|
||||
uptr __asan_region_is_poisoned(uptr beg, uptr size)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __asan_region_is_poisoned(uptr beg, uptr size);
|
||||
|
||||
void __asan_describe_address(uptr addr)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_describe_address(uptr addr);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_report_error(uptr pc, uptr bp, uptr sp,
|
||||
uptr addr, bool is_write, uptr access_size)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
uptr addr, bool is_write, uptr access_size);
|
||||
|
||||
int __asan_set_error_exit_code(int exit_code)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
void __asan_set_death_callback(void (*callback)(void))
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
void __asan_set_error_report_callback(void (*callback)(const char*))
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __asan_set_error_exit_code(int exit_code);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_set_death_callback(void (*callback)(void));
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_set_error_report_callback(void (*callback)(const char*));
|
||||
|
||||
/* OPTIONAL */ void __asan_on_error()
|
||||
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
/* OPTIONAL */ void __asan_on_error();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
/* OPTIONAL */ bool __asan_symbolize(const void *pc, char *out_buffer,
|
||||
int out_size)
|
||||
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
int out_size);
|
||||
|
||||
uptr __asan_get_estimated_allocated_size(uptr size)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
bool __asan_get_ownership(const void *p)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
uptr __asan_get_allocated_size(const void *p)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
uptr __asan_get_current_allocated_bytes()
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
uptr __asan_get_heap_size()
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
uptr __asan_get_free_bytes()
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
uptr __asan_get_unmapped_bytes()
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
void __asan_print_accumulated_stats()
|
||||
SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __asan_get_estimated_allocated_size(uptr size);
|
||||
|
||||
/* OPTIONAL */ const char* __asan_default_options()
|
||||
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE bool __asan_get_ownership(const void *p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_allocated_size(const void *p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_current_allocated_bytes();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_heap_size();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_free_bytes();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_unmapped_bytes();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_print_accumulated_stats();
|
||||
|
||||
/* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size)
|
||||
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
/* OPTIONAL */ void __asan_free_hook(void *ptr)
|
||||
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
/* OPTIONAL */ const char* __asan_default_options();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
/* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
/* OPTIONAL */ void __asan_free_hook(void *ptr);
|
||||
|
||||
// Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
extern int __asan_option_detect_stack_use_after_return;
|
||||
} // extern "C"
|
||||
|
||||
#endif // ASAN_INTERFACE_INTERNAL_H
|
||||
|
@ -98,6 +98,7 @@ void StopInitOrderChecking();
|
||||
void AsanTSDInit(void (*destructor)(void *tsd));
|
||||
void *AsanTSDGet();
|
||||
void AsanTSDSet(void *tsd);
|
||||
void PlatformTSDDtor(void *tsd);
|
||||
|
||||
void AppendToErrorMessageBuffer(const char *buffer);
|
||||
|
||||
|
@ -58,6 +58,12 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
||||
*pc = ucontext->uc_mcontext.arm_pc;
|
||||
*bp = ucontext->uc_mcontext.arm_fp;
|
||||
*sp = ucontext->uc_mcontext.arm_sp;
|
||||
# elif defined(__hppa__)
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.sc_iaoq[0];
|
||||
/* GCC uses %r3 whenever a frame pointer is needed. */
|
||||
*bp = ucontext->uc_mcontext.sc_gr[3];
|
||||
*sp = ucontext->uc_mcontext.sc_gr[30];
|
||||
# elif defined(__x86_64__)
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.gregs[REG_RIP];
|
||||
@ -89,6 +95,11 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
||||
stk_ptr = (uptr *) *sp;
|
||||
*bp = stk_ptr[15];
|
||||
# endif
|
||||
# elif defined(__mips__)
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.gregs[31];
|
||||
*bp = ucontext->uc_mcontext.gregs[30];
|
||||
*sp = ucontext->uc_mcontext.gregs[29];
|
||||
#else
|
||||
# error "Unsupported arch"
|
||||
#endif
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread.h"
|
||||
#include "sanitizer_common/sanitizer_atomic.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
#include <crt_externs.h> // for _NSGetArgv
|
||||
@ -52,7 +53,9 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
||||
# endif // SANITIZER_WORDSIZE
|
||||
}
|
||||
|
||||
int GetMacosVersion() {
|
||||
MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED;
|
||||
|
||||
MacosVersion GetMacosVersionInternal() {
|
||||
int mib[2] = { CTL_KERN, KERN_OSRELEASE };
|
||||
char version[100];
|
||||
uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]);
|
||||
@ -68,6 +71,7 @@ int GetMacosVersion() {
|
||||
case '0': return MACOS_VERSION_SNOW_LEOPARD;
|
||||
case '1': return MACOS_VERSION_LION;
|
||||
case '2': return MACOS_VERSION_MOUNTAIN_LION;
|
||||
case '3': return MACOS_VERSION_MAVERICKS;
|
||||
default: return MACOS_VERSION_UNKNOWN;
|
||||
}
|
||||
}
|
||||
@ -75,6 +79,18 @@ int GetMacosVersion() {
|
||||
}
|
||||
}
|
||||
|
||||
MacosVersion GetMacosVersion() {
|
||||
atomic_uint32_t *cache =
|
||||
reinterpret_cast<atomic_uint32_t*>(&cached_macos_version);
|
||||
MacosVersion result =
|
||||
static_cast<MacosVersion>(atomic_load(cache, memory_order_acquire));
|
||||
if (result == MACOS_VERSION_UNINITIALIZED) {
|
||||
result = GetMacosVersionInternal();
|
||||
atomic_store(cache, result, memory_order_release);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool PlatformHasDifferentMemcpyAndMemmove() {
|
||||
// On OS X 10.7 memcpy() and memmove() are both resolved
|
||||
// into memmove$VARIANT$sse42.
|
||||
@ -158,7 +174,7 @@ void MaybeReexec() {
|
||||
// Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
|
||||
setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
|
||||
}
|
||||
if (flags()->verbosity >= 1) {
|
||||
if (common_flags()->verbosity >= 1) {
|
||||
Report("exec()-ing the program with\n");
|
||||
Report("%s=%s\n", kDyldInsertLibraries, new_env);
|
||||
Report("to enable ASan wrappers.\n");
|
||||
@ -295,7 +311,7 @@ extern "C"
|
||||
void asan_dispatch_call_block_and_release(void *block) {
|
||||
GET_STACK_TRACE_THREAD;
|
||||
asan_block_context_t *context = (asan_block_context_t*)block;
|
||||
if (flags()->verbosity >= 2) {
|
||||
if (common_flags()->verbosity >= 2) {
|
||||
Report("asan_dispatch_call_block_and_release(): "
|
||||
"context: %p, pthread_self: %p\n",
|
||||
block, pthread_self());
|
||||
@ -330,7 +346,7 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
|
||||
dispatch_function_t func) { \
|
||||
GET_STACK_TRACE_THREAD; \
|
||||
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
|
||||
if (flags()->verbosity >= 2) { \
|
||||
if (common_flags()->verbosity >= 2) { \
|
||||
Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \
|
||||
asan_ctxt, pthread_self()); \
|
||||
PRINT_CURRENT_STACK(); \
|
||||
@ -348,7 +364,7 @@ INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
|
||||
dispatch_function_t func) {
|
||||
GET_STACK_TRACE_THREAD;
|
||||
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
|
||||
if (flags()->verbosity >= 2) {
|
||||
if (common_flags()->verbosity >= 2) {
|
||||
Report("dispatch_after_f: %p\n", asan_ctxt);
|
||||
PRINT_CURRENT_STACK();
|
||||
}
|
||||
@ -361,7 +377,7 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
|
||||
dispatch_function_t func) {
|
||||
GET_STACK_TRACE_THREAD;
|
||||
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
|
||||
if (flags()->verbosity >= 2) {
|
||||
if (common_flags()->verbosity >= 2) {
|
||||
Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
|
||||
asan_ctxt, pthread_self());
|
||||
PRINT_CURRENT_STACK();
|
||||
|
@ -12,7 +12,7 @@
|
||||
// Mac-specific ASan definitions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef ASAN_MAC_H
|
||||
#define ASAN__MAC_H
|
||||
#define ASAN_MAC_H
|
||||
|
||||
// CF_RC_BITS, the layout of CFRuntimeBase and __CFStrIsConstant are internal
|
||||
// and subject to change in further CoreFoundation versions. Apple does not
|
||||
@ -36,12 +36,14 @@ typedef struct __CFRuntimeBase {
|
||||
#endif
|
||||
} CFRuntimeBase;
|
||||
|
||||
enum {
|
||||
MACOS_VERSION_UNKNOWN = 0,
|
||||
enum MacosVersion {
|
||||
MACOS_VERSION_UNINITIALIZED = 0,
|
||||
MACOS_VERSION_UNKNOWN,
|
||||
MACOS_VERSION_LEOPARD,
|
||||
MACOS_VERSION_SNOW_LEOPARD,
|
||||
MACOS_VERSION_LION,
|
||||
MACOS_VERSION_MOUNTAIN_LION
|
||||
MACOS_VERSION_MOUNTAIN_LION,
|
||||
MACOS_VERSION_MAVERICKS
|
||||
};
|
||||
|
||||
// Used by asan_malloc_mac.cc and asan_mac.cc
|
||||
@ -49,7 +51,7 @@ extern "C" void __CFInitialize();
|
||||
|
||||
namespace __asan {
|
||||
|
||||
int GetMacosVersion();
|
||||
MacosVersion GetMacosVersion();
|
||||
void MaybeReplaceCFAllocator();
|
||||
|
||||
} // namespace __asan
|
||||
|
@ -105,8 +105,9 @@ INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s)
|
||||
ALIAS("memalign");
|
||||
|
||||
INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_malloc_usable_size(ptr, &stack);
|
||||
GET_CURRENT_PC_BP_SP;
|
||||
(void)sp;
|
||||
return asan_malloc_usable_size(ptr, pc, bp);
|
||||
}
|
||||
|
||||
// We avoid including malloc.h for portability reasons.
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
#include <dlfcn.h>
|
||||
#include <malloc/malloc.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_interceptors.h"
|
||||
@ -42,10 +43,19 @@ INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
|
||||
vm_size_t start_size, unsigned zone_flags) {
|
||||
if (!asan_inited) __asan_init();
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
uptr page_size = GetPageSizeCached();
|
||||
uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size);
|
||||
malloc_zone_t *new_zone =
|
||||
(malloc_zone_t*)asan_malloc(sizeof(asan_zone), &stack);
|
||||
(malloc_zone_t*)asan_memalign(page_size, allocated_size,
|
||||
&stack, FROM_MALLOC);
|
||||
internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone));
|
||||
new_zone->zone_name = NULL; // The name will be changed anyway.
|
||||
if (GetMacosVersion() >= MACOS_VERSION_LION) {
|
||||
// Prevent the client app from overwriting the zone contents.
|
||||
// Library functions that need to modify the zone will set PROT_WRITE on it.
|
||||
// This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
|
||||
mprotect(new_zone, allocated_size, PROT_READ);
|
||||
}
|
||||
return new_zone;
|
||||
}
|
||||
|
||||
|
@ -32,11 +32,13 @@ using namespace __asan; // NOLINT
|
||||
// revisited in the future.
|
||||
|
||||
extern "C" {
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void free(void *ptr) {
|
||||
GET_STACK_TRACE_FREE;
|
||||
return asan_free(ptr, &stack, FROM_MALLOC);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void _free_dbg(void* ptr, int) {
|
||||
free(ptr);
|
||||
}
|
||||
@ -45,38 +47,46 @@ void cfree(void *ptr) {
|
||||
CHECK(!"cfree() should not be used on Windows?");
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *malloc(size_t size) {
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_malloc(size, &stack);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void* _malloc_dbg(size_t size, int , const char*, int) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *calloc(size_t nmemb, size_t size) {
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_calloc(nmemb, size, &stack);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void* _calloc_dbg(size_t n, size_t size, int, const char*, int) {
|
||||
return calloc(n, size);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
|
||||
return calloc(nmemb, size);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *realloc(void *ptr, size_t size) {
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_realloc(ptr, size, &stack);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *_realloc_dbg(void *ptr, size_t size, int) {
|
||||
CHECK(!"_realloc_dbg should not exist!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void* _recalloc(void* p, size_t n, size_t elem_size) {
|
||||
if (!p)
|
||||
return calloc(n, elem_size);
|
||||
@ -86,9 +96,11 @@ void* _recalloc(void* p, size_t n, size_t elem_size) {
|
||||
return realloc(p, size);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
size_t _msize(void *ptr) {
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_malloc_usable_size(ptr, &stack);
|
||||
GET_CURRENT_PC_BP_SP;
|
||||
(void)sp;
|
||||
return asan_malloc_usable_size(ptr, pc, bp);
|
||||
}
|
||||
|
||||
int _CrtDbgReport(int, const char*, int,
|
||||
|
@ -49,6 +49,20 @@
|
||||
// || `[0x24000000, 0x27ffffff]` || ShadowGap ||
|
||||
// || `[0x20000000, 0x23ffffff]` || LowShadow ||
|
||||
// || `[0x00000000, 0x1fffffff]` || LowMem ||
|
||||
//
|
||||
// Default Linux/MIPS mapping:
|
||||
// || `[0x2aaa8000, 0xffffffff]` || HighMem ||
|
||||
// || `[0x0fffd000, 0x2aaa7fff]` || HighShadow ||
|
||||
// || `[0x0bffd000, 0x0fffcfff]` || ShadowGap ||
|
||||
// || `[0x0aaa8000, 0x0bffcfff]` || LowShadow ||
|
||||
// || `[0x00000000, 0x0aaa7fff]` || LowMem ||
|
||||
|
||||
static const u64 kDefaultShadowScale = 3;
|
||||
static const u64 kDefaultShadowOffset32 = 1ULL << 29;
|
||||
static const u64 kDefaultShadowOffset64 = 1ULL << 44;
|
||||
static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G.
|
||||
static const u64 kPPC64_ShadowOffset64 = 1ULL << 41;
|
||||
static const u64 kMIPS32_ShadowOffset32 = 0x0aaa8000;
|
||||
|
||||
#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1
|
||||
extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale;
|
||||
@ -56,22 +70,23 @@ extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset;
|
||||
# define SHADOW_SCALE (__asan_mapping_scale)
|
||||
# define SHADOW_OFFSET (__asan_mapping_offset)
|
||||
#else
|
||||
# define SHADOW_SCALE kDefaultShadowScale
|
||||
# if SANITIZER_ANDROID
|
||||
# define SHADOW_SCALE (3)
|
||||
# define SHADOW_OFFSET (0)
|
||||
# else
|
||||
# define SHADOW_SCALE (3)
|
||||
# if SANITIZER_WORDSIZE == 32
|
||||
# define SHADOW_OFFSET (1 << 29)
|
||||
# if defined(__mips__)
|
||||
# define SHADOW_OFFSET kMIPS32_ShadowOffset32
|
||||
# else
|
||||
# define SHADOW_OFFSET kDefaultShadowOffset32
|
||||
# endif
|
||||
# else
|
||||
# if defined(__powerpc64__)
|
||||
# define SHADOW_OFFSET (1ULL << 41)
|
||||
# define SHADOW_OFFSET kPPC64_ShadowOffset64
|
||||
# elif SANITIZER_MAC
|
||||
# define SHADOW_OFFSET kDefaultShadowOffset64
|
||||
# else
|
||||
# if SANITIZER_MAC
|
||||
# define SHADOW_OFFSET (1ULL << 44)
|
||||
# else
|
||||
# define SHADOW_OFFSET 0x7fff8000ULL
|
||||
# endif
|
||||
# define SHADOW_OFFSET kDefaultShort64bitShadowOffset
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
@ -133,7 +148,6 @@ static uptr kHighMemEnd = 0x7fffffffffffULL;
|
||||
static uptr kMidMemBeg = 0x3000000000ULL;
|
||||
static uptr kMidMemEnd = 0x4fffffffffULL;
|
||||
#else
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
extern uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; // Initialized in __asan_init.
|
||||
#endif
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "asan_poisoning.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
@ -68,7 +69,7 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) {
|
||||
if (!flags()->allow_user_poisoning || size == 0) return;
|
||||
uptr beg_addr = (uptr)addr;
|
||||
uptr end_addr = beg_addr + size;
|
||||
if (flags()->verbosity >= 1) {
|
||||
if (common_flags()->verbosity >= 1) {
|
||||
Printf("Trying to poison memory region [%p, %p)\n",
|
||||
(void*)beg_addr, (void*)end_addr);
|
||||
}
|
||||
@ -110,7 +111,7 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
|
||||
if (!flags()->allow_user_poisoning || size == 0) return;
|
||||
uptr beg_addr = (uptr)addr;
|
||||
uptr end_addr = beg_addr + size;
|
||||
if (flags()->verbosity >= 1) {
|
||||
if (common_flags()->verbosity >= 1) {
|
||||
Printf("Trying to unpoison memory region [%p, %p)\n",
|
||||
(void*)beg_addr, (void*)end_addr);
|
||||
}
|
||||
@ -183,37 +184,37 @@ uptr __asan_region_is_poisoned(uptr beg, uptr size) {
|
||||
|
||||
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||
u16 __sanitizer_unaligned_load16(const u16 *p) {
|
||||
u16 __sanitizer_unaligned_load16(const uu16 *p) {
|
||||
CHECK_SMALL_REGION(p, sizeof(*p), false);
|
||||
return *p;
|
||||
}
|
||||
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||
u32 __sanitizer_unaligned_load32(const u32 *p) {
|
||||
u32 __sanitizer_unaligned_load32(const uu32 *p) {
|
||||
CHECK_SMALL_REGION(p, sizeof(*p), false);
|
||||
return *p;
|
||||
}
|
||||
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||
u64 __sanitizer_unaligned_load64(const u64 *p) {
|
||||
u64 __sanitizer_unaligned_load64(const uu64 *p) {
|
||||
CHECK_SMALL_REGION(p, sizeof(*p), false);
|
||||
return *p;
|
||||
}
|
||||
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_unaligned_store16(u16 *p, u16 x) {
|
||||
void __sanitizer_unaligned_store16(uu16 *p, u16 x) {
|
||||
CHECK_SMALL_REGION(p, sizeof(*p), true);
|
||||
*p = x;
|
||||
}
|
||||
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_unaligned_store32(u32 *p, u32 x) {
|
||||
void __sanitizer_unaligned_store32(uu32 *p, u32 x) {
|
||||
CHECK_SMALL_REGION(p, sizeof(*p), true);
|
||||
*p = x;
|
||||
}
|
||||
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_unaligned_store64(u64 *p, u64 x) {
|
||||
void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
|
||||
CHECK_SMALL_REGION(p, sizeof(*p), true);
|
||||
*p = x;
|
||||
}
|
||||
@ -244,13 +245,55 @@ static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
|
||||
}
|
||||
|
||||
void __asan_poison_stack_memory(uptr addr, uptr size) {
|
||||
if (flags()->verbosity > 0)
|
||||
if (common_flags()->verbosity > 0)
|
||||
Report("poisoning: %p %zx\n", (void*)addr, size);
|
||||
PoisonAlignedStackMemory(addr, size, true);
|
||||
}
|
||||
|
||||
void __asan_unpoison_stack_memory(uptr addr, uptr size) {
|
||||
if (flags()->verbosity > 0)
|
||||
if (common_flags()->verbosity > 0)
|
||||
Report("unpoisoning: %p %zx\n", (void*)addr, size);
|
||||
PoisonAlignedStackMemory(addr, size, false);
|
||||
}
|
||||
|
||||
void __sanitizer_annotate_contiguous_container(void *beg_p, void *end_p,
|
||||
void *old_mid_p,
|
||||
void *new_mid_p) {
|
||||
uptr beg = reinterpret_cast<uptr>(beg_p);
|
||||
uptr end= reinterpret_cast<uptr>(end_p);
|
||||
uptr old_mid = reinterpret_cast<uptr>(old_mid_p);
|
||||
uptr new_mid = reinterpret_cast<uptr>(new_mid_p);
|
||||
uptr granularity = SHADOW_GRANULARITY;
|
||||
CHECK(beg <= end && beg <= old_mid && beg <= new_mid && old_mid <= end &&
|
||||
new_mid <= end && IsAligned(beg, granularity));
|
||||
CHECK_LE(end - beg,
|
||||
FIRST_32_SECOND_64(1UL << 30, 1UL << 34)); // Sanity check.
|
||||
|
||||
uptr a = RoundDownTo(Min(old_mid, new_mid), granularity);
|
||||
uptr c = RoundUpTo(Max(old_mid, new_mid), granularity);
|
||||
uptr b = new_mid;
|
||||
uptr b1 = RoundDownTo(b, granularity);
|
||||
uptr b2 = RoundUpTo(b, granularity);
|
||||
uptr d = old_mid;
|
||||
uptr d1 = RoundDownTo(d, granularity);
|
||||
uptr d2 = RoundUpTo(d, granularity);
|
||||
// Currently we should be in this state:
|
||||
// [a, d1) is good, [d2, c) is bad, [d1, d2) is partially good.
|
||||
// Make a quick sanity check that we are indeed in this state.
|
||||
if (d1 != d2)
|
||||
CHECK_EQ(*(u8*)MemToShadow(d1), d - d1);
|
||||
if (a + granularity <= d1)
|
||||
CHECK_EQ(*(u8*)MemToShadow(a), 0);
|
||||
if (d2 + granularity <= c && c <= end)
|
||||
CHECK_EQ(*(u8 *)MemToShadow(c - granularity), kAsanUserPoisonedMemoryMagic);
|
||||
|
||||
// New state:
|
||||
// [a, b1) is good, [b2, c) is bad, [b1, b2) is partially good.
|
||||
// FIXME: we may want to have a separate poison magic value.
|
||||
PoisonShadow(a, b1 - a, 0);
|
||||
PoisonShadow(b2, c - b2, kAsanUserPoisonedMemoryMagic);
|
||||
if (b1 != b2) {
|
||||
CHECK_EQ(b2 - b1, granularity);
|
||||
*(u8*)MemToShadow(b1) = static_cast<u8>(b - b1);
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
|
||||
ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone(
|
||||
uptr aligned_addr, uptr size, uptr redzone_size, u8 value) {
|
||||
DCHECK(flags()->poison_heap);
|
||||
bool poison_partial = flags()->poison_partial;
|
||||
u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr);
|
||||
for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, shadow++) {
|
||||
if (i + SHADOW_GRANULARITY <= size) {
|
||||
@ -50,7 +51,8 @@ ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone(
|
||||
} else if (i >= size) {
|
||||
*shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable
|
||||
} else {
|
||||
*shadow = size - i; // first size-i bytes are addressable
|
||||
// first size-i bytes are addressable
|
||||
*shadow = poison_partial ? static_cast<u8>(size - i) : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- asan_linux.cc -----------------------------------------------------===//
|
||||
//===-- asan_posix.cc -----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -44,7 +44,7 @@ static void MaybeInstallSigaction(int signum,
|
||||
sigact.sa_flags = SA_SIGINFO;
|
||||
if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
|
||||
CHECK_EQ(0, REAL(sigaction)(signum, &sigact, 0));
|
||||
if (flags()->verbosity >= 1) {
|
||||
if (common_flags()->verbosity >= 1) {
|
||||
Report("Installed the sigaction for signal %d\n", signum);
|
||||
}
|
||||
}
|
||||
@ -71,7 +71,7 @@ void SetAlternateSignalStack() {
|
||||
altstack.ss_flags = 0;
|
||||
altstack.ss_size = kAltStackSize;
|
||||
CHECK_EQ(0, sigaltstack(&altstack, 0));
|
||||
if (flags()->verbosity > 0) {
|
||||
if (common_flags()->verbosity > 0) {
|
||||
Report("Alternative stack for T%d set: [%p,%p)\n",
|
||||
GetCurrentTidOrInvalid(),
|
||||
altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size);
|
||||
@ -116,6 +116,15 @@ void AsanTSDSet(void *tsd) {
|
||||
pthread_setspecific(tsd_key, tsd);
|
||||
}
|
||||
|
||||
void PlatformTSDDtor(void *tsd) {
|
||||
AsanThreadContext *context = (AsanThreadContext*)tsd;
|
||||
if (context->destructor_iterations > 1) {
|
||||
context->destructor_iterations--;
|
||||
CHECK_EQ(0, pthread_setspecific(tsd_key, tsd));
|
||||
return;
|
||||
}
|
||||
AsanThread::TSDDtor(tsd);
|
||||
}
|
||||
} // namespace __asan
|
||||
|
||||
#endif // SANITIZER_LINUX || SANITIZER_MAC
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
#include "sanitizer_common/sanitizer_report_decorator.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
#include "sanitizer_common/sanitizer_symbolizer.h"
|
||||
|
||||
namespace __asan {
|
||||
@ -44,15 +45,6 @@ void AppendToErrorMessageBuffer(const char *buffer) {
|
||||
}
|
||||
|
||||
// ---------------------- Decorator ------------------------------ {{{1
|
||||
bool PrintsToTtyCached() {
|
||||
static int cached = 0;
|
||||
static bool prints_to_tty;
|
||||
if (!cached) { // Ok wrt threads since we are printing only from one thread.
|
||||
prints_to_tty = PrintsToTty();
|
||||
cached = 1;
|
||||
}
|
||||
return prints_to_tty;
|
||||
}
|
||||
class Decorator: private __sanitizer::AnsiColorDecorator {
|
||||
public:
|
||||
Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { }
|
||||
@ -113,7 +105,7 @@ static void PrintShadowBytes(const char *before, u8 *bytes,
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
u8 *p = bytes + i;
|
||||
const char *before = p == guilty ? "[" :
|
||||
p - 1 == guilty ? "" : " ";
|
||||
(p - 1 == guilty && i != 0) ? "" : " ";
|
||||
const char *after = p == guilty ? "]" : "";
|
||||
PrintShadowByte(before, *p, after);
|
||||
}
|
||||
@ -125,7 +117,7 @@ static void PrintLegend() {
|
||||
"application bytes):\n", (int)SHADOW_GRANULARITY);
|
||||
PrintShadowByte(" Addressable: ", 0);
|
||||
Printf(" Partially addressable: ");
|
||||
for (uptr i = 1; i < SHADOW_GRANULARITY; i++)
|
||||
for (u8 i = 1; i < SHADOW_GRANULARITY; i++)
|
||||
PrintShadowByte("", i, " ");
|
||||
Printf("\n");
|
||||
PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic);
|
||||
@ -175,6 +167,11 @@ static void PrintZoneForPointer(uptr ptr, uptr zone_ptr,
|
||||
}
|
||||
}
|
||||
|
||||
static void DescribeThread(AsanThread *t) {
|
||||
if (t)
|
||||
DescribeThread(t->context());
|
||||
}
|
||||
|
||||
// ---------------------- Address Descriptions ------------------- {{{1
|
||||
|
||||
static bool IsASCII(unsigned char c) {
|
||||
@ -184,7 +181,9 @@ static bool IsASCII(unsigned char c) {
|
||||
static 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.
|
||||
return (name[0] == '_' && name[1] == 'Z') ? Demangle(name) : name;
|
||||
return (name[0] == '_' && name[1] == 'Z')
|
||||
? Symbolizer::Get()->Demangle(name)
|
||||
: name;
|
||||
}
|
||||
|
||||
// Check if the global is a zero-terminated ASCII string. If so, print it.
|
||||
@ -264,10 +263,53 @@ const char *ThreadNameWithParenthesis(u32 tid, char buff[],
|
||||
return ThreadNameWithParenthesis(t, buff, buff_len);
|
||||
}
|
||||
|
||||
void PrintAccessAndVarIntersection(const char *var_name,
|
||||
uptr var_beg, uptr var_size,
|
||||
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 = 0;
|
||||
// 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";
|
||||
}
|
||||
Printf(" [%zd, %zd) '%s'", var_beg, var_beg + var_size, var_name);
|
||||
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.
|
||||
Printf("%s <== Memory access at offset %zd %s this variable%s\n",
|
||||
d.Location(), addr, pos_descr, d.EndLocation());
|
||||
} else {
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
struct StackVarDescr {
|
||||
uptr beg;
|
||||
uptr size;
|
||||
const char *name_pos;
|
||||
uptr name_len;
|
||||
};
|
||||
|
||||
bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
||||
AsanThread *t = FindThreadByStackAddress(addr);
|
||||
if (!t) return false;
|
||||
const sptr kBufSize = 4095;
|
||||
const uptr kBufSize = 4095;
|
||||
char buf[kBufSize];
|
||||
uptr offset = 0;
|
||||
uptr frame_pc = 0;
|
||||
@ -306,31 +348,44 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
||||
PrintStack(&alloca_stack);
|
||||
// Report the number of stack objects.
|
||||
char *p;
|
||||
uptr n_objects = internal_simple_strtoll(frame_descr, &p, 10);
|
||||
uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
|
||||
CHECK_GT(n_objects, 0);
|
||||
Printf(" This frame has %zu object(s):\n", n_objects);
|
||||
|
||||
// Report all objects in this frame.
|
||||
InternalScopedBuffer<StackVarDescr> vars(n_objects);
|
||||
for (uptr i = 0; i < n_objects; i++) {
|
||||
uptr beg, size;
|
||||
sptr len;
|
||||
beg = internal_simple_strtoll(p, &p, 10);
|
||||
size = internal_simple_strtoll(p, &p, 10);
|
||||
len = internal_simple_strtoll(p, &p, 10);
|
||||
if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') {
|
||||
uptr len;
|
||||
beg = (uptr)internal_simple_strtoll(p, &p, 10);
|
||||
size = (uptr)internal_simple_strtoll(p, &p, 10);
|
||||
len = (uptr)internal_simple_strtoll(p, &p, 10);
|
||||
if (beg == 0 || size == 0 || *p != ' ') {
|
||||
Printf("AddressSanitizer can't parse the stack frame "
|
||||
"descriptor: |%s|\n", frame_descr);
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
buf[0] = 0;
|
||||
internal_strncat(buf, p, Min(kBufSize, len));
|
||||
vars[i].beg = beg;
|
||||
vars[i].size = size;
|
||||
vars[i].name_pos = p;
|
||||
vars[i].name_len = len;
|
||||
p += len;
|
||||
Printf(" [%zu, %zu) '%s'\n", beg, beg + size, buf);
|
||||
}
|
||||
for (uptr i = 0; i < n_objects; i++) {
|
||||
buf[0] = 0;
|
||||
internal_strncat(buf, vars[i].name_pos,
|
||||
static_cast<uptr>(Min(kBufSize, vars[i].name_len)));
|
||||
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(buf, vars[i].beg, vars[i].size,
|
||||
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"
|
||||
" (longjmp and C++ exceptions *are* supported)\n");
|
||||
DescribeThread(t->context());
|
||||
DescribeThread(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -360,7 +415,11 @@ static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr,
|
||||
|
||||
void DescribeHeapAddress(uptr addr, uptr access_size) {
|
||||
AsanChunkView chunk = FindHeapChunkByAddress(addr);
|
||||
if (!chunk.IsValid()) return;
|
||||
if (!chunk.IsValid()) {
|
||||
Printf("AddressSanitizer can not describe address in more detail "
|
||||
"(wild memory access suspected).\n");
|
||||
return;
|
||||
}
|
||||
DescribeAccessToHeapChunk(chunk, addr, access_size);
|
||||
CHECK(chunk.AllocTid() != kInvalidTid);
|
||||
asanThreadRegistry().CheckLocked();
|
||||
@ -368,13 +427,11 @@ void DescribeHeapAddress(uptr addr, uptr access_size) {
|
||||
GetThreadContextByTidLocked(chunk.AllocTid());
|
||||
StackTrace alloc_stack;
|
||||
chunk.GetAllocStack(&alloc_stack);
|
||||
AsanThread *t = GetCurrentThread();
|
||||
CHECK(t);
|
||||
char tname[128];
|
||||
Decorator d;
|
||||
AsanThreadContext *free_thread = 0;
|
||||
if (chunk.FreeTid() != kInvalidTid) {
|
||||
AsanThreadContext *free_thread =
|
||||
GetThreadContextByTidLocked(chunk.FreeTid());
|
||||
free_thread = GetThreadContextByTidLocked(chunk.FreeTid());
|
||||
Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
|
||||
free_thread->tid,
|
||||
ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
|
||||
@ -386,19 +443,17 @@ void DescribeHeapAddress(uptr addr, uptr access_size) {
|
||||
d.Allocation(), alloc_thread->tid,
|
||||
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
|
||||
d.EndAllocation());
|
||||
PrintStack(&alloc_stack);
|
||||
DescribeThread(t->context());
|
||||
DescribeThread(free_thread);
|
||||
DescribeThread(alloc_thread);
|
||||
} else {
|
||||
Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
|
||||
alloc_thread->tid,
|
||||
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
|
||||
d.EndAllocation());
|
||||
PrintStack(&alloc_stack);
|
||||
DescribeThread(t->context());
|
||||
DescribeThread(alloc_thread);
|
||||
}
|
||||
PrintStack(&alloc_stack);
|
||||
DescribeThread(GetCurrentThread());
|
||||
if (free_thread)
|
||||
DescribeThread(free_thread);
|
||||
DescribeThread(alloc_thread);
|
||||
}
|
||||
|
||||
void DescribeAddress(uptr addr, uptr access_size) {
|
||||
@ -431,7 +486,9 @@ void DescribeThread(AsanThreadContext *context) {
|
||||
context->parent_tid,
|
||||
ThreadNameWithParenthesis(context->parent_tid,
|
||||
tname, sizeof(tname)));
|
||||
PrintStack(&context->stack);
|
||||
uptr stack_size;
|
||||
const uptr *stack_trace = StackDepotGet(context->stack_id, &stack_size);
|
||||
PrintStack(stack_trace, stack_size);
|
||||
// Recursively described parent thread if needed.
|
||||
if (flags()->print_full_thread_history) {
|
||||
AsanThreadContext *parent_context =
|
||||
@ -476,21 +533,11 @@ class ScopedInErrorReport {
|
||||
reporting_thread_tid = GetCurrentTidOrInvalid();
|
||||
Printf("===================================================="
|
||||
"=============\n");
|
||||
if (reporting_thread_tid != kInvalidTid) {
|
||||
// We started reporting an error message. Stop using the fake stack
|
||||
// in case we call an instrumented function from a symbolizer.
|
||||
AsanThread *curr_thread = GetCurrentThread();
|
||||
CHECK(curr_thread);
|
||||
curr_thread->fake_stack().StopUsingFakeStack();
|
||||
}
|
||||
}
|
||||
// Destructor is NORETURN, as functions that report errors are.
|
||||
NORETURN ~ScopedInErrorReport() {
|
||||
// Make sure the current thread is announced.
|
||||
AsanThread *curr_thread = GetCurrentThread();
|
||||
if (curr_thread) {
|
||||
DescribeThread(curr_thread->context());
|
||||
}
|
||||
DescribeThread(GetCurrentThread());
|
||||
// Print memory stats.
|
||||
if (flags()->print_stats)
|
||||
__asan_print_accumulated_stats();
|
||||
@ -502,22 +549,6 @@ class ScopedInErrorReport {
|
||||
}
|
||||
};
|
||||
|
||||
static void ReportSummary(const char *error_type, StackTrace *stack) {
|
||||
if (!stack->size) return;
|
||||
if (IsSymbolizerAvailable()) {
|
||||
AddressInfo ai;
|
||||
// Currently, we include the first stack frame into the report summary.
|
||||
// Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
|
||||
uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
|
||||
SymbolizeCode(pc, &ai, 1);
|
||||
ReportErrorSummary(error_type,
|
||||
StripPathPrefix(ai.file,
|
||||
common_flags()->strip_path_prefix),
|
||||
ai.line, ai.function);
|
||||
}
|
||||
// FIXME: do we need to print anything at all if there is no symbolizer?
|
||||
}
|
||||
|
||||
void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) {
|
||||
ScopedInErrorReport in_report;
|
||||
Decorator d;
|
||||
@ -527,13 +558,13 @@ void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) {
|
||||
(void*)addr, (void*)pc, (void*)sp, (void*)bp,
|
||||
GetCurrentTidOrInvalid());
|
||||
Printf("%s", d.EndWarning());
|
||||
Printf("AddressSanitizer can not provide additional info.\n");
|
||||
GET_STACK_TRACE_FATAL(pc, bp);
|
||||
PrintStack(&stack);
|
||||
ReportSummary("SEGV", &stack);
|
||||
Printf("AddressSanitizer can not provide additional info.\n");
|
||||
ReportErrorSummary("SEGV", &stack);
|
||||
}
|
||||
|
||||
void ReportDoubleFree(uptr addr, StackTrace *stack) {
|
||||
void ReportDoubleFree(uptr addr, StackTrace *free_stack) {
|
||||
ScopedInErrorReport in_report;
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
@ -543,14 +574,15 @@ void ReportDoubleFree(uptr addr, StackTrace *stack) {
|
||||
"thread T%d%s:\n",
|
||||
addr, curr_tid,
|
||||
ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
|
||||
|
||||
Printf("%s", d.EndWarning());
|
||||
PrintStack(stack);
|
||||
CHECK_GT(free_stack->size, 0);
|
||||
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
|
||||
PrintStack(&stack);
|
||||
DescribeHeapAddress(addr, 1);
|
||||
ReportSummary("double-free", stack);
|
||||
ReportErrorSummary("double-free", &stack);
|
||||
}
|
||||
|
||||
void ReportFreeNotMalloced(uptr addr, StackTrace *stack) {
|
||||
void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) {
|
||||
ScopedInErrorReport in_report;
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
@ -560,12 +592,14 @@ void ReportFreeNotMalloced(uptr addr, StackTrace *stack) {
|
||||
"which was not malloc()-ed: %p in thread T%d%s\n", addr,
|
||||
curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
|
||||
Printf("%s", d.EndWarning());
|
||||
PrintStack(stack);
|
||||
CHECK_GT(free_stack->size, 0);
|
||||
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
|
||||
PrintStack(&stack);
|
||||
DescribeHeapAddress(addr, 1);
|
||||
ReportSummary("bad-free", stack);
|
||||
ReportErrorSummary("bad-free", &stack);
|
||||
}
|
||||
|
||||
void ReportAllocTypeMismatch(uptr addr, StackTrace *stack,
|
||||
void ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
|
||||
AllocType alloc_type,
|
||||
AllocType dealloc_type) {
|
||||
static const char *alloc_names[] =
|
||||
@ -579,9 +613,11 @@ void ReportAllocTypeMismatch(uptr addr, StackTrace *stack,
|
||||
Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n",
|
||||
alloc_names[alloc_type], dealloc_names[dealloc_type], addr);
|
||||
Printf("%s", d.EndWarning());
|
||||
PrintStack(stack);
|
||||
CHECK_GT(free_stack->size, 0);
|
||||
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
|
||||
PrintStack(&stack);
|
||||
DescribeHeapAddress(addr, 1);
|
||||
ReportSummary("alloc-dealloc-mismatch", stack);
|
||||
ReportErrorSummary("alloc-dealloc-mismatch", &stack);
|
||||
Report("HINT: if you don't care about these warnings you may set "
|
||||
"ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
|
||||
}
|
||||
@ -596,7 +632,7 @@ void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) {
|
||||
Printf("%s", d.EndWarning());
|
||||
PrintStack(stack);
|
||||
DescribeHeapAddress(addr, 1);
|
||||
ReportSummary("bad-malloc_usable_size", stack);
|
||||
ReportErrorSummary("bad-malloc_usable_size", stack);
|
||||
}
|
||||
|
||||
void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
|
||||
@ -609,7 +645,7 @@ void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
|
||||
Printf("%s", d.EndWarning());
|
||||
PrintStack(stack);
|
||||
DescribeHeapAddress(addr, 1);
|
||||
ReportSummary("bad-__asan_get_allocated_size", stack);
|
||||
ReportErrorSummary("bad-__asan_get_allocated_size", stack);
|
||||
}
|
||||
|
||||
void ReportStringFunctionMemoryRangesOverlap(
|
||||
@ -627,7 +663,7 @@ void ReportStringFunctionMemoryRangesOverlap(
|
||||
PrintStack(stack);
|
||||
DescribeAddress((uptr)offset1, length1);
|
||||
DescribeAddress((uptr)offset2, length2);
|
||||
ReportSummary(bug_type, stack);
|
||||
ReportErrorSummary(bug_type, stack);
|
||||
}
|
||||
|
||||
// ----------------------- Mac-specific reports ----------------- {{{1
|
||||
@ -737,7 +773,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp,
|
||||
PrintStack(&stack);
|
||||
|
||||
DescribeAddress(addr, access_size);
|
||||
ReportSummary(bug_descr, &stack);
|
||||
ReportErrorSummary(bug_descr, &stack);
|
||||
PrintShadowMemoryForAddress(addr);
|
||||
}
|
||||
|
||||
@ -758,6 +794,6 @@ void __asan_describe_address(uptr addr) {
|
||||
#if !SANITIZER_SUPPORTS_WEAK_HOOKS
|
||||
// Provide default implementation of __asan_on_error that does nothing
|
||||
// and may be overriden by user.
|
||||
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE
|
||||
void __asan_on_error() {}
|
||||
#endif
|
||||
|
@ -33,9 +33,9 @@ void DescribeThread(AsanThreadContext *context);
|
||||
|
||||
// Different kinds of error reports.
|
||||
void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr);
|
||||
void NORETURN ReportDoubleFree(uptr addr, StackTrace *stack);
|
||||
void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *stack);
|
||||
void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *stack,
|
||||
void NORETURN ReportDoubleFree(uptr addr, StackTrace *free_stack);
|
||||
void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *free_stack);
|
||||
void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
|
||||
AllocType alloc_type,
|
||||
AllocType dealloc_type);
|
||||
void NORETURN ReportMallocUsableSizeNotOwned(uptr addr,
|
||||
|
@ -13,6 +13,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_interface_internal.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_poisoning.h"
|
||||
@ -26,6 +27,8 @@
|
||||
#include "sanitizer_common/sanitizer_symbolizer.h"
|
||||
#include "lsan/lsan_common.h"
|
||||
|
||||
int __asan_option_detect_stack_use_after_return; // Global interface symbol.
|
||||
|
||||
namespace __asan {
|
||||
|
||||
uptr AsanMappingProfile[kAsanMappingProfileSize];
|
||||
@ -89,7 +92,6 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
|
||||
CHECK((uptr)common_flags()->malloc_context_size <= kStackTraceMax);
|
||||
|
||||
ParseFlag(str, &f->quarantine_size, "quarantine_size");
|
||||
ParseFlag(str, &f->verbosity, "verbosity");
|
||||
ParseFlag(str, &f->redzone, "redzone");
|
||||
CHECK_GE(f->redzone, 16);
|
||||
CHECK(IsPowerOfTwo(f->redzone));
|
||||
@ -101,7 +103,9 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
|
||||
ParseFlag(str, &f->replace_str, "replace_str");
|
||||
ParseFlag(str, &f->replace_intrin, "replace_intrin");
|
||||
ParseFlag(str, &f->mac_ignore_invalid_free, "mac_ignore_invalid_free");
|
||||
ParseFlag(str, &f->use_fake_stack, "use_fake_stack");
|
||||
ParseFlag(str, &f->detect_stack_use_after_return,
|
||||
"detect_stack_use_after_return");
|
||||
ParseFlag(str, &f->uar_stack_size_log, "uar_stack_size_log");
|
||||
ParseFlag(str, &f->max_malloc_fill_size, "max_malloc_fill_size");
|
||||
ParseFlag(str, &f->malloc_fill_byte, "malloc_fill_byte");
|
||||
ParseFlag(str, &f->exitcode, "exitcode");
|
||||
@ -116,30 +120,25 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
|
||||
ParseFlag(str, &f->print_stats, "print_stats");
|
||||
ParseFlag(str, &f->print_legend, "print_legend");
|
||||
ParseFlag(str, &f->atexit, "atexit");
|
||||
ParseFlag(str, &f->coverage, "coverage");
|
||||
ParseFlag(str, &f->disable_core, "disable_core");
|
||||
ParseFlag(str, &f->allow_reexec, "allow_reexec");
|
||||
ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history");
|
||||
ParseFlag(str, &f->log_path, "log_path");
|
||||
ParseFlag(str, &f->poison_heap, "poison_heap");
|
||||
ParseFlag(str, &f->poison_partial, "poison_partial");
|
||||
ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch");
|
||||
ParseFlag(str, &f->use_stack_depot, "use_stack_depot");
|
||||
ParseFlag(str, &f->strict_memcmp, "strict_memcmp");
|
||||
ParseFlag(str, &f->strict_init_order, "strict_init_order");
|
||||
ParseFlag(str, &f->detect_leaks, "detect_leaks");
|
||||
}
|
||||
|
||||
void InitializeFlags(Flags *f, const char *env) {
|
||||
CommonFlags *cf = common_flags();
|
||||
SetCommonFlagDefaults();
|
||||
cf->external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH");
|
||||
cf->symbolize = true;
|
||||
cf->malloc_context_size = kDefaultMallocContextSize;
|
||||
cf->fast_unwind_on_fatal = false;
|
||||
cf->fast_unwind_on_malloc = true;
|
||||
cf->strip_path_prefix = "";
|
||||
|
||||
internal_memset(f, 0, sizeof(*f));
|
||||
f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28;
|
||||
f->verbosity = 0;
|
||||
f->redzone = 16;
|
||||
f->debug = false;
|
||||
f->report_globals = 1;
|
||||
@ -147,7 +146,8 @@ void InitializeFlags(Flags *f, const char *env) {
|
||||
f->replace_str = true;
|
||||
f->replace_intrin = true;
|
||||
f->mac_ignore_invalid_free = false;
|
||||
f->use_fake_stack = true;
|
||||
f->detect_stack_use_after_return = false; // Also needs the compiler flag.
|
||||
f->uar_stack_size_log = 0;
|
||||
f->max_malloc_fill_size = 0x1000; // By default, fill only the first 4K.
|
||||
f->malloc_fill_byte = 0xbe;
|
||||
f->exitcode = ASAN_DEFAULT_FAILURE_EXITCODE;
|
||||
@ -162,25 +162,24 @@ void InitializeFlags(Flags *f, const char *env) {
|
||||
f->print_stats = false;
|
||||
f->print_legend = true;
|
||||
f->atexit = false;
|
||||
f->coverage = false;
|
||||
f->disable_core = (SANITIZER_WORDSIZE == 64);
|
||||
f->allow_reexec = true;
|
||||
f->print_full_thread_history = true;
|
||||
f->log_path = 0;
|
||||
f->poison_heap = true;
|
||||
// Turn off alloc/dealloc mismatch checker on Mac for now.
|
||||
// TODO(glider): Fix known issues and enable this back.
|
||||
f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0);;
|
||||
f->use_stack_depot = true;
|
||||
f->poison_partial = true;
|
||||
// Turn off alloc/dealloc mismatch checker on Mac and Windows for now.
|
||||
// TODO(glider,timurrrr): Fix known issues and enable this back.
|
||||
f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0);
|
||||
f->strict_memcmp = true;
|
||||
f->strict_init_order = false;
|
||||
f->detect_leaks = false;
|
||||
|
||||
// Override from compile definition.
|
||||
ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton());
|
||||
|
||||
// Override from user-specified string.
|
||||
ParseFlagsFromString(f, MaybeCallAsanDefaultOptions());
|
||||
if (flags()->verbosity) {
|
||||
if (common_flags()->verbosity) {
|
||||
Report("Using the defaults from __asan_default_options: %s\n",
|
||||
MaybeCallAsanDefaultOptions());
|
||||
}
|
||||
@ -189,17 +188,17 @@ void InitializeFlags(Flags *f, const char *env) {
|
||||
ParseFlagsFromString(f, env);
|
||||
|
||||
#if !CAN_SANITIZE_LEAKS
|
||||
if (f->detect_leaks) {
|
||||
if (cf->detect_leaks) {
|
||||
Report("%s: detect_leaks is not supported on this platform.\n",
|
||||
SanitizerToolName);
|
||||
f->detect_leaks = false;
|
||||
cf->detect_leaks = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (f->detect_leaks && !f->use_stack_depot) {
|
||||
Report("%s: detect_leaks is ignored (requires use_stack_depot).\n",
|
||||
SanitizerToolName);
|
||||
f->detect_leaks = false;
|
||||
// Make "strict_init_order" imply "check_initialization_order".
|
||||
// TODO(samsonov): Use a single runtime flag for an init-order checker.
|
||||
if (f->strict_init_order) {
|
||||
f->check_initialization_order = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,8 +304,6 @@ static NOINLINE void force_interface_symbols() {
|
||||
case 25: __asan_poison_memory_region(0, 0); break;
|
||||
case 26: __asan_unpoison_memory_region(0, 0); break;
|
||||
case 27: __asan_set_error_exit_code(0); break;
|
||||
case 28: __asan_stack_free(0, 0, 0); break;
|
||||
case 29: __asan_stack_malloc(0, 0); break;
|
||||
case 30: __asan_before_dynamic_init(0); break;
|
||||
case 31: __asan_after_dynamic_init(); break;
|
||||
case 32: __asan_poison_stack_memory(0, 0); break;
|
||||
@ -328,22 +325,12 @@ static void asan_atexit() {
|
||||
|
||||
static void InitializeHighMemEnd() {
|
||||
#if !ASAN_FIXED_MAPPING
|
||||
#if SANITIZER_WORDSIZE == 64
|
||||
# if defined(__powerpc64__)
|
||||
// FIXME:
|
||||
// On PowerPC64 we have two different address space layouts: 44- and 46-bit.
|
||||
// We somehow need to figure our which one we are using now and choose
|
||||
// one of 0x00000fffffffffffUL and 0x00003fffffffffffUL.
|
||||
// Note that with 'ulimit -s unlimited' the stack is moved away from the top
|
||||
// of the address space, so simply checking the stack address is not enough.
|
||||
kHighMemEnd = (1ULL << 44) - 1; // 0x00000fffffffffffUL
|
||||
# else
|
||||
kHighMemEnd = (1ULL << 47) - 1; // 0x00007fffffffffffUL;
|
||||
# endif
|
||||
#else // SANITIZER_WORDSIZE == 32
|
||||
kHighMemEnd = (1ULL << 32) - 1; // 0xffffffff;
|
||||
#endif // SANITIZER_WORDSIZE
|
||||
kHighMemEnd = GetMaxVirtualAddress();
|
||||
// Increase kHighMemEnd to make sure it's properly
|
||||
// aligned together with kHighMemBeg:
|
||||
kHighMemEnd |= SHADOW_GRANULARITY * GetPageSizeCached() - 1;
|
||||
#endif // !ASAN_FIXED_MAPPING
|
||||
CHECK_EQ((kHighMemBeg % GetPageSizeCached()), 0);
|
||||
}
|
||||
|
||||
static void ProtectGap(uptr a, uptr size) {
|
||||
@ -385,6 +372,7 @@ static void PrintAddressSpaceLayout() {
|
||||
}
|
||||
Printf("\n");
|
||||
Printf("red_zone=%zu\n", (uptr)flags()->redzone);
|
||||
Printf("quarantine_size=%zuM\n", (uptr)flags()->quarantine_size >> 20);
|
||||
Printf("malloc_context_size=%zu\n",
|
||||
(uptr)common_flags()->malloc_context_size);
|
||||
|
||||
@ -405,7 +393,7 @@ using namespace __asan; // NOLINT
|
||||
|
||||
#if !SANITIZER_SUPPORTS_WEAK_HOOKS
|
||||
extern "C" {
|
||||
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
const char* __asan_default_options() { return ""; }
|
||||
} // extern "C"
|
||||
#endif
|
||||
@ -438,6 +426,8 @@ void NOINLINE __asan_handle_no_return() {
|
||||
return;
|
||||
}
|
||||
PoisonShadow(bottom, top - bottom, 0);
|
||||
if (curr_thread->has_fake_stack())
|
||||
curr_thread->fake_stack()->HandleNoReturn();
|
||||
}
|
||||
|
||||
void NOINLINE __asan_set_death_callback(void (*callback)(void)) {
|
||||
@ -463,9 +453,11 @@ void __asan_init() {
|
||||
// initialization steps look at flags().
|
||||
const char *options = GetEnv("ASAN_OPTIONS");
|
||||
InitializeFlags(flags(), options);
|
||||
__sanitizer_set_report_path(flags()->log_path);
|
||||
__sanitizer_set_report_path(common_flags()->log_path);
|
||||
__asan_option_detect_stack_use_after_return =
|
||||
flags()->detect_stack_use_after_return;
|
||||
|
||||
if (flags()->verbosity && options) {
|
||||
if (common_flags()->verbosity && options) {
|
||||
Report("Parsed ASAN_OPTIONS: %s\n", options);
|
||||
}
|
||||
|
||||
@ -475,21 +467,16 @@ void __asan_init() {
|
||||
// Setup internal allocator callback.
|
||||
SetLowLevelAllocateCallback(OnLowLevelAllocate);
|
||||
|
||||
if (flags()->atexit) {
|
||||
Atexit(asan_atexit);
|
||||
}
|
||||
|
||||
// interceptors
|
||||
InitializeAsanInterceptors();
|
||||
|
||||
ReplaceSystemMalloc();
|
||||
ReplaceOperatorsNewAndDelete();
|
||||
|
||||
uptr shadow_start = kLowShadowBeg;
|
||||
if (kLowShadowBeg) shadow_start -= GetMmapGranularity();
|
||||
uptr shadow_end = kHighShadowEnd;
|
||||
if (kLowShadowBeg)
|
||||
shadow_start -= GetMmapGranularity();
|
||||
bool full_shadow_is_available =
|
||||
MemoryRangeIsAvailable(shadow_start, shadow_end);
|
||||
MemoryRangeIsAvailable(shadow_start, kHighShadowEnd);
|
||||
|
||||
#if SANITIZER_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING
|
||||
if (!full_shadow_is_available) {
|
||||
@ -498,7 +485,7 @@ void __asan_init() {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (flags()->verbosity)
|
||||
if (common_flags()->verbosity)
|
||||
PrintAddressSpaceLayout();
|
||||
|
||||
if (flags()->disable_core) {
|
||||
@ -515,7 +502,7 @@ void __asan_init() {
|
||||
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
|
||||
} else if (kMidMemBeg &&
|
||||
MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) &&
|
||||
MemoryRangeIsAvailable(kMidMemEnd + 1, shadow_end)) {
|
||||
MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) {
|
||||
CHECK(kLowShadowBeg != kLowShadowEnd);
|
||||
// mmap the low shadow plus at least one page at the left.
|
||||
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd);
|
||||
@ -534,12 +521,18 @@ void __asan_init() {
|
||||
Die();
|
||||
}
|
||||
|
||||
AsanTSDInit(PlatformTSDDtor);
|
||||
InstallSignalHandlers();
|
||||
|
||||
// Allocator should be initialized before starting external symbolizer, as
|
||||
// fork() on Mac locks the allocator.
|
||||
InitializeAllocator();
|
||||
|
||||
// Start symbolizer process if necessary.
|
||||
const char* external_symbolizer = common_flags()->external_symbolizer_path;
|
||||
if (common_flags()->symbolize && external_symbolizer &&
|
||||
external_symbolizer[0]) {
|
||||
InitializeExternalSymbolizer(external_symbolizer);
|
||||
if (common_flags()->symbolize) {
|
||||
Symbolizer::Init(common_flags()->external_symbolizer_path);
|
||||
} else {
|
||||
Symbolizer::Disable();
|
||||
}
|
||||
|
||||
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
|
||||
@ -547,8 +540,16 @@ void __asan_init() {
|
||||
asan_inited = 1;
|
||||
asan_init_is_running = false;
|
||||
|
||||
if (flags()->atexit)
|
||||
Atexit(asan_atexit);
|
||||
|
||||
if (flags()->coverage)
|
||||
Atexit(__sanitizer_cov_dump);
|
||||
|
||||
// interceptors
|
||||
InitTlsSize();
|
||||
|
||||
// Create main thread.
|
||||
AsanTSDInit(AsanThread::TSDDtor);
|
||||
AsanThread *main_thread = AsanThread::Create(0, 0);
|
||||
CreateThreadContextArgs create_main_args = { main_thread, 0 };
|
||||
u32 main_tid = asanThreadRegistry().CreateThread(
|
||||
@ -558,16 +559,14 @@ void __asan_init() {
|
||||
main_thread->ThreadStart(internal_getpid());
|
||||
force_interface_symbols(); // no-op.
|
||||
|
||||
InitializeAllocator();
|
||||
|
||||
#if CAN_SANITIZE_LEAKS
|
||||
__lsan::InitCommonLsan();
|
||||
if (flags()->detect_leaks) {
|
||||
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
|
||||
Atexit(__lsan::DoLeakCheck);
|
||||
}
|
||||
#endif // CAN_SANITIZE_LEAKS
|
||||
|
||||
if (flags()->verbosity) {
|
||||
if (common_flags()->verbosity) {
|
||||
Report("AddressSanitizer Init done\n");
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,12 @@ static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer,
|
||||
: false;
|
||||
}
|
||||
|
||||
void PrintStack(const uptr *trace, uptr size) {
|
||||
StackTrace::PrintStack(trace, size, MaybeCallAsanSymbolize);
|
||||
}
|
||||
|
||||
void PrintStack(StackTrace *stack) {
|
||||
stack->PrintStack(stack->trace, stack->size, common_flags()->symbolize,
|
||||
common_flags()->strip_path_prefix, MaybeCallAsanSymbolize);
|
||||
PrintStack(stack->trace, stack->size);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
@ -37,7 +40,7 @@ void PrintStack(StackTrace *stack) {
|
||||
// and may be overriden by user if he wants to use his own symbolization.
|
||||
// ASan on Windows has its own implementation of this.
|
||||
#if !SANITIZER_WINDOWS && !SANITIZER_SUPPORTS_WEAK_HOOKS
|
||||
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE
|
||||
bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) {
|
||||
return false;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
namespace __asan {
|
||||
|
||||
void PrintStack(StackTrace *stack);
|
||||
void PrintStack(const uptr *trace, uptr size);
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
@ -29,21 +30,21 @@ void PrintStack(StackTrace *stack);
|
||||
// The pc will be in the position 0 of the resulting stack trace.
|
||||
// The bp may refer to the current frame or to the caller's frame.
|
||||
#if SANITIZER_WINDOWS
|
||||
#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \
|
||||
StackTrace stack; \
|
||||
GetStackTrace(&stack, max_s, pc, bp, 0, 0, fast)
|
||||
#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \
|
||||
StackTrace stack; \
|
||||
stack.Unwind(max_s, pc, bp, 0, 0, fast)
|
||||
#else
|
||||
#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \
|
||||
StackTrace stack; \
|
||||
{ \
|
||||
uptr stack_top = 0, stack_bottom = 0; \
|
||||
AsanThread *t; \
|
||||
if (asan_inited && (t = GetCurrentThread())) { \
|
||||
stack_top = t->stack_top(); \
|
||||
stack_bottom = t->stack_bottom(); \
|
||||
} \
|
||||
GetStackTrace(&stack, max_s, pc, bp, \
|
||||
stack_top, stack_bottom, fast); \
|
||||
#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \
|
||||
StackTrace stack; \
|
||||
{ \
|
||||
AsanThread *t; \
|
||||
stack.size = 0; \
|
||||
if (asan_inited && (t = GetCurrentThread()) && !t->isUnwinding()) { \
|
||||
uptr stack_top = t->stack_top(); \
|
||||
uptr stack_bottom = t->stack_bottom(); \
|
||||
ScopedUnwinding unwind_scope(t); \
|
||||
stack.Unwind(max_s, pc, bp, stack_top, stack_bottom, fast); \
|
||||
} \
|
||||
}
|
||||
#endif // SANITIZER_WINDOWS
|
||||
|
||||
|
@ -21,6 +21,10 @@
|
||||
namespace __asan {
|
||||
|
||||
AsanStats::AsanStats() {
|
||||
Clear();
|
||||
}
|
||||
|
||||
void AsanStats::Clear() {
|
||||
CHECK(REAL(memset));
|
||||
REAL(memset)(this, 0, sizeof(AsanStats));
|
||||
}
|
||||
@ -54,8 +58,70 @@ void AsanStats::Print() {
|
||||
malloc_large, malloc_small_slow);
|
||||
}
|
||||
|
||||
void AsanStats::MergeFrom(const AsanStats *stats) {
|
||||
uptr *dst_ptr = reinterpret_cast<uptr*>(this);
|
||||
const uptr *src_ptr = reinterpret_cast<const uptr*>(stats);
|
||||
uptr num_fields = sizeof(*this) / sizeof(uptr);
|
||||
for (uptr i = 0; i < num_fields; i++)
|
||||
dst_ptr[i] += src_ptr[i];
|
||||
}
|
||||
|
||||
static BlockingMutex print_lock(LINKER_INITIALIZED);
|
||||
|
||||
static AsanStats unknown_thread_stats(LINKER_INITIALIZED);
|
||||
static AsanStats dead_threads_stats(LINKER_INITIALIZED);
|
||||
static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED);
|
||||
// Required for malloc_zone_statistics() on OS X. This can't be stored in
|
||||
// per-thread AsanStats.
|
||||
static uptr max_malloced_memory;
|
||||
|
||||
static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) {
|
||||
AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg);
|
||||
AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
|
||||
if (AsanThread *t = tctx->thread)
|
||||
accumulated_stats->MergeFrom(&t->stats());
|
||||
}
|
||||
|
||||
static void GetAccumulatedStats(AsanStats *stats) {
|
||||
stats->Clear();
|
||||
{
|
||||
ThreadRegistryLock l(&asanThreadRegistry());
|
||||
asanThreadRegistry()
|
||||
.RunCallbackForEachThreadLocked(MergeThreadStats, stats);
|
||||
}
|
||||
stats->MergeFrom(&unknown_thread_stats);
|
||||
{
|
||||
BlockingMutexLock lock(&dead_threads_stats_lock);
|
||||
stats->MergeFrom(&dead_threads_stats);
|
||||
}
|
||||
// This is not very accurate: we may miss allocation peaks that happen
|
||||
// between two updates of accumulated_stats_. For more accurate bookkeeping
|
||||
// the maximum should be updated on every malloc(), which is unacceptable.
|
||||
if (max_malloced_memory < stats->malloced) {
|
||||
max_malloced_memory = stats->malloced;
|
||||
}
|
||||
}
|
||||
|
||||
void FlushToDeadThreadStats(AsanStats *stats) {
|
||||
BlockingMutexLock lock(&dead_threads_stats_lock);
|
||||
dead_threads_stats.MergeFrom(stats);
|
||||
stats->Clear();
|
||||
}
|
||||
|
||||
void FillMallocStatistics(AsanMallocStats *malloc_stats) {
|
||||
AsanStats stats;
|
||||
GetAccumulatedStats(&stats);
|
||||
malloc_stats->blocks_in_use = stats.mallocs;
|
||||
malloc_stats->size_in_use = stats.malloced;
|
||||
malloc_stats->max_size_in_use = max_malloced_memory;
|
||||
malloc_stats->size_allocated = stats.mmaped;
|
||||
}
|
||||
|
||||
AsanStats &GetCurrentThreadStats() {
|
||||
AsanThread *t = GetCurrentThread();
|
||||
return (t) ? t->stats() : unknown_thread_stats;
|
||||
}
|
||||
|
||||
static void PrintAccumulatedStats() {
|
||||
AsanStats stats;
|
||||
GetAccumulatedStats(&stats);
|
||||
@ -68,100 +134,36 @@ static void PrintAccumulatedStats() {
|
||||
PrintInternalAllocatorStats();
|
||||
}
|
||||
|
||||
static AsanStats unknown_thread_stats(LINKER_INITIALIZED);
|
||||
static AsanStats accumulated_stats(LINKER_INITIALIZED);
|
||||
// Required for malloc_zone_statistics() on OS X. This can't be stored in
|
||||
// per-thread AsanStats.
|
||||
static uptr max_malloced_memory;
|
||||
static BlockingMutex acc_stats_lock(LINKER_INITIALIZED);
|
||||
|
||||
static void FlushToAccumulatedStatsUnlocked(AsanStats *stats) {
|
||||
acc_stats_lock.CheckLocked();
|
||||
uptr *dst = (uptr*)&accumulated_stats;
|
||||
uptr *src = (uptr*)stats;
|
||||
uptr num_fields = sizeof(*stats) / sizeof(uptr);
|
||||
for (uptr i = 0; i < num_fields; i++) {
|
||||
dst[i] += src[i];
|
||||
src[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void FlushThreadStats(ThreadContextBase *tctx_base, void *arg) {
|
||||
AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
|
||||
if (AsanThread *t = tctx->thread)
|
||||
FlushToAccumulatedStatsUnlocked(&t->stats());
|
||||
}
|
||||
|
||||
static void UpdateAccumulatedStatsUnlocked() {
|
||||
acc_stats_lock.CheckLocked();
|
||||
{
|
||||
ThreadRegistryLock l(&asanThreadRegistry());
|
||||
asanThreadRegistry().RunCallbackForEachThreadLocked(FlushThreadStats, 0);
|
||||
}
|
||||
FlushToAccumulatedStatsUnlocked(&unknown_thread_stats);
|
||||
// This is not very accurate: we may miss allocation peaks that happen
|
||||
// between two updates of accumulated_stats_. For more accurate bookkeeping
|
||||
// the maximum should be updated on every malloc(), which is unacceptable.
|
||||
if (max_malloced_memory < accumulated_stats.malloced) {
|
||||
max_malloced_memory = accumulated_stats.malloced;
|
||||
}
|
||||
}
|
||||
|
||||
void FlushToAccumulatedStats(AsanStats *stats) {
|
||||
BlockingMutexLock lock(&acc_stats_lock);
|
||||
FlushToAccumulatedStatsUnlocked(stats);
|
||||
}
|
||||
|
||||
void GetAccumulatedStats(AsanStats *stats) {
|
||||
BlockingMutexLock lock(&acc_stats_lock);
|
||||
UpdateAccumulatedStatsUnlocked();
|
||||
internal_memcpy(stats, &accumulated_stats, sizeof(accumulated_stats));
|
||||
}
|
||||
|
||||
void FillMallocStatistics(AsanMallocStats *malloc_stats) {
|
||||
BlockingMutexLock lock(&acc_stats_lock);
|
||||
UpdateAccumulatedStatsUnlocked();
|
||||
malloc_stats->blocks_in_use = accumulated_stats.mallocs;
|
||||
malloc_stats->size_in_use = accumulated_stats.malloced;
|
||||
malloc_stats->max_size_in_use = max_malloced_memory;
|
||||
malloc_stats->size_allocated = accumulated_stats.mmaped;
|
||||
}
|
||||
|
||||
AsanStats &GetCurrentThreadStats() {
|
||||
AsanThread *t = GetCurrentThread();
|
||||
return (t) ? t->stats() : unknown_thread_stats;
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
// ---------------------- Interface ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
uptr __asan_get_current_allocated_bytes() {
|
||||
BlockingMutexLock lock(&acc_stats_lock);
|
||||
UpdateAccumulatedStatsUnlocked();
|
||||
uptr malloced = accumulated_stats.malloced;
|
||||
uptr freed = accumulated_stats.freed;
|
||||
AsanStats stats;
|
||||
GetAccumulatedStats(&stats);
|
||||
uptr malloced = stats.malloced;
|
||||
uptr freed = stats.freed;
|
||||
// Return sane value if malloced < freed due to racy
|
||||
// way we update accumulated stats.
|
||||
return (malloced > freed) ? malloced - freed : 1;
|
||||
}
|
||||
|
||||
uptr __asan_get_heap_size() {
|
||||
BlockingMutexLock lock(&acc_stats_lock);
|
||||
UpdateAccumulatedStatsUnlocked();
|
||||
return accumulated_stats.mmaped - accumulated_stats.munmaped;
|
||||
AsanStats stats;
|
||||
GetAccumulatedStats(&stats);
|
||||
return stats.mmaped - stats.munmaped;
|
||||
}
|
||||
|
||||
uptr __asan_get_free_bytes() {
|
||||
BlockingMutexLock lock(&acc_stats_lock);
|
||||
UpdateAccumulatedStatsUnlocked();
|
||||
uptr total_free = accumulated_stats.mmaped
|
||||
- accumulated_stats.munmaped
|
||||
+ accumulated_stats.really_freed
|
||||
+ accumulated_stats.really_freed_redzones;
|
||||
uptr total_used = accumulated_stats.malloced
|
||||
+ accumulated_stats.malloced_redzones;
|
||||
AsanStats stats;
|
||||
GetAccumulatedStats(&stats);
|
||||
uptr total_free = stats.mmaped
|
||||
- stats.munmaped
|
||||
+ stats.really_freed
|
||||
+ stats.really_freed_redzones;
|
||||
uptr total_used = stats.malloced
|
||||
+ stats.malloced_redzones;
|
||||
// Return sane value if total_free < total_used due to racy
|
||||
// way we update accumulated stats.
|
||||
return (total_free > total_used) ? total_free - total_used : 1;
|
||||
|
@ -52,18 +52,16 @@ struct AsanStats {
|
||||
// Default ctor for thread-local stats.
|
||||
AsanStats();
|
||||
|
||||
// Prints formatted stats to stderr.
|
||||
void Print();
|
||||
void Print(); // Prints formatted stats to stderr.
|
||||
void Clear();
|
||||
void MergeFrom(const AsanStats *stats);
|
||||
};
|
||||
|
||||
// Returns stats for GetCurrentThread(), or stats for fake "unknown thread"
|
||||
// if GetCurrentThread() returns 0.
|
||||
AsanStats &GetCurrentThreadStats();
|
||||
// Flushes all thread-local stats to accumulated stats, and makes
|
||||
// a copy of accumulated stats.
|
||||
void GetAccumulatedStats(AsanStats *stats);
|
||||
// Flushes a given stats into accumulated stats.
|
||||
void FlushToAccumulatedStats(AsanStats *stats);
|
||||
// Flushes a given stats into accumulated stats of dead threads.
|
||||
void FlushToDeadThreadStats(AsanStats *stats);
|
||||
|
||||
// A cross-platform equivalent of malloc_statistics_t on Mac OS.
|
||||
struct AsanMallocStats {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "asan_mapping.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
#include "lsan/lsan_common.h"
|
||||
|
||||
namespace __asan {
|
||||
@ -27,9 +28,8 @@ namespace __asan {
|
||||
|
||||
void AsanThreadContext::OnCreated(void *arg) {
|
||||
CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg);
|
||||
if (args->stack) {
|
||||
internal_memcpy(&stack, args->stack, sizeof(stack));
|
||||
}
|
||||
if (args->stack)
|
||||
stack_id = StackDepotPut(args->stack->trace, args->stack->size);
|
||||
thread = args->thread;
|
||||
thread->set_context(this);
|
||||
}
|
||||
@ -39,12 +39,16 @@ void AsanThreadContext::OnFinished() {
|
||||
thread = 0;
|
||||
}
|
||||
|
||||
static char thread_registry_placeholder[sizeof(ThreadRegistry)];
|
||||
// MIPS requires aligned address
|
||||
static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)];
|
||||
static ThreadRegistry *asan_thread_registry;
|
||||
|
||||
static BlockingMutex mu_for_thread_context(LINKER_INITIALIZED);
|
||||
static LowLevelAllocator allocator_for_thread_context;
|
||||
|
||||
static ThreadContextBase *GetAsanThreadContext(u32 tid) {
|
||||
void *mem = MmapOrDie(sizeof(AsanThreadContext), "AsanThreadContext");
|
||||
return new(mem) AsanThreadContext(tid);
|
||||
BlockingMutexLock lock(&mu_for_thread_context);
|
||||
return new(allocator_for_thread_context) AsanThreadContext(tid);
|
||||
}
|
||||
|
||||
ThreadRegistry &asanThreadRegistry() {
|
||||
@ -84,40 +88,68 @@ AsanThread *AsanThread::Create(thread_callback_t start_routine,
|
||||
|
||||
void AsanThread::TSDDtor(void *tsd) {
|
||||
AsanThreadContext *context = (AsanThreadContext*)tsd;
|
||||
if (flags()->verbosity >= 1)
|
||||
if (common_flags()->verbosity >= 1)
|
||||
Report("T%d TSDDtor\n", context->tid);
|
||||
if (context->thread)
|
||||
context->thread->Destroy();
|
||||
}
|
||||
|
||||
void AsanThread::Destroy() {
|
||||
if (flags()->verbosity >= 1) {
|
||||
if (common_flags()->verbosity >= 1) {
|
||||
Report("T%d exited\n", tid());
|
||||
}
|
||||
|
||||
malloc_storage().CommitBack();
|
||||
if (flags()->use_sigaltstack) UnsetAlternateSignalStack();
|
||||
asanThreadRegistry().FinishThread(tid());
|
||||
FlushToAccumulatedStats(&stats_);
|
||||
FlushToDeadThreadStats(&stats_);
|
||||
// We also clear the shadow on thread destruction because
|
||||
// some code may still be executing in later TSD destructors
|
||||
// and we don't want it to have any poisoned stack.
|
||||
ClearShadowForThreadStack();
|
||||
fake_stack().Cleanup();
|
||||
ClearShadowForThreadStackAndTLS();
|
||||
DeleteFakeStack();
|
||||
uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached());
|
||||
UnmapOrDie(this, size);
|
||||
}
|
||||
|
||||
// We want to create the FakeStack lazyly on the first use, but not eralier
|
||||
// than the stack size is known and the procedure has to be async-signal safe.
|
||||
FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
|
||||
uptr stack_size = this->stack_size();
|
||||
if (stack_size == 0) // stack_size is not yet available, don't use FakeStack.
|
||||
return 0;
|
||||
uptr old_val = 0;
|
||||
// fake_stack_ has 3 states:
|
||||
// 0 -- not initialized
|
||||
// 1 -- being initialized
|
||||
// ptr -- initialized
|
||||
// This CAS checks if the state was 0 and if so changes it to state 1,
|
||||
// if that was successfull, it initilizes the pointer.
|
||||
if (atomic_compare_exchange_strong(
|
||||
reinterpret_cast<atomic_uintptr_t *>(&fake_stack_), &old_val, 1UL,
|
||||
memory_order_relaxed)) {
|
||||
uptr stack_size_log = Log2(RoundUpToPowerOfTwo(stack_size));
|
||||
if (flags()->uar_stack_size_log)
|
||||
stack_size_log = static_cast<uptr>(flags()->uar_stack_size_log);
|
||||
fake_stack_ = FakeStack::Create(stack_size_log);
|
||||
SetTLSFakeStack(fake_stack_);
|
||||
return fake_stack_;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AsanThread::Init() {
|
||||
SetThreadStackTopAndBottom();
|
||||
SetThreadStackAndTls();
|
||||
CHECK(AddrIsInMem(stack_bottom_));
|
||||
CHECK(AddrIsInMem(stack_top_ - 1));
|
||||
ClearShadowForThreadStack();
|
||||
if (flags()->verbosity >= 1) {
|
||||
ClearShadowForThreadStackAndTLS();
|
||||
if (common_flags()->verbosity >= 1) {
|
||||
int local = 0;
|
||||
Report("T%d: stack [%p,%p) size 0x%zx; local=%p\n",
|
||||
tid(), (void*)stack_bottom_, (void*)stack_top_,
|
||||
stack_top_ - stack_bottom_, &local);
|
||||
}
|
||||
fake_stack_.Init(stack_size());
|
||||
fake_stack_ = 0; // Will be initialized lazily if needed.
|
||||
AsanPlatformThreadInit();
|
||||
}
|
||||
|
||||
@ -135,22 +167,33 @@ thread_return_t AsanThread::ThreadStart(uptr os_id) {
|
||||
}
|
||||
|
||||
thread_return_t res = start_routine_(arg_);
|
||||
malloc_storage().CommitBack();
|
||||
if (flags()->use_sigaltstack) UnsetAlternateSignalStack();
|
||||
|
||||
this->Destroy();
|
||||
// On POSIX systems we defer this to the TSD destructor. LSan will consider
|
||||
// the thread's memory as non-live from the moment we call Destroy(), even
|
||||
// though that memory might contain pointers to heap objects which will be
|
||||
// cleaned up by a user-defined TSD destructor. Thus, calling Destroy() before
|
||||
// the TSD destructors have run might cause false positives in LSan.
|
||||
if (!SANITIZER_POSIX)
|
||||
this->Destroy();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void AsanThread::SetThreadStackTopAndBottom() {
|
||||
GetThreadStackTopAndBottom(tid() == 0, &stack_top_, &stack_bottom_);
|
||||
void AsanThread::SetThreadStackAndTls() {
|
||||
uptr tls_size = 0;
|
||||
GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size_, &tls_begin_,
|
||||
&tls_size);
|
||||
stack_top_ = stack_bottom_ + stack_size_;
|
||||
tls_end_ = tls_begin_ + tls_size;
|
||||
|
||||
int local;
|
||||
CHECK(AddrIsInStack((uptr)&local));
|
||||
}
|
||||
|
||||
void AsanThread::ClearShadowForThreadStack() {
|
||||
void AsanThread::ClearShadowForThreadStackAndTLS() {
|
||||
PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0);
|
||||
if (tls_begin_ != tls_end_)
|
||||
PoisonShadow(tls_begin_, tls_end_ - tls_begin_, 0);
|
||||
}
|
||||
|
||||
const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset,
|
||||
@ -158,8 +201,8 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset,
|
||||
uptr bottom = 0;
|
||||
if (AddrIsInStack(addr)) {
|
||||
bottom = stack_bottom();
|
||||
} else {
|
||||
bottom = fake_stack().AddrIsInFakeStack(addr);
|
||||
} else if (has_fake_stack()) {
|
||||
bottom = fake_stack()->AddrIsInFakeStack(addr);
|
||||
CHECK(bottom);
|
||||
*offset = addr - bottom;
|
||||
*frame_pc = ((uptr*)bottom)[2];
|
||||
@ -195,13 +238,16 @@ static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
|
||||
void *addr) {
|
||||
AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
|
||||
AsanThread *t = tctx->thread;
|
||||
return (t && t->fake_stack().StackSize() &&
|
||||
(t->fake_stack().AddrIsInFakeStack((uptr)addr) ||
|
||||
t->AddrIsInStack((uptr)addr)));
|
||||
if (!t) return false;
|
||||
if (t->AddrIsInStack((uptr)addr)) return true;
|
||||
if (t->has_fake_stack() && t->fake_stack()->AddrIsInFakeStack((uptr)addr))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
AsanThread *GetCurrentThread() {
|
||||
AsanThreadContext *context = (AsanThreadContext*)AsanTSDGet();
|
||||
AsanThreadContext *context =
|
||||
reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
|
||||
if (!context) {
|
||||
if (SANITIZER_ANDROID) {
|
||||
// On Android, libc constructor is called _after_ asan_init, and cleans up
|
||||
@ -222,7 +268,7 @@ AsanThread *GetCurrentThread() {
|
||||
|
||||
void SetCurrentThread(AsanThread *t) {
|
||||
CHECK(t->context());
|
||||
if (flags()->verbosity >= 2) {
|
||||
if (common_flags()->verbosity >= 2) {
|
||||
Report("SetCurrentThread: %p for thread %p\n",
|
||||
t->context(), (void*)GetThreadSelf());
|
||||
}
|
||||
@ -244,6 +290,20 @@ AsanThread *FindThreadByStackAddress(uptr addr) {
|
||||
(void *)addr));
|
||||
return tctx ? tctx->thread : 0;
|
||||
}
|
||||
|
||||
void EnsureMainThreadIDIsCorrect() {
|
||||
AsanThreadContext *context =
|
||||
reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
|
||||
if (context && (context->tid == 0))
|
||||
context->os_id = GetTid();
|
||||
}
|
||||
|
||||
__asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) {
|
||||
__asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>(
|
||||
__asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id));
|
||||
if (!context) return 0;
|
||||
return context->thread;
|
||||
}
|
||||
} // namespace __asan
|
||||
|
||||
// --- Implementation of LSan-specific functions --- {{{1
|
||||
@ -251,8 +311,23 @@ namespace __lsan {
|
||||
bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
|
||||
uptr *tls_begin, uptr *tls_end,
|
||||
uptr *cache_begin, uptr *cache_end) {
|
||||
// FIXME: Stub.
|
||||
return false;
|
||||
__asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
|
||||
if (!t) return false;
|
||||
*stack_begin = t->stack_bottom();
|
||||
*stack_end = t->stack_top();
|
||||
*tls_begin = t->tls_begin();
|
||||
*tls_end = t->tls_end();
|
||||
// ASan doesn't keep allocator caches in TLS, so these are unused.
|
||||
*cache_begin = 0;
|
||||
*cache_end = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback,
|
||||
void *arg) {
|
||||
__asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
|
||||
if (t && t->has_fake_stack())
|
||||
t->fake_stack()->ForEachFakeFrame(callback, arg);
|
||||
}
|
||||
|
||||
void LockThreadRegistry() {
|
||||
@ -262,4 +337,8 @@ void LockThreadRegistry() {
|
||||
void UnlockThreadRegistry() {
|
||||
__asan::asanThreadRegistry().Unlock();
|
||||
}
|
||||
|
||||
void EnsureMainThreadIDIsCorrect() {
|
||||
__asan::EnsureMainThreadIDIsCorrect();
|
||||
}
|
||||
} // namespace __lsan
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "asan_fake_stack.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_stats.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_thread_registry.h"
|
||||
|
||||
@ -36,11 +37,13 @@ class AsanThreadContext : public ThreadContextBase {
|
||||
explicit AsanThreadContext(int tid)
|
||||
: ThreadContextBase(tid),
|
||||
announced(false),
|
||||
destructor_iterations(kPthreadDestructorIterations),
|
||||
stack_id(0),
|
||||
thread(0) {
|
||||
internal_memset(&stack, 0, sizeof(stack));
|
||||
}
|
||||
bool announced;
|
||||
StackTrace stack;
|
||||
u8 destructor_iterations;
|
||||
u32 stack_id;
|
||||
AsanThread *thread;
|
||||
|
||||
void OnCreated(void *arg);
|
||||
@ -48,7 +51,7 @@ class AsanThreadContext : public ThreadContextBase {
|
||||
};
|
||||
|
||||
// AsanThreadContext objects are never freed, so we need many of them.
|
||||
COMPILER_CHECK(sizeof(AsanThreadContext) <= 4096);
|
||||
COMPILER_CHECK(sizeof(AsanThreadContext) <= 256);
|
||||
|
||||
// AsanThread are stored in TSD and destroyed when the thread dies.
|
||||
class AsanThread {
|
||||
@ -62,7 +65,9 @@ class AsanThread {
|
||||
|
||||
uptr stack_top() { return stack_top_; }
|
||||
uptr stack_bottom() { return stack_bottom_; }
|
||||
uptr stack_size() { return stack_top_ - stack_bottom_; }
|
||||
uptr stack_size() { return stack_size_; }
|
||||
uptr tls_begin() { return tls_begin_; }
|
||||
uptr tls_end() { return tls_end_; }
|
||||
u32 tid() { return context_->tid; }
|
||||
AsanThreadContext *context() { return context_; }
|
||||
void set_context(AsanThreadContext *context) { context_ = context; }
|
||||
@ -73,23 +78,68 @@ class AsanThread {
|
||||
return addr >= stack_bottom_ && addr < stack_top_;
|
||||
}
|
||||
|
||||
FakeStack &fake_stack() { return fake_stack_; }
|
||||
void DeleteFakeStack() {
|
||||
if (!fake_stack_) return;
|
||||
FakeStack *t = fake_stack_;
|
||||
fake_stack_ = 0;
|
||||
SetTLSFakeStack(0);
|
||||
t->Destroy();
|
||||
}
|
||||
|
||||
bool has_fake_stack() {
|
||||
return (reinterpret_cast<uptr>(fake_stack_) > 1);
|
||||
}
|
||||
|
||||
FakeStack *fake_stack() {
|
||||
if (!__asan_option_detect_stack_use_after_return)
|
||||
return 0;
|
||||
if (!has_fake_stack())
|
||||
return AsyncSignalSafeLazyInitFakeStack();
|
||||
return fake_stack_;
|
||||
}
|
||||
|
||||
// True is this thread is currently unwinding stack (i.e. collecting a stack
|
||||
// trace). Used to prevent deadlocks on platforms where libc unwinder calls
|
||||
// malloc internally. See PR17116 for more details.
|
||||
bool isUnwinding() const { return unwinding; }
|
||||
void setUnwinding(bool b) { unwinding = b; }
|
||||
|
||||
AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; }
|
||||
AsanStats &stats() { return stats_; }
|
||||
|
||||
private:
|
||||
AsanThread() {}
|
||||
void SetThreadStackTopAndBottom();
|
||||
void ClearShadowForThreadStack();
|
||||
AsanThread() : unwinding(false) {}
|
||||
void SetThreadStackAndTls();
|
||||
void ClearShadowForThreadStackAndTLS();
|
||||
FakeStack *AsyncSignalSafeLazyInitFakeStack();
|
||||
|
||||
AsanThreadContext *context_;
|
||||
thread_callback_t start_routine_;
|
||||
void *arg_;
|
||||
uptr stack_top_;
|
||||
uptr stack_bottom_;
|
||||
// stack_size_ == stack_top_ - stack_bottom_;
|
||||
// It needs to be set in a async-signal-safe manner.
|
||||
uptr stack_size_;
|
||||
uptr tls_begin_;
|
||||
uptr tls_end_;
|
||||
|
||||
FakeStack fake_stack_;
|
||||
FakeStack *fake_stack_;
|
||||
AsanThreadLocalMallocStorage malloc_storage_;
|
||||
AsanStats stats_;
|
||||
bool unwinding;
|
||||
};
|
||||
|
||||
// ScopedUnwinding is a scope for stacktracing member of a context
|
||||
class ScopedUnwinding {
|
||||
public:
|
||||
explicit ScopedUnwinding(AsanThread *t) : thread(t) {
|
||||
t->setUnwinding(true);
|
||||
}
|
||||
~ScopedUnwinding() { thread->setUnwinding(false); }
|
||||
|
||||
private:
|
||||
AsanThread *thread;
|
||||
};
|
||||
|
||||
struct CreateThreadContextArgs {
|
||||
@ -109,6 +159,8 @@ void SetCurrentThread(AsanThread *t);
|
||||
u32 GetCurrentTidOrInvalid();
|
||||
AsanThread *FindThreadByStackAddress(uptr addr);
|
||||
|
||||
// Used to handle fork().
|
||||
void EnsureMainThreadIDIsCorrect();
|
||||
} // namespace __asan
|
||||
|
||||
#endif // ASAN_THREAD_H
|
||||
|
@ -25,6 +25,14 @@
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
|
||||
extern "C" {
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __asan_should_detect_stack_use_after_return() {
|
||||
__asan_init();
|
||||
return __asan_option_detect_stack_use_after_return;
|
||||
}
|
||||
}
|
||||
|
||||
namespace __asan {
|
||||
|
||||
// ---------------------- Stacktraces, symbols, etc. ---------------- {{{1
|
||||
@ -52,6 +60,9 @@ void AsanTSDSet(void *tsd) {
|
||||
fake_tsd = tsd;
|
||||
}
|
||||
|
||||
void PlatformTSDDtor(void *tsd) {
|
||||
AsanThread::TSDDtor(tsd);
|
||||
}
|
||||
// ---------------------- Various stuff ---------------- {{{1
|
||||
void MaybeReexec() {
|
||||
// No need to re-exec on Windows.
|
||||
|
13
lib/asan/lit_tests/32bitConfig/lit.site.cfg.in
Normal file
13
lib/asan/lit_tests/32bitConfig/lit.site.cfg.in
Normal file
@ -0,0 +1,13 @@
|
||||
## Autogenerated by LLVM/Clang configuration.
|
||||
# Do not edit!
|
||||
|
||||
# Load common config for all compiler-rt lit tests.
|
||||
lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured")
|
||||
|
||||
# Tool-specific config options.
|
||||
config.asan_source_dir = "@ASAN_SOURCE_DIR@"
|
||||
config.bits = "32"
|
||||
|
||||
# Load tool-specific config that would do the real work.
|
||||
lit_config.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/lit.cfg")
|
||||
|
12
lib/asan/lit_tests/64bitConfig/lit.site.cfg.in
Normal file
12
lib/asan/lit_tests/64bitConfig/lit.site.cfg.in
Normal file
@ -0,0 +1,12 @@
|
||||
## Autogenerated by LLVM/Clang configuration.
|
||||
# Do not edit!
|
||||
|
||||
# Load common config for all compiler-rt lit tests.
|
||||
lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured")
|
||||
|
||||
# Tool-specific config options.
|
||||
config.asan_source_dir = "@ASAN_SOURCE_DIR@"
|
||||
config.bits = "64"
|
||||
|
||||
# Load tool-specific config that would do the real work.
|
||||
lit_config.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/lit.cfg")
|
@ -2,8 +2,13 @@ set(ASAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||
set(ASAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..)
|
||||
|
||||
configure_lit_site_cfg(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/64bitConfig/lit.site.cfg.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/64bitConfig/lit.site.cfg
|
||||
)
|
||||
|
||||
configure_lit_site_cfg(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/32bitConfig/lit.site.cfg.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg
|
||||
)
|
||||
|
||||
configure_lit_site_cfg(
|
||||
@ -12,21 +17,26 @@ configure_lit_site_cfg(
|
||||
)
|
||||
|
||||
if(COMPILER_RT_CAN_EXECUTE_TESTS)
|
||||
set(ASAN_TESTSUITES)
|
||||
if(CAN_TARGET_i386)
|
||||
list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig)
|
||||
endif()
|
||||
if(CAN_TARGET_x86_64 OR CAN_TARGET_powerpc64)
|
||||
list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig)
|
||||
endif()
|
||||
# Run ASan tests only if we're sure we may produce working binaries.
|
||||
set(ASAN_TEST_DEPS
|
||||
${SANITIZER_COMMON_LIT_TEST_DEPS}
|
||||
${ASAN_RUNTIME_LIBRARIES}
|
||||
asan_blacklist)
|
||||
asan_runtime_libraries)
|
||||
set(ASAN_TEST_PARAMS
|
||||
asan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
|
||||
)
|
||||
asan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg)
|
||||
if(LLVM_INCLUDE_TESTS)
|
||||
list(APPEND ASAN_TEST_DEPS AsanUnitTests)
|
||||
list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit)
|
||||
endif()
|
||||
add_lit_testsuite(check-asan "Running the AddressSanitizer tests"
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${ASAN_TESTSUITES}
|
||||
PARAMS ${ASAN_TEST_PARAMS}
|
||||
DEPENDS ${ASAN_TEST_DEPS}
|
||||
)
|
||||
DEPENDS ${ASAN_TEST_DEPS})
|
||||
set_target_properties(check-asan PROPERTIES FOLDER "ASan tests")
|
||||
endif()
|
||||
|
@ -1,20 +0,0 @@
|
||||
// Make sure ASan removes the runtime library from DYLD_INSERT_LIBRARIES before
|
||||
// executing other programs.
|
||||
|
||||
// RUN: %clangxx_asan -m64 %s -o %t
|
||||
// RUN: %clangxx -m64 %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \
|
||||
// RUN: -dynamiclib -o darwin-dummy-shared-lib-so.dylib
|
||||
|
||||
// Make sure DYLD_INSERT_LIBRARIES doesn't contain the runtime library before
|
||||
// execl().
|
||||
|
||||
// RUN: %t >/dev/null 2>&1
|
||||
// RUN: DYLD_INSERT_LIBRARIES=darwin-dummy-shared-lib-so.dylib \
|
||||
// RUN: %t 2>&1 | FileCheck %s || exit 1
|
||||
#include <unistd.h>
|
||||
int main() {
|
||||
execl("/bin/bash", "/bin/bash", "-c",
|
||||
"echo DYLD_INSERT_LIBRARIES=$DYLD_INSERT_LIBRARIES", NULL);
|
||||
// CHECK: {{DYLD_INSERT_LIBRARIES=.*darwin-dummy-shared-lib-so.dylib.*}}
|
||||
return 0;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
global-init:*badGlobal*
|
||||
global-init-type:*badNamespace::BadClass*
|
||||
global-init-src:*initialization-blacklist-extra2.cc
|
@ -1,26 +0,0 @@
|
||||
// If user provides his own libc functions, ASan doesn't
|
||||
// intercept these functions.
|
||||
|
||||
// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" long strtol(const char *nptr, char **endptr, int base) {
|
||||
fprintf(stderr, "my_strtol_interceptor\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
char *x = (char*)malloc(10 * sizeof(char));
|
||||
free(x);
|
||||
return (int)strtol(x, 0, 10);
|
||||
// CHECK: my_strtol_interceptor
|
||||
// CHECK-NOT: heap-use-after-free
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// ASan interceptor can be accessed with __interceptor_ prefix.
|
||||
|
||||
// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" void *__interceptor_malloc(size_t size);
|
||||
extern "C" void *malloc(size_t size) {
|
||||
write(2, "malloc call\n", sizeof("malloc call\n") - 1);
|
||||
return __interceptor_malloc(size);
|
||||
}
|
||||
|
||||
int main() {
|
||||
char *x = (char*)malloc(10 * sizeof(char));
|
||||
free(x);
|
||||
return (int)strtol(x, 0, 10);
|
||||
// CHECK: malloc call
|
||||
// CHECK: heap-use-after-free
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// ASan interceptor can be accessed with __interceptor_ prefix.
|
||||
|
||||
// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base);
|
||||
extern "C" long strtol(const char *nptr, char **endptr, int base) {
|
||||
fprintf(stderr, "my_strtol_interceptor\n");
|
||||
return __interceptor_strtol(nptr, endptr, base);
|
||||
}
|
||||
|
||||
int main() {
|
||||
char *x = (char*)malloc(10 * sizeof(char));
|
||||
free(x);
|
||||
return (int)strtol(x, 0, 10);
|
||||
// CHECK: my_strtol_interceptor
|
||||
// CHECK: heap-use-after-free
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
// RUN: %clangxx_asan -m64 -O0 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out
|
||||
// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-64 < %t.out
|
||||
// RUN: %clangxx_asan -m64 -O1 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out
|
||||
// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-64 < %t.out
|
||||
// RUN: %clangxx_asan -m64 -O2 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out
|
||||
// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-64 < %t.out
|
||||
// RUN: %clangxx_asan -m32 -O0 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out
|
||||
// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-32 < %t.out
|
||||
// RUN: %clangxx_asan -m32 -O1 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out
|
||||
// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-32 < %t.out
|
||||
// RUN: %clangxx_asan -m32 -O2 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out
|
||||
// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-32 < %t.out
|
||||
|
||||
// Zero-base shadow only works on x86_64 and i386.
|
||||
// REQUIRES: x86_64-supported-target,i386-supported-target
|
||||
|
||||
#include <string.h>
|
||||
int main(int argc, char **argv) {
|
||||
char x[10];
|
||||
memset(x, 0, 10);
|
||||
int res = x[argc * 10]; // BOOOM
|
||||
// CHECK: {{READ of size 1 at 0x.* thread T0}}
|
||||
// CHECK: {{ #0 0x.* in _?main .*zero-base-shadow.cc:}}[[@LINE-2]]
|
||||
// CHECK: {{Address 0x.* is .* frame}}
|
||||
// CHECK: main
|
||||
|
||||
// Check that shadow for stack memory occupies lower part of address space.
|
||||
// CHECK-64: =>0x0f
|
||||
// CHECK-32: =>0x1
|
||||
return res;
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
// If you're changing this file, please also change
|
||||
// ../Linux/interface_symbols.c
|
||||
|
||||
// RUN: %clang -fsanitize=address -dead_strip -O2 %s -o %t.exe
|
||||
// RUN: %clang_asan -dead_strip -O2 %s -o %t.exe
|
||||
// RUN: rm -f %t.symbols %t.interface
|
||||
|
||||
// RUN: nm -g `otool -L %t.exe | grep "asan_osx_dynamic.dylib" | \
|
||||
@ -16,7 +16,7 @@
|
||||
// RUN: | grep -v "__asan_default_options" \
|
||||
// RUN: | grep -v "__asan_on_error" > %t.symbols
|
||||
|
||||
// RUN: cat %p/../../asan_interface_internal.h \
|
||||
// RUN: cat %p/../../../asan_interface_internal.h \
|
||||
// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \
|
||||
// RUN: | grep -v "OPTIONAL" \
|
||||
// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \
|
||||
@ -33,6 +33,8 @@
|
||||
// RUN: echo __asan_report_store16 >> %t.interface
|
||||
// RUN: echo __asan_report_load_n >> %t.interface
|
||||
// RUN: echo __asan_report_store_n >> %t.interface
|
||||
// RUN: for i in `jot - 0 10`; do echo __asan_stack_malloc_$i >> %t.interface; done
|
||||
// RUN: for i in `jot - 0 10`; do echo __asan_stack_free_$i >> %t.interface; done
|
||||
|
||||
// RUN: cat %t.interface | sort -u | diff %t.symbols -
|
||||
|
@ -0,0 +1,51 @@
|
||||
// Regression test for a bug in malloc_create_zone()
|
||||
// (https://code.google.com/p/address-sanitizer/issues/detail?id=203)
|
||||
// The old implementation of malloc_create_zone() didn't always return a
|
||||
// page-aligned address, so we can only test on a best-effort basis.
|
||||
|
||||
// RUN: %clangxx_asan %s -o %t
|
||||
// RUN: %t 2>&1
|
||||
|
||||
#include <malloc/malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
const int kNumIter = 4096;
|
||||
const int kNumZones = 100;
|
||||
int main() {
|
||||
char *mem[kNumIter * 2];
|
||||
// Allocate memory chunks from different size classes up to 1 page.
|
||||
// (For the case malloc() returns memory chunks in descending order)
|
||||
for (int i = 0; i < kNumIter; i++) {
|
||||
mem[i] = (char*)malloc(8 * i);
|
||||
}
|
||||
// Try to allocate a page-aligned malloc zone. Otherwise the mprotect() call
|
||||
// in malloc_set_zone_name() will silently fail.
|
||||
malloc_zone_t *zone = NULL;
|
||||
bool aligned = false;
|
||||
for (int i = 0; i < kNumZones; i++) {
|
||||
zone = malloc_create_zone(0, 0);
|
||||
if (((uintptr_t)zone & (~0xfff)) == (uintptr_t)zone) {
|
||||
aligned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!aligned) {
|
||||
printf("Warning: couldn't allocate a page-aligned zone.");
|
||||
return 0;
|
||||
}
|
||||
// malloc_set_zone_name() calls mprotect(zone, 4096, PROT_READ | PROT_WRITE),
|
||||
// modifies the zone contents and then calls mprotect(zone, 4096, PROT_READ).
|
||||
malloc_set_zone_name(zone, "foobar");
|
||||
// Allocate memory chunks from different size classes again.
|
||||
for (int i = 0; i < kNumIter; i++) {
|
||||
mem[i + kNumIter] = (char*)malloc(8 * i);
|
||||
}
|
||||
// Access the allocated memory chunks and free them.
|
||||
for (int i = 0; i < kNumIter * 2; i++) {
|
||||
memset(mem[i], 'a', 8 * (i % kNumIter));
|
||||
free(mem[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
20
lib/asan/lit_tests/TestCases/Darwin/malloc_zone-protected.cc
Normal file
20
lib/asan/lit_tests/TestCases/Darwin/malloc_zone-protected.cc
Normal file
@ -0,0 +1,20 @@
|
||||
// Make sure the zones created by malloc_create_zone() are write-protected.
|
||||
#include <malloc/malloc.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// RUN: %clangxx_asan %s -o %t
|
||||
// RUN: not %t 2>&1 | FileCheck %s
|
||||
|
||||
|
||||
void *pwn(malloc_zone_t *unused_zone, size_t unused_size) {
|
||||
printf("PWNED\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
malloc_zone_t *zone = malloc_create_zone(0, 0);
|
||||
zone->malloc = pwn;
|
||||
void *v = malloc_zone_malloc(zone, 1);
|
||||
// CHECK-NOT: PWNED
|
||||
return 0;
|
||||
}
|
@ -2,8 +2,8 @@
|
||||
// This is a regression test for
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=159
|
||||
|
||||
// RUN: %clangxx_asan -m64 %s -o %t
|
||||
// RUN: %clangxx -m64 %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \
|
||||
// RUN: %clangxx_asan %s -o %t
|
||||
// RUN: %clangxx %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \
|
||||
// RUN: -dynamiclib -o darwin-dummy-shared-lib-so.dylib
|
||||
|
||||
// FIXME: the following command line may hang in the case of a regression.
|
@ -0,0 +1,20 @@
|
||||
// Make sure ASan removes the runtime library from DYLD_INSERT_LIBRARIES before
|
||||
// executing other programs.
|
||||
|
||||
// RUN: %clangxx_asan %s -o %t
|
||||
// RUN: %clangxx %p/../Helpers/echo-env.cc -o %T/echo-env
|
||||
// RUN: %clangxx %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \
|
||||
// RUN: -dynamiclib -o %t-darwin-dummy-shared-lib-so.dylib
|
||||
|
||||
// Make sure DYLD_INSERT_LIBRARIES doesn't contain the runtime library before
|
||||
// execl().
|
||||
|
||||
// RUN: %t %T/echo-env >/dev/null 2>&1
|
||||
// RUN: DYLD_INSERT_LIBRARIES=%t-darwin-dummy-shared-lib-so.dylib \
|
||||
// RUN: %t %T/echo-env 2>&1 | FileCheck %s || exit 1
|
||||
#include <unistd.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
execl(argv[1], argv[1], "DYLD_INSERT_LIBRARIES", NULL);
|
||||
// CHECK: {{DYLD_INSERT_LIBRARIES = .*darwin-dummy-shared-lib-so.dylib.*}}
|
||||
return 0;
|
||||
}
|
19
lib/asan/lit_tests/TestCases/Helpers/echo-env.cc
Normal file
19
lib/asan/lit_tests/TestCases/Helpers/echo-env.cc
Normal file
@ -0,0 +1,19 @@
|
||||
// Helper binary for
|
||||
// lit_tests/TestCases/Darwin/unset-insert-libraries-on-exec.cc
|
||||
// Prints the environment variable with the given name.
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s ENVNAME\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
const char *value = getenv(argv[1]);
|
||||
if (value) {
|
||||
printf("%s = %s\n", argv[1], value);
|
||||
} else {
|
||||
printf("%s not set.\n", argv[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
void *bar(void *input);
|
||||
void *glob2 = bar((void*)0x2345);
|
@ -0,0 +1,3 @@
|
||||
global:*badGlobal*=init
|
||||
type:*badNamespace::BadClass*=init
|
||||
src:*initialization-blacklist-extra2.cc=init
|
@ -3,13 +3,13 @@
|
||||
// or gold's flag -Ttext (we try the first flag first, if that fails we
|
||||
// try the second flag).
|
||||
//
|
||||
// RUN: %clangxx_asan -m64 -c %s -o %t.o
|
||||
// RUN: %clangxx_asan -m64 -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext-segment=0x3600000000 ||\
|
||||
// RUN: %clangxx_asan -m64 -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext=0x3600000000
|
||||
// RUN: %clangxx_asan -m64 %t.o %t.so -Wl,-R. -o %t
|
||||
// RUN: %clangxx_asan -c %s -o %t.o
|
||||
// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext-segment=0x3600000000 ||\
|
||||
// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext=0x3600000000
|
||||
// RUN: %clangxx_asan %t.o %t.so -Wl,-R. -o %t
|
||||
// RUN: ASAN_OPTIONS=verbosity=1 %t 2>&1 | FileCheck %s
|
||||
|
||||
// REQUIRES: x86_64-supported-target
|
||||
// REQUIRES: x86_64-supported-target, asan-64-bits
|
||||
#if BUILD_SO
|
||||
int G;
|
||||
int *getG() {
|
@ -1,14 +1,10 @@
|
||||
// Regression test for:
|
||||
// http://code.google.com/p/address-sanitizer/issues/detail?id=37
|
||||
|
||||
// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t | FileCheck %s
|
||||
// RUN: %clangxx_asan -O0 %s -o %t && %t | FileCheck %s
|
||||
// RUN: %clangxx_asan -O1 %s -o %t && %t | FileCheck %s
|
||||
// RUN: %clangxx_asan -O2 %s -o %t && %t | FileCheck %s
|
||||
// RUN: %clangxx_asan -O3 %s -o %t && %t | FileCheck %s
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sched.h>
|
45
lib/asan/lit_tests/TestCases/Linux/coverage.cc
Normal file
45
lib/asan/lit_tests/TestCases/Linux/coverage.cc
Normal file
@ -0,0 +1,45 @@
|
||||
// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %t.so -fPIC
|
||||
// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s -o %t -Wl,-R. %t.so
|
||||
// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1
|
||||
// RUN: %t 2>&1 | FileCheck %s --check-prefix=CHECK-main
|
||||
// RUN: %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-foo
|
||||
// RUN: %t bar 2>&1 | FileCheck %s --check-prefix=CHECK-bar
|
||||
// RUN: %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef SHARED
|
||||
void bar() { printf("bar\n"); }
|
||||
#else
|
||||
__attribute__((noinline))
|
||||
void foo() { printf("foo\n"); }
|
||||
extern void bar();
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
fprintf(stderr, "PID: %d\n", getpid());
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "foo"))
|
||||
foo();
|
||||
if (!strcmp(argv[i], "bar"))
|
||||
bar();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// CHECK-main: PID: [[PID:[0-9]+]]
|
||||
// CHECK-main: [[PID]].sancov: 1 PCs written
|
||||
// CHECK-main-NOT: .so.[[PID]]
|
||||
//
|
||||
// CHECK-foo: PID: [[PID:[0-9]+]]
|
||||
// CHECK-foo: [[PID]].sancov: 2 PCs written
|
||||
// CHECK-foo-NOT: .so.[[PID]]
|
||||
//
|
||||
// CHECK-bar: PID: [[PID:[0-9]+]]
|
||||
// CHECK-bar: [[PID]].sancov: 1 PCs written
|
||||
// CHECK-bar: .so.[[PID]].sancov: 1 PCs written
|
||||
//
|
||||
// CHECK-foo-bar: PID: [[PID:[0-9]+]]
|
||||
// CHECK-foo-bar: [[PID]].sancov: 2 PCs written
|
||||
// CHECK-foo-bar: so.[[PID]].sancov: 1 PCs written
|
@ -1,7 +1,5 @@
|
||||
// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -O0 %s -o %t && %t %p 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -O3 %s -o %t && %t %p 2>&1 | FileCheck %s
|
||||
|
||||
#include <assert.h>
|
||||
#include <glob.h>
|
||||
@ -24,6 +22,7 @@ int main(int argc, char *argv[]) {
|
||||
assert(globbuf.gl_pathc == 2);
|
||||
printf("%zu\n", strlen(globbuf.gl_pathv[0]));
|
||||
printf("%zu\n", strlen(globbuf.gl_pathv[1]));
|
||||
globfree(&globbuf);
|
||||
printf("PASS\n");
|
||||
// CHECK: PASS
|
||||
return 0;
|
23
lib/asan/lit_tests/TestCases/Linux/heap-overflow-large.cc
Normal file
23
lib/asan/lit_tests/TestCases/Linux/heap-overflow-large.cc
Normal file
@ -0,0 +1,23 @@
|
||||
// Regression test for
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=183
|
||||
|
||||
// RUN: %clangxx_asan -O2 %s -o %t
|
||||
// RUN: not %t 12 2>&1 | FileCheck %s
|
||||
// RUN: not %t 100 2>&1 | FileCheck %s
|
||||
// RUN: not %t 10000 2>&1 | FileCheck %s
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int *x = new int[5];
|
||||
memset(x, 0, sizeof(x[0]) * 5);
|
||||
int index = atoi(argv[1]);
|
||||
int res = x[index];
|
||||
// CHECK: AddressSanitizer: {{(heap-buffer-overflow|SEGV)}}
|
||||
// CHECK: #0 0x{{.*}} in main {{.*}}heap-overflow-large.cc:[[@LINE-2]]
|
||||
// CHECK: AddressSanitizer can not {{(provide additional info|describe address in more detail \(wild memory access suspected\))}}
|
||||
// CHECK: SUMMARY: AddressSanitizer: {{(heap-buffer-overflow|SEGV)}}
|
||||
delete[] x;
|
||||
return res;
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O0 %s -o %t && \
|
||||
// RUN: %t 2>&1 | %symbolize | FileCheck %s
|
||||
// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O2 %s -o %t && \
|
||||
// RUN: %t 2>&1 | %symbolize | FileCheck %s
|
||||
// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O2 %s -o %t && \
|
||||
// RUN: %t 2>&1 | %symbolize | FileCheck %s
|
||||
// RUN: export ASAN_OPTIONS=detect_stack_use_after_return=1
|
||||
// RUN: %clangxx_asan -O0 %s -o %t && \
|
||||
// RUN: not %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -O2 %s -o %t && \
|
||||
// RUN: not %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
@ -3,12 +3,10 @@
|
||||
// independently on order in which we list source files (if we specify
|
||||
// strict init-order checking).
|
||||
|
||||
// RUN: %clangxx_asan -m64 -O0 %s %p/../Helpers/initialization-bug-extra.cc -o %t
|
||||
// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 \
|
||||
// RUN: | %symbolize | FileCheck %s
|
||||
// RUN: %clangxx_asan -m64 -O0 %p/../Helpers/initialization-bug-extra.cc %s -o %t
|
||||
// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 \
|
||||
// RUN: | %symbolize | FileCheck %s
|
||||
// RUN: %clangxx_asan -O0 %s %p/../Helpers/initialization-bug-extra.cc -o %t
|
||||
// RUN: ASAN_OPTIONS=strict_init_order=true not %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan -O0 %p/../Helpers/initialization-bug-extra.cc %s -o %t
|
||||
// RUN: ASAN_OPTIONS=strict_init_order=true not %t 2>&1 | FileCheck %s
|
||||
|
||||
// Do not test with optimization -- the error may be optimized away.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user