diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:11:54 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:11:54 +0000 |
commit | cdf4f3055e964bb585f294cf77cb549ead82783f (patch) | |
tree | 7bceeca766b3fbe491245bc926a083f78c35d1de | |
parent | 625108084a3ec7c19c7745004c5af0ed7aa417a9 (diff) | |
download | src-cdf4f3055e964bb585f294cf77cb549ead82783f.tar.gz src-cdf4f3055e964bb585f294cf77cb549ead82783f.zip |
Vendor import of compiler-rt trunk r321017:vendor/compiler-rt/compiler-rt-trunk-r321017
Notes
Notes:
svn path=/vendor/compiler-rt/dist/; revision=326943
svn path=/vendor/compiler-rt/compiler-rt-trunk-r321017/; revision=326944; tag=vendor/compiler-rt/compiler-rt-trunk-r321017
907 files changed, 32685 insertions, 6321 deletions
diff --git a/.arcconfig b/.arcconfig index bc39977f559f..74604d35b6f5 100644 --- a/.arcconfig +++ b/.arcconfig @@ -1,4 +1,4 @@ { - "project_id" : "compiler-rt", + "repository.callsign" : "CRT", "conduit_uri" : "https://reviews.llvm.org/" } diff --git a/.gitignore b/.gitignore index 2a7bdd6a8910..f7d4697ebc5f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ darwin_fat clang_darwin multi_arch *.sw? +*.pyc diff --git a/CMakeLists.txt b/CMakeLists.txt index f997c53410c1..4b953b212829 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,5 @@ # CMake build for CompilerRT. # -# This build assumes that CompilerRT is checked out into the -# 'projects/compiler-rt' or 'runtimes/compiler-rt' inside of an LLVM tree. -# Standalone build system for CompilerRT is not yet ready. -# # An important constraint of the build is that it only produces libraries # based on the ability of the host toolchain to target various platforms. @@ -21,6 +17,12 @@ list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" ) +if(CMAKE_CONFIGURATION_TYPES) + set(CMAKE_CFG_RESOLVED_INTDIR "${CMAKE_CFG_INTDIR}/") +else() + set(CMAKE_CFG_RESOLVED_INTDIR "") +endif() + include(base-config-ix) include(CompilerRTUtils) @@ -30,6 +32,31 @@ option(COMPILER_RT_BUILD_SANITIZERS "Build sanitizers" ON) mark_as_advanced(COMPILER_RT_BUILD_SANITIZERS) option(COMPILER_RT_BUILD_XRAY "Build xray" ON) mark_as_advanced(COMPILER_RT_BUILD_XRAY) +option(COMPILER_RT_BUILD_LIBFUZZER "Build libFuzzer" ON) +mark_as_advanced(COMPILER_RT_BUILD_LIBFUZZER) +option(COMPILER_RT_BUILD_PROFILE "Build profile runtime" ON) +mark_as_advanced(COMPILER_RT_BUILD_PROFILE) +option(COMPILER_RT_BUILD_XRAY_NO_PREINIT "Build xray with no preinit patching" OFF) +mark_as_advanced(COMPILER_RT_BUILD_XRAY_NO_PREINIT) + +set(COMPILER_RT_ASAN_SHADOW_SCALE "" + CACHE STRING "Override the shadow scale to be used in ASan runtime") + +if (NOT COMPILER_RT_ASAN_SHADOW_SCALE STREQUAL "") + # Check that the shadow scale value is valid. + if (NOT (COMPILER_RT_ASAN_SHADOW_SCALE GREATER -1 AND + COMPILER_RT_ASAN_SHADOW_SCALE LESS 8)) + message(FATAL_ERROR " + Invalid ASan Shadow Scale '${COMPILER_RT_ASAN_SHADOW_SCALE}'.") + endif() + + set(COMPILER_RT_ASAN_SHADOW_SCALE_LLVM_FLAG + -mllvm -asan-mapping-scale=${COMPILER_RT_ASAN_SHADOW_SCALE}) + set(COMPILER_RT_ASAN_SHADOW_SCALE_DEFINITION + ASAN_SHADOW_SCALE=${COMPILER_RT_ASAN_SHADOW_SCALE}) + set(COMPILER_RT_ASAN_SHADOW_SCALE_FLAG + -D${COMPILER_RT_ASAN_SHADOW_SCALE_DEFINITION}) +endif() set(COMPILER_RT_BAREMETAL_BUILD OFF CACHE BOOLEAN "Build for a bare-metal target.") @@ -52,12 +79,19 @@ if (COMPILER_RT_STANDALONE_BUILD) set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar") endif() set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit") + set(LLVM_LIT_OUTPUT_DIR "${COMPILER_RT_EXEC_OUTPUT_DIR}") endif() construct_compiler_rt_default_triple() -if ("${COMPILER_RT_DEFAULT_TARGET_ABI}" STREQUAL "androideabi") +if ("${COMPILER_RT_DEFAULT_TARGET_ABI}" MATCHES "hf$") + if (${COMPILER_RT_DEFAULT_TARGET_ARCH} MATCHES "^arm") + set(COMPILER_RT_DEFAULT_TARGET_ARCH "armhf") + endif() +endif() +if ("${COMPILER_RT_DEFAULT_TARGET_ABI}" MATCHES "^android") set(ANDROID 1) endif() +pythonize_bool(ANDROID) set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(COMPILER_RT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) @@ -78,21 +112,63 @@ option(COMPILER_RT_EXTERNALIZE_DEBUGINFO # COMPILER_RT_DEBUG_PYBOOL is used by lit.common.configured.in. pythonize_bool(COMPILER_RT_DEBUG) +include(HandleCompilerRT) include(config-ix) if(APPLE AND SANITIZER_MIN_OSX_VERSION AND SANITIZER_MIN_OSX_VERSION VERSION_LESS "10.9") # Mac OS X prior to 10.9 had problems with exporting symbols from # libc++/libc++abi. - set(use_cxxabi_default OFF) -elseif(MSVC) - set(use_cxxabi_default OFF) + set(cxxabi_supported OFF) else() - set(use_cxxabi_default ON) + set(cxxabi_supported ON) endif() -option(SANITIZER_CAN_USE_CXXABI "Sanitizers can use cxxabi" ${use_cxxabi_default}) +option(SANITIZER_ALLOW_CXXABI "Allow use of C++ ABI details in ubsan" ON) + +set(SANITIZE_CAN_USE_CXXABI OFF) +if (cxxabi_supported AND SANITIZER_ALLOW_CXXABI) + set(SANITIZER_CAN_USE_CXXABI ON) +endif() pythonize_bool(SANITIZER_CAN_USE_CXXABI) +set(SANITIZER_CXX_ABI "default" CACHE STRING + "Specify C++ ABI library to use.") +set(CXXABIS none default libcxxabi libstdc++ libc++) +set_property(CACHE SANITIZER_CXX_ABI PROPERTY STRINGS ;${CXXABIS}) + +if (SANITIZER_CXX_ABI STREQUAL "default") + if (HAVE_LIBCXXABI AND COMPILER_RT_DEFAULT_TARGET_ONLY) + set(SANITIZER_CXX_ABI_LIBNAME "libcxxabi") + set(SANITIZER_CXX_ABI_INTREE 1) + elseif (APPLE) + set(SANITIZER_CXX_ABI_LIBNAME "libcxxabi") + set(SANITIZER_CXX_ABI_SYSTEM 1) + else() + set(SANITIZER_CXX_ABI_LIBNAME "libstdc++") + endif() +else() + set(SANITIZER_CXX_ABI_LIBNAME "${SANITIZER_CXX_ABI}") +endif() + +if (SANITIZER_CXX_ABI_LIBNAME STREQUAL "libcxxabi") + if (SANITIZER_CXX_ABI_INTREE) + if (TARGET unwind_shared OR HAVE_LIBUNWIND) + list(APPEND SANITIZER_CXX_ABI_LIBRARY unwind_shared) + endif() + if (TARGET cxxabi_shared OR HAVE_LIBCXXABI) + list(APPEND SANITIZER_CXX_ABI_LIBRARY cxxabi_shared) + endif() + else() + list(APPEND SANITIZER_CXX_ABI_LIBRARY "c++abi") + endif() +elseif (SANITIZER_CXX_ABI_LIBNAME STREQUAL "libc++") + list(APPEND SANITIZER_CXX_ABI_LIBRARY "c++") +elseif (SANITIZER_CXX_ABI_LIBNAME STREQUAL "libstdc++") + append_list_if(COMPILER_RT_HAS_LIBSTDCXX stdc++ SANITIZER_CXX_ABI_LIBRARY) +endif() + +option(SANITIZER_USE_COMPILER_RT "Use compiler-rt builtins instead of libgcc" OFF) + #================================ # Setup Compiler Flags #================================ @@ -134,7 +210,9 @@ append_list_if(COMPILER_RT_HAS_FUNWIND_TABLES_FLAG -funwind-tables SANITIZER_COM append_list_if(COMPILER_RT_HAS_FNO_STACK_PROTECTOR_FLAG -fno-stack-protector SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_FNO_SANITIZE_SAFE_STACK_FLAG -fno-sanitize=safe-stack SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG -fvisibility=hidden SANITIZER_COMMON_CFLAGS) -append_list_if(COMPILER_RT_HAS_FVISIBILITY_INLINES_HIDDEN_FLAG -fvisibility-inlines-hidden SANITIZER_COMMON_CFLAGS) +if(NOT COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG) + append_list_if(COMPILER_RT_HAS_FVISIBILITY_INLINES_HIDDEN_FLAG -fvisibility-inlines-hidden SANITIZER_COMMON_CFLAGS) +endif() append_list_if(COMPILER_RT_HAS_FNO_LTO_FLAG -fno-lto SANITIZER_COMMON_CFLAGS) # The following is a workaround for powerpc64le. This is the only architecture @@ -176,7 +254,7 @@ append_list_if(COMPILER_RT_DEBUG -DSANITIZER_DEBUG=1 SANITIZER_COMMON_CFLAGS) # always respect the optimization flags set by CMAKE_BUILD_TYPE instead. if (NOT MSVC) - # Build with optimization, unless we're in debug mode. + # Build with optimization, unless we're in debug mode. if(COMPILER_RT_DEBUG) list(APPEND SANITIZER_COMMON_CFLAGS -O0) else() @@ -228,6 +306,28 @@ append_list_if(COMPILER_RT_HAS_WD4391_FLAG /wd4391 SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_WD4722_FLAG /wd4722 SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_WD4800_FLAG /wd4800 SANITIZER_COMMON_CFLAGS) +# Set common link flags. +append_list_if(COMPILER_RT_HAS_NODEFAULTLIBS_FLAG -nodefaultlibs SANITIZER_COMMON_LINK_FLAGS) + +if (SANITIZER_USE_COMPILER_RT) + list(APPEND SANITIZER_COMMON_LINK_FLAGS -rtlib=compiler-rt) + find_compiler_rt_library(builtins COMPILER_RT_BUILTINS_LIBRARY) + list(APPEND SANITIZER_COMMON_LINK_LIBS ${COMPILER_RT_BUILTINS_LIBRARY}) +else() + if (ANDROID) + append_list_if(COMPILER_RT_HAS_GCC_LIB gcc SANITIZER_COMMON_LINK_LIBS) + else() + append_list_if(COMPILER_RT_HAS_GCC_S_LIB gcc_s SANITIZER_COMMON_LINK_LIBS) + endif() +endif() + +append_list_if(COMPILER_RT_HAS_LIBC c SANITIZER_COMMON_LINK_LIBS) + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia") + list(APPEND SANITIZER_COMMON_LINK_FLAGS -Wl,-z,defs,-z,now,-z,relro) + list(APPEND SANITIZER_COMMON_LINK_LIBS zircon) +endif() + # Warnings to turn off for all libraries, not just sanitizers. append_string_if(COMPILER_RT_HAS_WUNUSED_PARAMETER_FLAG -Wno-unused-parameter CMAKE_C_FLAGS CMAKE_CXX_FLAGS) @@ -244,17 +344,15 @@ endif() add_subdirectory(include) -set(COMPILER_RT_LIBCXX_PATH ${LLVM_MAIN_SRC_DIR}/projects/libcxx) -if(EXISTS ${COMPILER_RT_LIBCXX_PATH}/) - set(COMPILER_RT_HAS_LIBCXX_SOURCES TRUE) -else() - set(COMPILER_RT_LIBCXX_PATH ${LLVM_MAIN_SRC_DIR}/../libcxx) - if(EXISTS ${COMPILER_RT_LIBCXX_PATH}/) - set(COMPILER_RT_HAS_LIBCXX_SOURCES TRUE) - else() - set(COMPILER_RT_HAS_LIBCXX_SOURCES FALSE) +foreach(path IN ITEMS ${LLVM_MAIN_SRC_DIR}/projects/libcxx + ${LLVM_MAIN_SRC_DIR}/runtimes/libcxx + ${LLVM_MAIN_SRC_DIR}/../libcxx + ${LLVM_EXTERNAL_LIBCXX_SOURCE_DIR}) + if(IS_DIRECTORY ${path}) + set(COMPILER_RT_LIBCXX_PATH ${path}) + break() endif() -endif() +endforeach() set(COMPILER_RT_LLD_PATH ${LLVM_MAIN_SRC_DIR}/tools/lld) if(EXISTS ${COMPILER_RT_LLD_PATH}/ AND LLVM_TOOL_LLD_BUILD) @@ -274,4 +372,15 @@ add_subdirectory(lib) if(COMPILER_RT_INCLUDE_TESTS) add_subdirectory(unittests) add_subdirectory(test) + if (COMPILER_RT_STANDALONE_BUILD) + # If we have a valid source tree, generate llvm-lit into the bin directory. + # The user can still choose to have the check targets *use* a different lit + # by specifying -DLLVM_EXTERNAL_LIT, but we generate it regardless. + if (EXISTS ${LLVM_MAIN_SRC_DIR}/utils/llvm-lit) + add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/llvm-lit ${CMAKE_CURRENT_BINARY_DIR}/llvm-lit) + elseif(NOT EXISTS ${LLVM_EXTERNAL_LIT}) + message(WARNING "Could not find LLVM source directory and LLVM_EXTERNAL_LIT does not" + "point to a valid file. You will not be able to run tests.") + endif() + endif() endif() diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake index bc5fb9ff722b..d4f636857665 100644 --- a/cmake/Modules/AddCompilerRT.cmake +++ b/cmake/Modules/AddCompilerRT.cmake @@ -56,8 +56,15 @@ function(add_compiler_rt_object_libraries name) foreach(libname ${libnames}) add_library(${libname} OBJECT ${LIB_SOURCES}) + + # Strip out -msse3 if this isn't macOS. + set(target_flags ${LIB_CFLAGS}) + if(APPLE AND NOT "${libname}" MATCHES ".*\.osx.*") + list(REMOVE_ITEM target_flags "-msse3") + endif() + set_target_compile_flags(${libname} - ${CMAKE_CXX_FLAGS} ${extra_cflags_${libname}} ${LIB_CFLAGS}) + ${CMAKE_CXX_FLAGS} ${extra_cflags_${libname}} ${target_flags}) set_property(TARGET ${libname} APPEND PROPERTY COMPILE_DEFINITIONS ${LIB_DEFS}) set_target_properties(${libname} PROPERTIES FOLDER "Compiler-RT Libraries") @@ -86,6 +93,25 @@ function(add_compiler_rt_component name) add_dependencies(compiler-rt ${name}) endfunction() +function(add_asm_sources output) + set(${output} ${ARGN} PARENT_SCOPE) + # Xcode will try to compile asm files as C ('clang -x c'), and that will fail. + if (${CMAKE_GENERATOR} STREQUAL "Xcode") + enable_language(ASM) + else() + # Pass ASM file directly to the C++ compiler. + set_source_files_properties(${ARGN} PROPERTIES LANGUAGE C) + endif() +endfunction() + +macro(set_output_name output name arch) + if(ANDROID AND ${arch} STREQUAL "i386") + set(${output} "${name}-i686${COMPILER_RT_OS_SUFFIX}") + else() + set(${output} "${name}-${arch}${COMPILER_RT_OS_SUFFIX}") + endif() +endmacro() + # Adds static or shared runtime for a list of architectures and operating # systems and puts it in the proper directory in the build and install trees. # add_compiler_rt_runtime(<name> @@ -117,8 +143,14 @@ function(add_compiler_rt_runtime name type) else() set(NO_LTO_FLAGS "") endif() + if(APPLE) foreach(os ${LIB_OS}) + # Strip out -msse3 if this isn't macOS. + list(LENGTH LIB_CFLAGS HAS_EXTRA_CFLAGS) + if(HAS_EXTRA_CFLAGS AND NOT "${os}" MATCHES "^(osx)$") + list(REMOVE_ITEM LIB_CFLAGS "-msse3") + endif() if(type STREQUAL "STATIC") set(libname "${name}_${os}") else() @@ -142,15 +174,15 @@ function(add_compiler_rt_runtime name type) endif() if(type STREQUAL "STATIC") set(libname "${name}-${arch}") - set(output_name_${libname} ${libname}${COMPILER_RT_OS_SUFFIX}) + set_output_name(output_name_${libname} ${name} ${arch}) else() set(libname "${name}-dynamic-${arch}") set(extra_cflags_${libname} ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) set(extra_link_flags_${libname} ${TARGET_${arch}_LINK_FLAGS} ${LIB_LINK_FLAGS}) if(WIN32) - set(output_name_${libname} ${name}_dynamic-${arch}${COMPILER_RT_OS_SUFFIX}) + set_output_name(output_name_${libname} ${name}_dynamic ${arch}) else() - set(output_name_${libname} ${name}-${arch}${COMPILER_RT_OS_SUFFIX}) + set_output_name(output_name_${libname} ${name} ${arch}) endif() endif() set(sources_${libname} ${LIB_SOURCES}) @@ -178,9 +210,18 @@ function(add_compiler_rt_runtime name type) COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=${LIB_PARENT_TARGET} -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") + add_custom_target(install-${LIB_PARENT_TARGET}-stripped + DEPENDS ${LIB_PARENT_TARGET} + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=${LIB_PARENT_TARGET} + -DCMAKE_INSTALL_DO_STRIP=1 + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") set_target_properties(install-${LIB_PARENT_TARGET} PROPERTIES FOLDER "Compiler-RT Misc") + set_target_properties(install-${LIB_PARENT_TARGET}-stripped PROPERTIES + FOLDER "Compiler-RT Misc") add_dependencies(install-compiler-rt install-${LIB_PARENT_TARGET}) + add_dependencies(install-compiler-rt-stripped install-${LIB_PARENT_TARGET}-stripped) endif() endif() @@ -202,10 +243,10 @@ function(add_compiler_rt_runtime name type) set_target_properties(${libname} PROPERTIES OUTPUT_NAME ${output_name_${libname}}) set_target_properties(${libname} PROPERTIES FOLDER "Compiler-RT Runtime") + if(LIB_LINK_LIBS) + target_link_libraries(${libname} ${LIB_LINK_LIBS}) + endif() if(${type} STREQUAL "SHARED") - if(LIB_LINK_LIBS) - target_link_libraries(${libname} ${LIB_LINK_LIBS}) - endif() if(WIN32 AND NOT CYGWIN AND NOT MINGW) set_target_properties(${libname} PROPERTIES IMPORT_PREFIX "") set_target_properties(${libname} PROPERTIES IMPORT_SUFFIX ".lib") @@ -235,10 +276,17 @@ function(add_compiler_rt_runtime name type) COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=${libname} -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") + add_custom_target(install-${libname}-stripped + DEPENDS ${libname} + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=${libname} + -DCMAKE_INSTALL_DO_STRIP=1 + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") # If you have a parent target specified, we bind the new install target # to the parent install target. if(LIB_PARENT_TARGET) add_dependencies(install-${LIB_PARENT_TARGET} install-${libname}) + add_dependencies(install-${LIB_PARENT_TARGET}-stripped install-${libname}-stripped) endif() endif() if(APPLE) @@ -287,24 +335,65 @@ if(MSVC) list(APPEND COMPILER_RT_GTEST_CFLAGS -Wno-deprecated-declarations) endif() +# Compile and register compiler-rt tests. +# generate_compiler_rt_tests(<output object files> <test_suite> <test_name> +# <test architecture> +# KIND <custom prefix> +# SUBDIR <subdirectory for testing binary> +# SOURCES <sources to compile> +# RUNTIME <tests runtime to link in> +# CFLAGS <compile-time flags> +# COMPILE_DEPS <compile-time dependencies> +# DEPS <dependencies> +# LINK_FLAGS <flags to use during linking> +# ) +function(generate_compiler_rt_tests test_objects test_suite testname arch) + cmake_parse_arguments(TEST "" "KIND;RUNTIME;SUBDIR" + "SOURCES;COMPILE_DEPS;DEPS;CFLAGS;LINK_FLAGS" ${ARGN}) + + foreach(source ${TEST_SOURCES}) + sanitizer_test_compile( + "${test_objects}" "${source}" "${arch}" + KIND ${TEST_KIND} + COMPILE_DEPS ${TEST_COMPILE_DEPS} + DEPS ${TEST_DEPS} + CFLAGS ${TEST_CFLAGS} + ) + endforeach() + + set(TEST_DEPS ${${test_objects}}) + + if(NOT "${TEST_RUNTIME}" STREQUAL "") + list(APPEND TEST_DEPS ${TEST_RUNTIME}) + list(APPEND "${test_objects}" $<TARGET_FILE:${TEST_RUNTIME}>) + endif() + + add_compiler_rt_test(${test_suite} "${testname}" "${arch}" + SUBDIR ${TEST_SUBDIR} + OBJECTS ${${test_objects}} + DEPS ${TEST_DEPS} + LINK_FLAGS ${TEST_LINK_FLAGS} + ) + set("${test_objects}" "${${test_objects}}" PARENT_SCOPE) +endfunction() + # Link objects into a single executable with COMPILER_RT_TEST_COMPILER, # using specified link flags. Make executable a part of provided # test_suite. -# add_compiler_rt_test(<test_suite> <test_name> +# add_compiler_rt_test(<test_suite> <test_name> <arch> # SUBDIR <subdirectory for binary> # OBJECTS <object files> # DEPS <deps (e.g. runtime libs)> # LINK_FLAGS <link flags>) -macro(add_compiler_rt_test test_suite test_name) +function(add_compiler_rt_test test_suite test_name arch) cmake_parse_arguments(TEST "" "SUBDIR" "OBJECTS;DEPS;LINK_FLAGS" "" ${ARGN}) - set(output_bin ${CMAKE_CURRENT_BINARY_DIR}) + set(output_dir ${CMAKE_CURRENT_BINARY_DIR}) if(TEST_SUBDIR) - set(output_bin "${output_bin}/${TEST_SUBDIR}") - endif() - if(CMAKE_CONFIGURATION_TYPES) - set(output_bin "${output_bin}/${CMAKE_CFG_INTDIR}") + set(output_dir "${output_dir}/${TEST_SUBDIR}") endif() - set(output_bin "${output_bin}/${test_name}") + set(output_dir "${output_dir}/${CMAKE_CFG_INTDIR}") + file(MAKE_DIRECTORY "${output_dir}") + set(output_bin "${output_dir}/${test_name}") if(MSVC) set(output_bin "${output_bin}.exe") endif() @@ -313,6 +402,10 @@ macro(add_compiler_rt_test test_suite test_name) if(NOT COMPILER_RT_STANDALONE_BUILD) list(APPEND TEST_DEPS clang) endif() + + get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + list(APPEND TEST_LINK_FLAGS ${TARGET_LINK_FLAGS}) + # If we're not on MSVC, include the linker flags from CMAKE but override them # with the provided link flags. This ensures that flags which are required to # link programs at all are included, but the changes needed for the test @@ -323,16 +416,18 @@ macro(add_compiler_rt_test test_suite test_name) set(TEST_LINK_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${TEST_LINK_FLAGS}") separate_arguments(TEST_LINK_FLAGS) endif() - add_custom_target(${test_name} - COMMAND ${COMPILER_RT_TEST_COMPILER} ${TEST_OBJECTS} - -o "${output_bin}" + add_custom_command( + OUTPUT "${output_bin}" + COMMAND ${COMPILER_RT_TEST_COMPILER} ${TEST_OBJECTS} -o "${output_bin}" ${TEST_LINK_FLAGS} - DEPENDS ${TEST_DEPS}) - set_target_properties(${test_name} PROPERTIES FOLDER "Compiler-RT Tests") + DEPENDS ${TEST_DEPS} + ) + add_custom_target(T${test_name} DEPENDS "${output_bin}") + set_target_properties(T${test_name} PROPERTIES FOLDER "Compiler-RT Tests") # Make the test suite depend on the binary. - add_dependencies(${test_suite} ${test_name}) -endmacro() + add_dependencies(${test_suite} T${test_name}) +endfunction() macro(add_compiler_rt_resource_file target_name file_name component) set(src_file "${CMAKE_CURRENT_SOURCE_DIR}/${file_name}") @@ -370,7 +465,7 @@ endmacro(add_compiler_rt_script src name) # DEPS <list of build deps> # CFLAGS <list of compile flags>) macro(add_custom_libcxx name prefix) - if(NOT COMPILER_RT_HAS_LIBCXX_SOURCES) + if(NOT COMPILER_RT_LIBCXX_PATH) message(FATAL_ERROR "libcxx not found!") endif() @@ -440,3 +535,14 @@ function(rt_externalize_debuginfo name) message(FATAL_ERROR "COMPILER_RT_EXTERNALIZE_DEBUGINFO isn't implemented for non-darwin platforms!") endif() endfunction() + + +# Configure lit configuration files, including compiler-rt specific variables. +function(configure_compiler_rt_lit_site_cfg input output) + set_llvm_build_mode() + + string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} COMPILER_RT_RESOLVED_TEST_COMPILER ${COMPILER_RT_TEST_COMPILER}) + string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} COMPILER_RT_RESOLVED_LIBRARY_OUTPUT_DIR ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) + + configure_lit_site_cfg(${input} ${output}) +endfunction() diff --git a/cmake/Modules/CompilerRTCompile.cmake b/cmake/Modules/CompilerRTCompile.cmake index 30663b695955..556ee78969ca 100644 --- a/cmake/Modules/CompilerRTCompile.cmake +++ b/cmake/Modules/CompilerRTCompile.cmake @@ -24,12 +24,44 @@ function(translate_msvc_cflags out_flags msvc_flags) set(${out_flags} "${clang_flags}" PARENT_SCOPE) endfunction() +# Compile a sanitizer test with a freshly built clang +# for a given architecture, adding the result to the object list. +# - obj_list: output list of objects, populated by path +# of a generated object file. +# - source: source file of a test. +# - arch: architecture to compile for. +# sanitizer_test_compile(<obj_list> <source> <arch> +# KIND <custom namespace> +# COMPILE_DEPS <list of compile-time dependencies> +# DEPS <list of dependencies> +# CFLAGS <list of flags> +# ) +function(sanitizer_test_compile obj_list source arch) + cmake_parse_arguments(TEST + "" "" "KIND;COMPILE_DEPS;DEPS;CFLAGS" ${ARGN}) + get_filename_component(basename ${source} NAME) + set(output_obj + "${CMAKE_CFG_RESOLVED_INTDIR}${obj_list}.${basename}.${arch}${TEST_KIND}.o") + + # Write out architecture-specific flags into TARGET_CFLAGS variable. + get_target_flags_for_arch(${arch} TARGET_CFLAGS) + set(COMPILE_DEPS ${TEST_COMPILE_DEPS}) + if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND COMPILE_DEPS ${TEST_DEPS}) + endif() + clang_compile(${output_obj} ${source} + CFLAGS ${TEST_CFLAGS} ${TARGET_CFLAGS} + DEPS ${COMPILE_DEPS}) + list(APPEND ${obj_list} ${output_obj}) + set("${obj_list}" "${${obj_list}}" PARENT_SCOPE) +endfunction() + # Compile a source into an object file with COMPILER_RT_TEST_COMPILER using # a provided compile flags and dependenices. # clang_compile(<object> <source> # CFLAGS <list of compile flags> # DEPS <list of dependencies>) -macro(clang_compile object_file source) +function(clang_compile object_file source) cmake_parse_arguments(SOURCE "" "" "CFLAGS;DEPS" ${ARGN}) get_filename_component(source_rpath ${source} REALPATH) if(NOT COMPILER_RT_STANDALONE_BUILD) @@ -39,6 +71,7 @@ macro(clang_compile object_file source) list(APPEND SOURCE_DEPS CompilerRTUnitTestCheckCxx) endif() string(REGEX MATCH "[.](cc|cpp)$" is_cxx ${source_rpath}) + string(REGEX MATCH "[.](m|mm)$" is_objc ${source_rpath}) if(is_cxx) string(REPLACE " " ";" global_flags "${CMAKE_CXX_FLAGS}") else() @@ -52,6 +85,9 @@ macro(clang_compile object_file source) if (APPLE) set(global_flags ${OSX_SYSROOT_FLAG} ${global_flags}) endif() + if (is_objc) + list(APPEND global_flags -ObjC) + endif() # Ignore unknown warnings. CMAKE_CXX_FLAGS may contain GCC-specific options # which are not supported by Clang. @@ -64,7 +100,7 @@ macro(clang_compile object_file source) ${source_rpath} MAIN_DEPENDENCY ${source} DEPENDS ${SOURCE_DEPS}) -endmacro() +endfunction() # On Darwin, there are no system-wide C++ headers and the just-built clang is # therefore not able to compile C++ files unless they are copied/symlinked into @@ -100,7 +136,7 @@ macro(clang_compiler_add_cxx_check) COMMAND bash -c "${CMD}" COMMENT "Checking that just-built clang can find C++ headers..." VERBATIM) - if (TARGET clang) + if (NOT COMPILER_RT_STANDALONE_BUILD) ADD_DEPENDENCIES(CompilerRTUnitTestCheckCxx clang) endif() endif() diff --git a/cmake/Modules/CompilerRTDarwinUtils.cmake b/cmake/Modules/CompilerRTDarwinUtils.cmake index f646975475bb..a25540bf46d1 100644 --- a/cmake/Modules/CompilerRTDarwinUtils.cmake +++ b/cmake/Modules/CompilerRTDarwinUtils.cmake @@ -200,10 +200,23 @@ macro(darwin_add_builtin_library name suffix) if(DARWIN_${LIB_OS}_SYSROOT) set(sysroot_flag -isysroot ${DARWIN_${LIB_OS}_SYSROOT}) endif() + + # Make a copy of the compilation flags. + set(builtin_cflags ${LIB_CFLAGS}) + + # Strip out any inappropriate flags for the target. + if("${LIB_ARCH}" MATCHES "^(armv7|armv7k|armv7s)$") + set(builtin_cflags "") + foreach(cflag "${LIB_CFLAGS}") + string(REPLACE "-fomit-frame-pointer" "" cflag "${cflag}") + list(APPEND builtin_cflags ${cflag}) + endforeach(cflag) + endif() + set_target_compile_flags(${libname} ${sysroot_flag} ${DARWIN_${LIB_OS}_BUILTIN_MIN_VER_FLAG} - ${LIB_CFLAGS}) + ${builtin_cflags}) set_property(TARGET ${libname} APPEND PROPERTY COMPILE_DEFINITIONS ${LIB_DEFS}) set_target_properties(${libname} PROPERTIES @@ -243,35 +256,6 @@ function(darwin_lipo_libs name) endif() endfunction() -# Filter out generic versions of routines that are re-implemented in -# architecture specific manner. This prevents multiple definitions of the -# same symbols, making the symbol selection non-deterministic. -function(darwin_filter_builtin_sources output_var exclude_or_include excluded_list) - if(exclude_or_include STREQUAL "EXCLUDE") - set(filter_action GREATER) - set(filter_value -1) - elseif(exclude_or_include STREQUAL "INCLUDE") - set(filter_action LESS) - set(filter_value 0) - else() - message(FATAL_ERROR "darwin_filter_builtin_sources called without EXCLUDE|INCLUDE") - endif() - - set(intermediate ${ARGN}) - foreach (_file ${intermediate}) - get_filename_component(_name_we ${_file} NAME_WE) - list(FIND ${excluded_list} ${_name_we} _found) - if(_found ${filter_action} ${filter_value}) - list(REMOVE_ITEM intermediate ${_file}) - elseif(${_file} MATCHES ".*/.*\\.S" OR ${_file} MATCHES ".*/.*\\.c") - get_filename_component(_name ${_file} NAME) - string(REPLACE ".S" ".c" _cname "${_name}") - list(REMOVE_ITEM intermediate ${_cname}) - endif () - endforeach () - set(${output_var} ${intermediate} PARENT_SCOPE) -endfunction() - # Generates builtin libraries for all operating systems specified in ARGN. Each # OS library is constructed by lipo-ing together single-architecture libraries. macro(darwin_add_builtin_libraries) @@ -294,7 +278,7 @@ macro(darwin_add_builtin_libraries) ARCH ${arch} MIN_VERSION ${DARWIN_${os}_BUILTIN_MIN_VER}) - darwin_filter_builtin_sources(filtered_sources + filter_builtin_sources(filtered_sources EXCLUDE ${arch}_${os}_EXCLUDED_BUILTINS ${${arch}_SOURCES}) @@ -316,7 +300,7 @@ macro(darwin_add_builtin_libraries) OS ${os} ARCH ${arch}) - darwin_filter_builtin_sources(filtered_sources + filter_builtin_sources(filtered_sources EXCLUDE ${arch}_${os}_EXCLUDED_BUILTINS ${${arch}_SOURCES}) @@ -411,7 +395,7 @@ macro(darwin_add_embedded_builtin_libraries) set(x86_64_FUNCTIONS ${common_FUNCTIONS}) foreach(arch ${DARWIN_macho_embedded_ARCHS}) - darwin_filter_builtin_sources(${arch}_filtered_sources + filter_builtin_sources(${arch}_filtered_sources INCLUDE ${arch}_FUNCTIONS ${${arch}_SOURCES}) if(NOT ${arch}_filtered_sources) diff --git a/cmake/Modules/CompilerRTUtils.cmake b/cmake/Modules/CompilerRTUtils.cmake index 3b3a0c1532fc..9f79a9b920db 100644 --- a/cmake/Modules/CompilerRTUtils.cmake +++ b/cmake/Modules/CompilerRTUtils.cmake @@ -163,10 +163,11 @@ macro(detect_target_arch) check_symbol_exists(__arm__ "" __ARM) check_symbol_exists(__aarch64__ "" __AARCH64) check_symbol_exists(__x86_64__ "" __X86_64) - check_symbol_exists(__i686__ "" __I686) check_symbol_exists(__i386__ "" __I386) check_symbol_exists(__mips__ "" __MIPS) check_symbol_exists(__mips64__ "" __MIPS64) + check_symbol_exists(__powerpc64__ "" __PPC64) + check_symbol_exists(__powerpc64le__ "" __PPC64LE) check_symbol_exists(__s390x__ "" __S390X) check_symbol_exists(__wasm32__ "" __WEBASSEMBLY32) check_symbol_exists(__wasm64__ "" __WEBASSEMBLY64) @@ -176,14 +177,16 @@ macro(detect_target_arch) add_default_target_arch(aarch64) elseif(__X86_64) add_default_target_arch(x86_64) - elseif(__I686) - add_default_target_arch(i686) elseif(__I386) add_default_target_arch(i386) elseif(__MIPS64) # must be checked before __MIPS add_default_target_arch(mips64) elseif(__MIPS) add_default_target_arch(mips) + elseif(__PPC64) + add_default_target_arch(powerpc64) + elseif(__PPC64LE) + add_default_target_arch(powerpc64le) elseif(__S390X) add_default_target_arch(s390x) elseif(__WEBASSEMBLY32) @@ -273,3 +276,32 @@ macro(construct_compiler_rt_default_triple) set(COMPILER_RT_HAS_EXPLICIT_DEFAULT_TARGET_TRIPLE FALSE) endif() endmacro() + +# Filter out generic versions of routines that are re-implemented in +# architecture specific manner. This prevents multiple definitions of the +# same symbols, making the symbol selection non-deterministic. +function(filter_builtin_sources output_var exclude_or_include excluded_list) + if(exclude_or_include STREQUAL "EXCLUDE") + set(filter_action GREATER) + set(filter_value -1) + elseif(exclude_or_include STREQUAL "INCLUDE") + set(filter_action LESS) + set(filter_value 0) + else() + message(FATAL_ERROR "filter_builtin_sources called without EXCLUDE|INCLUDE") + endif() + + set(intermediate ${ARGN}) + foreach (_file ${intermediate}) + get_filename_component(_name_we ${_file} NAME_WE) + list(FIND ${excluded_list} ${_name_we} _found) + if(_found ${filter_action} ${filter_value}) + list(REMOVE_ITEM intermediate ${_file}) + elseif(${_file} MATCHES ".*/.*\\.S" OR ${_file} MATCHES ".*/.*\\.c") + get_filename_component(_name ${_file} NAME) + string(REPLACE ".S" ".c" _cname "${_name}") + list(REMOVE_ITEM intermediate ${_cname}) + endif () + endforeach () + set(${output_var} ${intermediate} PARENT_SCOPE) +endfunction() diff --git a/cmake/Modules/HandleCompilerRT.cmake b/cmake/Modules/HandleCompilerRT.cmake new file mode 100644 index 000000000000..cff5031ec9b2 --- /dev/null +++ b/cmake/Modules/HandleCompilerRT.cmake @@ -0,0 +1,21 @@ +function(find_compiler_rt_library name dest) + set(dest "" PARENT_SCOPE) + set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${SANITIZER_COMMON_CFLAGS} + "--rtlib=compiler-rt" "--print-libgcc-file-name") + if (CMAKE_CXX_COMPILER_ID MATCHES Clang AND CMAKE_CXX_COMPILER_TARGET) + list(APPEND CLANG_COMMAND "--target=${CMAKE_CXX_COMPILER_TARGET}") + endif() + execute_process( + COMMAND ${CLANG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LIBRARY_FILE + ) + string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE) + string(REPLACE "builtins" "${name}" LIBRARY_FILE "${LIBRARY_FILE}") + if (NOT HAD_ERROR AND EXISTS "${LIBRARY_FILE}") + message(STATUS "Found compiler-rt ${name} library: ${LIBRARY_FILE}") + set(${dest} "${LIBRARY_FILE}" PARENT_SCOPE) + else() + message(STATUS "Failed to find compiler-rt ${name} library") + endif() +endfunction() diff --git a/cmake/base-config-ix.cmake b/cmake/base-config-ix.cmake index b38c6ca96fac..b208f0852408 100644 --- a/cmake/base-config-ix.cmake +++ b/cmake/base-config-ix.cmake @@ -4,11 +4,14 @@ # runtime libraries. include(CheckIncludeFile) +include(CheckCXXSourceCompiles) + check_include_file(unwind.h HAVE_UNWIND_H) # Top level target used to build all compiler-rt libraries. add_custom_target(compiler-rt ALL) add_custom_target(install-compiler-rt) +add_custom_target(install-compiler-rt-stripped) set_target_properties(compiler-rt PROPERTIES FOLDER "Compiler-RT Misc") # Setting these variables from an LLVM build is sufficient that compiler-rt can @@ -86,6 +89,7 @@ if(APPLE) option(COMPILER_RT_ENABLE_IOS "Enable building for iOS" On) option(COMPILER_RT_ENABLE_WATCHOS "Enable building for watchOS - Experimental" Off) option(COMPILER_RT_ENABLE_TVOS "Enable building for tvOS - Experimental" Off) + else() option(COMPILER_RT_DEFAULT_TARGET_ONLY "Build builtins only for the default target" Off) endif() @@ -136,10 +140,6 @@ macro(test_targets) elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "i[2-6]86|x86|amd64") if(NOT MSVC) test_target_arch(x86_64 "" "-m64") - # FIXME: We build runtimes for both i686 and i386, as "clang -m32" may - # target different variant than "$CMAKE_C_COMPILER -m32". This part should - # be gone after we resolve PR14109. - test_target_arch(i686 __i686__ "-m32") test_target_arch(i386 __i386__ "-m32") else() if (CMAKE_SIZEOF_VOID_P EQUAL 4) @@ -149,7 +149,13 @@ macro(test_targets) endif() endif() elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "powerpc") + # Strip out -nodefaultlibs when calling TEST_BIG_ENDIAN. Configuration + # will fail with this option when building with a sanitizer. + cmake_push_check_state() + string(REPLACE "-nodefaultlibs" "" CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) TEST_BIG_ENDIAN(HOST_IS_BIG_ENDIAN) + cmake_pop_check_state() + if(HOST_IS_BIG_ENDIAN) test_target_arch(powerpc64 "" "-m64") else() diff --git a/cmake/builtin-config-ix.cmake b/cmake/builtin-config-ix.cmake index 20bc68476c7d..bdb2529b5ee2 100644 --- a/cmake/builtin-config-ix.cmake +++ b/cmake/builtin-config-ix.cmake @@ -25,7 +25,7 @@ int foo(int x, int y) { set(ARM64 aarch64) set(ARM32 arm armhf armv6m armv7m armv7em armv7 armv7s armv7k) -set(X86 i386 i686) +set(X86 i386) set(X86_64 x86_64) set(MIPS32 mips mipsel) set(MIPS64 mips64 mips64el) @@ -40,7 +40,7 @@ if(APPLE) endif() set(ALL_BUILTIN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} - ${MIPS32} ${MIPS64} ${WASM32} ${WASM64}) + ${MIPS32} ${MIPS64} ${PPC64} ${WASM32} ${WASM64}) include(CompilerRTUtils) include(CompilerRTDarwinUtils) diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake index c329c6a979a3..ada471953663 100644 --- a/cmake/config-ix.cmake +++ b/cmake/config-ix.cmake @@ -1,4 +1,5 @@ include(CMakePushCheckState) +include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) include(CheckLibraryExists) include(CheckSymbolExists) @@ -11,6 +12,32 @@ function(check_linker_flag flag out_var) cmake_pop_check_state() endfunction() +check_library_exists(c fopen "" COMPILER_RT_HAS_LIBC) +if (NOT SANITIZER_USE_COMPILER_RT) + if (ANDROID) + check_library_exists(gcc __gcc_personality_v0 "" COMPILER_RT_HAS_GCC_LIB) + else() + check_library_exists(gcc_s __gcc_personality_v0 "" COMPILER_RT_HAS_GCC_S_LIB) + endif() +endif() + +check_c_compiler_flag(-nodefaultlibs COMPILER_RT_HAS_NODEFAULTLIBS_FLAG) +if (COMPILER_RT_HAS_NODEFAULTLIBS_FLAG) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -nodefaultlibs") + if (COMPILER_RT_HAS_LIBC) + list(APPEND CMAKE_REQUIRED_LIBRARIES c) + endif () + if (SANITIZER_USE_COMPILER_RT) + list(APPEND CMAKE_REQUIRED_FLAGS -rtlib=compiler-rt) + find_compiler_rt_library(builtins COMPILER_RT_BUILTINS_LIBRARY) + list(APPEND CMAKE_REQUIRED_LIBRARIES "${COMPILER_RT_BUILTINS_LIBRARY}") + elseif (COMPILER_RT_HAS_GCC_S_LIB) + list(APPEND CMAKE_REQUIRED_LIBRARIES gcc_s) + elseif (COMPILER_RT_HAS_GCC_LIB) + list(APPEND CMAKE_REQUIRED_LIBRARIES gcc) + endif () +endif () + # CodeGen options. check_cxx_compiler_flag(-fPIC COMPILER_RT_HAS_FPIC_FLAG) check_cxx_compiler_flag(-fPIE COMPILER_RT_HAS_FPIE_FLAG) @@ -73,11 +100,14 @@ check_cxx_compiler_flag(/wd4800 COMPILER_RT_HAS_WD4800_FLAG) check_symbol_exists(__func__ "" COMPILER_RT_HAS_FUNC_SYMBOL) # Libraries. -check_library_exists(c fopen "" COMPILER_RT_HAS_LIBC) check_library_exists(dl dlopen "" COMPILER_RT_HAS_LIBDL) check_library_exists(rt shm_open "" COMPILER_RT_HAS_LIBRT) check_library_exists(m pow "" COMPILER_RT_HAS_LIBM) check_library_exists(pthread pthread_create "" COMPILER_RT_HAS_LIBPTHREAD) +if (ANDROID AND COMPILER_RT_HAS_LIBDL) + # Android's libstdc++ has a dependency on libdl. + list(APPEND CMAKE_REQUIRED_LIBRARIES dl) +endif() check_library_exists(stdc++ __cxa_throw "" COMPILER_RT_HAS_LIBSTDCXX) # Linker flags. @@ -144,7 +174,7 @@ endmacro() set(ARM64 aarch64) set(ARM32 arm armhf) -set(X86 i386 i686) +set(X86 i386) set(X86_64 x86_64) set(MIPS32 mips mipsel) set(MIPS64 mips64 mips64el) @@ -164,23 +194,29 @@ set(ALL_SANITIZER_COMMON_SUPPORTED_ARCH ${X86} ${X86_64} ${PPC64} set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} ${PPC64} ${S390X}) set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64}) +set(ALL_FUZZER_SUPPORTED_ARCH x86_64) if(APPLE) set(ALL_LSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${MIPS64} ${ARM64}) else() - set(ALL_LSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${MIPS64} ${ARM64} ${ARM32}) + set(ALL_LSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${MIPS64} ${ARM64} ${ARM32} ${PPC64}) endif() set(ALL_MSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64}) +set(ALL_HWASAN_SUPPORTED_ARCH ${ARM64}) set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC64} ${MIPS32} ${MIPS64} ${S390X}) set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64}) set(ALL_UBSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} ${PPC64} ${S390X}) set(ALL_SAFESTACK_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM64} ${MIPS32} ${MIPS64}) -set(ALL_CFI_SUPPORTED_ARCH ${X86} ${X86_64} ${MIPS64}) +set(ALL_CFI_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${MIPS64}) set(ALL_ESAN_SUPPORTED_ARCH ${X86_64} ${MIPS64}) set(ALL_SCUDO_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64}) +if(APPLE) +set(ALL_XRAY_SUPPORTED_ARCH ${X86_64}) +else() set(ALL_XRAY_SUPPORTED_ARCH ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} powerpc64le) +endif() if(APPLE) include(CompilerRTDarwinUtils) @@ -225,6 +261,7 @@ if(APPLE) set(SANITIZER_COMMON_SUPPORTED_OS osx) set(PROFILE_SUPPORTED_OS osx) set(TSAN_SUPPORTED_OS osx) + set(XRAY_SUPPORTED_OS osx) if(NOT SANITIZER_MIN_OSX_VERSION) string(REGEX MATCH "-mmacosx-version-min=([.0-9]+)" MACOSX_VERSION_MIN_FLAG "${CMAKE_CXX_FLAGS}") @@ -360,6 +397,9 @@ if(APPLE) list_intersect(MSAN_SUPPORTED_ARCH ALL_MSAN_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) + list_intersect(HWASAN_SUPPORTED_ARCH + ALL_HWASAN_SUPPORTED_ARCH + SANITIZER_COMMON_SUPPORTED_ARCH) list_intersect(PROFILE_SUPPORTED_ARCH ALL_PROFILE_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) @@ -381,9 +421,13 @@ if(APPLE) list_intersect(SCUDO_SUPPORTED_ARCH ALL_SCUDO_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) + list_intersect(FUZZER_SUPPORTED_ARCH + ALL_FUZZER_SUPPORTED_ARCH + ALL_SANITIZER_COMMON_SUPPORTED_ARCH) list_intersect(XRAY_SUPPORTED_ARCH ALL_XRAY_SUPPORTED_ARCH - SANITIZER_COMMON_SUPPORTED_ARCH) + SANITIZER_COMMON_SUPPORTED_ARCH) + else() # Architectures supported by compiler-rt libraries. filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH @@ -395,9 +439,11 @@ else() filter_available_targets(UBSAN_COMMON_SUPPORTED_ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH}) filter_available_targets(ASAN_SUPPORTED_ARCH ${ALL_ASAN_SUPPORTED_ARCH}) + filter_available_targets(FUZZER_SUPPORTED_ARCH ${ALL_FUZZER_SUPPORTED_ARCH}) filter_available_targets(DFSAN_SUPPORTED_ARCH ${ALL_DFSAN_SUPPORTED_ARCH}) filter_available_targets(LSAN_SUPPORTED_ARCH ${ALL_LSAN_SUPPORTED_ARCH}) filter_available_targets(MSAN_SUPPORTED_ARCH ${ALL_MSAN_SUPPORTED_ARCH}) + filter_available_targets(HWASAN_SUPPORTED_ARCH ${ALL_HWASAN_SUPPORTED_ARCH}) filter_available_targets(PROFILE_SUPPORTED_ARCH ${ALL_PROFILE_SUPPORTED_ARCH}) filter_available_targets(TSAN_SUPPORTED_ARCH ${ALL_TSAN_SUPPORTED_ARCH}) filter_available_targets(UBSAN_SUPPORTED_ARCH ${ALL_UBSAN_SUPPORTED_ARCH}) @@ -434,13 +480,13 @@ else() set(OS_NAME "${CMAKE_SYSTEM_NAME}") endif() -set(ALL_SANITIZERS asan;dfsan;msan;tsan;safestack;cfi;esan;scudo) -set(COMPILER_RT_SANITIZERS_TO_BUILD ${ALL_SANITIZERS} CACHE STRING +set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;cfi;esan;scudo;ubsan_minimal) +set(COMPILER_RT_SANITIZERS_TO_BUILD all CACHE STRING "sanitizers to build if supported on the target (all;${ALL_SANITIZERS})") list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}") if (SANITIZER_COMMON_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND - (OS_NAME MATCHES "Android|Darwin|Linux|FreeBSD" OR + (OS_NAME MATCHES "Android|Darwin|Linux|FreeBSD|NetBSD|Fuchsia" OR (OS_NAME MATCHES "Windows" AND (NOT MINGW AND NOT CYGWIN)))) set(COMPILER_RT_HAS_SANITIZER_COMMON TRUE) else() @@ -459,7 +505,7 @@ else() set(COMPILER_RT_HAS_ASAN FALSE) endif() -if (OS_NAME MATCHES "Linux|FreeBSD|Windows") +if (OS_NAME MATCHES "Linux|FreeBSD|Windows|NetBSD") set(COMPILER_RT_ASAN_HAS_STATIC_RUNTIME TRUE) else() set(COMPILER_RT_ASAN_HAS_STATIC_RUNTIME FALSE) @@ -475,49 +521,62 @@ else() endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND LSAN_SUPPORTED_ARCH AND - OS_NAME MATCHES "Darwin|Linux|FreeBSD") + OS_NAME MATCHES "Darwin|Linux|FreeBSD|NetBSD") set(COMPILER_RT_HAS_LSAN TRUE) else() set(COMPILER_RT_HAS_LSAN FALSE) endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND MSAN_SUPPORTED_ARCH AND - OS_NAME MATCHES "Linux") + OS_NAME MATCHES "Linux|NetBSD") set(COMPILER_RT_HAS_MSAN TRUE) else() set(COMPILER_RT_HAS_MSAN FALSE) endif() +if (COMPILER_RT_HAS_SANITIZER_COMMON AND HWASAN_SUPPORTED_ARCH AND + OS_NAME MATCHES "Linux|Android") + set(COMPILER_RT_HAS_HWASAN TRUE) +else() + set(COMPILER_RT_HAS_HWASAN FALSE) +endif() + if (PROFILE_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND - OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android") + OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android|SunOS") set(COMPILER_RT_HAS_PROFILE TRUE) else() set(COMPILER_RT_HAS_PROFILE FALSE) endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND TSAN_SUPPORTED_ARCH AND - OS_NAME MATCHES "Darwin|Linux|FreeBSD|Android") + OS_NAME MATCHES "Darwin|Linux|FreeBSD|Android|NetBSD") set(COMPILER_RT_HAS_TSAN TRUE) else() set(COMPILER_RT_HAS_TSAN FALSE) endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND UBSAN_SUPPORTED_ARCH AND - OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android") + OS_NAME MATCHES "Darwin|Linux|FreeBSD|NetBSD|Windows|Android|Fuchsia") set(COMPILER_RT_HAS_UBSAN TRUE) else() set(COMPILER_RT_HAS_UBSAN FALSE) endif() +if (COMPILER_RT_HAS_SANITIZER_COMMON AND UBSAN_SUPPORTED_ARCH AND + OS_NAME MATCHES "Linux|FreeBSD|NetBSD|Android|Darwin") + set(COMPILER_RT_HAS_UBSAN_MINIMAL TRUE) +else() + set(COMPILER_RT_HAS_UBSAN_MINIMAL FALSE) +endif() + if (COMPILER_RT_HAS_SANITIZER_COMMON AND SAFESTACK_SUPPORTED_ARCH AND - OS_NAME MATCHES "Darwin|Linux|FreeBSD") + OS_NAME MATCHES "Darwin|Linux|FreeBSD|NetBSD") set(COMPILER_RT_HAS_SAFESTACK TRUE) else() set(COMPILER_RT_HAS_SAFESTACK FALSE) endif() -if (COMPILER_RT_HAS_SANITIZER_COMMON AND CFI_SUPPORTED_ARCH AND - OS_NAME MATCHES "Linux") +if (COMPILER_RT_HAS_SANITIZER_COMMON AND CFI_SUPPORTED_ARCH) set(COMPILER_RT_HAS_CFI TRUE) else() set(COMPILER_RT_HAS_CFI FALSE) @@ -531,15 +590,22 @@ else() endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND SCUDO_SUPPORTED_ARCH AND - OS_NAME MATCHES "Linux") + OS_NAME MATCHES "Linux|Android") set(COMPILER_RT_HAS_SCUDO TRUE) else() set(COMPILER_RT_HAS_SCUDO FALSE) endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND XRAY_SUPPORTED_ARCH AND - OS_NAME MATCHES "Linux") + OS_NAME MATCHES "Darwin|Linux") set(COMPILER_RT_HAS_XRAY TRUE) else() set(COMPILER_RT_HAS_XRAY FALSE) endif() + +if (COMPILER_RT_HAS_SANITIZER_COMMON AND FUZZER_SUPPORTED_ARCH AND + OS_NAME MATCHES "Android|Darwin|Linux|NetBSD") + set(COMPILER_RT_HAS_FUZZER TRUE) +else() + set(COMPILER_RT_HAS_FUZZER FALSE) +endif() diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index ec3bf40b95e6..f7efb102ab63 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -6,9 +6,11 @@ if (COMPILER_RT_BUILD_SANITIZERS) sanitizer/coverage_interface.h sanitizer/dfsan_interface.h sanitizer/esan_interface.h + sanitizer/hwasan_interface.h sanitizer/linux_syscall_hooks.h sanitizer/lsan_interface.h sanitizer/msan_interface.h + sanitizer/scudo_interface.h sanitizer/tsan_interface.h sanitizer/tsan_interface_atomic.h) endif(COMPILER_RT_BUILD_SANITIZERS) @@ -21,7 +23,7 @@ endif(COMPILER_RT_BUILD_XRAY) set(COMPILER_RT_HEADERS ${SANITIZER_HEADERS} - ${XRAY_HEADERS}) + ${XRAY_HEADERS}) set(output_dir ${COMPILER_RT_OUTPUT_DIR}/include) @@ -43,9 +45,27 @@ set_target_properties(compiler-rt-headers PROPERTIES FOLDER "Compiler-RT Misc") # Install sanitizer headers. install(FILES ${SANITIZER_HEADERS} + COMPONENT compiler-rt-headers PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ DESTINATION ${COMPILER_RT_INSTALL_PATH}/include/sanitizer) # Install xray headers. install(FILES ${XRAY_HEADERS} + COMPONENT compiler-rt-headers PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ DESTINATION ${COMPILER_RT_INSTALL_PATH}/include/xray) + +if (NOT CMAKE_CONFIGURATION_TYPES) # don't add this for IDEs. + add_custom_target(install-compiler-rt-headers + DEPENDS compiler-rt-headers + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT="compiler-rt-headers" + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake" + USES_TERMINAL) + add_custom_target(install-compiler-rt-headers-stripped + DEPENDS compiler-rt-headers + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT="compiler-rt-headers" + -DCMAKE_INSTALL_DO_STRIP=1 + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake" + USES_TERMINAL) +endif() diff --git a/include/sanitizer/allocator_interface.h b/include/sanitizer/allocator_interface.h index 5220631619fc..2d35804b3764 100644 --- a/include/sanitizer/allocator_interface.h +++ b/include/sanitizer/allocator_interface.h @@ -76,6 +76,13 @@ extern "C" { void (*malloc_hook)(const volatile void *, size_t), void (*free_hook)(const volatile void *)); + /* Drains allocator quarantines (calling thread's and global ones), returns + freed memory back to OS and releases other non-essential internal allocator + resources in attempt to reduce process RSS. + Currently available with ASan only. + */ + void __sanitizer_purge_allocator(); + #ifdef __cplusplus } // extern "C" #endif diff --git a/include/sanitizer/asan_interface.h b/include/sanitizer/asan_interface.h index 97ba0ceb0b23..e689a730e2c3 100644 --- a/include/sanitizer/asan_interface.h +++ b/include/sanitizer/asan_interface.h @@ -144,6 +144,10 @@ extern "C" { void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, void **end); + // 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(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/include/sanitizer/coverage_interface.h b/include/sanitizer/coverage_interface.h index 637379d47c41..081033cec8e5 100644 --- a/include/sanitizer/coverage_interface.h +++ b/include/sanitizer/coverage_interface.h @@ -22,8 +22,11 @@ extern "C" { // Record and dump coverage info. void __sanitizer_cov_dump(); - // Dump collected coverage info. Sorts pcs by module into individual - // .sancov files. + // Clear collected coverage info. + void __sanitizer_cov_reset(); + + // Dump collected coverage info. Sorts pcs by module into individual .sancov + // files. void __sanitizer_dump_coverage(const uintptr_t *pcs, uintptr_t len); #ifdef __cplusplus diff --git a/include/sanitizer/hwasan_interface.h b/include/sanitizer/hwasan_interface.h new file mode 100644 index 000000000000..f70d87875968 --- /dev/null +++ b/include/sanitizer/hwasan_interface.h @@ -0,0 +1,33 @@ +//===-- sanitizer/asan_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 HWAddressSanitizer. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_HWASAN_INTERFACE_H +#define SANITIZER_HWASAN_INTERFACE_H + +#include <sanitizer/common_interface_defs.h> + +#ifdef __cplusplus +extern "C" { +#endif + // This function may be optionally provided by user and should return + // a string containing HWASan runtime options. See asan_flags.h for details. + const char* __hwasan_default_options(); + + void __hwasan_enable_allocator_tagging(); + void __hwasan_disable_allocator_tagging(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_HWASAN_INTERFACE_H diff --git a/include/sanitizer/lsan_interface.h b/include/sanitizer/lsan_interface.h index 8fb8e756da26..e3e509ff7d9f 100644 --- a/include/sanitizer/lsan_interface.h +++ b/include/sanitizer/lsan_interface.h @@ -64,8 +64,14 @@ extern "C" { // 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. + // To avoid dead stripping, you may need to define this function with + // __attribute__((used)) int __lsan_is_turned_off(); + // This function may be optionally provided by user and should return + // a string containing LSan runtime options. See lsan_flags.inc for details. + const char *__lsan_default_options(); + // This function may be optionally provided by the user and should return // a string containing LSan suppressions. const char *__lsan_default_suppressions(); diff --git a/include/sanitizer/scudo_interface.h b/include/sanitizer/scudo_interface.h new file mode 100644 index 000000000000..cab7e0f5f344 --- /dev/null +++ b/include/sanitizer/scudo_interface.h @@ -0,0 +1,34 @@ +//===-- sanitizer/scudo_interface.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// Public Scudo interface header. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_SCUDO_INTERFACE_H_ +#define SANITIZER_SCUDO_INTERFACE_H_ + +#include <sanitizer/common_interface_defs.h> + +#ifdef __cplusplus +extern "C" { +#endif + // This function may be optionally provided by a user and should return + // a string containing Scudo runtime options. See scudo_flags.h for details. + const char* __scudo_default_options(); + + // This function allows to set the RSS limit at runtime. This can be either + // the hard limit (HardLimit=1) or the soft limit (HardLimit=0). The limit + // can be removed by setting LimitMb to 0. This function's parameters should + // be fully trusted to avoid security mishaps. + void __scudo_set_rss_limit(unsigned long LimitMb, int HardLimit); +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_SCUDO_INTERFACE_H_ diff --git a/include/sanitizer/tsan_interface.h b/include/sanitizer/tsan_interface.h index 5ea09ab5c1ff..530885014ac3 100644 --- a/include/sanitizer/tsan_interface.h +++ b/include/sanitizer/tsan_interface.h @@ -44,6 +44,11 @@ const unsigned __tsan_mutex_linker_init = 1 << 0; const unsigned __tsan_mutex_write_reentrant = 1 << 1; // Mutex is read reentrant. const unsigned __tsan_mutex_read_reentrant = 1 << 2; +// Mutex does not have static storage duration, and must not be used after +// its destructor runs. The opposite of __tsan_mutex_linker_init. +// If this flag is passed to __tsan_mutex_destroy, then the destruction +// is ignored unless this flag was previously set on the mutex. +const unsigned __tsan_mutex_not_static = 1 << 8; // Mutex operation flags: @@ -70,6 +75,7 @@ void __tsan_mutex_create(void *addr, unsigned flags); // Annotate destruction of a mutex. // Supported flags: // - __tsan_mutex_linker_init +// - __tsan_mutex_not_static void __tsan_mutex_destroy(void *addr, unsigned flags); // Annotate start of lock operation. diff --git a/include/xray/xray_interface.h b/include/xray/xray_interface.h index 564613417069..d08039a67fa2 100644 --- a/include/xray/xray_interface.h +++ b/include/xray/xray_interface.h @@ -106,6 +106,14 @@ extern uintptr_t __xray_function_address(int32_t FuncId); /// encounter errors (when there are no instrumented functions, etc.). extern size_t __xray_max_function_id(); +/// Initialize the required XRay data structures. This is useful in cases where +/// users want to control precisely when the XRay instrumentation data +/// structures are initialized, for example when the XRay library is built with +/// the XRAY_NO_PREINIT preprocessor definition. +/// +/// Calling __xray_init() more than once is safe across multiple threads. +extern void __xray_init(); + } // end extern "C" #endif // XRAY_XRAY_INTERFACE_H diff --git a/include/xray/xray_log_interface.h b/include/xray/xray_log_interface.h index cdb20094dc26..6c53cdaebfe2 100644 --- a/include/xray/xray_log_interface.h +++ b/include/xray/xray_log_interface.h @@ -128,6 +128,16 @@ enum XRayLogFlushStatus { XRAY_LOG_FLUSHED = 2, }; +/// This enum indicates the installation state of a logging implementation, when +/// associating a mode to a particular logging implementation through +/// `__xray_log_register_impl(...)` or through `__xray_log_select_mode(...`. +enum XRayLogRegisterStatus { + XRAY_REGISTRATION_OK = 0, + XRAY_DUPLICATE_MODE = 1, + XRAY_MODE_NOT_FOUND = 2, + XRAY_INCOMPLETE_IMPL = 3, +}; + /// A valid XRay logging implementation MUST provide all of the function /// pointers in XRayLogImpl when being installed through `__xray_set_log_impl`. /// To be precise, ALL the functions pointers MUST NOT be nullptr. @@ -159,6 +169,9 @@ struct XRayLogImpl { /// always have a handler for function entry and exit events. In case the /// implementation wants to support arg1 (or other future extensions to XRay /// logging) those MUST be installed by the installed 'log_init' handler. + /// + /// Because we didn't want to change the ABI of this struct, the arg1 handler + /// may be silently overwritten during initialization as well. void (*handle_arg0)(int32_t, XRayEntryType); /// The log implementation provided routine for when __xray_log_flushLog() is @@ -186,6 +199,34 @@ struct XRayLogImpl { /// called while in any other states. void __xray_set_log_impl(XRayLogImpl Impl); +/// This function registers a logging implementation against a "mode" +/// identifier. This allows multiple modes to be registered, and chosen at +/// runtime using the same mode identifier through +/// `__xray_log_select_mode(...)`. +/// +/// We treat the Mode identifier as a null-terminated byte string, as the +/// identifier used when retrieving the log impl. +/// +/// Returns: +/// - XRAY_REGISTRATION_OK on success. +/// - XRAY_DUPLICATE_MODE when an implementation is already associated with +/// the provided Mode; does not update the already-registered +/// implementation. +XRayLogRegisterStatus __xray_log_register_mode(const char *Mode, + XRayLogImpl Impl); + +/// This function selects the implementation associated with Mode that has been +/// registered through __xray_log_register_mode(...) and installs that +/// implementation (as if through calling __xray_set_log_impl(...)). The same +/// caveats apply to __xray_log_select_mode(...) as with +/// __xray_log_set_log_impl(...). +/// +/// Returns: +/// - XRAY_REGISTRATION_OK on success. +/// - XRAY_MODE_NOT_FOUND if there is no implementation associated with Mode; +/// does not update the currently installed implementation. +XRayLogRegisterStatus __xray_log_select_mode(const char *Mode); + /// This function removes the currently installed implementation. It will also /// uninstall any handlers that have been previously installed. It does NOT /// unpatch the instrumentation sleds. @@ -220,12 +261,19 @@ XRayLogFlushStatus __xray_log_flushLog(); namespace __xray { -// Options used by the LLVM XRay FDR implementation. +/// Options used by the LLVM XRay FDR logging implementation. struct FDRLoggingOptions { bool ReportErrors = false; int Fd = -1; }; +/// Options used by the LLVM XRay Basic (Naive) logging implementation. +struct BasicLoggingOptions { + int DurationFilterMicros = 0; + size_t MaxStackDepth = 0; + size_t ThreadBufferSize = 0; +}; + } // namespace __xray #endif // XRAY_XRAY_LOG_INTERFACE_H diff --git a/include/xray/xray_records.h b/include/xray/xray_records.h index feb8d228b2fd..d4b7b4c31a9a 100644 --- a/include/xray/xray_records.h +++ b/include/xray/xray_records.h @@ -17,6 +17,8 @@ #ifndef XRAY_XRAY_RECORDS_H #define XRAY_XRAY_RECORDS_H +#include <cstdint> + namespace __xray { enum FileTypes { @@ -65,18 +67,23 @@ static_assert(sizeof(XRayFileHeader) == 32, "XRayFileHeader != 32 bytes"); enum RecordTypes { NORMAL = 0, + ARG_PAYLOAD = 1, }; struct alignas(32) XRayRecord { // This is the type of the record being written. We use 16 bits to allow us to // treat this as a discriminant, and so that the first 4 bytes get packed // properly. See RecordTypes for more supported types. - uint16_t RecordType = 0; + uint16_t RecordType = RecordTypes::NORMAL; // The CPU where the thread is running. We assume number of CPUs <= 256. uint8_t CPU = 0; - // The type of the event. Usually either ENTER = 0 or EXIT = 1. + // The type of the event. One of the following: + // ENTER = 0 + // EXIT = 1 + // TAIL_EXIT = 2 + // ENTER_ARG = 3 uint8_t Type = 0; // The function ID for the record. @@ -94,6 +101,32 @@ struct alignas(32) XRayRecord { static_assert(sizeof(XRayRecord) == 32, "XRayRecord != 32 bytes"); +struct alignas(32) XRayArgPayload { + // We use the same 16 bits as a discriminant for the records in the log here + // too, and so that the first 4 bytes are packed properly. + uint16_t RecordType = RecordTypes::ARG_PAYLOAD; + + // Add a few bytes to pad. + uint8_t Padding[2] = {}; + + // The function ID for the record. + int32_t FuncId = 0; + + // The thread ID for the currently running thread. + uint32_t TId = 0; + + // Add more padding. + uint8_t Padding2[4] = {}; + + // The argument payload. + uint64_t Arg = 0; + + // The rest of this record ought to be left as padding. + uint8_t TailPadding[8] = {}; +} __attribute__((packed)); + +static_assert(sizeof(XRayArgPayload) == 32, "XRayArgPayload != 32 bytes"); + } // namespace __xray #endif // XRAY_XRAY_RECORDS_H diff --git a/lib/BlocksRuntime/Block.h b/lib/BlocksRuntime/Block.h index 55cdd01a9123..e6cb142fd755 100644 --- a/lib/BlocksRuntime/Block.h +++ b/lib/BlocksRuntime/Block.h @@ -27,7 +27,7 @@ #if !defined(BLOCK_EXPORT) # if defined(__cplusplus) -# define BLOCK_EXPORT extern "C" +# define BLOCK_EXPORT extern "C" # else # define BLOCK_EXPORT extern # endif diff --git a/lib/BlocksRuntime/Block_private.h b/lib/BlocksRuntime/Block_private.h index 8ae821815ebe..91d8d8a9850b 100644 --- a/lib/BlocksRuntime/Block_private.h +++ b/lib/BlocksRuntime/Block_private.h @@ -27,7 +27,7 @@ #if !defined(BLOCK_EXPORT) # if defined(__cplusplus) -# define BLOCK_EXPORT extern "C" +# define BLOCK_EXPORT extern "C" # else # define BLOCK_EXPORT extern # endif diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 90f72d02afa1..b3731f653a01 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -39,10 +39,16 @@ if(COMPILER_RT_BUILD_SANITIZERS) foreach(sanitizer ${COMPILER_RT_SANITIZERS_TO_BUILD}) compiler_rt_build_runtime(${sanitizer}) endforeach() +endif() +if(COMPILER_RT_BUILD_PROFILE AND COMPILER_RT_HAS_PROFILE) compiler_rt_build_runtime(profile) endif() if(COMPILER_RT_BUILD_XRAY) compiler_rt_build_runtime(xray) endif() + +if(COMPILER_RT_BUILD_LIBFUZZER) + compiler_rt_build_runtime(fuzzer) +endif() diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt index a2d4630dfc33..da82e485b581 100644 --- a/lib/asan/CMakeLists.txt +++ b/lib/asan/CMakeLists.txt @@ -8,9 +8,11 @@ set(ASAN_SOURCES asan_errors.cc asan_fake_stack.cc asan_flags.cc + asan_fuchsia.cc asan_globals.cc asan_globals_win.cc asan_interceptors.cc + asan_interceptors_memintrinsics.cc asan_linux.cc asan_mac.cc asan_malloc_linux.cc @@ -19,8 +21,10 @@ set(ASAN_SOURCES asan_memory_profile.cc asan_poisoning.cc asan_posix.cc + asan_premap_shadow.cc asan_report.cc asan_rtl.cc + asan_shadow_setup.cc asan_stack.cc asan_stats.cc asan_suppressions.cc @@ -36,10 +40,11 @@ set(ASAN_PREINIT_SOURCES include_directories(..) set(ASAN_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +set(ASAN_COMMON_DEFINITIONS ${COMPILER_RT_ASAN_SHADOW_SCALE_DEFINITION}) append_rtti_flag(OFF ASAN_CFLAGS) -set(ASAN_DYNAMIC_LINK_FLAGS) +set(ASAN_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS}) if(ANDROID) # On Android, -z global does not do what it is documented to do. @@ -64,12 +69,12 @@ append_list_if(COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC -ftls-model=initial-exec ASAN_DYNAMIC_CFLAGS) append_list_if(MSVC /DEBUG ASAN_DYNAMIC_LINK_FLAGS) -append_list_if(COMPILER_RT_HAS_LIBC c ASAN_DYNAMIC_LIBS) +set(ASAN_DYNAMIC_LIBS ${SANITIZER_CXX_ABI_LIBRARY} ${SANITIZER_COMMON_LINK_LIBS}) + append_list_if(COMPILER_RT_HAS_LIBDL dl ASAN_DYNAMIC_LIBS) append_list_if(COMPILER_RT_HAS_LIBRT rt ASAN_DYNAMIC_LIBS) append_list_if(COMPILER_RT_HAS_LIBM m ASAN_DYNAMIC_LIBS) append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread ASAN_DYNAMIC_LIBS) -append_list_if(COMPILER_RT_HAS_LIBSTDCXX stdc++ ASAN_DYNAMIC_LIBS) append_list_if(COMPILER_RT_HAS_LIBLOG log ASAN_DYNAMIC_LIBS) # Compile ASan sources into an object library. @@ -164,15 +169,15 @@ else() PARENT_TARGET asan) foreach(arch ${ASAN_SUPPORTED_ARCH}) - if (UNIX AND NOT ${arch} MATCHES "i386|i686") + if (UNIX) add_sanitizer_rt_version_list(clang_rt.asan-dynamic-${arch} LIBS clang_rt.asan-${arch} clang_rt.asan_cxx-${arch} EXTRA asan.syms.extra) set(VERSION_SCRIPT_FLAG -Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/clang_rt.asan-dynamic-${arch}.vers) - set_source_files_properties( + set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/dummy.cc - PROPERTIES + APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clang_rt.asan-dynamic-${arch}.vers) else() set(VERSION_SCRIPT_FLAG) @@ -198,10 +203,11 @@ else() ARCHS ${arch} OBJECT_LIBS ${ASAN_COMMON_RUNTIME_OBJECT_LIBS} RTAsan_dynamic - # The only purpose of RTAsan_dynamic_version_script_dummy is to carry - # a dependency of the shared runtime on the version script. With CMake - # 3.1 or later it can be replaced with a straightforward + # The only purpose of RTAsan_dynamic_version_script_dummy is to + # carry a dependency of the shared runtime on the version script. + # Replacing it with a straightforward # add_dependencies(clang_rt.asan-dynamic-${arch} clang_rt.asan-dynamic-${arch}-version-list) + # generates an order-only dependency in ninja. RTAsan_dynamic_version_script_dummy RTUbsan_cxx ${ASAN_DYNAMIC_WEAK_INTERCEPTION} @@ -212,7 +218,7 @@ else() DEFS ${ASAN_DYNAMIC_DEFINITIONS} PARENT_TARGET asan) - if (UNIX AND NOT ${arch} MATCHES "i386|i686") + if (UNIX AND NOT ${arch} STREQUAL "i386") add_sanitizer_rt_symbols(clang_rt.asan_cxx ARCHS ${arch}) add_dependencies(asan clang_rt.asan_cxx-${arch}-symbols) diff --git a/lib/asan/asan_activation.cc b/lib/asan/asan_activation.cc index 66eba9ce2748..d642be93488d 100644 --- a/lib/asan/asan_activation.cc +++ b/lib/asan/asan_activation.cc @@ -16,8 +16,10 @@ #include "asan_allocator.h" #include "asan_flags.h" #include "asan_internal.h" +#include "asan_mapping.h" #include "asan_poisoning.h" #include "asan_stack.h" +#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" namespace __asan { @@ -110,8 +112,9 @@ void AsanDeactivate() { AllocatorOptions disabled = asan_deactivated_flags.allocator_options; disabled.quarantine_size_mb = 0; disabled.thread_local_quarantine_size_kb = 0; - disabled.min_redzone = 16; // Redzone must be at least 16 bytes long. - disabled.max_redzone = 16; + // Redzone must be at least Max(16, granularity) bytes long. + disabled.min_redzone = Max(16, (int)SHADOW_GRANULARITY); + disabled.max_redzone = disabled.min_redzone; disabled.alloc_dealloc_mismatch = false; disabled.may_return_null = true; ReInitializeAllocator(disabled); diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc index 92963ddfc4da..a437ae1cd3b1 100644 --- a/lib/asan/asan_allocator.cc +++ b/lib/asan/asan_allocator.cc @@ -84,7 +84,10 @@ struct ChunkHeader { // This field is used for small sizes. For large sizes it is equal to // SizeClassMap::kMaxSize and the actual size is stored in the // SecondaryAllocator's metadata. - u32 user_requested_size; + u32 user_requested_size : 29; + // align < 8 -> 0 + // else -> log2(min(align, 512)) - 2 + u32 user_requested_alignment_log : 3; u32 alloc_context_id; }; @@ -271,9 +274,9 @@ struct Allocator { atomic_store(&max_redzone, options.max_redzone, memory_order_release); } - void Initialize(const AllocatorOptions &options) { + void InitLinkerInitialized(const AllocatorOptions &options) { SetAllocatorMayReturnNull(options.may_return_null); - allocator.Init(options.release_to_os_interval_ms); + allocator.InitLinkerInitialized(options.release_to_os_interval_ms); SharedInitCode(options); } @@ -351,6 +354,20 @@ struct Allocator { return Min(Max(rz_log, RZSize2Log(min_rz)), RZSize2Log(max_rz)); } + static uptr ComputeUserRequestedAlignmentLog(uptr user_requested_alignment) { + if (user_requested_alignment < 8) + return 0; + if (user_requested_alignment > 512) + user_requested_alignment = 512; + return Log2(user_requested_alignment) - 2; + } + + static uptr ComputeUserAlignment(uptr user_requested_alignment_log) { + if (user_requested_alignment_log == 0) + return 0; + return 1LL << (user_requested_alignment_log + 2); + } + // We have an address between two chunks, and we want to report just one. AsanChunk *ChooseChunk(uptr addr, AsanChunk *left_chunk, AsanChunk *right_chunk) { @@ -385,6 +402,8 @@ struct Allocator { Flags &fl = *flags(); CHECK(stack); const uptr min_alignment = SHADOW_GRANULARITY; + const uptr user_requested_alignment_log = + ComputeUserRequestedAlignmentLog(alignment); if (alignment < min_alignment) alignment = min_alignment; if (size == 0) { @@ -472,6 +491,7 @@ struct Allocator { meta[0] = size; meta[1] = chunk_beg; } + m->user_requested_alignment_log = user_requested_alignment_log; m->alloc_context_id = StackDepotPut(*stack); @@ -573,8 +593,8 @@ struct Allocator { } } - void Deallocate(void *ptr, uptr delete_size, BufferedStackTrace *stack, - AllocType alloc_type) { + void Deallocate(void *ptr, uptr delete_size, uptr delete_alignment, + BufferedStackTrace *stack, AllocType alloc_type) { uptr p = reinterpret_cast<uptr>(ptr); if (p == 0) return; @@ -601,11 +621,14 @@ struct Allocator { ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type, (AllocType)alloc_type); } - } - - if (delete_size && flags()->new_delete_type_mismatch && - delete_size != m->UsedSize()) { - ReportNewDeleteSizeMismatch(p, delete_size, stack); + } else { + if (flags()->new_delete_type_mismatch && + (alloc_type == FROM_NEW || alloc_type == FROM_NEW_BR) && + ((delete_size && delete_size != m->UsedSize()) || + ComputeUserRequestedAlignmentLog(delete_alignment) != + m->user_requested_alignment_log)) { + ReportNewDeleteTypeMismatch(p, delete_size, delete_alignment, stack); + } } QuarantineChunk(m, ptr, stack); @@ -631,7 +654,7 @@ struct Allocator { // 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); - Deallocate(old_ptr, 0, stack, FROM_MALLOC); + Deallocate(old_ptr, 0, 0, stack, FROM_MALLOC); } return new_ptr; } @@ -716,6 +739,22 @@ struct Allocator { return AsanChunkView(m1); } + void Purge() { + AsanThread *t = GetCurrentThread(); + if (t) { + AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); + quarantine.DrainAndRecycle(GetQuarantineCache(ms), + QuarantineCallback(GetAllocatorCache(ms))); + } + { + SpinMutexLock l(&fallback_mutex); + quarantine.DrainAndRecycle(&fallback_quarantine_cache, + QuarantineCallback(&fallback_allocator_cache)); + } + + allocator.ForceReleaseToOS(); + } + void PrintStats() { allocator.PrintStats(); quarantine.PrintStats(); @@ -750,6 +789,9 @@ bool AsanChunkView::IsQuarantined() const { uptr AsanChunkView::Beg() const { return chunk_->Beg(); } uptr AsanChunkView::End() const { return Beg() + UsedSize(); } uptr AsanChunkView::UsedSize() const { return chunk_->UsedSize(); } +u32 AsanChunkView::UserRequestedAlignment() const { + return Allocator::ComputeUserAlignment(chunk_->user_requested_alignment_log); +} uptr AsanChunkView::AllocTid() const { return chunk_->alloc_tid; } uptr AsanChunkView::FreeTid() const { return chunk_->free_tid; } AllocType AsanChunkView::GetAllocType() const { @@ -775,7 +817,7 @@ StackTrace AsanChunkView::GetFreeStack() const { } void InitializeAllocator(const AllocatorOptions &options) { - instance.Initialize(options); + instance.InitLinkerInitialized(options); } void ReInitializeAllocator(const AllocatorOptions &options) { @@ -802,12 +844,12 @@ void PrintInternalAllocatorStats() { } void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) { - instance.Deallocate(ptr, 0, stack, alloc_type); + instance.Deallocate(ptr, 0, 0, stack, alloc_type); } -void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack, - AllocType alloc_type) { - instance.Deallocate(ptr, size, stack, alloc_type); +void asan_delete(void *ptr, uptr size, uptr alignment, + BufferedStackTrace *stack, AllocType alloc_type) { + instance.Deallocate(ptr, size, alignment, stack, alloc_type); } void *asan_malloc(uptr size, BufferedStackTrace *stack) { @@ -823,7 +865,7 @@ void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) { return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true)); if (size == 0) { if (flags()->allocator_frees_and_returns_null_on_realloc_zero) { - instance.Deallocate(p, 0, stack, FROM_MALLOC); + instance.Deallocate(p, 0, 0, stack, FROM_MALLOC); return nullptr; } // Allocate a size of 1 if we shouldn't free() on Realloc to 0 @@ -839,6 +881,10 @@ void *asan_valloc(uptr size, BufferedStackTrace *stack) { void *asan_pvalloc(uptr size, BufferedStackTrace *stack) { uptr PageSize = GetPageSizeCached(); + if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) { + errno = errno_ENOMEM; + return AsanAllocator::FailureHandler::OnBadRequest(); + } // pvalloc(0) should allocate one page. size = size ? RoundUpTo(size, PageSize) : PageSize; return SetErrnoOnNull( @@ -1007,6 +1053,10 @@ uptr __sanitizer_get_allocated_size(const void *p) { return allocated_size; } +void __sanitizer_purge_allocator() { + instance.Purge(); +} + #if !SANITIZER_SUPPORTS_WEAK_HOOKS // Provide default (no-op) implementation of malloc hooks. SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_malloc_hook, diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h index ad1aeb58a86b..26483db4c60e 100644 --- a/lib/asan/asan_allocator.h +++ b/lib/asan/asan_allocator.h @@ -58,6 +58,7 @@ class AsanChunkView { uptr Beg() const; // First byte of user memory. uptr End() const; // Last byte of user memory. uptr UsedSize() const; // Size requested by the user. + u32 UserRequestedAlignment() const; // Originally requested alignment. uptr AllocTid() const; uptr FreeTid() const; bool Eq(const AsanChunkView &c) const { return chunk_ == c.chunk_; } @@ -119,7 +120,11 @@ struct AsanMapUnmapCallback { }; #if SANITIZER_CAN_USE_ALLOCATOR64 -# if defined(__powerpc64__) +# if SANITIZER_FUCHSIA +const uptr kAllocatorSpace = ~(uptr)0; +const uptr kAllocatorSize = 0x40000000000ULL; // 4T. +typedef DefaultSizeClassMap SizeClassMap; +# elif defined(__powerpc64__) const uptr kAllocatorSpace = 0xa0000000000ULL; const uptr kAllocatorSize = 0x20000000000ULL; // 2T. typedef DefaultSizeClassMap SizeClassMap; @@ -193,8 +198,8 @@ struct AsanThreadLocalMallocStorage { void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack, AllocType alloc_type); void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type); -void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack, - AllocType alloc_type); +void asan_delete(void *ptr, uptr size, uptr alignment, + BufferedStackTrace *stack, AllocType alloc_type); void *asan_malloc(uptr size, BufferedStackTrace *stack); void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack); diff --git a/lib/asan/asan_descriptions.cc b/lib/asan/asan_descriptions.cc index 822a6a62d64e..86c6af7d9f75 100644 --- a/lib/asan/asan_descriptions.cc +++ b/lib/asan/asan_descriptions.cc @@ -122,6 +122,7 @@ static void GetAccessToHeapChunkInformation(ChunkAccess *descr, } descr->chunk_begin = chunk.Beg(); descr->chunk_size = chunk.UsedSize(); + descr->user_requested_alignment = chunk.UserRequestedAlignment(); descr->alloc_type = chunk.GetAllocType(); } @@ -150,7 +151,7 @@ static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) { str.append(" %zu-byte region [%p,%p)\n", descr.chunk_size, (void *)descr.chunk_begin, (void *)(descr.chunk_begin + descr.chunk_size)); - str.append("%s", d.EndLocation()); + str.append("%s", d.Default()); Printf("%s", str.data()); } @@ -260,7 +261,7 @@ static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, // FIXME: we may want to also print the size of the access here, // but in case of accesses generated by memset it may be confusing. str.append("%s <== Memory access at offset %zd %s this variable%s\n", - d.Location(), addr, pos_descr, d.EndLocation()); + d.Location(), addr, pos_descr, d.Default()); } else { str.append("\n"); } @@ -295,7 +296,7 @@ static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, MaybeDemangleGlobalName(g.name)); PrintGlobalLocation(&str, g); str.append("' (0x%zx) of size %zu\n", g.beg, g.size); - str.append("%s", d.EndLocation()); + str.append("%s", d.Default()); PrintGlobalNameIfASCII(&str, g); Printf("%s", str.data()); } @@ -335,6 +336,26 @@ void GlobalAddressDescription::Print(const char *bug_type) const { } } +bool GlobalAddressDescription::PointsInsideTheSameVariable( + const GlobalAddressDescription &other) const { + if (size == 0 || other.size == 0) return false; + + for (uptr i = 0; i < size; i++) { + const __asan_global &a = globals[i]; + for (uptr j = 0; j < other.size; j++) { + const __asan_global &b = other.globals[j]; + if (a.beg == b.beg && + a.beg <= addr && + b.beg <= other.addr && + (addr + access_size) < (a.beg + a.size) && + (other.addr + other.access_size) < (b.beg + b.size)) + return true; + } + } + + return false; +} + void StackAddressDescription::Print() const { Decorator d; char tname[128]; @@ -343,10 +364,10 @@ void StackAddressDescription::Print() const { ThreadNameWithParenthesis(tid, tname, sizeof(tname))); if (!frame_descr) { - Printf("%s\n", d.EndLocation()); + Printf("%s\n", d.Default()); return; } - Printf(" at offset %zu in frame%s\n", offset, d.EndLocation()); + Printf(" at offset %zu in frame%s\n", offset, d.Default()); // Now we print the frame where the alloca has happened. // We print this frame as a stack trace with one element. @@ -355,7 +376,7 @@ void StackAddressDescription::Print() const { // previously. That's unfortunate, but I have no better solution, // especially given that the alloca may be from entirely different place // (e.g. use-after-scope, or different thread's stack). - Printf("%s", d.EndLocation()); + Printf("%s", d.Default()); StackTrace alloca_stack(&frame_pc, 1); alloca_stack.Print(); @@ -405,18 +426,18 @@ void HeapAddressDescription::Print() const { Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), free_thread->tid, ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)), - d.EndAllocation()); + d.Default()); StackTrace free_stack = GetStackTraceFromId(free_stack_id); free_stack.Print(); Printf("%spreviously allocated by thread T%d%s here:%s\n", d.Allocation(), alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), - d.EndAllocation()); + d.Default()); } else { Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(), alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), - d.EndAllocation()); + d.Default()); } alloc_stack.Print(); DescribeThread(GetCurrentThread()); diff --git a/lib/asan/asan_descriptions.h b/lib/asan/asan_descriptions.h index 0ee677eb7d0e..1a1b01cf20cd 100644 --- a/lib/asan/asan_descriptions.h +++ b/lib/asan/asan_descriptions.h @@ -34,11 +34,8 @@ class Decorator : public __sanitizer::SanitizerCommonDecorator { public: Decorator() : SanitizerCommonDecorator() {} const char *Access() { return Blue(); } - const char *EndAccess() { return Default(); } const char *Location() { return Green(); } - const char *EndLocation() { return Default(); } const char *Allocation() { return Magenta(); } - const char *EndAllocation() { return Default(); } const char *ShadowByte(u8 byte) { switch (byte) { @@ -72,9 +69,6 @@ class Decorator : public __sanitizer::SanitizerCommonDecorator { return Default(); } } - const char *EndShadowByte() { return Default(); } - const char *MemoryByte() { return Magenta(); } - const char *EndMemoryByte() { return Default(); } }; enum ShadowKind : u8 { @@ -108,6 +102,7 @@ struct ChunkAccess { sptr offset; uptr chunk_begin; uptr chunk_size; + u32 user_requested_alignment : 12; u32 access_type : 2; u32 alloc_type : 2; }; @@ -151,6 +146,10 @@ struct GlobalAddressDescription { u8 size; void Print(const char *bug_type = "") const; + + // Returns true when this descriptions points inside the same global variable + // as other. Descriptions can have different address within the variable + bool PointsInsideTheSameVariable(const GlobalAddressDescription &other) const; }; bool GetGlobalAddressInformation(uptr addr, uptr access_size, diff --git a/lib/asan/asan_errors.cc b/lib/asan/asan_errors.cc index b7a38eb7cece..0f4a3abff9a7 100644 --- a/lib/asan/asan_errors.cc +++ b/lib/asan/asan_errors.cc @@ -13,7 +13,6 @@ //===----------------------------------------------------------------------===// #include "asan_errors.h" -#include <signal.h> #include "asan_descriptions.h" #include "asan_mapping.h" #include "asan_report.h" @@ -22,82 +21,26 @@ namespace __asan { -void ErrorStackOverflow::Print() { - Decorator d; - Printf("%s", d.Warning()); - Report( - "ERROR: AddressSanitizer: %s on address %p" - " (pc %p bp %p sp %p T%d)\n", scariness.GetDescription(), - (void *)addr, (void *)pc, (void *)bp, (void *)sp, tid); - Printf("%s", d.EndWarning()); - scariness.Print(); - BufferedStackTrace stack; - GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context, - common_flags()->fast_unwind_on_fatal); - stack.Print(); - ReportErrorSummary(scariness.GetDescription(), &stack); -} - -static void MaybeDumpInstructionBytes(uptr pc) { - if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) return; - InternalScopedString str(1024); - str.append("First 16 instruction bytes at pc: "); - if (IsAccessibleMemoryRange(pc, 16)) { - for (int i = 0; i < 16; ++i) { - PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/ false, " "); - } - str.append("\n"); - } else { - str.append("unaccessible\n"); - } - Report("%s", str.data()); -} - -static void MaybeDumpRegisters(void *context) { - if (!flags()->dump_registers) return; - SignalContext::DumpAllRegisters(context); -} - -static void MaybeReportNonExecRegion(uptr pc) { -#if SANITIZER_FREEBSD || SANITIZER_LINUX - MemoryMappingLayout proc_maps(/*cache_enabled*/ true); - MemoryMappedSegment segment; - while (proc_maps.Next(&segment)) { - if (pc >= segment.start && pc < segment.end && !segment.IsExecutable()) - Report("Hint: PC is at a non-executable region. Maybe a wild jump?\n"); - } +static void OnStackUnwind(const SignalContext &sig, + const void *callback_context, + BufferedStackTrace *stack) { + bool fast = common_flags()->fast_unwind_on_fatal; +#if SANITIZER_FREEBSD || SANITIZER_NETBSD + // On FreeBSD the slow unwinding that leverages _Unwind_Backtrace() + // yields the call stack of the signal's handler and not of the code + // that raised the signal (as it does on Linux). + fast = true; #endif + // Tests and maybe some users expect that scariness is going to be printed + // just before the stack. As only asan has scariness score we have no + // corresponding code in the sanitizer_common and we use this callback to + // print it. + static_cast<const ScarinessScoreBase *>(callback_context)->Print(); + GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, sig.context, fast); } void ErrorDeadlySignal::Print() { - Decorator d; - Printf("%s", d.Warning()); - const char *description = __sanitizer::DescribeSignalOrException(signo); - Report( - "ERROR: AddressSanitizer: %s on unknown address %p (pc %p bp %p sp %p " - "T%d)\n", - description, (void *)addr, (void *)pc, (void *)bp, (void *)sp, tid); - Printf("%s", d.EndWarning()); - if (pc < GetPageSizeCached()) Report("Hint: pc points to the zero page.\n"); - if (is_memory_access) { - const char *access_type = - write_flag == SignalContext::WRITE - ? "WRITE" - : (write_flag == SignalContext::READ ? "READ" : "UNKNOWN"); - Report("The signal is caused by a %s memory access.\n", access_type); - if (addr < GetPageSizeCached()) - Report("Hint: address points to the zero page.\n"); - } - MaybeReportNonExecRegion(pc); - scariness.Print(); - BufferedStackTrace stack; - GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context, - common_flags()->fast_unwind_on_fatal); - stack.Print(); - MaybeDumpInstructionBytes(pc); - MaybeDumpRegisters(context); - Printf("AddressSanitizer can not provide additional info.\n"); - ReportErrorSummary(description, &stack); + ReportDeadlySignal(signal, tid, &OnStackUnwind, &scariness); } void ErrorDoubleFree::Print() { @@ -109,7 +52,7 @@ void ErrorDoubleFree::Print() { "thread T%d%s:\n", scariness.GetDescription(), addr_description.addr, tid, ThreadNameWithParenthesis(tid, tname, sizeof(tname))); - Printf("%s", d.EndWarning()); + Printf("%s", d.Default()); scariness.Print(); GET_STACK_TRACE_FATAL(second_free_stack->trace[0], second_free_stack->top_frame_bp); @@ -118,7 +61,7 @@ void ErrorDoubleFree::Print() { ReportErrorSummary(scariness.GetDescription(), &stack); } -void ErrorNewDeleteSizeMismatch::Print() { +void ErrorNewDeleteTypeMismatch::Print() { Decorator d; Printf("%s", d.Warning()); char tname[128]; @@ -127,11 +70,29 @@ void ErrorNewDeleteSizeMismatch::Print() { "T%d%s:\n", scariness.GetDescription(), addr_description.addr, tid, ThreadNameWithParenthesis(tid, tname, sizeof(tname))); - Printf("%s object passed to delete has wrong type:\n", d.EndWarning()); - Printf( - " size of the allocated type: %zd bytes;\n" - " size of the deallocated type: %zd bytes.\n", - addr_description.chunk_access.chunk_size, delete_size); + Printf("%s object passed to delete has wrong type:\n", d.Default()); + if (delete_size != 0) { + Printf( + " size of the allocated type: %zd bytes;\n" + " size of the deallocated type: %zd bytes.\n", + addr_description.chunk_access.chunk_size, delete_size); + } + const uptr user_alignment = + addr_description.chunk_access.user_requested_alignment; + if (delete_alignment != user_alignment) { + char user_alignment_str[32]; + char delete_alignment_str[32]; + internal_snprintf(user_alignment_str, sizeof(user_alignment_str), + "%zd bytes", user_alignment); + internal_snprintf(delete_alignment_str, sizeof(delete_alignment_str), + "%zd bytes", delete_alignment); + static const char *kDefaultAlignment = "default-aligned"; + Printf( + " alignment of the allocated type: %s;\n" + " alignment of the deallocated type: %s.\n", + user_alignment > 0 ? user_alignment_str : kDefaultAlignment, + delete_alignment > 0 ? delete_alignment_str : kDefaultAlignment); + } CHECK_GT(free_stack->size, 0); scariness.Print(); GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); @@ -152,7 +113,7 @@ void ErrorFreeNotMalloced::Print() { "which was not malloc()-ed: %p in thread T%d%s\n", addr_description.Address(), tid, ThreadNameWithParenthesis(tid, tname, sizeof(tname))); - Printf("%s", d.EndWarning()); + Printf("%s", d.Default()); CHECK_GT(free_stack->size, 0); scariness.Print(); GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); @@ -173,7 +134,7 @@ void ErrorAllocTypeMismatch::Print() { scariness.GetDescription(), alloc_names[alloc_type], dealloc_names[dealloc_type], addr_description.addr); - Printf("%s", d.EndWarning()); + Printf("%s", d.Default()); CHECK_GT(dealloc_stack->size, 0); scariness.Print(); GET_STACK_TRACE_FATAL(dealloc_stack->trace[0], dealloc_stack->top_frame_bp); @@ -192,7 +153,7 @@ void ErrorMallocUsableSizeNotOwned::Print() { "ERROR: AddressSanitizer: attempting to call malloc_usable_size() for " "pointer which is not owned: %p\n", addr_description.Address()); - Printf("%s", d.EndWarning()); + Printf("%s", d.Default()); stack->Print(); addr_description.Print(); ReportErrorSummary(scariness.GetDescription(), stack); @@ -205,7 +166,7 @@ void ErrorSanitizerGetAllocatedSizeNotOwned::Print() { "ERROR: AddressSanitizer: attempting to call " "__sanitizer_get_allocated_size() for pointer which is not owned: %p\n", addr_description.Address()); - Printf("%s", d.EndWarning()); + Printf("%s", d.Default()); stack->Print(); addr_description.Print(); ReportErrorSummary(scariness.GetDescription(), stack); @@ -222,7 +183,7 @@ void ErrorStringFunctionMemoryRangesOverlap::Print() { bug_type, addr1_description.Address(), addr1_description.Address() + length1, addr2_description.Address(), addr2_description.Address() + length2); - Printf("%s", d.EndWarning()); + Printf("%s", d.Default()); scariness.Print(); stack->Print(); addr1_description.Print(); @@ -235,7 +196,7 @@ void ErrorStringFunctionSizeOverflow::Print() { Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", scariness.GetDescription(), size); - Printf("%s", d.EndWarning()); + Printf("%s", d.Default()); scariness.Print(); stack->Print(); addr_description.Print(); @@ -263,7 +224,7 @@ void ErrorODRViolation::Print() { Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: %s (%p):\n", scariness.GetDescription(), global1.beg); - Printf("%s", d.EndWarning()); + Printf("%s", d.Default()); InternalScopedString g1_loc(256), g2_loc(256); PrintGlobalLocation(&g1_loc, global1); PrintGlobalLocation(&g2_loc, global2); @@ -292,7 +253,7 @@ void ErrorInvalidPointerPair::Print() { Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: %s: %p %p\n", scariness.GetDescription(), addr1_description.Address(), addr2_description.Address()); - Printf("%s", d.EndWarning()); + Printf("%s", d.Default()); GET_STACK_TRACE_FATAL(pc, bp); stack.Print(); addr1_description.Print(); @@ -477,9 +438,14 @@ static void PrintShadowMemoryForAddress(uptr addr) { InternalScopedString str(4096 * 8); str.append("Shadow bytes around the buggy address:\n"); for (int i = -5; i <= 5; i++) { + uptr row_shadow_addr = aligned_shadow + i * n_bytes_per_row; + // Skip rows that would be outside the shadow range. This can happen when + // the user address is near the bottom, top, or shadow gap of the address + // space. + if (!AddrIsInShadow(row_shadow_addr)) continue; const char *prefix = (i == 0) ? "=>" : " "; - PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row), - (u8 *)shadow_addr, n_bytes_per_row); + PrintShadowBytes(&str, prefix, (u8 *)row_shadow_addr, (u8 *)shadow_addr, + n_bytes_per_row); } if (flags()->print_legend) PrintLegend(&str); Printf("%s", str.data()); @@ -491,13 +457,13 @@ void ErrorGeneric::Print() { uptr addr = addr_description.Address(); Report("ERROR: AddressSanitizer: %s on address %p at pc %p bp %p sp %p\n", bug_descr, (void *)addr, pc, bp, sp); - Printf("%s", d.EndWarning()); + Printf("%s", d.Default()); char tname[128]; Printf("%s%s of size %zu at %p thread T%d%s%s\n", d.Access(), access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", access_size, (void *)addr, tid, - ThreadNameWithParenthesis(tid, tname, sizeof(tname)), d.EndAccess()); + ThreadNameWithParenthesis(tid, tname, sizeof(tname)), d.Default()); scariness.Print(); GET_STACK_TRACE_FATAL(pc, bp); diff --git a/lib/asan/asan_errors.h b/lib/asan/asan_errors.h index 9a124924470e..518ba0c6945e 100644 --- a/lib/asan/asan_errors.h +++ b/lib/asan/asan_errors.h @@ -27,61 +27,28 @@ struct ErrorBase { u32 tid; }; -struct ErrorStackOverflow : ErrorBase { - uptr addr, pc, bp, sp; - // ErrorStackOverflow never owns the context. - void *context; - // VS2013 doesn't implement unrestricted unions, so we need a trivial default - // constructor - ErrorStackOverflow() = default; - ErrorStackOverflow(u32 tid, const SignalContext &sig) - : ErrorBase(tid), - addr(sig.addr), - pc(sig.pc), - bp(sig.bp), - sp(sig.sp), - context(sig.context) { - scariness.Clear(); - scariness.Scare(10, "stack-overflow"); - } - void Print(); -}; - struct ErrorDeadlySignal : ErrorBase { - uptr addr, pc, bp, sp; - // ErrorDeadlySignal never owns the context. - void *context; - int signo; - SignalContext::WriteFlag write_flag; - bool is_memory_access; + SignalContext signal; // VS2013 doesn't implement unrestricted unions, so we need a trivial default // constructor ErrorDeadlySignal() = default; - ErrorDeadlySignal(u32 tid, const SignalContext &sig, int signo_) - : ErrorBase(tid), - addr(sig.addr), - pc(sig.pc), - bp(sig.bp), - sp(sig.sp), - context(sig.context), - signo(signo_), - write_flag(sig.write_flag), - is_memory_access(sig.is_memory_access) { + ErrorDeadlySignal(u32 tid, const SignalContext &sig) + : ErrorBase(tid), signal(sig) { scariness.Clear(); - if (is_memory_access) { - if (addr < GetPageSizeCached()) { - scariness.Scare(10, "null-deref"); - } else if (addr == pc) { - scariness.Scare(60, "wild-jump"); - } else if (write_flag == SignalContext::WRITE) { - scariness.Scare(30, "wild-addr-write"); - } else if (write_flag == SignalContext::READ) { - scariness.Scare(20, "wild-addr-read"); - } else { - scariness.Scare(25, "wild-addr"); - } - } else { + if (signal.IsStackOverflow()) { + scariness.Scare(10, "stack-overflow"); + } else if (!signal.is_memory_access) { scariness.Scare(10, "signal"); + } else if (signal.addr < GetPageSizeCached()) { + scariness.Scare(10, "null-deref"); + } else if (signal.addr == signal.pc) { + scariness.Scare(60, "wild-jump"); + } else if (signal.write_flag == SignalContext::WRITE) { + scariness.Scare(30, "wild-addr-write"); + } else if (signal.write_flag == SignalContext::READ) { + scariness.Scare(20, "wild-addr-read"); + } else { + scariness.Scare(25, "wild-addr"); } } void Print(); @@ -104,17 +71,19 @@ struct ErrorDoubleFree : ErrorBase { void Print(); }; -struct ErrorNewDeleteSizeMismatch : ErrorBase { - // ErrorNewDeleteSizeMismatch doesn't own the stack trace. +struct ErrorNewDeleteTypeMismatch : ErrorBase { + // ErrorNewDeleteTypeMismatch doesn't own the stack trace. const BufferedStackTrace *free_stack; HeapAddressDescription addr_description; uptr delete_size; + uptr delete_alignment; // VS2013 doesn't implement unrestricted unions, so we need a trivial default // constructor - ErrorNewDeleteSizeMismatch() = default; - ErrorNewDeleteSizeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr, - uptr delete_size_) - : ErrorBase(tid), free_stack(stack), delete_size(delete_size_) { + ErrorNewDeleteTypeMismatch() = default; + ErrorNewDeleteTypeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr, + uptr delete_size_, uptr delete_alignment_) + : ErrorBase(tid), free_stack(stack), delete_size(delete_size_), + delete_alignment(delete_alignment_) { GetHeapAddressInformation(addr, 1, &addr_description); scariness.Clear(); scariness.Scare(10, "new-delete-type-mismatch"); @@ -324,10 +293,9 @@ struct ErrorGeneric : ErrorBase { // clang-format off #define ASAN_FOR_EACH_ERROR_KIND(macro) \ - macro(StackOverflow) \ macro(DeadlySignal) \ macro(DoubleFree) \ - macro(NewDeleteSizeMismatch) \ + macro(NewDeleteTypeMismatch) \ macro(FreeNotMalloced) \ macro(AllocTypeMismatch) \ macro(MallocUsableSizeNotOwned) \ diff --git a/lib/asan/asan_fake_stack.cc b/lib/asan/asan_fake_stack.cc index 017b7d2af129..1c6184e3c7fc 100644 --- a/lib/asan/asan_fake_stack.cc +++ b/lib/asan/asan_fake_stack.cc @@ -28,9 +28,9 @@ static const u64 kAllocaRedzoneMask = 31UL; // 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) { + if (SHADOW_SCALE == 3 && class_id <= 6) { + // This code expects SHADOW_SCALE=3. for (uptr i = 0; i < (((uptr)1) << class_id); i++) { shadow[i] = magic; // Make sure this does not become memset. @@ -171,7 +171,7 @@ void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void *arg) { } } -#if SANITIZER_LINUX && !SANITIZER_ANDROID +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA static THREADLOCAL FakeStack *fake_stack_tls; FakeStack *GetTLSFakeStack() { @@ -183,7 +183,7 @@ void SetTLSFakeStack(FakeStack *fs) { #else FakeStack *GetTLSFakeStack() { return 0; } void SetTLSFakeStack(FakeStack *fs) { } -#endif // SANITIZER_LINUX && !SANITIZER_ANDROID +#endif // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA static FakeStack *GetFakeStack() { AsanThread *t = GetCurrentThread(); diff --git a/lib/asan/asan_flags.cc b/lib/asan/asan_flags.cc index 6be0d6e94b9a..562168e6d428 100644 --- a/lib/asan/asan_flags.cc +++ b/lib/asan/asan_flags.cc @@ -118,6 +118,10 @@ void InitializeFlags() { const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions(); ubsan_parser.ParseString(ubsan_default_options); #endif +#if CAN_SANITIZE_LEAKS + const char *lsan_default_options = __lsan::MaybeCallLsanDefaultOptions(); + lsan_parser.ParseString(lsan_default_options); +#endif // Override from command line. asan_parser.ParseString(GetEnv("ASAN_OPTIONS")); @@ -144,6 +148,9 @@ void InitializeFlags() { SanitizerToolName); Die(); } + // Ensure that redzone is at least SHADOW_GRANULARITY. + if (f->redzone < (int)SHADOW_GRANULARITY) + f->redzone = SHADOW_GRANULARITY; // 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) { diff --git a/lib/asan/asan_flags.inc b/lib/asan/asan_flags.inc index f2216c2e9b3b..00071d39f041 100644 --- a/lib/asan/asan_flags.inc +++ b/lib/asan/asan_flags.inc @@ -79,6 +79,10 @@ ASAN_FLAG( "Number of seconds to sleep between printing an error report and " "terminating the program. Useful for debugging purposes (e.g. when one " "needs to attach gdb).") +ASAN_FLAG( + int, sleep_after_init, 0, + "Number of seconds to sleep after AddressSanitizer is initialized. " + "Useful for debugging purposes (e.g. when one needs to attach gdb).") ASAN_FLAG(bool, check_malloc_usable_size, true, "Allows the users to work around the bug in Nvidia drivers prior to " "295.*.") @@ -143,11 +147,6 @@ ASAN_FLAG(int, detect_odr_violation, 2, "If >=2, detect violation of One-Definition-Rule (ODR); " "If ==1, detect ODR-violation only if the two variables " "have different sizes") -ASAN_FLAG(bool, dump_instruction_bytes, false, - "If true, dump 16 bytes starting at the instruction that caused SEGV") -ASAN_FLAG(bool, dump_registers, true, - "If true, dump values of CPU registers when SEGV happens. Only " - "available on OS X for now.") ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.") ASAN_FLAG(bool, halt_on_error, true, "Crash the program after printing the first error report " diff --git a/lib/asan/asan_fuchsia.cc b/lib/asan/asan_fuchsia.cc new file mode 100644 index 000000000000..0b5bff4f565e --- /dev/null +++ b/lib/asan/asan_fuchsia.cc @@ -0,0 +1,218 @@ +//===-- asan_fuchsia.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. +// +// Fuchsia-specific details. +//===---------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_fuchsia.h" +#if SANITIZER_FUCHSIA + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_stack.h" +#include "asan_thread.h" + +#include <limits.h> +#include <zircon/sanitizer.h> +#include <zircon/syscalls.h> +#include <zircon/threads.h> + +namespace __asan { + +// The system already set up the shadow memory for us. +// __sanitizer::GetMaxUserVirtualAddress has already been called by +// AsanInitInternal->InitializeHighMemEnd (asan_rtl.cc). +// Just do some additional sanity checks here. +void InitializeShadowMemory() { + if (Verbosity()) PrintAddressSpaceLayout(); + + // Make sure SHADOW_OFFSET doesn't use __asan_shadow_memory_dynamic_address. + __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel; + DCHECK(kLowShadowBeg != kDefaultShadowSentinel); + __asan_shadow_memory_dynamic_address = kLowShadowBeg; + + CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1); + CHECK_EQ(kHighMemEnd, __sanitizer::ShadowBounds.memory_limit - 1); + CHECK_EQ(kHighMemBeg, __sanitizer::ShadowBounds.shadow_limit); + CHECK_EQ(kHighShadowBeg, __sanitizer::ShadowBounds.shadow_base); + CHECK_EQ(kShadowGapEnd, __sanitizer::ShadowBounds.shadow_base - 1); + CHECK_EQ(kLowShadowEnd, 0); + CHECK_EQ(kLowShadowBeg, 0); +} + +void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { + UNIMPLEMENTED(); +} + +void AsanCheckDynamicRTPrereqs() {} +void AsanCheckIncompatibleRT() {} +void InitializeAsanInterceptors() {} + +void *AsanDoesNotSupportStaticLinkage() { return nullptr; } + +void InitializePlatformExceptionHandlers() {} +void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { + UNIMPLEMENTED(); +} + +// We can use a plain thread_local variable for TSD. +static thread_local void *per_thread; + +void *AsanTSDGet() { return per_thread; } + +void AsanTSDSet(void *tsd) { per_thread = tsd; } + +// There's no initialization needed, and the passed-in destructor +// will never be called. Instead, our own thread destruction hook +// (below) will call AsanThread::TSDDtor directly. +void AsanTSDInit(void (*destructor)(void *tsd)) { + DCHECK(destructor == &PlatformTSDDtor); +} + +void PlatformTSDDtor(void *tsd) { UNREACHABLE(__func__); } + +static inline size_t AsanThreadMmapSize() { + return RoundUpTo(sizeof(AsanThread), PAGE_SIZE); +} + +struct AsanThread::InitOptions { + uptr stack_bottom, stack_size; +}; + +// Shared setup between thread creation and startup for the initial thread. +static AsanThread *CreateAsanThread(StackTrace *stack, u32 parent_tid, + uptr user_id, bool detached, + const char *name, uptr stack_bottom, + uptr stack_size) { + // In lieu of AsanThread::Create. + AsanThread *thread = (AsanThread *)MmapOrDie(AsanThreadMmapSize(), __func__); + + AsanThreadContext::CreateThreadContextArgs args = {thread, stack}; + u32 tid = + asanThreadRegistry().CreateThread(user_id, detached, parent_tid, &args); + asanThreadRegistry().SetThreadName(tid, name); + + // On other systems, AsanThread::Init() is called from the new + // thread itself. But on Fuchsia we already know the stack address + // range beforehand, so we can do most of the setup right now. + const AsanThread::InitOptions options = {stack_bottom, stack_size}; + thread->Init(&options); + + return thread; +} + +// This gets the same arguments passed to Init by CreateAsanThread, above. +// We're in the creator thread before the new thread is actually started, +// but its stack address range is already known. We don't bother tracking +// the static TLS address range because the system itself already uses an +// ASan-aware allocator for that. +void AsanThread::SetThreadStackAndTls(const AsanThread::InitOptions *options) { + DCHECK_NE(GetCurrentThread(), this); + DCHECK_NE(GetCurrentThread(), nullptr); + CHECK_NE(options->stack_bottom, 0); + CHECK_NE(options->stack_size, 0); + stack_bottom_ = options->stack_bottom; + stack_top_ = options->stack_bottom + options->stack_size; +} + +// Called by __asan::AsanInitInternal (asan_rtl.c). +AsanThread *CreateMainThread() { + thrd_t self = thrd_current(); + char name[ZX_MAX_NAME_LEN]; + CHECK_NE(__sanitizer::MainThreadStackBase, 0); + CHECK_GT(__sanitizer::MainThreadStackSize, 0); + AsanThread *t = CreateAsanThread( + nullptr, 0, reinterpret_cast<uptr>(self), true, + _zx_object_get_property(thrd_get_zx_handle(self), ZX_PROP_NAME, name, + sizeof(name)) == ZX_OK + ? name + : nullptr, + __sanitizer::MainThreadStackBase, __sanitizer::MainThreadStackSize); + SetCurrentThread(t); + return t; +} + +// This is called before each thread creation is attempted. So, in +// its first call, the calling thread is the initial and sole thread. +static void *BeforeThreadCreateHook(uptr user_id, bool detached, + const char *name, uptr stack_bottom, + uptr stack_size) { + EnsureMainThreadIDIsCorrect(); + // Strict init-order checking is thread-hostile. + if (flags()->strict_init_order) StopInitOrderChecking(); + + GET_STACK_TRACE_THREAD; + u32 parent_tid = GetCurrentTidOrInvalid(); + + return CreateAsanThread(&stack, parent_tid, user_id, detached, name, + stack_bottom, stack_size); +} + +// This is called after creating a new thread (in the creating thread), +// with the pointer returned by BeforeThreadCreateHook (above). +static void ThreadCreateHook(void *hook, bool aborted) { + AsanThread *thread = static_cast<AsanThread *>(hook); + if (!aborted) { + // The thread was created successfully. + // ThreadStartHook is already running in the new thread. + } else { + // The thread wasn't created after all. + // Clean up everything we set up in BeforeThreadCreateHook. + asanThreadRegistry().FinishThread(thread->tid()); + UnmapOrDie(thread, AsanThreadMmapSize()); + } +} + +// This is called in the newly-created thread before it runs anything else, +// with the pointer returned by BeforeThreadCreateHook (above). +// cf. asan_interceptors.cc:asan_thread_start +static void ThreadStartHook(void *hook, uptr os_id) { + AsanThread *thread = static_cast<AsanThread *>(hook); + SetCurrentThread(thread); + + // In lieu of AsanThread::ThreadStart. + asanThreadRegistry().StartThread(thread->tid(), os_id, /*workerthread*/ false, + nullptr); +} + +// Each thread runs this just before it exits, +// with the pointer returned by BeforeThreadCreateHook (above). +// All per-thread destructors have already been called. +static void ThreadExitHook(void *hook, uptr os_id) { + AsanThread::TSDDtor(per_thread); +} + +} // namespace __asan + +// These are declared (in extern "C") by <zircon/sanitizer.h>. +// The system runtime will call our definitions directly. + +void *__sanitizer_before_thread_create_hook(thrd_t thread, bool detached, + const char *name, void *stack_base, + size_t stack_size) { + return __asan::BeforeThreadCreateHook( + reinterpret_cast<uptr>(thread), detached, name, + reinterpret_cast<uptr>(stack_base), stack_size); +} + +void __sanitizer_thread_create_hook(void *hook, thrd_t thread, int error) { + __asan::ThreadCreateHook(hook, error != thrd_success); +} + +void __sanitizer_thread_start_hook(void *hook, thrd_t self) { + __asan::ThreadStartHook(hook, reinterpret_cast<uptr>(self)); +} + +void __sanitizer_thread_exit_hook(void *hook, thrd_t self) { + __asan::ThreadExitHook(hook, reinterpret_cast<uptr>(self)); +} + +#endif // SANITIZER_FUCHSIA diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc index eebada804f07..0db65d010349 100644 --- a/lib/asan/asan_globals.cc +++ b/lib/asan/asan_globals.cc @@ -384,6 +384,10 @@ void __asan_register_globals(__asan_global *globals, uptr n) { } RegisterGlobal(&globals[i]); } + + // Poison the metadata. It should not be accessible to user code. + PoisonShadow(reinterpret_cast<uptr>(globals), n * sizeof(__asan_global), + kAsanGlobalRedzoneMagic); } // Unregister an array of globals. @@ -399,6 +403,9 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) { } UnregisterGlobal(&globals[i]); } + + // Unpoison the metadata. + PoisonShadow(reinterpret_cast<uptr>(globals), n * sizeof(__asan_global), 0); } // This method runs immediately prior to dynamic initialization in each TU, diff --git a/lib/asan/asan_init_version.h b/lib/asan/asan_init_version.h index f48cc19cc515..c49fcd740248 100644 --- a/lib/asan/asan_init_version.h +++ b/lib/asan/asan_init_version.h @@ -15,6 +15,8 @@ #ifndef ASAN_INIT_VERSION_H #define ASAN_INIT_VERSION_H +#include "sanitizer_common/sanitizer_platform.h" + extern "C" { // Every time the ASan ABI changes we also change the version number in the // __asan_init function name. Objects built with incompatible ASan ABI @@ -32,7 +34,12 @@ extern "C" { // v6=>v7: added 'odr_indicator' to __asan_global // v7=>v8: added '__asan_(un)register_image_globals' functions for dead // stripping support on Mach-O platforms +#if SANITIZER_WORDSIZE == 32 && SANITIZER_ANDROID + // v8=>v9: 32-bit Android switched to dynamic shadow + #define __asan_version_mismatch_check __asan_version_mismatch_check_v9 +#else #define __asan_version_mismatch_check __asan_version_mismatch_check_v8 +#endif } #endif // ASAN_INIT_VERSION_H diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc index 34ca22b8616e..cb7dcb3b1ca8 100644 --- a/lib/asan/asan_interceptors.cc +++ b/lib/asan/asan_interceptors.cc @@ -24,6 +24,11 @@ #include "lsan/lsan_common.h" #include "sanitizer_common/sanitizer_libc.h" +// There is no general interception at all on Fuchsia. +// Only the functions in asan_interceptors_memintrinsics.cc are +// really defined to replace libc functions. +#if !SANITIZER_FUCHSIA + #if SANITIZER_POSIX #include "sanitizer_common/sanitizer_posix.h" #endif @@ -36,108 +41,6 @@ namespace __asan { -// Return true if we can quickly decide that the region is unpoisoned. -// We assume that a redzone is at least 16 bytes. -static inline bool QuickCheckForUnpoisonedRegion(uptr beg, uptr size) { - if (size == 0) return true; - if (size <= 32) - return !AddressIsPoisoned(beg) && - !AddressIsPoisoned(beg + size - 1) && - !AddressIsPoisoned(beg + size / 2); - if (size <= 64) - return !AddressIsPoisoned(beg) && - !AddressIsPoisoned(beg + size / 4) && - !AddressIsPoisoned(beg + size - 1) && - !AddressIsPoisoned(beg + 3 * size / 4) && - !AddressIsPoisoned(beg + size / 2); - return false; -} - -struct AsanInterceptorContext { - const char *interceptor_name; -}; - -// We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE, -// and ASAN_WRITE_RANGE as macro instead of function so -// that no extra frames are created, and stack trace contains -// relevant information only. -// We check all shadow bytes. -#define ACCESS_MEMORY_RANGE(ctx, offset, size, isWrite) do { \ - uptr __offset = (uptr)(offset); \ - uptr __size = (uptr)(size); \ - uptr __bad = 0; \ - if (__offset > __offset + __size) { \ - GET_STACK_TRACE_FATAL_HERE; \ - ReportStringFunctionSizeOverflow(__offset, __size, &stack); \ - } \ - if (!QuickCheckForUnpoisonedRegion(__offset, __size) && \ - (__bad = __asan_region_is_poisoned(__offset, __size))) { \ - AsanInterceptorContext *_ctx = (AsanInterceptorContext *)ctx; \ - bool suppressed = false; \ - if (_ctx) { \ - suppressed = IsInterceptorSuppressed(_ctx->interceptor_name); \ - if (!suppressed && HaveStackTraceBasedSuppressions()) { \ - GET_STACK_TRACE_FATAL_HERE; \ - suppressed = IsStackTraceSuppressed(&stack); \ - } \ - } \ - if (!suppressed) { \ - GET_CURRENT_PC_BP_SP; \ - ReportGenericError(pc, bp, sp, __bad, isWrite, __size, 0, false);\ - } \ - } \ - } while (0) - -// memcpy is called during __asan_init() from the internals of printf(...). -// We do not treat memcpy with to==from as a bug. -// See http://llvm.org/bugs/show_bug.cgi?id=11763. -#define ASAN_MEMCPY_IMPL(ctx, to, from, size) \ - do { \ - if (UNLIKELY(!asan_inited)) return internal_memcpy(to, from, size); \ - if (asan_init_is_running) { \ - return REAL(memcpy)(to, from, size); \ - } \ - ENSURE_ASAN_INITED(); \ - if (flags()->replace_intrin) { \ - if (to != from) { \ - CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); \ - } \ - ASAN_READ_RANGE(ctx, from, size); \ - ASAN_WRITE_RANGE(ctx, to, size); \ - } \ - return REAL(memcpy)(to, from, size); \ - } while (0) - -// memset is called inside Printf. -#define ASAN_MEMSET_IMPL(ctx, block, c, size) \ - do { \ - if (UNLIKELY(!asan_inited)) return internal_memset(block, c, size); \ - if (asan_init_is_running) { \ - return REAL(memset)(block, c, size); \ - } \ - ENSURE_ASAN_INITED(); \ - if (flags()->replace_intrin) { \ - ASAN_WRITE_RANGE(ctx, block, size); \ - } \ - return REAL(memset)(block, c, size); \ - } while (0) - -#define ASAN_MEMMOVE_IMPL(ctx, to, from, size) \ - do { \ - if (UNLIKELY(!asan_inited)) return internal_memmove(to, from, size); \ - ENSURE_ASAN_INITED(); \ - if (flags()->replace_intrin) { \ - ASAN_READ_RANGE(ctx, from, size); \ - ASAN_WRITE_RANGE(ctx, to, size); \ - } \ - return internal_memmove(to, from, size); \ - } while (0) - -#define ASAN_READ_RANGE(ctx, offset, size) \ - ACCESS_MEMORY_RANGE(ctx, offset, size, false) -#define ASAN_WRITE_RANGE(ctx, offset, size) \ - ACCESS_MEMORY_RANGE(ctx, offset, size, true) - #define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \ ASAN_READ_RANGE((ctx), (s), \ common_flags()->strict_string_checks ? (len) + 1 : (n)) @@ -145,23 +48,6 @@ struct AsanInterceptorContext { #define ASAN_READ_STRING(ctx, s, n) \ ASAN_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n)) -// Behavior of functions like "memcpy" or "strcpy" is undefined -// if memory intervals overlap. We report error in this case. -// Macro is used to avoid creation of new frames. -static inline bool RangesOverlap(const char *offset1, uptr length1, - const char *offset2, uptr length2) { - return !((offset1 + length1 <= offset2) || (offset2 + length2 <= offset1)); -} -#define CHECK_RANGES_OVERLAP(name, _offset1, length1, _offset2, length2) do { \ - const char *offset1 = (const char*)_offset1; \ - const char *offset2 = (const char*)_offset2; \ - if (RangesOverlap(offset1, length1, offset2, length2)) { \ - GET_STACK_TRACE_FATAL_HERE; \ - ReportStringFunctionMemoryRangesOverlap(name, offset1, length1, \ - offset2, length2, &stack); \ - } \ -} while (0) - static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { #if SANITIZER_INTERCEPT_STRNLEN if (REAL(strnlen)) { @@ -275,6 +161,7 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) } while (false) #include "sanitizer_common/sanitizer_common_interceptors.inc" +#include "sanitizer_common/sanitizer_signal_interceptors.inc" // Syscall interceptors don't have contexts, we don't support suppressions // for them. @@ -356,42 +243,6 @@ INTERCEPTOR(int, pthread_join, void *t, void **arg) { DEFINE_REAL_PTHREAD_FUNCTIONS #endif // ASAN_INTERCEPT_PTHREAD_CREATE -#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION - -#if SANITIZER_ANDROID -INTERCEPTOR(void*, bsd_signal, int signum, void *handler) { - if (GetHandleSignalMode(signum) != kHandleSignalExclusive) - return REAL(bsd_signal)(signum, handler); - return 0; -} -#endif - -INTERCEPTOR(void*, signal, int signum, void *handler) { - if (GetHandleSignalMode(signum) != kHandleSignalExclusive) - return REAL(signal)(signum, handler); - return nullptr; -} - -INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, - struct sigaction *oldact) { - if (GetHandleSignalMode(signum) != kHandleSignalExclusive) - return REAL(sigaction)(signum, act, oldact); - return 0; -} - -namespace __sanitizer { -int real_sigaction(int signum, const void *act, void *oldact) { - return REAL(sigaction)(signum, (const struct sigaction *)act, - (struct sigaction *)oldact); -} -} // namespace __sanitizer - -#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) -#endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION - #if ASAN_INTERCEPT_SWAPCONTEXT static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) { // Align to page size. @@ -428,6 +279,11 @@ INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp, } #endif // ASAN_INTERCEPT_SWAPCONTEXT +#if SANITIZER_NETBSD +#define longjmp __longjmp14 +#define siglongjmp __siglongjmp14 +#endif + INTERCEPTOR(void, longjmp, void *env, int val) { __asan_handle_no_return(); REAL(longjmp)(env, val); @@ -462,18 +318,6 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) { } #endif -void *__asan_memcpy(void *to, const void *from, uptr size) { - ASAN_MEMCPY_IMPL(nullptr, to, from, size); -} - -void *__asan_memset(void *block, int c, uptr size) { - ASAN_MEMSET_IMPL(nullptr, block, c, size); -} - -void *__asan_memmove(void *to, const void *from, uptr size) { - ASAN_MEMMOVE_IMPL(nullptr, to, from, size); -} - #if ASAN_INTERCEPT_INDEX # if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX INTERCEPTOR(char*, index, const char *string, int c) @@ -711,6 +555,7 @@ void InitializeAsanInterceptors() { CHECK(!was_called_once); was_called_once = true; InitializeCommonInterceptors(); + InitializeSignalInterceptors(); // Intercept str* functions. ASAN_INTERCEPT_FUNC(strcat); // NOLINT @@ -733,15 +578,9 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(strtoll); #endif - // Intecept signal- and jump-related functions. + // Intecept jump-related functions. ASAN_INTERCEPT_FUNC(longjmp); -#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION - ASAN_INTERCEPT_FUNC(sigaction); -#if SANITIZER_ANDROID - ASAN_INTERCEPT_FUNC(bsd_signal); -#endif - ASAN_INTERCEPT_FUNC(signal); -#endif + #if ASAN_INTERCEPT_SWAPCONTEXT ASAN_INTERCEPT_FUNC(swapcontext); #endif @@ -785,3 +624,5 @@ void InitializeAsanInterceptors() { } } // namespace __asan + +#endif // !SANITIZER_FUCHSIA diff --git a/lib/asan/asan_interceptors.h b/lib/asan/asan_interceptors.h index 93fca4f67366..e13bdecfdede 100644 --- a/lib/asan/asan_interceptors.h +++ b/lib/asan/asan_interceptors.h @@ -15,9 +15,30 @@ #define ASAN_INTERCEPTORS_H #include "asan_internal.h" +#include "asan_interceptors_memintrinsics.h" #include "interception/interception.h" #include "sanitizer_common/sanitizer_platform_interceptors.h" +namespace __asan { + +void InitializeAsanInterceptors(); +void InitializePlatformInterceptors(); + +#define ENSURE_ASAN_INITED() \ + do { \ + CHECK(!asan_init_is_running); \ + if (UNLIKELY(!asan_inited)) { \ + AsanInitFromRtl(); \ + } \ + } while (0) + +} // namespace __asan + +// There is no general interception at all on Fuchsia. +// Only the functions in asan_interceptors_memintrinsics.h are +// really defined to replace libc functions. +#if !SANITIZER_FUCHSIA + // Use macro to describe if specific function should be // intercepted on a given platform. #if !SANITIZER_WINDOWS @@ -34,25 +55,20 @@ # define ASAN_INTERCEPT_FORK 0 #endif -#if SANITIZER_FREEBSD || SANITIZER_LINUX +#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ + SANITIZER_SOLARIS # define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1 #else # define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0 #endif -#if SANITIZER_LINUX && !SANITIZER_ANDROID +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_SOLARIS # define ASAN_INTERCEPT_SWAPCONTEXT 1 #else # define ASAN_INTERCEPT_SWAPCONTEXT 0 #endif #if !SANITIZER_WINDOWS -# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1 -#else -# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0 -#endif - -#if !SANITIZER_WINDOWS # define ASAN_INTERCEPT_SIGLONGJMP 1 #else # define ASAN_INTERCEPT_SIGLONGJMP 0 @@ -66,7 +82,8 @@ // Android bug: https://code.google.com/p/android/issues/detail?id=61799 #if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS && \ - !(SANITIZER_ANDROID && defined(__i386)) + !(SANITIZER_ANDROID && defined(__i386)) && \ + !SANITIZER_SOLARIS # define ASAN_INTERCEPT___CXA_THROW 1 #else # define ASAN_INTERCEPT___CXA_THROW 0 @@ -85,16 +102,11 @@ #endif DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size) -DECLARE_REAL(void*, memcpy, void *to, const void *from, uptr size) -DECLARE_REAL(void*, memset, void *block, int c, uptr size) DECLARE_REAL(char*, strchr, const char *str, int c) DECLARE_REAL(SIZE_T, strlen, const char *s) DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size) DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen) DECLARE_REAL(char*, strstr, const char *s1, const char *s2) -struct sigaction; -DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act, - struct sigaction *oldact) #if !SANITIZER_MAC #define ASAN_INTERCEPT_FUNC(name) \ @@ -113,18 +125,6 @@ DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act, #define ASAN_INTERCEPT_FUNC(name) #endif // SANITIZER_MAC -namespace __asan { - -void InitializeAsanInterceptors(); -void InitializePlatformInterceptors(); - -#define ENSURE_ASAN_INITED() do { \ - CHECK(!asan_init_is_running); \ - if (UNLIKELY(!asan_inited)) { \ - AsanInitFromRtl(); \ - } \ -} while (0) - -} // namespace __asan +#endif // !SANITIZER_FUCHSIA #endif // ASAN_INTERCEPTORS_H diff --git a/lib/asan/asan_interceptors_memintrinsics.cc b/lib/asan/asan_interceptors_memintrinsics.cc new file mode 100644 index 000000000000..c89cb011492e --- /dev/null +++ b/lib/asan/asan_interceptors_memintrinsics.cc @@ -0,0 +1,44 @@ +//===-- asan_interceptors_memintrinsics.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. +// +// ASan versions of memcpy, memmove, and memset. +//===---------------------------------------------------------------------===// + +#include "asan_interceptors_memintrinsics.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_suppressions.h" + +using namespace __asan; // NOLINT + +void *__asan_memcpy(void *to, const void *from, uptr size) { + ASAN_MEMCPY_IMPL(nullptr, to, from, size); +} + +void *__asan_memset(void *block, int c, uptr size) { + ASAN_MEMSET_IMPL(nullptr, block, c, size); +} + +void *__asan_memmove(void *to, const void *from, uptr size) { + ASAN_MEMMOVE_IMPL(nullptr, to, from, size); +} + +#if SANITIZER_FUCHSIA + +// Fuchsia doesn't use sanitizer_common_interceptors.inc, but the only +// things there it wants are these three. Just define them as aliases +// here rather than repeating the contents. + +decltype(memcpy) memcpy[[gnu::alias("__asan_memcpy")]]; +decltype(memmove) memmove[[gnu::alias("__asan_memmove")]]; +decltype(memset) memset[[gnu::alias("__asan_memset")]]; + +#endif // SANITIZER_FUCHSIA diff --git a/lib/asan/asan_interceptors_memintrinsics.h b/lib/asan/asan_interceptors_memintrinsics.h new file mode 100644 index 000000000000..5a8339a23e97 --- /dev/null +++ b/lib/asan/asan_interceptors_memintrinsics.h @@ -0,0 +1,148 @@ +//===-- asan_interceptors_memintrinsics.h -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_memintrin.cc +//===---------------------------------------------------------------------===// +#ifndef ASAN_MEMINTRIN_H +#define ASAN_MEMINTRIN_H + +#include "asan_interface_internal.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "interception/interception.h" + +DECLARE_REAL(void*, memcpy, void *to, const void *from, uptr size) +DECLARE_REAL(void*, memset, void *block, int c, uptr size) + +namespace __asan { + +// Return true if we can quickly decide that the region is unpoisoned. +// We assume that a redzone is at least 16 bytes. +static inline bool QuickCheckForUnpoisonedRegion(uptr beg, uptr size) { + if (size == 0) return true; + if (size <= 32) + return !AddressIsPoisoned(beg) && + !AddressIsPoisoned(beg + size - 1) && + !AddressIsPoisoned(beg + size / 2); + if (size <= 64) + return !AddressIsPoisoned(beg) && + !AddressIsPoisoned(beg + size / 4) && + !AddressIsPoisoned(beg + size - 1) && + !AddressIsPoisoned(beg + 3 * size / 4) && + !AddressIsPoisoned(beg + size / 2); + return false; +} + +struct AsanInterceptorContext { + const char *interceptor_name; +}; + +// We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE, +// and ASAN_WRITE_RANGE as macro instead of function so +// that no extra frames are created, and stack trace contains +// relevant information only. +// We check all shadow bytes. +#define ACCESS_MEMORY_RANGE(ctx, offset, size, isWrite) do { \ + uptr __offset = (uptr)(offset); \ + uptr __size = (uptr)(size); \ + uptr __bad = 0; \ + if (__offset > __offset + __size) { \ + GET_STACK_TRACE_FATAL_HERE; \ + ReportStringFunctionSizeOverflow(__offset, __size, &stack); \ + } \ + if (!QuickCheckForUnpoisonedRegion(__offset, __size) && \ + (__bad = __asan_region_is_poisoned(__offset, __size))) { \ + AsanInterceptorContext *_ctx = (AsanInterceptorContext *)ctx; \ + bool suppressed = false; \ + if (_ctx) { \ + suppressed = IsInterceptorSuppressed(_ctx->interceptor_name); \ + if (!suppressed && HaveStackTraceBasedSuppressions()) { \ + GET_STACK_TRACE_FATAL_HERE; \ + suppressed = IsStackTraceSuppressed(&stack); \ + } \ + } \ + if (!suppressed) { \ + GET_CURRENT_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, __bad, isWrite, __size, 0, false);\ + } \ + } \ + } while (0) + +// memcpy is called during __asan_init() from the internals of printf(...). +// We do not treat memcpy with to==from as a bug. +// See http://llvm.org/bugs/show_bug.cgi?id=11763. +#define ASAN_MEMCPY_IMPL(ctx, to, from, size) \ + do { \ + if (UNLIKELY(!asan_inited)) return internal_memcpy(to, from, size); \ + if (asan_init_is_running) { \ + return REAL(memcpy)(to, from, size); \ + } \ + ENSURE_ASAN_INITED(); \ + if (flags()->replace_intrin) { \ + if (to != from) { \ + CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); \ + } \ + ASAN_READ_RANGE(ctx, from, size); \ + ASAN_WRITE_RANGE(ctx, to, size); \ + } \ + return REAL(memcpy)(to, from, size); \ + } while (0) + +// memset is called inside Printf. +#define ASAN_MEMSET_IMPL(ctx, block, c, size) \ + do { \ + if (UNLIKELY(!asan_inited)) return internal_memset(block, c, size); \ + if (asan_init_is_running) { \ + return REAL(memset)(block, c, size); \ + } \ + ENSURE_ASAN_INITED(); \ + if (flags()->replace_intrin) { \ + ASAN_WRITE_RANGE(ctx, block, size); \ + } \ + return REAL(memset)(block, c, size); \ + } while (0) + +#define ASAN_MEMMOVE_IMPL(ctx, to, from, size) \ + do { \ + if (UNLIKELY(!asan_inited)) return internal_memmove(to, from, size); \ + ENSURE_ASAN_INITED(); \ + if (flags()->replace_intrin) { \ + ASAN_READ_RANGE(ctx, from, size); \ + ASAN_WRITE_RANGE(ctx, to, size); \ + } \ + return internal_memmove(to, from, size); \ + } while (0) + +#define ASAN_READ_RANGE(ctx, offset, size) \ + ACCESS_MEMORY_RANGE(ctx, offset, size, false) +#define ASAN_WRITE_RANGE(ctx, offset, size) \ + ACCESS_MEMORY_RANGE(ctx, offset, size, true) + +// Behavior of functions like "memcpy" or "strcpy" is undefined +// if memory intervals overlap. We report error in this case. +// Macro is used to avoid creation of new frames. +static inline bool RangesOverlap(const char *offset1, uptr length1, + const char *offset2, uptr length2) { + return !((offset1 + length1 <= offset2) || (offset2 + length2 <= offset1)); +} +#define CHECK_RANGES_OVERLAP(name, _offset1, length1, _offset2, length2) do { \ + const char *offset1 = (const char*)_offset1; \ + const char *offset2 = (const char*)_offset2; \ + if (RangesOverlap(offset1, length1, offset2, length2)) { \ + GET_STACK_TRACE_FATAL_HERE; \ + ReportStringFunctionMemoryRangesOverlap(name, offset1, length1, \ + offset2, length2, &stack); \ + } \ +} while (0) + +} // namespace __asan + +#endif // ASAN_MEMINTRIN_H diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h index f09bbd83af25..19133e5291a9 100644 --- a/lib/asan/asan_internal.h +++ b/lib/asan/asan_internal.h @@ -69,8 +69,12 @@ void InitializePlatformExceptionHandlers(); bool IsSystemHeapAddress(uptr addr); // asan_rtl.cc +void PrintAddressSpaceLayout(); void NORETURN ShowStatsAndAbort(); +// asan_shadow_setup.cc +void InitializeShadowMemory(); + // asan_malloc_linux.cc / asan_malloc_mac.cc void ReplaceSystemMalloc(); @@ -80,6 +84,9 @@ void *AsanDoesNotSupportStaticLinkage(); void AsanCheckDynamicRTPrereqs(); void AsanCheckIncompatibleRT(); +// asan_thread.cc +AsanThread *CreateMainThread(); + // Support function for __asan_(un)register_image_globals. Searches for the // loaded image containing `needle' and then enumerates all global metadata // structures declared in that image, applying `op' (e.g., diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc index 6d47ba432a61..047e1dbb72fa 100644 --- a/lib/asan/asan_linux.cc +++ b/lib/asan/asan_linux.cc @@ -13,10 +13,12 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_FREEBSD || SANITIZER_LINUX +#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ + SANITIZER_SOLARIS #include "asan_interceptors.h" #include "asan_internal.h" +#include "asan_premap_shadow.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_freebsd.h" @@ -39,9 +41,17 @@ #include <sys/link_elf.h> #endif -#if SANITIZER_ANDROID || SANITIZER_FREEBSD +#if SANITIZER_SOLARIS +#include <link.h> +#endif + +#if SANITIZER_ANDROID || SANITIZER_FREEBSD || SANITIZER_SOLARIS #include <ucontext.h> extern "C" void* _DYNAMIC; +#elif SANITIZER_NETBSD +#include <link_elf.h> +#include <ucontext.h> +extern Elf_Dyn _DYNAMIC; #else #include <sys/ucontext.h> #include <link.h> @@ -77,9 +87,51 @@ void *AsanDoesNotSupportStaticLinkage() { return &_DYNAMIC; // defined in link.h } +static void UnmapFromTo(uptr from, uptr to) { + CHECK(to >= from); + if (to == from) return; + uptr res = internal_munmap(reinterpret_cast<void *>(from), to - from); + if (UNLIKELY(internal_iserror(res))) { + Report( + "ERROR: AddresSanitizer failed to unmap 0x%zx (%zd) bytes at address " + "%p\n", + to - from, to - from, from); + CHECK("unable to unmap" && 0); + } +} + +#if ASAN_PREMAP_SHADOW +uptr FindPremappedShadowStart() { + uptr granularity = GetMmapGranularity(); + uptr shadow_start = reinterpret_cast<uptr>(&__asan_shadow); + uptr premap_shadow_size = PremapShadowSize(); + uptr shadow_size = RoundUpTo(kHighShadowEnd, granularity); + // We may have mapped too much. Release extra memory. + UnmapFromTo(shadow_start + shadow_size, shadow_start + premap_shadow_size); + return shadow_start; +} +#endif + uptr FindDynamicShadowStart() { - UNREACHABLE("FindDynamicShadowStart is not available"); - return 0; +#if ASAN_PREMAP_SHADOW + if (!PremapShadowFailed()) + return FindPremappedShadowStart(); +#endif + + uptr granularity = GetMmapGranularity(); + uptr alignment = granularity * 8; + uptr left_padding = granularity; + uptr shadow_size = RoundUpTo(kHighShadowEnd, granularity); + uptr map_size = shadow_size + left_padding + alignment; + + uptr map_start = (uptr)MmapNoAccess(map_size); + CHECK_NE(map_start, ~(uptr)0); + + uptr shadow_start = RoundUpTo(map_start + left_padding, alignment); + UnmapFromTo(map_start, shadow_start - left_padding); + UnmapFromTo(shadow_start + shadow_size, map_start + map_size); + + return shadow_start; } void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { @@ -93,6 +145,9 @@ void AsanCheckIncompatibleRT() {} #else static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size, void *data) { + VReport(2, "info->dlpi_name = %s\tinfo->dlpi_addr = %p\n", + info->dlpi_name, info->dlpi_addr); + // Continue until the first dynamic library is found if (!info->dlpi_name || info->dlpi_name[0] == 0) return 0; @@ -101,6 +156,21 @@ static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size, if (internal_strncmp(info->dlpi_name, "linux-", sizeof("linux-") - 1) == 0) return 0; +#if SANITIZER_FREEBSD || SANITIZER_NETBSD + // Ignore first entry (the main program) + char **p = (char **)data; + if (!(*p)) { + *p = (char *)-1; + return 0; + } +#endif + +#if SANITIZER_SOLARIS + // Ignore executable on Solaris + if (info->dlpi_addr == 0) + return 0; +#endif + *(const char **)data = info->dlpi_name; return 1; } @@ -179,4 +249,5 @@ void *AsanDlSymNext(const char *sym) { } // namespace __asan -#endif // SANITIZER_FREEBSD || SANITIZER_LINUX +#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || + // SANITIZER_SOLARIS diff --git a/lib/asan/asan_malloc_linux.cc b/lib/asan/asan_malloc_linux.cc index fd40f47db1c4..6697ff8764ed 100644 --- a/lib/asan/asan_malloc_linux.cc +++ b/lib/asan/asan_malloc_linux.cc @@ -15,7 +15,8 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_FREEBSD || SANITIZER_LINUX +#if SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || \ + SANITIZER_NETBSD || SANITIZER_SOLARIS #include "sanitizer_common/sanitizer_tls_get_addr.h" #include "asan_allocator.h" @@ -30,9 +31,9 @@ static uptr allocated_for_dlsym; static const uptr kDlsymAllocPoolSize = 1024; static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize]; -static bool IsInDlsymAllocPool(const void *ptr) { +static INLINE bool IsInDlsymAllocPool(const void *ptr) { uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym; - return off < sizeof(alloc_memory_for_dlsym); + return off < allocated_for_dlsym * sizeof(alloc_memory_for_dlsym[0]); } static void *AllocateFromLocalPool(uptr size_in_bytes) { @@ -43,6 +44,26 @@ static void *AllocateFromLocalPool(uptr size_in_bytes) { return mem; } +static INLINE bool MaybeInDlsym() { + // Fuchsia doesn't use dlsym-based interceptors. + return !SANITIZER_FUCHSIA && asan_init_is_running; +} + +static void *ReallocFromLocalPool(void *ptr, uptr size) { + const uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym; + const uptr copy_size = Min(size, kDlsymAllocPoolSize - offset); + void *new_ptr; + if (UNLIKELY(MaybeInDlsym())) { + new_ptr = AllocateFromLocalPool(size); + } else { + ENSURE_ASAN_INITED(); + GET_STACK_TRACE_MALLOC; + new_ptr = asan_malloc(size, &stack); + } + internal_memcpy(new_ptr, ptr, copy_size); + return new_ptr; +} + INTERCEPTOR(void, free, void *ptr) { GET_STACK_TRACE_FREE; if (UNLIKELY(IsInDlsymAllocPool(ptr))) @@ -60,7 +81,7 @@ INTERCEPTOR(void, cfree, void *ptr) { #endif // SANITIZER_INTERCEPT_CFREE INTERCEPTOR(void*, malloc, uptr size) { - if (UNLIKELY(asan_init_is_running)) + if (UNLIKELY(MaybeInDlsym())) // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym. return AllocateFromLocalPool(size); ENSURE_ASAN_INITED(); @@ -69,7 +90,7 @@ INTERCEPTOR(void*, malloc, uptr size) { } INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { - if (UNLIKELY(asan_init_is_running)) + if (UNLIKELY(MaybeInDlsym())) // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. return AllocateFromLocalPool(nmemb * size); ENSURE_ASAN_INITED(); @@ -78,21 +99,9 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { } INTERCEPTOR(void*, realloc, void *ptr, uptr size) { - if (UNLIKELY(IsInDlsymAllocPool(ptr))) { - const uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym; - const uptr copy_size = Min(size, kDlsymAllocPoolSize - offset); - void *new_ptr; - if (UNLIKELY(asan_init_is_running)) { - new_ptr = AllocateFromLocalPool(size); - } else { - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - new_ptr = asan_malloc(size, &stack); - } - internal_memcpy(new_ptr, ptr, copy_size); - return new_ptr; - } - if (UNLIKELY(asan_init_is_running)) + if (UNLIKELY(IsInDlsymAllocPool(ptr))) + return ReallocFromLocalPool(ptr, size); + if (UNLIKELY(MaybeInDlsym())) return AllocateFromLocalPool(size); ENSURE_ASAN_INITED(); GET_STACK_TRACE_MALLOC; @@ -226,4 +235,5 @@ void ReplaceSystemMalloc() { } // namespace __asan #endif // SANITIZER_ANDROID -#endif // SANITIZER_FREEBSD || SANITIZER_LINUX +#endif // SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || + // SANITIZER_NETBSD || SANITIZER_SOLARIS diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h index 695740cd982f..d3f360ffc969 100644 --- a/lib/asan/asan_mapping.h +++ b/lib/asan/asan_mapping.h @@ -115,6 +115,13 @@ // || `[0x40000000, 0x47ffffff]` || LowShadow || // || `[0x00000000, 0x3fffffff]` || LowMem || // +// Shadow mapping on NetBSD/x86-64 with SHADOW_OFFSET == 0x400000000000: +// || `[0x4feffffffe01, 0x7f7ffffff000]` || HighMem || +// || `[0x49fdffffffc0, 0x4feffffffe00]` || HighShadow || +// || `[0x480000000000, 0x49fdffffffbf]` || ShadowGap || +// || `[0x400000000000, 0x47ffffffffff]` || LowShadow || +// || `[0x000000000000, 0x3fffffffffff]` || LowMem || +// // Default Windows/i386 mapping: // (the exact location of HighShadow/HighMem may vary depending // on WoW64, /LARGEADDRESSAWARE, etc). @@ -124,11 +131,16 @@ // || `[0x30000000, 0x35ffffff]` || LowShadow || // || `[0x00000000, 0x2fffffff]` || LowMem || +#if defined(ASAN_SHADOW_SCALE) +static const u64 kDefaultShadowScale = ASAN_SHADOW_SCALE; +#else static const u64 kDefaultShadowScale = 3; +#endif static const u64 kDefaultShadowSentinel = ~(uptr)0; static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000 static const u64 kDefaultShadowOffset64 = 1ULL << 44; -static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G. +static const u64 kDefaultShort64bitShadowOffset = + 0x7FFFFFFF & (~0xFFFULL << kDefaultShadowScale); // < 2G. static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000 static const u64 kIosShadowOffset64 = 0x120200000; static const u64 kIosSimShadowOffset32 = 1ULL << 30; @@ -136,18 +148,20 @@ static const u64 kIosSimShadowOffset64 = kDefaultShadowOffset64; static const u64 kAArch64_ShadowOffset64 = 1ULL << 36; static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000; static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37; -static const u64 kPPC64_ShadowOffset64 = 1ULL << 41; +static const u64 kPPC64_ShadowOffset64 = 1ULL << 44; static const u64 kSystemZ_ShadowOffset64 = 1ULL << 52; static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000 static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000 +static const u64 kNetBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000 static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 #define SHADOW_SCALE kDefaultShadowScale - -#if SANITIZER_WORDSIZE == 32 +#if SANITIZER_FUCHSIA +# define SHADOW_OFFSET (0) +#elif SANITIZER_WORDSIZE == 32 # if SANITIZER_ANDROID -# define SHADOW_OFFSET (0) +# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address # elif defined(__mips__) # define SHADOW_OFFSET kMIPS32_ShadowOffset32 # elif SANITIZER_FREEBSD @@ -178,6 +192,8 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 # define SHADOW_OFFSET kSystemZ_ShadowOffset64 # elif SANITIZER_FREEBSD # define SHADOW_OFFSET kFreeBSD_ShadowOffset64 +# elif SANITIZER_NETBSD +# define SHADOW_OFFSET kNetBSD_ShadowOffset64 # elif SANITIZER_MAC # define SHADOW_OFFSET kDefaultShadowOffset64 # elif defined(__mips64) @@ -189,6 +205,12 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 # endif #endif +#if SANITIZER_ANDROID && defined(__arm__) +# define ASAN_PREMAP_SHADOW 1 +#else +# define ASAN_PREMAP_SHADOW 0 +#endif + #define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE) #define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) + (SHADOW_OFFSET)) diff --git a/lib/asan/asan_memory_profile.cc b/lib/asan/asan_memory_profile.cc index 05846c37cb6d..603284c8c688 100644 --- a/lib/asan/asan_memory_profile.cc +++ b/lib/asan/asan_memory_profile.cc @@ -107,6 +107,9 @@ static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list, __lsan::ForEachChunk(ChunkCallback, &hp); uptr *Arg = reinterpret_cast<uptr*>(argument); hp.Print(Arg[0], Arg[1]); + + if (Verbosity()) + __asan_print_accumulated_stats(); } } // namespace __asan diff --git a/lib/asan/asan_new_delete.cc b/lib/asan/asan_new_delete.cc index e68c7f3e2400..072f027addd6 100644 --- a/lib/asan/asan_new_delete.cc +++ b/lib/asan/asan_new_delete.cc @@ -125,77 +125,69 @@ INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) { INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/); } -#endif +#endif // !SANITIZER_MAC #define OPERATOR_DELETE_BODY(type) \ GET_STACK_TRACE_FREE;\ - asan_free(ptr, &stack, type); + asan_delete(ptr, 0, 0, &stack, type); + +#define OPERATOR_DELETE_BODY_SIZE(type) \ + GET_STACK_TRACE_FREE;\ + asan_delete(ptr, size, 0, &stack, type); + +#define OPERATOR_DELETE_BODY_ALIGN(type) \ + GET_STACK_TRACE_FREE;\ + asan_delete(ptr, 0, static_cast<uptr>(align), &stack, type); + +#define OPERATOR_DELETE_BODY_SIZE_ALIGN(type) \ + GET_STACK_TRACE_FREE;\ + asan_delete(ptr, size, static_cast<uptr>(align), &stack, type); #if !SANITIZER_MAC CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr) NOEXCEPT { - OPERATOR_DELETE_BODY(FROM_NEW); -} +void operator delete(void *ptr) NOEXCEPT +{ OPERATOR_DELETE_BODY(FROM_NEW); } CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr) NOEXCEPT { - OPERATOR_DELETE_BODY(FROM_NEW_BR); -} +void operator delete[](void *ptr) NOEXCEPT +{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, std::nothrow_t const&) { - OPERATOR_DELETE_BODY(FROM_NEW); -} +void operator delete(void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FROM_NEW); } CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, std::nothrow_t const&) { - OPERATOR_DELETE_BODY(FROM_NEW_BR); -} +void operator delete[](void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, size_t size) NOEXCEPT { - GET_STACK_TRACE_FREE; - asan_sized_free(ptr, size, &stack, FROM_NEW); -} +void operator delete(void *ptr, size_t size) NOEXCEPT +{ OPERATOR_DELETE_BODY_SIZE(FROM_NEW); } CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, size_t size) NOEXCEPT { - GET_STACK_TRACE_FREE; - asan_sized_free(ptr, size, &stack, FROM_NEW_BR); -} +void operator delete[](void *ptr, size_t size) NOEXCEPT +{ OPERATOR_DELETE_BODY_SIZE(FROM_NEW_BR); } CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, std::align_val_t) NOEXCEPT { - OPERATOR_DELETE_BODY(FROM_NEW); -} +void operator delete(void *ptr, std::align_val_t align) NOEXCEPT +{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW); } CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, std::align_val_t) NOEXCEPT { - OPERATOR_DELETE_BODY(FROM_NEW_BR); -} +void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT +{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW_BR); } CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, std::align_val_t, std::nothrow_t const&) { - OPERATOR_DELETE_BODY(FROM_NEW); -} +void operator delete(void *ptr, std::align_val_t align, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW); } CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, std::align_val_t, std::nothrow_t const&) { - OPERATOR_DELETE_BODY(FROM_NEW_BR); -} +void operator delete[](void *ptr, std::align_val_t align, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW_BR); } CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, size_t size, std::align_val_t) NOEXCEPT { - GET_STACK_TRACE_FREE; - asan_sized_free(ptr, size, &stack, FROM_NEW); -} +void operator delete(void *ptr, size_t size, std::align_val_t align) NOEXCEPT +{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW); } CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, size_t size, std::align_val_t) NOEXCEPT { - GET_STACK_TRACE_FREE; - asan_sized_free(ptr, size, &stack, FROM_NEW_BR); -} +void operator delete[](void *ptr, size_t size, std::align_val_t align) NOEXCEPT +{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW_BR); } #else // SANITIZER_MAC -INTERCEPTOR(void, _ZdlPv, void *ptr) { - OPERATOR_DELETE_BODY(FROM_NEW); -} -INTERCEPTOR(void, _ZdaPv, void *ptr) { - OPERATOR_DELETE_BODY(FROM_NEW_BR); -} -INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) { - OPERATOR_DELETE_BODY(FROM_NEW); -} -INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) { - OPERATOR_DELETE_BODY(FROM_NEW_BR); -} -#endif +INTERCEPTOR(void, _ZdlPv, void *ptr) +{ OPERATOR_DELETE_BODY(FROM_NEW); } +INTERCEPTOR(void, _ZdaPv, void *ptr) +{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } +INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FROM_NEW); } +INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } +#endif // !SANITIZER_MAC diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc index abb75ab3bf92..c3a82aa0049e 100644 --- a/lib/asan/asan_poisoning.cc +++ b/lib/asan/asan_poisoning.cc @@ -217,7 +217,7 @@ uptr __asan_region_is_poisoned(uptr beg, uptr size) { uptr __bad = __asan_region_is_poisoned(__p, __size); \ __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0);\ } \ - } while (false); \ + } while (false) extern "C" SANITIZER_INTERFACE_ATTRIBUTE diff --git a/lib/asan/asan_poisoning.h b/lib/asan/asan_poisoning.h index cc3281e08a71..1e00070bcf63 100644 --- a/lib/asan/asan_poisoning.h +++ b/lib/asan/asan_poisoning.h @@ -46,8 +46,11 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, // for mapping shadow and zeroing out pages doesn't "just work", so we should // probably provide higher-level interface for these operations. // For now, just memset on Windows. - if (value || - SANITIZER_WINDOWS == 1 || + if (value || SANITIZER_WINDOWS == 1 || + // TODO(mcgrathr): Fuchsia doesn't allow the shadow mapping to be + // changed at all. It doesn't currently have an efficient means + // to zero a bunch of pages, but maybe we should add one. + SANITIZER_FUCHSIA == 1 || shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); } else { diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc index added746ace8..17c28b0aea2a 100644 --- a/lib/asan/asan_posix.cc +++ b/lib/asan/asan_posix.cc @@ -25,7 +25,6 @@ #include "sanitizer_common/sanitizer_procmaps.h" #include <pthread.h> -#include <signal.h> #include <stdlib.h> #include <sys/time.h> #include <sys/resource.h> @@ -34,58 +33,9 @@ namespace __asan { void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { - ScopedDeadlySignal signal_scope(GetCurrentThread()); - int code = (int)((siginfo_t*)siginfo)->si_code; - // Write the first message using fd=2, just in case. - // It may actually fail to write in case stderr is closed. - internal_write(2, "ASAN:DEADLYSIGNAL\n", 18); - SignalContext sig = SignalContext::Create(siginfo, context); - - // Access at a reasonable offset above SP, or slightly below it (to account - // for x86_64 or PowerPC redzone, ARM push of multiple registers, etc) is - // probably a stack overflow. -#ifdef __s390__ - // On s390, the fault address in siginfo points to start of the page, not - // to the precise word that was accessed. Mask off the low bits of sp to - // take it into account. - bool IsStackAccess = sig.addr >= (sig.sp & ~0xFFF) && - sig.addr < sig.sp + 0xFFFF; -#else - bool IsStackAccess = sig.addr + 512 > sig.sp && sig.addr < sig.sp + 0xFFFF; -#endif - -#if __powerpc__ - // Large stack frames can be allocated with e.g. - // lis r0,-10000 - // stdux r1,r1,r0 # store sp to [sp-10000] and update sp by -10000 - // If the store faults then sp will not have been updated, so test above - // will not work, because the fault address will be more than just "slightly" - // below sp. - if (!IsStackAccess && IsAccessibleMemoryRange(sig.pc, 4)) { - u32 inst = *(unsigned *)sig.pc; - u32 ra = (inst >> 16) & 0x1F; - u32 opcd = inst >> 26; - u32 xo = (inst >> 1) & 0x3FF; - // Check for store-with-update to sp. The instructions we accept are: - // stbu rs,d(ra) stbux rs,ra,rb - // sthu rs,d(ra) sthux rs,ra,rb - // stwu rs,d(ra) stwux rs,ra,rb - // stdu rs,ds(ra) stdux rs,ra,rb - // where ra is r1 (the stack pointer). - if (ra == 1 && - (opcd == 39 || opcd == 45 || opcd == 37 || opcd == 62 || - (opcd == 31 && (xo == 247 || xo == 439 || xo == 183 || xo == 181)))) - IsStackAccess = true; - } -#endif // __powerpc__ - - // We also check si_code to filter out SEGV caused by something else other - // then hitting the guard page or unmapped memory, like, for example, - // unaligned memory access. - if (IsStackAccess && (code == si_SEGV_MAPERR || code == si_SEGV_ACCERR)) - ReportStackOverflow(sig); - else - ReportDeadlySignal(signo, sig); + StartReportDeadlySignal(); + SignalContext sig(siginfo, context); + ReportDeadlySignal(sig); } // ---------------------- TSD ---------------- {{{1 diff --git a/lib/asan/asan_premap_shadow.cc b/lib/asan/asan_premap_shadow.cc new file mode 100644 index 000000000000..229eba99fe0e --- /dev/null +++ b/lib/asan/asan_premap_shadow.cc @@ -0,0 +1,79 @@ +//===-- asan_premap_shadow.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. +// +// Reserve shadow memory with an ifunc resolver. +//===----------------------------------------------------------------------===// + +#include "asan_mapping.h" + +#if ASAN_PREMAP_SHADOW + +#include "asan_premap_shadow.h" +#include "sanitizer_common/sanitizer_posix.h" + +namespace __asan { + +// The code in this file needs to run in an unrelocated binary. It may not +// access any external symbol, including its own non-hidden globals. + +// Conservative upper limit. +uptr PremapShadowSize() { + uptr granularity = GetMmapGranularity(); + return RoundUpTo(GetMaxVirtualAddress() >> SHADOW_SCALE, granularity); +} + +// Returns an address aligned to 8 pages, such that one page on the left and +// PremapShadowSize() bytes on the right of it are mapped r/o. +uptr PremapShadow() { + uptr granularity = GetMmapGranularity(); + uptr alignment = granularity * 8; + uptr left_padding = granularity; + uptr shadow_size = PremapShadowSize(); + uptr map_size = shadow_size + left_padding + alignment; + + uptr map_start = (uptr)MmapNoAccess(map_size); + CHECK_NE(map_start, ~(uptr)0); + + uptr shadow_start = RoundUpTo(map_start + left_padding, alignment); + uptr shadow_end = shadow_start + shadow_size; + internal_munmap(reinterpret_cast<void *>(map_start), + shadow_start - left_padding - map_start); + internal_munmap(reinterpret_cast<void *>(shadow_end), + map_start + map_size - shadow_end); + return shadow_start; +} + +bool PremapShadowFailed() { + uptr shadow = reinterpret_cast<uptr>(&__asan_shadow); + uptr resolver = reinterpret_cast<uptr>(&__asan_premap_shadow); + // shadow == resolver is how Android KitKat and older handles ifunc. + // shadow == 0 just in case. + if (shadow == 0 || shadow == resolver) + return true; + return false; +} +} // namespace __asan + +extern "C" { +decltype(__asan_shadow)* __asan_premap_shadow() { + // The resolver may be called multiple times. Map the shadow just once. + static uptr premapped_shadow = 0; + if (!premapped_shadow) premapped_shadow = __asan::PremapShadow(); + return reinterpret_cast<decltype(__asan_shadow)*>(premapped_shadow); +} + +// __asan_shadow is a "function" that has the same address as the first byte of +// the shadow mapping. +INTERFACE_ATTRIBUTE __attribute__((ifunc("__asan_premap_shadow"))) void +__asan_shadow(); +} + +#endif // ASAN_PREMAP_SHADOW diff --git a/lib/asan/asan_premap_shadow.h b/lib/asan/asan_premap_shadow.h new file mode 100644 index 000000000000..41acbdbbb69f --- /dev/null +++ b/lib/asan/asan_premap_shadow.h @@ -0,0 +1,30 @@ +//===-- asan_mapping.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Premap shadow range with an ifunc resolver. +//===----------------------------------------------------------------------===// + + +#ifndef ASAN_PREMAP_SHADOW_H +#define ASAN_PREMAP_SHADOW_H + +#if ASAN_PREMAP_SHADOW +namespace __asan { +// Conservative upper limit. +uptr PremapShadowSize(); +bool PremapShadowFailed(); +} +#endif + +extern "C" INTERFACE_ATTRIBUTE void __asan_shadow(); +extern "C" decltype(__asan_shadow)* __asan_premap_shadow(); + +#endif // ASAN_PREMAP_SHADOW_H diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc index 2e477f258b8d..e3bc02994efd 100644 --- a/lib/asan/asan_report.cc +++ b/lib/asan/asan_report.cc @@ -60,9 +60,8 @@ void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte, bool in_shadow, const char *after) { Decorator d; str->append("%s%s%x%x%s%s", before, - in_shadow ? d.ShadowByte(byte) : d.MemoryByte(), - byte >> 4, byte & 15, - in_shadow ? d.EndShadowByte() : d.EndMemoryByte(), after); + in_shadow ? d.ShadowByte(byte) : d.MemoryByte(), byte >> 4, + byte & 15, d.Default(), after); } static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, @@ -123,53 +122,15 @@ bool ParseFrameDescription(const char *frame_descr, // immediately after printing error report. class ScopedInErrorReport { public: - explicit ScopedInErrorReport(bool fatal = false) { - halt_on_error_ = fatal || flags()->halt_on_error; - - if (lock_.TryLock()) { - StartReporting(); - return; - } - - // ASan found two bugs in different threads simultaneously. - - u32 current_tid = GetCurrentTidOrInvalid(); - if (reporting_thread_tid_ == current_tid || - reporting_thread_tid_ == kInvalidTid) { - // This is either asynch signal or nested error during error reporting. - // Fail simple to avoid deadlocks in Report(). - - // Can't use Report() here because of potential deadlocks - // in nested signal handlers. - const char msg[] = "AddressSanitizer: nested bug in the same thread, " - "aborting.\n"; - WriteToFile(kStderrFd, msg, sizeof(msg)); - - internal__exit(common_flags()->exitcode); - } - - if (halt_on_error_) { - // Do not print more than one report, otherwise they will mix up. - // Error reporting functions shouldn't return at this situation, as - // they are effectively no-returns. - - Report("AddressSanitizer: while reporting a bug found another one. " - "Ignoring.\n"); - - // Sleep long enough to make sure that the thread which started - // to print an error report will finish doing it. - SleepForSeconds(Max(100, flags()->sleep_before_dying + 1)); - - // If we're still not dead for some reason, use raw _exit() instead of - // Die() to bypass any additional checks. - internal__exit(common_flags()->exitcode); - } else { - // The other thread will eventually finish reporting - // so it's safe to wait - lock_.Lock(); - } - - StartReporting(); + explicit ScopedInErrorReport(bool fatal = false) + : halt_on_error_(fatal || flags()->halt_on_error) { + // Make sure the registry and sanitizer report mutexes are locked while + // we're printing an error report. + // We can lock them only here to avoid self-deadlock in case of + // recursive reports. + asanThreadRegistry().Lock(); + Printf( + "=================================================================\n"); } ~ScopedInErrorReport() { @@ -217,9 +178,6 @@ class ScopedInErrorReport { if (!halt_on_error_) internal_memset(¤t_error_, 0, sizeof(current_error_)); - CommonSanitizerReportMutex.Unlock(); - reporting_thread_tid_ = kInvalidTid; - lock_.Unlock(); if (halt_on_error_) { Report("ABORTING\n"); Die(); @@ -237,39 +195,18 @@ class ScopedInErrorReport { } private: - void StartReporting() { - // Make sure the registry and sanitizer report mutexes are locked while - // we're printing an error report. - // We can lock them only here to avoid self-deadlock in case of - // recursive reports. - asanThreadRegistry().Lock(); - CommonSanitizerReportMutex.Lock(); - reporting_thread_tid_ = GetCurrentTidOrInvalid(); - Printf("====================================================" - "=============\n"); - } - - static StaticSpinMutex lock_; - static u32 reporting_thread_tid_; + ScopedErrorReportLock error_report_lock_; // Error currently being reported. This enables the destructor to interact // with the debugger and point it to an error description. static ErrorDescription current_error_; bool halt_on_error_; }; -StaticSpinMutex ScopedInErrorReport::lock_; -u32 ScopedInErrorReport::reporting_thread_tid_ = kInvalidTid; ErrorDescription ScopedInErrorReport::current_error_; -void ReportStackOverflow(const SignalContext &sig) { +void ReportDeadlySignal(const SignalContext &sig) { ScopedInErrorReport in_report(/*fatal*/ true); - ErrorStackOverflow error(GetCurrentTidOrInvalid(), sig); - in_report.ReportError(error); -} - -void ReportDeadlySignal(int signo, const SignalContext &sig) { - ScopedInErrorReport in_report(/*fatal*/ true); - ErrorDeadlySignal error(GetCurrentTidOrInvalid(), sig, signo); + ErrorDeadlySignal error(GetCurrentTidOrInvalid(), sig); in_report.ReportError(error); } @@ -279,11 +216,12 @@ void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) { in_report.ReportError(error); } -void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size, +void ReportNewDeleteTypeMismatch(uptr addr, uptr delete_size, + uptr delete_alignment, BufferedStackTrace *free_stack) { ScopedInErrorReport in_report; - ErrorNewDeleteSizeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr, - delete_size); + ErrorNewDeleteTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr, + delete_size, delete_alignment); in_report.ReportError(error); } @@ -360,17 +298,58 @@ static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, in_report.ReportError(error); } +static bool IsInvalidPointerPair(uptr a1, uptr a2) { + if (a1 == a2) + return false; + + // 256B in shadow memory can be iterated quite fast + static const uptr kMaxOffset = 2048; + + uptr left = a1 < a2 ? a1 : a2; + uptr right = a1 < a2 ? a2 : a1; + uptr offset = right - left; + if (offset <= kMaxOffset) + return __asan_region_is_poisoned(left, offset); + + AsanThread *t = GetCurrentThread(); + + // check whether left is a stack memory pointer + if (uptr shadow_offset1 = t->GetStackVariableShadowStart(left)) { + uptr shadow_offset2 = t->GetStackVariableShadowStart(right); + return shadow_offset2 == 0 || shadow_offset1 != shadow_offset2; + } + + // check whether left is a heap memory address + HeapAddressDescription hdesc1, hdesc2; + if (GetHeapAddressInformation(left, 0, &hdesc1) && + hdesc1.chunk_access.access_type == kAccessTypeInside) + return !GetHeapAddressInformation(right, 0, &hdesc2) || + hdesc2.chunk_access.access_type != kAccessTypeInside || + hdesc1.chunk_access.chunk_begin != hdesc2.chunk_access.chunk_begin; + + // check whether left is an address of a global variable + GlobalAddressDescription gdesc1, gdesc2; + if (GetGlobalAddressInformation(left, 0, &gdesc1)) + return !GetGlobalAddressInformation(right - 1, 0, &gdesc2) || + !gdesc1.PointsInsideTheSameVariable(gdesc2); + + if (t->GetStackVariableShadowStart(right) || + GetHeapAddressInformation(right, 0, &hdesc2) || + GetGlobalAddressInformation(right - 1, 0, &gdesc2)) + return true; + + // At this point we know nothing about both a1 and a2 addresses. + return false; +} + static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) { if (!flags()->detect_invalid_pointer_pairs) return; uptr a1 = reinterpret_cast<uptr>(p1); uptr a2 = reinterpret_cast<uptr>(p2); - AsanChunkView chunk1 = FindHeapChunkByAddress(a1); - AsanChunkView chunk2 = FindHeapChunkByAddress(a2); - bool valid1 = chunk1.IsAllocated(); - bool valid2 = chunk2.IsAllocated(); - if (!valid1 || !valid2 || !chunk1.Eq(chunk2)) { + + if (IsInvalidPointerPair(a1, a2)) { GET_CALLER_PC_BP_SP; - return ReportInvalidPointerPair(pc, bp, sp, a1, a2); + ReportInvalidPointerPair(pc, bp, sp, a1, a2); } } // ----------------------- Mac-specific reports ----------------- {{{1 diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h index 5a3533a319af..f2cdad47ee81 100644 --- a/lib/asan/asan_report.h +++ b/lib/asan/asan_report.h @@ -46,9 +46,9 @@ bool ParseFrameDescription(const char *frame_descr, // Different kinds of error reports. void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write, uptr access_size, u32 exp, bool fatal); -void ReportStackOverflow(const SignalContext &sig); -void ReportDeadlySignal(int signo, const SignalContext &sig); -void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size, +void ReportDeadlySignal(const SignalContext &sig); +void ReportNewDeleteTypeMismatch(uptr addr, uptr delete_size, + uptr delete_alignment, BufferedStackTrace *free_stack); void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack); void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack); diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index 5ae3568ae04a..21fd0e240708 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -84,26 +84,6 @@ void ShowStatsAndAbort() { Die(); } -// ---------------------- mmap -------------------- {{{1 -// Reserve memory range [beg, end]. -// We need to use inclusive range because end+1 may not be representable. -void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) { - CHECK_EQ((beg % GetMmapGranularity()), 0); - CHECK_EQ(((end + 1) % GetMmapGranularity()), 0); - uptr size = end - beg + 1; - DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb. - void *res = MmapFixedNoReserve(beg, size, name); - if (res != (void*)beg) { - Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " - "Perhaps you're using ulimit -v\n", size); - Abort(); - } - if (common_flags()->no_huge_pages_for_shadow) - NoHugePagesInRegion(beg, size); - if (common_flags()->use_madv_dontdump) - DontDumpShadowMemory(beg, size); -} - // --------------- LowLevelAllocateCallbac ---------- {{{1 static void OnLowLevelAllocate(uptr ptr, uptr size) { PoisonShadow(ptr, size, kAsanInternalHeapMagic); @@ -327,7 +307,7 @@ static void asan_atexit() { static void InitializeHighMemEnd() { #if !ASAN_FIXED_MAPPING - kHighMemEnd = GetMaxVirtualAddress(); + kHighMemEnd = GetMaxUserVirtualAddress(); // Increase kHighMemEnd to make sure it's properly // aligned together with kHighMemBeg: kHighMemEnd |= SHADOW_GRANULARITY * GetMmapGranularity() - 1; @@ -335,46 +315,7 @@ static void InitializeHighMemEnd() { CHECK_EQ((kHighMemBeg % GetMmapGranularity()), 0); } -static void ProtectGap(uptr addr, uptr size) { - if (!flags()->protect_shadow_gap) { - // The shadow gap is unprotected, so there is a chance that someone - // is actually using this memory. Which means it needs a shadow... - uptr GapShadowBeg = RoundDownTo(MEM_TO_SHADOW(addr), GetPageSizeCached()); - uptr GapShadowEnd = - RoundUpTo(MEM_TO_SHADOW(addr + size), GetPageSizeCached()) - 1; - if (Verbosity()) - Printf("protect_shadow_gap=0:" - " not protecting shadow gap, allocating gap's shadow\n" - "|| `[%p, %p]` || ShadowGap's shadow ||\n", GapShadowBeg, - GapShadowEnd); - ReserveShadowMemoryRange(GapShadowBeg, GapShadowEnd, - "unprotected gap shadow"); - return; - } - void *res = MmapFixedNoAccess(addr, size, "shadow gap"); - if (addr == (uptr)res) - return; - // A few pages at the start of the address space can not be protected. - // But we really want to protect as much as possible, to prevent this memory - // being returned as a result of a non-FIXED mmap(). - if (addr == kZeroBaseShadowStart) { - uptr step = GetMmapGranularity(); - while (size > step && addr < kZeroBaseMaxShadowStart) { - addr += step; - size -= step; - void *res = MmapFixedNoAccess(addr, size, "shadow gap"); - if (addr == (uptr)res) - return; - } - } - - Report("ERROR: Failed to protect the shadow gap. " - "ASan cannot proceed correctly. ABORTING.\n"); - DumpProcessMap(); - Die(); -} - -static void PrintAddressSpaceLayout() { +void PrintAddressSpaceLayout() { Printf("|| `[%p, %p]` || HighMem ||\n", (void*)kHighMemBeg, (void*)kHighMemEnd); Printf("|| `[%p, %p]` || HighShadow ||\n", @@ -426,71 +367,6 @@ static void PrintAddressSpaceLayout() { kHighShadowBeg > kMidMemEnd); } -static void InitializeShadowMemory() { - // Set the shadow memory address to uninitialized. - __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel; - - uptr shadow_start = kLowShadowBeg; - // Detect if a dynamic shadow address must used and find a available location - // when necessary. When dynamic address is used, the macro |kLowShadowBeg| - // expands to |__asan_shadow_memory_dynamic_address| which is - // |kDefaultShadowSentinel|. - if (shadow_start == kDefaultShadowSentinel) { - __asan_shadow_memory_dynamic_address = 0; - CHECK_EQ(0, kLowShadowBeg); - shadow_start = FindDynamicShadowStart(); - } - // Update the shadow memory address (potentially) used by instrumentation. - __asan_shadow_memory_dynamic_address = shadow_start; - - if (kLowShadowBeg) - shadow_start -= GetMmapGranularity(); - bool full_shadow_is_available = - MemoryRangeIsAvailable(shadow_start, kHighShadowEnd); - -#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \ - !ASAN_FIXED_MAPPING - if (!full_shadow_is_available) { - kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0; - kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0; - } -#endif - - if (Verbosity()) PrintAddressSpaceLayout(); - - if (full_shadow_is_available) { - // mmap the low shadow plus at least one page at the left. - if (kLowShadowBeg) - ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow"); - // mmap the high shadow. - ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow"); - // protect the gap. - ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); - CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1); - } else if (kMidMemBeg && - MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) && - MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) { - CHECK(kLowShadowBeg != kLowShadowEnd); - // mmap the low shadow plus at least one page at the left. - ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow"); - // mmap the mid shadow. - ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow"); - // mmap the high shadow. - ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow"); - // protect the gaps. - ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); - ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1); - ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1); - } else { - Report("Shadow memory range interleaves with an existing memory mapping. " - "ASan cannot proceed correctly. ABORTING.\n"); - Report("ASan shadow was supposed to be located in the [%p-%p] range.\n", - shadow_start, kHighShadowEnd); - DumpProcessMap(); - Die(); - } -} - static void AsanInitInternal() { if (LIKELY(asan_inited)) return; SanitizerToolName = "AddressSanitizer"; @@ -531,6 +407,7 @@ static void AsanInitInternal() { MaybeReexec(); // Setup internal allocator callback. + SetLowLevelAllocateMinAlignment(SHADOW_GRANULARITY); SetLowLevelAllocateCallback(OnLowLevelAllocate); InitializeAsanInterceptors(); @@ -575,20 +452,18 @@ static void AsanInitInternal() { InitTlsSize(); // Create main thread. - AsanThread *main_thread = AsanThread::Create( - /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ 0, - /* stack */ nullptr, /* detached */ true); + AsanThread *main_thread = CreateMainThread(); CHECK_EQ(0, main_thread->tid()); - SetCurrentThread(main_thread); - main_thread->ThreadStart(internal_getpid(), - /* signal_thread_is_registered */ nullptr); force_interface_symbols(); // no-op. SanitizerInitializeUnwinder(); if (CAN_SANITIZE_LEAKS) { __lsan::InitCommonLsan(); if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { - Atexit(__lsan::DoLeakCheck); + if (flags()->halt_on_error) + Atexit(__lsan::DoLeakCheck); + else + Atexit(__lsan::DoRecoverableLeakCheckVoid); } } @@ -608,6 +483,11 @@ static void AsanInitInternal() { } VReport(1, "AddressSanitizer Init done\n"); + + if (flags()->sleep_after_init) { + Report("Sleeping for %d second(s)\n", flags()->sleep_after_init); + SleepForSeconds(flags()->sleep_after_init); + } } // Initialize as requested from some part of ASan runtime library (interceptors, @@ -647,6 +527,7 @@ void NOINLINE __asan_handle_no_return() { top = curr_thread->stack_top(); bottom = ((uptr)&local_stack - PageSize) & ~(PageSize - 1); } else { + CHECK(!SANITIZER_FUCHSIA); // If we haven't seen this thread, try asking the OS for stack bounds. uptr tls_addr, tls_size, stack_size; GetThreadStackAndTls(/*main=*/false, &bottom, &stack_size, &tls_addr, diff --git a/lib/asan/asan_scariness_score.h b/lib/asan/asan_scariness_score.h index 7f1571416fd5..7f095dd29f29 100644 --- a/lib/asan/asan_scariness_score.h +++ b/lib/asan/asan_scariness_score.h @@ -47,7 +47,7 @@ struct ScarinessScoreBase { }; int GetScore() const { return score; } const char *GetDescription() const { return descr; } - void Print() { + void Print() const { if (score && flags()->print_scariness) Printf("SCARINESS: %d (%s)\n", score, descr); } diff --git a/lib/asan/asan_shadow_setup.cc b/lib/asan/asan_shadow_setup.cc new file mode 100644 index 000000000000..b3cf0b5c1ce2 --- /dev/null +++ b/lib/asan/asan_shadow_setup.cc @@ -0,0 +1,165 @@ +//===-- asan_shadow_setup.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. +// +// Set up the shadow memory. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" + +// asan_fuchsia.cc has its own InitializeShadowMemory implementation. +#if !SANITIZER_FUCHSIA + +#include "asan_internal.h" +#include "asan_mapping.h" + +namespace __asan { + +// ---------------------- mmap -------------------- {{{1 +// Reserve memory range [beg, end]. +// We need to use inclusive range because end+1 may not be representable. +void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) { + CHECK_EQ((beg % GetMmapGranularity()), 0); + CHECK_EQ(((end + 1) % GetMmapGranularity()), 0); + uptr size = end - beg + 1; + DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb. + void *res = MmapFixedNoReserve(beg, size, name); + if (res != (void *)beg) { + Report( + "ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " + "Perhaps you're using ulimit -v\n", + size); + Abort(); + } + if (common_flags()->no_huge_pages_for_shadow) NoHugePagesInRegion(beg, size); + if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(beg, size); +} + +static void ProtectGap(uptr addr, uptr size) { + if (!flags()->protect_shadow_gap) { + // The shadow gap is unprotected, so there is a chance that someone + // is actually using this memory. Which means it needs a shadow... + uptr GapShadowBeg = RoundDownTo(MEM_TO_SHADOW(addr), GetPageSizeCached()); + uptr GapShadowEnd = + RoundUpTo(MEM_TO_SHADOW(addr + size), GetPageSizeCached()) - 1; + if (Verbosity()) + Printf( + "protect_shadow_gap=0:" + " not protecting shadow gap, allocating gap's shadow\n" + "|| `[%p, %p]` || ShadowGap's shadow ||\n", + GapShadowBeg, GapShadowEnd); + ReserveShadowMemoryRange(GapShadowBeg, GapShadowEnd, + "unprotected gap shadow"); + return; + } + void *res = MmapFixedNoAccess(addr, size, "shadow gap"); + if (addr == (uptr)res) return; + // A few pages at the start of the address space can not be protected. + // But we really want to protect as much as possible, to prevent this memory + // being returned as a result of a non-FIXED mmap(). + if (addr == kZeroBaseShadowStart) { + uptr step = GetMmapGranularity(); + while (size > step && addr < kZeroBaseMaxShadowStart) { + addr += step; + size -= step; + void *res = MmapFixedNoAccess(addr, size, "shadow gap"); + if (addr == (uptr)res) return; + } + } + + Report( + "ERROR: Failed to protect the shadow gap. " + "ASan cannot proceed correctly. ABORTING.\n"); + DumpProcessMap(); + Die(); +} + +static void MaybeReportLinuxPIEBug() { +#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__aarch64__)) + Report("This might be related to ELF_ET_DYN_BASE change in Linux 4.12.\n"); + Report( + "See https://github.com/google/sanitizers/issues/856 for possible " + "workarounds.\n"); +#endif +} + +void InitializeShadowMemory() { + // Set the shadow memory address to uninitialized. + __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel; + + uptr shadow_start = kLowShadowBeg; + // Detect if a dynamic shadow address must used and find a available location + // when necessary. When dynamic address is used, the macro |kLowShadowBeg| + // expands to |__asan_shadow_memory_dynamic_address| which is + // |kDefaultShadowSentinel|. + bool full_shadow_is_available = false; + if (shadow_start == kDefaultShadowSentinel) { + __asan_shadow_memory_dynamic_address = 0; + CHECK_EQ(0, kLowShadowBeg); + shadow_start = FindDynamicShadowStart(); + if (SANITIZER_LINUX) full_shadow_is_available = true; + } + // Update the shadow memory address (potentially) used by instrumentation. + __asan_shadow_memory_dynamic_address = shadow_start; + + if (kLowShadowBeg) shadow_start -= GetMmapGranularity(); + + if (!full_shadow_is_available) + full_shadow_is_available = + MemoryRangeIsAvailable(shadow_start, kHighShadowEnd); + +#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \ + !ASAN_FIXED_MAPPING + if (!full_shadow_is_available) { + kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0; + kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0; + } +#endif + + if (Verbosity()) PrintAddressSpaceLayout(); + + if (full_shadow_is_available) { + // mmap the low shadow plus at least one page at the left. + if (kLowShadowBeg) + ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow"); + // mmap the high shadow. + ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow"); + // protect the gap. + ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); + CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1); + } else if (kMidMemBeg && + MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) && + MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) { + CHECK(kLowShadowBeg != kLowShadowEnd); + // mmap the low shadow plus at least one page at the left. + ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow"); + // mmap the mid shadow. + ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow"); + // mmap the high shadow. + ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow"); + // protect the gaps. + ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); + ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1); + ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1); + } else { + Report( + "Shadow memory range interleaves with an existing memory mapping. " + "ASan cannot proceed correctly. ABORTING.\n"); + Report("ASan shadow was supposed to be located in the [%p-%p] range.\n", + shadow_start, kHighShadowEnd); + MaybeReportLinuxPIEBug(); + DumpProcessMap(); + Die(); + } +} + +} // namespace __asan + +#endif // !SANITIZER_FUCHSIA diff --git a/lib/asan/asan_stack.h b/lib/asan/asan_stack.h index cc95e0f30a33..8e9df888d798 100644 --- a/lib/asan/asan_stack.h +++ b/lib/asan/asan_stack.h @@ -31,9 +31,8 @@ u32 GetMallocContextSize(); // 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. ALWAYS_INLINE -void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth, - uptr pc, uptr bp, void *context, - bool fast) { +void GetStackTrace(BufferedStackTrace *stack, uptr max_depth, uptr pc, uptr bp, + void *context, bool fast) { #if SANITIZER_WINDOWS stack->Unwind(max_depth, pc, bp, context, 0, 0, fast); #else @@ -41,10 +40,6 @@ void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth, stack->size = 0; if (LIKELY(asan_inited)) { if ((t = GetCurrentThread()) && !t->isUnwinding()) { - // On FreeBSD the slow unwinding that leverages _Unwind_Backtrace() - // yields the call stack of the signal's handler and not of the code - // that raised the signal (as it does on Linux). - if (SANITIZER_FREEBSD && t->isInDeadlySignal()) fast = true; uptr stack_top = t->stack_top(); uptr stack_bottom = t->stack_bottom(); ScopedUnwinding unwind_scope(t); @@ -66,32 +61,29 @@ void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth, // as early as possible (in functions exposed to the user), as we generally // don't want stack trace to contain functions from ASan internals. -#define GET_STACK_TRACE(max_size, fast) \ - BufferedStackTrace stack; \ - if (max_size <= 2) { \ - stack.size = max_size; \ - if (max_size > 0) { \ - stack.top_frame_bp = GET_CURRENT_FRAME(); \ - stack.trace_buffer[0] = StackTrace::GetCurrentPc(); \ - if (max_size > 1) \ - stack.trace_buffer[1] = GET_CALLER_PC(); \ - } \ - } else { \ - GetStackTraceWithPcBpAndContext(&stack, max_size, \ - StackTrace::GetCurrentPc(), \ - GET_CURRENT_FRAME(), 0, fast); \ +#define GET_STACK_TRACE(max_size, fast) \ + BufferedStackTrace stack; \ + if (max_size <= 2) { \ + stack.size = max_size; \ + if (max_size > 0) { \ + stack.top_frame_bp = GET_CURRENT_FRAME(); \ + stack.trace_buffer[0] = StackTrace::GetCurrentPc(); \ + if (max_size > 1) stack.trace_buffer[1] = GET_CALLER_PC(); \ + } \ + } else { \ + GetStackTrace(&stack, max_size, StackTrace::GetCurrentPc(), \ + GET_CURRENT_FRAME(), 0, fast); \ } -#define GET_STACK_TRACE_FATAL(pc, bp) \ - BufferedStackTrace stack; \ - GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, 0, \ - common_flags()->fast_unwind_on_fatal) +#define GET_STACK_TRACE_FATAL(pc, bp) \ + BufferedStackTrace stack; \ + GetStackTrace(&stack, kStackTraceMax, pc, bp, 0, \ + common_flags()->fast_unwind_on_fatal) -#define GET_STACK_TRACE_SIGNAL(sig) \ - BufferedStackTrace stack; \ - GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, \ - (sig).pc, (sig).bp, (sig).context, \ - common_flags()->fast_unwind_on_fatal) +#define GET_STACK_TRACE_SIGNAL(sig) \ + BufferedStackTrace stack; \ + GetStackTrace(&stack, kStackTraceMax, (sig).pc, (sig).bp, (sig).context, \ + common_flags()->fast_unwind_on_fatal) #define GET_STACK_TRACE_FATAL_HERE \ GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal) diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc index b1a0d9a3b37f..ad81512dff08 100644 --- a/lib/asan/asan_thread.cc +++ b/lib/asan/asan_thread.cc @@ -27,11 +27,6 @@ namespace __asan { // AsanThreadContext implementation. -struct CreateThreadContextArgs { - AsanThread *thread; - StackTrace *stack; -}; - void AsanThreadContext::OnCreated(void *arg) { CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg); if (args->stack) @@ -88,7 +83,7 @@ AsanThread *AsanThread::Create(thread_callback_t start_routine, void *arg, AsanThread *thread = (AsanThread*)MmapOrDie(size, __func__); thread->start_routine_ = start_routine; thread->arg_ = arg; - CreateThreadContextArgs args = { thread, stack }; + AsanThreadContext::CreateThreadContextArgs args = {thread, stack}; asanThreadRegistry().CreateThread(*reinterpret_cast<uptr *>(thread), detached, parent_tid, &args); @@ -223,12 +218,12 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() { return nullptr; } -void AsanThread::Init() { +void AsanThread::Init(const InitOptions *options) { next_stack_top_ = next_stack_bottom_ = 0; atomic_store(&stack_switching_, false, memory_order_release); fake_stack_ = nullptr; // Will be initialized lazily if needed. CHECK_EQ(this->stack_size(), 0U); - SetThreadStackAndTls(); + SetThreadStackAndTls(options); CHECK_GT(this->stack_size(), 0U); CHECK(AddrIsInMem(stack_bottom_)); CHECK(AddrIsInMem(stack_top_ - 1)); @@ -239,6 +234,10 @@ void AsanThread::Init() { &local); } +// Fuchsia doesn't use ThreadStart. +// asan_fuchsia.c defines CreateMainThread and SetThreadStackAndTls. +#if !SANITIZER_FUCHSIA + thread_return_t AsanThread::ThreadStart( tid_t os_id, atomic_uintptr_t *signal_thread_is_registered) { Init(); @@ -270,7 +269,21 @@ thread_return_t AsanThread::ThreadStart( return res; } -void AsanThread::SetThreadStackAndTls() { +AsanThread *CreateMainThread() { + AsanThread *main_thread = AsanThread::Create( + /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ 0, + /* stack */ nullptr, /* detached */ true); + SetCurrentThread(main_thread); + main_thread->ThreadStart(internal_getpid(), + /* signal_thread_is_registered */ nullptr); + return main_thread; +} + +// This implementation doesn't use the argument, which is just passed down +// from the caller of Init (which see, above). It's only there to support +// OS-specific implementations that need more information passed through. +void AsanThread::SetThreadStackAndTls(const InitOptions *options) { + DCHECK_EQ(options, nullptr); uptr tls_size = 0; uptr stack_size = 0; GetThreadStackAndTls(tid() == 0, const_cast<uptr *>(&stack_bottom_), @@ -283,6 +296,8 @@ void AsanThread::SetThreadStackAndTls() { CHECK(AddrIsInStack((uptr)&local)); } +#endif // !SANITIZER_FUCHSIA + void AsanThread::ClearShadowForThreadStackAndTLS() { PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); if (tls_begin_ != tls_end_) @@ -302,7 +317,7 @@ bool AsanThread::GetStackFrameAccessByAddr(uptr addr, access->frame_descr = (const char *)((uptr*)bottom)[1]; return true; } - uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr. + uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr. uptr mem_ptr = RoundDownTo(aligned_addr, SHADOW_GRANULARITY); u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); u8 *shadow_bottom = (u8*)MemToShadow(bottom); @@ -331,6 +346,29 @@ bool AsanThread::GetStackFrameAccessByAddr(uptr addr, return true; } +uptr AsanThread::GetStackVariableShadowStart(uptr addr) { + uptr bottom = 0; + if (AddrIsInStack(addr)) { + bottom = stack_bottom(); + } else if (has_fake_stack()) { + bottom = fake_stack()->AddrIsInFakeStack(addr); + CHECK(bottom); + } else + return 0; + + uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr. + u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); + u8 *shadow_bottom = (u8*)MemToShadow(bottom); + + while (shadow_ptr >= shadow_bottom && + (*shadow_ptr != kAsanStackLeftRedzoneMagic && + *shadow_ptr != kAsanStackMidRedzoneMagic && + *shadow_ptr != kAsanStackRightRedzoneMagic)) + shadow_ptr--; + + return (uptr)shadow_ptr + 1; +} + bool AsanThread::AddrIsInStack(uptr addr) { const auto bounds = GetStackBounds(); return addr >= bounds.bottom && addr < bounds.top; diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h index 424f9e68dfea..66091921101f 100644 --- a/lib/asan/asan_thread.h +++ b/lib/asan/asan_thread.h @@ -49,6 +49,11 @@ class AsanThreadContext : public ThreadContextBase { void OnCreated(void *arg) override; void OnFinished() override; + + struct CreateThreadContextArgs { + AsanThread *thread; + StackTrace *stack; + }; }; // AsanThreadContext objects are never freed, so we need many of them. @@ -62,7 +67,9 @@ class AsanThread { static void TSDDtor(void *tsd); void Destroy(); - void Init(); // Should be called from the thread itself. + struct InitOptions; + void Init(const InitOptions *options = nullptr); + thread_return_t ThreadStart(tid_t os_id, atomic_uintptr_t *signal_thread_is_registered); @@ -83,6 +90,9 @@ class AsanThread { }; bool GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access); + // Returns a pointer to the start of the stack variable's shadow memory. + uptr GetStackVariableShadowStart(uptr addr); + bool AddrIsInStack(uptr addr); void DeleteFakeStack(int tid) { @@ -118,17 +128,15 @@ class AsanThread { bool isUnwinding() const { return unwinding_; } void setUnwinding(bool b) { unwinding_ = b; } - // True if we are in a deadly signal handler. - bool isInDeadlySignal() const { return in_deadly_signal_; } - void setInDeadlySignal(bool b) { in_deadly_signal_ = b; } - AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; } AsanStats &stats() { return stats_; } private: // NOTE: There is no AsanThread constructor. It is allocated // via mmap() and *must* be valid in zero-initialized state. - void SetThreadStackAndTls(); + + void SetThreadStackAndTls(const InitOptions *options); + void ClearShadowForThreadStackAndTLS(); FakeStack *AsyncSignalSafeLazyInitFakeStack(); @@ -158,7 +166,6 @@ class AsanThread { AsanThreadLocalMallocStorage malloc_storage_; AsanStats stats_; bool unwinding_; - bool in_deadly_signal_; }; // ScopedUnwinding is a scope for stacktracing member of a context @@ -173,20 +180,6 @@ class ScopedUnwinding { AsanThread *thread; }; -// ScopedDeadlySignal is a scope for handling deadly signals. -class ScopedDeadlySignal { - public: - explicit ScopedDeadlySignal(AsanThread *t) : thread(t) { - if (thread) thread->setInDeadlySignal(true); - } - ~ScopedDeadlySignal() { - if (thread) thread->setInDeadlySignal(false); - } - - private: - AsanThread *thread; -}; - // Returns a single instance of registry. ThreadRegistry &asanThreadRegistry(); diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc index 8a839d913f95..68eedd1863bc 100644 --- a/lib/asan/asan_win.cc +++ b/lib/asan/asan_win.cc @@ -57,8 +57,8 @@ long __asan_unhandled_exception_filter(EXCEPTION_POINTERS *info) { // FIXME: Handle EXCEPTION_STACK_OVERFLOW here. - SignalContext sig = SignalContext::Create(exception_record, context); - ReportDeadlySignal(exception_record->ExceptionCode, sig); + SignalContext sig(exception_record, context); + ReportDeadlySignal(sig); UNREACHABLE("returned from reporting deadly signal"); } diff --git a/lib/asan/scripts/asan_device_setup b/lib/asan/scripts/asan_device_setup index 5a4f7c47cc21..92a109727267 100755 --- a/lib/asan/scripts/asan_device_setup +++ b/lib/asan/scripts/asan_device_setup @@ -95,7 +95,7 @@ function get_device_arch { # OUT OUT64 local _ARCH= local _ARCH64= if [[ $_ABI == x86* ]]; then - _ARCH=i686 + _ARCH=i386 elif [[ $_ABI == armeabi* ]]; then _ARCH=arm elif [[ $_ABI == arm64-v8a* ]]; then @@ -181,6 +181,17 @@ if [[ -n $ARCH64 ]]; then ASAN_RT64="libclang_rt.asan-$ARCH64-android.so" fi +RELEASE=$(adb_shell getprop ro.build.version.release) +PRE_L=0 +if echo "$RELEASE" | grep '^4\.' >&/dev/null; then + PRE_L=1 +fi +ANDROID_O=0 +if echo "$RELEASE" | grep '^8\.0\.' >&/dev/null; then + # 8.0.x is for Android O + ANDROID_O=1 +fi + if [[ x$revert == xyes ]]; then echo '>> Uninstalling ASan' @@ -202,6 +213,10 @@ if [[ x$revert == xyes ]]; then adb_shell ln -s /system/bin/app_process32 /system/bin/app_process fi + if [[ ANDROID_O -eq 1 ]]; then + adb_shell mv /system/etc/ld.config.txt.saved /system/etc/ld.config.txt + fi + echo '>> Restarting shell' adb_shell stop adb_shell start @@ -251,12 +266,6 @@ TMPDIROLD="$TMPDIRBASE/old" TMPDIR="$TMPDIRBASE/new" mkdir "$TMPDIROLD" -RELEASE=$(adb_shell getprop ro.build.version.release) -PRE_L=0 -if echo "$RELEASE" | grep '^4\.' >&/dev/null; then - PRE_L=1 -fi - if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then if adb_pull /system/bin/app_process.real /dev/null >&/dev/null; then @@ -410,15 +419,18 @@ if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then install "$TMPDIR/asanwrapper" /system/bin 755 install "$TMPDIR/asanwrapper64" /system/bin 755 - adb_shell ln -sf $ASAN_RT /system/lib/$ASAN_RT_SYMLINK - adb_shell ln -sf $ASAN_RT64 /system/lib64/$ASAN_RT_SYMLINK + adb_shell rm -f /system/lib/$ASAN_RT_SYMLINK + adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK + adb_shell rm -f /system/lib64/$ASAN_RT_SYMLINK + adb_shell ln -s $ASAN_RT64 /system/lib64/$ASAN_RT_SYMLINK else install "$TMPDIR/$ASAN_RT" /system/lib 644 install "$TMPDIR/app_process32" /system/bin 755 $CTX install "$TMPDIR/app_process.wrap" /system/bin 755 $CTX install "$TMPDIR/asanwrapper" /system/bin 755 $CTX - adb_shell ln -sf $ASAN_RT /system/lib/$ASAN_RT_SYMLINK + adb_shell rm -f /system/lib/$ASAN_RT_SYMLINK + adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK adb_shell rm /system/bin/app_process adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process @@ -427,6 +439,11 @@ if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then adb_shell cp /system/bin/sh /system/bin/sh-from-zygote adb_shell chcon $CTX /system/bin/sh-from-zygote + if [[ ANDROID_O -eq 1 ]]; then + # For Android O, the linker namespace is temporarily disabled. + adb_shell mv /system/etc/ld.config.txt /system/etc/ld.config.txt.saved + fi + if [ $ENFORCING == 1 ]; then adb_shell setenforce 1 fi diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py index 1a56e44127c1..cd5d89ba2219 100755 --- a/lib/asan/scripts/asan_symbolize.py +++ b/lib/asan/scripts/asan_symbolize.py @@ -280,7 +280,7 @@ def BreakpadSymbolizerFactory(binary): def SystemSymbolizerFactory(system, addr, binary, arch): if system == 'Darwin': return DarwinSymbolizer(addr, binary, arch) - elif system == 'Linux' or system == 'FreeBSD': + elif system in ['Linux', 'FreeBSD', 'NetBSD']: return Addr2LineSymbolizer(binary) @@ -370,7 +370,7 @@ class SymbolizationLoop(object): self.binary_name_filter = binary_name_filter self.dsym_hint_producer = dsym_hint_producer self.system = os.uname()[0] - if self.system not in ['Linux', 'Darwin', 'FreeBSD']: + if self.system not in ['Linux', 'Darwin', 'FreeBSD', 'NetBSD']: raise Exception('Unknown system') self.llvm_symbolizers = {} self.last_llvm_symbolizer = None diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt index 8089d51efe68..67a8fafaba3c 100644 --- a/lib/asan/tests/CMakeLists.txt +++ b/lib/asan/tests/CMakeLists.txt @@ -23,6 +23,7 @@ set(ASAN_UNITTEST_HEADERS set(ASAN_UNITTEST_COMMON_CFLAGS ${COMPILER_RT_UNITTEST_CFLAGS} ${COMPILER_RT_GTEST_CFLAGS} + ${COMPILER_RT_ASAN_SHADOW_SCALE_LLVM_FLAG} -I${COMPILER_RT_SOURCE_DIR}/include -I${COMPILER_RT_SOURCE_DIR}/lib -I${COMPILER_RT_SOURCE_DIR}/lib/asan @@ -52,6 +53,7 @@ list(APPEND ASAN_UNITTEST_COMMON_LINK_FLAGS -g) # Use -D instead of definitions to please custom compile command. list(APPEND ASAN_UNITTEST_COMMON_CFLAGS + ${COMPILER_RT_ASAN_SHADOW_SCALE_FLAG} -DASAN_HAS_BLACKLIST=1 -DASAN_HAS_EXCEPTIONS=1 -DASAN_UAR=0) @@ -125,57 +127,6 @@ append_list_if(COMPILER_RT_HAS_LIBLOG log ASAN_UNITTEST_NOINST_LIBS) # NDK r10 requires -latomic almost always. append_list_if(ANDROID atomic ASAN_UNITTEST_NOINST_LIBS) -# Compile source for the given architecture, using compiler -# options in ${ARGN}, and add it to the object list. -macro(asan_compile obj_list source arch kind) - get_filename_component(basename ${source} NAME) - if(CMAKE_CONFIGURATION_TYPES) - set(output_obj "${CMAKE_CFG_INTDIR}/${obj_list}.${basename}.${arch}${kind}.o") - else() - set(output_obj "${obj_list}.${basename}.${arch}${kind}.o") - endif() - get_target_flags_for_arch(${arch} TARGET_CFLAGS) - set(COMPILE_DEPS ${ASAN_UNITTEST_HEADERS} ${ASAN_BLACKLIST_FILE}) - if(NOT COMPILER_RT_STANDALONE_BUILD) - list(APPEND COMPILE_DEPS gtest asan) - endif() - clang_compile(${output_obj} ${source} - CFLAGS ${ARGN} ${TARGET_CFLAGS} - DEPS ${COMPILE_DEPS}) - list(APPEND ${obj_list} ${output_obj}) -endmacro() - -# Link ASan unit test for a given architecture from a set -# of objects in with given linker flags. -macro(add_asan_test test_suite test_name arch kind) - cmake_parse_arguments(TEST "WITH_TEST_RUNTIME" "" "OBJECTS;LINK_FLAGS;SUBDIR" ${ARGN}) - get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) - set(TEST_DEPS ${TEST_OBJECTS}) - if(NOT COMPILER_RT_STANDALONE_BUILD) - list(APPEND TEST_DEPS asan) - endif() - if(TEST_WITH_TEST_RUNTIME) - list(APPEND TEST_DEPS ${ASAN_TEST_RUNTIME}) - if(CMAKE_CONFIGURATION_TYPES) - set(configuration_path "${CMAKE_CFG_INTDIR}/") - else() - set(configuration_path "") - endif() - if(NOT MSVC) - set(asan_test_runtime_path ${configuration_path}lib${ASAN_TEST_RUNTIME}.a) - else() - set(asan_test_runtime_path ${configuration_path}${ASAN_TEST_RUNTIME}.lib) - endif() - list(APPEND TEST_OBJECTS ${asan_test_runtime_path}) - endif() - add_compiler_rt_test(${test_suite} ${test_name} - SUBDIR ${TEST_SUBDIR} - OBJECTS ${TEST_OBJECTS} - DEPS ${TEST_DEPS} - LINK_FLAGS ${TEST_LINK_FLAGS} - ${TARGET_LINK_FLAGS}) -endmacro() - # Main AddressSanitizer unit tests. add_custom_target(AsanUnitTests) set_target_properties(AsanUnitTests PROPERTIES FOLDER "Compiler-RT Tests") @@ -206,131 +157,118 @@ set(ASAN_INST_TEST_SOURCES asan_str_test.cc asan_test_main.cc) if(APPLE) - list(APPEND ASAN_INST_TEST_SOURCES asan_mac_test.cc) + list(APPEND ASAN_INST_TEST_SOURCES asan_mac_test.cc asan_mac_test_helpers.mm) endif() set(ASAN_BENCHMARKS_SOURCES ${COMPILER_RT_GTEST_SOURCE} asan_benchmarks_test.cc) -# Adds ASan unit tests and benchmarks for architecture. -macro(add_asan_tests_for_arch_and_kind arch kind) - # Instrumented tests. - set(ASAN_INST_TEST_OBJECTS) - foreach(src ${ASAN_INST_TEST_SOURCES}) - asan_compile(ASAN_INST_TEST_OBJECTS ${src} ${arch} ${kind} - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} ${ARGN}) - endforeach() - if (APPLE) - # Add Mac-specific helper. - asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test_helpers.mm ${arch} ${kind} - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -ObjC ${ARGN}) - endif() +function(add_asan_tests arch test_runtime) + cmake_parse_arguments(TEST "" "KIND" "CFLAGS" ${ARGN}) - if (MSVC) - # With the MSVC CRT, the choice between static and dynamic CRT is made at - # compile time with a macro. Simulate the effect of passing /MD to clang-cl. - set(ASAN_INST_DYNAMIC_TEST_OBJECTS) - foreach(src ${ASAN_INST_TEST_SOURCES}) - asan_compile(ASAN_INST_DYNAMIC_TEST_OBJECTS ${src} ${arch} ${kind} - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -D_MT -D_DLL ${ARGN}) - endforeach() - # Clang links the static CRT by default. Override that to use the dynamic - # CRT. - set(ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINK_FLAGS - ${ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINK_FLAGS} - -Wl,-nodefaultlib:libcmt,-defaultlib:msvcrt,-defaultlib:oldnames) - else() - set(ASAN_INST_DYNAMIC_TEST_OBJECTS ${ASAN_INST_TEST_OBJECTS}) - endif() + # Closure to keep the values. + function(generate_asan_tests test_objects test_suite testname) + generate_compiler_rt_tests(${test_objects} ${test_suite} ${testname} ${arch} + COMPILE_DEPS ${ASAN_UNITTEST_HEADERS} ${ASAN_BLACKLIST_FILE} + DEPS gtest asan + KIND ${TEST_KIND} + ${ARGN} + ) + set("${test_objects}" "${${test_objects}}" PARENT_SCOPE) + endfunction() - # Create the 'default' folder where ASAN tests are produced. - if(CMAKE_CONFIGURATION_TYPES) - foreach(build_mode ${CMAKE_CONFIGURATION_TYPES}) - file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/default/${build_mode}") - endforeach() - else() - file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/default") - endif() + set(ASAN_INST_TEST_OBJECTS) + generate_asan_tests(ASAN_INST_TEST_OBJECTS AsanUnitTests + "Asan-${arch}${TEST_KIND}-Test" + SUBDIR "default" + LINK_FLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINK_FLAGS} + SOURCES ${ASAN_INST_TEST_SOURCES} + CFLAGS ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} ${TEST_CFLAGS}) - add_asan_test(AsanUnitTests "Asan-${arch}${kind}-Test" - ${arch} ${kind} SUBDIR "default" - OBJECTS ${ASAN_INST_TEST_OBJECTS} - LINK_FLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINK_FLAGS}) if(COMPILER_RT_ASAN_HAS_STATIC_RUNTIME) - # Create the 'dynamic' folder where ASAN tests are produced. - if(CMAKE_CONFIGURATION_TYPES) - foreach(build_mode ${CMAKE_CONFIGURATION_TYPES}) - file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dynamic/${build_mode}") - endforeach() + set(dynamic_test_name "Asan-${arch}${TEST_KIND}-Dynamic-Test") + if(MSVC) + + # With the MSVC CRT, the choice between static and dynamic CRT is made at + # compile time with a macro. Simulate the effect of passing /MD to clang-cl. + set(ASAN_DYNAMIC_TEST_OBJECTS) + generate_asan_tests(ASAN_DYNAMIC_TEST_OBJECTS + AsanDynamicUnitTests "${dynamic_test_name}" + SUBDIR "dynamic" + CFLAGS ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -D_MT -D_DLL + SOURCES ${ASAN_INST_TEST_SOURCES} + LINK_FLAGS ${ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINK_FLAGS} + -Wl,-nodefaultlib:libcmt,-defaultlib:msvcrt,-defaultlib:oldnames + ) else() - file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dynamic") - endif() - add_asan_test(AsanDynamicUnitTests "Asan-${arch}${kind}-Dynamic-Test" - ${arch} ${kind} SUBDIR "dynamic" - OBJECTS ${ASAN_INST_DYNAMIC_TEST_OBJECTS} - LINK_FLAGS ${ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINK_FLAGS}) + # Otherwise, reuse ASAN_INST_TEST_OBJECTS. + add_compiler_rt_test(AsanDynamicUnitTests "${dynamic_test_name}" "${arch}" + SUBDIR "dynamic" + OBJECTS ${ASAN_INST_TEST_OBJECTS} + DEPS asan ${ASAN_INST_TEST_OBJECTS} + LINK_FLAGS ${ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINK_FLAGS} + ) + endif() endif() - # Add static ASan runtime that will be linked with uninstrumented tests. - set(ASAN_TEST_RUNTIME RTAsanTest.${arch}${kind}) - if(APPLE) - set(ASAN_TEST_RUNTIME_OBJECTS - $<TARGET_OBJECTS:RTAsan_dynamic.osx> - $<TARGET_OBJECTS:RTInterception.osx> - $<TARGET_OBJECTS:RTSanitizerCommon.osx> - $<TARGET_OBJECTS:RTSanitizerCommonLibc.osx> - $<TARGET_OBJECTS:RTLSanCommon.osx> - $<TARGET_OBJECTS:RTUbsan.osx>) - else() - set(ASAN_TEST_RUNTIME_OBJECTS - $<TARGET_OBJECTS:RTAsan.${arch}> - $<TARGET_OBJECTS:RTAsan_cxx.${arch}> - $<TARGET_OBJECTS:RTInterception.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> - $<TARGET_OBJECTS:RTLSanCommon.${arch}> - $<TARGET_OBJECTS:RTUbsan.${arch}> - $<TARGET_OBJECTS:RTUbsan_cxx.${arch}>) - endif() - add_library(${ASAN_TEST_RUNTIME} STATIC ${ASAN_TEST_RUNTIME_OBJECTS}) - set_target_properties(${ASAN_TEST_RUNTIME} PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - FOLDER "Compiler-RT Runtime tests") # Uninstrumented tests. set(ASAN_NOINST_TEST_OBJECTS) - foreach(src ${ASAN_NOINST_TEST_SOURCES}) - asan_compile(ASAN_NOINST_TEST_OBJECTS ${src} ${arch} ${kind} - ${ASAN_UNITTEST_COMMON_CFLAGS} ${ARGN}) - endforeach() - add_asan_test(AsanUnitTests "Asan-${arch}${kind}-Noinst-Test" - ${arch} ${kind} SUBDIR "default" - OBJECTS ${ASAN_NOINST_TEST_OBJECTS} - LINK_FLAGS ${ASAN_UNITTEST_NOINST_LINK_FLAGS} - WITH_TEST_RUNTIME) - - # Benchmarks. - set(ASAN_BENCHMARKS_OBJECTS) - foreach(src ${ASAN_BENCHMARKS_SOURCES}) - asan_compile(ASAN_BENCHMARKS_OBJECTS ${src} ${arch} ${kind} - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} ${ARGN}) - endforeach() - add_asan_test(AsanBenchmarks "Asan-${arch}${kind}-Benchmark" - ${arch} ${kind} SUBDIR "default" - OBJECTS ${ASAN_BENCHMARKS_OBJECTS} - LINK_FLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINK_FLAGS}) -endmacro() + generate_asan_tests(ASAN_NOINST_TEST_OBJECTS + AsanUnitTests "Asan-${arch}${TEST_KIND}-Noinst-Test" + SUBDIR "default" + CFLAGS ${ASAN_UNITTEST_COMMON_CFLAGS} + LINK_FLAGS ${ASAN_UNITTEST_NOINST_LINK_FLAGS} + SOURCES ${ASAN_NOINST_TEST_SOURCES} + RUNTIME ${test_runtime}) + + set(ASAN_BENCHMARK_OBJECTS) + generate_asan_tests(ASAN_BENCHMARK_OBJECTS + AsanBenchmarks "Asan-${arch}${TEST_KIND}-Benchmark" + SUBDIR "default" + CFLAGS ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} + SOURCES ${ASAN_BENCHMARKS_SOURCES} + LINK_FLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINK_FLAGS}) +endfunction() if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID) set(ASAN_TEST_ARCH ${ASAN_SUPPORTED_ARCH}) if(APPLE) darwin_filter_host_archs(ASAN_SUPPORTED_ARCH ASAN_TEST_ARCH) endif() + foreach(arch ${ASAN_TEST_ARCH}) - add_asan_tests_for_arch_and_kind(${arch} "-inline") - add_asan_tests_for_arch_and_kind(${arch} "-with-calls" - -mllvm -asan-instrumentation-with-call-threshold=0) + + # Add static ASan runtime that will be linked with uninstrumented tests. + set(ASAN_TEST_RUNTIME RTAsanTest.${arch}) + if(APPLE) + set(ASAN_TEST_RUNTIME_OBJECTS + $<TARGET_OBJECTS:RTAsan_dynamic.osx> + $<TARGET_OBJECTS:RTInterception.osx> + $<TARGET_OBJECTS:RTSanitizerCommon.osx> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.osx> + $<TARGET_OBJECTS:RTLSanCommon.osx> + $<TARGET_OBJECTS:RTUbsan.osx>) + else() + set(ASAN_TEST_RUNTIME_OBJECTS + $<TARGET_OBJECTS:RTAsan.${arch}> + $<TARGET_OBJECTS:RTAsan_cxx.${arch}> + $<TARGET_OBJECTS:RTInterception.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + $<TARGET_OBJECTS:RTLSanCommon.${arch}> + $<TARGET_OBJECTS:RTUbsan.${arch}> + $<TARGET_OBJECTS:RTUbsan_cxx.${arch}>) + endif() + add_library(${ASAN_TEST_RUNTIME} STATIC ${ASAN_TEST_RUNTIME_OBJECTS}) + set_target_properties(${ASAN_TEST_RUNTIME} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + FOLDER "Compiler-RT Runtime tests") + + add_asan_tests(${arch} ${ASAN_TEST_RUNTIME} KIND "-inline") + add_asan_tests(${arch} ${ASAN_TEST_RUNTIME} KIND "-calls" + CFLAGS -mllvm -asan-instrumentation-with-call-threshold=0) endforeach() endif() diff --git a/lib/asan/tests/asan_asm_test.cc b/lib/asan/tests/asan_asm_test.cc index 2bb37946bb4a..91f8aacd6eeb 100644 --- a/lib/asan/tests/asan_asm_test.cc +++ b/lib/asan/tests/asan_asm_test.cc @@ -12,7 +12,8 @@ //===----------------------------------------------------------------------===// #include "asan_test_utils.h" -#if defined(__linux__) +#if defined(__linux__) && \ + (!defined(ASAN_SHADOW_SCALE) || ASAN_SHADOW_SCALE == 3) // Assembly instrumentation is broken on x86 Android (x86 + PIC + shared runtime // library). See https://github.com/google/sanitizers/issues/353 diff --git a/lib/asan/tests/asan_interface_test.cc b/lib/asan/tests/asan_interface_test.cc index 7d3e520d81a4..69c8fe6f4818 100644 --- a/lib/asan/tests/asan_interface_test.cc +++ b/lib/asan/tests/asan_interface_test.cc @@ -153,14 +153,15 @@ TEST(AddressSanitizerInterface, DeathCallbackTest) { __asan_set_death_callback(NULL); } -static const char* kUseAfterPoisonErrorMessage = "use-after-poison"; - #define GOOD_ACCESS(ptr, offset) \ EXPECT_FALSE(__asan_address_is_poisoned(ptr + offset)) #define BAD_ACCESS(ptr, offset) \ EXPECT_TRUE(__asan_address_is_poisoned(ptr + offset)) +#if !defined(ASAN_SHADOW_SCALE) || ASAN_SHADOW_SCALE == 3 +static const char* kUseAfterPoisonErrorMessage = "use-after-poison"; + TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) { char *array = Ident((char*)malloc(120)); // poison array[40..80) @@ -199,6 +200,7 @@ TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) { BAD_ACCESS(array, 96); free(array); } +#endif // !defined(ASAN_SHADOW_SCALE) || ASAN_SHADOW_SCALE == 3 TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { // Vector of capacity 20 @@ -219,6 +221,7 @@ TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { free(vec); } +#if !defined(ASAN_SHADOW_SCALE) || ASAN_SHADOW_SCALE == 3 // Make sure that each aligned block of size "2^granularity" doesn't have // "true" value before "false" value. static void MakeShadowValid(bool *shadow, int length, int granularity) { @@ -272,6 +275,7 @@ TEST(AddressSanitizerInterface, PoisoningStressTest) { } free(arr); } +#endif // !defined(ASAN_SHADOW_SCALE) || ASAN_SHADOW_SCALE == 3 TEST(AddressSanitizerInterface, GlobalRedzones) { GOOD_ACCESS(glob1, 1 - 1); @@ -386,23 +390,6 @@ TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) { free(array); } -#if !defined(_WIN32) // FIXME: This should really be a lit test. -static void ErrorReportCallbackOneToZ(const char *report) { - int report_len = strlen(report); - ASSERT_EQ(6, write(2, "ABCDEF", 6)); - ASSERT_EQ(report_len, write(2, report, report_len)); - ASSERT_EQ(6, write(2, "ABCDEF", 6)); - _exit(1); -} - -TEST(AddressSanitizerInterface, SetErrorReportCallbackTest) { - __asan_set_error_report_callback(ErrorReportCallbackOneToZ); - EXPECT_DEATH(__asan_report_error((void *)GET_CALLER_PC(), 0, 0, 0, true, 1), - ASAN_PCRE_DOTALL "ABCDEF.*AddressSanitizer.*WRITE.*ABCDEF"); - __asan_set_error_report_callback(NULL); -} -#endif - TEST(AddressSanitizerInterface, GetOwnershipStressTest) { std::vector<char *> pointers; std::vector<size_t> sizes; @@ -423,3 +410,11 @@ TEST(AddressSanitizerInterface, GetOwnershipStressTest) { free(pointers[i]); } +TEST(AddressSanitizerInterface, HandleNoReturnTest) { + char array[40]; + __asan_poison_memory_region(array, sizeof(array)); + BAD_ACCESS(array, 20); + __asan_handle_no_return(); + // It unpoisons the whole thread stack. + GOOD_ACCESS(array, 20); +} diff --git a/lib/asan/tests/asan_str_test.cc b/lib/asan/tests/asan_str_test.cc index 964f6da0297d..5cf4e05c81c8 100644 --- a/lib/asan/tests/asan_str_test.cc +++ b/lib/asan/tests/asan_str_test.cc @@ -95,6 +95,9 @@ TEST(AddressSanitizer, StrLenOOBTest) { free(heap_string); } +// 32-bit android libc++-based NDK toolchain links wcslen statically, disabling +// the interceptor. +#if !defined(__ANDROID__) || defined(__LP64__) TEST(AddressSanitizer, WcsLenTest) { EXPECT_EQ(0U, wcslen(Ident(L""))); size_t hello_len = 13; @@ -106,6 +109,7 @@ TEST(AddressSanitizer, WcsLenTest) { EXPECT_DEATH(Ident(wcslen(heap_string + 14)), RightOOBReadMessage(0)); free(heap_string); } +#endif #if SANITIZER_TEST_HAS_STRNLEN TEST(AddressSanitizer, StrNLenOOBTest) { @@ -629,5 +633,3 @@ TEST(AddressSanitizer, StrtolOOBTest) { RunStrtolOOBTest(&CallStrtol); } #endif - - diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc index 7e9cf3babc67..ed000327f126 100644 --- a/lib/asan/tests/asan_test.cc +++ b/lib/asan/tests/asan_test.cc @@ -13,6 +13,17 @@ #include "asan_test_utils.h" #include <errno.h> +#include <stdarg.h> + +#ifdef _LIBCPP_GET_C_LOCALE +#define SANITIZER_GET_C_LOCALE _LIBCPP_GET_C_LOCALE +#else +#if defined(__FreeBSD__) +#define SANITIZER_GET_C_LOCALE 0 +#elif defined(__NetBSD__) +#define SANITIZER_GET_C_LOCALE LC_C_LOCALE +#endif +#endif NOINLINE void *malloc_fff(size_t size) { void *res = malloc/**/(size); break_optimization(0); return res;} @@ -1328,19 +1339,18 @@ static int vsnprintf_l_wrapper(char *s, size_t n, TEST(AddressSanitizer, snprintf_l) { char buff[5]; // Check that snprintf_l() works fine with Asan. - int res = snprintf_l(buff, 5, - _LIBCPP_GET_C_LOCALE, "%s", "snprintf_l()"); + int res = snprintf_l(buff, 5, SANITIZER_GET_C_LOCALE, "%s", "snprintf_l()"); EXPECT_EQ(12, res); // Check that vsnprintf_l() works fine with Asan. - res = vsnprintf_l_wrapper(buff, 5, - _LIBCPP_GET_C_LOCALE, "%s", "vsnprintf_l()"); + res = vsnprintf_l_wrapper(buff, 5, SANITIZER_GET_C_LOCALE, "%s", + "vsnprintf_l()"); EXPECT_EQ(13, res); - EXPECT_DEATH(snprintf_l(buff, 10, - _LIBCPP_GET_C_LOCALE, "%s", "snprintf_l()"), - "AddressSanitizer: stack-buffer-overflow"); - EXPECT_DEATH(vsnprintf_l_wrapper(buff, 10, - _LIBCPP_GET_C_LOCALE, "%s", "vsnprintf_l()"), - "AddressSanitizer: stack-buffer-overflow"); + EXPECT_DEATH( + snprintf_l(buff, 10, SANITIZER_GET_C_LOCALE, "%s", "snprintf_l()"), + "AddressSanitizer: stack-buffer-overflow"); + EXPECT_DEATH(vsnprintf_l_wrapper(buff, 10, SANITIZER_GET_C_LOCALE, "%s", + "vsnprintf_l()"), + "AddressSanitizer: stack-buffer-overflow"); } #endif diff --git a/lib/asan/tests/asan_test_utils.h b/lib/asan/tests/asan_test_utils.h index c292467220d4..d7b6f82e2978 100644 --- a/lib/asan/tests/asan_test_utils.h +++ b/lib/asan/tests/asan_test_utils.h @@ -45,7 +45,7 @@ #include <unistd.h> #endif -#if !defined(__APPLE__) && !defined(__FreeBSD__) +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__) #include <malloc.h> #endif diff --git a/lib/builtins/CMakeLists.txt b/lib/builtins/CMakeLists.txt index f0d3f50714c1..6fa958319121 100644 --- a/lib/builtins/CMakeLists.txt +++ b/lib/builtins/CMakeLists.txt @@ -51,7 +51,6 @@ set(GENERIC_SOURCES cmpti2.c comparedf2.c comparesf2.c - cpu_model.c ctzdi2.c ctzsi2.c ctzti2.c @@ -66,7 +65,6 @@ set(GENERIC_SOURCES divtc3.c divti3.c divtf3.c - divxc3.c extendsfdf2.c extendhfsf2.c ffsdi2.c @@ -84,27 +82,18 @@ set(GENERIC_SOURCES fixunssfdi.c fixunssfsi.c fixunssfti.c - fixunsxfdi.c - fixunsxfsi.c - fixunsxfti.c - fixxfdi.c - fixxfti.c floatdidf.c floatdisf.c - floatdixf.c floatsidf.c floatsisf.c floattidf.c floattisf.c - floattixf.c floatundidf.c floatundisf.c - floatundixf.c floatunsidf.c floatunsisf.c floatuntidf.c floatuntisf.c - floatuntixf.c int_util.c lshrdi3.c lshrti3.c @@ -124,7 +113,6 @@ set(GENERIC_SOURCES mulvdi3.c mulvsi3.c mulvti3.c - mulxc3.c negdf2.c negdi2.c negsf2.c @@ -142,7 +130,6 @@ set(GENERIC_SOURCES powidf2.c powisf2.c powitf2.c - powixf2.c subdf3.c subsf3.c subvdi3.c @@ -226,6 +213,23 @@ if (NOT FUCHSIA) clear_cache.c) endif() +# These sources work on all x86 variants, but only x86 variants. +set(x86_ARCH_SOURCES + cpu_model.c + divxc3.c + fixxfdi.c + fixxfti.c + fixunsxfdi.c + fixunsxfsi.c + fixunsxfti.c + floatdixf.c + floattixf.c + floatundixf.c + floatuntixf.c + mulxc3.c + powixf2.c +) + if (NOT MSVC) set(x86_64_SOURCES x86_64/chkstk.S @@ -235,8 +239,8 @@ if (NOT MSVC) x86_64/floatdixf.c x86_64/floatundidf.S x86_64/floatundisf.S - x86_64/floatundixf.S - ${GENERIC_SOURCES}) + x86_64/floatundixf.S) + filter_builtin_sources(x86_64_SOURCES EXCLUDE x86_64_SOURCES "${x86_64_SOURCES};${GENERIC_SOURCES}") set(x86_64h_SOURCES ${x86_64_SOURCES}) if (WIN32) @@ -262,8 +266,8 @@ if (NOT MSVC) i386/moddi3.S i386/muldi3.S i386/udivdi3.S - i386/umoddi3.S - ${GENERIC_SOURCES}) + i386/umoddi3.S) + filter_builtin_sources(i386_SOURCES EXCLUDE i386_SOURCES "${i386_SOURCES};${GENERIC_SOURCES}") if (WIN32) set(i386_SOURCES @@ -271,9 +275,6 @@ if (NOT MSVC) i386/chkstk.S i386/chkstk2.S) endif() - - set(i686_SOURCES - ${i386_SOURCES}) else () # MSVC # Use C versions of functions when building on MSVC # MSVC's assembler takes Intel syntax, not AT&T syntax. @@ -285,9 +286,13 @@ else () # MSVC ${GENERIC_SOURCES}) set(x86_64h_SOURCES ${x86_64_SOURCES}) set(i386_SOURCES ${GENERIC_SOURCES}) - set(i686_SOURCES ${i386_SOURCES}) endif () # if (NOT MSVC) +set(x86_64h_SOURCES ${x86_64h_SOURCES} ${x86_ARCH_SOURCES}) +set(x86_64_SOURCES ${x86_64_SOURCES} ${x86_ARCH_SOURCES}) +set(i386_SOURCES ${i386_SOURCES} ${x86_ARCH_SOURCES}) +set(i686_SOURCES ${i686_SOURCES} ${x86_ARCH_SOURCES}) + set(arm_SOURCES arm/bswapdi2.S arm/bswapsi2.S @@ -319,8 +324,8 @@ set(arm_SOURCES arm/sync_fetch_and_xor_8.S arm/udivmodsi4.S arm/udivsi3.S - arm/umodsi3.S - ${GENERIC_SOURCES}) + arm/umodsi3.S) +filter_builtin_sources(arm_SOURCES EXCLUDE arm_SOURCES "${arm_SOURCES};${GENERIC_SOURCES}") set(thumb1_SOURCES arm/divsi3.S @@ -424,6 +429,7 @@ if(MINGW) udivsi3.c umoddi3.c emutls.c) + filter_builtin_sources(arm_SOURCES EXCLUDE arm_SOURCES "${arm_SOURCES};${GENERIC_SOURCES}") elseif(NOT WIN32) # TODO the EABI sources should only be added to EABI targets set(arm_SOURCES @@ -458,8 +464,26 @@ set(mips64_SOURCES ${GENERIC_TF_SOURCES} set(mips64el_SOURCES ${GENERIC_TF_SOURCES} ${mips_SOURCES}) -set(wasm32_SOURCES ${GENERIC_SOURCES}) -set(wasm64_SOURCES ${GENERIC_SOURCES}) +set(powerpc64_SOURCES + ppc/divtc3.c + ppc/fixtfdi.c + ppc/fixunstfdi.c + ppc/floatditf.c + ppc/floatunditf.c + ppc/gcc_qadd.c + ppc/gcc_qdiv.c + ppc/gcc_qmul.c + ppc/gcc_qsub.c + ppc/multc3.c + ${GENERIC_SOURCES}) +set(powerpc64le_SOURCES ${powerpc64_SOURCES}) + +set(wasm32_SOURCES + ${GENERIC_TF_SOURCES} + ${GENERIC_SOURCES}) +set(wasm64_SOURCES + ${GENERIC_TF_SOURCES} + ${GENERIC_SOURCES}) add_custom_target(builtins) set_target_properties(builtins PROPERTIES FOLDER "Compiler-RT Misc") @@ -493,8 +517,10 @@ else () # NOTE: some architectures (e.g. i386) have multiple names. Ensure that # we catch them all. set(_arch ${arch}) - if("${arch}" STREQUAL "i686") - set(_arch "i386|i686") + if("${arch}" STREQUAL "armv6m") + set(_arch "arm|armv6m") + elseif("${arch}" MATCHES "^(armhf|armv7|armv7s|armv7k|armv7m|armv7em)$") + set(_arch "arm") endif() # Filter out generic versions of routines that are re-implemented in diff --git a/lib/builtins/adddf3.c b/lib/builtins/adddf3.c index c528e9e21f51..9a3901312e51 100644 --- a/lib/builtins/adddf3.c +++ b/lib/builtins/adddf3.c @@ -20,8 +20,11 @@ COMPILER_RT_ABI double __adddf3(double a, double b){ } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI double __aeabi_dadd(double a, double b) { return __adddf3(a, b); } +#else +AEABI_RTABI double __aeabi_dadd(double a, double b) COMPILER_RT_ALIAS(__adddf3); +#endif #endif - diff --git a/lib/builtins/addsf3.c b/lib/builtins/addsf3.c index fe570687a25e..c5c1a41c3611 100644 --- a/lib/builtins/addsf3.c +++ b/lib/builtins/addsf3.c @@ -20,8 +20,11 @@ COMPILER_RT_ABI float __addsf3(float a, float b) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI float __aeabi_fadd(float a, float b) { return __addsf3(a, b); } +#else +AEABI_RTABI float __aeabi_fadd(float a, float b) COMPILER_RT_ALIAS(__addsf3); +#endif #endif - diff --git a/lib/builtins/arm/aeabi_cdcmp.S b/lib/builtins/arm/aeabi_cdcmp.S index 3e7a8b86b739..87dd03dce94d 100644 --- a/lib/builtins/arm/aeabi_cdcmp.S +++ b/lib/builtins/arm/aeabi_cdcmp.S @@ -30,7 +30,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmpeq) push {r0-r3, lr} bl __aeabi_cdcmpeq_check_nan cmp r0, #1 -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) beq 1f // NaN has been ruled out, so __aeabi_cdcmple can't trap mov r0, sp @@ -46,9 +46,12 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmpeq) pop {r0-r3, lr} // NaN has been ruled out, so __aeabi_cdcmple can't trap + // Use "it ne" + unconditional branch to guarantee a supported relocation if + // __aeabi_cdcmple is in a different section for some builds. + IT(ne) bne __aeabi_cdcmple -#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) +#if defined(USE_THUMB_2) mov ip, #APSR_C msr APSR_nzcvq, ip #else @@ -78,7 +81,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmple) bl __aeabi_dcmplt cmp r0, #1 -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) bne 1f // Z = 0, C = 0 movs r0, #1 diff --git a/lib/builtins/arm/aeabi_cfcmp.S b/lib/builtins/arm/aeabi_cfcmp.S index 1f304ffd964c..c5fee6b6a08e 100644 --- a/lib/builtins/arm/aeabi_cfcmp.S +++ b/lib/builtins/arm/aeabi_cfcmp.S @@ -30,7 +30,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmpeq) push {r0-r3, lr} bl __aeabi_cfcmpeq_check_nan cmp r0, #1 -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) beq 1f // NaN has been ruled out, so __aeabi_cfcmple can't trap mov r0, sp @@ -46,9 +46,12 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmpeq) pop {r0-r3, lr} // NaN has been ruled out, so __aeabi_cfcmple can't trap + // Use "it ne" + unconditional branch to guarantee a supported relocation if + // __aeabi_cfcmple is in a different section for some builds. + IT(ne) bne __aeabi_cfcmple -#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) +#if defined(USE_THUMB_2) mov ip, #APSR_C msr APSR_nzcvq, ip #else @@ -78,7 +81,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmple) bl __aeabi_fcmplt cmp r0, #1 -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) bne 1f // Z = 0, C = 0 movs r0, #1 diff --git a/lib/builtins/arm/aeabi_idivmod.S b/lib/builtins/arm/aeabi_idivmod.S index 0164b15dca18..9c9c80ab5a7b 100644 --- a/lib/builtins/arm/aeabi_idivmod.S +++ b/lib/builtins/arm/aeabi_idivmod.S @@ -20,16 +20,18 @@ #endif .syntax unified + .text + DEFINE_CODE_STATE .p2align 2 DEFINE_COMPILERRT_FUNCTION(__aeabi_idivmod) -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) push {r0, r1, lr} bl SYMBOL_NAME(__divsi3) pop {r1, r2, r3} // now r0 = quot, r1 = num, r2 = denom muls r2, r0, r2 // r2 = quot * denom subs r1, r1, r2 JMP (r3) -#else +#else // defined(USE_THUMB_1) push { lr } sub sp, sp, #4 mov r2, sp @@ -42,7 +44,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_idivmod) ldr r1, [sp] add sp, sp, #4 pop { pc } -#endif // __ARM_ARCH_ISA_THUMB == 1 +#endif // defined(USE_THUMB_1) END_COMPILERRT_FUNCTION(__aeabi_idivmod) NO_EXEC_STACK_DIRECTIVE diff --git a/lib/builtins/arm/aeabi_memcmp.S b/lib/builtins/arm/aeabi_memcmp.S index 33ea54848b26..e86d6113760e 100644 --- a/lib/builtins/arm/aeabi_memcmp.S +++ b/lib/builtins/arm/aeabi_memcmp.S @@ -14,7 +14,13 @@ .syntax unified .p2align 2 DEFINE_COMPILERRT_FUNCTION(__aeabi_memcmp) +#ifdef USE_THUMB_1 + push {r7, lr} + bl memcmp + pop {r7, pc} +#else b memcmp +#endif END_COMPILERRT_FUNCTION(__aeabi_memcmp) DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcmp4, __aeabi_memcmp) diff --git a/lib/builtins/arm/aeabi_memcpy.S b/lib/builtins/arm/aeabi_memcpy.S index eabfa490494c..e83c5fd4dbb3 100644 --- a/lib/builtins/arm/aeabi_memcpy.S +++ b/lib/builtins/arm/aeabi_memcpy.S @@ -14,7 +14,13 @@ .syntax unified .p2align 2 DEFINE_COMPILERRT_FUNCTION(__aeabi_memcpy) +#ifdef USE_THUMB_1 + push {r7, lr} + bl memcpy + pop {r7, pc} +#else b memcpy +#endif END_COMPILERRT_FUNCTION(__aeabi_memcpy) DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcpy4, __aeabi_memcpy) diff --git a/lib/builtins/arm/aeabi_memmove.S b/lib/builtins/arm/aeabi_memmove.S index 1bf08c0d5b75..ee28300e46f2 100644 --- a/lib/builtins/arm/aeabi_memmove.S +++ b/lib/builtins/arm/aeabi_memmove.S @@ -13,7 +13,13 @@ .p2align 2 DEFINE_COMPILERRT_FUNCTION(__aeabi_memmove) +#ifdef USE_THUMB_1 + push {r7, lr} + bl memmove + pop {r7, pc} +#else b memmove +#endif END_COMPILERRT_FUNCTION(__aeabi_memmove) DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memmove4, __aeabi_memmove) diff --git a/lib/builtins/arm/aeabi_memset.S b/lib/builtins/arm/aeabi_memset.S index 633f592279b5..0a678d7627e7 100644 --- a/lib/builtins/arm/aeabi_memset.S +++ b/lib/builtins/arm/aeabi_memset.S @@ -18,16 +18,29 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_memset) mov r3, r1 mov r1, r2 mov r2, r3 +#ifdef USE_THUMB_1 + push {r7, lr} + bl memset + pop {r7, pc} +#else b memset +#endif END_COMPILERRT_FUNCTION(__aeabi_memset) DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memset4, __aeabi_memset) DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memset8, __aeabi_memset) + .p2align 2 DEFINE_COMPILERRT_FUNCTION(__aeabi_memclr) mov r2, r1 movs r1, #0 +#ifdef USE_THUMB_1 + push {r7, lr} + bl memset + pop {r7, pc} +#else b memset +#endif END_COMPILERRT_FUNCTION(__aeabi_memclr) DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memclr4, __aeabi_memclr) diff --git a/lib/builtins/arm/aeabi_uidivmod.S b/lib/builtins/arm/aeabi_uidivmod.S index a627fc740a02..88a4a6d8bc12 100644 --- a/lib/builtins/arm/aeabi_uidivmod.S +++ b/lib/builtins/arm/aeabi_uidivmod.S @@ -21,9 +21,11 @@ #endif .syntax unified + .text + DEFINE_CODE_STATE .p2align 2 DEFINE_COMPILERRT_FUNCTION(__aeabi_uidivmod) -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) cmp r0, r1 bcc LOCAL_LABEL(case_denom_larger) push {r0, r1, lr} @@ -36,7 +38,7 @@ LOCAL_LABEL(case_denom_larger): movs r1, r0 movs r0, #0 JMP (lr) -#else +#else // defined(USE_THUMB_1) push { lr } sub sp, sp, #4 mov r2, sp diff --git a/lib/builtins/arm/bswapdi2.S b/lib/builtins/arm/bswapdi2.S index fb226cea249e..e9db8bac7994 100644 --- a/lib/builtins/arm/bswapdi2.S +++ b/lib/builtins/arm/bswapdi2.S @@ -11,9 +11,7 @@ .syntax unified .text -#if __ARM_ARCH_ISA_THUMB == 2 - .thumb -#endif + DEFINE_CODE_STATE // // extern uint64_t __bswapdi2(uint64_t); @@ -21,11 +19,7 @@ // Reverse all the bytes in a 64-bit integer. // .p2align 2 -#if __ARM_ARCH_ISA_THUMB == 2 -DEFINE_COMPILERRT_THUMB_FUNCTION(__bswapdi2) -#else DEFINE_COMPILERRT_FUNCTION(__bswapdi2) -#endif #if __ARM_ARCH < 6 // before armv6 does not have "rev" instruction // r2 = rev(r0) diff --git a/lib/builtins/arm/bswapsi2.S b/lib/builtins/arm/bswapsi2.S index 553c3c2e39c8..1f6eed5c1bbf 100644 --- a/lib/builtins/arm/bswapsi2.S +++ b/lib/builtins/arm/bswapsi2.S @@ -11,9 +11,7 @@ .syntax unified .text -#if __ARM_ARCH_ISA_THUMB == 2 - .thumb -#endif + DEFINE_CODE_STATE // // extern uint32_t __bswapsi2(uint32_t); @@ -21,11 +19,7 @@ // Reverse all the bytes in a 32-bit integer. // .p2align 2 -#if __ARM_ARCH_ISA_THUMB == 2 -DEFINE_COMPILERRT_THUMB_FUNCTION(__bswapsi2) -#else DEFINE_COMPILERRT_FUNCTION(__bswapsi2) -#endif #if __ARM_ARCH < 6 // before armv6 does not have "rev" instruction eor r1, r0, r0, ror #16 diff --git a/lib/builtins/arm/clzdi2.S b/lib/builtins/arm/clzdi2.S index 6068c176fd15..fc03b385cdfa 100644 --- a/lib/builtins/arm/clzdi2.S +++ b/lib/builtins/arm/clzdi2.S @@ -15,17 +15,10 @@ .syntax unified .text -#if __ARM_ARCH_ISA_THUMB == 2 - .thumb -#endif - + DEFINE_CODE_STATE .p2align 2 -#if __ARM_ARCH_ISA_THUMB == 2 -DEFINE_COMPILERRT_THUMB_FUNCTION(__clzdi2) -#else DEFINE_COMPILERRT_FUNCTION(__clzdi2) -#endif #ifdef __ARM_FEATURE_CLZ #ifdef __ARMEB__ cmp r0, 0 diff --git a/lib/builtins/arm/clzsi2.S b/lib/builtins/arm/clzsi2.S index c2ba3a8cfcda..f2ce59c90119 100644 --- a/lib/builtins/arm/clzsi2.S +++ b/lib/builtins/arm/clzsi2.S @@ -15,16 +15,10 @@ .syntax unified .text -#if __ARM_ARCH_ISA_THUMB == 2 - .thumb -#endif + DEFINE_CODE_STATE .p2align 2 -#if __ARM_ARCH_ISA_THUMB == 2 -DEFINE_COMPILERRT_THUMB_FUNCTION(__clzsi2) -#else DEFINE_COMPILERRT_FUNCTION(__clzsi2) -#endif #ifdef __ARM_FEATURE_CLZ clz r0, r0 JMP(lr) diff --git a/lib/builtins/arm/comparesf2.S b/lib/builtins/arm/comparesf2.S index ef7091bf3c8f..c6c4cc067f07 100644 --- a/lib/builtins/arm/comparesf2.S +++ b/lib/builtins/arm/comparesf2.S @@ -38,10 +38,9 @@ //===----------------------------------------------------------------------===// #include "../assembly.h" -.syntax unified -#if __ARM_ARCH_ISA_THUMB == 2 -.thumb -#endif + .syntax unified + .text + DEFINE_CODE_STATE @ int __eqsf2(float a, float b) @@ -53,7 +52,7 @@ DEFINE_COMPILERRT_FUNCTION(__eqsf2) #endif // Make copies of a and b with the sign bit shifted off the top. These will // be used to detect zeros and NaNs. -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) push {r6, lr} lsls r2, r0, #1 lsls r3, r1, #1 @@ -67,7 +66,7 @@ DEFINE_COMPILERRT_FUNCTION(__eqsf2) // flag if both a and b are zero (of either sign). The shift of r3 doesn't // effect this at all, but it *does* make sure that the C flag is clear for // the subsequent operations. -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) lsrs r6, r3, #1 orrs r6, r2 #else @@ -75,7 +74,7 @@ DEFINE_COMPILERRT_FUNCTION(__eqsf2) #endif // 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. -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) beq 1f movs r6, r0 eors r6, r1 @@ -89,7 +88,7 @@ DEFINE_COMPILERRT_FUNCTION(__eqsf2) // 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. -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) bmi 1f subs r0, r2, r3 1: @@ -108,7 +107,7 @@ DEFINE_COMPILERRT_FUNCTION(__eqsf2) // 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. -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) bhs 1f // Here if a and b have the same sign and absA < absB, the result is thus // b < 0 ? 1 : -1. Same if a and b have the opposite sign (ignoring Nan). @@ -127,7 +126,7 @@ DEFINE_COMPILERRT_FUNCTION(__eqsf2) // 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. -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) bls 1f // Here both have the same sign and absA > absB. movs r0, #1 @@ -145,14 +144,14 @@ DEFINE_COMPILERRT_FUNCTION(__eqsf2) // 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. // For Thumb-1, r0 contains -1 if a < b, 0 if a > b and 0 if a == b. -#if __ARM_ARCH_ISA_THUMB != 1 +#if !defined(USE_THUMB_1) it ne orrne r0, r0, #1 #endif // Finally, we need to deal with NaNs. If either argument is NaN, replace // the value in r0 with 1. -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) LOCAL_LABEL(CHECK_NAN): movs r6, #0xff lsls r6, #24 @@ -189,7 +188,7 @@ DEFINE_COMPILERRT_FUNCTION(__gtsf2) vmov r0, s0 vmov r1, s1 #endif -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) push {r6, lr} lsls r2, r0, #1 lsls r3, r1, #1 @@ -255,6 +254,7 @@ DEFINE_COMPILERRT_FUNCTION_ALIAS(__gesf2, __gtsf2) .p2align 2 DEFINE_COMPILERRT_FUNCTION(__unordsf2) + #if defined(COMPILER_RT_ARMHF_TARGET) vmov r0, s0 vmov r1, s1 @@ -263,7 +263,7 @@ DEFINE_COMPILERRT_FUNCTION(__unordsf2) lsls r2, r0, #1 lsls r3, r1, #1 movs r0, #0 -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) movs r1, #0xff lsls r1, #24 cmp r2, r1 diff --git a/lib/builtins/arm/divmodsi4.S b/lib/builtins/arm/divmodsi4.S index 999c310ec8a3..8a027b741efe 100644 --- a/lib/builtins/arm/divmodsi4.S +++ b/lib/builtins/arm/divmodsi4.S @@ -23,20 +23,14 @@ .syntax unified .text -#if __ARM_ARCH_ISA_THUMB == 2 - .thumb -#endif + DEFINE_CODE_STATE @ int __divmodsi4(int divident, int divisor, int *remainder) @ Calculate the quotient and remainder of the (signed) division. The return @ value is the quotient, the remainder is placed in the variable. .p2align 3 -#if __ARM_ARCH_ISA_THUMB == 2 -DEFINE_COMPILERRT_THUMB_FUNCTION(__divmodsi4) -#else DEFINE_COMPILERRT_FUNCTION(__divmodsi4) -#endif #if __ARM_ARCH_EXT_IDIV__ tst r1, r1 beq LOCAL_LABEL(divzero) diff --git a/lib/builtins/arm/divsi3.S b/lib/builtins/arm/divsi3.S index f066f60ad96d..19757af177eb 100644 --- a/lib/builtins/arm/divsi3.S +++ b/lib/builtins/arm/divsi3.S @@ -20,11 +20,9 @@ #define CLEAR_FRAME_AND_RETURN \ pop {r4, r7, pc} - .syntax unified - .text -#if __ARM_ARCH_ISA_THUMB == 2 - .thumb -#endif + .syntax unified + .text + DEFINE_CODE_STATE .p2align 3 // Ok, APCS and AAPCS agree on 32 bit args, so it's safe to use the same routine. @@ -33,11 +31,7 @@ DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_idiv, __divsi3) @ int __divsi3(int divident, int divisor) @ Calculate and return the quotient of the (signed) division. -#if __ARM_ARCH_ISA_THUMB == 2 -DEFINE_COMPILERRT_THUMB_FUNCTION(__divsi3) -#else DEFINE_COMPILERRT_FUNCTION(__divsi3) -#endif #if __ARM_ARCH_EXT_IDIV__ tst r1,r1 beq LOCAL_LABEL(divzero) @@ -49,14 +43,14 @@ LOCAL_LABEL(divzero): #else ESTABLISH_FRAME // Set aside the sign of the quotient. -# if __ARM_ARCH_ISA_THUMB == 1 +# if defined(USE_THUMB_1) movs r4, r0 eors r4, r1 # else eor r4, r0, r1 # endif // Take absolute value of a and b via abs(x) = (x^(x >> 31)) - (x >> 31). -# if __ARM_ARCH_ISA_THUMB == 1 +# if defined(USE_THUMB_1) asrs r2, r0, #31 asrs r3, r1, #31 eors r0, r2 @@ -72,7 +66,7 @@ ESTABLISH_FRAME // abs(a) / abs(b) bl SYMBOL_NAME(__udivsi3) // Apply sign of quotient to result and return. -# if __ARM_ARCH_ISA_THUMB == 1 +# if defined(USE_THUMB_1) asrs r4, #31 eors r0, r4 subs r0, r0, r4 diff --git a/lib/builtins/arm/modsi3.S b/lib/builtins/arm/modsi3.S index 1d302edc67bd..be263834d7f1 100644 --- a/lib/builtins/arm/modsi3.S +++ b/lib/builtins/arm/modsi3.S @@ -22,19 +22,13 @@ .syntax unified .text -#if __ARM_ARCH_ISA_THUMB == 2 - .thumb -#endif + DEFINE_CODE_STATE @ int __modsi3(int divident, int divisor) @ Calculate and return the remainder of the (signed) division. .p2align 3 -#if __ARM_ARCH_ISA_THUMB == 2 -DEFINE_COMPILERRT_THUMB_FUNCTION(__modsi3) -#else DEFINE_COMPILERRT_FUNCTION(__modsi3) -#endif #if __ARM_ARCH_EXT_IDIV__ tst r1, r1 beq LOCAL_LABEL(divzero) diff --git a/lib/builtins/arm/udivmodsi4.S b/lib/builtins/arm/udivmodsi4.S index 1ad8ee34bdef..ee3950c9b0eb 100644 --- a/lib/builtins/arm/udivmodsi4.S +++ b/lib/builtins/arm/udivmodsi4.S @@ -16,10 +16,7 @@ .syntax unified .text - -#if __ARM_ARCH_ISA_THUMB == 2 - .thumb -#endif + DEFINE_CODE_STATE @ unsigned int __udivmodsi4(unsigned int divident, unsigned int divisor, @ unsigned int *remainder) @@ -27,11 +24,7 @@ @ value is the quotient, the remainder is placed in the variable. .p2align 2 -#if __ARM_ARCH_ISA_THUMB == 2 -DEFINE_COMPILERRT_THUMB_FUNCTION(__udivmodsi4) -#else DEFINE_COMPILERRT_FUNCTION(__udivmodsi4) -#endif #if __ARM_ARCH_EXT_IDIV__ tst r1, r1 beq LOCAL_LABEL(divby0) @@ -67,7 +60,7 @@ DEFINE_COMPILERRT_FUNCTION(__udivmodsi4) clz r3, r1 /* r0 >= r1 implies clz(r0) <= clz(r1), so ip <= r3. */ sub r3, r3, ip -# if __ARM_ARCH_ISA_THUMB == 2 +# if defined(USE_THUMB_2) adr ip, LOCAL_LABEL(div0block) + 1 sub ip, ip, r3, lsl #1 # else @@ -78,7 +71,7 @@ DEFINE_COMPILERRT_FUNCTION(__udivmodsi4) mov r3, #0 bx ip # else -# if __ARM_ARCH_ISA_THUMB == 2 +# if defined(USE_THUMB_2) # error THUMB mode requires CLZ or UDIV # endif str r4, [sp, #-8]! diff --git a/lib/builtins/arm/udivsi3.S b/lib/builtins/arm/udivsi3.S index b97b3080bff4..6dea27d404ff 100644 --- a/lib/builtins/arm/udivsi3.S +++ b/lib/builtins/arm/udivsi3.S @@ -17,9 +17,7 @@ .syntax unified .text -#if __ARM_ARCH_ISA_THUMB == 2 - .thumb -#endif +DEFINE_CODE_STATE .p2align 2 DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_uidiv, __udivsi3) @@ -27,11 +25,7 @@ DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_uidiv, __udivsi3) @ unsigned int __udivsi3(unsigned int divident, unsigned int divisor) @ Calculate and return the quotient of the (unsigned) division. -#if __ARM_ARCH_ISA_THUMB == 2 -DEFINE_COMPILERRT_THUMB_FUNCTION(__udivsi3) -#else DEFINE_COMPILERRT_FUNCTION(__udivsi3) -#endif #if __ARM_ARCH_EXT_IDIV__ tst r1, r1 beq LOCAL_LABEL(divby0) @@ -49,7 +43,7 @@ LOCAL_LABEL(divby0): #else /* ! __ARM_ARCH_EXT_IDIV__ */ cmp r1, #1 bcc LOCAL_LABEL(divby0) -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) bne LOCAL_LABEL(num_neq_denom) JMP(lr) LOCAL_LABEL(num_neq_denom): @@ -58,7 +52,7 @@ LOCAL_LABEL(num_neq_denom): JMPc(lr, eq) #endif cmp r0, r1 -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) bhs LOCAL_LABEL(num_ge_denom) movs r0, #0 JMP(lr) @@ -90,7 +84,7 @@ LOCAL_LABEL(num_ge_denom): clz r3, r1 /* r0 >= r1 implies clz(r0) <= clz(r1), so ip <= r3. */ sub r3, r3, ip -# if __ARM_ARCH_ISA_THUMB == 2 +# if defined(USE_THUMB_2) adr ip, LOCAL_LABEL(div0block) + 1 sub ip, ip, r3, lsl #1 # else @@ -101,17 +95,17 @@ LOCAL_LABEL(num_ge_denom): mov r3, #0 bx ip # else /* No CLZ Feature */ -# if __ARM_ARCH_ISA_THUMB == 2 +# if defined(USE_THUMB_2) # error THUMB mode requires CLZ or UDIV # endif -# if __ARM_ARCH_ISA_THUMB == 1 +# if defined(USE_THUMB_1) # define BLOCK_SIZE 10 # else # define BLOCK_SIZE 12 # endif mov r2, r0 -# if __ARM_ARCH_ISA_THUMB == 1 +# if defined(USE_THUMB_1) mov ip, r0 adr r0, LOCAL_LABEL(div0block) adds r0, #1 @@ -120,7 +114,7 @@ LOCAL_LABEL(num_ge_denom): # endif lsrs r3, r2, #16 cmp r3, r1 -# if __ARM_ARCH_ISA_THUMB == 1 +# if defined(USE_THUMB_1) blo LOCAL_LABEL(skip_16) movs r2, r3 subs r0, r0, #(16 * BLOCK_SIZE) @@ -132,7 +126,7 @@ LOCAL_LABEL(skip_16): lsrs r3, r2, #8 cmp r3, r1 -# if __ARM_ARCH_ISA_THUMB == 1 +# if defined(USE_THUMB_1) blo LOCAL_LABEL(skip_8) movs r2, r3 subs r0, r0, #(8 * BLOCK_SIZE) @@ -144,7 +138,7 @@ LOCAL_LABEL(skip_8): lsrs r3, r2, #4 cmp r3, r1 -# if __ARM_ARCH_ISA_THUMB == 1 +# if defined(USE_THUMB_1) blo LOCAL_LABEL(skip_4) movs r2, r3 subs r0, r0, #(4 * BLOCK_SIZE) @@ -156,7 +150,7 @@ LOCAL_LABEL(skip_4): lsrs r3, r2, #2 cmp r3, r1 -# if __ARM_ARCH_ISA_THUMB == 1 +# if defined(USE_THUMB_1) blo LOCAL_LABEL(skip_2) movs r2, r3 subs r0, r0, #(2 * BLOCK_SIZE) @@ -167,7 +161,7 @@ LOCAL_LABEL(skip_2): # endif /* Last block, no need to update r2 or r3. */ -# if __ARM_ARCH_ISA_THUMB == 1 +# if defined(USE_THUMB_1) lsrs r3, r2, #1 cmp r3, r1 blo LOCAL_LABEL(skip_1) @@ -203,7 +197,7 @@ LOCAL_LABEL(divby0): # endif -#if __ARM_ARCH_ISA_THUMB == 1 +#if defined(USE_THUMB_1) #define block(shift) \ lsls r2, r1, IMM shift; \ cmp r0, r2; \ diff --git a/lib/builtins/arm/umodsi3.S b/lib/builtins/arm/umodsi3.S index 672487e81a63..069fad34cb9c 100644 --- a/lib/builtins/arm/umodsi3.S +++ b/lib/builtins/arm/umodsi3.S @@ -16,19 +16,13 @@ .syntax unified .text -#if __ARM_ARCH_ISA_THUMB == 2 - .thumb -#endif + DEFINE_CODE_STATE @ unsigned int __umodsi3(unsigned int divident, unsigned int divisor) @ Calculate and return the remainder of the (unsigned) division. .p2align 2 -#if __ARM_ARCH_ISA_THUMB == 2 -DEFINE_COMPILERRT_THUMB_FUNCTION(__umodsi3) -#else DEFINE_COMPILERRT_FUNCTION(__umodsi3) -#endif #if __ARM_ARCH_EXT_IDIV__ tst r1, r1 beq LOCAL_LABEL(divby0) @@ -65,7 +59,7 @@ DEFINE_COMPILERRT_FUNCTION(__umodsi3) clz r3, r1 /* r0 >= r1 implies clz(r0) <= clz(r1), so ip <= r3. */ sub r3, r3, ip -# if __ARM_ARCH_ISA_THUMB == 2 +# if defined(USE_THUMB_2) adr ip, LOCAL_LABEL(div0block) + 1 sub ip, ip, r3, lsl #1 # else @@ -74,7 +68,7 @@ DEFINE_COMPILERRT_FUNCTION(__umodsi3) sub ip, ip, r3, lsl #3 bx ip # else -# if __ARM_ARCH_ISA_THUMB == 2 +# if defined(USE_THUMB_2) # error THUMB mode requires CLZ or UDIV # endif mov r2, r0 diff --git a/lib/builtins/ashldi3.c b/lib/builtins/ashldi3.c index fcb0abdb1fce..a5c1836006b9 100644 --- a/lib/builtins/ashldi3.c +++ b/lib/builtins/ashldi3.c @@ -41,8 +41,5 @@ __ashldi3(di_int a, si_int b) } #if defined(__ARM_EABI__) -AEABI_RTABI di_int __aeabi_llsl(di_int a, si_int b) { - return __ashldi3(a, b); -} +AEABI_RTABI di_int __aeabi_llsl(di_int a, si_int b) COMPILER_RT_ALIAS(__ashldi3); #endif - diff --git a/lib/builtins/ashrdi3.c b/lib/builtins/ashrdi3.c index b4ab4c617ba0..84619965eca0 100644 --- a/lib/builtins/ashrdi3.c +++ b/lib/builtins/ashrdi3.c @@ -42,8 +42,5 @@ __ashrdi3(di_int a, si_int b) } #if defined(__ARM_EABI__) -AEABI_RTABI di_int __aeabi_lasr(di_int a, si_int b) { - return __ashrdi3(a, b); -} +AEABI_RTABI di_int __aeabi_lasr(di_int a, si_int b) COMPILER_RT_ALIAS(__ashrdi3); #endif - diff --git a/lib/builtins/assembly.h b/lib/builtins/assembly.h index b15da52345c8..3f5e59b25442 100644 --- a/lib/builtins/assembly.h +++ b/lib/builtins/assembly.h @@ -68,10 +68,42 @@ #endif #if defined(__arm__) + +/* + * Determine actual [ARM][THUMB[1][2]] ISA using compiler predefined macros: + * - for '-mthumb -march=armv6' compiler defines '__thumb__' + * - for '-mthumb -march=armv7' compiler defines '__thumb__' and '__thumb2__' + */ +#if defined(__thumb2__) || defined(__thumb__) +#define DEFINE_CODE_STATE .thumb SEPARATOR +#define DECLARE_FUNC_ENCODING .thumb_func SEPARATOR +#if defined(__thumb2__) +#define USE_THUMB_2 +#define IT(cond) it cond +#define ITT(cond) itt cond +#define ITE(cond) ite cond +#else +#define USE_THUMB_1 +#define IT(cond) +#define ITT(cond) +#define ITE(cond) +#endif // defined(__thumb__2) +#else // !defined(__thumb2__) && !defined(__thumb__) +#define DEFINE_CODE_STATE .arm SEPARATOR +#define DECLARE_FUNC_ENCODING +#define IT(cond) +#define ITT(cond) +#define ITE(cond) +#endif + +#if defined(USE_THUMB_1) && defined(USE_THUMB_2) +#error "USE_THUMB_1 and USE_THUMB_2 can't be defined together." +#endif + #if defined(__ARM_ARCH_4T__) || __ARM_ARCH >= 5 #define ARM_HAS_BX #endif -#if !defined(__ARM_FEATURE_CLZ) && __ARM_ARCH_ISA_THUMB != 1 && \ +#if !defined(__ARM_FEATURE_CLZ) && !defined(USE_THUMB_1) && \ (__ARM_ARCH >= 6 || (__ARM_ARCH == 5 && !defined(__ARM_ARCH_5__))) #define __ARM_FEATURE_CLZ #endif @@ -93,21 +125,14 @@ JMP(ip) #endif -#if __ARM_ARCH_ISA_THUMB == 2 -#define IT(cond) it cond -#define ITT(cond) itt cond -#define ITE(cond) ite cond -#else -#define IT(cond) -#define ITT(cond) -#define ITE(cond) -#endif - -#if __ARM_ARCH_ISA_THUMB == 2 +#if defined(USE_THUMB_2) #define WIDE(op) op.w #else #define WIDE(op) op #endif +#else // !defined(__arm) +#define DECLARE_FUNC_ENCODING +#define DEFINE_CODE_STATE #endif #define GLUE2(a, b) a##b @@ -122,13 +147,16 @@ #endif #define DEFINE_COMPILERRT_FUNCTION(name) \ + DEFINE_CODE_STATE \ FILE_LEVEL_DIRECTIVE SEPARATOR \ .globl SYMBOL_NAME(name) SEPARATOR \ SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \ DECLARE_SYMBOL_VISIBILITY(name) \ + DECLARE_FUNC_ENCODING \ SYMBOL_NAME(name): #define DEFINE_COMPILERRT_THUMB_FUNCTION(name) \ + DEFINE_CODE_STATE \ FILE_LEVEL_DIRECTIVE SEPARATOR \ .globl SYMBOL_NAME(name) SEPARATOR \ SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \ @@ -137,16 +165,20 @@ SYMBOL_NAME(name): #define DEFINE_COMPILERRT_PRIVATE_FUNCTION(name) \ + DEFINE_CODE_STATE \ FILE_LEVEL_DIRECTIVE SEPARATOR \ .globl SYMBOL_NAME(name) SEPARATOR \ SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \ HIDDEN(SYMBOL_NAME(name)) SEPARATOR \ + DECLARE_FUNC_ENCODING \ SYMBOL_NAME(name): #define DEFINE_COMPILERRT_PRIVATE_FUNCTION_UNMANGLED(name) \ + DEFINE_CODE_STATE \ .globl name SEPARATOR \ SYMBOL_IS_FUNC(name) SEPARATOR \ HIDDEN(name) SEPARATOR \ + DECLARE_FUNC_ENCODING \ name: #define DEFINE_COMPILERRT_FUNCTION_ALIAS(name, target) \ diff --git a/lib/builtins/clear_cache.c b/lib/builtins/clear_cache.c index 25570fc2157a..4a01cb46d4ac 100644 --- a/lib/builtins/clear_cache.c +++ b/lib/builtins/clear_cache.c @@ -9,6 +9,7 @@ */ #include "int_lib.h" +#include <assert.h> #include <stddef.h> #if __APPLE__ @@ -23,7 +24,7 @@ uint32_t FlushInstructionCache(uintptr_t hProcess, void *lpBaseAddress, uintptr_t GetCurrentProcess(void); #endif -#if (defined(__FreeBSD__) || defined(__Bitrig__)) && defined(__arm__) +#if defined(__FreeBSD__) && defined(__arm__) #include <sys/types.h> #include <machine/sysarch.h> #endif @@ -32,7 +33,7 @@ uintptr_t GetCurrentProcess(void); #include <machine/sysarch.h> #endif -#if defined(__mips__) +#if defined(__linux__) && defined(__mips__) #include <sys/cachectl.h> #include <sys/syscall.h> #include <unistd.h> @@ -41,7 +42,7 @@ uintptr_t GetCurrentProcess(void); * clear_mips_cache - Invalidates instruction cache for Mips. */ static void clear_mips_cache(const void* Addr, size_t Size) { - asm volatile ( + __asm__ volatile ( ".set push\n" ".set noreorder\n" ".set noat\n" @@ -96,7 +97,7 @@ void __clear_cache(void *start, void *end) { * so there is nothing to do */ #elif defined(__arm__) && !defined(__APPLE__) - #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__Bitrig__) + #if defined(__FreeBSD__) || defined(__NetBSD__) struct arm_sync_icache_args arg; arg.addr = (uintptr_t)start; @@ -121,15 +122,13 @@ void __clear_cache(void *start, void *end) { : "=r"(start_reg) : "r"(syscall_nr), "r"(start_reg), "r"(end_reg), "r"(flags)); - if (start_reg != 0) { - compilerrt_abort(); - } + assert(start_reg == 0 && "Cache flush syscall failed."); #elif defined(_WIN32) FlushInstructionCache(GetCurrentProcess(), start, end - start); #else compilerrt_abort(); #endif -#elif defined(__mips__) +#elif defined(__linux__) && defined(__mips__) const uintptr_t start_int = (uintptr_t) start; const uintptr_t end_int = (uintptr_t) end; #if defined(__ANDROID__) && defined(__LP64__) @@ -165,6 +164,21 @@ void __clear_cache(void *start, void *end) { for (addr = xstart; addr < xend; addr += icache_line_size) __asm __volatile("ic ivau, %0" :: "r"(addr)); __asm __volatile("isb sy"); +#elif defined (__powerpc64__) + const size_t line_size = 32; + const size_t len = (uintptr_t)end - (uintptr_t)start; + + const uintptr_t mask = ~(line_size - 1); + const uintptr_t start_line = ((uintptr_t)start) & mask; + const uintptr_t end_line = ((uintptr_t)start + len + line_size - 1) & mask; + + for (uintptr_t line = start_line; line < end_line; line += line_size) + __asm__ volatile("dcbf 0, %0" : : "r"(line)); + __asm__ volatile("sync"); + + for (uintptr_t line = start_line; line < end_line; line += line_size) + __asm__ volatile("icbi 0, %0" : : "r"(line)); + __asm__ volatile("isync"); #else #if __APPLE__ /* On Darwin, sys_icache_invalidate() provides this functionality */ @@ -174,4 +188,3 @@ void __clear_cache(void *start, void *end) { #endif #endif } - diff --git a/lib/builtins/comparedf2.c b/lib/builtins/comparedf2.c index c5bb169d0021..44e5d2b288a6 100644 --- a/lib/builtins/comparedf2.c +++ b/lib/builtins/comparedf2.c @@ -143,8 +143,11 @@ __gtdf2(fp_t a, fp_t b) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI int __aeabi_dcmpun(fp_t a, fp_t b) { return __unorddf2(a, b); } +#else +AEABI_RTABI int __aeabi_dcmpun(fp_t a, fp_t b) COMPILER_RT_ALIAS(__unorddf2); +#endif #endif - diff --git a/lib/builtins/comparesf2.c b/lib/builtins/comparesf2.c index 4badb5e1b9f7..43cd6a6a7003 100644 --- a/lib/builtins/comparesf2.c +++ b/lib/builtins/comparesf2.c @@ -143,8 +143,11 @@ __gtsf2(fp_t a, fp_t b) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI int __aeabi_fcmpun(fp_t a, fp_t b) { return __unordsf2(a, b); } +#else +AEABI_RTABI int __aeabi_fcmpun(fp_t a, fp_t b) COMPILER_RT_ALIAS(__unordsf2); +#endif #endif - diff --git a/lib/builtins/cpu_model.c b/lib/builtins/cpu_model.c index 83ea7a49faf7..4c96e9cd85d5 100644 --- a/lib/builtins/cpu_model.c +++ b/lib/builtins/cpu_model.c @@ -54,6 +54,7 @@ enum ProcessorTypes { AMD_BTVER1, AMD_BTVER2, AMDFAM17H, + INTEL_KNM, CPU_TYPE_MAX }; @@ -74,6 +75,7 @@ enum ProcessorSubtypes { INTEL_COREI7_BROADWELL, INTEL_COREI7_SKYLAKE, INTEL_COREI7_SKYLAKE_AVX512, + INTEL_COREI7_CANNONLAKE, CPU_SUBTYPE_MAX }; @@ -339,6 +341,12 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, *Subtype = INTEL_COREI7_SKYLAKE_AVX512; // "skylake-avx512" break; + // Cannonlake: + case 0x66: + *Type = INTEL_COREI7; + *Subtype = INTEL_COREI7_CANNONLAKE; // "cannonlake" + break; + case 0x1c: // Most 45 nm Intel Atom processors case 0x26: // 45 nm Atom Lincroft case 0x27: // 32 nm Atom Medfield @@ -361,6 +369,10 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, *Type = INTEL_KNL; // knl break; + case 0x85: + *Type = INTEL_KNM; // knm + break; + default: // Unknown family 6 CPU. break; break; diff --git a/lib/builtins/divdf3.c b/lib/builtins/divdf3.c index 492e32b851e9..04a4dc5571ca 100644 --- a/lib/builtins/divdf3.c +++ b/lib/builtins/divdf3.c @@ -183,8 +183,11 @@ __divdf3(fp_t a, fp_t b) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI fp_t __aeabi_ddiv(fp_t a, fp_t b) { return __divdf3(a, b); } +#else +AEABI_RTABI fp_t __aeabi_ddiv(fp_t a, fp_t b) COMPILER_RT_ALIAS(__divdf3); +#endif #endif - diff --git a/lib/builtins/divsf3.c b/lib/builtins/divsf3.c index aa6289a6d70a..65294d70fc61 100644 --- a/lib/builtins/divsf3.c +++ b/lib/builtins/divsf3.c @@ -167,8 +167,11 @@ __divsf3(fp_t a, fp_t b) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI fp_t __aeabi_fdiv(fp_t a, fp_t b) { return __divsf3(a, b); } +#else +AEABI_RTABI fp_t __aeabi_fdiv(fp_t a, fp_t b) COMPILER_RT_ALIAS(__divsf3); +#endif #endif - diff --git a/lib/builtins/divsi3.c b/lib/builtins/divsi3.c index 3852e3990b5b..75aea008ddc1 100644 --- a/lib/builtins/divsi3.c +++ b/lib/builtins/divsi3.c @@ -35,8 +35,5 @@ __divsi3(si_int a, si_int b) } #if defined(__ARM_EABI__) -AEABI_RTABI si_int __aeabi_idiv(si_int a, si_int b) { - return __divsi3(a, b); -} +AEABI_RTABI si_int __aeabi_idiv(si_int a, si_int b) COMPILER_RT_ALIAS(__divsi3); #endif - diff --git a/lib/builtins/emutls.c b/lib/builtins/emutls.c index 12aad3a42b76..5dd8dd154771 100644 --- a/lib/builtins/emutls.c +++ b/lib/builtins/emutls.c @@ -102,7 +102,6 @@ static __inline emutls_address_array* emutls_getspecific() { #include <malloc.h> #include <stdio.h> #include <assert.h> -#include <immintrin.h> static LPCRITICAL_SECTION emutls_mutex; static DWORD emutls_tls_index = TLS_OUT_OF_INDEXES; @@ -203,25 +202,24 @@ static __inline emutls_address_array* emutls_getspecific() { /* Provide atomic load/store functions for emutls_get_index if built with MSVC. */ #if !defined(__ATOMIC_RELEASE) +#include <intrin.h> enum { __ATOMIC_ACQUIRE = 2, __ATOMIC_RELEASE = 3 }; static __inline uintptr_t __atomic_load_n(void *ptr, unsigned type) { assert(type == __ATOMIC_ACQUIRE); + // These return the previous value - but since we do an OR with 0, + // it's equivalent to a plain load. #ifdef _WIN64 - return (uintptr_t) _load_be_u64(ptr); + return InterlockedOr64(ptr, 0); #else - return (uintptr_t) _load_be_u32(ptr); + return InterlockedOr(ptr, 0); #endif } static __inline void __atomic_store_n(void *ptr, uintptr_t val, unsigned type) { assert(type == __ATOMIC_RELEASE); -#ifdef _WIN64 - _store_be_u64(ptr, val); -#else - _store_be_u32(ptr, val); -#endif + InterlockedExchangePointer((void *volatile *)ptr, (void *)val); } #endif diff --git a/lib/builtins/enable_execute_stack.c b/lib/builtins/enable_execute_stack.c index 0dc3482c4467..327d460b4253 100644 --- a/lib/builtins/enable_execute_stack.c +++ b/lib/builtins/enable_execute_stack.c @@ -22,7 +22,7 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN -#include <Windows.h> +#include <windows.h> #else #ifndef __APPLE__ #include <unistd.h> diff --git a/lib/builtins/extendhfsf2.c b/lib/builtins/extendhfsf2.c index e7d9fde8abfc..d9c0db84b0ce 100644 --- a/lib/builtins/extendhfsf2.c +++ b/lib/builtins/extendhfsf2.c @@ -23,8 +23,11 @@ COMPILER_RT_ABI float __gnu_h2f_ieee(uint16_t a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI float __aeabi_h2f(uint16_t a) { return __extendhfsf2(a); } +#else +AEABI_RTABI float __aeabi_h2f(uint16_t a) COMPILER_RT_ALIAS(__extendhfsf2); +#endif #endif - diff --git a/lib/builtins/extendsfdf2.c b/lib/builtins/extendsfdf2.c index b9e7a7471a98..3d84529a6c53 100644 --- a/lib/builtins/extendsfdf2.c +++ b/lib/builtins/extendsfdf2.c @@ -17,8 +17,11 @@ COMPILER_RT_ABI double __extendsfdf2(float a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI double __aeabi_f2d(float a) { return __extendsfdf2(a); } +#else +AEABI_RTABI double __aeabi_f2d(float a) COMPILER_RT_ALIAS(__extendsfdf2); +#endif #endif - diff --git a/lib/builtins/fixdfdi.c b/lib/builtins/fixdfdi.c index 31d76df28255..54e312d3c8f7 100644 --- a/lib/builtins/fixdfdi.c +++ b/lib/builtins/fixdfdi.c @@ -45,13 +45,11 @@ __fixdfdi(fp_t a) { #endif #if defined(__ARM_EABI__) -AEABI_RTABI di_int -#if defined(__SOFT_FP__) -__aeabi_d2lz(fp_t a) { -#else -__aeabi_d2lz(double a) { -#endif +#if defined(COMPILER_RT_ARMHF_TARGET) +AEABI_RTABI di_int __aeabi_d2lz(fp_t a) { return __fixdfdi(a); } +#else +AEABI_RTABI di_int __aeabi_d2lz(fp_t a) COMPILER_RT_ALIAS(__fixdfdi); +#endif #endif - diff --git a/lib/builtins/fixdfsi.c b/lib/builtins/fixdfsi.c index fc316dcd0545..5b9588175717 100644 --- a/lib/builtins/fixdfsi.c +++ b/lib/builtins/fixdfsi.c @@ -20,8 +20,11 @@ __fixdfsi(fp_t a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI si_int __aeabi_d2iz(fp_t a) { return __fixdfsi(a); } +#else +AEABI_RTABI si_int __aeabi_d2iz(fp_t a) COMPILER_RT_ALIAS(__fixdfsi); +#endif #endif - diff --git a/lib/builtins/fixsfdi.c b/lib/builtins/fixsfdi.c index c43473637d60..32e87c60889f 100644 --- a/lib/builtins/fixsfdi.c +++ b/lib/builtins/fixsfdi.c @@ -45,13 +45,11 @@ __fixsfdi(fp_t a) { #endif #if defined(__ARM_EABI__) -AEABI_RTABI di_int -#if defined(__SOFT_FP__) -__aeabi_f2lz(fp_t a) { -#else -__aeabi_f2lz(float a) { -#endif +#if defined(COMPILER_RT_ARMHF_TARGET) +AEABI_RTABI di_int __aeabi_f2lz(fp_t a) { return __fixsfdi(a); } +#else +AEABI_RTABI di_int __aeabi_f2lz(fp_t a) COMPILER_RT_ALIAS(__fixsfdi); +#endif #endif - diff --git a/lib/builtins/fixsfsi.c b/lib/builtins/fixsfsi.c index 3276df966460..e94e5f3dcd68 100644 --- a/lib/builtins/fixsfsi.c +++ b/lib/builtins/fixsfsi.c @@ -20,8 +20,11 @@ __fixsfsi(fp_t a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI si_int __aeabi_f2iz(fp_t a) { return __fixsfsi(a); } +#else +AEABI_RTABI si_int __aeabi_f2iz(fp_t a) COMPILER_RT_ALIAS(__fixsfsi); +#endif #endif - diff --git a/lib/builtins/fixunsdfdi.c b/lib/builtins/fixunsdfdi.c index b734409709bf..bfe4dbb25656 100644 --- a/lib/builtins/fixunsdfdi.c +++ b/lib/builtins/fixunsdfdi.c @@ -42,13 +42,11 @@ __fixunsdfdi(fp_t a) { #endif #if defined(__ARM_EABI__) -AEABI_RTABI du_int -#if defined(__SOFT_FP__) -__aeabi_d2ulz(fp_t a) { -#else -__aeabi_d2ulz(double a) { -#endif +#if defined(COMPILER_RT_ARMHF_TARGET) +AEABI_RTABI du_int __aeabi_d2ulz(fp_t a) { return __fixunsdfdi(a); } +#else +AEABI_RTABI du_int __aeabi_d2ulz(fp_t a) COMPILER_RT_ALIAS(__fixunsdfdi); +#endif #endif - diff --git a/lib/builtins/fixunsdfsi.c b/lib/builtins/fixunsdfsi.c index bb3d8e0f831b..3c5355beae1a 100644 --- a/lib/builtins/fixunsdfsi.c +++ b/lib/builtins/fixunsdfsi.c @@ -19,8 +19,11 @@ __fixunsdfsi(fp_t a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI su_int __aeabi_d2uiz(fp_t a) { return __fixunsdfsi(a); } +#else +AEABI_RTABI su_int __aeabi_d2uiz(fp_t a) COMPILER_RT_ALIAS(__fixunsdfsi); +#endif #endif - diff --git a/lib/builtins/fixunssfdi.c b/lib/builtins/fixunssfdi.c index 5d92245df0d9..080a25bb1e99 100644 --- a/lib/builtins/fixunssfdi.c +++ b/lib/builtins/fixunssfdi.c @@ -43,13 +43,11 @@ __fixunssfdi(fp_t a) { #endif #if defined(__ARM_EABI__) -AEABI_RTABI du_int -#if defined(__SOFT_FP__) -__aeabi_f2ulz(fp_t a) { -#else -__aeabi_f2ulz(float a) { -#endif +#if defined(COMPILER_RT_ARMHF_TARGET) +AEABI_RTABI du_int __aeabi_f2ulz(fp_t a) { return __fixunssfdi(a); } +#else +AEABI_RTABI du_int __aeabi_f2ulz(fp_t a) COMPILER_RT_ALIAS(__fixunssfdi); +#endif #endif - diff --git a/lib/builtins/fixunssfsi.c b/lib/builtins/fixunssfsi.c index 91d5e8ae5d7f..eca2916a5c88 100644 --- a/lib/builtins/fixunssfsi.c +++ b/lib/builtins/fixunssfsi.c @@ -23,8 +23,11 @@ __fixunssfsi(fp_t a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI su_int __aeabi_f2uiz(fp_t a) { return __fixunssfsi(a); } +#else +AEABI_RTABI su_int __aeabi_f2uiz(fp_t a) COMPILER_RT_ALIAS(__fixunssfsi); +#endif #endif - diff --git a/lib/builtins/floatdidf.c b/lib/builtins/floatdidf.c index 681fecef9682..36b856e078d4 100644 --- a/lib/builtins/floatdidf.c +++ b/lib/builtins/floatdidf.c @@ -105,8 +105,11 @@ __floatdidf(di_int a) #endif #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI double __aeabi_l2d(di_int a) { return __floatdidf(a); } +#else +AEABI_RTABI double __aeabi_l2d(di_int a) COMPILER_RT_ALIAS(__floatdidf); +#endif #endif - diff --git a/lib/builtins/floatdisf.c b/lib/builtins/floatdisf.c index dd548165c373..a2f09eb2ed2c 100644 --- a/lib/builtins/floatdisf.c +++ b/lib/builtins/floatdisf.c @@ -78,8 +78,11 @@ __floatdisf(di_int a) } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI float __aeabi_l2f(di_int a) { return __floatdisf(a); } +#else +AEABI_RTABI float __aeabi_l2f(di_int a) COMPILER_RT_ALIAS(__floatdisf); +#endif #endif - diff --git a/lib/builtins/floatsidf.c b/lib/builtins/floatsidf.c index 2ae395bdc1db..fe051123ce7c 100644 --- a/lib/builtins/floatsidf.c +++ b/lib/builtins/floatsidf.c @@ -51,8 +51,11 @@ __floatsidf(int a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI fp_t __aeabi_i2d(int a) { return __floatsidf(a); } +#else +AEABI_RTABI fp_t __aeabi_i2d(int a) COMPILER_RT_ALIAS(__floatsidf); +#endif #endif - diff --git a/lib/builtins/floatsisf.c b/lib/builtins/floatsisf.c index 08891fcdf201..bf087ee3c295 100644 --- a/lib/builtins/floatsisf.c +++ b/lib/builtins/floatsisf.c @@ -57,8 +57,11 @@ __floatsisf(int a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI fp_t __aeabi_i2f(int a) { return __floatsisf(a); } +#else +AEABI_RTABI fp_t __aeabi_i2f(int a) COMPILER_RT_ALIAS(__floatsisf); +#endif #endif - diff --git a/lib/builtins/floatundidf.c b/lib/builtins/floatundidf.c index 6c1a931ef2f3..8bc2a096324f 100644 --- a/lib/builtins/floatundidf.c +++ b/lib/builtins/floatundidf.c @@ -104,8 +104,11 @@ __floatundidf(du_int a) #endif #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI double __aeabi_ul2d(du_int a) { return __floatundidf(a); } +#else +AEABI_RTABI double __aeabi_ul2d(du_int a) COMPILER_RT_ALIAS(__floatundidf); +#endif #endif - diff --git a/lib/builtins/floatundisf.c b/lib/builtins/floatundisf.c index 86841a75dc66..844786ea7777 100644 --- a/lib/builtins/floatundisf.c +++ b/lib/builtins/floatundisf.c @@ -75,8 +75,11 @@ __floatundisf(du_int a) } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI float __aeabi_ul2f(du_int a) { return __floatundisf(a); } +#else +AEABI_RTABI float __aeabi_ul2f(du_int a) COMPILER_RT_ALIAS(__floatundisf); +#endif #endif - diff --git a/lib/builtins/floatunsidf.c b/lib/builtins/floatunsidf.c index 8d4807194f0b..75cf6b9177df 100644 --- a/lib/builtins/floatunsidf.c +++ b/lib/builtins/floatunsidf.c @@ -40,8 +40,11 @@ __floatunsidf(unsigned int a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI fp_t __aeabi_ui2d(unsigned int a) { return __floatunsidf(a); } +#else +AEABI_RTABI fp_t __aeabi_ui2d(unsigned int a) COMPILER_RT_ALIAS(__floatunsidf); +#endif #endif - diff --git a/lib/builtins/floatunsisf.c b/lib/builtins/floatunsisf.c index f194c046d2fb..29525ccedbbe 100644 --- a/lib/builtins/floatunsisf.c +++ b/lib/builtins/floatunsisf.c @@ -48,8 +48,11 @@ __floatunsisf(unsigned int a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI fp_t __aeabi_ui2f(unsigned int a) { return __floatunsisf(a); } +#else +AEABI_RTABI fp_t __aeabi_ui2f(unsigned int a) COMPILER_RT_ALIAS(__floatunsisf); +#endif #endif - diff --git a/lib/builtins/int_endianness.h b/lib/builtins/int_endianness.h index 7995ddbb953c..e2586c56bac8 100644 --- a/lib/builtins/int_endianness.h +++ b/lib/builtins/int_endianness.h @@ -61,7 +61,7 @@ #endif /* *BSD */ -#if defined(__OpenBSD__) || defined(__Bitrig__) +#if defined(__OpenBSD__) #include <machine/endian.h> #if _BYTE_ORDER == _BIG_ENDIAN @@ -72,7 +72,7 @@ #define _YUGA_BIG_ENDIAN 0 #endif /* _BYTE_ORDER */ -#endif /* OpenBSD and Bitrig. */ +#endif /* OpenBSD */ /* .. */ diff --git a/lib/builtins/int_lib.h b/lib/builtins/int_lib.h index 9a8092d50d8e..9d09e2dc915b 100644 --- a/lib/builtins/int_lib.h +++ b/lib/builtins/int_lib.h @@ -22,9 +22,11 @@ #if defined(__ELF__) #define FNALIAS(alias_name, original_name) \ - void alias_name() __attribute__((alias(#original_name))) + void alias_name() __attribute__((__alias__(#original_name))) +#define COMPILER_RT_ALIAS(aliasee) __attribute__((__alias__(#aliasee))) #else #define FNALIAS(alias, name) _Pragma("GCC error(\"alias unsupported on this file format\")") +#define COMPILER_RT_ALIAS(aliasee) _Pragma("GCC error(\"alias unsupported on this file format\")") #endif /* ABI macro definitions */ diff --git a/lib/builtins/lshrdi3.c b/lib/builtins/lshrdi3.c index becbbef4eb09..67b2a7668345 100644 --- a/lib/builtins/lshrdi3.c +++ b/lib/builtins/lshrdi3.c @@ -41,8 +41,5 @@ __lshrdi3(di_int a, si_int b) } #if defined(__ARM_EABI__) -AEABI_RTABI di_int __aeabi_llsr(di_int a, si_int b) { - return __lshrdi3(a, b); -} +AEABI_RTABI di_int __aeabi_llsr(di_int a, si_int b) COMPILER_RT_ALIAS(__lshrdi3); #endif - diff --git a/lib/builtins/muldf3.c b/lib/builtins/muldf3.c index 59a60190eba3..1bb103e38c13 100644 --- a/lib/builtins/muldf3.c +++ b/lib/builtins/muldf3.c @@ -20,8 +20,11 @@ COMPILER_RT_ABI fp_t __muldf3(fp_t a, fp_t b) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI fp_t __aeabi_dmul(fp_t a, fp_t b) { return __muldf3(a, b); } +#else +AEABI_RTABI fp_t __aeabi_dmul(fp_t a, fp_t b) COMPILER_RT_ALIAS(__muldf3); +#endif #endif - diff --git a/lib/builtins/muldi3.c b/lib/builtins/muldi3.c index 6818a9e2f722..a187315e9165 100644 --- a/lib/builtins/muldi3.c +++ b/lib/builtins/muldi3.c @@ -54,8 +54,5 @@ __muldi3(di_int a, di_int b) } #if defined(__ARM_EABI__) -AEABI_RTABI di_int __aeabi_lmul(di_int a, di_int b) { - return __muldi3(a, b); -} +AEABI_RTABI di_int __aeabi_lmul(di_int a, di_int b) COMPILER_RT_ALIAS(__muldi3); #endif - diff --git a/lib/builtins/mulsf3.c b/lib/builtins/mulsf3.c index f141af1acc58..1e2cf3e717c9 100644 --- a/lib/builtins/mulsf3.c +++ b/lib/builtins/mulsf3.c @@ -20,8 +20,11 @@ COMPILER_RT_ABI fp_t __mulsf3(fp_t a, fp_t b) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI fp_t __aeabi_fmul(fp_t a, fp_t b) { return __mulsf3(a, b); } +#else +AEABI_RTABI fp_t __aeabi_fmul(fp_t a, fp_t b) COMPILER_RT_ALIAS(__mulsf3); +#endif #endif - diff --git a/lib/builtins/negdf2.c b/lib/builtins/negdf2.c index 5e2544cdb4be..f0bfaad24743 100644 --- a/lib/builtins/negdf2.c +++ b/lib/builtins/negdf2.c @@ -20,8 +20,11 @@ __negdf2(fp_t a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI fp_t __aeabi_dneg(fp_t a) { return __negdf2(a); } +#else +AEABI_RTABI fp_t __aeabi_dneg(fp_t a) COMPILER_RT_ALIAS(__negdf2); +#endif #endif - diff --git a/lib/builtins/negsf2.c b/lib/builtins/negsf2.c index f90b34335680..05c97d4d5a11 100644 --- a/lib/builtins/negsf2.c +++ b/lib/builtins/negsf2.c @@ -20,8 +20,11 @@ __negsf2(fp_t a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI fp_t __aeabi_fneg(fp_t a) { return __negsf2(a); } +#else +AEABI_RTABI fp_t __aeabi_fneg(fp_t a) COMPILER_RT_ALIAS(__negsf2); +#endif #endif - diff --git a/lib/builtins/subdf3.c b/lib/builtins/subdf3.c index 38340dfab1a6..a892fa603cf2 100644 --- a/lib/builtins/subdf3.c +++ b/lib/builtins/subdf3.c @@ -22,8 +22,11 @@ __subdf3(fp_t a, fp_t b) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI fp_t __aeabi_dsub(fp_t a, fp_t b) { return __subdf3(a, b); } +#else +AEABI_RTABI fp_t __aeabi_dsub(fp_t a, fp_t b) COMPILER_RT_ALIAS(__subdf3); +#endif #endif - diff --git a/lib/builtins/subsf3.c b/lib/builtins/subsf3.c index 34276b1447ba..4b2786177dcc 100644 --- a/lib/builtins/subsf3.c +++ b/lib/builtins/subsf3.c @@ -22,8 +22,11 @@ __subsf3(fp_t a, fp_t b) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI fp_t __aeabi_fsub(fp_t a, fp_t b) { return __subsf3(a, b); } +#else +AEABI_RTABI fp_t __aeabi_fsub(fp_t a, fp_t b) COMPILER_RT_ALIAS(__subsf3); +#endif #endif - diff --git a/lib/builtins/truncdfhf2.c b/lib/builtins/truncdfhf2.c index 4bb71aa178a0..8354a41b8b6f 100644 --- a/lib/builtins/truncdfhf2.c +++ b/lib/builtins/truncdfhf2.c @@ -16,8 +16,11 @@ COMPILER_RT_ABI uint16_t __truncdfhf2(double a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI uint16_t __aeabi_d2h(double a) { return __truncdfhf2(a); } +#else +AEABI_RTABI uint16_t __aeabi_d2h(double a) COMPILER_RT_ALIAS(__truncdfhf2); +#endif #endif - diff --git a/lib/builtins/truncdfsf2.c b/lib/builtins/truncdfsf2.c index 8bf58bb23a3b..195d3e0656e7 100644 --- a/lib/builtins/truncdfsf2.c +++ b/lib/builtins/truncdfsf2.c @@ -16,8 +16,11 @@ COMPILER_RT_ABI float __truncdfsf2(double a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI float __aeabi_d2f(double a) { return __truncdfsf2(a); } +#else +AEABI_RTABI float __aeabi_d2f(double a) COMPILER_RT_ALIAS(__truncdfsf2); +#endif #endif - diff --git a/lib/builtins/truncsfhf2.c b/lib/builtins/truncsfhf2.c index f6ce1fa1de05..9c84ab4f938a 100644 --- a/lib/builtins/truncsfhf2.c +++ b/lib/builtins/truncsfhf2.c @@ -22,8 +22,11 @@ COMPILER_RT_ABI uint16_t __gnu_f2h_ieee(float a) { } #if defined(__ARM_EABI__) +#if defined(COMPILER_RT_ARMHF_TARGET) AEABI_RTABI uint16_t __aeabi_f2h(float a) { return __truncsfhf2(a); } +#else +AEABI_RTABI uint16_t __aeabi_f2h(float a) COMPILER_RT_ALIAS(__truncsfhf2); +#endif #endif - diff --git a/lib/builtins/udivsi3.c b/lib/builtins/udivsi3.c index 8eccf102cc97..bb720f8c382b 100644 --- a/lib/builtins/udivsi3.c +++ b/lib/builtins/udivsi3.c @@ -64,8 +64,5 @@ __udivsi3(su_int n, su_int d) } #if defined(__ARM_EABI__) -AEABI_RTABI su_int __aeabi_uidiv(su_int n, su_int d) { - return __udivsi3(n, d); -} +AEABI_RTABI su_int __aeabi_uidiv(su_int n, su_int d) COMPILER_RT_ALIAS(__udivsi3); #endif - diff --git a/lib/cfi/CMakeLists.txt b/lib/cfi/CMakeLists.txt index 206400201466..6c531445626a 100644 --- a/lib/cfi/CMakeLists.txt +++ b/lib/cfi/CMakeLists.txt @@ -1,37 +1,39 @@ add_compiler_rt_component(cfi) -set(CFI_SOURCES cfi.cc) +if(OS_NAME MATCHES "Linux") + set(CFI_SOURCES cfi.cc) -include_directories(..) + include_directories(..) -set(CFI_CFLAGS - ${SANITIZER_COMMON_CFLAGS} -) + set(CFI_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + ) -set(CFI_DIAG_CFLAGS - -DCFI_ENABLE_DIAG=1 -) + set(CFI_DIAG_CFLAGS + -DCFI_ENABLE_DIAG=1 + ) -foreach(arch ${CFI_SUPPORTED_ARCH}) - add_compiler_rt_runtime(clang_rt.cfi - STATIC - ARCHS ${arch} - SOURCES ${CFI_SOURCES} - OBJECT_LIBS RTInterception - RTSanitizerCommon - RTSanitizerCommonLibc - CFLAGS ${CFI_CFLAGS} - PARENT_TARGET cfi) - add_compiler_rt_runtime(clang_rt.cfi_diag - STATIC - ARCHS ${arch} - SOURCES ${CFI_SOURCES} - OBJECT_LIBS RTInterception - RTSanitizerCommon - RTSanitizerCommonLibc - RTUbsan - CFLAGS ${CFI_CFLAGS} ${CFI_DIAG_CFLAGS} - PARENT_TARGET cfi) -endforeach() + foreach(arch ${CFI_SUPPORTED_ARCH}) + add_compiler_rt_runtime(clang_rt.cfi + STATIC + ARCHS ${arch} + SOURCES ${CFI_SOURCES} + OBJECT_LIBS RTInterception + RTSanitizerCommon + RTSanitizerCommonLibc + CFLAGS ${CFI_CFLAGS} + PARENT_TARGET cfi) + add_compiler_rt_runtime(clang_rt.cfi_diag + STATIC + ARCHS ${arch} + SOURCES ${CFI_SOURCES} + OBJECT_LIBS RTInterception + RTSanitizerCommon + RTSanitizerCommonLibc + RTUbsan + CFLAGS ${CFI_CFLAGS} ${CFI_DIAG_CFLAGS} + PARENT_TARGET cfi) + endforeach() +endif() add_compiler_rt_resource_file(cfi_blacklist cfi_blacklist.txt cfi) diff --git a/lib/cfi/cfi.cc b/lib/cfi/cfi.cc index f720230a70be..f7693b37d29c 100644 --- a/lib/cfi/cfi.cc +++ b/lib/cfi/cfi.cc @@ -280,7 +280,7 @@ void InitShadow() { CHECK_EQ(0, GetShadow()); CHECK_EQ(0, GetShadowSize()); - uptr vma = GetMaxVirtualAddress(); + uptr vma = GetMaxUserVirtualAddress(); // Shadow is 2 -> 2**kShadowGranularity. SetShadowSize((vma >> (kShadowGranularity - 1)) + 1); VReport(1, "CFI: VMA size %zx, shadow size %zx\n", vma, GetShadowSize()); diff --git a/lib/cfi/cfi_blacklist.txt b/lib/cfi/cfi_blacklist.txt index cc111be8120e..d8c9d49e3cdd 100644 --- a/lib/cfi/cfi_blacklist.txt +++ b/lib/cfi/cfi_blacklist.txt @@ -1,18 +1,4 @@ -# Standard library types. -type:std::* - -# The stdext namespace contains Microsoft standard library extensions. -type:stdext::* - -# Types with a uuid attribute, i.e. COM types. -type:attr:uuid - -# STL allocators (T *allocator<T *>::allocate(size_type, const void*)). -# The type signature mandates a cast from uninitialized void* to T*. -# size_type can either be unsigned int (j) or unsigned long (m). -fun:*8allocateEjPKv -fun:*8allocateEmPKv - +[cfi-unrelated-cast] # std::get_temporary_buffer, likewise (libstdc++, libc++). fun:_ZSt20get_temporary_buffer* fun:_ZNSt3__120get_temporary_buffer* diff --git a/lib/dfsan/dfsan.cc b/lib/dfsan/dfsan.cc index 3aa99b7f9c27..9e360c959fa7 100644 --- a/lib/dfsan/dfsan.cc +++ b/lib/dfsan/dfsan.cc @@ -21,6 +21,7 @@ #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_file.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" diff --git a/lib/esan/esan_sideline.h b/lib/esan/esan_sideline.h index aa3fae1db318..04aff22f4cf0 100644 --- a/lib/esan/esan_sideline.h +++ b/lib/esan/esan_sideline.h @@ -17,6 +17,7 @@ #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_platform_limits_posix.h" namespace __esan { @@ -46,7 +47,8 @@ public: private: static int runSideline(void *Arg); static void registerSignal(int SigNum); - static void handleSidelineSignal(int SigNum, void *SigInfo, void *Ctx); + static void handleSidelineSignal(int SigNum, __sanitizer_siginfo *SigInfo, + void *Ctx); char *Stack; SidelineFunc sampleFunc; diff --git a/lib/esan/esan_sideline_linux.cpp b/lib/esan/esan_sideline_linux.cpp index bc272dfe49f8..4a96910ece35 100644 --- a/lib/esan/esan_sideline_linux.cpp +++ b/lib/esan/esan_sideline_linux.cpp @@ -40,7 +40,8 @@ static const uptr SidelineIdUninitialized = 1; static SidelineThread *TheThread; // We aren't passing SA_NODEFER so the same signal is blocked while here. -void SidelineThread::handleSidelineSignal(int SigNum, void *SigInfo, +void SidelineThread::handleSidelineSignal(int SigNum, + __sanitizer_siginfo *SigInfo, void *Ctx) { VPrintf(3, "Sideline signal %d\n", SigNum); CHECK_EQ(SigNum, SIGALRM); diff --git a/lib/esan/working_set_posix.cpp b/lib/esan/working_set_posix.cpp index fcfa87128857..5ec53b959261 100644 --- a/lib/esan/working_set_posix.cpp +++ b/lib/esan/working_set_posix.cpp @@ -34,7 +34,7 @@ bool processWorkingSetSignal(int SigNum, void (*Handler)(int), VPrintf(2, "%s: %d\n", __FUNCTION__, SigNum); if (SigNum == SIGSEGV) { *Result = AppSigAct.handler; - AppSigAct.sigaction = (void (*)(int, void*, void*))Handler; + AppSigAct.sigaction = (decltype(AppSigAct.sigaction))Handler; return false; // Skip real call. } return true; @@ -73,7 +73,7 @@ bool processWorkingSetSigprocmask(int How, void *Set, void *OldSet) { static void reinstateDefaultHandler(int SigNum) { __sanitizer_sigaction SigAct; internal_memset(&SigAct, 0, sizeof(SigAct)); - SigAct.sigaction = (void (*)(int, void*, void*)) SIG_DFL; + SigAct.sigaction = (decltype(SigAct.sigaction))SIG_DFL; int Res = internal_sigaction(SigNum, &SigAct, nullptr); CHECK(Res == 0); VPrintf(1, "Unregistered for %d handler\n", SigNum); @@ -81,7 +81,8 @@ static void reinstateDefaultHandler(int SigNum) { // If this is a shadow fault, we handle it here; otherwise, we pass it to the // app to handle it just as the app would do without our tool in place. -static void handleMemoryFault(int SigNum, void *Info, void *Ctx) { +static void handleMemoryFault(int SigNum, __sanitizer_siginfo *Info, + void *Ctx) { if (SigNum == SIGSEGV) { // We rely on si_addr being filled in (thus we do not support old kernels). siginfo_t *SigInfo = (siginfo_t *)Info; diff --git a/lib/fuzzer/CMakeLists.txt b/lib/fuzzer/CMakeLists.txt new file mode 100644 index 000000000000..9769be52ae01 --- /dev/null +++ b/lib/fuzzer/CMakeLists.txt @@ -0,0 +1,80 @@ +set(LIBFUZZER_SOURCES + FuzzerClangCounters.cpp + FuzzerCrossOver.cpp + FuzzerDriver.cpp + FuzzerExtFunctionsDlsym.cpp + FuzzerExtFunctionsDlsymWin.cpp + FuzzerExtFunctionsWeak.cpp + FuzzerExtraCounters.cpp + FuzzerIO.cpp + FuzzerIOPosix.cpp + FuzzerIOWindows.cpp + FuzzerLoop.cpp + FuzzerMerge.cpp + FuzzerMutate.cpp + FuzzerSHA1.cpp + FuzzerShmemPosix.cpp + FuzzerShmemWindows.cpp + FuzzerTracePC.cpp + FuzzerUtil.cpp + FuzzerUtilDarwin.cpp + FuzzerUtilFuchsia.cpp + FuzzerUtilLinux.cpp + FuzzerUtilPosix.cpp + FuzzerUtilWindows.cpp + ) + +CHECK_CXX_SOURCE_COMPILES(" + static thread_local int blah; + int main() { + return 0; + } + " HAS_THREAD_LOCAL) + +set(LIBFUZZER_CFLAGS ${SANITIZER_COMMON_CFLAGS}) + +append_list_if(COMPILER_RT_HAS_OMIT_FRAME_POINTER_FLAG -fno-omit-frame-pointer LIBFUZZER_CFLAGS) + +if (CMAKE_CXX_FLAGS MATCHES "fsanitize-coverage") + list(APPEND LIBFUZZER_CFLAGS -fno-sanitize-coverage=trace-pc-guard,edge,trace-cmp,indirect-calls,8bit-counters) +endif() + +if(NOT HAS_THREAD_LOCAL) + list(APPEND LIBFUZZER_CFLAGS -Dthread_local=__thread) +endif() + +if(APPLE) + set(FUZZER_SUPPORTED_OS osx) +endif() + +add_compiler_rt_object_libraries(RTfuzzer + OS ${FUZZER_SUPPORTED_OS} + ARCHS ${FUZZER_SUPPORTED_ARCH} + SOURCES ${LIBFUZZER_SOURCES} + CFLAGS ${LIBFUZZER_CFLAGS}) + +add_compiler_rt_object_libraries(RTfuzzer_main + OS ${FUZZER_SUPPORTED_OS} + ARCHS ${FUZZER_SUPPORTED_ARCH} + SOURCES FuzzerMain.cpp + CFLAGS ${LIBFUZZER_CFLAGS}) + +add_compiler_rt_runtime(clang_rt.fuzzer + STATIC + OS ${FUZZER_SUPPORTED_OS} + ARCHS ${FUZZER_SUPPORTED_ARCH} + OBJECT_LIBS RTfuzzer RTfuzzer_main + CFLAGS ${LIBFUZZER_CFLAGS} + PARENT_TARGET fuzzer) + +add_compiler_rt_runtime(clang_rt.fuzzer_no_main + STATIC + OS ${FUZZER_SUPPORTED_OS} + ARCHS ${FUZZER_SUPPORTED_ARCH} + OBJECT_LIBS RTfuzzer + CFLAGS ${LIBFUZZER_CFLAGS} + PARENT_TARGET fuzzer) + +if(COMPILER_RT_INCLUDE_TESTS) + add_subdirectory(tests) +endif() diff --git a/lib/fuzzer/FuzzerClangCounters.cpp b/lib/fuzzer/FuzzerClangCounters.cpp new file mode 100644 index 000000000000..f69e922cf004 --- /dev/null +++ b/lib/fuzzer/FuzzerClangCounters.cpp @@ -0,0 +1,49 @@ +//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Coverage counters from Clang's SourceBasedCodeCoverage. +//===----------------------------------------------------------------------===// + +// Support for SourceBasedCodeCoverage is experimental: +// * Works only for the main binary, not DSOs yet. +// * Works only on Linux. +// * Does not implement print_pcs/print_coverage yet. +// * Is not fully evaluated for performance and sensitivity. +// We expect large performance drop due to 64-bit counters, +// and *maybe* better sensitivity due to more fine-grained counters. +// Preliminary comparison on a single benchmark (RE2) shows +// a bit worse sensitivity though. + +#include "FuzzerDefs.h" + +#if LIBFUZZER_LINUX +__attribute__((weak)) extern uint64_t __start___llvm_prf_cnts; +__attribute__((weak)) extern uint64_t __stop___llvm_prf_cnts; +namespace fuzzer { +uint64_t *ClangCountersBegin() { return &__start___llvm_prf_cnts; } +uint64_t *ClangCountersEnd() { return &__stop___llvm_prf_cnts; } +} // namespace fuzzer +#else +// TODO: Implement on Mac (if the data shows it's worth it). +//__attribute__((visibility("hidden"))) +//extern uint64_t CountersStart __asm("section$start$__DATA$__llvm_prf_cnts"); +//__attribute__((visibility("hidden"))) +//extern uint64_t CountersEnd __asm("section$end$__DATA$__llvm_prf_cnts"); +namespace fuzzer { +uint64_t *ClangCountersBegin() { return nullptr; } +uint64_t *ClangCountersEnd() { return nullptr; } +} // namespace fuzzer +#endif + +namespace fuzzer { +ATTRIBUTE_NO_SANITIZE_ALL +void ClearClangCounters() { // hand-written memset, don't asan-ify. + for (auto P = ClangCountersBegin(); P < ClangCountersEnd(); P++) + *P = 0; +} +} diff --git a/lib/fuzzer/FuzzerCommand.h b/lib/fuzzer/FuzzerCommand.h new file mode 100644 index 000000000000..c5500ed21fc1 --- /dev/null +++ b/lib/fuzzer/FuzzerCommand.h @@ -0,0 +1,180 @@ +//===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// FuzzerCommand represents a command to run in a subprocess. It allows callers +// to manage command line arguments and output and error streams. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_COMMAND_H +#define LLVM_FUZZER_COMMAND_H + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" + +#include <algorithm> +#include <sstream> +#include <string> +#include <vector> + +namespace fuzzer { + +class Command final { +public: + // This command line flag is used to indicate that the remaining command line + // is immutable, meaning this flag effectively marks the end of the mutable + // argument list. + static inline const char *ignoreRemainingArgs() { + static const char *kIgnoreRemaining = "-ignore_remaining_args=1"; + return kIgnoreRemaining; + } + + Command() : CombinedOutAndErr(false) {} + + explicit Command(const Vector<std::string> &ArgsToAdd) + : Args(ArgsToAdd), CombinedOutAndErr(false) {} + + explicit Command(const Command &Other) + : Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr), + OutputFile(Other.OutputFile) {} + + Command &operator=(const Command &Other) { + Args = Other.Args; + CombinedOutAndErr = Other.CombinedOutAndErr; + OutputFile = Other.OutputFile; + return *this; + } + + ~Command() {} + + // Returns true if the given Arg is present in Args. Only checks up to + // "-ignore_remaining_args=1". + bool hasArgument(const std::string &Arg) const { + auto i = endMutableArgs(); + return std::find(Args.begin(), i, Arg) != i; + } + + // Gets all of the current command line arguments, **including** those after + // "-ignore-remaining-args=1". + const Vector<std::string> &getArguments() const { return Args; } + + // Adds the given argument before "-ignore_remaining_args=1", or at the end + // if that flag isn't present. + void addArgument(const std::string &Arg) { + Args.insert(endMutableArgs(), Arg); + } + + // Adds all given arguments before "-ignore_remaining_args=1", or at the end + // if that flag isn't present. + void addArguments(const Vector<std::string> &ArgsToAdd) { + Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end()); + } + + // Removes the given argument from the command argument list. Ignores any + // occurrences after "-ignore_remaining_args=1", if present. + void removeArgument(const std::string &Arg) { + auto i = endMutableArgs(); + Args.erase(std::remove(Args.begin(), i, Arg), i); + } + + // Like hasArgument, but checks for "-[Flag]=...". + bool hasFlag(const std::string &Flag) { + std::string Arg("-" + Flag + "="); + auto IsMatch = [&](const std::string &Other) { + return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; + }; + return std::any_of(Args.begin(), endMutableArgs(), IsMatch); + } + + // Returns the value of the first instance of a given flag, or an empty string + // if the flag isn't present. Ignores any occurrences after + // "-ignore_remaining_args=1", if present. + std::string getFlagValue(const std::string &Flag) { + std::string Arg("-" + Flag + "="); + auto IsMatch = [&](const std::string &Other) { + return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; + }; + auto i = endMutableArgs(); + auto j = std::find_if(Args.begin(), i, IsMatch); + std::string result; + if (j != i) { + result = j->substr(Arg.length()); + } + return result; + } + + // Like AddArgument, but adds "-[Flag]=[Value]". + void addFlag(const std::string &Flag, const std::string &Value) { + addArgument("-" + Flag + "=" + Value); + } + + // Like RemoveArgument, but removes "-[Flag]=...". + void removeFlag(const std::string &Flag) { + std::string Arg("-" + Flag + "="); + auto IsMatch = [&](const std::string &Other) { + return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; + }; + auto i = endMutableArgs(); + Args.erase(std::remove_if(Args.begin(), i, IsMatch), i); + } + + // Returns whether the command's stdout is being written to an output file. + bool hasOutputFile() const { return !OutputFile.empty(); } + + // Returns the currently set output file. + const std::string &getOutputFile() const { return OutputFile; } + + // Configures the command to redirect its output to the name file. + void setOutputFile(const std::string &FileName) { OutputFile = FileName; } + + // Returns whether the command's stderr is redirected to stdout. + bool isOutAndErrCombined() const { return CombinedOutAndErr; } + + // Sets whether to redirect the command's stderr to its stdout. + void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; } + + // Returns a string representation of the command. On many systems this will + // be the equivalent command line. + std::string toString() const { + std::stringstream SS; + for (auto arg : getArguments()) + SS << arg << " "; + if (hasOutputFile()) + SS << ">" << getOutputFile() << " "; + if (isOutAndErrCombined()) + SS << "2>&1 "; + std::string result = SS.str(); + if (!result.empty()) + result = result.substr(0, result.length() - 1); + return result; + } + +private: + Command(Command &&Other) = delete; + Command &operator=(Command &&Other) = delete; + + Vector<std::string>::iterator endMutableArgs() { + return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); + } + + Vector<std::string>::const_iterator endMutableArgs() const { + return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); + } + + // The command arguments. Args[0] is the command name. + Vector<std::string> Args; + + // True indicates stderr is redirected to stdout. + bool CombinedOutAndErr; + + // If not empty, stdout is redirected to the named file. + std::string OutputFile; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_COMMAND_H diff --git a/lib/fuzzer/FuzzerCorpus.h b/lib/fuzzer/FuzzerCorpus.h new file mode 100644 index 000000000000..2da929835f45 --- /dev/null +++ b/lib/fuzzer/FuzzerCorpus.h @@ -0,0 +1,302 @@ +//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::InputCorpus +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_CORPUS +#define LLVM_FUZZER_CORPUS + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" +#include "FuzzerRandom.h" +#include "FuzzerSHA1.h" +#include "FuzzerTracePC.h" +#include <algorithm> +#include <numeric> +#include <random> +#include <unordered_set> + +namespace fuzzer { + +struct InputInfo { + Unit U; // The actual input data. + uint8_t Sha1[kSHA1NumBytes]; // Checksum. + // Number of features that this input has and no smaller input has. + size_t NumFeatures = 0; + size_t Tmp = 0; // Used by ValidateFeatureSet. + // Stats. + size_t NumExecutedMutations = 0; + size_t NumSuccessfullMutations = 0; + bool MayDeleteFile = false; + bool Reduced = false; + Vector<uint32_t> UniqFeatureSet; + float FeatureFrequencyScore = 1.0; +}; + +class InputCorpus { + static const size_t kFeatureSetSize = 1 << 21; + public: + InputCorpus(const std::string &OutputCorpus) : OutputCorpus(OutputCorpus) { + memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature)); + memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature)); + memset(FeatureFrequency, 0, sizeof(FeatureFrequency)); + } + ~InputCorpus() { + for (auto II : Inputs) + delete II; + } + size_t size() const { return Inputs.size(); } + size_t SizeInBytes() const { + size_t Res = 0; + for (auto II : Inputs) + Res += II->U.size(); + return Res; + } + size_t NumActiveUnits() const { + size_t Res = 0; + for (auto II : Inputs) + Res += !II->U.empty(); + return Res; + } + size_t MaxInputSize() const { + size_t Res = 0; + for (auto II : Inputs) + Res = std::max(Res, II->U.size()); + return Res; + } + bool empty() const { return Inputs.empty(); } + const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; } + void AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile, + const Vector<uint32_t> &FeatureSet) { + assert(!U.empty()); + if (FeatureDebug) + Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures); + Inputs.push_back(new InputInfo()); + InputInfo &II = *Inputs.back(); + II.U = U; + II.NumFeatures = NumFeatures; + II.MayDeleteFile = MayDeleteFile; + II.UniqFeatureSet = FeatureSet; + std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end()); + ComputeSHA1(U.data(), U.size(), II.Sha1); + Hashes.insert(Sha1ToString(II.Sha1)); + UpdateCorpusDistribution(); + PrintCorpus(); + // ValidateFeatureSet(); + } + + // Debug-only + void PrintUnit(const Unit &U) { + if (!FeatureDebug) return; + for (uint8_t C : U) { + if (C != 'F' && C != 'U' && C != 'Z') + C = '.'; + Printf("%c", C); + } + } + + // Debug-only + void PrintFeatureSet(const Vector<uint32_t> &FeatureSet) { + if (!FeatureDebug) return; + Printf("{"); + for (uint32_t Feature: FeatureSet) + Printf("%u,", Feature); + Printf("}"); + } + + // Debug-only + void PrintCorpus() { + if (!FeatureDebug) return; + Printf("======= CORPUS:\n"); + int i = 0; + for (auto II : Inputs) { + if (std::find(II->U.begin(), II->U.end(), 'F') != II->U.end()) { + Printf("[%2d] ", i); + Printf("%s sz=%zd ", Sha1ToString(II->Sha1).c_str(), II->U.size()); + PrintUnit(II->U); + Printf(" "); + PrintFeatureSet(II->UniqFeatureSet); + Printf("\n"); + } + i++; + } + } + + void Replace(InputInfo *II, const Unit &U) { + assert(II->U.size() > U.size()); + Hashes.erase(Sha1ToString(II->Sha1)); + DeleteFile(*II); + ComputeSHA1(U.data(), U.size(), II->Sha1); + Hashes.insert(Sha1ToString(II->Sha1)); + II->U = U; + II->Reduced = true; + UpdateCorpusDistribution(); + } + + bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); } + bool HasUnit(const std::string &H) { return Hashes.count(H); } + InputInfo &ChooseUnitToMutate(Random &Rand) { + InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)]; + assert(!II.U.empty()); + return II; + }; + + // Returns an index of random unit from the corpus to mutate. + size_t ChooseUnitIdxToMutate(Random &Rand) { + size_t Idx = static_cast<size_t>(CorpusDistribution(Rand)); + assert(Idx < Inputs.size()); + return Idx; + } + + void PrintStats() { + for (size_t i = 0; i < Inputs.size(); i++) { + const auto &II = *Inputs[i]; + Printf(" [%zd %s]\tsz: %zd\truns: %zd\tsucc: %zd\n", i, + Sha1ToString(II.Sha1).c_str(), II.U.size(), + II.NumExecutedMutations, II.NumSuccessfullMutations); + } + } + + void PrintFeatureSet() { + for (size_t i = 0; i < kFeatureSetSize; i++) { + if(size_t Sz = GetFeature(i)) + Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz); + } + Printf("\n\t"); + for (size_t i = 0; i < Inputs.size(); i++) + if (size_t N = Inputs[i]->NumFeatures) + Printf(" %zd=>%zd ", i, N); + Printf("\n"); + } + + void DeleteFile(const InputInfo &II) { + if (!OutputCorpus.empty() && II.MayDeleteFile) + RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1))); + } + + void DeleteInput(size_t Idx) { + InputInfo &II = *Inputs[Idx]; + DeleteFile(II); + Unit().swap(II.U); + if (FeatureDebug) + Printf("EVICTED %zd\n", Idx); + } + + bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) { + assert(NewSize); + Idx = Idx % kFeatureSetSize; + uint32_t OldSize = GetFeature(Idx); + if (OldSize == 0 || (Shrink && OldSize > NewSize)) { + if (OldSize > 0) { + size_t OldIdx = SmallestElementPerFeature[Idx]; + InputInfo &II = *Inputs[OldIdx]; + assert(II.NumFeatures > 0); + II.NumFeatures--; + if (II.NumFeatures == 0) + DeleteInput(OldIdx); + } else { + NumAddedFeatures++; + } + NumUpdatedFeatures++; + if (FeatureDebug) + Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize); + SmallestElementPerFeature[Idx] = Inputs.size(); + InputSizesPerFeature[Idx] = NewSize; + return true; + } + return false; + } + + void UpdateFeatureFrequency(size_t Idx) { + FeatureFrequency[Idx % kFeatureSetSize]++; + } + float GetFeatureFrequency(size_t Idx) const { + return FeatureFrequency[Idx % kFeatureSetSize]; + } + void UpdateFeatureFrequencyScore(InputInfo *II) { + const float kMin = 0.01, kMax = 100.; + II->FeatureFrequencyScore = kMin; + for (auto Idx : II->UniqFeatureSet) + II->FeatureFrequencyScore += 1. / (GetFeatureFrequency(Idx) + 1.); + II->FeatureFrequencyScore = Min(II->FeatureFrequencyScore, kMax); + } + + size_t NumFeatures() const { return NumAddedFeatures; } + size_t NumFeatureUpdates() const { return NumUpdatedFeatures; } + +private: + + static const bool FeatureDebug = false; + + size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; } + + void ValidateFeatureSet() { + if (FeatureDebug) + PrintFeatureSet(); + for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++) + if (GetFeature(Idx)) + Inputs[SmallestElementPerFeature[Idx]]->Tmp++; + for (auto II: Inputs) { + if (II->Tmp != II->NumFeatures) + Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures); + assert(II->Tmp == II->NumFeatures); + II->Tmp = 0; + } + } + + // Updates the probability distribution for the units in the corpus. + // Must be called whenever the corpus or unit weights are changed. + // + // Hypothesis: units added to the corpus last are more interesting. + // + // Hypothesis: inputs with infrequent features are more interesting. + void UpdateCorpusDistribution() { + size_t N = Inputs.size(); + assert(N); + Intervals.resize(N + 1); + Weights.resize(N); + std::iota(Intervals.begin(), Intervals.end(), 0); + for (size_t i = 0; i < N; i++) + Weights[i] = Inputs[i]->NumFeatures + ? (i + 1) * Inputs[i]->FeatureFrequencyScore + : 0.; + if (FeatureDebug) { + for (size_t i = 0; i < N; i++) + Printf("%zd ", Inputs[i]->NumFeatures); + Printf("NUM\n"); + for (size_t i = 0; i < N; i++) + Printf("%f ", Inputs[i]->FeatureFrequencyScore); + Printf("SCORE\n"); + for (size_t i = 0; i < N; i++) + Printf("%f ", Weights[i]); + Printf("Weights\n"); + } + CorpusDistribution = std::piecewise_constant_distribution<double>( + Intervals.begin(), Intervals.end(), Weights.begin()); + } + std::piecewise_constant_distribution<double> CorpusDistribution; + + Vector<double> Intervals; + Vector<double> Weights; + + std::unordered_set<std::string> Hashes; + Vector<InputInfo*> Inputs; + + size_t NumAddedFeatures = 0; + size_t NumUpdatedFeatures = 0; + uint32_t InputSizesPerFeature[kFeatureSetSize]; + uint32_t SmallestElementPerFeature[kFeatureSetSize]; + float FeatureFrequency[kFeatureSetSize]; + + std::string OutputCorpus; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_CORPUS diff --git a/lib/fuzzer/FuzzerCrossOver.cpp b/lib/fuzzer/FuzzerCrossOver.cpp new file mode 100644 index 000000000000..8b0fd7d529a8 --- /dev/null +++ b/lib/fuzzer/FuzzerCrossOver.cpp @@ -0,0 +1,52 @@ +//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Cross over test inputs. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include <cstring> + +namespace fuzzer { + +// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out. +size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize) { + assert(Size1 || Size2); + MaxOutSize = Rand(MaxOutSize) + 1; + size_t OutPos = 0; + size_t Pos1 = 0; + size_t Pos2 = 0; + size_t *InPos = &Pos1; + size_t InSize = Size1; + const uint8_t *Data = Data1; + bool CurrentlyUsingFirstData = true; + while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) { + // Merge a part of Data into Out. + size_t OutSizeLeft = MaxOutSize - OutPos; + if (*InPos < InSize) { + size_t InSizeLeft = InSize - *InPos; + size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft); + size_t ExtraSize = Rand(MaxExtraSize) + 1; + memcpy(Out + OutPos, Data + *InPos, ExtraSize); + OutPos += ExtraSize; + (*InPos) += ExtraSize; + } + // Use the other input data on the next iteration. + InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1; + InSize = CurrentlyUsingFirstData ? Size2 : Size1; + Data = CurrentlyUsingFirstData ? Data2 : Data1; + CurrentlyUsingFirstData = !CurrentlyUsingFirstData; + } + return OutPos; +} + +} // namespace fuzzer diff --git a/lib/fuzzer/FuzzerDefs.h b/lib/fuzzer/FuzzerDefs.h new file mode 100644 index 000000000000..5942efc47a4a --- /dev/null +++ b/lib/fuzzer/FuzzerDefs.h @@ -0,0 +1,167 @@ +//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Basic definitions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DEFS_H +#define LLVM_FUZZER_DEFS_H + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <string> +#include <vector> +#include <set> +#include <memory> + +// Platform detection. +#ifdef __linux__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 1 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_WINDOWS 0 +#elif __APPLE__ +#define LIBFUZZER_APPLE 1 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_WINDOWS 0 +#elif __NetBSD__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 1 +#define LIBFUZZER_WINDOWS 0 +#elif _WIN32 +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_WINDOWS 1 +#elif __Fuchsia__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 1 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_WINDOWS 0 +#else +#error "Support for your platform has not been implemented" +#endif + +#ifndef __has_attribute +# define __has_attribute(x) 0 +#endif + +#define LIBFUZZER_POSIX (LIBFUZZER_APPLE || LIBFUZZER_LINUX || LIBFUZZER_NETBSD) + +#ifdef __x86_64 +# if __has_attribute(target) +# define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt"))) +# else +# define ATTRIBUTE_TARGET_POPCNT +# endif +#else +# define ATTRIBUTE_TARGET_POPCNT +#endif + + +#ifdef __clang__ // avoid gcc warning. +# if __has_attribute(no_sanitize) +# define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +# else +# define ATTRIBUTE_NO_SANITIZE_MEMORY +# endif +# define ALWAYS_INLINE __attribute__((always_inline)) +#else +# define ATTRIBUTE_NO_SANITIZE_MEMORY +# define ALWAYS_INLINE +#endif // __clang__ + +#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) + +#if defined(__has_feature) +# if __has_feature(address_sanitizer) +# define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS +# elif __has_feature(memory_sanitizer) +# define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_MEMORY +# else +# define ATTRIBUTE_NO_SANITIZE_ALL +# endif +#else +# define ATTRIBUTE_NO_SANITIZE_ALL +#endif + +#if LIBFUZZER_WINDOWS +#define ATTRIBUTE_INTERFACE __declspec(dllexport) +#else +#define ATTRIBUTE_INTERFACE __attribute__((visibility("default"))) +#endif + +namespace fuzzer { + +template <class T> T Min(T a, T b) { return a < b ? a : b; } +template <class T> T Max(T a, T b) { return a > b ? a : b; } + +class Random; +class Dictionary; +class DictionaryEntry; +class MutationDispatcher; +struct FuzzingOptions; +class InputCorpus; +struct InputInfo; +struct ExternalFunctions; + +// Global interface to functions that may or may not be available. +extern ExternalFunctions *EF; + +// We are using a custom allocator to give a different symbol name to STL +// containers in order to avoid ODR violations. +template<typename T> + class fuzzer_allocator: public std::allocator<T> { + public: + template<class Other> + struct rebind { typedef fuzzer_allocator<Other> other; }; + }; + +template<typename T> +using Vector = std::vector<T, fuzzer_allocator<T>>; + +template<typename T> +using Set = std::set<T, std::less<T>, fuzzer_allocator<T>>; + +typedef Vector<uint8_t> Unit; +typedef Vector<Unit> UnitVector; +typedef int (*UserCallback)(const uint8_t *Data, size_t Size); + +int FuzzerDriver(int *argc, char ***argv, UserCallback Callback); + +struct ScopedDoingMyOwnMemOrStr { + ScopedDoingMyOwnMemOrStr() { DoingMyOwnMemOrStr++; } + ~ScopedDoingMyOwnMemOrStr() { DoingMyOwnMemOrStr--; } + static int DoingMyOwnMemOrStr; +}; + +inline uint8_t Bswap(uint8_t x) { return x; } +inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); } +inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); } +inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); } + +uint8_t *ExtraCountersBegin(); +uint8_t *ExtraCountersEnd(); +void ClearExtraCounters(); + +uint64_t *ClangCountersBegin(); +uint64_t *ClangCountersEnd(); +void ClearClangCounters(); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DEFS_H diff --git a/lib/fuzzer/FuzzerDictionary.h b/lib/fuzzer/FuzzerDictionary.h new file mode 100644 index 000000000000..daf7d003ea91 --- /dev/null +++ b/lib/fuzzer/FuzzerDictionary.h @@ -0,0 +1,127 @@ +//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::Dictionary +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DICTIONARY_H +#define LLVM_FUZZER_DICTIONARY_H + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" +#include "FuzzerUtil.h" +#include <algorithm> +#include <limits> + +namespace fuzzer { +// A simple POD sized array of bytes. +template <size_t kMaxSizeT> class FixedWord { +public: + static const size_t kMaxSize = kMaxSizeT; + FixedWord() {} + FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); } + + void Set(const uint8_t *B, uint8_t S) { + assert(S <= kMaxSize); + memcpy(Data, B, S); + Size = S; + } + + bool operator==(const FixedWord<kMaxSize> &w) const { + ScopedDoingMyOwnMemOrStr scoped_doing_my_own_mem_os_str; + return Size == w.Size && 0 == memcmp(Data, w.Data, Size); + } + + bool operator<(const FixedWord<kMaxSize> &w) const { + ScopedDoingMyOwnMemOrStr scoped_doing_my_own_mem_os_str; + if (Size != w.Size) + return Size < w.Size; + return memcmp(Data, w.Data, Size) < 0; + } + + static size_t GetMaxSize() { return kMaxSize; } + const uint8_t *data() const { return Data; } + uint8_t size() const { return Size; } + +private: + uint8_t Size = 0; + uint8_t Data[kMaxSize]; +}; + +typedef FixedWord<64> Word; + +class DictionaryEntry { + public: + DictionaryEntry() {} + DictionaryEntry(Word W) : W(W) {} + DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {} + const Word &GetW() const { return W; } + + bool HasPositionHint() const { return PositionHint != std::numeric_limits<size_t>::max(); } + size_t GetPositionHint() const { + assert(HasPositionHint()); + return PositionHint; + } + void IncUseCount() { UseCount++; } + void IncSuccessCount() { SuccessCount++; } + size_t GetUseCount() const { return UseCount; } + size_t GetSuccessCount() const {return SuccessCount; } + + void Print(const char *PrintAfter = "\n") { + PrintASCII(W.data(), W.size()); + if (HasPositionHint()) + Printf("@%zd", GetPositionHint()); + Printf("%s", PrintAfter); + } + +private: + Word W; + size_t PositionHint = std::numeric_limits<size_t>::max(); + size_t UseCount = 0; + size_t SuccessCount = 0; +}; + +class Dictionary { + public: + static const size_t kMaxDictSize = 1 << 14; + + bool ContainsWord(const Word &W) const { + return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) { + return DE.GetW() == W; + }); + } + const DictionaryEntry *begin() const { return &DE[0]; } + const DictionaryEntry *end() const { return begin() + Size; } + DictionaryEntry & operator[] (size_t Idx) { + assert(Idx < Size); + return DE[Idx]; + } + void push_back(DictionaryEntry DE) { + if (Size < kMaxDictSize) + this->DE[Size++] = DE; + } + void clear() { Size = 0; } + bool empty() const { return Size == 0; } + size_t size() const { return Size; } + +private: + DictionaryEntry DE[kMaxDictSize]; + size_t Size = 0; +}; + +// Parses one dictionary entry. +// If successfull, write the enty to Unit and returns true, +// otherwise returns false. +bool ParseOneDictionaryEntry(const std::string &Str, Unit *U); +// Parses the dictionary file, fills Units, returns true iff all lines +// were parsed succesfully. +bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DICTIONARY_H diff --git a/lib/fuzzer/FuzzerDriver.cpp b/lib/fuzzer/FuzzerDriver.cpp new file mode 100644 index 000000000000..f6b642daeda7 --- /dev/null +++ b/lib/fuzzer/FuzzerDriver.cpp @@ -0,0 +1,767 @@ +//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// FuzzerDriver and flag parsing. +//===----------------------------------------------------------------------===// + +#include "FuzzerCommand.h" +#include "FuzzerCorpus.h" +#include "FuzzerIO.h" +#include "FuzzerInterface.h" +#include "FuzzerInternal.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include "FuzzerShmem.h" +#include "FuzzerTracePC.h" +#include <algorithm> +#include <atomic> +#include <chrono> +#include <cstdlib> +#include <cstring> +#include <mutex> +#include <string> +#include <thread> + +// This function should be present in the libFuzzer so that the client +// binary can test for its existence. +extern "C" __attribute__((used)) void __libfuzzer_is_present() {} + +namespace fuzzer { + +// Program arguments. +struct FlagDescription { + const char *Name; + const char *Description; + int Default; + int *IntFlag; + const char **StrFlag; + unsigned int *UIntFlag; +}; + +struct { +#define FUZZER_DEPRECATED_FLAG(Name) +#define FUZZER_FLAG_INT(Name, Default, Description) int Name; +#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name; +#define FUZZER_FLAG_STRING(Name, Description) const char *Name; +#include "FuzzerFlags.def" +#undef FUZZER_DEPRECATED_FLAG +#undef FUZZER_FLAG_INT +#undef FUZZER_FLAG_UNSIGNED +#undef FUZZER_FLAG_STRING +} Flags; + +static const FlagDescription FlagDescriptions [] { +#define FUZZER_DEPRECATED_FLAG(Name) \ + {#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr}, +#define FUZZER_FLAG_INT(Name, Default, Description) \ + {#Name, Description, Default, &Flags.Name, nullptr, nullptr}, +#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \ + {#Name, Description, static_cast<int>(Default), \ + nullptr, nullptr, &Flags.Name}, +#define FUZZER_FLAG_STRING(Name, Description) \ + {#Name, Description, 0, nullptr, &Flags.Name, nullptr}, +#include "FuzzerFlags.def" +#undef FUZZER_DEPRECATED_FLAG +#undef FUZZER_FLAG_INT +#undef FUZZER_FLAG_UNSIGNED +#undef FUZZER_FLAG_STRING +}; + +static const size_t kNumFlags = + sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]); + +static Vector<std::string> *Inputs; +static std::string *ProgName; + +static void PrintHelp() { + Printf("Usage:\n"); + auto Prog = ProgName->c_str(); + Printf("\nTo run fuzzing pass 0 or more directories.\n"); + Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog); + + Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n"); + Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog); + + Printf("\nFlags: (strictly in form -flag=value)\n"); + size_t MaxFlagLen = 0; + for (size_t F = 0; F < kNumFlags; F++) + MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen); + + for (size_t F = 0; F < kNumFlags; F++) { + const auto &D = FlagDescriptions[F]; + if (strstr(D.Description, "internal flag") == D.Description) continue; + Printf(" %s", D.Name); + for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++) + Printf(" "); + Printf("\t"); + Printf("%d\t%s\n", D.Default, D.Description); + } + Printf("\nFlags starting with '--' will be ignored and " + "will be passed verbatim to subprocesses.\n"); +} + +static const char *FlagValue(const char *Param, const char *Name) { + size_t Len = strlen(Name); + if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 && + Param[Len + 1] == '=') + return &Param[Len + 2]; + return nullptr; +} + +// Avoid calling stol as it triggers a bug in clang/glibc build. +static long MyStol(const char *Str) { + long Res = 0; + long Sign = 1; + if (*Str == '-') { + Str++; + Sign = -1; + } + for (size_t i = 0; Str[i]; i++) { + char Ch = Str[i]; + if (Ch < '0' || Ch > '9') + return Res; + Res = Res * 10 + (Ch - '0'); + } + return Res * Sign; +} + +static bool ParseOneFlag(const char *Param) { + if (Param[0] != '-') return false; + if (Param[1] == '-') { + static bool PrintedWarning = false; + if (!PrintedWarning) { + PrintedWarning = true; + Printf("INFO: libFuzzer ignores flags that start with '--'\n"); + } + for (size_t F = 0; F < kNumFlags; F++) + if (FlagValue(Param + 1, FlagDescriptions[F].Name)) + Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1); + return true; + } + for (size_t F = 0; F < kNumFlags; F++) { + const char *Name = FlagDescriptions[F].Name; + const char *Str = FlagValue(Param, Name); + if (Str) { + if (FlagDescriptions[F].IntFlag) { + int Val = MyStol(Str); + *FlagDescriptions[F].IntFlag = Val; + if (Flags.verbosity >= 2) + Printf("Flag: %s %d\n", Name, Val); + return true; + } else if (FlagDescriptions[F].UIntFlag) { + unsigned int Val = std::stoul(Str); + *FlagDescriptions[F].UIntFlag = Val; + if (Flags.verbosity >= 2) + Printf("Flag: %s %u\n", Name, Val); + return true; + } else if (FlagDescriptions[F].StrFlag) { + *FlagDescriptions[F].StrFlag = Str; + if (Flags.verbosity >= 2) + Printf("Flag: %s %s\n", Name, Str); + return true; + } else { // Deprecated flag. + Printf("Flag: %s: deprecated, don't use\n", Name); + return true; + } + } + } + Printf("\n\nWARNING: unrecognized flag '%s'; " + "use -help=1 to list all flags\n\n", Param); + return true; +} + +// We don't use any library to minimize dependencies. +static void ParseFlags(const Vector<std::string> &Args) { + for (size_t F = 0; F < kNumFlags; F++) { + if (FlagDescriptions[F].IntFlag) + *FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default; + if (FlagDescriptions[F].UIntFlag) + *FlagDescriptions[F].UIntFlag = + static_cast<unsigned int>(FlagDescriptions[F].Default); + if (FlagDescriptions[F].StrFlag) + *FlagDescriptions[F].StrFlag = nullptr; + } + Inputs = new Vector<std::string>; + for (size_t A = 1; A < Args.size(); A++) { + if (ParseOneFlag(Args[A].c_str())) { + if (Flags.ignore_remaining_args) + break; + continue; + } + Inputs->push_back(Args[A]); + } +} + +static std::mutex Mu; + +static void PulseThread() { + while (true) { + SleepSeconds(600); + std::lock_guard<std::mutex> Lock(Mu); + Printf("pulse...\n"); + } +} + +static void WorkerThread(const Command &BaseCmd, std::atomic<unsigned> *Counter, + unsigned NumJobs, std::atomic<bool> *HasErrors) { + while (true) { + unsigned C = (*Counter)++; + if (C >= NumJobs) break; + std::string Log = "fuzz-" + std::to_string(C) + ".log"; + Command Cmd(BaseCmd); + Cmd.setOutputFile(Log); + Cmd.combineOutAndErr(); + if (Flags.verbosity) { + std::string CommandLine = Cmd.toString(); + Printf("%s\n", CommandLine.c_str()); + } + int ExitCode = ExecuteCommand(Cmd); + if (ExitCode != 0) + *HasErrors = true; + std::lock_guard<std::mutex> Lock(Mu); + Printf("================== Job %u exited with exit code %d ============\n", + C, ExitCode); + fuzzer::CopyFileToErr(Log); + } +} + +std::string CloneArgsWithoutX(const Vector<std::string> &Args, + const char *X1, const char *X2) { + std::string Cmd; + for (auto &S : Args) { + if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2)) + continue; + Cmd += S + " "; + } + return Cmd; +} + +static int RunInMultipleProcesses(const Vector<std::string> &Args, + unsigned NumWorkers, unsigned NumJobs) { + std::atomic<unsigned> Counter(0); + std::atomic<bool> HasErrors(false); + Command Cmd(Args); + Cmd.removeFlag("jobs"); + Cmd.removeFlag("workers"); + Vector<std::thread> V; + std::thread Pulse(PulseThread); + Pulse.detach(); + for (unsigned i = 0; i < NumWorkers; i++) + V.push_back(std::thread(WorkerThread, std::ref(Cmd), &Counter, NumJobs, &HasErrors)); + for (auto &T : V) + T.join(); + return HasErrors ? 1 : 0; +} + +static void RssThread(Fuzzer *F, size_t RssLimitMb) { + while (true) { + SleepSeconds(1); + size_t Peak = GetPeakRSSMb(); + if (Peak > RssLimitMb) + F->RssLimitCallback(); + } +} + +static void StartRssThread(Fuzzer *F, size_t RssLimitMb) { + if (!RssLimitMb) return; + std::thread T(RssThread, F, RssLimitMb); + T.detach(); +} + +int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) { + Unit U = FileToVector(InputFilePath); + if (MaxLen && MaxLen < U.size()) + U.resize(MaxLen); + F->ExecuteCallback(U.data(), U.size()); + F->TryDetectingAMemoryLeak(U.data(), U.size(), true); + return 0; +} + +static bool AllInputsAreFiles() { + if (Inputs->empty()) return false; + for (auto &Path : *Inputs) + if (!IsFile(Path)) + return false; + return true; +} + +static std::string GetDedupTokenFromFile(const std::string &Path) { + auto S = FileToString(Path); + auto Beg = S.find("DEDUP_TOKEN:"); + if (Beg == std::string::npos) + return ""; + auto End = S.find('\n', Beg); + if (End == std::string::npos) + return ""; + return S.substr(Beg, End - Beg); +} + +int CleanseCrashInput(const Vector<std::string> &Args, + const FuzzingOptions &Options) { + if (Inputs->size() != 1 || !Flags.exact_artifact_path) { + Printf("ERROR: -cleanse_crash should be given one input file and" + " -exact_artifact_path\n"); + exit(1); + } + std::string InputFilePath = Inputs->at(0); + std::string OutputFilePath = Flags.exact_artifact_path; + Command Cmd(Args); + Cmd.removeFlag("cleanse_crash"); + + assert(Cmd.hasArgument(InputFilePath)); + Cmd.removeArgument(InputFilePath); + + auto LogFilePath = DirPlusFile( + TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt"); + auto TmpFilePath = DirPlusFile( + TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".repro"); + Cmd.addArgument(TmpFilePath); + Cmd.setOutputFile(LogFilePath); + Cmd.combineOutAndErr(); + + std::string CurrentFilePath = InputFilePath; + auto U = FileToVector(CurrentFilePath); + size_t Size = U.size(); + + const Vector<uint8_t> ReplacementBytes = {' ', 0xff}; + for (int NumAttempts = 0; NumAttempts < 5; NumAttempts++) { + bool Changed = false; + for (size_t Idx = 0; Idx < Size; Idx++) { + Printf("CLEANSE[%d]: Trying to replace byte %zd of %zd\n", NumAttempts, + Idx, Size); + uint8_t OriginalByte = U[Idx]; + if (ReplacementBytes.end() != std::find(ReplacementBytes.begin(), + ReplacementBytes.end(), + OriginalByte)) + continue; + for (auto NewByte : ReplacementBytes) { + U[Idx] = NewByte; + WriteToFile(U, TmpFilePath); + auto ExitCode = ExecuteCommand(Cmd); + RemoveFile(TmpFilePath); + if (!ExitCode) { + U[Idx] = OriginalByte; + } else { + Changed = true; + Printf("CLEANSE: Replaced byte %zd with 0x%x\n", Idx, NewByte); + WriteToFile(U, OutputFilePath); + break; + } + } + } + if (!Changed) break; + } + RemoveFile(LogFilePath); + return 0; +} + +int MinimizeCrashInput(const Vector<std::string> &Args, + const FuzzingOptions &Options) { + if (Inputs->size() != 1) { + Printf("ERROR: -minimize_crash should be given one input file\n"); + exit(1); + } + std::string InputFilePath = Inputs->at(0); + Command BaseCmd(Args); + BaseCmd.removeFlag("minimize_crash"); + BaseCmd.removeFlag("exact_artifact_path"); + assert(BaseCmd.hasArgument(InputFilePath)); + BaseCmd.removeArgument(InputFilePath); + if (Flags.runs <= 0 && Flags.max_total_time == 0) { + Printf("INFO: you need to specify -runs=N or " + "-max_total_time=N with -minimize_crash=1\n" + "INFO: defaulting to -max_total_time=600\n"); + BaseCmd.addFlag("max_total_time", "600"); + } + + auto LogFilePath = DirPlusFile( + TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt"); + BaseCmd.setOutputFile(LogFilePath); + BaseCmd.combineOutAndErr(); + + std::string CurrentFilePath = InputFilePath; + while (true) { + Unit U = FileToVector(CurrentFilePath); + Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n", + CurrentFilePath.c_str(), U.size()); + + Command Cmd(BaseCmd); + Cmd.addArgument(CurrentFilePath); + + std::string CommandLine = Cmd.toString(); + Printf("CRASH_MIN: executing: %s\n", CommandLine.c_str()); + int ExitCode = ExecuteCommand(Cmd); + if (ExitCode == 0) { + Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str()); + exit(1); + } + Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize " + "it further\n", + CurrentFilePath.c_str(), U.size()); + auto DedupToken1 = GetDedupTokenFromFile(LogFilePath); + if (!DedupToken1.empty()) + Printf("CRASH_MIN: DedupToken1: %s\n", DedupToken1.c_str()); + + std::string ArtifactPath = + Flags.exact_artifact_path + ? Flags.exact_artifact_path + : Options.ArtifactPrefix + "minimized-from-" + Hash(U); + Cmd.addFlag("minimize_crash_internal_step", "1"); + Cmd.addFlag("exact_artifact_path", ArtifactPath); + CommandLine = Cmd.toString(); + Printf("CRASH_MIN: executing: %s\n", CommandLine.c_str()); + ExitCode = ExecuteCommand(Cmd); + CopyFileToErr(LogFilePath); + if (ExitCode == 0) { + if (Flags.exact_artifact_path) { + CurrentFilePath = Flags.exact_artifact_path; + WriteToFile(U, CurrentFilePath); + } + Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n", + CurrentFilePath.c_str(), U.size()); + break; + } + auto DedupToken2 = GetDedupTokenFromFile(LogFilePath); + if (!DedupToken2.empty()) + Printf("CRASH_MIN: DedupToken2: %s\n", DedupToken2.c_str()); + + if (DedupToken1 != DedupToken2) { + if (Flags.exact_artifact_path) { + CurrentFilePath = Flags.exact_artifact_path; + WriteToFile(U, CurrentFilePath); + } + Printf("CRASH_MIN: mismatch in dedup tokens" + " (looks like a different bug). Won't minimize further\n"); + break; + } + + CurrentFilePath = ArtifactPath; + Printf("*********************************\n"); + } + RemoveFile(LogFilePath); + return 0; +} + +int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { + assert(Inputs->size() == 1); + std::string InputFilePath = Inputs->at(0); + Unit U = FileToVector(InputFilePath); + Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size()); + if (U.size() < 2) { + Printf("INFO: The input is small enough, exiting\n"); + exit(0); + } + F->SetMaxInputLen(U.size()); + F->SetMaxMutationLen(U.size() - 1); + F->MinimizeCrashLoop(U); + Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n"); + exit(0); + return 0; +} + +int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict, + UnitVector& Corpus) { + Printf("Started dictionary minimization (up to %d tests)\n", + Dict.size() * Corpus.size() * 2); + + // Scores and usage count for each dictionary unit. + Vector<int> Scores(Dict.size()); + Vector<int> Usages(Dict.size()); + + Vector<size_t> InitialFeatures; + Vector<size_t> ModifiedFeatures; + for (auto &C : Corpus) { + // Get coverage for the testcase without modifications. + F->ExecuteCallback(C.data(), C.size()); + InitialFeatures.clear(); + TPC.CollectFeatures([&](size_t Feature) { + InitialFeatures.push_back(Feature); + }); + + for (size_t i = 0; i < Dict.size(); ++i) { + Vector<uint8_t> Data = C; + auto StartPos = std::search(Data.begin(), Data.end(), + Dict[i].begin(), Dict[i].end()); + // Skip dictionary unit, if the testcase does not contain it. + if (StartPos == Data.end()) + continue; + + ++Usages[i]; + while (StartPos != Data.end()) { + // Replace all occurrences of dictionary unit in the testcase. + auto EndPos = StartPos + Dict[i].size(); + for (auto It = StartPos; It != EndPos; ++It) + *It ^= 0xFF; + + StartPos = std::search(EndPos, Data.end(), + Dict[i].begin(), Dict[i].end()); + } + + // Get coverage for testcase with masked occurrences of dictionary unit. + F->ExecuteCallback(Data.data(), Data.size()); + ModifiedFeatures.clear(); + TPC.CollectFeatures([&](size_t Feature) { + ModifiedFeatures.push_back(Feature); + }); + + if (InitialFeatures == ModifiedFeatures) + --Scores[i]; + else + Scores[i] += 2; + } + } + + Printf("###### Useless dictionary elements. ######\n"); + for (size_t i = 0; i < Dict.size(); ++i) { + // Dictionary units with positive score are treated as useful ones. + if (Scores[i] > 0) + continue; + + Printf("\""); + PrintASCII(Dict[i].data(), Dict[i].size(), "\""); + Printf(" # Score: %d, Used: %d\n", Scores[i], Usages[i]); + } + Printf("###### End of useless dictionary elements. ######\n"); + return 0; +} + +int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { + using namespace fuzzer; + assert(argc && argv && "Argument pointers cannot be nullptr"); + std::string Argv0((*argv)[0]); + EF = new ExternalFunctions(); + if (EF->LLVMFuzzerInitialize) + EF->LLVMFuzzerInitialize(argc, argv); + const Vector<std::string> Args(*argv, *argv + *argc); + assert(!Args.empty()); + ProgName = new std::string(Args[0]); + if (Argv0 != *ProgName) { + Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n"); + exit(1); + } + ParseFlags(Args); + if (Flags.help) { + PrintHelp(); + return 0; + } + + if (Flags.close_fd_mask & 2) + DupAndCloseStderr(); + if (Flags.close_fd_mask & 1) + CloseStdout(); + + if (Flags.jobs > 0 && Flags.workers == 0) { + Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs); + if (Flags.workers > 1) + Printf("Running %u workers\n", Flags.workers); + } + + if (Flags.workers > 0 && Flags.jobs > 0) + return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs); + + FuzzingOptions Options; + Options.Verbosity = Flags.verbosity; + Options.MaxLen = Flags.max_len; + Options.ExperimentalLenControl = Flags.experimental_len_control; + Options.UnitTimeoutSec = Flags.timeout; + Options.ErrorExitCode = Flags.error_exitcode; + Options.TimeoutExitCode = Flags.timeout_exitcode; + Options.MaxTotalTimeSec = Flags.max_total_time; + Options.DoCrossOver = Flags.cross_over; + Options.MutateDepth = Flags.mutate_depth; + Options.ReduceDepth = Flags.reduce_depth; + Options.UseCounters = Flags.use_counters; + Options.UseMemmem = Flags.use_memmem; + Options.UseCmp = Flags.use_cmp; + Options.UseValueProfile = Flags.use_value_profile; + Options.Shrink = Flags.shrink; + Options.ReduceInputs = Flags.reduce_inputs; + Options.ShuffleAtStartUp = Flags.shuffle; + Options.PreferSmall = Flags.prefer_small; + Options.ReloadIntervalSec = Flags.reload; + Options.OnlyASCII = Flags.only_ascii; + Options.DetectLeaks = Flags.detect_leaks; + Options.PurgeAllocatorIntervalSec = Flags.purge_allocator_interval; + Options.TraceMalloc = Flags.trace_malloc; + Options.RssLimitMb = Flags.rss_limit_mb; + Options.MallocLimitMb = Flags.malloc_limit_mb; + if (!Options.MallocLimitMb) + Options.MallocLimitMb = Options.RssLimitMb; + if (Flags.runs >= 0) + Options.MaxNumberOfRuns = Flags.runs; + if (!Inputs->empty() && !Flags.minimize_crash_internal_step) + Options.OutputCorpus = (*Inputs)[0]; + Options.ReportSlowUnits = Flags.report_slow_units; + if (Flags.artifact_prefix) + Options.ArtifactPrefix = Flags.artifact_prefix; + if (Flags.exact_artifact_path) + Options.ExactArtifactPath = Flags.exact_artifact_path; + Vector<Unit> Dictionary; + if (Flags.dict) + if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary)) + return 1; + if (Flags.verbosity > 0 && !Dictionary.empty()) + Printf("Dictionary: %zd entries\n", Dictionary.size()); + bool DoPlainRun = AllInputsAreFiles(); + Options.SaveArtifacts = + !DoPlainRun || Flags.minimize_crash_internal_step; + Options.PrintNewCovPcs = Flags.print_pcs; + Options.PrintNewCovFuncs = Flags.print_funcs; + Options.PrintFinalStats = Flags.print_final_stats; + Options.PrintCorpusStats = Flags.print_corpus_stats; + Options.PrintCoverage = Flags.print_coverage; + Options.DumpCoverage = Flags.dump_coverage; + Options.UseClangCoverage = Flags.use_clang_coverage; + Options.UseFeatureFrequency = Flags.use_feature_frequency; + if (Flags.exit_on_src_pos) + Options.ExitOnSrcPos = Flags.exit_on_src_pos; + if (Flags.exit_on_item) + Options.ExitOnItem = Flags.exit_on_item; + + unsigned Seed = Flags.seed; + // Initialize Seed. + if (Seed == 0) + Seed = + std::chrono::system_clock::now().time_since_epoch().count() + GetPid(); + if (Flags.verbosity) + Printf("INFO: Seed: %u\n", Seed); + + Random Rand(Seed); + auto *MD = new MutationDispatcher(Rand, Options); + auto *Corpus = new InputCorpus(Options.OutputCorpus); + auto *F = new Fuzzer(Callback, *Corpus, *MD, Options); + + for (auto &U: Dictionary) + if (U.size() <= Word::GetMaxSize()) + MD->AddWordToManualDictionary(Word(U.data(), U.size())); + + StartRssThread(F, Flags.rss_limit_mb); + + Options.HandleAbrt = Flags.handle_abrt; + Options.HandleBus = Flags.handle_bus; + Options.HandleFpe = Flags.handle_fpe; + Options.HandleIll = Flags.handle_ill; + Options.HandleInt = Flags.handle_int; + Options.HandleSegv = Flags.handle_segv; + Options.HandleTerm = Flags.handle_term; + Options.HandleXfsz = Flags.handle_xfsz; + Options.HandleUsr1 = Flags.handle_usr1; + Options.HandleUsr2 = Flags.handle_usr2; + SetSignalHandler(Options); + + std::atexit(Fuzzer::StaticExitCallback); + + if (Flags.minimize_crash) + return MinimizeCrashInput(Args, Options); + + if (Flags.minimize_crash_internal_step) + return MinimizeCrashInputInternalStep(F, Corpus); + + if (Flags.cleanse_crash) + return CleanseCrashInput(Args, Options); + + if (auto Name = Flags.run_equivalence_server) { + SMR.Destroy(Name); + if (!SMR.Create(Name)) { + Printf("ERROR: can't create shared memory region\n"); + return 1; + } + Printf("INFO: EQUIVALENCE SERVER UP\n"); + while (true) { + SMR.WaitClient(); + size_t Size = SMR.ReadByteArraySize(); + SMR.WriteByteArray(nullptr, 0); + const Unit tmp(SMR.GetByteArray(), SMR.GetByteArray() + Size); + F->ExecuteCallback(tmp.data(), tmp.size()); + SMR.PostServer(); + } + return 0; + } + + if (auto Name = Flags.use_equivalence_server) { + if (!SMR.Open(Name)) { + Printf("ERROR: can't open shared memory region\n"); + return 1; + } + Printf("INFO: EQUIVALENCE CLIENT UP\n"); + } + + if (DoPlainRun) { + Options.SaveArtifacts = false; + int Runs = std::max(1, Flags.runs); + Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(), + Inputs->size(), Runs); + for (auto &Path : *Inputs) { + auto StartTime = system_clock::now(); + Printf("Running: %s\n", Path.c_str()); + for (int Iter = 0; Iter < Runs; Iter++) + RunOneTest(F, Path.c_str(), Options.MaxLen); + auto StopTime = system_clock::now(); + auto MS = duration_cast<milliseconds>(StopTime - StartTime).count(); + Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS); + } + Printf("***\n" + "*** NOTE: fuzzing was not performed, you have only\n" + "*** executed the target code on a fixed set of inputs.\n" + "***\n"); + F->PrintFinalStats(); + exit(0); + } + + if (Flags.merge) { + F->CrashResistantMerge(Args, *Inputs, + Flags.load_coverage_summary, + Flags.save_coverage_summary, + Flags.merge_control_file); + exit(0); + } + + if (Flags.merge_inner) { + const size_t kDefaultMaxMergeLen = 1 << 20; + if (Options.MaxLen == 0) + F->SetMaxInputLen(kDefaultMaxMergeLen); + assert(Flags.merge_control_file); + F->CrashResistantMergeInternalStep(Flags.merge_control_file); + exit(0); + } + + if (Flags.analyze_dict) { + size_t MaxLen = INT_MAX; // Large max length. + UnitVector InitialCorpus; + for (auto &Inp : *Inputs) { + Printf("Loading corpus dir: %s\n", Inp.c_str()); + ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr, + MaxLen, /*ExitOnError=*/false); + } + + if (Dictionary.empty() || Inputs->empty()) { + Printf("ERROR: can't analyze dict without dict and corpus provided\n"); + return 1; + } + if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) { + Printf("Dictionary analysis failed\n"); + exit(1); + } + Printf("Dictionary analysis suceeded\n"); + exit(0); + } + + F->Loop(*Inputs); + + if (Flags.verbosity) + Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(), + F->secondsSinceProcessStartUp()); + F->PrintFinalStats(); + + exit(0); // Don't let F destroy itself. +} + +// Storage for global ExternalFunctions object. +ExternalFunctions *EF = nullptr; + +} // namespace fuzzer diff --git a/lib/fuzzer/FuzzerExtFunctions.def b/lib/fuzzer/FuzzerExtFunctions.def new file mode 100644 index 000000000000..25a655bfd71d --- /dev/null +++ b/lib/fuzzer/FuzzerExtFunctions.def @@ -0,0 +1,47 @@ +//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This defines the external function pointers that +// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The +// EXT_FUNC macro must be defined at the point of inclusion. The signature of +// the macro is: +// +// EXT_FUNC(<name>, <return_type>, <function_signature>, <warn_if_missing>) +//===----------------------------------------------------------------------===// + +// Optional user functions +EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false); +EXT_FUNC(LLVMFuzzerCustomMutator, size_t, + (uint8_t * Data, size_t Size, size_t MaxSize, unsigned int Seed), + false); +EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t, + (const uint8_t * Data1, size_t Size1, + const uint8_t * Data2, size_t Size2, + uint8_t * Out, size_t MaxOutSize, unsigned int Seed), + false); + +// Sanitizer functions +EXT_FUNC(__lsan_enable, void, (), false); +EXT_FUNC(__lsan_disable, void, (), false); +EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false); +EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int, + (void (*malloc_hook)(const volatile void *, size_t), + void (*free_hook)(const volatile void *)), + false); +EXT_FUNC(__sanitizer_purge_allocator, void, (), false); +EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t, size_t), false); +EXT_FUNC(__sanitizer_print_stack_trace, void, (), true); +EXT_FUNC(__sanitizer_symbolize_pc, void, + (void *, const char *fmt, char *out_buf, size_t out_buf_size), false); +EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int, + (void *pc, char *module_path, + size_t module_path_len,void **pc_offset), false); +EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true); +EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false); +EXT_FUNC(__sanitizer_dump_coverage, void, (const uintptr_t *, uintptr_t), + false); diff --git a/lib/fuzzer/FuzzerExtFunctions.h b/lib/fuzzer/FuzzerExtFunctions.h new file mode 100644 index 000000000000..2672a385478d --- /dev/null +++ b/lib/fuzzer/FuzzerExtFunctions.h @@ -0,0 +1,35 @@ +//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Defines an interface to (possibly optional) functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H +#define LLVM_FUZZER_EXT_FUNCTIONS_H + +#include <stddef.h> +#include <stdint.h> + +namespace fuzzer { + +struct ExternalFunctions { + // Initialize function pointers. Functions that are not available will be set + // to nullptr. Do not call this constructor before ``main()`` has been + // entered. + ExternalFunctions(); + +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE(*NAME) FUNC_SIG = nullptr + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +}; +} // namespace fuzzer + +#endif diff --git a/lib/fuzzer/FuzzerExtFunctionsDlsym.cpp b/lib/fuzzer/FuzzerExtFunctionsDlsym.cpp new file mode 100644 index 000000000000..06bddd5de38f --- /dev/null +++ b/lib/fuzzer/FuzzerExtFunctionsDlsym.cpp @@ -0,0 +1,52 @@ +//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation for operating systems that support dlsym(). We only use it on +// Apple platforms for now. We don't use this approach on Linux because it +// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker. +// That is a complication we don't wish to expose to clients right now. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_APPLE + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include <dlfcn.h> + +using namespace fuzzer; + +template <typename T> +static T GetFnPtr(const char *FnName, bool WarnIfMissing) { + dlerror(); // Clear any previous errors. + void *Fn = dlsym(RTLD_DEFAULT, FnName); + if (Fn == nullptr) { + if (WarnIfMissing) { + const char *ErrorMsg = dlerror(); + Printf("WARNING: Failed to find function \"%s\".", FnName); + if (ErrorMsg) + Printf(" Reason %s.", ErrorMsg); + Printf("\n"); + } + } + return reinterpret_cast<T>(Fn); +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = GetFnPtr<decltype(ExternalFunctions::NAME)>(#NAME, WARN) + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +} // namespace fuzzer + +#endif // LIBFUZZER_APPLE diff --git a/lib/fuzzer/FuzzerExtFunctionsDlsymWin.cpp b/lib/fuzzer/FuzzerExtFunctionsDlsymWin.cpp new file mode 100644 index 000000000000..321b3ec5d414 --- /dev/null +++ b/lib/fuzzer/FuzzerExtFunctionsDlsymWin.cpp @@ -0,0 +1,62 @@ +//===- FuzzerExtFunctionsDlsymWin.cpp - Interface to external functions ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation using dynamic loading for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_WINDOWS + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "Windows.h" + +// This must be included after Windows.h. +#include "Psapi.h" + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { + HMODULE Modules[1024]; + DWORD BytesNeeded; + HANDLE CurrentProcess = GetCurrentProcess(); + + if (!EnumProcessModules(CurrentProcess, Modules, sizeof(Modules), + &BytesNeeded)) { + Printf("EnumProcessModules failed (error: %d).\n", GetLastError()); + exit(1); + } + + if (sizeof(Modules) < BytesNeeded) { + Printf("Error: the array is not big enough to hold all loaded modules.\n"); + exit(1); + } + + for (size_t i = 0; i < (BytesNeeded / sizeof(HMODULE)); i++) + { + FARPROC Fn; +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + if (this->NAME == nullptr) { \ + Fn = GetProcAddress(Modules[i], #NAME); \ + if (Fn == nullptr) \ + Fn = GetProcAddress(Modules[i], #NAME "__dll"); \ + this->NAME = (decltype(ExternalFunctions::NAME)) Fn; \ + } +#include "FuzzerExtFunctions.def" +#undef EXT_FUNC + } + +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + if (this->NAME == nullptr && WARN) \ + Printf("WARNING: Failed to find function \"%s\".\n", #NAME); +#include "FuzzerExtFunctions.def" +#undef EXT_FUNC +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS diff --git a/lib/fuzzer/FuzzerExtFunctionsWeak.cpp b/lib/fuzzer/FuzzerExtFunctionsWeak.cpp new file mode 100644 index 000000000000..5a90723986af --- /dev/null +++ b/lib/fuzzer/FuzzerExtFunctionsWeak.cpp @@ -0,0 +1,54 @@ +//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation for Linux. This relies on the linker's support for weak +// symbols. We don't use this approach on Apple platforms because it requires +// clients of LibFuzzer to pass ``-U _<symbol_name>`` to the linker to allow +// weak symbols to be undefined. That is a complication we don't want to expose +// to clients right now. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FUCHSIA + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" + +extern "C" { +// Declare these symbols as weak to allow them to be optionally defined. +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + __attribute__((weak)) RETURN_TYPE NAME FUNC_SIG + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +using namespace fuzzer; + +static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) { + if (FnPtr == nullptr && WarnIfMissing) { + Printf("WARNING: Failed to find function \"%s\".\n", FnName); + } +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = ::NAME; \ + CheckFnPtr(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(::NAME)), \ + #NAME, WARN); + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +} // namespace fuzzer + +#endif // LIBFUZZER_LINUX || LIBFUZZER_NETBSD diff --git a/lib/fuzzer/FuzzerExtFunctionsWeakAlias.cpp b/lib/fuzzer/FuzzerExtFunctionsWeakAlias.cpp new file mode 100644 index 000000000000..e10f7b4dcac2 --- /dev/null +++ b/lib/fuzzer/FuzzerExtFunctionsWeakAlias.cpp @@ -0,0 +1,56 @@ +//===- FuzzerExtFunctionsWeakAlias.cpp - Interface to external functions --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation using weak aliases. Works for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_WINDOWS + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" + +using namespace fuzzer; + +extern "C" { +// Declare these symbols as weak to allow them to be optionally defined. +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE NAME##Def FUNC_SIG { \ + Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \ + exit(1); \ + } \ + RETURN_TYPE NAME FUNC_SIG __attribute__((weak, alias(#NAME "Def"))); + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +template <typename T> +static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) { + if (Fun == FunDef) { + if (WarnIfMissing) + Printf("WARNING: Failed to find function \"%s\".\n", FnName); + return nullptr; + } + return Fun; +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = GetFnPtr<decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN); + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS diff --git a/lib/fuzzer/FuzzerExtraCounters.cpp b/lib/fuzzer/FuzzerExtraCounters.cpp new file mode 100644 index 000000000000..0e7a7761bf80 --- /dev/null +++ b/lib/fuzzer/FuzzerExtraCounters.cpp @@ -0,0 +1,41 @@ +//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Extra coverage counters defined by user code. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" + +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD +__attribute__((weak)) extern uint8_t __start___libfuzzer_extra_counters; +__attribute__((weak)) extern uint8_t __stop___libfuzzer_extra_counters; + +namespace fuzzer { +uint8_t *ExtraCountersBegin() { return &__start___libfuzzer_extra_counters; } +uint8_t *ExtraCountersEnd() { return &__stop___libfuzzer_extra_counters; } +ATTRIBUTE_NO_SANITIZE_ALL +void ClearExtraCounters() { // hand-written memset, don't asan-ify. + uintptr_t *Beg = reinterpret_cast<uintptr_t*>(ExtraCountersBegin()); + uintptr_t *End = reinterpret_cast<uintptr_t*>(ExtraCountersEnd()); + for (; Beg < End; Beg++) { + *Beg = 0; + __asm__ __volatile__("" : : : "memory"); + } +} + +} // namespace fuzzer + +#else +// TODO: implement for other platforms. +namespace fuzzer { +uint8_t *ExtraCountersBegin() { return nullptr; } +uint8_t *ExtraCountersEnd() { return nullptr; } +void ClearExtraCounters() {} +} // namespace fuzzer + +#endif diff --git a/lib/fuzzer/FuzzerFlags.def b/lib/fuzzer/FuzzerFlags.def new file mode 100644 index 000000000000..a32102a7da07 --- /dev/null +++ b/lib/fuzzer/FuzzerFlags.def @@ -0,0 +1,150 @@ +//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the +// point of inclusion. We are not using any flag parsing library for better +// portability and independence. +//===----------------------------------------------------------------------===// +FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.") +FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.") |