diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2014-11-06 22:49:13 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2014-11-06 22:49:13 +0000 |
commit | 8ef50bf3d1c287b5013c3168de77a462dfce3495 (patch) | |
tree | 3467f3372c1195b1546172d89af2205a50b1866d | |
parent | 11023dc647fd8f41418da90d59db138400d0f334 (diff) |
Notes
679 files changed, 36733 insertions, 7815 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index a57751ce6f61..a4424086c696 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,9 @@ include(LLVMParseArguments) # runtime libraries. cmake_minimum_required(VERSION 2.8.8) +# Top level target used to build all compiler-rt libraries. +add_custom_target(compiler-rt) + # Compute the Clang version from the LLVM version. # FIXME: We should be able to reuse CLANG_VERSION variable calculated # in Clang cmake files, instead of copying the rules here. @@ -36,21 +39,24 @@ set(CMAKE_MODULE_PATH include(AddCompilerRT) set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(COMPILER_RT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) # Setup custom SDK sysroots. set(COMPILER_RT_DARWIN_SDK_SYSROOT ${COMPILER_RT_SOURCE_DIR}/SDKs/darwin) set(COMPILER_RT_LINUX_SDK_SYSROOT ${COMPILER_RT_SOURCE_DIR}/SDKs/linux) +include(SanitizerUtils) # Detect whether the current target platform is 32-bit or 64-bit, and setup # the correct commandline flags needed to attempt to target 32-bit and 64-bit. -if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR LLVM_BUILD_32_BITS) +if (NOT CMAKE_SIZEOF_VOID_P EQUAL 4 AND + NOT CMAKE_SIZEOF_VOID_P EQUAL 8) + message(FATAL_ERROR "Please use architecture with 4 or 8 byte pointers.") +endif() +if (NOT MSVC) set(TARGET_64_BIT_CFLAGS "-m64") - set(TARGET_32_BIT_CFLAGS "") + set(TARGET_32_BIT_CFLAGS "-m32") else() - if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8) - message(FATAL_ERROR "Please use a sane architecture with 4 or 8 byte pointers.") - endif() set(TARGET_64_BIT_CFLAGS "") - set(TARGET_32_BIT_CFLAGS "-m32") + set(TARGET_32_BIT_CFLAGS "") endif() # List of architectures we can target. @@ -86,13 +92,12 @@ macro(test_target_arch arch) endmacro() if("${LLVM_NATIVE_ARCH}" STREQUAL "X86") - test_target_arch(x86_64 ${TARGET_64_BIT_CFLAGS}) + if (NOT MSVC) + test_target_arch(x86_64 ${TARGET_64_BIT_CFLAGS}) + endif() test_target_arch(i386 ${TARGET_32_BIT_CFLAGS}) elseif("${LLVM_NATIVE_ARCH}" STREQUAL "PowerPC") - # Explicitly set -m flag on powerpc, because on ppc64 defaults for gcc and - # clang are different. - test_target_arch(powerpc64 "-m64") - test_target_arch(powerpc "-m32") + test_target_arch(powerpc64 ${TARGET_64_BIT_CFLAGS}) endif() # We only support running instrumented tests when we're not cross compiling @@ -119,26 +124,43 @@ function(filter_available_targets out_var) set(${out_var} ${archs} PARENT_SCOPE) endfunction() +option(COMPILER_RT_DEBUG "Build runtimes with full debug info" OFF) + +# COMPILER_RT_DEBUG_PYBOOL is used by lit.common.configured.in. +pythonize_bool(COMPILER_RT_DEBUG) + # Provide some common commmandline flags for Sanitizer runtimes. -set(SANITIZER_COMMON_CFLAGS - -fPIC - -fno-builtin - -fno-exceptions - -fomit-frame-pointer - -funwind-tables - -fno-stack-protector - -Wno-gnu # Variadic macros with 0 arguments for ... - -O3 - ) -if(NOT WIN32) - list(APPEND SANITIZER_COMMON_CFLAGS -fvisibility=hidden) -endif() -# Build sanitizer runtimes with debug info. -check_cxx_compiler_flag(-gline-tables-only SUPPORTS_GLINE_TABLES_ONLY_FLAG) -if(SUPPORTS_GLINE_TABLES_ONLY_FLAG) - list(APPEND SANITIZER_COMMON_CFLAGS -gline-tables-only) +if (NOT MSVC) + set(SANITIZER_COMMON_CFLAGS + -fPIC + -fno-builtin + -fno-exceptions + -fomit-frame-pointer + -funwind-tables + -fno-stack-protector + -Wno-gnu # Variadic macros with 0 arguments for ... + -fvisibility=hidden + ) + if (NOT COMPILER_RT_DEBUG) + list(APPEND SANITIZER_COMMON_CFLAGS -O3) + endif() else() - list(APPEND SANITIZER_COMMON_CFLAGS -g) + set(SANITIZER_COMMON_CFLAGS + /MT + /Zi + /Oy- + /GS- + /wd4722 + ) +endif() +# Build sanitizer runtimes with debug info. (MSVC gets /Zi above) +if (NOT MSVC) + check_cxx_compiler_flag(-gline-tables-only SUPPORTS_GLINE_TABLES_ONLY_FLAG) + if(SUPPORTS_GLINE_TABLES_ONLY_FLAG AND NOT COMPILER_RT_DEBUG) + list(APPEND SANITIZER_COMMON_CFLAGS -gline-tables-only) + else() + list(APPEND SANITIZER_COMMON_CFLAGS -g) + endif() endif() # Warnings suppressions. check_cxx_compiler_flag(-Wno-variadic-macros SUPPORTS_NO_VARIADIC_MACROS_FLAG) @@ -155,30 +177,50 @@ check_cxx_compiler_flag(-Wno-non-virtual-dtor SUPPORTS_NO_NON_VIRTUAL_DTOR_FLAG) if (SUPPORTS_NO_NON_VIRTUAL_DTOR_FLAG) list(APPEND SANITIZER_COMMON_CFLAGS -Wno-non-virtual-dtor) endif() +check_cxx_compiler_flag(-Wglobal-constructors SUPPORTS_GLOBAL_CONSTRUCTORS_FLAG) +# Not all sanitizers forbid global constructors. -# Setup min Mac OS X version. if(APPLE) + # Obtain the iOS Simulator SDK path from xcodebuild. + execute_process( + COMMAND xcodebuild -version -sdk iphonesimulator Path + OUTPUT_VARIABLE IOSSIM_SDK_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set(SANITIZER_COMMON_SUPPORTED_DARWIN_OS osx) + if (IOSSIM_SDK_DIR) + list(APPEND SANITIZER_COMMON_SUPPORTED_DARWIN_OS iossim) + endif() + if(COMPILER_RT_USES_LIBCXX) set(SANITIZER_MIN_OSX_VERSION 10.7) else() - set(SANITIZER_MIN_OSX_VERSION 10.5) + set(SANITIZER_MIN_OSX_VERSION 10.6) endif() - list(APPEND SANITIZER_COMMON_CFLAGS - -mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION}) + set(DARWIN_osx_CFLAGS -mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION}) + set(DARWIN_iossim_CFLAGS + -mios-simulator-version-min=7.0 -isysroot ${IOSSIM_SDK_DIR}) + set(DARWIN_osx_LINKFLAGS) + set(DARWIN_iossim_LINKFLAGS + -Wl,-ios_simulator_version_min,7.0.0 + -mios-simulator-version-min=7.0 + -isysroot ${IOSSIM_SDK_DIR}) endif() # Architectures supported by Sanitizer runtimes. Specific sanitizers may # support only subset of these (e.g. TSan works on x86_64 only). filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH - x86_64 i386 powerpc64 powerpc) + x86_64 i386 powerpc64) -# Add the public header's directory to the includes for all of compiler-rt. -include_directories(include) add_subdirectory(include) set(SANITIZER_COMMON_LIT_TEST_DEPS clang clang-headers FileCheck count not llvm-nm llvm-symbolizer compiler-rt-headers) +# Check code style when running lit tests for sanitizers. +if(UNIX) + list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS SanitizerLintCheck) +endif() add_subdirectory(lib) diff --git a/SDKs/darwin/usr/include/errno.h b/SDKs/darwin/usr/include/errno.h new file mode 100644 index 000000000000..f06e53713398 --- /dev/null +++ b/SDKs/darwin/usr/include/errno.h @@ -0,0 +1,17 @@ +/* ===-- errno.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#include <sys/errno.h> diff --git a/SDKs/darwin/usr/include/string.h b/SDKs/darwin/usr/include/string.h index c7da1f57ba57..c6ab5d8e5876 100644 --- a/SDKs/darwin/usr/include/string.h +++ b/SDKs/darwin/usr/include/string.h @@ -28,4 +28,25 @@ char *strdup(const char *); size_t strlen(const char *); char *strncpy(char *, const char *, size_t); +/* Determine the appropriate strerror() function. */ +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) +# if defined(__i386) +# define __STRERROR_NAME "_strerror$UNIX2003" +# elif defined(__x86_64__) || defined(__arm) +# define __STRERROR_NAME "_strerror" +# else +# error "unrecognized architecture for targetting OS X" +# endif +#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) +# if defined(__i386) || defined (__x86_64) || defined(__arm) +# define __STRERROR_NAME "_strerror" +# else +# error "unrecognized architecture for targetting iOS" +# endif +#else +# error "unrecognized architecture for targetting Darwin" +#endif + +char *strerror(int) __asm(__STRERROR_NAME); + #endif /* __STRING_H__ */ diff --git a/SDKs/darwin/usr/include/sys/errno.h b/SDKs/darwin/usr/include/sys/errno.h new file mode 100644 index 000000000000..4befe385535a --- /dev/null +++ b/SDKs/darwin/usr/include/sys/errno.h @@ -0,0 +1,31 @@ +/* ===-- errno.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef _SYS_ERRNO_H_ +#define _SYS_ERRNO_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +extern int *__error(void); +#define errno (*__error()) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake index bf114a401ef0..fd117ac522ca 100644 --- a/cmake/Modules/AddCompilerRT.cmake +++ b/cmake/Modules/AddCompilerRT.cmake @@ -6,29 +6,35 @@ include(CompilerRTUtils) # with name "<name>.<arch>" if architecture can be targeted. # add_compiler_rt_object_library(<name> <arch> # SOURCES <source files> -# CFLAGS <compile flags>) +# CFLAGS <compile flags> +# DEFS <compile definitions>) macro(add_compiler_rt_object_library name arch) if(CAN_TARGET_${arch}) - parse_arguments(LIB "SOURCES;CFLAGS" "" ${ARGN}) + parse_arguments(LIB "SOURCES;CFLAGS;DEFS" "" ${ARGN}) add_library(${name}.${arch} OBJECT ${LIB_SOURCES}) set_target_compile_flags(${name}.${arch} ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) + set_property(TARGET ${name}.${arch} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) else() message(FATAL_ERROR "Archtecture ${arch} can't be targeted") endif() endmacro() -# Same as above, but adds universal osx library with name "<name>.osx" -# targeting multiple architectures. -# add_compiler_rt_osx_object_library(<name> ARCH <architectures> -# SOURCES <source files> -# CFLAGS <compile flags>) -macro(add_compiler_rt_osx_object_library name) - parse_arguments(LIB "ARCH;SOURCES;CFLAGS" "" ${ARGN}) - set(libname "${name}.osx") +# Same as above, but adds universal osx library for either OSX or iOS simulator +# with name "<name>.<os>" targeting multiple architectures. +# add_compiler_rt_darwin_object_library(<name> <os> ARCH <architectures> +# SOURCES <source files> +# CFLAGS <compile flags> +# DEFS <compile definitions>) +macro(add_compiler_rt_darwin_object_library name os) + parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS" "" ${ARGN}) + set(libname "${name}.${os}") add_library(${libname} OBJECT ${LIB_SOURCES}) - set_target_compile_flags(${libname} ${LIB_CFLAGS}) + set_target_compile_flags(${libname} ${LIB_CFLAGS} ${DARWIN_${os}_CFLAGS}) set_target_properties(${libname} PROPERTIES OSX_ARCHITECTURES "${LIB_ARCH}") + set_property(TARGET ${libname} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) endmacro() # Adds static runtime for a given architecture and puts it in the proper @@ -36,11 +42,10 @@ endmacro() # add_compiler_rt_static_runtime(<name> <arch> # SOURCES <source files> # CFLAGS <compile flags> -# DEFS <compile definitions> -# SYMS <symbols file>) +# DEFS <compile definitions>) macro(add_compiler_rt_static_runtime name arch) if(CAN_TARGET_${arch}) - parse_arguments(LIB "SOURCES;CFLAGS;DEFS;SYMS" "" ${ARGN}) + parse_arguments(LIB "SOURCES;CFLAGS;DEFS" "" ${ARGN}) add_library(${name} STATIC ${LIB_SOURCES}) # Setup compile flags and definitions. set_target_compile_flags(${name} @@ -53,13 +58,7 @@ macro(add_compiler_rt_static_runtime name arch) # Add installation command. install(TARGETS ${name} ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) - # Generate the .syms file if possible. - if(LIB_SYMS) - get_target_property(libfile ${name} LOCATION) - configure_file(${LIB_SYMS} ${libfile}.syms) - install(FILES ${libfile}.syms - DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) - endif(LIB_SYMS) + add_dependencies(compiler-rt ${name}) else() message(FATAL_ERROR "Archtecture ${arch} can't be targeted") endif() @@ -82,19 +81,22 @@ macro(add_compiler_rt_osx_static_runtime name) ARCHIVE_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) install(TARGETS ${name} ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) + add_dependencies(compiler-rt ${name}) endmacro() -# Adds dynamic runtime library on osx, which supports multiple architectures. -# add_compiler_rt_osx_dynamic_runtime(<name> ARCH <architectures> -# SOURCES <source files> -# CFLAGS <compile flags> -# DEFS <compile definitions> -# LINKFLAGS <link flags>) -macro(add_compiler_rt_osx_dynamic_runtime name) +# Adds dynamic runtime library on osx/iossim, which supports multiple +# architectures. +# add_compiler_rt_darwin_dynamic_runtime(<name> <os> +# ARCH <architectures> +# SOURCES <source files> +# CFLAGS <compile flags> +# DEFS <compile definitions> +# LINKFLAGS <link flags>) +macro(add_compiler_rt_darwin_dynamic_runtime name os) parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS;LINKFLAGS" "" ${ARGN}) add_library(${name} SHARED ${LIB_SOURCES}) - set_target_compile_flags(${name} ${LIB_CFLAGS}) - set_target_link_flags(${name} ${LIB_LINKFLAGS}) + set_target_compile_flags(${name} ${LIB_CFLAGS} ${DARWIN_${os}_CFLAGS}) + set_target_link_flags(${name} ${LIB_LINKFLAGS} ${DARWIN_${os}_LINKFLAGS}) set_property(TARGET ${name} APPEND PROPERTY COMPILE_DEFINITIONS ${LIB_DEFS}) set_target_properties(${name} PROPERTIES @@ -102,14 +104,16 @@ macro(add_compiler_rt_osx_dynamic_runtime name) LIBRARY_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) install(TARGETS ${name} LIBRARY DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) + add_dependencies(compiler-rt ${name}) endmacro() # Unittests support. set(COMPILER_RT_GTEST_PATH ${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest) -set(COMPILER_RT_GTEST_SOURCE ${COMPILER_RT_GTEST_PATH}/gtest-all.cc) +set(COMPILER_RT_GTEST_SOURCE ${COMPILER_RT_GTEST_PATH}/src/gtest-all.cc) set(COMPILER_RT_GTEST_INCLUDE_CFLAGS -DGTEST_NO_LLVM_RAW_OSTREAM=1 -I${COMPILER_RT_GTEST_PATH}/include + -I${COMPILER_RT_GTEST_PATH} ) # Use Clang to link objects into a single executable with just-built diff --git a/cmake/Modules/CompilerRTUtils.cmake b/cmake/Modules/CompilerRTUtils.cmake index f9760f40dbd5..fce37e3eb49a 100644 --- a/cmake/Modules/CompilerRTUtils.cmake +++ b/cmake/Modules/CompilerRTUtils.cmake @@ -26,3 +26,13 @@ function(find_flag_in_string flag_string flag out_var) set(${out_var} FALSE PARENT_SCOPE) endif() endfunction() + +# Set the variable var_PYBOOL to True if var holds a true-ish string, +# otherwise set it to False. +macro(pythonize_bool var) + if (${var}) + set(${var}_PYBOOL True) + else() + set(${var}_PYBOOL False) + endif() +endmacro() diff --git a/cmake/Modules/SanitizerUtils.cmake b/cmake/Modules/SanitizerUtils.cmake new file mode 100644 index 000000000000..0836edee2644 --- /dev/null +++ b/cmake/Modules/SanitizerUtils.cmake @@ -0,0 +1,42 @@ +include(LLVMParseArguments) + +set(SANITIZER_GEN_DYNAMIC_LIST + ${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/scripts/gen_dynamic_list.py) + +set(SANITIZER_LINT_SCRIPT + ${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/scripts/check_lint.sh) + +# Create a target "<name>-symbols" that would generate the list of symbols +# that need to be exported from sanitizer runtime "<name>". Function +# interceptors are exported automatically, user can also provide files with +# symbol names that should be exported as well. +# add_sanitizer_rt_symbols(<name> <files with extra symbols to export>) +macro(add_sanitizer_rt_symbols name) + get_target_property(libfile ${name} LOCATION) + set(symsfile "${libfile}.syms") + add_custom_command(OUTPUT ${symsfile} + COMMAND ${PYTHON_EXECUTABLE} + ${SANITIZER_GEN_DYNAMIC_LIST} ${libfile} ${ARGN} + > ${symsfile} + DEPENDS ${name} ${SANITIZER_GEN_DYNAMIC_LIST} ${ARGN} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating exported symbols for ${name}" + VERBATIM) + add_custom_target(${name}-symbols ALL + DEPENDS ${symsfile} + SOURCES ${SANITIZER_GEN_DYNAMIC_LIST} ${ARGN}) + install(FILES ${symsfile} DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) + add_dependencies(compiler-rt ${name}-symbols) +endmacro() + +# Add target to check code style for sanitizer runtimes. +if(UNIX) + add_custom_target(SanitizerLintCheck + COMMAND LLVM_CHECKOUT=${LLVM_MAIN_SRC_DIR} SILENT=1 TMPDIR= + PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} + ${SANITIZER_LINT_SCRIPT} + DEPENDS ${SANITIZER_LINT_SCRIPT} + COMMENT "Running lint check for sanitizer sources..." + VERBATIM) +endif() + diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 700b5326b06c..d8a73872ba44 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -1,7 +1,9 @@ set(SANITIZER_HEADERS sanitizer/asan_interface.h sanitizer/common_interface_defs.h + sanitizer/dfsan_interface.h sanitizer/linux_syscall_hooks.h + sanitizer/lsan_interface.h sanitizer/msan_interface.h) set(output_dir ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/include) @@ -32,6 +34,7 @@ foreach( f ${SANITIZER_HEADERS} ) endforeach( f ) add_custom_target(compiler-rt-headers ALL DEPENDS ${out_files}) +add_dependencies(compiler-rt compiler-rt-headers) # Install sanitizer headers. install(FILES ${SANITIZER_HEADERS} diff --git a/include/sanitizer/common_interface_defs.h b/include/sanitizer/common_interface_defs.h index 31d0dea5484b..4cc2aeae23cb 100644 --- a/include/sanitizer/common_interface_defs.h +++ b/include/sanitizer/common_interface_defs.h @@ -27,10 +27,6 @@ extern "C" { // Tell the tools to write their reports to "path.<pid>" instead of stderr. void __sanitizer_set_report_path(const char *path); - // Tell the tools to write their reports to given file descriptor instead of - // stderr. - void __sanitizer_set_report_fd(int fd); - // Notify the tools that the sandbox is going to be turned on. The reserved // parameter will be used in the future to hold a structure with functions // that the tools may call to bypass the sandbox. @@ -51,6 +47,33 @@ extern "C" { void __sanitizer_unaligned_store32(void *p, uint32_t x); void __sanitizer_unaligned_store64(void *p, uint64_t x); + // Record and dump coverage info. + void __sanitizer_cov_dump(); + + // Annotate the current state of a contiguous container, such as + // std::vector, std::string or similar. + // A contiguous container is a container that keeps all of its elements + // in a contiguous region of memory. The container owns the region of memory + // [beg, end); the memory [beg, mid) is used to store the current elements + // and the memory [mid, end) is reserved for future elements; + // end <= mid <= end. For example, in "std::vector<> v" + // beg = &v[0]; + // end = beg + v.capacity() * sizeof(v[0]); + // mid = beg + v.size() * sizeof(v[0]); + // + // This annotation tells the Sanitizer tool about the current state of the + // container so that the tool can report errors when memory from [mid, end) + // is accessed. Insert this annotation into methods like push_back/pop_back. + // Supply the old and the new values of mid (old_mid/new_mid). + // In the initial state mid == end and so should be the final + // state when the container is destroyed or when it reallocates the storage. + // + // Use with caution and don't use for anything other than vector-like classes. + // + // For AddressSanitizer, 'beg' should be 8-aligned. + void __sanitizer_annotate_contiguous_container(void *beg, void *end, + void *old_mid, void *new_mid); + #ifdef __cplusplus } // extern "C" #endif diff --git a/include/sanitizer/dfsan_interface.h b/include/sanitizer/dfsan_interface.h new file mode 100644 index 000000000000..f14d45a2b2a1 --- /dev/null +++ b/include/sanitizer/dfsan_interface.h @@ -0,0 +1,87 @@ +//===-- dfsan_interface.h -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of DataFlowSanitizer. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef DFSAN_INTERFACE_H +#define DFSAN_INTERFACE_H + +#include <stddef.h> +#include <stdint.h> +#include <sanitizer/common_interface_defs.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint16_t dfsan_label; + +/// Stores information associated with a specific label identifier. A label +/// may be a base label created using dfsan_create_label, with associated +/// text description and user data, or an automatically created union label, +/// which represents the union of two label identifiers (which may themselves +/// be base or union labels). +struct dfsan_label_info { + // Fields for union labels, set to 0 for base labels. + dfsan_label l1; + dfsan_label l2; + + // Fields for base labels. + const char *desc; + void *userdata; +}; + +/// Computes the union of \c l1 and \c l2, possibly creating a union label in +/// the process. +dfsan_label dfsan_union(dfsan_label l1, dfsan_label l2); + +/// Creates and returns a base label with the given description and user data. +dfsan_label dfsan_create_label(const char *desc, void *userdata); + +/// Sets the label for each address in [addr,addr+size) to \c label. +void dfsan_set_label(dfsan_label label, void *addr, size_t size); + +/// Sets the label for each address in [addr,addr+size) to the union of the +/// current label for that address and \c label. +void dfsan_add_label(dfsan_label label, void *addr, size_t size); + +/// Retrieves the label associated with the given data. +/// +/// The type of 'data' is arbitrary. The function accepts a value of any type, +/// which can be truncated or extended (implicitly or explicitly) as necessary. +/// The truncation/extension operations will preserve the label of the original +/// value. +dfsan_label dfsan_get_label(long data); + +/// Retrieves the label associated with the data at the given address. +dfsan_label dfsan_read_label(const void *addr, size_t size); + +/// Retrieves a pointer to the dfsan_label_info struct for the given label. +const struct dfsan_label_info *dfsan_get_label_info(dfsan_label label); + +/// Returns whether the given label label contains the label elem. +int dfsan_has_label(dfsan_label label, dfsan_label elem); + +/// If the given label label contains a label with the description desc, returns +/// that label, else returns 0. +dfsan_label dfsan_has_label_with_desc(dfsan_label label, const char *desc); + +#ifdef __cplusplus +} // extern "C" + +template <typename T> +void dfsan_set_label(dfsan_label label, T &data) { // NOLINT + dfsan_set_label(label, (void *)&data, sizeof(T)); +} + +#endif + +#endif // DFSAN_INTERFACE_H diff --git a/include/sanitizer/linux_syscall_hooks.h b/include/sanitizer/linux_syscall_hooks.h index 894d5c2bebff..89867c15190a 100644 --- a/include/sanitizer/linux_syscall_hooks.h +++ b/include/sanitizer/linux_syscall_hooks.h @@ -15,788 +15,3056 @@ // actions for the active sanitizer. // Usage: // __sanitizer_syscall_pre_getfoo(...args...); -// int res = syscall(__NR_getfoo, ...args...); +// long res = syscall(__NR_getfoo, ...args...); // __sanitizer_syscall_post_getfoo(res, ...args...); //===----------------------------------------------------------------------===// #ifndef SANITIZER_LINUX_SYSCALL_HOOKS_H #define SANITIZER_LINUX_SYSCALL_HOOKS_H -#ifdef __cplusplus -extern "C" { -#endif +#define __sanitizer_syscall_pre_time(tloc) \ + __sanitizer_syscall_pre_impl_time((long)(tloc)) +#define __sanitizer_syscall_post_time(res, tloc) \ + __sanitizer_syscall_post_impl_time(res, (long)(tloc)) +#define __sanitizer_syscall_pre_stime(tptr) \ + __sanitizer_syscall_pre_impl_stime((long)(tptr)) +#define __sanitizer_syscall_post_stime(res, tptr) \ + __sanitizer_syscall_post_impl_stime(res, (long)(tptr)) +#define __sanitizer_syscall_pre_gettimeofday(tv, tz) \ + __sanitizer_syscall_pre_impl_gettimeofday((long)(tv), (long)(tz)) +#define __sanitizer_syscall_post_gettimeofday(res, tv, tz) \ + __sanitizer_syscall_post_impl_gettimeofday(res, (long)(tv), (long)(tz)) +#define __sanitizer_syscall_pre_settimeofday(tv, tz) \ + __sanitizer_syscall_pre_impl_settimeofday((long)(tv), (long)(tz)) +#define __sanitizer_syscall_post_settimeofday(res, tv, tz) \ + __sanitizer_syscall_post_impl_settimeofday(res, (long)(tv), (long)(tz)) +#define __sanitizer_syscall_pre_adjtimex(txc_p) \ + __sanitizer_syscall_pre_impl_adjtimex((long)(txc_p)) +#define __sanitizer_syscall_post_adjtimex(res, txc_p) \ + __sanitizer_syscall_post_impl_adjtimex(res, (long)(txc_p)) +#define __sanitizer_syscall_pre_times(tbuf) \ + __sanitizer_syscall_pre_impl_times((long)(tbuf)) +#define __sanitizer_syscall_post_times(res, tbuf) \ + __sanitizer_syscall_post_impl_times(res, (long)(tbuf)) +#define __sanitizer_syscall_pre_gettid() __sanitizer_syscall_pre_impl_gettid() +#define __sanitizer_syscall_post_gettid(res) \ + __sanitizer_syscall_post_impl_gettid(res) +#define __sanitizer_syscall_pre_nanosleep(rqtp, rmtp) \ + __sanitizer_syscall_pre_impl_nanosleep((long)(rqtp), (long)(rmtp)) +#define __sanitizer_syscall_post_nanosleep(res, rqtp, rmtp) \ + __sanitizer_syscall_post_impl_nanosleep(res, (long)(rqtp), (long)(rmtp)) +#define __sanitizer_syscall_pre_alarm(seconds) \ + __sanitizer_syscall_pre_impl_alarm((long)(seconds)) +#define __sanitizer_syscall_post_alarm(res, seconds) \ + __sanitizer_syscall_post_impl_alarm(res, (long)(seconds)) +#define __sanitizer_syscall_pre_getpid() __sanitizer_syscall_pre_impl_getpid() +#define __sanitizer_syscall_post_getpid(res) \ + __sanitizer_syscall_post_impl_getpid(res) +#define __sanitizer_syscall_pre_getppid() __sanitizer_syscall_pre_impl_getppid() +#define __sanitizer_syscall_post_getppid(res) \ + __sanitizer_syscall_post_impl_getppid(res) +#define __sanitizer_syscall_pre_getuid() __sanitizer_syscall_pre_impl_getuid() +#define __sanitizer_syscall_post_getuid(res) \ + __sanitizer_syscall_post_impl_getuid(res) +#define __sanitizer_syscall_pre_geteuid() __sanitizer_syscall_pre_impl_geteuid() +#define __sanitizer_syscall_post_geteuid(res) \ + __sanitizer_syscall_post_impl_geteuid(res) +#define __sanitizer_syscall_pre_getgid() __sanitizer_syscall_pre_impl_getgid() +#define __sanitizer_syscall_post_getgid(res) \ + __sanitizer_syscall_post_impl_getgid(res) +#define __sanitizer_syscall_pre_getegid() __sanitizer_syscall_pre_impl_getegid() +#define __sanitizer_syscall_post_getegid(res) \ + __sanitizer_syscall_post_impl_getegid(res) +#define __sanitizer_syscall_pre_getresuid(ruid, euid, suid) \ + __sanitizer_syscall_pre_impl_getresuid((long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_post_getresuid(res, ruid, euid, suid) \ + __sanitizer_syscall_post_impl_getresuid(res, (long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_pre_getresgid(rgid, egid, sgid) \ + __sanitizer_syscall_pre_impl_getresgid((long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_post_getresgid(res, rgid, egid, sgid) \ + __sanitizer_syscall_post_impl_getresgid(res, (long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_pre_getpgid(pid) \ + __sanitizer_syscall_pre_impl_getpgid((long)(pid)) +#define __sanitizer_syscall_post_getpgid(res, pid) \ + __sanitizer_syscall_post_impl_getpgid(res, (long)(pid)) +#define __sanitizer_syscall_pre_getpgrp() __sanitizer_syscall_pre_impl_getpgrp() +#define __sanitizer_syscall_post_getpgrp(res) \ + __sanitizer_syscall_post_impl_getpgrp(res) +#define __sanitizer_syscall_pre_getsid(pid) \ + __sanitizer_syscall_pre_impl_getsid((long)(pid)) +#define __sanitizer_syscall_post_getsid(res, pid) \ + __sanitizer_syscall_post_impl_getsid(res, (long)(pid)) +#define __sanitizer_syscall_pre_getgroups(gidsetsize, grouplist) \ + __sanitizer_syscall_pre_impl_getgroups((long)(gidsetsize), (long)(grouplist)) +#define __sanitizer_syscall_post_getgroups(res, gidsetsize, grouplist) \ + __sanitizer_syscall_post_impl_getgroups(res, (long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_pre_setregid(rgid, egid) \ + __sanitizer_syscall_pre_impl_setregid((long)(rgid), (long)(egid)) +#define __sanitizer_syscall_post_setregid(res, rgid, egid) \ + __sanitizer_syscall_post_impl_setregid(res, (long)(rgid), (long)(egid)) +#define __sanitizer_syscall_pre_setgid(gid) \ + __sanitizer_syscall_pre_impl_setgid((long)(gid)) +#define __sanitizer_syscall_post_setgid(res, gid) \ + __sanitizer_syscall_post_impl_setgid(res, (long)(gid)) +#define __sanitizer_syscall_pre_setreuid(ruid, euid) \ + __sanitizer_syscall_pre_impl_setreuid((long)(ruid), (long)(euid)) +#define __sanitizer_syscall_post_setreuid(res, ruid, euid) \ + __sanitizer_syscall_post_impl_setreuid(res, (long)(ruid), (long)(euid)) +#define __sanitizer_syscall_pre_setuid(uid) \ + __sanitizer_syscall_pre_impl_setuid((long)(uid)) +#define __sanitizer_syscall_post_setuid(res, uid) \ + __sanitizer_syscall_post_impl_setuid(res, (long)(uid)) +#define __sanitizer_syscall_pre_setresuid(ruid, euid, suid) \ + __sanitizer_syscall_pre_impl_setresuid((long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_post_setresuid(res, ruid, euid, suid) \ + __sanitizer_syscall_post_impl_setresuid(res, (long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_pre_setresgid(rgid, egid, sgid) \ + __sanitizer_syscall_pre_impl_setresgid((long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_post_setresgid(res, rgid, egid, sgid) \ + __sanitizer_syscall_post_impl_setresgid(res, (long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_pre_setfsuid(uid) \ + __sanitizer_syscall_pre_impl_setfsuid((long)(uid)) +#define __sanitizer_syscall_post_setfsuid(res, uid) \ + __sanitizer_syscall_post_impl_setfsuid(res, (long)(uid)) +#define __sanitizer_syscall_pre_setfsgid(gid) \ + __sanitizer_syscall_pre_impl_setfsgid((long)(gid)) +#define __sanitizer_syscall_post_setfsgid(res, gid) \ + __sanitizer_syscall_post_impl_setfsgid(res, (long)(gid)) +#define __sanitizer_syscall_pre_setpgid(pid, pgid) \ + __sanitizer_syscall_pre_impl_setpgid((long)(pid), (long)(pgid)) +#define __sanitizer_syscall_post_setpgid(res, pid, pgid) \ + __sanitizer_syscall_post_impl_setpgid(res, (long)(pid), (long)(pgid)) +#define __sanitizer_syscall_pre_setsid() __sanitizer_syscall_pre_impl_setsid() +#define __sanitizer_syscall_post_setsid(res) \ + __sanitizer_syscall_post_impl_setsid(res) +#define __sanitizer_syscall_pre_setgroups(gidsetsize, grouplist) \ + __sanitizer_syscall_pre_impl_setgroups((long)(gidsetsize), (long)(grouplist)) +#define __sanitizer_syscall_post_setgroups(res, gidsetsize, grouplist) \ + __sanitizer_syscall_post_impl_setgroups(res, (long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_pre_acct(name) \ + __sanitizer_syscall_pre_impl_acct((long)(name)) +#define __sanitizer_syscall_post_acct(res, name) \ + __sanitizer_syscall_post_impl_acct(res, (long)(name)) +#define __sanitizer_syscall_pre_capget(header, dataptr) \ + __sanitizer_syscall_pre_impl_capget((long)(header), (long)(dataptr)) +#define __sanitizer_syscall_post_capget(res, header, dataptr) \ + __sanitizer_syscall_post_impl_capget(res, (long)(header), (long)(dataptr)) +#define __sanitizer_syscall_pre_capset(header, data) \ + __sanitizer_syscall_pre_impl_capset((long)(header), (long)(data)) +#define __sanitizer_syscall_post_capset(res, header, data) \ + __sanitizer_syscall_post_impl_capset(res, (long)(header), (long)(data)) +#define __sanitizer_syscall_pre_personality(personality) \ + __sanitizer_syscall_pre_impl_personality((long)(personality)) +#define __sanitizer_syscall_post_personality(res, personality) \ + __sanitizer_syscall_post_impl_personality(res, (long)(personality)) +#define __sanitizer_syscall_pre_sigpending(set) \ + __sanitizer_syscall_pre_impl_sigpending((long)(set)) +#define __sanitizer_syscall_post_sigpending(res, set) \ + __sanitizer_syscall_post_impl_sigpending(res, (long)(set)) +#define __sanitizer_syscall_pre_sigprocmask(how, set, oset) \ + __sanitizer_syscall_pre_impl_sigprocmask((long)(how), (long)(set), \ + (long)(oset)) +#define __sanitizer_syscall_post_sigprocmask(res, how, set, oset) \ + __sanitizer_syscall_post_impl_sigprocmask(res, (long)(how), (long)(set), \ + (long)(oset)) +#define __sanitizer_syscall_pre_getitimer(which, value) \ + __sanitizer_syscall_pre_impl_getitimer((long)(which), (long)(value)) +#define __sanitizer_syscall_post_getitimer(res, which, value) \ + __sanitizer_syscall_post_impl_getitimer(res, (long)(which), (long)(value)) +#define __sanitizer_syscall_pre_setitimer(which, value, ovalue) \ + __sanitizer_syscall_pre_impl_setitimer((long)(which), (long)(value), \ + (long)(ovalue)) +#define __sanitizer_syscall_post_setitimer(res, which, value, ovalue) \ + __sanitizer_syscall_post_impl_setitimer(res, (long)(which), (long)(value), \ + (long)(ovalue)) +#define __sanitizer_syscall_pre_timer_create(which_clock, timer_event_spec, \ + created_timer_id) \ + __sanitizer_syscall_pre_impl_timer_create( \ + (long)(which_clock), (long)(timer_event_spec), (long)(created_timer_id)) +#define __sanitizer_syscall_post_timer_create( \ + res, which_clock, timer_event_spec, created_timer_id) \ + __sanitizer_syscall_post_impl_timer_create(res, (long)(which_clock), \ + (long)(timer_event_spec), \ + (long)(created_timer_id)) +#define __sanitizer_syscall_pre_timer_gettime(timer_id, setting) \ + __sanitizer_syscall_pre_impl_timer_gettime((long)(timer_id), (long)(setting)) +#define __sanitizer_syscall_post_timer_gettime(res, timer_id, setting) \ + __sanitizer_syscall_post_impl_timer_gettime(res, (long)(timer_id), \ + (long)(setting)) +#define __sanitizer_syscall_pre_timer_getoverrun(timer_id) \ + __sanitizer_syscall_pre_impl_timer_getoverrun((long)(timer_id)) +#define __sanitizer_syscall_post_timer_getoverrun(res, timer_id) \ + __sanitizer_syscall_post_impl_timer_getoverrun(res, (long)(timer_id)) +#define __sanitizer_syscall_pre_timer_settime(timer_id, flags, new_setting, \ + old_setting) \ + __sanitizer_syscall_pre_impl_timer_settime((long)(timer_id), (long)(flags), \ + (long)(new_setting), \ + (long)(old_setting)) +#define __sanitizer_syscall_post_timer_settime(res, timer_id, flags, \ + new_setting, old_setting) \ + __sanitizer_syscall_post_impl_timer_settime( \ + res, (long)(timer_id), (long)(flags), (long)(new_setting), \ + (long)(old_setting)) +#define __sanitizer_syscall_pre_timer_delete(timer_id) \ + __sanitizer_syscall_pre_impl_timer_delete((long)(timer_id)) +#define __sanitizer_syscall_post_timer_delete(res, timer_id) \ + __sanitizer_syscall_post_impl_timer_delete(res, (long)(timer_id)) +#define __sanitizer_syscall_pre_clock_settime(which_clock, tp) \ + __sanitizer_syscall_pre_impl_clock_settime((long)(which_clock), (long)(tp)) +#define __sanitizer_syscall_post_clock_settime(res, which_clock, tp) \ + __sanitizer_syscall_post_impl_clock_settime(res, (long)(which_clock), \ + (long)(tp)) +#define __sanitizer_syscall_pre_clock_gettime(which_clock, tp) \ + __sanitizer_syscall_pre_impl_clock_gettime((long)(which_clock), (long)(tp)) +#define __sanitizer_syscall_post_clock_gettime(res, which_clock, tp) \ + __sanitizer_syscall_post_impl_clock_gettime(res, (long)(which_clock), \ + (long)(tp)) +#define __sanitizer_syscall_pre_clock_adjtime(which_clock, tx) \ + __sanitizer_syscall_pre_impl_clock_adjtime((long)(which_clock), (long)(tx)) +#define __sanitizer_syscall_post_clock_adjtime(res, which_clock, tx) \ + __sanitizer_syscall_post_impl_clock_adjtime(res, (long)(which_clock), \ + (long)(tx)) +#define __sanitizer_syscall_pre_clock_getres(which_clock, tp) \ + __sanitizer_syscall_pre_impl_clock_getres((long)(which_clock), (long)(tp)) +#define __sanitizer_syscall_post_clock_getres(res, which_clock, tp) \ + __sanitizer_syscall_post_impl_clock_getres(res, (long)(which_clock), \ + (long)(tp)) +#define __sanitizer_syscall_pre_clock_nanosleep(which_clock, flags, rqtp, \ + rmtp) \ + __sanitizer_syscall_pre_impl_clock_nanosleep( \ + (long)(which_clock), (long)(flags), (long)(rqtp), (long)(rmtp)) +#define __sanitizer_syscall_post_clock_nanosleep(res, which_clock, flags, \ + rqtp, rmtp) \ + __sanitizer_syscall_post_impl_clock_nanosleep( \ + res, (long)(which_clock), (long)(flags), (long)(rqtp), (long)(rmtp)) +#define __sanitizer_syscall_pre_nice(increment) \ + __sanitizer_syscall_pre_impl_nice((long)(increment)) +#define __sanitizer_syscall_post_nice(res, increment) \ + __sanitizer_syscall_post_impl_nice(res, (long)(increment)) +#define __sanitizer_syscall_pre_sched_setscheduler(pid, policy, param) \ + __sanitizer_syscall_pre_impl_sched_setscheduler((long)(pid), (long)(policy), \ + (long)(param)) +#define __sanitizer_syscall_post_sched_setscheduler(res, pid, policy, param) \ + __sanitizer_syscall_post_impl_sched_setscheduler( \ + res, (long)(pid), (long)(policy), (long)(param)) +#define __sanitizer_syscall_pre_sched_setparam(pid, param) \ + __sanitizer_syscall_pre_impl_sched_setparam((long)(pid), (long)(param)) +#define __sanitizer_syscall_post_sched_setparam(res, pid, param) \ + __sanitizer_syscall_post_impl_sched_setparam(res, (long)(pid), (long)(param)) +#define __sanitizer_syscall_pre_sched_getscheduler(pid) \ + __sanitizer_syscall_pre_impl_sched_getscheduler((long)(pid)) +#define __sanitizer_syscall_post_sched_getscheduler(res, pid) \ + __sanitizer_syscall_post_impl_sched_getscheduler(res, (long)(pid)) +#define __sanitizer_syscall_pre_sched_getparam(pid, param) \ + __sanitizer_syscall_pre_impl_sched_getparam((long)(pid), (long)(param)) +#define __sanitizer_syscall_post_sched_getparam(res, pid, param) \ + __sanitizer_syscall_post_impl_sched_getparam(res, (long)(pid), (long)(param)) +#define __sanitizer_syscall_pre_sched_setaffinity(pid, len, user_mask_ptr) \ + __sanitizer_syscall_pre_impl_sched_setaffinity((long)(pid), (long)(len), \ + (long)(user_mask_ptr)) +#define __sanitizer_syscall_post_sched_setaffinity(res, pid, len, \ + user_mask_ptr) \ + __sanitizer_syscall_post_impl_sched_setaffinity( \ + res, (long)(pid), (long)(len), (long)(user_mask_ptr)) +#define __sanitizer_syscall_pre_sched_getaffinity(pid, len, user_mask_ptr) \ + __sanitizer_syscall_pre_impl_sched_getaffinity((long)(pid), (long)(len), \ + (long)(user_mask_ptr)) +#define __sanitizer_syscall_post_sched_getaffinity(res, pid, len, \ + user_mask_ptr) \ + __sanitizer_syscall_post_impl_sched_getaffinity( \ + res, (long)(pid), (long)(len), (long)(user_mask_ptr)) +#define __sanitizer_syscall_pre_sched_yield() \ + __sanitizer_syscall_pre_impl_sched_yield() +#define __sanitizer_syscall_post_sched_yield(res) \ + __sanitizer_syscall_post_impl_sched_yield(res) +#define __sanitizer_syscall_pre_sched_get_priority_max(policy) \ + __sanitizer_syscall_pre_impl_sched_get_priority_max((long)(policy)) +#define __sanitizer_syscall_post_sched_get_priority_max(res, policy) \ + __sanitizer_syscall_post_impl_sched_get_priority_max(res, (long)(policy)) +#define __sanitizer_syscall_pre_sched_get_priority_min(policy) \ + __sanitizer_syscall_pre_impl_sched_get_priority_min((long)(policy)) +#define __sanitizer_syscall_post_sched_get_priority_min(res, policy) \ + __sanitizer_syscall_post_impl_sched_get_priority_min(res, (long)(policy)) +#define __sanitizer_syscall_pre_sched_rr_get_interval(pid, interval) \ + __sanitizer_syscall_pre_impl_sched_rr_get_interval((long)(pid), \ + (long)(interval)) +#define __sanitizer_syscall_post_sched_rr_get_interval(res, pid, interval) \ + __sanitizer_syscall_post_impl_sched_rr_get_interval(res, (long)(pid), \ + (long)(interval)) +#define __sanitizer_syscall_pre_setpriority(which, who, niceval) \ + __sanitizer_syscall_pre_impl_setpriority((long)(which), (long)(who), \ + (long)(niceval)) +#define __sanitizer_syscall_post_setpriority(res, which, who, niceval) \ + __sanitizer_syscall_post_impl_setpriority(res, (long)(which), (long)(who), \ + (long)(niceval)) +#define __sanitizer_syscall_pre_getpriority(which, who) \ + __sanitizer_syscall_pre_impl_getpriority((long)(which), (long)(who)) +#define __sanitizer_syscall_post_getpriority(res, which, who) \ + __sanitizer_syscall_post_impl_getpriority(res, (long)(which), (long)(who)) +#define __sanitizer_syscall_pre_shutdown(arg0, arg1) \ + __sanitizer_syscall_pre_impl_shutdown((long)(arg0), (long)(arg1)) +#define __sanitizer_syscall_post_shutdown(res, arg0, arg1) \ + __sanitizer_syscall_post_impl_shutdown(res, (long)(arg0), (long)(arg1)) +#define __sanitizer_syscall_pre_reboot(magic1, magic2, cmd, arg) \ + __sanitizer_syscall_pre_impl_reboot((long)(magic1), (long)(magic2), \ + (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_reboot(res, magic1, magic2, cmd, arg) \ + __sanitizer_syscall_post_impl_reboot(res, (long)(magic1), (long)(magic2), \ + (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_pre_restart_syscall() \ + __sanitizer_syscall_pre_impl_restart_syscall() +#define __sanitizer_syscall_post_restart_syscall(res) \ + __sanitizer_syscall_post_impl_restart_syscall(res) +#define __sanitizer_syscall_pre_kexec_load(entry, nr_segments, segments, \ + flags) \ + __sanitizer_syscall_pre_impl_kexec_load((long)(entry), (long)(nr_segments), \ + (long)(segments), (long)(flags)) +#define __sanitizer_syscall_post_kexec_load(res, entry, nr_segments, segments, \ + flags) \ + __sanitizer_syscall_post_impl_kexec_load(res, (long)(entry), \ + (long)(nr_segments), \ + (long)(segments), (long)(flags)) +#define __sanitizer_syscall_pre_exit(error_code) \ + __sanitizer_syscall_pre_impl_exit((long)(error_code)) +#define __sanitizer_syscall_post_exit(res, error_code) \ + __sanitizer_syscall_post_impl_exit(res, (long)(error_code)) +#define __sanitizer_syscall_pre_exit_group(error_code) \ + __sanitizer_syscall_pre_impl_exit_group((long)(error_code)) +#define __sanitizer_syscall_post_exit_group(res, error_code) \ + __sanitizer_syscall_post_impl_exit_group(res, (long)(error_code)) +#define __sanitizer_syscall_pre_wait4(pid, stat_addr, options, ru) \ + __sanitizer_syscall_pre_impl_wait4((long)(pid), (long)(stat_addr), \ + (long)(options), (long)(ru)) +#define __sanitizer_syscall_post_wait4(res, pid, stat_addr, options, ru) \ + __sanitizer_syscall_post_impl_wait4(res, (long)(pid), (long)(stat_addr), \ + (long)(options), (long)(ru)) +#define __sanitizer_syscall_pre_waitid(which, pid, infop, options, ru) \ + __sanitizer_syscall_pre_impl_waitid( \ + (long)(which), (long)(pid), (long)(infop), (long)(options), (long)(ru)) +#define __sanitizer_syscall_post_waitid(res, which, pid, infop, options, ru) \ + __sanitizer_syscall_post_impl_waitid(res, (long)(which), (long)(pid), \ + (long)(infop), (long)(options), \ + (long)(ru)) +#define __sanitizer_syscall_pre_waitpid(pid, stat_addr, options) \ + __sanitizer_syscall_pre_impl_waitpid((long)(pid), (long)(stat_addr), \ + (long)(options)) +#define __sanitizer_syscall_post_waitpid(res, pid, stat_addr, options) \ + __sanitizer_syscall_post_impl_waitpid(res, (long)(pid), (long)(stat_addr), \ + (long)(options)) +#define __sanitizer_syscall_pre_set_tid_address(tidptr) \ + __sanitizer_syscall_pre_impl_set_tid_address((long)(tidptr)) +#define __sanitizer_syscall_post_set_tid_address(res, tidptr) \ + __sanitizer_syscall_post_impl_set_tid_address(res, (long)(tidptr)) +#define __sanitizer_syscall_pre_init_module(umod, len, uargs) \ + __sanitizer_syscall_pre_impl_init_module((long)(umod), (long)(len), \ + (long)(uargs)) +#define __sanitizer_syscall_post_init_module(res, umod, len, uargs) \ + __sanitizer_syscall_post_impl_init_module(res, (long)(umod), (long)(len), \ + (long)(uargs)) +#define __sanitizer_syscall_pre_delete_module(name_user, flags) \ + __sanitizer_syscall_pre_impl_delete_module((long)(name_user), (long)(flags)) +#define __sanitizer_syscall_post_delete_module(res, name_user, flags) \ + __sanitizer_syscall_post_impl_delete_module(res, (long)(name_user), \ + (long)(flags)) +#define __sanitizer_syscall_pre_rt_sigprocmask(how, set, oset, sigsetsize) \ + __sanitizer_syscall_pre_impl_rt_sigprocmask( \ + (long)(how), (long)(set), (long)(oset), (long)(sigsetsize)) +#define __sanitizer_syscall_post_rt_sigprocmask(res, how, set, oset, \ + sigsetsize) \ + __sanitizer_syscall_post_impl_rt_sigprocmask( \ + res, (long)(how), (long)(set), (long)(oset), (long)(sigsetsize)) +#define __sanitizer_syscall_pre_rt_sigpending(set, sigsetsize) \ + __sanitizer_syscall_pre_impl_rt_sigpending((long)(set), (long)(sigsetsize)) +#define __sanitizer_syscall_post_rt_sigpending(res, set, sigsetsize) \ + __sanitizer_syscall_post_impl_rt_sigpending(res, (long)(set), \ + (long)(sigsetsize)) +#define __sanitizer_syscall_pre_rt_sigtimedwait(uthese, uinfo, uts, \ + sigsetsize) \ + __sanitizer_syscall_pre_impl_rt_sigtimedwait( \ + (long)(uthese), (long)(uinfo), (long)(uts), (long)(sigsetsize)) +#define __sanitizer_syscall_post_rt_sigtimedwait(res, uthese, uinfo, uts, \ + sigsetsize) \ + __sanitizer_syscall_post_impl_rt_sigtimedwait( \ + res, (long)(uthese), (long)(uinfo), (long)(uts), (long)(sigsetsize)) +#define __sanitizer_syscall_pre_rt_tgsigqueueinfo(tgid, pid, sig, uinfo) \ + __sanitizer_syscall_pre_impl_rt_tgsigqueueinfo((long)(tgid), (long)(pid), \ + (long)(sig), (long)(uinfo)) +#define __sanitizer_syscall_post_rt_tgsigqueueinfo(res, tgid, pid, sig, uinfo) \ + __sanitizer_syscall_post_impl_rt_tgsigqueueinfo( \ + res, (long)(tgid), (long)(pid), (long)(sig), (long)(uinfo)) +#define __sanitizer_syscall_pre_kill(pid, sig) \ + __sanitizer_syscall_pre_impl_kill((long)(pid), (long)(sig)) +#define __sanitizer_syscall_post_kill(res, pid, sig) \ + __sanitizer_syscall_post_impl_kill(res, (long)(pid), (long)(sig)) +#define __sanitizer_syscall_pre_tgkill(tgid, pid, sig) \ + __sanitizer_syscall_pre_impl_tgkill((long)(tgid), (long)(pid), (long)(sig)) +#define __sanitizer_syscall_post_tgkill(res, tgid, pid, sig) \ + __sanitizer_syscall_post_impl_tgkill(res, (long)(tgid), (long)(pid), \ + (long)(sig)) +#define __sanitizer_syscall_pre_tkill(pid, sig) \ + __sanitizer_syscall_pre_impl_tkill((long)(pid), (long)(sig)) +#define __sanitizer_syscall_post_tkill(res, pid, sig) \ + __sanitizer_syscall_post_impl_tkill(res, (long)(pid), (long)(sig)) +#define __sanitizer_syscall_pre_rt_sigqueueinfo(pid, sig, uinfo) \ + __sanitizer_syscall_pre_impl_rt_sigqueueinfo((long)(pid), (long)(sig), \ + (long)(uinfo)) +#define __sanitizer_syscall_post_rt_sigqueueinfo(res, pid, sig, uinfo) \ + __sanitizer_syscall_post_impl_rt_sigqueueinfo(res, (long)(pid), (long)(sig), \ + (long)(uinfo)) +#define __sanitizer_syscall_pre_sgetmask() \ + __sanitizer_syscall_pre_impl_sgetmask() +#define __sanitizer_syscall_post_sgetmask(res) \ + __sanitizer_syscall_post_impl_sgetmask(res) +#define __sanitizer_syscall_pre_ssetmask(newmask) \ + __sanitizer_syscall_pre_impl_ssetmask((long)(newmask)) +#define __sanitizer_syscall_post_ssetmask(res, newmask) \ + __sanitizer_syscall_post_impl_ssetmask(res, (long)(newmask)) +#define __sanitizer_syscall_pre_signal(sig, handler) \ + __sanitizer_syscall_pre_impl_signal((long)(sig), (long)(handler)) +#define __sanitizer_syscall_post_signal(res, sig, handler) \ + __sanitizer_syscall_post_impl_signal(res, (long)(sig), (long)(handler)) +#define __sanitizer_syscall_pre_pause() __sanitizer_syscall_pre_impl_pause() +#define __sanitizer_syscall_post_pause(res) \ + __sanitizer_syscall_post_impl_pause(res) +#define __sanitizer_syscall_pre_sync() __sanitizer_syscall_pre_impl_sync() +#define __sanitizer_syscall_post_sync(res) \ + __sanitizer_syscall_post_impl_sync(res) +#define __sanitizer_syscall_pre_fsync(fd) \ + __sanitizer_syscall_pre_impl_fsync((long)(fd)) +#define __sanitizer_syscall_post_fsync(res, fd) \ + __sanitizer_syscall_post_impl_fsync(res, (long)(fd)) +#define __sanitizer_syscall_pre_fdatasync(fd) \ + __sanitizer_syscall_pre_impl_fdatasync((long)(fd)) +#define __sanitizer_syscall_post_fdatasync(res, fd) \ + __sanitizer_syscall_post_impl_fdatasync(res, (long)(fd)) +#define __sanitizer_syscall_pre_bdflush(func, data) \ + __sanitizer_syscall_pre_impl_bdflush((long)(func), (long)(data)) +#define __sanitizer_syscall_post_bdflush(res, func, data) \ + __sanitizer_syscall_post_impl_bdflush(res, (long)(func), (long)(data)) +#define __sanitizer_syscall_pre_mount(dev_name, dir_name, type, flags, data) \ + __sanitizer_syscall_pre_impl_mount((long)(dev_name), (long)(dir_name), \ + (long)(type), (long)(flags), \ + (long)(data)) +#define __sanitizer_syscall_post_mount(res, dev_name, dir_name, type, flags, \ + data) \ + __sanitizer_syscall_post_impl_mount(res, (long)(dev_name), (long)(dir_name), \ + (long)(type), (long)(flags), \ + (long)(data)) +#define __sanitizer_syscall_pre_umount(name, flags) \ + __sanitizer_syscall_pre_impl_umount((long)(name), (long)(flags)) +#define __sanitizer_syscall_post_umount(res, name, flags) \ + __sanitizer_syscall_post_impl_umount(res, (long)(name), (long)(flags)) +#define __sanitizer_syscall_pre_oldumount(name) \ + __sanitizer_syscall_pre_impl_oldumount((long)(name)) +#define __sanitizer_syscall_post_oldumount(res, name) \ + __sanitizer_syscall_post_impl_oldumount(res, (long)(name)) +#define __sanitizer_syscall_pre_truncate(path, length) \ + __sanitizer_syscall_pre_impl_truncate((long)(path), (long)(length)) +#define __sanitizer_syscall_post_truncate(res, path, length) \ + __sanitizer_syscall_post_impl_truncate(res, (long)(path), (long)(length)) +#define __sanitizer_syscall_pre_ftruncate(fd, length) \ + __sanitizer_syscall_pre_impl_ftruncate((long)(fd), (long)(length)) +#define __sanitizer_syscall_post_ftruncate(res, fd, length) \ + __sanitizer_syscall_post_impl_ftruncate(res, (long)(fd), (long)(length)) +#define __sanitizer_syscall_pre_stat(filename, statbuf) \ + __sanitizer_syscall_pre_impl_stat((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_stat(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_stat(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_statfs(path, buf) \ + __sanitizer_syscall_pre_impl_statfs((long)(path), (long)(buf)) +#define __sanitizer_syscall_post_statfs(res, path, buf) \ + __sanitizer_syscall_post_impl_statfs(res, (long)(path), (long)(buf)) +#define __sanitizer_syscall_pre_statfs64(path, sz, buf) \ + __sanitizer_syscall_pre_impl_statfs64((long)(path), (long)(sz), (long)(buf)) +#define __sanitizer_syscall_post_statfs64(res, path, sz, buf) \ + __sanitizer_syscall_post_impl_statfs64(res, (long)(path), (long)(sz), \ + (long)(buf)) +#define __sanitizer_syscall_pre_fstatfs(fd, buf) \ + __sanitizer_syscall_pre_impl_fstatfs((long)(fd), (long)(buf)) +#define __sanitizer_syscall_post_fstatfs(res, fd, buf) \ + __sanitizer_syscall_post_impl_fstatfs(res, (long)(fd), (long)(buf)) +#define __sanitizer_syscall_pre_fstatfs64(fd, sz, buf) \ + __sanitizer_syscall_pre_impl_fstatfs64((long)(fd), (long)(sz), (long)(buf)) +#define __sanitizer_syscall_post_fstatfs64(res, fd, sz, buf) \ + __sanitizer_syscall_post_impl_fstatfs64(res, (long)(fd), (long)(sz), \ + (long)(buf)) +#define __sanitizer_syscall_pre_lstat(filename, statbuf) \ + __sanitizer_syscall_pre_impl_lstat((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_lstat(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_lstat(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_fstat(fd, statbuf) \ + __sanitizer_syscall_pre_impl_fstat((long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_post_fstat(res, fd, statbuf) \ + __sanitizer_syscall_post_impl_fstat(res, (long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_pre_newstat(filename, statbuf) \ + __sanitizer_syscall_pre_impl_newstat((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_newstat(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_newstat(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_newlstat(filename, statbuf) \ + __sanitizer_syscall_pre_impl_newlstat((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_newlstat(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_newlstat(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_newfstat(fd, statbuf) \ + __sanitizer_syscall_pre_impl_newfstat((long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_post_newfstat(res, fd, statbuf) \ + __sanitizer_syscall_post_impl_newfstat(res, (long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_pre_ustat(dev, ubuf) \ + __sanitizer_syscall_pre_impl_ustat((long)(dev), (long)(ubuf)) +#define __sanitizer_syscall_post_ustat(res, dev, ubuf) \ + __sanitizer_syscall_post_impl_ustat(res, (long)(dev), (long)(ubuf)) +#define __sanitizer_syscall_pre_stat64(filename, statbuf) \ + __sanitizer_syscall_pre_impl_stat64((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_stat64(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_stat64(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_fstat64(fd, statbuf) \ + __sanitizer_syscall_pre_impl_fstat64((long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_post_fstat64(res, fd, statbuf) \ + __sanitizer_syscall_post_impl_fstat64(res, (long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_pre_lstat64(filename, statbuf) \ + __sanitizer_syscall_pre_impl_lstat64((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_lstat64(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_lstat64(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_setxattr(path, name, value, size, flags) \ + __sanitizer_syscall_pre_impl_setxattr( \ + (long)(path), (long)(name), (long)(value), (long)(size), (long)(flags)) +#define __sanitizer_syscall_post_setxattr(res, path, name, value, size, flags) \ + __sanitizer_syscall_post_impl_setxattr(res, (long)(path), (long)(name), \ + (long)(value), (long)(size), \ + (long)(flags)) +#define __sanitizer_syscall_pre_lsetxattr(path, name, value, size, flags) \ + __sanitizer_syscall_pre_impl_lsetxattr( \ + (long)(path), (long)(name), (long)(value), (long)(size), (long)(flags)) +#define __sanitizer_syscall_post_lsetxattr(res, path, name, value, size, \ + flags) \ + __sanitizer_syscall_post_impl_lsetxattr(res, (long)(path), (long)(name), \ + (long)(value), (long)(size), \ + (long)(flags)) +#define __sanitizer_syscall_pre_fsetxattr(fd, name, value, size, flags) \ + __sanitizer_syscall_pre_impl_fsetxattr( \ + (long)(fd), (long)(name), (long)(value), (long)(size), (long)(flags)) +#define __sanitizer_syscall_post_fsetxattr(res, fd, name, value, size, flags) \ + __sanitizer_syscall_post_impl_fsetxattr(res, (long)(fd), (long)(name), \ + (long)(value), (long)(size), \ + (long)(flags)) +#define __sanitizer_syscall_pre_getxattr(path, name, value, size) \ + __sanitizer_syscall_pre_impl_getxattr((long)(path), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_post_getxattr(res, path, name, value, size) \ + __sanitizer_syscall_post_impl_getxattr(res, (long)(path), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_pre_lgetxattr(path, name, value, size) \ + __sanitizer_syscall_pre_impl_lgetxattr((long)(path), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_post_lgetxattr(res, path, name, value, size) \ + __sanitizer_syscall_post_impl_lgetxattr(res, (long)(path), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_pre_fgetxattr(fd, name, value, size) \ + __sanitizer_syscall_pre_impl_fgetxattr((long)(fd), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_post_fgetxattr(res, fd, name, value, size) \ + __sanitizer_syscall_post_impl_fgetxattr(res, (long)(fd), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_pre_listxattr(path, list, size) \ + __sanitizer_syscall_pre_impl_listxattr((long)(path), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_post_listxattr(res, path, list, size) \ + __sanitizer_syscall_post_impl_listxattr(res, (long)(path), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_pre_llistxattr(path, list, size) \ + __sanitizer_syscall_pre_impl_llistxattr((long)(path), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_post_llistxattr(res, path, list, size) \ + __sanitizer_syscall_post_impl_llistxattr(res, (long)(path), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_pre_flistxattr(fd, list, size) \ + __sanitizer_syscall_pre_impl_flistxattr((long)(fd), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_post_flistxattr(res, fd, list, size) \ + __sanitizer_syscall_post_impl_flistxattr(res, (long)(fd), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_pre_removexattr(path, name) \ + __sanitizer_syscall_pre_impl_removexattr((long)(path), (long)(name)) +#define __sanitizer_syscall_post_removexattr(res, path, name) \ + __sanitizer_syscall_post_impl_removexattr(res, (long)(path), (long)(name)) +#define __sanitizer_syscall_pre_lremovexattr(path, name) \ + __sanitizer_syscall_pre_impl_lremovexattr((long)(path), (long)(name)) +#define __sanitizer_syscall_post_lremovexattr(res, path, name) \ + __sanitizer_syscall_post_impl_lremovexattr(res, (long)(path), (long)(name)) +#define __sanitizer_syscall_pre_fremovexattr(fd, name) \ + __sanitizer_syscall_pre_impl_fremovexattr((long)(fd), (long)(name)) +#define __sanitizer_syscall_post_fremovexattr(res, fd, name) \ + __sanitizer_syscall_post_impl_fremovexattr(res, (long)(fd), (long)(name)) +#define __sanitizer_syscall_pre_brk(brk) \ + __sanitizer_syscall_pre_impl_brk((long)(brk)) +#define __sanitizer_syscall_post_brk(res, brk) \ + __sanitizer_syscall_post_impl_brk(res, (long)(brk)) +#define __sanitizer_syscall_pre_mprotect(start, len, prot) \ + __sanitizer_syscall_pre_impl_mprotect((long)(start), (long)(len), \ + (long)(prot)) +#define __sanitizer_syscall_post_mprotect(res, start, len, prot) \ + __sanitizer_syscall_post_impl_mprotect(res, (long)(start), (long)(len), \ + (long)(prot)) +#define __sanitizer_syscall_pre_mremap(addr, old_len, new_len, flags, \ + new_addr) \ + __sanitizer_syscall_pre_impl_mremap((long)(addr), (long)(old_len), \ + (long)(new_len), (long)(flags), \ + (long)(new_addr)) +#define __sanitizer_syscall_post_mremap(res, addr, old_len, new_len, flags, \ + new_addr) \ + __sanitizer_syscall_post_impl_mremap(res, (long)(addr), (long)(old_len), \ + (long)(new_len), (long)(flags), \ + (long)(new_addr)) +#define __sanitizer_syscall_pre_remap_file_pages(start, size, prot, pgoff, \ + flags) \ + __sanitizer_syscall_pre_impl_remap_file_pages( \ + (long)(start), (long)(size), (long)(prot), (long)(pgoff), (long)(flags)) +#define __sanitizer_syscall_post_remap_file_pages(res, start, size, prot, \ + pgoff, flags) \ + __sanitizer_syscall_post_impl_remap_file_pages(res, (long)(start), \ + (long)(size), (long)(prot), \ + (long)(pgoff), (long)(flags)) +#define __sanitizer_syscall_pre_msync(start, len, flags) \ + __sanitizer_syscall_pre_impl_msync((long)(start), (long)(len), (long)(flags)) +#define __sanitizer_syscall_post_msync(res, start, len, flags) \ + __sanitizer_syscall_post_impl_msync(res, (long)(start), (long)(len), \ + (long)(flags)) +#define __sanitizer_syscall_pre_munmap(addr, len) \ + __sanitizer_syscall_pre_impl_munmap((long)(addr), (long)(len)) +#define __sanitizer_syscall_post_munmap(res, addr, len) \ + __sanitizer_syscall_post_impl_munmap(res, (long)(addr), (long)(len)) +#define __sanitizer_syscall_pre_mlock(start, len) \ + __sanitizer_syscall_pre_impl_mlock((long)(start), (long)(len)) +#define __sanitizer_syscall_post_mlock(res, start, len) \ + __sanitizer_syscall_post_impl_mlock(res, (long)(start), (long)(len)) +#define __sanitizer_syscall_pre_munlock(start, len) \ + __sanitizer_syscall_pre_impl_munlock((long)(start), (long)(len)) +#define __sanitizer_syscall_post_munlock(res, start, len) \ + __sanitizer_syscall_post_impl_munlock(res, (long)(start), (long)(len)) +#define __sanitizer_syscall_pre_mlockall(flags) \ + __sanitizer_syscall_pre_impl_mlockall((long)(flags)) +#define __sanitizer_syscall_post_mlockall(res, flags) \ + __sanitizer_syscall_post_impl_mlockall(res, (long)(flags)) +#define __sanitizer_syscall_pre_munlockall() \ + __sanitizer_syscall_pre_impl_munlockall() +#define __sanitizer_syscall_post_munlockall(res) \ + __sanitizer_syscall_post_impl_munlockall(res) +#define __sanitizer_syscall_pre_madvise(start, len, behavior) \ + __sanitizer_syscall_pre_impl_madvise((long)(start), (long)(len), \ + (long)(behavior)) +#define __sanitizer_syscall_post_madvise(res, start, len, behavior) \ + __sanitizer_syscall_post_impl_madvise(res, (long)(start), (long)(len), \ + (long)(behavior)) +#define __sanitizer_syscall_pre_mincore(start, len, vec) \ + __sanitizer_syscall_pre_impl_mincore((long)(start), (long)(len), (long)(vec)) +#define __sanitizer_syscall_post_mincore(res, start, len, vec) \ + __sanitizer_syscall_post_impl_mincore(res, (long)(start), (long)(len), \ + (long)(vec)) +#define __sanitizer_syscall_pre_pivot_root(new_root, put_old) \ + __sanitizer_syscall_pre_impl_pivot_root((long)(new_root), (long)(put_old)) +#define __sanitizer_syscall_post_pivot_root(res, new_root, put_old) \ + __sanitizer_syscall_post_impl_pivot_root(res, (long)(new_root), \ + (long)(put_old)) +#define __sanitizer_syscall_pre_chroot(filename) \ + __sanitizer_syscall_pre_impl_chroot((long)(filename)) +#define __sanitizer_syscall_post_chroot(res, filename) \ + __sanitizer_syscall_post_impl_chroot(res, (long)(filename)) +#define __sanitizer_syscall_pre_mknod(filename, mode, dev) \ + __sanitizer_syscall_pre_impl_mknod((long)(filename), (long)(mode), \ + (long)(dev)) +#define __sanitizer_syscall_post_mknod(res, filename, mode, dev) \ + __sanitizer_syscall_post_impl_mknod(res, (long)(filename), (long)(mode), \ + (long)(dev)) +#define __sanitizer_syscall_pre_link(oldname, newname) \ + __sanitizer_syscall_pre_impl_link((long)(oldname), (long)(newname)) +#define __sanitizer_syscall_post_link(res, oldname, newname) \ + __sanitizer_syscall_post_impl_link(res, (long)(oldname), (long)(newname)) +#define __sanitizer_syscall_pre_symlink(old, new_) \ + __sanitizer_syscall_pre_impl_symlink((long)(old), (long)(new_)) +#define __sanitizer_syscall_post_symlink(res, old, new_) \ + __sanitizer_syscall_post_impl_symlink(res, (long)(old), (long)(new_)) +#define __sanitizer_syscall_pre_unlink(pathname) \ + __sanitizer_syscall_pre_impl_unlink((long)(pathname)) +#define __sanitizer_syscall_post_unlink(res, pathname) \ + __sanitizer_syscall_post_impl_unlink(res, (long)(pathname)) +#define __sanitizer_syscall_pre_rename(oldname, newname) \ + __sanitizer_syscall_pre_impl_rename((long)(oldname), (long)(newname)) +#define __sanitizer_syscall_post_rename(res, oldname, newname) \ + __sanitizer_syscall_post_impl_rename(res, (long)(oldname), (long)(newname)) +#define __sanitizer_syscall_pre_chmod(filename, mode) \ + __sanitizer_syscall_pre_impl_chmod((long)(filename), (long)(mode)) +#define __sanitizer_syscall_post_chmod(res, filename, mode) \ + __sanitizer_syscall_post_impl_chmod(res, (long)(filename), (long)(mode)) +#define __sanitizer_syscall_pre_fchmod(fd, mode) \ + __sanitizer_syscall_pre_impl_fchmod((long)(fd), (long)(mode)) +#define __sanitizer_syscall_post_fchmod(res, fd, mode) \ + __sanitizer_syscall_post_impl_fchmod(res, (long)(fd), (long)(mode)) +#define __sanitizer_syscall_pre_fcntl(fd, cmd, arg) \ + __sanitizer_syscall_pre_impl_fcntl((long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_fcntl(res, fd, cmd, arg) \ + __sanitizer_syscall_post_impl_fcntl(res, (long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_pre_fcntl64(fd, cmd, arg) \ + __sanitizer_syscall_pre_impl_fcntl64((long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_fcntl64(res, fd, cmd, arg) \ + __sanitizer_syscall_post_impl_fcntl64(res, (long)(fd), (long)(cmd), \ + (long)(arg)) +#define __sanitizer_syscall_pre_pipe(fildes) \ + __sanitizer_syscall_pre_impl_pipe((long)(fildes)) +#define __sanitizer_syscall_post_pipe(res, fildes) \ + __sanitizer_syscall_post_impl_pipe(res, (long)(fildes)) +#define __sanitizer_syscall_pre_pipe2(fildes, flags) \ + __sanitizer_syscall_pre_impl_pipe2((long)(fildes), (long)(flags)) +#define __sanitizer_syscall_post_pipe2(res, fildes, flags) \ + __sanitizer_syscall_post_impl_pipe2(res, (long)(fildes), (long)(flags)) +#define __sanitizer_syscall_pre_dup(fildes) \ + __sanitizer_syscall_pre_impl_dup((long)(fildes)) +#define __sanitizer_syscall_post_dup(res, fildes) \ + __sanitizer_syscall_post_impl_dup(res, (long)(fildes)) +#define __sanitizer_syscall_pre_dup2(oldfd, newfd) \ + __sanitizer_syscall_pre_impl_dup2((long)(oldfd), (long)(newfd)) +#define __sanitizer_syscall_post_dup2(res, oldfd, newfd) \ + __sanitizer_syscall_post_impl_dup2(res, (long)(oldfd), (long)(newfd)) +#define __sanitizer_syscall_pre_dup3(oldfd, newfd, flags) \ + __sanitizer_syscall_pre_impl_dup3((long)(oldfd), (long)(newfd), (long)(flags)) +#define __sanitizer_syscall_post_dup3(res, oldfd, newfd, flags) \ + __sanitizer_syscall_post_impl_dup3(res, (long)(oldfd), (long)(newfd), \ + (long)(flags)) +#define __sanitizer_syscall_pre_ioperm(from, num, on) \ + __sanitizer_syscall_pre_impl_ioperm((long)(from), (long)(num), (long)(on)) +#define __sanitizer_syscall_post_ioperm(res, from, num, on) \ + __sanitizer_syscall_post_impl_ioperm(res, (long)(from), (long)(num), \ + (long)(on)) +#define __sanitizer_syscall_pre_ioctl(fd, cmd, arg) \ + __sanitizer_syscall_pre_impl_ioctl((long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_ioctl(res, fd, cmd, arg) \ + __sanitizer_syscall_post_impl_ioctl(res, (long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_pre_flock(fd, cmd) \ + __sanitizer_syscall_pre_impl_flock((long)(fd), (long)(cmd)) +#define __sanitizer_syscall_post_flock(res, fd, cmd) \ + __sanitizer_syscall_post_impl_flock(res, (long)(fd), (long)(cmd)) +#define __sanitizer_syscall_pre_io_setup(nr_reqs, ctx) \ + __sanitizer_syscall_pre_impl_io_setup((long)(nr_reqs), (long)(ctx)) +#define __sanitizer_syscall_post_io_setup(res, nr_reqs, ctx) \ + __sanitizer_syscall_post_impl_io_setup(res, (long)(nr_reqs), (long)(ctx)) +#define __sanitizer_syscall_pre_io_destroy(ctx) \ + __sanitizer_syscall_pre_impl_io_destroy((long)(ctx)) +#define __sanitizer_syscall_post_io_destroy(res, ctx) \ + __sanitizer_syscall_post_impl_io_destroy(res, (long)(ctx)) +#define __sanitizer_syscall_pre_io_getevents(ctx_id, min_nr, nr, events, \ + timeout) \ + __sanitizer_syscall_pre_impl_io_getevents((long)(ctx_id), (long)(min_nr), \ + (long)(nr), (long)(events), \ + (long)(timeout)) +#define __sanitizer_syscall_post_io_getevents(res, ctx_id, min_nr, nr, events, \ + timeout) \ + __sanitizer_syscall_post_impl_io_getevents(res, (long)(ctx_id), \ + (long)(min_nr), (long)(nr), \ + (long)(events), (long)(timeout)) +#define __sanitizer_syscall_pre_io_submit(ctx_id, arg1, arg2) \ + __sanitizer_syscall_pre_impl_io_submit((long)(ctx_id), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_post_io_submit(res, ctx_id, arg1, arg2) \ + __sanitizer_syscall_post_impl_io_submit(res, (long)(ctx_id), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_io_cancel(ctx_id, iocb, result) \ + __sanitizer_syscall_pre_impl_io_cancel((long)(ctx_id), (long)(iocb), \ + (long)(result)) +#define __sanitizer_syscall_post_io_cancel(res, ctx_id, iocb, result) \ + __sanitizer_syscall_post_impl_io_cancel(res, (long)(ctx_id), (long)(iocb), \ + (long)(result)) +#define __sanitizer_syscall_pre_sendfile(out_fd, in_fd, offset, count) \ + __sanitizer_syscall_pre_impl_sendfile((long)(out_fd), (long)(in_fd), \ + (long)(offset), (long)(count)) +#define __sanitizer_syscall_post_sendfile(res, out_fd, in_fd, offset, count) \ + __sanitizer_syscall_post_impl_sendfile(res, (long)(out_fd), (long)(in_fd), \ + (long)(offset), (long)(count)) +#define __sanitizer_syscall_pre_sendfile64(out_fd, in_fd, offset, count) \ + __sanitizer_syscall_pre_impl_sendfile64((long)(out_fd), (long)(in_fd), \ + (long)(offset), (long)(count)) +#define __sanitizer_syscall_post_sendfile64(res, out_fd, in_fd, offset, count) \ + __sanitizer_syscall_post_impl_sendfile64(res, (long)(out_fd), (long)(in_fd), \ + (long)(offset), (long)(count)) +#define __sanitizer_syscall_pre_readlink(path, buf, bufsiz) \ + __sanitizer_syscall_pre_impl_readlink((long)(path), (long)(buf), \ + (long)(bufsiz)) +#define __sanitizer_syscall_post_readlink(res, path, buf, bufsiz) \ + __sanitizer_syscall_post_impl_readlink(res, (long)(path), (long)(buf), \ + (long)(bufsiz)) +#define __sanitizer_syscall_pre_creat(pathname, mode) \ + __sanitizer_syscall_pre_impl_creat((long)(pathname), (long)(mode)) +#define __sanitizer_syscall_post_creat(res, pathname, mode) \ + __sanitizer_syscall_post_impl_creat(res, (long)(pathname), (long)(mode)) +#define __sanitizer_syscall_pre_open(filename, flags, mode) \ + __sanitizer_syscall_pre_impl_open((long)(filename), (long)(flags), \ + (long)(mode)) +#define __sanitizer_syscall_post_open(res, filename, flags, mode) \ + __sanitizer_syscall_post_impl_open(res, (long)(filename), (long)(flags), \ + (long)(mode)) +#define __sanitizer_syscall_pre_close(fd) \ + __sanitizer_syscall_pre_impl_close((long)(fd)) +#define __sanitizer_syscall_post_close(res, fd) \ + __sanitizer_syscall_post_impl_close(res, (long)(fd)) +#define __sanitizer_syscall_pre_access(filename, mode) \ + __sanitizer_syscall_pre_impl_access((long)(filename), (long)(mode)) +#define __sanitizer_syscall_post_access(res, filename, mode) \ + __sanitizer_syscall_post_impl_access(res, (long)(filename), (long)(mode)) +#define __sanitizer_syscall_pre_vhangup() __sanitizer_syscall_pre_impl_vhangup() +#define __sanitizer_syscall_post_vhangup(res) \ + __sanitizer_syscall_post_impl_vhangup(res) +#define __sanitizer_syscall_pre_chown(filename, user, group) \ + __sanitizer_syscall_pre_impl_chown((long)(filename), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_post_chown(res, filename, user, group) \ + __sanitizer_syscall_post_impl_chown(res, (long)(filename), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_pre_lchown(filename, user, group) \ + __sanitizer_syscall_pre_impl_lchown((long)(filename), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_post_lchown(res, filename, user, group) \ + __sanitizer_syscall_post_impl_lchown(res, (long)(filename), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_pre_fchown(fd, user, group) \ + __sanitizer_syscall_pre_impl_fchown((long)(fd), (long)(user), (long)(group)) +#define __sanitizer_syscall_post_fchown(res, fd, user, group) \ + __sanitizer_syscall_post_impl_fchown(res, (long)(fd), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_pre_chown16(filename, user, group) \ + __sanitizer_syscall_pre_impl_chown16((long)(filename), (long)user, \ + (long)group) +#define __sanitizer_syscall_post_chown16(res, filename, user, group) \ + __sanitizer_syscall_post_impl_chown16(res, (long)(filename), (long)user, \ + (long)group) +#define __sanitizer_syscall_pre_lchown16(filename, user, group) \ + __sanitizer_syscall_pre_impl_lchown16((long)(filename), (long)user, \ + (long)group) +#define __sanitizer_syscall_post_lchown16(res, filename, user, group) \ + __sanitizer_syscall_post_impl_lchown16(res, (long)(filename), (long)user, \ + (long)group) +#define __sanitizer_syscall_pre_fchown16(fd, user, group) \ + __sanitizer_syscall_pre_impl_fchown16((long)(fd), (long)user, (long)group) +#define __sanitizer_syscall_post_fchown16(res, fd, user, group) \ + __sanitizer_syscall_post_impl_fchown16(res, (long)(fd), (long)user, \ + (long)group) +#define __sanitizer_syscall_pre_setregid16(rgid, egid) \ + __sanitizer_syscall_pre_impl_setregid16((long)rgid, (long)egid) +#define __sanitizer_syscall_post_setregid16(res, rgid, egid) \ + __sanitizer_syscall_post_impl_setregid16(res, (long)rgid, (long)egid) +#define __sanitizer_syscall_pre_setgid16(gid) \ + __sanitizer_syscall_pre_impl_setgid16((long)gid) +#define __sanitizer_syscall_post_setgid16(res, gid) \ + __sanitizer_syscall_post_impl_setgid16(res, (long)gid) +#define __sanitizer_syscall_pre_setreuid16(ruid, euid) \ + __sanitizer_syscall_pre_impl_setreuid16((long)ruid, (long)euid) +#define __sanitizer_syscall_post_setreuid16(res, ruid, euid) \ + __sanitizer_syscall_post_impl_setreuid16(res, (long)ruid, (long)euid) +#define __sanitizer_syscall_pre_setuid16(uid) \ + __sanitizer_syscall_pre_impl_setuid16((long)uid) +#define __sanitizer_syscall_post_setuid16(res, uid) \ + __sanitizer_syscall_post_impl_setuid16(res, (long)uid) +#define __sanitizer_syscall_pre_setresuid16(ruid, euid, suid) \ + __sanitizer_syscall_pre_impl_setresuid16((long)ruid, (long)euid, (long)suid) +#define __sanitizer_syscall_post_setresuid16(res, ruid, euid, suid) \ + __sanitizer_syscall_post_impl_setresuid16(res, (long)ruid, (long)euid, \ + (long)suid) +#define __sanitizer_syscall_pre_getresuid16(ruid, euid, suid) \ + __sanitizer_syscall_pre_impl_getresuid16((long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_post_getresuid16(res, ruid, euid, suid) \ + __sanitizer_syscall_post_impl_getresuid16(res, (long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_pre_setresgid16(rgid, egid, sgid) \ + __sanitizer_syscall_pre_impl_setresgid16((long)rgid, (long)egid, (long)sgid) +#define __sanitizer_syscall_post_setresgid16(res, rgid, egid, sgid) \ + __sanitizer_syscall_post_impl_setresgid16(res, (long)rgid, (long)egid, \ + (long)sgid) +#define __sanitizer_syscall_pre_getresgid16(rgid, egid, sgid) \ + __sanitizer_syscall_pre_impl_getresgid16((long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_post_getresgid16(res, rgid, egid, sgid) \ + __sanitizer_syscall_post_impl_getresgid16(res, (long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_pre_setfsuid16(uid) \ + __sanitizer_syscall_pre_impl_setfsuid16((long)uid) +#define __sanitizer_syscall_post_setfsuid16(res, uid) \ + __sanitizer_syscall_post_impl_setfsuid16(res, (long)uid) +#define __sanitizer_syscall_pre_setfsgid16(gid) \ + __sanitizer_syscall_pre_impl_setfsgid16((long)gid) +#define __sanitizer_syscall_post_setfsgid16(res, gid) \ + __sanitizer_syscall_post_impl_setfsgid16(res, (long)gid) +#define __sanitizer_syscall_pre_getgroups16(gidsetsize, grouplist) \ + __sanitizer_syscall_pre_impl_getgroups16((long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_post_getgroups16(res, gidsetsize, grouplist) \ + __sanitizer_syscall_post_impl_getgroups16(res, (long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_pre_setgroups16(gidsetsize, grouplist) \ + __sanitizer_syscall_pre_impl_setgroups16((long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_post_setgroups16(res, gidsetsize, grouplist) \ + __sanitizer_syscall_post_impl_setgroups16(res, (long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_pre_getuid16() \ + __sanitizer_syscall_pre_impl_getuid16() +#define __sanitizer_syscall_post_getuid16(res) \ + __sanitizer_syscall_post_impl_getuid16(res) +#define __sanitizer_syscall_pre_geteuid16() \ + __sanitizer_syscall_pre_impl_geteuid16() +#define __sanitizer_syscall_post_geteuid16(res) \ + __sanitizer_syscall_post_impl_geteuid16(res) +#define __sanitizer_syscall_pre_getgid16() \ + __sanitizer_syscall_pre_impl_getgid16() +#define __sanitizer_syscall_post_getgid16(res) \ + __sanitizer_syscall_post_impl_getgid16(res) +#define __sanitizer_syscall_pre_getegid16() \ + __sanitizer_syscall_pre_impl_getegid16() +#define __sanitizer_syscall_post_getegid16(res) \ + __sanitizer_syscall_post_impl_getegid16(res) +#define __sanitizer_syscall_pre_utime(filename, times) \ + __sanitizer_syscall_pre_impl_utime((long)(filename), (long)(times)) +#define __sanitizer_syscall_post_utime(res, filename, times) \ + __sanitizer_syscall_post_impl_utime(res, (long)(filename), (long)(times)) +#define __sanitizer_syscall_pre_utimes(filename, utimes) \ + __sanitizer_syscall_pre_impl_utimes((long)(filename), (long)(utimes)) +#define __sanitizer_syscall_post_utimes(res, filename, utimes) \ + __sanitizer_syscall_post_impl_utimes(res, (long)(filename), (long)(utimes)) +#define __sanitizer_syscall_pre_lseek(fd, offset, origin) \ + __sanitizer_syscall_pre_impl_lseek((long)(fd), (long)(offset), (long)(origin)) +#define __sanitizer_syscall_post_lseek(res, fd, offset, origin) \ + __sanitizer_syscall_post_impl_lseek(res, (long)(fd), (long)(offset), \ + (long)(origin)) +#define __sanitizer_syscall_pre_llseek(fd, offset_high, offset_low, result, \ + origin) \ + __sanitizer_syscall_pre_impl_llseek((long)(fd), (long)(offset_high), \ + (long)(offset_low), (long)(result), \ + (long)(origin)) +#define __sanitizer_syscall_post_llseek(res, fd, offset_high, offset_low, \ + result, origin) \ + __sanitizer_syscall_post_impl_llseek(res, (long)(fd), (long)(offset_high), \ + (long)(offset_low), (long)(result), \ + (long)(origin)) +#define __sanitizer_syscall_pre_read(fd, buf, count) \ + __sanitizer_syscall_pre_impl_read((long)(fd), (long)(buf), (long)(count)) +#define __sanitizer_syscall_post_read(res, fd, buf, count) \ + __sanitizer_syscall_post_impl_read(res, (long)(fd), (long)(buf), \ + (long)(count)) +#define __sanitizer_syscall_pre_readv(fd, vec, vlen) \ + __sanitizer_syscall_pre_impl_readv((long)(fd), (long)(vec), (long)(vlen)) +#define __sanitizer_syscall_post_readv(res, fd, vec, vlen) \ + __sanitizer_syscall_post_impl_readv(res, (long)(fd), (long)(vec), \ + (long)(vlen)) +#define __sanitizer_syscall_pre_write(fd, buf, count) \ + __sanitizer_syscall_pre_impl_write((long)(fd), (long)(buf), (long)(count)) +#define __sanitizer_syscall_post_write(res, fd, buf, count) \ + __sanitizer_syscall_post_impl_write(res, (long)(fd), (long)(buf), \ + (long)(count)) +#define __sanitizer_syscall_pre_writev(fd, vec, vlen) \ + __sanitizer_syscall_pre_impl_writev((long)(fd), (long)(vec), (long)(vlen)) +#define __sanitizer_syscall_post_writev(res, fd, vec, vlen) \ + __sanitizer_syscall_post_impl_writev(res, (long)(fd), (long)(vec), \ + (long)(vlen)) -void __sanitizer_syscall_pre_rt_sigpending(void *p, size_t s); -void __sanitizer_syscall_pre_getdents(int fd, void *dirp, int count); -void __sanitizer_syscall_pre_getdents64(int fd, void *dirp, int count); -void __sanitizer_syscall_pre_recvmsg(int sockfd, void *msg, int flags); -void __sanitizer_syscall_pre_wait4(int pid, int *status, int options, void *r); -void __sanitizer_syscall_pre_waitpid(int pid, int *status, int options); +#ifdef _LP64 +#define __sanitizer_syscall_pre_pread64(fd, buf, count, pos) \ + __sanitizer_syscall_pre_impl_pread64((long)(fd), (long)(buf), (long)(count), \ + (long)(pos)) +#define __sanitizer_syscall_post_pread64(res, fd, buf, count, pos) \ + __sanitizer_syscall_post_impl_pread64(res, (long)(fd), (long)(buf), \ + (long)(count), (long)(pos)) +#define __sanitizer_syscall_pre_pwrite64(fd, buf, count, pos) \ + __sanitizer_syscall_pre_impl_pwrite64((long)(fd), (long)(buf), \ + (long)(count), (long)(pos)) +#define __sanitizer_syscall_post_pwrite64(res, fd, buf, count, pos) \ + __sanitizer_syscall_post_impl_pwrite64(res, (long)(fd), (long)(buf), \ + (long)(count), (long)(pos)) +#else +#define __sanitizer_syscall_pre_pread64(fd, buf, count, pos0, pos1) \ + __sanitizer_syscall_pre_impl_pread64((long)(fd), (long)(buf), (long)(count), \ + (long)(pos0), (long)(pos1)) +#define __sanitizer_syscall_post_pread64(res, fd, buf, count, pos0, pos1) \ + __sanitizer_syscall_post_impl_pread64(res, (long)(fd), (long)(buf), \ + (long)(count), (long)(pos0), \ + (long)(pos1)) +#define __sanitizer_syscall_pre_pwrite64(fd, buf, count, pos0, pos1) \ + __sanitizer_syscall_pre_impl_pwrite64( \ + (long)(fd), (long)(buf), (long)(count), (long)(pos0), (long)(pos1)) +#define __sanitizer_syscall_post_pwrite64(res, fd, buf, count, pos0, pos1) \ + __sanitizer_syscall_post_impl_pwrite64( \ + res, (long)(fd), (long)(buf), (long)(count), (long)(pos0), (long)(pos1)) +#endif -void __sanitizer_syscall_post_rt_sigpending(long res, void *p, size_t s); -void __sanitizer_syscall_post_getdents(long res, int fd, void *dirp, int count); -void __sanitizer_syscall_post_getdents64(long res, int fd, void *dirp, - int count); -void __sanitizer_syscall_post_recvmsg(long res, int sockfd, void *msg, - int flags); -void __sanitizer_syscall_post_wait4(long res, int pid, int *status, int options, - void *r); -void __sanitizer_syscall_post_waitpid(long res, int pid, int *status, - int options); +#define __sanitizer_syscall_pre_preadv(fd, vec, vlen, pos_l, pos_h) \ + __sanitizer_syscall_pre_impl_preadv((long)(fd), (long)(vec), (long)(vlen), \ + (long)(pos_l), (long)(pos_h)) +#define __sanitizer_syscall_post_preadv(res, fd, vec, vlen, pos_l, pos_h) \ + __sanitizer_syscall_post_impl_preadv(res, (long)(fd), (long)(vec), \ + (long)(vlen), (long)(pos_l), \ + (long)(pos_h)) +#define __sanitizer_syscall_pre_pwritev(fd, vec, vlen, pos_l, pos_h) \ + __sanitizer_syscall_pre_impl_pwritev((long)(fd), (long)(vec), (long)(vlen), \ + (long)(pos_l), (long)(pos_h)) +#define __sanitizer_syscall_post_pwritev(res, fd, vec, vlen, pos_l, pos_h) \ + __sanitizer_syscall_post_impl_pwritev(res, (long)(fd), (long)(vec), \ + (long)(vlen), (long)(pos_l), \ + (long)(pos_h)) +#define __sanitizer_syscall_pre_getcwd(buf, size) \ + __sanitizer_syscall_pre_impl_getcwd((long)(buf), (long)(size)) +#define __sanitizer_syscall_post_getcwd(res, buf, size) \ + __sanitizer_syscall_post_impl_getcwd(res, (long)(buf), (long)(size)) +#define __sanitizer_syscall_pre_mkdir(pathname, mode) \ + __sanitizer_syscall_pre_impl_mkdir((long)(pathname), (long)(mode)) +#define __sanitizer_syscall_post_mkdir(res, pathname, mode) \ + __sanitizer_syscall_post_impl_mkdir(res, (long)(pathname), (long)(mode)) +#define __sanitizer_syscall_pre_chdir(filename) \ + __sanitizer_syscall_pre_impl_chdir((long)(filename)) +#define __sanitizer_syscall_post_chdir(res, filename) \ + __sanitizer_syscall_post_impl_chdir(res, (long)(filename)) +#define __sanitizer_syscall_pre_fchdir(fd) \ + __sanitizer_syscall_pre_impl_fchdir((long)(fd)) +#define __sanitizer_syscall_post_fchdir(res, fd) \ + __sanitizer_syscall_post_impl_fchdir(res, (long)(fd)) +#define __sanitizer_syscall_pre_rmdir(pathname) \ + __sanitizer_syscall_pre_impl_rmdir((long)(pathname)) +#define __sanitizer_syscall_post_rmdir(res, pathname) \ + __sanitizer_syscall_post_impl_rmdir(res, (long)(pathname)) +#define __sanitizer_syscall_pre_lookup_dcookie(cookie64, buf, len) \ + __sanitizer_syscall_pre_impl_lookup_dcookie((long)(cookie64), (long)(buf), \ + (long)(len)) +#define __sanitizer_syscall_post_lookup_dcookie(res, cookie64, buf, len) \ + __sanitizer_syscall_post_impl_lookup_dcookie(res, (long)(cookie64), \ + (long)(buf), (long)(len)) +#define __sanitizer_syscall_pre_quotactl(cmd, special, id, addr) \ + __sanitizer_syscall_pre_impl_quotactl((long)(cmd), (long)(special), \ + (long)(id), (long)(addr)) +#define __sanitizer_syscall_post_quotactl(res, cmd, special, id, addr) \ + __sanitizer_syscall_post_impl_quotactl(res, (long)(cmd), (long)(special), \ + (long)(id), (long)(addr)) +#define __sanitizer_syscall_pre_getdents(fd, dirent, count) \ + __sanitizer_syscall_pre_impl_getdents((long)(fd), (long)(dirent), \ + (long)(count)) +#define __sanitizer_syscall_post_getdents(res, fd, dirent, count) \ + __sanitizer_syscall_post_impl_getdents(res, (long)(fd), (long)(dirent), \ + (long)(count)) +#define __sanitizer_syscall_pre_getdents64(fd, dirent, count) \ + __sanitizer_syscall_pre_impl_getdents64((long)(fd), (long)(dirent), \ + (long)(count)) +#define __sanitizer_syscall_post_getdents64(res, fd, dirent, count) \ + __sanitizer_syscall_post_impl_getdents64(res, (long)(fd), (long)(dirent), \ + (long)(count)) +#define __sanitizer_syscall_pre_setsockopt(fd, level, optname, optval, optlen) \ + __sanitizer_syscall_pre_impl_setsockopt((long)(fd), (long)(level), \ + (long)(optname), (long)(optval), \ + (long)(optlen)) +#define __sanitizer_syscall_post_setsockopt(res, fd, level, optname, optval, \ + optlen) \ + __sanitizer_syscall_post_impl_setsockopt(res, (long)(fd), (long)(level), \ + (long)(optname), (long)(optval), \ + (long)(optlen)) +#define __sanitizer_syscall_pre_getsockopt(fd, level, optname, optval, optlen) \ + __sanitizer_syscall_pre_impl_getsockopt((long)(fd), (long)(level), \ + (long)(optname), (long)(optval), \ + (long)(optlen)) +#define __sanitizer_syscall_post_getsockopt(res, fd, level, optname, optval, \ + optlen) \ + __sanitizer_syscall_post_impl_getsockopt(res, (long)(fd), (long)(level), \ + (long)(optname), (long)(optval), \ + (long)(optlen)) +#define __sanitizer_syscall_pre_bind(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_bind((long)(arg0), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_bind(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_bind(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_connect(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_connect((long)(arg0), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_connect(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_connect(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_accept(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_accept((long)(arg0), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_accept(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_accept(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_accept4(arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_pre_impl_accept4((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_post_accept4(res, arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_post_impl_accept4(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_pre_getsockname(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_getsockname((long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_post_getsockname(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_getsockname(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_getpeername(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_getpeername((long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_post_getpeername(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_getpeername(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_send(arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_pre_impl_send((long)(arg0), (long)(arg1), (long)(arg2), \ + (long)(arg3)) +#define __sanitizer_syscall_post_send(res, arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_post_impl_send(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_pre_sendto(arg0, arg1, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_pre_impl_sendto((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_post_sendto(res, arg0, arg1, arg2, arg3, arg4, \ + arg5) \ + __sanitizer_syscall_post_impl_sendto(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_pre_sendmsg(fd, msg, flags) \ + __sanitizer_syscall_pre_impl_sendmsg((long)(fd), (long)(msg), (long)(flags)) +#define __sanitizer_syscall_post_sendmsg(res, fd, msg, flags) \ + __sanitizer_syscall_post_impl_sendmsg(res, (long)(fd), (long)(msg), \ + (long)(flags)) +#define __sanitizer_syscall_pre_sendmmsg(fd, msg, vlen, flags) \ + __sanitizer_syscall_pre_impl_sendmmsg((long)(fd), (long)(msg), (long)(vlen), \ + (long)(flags)) +#define __sanitizer_syscall_post_sendmmsg(res, fd, msg, vlen, flags) \ + __sanitizer_syscall_post_impl_sendmmsg(res, (long)(fd), (long)(msg), \ + (long)(vlen), (long)(flags)) +#define __sanitizer_syscall_pre_recv(arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_pre_impl_recv((long)(arg0), (long)(arg1), (long)(arg2), \ + (long)(arg3)) +#define __sanitizer_syscall_post_recv(res, arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_post_impl_recv(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_pre_recvfrom(arg0, arg1, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_pre_impl_recvfrom((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_post_recvfrom(res, arg0, arg1, arg2, arg3, arg4, \ + arg5) \ + __sanitizer_syscall_post_impl_recvfrom(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_pre_recvmsg(fd, msg, flags) \ + __sanitizer_syscall_pre_impl_recvmsg((long)(fd), (long)(msg), (long)(flags)) +#define __sanitizer_syscall_post_recvmsg(res, fd, msg, flags) \ + __sanitizer_syscall_post_impl_recvmsg(res, (long)(fd), (long)(msg), \ + (long)(flags)) +#define __sanitizer_syscall_pre_recvmmsg(fd, msg, vlen, flags, timeout) \ + __sanitizer_syscall_pre_impl_recvmmsg((long)(fd), (long)(msg), (long)(vlen), \ + (long)(flags), (long)(timeout)) +#define __sanitizer_syscall_post_recvmmsg(res, fd, msg, vlen, flags, timeout) \ + __sanitizer_syscall_post_impl_recvmmsg(res, (long)(fd), (long)(msg), \ + (long)(vlen), (long)(flags), \ + (long)(timeout)) +#define __sanitizer_syscall_pre_socket(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_socket((long)(arg0), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_socket(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_socket(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_socketpair(arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_pre_impl_socketpair((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_post_socketpair(res, arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_post_impl_socketpair(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_pre_socketcall(call, args) \ + __sanitizer_syscall_pre_impl_socketcall((long)(call), (long)(args)) +#define __sanitizer_syscall_post_socketcall(res, call, args) \ + __sanitizer_syscall_post_impl_socketcall(res, (long)(call), (long)(args)) +#define __sanitizer_syscall_pre_listen(arg0, arg1) \ + __sanitizer_syscall_pre_impl_listen((long)(arg0), (long)(arg1)) +#define __sanitizer_syscall_post_listen(res, arg0, arg1) \ + __sanitizer_syscall_post_impl_listen(res, (long)(arg0), (long)(arg1)) +#define __sanitizer_syscall_pre_poll(ufds, nfds, timeout) \ + __sanitizer_syscall_pre_impl_poll((long)(ufds), (long)(nfds), (long)(timeout)) +#define __sanitizer_syscall_post_poll(res, ufds, nfds, timeout) \ + __sanitizer_syscall_post_impl_poll(res, (long)(ufds), (long)(nfds), \ + (long)(timeout)) +#define __sanitizer_syscall_pre_select(n, inp, outp, exp, tvp) \ + __sanitizer_syscall_pre_impl_select((long)(n), (long)(inp), (long)(outp), \ + (long)(exp), (long)(tvp)) +#define __sanitizer_syscall_post_select(res, n, inp, outp, exp, tvp) \ + __sanitizer_syscall_post_impl_select(res, (long)(n), (long)(inp), \ + (long)(outp), (long)(exp), (long)(tvp)) +#define __sanitizer_syscall_pre_old_select(arg) \ + __sanitizer_syscall_pre_impl_old_select((long)(arg)) +#define __sanitizer_syscall_post_old_select(res, arg) \ + __sanitizer_syscall_post_impl_old_select(res, (long)(arg)) +#define __sanitizer_syscall_pre_epoll_create(size) \ + __sanitizer_syscall_pre_impl_epoll_create((long)(size)) +#define __sanitizer_syscall_post_epoll_create(res, size) \ + __sanitizer_syscall_post_impl_epoll_create(res, (long)(size)) +#define __sanitizer_syscall_pre_epoll_create1(flags) \ + __sanitizer_syscall_pre_impl_epoll_create1((long)(flags)) +#define __sanitizer_syscall_post_epoll_create1(res, flags) \ + __sanitizer_syscall_post_impl_epoll_create1(res, (long)(flags)) +#define __sanitizer_syscall_pre_epoll_ctl(epfd, op, fd, event) \ + __sanitizer_syscall_pre_impl_epoll_ctl((long)(epfd), (long)(op), (long)(fd), \ + (long)(event)) +#define __sanitizer_syscall_post_epoll_ctl(res, epfd, op, fd, event) \ + __sanitizer_syscall_post_impl_epoll_ctl(res, (long)(epfd), (long)(op), \ + (long)(fd), (long)(event)) +#define __sanitizer_syscall_pre_epoll_wait(epfd, events, maxevents, timeout) \ + __sanitizer_syscall_pre_impl_epoll_wait((long)(epfd), (long)(events), \ + (long)(maxevents), (long)(timeout)) +#define __sanitizer_syscall_post_epoll_wait(res, epfd, events, maxevents, \ + timeout) \ + __sanitizer_syscall_post_impl_epoll_wait(res, (long)(epfd), (long)(events), \ + (long)(maxevents), (long)(timeout)) +#define __sanitizer_syscall_pre_epoll_pwait(epfd, events, maxevents, timeout, \ + sigmask, sigsetsize) \ + __sanitizer_syscall_pre_impl_epoll_pwait( \ + (long)(epfd), (long)(events), (long)(maxevents), (long)(timeout), \ + (long)(sigmask), (long)(sigsetsize)) +#define __sanitizer_syscall_post_epoll_pwait(res, epfd, events, maxevents, \ + timeout, sigmask, sigsetsize) \ + __sanitizer_syscall_post_impl_epoll_pwait( \ + res, (long)(epfd), (long)(events), (long)(maxevents), (long)(timeout), \ + (long)(sigmask), (long)(sigsetsize)) +#define __sanitizer_syscall_pre_gethostname(name, len) \ + __sanitizer_syscall_pre_impl_gethostname((long)(name), (long)(len)) +#define __sanitizer_syscall_post_gethostname(res, name, len) \ + __sanitizer_syscall_post_impl_gethostname(res, (long)(name), (long)(len)) +#define __sanitizer_syscall_pre_sethostname(name, len) \ + __sanitizer_syscall_pre_impl_sethostname((long)(name), (long)(len)) +#define __sanitizer_syscall_post_sethostname(res, name, len) \ + __sanitizer_syscall_post_impl_sethostname(res, (long)(name), (long)(len)) +#define __sanitizer_syscall_pre_setdomainname(name, len) \ + __sanitizer_syscall_pre_impl_setdomainname((long)(name), (long)(len)) +#define __sanitizer_syscall_post_setdomainname(res, name, len) \ + __sanitizer_syscall_post_impl_setdomainname(res, (long)(name), (long)(len)) +#define __sanitizer_syscall_pre_newuname(name) \ + __sanitizer_syscall_pre_impl_newuname((long)(name)) +#define __sanitizer_syscall_post_newuname(res, name) \ + __sanitizer_syscall_post_impl_newuname(res, (long)(name)) +#define __sanitizer_syscall_pre_uname(arg0) \ + __sanitizer_syscall_pre_impl_uname((long)(arg0)) +#define __sanitizer_syscall_post_uname(res, arg0) \ + __sanitizer_syscall_post_impl_uname(res, (long)(arg0)) +#define __sanitizer_syscall_pre_olduname(arg0) \ + __sanitizer_syscall_pre_impl_olduname((long)(arg0)) +#define __sanitizer_syscall_post_olduname(res, arg0) \ + __sanitizer_syscall_post_impl_olduname(res, (long)(arg0)) +#define __sanitizer_syscall_pre_getrlimit(resource, rlim) \ + __sanitizer_syscall_pre_impl_getrlimit((long)(resource), (long)(rlim)) +#define __sanitizer_syscall_post_getrlimit(res, resource, rlim) \ + __sanitizer_syscall_post_impl_getrlimit(res, (long)(resource), (long)(rlim)) +#define __sanitizer_syscall_pre_old_getrlimit(resource, rlim) \ + __sanitizer_syscall_pre_impl_old_getrlimit((long)(resource), (long)(rlim)) +#define __sanitizer_syscall_post_old_getrlimit(res, resource, rlim) \ + __sanitizer_syscall_post_impl_old_getrlimit(res, (long)(resource), \ + (long)(rlim)) +#define __sanitizer_syscall_pre_setrlimit(resource, rlim) \ + __sanitizer_syscall_pre_impl_setrlimit((long)(resource), (long)(rlim)) +#define __sanitizer_syscall_post_setrlimit(res, resource, rlim) \ + __sanitizer_syscall_post_impl_setrlimit(res, (long)(resource), (long)(rlim)) +#define __sanitizer_syscall_pre_prlimit64(pid, resource, new_rlim, old_rlim) \ + __sanitizer_syscall_pre_impl_prlimit64((long)(pid), (long)(resource), \ + (long)(new_rlim), (long)(old_rlim)) +#define __sanitizer_syscall_post_prlimit64(res, pid, resource, new_rlim, \ + old_rlim) \ + __sanitizer_syscall_post_impl_prlimit64(res, (long)(pid), (long)(resource), \ + (long)(new_rlim), (long)(old_rlim)) +#define __sanitizer_syscall_pre_getrusage(who, ru) \ + __sanitizer_syscall_pre_impl_getrusage((long)(who), (long)(ru)) +#define __sanitizer_syscall_post_getrusage(res, who, ru) \ + __sanitizer_syscall_post_impl_getrusage(res, (long)(who), (long)(ru)) +#define __sanitizer_syscall_pre_umask(mask) \ + __sanitizer_syscall_pre_impl_umask((long)(mask)) +#define __sanitizer_syscall_post_umask(res, mask) \ + __sanitizer_syscall_post_impl_umask(res, (long)(mask)) +#define __sanitizer_syscall_pre_msgget(key, msgflg) \ + __sanitizer_syscall_pre_impl_msgget((long)(key), (long)(msgflg)) +#define __sanitizer_syscall_post_msgget(res, key, msgflg) \ + __sanitizer_syscall_post_impl_msgget(res, (long)(key), (long)(msgflg)) +#define __sanitizer_syscall_pre_msgsnd(msqid, msgp, msgsz, msgflg) \ + __sanitizer_syscall_pre_impl_msgsnd((long)(msqid), (long)(msgp), \ + (long)(msgsz), (long)(msgflg)) +#define __sanitizer_syscall_post_msgsnd(res, msqid, msgp, msgsz, msgflg) \ + __sanitizer_syscall_post_impl_msgsnd(res, (long)(msqid), (long)(msgp), \ + (long)(msgsz), (long)(msgflg)) +#define __sanitizer_syscall_pre_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg) \ + __sanitizer_syscall_pre_impl_msgrcv((long)(msqid), (long)(msgp), \ + (long)(msgsz), (long)(msgtyp), \ + (long)(msgflg)) +#define __sanitizer_syscall_post_msgrcv(res, msqid, msgp, msgsz, msgtyp, \ + msgflg) \ + __sanitizer_syscall_post_impl_msgrcv(res, (long)(msqid), (long)(msgp), \ + (long)(msgsz), (long)(msgtyp), \ + (long)(msgflg)) +#define __sanitizer_syscall_pre_msgctl(msqid, cmd, buf) \ + __sanitizer_syscall_pre_impl_msgctl((long)(msqid), (long)(cmd), (long)(buf)) +#define __sanitizer_syscall_post_msgctl(res, msqid, cmd, buf) \ + __sanitizer_syscall_post_impl_msgctl(res, (long)(msqid), (long)(cmd), \ + (long)(buf)) +#define __sanitizer_syscall_pre_semget(key, nsems, semflg) \ + __sanitizer_syscall_pre_impl_semget((long)(key), (long)(nsems), \ + (long)(semflg)) +#define __sanitizer_syscall_post_semget(res, key, nsems, semflg) \ + __sanitizer_syscall_post_impl_semget(res, (long)(key), (long)(nsems), \ + (long)(semflg)) +#define __sanitizer_syscall_pre_semop(semid, sops, nsops) \ + __sanitizer_syscall_pre_impl_semop((long)(semid), (long)(sops), (long)(nsops)) +#define __sanitizer_syscall_post_semop(res, semid, sops, nsops) \ + __sanitizer_syscall_post_impl_semop(res, (long)(semid), (long)(sops), \ + (long)(nsops)) +#define __sanitizer_syscall_pre_semctl(semid, semnum, cmd, arg) \ + __sanitizer_syscall_pre_impl_semctl((long)(semid), (long)(semnum), \ + (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_semctl(res, semid, semnum, cmd, arg) \ + __sanitizer_syscall_post_impl_semctl(res, (long)(semid), (long)(semnum), \ + (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_pre_semtimedop(semid, sops, nsops, timeout) \ + __sanitizer_syscall_pre_impl_semtimedop((long)(semid), (long)(sops), \ + (long)(nsops), (long)(timeout)) +#define __sanitizer_syscall_post_semtimedop(res, semid, sops, nsops, timeout) \ + __sanitizer_syscall_post_impl_semtimedop(res, (long)(semid), (long)(sops), \ + (long)(nsops), (long)(timeout)) +#define __sanitizer_syscall_pre_shmat(shmid, shmaddr, shmflg) \ + __sanitizer_syscall_pre_impl_shmat((long)(shmid), (long)(shmaddr), \ + (long)(shmflg)) +#define __sanitizer_syscall_post_shmat(res, shmid, shmaddr, shmflg) \ + __sanitizer_syscall_post_impl_shmat(res, (long)(shmid), (long)(shmaddr), \ + (long)(shmflg)) +#define __sanitizer_syscall_pre_shmget(key, size, flag) \ + __sanitizer_syscall_pre_impl_shmget((long)(key), (long)(size), (long)(flag)) +#define __sanitizer_syscall_post_shmget(res, key, size, flag) \ + __sanitizer_syscall_post_impl_shmget(res, (long)(key), (long)(size), \ + (long)(flag)) +#define __sanitizer_syscall_pre_shmdt(shmaddr) \ + __sanitizer_syscall_pre_impl_shmdt((long)(shmaddr)) +#define __sanitizer_syscall_post_shmdt(res, shmaddr) \ + __sanitizer_syscall_post_impl_shmdt(res, (long)(shmaddr)) +#define __sanitizer_syscall_pre_shmctl(shmid, cmd, buf) \ + __sanitizer_syscall_pre_impl_shmctl((long)(shmid), (long)(cmd), (long)(buf)) +#define __sanitizer_syscall_post_shmctl(res, shmid, cmd, buf) \ + __sanitizer_syscall_post_impl_shmctl(res, (long)(shmid), (long)(cmd), \ + (long)(buf)) +#define __sanitizer_syscall_pre_ipc(call, first, second, third, ptr, fifth) \ + __sanitizer_syscall_pre_impl_ipc((long)(call), (long)(first), \ + (long)(second), (long)(third), (long)(ptr), \ + (long)(fifth)) +#define __sanitizer_syscall_post_ipc(res, call, first, second, third, ptr, \ + fifth) \ + __sanitizer_syscall_post_impl_ipc(res, (long)(call), (long)(first), \ + (long)(second), (long)(third), \ + (long)(ptr), (long)(fifth)) +#define __sanitizer_syscall_pre_mq_open(name, oflag, mode, attr) \ + __sanitizer_syscall_pre_impl_mq_open((long)(name), (long)(oflag), \ + (long)(mode), (long)(attr)) +#define __sanitizer_syscall_post_mq_open(res, name, oflag, mode, attr) \ + __sanitizer_syscall_post_impl_mq_open(res, (long)(name), (long)(oflag), \ + (long)(mode), (long)(attr)) +#define __sanitizer_syscall_pre_mq_unlink(name) \ + __sanitizer_syscall_pre_impl_mq_unlink((long)(name)) +#define __sanitizer_syscall_post_mq_unlink(res, name) \ + __sanitizer_syscall_post_impl_mq_unlink(res, (long)(name)) +#define __sanitizer_syscall_pre_mq_timedsend(mqdes, msg_ptr, msg_len, \ + msg_prio, abs_timeout) \ + __sanitizer_syscall_pre_impl_mq_timedsend((long)(mqdes), (long)(msg_ptr), \ + (long)(msg_len), (long)(msg_prio), \ + (long)(abs_timeout)) +#define __sanitizer_syscall_post_mq_timedsend(res, mqdes, msg_ptr, msg_len, \ + msg_prio, abs_timeout) \ + __sanitizer_syscall_post_impl_mq_timedsend( \ + res, (long)(mqdes), (long)(msg_ptr), (long)(msg_len), (long)(msg_prio), \ + (long)(abs_timeout)) +#define __sanitizer_syscall_pre_mq_timedreceive(mqdes, msg_ptr, msg_len, \ + msg_prio, abs_timeout) \ + __sanitizer_syscall_pre_impl_mq_timedreceive( \ + (long)(mqdes), (long)(msg_ptr), (long)(msg_len), (long)(msg_prio), \ + (long)(abs_timeout)) +#define __sanitizer_syscall_post_mq_timedreceive(res, mqdes, msg_ptr, msg_len, \ + msg_prio, abs_timeout) \ + __sanitizer_syscall_post_impl_mq_timedreceive( \ + res, (long)(mqdes), (long)(msg_ptr), (long)(msg_len), (long)(msg_prio), \ + (long)(abs_timeout)) +#define __sanitizer_syscall_pre_mq_notify(mqdes, notification) \ + __sanitizer_syscall_pre_impl_mq_notify((long)(mqdes), (long)(notification)) +#define __sanitizer_syscall_post_mq_notify(res, mqdes, notification) \ + __sanitizer_syscall_post_impl_mq_notify(res, (long)(mqdes), \ + (long)(notification)) +#define __sanitizer_syscall_pre_mq_getsetattr(mqdes, mqstat, omqstat) \ + __sanitizer_syscall_pre_impl_mq_getsetattr((long)(mqdes), (long)(mqstat), \ + (long)(omqstat)) +#define __sanitizer_syscall_post_mq_getsetattr(res, mqdes, mqstat, omqstat) \ + __sanitizer_syscall_post_impl_mq_getsetattr(res, (long)(mqdes), \ + (long)(mqstat), (long)(omqstat)) +#define __sanitizer_syscall_pre_pciconfig_iobase(which, bus, devfn) \ + __sanitizer_syscall_pre_impl_pciconfig_iobase((long)(which), (long)(bus), \ + (long)(devfn)) +#define __sanitizer_syscall_post_pciconfig_iobase(res, which, bus, devfn) \ + __sanitizer_syscall_post_impl_pciconfig_iobase(res, (long)(which), \ + (long)(bus), (long)(devfn)) +#define __sanitizer_syscall_pre_pciconfig_read(bus, dfn, off, len, buf) \ + __sanitizer_syscall_pre_impl_pciconfig_read( \ + (long)(bus), (long)(dfn), (long)(off), (long)(len), (long)(buf)) +#define __sanitizer_syscall_post_pciconfig_read(res, bus, dfn, off, len, buf) \ + __sanitizer_syscall_post_impl_pciconfig_read( \ + res, (long)(bus), (long)(dfn), (long)(off), (long)(len), (long)(buf)) +#define __sanitizer_syscall_pre_pciconfig_write(bus, dfn, off, len, buf) \ + __sanitizer_syscall_pre_impl_pciconfig_write( \ + (long)(bus), (long)(dfn), (long)(off), (long)(len), (long)(buf)) +#define __sanitizer_syscall_post_pciconfig_write(res, bus, dfn, off, len, buf) \ + __sanitizer_syscall_post_impl_pciconfig_write( \ + res, (long)(bus), (long)(dfn), (long)(off), (long)(len), (long)(buf)) +#define __sanitizer_syscall_pre_swapon(specialfile, swap_flags) \ + __sanitizer_syscall_pre_impl_swapon((long)(specialfile), (long)(swap_flags)) +#define __sanitizer_syscall_post_swapon(res, specialfile, swap_flags) \ + __sanitizer_syscall_post_impl_swapon(res, (long)(specialfile), \ + (long)(swap_flags)) +#define __sanitizer_syscall_pre_swapoff(specialfile) \ + __sanitizer_syscall_pre_impl_swapoff((long)(specialfile)) +#define __sanitizer_syscall_post_swapoff(res, specialfile) \ + __sanitizer_syscall_post_impl_swapoff(res, (long)(specialfile)) +#define __sanitizer_syscall_pre_sysctl(args) \ + __sanitizer_syscall_pre_impl_sysctl((long)(args)) +#define __sanitizer_syscall_post_sysctl(res, args) \ + __sanitizer_syscall_post_impl_sysctl(res, (long)(args)) +#define __sanitizer_syscall_pre_sysinfo(info) \ + __sanitizer_syscall_pre_impl_sysinfo((long)(info)) +#define __sanitizer_syscall_post_sysinfo(res, info) \ + __sanitizer_syscall_post_impl_sysinfo(res, (long)(info)) +#define __sanitizer_syscall_pre_sysfs(option, arg1, arg2) \ + __sanitizer_syscall_pre_impl_sysfs((long)(option), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_sysfs(res, option, arg1, arg2) \ + __sanitizer_syscall_post_impl_sysfs(res, (long)(option), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_syslog(type, buf, len) \ + __sanitizer_syscall_pre_impl_syslog((long)(type), (long)(buf), (long)(len)) +#define __sanitizer_syscall_post_syslog(res, type, buf, len) \ + __sanitizer_syscall_post_impl_syslog(res, (long)(type), (long)(buf), \ + (long)(len)) +#define __sanitizer_syscall_pre_uselib(library) \ + __sanitizer_syscall_pre_impl_uselib((long)(library)) +#define __sanitizer_syscall_post_uselib(res, library) \ + __sanitizer_syscall_post_impl_uselib(res, (long)(library)) +#define __sanitizer_syscall_pre_ni_syscall() \ + __sanitizer_syscall_pre_impl_ni_syscall() +#define __sanitizer_syscall_post_ni_syscall(res) \ + __sanitizer_syscall_post_impl_ni_syscall(res) +#define __sanitizer_syscall_pre_ptrace(request, pid, addr, data) \ + __sanitizer_syscall_pre_impl_ptrace((long)(request), (long)(pid), \ + (long)(addr), (long)(data)) +#define __sanitizer_syscall_post_ptrace(res, request, pid, addr, data) \ + __sanitizer_syscall_post_impl_ptrace(res, (long)(request), (long)(pid), \ + (long)(addr), (long)(data)) +#define __sanitizer_syscall_pre_add_key(_type, _description, _payload, plen, \ + destringid) \ + __sanitizer_syscall_pre_impl_add_key((long)(_type), (long)(_description), \ + (long)(_payload), (long)(plen), \ + (long)(destringid)) +#define __sanitizer_syscall_post_add_key(res, _type, _description, _payload, \ + plen, destringid) \ + __sanitizer_syscall_post_impl_add_key( \ + res, (long)(_type), (long)(_description), (long)(_payload), \ + (long)(plen), (long)(destringid)) +#define __sanitizer_syscall_pre_request_key(_type, _description, \ + _callout_info, destringid) \ + __sanitizer_syscall_pre_impl_request_key( \ + (long)(_type), (long)(_description), (long)(_callout_info), \ + (long)(destringid)) +#define __sanitizer_syscall_post_request_key(res, _type, _description, \ + _callout_info, destringid) \ + __sanitizer_syscall_post_impl_request_key( \ + res, (long)(_type), (long)(_description), (long)(_callout_info), \ + (long)(destringid)) +#define __sanitizer_syscall_pre_keyctl(cmd, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_pre_impl_keyctl((long)(cmd), (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_post_keyctl(res, cmd, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_post_impl_keyctl(res, (long)(cmd), (long)(arg2), \ + (long)(arg3), (long)(arg4), \ + (long)(arg5)) +#define __sanitizer_syscall_pre_ioprio_set(which, who, ioprio) \ + __sanitizer_syscall_pre_impl_ioprio_set((long)(which), (long)(who), \ + (long)(ioprio)) +#define __sanitizer_syscall_post_ioprio_set(res, which, who, ioprio) \ + __sanitizer_syscall_post_impl_ioprio_set(res, (long)(which), (long)(who), \ + (long)(ioprio)) +#define __sanitizer_syscall_pre_ioprio_get(which, who) \ + __sanitizer_syscall_pre_impl_ioprio_get((long)(which), (long)(who)) +#define __sanitizer_syscall_post_ioprio_get(res, which, who) \ + __sanitizer_syscall_post_impl_ioprio_get(res, (long)(which), (long)(who)) +#define __sanitizer_syscall_pre_set_mempolicy(mode, nmask, maxnode) \ + __sanitizer_syscall_pre_impl_set_mempolicy((long)(mode), (long)(nmask), \ + (long)(maxnode)) +#define __sanitizer_syscall_post_set_mempolicy(res, mode, nmask, maxnode) \ + __sanitizer_syscall_post_impl_set_mempolicy(res, (long)(mode), \ + (long)(nmask), (long)(maxnode)) +#define __sanitizer_syscall_pre_migrate_pages(pid, maxnode, from, to) \ + __sanitizer_syscall_pre_impl_migrate_pages((long)(pid), (long)(maxnode), \ + (long)(from), (long)(to)) +#define __sanitizer_syscall_post_migrate_pages(res, pid, maxnode, from, to) \ + __sanitizer_syscall_post_impl_migrate_pages( \ + res, (long)(pid), (long)(maxnode), (long)(from), (long)(to)) +#define __sanitizer_syscall_pre_move_pages(pid, nr_pages, pages, nodes, \ + status, flags) \ + __sanitizer_syscall_pre_impl_move_pages((long)(pid), (long)(nr_pages), \ + (long)(pages), (long)(nodes), \ + (long)(status), (long)(flags)) +#define __sanitizer_syscall_post_move_pages(res, pid, nr_pages, pages, nodes, \ + status, flags) \ + __sanitizer_syscall_post_impl_move_pages(res, (long)(pid), (long)(nr_pages), \ + (long)(pages), (long)(nodes), \ + (long)(status), (long)(flags)) +#define __sanitizer_syscall_pre_mbind(start, len, mode, nmask, maxnode, flags) \ + __sanitizer_syscall_pre_impl_mbind((long)(start), (long)(len), (long)(mode), \ + (long)(nmask), (long)(maxnode), \ + (long)(flags)) +#define __sanitizer_syscall_post_mbind(res, start, len, mode, nmask, maxnode, \ + flags) \ + __sanitizer_syscall_post_impl_mbind(res, (long)(start), (long)(len), \ + (long)(mode), (long)(nmask), \ + (long)(maxnode), (long)(flags)) +#define __sanitizer_syscall_pre_get_mempolicy(policy, nmask, maxnode, addr, \ + flags) \ + __sanitizer_syscall_pre_impl_get_mempolicy((long)(policy), (long)(nmask), \ + (long)(maxnode), (long)(addr), \ + (long)(flags)) +#define __sanitizer_syscall_post_get_mempolicy(res, policy, nmask, maxnode, \ + addr, flags) \ + __sanitizer_syscall_post_impl_get_mempolicy(res, (long)(policy), \ + (long)(nmask), (long)(maxnode), \ + (long)(addr), (long)(flags)) +#define __sanitizer_syscall_pre_inotify_init() \ + __sanitizer_syscall_pre_impl_inotify_init() +#define __sanitizer_syscall_post_inotify_init(res) \ + __sanitizer_syscall_post_impl_inotify_init(res) +#define __sanitizer_syscall_pre_inotify_init1(flags) \ + __sanitizer_syscall_pre_impl_inotify_init1((long)(flags)) +#define __sanitizer_syscall_post_inotify_init1(res, flags) \ + __sanitizer_syscall_post_impl_inotify_init1(res, (long)(flags)) +#define __sanitizer_syscall_pre_inotify_add_watch(fd, path, mask) \ + __sanitizer_syscall_pre_impl_inotify_add_watch((long)(fd), (long)(path), \ + (long)(mask)) +#define __sanitizer_syscall_post_inotify_add_watch(res, fd, path, mask) \ + __sanitizer_syscall_post_impl_inotify_add_watch(res, (long)(fd), \ + (long)(path), (long)(mask)) +#define __sanitizer_syscall_pre_inotify_rm_watch(fd, wd) \ + __sanitizer_syscall_pre_impl_inotify_rm_watch((long)(fd), (long)(wd)) +#define __sanitizer_syscall_post_inotify_rm_watch(res, fd, wd) \ + __sanitizer_syscall_post_impl_inotify_rm_watch(res, (long)(fd), (long)(wd)) +#define __sanitizer_syscall_pre_spu_run(fd, unpc, ustatus) \ + __sanitizer_syscall_pre_impl_spu_run((long)(fd), (long)(unpc), \ + (long)(ustatus)) +#define __sanitizer_syscall_post_spu_run(res, fd, unpc, ustatus) \ + __sanitizer_syscall_post_impl_spu_run(res, (long)(fd), (long)(unpc), \ + (long)(ustatus)) +#define __sanitizer_syscall_pre_spu_create(name, flags, mode, fd) \ + __sanitizer_syscall_pre_impl_spu_create((long)(name), (long)(flags), \ + (long)(mode), (long)(fd)) +#define __sanitizer_syscall_post_spu_create(res, name, flags, mode, fd) \ + __sanitizer_syscall_post_impl_spu_create(res, (long)(name), (long)(flags), \ + (long)(mode), (long)(fd)) +#define __sanitizer_syscall_pre_mknodat(dfd, filename, mode, dev) \ + __sanitizer_syscall_pre_impl_mknodat((long)(dfd), (long)(filename), \ + (long)(mode), (long)(dev)) +#define __sanitizer_syscall_post_mknodat(res, dfd, filename, mode, dev) \ + __sanitizer_syscall_post_impl_mknodat(res, (long)(dfd), (long)(filename), \ + (long)(mode), (long)(dev)) +#define __sanitizer_syscall_pre_mkdirat(dfd, pathname, mode) \ + __sanitizer_syscall_pre_impl_mkdirat((long)(dfd), (long)(pathname), \ + (long)(mode)) +#define __sanitizer_syscall_post_mkdirat(res, dfd, pathname, mode) \ + __sanitizer_syscall_post_impl_mkdirat(res, (long)(dfd), (long)(pathname), \ + (long)(mode)) +#define __sanitizer_syscall_pre_unlinkat(dfd, pathname, flag) \ + __sanitizer_syscall_pre_impl_unlinkat((long)(dfd), (long)(pathname), \ + (long)(flag)) +#define __sanitizer_syscall_post_unlinkat(res, dfd, pathname, flag) \ + __sanitizer_syscall_post_impl_unlinkat(res, (long)(dfd), (long)(pathname), \ + (long)(flag)) +#define __sanitizer_syscall_pre_symlinkat(oldname, newdfd, newname) \ + __sanitizer_syscall_pre_impl_symlinkat((long)(oldname), (long)(newdfd), \ + (long)(newname)) +#define __sanitizer_syscall_post_symlinkat(res, oldname, newdfd, newname) \ + __sanitizer_syscall_post_impl_symlinkat(res, (long)(oldname), \ + (long)(newdfd), (long)(newname)) +#define __sanitizer_syscall_pre_linkat(olddfd, oldname, newdfd, newname, \ + flags) \ + __sanitizer_syscall_pre_impl_linkat((long)(olddfd), (long)(oldname), \ + (long)(newdfd), (long)(newname), \ + (long)(flags)) +#define __sanitizer_syscall_post_linkat(res, olddfd, oldname, newdfd, newname, \ + flags) \ + __sanitizer_syscall_post_impl_linkat(res, (long)(olddfd), (long)(oldname), \ + (long)(newdfd), (long)(newname), \ + (long)(flags)) +#define __sanitizer_syscall_pre_renameat(olddfd, oldname, newdfd, newname) \ + __sanitizer_syscall_pre_impl_renameat((long)(olddfd), (long)(oldname), \ + (long)(newdfd), (long)(newname)) +#define __sanitizer_syscall_post_renameat(res, olddfd, oldname, newdfd, \ + newname) \ + __sanitizer_syscall_post_impl_renameat(res, (long)(olddfd), (long)(oldname), \ + (long)(newdfd), (long)(newname)) +#define __sanitizer_syscall_pre_futimesat(dfd, filename, utimes) \ + __sanitizer_syscall_pre_impl_futimesat((long)(dfd), (long)(filename), \ + (long)(utimes)) +#define __sanitizer_syscall_post_futimesat(res, dfd, filename, utimes) \ + __sanitizer_syscall_post_impl_futimesat(res, (long)(dfd), (long)(filename), \ + (long)(utimes)) +#define __sanitizer_syscall_pre_faccessat(dfd, filename, mode) \ + __sanitizer_syscall_pre_impl_faccessat((long)(dfd), (long)(filename), \ + (long)(mode)) +#define __sanitizer_syscall_post_faccessat(res, dfd, filename, mode) \ + __sanitizer_syscall_post_impl_faccessat(res, (long)(dfd), (long)(filename), \ + (long)(mode)) +#define __sanitizer_syscall_pre_fchmodat(dfd, filename, mode) \ + __sanitizer_syscall_pre_impl_fchmodat((long)(dfd), (long)(filename), \ + (long)(mode)) +#define __sanitizer_syscall_post_fchmodat(res, dfd, filename, mode) \ + __sanitizer_syscall_post_impl_fchmodat(res, (long)(dfd), (long)(filename), \ + (long)(mode)) +#define __sanitizer_syscall_pre_fchownat(dfd, filename, user, group, flag) \ + __sanitizer_syscall_pre_impl_fchownat((long)(dfd), (long)(filename), \ + (long)(user), (long)(group), \ + (long)(flag)) +#define __sanitizer_syscall_post_fchownat(res, dfd, filename, user, group, \ + flag) \ + __sanitizer_syscall_post_impl_fchownat(res, (long)(dfd), (long)(filename), \ + (long)(user), (long)(group), \ + (long)(flag)) +#define __sanitizer_syscall_pre_openat(dfd, filename, flags, mode) \ + __sanitizer_syscall_pre_impl_openat((long)(dfd), (long)(filename), \ + (long)(flags), (long)(mode)) +#define __sanitizer_syscall_post_openat(res, dfd, filename, flags, mode) \ + __sanitizer_syscall_post_impl_openat(res, (long)(dfd), (long)(filename), \ + (long)(flags), (long)(mode)) +#define __sanitizer_syscall_pre_newfstatat(dfd, filename, statbuf, flag) \ + __sanitizer_syscall_pre_impl_newfstatat((long)(dfd), (long)(filename), \ + (long)(statbuf), (long)(flag)) +#define __sanitizer_syscall_post_newfstatat(res, dfd, filename, statbuf, flag) \ + __sanitizer_syscall_post_impl_newfstatat(res, (long)(dfd), (long)(filename), \ + (long)(statbuf), (long)(flag)) +#define __sanitizer_syscall_pre_fstatat64(dfd, filename, statbuf, flag) \ + __sanitizer_syscall_pre_impl_fstatat64((long)(dfd), (long)(filename), \ + (long)(statbuf), (long)(flag)) +#define __sanitizer_syscall_post_fstatat64(res, dfd, filename, statbuf, flag) \ + __sanitizer_syscall_post_impl_fstatat64(res, (long)(dfd), (long)(filename), \ + (long)(statbuf), (long)(flag)) +#define __sanitizer_syscall_pre_readlinkat(dfd, path, buf, bufsiz) \ + __sanitizer_syscall_pre_impl_readlinkat((long)(dfd), (long)(path), \ + (long)(buf), (long)(bufsiz)) +#define __sanitizer_syscall_post_readlinkat(res, dfd, path, buf, bufsiz) \ + __sanitizer_syscall_post_impl_readlinkat(res, (long)(dfd), (long)(path), \ + (long)(buf), (long)(bufsiz)) +#define __sanitizer_syscall_pre_utimensat(dfd, filename, utimes, flags) \ + __sanitizer_syscall_pre_impl_utimensat((long)(dfd), (long)(filename), \ + (long)(utimes), (long)(flags)) +#define __sanitizer_syscall_post_utimensat(res, dfd, filename, utimes, flags) \ + __sanitizer_syscall_post_impl_utimensat(res, (long)(dfd), (long)(filename), \ + (long)(utimes), (long)(flags)) +#define __sanitizer_syscall_pre_unshare(unshare_flags) \ + __sanitizer_syscall_pre_impl_unshare((long)(unshare_flags)) +#define __sanitizer_syscall_post_unshare(res, unshare_flags) \ + __sanitizer_syscall_post_impl_unshare(res, (long)(unshare_flags)) +#define __sanitizer_syscall_pre_splice(fd_in, off_in, fd_out, off_out, len, \ + flags) \ + __sanitizer_syscall_pre_impl_splice((long)(fd_in), (long)(off_in), \ + (long)(fd_out), (long)(off_out), \ + (long)(len), (long)(flags)) +#define __sanitizer_syscall_post_splice(res, fd_in, off_in, fd_out, off_out, \ + len, flags) \ + __sanitizer_syscall_post_impl_splice(res, (long)(fd_in), (long)(off_in), \ + (long)(fd_out), (long)(off_out), \ + (long)(len), (long)(flags)) +#define __sanitizer_syscall_pre_vmsplice(fd, iov, nr_segs, flags) \ + __sanitizer_syscall_pre_impl_vmsplice((long)(fd), (long)(iov), \ + (long)(nr_segs), (long)(flags)) +#define __sanitizer_syscall_post_vmsplice(res, fd, iov, nr_segs, flags) \ + __sanitizer_syscall_post_impl_vmsplice(res, (long)(fd), (long)(iov), \ + (long)(nr_segs), (long)(flags)) +#define __sanitizer_syscall_pre_tee(fdin, fdout, len, flags) \ + __sanitizer_syscall_pre_impl_tee((long)(fdin), (long)(fdout), (long)(len), \ + (long)(flags)) +#define __sanitizer_syscall_post_tee(res, fdin, fdout, len, flags) \ + __sanitizer_syscall_post_impl_tee(res, (long)(fdin), (long)(fdout), \ + (long)(len), (long)(flags)) +#define __sanitizer_syscall_pre_get_robust_list(pid, head_ptr, len_ptr) \ + __sanitizer_syscall_pre_impl_get_robust_list((long)(pid), (long)(head_ptr), \ + (long)(len_ptr)) +#define __sanitizer_syscall_post_get_robust_list(res, pid, head_ptr, len_ptr) \ + __sanitizer_syscall_post_impl_get_robust_list( \ + res, (long)(pid), (long)(head_ptr), (long)(len_ptr)) +#define __sanitizer_syscall_pre_set_robust_list(head, len) \ + __sanitizer_syscall_pre_impl_set_robust_list((long)(head), (long)(len)) +#define __sanitizer_syscall_post_set_robust_list(res, head, len) \ + __sanitizer_syscall_post_impl_set_robust_list(res, (long)(head), (long)(len)) +#define __sanitizer_syscall_pre_getcpu(cpu, node, cache) \ + __sanitizer_syscall_pre_impl_getcpu((long)(cpu), (long)(node), (long)(cache)) +#define __sanitizer_syscall_post_getcpu(res, cpu, node, cache) \ + __sanitizer_syscall_post_impl_getcpu(res, (long)(cpu), (long)(node), \ + (long)(cache)) +#define __sanitizer_syscall_pre_signalfd(ufd, user_mask, sizemask) \ + __sanitizer_syscall_pre_impl_signalfd((long)(ufd), (long)(user_mask), \ + (long)(sizemask)) +#define __sanitizer_syscall_post_signalfd(res, ufd, user_mask, sizemask) \ + __sanitizer_syscall_post_impl_signalfd(res, (long)(ufd), (long)(user_mask), \ + (long)(sizemask)) +#define __sanitizer_syscall_pre_signalfd4(ufd, user_mask, sizemask, flags) \ + __sanitizer_syscall_pre_impl_signalfd4((long)(ufd), (long)(user_mask), \ + (long)(sizemask), (long)(flags)) +#define __sanitizer_syscall_post_signalfd4(res, ufd, user_mask, sizemask, \ + flags) \ + __sanitizer_syscall_post_impl_signalfd4(res, (long)(ufd), (long)(user_mask), \ + (long)(sizemask), (long)(flags)) +#define __sanitizer_syscall_pre_timerfd_create(clockid, flags) \ + __sanitizer_syscall_pre_impl_timerfd_create((long)(clockid), (long)(flags)) +#define __sanitizer_syscall_post_timerfd_create(res, clockid, flags) \ + __sanitizer_syscall_post_impl_timerfd_create(res, (long)(clockid), \ + (long)(flags)) +#define __sanitizer_syscall_pre_timerfd_settime(ufd, flags, utmr, otmr) \ + __sanitizer_syscall_pre_impl_timerfd_settime((long)(ufd), (long)(flags), \ + (long)(utmr), (long)(otmr)) +#define __sanitizer_syscall_post_timerfd_settime(res, ufd, flags, utmr, otmr) \ + __sanitizer_syscall_post_impl_timerfd_settime( \ + res, (long)(ufd), (long)(flags), (long)(utmr), (long)(otmr)) +#define __sanitizer_syscall_pre_timerfd_gettime(ufd, otmr) \ + __sanitizer_syscall_pre_impl_timerfd_gettime((long)(ufd), (long)(otmr)) +#define __sanitizer_syscall_post_timerfd_gettime(res, ufd, otmr) \ + __sanitizer_syscall_post_impl_timerfd_gettime(res, (long)(ufd), (long)(otmr)) +#define __sanitizer_syscall_pre_eventfd(count) \ + __sanitizer_syscall_pre_impl_eventfd((long)(count)) +#define __sanitizer_syscall_post_eventfd(res, count) \ + __sanitizer_syscall_post_impl_eventfd(res, (long)(count)) +#define __sanitizer_syscall_pre_eventfd2(count, flags) \ + __sanitizer_syscall_pre_impl_eventfd2((long)(count), (long)(flags)) +#define __sanitizer_syscall_post_eventfd2(res, count, flags) \ + __sanitizer_syscall_post_impl_eventfd2(res, (long)(count), (long)(flags)) +#define __sanitizer_syscall_pre_old_readdir(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_old_readdir((long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_post_old_readdir(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_old_readdir(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_pselect6(arg0, arg1, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_pre_impl_pselect6((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_post_pselect6(res, arg0, arg1, arg2, arg3, arg4, \ + arg5) \ + __sanitizer_syscall_post_impl_pselect6(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_pre_ppoll(arg0, arg1, arg2, arg3, arg4) \ + __sanitizer_syscall_pre_impl_ppoll((long)(arg0), (long)(arg1), (long)(arg2), \ + (long)(arg3), (long)(arg4)) +#define __sanitizer_syscall_post_ppoll(res, arg0, arg1, arg2, arg3, arg4) \ + __sanitizer_syscall_post_impl_ppoll(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4)) +#define __sanitizer_syscall_pre_syncfs(fd) \ + __sanitizer_syscall_pre_impl_syncfs((long)(fd)) +#define __sanitizer_syscall_post_syncfs(res, fd) \ + __sanitizer_syscall_post_impl_syncfs(res, (long)(fd)) +#define __sanitizer_syscall_pre_perf_event_open(attr_uptr, pid, cpu, group_fd, \ + flags) \ + __sanitizer_syscall_pre_impl_perf_event_open((long)(attr_uptr), (long)(pid), \ + (long)(cpu), (long)(group_fd), \ + (long)(flags)) +#define __sanitizer_syscall_post_perf_event_open(res, attr_uptr, pid, cpu, \ + group_fd, flags) \ + __sanitizer_syscall_post_impl_perf_event_open( \ + res, (long)(attr_uptr), (long)(pid), (long)(cpu), (long)(group_fd), \ + (long)(flags)) +#define __sanitizer_syscall_pre_mmap_pgoff(addr, len, prot, flags, fd, pgoff) \ + __sanitizer_syscall_pre_impl_mmap_pgoff((long)(addr), (long)(len), \ + (long)(prot), (long)(flags), \ + (long)(fd), (long)(pgoff)) +#define __sanitizer_syscall_post_mmap_pgoff(res, addr, len, prot, flags, fd, \ + pgoff) \ + __sanitizer_syscall_post_impl_mmap_pgoff(res, (long)(addr), (long)(len), \ + (long)(prot), (long)(flags), \ + (long)(fd), (long)(pgoff)) +#define __sanitizer_syscall_pre_old_mmap(arg) \ + __sanitizer_syscall_pre_impl_old_mmap((long)(arg)) +#define __sanitizer_syscall_post_old_mmap(res, arg) \ + __sanitizer_syscall_post_impl_old_mmap(res, (long)(arg)) +#define __sanitizer_syscall_pre_name_to_handle_at(dfd, name, handle, mnt_id, \ + flag) \ + __sanitizer_syscall_pre_impl_name_to_handle_at( \ + (long)(dfd), (long)(name), (long)(handle), (long)(mnt_id), (long)(flag)) +#define __sanitizer_syscall_post_name_to_handle_at(res, dfd, name, handle, \ + mnt_id, flag) \ + __sanitizer_syscall_post_impl_name_to_handle_at( \ + res, (long)(dfd), (long)(name), (long)(handle), (long)(mnt_id), \ + (long)(flag)) +#define __sanitizer_syscall_pre_open_by_handle_at(mountdirfd, handle, flags) \ + __sanitizer_syscall_pre_impl_open_by_handle_at( \ + (long)(mountdirfd), (long)(handle), (long)(flags)) +#define __sanitizer_syscall_post_open_by_handle_at(res, mountdirfd, handle, \ + flags) \ + __sanitizer_syscall_post_impl_open_by_handle_at( \ + res, (long)(mountdirfd), (long)(handle), (long)(flags)) +#define __sanitizer_syscall_pre_setns(fd, nstype) \ + __sanitizer_syscall_pre_impl_setns((long)(fd), (long)(nstype)) +#define __sanitizer_syscall_post_setns(res, fd, nstype) \ + __sanitizer_syscall_post_impl_setns(res, (long)(fd), (long)(nstype)) +#define __sanitizer_syscall_pre_process_vm_readv(pid, lvec, liovcnt, rvec, \ + riovcnt, flags) \ + __sanitizer_syscall_pre_impl_process_vm_readv( \ + (long)(pid), (long)(lvec), (long)(liovcnt), (long)(rvec), \ + (long)(riovcnt), (long)(flags)) +#define __sanitizer_syscall_post_process_vm_readv(res, pid, lvec, liovcnt, \ + rvec, riovcnt, flags) \ + __sanitizer_syscall_post_impl_process_vm_readv( \ + res, (long)(pid), (long)(lvec), (long)(liovcnt), (long)(rvec), \ + (long)(riovcnt), (long)(flags)) +#define __sanitizer_syscall_pre_process_vm_writev(pid, lvec, liovcnt, rvec, \ + riovcnt, flags) \ + __sanitizer_syscall_pre_impl_process_vm_writev( \ + (long)(pid), (long)(lvec), (long)(liovcnt), (long)(rvec), \ + (long)(riovcnt), (long)(flags)) +#define __sanitizer_syscall_post_process_vm_writev(res, pid, lvec, liovcnt, \ + rvec, riovcnt, flags) \ + __sanitizer_syscall_post_impl_process_vm_writev( \ + res, (long)(pid), (long)(lvec), (long)(liovcnt), (long)(rvec), \ + (long)(riovcnt), (long)(flags)) +#define __sanitizer_syscall_pre_fork() \ + __sanitizer_syscall_pre_impl_fork() +#define __sanitizer_syscall_post_fork(res) \ + __sanitizer_syscall_post_impl_fork(res) +#define __sanitizer_syscall_pre_vfork() \ + __sanitizer_syscall_pre_impl_vfork() +#define __sanitizer_syscall_post_vfork(res) \ + __sanitizer_syscall_post_impl_vfork(res) // And now a few syscalls we don't handle yet. - -#define __sanitizer_syscall_pre_accept(...) -#define __sanitizer_syscall_pre_accept4(...) -#define __sanitizer_syscall_pre_access(...) -#define __sanitizer_syscall_pre_acct(...) -#define __sanitizer_syscall_pre_add_key(...) -#define __sanitizer_syscall_pre_adjtimex(...) #define __sanitizer_syscall_pre_afs_syscall(...) -#define __sanitizer_syscall_pre_alarm(...) #define __sanitizer_syscall_pre_arch_prctl(...) -#define __sanitizer_syscall_pre_bdflush(...) -#define __sanitizer_syscall_pre_bind(...) #define __sanitizer_syscall_pre_break(...) -#define __sanitizer_syscall_pre_brk(...) -#define __sanitizer_syscall_pre_capget(...) -#define __sanitizer_syscall_pre_capset(...) -#define __sanitizer_syscall_pre_chdir(...) -#define __sanitizer_syscall_pre_chmod(...) -#define __sanitizer_syscall_pre_chown(...) #define __sanitizer_syscall_pre_chown32(...) -#define __sanitizer_syscall_pre_chroot(...) -#define __sanitizer_syscall_pre_clock_adjtime(...) -#define __sanitizer_syscall_pre_clock_getres(...) -#define __sanitizer_syscall_pre_clock_gettime(...) -#define __sanitizer_syscall_pre_clock_nanosleep(...) -#define __sanitizer_syscall_pre_clock_settime(...) #define __sanitizer_syscall_pre_clone(...) -#define __sanitizer_syscall_pre_close(...) -#define __sanitizer_syscall_pre_connect(...) -#define __sanitizer_syscall_pre_creat(...) #define __sanitizer_syscall_pre_create_module(...) -#define __sanitizer_syscall_pre_delete_module(...) -#define __sanitizer_syscall_pre_dup(...) -#define __sanitizer_syscall_pre_dup2(...) -#define __sanitizer_syscall_pre_dup3(...) -#define __sanitizer_syscall_pre_epoll_create(...) -#define __sanitizer_syscall_pre_epoll_create1(...) -#define __sanitizer_syscall_pre_epoll_ctl(...) #define __sanitizer_syscall_pre_epoll_ctl_old(...) -#define __sanitizer_syscall_pre_epoll_pwait(...) -#define __sanitizer_syscall_pre_epoll_wait(...) #define __sanitizer_syscall_pre_epoll_wait_old(...) -#define __sanitizer_syscall_pre_eventfd(...) -#define __sanitizer_syscall_pre_eventfd2(...) #define __sanitizer_syscall_pre_execve(...) -#define __sanitizer_syscall_pre_exit(...) -#define __sanitizer_syscall_pre_exit_group(...) -#define __sanitizer_syscall_pre_faccessat(...) #define __sanitizer_syscall_pre_fadvise64(...) #define __sanitizer_syscall_pre_fadvise64_64(...) #define __sanitizer_syscall_pre_fallocate(...) #define __sanitizer_syscall_pre_fanotify_init(...) #define __sanitizer_syscall_pre_fanotify_mark(...) -#define __sanitizer_syscall_pre_fchdir(...) -#define __sanitizer_syscall_pre_fchmod(...) -#define __sanitizer_syscall_pre_fchmodat(...) -#define __sanitizer_syscall_pre_fchown(...) #define __sanitizer_syscall_pre_fchown32(...) -#define __sanitizer_syscall_pre_fchownat(...) -#define __sanitizer_syscall_pre_fcntl(...) -#define __sanitizer_syscall_pre_fcntl64(...) -#define __sanitizer_syscall_pre_fdatasync(...) -#define __sanitizer_syscall_pre_fgetxattr(...) -#define __sanitizer_syscall_pre_flistxattr(...) -#define __sanitizer_syscall_pre_flock(...) -#define __sanitizer_syscall_pre_fork(...) -#define __sanitizer_syscall_pre_fremovexattr(...) -#define __sanitizer_syscall_pre_fsetxattr(...) -#define __sanitizer_syscall_pre_fstat(...) -#define __sanitizer_syscall_pre_fstat64(...) -#define __sanitizer_syscall_pre_fstatat64(...) -#define __sanitizer_syscall_pre_fstatfs(...) -#define __sanitizer_syscall_pre_fstatfs64(...) -#define __sanitizer_syscall_pre_fsync(...) #define __sanitizer_syscall_pre_ftime(...) -#define __sanitizer_syscall_pre_ftruncate(...) #define __sanitizer_syscall_pre_ftruncate64(...) #define __sanitizer_syscall_pre_futex(...) -#define __sanitizer_syscall_pre_futimesat(...) -#define __sanitizer_syscall_pre_getcpu(...) -#define __sanitizer_syscall_pre_getcwd(...) -#define __sanitizer_syscall_pre_getegid(...) #define __sanitizer_syscall_pre_getegid32(...) -#define __sanitizer_syscall_pre_geteuid(...) #define __sanitizer_syscall_pre_geteuid32(...) -#define __sanitizer_syscall_pre_getgid(...) #define __sanitizer_syscall_pre_getgid32(...) -#define __sanitizer_syscall_pre_getgroups(...) #define __sanitizer_syscall_pre_getgroups32(...) -#define __sanitizer_syscall_pre_getitimer(...) #define __sanitizer_syscall_pre_get_kernel_syms(...) -#define __sanitizer_syscall_pre_get_mempolicy(...) -#define __sanitizer_syscall_pre_getpeername(...) -#define __sanitizer_syscall_pre_getpgid(...) -#define __sanitizer_syscall_pre_getpgrp(...) -#define __sanitizer_syscall_pre_getpid(...) #define __sanitizer_syscall_pre_getpmsg(...) -#define __sanitizer_syscall_pre_getppid(...) -#define __sanitizer_syscall_pre_getpriority(...) -#define __sanitizer_syscall_pre_getresgid(...) #define __sanitizer_syscall_pre_getresgid32(...) -#define __sanitizer_syscall_pre_getresuid(...) #define __sanitizer_syscall_pre_getresuid32(...) -#define __sanitizer_syscall_pre_getrlimit(...) -#define __sanitizer_syscall_pre_get_robust_list(...) -#define __sanitizer_syscall_pre_getrusage(...) -#define __sanitizer_syscall_pre_getsid(...) -#define __sanitizer_syscall_pre_getsockname(...) -#define __sanitizer_syscall_pre_getsockopt(...) #define __sanitizer_syscall_pre_get_thread_area(...) -#define __sanitizer_syscall_pre_gettid(...) -#define __sanitizer_syscall_pre_gettimeofday(...) -#define __sanitizer_syscall_pre_getuid(...) #define __sanitizer_syscall_pre_getuid32(...) -#define __sanitizer_syscall_pre_getxattr(...) #define __sanitizer_syscall_pre_gtty(...) #define __sanitizer_syscall_pre_idle(...) -#define __sanitizer_syscall_pre_init_module(...) -#define __sanitizer_syscall_pre_inotify_add_watch(...) -#define __sanitizer_syscall_pre_inotify_init(...) -#define __sanitizer_syscall_pre_inotify_init1(...) -#define __sanitizer_syscall_pre_inotify_rm_watch(...) -#define __sanitizer_syscall_pre_io_cancel(...) -#define __sanitizer_syscall_pre_ioctl(...) -#define __sanitizer_syscall_pre_io_destroy(...) -#define __sanitizer_syscall_pre_io_getevents(...) -#define __sanitizer_syscall_pre_ioperm(...) #define __sanitizer_syscall_pre_iopl(...) -#define __sanitizer_syscall_pre_ioprio_get(...) -#define __sanitizer_syscall_pre_ioprio_set(...) -#define __sanitizer_syscall_pre_io_setup(...) -#define __sanitizer_syscall_pre_io_submit(...) -#define __sanitizer_syscall_pre_ipc(...) -#define __sanitizer_syscall_pre_kexec_load(...) -#define __sanitizer_syscall_pre_keyctl(...) -#define __sanitizer_syscall_pre_kill(...) -#define __sanitizer_syscall_pre_lchown(...) #define __sanitizer_syscall_pre_lchown32(...) -#define __sanitizer_syscall_pre_lgetxattr(...) -#define __sanitizer_syscall_pre_link(...) -#define __sanitizer_syscall_pre_linkat(...) -#define __sanitizer_syscall_pre_listen(...) -#define __sanitizer_syscall_pre_listxattr(...) -#define __sanitizer_syscall_pre_llistxattr(...) #define __sanitizer_syscall_pre__llseek(...) #define __sanitizer_syscall_pre_lock(...) -#define __sanitizer_syscall_pre_lookup_dcookie(...) -#define __sanitizer_syscall_pre_lremovexattr(...) -#define __sanitizer_syscall_pre_lseek(...) -#define __sanitizer_syscall_pre_lsetxattr(...) -#define __sanitizer_syscall_pre_lstat(...) -#define __sanitizer_syscall_pre_lstat64(...) -#define __sanitizer_syscall_pre_madvise(...) #define __sanitizer_syscall_pre_madvise1(...) -#define __sanitizer_syscall_pre_mbind(...) -#define __sanitizer_syscall_pre_migrate_pages(...) -#define __sanitizer_syscall_pre_mincore(...) -#define __sanitizer_syscall_pre_mkdir(...) -#define __sanitizer_syscall_pre_mkdirat(...) -#define __sanitizer_syscall_pre_mknod(...) -#define __sanitizer_syscall_pre_mknodat(...) -#define __sanitizer_syscall_pre_mlock(...) -#define __sanitizer_syscall_pre_mlockall(...) #define __sanitizer_syscall_pre_mmap(...) #define __sanitizer_syscall_pre_mmap2(...) #define __sanitizer_syscall_pre_modify_ldt(...) -#define __sanitizer_syscall_pre_mount(...) -#define __sanitizer_syscall_pre_move_pages(...) -#define __sanitizer_syscall_pre_mprotect(...) #define __sanitizer_syscall_pre_mpx(...) -#define __sanitizer_syscall_pre_mq_getsetattr(...) -#define __sanitizer_syscall_pre_mq_notify(...) -#define __sanitizer_syscall_pre_mq_open(...) -#define __sanitizer_syscall_pre_mq_timedreceive(...) -#define __sanitizer_syscall_pre_mq_timedsend(...) -#define __sanitizer_syscall_pre_mq_unlink(...) -#define __sanitizer_syscall_pre_mremap(...) -#define __sanitizer_syscall_pre_msgctl(...) -#define __sanitizer_syscall_pre_msgget(...) -#define __sanitizer_syscall_pre_msgrcv(...) -#define __sanitizer_syscall_pre_msgsnd(...) -#define __sanitizer_syscall_pre_msync(...) -#define __sanitizer_syscall_pre_munlock(...) -#define __sanitizer_syscall_pre_munlockall(...) -#define __sanitizer_syscall_pre_munmap(...) -#define __sanitizer_syscall_pre_name_to_handle_at(...) -#define __sanitizer_syscall_pre_nanosleep(...) -#define __sanitizer_syscall_pre_newfstatat(...) #define __sanitizer_syscall_pre__newselect(...) #define __sanitizer_syscall_pre_nfsservctl(...) -#define __sanitizer_syscall_pre_nice(...) #define __sanitizer_syscall_pre_oldfstat(...) #define __sanitizer_syscall_pre_oldlstat(...) #define __sanitizer_syscall_pre_oldolduname(...) #define __sanitizer_syscall_pre_oldstat(...) -#define __sanitizer_syscall_pre_olduname(...) -#define __sanitizer_syscall_pre_open(...) -#define __sanitizer_syscall_pre_openat(...) -#define __sanitizer_syscall_pre_open_by_handle_at(...) -#define __sanitizer_syscall_pre_pause(...) -#define __sanitizer_syscall_pre_perf_event_open(...) -#define __sanitizer_syscall_pre_personality(...) -#define __sanitizer_syscall_pre_pipe(...) -#define __sanitizer_syscall_pre_pipe2(...) -#define __sanitizer_syscall_pre_pivot_root(...) -#define __sanitizer_syscall_pre_poll(...) -#define __sanitizer_syscall_pre_ppoll(...) #define __sanitizer_syscall_pre_prctl(...) -#define __sanitizer_syscall_pre_pread64(...) -#define __sanitizer_syscall_pre_preadv(...) -#define __sanitizer_syscall_pre_prlimit64(...) -#define __sanitizer_syscall_pre_process_vm_readv(...) -#define __sanitizer_syscall_pre_process_vm_writev(...) #define __sanitizer_syscall_pre_prof(...) #define __sanitizer_syscall_pre_profil(...) -#define __sanitizer_syscall_pre_pselect6(...) -#define __sanitizer_syscall_pre_ptrace(...) #define __sanitizer_syscall_pre_putpmsg(...) -#define __sanitizer_syscall_pre_pwrite64(...) -#define __sanitizer_syscall_pre_pwritev(...) #define __sanitizer_syscall_pre_query_module(...) -#define __sanitizer_syscall_pre_quotactl(...) -#define __sanitizer_syscall_pre_read(...) #define __sanitizer_syscall_pre_readahead(...) #define __sanitizer_syscall_pre_readdir(...) -#define __sanitizer_syscall_pre_readlink(...) -#define __sanitizer_syscall_pre_readlinkat(...) -#define __sanitizer_syscall_pre_readv(...) -#define __sanitizer_syscall_pre_reboot(...) -#define __sanitizer_syscall_pre_recvfrom(...) -#define __sanitizer_syscall_pre_recvmmsg(...) -#define __sanitizer_syscall_pre_remap_file_pages(...) -#define __sanitizer_syscall_pre_removexattr(...) -#define __sanitizer_syscall_pre_rename(...) -#define __sanitizer_syscall_pre_renameat(...) -#define __sanitizer_syscall_pre_request_key(...) -#define __sanitizer_syscall_pre_restart_syscall(...) -#define __sanitizer_syscall_pre_rmdir(...) #define __sanitizer_syscall_pre_rt_sigaction(...) -#define __sanitizer_syscall_pre_rt_sigprocmask(...) -#define __sanitizer_syscall_pre_rt_sigqueueinfo(...) #define __sanitizer_syscall_pre_rt_sigreturn(...) #define __sanitizer_syscall_pre_rt_sigsuspend(...) -#define __sanitizer_syscall_pre_rt_sigtimedwait(...) -#define __sanitizer_syscall_pre_rt_tgsigqueueinfo(...) -#define __sanitizer_syscall_pre_sched_getaffinity(...) -#define __sanitizer_syscall_pre_sched_getparam(...) -#define __sanitizer_syscall_pre_sched_get_priority_max(...) -#define __sanitizer_syscall_pre_sched_get_priority_min(...) -#define __sanitizer_syscall_pre_sched_getscheduler(...) -#define __sanitizer_syscall_pre_sched_rr_get_interval(...) -#define __sanitizer_syscall_pre_sched_setaffinity(...) -#define __sanitizer_syscall_pre_sched_setparam(...) -#define __sanitizer_syscall_pre_sched_setscheduler(...) -#define __sanitizer_syscall_pre_sched_yield(...) #define __sanitizer_syscall_pre_security(...) -#define __sanitizer_syscall_pre_select(...) -#define __sanitizer_syscall_pre_semctl(...) -#define __sanitizer_syscall_pre_semget(...) -#define __sanitizer_syscall_pre_semop(...) -#define __sanitizer_syscall_pre_semtimedop(...) -#define __sanitizer_syscall_pre_sendfile(...) -#define __sanitizer_syscall_pre_sendfile64(...) -#define __sanitizer_syscall_pre_sendmmsg(...) -#define __sanitizer_syscall_pre_sendmsg(...) -#define __sanitizer_syscall_pre_sendto(...) -#define __sanitizer_syscall_pre_setdomainname(...) -#define __sanitizer_syscall_pre_setfsgid(...) #define __sanitizer_syscall_pre_setfsgid32(...) -#define __sanitizer_syscall_pre_setfsuid(...) #define __sanitizer_syscall_pre_setfsuid32(...) -#define __sanitizer_syscall_pre_setgid(...) #define __sanitizer_syscall_pre_setgid32(...) -#define __sanitizer_syscall_pre_setgroups(...) #define __sanitizer_syscall_pre_setgroups32(...) -#define __sanitizer_syscall_pre_sethostname(...) -#define __sanitizer_syscall_pre_setitimer(...) -#define __sanitizer_syscall_pre_set_mempolicy(...) -#define __sanitizer_syscall_pre_setns(...) -#define __sanitizer_syscall_pre_setpgid(...) -#define __sanitizer_syscall_pre_setpriority(...) -#define __sanitizer_syscall_pre_setregid(...) #define __sanitizer_syscall_pre_setregid32(...) -#define __sanitizer_syscall_pre_setresgid(...) #define __sanitizer_syscall_pre_setresgid32(...) -#define __sanitizer_syscall_pre_setresuid(...) #define __sanitizer_syscall_pre_setresuid32(...) -#define __sanitizer_syscall_pre_setreuid(...) #define __sanitizer_syscall_pre_setreuid32(...) -#define __sanitizer_syscall_pre_setrlimit(...) -#define __sanitizer_syscall_pre_set_robust_list(...) -#define __sanitizer_syscall_pre_setsid(...) -#define __sanitizer_syscall_pre_setsockopt(...) #define __sanitizer_syscall_pre_set_thread_area(...) -#define __sanitizer_syscall_pre_set_tid_address(...) -#define __sanitizer_syscall_pre_settimeofday(...) -#define __sanitizer_syscall_pre_setuid(...) #define __sanitizer_syscall_pre_setuid32(...) -#define __sanitizer_syscall_pre_setxattr(...) -#define __sanitizer_syscall_pre_sgetmask(...) -#define __sanitizer_syscall_pre_shmat(...) -#define __sanitizer_syscall_pre_shmctl(...) -#define __sanitizer_syscall_pre_shmdt(...) -#define __sanitizer_syscall_pre_shmget(...) -#define __sanitizer_syscall_pre_shutdown(...) #define __sanitizer_syscall_pre_sigaction(...) #define __sanitizer_syscall_pre_sigaltstack(...) -#define __sanitizer_syscall_pre_signal(...) -#define __sanitizer_syscall_pre_signalfd(...) -#define __sanitizer_syscall_pre_signalfd4(...) -#define __sanitizer_syscall_pre_sigpending(...) -#define __sanitizer_syscall_pre_sigprocmask(...) #define __sanitizer_syscall_pre_sigreturn(...) #define __sanitizer_syscall_pre_sigsuspend(...) -#define __sanitizer_syscall_pre_socket(...) -#define __sanitizer_syscall_pre_socketcall(...) -#define __sanitizer_syscall_pre_socketpair(...) -#define __sanitizer_syscall_pre_splice(...) -#define __sanitizer_syscall_pre_ssetmask(...) -#define __sanitizer_syscall_pre_stat(...) -#define __sanitizer_syscall_pre_stat64(...) -#define __sanitizer_syscall_pre_statfs(...) -#define __sanitizer_syscall_pre_statfs64(...) -#define __sanitizer_syscall_pre_stime(...) #define __sanitizer_syscall_pre_stty(...) -#define __sanitizer_syscall_pre_swapoff(...) -#define __sanitizer_syscall_pre_swapon(...) -#define __sanitizer_syscall_pre_symlink(...) -#define __sanitizer_syscall_pre_symlinkat(...) -#define __sanitizer_syscall_pre_sync(...) #define __sanitizer_syscall_pre_sync_file_range(...) -#define __sanitizer_syscall_pre_syncfs(...) #define __sanitizer_syscall_pre__sysctl(...) -#define __sanitizer_syscall_pre_sysfs(...) -#define __sanitizer_syscall_pre_sysinfo(...) -#define __sanitizer_syscall_pre_syslog(...) -#define __sanitizer_syscall_pre_tee(...) -#define __sanitizer_syscall_pre_tgkill(...) -#define __sanitizer_syscall_pre_time(...) -#define __sanitizer_syscall_pre_timer_create(...) -#define __sanitizer_syscall_pre_timer_delete(...) -#define __sanitizer_syscall_pre_timerfd_create(...) -#define __sanitizer_syscall_pre_timerfd_gettime(...) -#define __sanitizer_syscall_pre_timerfd_settime(...) -#define __sanitizer_syscall_pre_timer_getoverrun(...) -#define __sanitizer_syscall_pre_timer_gettime(...) -#define __sanitizer_syscall_pre_timer_settime(...) -#define __sanitizer_syscall_pre_times(...) -#define __sanitizer_syscall_pre_tkill(...) -#define __sanitizer_syscall_pre_truncate(...) #define __sanitizer_syscall_pre_truncate64(...) #define __sanitizer_syscall_pre_tuxcall(...) #define __sanitizer_syscall_pre_ugetrlimit(...) #define __sanitizer_syscall_pre_ulimit(...) -#define __sanitizer_syscall_pre_umask(...) -#define __sanitizer_syscall_pre_umount(...) #define __sanitizer_syscall_pre_umount2(...) -#define __sanitizer_syscall_pre_uname(...) -#define __sanitizer_syscall_pre_unlink(...) -#define __sanitizer_syscall_pre_unlinkat(...) -#define __sanitizer_syscall_pre_unshare(...) -#define __sanitizer_syscall_pre_uselib(...) -#define __sanitizer_syscall_pre_ustat(...) -#define __sanitizer_syscall_pre_utime(...) -#define __sanitizer_syscall_pre_utimensat(...) -#define __sanitizer_syscall_pre_utimes(...) -#define __sanitizer_syscall_pre_vfork(...) -#define __sanitizer_syscall_pre_vhangup(...) #define __sanitizer_syscall_pre_vm86(...) #define __sanitizer_syscall_pre_vm86old(...) -#define __sanitizer_syscall_pre_vmsplice(...) #define __sanitizer_syscall_pre_vserver(...) -#define __sanitizer_syscall_pre_waitid(...) -#define __sanitizer_syscall_pre_write(...) -#define __sanitizer_syscall_pre_writev(...) -#define __sanitizer_syscall_post_accept4(res, ...) -#define __sanitizer_syscall_post_accept(res, ...) -#define __sanitizer_syscall_post_access(res, ...) -#define __sanitizer_syscall_post_acct(res, ...) -#define __sanitizer_syscall_post_add_key(res, ...) -#define __sanitizer_syscall_post_adjtimex(res, ...) #define __sanitizer_syscall_post_afs_syscall(res, ...) -#define __sanitizer_syscall_post_alarm(res, ...) #define __sanitizer_syscall_post_arch_prctl(res, ...) -#define __sanitizer_syscall_post_bdflush(res, ...) -#define __sanitizer_syscall_post_bind(res, ...) #define __sanitizer_syscall_post_break(res, ...) -#define __sanitizer_syscall_post_brk(res, ...) -#define __sanitizer_syscall_post_capget(res, ...) -#define __sanitizer_syscall_post_capset(res, ...) -#define __sanitizer_syscall_post_chdir(res, ...) -#define __sanitizer_syscall_post_chmod(res, ...) #define __sanitizer_syscall_post_chown32(res, ...) -#define __sanitizer_syscall_post_chown(res, ...) -#define __sanitizer_syscall_post_chroot(res, ...) -#define __sanitizer_syscall_post_clock_adjtime(res, ...) -#define __sanitizer_syscall_post_clock_getres(res, ...) -#define __sanitizer_syscall_post_clock_gettime(res, ...) -#define __sanitizer_syscall_post_clock_nanosleep(res, ...) -#define __sanitizer_syscall_post_clock_settime(res, ...) #define __sanitizer_syscall_post_clone(res, ...) -#define __sanitizer_syscall_post_close(res, ...) -#define __sanitizer_syscall_post_connect(res, ...) #define __sanitizer_syscall_post_create_module(res, ...) -#define __sanitizer_syscall_post_creat(res, ...) -#define __sanitizer_syscall_post_delete_module(res, ...) -#define __sanitizer_syscall_post_dup2(res, ...) -#define __sanitizer_syscall_post_dup3(res, ...) -#define __sanitizer_syscall_post_dup(res, ...) -#define __sanitizer_syscall_post_epoll_create1(res, ...) -#define __sanitizer_syscall_post_epoll_create(res, ...) #define __sanitizer_syscall_post_epoll_ctl_old(res, ...) -#define __sanitizer_syscall_post_epoll_ctl(res, ...) -#define __sanitizer_syscall_post_epoll_pwait(res, ...) #define __sanitizer_syscall_post_epoll_wait_old(res, ...) -#define __sanitizer_syscall_post_epoll_wait(res, ...) -#define __sanitizer_syscall_post_eventfd2(res, ...) -#define __sanitizer_syscall_post_eventfd(res, ...) #define __sanitizer_syscall_post_execve(res, ...) -#define __sanitizer_syscall_post_exit_group(res, ...) -#define __sanitizer_syscall_post_exit(res, ...) -#define __sanitizer_syscall_post_faccessat(res, ...) -#define __sanitizer_syscall_post_fadvise64_64(res, ...) #define __sanitizer_syscall_post_fadvise64(res, ...) +#define __sanitizer_syscall_post_fadvise64_64(res, ...) #define __sanitizer_syscall_post_fallocate(res, ...) #define __sanitizer_syscall_post_fanotify_init(res, ...) #define __sanitizer_syscall_post_fanotify_mark(res, ...) -#define __sanitizer_syscall_post_fchdir(res, ...) -#define __sanitizer_syscall_post_fchmodat(res, ...) -#define __sanitizer_syscall_post_fchmod(res, ...) #define __sanitizer_syscall_post_fchown32(res, ...) -#define __sanitizer_syscall_post_fchownat(res, ...) -#define __sanitizer_syscall_post_fchown(res, ...) -#define __sanitizer_syscall_post_fcntl64(res, ...) -#define __sanitizer_syscall_post_fcntl(res, ...) -#define __sanitizer_syscall_post_fdatasync(res, ...) -#define __sanitizer_syscall_post_fgetxattr(res, ...) -#define __sanitizer_syscall_post_flistxattr(res, ...) -#define __sanitizer_syscall_post_flock(res, ...) -#define __sanitizer_syscall_post_fork(res, ...) -#define __sanitizer_syscall_post_fremovexattr(res, ...) -#define __sanitizer_syscall_post_fsetxattr(res, ...) -#define __sanitizer_syscall_post_fstat64(res, ...) -#define __sanitizer_syscall_post_fstatat64(res, ...) -#define __sanitizer_syscall_post_fstatfs64(res, ...) -#define __sanitizer_syscall_post_fstatfs(res, ...) -#define __sanitizer_syscall_post_fstat(res, ...) -#define __sanitizer_syscall_post_fsync(res, ...) #define __sanitizer_syscall_post_ftime(res, ...) #define __sanitizer_syscall_post_ftruncate64(res, ...) -#define __sanitizer_syscall_post_ftruncate(res, ...) #define __sanitizer_syscall_post_futex(res, ...) -#define __sanitizer_syscall_post_futimesat(res, ...) -#define __sanitizer_syscall_post_getcpu(res, ...) -#define __sanitizer_syscall_post_getcwd(res, ...) #define __sanitizer_syscall_post_getegid32(res, ...) -#define __sanitizer_syscall_post_getegid(res, ...) #define __sanitizer_syscall_post_geteuid32(res, ...) -#define __sanitizer_syscall_post_geteuid(res, ...) #define __sanitizer_syscall_post_getgid32(res, ...) -#define __sanitizer_syscall_post_getgid(res, ...) #define __sanitizer_syscall_post_getgroups32(res, ...) -#define __sanitizer_syscall_post_getgroups(res, ...) -#define __sanitizer_syscall_post_getitimer(res, ...) #define __sanitizer_syscall_post_get_kernel_syms(res, ...) -#define __sanitizer_syscall_post_get_mempolicy(res, ...) -#define __sanitizer_syscall_post_getpeername(res, ...) -#define __sanitizer_syscall_post_getpgid(res, ...) -#define __sanitizer_syscall_post_getpgrp(res, ...) -#define __sanitizer_syscall_post_getpid(res, ...) #define __sanitizer_syscall_post_getpmsg(res, ...) -#define __sanitizer_syscall_post_getppid(res, ...) -#define __sanitizer_syscall_post_getpriority(res, ...) #define __sanitizer_syscall_post_getresgid32(res, ...) -#define __sanitizer_syscall_post_getresgid(res, ...) #define __sanitizer_syscall_post_getresuid32(res, ...) -#define __sanitizer_syscall_post_getresuid(res, ...) -#define __sanitizer_syscall_post_getrlimit(res, ...) -#define __sanitizer_syscall_post_get_robust_list(res, ...) -#define __sanitizer_syscall_post_getrusage(res, ...) -#define __sanitizer_syscall_post_getsid(res, ...) -#define __sanitizer_syscall_post_getsockname(res, ...) -#define __sanitizer_syscall_post_getsockopt(res, ...) #define __sanitizer_syscall_post_get_thread_area(res, ...) -#define __sanitizer_syscall_post_gettid(res, ...) -#define __sanitizer_syscall_post_gettimeofday(res, ...) #define __sanitizer_syscall_post_getuid32(res, ...) -#define __sanitizer_syscall_post_getuid(res, ...) -#define __sanitizer_syscall_post_getxattr(res, ...) #define __sanitizer_syscall_post_gtty(res, ...) #define __sanitizer_syscall_post_idle(res, ...) -#define __sanitizer_syscall_post_init_module(res, ...) -#define __sanitizer_syscall_post_inotify_add_watch(res, ...) -#define __sanitizer_syscall_post_inotify_init1(res, ...) -#define __sanitizer_syscall_post_inotify_init(res, ...) -#define __sanitizer_syscall_post_inotify_rm_watch(res, ...) -#define __sanitizer_syscall_post_io_cancel(res, ...) -#define __sanitizer_syscall_post_ioctl(res, ...) -#define __sanitizer_syscall_post_io_destroy(res, ...) -#define __sanitizer_syscall_post_io_getevents(res, ...) -#define __sanitizer_syscall_post_ioperm(res, ...) #define __sanitizer_syscall_post_iopl(res, ...) -#define __sanitizer_syscall_post_ioprio_get(res, ...) -#define __sanitizer_syscall_post_ioprio_set(res, ...) -#define __sanitizer_syscall_post_io_setup(res, ...) -#define __sanitizer_syscall_post_io_submit(res, ...) -#define __sanitizer_syscall_post_ipc(res, ...) -#define __sanitizer_syscall_post_kexec_load(res, ...) -#define __sanitizer_syscall_post_keyctl(res, ...) -#define __sanitizer_syscall_post_kill(res, ...) #define __sanitizer_syscall_post_lchown32(res, ...) -#define __sanitizer_syscall_post_lchown(res, ...) -#define __sanitizer_syscall_post_lgetxattr(res, ...) -#define __sanitizer_syscall_post_linkat(res, ...) -#define __sanitizer_syscall_post_link(res, ...) -#define __sanitizer_syscall_post_listen(res, ...) -#define __sanitizer_syscall_post_listxattr(res, ...) -#define __sanitizer_syscall_post_llistxattr(res, ...) #define __sanitizer_syscall_post__llseek(res, ...) #define __sanitizer_syscall_post_lock(res, ...) -#define __sanitizer_syscall_post_lookup_dcookie(res, ...) -#define __sanitizer_syscall_post_lremovexattr(res, ...) -#define __sanitizer_syscall_post_lseek(res, ...) -#define __sanitizer_syscall_post_lsetxattr(res, ...) -#define __sanitizer_syscall_post_lstat64(res, ...) -#define __sanitizer_syscall_post_lstat(res, ...) #define __sanitizer_syscall_post_madvise1(res, ...) -#define __sanitizer_syscall_post_madvise(res, ...) -#define __sanitizer_syscall_post_mbind(res, ...) -#define __sanitizer_syscall_post_migrate_pages(res, ...) -#define __sanitizer_syscall_post_mincore(res, ...) -#define __sanitizer_syscall_post_mkdirat(res, ...) -#define __sanitizer_syscall_post_mkdir(res, ...) -#define __sanitizer_syscall_post_mknodat(res, ...) -#define __sanitizer_syscall_post_mknod(res, ...) -#define __sanitizer_syscall_post_mlockall(res, ...) -#define __sanitizer_syscall_post_mlock(res, ...) #define __sanitizer_syscall_post_mmap2(res, ...) #define __sanitizer_syscall_post_mmap(res, ...) #define __sanitizer_syscall_post_modify_ldt(res, ...) -#define __sanitizer_syscall_post_mount(res, ...) -#define __sanitizer_syscall_post_move_pages(res, ...) -#define __sanitizer_syscall_post_mprotect(res, ...) #define __sanitizer_syscall_post_mpx(res, ...) -#define __sanitizer_syscall_post_mq_getsetattr(res, ...) -#define __sanitizer_syscall_post_mq_notify(res, ...) -#define __sanitizer_syscall_post_mq_open(res, ...) -#define __sanitizer_syscall_post_mq_timedreceive(res, ...) -#define __sanitizer_syscall_post_mq_timedsend(res, ...) -#define __sanitizer_syscall_post_mq_unlink(res, ...) -#define __sanitizer_syscall_post_mremap(res, ...) -#define __sanitizer_syscall_post_msgctl(res, ...) -#define __sanitizer_syscall_post_msgget(res, ...) -#define __sanitizer_syscall_post_msgrcv(res, ...) -#define __sanitizer_syscall_post_msgsnd(res, ...) -#define __sanitizer_syscall_post_msync(res, ...) -#define __sanitizer_syscall_post_munlockall(res, ...) -#define __sanitizer_syscall_post_munlock(res, ...) -#define __sanitizer_syscall_post_munmap(res, ...) -#define __sanitizer_syscall_post_name_to_handle_at(res, ...) -#define __sanitizer_syscall_post_nanosleep(res, ...) -#define __sanitizer_syscall_post_newfstatat(res, ...) #define __sanitizer_syscall_post__newselect(res, ...) #define __sanitizer_syscall_post_nfsservctl(res, ...) -#define __sanitizer_syscall_post_nice(res, ...) #define __sanitizer_syscall_post_oldfstat(res, ...) #define __sanitizer_syscall_post_oldlstat(res, ...) #define __sanitizer_syscall_post_oldolduname(res, ...) #define __sanitizer_syscall_post_oldstat(res, ...) -#define __sanitizer_syscall_post_olduname(res, ...) -#define __sanitizer_syscall_post_openat(res, ...) -#define __sanitizer_syscall_post_open_by_handle_at(res, ...) -#define __sanitizer_syscall_post_open(res, ...) -#define __sanitizer_syscall_post_pause(res, ...) -#define __sanitizer_syscall_post_perf_event_open(res, ...) -#define __sanitizer_syscall_post_personality(res, ...) -#define __sanitizer_syscall_post_pipe2(res, ...) -#define __sanitizer_syscall_post_pipe(res, ...) -#define __sanitizer_syscall_post_pivot_root(res, ...) -#define __sanitizer_syscall_post_poll(res, ...) -#define __sanitizer_syscall_post_ppoll(res, ...) #define __sanitizer_syscall_post_prctl(res, ...) -#define __sanitizer_syscall_post_pread64(res, ...) -#define __sanitizer_syscall_post_preadv(res, ...) -#define __sanitizer_syscall_post_prlimit64(res, ...) -#define __sanitizer_syscall_post_process_vm_readv(res, ...) -#define __sanitizer_syscall_post_process_vm_writev(res, ...) #define __sanitizer_syscall_post_profil(res, ...) #define __sanitizer_syscall_post_prof(res, ...) -#define __sanitizer_syscall_post_pselect6(res, ...) -#define __sanitizer_syscall_post_ptrace(res, ...) #define __sanitizer_syscall_post_putpmsg(res, ...) -#define __sanitizer_syscall_post_pwrite64(res, ...) -#define __sanitizer_syscall_post_pwritev(res, ...) #define __sanitizer_syscall_post_query_module(res, ...) -#define __sanitizer_syscall_post_quotactl(res, ...) #define __sanitizer_syscall_post_readahead(res, ...) #define __sanitizer_syscall_post_readdir(res, ...) -#define __sanitizer_syscall_post_readlinkat(res, ...) -#define __sanitizer_syscall_post_readlink(res, ...) -#define __sanitizer_syscall_post_read(res, ...) -#define __sanitizer_syscall_post_readv(res, ...) -#define __sanitizer_syscall_post_reboot(res, ...) -#define __sanitizer_syscall_post_recvfrom(res, ...) -#define __sanitizer_syscall_post_recvmmsg(res, ...) -#define __sanitizer_syscall_post_remap_file_pages(res, ...) -#define __sanitizer_syscall_post_removexattr(res, ...) -#define __sanitizer_syscall_post_renameat(res, ...) -#define __sanitizer_syscall_post_rename(res, ...) -#define __sanitizer_syscall_post_request_key(res, ...) -#define __sanitizer_syscall_post_restart_syscall(res, ...) -#define __sanitizer_syscall_post_rmdir(res, ...) #define __sanitizer_syscall_post_rt_sigaction(res, ...) -#define __sanitizer_syscall_post_rt_sigprocmask(res, ...) -#define __sanitizer_syscall_post_rt_sigqueueinfo(res, ...) #define __sanitizer_syscall_post_rt_sigreturn(res, ...) #define __sanitizer_syscall_post_rt_sigsuspend(res, ...) -#define __sanitizer_syscall_post_rt_sigtimedwait(res, ...) -#define __sanitizer_syscall_post_rt_tgsigqueueinfo(res, ...) -#define __sanitizer_syscall_post_sched_getaffinity(res, ...) -#define __sanitizer_syscall_post_sched_getparam(res, ...) -#define __sanitizer_syscall_post_sched_get_priority_max(res, ...) -#define __sanitizer_syscall_post_sched_get_priority_min(res, ...) -#define __sanitizer_syscall_post_sched_getscheduler(res, ...) -#define __sanitizer_syscall_post_sched_rr_get_interval(res, ...) -#define __sanitizer_syscall_post_sched_setaffinity(res, ...) -#define __sanitizer_syscall_post_sched_setparam(res, ...) -#define __sanitizer_syscall_post_sched_setscheduler(res, ...) -#define __sanitizer_syscall_post_sched_yield(res, ...) #define __sanitizer_syscall_post_security(res, ...) -#define __sanitizer_syscall_post_select(res, ...) -#define __sanitizer_syscall_post_semctl(res, ...) -#define __sanitizer_syscall_post_semget(res, ...) -#define __sanitizer_syscall_post_semop(res, ...) -#define __sanitizer_syscall_post_semtimedop(res, ...) -#define __sanitizer_syscall_post_sendfile64(res, ...) -#define __sanitizer_syscall_post_sendfile(res, ...) -#define __sanitizer_syscall_post_sendmmsg(res, ...) -#define __sanitizer_syscall_post_sendmsg(res, ...) -#define __sanitizer_syscall_post_sendto(res, ...) -#define __sanitizer_syscall_post_setdomainname(res, ...) #define __sanitizer_syscall_post_setfsgid32(res, ...) -#define __sanitizer_syscall_post_setfsgid(res, ...) #define __sanitizer_syscall_post_setfsuid32(res, ...) -#define __sanitizer_syscall_post_setfsuid(res, ...) #define __sanitizer_syscall_post_setgid32(res, ...) -#define __sanitizer_syscall_post_setgid(res, ...) #define __sanitizer_syscall_post_setgroups32(res, ...) -#define __sanitizer_syscall_post_setgroups(res, ...) -#define __sanitizer_syscall_post_sethostname(res, ...) -#define __sanitizer_syscall_post_setitimer(res, ...) -#define __sanitizer_syscall_post_set_mempolicy(res, ...) -#define __sanitizer_syscall_post_setns(res, ...) -#define __sanitizer_syscall_post_setpgid(res, ...) -#define __sanitizer_syscall_post_setpriority(res, ...) #define __sanitizer_syscall_post_setregid32(res, ...) -#define __sanitizer_syscall_post_setregid(res, ...) #define __sanitizer_syscall_post_setresgid32(res, ...) -#define __sanitizer_syscall_post_setresgid(res, ...) #define __sanitizer_syscall_post_setresuid32(res, ...) -#define __sanitizer_syscall_post_setresuid(res, ...) #define __sanitizer_syscall_post_setreuid32(res, ...) -#define __sanitizer_syscall_post_setreuid(res, ...) -#define __sanitizer_syscall_post_setrlimit(res, ...) -#define __sanitizer_syscall_post_set_robust_list(res, ...) -#define __sanitizer_syscall_post_setsid(res, ...) -#define __sanitizer_syscall_post_setsockopt(res, ...) #define __sanitizer_syscall_post_set_thread_area(res, ...) -#define __sanitizer_syscall_post_set_tid_address(res, ...) -#define __sanitizer_syscall_post_settimeofday(res, ...) #define __sanitizer_syscall_post_setuid32(res, ...) -#define __sanitizer_syscall_post_setuid(res, ...) -#define __sanitizer_syscall_post_setxattr(res, ...) -#define __sanitizer_syscall_post_sgetmask(res, ...) -#define __sanitizer_syscall_post_shmat(res, ...) -#define __sanitizer_syscall_post_shmctl(res, ...) -#define __sanitizer_syscall_post_shmdt(res, ...) -#define __sanitizer_syscall_post_shmget(res, ...) -#define __sanitizer_syscall_post_shutdown(res, ...) #define __sanitizer_syscall_post_sigaction(res, ...) #define __sanitizer_syscall_post_sigaltstack(res, ...) -#define __sanitizer_syscall_post_signalfd4(res, ...) -#define __sanitizer_syscall_post_signalfd(res, ...) -#define __sanitizer_syscall_post_signal(res, ...) -#define __sanitizer_syscall_post_sigpending(res, ...) -#define __sanitizer_syscall_post_sigprocmask(res, ...) #define __sanitizer_syscall_post_sigreturn(res, ...) #define __sanitizer_syscall_post_sigsuspend(res, ...) -#define __sanitizer_syscall_post_socketcall(res, ...) -#define __sanitizer_syscall_post_socketpair(res, ...) -#define __sanitizer_syscall_post_socket(res, ...) -#define __sanitizer_syscall_post_splice(res, ...) -#define __sanitizer_syscall_post_ssetmask(res, ...) -#define __sanitizer_syscall_post_stat64(res, ...) -#define __sanitizer_syscall_post_statfs64(res, ...) -#define __sanitizer_syscall_post_statfs(res, ...) -#define __sanitizer_syscall_post_stat(res, ...) -#define __sanitizer_syscall_post_stime(res, ...) #define __sanitizer_syscall_post_stty(res, ...) -#define __sanitizer_syscall_post_swapoff(res, ...) -#define __sanitizer_syscall_post_swapon(res, ...) -#define __sanitizer_syscall_post_symlinkat(res, ...) -#define __sanitizer_syscall_post_symlink(res, ...) #define __sanitizer_syscall_post_sync_file_range(res, ...) -#define __sanitizer_syscall_post_syncfs(res, ...) -#define __sanitizer_syscall_post_sync(res, ...) #define __sanitizer_syscall_post__sysctl(res, ...) -#define __sanitizer_syscall_post_sysfs(res, ...) -#define __sanitizer_syscall_post_sysinfo(res, ...) -#define __sanitizer_syscall_post_syslog(res, ...) -#define __sanitizer_syscall_post_tee(res, ...) -#define __sanitizer_syscall_post_tgkill(res, ...) -#define __sanitizer_syscall_post_timer_create(res, ...) -#define __sanitizer_syscall_post_timer_delete(res, ...) -#define __sanitizer_syscall_post_time(res, ...) -#define __sanitizer_syscall_post_timerfd_create(res, ...) -#define __sanitizer_syscall_post_timerfd_gettime(res, ...) -#define __sanitizer_syscall_post_timerfd_settime(res, ...) -#define __sanitizer_syscall_post_timer_getoverrun(res, ...) -#define __sanitizer_syscall_post_timer_gettime(res, ...) -#define __sanitizer_syscall_post_timer_settime(res, ...) -#define __sanitizer_syscall_post_times(res, ...) -#define __sanitizer_syscall_post_tkill(res, ...) #define __sanitizer_syscall_post_truncate64(res, ...) -#define __sanitizer_syscall_post_truncate(res, ...) #define __sanitizer_syscall_post_tuxcall(res, ...) #define __sanitizer_syscall_post_ugetrlimit(res, ...) #define __sanitizer_syscall_post_ulimit(res, ...) -#define __sanitizer_syscall_post_umask(res, ...) #define __sanitizer_syscall_post_umount2(res, ...) -#define __sanitizer_syscall_post_umount(res, ...) -#define __sanitizer_syscall_post_uname(res, ...) -#define __sanitizer_syscall_post_unlinkat(res, ...) -#define __sanitizer_syscall_post_unlink(res, ...) -#define __sanitizer_syscall_post_unshare(res, ...) -#define __sanitizer_syscall_post_uselib(res, ...) -#define __sanitizer_syscall_post_ustat(res, ...) -#define __sanitizer_syscall_post_utimensat(res, ...) -#define __sanitizer_syscall_post_utime(res, ...) -#define __sanitizer_syscall_post_utimes(res, ...) -#define __sanitizer_syscall_post_vfork(res, ...) -#define __sanitizer_syscall_post_vhangup(res, ...) #define __sanitizer_syscall_post_vm86old(res, ...) #define __sanitizer_syscall_post_vm86(res, ...) -#define __sanitizer_syscall_post_vmsplice(res, ...) #define __sanitizer_syscall_post_vserver(res, ...) -#define __sanitizer_syscall_post_waitid(res, ...) -#define __sanitizer_syscall_post_write(res, ...) -#define __sanitizer_syscall_post_writev(res, ...) #ifdef __cplusplus -} // extern "C" +extern "C" { +#endif + +// Private declarations. Do not call directly from user code. Use macros above. +void __sanitizer_syscall_pre_impl_time(long tloc); +void __sanitizer_syscall_post_impl_time(long res, long tloc); +void __sanitizer_syscall_pre_impl_stime(long tptr); +void __sanitizer_syscall_post_impl_stime(long res, long tptr); +void __sanitizer_syscall_pre_impl_gettimeofday(long tv, long tz); +void __sanitizer_syscall_post_impl_gettimeofday(long res, long tv, long tz); +void __sanitizer_syscall_pre_impl_settimeofday(long tv, long tz); +void __sanitizer_syscall_post_impl_settimeofday(long res, long tv, long tz); +void __sanitizer_syscall_pre_impl_adjtimex(long txc_p); +void __sanitizer_syscall_post_impl_adjtimex(long res, long txc_p); +void __sanitizer_syscall_pre_impl_times(long tbuf); +void __sanitizer_syscall_post_impl_times(long res, long tbuf); +void __sanitizer_syscall_pre_impl_gettid(); +void __sanitizer_syscall_post_impl_gettid(long res); +void __sanitizer_syscall_pre_impl_nanosleep(long rqtp, long rmtp); +void __sanitizer_syscall_post_impl_nanosleep(long res, long rqtp, long rmtp); +void __sanitizer_syscall_pre_impl_alarm(long seconds); +void __sanitizer_syscall_post_impl_alarm(long res, long seconds); +void __sanitizer_syscall_pre_impl_getpid(); +void __sanitizer_syscall_post_impl_getpid(long res); +void __sanitizer_syscall_pre_impl_getppid(); +void __sanitizer_syscall_post_impl_getppid(long res); +void __sanitizer_syscall_pre_impl_getuid(); +void __sanitizer_syscall_post_impl_getuid(long res); +void __sanitizer_syscall_pre_impl_geteuid(); +void __sanitizer_syscall_post_impl_geteuid(long res); +void __sanitizer_syscall_pre_impl_getgid(); +void __sanitizer_syscall_post_impl_getgid(long res); +void __sanitizer_syscall_pre_impl_getegid(); +void __sanitizer_syscall_post_impl_getegid(long res); +void __sanitizer_syscall_pre_impl_getresuid(long ruid, long euid, long suid); +void __sanitizer_syscall_post_impl_getresuid(long res, long ruid, long euid, + long suid); +void __sanitizer_syscall_pre_impl_getresgid(long rgid, long egid, long sgid); +void __sanitizer_syscall_post_impl_getresgid(long res, long rgid, long egid, + long sgid); +void __sanitizer_syscall_pre_impl_getpgid(long pid); +void __sanitizer_syscall_post_impl_getpgid(long res, long pid); +void __sanitizer_syscall_pre_impl_getpgrp(); +void __sanitizer_syscall_post_impl_getpgrp(long res); +void __sanitizer_syscall_pre_impl_getsid(long pid); +void __sanitizer_syscall_post_impl_getsid(long res, long pid); +void __sanitizer_syscall_pre_impl_getgroups(long gidsetsize, long grouplist); +void __sanitizer_syscall_post_impl_getgroups(long res, long gidsetsize, + long grouplist); +void __sanitizer_syscall_pre_impl_setregid(long rgid, long egid); +void __sanitizer_syscall_post_impl_setregid(long res, long rgid, long egid); +void __sanitizer_syscall_pre_impl_setgid(long gid); +void __sanitizer_syscall_post_impl_setgid(long res, long gid); +void __sanitizer_syscall_pre_impl_setreuid(long ruid, long euid); +void __sanitizer_syscall_post_impl_setreuid(long res, long ruid, long euid); +void __sanitizer_syscall_pre_impl_setuid(long uid); +void __sanitizer_syscall_post_impl_setuid(long res, long uid); +void __sanitizer_syscall_pre_impl_setresuid(long ruid, long euid, long suid); +void __sanitizer_syscall_post_impl_setresuid(long res, long ruid, long euid, + long suid); +void __sanitizer_syscall_pre_impl_setresgid(long rgid, long egid, long sgid); +void __sanitizer_syscall_post_impl_setresgid(long res, long rgid, long egid, + long sgid); +void __sanitizer_syscall_pre_impl_setfsuid(long uid); +void __sanitizer_syscall_post_impl_setfsuid(long res, long uid); +void __sanitizer_syscall_pre_impl_setfsgid(long gid); +void __sanitizer_syscall_post_impl_setfsgid(long res, long gid); +void __sanitizer_syscall_pre_impl_setpgid(long pid, long pgid); +void __sanitizer_syscall_post_impl_setpgid(long res, long pid, long pgid); +void __sanitizer_syscall_pre_impl_setsid(); +void __sanitizer_syscall_post_impl_setsid(long res); +void __sanitizer_syscall_pre_impl_setgroups(long gidsetsize, long grouplist); +void __sanitizer_syscall_post_impl_setgroups(long res, long gidsetsize, + long grouplist); +void __sanitizer_syscall_pre_impl_acct(long name); +void __sanitizer_syscall_post_impl_acct(long res, long name); +void __sanitizer_syscall_pre_impl_capget(long header, long dataptr); +void __sanitizer_syscall_post_impl_capget(long res, long header, long dataptr); +void __sanitizer_syscall_pre_impl_capset(long header, long data); +void __sanitizer_syscall_post_impl_capset(long res, long header, long data); +void __sanitizer_syscall_pre_impl_personality(long personality); +void __sanitizer_syscall_post_impl_personality(long res, long personality); +void __sanitizer_syscall_pre_impl_sigpending(long set); +void __sanitizer_syscall_post_impl_sigpending(long res, long set); +void __sanitizer_syscall_pre_impl_sigprocmask(long how, long set, long oset); +void __sanitizer_syscall_post_impl_sigprocmask(long res, long how, long set, + long oset); +void __sanitizer_syscall_pre_impl_getitimer(long which, long value); +void __sanitizer_syscall_post_impl_getitimer(long res, long which, long value); +void __sanitizer_syscall_pre_impl_setitimer(long which, long value, + long ovalue); +void __sanitizer_syscall_post_impl_setitimer(long res, long which, long value, + long ovalue); +void __sanitizer_syscall_pre_impl_timer_create(long which_clock, + long timer_event_spec, + long created_timer_id); +void __sanitizer_syscall_post_impl_timer_create(long res, long which_clock, + long timer_event_spec, + long created_timer_id); +void __sanitizer_syscall_pre_impl_timer_gettime(long timer_id, long setting); +void __sanitizer_syscall_post_impl_timer_gettime(long res, long timer_id, + long setting); +void __sanitizer_syscall_pre_impl_timer_getoverrun(long timer_id); +void __sanitizer_syscall_post_impl_timer_getoverrun(long res, long timer_id); +void __sanitizer_syscall_pre_impl_timer_settime(long timer_id, long flags, + long new_setting, + long old_setting); +void __sanitizer_syscall_post_impl_timer_settime(long res, long timer_id, + long flags, long new_setting, + long old_setting); +void __sanitizer_syscall_pre_impl_timer_delete(long timer_id); +void __sanitizer_syscall_post_impl_timer_delete(long res, long timer_id); +void __sanitizer_syscall_pre_impl_clock_settime(long which_clock, long tp); +void __sanitizer_syscall_post_impl_clock_settime(long res, long which_clock, + long tp); +void __sanitizer_syscall_pre_impl_clock_gettime(long which_clock, long tp); +void __sanitizer_syscall_post_impl_clock_gettime(long res, long which_clock, + long tp); +void __sanitizer_syscall_pre_impl_clock_adjtime(long which_clock, long tx); +void __sanitizer_syscall_post_impl_clock_adjtime(long res, long which_clock, + long tx); +void __sanitizer_syscall_pre_impl_clock_getres(long which_clock, long tp); +void __sanitizer_syscall_post_impl_clock_getres(long res, long which_clock, + long tp); +void __sanitizer_syscall_pre_impl_clock_nanosleep(long which_clock, long flags, + long rqtp, long rmtp); +void __sanitizer_syscall_post_impl_clock_nanosleep(long res, long which_clock, + long flags, long rqtp, + long rmtp); +void __sanitizer_syscall_pre_impl_nice(long increment); +void __sanitizer_syscall_post_impl_nice(long res, long increment); +void __sanitizer_syscall_pre_impl_sched_setscheduler(long pid, long policy, + long param); +void __sanitizer_syscall_post_impl_sched_setscheduler(long res, long pid, + long policy, long param); +void __sanitizer_syscall_pre_impl_sched_setparam(long pid, long param); +void __sanitizer_syscall_post_impl_sched_setparam(long res, long pid, + long param); +void __sanitizer_syscall_pre_impl_sched_getscheduler(long pid); +void __sanitizer_syscall_post_impl_sched_getscheduler(long res, long pid); +void __sanitizer_syscall_pre_impl_sched_getparam(long pid, long param); +void __sanitizer_syscall_post_impl_sched_getparam(long res, long pid, + long param); +void __sanitizer_syscall_pre_impl_sched_setaffinity(long pid, long len, + long user_mask_ptr); +void __sanitizer_syscall_post_impl_sched_setaffinity(long res, long pid, + long len, + long user_mask_ptr); +void __sanitizer_syscall_pre_impl_sched_getaffinity(long pid, long len, + long user_mask_ptr); +void __sanitizer_syscall_post_impl_sched_getaffinity(long res, long pid, + long len, + long user_mask_ptr); +void __sanitizer_syscall_pre_impl_sched_yield(); +void __sanitizer_syscall_post_impl_sched_yield(long res); +void __sanitizer_syscall_pre_impl_sched_get_priority_max(long policy); +void __sanitizer_syscall_post_impl_sched_get_priority_max(long res, + long policy); +void __sanitizer_syscall_pre_impl_sched_get_priority_min(long policy); +void __sanitizer_syscall_post_impl_sched_get_priority_min(long res, + long policy); +void __sanitizer_syscall_pre_impl_sched_rr_get_interval(long pid, + long interval); +void __sanitizer_syscall_post_impl_sched_rr_get_interval(long res, long pid, + long interval); +void __sanitizer_syscall_pre_impl_setpriority(long which, long who, + long niceval); +void __sanitizer_syscall_post_impl_setpriority(long res, long which, long who, + long niceval); +void __sanitizer_syscall_pre_impl_getpriority(long which, long who); +void __sanitizer_syscall_post_impl_getpriority(long res, long which, long who); +void __sanitizer_syscall_pre_impl_shutdown(long arg0, long arg1); +void __sanitizer_syscall_post_impl_shutdown(long res, long arg0, long arg1); +void __sanitizer_syscall_pre_impl_reboot(long magic1, long magic2, long cmd, + long arg); +void __sanitizer_syscall_post_impl_reboot(long res, long magic1, long magic2, + long cmd, long arg); +void __sanitizer_syscall_pre_impl_restart_syscall(); +void __sanitizer_syscall_post_impl_restart_syscall(long res); +void __sanitizer_syscall_pre_impl_kexec_load(long entry, long nr_segments, + long segments, long flags); +void __sanitizer_syscall_post_impl_kexec_load(long res, long entry, + long nr_segments, long segments, + long flags); +void __sanitizer_syscall_pre_impl_exit(long error_code); +void __sanitizer_syscall_post_impl_exit(long res, long error_code); +void __sanitizer_syscall_pre_impl_exit_group(long error_code); +void __sanitizer_syscall_post_impl_exit_group(long res, long error_code); +void __sanitizer_syscall_pre_impl_wait4(long pid, long stat_addr, long options, + long ru); +void __sanitizer_syscall_post_impl_wait4(long res, long pid, long stat_addr, + long options, long ru); +void __sanitizer_syscall_pre_impl_waitid(long which, long pid, long infop, + long options, long ru); +void __sanitizer_syscall_post_impl_waitid(long res, long which, long pid, + long infop, long options, long ru); +void __sanitizer_syscall_pre_impl_waitpid(long pid, long stat_addr, + long options); +void __sanitizer_syscall_post_impl_waitpid(long res, long pid, long stat_addr, + long options); +void __sanitizer_syscall_pre_impl_set_tid_address(long tidptr); +void __sanitizer_syscall_post_impl_set_tid_address(long res, long tidptr); +void __sanitizer_syscall_pre_impl_init_module(long umod, long len, long uargs); +void __sanitizer_syscall_post_impl_init_module(long res, long umod, long len, + long uargs); +void __sanitizer_syscall_pre_impl_delete_module(long name_user, long flags); +void __sanitizer_syscall_post_impl_delete_module(long res, long name_user, + long flags); +void __sanitizer_syscall_pre_impl_rt_sigprocmask(long how, long set, long oset, + long sigsetsize); +void __sanitizer_syscall_post_impl_rt_sigprocmask(long res, long how, long set, + long oset, long sigsetsize); +void __sanitizer_syscall_pre_impl_rt_sigpending(long set, long sigsetsize); +void __sanitizer_syscall_post_impl_rt_sigpending(long res, long set, + long sigsetsize); +void __sanitizer_syscall_pre_impl_rt_sigtimedwait(long uthese, long uinfo, + long uts, long sigsetsize); +void __sanitizer_syscall_post_impl_rt_sigtimedwait(long res, long uthese, + long uinfo, long uts, + long sigsetsize); +void __sanitizer_syscall_pre_impl_rt_tgsigqueueinfo(long tgid, long pid, + long sig, long uinfo); +void __sanitizer_syscall_post_impl_rt_tgsigqueueinfo(long res, long tgid, + long pid, long sig, + long uinfo); +void __sanitizer_syscall_pre_impl_kill(long pid, long sig); +void __sanitizer_syscall_post_impl_kill(long res, long pid, long sig); +void __sanitizer_syscall_pre_impl_tgkill(long tgid, long pid, long sig); +void __sanitizer_syscall_post_impl_tgkill(long res, long tgid, long pid, + long sig); +void __sanitizer_syscall_pre_impl_tkill(long pid, long sig); +void __sanitizer_syscall_post_impl_tkill(long res, long pid, long sig); +void __sanitizer_syscall_pre_impl_rt_sigqueueinfo(long pid, long sig, + long uinfo); +void __sanitizer_syscall_post_impl_rt_sigqueueinfo(long res, long pid, long sig, + long uinfo); +void __sanitizer_syscall_pre_impl_sgetmask(); +void __sanitizer_syscall_post_impl_sgetmask(long res); +void __sanitizer_syscall_pre_impl_ssetmask(long newmask); +void __sanitizer_syscall_post_impl_ssetmask(long res, long newmask); +void __sanitizer_syscall_pre_impl_signal(long sig, long handler); +void __sanitizer_syscall_post_impl_signal(long res, long sig, long handler); +void __sanitizer_syscall_pre_impl_pause(); +void __sanitizer_syscall_post_impl_pause(long res); +void __sanitizer_syscall_pre_impl_sync(); +void __sanitizer_syscall_post_impl_sync(long res); +void __sanitizer_syscall_pre_impl_fsync(long fd); +void __sanitizer_syscall_post_impl_fsync(long res, long fd); +void __sanitizer_syscall_pre_impl_fdatasync(long fd); +void __sanitizer_syscall_post_impl_fdatasync(long res, long fd); +void __sanitizer_syscall_pre_impl_bdflush(long func, long data); +void __sanitizer_syscall_post_impl_bdflush(long res, long func, long data); +void __sanitizer_syscall_pre_impl_mount(long dev_name, long dir_name, long type, + long flags, long data); +void __sanitizer_syscall_post_impl_mount(long res, long dev_name, long dir_name, + long type, long flags, long data); +void __sanitizer_syscall_pre_impl_umount(long name, long flags); +void __sanitizer_syscall_post_impl_umount(long res, long name, long flags); +void __sanitizer_syscall_pre_impl_oldumount(long name); +void __sanitizer_syscall_post_impl_oldumount(long res, long name); +void __sanitizer_syscall_pre_impl_truncate(long path, long length); +void __sanitizer_syscall_post_impl_truncate(long res, long path, long length); +void __sanitizer_syscall_pre_impl_ftruncate(long fd, long length); +void __sanitizer_syscall_post_impl_ftruncate(long res, long fd, long length); +void __sanitizer_syscall_pre_impl_stat(long filename, long statbuf); +void __sanitizer_syscall_post_impl_stat(long res, long filename, long statbuf); +void __sanitizer_syscall_pre_impl_statfs(long path, long buf); +void __sanitizer_syscall_post_impl_statfs(long res, long path, long buf); +void __sanitizer_syscall_pre_impl_statfs64(long path, long sz, long buf); +void __sanitizer_syscall_post_impl_statfs64(long res, long path, long sz, + long buf); +void __sanitizer_syscall_pre_impl_fstatfs(long fd, long buf); +void __sanitizer_syscall_post_impl_fstatfs(long res, long fd, long buf); +void __sanitizer_syscall_pre_impl_fstatfs64(long fd, long sz, long buf); +void __sanitizer_syscall_post_impl_fstatfs64(long res, long fd, long sz, + long buf); +void __sanitizer_syscall_pre_impl_lstat(long filename, long statbuf); +void __sanitizer_syscall_post_impl_lstat(long res, long filename, long statbuf); +void __sanitizer_syscall_pre_impl_fstat(long fd, long statbuf); +void __sanitizer_syscall_post_impl_fstat(long res, long fd, long statbuf); +void __sanitizer_syscall_pre_impl_newstat(long filename, long statbuf); +void __sanitizer_syscall_post_impl_newstat(long res, long filename, + long statbuf); +void __sanitizer_syscall_pre_impl_newlstat(long filename, long statbuf); +void __sanitizer_syscall_post_impl_newlstat(long res, long filename, + long statbuf); +void __sanitizer_syscall_pre_impl_newfstat(long fd, long statbuf); +void __sanitizer_syscall_post_impl_newfstat(long res, long fd, long statbuf); +void __sanitizer_syscall_pre_impl_ustat(long dev, long ubuf); +void __sanitizer_syscall_post_impl_ustat(long res, long dev, long ubuf); +void __sanitizer_syscall_pre_impl_stat64(long filename, long statbuf); +void __sanitizer_syscall_post_impl_stat64(long res, long filename, + long statbuf); +void __sanitizer_syscall_pre_impl_fstat64(long fd, long statbuf); +void __sanitizer_syscall_post_impl_fstat64(long res, long fd, long statbuf); +void __sanitizer_syscall_pre_impl_lstat64(long filename, long statbuf); +void __sanitizer_syscall_post_impl_lstat64(long res, long filename, + long statbuf); +void __sanitizer_syscall_pre_impl_setxattr(long path, long name, long value, + long size, long flags); +void __sanitizer_syscall_post_impl_setxattr(long res, long path, long name, + long value, long size, long flags); +void __sanitizer_syscall_pre_impl_lsetxattr(long path, long name, long value, + long size, long flags); +void __sanitizer_syscall_post_impl_lsetxattr(long res, long path, long name, + long value, long size, long flags); +void __sanitizer_syscall_pre_impl_fsetxattr(long fd, long name, long value, + long size, long flags); +void __sanitizer_syscall_post_impl_fsetxattr(long res, long fd, long name, + long value, long size, long flags); +void __sanitizer_syscall_pre_impl_getxattr(long path, long name, long value, + long size); +void __sanitizer_syscall_post_impl_getxattr(long res, long path, long name, + long value, long size); +void __sanitizer_syscall_pre_impl_lgetxattr(long path, long name, long value, + long size); +void __sanitizer_syscall_post_impl_lgetxattr(long res, long path, long name, + long value, long size); +void __sanitizer_syscall_pre_impl_fgetxattr(long fd, long name, long value, + long size); +void __sanitizer_syscall_post_impl_fgetxattr(long res, long fd, long name, + long value, long size); +void __sanitizer_syscall_pre_impl_listxattr(long path, long list, long size); +void __sanitizer_syscall_post_impl_listxattr(long res, long path, long list, + long size); +void __sanitizer_syscall_pre_impl_llistxattr(long path, long list, long size); +void __sanitizer_syscall_post_impl_llistxattr(long res, long path, long list, + long size); +void __sanitizer_syscall_pre_impl_flistxattr(long fd, long list, long size); +void __sanitizer_syscall_post_impl_flistxattr(long res, long fd, long list, + long size); +void __sanitizer_syscall_pre_impl_removexattr(long path, long name); +void __sanitizer_syscall_post_impl_removexattr(long res, long path, long name); +void __sanitizer_syscall_pre_impl_lremovexattr(long path, long name); +void __sanitizer_syscall_post_impl_lremovexattr(long res, long path, long name); +void __sanitizer_syscall_pre_impl_fremovexattr(long fd, long name); +void __sanitizer_syscall_post_impl_fremovexattr(long res, long fd, long name); +void __sanitizer_syscall_pre_impl_brk(long brk); +void __sanitizer_syscall_post_impl_brk(long res, long brk); +void __sanitizer_syscall_pre_impl_mprotect(long start, long len, long prot); +void __sanitizer_syscall_post_impl_mprotect(long res, long start, long len, + long prot); +void __sanitizer_syscall_pre_impl_mremap(long addr, long old_len, long new_len, + long flags, long new_addr); +void __sanitizer_syscall_post_impl_mremap(long res, long addr, long old_len, + long new_len, long flags, + long new_addr); +void __sanitizer_syscall_pre_impl_remap_file_pages(long start, long size, + long prot, long pgoff, + long flags); +void __sanitizer_syscall_post_impl_remap_file_pages(long res, long start, + long size, long prot, + long pgoff, long flags); +void __sanitizer_syscall_pre_impl_msync(long start, long len, long flags); +void __sanitizer_syscall_post_impl_msync(long res, long start, long len, + long flags); +void __sanitizer_syscall_pre_impl_munmap(long addr, long len); +void __sanitizer_syscall_post_impl_munmap(long res, long addr, long len); +void __sanitizer_syscall_pre_impl_mlock(long start, long len); +void __sanitizer_syscall_post_impl_mlock(long res, long start, long len); +void __sanitizer_syscall_pre_impl_munlock(long start, long len); +void __sanitizer_syscall_post_impl_munlock(long res, long start, long len); +void __sanitizer_syscall_pre_impl_mlockall(long flags); +void __sanitizer_syscall_post_impl_mlockall(long res, long flags); +void __sanitizer_syscall_pre_impl_munlockall(); +void __sanitizer_syscall_post_impl_munlockall(long res); +void __sanitizer_syscall_pre_impl_madvise(long start, long len, long behavior); +void __sanitizer_syscall_post_impl_madvise(long res, long start, long len, + long behavior); +void __sanitizer_syscall_pre_impl_mincore(long start, long len, long vec); +void __sanitizer_syscall_post_impl_mincore(long res, long start, long len, + long vec); +void __sanitizer_syscall_pre_impl_pivot_root(long new_root, long put_old); +void __sanitizer_syscall_post_impl_pivot_root(long res, long new_root, + long put_old); +void __sanitizer_syscall_pre_impl_chroot(long filename); +void __sanitizer_syscall_post_impl_chroot(long res, long filename); +void __sanitizer_syscall_pre_impl_mknod(long filename, long mode, long dev); +void __sanitizer_syscall_post_impl_mknod(long res, long filename, long mode, + long dev); +void __sanitizer_syscall_pre_impl_link(long oldname, long newname); +void __sanitizer_syscall_post_impl_link(long res, long oldname, long newname); +void __sanitizer_syscall_pre_impl_symlink(long old, long new_); +void __sanitizer_syscall_post_impl_symlink(long res, long old, long new_); +void __sanitizer_syscall_pre_impl_unlink(long pathname); +void __sanitizer_syscall_post_impl_unlink(long res, long pathname); +void __sanitizer_syscall_pre_impl_rename(long oldname, long newname); +void __sanitizer_syscall_post_impl_rename(long res, long oldname, long newname); +void __sanitizer_syscall_pre_impl_chmod(long filename, long mode); +void __sanitizer_syscall_post_impl_chmod(long res, long filename, long mode); +void __sanitizer_syscall_pre_impl_fchmod(long fd, long mode); +void __sanitizer_syscall_post_impl_fchmod(long res, long fd, long mode); +void __sanitizer_syscall_pre_impl_fcntl(long fd, long cmd, long arg); +void __sanitizer_syscall_post_impl_fcntl(long res, long fd, long cmd, long arg); +void __sanitizer_syscall_pre_impl_fcntl64(long fd, long cmd, long arg); +void __sanitizer_syscall_post_impl_fcntl64(long res, long fd, long cmd, + long arg); +void __sanitizer_syscall_pre_impl_pipe(long fildes); +void __sanitizer_syscall_post_impl_pipe(long res, long fildes); +void __sanitizer_syscall_pre_impl_pipe2(long fildes, long flags); +void __sanitizer_syscall_post_impl_pipe2(long res, long fildes, long flags); +void __sanitizer_syscall_pre_impl_dup(long fildes); +void __sanitizer_syscall_post_impl_dup(long res, long fildes); +void __sanitizer_syscall_pre_impl_dup2(long oldfd, long newfd); +void __sanitizer_syscall_post_impl_dup2(long res, long oldfd, long newfd); +void __sanitizer_syscall_pre_impl_dup3(long oldfd, long newfd, long flags); +void __sanitizer_syscall_post_impl_dup3(long res, long oldfd, long newfd, + long flags); +void __sanitizer_syscall_pre_impl_ioperm(long from, long num, long on); +void __sanitizer_syscall_post_impl_ioperm(long res, long from, long num, + long on); +void __sanitizer_syscall_pre_impl_ioctl(long fd, long cmd, long arg); +void __sanitizer_syscall_post_impl_ioctl(long res, long fd, long cmd, long arg); +void __sanitizer_syscall_pre_impl_flock(long fd, long cmd); +void __sanitizer_syscall_post_impl_flock(long res, long fd, long cmd); +void __sanitizer_syscall_pre_impl_io_setup(long nr_reqs, long ctx); +void __sanitizer_syscall_post_impl_io_setup(long res, long nr_reqs, long ctx); +void __sanitizer_syscall_pre_impl_io_destroy(long ctx); +void __sanitizer_syscall_post_impl_io_destroy(long res, long ctx); +void __sanitizer_syscall_pre_impl_io_getevents(long ctx_id, long min_nr, + long nr, long events, + long timeout); +void __sanitizer_syscall_post_impl_io_getevents(long res, long ctx_id, + long min_nr, long nr, + long events, long timeout); +void __sanitizer_syscall_pre_impl_io_submit(long ctx_id, long arg1, long arg2); +void __sanitizer_syscall_post_impl_io_submit(long res, long ctx_id, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_io_cancel(long ctx_id, long iocb, + long result); +void __sanitizer_syscall_post_impl_io_cancel(long res, long ctx_id, long iocb, + long result); +void __sanitizer_syscall_pre_impl_sendfile(long out_fd, long in_fd, long offset, + long count); +void __sanitizer_syscall_post_impl_sendfile(long res, long out_fd, long in_fd, + long offset, long count); +void __sanitizer_syscall_pre_impl_sendfile64(long out_fd, long in_fd, + long offset, long count); +void __sanitizer_syscall_post_impl_sendfile64(long res, long out_fd, long in_fd, + long offset, long count); +void __sanitizer_syscall_pre_impl_readlink(long path, long buf, long bufsiz); +void __sanitizer_syscall_post_impl_readlink(long res, long path, long buf, + long bufsiz); +void __sanitizer_syscall_pre_impl_creat(long pathname, long mode); +void __sanitizer_syscall_post_impl_creat(long res, long pathname, long mode); +void __sanitizer_syscall_pre_impl_open(long filename, long flags, long mode); +void __sanitizer_syscall_post_impl_open(long res, long filename, long flags, + long mode); +void __sanitizer_syscall_pre_impl_close(long fd); +void __sanitizer_syscall_post_impl_close(long res, long fd); +void __sanitizer_syscall_pre_impl_access(long filename, long mode); +void __sanitizer_syscall_post_impl_access(long res, long filename, long mode); +void __sanitizer_syscall_pre_impl_vhangup(); +void __sanitizer_syscall_post_impl_vhangup(long res); +void __sanitizer_syscall_pre_impl_chown(long filename, long user, long group); +void __sanitizer_syscall_post_impl_chown(long res, long filename, long user, + long group); +void __sanitizer_syscall_pre_impl_lchown(long filename, long user, long group); +void __sanitizer_syscall_post_impl_lchown(long res, long filename, long user, + long group); +void __sanitizer_syscall_pre_impl_fchown(long fd, long user, long group); +void __sanitizer_syscall_post_impl_fchown(long res, long fd, long user, + long group); +void __sanitizer_syscall_pre_impl_chown16(long filename, long user, long group); +void __sanitizer_syscall_post_impl_chown16(long res, long filename, long user, + long group); +void __sanitizer_syscall_pre_impl_lchown16(long filename, long user, + long group); +void __sanitizer_syscall_post_impl_lchown16(long res, long filename, long user, + long group); +void __sanitizer_syscall_pre_impl_fchown16(long fd, long user, long group); +void __sanitizer_syscall_post_impl_fchown16(long res, long fd, long user, + long group); +void __sanitizer_syscall_pre_impl_setregid16(long rgid, long egid); +void __sanitizer_syscall_post_impl_setregid16(long res, long rgid, long egid); +void __sanitizer_syscall_pre_impl_setgid16(long gid); +void __sanitizer_syscall_post_impl_setgid16(long res, long gid); +void __sanitizer_syscall_pre_impl_setreuid16(long ruid, long euid); +void __sanitizer_syscall_post_impl_setreuid16(long res, long ruid, long euid); +void __sanitizer_syscall_pre_impl_setuid16(long uid); +void __sanitizer_syscall_post_impl_setuid16(long res, long uid); +void __sanitizer_syscall_pre_impl_setresuid16(long ruid, long euid, long suid); +void __sanitizer_syscall_post_impl_setresuid16(long res, long ruid, long euid, + long suid); +void __sanitizer_syscall_pre_impl_getresuid16(long ruid, long euid, long suid); +void __sanitizer_syscall_post_impl_getresuid16(long res, long ruid, long euid, + long suid); +void __sanitizer_syscall_pre_impl_setresgid16(long rgid, long egid, long sgid); +void __sanitizer_syscall_post_impl_setresgid16(long res, long rgid, long egid, + long sgid); +void __sanitizer_syscall_pre_impl_getresgid16(long rgid, long egid, long sgid); +void __sanitizer_syscall_post_impl_getresgid16(long res, long rgid, long egid, + long sgid); +void __sanitizer_syscall_pre_impl_setfsuid16(long uid); +void __sanitizer_syscall_post_impl_setfsuid16(long res, long uid); +void __sanitizer_syscall_pre_impl_setfsgid16(long gid); +void __sanitizer_syscall_post_impl_setfsgid16(long res, long gid); +void __sanitizer_syscall_pre_impl_getgroups16(long gidsetsize, long grouplist); +void __sanitizer_syscall_post_impl_getgroups16(long res, long gidsetsize, + long grouplist); +void __sanitizer_syscall_pre_impl_setgroups16(long gidsetsize, long grouplist); +void __sanitizer_syscall_post_impl_setgroups16(long res, long gidsetsize, + long grouplist); +void __sanitizer_syscall_pre_impl_getuid16(); +void __sanitizer_syscall_post_impl_getuid16(long res); +void __sanitizer_syscall_pre_impl_geteuid16(); +void __sanitizer_syscall_post_impl_geteuid16(long res); +void __sanitizer_syscall_pre_impl_getgid16(); +void __sanitizer_syscall_post_impl_getgid16(long res); +void __sanitizer_syscall_pre_impl_getegid16(); +void __sanitizer_syscall_post_impl_getegid16(long res); +void __sanitizer_syscall_pre_impl_utime(long filename, long times); +void __sanitizer_syscall_post_impl_utime(long res, long filename, long times); +void __sanitizer_syscall_pre_impl_utimes(long filename, long utimes); +void __sanitizer_syscall_post_impl_utimes(long res, long filename, long utimes); +void __sanitizer_syscall_pre_impl_lseek(long fd, long offset, long origin); +void __sanitizer_syscall_post_impl_lseek(long res, long fd, long offset, + long origin); +void __sanitizer_syscall_pre_impl_llseek(long fd, long offset_high, + long offset_low, long result, + long origin); +void __sanitizer_syscall_post_impl_llseek(long res, long fd, long offset_high, + long offset_low, long result, + long origin); +void __sanitizer_syscall_pre_impl_read(long fd, long buf, long count); +void __sanitizer_syscall_post_impl_read(long res, long fd, long buf, + long count); +void __sanitizer_syscall_pre_impl_readv(long fd, long vec, long vlen); +void __sanitizer_syscall_post_impl_readv(long res, long fd, long vec, + long vlen); +void __sanitizer_syscall_pre_impl_write(long fd, long buf, long count); +void __sanitizer_syscall_post_impl_write(long res, long fd, long buf, + long count); +void __sanitizer_syscall_pre_impl_writev(long fd, long vec, long vlen); +void __sanitizer_syscall_post_impl_writev(long res, long fd, long vec, + long vlen); + +#ifdef _LP64 +void __sanitizer_syscall_pre_impl_pread64(long fd, long buf, long count, + long pos); +void __sanitizer_syscall_post_impl_pread64(long res, long fd, long buf, + long count, long pos); +void __sanitizer_syscall_pre_impl_pwrite64(long fd, long buf, long count, + long pos); +void __sanitizer_syscall_post_impl_pwrite64(long res, long fd, long buf, + long count, long pos); +#else +void __sanitizer_syscall_pre_impl_pread64(long fd, long buf, long count, + long pos0, long pos1); +void __sanitizer_syscall_post_impl_pread64(long res, long fd, long buf, + long count, long pos0, long pos1); +void __sanitizer_syscall_pre_impl_pwrite64(long fd, long buf, long count, + long pos0, long pos1); +void __sanitizer_syscall_post_impl_pwrite64(long res, long fd, long buf, + long count, long pos0, long pos1); +#endif + +void __sanitizer_syscall_pre_impl_preadv(long fd, long vec, long vlen, + long pos_l, long pos_h); +void __sanitizer_syscall_post_impl_preadv(long res, long fd, long vec, + long vlen, long pos_l, long pos_h); +void __sanitizer_syscall_pre_impl_pwritev(long fd, long vec, long vlen, + long pos_l, long pos_h); +void __sanitizer_syscall_post_impl_pwritev(long res, long fd, long vec, + long vlen, long pos_l, long pos_h); +void __sanitizer_syscall_pre_impl_getcwd(long buf, long size); +void __sanitizer_syscall_post_impl_getcwd(long res, long buf, long size); +void __sanitizer_syscall_pre_impl_mkdir(long pathname, long mode); +void __sanitizer_syscall_post_impl_mkdir(long res, long pathname, long mode); +void __sanitizer_syscall_pre_impl_chdir(long filename); +void __sanitizer_syscall_post_impl_chdir(long res, long filename); +void __sanitizer_syscall_pre_impl_fchdir(long fd); +void __sanitizer_syscall_post_impl_fchdir(long res, long fd); +void __sanitizer_syscall_pre_impl_rmdir(long pathname); +void __sanitizer_syscall_post_impl_rmdir(long res, long pathname); +void __sanitizer_syscall_pre_impl_lookup_dcookie(long cookie64, long buf, + long len); +void __sanitizer_syscall_post_impl_lookup_dcookie(long res, long cookie64, + long buf, long len); +void __sanitizer_syscall_pre_impl_quotactl(long cmd, long special, long id, + long addr); +void __sanitizer_syscall_post_impl_quotactl(long res, long cmd, long special, + long id, long addr); +void __sanitizer_syscall_pre_impl_getdents(long fd, long dirent, long count); +void __sanitizer_syscall_post_impl_getdents(long res, long fd, long dirent, + long count); +void __sanitizer_syscall_pre_impl_getdents64(long fd, long dirent, long count); +void __sanitizer_syscall_post_impl_getdents64(long res, long fd, long dirent, + long count); +void __sanitizer_syscall_pre_impl_setsockopt(long fd, long level, long optname, + long optval, long optlen); +void __sanitizer_syscall_post_impl_setsockopt(long res, long fd, long level, + long optname, long optval, + long optlen); +void __sanitizer_syscall_pre_impl_getsockopt(long fd, long level, long optname, + long optval, long optlen); +void __sanitizer_syscall_post_impl_getsockopt(long res, long fd, long level, + long optname, long optval, + long optlen); +void __sanitizer_syscall_pre_impl_bind(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_bind(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_connect(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_connect(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_accept(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_accept(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_accept4(long arg0, long arg1, long arg2, + long arg3); +void __sanitizer_syscall_post_impl_accept4(long res, long arg0, long arg1, + long arg2, long arg3); +void __sanitizer_syscall_pre_impl_getsockname(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_getsockname(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_getpeername(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_getpeername(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_send(long arg0, long arg1, long arg2, + long arg3); +void __sanitizer_syscall_post_impl_send(long res, long arg0, long arg1, + long arg2, long arg3); +void __sanitizer_syscall_pre_impl_sendto(long arg0, long arg1, long arg2, + long arg3, long arg4, long arg5); +void __sanitizer_syscall_post_impl_sendto(long res, long arg0, long arg1, + long arg2, long arg3, long arg4, + long arg5); +void __sanitizer_syscall_pre_impl_sendmsg(long fd, long msg, long flags); +void __sanitizer_syscall_post_impl_sendmsg(long res, long fd, long msg, + long flags); +void __sanitizer_syscall_pre_impl_sendmmsg(long fd, long msg, long vlen, + long flags); +void __sanitizer_syscall_post_impl_sendmmsg(long res, long fd, long msg, + long vlen, long flags); +void __sanitizer_syscall_pre_impl_recv(long arg0, long arg1, long arg2, + long arg3); +void __sanitizer_syscall_post_impl_recv(long res, long arg0, long arg1, + long arg2, long arg3); +void __sanitizer_syscall_pre_impl_recvfrom(long arg0, long arg1, long arg2, + long arg3, long arg4, long arg5); +void __sanitizer_syscall_post_impl_recvfrom(long res, long arg0, long arg1, + long arg2, long arg3, long arg4, + long arg5); +void __sanitizer_syscall_pre_impl_recvmsg(long fd, long msg, long flags); +void __sanitizer_syscall_post_impl_recvmsg(long res, long fd, long msg, + long flags); +void __sanitizer_syscall_pre_impl_recvmmsg(long fd, long msg, long vlen, + long flags, long timeout); +void __sanitizer_syscall_post_impl_recvmmsg(long res, long fd, long msg, + long vlen, long flags, + long timeout); +void __sanitizer_syscall_pre_impl_socket(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_socket(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_socketpair(long arg0, long arg1, long arg2, + long arg3); +void __sanitizer_syscall_post_impl_socketpair(long res, long arg0, long arg1, + long arg2, long arg3); +void __sanitizer_syscall_pre_impl_socketcall(long call, long args); +void __sanitizer_syscall_post_impl_socketcall(long res, long call, long args); +void __sanitizer_syscall_pre_impl_listen(long arg0, long arg1); +void __sanitizer_syscall_post_impl_listen(long res, long arg0, long arg1); +void __sanitizer_syscall_pre_impl_poll(long ufds, long nfds, long timeout); +void __sanitizer_syscall_post_impl_poll(long res, long ufds, long nfds, + long timeout); +void __sanitizer_syscall_pre_impl_select(long n, long inp, long outp, long exp, + long tvp); +void __sanitizer_syscall_post_impl_select(long res, long n, long inp, long outp, + long exp, long tvp); +void __sanitizer_syscall_pre_impl_old_select(long arg); +void __sanitizer_syscall_post_impl_old_select(long res, long arg); +void __sanitizer_syscall_pre_impl_epoll_create(long size); +void __sanitizer_syscall_post_impl_epoll_create(long res, long size); +void __sanitizer_syscall_pre_impl_epoll_create1(long flags); +void __sanitizer_syscall_post_impl_epoll_create1(long res, long flags); +void __sanitizer_syscall_pre_impl_epoll_ctl(long epfd, long op, long fd, + long event); +void __sanitizer_syscall_post_impl_epoll_ctl(long res, long epfd, long op, + long fd, long event); +void __sanitizer_syscall_pre_impl_epoll_wait(long epfd, long events, + long maxevents, long timeout); +void __sanitizer_syscall_post_impl_epoll_wait(long res, long epfd, long events, + long maxevents, long timeout); +void __sanitizer_syscall_pre_impl_epoll_pwait(long epfd, long events, + long maxevents, long timeout, + long sigmask, long sigsetsize); +void __sanitizer_syscall_post_impl_epoll_pwait(long res, long epfd, long events, + long maxevents, long timeout, + long sigmask, long sigsetsize); +void __sanitizer_syscall_pre_impl_gethostname(long name, long len); +void __sanitizer_syscall_post_impl_gethostname(long res, long name, long len); +void __sanitizer_syscall_pre_impl_sethostname(long name, long len); +void __sanitizer_syscall_post_impl_sethostname(long res, long name, long len); +void __sanitizer_syscall_pre_impl_setdomainname(long name, long len); +void __sanitizer_syscall_post_impl_setdomainname(long res, long name, long len); +void __sanitizer_syscall_pre_impl_newuname(long name); +void __sanitizer_syscall_post_impl_newuname(long res, long name); +void __sanitizer_syscall_pre_impl_uname(long arg0); +void __sanitizer_syscall_post_impl_uname(long res, long arg0); +void __sanitizer_syscall_pre_impl_olduname(long arg0); +void __sanitizer_syscall_post_impl_olduname(long res, long arg0); +void __sanitizer_syscall_pre_impl_getrlimit(long resource, long rlim); +void __sanitizer_syscall_post_impl_getrlimit(long res, long resource, + long rlim); +void __sanitizer_syscall_pre_impl_old_getrlimit(long resource, long rlim); +void __sanitizer_syscall_post_impl_old_getrlimit(long res, long resource, + long rlim); +void __sanitizer_syscall_pre_impl_setrlimit(long resource, long rlim); +void __sanitizer_syscall_post_impl_setrlimit(long res, long resource, + long rlim); +void __sanitizer_syscall_pre_impl_prlimit64(long pid, long resource, + long new_rlim, long old_rlim); +void __sanitizer_syscall_post_impl_prlimit64(long res, long pid, long resource, + long new_rlim, long old_rlim); +void __sanitizer_syscall_pre_impl_getrusage(long who, long ru); +void __sanitizer_syscall_post_impl_getrusage(long res, long who, long ru); +void __sanitizer_syscall_pre_impl_umask(long mask); +void __sanitizer_syscall_post_impl_umask(long res, long mask); +void __sanitizer_syscall_pre_impl_msgget(long key, long msgflg); +void __sanitizer_syscall_post_impl_msgget(long res, long key, long msgflg); +void __sanitizer_syscall_pre_impl_msgsnd(long msqid, long msgp, long msgsz, + long msgflg); +void __sanitizer_syscall_post_impl_msgsnd(long res, long msqid, long msgp, + long msgsz, long msgflg); +void __sanitizer_syscall_pre_impl_msgrcv(long msqid, long msgp, long msgsz, + long msgtyp, long msgflg); +void __sanitizer_syscall_post_impl_msgrcv(long res, long msqid, long msgp, + long msgsz, long msgtyp, long msgflg); +void __sanitizer_syscall_pre_impl_msgctl(long msqid, long cmd, long buf); +void __sanitizer_syscall_post_impl_msgctl(long res, long msqid, long cmd, + long buf); +void __sanitizer_syscall_pre_impl_semget(long key, long nsems, long semflg); +void __sanitizer_syscall_post_impl_semget(long res, long key, long nsems, + long semflg); +void __sanitizer_syscall_pre_impl_semop(long semid, long sops, long nsops); +void __sanitizer_syscall_post_impl_semop(long res, long semid, long sops, + long nsops); +void __sanitizer_syscall_pre_impl_semctl(long semid, long semnum, long cmd, + long arg); +void __sanitizer_syscall_post_impl_semctl(long res, long semid, long semnum, + long cmd, long arg); +void __sanitizer_syscall_pre_impl_semtimedop(long semid, long sops, long nsops, + long timeout); +void __sanitizer_syscall_post_impl_semtimedop(long res, long semid, long sops, + long nsops, long timeout); +void __sanitizer_syscall_pre_impl_shmat(long shmid, long shmaddr, long shmflg); +void __sanitizer_syscall_post_impl_shmat(long res, long shmid, long shmaddr, + long shmflg); +void __sanitizer_syscall_pre_impl_shmget(long key, long size, long flag); +void __sanitizer_syscall_post_impl_shmget(long res, long key, long size, + long flag); +void __sanitizer_syscall_pre_impl_shmdt(long shmaddr); +void __sanitizer_syscall_post_impl_shmdt(long res, long shmaddr); +void __sanitizer_syscall_pre_impl_shmctl(long shmid, long cmd, long buf); +void __sanitizer_syscall_post_impl_shmctl(long res, long shmid, long cmd, + long buf); +void __sanitizer_syscall_pre_impl_ipc(long call, long first, long second, + long third, long ptr, long fifth); +void __sanitizer_syscall_post_impl_ipc(long res, long call, long first, + long second, long third, long ptr, + long fifth); +void __sanitizer_syscall_pre_impl_mq_open(long name, long oflag, long mode, + long attr); +void __sanitizer_syscall_post_impl_mq_open(long res, long name, long oflag, + long mode, long attr); +void __sanitizer_syscall_pre_impl_mq_unlink(long name); +void __sanitizer_syscall_post_impl_mq_unlink(long res, long name); +void __sanitizer_syscall_pre_impl_mq_timedsend(long mqdes, long msg_ptr, + long msg_len, long msg_prio, + long abs_timeout); +void __sanitizer_syscall_post_impl_mq_timedsend(long res, long mqdes, + long msg_ptr, long msg_len, + long msg_prio, + long abs_timeout); +void __sanitizer_syscall_pre_impl_mq_timedreceive(long mqdes, long msg_ptr, + long msg_len, long msg_prio, + long abs_timeout); +void __sanitizer_syscall_post_impl_mq_timedreceive(long res, long mqdes, + long msg_ptr, long msg_len, + long msg_prio, + long abs_timeout); +void __sanitizer_syscall_pre_impl_mq_notify(long mqdes, long notification); +void __sanitizer_syscall_post_impl_mq_notify(long res, long mqdes, + long notification); +void __sanitizer_syscall_pre_impl_mq_getsetattr(long mqdes, long mqstat, + long omqstat); +void __sanitizer_syscall_post_impl_mq_getsetattr(long res, long mqdes, + long mqstat, long omqstat); +void __sanitizer_syscall_pre_impl_pciconfig_iobase(long which, long bus, + long devfn); +void __sanitizer_syscall_post_impl_pciconfig_iobase(long res, long which, + long bus, long devfn); +void __sanitizer_syscall_pre_impl_pciconfig_read(long bus, long dfn, long off, + long len, long buf); +void __sanitizer_syscall_post_impl_pciconfig_read(long res, long bus, long dfn, + long off, long len, long buf); +void __sanitizer_syscall_pre_impl_pciconfig_write(long bus, long dfn, long off, + long len, long buf); +void __sanitizer_syscall_post_impl_pciconfig_write(long res, long bus, long dfn, + long off, long len, + long buf); +void __sanitizer_syscall_pre_impl_swapon(long specialfile, long swap_flags); +void __sanitizer_syscall_post_impl_swapon(long res, long specialfile, + long swap_flags); +void __sanitizer_syscall_pre_impl_swapoff(long specialfile); +void __sanitizer_syscall_post_impl_swapoff(long res, long specialfile); +void __sanitizer_syscall_pre_impl_sysctl(long args); +void __sanitizer_syscall_post_impl_sysctl(long res, long args); +void __sanitizer_syscall_pre_impl_sysinfo(long info); +void __sanitizer_syscall_post_impl_sysinfo(long res, long info); +void __sanitizer_syscall_pre_impl_sysfs(long option, long arg1, long arg2); +void __sanitizer_syscall_post_impl_sysfs(long res, long option, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_syslog(long type, long buf, long len); +void __sanitizer_syscall_post_impl_syslog(long res, long type, long buf, + long len); +void __sanitizer_syscall_pre_impl_uselib(long library); +void __sanitizer_syscall_post_impl_uselib(long res, long library); +void __sanitizer_syscall_pre_impl_ni_syscall(); +void __sanitizer_syscall_post_impl_ni_syscall(long res); +void __sanitizer_syscall_pre_impl_ptrace(long request, long pid, long addr, + long data); +void __sanitizer_syscall_post_impl_ptrace(long res, long request, long pid, + long addr, long data); +void __sanitizer_syscall_pre_impl_add_key(long _type, long _description, + long _payload, long plen, + long destringid); +void __sanitizer_syscall_post_impl_add_key(long res, long _type, + long _description, long _payload, + long plen, long destringid); +void __sanitizer_syscall_pre_impl_request_key(long _type, long _description, + long _callout_info, + long destringid); +void __sanitizer_syscall_post_impl_request_key(long res, long _type, + long _description, + long _callout_info, + long destringid); +void __sanitizer_syscall_pre_impl_keyctl(long cmd, long arg2, long arg3, + long arg4, long arg5); +void __sanitizer_syscall_post_impl_keyctl(long res, long cmd, long arg2, + long arg3, long arg4, long arg5); +void __sanitizer_syscall_pre_impl_ioprio_set(long which, long who, long ioprio); +void __sanitizer_syscall_post_impl_ioprio_set(long res, long which, long who, + long ioprio); +void __sanitizer_syscall_pre_impl_ioprio_get(long which, long who); +void __sanitizer_syscall_post_impl_ioprio_get(long res, long which, long who); +void __sanitizer_syscall_pre_impl_set_mempolicy(long mode, long nmask, + long maxnode); +void __sanitizer_syscall_post_impl_set_mempolicy(long res, long mode, + long nmask, long maxnode); +void __sanitizer_syscall_pre_impl_migrate_pages(long pid, long maxnode, + long from, long to); +void __sanitizer_syscall_post_impl_migrate_pages(long res, long pid, + long maxnode, long from, + long to); +void __sanitizer_syscall_pre_impl_move_pages(long pid, long nr_pages, + long pages, long nodes, + long status, long flags); +void __sanitizer_syscall_post_impl_move_pages(long res, long pid, long nr_pages, + long pages, long nodes, + long status, long flags); +void __sanitizer_syscall_pre_impl_mbind(long start, long len, long mode, + long nmask, long maxnode, long flags); +void __sanitizer_syscall_post_impl_mbind(long res, long start, long len, + long mode, long nmask, long maxnode, + long flags); +void __sanitizer_syscall_pre_impl_get_mempolicy(long policy, long nmask, + long maxnode, long addr, + long flags); +void __sanitizer_syscall_post_impl_get_mempolicy(long res, long policy, + long nmask, long maxnode, + long addr, long flags); +void __sanitizer_syscall_pre_impl_inotify_init(); +void __sanitizer_syscall_post_impl_inotify_init(long res); +void __sanitizer_syscall_pre_impl_inotify_init1(long flags); +void __sanitizer_syscall_post_impl_inotify_init1(long res, long flags); +void __sanitizer_syscall_pre_impl_inotify_add_watch(long fd, long path, + long mask); +void __sanitizer_syscall_post_impl_inotify_add_watch(long res, long fd, + long path, long mask); +void __sanitizer_syscall_pre_impl_inotify_rm_watch(long fd, long wd); +void __sanitizer_syscall_post_impl_inotify_rm_watch(long res, long fd, long wd); +void __sanitizer_syscall_pre_impl_spu_run(long fd, long unpc, long ustatus); +void __sanitizer_syscall_post_impl_spu_run(long res, long fd, long unpc, + long ustatus); +void __sanitizer_syscall_pre_impl_spu_create(long name, long flags, long mode, + long fd); +void __sanitizer_syscall_post_impl_spu_create(long res, long name, long flags, + long mode, long fd); +void __sanitizer_syscall_pre_impl_mknodat(long dfd, long filename, long mode, + long dev); +void __sanitizer_syscall_post_impl_mknodat(long res, long dfd, long filename, + long mode, long dev); +void __sanitizer_syscall_pre_impl_mkdirat(long dfd, long pathname, long mode); +void __sanitizer_syscall_post_impl_mkdirat(long res, long dfd, long pathname, + long mode); +void __sanitizer_syscall_pre_impl_unlinkat(long dfd, long pathname, long flag); +void __sanitizer_syscall_post_impl_unlinkat(long res, long dfd, long pathname, + long flag); +void __sanitizer_syscall_pre_impl_symlinkat(long oldname, long newdfd, + long newname); +void __sanitizer_syscall_post_impl_symlinkat(long res, long oldname, + long newdfd, long newname); +void __sanitizer_syscall_pre_impl_linkat(long olddfd, long oldname, long newdfd, + long newname, long flags); +void __sanitizer_syscall_post_impl_linkat(long res, long olddfd, long oldname, + long newdfd, long newname, + long flags); +void __sanitizer_syscall_pre_impl_renameat(long olddfd, long oldname, + long newdfd, long newname); +void __sanitizer_syscall_post_impl_renameat(long res, long olddfd, long oldname, + long newdfd, long newname); +void __sanitizer_syscall_pre_impl_futimesat(long dfd, long filename, + long utimes); +void __sanitizer_syscall_post_impl_futimesat(long res, long dfd, long filename, + long utimes); +void __sanitizer_syscall_pre_impl_faccessat(long dfd, long filename, long mode); +void __sanitizer_syscall_post_impl_faccessat(long res, long dfd, long filename, + long mode); +void __sanitizer_syscall_pre_impl_fchmodat(long dfd, long filename, long mode); +void __sanitizer_syscall_post_impl_fchmodat(long res, long dfd, long filename, + long mode); +void __sanitizer_syscall_pre_impl_fchownat(long dfd, long filename, long user, + long group, long flag); +void __sanitizer_syscall_post_impl_fchownat(long res, long dfd, long filename, + long user, long group, long flag); +void __sanitizer_syscall_pre_impl_openat(long dfd, long filename, long flags, + long mode); +void __sanitizer_syscall_post_impl_openat(long res, long dfd, long filename, + long flags, long mode); +void __sanitizer_syscall_pre_impl_newfstatat(long dfd, long filename, + long statbuf, long flag); +void __sanitizer_syscall_post_impl_newfstatat(long res, long dfd, long filename, + long statbuf, long flag); +void __sanitizer_syscall_pre_impl_fstatat64(long dfd, long filename, + long statbuf, long flag); +void __sanitizer_syscall_post_impl_fstatat64(long res, long dfd, long filename, + long statbuf, long flag); +void __sanitizer_syscall_pre_impl_readlinkat(long dfd, long path, long buf, + long bufsiz); +void __sanitizer_syscall_post_impl_readlinkat(long res, long dfd, long path, + long buf, long bufsiz); +void __sanitizer_syscall_pre_impl_utimensat(long dfd, long filename, + long utimes, long flags); +void __sanitizer_syscall_post_impl_utimensat(long res, long dfd, long filename, + long utimes, long flags); +void __sanitizer_syscall_pre_impl_unshare(long unshare_flags); +void __sanitizer_syscall_post_impl_unshare(long res, long unshare_flags); +void __sanitizer_syscall_pre_impl_splice(long fd_in, long off_in, long fd_out, + long off_out, long len, long flags); +void __sanitizer_syscall_post_impl_splice(long res, long fd_in, long off_in, + long fd_out, long off_out, long len, + long flags); +void __sanitizer_syscall_pre_impl_vmsplice(long fd, long iov, long nr_segs, + long flags); +void __sanitizer_syscall_post_impl_vmsplice(long res, long fd, long iov, + long nr_segs, long flags); +void __sanitizer_syscall_pre_impl_tee(long fdin, long fdout, long len, + long flags); +void __sanitizer_syscall_post_impl_tee(long res, long fdin, long fdout, + long len, long flags); +void __sanitizer_syscall_pre_impl_get_robust_list(long pid, long head_ptr, + long len_ptr); +void __sanitizer_syscall_post_impl_get_robust_list(long res, long pid, + long head_ptr, long len_ptr); +void __sanitizer_syscall_pre_impl_set_robust_list(long head, long len); +void __sanitizer_syscall_post_impl_set_robust_list(long res, long head, + long len); +void __sanitizer_syscall_pre_impl_getcpu(long cpu, long node, long cache); +void __sanitizer_syscall_post_impl_getcpu(long res, long cpu, long node, + long cache); +void __sanitizer_syscall_pre_impl_signalfd(long ufd, long user_mask, + long sizemask); +void __sanitizer_syscall_post_impl_signalfd(long res, long ufd, long user_mask, + long sizemask); +void __sanitizer_syscall_pre_impl_signalfd4(long ufd, long user_mask, + long sizemask, long flags); +void __sanitizer_syscall_post_impl_signalfd4(long res, long ufd, long user_mask, + long sizemask, long flags); +void __sanitizer_syscall_pre_impl_timerfd_create(long clockid, long flags); +void __sanitizer_syscall_post_impl_timerfd_create(long res, long clockid, + long flags); +void __sanitizer_syscall_pre_impl_timerfd_settime(long ufd, long flags, + long utmr, long otmr); +void __sanitizer_syscall_post_impl_timerfd_settime(long res, long ufd, + long flags, long utmr, + long otmr); +void __sanitizer_syscall_pre_impl_timerfd_gettime(long ufd, long otmr); +void __sanitizer_syscall_post_impl_timerfd_gettime(long res, long ufd, + long otmr); +void __sanitizer_syscall_pre_impl_eventfd(long count); +void __sanitizer_syscall_post_impl_eventfd(long res, long count); +void __sanitizer_syscall_pre_impl_eventfd2(long count, long flags); +void __sanitizer_syscall_post_impl_eventfd2(long res, long count, long flags); +void __sanitizer_syscall_pre_impl_old_readdir(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_old_readdir(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_pselect6(long arg0, long arg1, long arg2, + long arg3, long arg4, long arg5); +void __sanitizer_syscall_post_impl_pselect6(long res, long arg0, long arg1, + long arg2, long arg3, long arg4, + long arg5); +void __sanitizer_syscall_pre_impl_ppoll(long arg0, long arg1, long arg2, + long arg3, long arg4); +void __sanitizer_syscall_post_impl_ppoll(long res, long arg0, long arg1, + long arg2, long arg3, long arg4); +void __sanitizer_syscall_pre_impl_fanotify_init(long flags, long event_f_flags); +void __sanitizer_syscall_post_impl_fanotify_init(long res, long flags, + long event_f_flags); +void __sanitizer_syscall_pre_impl_fanotify_mark(long fanotify_fd, long flags, + long mask, long fd, + long pathname); +void __sanitizer_syscall_post_impl_fanotify_mark(long res, long fanotify_fd, + long flags, long mask, long fd, + long pathname); +void __sanitizer_syscall_pre_impl_syncfs(long fd); +void __sanitizer_syscall_post_impl_syncfs(long res, long fd); +void __sanitizer_syscall_pre_impl_perf_event_open(long attr_uptr, long pid, + long cpu, long group_fd, + long flags); +void __sanitizer_syscall_post_impl_perf_event_open(long res, long attr_uptr, + long pid, long cpu, + long group_fd, long flags); +void __sanitizer_syscall_pre_impl_mmap_pgoff(long addr, long len, long prot, + long flags, long fd, long pgoff); +void __sanitizer_syscall_post_impl_mmap_pgoff(long res, long addr, long len, + long prot, long flags, long fd, + long pgoff); +void __sanitizer_syscall_pre_impl_old_mmap(long arg); +void __sanitizer_syscall_post_impl_old_mmap(long res, long arg); +void __sanitizer_syscall_pre_impl_name_to_handle_at(long dfd, long name, + long handle, long mnt_id, + long flag); +void __sanitizer_syscall_post_impl_name_to_handle_at(long res, long dfd, + long name, long handle, + long mnt_id, long flag); +void __sanitizer_syscall_pre_impl_open_by_handle_at(long mountdirfd, + long handle, long flags); +void __sanitizer_syscall_post_impl_open_by_handle_at(long res, long mountdirfd, + long handle, long flags); +void __sanitizer_syscall_pre_impl_setns(long fd, long nstype); +void __sanitizer_syscall_post_impl_setns(long res, long fd, long nstype); +void __sanitizer_syscall_pre_impl_process_vm_readv(long pid, long lvec, + long liovcnt, long rvec, + long riovcnt, long flags); +void __sanitizer_syscall_post_impl_process_vm_readv(long res, long pid, + long lvec, long liovcnt, + long rvec, long riovcnt, + long flags); +void __sanitizer_syscall_pre_impl_process_vm_writev(long pid, long lvec, + long liovcnt, long rvec, + long riovcnt, long flags); +void __sanitizer_syscall_post_impl_process_vm_writev(long res, long pid, + long lvec, long liovcnt, + long rvec, long riovcnt, + long flags); +void __sanitizer_syscall_pre_impl_fork(); +void __sanitizer_syscall_post_impl_fork(long res); +void __sanitizer_syscall_pre_impl_vfork(); +void __sanitizer_syscall_post_impl_vfork(long res); + +#ifdef __cplusplus +} // extern "C" #endif -#endif // SANITIZER_LINUX_SYSCALL_HOOKS_H +#endif // SANITIZER_LINUX_SYSCALL_HOOKS_H diff --git a/include/sanitizer/lsan_interface.h b/include/sanitizer/lsan_interface.h new file mode 100644 index 000000000000..df256c0e5384 --- /dev/null +++ b/include/sanitizer/lsan_interface.h @@ -0,0 +1,52 @@ +//===-- sanitizer/lsan_interface.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LSAN_INTERFACE_H +#define SANITIZER_LSAN_INTERFACE_H + +#include <sanitizer/common_interface_defs.h> + +#ifdef __cplusplus +extern "C" { +#endif + // Allocations made between calls to __lsan_disable() and __lsan_enable() will + // be treated as non-leaks. Disable/enable pairs may be nested. + void __lsan_disable(); + void __lsan_enable(); + // The heap object into which p points will be treated as a non-leak. + void __lsan_ignore_object(const void *p); + // The user may optionally provide this function to disallow leak checking + // for the program it is linked into (if the return value is non-zero). This + // function must be defined as returning a constant value; any behavior beyond + // that is unsupported. + int __lsan_is_turned_off(); + // Calling this function makes LSan enter the leak checking phase immediately. + // Use this if normal end-of-process leak checking happens too late (e.g. if + // you have intentional memory leaks in your shutdown code). Calling this + // function overrides end-of-process leak checking; it must be called at + // most once per process. This function will terminate the process if there + // are memory leaks and the exit_code flag is non-zero. + void __lsan_do_leak_check(); +#ifdef __cplusplus +} // extern "C" + +namespace __lsan { +class ScopedDisabler { + public: + ScopedDisabler() { __lsan_disable(); } + ~ScopedDisabler() { __lsan_enable(); } +}; +} // namespace __lsan +#endif + +#endif // SANITIZER_LSAN_INTERFACE_H diff --git a/include/sanitizer/msan_interface.h b/include/sanitizer/msan_interface.h index 9eff7b597b69..63af84fc3ff1 100644 --- a/include/sanitizer/msan_interface.h +++ b/include/sanitizer/msan_interface.h @@ -27,10 +27,10 @@ extern "C" { /* Set raw origin for the memory range. */ - void __msan_set_origin(const void *a, size_t size, uint32_t origin); + void __msan_set_origin(const volatile void *a, size_t size, uint32_t origin); /* Get raw origin for an address. */ - uint32_t __msan_get_origin(const void *a); + uint32_t __msan_get_origin(const volatile void *a); /* Returns non-zero if tracking origins. */ int __msan_get_track_origins(); @@ -39,18 +39,19 @@ extern "C" { uint32_t __msan_get_umr_origin(); /* Make memory region fully initialized (without changing its contents). */ - void __msan_unpoison(const void *a, size_t size); + void __msan_unpoison(const volatile void *a, size_t size); /* Make memory region fully uninitialized (without changing its contents). */ - void __msan_poison(const void *a, size_t size); + void __msan_poison(const volatile void *a, size_t size); /* Make memory region partially uninitialized (without changing its contents). */ - void __msan_partial_poison(const void* data, void* shadow, size_t size); + void __msan_partial_poison(const volatile void *data, void *shadow, + size_t size); /* Returns the offset of the first (at least partially) poisoned byte in the memory range, or -1 if the whole range is good. */ - intptr_t __msan_test_shadow(const void *x, size_t size); + intptr_t __msan_test_shadow(const volatile void *x, size_t size); /* Set exit code when error(s) were detected. Value of 0 means don't change the program exit code. */ @@ -63,9 +64,14 @@ extern "C" { The last line will verify that a UMR happened. */ void __msan_set_expect_umr(int expect_umr); + /* Change the value of keep_going flag. Non-zero value means don't terminate + program execution when an error is detected. This will not affect error in + modules that were compiled without the corresponding compiler flag. */ + void __msan_set_keep_going(int keep_going); + /* Print shadow and origin for the memory range to stdout in a human-readable format. */ - void __msan_print_shadow(const void *x, size_t size); + void __msan_print_shadow(const volatile void *x, size_t size); /* Print current function arguments shadow and origin to stdout in a human-readable format. */ @@ -76,7 +82,58 @@ extern "C" { /* Tell MSan about newly allocated memory (ex.: custom allocator). Memory will be marked uninitialized, with origin at the call site. */ - void __msan_allocated_memory(const void* data, size_t size); + void __msan_allocated_memory(const volatile void* data, size_t size); + + /* This function may be optionally provided by user and should return + a string containing Msan runtime options. See msan_flags.h for details. */ + const char* __msan_default_options(); + + + /***********************************/ + /* Allocator statistics interface. */ + + /* Returns the estimated number of bytes that will be reserved by allocator + for request of "size" bytes. If Msan allocator can't allocate that much + memory, returns the maximal possible allocation size, otherwise returns + "size". */ + size_t __msan_get_estimated_allocated_size(size_t size); + + /* Returns true if p was returned by the Msan allocator and + is not yet freed. */ + int __msan_get_ownership(const volatile void *p); + + /* Returns the number of bytes reserved for the pointer p. + Requires (get_ownership(p) == true) or (p == 0). */ + size_t __msan_get_allocated_size(const volatile void *p); + + /* Number of bytes, allocated and not yet freed by the application. */ + size_t __msan_get_current_allocated_bytes(); + + /* Number of bytes, mmaped by msan allocator to fulfill allocation requests. + Generally, for request of X bytes, allocator can reserve and add to free + lists a large number of chunks of size X to use them for future requests. + All these chunks count toward the heap size. Currently, allocator never + releases memory to OS (instead, it just puts freed chunks to free + lists). */ + size_t __msan_get_heap_size(); + + /* Number of bytes, mmaped by msan allocator, which can be used to fulfill + allocation requests. When a user program frees memory chunk, it can first + fall into quarantine and will count toward __msan_get_free_bytes() + later. */ + size_t __msan_get_free_bytes(); + + /* Number of bytes in unmapped pages, that are released to OS. Currently, + always returns 0. */ + size_t __msan_get_unmapped_bytes(); + + /* Malloc hooks that may be optionally provided by user. + __msan_malloc_hook(ptr, size) is called immediately after + allocation of "size" bytes, which returned "ptr". + __msan_free_hook(ptr) is called immediately before + deallocation of "ptr". */ + void __msan_malloc_hook(const volatile void *ptr, size_t size); + void __msan_free_hook(const volatile void *ptr); #else // __has_feature(memory_sanitizer) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index f07ab1e1872b..b620828beb2b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,22 +1,36 @@ # First, add the subdirectories which contain feature-based runtime libraries # and several convenience helper libraries. -if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux") + +# Don't build sanitizers in the bootstrap build. +if(LLVM_USE_SANITIZER STREQUAL "") # AddressSanitizer is supported on Linux and Mac OS X. - # Windows support is work in progress. - add_subdirectory(asan) - add_subdirectory(interception) - add_subdirectory(sanitizer_common) - if(NOT ANDROID) + # 32-bit Windows support is experimental. + if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux") + set(SUPPORTS_BUILDING_ASAN TRUE) + elseif(CMAKE_SYSTEM_NAME MATCHES "Windows" + AND MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 4) + set(SUPPORTS_BUILDING_ASAN TRUE) + else() + set(SUPPORTS_BUILDING_ASAN FALSE) + endif() + if(SUPPORTS_BUILDING_ASAN) + add_subdirectory(asan) + add_subdirectory(interception) + add_subdirectory(sanitizer_common) + endif() + if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux" AND NOT ANDROID) + # LSan, UBsan and profile can be built on Mac OS and Linux. + add_subdirectory(lsan) add_subdirectory(profile) add_subdirectory(ubsan) endif() -endif() -if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND NOT ANDROID) - # ThreadSanitizer and MemorySanitizer are supported on Linux only. - add_subdirectory(tsan) - add_subdirectory(msan) - add_subdirectory(msandr) - add_subdirectory(lsan) + if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT ANDROID) + # ThreadSanitizer and MemorySanitizer are supported on Linux only. + add_subdirectory(tsan) + add_subdirectory(msan) + add_subdirectory(msandr) + add_subdirectory(dfsan) + endif() endif() # The top-level lib directory contains a large amount of C code which provides @@ -181,10 +195,22 @@ set(i386_SOURCES i386/umoddi3.S ${GENERIC_SOURCES}) -foreach(arch x86_64 i386) - if(CAN_TARGET_${arch}) - add_compiler_rt_static_runtime(clang_rt.${arch} ${arch} - SOURCES ${${arch}_SOURCES} - CFLAGS "-std=c99") - endif() -endforeach() +if (NOT WIN32) + foreach(arch x86_64 i386) + if(CAN_TARGET_${arch}) + add_compiler_rt_static_runtime(clang_rt.${arch} ${arch} + SOURCES ${${arch}_SOURCES} + CFLAGS "-std=c99") + endif() + endforeach() +endif() + +# Generate configs for running lit and unit tests. +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.common.configured.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.common.configured) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.common.unit.configured.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.common.unit.configured) + diff --git a/lib/Makefile.mk b/lib/Makefile.mk index 8054c35aa362..f9d7800cc4c4 100644 --- a/lib/Makefile.mk +++ b/lib/Makefile.mk @@ -22,6 +22,7 @@ SubDirs += tsan SubDirs += msan SubDirs += ubsan SubDirs += lsan +SubDirs += dfsan # Define the variables for this specific directory. Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file))) diff --git a/lib/apple_versioning.c b/lib/apple_versioning.c index 09f149f14cf7..3797a1ab02da 100644 --- a/lib/apple_versioning.c +++ b/lib/apple_versioning.c @@ -9,19 +9,20 @@ */ - #if __APPLE__ - #if __arm__ + #include <Availability.h> + + #if __IPHONE_OS_VERSION_MIN_REQUIRED #define NOT_HERE_BEFORE_10_6(sym) - #define NOT_HERE_IN_10_8_AND_EARLIER(sym) - #elif __ppc__ - #define NOT_HERE_BEFORE_10_6(sym) \ - extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \ - extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ - extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \ - __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NOT_HERE_IN_10_8_AND_EARLIER(sym) \ + extern const char sym##_tmp61 __asm("$ld$hide$os6.1$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp61 = 0; \ + extern const char sym##_tmp60 __asm("$ld$hide$os6.0$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp60 = 0; \ + extern const char sym##_tmp51 __asm("$ld$hide$os5.1$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp51 = 0; \ + extern const char sym##_tmp50 __asm("$ld$hide$os5.0$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp50 = 0; #else #define NOT_HERE_BEFORE_10_6(sym) \ extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \ @@ -35,7 +36,7 @@ __attribute__((visibility("default"))) const char sym##_tmp7 = 0; \ extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); \ __attribute__((visibility("default"))) const char sym##_tmp6 = 0; - #endif /* __ppc__ */ + #endif /* Symbols in libSystem.dylib in 10.6 and later, diff --git a/lib/arm/Makefile.mk b/lib/arm/Makefile.mk index 04dec88a9714..a2df115f926f 100644 --- a/lib/arm/Makefile.mk +++ b/lib/arm/Makefile.mk @@ -9,7 +9,7 @@ ModuleName := builtins SubDirs := -OnlyArchs := armv5 armv6 armv7 armv7f armv7k armv7s +OnlyArchs := armv5 armv6 armv7 armv7f armv7k armv7m armv7em armv7s AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file))) Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file))) diff --git a/lib/arm/comparesf2.S b/lib/arm/comparesf2.S index ee18203392dc..ce6f4b9efd00 100644 --- a/lib/arm/comparesf2.S +++ b/lib/arm/comparesf2.S @@ -59,12 +59,14 @@ DEFINE_COMPILERRT_FUNCTION(__nesf2) // Next, we check if a and b have the same or different signs. If they have // opposite signs, this eor will set the N flag. + it ne eorsne r12, r0, r1 // If a and b are equal (either both zeros or bit identical; again, we're // ignoring NaNs for now), this subtract will zero out r0. If they have the // same sign, the flags are updated as they would be for a comparison of the // absolute values of a and b. + it pl subspl r0, r2, r3 // If a is smaller in magnitude than b and both have the same sign, place @@ -77,23 +79,27 @@ DEFINE_COMPILERRT_FUNCTION(__nesf2) // still clear from the shift argument in orrs; if a is positive and b // negative, this places 0 in r0; if a is negative and b positive, -1 is // placed in r0. + it lo mvnlo r0, r1, asr #31 // If a is greater in magnitude than b and both have the same sign, place // the sign of b in r0. Thus, if both are negative and a < b, -1 is placed // in r0, which is the desired result. Conversely, if both are positive // and a > b, zero is placed in r0. + it hi movhi r0, r1, asr #31 // If you've been keeping track, at this point r0 contains -1 if a < b and // 0 if a >= b. All that remains to be done is to set it to 1 if a > b. // If a == b, then the Z flag is set, so we can get the correct final value // into r0 by simply or'ing with 1 if Z is clear. - orrne r0, r0, #1 + it ne + orrne r0, r0, #1 // Finally, we need to deal with NaNs. If either argument is NaN, replace // the value in r0 with 1. cmp r2, #0xff000000 + ite ls cmpls r3, #0xff000000 movhi r0, #1 bx lr @@ -108,12 +114,18 @@ DEFINE_COMPILERRT_FUNCTION(__gtsf2) mov r2, r0, lsl #1 mov r3, r1, lsl #1 orrs r12, r2, r3, lsr #1 + it ne eorsne r12, r0, r1 + it pl subspl r0, r2, r3 + it lo mvnlo r0, r1, asr #31 + it hi movhi r0, r1, asr #31 - orrne r0, r0, #1 + it ne + orrne r0, r0, #1 cmp r2, #0xff000000 + ite ls cmpls r3, #0xff000000 movhi r0, #-1 bx lr @@ -125,6 +137,7 @@ DEFINE_COMPILERRT_FUNCTION(__unordsf2) mov r3, r1, lsl #1 mov r0, #0 cmp r2, #0xff000000 + ite ls cmpls r3, #0xff000000 movhi r0, #1 bx lr diff --git a/lib/arm/divmodsi4.S b/lib/arm/divmodsi4.S index d31e510c8f38..6495a8b4cc3a 100644 --- a/lib/arm/divmodsi4.S +++ b/lib/arm/divmodsi4.S @@ -24,7 +24,7 @@ .syntax unified .align 3 DEFINE_COMPILERRT_FUNCTION(__divmodsi4) -#if __ARM_ARCH_7S__ +#if __ARM_ARCH_EXT_IDIV__ tst r1, r1 beq LOCAL_LABEL(divzero) mov r3, r0 diff --git a/lib/arm/divsi3.S b/lib/arm/divsi3.S index e76fe31bb782..b631db292b8a 100644 --- a/lib/arm/divsi3.S +++ b/lib/arm/divsi3.S @@ -25,7 +25,7 @@ // Ok, APCS and AAPCS agree on 32 bit args, so it's safe to use the same routine. DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_idiv, __divsi3) DEFINE_COMPILERRT_FUNCTION(__divsi3) -#if __ARM_ARCH_7S__ +#if __ARM_ARCH_EXT_IDIV__ tst r1,r1 beq LOCAL_LABEL(divzero) sdiv r0, r0, r1 diff --git a/lib/arm/modsi3.S b/lib/arm/modsi3.S index 04595011d0ec..fe75b41b1331 100644 --- a/lib/arm/modsi3.S +++ b/lib/arm/modsi3.S @@ -23,7 +23,7 @@ .syntax unified .align 3 DEFINE_COMPILERRT_FUNCTION(__modsi3) -#if __ARM_ARCH_7S__ +#if __ARM_ARCH_EXT_IDIV__ tst r1, r1 beq LOCAL_LABEL(divzero) sdiv r2, r0, r1 diff --git a/lib/arm/switch16.S b/lib/arm/switch16.S index e8f08c49c5df..9c3f0cf9915f 100644 --- a/lib/arm/switch16.S +++ b/lib/arm/switch16.S @@ -34,8 +34,9 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switch16) ldrh ip, [lr, #-1] // get first 16-bit word in table cmp r0, ip // compare with index add r0, lr, r0, lsl #1 // compute address of element in table - ldrshcc r0, [r0, #1] // load 16-bit element if r0 is in range add ip, lr, ip, lsl #1 // compute address of last element in table + ite lo + ldrshlo r0, [r0, #1] // load 16-bit element if r0 is in range ldrshhs r0, [ip, #1] // load 16-bit element if r0 out of range add ip, lr, r0, lsl #1 // compute label = lr + element*2 bx ip // jump to computed label diff --git a/lib/arm/switch32.S b/lib/arm/switch32.S index 7008fccb18ee..3152dfa1d0be 100644 --- a/lib/arm/switch32.S +++ b/lib/arm/switch32.S @@ -34,9 +34,10 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switch32) ldr ip, [lr, #-1] // get first 32-bit word in table cmp r0, ip // compare with index add r0, lr, r0, lsl #2 // compute address of element in table - ldrcc r0, [r0, #3] // load 32-bit element if r0 is in range add ip, lr, ip, lsl #2 // compute address of last element in table - ldrcs r0, [ip, #3] // load 32-bit element if r0 out of range + ite lo + ldrlo r0, [r0, #3] // load 32-bit element if r0 is in range + ldrhs r0, [ip, #3] // load 32-bit element if r0 out of range add ip, lr, r0 // compute label = lr + element bx ip // jump to computed label diff --git a/lib/arm/switch8.S b/lib/arm/switch8.S index e784b4082e1f..15729ebc3165 100644 --- a/lib/arm/switch8.S +++ b/lib/arm/switch8.S @@ -33,7 +33,8 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switch8) ldrb ip, [lr, #-1] // get first byte in table cmp r0, ip // signed compare with index - ldrsbcc r0, [lr, r0] // get indexed byte out of table + ite lo + ldrsblo r0, [lr, r0] // get indexed byte out of table ldrsbhs r0, [lr, ip] // if out of range, use last entry in table add ip, lr, r0, lsl #1 // compute label = lr + element*2 bx ip // jump to computed label diff --git a/lib/arm/switchu8.S b/lib/arm/switchu8.S index 19bed2f664d7..0a4efac88abe 100644 --- a/lib/arm/switchu8.S +++ b/lib/arm/switchu8.S @@ -33,7 +33,8 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switchu8) ldrb ip, [lr, #-1] // get first byte in table cmp r0, ip // compare with index - ldrbcc r0, [lr, r0] // get indexed byte out of table + ite lo + ldrblo r0, [lr, r0] // get indexed byte out of table ldrbhs r0, [lr, ip] // if out of range, use last entry in table add ip, lr, r0, lsl #1 // compute label = lr + element*2 bx ip // jump to computed label diff --git a/lib/arm/udivmodsi4.S b/lib/arm/udivmodsi4.S index 9956cd48442f..aee2776671f7 100644 --- a/lib/arm/udivmodsi4.S +++ b/lib/arm/udivmodsi4.S @@ -31,7 +31,7 @@ .syntax unified .align 3 DEFINE_COMPILERRT_FUNCTION(__udivmodsi4) -#if __ARM_ARCH_7S__ +#if __ARM_ARCH_EXT_IDIV__ tst r1, r1 beq LOCAL_LABEL(divzero) mov r3, r0 @@ -74,14 +74,17 @@ LOCAL_LABEL(mainLoop): // this way, we can merge the two branches which is a substantial win for // such a tight loop on current ARM architectures. subs r, a, b, lsl i + itt hs orrhs q, q,one, lsl i movhs a, r + it ne subsne i, i, #1 bhi LOCAL_LABEL(mainLoop) // Do the final test subtraction and update of quotient (i == 0), as it is // not performed in the main loop. subs r, a, b + itt hs orrhs q, #1 movhs a, r diff --git a/lib/arm/udivsi3.S b/lib/arm/udivsi3.S index 28979fee4bdc..2bb14123ca31 100644 --- a/lib/arm/udivsi3.S +++ b/lib/arm/udivsi3.S @@ -33,7 +33,7 @@ // Ok, APCS and AAPCS agree on 32 bit args, so it's safe to use the same routine. DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_uidiv, __udivsi3) DEFINE_COMPILERRT_FUNCTION(__udivsi3) -#if __ARM_ARCH_7S__ +#if __ARM_ARCH_EXT_IDIV__ tst r1,r1 beq LOCAL_LABEL(divzero) udiv r0, r0, r1 @@ -73,14 +73,17 @@ LOCAL_LABEL(mainLoop): // this way, we can merge the two branches which is a substantial win for // such a tight loop on current ARM architectures. subs r, a, b, lsl i + itt hs orrhs q, q,one, lsl i movhs a, r + it ne subsne i, i, #1 bhi LOCAL_LABEL(mainLoop) // Do the final test subtraction and update of quotient (i == 0), as it is // not performed in the main loop. subs r, a, b + it hs orrhs q, #1 LOCAL_LABEL(return): diff --git a/lib/arm/umodsi3.S b/lib/arm/umodsi3.S index 328e7054b857..092a4f1a2062 100644 --- a/lib/arm/umodsi3.S +++ b/lib/arm/umodsi3.S @@ -23,7 +23,7 @@ .syntax unified .align 3 DEFINE_COMPILERRT_FUNCTION(__umodsi3) -#if __ARM_ARCH_7S__ +#if __ARM_ARCH_EXT_IDIV__ tst r1, r1 beq LOCAL_LABEL(divzero) udiv r2, r0, r1 @@ -57,13 +57,16 @@ LOCAL_LABEL(mainLoop): // this way, we can merge the two branches which is a substantial win for // such a tight loop on current ARM architectures. subs r, a, b, lsl i + it hs movhs a, r + it ne subsne i, i, #1 bhi LOCAL_LABEL(mainLoop) // Do the final test subtraction and update of remainder (i == 0), as it is // not performed in the main loop. subs r, a, b + it hs movhs a, r bx lr #endif diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt index a567a4d3e970..ad3f05488ebf 100644 --- a/lib/asan/CMakeLists.txt +++ b/lib/asan/CMakeLists.txt @@ -19,18 +19,19 @@ set(ASAN_SOURCES asan_stack.cc asan_stats.cc asan_thread.cc - asan_win.cc - ) - -set(ASAN_DYLIB_SOURCES - ${ASAN_SOURCES} - ) + asan_win.cc) include_directories(..) -set(ASAN_CFLAGS - ${SANITIZER_COMMON_CFLAGS} - -fno-rtti) +if (NOT MSVC) + set(ASAN_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + -fno-rtti) +else() + set(ASAN_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + /GR-) +endif() set(ASAN_COMMON_DEFINITIONS ASAN_HAS_EXCEPTIONS=1) @@ -40,6 +41,10 @@ if(ANDROID) ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 ASAN_NEEDS_SEGV=0 ASAN_LOW_MEMORY=1) +elseif(MSVC) + list(APPEND ASAN_COMMON_DEFINITIONS + ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 + ASAN_NEEDS_SEGV=0) else() list(APPEND ASAN_COMMON_DEFINITIONS ASAN_FLEXIBLE_MAPPING_AND_OFFSET=1 @@ -48,29 +53,55 @@ endif() # Architectures supported by ASan. filter_available_targets(ASAN_SUPPORTED_ARCH - x86_64 i386 powerpc64 powerpc) + x86_64 i386 powerpc64) +# Compile ASan sources into an object library. +if(APPLE) + foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) + add_compiler_rt_darwin_object_library(RTAsan ${os} + ARCH ${ASAN_SUPPORTED_ARCH} + SOURCES ${ASAN_SOURCES} + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + endforeach() +elseif(ANDROID) + add_library(RTAsan.arm.android OBJECT ${ASAN_SOURCES}) + set_target_compile_flags(RTAsan.arm.android ${ASAN_CFLAGS}) + set_property(TARGET RTAsan.arm.android APPEND PROPERTY + COMPILE_DEFINITIONS ${ASAN_COMMON_DEFINITIONS}) +else() + foreach(arch ${ASAN_SUPPORTED_ARCH}) + add_compiler_rt_object_library(RTAsan ${arch} + SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + endforeach() +endif() + +# Build ASan runtimes shipped with Clang. set(ASAN_RUNTIME_LIBRARIES) if(APPLE) - # Build universal binary on APPLE. - add_compiler_rt_osx_dynamic_runtime(clang_rt.asan_osx_dynamic - ARCH ${ASAN_SUPPORTED_ARCH} - SOURCES ${ASAN_DYLIB_SOURCES} - $<TARGET_OBJECTS:RTInterception.osx> - $<TARGET_OBJECTS:RTSanitizerCommon.osx> - CFLAGS ${ASAN_CFLAGS} - DEFS ${ASAN_COMMON_DEFINITIONS} + foreach (os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) # Dynamic lookup is needed because shadow scale and offset are # provided by the instrumented modules. - LINKFLAGS "-framework Foundation" - "-undefined dynamic_lookup") - list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_osx_dynamic) + set(ASAN_RUNTIME_LDFLAGS + "-undefined dynamic_lookup") + add_compiler_rt_darwin_dynamic_runtime(clang_rt.asan_${os}_dynamic ${os} + ARCH ${ASAN_SUPPORTED_ARCH} + SOURCES $<TARGET_OBJECTS:RTAsan.${os}> + $<TARGET_OBJECTS:RTInterception.${os}> + $<TARGET_OBJECTS:RTSanitizerCommon.${os}> + $<TARGET_OBJECTS:RTLSanCommon.${os}> + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS} + LINKFLAGS ${ASAN_RUNTIME_LDFLAGS}) + list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_${os}_dynamic) + endforeach() + elseif(ANDROID) add_library(clang_rt.asan-arm-android SHARED - ${ASAN_SOURCES} + $<TARGET_OBJECTS:RTAsan.arm.android> $<TARGET_OBJECTS:RTInterception.arm.android> - $<TARGET_OBJECTS:RTSanitizerCommon.arm.android> - ) + $<TARGET_OBJECTS:RTSanitizerCommon.arm.android>) set_target_compile_flags(clang_rt.asan-arm-android ${ASAN_CFLAGS}) set_property(TARGET clang_rt.asan-arm-android APPEND PROPERTY @@ -78,23 +109,44 @@ elseif(ANDROID) target_link_libraries(clang_rt.asan-arm-android dl) list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-arm-android) else() - # Otherwise, build separate libraries for each target. + # Build separate libraries for each target. foreach(arch ${ASAN_SUPPORTED_ARCH}) + set(ASAN_RUNTIME_OBJECTS + $<TARGET_OBJECTS:RTAsan.${arch}> + $<TARGET_OBJECTS:RTInterception.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>) + if (NOT WIN32) + # We can't build Leak Sanitizer on Windows yet. + list(APPEND ASAN_RUNTIME_OBJECTS $<TARGET_OBJECTS:RTLSanCommon.${arch}>) + endif() + add_compiler_rt_static_runtime(clang_rt.asan-${arch} ${arch} - SOURCES ${ASAN_SOURCES} - $<TARGET_OBJECTS:RTInterception.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> - $<TARGET_OBJECTS:RTLSanCommon.${arch}> + SOURCES ${ASAN_RUNTIME_OBJECTS} CFLAGS ${ASAN_CFLAGS} - DEFS ${ASAN_COMMON_DEFINITIONS} - SYMS asan.syms) + DEFS ${ASAN_COMMON_DEFINITIONS}) list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-${arch}) + if (UNIX AND NOT ${arch} STREQUAL "i386") + add_sanitizer_rt_symbols(clang_rt.asan-${arch} asan.syms.extra) + list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-${arch}-symbols) + endif() + + if (WIN32) + add_compiler_rt_static_runtime(clang_rt.asan_dll_thunk-${arch} ${arch} + SOURCES asan_dll_thunk.cc + CFLAGS ${ASAN_CFLAGS} -DASAN_DLL_THUNK + DEFS ${ASAN_COMMON_DEFINITIONS}) + list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_dll_thunk-${arch}) + endif() endforeach() endif() add_compiler_rt_resource_file(asan_blacklist asan_blacklist.txt) +# All ASan runtime dependencies. +add_custom_target(asan_runtime_libraries + DEPENDS asan_blacklist ${ASAN_RUNTIME_LIBRARIES}) + if(LLVM_INCLUDE_TESTS) add_subdirectory(tests) endif() diff --git a/lib/asan/asan.syms b/lib/asan/asan.syms deleted file mode 100644 index fce367314093..000000000000 --- a/lib/asan/asan.syms +++ /dev/null @@ -1,5 +0,0 @@ -{ - __asan_*; - __sanitizer_syscall_pre_*; - __sanitizer_syscall_post_*; -}; diff --git a/lib/asan/asan.syms.extra b/lib/asan/asan.syms.extra new file mode 100644 index 000000000000..007aafe380a8 --- /dev/null +++ b/lib/asan/asan.syms.extra @@ -0,0 +1,3 @@ +__asan_* +__lsan_* +__ubsan_* diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h index f817ce352ee2..c5fcbbb5d64b 100644 --- a/lib/asan/asan_allocator.h +++ b/lib/asan/asan_allocator.h @@ -35,10 +35,11 @@ void InitializeAllocator(); class AsanChunkView { public: explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {} - bool IsValid() { return chunk_ != 0; } - uptr Beg(); // first byte of user memory. - uptr End(); // last byte of user memory. - uptr UsedSize(); // size requested by the user. + bool IsValid(); // Checks if AsanChunkView points to a valid allocated + // or quarantined chunk. + uptr Beg(); // First byte of user memory. + uptr End(); // Last byte of user memory. + uptr UsedSize(); // Size requested by the user. uptr AllocTid(); uptr FreeTid(); void GetAllocStack(StackTrace *stack); @@ -114,7 +115,7 @@ void *asan_pvalloc(uptr size, StackTrace *stack); int asan_posix_memalign(void **memptr, uptr alignment, uptr size, StackTrace *stack); -uptr asan_malloc_usable_size(void *ptr, StackTrace *stack); +uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp); uptr asan_mz_size(const void *ptr); void asan_mz_force_lock(); diff --git a/lib/asan/asan_allocator2.cc b/lib/asan/asan_allocator2.cc index d74aa553a288..7a29975d711e 100644 --- a/lib/asan/asan_allocator2.cc +++ b/lib/asan/asan_allocator2.cc @@ -94,7 +94,7 @@ AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) { static Allocator allocator; static const uptr kMaxAllowedMallocSize = - FIRST_32_SECOND_64(3UL << 30, 8UL << 30); + FIRST_32_SECOND_64(3UL << 30, 64UL << 30); static const uptr kMaxThreadLocalQuarantine = FIRST_32_SECOND_64(1 << 18, 1 << 20); @@ -146,14 +146,15 @@ static uptr ComputeRZLog(uptr user_requested_size) { // ChunkBase consists of ChunkHeader and other bytes that overlap with user // memory. -// If a memory chunk is allocated by memalign and we had to increase the -// allocation size to achieve the proper alignment, then we store this magic +// If the left redzone is greater than the ChunkHeader size we store a magic // value in the first uptr word of the memory block and store the address of // ChunkBase in the next uptr. -// M B ? ? ? L L L L L L H H U U U U U U -// M -- magic value kMemalignMagic +// M B L L L L L L L L L H H U U U U U U +// | ^ +// ---------------------| +// M -- magic value kAllocBegMagic // B -- address of ChunkHeader pointing to the first 'H' -static const uptr kMemalignMagic = 0xCC6E96B9; +static const uptr kAllocBegMagic = 0xCC6E96B9; struct ChunkHeader { // 1-st 8 bytes. @@ -185,14 +186,19 @@ COMPILER_CHECK(kChunkHeader2Size <= 16); struct AsanChunk: ChunkBase { uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; } - uptr UsedSize() { + uptr UsedSize(bool locked_version = false) { if (user_requested_size != SizeClassMap::kMaxSize) return user_requested_size; - return *reinterpret_cast<uptr *>(allocator.GetMetaData(AllocBeg())); - } - void *AllocBeg() { - if (from_memalign) + return *reinterpret_cast<uptr *>( + allocator.GetMetaData(AllocBeg(locked_version))); + } + void *AllocBeg(bool locked_version = false) { + if (from_memalign) { + if (locked_version) + return allocator.GetBlockBeginFastLocked( + reinterpret_cast<void *>(this)); return allocator.GetBlockBegin(reinterpret_cast<void *>(this)); + } return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log)); } // If we don't use stack depot, we store the alloc/free stack traces @@ -212,11 +218,14 @@ struct AsanChunk: ChunkBase { uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY); return (available - kChunkHeader2Size) / sizeof(u32); } - bool AddrIsInside(uptr addr) { - return (addr >= Beg()) && (addr < Beg() + UsedSize()); + bool AddrIsInside(uptr addr, bool locked_version = false) { + return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version)); } }; +bool AsanChunkView::IsValid() { + return chunk_ != 0 && chunk_->chunk_state != CHUNK_AVAILABLE; +} uptr AsanChunkView::Beg() { return chunk_->Beg(); } uptr AsanChunkView::End() { return Beg() + UsedSize(); } uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); } @@ -227,25 +236,16 @@ static void GetStackTraceFromId(u32 id, StackTrace *stack) { CHECK(id); uptr size = 0; const uptr *trace = StackDepotGet(id, &size); - CHECK_LT(size, kStackTraceMax); - internal_memcpy(stack->trace, trace, sizeof(uptr) * size); - stack->size = size; + CHECK(trace); + stack->CopyFrom(trace, size); } void AsanChunkView::GetAllocStack(StackTrace *stack) { - if (flags()->use_stack_depot) - GetStackTraceFromId(chunk_->alloc_context_id, stack); - else - StackTrace::UncompressStack(stack, chunk_->AllocStackBeg(), - chunk_->AllocStackSize()); + GetStackTraceFromId(chunk_->alloc_context_id, stack); } void AsanChunkView::GetFreeStack(StackTrace *stack) { - if (flags()->use_stack_depot) - GetStackTraceFromId(chunk_->free_context_id, stack); - else - StackTrace::UncompressStack(stack, chunk_->FreeStackBeg(), - chunk_->FreeStackSize()); + GetStackTraceFromId(chunk_->free_context_id, stack); } struct QuarantineCallback; @@ -276,10 +276,13 @@ struct QuarantineCallback { RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), kAsanHeapLeftRedzoneMagic); void *p = reinterpret_cast<void *>(m->AllocBeg()); - if (m->from_memalign) { - uptr *memalign_magic = reinterpret_cast<uptr *>(p); - CHECK_EQ(memalign_magic[0], kMemalignMagic); - CHECK_EQ(memalign_magic[1], reinterpret_cast<uptr>(m)); + if (p != m) { + uptr *alloc_magic = reinterpret_cast<uptr *>(p); + CHECK_EQ(alloc_magic[0], kAllocBegMagic); + // Clear the magic value, as allocator internals may overwrite the + // contents of deallocated chunk, confusing GetAsanChunk lookup. + alloc_magic[0] = 0; + CHECK_EQ(alloc_magic[1], reinterpret_cast<uptr>(m)); } // Statistics. @@ -341,7 +344,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", (void*)size); - return 0; + return AllocatorReturnNull(); } AsanThread *t = GetCurrentThread(); @@ -355,8 +358,6 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, allocated = allocator.Allocate(cache, needed_size, 8, false); } uptr alloc_beg = reinterpret_cast<uptr>(allocated); - // Clear the first allocated word (an old kMemalignMagic may still be there). - reinterpret_cast<uptr *>(alloc_beg)[0] = 0; uptr alloc_end = alloc_beg + needed_size; uptr beg_plus_redzone = alloc_beg + rz_size; uptr user_beg = beg_plus_redzone; @@ -373,11 +374,10 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield? m->free_tid = kInvalidTid; m->from_memalign = user_beg != beg_plus_redzone; - if (m->from_memalign) { - CHECK_LE(beg_plus_redzone + 2 * sizeof(uptr), user_beg); - uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg); - memalign_magic[0] = kMemalignMagic; - memalign_magic[1] = chunk_beg; + if (alloc_beg != chunk_beg) { + CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg); + reinterpret_cast<uptr *>(alloc_beg)[0] = kAllocBegMagic; + reinterpret_cast<uptr *>(alloc_beg)[1] = chunk_beg; } if (using_primary_allocator) { CHECK(size); @@ -391,12 +391,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, meta[1] = chunk_beg; } - if (fl.use_stack_depot) { - m->alloc_context_id = StackDepotPut(stack->trace, stack->size); - } else { - m->alloc_context_id = 0; - StackTrace::CompressStack(stack, m->AllocStackBeg(), m->AllocStackSize()); - } + m->alloc_context_id = StackDepotPut(stack->trace, stack->size); uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY); // Unpoison the bulk of the memory region. @@ -405,7 +400,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, // Deal with the end of the region if size is not aligned to granularity. if (size != size_rounded_down_to_granularity && fl.poison_heap) { u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity); - *shadow = size & (SHADOW_GRANULARITY - 1); + *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; } AsanStats &thread_stats = GetCurrentThreadStats(); @@ -422,23 +417,30 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size); REAL(memset)(res, fl.malloc_fill_byte, fill_size); } +#if CAN_SANITIZE_LEAKS + m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored + : __lsan::kDirectlyLeaked; +#endif // Must be the last mutation of metadata in this function. atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release); ASAN_MALLOC_HOOK(res, size); return res; } +static void ReportInvalidFree(void *ptr, u8 chunk_state, StackTrace *stack) { + if (chunk_state == CHUNK_QUARANTINE) + ReportDoubleFree((uptr)ptr, stack); + else + ReportFreeNotMalloced((uptr)ptr, stack); +} + static void AtomicallySetQuarantineFlag(AsanChunk *m, void *ptr, StackTrace *stack) { u8 old_chunk_state = CHUNK_ALLOCATED; // Flip the chunk_state atomically to avoid race on double-free. if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state, - CHUNK_QUARANTINE, memory_order_acquire)) { - if (old_chunk_state == CHUNK_QUARANTINE) - ReportDoubleFree((uptr)ptr, stack); - else - ReportFreeNotMalloced((uptr)ptr, stack); - } + CHUNK_QUARANTINE, memory_order_acquire)) + ReportInvalidFree(ptr, old_chunk_state, stack); CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state); } @@ -448,12 +450,6 @@ static void QuarantineChunk(AsanChunk *m, void *ptr, StackTrace *stack, AllocType alloc_type) { CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); - // FIXME: if the free hook produces an ASan report (e.g. due to a bug), - // printing the report may crash as the AsanChunk free-related fields have not - // been updated yet. We might need to introduce yet another chunk state to - // handle this correctly, but don't want to yet. - ASAN_FREE_HOOK(ptr); - if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch) ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type, (AllocType)alloc_type); @@ -463,12 +459,7 @@ static void QuarantineChunk(AsanChunk *m, void *ptr, CHECK_EQ(m->free_tid, kInvalidTid); AsanThread *t = GetCurrentThread(); m->free_tid = t ? t->tid() : 0; - if (flags()->use_stack_depot) { - m->free_context_id = StackDepotPut(stack->trace, stack->size); - } else { - m->free_context_id = 0; - StackTrace::CompressStack(stack, m->FreeStackBeg(), m->FreeStackSize()); - } + m->free_context_id = StackDepotPut(stack->trace, stack->size); // Poison the region. PoisonShadow(m->Beg(), RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), @@ -498,6 +489,7 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { uptr chunk_beg = p - kChunkHeaderSize; AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); + ASAN_FREE_HOOK(ptr); // Must mark the chunk as quarantined before any changes to its metadata. AtomicallySetQuarantineFlag(m, ptr, stack); QuarantineChunk(m, ptr, stack, alloc_type); @@ -513,50 +505,45 @@ static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) { thread_stats.reallocs++; thread_stats.realloced += new_size; - // Must mark the chunk as quarantined before any changes to its metadata. - // This also ensures that other threads can't deallocate it in the meantime. - AtomicallySetQuarantineFlag(m, old_ptr, stack); - - uptr old_size = m->UsedSize(); - uptr memcpy_size = Min(new_size, old_size); void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); if (new_ptr) { + u8 chunk_state = m->chunk_state; + if (chunk_state != CHUNK_ALLOCATED) + ReportInvalidFree(old_ptr, chunk_state, stack); CHECK_NE(REAL(memcpy), (void*)0); + uptr memcpy_size = Min(new_size, m->UsedSize()); + // If realloc() races with free(), we may start copying freed memory. + // However, we will report racy double-free later anyway. REAL(memcpy)(new_ptr, old_ptr, memcpy_size); - QuarantineChunk(m, old_ptr, stack, FROM_MALLOC); + Deallocate(old_ptr, stack, FROM_MALLOC); } return new_ptr; } -static AsanChunk *GetAsanChunkByAddr(uptr p) { - void *ptr = reinterpret_cast<void *>(p); - uptr alloc_beg = reinterpret_cast<uptr>(allocator.GetBlockBegin(ptr)); +// Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). +static AsanChunk *GetAsanChunk(void *alloc_beg) { if (!alloc_beg) return 0; - uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg); - if (memalign_magic[0] == kMemalignMagic) { - AsanChunk *m = reinterpret_cast<AsanChunk *>(memalign_magic[1]); - CHECK(m->from_memalign); - return m; - } - if (!allocator.FromPrimary(ptr)) { - uptr *meta = reinterpret_cast<uptr *>( - allocator.GetMetaData(reinterpret_cast<void *>(alloc_beg))); + if (!allocator.FromPrimary(alloc_beg)) { + uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(alloc_beg)); AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]); return m; } - uptr actual_size = allocator.GetActuallyAllocatedSize(ptr); - CHECK_LE(actual_size, SizeClassMap::kMaxSize); - // We know the actually allocted size, but we don't know the redzone size. - // Just try all possible redzone sizes. - for (u32 rz_log = 0; rz_log < 8; rz_log++) { - u32 rz_size = RZLog2Size(rz_log); - uptr max_possible_size = actual_size - rz_size; - if (ComputeRZLog(max_possible_size) != rz_log) - continue; - return reinterpret_cast<AsanChunk *>( - alloc_beg + rz_size - kChunkHeaderSize); - } - return 0; + uptr *alloc_magic = reinterpret_cast<uptr *>(alloc_beg); + if (alloc_magic[0] == kAllocBegMagic) + return reinterpret_cast<AsanChunk *>(alloc_magic[1]); + return reinterpret_cast<AsanChunk *>(alloc_beg); +} + +static AsanChunk *GetAsanChunkByAddr(uptr p) { + void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast<void *>(p)); + return GetAsanChunk(alloc_beg); +} + +// Allocator must be locked when this function is called. +static AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) { + void *alloc_beg = + allocator.GetBlockBeginFastLocked(reinterpret_cast<void *>(p)); + return GetAsanChunk(alloc_beg); } static uptr AllocationSize(uptr p) { @@ -621,24 +608,22 @@ void PrintInternalAllocatorStats() { allocator.PrintStats(); } -SANITIZER_INTERFACE_ATTRIBUTE void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, AllocType alloc_type) { return Allocate(size, alignment, stack, alloc_type, true); } -SANITIZER_INTERFACE_ATTRIBUTE void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { Deallocate(ptr, stack, alloc_type); } -SANITIZER_INTERFACE_ATTRIBUTE void *asan_malloc(uptr size, StackTrace *stack) { return Allocate(size, 8, stack, FROM_MALLOC, true); } void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + return AllocatorReturnNull(); void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); // If the memory comes from the secondary allocator no need to clear it // as it comes directly from mmap. @@ -679,12 +664,13 @@ int asan_posix_memalign(void **memptr, uptr alignment, uptr size, return 0; } -uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) { - CHECK(stack); +uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) { if (ptr == 0) return 0; uptr usable_size = AllocationSize(reinterpret_cast<uptr>(ptr)); - if (flags()->check_malloc_usable_size && (usable_size == 0)) - ReportMallocUsableSizeNotOwned((uptr)ptr, stack); + if (flags()->check_malloc_usable_size && (usable_size == 0)) { + GET_STACK_TRACE_FATAL(pc, bp); + ReportMallocUsableSizeNotOwned((uptr)ptr, &stack); + } return usable_size; } @@ -719,25 +705,26 @@ void GetAllocatorGlobalRange(uptr *begin, uptr *end) { *end = *begin + sizeof(__asan::allocator); } -void *PointsIntoChunk(void* p) { +uptr PointsIntoChunk(void* p) { uptr addr = reinterpret_cast<uptr>(p); - __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(addr); + __asan::AsanChunk *m = __asan::GetAsanChunkByAddrFastLocked(addr); if (!m) return 0; uptr chunk = m->Beg(); - if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) - return reinterpret_cast<void *>(chunk); + if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && + m->AddrIsInside(addr, /*locked_version=*/true)) + return chunk; return 0; } -void *GetUserBegin(void *p) { - __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(reinterpret_cast<uptr>(p)); +uptr GetUserBegin(uptr chunk) { + __asan::AsanChunk *m = + __asan::GetAsanChunkByAddrFastLocked(chunk); CHECK(m); - return reinterpret_cast<void *>(m->Beg()); + return m->Beg(); } -LsanMetadata::LsanMetadata(void *chunk) { - uptr addr = reinterpret_cast<uptr>(chunk); - metadata_ = reinterpret_cast<void *>(addr - __asan::kChunkHeaderSize); +LsanMetadata::LsanMetadata(uptr chunk) { + metadata_ = reinterpret_cast<void *>(chunk - __asan::kChunkHeaderSize); } bool LsanMetadata::allocated() const { @@ -757,7 +744,7 @@ void LsanMetadata::set_tag(ChunkTag value) { uptr LsanMetadata::requested_size() const { __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return m->UsedSize(); + return m->UsedSize(/*locked_version=*/true); } u32 LsanMetadata::stack_trace_id() const { @@ -765,18 +752,23 @@ u32 LsanMetadata::stack_trace_id() const { return m->alloc_context_id; } -template <typename Callable> void ForEachChunk(Callable const &callback) { - __asan::allocator.ForEachChunk(callback); +void ForEachChunk(ForEachChunkCallback callback, void *arg) { + __asan::allocator.ForEachChunk(callback, arg); +} + +IgnoreObjectResult IgnoreObjectLocked(const void *p) { + uptr addr = reinterpret_cast<uptr>(p); + __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(addr); + if (!m) return kIgnoreObjectInvalid; + if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) { + if (m->lsan_tag == kIgnored) + return kIgnoreObjectAlreadyIgnored; + m->lsan_tag = __lsan::kIgnored; + return kIgnoreObjectSuccess; + } else { + return kIgnoreObjectInvalid; + } } -#if CAN_SANITIZE_LEAKS -template void ForEachChunk<ProcessPlatformSpecificAllocationsCb>( - ProcessPlatformSpecificAllocationsCb const &callback); -template void ForEachChunk<PrintLeakedCb>(PrintLeakedCb const &callback); -template void ForEachChunk<CollectLeaksCb>(CollectLeaksCb const &callback); -template void ForEachChunk<MarkIndirectlyLeakedCb>( - MarkIndirectlyLeakedCb const &callback); -template void ForEachChunk<ClearTagCb>(ClearTagCb const &callback); -#endif // CAN_SANITIZE_LEAKS } // namespace __lsan // ---------------------- Interface ---------------- {{{1 @@ -808,12 +800,12 @@ uptr __asan_get_allocated_size(const void *p) { #if !SANITIZER_SUPPORTS_WEAK_HOOKS // Provide default (no-op) implementation of malloc hooks. extern "C" { -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __asan_malloc_hook(void *ptr, uptr size) { (void)ptr; (void)size; } -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __asan_free_hook(void *ptr) { (void)ptr; } diff --git a/lib/asan/asan_blacklist.txt b/lib/asan/asan_blacklist.txt index 03da08598d23..63b3c315b26b 100644 --- a/lib/asan/asan_blacklist.txt +++ b/lib/asan/asan_blacklist.txt @@ -6,5 +6,5 @@ # fun:*bad_function_name* # src:file_with_tricky_code.cc # global:*global_with_bad_access_or_initialization* -# global-init:*global_with_initialization_issues* -# global-init-type:*Namespace::ClassName* +# global:*global_with_initialization_issues*=init +# type:*Namespace::ClassName*=init diff --git a/lib/asan/asan_dll_thunk.cc b/lib/asan/asan_dll_thunk.cc new file mode 100644 index 000000000000..cedd60d342f6 --- /dev/null +++ b/lib/asan/asan_dll_thunk.cc @@ -0,0 +1,196 @@ +//===-- asan_dll_thunk.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This file defines a family of thunks that should be statically linked into +// the DLLs that have ASan instrumentation in order to delegate the calls to the +// shared runtime that lives in the main binary. +// See https://code.google.com/p/address-sanitizer/issues/detail?id=209 for the +// details. +//===----------------------------------------------------------------------===// + +// Only compile this code when buidling asan_dll_thunk.lib +// Using #ifdef rather than relying on Makefiles etc. +// simplifies the build procedure. +#ifdef ASAN_DLL_THUNK + +// ----------------- Helper functions and macros --------------------- {{{1 +extern "C" { +void *__stdcall GetModuleHandleA(const char *module_name); +void *__stdcall GetProcAddress(void *module, const char *proc_name); +void abort(); +} + +static void *getRealProcAddressOrDie(const char *name) { + void *ret = GetProcAddress(GetModuleHandleA(0), name); + if (!ret) + abort(); + return ret; +} + +#define WRAP_V_V(name) \ + extern "C" void name() { \ + typedef void (*fntype)(); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + fn(); \ + } + +#define WRAP_V_W(name) \ + extern "C" void name(void *arg) { \ + typedef void (*fntype)(void *arg); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + fn(arg); \ + } + +#define WRAP_V_WW(name) \ + extern "C" void name(void *arg1, void *arg2) { \ + typedef void (*fntype)(void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + fn(arg1, arg2); \ + } + +#define WRAP_V_WWW(name) \ + extern "C" void name(void *arg1, void *arg2, void *arg3) { \ + typedef void *(*fntype)(void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + fn(arg1, arg2, arg3); \ + } + +#define WRAP_W_V(name) \ + extern "C" void *name() { \ + typedef void *(*fntype)(); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(); \ + } + +#define WRAP_W_W(name) \ + extern "C" void *name(void *arg) { \ + typedef void *(*fntype)(void *arg); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg); \ + } + +#define WRAP_W_WW(name) \ + extern "C" void *name(void *arg1, void *arg2) { \ + typedef void *(*fntype)(void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2); \ + } + +#define WRAP_W_WWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3) { \ + typedef void *(*fntype)(void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2, arg3); \ + } + +#define WRAP_W_WWWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \ + typedef void *(*fntype)(void *, void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2, arg3, arg4); \ + } + +#define WRAP_W_WWWWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ + void *arg5) { \ + typedef void *(*fntype)(void *, void *, void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2, arg3, arg4, arg5); \ + } + +#define WRAP_W_WWWWWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ + void *arg5, void *arg6) { \ + typedef void *(*fntype)(void *, void *, void *, void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2, arg3, arg4, arg5, arg6); \ + } +// }}} + +// ----------------- ASan own interface functions -------------------- +WRAP_W_V(__asan_should_detect_stack_use_after_return) + +extern "C" { + int __asan_option_detect_stack_use_after_return; + + // Manually wrap __asan_init as we need to initialize + // __asan_option_detect_stack_use_after_return afterwards. + void __asan_init_v3() { + typedef void (*fntype)(); + static fntype fn = (fntype)getRealProcAddressOrDie("__asan_init_v3"); + fn(); + __asan_option_detect_stack_use_after_return = + (__asan_should_detect_stack_use_after_return() != 0); + } +} + +WRAP_V_W(__asan_report_store1) +WRAP_V_W(__asan_report_store2) +WRAP_V_W(__asan_report_store4) +WRAP_V_W(__asan_report_store8) +WRAP_V_W(__asan_report_store16) +WRAP_V_WW(__asan_report_store_n) + +WRAP_V_W(__asan_report_load1) +WRAP_V_W(__asan_report_load2) +WRAP_V_W(__asan_report_load4) +WRAP_V_W(__asan_report_load8) +WRAP_V_W(__asan_report_load16) +WRAP_V_WW(__asan_report_load_n) + +WRAP_V_WW(__asan_register_globals) +WRAP_V_WW(__asan_unregister_globals) + +WRAP_W_WW(__asan_stack_malloc_0) +WRAP_W_WW(__asan_stack_malloc_1) +WRAP_W_WW(__asan_stack_malloc_2) +WRAP_W_WW(__asan_stack_malloc_3) +WRAP_W_WW(__asan_stack_malloc_4) +WRAP_W_WW(__asan_stack_malloc_5) +WRAP_W_WW(__asan_stack_malloc_6) +WRAP_W_WW(__asan_stack_malloc_7) +WRAP_W_WW(__asan_stack_malloc_8) +WRAP_W_WW(__asan_stack_malloc_9) +WRAP_W_WW(__asan_stack_malloc_10) + +WRAP_V_WWW(__asan_stack_free_0) +WRAP_V_WWW(__asan_stack_free_1) +WRAP_V_WWW(__asan_stack_free_2) +WRAP_V_WWW(__asan_stack_free_4) +WRAP_V_WWW(__asan_stack_free_5) +WRAP_V_WWW(__asan_stack_free_6) +WRAP_V_WWW(__asan_stack_free_7) +WRAP_V_WWW(__asan_stack_free_8) +WRAP_V_WWW(__asan_stack_free_9) +WRAP_V_WWW(__asan_stack_free_10) + +// TODO(timurrrr): Add more interface functions on the as-needed basis. + +// ----------------- Memory allocation functions --------------------- +WRAP_V_W(free) +WRAP_V_WW(_free_dbg) + +WRAP_W_W(malloc) +WRAP_W_WWWW(_malloc_dbg) + +WRAP_W_WW(calloc) +WRAP_W_WWWWW(_calloc_dbg) +WRAP_W_WWW(_calloc_impl) + +WRAP_W_WW(realloc) +WRAP_W_WWW(_realloc_dbg) +WRAP_W_WWW(_recalloc) + +WRAP_W_W(_msize) + +// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc). + +#endif // ASAN_DLL_THUNK diff --git a/lib/asan/asan_fake_stack.cc b/lib/asan/asan_fake_stack.cc index 23eebe64e612..d3e55bff1e6e 100644 --- a/lib/asan/asan_fake_stack.cc +++ b/lib/asan/asan_fake_stack.cc @@ -17,167 +17,204 @@ namespace __asan { -FakeStack::FakeStack() { - CHECK(REAL(memset)); - REAL(memset)(this, 0, sizeof(*this)); +static const u64 kMagic1 = kAsanStackAfterReturnMagic; +static const u64 kMagic2 = (kMagic1 << 8) | kMagic1; +static const u64 kMagic4 = (kMagic2 << 16) | kMagic2; +static const u64 kMagic8 = (kMagic4 << 32) | kMagic4; + +// For small size classes inline PoisonShadow for better performance. +ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) { + CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3. + u64 *shadow = reinterpret_cast<u64*>(MemToShadow(ptr)); + if (class_id <= 6) { + for (uptr i = 0; i < (1U << class_id); i++) + shadow[i] = magic; + } else { + // The size class is too big, it's cheaper to poison only size bytes. + PoisonShadow(ptr, size, static_cast<u8>(magic)); + } } -bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) { - uptr mem = allocated_size_classes_[size_class]; - uptr size = ClassMmapSize(size_class); - bool res = mem && addr >= mem && addr < mem + size; +FakeStack *FakeStack::Create(uptr stack_size_log) { + static uptr kMinStackSizeLog = 16; + static uptr kMaxStackSizeLog = FIRST_32_SECOND_64(24, 28); + if (stack_size_log < kMinStackSizeLog) + stack_size_log = kMinStackSizeLog; + if (stack_size_log > kMaxStackSizeLog) + stack_size_log = kMaxStackSizeLog; + FakeStack *res = reinterpret_cast<FakeStack *>( + MmapOrDie(RequiredSize(stack_size_log), "FakeStack")); + res->stack_size_log_ = stack_size_log; + if (common_flags()->verbosity) { + u8 *p = reinterpret_cast<u8 *>(res); + Report("T%d: FakeStack created: %p -- %p stack_size_log: %zd \n", + GetCurrentTidOrInvalid(), p, + p + FakeStack::RequiredSize(stack_size_log), stack_size_log); + } return res; } -uptr FakeStack::AddrIsInFakeStack(uptr addr) { - for (uptr size_class = 0; size_class < kNumberOfSizeClasses; size_class++) { - if (!AddrIsInSizeClass(addr, size_class)) continue; - uptr size_class_first_ptr = allocated_size_classes_[size_class]; - uptr size = ClassSize(size_class); - CHECK_LE(size_class_first_ptr, addr); - CHECK_GT(size_class_first_ptr + ClassMmapSize(size_class), addr); - return size_class_first_ptr + ((addr - size_class_first_ptr) / size) * size; - } - return 0; +void FakeStack::Destroy() { + PoisonAll(0); + UnmapOrDie(this, RequiredSize(stack_size_log_)); } -// We may want to compute this during compilation. -ALWAYS_INLINE uptr FakeStack::ComputeSizeClass(uptr alloc_size) { - uptr rounded_size = RoundUpToPowerOfTwo(alloc_size); - uptr log = Log2(rounded_size); - CHECK_LE(alloc_size, (1UL << log)); - CHECK_GT(alloc_size, (1UL << (log-1))); - uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog; - CHECK_LT(res, kNumberOfSizeClasses); - CHECK_GE(ClassSize(res), rounded_size); - return res; +void FakeStack::PoisonAll(u8 magic) { + PoisonShadow(reinterpret_cast<uptr>(this), RequiredSize(stack_size_log()), + magic); } -void FakeFrameFifo::FifoPush(FakeFrame *node) { - CHECK(node); - node->next = 0; - if (first_ == 0 && last_ == 0) { - first_ = last_ = node; - } else { - CHECK(first_); - CHECK(last_); - last_->next = node; - last_ = node; +ALWAYS_INLINE USED +FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id, + uptr real_stack) { + CHECK_LT(class_id, kNumberOfSizeClasses); + if (needs_gc_) + GC(real_stack); + uptr &hint_position = hint_position_[class_id]; + const int num_iter = NumberOfFrames(stack_size_log, class_id); + u8 *flags = GetFlags(stack_size_log, class_id); + for (int i = 0; i < num_iter; i++) { + uptr pos = ModuloNumberOfFrames(stack_size_log, class_id, hint_position++); + // This part is tricky. On one hand, checking and setting flags[pos] + // should be atomic to ensure async-signal safety. But on the other hand, + // if the signal arrives between checking and setting flags[pos], the + // signal handler's fake stack will start from a different hint_position + // and so will not touch this particular byte. So, it is safe to do this + // with regular non-atimic load and store (at least I was not able to make + // this code crash). + if (flags[pos]) continue; + flags[pos] = 1; + FakeFrame *res = reinterpret_cast<FakeFrame *>( + GetFrame(stack_size_log, class_id, pos)); + res->real_stack = real_stack; + *SavedFlagPtr(reinterpret_cast<uptr>(res), class_id) = &flags[pos]; + return res; } + return 0; // We are out of fake stack. } -FakeFrame *FakeFrameFifo::FifoPop() { - CHECK(first_ && last_ && "Exhausted fake stack"); - FakeFrame *res = 0; - if (first_ == last_) { - res = first_; - first_ = last_ = 0; - } else { - res = first_; - first_ = first_->next; - } - return res; +uptr FakeStack::AddrIsInFakeStack(uptr ptr) { + uptr stack_size_log = this->stack_size_log(); + uptr beg = reinterpret_cast<uptr>(GetFrame(stack_size_log, 0, 0)); + uptr end = reinterpret_cast<uptr>(this) + RequiredSize(stack_size_log); + if (ptr < beg || ptr >= end) return 0; + uptr class_id = (ptr - beg) >> stack_size_log; + uptr base = beg + (class_id << stack_size_log); + CHECK_LE(base, ptr); + CHECK_LT(ptr, base + (1UL << stack_size_log)); + uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id); + return base + pos * BytesInSizeClass(class_id); } -void FakeStack::Init(uptr stack_size) { - stack_size_ = stack_size; - alive_ = true; +void FakeStack::HandleNoReturn() { + needs_gc_ = true; } -void FakeStack::Cleanup() { - alive_ = false; - for (uptr i = 0; i < kNumberOfSizeClasses; i++) { - uptr mem = allocated_size_classes_[i]; - if (mem) { - PoisonShadow(mem, ClassMmapSize(i), 0); - allocated_size_classes_[i] = 0; - UnmapOrDie((void*)mem, ClassMmapSize(i)); +// When throw, longjmp or some such happens we don't call OnFree() and +// as the result may leak one or more fake frames, but the good news is that +// we are notified about all such events by HandleNoReturn(). +// If we recently had such no-return event we need to collect garbage frames. +// We do it based on their 'real_stack' values -- everything that is lower +// than the current real_stack is garbage. +NOINLINE void FakeStack::GC(uptr real_stack) { + uptr collected = 0; + for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { + u8 *flags = GetFlags(stack_size_log(), class_id); + for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; + i++) { + if (flags[i] == 0) continue; // not allocated. + FakeFrame *ff = reinterpret_cast<FakeFrame *>( + GetFrame(stack_size_log(), class_id, i)); + if (ff->real_stack < real_stack) { + flags[i] = 0; + collected++; + } } } + needs_gc_ = false; } -uptr FakeStack::ClassMmapSize(uptr size_class) { - return RoundUpToPowerOfTwo(stack_size_); +void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void *arg) { + for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { + u8 *flags = GetFlags(stack_size_log(), class_id); + for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; + i++) { + if (flags[i] == 0) continue; // not allocated. + FakeFrame *ff = reinterpret_cast<FakeFrame *>( + GetFrame(stack_size_log(), class_id, i)); + uptr begin = reinterpret_cast<uptr>(ff); + callback(begin, begin + FakeStack::BytesInSizeClass(class_id), arg); + } + } } -void FakeStack::AllocateOneSizeClass(uptr size_class) { - CHECK(ClassMmapSize(size_class) >= GetPageSizeCached()); - uptr new_mem = (uptr)MmapOrDie( - ClassMmapSize(size_class), __FUNCTION__); - // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n", - // GetCurrentThread()->tid(), - // size_class, new_mem, new_mem + ClassMmapSize(size_class), - // ClassMmapSize(size_class)); - uptr i; - for (i = 0; i < ClassMmapSize(size_class); - i += ClassSize(size_class)) { - size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i)); - } - CHECK(i == ClassMmapSize(size_class)); - allocated_size_classes_[size_class] = new_mem; +#if SANITIZER_LINUX && !SANITIZER_ANDROID +static THREADLOCAL FakeStack *fake_stack_tls; + +FakeStack *GetTLSFakeStack() { + return fake_stack_tls; +} +void SetTLSFakeStack(FakeStack *fs) { + fake_stack_tls = fs; } +#else +FakeStack *GetTLSFakeStack() { return 0; } +void SetTLSFakeStack(FakeStack *fs) { } +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID -ALWAYS_INLINE uptr FakeStack::AllocateStack(uptr size, uptr real_stack) { - if (!alive_) return real_stack; - CHECK(size <= kMaxStackMallocSize && size > 1); - uptr size_class = ComputeSizeClass(size); - if (!allocated_size_classes_[size_class]) { - AllocateOneSizeClass(size_class); - } - FakeFrame *fake_frame = size_classes_[size_class].FifoPop(); - CHECK(fake_frame); - fake_frame->size_minus_one = size - 1; - fake_frame->real_stack = real_stack; - while (FakeFrame *top = call_stack_.top()) { - if (top->real_stack > real_stack) break; - call_stack_.LifoPop(); - DeallocateFrame(top); - } - call_stack_.LifoPush(fake_frame); - uptr ptr = (uptr)fake_frame; - PoisonShadow(ptr, size, 0); - return ptr; +static FakeStack *GetFakeStack() { + AsanThread *t = GetCurrentThread(); + if (!t) return 0; + return t->fake_stack(); } -ALWAYS_INLINE void FakeStack::DeallocateFrame(FakeFrame *fake_frame) { - CHECK(alive_); - uptr size = fake_frame->size_minus_one + 1; - uptr size_class = ComputeSizeClass(size); - CHECK(allocated_size_classes_[size_class]); - uptr ptr = (uptr)fake_frame; - CHECK(AddrIsInSizeClass(ptr, size_class)); - CHECK(AddrIsInSizeClass(ptr + size - 1, size_class)); - size_classes_[size_class].FifoPush(fake_frame); +static FakeStack *GetFakeStackFast() { + if (FakeStack *fs = GetTLSFakeStack()) + return fs; + if (!__asan_option_detect_stack_use_after_return) + return 0; + return GetFakeStack(); +} + +ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size, uptr real_stack) { + FakeStack *fs = GetFakeStackFast(); + if (!fs) return real_stack; + FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack); + if (!ff) + return real_stack; // Out of fake stack, return the real one. + uptr ptr = reinterpret_cast<uptr>(ff); + SetShadow(ptr, size, class_id, 0); + return ptr; } -ALWAYS_INLINE void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) { - FakeFrame *fake_frame = (FakeFrame*)ptr; - CHECK_EQ(fake_frame->magic, kRetiredStackFrameMagic); - CHECK_NE(fake_frame->descr, 0); - CHECK_EQ(fake_frame->size_minus_one, size - 1); - PoisonShadow(ptr, size, kAsanStackAfterReturnMagic); +ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size, uptr real_stack) { + if (ptr == real_stack) + return; + FakeStack::Deallocate(ptr, class_id); + SetShadow(ptr, size, class_id, kMagic8); } } // namespace __asan // ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - -uptr __asan_stack_malloc(uptr size, uptr real_stack) { - if (!flags()->use_fake_stack) return real_stack; - AsanThread *t = GetCurrentThread(); - if (!t) { - // TSD is gone, use the real stack. - return real_stack; +#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \ + __asan_stack_malloc_##class_id(uptr size, uptr real_stack) { \ + return __asan::OnMalloc(class_id, size, real_stack); \ + } \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \ + uptr ptr, uptr size, uptr real_stack) { \ + __asan::OnFree(ptr, class_id, size, real_stack); \ } - uptr ptr = t->fake_stack().AllocateStack(size, real_stack); - // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack); - return ptr; -} -void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) { - if (!flags()->use_fake_stack) return; - if (ptr != real_stack) { - FakeStack::OnFree(ptr, size, real_stack); - } -} +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10) diff --git a/lib/asan/asan_fake_stack.h b/lib/asan/asan_fake_stack.h index 308b4c571832..f17ee0268917 100644 --- a/lib/asan/asan_fake_stack.h +++ b/lib/asan/asan_fake_stack.h @@ -9,12 +9,14 @@ // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_fake_stack.cc +// ASan-private header for asan_fake_stack.cc, implements FakeStack. //===----------------------------------------------------------------------===// #ifndef ASAN_FAKE_STACK_H #define ASAN_FAKE_STACK_H +#include "sanitizer_common/sanitizer_common.h" + namespace __asan { // Fake stack frame contains local variables of one function. @@ -22,96 +24,148 @@ struct FakeFrame { uptr magic; // Modified by the instrumented code. uptr descr; // Modified by the instrumented code. uptr pc; // Modified by the instrumented code. - u64 real_stack : 48; - u64 size_minus_one : 16; - // End of the first 32 bytes. - // The rest should not be used when the frame is active. - FakeFrame *next; -}; - -struct FakeFrameFifo { - public: - void FifoPush(FakeFrame *node); - FakeFrame *FifoPop(); - private: - FakeFrame *first_, *last_; -}; - -template<uptr kMaxNumberOfFrames> -class FakeFrameLifo { - public: - explicit FakeFrameLifo(LinkerInitialized) {} - FakeFrameLifo() : n_frames_(0) {} - void LifoPush(FakeFrame *node) { - CHECK_LT(n_frames_, kMaxNumberOfFrames); - frames_[n_frames_++] = node; - } - void LifoPop() { - CHECK(n_frames_); - n_frames_--; - } - FakeFrame *top() { - if (n_frames_ == 0) - return 0; - return frames_[n_frames_ - 1]; - } - private: - uptr n_frames_; - FakeFrame *frames_[kMaxNumberOfFrames]; + uptr real_stack; }; // For each thread we create a fake stack and place stack objects on this fake // stack instead of the real stack. The fake stack is not really a stack but // a fast malloc-like allocator so that when a function exits the fake stack -// is not poped but remains there for quite some time until gets used again. +// is not popped but remains there for quite some time until gets used again. // So, we poison the objects on the fake stack when function returns. // It helps us find use-after-return bugs. -// We can not rely on __asan_stack_free being called on every function exit, -// so we maintain a lifo list of all current fake frames and update it on every -// call to __asan_stack_malloc. +// +// The FakeStack objects is allocated by a single mmap call and has no other +// pointers. The size of the fake stack depends on the actual thread stack size +// and thus can not be a constant. +// stack_size is a power of two greater or equal to the thread's stack size; +// we store it as its logarithm (stack_size_log). +// FakeStack has kNumberOfSizeClasses (11) size classes, each size class +// is a power of two, starting from 64 bytes. Each size class occupies +// stack_size bytes and thus can allocate +// NumberOfFrames=(stack_size/BytesInSizeClass) fake frames (also a power of 2). +// For each size class we have NumberOfFrames allocation flags, +// each flag indicates whether the given frame is currently allocated. +// All flags for size classes 0 .. 10 are stored in a single contiguous region +// followed by another contiguous region which contains the actual memory for +// size classes. The addresses are computed by GetFlags and GetFrame without +// any memory accesses solely based on 'this' and stack_size_log. +// Allocate() flips the appropriate allocation flag atomically, thus achieving +// async-signal safety. +// This allocator does not have quarantine per se, but it tries to allocate the +// frames in round robin fasion to maximize the delay between a deallocation +// and the next allocation. class FakeStack { - public: - FakeStack(); - explicit FakeStack(LinkerInitialized x) : call_stack_(x) {} - void Init(uptr stack_size); - void StopUsingFakeStack() { alive_ = false; } - void Cleanup(); - uptr AllocateStack(uptr size, uptr real_stack); - static void OnFree(uptr ptr, uptr size, uptr real_stack); - // Return the bottom of the maped region. - uptr AddrIsInFakeStack(uptr addr); - bool StackSize() { return stack_size_; } - - private: - static const uptr kMinStackFrameSizeLog = 9; // Min frame is 512B. + static const uptr kMinStackFrameSizeLog = 6; // Min frame is 64B. static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K. - static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; + + public: static const uptr kNumberOfSizeClasses = - kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1; - static const uptr kMaxRecursionDepth = 1023; + kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1; + + // CTOR: create the FakeStack as a single mmap-ed object. + static FakeStack *Create(uptr stack_size_log); + + void Destroy(); + + // stack_size_log is at least 15 (stack_size >= 32K). + static uptr SizeRequiredForFlags(uptr stack_size_log) { + return 1UL << (stack_size_log + 1 - kMinStackFrameSizeLog); + } - bool AddrIsInSizeClass(uptr addr, uptr size_class); + // Each size class occupies stack_size bytes. + static uptr SizeRequiredForFrames(uptr stack_size_log) { + return (1ULL << stack_size_log) * kNumberOfSizeClasses; + } + + // Number of bytes requires for the whole object. + static uptr RequiredSize(uptr stack_size_log) { + return kFlagsOffset + SizeRequiredForFlags(stack_size_log) + + SizeRequiredForFrames(stack_size_log); + } + + // Offset of the given flag from the first flag. + // The flags for class 0 begin at offset 000000000 + // The flags for class 1 begin at offset 100000000 + // ....................2................ 110000000 + // ....................3................ 111000000 + // and so on. + static uptr FlagsOffset(uptr stack_size_log, uptr class_id) { + uptr t = kNumberOfSizeClasses - 1 - class_id; + const uptr all_ones = (1 << (kNumberOfSizeClasses - 1)) - 1; + return ((all_ones >> t) << t) << (stack_size_log - 15); + } + + static uptr NumberOfFrames(uptr stack_size_log, uptr class_id) { + return 1UL << (stack_size_log - kMinStackFrameSizeLog - class_id); + } + + // Divide n by the numbe of frames in size class. + static uptr ModuloNumberOfFrames(uptr stack_size_log, uptr class_id, uptr n) { + return n & (NumberOfFrames(stack_size_log, class_id) - 1); + } + + // The the pointer to the flags of the given class_id. + u8 *GetFlags(uptr stack_size_log, uptr class_id) { + return reinterpret_cast<u8 *>(this) + kFlagsOffset + + FlagsOffset(stack_size_log, class_id); + } + + // Get frame by class_id and pos. + u8 *GetFrame(uptr stack_size_log, uptr class_id, uptr pos) { + return reinterpret_cast<u8 *>(this) + kFlagsOffset + + SizeRequiredForFlags(stack_size_log) + + (1 << stack_size_log) * class_id + BytesInSizeClass(class_id) * pos; + } + + // Allocate the fake frame. + FakeFrame *Allocate(uptr stack_size_log, uptr class_id, uptr real_stack); + + // Deallocate the fake frame: read the saved flag address and write 0 there. + static void Deallocate(uptr x, uptr class_id) { + **SavedFlagPtr(x, class_id) = 0; + } + + // Poison the entire FakeStack's shadow with the magic value. + void PoisonAll(u8 magic); + + // Return the beginning of the FakeFrame or 0 if the address is not ours. + uptr AddrIsInFakeStack(uptr addr); - // Each size class should be large enough to hold all frames. - uptr ClassMmapSize(uptr size_class); + // Number of bytes in a fake frame of this size class. + static uptr BytesInSizeClass(uptr class_id) { + return 1UL << (class_id + kMinStackFrameSizeLog); + } - uptr ClassSize(uptr size_class) { - return 1UL << (size_class + kMinStackFrameSizeLog); + // The fake frame is guaranteed to have a right redzone. + // We use the last word of that redzone to store the address of the flag + // that corresponds to the current frame to make faster deallocation. + static u8 **SavedFlagPtr(uptr x, uptr class_id) { + return reinterpret_cast<u8 **>(x + BytesInSizeClass(class_id) - sizeof(x)); } - void DeallocateFrame(FakeFrame *fake_frame); + uptr stack_size_log() const { return stack_size_log_; } + + void HandleNoReturn(); + void GC(uptr real_stack); - uptr ComputeSizeClass(uptr alloc_size); - void AllocateOneSizeClass(uptr size_class); + void ForEachFakeFrame(RangeIteratorCallback callback, void *arg); - uptr stack_size_; - bool alive_; + private: + FakeStack() { } + static const uptr kFlagsOffset = 4096; // This is were the flags begin. + // Must match the number of uses of DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID + COMPILER_CHECK(kNumberOfSizeClasses == 11); + static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; - uptr allocated_size_classes_[kNumberOfSizeClasses]; - FakeFrameFifo size_classes_[kNumberOfSizeClasses]; - FakeFrameLifo<kMaxRecursionDepth> call_stack_; + uptr hint_position_[kNumberOfSizeClasses]; + uptr stack_size_log_; + // a bit is set if something was allocated from the corresponding size class. + bool needs_gc_; }; +FakeStack *GetTLSFakeStack(); +void SetTLSFakeStack(FakeStack *fs); + } // namespace __asan #endif // ASAN_FAKE_STACK_H diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h index 2f3bc9051ae1..89662f28b29c 100644 --- a/lib/asan/asan_flags.h +++ b/lib/asan/asan_flags.h @@ -32,8 +32,6 @@ struct Flags { // Lower value may reduce memory usage but increase the chance of // false negatives. int quarantine_size; - // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). - int verbosity; // Size (in bytes) of redzones around heap objects. // Requirement: redzone >= 32, is a power of two. int redzone; @@ -52,8 +50,10 @@ struct Flags { bool replace_intrin; // Used on Mac only. bool mac_ignore_invalid_free; - // ASan allocator flag. - bool use_fake_stack; + // Enables stack-use-after-return checking at run-time. + bool detect_stack_use_after_return; + // The minimal fake stack size log. + int uar_stack_size_log; // ASan allocator flag. max_malloc_fill_size is the maximal amount of bytes // that will be filled with malloc_fill_byte on malloc. int max_malloc_fill_size, malloc_fill_byte; @@ -83,6 +83,9 @@ struct Flags { bool print_legend; // If set, prints ASan exit stats even after program terminates successfully. bool atexit; + // If set, coverage information will be dumped at shutdown time if the + // appropriate instrumentation was enabled. + bool coverage; // By default, disable core dumper on 64-bit - it makes little sense // to dump 16T+ core. bool disable_core; @@ -93,23 +96,20 @@ struct Flags { // but also thread creation stacks for threads that created those threads, // etc. up to main thread. bool print_full_thread_history; - // ASan will write logs to "log_path.pid" instead of stderr. - const char *log_path; // Poison (or not) the heap memory on [de]allocation. Zero value is useful // for benchmarking the allocator or instrumentator. bool poison_heap; + // If true, poison partially addressable 8-byte aligned words (default=true). + // This flag affects heap and global buffers, but not stack buffers. + bool poison_partial; // Report errors on malloc/delete, new/free, new/delete[], etc. bool alloc_dealloc_mismatch; - // Use stack depot instead of storing stacks in the redzones. - bool use_stack_depot; // If true, assume that memcmp(p1, p2, n) always reads n bytes before // comparing p1 and p2. bool strict_memcmp; // If true, assume that dynamic initializers can never access globals from // other modules, even if the latter are already initialized. bool strict_init_order; - // Invoke LeakSanitizer at process exit. - bool detect_leaks; }; extern Flags asan_flags_dont_use_directly; diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc index 301ea44f2ca5..81699676b574 100644 --- a/lib/asan/asan_globals.cc +++ b/lib/asan/asan_globals.cc @@ -41,7 +41,7 @@ struct DynInitGlobal { Global g; bool initialized; }; -typedef InternalVector<DynInitGlobal> VectorOfGlobals; +typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals; // Lazy-initialized and never deleted. static VectorOfGlobals *dynamic_init_globals; @@ -94,15 +94,13 @@ static void RegisterGlobal(const Global *g) { CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); if (flags()->poison_heap) PoisonRedZones(*g); - ListOfGlobals *l = - (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); + ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals; l->g = g; l->next = list_of_all_globals; list_of_all_globals = l; if (g->has_dynamic_init) { if (dynamic_init_globals == 0) { - void *mem = allocator_for_globals.Allocate(sizeof(VectorOfGlobals)); - dynamic_init_globals = new(mem) + dynamic_init_globals = new(allocator_for_globals) VectorOfGlobals(kDynamicInitGlobalsInitialCapacity); } DynInitGlobal dyn_global = { *g, false }; diff --git a/lib/asan/asan_intercepted_functions.h b/lib/asan/asan_intercepted_functions.h index 842781cdb17f..de42cd6d5ca6 100644 --- a/lib/asan/asan_intercepted_functions.h +++ b/lib/asan/asan_intercepted_functions.h @@ -14,15 +14,8 @@ #ifndef ASAN_INTERCEPTED_FUNCTIONS_H #define ASAN_INTERCEPTED_FUNCTIONS_H -#include "asan_internal.h" -#include "interception/interception.h" #include "sanitizer_common/sanitizer_platform_interceptors.h" -#include <stdarg.h> -#include <stddef.h> - -using __sanitizer::uptr; - // Use macro to describe if specific function should be // intercepted on a given platform. #if !SANITIZER_WINDOWS @@ -83,32 +76,4 @@ using __sanitizer::uptr; # define ASAN_INTERCEPT___CXA_ATEXIT 0 #endif -# if SANITIZER_WINDOWS -extern "C" { -// Windows threads. -__declspec(dllimport) -void* __stdcall CreateThread(void *sec, uptr st, void* start, - void *arg, DWORD fl, DWORD *id); - -int memcmp(const void *a1, const void *a2, uptr size); -void memmove(void *to, const void *from, uptr size); -void* memset(void *block, int c, uptr size); -void* memcpy(void *to, const void *from, uptr size); -char* strcat(char *to, const char* from); // NOLINT -char* strchr(const char *str, int c); -int strcmp(const char *s1, const char* s2); -char* strcpy(char *to, const char* from); // NOLINT -uptr strlen(const char *s); -char* strncat(char *to, const char* from, uptr size); -int strncmp(const char *s1, const char* s2, uptr size); -char* strncpy(char *to, const char* from, uptr size); -uptr strnlen(const char *s, uptr maxlen); -int atoi(const char *nptr); -long atol(const char *nptr); // NOLINT -long strtol(const char *nptr, char **endptr, int base); // NOLINT -void longjmp(void *env, int value); -double frexp(double x, int *expptr); -} -# endif - #endif // ASAN_INTERCEPTED_FUNCTIONS_H diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc index 7e7deea29634..a25827b6b9ae 100644 --- a/lib/asan/asan_interceptors.cc +++ b/lib/asan/asan_interceptors.cc @@ -53,7 +53,7 @@ static inline bool QuickCheckForUnpoisonedRegion(uptr beg, uptr size) { } while (0) #define ASAN_READ_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, false) -#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true); +#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true) // Behavior of functions like "memcpy" or "strcpy" is undefined // if memory intervals overlap. We report error in this case. @@ -94,9 +94,9 @@ void SetThreadName(const char *name) { asanThreadRegistry().SetThreadName(t->tid(), name); } -static void DisableStrictInitOrderChecker() { - if (flags()->strict_init_order) - flags()->check_initialization_order = false; +int OnExit() { + // FIXME: ask frontend whether we need to return failure. + return 0; } } // namespace __asan @@ -104,26 +104,69 @@ static void DisableStrictInitOrderChecker() { // ---------------------- Wrappers ---------------- {{{1 using namespace __asan; // NOLINT +DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr) +DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) + +#if !SANITIZER_MAC +#define ASAN_INTERCEPT_FUNC(name) \ + do { \ + if ((!INTERCEPT_FUNCTION(name) || !REAL(name)) && \ + common_flags()->verbosity > 0) \ + Report("AddressSanitizer: failed to intercept '" #name "'\n"); \ + } while (0) +#else +// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION. +#define ASAN_INTERCEPT_FUNC(name) +#endif // SANITIZER_MAC + +#define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name) +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ + do { \ + } while (false) #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ ASAN_WRITE_RANGE(ptr, size) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) ASAN_READ_RANGE(ptr, size) -#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ - do { \ - if (asan_init_is_running) \ - return REAL(func)(__VA_ARGS__); \ - ctx = 0; \ - (void)ctx; \ - ENSURE_ASAN_INITED(); \ +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + do { \ + if (asan_init_is_running) return REAL(func)(__VA_ARGS__); \ + ctx = 0; \ + (void) ctx; \ + if (SANITIZER_MAC && !asan_inited) return REAL(func)(__VA_ARGS__); \ + ENSURE_ASAN_INITED(); \ + } while (false) +#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + do { \ } while (false) -#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) do { } while (false) -#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) do { } while (false) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name) +// Should be asanThreadRegistry().SetThreadNameByUserId(thread, name) +// But asan does not remember UserId's for threads (pthread_t); +// and remembers all ever existed threads, so the linear search by UserId +// can be slow. +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() #include "sanitizer_common/sanitizer_common_interceptors.inc" #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s) #define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(p, s) -#define COMMON_SYSCALL_POST_READ_RANGE(p, s) -#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) #include "sanitizer_common/sanitizer_common_syscalls.inc" static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { @@ -133,16 +176,16 @@ static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { } #if ASAN_INTERCEPT_PTHREAD_CREATE -extern "C" int pthread_attr_getdetachstate(void *attr, int *v); - INTERCEPTOR(int, pthread_create, void *thread, void *attr, void *(*start_routine)(void*), void *arg) { + EnsureMainThreadIDIsCorrect(); // Strict init-order checking in thread-hostile. - DisableStrictInitOrderChecker(); + if (flags()->strict_init_order) + StopInitOrderChecking(); GET_STACK_TRACE_THREAD; int detached = 0; if (attr != 0) - pthread_attr_getdetachstate(attr, &detached); + REAL(pthread_attr_getdetachstate)(attr, &detached); u32 current_tid = GetCurrentTidOrInvalid(); AsanThread *t = AsanThread::Create(start_routine, arg); @@ -170,7 +213,7 @@ INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, #elif SANITIZER_POSIX // We need to have defined REAL(sigaction) on posix systems. DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act, - struct sigaction *oldact); + struct sigaction *oldact) #endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION #if ASAN_INTERCEPT_SWAPCONTEXT @@ -240,13 +283,15 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) { // Since asan maps 16T of RAM, mlock is completely unfriendly to asan. // All functions return 0 (success). static void MlockIsUnsupported() { - static bool printed = 0; + static bool printed = false; if (printed) return; printed = true; - Printf("INFO: AddressSanitizer ignores mlock/mlockall/munlock/munlockall\n"); + if (common_flags()->verbosity > 0) { + Printf("INFO: AddressSanitizer ignores " + "mlock/mlockall/munlock/munlockall\n"); + } } -extern "C" { INTERCEPTOR(int, mlock, const void *addr, uptr len) { MlockIsUnsupported(); return 0; @@ -266,7 +311,6 @@ INTERCEPTOR(int, munlockall, void) { MlockIsUnsupported(); return 0; } -} // extern "C" static inline int CharCmp(unsigned char c1, unsigned char c2) { return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; @@ -300,7 +344,23 @@ INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { return REAL(memcmp(a1, a2, size)); } +#define MEMMOVE_BODY { \ + if (!asan_inited) return internal_memmove(to, from, size); \ + if (asan_init_is_running) { \ + return REAL(memmove)(to, from, size); \ + } \ + ENSURE_ASAN_INITED(); \ + if (flags()->replace_intrin) { \ + ASAN_READ_RANGE(from, size); \ + ASAN_WRITE_RANGE(to, size); \ + } \ + return internal_memmove(to, from, size); \ +} + +INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) MEMMOVE_BODY + INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { +#if !SANITIZER_MAC if (!asan_inited) return internal_memcpy(to, from, size); // memcpy is called during __asan_init() from the internals // of printf(...). @@ -317,24 +377,19 @@ INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { ASAN_READ_RANGE(from, size); ASAN_WRITE_RANGE(to, size); } - // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8. + // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8, so + // calling REAL(memcpy) here leads to infinite recursion. // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116. return internal_memcpy(to, from, size); -} - -INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) { - if (!asan_inited) return internal_memmove(to, from, size); - if (asan_init_is_running) { - return REAL(memmove)(to, from, size); - } - ENSURE_ASAN_INITED(); - if (flags()->replace_intrin) { - ASAN_READ_RANGE(from, size); - ASAN_WRITE_RANGE(to, size); - } - // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8. - // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116. - return internal_memmove(to, from, size); +#else + // At least on 10.7 and 10.8 both memcpy() and memmove() are being replaced + // with WRAP(memcpy). As a result, false positives are reported for memmove() + // calls. If we just disable error reporting with + // ASAN_OPTIONS=replace_intrin=0, memmove() is still replaced with + // internal_memcpy(), which may lead to crashes, see + // http://llvm.org/bugs/show_bug.cgi?id=16362. + MEMMOVE_BODY +#endif // !SANITIZER_MAC } INTERCEPTOR(void*, memset, void *block, int c, uptr size) { @@ -375,7 +430,7 @@ INTERCEPTOR(char*, index, const char *string, int c) DECLARE_REAL(char*, index, const char *string, int c) OVERRIDE_FUNCTION(index, strchr); # else -DEFINE_REAL(char*, index, const char *string, int c); +DEFINE_REAL(char*, index, const char *string, int c) # endif # endif #endif // ASAN_INTERCEPT_INDEX @@ -418,24 +473,6 @@ INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { return REAL(strncat)(to, from, size); } -INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { - if (!asan_inited) return internal_strcmp(s1, s2); - if (asan_init_is_running) { - return REAL(strcmp)(s1, s2); - } - ENSURE_ASAN_INITED(); - unsigned char c1, c2; - uptr i; - for (i = 0; ; i++) { - c1 = (unsigned char)s1[i]; - c2 = (unsigned char)s2[i]; - if (c1 != c2 || c1 == '\0') break; - } - ASAN_READ_RANGE(s1, i + 1); - ASAN_READ_RANGE(s2, i + 1); - return CharCmp(c1, c2); -} - INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT #if SANITIZER_MAC if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT @@ -457,21 +494,16 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT #if ASAN_INTERCEPT_STRDUP INTERCEPTOR(char*, strdup, const char *s) { -#if SANITIZER_MAC - // FIXME: because internal_strdup() uses InternalAlloc(), which currently - // just calls malloc() on Mac, we can't use internal_strdup() with the - // dynamic runtime. We can remove the call to REAL(strdup) once InternalAlloc - // starts using mmap() instead. - // See also http://code.google.com/p/address-sanitizer/issues/detail?id=123. - if (!asan_inited) return REAL(strdup)(s); -#endif if (!asan_inited) return internal_strdup(s); ENSURE_ASAN_INITED(); + uptr length = REAL(strlen)(s); if (flags()->replace_str) { - uptr length = REAL(strlen)(s); ASAN_READ_RANGE(s, length + 1); } - return REAL(strdup)(s); + GET_STACK_TRACE_MALLOC; + void *new_mem = asan_malloc(length + 1, &stack); + REAL(memcpy)(new_mem, s, length + 1); + return reinterpret_cast<char*>(new_mem); } #endif @@ -490,24 +522,13 @@ INTERCEPTOR(uptr, strlen, const char *s) { return length; } -INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { - if (!asan_inited) return internal_strncmp(s1, s2, size); - // strncmp is called from malloc_default_purgeable_zone() - // in __asan::ReplaceSystemAlloc() on Mac. - if (asan_init_is_running) { - return REAL(strncmp)(s1, s2, size); +INTERCEPTOR(uptr, wcslen, const wchar_t *s) { + uptr length = REAL(wcslen)(s); + if (!asan_init_is_running) { + ENSURE_ASAN_INITED(); + ASAN_READ_RANGE(s, (length + 1) * sizeof(wchar_t)); } - ENSURE_ASAN_INITED(); - unsigned char c1 = 0, c2 = 0; - uptr i; - for (i = 0; i < size; i++) { - c1 = (unsigned char)s1[i]; - c2 = (unsigned char)s2[i]; - if (c1 != c2 || c1 == '\0') break; - } - ASAN_READ_RANGE(s1, Min(i + 1, size)); - ASAN_READ_RANGE(s2, Min(i + 1, size)); - return CharCmp(c1, c2); + return length; } INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { @@ -644,6 +665,9 @@ static void AtCxaAtexit(void *unused) { #if ASAN_INTERCEPT___CXA_ATEXIT INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, void *dso_handle) { +#if SANITIZER_MAC + if (!asan_inited) return REAL(__cxa_atexit)(func, arg, dso_handle); +#endif ENSURE_ASAN_INITED(); int res = REAL(__cxa_atexit)(func, arg, dso_handle); REAL(__cxa_atexit)(AtCxaAtexit, 0, 0); @@ -651,26 +675,22 @@ INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, } #endif // ASAN_INTERCEPT___CXA_ATEXIT -#define ASAN_INTERCEPT_FUNC(name) do { \ - if (!INTERCEPT_FUNCTION(name) && flags()->verbosity > 0) \ - Report("AddressSanitizer: failed to intercept '" #name "'\n"); \ - } while (0) - #if SANITIZER_WINDOWS INTERCEPTOR_WINAPI(DWORD, CreateThread, void* security, uptr stack_size, DWORD (__stdcall *start_routine)(void*), void* arg, - DWORD flags, void* tid) { + DWORD thr_flags, void* tid) { // Strict init-order checking in thread-hostile. - DisableStrictInitOrderChecker(); + if (flags()->strict_init_order) + StopInitOrderChecking(); GET_STACK_TRACE_THREAD; u32 current_tid = GetCurrentTidOrInvalid(); AsanThread *t = AsanThread::Create(start_routine, arg); CreateThreadContextArgs args = { t, &stack }; - int detached = 0; // FIXME: how can we determine it on Windows? + bool detached = false; // FIXME: how can we determine it on Windows? asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args); return REAL(CreateThread)(security, stack_size, - asan_thread_start, t, flags, tid); + asan_thread_start, t, thr_flags, tid); } namespace __asan { @@ -687,9 +707,6 @@ void InitializeAsanInterceptors() { static bool was_called_once; CHECK(was_called_once == false); was_called_once = true; -#if SANITIZER_MAC - return; -#else SANITIZER_COMMON_INTERCEPTORS_INIT; // Intercept mem* functions. @@ -703,11 +720,10 @@ void InitializeAsanInterceptors() { // Intercept str* functions. ASAN_INTERCEPT_FUNC(strcat); // NOLINT ASAN_INTERCEPT_FUNC(strchr); - ASAN_INTERCEPT_FUNC(strcmp); ASAN_INTERCEPT_FUNC(strcpy); // NOLINT ASAN_INTERCEPT_FUNC(strlen); + ASAN_INTERCEPT_FUNC(wcslen); ASAN_INTERCEPT_FUNC(strncat); - ASAN_INTERCEPT_FUNC(strncmp); ASAN_INTERCEPT_FUNC(strncpy); #if ASAN_INTERCEPT_STRDUP ASAN_INTERCEPT_FUNC(strdup); @@ -771,10 +787,9 @@ void InitializeAsanInterceptors() { InitializeWindowsInterceptors(); #endif - if (flags()->verbosity > 0) { + if (common_flags()->verbosity > 0) { Report("AddressSanitizer: libc interceptors initialized\n"); } -#endif // SANITIZER_MAC } } // namespace __asan diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h index 24f76253bccd..5c1d025296fb 100644 --- a/lib/asan/asan_interface_internal.h +++ b/lib/asan/asan_interface_internal.h @@ -30,7 +30,7 @@ extern "C" { // v2=>v3: stack frame description (created by the compiler) // contains the function PC as the 3-rd field (see // DescribeAddressIfStack). - void __asan_init_v3() SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE void __asan_init_v3(); #define __asan_init __asan_init_v3 // This structure describes an instrumented global variable. @@ -46,96 +46,85 @@ extern "C" { // These two functions should be called by the instrumented code. // 'globals' is an array of structures describing 'n' globals. - void __asan_register_globals(__asan_global *globals, uptr n) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unregister_globals(__asan_global *globals, uptr n) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_register_globals(__asan_global *globals, uptr n); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unregister_globals(__asan_global *globals, uptr n); // These two functions should be called before and after dynamic initializers // of a single module run, respectively. - void __asan_before_dynamic_init(const char *module_name) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_after_dynamic_init() - SANITIZER_INTERFACE_ATTRIBUTE; - - // These two functions are used by the instrumented code in the - // use-after-return mode. __asan_stack_malloc allocates size bytes of - // fake stack and __asan_stack_free poisons it. real_stack is a pointer to - // the real stack region. - uptr __asan_stack_malloc(uptr size, uptr real_stack) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_before_dynamic_init(const char *module_name); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_after_dynamic_init(); // These two functions are used by instrumented code in the // use-after-scope mode. They mark memory for local variables as // unaddressable when they leave scope and addressable before the // function exits. - void __asan_poison_stack_memory(uptr addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unpoison_stack_memory(uptr addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_poison_stack_memory(uptr addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unpoison_stack_memory(uptr addr, uptr size); // Performs cleanup before a NoReturn function. Must be called before things // like _exit and execl to avoid false positives on stack. - void __asan_handle_no_return() SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE void __asan_handle_no_return(); - void __asan_poison_memory_region(void const volatile *addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unpoison_memory_region(void const volatile *addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_poison_memory_region(void const volatile *addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unpoison_memory_region(void const volatile *addr, uptr size); - bool __asan_address_is_poisoned(void const volatile *addr) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + bool __asan_address_is_poisoned(void const volatile *addr); - uptr __asan_region_is_poisoned(uptr beg, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + uptr __asan_region_is_poisoned(uptr beg, uptr size); - void __asan_describe_address(uptr addr) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_describe_address(uptr addr); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_report_error(uptr pc, uptr bp, uptr sp, - uptr addr, bool is_write, uptr access_size) - SANITIZER_INTERFACE_ATTRIBUTE; + uptr addr, bool is_write, uptr access_size); - int __asan_set_error_exit_code(int exit_code) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_set_death_callback(void (*callback)(void)) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_set_error_report_callback(void (*callback)(const char*)) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + int __asan_set_error_exit_code(int exit_code); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_death_callback(void (*callback)(void)); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_error_report_callback(void (*callback)(const char*)); - /* OPTIONAL */ void __asan_on_error() - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ void __asan_on_error(); + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE /* OPTIONAL */ bool __asan_symbolize(const void *pc, char *out_buffer, - int out_size) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; - - uptr __asan_get_estimated_allocated_size(uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - bool __asan_get_ownership(const void *p) - SANITIZER_INTERFACE_ATTRIBUTE; - uptr __asan_get_allocated_size(const void *p) - SANITIZER_INTERFACE_ATTRIBUTE; - uptr __asan_get_current_allocated_bytes() - SANITIZER_INTERFACE_ATTRIBUTE; - uptr __asan_get_heap_size() - SANITIZER_INTERFACE_ATTRIBUTE; - uptr __asan_get_free_bytes() - SANITIZER_INTERFACE_ATTRIBUTE; - uptr __asan_get_unmapped_bytes() - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_print_accumulated_stats() - SANITIZER_INTERFACE_ATTRIBUTE; - - /* OPTIONAL */ const char* __asan_default_options() - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; - - /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; - /* OPTIONAL */ void __asan_free_hook(void *ptr) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + int out_size); + + SANITIZER_INTERFACE_ATTRIBUTE + uptr __asan_get_estimated_allocated_size(uptr size); + + SANITIZER_INTERFACE_ATTRIBUTE bool __asan_get_ownership(const void *p); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_allocated_size(const void *p); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_current_allocated_bytes(); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_heap_size(); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_free_bytes(); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_unmapped_bytes(); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_print_accumulated_stats(); + + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ const char* __asan_default_options(); + + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ void __asan_free_hook(void *ptr); + + // Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return + SANITIZER_INTERFACE_ATTRIBUTE + extern int __asan_option_detect_stack_use_after_return; } // extern "C" #endif // ASAN_INTERFACE_INTERNAL_H diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h index 7a4d74472bcd..70e55ea0afef 100644 --- a/lib/asan/asan_internal.h +++ b/lib/asan/asan_internal.h @@ -98,6 +98,7 @@ void StopInitOrderChecking(); void AsanTSDInit(void (*destructor)(void *tsd)); void *AsanTSDGet(); void AsanTSDSet(void *tsd); +void PlatformTSDDtor(void *tsd); void AppendToErrorMessageBuffer(const char *buffer); diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc index 17bb4ca5f01c..39eec3bfd301 100644 --- a/lib/asan/asan_linux.cc +++ b/lib/asan/asan_linux.cc @@ -58,6 +58,12 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { *pc = ucontext->uc_mcontext.arm_pc; *bp = ucontext->uc_mcontext.arm_fp; *sp = ucontext->uc_mcontext.arm_sp; +# elif defined(__hppa__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.sc_iaoq[0]; + /* GCC uses %r3 whenever a frame pointer is needed. */ + *bp = ucontext->uc_mcontext.sc_gr[3]; + *sp = ucontext->uc_mcontext.sc_gr[30]; # elif defined(__x86_64__) ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.gregs[REG_RIP]; @@ -89,6 +95,11 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { stk_ptr = (uptr *) *sp; *bp = stk_ptr[15]; # endif +# elif defined(__mips__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[31]; + *bp = ucontext->uc_mcontext.gregs[30]; + *sp = ucontext->uc_mcontext.gregs[29]; #else # error "Unsupported arch" #endif diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc index 4313534008e7..e27d70adb81e 100644 --- a/lib/asan/asan_mac.cc +++ b/lib/asan/asan_mac.cc @@ -21,6 +21,7 @@ #include "asan_mapping.h" #include "asan_stack.h" #include "asan_thread.h" +#include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_libc.h" #include <crt_externs.h> // for _NSGetArgv @@ -52,7 +53,9 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { # endif // SANITIZER_WORDSIZE } -int GetMacosVersion() { +MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED; + +MacosVersion GetMacosVersionInternal() { int mib[2] = { CTL_KERN, KERN_OSRELEASE }; char version[100]; uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]); @@ -68,6 +71,7 @@ int GetMacosVersion() { case '0': return MACOS_VERSION_SNOW_LEOPARD; case '1': return MACOS_VERSION_LION; case '2': return MACOS_VERSION_MOUNTAIN_LION; + case '3': return MACOS_VERSION_MAVERICKS; default: return MACOS_VERSION_UNKNOWN; } } @@ -75,6 +79,18 @@ int GetMacosVersion() { } } +MacosVersion GetMacosVersion() { + atomic_uint32_t *cache = + reinterpret_cast<atomic_uint32_t*>(&cached_macos_version); + MacosVersion result = + static_cast<MacosVersion>(atomic_load(cache, memory_order_acquire)); + if (result == MACOS_VERSION_UNINITIALIZED) { + result = GetMacosVersionInternal(); + atomic_store(cache, result, memory_order_release); + } + return result; +} + bool PlatformHasDifferentMemcpyAndMemmove() { // On OS X 10.7 memcpy() and memmove() are both resolved // into memmove$VARIANT$sse42. @@ -158,7 +174,7 @@ void MaybeReexec() { // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name. setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); } - if (flags()->verbosity >= 1) { + if (common_flags()->verbosity >= 1) { Report("exec()-ing the program with\n"); Report("%s=%s\n", kDyldInsertLibraries, new_env); Report("to enable ASan wrappers.\n"); @@ -295,7 +311,7 @@ extern "C" void asan_dispatch_call_block_and_release(void *block) { GET_STACK_TRACE_THREAD; asan_block_context_t *context = (asan_block_context_t*)block; - if (flags()->verbosity >= 2) { + if (common_flags()->verbosity >= 2) { Report("asan_dispatch_call_block_and_release(): " "context: %p, pthread_self: %p\n", block, pthread_self()); @@ -330,7 +346,7 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, dispatch_function_t func) { \ GET_STACK_TRACE_THREAD; \ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \ - if (flags()->verbosity >= 2) { \ + if (common_flags()->verbosity >= 2) { \ Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \ asan_ctxt, pthread_self()); \ PRINT_CURRENT_STACK(); \ @@ -348,7 +364,7 @@ INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, dispatch_function_t func) { GET_STACK_TRACE_THREAD; asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (flags()->verbosity >= 2) { + if (common_flags()->verbosity >= 2) { Report("dispatch_after_f: %p\n", asan_ctxt); PRINT_CURRENT_STACK(); } @@ -361,7 +377,7 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, dispatch_function_t func) { GET_STACK_TRACE_THREAD; asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (flags()->verbosity >= 2) { + if (common_flags()->verbosity >= 2) { Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", asan_ctxt, pthread_self()); PRINT_CURRENT_STACK(); diff --git a/lib/asan/asan_mac.h b/lib/asan/asan_mac.h index b1a1966dbc6e..827b8b001699 100644 --- a/lib/asan/asan_mac.h +++ b/lib/asan/asan_mac.h @@ -12,7 +12,7 @@ // Mac-specific ASan definitions. //===----------------------------------------------------------------------===// #ifndef ASAN_MAC_H -#define ASAN__MAC_H +#define ASAN_MAC_H // CF_RC_BITS, the layout of CFRuntimeBase and __CFStrIsConstant are internal // and subject to change in further CoreFoundation versions. Apple does not @@ -36,12 +36,14 @@ typedef struct __CFRuntimeBase { #endif } CFRuntimeBase; -enum { - MACOS_VERSION_UNKNOWN = 0, +enum MacosVersion { + MACOS_VERSION_UNINITIALIZED = 0, + MACOS_VERSION_UNKNOWN, MACOS_VERSION_LEOPARD, MACOS_VERSION_SNOW_LEOPARD, MACOS_VERSION_LION, - MACOS_VERSION_MOUNTAIN_LION + MACOS_VERSION_MOUNTAIN_LION, + MACOS_VERSION_MAVERICKS }; // Used by asan_malloc_mac.cc and asan_mac.cc @@ -49,7 +51,7 @@ extern "C" void __CFInitialize(); namespace __asan { -int GetMacosVersion(); +MacosVersion GetMacosVersion(); void MaybeReplaceCFAllocator(); } // namespace __asan diff --git a/lib/asan/asan_malloc_linux.cc b/lib/asan/asan_malloc_linux.cc index 20e636b9b3c0..24b7f6977927 100644 --- a/lib/asan/asan_malloc_linux.cc +++ b/lib/asan/asan_malloc_linux.cc @@ -105,8 +105,9 @@ INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s) ALIAS("memalign"); INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { - GET_STACK_TRACE_MALLOC; - return asan_malloc_usable_size(ptr, &stack); + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(ptr, pc, bp); } // We avoid including malloc.h for portability reasons. diff --git a/lib/asan/asan_malloc_mac.cc b/lib/asan/asan_malloc_mac.cc index 4f353cb99ca7..f9f08f0201c2 100644 --- a/lib/asan/asan_malloc_mac.cc +++ b/lib/asan/asan_malloc_mac.cc @@ -19,6 +19,7 @@ #include <CoreFoundation/CFBase.h> #include <dlfcn.h> #include <malloc/malloc.h> +#include <sys/mman.h> #include "asan_allocator.h" #include "asan_interceptors.h" @@ -42,10 +43,19 @@ INTERCEPTOR(malloc_zone_t *, malloc_create_zone, vm_size_t start_size, unsigned zone_flags) { if (!asan_inited) __asan_init(); GET_STACK_TRACE_MALLOC; + uptr page_size = GetPageSizeCached(); + uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size); malloc_zone_t *new_zone = - (malloc_zone_t*)asan_malloc(sizeof(asan_zone), &stack); + (malloc_zone_t*)asan_memalign(page_size, allocated_size, + &stack, FROM_MALLOC); internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone)); new_zone->zone_name = NULL; // The name will be changed anyway. + if (GetMacosVersion() >= MACOS_VERSION_LION) { + // Prevent the client app from overwriting the zone contents. + // Library functions that need to modify the zone will set PROT_WRITE on it. + // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher. + mprotect(new_zone, allocated_size, PROT_READ); + } return new_zone; } diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc index 31fb777c7045..73e4c825092c 100644 --- a/lib/asan/asan_malloc_win.cc +++ b/lib/asan/asan_malloc_win.cc @@ -32,11 +32,13 @@ using namespace __asan; // NOLINT // revisited in the future. extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE void free(void *ptr) { GET_STACK_TRACE_FREE; return asan_free(ptr, &stack, FROM_MALLOC); } +SANITIZER_INTERFACE_ATTRIBUTE void _free_dbg(void* ptr, int) { free(ptr); } @@ -45,38 +47,46 @@ void cfree(void *ptr) { CHECK(!"cfree() should not be used on Windows?"); } +SANITIZER_INTERFACE_ATTRIBUTE void *malloc(size_t size) { GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } +SANITIZER_INTERFACE_ATTRIBUTE void* _malloc_dbg(size_t size, int , const char*, int) { return malloc(size); } +SANITIZER_INTERFACE_ATTRIBUTE void *calloc(size_t nmemb, size_t size) { GET_STACK_TRACE_MALLOC; return asan_calloc(nmemb, size, &stack); } +SANITIZER_INTERFACE_ATTRIBUTE void* _calloc_dbg(size_t n, size_t size, int, const char*, int) { return calloc(n, size); } +SANITIZER_INTERFACE_ATTRIBUTE void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { return calloc(nmemb, size); } +SANITIZER_INTERFACE_ATTRIBUTE void *realloc(void *ptr, size_t size) { GET_STACK_TRACE_MALLOC; return asan_realloc(ptr, size, &stack); } +SANITIZER_INTERFACE_ATTRIBUTE void *_realloc_dbg(void *ptr, size_t size, int) { CHECK(!"_realloc_dbg should not exist!"); return 0; } +SANITIZER_INTERFACE_ATTRIBUTE void* _recalloc(void* p, size_t n, size_t elem_size) { if (!p) return calloc(n, elem_size); @@ -86,9 +96,11 @@ void* _recalloc(void* p, size_t n, size_t elem_size) { return realloc(p, size); } +SANITIZER_INTERFACE_ATTRIBUTE size_t _msize(void *ptr) { - GET_STACK_TRACE_MALLOC; - return asan_malloc_usable_size(ptr, &stack); + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(ptr, pc, bp); } int _CrtDbgReport(int, const char*, int, diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h index f04629222419..1fecaeb35e1e 100644 --- a/lib/asan/asan_mapping.h +++ b/lib/asan/asan_mapping.h @@ -49,6 +49,20 @@ // || `[0x24000000, 0x27ffffff]` || ShadowGap || // || `[0x20000000, 0x23ffffff]` || LowShadow || // || `[0x00000000, 0x1fffffff]` || LowMem || +// +// Default Linux/MIPS mapping: +// || `[0x2aaa8000, 0xffffffff]` || HighMem || +// || `[0x0fffd000, 0x2aaa7fff]` || HighShadow || +// || `[0x0bffd000, 0x0fffcfff]` || ShadowGap || +// || `[0x0aaa8000, 0x0bffcfff]` || LowShadow || +// || `[0x00000000, 0x0aaa7fff]` || LowMem || + +static const u64 kDefaultShadowScale = 3; +static const u64 kDefaultShadowOffset32 = 1ULL << 29; +static const u64 kDefaultShadowOffset64 = 1ULL << 44; +static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G. +static const u64 kPPC64_ShadowOffset64 = 1ULL << 41; +static const u64 kMIPS32_ShadowOffset32 = 0x0aaa8000; #if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1 extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale; @@ -56,22 +70,23 @@ extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset; # define SHADOW_SCALE (__asan_mapping_scale) # define SHADOW_OFFSET (__asan_mapping_offset) #else +# define SHADOW_SCALE kDefaultShadowScale # if SANITIZER_ANDROID -# define SHADOW_SCALE (3) # define SHADOW_OFFSET (0) # else -# define SHADOW_SCALE (3) # if SANITIZER_WORDSIZE == 32 -# define SHADOW_OFFSET (1 << 29) +# if defined(__mips__) +# define SHADOW_OFFSET kMIPS32_ShadowOffset32 +# else +# define SHADOW_OFFSET kDefaultShadowOffset32 +# endif # else # if defined(__powerpc64__) -# define SHADOW_OFFSET (1ULL << 41) +# define SHADOW_OFFSET kPPC64_ShadowOffset64 +# elif SANITIZER_MAC +# define SHADOW_OFFSET kDefaultShadowOffset64 # else -# if SANITIZER_MAC -# define SHADOW_OFFSET (1ULL << 44) -# else -# define SHADOW_OFFSET 0x7fff8000ULL -# endif +# define SHADOW_OFFSET kDefaultShort64bitShadowOffset # endif # endif # endif @@ -133,7 +148,6 @@ static uptr kHighMemEnd = 0x7fffffffffffULL; static uptr kMidMemBeg = 0x3000000000ULL; static uptr kMidMemEnd = 0x4fffffffffULL; #else -SANITIZER_INTERFACE_ATTRIBUTE extern uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; // Initialized in __asan_init. #endif diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc index 772b5e64b027..280aaeb909a3 100644 --- a/lib/asan/asan_poisoning.cc +++ b/lib/asan/asan_poisoning.cc @@ -14,6 +14,7 @@ #include "asan_poisoning.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_flags.h" namespace __asan { @@ -68,7 +69,7 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) { if (!flags()->allow_user_poisoning || size == 0) return; uptr beg_addr = (uptr)addr; uptr end_addr = beg_addr + size; - if (flags()->verbosity >= 1) { + if (common_flags()->verbosity >= 1) { Printf("Trying to poison memory region [%p, %p)\n", (void*)beg_addr, (void*)end_addr); } @@ -110,7 +111,7 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) { if (!flags()->allow_user_poisoning || size == 0) return; uptr beg_addr = (uptr)addr; uptr end_addr = beg_addr + size; - if (flags()->verbosity >= 1) { + if (common_flags()->verbosity >= 1) { Printf("Trying to unpoison memory region [%p, %p)\n", (void*)beg_addr, (void*)end_addr); } @@ -183,37 +184,37 @@ uptr __asan_region_is_poisoned(uptr beg, uptr size) { extern "C" SANITIZER_INTERFACE_ATTRIBUTE -u16 __sanitizer_unaligned_load16(const u16 *p) { +u16 __sanitizer_unaligned_load16(const uu16 *p) { CHECK_SMALL_REGION(p, sizeof(*p), false); return *p; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE -u32 __sanitizer_unaligned_load32(const u32 *p) { +u32 __sanitizer_unaligned_load32(const uu32 *p) { CHECK_SMALL_REGION(p, sizeof(*p), false); return *p; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE -u64 __sanitizer_unaligned_load64(const u64 *p) { +u64 __sanitizer_unaligned_load64(const uu64 *p) { CHECK_SMALL_REGION(p, sizeof(*p), false); return *p; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_unaligned_store16(u16 *p, u16 x) { +void __sanitizer_unaligned_store16(uu16 *p, u16 x) { CHECK_SMALL_REGION(p, sizeof(*p), true); *p = x; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_unaligned_store32(u32 *p, u32 x) { +void __sanitizer_unaligned_store32(uu32 *p, u32 x) { CHECK_SMALL_REGION(p, sizeof(*p), true); *p = x; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_unaligned_store64(u64 *p, u64 x) { +void __sanitizer_unaligned_store64(uu64 *p, u64 x) { CHECK_SMALL_REGION(p, sizeof(*p), true); *p = x; } @@ -244,13 +245,55 @@ static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) { } void __asan_poison_stack_memory(uptr addr, uptr size) { - if (flags()->verbosity > 0) + if (common_flags()->verbosity > 0) Report("poisoning: %p %zx\n", (void*)addr, size); PoisonAlignedStackMemory(addr, size, true); } void __asan_unpoison_stack_memory(uptr addr, uptr size) { - if (flags()->verbosity > 0) + if (common_flags()->verbosity > 0) Report("unpoisoning: %p %zx\n", (void*)addr, size); PoisonAlignedStackMemory(addr, size, false); } + +void __sanitizer_annotate_contiguous_container(void *beg_p, void *end_p, + void *old_mid_p, + void *new_mid_p) { + uptr beg = reinterpret_cast<uptr>(beg_p); + uptr end= reinterpret_cast<uptr>(end_p); + uptr old_mid = reinterpret_cast<uptr>(old_mid_p); + uptr new_mid = reinterpret_cast<uptr>(new_mid_p); + uptr granularity = SHADOW_GRANULARITY; + CHECK(beg <= end && beg <= old_mid && beg <= new_mid && old_mid <= end && + new_mid <= end && IsAligned(beg, granularity)); + CHECK_LE(end - beg, + FIRST_32_SECOND_64(1UL << 30, 1UL << 34)); // Sanity check. + + uptr a = RoundDownTo(Min(old_mid, new_mid), granularity); + uptr c = RoundUpTo(Max(old_mid, new_mid), granularity); + uptr b = new_mid; + uptr b1 = RoundDownTo(b, granularity); + uptr b2 = RoundUpTo(b, granularity); + uptr d = old_mid; + uptr d1 = RoundDownTo(d, granularity); + uptr d2 = RoundUpTo(d, granularity); + // Currently we should be in this state: + // [a, d1) is good, [d2, c) is bad, [d1, d2) is partially good. + // Make a quick sanity check that we are indeed in this state. + if (d1 != d2) + CHECK_EQ(*(u8*)MemToShadow(d1), d - d1); + if (a + granularity <= d1) + CHECK_EQ(*(u8*)MemToShadow(a), 0); + if (d2 + granularity <= c && c <= end) + CHECK_EQ(*(u8 *)MemToShadow(c - granularity), kAsanUserPoisonedMemoryMagic); + + // New state: + // [a, b1) is good, [b2, c) is bad, [b1, b2) is partially good. + // FIXME: we may want to have a separate poison magic value. + PoisonShadow(a, b1 - a, 0); + PoisonShadow(b2, c - b2, kAsanUserPoisonedMemoryMagic); + if (b1 != b2) { + CHECK_EQ(b2 - b1, granularity); + *(u8*)MemToShadow(b1) = static_cast<u8>(b - b1); + } +} diff --git a/lib/asan/asan_poisoning.h b/lib/asan/asan_poisoning.h index 86f81e5d0ae5..fbac21196b8f 100644 --- a/lib/asan/asan_poisoning.h +++ b/lib/asan/asan_poisoning.h @@ -43,6 +43,7 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone( uptr aligned_addr, uptr size, uptr redzone_size, u8 value) { DCHECK(flags()->poison_heap); + bool poison_partial = flags()->poison_partial; u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr); for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, shadow++) { if (i + SHADOW_GRANULARITY <= size) { @@ -50,7 +51,8 @@ ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone( } else if (i >= size) { *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable } else { - *shadow = size - i; // first size-i bytes are addressable + // first size-i bytes are addressable + *shadow = poison_partial ? static_cast<u8>(size - i) : 0; } } } diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc index 5126a756d1c8..bcc6b381a785 100644 --- a/lib/asan/asan_posix.cc +++ b/lib/asan/asan_posix.cc @@ -1,4 +1,4 @@ -//===-- asan_linux.cc -----------------------------------------------------===// +//===-- asan_posix.cc -----------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -44,7 +44,7 @@ static void MaybeInstallSigaction(int signum, sigact.sa_flags = SA_SIGINFO; if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; CHECK_EQ(0, REAL(sigaction)(signum, &sigact, 0)); - if (flags()->verbosity >= 1) { + if (common_flags()->verbosity >= 1) { Report("Installed the sigaction for signal %d\n", signum); } } @@ -71,7 +71,7 @@ void SetAlternateSignalStack() { altstack.ss_flags = 0; altstack.ss_size = kAltStackSize; CHECK_EQ(0, sigaltstack(&altstack, 0)); - if (flags()->verbosity > 0) { + if (common_flags()->verbosity > 0) { Report("Alternative stack for T%d set: [%p,%p)\n", GetCurrentTidOrInvalid(), altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size); @@ -116,6 +116,15 @@ void AsanTSDSet(void *tsd) { pthread_setspecific(tsd_key, tsd); } +void PlatformTSDDtor(void *tsd) { + AsanThreadContext *context = (AsanThreadContext*)tsd; + if (context->destructor_iterations > 1) { + context->destructor_iterations--; + CHECK_EQ(0, pthread_setspecific(tsd_key, tsd)); + return; + } + AsanThread::TSDDtor(tsd); +} } // namespace __asan #endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc index aeeebf452ca8..ed4e433c7a54 100644 --- a/lib/asan/asan_report.cc +++ b/lib/asan/asan_report.cc @@ -20,6 +20,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_report_decorator.h" +#include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_symbolizer.h" namespace __asan { @@ -44,15 +45,6 @@ void AppendToErrorMessageBuffer(const char *buffer) { } // ---------------------- Decorator ------------------------------ {{{1 -bool PrintsToTtyCached() { - static int cached = 0; - static bool prints_to_tty; - if (!cached) { // Ok wrt threads since we are printing only from one thread. - prints_to_tty = PrintsToTty(); - cached = 1; - } - return prints_to_tty; -} class Decorator: private __sanitizer::AnsiColorDecorator { public: Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } @@ -113,7 +105,7 @@ static void PrintShadowBytes(const char *before, u8 *bytes, for (uptr i = 0; i < n; i++) { u8 *p = bytes + i; const char *before = p == guilty ? "[" : - p - 1 == guilty ? "" : " "; + (p - 1 == guilty && i != 0) ? "" : " "; const char *after = p == guilty ? "]" : ""; PrintShadowByte(before, *p, after); } @@ -125,7 +117,7 @@ static void PrintLegend() { "application bytes):\n", (int)SHADOW_GRANULARITY); PrintShadowByte(" Addressable: ", 0); Printf(" Partially addressable: "); - for (uptr i = 1; i < SHADOW_GRANULARITY; i++) + for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte("", i, " "); Printf("\n"); PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic); @@ -175,6 +167,11 @@ static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, } } +static void DescribeThread(AsanThread *t) { + if (t) + DescribeThread(t->context()); +} + // ---------------------- Address Descriptions ------------------- {{{1 static bool IsASCII(unsigned char c) { @@ -184,7 +181,9 @@ static bool IsASCII(unsigned char c) { static const char *MaybeDemangleGlobalName(const char *name) { // We can spoil names of globals with C linkage, so use an heuristic // approach to check if the name should be demangled. - return (name[0] == '_' && name[1] == 'Z') ? Demangle(name) : name; + return (name[0] == '_' && name[1] == 'Z') + ? Symbolizer::Get()->Demangle(name) + : name; } // Check if the global is a zero-terminated ASCII string. If so, print it. @@ -264,10 +263,53 @@ const char *ThreadNameWithParenthesis(u32 tid, char buff[], return ThreadNameWithParenthesis(t, buff, buff_len); } +void PrintAccessAndVarIntersection(const char *var_name, + uptr var_beg, uptr var_size, + uptr addr, uptr access_size, + uptr prev_var_end, uptr next_var_beg) { + uptr var_end = var_beg + var_size; + uptr addr_end = addr + access_size; + const char *pos_descr = 0; + // If the variable [var_beg, var_end) is the nearest variable to the + // current memory access, indicate it in the log. + if (addr >= var_beg) { + if (addr_end <= var_end) + pos_descr = "is inside"; // May happen if this is a use-after-return. + else if (addr < var_end) + pos_descr = "partially overflows"; + else if (addr_end <= next_var_beg && + next_var_beg - addr_end >= addr - var_end) + pos_descr = "overflows"; + } else { + if (addr_end > var_beg) + pos_descr = "partially underflows"; + else if (addr >= prev_var_end && + addr - prev_var_end >= var_beg - addr_end) + pos_descr = "underflows"; + } + Printf(" [%zd, %zd) '%s'", var_beg, var_beg + var_size, var_name); + if (pos_descr) { + Decorator d; + // FIXME: we may want to also print the size of the access here, + // but in case of accesses generated by memset it may be confusing. + Printf("%s <== Memory access at offset %zd %s this variable%s\n", + d.Location(), addr, pos_descr, d.EndLocation()); + } else { + Printf("\n"); + } +} + +struct StackVarDescr { + uptr beg; + uptr size; + const char *name_pos; + uptr name_len; +}; + bool DescribeAddressIfStack(uptr addr, uptr access_size) { AsanThread *t = FindThreadByStackAddress(addr); if (!t) return false; - const sptr kBufSize = 4095; + const uptr kBufSize = 4095; char buf[kBufSize]; uptr offset = 0; uptr frame_pc = 0; @@ -306,31 +348,44 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) { PrintStack(&alloca_stack); // Report the number of stack objects. char *p; - uptr n_objects = internal_simple_strtoll(frame_descr, &p, 10); + uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10); CHECK_GT(n_objects, 0); Printf(" This frame has %zu object(s):\n", n_objects); + // Report all objects in this frame. + InternalScopedBuffer<StackVarDescr> vars(n_objects); for (uptr i = 0; i < n_objects; i++) { uptr beg, size; - sptr len; - beg = internal_simple_strtoll(p, &p, 10); - size = internal_simple_strtoll(p, &p, 10); - len = internal_simple_strtoll(p, &p, 10); - if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') { + uptr len; + beg = (uptr)internal_simple_strtoll(p, &p, 10); + size = (uptr)internal_simple_strtoll(p, &p, 10); + len = (uptr)internal_simple_strtoll(p, &p, 10); + if (beg == 0 || size == 0 || *p != ' ') { Printf("AddressSanitizer can't parse the stack frame " "descriptor: |%s|\n", frame_descr); break; } p++; - buf[0] = 0; - internal_strncat(buf, p, Min(kBufSize, len)); + vars[i].beg = beg; + vars[i].size = size; + vars[i].name_pos = p; + vars[i].name_len = len; p += len; - Printf(" [%zu, %zu) '%s'\n", beg, beg + size, buf); + } + for (uptr i = 0; i < n_objects; i++) { + buf[0] = 0; + internal_strncat(buf, vars[i].name_pos, + static_cast<uptr>(Min(kBufSize, vars[i].name_len))); + uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0; + uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL); + PrintAccessAndVarIntersection(buf, vars[i].beg, vars[i].size, + offset, access_size, + prev_var_end, next_var_beg); } Printf("HINT: this may be a false positive if your program uses " "some custom stack unwind mechanism or swapcontext\n" " (longjmp and C++ exceptions *are* supported)\n"); - DescribeThread(t->context()); + DescribeThread(t); return true; } @@ -360,7 +415,11 @@ static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, void DescribeHeapAddress(uptr addr, uptr access_size) { AsanChunkView chunk = FindHeapChunkByAddress(addr); - if (!chunk.IsValid()) return; + if (!chunk.IsValid()) { + Printf("AddressSanitizer can not describe address in more detail " + "(wild memory access suspected).\n"); + return; + } DescribeAccessToHeapChunk(chunk, addr, access_size); CHECK(chunk.AllocTid() != kInvalidTid); asanThreadRegistry().CheckLocked(); @@ -368,13 +427,11 @@ void DescribeHeapAddress(uptr addr, uptr access_size) { GetThreadContextByTidLocked(chunk.AllocTid()); StackTrace alloc_stack; chunk.GetAllocStack(&alloc_stack); - AsanThread *t = GetCurrentThread(); - CHECK(t); char tname[128]; Decorator d; + AsanThreadContext *free_thread = 0; if (chunk.FreeTid() != kInvalidTid) { - AsanThreadContext *free_thread = - GetThreadContextByTidLocked(chunk.FreeTid()); + free_thread = GetThreadContextByTidLocked(chunk.FreeTid()); Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), free_thread->tid, ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)), @@ -386,19 +443,17 @@ void DescribeHeapAddress(uptr addr, uptr access_size) { d.Allocation(), alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), d.EndAllocation()); - PrintStack(&alloc_stack); - DescribeThread(t->context()); - DescribeThread(free_thread); - DescribeThread(alloc_thread); } else { Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(), alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), d.EndAllocation()); - PrintStack(&alloc_stack); - DescribeThread(t->context()); - DescribeThread(alloc_thread); } + PrintStack(&alloc_stack); + DescribeThread(GetCurrentThread()); + if (free_thread) + DescribeThread(free_thread); + DescribeThread(alloc_thread); } void DescribeAddress(uptr addr, uptr access_size) { @@ -431,7 +486,9 @@ void DescribeThread(AsanThreadContext *context) { context->parent_tid, ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname))); - PrintStack(&context->stack); + uptr stack_size; + const uptr *stack_trace = StackDepotGet(context->stack_id, &stack_size); + PrintStack(stack_trace, stack_size); // Recursively described parent thread if needed. if (flags()->print_full_thread_history) { AsanThreadContext *parent_context = @@ -476,21 +533,11 @@ class ScopedInErrorReport { reporting_thread_tid = GetCurrentTidOrInvalid(); Printf("====================================================" "=============\n"); - if (reporting_thread_tid != kInvalidTid) { - // We started reporting an error message. Stop using the fake stack - // in case we call an instrumented function from a symbolizer. - AsanThread *curr_thread = GetCurrentThread(); - CHECK(curr_thread); - curr_thread->fake_stack().StopUsingFakeStack(); - } } // Destructor is NORETURN, as functions that report errors are. NORETURN ~ScopedInErrorReport() { // Make sure the current thread is announced. - AsanThread *curr_thread = GetCurrentThread(); - if (curr_thread) { - DescribeThread(curr_thread->context()); - } + DescribeThread(GetCurrentThread()); // Print memory stats. if (flags()->print_stats) __asan_print_accumulated_stats(); @@ -502,22 +549,6 @@ class ScopedInErrorReport { } }; -static void ReportSummary(const char *error_type, StackTrace *stack) { - if (!stack->size) return; - if (IsSymbolizerAvailable()) { - AddressInfo ai; - // Currently, we include the first stack frame into the report summary. - // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). - uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); - SymbolizeCode(pc, &ai, 1); - ReportErrorSummary(error_type, - StripPathPrefix(ai.file, - common_flags()->strip_path_prefix), - ai.line, ai.function); - } - // FIXME: do we need to print anything at all if there is no symbolizer? -} - void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { ScopedInErrorReport in_report; Decorator d; @@ -527,13 +558,13 @@ void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { (void*)addr, (void*)pc, (void*)sp, (void*)bp, GetCurrentTidOrInvalid()); Printf("%s", d.EndWarning()); - Printf("AddressSanitizer can not provide additional info.\n"); GET_STACK_TRACE_FATAL(pc, bp); PrintStack(&stack); - ReportSummary("SEGV", &stack); + Printf("AddressSanitizer can not provide additional info.\n"); + ReportErrorSummary("SEGV", &stack); } -void ReportDoubleFree(uptr addr, StackTrace *stack) { +void ReportDoubleFree(uptr addr, StackTrace *free_stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); @@ -543,14 +574,15 @@ void ReportDoubleFree(uptr addr, StackTrace *stack) { "thread T%d%s:\n", addr, curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); - Printf("%s", d.EndWarning()); - PrintStack(stack); + CHECK_GT(free_stack->size, 0); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + PrintStack(&stack); DescribeHeapAddress(addr, 1); - ReportSummary("double-free", stack); + ReportErrorSummary("double-free", &stack); } -void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { +void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); @@ -560,12 +592,14 @@ void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { "which was not malloc()-ed: %p in thread T%d%s\n", addr, curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); Printf("%s", d.EndWarning()); - PrintStack(stack); + CHECK_GT(free_stack->size, 0); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + PrintStack(&stack); DescribeHeapAddress(addr, 1); - ReportSummary("bad-free", stack); + ReportErrorSummary("bad-free", &stack); } -void ReportAllocTypeMismatch(uptr addr, StackTrace *stack, +void ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack, AllocType alloc_type, AllocType dealloc_type) { static const char *alloc_names[] = @@ -579,9 +613,11 @@ void ReportAllocTypeMismatch(uptr addr, StackTrace *stack, Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n", alloc_names[alloc_type], dealloc_names[dealloc_type], addr); Printf("%s", d.EndWarning()); - PrintStack(stack); + CHECK_GT(free_stack->size, 0); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + PrintStack(&stack); DescribeHeapAddress(addr, 1); - ReportSummary("alloc-dealloc-mismatch", stack); + ReportErrorSummary("alloc-dealloc-mismatch", &stack); Report("HINT: if you don't care about these warnings you may set " "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); } @@ -596,7 +632,7 @@ void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) { Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); - ReportSummary("bad-malloc_usable_size", stack); + ReportErrorSummary("bad-malloc_usable_size", stack); } void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { @@ -609,7 +645,7 @@ void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); - ReportSummary("bad-__asan_get_allocated_size", stack); + ReportErrorSummary("bad-__asan_get_allocated_size", stack); } void ReportStringFunctionMemoryRangesOverlap( @@ -627,7 +663,7 @@ void ReportStringFunctionMemoryRangesOverlap( PrintStack(stack); DescribeAddress((uptr)offset1, length1); DescribeAddress((uptr)offset2, length2); - ReportSummary(bug_type, stack); + ReportErrorSummary(bug_type, stack); } // ----------------------- Mac-specific reports ----------------- {{{1 @@ -737,7 +773,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, PrintStack(&stack); DescribeAddress(addr, access_size); - ReportSummary(bug_descr, &stack); + ReportErrorSummary(bug_descr, &stack); PrintShadowMemoryForAddress(addr); } @@ -758,6 +794,6 @@ void __asan_describe_address(uptr addr) { #if !SANITIZER_SUPPORTS_WEAK_HOOKS // Provide default implementation of __asan_on_error that does nothing // and may be overriden by user. -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE void __asan_on_error() {} #endif diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h index db271fc10e97..f55b57bd4d9f 100644 --- a/lib/asan/asan_report.h +++ b/lib/asan/asan_report.h @@ -33,9 +33,9 @@ void DescribeThread(AsanThreadContext *context); // Different kinds of error reports. void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr); -void NORETURN ReportDoubleFree(uptr addr, StackTrace *stack); -void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *stack); -void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *stack, +void NORETURN ReportDoubleFree(uptr addr, StackTrace *free_stack); +void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *free_stack); +void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack, AllocType alloc_type, AllocType dealloc_type); void NORETURN ReportMallocUsableSizeNotOwned(uptr addr, diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index f989c5c0d2a5..11f05954d31e 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_interceptors.h" +#include "asan_interface_internal.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_poisoning.h" @@ -26,6 +27,8 @@ #include "sanitizer_common/sanitizer_symbolizer.h" #include "lsan/lsan_common.h" +int __asan_option_detect_stack_use_after_return; // Global interface symbol. + namespace __asan { uptr AsanMappingProfile[kAsanMappingProfileSize]; @@ -89,7 +92,6 @@ static void ParseFlagsFromString(Flags *f, const char *str) { CHECK((uptr)common_flags()->malloc_context_size <= kStackTraceMax); ParseFlag(str, &f->quarantine_size, "quarantine_size"); - ParseFlag(str, &f->verbosity, "verbosity"); ParseFlag(str, &f->redzone, "redzone"); CHECK_GE(f->redzone, 16); CHECK(IsPowerOfTwo(f->redzone)); @@ -101,7 +103,9 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->replace_str, "replace_str"); ParseFlag(str, &f->replace_intrin, "replace_intrin"); ParseFlag(str, &f->mac_ignore_invalid_free, "mac_ignore_invalid_free"); - ParseFlag(str, &f->use_fake_stack, "use_fake_stack"); + ParseFlag(str, &f->detect_stack_use_after_return, + "detect_stack_use_after_return"); + ParseFlag(str, &f->uar_stack_size_log, "uar_stack_size_log"); ParseFlag(str, &f->max_malloc_fill_size, "max_malloc_fill_size"); ParseFlag(str, &f->malloc_fill_byte, "malloc_fill_byte"); ParseFlag(str, &f->exitcode, "exitcode"); @@ -116,30 +120,25 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->print_stats, "print_stats"); ParseFlag(str, &f->print_legend, "print_legend"); ParseFlag(str, &f->atexit, "atexit"); + ParseFlag(str, &f->coverage, "coverage"); ParseFlag(str, &f->disable_core, "disable_core"); ParseFlag(str, &f->allow_reexec, "allow_reexec"); ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history"); - ParseFlag(str, &f->log_path, "log_path"); ParseFlag(str, &f->poison_heap, "poison_heap"); + ParseFlag(str, &f->poison_partial, "poison_partial"); ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch"); - ParseFlag(str, &f->use_stack_depot, "use_stack_depot"); ParseFlag(str, &f->strict_memcmp, "strict_memcmp"); ParseFlag(str, &f->strict_init_order, "strict_init_order"); - ParseFlag(str, &f->detect_leaks, "detect_leaks"); } void InitializeFlags(Flags *f, const char *env) { CommonFlags *cf = common_flags(); + SetCommonFlagDefaults(); cf->external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); - cf->symbolize = true; cf->malloc_context_size = kDefaultMallocContextSize; - cf->fast_unwind_on_fatal = false; - cf->fast_unwind_on_malloc = true; - cf->strip_path_prefix = ""; internal_memset(f, 0, sizeof(*f)); f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28; - f->verbosity = 0; f->redzone = 16; f->debug = false; f->report_globals = 1; @@ -147,7 +146,8 @@ void InitializeFlags(Flags *f, const char *env) { f->replace_str = true; f->replace_intrin = true; f->mac_ignore_invalid_free = false; - f->use_fake_stack = true; + f->detect_stack_use_after_return = false; // Also needs the compiler flag. + f->uar_stack_size_log = 0; f->max_malloc_fill_size = 0x1000; // By default, fill only the first 4K. f->malloc_fill_byte = 0xbe; f->exitcode = ASAN_DEFAULT_FAILURE_EXITCODE; @@ -162,25 +162,24 @@ void InitializeFlags(Flags *f, const char *env) { f->print_stats = false; f->print_legend = true; f->atexit = false; + f->coverage = false; f->disable_core = (SANITIZER_WORDSIZE == 64); f->allow_reexec = true; f->print_full_thread_history = true; - f->log_path = 0; f->poison_heap = true; - // Turn off alloc/dealloc mismatch checker on Mac for now. - // TODO(glider): Fix known issues and enable this back. - f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0);; - f->use_stack_depot = true; + f->poison_partial = true; + // Turn off alloc/dealloc mismatch checker on Mac and Windows for now. + // TODO(glider,timurrrr): Fix known issues and enable this back. + f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0); f->strict_memcmp = true; f->strict_init_order = false; - f->detect_leaks = false; // Override from compile definition. ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton()); // Override from user-specified string. ParseFlagsFromString(f, MaybeCallAsanDefaultOptions()); - if (flags()->verbosity) { + if (common_flags()->verbosity) { Report("Using the defaults from __asan_default_options: %s\n", MaybeCallAsanDefaultOptions()); } @@ -189,17 +188,17 @@ void InitializeFlags(Flags *f, const char *env) { ParseFlagsFromString(f, env); #if !CAN_SANITIZE_LEAKS - if (f->detect_leaks) { + if (cf->detect_leaks) { Report("%s: detect_leaks is not supported on this platform.\n", SanitizerToolName); - f->detect_leaks = false; + cf->detect_leaks = false; } #endif - if (f->detect_leaks && !f->use_stack_depot) { - Report("%s: detect_leaks is ignored (requires use_stack_depot).\n", - SanitizerToolName); - f->detect_leaks = false; + // Make "strict_init_order" imply "check_initialization_order". + // TODO(samsonov): Use a single runtime flag for an init-order checker. + if (f->strict_init_order) { + f->check_initialization_order = true; } } @@ -305,8 +304,6 @@ static NOINLINE void force_interface_symbols() { case 25: __asan_poison_memory_region(0, 0); break; case 26: __asan_unpoison_memory_region(0, 0); break; case 27: __asan_set_error_exit_code(0); break; - case 28: __asan_stack_free(0, 0, 0); break; - case 29: __asan_stack_malloc(0, 0); break; case 30: __asan_before_dynamic_init(0); break; case 31: __asan_after_dynamic_init(); break; case 32: __asan_poison_stack_memory(0, 0); break; @@ -328,22 +325,12 @@ static void asan_atexit() { static void InitializeHighMemEnd() { #if !ASAN_FIXED_MAPPING -#if SANITIZER_WORDSIZE == 64 -# if defined(__powerpc64__) - // FIXME: - // On PowerPC64 we have two different address space layouts: 44- and 46-bit. - // We somehow need to figure our which one we are using now and choose - // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. - // Note that with 'ulimit -s unlimited' the stack is moved away from the top - // of the address space, so simply checking the stack address is not enough. - kHighMemEnd = (1ULL << 44) - 1; // 0x00000fffffffffffUL -# else - kHighMemEnd = (1ULL << 47) - 1; // 0x00007fffffffffffUL; -# endif -#else // SANITIZER_WORDSIZE == 32 - kHighMemEnd = (1ULL << 32) - 1; // 0xffffffff; -#endif // SANITIZER_WORDSIZE + kHighMemEnd = GetMaxVirtualAddress(); + // Increase kHighMemEnd to make sure it's properly + // aligned together with kHighMemBeg: + kHighMemEnd |= SHADOW_GRANULARITY * GetPageSizeCached() - 1; #endif // !ASAN_FIXED_MAPPING + CHECK_EQ((kHighMemBeg % GetPageSizeCached()), 0); } static void ProtectGap(uptr a, uptr size) { @@ -385,6 +372,7 @@ static void PrintAddressSpaceLayout() { } Printf("\n"); Printf("red_zone=%zu\n", (uptr)flags()->redzone); + Printf("quarantine_size=%zuM\n", (uptr)flags()->quarantine_size >> 20); Printf("malloc_context_size=%zu\n", (uptr)common_flags()->malloc_context_size); @@ -405,7 +393,7 @@ using namespace __asan; // NOLINT #if !SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char* __asan_default_options() { return ""; } } // extern "C" #endif @@ -438,6 +426,8 @@ void NOINLINE __asan_handle_no_return() { return; } PoisonShadow(bottom, top - bottom, 0); + if (curr_thread->has_fake_stack()) + curr_thread->fake_stack()->HandleNoReturn(); } void NOINLINE __asan_set_death_callback(void (*callback)(void)) { @@ -463,9 +453,11 @@ void __asan_init() { // initialization steps look at flags(). const char *options = GetEnv("ASAN_OPTIONS"); InitializeFlags(flags(), options); - __sanitizer_set_report_path(flags()->log_path); + __sanitizer_set_report_path(common_flags()->log_path); + __asan_option_detect_stack_use_after_return = + flags()->detect_stack_use_after_return; - if (flags()->verbosity && options) { + if (common_flags()->verbosity && options) { Report("Parsed ASAN_OPTIONS: %s\n", options); } @@ -475,21 +467,16 @@ void __asan_init() { // Setup internal allocator callback. SetLowLevelAllocateCallback(OnLowLevelAllocate); - if (flags()->atexit) { - Atexit(asan_atexit); - } - - // interceptors InitializeAsanInterceptors(); ReplaceSystemMalloc(); ReplaceOperatorsNewAndDelete(); uptr shadow_start = kLowShadowBeg; - if (kLowShadowBeg) shadow_start -= GetMmapGranularity(); - uptr shadow_end = kHighShadowEnd; + if (kLowShadowBeg) + shadow_start -= GetMmapGranularity(); bool full_shadow_is_available = - MemoryRangeIsAvailable(shadow_start, shadow_end); + MemoryRangeIsAvailable(shadow_start, kHighShadowEnd); #if SANITIZER_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING if (!full_shadow_is_available) { @@ -498,7 +485,7 @@ void __asan_init() { } #endif - if (flags()->verbosity) + if (common_flags()->verbosity) PrintAddressSpaceLayout(); if (flags()->disable_core) { @@ -515,7 +502,7 @@ void __asan_init() { ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); } else if (kMidMemBeg && MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) && - MemoryRangeIsAvailable(kMidMemEnd + 1, shadow_end)) { + MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) { CHECK(kLowShadowBeg != kLowShadowEnd); // mmap the low shadow plus at least one page at the left. ReserveShadowMemoryRange(shadow_start, kLowShadowEnd); @@ -534,12 +521,18 @@ void __asan_init() { Die(); } + AsanTSDInit(PlatformTSDDtor); InstallSignalHandlers(); + + // Allocator should be initialized before starting external symbolizer, as + // fork() on Mac locks the allocator. + InitializeAllocator(); + // Start symbolizer process if necessary. - const char* external_symbolizer = common_flags()->external_symbolizer_path; - if (common_flags()->symbolize && external_symbolizer && - external_symbolizer[0]) { - InitializeExternalSymbolizer(external_symbolizer); + if (common_flags()->symbolize) { + Symbolizer::Init(common_flags()->external_symbolizer_path); + } else { + Symbolizer::Disable(); } // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited @@ -547,8 +540,16 @@ void __asan_init() { asan_inited = 1; asan_init_is_running = false; + if (flags()->atexit) + Atexit(asan_atexit); + + if (flags()->coverage) + Atexit(__sanitizer_cov_dump); + + // interceptors + InitTlsSize(); + // Create main thread. - AsanTSDInit(AsanThread::TSDDtor); AsanThread *main_thread = AsanThread::Create(0, 0); CreateThreadContextArgs create_main_args = { main_thread, 0 }; u32 main_tid = asanThreadRegistry().CreateThread( @@ -558,16 +559,14 @@ void __asan_init() { main_thread->ThreadStart(internal_getpid()); force_interface_symbols(); // no-op. - InitializeAllocator(); - #if CAN_SANITIZE_LEAKS __lsan::InitCommonLsan(); - if (flags()->detect_leaks) { + if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { Atexit(__lsan::DoLeakCheck); } #endif // CAN_SANITIZE_LEAKS - if (flags()->verbosity) { + if (common_flags()->verbosity) { Report("AddressSanitizer Init done\n"); } } diff --git a/lib/asan/asan_stack.cc b/lib/asan/asan_stack.cc index 21dae7df096a..0bc5a5f2d036 100644 --- a/lib/asan/asan_stack.cc +++ b/lib/asan/asan_stack.cc @@ -24,9 +24,12 @@ static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer, : false; } +void PrintStack(const uptr *trace, uptr size) { + StackTrace::PrintStack(trace, size, MaybeCallAsanSymbolize); +} + void PrintStack(StackTrace *stack) { - stack->PrintStack(stack->trace, stack->size, common_flags()->symbolize, - common_flags()->strip_path_prefix, MaybeCallAsanSymbolize); + PrintStack(stack->trace, stack->size); } } // namespace __asan @@ -37,7 +40,7 @@ void PrintStack(StackTrace *stack) { // and may be overriden by user if he wants to use his own symbolization. // ASan on Windows has its own implementation of this. #if !SANITIZER_WINDOWS && !SANITIZER_SUPPORTS_WEAK_HOOKS -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) { return false; } diff --git a/lib/asan/asan_stack.h b/lib/asan/asan_stack.h index 176aa183c93b..7f5fd661eec8 100644 --- a/lib/asan/asan_stack.h +++ b/lib/asan/asan_stack.h @@ -22,6 +22,7 @@ namespace __asan { void PrintStack(StackTrace *stack); +void PrintStack(const uptr *trace, uptr size); } // namespace __asan @@ -29,21 +30,21 @@ void PrintStack(StackTrace *stack); // The pc will be in the position 0 of the resulting stack trace. // The bp may refer to the current frame or to the caller's frame. #if SANITIZER_WINDOWS -#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ - StackTrace stack; \ - GetStackTrace(&stack, max_s, pc, bp, 0, 0, fast) +#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ + StackTrace stack; \ + stack.Unwind(max_s, pc, bp, 0, 0, fast) #else -#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ - StackTrace stack; \ - { \ - uptr stack_top = 0, stack_bottom = 0; \ - AsanThread *t; \ - if (asan_inited && (t = GetCurrentThread())) { \ - stack_top = t->stack_top(); \ - stack_bottom = t->stack_bottom(); \ - } \ - GetStackTrace(&stack, max_s, pc, bp, \ - stack_top, stack_bottom, fast); \ +#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ + StackTrace stack; \ + { \ + AsanThread *t; \ + stack.size = 0; \ + if (asan_inited && (t = GetCurrentThread()) && !t->isUnwinding()) { \ + uptr stack_top = t->stack_top(); \ + uptr stack_bottom = t->stack_bottom(); \ + ScopedUnwinding unwind_scope(t); \ + stack.Unwind(max_s, pc, bp, stack_top, stack_bottom, fast); \ + } \ } #endif // SANITIZER_WINDOWS diff --git a/lib/asan/asan_stats.cc b/lib/asan/asan_stats.cc index ba7c1ab6e91a..73dc3c5ac44f 100644 --- a/lib/asan/asan_stats.cc +++ b/lib/asan/asan_stats.cc @@ -21,6 +21,10 @@ namespace __asan { AsanStats::AsanStats() { + Clear(); +} + +void AsanStats::Clear() { CHECK(REAL(memset)); REAL(memset)(this, 0, sizeof(AsanStats)); } @@ -54,77 +58,63 @@ void AsanStats::Print() { malloc_large, malloc_small_slow); } -static BlockingMutex print_lock(LINKER_INITIALIZED); - -static void PrintAccumulatedStats() { - AsanStats stats; - GetAccumulatedStats(&stats); - // Use lock to keep reports from mixing up. - BlockingMutexLock lock(&print_lock); - stats.Print(); - StackDepotStats *stack_depot_stats = StackDepotGetStats(); - Printf("Stats: StackDepot: %zd ids; %zdM mapped\n", - stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20); - PrintInternalAllocatorStats(); +void AsanStats::MergeFrom(const AsanStats *stats) { + uptr *dst_ptr = reinterpret_cast<uptr*>(this); + const uptr *src_ptr = reinterpret_cast<const uptr*>(stats); + uptr num_fields = sizeof(*this) / sizeof(uptr); + for (uptr i = 0; i < num_fields; i++) + dst_ptr[i] += src_ptr[i]; } +static BlockingMutex print_lock(LINKER_INITIALIZED); + static AsanStats unknown_thread_stats(LINKER_INITIALIZED); -static AsanStats accumulated_stats(LINKER_INITIALIZED); +static AsanStats dead_threads_stats(LINKER_INITIALIZED); +static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED); // Required for malloc_zone_statistics() on OS X. This can't be stored in // per-thread AsanStats. static uptr max_malloced_memory; -static BlockingMutex acc_stats_lock(LINKER_INITIALIZED); - -static void FlushToAccumulatedStatsUnlocked(AsanStats *stats) { - acc_stats_lock.CheckLocked(); - uptr *dst = (uptr*)&accumulated_stats; - uptr *src = (uptr*)stats; - uptr num_fields = sizeof(*stats) / sizeof(uptr); - for (uptr i = 0; i < num_fields; i++) { - dst[i] += src[i]; - src[i] = 0; - } -} -static void FlushThreadStats(ThreadContextBase *tctx_base, void *arg) { +static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) { + AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg); AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base); if (AsanThread *t = tctx->thread) - FlushToAccumulatedStatsUnlocked(&t->stats()); + accumulated_stats->MergeFrom(&t->stats()); } -static void UpdateAccumulatedStatsUnlocked() { - acc_stats_lock.CheckLocked(); +static void GetAccumulatedStats(AsanStats *stats) { + stats->Clear(); { ThreadRegistryLock l(&asanThreadRegistry()); - asanThreadRegistry().RunCallbackForEachThreadLocked(FlushThreadStats, 0); + asanThreadRegistry() + .RunCallbackForEachThreadLocked(MergeThreadStats, stats); + } + stats->MergeFrom(&unknown_thread_stats); + { + BlockingMutexLock lock(&dead_threads_stats_lock); + stats->MergeFrom(&dead_threads_stats); } - FlushToAccumulatedStatsUnlocked(&unknown_thread_stats); // This is not very accurate: we may miss allocation peaks that happen // between two updates of accumulated_stats_. For more accurate bookkeeping // the maximum should be updated on every malloc(), which is unacceptable. - if (max_malloced_memory < accumulated_stats.malloced) { - max_malloced_memory = accumulated_stats.malloced; + if (max_malloced_memory < stats->malloced) { + max_malloced_memory = stats->malloced; } } -void FlushToAccumulatedStats(AsanStats *stats) { - BlockingMutexLock lock(&acc_stats_lock); - FlushToAccumulatedStatsUnlocked(stats); -} - -void GetAccumulatedStats(AsanStats *stats) { - BlockingMutexLock lock(&acc_stats_lock); - UpdateAccumulatedStatsUnlocked(); - internal_memcpy(stats, &accumulated_stats, sizeof(accumulated_stats)); +void FlushToDeadThreadStats(AsanStats *stats) { + BlockingMutexLock lock(&dead_threads_stats_lock); + dead_threads_stats.MergeFrom(stats); + stats->Clear(); } void FillMallocStatistics(AsanMallocStats *malloc_stats) { - BlockingMutexLock lock(&acc_stats_lock); - UpdateAccumulatedStatsUnlocked(); - malloc_stats->blocks_in_use = accumulated_stats.mallocs; - malloc_stats->size_in_use = accumulated_stats.malloced; + AsanStats stats; + GetAccumulatedStats(&stats); + malloc_stats->blocks_in_use = stats.mallocs; + malloc_stats->size_in_use = stats.malloced; malloc_stats->max_size_in_use = max_malloced_memory; - malloc_stats->size_allocated = accumulated_stats.mmaped; + malloc_stats->size_allocated = stats.mmaped; } AsanStats &GetCurrentThreadStats() { @@ -132,36 +122,48 @@ AsanStats &GetCurrentThreadStats() { return (t) ? t->stats() : unknown_thread_stats; } +static void PrintAccumulatedStats() { + AsanStats stats; + GetAccumulatedStats(&stats); + // Use lock to keep reports from mixing up. + BlockingMutexLock lock(&print_lock); + stats.Print(); + StackDepotStats *stack_depot_stats = StackDepotGetStats(); + Printf("Stats: StackDepot: %zd ids; %zdM mapped\n", + stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20); + PrintInternalAllocatorStats(); +} + } // namespace __asan // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT uptr __asan_get_current_allocated_bytes() { - BlockingMutexLock lock(&acc_stats_lock); - UpdateAccumulatedStatsUnlocked(); - uptr malloced = accumulated_stats.malloced; - uptr freed = accumulated_stats.freed; + AsanStats stats; + GetAccumulatedStats(&stats); + uptr malloced = stats.malloced; + uptr freed = stats.freed; // Return sane value if malloced < freed due to racy // way we update accumulated stats. return (malloced > freed) ? malloced - freed : 1; } uptr __asan_get_heap_size() { - BlockingMutexLock lock(&acc_stats_lock); - UpdateAccumulatedStatsUnlocked(); - return accumulated_stats.mmaped - accumulated_stats.munmaped; + AsanStats stats; + GetAccumulatedStats(&stats); + return stats.mmaped - stats.munmaped; } uptr __asan_get_free_bytes() { - BlockingMutexLock lock(&acc_stats_lock); - UpdateAccumulatedStatsUnlocked(); - uptr total_free = accumulated_stats.mmaped - - accumulated_stats.munmaped - + accumulated_stats.really_freed - + accumulated_stats.really_freed_redzones; - uptr total_used = accumulated_stats.malloced - + accumulated_stats.malloced_redzones; + AsanStats stats; + GetAccumulatedStats(&stats); + uptr total_free = stats.mmaped + - stats.munmaped + + stats.really_freed + + stats.really_freed_redzones; + uptr total_used = stats.malloced + + stats.malloced_redzones; // Return sane value if total_free < total_used due to racy // way we update accumulated stats. return (total_free > total_used) ? total_free - total_used : 1; diff --git a/lib/asan/asan_stats.h b/lib/asan/asan_stats.h index 68495fb33f95..e3030e88fdc2 100644 --- a/lib/asan/asan_stats.h +++ b/lib/asan/asan_stats.h @@ -52,18 +52,16 @@ struct AsanStats { // Default ctor for thread-local stats. AsanStats(); - // Prints formatted stats to stderr. - void Print(); + void Print(); // Prints formatted stats to stderr. + void Clear(); + void MergeFrom(const AsanStats *stats); }; // Returns stats for GetCurrentThread(), or stats for fake "unknown thread" // if GetCurrentThread() returns 0. AsanStats &GetCurrentThreadStats(); -// Flushes all thread-local stats to accumulated stats, and makes -// a copy of accumulated stats. -void GetAccumulatedStats(AsanStats *stats); -// Flushes a given stats into accumulated stats. -void FlushToAccumulatedStats(AsanStats *stats); +// Flushes a given stats into accumulated stats of dead threads. +void FlushToDeadThreadStats(AsanStats *stats); // A cross-platform equivalent of malloc_statistics_t on Mac OS. struct AsanMallocStats { diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc index da28381031a5..328ac2fcd398 100644 --- a/lib/asan/asan_thread.cc +++ b/lib/asan/asan_thread.cc @@ -19,6 +19,7 @@ #include "asan_mapping.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stackdepot.h" #include "lsan/lsan_common.h" namespace __asan { @@ -27,9 +28,8 @@ namespace __asan { void AsanThreadContext::OnCreated(void *arg) { CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg); - if (args->stack) { - internal_memcpy(&stack, args->stack, sizeof(stack)); - } + if (args->stack) + stack_id = StackDepotPut(args->stack->trace, args->stack->size); thread = args->thread; thread->set_context(this); } @@ -39,12 +39,16 @@ void AsanThreadContext::OnFinished() { thread = 0; } -static char thread_registry_placeholder[sizeof(ThreadRegistry)]; +// MIPS requires aligned address +static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)]; static ThreadRegistry *asan_thread_registry; +static BlockingMutex mu_for_thread_context(LINKER_INITIALIZED); +static LowLevelAllocator allocator_for_thread_context; + static ThreadContextBase *GetAsanThreadContext(u32 tid) { - void *mem = MmapOrDie(sizeof(AsanThreadContext), "AsanThreadContext"); - return new(mem) AsanThreadContext(tid); + BlockingMutexLock lock(&mu_for_thread_context); + return new(allocator_for_thread_context) AsanThreadContext(tid); } ThreadRegistry &asanThreadRegistry() { @@ -84,40 +88,68 @@ AsanThread *AsanThread::Create(thread_callback_t start_routine, void AsanThread::TSDDtor(void *tsd) { AsanThreadContext *context = (AsanThreadContext*)tsd; - if (flags()->verbosity >= 1) + if (common_flags()->verbosity >= 1) Report("T%d TSDDtor\n", context->tid); if (context->thread) context->thread->Destroy(); } void AsanThread::Destroy() { - if (flags()->verbosity >= 1) { + if (common_flags()->verbosity >= 1) { Report("T%d exited\n", tid()); } + malloc_storage().CommitBack(); + if (flags()->use_sigaltstack) UnsetAlternateSignalStack(); asanThreadRegistry().FinishThread(tid()); - FlushToAccumulatedStats(&stats_); + FlushToDeadThreadStats(&stats_); // We also clear the shadow on thread destruction because // some code may still be executing in later TSD destructors // and we don't want it to have any poisoned stack. - ClearShadowForThreadStack(); - fake_stack().Cleanup(); + ClearShadowForThreadStackAndTLS(); + DeleteFakeStack(); uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached()); UnmapOrDie(this, size); } +// We want to create the FakeStack lazyly on the first use, but not eralier +// than the stack size is known and the procedure has to be async-signal safe. +FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() { + uptr stack_size = this->stack_size(); + if (stack_size == 0) // stack_size is not yet available, don't use FakeStack. + return 0; + uptr old_val = 0; + // fake_stack_ has 3 states: + // 0 -- not initialized + // 1 -- being initialized + // ptr -- initialized + // This CAS checks if the state was 0 and if so changes it to state 1, + // if that was successfull, it initilizes the pointer. + if (atomic_compare_exchange_strong( + reinterpret_cast<atomic_uintptr_t *>(&fake_stack_), &old_val, 1UL, + memory_order_relaxed)) { + uptr stack_size_log = Log2(RoundUpToPowerOfTwo(stack_size)); + if (flags()->uar_stack_size_log) + stack_size_log = static_cast<uptr>(flags()->uar_stack_size_log); + fake_stack_ = FakeStack::Create(stack_size_log); + SetTLSFakeStack(fake_stack_); + return fake_stack_; + } + return 0; +} + void AsanThread::Init() { - SetThreadStackTopAndBottom(); + SetThreadStackAndTls(); CHECK(AddrIsInMem(stack_bottom_)); CHECK(AddrIsInMem(stack_top_ - 1)); - ClearShadowForThreadStack(); - if (flags()->verbosity >= 1) { + ClearShadowForThreadStackAndTLS(); + if (common_flags()->verbosity >= 1) { int local = 0; Report("T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(), (void*)stack_bottom_, (void*)stack_top_, stack_top_ - stack_bottom_, &local); } - fake_stack_.Init(stack_size()); + fake_stack_ = 0; // Will be initialized lazily if needed. AsanPlatformThreadInit(); } @@ -135,22 +167,33 @@ thread_return_t AsanThread::ThreadStart(uptr os_id) { } thread_return_t res = start_routine_(arg_); - malloc_storage().CommitBack(); - if (flags()->use_sigaltstack) UnsetAlternateSignalStack(); - this->Destroy(); + // On POSIX systems we defer this to the TSD destructor. LSan will consider + // the thread's memory as non-live from the moment we call Destroy(), even + // though that memory might contain pointers to heap objects which will be + // cleaned up by a user-defined TSD destructor. Thus, calling Destroy() before + // the TSD destructors have run might cause false positives in LSan. + if (!SANITIZER_POSIX) + this->Destroy(); return res; } -void AsanThread::SetThreadStackTopAndBottom() { - GetThreadStackTopAndBottom(tid() == 0, &stack_top_, &stack_bottom_); +void AsanThread::SetThreadStackAndTls() { + uptr tls_size = 0; + GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size_, &tls_begin_, + &tls_size); + stack_top_ = stack_bottom_ + stack_size_; + tls_end_ = tls_begin_ + tls_size; + int local; CHECK(AddrIsInStack((uptr)&local)); } -void AsanThread::ClearShadowForThreadStack() { +void AsanThread::ClearShadowForThreadStackAndTLS() { PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); + if (tls_begin_ != tls_end_) + PoisonShadow(tls_begin_, tls_end_ - tls_begin_, 0); } const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset, @@ -158,8 +201,8 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset, uptr bottom = 0; if (AddrIsInStack(addr)) { bottom = stack_bottom(); - } else { - bottom = fake_stack().AddrIsInFakeStack(addr); + } else if (has_fake_stack()) { + bottom = fake_stack()->AddrIsInFakeStack(addr); CHECK(bottom); *offset = addr - bottom; *frame_pc = ((uptr*)bottom)[2]; @@ -195,13 +238,16 @@ static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base, void *addr) { AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base); AsanThread *t = tctx->thread; - return (t && t->fake_stack().StackSize() && - (t->fake_stack().AddrIsInFakeStack((uptr)addr) || - t->AddrIsInStack((uptr)addr))); + if (!t) return false; + if (t->AddrIsInStack((uptr)addr)) return true; + if (t->has_fake_stack() && t->fake_stack()->AddrIsInFakeStack((uptr)addr)) + return true; + return false; } AsanThread *GetCurrentThread() { - AsanThreadContext *context = (AsanThreadContext*)AsanTSDGet(); + AsanThreadContext *context = + reinterpret_cast<AsanThreadContext *>(AsanTSDGet()); if (!context) { if (SANITIZER_ANDROID) { // On Android, libc constructor is called _after_ asan_init, and cleans up @@ -222,7 +268,7 @@ AsanThread *GetCurrentThread() { void SetCurrentThread(AsanThread *t) { CHECK(t->context()); - if (flags()->verbosity >= 2) { + if (common_flags()->verbosity >= 2) { Report("SetCurrentThread: %p for thread %p\n", t->context(), (void*)GetThreadSelf()); } @@ -244,6 +290,20 @@ AsanThread *FindThreadByStackAddress(uptr addr) { (void *)addr)); return tctx ? tctx->thread : 0; } + +void EnsureMainThreadIDIsCorrect() { + AsanThreadContext *context = + reinterpret_cast<AsanThreadContext *>(AsanTSDGet()); + if (context && (context->tid == 0)) + context->os_id = GetTid(); +} + +__asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) { + __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>( + __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id)); + if (!context) return 0; + return context->thread; +} } // namespace __asan // --- Implementation of LSan-specific functions --- {{{1 @@ -251,8 +311,23 @@ namespace __lsan { bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, uptr *tls_begin, uptr *tls_end, uptr *cache_begin, uptr *cache_end) { - // FIXME: Stub. - return false; + __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); + if (!t) return false; + *stack_begin = t->stack_bottom(); + *stack_end = t->stack_top(); + *tls_begin = t->tls_begin(); + *tls_end = t->tls_end(); + // ASan doesn't keep allocator caches in TLS, so these are unused. + *cache_begin = 0; + *cache_end = 0; + return true; +} + +void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, + void *arg) { + __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); + if (t && t->has_fake_stack()) + t->fake_stack()->ForEachFakeFrame(callback, arg); } void LockThreadRegistry() { @@ -262,4 +337,8 @@ void LockThreadRegistry() { void UnlockThreadRegistry() { __asan::asanThreadRegistry().Unlock(); } + +void EnsureMainThreadIDIsCorrect() { + __asan::EnsureMainThreadIDIsCorrect(); +} } // namespace __lsan diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h index 14062b62f751..11771ecd09f0 100644 --- a/lib/asan/asan_thread.h +++ b/lib/asan/asan_thread.h @@ -19,6 +19,7 @@ #include "asan_fake_stack.h" #include "asan_stack.h" #include "asan_stats.h" +#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_thread_registry.h" @@ -36,11 +37,13 @@ class AsanThreadContext : public ThreadContextBase { explicit AsanThreadContext(int tid) : ThreadContextBase(tid), announced(false), + destructor_iterations(kPthreadDestructorIterations), + stack_id(0), thread(0) { - internal_memset(&stack, 0, sizeof(stack)); } bool announced; - StackTrace stack; + u8 destructor_iterations; + u32 stack_id; AsanThread *thread; void OnCreated(void *arg); @@ -48,7 +51,7 @@ class AsanThreadContext : public ThreadContextBase { }; // AsanThreadContext objects are never freed, so we need many of them. -COMPILER_CHECK(sizeof(AsanThreadContext) <= 4096); +COMPILER_CHECK(sizeof(AsanThreadContext) <= 256); // AsanThread are stored in TSD and destroyed when the thread dies. class AsanThread { @@ -62,7 +65,9 @@ class AsanThread { uptr stack_top() { return stack_top_; } uptr stack_bottom() { return stack_bottom_; } - uptr stack_size() { return stack_top_ - stack_bottom_; } + uptr stack_size() { return stack_size_; } + uptr tls_begin() { return tls_begin_; } + uptr tls_end() { return tls_end_; } u32 tid() { return context_->tid; } AsanThreadContext *context() { return context_; } void set_context(AsanThreadContext *context) { context_ = context; } @@ -73,23 +78,68 @@ class AsanThread { return addr >= stack_bottom_ && addr < stack_top_; } - FakeStack &fake_stack() { return fake_stack_; } + void DeleteFakeStack() { + if (!fake_stack_) return; + FakeStack *t = fake_stack_; + fake_stack_ = 0; + SetTLSFakeStack(0); + t->Destroy(); + } + + bool has_fake_stack() { + return (reinterpret_cast<uptr>(fake_stack_) > 1); + } + + FakeStack *fake_stack() { + if (!__asan_option_detect_stack_use_after_return) + return 0; + if (!has_fake_stack()) + return AsyncSignalSafeLazyInitFakeStack(); + return fake_stack_; + } + + // True is this thread is currently unwinding stack (i.e. collecting a stack + // trace). Used to prevent deadlocks on platforms where libc unwinder calls + // malloc internally. See PR17116 for more details. + bool isUnwinding() const { return unwinding; } + void setUnwinding(bool b) { unwinding = b; } + AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; } AsanStats &stats() { return stats_; } private: - AsanThread() {} - void SetThreadStackTopAndBottom(); - void ClearShadowForThreadStack(); + AsanThread() : unwinding(false) {} + void SetThreadStackAndTls(); + void ClearShadowForThreadStackAndTLS(); + FakeStack *AsyncSignalSafeLazyInitFakeStack(); + AsanThreadContext *context_; thread_callback_t start_routine_; void *arg_; uptr stack_top_; uptr stack_bottom_; + // stack_size_ == stack_top_ - stack_bottom_; + // It needs to be set in a async-signal-safe manner. + uptr stack_size_; + uptr tls_begin_; + uptr tls_end_; - FakeStack fake_stack_; + FakeStack *fake_stack_; AsanThreadLocalMallocStorage malloc_storage_; AsanStats stats_; + bool unwinding; +}; + +// ScopedUnwinding is a scope for stacktracing member of a context +class ScopedUnwinding { + public: + explicit ScopedUnwinding(AsanThread *t) : thread(t) { + t->setUnwinding(true); + } + ~ScopedUnwinding() { thread->setUnwinding(false); } + + private: + AsanThread *thread; }; struct CreateThreadContextArgs { @@ -109,6 +159,8 @@ void SetCurrentThread(AsanThread *t); u32 GetCurrentTidOrInvalid(); AsanThread *FindThreadByStackAddress(uptr addr); +// Used to handle fork(). +void EnsureMainThreadIDIsCorrect(); } // namespace __asan #endif // ASAN_THREAD_H diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc index f74de7227ed2..9e66b3417a1e 100644 --- a/lib/asan/asan_win.cc +++ b/lib/asan/asan_win.cc @@ -25,6 +25,14 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mutex.h" +extern "C" { + SANITIZER_INTERFACE_ATTRIBUTE + int __asan_should_detect_stack_use_after_return() { + __asan_init(); + return __asan_option_detect_stack_use_after_return; + } +} + namespace __asan { // ---------------------- Stacktraces, symbols, etc. ---------------- {{{1 @@ -52,6 +60,9 @@ void AsanTSDSet(void *tsd) { fake_tsd = tsd; } +void PlatformTSDDtor(void *tsd) { + AsanThread::TSDDtor(tsd); +} // ---------------------- Various stuff ---------------- {{{1 void MaybeReexec() { // No need to re-exec on Windows. diff --git a/lib/asan/lit_tests/32bitConfig/lit.site.cfg.in b/lib/asan/lit_tests/32bitConfig/lit.site.cfg.in new file mode 100644 index 000000000000..faef4e8c9b17 --- /dev/null +++ b/lib/asan/lit_tests/32bitConfig/lit.site.cfg.in @@ -0,0 +1,13 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Tool-specific config options. +config.asan_source_dir = "@ASAN_SOURCE_DIR@" +config.bits = "32" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/lit.cfg") + diff --git a/lib/asan/lit_tests/64bitConfig/lit.site.cfg.in b/lib/asan/lit_tests/64bitConfig/lit.site.cfg.in new file mode 100644 index 000000000000..a35994484d5f --- /dev/null +++ b/lib/asan/lit_tests/64bitConfig/lit.site.cfg.in @@ -0,0 +1,12 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Tool-specific config options. +config.asan_source_dir = "@ASAN_SOURCE_DIR@" +config.bits = "64" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/lit.cfg") diff --git a/lib/asan/lit_tests/CMakeLists.txt b/lib/asan/lit_tests/CMakeLists.txt index d2420b50da83..72a3f5439b70 100644 --- a/lib/asan/lit_tests/CMakeLists.txt +++ b/lib/asan/lit_tests/CMakeLists.txt @@ -2,8 +2,13 @@ set(ASAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) set(ASAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..) configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ${CMAKE_CURRENT_SOURCE_DIR}/64bitConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig/lit.site.cfg + ) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/32bitConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg ) configure_lit_site_cfg( @@ -12,21 +17,26 @@ configure_lit_site_cfg( ) if(COMPILER_RT_CAN_EXECUTE_TESTS) + set(ASAN_TESTSUITES) + if(CAN_TARGET_i386) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig) + endif() + if(CAN_TARGET_x86_64 OR CAN_TARGET_powerpc64) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig) + endif() # Run ASan tests only if we're sure we may produce working binaries. set(ASAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS} - ${ASAN_RUNTIME_LIBRARIES} - asan_blacklist) + asan_runtime_libraries) set(ASAN_TEST_PARAMS - asan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg - ) + asan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) if(LLVM_INCLUDE_TESTS) list(APPEND ASAN_TEST_DEPS AsanUnitTests) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit) endif() add_lit_testsuite(check-asan "Running the AddressSanitizer tests" - ${CMAKE_CURRENT_BINARY_DIR} + ${ASAN_TESTSUITES} PARAMS ${ASAN_TEST_PARAMS} - DEPENDS ${ASAN_TEST_DEPS} - ) + DEPENDS ${ASAN_TEST_DEPS}) set_target_properties(check-asan PROPERTIES FOLDER "ASan tests") endif() diff --git a/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc b/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc deleted file mode 100644 index cf89949cf942..000000000000 --- a/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc +++ /dev/null @@ -1,20 +0,0 @@ -// Make sure ASan removes the runtime library from DYLD_INSERT_LIBRARIES before -// executing other programs. - -// RUN: %clangxx_asan -m64 %s -o %t -// RUN: %clangxx -m64 %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \ -// RUN: -dynamiclib -o darwin-dummy-shared-lib-so.dylib - -// Make sure DYLD_INSERT_LIBRARIES doesn't contain the runtime library before -// execl(). - -// RUN: %t >/dev/null 2>&1 -// RUN: DYLD_INSERT_LIBRARIES=darwin-dummy-shared-lib-so.dylib \ -// RUN: %t 2>&1 | FileCheck %s || exit 1 -#include <unistd.h> -int main() { - execl("/bin/bash", "/bin/bash", "-c", - "echo DYLD_INSERT_LIBRARIES=$DYLD_INSERT_LIBRARIES", NULL); - // CHECK: {{DYLD_INSERT_LIBRARIES=.*darwin-dummy-shared-lib-so.dylib.*}} - return 0; -} diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist.txt b/lib/asan/lit_tests/Helpers/initialization-blacklist.txt deleted file mode 100644 index fa4a83667f4b..000000000000 --- a/lib/asan/lit_tests/Helpers/initialization-blacklist.txt +++ /dev/null @@ -1,3 +0,0 @@ -global-init:*badGlobal* -global-init-type:*badNamespace::BadClass* -global-init-src:*initialization-blacklist-extra2.cc diff --git a/lib/asan/lit_tests/Linux/interception_failure_test.cc b/lib/asan/lit_tests/Linux/interception_failure_test.cc deleted file mode 100644 index dfad909f528c..000000000000 --- a/lib/asan/lit_tests/Linux/interception_failure_test.cc +++ /dev/null @@ -1,26 +0,0 @@ -// If user provides his own libc functions, ASan doesn't -// intercept these functions. - -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s -#include <stdlib.h> -#include <stdio.h> - -extern "C" long strtol(const char *nptr, char **endptr, int base) { - fprintf(stderr, "my_strtol_interceptor\n"); - return 0; -} - -int main() { - char *x = (char*)malloc(10 * sizeof(char)); - free(x); - return (int)strtol(x, 0, 10); - // CHECK: my_strtol_interceptor - // CHECK-NOT: heap-use-after-free -} diff --git a/lib/asan/lit_tests/Linux/interception_malloc_test.cc b/lib/asan/lit_tests/Linux/interception_malloc_test.cc deleted file mode 100644 index 8f66788e9a82..000000000000 --- a/lib/asan/lit_tests/Linux/interception_malloc_test.cc +++ /dev/null @@ -1,27 +0,0 @@ -// ASan interceptor can be accessed with __interceptor_ prefix. - -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> - -extern "C" void *__interceptor_malloc(size_t size); -extern "C" void *malloc(size_t size) { - write(2, "malloc call\n", sizeof("malloc call\n") - 1); - return __interceptor_malloc(size); -} - -int main() { - char *x = (char*)malloc(10 * sizeof(char)); - free(x); - return (int)strtol(x, 0, 10); - // CHECK: malloc call - // CHECK: heap-use-after-free -} diff --git a/lib/asan/lit_tests/Linux/interception_test.cc b/lib/asan/lit_tests/Linux/interception_test.cc deleted file mode 100644 index 94fb499f2f87..000000000000 --- a/lib/asan/lit_tests/Linux/interception_test.cc +++ /dev/null @@ -1,26 +0,0 @@ -// ASan interceptor can be accessed with __interceptor_ prefix. - -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s -#include <stdlib.h> -#include <stdio.h> - -extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base); -extern "C" long strtol(const char *nptr, char **endptr, int base) { - fprintf(stderr, "my_strtol_interceptor\n"); - return __interceptor_strtol(nptr, endptr, base); -} - -int main() { - char *x = (char*)malloc(10 * sizeof(char)); - free(x); - return (int)strtol(x, 0, 10); - // CHECK: my_strtol_interceptor - // CHECK: heap-use-after-free -} diff --git a/lib/asan/lit_tests/Linux/zero-base-shadow.cc b/lib/asan/lit_tests/Linux/zero-base-shadow.cc deleted file mode 100644 index 682e7e8d7e59..000000000000 --- a/lib/asan/lit_tests/Linux/zero-base-shadow.cc +++ /dev/null @@ -1,31 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-64 < %t.out -// RUN: %clangxx_asan -m64 -O1 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-64 < %t.out -// RUN: %clangxx_asan -m64 -O2 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-64 < %t.out -// RUN: %clangxx_asan -m32 -O0 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-32 < %t.out -// RUN: %clangxx_asan -m32 -O1 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-32 < %t.out -// RUN: %clangxx_asan -m32 -O2 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-32 < %t.out - -// Zero-base shadow only works on x86_64 and i386. -// REQUIRES: x86_64-supported-target,i386-supported-target - -#include <string.h> -int main(int argc, char **argv) { - char x[10]; - memset(x, 0, 10); - int res = x[argc * 10]; // BOOOM - // CHECK: {{READ of size 1 at 0x.* thread T0}} - // CHECK: {{ #0 0x.* in _?main .*zero-base-shadow.cc:}}[[@LINE-2]] - // CHECK: {{Address 0x.* is .* frame}} - // CHECK: main - - // Check that shadow for stack memory occupies lower part of address space. - // CHECK-64: =>0x0f - // CHECK-32: =>0x1 - return res; -} diff --git a/lib/asan/lit_tests/Darwin/interface_symbols_darwin.c b/lib/asan/lit_tests/TestCases/Darwin/interface_symbols_darwin.c index 3fca6e915324..b453b64cafd2 100644 --- a/lib/asan/lit_tests/Darwin/interface_symbols_darwin.c +++ b/lib/asan/lit_tests/TestCases/Darwin/interface_symbols_darwin.c @@ -2,7 +2,7 @@ // If you're changing this file, please also change // ../Linux/interface_symbols.c -// RUN: %clang -fsanitize=address -dead_strip -O2 %s -o %t.exe +// RUN: %clang_asan -dead_strip -O2 %s -o %t.exe // RUN: rm -f %t.symbols %t.interface // RUN: nm -g `otool -L %t.exe | grep "asan_osx_dynamic.dylib" | \ @@ -16,7 +16,7 @@ // RUN: | grep -v "__asan_default_options" \ // RUN: | grep -v "__asan_on_error" > %t.symbols -// RUN: cat %p/../../asan_interface_internal.h \ +// RUN: cat %p/../../../asan_interface_internal.h \ // RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \ // RUN: | grep -v "OPTIONAL" \ // RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \ @@ -33,6 +33,8 @@ // RUN: echo __asan_report_store16 >> %t.interface // RUN: echo __asan_report_load_n >> %t.interface // RUN: echo __asan_report_store_n >> %t.interface +// RUN: for i in `jot - 0 10`; do echo __asan_stack_malloc_$i >> %t.interface; done +// RUN: for i in `jot - 0 10`; do echo __asan_stack_free_$i >> %t.interface; done // RUN: cat %t.interface | sort -u | diff %t.symbols - diff --git a/lib/asan/lit_tests/Darwin/lit.local.cfg b/lib/asan/lit_tests/TestCases/Darwin/lit.local.cfg index a85dfcd24c08..a85dfcd24c08 100644 --- a/lib/asan/lit_tests/Darwin/lit.local.cfg +++ b/lib/asan/lit_tests/TestCases/Darwin/lit.local.cfg diff --git a/lib/asan/lit_tests/TestCases/Darwin/malloc_set_zone_name-mprotect.cc b/lib/asan/lit_tests/TestCases/Darwin/malloc_set_zone_name-mprotect.cc new file mode 100644 index 000000000000..807a8283e788 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Darwin/malloc_set_zone_name-mprotect.cc @@ -0,0 +1,51 @@ +// Regression test for a bug in malloc_create_zone() +// (https://code.google.com/p/address-sanitizer/issues/detail?id=203) +// The old implementation of malloc_create_zone() didn't always return a +// page-aligned address, so we can only test on a best-effort basis. + +// RUN: %clangxx_asan %s -o %t +// RUN: %t 2>&1 + +#include <malloc/malloc.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +const int kNumIter = 4096; +const int kNumZones = 100; +int main() { + char *mem[kNumIter * 2]; + // Allocate memory chunks from different size classes up to 1 page. + // (For the case malloc() returns memory chunks in descending order) + for (int i = 0; i < kNumIter; i++) { + mem[i] = (char*)malloc(8 * i); + } + // Try to allocate a page-aligned malloc zone. Otherwise the mprotect() call + // in malloc_set_zone_name() will silently fail. + malloc_zone_t *zone = NULL; + bool aligned = false; + for (int i = 0; i < kNumZones; i++) { + zone = malloc_create_zone(0, 0); + if (((uintptr_t)zone & (~0xfff)) == (uintptr_t)zone) { + aligned = true; + break; + } + } + if (!aligned) { + printf("Warning: couldn't allocate a page-aligned zone."); + return 0; + } + // malloc_set_zone_name() calls mprotect(zone, 4096, PROT_READ | PROT_WRITE), + // modifies the zone contents and then calls mprotect(zone, 4096, PROT_READ). + malloc_set_zone_name(zone, "foobar"); + // Allocate memory chunks from different size classes again. + for (int i = 0; i < kNumIter; i++) { + mem[i + kNumIter] = (char*)malloc(8 * i); + } + // Access the allocated memory chunks and free them. + for (int i = 0; i < kNumIter * 2; i++) { + memset(mem[i], 'a', 8 * (i % kNumIter)); + free(mem[i]); + } + return 0; +} diff --git a/lib/asan/lit_tests/TestCases/Darwin/malloc_zone-protected.cc b/lib/asan/lit_tests/TestCases/Darwin/malloc_zone-protected.cc new file mode 100644 index 000000000000..d5f6c7c12e67 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Darwin/malloc_zone-protected.cc @@ -0,0 +1,20 @@ +// Make sure the zones created by malloc_create_zone() are write-protected. +#include <malloc/malloc.h> +#include <stdio.h> + +// RUN: %clangxx_asan %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + + +void *pwn(malloc_zone_t *unused_zone, size_t unused_size) { + printf("PWNED\n"); + return NULL; +} + +int main() { + malloc_zone_t *zone = malloc_create_zone(0, 0); + zone->malloc = pwn; + void *v = malloc_zone_malloc(zone, 1); + // CHECK-NOT: PWNED + return 0; +} diff --git a/lib/asan/lit_tests/Darwin/reexec-insert-libraries-env.cc b/lib/asan/lit_tests/TestCases/Darwin/reexec-insert-libraries-env.cc index 40a459fd84db..208fe43ac7e4 100644 --- a/lib/asan/lit_tests/Darwin/reexec-insert-libraries-env.cc +++ b/lib/asan/lit_tests/TestCases/Darwin/reexec-insert-libraries-env.cc @@ -2,8 +2,8 @@ // This is a regression test for // https://code.google.com/p/address-sanitizer/issues/detail?id=159 -// RUN: %clangxx_asan -m64 %s -o %t -// RUN: %clangxx -m64 %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \ +// RUN: %clangxx_asan %s -o %t +// RUN: %clangxx %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \ // RUN: -dynamiclib -o darwin-dummy-shared-lib-so.dylib // FIXME: the following command line may hang in the case of a regression. diff --git a/lib/asan/lit_tests/TestCases/Darwin/unset-insert-libraries-on-exec.cc b/lib/asan/lit_tests/TestCases/Darwin/unset-insert-libraries-on-exec.cc new file mode 100644 index 000000000000..fa0dd4f9df88 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Darwin/unset-insert-libraries-on-exec.cc @@ -0,0 +1,20 @@ +// Make sure ASan removes the runtime library from DYLD_INSERT_LIBRARIES before +// executing other programs. + +// RUN: %clangxx_asan %s -o %t +// RUN: %clangxx %p/../Helpers/echo-env.cc -o %T/echo-env +// RUN: %clangxx %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \ +// RUN: -dynamiclib -o %t-darwin-dummy-shared-lib-so.dylib + +// Make sure DYLD_INSERT_LIBRARIES doesn't contain the runtime library before +// execl(). + +// RUN: %t %T/echo-env >/dev/null 2>&1 +// RUN: DYLD_INSERT_LIBRARIES=%t-darwin-dummy-shared-lib-so.dylib \ +// RUN: %t %T/echo-env 2>&1 | FileCheck %s || exit 1 +#include <unistd.h> +int main(int argc, char *argv[]) { + execl(argv[1], argv[1], "DYLD_INSERT_LIBRARIES", NULL); + // CHECK: {{DYLD_INSERT_LIBRARIES = .*darwin-dummy-shared-lib-so.dylib.*}} + return 0; +} diff --git a/lib/asan/lit_tests/Helpers/blacklist-extra.cc b/lib/asan/lit_tests/TestCases/Helpers/blacklist-extra.cc index 627115cdda2b..627115cdda2b 100644 --- a/lib/asan/lit_tests/Helpers/blacklist-extra.cc +++ b/lib/asan/lit_tests/TestCases/Helpers/blacklist-extra.cc diff --git a/lib/asan/lit_tests/TestCases/Helpers/echo-env.cc b/lib/asan/lit_tests/TestCases/Helpers/echo-env.cc new file mode 100644 index 000000000000..65e91c155c84 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Helpers/echo-env.cc @@ -0,0 +1,19 @@ +// Helper binary for +// lit_tests/TestCases/Darwin/unset-insert-libraries-on-exec.cc +// Prints the environment variable with the given name. +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) { + if (argc != 2) { + printf("Usage: %s ENVNAME\n", argv[0]); + exit(1); + } + const char *value = getenv(argv[1]); + if (value) { + printf("%s = %s\n", argv[1], value); + } else { + printf("%s not set.\n", argv[1]); + } + return 0; +} diff --git a/lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc b/lib/asan/lit_tests/TestCases/Helpers/init-order-atexit-extra.cc index e4189d19d099..e4189d19d099 100644 --- a/lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc +++ b/lib/asan/lit_tests/TestCases/Helpers/init-order-atexit-extra.cc diff --git a/lib/asan/lit_tests/TestCases/Helpers/init-order-pthread-create-extra.cc b/lib/asan/lit_tests/TestCases/Helpers/init-order-pthread-create-extra.cc new file mode 100644 index 000000000000..d4606f0afb52 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Helpers/init-order-pthread-create-extra.cc @@ -0,0 +1,2 @@ +void *bar(void *input); +void *glob2 = bar((void*)0x2345); diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc b/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist-extra.cc index 09aed2112d5e..09aed2112d5e 100644 --- a/lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc +++ b/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist-extra.cc diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist-extra2.cc b/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist-extra2.cc index 69455a0a6fc9..69455a0a6fc9 100644 --- a/lib/asan/lit_tests/Helpers/initialization-blacklist-extra2.cc +++ b/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist-extra2.cc diff --git a/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist.txt b/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist.txt new file mode 100644 index 000000000000..83294635622d --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist.txt @@ -0,0 +1,3 @@ +global:*badGlobal*=init +type:*badNamespace::BadClass*=init +src:*initialization-blacklist-extra2.cc=init diff --git a/lib/asan/lit_tests/Helpers/initialization-bug-extra.cc b/lib/asan/lit_tests/TestCases/Helpers/initialization-bug-extra.cc index 3c4cb411defa..3c4cb411defa 100644 --- a/lib/asan/lit_tests/Helpers/initialization-bug-extra.cc +++ b/lib/asan/lit_tests/TestCases/Helpers/initialization-bug-extra.cc diff --git a/lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc b/lib/asan/lit_tests/TestCases/Helpers/initialization-bug-extra2.cc index a3d8f190e58b..a3d8f190e58b 100644 --- a/lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc +++ b/lib/asan/lit_tests/TestCases/Helpers/initialization-bug-extra2.cc diff --git a/lib/asan/lit_tests/Helpers/initialization-constexpr-extra.cc b/lib/asan/lit_tests/TestCases/Helpers/initialization-constexpr-extra.cc index b32466a981b3..b32466a981b3 100644 --- a/lib/asan/lit_tests/Helpers/initialization-constexpr-extra.cc +++ b/lib/asan/lit_tests/TestCases/Helpers/initialization-constexpr-extra.cc diff --git a/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc b/lib/asan/lit_tests/TestCases/Helpers/initialization-nobug-extra.cc index 886165affd76..886165affd76 100644 --- a/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc +++ b/lib/asan/lit_tests/TestCases/Helpers/initialization-nobug-extra.cc diff --git a/lib/asan/lit_tests/Helpers/lit.local.cfg b/lib/asan/lit_tests/TestCases/Helpers/lit.local.cfg index 2fc4d99456b0..2fc4d99456b0 100644 --- a/lib/asan/lit_tests/Helpers/lit.local.cfg +++ b/lib/asan/lit_tests/TestCases/Helpers/lit.local.cfg diff --git a/lib/asan/lit_tests/Linux/asan_prelink_test.cc b/lib/asan/lit_tests/TestCases/Linux/asan_prelink_test.cc index c209c39c8c42..0f158c1bb3dd 100644 --- a/lib/asan/lit_tests/Linux/asan_prelink_test.cc +++ b/lib/asan/lit_tests/TestCases/Linux/asan_prelink_test.cc @@ -3,13 +3,13 @@ // or gold's flag -Ttext (we try the first flag first, if that fails we // try the second flag). // -// RUN: %clangxx_asan -m64 -c %s -o %t.o -// RUN: %clangxx_asan -m64 -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext-segment=0x3600000000 ||\ -// RUN: %clangxx_asan -m64 -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext=0x3600000000 -// RUN: %clangxx_asan -m64 %t.o %t.so -Wl,-R. -o %t +// RUN: %clangxx_asan -c %s -o %t.o +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext-segment=0x3600000000 ||\ +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext=0x3600000000 +// RUN: %clangxx_asan %t.o %t.so -Wl,-R. -o %t // RUN: ASAN_OPTIONS=verbosity=1 %t 2>&1 | FileCheck %s -// REQUIRES: x86_64-supported-target +// REQUIRES: x86_64-supported-target, asan-64-bits #if BUILD_SO int G; int *getG() { diff --git a/lib/asan/lit_tests/Linux/clone_test.cc b/lib/asan/lit_tests/TestCases/Linux/clone_test.cc index ca13b22425f2..0e12f35b400a 100644 --- a/lib/asan/lit_tests/Linux/clone_test.cc +++ b/lib/asan/lit_tests/TestCases/Linux/clone_test.cc @@ -1,14 +1,10 @@ // Regression test for: // http://code.google.com/p/address-sanitizer/issues/detail?id=37 -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t | FileCheck %s #include <stdio.h> #include <sched.h> diff --git a/lib/asan/lit_tests/TestCases/Linux/coverage.cc b/lib/asan/lit_tests/TestCases/Linux/coverage.cc new file mode 100644 index 000000000000..4373e9b13c68 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Linux/coverage.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %t.so -fPIC +// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s -o %t -Wl,-R. %t.so +// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1 +// RUN: %t 2>&1 | FileCheck %s --check-prefix=CHECK-main +// RUN: %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-foo +// RUN: %t bar 2>&1 | FileCheck %s --check-prefix=CHECK-bar +// RUN: %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#ifdef SHARED +void bar() { printf("bar\n"); } +#else +__attribute__((noinline)) +void foo() { printf("foo\n"); } +extern void bar(); + +int main(int argc, char **argv) { + fprintf(stderr, "PID: %d\n", getpid()); + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "foo")) + foo(); + if (!strcmp(argv[i], "bar")) + bar(); + } +} +#endif + +// CHECK-main: PID: [[PID:[0-9]+]] +// CHECK-main: [[PID]].sancov: 1 PCs written +// CHECK-main-NOT: .so.[[PID]] +// +// CHECK-foo: PID: [[PID:[0-9]+]] +// CHECK-foo: [[PID]].sancov: 2 PCs written +// CHECK-foo-NOT: .so.[[PID]] +// +// CHECK-bar: PID: [[PID:[0-9]+]] +// CHECK-bar: [[PID]].sancov: 1 PCs written +// CHECK-bar: .so.[[PID]].sancov: 1 PCs written +// +// CHECK-foo-bar: PID: [[PID:[0-9]+]] +// CHECK-foo-bar: [[PID]].sancov: 2 PCs written +// CHECK-foo-bar: so.[[PID]].sancov: 1 PCs written diff --git a/lib/asan/lit_tests/Linux/glob.cc b/lib/asan/lit_tests/TestCases/Linux/glob.cc index e05228ff39e3..123768b099ed 100644 --- a/lib/asan/lit_tests/Linux/glob.cc +++ b/lib/asan/lit_tests/TestCases/Linux/glob.cc @@ -1,7 +1,5 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t %p 2>&1 | FileCheck %s #include <assert.h> #include <glob.h> @@ -24,6 +22,7 @@ int main(int argc, char *argv[]) { assert(globbuf.gl_pathc == 2); printf("%zu\n", strlen(globbuf.gl_pathv[0])); printf("%zu\n", strlen(globbuf.gl_pathv[1])); + globfree(&globbuf); printf("PASS\n"); // CHECK: PASS return 0; diff --git a/lib/asan/lit_tests/Linux/glob_test_root/aa b/lib/asan/lit_tests/TestCases/Linux/glob_test_root/aa index e69de29bb2d1..e69de29bb2d1 100644 --- a/lib/asan/lit_tests/Linux/glob_test_root/aa +++ b/lib/asan/lit_tests/TestCases/Linux/glob_test_root/aa diff --git a/lib/asan/lit_tests/Linux/glob_test_root/ab b/lib/asan/lit_tests/TestCases/Linux/glob_test_root/ab index e69de29bb2d1..e69de29bb2d1 100644 --- a/lib/asan/lit_tests/Linux/glob_test_root/ab +++ b/lib/asan/lit_tests/TestCases/Linux/glob_test_root/ab diff --git a/lib/asan/lit_tests/Linux/glob_test_root/ba b/lib/asan/lit_tests/TestCases/Linux/glob_test_root/ba index e69de29bb2d1..e69de29bb2d1 100644 --- a/lib/asan/lit_tests/Linux/glob_test_root/ba +++ b/lib/asan/lit_tests/TestCases/Linux/glob_test_root/ba diff --git a/lib/asan/lit_tests/TestCases/Linux/heap-overflow-large.cc b/lib/asan/lit_tests/TestCases/Linux/heap-overflow-large.cc new file mode 100644 index 000000000000..67e9c3718d59 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Linux/heap-overflow-large.cc @@ -0,0 +1,23 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=183 + +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: not %t 12 2>&1 | FileCheck %s +// RUN: not %t 100 2>&1 | FileCheck %s +// RUN: not %t 10000 2>&1 | FileCheck %s + +#include <stdlib.h> +#include <string.h> + +int main(int argc, char *argv[]) { + int *x = new int[5]; + memset(x, 0, sizeof(x[0]) * 5); + int index = atoi(argv[1]); + int res = x[index]; + // CHECK: AddressSanitizer: {{(heap-buffer-overflow|SEGV)}} + // CHECK: #0 0x{{.*}} in main {{.*}}heap-overflow-large.cc:[[@LINE-2]] + // CHECK: AddressSanitizer can not {{(provide additional info|describe address in more detail \(wild memory access suspected\))}} + // CHECK: SUMMARY: AddressSanitizer: {{(heap-buffer-overflow|SEGV)}} + delete[] x; + return res; +} diff --git a/lib/asan/lit_tests/Linux/heavy_uar_test.cc b/lib/asan/lit_tests/TestCases/Linux/heavy_uar_test.cc index c0f4560fb4e7..27b179e83624 100644 --- a/lib/asan/lit_tests/Linux/heavy_uar_test.cc +++ b/lib/asan/lit_tests/TestCases/Linux/heavy_uar_test.cc @@ -1,9 +1,8 @@ -// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O0 %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O2 %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O2 %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s +// RUN: export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O0 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s #include <stdio.h> #include <string.h> diff --git a/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc b/lib/asan/lit_tests/TestCases/Linux/initialization-bug-any-order.cc index 4f41dda18128..042a07e428d9 100644 --- a/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc +++ b/lib/asan/lit_tests/TestCases/Linux/initialization-bug-any-order.cc @@ -3,12 +3,10 @@ // independently on order in which we list source files (if we specify // strict init-order checking). -// RUN: %clangxx_asan -m64 -O0 %s %p/../Helpers/initialization-bug-extra.cc -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 \ -// RUN: | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O0 %p/../Helpers/initialization-bug-extra.cc %s -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 \ -// RUN: | %symbolize | FileCheck %s +// RUN: %clangxx_asan -O0 %s %p/../Helpers/initialization-bug-extra.cc -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %p/../Helpers/initialization-bug-extra.cc %s -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true not %t 2>&1 | FileCheck %s // Do not test with optimization -- the error may be optimized away. diff --git a/lib/asan/lit_tests/TestCases/Linux/interception_failure_test.cc b/lib/asan/lit_tests/TestCases/Linux/interception_failure_test.cc new file mode 100644 index 000000000000..9d161aa2dccb --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Linux/interception_failure_test.cc @@ -0,0 +1,22 @@ +// If user provides his own libc functions, ASan doesn't +// intercept these functions. + +// RUN: %clangxx_asan -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> + +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return 0; +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: my_strtol_interceptor + // CHECK-NOT: heap-use-after-free +} diff --git a/lib/asan/lit_tests/TestCases/Linux/interception_malloc_test.cc b/lib/asan/lit_tests/TestCases/Linux/interception_malloc_test.cc new file mode 100644 index 000000000000..cdd7239ab7bc --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Linux/interception_malloc_test.cc @@ -0,0 +1,23 @@ +// ASan interceptor can be accessed with __interceptor_ prefix. + +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" void *__interceptor_malloc(size_t size); +extern "C" void *malloc(size_t size) { + write(2, "malloc call\n", sizeof("malloc call\n") - 1); + return __interceptor_malloc(size); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: malloc call + // CHECK: heap-use-after-free +} diff --git a/lib/asan/lit_tests/TestCases/Linux/interception_readdir_r_test.cc b/lib/asan/lit_tests/TestCases/Linux/interception_readdir_r_test.cc new file mode 100644 index 000000000000..198e1f3884dd --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Linux/interception_readdir_r_test.cc @@ -0,0 +1,59 @@ +// RUN: %clangxx_asan -O0 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// +// RUN: %clangxx_asan -O0 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s + +#include <dirent.h> +#include <memory.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + + +int main() { + // Ensure the readdir_r interceptor doesn't erroneously mark the entire dirent + // as written when the end of the directory pointer is reached. + fputs("test1: reading the " TEMP_DIR " directory...\n", stderr); + DIR *d = opendir(TEMP_DIR); + struct dirent *result = (struct dirent *)(0xfeedbeef); + // We assume the temp dir for this test doesn't have crazy long file names. + char entry_buffer[4096]; + memset(entry_buffer, 0xab, sizeof(entry_buffer)); + unsigned count = 0; + do { + // Stamp the entry struct to try to trick the interceptor. + ((struct dirent *)entry_buffer)->d_reclen = 9999; + if (readdir_r(d, (struct dirent *)entry_buffer, &result) != 0) + abort(); + ++count; + } while (result != NULL); + fprintf(stderr, "read %d entries\n", count); + closedir(d); + // CHECK: test1: reading the {{.*}} directory... + // CHECK-NOT: stack-buffer-overflow + // CHECK: read {{.*}} entries + + // Ensure the readdir64_r interceptor doesn't have the bug either. + fputs("test2: reading the " TEMP_DIR " directory...\n", stderr); + d = opendir(TEMP_DIR); + struct dirent64 *result64; + memset(entry_buffer, 0xab, sizeof(entry_buffer)); + count = 0; + do { + // Stamp the entry struct to try to trick the interceptor. + ((struct dirent64 *)entry_buffer)->d_reclen = 9999; + if (readdir64_r(d, (struct dirent64 *)entry_buffer, &result64) != 0) + abort(); + ++count; + } while (result64 != NULL); + fprintf(stderr, "read %d entries\n", count); + closedir(d); + // CHECK: test2: reading the {{.*}} directory... + // CHECK-NOT: stack-buffer-overflow + // CHECK: read {{.*}} entries +} diff --git a/lib/asan/lit_tests/TestCases/Linux/interception_test.cc b/lib/asan/lit_tests/TestCases/Linux/interception_test.cc new file mode 100644 index 000000000000..2b3316d7dc8a --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Linux/interception_test.cc @@ -0,0 +1,22 @@ +// ASan interceptor can be accessed with __interceptor_ prefix. + +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> + +extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base); +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return __interceptor_strtol(nptr, endptr, base); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: my_strtol_interceptor + // CHECK: heap-use-after-free +} diff --git a/lib/asan/lit_tests/Linux/interface_symbols_linux.c b/lib/asan/lit_tests/TestCases/Linux/interface_symbols_linux.c index 4134c8744043..d6ceda7af8b6 100644 --- a/lib/asan/lit_tests/Linux/interface_symbols_linux.c +++ b/lib/asan/lit_tests/TestCases/Linux/interface_symbols_linux.c @@ -1,14 +1,15 @@ // Check the presense of interface symbols in compiled file. -// RUN: %clang -fsanitize=address -O2 %s -o %t.exe +// RUN: %clang_asan -O2 %s -o %t.exe // RUN: nm -D %t.exe | grep " T " | sed "s/.* T //" \ // RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \ // RUN: | grep -v "__asan_malloc_hook" \ // RUN: | grep -v "__asan_free_hook" \ // RUN: | grep -v "__asan_symbolize" \ // RUN: | grep -v "__asan_default_options" \ +// RUN: | grep -v "__asan_stack_" \ // RUN: | grep -v "__asan_on_error" > %t.symbols -// RUN: cat %p/../../asan_interface_internal.h \ +// RUN: cat %p/../../../asan_interface_internal.h \ // RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \ // RUN: | grep -v "OPTIONAL" \ // RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \ diff --git a/lib/asan/lit_tests/Linux/lit.local.cfg b/lib/asan/lit_tests/TestCases/Linux/lit.local.cfg index 57271b8078a4..57271b8078a4 100644 --- a/lib/asan/lit_tests/Linux/lit.local.cfg +++ b/lib/asan/lit_tests/TestCases/Linux/lit.local.cfg diff --git a/lib/asan/lit_tests/Linux/malloc-in-qsort.cc b/lib/asan/lit_tests/TestCases/Linux/malloc-in-qsort.cc index ee2e81f0d2ab..3251b35e143c 100644 --- a/lib/asan/lit_tests/Linux/malloc-in-qsort.cc +++ b/lib/asan/lit_tests/TestCases/Linux/malloc-in-qsort.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_asan -m64 -O2 %s -o %t -// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=1 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-FAST -// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=0 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-SLOW +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAST +// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-SLOW // Test how well we unwind in presence of qsort in the stack // (i.e. if we can unwind through a function compiled w/o frame pointers). @@ -9,6 +9,8 @@ // Fast unwinder is only avaliable on x86_64 and i386. // REQUIRES: x86_64-supported-target +// REQUIRES: compiler-rt-optimized + #include <stdlib.h> #include <stdio.h> diff --git a/lib/asan/lit_tests/Linux/malloc_delete_mismatch.cc b/lib/asan/lit_tests/TestCases/Linux/malloc_delete_mismatch.cc index f34b33a38fb3..7010eb2de2e7 100644 --- a/lib/asan/lit_tests/Linux/malloc_delete_mismatch.cc +++ b/lib/asan/lit_tests/TestCases/Linux/malloc_delete_mismatch.cc @@ -2,11 +2,16 @@ // is set. // RUN: %clangxx_asan -g %s -o %t 2>&1 -// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1 %t 2>&1 | \ -// RUN: %symbolize | FileCheck %s + +// Find error and provide malloc context. +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=ALLOC-STACK // No error here. // RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=0 %t + +// Also works if no malloc context is available. +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s #include <stdlib.h> static volatile char *x; @@ -21,6 +26,6 @@ int main() { // CHECK: #{{.*}}main // CHECK: is located 0 bytes inside of 10-byte region // CHECK-NEXT: allocated by thread T0 here: -// CHECK-NEXT: #0{{.*}}malloc -// CHECK: #{{.*}}main +// ALLOC-STACK-NEXT: #0{{.*}}malloc +// ALLOC-STACK: #{{.*}}main // CHECK: HINT: {{.*}} you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0 diff --git a/lib/asan/lit_tests/Linux/overflow-in-qsort.cc b/lib/asan/lit_tests/TestCases/Linux/overflow-in-qsort.cc index 8bc43ca0a5c3..139977261ec9 100644 --- a/lib/asan/lit_tests/Linux/overflow-in-qsort.cc +++ b/lib/asan/lit_tests/TestCases/Linux/overflow-in-qsort.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_asan -m64 -O2 %s -o %t -// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=1 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-FAST -// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=0 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-SLOW +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAST +// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=0 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-SLOW // Test how well we unwind in presence of qsort in the stack // (i.e. if we can unwind through a function compiled w/o frame pointers). diff --git a/lib/asan/lit_tests/Linux/preinit_test.cc b/lib/asan/lit_tests/TestCases/Linux/preinit_test.cc index 28e509472c0c..28e509472c0c 100644 --- a/lib/asan/lit_tests/Linux/preinit_test.cc +++ b/lib/asan/lit_tests/TestCases/Linux/preinit_test.cc diff --git a/lib/asan/lit_tests/TestCases/Linux/ptrace.cc b/lib/asan/lit_tests/TestCases/Linux/ptrace.cc new file mode 100644 index 000000000000..8831b81efa96 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Linux/ptrace.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t +// RUN: %clangxx_asan -DPOSITIVE -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdio.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <sys/user.h> +#include <sys/wait.h> +#include <unistd.h> + +int main(void) { + pid_t pid; + pid = fork(); + if (pid == 0) { // child + ptrace(PTRACE_TRACEME, 0, NULL, NULL); + execl("/bin/true", "true", NULL); + } else { + wait(NULL); + user_regs_struct regs; + int res; + user_regs_struct * volatile pregs = ®s; +#ifdef POSITIVE + ++pregs; +#endif + res = ptrace(PTRACE_GETREGS, pid, NULL, pregs); + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: {{.*ptrace.cc:}}[[@LINE-2]] + assert(!res); +#if __WORDSIZE == 64 + printf("%zx\n", regs.rip); +#else + printf("%lx\n", regs.eip); +#endif + + user_fpregs_struct fpregs; + res = ptrace(PTRACE_GETFPREGS, pid, NULL, &fpregs); + assert(!res); + printf("%lx\n", (unsigned long)fpregs.cwd); + +#if __WORDSIZE == 32 + user_fpxregs_struct fpxregs; + res = ptrace(PTRACE_GETFPXREGS, pid, NULL, &fpxregs); + assert(!res); + printf("%lx\n", (unsigned long)fpxregs.mxcsr); +#endif + + ptrace(PTRACE_CONT, pid, NULL, NULL); + wait(NULL); + } + return 0; +} diff --git a/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc b/lib/asan/lit_tests/TestCases/Linux/rlimit_mmap_test.cc index 86794756c76f..0d1d4baa7671 100644 --- a/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc +++ b/lib/asan/lit_tests/TestCases/Linux/rlimit_mmap_test.cc @@ -1,5 +1,5 @@ // Check that we properly report mmap failure. -// RUN: %clangxx_asan %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan %s -o %t && not %t 2>&1 | FileCheck %s #include <stdlib.h> #include <assert.h> #include <sys/time.h> diff --git a/lib/asan/lit_tests/Linux/swapcontext_test.cc b/lib/asan/lit_tests/TestCases/Linux/swapcontext_test.cc index 47a8d9891f51..6cbb69a35321 100644 --- a/lib/asan/lit_tests/Linux/swapcontext_test.cc +++ b/lib/asan/lit_tests/TestCases/Linux/swapcontext_test.cc @@ -1,13 +1,9 @@ // Check that ASan plays well with easy cases of makecontext/swapcontext. -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t 2>&1 | FileCheck %s // // This test is too sublte to try on non-x86 arch for now. // REQUIRES: x86_64-supported-target,i386-supported-target diff --git a/lib/asan/lit_tests/Linux/syscalls.cc b/lib/asan/lit_tests/TestCases/Linux/syscalls.cc index b2edcfb92375..4bcbe446113e 100644 --- a/lib/asan/lit_tests/Linux/syscalls.cc +++ b/lib/asan/lit_tests/TestCases/Linux/syscalls.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s #include <assert.h> #include <errno.h> @@ -17,6 +17,6 @@ int main(int argc, char *argv[]) { __sanitizer_syscall_pre_recvmsg(0, buf - 1, 0); // CHECK: AddressSanitizer: stack-buffer-{{.*}}erflow // CHECK: READ of size {{.*}} at {{.*}} thread T0 - // CHECK: #0 {{.*}} in __sanitizer_syscall_pre_recvmsg + // CHECK: #0 {{.*}} in __sanitizer_syscall{{.*}}recvmsg return 0; } diff --git a/lib/asan/lit_tests/Linux/time_null_regtest.cc b/lib/asan/lit_tests/TestCases/Linux/time_null_regtest.cc index 975bca3d105a..566409be6a19 100644 --- a/lib/asan/lit_tests/Linux/time_null_regtest.cc +++ b/lib/asan/lit_tests/TestCases/Linux/time_null_regtest.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_asan -m64 -O0 %s -fsanitize-address-zero-base-shadow -pie -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -O0 %s -fsanitize-address-zero-base-shadow -pie -o %t && %t 2>&1 | FileCheck %s // Zero-base shadow only works on x86_64 and i386. // REQUIRES: x86_64-supported-target diff --git a/lib/asan/lit_tests/TestCases/Linux/tsd_dtor_leak.cc b/lib/asan/lit_tests/TestCases/Linux/tsd_dtor_leak.cc new file mode 100644 index 000000000000..a1d89ee437d4 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Linux/tsd_dtor_leak.cc @@ -0,0 +1,39 @@ +// Regression test for a leak in tsd: +// https://code.google.com/p/address-sanitizer/issues/detail?id=233 +// RUN: %clangxx_asan -O1 %s -o %t +// RUN: ASAN_OPTIONS=quarantine_size=1 %t +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +extern "C" size_t __asan_get_heap_size(); +static pthread_key_t tsd_key; + +void *Thread(void *) { + pthread_setspecific(tsd_key, malloc(10)); + return 0; +} + +static volatile void *v; + +void Dtor(void *tsd) { + v = malloc(10000); + free(tsd); + free((void*)v); // The bug was that this was leaking. +} + +int main() { + assert(0 == pthread_key_create(&tsd_key, Dtor)); + size_t old_heap_size = 0; + for (int i = 0; i < 10; i++) { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); + size_t new_heap_size = __asan_get_heap_size(); + fprintf(stderr, "heap size: new: %zd old: %zd\n", new_heap_size, old_heap_size); + if (old_heap_size) + assert(old_heap_size == new_heap_size); + old_heap_size = new_heap_size; + } +} diff --git a/lib/asan/lit_tests/TestCases/Linux/uar_signals.cc b/lib/asan/lit_tests/TestCases/Linux/uar_signals.cc new file mode 100644 index 000000000000..9663859dfefd --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Linux/uar_signals.cc @@ -0,0 +1,70 @@ +// This test shows that the current implementation of use-after-return is +// not signal-safe. +// RUN: %clangxx_asan -O1 %s -o %t -lpthread && %t +// RUN: %clangxx_asan -O1 %s -o %t -lpthread && %t +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/time.h> +#include <pthread.h> + +int *g; +int n_signals; + +typedef void (*Sigaction)(int, siginfo_t *, void *); + +void SignalHandler(int, siginfo_t*, void*) { + int local; + g = &local; + n_signals++; + // printf("s: %p\n", &local); +} + +static void EnableSigprof(Sigaction SignalHandler) { + struct sigaction sa; + sa.sa_sigaction = SignalHandler; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGPROF, &sa, NULL) != 0) { + perror("sigaction"); + abort(); + } + struct itimerval timer; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 1; + timer.it_value = timer.it_interval; + if (setitimer(ITIMER_PROF, &timer, 0) != 0) { + perror("setitimer"); + abort(); + } +} + +void RecursiveFunction(int depth) { + if (depth == 0) return; + int local; + g = &local; + // printf("r: %p\n", &local); + // printf("[%2d] n_signals: %d\n", depth, n_signals); + RecursiveFunction(depth - 1); + RecursiveFunction(depth - 1); +} + +void *Thread(void *) { + RecursiveFunction(18); + return NULL; +} + +int main(int argc, char **argv) { + EnableSigprof(SignalHandler); + + for (int i = 0; i < 4; i++) { + fprintf(stderr, "."); + const int kNumThread = sizeof(void*) == 8 ? 16 : 8; + pthread_t t[kNumThread]; + for (int i = 0; i < kNumThread; i++) + pthread_create(&t[i], 0, Thread, 0); + for (int i = 0; i < kNumThread; i++) + pthread_join(t[i], 0); + } + fprintf(stderr, "\n"); +} diff --git a/lib/asan/lit_tests/TestCases/Linux/unpoison_tls.cc b/lib/asan/lit_tests/TestCases/Linux/unpoison_tls.cc new file mode 100644 index 000000000000..d67c4f954e43 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Linux/unpoison_tls.cc @@ -0,0 +1,35 @@ +// Test that TLS is unpoisoned on thread death. +// REQUIRES: x86_64-supported-target,i386-supported-target + +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> + +#include <sanitizer/asan_interface.h> + +__thread int64_t tls_var[2]; + +volatile int64_t *p_tls_var; + +void *first(void *arg) { + ASAN_POISON_MEMORY_REGION(&tls_var, sizeof(tls_var)); + p_tls_var = tls_var; + return 0; +} + +void *second(void *arg) { + assert(tls_var == p_tls_var); + *p_tls_var = 1; + return 0; +} + +int main(int argc, char *argv[]) { + pthread_t p; + assert(0 == pthread_create(&p, 0, first, 0)); + assert(0 == pthread_join(p, 0)); + assert(0 == pthread_create(&p, 0, second, 0)); + assert(0 == pthread_join(p, 0)); + return 0; +} diff --git a/lib/asan/lit_tests/TestCases/Linux/zero-base-shadow32.cc b/lib/asan/lit_tests/TestCases/Linux/zero-base-shadow32.cc new file mode 100644 index 000000000000..e6bcc5597471 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Linux/zero-base-shadow32.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O0 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + +// Zero-base shadow only works on x86_64 and i386. +// REQUIRES: i386-supported-target, asan-32-bits + +#include <string.h> +int main(int argc, char **argv) { + char x[10]; + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*zero-base-shadow32.cc:}}[[@LINE-2]] + // CHECK: {{Address 0x.* is .* frame}} + // CHECK: main + + // Check that shadow for stack memory occupies lower part of address space. + // CHECK: =>0x1 + return res; +} diff --git a/lib/asan/lit_tests/TestCases/Linux/zero-base-shadow64.cc b/lib/asan/lit_tests/TestCases/Linux/zero-base-shadow64.cc new file mode 100644 index 000000000000..1db725c95752 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/Linux/zero-base-shadow64.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O0 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + +// Zero-base shadow only works on x86_64 and i386. +// REQUIRES: x86_64-supported-target, asan-64-bits + +#include <string.h> +int main(int argc, char **argv) { + char x[10]; + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*zero-base-shadow64.cc:}}[[@LINE-2]] + // CHECK: {{Address 0x.* is .* frame}} + // CHECK: main + + // Check that shadow for stack memory occupies lower part of address space. + // CHECK: =>0x0f + return res; +} diff --git a/lib/asan/lit_tests/SharedLibs/darwin-dummy-shared-lib-so.cc b/lib/asan/lit_tests/TestCases/SharedLibs/darwin-dummy-shared-lib-so.cc index 5d939991476e..5d939991476e 100644 --- a/lib/asan/lit_tests/SharedLibs/darwin-dummy-shared-lib-so.cc +++ b/lib/asan/lit_tests/TestCases/SharedLibs/darwin-dummy-shared-lib-so.cc diff --git a/lib/asan/lit_tests/SharedLibs/dlclose-test-so.cc b/lib/asan/lit_tests/TestCases/SharedLibs/dlclose-test-so.cc index 73e00507358a..73e00507358a 100644 --- a/lib/asan/lit_tests/SharedLibs/dlclose-test-so.cc +++ b/lib/asan/lit_tests/TestCases/SharedLibs/dlclose-test-so.cc diff --git a/lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc b/lib/asan/lit_tests/TestCases/SharedLibs/init-order-dlopen-so.cc index 20ef2d8a00bb..dc097a520d03 100644 --- a/lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc +++ b/lib/asan/lit_tests/TestCases/SharedLibs/init-order-dlopen-so.cc @@ -1,7 +1,7 @@ #include <stdio.h> #include <unistd.h> -void inc_global(); +extern "C" void inc_global(); int slow_init() { sleep(1); diff --git a/lib/asan/lit_tests/SharedLibs/lit.local.cfg b/lib/asan/lit_tests/TestCases/SharedLibs/lit.local.cfg index b3677c17a0f2..b3677c17a0f2 100644 --- a/lib/asan/lit_tests/SharedLibs/lit.local.cfg +++ b/lib/asan/lit_tests/TestCases/SharedLibs/lit.local.cfg diff --git a/lib/asan/lit_tests/SharedLibs/shared-lib-test-so.cc b/lib/asan/lit_tests/TestCases/SharedLibs/shared-lib-test-so.cc index 686a24578082..6ef565ce47b3 100644 --- a/lib/asan/lit_tests/SharedLibs/shared-lib-test-so.cc +++ b/lib/asan/lit_tests/TestCases/SharedLibs/shared-lib-test-so.cc @@ -19,3 +19,8 @@ extern "C" void inc(int index) { GLOB[index]++; } + +extern "C" +void inc2(int *a, int index) { + a[index]++; +} diff --git a/lib/asan/lit_tests/TestCases/allocator_returns_null.cc b/lib/asan/lit_tests/TestCases/allocator_returns_null.cc new file mode 100644 index 000000000000..595c9e252f86 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/allocator_returns_null.cc @@ -0,0 +1,81 @@ +// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// By default (allocator_may_return_null=0) the process should crash. +// With allocator_may_return_null=1 the allocator should return 0. +// +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <limits> +int main(int argc, char **argv) { + volatile size_t size = std::numeric_limits<size_t>::max() - 10000; + assert(argc == 2); + char *x = 0; + if (!strcmp(argv[1], "malloc")) { + fprintf(stderr, "malloc:\n"); + x = (char*)malloc(size); + } + if (!strcmp(argv[1], "calloc")) { + fprintf(stderr, "calloc:\n"); + x = (char*)calloc(size / 4, 4); + } + + if (!strcmp(argv[1], "calloc-overflow")) { + fprintf(stderr, "calloc-overflow:\n"); + volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = (char*)calloc(kArraySize, kArraySize2); + } + + if (!strcmp(argv[1], "realloc")) { + fprintf(stderr, "realloc:\n"); + x = (char*)realloc(0, size); + } + if (!strcmp(argv[1], "realloc-after-malloc")) { + fprintf(stderr, "realloc-after-malloc:\n"); + char *t = (char*)malloc(100); + *t = 42; + x = (char*)realloc(t, size); + assert(*t == 42); + } + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "x: %lx\n", (long)x); + return x != 0; +} +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: AddressSanitizer's allocator is terminating the process + +// CHECK-mNULL: malloc: +// CHECK-mNULL: x: 0 +// CHECK-cNULL: calloc: +// CHECK-cNULL: x: 0 +// CHECK-coNULL: calloc-overflow: +// CHECK-coNULL: x: 0 +// CHECK-rNULL: realloc: +// CHECK-rNULL: x: 0 +// CHECK-mrNULL: realloc-after-malloc: +// CHECK-mrNULL: x: 0 diff --git a/lib/asan/lit_tests/allow_user_segv.cc b/lib/asan/lit_tests/TestCases/allow_user_segv.cc index f8aed0d4ca80..55cf6044e7a0 100644 --- a/lib/asan/lit_tests/allow_user_segv.cc +++ b/lib/asan/lit_tests/TestCases/allow_user_segv.cc @@ -1,10 +1,8 @@ // Regression test for // https://code.google.com/p/address-sanitizer/issues/detail?id=180 -// RUN: %clangxx_asan -m64 -O0 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true not %t 2>&1 | FileCheck %s #include <signal.h> #include <stdio.h> diff --git a/lib/asan/lit_tests/TestCases/asan-symbolize-sanity-test.cc b/lib/asan/lit_tests/TestCases/asan-symbolize-sanity-test.cc new file mode 100644 index 000000000000..0efe245bb6b5 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/asan-symbolize-sanity-test.cc @@ -0,0 +1,39 @@ +// Check that asan_symbolize.py script works (for binaries, ASan RTL and +// shared object files. + +// RUN: %clangxx_asan -O0 %p/SharedLibs/shared-lib-test-so.cc -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: ASAN_SYMBOLIZER_PATH= not %t 2>&1 | %asan_symbolize | FileCheck %s +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include <string> + +using std::string; + +typedef void (fun_t)(int*, int); + +int main(int argc, char *argv[]) { + string path = string(argv[0]) + "-so.so"; + printf("opening %s ... \n", path.c_str()); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + fun_t *inc2 = (fun_t*)dlsym(lib, "inc2"); + if (!inc2) return 1; + printf("ok\n"); + int *array = (int*)malloc(40); + inc2(array, 1); + inc2(array, -1); // BOOM + // CHECK: ERROR: AddressSanitizer: heap-buffer-overflow + // CHECK: READ of size 4 at 0x{{.*}} + // CHECK: #0 {{.*}} in inc2 {{.*}}shared-lib-test-so.cc:25 + // CHECK: #1 {{.*}} in main {{.*}}asan-symbolize-sanity-test.cc:[[@LINE-4]] + // CHECK: allocated by thread T{{.*}} here: + // CHECK: #{{.*}} in {{(wrap_|__interceptor_)?}}malloc + // CHECK: #{{.*}} in main {{.*}}asan-symbolize-sanity-test.cc:[[@LINE-9]] + return 0; +} diff --git a/lib/asan/lit_tests/TestCases/assign_large_valloc_to_global.cc b/lib/asan/lit_tests/TestCases/assign_large_valloc_to_global.cc new file mode 100644 index 000000000000..b0a501576945 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/assign_large_valloc_to_global.cc @@ -0,0 +1,8 @@ +// Make sure we don't report a leak nor hang. +// RUN: %clangxx_asan -O3 %s -o %t && %t +#include <stdlib.h> +#ifndef __APPLE__ +#include <malloc.h> +#endif // __APPLE__ +int *p = (int*)valloc(1 << 20); +int main() { } diff --git a/lib/asan/lit_tests/TestCases/atexit_stats.cc b/lib/asan/lit_tests/TestCases/atexit_stats.cc new file mode 100644 index 000000000000..e3b1269d25cc --- /dev/null +++ b/lib/asan/lit_tests/TestCases/atexit_stats.cc @@ -0,0 +1,13 @@ +// Make sure we report atexit stats. +// RUN: %clangxx_asan -O3 %s -o %t +// RUN: ASAN_OPTIONS=atexit=1:print_stats=1 %t 2>&1 | FileCheck %s +#include <stdlib.h> +#if !defined(__APPLE__) +#include <malloc.h> +#endif +int *p1 = (int*)malloc(900); +int *p2 = (int*)malloc(90000); +int *p3 = (int*)malloc(9000000); +int main() { } + +// CHECK: AddressSanitizer exit stats: diff --git a/lib/asan/lit_tests/blacklist.cc b/lib/asan/lit_tests/TestCases/blacklist.cc index 6cfc1500c503..46625ee7bdb5 100644 --- a/lib/asan/lit_tests/blacklist.cc +++ b/lib/asan/lit_tests/TestCases/blacklist.cc @@ -3,25 +3,19 @@ // RUN: echo "fun:*brokenFunction*" > %tmp // RUN: echo "global:*badGlobal*" >> %tmp // RUN: echo "src:*blacklist-extra.cc" >> %tmp -// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O0 %s -o %t \ +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O0 %s -o %t \ // RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 -// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O1 %s -o %t \ +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O1 %s -o %t \ // RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 -// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O2 %s -o %t \ +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O2 %s -o %t \ // RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 -// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O3 %s -o %t \ -// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 -// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O0 %s -o %t \ -// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 -// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O1 %s -o %t \ -// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 -// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O2 %s -o %t \ -// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 -// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O3 %s -o %t \ +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O3 %s -o %t \ // RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 -// badGlobal is accessed improperly, but we blacklisted it. -int badGlobal; +// badGlobal is accessed improperly, but we blacklisted it. Align +// it to make sure memory past the end of badGlobal will be in +// the same page. +__attribute__((aligned(16))) int badGlobal; int readBadGlobal() { return (&badGlobal)[1]; } diff --git a/lib/asan/lit_tests/TestCases/contiguous_container.cc b/lib/asan/lit_tests/TestCases/contiguous_container.cc new file mode 100644 index 000000000000..aa97592c7bb9 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/contiguous_container.cc @@ -0,0 +1,47 @@ +// RUN: %clangxx_asan -O %s -o %t && %t +// +// Test __sanitizer_annotate_contiguous_container. + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +extern "C" { +void __sanitizer_annotate_contiguous_container(void *beg, void *end, + void *old_mid, void *new_mid); +bool __asan_address_is_poisoned(void *addr); +} // extern "C" + +void TestContainer(size_t capacity) { + char *beg = new char[capacity]; + char *end = beg + capacity; + char *mid = beg + capacity; + char *old_mid = 0; + unsigned seed = 0; + + for (int i = 0; i < 10000; i++) { + size_t size = rand_r(&seed) % (capacity + 1); + assert(size <= capacity); + old_mid = mid; + mid = beg + size; + __sanitizer_annotate_contiguous_container(beg, end, old_mid, mid); + + for (size_t idx = 0; idx < size; idx++) + assert(!__asan_address_is_poisoned(beg + idx)); + for (size_t idx = size; idx < capacity; idx++) + assert(__asan_address_is_poisoned(beg + idx)); + } + + // Don't forget to unpoison the whole thing before destroing/reallocating. + __sanitizer_annotate_contiguous_container(beg, end, mid, end); + for (size_t idx = 0; idx < capacity; idx++) + assert(!__asan_address_is_poisoned(beg + idx)); + delete[] beg; +} + +int main(int argc, char **argv) { + int n = argc == 1 ? 128 : atoi(argv[1]); + for (int i = 0; i <= n; i++) + TestContainer(i); +} diff --git a/lib/asan/lit_tests/TestCases/current_allocated_bytes.cc b/lib/asan/lit_tests/TestCases/current_allocated_bytes.cc new file mode 100644 index 000000000000..669cf150bdfb --- /dev/null +++ b/lib/asan/lit_tests/TestCases/current_allocated_bytes.cc @@ -0,0 +1,43 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t +// RUN: %clangxx_asan -O2 %s -o %t && %t + +#include <assert.h> +#include <pthread.h> +#include <sanitizer/asan_interface.h> +#include <stdio.h> +#include <stdlib.h> + +const size_t kLargeAlloc = 1UL << 20; + +void* allocate(void *arg) { + volatile void *ptr = malloc(kLargeAlloc); + free((void*)ptr); + return 0; +} + +void* check_stats(void *arg) { + assert(__asan_get_current_allocated_bytes() > 0); + return 0; +} + +int main() { + size_t used_mem = __asan_get_current_allocated_bytes(); + printf("Before: %zu\n", used_mem); + const int kNumIterations = 1000; + for (int iter = 0; iter < kNumIterations; iter++) { + pthread_t thr[4]; + for (int j = 0; j < 4; j++) { + assert(0 == + pthread_create(&thr[j], 0, (j < 2) ? allocate : check_stats, 0)); + } + for (int j = 0; j < 4; j++) + assert(0 == pthread_join(thr[j], 0)); + used_mem = __asan_get_current_allocated_bytes(); + if (used_mem > kLargeAlloc) { + printf("After iteration %d: %zu\n", iter, used_mem); + return 1; + } + } + printf("Success after %d iterations\n", kNumIterations); + return 0; +} diff --git a/lib/asan/lit_tests/TestCases/deep_call_stack.cc b/lib/asan/lit_tests/TestCases/deep_call_stack.cc new file mode 100644 index 000000000000..e24704b9019e --- /dev/null +++ b/lib/asan/lit_tests/TestCases/deep_call_stack.cc @@ -0,0 +1,25 @@ +// Check that UAR mode can handle very deep recusrion. +// export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O2 %s -o %t && \ +// RUN: %t 2>&1 | FileCheck %s +// Also check that use_sigaltstack+verbosity doesn't crash. +// RUN: ASAN_OPTIONS=verbosity=1:use_sigaltstack=1 %t | FileCheck %s +#include <stdio.h> + +__attribute__((noinline)) +void RecursiveFunc(int depth, int *ptr) { + if ((depth % 1000) == 0) + printf("[%05d] ptr: %p\n", depth, ptr); + if (depth == 0) + return; + int local; + RecursiveFunc(depth - 1, &local); +} + +int main(int argc, char **argv) { + RecursiveFunc(40000, 0); + return 0; +} +// CHECK: [40000] ptr: +// CHECK: [20000] ptr: +// CHECK: [00000] ptr diff --git a/lib/asan/lit_tests/deep_stack_uaf.cc b/lib/asan/lit_tests/TestCases/deep_stack_uaf.cc index 7b32798fefcc..920411c4ac52 100644 --- a/lib/asan/lit_tests/deep_stack_uaf.cc +++ b/lib/asan/lit_tests/TestCases/deep_stack_uaf.cc @@ -1,12 +1,7 @@ // Check that we can store lots of stack frames if asked to. -// RUN: %clangxx_asan -m64 -O0 %s -o %t 2>&1 -// RUN: ASAN_OPTIONS=malloc_context_size=120:redzone=512 %t 2>&1 | \ -// RUN: %symbolize | FileCheck %s - -// RUN: %clangxx_asan -m32 -O0 %s -o %t 2>&1 -// RUN: ASAN_OPTIONS=malloc_context_size=120:redzone=512 %t 2>&1 | \ -// RUN: %symbolize | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t 2>&1 +// RUN: ASAN_OPTIONS=malloc_context_size=120:redzone=512 not %t 2>&1 | FileCheck %s #include <stdlib.h> #include <stdio.h> diff --git a/lib/asan/lit_tests/TestCases/deep_tail_call.cc b/lib/asan/lit_tests/TestCases/deep_tail_call.cc new file mode 100644 index 000000000000..2e7aa8e0208f --- /dev/null +++ b/lib/asan/lit_tests/TestCases/deep_tail_call.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// CHECK: AddressSanitizer: global-buffer-overflow +int global[10]; +// CHECK: {{#0.*call4}} +void __attribute__((noinline)) call4(int i) { global[i+10]++; } +// CHECK: {{#1.*call3}} +void __attribute__((noinline)) call3(int i) { call4(i); } +// CHECK: {{#2.*call2}} +void __attribute__((noinline)) call2(int i) { call3(i); } +// CHECK: {{#3.*call1}} +void __attribute__((noinline)) call1(int i) { call2(i); } +// CHECK: {{#4.*main}} +int main(int argc, char **argv) { + call1(argc); + return global[0]; +} diff --git a/lib/asan/lit_tests/deep_thread_stack.cc b/lib/asan/lit_tests/TestCases/deep_thread_stack.cc index 781508d61616..92e0d66c82eb 100644 --- a/lib/asan/lit_tests/deep_thread_stack.cc +++ b/lib/asan/lit_tests/TestCases/deep_thread_stack.cc @@ -1,11 +1,7 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> diff --git a/lib/asan/lit_tests/default_blacklist.cc b/lib/asan/lit_tests/TestCases/default_blacklist.cc index 25a1ae1752b0..25a1ae1752b0 100644 --- a/lib/asan/lit_tests/default_blacklist.cc +++ b/lib/asan/lit_tests/TestCases/default_blacklist.cc diff --git a/lib/asan/lit_tests/default_options.cc b/lib/asan/lit_tests/TestCases/default_options.cc index 84b80557b852..84b80557b852 100644 --- a/lib/asan/lit_tests/default_options.cc +++ b/lib/asan/lit_tests/TestCases/default_options.cc diff --git a/lib/asan/lit_tests/dlclose-test.cc b/lib/asan/lit_tests/TestCases/dlclose-test.cc index b15895bf3579..03ed16016116 100644 --- a/lib/asan/lit_tests/dlclose-test.cc +++ b/lib/asan/lit_tests/TestCases/dlclose-test.cc @@ -14,30 +14,18 @@ // It works on i368/x86_64 Linux, but not necessary anywhere else. // REQUIRES: x86_64-supported-target,i386-supported-target -// RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: %clangxx_asan -O0 %p/SharedLibs/dlclose-test-so.cc \ // RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: %clangxx_asan -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %p/SharedLibs/dlclose-test-so.cc \ // RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %p/SharedLibs/dlclose-test-so.cc \ // RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %p/SharedLibs/dlclose-test-so.cc \ // RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %p/SharedLibs/dlclose-test-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %p/SharedLibs/dlclose-test-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %p/SharedLibs/dlclose-test-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %p/SharedLibs/dlclose-test-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t 2>&1 | FileCheck %s #include <assert.h> #include <dlfcn.h> diff --git a/lib/asan/lit_tests/TestCases/double-free.cc b/lib/asan/lit_tests/TestCases/double-free.cc new file mode 100644 index 000000000000..6bfd4fa2c7e5 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/double-free.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_asan -O0 %s -o %t 2>&1 +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=MALLOC-CTX + +// Also works if no malloc context is available. +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char *x = (char*)malloc(10 * sizeof(char)); + memset(x, 0, 10); + int res = x[argc]; + free(x); + free(x + argc - 1); // BOOM + // CHECK: AddressSanitizer: attempting double-free{{.*}}in thread T0 + // CHECK: #0 0x{{.*}} in {{.*}}free + // CHECK: #1 0x{{.*}} in main {{.*}}double-free.cc:[[@LINE-3]] + // CHECK: freed by thread T0 here: + // MALLOC-CTX: #0 0x{{.*}} in {{.*}}free + // MALLOC-CTX: #1 0x{{.*}} in main {{.*}}double-free.cc:[[@LINE-7]] + // CHECK: allocated by thread T0 here: + // MALLOC-CTX: double-free.cc:[[@LINE-12]] + return res; +} diff --git a/lib/asan/lit_tests/force_inline_opt0.cc b/lib/asan/lit_tests/TestCases/force_inline_opt0.cc index 955ce38156fb..775a66dfe2fe 100644 --- a/lib/asan/lit_tests/force_inline_opt0.cc +++ b/lib/asan/lit_tests/TestCases/force_inline_opt0.cc @@ -1,7 +1,7 @@ // This test checks that we are no instrumenting a memory access twice // (before and after inlining) -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t +// RUN: %clangxx_asan -O1 %s -o %t && %t +// RUN: %clangxx_asan -O0 %s -o %t && %t __attribute__((always_inline)) void foo(int *x) { *x = 0; diff --git a/lib/asan/lit_tests/TestCases/free_hook_realloc.cc b/lib/asan/lit_tests/TestCases/free_hook_realloc.cc new file mode 100644 index 000000000000..7a71964b0032 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/free_hook_realloc.cc @@ -0,0 +1,32 @@ +// Check that free hook doesn't conflict with Realloc. +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <unistd.h> + +static void *glob_ptr; + +extern "C" { +void __asan_free_hook(void *ptr) { + if (ptr == glob_ptr) { + *(int*)ptr = 0; + write(1, "FreeHook\n", sizeof("FreeHook\n")); + } +} +} + +int main() { + int *x = (int*)malloc(100); + x[0] = 42; + glob_ptr = x; + int *y = (int*)realloc(x, 200); + // Verify that free hook was called and didn't spoil the memory. + if (y[0] != 42) { + _exit(1); + } + write(1, "Passed\n", sizeof("Passed\n")); + free(y); + // CHECK: FreeHook + // CHECK: Passed + return 0; +} diff --git a/lib/asan/lit_tests/global-demangle.cc b/lib/asan/lit_tests/TestCases/global-demangle.cc index 5696a38a7705..d050b70f0fca 100644 --- a/lib/asan/lit_tests/global-demangle.cc +++ b/lib/asan/lit_tests/TestCases/global-demangle.cc @@ -1,5 +1,4 @@ -// Don't run through %symbolize to avoid c++filt demangling. -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s namespace XXX { class YYY { diff --git a/lib/asan/lit_tests/TestCases/global-overflow.cc b/lib/asan/lit_tests/TestCases/global-overflow.cc new file mode 100644 index 000000000000..0f080f55f2f1 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/global-overflow.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <string.h> +int main(int argc, char **argv) { + static char XXX[10]; + static char YYY[10]; + static char ZZZ[10]; + memset(XXX, 0, 10); + memset(YYY, 0, 10); + memset(ZZZ, 0, 10); + int res = YYY[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*global-overflow.cc:}}[[@LINE-2]] + // CHECK: {{0x.* is located 0 bytes to the right of global variable}} + // CHECK: {{.*YYY.* of size 10}} + res += XXX[argc] + ZZZ[argc]; + return res; +} diff --git a/lib/asan/lit_tests/TestCases/heap-overflow.cc b/lib/asan/lit_tests/TestCases/heap-overflow.cc new file mode 100644 index 000000000000..2c943a36014f --- /dev/null +++ b/lib/asan/lit_tests/TestCases/heap-overflow.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char *x = (char*)malloc(10 * sizeof(char)); + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*heap-overflow.cc:}}[[@LINE-2]] + // CHECK: {{0x.* is located 0 bytes to the right of 10-byte region}} + // CHECK: {{allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*heap-overflow.cc:9}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*heap-overflow.cc:9}} + free(x); + return res; +} diff --git a/lib/asan/lit_tests/huge_negative_hea_oob.cc b/lib/asan/lit_tests/TestCases/huge_negative_hea_oob.cc index a09e3bf87d60..58a44c5fb68e 100644 --- a/lib/asan/lit_tests/huge_negative_hea_oob.cc +++ b/lib/asan/lit_tests/TestCases/huge_negative_hea_oob.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_asan -m64 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -m64 -O %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O %s -o %t && not %t 2>&1 | FileCheck %s // Check that we can find huge buffer overflows to the left. #include <stdlib.h> #include <string.h> diff --git a/lib/asan/lit_tests/init-order-atexit.cc b/lib/asan/lit_tests/TestCases/init-order-atexit.cc index 45f4f17c0cb0..e38cdd273d58 100644 --- a/lib/asan/lit_tests/init-order-atexit.cc +++ b/lib/asan/lit_tests/TestCases/init-order-atexit.cc @@ -4,8 +4,8 @@ // (3) destructor of A reads uninitialized global C from another module. // We do *not* want to report init-order bug in this case. -// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/init-order-atexit-extra.cc -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s %p/Helpers/init-order-atexit-extra.cc -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true not %t 2>&1 | FileCheck %s #include <stdio.h> #include <stdlib.h> diff --git a/lib/asan/lit_tests/init-order-dlopen.cc b/lib/asan/lit_tests/TestCases/init-order-dlopen.cc index 228f44204c99..d30d11999246 100644 --- a/lib/asan/lit_tests/init-order-dlopen.cc +++ b/lib/asan/lit_tests/TestCases/init-order-dlopen.cc @@ -1,14 +1,18 @@ // Regression test for // https://code.google.com/p/address-sanitizer/issues/detail?id=178 -// RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/init-order-dlopen-so.cc \ -// RUN: -fPIC -shared -o %t-so.so +// Assume we're on Darwin and try to pass -U to the linker. If this flag is +// unsupported, don't use it. +// RUN: %clangxx_asan -O0 %p/SharedLibs/init-order-dlopen-so.cc \ +// RUN: -fPIC -shared -o %t-so.so -Wl,-U,_inc_global || \ +// RUN: %clangxx_asan -O0 %p/SharedLibs/init-order-dlopen-so.cc \ +// RUN: -fPIC -shared -o %t-so.so // If the linker doesn't support --export-dynamic (which is ELF-specific), // try to link without that option. // FIXME: find a better solution. -// RUN: %clangxx_asan -m64 -O0 %s -o %t -Wl,--export-dynamic || \ -// RUN: %clangxx_asan -m64 -O0 %s -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t -Wl,--export-dynamic || \ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true %t 2>&1 | FileCheck %s #include <dlfcn.h> #include <pthread.h> #include <stdio.h> @@ -24,6 +28,7 @@ int foo() { int global = foo(); __attribute__((visibility("default"))) +extern "C" void inc_global() { global++; } diff --git a/lib/asan/lit_tests/TestCases/init-order-pthread-create.cc b/lib/asan/lit_tests/TestCases/init-order-pthread-create.cc new file mode 100644 index 000000000000..52031216d5bb --- /dev/null +++ b/lib/asan/lit_tests/TestCases/init-order-pthread-create.cc @@ -0,0 +1,32 @@ +// Check that init-order checking is properly disabled if pthread_create is +// called. + +// RUN: %clangxx_asan %s %p/Helpers/init-order-pthread-create-extra.cc -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true %t + +#include <stdio.h> +#include <pthread.h> + +void *run(void *arg) { + return arg; +} + +void *foo(void *input) { + pthread_t t; + pthread_create(&t, 0, run, input); + void *res; + pthread_join(t, &res); + return res; +} + +void *bar(void *input) { + return input; +} + +void *glob = foo((void*)0x1234); +extern void *glob2; + +int main() { + printf("%p %p\n", glob, glob2); + return 0; +} diff --git a/lib/asan/lit_tests/TestCases/initialization-blacklist.cc b/lib/asan/lit_tests/TestCases/initialization-blacklist.cc new file mode 100644 index 000000000000..f40fcc082f9a --- /dev/null +++ b/lib/asan/lit_tests/TestCases/initialization-blacklist.cc @@ -0,0 +1,32 @@ +// Test for blacklist functionality of initialization-order checker. + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 + +// Function is defined in another TU. +int readBadGlobal(); +int x = readBadGlobal(); // init-order bug. + +// Function is defined in another TU. +int accessBadObject(); +int y = accessBadObject(); // init-order bug. + +int readBadSrcGlobal(); +int z = readBadSrcGlobal(); // init-order bug. + +int main(int argc, char **argv) { + return argc + x + y + z - 1; +} diff --git a/lib/asan/lit_tests/initialization-bug.cc b/lib/asan/lit_tests/TestCases/initialization-bug.cc index ee2c725f0b13..fb289b1c7ebe 100644 --- a/lib/asan/lit_tests/initialization-bug.cc +++ b/lib/asan/lit_tests/TestCases/initialization-bug.cc @@ -1,11 +1,7 @@ // Test to make sure basic initialization order errors are caught. -// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 \ -// RUN: | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 \ -// RUN: | %symbolize | FileCheck %s +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true not %t 2>&1 | FileCheck %s // Do not test with optimization -- the error may be optimized away. diff --git a/lib/asan/lit_tests/TestCases/initialization-constexpr.cc b/lib/asan/lit_tests/TestCases/initialization-constexpr.cc new file mode 100644 index 000000000000..65c95edd5081 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/initialization-constexpr.cc @@ -0,0 +1,31 @@ +// Constexpr: +// We need to check that a global variable initialized with a constexpr +// constructor can be accessed during dynamic initialization (as a constexpr +// constructor implies that it was initialized during constant initialization, +// not dynamic initialization). + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O3 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 + +class Integer { + private: + int value; + + public: + constexpr Integer(int x = 0) : value(x) {} + int getValue() {return value;} +}; +Integer coolestInteger(42); +int getCoolestInteger() { return coolestInteger.getValue(); } + +int main() { return 0; } diff --git a/lib/asan/lit_tests/initialization-nobug.cc b/lib/asan/lit_tests/TestCases/initialization-nobug.cc index 407226e29a1b..ed37d137f8cb 100644 --- a/lib/asan/lit_tests/initialization-nobug.cc +++ b/lib/asan/lit_tests/TestCases/initialization-nobug.cc @@ -1,21 +1,13 @@ // A collection of various initializers which shouldn't trip up initialization // order checking. If successful, this will just return 0. -// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t // RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t // RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t // RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m64 -O3 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m32 -O3 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: %clangxx_asan -O3 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t // RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 // Simple access: diff --git a/lib/asan/lit_tests/TestCases/inline.cc b/lib/asan/lit_tests/TestCases/inline.cc new file mode 100644 index 000000000000..792aff59f4ba --- /dev/null +++ b/lib/asan/lit_tests/TestCases/inline.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O3 %s -o %t && %t + +// Test that no_sanitize_address attribute applies even when the function would +// be normally inlined. + +#include <stdlib.h> + +__attribute__((no_sanitize_address)) +int f(int *p) { + return *p; // BOOOM?? Nope! +} + +int main(int argc, char **argv) { + int * volatile x = (int*)malloc(2*sizeof(int) + 2); + int res = f(x + 2); + if (res) + exit(0); + return 0; +} diff --git a/lib/asan/lit_tests/TestCases/interface_test.cc b/lib/asan/lit_tests/TestCases/interface_test.cc new file mode 100644 index 000000000000..297b5526e9d9 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/interface_test.cc @@ -0,0 +1,8 @@ +// Check that user may include ASan interface header. +// RUN: %clang_asan %s -o %t && %t +// RUN: %clang %s -o %t && %t +#include <sanitizer/asan_interface.h> + +int main() { + return 0; +} diff --git a/lib/asan/lit_tests/invalid-free.cc b/lib/asan/lit_tests/TestCases/invalid-free.cc index 0ef064056b63..f940b501279a 100644 --- a/lib/asan/lit_tests/invalid-free.cc +++ b/lib/asan/lit_tests/TestCases/invalid-free.cc @@ -1,4 +1,9 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=MALLOC-CTX + +// Also works if no malloc context is available. +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s #include <stdlib.h> #include <string.h> @@ -11,6 +16,6 @@ int main(int argc, char **argv) { // CHECK: invalid-free.cc:[[@LINE-2]] // CHECK: is located 5 bytes inside of 10-byte region // CHECK: allocated by thread T0 here: - // CHECK: invalid-free.cc:[[@LINE-8]] + // MALLOC-CTX: invalid-free.cc:[[@LINE-8]] return res; } diff --git a/lib/asan/lit_tests/TestCases/ioctl.cc b/lib/asan/lit_tests/TestCases/ioctl.cc new file mode 100644 index 000000000000..08ca688d3872 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/ioctl.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O0 -g %s -o %t && ASAN_OPTIONS=handle_ioctl=1 not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 -g %s -o %t && ASAN_OPTIONS=handle_ioctl=1 not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -O0 -g %s -o %t && %t +// RUN: %clangxx_asan -O3 -g %s -o %t && %t + +#include <assert.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <unistd.h> + +int main(int argc, char **argv) { + int fd = socket(AF_INET, SOCK_DGRAM, 0); + + int nonblock; + int res = ioctl(fd, FIONBIO, &nonblock + 1); + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: READ of size 4 at + // CHECK: {{#.* in main .*ioctl.cc:}}[[@LINE-3]] + assert(res == 0); + close(fd); + return 0; +} diff --git a/lib/asan/lit_tests/TestCases/large_func_test.cc b/lib/asan/lit_tests/TestCases/large_func_test.cc new file mode 100644 index 000000000000..0534bcd31142 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/large_func_test.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +#include <stdlib.h> +__attribute__((noinline)) +static void LargeFunction(int *x, int zero) { + x[0]++; + x[1]++; + x[2]++; + x[3]++; + x[4]++; + x[5]++; + x[6]++; + x[7]++; + x[8]++; + x[9]++; + + // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{READ of size 4 at 0x.* thread T0}} + x[zero + 103]++; // we should report this exact line + // atos incorrectly extracts the symbol name for the static functions on + // Darwin. + // CHECK-Linux: {{#0 0x.* in LargeFunction.*large_func_test.cc:}}[[@LINE-3]] + // CHECK-Darwin: {{#0 0x.* in .*LargeFunction.*large_func_test.cc}}:[[@LINE-4]] + + x[10]++; + x[11]++; + x[12]++; + x[13]++; + x[14]++; + x[15]++; + x[16]++; + x[17]++; + x[18]++; + x[19]++; +} + +int main(int argc, char **argv) { + int *x = new int[100]; + LargeFunction(x, argc - 1); + // CHECK: {{ #1 0x.* in main .*large_func_test.cc:}}[[@LINE-1]] + // CHECK: {{0x.* is located 12 bytes to the right of 400-byte region}} + // CHECK: {{allocated by thread T0 here:}} + // CHECK-Linux: {{ #0 0x.* in operator new.*}} + // CHECK-Darwin: {{ #0 0x.* in .*_Zna.*}} + // CHECK: {{ #1 0x.* in main .*large_func_test.cc:}}[[@LINE-7]] + delete x; +} diff --git a/lib/asan/lit_tests/log-path_test.cc b/lib/asan/lit_tests/TestCases/log-path_test.cc index 1072670fbff4..1072670fbff4 100644 --- a/lib/asan/lit_tests/log-path_test.cc +++ b/lib/asan/lit_tests/TestCases/log-path_test.cc diff --git a/lib/asan/lit_tests/log_path_fork_test.cc.disabled b/lib/asan/lit_tests/TestCases/log_path_fork_test.cc.disabled index c6c1b49e994d..c6c1b49e994d 100644 --- a/lib/asan/lit_tests/log_path_fork_test.cc.disabled +++ b/lib/asan/lit_tests/TestCases/log_path_fork_test.cc.disabled diff --git a/lib/asan/lit_tests/TestCases/lsan_annotations.cc b/lib/asan/lit_tests/TestCases/lsan_annotations.cc new file mode 100644 index 000000000000..c55ab8692eb5 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/lsan_annotations.cc @@ -0,0 +1,16 @@ +// Check that LSan annotations work fine. +// RUN: %clangxx_asan -O0 %s -o %t && %t +// RUN: %clangxx_asan -O3 %s -o %t && %t + +#include <sanitizer/lsan_interface.h> +#include <stdlib.h> + +int main() { + int *x = new int; + __lsan_ignore_object(x); + { + __lsan::ScopedDisabler disabler; + double *y = new double; + } + return 0; +} diff --git a/lib/asan/lit_tests/TestCases/malloc_context_size.cc b/lib/asan/lit_tests/TestCases/malloc_context_size.cc new file mode 100644 index 000000000000..266ce66f59e2 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/malloc_context_size.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os +// RUN: ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os +// RUN: ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os + +int main() { + char *x = new char[20]; + delete[] x; + return x[0]; + // We need to keep duplicate lines with different 'CHECK-%os' prefixes, + // otherwise FileCheck barks on missing 'CHECK-%os' before 'CHECK-%os-NEXT'. + + // CHECK-Linux: freed by thread T{{.*}} here: + // CHECK-Linux-NEXT: #0 0x{{.*}} in operator delete[] + // CHECK-Darwin: freed by thread T{{.*}} here: + // CHECK-Darwin-NEXT: #0 0x{{.*}} in wrap__ZdaPv + // CHECK-NOT: #1 0x{{.*}} + + // CHECK-Linux: previously allocated by thread T{{.*}} here: + // CHECK-Linux-NEXT: #0 0x{{.*}} in operator new[] + // CHECK-Darwin: previously allocated by thread T{{.*}} here: + // CHECK-Darwin-NEXT: #0 0x{{.*}} in wrap__Znam + // CHECK-NOT: #1 0x{{.*}} + + // CHECK: SUMMARY: AddressSanitizer: heap-use-after-free +} diff --git a/lib/asan/lit_tests/malloc_fill.cc b/lib/asan/lit_tests/TestCases/malloc_fill.cc index c23516b33299..57f50d1438b7 100644 --- a/lib/asan/lit_tests/malloc_fill.cc +++ b/lib/asan/lit_tests/TestCases/malloc_fill.cc @@ -1,5 +1,5 @@ // Check that we fill malloc-ed memory correctly. -// RUN: %clangxx_asan -m64 %s -o %t +// RUN: %clangxx_asan %s -o %t // RUN: %t | FileCheck %s // RUN: ASAN_OPTIONS=max_malloc_fill_size=10:malloc_fill_byte=8 %t | FileCheck %s --check-prefix=CHECK-10-8 // RUN: ASAN_OPTIONS=max_malloc_fill_size=20:malloc_fill_byte=171 %t | FileCheck %s --check-prefix=CHECK-20-ab diff --git a/lib/asan/lit_tests/malloc_hook.cc b/lib/asan/lit_tests/TestCases/malloc_hook.cc index 6435d105ee26..83be1020ea43 100644 --- a/lib/asan/lit_tests/malloc_hook.cc +++ b/lib/asan/lit_tests/TestCases/malloc_hook.cc @@ -4,19 +4,31 @@ #include <unistd.h> extern "C" { +bool __asan_get_ownership(const void *p); + +void *global_ptr; + // Note: avoid calling functions that allocate memory in malloc/free // to avoid infinite recursion. void __asan_malloc_hook(void *ptr, size_t sz) { - write(1, "MallocHook\n", sizeof("MallocHook\n")); + if (__asan_get_ownership(ptr)) { + write(1, "MallocHook\n", sizeof("MallocHook\n")); + global_ptr = ptr; + } } void __asan_free_hook(void *ptr) { - write(1, "FreeHook\n", sizeof("FreeHook\n")); + if (__asan_get_ownership(ptr) && ptr == global_ptr) + write(1, "FreeHook\n", sizeof("FreeHook\n")); } } // extern "C" int main() { volatile int *x = new int; // CHECK: MallocHook + // Check that malloc hook was called with correct argument. + if (global_ptr != (void*)x) { + _exit(1); + } *x = 0; delete x; // CHECK: FreeHook diff --git a/lib/asan/lit_tests/TestCases/memcmp_strict_test.cc b/lib/asan/lit_tests/TestCases/memcmp_strict_test.cc new file mode 100644 index 000000000000..e06a8c7e9e2f --- /dev/null +++ b/lib/asan/lit_tests/TestCases/memcmp_strict_test.cc @@ -0,0 +1,15 @@ +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=strict_memcmp=0 %t +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=strict_memcmp=1 not %t 2>&1 | FileCheck %s +// Default to strict_memcmp=1. +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> +int main() { + char kFoo[] = "foo"; + char kFubar[] = "fubar"; + int res = memcmp(kFoo, kFubar, strlen(kFubar)); + printf("res: %d\n", res); + // CHECK: AddressSanitizer: stack-buffer-overflow + return 0; +} diff --git a/lib/asan/lit_tests/TestCases/memcmp_test.cc b/lib/asan/lit_tests/TestCases/memcmp_test.cc new file mode 100644 index 000000000000..758311ddc476 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/memcmp_test.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// REQUIRES: compiler-rt-optimized + +#include <string.h> +int main(int argc, char **argv) { + char a1[] = {argc, 2, 3, 4}; + char a2[] = {1, 2*argc, 3, 4}; + int res = memcmp(a1, a2, 4 + argc); // BOOM + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: {{#0.*memcmp}} + // CHECK: {{#1.*main}} + return res; +} diff --git a/lib/asan/lit_tests/TestCases/null_deref.cc b/lib/asan/lit_tests/TestCases/null_deref.cc new file mode 100644 index 000000000000..476418324cc5 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/null_deref.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +__attribute__((noinline)) +static void NullDeref(int *ptr) { + // CHECK: ERROR: AddressSanitizer: SEGV on unknown address + // CHECK: {{0x0*00028 .*pc 0x.*}} + ptr[10]++; // BOOM + // atos on Mac cannot extract the symbol name correctly. + // CHECK-Linux: {{ #0 0x.* in NullDeref.*null_deref.cc:}}[[@LINE-2]] + // CHECK-Darwin: {{ #0 0x.* in .*NullDeref.*null_deref.cc:}}[[@LINE-3]] +} +int main() { + NullDeref((int*)0); + // CHECK: {{ #1 0x.* in main.*null_deref.cc:}}[[@LINE-1]] + // CHECK: {{AddressSanitizer can not provide additional info.}} +} diff --git a/lib/asan/lit_tests/on_error_callback.cc b/lib/asan/lit_tests/TestCases/on_error_callback.cc index bb94d9fb579b..d0cec2eb2703 100644 --- a/lib/asan/lit_tests/on_error_callback.cc +++ b/lib/asan/lit_tests/TestCases/on_error_callback.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s #include <stdio.h> #include <stdlib.h> diff --git a/lib/asan/lit_tests/TestCases/partial_right.cc b/lib/asan/lit_tests/TestCases/partial_right.cc new file mode 100644 index 000000000000..a000a913d6be --- /dev/null +++ b/lib/asan/lit_tests/TestCases/partial_right.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <stdlib.h> +int main(int argc, char **argv) { + volatile int *x = (int*)malloc(2*sizeof(int) + 2); + int res = x[2]; // BOOOM + // CHECK: {{READ of size 4 at 0x.* thread T0}} + // CHECK: [[ADDR:0x[01-9a-fa-f]+]] is located 0 bytes to the right of {{.*}}-byte region [{{.*}},{{.*}}[[ADDR]]) + return res; +} diff --git a/lib/asan/lit_tests/TestCases/poison_partial.cc b/lib/asan/lit_tests/TestCases/poison_partial.cc new file mode 100644 index 000000000000..f7c48bf597b6 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/poison_partial.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s +// RUN: not %t heap 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=poison_partial=0 %t +// RUN: ASAN_OPTIONS=poison_partial=0 %t heap +#include <string.h> +char g[21]; +char *x; + +int main(int argc, char **argv) { + if (argc >= 2) + x = new char[21]; + else + x = &g[0]; + memset(x, 0, 21); + int *y = (int*)x; + return y[5]; +} +// CHECK: 0 bytes to the right diff --git a/lib/asan/lit_tests/TestCases/print_summary.cc b/lib/asan/lit_tests/TestCases/print_summary.cc new file mode 100644 index 000000000000..949c9b54f8a3 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/print_summary.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=YES +// RUN: ASAN_OPTIONS=print_summary=false not %t 2>&1 | FileCheck %s --check-prefix=NO + +int main() { + char *x = new char[20]; + delete[] x; + return x[0]; + // YES: ERROR: AddressSanitizer: heap-use-after-free + // YES: SUMMARY: AddressSanitizer: heap-use-after-free + // NO: ERROR: AddressSanitizer: heap-use-after-free + // NO-NOT: SUMMARY: AddressSanitizer: heap-use-after-free +} + diff --git a/lib/asan/lit_tests/TestCases/readv.cc b/lib/asan/lit_tests/TestCases/readv.cc new file mode 100644 index 000000000000..ba17505f37ac --- /dev/null +++ b/lib/asan/lit_tests/TestCases/readv.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t +// RUN: %clangxx_asan -O0 %s -DPOSITIVE -o %t && not %t 2>&1 | FileCheck %s + +// Test the readv() interceptor. + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/uio.h> +#include <time.h> + +int main() { + char buf[2011]; + struct iovec iov[2]; +#ifdef POSITIVE + char * volatile buf_ = buf; + iov[0].iov_base = buf_ - 1; +#else + iov[0].iov_base = buf + 1; +#endif + iov[0].iov_len = 5; + iov[1].iov_base = buf + 10; + iov[1].iov_len = 2000; + int fd = open("/etc/hosts", O_RDONLY); + assert(fd > 0); + readv(fd, iov, 2); + // CHECK: WRITE of size 5 at + close(fd); + return 0; +} diff --git a/lib/asan/lit_tests/sanity_check_pure_c.c b/lib/asan/lit_tests/TestCases/sanity_check_pure_c.c index 3d830653e33e..df150675bd97 100644 --- a/lib/asan/lit_tests/sanity_check_pure_c.c +++ b/lib/asan/lit_tests/TestCases/sanity_check_pure_c.c @@ -1,10 +1,10 @@ // Sanity checking a test in pure C. -// RUN: %clang -g -fsanitize=address -O2 %s -o %t -// RUN: %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clang_asan -O2 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s // Sanity checking a test in pure C with -pie. -// RUN: %clang -g -fsanitize=address -O2 %s -pie -o %t -// RUN: %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clang_asan -O2 %s -pie -o %t +// RUN: not %t 2>&1 | FileCheck %s #include <stdlib.h> int main() { diff --git a/lib/asan/lit_tests/TestCases/shared-lib-test.cc b/lib/asan/lit_tests/TestCases/shared-lib-test.cc new file mode 100644 index 000000000000..126903a55866 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/shared-lib-test.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_asan -O0 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> + +#include <string> + +using std::string; + +typedef void (fun_t)(int x); + +int main(int argc, char *argv[]) { + string path = string(argv[0]) + "-so.so"; + printf("opening %s ... \n", path.c_str()); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + fun_t *inc = (fun_t*)dlsym(lib, "inc"); + if (!inc) return 1; + printf("ok\n"); + inc(1); + inc(-1); // BOOM + // CHECK: {{.*ERROR: AddressSanitizer: global-buffer-overflow}} + // CHECK: {{READ of size 4 at 0x.* thread T0}} + // CHECK: {{ #0 0x.*}} + // CHECK: {{ #1 0x.* in main .*shared-lib-test.cc:}}[[@LINE-4]] + return 0; +} diff --git a/lib/asan/lit_tests/sleep_before_dying.c b/lib/asan/lit_tests/TestCases/sleep_before_dying.c index df9eba276039..8dee9f2778ce 100644 --- a/lib/asan/lit_tests/sleep_before_dying.c +++ b/lib/asan/lit_tests/TestCases/sleep_before_dying.c @@ -1,5 +1,5 @@ -// RUN: %clang -g -fsanitize=address -O2 %s -o %t -// RUN: ASAN_OPTIONS="sleep_before_dying=1" %t 2>&1 | FileCheck %s +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS="sleep_before_dying=1" not %t 2>&1 | FileCheck %s #include <stdlib.h> int main() { diff --git a/lib/asan/lit_tests/TestCases/stack-buffer-overflow-with-position.cc b/lib/asan/lit_tests/TestCases/stack-buffer-overflow-with-position.cc new file mode 100644 index 000000000000..91820db0142a --- /dev/null +++ b/lib/asan/lit_tests/TestCases/stack-buffer-overflow-with-position.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: not %t -2 2>&1 | FileCheck --check-prefix=CHECK-m2 %s +// RUN: not %t -1 2>&1 | FileCheck --check-prefix=CHECK-m1 %s +// RUN: %t 0 +// RUN: %t 8 +// RUN: not %t 9 2>&1 | FileCheck --check-prefix=CHECK-9 %s +// RUN: not %t 10 2>&1 | FileCheck --check-prefix=CHECK-10 %s +// RUN: not %t 62 2>&1 | FileCheck --check-prefix=CHECK-62 %s +// RUN: not %t 63 2>&1 | FileCheck --check-prefix=CHECK-63 %s +// RUN: not %t 63 2>&1 | FileCheck --check-prefix=CHECK-63 %s +// RUN: not %t 73 2>&1 | FileCheck --check-prefix=CHECK-73 %s +// RUN: not %t 74 2>&1 | FileCheck --check-prefix=CHECK-74 %s +// RUN: not %t 126 2>&1 | FileCheck --check-prefix=CHECK-126 %s +// RUN: not %t 127 2>&1 | FileCheck --check-prefix=CHECK-127 %s +// RUN: not %t 137 2>&1 | FileCheck --check-prefix=CHECK-137 %s +// RUN: not %t 138 2>&1 | FileCheck --check-prefix=CHECK-138 %s +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +int main(int argc, char **argv) { + assert(argc >= 2); + int idx = atoi(argv[1]); + char AAA[10], BBB[10], CCC[10]; + memset(AAA, 0, sizeof(AAA)); + memset(BBB, 0, sizeof(BBB)); + memset(CCC, 0, sizeof(CCC)); + int res = 0; + char *p = AAA + idx; + printf("AAA: %p\ny: %p\nz: %p\np: %p\n", AAA, BBB, CCC, p); + // make sure BBB and CCC are not removed; + return *(short*)(p) + BBB[argc % 2] + CCC[argc % 2]; +} +// CHECK-m2: 'AAA' <== Memory access at offset 30 underflows this variable +// CHECK-m1: 'AAA' <== Memory access at offset 31 partially underflows this variable +// CHECK-9: 'AAA' <== Memory access at offset 41 partially overflows this variable +// CHECK-10: 'AAA' <== Memory access at offset 42 overflows this variable +// CHECK-62: 'BBB' <== Memory access at offset 94 underflows this variable +// CHECK-63: 'BBB' <== Memory access at offset 95 partially underflows this variable +// CHECK-73: 'BBB' <== Memory access at offset 105 partially overflows this variable +// CHECK-74: 'BBB' <== Memory access at offset 106 overflows this variable +// CHECK-126: 'CCC' <== Memory access at offset 158 underflows this variable +// CHECK-127: 'CCC' <== Memory access at offset 159 partially underflows this variable +// CHECK-137: 'CCC' <== Memory access at offset 169 partially overflows this variable +// CHECK-138: 'CCC' <== Memory access at offset 170 overflows this variable diff --git a/lib/asan/lit_tests/stack-frame-demangle.cc b/lib/asan/lit_tests/TestCases/stack-frame-demangle.cc index bb8de16b2b8a..2b83ecc2923b 100644 --- a/lib/asan/lit_tests/stack-frame-demangle.cc +++ b/lib/asan/lit_tests/TestCases/stack-frame-demangle.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s #include <string.h> diff --git a/lib/asan/lit_tests/stack-oob-frames.cc b/lib/asan/lit_tests/TestCases/stack-oob-frames.cc index 0395522252e8..909e700b3d00 100644 --- a/lib/asan/lit_tests/stack-oob-frames.cc +++ b/lib/asan/lit_tests/TestCases/stack-oob-frames.cc @@ -1,8 +1,8 @@ -// RUN: %clangxx_asan -m64 -O1 %s -o %t -// RUN: %t 0 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK0 -// RUN: %t 1 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK1 -// RUN: %t 2 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK2 -// RUN: %t 3 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK3 +// RUN: %clangxx_asan -O1 %s -o %t +// RUN: not %t 0 2>&1 | FileCheck %s --check-prefix=CHECK0 +// RUN: not %t 1 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: not %t 2 2>&1 | FileCheck %s --check-prefix=CHECK2 +// RUN: not %t 3 2>&1 | FileCheck %s --check-prefix=CHECK3 #define NOINLINE __attribute__((noinline)) inline void break_optimization(void *arg) { diff --git a/lib/asan/lit_tests/TestCases/stack-overflow.cc b/lib/asan/lit_tests/TestCases/stack-overflow.cc new file mode 100644 index 000000000000..adf1c0784f70 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/stack-overflow.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <string.h> +int main(int argc, char **argv) { + char x[10]; + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*stack-overflow.cc:}}[[@LINE-2]] + // CHECK: {{Address 0x.* is located in stack of thread T0 at offset}} + // CHECK-NEXT: in{{.*}}main{{.*}}stack-overflow.cc + return res; +} diff --git a/lib/asan/lit_tests/TestCases/stack-use-after-return.cc b/lib/asan/lit_tests/TestCases/stack-use-after-return.cc new file mode 100644 index 000000000000..5ed42a8c0c97 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/stack-use-after-return.cc @@ -0,0 +1,77 @@ +// RUN: export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O0 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=detect_stack_use_after_return=0 %t +// Regression test for a CHECK failure with small stack size and large frame. +// RUN: %clangxx_asan -O3 %s -o %t -DkSize=10000 && \ +// RUN: (ulimit -s 65; not %t) 2>&1 | FileCheck %s +// +// Test that we can find UAR in a thread other than main: +// RUN: %clangxx_asan -DUseThread -O2 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck --check-prefix=THREAD %s +// +// Test the uar_stack_size_log flag. +// +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:uar_stack_size_log=20:verbosity=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-20 %s +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:uar_stack_size_log=24:verbosity=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-24 %s + +#include <stdio.h> +#include <pthread.h> + +#ifndef kSize +# define kSize 1 +#endif + +#ifndef UseThread +# define UseThread 0 +#endif + +__attribute__((noinline)) +char *Ident(char *x) { + fprintf(stderr, "1: %p\n", x); + return x; +} + +__attribute__((noinline)) +char *Func1() { + char local[kSize]; + return Ident(local); +} + +__attribute__((noinline)) +void Func2(char *x) { + fprintf(stderr, "2: %p\n", x); + *x = 1; + // CHECK: WRITE of size 1 {{.*}} thread T0 + // CHECK: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-2]] + // CHECK: is located in stack of thread T0 at offset + // CHECK: 'local' <== Memory access at offset 32 is inside this variable + // THREAD: WRITE of size 1 {{.*}} thread T{{[1-9]}} + // THREAD: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-6]] + // THREAD: is located in stack of thread T{{[1-9]}} at offset + // THREAD: 'local' <== Memory access at offset 32 is inside this variable + // CHECK-20: T0: FakeStack created:{{.*}} stack_size_log: 20 + // CHECK-24: T0: FakeStack created:{{.*}} stack_size_log: 24 +} + +void *Thread(void *unused) { + Func2(Func1()); + return NULL; +} + +int main(int argc, char **argv) { +#if UseThread + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); +#else + Func2(Func1()); +#endif + return 0; +} diff --git a/lib/asan/lit_tests/TestCases/strdup_oob_test.cc b/lib/asan/lit_tests/TestCases/strdup_oob_test.cc new file mode 100644 index 000000000000..e92afd3caaf9 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/strdup_oob_test.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <string.h> + +char kString[] = "foo"; + +int main(int argc, char **argv) { + char *copy = strdup(kString); + int x = copy[4 + argc]; // BOOM + // CHECK: AddressSanitizer: heap-buffer-overflow + // CHECK: #0 {{.*}}main {{.*}}strdup_oob_test.cc:[[@LINE-2]] + // CHECK: allocated by thread T{{.*}} here: + // CHECK: #0 {{.*}}strdup + // CHECK: strdup_oob_test.cc:[[@LINE-6]] + return x; +} diff --git a/lib/asan/lit_tests/TestCases/strerror_r_test.cc b/lib/asan/lit_tests/TestCases/strerror_r_test.cc new file mode 100644 index 000000000000..0df009b83f9c --- /dev/null +++ b/lib/asan/lit_tests/TestCases/strerror_r_test.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t + +// Regression test for PR17138. + +#include <assert.h> +#include <string.h> + +int main() { + char buf[1024]; + char *res = (char *)strerror_r(300, buf, sizeof(buf)); + assert(res != 0); + return 0; +} diff --git a/lib/asan/lit_tests/strip_path_prefix.c b/lib/asan/lit_tests/TestCases/strip_path_prefix.c index ef7bf98ab3c2..c4d6ba49d5e3 100644 --- a/lib/asan/lit_tests/strip_path_prefix.c +++ b/lib/asan/lit_tests/TestCases/strip_path_prefix.c @@ -1,5 +1,5 @@ -// RUN: %clang -g -fsanitize=address -O2 %s -o %t -// RUN: ASAN_OPTIONS="strip_path_prefix='/'" %t 2>&1 | FileCheck %s +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS="strip_path_prefix='/'" not %t 2>&1 | FileCheck %s #include <stdlib.h> int main() { diff --git a/lib/asan/lit_tests/TestCases/strncpy-overflow.cc b/lib/asan/lit_tests/TestCases/strncpy-overflow.cc new file mode 100644 index 000000000000..f91e191fde23 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/strncpy-overflow.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +// REQUIRES: compiler-rt-optimized + +#include <string.h> +#include <stdlib.h> +int main(int argc, char **argv) { + char *hello = (char*)malloc(6); + strcpy(hello, "hello"); + char *short_buffer = (char*)malloc(9); + strncpy(short_buffer, hello, 10); // BOOM + // CHECK: {{WRITE of size 10 at 0x.* thread T0}} + // CHECK-Linux: {{ #0 0x.* in .*strncpy}} + // CHECK-Darwin: {{ #0 0x.* in wrap_strncpy}} + // CHECK: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-4]] + // CHECK: {{0x.* is located 0 bytes to the right of 9-byte region}} + // CHECK: {{allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-10]] + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-13]] + return short_buffer[8]; +} diff --git a/lib/asan/lit_tests/symbolize_callback.cc b/lib/asan/lit_tests/TestCases/symbolize_callback.cc index 0691d501e24d..058b3150fbba 100644 --- a/lib/asan/lit_tests/symbolize_callback.cc +++ b/lib/asan/lit_tests/TestCases/symbolize_callback.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s #include <stdio.h> #include <stdlib.h> diff --git a/lib/asan/lit_tests/throw_call_test.cc b/lib/asan/lit_tests/TestCases/throw_call_test.cc index 974bc51d97c5..974bc51d97c5 100644 --- a/lib/asan/lit_tests/throw_call_test.cc +++ b/lib/asan/lit_tests/TestCases/throw_call_test.cc diff --git a/lib/asan/lit_tests/throw_invoke_test.cc b/lib/asan/lit_tests/TestCases/throw_invoke_test.cc index 077a940e8d19..077a940e8d19 100644 --- a/lib/asan/lit_tests/throw_invoke_test.cc +++ b/lib/asan/lit_tests/TestCases/throw_invoke_test.cc diff --git a/lib/asan/lit_tests/time_interceptor.cc b/lib/asan/lit_tests/TestCases/time_interceptor.cc index f5f2ad62b815..3be00d60c01d 100644 --- a/lib/asan/lit_tests/time_interceptor.cc +++ b/lib/asan/lit_tests/TestCases/time_interceptor.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s // Test the time() interceptor. diff --git a/lib/asan/lit_tests/TestCases/uar_and_exceptions.cc b/lib/asan/lit_tests/TestCases/uar_and_exceptions.cc new file mode 100644 index 000000000000..c967531c2c02 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/uar_and_exceptions.cc @@ -0,0 +1,40 @@ +// Test that use-after-return works with exceptions. +// export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O0 %s -o %t && %t + +#include <stdio.h> + +volatile char *g; + +#ifndef FRAME_SIZE +# define FRAME_SIZE 100 +#endif + +#ifndef NUM_ITER +# define NUM_ITER 4000 +#endif + +#ifndef DO_THROW +# define DO_THROW 1 +#endif + +void Func(int depth) { + char frame[FRAME_SIZE]; + g = &frame[0]; + if (depth) + Func(depth - 1); + else if (DO_THROW) + throw 1; +} + +int main(int argc, char **argv) { + for (int i = 0; i < NUM_ITER; i++) { + try { + Func(argc * 100); + } catch(...) { + } + if ((i % (NUM_ITER / 10)) == 0) + fprintf(stderr, "done [%d]\n", i); + } + return 0; +} diff --git a/lib/asan/lit_tests/unaligned_loads_and_stores.cc b/lib/asan/lit_tests/TestCases/unaligned_loads_and_stores.cc index bcae089b427b..d50566c44e9a 100644 --- a/lib/asan/lit_tests/unaligned_loads_and_stores.cc +++ b/lib/asan/lit_tests/TestCases/unaligned_loads_and_stores.cc @@ -1,15 +1,15 @@ -// RUN: %clangxx_asan -O0 -I %p/../../../include %s -o %t -// RUN: %t A 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-A %s -// RUN: %t B 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-B %s -// RUN: %t C 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-C %s -// RUN: %t D 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-D %s -// RUN: %t E 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-E %s +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t A 2>&1 | FileCheck --check-prefix=CHECK-A %s +// RUN: not %t B 2>&1 | FileCheck --check-prefix=CHECK-B %s +// RUN: not %t C 2>&1 | FileCheck --check-prefix=CHECK-C %s +// RUN: not %t D 2>&1 | FileCheck --check-prefix=CHECK-D %s +// RUN: not %t E 2>&1 | FileCheck --check-prefix=CHECK-E %s -// RUN: %t K 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-K %s -// RUN: %t L 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-L %s -// RUN: %t M 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-M %s -// RUN: %t N 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-N %s -// RUN: %t O 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-O %s +// RUN: not %t K 2>&1 | FileCheck --check-prefix=CHECK-K %s +// RUN: not %t L 2>&1 | FileCheck --check-prefix=CHECK-L %s +// RUN: not %t M 2>&1 | FileCheck --check-prefix=CHECK-M %s +// RUN: not %t N 2>&1 | FileCheck --check-prefix=CHECK-N %s +// RUN: not %t O 2>&1 | FileCheck --check-prefix=CHECK-O %s #include <sanitizer/asan_interface.h> diff --git a/lib/asan/lit_tests/TestCases/use-after-free-right.cc b/lib/asan/lit_tests/TestCases/use-after-free-right.cc new file mode 100644 index 000000000000..88d91f53d24e --- /dev/null +++ b/lib/asan/lit_tests/TestCases/use-after-free-right.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +// Test use-after-free report in the case when access is at the right border of +// the allocation. + +#include <stdlib.h> +int main() { + volatile char *x = (char*)malloc(sizeof(char)); + free((void*)x); + *x = 42; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{WRITE of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*use-after-free-right.cc:13}} + // CHECK: {{0x.* is located 0 bytes inside of 1-byte region .0x.*,0x.*}} + // CHECK: {{freed by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*free}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:12}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_free}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free-right.cc:12}} + + // CHECK: {{previously allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:11}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free-right.cc:11}} +} diff --git a/lib/asan/lit_tests/TestCases/use-after-free.cc b/lib/asan/lit_tests/TestCases/use-after-free.cc new file mode 100644 index 000000000000..84ba479c1393 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/use-after-free.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*use-after-free.cc:10}} + // CHECK: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}} + // CHECK: {{freed by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*free}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:9}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_free}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free.cc:9}} + + // CHECK: {{previously allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:8}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free.cc:8}} +} diff --git a/lib/asan/lit_tests/use-after-poison.cc b/lib/asan/lit_tests/TestCases/use-after-poison.cc index d87342900245..e3bc6ecee7f2 100644 --- a/lib/asan/lit_tests/use-after-poison.cc +++ b/lib/asan/lit_tests/TestCases/use-after-poison.cc @@ -1,5 +1,5 @@ // Check that __asan_poison_memory_region works. -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s // // Check that we can disable it // RUN: ASAN_OPTIONS=allow_user_poisoning=0 %t diff --git a/lib/asan/lit_tests/TestCases/use-after-scope-dtor-order.cc b/lib/asan/lit_tests/TestCases/use-after-scope-dtor-order.cc new file mode 100644 index 000000000000..32fa6ad8a464 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/use-after-scope-dtor-order.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +#include <stdio.h> + +struct IntHolder { + explicit IntHolder(int *val = 0) : val_(val) { } + ~IntHolder() { + printf("Value: %d\n", *val_); // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: #0 0x{{.*}} in IntHolder::~IntHolder{{.*}}use-after-scope-dtor-order.cc:[[@LINE-2]] + } + void set(int *val) { val_ = val; } + int *get() { return val_; } + + int *val_; +}; + +int main(int argc, char *argv[]) { + // It is incorrect to use "x" int IntHolder destructor, because "x" is + // "destroyed" earlier as it's declared later. + IntHolder holder; + int x = argc; + holder.set(&x); + return 0; +} diff --git a/lib/asan/lit_tests/use-after-scope-inlined.cc b/lib/asan/lit_tests/TestCases/use-after-scope-inlined.cc index 5c121ea187eb..0bad048e3b8f 100644 --- a/lib/asan/lit_tests/use-after-scope-inlined.cc +++ b/lib/asan/lit_tests/TestCases/use-after-scope-inlined.cc @@ -2,10 +2,7 @@ // happens. "always_inline" is not enough, as Clang doesn't emit // llvm.lifetime intrinsics at -O0. // -// RUN: %clangxx_asan -m64 -O2 -fsanitize=use-after-scope %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 -fsanitize=use-after-scope %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s +// RUN: %clangxx_asan -O2 -fsanitize=use-after-scope %s -o %t && not %t 2>&1 | FileCheck %s int *arr; @@ -21,7 +18,7 @@ int main(int argc, char *argv[]) { return arr[argc - 1]; // BOOM // CHECK: ERROR: AddressSanitizer: stack-use-after-scope // CHECK: READ of size 4 at 0x{{.*}} thread T0 - // CHECK: #0 0x{{.*}} in {{_?}}main + // CHECK: #0 0x{{.*}} in main // CHECK: {{.*}}use-after-scope-inlined.cc:[[@LINE-4]] // CHECK: Address 0x{{.*}} is located in stack of thread T0 at offset // CHECK: [[OFFSET:[^ ]*]] in frame diff --git a/lib/asan/lit_tests/TestCases/use-after-scope-nobug.cc b/lib/asan/lit_tests/TestCases/use-after-scope-nobug.cc new file mode 100644 index 000000000000..c23acf76eaee --- /dev/null +++ b/lib/asan/lit_tests/TestCases/use-after-scope-nobug.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && %t + +#include <stdio.h> + +int main() { + int *p = 0; + // Variable goes in and out of scope. + for (int i = 0; i < 3; i++) { + int x = 0; + p = &x; + } + printf("PASSED\n"); + return 0; +} diff --git a/lib/asan/lit_tests/TestCases/use-after-scope-temp.cc b/lib/asan/lit_tests/TestCases/use-after-scope-temp.cc new file mode 100644 index 000000000000..13d714f9def7 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/use-after-scope-temp.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ +// RUN: %t 2>&1 | FileCheck %s +// +// Lifetime for temporaries is not emitted yet. +// XFAIL: * + +#include <stdio.h> + +struct IntHolder { + explicit IntHolder(int val) : val(val) { + printf("IntHolder: %d\n", val); + } + int val; +}; + +const IntHolder *saved; + +void save(const IntHolder &holder) { + saved = &holder; +} + +int main(int argc, char *argv[]) { + save(IntHolder(10)); + int x = saved->val; // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope-temp.cc:[[@LINE-2]] + printf("saved value: %d\n", x); + return 0; +} diff --git a/lib/asan/lit_tests/TestCases/use-after-scope.cc b/lib/asan/lit_tests/TestCases/use-after-scope.cc new file mode 100644 index 000000000000..c46c9594c314 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/use-after-scope.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS="detect_stack_use_after_return=1" not %t 2>&1 | FileCheck %s + +int main() { + int *p = 0; + { + int x = 0; + p = &x; + } + return *p; // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope.cc:[[@LINE-2]] + // CHECK: Address 0x{{.*}} is located in stack of thread T{{.*}} at offset [[OFFSET:[^ ]+]] in frame + // {{\[}}[[OFFSET]], {{[0-9]+}}) 'x' +} diff --git a/lib/asan/lit_tests/TestCases/wait.cc b/lib/asan/lit_tests/TestCases/wait.cc new file mode 100644 index 000000000000..b5580dc88675 --- /dev/null +++ b/lib/asan/lit_tests/TestCases/wait.cc @@ -0,0 +1,63 @@ +// RUN: %clangxx_asan -DWAIT -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAITPID -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAITPID -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAITID -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAITID -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT3 -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT3 -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT4 -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT4 -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT3_RUSAGE -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT3_RUSAGE -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT4_RUSAGE -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT4_RUSAGE -O3 %s -o %t && not %t 2>&1 | FileCheck %s + + +#include <assert.h> +#include <sys/wait.h> +#include <unistd.h> + +int main(int argc, char **argv) { + pid_t pid = fork(); + if (pid) { // parent + int x[3]; + int *status = x + argc * 3; + int res; +#if defined(WAIT) + res = wait(status); +#elif defined(WAITPID) + res = waitpid(pid, status, WNOHANG); +#elif defined(WAITID) + siginfo_t *si = (siginfo_t*)(x + argc * 3); + res = waitid(P_ALL, 0, si, WEXITED | WNOHANG); +#elif defined(WAIT3) + res = wait3(status, WNOHANG, NULL); +#elif defined(WAIT4) + res = wait4(pid, status, WNOHANG, NULL); +#elif defined(WAIT3_RUSAGE) || defined(WAIT4_RUSAGE) + struct rusage *ru = (struct rusage*)(x + argc * 3); + int good_status; +# if defined(WAIT3_RUSAGE) + res = wait3(&good_status, WNOHANG, ru); +# elif defined(WAIT4_RUSAGE) + res = wait4(pid, &good_status, WNOHANG, ru); +# endif +#endif + // CHECK: stack-buffer-overflow + // CHECK: {{WRITE of size .* at 0x.* thread T0}} + // CHECK: {{in .*wait}} + // CHECK: {{in main .*wait.cc:}} + // CHECK: is located in stack of thread T0 at offset + // CHECK: {{in main}} + return res != -1; + } + // child + return 0; +} diff --git a/lib/asan/lit_tests/Unit/lit.cfg b/lib/asan/lit_tests/Unit/lit.cfg deleted file mode 100644 index e24361b014e9..000000000000 --- a/lib/asan/lit_tests/Unit/lit.cfg +++ /dev/null @@ -1,26 +0,0 @@ -# -*- Python -*- - -import os - -def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if not attr_value: - lit.fatal("No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) - return attr_value - -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') -compiler_rt_lit_unit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.unit.cfg") -lit.load_config(config, compiler_rt_lit_unit_cfg) - -# Setup config name. -config.name = 'AddressSanitizer-Unit' - -# Setup test source and exec root. For unit tests, we define -# it as build directory with ASan unit tests. -asan_binary_dir = get_required_attr(config, "asan_binary_dir") -config.test_exec_root = os.path.join(asan_binary_dir, "tests") -config.test_source_root = config.test_exec_root diff --git a/lib/asan/lit_tests/Unit/lit.site.cfg.in b/lib/asan/lit_tests/Unit/lit.site.cfg.in index 315d24d1ed09..a45870c9b0e1 100644 --- a/lib/asan/lit_tests/Unit/lit.site.cfg.in +++ b/lib/asan/lit_tests/Unit/lit.site.cfg.in @@ -1,17 +1,16 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! -config.target_triple = "@TARGET_TRIPLE@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_build_mode = "@LLVM_BUILD_MODE@" -config.asan_binary_dir = "@ASAN_BINARY_DIR@" +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") -try: - config.llvm_build_mode = config.llvm_build_mode % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) +# Setup config name. +config.name = 'AddressSanitizer-Unit' -# Let the main config do the real work. -lit.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/Unit/lit.cfg") +# Setup test source and exec root. For unit tests, we define +# it as build directory with ASan unit tests. +config.test_exec_root = "@ASAN_BINARY_DIR@/tests" +config.test_source_root = config.test_exec_root + +if config.host_os == 'Linux': + config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' diff --git a/lib/asan/lit_tests/deep_tail_call.cc b/lib/asan/lit_tests/deep_tail_call.cc deleted file mode 100644 index 6aa15e81f6ec..000000000000 --- a/lib/asan/lit_tests/deep_tail_call.cc +++ /dev/null @@ -1,24 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -// CHECK: AddressSanitizer: global-buffer-overflow -int global[10]; -// CHECK: {{#0.*call4}} -void __attribute__((noinline)) call4(int i) { global[i+10]++; } -// CHECK: {{#1.*call3}} -void __attribute__((noinline)) call3(int i) { call4(i); } -// CHECK: {{#2.*call2}} -void __attribute__((noinline)) call2(int i) { call3(i); } -// CHECK: {{#3.*call1}} -void __attribute__((noinline)) call1(int i) { call2(i); } -// CHECK: {{#4.*main}} -int main(int argc, char **argv) { - call1(argc); - return global[0]; -} diff --git a/lib/asan/lit_tests/double-free.cc b/lib/asan/lit_tests/double-free.cc deleted file mode 100644 index 9e201117c563..000000000000 --- a/lib/asan/lit_tests/double-free.cc +++ /dev/null @@ -1,18 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -#include <stdlib.h> -#include <string.h> -int main(int argc, char **argv) { - char *x = (char*)malloc(10 * sizeof(char)); - memset(x, 0, 10); - int res = x[argc]; - free(x); - free(x + argc - 1); // BOOM - // CHECK: AddressSanitizer: attempting double-free{{.*}}in thread T0 - // CHECK: double-free.cc:[[@LINE-2]] - // CHECK: freed by thread T0 here: - // CHECK: double-free.cc:[[@LINE-5]] - // CHECK: allocated by thread T0 here: - // CHECK: double-free.cc:[[@LINE-10]] - return res; -} diff --git a/lib/asan/lit_tests/global-overflow.cc b/lib/asan/lit_tests/global-overflow.cc deleted file mode 100644 index 6a2f12e106fe..000000000000 --- a/lib/asan/lit_tests/global-overflow.cc +++ /dev/null @@ -1,25 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -#include <string.h> -int main(int argc, char **argv) { - static char XXX[10]; - static char YYY[10]; - static char ZZZ[10]; - memset(XXX, 0, 10); - memset(YYY, 0, 10); - memset(ZZZ, 0, 10); - int res = YYY[argc * 10]; // BOOOM - // CHECK: {{READ of size 1 at 0x.* thread T0}} - // CHECK: {{ #0 0x.* in _?main .*global-overflow.cc:}}[[@LINE-2]] - // CHECK: {{0x.* is located 0 bytes to the right of global variable}} - // CHECK: {{.*YYY.* of size 10}} - res += XXX[argc] + ZZZ[argc]; - return res; -} diff --git a/lib/asan/lit_tests/heap-overflow.cc b/lib/asan/lit_tests/heap-overflow.cc deleted file mode 100644 index f1d719cd0b20..000000000000 --- a/lib/asan/lit_tests/heap-overflow.cc +++ /dev/null @@ -1,36 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out - -#include <stdlib.h> -#include <string.h> -int main(int argc, char **argv) { - char *x = (char*)malloc(10 * sizeof(char)); - memset(x, 0, 10); - int res = x[argc * 10]; // BOOOM - // CHECK: {{READ of size 1 at 0x.* thread T0}} - // CHECK: {{ #0 0x.* in _?main .*heap-overflow.cc:}}[[@LINE-2]] - // CHECK: {{0x.* is located 0 bytes to the right of 10-byte region}} - // CHECK: {{allocated by thread T0 here:}} - - // CHECK-Linux: {{ #0 0x.* in .*malloc}} - // CHECK-Linux: {{ #1 0x.* in main .*heap-overflow.cc:21}} - - // CHECK-Darwin: {{ #0 0x.* in _?wrap_malloc.*}} - // CHECK-Darwin: {{ #1 0x.* in _?main .*heap-overflow.cc:21}} - free(x); - return res; -} diff --git a/lib/asan/lit_tests/initialization-blacklist.cc b/lib/asan/lit_tests/initialization-blacklist.cc deleted file mode 100644 index 12fbc49ed91b..000000000000 --- a/lib/asan/lit_tests/initialization-blacklist.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Test for blacklist functionality of initialization-order checker. - -// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-blacklist-extra.cc\ -// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ -// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ -// RUN: -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-blacklist-extra.cc\ -// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ -// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ -// RUN: -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-blacklist-extra.cc\ -// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ -// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ -// RUN: -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-blacklist-extra.cc\ -// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ -// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ -// RUN: -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-blacklist-extra.cc\ -// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ -// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ -// RUN: -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-blacklist-extra.cc\ -// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ -// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ -// RUN: -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 - -// Function is defined in another TU. -int readBadGlobal(); -int x = readBadGlobal(); // init-order bug. - -// Function is defined in another TU. -int accessBadObject(); -int y = accessBadObject(); // init-order bug. - -int readBadSrcGlobal(); -int z = readBadSrcGlobal(); // init-order bug. - -int main(int argc, char **argv) { - return argc + x + y + z - 1; -} diff --git a/lib/asan/lit_tests/initialization-constexpr.cc b/lib/asan/lit_tests/initialization-constexpr.cc deleted file mode 100644 index ba5410674f76..000000000000 --- a/lib/asan/lit_tests/initialization-constexpr.cc +++ /dev/null @@ -1,43 +0,0 @@ -// Constexpr: -// We need to check that a global variable initialized with a constexpr -// constructor can be accessed during dynamic initialization (as a constexpr -// constructor implies that it was initialized during constant initialization, -// not dynamic initialization). - -// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-constexpr-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-constexpr-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-constexpr-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m64 -O3 %s %p/Helpers/initialization-constexpr-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-constexpr-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-constexpr-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-constexpr-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 -// RUN: %clangxx_asan -m32 -O3 %s %p/Helpers/initialization-constexpr-extra.cc\ -// RUN: --std=c++11 -fsanitize=init-order -o %t -// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 - -class Integer { - private: - int value; - - public: - constexpr Integer(int x = 0) : value(x) {} - int getValue() {return value;} -}; -Integer coolestInteger(42); -int getCoolestInteger() { return coolestInteger.getValue(); } - -int main() { return 0; } diff --git a/lib/asan/lit_tests/interface_test.cc b/lib/asan/lit_tests/interface_test.cc deleted file mode 100644 index 428a109fe70d..000000000000 --- a/lib/asan/lit_tests/interface_test.cc +++ /dev/null @@ -1,8 +0,0 @@ -// Check that user may include ASan interface header. -// RUN: %clang -fsanitize=address -I %p/../../../include %s -o %t && %t -// RUN: %clang -I %p/../../../include %s -o %t && %t -#include <sanitizer/asan_interface.h> - -int main() { - return 0; -} diff --git a/lib/asan/lit_tests/large_func_test.cc b/lib/asan/lit_tests/large_func_test.cc deleted file mode 100644 index ceecc29b7b0a..000000000000 --- a/lib/asan/lit_tests/large_func_test.cc +++ /dev/null @@ -1,63 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out - -#include <stdlib.h> -__attribute__((noinline)) -static void LargeFunction(int *x, int zero) { - x[0]++; - x[1]++; - x[2]++; - x[3]++; - x[4]++; - x[5]++; - x[6]++; - x[7]++; - x[8]++; - x[9]++; - - // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} - // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} - // CHECK: {{READ of size 4 at 0x.* thread T0}} - x[zero + 103]++; // we should report this exact line - // atos incorrectly extracts the symbol name for the static functions on - // Darwin. - // CHECK-Linux: {{#0 0x.* in LargeFunction.*large_func_test.cc:}}[[@LINE-3]] - // CHECK-Darwin: {{#0 0x.* in .*LargeFunction.*large_func_test.cc}}:[[@LINE-4]] - - x[10]++; - x[11]++; - x[12]++; - x[13]++; - x[14]++; - x[15]++; - x[16]++; - x[17]++; - x[18]++; - x[19]++; -} - -int main(int argc, char **argv) { - int *x = new int[100]; - LargeFunction(x, argc - 1); - // CHECK: {{ #1 0x.* in _?main .*large_func_test.cc:}}[[@LINE-1]] - // CHECK: {{0x.* is located 12 bytes to the right of 400-byte region}} - // CHECK: {{allocated by thread T0 here:}} - // CHECK-Linux: {{ #0 0x.* in operator new.*}} - // CHECK-Darwin: {{ #0 0x.* in .*_Zna.*}} - // CHECK: {{ #1 0x.* in _?main .*large_func_test.cc:}}[[@LINE-7]] - delete x; -} diff --git a/lib/asan/lit_tests/lit.cfg b/lib/asan/lit_tests/lit.cfg index 5daecd9e557d..71a700c6ac3b 100644 --- a/lib/asan/lit_tests/lit.cfg +++ b/lib/asan/lit_tests/lit.cfg @@ -2,24 +2,27 @@ import os +import lit.util + def get_required_attr(config, attr_name): attr_value = getattr(config, attr_name, None) if not attr_value: - lit.fatal("No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) return attr_value # Setup config name. -config.name = 'AddressSanitizer' +config.name = 'AddressSanitizer' + config.bits # Setup source root. config.test_source_root = os.path.dirname(__file__) def DisplayNoConfigMessage(): - lit.fatal("No site specific configuration available! " + - "Try running your test from the build tree or running " + - "make check-asan") + lit_config.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-asan") # Figure out LLVM source root. llvm_src_root = getattr(config, 'llvm_src_root', None) @@ -27,9 +30,9 @@ if llvm_src_root is None: # We probably haven't loaded the site-specific configuration: the user # is likely trying to run a test file directly, and the site configuration # wasn't created by the build system. - asan_site_cfg = lit.params.get('asan_site_config', None) + asan_site_cfg = lit_config.params.get('asan_site_config', None) if (asan_site_cfg) and (os.path.exists(asan_site_cfg)): - lit.load_config(config, asan_site_cfg) + lit_config.load_config(config, asan_site_cfg) raise SystemExit # Try to guess the location of site-specific configuration using llvm-config @@ -45,51 +48,45 @@ if llvm_src_root is None: if (not asan_site_cfg) or (not os.path.exists(asan_site_cfg)): DisplayNoConfigMessage() - lit.load_config(config, asan_site_cfg) + lit_config.load_config(config, asan_site_cfg) raise SystemExit -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, "compiler_rt_src_root") -compiler_rt_lit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.cfg") -if (not compiler_rt_lit_cfg) or (not os.path.exists(compiler_rt_lit_cfg)): - lit.fatal("Can't find common compiler-rt lit config at: %r" - % compiler_rt_lit_cfg) -lit.load_config(config, compiler_rt_lit_cfg) - # Setup default compiler flags used with -fsanitize=address option. # FIXME: Review the set of required flags and check if it can be reduced. -clang_asan_cxxflags = ("-ccc-cxx " - + "-fsanitize=address " - + "-mno-omit-leaf-frame-pointer " - + "-fno-omit-frame-pointer " - + "-fno-optimize-sibling-calls " - + "-g") +bits_cflag = " -m" + config.bits +clang_asan_cflags = (" -fsanitize=address" + + " -mno-omit-leaf-frame-pointer" + + " -fno-omit-frame-pointer" + + " -fno-optimize-sibling-calls" + + " -g" + + bits_cflag) +clang_asan_cxxflags = " --driver-mode=g++" + clang_asan_cflags +config.substitutions.append( ("%clang ", " " + config.clang + bits_cflag + " ")) +config.substitutions.append( ("%clangxx ", (" " + config.clang + + " --driver-mode=g++" + + bits_cflag + " ")) ) +config.substitutions.append( ("%clang_asan ", (" " + config.clang + " " + + clang_asan_cflags + " ")) ) config.substitutions.append( ("%clangxx_asan ", (" " + config.clang + " " + clang_asan_cxxflags + " ")) ) -# Setup path to external LLVM symbolizer to run AddressSanitizer output tests. -llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) -if llvm_tools_dir: - config.environment['LLVM_SYMBOLIZER_PATH'] = os.path.join( - llvm_tools_dir, "llvm-symbolizer") - -# Setup path to symbolizer script. -# FIXME: Instead we should copy this script to the build tree and point -# at it there. -asan_source_dir = os.path.join(config.test_source_root, "..") -symbolizer = os.path.join(asan_source_dir, - 'scripts', 'asan_symbolize.py') -if not os.path.exists(symbolizer): - lit.fatal("Can't find symbolizer script on path %r" % symbolizer) -# Define %symbolize substitution that filters output through -# symbolizer and c++filt (for demangling). -config.substitutions.append( ("%symbolize ", (" " + symbolizer + - " | c++filt " ))) +# Setup path to asan_symbolize.py script. +asan_source_dir = get_required_attr(config, "asan_source_dir") +asan_symbolize = os.path.join(asan_source_dir, "scripts", "asan_symbolize.py") +if not os.path.exists(asan_symbolize): + lit_config.fatal("Can't find script on path %r" % asan_symbolize) +python_exec = get_required_attr(config, "python_executable") +config.substitutions.append( ("%asan_symbolize", python_exec + " " + asan_symbolize + " ") ) # Define CHECK-%os to check for OS-dependent output. config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os))) +config.available_features.add("asan-" + config.bits + "-bits") + +# Turn on leak detection on 64-bit Linux. +if config.host_os == 'Linux' and config.bits == '64': + config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' + # Default test suffixes. config.suffixes = ['.c', '.cc', '.cpp'] diff --git a/lib/asan/lit_tests/memcmp_strict_test.cc b/lib/asan/lit_tests/memcmp_strict_test.cc deleted file mode 100644 index 00bf921c744a..000000000000 --- a/lib/asan/lit_tests/memcmp_strict_test.cc +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && ASAN_OPTIONS=strict_memcmp=0 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-nonstrict -// RUN: %clangxx_asan -m64 -O0 %s -o %t && ASAN_OPTIONS=strict_memcmp=1 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-strict -// Default to strict_memcmp=1. -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-strict - -#include <stdio.h> -#include <string.h> -int main() { - char kFoo[] = "foo"; - char kFubar[] = "fubar"; - int res = memcmp(kFoo, kFubar, strlen(kFubar)); - printf("res: %d\n", res); - // CHECK-nonstrict: {{res: -1}} - // CHECK-strict: AddressSanitizer: stack-buffer-overflow - return 0; -} diff --git a/lib/asan/lit_tests/memcmp_test.cc b/lib/asan/lit_tests/memcmp_test.cc deleted file mode 100644 index ac3f7f32ea75..000000000000 --- a/lib/asan/lit_tests/memcmp_test.cc +++ /dev/null @@ -1,19 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -#include <string.h> -int main(int argc, char **argv) { - char a1[] = {argc, 2, 3, 4}; - char a2[] = {1, 2*argc, 3, 4}; - int res = memcmp(a1, a2, 4 + argc); // BOOM - // CHECK: AddressSanitizer: stack-buffer-overflow - // CHECK: {{#0.*memcmp}} - // CHECK: {{#1.*main}} - return res; -} diff --git a/lib/asan/lit_tests/null_deref.cc b/lib/asan/lit_tests/null_deref.cc deleted file mode 100644 index 60a521d1c210..000000000000 --- a/lib/asan/lit_tests/null_deref.cc +++ /dev/null @@ -1,31 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out - -__attribute__((noinline)) -static void NullDeref(int *ptr) { - // CHECK: ERROR: AddressSanitizer: SEGV on unknown address - // CHECK: {{0x0*00028 .*pc 0x.*}} - // CHECK: {{AddressSanitizer can not provide additional info.}} - ptr[10]++; // BOOM - // atos on Mac cannot extract the symbol name correctly. - // CHECK-Linux: {{ #0 0x.* in NullDeref.*null_deref.cc:}}[[@LINE-2]] - // CHECK-Darwin: {{ #0 0x.* in .*NullDeref.*null_deref.cc:}}[[@LINE-3]] -} -int main() { - NullDeref((int*)0); - // CHECK: {{ #1 0x.* in _?main.*null_deref.cc:}}[[@LINE-1]] -} diff --git a/lib/asan/lit_tests/partial_right.cc b/lib/asan/lit_tests/partial_right.cc deleted file mode 100644 index c579262726f9..000000000000 --- a/lib/asan/lit_tests/partial_right.cc +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -#include <stdlib.h> -int main(int argc, char **argv) { - volatile int *x = (int*)malloc(2*sizeof(int) + 2); - int res = x[2]; // BOOOM - // CHECK: {{READ of size 4 at 0x.* thread T0}} - // CHECK: [[ADDR:0x[01-9a-fa-f]+]] is located 0 bytes to the right of {{.*}}-byte region [{{.*}},{{.*}}[[ADDR]]) - return res; -} diff --git a/lib/asan/lit_tests/shared-lib-test.cc b/lib/asan/lit_tests/shared-lib-test.cc deleted file mode 100644 index 05bf3ecdf4f9..000000000000 --- a/lib/asan/lit_tests/shared-lib-test.cc +++ /dev/null @@ -1,54 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/shared-lib-test-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %p/SharedLibs/shared-lib-test-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %p/SharedLibs/shared-lib-test-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %p/SharedLibs/shared-lib-test-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %p/SharedLibs/shared-lib-test-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %p/SharedLibs/shared-lib-test-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %p/SharedLibs/shared-lib-test-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %p/SharedLibs/shared-lib-test-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -#include <dlfcn.h> -#include <stdio.h> -#include <string.h> - -#include <string> - -using std::string; - -typedef void (fun_t)(int x); - -int main(int argc, char *argv[]) { - string path = string(argv[0]) + "-so.so"; - printf("opening %s ... \n", path.c_str()); - void *lib = dlopen(path.c_str(), RTLD_NOW); - if (!lib) { - printf("error in dlopen(): %s\n", dlerror()); - return 1; - } - fun_t *inc = (fun_t*)dlsym(lib, "inc"); - if (!inc) return 1; - printf("ok\n"); - inc(1); - inc(-1); // BOOM - // CHECK: {{.*ERROR: AddressSanitizer: global-buffer-overflow}} - // CHECK: {{READ of size 4 at 0x.* thread T0}} - // CHECK: {{ #0 0x.*}} - // CHECK: {{ #1 0x.* in _?main .*shared-lib-test.cc:}}[[@LINE-4]] - return 0; -} diff --git a/lib/asan/lit_tests/stack-overflow.cc b/lib/asan/lit_tests/stack-overflow.cc deleted file mode 100644 index 25ea43af48a4..000000000000 --- a/lib/asan/lit_tests/stack-overflow.cc +++ /dev/null @@ -1,20 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -#include <string.h> -int main(int argc, char **argv) { - char x[10]; - memset(x, 0, 10); - int res = x[argc * 10]; // BOOOM - // CHECK: {{READ of size 1 at 0x.* thread T0}} - // CHECK: {{ #0 0x.* in _?main .*stack-overflow.cc:}}[[@LINE-2]] - // CHECK: {{Address 0x.* is located in stack of thread T0 at offset}} - // CHECK-NEXT: in{{.*}}main{{.*}}stack-overflow.cc - return res; -} diff --git a/lib/asan/lit_tests/stack-use-after-return.cc b/lib/asan/lit_tests/stack-use-after-return.cc deleted file mode 100644 index f8d8a1a2ae39..000000000000 --- a/lib/asan/lit_tests/stack-use-after-return.cc +++ /dev/null @@ -1,45 +0,0 @@ -// XFAIL: * -// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O0 %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O1 %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O2 %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O3 %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O0 %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O1 %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O2 %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O3 %s -o %t && \ -// RUN: %t 2>&1 | %symbolize | FileCheck %s - -#include <stdio.h> - -__attribute__((noinline)) -char *Ident(char *x) { - fprintf(stderr, "1: %p\n", x); - return x; -} - -__attribute__((noinline)) -char *Func1() { - char local; - return Ident(&local); -} - -__attribute__((noinline)) -void Func2(char *x) { - fprintf(stderr, "2: %p\n", x); - *x = 1; - // CHECK: WRITE of size 1 {{.*}} thread T0 - // CHECK: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-2]] - // CHECK: is located {{.*}} in frame <{{.*}}Func1{{.*}}> of T0's stack -} - -int main(int argc, char **argv) { - Func2(Func1()); - return 0; -} diff --git a/lib/asan/lit_tests/strncpy-overflow.cc b/lib/asan/lit_tests/strncpy-overflow.cc deleted file mode 100644 index 5133b5c1653e..000000000000 --- a/lib/asan/lit_tests/strncpy-overflow.cc +++ /dev/null @@ -1,38 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out - -#include <string.h> -#include <stdlib.h> -int main(int argc, char **argv) { - char *hello = (char*)malloc(6); - strcpy(hello, "hello"); - char *short_buffer = (char*)malloc(9); - strncpy(short_buffer, hello, 10); // BOOM - // CHECK: {{WRITE of size 10 at 0x.* thread T0}} - // CHECK-Linux: {{ #0 0x.* in .*strncpy}} - // CHECK-Darwin: {{ #0 0x.* in _?wrap_strncpy}} - // CHECK: {{ #1 0x.* in _?main .*strncpy-overflow.cc:}}[[@LINE-4]] - // CHECK: {{0x.* is located 0 bytes to the right of 9-byte region}} - // CHECK: {{allocated by thread T0 here:}} - - // CHECK-Linux: {{ #0 0x.* in .*malloc}} - // CHECK-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-10]] - - // CHECK-Darwin: {{ #0 0x.* in _?wrap_malloc.*}} - // CHECK-Darwin: {{ #1 0x.* in _?main .*strncpy-overflow.cc:}}[[@LINE-13]] - return short_buffer[8]; -} diff --git a/lib/asan/lit_tests/use-after-free-right.cc b/lib/asan/lit_tests/use-after-free-right.cc deleted file mode 100644 index b0de07b04a08..000000000000 --- a/lib/asan/lit_tests/use-after-free-right.cc +++ /dev/null @@ -1,46 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out - -// Test use-after-free report in the case when access is at the right border of -// the allocation. - -#include <stdlib.h> -int main() { - volatile char *x = (char*)malloc(sizeof(char)); - free((void*)x); - *x = 42; - // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} - // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} - // CHECK: {{WRITE of size 1 at 0x.* thread T0}} - // CHECK: {{ #0 0x.* in _?main .*use-after-free-right.cc:25}} - // CHECK: {{0x.* is located 0 bytes inside of 1-byte region .0x.*,0x.*}} - // CHECK: {{freed by thread T0 here:}} - - // CHECK-Linux: {{ #0 0x.* in .*free}} - // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:24}} - - // CHECK-Darwin: {{ #0 0x.* in _?wrap_free}} - // CHECK-Darwin: {{ #1 0x.* in _?main .*use-after-free-right.cc:24}} - - // CHECK: {{previously allocated by thread T0 here:}} - - // CHECK-Linux: {{ #0 0x.* in .*malloc}} - // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:23}} - - // CHECK-Darwin: {{ #0 0x.* in _?wrap_malloc.*}} - // CHECK-Darwin: {{ #1 0x.* in _?main .*use-after-free-right.cc:23}} -} diff --git a/lib/asan/lit_tests/use-after-free.cc b/lib/asan/lit_tests/use-after-free.cc deleted file mode 100644 index aee185dc4518..000000000000 --- a/lib/asan/lit_tests/use-after-free.cc +++ /dev/null @@ -1,43 +0,0 @@ -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out -// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out - -#include <stdlib.h> -int main() { - char *x = (char*)malloc(10 * sizeof(char)); - free(x); - return x[5]; - // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} - // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} - // CHECK: {{READ of size 1 at 0x.* thread T0}} - // CHECK: {{ #0 0x.* in _?main .*use-after-free.cc:22}} - // CHECK: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}} - // CHECK: {{freed by thread T0 here:}} - - // CHECK-Linux: {{ #0 0x.* in .*free}} - // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:21}} - - // CHECK-Darwin: {{ #0 0x.* in _?wrap_free}} - // CHECK-Darwin: {{ #1 0x.* in _?main .*use-after-free.cc:21}} - - // CHECK: {{previously allocated by thread T0 here:}} - - // CHECK-Linux: {{ #0 0x.* in .*malloc}} - // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:20}} - - // CHECK-Darwin: {{ #0 0x.* in _?wrap_malloc.*}} - // CHECK-Darwin: {{ #1 0x.* in _?main .*use-after-free.cc:20}} -} diff --git a/lib/asan/lit_tests/wait.cc b/lib/asan/lit_tests/wait.cc deleted file mode 100644 index 88fbb17176fa..000000000000 --- a/lib/asan/lit_tests/wait.cc +++ /dev/null @@ -1,77 +0,0 @@ -// RUN: %clangxx_asan -DWAIT -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -// RUN: %clangxx_asan -DWAITPID -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAITPID -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAITPID -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAITPID -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -// RUN: %clangxx_asan -DWAITID -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAITID -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAITID -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAITID -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -// RUN: %clangxx_asan -DWAIT3 -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT3 -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT3 -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT3 -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -// RUN: %clangxx_asan -DWAIT4 -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT4 -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT4 -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT4 -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -// RUN: %clangxx_asan -DWAIT3_RUSAGE -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT3_RUSAGE -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT3_RUSAGE -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT3_RUSAGE -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - -// RUN: %clangxx_asan -DWAIT4_RUSAGE -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT4_RUSAGE -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT4_RUSAGE -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s -// RUN: %clangxx_asan -DWAIT4_RUSAGE -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s - - -#include <assert.h> -#include <sys/wait.h> -#include <unistd.h> - -int main(int argc, char **argv) { - pid_t pid = fork(); - if (pid) { // parent - int x[3]; - int *status = x + argc * 3; - int res; -#if defined(WAIT) - res = wait(status); -#elif defined(WAITPID) - res = waitpid(pid, status, WNOHANG); -#elif defined(WAITID) - siginfo_t *si = (siginfo_t*)(x + argc * 3); - res = waitid(P_ALL, 0, si, WEXITED | WNOHANG); -#elif defined(WAIT3) - res = wait3(status, WNOHANG, NULL); -#elif defined(WAIT4) - res = wait4(pid, status, WNOHANG, NULL); -#elif defined(WAIT3_RUSAGE) || defined(WAIT4_RUSAGE) - struct rusage *ru = (struct rusage*)(x + argc * 3); - int good_status; -# if defined(WAIT3_RUSAGE) - res = wait3(&good_status, WNOHANG, ru); -# elif defined(WAIT4_RUSAGE) - res = wait4(pid, &good_status, WNOHANG, ru); -# endif -#endif - // CHECK: stack-buffer-overflow - // CHECK: {{WRITE of size .* at 0x.* thread T0}} - // CHECK: {{in .*wait}} - // CHECK: {{in _?main .*wait.cc:}} - // CHECK: is located in stack of thread T0 at offset - // CHECK: {{in _?main}} - return res != -1; - } - // child - return 0; -} diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py index bd3bf1e9b53e..a398fcf10361 100755 --- a/lib/asan/scripts/asan_symbolize.py +++ b/lib/asan/scripts/asan_symbolize.py @@ -10,14 +10,14 @@ import bisect import getopt import os +import pty import re import subprocess import sys +import termios llvm_symbolizer = None symbolizers = {} -filetypes = {} -vmaddrs = {} DEBUG = False demangle = False; @@ -101,8 +101,10 @@ class LLVMSymbolizer(Symbolizer): def LLVMSymbolizerFactory(system): symbolizer_path = os.getenv('LLVM_SYMBOLIZER_PATH') if not symbolizer_path: - # Assume llvm-symbolizer is in PATH. - symbolizer_path = 'llvm-symbolizer' + symbolizer_path = os.getenv('ASAN_SYMBOLIZER_PATH') + if not symbolizer_path: + # Assume llvm-symbolizer is in PATH. + symbolizer_path = 'llvm-symbolizer' return LLVMSymbolizer(symbolizer_path) @@ -137,6 +139,36 @@ class Addr2LineSymbolizer(Symbolizer): return ['%s in %s %s' % (addr, function_name, file_name)] +class UnbufferedLineConverter(object): + """ + Wrap a child process that responds to each line of input with one line of + output. Uses pty to trick the child into providing unbuffered output. + """ + def __init__(self, args, close_stderr=False): + pid, fd = pty.fork() + if pid == 0: + # We're the child. Transfer control to command. + if close_stderr: + dev_null = os.open('/dev/null', 0) + os.dup2(dev_null, 2) + os.execvp(args[0], args) + else: + # Disable echoing. + attr = termios.tcgetattr(fd) + attr[3] = attr[3] & ~termios.ECHO + termios.tcsetattr(fd, termios.TCSANOW, attr) + # Set up a file()-like interface to the child process + self.r = os.fdopen(fd, "r", 1) + self.w = os.fdopen(os.dup(fd), "w", 1) + + def convert(self, line): + self.w.write(line + "\n") + return self.readline() + + def readline(self): + return self.r.readline().rstrip() + + class DarwinSymbolizer(Symbolizer): def __init__(self, addr, binary): super(DarwinSymbolizer, self).__init__() @@ -146,29 +178,21 @@ class DarwinSymbolizer(Symbolizer): self.arch = 'x86_64' else: self.arch = 'i386' - self.vmaddr = None - self.pipe = None - - def write_addr_to_pipe(self, offset): - print >> self.pipe.stdin, '0x%x' % int(offset, 16) + self.open_atos() def open_atos(self): if DEBUG: print 'atos -o %s -arch %s' % (self.binary, self.arch) cmdline = ['atos', '-o', self.binary, '-arch', self.arch] - self.pipe = subprocess.Popen(cmdline, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + self.atos = UnbufferedLineConverter(cmdline, close_stderr=True) def symbolize(self, addr, binary, offset): """Overrides Symbolizer.symbolize.""" if self.binary != binary: return None - self.open_atos() - self.write_addr_to_pipe(offset) - self.pipe.stdin.close() - atos_line = self.pipe.stdout.readline().rstrip() + atos_line = self.atos.convert('0x%x' % int(offset, 16)) + while "got symbolicator for" in atos_line: + atos_line = self.atos.readline() # A well-formed atos response looks like this: # foo(type1, type2) (in object.name) (filename.cc:80) match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line) @@ -331,7 +355,10 @@ class SymbolizationLoop(object): def process_stdin(self): self.frame_no = 0 - for line in sys.stdin: + while True: + line = sys.stdin.readline() + if not line: + break self.current_line = line.rstrip() #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) stack_trace_line_format = ( diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt index 80d6f5d67aad..fc0f1788bd35 100644 --- a/lib/asan/tests/CMakeLists.txt +++ b/lib/asan/tests/CMakeLists.txt @@ -36,13 +36,9 @@ set(ASAN_UNITTEST_COMMON_CFLAGS -Wall -Wno-format -Werror + -Werror=sign-compare -g - -O2 -) - -if(ASAN_TESTS_USE_ZERO_BASE_SHADOW) - list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -fPIE) -endif() + -O2) if(SUPPORTS_NO_VARIADIC_MACROS_FLAG) list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -Wno-variadic-macros) endif() @@ -62,24 +58,7 @@ else() -DASAN_NEEDS_SEGV=1) endif() -set(ASAN_LINK_FLAGS) -if(ASAN_TESTS_USE_ZERO_BASE_SHADOW) - list(APPEND ASAN_LINK_FLAGS -pie) -endif() -# On Android, we link with ASan runtime manually. On other platforms we depend -# on Clang driver behavior, passing -fsanitize=address flag. -if(NOT ANDROID) - list(APPEND ASAN_LINK_FLAGS -fsanitize=address) -endif() -# Unit tests on Mac depend on Foundation. -if(APPLE) - list(APPEND ASAN_LINK_FLAGS -framework Foundation) -endif() -# Unit tests require libstdc++. -list(APPEND ASAN_LINK_FLAGS -lstdc++) - set(ASAN_BLACKLIST_FILE "${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore") - set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS ${ASAN_UNITTEST_COMMON_CFLAGS} -fsanitize=address @@ -88,35 +67,65 @@ set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS -mllvm -asan-globals=1 -mllvm -asan-mapping-scale=0 # default will be used -mllvm -asan-mapping-offset-log=-1 # default will be used - -mllvm -asan-use-after-return=0 ) if(ASAN_TESTS_USE_ZERO_BASE_SHADOW) list(APPEND ASAN_UNITTEST_INSTRUMENTED_CFLAGS -fsanitize-address-zero-base-shadow) endif() +# Unit tests require libstdc++. +set(ASAN_UNITTEST_COMMON_LINKFLAGS -lstdc++) +# Unit tests on Mac depend on Foundation. +if(APPLE) + list(APPEND ASAN_UNITTEST_COMMON_LINKFLAGS -framework Foundation) +endif() +if(ASAN_TESTS_USE_ZERO_BASE_SHADOW) + list(APPEND ASAN_UNITTEST_COMMON_LINKFLAGS -pie) +endif() + +set(ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS + ${ASAN_UNITTEST_COMMON_LINKFLAGS}) +# On Android, we link with ASan runtime manually. On other platforms we depend +# on Clang driver behavior, passing -fsanitize=address flag. +if(NOT ANDROID) + list(APPEND ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS -fsanitize=address) +endif() + +set(ASAN_UNITTEST_NOINST_LINKFLAGS + ${ASAN_UNITTEST_COMMON_LINKFLAGS} + -ldl -lm) +if(NOT ANDROID) + list(APPEND ASAN_UNITTEST_NOINST_LINKFLAGS -lpthread) +endif() + # 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) get_filename_component(basename ${source} NAME) - set(output_obj "${basename}.${arch}.o") + set(output_obj "${obj_list}.${basename}.${arch}.o") get_target_flags_for_arch(${arch} TARGET_CFLAGS) clang_compile(${output_obj} ${source} CFLAGS ${ARGN} ${TARGET_CFLAGS} - DEPS gtest ${ASAN_RUNTIME_LIBRARIES} + DEPS gtest asan_runtime_libraries ${ASAN_UNITTEST_HEADERS} ${ASAN_BLACKLIST_FILE}) list(APPEND ${obj_list} ${output_obj}) endmacro() # Link ASan unit test for a given architecture from a set -# of objects in ${ARGN}. +# of objects in with given linker flags. macro(add_asan_test test_suite test_name arch) + parse_arguments(TEST "OBJECTS;LINKFLAGS" "WITH_TEST_RUNTIME" ${ARGN}) get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + set(TEST_DEPS asan_runtime_libraries ${TEST_OBJECTS}) + if(TEST_WITH_TEST_RUNTIME) + list(APPEND TEST_DEPS ${ASAN_TEST_RUNTIME}) + list(APPEND TEST_OBJECTS lib${ASAN_TEST_RUNTIME}.a) + endif() add_compiler_rt_test(${test_suite} ${test_name} - OBJECTS ${ARGN} - DEPS ${ASAN_RUNTIME_LIBRARIES} ${ARGN} - LINK_FLAGS ${ASAN_LINK_FLAGS} + OBJECTS ${TEST_OBJECTS} + DEPS ${TEST_DEPS} + LINK_FLAGS ${TEST_LINKFLAGS} ${TARGET_LINK_FLAGS}) endmacro() @@ -128,52 +137,84 @@ add_custom_target(AsanBenchmarks) set_target_properties(AsanBenchmarks PROPERTIES FOLDER "Asan benchmarks") set(ASAN_NOINST_TEST_SOURCES + ${COMPILER_RT_GTEST_SOURCE} + asan_fake_stack_test.cc asan_noinst_test.cc asan_test_main.cc) + set(ASAN_INST_TEST_SOURCES + ${COMPILER_RT_GTEST_SOURCE} asan_globals_test.cc + asan_interface_test.cc asan_test.cc asan_oob_test.cc asan_mem_test.cc - asan_str_test.cc) + asan_str_test.cc + asan_test_main.cc) +if(APPLE) + list(APPEND ASAN_INST_TEST_SOURCES asan_mac_test.cc) +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 arch) - # Build gtest instrumented with ASan. - set(ASAN_INST_GTEST) - asan_compile(ASAN_INST_GTEST ${COMPILER_RT_GTEST_SOURCE} ${arch} - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) # Instrumented tests. set(ASAN_INST_TEST_OBJECTS) foreach(src ${ASAN_INST_TEST_SOURCES}) asan_compile(ASAN_INST_TEST_OBJECTS ${src} ${arch} ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) endforeach() - # Add Mac-specific tests. if (APPLE) - asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test.cc ${arch} - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + # Add Mac-specific helper. asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test_helpers.mm ${arch} ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -ObjC) endif() + add_asan_test(AsanUnitTests "Asan-${arch}-Test" ${arch} + OBJECTS ${ASAN_INST_TEST_OBJECTS} + LINKFLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) + + # 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.osx> + $<TARGET_OBJECTS:RTInterception.osx> + $<TARGET_OBJECTS:RTSanitizerCommon.osx> + $<TARGET_OBJECTS:RTLSanCommon.osx>) + else() + set(ASAN_TEST_RUNTIME_OBJECTS + $<TARGET_OBJECTS:RTAsan.${arch}> + $<TARGET_OBJECTS:RTInterception.${arch}> + $<TARGET_OBJECTS:RTLSanCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${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}) # Uninstrumented tests. set(ASAN_NOINST_TEST_OBJECTS) foreach(src ${ASAN_NOINST_TEST_SOURCES}) asan_compile(ASAN_NOINST_TEST_OBJECTS ${src} ${arch} ${ASAN_UNITTEST_COMMON_CFLAGS}) endforeach() - # Link everything together. - add_asan_test(AsanUnitTests "Asan-${arch}-Test" ${arch} - ${ASAN_NOINST_TEST_OBJECTS} - ${ASAN_INST_TEST_OBJECTS} ${ASAN_INST_GTEST}) + add_asan_test(AsanUnitTests "Asan-${arch}-Noinst-Test" ${arch} + OBJECTS ${ASAN_NOINST_TEST_OBJECTS} + LINKFLAGS ${ASAN_UNITTEST_NOINST_LINKFLAGS} + WITH_TEST_RUNTIME) - # Instrumented benchmarks. + # Benchmarks. set(ASAN_BENCHMARKS_OBJECTS) - asan_compile(ASAN_BENCHMARKS_OBJECTS asan_benchmarks_test.cc ${arch} - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) - # Link benchmarks. + foreach(src ${ASAN_BENCHMARKS_SOURCES}) + asan_compile(ASAN_BENCHMARKS_OBJECTS ${src} ${arch} + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + endforeach() add_asan_test(AsanBenchmarks "Asan-${arch}-Benchmark" ${arch} - ${ASAN_BENCHMARKS_OBJECTS} ${ASAN_INST_GTEST}) + OBJECTS ${ASAN_BENCHMARKS_OBJECTS} + LINKFLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) endmacro() if(COMPILER_RT_CAN_EXECUTE_TESTS) @@ -185,20 +226,28 @@ endif() if(ANDROID) # We assume that unit tests on Android are built in a build # tree with fresh Clang as a host compiler. - add_library(asan_noinst_test OBJECT ${ASAN_NOINST_TEST_SOURCES}) - set_target_compile_flags(asan_noinst_test ${ASAN_UNITTEST_COMMON_CFLAGS}) - add_library(asan_inst_test OBJECT - ${ASAN_INST_TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}) - set_target_compile_flags(asan_inst_test ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + + # Test w/o ASan instrumentation. Link it with ASan statically. + add_executable(AsanNoinstTest + $<TARGET_OBJECTS:RTAsan.arm.android> + $<TARGET_OBJECTS:RTInterception.arm.android> + $<TARGET_OBJECTS:RTSanitizerCommon.arm.android> + ${COMPILER_RT_GTEST_SOURCE} + ${ASAN_NOINST_TEST_SOURCES}) + set_target_compile_flags(AsanNoinstTest ${ASAN_UNITTEST_COMMON_CFLAGS}) + set_target_link_flags(AsanNoinstTest ${ASAN_UNITTEST_NOINST_LINKFLAGS}) + + # Test with ASan instrumentation. Link with ASan dynamic runtime. add_executable(AsanTest - $<TARGET_OBJECTS:asan_noinst_test> - $<TARGET_OBJECTS:asan_inst_test> - ) + ${COMPILER_RT_GTEST_SOURCE} + ${ASAN_INST_TEST_SOURCES}) + set_target_compile_flags(AsanTest ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + set_target_link_flags(AsanTest ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) + target_link_libraries(AsanTest clang_rt.asan-arm-android) + # Setup correct output directory and link flags. - set_target_properties(AsanTest PROPERTIES + set_target_properties(AsanNoinstTest AsanTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - set_target_link_flags(AsanTest ${ASAN_LINK_FLAGS}) - target_link_libraries(AsanTest clang_rt.asan-arm-android) # Add unit test to test suite. - add_dependencies(AsanUnitTests AsanTest) + add_dependencies(AsanUnitTests AsanNoinstTest AsanTest) endif() diff --git a/lib/asan/tests/asan_fake_stack_test.cc b/lib/asan/tests/asan_fake_stack_test.cc new file mode 100644 index 000000000000..1c98125eb45e --- /dev/null +++ b/lib/asan/tests/asan_fake_stack_test.cc @@ -0,0 +1,150 @@ +//===-- asan_fake_stack_test.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Tests for FakeStack. +// This test file should be compiled w/o asan instrumentation. +//===----------------------------------------------------------------------===// + +#include "asan_fake_stack.h" +#include "asan_test_utils.h" +#include "sanitizer_common/sanitizer_common.h" + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +#include <map> + +namespace __asan { + +TEST(FakeStack, FlagsSize) { + EXPECT_EQ(FakeStack::SizeRequiredForFlags(10), 1U << 5); + EXPECT_EQ(FakeStack::SizeRequiredForFlags(11), 1U << 6); + EXPECT_EQ(FakeStack::SizeRequiredForFlags(20), 1U << 15); +} + +TEST(FakeStack, RequiredSize) { + // for (int i = 15; i < 20; i++) { + // uptr alloc_size = FakeStack::RequiredSize(i); + // printf("%zdK ==> %zd\n", 1 << (i - 10), alloc_size); + // } + EXPECT_EQ(FakeStack::RequiredSize(15), 365568U); + EXPECT_EQ(FakeStack::RequiredSize(16), 727040U); + EXPECT_EQ(FakeStack::RequiredSize(17), 1449984U); + EXPECT_EQ(FakeStack::RequiredSize(18), 2895872U); + EXPECT_EQ(FakeStack::RequiredSize(19), 5787648U); +} + +TEST(FakeStack, FlagsOffset) { + for (uptr stack_size_log = 15; stack_size_log <= 20; stack_size_log++) { + uptr stack_size = 1UL << stack_size_log; + uptr offset = 0; + for (uptr class_id = 0; class_id < FakeStack::kNumberOfSizeClasses; + class_id++) { + uptr frame_size = FakeStack::BytesInSizeClass(class_id); + uptr num_flags = stack_size / frame_size; + EXPECT_EQ(offset, FakeStack::FlagsOffset(stack_size_log, class_id)); + // printf("%zd: %zd => %zd %zd\n", stack_size_log, class_id, offset, + // FakeStack::FlagsOffset(stack_size_log, class_id)); + offset += num_flags; + } + } +} + +TEST(FakeStack, CreateDestroy) { + for (int i = 0; i < 1000; i++) { + for (uptr stack_size_log = 20; stack_size_log <= 22; stack_size_log++) { + FakeStack *fake_stack = FakeStack::Create(stack_size_log); + fake_stack->Destroy(); + } + } +} + +TEST(FakeStack, ModuloNumberOfFrames) { + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, 0), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<15)), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<10)), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<9)), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<8)), 1U<<8); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<15) + 1), 1U); + + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 0), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 1<<9), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 1<<8), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 1<<7), 1U<<7); + + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 0), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 1), 1U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 15), 15U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 16), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 17), 1U); +} + +TEST(FakeStack, GetFrame) { + const uptr stack_size_log = 20; + const uptr stack_size = 1 << stack_size_log; + FakeStack *fs = FakeStack::Create(stack_size_log); + u8 *base = fs->GetFrame(stack_size_log, 0, 0); + EXPECT_EQ(base, reinterpret_cast<u8 *>(fs) + + fs->SizeRequiredForFlags(stack_size_log) + 4096); + EXPECT_EQ(base + 0*stack_size + 64 * 7, fs->GetFrame(stack_size_log, 0, 7U)); + EXPECT_EQ(base + 1*stack_size + 128 * 3, fs->GetFrame(stack_size_log, 1, 3U)); + EXPECT_EQ(base + 2*stack_size + 256 * 5, fs->GetFrame(stack_size_log, 2, 5U)); + fs->Destroy(); +} + +TEST(FakeStack, Allocate) { + const uptr stack_size_log = 19; + FakeStack *fs = FakeStack::Create(stack_size_log); + std::map<FakeFrame *, uptr> s; + for (int iter = 0; iter < 2; iter++) { + s.clear(); + for (uptr cid = 0; cid < FakeStack::kNumberOfSizeClasses; cid++) { + uptr n = FakeStack::NumberOfFrames(stack_size_log, cid); + uptr bytes_in_class = FakeStack::BytesInSizeClass(cid); + for (uptr j = 0; j < n; j++) { + FakeFrame *ff = fs->Allocate(stack_size_log, cid, 0); + uptr x = reinterpret_cast<uptr>(ff); + EXPECT_TRUE(s.insert(std::make_pair(ff, cid)).second); + EXPECT_EQ(x, fs->AddrIsInFakeStack(x)); + EXPECT_EQ(x, fs->AddrIsInFakeStack(x + 1)); + EXPECT_EQ(x, fs->AddrIsInFakeStack(x + bytes_in_class - 1)); + EXPECT_NE(x, fs->AddrIsInFakeStack(x + bytes_in_class)); + } + // We are out of fake stack, so Allocate should return 0. + EXPECT_EQ(0UL, fs->Allocate(stack_size_log, cid, 0)); + } + for (std::map<FakeFrame *, uptr>::iterator it = s.begin(); it != s.end(); + ++it) { + fs->Deallocate(reinterpret_cast<uptr>(it->first), it->second); + } + } + fs->Destroy(); +} + +static void RecursiveFunction(FakeStack *fs, int depth) { + uptr class_id = depth / 3; + FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, 0); + if (depth) { + RecursiveFunction(fs, depth - 1); + RecursiveFunction(fs, depth - 1); + } + fs->Deallocate(reinterpret_cast<uptr>(ff), class_id); +} + +TEST(FakeStack, RecursiveStressTest) { + const uptr stack_size_log = 16; + FakeStack *fs = FakeStack::Create(stack_size_log); + RecursiveFunction(fs, 22); // with 26 runs for 2-3 seconds. + fs->Destroy(); +} + +} // namespace __asan diff --git a/lib/asan/tests/asan_interface_test.cc b/lib/asan/tests/asan_interface_test.cc new file mode 100644 index 000000000000..f36679012f27 --- /dev/null +++ b/lib/asan/tests/asan_interface_test.cc @@ -0,0 +1,426 @@ +//===-- asan_interface_test.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" +#include "sanitizer/asan_interface.h" + +TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) { + EXPECT_EQ(0U, __asan_get_estimated_allocated_size(0)); + const size_t sizes[] = { 1, 30, 1<<30 }; + for (size_t i = 0; i < 3; i++) { + EXPECT_EQ(sizes[i], __asan_get_estimated_allocated_size(sizes[i])); + } +} + +static const char* kGetAllocatedSizeErrorMsg = + "attempting to call __asan_get_allocated_size()"; + +TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) { + const size_t kArraySize = 100; + char *array = Ident((char*)malloc(kArraySize)); + int *int_ptr = Ident(new int); + + // Allocated memory is owned by allocator. Allocated size should be + // equal to requested size. + EXPECT_EQ(true, __asan_get_ownership(array)); + EXPECT_EQ(kArraySize, __asan_get_allocated_size(array)); + EXPECT_EQ(true, __asan_get_ownership(int_ptr)); + EXPECT_EQ(sizeof(int), __asan_get_allocated_size(int_ptr)); + + // We cannot call GetAllocatedSize from the memory we didn't map, + // and from the interior pointers (not returned by previous malloc). + void *wild_addr = (void*)0x1; + EXPECT_FALSE(__asan_get_ownership(wild_addr)); + EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg); + EXPECT_FALSE(__asan_get_ownership(array + kArraySize / 2)); + EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2), + kGetAllocatedSizeErrorMsg); + + // NULL is not owned, but is a valid argument for __asan_get_allocated_size(). + EXPECT_FALSE(__asan_get_ownership(NULL)); + EXPECT_EQ(0U, __asan_get_allocated_size(NULL)); + + // When memory is freed, it's not owned, and call to GetAllocatedSize + // is forbidden. + free(array); + EXPECT_FALSE(__asan_get_ownership(array)); + EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg); + delete int_ptr; + + void *zero_alloc = Ident(malloc(0)); + if (zero_alloc != 0) { + // If malloc(0) is not null, this pointer is owned and should have valid + // allocated size. + EXPECT_TRUE(__asan_get_ownership(zero_alloc)); + // Allocated size is 0 or 1 depending on the allocator used. + EXPECT_LT(__asan_get_allocated_size(zero_alloc), 2U); + } + free(zero_alloc); +} + +TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) { + size_t before_malloc, after_malloc, after_free; + char *array; + const size_t kMallocSize = 100; + before_malloc = __asan_get_current_allocated_bytes(); + + array = Ident((char*)malloc(kMallocSize)); + after_malloc = __asan_get_current_allocated_bytes(); + EXPECT_EQ(before_malloc + kMallocSize, after_malloc); + + free(array); + after_free = __asan_get_current_allocated_bytes(); + EXPECT_EQ(before_malloc, after_free); +} + +TEST(AddressSanitizerInterface, GetHeapSizeTest) { + // asan_allocator2 does not keep huge chunks in free list, but unmaps them. + // The chunk should be greater than the quarantine size, + // otherwise it will be stuck in quarantine instead of being unmaped. + static const size_t kLargeMallocSize = (1 << 28) + 1; // 256M + free(Ident(malloc(kLargeMallocSize))); // Drain quarantine. + size_t old_heap_size = __asan_get_heap_size(); + for (int i = 0; i < 3; i++) { + // fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); + free(Ident(malloc(kLargeMallocSize))); + EXPECT_EQ(old_heap_size, __asan_get_heap_size()); + } +} + +static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357}; +static const size_t kManyThreadsIterations = 250; +static const size_t kManyThreadsNumThreads = + (SANITIZER_WORDSIZE == 32) ? 40 : 200; + +static void *ManyThreadsWithStatsWorker(void *arg) { + (void)arg; + for (size_t iter = 0; iter < kManyThreadsIterations; iter++) { + for (size_t size_index = 0; size_index < 4; size_index++) { + free(Ident(malloc(kManyThreadsMallocSizes[size_index]))); + } + } + // Just one large allocation. + free(Ident(malloc(1 << 20))); + return 0; +} + +TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) { + size_t before_test, after_test, i; + pthread_t threads[kManyThreadsNumThreads]; + before_test = __asan_get_current_allocated_bytes(); + for (i = 0; i < kManyThreadsNumThreads; i++) { + PTHREAD_CREATE(&threads[i], 0, + (void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i); + } + for (i = 0; i < kManyThreadsNumThreads; i++) { + PTHREAD_JOIN(threads[i], 0); + } + after_test = __asan_get_current_allocated_bytes(); + // ASan stats also reflect memory usage of internal ASan RTL structs, + // so we can't check for equality here. + EXPECT_LT(after_test, before_test + (1UL<<20)); +} + +static void DoDoubleFree() { + int *x = Ident(new int); + delete Ident(x); + delete Ident(x); +} + +TEST(AddressSanitizerInterface, ExitCode) { + int original_exit_code = __asan_set_error_exit_code(7); + EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), ""); + EXPECT_EQ(7, __asan_set_error_exit_code(8)); + EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), ""); + EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code)); + EXPECT_EXIT(DoDoubleFree(), + ::testing::ExitedWithCode(original_exit_code), ""); +} + +static void MyDeathCallback() { + fprintf(stderr, "MyDeathCallback\n"); +} + +TEST(AddressSanitizerInterface, DeathCallbackTest) { + __asan_set_death_callback(MyDeathCallback); + EXPECT_DEATH(DoDoubleFree(), "MyDeathCallback"); + __asan_set_death_callback(NULL); +} + +static const char* kUseAfterPoisonErrorMessage = "use-after-poison"; + +#define GOOD_ACCESS(ptr, offset) \ + EXPECT_FALSE(__asan_address_is_poisoned(ptr + offset)) + +#define BAD_ACCESS(ptr, offset) \ + EXPECT_TRUE(__asan_address_is_poisoned(ptr + offset)) + +TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) { + char *array = Ident((char*)malloc(120)); + // poison array[40..80) + __asan_poison_memory_region(array + 40, 40); + GOOD_ACCESS(array, 39); + GOOD_ACCESS(array, 80); + BAD_ACCESS(array, 40); + BAD_ACCESS(array, 60); + BAD_ACCESS(array, 79); + char value; + EXPECT_DEATH(value = Ident(array[40]), kUseAfterPoisonErrorMessage); + __asan_unpoison_memory_region(array + 40, 40); + // access previously poisoned memory. + GOOD_ACCESS(array, 40); + GOOD_ACCESS(array, 79); + free(array); +} + +TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) { + char *array = Ident((char*)malloc(120)); + // Poison [0..40) and [80..120) + __asan_poison_memory_region(array, 40); + __asan_poison_memory_region(array + 80, 40); + BAD_ACCESS(array, 20); + GOOD_ACCESS(array, 60); + BAD_ACCESS(array, 100); + // Poison whole array - [0..120) + __asan_poison_memory_region(array, 120); + BAD_ACCESS(array, 60); + // Unpoison [24..96) + __asan_unpoison_memory_region(array + 24, 72); + BAD_ACCESS(array, 23); + GOOD_ACCESS(array, 24); + GOOD_ACCESS(array, 60); + GOOD_ACCESS(array, 95); + BAD_ACCESS(array, 96); + free(array); +} + +TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { + // Vector of capacity 20 + char *vec = Ident((char*)malloc(20)); + __asan_poison_memory_region(vec, 20); + for (size_t i = 0; i < 7; i++) { + // Simulate push_back. + __asan_unpoison_memory_region(vec + i, 1); + GOOD_ACCESS(vec, i); + BAD_ACCESS(vec, i + 1); + } + for (size_t i = 7; i > 0; i--) { + // Simulate pop_back. + __asan_poison_memory_region(vec + i - 1, 1); + BAD_ACCESS(vec, i - 1); + if (i > 1) GOOD_ACCESS(vec, i - 2); + } + free(vec); +} + +// Make sure that each aligned block of size "2^granularity" doesn't have +// "true" value before "false" value. +static void MakeShadowValid(bool *shadow, int length, int granularity) { + bool can_be_poisoned = true; + for (int i = length - 1; i >= 0; i--) { + if (!shadow[i]) + can_be_poisoned = false; + if (!can_be_poisoned) + shadow[i] = false; + if (i % (1 << granularity) == 0) { + can_be_poisoned = true; + } + } +} + +TEST(AddressSanitizerInterface, PoisoningStressTest) { + const size_t kSize = 24; + bool expected[kSize]; + char *arr = Ident((char*)malloc(kSize)); + for (size_t l1 = 0; l1 < kSize; l1++) { + for (size_t s1 = 1; l1 + s1 <= kSize; s1++) { + for (size_t l2 = 0; l2 < kSize; l2++) { + for (size_t s2 = 1; l2 + s2 <= kSize; s2++) { + // Poison [l1, l1+s1), [l2, l2+s2) and check result. + __asan_unpoison_memory_region(arr, kSize); + __asan_poison_memory_region(arr + l1, s1); + __asan_poison_memory_region(arr + l2, s2); + memset(expected, false, kSize); + memset(expected + l1, true, s1); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + memset(expected + l2, true, s2); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + for (size_t i = 0; i < kSize; i++) { + ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); + } + // Unpoison [l1, l1+s1) and [l2, l2+s2) and check result. + __asan_poison_memory_region(arr, kSize); + __asan_unpoison_memory_region(arr + l1, s1); + __asan_unpoison_memory_region(arr + l2, s2); + memset(expected, true, kSize); + memset(expected + l1, false, s1); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + memset(expected + l2, false, s2); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + for (size_t i = 0; i < kSize; i++) { + ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); + } + } + } + } + } + free(arr); +} + +TEST(AddressSanitizerInterface, GlobalRedzones) { + GOOD_ACCESS(glob1, 1 - 1); + GOOD_ACCESS(glob2, 2 - 1); + GOOD_ACCESS(glob3, 3 - 1); + GOOD_ACCESS(glob4, 4 - 1); + GOOD_ACCESS(glob5, 5 - 1); + GOOD_ACCESS(glob6, 6 - 1); + GOOD_ACCESS(glob7, 7 - 1); + GOOD_ACCESS(glob8, 8 - 1); + GOOD_ACCESS(glob9, 9 - 1); + GOOD_ACCESS(glob10, 10 - 1); + GOOD_ACCESS(glob11, 11 - 1); + GOOD_ACCESS(glob12, 12 - 1); + GOOD_ACCESS(glob13, 13 - 1); + GOOD_ACCESS(glob14, 14 - 1); + GOOD_ACCESS(glob15, 15 - 1); + GOOD_ACCESS(glob16, 16 - 1); + GOOD_ACCESS(glob17, 17 - 1); + GOOD_ACCESS(glob1000, 1000 - 1); + GOOD_ACCESS(glob10000, 10000 - 1); + GOOD_ACCESS(glob100000, 100000 - 1); + + BAD_ACCESS(glob1, 1); + BAD_ACCESS(glob2, 2); + BAD_ACCESS(glob3, 3); + BAD_ACCESS(glob4, 4); + BAD_ACCESS(glob5, 5); + BAD_ACCESS(glob6, 6); + BAD_ACCESS(glob7, 7); + BAD_ACCESS(glob8, 8); + BAD_ACCESS(glob9, 9); + BAD_ACCESS(glob10, 10); + BAD_ACCESS(glob11, 11); + BAD_ACCESS(glob12, 12); + BAD_ACCESS(glob13, 13); + BAD_ACCESS(glob14, 14); + BAD_ACCESS(glob15, 15); + BAD_ACCESS(glob16, 16); + BAD_ACCESS(glob17, 17); + BAD_ACCESS(glob1000, 1000); + BAD_ACCESS(glob1000, 1100); // Redzone is at least 101 bytes. + BAD_ACCESS(glob10000, 10000); + BAD_ACCESS(glob10000, 11000); // Redzone is at least 1001 bytes. + BAD_ACCESS(glob100000, 100000); + BAD_ACCESS(glob100000, 110000); // Redzone is at least 10001 bytes. +} + +TEST(AddressSanitizerInterface, PoisonedRegion) { + size_t rz = 16; + for (size_t size = 1; size <= 64; size++) { + char *p = new char[size]; + for (size_t beg = 0; beg < size + rz; beg++) { + for (size_t end = beg; end < size + rz; end++) { + void *first_poisoned = __asan_region_is_poisoned(p + beg, end - beg); + if (beg == end) { + EXPECT_FALSE(first_poisoned); + } else if (beg < size && end <= size) { + EXPECT_FALSE(first_poisoned); + } else if (beg >= size) { + EXPECT_EQ(p + beg, first_poisoned); + } else { + EXPECT_GT(end, size); + EXPECT_EQ(p + size, first_poisoned); + } + } + } + delete [] p; + } +} + +// This is a performance benchmark for manual runs. +// asan's memset interceptor calls mem_is_zero for the entire shadow region. +// the profile should look like this: +// 89.10% [.] __memset_sse2 +// 10.50% [.] __sanitizer::mem_is_zero +// I.e. mem_is_zero should consume ~ SHADOW_GRANULARITY less CPU cycles +// than memset itself. +TEST(AddressSanitizerInterface, DISABLED_StressLargeMemset) { + size_t size = 1 << 20; + char *x = new char[size]; + for (int i = 0; i < 100000; i++) + Ident(memset)(x, 0, size); + delete [] x; +} + +// Same here, but we run memset with small sizes. +TEST(AddressSanitizerInterface, DISABLED_StressSmallMemset) { + size_t size = 32; + char *x = new char[size]; + for (int i = 0; i < 100000000; i++) + Ident(memset)(x, 0, size); + delete [] x; +} +static const char *kInvalidPoisonMessage = "invalid-poison-memory-range"; +static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range"; + +TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) { + char *array = Ident((char*)malloc(120)); + __asan_unpoison_memory_region(array, 120); + // Try to unpoison not owned memory + EXPECT_DEATH(__asan_unpoison_memory_region(array, 121), + kInvalidUnpoisonMessage); + EXPECT_DEATH(__asan_unpoison_memory_region(array - 1, 120), + kInvalidUnpoisonMessage); + + __asan_poison_memory_region(array, 120); + // Try to poison not owned memory. + EXPECT_DEATH(__asan_poison_memory_region(array, 121), kInvalidPoisonMessage); + EXPECT_DEATH(__asan_poison_memory_region(array - 1, 120), + kInvalidPoisonMessage); + free(array); +} + +static void ErrorReportCallbackOneToZ(const char *report) { + 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(0, 0, 0, 0, true, 1), + ASAN_PCRE_DOTALL "ABCDEF.*AddressSanitizer.*WRITE.*ABCDEF"); + __asan_set_error_report_callback(NULL); +} + +TEST(AddressSanitizerInterface, GetOwnershipStressTest) { + std::vector<char *> pointers; + std::vector<size_t> sizes; + const size_t kNumMallocs = 1 << 9; + for (size_t i = 0; i < kNumMallocs; i++) { + size_t size = i * 100 + 1; + pointers.push_back((char*)malloc(size)); + sizes.push_back(size); + } + for (size_t i = 0; i < 4000000; i++) { + EXPECT_FALSE(__asan_get_ownership(&pointers)); + EXPECT_FALSE(__asan_get_ownership((void*)0x1234)); + size_t idx = i % kNumMallocs; + EXPECT_TRUE(__asan_get_ownership(pointers[idx])); + EXPECT_EQ(sizes[idx], __asan_get_allocated_size(pointers[idx])); + } + for (size_t i = 0, n = pointers.size(); i < n; i++) + free(pointers[i]); +} + diff --git a/lib/asan/tests/asan_mac_test_helpers.mm b/lib/asan/tests/asan_mac_test_helpers.mm index 4cbd2bb247fd..a7e4b9d1928b 100644 --- a/lib/asan/tests/asan_mac_test_helpers.mm +++ b/lib/asan/tests/asan_mac_test_helpers.mm @@ -1,5 +1,6 @@ // Mac OS X 10.6 or higher only. #include <dispatch/dispatch.h> +#include <pthread.h> // for pthread_yield_np() #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -11,26 +12,26 @@ // This is a (void*)(void*) function so it can be passed to pthread_create. void *CFAllocatorDefaultDoubleFree(void *unused) { - void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0); + void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0); CFAllocatorDeallocate(kCFAllocatorDefault, mem); CFAllocatorDeallocate(kCFAllocatorDefault, mem); return 0; } void CFAllocatorSystemDefaultDoubleFree() { - void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0); + void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0); CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); } void CFAllocatorMallocDoubleFree() { - void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0); + void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0); CFAllocatorDeallocate(kCFAllocatorMalloc, mem); CFAllocatorDeallocate(kCFAllocatorMalloc, mem); } void CFAllocatorMallocZoneDoubleFree() { - void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0); + void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0); CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); } @@ -77,10 +78,17 @@ void worker_do_crash(int size) { free(mem); } +// Used by the GCD tests to avoid a race between the worker thread reporting a +// memory error and the main thread which may exit with exit code 0 before +// that. +void wait_forever() { + volatile bool infinite = true; + while (infinite) pthread_yield_np(); +} + // Tests for the Grand Central Dispatch. See // http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html // for the reference. - void TestGCDDispatchAsync() { dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_block_t block = ^{ worker_do_crash(1024); }; @@ -88,8 +96,7 @@ void TestGCDDispatchAsync() { // pthread_create(). We need to verify that AddressSanitizer notices that the // thread has started. dispatch_async(queue, block); - // TODO(glider): this is hacky. Need to wait for the worker instead. - sleep(1); + wait_forever(); } void TestGCDDispatchSync() { @@ -99,8 +106,7 @@ void TestGCDDispatchSync() { // pthread_create(). We need to verify that AddressSanitizer notices that the // thread has started. dispatch_sync(queue, block); - // TODO(glider): this is hacky. Need to wait for the worker instead. - sleep(1); + wait_forever(); } // libdispatch spawns a rather small number of threads and reuses them. We need @@ -113,8 +119,7 @@ void TestGCDReuseWqthreadsAsync() { dispatch_async(queue, block_alloc); } dispatch_async(queue, block_crash); - // TODO(glider): this is hacky. Need to wait for the workers instead. - sleep(1); + wait_forever(); } // Try to trigger abnormal behaviour of dispatch_sync() being unhandled by us. @@ -130,8 +135,7 @@ void TestGCDReuseWqthreadsSync() { dispatch_sync(queue[i % 4], block_alloc); } dispatch_sync(queue[3], block_crash); - // TODO(glider): this is hacky. Need to wait for the workers instead. - sleep(1); + wait_forever(); } void TestGCDDispatchAfter() { @@ -141,9 +145,7 @@ void TestGCDDispatchAfter() { dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); dispatch_after(milestone, queue, block_crash); - // Let's wait for a bit longer now. - // TODO(glider): this is still hacky. - sleep(2); + wait_forever(); } void worker_do_deallocate(void *ptr) { @@ -172,7 +174,7 @@ void TestGCDSourceEvent() { access_memory(&mem[10]); }); dispatch_resume(timer); - sleep(2); + wait_forever(); } void TestGCDSourceCancel() { @@ -196,7 +198,7 @@ void TestGCDSourceCancel() { access_memory(&mem[10]); }); dispatch_resume(timer); - sleep(2); + wait_forever(); } void TestGCDGroupAsync() { @@ -207,6 +209,7 @@ void TestGCDGroupAsync() { access_memory(&mem[10]); }); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + wait_forever(); } @interface FixedArray : NSObject { diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc index 54fdd1979b8e..cb6223c09a46 100644 --- a/lib/asan/tests/asan_noinst_test.cc +++ b/lib/asan/tests/asan_noinst_test.cc @@ -25,6 +25,46 @@ #include <vector> #include <limits> +#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1 +// Manually set correct ASan mapping scale and offset, as they won't be +// exported from instrumented sources (there are none). +# define FLEXIBLE_SHADOW_SCALE kDefaultShadowScale +# if SANITIZER_ANDROID +# define FLEXIBLE_SHADOW_OFFSET (0) +# else +# if SANITIZER_WORDSIZE == 32 +# if defined(__mips__) +# define FLEXIBLE_SHADOW_OFFSET kMIPS32_ShadowOffset32 +# else +# define FLEXIBLE_SHADOW_OFFSET kDefaultShadowOffset32 +# endif +# else +# if defined(__powerpc64__) +# define FLEXIBLE_SHADOW_OFFSET kPPC64_ShadowOffset64 +# elif SANITIZER_MAC +# define FLEXIBLE_SHADOW_OFFSET kDefaultShadowOffset64 +# else +# define FLEXIBLE_SHADOW_OFFSET kDefaultShort64bitShadowOffset +# endif +# endif +# endif +SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale = FLEXIBLE_SHADOW_SCALE; +SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset = + FLEXIBLE_SHADOW_OFFSET; +#endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET + +extern "C" { +// Set specific ASan options for uninstrumented unittest. +const char* __asan_default_options() { + return "allow_reexec=0"; +} +} // extern "C" + +// Make sure __asan_init is called before any test case is run. +struct AsanInitCaller { + AsanInitCaller() { __asan_init(); } +}; +static AsanInitCaller asan_init_caller; TEST(AddressSanitizer, InternalSimpleDeathTest) { EXPECT_DEATH(exit(1), ""); @@ -66,6 +106,7 @@ static void MallocStress(size_t n) { size_t alignment = 1 << (my_rand_r(&seed) % 10 + 1); char *ptr = (char*)__asan::asan_memalign(alignment, size, &stack2, __asan::FROM_MALLOC); + EXPECT_EQ(size, __asan::asan_malloc_usable_size(ptr, 0, 0)); vec.push_back(ptr); ptr[0] = 0; ptr[size-1] = 0; @@ -118,144 +159,6 @@ TEST(AddressSanitizer, DISABLED_InternalPrintShadow) { } } -static uptr pc_array[] = { -#if SANITIZER_WORDSIZE == 64 - 0x7effbf756068ULL, - 0x7effbf75e5abULL, - 0x7effc0625b7cULL, - 0x7effc05b8997ULL, - 0x7effbf990577ULL, - 0x7effbf990c56ULL, - 0x7effbf992f3cULL, - 0x7effbf950c22ULL, - 0x7effc036dba0ULL, - 0x7effc03638a3ULL, - 0x7effc035be4aULL, - 0x7effc0539c45ULL, - 0x7effc0539a65ULL, - 0x7effc03db9b3ULL, - 0x7effc03db100ULL, - 0x7effc037c7b8ULL, - 0x7effc037bfffULL, - 0x7effc038b777ULL, - 0x7effc038021cULL, - 0x7effc037c7d1ULL, - 0x7effc037bfffULL, - 0x7effc038b777ULL, - 0x7effc038021cULL, - 0x7effc037c7d1ULL, - 0x7effc037bfffULL, - 0x7effc038b777ULL, - 0x7effc038021cULL, - 0x7effc037c7d1ULL, - 0x7effc037bfffULL, - 0x7effc0520d26ULL, - 0x7effc009ddffULL, - 0x7effbf90bb50ULL, - 0x7effbdddfa69ULL, - 0x7effbdde1fe2ULL, - 0x7effbdde2424ULL, - 0x7effbdde27b3ULL, - 0x7effbddee53bULL, - 0x7effbdde1988ULL, - 0x7effbdde0904ULL, - 0x7effc106ce0dULL, - 0x7effbcc3fa04ULL, - 0x7effbcc3f6a4ULL, - 0x7effbcc3e726ULL, - 0x7effbcc40852ULL, - 0x7effb681ec4dULL, -#endif // SANITIZER_WORDSIZE - 0xB0B5E768, - 0x7B682EC1, - 0x367F9918, - 0xAE34E13, - 0xBA0C6C6, - 0x13250F46, - 0xA0D6A8AB, - 0x2B07C1A8, - 0x6C844F4A, - 0x2321B53, - 0x1F3D4F8F, - 0x3FE2924B, - 0xB7A2F568, - 0xBD23950A, - 0x61020930, - 0x33E7970C, - 0x405998A1, - 0x59F3551D, - 0x350E3028, - 0xBC55A28D, - 0x361F3AED, - 0xBEAD0F73, - 0xAEF28479, - 0x757E971F, - 0xAEBA450, - 0x43AD22F5, - 0x8C2C50C4, - 0x7AD8A2E1, - 0x69EE4EE8, - 0xC08DFF, - 0x4BA6538, - 0x3708AB2, - 0xC24B6475, - 0x7C8890D7, - 0x6662495F, - 0x9B641689, - 0xD3596B, - 0xA1049569, - 0x44CBC16, - 0x4D39C39F -}; - -void CompressStackTraceTest(size_t n_iter) { - u32 seed = my_rand(); - const size_t kNumPcs = ARRAY_SIZE(pc_array); - u32 compressed[2 * kNumPcs]; - - for (size_t iter = 0; iter < n_iter; iter++) { - std::random_shuffle(pc_array, pc_array + kNumPcs); - StackTrace stack0, stack1; - stack0.CopyFrom(pc_array, kNumPcs); - stack0.size = std::max((size_t)1, (size_t)(my_rand_r(&seed) % stack0.size)); - size_t compress_size = - std::max((size_t)2, (size_t)my_rand_r(&seed) % (2 * kNumPcs)); - size_t n_frames = - StackTrace::CompressStack(&stack0, compressed, compress_size); - Ident(n_frames); - assert(n_frames <= stack0.size); - StackTrace::UncompressStack(&stack1, compressed, compress_size); - assert(stack1.size == n_frames); - for (size_t i = 0; i < stack1.size; i++) { - assert(stack0.trace[i] == stack1.trace[i]); - } - } -} - -TEST(AddressSanitizer, CompressStackTraceTest) { - CompressStackTraceTest(10000); -} - -void CompressStackTraceBenchmark(size_t n_iter) { - const size_t kNumPcs = ARRAY_SIZE(pc_array); - u32 compressed[2 * kNumPcs]; - std::random_shuffle(pc_array, pc_array + kNumPcs); - - StackTrace stack0; - stack0.CopyFrom(pc_array, kNumPcs); - stack0.size = kNumPcs; - for (size_t iter = 0; iter < n_iter; iter++) { - size_t compress_size = kNumPcs; - size_t n_frames = - StackTrace::CompressStack(&stack0, compressed, compress_size); - Ident(n_frames); - } -} - -TEST(AddressSanitizer, CompressStackTraceBenchmark) { - CompressStackTraceBenchmark(1 << 24); -} - TEST(AddressSanitizer, QuarantineTest) { StackTrace stack; stack.trace[0] = 0x890; @@ -332,463 +235,13 @@ TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) { } } -TEST(AddressSanitizer, MemsetWildAddressTest) { +TEST(AddressSanitizer, ShadowRegionIsPoisonedTest) { using __asan::kHighMemEnd; - typedef void*(*memset_p)(void*, int, size_t); - // Prevent inlining of memset(). - volatile memset_p libc_memset = (memset_p)memset; - EXPECT_DEATH(libc_memset((void*)(kLowShadowBeg + 200), 0, 100), - (kLowShadowEnd == 0) ? "unknown-crash.*shadow gap" - : "unknown-crash.*low shadow"); - EXPECT_DEATH(libc_memset((void*)(kShadowGapBeg + 200), 0, 100), - "unknown-crash.*shadow gap"); - EXPECT_DEATH(libc_memset((void*)(kHighShadowBeg + 200), 0, 100), - "unknown-crash.*high shadow"); -} - -TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) { - EXPECT_EQ(0U, __asan_get_estimated_allocated_size(0)); - const size_t sizes[] = { 1, 30, 1<<30 }; - for (size_t i = 0; i < 3; i++) { - EXPECT_EQ(sizes[i], __asan_get_estimated_allocated_size(sizes[i])); - } -} - -static const char* kGetAllocatedSizeErrorMsg = - "attempting to call __asan_get_allocated_size()"; - -TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) { - const size_t kArraySize = 100; - char *array = Ident((char*)malloc(kArraySize)); - int *int_ptr = Ident(new int); - - // Allocated memory is owned by allocator. Allocated size should be - // equal to requested size. - EXPECT_EQ(true, __asan_get_ownership(array)); - EXPECT_EQ(kArraySize, __asan_get_allocated_size(array)); - EXPECT_EQ(true, __asan_get_ownership(int_ptr)); - EXPECT_EQ(sizeof(int), __asan_get_allocated_size(int_ptr)); - - // We cannot call GetAllocatedSize from the memory we didn't map, - // and from the interior pointers (not returned by previous malloc). - void *wild_addr = (void*)0x1; - EXPECT_FALSE(__asan_get_ownership(wild_addr)); - EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg); - EXPECT_FALSE(__asan_get_ownership(array + kArraySize / 2)); - EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2), - kGetAllocatedSizeErrorMsg); - - // NULL is not owned, but is a valid argument for __asan_get_allocated_size(). - EXPECT_FALSE(__asan_get_ownership(NULL)); - EXPECT_EQ(0U, __asan_get_allocated_size(NULL)); - - // When memory is freed, it's not owned, and call to GetAllocatedSize - // is forbidden. - free(array); - EXPECT_FALSE(__asan_get_ownership(array)); - EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg); - delete int_ptr; - - void *zero_alloc = Ident(malloc(0)); - if (zero_alloc != 0) { - // If malloc(0) is not null, this pointer is owned and should have valid - // allocated size. - EXPECT_TRUE(__asan_get_ownership(zero_alloc)); - // Allocated size is 0 or 1 depending on the allocator used. - EXPECT_LT(__asan_get_allocated_size(zero_alloc), 2U); - } - free(zero_alloc); -} - -TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) { - size_t before_malloc, after_malloc, after_free; - char *array; - const size_t kMallocSize = 100; - before_malloc = __asan_get_current_allocated_bytes(); - - array = Ident((char*)malloc(kMallocSize)); - after_malloc = __asan_get_current_allocated_bytes(); - EXPECT_EQ(before_malloc + kMallocSize, after_malloc); - - free(array); - after_free = __asan_get_current_allocated_bytes(); - EXPECT_EQ(before_malloc, after_free); -} - -static void DoDoubleFree() { - int *x = Ident(new int); - delete Ident(x); - delete Ident(x); -} - -TEST(AddressSanitizerInterface, GetHeapSizeTest) { - // asan_allocator2 does not keep huge chunks in free list, but unmaps them. - // The chunk should be greater than the quarantine size, - // otherwise it will be stuck in quarantine instead of being unmaped. - static const size_t kLargeMallocSize = (1 << 28) + 1; // 256M - free(Ident(malloc(kLargeMallocSize))); // Drain quarantine. - uptr old_heap_size = __asan_get_heap_size(); - for (int i = 0; i < 3; i++) { - // fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); - free(Ident(malloc(kLargeMallocSize))); - EXPECT_EQ(old_heap_size, __asan_get_heap_size()); - } -} - -static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357}; -static const size_t kManyThreadsIterations = 250; -static const size_t kManyThreadsNumThreads = - (SANITIZER_WORDSIZE == 32) ? 40 : 200; - -void *ManyThreadsWithStatsWorker(void *arg) { - (void)arg; - for (size_t iter = 0; iter < kManyThreadsIterations; iter++) { - for (size_t size_index = 0; size_index < 4; size_index++) { - free(Ident(malloc(kManyThreadsMallocSizes[size_index]))); - } - } - // Just one large allocation. - free(Ident(malloc(1 << 20))); - return 0; -} - -TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) { - size_t before_test, after_test, i; - pthread_t threads[kManyThreadsNumThreads]; - before_test = __asan_get_current_allocated_bytes(); - for (i = 0; i < kManyThreadsNumThreads; i++) { - PTHREAD_CREATE(&threads[i], 0, - (void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i); - } - for (i = 0; i < kManyThreadsNumThreads; i++) { - PTHREAD_JOIN(threads[i], 0); - } - after_test = __asan_get_current_allocated_bytes(); - // ASan stats also reflect memory usage of internal ASan RTL structs, - // so we can't check for equality here. - EXPECT_LT(after_test, before_test + (1UL<<20)); -} - -TEST(AddressSanitizerInterface, ExitCode) { - int original_exit_code = __asan_set_error_exit_code(7); - EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), ""); - EXPECT_EQ(7, __asan_set_error_exit_code(8)); - EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), ""); - EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code)); - EXPECT_EXIT(DoDoubleFree(), - ::testing::ExitedWithCode(original_exit_code), ""); -} - -static void MyDeathCallback() { - fprintf(stderr, "MyDeathCallback\n"); -} - -TEST(AddressSanitizerInterface, DeathCallbackTest) { - __asan_set_death_callback(MyDeathCallback); - EXPECT_DEATH(DoDoubleFree(), "MyDeathCallback"); - __asan_set_death_callback(NULL); -} - -static const char* kUseAfterPoisonErrorMessage = "use-after-poison"; - -#define GOOD_ACCESS(ptr, offset) \ - EXPECT_FALSE(__asan::AddressIsPoisoned((uptr)(ptr + offset))) - -#define BAD_ACCESS(ptr, offset) \ - EXPECT_TRUE(__asan::AddressIsPoisoned((uptr)(ptr + offset))) - -TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) { - char *array = Ident((char*)malloc(120)); - // poison array[40..80) - __asan_poison_memory_region(array + 40, 40); - GOOD_ACCESS(array, 39); - GOOD_ACCESS(array, 80); - BAD_ACCESS(array, 40); - BAD_ACCESS(array, 60); - BAD_ACCESS(array, 79); - EXPECT_DEATH(__asan_report_error(0, 0, 0, (uptr)(array + 40), true, 1), - kUseAfterPoisonErrorMessage); - __asan_unpoison_memory_region(array + 40, 40); - // access previously poisoned memory. - GOOD_ACCESS(array, 40); - GOOD_ACCESS(array, 79); - free(array); -} - -TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) { - char *array = Ident((char*)malloc(120)); - // Poison [0..40) and [80..120) - __asan_poison_memory_region(array, 40); - __asan_poison_memory_region(array + 80, 40); - BAD_ACCESS(array, 20); - GOOD_ACCESS(array, 60); - BAD_ACCESS(array, 100); - // Poison whole array - [0..120) - __asan_poison_memory_region(array, 120); - BAD_ACCESS(array, 60); - // Unpoison [24..96) - __asan_unpoison_memory_region(array + 24, 72); - BAD_ACCESS(array, 23); - GOOD_ACCESS(array, 24); - GOOD_ACCESS(array, 60); - GOOD_ACCESS(array, 95); - BAD_ACCESS(array, 96); - free(array); -} - -TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { - // Vector of capacity 20 - char *vec = Ident((char*)malloc(20)); - __asan_poison_memory_region(vec, 20); - for (size_t i = 0; i < 7; i++) { - // Simulate push_back. - __asan_unpoison_memory_region(vec + i, 1); - GOOD_ACCESS(vec, i); - BAD_ACCESS(vec, i + 1); - } - for (size_t i = 7; i > 0; i--) { - // Simulate pop_back. - __asan_poison_memory_region(vec + i - 1, 1); - BAD_ACCESS(vec, i - 1); - if (i > 1) GOOD_ACCESS(vec, i - 2); - } - free(vec); -} - -TEST(AddressSanitizerInterface, GlobalRedzones) { - GOOD_ACCESS(glob1, 1 - 1); - GOOD_ACCESS(glob2, 2 - 1); - GOOD_ACCESS(glob3, 3 - 1); - GOOD_ACCESS(glob4, 4 - 1); - GOOD_ACCESS(glob5, 5 - 1); - GOOD_ACCESS(glob6, 6 - 1); - GOOD_ACCESS(glob7, 7 - 1); - GOOD_ACCESS(glob8, 8 - 1); - GOOD_ACCESS(glob9, 9 - 1); - GOOD_ACCESS(glob10, 10 - 1); - GOOD_ACCESS(glob11, 11 - 1); - GOOD_ACCESS(glob12, 12 - 1); - GOOD_ACCESS(glob13, 13 - 1); - GOOD_ACCESS(glob14, 14 - 1); - GOOD_ACCESS(glob15, 15 - 1); - GOOD_ACCESS(glob16, 16 - 1); - GOOD_ACCESS(glob17, 17 - 1); - GOOD_ACCESS(glob1000, 1000 - 1); - GOOD_ACCESS(glob10000, 10000 - 1); - GOOD_ACCESS(glob100000, 100000 - 1); - - BAD_ACCESS(glob1, 1); - BAD_ACCESS(glob2, 2); - BAD_ACCESS(glob3, 3); - BAD_ACCESS(glob4, 4); - BAD_ACCESS(glob5, 5); - BAD_ACCESS(glob6, 6); - BAD_ACCESS(glob7, 7); - BAD_ACCESS(glob8, 8); - BAD_ACCESS(glob9, 9); - BAD_ACCESS(glob10, 10); - BAD_ACCESS(glob11, 11); - BAD_ACCESS(glob12, 12); - BAD_ACCESS(glob13, 13); - BAD_ACCESS(glob14, 14); - BAD_ACCESS(glob15, 15); - BAD_ACCESS(glob16, 16); - BAD_ACCESS(glob17, 17); - BAD_ACCESS(glob1000, 1000); - BAD_ACCESS(glob1000, 1100); // Redzone is at least 101 bytes. - BAD_ACCESS(glob10000, 10000); - BAD_ACCESS(glob10000, 11000); // Redzone is at least 1001 bytes. - BAD_ACCESS(glob100000, 100000); - BAD_ACCESS(glob100000, 110000); // Redzone is at least 10001 bytes. -} - -// Make sure that each aligned block of size "2^granularity" doesn't have -// "true" value before "false" value. -static void MakeShadowValid(bool *shadow, int length, int granularity) { - bool can_be_poisoned = true; - for (int i = length - 1; i >= 0; i--) { - if (!shadow[i]) - can_be_poisoned = false; - if (!can_be_poisoned) - shadow[i] = false; - if (i % (1 << granularity) == 0) { - can_be_poisoned = true; - } - } -} - -TEST(AddressSanitizerInterface, PoisoningStressTest) { - const size_t kSize = 24; - bool expected[kSize]; - char *arr = Ident((char*)malloc(kSize)); - for (size_t l1 = 0; l1 < kSize; l1++) { - for (size_t s1 = 1; l1 + s1 <= kSize; s1++) { - for (size_t l2 = 0; l2 < kSize; l2++) { - for (size_t s2 = 1; l2 + s2 <= kSize; s2++) { - // Poison [l1, l1+s1), [l2, l2+s2) and check result. - __asan_unpoison_memory_region(arr, kSize); - __asan_poison_memory_region(arr + l1, s1); - __asan_poison_memory_region(arr + l2, s2); - memset(expected, false, kSize); - memset(expected + l1, true, s1); - MakeShadowValid(expected, kSize, /*granularity*/ 3); - memset(expected + l2, true, s2); - MakeShadowValid(expected, kSize, /*granularity*/ 3); - for (size_t i = 0; i < kSize; i++) { - ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); - } - // Unpoison [l1, l1+s1) and [l2, l2+s2) and check result. - __asan_poison_memory_region(arr, kSize); - __asan_unpoison_memory_region(arr + l1, s1); - __asan_unpoison_memory_region(arr + l2, s2); - memset(expected, true, kSize); - memset(expected + l1, false, s1); - MakeShadowValid(expected, kSize, /*granularity*/ 3); - memset(expected + l2, false, s2); - MakeShadowValid(expected, kSize, /*granularity*/ 3); - for (size_t i = 0; i < kSize; i++) { - ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); - } - } - } - } - } -} - -TEST(AddressSanitizerInterface, PoisonedRegion) { - size_t rz = 16; - for (size_t size = 1; size <= 64; size++) { - char *p = new char[size]; - uptr x = reinterpret_cast<uptr>(p); - for (size_t beg = 0; beg < size + rz; beg++) { - for (size_t end = beg; end < size + rz; end++) { - uptr first_poisoned = __asan_region_is_poisoned(x + beg, end - beg); - if (beg == end) { - EXPECT_FALSE(first_poisoned); - } else if (beg < size && end <= size) { - EXPECT_FALSE(first_poisoned); - } else if (beg >= size) { - EXPECT_EQ(x + beg, first_poisoned); - } else { - EXPECT_GT(end, size); - EXPECT_EQ(x + size, first_poisoned); - } - } - } - delete [] p; - } -} - -// This is a performance benchmark for manual runs. -// asan's memset interceptor calls mem_is_zero for the entire shadow region. -// the profile should look like this: -// 89.10% [.] __memset_sse2 -// 10.50% [.] __sanitizer::mem_is_zero -// I.e. mem_is_zero should consume ~ SHADOW_GRANULARITY less CPU cycles -// than memset itself. -TEST(AddressSanitizerInterface, DISABLED_StressLargeMemset) { - size_t size = 1 << 20; - char *x = new char[size]; - for (int i = 0; i < 100000; i++) - Ident(memset)(x, 0, size); - delete [] x; -} - -// Same here, but we run memset with small sizes. -TEST(AddressSanitizerInterface, DISABLED_StressSmallMemset) { - size_t size = 32; - char *x = new char[size]; - for (int i = 0; i < 100000000; i++) - Ident(memset)(x, 0, size); - delete [] x; -} - -static const char *kInvalidPoisonMessage = "invalid-poison-memory-range"; -static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range"; - -TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) { - char *array = Ident((char*)malloc(120)); - __asan_unpoison_memory_region(array, 120); - // Try to unpoison not owned memory - EXPECT_DEATH(__asan_unpoison_memory_region(array, 121), - kInvalidUnpoisonMessage); - EXPECT_DEATH(__asan_unpoison_memory_region(array - 1, 120), - kInvalidUnpoisonMessage); - - __asan_poison_memory_region(array, 120); - // Try to poison not owned memory. - EXPECT_DEATH(__asan_poison_memory_region(array, 121), kInvalidPoisonMessage); - EXPECT_DEATH(__asan_poison_memory_region(array - 1, 120), - kInvalidPoisonMessage); - free(array); -} - -static void ErrorReportCallbackOneToZ(const char *report) { - 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(0, 0, 0, 0, true, 1), - ASAN_PCRE_DOTALL "ABCDEF.*AddressSanitizer.*WRITE.*ABCDEF"); - __asan_set_error_report_callback(NULL); -} - -TEST(AddressSanitizerInterface, GetOwnershipStressTest) { - std::vector<char *> pointers; - std::vector<size_t> sizes; - const size_t kNumMallocs = 1 << 9; - for (size_t i = 0; i < kNumMallocs; i++) { - size_t size = i * 100 + 1; - pointers.push_back((char*)malloc(size)); - sizes.push_back(size); - } - for (size_t i = 0; i < 4000000; i++) { - EXPECT_FALSE(__asan_get_ownership(&pointers)); - EXPECT_FALSE(__asan_get_ownership((void*)0x1234)); - size_t idx = i % kNumMallocs; - EXPECT_TRUE(__asan_get_ownership(pointers[idx])); - EXPECT_EQ(sizes[idx], __asan_get_allocated_size(pointers[idx])); - } - for (size_t i = 0, n = pointers.size(); i < n; i++) - free(pointers[i]); -} - -TEST(AddressSanitizerInterface, CallocOverflow) { - size_t kArraySize = 4096; - volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); - volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; - void *p = calloc(kArraySize, kArraySize2); // Should return 0. - EXPECT_EQ(0L, Ident(p)); -} - -TEST(AddressSanitizerInterface, CallocOverflow2) { -#if SANITIZER_WORDSIZE == 32 - size_t kArraySize = 112; - volatile size_t kArraySize2 = 43878406; - void *p = calloc(kArraySize, kArraySize2); // Should return 0. - EXPECT_EQ(0L, Ident(p)); -#endif -} - -TEST(AddressSanitizerInterface, CallocReturnsZeroMem) { - size_t sizes[] = {16, 1000, 10000, 100000, 2100000}; - for (size_t s = 0; s < ARRAY_SIZE(sizes); s++) { - size_t size = sizes[s]; - for (size_t iter = 0; iter < 5; iter++) { - char *x = Ident((char*)calloc(1, size)); - EXPECT_EQ(x[0], 0); - EXPECT_EQ(x[size - 1], 0); - EXPECT_EQ(x[size / 2], 0); - EXPECT_EQ(x[size / 3], 0); - EXPECT_EQ(x[size / 4], 0); - memset(x, 0x42, size); - free(Ident(x)); - free(Ident(malloc(Ident(1 << 27)))); // Try to drain the quarantine. - } - } + // Check that __asan_region_is_poisoned works for shadow regions. + uptr ptr = kLowShadowBeg + 200; + EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); + ptr = kShadowGapBeg + 200; + EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); + ptr = kHighShadowBeg + 200; + EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); } diff --git a/lib/asan/tests/asan_str_test.cc b/lib/asan/tests/asan_str_test.cc index 128fb61c25a9..178d00d4da86 100644 --- a/lib/asan/tests/asan_str_test.cc +++ b/lib/asan/tests/asan_str_test.cc @@ -12,6 +12,10 @@ //===----------------------------------------------------------------------===// #include "asan_test_utils.h" +#if defined(__APPLE__) +#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_* +#endif + // Used for string functions tests static char global_string[] = "global"; static size_t global_string_length = 6; @@ -61,6 +65,18 @@ TEST(AddressSanitizer, StrLenOOBTest) { free(heap_string); } +TEST(AddressSanitizer, WcsLenTest) { + EXPECT_EQ(0U, wcslen(Ident(L""))); + size_t hello_len = 13; + size_t hello_size = (hello_len + 1) * sizeof(wchar_t); + EXPECT_EQ(hello_len, wcslen(Ident(L"Hello, World!"))); + wchar_t *heap_string = Ident((wchar_t*)malloc(hello_size)); + memcpy(heap_string, L"Hello, World!", hello_size); + EXPECT_EQ(hello_len, Ident(wcslen(heap_string))); + EXPECT_DEATH(Ident(wcslen(heap_string + 14)), RightOOBReadMessage(0)); + free(heap_string); +} + #ifndef __APPLE__ TEST(AddressSanitizer, StrNLenOOBTest) { size_t size = Ident(123); diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc index 5ae525de9876..6539ae8af4ea 100644 --- a/lib/asan/tests/asan_test.cc +++ b/lib/asan/tests/asan_test.cc @@ -111,6 +111,24 @@ TEST(AddressSanitizer, CallocTest) { free(a); } +TEST(AddressSanitizer, CallocReturnsZeroMem) { + size_t sizes[] = {16, 1000, 10000, 100000, 2100000}; + for (size_t s = 0; s < sizeof(sizes)/sizeof(sizes[0]); s++) { + size_t size = sizes[s]; + for (size_t iter = 0; iter < 5; iter++) { + char *x = Ident((char*)calloc(1, size)); + EXPECT_EQ(x[0], 0); + EXPECT_EQ(x[size - 1], 0); + EXPECT_EQ(x[size / 2], 0); + EXPECT_EQ(x[size / 3], 0); + EXPECT_EQ(x[size / 4], 0); + memset(x, 0x42, size); + free(Ident(x)); + free(Ident(malloc(Ident(1 << 27)))); // Try to drain the quarantine. + } + } +} + TEST(AddressSanitizer, VallocTest) { void *a = valloc(100); EXPECT_EQ(0U, (uintptr_t)a % kPageSize); @@ -227,16 +245,6 @@ TEST(AddressSanitizer, BitFieldNegativeTest) { delete Ident(x); } -TEST(AddressSanitizer, OutOfMemoryTest) { - size_t size = SANITIZER_WORDSIZE == 64 ? (size_t)(1ULL << 48) : (0xf0000000); - EXPECT_EQ(0, realloc(0, size)); - EXPECT_EQ(0, realloc(0, ~Ident(0))); - EXPECT_EQ(0, malloc(size)); - EXPECT_EQ(0, malloc(~Ident(0))); - EXPECT_EQ(0, calloc(1, size)); - EXPECT_EQ(0, calloc(1, ~Ident(0))); -} - #if ASAN_NEEDS_SEGV namespace { @@ -363,6 +371,7 @@ TEST(AddressSanitizer, ReallocFreedPointerTest) { TEST(AddressSanitizer, ReallocInvalidPointerTest) { void *ptr = Ident(malloc(42)); EXPECT_DEATH(ptr = realloc((int*)ptr + 1, 77), "attempting free.*not malloc"); + free(ptr); } TEST(AddressSanitizer, ZeroSizeMallocTest) { @@ -401,6 +410,7 @@ TEST(AddressSanitizer, MallocUsableSizeTest) { kMallocUsableSizeErrorMsg); free(array); EXPECT_DEATH(malloc_usable_size(array), kMallocUsableSizeErrorMsg); + delete int_ptr; } #endif @@ -659,7 +669,8 @@ TEST(AddressSanitizer, ThreadStackReuseTest) { PTHREAD_JOIN(t, 0); } -#if defined(__i386__) || defined(__x86_64__) +#if defined(__i686__) || defined(__x86_64__) +#include <emmintrin.h> TEST(AddressSanitizer, Store128Test) { char *a = Ident((char*)malloc(Ident(12))); char *p = a; @@ -1089,15 +1100,15 @@ TEST(AddressSanitizer, LargeStructCopyTest) { *Ident(&a) = *Ident(&a); } -ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS -static void NoAddressSafety() { +ATTRIBUTE_NO_SANITIZE_ADDRESS +static void NoSanitizeAddress() { char *foo = new char[10]; Ident(foo)[10] = 0; delete [] foo; } -TEST(AddressSanitizer, AttributeNoAddressSafetyTest) { - Ident(NoAddressSafety)(); +TEST(AddressSanitizer, AttributeNoSanitizeAddressTest) { + Ident(NoSanitizeAddress)(); } // It doesn't work on Android, as calls to new/delete go through malloc/free. @@ -1223,10 +1234,10 @@ TEST(AddressSanitizer, pthread_getschedparam) { struct sched_param param; EXPECT_DEATH( pthread_getschedparam(pthread_self(), &policy, Ident(¶m) + 2), - "AddressSanitizer: stack-buffer-overflow"); + "AddressSanitizer: stack-buffer-.*flow"); EXPECT_DEATH( pthread_getschedparam(pthread_self(), Ident(&policy) - 1, ¶m), - "AddressSanitizer: stack-buffer-overflow"); + "AddressSanitizer: stack-buffer-.*flow"); int res = pthread_getschedparam(pthread_self(), &policy, ¶m); ASSERT_EQ(0, res); } diff --git a/lib/asan/tests/asan_test_utils.h b/lib/asan/tests/asan_test_utils.h index 403773180c2f..b6bf6b82066d 100644 --- a/lib/asan/tests/asan_test_utils.h +++ b/lib/asan/tests/asan_test_utils.h @@ -41,10 +41,6 @@ #include <unistd.h> #endif -#if defined(__i386__) || defined(__x86_64__) -#include <emmintrin.h> -#endif - #ifndef __APPLE__ #include <malloc.h> #endif diff --git a/lib/dfsan/CMakeLists.txt b/lib/dfsan/CMakeLists.txt new file mode 100644 index 000000000000..b952799ffb5b --- /dev/null +++ b/lib/dfsan/CMakeLists.txt @@ -0,0 +1,44 @@ +include_directories(..) + +# Runtime library sources and build flags. +set(DFSAN_RTL_SOURCES + dfsan.cc + dfsan_custom.cc + dfsan_interceptors.cc) +set(DFSAN_RTL_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + # Prevent clang from generating libc calls. + -ffreestanding) + +# Static runtime library. +set(DFSAN_RUNTIME_LIBRARIES) +set(arch "x86_64") +if(CAN_TARGET_${arch}) + add_compiler_rt_static_runtime(clang_rt.dfsan-${arch} ${arch} + SOURCES ${DFSAN_RTL_SOURCES} + $<TARGET_OBJECTS:RTInterception.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + CFLAGS ${DFSAN_RTL_CFLAGS} -fPIE) + add_compiler_rt_static_runtime(clang_rt.dfsan-libc-${arch} ${arch} + SOURCES ${DFSAN_RTL_SOURCES} + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + CFLAGS ${DFSAN_RTL_CFLAGS} -fPIC -DDFSAN_NOLIBC) + add_sanitizer_rt_symbols(clang_rt.dfsan-${arch} dfsan.syms.extra) + list(APPEND DFSAN_RUNTIME_LIBRARIES clang_rt.dfsan-${arch} + clang_rt.dfsan-${arch}-symbols) +endif() + +add_custom_target(dfsan_abilist ALL + SOURCES ${CLANG_RESOURCE_DIR}/dfsan_abilist.txt) +add_custom_command(OUTPUT ${CLANG_RESOURCE_DIR}/dfsan_abilist.txt + VERBATIM + COMMAND + cat ${CMAKE_CURRENT_SOURCE_DIR}/done_abilist.txt + ${CMAKE_CURRENT_SOURCE_DIR}/libc_ubuntu1204_abilist.txt + > ${CLANG_RESOURCE_DIR}/dfsan_abilist.txt + DEPENDS done_abilist.txt libc_ubuntu1204_abilist.txt) +install(FILES ${CLANG_RESOURCE_DIR}/dfsan_abilist.txt + DESTINATION ${LIBCLANG_INSTALL_PATH}) + +add_subdirectory(lit_tests) diff --git a/lib/dfsan/Makefile.mk b/lib/dfsan/Makefile.mk new file mode 100644 index 000000000000..4aeaac42dea2 --- /dev/null +++ b/lib/dfsan/Makefile.mk @@ -0,0 +1,23 @@ +#===- lib/dfsan/Makefile.mk --------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := dfsan +SubDirs := + +Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) +ObjNames := $(Sources:%.cc=%.o) + +Implementation := Generic + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard $(Dir)/*.h) +Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) + +# Define a convenience variable for all the dfsan functions. +DfsanFunctions := $(Sources:%.cc=%) diff --git a/lib/dfsan/dfsan.cc b/lib/dfsan/dfsan.cc new file mode 100644 index 000000000000..72d05c68604b --- /dev/null +++ b/lib/dfsan/dfsan.cc @@ -0,0 +1,265 @@ +//===-- dfsan.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 DataFlowSanitizer. +// +// DataFlowSanitizer runtime. This file defines the public interface to +// DataFlowSanitizer as well as the definition of certain runtime functions +// called automatically by the compiler (specifically the instrumentation pass +// in llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp). +// +// The public interface is defined in include/sanitizer/dfsan_interface.h whose +// functions are prefixed dfsan_ while the compiler interface functions are +// prefixed __dfsan_. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_libc.h" + +#include "dfsan/dfsan.h" + +using namespace __dfsan; + +typedef atomic_uint16_t atomic_dfsan_label; +static const dfsan_label kInitializingLabel = -1; + +static const uptr kNumLabels = 1 << (sizeof(dfsan_label) * 8); + +static atomic_dfsan_label __dfsan_last_label; +static dfsan_label_info __dfsan_label_info[kNumLabels]; + +Flags __dfsan::flags_data; + +SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_retval_tls; +SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_arg_tls[64]; + +// On Linux/x86_64, memory is laid out as follows: +// +// +--------------------+ 0x800000000000 (top of memory) +// | application memory | +// +--------------------+ 0x700000008000 (kAppAddr) +// | | +// | unused | +// | | +// +--------------------+ 0x200200000000 (kUnusedAddr) +// | union table | +// +--------------------+ 0x200000000000 (kUnionTableAddr) +// | shadow memory | +// +--------------------+ 0x000000010000 (kShadowAddr) +// | reserved by kernel | +// +--------------------+ 0x000000000000 +// +// To derive a shadow memory address from an application memory address, +// bits 44-46 are cleared to bring the address into the range +// [0x000000008000,0x100000000000). Then the address is shifted left by 1 to +// account for the double byte representation of shadow labels and move the +// address into the shadow memory range. See the function shadow_for below. + +typedef atomic_dfsan_label dfsan_union_table_t[kNumLabels][kNumLabels]; + +static const uptr kShadowAddr = 0x10000; +static const uptr kUnionTableAddr = 0x200000000000; +static const uptr kUnusedAddr = kUnionTableAddr + sizeof(dfsan_union_table_t); +static const uptr kAppAddr = 0x700000008000; + +static atomic_dfsan_label *union_table(dfsan_label l1, dfsan_label l2) { + return &(*(dfsan_union_table_t *) kUnionTableAddr)[l1][l2]; +} + +// Resolves the union of two unequal labels. Nonequality is a precondition for +// this function (the instrumentation pass inlines the equality test). +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +dfsan_label __dfsan_union(dfsan_label l1, dfsan_label l2) { + DCHECK_NE(l1, l2); + + if (l1 == 0) + return l2; + if (l2 == 0) + return l1; + + if (l1 > l2) + Swap(l1, l2); + + atomic_dfsan_label *table_ent = union_table(l1, l2); + // We need to deal with the case where two threads concurrently request + // a union of the same pair of labels. If the table entry is uninitialized, + // (i.e. 0) use a compare-exchange to set the entry to kInitializingLabel + // (i.e. -1) to mark that we are initializing it. + dfsan_label label = 0; + if (atomic_compare_exchange_strong(table_ent, &label, kInitializingLabel, + memory_order_acquire)) { + // Check whether l2 subsumes l1. We don't need to check whether l1 + // subsumes l2 because we are guaranteed here that l1 < l2, and (at least + // in the cases we are interested in) a label may only subsume labels + // created earlier (i.e. with a lower numerical value). + if (__dfsan_label_info[l2].l1 == l1 || + __dfsan_label_info[l2].l2 == l1) { + label = l2; + } else { + label = + atomic_fetch_add(&__dfsan_last_label, 1, memory_order_relaxed) + 1; + CHECK_NE(label, kInitializingLabel); + __dfsan_label_info[label].l1 = l1; + __dfsan_label_info[label].l2 = l2; + } + atomic_store(table_ent, label, memory_order_release); + } else if (label == kInitializingLabel) { + // Another thread is initializing the entry. Wait until it is finished. + do { + internal_sched_yield(); + label = atomic_load(table_ent, memory_order_acquire); + } while (label == kInitializingLabel); + } + return label; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +dfsan_label __dfsan_union_load(const dfsan_label *ls, uptr n) { + dfsan_label label = ls[0]; + for (uptr i = 1; i != n; ++i) { + dfsan_label next_label = ls[i]; + if (label != next_label) + label = __dfsan_union(label, next_label); + } + return label; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __dfsan_unimplemented(char *fname) { + if (flags().warn_unimplemented) + Report("WARNING: DataFlowSanitizer: call to uninstrumented function %s\n", + fname); +} + +// Use '-mllvm -dfsan-debug-nonzero-labels' and break on this function +// to try to figure out where labels are being introduced in a nominally +// label-free program. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __dfsan_nonzero_label() { + if (flags().warn_nonzero_labels) + Report("WARNING: DataFlowSanitizer: saw nonzero label\n"); +} + +// Like __dfsan_union, but for use from the client or custom functions. Hence +// the equality comparison is done here before calling __dfsan_union. +SANITIZER_INTERFACE_ATTRIBUTE dfsan_label +dfsan_union(dfsan_label l1, dfsan_label l2) { + if (l1 == l2) + return l1; + return __dfsan_union(l1, l2); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +dfsan_label dfsan_create_label(const char *desc, void *userdata) { + dfsan_label label = + atomic_fetch_add(&__dfsan_last_label, 1, memory_order_relaxed) + 1; + CHECK_NE(label, kInitializingLabel); + __dfsan_label_info[label].l1 = __dfsan_label_info[label].l2 = 0; + __dfsan_label_info[label].desc = desc; + __dfsan_label_info[label].userdata = userdata; + return label; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __dfsan_set_label(dfsan_label label, void *addr, uptr size) { + for (dfsan_label *labelp = shadow_for(addr); size != 0; --size, ++labelp) + *labelp = label; +} + +SANITIZER_INTERFACE_ATTRIBUTE +void dfsan_set_label(dfsan_label label, void *addr, uptr size) { + __dfsan_set_label(label, addr, size); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void dfsan_add_label(dfsan_label label, void *addr, uptr size) { + for (dfsan_label *labelp = shadow_for(addr); size != 0; --size, ++labelp) + if (*labelp != label) + *labelp = __dfsan_union(*labelp, label); +} + +// Unlike the other dfsan interface functions the behavior of this function +// depends on the label of one of its arguments. Hence it is implemented as a +// custom function. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label +__dfsw_dfsan_get_label(long data, dfsan_label data_label, + dfsan_label *ret_label) { + *ret_label = 0; + return data_label; +} + +SANITIZER_INTERFACE_ATTRIBUTE dfsan_label +dfsan_read_label(const void *addr, uptr size) { + if (size == 0) + return 0; + return __dfsan_union_load(shadow_for(addr), size); +} + +SANITIZER_INTERFACE_ATTRIBUTE +const struct dfsan_label_info *dfsan_get_label_info(dfsan_label label) { + return &__dfsan_label_info[label]; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE int +dfsan_has_label(dfsan_label label, dfsan_label elem) { + if (label == elem) + return true; + const dfsan_label_info *info = dfsan_get_label_info(label); + if (info->l1 != 0) { + return dfsan_has_label(info->l1, elem) || dfsan_has_label(info->l2, elem); + } else { + return false; + } +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label +dfsan_has_label_with_desc(dfsan_label label, const char *desc) { + const dfsan_label_info *info = dfsan_get_label_info(label); + if (info->l1 != 0) { + return dfsan_has_label_with_desc(info->l1, desc) || + dfsan_has_label_with_desc(info->l2, desc); + } else { + return internal_strcmp(desc, info->desc) == 0; + } +} + +static void InitializeFlags(Flags &f, const char *env) { + f.warn_unimplemented = true; + f.warn_nonzero_labels = false; + + ParseFlag(env, &f.warn_unimplemented, "warn_unimplemented"); + ParseFlag(env, &f.warn_nonzero_labels, "warn_nonzero_labels"); +} + +#ifdef DFSAN_NOLIBC +extern "C" void dfsan_init() { +#else +static void dfsan_init(int argc, char **argv, char **envp) { +#endif + MmapFixedNoReserve(kShadowAddr, kUnusedAddr - kShadowAddr); + + // Protect the region of memory we don't use, to preserve the one-to-one + // mapping from application to shadow memory. But if ASLR is disabled, Linux + // will load our executable in the middle of our unused region. This mostly + // works so long as the program doesn't use too much memory. We support this + // case by disabling memory protection when ASLR is disabled. + uptr init_addr = (uptr)&dfsan_init; + if (!(init_addr >= kUnusedAddr && init_addr < kAppAddr)) + Mprotect(kUnusedAddr, kAppAddr - kUnusedAddr); + + InitializeFlags(flags(), GetEnv("DFSAN_OPTIONS")); + + InitializeInterceptors(); +} + +#ifndef DFSAN_NOLIBC +__attribute__((section(".preinit_array"), used)) +static void (*dfsan_init_ptr)(int, char **, char **) = dfsan_init; +#endif diff --git a/lib/dfsan/dfsan.h b/lib/dfsan/dfsan.h new file mode 100644 index 000000000000..693e0c5efe04 --- /dev/null +++ b/lib/dfsan/dfsan.h @@ -0,0 +1,67 @@ +//===-- dfsan.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 DataFlowSanitizer. +// +// Private DFSan header. +//===----------------------------------------------------------------------===// + +#ifndef DFSAN_H +#define DFSAN_H + +#include "sanitizer_common/sanitizer_internal_defs.h" + +// Copy declarations from public sanitizer/dfsan_interface.h header here. +typedef u16 dfsan_label; + +struct dfsan_label_info { + dfsan_label l1; + dfsan_label l2; + const char *desc; + void *userdata; +}; + +extern "C" { +void dfsan_set_label(dfsan_label label, void *addr, uptr size); +dfsan_label dfsan_read_label(const void *addr, uptr size); +dfsan_label dfsan_union(dfsan_label l1, dfsan_label l2); +} // extern "C" + +template <typename T> +void dfsan_set_label(dfsan_label label, T &data) { // NOLINT + dfsan_set_label(label, (void *)&data, sizeof(T)); +} + +namespace __dfsan { + +void InitializeInterceptors(); + +inline dfsan_label *shadow_for(void *ptr) { + return (dfsan_label *) ((((uptr) ptr) & ~0x700000000000) << 1); +} + +inline const dfsan_label *shadow_for(const void *ptr) { + return shadow_for(const_cast<void *>(ptr)); +} + +struct Flags { + // Whether to warn on unimplemented functions. + bool warn_unimplemented; + // Whether to warn on non-zero labels. + bool warn_nonzero_labels; +}; + +extern Flags flags_data; +inline Flags &flags() { + return flags_data; +} + +} // namespace __dfsan + +#endif // DFSAN_H diff --git a/lib/dfsan/dfsan.syms.extra b/lib/dfsan/dfsan.syms.extra new file mode 100644 index 000000000000..0d507eef0814 --- /dev/null +++ b/lib/dfsan/dfsan.syms.extra @@ -0,0 +1,3 @@ +dfsan_* +__dfsan_* +__dfsw_* diff --git a/lib/dfsan/dfsan_custom.cc b/lib/dfsan/dfsan_custom.cc new file mode 100644 index 000000000000..6a132db062b3 --- /dev/null +++ b/lib/dfsan/dfsan_custom.cc @@ -0,0 +1,341 @@ +//===-- dfsan.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 DataFlowSanitizer. +// +// This file defines the custom functions listed in done_abilist.txt. +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_linux.h" + +#include "dfsan/dfsan.h" + +#include <ctype.h> +#include <dlfcn.h> +#include <link.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> + +using namespace __dfsan; + +extern "C" { + +SANITIZER_INTERFACE_ATTRIBUTE int +__dfsw_stat(const char *path, struct stat *buf, dfsan_label path_label, + dfsan_label buf_label, dfsan_label *ret_label) { + int ret = stat(path, buf); + if (ret == 0) + dfsan_set_label(0, buf, sizeof(struct stat)); + *ret_label = 0; + return ret; +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_fstat(int fd, struct stat *buf, + dfsan_label fd_label, + dfsan_label buf_label, + dfsan_label *ret_label) { + int ret = fstat(fd, buf); + if (ret == 0) + dfsan_set_label(0, buf, sizeof(struct stat)); + *ret_label = 0; + return ret; +} + +SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_strchr(const char *s, int c, + dfsan_label s_label, + dfsan_label c_label, + dfsan_label *ret_label) { + for (size_t i = 0;; ++i) { + if (s[i] == c || s[i] == 0) { + *ret_label = dfsan_union(dfsan_read_label(s, i+1), c_label); + return s[i] == 0 ? 0 : const_cast<char *>(s+i); + } + } +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_memcmp(const void *s1, const void *s2, + size_t n, dfsan_label s1_label, + dfsan_label s2_label, + dfsan_label n_label, + dfsan_label *ret_label) { + const char *cs1 = (const char *) s1, *cs2 = (const char *) s2; + for (size_t i = 0; i != n; ++i) { + if (cs1[i] != cs2[i]) { + *ret_label = dfsan_union(dfsan_read_label(cs1, i+1), + dfsan_read_label(cs2, i+1)); + return cs1[i] - cs2[i]; + } + } + *ret_label = dfsan_union(dfsan_read_label(cs1, n), + dfsan_read_label(cs2, n)); + return 0; +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_strcmp(const char *s1, const char *s2, + dfsan_label s1_label, + dfsan_label s2_label, + dfsan_label *ret_label) { + for (size_t i = 0;; ++i) { + if (s1[i] != s2[i] || s1[i] == 0 || s2[i] == 0) { + *ret_label = dfsan_union(dfsan_read_label(s1, i+1), + dfsan_read_label(s2, i+1)); + return s1[i] - s2[i]; + } + } + return 0; +} + +SANITIZER_INTERFACE_ATTRIBUTE int +__dfsw_strcasecmp(const char *s1, const char *s2, dfsan_label s1_label, + dfsan_label s2_label, dfsan_label *ret_label) { + for (size_t i = 0;; ++i) { + if (tolower(s1[i]) != tolower(s2[i]) || s1[i] == 0 || s2[i] == 0) { + *ret_label = dfsan_union(dfsan_read_label(s1, i+1), + dfsan_read_label(s2, i+1)); + return s1[i] - s2[i]; + } + } + return 0; +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_strncmp(const char *s1, const char *s2, + size_t n, dfsan_label s1_label, + dfsan_label s2_label, + dfsan_label n_label, + dfsan_label *ret_label) { + if (n == 0) { + *ret_label = 0; + return 0; + } + + for (size_t i = 0;; ++i) { + if (s1[i] != s2[i] || s1[i] == 0 || s2[i] == 0 || i == n-1) { + *ret_label = dfsan_union(dfsan_read_label(s1, i+1), + dfsan_read_label(s2, i+1)); + return s1[i] - s2[i]; + } + } + return 0; +} + +SANITIZER_INTERFACE_ATTRIBUTE int +__dfsw_strncasecmp(const char *s1, const char *s2, size_t n, + dfsan_label s1_label, dfsan_label s2_label, + dfsan_label n_label, dfsan_label *ret_label) { + if (n == 0) { + *ret_label = 0; + return 0; + } + + for (size_t i = 0;; ++i) { + if (tolower(s1[i]) != tolower(s2[i]) || s1[i] == 0 || s2[i] == 0 || + i == n - 1) { + *ret_label = dfsan_union(dfsan_read_label(s1, i+1), + dfsan_read_label(s2, i+1)); + return s1[i] - s2[i]; + } + } + return 0; +} + +SANITIZER_INTERFACE_ATTRIBUTE void *__dfsw_calloc(size_t nmemb, size_t size, + dfsan_label nmemb_label, + dfsan_label size_label, + dfsan_label *ret_label) { + void *p = calloc(nmemb, size); + dfsan_set_label(0, p, nmemb * size); + *ret_label = 0; + return p; +} + +SANITIZER_INTERFACE_ATTRIBUTE size_t +__dfsw_strlen(const char *s, dfsan_label s_label, dfsan_label *ret_label) { + size_t ret = strlen(s); + *ret_label = dfsan_read_label(s, ret+1); + return ret; +} + + +static void *dfsan_memcpy(void *dest, const void *src, size_t n) { + dfsan_label *sdest = shadow_for(dest), *ssrc = shadow_for((void *)src); + internal_memcpy((void *)sdest, (void *)ssrc, n * sizeof(dfsan_label)); + return internal_memcpy(dest, src, n); +} + +static void dfsan_memset(void *s, int c, dfsan_label c_label, size_t n) { + internal_memset(s, c, n); + dfsan_set_label(c_label, s, n); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *__dfsw_memcpy(void *dest, const void *src, size_t n, + dfsan_label dest_label, dfsan_label src_label, + dfsan_label n_label, dfsan_label *ret_label) { + *ret_label = 0; + return dfsan_memcpy(dest, src, n); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *__dfsw_memset(void *s, int c, size_t n, + dfsan_label s_label, dfsan_label c_label, + dfsan_label n_label, dfsan_label *ret_label) { + dfsan_memset(s, c, c_label, n); + *ret_label = 0; + return s; +} + +SANITIZER_INTERFACE_ATTRIBUTE char * +__dfsw_strdup(const char *s, dfsan_label s_label, dfsan_label *ret_label) { + size_t len = strlen(s); + void *p = malloc(len+1); + dfsan_memcpy(p, s, len+1); + *ret_label = 0; + return static_cast<char *>(p); +} + +SANITIZER_INTERFACE_ATTRIBUTE char * +__dfsw_strncpy(char *s1, const char *s2, size_t n, dfsan_label s1_label, + dfsan_label s2_label, dfsan_label n_label, + dfsan_label *ret_label) { + size_t len = strlen(s2); + if (len < n) { + dfsan_memcpy(s1, s2, len+1); + dfsan_memset(s1+len+1, 0, 0, n-len-1); + } else { + dfsan_memcpy(s1, s2, n); + } + + *ret_label = 0; + return s1; +} + +SANITIZER_INTERFACE_ATTRIBUTE ssize_t +__dfsw_pread(int fd, void *buf, size_t count, off_t offset, + dfsan_label fd_label, dfsan_label buf_label, + dfsan_label count_label, dfsan_label offset_label, + dfsan_label *ret_label) { + ssize_t ret = pread(fd, buf, count, offset); + if (ret > 0) + dfsan_set_label(0, buf, ret); + *ret_label = 0; + return ret; +} + +SANITIZER_INTERFACE_ATTRIBUTE ssize_t +__dfsw_read(int fd, void *buf, size_t count, + dfsan_label fd_label, dfsan_label buf_label, + dfsan_label count_label, + dfsan_label *ret_label) { + ssize_t ret = read(fd, buf, count); + if (ret > 0) + dfsan_set_label(0, buf, ret); + *ret_label = 0; + return ret; +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_clock_gettime(clockid_t clk_id, + struct timespec *tp, + dfsan_label clk_id_label, + dfsan_label tp_label, + dfsan_label *ret_label) { + int ret = clock_gettime(clk_id, tp); + if (ret == 0) + dfsan_set_label(0, tp, sizeof(struct timespec)); + *ret_label = 0; + return ret; +} + +static void unpoison(const void *ptr, uptr size) { + dfsan_set_label(0, const_cast<void *>(ptr), size); +} + +// dlopen() ultimately calls mmap() down inside the loader, which generally +// doesn't participate in dynamic symbol resolution. Therefore we won't +// intercept its calls to mmap, and we have to hook it here. +SANITIZER_INTERFACE_ATTRIBUTE void * +__dfsw_dlopen(const char *filename, int flag, dfsan_label filename_label, + dfsan_label flag_label, dfsan_label *ret_label) { + link_map *map = (link_map *)dlopen(filename, flag); + if (map) + ForEachMappedRegion(map, unpoison); + *ret_label = 0; + return (void *)map; +} + +struct pthread_create_info { + void *(*start_routine_trampoline)(void *, void *, dfsan_label, dfsan_label *); + void *start_routine; + void *arg; +}; + +static void *pthread_create_cb(void *p) { + pthread_create_info pci(*(pthread_create_info *)p); + free(p); + dfsan_label ret_label; + return pci.start_routine_trampoline(pci.start_routine, pci.arg, 0, + &ret_label); +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_pthread_create( + pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine_trampoline)(void *, void *, dfsan_label, + dfsan_label *), + void *start_routine, void *arg, dfsan_label thread_label, + dfsan_label attr_label, dfsan_label start_routine_label, + dfsan_label arg_label, dfsan_label *ret_label) { + pthread_create_info *pci = + (pthread_create_info *)malloc(sizeof(pthread_create_info)); + pci->start_routine_trampoline = start_routine_trampoline; + pci->start_routine = start_routine; + pci->arg = arg; + int rv = pthread_create(thread, attr, pthread_create_cb, (void *)pci); + if (rv != 0) + free(pci); + *ret_label = 0; + return rv; +} + +struct dl_iterate_phdr_info { + int (*callback_trampoline)(void *callback, struct dl_phdr_info *info, + size_t size, void *data, dfsan_label info_label, + dfsan_label size_label, dfsan_label data_label, + dfsan_label *ret_label); + void *callback; + void *data; +}; + +int dl_iterate_phdr_cb(struct dl_phdr_info *info, size_t size, void *data) { + dl_iterate_phdr_info *dipi = (dl_iterate_phdr_info *)data; + dfsan_set_label(0, *info); + dfsan_set_label(0, (void *)info->dlpi_name, strlen(info->dlpi_name) + 1); + dfsan_set_label(0, (void *)info->dlpi_phdr, + sizeof(*info->dlpi_phdr) * info->dlpi_phnum); + dfsan_label ret_label; + return dipi->callback_trampoline(dipi->callback, info, size, dipi->data, 0, 0, + 0, &ret_label); +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_dl_iterate_phdr( + int (*callback_trampoline)(void *callback, struct dl_phdr_info *info, + size_t size, void *data, dfsan_label info_label, + dfsan_label size_label, dfsan_label data_label, + dfsan_label *ret_label), + void *callback, void *data, dfsan_label callback_label, + dfsan_label data_label, dfsan_label *ret_label) { + dl_iterate_phdr_info dipi = { callback_trampoline, callback, data }; + *ret_label = 0; + return dl_iterate_phdr(dl_iterate_phdr_cb, &dipi); +} + +} diff --git a/lib/dfsan/dfsan_interceptors.cc b/lib/dfsan/dfsan_interceptors.cc new file mode 100644 index 000000000000..8b7d64e25a39 --- /dev/null +++ b/lib/dfsan/dfsan_interceptors.cc @@ -0,0 +1,44 @@ +//===-- dfsan_interceptors.cc ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of DataFlowSanitizer. +// +// Interceptors for standard library functions. +//===----------------------------------------------------------------------===// + +#include "dfsan/dfsan.h" +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_common.h" + +INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags, + int fd, OFF_T offset) { + void *res = REAL(mmap)(addr, length, prot, flags, fd, offset); + if (res != (void*)-1) + dfsan_set_label(0, res, RoundUpTo(length, GetPageSize())); + return res; +} + +INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags, + int fd, OFF64_T offset) { + void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset); + if (res != (void*)-1) + dfsan_set_label(0, res, RoundUpTo(length, GetPageSize())); + return res; +} + +namespace __dfsan { +void InitializeInterceptors() { + static int inited = 0; + CHECK_EQ(inited, 0); + + INTERCEPT_FUNCTION(mmap); + INTERCEPT_FUNCTION(mmap64); + inited = 1; +} +} // namespace __dfsan diff --git a/lib/dfsan/done_abilist.txt b/lib/dfsan/done_abilist.txt new file mode 100644 index 000000000000..27df3e9b707c --- /dev/null +++ b/lib/dfsan/done_abilist.txt @@ -0,0 +1,127 @@ +fun:main=uninstrumented +fun:main=discard + +# DFSan interface functions. +fun:dfsan_union=uninstrumented +fun:dfsan_union=discard + +fun:dfsan_create_label=uninstrumented +fun:dfsan_create_label=discard + +fun:dfsan_set_label=uninstrumented +fun:dfsan_set_label=discard + +fun:dfsan_add_label=uninstrumented +fun:dfsan_add_label=discard + +fun:dfsan_get_label=uninstrumented +fun:dfsan_get_label=custom + +fun:dfsan_read_label=uninstrumented +fun:dfsan_read_label=discard + +fun:dfsan_get_label_info=uninstrumented +fun:dfsan_get_label_info=discard + +fun:dfsan_has_label=uninstrumented +fun:dfsan_has_label=discard + +fun:dfsan_has_label_with_desc=uninstrumented +fun:dfsan_has_label_with_desc=discard + +# glibc functions. +fun:malloc=discard +fun:realloc=discard +fun:free=discard +fun:isalpha=functional +fun:isdigit=functional +fun:isprint=functional +fun:isxdigit=functional +fun:isalnum=functional +fun:ispunct=functional +fun:isspace=functional +fun:tolower=functional +fun:toupper=functional +fun:exp=functional +fun:exp2=functional +fun:log=functional +fun:sqrt=functional +fun:__cxa_atexit=discard +fun:open=discard +fun:pthread_key_create=discard +fun:getenv=discard +fun:__ctype_b_loc=discard +fun:__errno_location=discard +fun:mmap=discard +fun:munmap=discard +fun:write=discard +fun:close=discard +fun:pthread_equal=discard +fun:pthread_getspecific=discard +fun:pthread_setspecific=discard +fun:pthread_mutex_destroy=discard +fun:pthread_mutexattr_init=discard +fun:pthread_mutexattr_settype=discard +fun:pthread_mutex_init=discard +fun:pthread_mutex_lock=discard +fun:pthread_mutex_trylock=discard +fun:pthread_mutex_unlock=discard +fun:pthread_mutexattr_destroy=discard +fun:pthread_once=discard +fun:pthread_key_delete=discard +fun:pthread_self=discard +fun:printf=discard +fun:fprintf=discard +fun:fputs=discard +fun:fputc=discard +fun:fopen=discard +fun:fseek=discard +fun:lseek=discard +fun:ftell=discard +fun:fclose=discard +fun:dladdr=discard +fun:getpagesize=discard +fun:sched_getcpu=discard +fun:sched_getaffinity=discard +fun:sched_setaffinity=discard +fun:syscall=discard +fun:sem_init=discard +fun:sem_post=discard +fun:sem_wait=discard +fun:sched_yield=discard +fun:uselocale=discard +fun:rand=discard +fun:random=discard +fun:sleep=discard + +fun:stat=custom +fun:fstat=custom +fun:memcmp=custom +fun:memcpy=custom +fun:memset=custom +fun:strcmp=custom +fun:strdup=custom +fun:strncmp=custom +fun:strncpy=custom +fun:strcasecmp=custom +fun:strncasecmp=custom +fun:strchr=custom +fun:strlen=custom +fun:calloc=custom +fun:dlopen=custom +fun:read=custom +fun:pread=custom +fun:clock_gettime=custom +fun:pthread_create=custom +fun:dl_iterate_phdr=custom + +# TODO: custom +fun:snprintf=discard +fun:vsnprintf=discard +fun:asprintf=discard +fun:qsort=discard +fun:strtoll=discard +fun:strtoull=discard +fun:sigemptyset=discard +fun:sigaction=discard +fun:gettimeofday=discard diff --git a/lib/dfsan/libc_ubuntu1204_abilist.txt b/lib/dfsan/libc_ubuntu1204_abilist.txt new file mode 100644 index 000000000000..5fc8194c8f16 --- /dev/null +++ b/lib/dfsan/libc_ubuntu1204_abilist.txt @@ -0,0 +1,3685 @@ +fun:_Exit=uninstrumented +fun:_IO_adjust_column=uninstrumented +fun:_IO_adjust_wcolumn=uninstrumented +fun:_IO_default_doallocate=uninstrumented +fun:_IO_default_finish=uninstrumented +fun:_IO_default_pbackfail=uninstrumented +fun:_IO_default_uflow=uninstrumented +fun:_IO_default_xsgetn=uninstrumented +fun:_IO_default_xsputn=uninstrumented +fun:_IO_do_write=uninstrumented +fun:_IO_doallocbuf=uninstrumented +fun:_IO_fclose=uninstrumented +fun:_IO_fdopen=uninstrumented +fun:_IO_feof=uninstrumented +fun:_IO_ferror=uninstrumented +fun:_IO_fflush=uninstrumented +fun:_IO_fgetpos=uninstrumented +fun:_IO_fgetpos64=uninstrumented +fun:_IO_fgets=uninstrumented +fun:_IO_file_attach=uninstrumented +fun:_IO_file_close=uninstrumented +fun:_IO_file_close_it=uninstrumented +fun:_IO_file_doallocate=uninstrumented +fun:_IO_file_finish=uninstrumented +fun:_IO_file_fopen=uninstrumented +fun:_IO_file_init=uninstrumented +fun:_IO_file_open=uninstrumented +fun:_IO_file_overflow=uninstrumented +fun:_IO_file_read=uninstrumented +fun:_IO_file_seek=uninstrumented +fun:_IO_file_seekoff=uninstrumented +fun:_IO_file_setbuf=uninstrumented +fun:_IO_file_stat=uninstrumented +fun:_IO_file_sync=uninstrumented +fun:_IO_file_underflow=uninstrumented +fun:_IO_file_write=uninstrumented +fun:_IO_file_xsputn=uninstrumented +fun:_IO_flockfile=uninstrumented +fun:_IO_flush_all=uninstrumented +fun:_IO_flush_all_linebuffered=uninstrumented +fun:_IO_fopen=uninstrumented +fun:_IO_fprintf=uninstrumented +fun:_IO_fputs=uninstrumented +fun:_IO_fread=uninstrumented +fun:_IO_free_backup_area=uninstrumented +fun:_IO_free_wbackup_area=uninstrumented +fun:_IO_fsetpos=uninstrumented +fun:_IO_fsetpos64=uninstrumented +fun:_IO_ftell=uninstrumented +fun:_IO_ftrylockfile=uninstrumented +fun:_IO_funlockfile=uninstrumented +fun:_IO_fwrite=uninstrumented +fun:_IO_getc=uninstrumented +fun:_IO_getline=uninstrumented +fun:_IO_getline_info=uninstrumented +fun:_IO_gets=uninstrumented +fun:_IO_init=uninstrumented +fun:_IO_init_marker=uninstrumented +fun:_IO_init_wmarker=uninstrumented +fun:_IO_iter_begin=uninstrumented +fun:_IO_iter_end=uninstrumented +fun:_IO_iter_file=uninstrumented +fun:_IO_iter_next=uninstrumented +fun:_IO_least_wmarker=uninstrumented +fun:_IO_link_in=uninstrumented +fun:_IO_list_lock=uninstrumented +fun:_IO_list_resetlock=uninstrumented +fun:_IO_list_unlock=uninstrumented +fun:_IO_marker_delta=uninstrumented +fun:_IO_marker_difference=uninstrumented +fun:_IO_padn=uninstrumented +fun:_IO_peekc_locked=uninstrumented +fun:_IO_popen=uninstrumented +fun:_IO_printf=uninstrumented +fun:_IO_proc_close=uninstrumented +fun:_IO_proc_open=uninstrumented +fun:_IO_putc=uninstrumented +fun:_IO_puts=uninstrumented +fun:_IO_remove_marker=uninstrumented +fun:_IO_seekmark=uninstrumented +fun:_IO_seekoff=uninstrumented +fun:_IO_seekpos=uninstrumented +fun:_IO_seekwmark=uninstrumented +fun:_IO_setb=uninstrumented +fun:_IO_setbuffer=uninstrumented +fun:_IO_setvbuf=uninstrumented +fun:_IO_sgetn=uninstrumented +fun:_IO_sprintf=uninstrumented +fun:_IO_sputbackc=uninstrumented +fun:_IO_sputbackwc=uninstrumented +fun:_IO_sscanf=uninstrumented +fun:_IO_str_init_readonly=uninstrumented +fun:_IO_str_init_static=uninstrumented +fun:_IO_str_overflow=uninstrumented +fun:_IO_str_pbackfail=uninstrumented +fun:_IO_str_seekoff=uninstrumented +fun:_IO_str_underflow=uninstrumented +fun:_IO_sungetc=uninstrumented +fun:_IO_sungetwc=uninstrumented +fun:_IO_switch_to_get_mode=uninstrumented +fun:_IO_switch_to_main_wget_area=uninstrumented +fun:_IO_switch_to_wbackup_area=uninstrumented +fun:_IO_switch_to_wget_mode=uninstrumented +fun:_IO_un_link=uninstrumented +fun:_IO_ungetc=uninstrumented +fun:_IO_unsave_markers=uninstrumented +fun:_IO_unsave_wmarkers=uninstrumented +fun:_IO_vfprintf=uninstrumented +fun:_IO_vfscanf=uninstrumented +fun:_IO_vsprintf=uninstrumented +fun:_IO_wdefault_doallocate=uninstrumented +fun:_IO_wdefault_finish=uninstrumented +fun:_IO_wdefault_pbackfail=uninstrumented +fun:_IO_wdefault_uflow=uninstrumented +fun:_IO_wdefault_xsgetn=uninstrumented +fun:_IO_wdefault_xsputn=uninstrumented +fun:_IO_wdo_write=uninstrumented +fun:_IO_wdoallocbuf=uninstrumented +fun:_IO_wfile_overflow=uninstrumented +fun:_IO_wfile_seekoff=uninstrumented +fun:_IO_wfile_sync=uninstrumented +fun:_IO_wfile_underflow=uninstrumented +fun:_IO_wfile_xsputn=uninstrumented +fun:_IO_wmarker_delta=uninstrumented +fun:_IO_wsetb=uninstrumented +fun:_L_cond_lock_1021=uninstrumented +fun:_L_cond_lock_874=uninstrumented +fun:_L_cond_lock_918=uninstrumented +fun:_L_lock_1006=uninstrumented +fun:_L_lock_125=uninstrumented +fun:_L_lock_1281=uninstrumented +fun:_L_lock_13=uninstrumented +fun:_L_lock_133=uninstrumented +fun:_L_lock_1392=uninstrumented +fun:_L_lock_175=uninstrumented +fun:_L_lock_19=uninstrumented +fun:_L_lock_2009=uninstrumented +fun:_L_lock_21=uninstrumented +fun:_L_lock_211=uninstrumented +fun:_L_lock_2143=uninstrumented +fun:_L_lock_227=uninstrumented +fun:_L_lock_23=uninstrumented +fun:_L_lock_2338=uninstrumented +fun:_L_lock_26=uninstrumented +fun:_L_lock_29=uninstrumented +fun:_L_lock_3116=uninstrumented +fun:_L_lock_32=uninstrumented +fun:_L_lock_3335=uninstrumented +fun:_L_lock_34=uninstrumented +fun:_L_lock_37=uninstrumented +fun:_L_lock_40=uninstrumented +fun:_L_lock_4016=uninstrumented +fun:_L_lock_4410=uninstrumented +fun:_L_lock_451=uninstrumented +fun:_L_lock_4590=uninstrumented +fun:_L_lock_466=uninstrumented +fun:_L_lock_641=uninstrumented +fun:_L_lock_858=uninstrumented +fun:_L_lock_903=uninstrumented +fun:_L_robust_cond_lock_164=uninstrumented +fun:_L_robust_lock_160=uninstrumented +fun:_L_robust_timedlock_361=uninstrumented +fun:_L_robust_unlock_189=uninstrumented +fun:_L_timedlock_1043=uninstrumented +fun:_L_timedlock_108=uninstrumented +fun:_L_timedlock_292=uninstrumented +fun:_L_unlock_114=uninstrumented +fun:_L_unlock_1157=uninstrumented +fun:_L_unlock_1355=uninstrumented +fun:_L_unlock_137=uninstrumented +fun:_L_unlock_157=uninstrumented +fun:_L_unlock_16=uninstrumented +fun:_L_unlock_170=uninstrumented +fun:_L_unlock_174=uninstrumented +fun:_L_unlock_177=uninstrumented +fun:_L_unlock_1946=uninstrumented +fun:_L_unlock_197=uninstrumented +fun:_L_unlock_2117=uninstrumented +fun:_L_unlock_2318=uninstrumented +fun:_L_unlock_2384=uninstrumented +fun:_L_unlock_27=uninstrumented +fun:_L_unlock_3119=uninstrumented +fun:_L_unlock_312=uninstrumented +fun:_L_unlock_3464=uninstrumented +fun:_L_unlock_37=uninstrumented +fun:_L_unlock_3744=uninstrumented +fun:_L_unlock_4037=uninstrumented +fun:_L_unlock_43=uninstrumented +fun:_L_unlock_4353=uninstrumented +fun:_L_unlock_4432=uninstrumented +fun:_L_unlock_4525=uninstrumented +fun:_L_unlock_4619=uninstrumented +fun:_L_unlock_494=uninstrumented +fun:_L_unlock_54=uninstrumented +fun:_L_unlock_544=uninstrumented +fun:_L_unlock_61=uninstrumented +fun:_L_unlock_62=uninstrumented +fun:_L_unlock_64=uninstrumented +fun:_L_unlock_644=uninstrumented +fun:_L_unlock_698=uninstrumented +fun:_L_unlock_708=uninstrumented +fun:_L_unlock_88=uninstrumented +fun:_Unwind_Backtrace=uninstrumented +fun:_Unwind_DeleteException=uninstrumented +fun:_Unwind_FindEnclosingFunction=uninstrumented +fun:_Unwind_Find_FDE=uninstrumented +fun:_Unwind_ForcedUnwind=uninstrumented +fun:_Unwind_GetCFA=uninstrumented +fun:_Unwind_GetDataRelBase=uninstrumented +fun:_Unwind_GetGR=uninstrumented +fun:_Unwind_GetIP=uninstrumented +fun:_Unwind_GetIPInfo=uninstrumented +fun:_Unwind_GetLanguageSpecificData=uninstrumented +fun:_Unwind_GetRegionStart=uninstrumented +fun:_Unwind_GetTextRelBase=uninstrumented +fun:_Unwind_RaiseException=uninstrumented +fun:_Unwind_Resume=uninstrumented +fun:_Unwind_Resume_or_Rethrow=uninstrumented +fun:_Unwind_SetGR=uninstrumented +fun:_Unwind_SetIP=uninstrumented +fun:__GI___nptl_create_event=uninstrumented +fun:__GI___nptl_death_event=uninstrumented +fun:__GI___pthread_cleanup_upto=uninstrumented +fun:__GI___pthread_register_cancel=uninstrumented +fun:__GI___pthread_unregister_cancel=uninstrumented +fun:__GI___pthread_unwind=uninstrumented +fun:__GI___pthread_unwind_next=uninstrumented +fun:__absvdi2=uninstrumented +fun:__absvsi2=uninstrumented +fun:__absvti2=uninstrumented +fun:__accept=uninstrumented +fun:__accept_nocancel=uninstrumented +fun:__acos_finite=uninstrumented +fun:__acosf_finite=uninstrumented +fun:__acosh_finite=uninstrumented +fun:__acoshf_finite=uninstrumented +fun:__acoshl_finite=uninstrumented +fun:__acosl_finite=uninstrumented +fun:__addtf3=uninstrumented +fun:__addvdi3=uninstrumented +fun:__addvsi3=uninstrumented +fun:__addvti3=uninstrumented +fun:__adjtimex=uninstrumented +fun:__arch_prctl=uninstrumented +fun:__argz_count=uninstrumented +fun:__argz_next=uninstrumented +fun:__argz_stringify=uninstrumented +fun:__ashlti3=uninstrumented +fun:__ashrti3=uninstrumented +fun:__asin_finite=uninstrumented +fun:__asinf_finite=uninstrumented +fun:__asinl_finite=uninstrumented +fun:__asprintf=uninstrumented +fun:__asprintf_chk=uninstrumented +fun:__assert=uninstrumented +fun:__assert_fail=uninstrumented +fun:__assert_perror_fail=uninstrumented +fun:__atan2_finite=uninstrumented +fun:__atan2f_finite=uninstrumented +fun:__atan2l_finite=uninstrumented +fun:__atanh_finite=uninstrumented +fun:__atanhf_finite=uninstrumented +fun:__atanhl_finite=uninstrumented +fun:__b64_ntop=uninstrumented +fun:__b64_pton=uninstrumented +fun:__backtrace=uninstrumented +fun:__backtrace_symbols=uninstrumented +fun:__backtrace_symbols_fd=uninstrumented +fun:__bid128_abs=uninstrumented +fun:__bid128_add=uninstrumented +fun:__bid128_class=uninstrumented +fun:__bid128_copy=uninstrumented +fun:__bid128_copySign=uninstrumented +fun:__bid128_div=uninstrumented +fun:__bid128_fma=uninstrumented +fun:__bid128_from_int32=uninstrumented +fun:__bid128_from_int64=uninstrumented +fun:__bid128_from_uint32=uninstrumented +fun:__bid128_from_uint64=uninstrumented +fun:__bid128_isCanonical=uninstrumented +fun:__bid128_isFinite=uninstrumented +fun:__bid128_isInf=uninstrumented +fun:__bid128_isNaN=uninstrumented +fun:__bid128_isNormal=uninstrumented +fun:__bid128_isSignaling=uninstrumented +fun:__bid128_isSigned=uninstrumented +fun:__bid128_isSubnormal=uninstrumented +fun:__bid128_isZero=uninstrumented +fun:__bid128_mul=uninstrumented +fun:__bid128_negate=uninstrumented +fun:__bid128_quiet_equal=uninstrumented +fun:__bid128_quiet_greater=uninstrumented +fun:__bid128_quiet_greater_equal=uninstrumented +fun:__bid128_quiet_greater_unordered=uninstrumented +fun:__bid128_quiet_less=uninstrumented +fun:__bid128_quiet_less_equal=uninstrumented +fun:__bid128_quiet_less_unordered=uninstrumented +fun:__bid128_quiet_not_equal=uninstrumented +fun:__bid128_quiet_not_greater=uninstrumented +fun:__bid128_quiet_not_less=uninstrumented +fun:__bid128_quiet_ordered=uninstrumented +fun:__bid128_quiet_unordered=uninstrumented +fun:__bid128_radix=uninstrumented +fun:__bid128_sameQuantum=uninstrumented +fun:__bid128_signaling_greater=uninstrumented +fun:__bid128_signaling_greater_equal=uninstrumented +fun:__bid128_signaling_greater_unordered=uninstrumented +fun:__bid128_signaling_less=uninstrumented +fun:__bid128_signaling_less_equal=uninstrumented +fun:__bid128_signaling_less_unordered=uninstrumented +fun:__bid128_signaling_not_greater=uninstrumented +fun:__bid128_signaling_not_less=uninstrumented +fun:__bid128_sub=uninstrumented +fun:__bid128_to_bid32=uninstrumented +fun:__bid128_to_bid64=uninstrumented +fun:__bid128_to_binary128=uninstrumented +fun:__bid128_to_binary32=uninstrumented +fun:__bid128_to_binary64=uninstrumented +fun:__bid128_to_binary80=uninstrumented +fun:__bid128_to_int32_ceil=uninstrumented +fun:__bid128_to_int32_floor=uninstrumented +fun:__bid128_to_int32_int=uninstrumented +fun:__bid128_to_int32_rnint=uninstrumented +fun:__bid128_to_int32_rninta=uninstrumented +fun:__bid128_to_int32_xceil=uninstrumented +fun:__bid128_to_int32_xfloor=uninstrumented +fun:__bid128_to_int32_xint=uninstrumented +fun:__bid128_to_int32_xrnint=uninstrumented +fun:__bid128_to_int32_xrninta=uninstrumented +fun:__bid128_to_int64_ceil=uninstrumented +fun:__bid128_to_int64_floor=uninstrumented +fun:__bid128_to_int64_int=uninstrumented +fun:__bid128_to_int64_rnint=uninstrumented +fun:__bid128_to_int64_rninta=uninstrumented +fun:__bid128_to_int64_xceil=uninstrumented +fun:__bid128_to_int64_xfloor=uninstrumented +fun:__bid128_to_int64_xint=uninstrumented +fun:__bid128_to_int64_xrnint=uninstrumented +fun:__bid128_to_int64_xrninta=uninstrumented +fun:__bid128_to_uint32_ceil=uninstrumented +fun:__bid128_to_uint32_floor=uninstrumented +fun:__bid128_to_uint32_int=uninstrumented +fun:__bid128_to_uint32_rnint=uninstrumented +fun:__bid128_to_uint32_rninta=uninstrumented +fun:__bid128_to_uint32_xceil=uninstrumented +fun:__bid128_to_uint32_xfloor=uninstrumented +fun:__bid128_to_uint32_xint=uninstrumented +fun:__bid128_to_uint32_xrnint=uninstrumented +fun:__bid128_to_uint32_xrninta=uninstrumented +fun:__bid128_to_uint64_ceil=uninstrumented +fun:__bid128_to_uint64_floor=uninstrumented +fun:__bid128_to_uint64_int=uninstrumented +fun:__bid128_to_uint64_rnint=uninstrumented +fun:__bid128_to_uint64_rninta=uninstrumented +fun:__bid128_to_uint64_xceil=uninstrumented +fun:__bid128_to_uint64_xfloor=uninstrumented +fun:__bid128_to_uint64_xint=uninstrumented +fun:__bid128_to_uint64_xrnint=uninstrumented +fun:__bid128_to_uint64_xrninta=uninstrumented +fun:__bid128_totalOrder=uninstrumented +fun:__bid128_totalOrderMag=uninstrumented +fun:__bid128dd_add=uninstrumented +fun:__bid128dd_div=uninstrumented +fun:__bid128dd_mul=uninstrumented +fun:__bid128dd_sub=uninstrumented +fun:__bid128ddd_fma=uninstrumented +fun:__bid128ddq_fma=uninstrumented +fun:__bid128dq_add=uninstrumented +fun:__bid128dq_div=uninstrumented +fun:__bid128dq_mul=uninstrumented +fun:__bid128dq_sub=uninstrumented +fun:__bid128dqd_fma=uninstrumented +fun:__bid128dqq_fma=uninstrumented +fun:__bid128qd_add=uninstrumented +fun:__bid128qd_div=uninstrumented +fun:__bid128qd_mul=uninstrumented +fun:__bid128qd_sub=uninstrumented +fun:__bid128qdd_fma=uninstrumented +fun:__bid128qdq_fma=uninstrumented +fun:__bid128qqd_fma=uninstrumented +fun:__bid32_to_bid128=uninstrumented +fun:__bid32_to_bid64=uninstrumented +fun:__bid32_to_binary128=uninstrumented +fun:__bid32_to_binary32=uninstrumented +fun:__bid32_to_binary64=uninstrumented +fun:__bid32_to_binary80=uninstrumented +fun:__bid64_abs=uninstrumented +fun:__bid64_add=uninstrumented +fun:__bid64_class=uninstrumented +fun:__bid64_copy=uninstrumented +fun:__bid64_copySign=uninstrumented +fun:__bid64_div=uninstrumented +fun:__bid64_from_int32=uninstrumented +fun:__bid64_from_int64=uninstrumented +fun:__bid64_from_uint32=uninstrumented +fun:__bid64_from_uint64=uninstrumented +fun:__bid64_isCanonical=uninstrumented +fun:__bid64_isFinite=uninstrumented +fun:__bid64_isInf=uninstrumented +fun:__bid64_isNaN=uninstrumented +fun:__bid64_isNormal=uninstrumented +fun:__bid64_isSignaling=uninstrumented +fun:__bid64_isSigned=uninstrumented +fun:__bid64_isSubnormal=uninstrumented +fun:__bid64_isZero=uninstrumented +fun:__bid64_mul=uninstrumented +fun:__bid64_negate=uninstrumented +fun:__bid64_quiet_equal=uninstrumented +fun:__bid64_quiet_greater=uninstrumented +fun:__bid64_quiet_greater_equal=uninstrumented +fun:__bid64_quiet_greater_unordered=uninstrumented +fun:__bid64_quiet_less=uninstrumented +fun:__bid64_quiet_less_equal=uninstrumented +fun:__bid64_quiet_less_unordered=uninstrumented +fun:__bid64_quiet_not_equal=uninstrumented +fun:__bid64_quiet_not_greater=uninstrumented +fun:__bid64_quiet_not_less=uninstrumented +fun:__bid64_quiet_ordered=uninstrumented +fun:__bid64_quiet_unordered=uninstrumented +fun:__bid64_radix=uninstrumented +fun:__bid64_sameQuantum=uninstrumented +fun:__bid64_signaling_greater=uninstrumented +fun:__bid64_signaling_greater_equal=uninstrumented +fun:__bid64_signaling_greater_unordered=uninstrumented +fun:__bid64_signaling_less=uninstrumented +fun:__bid64_signaling_less_equal=uninstrumented +fun:__bid64_signaling_less_unordered=uninstrumented +fun:__bid64_signaling_not_greater=uninstrumented +fun:__bid64_signaling_not_less=uninstrumented +fun:__bid64_sub=uninstrumented +fun:__bid64_to_bid128=uninstrumented +fun:__bid64_to_bid32=uninstrumented +fun:__bid64_to_binary128=uninstrumented +fun:__bid64_to_binary32=uninstrumented +fun:__bid64_to_binary64=uninstrumented +fun:__bid64_to_binary80=uninstrumented +fun:__bid64_to_int32_ceil=uninstrumented +fun:__bid64_to_int32_floor=uninstrumented +fun:__bid64_to_int32_int=uninstrumented +fun:__bid64_to_int32_rnint=uninstrumented +fun:__bid64_to_int32_rninta=uninstrumented +fun:__bid64_to_int32_xceil=uninstrumented +fun:__bid64_to_int32_xfloor=uninstrumented +fun:__bid64_to_int32_xint=uninstrumented +fun:__bid64_to_int32_xrnint=uninstrumented +fun:__bid64_to_int32_xrninta=uninstrumented +fun:__bid64_to_int64_ceil=uninstrumented +fun:__bid64_to_int64_floor=uninstrumented +fun:__bid64_to_int64_int=uninstrumented +fun:__bid64_to_int64_rnint=uninstrumented +fun:__bid64_to_int64_rninta=uninstrumented +fun:__bid64_to_int64_xceil=uninstrumented +fun:__bid64_to_int64_xfloor=uninstrumented +fun:__bid64_to_int64_xint=uninstrumented +fun:__bid64_to_int64_xrnint=uninstrumented +fun:__bid64_to_int64_xrninta=uninstrumented +fun:__bid64_to_uint32_ceil=uninstrumented +fun:__bid64_to_uint32_floor=uninstrumented +fun:__bid64_to_uint32_int=uninstrumented +fun:__bid64_to_uint32_rnint=uninstrumented +fun:__bid64_to_uint32_rninta=uninstrumented +fun:__bid64_to_uint32_xceil=uninstrumented +fun:__bid64_to_uint32_xfloor=uninstrumented +fun:__bid64_to_uint32_xint=uninstrumented +fun:__bid64_to_uint32_xrnint=uninstrumented +fun:__bid64_to_uint32_xrninta=uninstrumented +fun:__bid64_to_uint64_ceil=uninstrumented +fun:__bid64_to_uint64_floor=uninstrumented +fun:__bid64_to_uint64_int=uninstrumented +fun:__bid64_to_uint64_rnint=uninstrumented +fun:__bid64_to_uint64_rninta=uninstrumented +fun:__bid64_to_uint64_xceil=uninstrumented +fun:__bid64_to_uint64_xfloor=uninstrumented +fun:__bid64_to_uint64_xint=uninstrumented +fun:__bid64_to_uint64_xrnint=uninstrumented +fun:__bid64_to_uint64_xrninta=uninstrumented +fun:__bid64_totalOrder=uninstrumented +fun:__bid64_totalOrderMag=uninstrumented +fun:__bid64ddq_fma=uninstrumented +fun:__bid64dq_add=uninstrumented +fun:__bid64dq_div=uninstrumented +fun:__bid64dq_mul=uninstrumented +fun:__bid64dq_sub=uninstrumented +fun:__bid64dqd_fma=uninstrumented +fun:__bid64dqq_fma=uninstrumented +fun:__bid64qd_add=uninstrumented +fun:__bid64qd_div=uninstrumented +fun:__bid64qd_mul=uninstrumented +fun:__bid64qd_sub=uninstrumented +fun:__bid64qdd_fma=uninstrumented +fun:__bid64qdq_fma=uninstrumented +fun:__bid64qq_add=uninstrumented +fun:__bid64qq_div=uninstrumented +fun:__bid64qq_mul=uninstrumented +fun:__bid64qq_sub=uninstrumented +fun:__bid64qqd_fma=uninstrumented +fun:__bid64qqq_fma=uninstrumented +fun:__bid_adddd3=uninstrumented +fun:__bid_addsd3=uninstrumented +fun:__bid_addtd3=uninstrumented +fun:__bid_divdd3=uninstrumented +fun:__bid_divsd3=uninstrumented +fun:__bid_divtd3=uninstrumented +fun:__bid_eqdd2=uninstrumented +fun:__bid_eqsd2=uninstrumented +fun:__bid_eqtd2=uninstrumented +fun:__bid_extendddtd2=uninstrumented +fun:__bid_extendddtf=uninstrumented +fun:__bid_extendddxf=uninstrumented +fun:__bid_extenddfdd=uninstrumented +fun:__bid_extenddftd=uninstrumented +fun:__bid_extendsddd2=uninstrumented +fun:__bid_extendsddf=uninstrumented +fun:__bid_extendsdtd2=uninstrumented +fun:__bid_extendsdtf=uninstrumented +fun:__bid_extendsdxf=uninstrumented +fun:__bid_extendsfdd=uninstrumented +fun:__bid_extendsfsd=uninstrumented +fun:__bid_extendsftd=uninstrumented +fun:__bid_extendtftd=uninstrumented +fun:__bid_extendxftd=uninstrumented +fun:__bid_fixdddi=uninstrumented +fun:__bid_fixddsi=uninstrumented +fun:__bid_fixsddi=uninstrumented +fun:__bid_fixsdsi=uninstrumented +fun:__bid_fixtddi=uninstrumented +fun:__bid_fixtdsi=uninstrumented +fun:__bid_fixunsdddi=uninstrumented +fun:__bid_fixunsddsi=uninstrumented +fun:__bid_fixunssddi=uninstrumented +fun:__bid_fixunssdsi=uninstrumented +fun:__bid_fixunstddi=uninstrumented +fun:__bid_fixunstdsi=uninstrumented +fun:__bid_floatdidd=uninstrumented +fun:__bid_floatdisd=uninstrumented +fun:__bid_floatditd=uninstrumented +fun:__bid_floatsidd=uninstrumented +fun:__bid_floatsisd=uninstrumented +fun:__bid_floatsitd=uninstrumented +fun:__bid_floatunsdidd=uninstrumented +fun:__bid_floatunsdisd=uninstrumented +fun:__bid_floatunsditd=uninstrumented +fun:__bid_floatunssidd=uninstrumented +fun:__bid_floatunssisd=uninstrumented +fun:__bid_floatunssitd=uninstrumented +fun:__bid_gedd2=uninstrumented +fun:__bid_gesd2=uninstrumented +fun:__bid_getd2=uninstrumented +fun:__bid_gtdd2=uninstrumented +fun:__bid_gtsd2=uninstrumented +fun:__bid_gttd2=uninstrumented +fun:__bid_ledd2=uninstrumented +fun:__bid_lesd2=uninstrumented +fun:__bid_letd2=uninstrumented +fun:__bid_ltdd2=uninstrumented +fun:__bid_ltsd2=uninstrumented +fun:__bid_lttd2=uninstrumented +fun:__bid_muldd3=uninstrumented +fun:__bid_mulsd3=uninstrumented +fun:__bid_multd3=uninstrumented +fun:__bid_nedd2=uninstrumented +fun:__bid_nesd2=uninstrumented +fun:__bid_netd2=uninstrumented +fun:__bid_round128_19_38=uninstrumented +fun:__bid_round192_39_57=uninstrumented +fun:__bid_round256_58_76=uninstrumented +fun:__bid_round64_2_18=uninstrumented +fun:__bid_subdd3=uninstrumented +fun:__bid_subsd3=uninstrumented +fun:__bid_subtd3=uninstrumented +fun:__bid_truncdddf=uninstrumented +fun:__bid_truncddsd2=uninstrumented +fun:__bid_truncddsf=uninstrumented +fun:__bid_truncdfsd=uninstrumented +fun:__bid_truncsdsf=uninstrumented +fun:__bid_trunctddd2=uninstrumented +fun:__bid_trunctddf=uninstrumented +fun:__bid_trunctdsd2=uninstrumented +fun:__bid_trunctdsf=uninstrumented +fun:__bid_trunctdtf=uninstrumented +fun:__bid_trunctdxf=uninstrumented +fun:__bid_trunctfdd=uninstrumented +fun:__bid_trunctfsd=uninstrumented +fun:__bid_truncxfdd=uninstrumented +fun:__bid_truncxfsd=uninstrumented +fun:__bid_unorddd2=uninstrumented +fun:__bid_unordsd2=uninstrumented +fun:__bid_unordtd2=uninstrumented +fun:__binary128_to_bid128=uninstrumented +fun:__binary128_to_bid32=uninstrumented +fun:__binary128_to_bid64=uninstrumented +fun:__binary32_to_bid128=uninstrumented +fun:__binary32_to_bid32=uninstrumented +fun:__binary32_to_bid64=uninstrumented +fun:__binary64_to_bid128=uninstrumented +fun:__binary64_to_bid32=uninstrumented +fun:__binary64_to_bid64=uninstrumented +fun:__binary80_to_bid128=uninstrumented +fun:__binary80_to_bid32=uninstrumented +fun:__binary80_to_bid64=uninstrumented +fun:__bsd_getpgrp=uninstrumented +fun:__bswapdi2=uninstrumented +fun:__bswapsi2=uninstrumented +fun:__bzero=uninstrumented +fun:__chk_fail=uninstrumented +fun:__clear_cache=uninstrumented +fun:__clog10=uninstrumented +fun:__clog10f=uninstrumented +fun:__clog10l=uninstrumented +fun:__clone=uninstrumented +fun:__close=uninstrumented +fun:__close_nocancel=uninstrumented +fun:__clzdi2=uninstrumented +fun:__clzti2=uninstrumented +fun:__cmpti2=uninstrumented +fun:__cmsg_nxthdr=uninstrumented +fun:__condvar_cleanup1=uninstrumented +fun:__condvar_cleanup2=uninstrumented +fun:__confstr_chk=uninstrumented +fun:__connect=uninstrumented +fun:__connect_internal=uninstrumented +fun:__connect_nocancel=uninstrumented +fun:__cosh_finite=uninstrumented +fun:__coshf_finite=uninstrumented +fun:__coshl_finite=uninstrumented +fun:__create_ib_request=uninstrumented +fun:__ctype_b_loc=uninstrumented +fun:__ctype_get_mb_cur_max=uninstrumented +fun:__ctype_init=uninstrumented +fun:__ctype_tolower_loc=uninstrumented +fun:__ctype_toupper_loc=uninstrumented +fun:__ctzdi2=uninstrumented +fun:__ctzti2=uninstrumented +fun:__cxa_at_quick_exit=uninstrumented +fun:__cxa_atexit=uninstrumented +fun:__cxa_finalize=uninstrumented +fun:__cyg_profile_func_enter=uninstrumented +fun:__cyg_profile_func_exit=uninstrumented +fun:__dcgettext=uninstrumented +fun:__deallocate_stack=uninstrumented +fun:__default_morecore=uninstrumented +fun:__deregister_frame=uninstrumented +fun:__deregister_frame_info=uninstrumented +fun:__deregister_frame_info_bases=uninstrumented +fun:__determine_cpumask_size=uninstrumented +fun:__dfp_clear_except=uninstrumented +fun:__dfp_get_round=uninstrumented +fun:__dfp_raise_except=uninstrumented +fun:__dfp_set_round=uninstrumented +fun:__dfp_test_except=uninstrumented +fun:__dgettext=uninstrumented +fun:__divdc3=uninstrumented +fun:__divsc3=uninstrumented +fun:__divtc3=uninstrumented +fun:__divtf3=uninstrumented +fun:__divti3=uninstrumented +fun:__divxc3=uninstrumented +fun:__dn_comp=uninstrumented +fun:__dn_count_labels=uninstrumented +fun:__dn_expand=uninstrumented +fun:__dn_skipname=uninstrumented +fun:__do_global_ctors_aux=uninstrumented +fun:__do_global_dtors_aux=uninstrumented +fun:__do_niscall3=uninstrumented +fun:__dprintf_chk=uninstrumented +fun:__dup2=uninstrumented +fun:__duplocale=uninstrumented +fun:__dyn_pthread_atfork=uninstrumented +fun:__emutls_get_address=uninstrumented +fun:__emutls_register_common=uninstrumented +fun:__enable_execute_stack=uninstrumented +fun:__endmntent=uninstrumented +fun:__eprintf=uninstrumented +fun:__eqtf2=uninstrumented +fun:__errno_location=uninstrumented +fun:__exp10_finite=uninstrumented +fun:__exp10f_finite=uninstrumented +fun:__exp10l_finite=uninstrumented +fun:__exp2_finite=uninstrumented +fun:__exp2f_finite=uninstrumented +fun:__exp2l_finite=uninstrumented +fun:__exp_finite=uninstrumented +fun:__expf_finite=uninstrumented +fun:__expl_finite=uninstrumented +fun:__extenddftf2=uninstrumented +fun:__extendsftf2=uninstrumented +fun:__extendxftf2=uninstrumented +fun:__fbufsize=uninstrumented +fun:__fcntl=uninstrumented +fun:__fcntl_nocancel=uninstrumented +fun:__fdelt_chk=uninstrumented +fun:__fdelt_warn=uninstrumented +fun:__fentry__=uninstrumented +fun:__ffs=uninstrumented +fun:__ffsdi2=uninstrumented +fun:__ffsti2=uninstrumented +fun:__fgets_chk=uninstrumented +fun:__fgets_unlocked_chk=uninstrumented +fun:__fgetws_chk=uninstrumented +fun:__fgetws_unlocked_chk=uninstrumented +fun:__find_in_stack_list=uninstrumented +fun:__find_thread_by_id=uninstrumented +fun:__finite=uninstrumented +fun:__finitef=uninstrumented +fun:__finitel=uninstrumented +fun:__fixdfti=uninstrumented +fun:__fixsfti=uninstrumented +fun:__fixtfdi=uninstrumented +fun:__fixtfsi=uninstrumented +fun:__fixtfti=uninstrumented +fun:__fixunsdfdi=uninstrumented +fun:__fixunsdfti=uninstrumented +fun:__fixunssfdi=uninstrumented +fun:__fixunssfti=uninstrumented +fun:__fixunstfdi=uninstrumented +fun:__fixunstfsi=uninstrumented +fun:__fixunstfti=uninstrumented +fun:__fixunsxfdi=uninstrumented +fun:__fixunsxfti=uninstrumented +fun:__fixxfti=uninstrumented +fun:__flbf=uninstrumented +fun:__floatditf=uninstrumented +fun:__floatsitf=uninstrumented +fun:__floattidf=uninstrumented +fun:__floattisf=uninstrumented +fun:__floattitf=uninstrumented +fun:__floattixf=uninstrumented +fun:__floatunditf=uninstrumented +fun:__floatunsitf=uninstrumented +fun:__floatuntidf=uninstrumented +fun:__floatuntisf=uninstrumented +fun:__floatuntitf=uninstrumented +fun:__floatuntixf=uninstrumented +fun:__flockfile=uninstrumented +fun:__fmod_finite=uninstrumented +fun:__fmodf_finite=uninstrumented +fun:__fmodl_finite=uninstrumented +fun:__follow_path=uninstrumented +fun:__fork=uninstrumented +fun:__fortify_fail=uninstrumented +fun:__fp_nquery=uninstrumented +fun:__fp_query=uninstrumented +fun:__fp_resstat=uninstrumented +fun:__fpclassify=uninstrumented +fun:__fpclassifyf=uninstrumented +fun:__fpclassifyl=uninstrumented +fun:__fpending=uninstrumented +fun:__fprintf_chk=uninstrumented +fun:__fpurge=uninstrumented +fun:__fread_chk=uninstrumented +fun:__fread_unlocked_chk=uninstrumented +fun:__freadable=uninstrumented +fun:__freading=uninstrumented +fun:__free_fdresult=uninstrumented +fun:__free_stacks=uninstrumented +fun:__free_tcb=uninstrumented +fun:__freelocale=uninstrumented +fun:__fsetlocking=uninstrumented +fun:__fstat=uninstrumented +fun:__fsync_nocancel=uninstrumented +fun:__ftrylockfile=uninstrumented +fun:__funlockfile=uninstrumented +fun:__fwprintf_chk=uninstrumented +fun:__fwritable=uninstrumented +fun:__fwriting=uninstrumented +fun:__fxstat=uninstrumented +fun:__fxstat64=uninstrumented +fun:__fxstatat=uninstrumented +fun:__fxstatat64=uninstrumented +fun:__gai_sigqueue=uninstrumented +fun:__gamma_r_finite=uninstrumented +fun:__gammaf_r_finite=uninstrumented +fun:__gammal_r_finite=uninstrumented +fun:__gcc_bcmp=uninstrumented +fun:__gcc_personality_v0=uninstrumented +fun:__gconv_get_alias_db=uninstrumented +fun:__gconv_get_cache=uninstrumented +fun:__gconv_get_modules_db=uninstrumented +fun:__generic_findstack=uninstrumented +fun:__generic_morestack=uninstrumented +fun:__generic_morestack_set_initial_sp=uninstrumented +fun:__generic_releasestack=uninstrumented +fun:__get_cpu_features=uninstrumented +fun:__getcwd_chk=uninstrumented +fun:__getdelim=uninstrumented +fun:__getdomainname_chk=uninstrumented +fun:__getf2=uninstrumented +fun:__getgroups_chk=uninstrumented +fun:__gethostname_chk=uninstrumented +fun:__getlogin_r_chk=uninstrumented +fun:__getmntent_r=uninstrumented +fun:__getpagesize=uninstrumented +fun:__getpgid=uninstrumented +fun:__getpid=uninstrumented +fun:__gets_chk=uninstrumented +fun:__gettimeofday=uninstrumented +fun:__getwd_chk=uninstrumented +fun:__gmtime_r=uninstrumented +fun:__gttf2=uninstrumented +fun:__h_errno_location=uninstrumented +fun:__hostalias=uninstrumented +fun:__hypot_finite=uninstrumented +fun:__hypotf_finite=uninstrumented +fun:__hypotl_finite=uninstrumented +fun:__init_sched_fifo_prio=uninstrumented +fun:__internal_endnetgrent=uninstrumented +fun:__internal_getnetgrent_r=uninstrumented +fun:__internal_setnetgrent=uninstrumented +fun:__isalnum_l=uninstrumented +fun:__isalpha_l=uninstrumented +fun:__isascii_l=uninstrumented +fun:__isblank_l=uninstrumented +fun:__iscntrl_l=uninstrumented +fun:__isctype=uninstrumented +fun:__isdigit_l=uninstrumented +fun:__isgraph_l=uninstrumented +fun:__isinf=uninstrumented +fun:__isinff=uninstrumented +fun:__isinfl=uninstrumented +fun:__islower_l=uninstrumented +fun:__isnan=uninstrumented +fun:__isnanf=uninstrumented +fun:__isnanl=uninstrumented +fun:__isoc99_fscanf=uninstrumented +fun:__isoc99_fwscanf=uninstrumented +fun:__isoc99_scanf=uninstrumented +fun:__isoc99_sscanf=uninstrumented +fun:__isoc99_swscanf=uninstrumented +fun:__isoc99_vfscanf=uninstrumented +fun:__isoc99_vfwscanf=uninstrumented +fun:__isoc99_vscanf=uninstrumented +fun:__isoc99_vsscanf=uninstrumented +fun:__isoc99_vswscanf=uninstrumented +fun:__isoc99_vwscanf=uninstrumented +fun:__isoc99_wscanf=uninstrumented +fun:__isprint_l=uninstrumented +fun:__ispunct_l=uninstrumented +fun:__isspace_l=uninstrumented +fun:__isupper_l=uninstrumented +fun:__iswalnum_l=uninstrumented +fun:__iswalpha_l=uninstrumented +fun:__iswblank_l=uninstrumented +fun:__iswcntrl_l=uninstrumented +fun:__iswctype=uninstrumented +fun:__iswctype_l=uninstrumented +fun:__iswdigit_l=uninstrumented +fun:__iswgraph_l=uninstrumented +fun:__iswlower_l=uninstrumented +fun:__iswprint_l=uninstrumented +fun:__iswpunct_l=uninstrumented +fun:__iswspace_l=uninstrumented +fun:__iswupper_l=uninstrumented +fun:__iswxdigit_l=uninstrumented +fun:__isxdigit_l=uninstrumented +fun:__ivaliduser=uninstrumented +fun:__j0_finite=uninstrumented +fun:__j0f_finite=uninstrumented +fun:__j0l_finite=uninstrumented +fun:__j1_finite=uninstrumented +fun:__j1f_finite=uninstrumented +fun:__j1l_finite=uninstrumented +fun:__jn_finite=uninstrumented +fun:__jnf_finite=uninstrumented +fun:__jnl_finite=uninstrumented +fun:__letf2=uninstrumented +fun:__lgamma_r_finite=uninstrumented +fun:__lgammaf_r_finite=uninstrumented +fun:__lgammal_r_finite=uninstrumented +fun:__libc_accept=uninstrumented +fun:__libc_alloca_cutoff=uninstrumented +fun:__libc_allocate_rtsig=uninstrumented +fun:__libc_allocate_rtsig_private=uninstrumented +fun:__libc_calloc=uninstrumented +fun:__libc_clntudp_bufcreate=uninstrumented +fun:__libc_close=uninstrumented +fun:__libc_connect=uninstrumented +fun:__libc_csu_fini=uninstrumented +fun:__libc_csu_init=uninstrumented +fun:__libc_current_sigrtmax=uninstrumented +fun:__libc_current_sigrtmax_private=uninstrumented +fun:__libc_current_sigrtmin=uninstrumented +fun:__libc_current_sigrtmin_private=uninstrumented +fun:__libc_dl_error_tsd=uninstrumented +fun:__libc_dlclose=uninstrumented +fun:__libc_dlopen_mode=uninstrumented +fun:__libc_dlsym=uninstrumented +fun:__libc_fatal=uninstrumented +fun:__libc_fcntl=uninstrumented +fun:__libc_fork=uninstrumented +fun:__libc_free=uninstrumented +fun:__libc_freeres=uninstrumented +fun:__libc_fsync=uninstrumented +fun:__libc_init_first=uninstrumented +fun:__libc_longjmp=uninstrumented +fun:__libc_lseek=uninstrumented +fun:__libc_lseek64=uninstrumented +fun:__libc_mallinfo=uninstrumented +fun:__libc_malloc=uninstrumented +fun:__libc_mallopt=uninstrumented +fun:__libc_memalign=uninstrumented +fun:__libc_msync=uninstrumented +fun:__libc_nanosleep=uninstrumented +fun:__libc_open=uninstrumented +fun:__libc_pause=uninstrumented +fun:__libc_pread=uninstrumented +fun:__libc_pread64=uninstrumented +fun:__libc_pthread_init=uninstrumented +fun:__libc_pvalloc=uninstrumented +fun:__libc_pwrite=uninstrumented +fun:__libc_pwrite64=uninstrumented +fun:__libc_read=uninstrumented +fun:__libc_realloc=uninstrumented +fun:__libc_recv=uninstrumented +fun:__libc_recvfrom=uninstrumented +fun:__libc_recvmsg=uninstrumented +fun:__libc_res_nquery=uninstrumented +fun:__libc_res_nsearch=uninstrumented +fun:__libc_rpc_getport=uninstrumented +fun:__libc_sa_len=uninstrumented +fun:__libc_send=uninstrumented +fun:__libc_sendmsg=uninstrumented +fun:__libc_sendto=uninstrumented +fun:__libc_sigaction=uninstrumented +fun:__libc_siglongjmp=uninstrumented +fun:__libc_sigsuspend=uninstrumented +fun:__libc_sigwait=uninstrumented +fun:__libc_start_main=uninstrumented +fun:__libc_system=uninstrumented +fun:__libc_tcdrain=uninstrumented +fun:__libc_thread_freeres=uninstrumented +fun:__libc_valloc=uninstrumented +fun:__libc_wait=uninstrumented +fun:__libc_waitpid=uninstrumented +fun:__libc_write=uninstrumented +fun:__lll_lock_wait=uninstrumented +fun:__lll_lock_wait_private=uninstrumented +fun:__lll_robust_lock_wait=uninstrumented +fun:__lll_robust_timedlock_wait=uninstrumented +fun:__lll_timedlock_wait=uninstrumented +fun:__lll_timedwait_tid=uninstrumented +fun:__lll_unlock_wake=uninstrumented +fun:__lll_unlock_wake_private=uninstrumented +fun:__llseek=uninstrumented +fun:__loc_aton=uninstrumented +fun:__loc_ntoa=uninstrumented +fun:__log10_finite=uninstrumented +fun:__log10f_finite=uninstrumented +fun:__log10l_finite=uninstrumented +fun:__log2_finite=uninstrumented +fun:__log2f_finite=uninstrumented +fun:__log2l_finite=uninstrumented +fun:__log_finite=uninstrumented +fun:__logf_finite=uninstrumented +fun:__logl_finite=uninstrumented +fun:__longjmp_chk=uninstrumented +fun:__lseek=uninstrumented +fun:__lseek64=uninstrumented +fun:__lseek_nocancel=uninstrumented +fun:__lshrti3=uninstrumented +fun:__lstat=uninstrumented +fun:__lttf2=uninstrumented +fun:__lxstat=uninstrumented +fun:__lxstat64=uninstrumented +fun:__make_stacks_executable=uninstrumented +fun:__mbrlen=uninstrumented +fun:__mbrtowc=uninstrumented +fun:__mbsnrtowcs_chk=uninstrumented +fun:__mbsrtowcs_chk=uninstrumented +fun:__mbstowcs_chk=uninstrumented +fun:__memcpy_chk=uninstrumented +fun:__memmove_chk=uninstrumented +fun:__mempcpy=uninstrumented +fun:__mempcpy_chk=uninstrumented +fun:__mempcpy_small=uninstrumented +fun:__memset_chk=uninstrumented +fun:__mknod=uninstrumented +fun:__modti3=uninstrumented +fun:__monstartup=uninstrumented +fun:__morestack=uninstrumented +fun:__morestack_allocate_stack_space=uninstrumented +fun:__morestack_block_signals=uninstrumented +fun:__morestack_fail=uninstrumented +fun:__morestack_large_model=uninstrumented +fun:__morestack_load_mmap=uninstrumented +fun:__morestack_non_split=uninstrumented +fun:__morestack_release_segments=uninstrumented +fun:__morestack_unblock_signals=uninstrumented +fun:__mq_open_2=uninstrumented +fun:__msgrcv=uninstrumented +fun:__msgrcv_nocancel=uninstrumented +fun:__msgsnd=uninstrumented +fun:__msgsnd_nocancel=uninstrumented +fun:__msync_nocancel=uninstrumented +fun:__muldc3=uninstrumented +fun:__mulsc3=uninstrumented +fun:__multc3=uninstrumented +fun:__multf3=uninstrumented +fun:__multi3=uninstrumented +fun:__mulvdi3=uninstrumented +fun:__mulvsi3=uninstrumented +fun:__mulvti3=uninstrumented +fun:__mulxc3=uninstrumented +fun:__nanosleep=uninstrumented +fun:__nanosleep_nocancel=uninstrumented +fun:__negtf2=uninstrumented +fun:__negti2=uninstrumented +fun:__negvdi2=uninstrumented +fun:__negvsi2=uninstrumented +fun:__negvti2=uninstrumented +fun:__netf2=uninstrumented +fun:__new_sem_destroy=uninstrumented +fun:__new_sem_getvalue=uninstrumented +fun:__new_sem_init=uninstrumented +fun:__newlocale=uninstrumented +fun:__nis_default_access=uninstrumented +fun:__nis_default_group=uninstrumented +fun:__nis_default_owner=uninstrumented +fun:__nis_default_ttl=uninstrumented +fun:__nis_finddirectory=uninstrumented +fun:__nis_hash=uninstrumented +fun:__nisbind_connect=uninstrumented +fun:__nisbind_create=uninstrumented +fun:__nisbind_destroy=uninstrumented +fun:__nisbind_next=uninstrumented +fun:__nl_langinfo_l=uninstrumented +fun:__nptl_create_event=uninstrumented +fun:__nptl_deallocate_tsd=uninstrumented +fun:__nptl_death_event=uninstrumented +fun:__nptl_main=uninstrumented +fun:__nptl_set_robust=uninstrumented +fun:__nptl_setxid=uninstrumented +fun:__ns_get16=uninstrumented +fun:__ns_get32=uninstrumented +fun:__ns_name_ntop=uninstrumented +fun:__ns_name_unpack=uninstrumented +fun:__nss_configure_lookup=uninstrumented +fun:__nss_database_lookup=uninstrumented +fun:__nss_disable_nscd=uninstrumented +fun:__nss_group_lookup=uninstrumented +fun:__nss_group_lookup2=uninstrumented +fun:__nss_hostname_digits_dots=uninstrumented +fun:__nss_hosts_lookup=uninstrumented +fun:__nss_hosts_lookup2=uninstrumented +fun:__nss_lookup=uninstrumented +fun:__nss_lookup_function=uninstrumented +fun:__nss_next=uninstrumented +fun:__nss_next2=uninstrumented +fun:__nss_passwd_lookup=uninstrumented +fun:__nss_passwd_lookup2=uninstrumented +fun:__nss_services_lookup2=uninstrumented +fun:__obstack_printf_chk=uninstrumented +fun:__obstack_vprintf_chk=uninstrumented +fun:__open=uninstrumented +fun:__open64=uninstrumented +fun:__open64_2=uninstrumented +fun:__open_2=uninstrumented +fun:__open_catalog=uninstrumented +fun:__open_nocancel=uninstrumented +fun:__openat64_2=uninstrumented +fun:__openat_2=uninstrumented +fun:__overflow=uninstrumented +fun:__p_cdname=uninstrumented +fun:__p_cdnname=uninstrumented +fun:__p_class=uninstrumented +fun:__p_fqname=uninstrumented +fun:__p_fqnname=uninstrumented +fun:__p_option=uninstrumented +fun:__p_query=uninstrumented +fun:__p_rcode=uninstrumented +fun:__p_secstodate=uninstrumented +fun:__p_time=uninstrumented +fun:__p_type=uninstrumented +fun:__paritydi2=uninstrumented +fun:__parityti2=uninstrumented +fun:__pause_nocancel=uninstrumented +fun:__pipe=uninstrumented +fun:__poll=uninstrumented +fun:__popcountdi2=uninstrumented +fun:__popcountti2=uninstrumented +fun:__posix_getopt=uninstrumented +fun:__pow_finite=uninstrumented +fun:__powf_finite=uninstrumented +fun:__powidf2=uninstrumented +fun:__powisf2=uninstrumented +fun:__powitf2=uninstrumented +fun:__powixf2=uninstrumented +fun:__powl_finite=uninstrumented +fun:__pread=uninstrumented +fun:__pread64=uninstrumented +fun:__pread64_chk=uninstrumented +fun:__pread_chk=uninstrumented +fun:__pread_nocancel=uninstrumented +fun:__prepare_niscall=uninstrumented +fun:__printf_chk=uninstrumented +fun:__printf_fp=uninstrumented +fun:__profile_frequency=uninstrumented +fun:__pthread_atfork=uninstrumented +fun:__pthread_attr_destroy=uninstrumented +fun:__pthread_attr_getaffinity_new=uninstrumented +fun:__pthread_attr_getaffinity_old=uninstrumented +fun:__pthread_attr_getdetachstate=uninstrumented +fun:__pthread_attr_getinheritsched=uninstrumented +fun:__pthread_attr_getschedparam=uninstrumented +fun:__pthread_attr_getschedpolicy=uninstrumented +fun:__pthread_attr_getscope=uninstrumented +fun:__pthread_attr_getstack=uninstrumented +fun:__pthread_attr_getstackaddr=uninstrumented +fun:__pthread_attr_getstacksize=uninstrumented +fun:__pthread_attr_init_2_1=uninstrumented +fun:__pthread_attr_setaffinity_new=uninstrumented +fun:__pthread_attr_setaffinity_old=uninstrumented +fun:__pthread_attr_setdetachstate=uninstrumented +fun:__pthread_attr_setinheritsched=uninstrumented +fun:__pthread_attr_setschedparam=uninstrumented +fun:__pthread_attr_setschedpolicy=uninstrumented +fun:__pthread_attr_setscope=uninstrumented +fun:__pthread_attr_setstack=uninstrumented +fun:__pthread_attr_setstackaddr=uninstrumented +fun:__pthread_attr_setstacksize=uninstrumented +fun:__pthread_cleanup_pop=uninstrumented +fun:__pthread_cleanup_pop_restore=uninstrumented +fun:__pthread_cleanup_push=uninstrumented +fun:__pthread_cleanup_push_defer=uninstrumented +fun:__pthread_cleanup_routine=uninstrumented +fun:__pthread_cleanup_upto=uninstrumented +fun:__pthread_clock_gettime=uninstrumented +fun:__pthread_clock_settime=uninstrumented +fun:__pthread_cond_broadcast=uninstrumented +fun:__pthread_cond_broadcast_2_0=uninstrumented +fun:__pthread_cond_destroy=uninstrumented +fun:__pthread_cond_destroy_2_0=uninstrumented +fun:__pthread_cond_init=uninstrumented +fun:__pthread_cond_init_2_0=uninstrumented +fun:__pthread_cond_signal=uninstrumented +fun:__pthread_cond_signal_2_0=uninstrumented +fun:__pthread_cond_timedwait=uninstrumented +fun:__pthread_cond_timedwait_2_0=uninstrumented +fun:__pthread_cond_wait=uninstrumented +fun:__pthread_cond_wait_2_0=uninstrumented +fun:__pthread_condattr_destroy=uninstrumented +fun:__pthread_condattr_init=uninstrumented +fun:__pthread_create_2_1=uninstrumented +fun:__pthread_current_priority=uninstrumented +fun:__pthread_disable_asynccancel=uninstrumented +fun:__pthread_enable_asynccancel=uninstrumented +fun:__pthread_equal=uninstrumented +fun:__pthread_exit=uninstrumented +fun:__pthread_get_minstack=uninstrumented +fun:__pthread_getaffinity_new=uninstrumented +fun:__pthread_getaffinity_np=uninstrumented +fun:__pthread_getaffinity_old=uninstrumented +fun:__pthread_getschedparam=uninstrumented +fun:__pthread_getspecific=uninstrumented +fun:__pthread_getspecific_internal=uninstrumented +fun:__pthread_init_static_tls=uninstrumented +fun:__pthread_initialize_minimal=uninstrumented +fun:__pthread_initialize_minimal_internal=uninstrumented +fun:__pthread_key_create=uninstrumented +fun:__pthread_key_create_internal=uninstrumented +fun:__pthread_kill=uninstrumented +fun:__pthread_kill_other_threads_np=uninstrumented +fun:__pthread_mutex_cond_lock=uninstrumented +fun:__pthread_mutex_cond_lock_adjust=uninstrumented +fun:__pthread_mutex_cond_lock_full=uninstrumented +fun:__pthread_mutex_destroy=uninstrumented +fun:__pthread_mutex_destroy_internal=uninstrumented +fun:__pthread_mutex_init=uninstrumented +fun:__pthread_mutex_init_internal=uninstrumented +fun:__pthread_mutex_lock=uninstrumented +fun:__pthread_mutex_lock_full=uninstrumented +fun:__pthread_mutex_lock_internal=uninstrumented +fun:__pthread_mutex_trylock=uninstrumented +fun:__pthread_mutex_unlock=uninstrumented +fun:__pthread_mutex_unlock_full=uninstrumented +fun:__pthread_mutex_unlock_internal=uninstrumented +fun:__pthread_mutex_unlock_usercnt=uninstrumented +fun:__pthread_mutexattr_destroy=uninstrumented +fun:__pthread_mutexattr_init=uninstrumented +fun:__pthread_mutexattr_settype=uninstrumented +fun:__pthread_once=uninstrumented +fun:__pthread_once_internal=uninstrumented +fun:__pthread_register_cancel=uninstrumented +fun:__pthread_register_cancel_defer=uninstrumented +fun:__pthread_rwlock_destroy=uninstrumented +fun:__pthread_rwlock_init=uninstrumented +fun:__pthread_rwlock_rdlock=uninstrumented +fun:__pthread_rwlock_rdlock_internal=uninstrumented +fun:__pthread_rwlock_tryrdlock=uninstrumented +fun:__pthread_rwlock_trywrlock=uninstrumented +fun:__pthread_rwlock_unlock=uninstrumented +fun:__pthread_rwlock_unlock_internal=uninstrumented +fun:__pthread_rwlock_wrlock=uninstrumented +fun:__pthread_rwlock_wrlock_internal=uninstrumented +fun:__pthread_self=uninstrumented +fun:__pthread_setaffinity_new=uninstrumented +fun:__pthread_setaffinity_old=uninstrumented +fun:__pthread_setcancelstate=uninstrumented +fun:__pthread_setcanceltype=uninstrumented +fun:__pthread_setschedparam=uninstrumented +fun:__pthread_setspecific=uninstrumented +fun:__pthread_setspecific_internal=uninstrumented +fun:__pthread_tpp_change_priority=uninstrumented +fun:__pthread_unregister_cancel=uninstrumented +fun:__pthread_unregister_cancel_restore=uninstrumented +fun:__pthread_unwind=uninstrumented +fun:__pthread_unwind_next=uninstrumented +fun:__ptsname_r_chk=uninstrumented +fun:__putlong=uninstrumented +fun:__putshort=uninstrumented +fun:__pwrite=uninstrumented +fun:__pwrite64=uninstrumented +fun:__pwrite_nocancel=uninstrumented +fun:__rawmemchr=uninstrumented +fun:__read=uninstrumented +fun:__read_chk=uninstrumented +fun:__read_nocancel=uninstrumented +fun:__readlink_chk=uninstrumented +fun:__readlinkat_chk=uninstrumented +fun:__realpath_chk=uninstrumented +fun:__reclaim_stacks=uninstrumented +fun:__recv=uninstrumented +fun:__recv_chk=uninstrumented +fun:__recvfrom=uninstrumented +fun:__recvfrom_chk=uninstrumented +fun:__recvfrom_nocancel=uninstrumented +fun:__recvmsg=uninstrumented +fun:__recvmsg_nocancel=uninstrumented +fun:__register_atfork=uninstrumented +fun:__register_frame=uninstrumented +fun:__register_frame_info=uninstrumented +fun:__register_frame_info_bases=uninstrumented +fun:__register_frame_info_table=uninstrumented +fun:__register_frame_info_table_bases=uninstrumented +fun:__register_frame_table=uninstrumented +fun:__remainder_finite=uninstrumented +fun:__remainderf_finite=uninstrumented +fun:__remainderl_finite=uninstrumented +fun:__res_close=uninstrumented +fun:__res_dnok=uninstrumented +fun:__res_hnok=uninstrumented +fun:__res_hostalias=uninstrumented +fun:__res_iclose=uninstrumented +fun:__res_init=uninstrumented +fun:__res_isourserver=uninstrumented +fun:__res_mailok=uninstrumented +fun:__res_maybe_init=uninstrumented +fun:__res_mkquery=uninstrumented +fun:__res_nameinquery=uninstrumented +fun:__res_nclose=uninstrumented +fun:__res_ninit=uninstrumented +fun:__res_nmkquery=uninstrumented +fun:__res_nquery=uninstrumented +fun:__res_nquerydomain=uninstrumented +fun:__res_nsearch=uninstrumented +fun:__res_nsend=uninstrumented +fun:__res_ownok=uninstrumented +fun:__res_queriesmatch=uninstrumented +fun:__res_query=uninstrumented +fun:__res_querydomain=uninstrumented +fun:__res_randomid=uninstrumented +fun:__res_search=uninstrumented +fun:__res_send=uninstrumented +fun:__res_state=uninstrumented +fun:__restore_rt=uninstrumented +fun:__rpc_thread_createerr=uninstrumented +fun:__rpc_thread_svc_fdset=uninstrumented +fun:__rpc_thread_svc_max_pollfd=uninstrumented +fun:__rpc_thread_svc_pollfd=uninstrumented +fun:__sbrk=uninstrumented +fun:__scalb_finite=uninstrumented +fun:__scalbf_finite=uninstrumented +fun:__scalbl_finite=uninstrumented +fun:__sched_cpualloc=uninstrumented +fun:__sched_cpucount=uninstrumented +fun:__sched_cpufree=uninstrumented +fun:__sched_get_priority_max=uninstrumented +fun:__sched_get_priority_min=uninstrumented +fun:__sched_getparam=uninstrumented +fun:__sched_getscheduler=uninstrumented +fun:__sched_setscheduler=uninstrumented +fun:__sched_yield=uninstrumented +fun:__secure_getenv=uninstrumented +fun:__select=uninstrumented +fun:__sem_search=uninstrumented +fun:__send=uninstrumented +fun:__sendmsg=uninstrumented +fun:__sendmsg_nocancel=uninstrumented +fun:__sendto=uninstrumented +fun:__sendto_nocancel=uninstrumented +fun:__setmntent=uninstrumented +fun:__setpgid=uninstrumented +fun:__sigaction=uninstrumented +fun:__sigaddset=uninstrumented +fun:__sigdelset=uninstrumented +fun:__sigismember=uninstrumented +fun:__signbit=uninstrumented +fun:__signbitf=uninstrumented +fun:__signbitl=uninstrumented +fun:__sigpause=uninstrumented +fun:__sigsetjmp=uninstrumented +fun:__sigsuspend=uninstrumented +fun:__sigsuspend_nocancel=uninstrumented +fun:__sigwait=uninstrumented +fun:__sinh_finite=uninstrumented +fun:__sinhf_finite=uninstrumented +fun:__sinhl_finite=uninstrumented +fun:__snprintf_chk=uninstrumented +fun:__splitstack_find=uninstrumented +fun:__sprintf_chk=uninstrumented +fun:__sqrt_finite=uninstrumented +fun:__sqrtf_finite=uninstrumented +fun:__sqrtl_finite=uninstrumented +fun:__stack_chk_fail=uninstrumented +fun:__stack_chk_fail_local=uninstrumented +fun:__stack_split_initialize=uninstrumented +fun:__stat=uninstrumented +fun:__statfs=uninstrumented +fun:__stpcpy=uninstrumented +fun:__stpcpy_chk=uninstrumented +fun:__stpcpy_small=uninstrumented +fun:__stpncpy=uninstrumented +fun:__stpncpy_chk=uninstrumented +fun:__strcasecmp=uninstrumented +fun:__strcasecmp_l=uninstrumented +fun:__strcasestr=uninstrumented +fun:__strcat_chk=uninstrumented +fun:__strcoll_l=uninstrumented +fun:__strcpy_chk=uninstrumented +fun:__strcpy_small=uninstrumented +fun:__strcspn_c1=uninstrumented +fun:__strcspn_c2=uninstrumented +fun:__strcspn_c3=uninstrumented +fun:__strdup=uninstrumented +fun:__strerror_r=uninstrumented +fun:__strfmon_l=uninstrumented +fun:__strftime_l=uninstrumented +fun:__strncasecmp_l=uninstrumented +fun:__strncat_chk=uninstrumented +fun:__strncpy_chk=uninstrumented +fun:__strndup=uninstrumented +fun:__strpbrk_c2=uninstrumented +fun:__strpbrk_c3=uninstrumented +fun:__strsep_1c=uninstrumented +fun:__strsep_2c=uninstrumented +fun:__strsep_3c=uninstrumented +fun:__strsep_g=uninstrumented +fun:__strspn_c1=uninstrumented +fun:__strspn_c2=uninstrumented +fun:__strspn_c3=uninstrumented +fun:__strtod_internal=uninstrumented +fun:__strtod_l=uninstrumented +fun:__strtof_internal=uninstrumented +fun:__strtof_l=uninstrumented +fun:__strtok_r=uninstrumented +fun:__strtok_r_1c=uninstrumented +fun:__strtol_internal=uninstrumented +fun:__strtol_l=uninstrumented +fun:__strtold_internal=uninstrumented +fun:__strtold_l=uninstrumented +fun:__strtoll_internal=uninstrumented +fun:__strtoll_l=uninstrumented +fun:__strtoul_internal=uninstrumented +fun:__strtoul_l=uninstrumented +fun:__strtoull_internal=uninstrumented +fun:__strtoull_l=uninstrumented +fun:__strverscmp=uninstrumented +fun:__strxfrm_l=uninstrumented +fun:__subtf3=uninstrumented +fun:__subvdi3=uninstrumented +fun:__subvsi3=uninstrumented +fun:__subvti3=uninstrumented +fun:__swprintf_chk=uninstrumented +fun:__sym_ntop=uninstrumented +fun:__sym_ntos=uninstrumented +fun:__sym_ston=uninstrumented +fun:__sysconf=uninstrumented +fun:__sysctl=uninstrumented +fun:__syslog_chk=uninstrumented +fun:__sysv_signal=uninstrumented +fun:__tls_get_addr=uninstrumented +fun:__toascii_l=uninstrumented +fun:__tolower_l=uninstrumented +fun:__toupper_l=uninstrumented +fun:__towctrans=uninstrumented +fun:__towctrans_l=uninstrumented +fun:__towlower_l=uninstrumented +fun:__towupper_l=uninstrumented +fun:__trunctfdf2=uninstrumented +fun:__trunctfsf2=uninstrumented +fun:__trunctfxf2=uninstrumented +fun:__ttyname_r_chk=uninstrumented +fun:__ucmpti2=uninstrumented +fun:__udiv_w_sdiv=uninstrumented +fun:__udivmodti4=uninstrumented +fun:__udivti3=uninstrumented +fun:__uflow=uninstrumented +fun:__umodti3=uninstrumented +fun:__underflow=uninstrumented +fun:__unordtf2=uninstrumented +fun:__unwind_freeres=uninstrumented +fun:__uselocale=uninstrumented +fun:__vasprintf_chk=uninstrumented +fun:__vdprintf_chk=uninstrumented +fun:__vfork=uninstrumented +fun:__vfprintf_chk=uninstrumented +fun:__vfscanf=uninstrumented +fun:__vfwprintf_chk=uninstrumented +fun:__vprintf_chk=uninstrumented +fun:__vsnprintf=uninstrumented +fun:__vsnprintf_chk=uninstrumented +fun:__vsprintf_chk=uninstrumented +fun:__vsscanf=uninstrumented +fun:__vswprintf_chk=uninstrumented +fun:__vsyslog_chk=uninstrumented +fun:__vwprintf_chk=uninstrumented +fun:__wait=uninstrumented +fun:__wait_lookup_done=uninstrumented +fun:__waitpid=uninstrumented +fun:__warn_memset_zero_len=uninstrumented +fun:__wcpcpy_chk=uninstrumented +fun:__wcpncpy_chk=uninstrumented +fun:__wcrtomb_chk=uninstrumented +fun:__wcscasecmp_l=uninstrumented +fun:__wcscat_chk=uninstrumented +fun:__wcscoll_l=uninstrumented +fun:__wcscpy_chk=uninstrumented +fun:__wcsftime_l=uninstrumented +fun:__wcsncasecmp_l=uninstrumented +fun:__wcsncat_chk=uninstrumented +fun:__wcsncpy_chk=uninstrumented +fun:__wcsnrtombs_chk=uninstrumented +fun:__wcsrtombs_chk=uninstrumented +fun:__wcstod_internal=uninstrumented +fun:__wcstod_l=uninstrumented +fun:__wcstof_internal=uninstrumented +fun:__wcstof_l=uninstrumented +fun:__wcstol_internal=uninstrumented +fun:__wcstol_l=uninstrumented +fun:__wcstold_internal=uninstrumented +fun:__wcstold_l=uninstrumented +fun:__wcstoll_internal=uninstrumented +fun:__wcstoll_l=uninstrumented +fun:__wcstombs_chk=uninstrumented +fun:__wcstoul_internal=uninstrumented +fun:__wcstoul_l=uninstrumented +fun:__wcstoull_internal=uninstrumented +fun:__wcstoull_l=uninstrumented +fun:__wcsxfrm_l=uninstrumented +fun:__wctomb_chk=uninstrumented +fun:__wctrans_l=uninstrumented +fun:__wctype_l=uninstrumented +fun:__where_is_shmfs=uninstrumented +fun:__wmemcpy_chk=uninstrumented +fun:__wmemmove_chk=uninstrumented +fun:__wmempcpy_chk=uninstrumented +fun:__wmemset_chk=uninstrumented +fun:__woverflow=uninstrumented +fun:__wprintf_chk=uninstrumented +fun:__wrap_pthread_create=uninstrumented +fun:__write=uninstrumented +fun:__write_nocancel=uninstrumented +fun:__wuflow=uninstrumented +fun:__wunderflow=uninstrumented +fun:__xmknod=uninstrumented +fun:__xmknodat=uninstrumented +fun:__xpg_basename=uninstrumented +fun:__xpg_sigpause=uninstrumented +fun:__xpg_strerror_r=uninstrumented +fun:__xstat=uninstrumented +fun:__xstat64=uninstrumented +fun:__y0_finite=uninstrumented +fun:__y0f_finite=uninstrumented +fun:__y0l_finite=uninstrumented +fun:__y1_finite=uninstrumented +fun:__y1f_finite=uninstrumented +fun:__y1l_finite=uninstrumented +fun:__yn_finite=uninstrumented +fun:__ynf_finite=uninstrumented +fun:__ynl_finite=uninstrumented +fun:__yp_check=uninstrumented +fun:_authenticate=uninstrumented +fun:_dl_addr=uninstrumented +fun:_dl_allocate_tls=uninstrumented +fun:_dl_allocate_tls_init=uninstrumented +fun:_dl_deallocate_tls=uninstrumented +fun:_dl_debug_state=uninstrumented +fun:_dl_get_tls_static_info=uninstrumented +fun:_dl_make_stack_executable=uninstrumented +fun:_dl_mcount=uninstrumented +fun:_dl_mcount_wrapper=uninstrumented +fun:_dl_mcount_wrapper_check=uninstrumented +fun:_dl_rtld_di_serinfo=uninstrumented +fun:_dl_sym=uninstrumented +fun:_dl_tls_setup=uninstrumented +fun:_dl_vsym=uninstrumented +fun:_exit=uninstrumented +fun:_fini=uninstrumented +fun:_flushlbf=uninstrumented +fun:_gethtbyaddr=uninstrumented +fun:_gethtbyname=uninstrumented +fun:_gethtbyname2=uninstrumented +fun:_gethtent=uninstrumented +fun:_getlong=uninstrumented +fun:_getshort=uninstrumented +fun:_init=uninstrumented +fun:_longjmp=uninstrumented +fun:_mcleanup=uninstrumented +fun:_mcount=uninstrumented +fun:_nsl_default_nss=uninstrumented +fun:_nss_files_parse_grent=uninstrumented +fun:_nss_files_parse_pwent=uninstrumented +fun:_nss_files_parse_sgent=uninstrumented +fun:_nss_files_parse_spent=uninstrumented +fun:_obstack_allocated_p=uninstrumented +fun:_obstack_begin=uninstrumented +fun:_obstack_begin_1=uninstrumented +fun:_obstack_free=uninstrumented +fun:_obstack_memory_used=uninstrumented +fun:_obstack_newchunk=uninstrumented +fun:_pthread_cleanup_pop=uninstrumented +fun:_pthread_cleanup_pop_restore=uninstrumented +fun:_pthread_cleanup_push=uninstrumented +fun:_pthread_cleanup_push_defer=uninstrumented +fun:_rpc_dtablesize=uninstrumented +fun:_seterr_reply=uninstrumented +fun:_sethtent=uninstrumented +fun:_setjmp=uninstrumented +fun:_tolower=uninstrumented +fun:_toupper=uninstrumented +fun:_xdr_ib_request=uninstrumented +fun:_xdr_nis_result=uninstrumented +fun:a64l=uninstrumented +fun:abort=uninstrumented +fun:abs=uninstrumented +fun:accept=uninstrumented +fun:accept4=uninstrumented +fun:access=uninstrumented +fun:acct=uninstrumented +fun:acos=uninstrumented +fun:acosf=uninstrumented +fun:acosh=uninstrumented +fun:acoshf=uninstrumented +fun:acoshl=uninstrumented +fun:acosl=uninstrumented +fun:add_and_round.constprop.0=uninstrumented +fun:addmntent=uninstrumented +fun:addseverity=uninstrumented +fun:adjtime=uninstrumented +fun:adjtimex=uninstrumented +fun:advance=uninstrumented +fun:aio_cancel=uninstrumented +fun:aio_cancel64=uninstrumented +fun:aio_error=uninstrumented +fun:aio_error64=uninstrumented +fun:aio_fsync=uninstrumented +fun:aio_fsync64=uninstrumented +fun:aio_init=uninstrumented +fun:aio_read=uninstrumented +fun:aio_read64=uninstrumented +fun:aio_return=uninstrumented +fun:aio_return64=uninstrumented +fun:aio_suspend=uninstrumented +fun:aio_suspend64=uninstrumented +fun:aio_write=uninstrumented +fun:aio_write64=uninstrumented +fun:alarm=uninstrumented +fun:alphasort=uninstrumented +fun:alphasort64=uninstrumented +fun:arch_prctl=uninstrumented +fun:argp_error=uninstrumented +fun:argp_failure=uninstrumented +fun:argp_help=uninstrumented +fun:argp_parse=uninstrumented +fun:argp_state_help=uninstrumented +fun:argp_usage=uninstrumented +fun:argz_add=uninstrumented +fun:argz_add_sep=uninstrumented +fun:argz_append=uninstrumented +fun:argz_count=uninstrumented +fun:argz_create=uninstrumented +fun:argz_create_sep=uninstrumented +fun:argz_delete=uninstrumented +fun:argz_extract=uninstrumented +fun:argz_insert=uninstrumented +fun:argz_next=uninstrumented +fun:argz_replace=uninstrumented +fun:argz_stringify=uninstrumented +fun:asctime=uninstrumented +fun:asctime_r=uninstrumented +fun:asin=uninstrumented +fun:asinf=uninstrumented +fun:asinh=uninstrumented +fun:asinhf=uninstrumented +fun:asinhl=uninstrumented +fun:asinl=uninstrumented +fun:asprintf=uninstrumented +fun:at_quick_exit=uninstrumented +fun:atan=uninstrumented +fun:atan2=uninstrumented +fun:atan2f=uninstrumented +fun:atan2l=uninstrumented +fun:atanf=uninstrumented +fun:atanh=uninstrumented +fun:atanhf=uninstrumented +fun:atanhl=uninstrumented +fun:atanl=uninstrumented +fun:atexit=uninstrumented +fun:atof=uninstrumented +fun:atoi=uninstrumented +fun:atol=uninstrumented +fun:atoll=uninstrumented +fun:authdes_create=uninstrumented +fun:authdes_getucred=uninstrumented +fun:authdes_pk_create=uninstrumented +fun:authnone_create=uninstrumented +fun:authunix_create=uninstrumented +fun:authunix_create_default=uninstrumented +fun:backtrace=uninstrumented +fun:backtrace_symbols=uninstrumented +fun:backtrace_symbols_fd=uninstrumented +fun:basename=uninstrumented +fun:bcmp=uninstrumented +fun:bcopy=uninstrumented +fun:bdflush=uninstrumented +fun:bid128_ext_fma=uninstrumented +fun:bind=uninstrumented +fun:bind_textdomain_codeset=uninstrumented +fun:bindresvport=uninstrumented +fun:bindtextdomain=uninstrumented +fun:brk=uninstrumented +fun:bsd_signal=uninstrumented +fun:bsearch=uninstrumented +fun:btowc=uninstrumented +fun:bzero=uninstrumented +fun:cabs=uninstrumented +fun:cabsf=uninstrumented +fun:cabsl=uninstrumented +fun:cacos=uninstrumented +fun:cacosf=uninstrumented +fun:cacosh=uninstrumented +fun:cacoshf=uninstrumented +fun:cacoshl=uninstrumented +fun:cacosl=uninstrumented +fun:calloc=uninstrumented +fun:callrpc=uninstrumented +fun:canonicalize_file_name=uninstrumented +fun:capget=uninstrumented +fun:capset=uninstrumented +fun:carg=uninstrumented +fun:cargf=uninstrumented +fun:cargl=uninstrumented +fun:casin=uninstrumented +fun:casinf=uninstrumented +fun:casinh=uninstrumented +fun:casinhf=uninstrumented +fun:casinhl=uninstrumented +fun:casinl=uninstrumented +fun:catan=uninstrumented +fun:catanf=uninstrumented +fun:catanh=uninstrumented +fun:catanhf=uninstrumented +fun:catanhl=uninstrumented +fun:catanl=uninstrumented +fun:catclose=uninstrumented +fun:catgets=uninstrumented +fun:catopen=uninstrumented +fun:cbc_crypt=uninstrumented +fun:cbrt=uninstrumented +fun:cbrtf=uninstrumented +fun:cbrtl=uninstrumented +fun:ccos=uninstrumented +fun:ccosf=uninstrumented +fun:ccosh=uninstrumented +fun:ccoshf=uninstrumented +fun:ccoshl=uninstrumented +fun:ccosl=uninstrumented +fun:ceil=uninstrumented +fun:ceilf=uninstrumented +fun:ceill=uninstrumented +fun:cexp=uninstrumented +fun:cexpf=uninstrumented +fun:cexpl=uninstrumented +fun:cfgetispeed=uninstrumented +fun:cfgetospeed=uninstrumented +fun:cfmakeraw=uninstrumented +fun:cfree=uninstrumented +fun:cfsetispeed=uninstrumented +fun:cfsetospeed=uninstrumented +fun:cfsetspeed=uninstrumented +fun:chdir=uninstrumented +fun:check_add_mapping=uninstrumented +fun:chflags=uninstrumented +fun:chmod=uninstrumented +fun:chown=uninstrumented +fun:chroot=uninstrumented +fun:cimag=uninstrumented +fun:cimagf=uninstrumented +fun:cimagl=uninstrumented +fun:cleanup=uninstrumented +fun:clear_once_control=uninstrumented +fun:clearenv=uninstrumented +fun:clearerr=uninstrumented +fun:clearerr_unlocked=uninstrumented +fun:clnt_broadcast=uninstrumented +fun:clnt_create=uninstrumented +fun:clnt_pcreateerror=uninstrumented +fun:clnt_perrno=uninstrumented +fun:clnt_perror=uninstrumented +fun:clnt_spcreateerror=uninstrumented +fun:clnt_sperrno=uninstrumented +fun:clnt_sperror=uninstrumented +fun:clntraw_create=uninstrumented +fun:clnttcp_create=uninstrumented +fun:clntudp_bufcreate=uninstrumented +fun:clntudp_create=uninstrumented +fun:clntunix_create=uninstrumented +fun:clock=uninstrumented +fun:clock_adjtime=uninstrumented +fun:clock_getcpuclockid=uninstrumented +fun:clock_getres=uninstrumented +fun:clock_gettime=uninstrumented +fun:clock_nanosleep=uninstrumented +fun:clock_settime=uninstrumented +fun:clog=uninstrumented +fun:clog10=uninstrumented +fun:clog10f=uninstrumented +fun:clog10l=uninstrumented +fun:clogf=uninstrumented +fun:clogl=uninstrumented +fun:clone=uninstrumented +fun:close=uninstrumented +fun:closedir=uninstrumented +fun:closelog=uninstrumented +fun:confstr=uninstrumented +fun:conj=uninstrumented +fun:conjf=uninstrumented +fun:conjl=uninstrumented +fun:connect=uninstrumented +fun:copysign=uninstrumented +fun:copysignf=uninstrumented +fun:copysignl=uninstrumented +fun:cos=uninstrumented +fun:cosf=uninstrumented +fun:cosh=uninstrumented +fun:coshf=uninstrumented +fun:coshl=uninstrumented +fun:cosl=uninstrumented +fun:cpow=uninstrumented +fun:cpowf=uninstrumented +fun:cpowl=uninstrumented +fun:cproj=uninstrumented +fun:cprojf=uninstrumented +fun:cprojl=uninstrumented +fun:creal=uninstrumented +fun:crealf=uninstrumented +fun:creall=uninstrumented +fun:creat=uninstrumented +fun:creat64=uninstrumented +fun:create_key=uninstrumented +fun:create_module=uninstrumented +fun:crypt=uninstrumented +fun:crypt_r=uninstrumented +fun:csin=uninstrumented +fun:csinf=uninstrumented +fun:csinh=uninstrumented +fun:csinhf=uninstrumented +fun:csinhl=uninstrumented +fun:csinl=uninstrumented +fun:csqrt=uninstrumented +fun:csqrtf=uninstrumented +fun:csqrtl=uninstrumented +fun:ctan=uninstrumented +fun:ctanf=uninstrumented +fun:ctanh=uninstrumented +fun:ctanhf=uninstrumented +fun:ctanhl=uninstrumented +fun:ctanl=uninstrumented +fun:ctermid=uninstrumented +fun:ctime=uninstrumented +fun:ctime_r=uninstrumented +fun:cuserid=uninstrumented +fun:daemon=uninstrumented +fun:dcgettext=uninstrumented +fun:dcngettext=uninstrumented +fun:delete_module=uninstrumented +fun:des_setparity=uninstrumented +fun:dgettext=uninstrumented +fun:difftime=uninstrumented +fun:dirfd=uninstrumented +fun:dirname=uninstrumented +fun:div=uninstrumented +fun:dl_iterate_phdr=uninstrumented +fun:dladdr=uninstrumented +fun:dladdr1=uninstrumented +fun:dlclose=uninstrumented +fun:dlerror=uninstrumented +fun:dlinfo=uninstrumented +fun:dlmopen=uninstrumented +fun:dlopen=uninstrumented +fun:dlsym=uninstrumented +fun:dlvsym=uninstrumented +fun:dngettext=uninstrumented +fun:do_clone.constprop.4=uninstrumented +fun:do_sigwait=uninstrumented +fun:dprintf=uninstrumented +fun:drand48=uninstrumented +fun:drand48_r=uninstrumented +fun:drem=uninstrumented +fun:dremf=uninstrumented +fun:dreml=uninstrumented +fun:dup=uninstrumented +fun:dup2=uninstrumented +fun:dup3=uninstrumented +fun:duplocale=uninstrumented +fun:dysize=uninstrumented +fun:eaccess=uninstrumented +fun:ecb_crypt=uninstrumented +fun:ecvt=uninstrumented +fun:ecvt_r=uninstrumented +fun:encrypt=uninstrumented +fun:encrypt_r=uninstrumented +fun:endaliasent=uninstrumented +fun:endfsent=uninstrumented +fun:endgrent=uninstrumented +fun:endhostent=uninstrumented +fun:endmntent=uninstrumented +fun:endnetent=uninstrumented +fun:endnetgrent=uninstrumented +fun:endprotoent=uninstrumented +fun:endpwent=uninstrumented +fun:endrpcent=uninstrumented +fun:endservent=uninstrumented +fun:endsgent=uninstrumented +fun:endspent=uninstrumented +fun:endttyent=uninstrumented +fun:endusershell=uninstrumented +fun:endutent=uninstrumented +fun:endutxent=uninstrumented +fun:envz_add=uninstrumented +fun:envz_entry=uninstrumented +fun:envz_get=uninstrumented +fun:envz_merge=uninstrumented +fun:envz_remove=uninstrumented +fun:envz_strip=uninstrumented +fun:epoll_create=uninstrumented +fun:epoll_create1=uninstrumented +fun:epoll_ctl=uninstrumented +fun:epoll_pwait=uninstrumented +fun:epoll_wait=uninstrumented +fun:erand48=uninstrumented +fun:erand48_r=uninstrumented +fun:erf=uninstrumented +fun:erfc=uninstrumented +fun:erfcf=uninstrumented +fun:erfcl=uninstrumented +fun:erff=uninstrumented +fun:erfl=uninstrumented +fun:err=uninstrumented +fun:error=uninstrumented +fun:error_at_line=uninstrumented +fun:errx=uninstrumented +fun:ether_aton=uninstrumented +fun:ether_aton_r=uninstrumented +fun:ether_hostton=uninstrumented +fun:ether_line=uninstrumented +fun:ether_ntoa=uninstrumented +fun:ether_ntoa_r=uninstrumented +fun:ether_ntohost=uninstrumented +fun:euidaccess=uninstrumented +fun:eventfd=uninstrumented +fun:eventfd_read=uninstrumented +fun:eventfd_write=uninstrumented +fun:execl=uninstrumented +fun:execle=uninstrumented +fun:execlp=uninstrumented +fun:execv=uninstrumented +fun:execve=uninstrumented +fun:execvp=uninstrumented +fun:execvpe=uninstrumented +fun:exit=uninstrumented +fun:exp=uninstrumented +fun:exp10=uninstrumented +fun:exp10f=uninstrumented +fun:exp10l=uninstrumented +fun:exp2=uninstrumented +fun:exp2f=uninstrumented +fun:exp2l=uninstrumented +fun:expf=uninstrumented +fun:expl=uninstrumented +fun:expm1=uninstrumented +fun:expm1f=uninstrumented +fun:expm1l=uninstrumented +fun:fabs=uninstrumented +fun:fabsf=uninstrumented +fun:fabsl=uninstrumented +fun:faccessat=uninstrumented +fun:fallocate=uninstrumented +fun:fallocate64=uninstrumented +fun:fanotify_init=uninstrumented +fun:fanotify_mark=uninstrumented +fun:fattach=uninstrumented +fun:fchdir=uninstrumented +fun:fchflags=uninstrumented +fun:fchmod=uninstrumented +fun:fchmodat=uninstrumented +fun:fchown=uninstrumented +fun:fchownat=uninstrumented +fun:fclose=uninstrumented +fun:fcloseall=uninstrumented +fun:fcntl=uninstrumented +fun:fcrypt=uninstrumented +fun:fcvt=uninstrumented +fun:fcvt_r=uninstrumented +fun:fdatasync=uninstrumented +fun:fdetach=uninstrumented +fun:fdim=uninstrumented +fun:fdimf=uninstrumented +fun:fdiml=uninstrumented +fun:fdopen=uninstrumented +fun:fdopendir=uninstrumented +fun:feclearexcept=uninstrumented +fun:fedisableexcept=uninstrumented +fun:feenableexcept=uninstrumented +fun:fegetenv=uninstrumented +fun:fegetexcept=uninstrumented +fun:fegetexceptflag=uninstrumented +fun:fegetround=uninstrumented +fun:feholdexcept=uninstrumented +fun:feof=uninstrumented +fun:feof_unlocked=uninstrumented +fun:feraiseexcept=uninstrumented +fun:ferror=uninstrumented +fun:ferror_unlocked=uninstrumented +fun:fesetenv=uninstrumented +fun:fesetexceptflag=uninstrumented +fun:fesetround=uninstrumented +fun:fetestexcept=uninstrumented +fun:feupdateenv=uninstrumented +fun:fexecve=uninstrumented +fun:fflush=uninstrumented +fun:fflush_unlocked=uninstrumented +fun:ffs=uninstrumented +fun:ffsl=uninstrumented +fun:ffsll=uninstrumented +fun:fgetc=uninstrumented +fun:fgetc_unlocked=uninstrumented +fun:fgetgrent=uninstrumented +fun:fgetgrent_r=uninstrumented +fun:fgetpos=uninstrumented +fun:fgetpos64=uninstrumented +fun:fgetpwent=uninstrumented +fun:fgetpwent_r=uninstrumented +fun:fgets=uninstrumented +fun:fgets_unlocked=uninstrumented +fun:fgetsgent=uninstrumented +fun:fgetsgent_r=uninstrumented +fun:fgetspent=uninstrumented +fun:fgetspent_r=uninstrumented +fun:fgetwc=uninstrumented +fun:fgetwc_unlocked=uninstrumented +fun:fgetws=uninstrumented +fun:fgetws_unlocked=uninstrumented +fun:fgetxattr=uninstrumented +fun:fileno=uninstrumented +fun:fileno_unlocked=uninstrumented +fun:finite=uninstrumented +fun:finitef=uninstrumented +fun:finitel=uninstrumented +fun:flistxattr=uninstrumented +fun:flock=uninstrumented +fun:flockfile=uninstrumented +fun:floor=uninstrumented +fun:floorf=uninstrumented +fun:floorl=uninstrumented +fun:fma=uninstrumented +fun:fmaf=uninstrumented +fun:fmal=uninstrumented +fun:fmax=uninstrumented +fun:fmaxf=uninstrumented +fun:fmaxl=uninstrumented +fun:fmemopen=uninstrumented +fun:fmin=uninstrumented +fun:fminf=uninstrumented +fun:fminl=uninstrumented +fun:fmod=uninstrumented +fun:fmodf=uninstrumented +fun:fmodl=uninstrumented +fun:fmtmsg=uninstrumented +fun:fnmatch=uninstrumented +fun:fopen=uninstrumented +fun:fopen64=uninstrumented +fun:fopencookie=uninstrumented +fun:fork=uninstrumented +fun:forkpty=uninstrumented +fun:fpathconf=uninstrumented +fun:fprintf=uninstrumented +fun:fputc=uninstrumented +fun:fputc_unlocked=uninstrumented +fun:fputs=uninstrumented +fun:fputs_unlocked=uninstrumented +fun:fputwc=uninstrumented +fun:fputwc_unlocked=uninstrumented +fun:fputws=uninstrumented +fun:fputws_unlocked=uninstrumented +fun:frame_dummy=uninstrumented +fun:fread=uninstrumented +fun:fread_unlocked=uninstrumented +fun:free=uninstrumented +fun:free_dynamic_blocks=uninstrumented +fun:free_segments=uninstrumented +fun:freeaddrinfo=uninstrumented +fun:freeifaddrs=uninstrumented +fun:freelocale=uninstrumented +fun:fremovexattr=uninstrumented +fun:freopen=uninstrumented +fun:freopen64=uninstrumented +fun:frexp=uninstrumented +fun:frexpf=uninstrumented +fun:frexpl=uninstrumented +fun:fscanf=uninstrumented +fun:fseek=uninstrumented +fun:fseeko=uninstrumented +fun:fseeko64=uninstrumented +fun:fsetpos=uninstrumented +fun:fsetpos64=uninstrumented +fun:fsetxattr=uninstrumented +fun:fstat=uninstrumented +fun:fstat64=uninstrumented +fun:fstatat=uninstrumented +fun:fstatat64=uninstrumented +fun:fstatfs=uninstrumented +fun:fstatfs64=uninstrumented +fun:fstatvfs=uninstrumented +fun:fstatvfs64=uninstrumented +fun:fsync=uninstrumented +fun:ftell=uninstrumented +fun:ftello=uninstrumented +fun:ftello64=uninstrumented +fun:ftime=uninstrumented +fun:ftok=uninstrumented +fun:ftruncate=uninstrumented +fun:ftruncate64=uninstrumented +fun:ftrylockfile=uninstrumented +fun:fts_children=uninstrumented +fun:fts_close=uninstrumented +fun:fts_open=uninstrumented +fun:fts_read=uninstrumented +fun:fts_set=uninstrumented +fun:ftw=uninstrumented +fun:ftw64=uninstrumented +fun:funlockfile=uninstrumented +fun:futimens=uninstrumented +fun:futimes=uninstrumented +fun:futimesat=uninstrumented +fun:fwide=uninstrumented +fun:fwprintf=uninstrumented +fun:fwrite=uninstrumented +fun:fwrite_unlocked=uninstrumented +fun:fwscanf=uninstrumented +fun:gai_cancel=uninstrumented +fun:gai_error=uninstrumented +fun:gai_strerror=uninstrumented +fun:gai_suspend=uninstrumented +fun:gamma=uninstrumented +fun:gammaf=uninstrumented +fun:gammal=uninstrumented +fun:gcvt=uninstrumented +fun:get_BID128.constprop.5=uninstrumented +fun:get_avphys_pages=uninstrumented +fun:get_current_dir_name=uninstrumented +fun:get_kernel_syms=uninstrumented +fun:get_myaddress=uninstrumented +fun:get_nprocs=uninstrumented +fun:get_nprocs_conf=uninstrumented +fun:get_phys_pages=uninstrumented +fun:getaddrinfo=uninstrumented +fun:getaddrinfo_a=uninstrumented +fun:getaliasbyname=uninstrumented +fun:getaliasbyname_r=uninstrumented +fun:getaliasent=uninstrumented +fun:getaliasent_r=uninstrumented +fun:getc=uninstrumented +fun:getc_unlocked=uninstrumented +fun:getchar=uninstrumented +fun:getchar_unlocked=uninstrumented +fun:getcontext=uninstrumented +fun:getcwd=uninstrumented +fun:getdate=uninstrumented +fun:getdate_r=uninstrumented +fun:getdelim=uninstrumented +fun:getdirentries=uninstrumented +fun:getdirentries64=uninstrumented +fun:getdomainname=uninstrumented +fun:getdtablesize=uninstrumented +fun:getegid=uninstrumented +fun:getenv=uninstrumented +fun:geteuid=uninstrumented +fun:getfsent=uninstrumented +fun:getfsfile=uninstrumented +fun:getfsspec=uninstrumented +fun:getgid=uninstrumented +fun:getgrent=uninstrumented +fun:getgrent_r=uninstrumented +fun:getgrgid=uninstrumented +fun:getgrgid_r=uninstrumented +fun:getgrnam=uninstrumented +fun:getgrnam_r=uninstrumented +fun:getgrouplist=uninstrumented +fun:getgroups=uninstrumented +fun:gethostbyaddr=uninstrumented +fun:gethostbyaddr_r=uninstrumented +fun:gethostbyname=uninstrumented +fun:gethostbyname2=uninstrumented +fun:gethostbyname2_r=uninstrumented +fun:gethostbyname_r=uninstrumented +fun:gethostent=uninstrumented +fun:gethostent_r=uninstrumented +fun:gethostid=uninstrumented +fun:gethostname=uninstrumented +fun:getifaddrs=uninstrumented +fun:getipv4sourcefilter=uninstrumented +fun:getitimer=uninstrumented +fun:getline=uninstrumented +fun:getloadavg=uninstrumented +fun:getlogin=uninstrumented +fun:getlogin_r=uninstrumented +fun:getmntent=uninstrumented +fun:getmntent_r=uninstrumented +fun:getmsg=uninstrumented +fun:getnameinfo=uninstrumented +fun:getnetbyaddr=uninstrumented +fun:getnetbyaddr_r=uninstrumented +fun:getnetbyname=uninstrumented +fun:getnetbyname_r=uninstrumented +fun:getnetent=uninstrumented +fun:getnetent_r=uninstrumented +fun:getnetgrent=uninstrumented +fun:getnetgrent_r=uninstrumented +fun:getnetname=uninstrumented +fun:getopt=uninstrumented +fun:getopt_long=uninstrumented +fun:getopt_long_only=uninstrumented +fun:getpagesize=uninstrumented +fun:getpass=uninstrumented +fun:getpeername=uninstrumented +fun:getpgid=uninstrumented +fun:getpgrp=uninstrumented +fun:getpid=uninstrumented +fun:getpmsg=uninstrumented +fun:getppid=uninstrumented +fun:getpriority=uninstrumented +fun:getprotobyname=uninstrumented +fun:getprotobyname_r=uninstrumented +fun:getprotobynumber=uninstrumented +fun:getprotobynumber_r=uninstrumented +fun:getprotoent=uninstrumented +fun:getprotoent_r=uninstrumented +fun:getpt=uninstrumented +fun:getpublickey=uninstrumented +fun:getpw=uninstrumented +fun:getpwent=uninstrumented +fun:getpwent_r=uninstrumented +fun:getpwnam=uninstrumented +fun:getpwnam_r=uninstrumented +fun:getpwuid=uninstrumented +fun:getpwuid_r=uninstrumented +fun:getresgid=uninstrumented +fun:getresuid=uninstrumented +fun:getrlimit=uninstrumented +fun:getrlimit64=uninstrumented +fun:getrpcbyname=uninstrumented +fun:getrpcbyname_r=uninstrumented +fun:getrpcbynumber=uninstrumented +fun:getrpcbynumber_r=uninstrumented +fun:getrpcent=uninstrumented +fun:getrpcent_r=uninstrumented +fun:getrpcport=uninstrumented +fun:getrusage=uninstrumented +fun:gets=uninstrumented +fun:getsecretkey=uninstrumented +fun:getservbyname=uninstrumented +fun:getservbyname_r=uninstrumented +fun:getservbyport=uninstrumented +fun:getservbyport_r=uninstrumented +fun:getservent=uninstrumented +fun:getservent_r=uninstrumented +fun:getsgent=uninstrumented +fun:getsgent_r=uninstrumented +fun:getsgnam=uninstrumented +fun:getsgnam_r=uninstrumented +fun:getsid=uninstrumented +fun:getsockname=uninstrumented +fun:getsockopt=uninstrumented +fun:getsourcefilter=uninstrumented +fun:getspent=uninstrumented +fun:getspent_r=uninstrumented +fun:getspnam=uninstrumented +fun:getspnam_r=uninstrumented +fun:getsubopt=uninstrumented +fun:gettext=uninstrumented +fun:gettimeofday=uninstrumented +fun:getttyent=uninstrumented +fun:getttynam=uninstrumented +fun:getuid=uninstrumented +fun:getusershell=uninstrumented +fun:getutent=uninstrumented +fun:getutent_r=uninstrumented +fun:getutid=uninstrumented +fun:getutid_r=uninstrumented +fun:getutline=uninstrumented +fun:getutline_r=uninstrumented +fun:getutmp=uninstrumented +fun:getutmpx=uninstrumented +fun:getutxent=uninstrumented +fun:getutxid=uninstrumented +fun:getutxline=uninstrumented +fun:getw=uninstrumented +fun:getwc=uninstrumented +fun:getwc_unlocked=uninstrumented +fun:getwchar=uninstrumented +fun:getwchar_unlocked=uninstrumented +fun:getwd=uninstrumented +fun:getxattr=uninstrumented +fun:glob=uninstrumented +fun:glob64=uninstrumented +fun:glob_pattern_p=uninstrumented +fun:globfree=uninstrumented +fun:globfree64=uninstrumented +fun:gmtime=uninstrumented +fun:gmtime_r=uninstrumented +fun:gnu_dev_major=uninstrumented +fun:gnu_dev_makedev=uninstrumented +fun:gnu_dev_minor=uninstrumented +fun:gnu_get_libc_release=uninstrumented +fun:gnu_get_libc_version=uninstrumented +fun:grantpt=uninstrumented +fun:group_member=uninstrumented +fun:gsignal=uninstrumented +fun:gtty=uninstrumented +fun:hasmntopt=uninstrumented +fun:hcreate=uninstrumented +fun:hcreate_r=uninstrumented +fun:hdestroy=uninstrumented +fun:hdestroy_r=uninstrumented +fun:herror=uninstrumented +fun:host2netname=uninstrumented +fun:hsearch=uninstrumented +fun:hsearch_r=uninstrumented +fun:hstrerror=uninstrumented +fun:htonl=uninstrumented +fun:htons=uninstrumented +fun:hypot=uninstrumented +fun:hypotf=uninstrumented +fun:hypotl=uninstrumented +fun:iconv=uninstrumented +fun:iconv_close=uninstrumented +fun:iconv_open=uninstrumented +fun:idna_to_ascii_lz=uninstrumented +fun:idna_to_unicode_lzlz=uninstrumented +fun:if_freenameindex=uninstrumented +fun:if_indextoname=uninstrumented +fun:if_nameindex=uninstrumented +fun:if_nametoindex=uninstrumented +fun:ilogb=uninstrumented +fun:ilogbf=uninstrumented +fun:ilogbl=uninstrumented +fun:imaxabs=uninstrumented +fun:imaxdiv=uninstrumented +fun:index=uninstrumented +fun:inet6_opt_append=uninstrumented +fun:inet6_opt_find=uninstrumented +fun:inet6_opt_finish=uninstrumented +fun:inet6_opt_get_val=uninstrumented +fun:inet6_opt_init=uninstrumented +fun:inet6_opt_next=uninstrumented +fun:inet6_opt_set_val=uninstrumented +fun:inet6_option_alloc=uninstrumented +fun:inet6_option_append=uninstrumented +fun:inet6_option_find=uninstrumented +fun:inet6_option_init=uninstrumented +fun:inet6_option_next=uninstrumented +fun:inet6_option_space=uninstrumented +fun:inet6_rth_add=uninstrumented +fun:inet6_rth_getaddr=uninstrumented +fun:inet6_rth_init=uninstrumented +fun:inet6_rth_reverse=uninstrumented +fun:inet6_rth_segments=uninstrumented +fun:inet6_rth_space=uninstrumented +fun:inet_addr=uninstrumented +fun:inet_aton=uninstrumented +fun:inet_lnaof=uninstrumented +fun:inet_makeaddr=uninstrumented +fun:inet_net_ntop=uninstrumented +fun:inet_net_pton=uninstrumented +fun:inet_neta=uninstrumented +fun:inet_netof=uninstrumented +fun:inet_network=uninstrumented +fun:inet_nsap_addr=uninstrumented +fun:inet_nsap_ntoa=uninstrumented +fun:inet_ntoa=uninstrumented +fun:inet_ntop=uninstrumented +fun:inet_pton=uninstrumented +fun:init_module=uninstrumented +fun:initgroups=uninstrumented +fun:initstate=uninstrumented +fun:initstate_r=uninstrumented +fun:innetgr=uninstrumented +fun:inotify_add_watch=uninstrumented +fun:inotify_init=uninstrumented +fun:inotify_init1=uninstrumented +fun:inotify_rm_watch=uninstrumented +fun:insque=uninstrumented +fun:ioctl=uninstrumented +fun:ioperm=uninstrumented +fun:iopl=uninstrumented +fun:iruserok=uninstrumented +fun:iruserok_af=uninstrumented +fun:isalnum=uninstrumented +fun:isalnum_l=uninstrumented +fun:isalpha=uninstrumented +fun:isalpha_l=uninstrumented +fun:isascii=uninstrumented +fun:isastream=uninstrumented +fun:isatty=uninstrumented +fun:isblank=uninstrumented +fun:isblank_l=uninstrumented +fun:iscntrl=uninstrumented +fun:iscntrl_l=uninstrumented +fun:isctype=uninstrumented +fun:isdigit=uninstrumented +fun:isdigit_l=uninstrumented +fun:isfdtype=uninstrumented +fun:isgraph=uninstrumented +fun:isgraph_l=uninstrumented +fun:isinf=uninstrumented +fun:isinfd128=uninstrumented +fun:isinfd32=uninstrumented +fun:isinfd64=uninstrumented +fun:isinff=uninstrumented +fun:isinfl=uninstrumented +fun:islower=uninstrumented +fun:islower_l=uninstrumented +fun:isnan=uninstrumented +fun:isnanf=uninstrumented +fun:isnanl=uninstrumented +fun:isprint=uninstrumented +fun:isprint_l=uninstrumented +fun:ispunct=uninstrumented +fun:ispunct_l=uninstrumented +fun:isspace=uninstrumented +fun:isspace_l=uninstrumented +fun:isupper=uninstrumented +fun:isupper_l=uninstrumented +fun:iswalnum=uninstrumented +fun:iswalnum_l=uninstrumented +fun:iswalpha=uninstrumented +fun:iswalpha_l=uninstrumented +fun:iswblank=uninstrumented +fun:iswblank_l=uninstrumented +fun:iswcntrl=uninstrumented +fun:iswcntrl_l=uninstrumented +fun:iswctype=uninstrumented +fun:iswctype_l=uninstrumented +fun:iswdigit=uninstrumented +fun:iswdigit_l=uninstrumented +fun:iswgraph=uninstrumented +fun:iswgraph_l=uninstrumented +fun:iswlower=uninstrumented +fun:iswlower_l=uninstrumented +fun:iswprint=uninstrumented +fun:iswprint_l=uninstrumented +fun:iswpunct=uninstrumented +fun:iswpunct_l=uninstrumented +fun:iswspace=uninstrumented +fun:iswspace_l=uninstrumented +fun:iswupper=uninstrumented +fun:iswupper_l=uninstrumented +fun:iswxdigit=uninstrumented +fun:iswxdigit_l=uninstrumented +fun:isxdigit=uninstrumented +fun:isxdigit_l=uninstrumented +fun:j0=uninstrumented +fun:j0f=uninstrumented +fun:j0l=uninstrumented +fun:j1=uninstrumented +fun:j1f=uninstrumented +fun:j1l=uninstrumented +fun:jn=uninstrumented +fun:jnf=uninstrumented +fun:jnl=uninstrumented +fun:jrand48=uninstrumented +fun:jrand48_r=uninstrumented +fun:key_decryptsession=uninstrumented +fun:key_decryptsession_pk=uninstrumented +fun:key_encryptsession=uninstrumented +fun:key_encryptsession_pk=uninstrumented +fun:key_gendes=uninstrumented +fun:key_get_conv=uninstrumented +fun:key_secretkey_is_set=uninstrumented +fun:key_setnet=uninstrumented +fun:key_setsecret=uninstrumented +fun:kill=uninstrumented +fun:killpg=uninstrumented +fun:klogctl=uninstrumented +fun:l64a=uninstrumented +fun:labs=uninstrumented +fun:lchmod=uninstrumented +fun:lchown=uninstrumented +fun:lckpwdf=uninstrumented +fun:lcong48=uninstrumented +fun:lcong48_r=uninstrumented +fun:ldexp=uninstrumented +fun:ldexpf=uninstrumented +fun:ldexpl=uninstrumented +fun:ldiv=uninstrumented +fun:lfind=uninstrumented +fun:lgamma=uninstrumented +fun:lgamma_r=uninstrumented +fun:lgammaf=uninstrumented +fun:lgammaf_r=uninstrumented +fun:lgammal=uninstrumented +fun:lgammal_r=uninstrumented +fun:lgetxattr=uninstrumented +fun:link=uninstrumented +fun:linkat=uninstrumented +fun:lio_listio=uninstrumented +fun:lio_listio64=uninstrumented +fun:listen=uninstrumented +fun:listxattr=uninstrumented +fun:llabs=uninstrumented +fun:lldiv=uninstrumented +fun:llistxattr=uninstrumented +fun:llrint=uninstrumented +fun:llrintf=uninstrumented +fun:llrintl=uninstrumented +fun:llround=uninstrumented +fun:llroundf=uninstrumented +fun:llroundl=uninstrumented +fun:llseek=uninstrumented +fun:localeconv=uninstrumented +fun:localtime=uninstrumented +fun:localtime_r=uninstrumented +fun:lockf=uninstrumented +fun:lockf64=uninstrumented +fun:log=uninstrumented +fun:log10=uninstrumented +fun:log10f=uninstrumented +fun:log10l=uninstrumented +fun:log1p=uninstrumented +fun:log1pf=uninstrumented +fun:log1pl=uninstrumented +fun:log2=uninstrumented +fun:log2f=uninstrumented +fun:log2l=uninstrumented +fun:logb=uninstrumented +fun:logbf=uninstrumented +fun:logbl=uninstrumented +fun:logf=uninstrumented +fun:login=uninstrumented +fun:login_tty=uninstrumented +fun:logl=uninstrumented +fun:logout=uninstrumented +fun:logwtmp=uninstrumented +fun:longjmp=uninstrumented +fun:lrand48=uninstrumented +fun:lrand48_r=uninstrumented +fun:lremovexattr=uninstrumented +fun:lrint=uninstrumented +fun:lrintf=uninstrumented +fun:lrintl=uninstrumented +fun:lround=uninstrumented +fun:lroundf=uninstrumented +fun:lroundl=uninstrumented +fun:lsearch=uninstrumented +fun:lseek=uninstrumented +fun:lseek64=uninstrumented +fun:lsetxattr=uninstrumented +fun:lstat=uninstrumented +fun:lstat64=uninstrumented +fun:lutimes=uninstrumented +fun:madvise=uninstrumented +fun:makecontext=uninstrumented +fun:mallinfo=uninstrumented +fun:malloc=uninstrumented +fun:malloc_get_state=uninstrumented +fun:malloc_info=uninstrumented +fun:malloc_set_state=uninstrumented +fun:malloc_stats=uninstrumented +fun:malloc_trim=uninstrumented +fun:malloc_usable_size=uninstrumented +fun:mallopt=uninstrumented +fun:matherr=uninstrumented +fun:mblen=uninstrumented +fun:mbrlen=uninstrumented +fun:mbrtowc=uninstrumented +fun:mbsinit=uninstrumented +fun:mbsnrtowcs=uninstrumented +fun:mbsrtowcs=uninstrumented +fun:mbstowcs=uninstrumented +fun:mbtowc=uninstrumented +fun:mcheck=uninstrumented +fun:mcheck_check_all=uninstrumented +fun:mcheck_pedantic=uninstrumented +fun:mcount=uninstrumented +fun:memalign=uninstrumented +fun:memccpy=uninstrumented +fun:memchr=uninstrumented +fun:memcmp=uninstrumented +fun:memcpy=uninstrumented +fun:memfrob=uninstrumented +fun:memmem=uninstrumented +fun:memmove=uninstrumented +fun:mempcpy=uninstrumented +fun:memrchr=uninstrumented +fun:memset=uninstrumented +fun:mincore=uninstrumented +fun:mkdir=uninstrumented +fun:mkdirat=uninstrumented +fun:mkdtemp=uninstrumented +fun:mkfifo=uninstrumented +fun:mkfifoat=uninstrumented +fun:mknod=uninstrumented +fun:mknodat=uninstrumented +fun:mkostemp=uninstrumented +fun:mkostemp64=uninstrumented +fun:mkostemps=uninstrumented +fun:mkostemps64=uninstrumented +fun:mkstemp=uninstrumented +fun:mkstemp64=uninstrumented +fun:mkstemps=uninstrumented +fun:mkstemps64=uninstrumented +fun:mktemp=uninstrumented +fun:mktime=uninstrumented +fun:mlock=uninstrumented +fun:mlockall=uninstrumented +fun:mmap=uninstrumented +fun:mmap64=uninstrumented +fun:modf=uninstrumented +fun:modff=uninstrumented +fun:modfl=uninstrumented +fun:modify_ldt=uninstrumented +fun:moncontrol=uninstrumented +fun:monstartup=uninstrumented +fun:mount=uninstrumented +fun:mprobe=uninstrumented +fun:mprotect=uninstrumented +fun:mq_close=uninstrumented +fun:mq_getattr=uninstrumented +fun:mq_notify=uninstrumented +fun:mq_open=uninstrumented +fun:mq_receive=uninstrumented +fun:mq_send=uninstrumented +fun:mq_setattr=uninstrumented +fun:mq_timedreceive=uninstrumented +fun:mq_timedsend=uninstrumented +fun:mq_unlink=uninstrumented +fun:mrand48=uninstrumented +fun:mrand48_r=uninstrumented +fun:mremap=uninstrumented +fun:msgctl=uninstrumented +fun:msgget=uninstrumented +fun:msgrcv=uninstrumented +fun:msgsnd=uninstrumented +fun:msync=uninstrumented +fun:mtrace=uninstrumented +fun:munlock=uninstrumented +fun:munlockall=uninstrumented +fun:munmap=uninstrumented +fun:muntrace=uninstrumented +fun:name_to_handle_at=uninstrumented +fun:nan=uninstrumented +fun:nanf=uninstrumented +fun:nanl=uninstrumented +fun:nanosleep=uninstrumented +fun:nearbyint=uninstrumented +fun:nearbyintf=uninstrumented +fun:nearbyintl=uninstrumented +fun:netname2host=uninstrumented +fun:netname2user=uninstrumented +fun:newlocale=uninstrumented +fun:nextafter=uninstrumented +fun:nextafterf=uninstrumented +fun:nextafterl=uninstrumented +fun:nexttoward=uninstrumented +fun:nexttowardf=uninstrumented +fun:nexttowardl=uninstrumented +fun:nfsservctl=uninstrumented +fun:nftw=uninstrumented +fun:nftw64=uninstrumented +fun:ngettext=uninstrumented +fun:nice=uninstrumented +fun:nis_add=uninstrumented +fun:nis_add_entry=uninstrumented +fun:nis_addmember=uninstrumented +fun:nis_checkpoint=uninstrumented +fun:nis_clone_directory=uninstrumented +fun:nis_clone_object=uninstrumented +fun:nis_clone_result=uninstrumented +fun:nis_creategroup=uninstrumented +fun:nis_destroy_object=uninstrumented +fun:nis_destroygroup=uninstrumented +fun:nis_dir_cmp=uninstrumented +fun:nis_domain_of=uninstrumented +fun:nis_domain_of_r=uninstrumented +fun:nis_first_entry=uninstrumented +fun:nis_free_directory=uninstrumented +fun:nis_free_object=uninstrumented +fun:nis_free_request=uninstrumented +fun:nis_freenames=uninstrumented +fun:nis_freeresult=uninstrumented +fun:nis_freeservlist=uninstrumented +fun:nis_freetags=uninstrumented +fun:nis_getnames=uninstrumented +fun:nis_getservlist=uninstrumented +fun:nis_ismember=uninstrumented +fun:nis_leaf_of=uninstrumented +fun:nis_leaf_of_r=uninstrumented +fun:nis_lerror=uninstrumented +fun:nis_list=uninstrumented +fun:nis_local_directory=uninstrumented +fun:nis_local_group=uninstrumented +fun:nis_local_host=uninstrumented +fun:nis_local_principal=uninstrumented +fun:nis_lookup=uninstrumented +fun:nis_mkdir=uninstrumented +fun:nis_modify=uninstrumented +fun:nis_modify_entry=uninstrumented +fun:nis_name_of=uninstrumented +fun:nis_name_of_r=uninstrumented +fun:nis_next_entry=uninstrumented +fun:nis_perror=uninstrumented +fun:nis_ping=uninstrumented +fun:nis_print_directory=uninstrumented +fun:nis_print_entry=uninstrumented +fun:nis_print_group=uninstrumented +fun:nis_print_group_entry=uninstrumented +fun:nis_print_link=uninstrumented +fun:nis_print_object=uninstrumented +fun:nis_print_result=uninstrumented +fun:nis_print_rights=uninstrumented +fun:nis_print_table=uninstrumented +fun:nis_read_obj=uninstrumented +fun:nis_remove=uninstrumented +fun:nis_remove_entry=uninstrumented +fun:nis_removemember=uninstrumented +fun:nis_rmdir=uninstrumented +fun:nis_servstate=uninstrumented +fun:nis_sperrno=uninstrumented +fun:nis_sperror=uninstrumented +fun:nis_sperror_r=uninstrumented +fun:nis_stats=uninstrumented +fun:nis_verifygroup=uninstrumented +fun:nis_write_obj=uninstrumented +fun:nl_langinfo=uninstrumented +fun:nl_langinfo_l=uninstrumented +fun:nop=uninstrumented +fun:nptl_freeres=uninstrumented +fun:nr_digits256=uninstrumented +fun:nrand48=uninstrumented +fun:nrand48_r=uninstrumented +fun:ns_datetosecs=uninstrumented +fun:ns_format_ttl=uninstrumented +fun:ns_get16=uninstrumented +fun:ns_get32=uninstrumented +fun:ns_initparse=uninstrumented +fun:ns_makecanon=uninstrumented +fun:ns_msg_getflag=uninstrumented +fun:ns_name_compress=uninstrumented +fun:ns_name_ntol=uninstrumented +fun:ns_name_ntop=uninstrumented +fun:ns_name_pack=uninstrumented +fun:ns_name_pton=uninstrumented +fun:ns_name_rollback=uninstrumented +fun:ns_name_skip=uninstrumented +fun:ns_name_uncompress=uninstrumented +fun:ns_name_unpack=uninstrumented +fun:ns_parse_ttl=uninstrumented +fun:ns_parserr=uninstrumented +fun:ns_put16=uninstrumented +fun:ns_put32=uninstrumented +fun:ns_samedomain=uninstrumented +fun:ns_samename=uninstrumented +fun:ns_skiprr=uninstrumented +fun:ns_sprintrr=uninstrumented +fun:ns_sprintrrf=uninstrumented +fun:ns_subdomain=uninstrumented +fun:ntohl=uninstrumented +fun:ntohs=uninstrumented +fun:ntp_adjtime=uninstrumented +fun:ntp_gettime=uninstrumented +fun:ntp_gettimex=uninstrumented +fun:obstack_free=uninstrumented +fun:obstack_printf=uninstrumented +fun:obstack_vprintf=uninstrumented +fun:on_exit=uninstrumented +fun:open=uninstrumented +fun:open64=uninstrumented +fun:open_by_handle_at=uninstrumented +fun:open_memstream=uninstrumented +fun:open_wmemstream=uninstrumented +fun:openat=uninstrumented +fun:openat64=uninstrumented +fun:opendir=uninstrumented +fun:openlog=uninstrumented +fun:openpty=uninstrumented +fun:parse_printf_format=uninstrumented +fun:passwd2des=uninstrumented +fun:pathconf=uninstrumented +fun:pause=uninstrumented +fun:pclose=uninstrumented +fun:perror=uninstrumented +fun:personality=uninstrumented +fun:pipe=uninstrumented +fun:pipe2=uninstrumented +fun:pivot_root=uninstrumented +fun:pmap_getmaps=uninstrumented +fun:pmap_getport=uninstrumented +fun:pmap_rmtcall=uninstrumented +fun:pmap_set=uninstrumented +fun:pmap_unset=uninstrumented +fun:poll=uninstrumented +fun:popen=uninstrumented +fun:posix_fadvise=uninstrumented +fun:posix_fadvise64=uninstrumented +fun:posix_fallocate=uninstrumented +fun:posix_fallocate64=uninstrumented +fun:posix_madvise=uninstrumented +fun:posix_memalign=uninstrumented +fun:posix_openpt=uninstrumented +fun:posix_spawn=uninstrumented +fun:posix_spawn_file_actions_addclose=uninstrumented +fun:posix_spawn_file_actions_adddup2=uninstrumented +fun:posix_spawn_file_actions_addopen=uninstrumented +fun:posix_spawn_file_actions_destroy=uninstrumented +fun:posix_spawn_file_actions_init=uninstrumented +fun:posix_spawnattr_destroy=uninstrumented +fun:posix_spawnattr_getflags=uninstrumented +fun:posix_spawnattr_getpgroup=uninstrumented +fun:posix_spawnattr_getschedparam=uninstrumented +fun:posix_spawnattr_getschedpolicy=uninstrumented +fun:posix_spawnattr_getsigdefault=uninstrumented +fun:posix_spawnattr_getsigmask=uninstrumented +fun:posix_spawnattr_init=uninstrumented +fun:posix_spawnattr_setflags=uninstrumented +fun:posix_spawnattr_setpgroup=uninstrumented +fun:posix_spawnattr_setschedparam=uninstrumented +fun:posix_spawnattr_setschedpolicy=uninstrumented +fun:posix_spawnattr_setsigdefault=uninstrumented +fun:posix_spawnattr_setsigmask=uninstrumented +fun:posix_spawnp=uninstrumented +fun:pow=uninstrumented +fun:pow10=uninstrumented +fun:pow10f=uninstrumented +fun:pow10l=uninstrumented +fun:powf=uninstrumented +fun:powl=uninstrumented +fun:ppoll=uninstrumented +fun:prctl=uninstrumented +fun:pread=uninstrumented +fun:pread64=uninstrumented +fun:preadv=uninstrumented +fun:preadv64=uninstrumented +fun:printf=uninstrumented +fun:printf_size=uninstrumented +fun:printf_size_info=uninstrumented +fun:prlimit=uninstrumented +fun:prlimit64=uninstrumented +fun:process_vm_readv=uninstrumented +fun:process_vm_writev=uninstrumented +fun:profil=uninstrumented +fun:pselect=uninstrumented +fun:psiginfo=uninstrumented +fun:psignal=uninstrumented +fun:pthread_atfork=uninstrumented +fun:pthread_attr_destroy=uninstrumented +fun:pthread_attr_getaffinity_np=uninstrumented +fun:pthread_attr_getdetachstate=uninstrumented +fun:pthread_attr_getguardsize=uninstrumented +fun:pthread_attr_getinheritsched=uninstrumented +fun:pthread_attr_getschedparam=uninstrumented +fun:pthread_attr_getschedpolicy=uninstrumented +fun:pthread_attr_getscope=uninstrumented +fun:pthread_attr_getstack=uninstrumented +fun:pthread_attr_getstackaddr=uninstrumented +fun:pthread_attr_getstacksize=uninstrumented +fun:pthread_attr_init=uninstrumented +fun:pthread_attr_setaffinity_np=uninstrumented +fun:pthread_attr_setdetachstate=uninstrumented +fun:pthread_attr_setguardsize=uninstrumented +fun:pthread_attr_setinheritsched=uninstrumented +fun:pthread_attr_setschedparam=uninstrumented +fun:pthread_attr_setschedpolicy=uninstrumented +fun:pthread_attr_setscope=uninstrumented +fun:pthread_attr_setstack=uninstrumented +fun:pthread_attr_setstackaddr=uninstrumented +fun:pthread_attr_setstacksize=uninstrumented +fun:pthread_barrier_destroy=uninstrumented +fun:pthread_barrier_init=uninstrumented +fun:pthread_barrier_wait=uninstrumented +fun:pthread_barrierattr_destroy=uninstrumented +fun:pthread_barrierattr_getpshared=uninstrumented +fun:pthread_barrierattr_init=uninstrumented +fun:pthread_barrierattr_setpshared=uninstrumented +fun:pthread_cancel=uninstrumented +fun:pthread_cancel_init=uninstrumented +fun:pthread_cond_broadcast=uninstrumented +fun:pthread_cond_destroy=uninstrumented +fun:pthread_cond_init=uninstrumented +fun:pthread_cond_signal=uninstrumented +fun:pthread_cond_timedwait=uninstrumented +fun:pthread_cond_wait=uninstrumented +fun:pthread_condattr_destroy=uninstrumented +fun:pthread_condattr_getclock=uninstrumented +fun:pthread_condattr_getpshared=uninstrumented +fun:pthread_condattr_init=uninstrumented +fun:pthread_condattr_setclock=uninstrumented +fun:pthread_condattr_setpshared=uninstrumented +fun:pthread_create=uninstrumented +fun:pthread_detach=uninstrumented +fun:pthread_equal=uninstrumented +fun:pthread_exit=uninstrumented +fun:pthread_getaffinity_np=uninstrumented +fun:pthread_getattr_np=uninstrumented +fun:pthread_getconcurrency=uninstrumented +fun:pthread_getcpuclockid=uninstrumented +fun:pthread_getname_np=uninstrumented +fun:pthread_getschedparam=uninstrumented +fun:pthread_getspecific=uninstrumented +fun:pthread_join=uninstrumented +fun:pthread_key_create=uninstrumented +fun:pthread_key_delete=uninstrumented +fun:pthread_kill=uninstrumented +fun:pthread_kill_other_threads_np=uninstrumented +fun:pthread_mutex_consistent=uninstrumented +fun:pthread_mutex_consistent_np=uninstrumented +fun:pthread_mutex_destroy=uninstrumented +fun:pthread_mutex_getprioceiling=uninstrumented +fun:pthread_mutex_init=uninstrumented +fun:pthread_mutex_lock=uninstrumented +fun:pthread_mutex_setprioceiling=uninstrumented +fun:pthread_mutex_timedlock=uninstrumented +fun:pthread_mutex_trylock=uninstrumented +fun:pthread_mutex_unlock=uninstrumented +fun:pthread_mutexattr_destroy=uninstrumented +fun:pthread_mutexattr_getkind_np=uninstrumented +fun:pthread_mutexattr_getprioceiling=uninstrumented +fun:pthread_mutexattr_getprotocol=uninstrumented +fun:pthread_mutexattr_getpshared=uninstrumented +fun:pthread_mutexattr_getrobust=uninstrumented +fun:pthread_mutexattr_getrobust_np=uninstrumented +fun:pthread_mutexattr_gettype=uninstrumented +fun:pthread_mutexattr_init=uninstrumented +fun:pthread_mutexattr_setkind_np=uninstrumented +fun:pthread_mutexattr_setprioceiling=uninstrumented +fun:pthread_mutexattr_setprotocol=uninstrumented +fun:pthread_mutexattr_setpshared=uninstrumented +fun:pthread_mutexattr_setrobust=uninstrumented +fun:pthread_mutexattr_setrobust_np=uninstrumented +fun:pthread_mutexattr_settype=uninstrumented +fun:pthread_once=uninstrumented +fun:pthread_rwlock_destroy=uninstrumented +fun:pthread_rwlock_init=uninstrumented +fun:pthread_rwlock_rdlock=uninstrumented +fun:pthread_rwlock_timedrdlock=uninstrumented +fun:pthread_rwlock_timedwrlock=uninstrumented +fun:pthread_rwlock_tryrdlock=uninstrumented +fun:pthread_rwlock_trywrlock=uninstrumented +fun:pthread_rwlock_unlock=uninstrumented +fun:pthread_rwlock_wrlock=uninstrumented +fun:pthread_rwlockattr_destroy=uninstrumented +fun:pthread_rwlockattr_getkind_np=uninstrumented +fun:pthread_rwlockattr_getpshared=uninstrumented +fun:pthread_rwlockattr_init=uninstrumented +fun:pthread_rwlockattr_setkind_np=uninstrumented +fun:pthread_rwlockattr_setpshared=uninstrumented +fun:pthread_self=uninstrumented +fun:pthread_setaffinity_np=uninstrumented +fun:pthread_setcancelstate=uninstrumented +fun:pthread_setcanceltype=uninstrumented +fun:pthread_setconcurrency=uninstrumented +fun:pthread_setname_np=uninstrumented +fun:pthread_setschedparam=uninstrumented +fun:pthread_setschedprio=uninstrumented +fun:pthread_setspecific=uninstrumented +fun:pthread_sigmask=uninstrumented +fun:pthread_sigqueue=uninstrumented +fun:pthread_spin_destroy=uninstrumented +fun:pthread_spin_init=uninstrumented +fun:pthread_spin_lock=uninstrumented +fun:pthread_spin_trylock=uninstrumented +fun:pthread_spin_unlock=uninstrumented +fun:pthread_testcancel=uninstrumented +fun:pthread_timedjoin_np=uninstrumented +fun:pthread_tryjoin_np=uninstrumented +fun:pthread_yield=uninstrumented +fun:ptrace=uninstrumented +fun:ptsname=uninstrumented +fun:ptsname_r=uninstrumented +fun:putc=uninstrumented +fun:putc_unlocked=uninstrumented +fun:putchar=uninstrumented +fun:putchar_unlocked=uninstrumented +fun:putenv=uninstrumented +fun:putgrent=uninstrumented +fun:putmsg=uninstrumented +fun:putpmsg=uninstrumented +fun:putpwent=uninstrumented +fun:puts=uninstrumented +fun:putsgent=uninstrumented +fun:putspent=uninstrumented +fun:pututline=uninstrumented +fun:pututxline=uninstrumented +fun:putw=uninstrumented +fun:putwc=uninstrumented +fun:putwc_unlocked=uninstrumented +fun:putwchar=uninstrumented +fun:putwchar_unlocked=uninstrumented +fun:pvalloc=uninstrumented +fun:pwrite=uninstrumented +fun:pwrite64=uninstrumented +fun:pwritev=uninstrumented +fun:pwritev64=uninstrumented +fun:qecvt=uninstrumented +fun:qecvt_r=uninstrumented +fun:qfcvt=uninstrumented +fun:qfcvt_r=uninstrumented +fun:qgcvt=uninstrumented +fun:qsort=uninstrumented +fun:qsort_r=uninstrumented +fun:query_module=uninstrumented +fun:quick_exit=uninstrumented +fun:quotactl=uninstrumented +fun:raise=uninstrumented +fun:rand=uninstrumented +fun:rand_r=uninstrumented +fun:random=uninstrumented +fun:random_r=uninstrumented +fun:rawmemchr=uninstrumented +fun:rcmd=uninstrumented +fun:rcmd_af=uninstrumented +fun:re_comp=uninstrumented +fun:re_compile_fastmap=uninstrumented +fun:re_compile_pattern=uninstrumented +fun:re_exec=uninstrumented +fun:re_match=uninstrumented +fun:re_match_2=uninstrumented +fun:re_search=uninstrumented +fun:re_search_2=uninstrumented +fun:re_set_registers=uninstrumented +fun:re_set_syntax=uninstrumented +fun:read=uninstrumented +fun:readColdStartFile=uninstrumented +fun:readahead=uninstrumented +fun:readdir=uninstrumented +fun:readdir64=uninstrumented +fun:readdir64_r=uninstrumented +fun:readdir_r=uninstrumented +fun:readlink=uninstrumented +fun:readlinkat=uninstrumented +fun:readv=uninstrumented +fun:realloc=uninstrumented +fun:realpath=uninstrumented +fun:reboot=uninstrumented +fun:recv=uninstrumented +fun:recvfrom=uninstrumented +fun:recvmmsg=uninstrumented +fun:recvmsg=uninstrumented +fun:regcomp=uninstrumented +fun:regerror=uninstrumented +fun:regexec=uninstrumented +fun:regfree=uninstrumented +fun:register_printf_function=uninstrumented +fun:register_printf_modifier=uninstrumented +fun:register_printf_specifier=uninstrumented +fun:register_printf_type=uninstrumented +fun:registerrpc=uninstrumented +fun:remainder=uninstrumented +fun:remainderf=uninstrumented +fun:remainderl=uninstrumented +fun:remap_file_pages=uninstrumented +fun:remove=uninstrumented +fun:removexattr=uninstrumented +fun:remque=uninstrumented +fun:remquo=uninstrumented +fun:remquof=uninstrumented +fun:remquol=uninstrumented +fun:rename=uninstrumented +fun:renameat=uninstrumented +fun:res_gethostbyaddr=uninstrumented +fun:res_gethostbyname=uninstrumented +fun:res_gethostbyname2=uninstrumented +fun:res_send_setqhook=uninstrumented +fun:res_send_setrhook=uninstrumented +fun:revoke=uninstrumented +fun:rewind=uninstrumented +fun:rewinddir=uninstrumented +fun:rexec=uninstrumented +fun:rexec_af=uninstrumented +fun:rindex=uninstrumented +fun:rint=uninstrumented +fun:rintf=uninstrumented +fun:rintl=uninstrumented +fun:rmdir=uninstrumented +fun:round=uninstrumented +fun:roundf=uninstrumented +fun:rounding_correction.constprop.1=uninstrumented +fun:roundl=uninstrumented +fun:rpmatch=uninstrumented +fun:rresvport=uninstrumented +fun:rresvport_af=uninstrumented +fun:rtime=uninstrumented +fun:ruserok=uninstrumented +fun:ruserok_af=uninstrumented +fun:ruserpass=uninstrumented +fun:sbrk=uninstrumented +fun:scalb=uninstrumented +fun:scalbf=uninstrumented +fun:scalbl=uninstrumented +fun:scalbln=uninstrumented +fun:scalblnf=uninstrumented +fun:scalblnl=uninstrumented +fun:scalbn=uninstrumented +fun:scalbnf=uninstrumented +fun:scalbnl=uninstrumented +fun:scandir=uninstrumented +fun:scandir64=uninstrumented +fun:scandirat=uninstrumented +fun:scandirat64=uninstrumented +fun:scanf=uninstrumented +fun:sched_get_priority_max=uninstrumented +fun:sched_get_priority_min=uninstrumented +fun:sched_getaffinity=uninstrumented +fun:sched_getcpu=uninstrumented +fun:sched_getparam=uninstrumented +fun:sched_getscheduler=uninstrumented +fun:sched_rr_get_interval=uninstrumented +fun:sched_setaffinity=uninstrumented +fun:sched_setparam=uninstrumented +fun:sched_setscheduler=uninstrumented +fun:sched_yield=uninstrumented +fun:seed48=uninstrumented +fun:seed48_r=uninstrumented +fun:seekdir=uninstrumented +fun:select=uninstrumented +fun:sem_close=uninstrumented +fun:sem_destroy=uninstrumented +fun:sem_getvalue=uninstrumented +fun:sem_init=uninstrumented +fun:sem_open=uninstrumented +fun:sem_post=uninstrumented +fun:sem_timedwait=uninstrumented +fun:sem_timedwait_cleanup=uninstrumented +fun:sem_timedwait_cleanup2=uninstrumented +fun:sem_trywait=uninstrumented +fun:sem_unlink=uninstrumented +fun:sem_wait=uninstrumented +fun:sem_wait_cleanup=uninstrumented +fun:semctl=uninstrumented +fun:semget=uninstrumented +fun:semop=uninstrumented +fun:semtimedop=uninstrumented +fun:send=uninstrumented +fun:sendfile=uninstrumented +fun:sendfile64=uninstrumented +fun:sendmmsg=uninstrumented +fun:sendmsg=uninstrumented +fun:sendto=uninstrumented +fun:setaliasent=uninstrumented +fun:setbuf=uninstrumented +fun:setbuffer=uninstrumented +fun:setcontext=uninstrumented +fun:setdomainname=uninstrumented +fun:setegid=uninstrumented +fun:setenv=uninstrumented +fun:seteuid=uninstrumented +fun:setfsent=uninstrumented +fun:setfsgid=uninstrumented +fun:setfsuid=uninstrumented +fun:setgid=uninstrumented +fun:setgrent=uninstrumented +fun:setgroups=uninstrumented +fun:sethostent=uninstrumented +fun:sethostid=uninstrumented +fun:sethostname=uninstrumented +fun:setipv4sourcefilter=uninstrumented +fun:setitimer=uninstrumented +fun:setjmp=uninstrumented +fun:setkey=uninstrumented +fun:setkey_r=uninstrumented +fun:setlinebuf=uninstrumented +fun:setlocale=uninstrumented +fun:setlogin=uninstrumented +fun:setlogmask=uninstrumented +fun:setmntent=uninstrumented +fun:setnetent=uninstrumented +fun:setnetgrent=uninstrumented +fun:setns=uninstrumented +fun:setpgid=uninstrumented +fun:setpgrp=uninstrumented +fun:setpriority=uninstrumented +fun:setprotoent=uninstrumented +fun:setpwent=uninstrumented +fun:setregid=uninstrumented +fun:setresgid=uninstrumented +fun:setresuid=uninstrumented +fun:setreuid=uninstrumented +fun:setrlimit=uninstrumented +fun:setrlimit64=uninstrumented +fun:setrpcent=uninstrumented +fun:setservent=uninstrumented +fun:setsgent=uninstrumented +fun:setsid=uninstrumented +fun:setsockopt=uninstrumented +fun:setsourcefilter=uninstrumented +fun:setspent=uninstrumented +fun:setstate=uninstrumented +fun:setstate_r=uninstrumented +fun:settimeofday=uninstrumented +fun:setttyent=uninstrumented +fun:setuid=uninstrumented +fun:setusershell=uninstrumented +fun:setutent=uninstrumented +fun:setutxent=uninstrumented +fun:setvbuf=uninstrumented +fun:setxattr=uninstrumented +fun:setxid_mark_thread.isra.1=uninstrumented +fun:sgetsgent=uninstrumented +fun:sgetsgent_r=uninstrumented +fun:sgetspent=uninstrumented +fun:sgetspent_r=uninstrumented +fun:shm_open=uninstrumented +fun:shm_unlink=uninstrumented +fun:shmat=uninstrumented +fun:shmctl=uninstrumented +fun:shmdt=uninstrumented +fun:shmget=uninstrumented +fun:shutdown=uninstrumented +fun:sigaction=uninstrumented +fun:sigaddset=uninstrumented +fun:sigaltstack=uninstrumented +fun:sigandset=uninstrumented +fun:sigblock=uninstrumented +fun:sigcancel_handler=uninstrumented +fun:sigdelset=uninstrumented +fun:sigemptyset=uninstrumented +fun:sigfillset=uninstrumented +fun:siggetmask=uninstrumented +fun:sighandler_setxid=uninstrumented +fun:sighold=uninstrumented +fun:sigignore=uninstrumented +fun:siginterrupt=uninstrumented +fun:sigisemptyset=uninstrumented +fun:sigismember=uninstrumented +fun:siglongjmp=uninstrumented +fun:signal=uninstrumented +fun:signalfd=uninstrumented +fun:significand=uninstrumented +fun:significandf=uninstrumented +fun:significandl=uninstrumented +fun:sigorset=uninstrumented +fun:sigpause=uninstrumented +fun:sigpending=uninstrumented +fun:sigprocmask=uninstrumented +fun:sigqueue=uninstrumented +fun:sigrelse=uninstrumented +fun:sigreturn=uninstrumented +fun:sigset=uninstrumented +fun:sigsetmask=uninstrumented +fun:sigstack=uninstrumented +fun:sigsuspend=uninstrumented +fun:sigtimedwait=uninstrumented +fun:sigvec=uninstrumented +fun:sigwait=uninstrumented +fun:sigwaitinfo=uninstrumented +fun:sin=uninstrumented +fun:sincos=uninstrumented +fun:sincosf=uninstrumented +fun:sincosl=uninstrumented +fun:sinf=uninstrumented +fun:sinh=uninstrumented +fun:sinhf=uninstrumented +fun:sinhl=uninstrumented +fun:sinl=uninstrumented +fun:sleep=uninstrumented +fun:snprintf=uninstrumented +fun:sockatmark=uninstrumented +fun:socket=uninstrumented +fun:socketpair=uninstrumented +fun:splice=uninstrumented +fun:sprintf=uninstrumented +fun:sprofil=uninstrumented +fun:sqrt=uninstrumented +fun:sqrtf=uninstrumented +fun:sqrtl=uninstrumented +fun:srand=uninstrumented +fun:srand48=uninstrumented +fun:srand48_r=uninstrumented +fun:srandom=uninstrumented +fun:srandom_r=uninstrumented +fun:sscanf=uninstrumented +fun:ssignal=uninstrumented +fun:sstk=uninstrumented +fun:stack_split_initialize_thread=uninstrumented +fun:start_thread=uninstrumented +fun:stat=uninstrumented +fun:stat64=uninstrumented +fun:statfs=uninstrumented +fun:statfs64=uninstrumented +fun:statvfs=uninstrumented +fun:statvfs64=uninstrumented +fun:step=uninstrumented +fun:stime=uninstrumented +fun:stpcpy=uninstrumented +fun:stpncpy=uninstrumented +fun:strcasecmp=uninstrumented +fun:strcasecmp_l=uninstrumented +fun:strcasestr=uninstrumented +fun:strcat=uninstrumented +fun:strchr=uninstrumented +fun:strchrnul=uninstrumented +fun:strcmp=uninstrumented +fun:strcoll=uninstrumented +fun:strcoll_l=uninstrumented +fun:strcpy=uninstrumented +fun:strcspn=uninstrumented +fun:strdup=uninstrumented +fun:strerror=uninstrumented +fun:strerror_l=uninstrumented +fun:strerror_r=uninstrumented +fun:strfmon=uninstrumented +fun:strfmon_l=uninstrumented +fun:strfry=uninstrumented +fun:strftime=uninstrumented +fun:strftime_l=uninstrumented +fun:strlen=uninstrumented +fun:strncasecmp=uninstrumented +fun:strncasecmp_l=uninstrumented +fun:strncat=uninstrumented +fun:strncmp=uninstrumented +fun:strncpy=uninstrumented +fun:strndup=uninstrumented +fun:strnlen=uninstrumented +fun:strpbrk=uninstrumented +fun:strptime=uninstrumented +fun:strptime_l=uninstrumented +fun:strrchr=uninstrumented +fun:strsep=uninstrumented +fun:strsignal=uninstrumented +fun:strspn=uninstrumented +fun:strstr=uninstrumented +fun:strtod=uninstrumented +fun:strtod_l=uninstrumented +fun:strtof=uninstrumented +fun:strtof_l=uninstrumented +fun:strtoimax=uninstrumented +fun:strtok=uninstrumented +fun:strtok_r=uninstrumented +fun:strtol=uninstrumented +fun:strtol_l=uninstrumented +fun:strtold=uninstrumented +fun:strtold_l=uninstrumented +fun:strtoll=uninstrumented +fun:strtoll_l=uninstrumented +fun:strtoq=uninstrumented +fun:strtoul=uninstrumented +fun:strtoul_l=uninstrumented +fun:strtoull=uninstrumented +fun:strtoull_l=uninstrumented +fun:strtoumax=uninstrumented +fun:strtouq=uninstrumented +fun:strverscmp=uninstrumented +fun:strxfrm=uninstrumented +fun:strxfrm_l=uninstrumented +fun:stty=uninstrumented +fun:sub256=uninstrumented +fun:svc_exit=uninstrumented +fun:svc_getreq=uninstrumented +fun:svc_getreq_common=uninstrumented +fun:svc_getreq_poll=uninstrumented +fun:svc_getreqset=uninstrumented +fun:svc_register=uninstrumented +fun:svc_run=uninstrumented +fun:svc_sendreply=uninstrumented +fun:svc_unregister=uninstrumented +fun:svcerr_auth=uninstrumented +fun:svcerr_decode=uninstrumented +fun:svcerr_noproc=uninstrumented +fun:svcerr_noprog=uninstrumented +fun:svcerr_progvers=uninstrumented +fun:svcerr_systemerr=uninstrumented +fun:svcerr_weakauth=uninstrumented +fun:svcfd_create=uninstrumented +fun:svcraw_create=uninstrumented +fun:svctcp_create=uninstrumented +fun:svcudp_bufcreate=uninstrumented +fun:svcudp_create=uninstrumented +fun:svcudp_enablecache=uninstrumented +fun:svcunix_create=uninstrumented +fun:svcunixfd_create=uninstrumented +fun:swab=uninstrumented +fun:swapcontext=uninstrumented +fun:swapoff=uninstrumented +fun:swapon=uninstrumented +fun:swprintf=uninstrumented +fun:swscanf=uninstrumented +fun:symlink=uninstrumented +fun:symlinkat=uninstrumented +fun:sync=uninstrumented +fun:sync_file_range=uninstrumented +fun:syncfs=uninstrumented +fun:syscall=uninstrumented +fun:sysconf=uninstrumented +fun:sysctl=uninstrumented +fun:sysinfo=uninstrumented +fun:syslog=uninstrumented +fun:system=uninstrumented +fun:sysv_signal=uninstrumented +fun:tan=uninstrumented +fun:tanf=uninstrumented +fun:tanh=uninstrumented +fun:tanhf=uninstrumented +fun:tanhl=uninstrumented +fun:tanl=uninstrumented +fun:tcdrain=uninstrumented +fun:tcflow=uninstrumented +fun:tcflush=uninstrumented +fun:tcgetattr=uninstrumented +fun:tcgetpgrp=uninstrumented +fun:tcgetsid=uninstrumented +fun:tcsendbreak=uninstrumented +fun:tcsetattr=uninstrumented +fun:tcsetpgrp=uninstrumented +fun:td_init=uninstrumented +fun:td_log=uninstrumented +fun:td_symbol_list=uninstrumented +fun:td_ta_clear_event=uninstrumented +fun:td_ta_delete=uninstrumented +fun:td_ta_enable_stats=uninstrumented +fun:td_ta_event_addr=uninstrumented +fun:td_ta_event_getmsg=uninstrumented +fun:td_ta_get_nthreads=uninstrumented +fun:td_ta_get_ph=uninstrumented +fun:td_ta_get_stats=uninstrumented +fun:td_ta_map_id2thr=uninstrumented +fun:td_ta_map_lwp2thr=uninstrumented +fun:td_ta_new=uninstrumented +fun:td_ta_reset_stats=uninstrumented +fun:td_ta_set_event=uninstrumented +fun:td_ta_setconcurrency=uninstrumented +fun:td_ta_thr_iter=uninstrumented +fun:td_ta_tsd_iter=uninstrumented +fun:td_thr_clear_event=uninstrumented +fun:td_thr_dbresume=uninstrumented +fun:td_thr_dbsuspend=uninstrumented +fun:td_thr_event_enable=uninstrumented +fun:td_thr_event_getmsg=uninstrumented +fun:td_thr_get_info=uninstrumented +fun:td_thr_getfpregs=uninstrumented +fun:td_thr_getgregs=uninstrumented +fun:td_thr_getxregs=uninstrumented +fun:td_thr_getxregsize=uninstrumented +fun:td_thr_set_event=uninstrumented +fun:td_thr_setfpregs=uninstrumented +fun:td_thr_setgregs=uninstrumented +fun:td_thr_setprio=uninstrumented +fun:td_thr_setsigpending=uninstrumented +fun:td_thr_setxregs=uninstrumented +fun:td_thr_sigsetmask=uninstrumented +fun:td_thr_tls_get_addr=uninstrumented +fun:td_thr_tlsbase=uninstrumented +fun:td_thr_tsd=uninstrumented +fun:td_thr_validate=uninstrumented +fun:tdelete=uninstrumented +fun:tdestroy=uninstrumented +fun:tee=uninstrumented +fun:telldir=uninstrumented +fun:tempnam=uninstrumented +fun:textdomain=uninstrumented +fun:tfind=uninstrumented +fun:tgamma=uninstrumented +fun:tgammaf=uninstrumented +fun:tgammal=uninstrumented +fun:time=uninstrumented +fun:timegm=uninstrumented +fun:timelocal=uninstrumented +fun:timer_create=uninstrumented +fun:timer_delete=uninstrumented +fun:timer_getoverrun=uninstrumented +fun:timer_gettime=uninstrumented +fun:timer_settime=uninstrumented +fun:timerfd_create=uninstrumented +fun:timerfd_gettime=uninstrumented +fun:timerfd_settime=uninstrumented +fun:times=uninstrumented +fun:tmpfile=uninstrumented +fun:tmpfile64=uninstrumented +fun:tmpnam=uninstrumented +fun:tmpnam_r=uninstrumented +fun:toascii=uninstrumented +fun:tolower=uninstrumented +fun:tolower_l=uninstrumented +fun:toupper=uninstrumented +fun:toupper_l=uninstrumented +fun:towctrans=uninstrumented +fun:towctrans_l=uninstrumented +fun:towlower=uninstrumented +fun:towlower_l=uninstrumented +fun:towupper=uninstrumented +fun:towupper_l=uninstrumented +fun:tr_break=uninstrumented +fun:trunc=uninstrumented +fun:truncate=uninstrumented +fun:truncate64=uninstrumented +fun:truncf=uninstrumented +fun:truncl=uninstrumented +fun:tsearch=uninstrumented +fun:ttyname=uninstrumented +fun:ttyname_r=uninstrumented +fun:ttyslot=uninstrumented +fun:twalk=uninstrumented +fun:tzset=uninstrumented +fun:ualarm=uninstrumented +fun:ulckpwdf=uninstrumented +fun:ulimit=uninstrumented +fun:umask=uninstrumented +fun:umount=uninstrumented +fun:umount2=uninstrumented +fun:uname=uninstrumented +fun:ungetc=uninstrumented +fun:ungetwc=uninstrumented +fun:unlink=uninstrumented +fun:unlinkat=uninstrumented +fun:unlockpt=uninstrumented +fun:unsetenv=uninstrumented +fun:unshare=uninstrumented +fun:unwind_cleanup=uninstrumented +fun:unwind_stop=uninstrumented +fun:updwtmp=uninstrumented +fun:updwtmpx=uninstrumented +fun:uselib=uninstrumented +fun:uselocale=uninstrumented +fun:user2netname=uninstrumented +fun:usleep=uninstrumented +fun:ustat=uninstrumented +fun:utime=uninstrumented +fun:utimensat=uninstrumented +fun:utimes=uninstrumented +fun:utmpname=uninstrumented +fun:utmpxname=uninstrumented +fun:valloc=uninstrumented +fun:vasprintf=uninstrumented +fun:vdprintf=uninstrumented +fun:verr=uninstrumented +fun:verrx=uninstrumented +fun:versionsort=uninstrumented +fun:versionsort64=uninstrumented +fun:vfork=uninstrumented +fun:vfprintf=uninstrumented +fun:vfscanf=uninstrumented +fun:vfwprintf=uninstrumented +fun:vfwscanf=uninstrumented +fun:vhangup=uninstrumented +fun:vlimit=uninstrumented +fun:vmsplice=uninstrumented +fun:vprintf=uninstrumented +fun:vscanf=uninstrumented +fun:vsnprintf=uninstrumented +fun:vsprintf=uninstrumented +fun:vsscanf=uninstrumented +fun:vswprintf=uninstrumented +fun:vswscanf=uninstrumented +fun:vsyslog=uninstrumented +fun:vtimes=uninstrumented +fun:vwarn=uninstrumented +fun:vwarnx=uninstrumented +fun:vwprintf=uninstrumented +fun:vwscanf=uninstrumented +fun:wait=uninstrumented +fun:wait3=uninstrumented +fun:wait4=uninstrumented +fun:waitid=uninstrumented +fun:waitpid=uninstrumented +fun:walker=uninstrumented +fun:warn=uninstrumented +fun:warnx=uninstrumented +fun:wcpcpy=uninstrumented +fun:wcpncpy=uninstrumented +fun:wcrtomb=uninstrumented +fun:wcscasecmp=uninstrumented +fun:wcscasecmp_l=uninstrumented +fun:wcscat=uninstrumented +fun:wcschr=uninstrumented +fun:wcschrnul=uninstrumented +fun:wcscmp=uninstrumented +fun:wcscoll=uninstrumented +fun:wcscoll_l=uninstrumented +fun:wcscpy=uninstrumented +fun:wcscspn=uninstrumented +fun:wcsdup=uninstrumented +fun:wcsftime=uninstrumented +fun:wcsftime_l=uninstrumented +fun:wcslen=uninstrumented +fun:wcsncasecmp=uninstrumented +fun:wcsncasecmp_l=uninstrumented +fun:wcsncat=uninstrumented +fun:wcsncmp=uninstrumented +fun:wcsncpy=uninstrumented +fun:wcsnlen=uninstrumented +fun:wcsnrtombs=uninstrumented +fun:wcspbrk=uninstrumented +fun:wcsrchr=uninstrumented +fun:wcsrtombs=uninstrumented +fun:wcsspn=uninstrumented +fun:wcsstr=uninstrumented +fun:wcstod=uninstrumented +fun:wcstod_l=uninstrumented +fun:wcstof=uninstrumented +fun:wcstof_l=uninstrumented +fun:wcstoimax=uninstrumented +fun:wcstok=uninstrumented +fun:wcstol=uninstrumented +fun:wcstol_l=uninstrumented +fun:wcstold=uninstrumented +fun:wcstold_l=uninstrumented +fun:wcstoll=uninstrumented +fun:wcstoll_l=uninstrumented +fun:wcstombs=uninstrumented +fun:wcstoq=uninstrumented +fun:wcstoul=uninstrumented +fun:wcstoul_l=uninstrumented +fun:wcstoull=uninstrumented +fun:wcstoull_l=uninstrumented +fun:wcstoumax=uninstrumented +fun:wcstouq=uninstrumented +fun:wcswcs=uninstrumented +fun:wcswidth=uninstrumented +fun:wcsxfrm=uninstrumented +fun:wcsxfrm_l=uninstrumented +fun:wctob=uninstrumented +fun:wctomb=uninstrumented +fun:wctrans=uninstrumented +fun:wctrans_l=uninstrumented +fun:wctype=uninstrumented +fun:wctype_l=uninstrumented +fun:wcwidth=uninstrumented +fun:wmemchr=uninstrumented +fun:wmemcmp=uninstrumented +fun:wmemcpy=uninstrumented +fun:wmemmove=uninstrumented +fun:wmempcpy=uninstrumented +fun:wmemset=uninstrumented +fun:wordexp=uninstrumented +fun:wordfree=uninstrumented +fun:wprintf=uninstrumented +fun:write=uninstrumented +fun:writeColdStartFile=uninstrumented +fun:writev=uninstrumented +fun:wscanf=uninstrumented +fun:xdecrypt=uninstrumented +fun:xdr_accepted_reply=uninstrumented +fun:xdr_array=uninstrumented +fun:xdr_authdes_cred=uninstrumented +fun:xdr_authdes_verf=uninstrumented +fun:xdr_authunix_parms=uninstrumented +fun:xdr_bool=uninstrumented +fun:xdr_bytes=uninstrumented +fun:xdr_callhdr=uninstrumented +fun:xdr_callmsg=uninstrumented +fun:xdr_cback_data=uninstrumented +fun:xdr_char=uninstrumented +fun:xdr_cryptkeyarg=uninstrumented +fun:xdr_cryptkeyarg2=uninstrumented +fun:xdr_cryptkeyres=uninstrumented +fun:xdr_des_block=uninstrumented +fun:xdr_domainname=uninstrumented +fun:xdr_double=uninstrumented +fun:xdr_enum=uninstrumented +fun:xdr_float=uninstrumented +fun:xdr_free=uninstrumented +fun:xdr_getcredres=uninstrumented +fun:xdr_hyper=uninstrumented +fun:xdr_int=uninstrumented +fun:xdr_int16_t=uninstrumented +fun:xdr_int32_t=uninstrumented +fun:xdr_int64_t=uninstrumented +fun:xdr_int8_t=uninstrumented +fun:xdr_key_netstarg=uninstrumented +fun:xdr_key_netstres=uninstrumented +fun:xdr_keybuf=uninstrumented +fun:xdr_keydat=uninstrumented +fun:xdr_keystatus=uninstrumented +fun:xdr_long=uninstrumented +fun:xdr_longlong_t=uninstrumented +fun:xdr_mapname=uninstrumented +fun:xdr_netnamestr=uninstrumented +fun:xdr_netobj=uninstrumented +fun:xdr_obj_p=uninstrumented +fun:xdr_opaque=uninstrumented +fun:xdr_opaque_auth=uninstrumented +fun:xdr_peername=uninstrumented +fun:xdr_pmap=uninstrumented +fun:xdr_pmaplist=uninstrumented +fun:xdr_pointer=uninstrumented +fun:xdr_quad_t=uninstrumented +fun:xdr_reference=uninstrumented +fun:xdr_rejected_reply=uninstrumented +fun:xdr_replymsg=uninstrumented +fun:xdr_rmtcall_args=uninstrumented +fun:xdr_rmtcallres=uninstrumented +fun:xdr_short=uninstrumented +fun:xdr_sizeof=uninstrumented +fun:xdr_string=uninstrumented +fun:xdr_u_char=uninstrumented +fun:xdr_u_hyper=uninstrumented +fun:xdr_u_int=uninstrumented +fun:xdr_u_long=uninstrumented +fun:xdr_u_longlong_t=uninstrumented +fun:xdr_u_quad_t=uninstrumented +fun:xdr_u_short=uninstrumented +fun:xdr_uint16_t=uninstrumented +fun:xdr_uint32_t=uninstrumented +fun:xdr_uint64_t=uninstrumented +fun:xdr_uint8_t=uninstrumented +fun:xdr_union=uninstrumented +fun:xdr_unixcred=uninstrumented +fun:xdr_valdat=uninstrumented +fun:xdr_vector=uninstrumented +fun:xdr_void=uninstrumented +fun:xdr_wrapstring=uninstrumented +fun:xdr_yp_buf=uninstrumented +fun:xdr_ypall=uninstrumented +fun:xdr_ypbind_binding=uninstrumented +fun:xdr_ypbind_resp=uninstrumented +fun:xdr_ypbind_resptype=uninstrumented +fun:xdr_ypbind_setdom=uninstrumented +fun:xdr_ypdelete_args=uninstrumented +fun:xdr_ypmap_parms=uninstrumented +fun:xdr_ypmaplist=uninstrumented +fun:xdr_yppush_status=uninstrumented +fun:xdr_yppushresp_xfr=uninstrumented +fun:xdr_ypreq_key=uninstrumented +fun:xdr_ypreq_nokey=uninstrumented +fun:xdr_ypreq_xfr=uninstrumented +fun:xdr_ypresp_all=uninstrumented +fun:xdr_ypresp_key_val=uninstrumented +fun:xdr_ypresp_maplist=uninstrumented +fun:xdr_ypresp_master=uninstrumented +fun:xdr_ypresp_order=uninstrumented +fun:xdr_ypresp_val=uninstrumented +fun:xdr_ypresp_xfr=uninstrumented +fun:xdr_ypstat=uninstrumented +fun:xdr_ypupdate_args=uninstrumented +fun:xdr_ypxfrstat=uninstrumented +fun:xdrmem_create=uninstrumented +fun:xdrrec_create=uninstrumented +fun:xdrrec_endofrecord=uninstrumented +fun:xdrrec_eof=uninstrumented +fun:xdrrec_skiprecord=uninstrumented +fun:xdrstdio_create=uninstrumented +fun:xencrypt=uninstrumented +fun:xprt_register=uninstrumented +fun:xprt_unregister=uninstrumented +fun:y0=uninstrumented +fun:y0f=uninstrumented +fun:y0l=uninstrumented +fun:y1=uninstrumented +fun:y1f=uninstrumented +fun:y1l=uninstrumented +fun:yn=uninstrumented +fun:ynf=uninstrumented +fun:ynl=uninstrumented +fun:yp_all=uninstrumented +fun:yp_bind=uninstrumented +fun:yp_first=uninstrumented +fun:yp_get_default_domain=uninstrumented +fun:yp_maplist=uninstrumented +fun:yp_master=uninstrumented +fun:yp_match=uninstrumented +fun:yp_next=uninstrumented +fun:yp_order=uninstrumented +fun:yp_unbind=uninstrumented +fun:yp_update=uninstrumented +fun:ypbinderr_string=uninstrumented +fun:yperr_string=uninstrumented +fun:ypprot_err=uninstrumented diff --git a/lib/dfsan/lit_tests/CMakeLists.txt b/lib/dfsan/lit_tests/CMakeLists.txt new file mode 100644 index 000000000000..d7c5c82ac3c0 --- /dev/null +++ b/lib/dfsan/lit_tests/CMakeLists.txt @@ -0,0 +1,21 @@ +set(DFSAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) +set(DFSAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + +if(COMPILER_RT_CAN_EXECUTE_TESTS) + # Run DFSan tests only if we're sure we may produce working binaries. + set(DFSAN_TEST_DEPS + ${SANITIZER_COMMON_LIT_TEST_DEPS} + ${DFSAN_RUNTIME_LIBRARIES} + dfsan_abilist) + set(DFSAN_TEST_PARAMS + dfsan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + add_lit_testsuite(check-dfsan "Running the DataFlowSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR} + PARAMS ${DFSAN_TEST_PARAMS} + DEPENDS ${DFSAN_TEST_DEPS}) + set_target_properties(check-dfsan PROPERTIES FOLDER "DFSan tests") +endif() diff --git a/lib/dfsan/lit_tests/Inputs/flags_abilist.txt b/lib/dfsan/lit_tests/Inputs/flags_abilist.txt new file mode 100644 index 000000000000..94b1fa298259 --- /dev/null +++ b/lib/dfsan/lit_tests/Inputs/flags_abilist.txt @@ -0,0 +1,10 @@ +fun:f=uninstrumented + +fun:main=uninstrumented +fun:main=discard + +fun:dfsan_create_label=uninstrumented +fun:dfsan_create_label=discard + +fun:dfsan_set_label=uninstrumented +fun:dfsan_set_label=discard diff --git a/lib/dfsan/lit_tests/basic.c b/lib/dfsan/lit_tests/basic.c new file mode 100644 index 000000000000..b566c9271d0b --- /dev/null +++ b/lib/dfsan/lit_tests/basic.c @@ -0,0 +1,21 @@ +// RUN: %clang_dfsan -m64 %s -o %t && %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %t + +// Tests that labels are propagated through loads and stores. + +#include <sanitizer/dfsan_interface.h> +#include <assert.h> + +int main(void) { + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + dfsan_label new_label = dfsan_get_label(i); + assert(i_label == new_label); + + dfsan_label read_label = dfsan_read_label(&i, sizeof(i)); + assert(i_label == read_label); + + return 0; +} diff --git a/lib/dfsan/lit_tests/custom.c b/lib/dfsan/lit_tests/custom.c new file mode 100644 index 000000000000..c9fa9356154d --- /dev/null +++ b/lib/dfsan/lit_tests/custom.c @@ -0,0 +1,154 @@ +// RUN: %clang_dfsan -m64 %s -o %t && %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %t + +// Tests custom implementations of various libc functions. + +#define _GNU_SOURCE +#include <sanitizer/dfsan_interface.h> +#include <assert.h> +#include <link.h> +#include <pthread.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +void *ptcb(void *p) { + assert(p == (void *)1); + assert(dfsan_get_label((uintptr_t)p) == 0); + return (void *)2; +} + +int dlcb(struct dl_phdr_info *info, size_t size, void *data) { + assert(data == (void *)3); + assert(dfsan_get_label((uintptr_t)info) == 0); + assert(dfsan_get_label(size) == 0); + assert(dfsan_get_label((uintptr_t)data) == 0); + return 0; +} + +int main(void) { + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + int j = 2; + dfsan_label j_label = dfsan_create_label("j", 0); + dfsan_set_label(j_label, &j, sizeof(j)); + + struct stat s; + s.st_dev = i; + int rv = stat("/", &s); + assert(rv == 0); + assert(dfsan_get_label(s.st_dev) == 0); + + s.st_dev = i; + rv = stat("/nonexistent", &s); + assert(rv == -1); + assert(dfsan_get_label(s.st_dev) == i_label); + + int fd = open("/dev/zero", O_RDONLY); + s.st_dev = i; + rv = fstat(fd, &s); + assert(rv == 0); + assert(dfsan_get_label(s.st_dev) == 0); + + char str1[] = "str1", str2[] = "str2"; + dfsan_set_label(i_label, &str1[3], 1); + dfsan_set_label(j_label, &str2[3], 1); + + rv = memcmp(str1, str2, sizeof(str1)); + assert(rv < 0); + assert(dfsan_get_label(rv) == dfsan_union(i_label, j_label)); + + char strc[sizeof(str1)]; + memcpy(strc, str1, sizeof(str1)); + assert(dfsan_get_label(strc[0]) == 0); + assert(dfsan_get_label(strc[3]) == i_label); + + memset(strc, j, sizeof(strc)); + assert(dfsan_get_label(strc[0]) == j_label); + assert(dfsan_get_label(strc[1]) == j_label); + assert(dfsan_get_label(strc[2]) == j_label); + assert(dfsan_get_label(strc[3]) == j_label); + assert(dfsan_get_label(strc[4]) == j_label); + + rv = strcmp(str1, str2); + assert(rv < 0); + assert(dfsan_get_label(rv) == dfsan_union(i_label, j_label)); + + char *strd = strdup(str1); + assert(dfsan_get_label(strd[0]) == 0); + assert(dfsan_get_label(strd[3]) == i_label); + free(strd); + + rv = strncmp(str1, str2, sizeof(str1)); + assert(rv < 0); + assert(dfsan_get_label(rv) == dfsan_union(i_label, j_label)); + + rv = strncmp(str1, str2, 3); + assert(rv == 0); + assert(dfsan_get_label(rv) == 0); + + str1[0] = 'S'; + + rv = strncasecmp(str1, str2, sizeof(str1)); + assert(rv < 0); + assert(dfsan_get_label(rv) == dfsan_union(i_label, j_label)); + + rv = strncasecmp(str1, str2, 3); + assert(rv == 0); + assert(dfsan_get_label(rv) == 0); + + char *crv = strchr(str1, 'r'); + assert(crv == &str1[2]); + assert(dfsan_get_label((uintptr_t)crv) == 0); + + crv = strchr(str1, '1'); + assert(crv == &str1[3]); + assert(dfsan_get_label((uintptr_t)crv) == i_label); + + crv = strchr(str1, 'x'); + assert(crv == 0); + assert(dfsan_get_label((uintptr_t)crv) == i_label); + + // With any luck this sequence of calls will cause calloc to return the same + // pointer both times. This is probably the best we can do to test this + // function. + crv = calloc(4096, 1); + assert(dfsan_get_label(crv[0]) == 0); + free(crv); + + crv = calloc(4096, 1); + assert(dfsan_get_label(crv[0]) == 0); + free(crv); + + char buf[16]; + buf[0] = i; + buf[15] = j; + rv = read(fd, buf, sizeof(buf)); + assert(rv == sizeof(buf)); + assert(dfsan_get_label(buf[0]) == 0); + assert(dfsan_get_label(buf[15]) == 0); + + close(fd); + fd = open("/bin/sh", O_RDONLY); + buf[0] = i; + buf[15] = j; + rv = pread(fd, buf, sizeof(buf), 0); + assert(rv == sizeof(buf)); + assert(dfsan_get_label(buf[0]) == 0); + assert(dfsan_get_label(buf[15]) == 0); + + pthread_t pt; + pthread_create(&pt, 0, ptcb, (void *)1); + void *cbrv; + pthread_join(pt, &cbrv); + assert(cbrv == (void *)2); + + dl_iterate_phdr(dlcb, (void *)3); + + return 0; +} diff --git a/lib/dfsan/lit_tests/flags.c b/lib/dfsan/lit_tests/flags.c new file mode 100644 index 000000000000..5cf970dce90d --- /dev/null +++ b/lib/dfsan/lit_tests/flags.c @@ -0,0 +1,24 @@ +// RUN: %clang_dfsan -m64 %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_dfsan -m64 %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && DFSAN_OPTIONS=warn_unimplemented=0 %t 2>&1 | count 0 +// RUN: %clang_dfsan -m64 %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && DFSAN_OPTIONS=warn_nonzero_labels=1 %t 2>&1 | FileCheck --check-prefix=CHECK-NONZERO %s + +// Tests that flags work correctly. + +#include <sanitizer/dfsan_interface.h> + +int f(int i) { + return i; +} + +int main(void) { + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + // CHECK: WARNING: DataFlowSanitizer: call to uninstrumented function f + // CHECK-NOT: WARNING: DataFlowSanitizer: saw nonzero label + // CHECK-NONZERO: WARNING: DataFlowSanitizer: saw nonzero label + f(i); + + return 0; +} diff --git a/lib/dfsan/lit_tests/fncall.c b/lib/dfsan/lit_tests/fncall.c new file mode 100644 index 000000000000..15b77bd67902 --- /dev/null +++ b/lib/dfsan/lit_tests/fncall.c @@ -0,0 +1,26 @@ +// RUN: %clang_dfsan -m64 %s -o %t && %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %t + +// Tests that labels are propagated through function calls. + +#include <sanitizer/dfsan_interface.h> +#include <assert.h> + +int f(int x) { + int j = 2; + dfsan_label j_label = dfsan_create_label("j", 0); + dfsan_set_label(j_label, &j, sizeof(j)); + return x + j; +} + +int main(void) { + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + dfsan_label ij_label = dfsan_get_label(f(i)); + assert(dfsan_has_label(ij_label, i_label)); + assert(dfsan_has_label_with_desc(ij_label, "j")); + + return 0; +} diff --git a/lib/dfsan/lit_tests/lit.cfg b/lib/dfsan/lit_tests/lit.cfg new file mode 100644 index 000000000000..19bc97690a86 --- /dev/null +++ b/lib/dfsan/lit_tests/lit.cfg @@ -0,0 +1,69 @@ +# -*- Python -*- + +import os + +import lit.util + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup config name. +config.name = 'DataFlowSanitizer' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +def DisplayNoConfigMessage(): + lit_config.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-dfsan") + +# Figure out LLVM source root. +llvm_src_root = getattr(config, 'llvm_src_root', None) +if llvm_src_root is None: + # We probably haven't loaded the site-specific configuration: the user + # is likely trying to run a test file directly, and the site configuration + # wasn't created by the build system. + dfsan_site_cfg = lit_config.params.get('dfsan_site_config', None) + if (dfsan_site_cfg) and (os.path.exists(dfsan_site_cfg)): + lit_config.load_config(config, dfsan_site_cfg) + raise SystemExit + + # Try to guess the location of site-specific configuration using llvm-config + # util that can point where the build tree is. + llvm_config = lit.util.which("llvm-config", config.environment["PATH"]) + if not llvm_config: + DisplayNoConfigMessage() + + # Find out the presumed location of generated site config. + llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip() + dfsan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt", + "lib", "dfsan", "lit_tests", "lit.site.cfg") + if (not dfsan_site_cfg) or (not os.path.exists(dfsan_site_cfg)): + DisplayNoConfigMessage() + + lit_config.load_config(config, dfsan_site_cfg) + raise SystemExit + +# Setup default compiler flags used with -fsanitize=dataflow option. +clang_dfsan_cflags = ["-fsanitize=dataflow"] +clang_dfsan_cxxflags = ["--driver-mode=g++ "] + clang_dfsan_cflags +config.substitutions.append( ("%clang_dfsan ", + " ".join([config.clang] + clang_dfsan_cflags) + + " ") ) +config.substitutions.append( ("%clangxx_dfsan ", + " ".join([config.clang] + clang_dfsan_cxxflags) + + " ") ) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# DataFlowSanitizer tests are currently supported on Linux only. +if config.host_os not in ['Linux']: + config.unsupported = True diff --git a/lib/dfsan/lit_tests/lit.site.cfg.in b/lib/dfsan/lit_tests/lit.site.cfg.in new file mode 100644 index 000000000000..0cf6d6b5580f --- /dev/null +++ b/lib/dfsan/lit_tests/lit.site.cfg.in @@ -0,0 +1,5 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@DFSAN_SOURCE_DIR@/lit_tests/lit.cfg") diff --git a/lib/dfsan/lit_tests/propagate.c b/lib/dfsan/lit_tests/propagate.c new file mode 100644 index 000000000000..86d182b8a7fc --- /dev/null +++ b/lib/dfsan/lit_tests/propagate.c @@ -0,0 +1,47 @@ +// RUN: %clang_dfsan -m64 %s -o %t && %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %t + +// Tests that labels are propagated through computation and that union labels +// are properly created. + +#include <sanitizer/dfsan_interface.h> +#include <assert.h> + +int main(void) { + assert(dfsan_union(0, 0) == 0); + + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + int j = 2; + dfsan_label j_label = dfsan_create_label("j", 0); + dfsan_set_label(j_label, &j, sizeof(j)); + + int k = 3; + dfsan_label k_label = dfsan_create_label("k", 0); + dfsan_set_label(k_label, &k, sizeof(k)); + + int k2 = 4; + dfsan_set_label(k_label, &k2, sizeof(k2)); + + dfsan_label ij_label = dfsan_get_label(i + j); + assert(dfsan_has_label(ij_label, i_label)); + assert(dfsan_has_label(ij_label, j_label)); + assert(!dfsan_has_label(ij_label, k_label)); + // Test uniquing. + assert(dfsan_union(i_label, j_label) == ij_label); + assert(dfsan_union(j_label, i_label) == ij_label); + + dfsan_label ijk_label = dfsan_get_label(i + j + k); + assert(dfsan_has_label(ijk_label, i_label)); + assert(dfsan_has_label(ijk_label, j_label)); + assert(dfsan_has_label(ijk_label, k_label)); + + assert(dfsan_get_label(k + k2) == k_label); + + struct { int i, j; } s = { i, j }; + assert(dfsan_read_label(&s, sizeof(s)) == ij_label); + + return 0; +} diff --git a/lib/dfsan/scripts/build-libc-list.py b/lib/dfsan/scripts/build-libc-list.py new file mode 100755 index 000000000000..8f6b8d513631 --- /dev/null +++ b/lib/dfsan/scripts/build-libc-list.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +#===- lib/dfsan/scripts/build-libc-list.py ---------------------------------===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# +# The purpose of this script is to identify every function symbol in a set of +# libraries (in this case, libc and libgcc) so that they can be marked as +# uninstrumented, thus allowing the instrumentation pass to treat calls to those +# functions correctly. + +import os +import subprocess +import sys +from optparse import OptionParser + +def defined_function_list(object): + functions = [] + readelf_proc = subprocess.Popen(['readelf', '-s', '-W', object], + stdout=subprocess.PIPE) + readelf = readelf_proc.communicate()[0].split('\n') + if readelf_proc.returncode != 0: + raise subprocess.CalledProcessError(readelf_proc.returncode, 'readelf') + for line in readelf: + if (line[31:35] == 'FUNC' or line[31:36] == 'IFUNC') and \ + line[55:58] != 'UND': + function_name = line[59:].split('@')[0] + functions.append(function_name) + return functions + +p = OptionParser() +p.add_option('--lib', metavar='PATH', + help='path to lib directory to use', + default='/lib/x86_64-linux-gnu') +p.add_option('--usrlib', metavar='PATH', + help='path to usr/lib directory to use', + default='/usr/lib/x86_64-linux-gnu') +p.add_option('--gcclib', metavar='PATH', + help='path to gcc lib directory to use', + default='/usr/lib/gcc/x86_64-linux-gnu/4.6') +p.add_option('--with-libstdcxx', action='store_true', + dest='with_libstdcxx', + help='include libstdc++ in the list (inadvisable)') +(options, args) = p.parse_args() + +libs = [os.path.join(options.lib, name) for name in + ['ld-linux-x86-64.so.2', + 'libanl.so.1', + 'libBrokenLocale.so.1', + 'libcidn.so.1', + 'libcrypt.so.1', + 'libc.so.6', + 'libdl.so.2', + 'libm.so.6', + 'libnsl.so.1', + 'libpthread.so.0', + 'libresolv.so.2', + 'librt.so.1', + 'libthread_db.so.1', + 'libutil.so.1']] +libs += [os.path.join(options.usrlib, name) for name in + ['libc_nonshared.a', + 'libpthread_nonshared.a']] +gcclibs = ['libgcc.a', + 'libgcc_s.so'] +if options.with_libstdcxx: + gcclibs += ['libstdc++.so'] +libs += [os.path.join(options.gcclib, name) for name in gcclibs] + +functions = [] +for l in libs: + if os.path.exists(l): + functions += defined_function_list(l) + else: + print >> sys.stderr, 'warning: library %s not found' % l + +functions = list(set(functions)) +functions.sort() + +for f in functions: + print 'fun:%s=uninstrumented' % f diff --git a/lib/eprintf.c b/lib/eprintf.c index b07d624b50f9..3626dbf8b0c4 100644 --- a/lib/eprintf.c +++ b/lib/eprintf.c @@ -22,7 +22,9 @@ * It should never be exported from a dylib, so it is marked * visibility hidden. */ +#ifndef _WIN32 __attribute__((visibility("hidden"))) +#endif void __eprintf(const char* format, const char* assertion_expression, const char* line, const char* file) { diff --git a/lib/int_endianness.h b/lib/int_endianness.h index edb58c810e21..a64f926478ca 100644 --- a/lib/int_endianness.h +++ b/lib/int_endianness.h @@ -19,13 +19,15 @@ #if defined(__SVR4) && defined(__sun) #include <sys/byteorder.h> -#if _BYTE_ORDER == _BIG_ENDIAN +#if defined(_BIG_ENDIAN) #define _YUGA_LITTLE_ENDIAN 0 #define _YUGA_BIG_ENDIAN 1 -#elif _BYTE_ORDER == _LITTLE_ENDIAN +#elif defined(_LITTLE_ENDIAN) #define _YUGA_LITTLE_ENDIAN 1 #define _YUGA_BIG_ENDIAN 0 -#endif /* _BYTE_ORDER */ +#else /* !_LITTLE_ENDIAN */ +#error "unknown endianness" +#endif /* !_LITTLE_ENDIAN */ #endif /* Solaris and AuroraUX. */ diff --git a/lib/int_util.c b/lib/int_util.c index 871d191658cb..323e46179e6c 100644 --- a/lib/int_util.c +++ b/lib/int_util.c @@ -24,31 +24,36 @@ #ifdef KERNEL_USE extern void panic(const char *, ...) __attribute__((noreturn)); +#ifndef _WIN32 __attribute__((visibility("hidden"))) +#endif void compilerrt_abort_impl(const char *file, int line, const char *function) { panic("%s:%d: abort in %s", file, line, function); } -#elif __APPLE__ && !__STATIC__ +#elif __APPLE__ /* from libSystem.dylib */ extern void __assert_rtn(const char *func, const char *file, int line, const char * message) __attribute__((noreturn)); +#ifndef _WIN32 __attribute__((weak)) __attribute__((visibility("hidden"))) +#endif void compilerrt_abort_impl(const char *file, int line, const char *function) { __assert_rtn(function, file, line, "libcompiler_rt abort"); } - #else /* Get the system definition of abort() */ #include <stdlib.h> +#ifndef _WIN32 __attribute__((weak)) __attribute__((visibility("hidden"))) +#endif void compilerrt_abort_impl(const char *file, int line, const char *function) { abort(); } diff --git a/lib/interception/CMakeLists.txt b/lib/interception/CMakeLists.txt index cd9e6e75504f..1dfdaffb6dc1 100644 --- a/lib/interception/CMakeLists.txt +++ b/lib/interception/CMakeLists.txt @@ -13,10 +13,12 @@ set(INTERCEPTION_CFLAGS ${SANITIZER_COMMON_CFLAGS}) if(APPLE) # Build universal binary on APPLE. - add_compiler_rt_osx_object_library(RTInterception - ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH} - SOURCES ${INTERCEPTION_SOURCES} - CFLAGS ${INTERCEPTION_CFLAGS}) + foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) + add_compiler_rt_darwin_object_library(RTInterception ${os} + ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH} + SOURCES ${INTERCEPTION_SOURCES} + CFLAGS ${INTERCEPTION_CFLAGS}) + endforeach() elseif(ANDROID) add_library(RTInterception.arm.android OBJECT ${INTERCEPTION_SOURCES}) set_target_compile_flags(RTInterception.arm.android diff --git a/lib/interception/interception.h b/lib/interception/interception.h index d50af35415d6..baddd6c132c9 100644 --- a/lib/interception/interception.h +++ b/lib/interception/interception.h @@ -130,7 +130,10 @@ const interpose_substitution substitution_##func_name[] \ # define WRAPPER_NAME(x) "wrap_"#x # define INTERCEPTOR_ATTRIBUTE # endif -# define DECLARE_WRAPPER(ret_type, func, ...) +# define DECLARE_WRAPPER(ret_type, func, ...) \ + extern "C" ret_type func(__VA_ARGS__); +# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \ + extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__); #else # define WRAP(x) __interceptor_ ## x # define WRAPPER_NAME(x) "__interceptor_" #x @@ -211,7 +214,7 @@ const interpose_substitution substitution_##func_name[] \ namespace __interception { \ FUNC_TYPE(func) PTR_TO_REAL(func); \ } \ - DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \ + DECLARE_WRAPPER_WINAPI(ret_type, func, __VA_ARGS__) \ extern "C" \ INTERCEPTOR_ATTRIBUTE \ ret_type __stdcall WRAP(func)(__VA_ARGS__) @@ -235,12 +238,18 @@ typedef unsigned long uptr; // NOLINT #if defined(__linux__) # include "interception_linux.h" # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX(func) +# define INTERCEPT_FUNCTION_VER(func, symver) \ + INTERCEPT_FUNCTION_VER_LINUX(func, symver) #elif defined(__APPLE__) # include "interception_mac.h" # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func) +# define INTERCEPT_FUNCTION_VER(func, symver) \ + INTERCEPT_FUNCTION_VER_MAC(func, symver) #else // defined(_WIN32) # include "interception_win.h" # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_WIN(func) +# define INTERCEPT_FUNCTION_VER(func, symver) \ + INTERCEPT_FUNCTION_VER_WIN(func, symver) #endif #undef INCLUDED_FROM_INTERCEPTION_LIB diff --git a/lib/interception/interception_linux.cc b/lib/interception/interception_linux.cc index 009098fbd657..53f42881016c 100644 --- a/lib/interception/interception_linux.cc +++ b/lib/interception/interception_linux.cc @@ -15,7 +15,6 @@ #ifdef __linux__ #include "interception.h" -#include <stddef.h> // for NULL #include <dlfcn.h> // for dlsym namespace __interception { @@ -24,6 +23,13 @@ bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, *func_addr = (uptr)dlsym(RTLD_NEXT, func_name); return real == wrapper; } + +#if !defined(__ANDROID__) // android does not have dlvsym +void *GetFuncAddrVer(const char *func_name, const char *ver) { + return dlvsym(RTLD_NEXT, func_name, ver); +} +#endif // !defined(__ANDROID__) + } // namespace __interception diff --git a/lib/interception/interception_linux.h b/lib/interception/interception_linux.h index dba60bf7315e..cea957320d05 100644 --- a/lib/interception/interception_linux.h +++ b/lib/interception/interception_linux.h @@ -25,6 +25,7 @@ namespace __interception { // returns true if a function with the given name was found. bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, uptr real, uptr wrapper); +void *GetFuncAddrVer(const char *func_name, const char *ver); } // namespace __interception #define INTERCEPT_FUNCTION_LINUX(func) \ @@ -33,5 +34,14 @@ bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, (::__interception::uptr)&(func), \ (::__interception::uptr)&WRAP(func)) +#if !defined(__ANDROID__) // android does not have dlvsym +# define INTERCEPT_FUNCTION_VER_LINUX(func, symver) \ + ::__interception::real_##func = (func##_f)(unsigned long) \ + ::__interception::GetFuncAddrVer(#func, symver) +#else +# define INTERCEPT_FUNCTION_VER_LINUX(func, symver) \ + INTERCEPT_FUNCTION_LINUX(func) +#endif // !defined(__ANDROID__) + #endif // INTERCEPTION_LINUX_H #endif // __linux__ diff --git a/lib/interception/interception_mac.h b/lib/interception/interception_mac.h index 5059489831ec..e5a35c6971cd 100644 --- a/lib/interception/interception_mac.h +++ b/lib/interception/interception_mac.h @@ -22,6 +22,7 @@ #define INTERCEPTION_MAC_H #define INTERCEPT_FUNCTION_MAC(func) +#define INTERCEPT_FUNCTION_VER_MAC(func, symver) #endif // INTERCEPTION_MAC_H #endif // __APPLE__ diff --git a/lib/interception/interception_win.h b/lib/interception/interception_win.h index c64af1baffe7..f2727c9241d3 100644 --- a/lib/interception/interception_win.h +++ b/lib/interception/interception_win.h @@ -41,5 +41,8 @@ bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func); (::__interception::uptr*)&REAL(func)) #endif +#define INTERCEPT_FUNCTION_VER_WIN(func, symver) \ + INTERCEPT_FUNCTION_WIN(func) + #endif // INTERCEPTION_WIN_H #endif // _WIN32 diff --git a/lib/lit.common.cfg b/lib/lit.common.cfg index b410259a9e71..6c2b4cca4546 100644 --- a/lib/lit.common.cfg +++ b/lib/lit.common.cfg @@ -6,17 +6,17 @@ import os import platform +import lit.formats + # Setup test format execute_external = (platform.system() != 'Windows' - or lit.getBashPath() not in [None, ""]) + or lit_config.getBashPath() not in [None, ""]) config.test_format = lit.formats.ShTest(execute_external) # Setup clang binary. clang_path = getattr(config, 'clang', None) if (not clang_path) or (not os.path.exists(clang_path)): - lit.fatal("Can't find Clang on path %r" % clang_path) -if not lit.quiet: - lit.note("using clang: %r" % clang_path) + lit_config.fatal("Can't find Clang on path %r" % clang_path) # Clear some environment variables that might affect Clang. possibly_dangerous_env_vars = ['COMPILER_PATH', 'RC_DEBUG_OPTIONS', @@ -38,14 +38,12 @@ for name in possibly_dangerous_env_vars: # Tweak PATH to include llvm tools dir. llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) if (not llvm_tools_dir) or (not os.path.exists(llvm_tools_dir)): - lit.fatal("Invalid llvm_tools_dir config attribute: %r" % llvm_tools_dir) + lit_config.fatal("Invalid llvm_tools_dir config attribute: %r" % llvm_tools_dir) path = os.path.pathsep.join((llvm_tools_dir, config.environment['PATH'])) config.environment['PATH'] = path -# Define %clang and %clangxx substitutions to use in test RUN lines. -config.substitutions.append( ("%clang ", (" " + config.clang + " ")) ) -config.substitutions.append( ("%clangxx ", (" " + config.clang + - " -ccc-cxx ")) ) +# Define path to external llvm-symbolizer tool. +config.llvm_symbolizer_path = os.path.join(llvm_tools_dir, "llvm-symbolizer") # Use ugly construction to explicitly prohibit "clang", "clang++" etc. # in RUN lines. @@ -58,3 +56,7 @@ compiler_rt_arch = getattr(config, 'compiler_rt_arch', None) if compiler_rt_arch: for arch in compiler_rt_arch.split(";"): config.available_features.add(arch + "-supported-target") + +compiler_rt_debug = getattr(config, 'compiler_rt_debug', False) +if not compiler_rt_debug: + config.available_features.add('compiler-rt-optimized') diff --git a/lib/asan/lit_tests/lit.site.cfg.in b/lib/lit.common.configured.in index 08546cdabe02..558655cbbee8 100644 --- a/lib/asan/lit_tests/lit.site.cfg.in +++ b/lib/lit.common.configured.in @@ -1,22 +1,27 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! +# Generic config options for all compiler-rt lit tests. config.target_triple = "@TARGET_TRIPLE@" +config.host_arch = "@HOST_ARCH@" config.host_os = "@HOST_OS@" +config.llvm_build_mode = "@LLVM_BUILD_MODE@" config.llvm_src_root = "@LLVM_SOURCE_DIR@" config.llvm_obj_root = "@LLVM_BINARY_DIR@" config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.clang = "@LLVM_BINARY_DIR@/bin/clang" config.compiler_rt_arch = "@COMPILER_RT_SUPPORTED_ARCH@" +config.python_executable = "@PYTHON_EXECUTABLE@" +config.compiler_rt_debug = @COMPILER_RT_DEBUG_PYBOOL@ # LLVM tools dir can be passed in lit parameters, so try to # apply substitution. try: - config.llvm_tools_dir = config.llvm_tools_dir % lit.params + config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params except KeyError,e: key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) + lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) -# Let the main config do the real work. -lit.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/lit.cfg") +# Setup attributes common for all compiler-rt projects. +lit_config.load_config(config, "@COMPILER_RT_SOURCE_DIR@/lib/lit.common.cfg") diff --git a/lib/lit.common.unit.cfg b/lib/lit.common.unit.cfg index ca00abb65e9f..2bd8f376f00e 100644 --- a/lib/lit.common.unit.cfg +++ b/lib/lit.common.unit.cfg @@ -6,6 +6,8 @@ import os +import lit.formats + # Setup test format llvm_build_mode = getattr(config, "llvm_build_mode", "Debug") config.test_format = lit.formats.GoogleTest(llvm_build_mode, "Test") @@ -13,6 +15,13 @@ config.test_format = lit.formats.GoogleTest(llvm_build_mode, "Test") # Setup test suffixes. config.suffixes = [] +# Tweak PATH to include llvm tools dir. +llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) +if (not llvm_tools_dir) or (not os.path.exists(llvm_tools_dir)): + lit_config.fatal("Invalid llvm_tools_dir config attribute: %r" % llvm_tools_dir) +path = os.path.pathsep.join((llvm_tools_dir, config.environment['PATH'])) +config.environment['PATH'] = path + # Propagate the temp directory. Windows requires this because it uses \Windows\ # if none of these are present. if 'TMP' in os.environ: diff --git a/lib/lit.common.unit.configured.in b/lib/lit.common.unit.configured.in new file mode 100644 index 000000000000..430816b24280 --- /dev/null +++ b/lib/lit.common.unit.configured.in @@ -0,0 +1,23 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Generic config options for all compiler-rt unit tests. +config.target_triple = "@TARGET_TRIPLE@" +config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.llvm_obj_root = "@LLVM_BINARY_DIR@" +config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" +config.llvm_build_mode = "@LLVM_BUILD_MODE@" +config.host_os = "@HOST_OS@" + +# LLVM tools dir and build mode can be passed in lit parameters, +# so try to apply substitution. +try: + config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params + config.llvm_build_mode = config.llvm_build_mode % lit_config.params +except KeyError,e: + key, = e.args + lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) + +# Setup attributes common for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_SOURCE_DIR@/lib/lit.common.unit.cfg") diff --git a/lib/lsan/CMakeLists.txt b/lib/lsan/CMakeLists.txt index 378f08199218..3018a06622ee 100644 --- a/lib/lsan/CMakeLists.txt +++ b/lib/lsan/CMakeLists.txt @@ -1,24 +1,26 @@ include_directories(..) set(LSAN_CFLAGS - ${SANITIZER_COMMON_CFLAGS}) + ${SANITIZER_COMMON_CFLAGS} + -fno-rtti) set(LSAN_COMMON_SOURCES lsan_common.cc lsan_common_linux.cc) set(LSAN_SOURCES - lsan_interceptors.cc + lsan.cc lsan_allocator.cc - lsan_thread.cc - lsan.cc) + lsan_interceptors.cc + lsan_preinit.cc + lsan_thread.cc) set(LSAN_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) # The common files need to build on every arch supported by ASan. # (Even if they build into dummy object files.) filter_available_targets(LSAN_COMMON_SUPPORTED_ARCH - x86_64 i386 powerpc64 powerpc) + x86_64 i386 powerpc64) # Architectures supported by the standalone LSan. filter_available_targets(LSAN_SUPPORTED_ARCH @@ -26,7 +28,14 @@ filter_available_targets(LSAN_SUPPORTED_ARCH set(LSAN_RUNTIME_LIBRARIES) -if (NOT APPLE AND NOT ANDROID) +if(APPLE) + foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) + add_compiler_rt_darwin_object_library(RTLSanCommon ${os} + ARCH ${LSAN_COMMON_SUPPORTED_ARCH} + SOURCES ${LSAN_COMMON_SOURCES} + CFLAGS ${LSAN_CFLAGS}) + endforeach() +elseif(NOT ANDROID) foreach(arch ${LSAN_COMMON_SUPPORTED_ARCH}) add_compiler_rt_object_library(RTLSanCommon ${arch} SOURCES ${LSAN_COMMON_SOURCES} diff --git a/lib/lsan/Makefile.mk b/lib/lsan/Makefile.mk index aae5c32fd32f..2a6b41c98e2c 100644 --- a/lib/lsan/Makefile.mk +++ b/lib/lsan/Makefile.mk @@ -7,17 +7,22 @@ # #===------------------------------------------------------------------------===# -ModuleName := lsan_common +ModuleName := lsan SubDirs := -Sources := $(foreach file,$(wildcard $(Dir)/lsan_common*.cc),$(notdir $(file))) +Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) ObjNames := $(Sources:%.cc=%.o) Implementation := Generic # FIXME: use automatic dependencies? Dependencies := $(wildcard $(Dir)/*.h) +Dependencies += $(wildcard $(Dir)/../interception/*.h) Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) -# Define a convenience variable for all the asan functions. -LsanCommonFunctions := $(Sources:%.cc=%) +# Define a convenience variable for all the lsan functions. +LsanFunctions := $(Sources:%.cc=%) + +# lsan functions used in another sanitizers. +LsanCommonSources := $(foreach file,$(wildcard $(Dir)/lsan_common*.cc),$(notdir $(file))) +LsanCommonFunctions := $(LsanCommonSources:%.cc=%) diff --git a/lib/lsan/lit_tests/AsanConfig/lit.cfg b/lib/lsan/lit_tests/AsanConfig/lit.cfg new file mode 100644 index 000000000000..ae9198173ffc --- /dev/null +++ b/lib/lsan/lit_tests/AsanConfig/lit.cfg @@ -0,0 +1,32 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +lsan_lit_src_root = get_required_attr(config, "lsan_lit_src_root") +lsan_lit_cfg = os.path.join(lsan_lit_src_root, "lit.common.cfg") +if not os.path.exists(lsan_lit_cfg): + lit_config.fatal("Can't find common LSan lit config at: %r" % lsan_lit_cfg) +lit_config.load_config(config, lsan_lit_cfg) + +config.name = 'LeakSanitizer-AddressSanitizer' + +clang_lsan_cxxflags = config.clang_cxxflags + " -fsanitize=address " + +config.substitutions.append( ("%clangxx_lsan ", (" " + config.clang + " " + + clang_lsan_cxxflags + " ")) ) + +clang_lsan_cflags = config.clang_cflags + " -fsanitize=address " + +config.substitutions.append( ("%clang_lsan ", (" " + config.clang + " " + + clang_lsan_cflags + " ")) ) + +config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' diff --git a/lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in b/lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in new file mode 100644 index 000000000000..9cf6572c54b7 --- /dev/null +++ b/lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in @@ -0,0 +1,8 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Tool-specific config options. +config.lsan_lit_src_root = "@LSAN_LIT_SOURCE_DIR@" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@LSAN_LIT_SOURCE_DIR@/AsanConfig/lit.cfg") diff --git a/lib/lsan/lit_tests/CMakeLists.txt b/lib/lsan/lit_tests/CMakeLists.txt index e1be508202b8..526d71bf3418 100644 --- a/lib/lsan/lit_tests/CMakeLists.txt +++ b/lib/lsan/lit_tests/CMakeLists.txt @@ -1,9 +1,16 @@ set(LSAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) set(LSAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..) +set(LSAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/LsanConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/LsanConfig/lit.site.cfg + ) + configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ${CMAKE_CURRENT_SOURCE_DIR}/AsanConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig/lit.site.cfg ) configure_lit_site_cfg( @@ -11,18 +18,20 @@ configure_lit_site_cfg( ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg ) -if(COMPILER_RT_CAN_EXECUTE_TESTS) +if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT APPLE AND NOT ANDROID) set(LSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS} ${LSAN_RUNTIME_LIBRARIES}) - set(LSAN_TEST_PARAMS - lsan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + foreach(arch ${LSAN_SUPPORTED_ARCH}) + list(APPEND LSAN_TEST_DEPS clang_rt.asan-${arch}) + endforeach() if(LLVM_INCLUDE_TESTS) - list(APPEND LSAN_TEST_DEPS LsanTests) + list(APPEND LSAN_TEST_DEPS LsanUnitTests) endif() add_lit_testsuite(check-lsan "Running the LeakSanitizer tests" - ${CMAKE_CURRENT_BINARY_DIR} - PARAMS ${LSAN_TEST_PARAMS} + ${CMAKE_CURRENT_BINARY_DIR}/LsanConfig + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig + ${CMAKE_CURRENT_BINARY_DIR}/Unit DEPENDS ${LSAN_TEST_DEPS}) set_target_properties(check-lsan PROPERTIES FOLDER "LSan tests") endif() diff --git a/lib/lsan/lit_tests/LsanConfig/lit.cfg b/lib/lsan/lit_tests/LsanConfig/lit.cfg new file mode 100644 index 000000000000..84faf9167a78 --- /dev/null +++ b/lib/lsan/lit_tests/LsanConfig/lit.cfg @@ -0,0 +1,30 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +lsan_lit_src_root = get_required_attr(config, "lsan_lit_src_root") +lsan_lit_cfg = os.path.join(lsan_lit_src_root, "lit.common.cfg") +if not os.path.exists(lsan_lit_cfg): + lit_config.fatal("Can't find common LSan lit config at: %r" % lsan_lit_cfg) +lit_config.load_config(config, lsan_lit_cfg) + +config.name = 'LeakSanitizer-Standalone' + +clang_lsan_cxxflags = config.clang_cxxflags + " -fsanitize=leak " + +config.substitutions.append( ("%clangxx_lsan ", (" " + config.clang + " " + + clang_lsan_cxxflags + " ")) ) + +clang_lsan_cflags = config.clang_cflags + " -fsanitize=leak " + +config.substitutions.append( ("%clang_lsan ", (" " + config.clang + " " + + clang_lsan_cflags + " ")) ) diff --git a/lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in b/lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in new file mode 100644 index 000000000000..2a6d724c0436 --- /dev/null +++ b/lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in @@ -0,0 +1,8 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Tool-specific config options. +config.lsan_lit_src_root = "@LSAN_LIT_SOURCE_DIR@" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@LSAN_LIT_SOURCE_DIR@/LsanConfig/lit.cfg") diff --git a/lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc b/lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc new file mode 100644 index 000000000000..475a66ec12a4 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc @@ -0,0 +1,12 @@ +// A loadable module with a large thread local section, which would require +// allocation of a new TLS storage chunk when loaded with dlopen(). We use it +// to test the reachability of such chunks in LSan tests. + +// This must be large enough that it doesn't fit into preallocated static TLS +// space (see STATIC_TLS_SURPLUS in glibc). +__thread void *huge_thread_local_array[(1 << 20) / sizeof(void *)]; // NOLINT + +extern "C" void **StoreToTLS(void *p) { + huge_thread_local_array[0] = p; + return &huge_thread_local_array[0]; +} diff --git a/lib/lsan/lit_tests/SharedLibs/lit.local.cfg b/lib/lsan/lit_tests/TestCases/SharedLibs/lit.local.cfg index b3677c17a0f2..b3677c17a0f2 100644 --- a/lib/lsan/lit_tests/SharedLibs/lit.local.cfg +++ b/lib/lsan/lit_tests/TestCases/SharedLibs/lit.local.cfg diff --git a/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc b/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc new file mode 100644 index 000000000000..ab368245317c --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc @@ -0,0 +1,45 @@ +// Regression test for thread lifetime tracking. Thread data should be +// considered live during the thread's termination, at least until the +// user-installed TSD destructors have finished running (since they may contain +// additional cleanup tasks). LSan doesn't actually meet that goal 100%, but it +// makes its best effort. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=1 %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=0 not %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +pthread_key_t key; +__thread void *p; + +void key_destructor(void *arg) { + // Generally this may happen on a different thread. + __lsan_do_leak_check(); +} + +void *thread_func(void *arg) { + p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + int res = pthread_setspecific(key, (void*)1); + assert(res == 0); + return 0; +} + +int main() { + int res = pthread_key_create(&key, &key_destructor); + assert(res == 0); + pthread_t thread_id; + res = pthread_create(&thread_id, 0, thread_func, 0); + assert(res == 0); + res = pthread_join(thread_id, 0); + assert(res == 0); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: leaked 1337 byte object at [[ADDR]] diff --git a/lib/lsan/lit_tests/TestCases/disabler.cc b/lib/lsan/lit_tests/TestCases/disabler.cc new file mode 100644 index 000000000000..db0cd8fabe4d --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/disabler.cc @@ -0,0 +1,23 @@ +// Test for ScopedDisabler. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +int main() { + void **p; + { + __lsan::ScopedDisabler d; + p = new void *; + } + *reinterpret_cast<void **>(p) = malloc(666); + void *q = malloc(1337); + // Break optimization. + fprintf(stderr, "Test alloc: %p.\n", q); + return 0; +} +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc b/lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc new file mode 100644 index 000000000000..94e4fc390b3b --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc @@ -0,0 +1,38 @@ +// Regression test. Disabler should not depend on TSD validity. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=1" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE %t + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +pthread_key_t key; + +void key_destructor(void *arg) { + __lsan::ScopedDisabler d; + void *p = malloc(1337); + // Break optimization. + fprintf(stderr, "Test alloc: %p.\n", p); + pthread_setspecific(key, 0); +} + +void *thread_func(void *arg) { + int res = pthread_setspecific(key, (void*)1); + assert(res == 0); + return 0; +} + +int main() { + int res = pthread_key_create(&key, &key_destructor); + assert(res == 0); + pthread_t thread_id; + res = pthread_create(&thread_id, 0, thread_func, 0); + assert(res == 0); + res = pthread_join(thread_id, 0); + assert(res == 0); + return 0; +} diff --git a/lib/lsan/lit_tests/TestCases/do_leak_check_override.cc b/lib/lsan/lit_tests/TestCases/do_leak_check_override.cc new file mode 100644 index 000000000000..be0ed0a6d48f --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/do_leak_check_override.cc @@ -0,0 +1,36 @@ +// Test for __lsan_do_leak_check(). We test it by making the leak check run +// before global destructors, which also tests compatibility with HeapChecker's +// "normal" mode (LSan runs in "strict" mode by default). +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck --check-prefix=CHECK-strict %s +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t foo 2>&1 | FileCheck --check-prefix=CHECK-normal %s + +#include <stdio.h> +#include <stdlib.h> +#include <sanitizer/lsan_interface.h> + +struct LeakyGlobal { + LeakyGlobal() { + p = malloc(1337); + } + ~LeakyGlobal() { + p = 0; + } + void *p; +}; + +LeakyGlobal leaky_global; + +int main(int argc, char *argv[]) { + // Register leak check to run before global destructors. + if (argc > 1) + atexit(&__lsan_do_leak_check); + void *p = malloc(666); + printf("Test alloc: %p\n", p); + printf("Test alloc in leaky global: %p\n", leaky_global.p); + return 0; +} + +// CHECK-strict: SUMMARY: {{(Leak|Address)}}Sanitizer: 2003 byte(s) leaked in 2 allocation(s) +// CHECK-normal: SUMMARY: {{(Leak|Address)}}Sanitizer: 666 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/fork.cc b/lib/lsan/lit_tests/TestCases/fork.cc new file mode 100644 index 000000000000..69258d9a0c72 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/fork.cc @@ -0,0 +1,24 @@ +// Test that thread local data is handled correctly after forking without exec(). +// RUN: %clangxx_lsan %s -o %t +// RUN: %t 2>&1 + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +__thread void *thread_local_var; + +int main() { + int status = 0; + thread_local_var = malloc(1337); + pid_t pid = fork(); + assert(pid >= 0); + if (pid > 0) { + waitpid(pid, &status, 0); + assert(WIFEXITED(status)); + return WEXITSTATUS(status); + } + return 0; +} diff --git a/lib/lsan/lit_tests/TestCases/fork_threaded.cc b/lib/lsan/lit_tests/TestCases/fork_threaded.cc new file mode 100644 index 000000000000..24a586109e28 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/fork_threaded.cc @@ -0,0 +1,43 @@ +// Test that thread local data is handled correctly after forking without +// exec(). In this test leak checking is initiated from a non-main thread. +// RUN: %clangxx_lsan %s -o %t +// RUN: %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +__thread void *thread_local_var; + +void *exit_thread_func(void *arg) { + exit(0); +} + +void ExitFromThread() { + pthread_t tid; + int res; + res = pthread_create(&tid, 0, exit_thread_func, 0); + assert(res == 0); + pthread_join(tid, 0); +} + +int main() { + int status = 0; + thread_local_var = malloc(1337); + pid_t pid = fork(); + assert(pid >= 0); + if (pid > 0) { + waitpid(pid, &status, 0); + assert(WIFEXITED(status)); + return WEXITSTATUS(status); + } else { + // Spawn a thread and call exit() from there, to check that we track main + // thread's pid correctly even if leak checking is initiated from another + // thread. + ExitFromThread(); + } + return 0; +} diff --git a/lib/lsan/lit_tests/TestCases/high_allocator_contention.cc b/lib/lsan/lit_tests/TestCases/high_allocator_contention.cc new file mode 100644 index 000000000000..1cecb2a550a1 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/high_allocator_contention.cc @@ -0,0 +1,48 @@ +// A benchmark that executes malloc/free pairs in parallel. +// Usage: ./a.out number_of_threads total_number_of_allocations +// RUN: %clangxx_lsan %s -o %t +// RUN: %t 5 1000000 2>&1 +#include <assert.h> +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +int num_threads; +int total_num_alloc; +const int kMaxNumThreads = 5000; +pthread_t tid[kMaxNumThreads]; + +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +bool go = false; + +void *thread_fun(void *arg) { + pthread_mutex_lock(&mutex); + while (!go) pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + for (int i = 0; i < total_num_alloc / num_threads; i++) { + void *p = malloc(10); + __asm__ __volatile__("" : : "r"(p) : "memory"); + free((void *)p); + } + return 0; +} + +int main(int argc, char** argv) { + assert(argc == 3); + num_threads = atoi(argv[1]); + assert(num_threads > 0); + assert(num_threads <= kMaxNumThreads); + total_num_alloc = atoi(argv[2]); + assert(total_num_alloc > 0); + printf("%d threads, %d allocations in each\n", num_threads, + total_num_alloc / num_threads); + for (int i = 0; i < num_threads; i++) + pthread_create(&tid[i], 0, thread_fun, 0); + pthread_mutex_lock(&mutex); + go = true; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); + for (int i = 0; i < num_threads; i++) pthread_join(tid[i], 0); + return 0; +} diff --git a/lib/lsan/lit_tests/TestCases/ignore_object.cc b/lib/lsan/lit_tests/TestCases/ignore_object.cc new file mode 100644 index 000000000000..cbc743b75497 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/ignore_object.cc @@ -0,0 +1,30 @@ +// Test for __lsan_ignore_object(). +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0:verbosity=3" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +int main() { + { + // The first malloc call can cause an allocation in libdl. Ignore it here so + // it doesn't show up in our output. + __lsan::ScopedDisabler d; + malloc(1); + } + // Explicitly ignored object. + void **p = new void *; + // Transitively ignored object. + *p = malloc(666); + // Non-ignored object. + volatile void *q = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + __lsan_ignore_object(p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: ignoring heap object at [[ADDR]] +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc b/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc new file mode 100644 index 000000000000..2a6c72551772 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc @@ -0,0 +1,22 @@ +// Test for incorrect use of __lsan_ignore_object(). +// RUN: LSAN_BASE="verbosity=2" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +int main() { + void *p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + __lsan_ignore_object(p); + __lsan_ignore_object(p); + free(p); + __lsan_ignore_object(p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: heap object at [[ADDR]] is already being ignored +// CHECK: no heap object found at [[ADDR]] diff --git a/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc b/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc new file mode 100644 index 000000000000..57d056597ee2 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc @@ -0,0 +1,18 @@ +// Test that LargeMmapAllocator's chunks aren't reachable via some internal data structure. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +int main() { + // maxsize in primary allocator is always less than this (1 << 25). + void *large_alloc = malloc(33554432); + fprintf(stderr, "Test alloc: %p.\n", large_alloc); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 33554432 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc b/lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc new file mode 100644 index 000000000000..38c1063b6ebb --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc @@ -0,0 +1,19 @@ +// Test for the leak_check_at_exit flag. +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS="verbosity=1" %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do +// RUN: LSAN_OPTIONS="verbosity=1" %t 2>&1 | FileCheck %s --check-prefix=CHECK-do +// RUN: LSAN_OPTIONS="verbosity=1:leak_check_at_exit=0" ASAN_OPTIONS="$ASAN_OPTIONS:leak_check_at_exit=0" %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do +// RUN: LSAN_OPTIONS="verbosity=1:leak_check_at_exit=0" ASAN_OPTIONS="$ASAN_OPTIONS:leak_check_at_exit=0" %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont + +#include <stdio.h> +#include <sanitizer/lsan_interface.h> + +int main(int argc, char *argv[]) { + printf("printf to break optimization\n"); + if (argc > 1) + __lsan_do_leak_check(); + return 0; +} + +// CHECK-do: SUMMARY: {{(Leak|Address)}}Sanitizer: +// CHECK-dont-NOT: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/link_turned_off.cc b/lib/lsan/lit_tests/TestCases/link_turned_off.cc new file mode 100644 index 000000000000..93628a1d15ee --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/link_turned_off.cc @@ -0,0 +1,24 @@ +// Test for disabling LSan at link-time. +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t foo 2>&1 | FileCheck %s + +#include <sanitizer/lsan_interface.h> + +int argc_copy; + +extern "C" { +int __lsan_is_turned_off() { + return (argc_copy == 1); +} +} + +int main(int argc, char *argv[]) { + volatile int *x = new int; + *x = 42; + argc_copy = argc; + return 0; +} + +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 4 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/pointer_to_self.cc b/lib/lsan/lit_tests/TestCases/pointer_to_self.cc new file mode 100644 index 000000000000..0d2818d2fa1d --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/pointer_to_self.cc @@ -0,0 +1,18 @@ +// Regression test: pointers to self should not confuse LSan into thinking the +// object is indirectly leaked. Only external pointers count. +// RUN: LSAN_BASE="report_objects=1:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +int main() { + void *p = malloc(1337); + *reinterpret_cast<void **>(p) = p; + fprintf(stderr, "Test alloc: %p.\n", p); +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c b/lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c new file mode 100644 index 000000000000..085412b47d55 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c @@ -0,0 +1,10 @@ +// Check that we can build C code. +// RUN: %clang_lsan %s -o %t +#ifdef __cplusplus +#error "This test must be built in C mode" +#endif + +int main() { + // FIXME: ideally this should somehow check that we don't have libstdc++ + return 0; +} diff --git a/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc b/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc new file mode 100644 index 000000000000..fabfb4ff21a9 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc @@ -0,0 +1,42 @@ +// Test that out-of-scope local variables are ignored by LSan. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=1" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE":exitcode=0" %t 2>&1 | FileCheck --check-prefix=CHECK-sanity %s + +#include <stdio.h> +#include <stdlib.h> + +void **pp; + +// Put pointer far enough on the stack that LSan has space to run in without +// overwriting it. +// Hopefully the argument p will be passed on a register, saving us from false +// negatives. +__attribute__((noinline)) +void *PutPointerOnStaleStack(void *p) { + void *locals[2048]; + locals[0] = p; + pp = &locals[0]; + fprintf(stderr, "Test alloc: %p.\n", locals[0]); + return 0; +} + +int main() { + PutPointerOnStaleStack(malloc(1337)); + return 0; +} + +// This must run after LSan, to ensure LSan didn't overwrite the pointer before +// it had a chance to see it. If LSan is invoked with atexit(), this works. +// Otherwise, we need a different method. +__attribute__((destructor)) +void ConfirmPointerHasSurvived() { + fprintf(stderr, "Value after LSan: %p.\n", *pp); +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK-sanity: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: +// CHECK-sanity: Value after LSan: [[ADDR]]. diff --git a/lib/lsan/lit_tests/TestCases/suppressions_default.cc b/lib/lsan/lit_tests/TestCases/suppressions_default.cc new file mode 100644 index 000000000000..9a165f8770f9 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/suppressions_default.cc @@ -0,0 +1,29 @@ +// Test for ScopedDisabler. +// RUN: LSAN_BASE="use_registers=0:use_stacks=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +extern "C" +const char *__lsan_default_suppressions() { + return "leak:*LSanTestLeakingFunc*"; +} + +void LSanTestLeakingFunc() { + void *p = malloc(666); + fprintf(stderr, "Test alloc: %p.\n", p); +} + +int main() { + LSanTestLeakingFunc(); + void *q = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", q); + return 0; +} +// CHECK: Suppressions used: +// CHECK: 1 666 *LSanTestLeakingFunc* +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/suppressions_file.cc b/lib/lsan/lit_tests/TestCases/suppressions_file.cc new file mode 100644 index 000000000000..9a165f8770f9 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/suppressions_file.cc @@ -0,0 +1,29 @@ +// Test for ScopedDisabler. +// RUN: LSAN_BASE="use_registers=0:use_stacks=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer/lsan_interface.h" + +extern "C" +const char *__lsan_default_suppressions() { + return "leak:*LSanTestLeakingFunc*"; +} + +void LSanTestLeakingFunc() { + void *p = malloc(666); + fprintf(stderr, "Test alloc: %p.\n", p); +} + +int main() { + LSanTestLeakingFunc(); + void *q = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", q); + return 0; +} +// CHECK: Suppressions used: +// CHECK: 1 666 *LSanTestLeakingFunc* +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp b/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp new file mode 100644 index 000000000000..8d8e560cba4c --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp @@ -0,0 +1 @@ +leak:*LSanTestLeakingFunc* diff --git a/lib/lsan/lit_tests/TestCases/swapcontext.cc b/lib/lsan/lit_tests/TestCases/swapcontext.cc new file mode 100644 index 000000000000..a06685ca2f03 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/swapcontext.cc @@ -0,0 +1,42 @@ +// We can't unwind stack if we're running coroutines on heap-allocated +// memory. Make sure we don't report these leaks. + +// RUN: %clangxx_lsan %s -o %t +// RUN: %t 2>&1 +// RUN: not %t foo 2>&1 | FileCheck %s + +#include <stdio.h> +#include <ucontext.h> +#include <unistd.h> + +const int kStackSize = 1 << 20; + +void Child() { + int child_stack; + printf("Child: %p\n", &child_stack); + int *leaked = new int[666]; +} + +int main(int argc, char *argv[]) { + char stack_memory[kStackSize + 1]; + char *heap_memory = new char[kStackSize + 1]; + char *child_stack = (argc > 1) ? stack_memory : heap_memory; + + printf("Child stack: %p\n", child_stack); + ucontext_t orig_context; + ucontext_t child_context; + getcontext(&child_context); + child_context.uc_stack.ss_sp = child_stack; + child_context.uc_stack.ss_size = kStackSize / 2; + child_context.uc_link = &orig_context; + makecontext(&child_context, Child, 0); + if (swapcontext(&orig_context, &child_context) < 0) { + perror("swapcontext"); + return 1; + } + + delete[] heap_memory; + return 0; +} + +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 2664 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/use_after_return.cc b/lib/lsan/lit_tests/TestCases/use_after_return.cc new file mode 100644 index 000000000000..93b0ea6068ef --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/use_after_return.cc @@ -0,0 +1,23 @@ +// Test that fake stack (introduced by ASan's use-after-return mode) is included +// in the root set. +// RUN: LSAN_BASE="report_objects=1:use_registers=0" +// RUN: %clangxx_lsan %s -O2 -o %t +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1 +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS="" %t 2>&1 + +#include <stdio.h> +#include <stdlib.h> + +int main() { + void *stack_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", stack_var); + // Take pointer to variable, to ensure it's not optimized into a register. + fprintf(stderr, "Stack var at: %p.\n", &stack_var); + // Do not return from main to prevent the pointer from going out of scope. + exit(0); +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/use_globals_initialized.cc b/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc index 53c22c8ac057..5a7c48bdf49a 100644 --- a/lib/lsan/lit_tests/use_globals_initialized.cc +++ b/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc @@ -1,7 +1,7 @@ -// Test that initialized globals are included in the root set. -// RUN: LSAN_BASE="report_blocks=1:use_stacks=0:use_registers=0" +// Test that initialized globals are included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %t 2>&1 | FileCheck %s // RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %t 2>&1 // RUN: LSAN_OPTIONS="" %t 2>&1 @@ -16,6 +16,6 @@ int main() { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks -// CHECK: Directly leaked 1337 byte block at [[ADDR]] -// CHECK: SUMMARY: LeakSanitizer: +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc b/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc new file mode 100644 index 000000000000..e1d045e3f79f --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc @@ -0,0 +1,21 @@ +// Test that uninitialized globals are included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include <stdio.h> +#include <stdlib.h> + +void *bss_var; + +int main() { + bss_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", bss_var); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_registers.cc b/lib/lsan/lit_tests/TestCases/use_registers.cc new file mode 100644 index 000000000000..a7d8a69d7173 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/use_registers.cc @@ -0,0 +1,51 @@ +// Test that registers of running threads are included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0" +// RUN: %clangxx_lsan -pthread %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +extern "C" +void *registers_thread_func(void *arg) { + int *sync = reinterpret_cast<int *>(arg); + void *p = malloc(1337); + // To store the pointer, choose a register which is unlikely to be reused by + // a function call. +#if defined(__i386__) + asm ( "mov %0, %%esi" + : + : "r" (p) + ); +#elif defined(__x86_64__) + asm ( "mov %0, %%r15" + : + : "r" (p) + ); +#else +#error "Test is not supported on this architecture." +#endif + fprintf(stderr, "Test alloc: %p.\n", p); + fflush(stderr); + __sync_fetch_and_xor(sync, 1); + while (true) + pthread_yield(); +} + +int main() { + int sync = 0; + pthread_t thread_id; + int res = pthread_create(&thread_id, 0, registers_thread_func, &sync); + assert(res == 0); + while (!__sync_fetch_and_xor(&sync, 0)) + pthread_yield(); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_stacks.cc b/lib/lsan/lit_tests/TestCases/use_stacks.cc new file mode 100644 index 000000000000..4287a96b2285 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/use_stacks.cc @@ -0,0 +1,20 @@ +// Test that stack of main thread is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include <stdio.h> +#include <stdlib.h> + +int main() { + void *stack_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", stack_var); + // Do not return from main to prevent the pointer from going out of scope. + exit(0); +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc b/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc new file mode 100644 index 000000000000..c7dfaf8abad6 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc @@ -0,0 +1,36 @@ +// Test that stacks of non-main threads are included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_registers=0" +// RUN: %clangxx_lsan -pthread %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +extern "C" +void *stacks_thread_func(void *arg) { + int *sync = reinterpret_cast<int *>(arg); + void *p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + fflush(stderr); + __sync_fetch_and_xor(sync, 1); + while (true) + pthread_yield(); +} + +int main() { + int sync = 0; + pthread_t thread_id; + int res = pthread_create(&thread_id, 0, stacks_thread_func, &sync); + assert(res == 0); + while (!__sync_fetch_and_xor(&sync, 0)) + pthread_yield(); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc b/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc new file mode 100644 index 000000000000..2570b63f0c5e --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc @@ -0,0 +1,33 @@ +// Test that dynamically allocated TLS space is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx %p/SharedLibs/huge_tls_lib_so.cc -fPIC -shared -o %t-so.so +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string> + +int main(int argc, char *argv[]) { + std::string path = std::string(argv[0]) + "-so.so"; + + void *handle = dlopen(path.c_str(), RTLD_LAZY); + assert(handle != 0); + typedef void **(* store_t)(void *p); + store_t StoreToTLS = (store_t)dlsym(handle, "StoreToTLS"); + assert(dlerror() == 0); + + void *p = malloc(1337); + void **p_in_tls = StoreToTLS(p); + assert(*p_in_tls == p); + fprintf(stderr, "Test alloc: %p.\n", p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc b/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc new file mode 100644 index 000000000000..3dea41edddd4 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc @@ -0,0 +1,37 @@ +// Test that dynamically allocated thread-specific storage is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +// From glibc: this many keys are stored in the thread descriptor directly. +const unsigned PTHREAD_KEY_2NDLEVEL_SIZE = 32; + +int main() { + static const unsigned kDummyKeysCount = PTHREAD_KEY_2NDLEVEL_SIZE; + int res; + pthread_key_t dummy_keys[kDummyKeysCount]; + for (unsigned i = 0; i < kDummyKeysCount; i++) { + res = pthread_key_create(&dummy_keys[i], NULL); + assert(res == 0); + } + pthread_key_t key; + res = pthread_key_create(&key, NULL); + assert(key >= PTHREAD_KEY_2NDLEVEL_SIZE); + assert(res == 0); + void *p = malloc(1337); + res = pthread_setspecific(key, p); + assert(res == 0); + fprintf(stderr, "Test alloc: %p.\n", p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc b/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc new file mode 100644 index 000000000000..b75f15153863 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc @@ -0,0 +1,31 @@ +// Test that statically allocated thread-specific storage is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +// From glibc: this many keys are stored in the thread descriptor directly. +const unsigned PTHREAD_KEY_2NDLEVEL_SIZE = 32; + +int main() { + pthread_key_t key; + int res; + res = pthread_key_create(&key, NULL); + assert(res == 0); + assert(key < PTHREAD_KEY_2NDLEVEL_SIZE); + void *p = malloc(1337); + res = pthread_setspecific(key, p); + assert(res == 0); + fprintf(stderr, "Test alloc: %p.\n", p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_tls_static.cc b/lib/lsan/lit_tests/TestCases/use_tls_static.cc new file mode 100644 index 000000000000..9ccb2b2b7fb1 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/use_tls_static.cc @@ -0,0 +1,21 @@ +// Test that statically allocated TLS space is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include <stdio.h> +#include <stdlib.h> + +__thread void *tls_var; + +int main() { + tls_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", tls_var); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_unaligned.cc b/lib/lsan/lit_tests/TestCases/use_unaligned.cc new file mode 100644 index 000000000000..bc75f11b99f0 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/use_unaligned.cc @@ -0,0 +1,23 @@ +// Test that unaligned pointers are detected correctly. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=1" %t 2>&1 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void *arr[2]; + +int main() { + void *p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + char *char_arr = (char *)arr; + memcpy(char_arr + 1, &p, sizeof(p)); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/Unit/lit.cfg b/lib/lsan/lit_tests/Unit/lit.cfg deleted file mode 100644 index bcd1de4477f1..000000000000 --- a/lib/lsan/lit_tests/Unit/lit.cfg +++ /dev/null @@ -1,26 +0,0 @@ -# -*- Python -*- - -import os - -def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if not attr_value: - lit.fatal("No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) - return attr_value - -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') -compiler_rt_lit_unit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.unit.cfg") -lit.load_config(config, compiler_rt_lit_unit_cfg) - -# Setup config name. -config.name = 'LeakSanitizer-Unit' - -# Setup test source and exec root. For unit tests, we define -# it as build directory with LSan unit tests. -lsan_binary_dir = get_required_attr(config, "lsan_binary_dir") -config.test_exec_root = os.path.join(lsan_binary_dir, "tests") -config.test_source_root = config.test_exec_root diff --git a/lib/lsan/lit_tests/Unit/lit.site.cfg.in b/lib/lsan/lit_tests/Unit/lit.site.cfg.in index 90c88c952156..a3a4e9ad0b13 100644 --- a/lib/lsan/lit_tests/Unit/lit.site.cfg.in +++ b/lib/lsan/lit_tests/Unit/lit.site.cfg.in @@ -1,17 +1,12 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! -config.target_triple = "@TARGET_TRIPLE@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_build_mode = "@LLVM_BUILD_MODE@" -config.lsan_binary_dir = "@LSAN_BINARY_DIR@" +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") -try: - config.llvm_build_mode = config.llvm_build_mode % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) - -# Let the main config do the real work. -lit.load_config(config, "@LSAN_SOURCE_DIR@/lit_tests/Unit/lit.cfg") +# Setup config name. +config.name = 'LeakSanitizer-Unit' +# Setup test source and exec root. For unit tests, we define +# it as build directory with LSan unit tests. +config.test_exec_root = "@LSAN_BINARY_DIR@/tests" +config.test_source_root = config.test_exec_root diff --git a/lib/lsan/lit_tests/lit.cfg b/lib/lsan/lit_tests/lit.cfg deleted file mode 100644 index 48e1453334d0..000000000000 --- a/lib/lsan/lit_tests/lit.cfg +++ /dev/null @@ -1,50 +0,0 @@ -# -*- Python -*- - -import os - -def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if not attr_value: - lit.fatal("No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) - return attr_value - -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') -compiler_rt_lit_unit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.unit.cfg") -lit.load_config(config, compiler_rt_lit_unit_cfg) - -# Setup config name. -config.name = 'LeakSanitizer' - -# Setup source root. -config.test_source_root = os.path.dirname(__file__) - -# Setup attributes common for all compiler-rt projects. -compiler_rt_lit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.cfg") -if (not compiler_rt_lit_cfg) or (not os.path.exists(compiler_rt_lit_cfg)): - lit.fatal("Can't find common compiler-rt lit config at: %r" - % compiler_rt_lit_cfg) -lit.load_config(config, compiler_rt_lit_cfg) - -clang_cxxflags = ("-ccc-cxx " - + "-g " - + "-O0 " - + "-m64 ") - -clang_lsan_cxxflags = clang_cxxflags + "-fsanitize=leak " - -config.substitutions.append( ("%clangxx ", (" " + config.clang + " " + - clang_cxxflags + " ")) ) -config.substitutions.append( ("%clangxx_lsan ", (" " + config.clang + " " + - clang_lsan_cxxflags + " ")) ) - -# Default test suffixes. -config.suffixes = ['.c', '.cc', '.cpp'] - -# LeakSanitizer tests are currently supported on x86-64 Linux only. -if config.host_os not in ['Linux'] or config.host_arch not in ['x86_64']: - config.unsupported = True diff --git a/lib/lsan/lit_tests/lit.common.cfg b/lib/lsan/lit_tests/lit.common.cfg new file mode 100644 index 000000000000..96dc1b1f55fc --- /dev/null +++ b/lib/lsan/lit_tests/lit.common.cfg @@ -0,0 +1,43 @@ +# -*- Python -*- + +# Common configuration for running leak detection tests under LSan/ASan. + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup source root. +lsan_lit_src_root = get_required_attr(config, 'lsan_lit_src_root') +config.test_source_root = os.path.join(lsan_lit_src_root, 'TestCases') + +clang_cxxflags = ("--driver-mode=g++ " + + "-g " + + "-O0 " + + "-m64 ") + +clang_cflags = ("-g " + + "-O0 " + + "-m64 ") + +config.clang_cxxflags = clang_cxxflags + +config.substitutions.append( ("%clangxx ", (" " + config.clang + " " + + clang_cxxflags + " ")) ) + +config.clang_cflags = clang_cflags + +config.substitutions.append( ("%clang ", (" " + config.clang + " " + + clang_cflags + " ")) ) + +# LeakSanitizer tests are currently supported on x86-64 Linux only. +if config.host_os not in ['Linux'] or config.host_arch not in ['x86_64']: + config.unsupported = True + +config.suffixes = ['.c', '.cc', '.cpp'] diff --git a/lib/lsan/lit_tests/lit.site.cfg.in b/lib/lsan/lit_tests/lit.site.cfg.in deleted file mode 100644 index 3de98a9811f8..000000000000 --- a/lib/lsan/lit_tests/lit.site.cfg.in +++ /dev/null @@ -1,20 +0,0 @@ -config.host_os = "@HOST_OS@" -config.host_arch = "@HOST_ARCH@" -config.llvm_build_mode = "@LLVM_BUILD_MODE@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_obj_root = "@LLVM_BINARY_DIR@" -config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" -config.clang = "@LLVM_BINARY_DIR@/bin/clang" -config.compiler_rt_arch = "@COMPILER_RT_SUPPORTED_ARCH@" - -# LLVM tools dir can be passed in lit parameters, so try to -# apply substitution. -try: - config.llvm_tools_dir = config.llvm_tools_dir % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) - -# Let the main config do the real work. -lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/lib/lsan/lsan.cc b/lib/lsan/lsan.cc index 9b83b411f843..058bbdba3907 100644 --- a/lib/lsan/lsan.cc +++ b/lib/lsan/lsan.cc @@ -20,25 +20,30 @@ #include "lsan_common.h" #include "lsan_thread.h" +bool lsan_inited; +bool lsan_init_is_running; + namespace __lsan { static void InitializeCommonFlags() { CommonFlags *cf = common_flags(); + SetCommonFlagDefaults(); cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); - cf->symbolize = (cf->external_symbolizer_path && - cf->external_symbolizer_path[0]); - cf->strip_path_prefix = ""; - cf->fast_unwind_on_malloc = true; cf->malloc_context_size = 30; + cf->detect_leaks = true; ParseCommonFlagsFromString(GetEnv("LSAN_OPTIONS")); } -void Init() { - static bool inited; - if (inited) +} // namespace __lsan + +using namespace __lsan; // NOLINT + +extern "C" void __lsan_init() { + CHECK(!lsan_init_is_running); + if (lsan_inited) return; - inited = true; + lsan_init_is_running = true; SanitizerToolName = "LeakSanitizer"; InitializeCommonFlags(); InitializeAllocator(); @@ -48,16 +53,19 @@ void Init() { u32 tid = ThreadCreate(0, 0, true); CHECK_EQ(tid, 0); ThreadStart(tid, GetTid()); + SetCurrentThread(tid); // Start symbolizer process if necessary. - const char* external_symbolizer = common_flags()->external_symbolizer_path; - if (common_flags()->symbolize && external_symbolizer && - external_symbolizer[0]) { - InitializeExternalSymbolizer(external_symbolizer); + if (common_flags()->symbolize) { + Symbolizer::Init(common_flags()->external_symbolizer_path); + } else { + Symbolizer::Disable(); } InitCommonLsan(); - Atexit(DoLeakCheck); + if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) + Atexit(DoLeakCheck); + lsan_inited = true; + lsan_init_is_running = false; } -} // namespace __lsan diff --git a/lib/lsan/lsan.h b/lib/lsan/lsan.h index d89a6ab8f983..3e7f76b08193 100644 --- a/lib/lsan/lsan.h +++ b/lib/lsan/lsan.h @@ -17,7 +17,11 @@ namespace __lsan { -void Init(); void InitializeInterceptors(); } // namespace __lsan + +extern bool lsan_inited; +extern bool lsan_init_is_running; + +extern "C" void __lsan_init(); diff --git a/lib/lsan/lsan_allocator.cc b/lib/lsan/lsan_allocator.cc index 49b5a9fa4c5d..f7eee1314bf0 100644 --- a/lib/lsan/lsan_allocator.cc +++ b/lib/lsan/lsan_allocator.cc @@ -20,13 +20,13 @@ #include "sanitizer_common/sanitizer_stacktrace.h" #include "lsan_common.h" -namespace __lsan { +extern "C" void *memset(void *ptr, int value, uptr num); -static const uptr kMaxAllowedMallocSize = - FIRST_32_SECOND_64(3UL << 30, 8UL << 30); +namespace __lsan { +static const uptr kMaxAllowedMallocSize = 8UL << 30; static const uptr kAllocatorSpace = 0x600000000000ULL; -static const uptr kAllocatorSize = 0x10000000000ULL; // 1T. +static const uptr kAllocatorSize = 0x40000000000ULL; // 4T. struct ChunkMetadata { bool allocated : 8; // Must be first. @@ -36,7 +36,7 @@ struct ChunkMetadata { }; typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, - sizeof(ChunkMetadata), CompactSizeClassMap> PrimaryAllocator; + sizeof(ChunkMetadata), DefaultSizeClassMap> PrimaryAllocator; typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; typedef LargeMmapAllocator<> SecondaryAllocator; typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, @@ -54,23 +54,24 @@ void AllocatorThreadFinish() { } static ChunkMetadata *Metadata(void *p) { - return (ChunkMetadata *)allocator.GetMetaData(p); + return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p)); } static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) { if (!p) return; ChunkMetadata *m = Metadata(p); CHECK(m); + m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked; m->stack_trace_id = StackDepotPut(stack.trace, stack.size); m->requested_size = size; - atomic_store((atomic_uint8_t*)m, 1, memory_order_relaxed); + atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 1, memory_order_relaxed); } static void RegisterDeallocation(void *p) { if (!p) return; ChunkMetadata *m = Metadata(p); CHECK(m); - atomic_store((atomic_uint8_t*)m, 0, memory_order_relaxed); + atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 0, memory_order_relaxed); } void *Allocate(const StackTrace &stack, uptr size, uptr alignment, @@ -78,11 +79,13 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment, if (size == 0) size = 1; if (size > kMaxAllowedMallocSize) { - Report("WARNING: LeakSanitizer failed to allocate %p bytes\n", - (void*)size); - return 0; + Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size); + return 0; } - void *p = allocator.Allocate(&cache, size, alignment, cleared); + void *p = allocator.Allocate(&cache, size, alignment, false); + // Do not rely on the allocator to clear the memory (it's slow). + if (cleared && allocator.FromPrimary(p)) + memset(p, 0, size); RegisterAllocation(stack, p, size); return p; } @@ -96,10 +99,9 @@ void *Reallocate(const StackTrace &stack, void *p, uptr new_size, uptr alignment) { RegisterDeallocation(p); if (new_size > kMaxAllowedMallocSize) { - Report("WARNING: LeakSanitizer failed to allocate %p bytes\n", - (void*)new_size); - allocator.Deallocate(&cache, p); - return 0; + Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", new_size); + allocator.Deallocate(&cache, p); + return 0; } p = allocator.Reallocate(&cache, p, new_size, alignment); RegisterAllocation(stack, p, new_size); @@ -132,26 +134,26 @@ void GetAllocatorGlobalRange(uptr *begin, uptr *end) { *end = *begin + sizeof(allocator); } -void *PointsIntoChunk(void* p) { - if (!allocator.PointerIsMine(p)) return 0; - void *chunk = allocator.GetBlockBegin(p); +uptr PointsIntoChunk(void* p) { + uptr addr = reinterpret_cast<uptr>(p); + uptr chunk = reinterpret_cast<uptr>(allocator.GetBlockBeginFastLocked(p)); if (!chunk) return 0; // LargeMmapAllocator considers pointers to the meta-region of a chunk to be // valid, but we don't want that. - if (p < chunk) return 0; - ChunkMetadata *m = Metadata(chunk); + if (addr < chunk) return 0; + ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk)); CHECK(m); - if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) + if (m->allocated && addr < chunk + m->requested_size) return chunk; return 0; } -void *GetUserBegin(void *p) { - return p; +uptr GetUserBegin(uptr chunk) { + return chunk; } -LsanMetadata::LsanMetadata(void *chunk) { - metadata_ = Metadata(chunk); +LsanMetadata::LsanMetadata(uptr chunk) { + metadata_ = Metadata(reinterpret_cast<void *>(chunk)); CHECK(metadata_); } @@ -175,16 +177,22 @@ u32 LsanMetadata::stack_trace_id() const { return reinterpret_cast<ChunkMetadata *>(metadata_)->stack_trace_id; } -template<typename Callable> -void ForEachChunk(Callable const &callback) { - allocator.ForEachChunk(callback); +void ForEachChunk(ForEachChunkCallback callback, void *arg) { + allocator.ForEachChunk(callback, arg); } -template void ForEachChunk<ProcessPlatformSpecificAllocationsCb>( - ProcessPlatformSpecificAllocationsCb const &callback); -template void ForEachChunk<PrintLeakedCb>(PrintLeakedCb const &callback); -template void ForEachChunk<CollectLeaksCb>(CollectLeaksCb const &callback); -template void ForEachChunk<MarkIndirectlyLeakedCb>( - MarkIndirectlyLeakedCb const &callback); -template void ForEachChunk<ClearTagCb>(ClearTagCb const &callback); +IgnoreObjectResult IgnoreObjectLocked(const void *p) { + void *chunk = allocator.GetBlockBegin(p); + if (!chunk || p < chunk) return kIgnoreObjectInvalid; + ChunkMetadata *m = Metadata(chunk); + CHECK(m); + if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) { + if (m->tag == kIgnored) + return kIgnoreObjectAlreadyIgnored; + m->tag = kIgnored; + return kIgnoreObjectSuccess; + } else { + return kIgnoreObjectInvalid; + } +} } // namespace __lsan diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc index e2971e999aa6..152588411e2f 100644 --- a/lib/lsan/lsan_common.cc +++ b/lib/lsan/lsan_common.cc @@ -16,27 +16,38 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_stoptheworld.h" +#include "sanitizer_common/sanitizer_suppressions.h" +#include "sanitizer_common/sanitizer_report_decorator.h" #if CAN_SANITIZE_LEAKS namespace __lsan { +// This mutex is used to prevent races between DoLeakCheck and IgnoreObject. +BlockingMutex global_mutex(LINKER_INITIALIZED); + +THREADLOCAL int disable_counter; +bool DisabledInThisThread() { return disable_counter > 0; } + Flags lsan_flags; static void InitializeFlags() { Flags *f = flags(); // Default values. - f->report_blocks = false; + f->report_objects = false; f->resolution = 0; f->max_leaks = 0; f->exitcode = 23; + f->suppressions=""; f->use_registers = true; f->use_globals = true; f->use_stacks = true; f->use_tls = true; f->use_unaligned = false; + f->verbosity = 0; f->log_pointers = false; f->log_threads = false; @@ -47,25 +58,60 @@ static void InitializeFlags() { ParseFlag(options, &f->use_stacks, "use_stacks"); ParseFlag(options, &f->use_tls, "use_tls"); ParseFlag(options, &f->use_unaligned, "use_unaligned"); - ParseFlag(options, &f->report_blocks, "report_blocks"); + ParseFlag(options, &f->report_objects, "report_objects"); ParseFlag(options, &f->resolution, "resolution"); CHECK_GE(&f->resolution, 0); ParseFlag(options, &f->max_leaks, "max_leaks"); CHECK_GE(&f->max_leaks, 0); + ParseFlag(options, &f->verbosity, "verbosity"); ParseFlag(options, &f->log_pointers, "log_pointers"); ParseFlag(options, &f->log_threads, "log_threads"); ParseFlag(options, &f->exitcode, "exitcode"); + ParseFlag(options, &f->suppressions, "suppressions"); + } +} + +SuppressionContext *suppression_ctx; + +void InitializeSuppressions() { + CHECK(!suppression_ctx); + ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)]; + suppression_ctx = new(placeholder_) SuppressionContext; + char *suppressions_from_file; + uptr buffer_size; + if (ReadFileToBuffer(flags()->suppressions, &suppressions_from_file, + &buffer_size, 1 << 26 /* max_len */)) + suppression_ctx->Parse(suppressions_from_file); + if (flags()->suppressions[0] && !buffer_size) { + Printf("LeakSanitizer: failed to read suppressions file '%s'\n", + flags()->suppressions); + Die(); } + if (&__lsan_default_suppressions) + suppression_ctx->Parse(__lsan_default_suppressions()); } void InitCommonLsan() { InitializeFlags(); - InitializePlatformSpecificModules(); + if (common_flags()->detect_leaks) { + // Initialization which can fail or print warnings should only be done if + // LSan is actually enabled. + InitializeSuppressions(); + InitializePlatformSpecificModules(); + } } +class Decorator: private __sanitizer::AnsiColorDecorator { + public: + Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + const char *Error() { return Red(); } + const char *Leak() { return Blue(); } + const char *End() { return Default(); } +}; + static inline bool CanBeAHeapPointer(uptr p) { // Since our heap is located in mmap-ed memory, we can assume a sensible lower - // boundary on heap addresses. + // bound on heap addresses. const uptr kMinAddress = 4 * 4096; if (p < kMinAddress) return false; #ifdef __x86_64__ @@ -76,13 +122,14 @@ static inline bool CanBeAHeapPointer(uptr p) { #endif } -// Scan the memory range, looking for byte patterns that point into allocator -// chunks. Mark those chunks with tag and add them to the frontier. -// There are two usage modes for this function: finding non-leaked chunks -// (tag = kReachable) and finding indirectly leaked chunks -// (tag = kIndirectlyLeaked). In the second case, there's no flood fill, -// so frontier = 0. -void ScanRangeForPointers(uptr begin, uptr end, InternalVector<uptr> *frontier, +// Scans the memory range, looking for byte patterns that point into allocator +// chunks. Marks those chunks with |tag| and adds them to |frontier|. +// There are two usage modes for this function: finding reachable or ignored +// chunks (|tag| = kReachable or kIgnored) and finding indirectly leaked chunks +// (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill, +// so |frontier| = 0. +void ScanRangeForPointers(uptr begin, uptr end, + Frontier *frontier, const char *region_type, ChunkTag tag) { const uptr alignment = flags()->pointer_alignment(); if (flags()->log_pointers) @@ -90,28 +137,34 @@ void ScanRangeForPointers(uptr begin, uptr end, InternalVector<uptr> *frontier, uptr pp = begin; if (pp % alignment) pp = pp + alignment - pp % alignment; - for (; pp + sizeof(uptr) <= end; pp += alignment) { - void *p = *reinterpret_cast<void**>(pp); + for (; pp + sizeof(void *) <= end; pp += alignment) { // NOLINT + void *p = *reinterpret_cast<void **>(pp); if (!CanBeAHeapPointer(reinterpret_cast<uptr>(p))) continue; - // FIXME: PointsIntoChunk is SLOW because GetBlockBegin() in - // LargeMmapAllocator involves a lock and a linear search. - void *chunk = PointsIntoChunk(p); + uptr chunk = PointsIntoChunk(p); if (!chunk) continue; + // Pointers to self don't count. This matters when tag == kIndirectlyLeaked. + if (chunk == begin) continue; LsanMetadata m(chunk); + // Reachable beats ignored beats leaked. if (m.tag() == kReachable) continue; + if (m.tag() == kIgnored && tag != kReachable) continue; m.set_tag(tag); if (flags()->log_pointers) - Report("%p: found %p pointing into chunk %p-%p of size %llu.\n", pp, p, - chunk, reinterpret_cast<uptr>(chunk) + m.requested_size(), - m.requested_size()); + Report("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p, + chunk, chunk + m.requested_size(), m.requested_size()); if (frontier) - frontier->push_back(reinterpret_cast<uptr>(chunk)); + frontier->push_back(chunk); } } -// Scan thread data (stacks and TLS) for heap pointers. +void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) { + Frontier *frontier = reinterpret_cast<Frontier *>(arg); + ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable); +} + +// Scans thread data (stacks and TLS) for heap pointers. static void ProcessThreads(SuspendedThreadsList const &suspended_threads, - InternalVector<uptr> *frontier) { + Frontier *frontier) { InternalScopedBuffer<uptr> registers(SuspendedThreadsList::RegisterCount()); uptr registers_begin = reinterpret_cast<uptr>(registers.data()); uptr registers_end = registers_begin + registers.size(); @@ -150,13 +203,14 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, // signal handler on alternate stack). Again, consider the entire stack // range to be reachable. if (flags()->log_threads) - Report("WARNING: stack_pointer not in stack_range.\n"); + Report("WARNING: stack pointer not in stack range.\n"); } else { // Shrink the stack range to ignore out-of-scope values. stack_begin = sp; } ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK", kReachable); + ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier); } if (flags()->use_tls) { @@ -178,152 +232,210 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, } } -static void FloodFillReachable(InternalVector<uptr> *frontier) { +static void FloodFillTag(Frontier *frontier, ChunkTag tag) { while (frontier->size()) { uptr next_chunk = frontier->back(); frontier->pop_back(); - LsanMetadata m(reinterpret_cast<void *>(next_chunk)); + LsanMetadata m(next_chunk); ScanRangeForPointers(next_chunk, next_chunk + m.requested_size(), frontier, - "HEAP", kReachable); + "HEAP", tag); } } -// Mark leaked chunks which are reachable from other leaked chunks. -void MarkIndirectlyLeakedCb::operator()(void *p) const { - p = GetUserBegin(p); - LsanMetadata m(p); +// ForEachChunk callback. If the chunk is marked as leaked, marks all chunks +// which are reachable from it as indirectly leaked. +static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) { + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); if (m.allocated() && m.tag() != kReachable) { - ScanRangeForPointers(reinterpret_cast<uptr>(p), - reinterpret_cast<uptr>(p) + m.requested_size(), + ScanRangeForPointers(chunk, chunk + m.requested_size(), /* frontier */ 0, "HEAP", kIndirectlyLeaked); } } -// Set the appropriate tag on each chunk. +// ForEachChunk callback. If chunk is marked as ignored, adds its address to +// frontier. +static void CollectIgnoredCb(uptr chunk, void *arg) { + CHECK(arg); + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (m.allocated() && m.tag() == kIgnored) + reinterpret_cast<Frontier *>(arg)->push_back(chunk); +} + +// Sets the appropriate tag on each chunk. static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) { // Holds the flood fill frontier. - InternalVector<uptr> frontier(GetPageSizeCached()); + Frontier frontier(GetPageSizeCached()); if (flags()->use_globals) ProcessGlobalRegions(&frontier); ProcessThreads(suspended_threads, &frontier); - FloodFillReachable(&frontier); + FloodFillTag(&frontier, kReachable); + // The check here is relatively expensive, so we do this in a separate flood + // fill. That way we can skip the check for chunks that are reachable + // otherwise. + if (flags()->log_pointers) + Report("Processing platform-specific allocations.\n"); ProcessPlatformSpecificAllocations(&frontier); - FloodFillReachable(&frontier); + FloodFillTag(&frontier, kReachable); - // Now all reachable chunks are marked. Iterate over leaked chunks and mark - // those that are reachable from other leaked chunks. if (flags()->log_pointers) - Report("Now scanning leaked blocks for pointers.\n"); - ForEachChunk(MarkIndirectlyLeakedCb()); -} + Report("Scanning ignored chunks.\n"); + CHECK_EQ(0, frontier.size()); + ForEachChunk(CollectIgnoredCb, &frontier); + FloodFillTag(&frontier, kIgnored); -void ClearTagCb::operator()(void *p) const { - p = GetUserBegin(p); - LsanMetadata m(p); - m.set_tag(kDirectlyLeaked); + // Iterate over leaked chunks and mark those that are reachable from other + // leaked chunks. + if (flags()->log_pointers) + Report("Scanning leaked chunks.\n"); + ForEachChunk(MarkIndirectlyLeakedCb, 0 /* arg */); } static void PrintStackTraceById(u32 stack_trace_id) { CHECK(stack_trace_id); uptr size = 0; const uptr *trace = StackDepotGet(stack_trace_id, &size); - StackTrace::PrintStack(trace, size, common_flags()->symbolize, - common_flags()->strip_path_prefix, 0); -} - -static void LockAndSuspendThreads(StopTheWorldCallback callback, void *arg) { - LockThreadRegistry(); - LockAllocator(); - StopTheWorld(callback, arg); - // Allocator must be unlocked by the callback. - UnlockThreadRegistry(); + StackTrace::PrintStack(trace, size); } -///// Normal leak checking. ///// - -void CollectLeaksCb::operator()(void *p) const { - p = GetUserBegin(p); - LsanMetadata m(p); +// ForEachChunk callback. Aggregates unreachable chunks into a LeakReport. +static void CollectLeaksCb(uptr chunk, void *arg) { + CHECK(arg); + LeakReport *leak_report = reinterpret_cast<LeakReport *>(arg); + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); if (!m.allocated()) return; - if (m.tag() != kReachable) { + if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) { uptr resolution = flags()->resolution; if (resolution > 0) { uptr size = 0; const uptr *trace = StackDepotGet(m.stack_trace_id(), &size); size = Min(size, resolution); - leak_report_->Add(StackDepotPut(trace, size), m.requested_size(), - m.tag()); + leak_report->Add(StackDepotPut(trace, size), m.requested_size(), m.tag()); } else { - leak_report_->Add(m.stack_trace_id(), m.requested_size(), m.tag()); + leak_report->Add(m.stack_trace_id(), m.requested_size(), m.tag()); } } } -static void CollectLeaks(LeakReport *leak_report) { - ForEachChunk(CollectLeaksCb(leak_report)); -} - -void PrintLeakedCb::operator()(void *p) const { - p = GetUserBegin(p); - LsanMetadata m(p); +// ForEachChunkCallback. Prints addresses of unreachable chunks. +static void PrintLeakedCb(uptr chunk, void *arg) { + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); if (!m.allocated()) return; - if (m.tag() != kReachable) { - CHECK(m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked); - Printf("%s leaked %llu byte block at %p\n", + if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) { + Printf("%s leaked %zu byte object at %p.\n", m.tag() == kDirectlyLeaked ? "Directly" : "Indirectly", - m.requested_size(), p); + m.requested_size(), chunk); } } +static void PrintMatchedSuppressions() { + InternalMmapVector<Suppression *> matched(1); + suppression_ctx->GetMatched(&matched); + if (!matched.size()) + return; + const char *line = "-----------------------------------------------------"; + Printf("%s\n", line); + Printf("Suppressions used:\n"); + Printf(" count bytes template\n"); + for (uptr i = 0; i < matched.size(); i++) + Printf("%7zu %10zu %s\n", static_cast<uptr>(matched[i]->hit_count), + matched[i]->weight, matched[i]->templ); + Printf("%s\n\n", line); +} + static void PrintLeaked() { - Printf("Reporting individual blocks:\n"); - Printf("============================\n"); - ForEachChunk(PrintLeakedCb()); Printf("\n"); + Printf("Reporting individual objects:\n"); + ForEachChunk(PrintLeakedCb, 0 /* arg */); } -enum LeakCheckResult { - kFatalError, - kLeaksFound, - kNoLeaks +struct DoLeakCheckParam { + bool success; + LeakReport leak_report; }; static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads, void *arg) { - LeakCheckResult *result = reinterpret_cast<LeakCheckResult *>(arg); - CHECK_EQ(*result, kFatalError); - // Allocator must not be locked when we call GetRegionBegin(). - UnlockAllocator(); + DoLeakCheckParam *param = reinterpret_cast<DoLeakCheckParam *>(arg); + CHECK(param); + CHECK(!param->success); + CHECK(param->leak_report.IsEmpty()); ClassifyAllChunks(suspended_threads); - LeakReport leak_report; - CollectLeaks(&leak_report); - if (leak_report.IsEmpty()) { - *result = kNoLeaks; - return; - } - Printf("\n"); - Printf("=================================================================\n"); - Report("ERROR: LeakSanitizer: detected memory leaks\n"); - leak_report.PrintLargest(flags()->max_leaks); - if (flags()->report_blocks) + ForEachChunk(CollectLeaksCb, ¶m->leak_report); + if (!param->leak_report.IsEmpty() && flags()->report_objects) PrintLeaked(); - leak_report.PrintSummary(); - Printf("\n"); - ForEachChunk(ClearTagCb()); - *result = kLeaksFound; + param->success = true; } void DoLeakCheck() { - LeakCheckResult result = kFatalError; - LockAndSuspendThreads(DoLeakCheckCallback, &result); - if (result == kFatalError) { + EnsureMainThreadIDIsCorrect(); + BlockingMutexLock l(&global_mutex); + static bool already_done; + if (already_done) return; + already_done = true; + if (&__lsan_is_turned_off && __lsan_is_turned_off()) + return; + + DoLeakCheckParam param; + param.success = false; + LockThreadRegistry(); + LockAllocator(); + StopTheWorld(DoLeakCheckCallback, ¶m); + UnlockAllocator(); + UnlockThreadRegistry(); + + if (!param.success) { Report("LeakSanitizer has encountered a fatal error.\n"); Die(); - } else if (result == kLeaksFound) { - if (flags()->exitcode) - internal__exit(flags()->exitcode); } + uptr have_unsuppressed = param.leak_report.ApplySuppressions(); + if (have_unsuppressed) { + Decorator d; + Printf("\n" + "=================================================================" + "\n"); + Printf("%s", d.Error()); + Report("ERROR: LeakSanitizer: detected memory leaks\n"); + Printf("%s", d.End()); + param.leak_report.PrintLargest(flags()->max_leaks); + } + if (have_unsuppressed || (flags()->verbosity >= 1)) { + PrintMatchedSuppressions(); + param.leak_report.PrintSummary(); + } + if (have_unsuppressed && flags()->exitcode) + internal__exit(flags()->exitcode); +} + +static Suppression *GetSuppressionForAddr(uptr addr) { + static const uptr kMaxAddrFrames = 16; + InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames); + for (uptr i = 0; i < kMaxAddrFrames; i++) new (&addr_frames[i]) AddressInfo(); + uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode( + addr, addr_frames.data(), kMaxAddrFrames); + for (uptr i = 0; i < addr_frames_num; i++) { + Suppression* s; + if (suppression_ctx->Match(addr_frames[i].function, SuppressionLeak, &s) || + suppression_ctx->Match(addr_frames[i].file, SuppressionLeak, &s) || + suppression_ctx->Match(addr_frames[i].module, SuppressionLeak, &s)) + return s; + } + return 0; +} + +static Suppression *GetSuppressionForStack(u32 stack_trace_id) { + uptr size = 0; + const uptr *trace = StackDepotGet(stack_trace_id, &size); + for (uptr i = 0; i < size; i++) { + Suppression *s = + GetSuppressionForAddr(StackTrace::GetPreviousInstructionPc(trace[i])); + if (s) return s; + } + return 0; } ///// LeakReport implementation. ///// @@ -333,7 +445,7 @@ void DoLeakCheck() { // real-world applications. // FIXME: Get rid of this limit by changing the implementation of LeakReport to // use a hash table. -const uptr kMaxLeaksConsidered = 1000; +const uptr kMaxLeaksConsidered = 5000; void LeakReport::Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag) { CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked); @@ -347,35 +459,47 @@ void LeakReport::Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag) { } if (leaks_.size() == kMaxLeaksConsidered) return; Leak leak = { /* hit_count */ 1, leaked_size, stack_trace_id, - is_directly_leaked }; + is_directly_leaked, /* is_suppressed */ false }; leaks_.push_back(leak); } -static bool IsLarger(const Leak &leak1, const Leak &leak2) { - return leak1.total_size > leak2.total_size; +static bool LeakComparator(const Leak &leak1, const Leak &leak2) { + if (leak1.is_directly_leaked == leak2.is_directly_leaked) + return leak1.total_size > leak2.total_size; + else + return leak1.is_directly_leaked; } -void LeakReport::PrintLargest(uptr max_leaks) { +void LeakReport::PrintLargest(uptr num_leaks_to_print) { CHECK(leaks_.size() <= kMaxLeaksConsidered); Printf("\n"); if (leaks_.size() == kMaxLeaksConsidered) - Printf("Too many leaks! Only the first %llu leaks encountered will be " + Printf("Too many leaks! Only the first %zu leaks encountered will be " "reported.\n", kMaxLeaksConsidered); - if (max_leaks > 0 && max_leaks < leaks_.size()) - Printf("The %llu largest leak(s):\n", max_leaks); - InternalSort(&leaks_, leaks_.size(), IsLarger); - max_leaks = max_leaks > 0 ? Min(max_leaks, leaks_.size()) : leaks_.size(); - for (uptr i = 0; i < max_leaks; i++) { - Printf("%s leak of %llu byte(s) in %llu object(s) allocated from:\n", + + uptr unsuppressed_count = 0; + for (uptr i = 0; i < leaks_.size(); i++) + if (!leaks_[i].is_suppressed) unsuppressed_count++; + if (num_leaks_to_print > 0 && num_leaks_to_print < unsuppressed_count) + Printf("The %zu largest leak(s):\n", num_leaks_to_print); + InternalSort(&leaks_, leaks_.size(), LeakComparator); + uptr leaks_printed = 0; + Decorator d; + for (uptr i = 0; i < leaks_.size(); i++) { + if (leaks_[i].is_suppressed) continue; + Printf("%s", d.Leak()); + Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n", leaks_[i].is_directly_leaked ? "Direct" : "Indirect", leaks_[i].total_size, leaks_[i].hit_count); + Printf("%s", d.End()); PrintStackTraceById(leaks_[i].stack_trace_id); - Printf("\n"); + leaks_printed++; + if (leaks_printed == num_leaks_to_print) break; } - if (max_leaks < leaks_.size()) { - uptr remaining = leaks_.size() - max_leaks; - Printf("Omitting %llu more leak(s).\n", remaining); + if (leaks_printed < unsuppressed_count) { + uptr remaining = unsuppressed_count - leaks_printed; + Printf("Omitting %zu more leak(s).\n", remaining); } } @@ -383,11 +507,86 @@ void LeakReport::PrintSummary() { CHECK(leaks_.size() <= kMaxLeaksConsidered); uptr bytes = 0, allocations = 0; for (uptr i = 0; i < leaks_.size(); i++) { + if (leaks_[i].is_suppressed) continue; bytes += leaks_[i].total_size; allocations += leaks_[i].hit_count; } - Printf("SUMMARY: LeakSanitizer: %llu byte(s) leaked in %llu allocation(s).\n", - bytes, allocations); + InternalScopedBuffer<char> summary(kMaxSummaryLength); + internal_snprintf(summary.data(), summary.size(), + "%zu byte(s) leaked in %zu allocation(s).", bytes, + allocations); + ReportErrorSummary(summary.data()); +} + +uptr LeakReport::ApplySuppressions() { + uptr unsuppressed_count = 0; + for (uptr i = 0; i < leaks_.size(); i++) { + Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id); + if (s) { + s->weight += leaks_[i].total_size; + s->hit_count += leaks_[i].hit_count; + leaks_[i].is_suppressed = true; + } else { + unsuppressed_count++; + } + } + return unsuppressed_count; } } // namespace __lsan #endif // CAN_SANITIZE_LEAKS + +using namespace __lsan; // NOLINT + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_ignore_object(const void *p) { +#if CAN_SANITIZE_LEAKS + if (!common_flags()->detect_leaks) + return; + // Cannot use PointsIntoChunk or LsanMetadata here, since the allocator is not + // locked. + BlockingMutexLock l(&global_mutex); + IgnoreObjectResult res = IgnoreObjectLocked(p); + if (res == kIgnoreObjectInvalid && flags()->verbosity >= 2) + Report("__lsan_ignore_object(): no heap object found at %p", p); + if (res == kIgnoreObjectAlreadyIgnored && flags()->verbosity >= 2) + Report("__lsan_ignore_object(): " + "heap object at %p is already being ignored\n", p); + if (res == kIgnoreObjectSuccess && flags()->verbosity >= 3) + Report("__lsan_ignore_object(): ignoring heap object at %p\n", p); +#endif // CAN_SANITIZE_LEAKS +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_disable() { +#if CAN_SANITIZE_LEAKS + __lsan::disable_counter++; +#endif +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_enable() { +#if CAN_SANITIZE_LEAKS + if (!__lsan::disable_counter && common_flags()->detect_leaks) { + Report("Unmatched call to __lsan_enable().\n"); + Die(); + } + __lsan::disable_counter--; +#endif +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_do_leak_check() { +#if CAN_SANITIZE_LEAKS + if (common_flags()->detect_leaks) + __lsan::DoLeakCheck(); +#endif // CAN_SANITIZE_LEAKS +} + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +int __lsan_is_turned_off() { + return 0; +} +#endif +} // extern "C" diff --git a/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h index 8cb4b2753cd9..d490f8bafd9e 100644 --- a/lib/lsan/lsan_common.h +++ b/lib/lsan/lsan_common.h @@ -15,6 +15,7 @@ #ifndef LSAN_COMMON_H #define LSAN_COMMON_H +#include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_platform.h" @@ -32,7 +33,8 @@ namespace __lsan { enum ChunkTag { kDirectlyLeaked = 0, // default kIndirectlyLeaked = 1, - kReachable = 2 + kReachable = 2, + kIgnored = 3 }; struct Flags { @@ -40,15 +42,17 @@ struct Flags { return use_unaligned ? 1 : sizeof(uptr); } - // Print addresses of leaked blocks after main leak report. - bool report_blocks; - // Aggregate two blocks into one leak if this many stack frames match. If + // Print addresses of leaked objects after main leak report. + bool report_objects; + // Aggregate two objects into one leak if this many stack frames match. If // zero, the entire stack trace must match. int resolution; // The number of leaks reported. int max_leaks; // If nonzero kill the process with this exit code upon finding leaks. int exitcode; + // Suppressions file name. + const char* suppressions; // Flags controlling the root set of reachable memory. // Global variables (.data and .bss). @@ -63,6 +67,9 @@ struct Flags { // Consider unaligned pointers valid. bool use_unaligned; + // User-visible verbosity. + int verbosity; + // Debug logging. bool log_pointers; bool log_threads; @@ -71,17 +78,12 @@ struct Flags { extern Flags lsan_flags; inline Flags *flags() { return &lsan_flags; } -void InitCommonLsan(); -// Testing interface. Find leaked chunks and dump their addresses to vector. -void ReportLeaked(InternalVector<void *> *leaked, uptr sources); -// Normal leak check. Find leaks and print a report according to flags. -void DoLeakCheck(); - struct Leak { uptr hit_count; uptr total_size; u32 stack_trace_id; bool is_directly_leaked; + bool is_suppressed; }; // Aggregates leaks by stack trace prefix. @@ -92,66 +94,37 @@ class LeakReport { void PrintLargest(uptr max_leaks); void PrintSummary(); bool IsEmpty() { return leaks_.size() == 0; } + uptr ApplySuppressions(); private: - InternalVector<Leak> leaks_; + InternalMmapVector<Leak> leaks_; }; +typedef InternalMmapVector<uptr> Frontier; + // Platform-specific functions. void InitializePlatformSpecificModules(); -void ProcessGlobalRegions(InternalVector<uptr> *frontier); -void ProcessPlatformSpecificAllocations(InternalVector<uptr> *frontier); +void ProcessGlobalRegions(Frontier *frontier); +void ProcessPlatformSpecificAllocations(Frontier *frontier); -void ScanRangeForPointers(uptr begin, uptr end, InternalVector<uptr> *frontier, +void ScanRangeForPointers(uptr begin, uptr end, + Frontier *frontier, const char *region_type, ChunkTag tag); -// Callables for iterating over chunks. Those classes are used as template -// parameters in ForEachChunk, so we must expose them here to allow for explicit -// template instantiation. - -// Identifies unreachable chunks which must be treated as reachable. Marks them -// as reachable and adds them to the frontier. -class ProcessPlatformSpecificAllocationsCb { - public: - explicit ProcessPlatformSpecificAllocationsCb(InternalVector<uptr> *frontier) - : frontier_(frontier) {} - void operator()(void *p) const; - private: - InternalVector<uptr> *frontier_; -}; - -// Prints addresses of unreachable chunks. -class PrintLeakedCb { - public: - void operator()(void *p) const; -}; - -// Aggregates unreachable chunks into a LeakReport. -class CollectLeaksCb { - public: - explicit CollectLeaksCb(LeakReport *leak_report) - : leak_report_(leak_report) {} - void operator()(void *p) const; - private: - LeakReport *leak_report_; -}; - -// Resets each chunk's tag to default (kDirectlyLeaked). -class ClearTagCb { - public: - void operator()(void *p) const; +enum IgnoreObjectResult { + kIgnoreObjectSuccess, + kIgnoreObjectAlreadyIgnored, + kIgnoreObjectInvalid }; -// Scans each leaked chunk for pointers to other leaked chunks, and marks each -// of them as indirectly leaked. -class MarkIndirectlyLeakedCb { - public: - void operator()(void *p) const; -}; +// Functions called from the parent tool. +void InitCommonLsan(); +void DoLeakCheck(); +bool DisabledInThisThread(); // The following must be implemented in the parent tool. -template<typename Callable> void ForEachChunk(Callable const &callback); -// The address range occupied by the global allocator object. +void ForEachChunk(ForEachChunkCallback callback, void *arg); +// Returns the address range occupied by the global allocator object. void GetAllocatorGlobalRange(uptr *begin, uptr *end); // Wrappers for allocator's ForceLock()/ForceUnlock(). void LockAllocator(); @@ -162,16 +135,27 @@ void UnlockThreadRegistry(); bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, uptr *tls_begin, uptr *tls_end, uptr *cache_begin, uptr *cache_end); -// If p points into a chunk that has been allocated to the user, return its -// user-visible address. Otherwise, return 0. -void *PointsIntoChunk(void *p); -// Return address of user-visible chunk contained in this allocator chunk. -void *GetUserBegin(void *p); +void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, + void *arg); +// If called from the main thread, updates the main thread's TID in the thread +// registry. We need this to handle processes that fork() without a subsequent +// exec(), which invalidates the recorded TID. To update it, we must call +// gettid() from the main thread. Our solution is to call this function before +// leak checking and also before every call to pthread_create() (to handle cases +// where leak checking is initiated from a non-main thread). +void EnsureMainThreadIDIsCorrect(); +// If p points into a chunk that has been allocated to the user, returns its +// user-visible address. Otherwise, returns 0. +uptr PointsIntoChunk(void *p); +// Returns address of user-visible chunk contained in this allocator chunk. +uptr GetUserBegin(uptr chunk); +// Helper for __lsan_ignore_object(). +IgnoreObjectResult IgnoreObjectLocked(const void *p); // Wrapper for chunk metadata operations. class LsanMetadata { public: - // Constructor accepts pointer to user-visible chunk. - explicit LsanMetadata(void *chunk); + // Constructor accepts address of user-visible chunk. + explicit LsanMetadata(uptr chunk); bool allocated() const; ChunkTag tag() const; void set_tag(ChunkTag value); @@ -183,4 +167,12 @@ class LsanMetadata { } // namespace __lsan +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +int __lsan_is_turned_off(); + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +const char *__lsan_default_suppressions(); +} // extern "C" + #endif // LSAN_COMMON_H diff --git a/lib/lsan/lsan_common_linux.cc b/lib/lsan/lsan_common_linux.cc index 10a434b5f851..ef8857fe8dbb 100644 --- a/lib/lsan/lsan_common_linux.cc +++ b/lib/lsan/lsan_common_linux.cc @@ -53,8 +53,7 @@ void InitializePlatformSpecificModules() { static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size, void *data) { - InternalVector<uptr> *frontier = - reinterpret_cast<InternalVector<uptr> *>(data); + Frontier *frontier = reinterpret_cast<Frontier *>(data); for (uptr j = 0; j < info->dlpi_phnum; j++) { const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]); // We're looking for .data and .bss sections, which reside in writeable, @@ -82,8 +81,8 @@ static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size, return 0; } -// Scan global variables for heap pointers. -void ProcessGlobalRegions(InternalVector<uptr> *frontier) { +// Scans global variables for heap pointers. +void ProcessGlobalRegions(Frontier *frontier) { // FIXME: dl_iterate_phdr acquires a linker lock, so we run a risk of // deadlocking by running this under StopTheWorld. However, the lock is // reentrant, so we should be able to fix this by acquiring the lock before @@ -91,32 +90,51 @@ void ProcessGlobalRegions(InternalVector<uptr> *frontier) { dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier); } -static uptr GetCallerPC(u32 stack_id) { +static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) { CHECK(stack_id); uptr size = 0; - const uptr *trace = StackDepotGet(stack_id, &size); + const uptr *trace = map->Get(stack_id, &size); // The top frame is our malloc/calloc/etc. The next frame is the caller. - CHECK_GE(size, 2); - return trace[1]; + if (size >= 2) + return trace[1]; + return 0; } -void ProcessPlatformSpecificAllocationsCb::operator()(void *p) const { - p = GetUserBegin(p); - LsanMetadata m(p); +struct ProcessPlatformAllocParam { + Frontier *frontier; + StackDepotReverseMap *stack_depot_reverse_map; +}; + +// ForEachChunk callback. Identifies unreachable chunks which must be treated as +// reachable. Marks them as reachable and adds them to the frontier. +static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) { + CHECK(arg); + ProcessPlatformAllocParam *param = + reinterpret_cast<ProcessPlatformAllocParam *>(arg); + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); if (m.allocated() && m.tag() != kReachable) { - if (linker->containsAddress(GetCallerPC(m.stack_trace_id()))) { + u32 stack_id = m.stack_trace_id(); + uptr caller_pc = 0; + if (stack_id > 0) + caller_pc = GetCallerPC(stack_id, param->stack_depot_reverse_map); + // If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark + // it as reachable, as we can't properly report its allocation stack anyway. + if (caller_pc == 0 || linker->containsAddress(caller_pc)) { m.set_tag(kReachable); - frontier_->push_back(reinterpret_cast<uptr>(p)); + param->frontier->push_back(chunk); } } } -// Handle dynamically allocated TLS blocks by treating all chunks allocated from -// ld-linux.so as reachable. -void ProcessPlatformSpecificAllocations(InternalVector<uptr> *frontier) { +// Handles dynamically allocated TLS blocks by treating all chunks allocated +// from ld-linux.so as reachable. +void ProcessPlatformSpecificAllocations(Frontier *frontier) { if (!flags()->use_tls) return; if (!linker) return; - ForEachChunk(ProcessPlatformSpecificAllocationsCb(frontier)); + StackDepotReverseMap stack_depot_reverse_map; + ProcessPlatformAllocParam arg = {frontier, &stack_depot_reverse_map}; + ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg); } } // namespace __lsan diff --git a/lib/lsan/lsan_interceptors.cc b/lib/lsan/lsan_interceptors.cc index b2eb6e310229..400230b885ff 100644 --- a/lib/lsan/lsan_interceptors.cc +++ b/lib/lsan/lsan_interceptors.cc @@ -44,66 +44,85 @@ int pthread_setspecific(unsigned key, const void *v); stack_top = t->stack_end(); \ stack_bottom = t->stack_begin(); \ } \ - GetStackTrace(&stack, __sanitizer::common_flags()->malloc_context_size, \ - StackTrace::GetCurrentPc(), \ - GET_CURRENT_FRAME(), stack_top, stack_bottom, fast); \ + stack.Unwind(__sanitizer::common_flags()->malloc_context_size, \ + StackTrace::GetCurrentPc(), \ + GET_CURRENT_FRAME(), stack_top, stack_bottom, fast); \ } +#define ENSURE_LSAN_INITED do { \ + CHECK(!lsan_init_is_running); \ + if (!lsan_inited) \ + __lsan_init(); \ +} while (0) + ///// Malloc/free interceptors. ///// +const bool kAlwaysClearMemory = true; + namespace std { struct nothrow_t; } INTERCEPTOR(void*, malloc, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; - return Allocate(stack, size, 1, false); + return Allocate(stack, size, 1, kAlwaysClearMemory); } INTERCEPTOR(void, free, void *p) { - Init(); + ENSURE_LSAN_INITED; Deallocate(p); } INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { + if (lsan_init_is_running) { + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + const uptr kCallocPoolSize = 1024; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + static uptr allocated; + uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; + void *mem = (void*)&calloc_memory_for_dlsym[allocated]; + allocated += size_in_words; + CHECK(allocated < kCallocPoolSize); + return mem; + } if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; size *= nmemb; return Allocate(stack, size, 1, true); } INTERCEPTOR(void*, realloc, void *q, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; return Reallocate(stack, q, size, 1); } INTERCEPTOR(void*, memalign, uptr alignment, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; - return Allocate(stack, size, alignment, false); + return Allocate(stack, size, alignment, kAlwaysClearMemory); } INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; - *memptr = Allocate(stack, size, alignment, false); + *memptr = Allocate(stack, size, alignment, kAlwaysClearMemory); // FIXME: Return ENOMEM if user requested more than max alloc size. return 0; } INTERCEPTOR(void*, valloc, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; if (size == 0) size = GetPageSizeCached(); - return Allocate(stack, size, GetPageSizeCached(), false); + return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); } INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { - Init(); + ENSURE_LSAN_INITED; return GetMallocUsableSize(ptr); } @@ -121,28 +140,52 @@ INTERCEPTOR(int, mallopt, int cmd, int value) { return -1; } -void *operator new(uptr size) ALIAS("malloc") SANITIZER_INTERFACE_ATTRIBUTE; -void *operator new[](uptr size) ALIAS("malloc") SANITIZER_INTERFACE_ATTRIBUTE; -void *operator new(uptr size, std::nothrow_t const&) ALIAS("malloc") - SANITIZER_INTERFACE_ATTRIBUTE; -void *operator new[](uptr size, std::nothrow_t const&) ALIAS("malloc") - SANITIZER_INTERFACE_ATTRIBUTE; -void operator delete(void *ptr) ALIAS("free") SANITIZER_INTERFACE_ATTRIBUTE; -void operator delete[](void *ptr) ALIAS("free") SANITIZER_INTERFACE_ATTRIBUTE; -void operator delete(void *ptr, std::nothrow_t const&) ALIAS("free") - SANITIZER_INTERFACE_ATTRIBUTE; -void operator delete[](void *ptr, std::nothrow_t const&) ALIAS("free") - SANITIZER_INTERFACE_ATTRIBUTE; +INTERCEPTOR(void*, pvalloc, uptr size) { + ENSURE_LSAN_INITED; + GET_STACK_TRACE; + uptr PageSize = GetPageSizeCached(); + size = RoundUpTo(size, PageSize); + if (size == 0) { + // pvalloc(0) should allocate one page. + size = PageSize; + } + return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); +} + +INTERCEPTOR(void, cfree, void *p) ALIAS("free"); + +#define OPERATOR_NEW_BODY \ + ENSURE_LSAN_INITED; \ + GET_STACK_TRACE; \ + return Allocate(stack, size, 1, kAlwaysClearMemory); + +INTERCEPTOR_ATTRIBUTE +void *operator new(uptr size) { OPERATOR_NEW_BODY; } +INTERCEPTOR_ATTRIBUTE +void *operator new[](uptr size) { OPERATOR_NEW_BODY; } +INTERCEPTOR_ATTRIBUTE +void *operator new(uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } +INTERCEPTOR_ATTRIBUTE +void *operator new[](uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } + +#define OPERATOR_DELETE_BODY \ + ENSURE_LSAN_INITED; \ + Deallocate(ptr); + +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr) { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr) { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const &) { + OPERATOR_DELETE_BODY; +} -extern "C" { -void cfree(void *p) ALIAS("free") SANITIZER_INTERFACE_ATTRIBUTE; -void *pvalloc(uptr size) ALIAS("valloc") - SANITIZER_INTERFACE_ATTRIBUTE; // We need this to intercept the __libc_memalign calls that are used to // allocate dynamic TLS space in ld-linux.so. -void *__libc_memalign(uptr alignment, uptr size) - ALIAS("memalign") SANITIZER_INTERFACE_ATTRIBUTE; -} +INTERCEPTOR(void *, __libc_memalign, uptr align, uptr s) ALIAS("memalign"); ///// Thread initialization and finalization. ///// @@ -166,9 +209,6 @@ struct ThreadParam { atomic_uintptr_t tid; }; -// PTHREAD_DESTRUCTOR_ITERATIONS from glibc. -const uptr kPthreadDestructorIterations = 4; - extern "C" void *__lsan_thread_start_func(void *arg) { ThreadParam *p = (ThreadParam*)arg; void* (*callback)(void *arg) = p->callback; @@ -191,13 +231,14 @@ extern "C" void *__lsan_thread_start_func(void *arg) { INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void *), void *param) { - Init(); + ENSURE_LSAN_INITED; + EnsureMainThreadIDIsCorrect(); __sanitizer_pthread_attr_t myattr; if (attr == 0) { pthread_attr_init(&myattr); attr = &myattr; } - AdjustStackSizeLinux(attr, 0); + AdjustStackSizeLinux(attr); int detached = 0; pthread_attr_getdetachstate(attr, &detached); ThreadParam p; @@ -218,7 +259,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, } INTERCEPTOR(int, pthread_join, void *th, void **ret) { - Init(); + ENSURE_LSAN_INITED; int tid = ThreadTid((uptr)th); int res = REAL(pthread_join)(th, ret); if (res == 0) @@ -231,12 +272,14 @@ namespace __lsan { void InitializeInterceptors() { INTERCEPT_FUNCTION(malloc); INTERCEPT_FUNCTION(free); + INTERCEPT_FUNCTION(cfree); INTERCEPT_FUNCTION(calloc); INTERCEPT_FUNCTION(realloc); INTERCEPT_FUNCTION(memalign); INTERCEPT_FUNCTION(posix_memalign); - INTERCEPT_FUNCTION(memalign); + INTERCEPT_FUNCTION(__libc_memalign); INTERCEPT_FUNCTION(valloc); + INTERCEPT_FUNCTION(pvalloc); INTERCEPT_FUNCTION(malloc_usable_size); INTERCEPT_FUNCTION(mallinfo); INTERCEPT_FUNCTION(mallopt); diff --git a/lib/lsan/lsan_preinit.cc b/lib/lsan/lsan_preinit.cc new file mode 100644 index 000000000000..e6639516dc81 --- /dev/null +++ b/lib/lsan/lsan_preinit.cc @@ -0,0 +1,26 @@ +//===-- lsan_preinit.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 LeakSanitizer. +// +// Call __lsan_init at the very early stage of process startup. +//===----------------------------------------------------------------------===// + +#include "lsan.h" + +#ifndef LSAN_USE_PREINIT_ARRAY +#define LSAN_USE_PREINIT_ARRAY 1 +#endif + +#if LSAN_USE_PREINIT_ARRAY && !defined(PIC) + // We force __lsan_init to be called before anyone else by placing it into + // .preinit_array section. + __attribute__((section(".preinit_array"), used)) + void (*__local_lsan_preinit)(void) = __lsan_init; +#endif diff --git a/lib/lsan/lsan_thread.cc b/lib/lsan/lsan_thread.cc index 3e28dee1325a..0f8efc093b56 100644 --- a/lib/lsan/lsan_thread.cc +++ b/lib/lsan/lsan_thread.cc @@ -123,6 +123,11 @@ void ThreadJoin(u32 tid) { thread_registry->JoinThread(tid, /* arg */0); } +void EnsureMainThreadIDIsCorrect() { + if (GetCurrentThread() == 0) + CurrentThreadContext()->os_id = GetTid(); +} + ///// Interface to the common LSan module. ///// bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, @@ -140,6 +145,10 @@ bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, return true; } +void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, + void *arg) { +} + void LockThreadRegistry() { thread_registry->Lock(); } diff --git a/lib/lsan/lsan_thread.h b/lib/lsan/lsan_thread.h index b62f04b8eb74..4641b32ed6e6 100644 --- a/lib/lsan/lsan_thread.h +++ b/lib/lsan/lsan_thread.h @@ -47,7 +47,7 @@ u32 ThreadTid(uptr uid); u32 GetCurrentThread(); void SetCurrentThread(u32 tid); ThreadContext *CurrentThreadContext(); - +void EnsureMainThreadIDIsCorrect(); } // namespace __lsan #endif // LSAN_THREAD_H diff --git a/lib/lsan/tests/CMakeLists.txt b/lib/lsan/tests/CMakeLists.txt index 3d97ae96f650..2221e0650237 100644 --- a/lib/lsan/tests/CMakeLists.txt +++ b/lib/lsan/tests/CMakeLists.txt @@ -9,13 +9,17 @@ set(LSAN_TESTS_SRC lsan_dummy_unittest.cc) set(LSAN_TESTS_CFLAGS - ${LSAN_CFLAGS} + ${SANITIZER_COMMON_CFLAGS} ${COMPILER_RT_GTEST_INCLUDE_CFLAGS} - -I${COMPILER_RT_SOURCE_DIR}/lib) + -I${COMPILER_RT_SOURCE_DIR}/lib + -I${LSAN_SRC_DIR}) -add_custom_target(LsanTests) -set_target_properties(LsanTests PROPERTIES - FOLDER "LSan unittests") +set(LSAN_TEST_LINK_FLAGS_COMMON + -lstdc++ -ldl -lpthread -lm) + +add_custom_target(LsanUnitTests) +set_target_properties(LsanUnitTests PROPERTIES + FOLDER "LSan unit tests") # Compile source for the given architecture, using compiler # options in ${ARGN}, and add it to the object list. @@ -25,24 +29,27 @@ macro(lsan_compile obj_list source arch) get_target_flags_for_arch(${arch} TARGET_CFLAGS) clang_compile(${output_obj} ${source} CFLAGS ${ARGN} ${TARGET_CFLAGS} - DEPS gtest) + DEPS gtest ${LSAN_RUNTIME_LIBRARIES}) list(APPEND ${obj_list} ${output_obj}) endmacro() -function(add_lsan_test testname arch) - set(testname_arch ${testname}-${arch}-Test) - get_target_flags_for_arch(${arch} TARGET_LINKFLAGS) - add_unittest(LsanTests ${testname_arch} ${ARGN}) - target_link_libraries(${testname_arch} "clang_rt.lsan-${arch}") - set_target_compile_flags(${testname_arch} ${LSAN_TESTS_CFLAGS}) - set_target_link_flags(${testname_arch} ${TARGET_LINKFLAGS}) +function(add_lsan_test test_suite test_name arch) + get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + add_compiler_rt_test(${test_suite} ${test_name} + OBJECTS ${ARGN} + DEPS ${LSAN_RUNTIME_LIBRARIES} ${ARGN} + LINK_FLAGS ${LSAN_TEST_LINK_FLAGS_COMMON} + ${TARGET_LINK_FLAGS}) endfunction() macro(add_lsan_tests_for_arch arch) set(LSAN_TESTS_OBJ) - lsan_compile(LSAN_TESTS_OBJ ${LSAN_TESTS_SRC} ${arch} ${LSAN_TESTS_CFLAGS} - -I${LSAN_SRC_DIR}) - add_lsan_test(Lsan ${arch} ${LSAN_TESTS_OBJ}) + set(LSAN_TEST_SOURCES ${LSAN_TESTS_SRC} + ${COMPILER_RT_GTEST_SOURCE}) + foreach(source ${LSAN_TEST_SOURCES}) + lsan_compile(LSAN_TESTS_OBJ ${source} ${arch} ${LSAN_TESTS_CFLAGS}) + endforeach() + add_lsan_test(LsanUnitTests Lsan-${arch}-Test ${arch} ${LSAN_TESTS_OBJ}) endmacro() # Build tests for 64-bit Linux only. diff --git a/lib/lsan/tests/lsan_dummy_unittest.cc b/lib/lsan/tests/lsan_dummy_unittest.cc index e69de29bb2d1..5468400775a0 100644 --- a/lib/lsan/tests/lsan_dummy_unittest.cc +++ b/lib/lsan/tests/lsan_dummy_unittest.cc @@ -0,0 +1,22 @@ +//===-- lsan_dummy_unittest.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 LeakSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "gtest/gtest.h" + +TEST(LeakSanitizer, EmptyTest) { + // Empty test to suppress LIT warnings about lack of tests. +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/lib/lsan/tests/lsan_testlib.cc b/lib/lsan/tests/lsan_testlib.cc index 363cc14f1941..8db6cf1e2f8c 100644 --- a/lib/lsan/tests/lsan_testlib.cc +++ b/lib/lsan/tests/lsan_testlib.cc @@ -14,12 +14,12 @@ /* Usage: clang++ ../sanitizer_common/sanitizer_*.cc ../interception/interception_*.cc \ lsan*.cc tests/lsan_testlib.cc -I. -I.. -g -ldl -lpthread -fPIC -shared -O2 \ - -o lsan.so + -DLSAN_USE_PREINIT_ARRAY=0 -o lsan.so LD_PRELOAD=./lsan.so /your/app */ #include "lsan.h" __attribute__((constructor)) void constructor() { - __lsan::Init(); + __lsan_init(); } diff --git a/lib/msan/CMakeLists.txt b/lib/msan/CMakeLists.txt index 0671b59c0025..06f3f65d8e38 100644 --- a/lib/msan/CMakeLists.txt +++ b/lib/msan/CMakeLists.txt @@ -25,13 +25,24 @@ if(CAN_TARGET_${arch}) $<TARGET_OBJECTS:RTInterception.${arch}> $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> - CFLAGS ${MSAN_RTL_CFLAGS} - SYMS msan.syms) + CFLAGS ${MSAN_RTL_CFLAGS}) list(APPEND MSAN_RUNTIME_LIBRARIES clang_rt.msan-${arch}) + if(UNIX) + add_sanitizer_rt_symbols(clang_rt.msan-${arch} msan.syms.extra) + list(APPEND MSAN_RUNTIME_LIBRARIES clang_rt.msan-${arch}-symbols) + endif() endif() add_compiler_rt_resource_file(msan_blacklist msan_blacklist.txt) +# We should only build MSan unit tests if we can build instrumented libcxx. +set(MSAN_LIBCXX_PATH ${LLVM_MAIN_SRC_DIR}/projects/libcxx) +if(EXISTS ${MSAN_LIBCXX_PATH}/) + set(MSAN_CAN_INSTRUMENT_LIBCXX TRUE) +else() + set(MSAN_CAN_INSTRUMENT_LIBCXX FALSE) +endif() + if(LLVM_INCLUDE_TESTS) add_subdirectory(tests) endif() diff --git a/lib/msan/lit_tests/CMakeLists.txt b/lib/msan/lit_tests/CMakeLists.txt index ed2da6b839f5..38d1e59e709e 100644 --- a/lib/msan/lit_tests/CMakeLists.txt +++ b/lib/msan/lit_tests/CMakeLists.txt @@ -3,24 +3,23 @@ set(MSAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..) configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg - ) + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg - ) +if(MSAN_CAN_INSTRUMENT_LIBCXX) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg) +endif() -if(COMPILER_RT_CAN_EXECUTE_TESTS) +if(COMPILER_RT_CAN_EXECUTE_TESTS AND CAN_TARGET_x86_64) # Run MSan tests only if we're sure we may produce working binaries. set(MSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS} ${MSAN_RUNTIME_LIBRARIES} msan_blacklist) set(MSAN_TEST_PARAMS - msan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg - ) - if(LLVM_INCLUDE_TESTS) + msan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + if(LLVM_INCLUDE_TESTS AND MSAN_CAN_INSTRUMENT_LIBCXX) list(APPEND MSAN_TEST_DEPS MsanUnitTests) endif() add_lit_testsuite(check-msan "Running the MemorySanitizer tests" diff --git a/lib/msan/lit_tests/Linux/glob.cc b/lib/msan/lit_tests/Linux/glob.cc index 513679c6d3d7..387ce3cf5f1a 100644 --- a/lib/msan/lit_tests/Linux/glob.cc +++ b/lib/msan/lit_tests/Linux/glob.cc @@ -1,4 +1,5 @@ // RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p 2>&1 | FileCheck %s // RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s #include <assert.h> diff --git a/lib/msan/lit_tests/Linux/glob_altdirfunc.cc b/lib/msan/lit_tests/Linux/glob_altdirfunc.cc new file mode 100644 index 000000000000..b8200c3ee899 --- /dev/null +++ b/lib/msan/lit_tests/Linux/glob_altdirfunc.cc @@ -0,0 +1,78 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <unistd.h> + +#include <sanitizer/msan_interface.h> + +static void my_gl_closedir(void *dir) { + if (!dir) + exit(1); + closedir((DIR *)dir); +} + +static struct dirent *my_gl_readdir(void *dir) { + if (!dir) + exit(1); + struct dirent *d = readdir((DIR *)dir); + if (d) __msan_poison(d, d->d_reclen); // hehe + return d; +} + +static void *my_gl_opendir(const char *s) { + assert(__msan_test_shadow(s, strlen(s) + 1) == (size_t)-1); + return opendir(s); +} + +static int my_gl_lstat(const char *s, struct stat *st) { + assert(__msan_test_shadow(s, strlen(s) + 1) == (size_t)-1); + if (!st) + exit(1); + return lstat(s, st); +} + +static int my_gl_stat(const char *s, struct stat *st) { + assert(__msan_test_shadow(s, strlen(s) + 1) == (size_t)-1); + if (!st) + exit(1); + return lstat(s, st); +} + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "glob_test_root/*a"); + + glob_t globbuf; + globbuf.gl_closedir = my_gl_closedir; + globbuf.gl_readdir = my_gl_readdir; + globbuf.gl_opendir = my_gl_opendir; + globbuf.gl_lstat = my_gl_lstat; + globbuf.gl_stat = my_gl_stat; + for (int i = 0; i < 10000; ++i) { + int res = glob(buf, GLOB_ALTDIRFUNC | GLOB_MARK, 0, &globbuf); + assert(res == 0); + printf("%d %s\n", errno, strerror(errno)); + assert(globbuf.gl_pathc == 2); + printf("%zu\n", strlen(globbuf.gl_pathv[0])); + printf("%zu\n", strlen(globbuf.gl_pathv[1])); + __msan_poison(globbuf.gl_pathv[0], strlen(globbuf.gl_pathv[0]) + 1); + __msan_poison(globbuf.gl_pathv[1], strlen(globbuf.gl_pathv[1]) + 1); + globfree(&globbuf); + } + + printf("PASS\n"); + // CHECK: PASS + return 0; +} diff --git a/lib/msan/lit_tests/Linux/glob_nomatch.cc b/lib/msan/lit_tests/Linux/glob_nomatch.cc new file mode 100644 index 000000000000..0262034aec5b --- /dev/null +++ b/lib/msan/lit_tests/Linux/glob_nomatch.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "glob_test_root/*c"); + + glob_t globbuf; + int res = glob(buf, 0, 0, &globbuf); + assert(res == GLOB_NOMATCH); + assert(globbuf.gl_pathc == 0); + if (globbuf.gl_pathv == 0) + exit(0); + return 0; +} diff --git a/lib/msan/lit_tests/Linux/syscalls.cc b/lib/msan/lit_tests/Linux/syscalls.cc index c12eda39189e..ec308bfe30ca 100644 --- a/lib/msan/lit_tests/Linux/syscalls.cc +++ b/lib/msan/lit_tests/Linux/syscalls.cc @@ -7,6 +7,10 @@ #include <stdio.h> #include <string.h> +#include <linux/aio_abi.h> +#include <sys/ptrace.h> +#include <sys/stat.h> + #include <sanitizer/linux_syscall_hooks.h> #include <sanitizer/msan_interface.h> @@ -16,6 +20,7 @@ int main(int argc, char *argv[]) { char buf[1000]; const int kTen = 10; + const int kFortyTwo = 42; memset(buf, 0, sizeof(buf)); __msan_unpoison(buf, sizeof(buf)); __sanitizer_syscall_pre_recvmsg(0, buf, 0); @@ -46,5 +51,50 @@ int main(int argc, char *argv[]) { __msan_poison(buf, kTen + 1); __sanitizer_syscall_post_getdents64(kTen, 0, buf, kTen); assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_clock_getres(0, 0, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(long) * 2); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_clock_gettime(0, 0, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(long) * 2); + + // Failed syscall does not write to the buffer. + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_clock_gettime(-1, 0, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == 0); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_read(5, 42, buf, 10); + assert(__msan_test_shadow(buf, sizeof(buf)) == 5); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_newfstatat(0, 5, "/path/to/file", buf, 0); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(struct stat)); + + __msan_poison(buf, sizeof(buf)); + int prio = 0; + __sanitizer_syscall_post_mq_timedreceive(kFortyTwo, 5, buf, sizeof(buf), &prio, 0); + assert(__msan_test_shadow(buf, sizeof(buf)) == kFortyTwo); + assert(__msan_test_shadow(&prio, sizeof(prio)) == -1); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_ptrace(0, PTRACE_PEEKUSER, kFortyTwo, 0xABCD, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(void *)); + + __msan_poison(buf, sizeof(buf)); + struct iocb iocb[2]; + struct iocb *iocbp[2] = { &iocb[0], &iocb[1] }; + memset(iocb, 0, sizeof(iocb)); + iocb[0].aio_lio_opcode = IOCB_CMD_PREAD; + iocb[0].aio_buf = (__u64)buf; + iocb[0].aio_nbytes = kFortyTwo; + iocb[1].aio_lio_opcode = IOCB_CMD_PREAD; + iocb[1].aio_buf = (__u64)(&buf[kFortyTwo]); + iocb[1].aio_nbytes = kFortyTwo; + __sanitizer_syscall_post_io_submit(1, 0, 2, &iocbp); + assert(__msan_test_shadow(buf, sizeof(buf)) == kFortyTwo); + return 0; } diff --git a/lib/msan/lit_tests/Linux/tcgetattr.cc b/lib/msan/lit_tests/Linux/tcgetattr.cc new file mode 100644 index 000000000000..e6e101db884f --- /dev/null +++ b/lib/msan/lit_tests/Linux/tcgetattr.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> +#include <termios.h> +#include <unistd.h> + +int main(int argc, char *argv[]) { + int fd = getpt(); + assert(fd >= 0); + + struct termios t; + int res = tcgetattr(fd, &t); + assert(!res); + + if (t.c_iflag == 0) + exit(0); + return 0; +} diff --git a/lib/msan/lit_tests/SharedLibs/dso-origin-so.cc b/lib/msan/lit_tests/SharedLibs/dso-origin-so.cc new file mode 100644 index 000000000000..8930a7159246 --- /dev/null +++ b/lib/msan/lit_tests/SharedLibs/dso-origin-so.cc @@ -0,0 +1,14 @@ +#include <stdlib.h> + +#include "dso-origin.h" + +void my_access(int *p) { + volatile int tmp; + // Force initialize-ness check. + if (*p) + tmp = 1; +} + +void *my_alloc(unsigned sz) { + return malloc(sz); +} diff --git a/lib/msan/lit_tests/SharedLibs/dso-origin.h b/lib/msan/lit_tests/SharedLibs/dso-origin.h new file mode 100644 index 000000000000..ff926b3f61c8 --- /dev/null +++ b/lib/msan/lit_tests/SharedLibs/dso-origin.h @@ -0,0 +1,4 @@ +extern "C" { +void my_access(int *p); +void *my_alloc(unsigned sz); +} diff --git a/lib/msan/lit_tests/SharedLibs/lit.local.cfg b/lib/msan/lit_tests/SharedLibs/lit.local.cfg new file mode 100644 index 000000000000..b3677c17a0f2 --- /dev/null +++ b/lib/msan/lit_tests/SharedLibs/lit.local.cfg @@ -0,0 +1,4 @@ +# Sources in this directory are compiled as shared libraries and used by +# tests in parent directory. + +config.suffixes = [] diff --git a/lib/msan/lit_tests/Unit/lit.cfg b/lib/msan/lit_tests/Unit/lit.cfg deleted file mode 100644 index ee379d0deaed..000000000000 --- a/lib/msan/lit_tests/Unit/lit.cfg +++ /dev/null @@ -1,26 +0,0 @@ -# -*- Python -*- - -import os - -def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if not attr_value: - lit.fatal("No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) - return attr_value - -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') -compiler_rt_lit_unit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.unit.cfg") -lit.load_config(config, compiler_rt_lit_unit_cfg) - -# Setup config name. -config.name = 'MemorySanitizer-Unit' - -# Setup test source and exec root. For unit tests, we define -# it as build directory with MSan unit tests. -msan_binary_dir = get_required_attr(config, "msan_binary_dir") -config.test_exec_root = os.path.join(msan_binary_dir, "tests") -config.test_source_root = config.test_exec_root diff --git a/lib/msan/lit_tests/Unit/lit.site.cfg.in b/lib/msan/lit_tests/Unit/lit.site.cfg.in index a91f6713303a..8e67f557d7fd 100644 --- a/lib/msan/lit_tests/Unit/lit.site.cfg.in +++ b/lib/msan/lit_tests/Unit/lit.site.cfg.in @@ -1,17 +1,13 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! -config.target_triple = "@TARGET_TRIPLE@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_build_mode = "@LLVM_BUILD_MODE@" -config.msan_binary_dir = "@MSAN_BINARY_DIR@" +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") -try: - config.llvm_build_mode = config.llvm_build_mode % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) +# Setup config name. +config.name = 'MemorySanitizer-Unit' -# Let the main config do the real work. -lit.load_config(config, "@MSAN_SOURCE_DIR@/lit_tests/Unit/lit.cfg") +# Setup test source and exec root. For unit tests, we define +# it as build directory with MSan unit tests. +config.test_exec_root = "@MSAN_BINARY_DIR@/tests" +config.test_source_root = config.test_exec_root diff --git a/lib/msan/lit_tests/allocator_returns_null.cc b/lib/msan/lit_tests/allocator_returns_null.cc new file mode 100644 index 000000000000..aaa85cce7113 --- /dev/null +++ b/lib/msan/lit_tests/allocator_returns_null.cc @@ -0,0 +1,81 @@ +// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// By default (allocator_may_return_null=0) the process should crash. +// With allocator_may_return_null=1 the allocator should return 0. +// +// RUN: %clangxx_msan -O0 %s -o %t +// RUN: not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <limits> +int main(int argc, char **argv) { + volatile size_t size = std::numeric_limits<size_t>::max() - 10000; + assert(argc == 2); + char *x = 0; + if (!strcmp(argv[1], "malloc")) { + fprintf(stderr, "malloc:\n"); + x = (char*)malloc(size); + } + if (!strcmp(argv[1], "calloc")) { + fprintf(stderr, "calloc:\n"); + x = (char*)calloc(size / 4, 4); + } + + if (!strcmp(argv[1], "calloc-overflow")) { + fprintf(stderr, "calloc-overflow:\n"); + volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = (char*)calloc(kArraySize, kArraySize2); + } + + if (!strcmp(argv[1], "realloc")) { + fprintf(stderr, "realloc:\n"); + x = (char*)realloc(0, size); + } + if (!strcmp(argv[1], "realloc-after-malloc")) { + fprintf(stderr, "realloc-after-malloc:\n"); + char *t = (char*)malloc(100); + *t = 42; + x = (char*)realloc(t, size); + assert(*t == 42); + } + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "x: %lx\n", (long)x); + return x != 0; +} +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: MemorySanitizer's allocator is terminating the process + +// CHECK-mNULL: malloc: +// CHECK-mNULL: x: 0 +// CHECK-cNULL: calloc: +// CHECK-cNULL: x: 0 +// CHECK-coNULL: calloc-overflow: +// CHECK-coNULL: x: 0 +// CHECK-rNULL: realloc: +// CHECK-rNULL: x: 0 +// CHECK-mrNULL: realloc-after-malloc: +// CHECK-mrNULL: x: 0 diff --git a/lib/msan/lit_tests/backtrace.cc b/lib/msan/lit_tests/backtrace.cc new file mode 100644 index 000000000000..48684c29c60d --- /dev/null +++ b/lib/msan/lit_tests/backtrace.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include <assert.h> +#include <execinfo.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +__attribute__((noinline)) +void f() { + void *buf[10]; + int sz = backtrace(buf, sizeof(buf) / sizeof(*buf)); + assert(sz > 0); + for (int i = 0; i < sz; ++i) + if (!buf[i]) + exit(1); + char **s = backtrace_symbols(buf, sz); + assert(s > 0); + for (int i = 0; i < sz; ++i) + printf("%d\n", strlen(s[i])); +} + +int main(void) { + f(); + return 0; +} diff --git a/lib/msan/lit_tests/cxa_atexit.cc b/lib/msan/lit_tests/cxa_atexit.cc new file mode 100644 index 000000000000..f3641aadce03 --- /dev/null +++ b/lib/msan/lit_tests/cxa_atexit.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p + +// PR17377: C++ module destructors get stale argument shadow. + +#include <stdio.h> +#include <stdlib.h> +class A { +public: + // This destructor get stale argument shadow left from the call to f(). + ~A() { + if (this) + exit(0); + } +}; + +A a; + +__attribute__((noinline)) +void f(long x) { +} + +int main(void) { + long x; + long * volatile p = &x; + // This call poisons TLS shadow for the first function argument. + f(*p); + return 0; +} diff --git a/lib/msan/lit_tests/dlerror.cc b/lib/msan/lit_tests/dlerror.cc new file mode 100644 index 000000000000..281b3164fd7e --- /dev/null +++ b/lib/msan/lit_tests/dlerror.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> + +int main(void) { + void *p = dlopen("/bad/file/name", RTLD_NOW); + assert(!p); + char *s = dlerror(); + printf("%s, %zu\n", s, strlen(s)); + return 0; +} diff --git a/lib/msan/lit_tests/dso-origin.cc b/lib/msan/lit_tests/dso-origin.cc new file mode 100644 index 000000000000..13661c65e744 --- /dev/null +++ b/lib/msan/lit_tests/dso-origin.cc @@ -0,0 +1,25 @@ +// Build a library with origin tracking and an executable w/o origin tracking. +// Test that origin tracking is enabled at runtime. +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %p/SharedLibs/dso-origin-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_msan -m64 -O0 %s %t-so.so -o %t && not %t 2>&1 | FileCheck %s + +#include <stdlib.h> + +#include "SharedLibs/dso-origin.h" + +int main(int argc, char **argv) { + int *x = (int *)my_alloc(sizeof(int)); + my_access(x); + delete x; + + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in my_access .*dso-origin-so.cc:}} + // CHECK: {{#1 0x.* in main .*dso-origin.cc:}}[[@LINE-5]] + // CHECK: Uninitialized value was created by a heap allocation + // CHECK: {{#0 0x.* in .*malloc}} + // CHECK: {{#1 0x.* in my_alloc .*dso-origin-so.cc:}} + // CHECK: {{#2 0x.* in main .*dso-origin.cc:}}[[@LINE-10]] + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*dso-origin-so.cc:.* my_access}} + return 0; +} diff --git a/lib/msan/lit_tests/errno.cc b/lib/msan/lit_tests/errno.cc new file mode 100644 index 000000000000..af27ad0b0329 --- /dev/null +++ b/lib/msan/lit_tests/errno.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> + +int main() +{ + int x; + int *volatile p = &x; + errno = *p; + int res = read(-1, 0, 0); + assert(res == -1); + if (errno) printf("errno %d\n", errno); + return 0; +} diff --git a/lib/msan/lit_tests/getaddrinfo-positive.cc b/lib/msan/lit_tests/getaddrinfo-positive.cc index f16679cc2aa2..7fde1fdfab93 100644 --- a/lib/msan/lit_tests/getaddrinfo-positive.cc +++ b/lib/msan/lit_tests/getaddrinfo-positive.cc @@ -8,12 +8,16 @@ #include <netdb.h> #include <stdlib.h> +volatile int z; + int main(void) { struct addrinfo *ai; struct addrinfo hint; - int res = getaddrinfo("localhost", NULL, &hint, &ai); + int res = getaddrinfo("localhost", NULL, NULL, &ai); + if (ai) z = 1; // OK + res = getaddrinfo("localhost", NULL, &hint, &ai); // CHECK: UMR in __interceptor_getaddrinfo at offset 0 inside - // CHECK: WARNING: Use of uninitialized value + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value // CHECK: #0 {{.*}} in main {{.*}}getaddrinfo-positive.cc:[[@LINE-3]] return 0; } diff --git a/lib/msan/lit_tests/getline.cc b/lib/msan/lit_tests/getline.cc new file mode 100644 index 000000000000..27168a885606 --- /dev/null +++ b/lib/msan/lit_tests/getline.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_msan -O0 %s -o %t && %t %p + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "getline_test_data"); + + FILE *fp = fopen(buf, "r"); + assert(fp); + + char *line = 0; + size_t len = 0; + int n = getline(&line, &len, fp); + assert(n == 6); + assert(strcmp(line, "abcde\n") == 0); + + n = getline(&line, &len, fp); + assert(n == 6); + assert(strcmp(line, "12345\n") == 0); + + free(line); + fclose(fp); + + return 0; +} diff --git a/lib/msan/lit_tests/getline_test_data b/lib/msan/lit_tests/getline_test_data new file mode 100644 index 000000000000..5ba1d4cec0dd --- /dev/null +++ b/lib/msan/lit_tests/getline_test_data @@ -0,0 +1,2 @@ +abcde +12345 diff --git a/lib/msan/lit_tests/heap-origin.cc b/lib/msan/lit_tests/heap-origin.cc index 54e2c31438ff..dfe7edd27e82 100644 --- a/lib/msan/lit_tests/heap-origin.cc +++ b/lib/msan/lit_tests/heap-origin.cc @@ -19,15 +19,13 @@ #include <stdlib.h> int main(int argc, char **argv) { char *volatile x = (char*)malloc(5 * sizeof(char)); - if (*x) - exit(0); - // CHECK: WARNING: Use of uninitialized value - // CHECK: {{#0 0x.* in main .*heap-origin.cc:}}[[@LINE-3]] + return *x; + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*heap-origin.cc:}}[[@LINE-2]] // CHECK-ORIGINS: Uninitialized value was created by a heap allocation // CHECK-ORIGINS: {{#0 0x.* in .*malloc}} - // CHECK-ORIGINS: {{#1 0x.* in main .*heap-origin.cc:}}[[@LINE-8]] + // CHECK-ORIGINS: {{#1 0x.* in main .*heap-origin.cc:}}[[@LINE-7]] // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*heap-origin.cc:.* main}} - return 0; } diff --git a/lib/msan/lit_tests/initgroups.cc b/lib/msan/lit_tests/initgroups.cc new file mode 100644 index 000000000000..adba5369579a --- /dev/null +++ b/lib/msan/lit_tests/initgroups.cc @@ -0,0 +1,11 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include <sys/types.h> +#include <grp.h> + +int main(void) { + initgroups("root", 0); + // The above fails unless you are root. Does not matter, MSan false positive + // (which we are testing for) happens anyway. + return 0; +} diff --git a/lib/msan/lit_tests/inline.cc b/lib/msan/lit_tests/inline.cc new file mode 100644 index 000000000000..4aeb15583f84 --- /dev/null +++ b/lib/msan/lit_tests/inline.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_msan -O3 %s -o %t && %t + +// Test that no_sanitize_memory attribute applies even when the function would +// be normally inlined. + +#include <stdlib.h> + +__attribute__((no_sanitize_memory)) +int f(int *p) { + if (*p) // BOOOM?? Nope! + exit(0); + return 0; +} + +int main(int argc, char **argv) { + int x; + int * volatile p = &x; + int res = f(p); + return 0; +} diff --git a/lib/msan/lit_tests/insertvalue_origin.cc b/lib/msan/lit_tests/insertvalue_origin.cc new file mode 100644 index 000000000000..769ea45f8c4d --- /dev/null +++ b/lib/msan/lit_tests/insertvalue_origin.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out + +// Test origin propagation through insertvalue IR instruction. + +#include <stdio.h> +#include <stdint.h> + +struct mypair { + int64_t x; + int y; +}; + +mypair my_make_pair(int64_t x, int y) { + mypair p; + p.x = x; + p.y = y; + return p; +} + +int main() { + int64_t * volatile p = new int64_t; + mypair z = my_make_pair(*p, 0); + if (z.x) + printf("zzz\n"); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{in main .*insertvalue_origin.cc:}}[[@LINE-3]] + + // CHECK: Uninitialized value was created by a heap allocation + // CHECK: {{in main .*insertvalue_origin.cc:}}[[@LINE-8]] + delete p; + return 0; +} diff --git a/lib/msan/lit_tests/ioctl.cc b/lib/msan/lit_tests/ioctl.cc new file mode 100644 index 000000000000..caff80c2e5d7 --- /dev/null +++ b/lib/msan/lit_tests/ioctl.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %t +// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %t + +#include <assert.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <unistd.h> + +int main(int argc, char **argv) { + int fd = socket(AF_INET, SOCK_DGRAM, 0); + + unsigned int z; + int res = ioctl(fd, FIOGETOWN, &z); + assert(res == 0); + close(fd); + if (z) + exit(0); + return 0; +} diff --git a/lib/msan/lit_tests/ioctl_custom.cc b/lib/msan/lit_tests/ioctl_custom.cc new file mode 100644 index 000000000000..94ed528c70b9 --- /dev/null +++ b/lib/msan/lit_tests/ioctl_custom.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %t +// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %t + +// RUN: %clangxx_msan -DPOSITIVE -m64 -O0 -g %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -DPOSITIVE -m64 -O3 -g %s -o %t && not %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdlib.h> +#include <net/if.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <unistd.h> + +int main(int argc, char **argv) { + int fd = socket(AF_INET, SOCK_STREAM, 0); + + struct ifreq ifreqs[20]; + struct ifconf ifc; + ifc.ifc_ifcu.ifcu_req = ifreqs; +#ifndef POSITIVE + ifc.ifc_len = sizeof(ifreqs); +#endif + int res = ioctl(fd, SIOCGIFCONF, (void *)&ifc); + // CHECK: UMR in ioctl{{.*}} at offset 0 + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #{{.*}} in main {{.*}}ioctl_custom.cc:[[@LINE-3]] + assert(res == 0); + for (int i = 0; i < ifc.ifc_len / sizeof(*ifc.ifc_ifcu.ifcu_req); ++i) + printf("%d %zu %s\n", i, strlen(ifreqs[i].ifr_name), ifreqs[i].ifr_name); + return 0; +} diff --git a/lib/msan/lit_tests/keep-going-dso.cc b/lib/msan/lit_tests/keep-going-dso.cc new file mode 100644 index 000000000000..6d006756a110 --- /dev/null +++ b/lib/msan/lit_tests/keep-going-dso.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out + +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out + +// Test how -mllvm -msan-keep-going and MSAN_OPTIONS=keep_going affect reports +// from interceptors. +// -mllvm -msan-keep-going provides the default value of keep_going flag, but is +// always overwritten by MSAN_OPTIONS + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + char *volatile x = (char*)malloc(5 * sizeof(char)); + x[4] = 0; + if (strlen(x) < 3) + exit(0); + fprintf(stderr, "Done\n"); + // CHECK-NOT: Done + // CHECK-KEEP-GOING: Done + return 0; +} diff --git a/lib/msan/lit_tests/keep-going.cc b/lib/msan/lit_tests/keep-going.cc new file mode 100644 index 000000000000..e33b137c76f7 --- /dev/null +++ b/lib/msan/lit_tests/keep-going.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %t >%t.out 2>&1 +// FileCheck %s <%t.out + +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=halt_on_error=1 not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=halt_on_error=0 not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out + +// Test behaviour of -mllvm -msan-keep-going and MSAN_OPTIONS=keep_going. +// -mllvm -msan-keep-going provides the default value of keep_going flag; value +// of 1 can be overwritten by MSAN_OPTIONS, value of 0 can not. + +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char **argv) { + char *volatile x = (char*)malloc(5 * sizeof(char)); + if (x[0]) + exit(0); + fprintf(stderr, "Done\n"); + // CHECK-NOT: Done + // CHECK-KEEP-GOING: Done + return 0; +} diff --git a/lib/msan/lit_tests/lit.cfg b/lib/msan/lit_tests/lit.cfg index 42381885fe8e..da1bde6dd04a 100644 --- a/lib/msan/lit_tests/lit.cfg +++ b/lib/msan/lit_tests/lit.cfg @@ -2,12 +2,15 @@ import os +import lit.util + def get_required_attr(config, attr_name): attr_value = getattr(config, attr_name, None) if not attr_value: - lit.fatal("No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) return attr_value # Setup config name. @@ -17,9 +20,9 @@ config.name = 'MemorySanitizer' config.test_source_root = os.path.dirname(__file__) def DisplayNoConfigMessage(): - lit.fatal("No site specific configuration available! " + - "Try running your test from the build tree or running " + - "make check-msan") + lit_config.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-msan") # Figure out LLVM source root. llvm_src_root = getattr(config, 'llvm_src_root', None) @@ -27,9 +30,9 @@ if llvm_src_root is None: # We probably haven't loaded the site-specific configuration: the user # is likely trying to run a test file directly, and the site configuration # wasn't created by the build system. - msan_site_cfg = lit.params.get('msan_site_config', None) + msan_site_cfg = lit_config.params.get('msan_site_config', None) if (msan_site_cfg) and (os.path.exists(msan_site_cfg)): - lit.load_config(config, msan_site_cfg) + lit_config.load_config(config, msan_site_cfg) raise SystemExit # Try to guess the location of site-specific configuration using llvm-config @@ -45,25 +48,17 @@ if llvm_src_root is None: if (not msan_site_cfg) or (not os.path.exists(msan_site_cfg)): DisplayNoConfigMessage() - lit.load_config(config, msan_site_cfg) + lit_config.load_config(config, msan_site_cfg) raise SystemExit -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, "compiler_rt_src_root") -compiler_rt_lit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.cfg") -if (not compiler_rt_lit_cfg) or (not os.path.exists(compiler_rt_lit_cfg)): - lit.fatal("Can't find common compiler-rt lit config at: %r" - % compiler_rt_lit_cfg) -lit.load_config(config, compiler_rt_lit_cfg) - # Setup default compiler flags used with -fsanitize=memory option. clang_msan_cflags = ["-fsanitize=memory", "-mno-omit-leaf-frame-pointer", "-fno-omit-frame-pointer", "-fno-optimize-sibling-calls", - "-g"] -clang_msan_cxxflags = ["-ccc-cxx "] + clang_msan_cflags + "-g", + "-m64"] +clang_msan_cxxflags = ["--driver-mode=g++ "] + clang_msan_cflags config.substitutions.append( ("%clang_msan ", " ".join([config.clang] + clang_msan_cflags) + " ") ) @@ -71,12 +66,6 @@ config.substitutions.append( ("%clangxx_msan ", " ".join([config.clang] + clang_msan_cxxflags) + " ") ) -# Setup path to external LLVM symbolizer to run MemorySanitizer output tests. -llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) -if llvm_tools_dir: - llvm_symbolizer_path = os.path.join(llvm_tools_dir, "llvm-symbolizer") - config.environment['MSAN_SYMBOLIZER_PATH'] = llvm_symbolizer_path - # Default test suffixes. config.suffixes = ['.c', '.cc', '.cpp'] diff --git a/lib/msan/lit_tests/lit.site.cfg.in b/lib/msan/lit_tests/lit.site.cfg.in index 3b969e0b0614..946df778f3d3 100644 --- a/lib/msan/lit_tests/lit.site.cfg.in +++ b/lib/msan/lit_tests/lit.site.cfg.in @@ -1,18 +1,5 @@ -config.target_triple = "@TARGET_TRIPLE@" -config.host_os = "@HOST_OS@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_obj_root = "@LLVM_BINARY_DIR@" -config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" -config.clang = "@LLVM_BINARY_DIR@/bin/clang" +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") -# LLVM tools dir can be passed in lit parameters, so try to -# apply substitution. -try: - config.llvm_tools_dir = config.llvm_tools_dir % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) - -# Let the main config do the real work. -lit.load_config(config, "@MSAN_SOURCE_DIR@/lit_tests/lit.cfg") +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@MSAN_SOURCE_DIR@/lit_tests/lit.cfg") diff --git a/lib/msan/lit_tests/malloc_hook.cc b/lib/msan/lit_tests/malloc_hook.cc new file mode 100644 index 000000000000..fc68fbc35fbb --- /dev/null +++ b/lib/msan/lit_tests/malloc_hook.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_msan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <unistd.h> + +extern "C" { +int __msan_get_ownership(const void *p); + +void *global_ptr; + +// Note: avoid calling functions that allocate memory in malloc/free +// to avoid infinite recursion. +void __msan_malloc_hook(void *ptr, size_t sz) { + if (__msan_get_ownership(ptr)) { + write(1, "MallocHook\n", sizeof("MallocHook\n")); + global_ptr = ptr; + } +} +void __msan_free_hook(void *ptr) { + if (__msan_get_ownership(ptr) && ptr == global_ptr) + write(1, "FreeHook\n", sizeof("FreeHook\n")); +} +} // extern "C" + +int main() { + volatile int *x = new int; + // CHECK: MallocHook + // Check that malloc hook was called with correct argument. + if (global_ptr != (void*)x) { + _exit(1); + } + *x = 0; + delete x; + // CHECK: FreeHook + return 0; +} diff --git a/lib/msan/lit_tests/no_sanitize_memory_prop.cc b/lib/msan/lit_tests/no_sanitize_memory_prop.cc index c74ca6b89db9..355152478852 100644 --- a/lib/msan/lit_tests/no_sanitize_memory_prop.cc +++ b/lib/msan/lit_tests/no_sanitize_memory_prop.cc @@ -25,7 +25,7 @@ int main(void) { int x; int * volatile p = &x; int y = f(*p); - // CHECK: WARNING: Use of uninitialized value + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value // CHECK: {{#0 0x.* in main .*no_sanitize_memory_prop.cc:}}[[@LINE+1]] if (y) exit(0); diff --git a/lib/msan/lit_tests/poison_in_free.cc b/lib/msan/lit_tests/poison_in_free.cc new file mode 100644 index 000000000000..f134d05abb1e --- /dev/null +++ b/lib/msan/lit_tests/poison_in_free.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -O0 %s -o %t && not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -O0 %s -o %t && MSAN_OPTIONS=poison_in_free=0 %t >%t.out 2>&1 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + char *volatile x = (char*)malloc(50 * sizeof(char)); + memset(x, 0, 50); + free(x); + return x[25]; + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: #0 {{.*}} in main{{.*}}poison_in_free.cc:[[@LINE-2]] +} diff --git a/lib/msan/lit_tests/ptrace.cc b/lib/msan/lit_tests/ptrace.cc new file mode 100644 index 000000000000..d0e83eabd6a4 --- /dev/null +++ b/lib/msan/lit_tests/ptrace.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include <assert.h> +#include <stdio.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <sys/user.h> +#include <sys/wait.h> +#include <unistd.h> + +int main(void) { + pid_t pid; + pid = fork(); + if (pid == 0) { // child + ptrace(PTRACE_TRACEME, 0, NULL, NULL); + execl("/bin/true", "true", NULL); + } else { + wait(NULL); + user_regs_struct regs; + int res; + res = ptrace(PTRACE_GETREGS, pid, NULL, ®s); + assert(!res); + if (regs.rip) + printf("%zx\n", regs.rip); + + user_fpregs_struct fpregs; + res = ptrace(PTRACE_GETFPREGS, pid, NULL, &fpregs); + assert(!res); + if (fpregs.mxcsr) + printf("%x\n", fpregs.mxcsr); + + ptrace(PTRACE_CONT, pid, NULL, NULL); + wait(NULL); + } + return 0; +} diff --git a/lib/msan/lit_tests/scandir.cc b/lib/msan/lit_tests/scandir.cc new file mode 100644 index 000000000000..94672e1adbee --- /dev/null +++ b/lib/msan/lit_tests/scandir.cc @@ -0,0 +1,56 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <unistd.h> + +#include <sanitizer/msan_interface.h> + + +static int my_filter(const struct dirent *a) { + assert(__msan_test_shadow(&a, sizeof(a)) == (size_t)-1); + printf("%s\n", a->d_name); + __msan_print_shadow(a, a->d_reclen); + assert(__msan_test_shadow(a, a->d_reclen) == (size_t)-1); + printf("%s\n", a->d_name); + return strlen(a->d_name) == 3 && a->d_name[2] == 'b'; +} + +static int my_compar(const struct dirent **a, const struct dirent **b) { + assert(__msan_test_shadow(a, sizeof(*a)) == (size_t)-1); + assert(__msan_test_shadow(*a, (*a)->d_reclen) == (size_t)-1); + assert(__msan_test_shadow(b, sizeof(*b)) == (size_t)-1); + assert(__msan_test_shadow(*b, (*b)->d_reclen) == (size_t)-1); + if ((*a)->d_name[1] == (*b)->d_name[1]) + return 0; + return ((*a)->d_name[1] < (*b)->d_name[1]) ? 1 : -1; +} + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "scandir_test_root/"); + + struct dirent **d; + int res = scandir(buf, &d, my_filter, my_compar); + assert(res == 2); + assert(__msan_test_shadow(&d, sizeof(*d)) == (size_t)-1); + for (int i = 0; i < res; ++i) { + assert(__msan_test_shadow(&d[i], sizeof(d[i])) == (size_t)-1); + assert(__msan_test_shadow(d[i], d[i]->d_reclen) == (size_t)-1); + } + + assert(strcmp(d[0]->d_name, "bbb") == 0); + assert(strcmp(d[1]->d_name, "aab") == 0); + return 0; +} diff --git a/lib/msan/lit_tests/scandir_null.cc b/lib/msan/lit_tests/scandir_null.cc new file mode 100644 index 000000000000..84af7f418d21 --- /dev/null +++ b/lib/msan/lit_tests/scandir_null.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <unistd.h> + +#include <sanitizer/msan_interface.h> + + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "scandir_test_root/"); + + struct dirent **d; + int res = scandir(buf, &d, NULL, NULL); + assert(res >= 3); + assert(__msan_test_shadow(&d, sizeof(*d)) == (size_t)-1); + for (int i = 0; i < res; ++i) { + assert(__msan_test_shadow(&d[i], sizeof(d[i])) == (size_t)-1); + assert(__msan_test_shadow(d[i], d[i]->d_reclen) == (size_t)-1); + } + return 0; +} diff --git a/lib/msan/lit_tests/scandir_test_root/aaa b/lib/msan/lit_tests/scandir_test_root/aaa new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/lib/msan/lit_tests/scandir_test_root/aaa diff --git a/lib/msan/lit_tests/scandir_test_root/aab b/lib/msan/lit_tests/scandir_test_root/aab new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/lib/msan/lit_tests/scandir_test_root/aab diff --git a/lib/msan/lit_tests/scandir_test_root/bbb b/lib/msan/lit_tests/scandir_test_root/bbb new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/lib/msan/lit_tests/scandir_test_root/bbb diff --git a/lib/msan/lit_tests/select.cc b/lib/msan/lit_tests/select.cc new file mode 100644 index 000000000000..a169a2dd9118 --- /dev/null +++ b/lib/msan/lit_tests/select.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdlib.h> +int main(int argc, char **argv) { + int x; + int *volatile p = &x; + int z = *p ? 1 : 0; + if (z) + exit(0); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*select.cc:}}[[@LINE-3]] + + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*select.cc:.* main}} + return 0; +} diff --git a/lib/msan/lit_tests/setlocale.cc b/lib/msan/lit_tests/setlocale.cc new file mode 100644 index 000000000000..a22b744d74db --- /dev/null +++ b/lib/msan/lit_tests/setlocale.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include <assert.h> +#include <locale.h> +#include <stdlib.h> + +int main(void) { + char *locale = setlocale (LC_ALL, ""); + assert(locale); + if (locale[0]) + exit(0); + return 0; +} diff --git a/lib/msan/lit_tests/signal_stress_test.cc b/lib/msan/lit_tests/signal_stress_test.cc new file mode 100644 index 000000000000..ea75eae1bdaa --- /dev/null +++ b/lib/msan/lit_tests/signal_stress_test.cc @@ -0,0 +1,71 @@ +// RUN: %clangxx_msan -std=c++11 -O0 %s -o %t && %t + +// Test that va_arg shadow from a signal handler does not leak outside. + +#include <signal.h> +#include <stdarg.h> +#include <sanitizer/msan_interface.h> +#include <assert.h> +#include <sys/time.h> +#include <stdio.h> + +const int kSigCnt = 200; + +void f(bool poisoned, int n, ...) { + va_list vl; + va_start(vl, n); + for (int i = 0; i < n; ++i) { + void *p = va_arg(vl, void *); + if (!poisoned) + assert(__msan_test_shadow(&p, sizeof(p)) == -1); + } + va_end(vl); +} + +int sigcnt; + +void SignalHandler(int signo) { + assert(signo == SIGPROF); + void *p; + void **volatile q = &p; + f(true, 10, + *q, *q, *q, *q, *q, + *q, *q, *q, *q, *q); + ++sigcnt; +} + +int main() { + signal(SIGPROF, SignalHandler); + + itimerval itv; + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 100; + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = 100; + setitimer(ITIMER_PROF, &itv, NULL); + + void *p; + void **volatile q = &p; + + do { + f(false, 20, + nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + f(true, 20, + *q, *q, *q, *q, *q, + *q, *q, *q, *q, *q, + *q, *q, *q, *q, *q, + *q, *q, *q, *q, *q); + } while (sigcnt < kSigCnt); + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = 0; + setitimer(ITIMER_PROF, &itv, NULL); + + signal(SIGPROF, SIG_DFL); + return 0; +} diff --git a/lib/msan/lit_tests/sigwait.cc b/lib/msan/lit_tests/sigwait.cc new file mode 100644 index 000000000000..29aa86c938f2 --- /dev/null +++ b/lib/msan/lit_tests/sigwait.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_msan -std=c++11 -O0 -g %s -o %t && %t + +#include <assert.h> +#include <sanitizer/msan_interface.h> +#include <signal.h> +#include <sys/time.h> +#include <unistd.h> + +void test_sigwait() { + sigset_t s; + sigemptyset(&s); + sigaddset(&s, SIGUSR1); + sigprocmask(SIG_BLOCK, &s, 0); + + if (pid_t pid = fork()) { + kill(pid, SIGUSR1); + _exit(0); + } else { + int sig; + int res = sigwait(&s, &sig); + assert(!res); + // The following checks that sig is initialized. + assert(sig == SIGUSR1); + } +} + +int main(void) { + test_sigwait(); + return 0; +} diff --git a/lib/msan/lit_tests/sigwaitinfo.cc b/lib/msan/lit_tests/sigwaitinfo.cc new file mode 100644 index 000000000000..d4f004598a62 --- /dev/null +++ b/lib/msan/lit_tests/sigwaitinfo.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_msan -std=c++11 -O0 -g %s -o %t && %t + +#include <assert.h> +#include <sanitizer/msan_interface.h> +#include <signal.h> +#include <sys/time.h> +#include <unistd.h> + +void test_sigwaitinfo() { + sigset_t s; + sigemptyset(&s); + sigaddset(&s, SIGUSR1); + sigprocmask(SIG_BLOCK, &s, 0); + + if (pid_t pid = fork()) { + kill(pid, SIGUSR1); + _exit(0); + } else { + siginfo_t info; + int res = sigwaitinfo(&s, &info); + assert(!res); + // The following checks that sig is initialized. + assert(info.si_signo == SIGUSR1); + assert(-1 == __msan_test_shadow(&info, sizeof(info))); + } +} + +int main(void) { + test_sigwaitinfo(); + return 0; +} diff --git a/lib/msan/lit_tests/stack-origin.cc b/lib/msan/lit_tests/stack-origin.cc index 90f527309224..b0b05d9658bf 100644 --- a/lib/msan/lit_tests/stack-origin.cc +++ b/lib/msan/lit_tests/stack-origin.cc @@ -20,13 +20,12 @@ int main(int argc, char **argv) { int x; int *volatile p = &x; - if (*p) - exit(0); - // CHECK: WARNING: Use of uninitialized value - // CHECK: {{#0 0x.* in main .*stack-origin.cc:}}[[@LINE-3]] + return *p; + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*stack-origin.cc:}}[[@LINE-2]] // CHECK-ORIGINS: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' + // CHECK-ORIGINS: {{#0 0x.* in main .*stack-origin.cc:}}[[@LINE-8]] // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*stack-origin.cc:.* main}} - return 0; } diff --git a/lib/msan/lit_tests/sync_lock_set_and_test.cc b/lib/msan/lit_tests/sync_lock_set_and_test.cc new file mode 100644 index 000000000000..1023b3e54368 --- /dev/null +++ b/lib/msan/lit_tests/sync_lock_set_and_test.cc @@ -0,0 +1,7 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +int main(void) { + int i; + __sync_lock_test_and_set(&i, 0); + return i; +} diff --git a/lib/msan/lit_tests/tzset.cc b/lib/msan/lit_tests/tzset.cc new file mode 100644 index 000000000000..7e1c2cfad566 --- /dev/null +++ b/lib/msan/lit_tests/tzset.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +extern char *tzname[2]; + +int main(void) { + if (!strlen(tzname[0]) || !strlen(tzname[1])) + exit(1); + tzset(); + if (!strlen(tzname[0]) || !strlen(tzname[1])) + exit(1); + return 0; +} diff --git a/lib/msan/lit_tests/unaligned_read_origin.cc b/lib/msan/lit_tests/unaligned_read_origin.cc new file mode 100644 index 000000000000..fa29ab69de1b --- /dev/null +++ b/lib/msan/lit_tests/unaligned_read_origin.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out + +#include <sanitizer/msan_interface.h> + +int main(int argc, char **argv) { + int x; + int *volatile p = &x; + return __sanitizer_unaligned_load32(p); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*unaligned_read_origin.cc:}}[[@LINE-2]] + // CHECK: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' + // CHECK: {{#0 0x.* in main .*unaligned_read_origin.cc:}}[[@LINE-7]] +} diff --git a/lib/msan/lit_tests/use-after-free.cc b/lib/msan/lit_tests/use-after-free.cc new file mode 100644 index 000000000000..ac47c0233a10 --- /dev/null +++ b/lib/msan/lit_tests/use-after-free.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include <stdlib.h> +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + *p = 42; + free(p); + + if (*p) + exit(0); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*use-after-free.cc:}}[[@LINE-3]] + + // CHECK-ORIGINS: Uninitialized value was created by a heap allocation + // CHECK-ORIGINS: {{#0 0x.* in .*free}} + // CHECK-ORIGINS: {{#1 0x.* in main .*use-after-free.cc:}}[[@LINE-9]] + return 0; +} diff --git a/lib/msan/lit_tests/vector_cvt.cc b/lib/msan/lit_tests/vector_cvt.cc new file mode 100644 index 000000000000..c200c77de96a --- /dev/null +++ b/lib/msan/lit_tests/vector_cvt.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t +// RUN: %clangxx_msan -DPOSITIVE -m64 -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <emmintrin.h> + +int to_int(double v) { + __m128d t = _mm_set_sd(v); + int x = _mm_cvtsd_si32(t); + return x; + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #{{.*}} in to_int{{.*}}vector_cvt.cc:[[@LINE-4]] +} + +int main() { +#ifdef POSITIVE + double v; +#else + double v = 1.1; +#endif + double* volatile p = &v; + int x = to_int(*p); + return !x; +} diff --git a/lib/msan/lit_tests/vector_select.cc b/lib/msan/lit_tests/vector_select.cc new file mode 100644 index 000000000000..e8d55423293c --- /dev/null +++ b/lib/msan/lit_tests/vector_select.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_msan -m64 -O0 %s -c -o %t +// RUN: %clangxx_msan -m64 -O3 %s -c -o %t + +// Regression test for MemorySanitizer instrumentation of a select instruction +// with vector arguments. + +#include <emmintrin.h> + +__m128d select(bool b, __m128d c, __m128d d) +{ + return b ? c : d; +} + diff --git a/lib/msan/lit_tests/wrap_indirect_calls.cc b/lib/msan/lit_tests/wrap_indirect_calls.cc new file mode 100644 index 000000000000..b4bac1ecbd22 --- /dev/null +++ b/lib/msan/lit_tests/wrap_indirect_calls.cc @@ -0,0 +1,64 @@ +// Test indirect call wrapping in MemorySanitizer. + +// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/two.cc -fPIC -shared -o %t-two-so.so +// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/wrapper.cc -fPIC -shared -o %t-wrapper-so.so + +// Disable fast path. + +// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc %s \ +// RUN: %t-two-so.so %t-wrapper-so.so \ +// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ +// RUN: -mllvm -msan-wrap-indirect-calls-fast=0 \ +// RUN: -DSLOW=1 \ +// RUN: -Wl,--defsym=__executable_start=0 -o %t +// RUN: %t + +// Enable fast path, call from executable, -O0. + +// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc %s \ +// RUN: %t-two-so.so %t-wrapper-so.so \ +// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ +// RUN: -mllvm -msan-wrap-indirect-calls-fast=1 \ +// RUN: -DSLOW=0 \ +// RUN: -Wl,--defsym=__executable_start=0 -o %t +// RUN: %t + +// Enable fast path, call from executable, -O3. + +// RUN: %clangxx_msan -O3 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc %s \ +// RUN: %t-two-so.so %t-wrapper-so.so \ +// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ +// RUN: -mllvm -msan-wrap-indirect-calls-fast=1 \ +// RUN: -DSLOW=0 \ +// RUN: -Wl,--defsym=__executable_start=0 -o %t +// RUN: %t + +// Enable fast path, call from DSO, -O0. + +// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc -shared \ +// RUN: %t-two-so.so %t-wrapper-so.so \ +// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ +// RUN: -mllvm -msan-wrap-indirect-calls-fast=1 \ +// RUN: -DSLOW=0 \ +// RUN: -Wl,--defsym=__executable_start=0 -o %t-caller-so.so +// RUN: %clangxx_msan -O0 %s %t-caller-so.so %t-two-so.so %t-wrapper-so.so -o %t +// RUN: %t + +// Enable fast path, call from DSO, -O3. + +// RUN: %clangxx_msan -O3 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc -shared \ +// RUN: %t-two-so.so %t-wrapper-so.so \ +// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ +// RUN: -mllvm -msan-wrap-indirect-calls-fast=1 \ +// RUN: -DSLOW=0 \ +// RUN: -Wl,--defsym=__executable_start=0 -o %t-caller-so.so +// RUN: %clangxx_msan -O3 %s %t-caller-so.so %t-two-so.so %t-wrapper-so.so -o %t +// RUN: %t + +// The actual test is in multiple files in wrap_indirect_calls/ directory. +void run_test(); + +int main() { + run_test(); + return 0; +} diff --git a/lib/msan/lit_tests/wrap_indirect_calls/caller.cc b/lib/msan/lit_tests/wrap_indirect_calls/caller.cc new file mode 100644 index 000000000000..a0af8b7bb0c5 --- /dev/null +++ b/lib/msan/lit_tests/wrap_indirect_calls/caller.cc @@ -0,0 +1,51 @@ +// Indirectly call a bunch of functions. + +#include <assert.h> + +extern int cnt; + +typedef int (*F)(int, int); + +// A function in the same object. +int f_local(int x, int y) { + return x + y; +} + +// A function in another object. +int f_other_object(int x, int y); + +// A function in another DSO. +int f_dso(int x, int y); + +// A function in another DSO that is replaced by the wrapper. +int f_replaced(int x, int y); + +void run_test(void) { + int x; + int expected_cnt = 0; + volatile F f; + + if (SLOW) ++expected_cnt; + f = &f_local; + x = f(1, 2); + assert(x == 3); + assert(cnt == expected_cnt); + + if (SLOW) ++expected_cnt; + f = &f_other_object; + x = f(2, 3); + assert(x == 6); + assert(cnt == expected_cnt); + + ++expected_cnt; + f = &f_dso; + x = f(2, 3); + assert(x == 7); + assert(cnt == expected_cnt); + + ++expected_cnt; + f = &f_replaced; + x = f(2, 3); + assert(x == 11); + assert(cnt == expected_cnt); +} diff --git a/lib/msan/lit_tests/wrap_indirect_calls/lit.local.cfg b/lib/msan/lit_tests/wrap_indirect_calls/lit.local.cfg new file mode 100644 index 000000000000..5e01230c0986 --- /dev/null +++ b/lib/msan/lit_tests/wrap_indirect_calls/lit.local.cfg @@ -0,0 +1,3 @@ +# Sources in this directory are used by tests in parent directory. + +config.suffixes = [] diff --git a/lib/msan/lit_tests/wrap_indirect_calls/one.cc b/lib/msan/lit_tests/wrap_indirect_calls/one.cc new file mode 100644 index 000000000000..ab7bf4125c0a --- /dev/null +++ b/lib/msan/lit_tests/wrap_indirect_calls/one.cc @@ -0,0 +1,3 @@ +int f_other_object(int x, int y) { + return x * y; +} diff --git a/lib/msan/lit_tests/wrap_indirect_calls/two.cc b/lib/msan/lit_tests/wrap_indirect_calls/two.cc new file mode 100644 index 000000000000..c939a993bc9a --- /dev/null +++ b/lib/msan/lit_tests/wrap_indirect_calls/two.cc @@ -0,0 +1,11 @@ +int f_dso(int x, int y) { + return 2 * x + y; +} + +int f_replaced(int x, int y) { + return x + y + 5; +} + +int f_replacement(int x, int y) { + return x + y + 6; +} diff --git a/lib/msan/lit_tests/wrap_indirect_calls/wrapper.cc b/lib/msan/lit_tests/wrap_indirect_calls/wrapper.cc new file mode 100644 index 000000000000..8fcd0c635d96 --- /dev/null +++ b/lib/msan/lit_tests/wrap_indirect_calls/wrapper.cc @@ -0,0 +1,11 @@ +int f_replaced(int x, int y); +int f_replacement(int x, int y); + +int cnt; + +extern "C" void *wrapper(void *p) { + ++cnt; + if (p == (void *)f_replaced) + return (void *)f_replacement; + return p; +} diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc index aa79b31be2e0..83b11e5c2ff3 100644 --- a/lib/msan/msan.cc +++ b/lib/msan/msan.cc @@ -58,14 +58,17 @@ static THREADLOCAL struct { uptr stack_top, stack_bottom; } __msan_stack_bounds; -static THREADLOCAL bool is_in_symbolizer; -static THREADLOCAL bool is_in_loader; +static THREADLOCAL int is_in_symbolizer; +static THREADLOCAL int is_in_loader; + +extern "C" SANITIZER_WEAK_ATTRIBUTE const int __msan_track_origins; -extern "C" const int __msan_track_origins; int __msan_get_track_origins() { - return __msan_track_origins; + return &__msan_track_origins ? __msan_track_origins : 0; } +extern "C" SANITIZER_WEAK_ATTRIBUTE const int __msan_keep_going; + namespace __msan { static bool IsRunningUnderDr() { @@ -84,12 +87,12 @@ static bool IsRunningUnderDr() { return result; } -void EnterSymbolizer() { is_in_symbolizer = true; } -void ExitSymbolizer() { is_in_symbolizer = false; } +void EnterSymbolizer() { ++is_in_symbolizer; } +void ExitSymbolizer() { --is_in_symbolizer; } bool IsInSymbolizer() { return is_in_symbolizer; } -void EnterLoader() { is_in_loader = true; } -void ExitLoader() { is_in_loader = false; } +void EnterLoader() { ++is_in_loader; } +void ExitLoader() { --is_in_loader; } extern "C" { SANITIZER_INTERFACE_ATTRIBUTE @@ -111,6 +114,7 @@ int msan_report_count = 0; // FIXME: make it resizable. static const uptr kNumStackOriginDescrs = 1024 * 1024; static const char *StackOriginDescr[kNumStackOriginDescrs]; +static uptr StackOriginPC[kNumStackOriginDescrs]; static atomic_uint32_t NumStackOriginDescrs; static void ParseFlagsFromString(Flags *f, const char *str) { @@ -118,33 +122,39 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->poison_heap_with_zeroes, "poison_heap_with_zeroes"); ParseFlag(str, &f->poison_stack_with_zeroes, "poison_stack_with_zeroes"); ParseFlag(str, &f->poison_in_malloc, "poison_in_malloc"); + ParseFlag(str, &f->poison_in_free, "poison_in_free"); ParseFlag(str, &f->exit_code, "exit_code"); if (f->exit_code < 0 || f->exit_code > 127) { Printf("Exit code not in [0, 128) range: %d\n", f->exit_code); - f->exit_code = 1; Die(); } ParseFlag(str, &f->report_umrs, "report_umrs"); - ParseFlag(str, &f->verbosity, "verbosity"); ParseFlag(str, &f->wrap_signals, "wrap_signals"); + + // keep_going is an old name for halt_on_error, + // and it has inverse meaning. + f->halt_on_error = !f->halt_on_error; + ParseFlag(str, &f->halt_on_error, "keep_going"); + f->halt_on_error = !f->halt_on_error; + ParseFlag(str, &f->halt_on_error, "halt_on_error"); } static void InitializeFlags(Flags *f, const char *options) { CommonFlags *cf = common_flags(); + SetCommonFlagDefaults(); cf->external_symbolizer_path = GetEnv("MSAN_SYMBOLIZER_PATH"); - cf->strip_path_prefix = ""; - cf->fast_unwind_on_fatal = false; - cf->fast_unwind_on_malloc = true; cf->malloc_context_size = 20; + cf->handle_ioctl = true; internal_memset(f, 0, sizeof(*f)); f->poison_heap_with_zeroes = false; f->poison_stack_with_zeroes = false; f->poison_in_malloc = true; + f->poison_in_free = true; f->exit_code = 77; f->report_umrs = true; - f->verbosity = 0; f->wrap_signals = true; + f->halt_on_error = !&__msan_keep_going; // Override from user-specified string. if (__msan_default_options) @@ -166,19 +176,15 @@ static void GetCurrentStackBounds(uptr *stack_top, uptr *stack_bottom) { } void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, - bool fast) { - if (!fast) { + bool request_fast_unwind) { + if (!StackTrace::WillUseFastUnwind(request_fast_unwind)) { // Block reports from our interceptors during _Unwind_Backtrace. SymbolizerScope sym_scope; - return stack->SlowUnwindStack(pc, max_s); + return stack->Unwind(max_s, pc, bp, 0, 0, request_fast_unwind); } - uptr stack_top, stack_bottom; GetCurrentStackBounds(&stack_top, &stack_bottom); - stack->size = 0; - stack->trace[0] = pc; - stack->max_size = max_s; - stack->FastUnwindStack(pc, bp, stack_top, stack_bottom); + stack->Unwind(max_s, pc, bp, stack_top, stack_bottom, request_fast_unwind); } void PrintWarning(uptr pc, uptr bp) { @@ -204,16 +210,55 @@ void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin) { common_flags()->fast_unwind_on_fatal); u32 report_origin = - (__msan_track_origins && OriginIsValid(origin)) ? origin : 0; + (__msan_get_track_origins() && OriginIsValid(origin)) ? origin : 0; ReportUMR(&stack, report_origin); - if (__msan_track_origins && !OriginIsValid(origin)) { - Printf(" ORIGIN: invalid (%x). Might be a bug in MemorySanitizer, " - "please report to MemorySanitizer developers.\n", - origin); + if (__msan_get_track_origins() && !OriginIsValid(origin)) { + Printf( + " ORIGIN: invalid (%x). Might be a bug in MemorySanitizer origin " + "tracking.\n This could still be a bug in your code, too!\n", + origin); + } +} + +void UnpoisonParam(uptr n) { + internal_memset(__msan_param_tls, 0, n * sizeof(*__msan_param_tls)); +} + +// Backup MSan runtime TLS state. +// Implementation must be async-signal-safe. +// Instances of this class may live on the signal handler stack, and data size +// may be an issue. +void ScopedThreadLocalStateBackup::Backup() { + va_arg_overflow_size_tls = __msan_va_arg_overflow_size_tls; +} + +void ScopedThreadLocalStateBackup::Restore() { + // A lame implementation that only keeps essential state and resets the rest. + __msan_va_arg_overflow_size_tls = va_arg_overflow_size_tls; + + internal_memset(__msan_param_tls, 0, sizeof(__msan_param_tls)); + internal_memset(__msan_retval_tls, 0, sizeof(__msan_retval_tls)); + internal_memset(__msan_va_arg_tls, 0, sizeof(__msan_va_arg_tls)); + + if (__msan_get_track_origins()) { + internal_memset(&__msan_retval_origin_tls, 0, sizeof(__msan_retval_tls)); + internal_memset(__msan_param_origin_tls, 0, + sizeof(__msan_param_origin_tls)); } } +void UnpoisonThreadLocalState() { +} + +const char *GetOriginDescrIfStack(u32 id, uptr *pc) { + if ((id >> 31) == 0) return 0; + id &= (1U << 31) - 1; + CHECK_LT(id, kNumStackOriginDescrs); + if (pc) *pc = StackOriginPC[id]; + return StackOriginDescr[id]; +} + } // namespace __msan // Interface. @@ -224,6 +269,10 @@ void __msan_warning() { GET_CALLER_PC_BP_SP; (void)sp; PrintWarning(pc, bp); + if (__msan::flags()->halt_on_error) { + Printf("Exiting\n"); + Die(); + } } void __msan_warning_noreturn() { @@ -239,17 +288,20 @@ void __msan_init() { msan_init_is_running = 1; SanitizerToolName = "MemorySanitizer"; - InstallAtExitHandler(); SetDieCallback(MsanDie); InitTlsSize(); + + const char *msan_options = GetEnv("MSAN_OPTIONS"); + InitializeFlags(&msan_flags, msan_options); + __sanitizer_set_report_path(common_flags()->log_path); + InitializeInterceptors(); + InstallAtExitHandler(); // Needs __cxa_atexit interceptor. if (MSAN_REPLACE_OPERATORS_NEW_AND_DELETE) ReplaceOperatorsNewAndDelete(); - const char *msan_options = GetEnv("MSAN_OPTIONS"); - InitializeFlags(&msan_flags, msan_options); if (StackSizeIsUnlimited()) { - if (flags()->verbosity) + if (common_flags()->verbosity) Printf("Unlimited stack, doing reexec\n"); // A reasonably large stack size. It is bigger than the usual 8Mb, because, // well, the program could have been run with unlimited stack for a reason. @@ -257,15 +309,15 @@ void __msan_init() { ReExec(); } - if (flags()->verbosity) + if (common_flags()->verbosity) Printf("MSAN_OPTIONS: %s\n", msan_options ? msan_options : "<empty>"); msan_running_under_dr = IsRunningUnderDr(); __msan_clear_on_return(); - if (__msan_track_origins && flags()->verbosity > 0) + if (__msan_get_track_origins() && common_flags()->verbosity > 0) Printf("msan_track_origins\n"); - if (!InitShadow(/* prot1 */false, /* prot2 */true, /* map_shadow */true, - __msan_track_origins)) { + if (!InitShadow(/* prot1 */ false, /* prot2 */ true, /* map_shadow */ true, + __msan_get_track_origins())) { // FIXME: prot1 = false is only required when running under DR. Printf("FATAL: MemorySanitizer can not mmap the shadow memory.\n"); Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n"); @@ -277,14 +329,17 @@ void __msan_init() { } const char *external_symbolizer = common_flags()->external_symbolizer_path; + bool external_symbolizer_started = + Symbolizer::Init(external_symbolizer)->IsExternalAvailable(); if (external_symbolizer && external_symbolizer[0]) { - CHECK(InitializeExternalSymbolizer(external_symbolizer)); + CHECK(external_symbolizer_started); } + Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer); GetThreadStackTopAndBottom(/* at_initialization */true, &__msan_stack_bounds.stack_top, &__msan_stack_bounds.stack_bottom); - if (flags()->verbosity) + if (common_flags()->verbosity) Printf("MemorySanitizer init done\n"); msan_init_is_running = 0; msan_inited = 1; @@ -294,6 +349,10 @@ void __msan_set_exit_code(int exit_code) { flags()->exit_code = exit_code; } +void __msan_set_keep_going(int keep_going) { + flags()->halt_on_error = !keep_going; +} + void __msan_set_expect_umr(int expect_umr) { if (expect_umr) { msan_expected_umr_found = 0; @@ -310,13 +369,17 @@ void __msan_set_expect_umr(int expect_umr) { } void __msan_print_shadow(const void *x, uptr size) { + if (!MEM_IS_APP(x)) { + Printf("Not a valid application address: %p\n", x); + return; + } unsigned char *s = (unsigned char*)MEM_TO_SHADOW(x); u32 *o = (u32*)MEM_TO_ORIGIN(x); for (uptr i = 0; i < size; i++) { Printf("%x%x ", s[i] >> 4, s[i] & 0xf); } Printf("\n"); - if (__msan_track_origins) { + if (__msan_get_track_origins()) { for (uptr i = 0; i < size / 4; i++) { Printf(" o: %x ", o[i]); } @@ -331,10 +394,6 @@ void __msan_print_param_shadow() { Printf("\n"); } -void __msan_unpoison_param(uptr n) { - internal_memset(__msan_param_tls, 0, n * sizeof(*__msan_param_tls)); -} - sptr __msan_test_shadow(const void *x, uptr size) { unsigned char *s = (unsigned char*)MEM_TO_SHADOW((uptr)x); for (uptr i = 0; i < size; ++i) @@ -396,7 +455,7 @@ void __msan_set_origin(const void *a, uptr size, u32 origin) { // Origin mapping is 4 bytes per 4 bytes of application memory. // Here we extend the range such that its left and right bounds are both // 4 byte aligned. - if (!__msan_track_origins) return; + if (!__msan_get_track_origins()) return; uptr x = MEM_TO_ORIGIN((uptr)a); uptr beg = x & ~3UL; // align down. uptr end = (x + size + 3) & ~3UL; // align up. @@ -417,6 +476,10 @@ void __msan_set_origin(const void *a, uptr size, u32 origin) { // When we see descr for the first time we replace '----' with a uniq id // and set the origin to (id | (31-th bit)). void __msan_set_alloca_origin(void *a, uptr size, const char *descr) { + __msan_set_alloca_origin4(a, size, descr, 0); +} + +void __msan_set_alloca_origin4(void *a, uptr size, const char *descr, uptr pc) { static const u32 dash = '-'; static const u32 first_timer = dash + (dash << 8) + (dash << 16) + (dash << 24); @@ -429,8 +492,9 @@ void __msan_set_alloca_origin(void *a, uptr size, const char *descr) { *id_ptr = id; CHECK_LT(id, kNumStackOriginDescrs); StackOriginDescr[id] = descr + 4; + StackOriginPC[id] = pc; if (print) - Printf("First time: id=%d %s \n", id, descr + 4); + Printf("First time: id=%d %s %p \n", id, descr + 4, pc); } id |= 1U << 31; if (print) @@ -439,15 +503,11 @@ void __msan_set_alloca_origin(void *a, uptr size, const char *descr) { } const char *__msan_get_origin_descr_if_stack(u32 id) { - if ((id >> 31) == 0) return 0; - id &= (1U << 31) - 1; - CHECK_LT(id, kNumStackOriginDescrs); - return StackOriginDescr[id]; + return GetOriginDescrIfStack(id, 0); } - u32 __msan_get_origin(const void *a) { - if (!__msan_track_origins) return 0; + if (!__msan_get_track_origins()) return 0; uptr x = (uptr)a; uptr aligned = x & ~3ULL; uptr origin_ptr = MEM_TO_ORIGIN(aligned); @@ -458,9 +518,46 @@ u32 __msan_get_umr_origin() { return __msan_origin_tls; } +u16 __sanitizer_unaligned_load16(const uu16 *p) { + __msan_retval_tls[0] = *(uu16 *)MEM_TO_SHADOW((uptr)p); + if (__msan_get_track_origins()) + __msan_retval_origin_tls = *(uu32 *)MEM_TO_ORIGIN((uptr)p); + return *p; +} +u32 __sanitizer_unaligned_load32(const uu32 *p) { + __msan_retval_tls[0] = *(uu32 *)MEM_TO_SHADOW((uptr)p); + if (__msan_get_track_origins()) + __msan_retval_origin_tls = *(uu32 *)MEM_TO_ORIGIN((uptr)p); + return *p; +} +u64 __sanitizer_unaligned_load64(const uu64 *p) { + __msan_retval_tls[0] = *(uu64 *)MEM_TO_SHADOW((uptr)p); + if (__msan_get_track_origins()) + __msan_retval_origin_tls = *(uu32 *)MEM_TO_ORIGIN((uptr)p); + return *p; +} +void __sanitizer_unaligned_store16(uu16 *p, u16 x) { + *(uu16 *)MEM_TO_SHADOW((uptr)p) = __msan_param_tls[1]; + if (__msan_get_track_origins()) + *(uu32 *)MEM_TO_ORIGIN((uptr)p) = __msan_param_origin_tls[1]; + *p = x; +} +void __sanitizer_unaligned_store32(uu32 *p, u32 x) { + *(uu32 *)MEM_TO_SHADOW((uptr)p) = __msan_param_tls[1]; + if (__msan_get_track_origins()) + *(uu32 *)MEM_TO_ORIGIN((uptr)p) = __msan_param_origin_tls[1]; + *p = x; +} +void __sanitizer_unaligned_store64(uu64 *p, u64 x) { + *(uu64 *)MEM_TO_SHADOW((uptr)p) = __msan_param_tls[1]; + if (__msan_get_track_origins()) + *(uu32 *)MEM_TO_ORIGIN((uptr)p) = __msan_param_origin_tls[1]; + *p = x; +} + #if !SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char* __msan_default_options() { return ""; } } // extern "C" #endif diff --git a/lib/msan/msan.h b/lib/msan/msan.h index baaba49f4187..4e6c6194505e 100644 --- a/lib/msan/msan.h +++ b/lib/msan/msan.h @@ -25,13 +25,12 @@ # define MSAN_REPLACE_OPERATORS_NEW_AND_DELETE 1 #endif -#define MEM_TO_SHADOW(mem) (((uptr)mem) & ~0x400000000000ULL) -#define MEM_TO_ORIGIN(mem) (MEM_TO_SHADOW(mem) + 0x200000000000ULL) -#define MEM_IS_APP(mem) ((uptr)mem >= 0x600000000000ULL) -#define MEM_IS_SHADOW(mem) ((uptr)mem >= 0x200000000000ULL && \ - (uptr)mem <= 0x400000000000ULL) - -struct link_map; // Opaque type returned by dlopen(). +#define MEM_TO_SHADOW(mem) (((uptr)mem) & ~0x400000000000ULL) +#define SHADOW_TO_ORIGIN(shadow) (((uptr)shadow) + 0x200000000000ULL) +#define MEM_TO_ORIGIN(mem) (SHADOW_TO_ORIGIN(MEM_TO_SHADOW(mem))) +#define MEM_IS_APP(mem) ((uptr)mem >= 0x600000000000ULL) +#define MEM_IS_SHADOW(mem) \ + ((uptr)mem >= 0x200000000000ULL && (uptr)mem <= 0x400000000000ULL) const int kMsanParamTlsSizeInWords = 100; const int kMsanRetvalTlsSizeInWords = 100; @@ -46,13 +45,16 @@ bool InitShadow(bool prot1, bool prot2, bool map_shadow, bool init_origins); char *GetProcSelfMaps(); void InitializeInterceptors(); +void MsanAllocatorThreadFinish(); void *MsanReallocate(StackTrace *stack, void *oldp, uptr size, uptr alignment, bool zeroise); -void MsanDeallocate(void *ptr); +void MsanDeallocate(StackTrace *stack, void *ptr); void InstallTrapHandler(); void InstallAtExitHandler(); void ReplaceOperatorsNewAndDelete(); +const char *GetOriginDescrIfStack(u32 id, uptr *pc); + void EnterSymbolizer(); void ExitSymbolizer(); bool IsInSymbolizer(); @@ -70,13 +72,15 @@ void PrintWarning(uptr pc, uptr bp); void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin); void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, - bool fast); + bool request_fast_unwind); void ReportUMR(StackTrace *stack, u32 origin); void ReportExpectedUMRNotFound(StackTrace *stack); void ReportAtExitStatistics(); -void UnpoisonMappedDSO(struct link_map *map); +// Unpoison first n function arguments. +void UnpoisonParam(uptr n); +void UnpoisonThreadLocalState(); #define GET_MALLOC_STACK_TRACE \ StackTrace stack; \ @@ -86,6 +90,20 @@ void UnpoisonMappedDSO(struct link_map *map); StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \ common_flags()->fast_unwind_on_malloc) +class ScopedThreadLocalStateBackup { + public: + ScopedThreadLocalStateBackup() { Backup(); } + ~ScopedThreadLocalStateBackup() { Restore(); } + void Backup(); + void Restore(); + private: + u64 va_arg_overflow_size_tls; +}; } // namespace __msan +#define MSAN_MALLOC_HOOK(ptr, size) \ + if (&__msan_malloc_hook) __msan_malloc_hook(ptr, size) +#define MSAN_FREE_HOOK(ptr) \ + if (&__msan_free_hook) __msan_free_hook(ptr) + #endif // MSAN_H diff --git a/lib/msan/msan.syms b/lib/msan/msan.syms deleted file mode 100644 index 24bbaba478b7..000000000000 --- a/lib/msan/msan.syms +++ /dev/null @@ -1,5 +0,0 @@ -{ - __msan_*; - __sanitizer_syscall_pre_*; - __sanitizer_syscall_post_*; -}; diff --git a/lib/msan/msan.syms.extra b/lib/msan/msan.syms.extra new file mode 100644 index 000000000000..aad41cf1124e --- /dev/null +++ b/lib/msan/msan.syms.extra @@ -0,0 +1 @@ +__msan_* diff --git a/lib/msan/msan_allocator.cc b/lib/msan/msan_allocator.cc index 7435843ce61e..2badf712188b 100644 --- a/lib/msan/msan_allocator.cc +++ b/lib/msan/msan_allocator.cc @@ -25,6 +25,7 @@ struct Metadata { static const uptr kAllocatorSpace = 0x600000000000ULL; static const uptr kAllocatorSize = 0x80000000000; // 8T. static const uptr kMetadataSize = sizeof(Metadata); +static const uptr kMaxAllowedMallocSize = 8UL << 30; typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize, DefaultSizeClassMap> PrimaryAllocator; @@ -45,35 +46,56 @@ static inline void Init() { allocator.Init(); } +void MsanAllocatorThreadFinish() { + allocator.SwallowCache(&cache); +} + static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment, bool zeroise) { Init(); + if (size > kMaxAllowedMallocSize) { + Report("WARNING: MemorySanitizer failed to allocate %p bytes\n", + (void *)size); + return AllocatorReturnNull(); + } void *res = allocator.Allocate(&cache, size, alignment, false); Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(res)); meta->requested_size = size; - if (zeroise) + if (zeroise) { __msan_clear_and_unpoison(res, size); - else if (flags()->poison_in_malloc) + } else if (flags()->poison_in_malloc) { __msan_poison(res, size); - if (__msan_get_track_origins()) { - u32 stack_id = StackDepotPut(stack->trace, stack->size); - CHECK(stack_id); - CHECK_EQ((stack_id >> 31), 0); // Higher bit is occupied by stack origins. - __msan_set_origin(res, size, stack_id); + if (__msan_get_track_origins()) { + u32 stack_id = StackDepotPut(stack->trace, stack->size); + CHECK(stack_id); + CHECK_EQ((stack_id >> 31), + 0); // Higher bit is occupied by stack origins. + __msan_set_origin(res, size, stack_id); + } } + MSAN_MALLOC_HOOK(res, size); return res; } -void MsanDeallocate(void *p) { +void MsanDeallocate(StackTrace *stack, void *p) { CHECK(p); Init(); + MSAN_FREE_HOOK(p); Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(p)); uptr size = meta->requested_size; + meta->requested_size = 0; // This memory will not be reused by anyone else, so we are free to keep it // poisoned. - __msan_poison(p, size); - if (__msan_get_track_origins()) - __msan_set_origin(p, size, -1); + if (flags()->poison_in_free) { + __msan_poison(p, size); + if (__msan_get_track_origins()) { + u32 stack_id = StackDepotPut(stack->trace, stack->size); + CHECK(stack_id); + CHECK_EQ((stack_id >> 31), + 0); // Higher bit is occupied by stack origins. + __msan_set_origin(p, size, stack_id); + } + } allocator.Deallocate(&cache, p); } @@ -82,7 +104,7 @@ void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, if (!old_p) return MsanAllocate(stack, new_size, alignment, zeroise); if (!new_size) { - MsanDeallocate(old_p); + MsanDeallocate(stack, old_p); return 0; } Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(old_p)); @@ -98,10 +120,59 @@ void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, uptr memcpy_size = Min(new_size, old_size); void *new_p = MsanAllocate(stack, new_size, alignment, zeroise); // Printf("realloc: old_size %zd new_size %zd\n", old_size, new_size); - if (new_p) + if (new_p) { __msan_memcpy(new_p, old_p, memcpy_size); - MsanDeallocate(old_p); + MsanDeallocate(stack, old_p); + } return new_p; } +static uptr AllocationSize(const void *p) { + if (p == 0) + return 0; + const void *beg = allocator.GetBlockBegin(p); + if (beg != p) + return 0; + Metadata *b = (Metadata*)allocator.GetMetaData(p); + return b->requested_size; +} + } // namespace __msan + +using namespace __msan; + +uptr __msan_get_current_allocated_bytes() { + u64 stats[AllocatorStatCount]; + allocator.GetStats(stats); + u64 m = stats[AllocatorStatMalloced]; + u64 f = stats[AllocatorStatFreed]; + return m >= f ? m - f : 1; +} + +uptr __msan_get_heap_size() { + u64 stats[AllocatorStatCount]; + allocator.GetStats(stats); + u64 m = stats[AllocatorStatMmapped]; + u64 f = stats[AllocatorStatUnmapped]; + return m >= f ? m - f : 1; +} + +uptr __msan_get_free_bytes() { + return 1; +} + +uptr __msan_get_unmapped_bytes() { + return 1; +} + +uptr __msan_get_estimated_allocated_size(uptr size) { + return size; +} + +int __msan_get_ownership(const void *p) { + return AllocationSize(p) != 0; +} + +uptr __msan_get_allocated_size(const void *p) { + return AllocationSize(p); +} diff --git a/lib/msan/msan_flags.h b/lib/msan/msan_flags.h index 64ef84509888..93fa8a60dba0 100644 --- a/lib/msan/msan_flags.h +++ b/lib/msan/msan_flags.h @@ -19,12 +19,13 @@ namespace __msan { // Flags. struct Flags { int exit_code; - int verbosity; bool poison_heap_with_zeroes; // default: false bool poison_stack_with_zeroes; // default: false bool poison_in_malloc; // default: true + bool poison_in_free; // default: true bool report_umrs; bool wrap_signals; + bool halt_on_error; }; Flags *flags(); diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc index 1bcf93db9440..15a8bec12f31 100644 --- a/lib/msan/msan_interceptors.cc +++ b/lib/msan/msan_interceptors.cc @@ -19,6 +19,8 @@ #include "msan.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_libc.h" @@ -28,10 +30,15 @@ // ACHTUNG! No other system header includes in this file. // Ideally, we should get rid of stdarg.h as well. -extern "C" const int __msan_keep_going; - using namespace __msan; +using __sanitizer::memory_order; +using __sanitizer::atomic_load; +using __sanitizer::atomic_store; +using __sanitizer::atomic_uintptr_t; + +static unsigned g_thread_finalize_key; + // True if this is a nested interceptor. static THREADLOCAL int in_interceptor_scope; @@ -52,28 +59,30 @@ bool IsInInterceptorScope() { } while (0) // Check that [x, x+n) range is unpoisoned. -#define CHECK_UNPOISONED_0(x, n) \ - do { \ - sptr offset = __msan_test_shadow(x, n); \ - if (__msan::IsInSymbolizer()) break; \ - if (offset >= 0 && __msan::flags()->report_umrs) { \ - GET_CALLER_PC_BP_SP; \ - (void) sp; \ - Printf("UMR in %s at offset %d inside [%p, +%d) \n", __FUNCTION__, \ - offset, x, n); \ - __msan::PrintWarningWithOrigin(pc, bp, \ - __msan_get_origin((char *) x + offset)); \ - if (!__msan_keep_going) { \ - Printf("Exiting\n"); \ - Die(); \ - } \ - } \ +#define CHECK_UNPOISONED_0(x, n) \ + do { \ + sptr offset = __msan_test_shadow(x, n); \ + if (__msan::IsInSymbolizer()) break; \ + if (offset >= 0 && __msan::flags()->report_umrs) { \ + GET_CALLER_PC_BP_SP; \ + (void) sp; \ + Printf("UMR in %s at offset %d inside [%p, +%d) \n", __FUNCTION__, \ + offset, x, n); \ + __msan::PrintWarningWithOrigin(pc, bp, \ + __msan_get_origin((char *)x + offset)); \ + if (__msan::flags()->halt_on_error) { \ + Printf("Exiting\n"); \ + Die(); \ + } \ + } \ } while (0) // Check that [x, x+n) range is unpoisoned unless we are in a nested // interceptor. -#define CHECK_UNPOISONED(x, n) \ - if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); +#define CHECK_UNPOISONED(x, n) \ + do { \ + if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \ + } while (0); static void *fast_memset(void *ptr, int c, SIZE_T n); static void *fast_memcpy(void *dst, const void *src, SIZE_T n); @@ -103,22 +112,22 @@ INTERCEPTOR(SSIZE_T, readlink, const char *path, char *buf, SIZE_T bufsiz) { return res; } -INTERCEPTOR(void *, readdir, void *a) { - ENSURE_MSAN_INITED(); - void *res = REAL(readdir)(a); - __msan_unpoison(res, __sanitizer::struct_dirent_sz); - return res; +INTERCEPTOR(void *, memcpy, void *dest, const void *src, SIZE_T n) { + return __msan_memcpy(dest, src, n); } -INTERCEPTOR(void *, readdir64, void *a) { - ENSURE_MSAN_INITED(); - void *res = REAL(readdir)(a); - __msan_unpoison(res, __sanitizer::struct_dirent64_sz); - return res; +INTERCEPTOR(void *, mempcpy, void *dest, const void *src, SIZE_T n) { + return (char *)__msan_memcpy(dest, src, n) + n; } -INTERCEPTOR(void *, memcpy, void *dest, const void *src, SIZE_T n) { - return __msan_memcpy(dest, src, n); +INTERCEPTOR(void *, memccpy, void *dest, const void *src, int c, SIZE_T n) { + ENSURE_MSAN_INITED(); + void *res = REAL(memccpy)(dest, src, c, n); + CHECK(!res || (res >= dest && res <= (char *)dest + n)); + SIZE_T sz = res ? (char *)res - (char *)dest : n; + CHECK_UNPOISONED(src, sz); + __msan_unpoison(dest, sz); + return res; } INTERCEPTOR(void *, memmove, void *dest, const void *src, SIZE_T n) { @@ -129,6 +138,10 @@ INTERCEPTOR(void *, memset, void *s, int c, SIZE_T n) { return __msan_memset(s, c, n); } +INTERCEPTOR(void *, bcopy, const void *src, void *dest, SIZE_T n) { + return __msan_memmove(dest, src, n); +} + INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(alignment & (alignment - 1), 0); @@ -139,10 +152,35 @@ INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { return 0; } +INTERCEPTOR(void *, memalign, SIZE_T boundary, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + CHECK_EQ(boundary & (boundary - 1), 0); + void *ptr = MsanReallocate(&stack, 0, size, boundary, false); + return ptr; +} + +INTERCEPTOR(void *, valloc, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + void *ptr = MsanReallocate(&stack, 0, size, GetPageSizeCached(), false); + return ptr; +} + +INTERCEPTOR(void *, pvalloc, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + uptr PageSize = GetPageSizeCached(); + size = RoundUpTo(size, PageSize); + if (size == 0) { + // pvalloc(0) should allocate one page. + size = PageSize; + } + void *ptr = MsanReallocate(&stack, 0, size, PageSize, false); + return ptr; +} + INTERCEPTOR(void, free, void *ptr) { - ENSURE_MSAN_INITED(); + GET_MALLOC_STACK_TRACE; if (ptr == 0) return; - MsanDeallocate(ptr); + MsanDeallocate(&stack, ptr); } INTERCEPTOR(SIZE_T, strlen, const char *s) { @@ -181,6 +219,14 @@ INTERCEPTOR(char *, strncpy, char *dest, const char *src, SIZE_T n) { // NOLINT return res; } +INTERCEPTOR(char *, stpcpy, char *dest, const char *src) { // NOLINT + ENSURE_MSAN_INITED(); + SIZE_T n = REAL(strlen)(src); + char *res = REAL(stpcpy)(dest, src); // NOLINT + __msan_copy_poison(dest, src, n + 1); + return res; +} + INTERCEPTOR(char *, strdup, char *src) { ENSURE_MSAN_INITED(); SIZE_T n = REAL(strlen)(src); @@ -295,6 +341,26 @@ INTERCEPTOR(double, strtod, const char *nptr, char **endptr) { // NOLINT return res; } +INTERCEPTOR(double, strtod_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + double res = REAL(strtod_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(double, __strtod_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + double res = REAL(__strtod_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + INTERCEPTOR(float, strtof, const char *nptr, char **endptr) { // NOLINT ENSURE_MSAN_INITED(); float res = REAL(strtof)(nptr, endptr); // NOLINT @@ -304,6 +370,26 @@ INTERCEPTOR(float, strtof, const char *nptr, char **endptr) { // NOLINT return res; } +INTERCEPTOR(float, strtof_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + float res = REAL(strtof_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(float, __strtof_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + float res = REAL(__strtof_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + INTERCEPTOR(long double, strtold, const char *nptr, char **endptr) { // NOLINT ENSURE_MSAN_INITED(); long double res = REAL(strtold)(nptr, endptr); // NOLINT @@ -313,11 +399,50 @@ INTERCEPTOR(long double, strtold, const char *nptr, char **endptr) { // NOLINT return res; } +INTERCEPTOR(long double, strtold_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + long double res = REAL(strtold_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(long double, __strtold_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + long double res = REAL(__strtold_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(int, vasprintf, char **strp, const char *format, va_list ap) { + ENSURE_MSAN_INITED(); + int res = REAL(vasprintf)(strp, format, ap); + if (res >= 0 && !__msan_has_dynamic_component()) { + __msan_unpoison(strp, sizeof(*strp)); + __msan_unpoison(*strp, res + 1); + } + return res; +} + +INTERCEPTOR(int, asprintf, char **strp, const char *format, ...) { // NOLINT + ENSURE_MSAN_INITED(); + va_list ap; + va_start(ap, format); + int res = vasprintf(strp, format, ap); // NOLINT + va_end(ap); + return res; +} + INTERCEPTOR(int, vsnprintf, char *str, uptr size, const char *format, va_list ap) { ENSURE_MSAN_INITED(); int res = REAL(vsnprintf)(str, size, format, ap); - if (!__msan_has_dynamic_component()) { + if (res >= 0 && !__msan_has_dynamic_component()) { __msan_unpoison(str, res + 1); } return res; @@ -326,7 +451,7 @@ INTERCEPTOR(int, vsnprintf, char *str, uptr size, INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) { ENSURE_MSAN_INITED(); int res = REAL(vsprintf)(str, format, ap); - if (!__msan_has_dynamic_component()) { + if (res >= 0 && !__msan_has_dynamic_component()) { __msan_unpoison(str, res + 1); } return res; @@ -335,7 +460,7 @@ INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) { INTERCEPTOR(int, vswprintf, void *str, uptr size, void *format, va_list ap) { ENSURE_MSAN_INITED(); int res = REAL(vswprintf)(str, size, format, ap); - if (!__msan_has_dynamic_component()) { + if (res >= 0 && !__msan_has_dynamic_component()) { __msan_unpoison(str, 4 * (res + 1)); } return res; @@ -370,25 +495,24 @@ INTERCEPTOR(int, swprintf, void *str, uptr size, void *format, ...) { // SIZE_T strftime(char *s, SIZE_T max, const char *format,const struct tm *tm); INTERCEPTOR(SIZE_T, strftime, char *s, SIZE_T max, const char *format, - void *tm) { + __sanitizer_tm *tm) { ENSURE_MSAN_INITED(); SIZE_T res = REAL(strftime)(s, max, format, tm); if (res) __msan_unpoison(s, res + 1); return res; } -INTERCEPTOR(SIZE_T, wcstombs, void *dest, void *src, SIZE_T size) { +INTERCEPTOR(int, mbtowc, wchar_t *dest, const char *src, SIZE_T n) { ENSURE_MSAN_INITED(); - SIZE_T res = REAL(wcstombs)(dest, src, size); - if (res != (SIZE_T)-1) __msan_unpoison(dest, res + 1); + int res = REAL(mbtowc)(dest, src, n); + if (res != -1 && dest) __msan_unpoison(dest, sizeof(wchar_t)); return res; } -// SIZE_T mbstowcs(wchar_t *dest, const char *src, SIZE_T n); -INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T n) { +INTERCEPTOR(int, mbrtowc, wchar_t *dest, const char *src, SIZE_T n, void *ps) { ENSURE_MSAN_INITED(); - SIZE_T res = REAL(mbstowcs)(dest, src, n); - if (res != (SIZE_T)-1) __msan_unpoison(dest, (res + 1) * sizeof(wchar_t)); + SIZE_T res = REAL(mbrtowc)(dest, src, n, ps); + if (res != (SIZE_T)-1 && dest) __msan_unpoison(dest, sizeof(wchar_t)); return res; } @@ -422,6 +546,13 @@ INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { return res; } +INTERCEPTOR(wchar_t *, wmempcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { + ENSURE_MSAN_INITED(); + wchar_t *res = REAL(wmempcpy)(dest, src, n); + __msan_copy_poison(dest, src, n * sizeof(wchar_t)); + return res; +} + INTERCEPTOR(wchar_t *, wmemset, wchar_t *s, wchar_t c, SIZE_T n) { CHECK(MEM_IS_APP(s)); ENSURE_MSAN_INITED(); @@ -450,28 +581,6 @@ INTERCEPTOR(double, wcstod, const wchar_t *nptr, wchar_t **endptr) { return res; } -// #define UNSUPPORTED(name) \ -// INTERCEPTOR(void, name, void) { \ -// Printf("MSAN: Unsupported %s\n", __FUNCTION__); \ -// Die(); \ -// } - -// FIXME: intercept the following functions: -// Note, they only matter when running without a dynamic tool. -// UNSUPPORTED(wcscoll_l) -// UNSUPPORTED(wcsnrtombs) -// UNSUPPORTED(wcstol) -// UNSUPPORTED(wcstoll) -// UNSUPPORTED(wcstold) -// UNSUPPORTED(wcstoul) -// UNSUPPORTED(wcstoull) -// UNSUPPORTED(wcsxfrm_l) -// UNSUPPORTED(wcsdup) -// UNSUPPORTED(wcsftime) -// UNSUPPORTED(wcsstr) -// UNSUPPORTED(wcsrchr) -// UNSUPPORTED(wctob) - INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { ENSURE_MSAN_INITED(); int res = REAL(gettimeofday)(tv, tz); @@ -502,6 +611,32 @@ INTERCEPTOR(char *, getenv, char *name) { return res; } +extern char **environ; + +static void UnpoisonEnviron() { + char **envp = environ; + for (; *envp; ++envp) { + __msan_unpoison(envp, sizeof(*envp)); + __msan_unpoison(*envp, REAL(strlen)(*envp) + 1); + } + // Trailing NULL pointer. + __msan_unpoison(envp, sizeof(*envp)); +} + +INTERCEPTOR(int, setenv, const char *name, const char *value, int overwrite) { + ENSURE_MSAN_INITED(); + int res = REAL(setenv)(name, value, overwrite); + if (!res) UnpoisonEnviron(); + return res; +} + +INTERCEPTOR(int, putenv, char *string) { + ENSURE_MSAN_INITED(); + int res = REAL(putenv)(string); + if (!res) UnpoisonEnviron(); + return res; +} + INTERCEPTOR(int, __fxstat, int magic, int fd, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__fxstat)(magic, fd, buf); @@ -518,6 +653,22 @@ INTERCEPTOR(int, __fxstat64, int magic, int fd, void *buf) { return res; } +INTERCEPTOR(int, __fxstatat, int magic, int fd, char *pathname, void *buf, + int flags) { + ENSURE_MSAN_INITED(); + int res = REAL(__fxstatat)(magic, fd, pathname, buf, flags); + if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); + return res; +} + +INTERCEPTOR(int, __fxstatat64, int magic, int fd, char *pathname, void *buf, + int flags) { + ENSURE_MSAN_INITED(); + int res = REAL(__fxstatat64)(magic, fd, pathname, buf, flags); + if (!res) __msan_unpoison(buf, __sanitizer::struct_stat64_sz); + return res; +} + INTERCEPTOR(int, __xstat, int magic, char *path, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__xstat)(magic, path, buf); @@ -592,22 +743,6 @@ INTERCEPTOR(char *, fgets_unlocked, char *s, int size, void *stream) { return res; } -INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) { - ENSURE_MSAN_INITED(); - char *res = REAL(getcwd)(buf, size); - if (res) - __msan_unpoison(res, REAL(strlen)(res) + 1); - return res; -} - -INTERCEPTOR(char *, realpath, char *path, char *abspath) { - ENSURE_MSAN_INITED(); - char *res = REAL(realpath)(path, abspath); - if (res) - __msan_unpoison(abspath, REAL(strlen)(abspath) + 1); - return res; -} - INTERCEPTOR(int, getrlimit, int resource, void *rlim) { if (msan_init_is_running) return REAL(getrlimit)(resource, rlim); @@ -628,38 +763,6 @@ INTERCEPTOR(int, getrlimit64, int resource, void *rlim) { return res; } -INTERCEPTOR(int, statfs, const char *s, void *buf) { - ENSURE_MSAN_INITED(); - int res = REAL(statfs)(s, buf); - if (!res) - __msan_unpoison(buf, __sanitizer::struct_statfs_sz); - return res; -} - -INTERCEPTOR(int, fstatfs, int fd, void *buf) { - ENSURE_MSAN_INITED(); - int res = REAL(fstatfs)(fd, buf); - if (!res) - __msan_unpoison(buf, __sanitizer::struct_statfs_sz); - return res; -} - -INTERCEPTOR(int, statfs64, const char *s, void *buf) { - ENSURE_MSAN_INITED(); - int res = REAL(statfs64)(s, buf); - if (!res) - __msan_unpoison(buf, __sanitizer::struct_statfs64_sz); - return res; -} - -INTERCEPTOR(int, fstatfs64, int fd, void *buf) { - ENSURE_MSAN_INITED(); - int res = REAL(fstatfs64)(fd, buf); - if (!res) - __msan_unpoison(buf, __sanitizer::struct_statfs64_sz); - return res; -} - INTERCEPTOR(int, uname, void *utsname) { ENSURE_MSAN_INITED(); int res = REAL(uname)(utsname); @@ -710,35 +813,24 @@ INTERCEPTOR(SSIZE_T, recv, int fd, void *buf, SIZE_T len, int flags) { } INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags, - void *srcaddr, void *addrlen) { + void *srcaddr, int *addrlen) { ENSURE_MSAN_INITED(); SIZE_T srcaddr_sz; - if (srcaddr) - srcaddr_sz = __sanitizer_get_socklen_t(addrlen); + if (srcaddr) srcaddr_sz = *addrlen; SSIZE_T res = REAL(recvfrom)(fd, buf, len, flags, srcaddr, addrlen); if (res > 0) { __msan_unpoison(buf, res); if (srcaddr) { - SIZE_T sz = __sanitizer_get_socklen_t(addrlen); + SIZE_T sz = *addrlen; __msan_unpoison(srcaddr, (sz < srcaddr_sz) ? sz : srcaddr_sz); } } return res; } -INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct msghdr *msg, int flags) { - ENSURE_MSAN_INITED(); - SSIZE_T res = REAL(recvmsg)(fd, msg, flags); - if (res > 0) { - for (SIZE_T i = 0; i < __sanitizer_get_msghdr_iovlen(msg); ++i) - __msan_unpoison(__sanitizer_get_msghdr_iov_iov_base(msg, i), - __sanitizer_get_msghdr_iov_iov_len(msg, i)); - } - return res; -} - INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) { - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + return AllocatorReturnNull(); GET_MALLOC_STACK_TRACE; if (!msan_inited) { // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. @@ -814,6 +906,13 @@ INTERCEPTOR(int, dladdr, void *addr, dlinfo *info) { return res; } +INTERCEPTOR(char *, dlerror) { + ENSURE_MSAN_INITED(); + char *res = REAL(dlerror)(); + if (res != 0) __msan_unpoison(res, REAL(strlen)(res) + 1); + return res; +} + // dlopen() ultimately calls mmap() down inside the loader, which generally // doesn't participate in dynamic symbol resolution. Therefore we won't // intercept its calls to mmap, and we have to hook it here. The loader @@ -828,7 +927,7 @@ INTERCEPTOR(void *, dlopen, const char *filename, int flag) { if (!__msan_has_dynamic_component() && map) { // If msandr didn't clear the shadow before the initializers ran, we do it // ourselves afterwards. - UnpoisonMappedDSO(map); + ForEachMappedRegion(map, __msan_unpoison); } return (void *)map; } @@ -848,7 +947,7 @@ static int msan_dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size, __msan_unpoison(info->dlpi_name, REAL(strlen)(info->dlpi_name) + 1); } dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data; - __msan_unpoison_param(3); + UnpoisonParam(3); return cbdata->callback(info, size, cbdata->data); } @@ -872,22 +971,32 @@ INTERCEPTOR(int, getrusage, int who, void *usage) { return res; } +// sigactions_mu guarantees atomicity of sigaction() and signal() calls. +// Access to sigactions[] is gone with relaxed atomics to avoid data race with +// the signal handler. const int kMaxSignals = 1024; -static uptr sigactions[kMaxSignals]; +static atomic_uintptr_t sigactions[kMaxSignals]; static StaticSpinMutex sigactions_mu; static void SignalHandler(int signo) { + ScopedThreadLocalStateBackup stlsb; + UnpoisonParam(1); + typedef void (*signal_cb)(int x); - signal_cb cb = (signal_cb)sigactions[signo]; + signal_cb cb = + (signal_cb)atomic_load(&sigactions[signo], memory_order_relaxed); cb(signo); } static void SignalAction(int signo, void *si, void *uc) { - __msan_unpoison(si, __sanitizer::struct_sigaction_sz); + ScopedThreadLocalStateBackup stlsb; + UnpoisonParam(3); + __msan_unpoison(si, sizeof(__sanitizer_sigaction)); __msan_unpoison(uc, __sanitizer::ucontext_t_sz); typedef void (*sigaction_cb)(int, void *, void *); - sigaction_cb cb = (sigaction_cb)sigactions[signo]; + sigaction_cb cb = + (sigaction_cb)atomic_load(&sigactions[signo], memory_order_relaxed); cb(signo, si, uc); } @@ -900,25 +1009,25 @@ INTERCEPTOR(int, sigaction, int signo, const __sanitizer_sigaction *act, if (flags()->wrap_signals) { SpinMutexLock lock(&sigactions_mu); CHECK_LT(signo, kMaxSignals); - uptr old_cb = sigactions[signo]; + uptr old_cb = atomic_load(&sigactions[signo], memory_order_relaxed); __sanitizer_sigaction new_act; __sanitizer_sigaction *pnew_act = act ? &new_act : 0; if (act) { - internal_memcpy(pnew_act, act, __sanitizer::struct_sigaction_sz); - uptr cb = __sanitizer::__sanitizer_get_sigaction_sa_sigaction(pnew_act); - uptr new_cb = - __sanitizer::__sanitizer_get_sigaction_sa_siginfo(pnew_act) ? - (uptr)SignalAction : (uptr)SignalHandler; + internal_memcpy(pnew_act, act, sizeof(__sanitizer_sigaction)); + uptr cb = (uptr)pnew_act->sa_sigaction; + uptr new_cb = (pnew_act->sa_flags & __sanitizer::sa_siginfo) + ? (uptr)SignalAction + : (uptr)SignalHandler; if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { - sigactions[signo] = cb; - __sanitizer::__sanitizer_set_sigaction_sa_sigaction(pnew_act, new_cb); + atomic_store(&sigactions[signo], cb, memory_order_relaxed); + pnew_act->sa_sigaction = (void (*)(int, void *, void *))new_cb; } } res = REAL(sigaction)(signo, pnew_act, oldact); if (res == 0 && oldact) { - uptr cb = __sanitizer::__sanitizer_get_sigaction_sa_sigaction(oldact); + uptr cb = (uptr)oldact->sa_sigaction; if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { - __sanitizer::__sanitizer_set_sigaction_sa_sigaction(oldact, old_cb); + oldact->sa_sigaction = (void (*)(int, void *, void *))old_cb; } } } else { @@ -926,7 +1035,7 @@ INTERCEPTOR(int, sigaction, int signo, const __sanitizer_sigaction *act, } if (res == 0 && oldact) { - __msan_unpoison(oldact, __sanitizer::struct_sigaction_sz); + __msan_unpoison(oldact, sizeof(__sanitizer_sigaction)); } return res; } @@ -937,7 +1046,7 @@ INTERCEPTOR(int, signal, int signo, uptr cb) { CHECK_LT(signo, kMaxSignals); SpinMutexLock lock(&sigactions_mu); if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { - sigactions[signo] = cb; + atomic_store(&sigactions[signo], cb, memory_order_relaxed); cb = (uptr) SignalHandler; } return REAL(signal)(signo, cb); @@ -948,8 +1057,39 @@ INTERCEPTOR(int, signal, int signo, uptr cb) { extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); -extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); -extern "C" int pthread_attr_getstack(void *attr, uptr *stack, uptr *stacksize); +extern "C" int pthread_setspecific(unsigned key, const void *v); +extern "C" int pthread_yield(); + +static void thread_finalize(void *v) { + uptr iter = (uptr)v; + if (iter > 1) { + if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) { + Printf("MemorySanitizer: failed to set thread key\n"); + Die(); + } + return; + } + MsanAllocatorThreadFinish(); +} + +struct ThreadParam { + void* (*callback)(void *arg); + void *param; + atomic_uintptr_t done; +}; + +static void *MsanThreadStartFunc(void *arg) { + ThreadParam *p = (ThreadParam *)arg; + void* (*callback)(void *arg) = p->callback; + void *param = p->param; + if (pthread_setspecific(g_thread_finalize_key, + (void *)kPthreadDestructorIterations)) { + Printf("MemorySanitizer: failed to set thread key\n"); + Die(); + } + atomic_store(&p->done, 1, memory_order_release); + return callback(param); +} INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), void * param) { @@ -960,9 +1100,19 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), attr = &myattr; } - AdjustStackSizeLinux(attr, flags()->verbosity); + AdjustStackSizeLinux(attr); + + ThreadParam p; + p.callback = callback; + p.param = param; + atomic_store(&p.done, 0, memory_order_relaxed); + + int res = REAL(pthread_create)(th, attr, MsanThreadStartFunc, (void *)&p); + if (res == 0) { + while (atomic_load(&p.done, memory_order_acquire) != 1) + pthread_yield(); + } - int res = REAL(pthread_create)(th, attr, callback, param); if (attr == &myattr) pthread_attr_destroy(&myattr); if (!res) { @@ -971,37 +1121,177 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), return res; } +INTERCEPTOR(int, pthread_key_create, __sanitizer_pthread_key_t *key, + void (*dtor)(void *value)) { + ENSURE_MSAN_INITED(); + int res = REAL(pthread_key_create)(key, dtor); + if (!res && key) + __msan_unpoison(key, sizeof(*key)); + return res; +} + +INTERCEPTOR(int, pthread_join, void *th, void **retval) { + ENSURE_MSAN_INITED(); + int res = REAL(pthread_join)(th, retval); + if (!res && retval) + __msan_unpoison(retval, sizeof(*retval)); + return res; +} + +extern char *tzname[2]; + +INTERCEPTOR(void, tzset) { + ENSURE_MSAN_INITED(); + REAL(tzset)(); + if (tzname[0]) + __msan_unpoison(tzname[0], REAL(strlen)(tzname[0]) + 1); + if (tzname[1]) + __msan_unpoison(tzname[1], REAL(strlen)(tzname[1]) + 1); + return; +} + +struct MSanAtExitRecord { + void (*func)(void *arg); + void *arg; +}; + +void MSanAtExitWrapper(void *arg) { + UnpoisonParam(1); + MSanAtExitRecord *r = (MSanAtExitRecord *)arg; + r->func(r->arg); + InternalFree(r); +} + +// Unpoison argument shadow for C++ module destructors. +INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, + void *dso_handle) { + if (msan_init_is_running) return REAL(__cxa_atexit)(func, arg, dso_handle); + ENSURE_MSAN_INITED(); + MSanAtExitRecord *r = + (MSanAtExitRecord *)InternalAlloc(sizeof(MSanAtExitRecord)); + r->func = func; + r->arg = arg; + return REAL(__cxa_atexit)(MSanAtExitWrapper, r, dso_handle); +} + +DECLARE_REAL(int, shmctl, int shmid, int cmd, void *buf) + +INTERCEPTOR(void *, shmat, int shmid, const void *shmaddr, int shmflg) { + ENSURE_MSAN_INITED(); + void *p = REAL(shmat)(shmid, shmaddr, shmflg); + if (p != (void *)-1) { + __sanitizer_shmid_ds ds; + int res = REAL(shmctl)(shmid, shmctl_ipc_stat, &ds); + if (!res) { + __msan_unpoison(p, ds.shm_segsz); + } + } + return p; +} + +// Linux kernel has a bug that leads to kernel deadlock if a process +// maps TBs of memory and then calls mlock(). +static void MlockIsUnsupported() { + static atomic_uint8_t printed; + if (atomic_exchange(&printed, 1, memory_order_relaxed)) + return; + if (common_flags()->verbosity > 0) + Printf("INFO: MemorySanitizer ignores mlock/mlockall/munlock/munlockall\n"); +} + +INTERCEPTOR(int, mlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, munlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, mlockall, int flags) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, munlockall, void) { + MlockIsUnsupported(); + return 0; +} + struct MSanInterceptorContext { bool in_interceptor_scope; }; -// A version of CHECK_UNPOISED using a saved scope value. Used in common +namespace __msan { + +int OnExit() { + // FIXME: ask frontend whether we need to return failure. + return 0; +} + +} // namespace __msan + +extern "C" int *__errno_location(void); + +// A version of CHECK_UNPOISONED using a saved scope value. Used in common // interceptors. -#define CHECK_UNPOISONED_CTX(ctx, x, n) \ - if (!((MSanInterceptorContext *) ctx)->in_interceptor_scope) \ - CHECK_UNPOISONED_0(x, n); +#define CHECK_UNPOISONED_CTX(ctx, x, n) \ + do { \ + if (!((MSanInterceptorContext *)ctx)->in_interceptor_scope) \ + CHECK_UNPOISONED_0(x, n); \ + } while (0) +#define MSAN_INTERCEPT_FUNC(name) \ + do { \ + if ((!INTERCEPT_FUNCTION(name) || !REAL(name)) && \ + common_flags()->verbosity > 0) \ + Report("MemorySanitizer: failed to intercept '" #name "'\n"); \ + } while (0) + +#define COMMON_INTERCEPT_FUNCTION(name) MSAN_INTERCEPT_FUNC(name) +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ + UnpoisonParam(count) #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ __msan_unpoison(ptr, size) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ - CHECK_UNPOISONED_CTX(ctx, ptr, size); -#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ - if (msan_init_is_running) return REAL(func)(__VA_ARGS__); \ - MSanInterceptorContext msan_ctx = { IsInInterceptorScope() }; \ - ctx = (void *)&msan_ctx; \ - InterceptorScope interceptor_scope; \ + CHECK_UNPOISONED_CTX(ctx, ptr, size) +#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, ptr, size) \ + __msan_unpoison(ptr, size) +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + if (msan_init_is_running) return REAL(func)(__VA_ARGS__); \ + MSanInterceptorContext msan_ctx = {IsInInterceptorScope()}; \ + ctx = (void *)&msan_ctx; \ + (void)ctx; \ + InterceptorScope interceptor_scope; \ + __msan_unpoison(__errno_location(), sizeof(int)); /* NOLINT */ \ ENSURE_MSAN_INITED(); #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ do { \ } while (false) -#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) do { } while (false) +#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + do { \ + } while (false) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ - do { } while (false) // FIXME + do { \ + } while (false) // FIXME +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + do { \ + } while (false) // FIXME +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() #include "sanitizer_common/sanitizer_common_interceptors.inc" #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s) -#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) -#define COMMON_SYSCALL_POST_READ_RANGE(p, s) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ + do { \ + } while (false) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + } while (false) #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) __msan_unpoison(p, s) #include "sanitizer_common/sanitizer_common_syscalls.inc" @@ -1062,15 +1352,41 @@ void __msan_clear_and_unpoison(void *a, uptr size) { fast_memset((void*)MEM_TO_SHADOW((uptr)a), 0, size); } +u32 get_origin_if_poisoned(uptr a, uptr size) { + unsigned char *s = (unsigned char *)MEM_TO_SHADOW(a); + for (uptr i = 0; i < size; ++i) + if (s[i]) + return *(u32 *)SHADOW_TO_ORIGIN((s + i) & ~3UL); + return 0; +} + void __msan_copy_origin(void *dst, const void *src, uptr size) { if (!__msan_get_track_origins()) return; if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return; - uptr d = MEM_TO_ORIGIN(dst); - uptr s = MEM_TO_ORIGIN(src); - uptr beg = d & ~3UL; // align down. - uptr end = (d + size + 3) & ~3UL; // align up. - s = s & ~3UL; // align down. - fast_memcpy((void*)beg, (void*)s, end - beg); + uptr d = (uptr)dst; + uptr beg = d & ~3UL; + // Copy left unaligned origin if that memory is poisoned. + if (beg < d) { + u32 o = get_origin_if_poisoned(beg, d - beg); + if (o) + *(u32 *)MEM_TO_ORIGIN(beg) = o; + beg += 4; + } + + uptr end = (d + size + 3) & ~3UL; + // Copy right unaligned origin if that memory is poisoned. + if (end > d + size) { + u32 o = get_origin_if_poisoned(d + size, end - d - size); + if (o) + *(u32 *)MEM_TO_ORIGIN(end - 4) = o; + end -= 4; + } + + if (beg < end) { + // Align src up. + uptr s = ((uptr)src + 3) & ~3UL; + fast_memcpy((void*)MEM_TO_ORIGIN(beg), (void*)MEM_TO_ORIGIN(s), end - beg); + } } void __msan_copy_poison(void *dst, const void *src, uptr size) { @@ -1119,6 +1435,9 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(mmap); INTERCEPT_FUNCTION(mmap64); INTERCEPT_FUNCTION(posix_memalign); + INTERCEPT_FUNCTION(memalign); + INTERCEPT_FUNCTION(valloc); + INTERCEPT_FUNCTION(pvalloc); INTERCEPT_FUNCTION(malloc); INTERCEPT_FUNCTION(calloc); INTERCEPT_FUNCTION(realloc); @@ -1126,15 +1445,18 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(fread); INTERCEPT_FUNCTION(fread_unlocked); INTERCEPT_FUNCTION(readlink); - INTERCEPT_FUNCTION(readdir); - INTERCEPT_FUNCTION(readdir64); INTERCEPT_FUNCTION(memcpy); + INTERCEPT_FUNCTION(memccpy); + INTERCEPT_FUNCTION(mempcpy); INTERCEPT_FUNCTION(memset); INTERCEPT_FUNCTION(memmove); + INTERCEPT_FUNCTION(bcopy); INTERCEPT_FUNCTION(wmemset); INTERCEPT_FUNCTION(wmemcpy); + INTERCEPT_FUNCTION(wmempcpy); INTERCEPT_FUNCTION(wmemmove); INTERCEPT_FUNCTION(strcpy); // NOLINT + INTERCEPT_FUNCTION(stpcpy); // NOLINT INTERCEPT_FUNCTION(strdup); INTERCEPT_FUNCTION(__strdup); INTERCEPT_FUNCTION(strndup); @@ -1150,8 +1472,16 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(strtoul); INTERCEPT_FUNCTION(strtoull); INTERCEPT_FUNCTION(strtod); + INTERCEPT_FUNCTION(strtod_l); + INTERCEPT_FUNCTION(__strtod_l); INTERCEPT_FUNCTION(strtof); + INTERCEPT_FUNCTION(strtof_l); + INTERCEPT_FUNCTION(__strtof_l); INTERCEPT_FUNCTION(strtold); + INTERCEPT_FUNCTION(strtold_l); + INTERCEPT_FUNCTION(__strtold_l); + INTERCEPT_FUNCTION(vasprintf); + INTERCEPT_FUNCTION(asprintf); INTERCEPT_FUNCTION(vsprintf); INTERCEPT_FUNCTION(vsnprintf); INTERCEPT_FUNCTION(vswprintf); @@ -1159,20 +1489,24 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(snprintf); INTERCEPT_FUNCTION(swprintf); INTERCEPT_FUNCTION(strftime); - INTERCEPT_FUNCTION(wcstombs); - INTERCEPT_FUNCTION(mbstowcs); + INTERCEPT_FUNCTION(mbtowc); + INTERCEPT_FUNCTION(mbrtowc); INTERCEPT_FUNCTION(wcslen); INTERCEPT_FUNCTION(wcschr); INTERCEPT_FUNCTION(wcscpy); INTERCEPT_FUNCTION(wcscmp); INTERCEPT_FUNCTION(wcstod); INTERCEPT_FUNCTION(getenv); + INTERCEPT_FUNCTION(setenv); + INTERCEPT_FUNCTION(putenv); INTERCEPT_FUNCTION(gettimeofday); INTERCEPT_FUNCTION(fcvt); INTERCEPT_FUNCTION(__fxstat); + INTERCEPT_FUNCTION(__fxstatat); INTERCEPT_FUNCTION(__xstat); INTERCEPT_FUNCTION(__lxstat); INTERCEPT_FUNCTION(__fxstat64); + INTERCEPT_FUNCTION(__fxstatat64); INTERCEPT_FUNCTION(__xstat64); INTERCEPT_FUNCTION(__lxstat64); INTERCEPT_FUNCTION(pipe); @@ -1180,28 +1514,33 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(socketpair); INTERCEPT_FUNCTION(fgets); INTERCEPT_FUNCTION(fgets_unlocked); - INTERCEPT_FUNCTION(getcwd); - INTERCEPT_FUNCTION(realpath); INTERCEPT_FUNCTION(getrlimit); INTERCEPT_FUNCTION(getrlimit64); - INTERCEPT_FUNCTION(statfs); - INTERCEPT_FUNCTION(fstatfs); - INTERCEPT_FUNCTION(statfs64); - INTERCEPT_FUNCTION(fstatfs64); INTERCEPT_FUNCTION(uname); INTERCEPT_FUNCTION(gethostname); INTERCEPT_FUNCTION(epoll_wait); INTERCEPT_FUNCTION(epoll_pwait); INTERCEPT_FUNCTION(recv); INTERCEPT_FUNCTION(recvfrom); - INTERCEPT_FUNCTION(recvmsg); INTERCEPT_FUNCTION(dladdr); + INTERCEPT_FUNCTION(dlerror); INTERCEPT_FUNCTION(dlopen); INTERCEPT_FUNCTION(dl_iterate_phdr); INTERCEPT_FUNCTION(getrusage); INTERCEPT_FUNCTION(sigaction); INTERCEPT_FUNCTION(signal); INTERCEPT_FUNCTION(pthread_create); + INTERCEPT_FUNCTION(pthread_key_create); + INTERCEPT_FUNCTION(pthread_join); + INTERCEPT_FUNCTION(tzset); + INTERCEPT_FUNCTION(__cxa_atexit); + INTERCEPT_FUNCTION(shmat); + + if (REAL(pthread_key_create)(&g_thread_finalize_key, &thread_finalize)) { + Printf("MemorySanitizer: failed to create thread key\n"); + Die(); + } + inited = 1; } } // namespace __msan diff --git a/lib/msan/msan_interface_internal.h b/lib/msan/msan_interface_internal.h index fb57f67c42ed..794b3540f931 100644 --- a/lib/msan/msan_interface_internal.h +++ b/lib/msan/msan_interface_internal.h @@ -73,6 +73,8 @@ void __msan_set_origin(const void *a, uptr size, u32 origin); SANITIZER_INTERFACE_ATTRIBUTE void __msan_set_alloca_origin(void *a, uptr size, const char *descr); SANITIZER_INTERFACE_ATTRIBUTE +void __msan_set_alloca_origin4(void *a, uptr size, const char *descr, uptr pc); +SANITIZER_INTERFACE_ATTRIBUTE u32 __msan_get_origin(const void *a); SANITIZER_INTERFACE_ATTRIBUTE @@ -83,9 +85,12 @@ SANITIZER_INTERFACE_ATTRIBUTE void __msan_set_exit_code(int exit_code); SANITIZER_INTERFACE_ATTRIBUTE +void __msan_set_keep_going(int keep_going); + +SANITIZER_INTERFACE_ATTRIBUTE int __msan_set_poison_in_malloc(int do_poison); -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE /* OPTIONAL */ const char* __msan_default_options(); // For testing. @@ -120,9 +125,51 @@ void __msan_partial_poison(const void* data, void* shadow, uptr size); // Memory will be marked uninitialized, with origin at the call site. SANITIZER_INTERFACE_ATTRIBUTE void __msan_allocated_memory(const void* data, uptr size); -} // extern "C" -// Unpoison first n function arguments. -void __msan_unpoison_param(uptr n); +SANITIZER_INTERFACE_ATTRIBUTE +u16 __sanitizer_unaligned_load16(const uu16 *p); + +SANITIZER_INTERFACE_ATTRIBUTE +u32 __sanitizer_unaligned_load32(const uu32 *p); + +SANITIZER_INTERFACE_ATTRIBUTE +u64 __sanitizer_unaligned_load64(const uu64 *p); + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store16(uu16 *p, u16 x); + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store32(uu32 *p, u32 x); + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store64(uu64 *p, u64 x); + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __msan_get_estimated_allocated_size(uptr size); + +SANITIZER_INTERFACE_ATTRIBUTE +int __msan_get_ownership(const void *p); + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __msan_get_allocated_size(const void *p); + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __msan_get_current_allocated_bytes(); + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __msan_get_heap_size(); + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __msan_get_free_bytes(); + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __msan_get_unmapped_bytes(); + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +/* OPTIONAL */ void __msan_malloc_hook(void *ptr, uptr size); + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +/* OPTIONAL */ void __msan_free_hook(void *ptr); +} // extern "C" #endif // MSAN_INTERFACE_INTERNAL_H diff --git a/lib/msan/msan_linux.cc b/lib/msan/msan_linux.cc index 367dc904d05d..46f501e488c5 100644 --- a/lib/msan/msan_linux.cc +++ b/lib/msan/msan_linux.cc @@ -17,7 +17,6 @@ #include "msan.h" -#include <algorithm> #include <elf.h> #include <link.h> #include <stdio.h> @@ -46,7 +45,13 @@ static const uptr kOriginsBeg = kBad2Beg; static const uptr kOriginsEnd = kBad2End; bool InitShadow(bool prot1, bool prot2, bool map_shadow, bool init_origins) { - if (flags()->verbosity) { + if ((uptr) & InitShadow < kMemBeg) { + Printf("FATAL: Code below application range: %p < %p. Non-PIE build?\n", + &InitShadow, (void *)kMemBeg); + return false; + } + + if (common_flags()->verbosity) { Printf("__msan_init %p\n", &__msan_init); Printf("Memory : %p %p\n", kMemBeg, kMemEnd); Printf("Bad2 : %p %p\n", kBad2Beg, kBad2End); @@ -92,41 +97,6 @@ void InstallAtExitHandler() { atexit(MsanAtExit); } -void UnpoisonMappedDSO(link_map *map) { - typedef ElfW(Phdr) Elf_Phdr; - typedef ElfW(Ehdr) Elf_Ehdr; - char *base = (char *)map->l_addr; - Elf_Ehdr *ehdr = (Elf_Ehdr *)base; - char *phdrs = base + ehdr->e_phoff; - char *phdrs_end = phdrs + ehdr->e_phnum * ehdr->e_phentsize; - - // Find the segment with the minimum base so we can "relocate" the p_vaddr - // fields. Typically ET_DYN objects (DSOs) have base of zero and ET_EXEC - // objects have a non-zero base. - uptr preferred_base = ~0ULL; - for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { - Elf_Phdr *phdr = (Elf_Phdr *)iter; - if (phdr->p_type == PT_LOAD) - preferred_base = std::min(preferred_base, (uptr)phdr->p_vaddr); - } - - // Compute the delta from the real base to get a relocation delta. - sptr delta = (uptr)base - preferred_base; - // Now we can figure out what the loader really mapped. - for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { - Elf_Phdr *phdr = (Elf_Phdr *)iter; - if (phdr->p_type == PT_LOAD) { - uptr seg_start = phdr->p_vaddr + delta; - uptr seg_end = seg_start + phdr->p_memsz; - // None of these values are aligned. We consider the ragged edges of the - // load command as defined, since they are mapped from the file. - seg_start = RoundDownTo(seg_start, GetPageSizeCached()); - seg_end = RoundUpTo(seg_end, GetPageSizeCached()); - __msan_unpoison((void *)seg_start, seg_end - seg_start); - } - } -} - } // namespace __msan #endif // __linux__ diff --git a/lib/msan/msan_new_delete.cc b/lib/msan/msan_new_delete.cc index 88d4364f6562..17687ddfc213 100644 --- a/lib/msan/msan_new_delete.cc +++ b/lib/msan/msan_new_delete.cc @@ -43,7 +43,8 @@ void *operator new(size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } #define OPERATOR_DELETE_BODY \ - if (ptr) MsanDeallocate(ptr) + GET_MALLOC_STACK_TRACE; \ + if (ptr) MsanDeallocate(&stack, ptr) void operator delete(void *ptr) { OPERATOR_DELETE_BODY; } void operator delete[](void *ptr) { OPERATOR_DELETE_BODY; } diff --git a/lib/msan/msan_report.cc b/lib/msan/msan_report.cc index 734fc96fe69f..e3ef99307940 100644 --- a/lib/msan/msan_report.cc +++ b/lib/msan/msan_report.cc @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "msan.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_mutex.h" @@ -24,16 +25,6 @@ using namespace __sanitizer; namespace __msan { -static bool PrintsToTtyCached() { - static int cached = 0; - static bool prints_to_tty; - if (!cached) { // Ok wrt threads since we are printing only from one thread. - prints_to_tty = PrintsToTty(); - cached = 1; - } - return prints_to_tty; -} - class Decorator: private __sanitizer::AnsiColorDecorator { public: Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } @@ -43,17 +34,12 @@ class Decorator: private __sanitizer::AnsiColorDecorator { const char *End() { return Default(); } }; -static void PrintStack(const uptr *trace, uptr size) { - SymbolizerScope sym_scope; - StackTrace::PrintStack(trace, size, true, - common_flags()->strip_path_prefix, 0); -} - static void DescribeOrigin(u32 origin) { Decorator d; - if (flags()->verbosity) + if (common_flags()->verbosity) Printf(" raw origin id: %d\n", origin); - if (const char *so = __msan_get_origin_descr_if_stack(origin)) { + uptr pc; + if (const char *so = GetOriginDescrIfStack(origin, &pc)) { char* s = internal_strdup(so); char* sep = internal_strchr(s, '@'); CHECK(sep); @@ -61,32 +47,25 @@ static void DescribeOrigin(u32 origin) { Printf("%s", d.Origin()); Printf(" %sUninitialized value was created by an allocation of '%s%s%s'" " in the stack frame of function '%s%s%s'%s\n", - d.Origin(), d.Name(), s, d.Origin(), d.Name(), Demangle(sep + 1), - d.Origin(), d.End()); + d.Origin(), d.Name(), s, d.Origin(), d.Name(), + Symbolizer::Get()->Demangle(sep + 1), d.Origin(), d.End()); InternalFree(s); + + if (pc) { + // For some reason function address in LLVM IR is 1 less then the address + // of the first instruction. + pc += 1; + StackTrace::PrintStack(&pc, 1); + } } else { uptr size = 0; const uptr *trace = StackDepotGet(origin, &size); Printf(" %sUninitialized value was created by a heap allocation%s\n", d.Origin(), d.End()); - PrintStack(trace, size); + StackTrace::PrintStack(trace, size); } } -static void ReportSummary(const char *error_type, StackTrace *stack) { - if (!stack->size || !IsSymbolizerAvailable()) return; - AddressInfo ai; - uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); - { - SymbolizerScope sym_scope; - SymbolizeCode(pc, &ai, 1); - } - ReportErrorSummary(error_type, - StripPathPrefix(ai.file, - common_flags()->strip_path_prefix), - ai.line, ai.function); -} - void ReportUMR(StackTrace *stack, u32 origin) { if (!__msan::flags()->report_umrs) return; @@ -94,20 +73,20 @@ void ReportUMR(StackTrace *stack, u32 origin) { Decorator d; Printf("%s", d.Warning()); - Report(" WARNING: Use of uninitialized value\n"); + Report(" WARNING: MemorySanitizer: use-of-uninitialized-value\n"); Printf("%s", d.End()); - PrintStack(stack->trace, stack->size); + StackTrace::PrintStack(stack->trace, stack->size); if (origin) { DescribeOrigin(origin); } - ReportSummary("use-of-uninitialized-value", stack); + ReportErrorSummary("use-of-uninitialized-value", stack); } void ReportExpectedUMRNotFound(StackTrace *stack) { SpinMutexLock l(&CommonSanitizerReportMutex); Printf(" WARNING: Expected use of uninitialized value not found\n"); - PrintStack(stack->trace, stack->size); + StackTrace::PrintStack(stack->trace, stack->size); } void ReportAtExitStatistics() { @@ -119,5 +98,4 @@ void ReportAtExitStatistics() { Printf("%s", d.End()); } - } // namespace __msan diff --git a/lib/msan/tests/CMakeLists.txt b/lib/msan/tests/CMakeLists.txt index 7a784023d2db..9c49f167fa19 100644 --- a/lib/msan/tests/CMakeLists.txt +++ b/lib/msan/tests/CMakeLists.txt @@ -6,7 +6,6 @@ include_directories(..) include_directories(../..) # Instrumented libcxx sources and build flags. -set(MSAN_LIBCXX_PATH ${LLVM_MAIN_SRC_DIR}/projects/libcxx) file(GLOB MSAN_LIBCXX_SOURCES ${MSAN_LIBCXX_PATH}/src/*.cpp) set(MSAN_LIBCXX_CFLAGS -I${MSAN_LIBCXX_PATH}/include @@ -52,6 +51,7 @@ set(MSAN_UNITTEST_COMMON_CFLAGS -fno-exceptions -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer + -Wno-deprecated-declarations ) set(MSAN_UNITTEST_INSTRUMENTED_CFLAGS ${MSAN_UNITTEST_COMMON_CFLAGS} @@ -165,7 +165,7 @@ macro(add_msan_tests_for_arch arch) ${MSAN_INST_LIBCXX} ${MSANDR_TEST_SO}) endmacro() -if(COMPILER_RT_CAN_EXECUTE_TESTS AND EXISTS ${MSAN_LIBCXX_PATH}/) +if(COMPILER_RT_CAN_EXECUTE_TESTS AND MSAN_CAN_INSTRUMENT_LIBCXX) if(CAN_TARGET_x86_64) add_msan_tests_for_arch(x86_64) endif() diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc index 1e05382e4b9f..f95bb4e7c618 100644 --- a/lib/msan/tests/msan_test.cc +++ b/lib/msan/tests/msan_test.cc @@ -19,12 +19,14 @@ #include "sanitizer/msan_interface.h" #include "msandr_test_so.h" +#include <inttypes.h> #include <stdlib.h> #include <stdarg.h> #include <stdio.h> #include <assert.h> #include <wchar.h> #include <math.h> +#include <malloc.h> #include <arpa/inet.h> #include <dlfcn.h> @@ -33,11 +35,14 @@ #include <link.h> #include <limits.h> #include <sys/time.h> +#include <poll.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/resource.h> #include <sys/ioctl.h> +#include <sys/statvfs.h> +#include <sys/sysinfo.h> #include <sys/utsname.h> #include <sys/mman.h> #include <sys/vfs.h> @@ -45,6 +50,11 @@ #include <pwd.h> #include <sys/socket.h> #include <netdb.h> +#include <wordexp.h> +#include <mntent.h> +#include <netinet/ether.h> +#include <sys/ipc.h> +#include <sys/shm.h> #if defined(__i386__) || defined(__x86_64__) # include <emmintrin.h> @@ -53,6 +63,8 @@ # define MSAN_HAS_M128 0 #endif +static const int kPageSize = 4096; + typedef unsigned char U1; typedef unsigned short U2; // NOLINT typedef unsigned int U4; @@ -522,12 +534,42 @@ LargeStruct LargeRetTest() { return res; } +TEST(MemorySanitizer, strcmp) { + char s1[10]; + char s2[10]; + strncpy(s1, "foo", 10); + s2[0] = 'f'; + s2[1] = 'n'; + EXPECT_GT(strcmp(s1, s2), 0); + s2[1] = 'o'; + int res; + EXPECT_UMR(res = strcmp(s1, s2)); + EXPECT_NOT_POISONED(res); + EXPECT_EQ(strncmp(s1, s2, 1), 0); +} + TEST(MemorySanitizer, LargeRet) { LargeStruct a = LargeRetTest(); EXPECT_POISONED(a.x[0]); EXPECT_POISONED(a.x[9]); } +TEST(MemorySanitizer, strerror) { + char *buf = strerror(EINVAL); + EXPECT_NOT_POISONED(strlen(buf)); + buf = strerror(123456); + EXPECT_NOT_POISONED(strlen(buf)); +} + +TEST(MemorySanitizer, strerror_r) { + errno = 0; + char buf[1000]; + char *res = strerror_r(EINVAL, buf, sizeof(buf)); + ASSERT_EQ(0, errno); + if (!res) res = buf; // POSIX version success. + EXPECT_NOT_POISONED(strlen(res)); +} + TEST(MemorySanitizer, fread) { char *x = new char[32]; FILE *f = fopen("/proc/self/stat", "r"); @@ -566,6 +608,52 @@ TEST(MemorySanitizer, pread) { delete x; } +TEST(MemorySanitizer, readv) { + char buf[2011]; + struct iovec iov[2]; + iov[0].iov_base = buf + 1; + iov[0].iov_len = 5; + iov[1].iov_base = buf + 10; + iov[1].iov_len = 2000; + int fd = open("/proc/self/stat", O_RDONLY); + assert(fd > 0); + int sz = readv(fd, iov, 2); + ASSERT_LT(sz, 5 + 2000); + ASSERT_GT(sz, iov[0].iov_len); + EXPECT_POISONED(buf[0]); + EXPECT_NOT_POISONED(buf[1]); + EXPECT_NOT_POISONED(buf[5]); + EXPECT_POISONED(buf[6]); + EXPECT_POISONED(buf[9]); + EXPECT_NOT_POISONED(buf[10]); + EXPECT_NOT_POISONED(buf[10 + (sz - 1) - 5]); + EXPECT_POISONED(buf[11 + (sz - 1) - 5]); + close(fd); +} + +TEST(MemorySanitizer, preadv) { + char buf[2011]; + struct iovec iov[2]; + iov[0].iov_base = buf + 1; + iov[0].iov_len = 5; + iov[1].iov_base = buf + 10; + iov[1].iov_len = 2000; + int fd = open("/proc/self/stat", O_RDONLY); + assert(fd > 0); + int sz = preadv(fd, iov, 2, 3); + ASSERT_LT(sz, 5 + 2000); + ASSERT_GT(sz, iov[0].iov_len); + EXPECT_POISONED(buf[0]); + EXPECT_NOT_POISONED(buf[1]); + EXPECT_NOT_POISONED(buf[5]); + EXPECT_POISONED(buf[6]); + EXPECT_POISONED(buf[9]); + EXPECT_NOT_POISONED(buf[10]); + EXPECT_NOT_POISONED(buf[10 + (sz - 1) - 5]); + EXPECT_POISONED(buf[11 + (sz - 1) - 5]); + close(fd); +} + // FIXME: fails now. TEST(MemorySanitizer, DISABLED_ioctl) { struct winsize ws; @@ -590,13 +678,47 @@ TEST(MemorySanitizer, stat) { EXPECT_NOT_POISONED(st->st_size); } +TEST(MemorySanitizer, fstatat) { + struct stat* st = new struct stat; + int dirfd = open("/proc/self", O_RDONLY); + assert(dirfd > 0); + int res = fstatat(dirfd, "stat", st, 0); + assert(!res); + EXPECT_NOT_POISONED(st->st_dev); + EXPECT_NOT_POISONED(st->st_mode); + EXPECT_NOT_POISONED(st->st_size); + close(dirfd); +} + TEST(MemorySanitizer, statfs) { - struct statfs* st = new struct statfs; - int res = statfs("/", st); + struct statfs st; + int res = statfs("/", &st); assert(!res); - EXPECT_NOT_POISONED(st->f_type); - EXPECT_NOT_POISONED(st->f_bfree); - EXPECT_NOT_POISONED(st->f_namelen); + EXPECT_NOT_POISONED(st.f_type); + EXPECT_NOT_POISONED(st.f_bfree); + EXPECT_NOT_POISONED(st.f_namelen); +} + +TEST(MemorySanitizer, statvfs) { + struct statvfs st; + int res = statvfs("/", &st); + assert(!res); + EXPECT_NOT_POISONED(st.f_bsize); + EXPECT_NOT_POISONED(st.f_blocks); + EXPECT_NOT_POISONED(st.f_bfree); + EXPECT_NOT_POISONED(st.f_namemax); +} + +TEST(MemorySanitizer, fstatvfs) { + struct statvfs st; + int fd = open("/", O_RDONLY | O_DIRECTORY); + int res = fstatvfs(fd, &st); + assert(!res); + EXPECT_NOT_POISONED(st.f_bsize); + EXPECT_NOT_POISONED(st.f_blocks); + EXPECT_NOT_POISONED(st.f_bfree); + EXPECT_NOT_POISONED(st.f_namemax); + close(fd); } TEST(MemorySanitizer, pipe) { @@ -629,6 +751,70 @@ TEST(MemorySanitizer, socketpair) { close(sv[1]); } +TEST(MemorySanitizer, poll) { + int* pipefd = new int[2]; + int res = pipe(pipefd); + ASSERT_EQ(0, res); + + char data = 42; + res = write(pipefd[1], &data, 1); + ASSERT_EQ(1, res); + + pollfd fds[2]; + fds[0].fd = pipefd[0]; + fds[0].events = POLLIN; + fds[1].fd = pipefd[1]; + fds[1].events = POLLIN; + res = poll(fds, 2, 500); + ASSERT_EQ(1, res); + EXPECT_NOT_POISONED(fds[0].revents); + EXPECT_NOT_POISONED(fds[1].revents); + + close(pipefd[0]); + close(pipefd[1]); +} + +TEST(MemorySanitizer, ppoll) { + int* pipefd = new int[2]; + int res = pipe(pipefd); + ASSERT_EQ(0, res); + + char data = 42; + res = write(pipefd[1], &data, 1); + ASSERT_EQ(1, res); + + pollfd fds[2]; + fds[0].fd = pipefd[0]; + fds[0].events = POLLIN; + fds[1].fd = pipefd[1]; + fds[1].events = POLLIN; + sigset_t ss; + sigemptyset(&ss); + res = ppoll(fds, 2, NULL, &ss); + ASSERT_EQ(1, res); + EXPECT_NOT_POISONED(fds[0].revents); + EXPECT_NOT_POISONED(fds[1].revents); + + close(pipefd[0]); + close(pipefd[1]); +} + +TEST(MemorySanitizer, poll_positive) { + int* pipefd = new int[2]; + int res = pipe(pipefd); + ASSERT_EQ(0, res); + + pollfd fds[2]; + fds[0].fd = pipefd[0]; + fds[0].events = POLLIN; + // fds[1].fd uninitialized + fds[1].events = POLLIN; + EXPECT_UMR(poll(fds, 2, 0)); + + close(pipefd[0]); + close(pipefd[1]); +} + TEST(MemorySanitizer, bind_getsockname) { int sock = socket(AF_UNIX, SOCK_STREAM, 0); @@ -651,6 +837,83 @@ TEST(MemorySanitizer, bind_getsockname) { close(sock); } +TEST(MemorySanitizer, accept) { + int listen_socket = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_LT(0, listen_socket); + + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_port = 0; + sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + int res = bind(listen_socket, (struct sockaddr *)&sai, sizeof(sai)); + ASSERT_EQ(0, res); + + res = listen(listen_socket, 1); + ASSERT_EQ(0, res); + + socklen_t sz = sizeof(sai); + res = getsockname(listen_socket, (struct sockaddr *)&sai, &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(sai), sz); + + int connect_socket = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_LT(0, connect_socket); + res = fcntl(connect_socket, F_SETFL, O_NONBLOCK); + ASSERT_EQ(0, res); + res = connect(connect_socket, (struct sockaddr *)&sai, sizeof(sai)); + ASSERT_EQ(-1, res); + ASSERT_EQ(EINPROGRESS, errno); + + __msan_poison(&sai, sizeof(sai)); + int new_sock = accept(listen_socket, (struct sockaddr *)&sai, &sz); + ASSERT_LT(0, new_sock); + ASSERT_EQ(sizeof(sai), sz); + EXPECT_NOT_POISONED(sai); + + __msan_poison(&sai, sizeof(sai)); + res = getpeername(new_sock, (struct sockaddr *)&sai, &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(sai), sz); + EXPECT_NOT_POISONED(sai); + + close(new_sock); + close(connect_socket); + close(listen_socket); +} + +TEST(MemorySanitizer, getaddrinfo) { + struct addrinfo *ai; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + int res = getaddrinfo("localhost", NULL, &hints, &ai); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(*ai); + ASSERT_EQ(sizeof(sockaddr_in), ai->ai_addrlen); + EXPECT_NOT_POISONED(*(sockaddr_in*)ai->ai_addr); +} + +TEST(MemorySanitizer, getnameinfo) { + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_port = 80; + sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + char host[500]; + char serv[500]; + int res = getnameinfo((struct sockaddr *)&sai, sizeof(sai), host, + sizeof(host), serv, sizeof(serv), 0); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(host[0]); + EXPECT_POISONED(host[sizeof(host) - 1]); + + ASSERT_NE(0, strlen(host)); + EXPECT_NOT_POISONED(serv[0]); + EXPECT_POISONED(serv[sizeof(serv) - 1]); + ASSERT_NE(0, strlen(serv)); +} + #define EXPECT_HOSTENT_NOT_POISONED(he) \ do { \ EXPECT_NOT_POISONED(*(he)); \ @@ -677,12 +940,87 @@ TEST(MemorySanitizer, gethostent) { EXPECT_HOSTENT_NOT_POISONED(he); } +#ifndef MSAN_TEST_DISABLE_GETHOSTBYNAME + TEST(MemorySanitizer, gethostbyname) { struct hostent *he = gethostbyname("localhost"); ASSERT_NE((void *)NULL, he); EXPECT_HOSTENT_NOT_POISONED(he); } +#endif // MSAN_TEST_DISABLE_GETHOSTBYNAME + +TEST(MemorySanitizer, recvmsg) { + int server_socket = socket(AF_INET, SOCK_DGRAM, 0); + ASSERT_LT(0, server_socket); + + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_port = 0; + sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + int res = bind(server_socket, (struct sockaddr *)&sai, sizeof(sai)); + ASSERT_EQ(0, res); + + socklen_t sz = sizeof(sai); + res = getsockname(server_socket, (struct sockaddr *)&sai, &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(sai), sz); + + + int client_socket = socket(AF_INET, SOCK_DGRAM, 0); + ASSERT_LT(0, client_socket); + + struct sockaddr_in client_sai; + memset(&client_sai, 0, sizeof(client_sai)); + client_sai.sin_family = AF_INET; + client_sai.sin_port = 0; + client_sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + res = bind(client_socket, (struct sockaddr *)&client_sai, sizeof(client_sai)); + ASSERT_EQ(0, res); + + sz = sizeof(client_sai); + res = getsockname(client_socket, (struct sockaddr *)&client_sai, &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(client_sai), sz); + + + const char *s = "message text"; + struct iovec iov; + iov.iov_base = (void *)s; + iov.iov_len = strlen(s) + 1; + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sai; + msg.msg_namelen = sizeof(sai); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + res = sendmsg(client_socket, &msg, 0); + ASSERT_LT(0, res); + + + char buf[1000]; + struct iovec recv_iov; + recv_iov.iov_base = (void *)&buf; + recv_iov.iov_len = sizeof(buf); + struct sockaddr_in recv_sai; + struct msghdr recv_msg; + memset(&recv_msg, 0, sizeof(recv_msg)); + recv_msg.msg_name = &recv_sai; + recv_msg.msg_namelen = sizeof(recv_sai); + recv_msg.msg_iov = &recv_iov; + recv_msg.msg_iovlen = 1; + res = recvmsg(server_socket, &recv_msg, 0); + ASSERT_LT(0, res); + + ASSERT_EQ(sizeof(recv_sai), recv_msg.msg_namelen); + EXPECT_NOT_POISONED(*(struct sockaddr_in *)recv_msg.msg_name); + EXPECT_STREQ(s, buf); + + close(server_socket); + close(client_socket); +} + TEST(MemorySanitizer, gethostbyname2) { struct hostent *he = gethostbyname2("localhost", AF_INET); ASSERT_NE((void *)NULL, he); @@ -778,6 +1116,96 @@ TEST(MemorySanitizer, getcwd_gnu) { free(res); } +TEST(MemorySanitizer, get_current_dir_name) { + char* res = get_current_dir_name(); + assert(res); + EXPECT_NOT_POISONED(res[0]); + free(res); +} + +TEST(MemorySanitizer, shmctl) { + int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); + ASSERT_GT(id, -1); + + struct shmid_ds ds; + int res = shmctl(id, IPC_STAT, &ds); + ASSERT_GT(res, -1); + EXPECT_NOT_POISONED(ds); + + struct shminfo si; + res = shmctl(id, IPC_INFO, (struct shmid_ds *)&si); + ASSERT_GT(res, -1); + EXPECT_NOT_POISONED(si); + + struct shm_info s_i; + res = shmctl(id, SHM_INFO, (struct shmid_ds *)&s_i); + ASSERT_GT(res, -1); + EXPECT_NOT_POISONED(s_i); + + res = shmctl(id, IPC_RMID, 0); + ASSERT_GT(res, -1); +} + +TEST(MemorySanitizer, shmat) { + void *p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + ASSERT_NE(MAP_FAILED, p); + + ((char *)p)[10] = *GetPoisoned<U1>(); + ((char *)p)[4095] = *GetPoisoned<U1>(); + + int res = munmap(p, 4096); + ASSERT_EQ(0, res); + + int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); + ASSERT_GT(id, -1); + + void *q = shmat(id, p, 0); + ASSERT_EQ(p, q); + + EXPECT_NOT_POISONED(((char *)q)[0]); + EXPECT_NOT_POISONED(((char *)q)[10]); + EXPECT_NOT_POISONED(((char *)q)[4095]); + + res = shmdt(q); + ASSERT_EQ(0, res); + + res = shmctl(id, IPC_RMID, 0); + ASSERT_GT(res, -1); +} + +TEST(MemorySanitizer, random_r) { + int32_t x; + char z[64]; + memset(z, 0, sizeof(z)); + + struct random_data buf; + memset(&buf, 0, sizeof(buf)); + + int res = initstate_r(0, z, sizeof(z), &buf); + ASSERT_EQ(0, res); + + res = random_r(&buf, &x); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(x); +} + +TEST(MemorySanitizer, confstr) { + char buf[3]; + size_t res = confstr(_CS_PATH, buf, sizeof(buf)); + ASSERT_GT(res, sizeof(buf)); + EXPECT_NOT_POISONED(buf[0]); + EXPECT_NOT_POISONED(buf[sizeof(buf) - 1]); + + char buf2[1000]; + res = confstr(_CS_PATH, buf2, sizeof(buf2)); + ASSERT_LT(res, sizeof(buf2)); + EXPECT_NOT_POISONED(buf2[0]); + EXPECT_NOT_POISONED(buf2[res - 1]); + EXPECT_POISONED(buf2[res]); + ASSERT_EQ(res, strlen(buf2) + 1); +} + TEST(MemorySanitizer, readdir) { DIR *dir = opendir("."); struct dirent *d = readdir(dir); @@ -786,6 +1214,17 @@ TEST(MemorySanitizer, readdir) { closedir(dir); } +TEST(MemorySanitizer, readdir_r) { + DIR *dir = opendir("."); + struct dirent d; + struct dirent *pd; + int res = readdir_r(dir, &d, &pd); + assert(!res); + EXPECT_NOT_POISONED(pd); + EXPECT_NOT_POISONED(d.d_name[0]); + closedir(dir); +} + TEST(MemorySanitizer, realpath) { const char* relpath = "."; char path[PATH_MAX + 1]; @@ -794,6 +1233,42 @@ TEST(MemorySanitizer, realpath) { EXPECT_NOT_POISONED(path[0]); } +TEST(MemorySanitizer, realpath_null) { + const char* relpath = "."; + char* res = realpath(relpath, NULL); + printf("%d, %s\n", errno, strerror(errno)); + assert(res); + EXPECT_NOT_POISONED(res[0]); + free(res); +} + +TEST(MemorySanitizer, canonicalize_file_name) { + const char* relpath = "."; + char* res = canonicalize_file_name(relpath); + assert(res); + EXPECT_NOT_POISONED(res[0]); + free(res); +} + +extern char **environ; + +TEST(MemorySanitizer, setenv) { + setenv("AAA", "BBB", 1); + for (char **envp = environ; *envp; ++envp) { + EXPECT_NOT_POISONED(*envp); + EXPECT_NOT_POISONED(*envp[0]); + } +} + +TEST(MemorySanitizer, putenv) { + char s[] = "AAA=BBB"; + putenv(s); + for (char **envp = environ; *envp; ++envp) { + EXPECT_NOT_POISONED(*envp); + EXPECT_NOT_POISONED(*envp[0]); + } +} + TEST(MemorySanitizer, memcpy) { char* x = new char[2]; char* y = new char[2]; @@ -804,6 +1279,35 @@ TEST(MemorySanitizer, memcpy) { EXPECT_POISONED(y[1]); } +void TestUnalignedMemcpy(int left, int right, bool src_is_aligned) { + const int sz = 20; + char *dst = (char *)malloc(sz); + U4 origin = __msan_get_origin(dst); + + char *src = (char *)malloc(sz); + memset(src, 0, sz); + + memcpy(dst + left, src_is_aligned ? src + left : src, sz - left - right); + for (int i = 0; i < left; ++i) + EXPECT_POISONED_O(dst[i], origin); + for (int i = 0; i < right; ++i) + EXPECT_POISONED_O(dst[sz - i - 1], origin); + EXPECT_NOT_POISONED(dst[left]); + EXPECT_NOT_POISONED(dst[sz - right - 1]); + + free(dst); + free(src); +} + +TEST(MemorySanitizer, memcpy_unaligned) { + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 10; ++j) { + TestUnalignedMemcpy(i, j, true); + TestUnalignedMemcpy(i, j, false); + } + } +} + TEST(MemorySanitizer, memmove) { char* x = new char[2]; char* y = new char[2]; @@ -814,6 +1318,63 @@ TEST(MemorySanitizer, memmove) { EXPECT_POISONED(y[1]); } +TEST(MemorySanitizer, memccpy_nomatch) { + char* x = new char[5]; + char* y = new char[5]; + strcpy(x, "abc"); + memccpy(y, x, 'd', 4); + EXPECT_NOT_POISONED(y[0]); + EXPECT_NOT_POISONED(y[1]); + EXPECT_NOT_POISONED(y[2]); + EXPECT_NOT_POISONED(y[3]); + EXPECT_POISONED(y[4]); + delete[] x; + delete[] y; +} + +TEST(MemorySanitizer, memccpy_match) { + char* x = new char[5]; + char* y = new char[5]; + strcpy(x, "abc"); + memccpy(y, x, 'b', 4); + EXPECT_NOT_POISONED(y[0]); + EXPECT_NOT_POISONED(y[1]); + EXPECT_POISONED(y[2]); + EXPECT_POISONED(y[3]); + EXPECT_POISONED(y[4]); + delete[] x; + delete[] y; +} + +TEST(MemorySanitizer, memccpy_nomatch_positive) { + char* x = new char[5]; + char* y = new char[5]; + strcpy(x, "abc"); + EXPECT_UMR(memccpy(y, x, 'd', 5)); + delete[] x; + delete[] y; +} + +TEST(MemorySanitizer, memccpy_match_positive) { + char* x = new char[5]; + char* y = new char[5]; + x[0] = 'a'; + x[2] = 'b'; + EXPECT_UMR(memccpy(y, x, 'b', 5)); + delete[] x; + delete[] y; +} + +TEST(MemorySanitizer, bcopy) { + char* x = new char[2]; + char* y = new char[2]; + x[0] = 1; + x[1] = *GetPoisoned<char>(); + bcopy(x, y, 2); + EXPECT_NOT_POISONED(y[0]); + EXPECT_POISONED(y[1]); +} + TEST(MemorySanitizer, strdup) { char buf[4] = "abc"; __msan_poison(buf + 2, sizeof(*buf)); @@ -896,6 +1457,19 @@ TEST(MemorySanitizer, strncpy) { // NOLINT EXPECT_POISONED(y[2]); } +TEST(MemorySanitizer, stpcpy) { // NOLINT + char* x = new char[3]; + char* y = new char[3]; + x[0] = 'a'; + x[1] = *GetPoisoned<char>(1, 1); + x[2] = 0; + char *res = stpcpy(y, x); // NOLINT + ASSERT_EQ(res, y + 2); + EXPECT_NOT_POISONED(y[0]); + EXPECT_POISONED(y[1]); + EXPECT_NOT_POISONED(y[2]); +} + TEST(MemorySanitizer, strtol) { char *e; assert(1 == strtol("1", &e, 10)); @@ -920,12 +1494,35 @@ TEST(MemorySanitizer, strtoull) { EXPECT_NOT_POISONED((S8) e); } +TEST(MemorySanitizer, strtoimax) { + char *e; + assert(1 == strtoimax("1", &e, 10)); + EXPECT_NOT_POISONED((S8) e); +} + +TEST(MemorySanitizer, strtoumax) { + char *e; + assert(1 == strtoumax("1", &e, 10)); + EXPECT_NOT_POISONED((S8) e); +} + TEST(MemorySanitizer, strtod) { char *e; assert(0 != strtod("1.5", &e)); EXPECT_NOT_POISONED((S8) e); } +#ifdef __GLIBC__ +extern "C" double __strtod_l(const char *nptr, char **endptr, locale_t loc); +TEST(MemorySanitizer, __strtod_l) { + locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); + char *e; + assert(0 != __strtod_l("1.5", &e, loc)); + EXPECT_NOT_POISONED((S8) e); + freelocale(loc); +} +#endif // __GLIBC__ + TEST(MemorySanitizer, strtof) { char *e; assert(0 != strtof("1.5", &e)); @@ -938,6 +1535,121 @@ TEST(MemorySanitizer, strtold) { EXPECT_NOT_POISONED((S8) e); } +TEST(MemorySanitizer, modf) { + double x, y; + x = modf(2.1, &y); + EXPECT_NOT_POISONED(y); +} + +TEST(MemorySanitizer, modff) { + float x, y; + x = modff(2.1, &y); + EXPECT_NOT_POISONED(y); +} + +TEST(MemorySanitizer, modfl) { + long double x, y; + x = modfl(2.1, &y); + EXPECT_NOT_POISONED(y); +} + +TEST(MemorySanitizer, sincos) { + double s, c; + sincos(0.2, &s, &c); + EXPECT_NOT_POISONED(s); + EXPECT_NOT_POISONED(c); +} + +TEST(MemorySanitizer, sincosf) { + float s, c; + sincosf(0.2, &s, &c); + EXPECT_NOT_POISONED(s); + EXPECT_NOT_POISONED(c); +} + +TEST(MemorySanitizer, sincosl) { + long double s, c; + sincosl(0.2, &s, &c); + EXPECT_NOT_POISONED(s); + EXPECT_NOT_POISONED(c); +} + +TEST(MemorySanitizer, remquo) { + int quo; + double res = remquo(29.0, 3.0, &quo); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(quo); +} + +TEST(MemorySanitizer, remquof) { + int quo; + float res = remquof(29.0, 3.0, &quo); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(quo); +} + +TEST(MemorySanitizer, remquol) { + int quo; + long double res = remquof(29.0, 3.0, &quo); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(quo); +} + +TEST(MemorySanitizer, lgamma) { + double res = lgamma(1.1); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(signgam); +} + +TEST(MemorySanitizer, lgammaf) { + float res = lgammaf(1.1); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(signgam); +} + +TEST(MemorySanitizer, lgammal) { + long double res = lgammal(1.1); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(signgam); +} + +TEST(MemorySanitizer, lgamma_r) { + int sgn; + double res = lgamma_r(1.1, &sgn); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(sgn); +} + +TEST(MemorySanitizer, lgammaf_r) { + int sgn; + float res = lgammaf_r(1.1, &sgn); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(sgn); +} + +TEST(MemorySanitizer, lgammal_r) { + int sgn; + long double res = lgammal_r(1.1, &sgn); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(sgn); +} + +TEST(MemorySanitizer, drand48_r) { + struct drand48_data buf; + srand48_r(0, &buf); + double d; + drand48_r(&buf, &d); + EXPECT_NOT_POISONED(d); +} + +TEST(MemorySanitizer, lrand48_r) { + struct drand48_data buf; + srand48_r(0, &buf); + long d; + lrand48_r(&buf, &d); + EXPECT_NOT_POISONED(d); +} + TEST(MemorySanitizer, sprintf) { // NOLINT char buff[10]; break_optimization(buff); @@ -981,6 +1693,33 @@ TEST(MemorySanitizer, swprintf) { EXPECT_POISONED(buff[8]); } +TEST(MemorySanitizer, asprintf) { // NOLINT + char *pbuf; + EXPECT_POISONED(pbuf); + int res = asprintf(&pbuf, "%d", 1234567); // NOLINT + assert(res == 7); + EXPECT_NOT_POISONED(pbuf); + assert(pbuf[0] == '1'); + assert(pbuf[1] == '2'); + assert(pbuf[2] == '3'); + assert(pbuf[6] == '7'); + assert(pbuf[7] == 0); + free(pbuf); +} + +TEST(MemorySanitizer, mbstowcs) { + const char *x = "abc"; + wchar_t buff[10]; + int res = mbstowcs(buff, x, 2); + EXPECT_EQ(2, res); + EXPECT_EQ(L'a', buff[0]); + EXPECT_EQ(L'b', buff[1]); + EXPECT_POISONED(buff[2]); + res = mbstowcs(buff, x, 10); + EXPECT_EQ(3, res); + EXPECT_NOT_POISONED(buff[3]); +} + TEST(MemorySanitizer, wcstombs) { const wchar_t *x = L"abc"; char buff[10]; @@ -991,6 +1730,52 @@ TEST(MemorySanitizer, wcstombs) { EXPECT_EQ(buff[2], 'c'); } +TEST(MemorySanitizer, wcsrtombs) { + const wchar_t *x = L"abc"; + const wchar_t *p = x; + char buff[10]; + mbstate_t mbs; + memset(&mbs, 0, sizeof(mbs)); + int res = wcsrtombs(buff, &p, 4, &mbs); + EXPECT_EQ(res, 3); + EXPECT_EQ(buff[0], 'a'); + EXPECT_EQ(buff[1], 'b'); + EXPECT_EQ(buff[2], 'c'); + EXPECT_EQ(buff[3], '\0'); + EXPECT_POISONED(buff[4]); +} + +TEST(MemorySanitizer, wcsnrtombs) { + const wchar_t *x = L"abc"; + const wchar_t *p = x; + char buff[10]; + mbstate_t mbs; + memset(&mbs, 0, sizeof(mbs)); + int res = wcsnrtombs(buff, &p, 2, 4, &mbs); + EXPECT_EQ(res, 2); + EXPECT_EQ(buff[0], 'a'); + EXPECT_EQ(buff[1], 'b'); + EXPECT_POISONED(buff[2]); +} + +TEST(MemorySanitizer, mbtowc) { + const char *x = "abc"; + wchar_t wx; + int res = mbtowc(&wx, x, 3); + EXPECT_GT(res, 0); + EXPECT_NOT_POISONED(wx); +} + +TEST(MemorySanitizer, mbrtowc) { + const char *x = "abc"; + wchar_t wx; + mbstate_t mbs; + memset(&mbs, 0, sizeof(mbs)); + int res = mbrtowc(&wx, x, 3, &mbs); + EXPECT_GT(res, 0); + EXPECT_NOT_POISONED(wx); +} + TEST(MemorySanitizer, gettimeofday) { struct timeval tv; struct timezone tz; @@ -1060,6 +1845,13 @@ TEST(MemorySanitizer, getitimer) { assert(!res); } +TEST(MemorySanitizer, setitimer_null) { + setitimer(ITIMER_VIRTUAL, 0, 0); + // Not testing the return value, since it the behaviour seems to differ + // between libc implementations and POSIX. + // Should never crash, though. +} + TEST(MemorySanitizer, time) { time_t t; EXPECT_POISONED(t); @@ -1068,6 +1860,15 @@ TEST(MemorySanitizer, time) { EXPECT_NOT_POISONED(t); } +TEST(MemorySanitizer, strptime) { + struct tm time; + char *p = strptime("11/1/2013-05:39", "%m/%d/%Y-%H:%M", &time); + assert(p != 0); + EXPECT_NOT_POISONED(time.tm_sec); + EXPECT_NOT_POISONED(time.tm_hour); + EXPECT_NOT_POISONED(time.tm_year); +} + TEST(MemorySanitizer, localtime) { time_t t = 123; struct tm *time = localtime(&t); @@ -1076,6 +1877,7 @@ TEST(MemorySanitizer, localtime) { EXPECT_NOT_POISONED(time->tm_hour); EXPECT_NOT_POISONED(time->tm_year); EXPECT_NOT_POISONED(time->tm_isdst); + EXPECT_NE(0, strlen(time->tm_zone)); } TEST(MemorySanitizer, localtime_r) { @@ -1087,6 +1889,54 @@ TEST(MemorySanitizer, localtime_r) { EXPECT_NOT_POISONED(time.tm_hour); EXPECT_NOT_POISONED(time.tm_year); EXPECT_NOT_POISONED(time.tm_isdst); + EXPECT_NE(0, strlen(time.tm_zone)); +} + +TEST(MemorySanitizer, getmntent) { + FILE *fp = setmntent("/etc/fstab", "r"); + struct mntent *mnt = getmntent(fp); + ASSERT_NE((void *)0, mnt); + ASSERT_NE(0, strlen(mnt->mnt_fsname)); + ASSERT_NE(0, strlen(mnt->mnt_dir)); + ASSERT_NE(0, strlen(mnt->mnt_type)); + ASSERT_NE(0, strlen(mnt->mnt_opts)); + EXPECT_NOT_POISONED(mnt->mnt_freq); + EXPECT_NOT_POISONED(mnt->mnt_passno); + fclose(fp); +} + +TEST(MemorySanitizer, getmntent_r) { + FILE *fp = setmntent("/etc/fstab", "r"); + struct mntent mntbuf; + char buf[1000]; + struct mntent *mnt = getmntent_r(fp, &mntbuf, buf, sizeof(buf)); + ASSERT_NE((void *)0, mnt); + ASSERT_NE(0, strlen(mnt->mnt_fsname)); + ASSERT_NE(0, strlen(mnt->mnt_dir)); + ASSERT_NE(0, strlen(mnt->mnt_type)); + ASSERT_NE(0, strlen(mnt->mnt_opts)); + EXPECT_NOT_POISONED(mnt->mnt_freq); + EXPECT_NOT_POISONED(mnt->mnt_passno); + fclose(fp); +} + +TEST(MemorySanitizer, ether) { + const char *asc = "11:22:33:44:55:66"; + struct ether_addr *paddr = ether_aton(asc); + EXPECT_NOT_POISONED(*paddr); + + struct ether_addr addr; + paddr = ether_aton_r(asc, &addr); + ASSERT_EQ(paddr, &addr); + EXPECT_NOT_POISONED(addr); + + char *s = ether_ntoa(&addr); + ASSERT_NE(0, strlen(s)); + + char buf[100]; + s = ether_ntoa_r(&addr, buf); + ASSERT_EQ(s, buf); + ASSERT_NE(0, strlen(buf)); } TEST(MemorySanitizer, mmap) { @@ -1201,6 +2051,39 @@ TEST(MemorySanitizer, sigaction) { } // namespace + +TEST(MemorySanitizer, sigemptyset) { + sigset_t s; + EXPECT_POISONED(s); + int res = sigemptyset(&s); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(s); +} + +TEST(MemorySanitizer, sigfillset) { + sigset_t s; + EXPECT_POISONED(s); + int res = sigfillset(&s); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(s); +} + +TEST(MemorySanitizer, sigpending) { + sigset_t s; + EXPECT_POISONED(s); + int res = sigpending(&s); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(s); +} + +TEST(MemorySanitizer, sigprocmask) { + sigset_t s; + EXPECT_POISONED(s); + int res = sigprocmask(SIG_BLOCK, 0, &s); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(s); +} + struct StructWithDtor { ~StructWithDtor(); }; @@ -1392,22 +2275,33 @@ TEST(MemorySanitizer, VAArgOverflow) { static void vaargsfn_tlsoverwrite2(int guard, ...) { va_list vl; va_start(vl, guard); - EXPECT_NOT_POISONED(va_arg(vl, int)); + for (int i = 0; i < 20; ++i) + EXPECT_NOT_POISONED(va_arg(vl, int)); va_end(vl); } static void vaargsfn_tlsoverwrite(int guard, ...) { // This call will overwrite TLS contents unless it's backed up somewhere. - vaargsfn_tlsoverwrite2(2, 42); + vaargsfn_tlsoverwrite2(2, + 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42); // 20x va_list vl; va_start(vl, guard); - EXPECT_POISONED(va_arg(vl, int)); + for (int i = 0; i < 20; ++i) + EXPECT_POISONED(va_arg(vl, int)); va_end(vl); } TEST(MemorySanitizer, VAArgTLSOverwrite) { int* x = GetPoisoned<int>(); - vaargsfn_tlsoverwrite(1, *x); + vaargsfn_tlsoverwrite(1, + *x, *x, *x, *x, *x, + *x, *x, *x, *x, *x, + *x, *x, *x, *x, *x, + *x, *x, *x, *x, *x); // 20x + } struct StructByVal { @@ -1616,18 +2510,6 @@ extern char *program_invocation_name; # error "TODO: port this" #endif -// Compute the path to our loadable DSO. We assume it's in the same -// directory. Only use string routines that we intercept so far to do this. -static int PathToLoadable(char *buf, size_t sz) { - const char *basename = "libmsan_loadable.x86_64.so"; - char *argv0 = program_invocation_name; - char *last_slash = strrchr(argv0, '/'); - assert(last_slash); - int res = - snprintf(buf, sz, "%.*s/%s", int(last_slash - argv0), argv0, basename); - return res < sz ? 0 : res; -} - static void dladdr_testfn() {} TEST(MemorySanitizer, dladdr) { @@ -1645,6 +2527,8 @@ TEST(MemorySanitizer, dladdr) { EXPECT_NOT_POISONED((unsigned long)info.dli_saddr); } +#ifndef MSAN_TEST_DISABLE_DLOPEN + static int dl_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) { (*(int *)data)++; EXPECT_NOT_POISONED(info->dlpi_addr); @@ -1655,6 +2539,18 @@ static int dl_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) return 0; } +// Compute the path to our loadable DSO. We assume it's in the same +// directory. Only use string routines that we intercept so far to do this. +static int PathToLoadable(char *buf, size_t sz) { + const char *basename = "libmsan_loadable.x86_64.so"; + char *argv0 = program_invocation_name; + char *last_slash = strrchr(argv0, '/'); + assert(last_slash); + int res = + snprintf(buf, sz, "%.*s/%s", int(last_slash - argv0), argv0, basename); + return res < sz ? 0 : res; +} + TEST(MemorySanitizer, dl_iterate_phdr) { char path[4096]; int res = PathToLoadable(path, sizeof(path)); @@ -1706,6 +2602,15 @@ TEST(MemorySanitizer, dlopenFailed) { ASSERT_EQ(0, lib); } +#endif // MSAN_TEST_DISABLE_DLOPEN + +TEST(MemorySanitizer, sched_getaffinity) { + cpu_set_t mask; + int res = sched_getaffinity(getpid(), sizeof(mask), &mask); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(mask); +} + TEST(MemorySanitizer, scanf) { const char *input = "42 hello"; int* d = new int; @@ -1737,8 +2642,7 @@ TEST(MemorySanitizer, SimpleThread) { EXPECT_NOT_POISONED(t); res = pthread_join(t, &p); assert(!res); - if (!__msan_has_dynamic_component()) // FIXME: intercept pthread_join (?). - __msan_unpoison(&p, sizeof(p)); + EXPECT_NOT_POISONED(p); delete (int*)p; } @@ -1783,6 +2687,71 @@ TEST(MemorySanitizer, PreAllocatedStackThread) { ASSERT_EQ(0, res); } +TEST(MemorySanitizer, pthread_attr_get) { + pthread_attr_t attr; + int res; + res = pthread_attr_init(&attr); + ASSERT_EQ(0, res); + { + int v; + res = pthread_attr_getdetachstate(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + size_t v; + res = pthread_attr_getguardsize(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + struct sched_param v; + res = pthread_attr_getschedparam(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + int v; + res = pthread_attr_getschedpolicy(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + int v; + res = pthread_attr_getinheritsched(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + int v; + res = pthread_attr_getscope(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + size_t v; + res = pthread_attr_getstacksize(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + void *v; + size_t w; + res = pthread_attr_getstack(&attr, &v, &w); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + EXPECT_NOT_POISONED(w); + } + { + cpu_set_t v; + res = pthread_attr_getaffinity_np(&attr, sizeof(v), &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + res = pthread_attr_destroy(&attr); + ASSERT_EQ(0, res); +} + TEST(MemorySanitizer, pthread_getschedparam) { int policy; struct sched_param param; @@ -1792,12 +2761,105 @@ TEST(MemorySanitizer, pthread_getschedparam) { EXPECT_NOT_POISONED(param.sched_priority); } +TEST(MemorySanitizer, pthread_key_create) { + pthread_key_t key; + int res = pthread_key_create(&key, NULL); + assert(!res); + EXPECT_NOT_POISONED(key); + res = pthread_key_delete(key); + assert(!res); +} + +namespace { +struct SignalCondArg { + pthread_cond_t* cond; + pthread_mutex_t* mu; + bool broadcast; +}; + +void *SignalCond(void *param) { + SignalCondArg *arg = reinterpret_cast<SignalCondArg *>(param); + pthread_mutex_lock(arg->mu); + if (arg->broadcast) + pthread_cond_broadcast(arg->cond); + else + pthread_cond_signal(arg->cond); + pthread_mutex_unlock(arg->mu); + return 0; +} +} // namespace + +TEST(MemorySanitizer, pthread_cond_wait) { + pthread_cond_t cond; + pthread_mutex_t mu; + SignalCondArg args = {&cond, &mu, false}; + pthread_cond_init(&cond, 0); + pthread_mutex_init(&mu, 0); + pthread_mutex_lock(&mu); + + // signal + pthread_t thr; + pthread_create(&thr, 0, SignalCond, &args); + int res = pthread_cond_wait(&cond, &mu); + assert(!res); + pthread_join(thr, 0); + + // broadcast + args.broadcast = true; + pthread_create(&thr, 0, SignalCond, &args); + res = pthread_cond_wait(&cond, &mu); + assert(!res); + pthread_join(thr, 0); + + pthread_mutex_unlock(&mu); + pthread_mutex_destroy(&mu); + pthread_cond_destroy(&cond); +} + +TEST(MemorySanitizer, tmpnam) { + char s[L_tmpnam]; + char *res = tmpnam(s); + ASSERT_EQ(s, res); + EXPECT_NOT_POISONED(strlen(res)); +} + +TEST(MemorySanitizer, tempnam) { + char *res = tempnam(NULL, "zzz"); + EXPECT_NOT_POISONED(strlen(res)); + free(res); +} + TEST(MemorySanitizer, posix_memalign) { void *p; EXPECT_POISONED(p); int res = posix_memalign(&p, 4096, 13); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(p); + EXPECT_EQ(0U, (uintptr_t)p % 4096); + free(p); +} + +TEST(MemorySanitizer, memalign) { + void *p = memalign(4096, 13); + EXPECT_EQ(0U, (uintptr_t)p % kPageSize); + free(p); +} + +TEST(MemorySanitizer, valloc) { + void *a = valloc(100); + EXPECT_EQ(0U, (uintptr_t)a % kPageSize); + free(a); +} + +TEST(MemorySanitizer, pvalloc) { + void *p = pvalloc(kPageSize + 100); + EXPECT_EQ(0U, (uintptr_t)p % kPageSize); + EXPECT_EQ(2 * kPageSize, __msan_get_allocated_size(p)); + free(p); + + p = pvalloc(0); // pvalloc(0) should allocate at least one page. + EXPECT_EQ(0U, (uintptr_t)p % kPageSize); + EXPECT_EQ(kPageSize, __msan_get_allocated_size(p)); free(p); } @@ -1816,6 +2878,15 @@ TEST(MemorySanitizer, inet_pton) { EXPECT_NOT_POISONED(s_out[3]); } +TEST(MemorySanitizer, inet_aton) { + const char *s = "127.0.0.1"; + struct in_addr in[2]; + int res = inet_aton(s, in); + ASSERT_NE(0, res); + EXPECT_NOT_POISONED(in[0]); + EXPECT_POISONED(*(char *)(in + 1)); +} + TEST(MemorySanitizer, uname) { struct utsname u; int res = uname(&u); @@ -1834,6 +2905,13 @@ TEST(MemorySanitizer, gethostname) { EXPECT_NOT_POISONED(strlen(buf)); } +TEST(MemorySanitizer, sysinfo) { + struct sysinfo info; + int res = sysinfo(&info); + assert(!res); + EXPECT_NOT_POISONED(info); +} + TEST(MemorySanitizer, getpwuid) { struct passwd *p = getpwuid(0); // root assert(p); @@ -1880,6 +2958,25 @@ TEST(MemorySanitizer, getgrnam_r) { EXPECT_NOT_POISONED(grp.gr_gid); } +TEST(MemorySanitizer, getgroups) { + int n = getgroups(0, 0); + gid_t *gids = new gid_t[n]; + int res = getgroups(n, gids); + ASSERT_EQ(n, res); + for (int i = 0; i < n; ++i) + EXPECT_NOT_POISONED(gids[i]); +} + +TEST(MemorySanitizer, wordexp) { + wordexp_t w; + int res = wordexp("a b c", &w, 0); + ASSERT_EQ(0, res); + ASSERT_EQ(3, w.we_wordc); + ASSERT_STREQ("a", w.we_wordv[0]); + ASSERT_STREQ("b", w.we_wordv[1]); + ASSERT_STREQ("c", w.we_wordv[2]); +} + template<class T> static bool applySlt(T value, T shadow) { __msan_partial_poison(&value, &shadow, sizeof(T)); @@ -1986,6 +3083,83 @@ TEST(MemorySanitizer, VolatileBitfield) { EXPECT_POISONED((unsigned)S->y); } +TEST(MemorySanitizer, UnalignedLoad) { + char x[32]; + memset(x + 8, 0, 16); + EXPECT_POISONED(__sanitizer_unaligned_load16(x+6)); + EXPECT_POISONED(__sanitizer_unaligned_load16(x+7)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x+8)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x+9)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x+22)); + EXPECT_POISONED(__sanitizer_unaligned_load16(x+23)); + EXPECT_POISONED(__sanitizer_unaligned_load16(x+24)); + + EXPECT_POISONED(__sanitizer_unaligned_load32(x+4)); + EXPECT_POISONED(__sanitizer_unaligned_load32(x+7)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x+8)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x+9)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x+20)); + EXPECT_POISONED(__sanitizer_unaligned_load32(x+21)); + EXPECT_POISONED(__sanitizer_unaligned_load32(x+24)); + + EXPECT_POISONED(__sanitizer_unaligned_load64(x)); + EXPECT_POISONED(__sanitizer_unaligned_load64(x+1)); + EXPECT_POISONED(__sanitizer_unaligned_load64(x+7)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x+8)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x+9)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x+16)); + EXPECT_POISONED(__sanitizer_unaligned_load64(x+17)); + EXPECT_POISONED(__sanitizer_unaligned_load64(x+21)); + EXPECT_POISONED(__sanitizer_unaligned_load64(x+24)); +} + +TEST(MemorySanitizer, UnalignedStore16) { + char x[5]; + U2 y = 0; + __msan_poison(&y, 1); + __sanitizer_unaligned_store16(x + 1, y); + EXPECT_POISONED(x[0]); + EXPECT_POISONED(x[1]); + EXPECT_NOT_POISONED(x[2]); + EXPECT_POISONED(x[3]); + EXPECT_POISONED(x[4]); +} + +TEST(MemorySanitizer, UnalignedStore32) { + char x[8]; + U4 y4 = 0; + __msan_poison(&y4, 2); + __sanitizer_unaligned_store32(x+3, y4); + EXPECT_POISONED(x[0]); + EXPECT_POISONED(x[1]); + EXPECT_POISONED(x[2]); + EXPECT_POISONED(x[3]); + EXPECT_POISONED(x[4]); + EXPECT_NOT_POISONED(x[5]); + EXPECT_NOT_POISONED(x[6]); + EXPECT_POISONED(x[7]); +} + +TEST(MemorySanitizer, UnalignedStore64) { + char x[16]; + U8 y = 0; + __msan_poison(&y, 3); + __msan_poison(((char *)&y) + sizeof(y) - 2, 1); + __sanitizer_unaligned_store64(x+3, y); + EXPECT_POISONED(x[0]); + EXPECT_POISONED(x[1]); + EXPECT_POISONED(x[2]); + EXPECT_POISONED(x[3]); + EXPECT_POISONED(x[4]); + EXPECT_POISONED(x[5]); + EXPECT_NOT_POISONED(x[6]); + EXPECT_NOT_POISONED(x[7]); + EXPECT_NOT_POISONED(x[8]); + EXPECT_POISONED(x[9]); + EXPECT_NOT_POISONED(x[10]); + EXPECT_POISONED(x[11]); +} + TEST(MemorySanitizerDr, StoreInDSOTest) { if (!__msan_has_dynamic_component()) return; char* s = new char[10]; @@ -2181,6 +3355,7 @@ void MemCpyTest() { T *x = new T[N]; T *y = new T[N]; T *z = new T[N]; + T *q = new T[N]; __msan_poison(x, N * sizeof(T)); __msan_set_origin(x, N * sizeof(T), ox); __msan_set_origin(y, N * sizeof(T), 777777); @@ -2191,6 +3366,12 @@ void MemCpyTest() { EXPECT_POISONED_O(y[N/2], ox); EXPECT_POISONED_O(y[N-1], ox); EXPECT_NOT_POISONED(x); + void *res = mempcpy(q, x, N * sizeof(T)); + ASSERT_EQ(q + N, res); + EXPECT_POISONED_O(q[0], ox); + EXPECT_POISONED_O(q[N/2], ox); + EXPECT_POISONED_O(q[N-1], ox); + EXPECT_NOT_POISONED(x); memmove(z, x, N * sizeof(T)); EXPECT_POISONED_O(z[0], ox); EXPECT_POISONED_O(z[N/2], ox); @@ -2325,14 +3506,56 @@ NOINLINE void RecursiveMalloc(int depth) { delete x2; } -TEST(MemorySanitizer, CallocOverflow) { - size_t kArraySize = 4096; - volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); - volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; - void *p = calloc(kArraySize, kArraySize2); // Should return 0. - EXPECT_EQ(0L, Ident(p)); +TEST(MemorySanitizer, Select) { + int x; + int volatile* p = &x; + int z = *p ? 1 : 0; + EXPECT_POISONED(z); } TEST(MemorySanitizerStress, DISABLED_MallocStackTrace) { RecursiveMalloc(22); } + +TEST(MemorySanitizerAllocator, get_estimated_allocated_size) { + size_t sizes[] = {0, 20, 5000, 1<<20}; + for (size_t i = 0; i < sizeof(sizes) / sizeof(*sizes); ++i) { + size_t alloc_size = __msan_get_estimated_allocated_size(sizes[i]); + EXPECT_EQ(alloc_size, sizes[i]); + } +} + +TEST(MemorySanitizerAllocator, get_allocated_size_and_ownership) { + char *array = reinterpret_cast<char*>(malloc(100)); + int *int_ptr = new int; + + EXPECT_TRUE(__msan_get_ownership(array)); + EXPECT_EQ(100, __msan_get_allocated_size(array)); + + EXPECT_TRUE(__msan_get_ownership(int_ptr)); + EXPECT_EQ(sizeof(*int_ptr), __msan_get_allocated_size(int_ptr)); + + void *wild_addr = reinterpret_cast<void*>(0x1); + EXPECT_FALSE(__msan_get_ownership(wild_addr)); + EXPECT_EQ(0, __msan_get_allocated_size(wild_addr)); + + EXPECT_FALSE(__msan_get_ownership(array + 50)); + EXPECT_EQ(0, __msan_get_allocated_size(array + 50)); + + // NULL is a valid argument for GetAllocatedSize but is not owned. + EXPECT_FALSE(__msan_get_ownership(NULL)); + EXPECT_EQ(0, __msan_get_allocated_size(NULL)); + + free(array); + EXPECT_FALSE(__msan_get_ownership(array)); + EXPECT_EQ(0, __msan_get_allocated_size(array)); + + delete int_ptr; +} + +TEST(MemorySanitizer, MlockTest) { + EXPECT_EQ(0, mlockall(MCL_CURRENT)); + EXPECT_EQ(0, mlock((void*)0x12345, 0x5678)); + EXPECT_EQ(0, munlockall()); + EXPECT_EQ(0, munlock((void*)0x987, 0x654)); +} diff --git a/lib/msandr/README.txt b/lib/msandr/README.txt index b328910c53ba..81a87c694083 100644 --- a/lib/msandr/README.txt +++ b/lib/msandr/README.txt @@ -6,7 +6,8 @@ Building: (svn co https://dynamorio.googlecode.com/svn/trunk dr && \ cd dr && mkdir build && cd build && \ cmake -DDR_EXT_DRMGR_STATIC=ON -DDR_EXT_DRSYMS_STATIC=ON \ - -DDR_EXT_DRUTIL_STATIC=ON -DDR_EXT_DRWRAP_STATIC=ON .. && \ + -DDR_EXT_DRUTIL_STATIC=ON -DDR_EXT_DRWRAP_STATIC=ON \ + -DDR_EXT_DRX_STATIC=ON .. && \ make -j10 && make install) 2. Download and build DrMemory (for DrSyscall extension) @@ -31,3 +32,9 @@ Running: <path_to_dynamorio>/exports/bin64/drrun -c lib/clang/$VERSION/lib/linux/libclang_rt.msandr-x86_64.so -- test_binary MSan unit tests contain several tests for MSanDR (use MemorySanitizerDr.* gtest filter). + +Debugging: + Add -DCMAKE_BUILD_TYPE=Debug to the first and/or second cmake invocation(s). + Add -debug -v to drrun invocation line (right before -c). + Add -checklevel 1 to drrun (as the first argument) to make debug DR faster. + diff --git a/lib/msandr/msandr.cc b/lib/msandr/msandr.cc index 27b1c9427d9d..6c9d7c675aca 100644 --- a/lib/msandr/msandr.cc +++ b/lib/msandr/msandr.cc @@ -60,8 +60,43 @@ #define VERBOSITY 0 +// XXX: it seems setting macro in CMakeLists.txt does not work, +// so manually set it here now. + +// Building msandr client for running in DynamoRIO hybrid mode, +// which allows some module running natively. +// TODO: turn it on by default when hybrid is stable enough +// #define MSANDR_NATIVE_EXEC + +// Building msandr client for standalone test that does not need to +// run with msan build executables. Disable by default. +// #define MSANDR_STANDALONE_TEST + +#define NUM_TLS_RETVAL 1 +#define NUM_TLS_PARAM 6 + +#ifdef MSANDR_STANDALONE_TEST +// For testing purpose, we map app to shadow memory at [0x100000, 0x20000). +// Normally, the app starts at 0x400000: +// 00400000-004e0000 r-xp 00000000 fc:00 524343 /bin/bash +// so there should be no problem. +# define SHADOW_MEMORY_BASE ((void *)0x100000) +# define SHADOW_MEMORY_SIZE (0x100000) +# define SHADOW_MEMORY_MASK (SHADOW_MEMORY_SIZE - 4 /* to avoid overflow */) +#else +// shadow memory range [0x200000000000, 0x400000000000) +// assuming no app memory below 0x200000000000 +# define SHADOW_MEMORY_MASK 0x3fffffffffffULL +#endif /* MSANDR_STANDALONE_TEST */ + namespace { +std::string g_app_path; + +int msan_retval_tls_offset; +int msan_param_tls_offset; + +#ifndef MSANDR_NATIVE_EXEC class ModuleData { public: ModuleData(); @@ -78,11 +113,6 @@ public: bool executed_; }; -std::string g_app_path; - -int msan_retval_tls_offset; -int msan_param_tls_offset; - // A vector of loaded modules sorted by module bounds. We lookup the current PC // in here from the bb event. This is better than an rb tree because the lookup // is faster and the bb event occurs far more than the module load event. @@ -99,19 +129,54 @@ ModuleData::ModuleData(const module_data_t *info) // We'll check the black/white lists later and adjust this. should_instrument_(true), executed_(false) { } +#endif /* !MSANDR_NATIVE_EXEC */ int(*__msan_get_retval_tls_offset)(); int(*__msan_get_param_tls_offset)(); void (*__msan_unpoison)(void *base, size_t size); bool (*__msan_is_in_loader)(); +#ifdef MSANDR_STANDALONE_TEST +uint mock_msan_retval_tls_offset; +uint mock_msan_param_tls_offset; +static int mock_msan_get_retval_tls_offset() { + return (int)mock_msan_retval_tls_offset; +} + +static int mock_msan_get_param_tls_offset() { + return (int)mock_msan_param_tls_offset; +} + +static void mock_msan_unpoison(void *base, size_t size) { + /* do nothing */ +} + +static bool mock_msan_is_in_loader() { + return false; +} +#endif /* MSANDR_STANDALONE_TEST */ + static generic_func_t LookupCallback(module_data_t *app, const char *name) { +#ifdef MSANDR_STANDALONE_TEST + if (strcmp("__msan_get_retval_tls_offset", name) == 0) { + return (generic_func_t)mock_msan_get_retval_tls_offset; + } else if (strcmp("__msan_get_param_tls_offset", name) == 0) { + return (generic_func_t)mock_msan_get_param_tls_offset; + } else if (strcmp("__msan_unpoison", name) == 0) { + return (generic_func_t)mock_msan_unpoison; + } else if (strcmp("__msan_is_in_loader", name) == 0) { + return (generic_func_t)mock_msan_is_in_loader; + } + CHECK(false); + return NULL; +#else /* !MSANDR_STANDALONE_TEST */ generic_func_t callback = dr_get_proc_address(app->handle, name); if (callback == NULL) { dr_printf("Couldn't find `%s` in %s\n", name, app->full_path); CHECK(callback); } return callback; +#endif /* !MSANDR_STANDALONE_TEST */ } void InitializeMSanCallbacks() { @@ -217,21 +282,14 @@ void InstrumentMops(void *drcontext, instrlist_t *bb, instr_t *instr, opnd_t op, } CHECK(reg_is_pointer_sized(R1)); // otherwise R2 may be wrong. - // Pick R2 that's not R1 or used by the operand. It's OK if the instr uses - // R2 elsewhere, since we'll restore it before instr. - reg_id_t GPR_TO_USE_FOR_R2[] = { - DR_REG_XAX, DR_REG_XBX, DR_REG_XCX, DR_REG_XDX - // Don't forget to update the +4 below if you add anything else! - }; - std::set<reg_id_t> unused_registers(GPR_TO_USE_FOR_R2, GPR_TO_USE_FOR_R2 + 4); - unused_registers.erase(R1); - for (int j = 0; j < opnd_num_regs_used(op); j++) { - unused_registers.erase(opnd_get_reg_used(op, j)); + // Pick R2 from R8 to R15. + // It's OK if the instr uses R2 elsewhere, since we'll restore it before instr. + reg_id_t R2; + for (R2 = DR_REG_R8; R2 <= DR_REG_R15; R2++) { + if (!opnd_uses_reg(op, R2)) + break; } - - CHECK(unused_registers.size() > 0); - reg_id_t R2 = *unused_registers.begin(); - CHECK(R1 != R2); + CHECK((R2 <= DR_REG_R15) && R1 != R2); // Save the current values of R1 and R2. dr_save_reg(drcontext, bb, instr, R1, SPILL_SLOT_1); @@ -241,21 +299,41 @@ void InstrumentMops(void *drcontext, instrlist_t *bb, instr_t *instr, opnd_t op, if (!address_in_R1) CHECK(drutil_insert_get_mem_addr(drcontext, bb, instr, op, R1, R2)); PRE(instr, mov_imm(drcontext, opnd_create_reg(R2), - OPND_CREATE_INT64(0xffffbfffffffffff))); + OPND_CREATE_INT64(SHADOW_MEMORY_MASK))); PRE(instr, and(drcontext, opnd_create_reg(R1), opnd_create_reg(R2))); +#ifdef MSANDR_STANDALONE_TEST + PRE(instr, add(drcontext, opnd_create_reg(R1), + OPND_CREATE_INT32(SHADOW_MEMORY_BASE))); +#endif // There is no mov_st of a 64-bit immediate, so... opnd_size_t op_size = opnd_get_size(op); CHECK(op_size != OPSZ_NA); uint access_size = opnd_size_in_bytes(op_size); - if (access_size <= 4) { - PRE(instr, - mov_st(drcontext, opnd_create_base_disp(R1, DR_REG_NULL, 0, 0, op_size), - opnd_create_immed_int((ptr_int_t) 0, op_size))); + if (access_size <= 4 || op_size == OPSZ_PTR /* x64 support sign extension */) { + instr_t *label = INSTR_CREATE_label(drcontext); + opnd_t immed; + if (op_size == OPSZ_PTR || op_size == OPSZ_4) + immed = OPND_CREATE_INT32(0); + else + immed = opnd_create_immed_int((ptr_int_t) 0, op_size); + // we check if target is 0 before write to reduce unnecessary memory stores. + PRE(instr, cmp(drcontext, + opnd_create_base_disp(R1, DR_REG_NULL, 0, 0, op_size), + immed)); + PRE(instr, jcc(drcontext, OP_je, opnd_create_instr(label))); + PRE(instr, mov_st(drcontext, + opnd_create_base_disp(R1, DR_REG_NULL, 0, 0, op_size), + immed)); + PREF(instr, label); } else { // FIXME: tail? for (uint ofs = 0; ofs < access_size; ofs += 4) { - PRE(instr, - mov_st(drcontext, OPND_CREATE_MEM32(R1, ofs), OPND_CREATE_INT32(0))); + instr_t *label = INSTR_CREATE_label(drcontext); + opnd_t immed = OPND_CREATE_INT32(0); + PRE(instr, cmp(drcontext, OPND_CREATE_MEM32(R1, ofs), immed)); + PRE(instr, jcc(drcontext, OP_je, opnd_create_instr(label))); + PRE(instr, mov_st(drcontext, OPND_CREATE_MEM32(R1, ofs), immed)); + PREF(instr, label) } } @@ -263,6 +341,7 @@ void InstrumentMops(void *drcontext, instrlist_t *bb, instr_t *instr, opnd_t op, dr_restore_reg(drcontext, bb, instr, R1, SPILL_SLOT_1); dr_restore_reg(drcontext, bb, instr, R2, SPILL_SLOT_2); + // TODO: move aflags save/restore to per instr instead of per opnd if (need_to_restore_eflags) { if (VERBOSITY > 1) dr_printf("Restoring eflags\n"); @@ -278,6 +357,18 @@ void InstrumentMops(void *drcontext, instrlist_t *bb, instr_t *instr, opnd_t op, } void InstrumentReturn(void *drcontext, instrlist_t *bb, instr_t *instr) { +#ifdef MSANDR_STANDALONE_TEST + PRE(instr, + mov_st(drcontext, + opnd_create_far_base_disp(DR_SEG_GS /* DR's TLS */, + DR_REG_NULL, DR_REG_NULL, + 0, msan_retval_tls_offset, + OPSZ_PTR), + OPND_CREATE_INT32(0))); +#else /* !MSANDR_STANDALONE_TEST */ + /* XXX: the code below only works if -mangle_app_seg and -private_loader, + * which is turned of for optimized native exec + */ dr_save_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1); // Clobbers nothing except xax. @@ -294,10 +385,27 @@ void InstrumentReturn(void *drcontext, instrlist_t *bb, instr_t *instr) { // The original instruction is left untouched. The above instrumentation is just // a prefix. +#endif /* !MSANDR_STANDALONE_TEST */ } void InstrumentIndirectBranch(void *drcontext, instrlist_t *bb, instr_t *instr) { +#ifdef MSANDR_STANDALONE_TEST + for (int i = 0; i < NUM_TLS_PARAM; ++i) { + PRE(instr, + mov_st(drcontext, + opnd_create_far_base_disp(DR_SEG_GS /* DR's TLS */, + DR_REG_NULL, DR_REG_NULL, + 0, + msan_param_tls_offset + + i * sizeof(void *), + OPSZ_PTR), + OPND_CREATE_INT32(0))); + } +#else /* !MSANDR_STANDALONE_TEST */ + /* XXX: the code below only works if -mangle_app_seg and -private_loader, + * which is turned off for optimized native exec + */ dr_save_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1); // Clobbers nothing except xax. @@ -306,7 +414,7 @@ void InstrumentIndirectBranch(void *drcontext, instrlist_t *bb, CHECK(res); // TODO: unpoison more bytes? - for (int i = 0; i < 6; ++i) { + for (int i = 0; i < NUM_TLS_PARAM; ++i) { PRE(instr, mov_st(drcontext, OPND_CREATE_MEMPTR(DR_REG_XAX, msan_param_tls_offset + i * sizeof(void *)), @@ -317,8 +425,10 @@ void InstrumentIndirectBranch(void *drcontext, instrlist_t *bb, // The original instruction is left untouched. The above instrumentation is just // a prefix. +#endif /* !MSANDR_STANDALONE_TEST */ } +#ifndef MSANDR_NATIVE_EXEC // For use with binary search. Modules shouldn't overlap, so we shouldn't have // to look at end_. If that can happen, we won't support such an application. bool ModuleDataCompareStart(const ModuleData &left, const ModuleData &right) { @@ -373,22 +483,26 @@ bool ShouldInstrumentPc(app_pc pc, ModuleData **pmod_data) { } return true; } +#endif /* !MSANDR_NATIVE_CLIENT */ // TODO(rnk): Make sure we instrument after __msan_init. dr_emit_flags_t event_basic_block_app2app(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { +#ifndef MSANDR_NATIVE_EXEC app_pc pc = dr_fragment_app_pc(tag); - if (ShouldInstrumentPc(pc, NULL)) CHECK(drutil_expand_rep_string(drcontext, bb)); - +#else /* MSANDR_NATIVE_EXEC */ + CHECK(drutil_expand_rep_string(drcontext, bb)); +#endif /* MSANDR_NATIVE_EXEC */ return DR_EMIT_PERSISTABLE; } dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { app_pc pc = dr_fragment_app_pc(tag); +#ifndef MSANDR_NATIVE_EXEC ModuleData *mod_data; if (!ShouldInstrumentPc(pc, &mod_data)) @@ -411,6 +525,8 @@ dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, pc - mod_data->start_); } } +#endif /* !MSANDR_NATIVE_EXEC */ + if (VERBOSITY > 1) { instrlist_disassemble(drcontext, pc, bb, STDOUT); instr_t *instr; @@ -474,6 +590,7 @@ dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, return DR_EMIT_PERSISTABLE; } +#ifndef MSANDR_NATIVE_EXEC void event_module_load(void *drcontext, const module_data_t *info, bool loaded) { // Insert the module into the list while maintaining the ordering. @@ -507,6 +624,7 @@ void event_module_unload(void *drcontext, const module_data_t *info) { it->end_ == mod_data.end_ && it->path_ == mod_data.path_); g_module_list.erase(it); } +#endif /* !MSANDR_NATIVE_EXEC */ void event_exit() { // Clean up so DR doesn't tell us we're leaking memory. @@ -514,6 +632,15 @@ void event_exit() { drutil_exit(); drmgr_exit(); +#ifdef MSANDR_STANDALONE_TEST + /* free tls */ + bool res; + res = dr_raw_tls_cfree(msan_retval_tls_offset, NUM_TLS_RETVAL); + CHECK(res); + res = dr_raw_tls_cfree(msan_param_tls_offset, NUM_TLS_PARAM); + CHECK(res); + /* we do not bother to free the shadow memory */ +#endif /* !MSANDR_STANDALONE_TEST */ if (VERBOSITY > 0) dr_printf("==DRMSAN== DONE\n"); } @@ -551,6 +678,7 @@ bool drsys_iter_memarg_cb(drsys_arg_t *arg, void *user_data) { drsys_syscall_t *syscall = (drsys_syscall_t *)user_data; const char *name; res = drsys_syscall_name(syscall, &name); + CHECK(res == DRMF_SUCCESS); dr_printf("drsyscall: syscall '%s' arg %d wrote range [%p, %p)\n", name, arg->ordinal, arg->start_addr, (char *)arg->start_addr + sz); @@ -689,6 +817,21 @@ DR_EXPORT void dr_init(client_id_t id) { res = drsys_filter_all_syscalls(); CHECK(res == DRMF_SUCCESS); +#ifdef MSANDR_STANDALONE_TEST + reg_id_t reg_seg; + /* alloc tls */ + if (!dr_raw_tls_calloc(®_seg, &mock_msan_retval_tls_offset, NUM_TLS_RETVAL, 0)) + CHECK(false); + CHECK(reg_seg == DR_SEG_GS /* x64 only! */); + if (!dr_raw_tls_calloc(®_seg, &mock_msan_param_tls_offset, NUM_TLS_PARAM, 0)) + CHECK(false); + CHECK(reg_seg == DR_SEG_GS /* x64 only! */); + /* alloc shadow memory */ + if (mmap(SHADOW_MEMORY_BASE, SHADOW_MEMORY_SIZE, PROT_READ|PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0) != SHADOW_MEMORY_BASE) { + CHECK(false); + } +#endif /* MSANDR_STANDALONE_TEST */ InitializeMSanCallbacks(); // FIXME: the shadow is initialized earlier when DR calls one of our wrapper @@ -719,8 +862,10 @@ DR_EXPORT void dr_init(client_id_t id) { drmgr_register_bb_app2app_event(event_basic_block_app2app, &priority); drmgr_register_bb_instru2instru_event(event_basic_block, &priority); +#ifndef MSANDR_NATIVE_EXEC drmgr_register_module_load_event(event_module_load); drmgr_register_module_unload_event(event_module_unload); +#endif /* MSANDR_NATIVE_EXEC */ if (VERBOSITY > 0) dr_printf("==MSANDR== Starting!\n"); } diff --git a/lib/profile/CMakeLists.txt b/lib/profile/CMakeLists.txt index 641f085c883f..bb4fd9e23a7e 100644 --- a/lib/profile/CMakeLists.txt +++ b/lib/profile/CMakeLists.txt @@ -6,11 +6,11 @@ filter_available_targets(PROFILE_SUPPORTED_ARCH x86_64 i386) if(APPLE) add_compiler_rt_osx_static_runtime(clang_rt.profile_osx ARCH ${PROFILE_SUPPORTED_ARCH} - SOURCES ${PROFILE_SOURCES} - CFLAGS --sysroot=${COMPILER_RT_DARWIN_SDK_SYSROOT}) + SOURCES ${PROFILE_SOURCES}) else() foreach(arch ${PROFILE_SUPPORTED_ARCH}) - add_compiler_rt_static_runtime(clang_rt.profile-${arch} ${arch} + add_compiler_rt_static_runtime(clang_rt.profile-${arch} + ${arch} SOURCES ${PROFILE_SOURCES}) endforeach() endif() diff --git a/lib/profile/GCDAProfiling.c b/lib/profile/GCDAProfiling.c index ce1b03c14de7..7edf6829b4a5 100644 --- a/lib/profile/GCDAProfiling.c +++ b/lib/profile/GCDAProfiling.c @@ -20,6 +20,7 @@ |* \*===----------------------------------------------------------------------===*/ +#include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> @@ -45,6 +46,11 @@ typedef unsigned int uint64_t; */ /* + * The current file name we're outputting. Used primarily for error logging. + */ +static char *filename = NULL; + +/* * The current file we're outputting. */ static FILE *output_file = NULL; @@ -196,17 +202,37 @@ static void recursive_mkdir(char *filename) { } } -static void map_file() { +static int map_file() { fseek(output_file, 0L, SEEK_END); file_size = ftell(output_file); + /* A size of 0 is invalid to `mmap'. Return a fail here, but don't issue an + * error message because it should "just work" for the user. */ + if (file_size == 0) + return -1; + write_buffer = mmap(0, file_size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0); + if (write_buffer == (void *)-1) { + int errnum = errno; + fprintf(stderr, "profiling: %s: cannot map: %s\n", filename, + strerror(errnum)); + return -1; + } + return 0; } static void unmap_file() { - msync(write_buffer, file_size, MS_SYNC); - munmap(write_buffer, file_size); + if (msync(write_buffer, file_size, MS_SYNC) == -1) { + int errnum = errno; + fprintf(stderr, "profiling: %s: cannot msync: %s\n", filename, + strerror(errnum)); + } + + /* We explicitly ignore errors from unmapping because at this point the data + * is written and we don't care. + */ + (void)munmap(write_buffer, file_size); write_buffer = NULL; file_size = 0; } @@ -220,8 +246,8 @@ static void unmap_file() { * started at a time. */ void llvm_gcda_start_file(const char *orig_filename, const char version[4]) { - char *filename = mangle_filename(orig_filename); const char *mode = "r+b"; + filename = mangle_filename(orig_filename); /* Try just opening the file. */ new_file = 0; @@ -236,10 +262,11 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4]) { /* Try creating the directories first then opening the file. */ recursive_mkdir(filename); fd = open(filename, O_RDWR | O_CREAT, 0644); - if (!output_file) { + if (fd == -1) { /* Bah! It's hopeless. */ - fprintf(stderr, "profiling:%s: cannot open\n", filename); - free(filename); + int errnum = errno; + fprintf(stderr, "profiling: %s: cannot open: %s\n", filename, + strerror(errnum)); return; } } @@ -256,7 +283,14 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4]) { resize_write_buffer(WRITE_BUFFER_SIZE); memset(write_buffer, 0, WRITE_BUFFER_SIZE); } else { - map_file(); + if (map_file() == -1) { + /* mmap failed, try to recover by clobbering */ + new_file = 1; + write_buffer = NULL; + cur_buffer_size = 0; + resize_write_buffer(WRITE_BUFFER_SIZE); + memset(write_buffer, 0, WRITE_BUFFER_SIZE); + } } /* gcda file, version, stamp LLVM. */ @@ -264,8 +298,6 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4]) { write_bytes(version, 4); write_bytes("MVLL", 4); - free(filename); - #ifdef DEBUG_GCDAPROFILING fprintf(stderr, "llvmgcda: [%s]\n", orig_filename); #endif @@ -334,7 +366,7 @@ void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { if (val != (uint32_t)-1) { /* There are counters present in the file. Merge them. */ if (val != 0x01a10000) { - fprintf(stderr, "profiling:invalid magic number (0x%08x)\n", val); + fprintf(stderr, "profiling:invalid arc tag (0x%08x)\n", val); return; } @@ -368,21 +400,72 @@ void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { #endif } -void llvm_gcda_end_file() { - /* Write out EOF record. */ +void llvm_gcda_summary_info() { + const int obj_summary_len = 9; // length for gcov compatibility + uint32_t i; + uint32_t runs = 1; + uint32_t val = 0; + uint64_t save_cur_pos = cur_pos; + if (!output_file) return; - write_bytes("\0\0\0\0\0\0\0\0", 8); - if (new_file) { - fwrite(write_buffer, cur_pos, 1, output_file); - free(write_buffer); - } else { - unmap_file(); + val = read_32bit_value(); + + if (val != (uint32_t)-1) { + /* There are counters present in the file. Merge them. */ + if (val != 0xa1000000) { + fprintf(stderr, "profiling:invalid object tag (0x%08x)\n", val); + return; + } + + val = read_32bit_value(); // length + if (val != obj_summary_len) { + fprintf(stderr, "profiling:invalid object length (%d)\n", val); // length + return; + } + + read_32bit_value(); // checksum, unused + read_32bit_value(); // num, unused + runs += read_32bit_value(); // add previous run count to new counter } - fclose(output_file); - output_file = NULL; - write_buffer = NULL; + cur_pos = save_cur_pos; + + /* Object summary tag */ + write_bytes("\0\0\0\xa1", 4); + write_32bit_value(obj_summary_len); + write_32bit_value(0); // checksum, unused + write_32bit_value(0); // num, unused + write_32bit_value(runs); + for (i = 3; i < obj_summary_len; ++i) + write_32bit_value(0); + + /* Program summary tag */ + write_bytes("\0\0\0\xa3", 4); // tag indicates 1 program + write_32bit_value(0); // 0 length + +#ifdef DEBUG_GCDAPROFILING + fprintf(stderr, "llvmgcda: %u runs\n", runs); +#endif +} + +void llvm_gcda_end_file() { + /* Write out EOF record. */ + if (output_file) { + write_bytes("\0\0\0\0\0\0\0\0", 8); + + if (new_file) { + fwrite(write_buffer, cur_pos, 1, output_file); + free(write_buffer); + } else { + unmap_file(); + } + + fclose(output_file); + output_file = NULL; + write_buffer = NULL; + } + free(filename); #ifdef DEBUG_GCDAPROFILING fprintf(stderr, "llvmgcda: -----\n"); diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt index 2683a37a32ca..84c1e67dc806 100644 --- a/lib/sanitizer_common/CMakeLists.txt +++ b/lib/sanitizer_common/CMakeLists.txt @@ -4,47 +4,52 @@ set(SANITIZER_SOURCES sanitizer_allocator.cc sanitizer_common.cc + sanitizer_coverage.cc sanitizer_flags.cc sanitizer_libc.cc + sanitizer_libignore.cc sanitizer_linux.cc sanitizer_mac.cc + sanitizer_platform_limits_linux.cc sanitizer_platform_limits_posix.cc sanitizer_posix.cc sanitizer_printf.cc sanitizer_stackdepot.cc sanitizer_stacktrace.cc - sanitizer_symbolizer_itanium.cc - sanitizer_symbolizer_mac.cc + sanitizer_suppressions.cc + sanitizer_symbolizer.cc sanitizer_symbolizer_win.cc sanitizer_thread_registry.cc - sanitizer_win.cc - ) + sanitizer_win.cc) set(SANITIZER_LIBCDEP_SOURCES sanitizer_common_libcdep.cc sanitizer_linux_libcdep.cc sanitizer_posix_libcdep.cc + sanitizer_stacktrace_libcdep.cc sanitizer_stoptheworld_linux_libcdep.cc sanitizer_symbolizer_libcdep.cc - sanitizer_symbolizer_linux_libcdep.cc - ) + sanitizer_symbolizer_posix_libcdep.cc) # Explicitly list all sanitizer_common headers. Not all of these are # included in sanitizer_common source files, but we need to depend on # headers when building our custom unit tests. set(SANITIZER_HEADERS sanitizer_allocator.h + sanitizer_allocator_internal.h sanitizer_atomic_clang.h sanitizer_atomic_msvc.h sanitizer_atomic.h sanitizer_common.h sanitizer_common_interceptors.inc + sanitizer_common_interceptors_ioctl.inc sanitizer_common_interceptors_scanf.inc sanitizer_common_syscalls.inc sanitizer_flags.h sanitizer_internal_defs.h sanitizer_lfstack.h sanitizer_libc.h + sanitizer_libignore.h sanitizer_linux.h sanitizer_list.h sanitizer_mutex.h @@ -56,21 +61,32 @@ set(SANITIZER_HEADERS sanitizer_stackdepot.h sanitizer_stacktrace.h sanitizer_symbolizer.h - sanitizer_thread_registry.h - ) + sanitizer_thread_registry.h) -set(SANITIZER_CFLAGS - ${SANITIZER_COMMON_CFLAGS} - -fno-rtti) +if (NOT MSVC) + set(SANITIZER_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + -fno-rtti) +else() + set(SANITIZER_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + /GR-) +endif() + +if(SUPPORTS_GLOBAL_CONSTRUCTORS_FLAG) + list(APPEND SANITIZER_CFLAGS -Wglobal-constructors) +endif() set(SANITIZER_RUNTIME_LIBRARIES) if(APPLE) # Build universal binary on APPLE. - add_compiler_rt_osx_object_library(RTSanitizerCommon - ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH} - SOURCES ${SANITIZER_SOURCES} ${SANITIZER_LIBCDEP_SOURCES} - CFLAGS ${SANITIZER_CFLAGS}) - list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.osx) + foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) + add_compiler_rt_darwin_object_library(RTSanitizerCommon ${os} + ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH} + SOURCES ${SANITIZER_SOURCES} ${SANITIZER_LIBCDEP_SOURCES} + CFLAGS ${SANITIZER_CFLAGS}) + list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.${os}) + endforeach() elseif(ANDROID) add_library(RTSanitizerCommon.arm.android OBJECT ${SANITIZER_SOURCES} ${SANITIZER_LIBCDEP_SOURCES}) @@ -88,7 +104,8 @@ else() SOURCES $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> CFLAGS ${SANITIZER_CFLAGS}) - list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.${arch}) + list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.${arch} + RTSanitizerCommonLibc.${arch}) endforeach() endif() diff --git a/lib/sanitizer_common/sanitizer_allocator.cc b/lib/sanitizer_common/sanitizer_allocator.cc index a97a70937a43..daaf7e1ce055 100644 --- a/lib/sanitizer_common/sanitizer_allocator.cc +++ b/lib/sanitizer_common/sanitizer_allocator.cc @@ -9,44 +9,103 @@ // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. -// This allocator that is used inside run-times. +// This allocator is used inside run-times. //===----------------------------------------------------------------------===// +#include "sanitizer_allocator.h" +#include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" +#include "sanitizer_flags.h" -// FIXME: We should probably use more low-level allocator that would -// mmap some pages and split them into chunks to fulfill requests. -#if SANITIZER_LINUX && !SANITIZER_ANDROID -extern "C" void *__libc_malloc(__sanitizer::uptr size); +namespace __sanitizer { + +// ThreadSanitizer for Go uses libc malloc/free. +#if defined(SANITIZER_GO) +# if SANITIZER_LINUX && !SANITIZER_ANDROID +extern "C" void *__libc_malloc(uptr size); extern "C" void __libc_free(void *ptr); -# define LIBC_MALLOC __libc_malloc -# define LIBC_FREE __libc_free -#else // SANITIZER_LINUX && !SANITIZER_ANDROID -# include <stdlib.h> -# define LIBC_MALLOC malloc -# define LIBC_FREE free -#endif // SANITIZER_LINUX && !SANITIZER_ANDROID +# define LIBC_MALLOC __libc_malloc +# define LIBC_FREE __libc_free +# else +# include <stdlib.h> +# define LIBC_MALLOC malloc +# define LIBC_FREE free +# endif -namespace __sanitizer { +static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) { + (void)cache; + return LIBC_MALLOC(size); +} + +static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { + (void)cache; + LIBC_FREE(ptr); +} + +InternalAllocator *internal_allocator() { + return 0; +} + +#else // SANITIZER_GO + +static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)]; +static atomic_uint8_t internal_allocator_initialized; +static StaticSpinMutex internal_alloc_init_mu; + +static InternalAllocatorCache internal_allocator_cache; +static StaticSpinMutex internal_allocator_cache_mu; + +InternalAllocator *internal_allocator() { + InternalAllocator *internal_allocator_instance = + reinterpret_cast<InternalAllocator *>(&internal_alloc_placeholder); + if (atomic_load(&internal_allocator_initialized, memory_order_acquire) == 0) { + SpinMutexLock l(&internal_alloc_init_mu); + if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) == + 0) { + internal_allocator_instance->Init(); + atomic_store(&internal_allocator_initialized, 1, memory_order_release); + } + } + return internal_allocator_instance; +} + +static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) { + if (cache == 0) { + SpinMutexLock l(&internal_allocator_cache_mu); + return internal_allocator()->Allocate(&internal_allocator_cache, size, 8, + false); + } + return internal_allocator()->Allocate(cache, size, 8, false); +} + +static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { + if (cache == 0) { + SpinMutexLock l(&internal_allocator_cache_mu); + return internal_allocator()->Deallocate(&internal_allocator_cache, ptr); + } + internal_allocator()->Deallocate(cache, ptr); +} + +#endif // SANITIZER_GO const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull; -void *InternalAlloc(uptr size) { +void *InternalAlloc(uptr size, InternalAllocatorCache *cache) { if (size + sizeof(u64) < size) return 0; - void *p = LIBC_MALLOC(size + sizeof(u64)); + void *p = RawInternalAlloc(size + sizeof(u64), cache); if (p == 0) return 0; ((u64*)p)[0] = kBlockMagic; return (char*)p + sizeof(u64); } -void InternalFree(void *addr) { +void InternalFree(void *addr, InternalAllocatorCache *cache) { if (addr == 0) return; addr = (char*)addr - sizeof(u64); - CHECK_EQ(((u64*)addr)[0], kBlockMagic); + CHECK_EQ(kBlockMagic, ((u64*)addr)[0]); ((u64*)addr)[0] = 0; - LIBC_FREE(addr); + RawInternalFree(addr, cache); } // LowLevelAllocator @@ -81,4 +140,14 @@ bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) { return (max / size) < n; } +void *AllocatorReturnNull() { + if (common_flags()->allocator_may_return_null) + return 0; + Report("%s's allocator is terminating the process instead of returning 0\n", + SanitizerToolName); + Report("If you don't like this behavior set allocator_may_return_null=1\n"); + CHECK(0); + return 0; +} + } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_allocator.h b/lib/sanitizer_common/sanitizer_allocator.h index 0542addb7f37..6075cfe92aac 100644 --- a/lib/sanitizer_common/sanitizer_allocator.h +++ b/lib/sanitizer_common/sanitizer_allocator.h @@ -23,6 +23,9 @@ namespace __sanitizer { +// Depending on allocator_may_return_null either return 0 or crash. +void *AllocatorReturnNull(); + // SizeClassMap maps allocation sizes into size classes and back. // Class 0 corresponds to size 0. // Classes 1 - 16 correspond to sizes 16 to 256 (size = class_id * 16). @@ -279,6 +282,9 @@ struct NoOpMapUnmapCallback { void OnUnmap(uptr p, uptr size) const { } }; +// Callback type for iterating over chunks. +typedef void (*ForEachChunkCallback)(uptr chunk, void *arg); + // SizeClassAllocator64 -- allocator for 64-bit address space. // // Space: a portion of address space of kSpaceSize bytes starting at @@ -344,15 +350,15 @@ class SizeClassAllocator64 { region->n_freed += b->count; } - static bool PointerIsMine(void *p) { + static bool PointerIsMine(const void *p) { return reinterpret_cast<uptr>(p) / kSpaceSize == kSpaceBeg / kSpaceSize; } - static uptr GetSizeClass(void *p) { + static uptr GetSizeClass(const void *p) { return (reinterpret_cast<uptr>(p) / kRegionSize) % kNumClassesRounded; } - void *GetBlockBegin(void *p) { + void *GetBlockBegin(const void *p) { uptr class_id = GetSizeClass(p); uptr size = SizeClassMap::Size(class_id); if (!size) return 0; @@ -374,7 +380,7 @@ class SizeClassAllocator64 { uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); } - void *GetMetaData(void *p) { + void *GetMetaData(const void *p) { uptr class_id = GetSizeClass(p); uptr size = SizeClassMap::Size(class_id); uptr chunk_idx = GetChunkIdx(reinterpret_cast<uptr>(p), size); @@ -433,20 +439,18 @@ class SizeClassAllocator64 { } } - // Iterate over existing chunks. May include chunks that are not currently - // allocated to the user (e.g. freed). - // The caller is expected to call ForceLock() before calling this function. - template<typename Callable> - void ForEachChunk(const Callable &callback) { + // Iterate over all existing chunks. + // The allocator must be locked when calling this function. + void ForEachChunk(ForEachChunkCallback callback, void *arg) { for (uptr class_id = 1; class_id < kNumClasses; class_id++) { RegionInfo *region = GetRegionInfo(class_id); uptr chunk_size = SizeClassMap::Size(class_id); uptr region_beg = kSpaceBeg + class_id * kRegionSize; - for (uptr p = region_beg; - p < region_beg + region->allocated_user; - p += chunk_size) { - // Too slow: CHECK_EQ((void *)p, GetBlockBegin((void *)p)); - callback((void *)p); + for (uptr chunk = region_beg; + chunk < region_beg + region->allocated_user; + chunk += chunk_size) { + // Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk)); + callback(chunk, arg); } } } @@ -639,7 +643,7 @@ class SizeClassAllocator32 { alignment <= SizeClassMap::kMaxSize; } - void *GetMetaData(void *p) { + void *GetMetaData(const void *p) { CHECK(PointerIsMine(p)); uptr mem = reinterpret_cast<uptr>(p); uptr beg = ComputeRegionBeg(mem); @@ -671,15 +675,15 @@ class SizeClassAllocator32 { sci->free_list.push_front(b); } - bool PointerIsMine(void *p) { + bool PointerIsMine(const void *p) { return GetSizeClass(p) != 0; } - uptr GetSizeClass(void *p) { + uptr GetSizeClass(const void *p) { return possible_regions[ComputeRegionId(reinterpret_cast<uptr>(p))]; } - void *GetBlockBegin(void *p) { + void *GetBlockBegin(const void *p) { CHECK(PointerIsMine(p)); uptr mem = reinterpret_cast<uptr>(p); uptr beg = ComputeRegionBeg(mem); @@ -726,21 +730,19 @@ class SizeClassAllocator32 { } } - // Iterate over existing chunks. May include chunks that are not currently - // allocated to the user (e.g. freed). - // The caller is expected to call ForceLock() before calling this function. - template<typename Callable> - void ForEachChunk(const Callable &callback) { + // Iterate over all existing chunks. + // The allocator must be locked when calling this function. + void ForEachChunk(ForEachChunkCallback callback, void *arg) { for (uptr region = 0; region < kNumPossibleRegions; region++) if (possible_regions[region]) { uptr chunk_size = SizeClassMap::Size(possible_regions[region]); uptr max_chunks_in_region = kRegionSize / (chunk_size + kMetadataSize); uptr region_beg = region * kRegionSize; - for (uptr p = region_beg; - p < region_beg + max_chunks_in_region * chunk_size; - p += chunk_size) { - // Too slow: CHECK_EQ((void *)p, GetBlockBegin((void *)p)); - callback((void *)p); + for (uptr chunk = region_beg; + chunk < region_beg + max_chunks_in_region * chunk_size; + chunk += chunk_size) { + // Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk)); + callback(chunk, arg); } } } @@ -779,7 +781,7 @@ class SizeClassAllocator32 { MapUnmapCallback().OnMap(res, kRegionSize); stat->Add(AllocatorStatMmapped, kRegionSize); CHECK_EQ(0U, (res & (kRegionSize - 1))); - possible_regions.set(ComputeRegionId(res), class_id); + possible_regions.set(ComputeRegionId(res), static_cast<u8>(class_id)); return res; } @@ -942,7 +944,7 @@ class LargeMmapAllocator { uptr map_size = RoundUpMapSize(size); if (alignment > page_size_) map_size += alignment; - if (map_size < size) return 0; // Overflow. + if (map_size < size) return AllocatorReturnNull(); // Overflow. uptr map_beg = reinterpret_cast<uptr>( MmapOrDie(map_size, "LargeMmapAllocator")); MapUnmapCallback().OnMap(map_beg, map_size); @@ -961,6 +963,7 @@ class LargeMmapAllocator { { SpinMutexLock l(&mutex_); uptr idx = n_chunks_++; + chunks_sorted_ = false; CHECK_LT(idx, kMaxNumChunks); h->chunk_idx = idx; chunks_[idx] = h; @@ -984,6 +987,7 @@ class LargeMmapAllocator { chunks_[idx] = chunks_[n_chunks_ - 1]; chunks_[idx]->chunk_idx = idx; n_chunks_--; + chunks_sorted_ = false; stats.n_frees++; stats.currently_allocated -= h->map_size; stat->Add(AllocatorStatFreed, h->map_size); @@ -1004,7 +1008,7 @@ class LargeMmapAllocator { return res; } - bool PointerIsMine(void *p) { + bool PointerIsMine(const void *p) { return GetBlockBegin(p) != 0; } @@ -1013,13 +1017,16 @@ class LargeMmapAllocator { } // At least page_size_/2 metadata bytes is available. - void *GetMetaData(void *p) { + void *GetMetaData(const void *p) { // Too slow: CHECK_EQ(p, GetBlockBegin(p)); - CHECK(IsAligned(reinterpret_cast<uptr>(p), page_size_)); + if (!IsAligned(reinterpret_cast<uptr>(p), page_size_)) { + Printf("%s: bad pointer %p\n", SanitizerToolName, p); + CHECK(IsAligned(reinterpret_cast<uptr>(p), page_size_)); + } return GetHeader(p) + 1; } - void *GetBlockBegin(void *ptr) { + void *GetBlockBegin(const void *ptr) { uptr p = reinterpret_cast<uptr>(ptr); SpinMutexLock l(&mutex_); uptr nearest_chunk = 0; @@ -1041,6 +1048,49 @@ class LargeMmapAllocator { return GetUser(h); } + // This function does the same as GetBlockBegin, but is much faster. + // Must be called with the allocator locked. + void *GetBlockBeginFastLocked(void *ptr) { + mutex_.CheckLocked(); + uptr p = reinterpret_cast<uptr>(ptr); + uptr n = n_chunks_; + if (!n) return 0; + if (!chunks_sorted_) { + // Do one-time sort. chunks_sorted_ is reset in Allocate/Deallocate. + SortArray(reinterpret_cast<uptr*>(chunks_), n); + for (uptr i = 0; i < n; i++) + chunks_[i]->chunk_idx = i; + chunks_sorted_ = true; + min_mmap_ = reinterpret_cast<uptr>(chunks_[0]); + max_mmap_ = reinterpret_cast<uptr>(chunks_[n - 1]) + + chunks_[n - 1]->map_size; + } + if (p < min_mmap_ || p >= max_mmap_) + return 0; + uptr beg = 0, end = n - 1; + // This loop is a log(n) lower_bound. It does not check for the exact match + // to avoid expensive cache-thrashing loads. + while (end - beg >= 2) { + uptr mid = (beg + end) / 2; // Invariant: mid >= beg + 1 + if (p < reinterpret_cast<uptr>(chunks_[mid])) + end = mid - 1; // We are not interested in chunks_[mid]. + else + beg = mid; // chunks_[mid] may still be what we want. + } + + if (beg < end) { + CHECK_EQ(beg + 1, end); + // There are 2 chunks left, choose one. + if (p >= reinterpret_cast<uptr>(chunks_[end])) + beg = end; + } + + Header *h = chunks_[beg]; + if (h->map_beg + h->map_size <= p || p < h->map_beg) + return 0; + return GetUser(h); + } + void PrintStats() { Printf("Stats: LargeMmapAllocator: allocated %zd times, " "remains %zd (%zd K) max %zd M; by size logs: ", @@ -1064,13 +1114,11 @@ class LargeMmapAllocator { mutex_.Unlock(); } - // Iterate over existing chunks. May include chunks that are not currently - // allocated to the user (e.g. freed). - // The caller is expected to call ForceLock() before calling this function. - template<typename Callable> - void ForEachChunk(const Callable &callback) { + // Iterate over all existing chunks. + // The allocator must be locked when calling this function. + void ForEachChunk(ForEachChunkCallback callback, void *arg) { for (uptr i = 0; i < n_chunks_; i++) - callback(GetUser(chunks_[i])); + callback(reinterpret_cast<uptr>(GetUser(chunks_[i])), arg); } private: @@ -1083,13 +1131,15 @@ class LargeMmapAllocator { }; Header *GetHeader(uptr p) { - CHECK_EQ(p % page_size_, 0); + CHECK(IsAligned(p, page_size_)); return reinterpret_cast<Header*>(p - page_size_); } - Header *GetHeader(void *p) { return GetHeader(reinterpret_cast<uptr>(p)); } + Header *GetHeader(const void *p) { + return GetHeader(reinterpret_cast<uptr>(p)); + } void *GetUser(Header *h) { - CHECK_EQ((uptr)h % page_size_, 0); + CHECK(IsAligned((uptr)h, page_size_)); return reinterpret_cast<void*>(reinterpret_cast<uptr>(h) + page_size_); } @@ -1100,6 +1150,8 @@ class LargeMmapAllocator { uptr page_size_; Header *chunks_[kMaxNumChunks]; uptr n_chunks_; + uptr min_mmap_, max_mmap_; + bool chunks_sorted_; struct Stats { uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64]; } stats; @@ -1128,7 +1180,7 @@ class CombinedAllocator { if (size == 0) size = 1; if (size + alignment < size) - return 0; + return AllocatorReturnNull(); if (alignment > 8) size = RoundUpTo(size, alignment); void *res; @@ -1179,18 +1231,26 @@ class CombinedAllocator { return primary_.PointerIsMine(p); } - void *GetMetaData(void *p) { + void *GetMetaData(const void *p) { if (primary_.PointerIsMine(p)) return primary_.GetMetaData(p); return secondary_.GetMetaData(p); } - void *GetBlockBegin(void *p) { + void *GetBlockBegin(const void *p) { if (primary_.PointerIsMine(p)) return primary_.GetBlockBegin(p); return secondary_.GetBlockBegin(p); } + // This function does the same as GetBlockBegin, but is much faster. + // Must be called with the allocator locked. + void *GetBlockBeginFastLocked(void *p) { + if (primary_.PointerIsMine(p)) + return primary_.GetBlockBegin(p); + return secondary_.GetBlockBeginFastLocked(p); + } + uptr GetActuallyAllocatedSize(void *p) { if (primary_.PointerIsMine(p)) return primary_.GetActuallyAllocatedSize(p); @@ -1236,13 +1296,11 @@ class CombinedAllocator { primary_.ForceUnlock(); } - // Iterate over existing chunks. May include chunks that are not currently - // allocated to the user (e.g. freed). - // The caller is expected to call ForceLock() before calling this function. - template<typename Callable> - void ForEachChunk(const Callable &callback) { - primary_.ForEachChunk(callback); - secondary_.ForEachChunk(callback); + // Iterate over all existing chunks. + // The allocator must be locked when calling this function. + void ForEachChunk(ForEachChunkCallback callback, void *arg) { + primary_.ForEachChunk(callback, arg); + secondary_.ForEachChunk(callback, arg); } private: diff --git a/lib/sanitizer_common/sanitizer_allocator_internal.h b/lib/sanitizer_common/sanitizer_allocator_internal.h new file mode 100644 index 000000000000..5b24bfdafa36 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_allocator_internal.h @@ -0,0 +1,64 @@ +//===-- sanitizer_allocator_internal.h -------------------------- C++ -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This allocator is used inside run-times. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_ALLOCATOR_INTERNAL_H +#define SANITIZER_ALLOCATOR_INTERNAL_H + +#include "sanitizer_allocator.h" +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +// FIXME: Check if we may use even more compact size class map for internal +// purposes. +typedef CompactSizeClassMap InternalSizeClassMap; + +static const uptr kInternalAllocatorSpace = 0; +#if SANITIZER_WORDSIZE == 32 +static const u64 kInternalAllocatorSize = (1ULL << 32); +static const uptr kInternalAllocatorRegionSizeLog = 20; +#else +static const u64 kInternalAllocatorSize = (1ULL << 47); +static const uptr kInternalAllocatorRegionSizeLog = 24; +#endif +static const uptr kInternalAllocatorFlatByteMapSize = + kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog; +typedef SizeClassAllocator32< + kInternalAllocatorSpace, kInternalAllocatorSize, 16, InternalSizeClassMap, + kInternalAllocatorRegionSizeLog, + FlatByteMap<kInternalAllocatorFlatByteMapSize> > PrimaryInternalAllocator; + +typedef SizeClassAllocatorLocalCache<PrimaryInternalAllocator> + InternalAllocatorCache; + +// We don't want our internal allocator to do any map/unmap operations. +struct CrashOnMapUnmap { + void OnMap(uptr p, uptr size) const { + RAW_CHECK_MSG(0, "Unexpected mmap in InternalAllocator!"); + } + void OnUnmap(uptr p, uptr size) const { + RAW_CHECK_MSG(0, "Unexpected munmap in InternalAllocator!"); + } +}; + +typedef CombinedAllocator<PrimaryInternalAllocator, InternalAllocatorCache, + LargeMmapAllocator<CrashOnMapUnmap> > + InternalAllocator; + +void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0); +void InternalFree(void *p, InternalAllocatorCache *cache = 0); +InternalAllocator *internal_allocator(); + +} // namespace __sanitizer + +#endif // SANITIZER_ALLOCATOR_INTERNAL_H diff --git a/lib/sanitizer_common/sanitizer_atomic_clang.h b/lib/sanitizer_common/sanitizer_atomic_clang.h index 30158b49683c..c5aa939b58c4 100644 --- a/lib/sanitizer_common/sanitizer_atomic_clang.h +++ b/lib/sanitizer_common/sanitizer_atomic_clang.h @@ -41,7 +41,17 @@ INLINE typename T::Type atomic_load( | memory_order_acquire | memory_order_seq_cst)); DCHECK(!((uptr)a % sizeof(*a))); typename T::Type v; - // FIXME(dvyukov): 64-bit load is not atomic on 32-bits. + // FIXME: + // 64-bit atomic operations are not atomic on 32-bit platforms. + // The implementation lacks necessary memory fences on ARM/PPC. + // We would like to use compiler builtin atomic operations, + // but they are mostly broken: + // - they lead to vastly inefficient code generation + // (http://llvm.org/bugs/show_bug.cgi?id=17281) + // - 64-bit atomic operations are not implemented on x86_32 + // (http://llvm.org/bugs/show_bug.cgi?id=15034) + // - they are not implemented on ARM + // error: undefined reference to '__atomic_load_4' if (mo == memory_order_relaxed) { v = a->val_dont_use; } else { @@ -57,7 +67,6 @@ INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) { DCHECK(mo & (memory_order_relaxed | memory_order_release | memory_order_seq_cst)); DCHECK(!((uptr)a % sizeof(*a))); - // FIXME(dvyukov): 64-bit store is not atomic on 32-bits. if (mo == memory_order_relaxed) { a->val_dont_use = v; } else { @@ -121,4 +130,6 @@ INLINE bool atomic_compare_exchange_weak(volatile T *a, } // namespace __sanitizer +#undef ATOMIC_ORDER + #endif // SANITIZER_ATOMIC_CLANG_H diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc index abbe5f92d1a9..7e870ff65455 100644 --- a/lib/sanitizer_common/sanitizer_common.cc +++ b/lib/sanitizer_common/sanitizer_common.cc @@ -12,12 +12,14 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common.h" +#include "sanitizer_flags.h" #include "sanitizer_libc.h" +#include "sanitizer_stacktrace.h" +#include "sanitizer_symbolizer.h" namespace __sanitizer { const char *SanitizerToolName = "SanitizerTool"; -uptr SanitizerVerbosity = 0; uptr GetPageSizeCached() { static uptr PageSize; @@ -26,22 +28,29 @@ uptr GetPageSizeCached() { return PageSize; } -static bool log_to_file = false; // Set to true by __sanitizer_set_report_path // By default, dump to stderr. If |log_to_file| is true and |report_fd_pid| // isn't equal to the current PID, try to obtain file descriptor by opening // file "report_path_prefix.<PID>". fd_t report_fd = kStderrFd; -static char report_path_prefix[4096]; // Set via __sanitizer_set_report_path. + +// Set via __sanitizer_set_report_path. +bool log_to_file = false; +char report_path_prefix[sizeof(report_path_prefix)]; + // PID of process that opened |report_fd|. If a fork() occurs, the PID of the // child thread will be different from |report_fd_pid|. -static uptr report_fd_pid = 0; +uptr report_fd_pid = 0; -static void (*DieCallback)(void); -void SetDieCallback(void (*callback)(void)) { +static DieCallbackType DieCallback; +void SetDieCallback(DieCallbackType callback) { DieCallback = callback; } +DieCallbackType GetDieCallback() { + return DieCallback; +} + void NORETURN Die() { if (DieCallback) { DieCallback(); @@ -64,36 +73,6 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond, Die(); } -void MaybeOpenReportFile() { - if (!log_to_file || (report_fd_pid == internal_getpid())) return; - InternalScopedBuffer<char> report_path_full(4096); - internal_snprintf(report_path_full.data(), report_path_full.size(), - "%s.%d", report_path_prefix, internal_getpid()); - uptr openrv = OpenFile(report_path_full.data(), true); - if (internal_iserror(openrv)) { - report_fd = kStderrFd; - log_to_file = false; - Report("ERROR: Can't open file: %s\n", report_path_full.data()); - Die(); - } - if (report_fd != kInvalidFd) { - // We're in the child. Close the parent's log. - internal_close(report_fd); - } - report_fd = openrv; - report_fd_pid = internal_getpid(); -} - -void RawWrite(const char *buffer) { - static const char *kRawWriteError = "RawWrite can't output requested buffer!"; - uptr length = (uptr)internal_strlen(buffer); - MaybeOpenReportFile(); - if (length != internal_write(report_fd, buffer, length)) { - internal_write(report_fd, kRawWriteError, internal_strlen(kRawWriteError)); - Die(); - } -} - uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, uptr max_len) { uptr PageSize = GetPageSizeCached(); @@ -159,14 +138,103 @@ void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) { return (void*)res; } +const char *StripPathPrefix(const char *filepath, + const char *strip_path_prefix) { + if (filepath == 0) return 0; + if (strip_path_prefix == 0) return filepath; + const char *pos = internal_strstr(filepath, strip_path_prefix); + if (pos == 0) return filepath; + pos += internal_strlen(strip_path_prefix); + if (pos[0] == '.' && pos[1] == '/') + pos += 2; + return pos; +} + +void PrintSourceLocation(InternalScopedString *buffer, const char *file, + int line, int column) { + CHECK(file); + buffer->append("%s", + StripPathPrefix(file, common_flags()->strip_path_prefix)); + if (line > 0) { + buffer->append(":%d", line); + if (column > 0) + buffer->append(":%d", column); + } +} + +void PrintModuleAndOffset(InternalScopedString *buffer, const char *module, + uptr offset) { + buffer->append("(%s+0x%zx)", + StripPathPrefix(module, common_flags()->strip_path_prefix), + offset); +} + +void ReportErrorSummary(const char *error_message) { + if (!common_flags()->print_summary) + return; + InternalScopedBuffer<char> buff(kMaxSummaryLength); + internal_snprintf(buff.data(), buff.size(), + "SUMMARY: %s: %s", SanitizerToolName, error_message); + __sanitizer_report_error_summary(buff.data()); +} + void ReportErrorSummary(const char *error_type, const char *file, int line, const char *function) { - const int kMaxSize = 1024; // We don't want a summary too long. - InternalScopedBuffer<char> buff(kMaxSize); - internal_snprintf(buff.data(), kMaxSize, "%s: %s %s:%d %s", - SanitizerToolName, error_type, - file ? file : "??", line, function ? function : "??"); - __sanitizer_report_error_summary(buff.data()); + if (!common_flags()->print_summary) + return; + InternalScopedBuffer<char> buff(kMaxSummaryLength); + internal_snprintf( + buff.data(), buff.size(), "%s %s:%d %s", error_type, + file ? StripPathPrefix(file, common_flags()->strip_path_prefix) : "??", + line, function ? function : "??"); + ReportErrorSummary(buff.data()); +} + +void ReportErrorSummary(const char *error_type, StackTrace *stack) { + if (!common_flags()->print_summary) + return; + AddressInfo ai; +#if !SANITIZER_GO + if (stack->size > 0 && Symbolizer::Get()->IsAvailable()) { + // Currently, we include the first stack frame into the report summary. + // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). + uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); + Symbolizer::Get()->SymbolizeCode(pc, &ai, 1); + } +#endif + ReportErrorSummary(error_type, ai.file, ai.line, ai.function); +} + +LoadedModule::LoadedModule(const char *module_name, uptr base_address) { + full_name_ = internal_strdup(module_name); + base_address_ = base_address; + n_ranges_ = 0; +} + +void LoadedModule::addAddressRange(uptr beg, uptr end) { + CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges); + ranges_[n_ranges_].beg = beg; + ranges_[n_ranges_].end = end; + n_ranges_++; +} + +bool LoadedModule::containsAddress(uptr address) const { + for (uptr i = 0; i < n_ranges_; i++) { + if (ranges_[i].beg <= address && address < ranges_[i].end) + return true; + } + return false; +} + +char *StripModuleName(const char *module) { + if (module == 0) + return 0; + const char *short_module_name = internal_strrchr(module, '/'); + if (short_module_name) + short_module_name += 1; + else + short_module_name = module; + return internal_strdup(short_module_name); } } // namespace __sanitizer @@ -175,7 +243,8 @@ using namespace __sanitizer; // NOLINT extern "C" { void __sanitizer_set_report_path(const char *path) { - if (!path) return; + if (!path) + return; uptr len = internal_strlen(path); if (len > sizeof(report_path_prefix) - 100) { Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n", @@ -183,18 +252,21 @@ void __sanitizer_set_report_path(const char *path) { path[4], path[5], path[6], path[7]); Die(); } - internal_strncpy(report_path_prefix, path, sizeof(report_path_prefix)); - report_path_prefix[len] = '\0'; - report_fd = kInvalidFd; - log_to_file = true; -} - -void __sanitizer_set_report_fd(int fd) { if (report_fd != kStdoutFd && report_fd != kStderrFd && report_fd != kInvalidFd) internal_close(report_fd); - report_fd = fd; + report_fd = kInvalidFd; + log_to_file = false; + if (internal_strcmp(path, "stdout") == 0) { + report_fd = kStdoutFd; + } else if (internal_strcmp(path, "stderr") == 0) { + report_fd = kStderrFd; + } else { + internal_strncpy(report_path_prefix, path, sizeof(report_path_prefix)); + report_path_prefix[len] = '\0'; + log_to_file = true; + } } void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) { @@ -203,6 +275,6 @@ void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) { } void __sanitizer_report_error_summary(const char *error_summary) { - Printf("SUMMARY: %s\n", error_summary); + Printf("%s\n", error_summary); } } // extern "C" diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h index d800360169fb..cf8a12d65a09 100644 --- a/lib/sanitizer_common/sanitizer_common.h +++ b/lib/sanitizer_common/sanitizer_common.h @@ -33,12 +33,14 @@ const uptr kCacheLineSize = 128; const uptr kCacheLineSize = 64; #endif +const uptr kMaxPathLength = 512; + extern const char *SanitizerToolName; // Can be changed by the tool. -extern uptr SanitizerVerbosity; uptr GetPageSize(); uptr GetPageSizeCached(); uptr GetMmapGranularity(); +uptr GetMaxVirtualAddress(); // Threads uptr GetTid(); uptr GetThreadSelf(); @@ -59,10 +61,6 @@ void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type); bool MemoryRangeIsAvailable(uptr range_start, uptr range_end); void FlushUnneededShadowMemory(uptr addr, uptr size); -// Internal allocator -void *InternalAlloc(uptr size); -void InternalFree(void *p); - // InternalScopedBuffer can be used instead of large stack arrays to // keep frame size low. // FIXME: use InternalAlloc instead of MmapOrDie once @@ -89,6 +87,23 @@ class InternalScopedBuffer { void operator=(const InternalScopedBuffer&); }; +class InternalScopedString : public InternalScopedBuffer<char> { + public: + explicit InternalScopedString(uptr max_length) + : InternalScopedBuffer<char>(max_length), length_(0) { + (*this)[0] = '\0'; + } + uptr length() { return length_; } + void clear() { + (*this)[0] = '\0'; + length_ = 0; + } + void append(const char *format, ...); + + private: + uptr length_; +}; + // Simple low-level (mmap-based) allocator for internal use. Doesn't have // constructor, so all instances of LowLevelAllocator should be // linker initialized. @@ -108,13 +123,19 @@ void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback); // IO void RawWrite(const char *buffer); bool PrintsToTty(); +// Caching version of PrintsToTty(). Not thread-safe. +bool PrintsToTtyCached(); void Printf(const char *format, ...); void Report(const char *format, ...); void SetPrintfAndReportCallback(void (*callback)(const char *)); + // Can be used to prevent mixing error reports from different sanitizers. extern StaticSpinMutex CommonSanitizerReportMutex; void MaybeOpenReportFile(); extern fd_t report_fd; +extern bool log_to_file; +extern char report_path_prefix[4096]; +extern uptr report_fd_pid; uptr OpenFile(const char *filename, bool write); // Opens the file 'file_name" and reads up to 'max_len' bytes. @@ -128,6 +149,14 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, // in '*buff_size'. void *MapFileToMemory(const char *file_name, uptr *buff_size); +// Error report formatting. +const char *StripPathPrefix(const char *filepath, + const char *strip_file_prefix); +void PrintSourceLocation(InternalScopedString *buffer, const char *file, + int line, int column); +void PrintModuleAndOffset(InternalScopedString *buffer, + const char *module, uptr offset); + // OS void DisableCoreDumper(); void DumpProcessMap(); @@ -135,6 +164,7 @@ bool FileExists(const char *filename); const char *GetEnv(const char *name); bool SetEnv(const char *name, const char *value); const char *GetPwd(); +char *FindPathToBinary(const char *name); u32 GetUid(); void ReExec(); bool StackSizeIsUnlimited(); @@ -150,11 +180,14 @@ void SleepForMillis(int millis); u64 NanoTime(); int Atexit(void (*function)(void)); void SortArray(uptr *array, uptr size); +// Strip the directories from the module name, return a new string allocated +// with internal_strdup. +char *StripModuleName(const char *module); // Exit void NORETURN Abort(); void NORETURN Die(); -void NORETURN SANITIZER_INTERFACE_ATTRIBUTE +void NORETURN CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); // Set the name of the current thread to 'name', return true on succees. @@ -166,19 +199,27 @@ bool SanitizerGetThreadName(char *name, int max_len); // Specific tools may override behavior of "Die" and "CheckFailed" functions // to do tool-specific job. -void SetDieCallback(void (*callback)(void)); +typedef void (*DieCallbackType)(void); +void SetDieCallback(DieCallbackType); +DieCallbackType GetDieCallback(); typedef void (*CheckFailedCallbackType)(const char *, int, const char *, u64, u64); void SetCheckFailedCallback(CheckFailedCallbackType callback); -// Construct a one-line string like -// SanitizerToolName: error_type file:line function -// and call __sanitizer_report_error_summary on it. +// We don't want a summary too long. +const int kMaxSummaryLength = 1024; +// Construct a one-line string: +// SUMMARY: SanitizerToolName: error_message +// and pass it to __sanitizer_report_error_summary. +void ReportErrorSummary(const char *error_message); +// Same as above, but construct error_message as: +// error_type: file:line function void ReportErrorSummary(const char *error_type, const char *file, int line, const char *function); +void ReportErrorSummary(const char *error_type, StackTrace *trace); // Math -#if SANITIZER_WINDOWS && !defined(__clang__) +#if SANITIZER_WINDOWS && !defined(__clang__) && !defined(__GNUC__) extern "C" { unsigned char _BitScanForward(unsigned long *index, unsigned long mask); // NOLINT unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); // NOLINT @@ -192,7 +233,7 @@ unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask); / INLINE uptr MostSignificantSetBitIndex(uptr x) { CHECK_NE(x, 0U); unsigned long up; // NOLINT -#if !SANITIZER_WINDOWS || defined(__clang__) +#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x); #elif defined(_WIN64) _BitScanReverse64(&up, x); @@ -231,7 +272,7 @@ INLINE bool IsAligned(uptr a, uptr alignment) { INLINE uptr Log2(uptr x) { CHECK(IsPowerOfTwo(x)); -#if !SANITIZER_WINDOWS || defined(__clang__) +#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) return __builtin_ctzl(x); #elif defined(_WIN64) unsigned long ret; // NOLINT @@ -276,15 +317,15 @@ INLINE int ToLower(int c) { // small vectors. // WARNING: The current implementation supports only POD types. template<typename T> -class InternalVector { +class InternalMmapVector { public: - explicit InternalVector(uptr initial_capacity) { + explicit InternalMmapVector(uptr initial_capacity) { CHECK_GT(initial_capacity, 0); capacity_ = initial_capacity; size_ = 0; - data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalVector"); + data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVector"); } - ~InternalVector() { + ~InternalMmapVector() { UnmapOrDie(data_, capacity_ * sizeof(T)); } T &operator[](uptr i) { @@ -321,12 +362,14 @@ class InternalVector { return capacity_; } + void clear() { size_ = 0; } + private: void Resize(uptr new_capacity) { CHECK_GT(new_capacity, 0); CHECK_LE(size_, new_capacity); T *new_data = (T *)MmapOrDie(new_capacity * sizeof(T), - "InternalVector"); + "InternalMmapVector"); internal_memcpy(new_data, data_, size_ * sizeof(T)); T *old_data = data_; data_ = new_data; @@ -334,15 +377,15 @@ class InternalVector { capacity_ = new_capacity; } // Disallow evil constructors. - InternalVector(const InternalVector&); - void operator=(const InternalVector&); + InternalMmapVector(const InternalMmapVector&); + void operator=(const InternalMmapVector&); T *data_; uptr capacity_; uptr size_; }; -// HeapSort for arrays and InternalVector. +// HeapSort for arrays and InternalMmapVector. template<class Container, class Compare> void InternalSort(Container *v, uptr size, Compare comp) { if (size < 2) @@ -379,6 +422,67 @@ void InternalSort(Container *v, uptr size, Compare comp) { } } +template<class Container, class Value, class Compare> +uptr InternalBinarySearch(const Container &v, uptr first, uptr last, + const Value &val, Compare comp) { + uptr not_found = last + 1; + while (last >= first) { + uptr mid = (first + last) / 2; + if (comp(v[mid], val)) + first = mid + 1; + else if (comp(val, v[mid])) + last = mid - 1; + else + return mid; + } + return not_found; +} + +// Represents a binary loaded into virtual memory (e.g. this can be an +// executable or a shared object). +class LoadedModule { + public: + LoadedModule(const char *module_name, uptr base_address); + void addAddressRange(uptr beg, uptr end); + bool containsAddress(uptr address) const; + + const char *full_name() const { return full_name_; } + uptr base_address() const { return base_address_; } + + private: + struct AddressRange { + uptr beg; + uptr end; + }; + char *full_name_; + uptr base_address_; + static const uptr kMaxNumberOfAddressRanges = 6; + AddressRange ranges_[kMaxNumberOfAddressRanges]; + uptr n_ranges_; +}; + +// OS-dependent function that fills array with descriptions of at most +// "max_modules" currently loaded modules. Returns the number of +// initialized modules. If filter is nonzero, ignores modules for which +// filter(full_name) is false. +typedef bool (*string_predicate_t)(const char *); +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter); + +#if SANITIZER_POSIX +const uptr kPthreadDestructorIterations = 4; +#else +// Unused on Windows. +const uptr kPthreadDestructorIterations = 0; +#endif + +// Callback type for iterating over a set of memory ranges. +typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg); } // namespace __sanitizer +inline void *operator new(__sanitizer::operator_new_size_type size, + __sanitizer::LowLevelAllocator &alloc) { + return alloc.Allocate(size); +} + #endif // SANITIZER_COMMON_H diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc index 8c0fb55f3ce9..d1c8976781c7 100644 --- a/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -15,9 +15,16 @@ // COMMON_INTERCEPTOR_ENTER // COMMON_INTERCEPTOR_READ_RANGE // COMMON_INTERCEPTOR_WRITE_RANGE +// COMMON_INTERCEPTOR_INITIALIZE_RANGE // COMMON_INTERCEPTOR_FD_ACQUIRE // COMMON_INTERCEPTOR_FD_RELEASE +// COMMON_INTERCEPTOR_FD_ACCESS // COMMON_INTERCEPTOR_SET_THREAD_NAME +// COMMON_INTERCEPTOR_ON_EXIT +// COMMON_INTERCEPTOR_MUTEX_LOCK +// COMMON_INTERCEPTOR_MUTEX_UNLOCK +// COMMON_INTERCEPTOR_MUTEX_REPAIR +// COMMON_INTERCEPTOR_SET_PTHREAD_NAME //===----------------------------------------------------------------------===// #include "interception/interception.h" #include "sanitizer_platform_interceptors.h" @@ -28,6 +35,68 @@ #define va_copy(dst, src) ((dst) = (src)) #endif // _WIN32 +#ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE +#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, p, size) {} +#endif + +#ifndef COMMON_INTERCEPTOR_FD_ACCESS +#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) {} +#endif + +#ifndef COMMON_INTERCEPTOR_MUTEX_LOCK +#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) {} +#endif + +#ifndef COMMON_INTERCEPTOR_MUTEX_UNLOCK +#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) {} +#endif + +#ifndef COMMON_INTERCEPTOR_MUTEX_REPAIR +#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) {} +#endif + +#if SANITIZER_INTERCEPT_STRCMP +static inline int CharCmpX(unsigned char c1, unsigned char c2) { + return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; +} + +INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strcmp, s1, s2); + unsigned char c1, c2; + uptr i; + for (i = 0;; i++) { + c1 = (unsigned char)s1[i]; + c2 = (unsigned char)s2[i]; + if (c1 != c2 || c1 == '\0') break; + } + COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1); + return CharCmpX(c1, c2); +} + +INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strncmp, s1, s2, size); + unsigned char c1 = 0, c2 = 0; + uptr i; + for (i = 0; i < size; i++) { + c1 = (unsigned char)s1[i]; + c2 = (unsigned char)s2[i]; + if (c1 != c2 || c1 == '\0') break; + } + COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size)); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size)); + return CharCmpX(c1, c2); +} + +#define INIT_STRCMP COMMON_INTERCEPT_FUNCTION(strcmp) +#define INIT_STRNCMP COMMON_INTERCEPT_FUNCTION(strncmp) +#else +#define INIT_STRCMP +#define INIT_STRNCMP +#endif + #if SANITIZER_INTERCEPT_STRCASECMP static inline int CharCaseCmp(unsigned char c1, unsigned char c2) { int c1_low = ToLower(c1); @@ -40,11 +109,10 @@ INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { COMMON_INTERCEPTOR_ENTER(ctx, strcasecmp, s1, s2); unsigned char c1 = 0, c2 = 0; uptr i; - for (i = 0; ; i++) { + for (i = 0;; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; - if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') - break; + if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; } COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1); @@ -59,16 +127,15 @@ INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T n) { for (i = 0; i < n; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; - if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') - break; + if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; } COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, n)); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, n)); return CharCaseCmp(c1, c2); } -#define INIT_STRCASECMP INTERCEPT_FUNCTION(strcasecmp) -#define INIT_STRNCASECMP INTERCEPT_FUNCTION(strncasecmp) +#define INIT_STRCASECMP COMMON_INTERCEPT_FUNCTION(strcasecmp) +#define INIT_STRNCASECMP COMMON_INTERCEPT_FUNCTION(strncasecmp) #else #define INIT_STRCASECMP #define INIT_STRNCASECMP @@ -83,10 +150,10 @@ INTERCEPTOR(double, frexp, double x, int *exp) { return res; } -#define INIT_FREXP INTERCEPT_FUNCTION(frexp); +#define INIT_FREXP COMMON_INTERCEPT_FUNCTION(frexp); #else #define INIT_FREXP -#endif // SANITIZER_INTERCEPT_FREXP +#endif // SANITIZER_INTERCEPT_FREXP #if SANITIZER_INTERCEPT_FREXPF_FREXPL INTERCEPTOR(float, frexpf, float x, int *exp) { @@ -105,25 +172,45 @@ INTERCEPTOR(long double, frexpl, long double x, int *exp) { return res; } -#define INIT_FREXPF_FREXPL \ - INTERCEPT_FUNCTION(frexpf); \ - INTERCEPT_FUNCTION(frexpl) +#define INIT_FREXPF_FREXPL \ + COMMON_INTERCEPT_FUNCTION(frexpf); \ + COMMON_INTERCEPT_FUNCTION(frexpl) #else #define INIT_FREXPF_FREXPL -#endif // SANITIZER_INTERCEPT_FREXPF_FREXPL +#endif // SANITIZER_INTERCEPT_FREXPF_FREXPL + +#if SI_NOT_WINDOWS +static void write_iovec(void *ctx, struct __sanitizer_iovec *iovec, + SIZE_T iovlen, SIZE_T maxlen) { + for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { + SSIZE_T sz = Min(iovec[i].iov_len, maxlen); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec[i].iov_base, sz); + maxlen -= sz; + } +} + +static void read_iovec(void *ctx, struct __sanitizer_iovec *iovec, + SIZE_T iovlen, SIZE_T maxlen) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec) * iovlen); + for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { + SSIZE_T sz = Min(iovec[i].iov_len, maxlen); + COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec[i].iov_base, sz); + maxlen -= sz; + } +} +#endif #if SANITIZER_INTERCEPT_READ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, read, fd, ptr, count); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(read)(fd, ptr, count); - if (res > 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); - if (res >= 0 && fd >= 0) - COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -#define INIT_READ INTERCEPT_FUNCTION(read) +#define INIT_READ COMMON_INTERCEPT_FUNCTION(read) #else #define INIT_READ #endif @@ -132,14 +219,13 @@ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread, fd, ptr, count, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(pread)(fd, ptr, count, offset); - if (res > 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); - if (res >= 0 && fd >= 0) - COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -#define INIT_PREAD INTERCEPT_FUNCTION(pread) +#define INIT_PREAD COMMON_INTERCEPT_FUNCTION(pread) #else #define INIT_PREAD #endif @@ -148,30 +234,77 @@ INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread64, fd, ptr, count, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(pread64)(fd, ptr, count, offset); - if (res > 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); - if (res >= 0 && fd >= 0) - COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -#define INIT_PREAD64 INTERCEPT_FUNCTION(pread64) +#define INIT_PREAD64 COMMON_INTERCEPT_FUNCTION(pread64) #else #define INIT_PREAD64 #endif +#if SANITIZER_INTERCEPT_READV +INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov, + int iovcnt) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readv, fd, iov, iovcnt); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SSIZE_T res = REAL(readv)(fd, iov, iovcnt); + if (res > 0) write_iovec(ctx, iov, iovcnt, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_READV COMMON_INTERCEPT_FUNCTION(readv) +#else +#define INIT_READV +#endif + +#if SANITIZER_INTERCEPT_PREADV +INTERCEPTOR(SSIZE_T, preadv, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, preadv, fd, iov, iovcnt, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SSIZE_T res = REAL(preadv)(fd, iov, iovcnt, offset); + if (res > 0) write_iovec(ctx, iov, iovcnt, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_PREADV COMMON_INTERCEPT_FUNCTION(preadv) +#else +#define INIT_PREADV +#endif + +#if SANITIZER_INTERCEPT_PREADV64 +INTERCEPTOR(SSIZE_T, preadv64, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF64_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, preadv64, fd, iov, iovcnt, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SSIZE_T res = REAL(preadv64)(fd, iov, iovcnt, offset); + if (res > 0) write_iovec(ctx, iov, iovcnt, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_PREADV64 COMMON_INTERCEPT_FUNCTION(preadv64) +#else +#define INIT_PREADV64 +#endif + #if SANITIZER_INTERCEPT_WRITE INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count); - if (fd >= 0) - COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(write)(fd, ptr, count); - if (res > 0) - COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); + // FIXME: this check should be _before_ the call to REAL(write), not after + if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } -#define INIT_WRITE INTERCEPT_FUNCTION(write) +#define INIT_WRITE COMMON_INTERCEPT_FUNCTION(write) #else #define INIT_WRITE #endif @@ -180,14 +313,13 @@ INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count, offset); - if (fd >= 0) - COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwrite)(fd, ptr, count, offset); - if (res > 0) - COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); + if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } -#define INIT_PWRITE INTERCEPT_FUNCTION(pwrite) +#define INIT_PWRITE COMMON_INTERCEPT_FUNCTION(pwrite) #else #define INIT_PWRITE #endif @@ -197,22 +329,69 @@ INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count, offset); - if (fd >= 0) - COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwrite64)(fd, ptr, count, offset); - if (res > 0) - COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); + if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } -#define INIT_PWRITE64 INTERCEPT_FUNCTION(pwrite64) +#define INIT_PWRITE64 COMMON_INTERCEPT_FUNCTION(pwrite64) #else #define INIT_PWRITE64 #endif +#if SANITIZER_INTERCEPT_WRITEV +INTERCEPTOR_WITH_SUFFIX(SSIZE_T, writev, int fd, __sanitizer_iovec *iov, + int iovcnt) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, writev, fd, iov, iovcnt); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(writev)(fd, iov, iovcnt); + if (res > 0) read_iovec(ctx, iov, iovcnt, res); + return res; +} +#define INIT_WRITEV COMMON_INTERCEPT_FUNCTION(writev) +#else +#define INIT_WRITEV +#endif + +#if SANITIZER_INTERCEPT_PWRITEV +INTERCEPTOR(SSIZE_T, pwritev, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwritev, fd, iov, iovcnt, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(pwritev)(fd, iov, iovcnt, offset); + if (res > 0) read_iovec(ctx, iov, iovcnt, res); + return res; +} +#define INIT_PWRITEV COMMON_INTERCEPT_FUNCTION(pwritev) +#else +#define INIT_PWRITEV +#endif + +#if SANITIZER_INTERCEPT_PWRITEV64 +INTERCEPTOR(SSIZE_T, pwritev64, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF64_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwritev64, fd, iov, iovcnt, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(pwritev64)(fd, iov, iovcnt, offset); + if (res > 0) read_iovec(ctx, iov, iovcnt, res); + return res; +} +#define INIT_PWRITEV64 COMMON_INTERCEPT_FUNCTION(pwritev64) +#else +#define INIT_PWRITEV64 +#endif + #if SANITIZER_INTERCEPT_PRCTL -INTERCEPTOR(int, prctl, int option, - unsigned long arg2, unsigned long arg3, // NOLINT - unsigned long arg4, unsigned long arg5) { // NOLINT +INTERCEPTOR(int, prctl, int option, unsigned long arg2, + unsigned long arg3, // NOLINT + unsigned long arg4, unsigned long arg5) { // NOLINT void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5); static const int PR_SET_NAME = 15; @@ -225,11 +404,10 @@ INTERCEPTOR(int, prctl, int option, } return res; } -#define INIT_PRCTL INTERCEPT_FUNCTION(prctl) +#define INIT_PRCTL COMMON_INTERCEPT_FUNCTION(prctl) #else #define INIT_PRCTL -#endif // SANITIZER_INTERCEPT_PRCTL - +#endif // SANITIZER_INTERCEPT_PRCTL #if SANITIZER_INTERCEPT_TIME INTERCEPTOR(unsigned long, time, unsigned long *t) { @@ -241,51 +419,58 @@ INTERCEPTOR(unsigned long, time, unsigned long *t) { } return res; } -#define INIT_TIME \ - INTERCEPT_FUNCTION(time); +#define INIT_TIME COMMON_INTERCEPT_FUNCTION(time); #else #define INIT_TIME -#endif // SANITIZER_INTERCEPT_TIME - +#endif // SANITIZER_INTERCEPT_TIME #if SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS -INTERCEPTOR(void *, localtime, unsigned long *timep) { +static void unpoison_tm(void *ctx, __sanitizer_tm *tm) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); + if (tm->tm_zone) { + // Can not use COMMON_INTERCEPTOR_WRITE_RANGE here, because tm->tm_zone + // can point to shared memory and tsan would report a data race. + COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, tm->tm_zone, + REAL(strlen(tm->tm_zone)) + 1); + } +} +INTERCEPTOR(__sanitizer_tm *, localtime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, localtime, timep); - void *res = REAL(localtime)(timep); + __sanitizer_tm *res = REAL(localtime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_tm_sz); + unpoison_tm(ctx, res); } return res; } -INTERCEPTOR(void *, localtime_r, unsigned long *timep, void *result) { +INTERCEPTOR(__sanitizer_tm *, localtime_r, unsigned long *timep, void *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, localtime_r, timep, result); - void *res = REAL(localtime_r)(timep, result); + __sanitizer_tm *res = REAL(localtime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_tm_sz); + unpoison_tm(ctx, res); } return res; } -INTERCEPTOR(void *, gmtime, unsigned long *timep) { +INTERCEPTOR(__sanitizer_tm *, gmtime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gmtime, timep); - void *res = REAL(gmtime)(timep); + __sanitizer_tm *res = REAL(gmtime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_tm_sz); + unpoison_tm(ctx, res); } return res; } -INTERCEPTOR(void *, gmtime_r, unsigned long *timep, void *result) { +INTERCEPTOR(__sanitizer_tm *, gmtime_r, unsigned long *timep, void *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gmtime_r, timep, result); - void *res = REAL(gmtime_r)(timep, result); + __sanitizer_tm *res = REAL(gmtime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_tm_sz); + unpoison_tm(ctx, res); } return res; } @@ -309,38 +494,59 @@ INTERCEPTOR(char *, ctime_r, unsigned long *timep, char *result) { } return res; } -INTERCEPTOR(char *, asctime, void *tm) { +INTERCEPTOR(char *, asctime, __sanitizer_tm *tm) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, asctime, tm); char *res = REAL(asctime)(tm); if (res) { - COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, struct_tm_sz); + COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } -INTERCEPTOR(char *, asctime_r, void *tm, char *result) { +INTERCEPTOR(char *, asctime_r, __sanitizer_tm *tm, char *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, asctime_r, tm, result); char *res = REAL(asctime_r)(tm, result); if (res) { - COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, struct_tm_sz); + COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } -#define INIT_LOCALTIME_AND_FRIENDS \ - INTERCEPT_FUNCTION(localtime); \ - INTERCEPT_FUNCTION(localtime_r); \ - INTERCEPT_FUNCTION(gmtime); \ - INTERCEPT_FUNCTION(gmtime_r); \ - INTERCEPT_FUNCTION(ctime); \ - INTERCEPT_FUNCTION(ctime_r); \ - INTERCEPT_FUNCTION(asctime); \ - INTERCEPT_FUNCTION(asctime_r); +#define INIT_LOCALTIME_AND_FRIENDS \ + COMMON_INTERCEPT_FUNCTION(localtime); \ + COMMON_INTERCEPT_FUNCTION(localtime_r); \ + COMMON_INTERCEPT_FUNCTION(gmtime); \ + COMMON_INTERCEPT_FUNCTION(gmtime_r); \ + COMMON_INTERCEPT_FUNCTION(ctime); \ + COMMON_INTERCEPT_FUNCTION(ctime_r); \ + COMMON_INTERCEPT_FUNCTION(asctime); \ + COMMON_INTERCEPT_FUNCTION(asctime_r); #else #define INIT_LOCALTIME_AND_FRIENDS -#endif // SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS +#endif // SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS + +#if SANITIZER_INTERCEPT_STRPTIME +INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strptime, s, format, tm); + if (format) + COMMON_INTERCEPTOR_READ_RANGE(ctx, format, REAL(strlen)(format) + 1); + char *res = REAL(strptime)(s, format, tm); + if (res) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, s, res - s); + // Do not call unpoison_tm here, because strptime does not, in fact, + // initialize the entire struct tm. For example, tm_zone pointer is left + // uninitialized. + if (tm) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); + } + return res; +} +#define INIT_STRPTIME COMMON_INTERCEPT_FUNCTION(strptime); +#else +#define INIT_STRPTIME +#endif #if SANITIZER_INTERCEPT_SCANF @@ -383,9 +589,9 @@ VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap) #define SCANF_INTERCEPTOR_IMPL(name, vname, ...) \ { \ void *ctx; \ - COMMON_INTERCEPTOR_ENTER(ctx, name, __VA_ARGS__); \ va_list ap; \ va_start(ap, format); \ + COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__, ap); \ int res = vname(__VA_ARGS__, ap); \ va_end(ap); \ return res; \ @@ -411,40 +617,74 @@ INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...) SCANF_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) #endif -#define INIT_SCANF \ - INTERCEPT_FUNCTION(scanf); \ - INTERCEPT_FUNCTION(sscanf); \ - INTERCEPT_FUNCTION(fscanf); \ - INTERCEPT_FUNCTION(vscanf); \ - INTERCEPT_FUNCTION(vsscanf); \ - INTERCEPT_FUNCTION(vfscanf); \ - INTERCEPT_FUNCTION(__isoc99_scanf); \ - INTERCEPT_FUNCTION(__isoc99_sscanf); \ - INTERCEPT_FUNCTION(__isoc99_fscanf); \ - INTERCEPT_FUNCTION(__isoc99_vscanf); \ - INTERCEPT_FUNCTION(__isoc99_vsscanf); \ - INTERCEPT_FUNCTION(__isoc99_vfscanf); +#endif +#if SANITIZER_INTERCEPT_SCANF +#define INIT_SCANF \ + COMMON_INTERCEPT_FUNCTION(scanf); \ + COMMON_INTERCEPT_FUNCTION(sscanf); \ + COMMON_INTERCEPT_FUNCTION(fscanf); \ + COMMON_INTERCEPT_FUNCTION(vscanf); \ + COMMON_INTERCEPT_FUNCTION(vsscanf); \ + COMMON_INTERCEPT_FUNCTION(vfscanf); #else #define INIT_SCANF #endif +#if SANITIZER_INTERCEPT_ISOC99_SCANF +#define INIT_ISOC99_SCANF \ + COMMON_INTERCEPT_FUNCTION(__isoc99_scanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_sscanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_fscanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vscanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vsscanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vfscanf); +#else +#define INIT_ISOC99_SCANF +#endif + +#if SANITIZER_INTERCEPT_IOCTL +#include "sanitizer_common_interceptors_ioctl.inc" +INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ioctl, d, request, arg); + + CHECK(ioctl_initialized); + + // Note: TSan does not use common flags, and they are zero-initialized. + // This effectively disables ioctl handling in TSan. + if (!common_flags()->handle_ioctl) return REAL(ioctl)(d, request, arg); + + const ioctl_desc *desc = ioctl_lookup(request); + if (!desc) Printf("WARNING: unknown ioctl %x\n", request); + + if (desc) ioctl_common_pre(ctx, desc, d, request, arg); + int res = REAL(ioctl)(d, request, arg); + // FIXME: some ioctls have different return values for success and failure. + if (desc && res != -1) ioctl_common_post(ctx, desc, res, d, request, arg); + return res; +} +#define INIT_IOCTL \ + ioctl_init(); \ + COMMON_INTERCEPT_FUNCTION(ioctl); +#else +#define INIT_IOCTL +#endif + #if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS INTERCEPTOR(void *, getpwnam, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwnam, name); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); void *res = REAL(getpwnam)(name); - if (res != 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_passwd_sz); + if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_passwd_sz); return res; } INTERCEPTOR(void *, getpwuid, u32 uid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwuid, uid); void *res = REAL(getpwuid)(uid); - if (res != 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_passwd_sz); + if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_passwd_sz); return res; } INTERCEPTOR(void *, getgrnam, const char *name) { @@ -452,31 +692,28 @@ INTERCEPTOR(void *, getgrnam, const char *name) { COMMON_INTERCEPTOR_ENTER(ctx, getgrnam, name); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); void *res = REAL(getgrnam)(name); - if (res != 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_group_sz); + if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_group_sz); return res; } INTERCEPTOR(void *, getgrgid, u32 gid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrgid, gid); void *res = REAL(getgrgid)(gid); - if (res != 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_group_sz); + if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_group_sz); return res; } -#define INIT_GETPWNAM_AND_FRIENDS \ - INTERCEPT_FUNCTION(getpwnam); \ - INTERCEPT_FUNCTION(getpwuid); \ - INTERCEPT_FUNCTION(getgrnam); \ - INTERCEPT_FUNCTION(getgrgid); +#define INIT_GETPWNAM_AND_FRIENDS \ + COMMON_INTERCEPT_FUNCTION(getpwnam); \ + COMMON_INTERCEPT_FUNCTION(getpwuid); \ + COMMON_INTERCEPT_FUNCTION(getgrnam); \ + COMMON_INTERCEPT_FUNCTION(getgrgid); #else #define INIT_GETPWNAM_AND_FRIENDS #endif - #if SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS -INTERCEPTOR(int, getpwnam_r, const char *name, void *pwd, - char *buf, SIZE_T buflen, void **result) { +INTERCEPTOR(int, getpwnam_r, const char *name, void *pwd, char *buf, + SIZE_T buflen, void **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwnam_r, name, pwd, buf, buflen, result); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); @@ -487,8 +724,8 @@ INTERCEPTOR(int, getpwnam_r, const char *name, void *pwd, } return res; } -INTERCEPTOR(int, getpwuid_r, u32 uid, void *pwd, - char *buf, SIZE_T buflen, void **result) { +INTERCEPTOR(int, getpwuid_r, u32 uid, void *pwd, char *buf, SIZE_T buflen, + void **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwuid_r, uid, pwd, buf, buflen, result); int res = REAL(getpwuid_r)(uid, pwd, buf, buflen, result); @@ -498,8 +735,8 @@ INTERCEPTOR(int, getpwuid_r, u32 uid, void *pwd, } return res; } -INTERCEPTOR(int, getgrnam_r, const char *name, void *grp, - char *buf, SIZE_T buflen, void **result) { +INTERCEPTOR(int, getgrnam_r, const char *name, void *grp, char *buf, + SIZE_T buflen, void **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrnam_r, name, grp, buf, buflen, result); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); @@ -510,8 +747,8 @@ INTERCEPTOR(int, getgrnam_r, const char *name, void *grp, } return res; } -INTERCEPTOR(int, getgrgid_r, u32 gid, void *grp, - char *buf, SIZE_T buflen, void **result) { +INTERCEPTOR(int, getgrgid_r, u32 gid, void *grp, char *buf, SIZE_T buflen, + void **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrgid_r, gid, grp, buf, buflen, result); int res = REAL(getgrgid_r)(gid, grp, buf, buflen, result); @@ -521,16 +758,15 @@ INTERCEPTOR(int, getgrgid_r, u32 gid, void *grp, } return res; } -#define INIT_GETPWNAM_R_AND_FRIENDS \ - INTERCEPT_FUNCTION(getpwnam_r); \ - INTERCEPT_FUNCTION(getpwuid_r); \ - INTERCEPT_FUNCTION(getgrnam_r); \ - INTERCEPT_FUNCTION(getgrgid_r); +#define INIT_GETPWNAM_R_AND_FRIENDS \ + COMMON_INTERCEPT_FUNCTION(getpwnam_r); \ + COMMON_INTERCEPT_FUNCTION(getpwuid_r); \ + COMMON_INTERCEPT_FUNCTION(getgrnam_r); \ + COMMON_INTERCEPT_FUNCTION(getgrgid_r); #else #define INIT_GETPWNAM_R_AND_FRIENDS #endif - #if SANITIZER_INTERCEPT_CLOCK_GETTIME INTERCEPTOR(int, clock_getres, u32 clk_id, void *tp) { void *ctx; @@ -556,21 +792,20 @@ INTERCEPTOR(int, clock_settime, u32 clk_id, const void *tp) { COMMON_INTERCEPTOR_READ_RANGE(ctx, tp, struct_timespec_sz); return REAL(clock_settime)(clk_id, tp); } -#define INIT_CLOCK_GETTIME \ - INTERCEPT_FUNCTION(clock_getres); \ - INTERCEPT_FUNCTION(clock_gettime); \ - INTERCEPT_FUNCTION(clock_settime); +#define INIT_CLOCK_GETTIME \ + COMMON_INTERCEPT_FUNCTION(clock_getres); \ + COMMON_INTERCEPT_FUNCTION(clock_gettime); \ + COMMON_INTERCEPT_FUNCTION(clock_settime); #else #define INIT_CLOCK_GETTIME #endif - #if SANITIZER_INTERCEPT_GETITIMER INTERCEPTOR(int, getitimer, int which, void *curr_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getitimer, which, curr_value); int res = REAL(getitimer)(which, curr_value); - if (!res) { + if (!res && curr_value) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerval_sz); } return res; @@ -578,66 +813,134 @@ INTERCEPTOR(int, getitimer, int which, void *curr_value) { INTERCEPTOR(int, setitimer, int which, const void *new_value, void *old_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setitimer, which, new_value, old_value); - COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerval_sz); + if (new_value) + COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerval_sz); int res = REAL(setitimer)(which, new_value, old_value); if (!res && old_value) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerval_sz); } return res; } -#define INIT_GETITIMER \ - INTERCEPT_FUNCTION(getitimer); \ - INTERCEPT_FUNCTION(setitimer); +#define INIT_GETITIMER \ + COMMON_INTERCEPT_FUNCTION(getitimer); \ + COMMON_INTERCEPT_FUNCTION(setitimer); #else #define INIT_GETITIMER #endif - #if SANITIZER_INTERCEPT_GLOB -struct sanitizer_glob_t { - SIZE_T gl_pathc; - char **gl_pathv; -}; - -static void unpoison_glob_t(void *ctx, sanitizer_glob_t *pglob) { +static void unpoison_glob_t(void *ctx, __sanitizer_glob_t *pglob) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pglob, sizeof(*pglob)); // +1 for NULL pointer at the end. - COMMON_INTERCEPTOR_WRITE_RANGE( - ctx, pglob->gl_pathv, (pglob->gl_pathc + 1) * sizeof(*pglob->gl_pathv)); + if (pglob->gl_pathv) + COMMON_INTERCEPTOR_WRITE_RANGE( + ctx, pglob->gl_pathv, (pglob->gl_pathc + 1) * sizeof(*pglob->gl_pathv)); for (SIZE_T i = 0; i < pglob->gl_pathc; ++i) { char *p = pglob->gl_pathv[i]; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, REAL(strlen)(p) + 1); } } +static THREADLOCAL __sanitizer_glob_t *pglob_copy; +static THREADLOCAL void *glob_ctx; + +static void wrapped_gl_closedir(void *dir) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 1); + pglob_copy->gl_closedir(dir); +} + +static void *wrapped_gl_readdir(void *dir) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 1); + return pglob_copy->gl_readdir(dir); +} + +static void *wrapped_gl_opendir(const char *s) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 1); + COMMON_INTERCEPTOR_WRITE_RANGE(glob_ctx, s, REAL(strlen)(s) + 1); + return pglob_copy->gl_opendir(s); +} + +static int wrapped_gl_lstat(const char *s, void *st) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 2); + COMMON_INTERCEPTOR_WRITE_RANGE(glob_ctx, s, REAL(strlen)(s) + 1); + return pglob_copy->gl_lstat(s, st); +} + +static int wrapped_gl_stat(const char *s, void *st) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 2); + COMMON_INTERCEPTOR_WRITE_RANGE(glob_ctx, s, REAL(strlen)(s) + 1); + return pglob_copy->gl_stat(s, st); +} + INTERCEPTOR(int, glob, const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), - sanitizer_glob_t *pglob) { + __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob); + __sanitizer_glob_t glob_copy = { + 0, 0, 0, + 0, wrapped_gl_closedir, wrapped_gl_readdir, + wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; + if (flags & glob_altdirfunc) { + Swap(pglob->gl_closedir, glob_copy.gl_closedir); + Swap(pglob->gl_readdir, glob_copy.gl_readdir); + Swap(pglob->gl_opendir, glob_copy.gl_opendir); + Swap(pglob->gl_lstat, glob_copy.gl_lstat); + Swap(pglob->gl_stat, glob_copy.gl_stat); + pglob_copy = &glob_copy; + glob_ctx = ctx; + } int res = REAL(glob)(pattern, flags, errfunc, pglob); - if (res == 0) - unpoison_glob_t(ctx, pglob); + if (flags & glob_altdirfunc) { + Swap(pglob->gl_closedir, glob_copy.gl_closedir); + Swap(pglob->gl_readdir, glob_copy.gl_readdir); + Swap(pglob->gl_opendir, glob_copy.gl_opendir); + Swap(pglob->gl_lstat, glob_copy.gl_lstat); + Swap(pglob->gl_stat, glob_copy.gl_stat); + } + pglob_copy = 0; + glob_ctx = 0; + if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); return res; } INTERCEPTOR(int, glob64, const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), - sanitizer_glob_t *pglob) { + __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob); + __sanitizer_glob_t glob_copy = { + 0, 0, 0, + 0, wrapped_gl_closedir, wrapped_gl_readdir, + wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; + if (flags & glob_altdirfunc) { + Swap(pglob->gl_closedir, glob_copy.gl_closedir); + Swap(pglob->gl_readdir, glob_copy.gl_readdir); + Swap(pglob->gl_opendir, glob_copy.gl_opendir); + Swap(pglob->gl_lstat, glob_copy.gl_lstat); + Swap(pglob->gl_stat, glob_copy.gl_stat); + pglob_copy = &glob_copy; + glob_ctx = ctx; + } int res = REAL(glob64)(pattern, flags, errfunc, pglob); - if (res == 0) - unpoison_glob_t(ctx, pglob); + if (flags & glob_altdirfunc) { + Swap(pglob->gl_closedir, glob_copy.gl_closedir); + Swap(pglob->gl_readdir, glob_copy.gl_readdir); + Swap(pglob->gl_opendir, glob_copy.gl_opendir); + Swap(pglob->gl_lstat, glob_copy.gl_lstat); + Swap(pglob->gl_stat, glob_copy.gl_stat); + } + pglob_copy = 0; + glob_ctx = 0; + if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); return res; } -#define INIT_GLOB \ - INTERCEPT_FUNCTION(glob); \ - INTERCEPT_FUNCTION(glob64); -#else // SANITIZER_INTERCEPT_GLOB +#define INIT_GLOB \ + COMMON_INTERCEPT_FUNCTION(glob); \ + COMMON_INTERCEPT_FUNCTION(glob64); +#else // SANITIZER_INTERCEPT_GLOB #define INIT_GLOB -#endif // SANITIZER_INTERCEPT_GLOB - +#endif // SANITIZER_INTERCEPT_GLOB #if SANITIZER_INTERCEPT_WAIT // According to sys/wait.h, wait(), waitid(), waitpid() may have symbol version @@ -652,7 +955,7 @@ INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) { return res; } INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop, - int options) { + int options) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, waitid, idtype, id, infop, options); int res = REAL(waitid)(idtype, id, infop, options); @@ -673,10 +976,8 @@ INTERCEPTOR(int, wait3, int *status, int options, void *rusage) { COMMON_INTERCEPTOR_ENTER(ctx, wait3, status, options, rusage); int res = REAL(wait3)(status, options, rusage); if (res != -1) { - if (status) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); - if (rusage) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); + if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); + if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } @@ -685,19 +986,17 @@ INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) { COMMON_INTERCEPTOR_ENTER(ctx, wait4, pid, status, options, rusage); int res = REAL(wait4)(pid, status, options, rusage); if (res != -1) { - if (status) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); - if (rusage) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); + if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); + if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } -#define INIT_WAIT \ - INTERCEPT_FUNCTION(wait); \ - INTERCEPT_FUNCTION(waitid); \ - INTERCEPT_FUNCTION(waitpid); \ - INTERCEPT_FUNCTION(wait3); \ - INTERCEPT_FUNCTION(wait4); +#define INIT_WAIT \ + COMMON_INTERCEPT_FUNCTION(wait); \ + COMMON_INTERCEPT_FUNCTION(waitid); \ + COMMON_INTERCEPT_FUNCTION(waitpid); \ + COMMON_INTERCEPT_FUNCTION(wait3); \ + COMMON_INTERCEPT_FUNCTION(wait4); #else #define INIT_WAIT #endif @@ -710,8 +1009,7 @@ INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) { if (sz) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sz); // FIXME: figure out read size based on the address family. char *res = REAL(inet_ntop)(af, src, dst, size); - if (res) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) { @@ -725,13 +1023,30 @@ INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) { } return res; } -#define INIT_INET \ - INTERCEPT_FUNCTION(inet_ntop); \ - INTERCEPT_FUNCTION(inet_pton); +#define INIT_INET \ + COMMON_INTERCEPT_FUNCTION(inet_ntop); \ + COMMON_INTERCEPT_FUNCTION(inet_pton); #else #define INIT_INET #endif +#if SANITIZER_INTERCEPT_INET +INTERCEPTOR(int, inet_aton, const char *cp, void *dst) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, inet_aton, cp, dst); + if (cp) COMMON_INTERCEPTOR_READ_RANGE(ctx, cp, REAL(strlen)(cp) + 1); + int res = REAL(inet_aton)(cp, dst); + if (res != 0) { + uptr sz = __sanitizer_in_addr_sz(af_inet); + if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz); + } + return res; +} +#define INIT_INET_ATON COMMON_INTERCEPT_FUNCTION(inet_aton); +#else +#define INIT_INET_ATON +#endif + #if SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM INTERCEPTOR(int, pthread_getschedparam, uptr thread, int *policy, int *param) { void *ctx; @@ -743,7 +1058,8 @@ INTERCEPTOR(int, pthread_getschedparam, uptr thread, int *policy, int *param) { } return res; } -#define INIT_PTHREAD_GETSCHEDPARAM INTERCEPT_FUNCTION(pthread_getschedparam); +#define INIT_PTHREAD_GETSCHEDPARAM \ + COMMON_INTERCEPT_FUNCTION(pthread_getschedparam); #else #define INIT_PTHREAD_GETSCHEDPARAM #endif @@ -760,12 +1076,13 @@ INTERCEPTOR(int, getaddrinfo, char *node, char *service, if (hints) COMMON_INTERCEPTOR_READ_RANGE(ctx, hints, sizeof(__sanitizer_addrinfo)); int res = REAL(getaddrinfo)(node, service, hints, out); - if (res == 0) { + if (res == 0 && out) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, out, sizeof(*out)); struct __sanitizer_addrinfo *p = *out; while (p) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(__sanitizer_addrinfo)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); if (p->ai_addr) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_addr, struct_sockaddr_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_addr, p->ai_addrlen); if (p->ai_canonname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_canonname, REAL(strlen)(p->ai_canonname) + 1); @@ -774,11 +1091,34 @@ INTERCEPTOR(int, getaddrinfo, char *node, char *service, } return res; } -#define INIT_GETADDRINFO INTERCEPT_FUNCTION(getaddrinfo); +#define INIT_GETADDRINFO COMMON_INTERCEPT_FUNCTION(getaddrinfo); #else #define INIT_GETADDRINFO #endif +#if SANITIZER_INTERCEPT_GETNAMEINFO +INTERCEPTOR(int, getnameinfo, void *sockaddr, unsigned salen, char *host, + unsigned hostlen, char *serv, unsigned servlen, int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getnameinfo, sockaddr, salen, host, hostlen, + serv, servlen, flags); + // FIXME: consider adding READ_RANGE(sockaddr, salen) + // There is padding in in_addr that may make this too noisy + int res = + REAL(getnameinfo)(sockaddr, salen, host, hostlen, serv, servlen, flags); + if (res == 0) { + if (host && hostlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, host, REAL(strlen)(host) + 1); + if (serv && servlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, serv, REAL(strlen)(serv) + 1); + } + return res; +} +#define INIT_GETNAMEINFO COMMON_INTERCEPT_FUNCTION(getnameinfo); +#else +#define INIT_GETNAMEINFO +#endif + #if SANITIZER_INTERCEPT_GETSOCKNAME INTERCEPTOR(int, getsockname, int sock_fd, void *addr, int *addrlen) { void *ctx; @@ -791,7 +1131,7 @@ INTERCEPTOR(int, getsockname, int sock_fd, void *addr, int *addrlen) { } return res; } -#define INIT_GETSOCKNAME INTERCEPT_FUNCTION(getsockname); +#define INIT_GETSOCKNAME COMMON_INTERCEPT_FUNCTION(getsockname); #else #define INIT_GETSOCKNAME #endif @@ -837,10 +1177,10 @@ INTERCEPTOR(struct __sanitizer_hostent *, gethostbyaddr, void *addr, int len, return res; } -INTERCEPTOR(struct __sanitizer_hostent *, gethostent) { +INTERCEPTOR(struct __sanitizer_hostent *, gethostent, int fake) { void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, gethostent); - struct __sanitizer_hostent *res = REAL(gethostent)(); + COMMON_INTERCEPTOR_ENTER(ctx, gethostent, fake); + struct __sanitizer_hostent *res = REAL(gethostent)(fake); if (res) write_hostent(ctx, res); return res; } @@ -852,11 +1192,11 @@ INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname2, char *name, int af) { if (res) write_hostent(ctx, res); return res; } -#define INIT_GETHOSTBYNAME \ - INTERCEPT_FUNCTION(gethostent); \ - INTERCEPT_FUNCTION(gethostbyaddr); \ - INTERCEPT_FUNCTION(gethostbyname); \ - INTERCEPT_FUNCTION(gethostbyname2); +#define INIT_GETHOSTBYNAME \ + COMMON_INTERCEPT_FUNCTION(gethostent); \ + COMMON_INTERCEPT_FUNCTION(gethostbyaddr); \ + COMMON_INTERCEPT_FUNCTION(gethostbyname); \ + COMMON_INTERCEPT_FUNCTION(gethostbyname2); #else #define INIT_GETHOSTBYNAME #endif @@ -935,11 +1275,11 @@ INTERCEPTOR(int, gethostbyname2_r, char *name, int af, } return res; } -#define INIT_GETHOSTBYNAME_R \ - INTERCEPT_FUNCTION(gethostent_r); \ - INTERCEPT_FUNCTION(gethostbyaddr_r); \ - INTERCEPT_FUNCTION(gethostbyname_r); \ - INTERCEPT_FUNCTION(gethostbyname2_r); +#define INIT_GETHOSTBYNAME_R \ + COMMON_INTERCEPT_FUNCTION(gethostent_r); \ + COMMON_INTERCEPT_FUNCTION(gethostbyaddr_r); \ + COMMON_INTERCEPT_FUNCTION(gethostbyname_r); \ + COMMON_INTERCEPT_FUNCTION(gethostbyname2_r); #else #define INIT_GETHOSTBYNAME_R #endif @@ -956,23 +1296,1555 @@ INTERCEPTOR(int, getsockopt, int sockfd, int level, int optname, void *optval, if (optval && optlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, optval, *optlen); return res; } -#define INIT_GETSOCKOPT INTERCEPT_FUNCTION(getsockopt); +#define INIT_GETSOCKOPT COMMON_INTERCEPT_FUNCTION(getsockopt); #else #define INIT_GETSOCKOPT #endif +#if SANITIZER_INTERCEPT_ACCEPT +INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, accept, fd, addr, addrlen); + unsigned addrlen0; + if (addrlen) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); + addrlen0 = *addrlen; + } + int fd2 = REAL(accept)(fd, addr, addrlen); + if (fd2 >= 0) { + if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); + if (addr && addrlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); + } + return fd2; +} +#define INIT_ACCEPT COMMON_INTERCEPT_FUNCTION(accept); +#else +#define INIT_ACCEPT +#endif + +#if SANITIZER_INTERCEPT_ACCEPT4 +INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, accept4, fd, addr, addrlen, f); + unsigned addrlen0; + if (addrlen) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); + addrlen0 = *addrlen; + } + int fd2 = REAL(accept4)(fd, addr, addrlen, f); + if (fd2 >= 0) { + if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); + if (addr && addrlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); + } + return fd2; +} +#define INIT_ACCEPT4 COMMON_INTERCEPT_FUNCTION(accept4); +#else +#define INIT_ACCEPT4 +#endif + +#if SANITIZER_INTERCEPT_MODF +INTERCEPTOR(double, modf, double x, double *iptr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, modf, x, iptr); + double res = REAL(modf)(x, iptr); + if (iptr) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); + } + return res; +} +INTERCEPTOR(float, modff, float x, float *iptr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, modff, x, iptr); + float res = REAL(modff)(x, iptr); + if (iptr) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); + } + return res; +} +INTERCEPTOR(long double, modfl, long double x, long double *iptr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, modfl, x, iptr); + long double res = REAL(modfl)(x, iptr); + if (iptr) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); + } + return res; +} +#define INIT_MODF \ + COMMON_INTERCEPT_FUNCTION(modf); \ + COMMON_INTERCEPT_FUNCTION(modff); \ + COMMON_INTERCEPT_FUNCTION(modfl); +#else +#define INIT_MODF +#endif + +#if SANITIZER_INTERCEPT_RECVMSG +static void write_msghdr(void *ctx, struct __sanitizer_msghdr *msg, + SSIZE_T maxlen) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg, sizeof(*msg)); + if (msg->msg_name && msg->msg_namelen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_name, msg->msg_namelen); + if (msg->msg_iov && msg->msg_iovlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_iov, + sizeof(*msg->msg_iov) * msg->msg_iovlen); + write_iovec(ctx, msg->msg_iov, msg->msg_iovlen, maxlen); + if (msg->msg_control && msg->msg_controllen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_control, msg->msg_controllen); +} + +INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg, + int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, recvmsg, fd, msg, flags); + SSIZE_T res = REAL(recvmsg)(fd, msg, flags); + if (res >= 0) { + if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + if (msg) write_msghdr(ctx, msg, res); + } + return res; +} +#define INIT_RECVMSG COMMON_INTERCEPT_FUNCTION(recvmsg); +#else +#define INIT_RECVMSG +#endif + +#if SANITIZER_INTERCEPT_GETPEERNAME +INTERCEPTOR(int, getpeername, int sockfd, void *addr, unsigned *addrlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getpeername, sockfd, addr, addrlen); + unsigned addr_sz; + if (addrlen) addr_sz = *addrlen; + int res = REAL(getpeername)(sockfd, addr, addrlen); + if (!res && addr && addrlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen)); + return res; +} +#define INIT_GETPEERNAME COMMON_INTERCEPT_FUNCTION(getpeername); +#else +#define INIT_GETPEERNAME +#endif + +#if SANITIZER_INTERCEPT_SYSINFO +INTERCEPTOR(int, sysinfo, void *info) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sysinfo, info); + int res = REAL(sysinfo)(info); + if (!res && info) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, struct_sysinfo_sz); + return res; +} +#define INIT_SYSINFO COMMON_INTERCEPT_FUNCTION(sysinfo); +#else +#define INIT_SYSINFO +#endif + +#if SANITIZER_INTERCEPT_READDIR +INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp); + __sanitizer_dirent *res = REAL(readdir)(dirp); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); + return res; +} + +INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry, + __sanitizer_dirent **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readdir_r, dirp, entry, result); + int res = REAL(readdir_r)(dirp, entry, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + if (*result) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); + } + return res; +} + +#define INIT_READDIR \ + COMMON_INTERCEPT_FUNCTION(readdir); \ + COMMON_INTERCEPT_FUNCTION(readdir_r); +#else +#define INIT_READDIR +#endif + +#if SANITIZER_INTERCEPT_READDIR64 +INTERCEPTOR(__sanitizer_dirent64 *, readdir64, void *dirp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readdir64, dirp); + __sanitizer_dirent64 *res = REAL(readdir64)(dirp); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); + return res; +} + +INTERCEPTOR(int, readdir64_r, void *dirp, __sanitizer_dirent64 *entry, + __sanitizer_dirent64 **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readdir64_r, dirp, entry, result); + int res = REAL(readdir64_r)(dirp, entry, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + if (*result) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); + } + return res; +} +#define INIT_READDIR64 \ + COMMON_INTERCEPT_FUNCTION(readdir64); \ + COMMON_INTERCEPT_FUNCTION(readdir64_r); +#else +#define INIT_READDIR64 +#endif + +#if SANITIZER_INTERCEPT_PTRACE +INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data); + + if (data) { + if (request == ptrace_setregs) + COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_regs_struct_sz); + else if (request == ptrace_setfpregs) + COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz); + else if (request == ptrace_setfpxregs) + COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz); + else if (request == ptrace_setsiginfo) + COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz); + else if (request == ptrace_setregset) { + __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + COMMON_INTERCEPTOR_READ_RANGE(ctx, iov->iov_base, iov->iov_len); + } + } + + uptr res = REAL(ptrace)(request, pid, addr, data); + + if (!res && data) { + // Note that PEEK* requests assing different meaning to the return value. + // This function does not handle them (nor does it need to). + if (request == ptrace_getregs) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_regs_struct_sz); + else if (request == ptrace_getfpregs) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz); + else if (request == ptrace_getfpxregs) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz); + else if (request == ptrace_getsiginfo) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz); + else if (request == ptrace_getregset) { + __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iov->iov_base, iov->iov_len); + } + } + return res; +} + +#define INIT_PTRACE COMMON_INTERCEPT_FUNCTION(ptrace); +#else +#define INIT_PTRACE +#endif + +#if SANITIZER_INTERCEPT_SETLOCALE +INTERCEPTOR(char *, setlocale, int category, char *locale) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, setlocale, category, locale); + if (locale) + COMMON_INTERCEPTOR_READ_RANGE(ctx, locale, REAL(strlen)(locale) + 1); + char *res = REAL(setlocale)(category, locale); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} + +#define INIT_SETLOCALE COMMON_INTERCEPT_FUNCTION(setlocale); +#else +#define INIT_SETLOCALE +#endif + +#if SANITIZER_INTERCEPT_GETCWD +INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getcwd, buf, size); + char *res = REAL(getcwd)(buf, size); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_GETCWD COMMON_INTERCEPT_FUNCTION(getcwd); +#else +#define INIT_GETCWD +#endif + +#if SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME +INTERCEPTOR(char *, get_current_dir_name, int fake) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, get_current_dir_name, fake); + char *res = REAL(get_current_dir_name)(fake); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} + +#define INIT_GET_CURRENT_DIR_NAME \ + COMMON_INTERCEPT_FUNCTION(get_current_dir_name); +#else +#define INIT_GET_CURRENT_DIR_NAME +#endif + +#if SANITIZER_INTERCEPT_STRTOIMAX +INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strtoimax, nptr, endptr, base); + INTMAX_T res = REAL(strtoimax)(nptr, endptr, base); + if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); + return res; +} + +INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strtoumax, nptr, endptr, base); + INTMAX_T res = REAL(strtoumax)(nptr, endptr, base); + if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); + return res; +} + +#define INIT_STRTOIMAX \ + COMMON_INTERCEPT_FUNCTION(strtoimax); \ + COMMON_INTERCEPT_FUNCTION(strtoumax); +#else +#define INIT_STRTOIMAX +#endif + +#if SANITIZER_INTERCEPT_MBSTOWCS +INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T len) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, mbstowcs, dest, src, len); + SIZE_T res = REAL(mbstowcs)(dest, src, len); + if (res != (SIZE_T) - 1 && dest) { + SIZE_T write_cnt = res + (res < len); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); + } + return res; +} + +INTERCEPTOR(SIZE_T, mbsrtowcs, wchar_t *dest, const char **src, SIZE_T len, + void *ps) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, mbsrtowcs, dest, src, len, ps); + if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + SIZE_T res = REAL(mbsrtowcs)(dest, src, len, ps); + if (res != (SIZE_T)(-1) && dest && src) { + // This function, and several others, may or may not write the terminating + // \0 character. They write it iff they clear *src. + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); + } + return res; +} + +#define INIT_MBSTOWCS \ + COMMON_INTERCEPT_FUNCTION(mbstowcs); \ + COMMON_INTERCEPT_FUNCTION(mbsrtowcs); +#else +#define INIT_MBSTOWCS +#endif + +#if SANITIZER_INTERCEPT_MBSNRTOWCS +INTERCEPTOR(SIZE_T, mbsnrtowcs, wchar_t *dest, const char **src, SIZE_T nms, + SIZE_T len, void *ps) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, mbsnrtowcs, dest, src, nms, len, ps); + if (src) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); + } + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + SIZE_T res = REAL(mbsnrtowcs)(dest, src, nms, len, ps); + if (res != (SIZE_T)(-1) && dest && src) { + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); + } + return res; +} + +#define INIT_MBSNRTOWCS COMMON_INTERCEPT_FUNCTION(mbsnrtowcs); +#else +#define INIT_MBSNRTOWCS +#endif + +#if SANITIZER_INTERCEPT_WCSTOMBS +INTERCEPTOR(SIZE_T, wcstombs, char *dest, const wchar_t *src, SIZE_T len) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcstombs, dest, src, len); + SIZE_T res = REAL(wcstombs)(dest, src, len); + if (res != (SIZE_T) - 1 && dest) { + SIZE_T write_cnt = res + (res < len); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +INTERCEPTOR(SIZE_T, wcsrtombs, char *dest, const wchar_t **src, SIZE_T len, + void *ps) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcsrtombs, dest, src, len, ps); + if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + SIZE_T res = REAL(wcsrtombs)(dest, src, len, ps); + if (res != (SIZE_T) - 1 && dest && src) { + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +#define INIT_WCSTOMBS \ + COMMON_INTERCEPT_FUNCTION(wcstombs); \ + COMMON_INTERCEPT_FUNCTION(wcsrtombs); +#else +#define INIT_WCSTOMBS +#endif + +#if SANITIZER_INTERCEPT_WCSNRTOMBS +INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms, + SIZE_T len, void *ps) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcsnrtombs, dest, src, nms, len, ps); + if (src) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); + } + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + SIZE_T res = REAL(wcsnrtombs)(dest, src, nms, len, ps); + if (res != (SIZE_T) - 1 && dest && src) { + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +#define INIT_WCSNRTOMBS COMMON_INTERCEPT_FUNCTION(wcsnrtombs); +#else +#define INIT_WCSNRTOMBS +#endif + +#if SANITIZER_INTERCEPT_TCGETATTR +INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, tcgetattr, fd, termios_p); + int res = REAL(tcgetattr)(fd, termios_p); + if (!res && termios_p) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, termios_p, struct_termios_sz); + return res; +} + +#define INIT_TCGETATTR COMMON_INTERCEPT_FUNCTION(tcgetattr); +#else +#define INIT_TCGETATTR +#endif + +#if SANITIZER_INTERCEPT_REALPATH +INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, realpath, path, resolved_path); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + + // Workaround a bug in glibc where dlsym(RTLD_NEXT, ...) returns the oldest + // version of a versioned symbol. For realpath(), this gives us something + // (called __old_realpath) that does not handle NULL in the second argument. + // Handle it as part of the interceptor. + char *allocated_path = 0; + if (!resolved_path) + allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1); + + char *res = REAL(realpath)(path, resolved_path); + if (allocated_path && !res) WRAP(free)(allocated_path); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_REALPATH COMMON_INTERCEPT_FUNCTION(realpath); +#else +#define INIT_REALPATH +#endif + +#if SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME +INTERCEPTOR(char *, canonicalize_file_name, const char *path) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, canonicalize_file_name, path); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + char *res = REAL(canonicalize_file_name)(path); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_CANONICALIZE_FILE_NAME \ + COMMON_INTERCEPT_FUNCTION(canonicalize_file_name); +#else +#define INIT_CANONICALIZE_FILE_NAME +#endif + +#if SANITIZER_INTERCEPT_CONFSTR +INTERCEPTOR(SIZE_T, confstr, int name, char *buf, SIZE_T len) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, confstr, name, buf, len); + SIZE_T res = REAL(confstr)(name, buf, len); + if (buf && res) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res < len ? res : len); + return res; +} +#define INIT_CONFSTR COMMON_INTERCEPT_FUNCTION(confstr); +#else +#define INIT_CONFSTR +#endif + +#if SANITIZER_INTERCEPT_SCHED_GETAFFINITY +INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sched_getaffinity, pid, cpusetsize, mask); + int res = REAL(sched_getaffinity)(pid, cpusetsize, mask); + if (mask && !res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mask, cpusetsize); + return res; +} +#define INIT_SCHED_GETAFFINITY COMMON_INTERCEPT_FUNCTION(sched_getaffinity); +#else +#define INIT_SCHED_GETAFFINITY +#endif + +#if SANITIZER_INTERCEPT_STRERROR +INTERCEPTOR(char *, strerror, int errnum) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strerror, errnum); + char *res = REAL(strerror)(errnum); + if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_STRERROR COMMON_INTERCEPT_FUNCTION(strerror); +#else +#define INIT_STRERROR +#endif + +#if SANITIZER_INTERCEPT_STRERROR_R +INTERCEPTOR(char *, strerror_r, int errnum, char *buf, SIZE_T buflen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strerror_r, errnum, buf, buflen); + char *res = REAL(strerror_r)(errnum, buf, buflen); + // There are 2 versions of strerror_r: + // * POSIX version returns 0 on success, negative error code on failure, + // writes message to buf. + // * GNU version returns message pointer, which points to either buf or some + // static storage. + SIZE_T posix_res = (SIZE_T)res; + if (posix_res < 1024 || posix_res > (SIZE_T) - 1024) { + // POSIX version. Spec is not clear on whether buf is NULL-terminated. + // At least on OSX, buf contents are valid even when the call fails. + SIZE_T sz = internal_strnlen(buf, buflen); + if (sz < buflen) ++sz; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz); + } else { + // GNU version. + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + } + return res; +} +#define INIT_STRERROR_R COMMON_INTERCEPT_FUNCTION(strerror_r); +#else +#define INIT_STRERROR_R +#endif + +#if SANITIZER_INTERCEPT_SCANDIR +typedef int (*scandir_filter_f)(const struct __sanitizer_dirent *); +typedef int (*scandir_compar_f)(const struct __sanitizer_dirent **, + const struct __sanitizer_dirent **); + +static THREADLOCAL void *scandir_ctx; +static THREADLOCAL scandir_filter_f scandir_filter; +static THREADLOCAL scandir_compar_f scandir_compar; + +static int wrapped_scandir_filter(const struct __sanitizer_dirent *dir) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(scandir_ctx, 1); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, dir, dir->d_reclen); + return scandir_filter(dir); +} + +static int wrapped_scandir_compar(const struct __sanitizer_dirent **a, + const struct __sanitizer_dirent **b) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(scandir_ctx, 2); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, a, sizeof(*a)); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, *a, (*a)->d_reclen); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, b, sizeof(*b)); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, *b, (*b)->d_reclen); + return scandir_compar(a, b); +} + +INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist, + scandir_filter_f filter, scandir_compar_f compar) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, scandir, dirp, namelist, filter, compar); + if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1); + CHECK_EQ(0, scandir_ctx); + scandir_ctx = ctx; + scandir_filter = filter; + scandir_compar = compar; + int res = REAL(scandir)(dirp, namelist, filter ? wrapped_scandir_filter : 0, + compar ? wrapped_scandir_compar : 0); + scandir_ctx = 0; + scandir_filter = 0; + scandir_compar = 0; + if (namelist && res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); + for (int i = 0; i < res; ++i) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], + (*namelist)[i]->d_reclen); + } + return res; +} +#define INIT_SCANDIR COMMON_INTERCEPT_FUNCTION(scandir); +#else +#define INIT_SCANDIR +#endif + +#if SANITIZER_INTERCEPT_SCANDIR64 +typedef int (*scandir64_filter_f)(const struct __sanitizer_dirent64 *); +typedef int (*scandir64_compar_f)(const struct __sanitizer_dirent64 **, + const struct __sanitizer_dirent64 **); + +static THREADLOCAL void *scandir64_ctx; +static THREADLOCAL scandir64_filter_f scandir64_filter; +static THREADLOCAL scandir64_compar_f scandir64_compar; + +static int wrapped_scandir64_filter(const struct __sanitizer_dirent64 *dir) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(scandir64_ctx, 1); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, dir, dir->d_reclen); + return scandir64_filter(dir); +} + +static int wrapped_scandir64_compar(const struct __sanitizer_dirent64 **a, + const struct __sanitizer_dirent64 **b) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(scandir64_ctx, 2); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, a, sizeof(*a)); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, *a, (*a)->d_reclen); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, b, sizeof(*b)); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, *b, (*b)->d_reclen); + return scandir64_compar(a, b); +} + +INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist, + scandir64_filter_f filter, scandir64_compar_f compar) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, scandir64, dirp, namelist, filter, compar); + if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1); + CHECK_EQ(0, scandir64_ctx); + scandir64_ctx = ctx; + scandir64_filter = filter; + scandir64_compar = compar; + int res = + REAL(scandir64)(dirp, namelist, filter ? wrapped_scandir64_filter : 0, + compar ? wrapped_scandir64_compar : 0); + scandir64_ctx = 0; + scandir64_filter = 0; + scandir64_compar = 0; + if (namelist && res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); + for (int i = 0; i < res; ++i) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], + (*namelist)[i]->d_reclen); + } + return res; +} +#define INIT_SCANDIR64 COMMON_INTERCEPT_FUNCTION(scandir64); +#else +#define INIT_SCANDIR64 +#endif + +#if SANITIZER_INTERCEPT_GETGROUPS +INTERCEPTOR(int, getgroups, int size, u32 *lst) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getgroups, size, lst); + int res = REAL(getgroups)(size, lst); + if (res && lst) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst)); + return res; +} +#define INIT_GETGROUPS COMMON_INTERCEPT_FUNCTION(getgroups); +#else +#define INIT_GETGROUPS +#endif + +#if SANITIZER_INTERCEPT_POLL +static void read_pollfd(void *ctx, __sanitizer_pollfd *fds, + __sanitizer_nfds_t nfds) { + for (unsigned i = 0; i < nfds; ++i) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].fd, sizeof(fds[i].fd)); + COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].events, sizeof(fds[i].events)); + } +} + +static void write_pollfd(void *ctx, __sanitizer_pollfd *fds, + __sanitizer_nfds_t nfds) { + for (unsigned i = 0; i < nfds; ++i) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &fds[i].revents, + sizeof(fds[i].revents)); +} + +INTERCEPTOR(int, poll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, + int timeout) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, poll, fds, nfds, timeout); + if (fds && nfds) read_pollfd(ctx, fds, nfds); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(poll)(fds, nfds, timeout); + if (fds && nfds) write_pollfd(ctx, fds, nfds); + return res; +} +#define INIT_POLL COMMON_INTERCEPT_FUNCTION(poll); +#else +#define INIT_POLL +#endif + +#if SANITIZER_INTERCEPT_PPOLL +INTERCEPTOR(int, ppoll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, + void *timeout_ts, __sanitizer_sigset_t *sigmask) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ppoll, fds, nfds, timeout_ts, sigmask); + if (fds && nfds) read_pollfd(ctx, fds, nfds); + if (timeout_ts) + COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout_ts, struct_timespec_sz); + // FIXME: read sigmask when all of sigemptyset, etc are intercepted. + int res = + COMMON_INTERCEPTOR_BLOCK_REAL(ppoll)(fds, nfds, timeout_ts, sigmask); + if (fds && nfds) write_pollfd(ctx, fds, nfds); + return res; +} +#define INIT_PPOLL COMMON_INTERCEPT_FUNCTION(ppoll); +#else +#define INIT_PPOLL +#endif + +#if SANITIZER_INTERCEPT_WORDEXP +INTERCEPTOR(int, wordexp, char *s, __sanitizer_wordexp_t *p, int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wordexp, s, p, flags); + if (s) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1); + int res = REAL(wordexp)(s, p, flags); + if (!res && p) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); + if (p->we_wordc) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->we_wordv, + sizeof(*p->we_wordv) * p->we_wordc); + for (uptr i = 0; i < p->we_wordc; ++i) { + char *w = p->we_wordv[i]; + if (w) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, w, REAL(strlen)(w) + 1); + } + } + return res; +} +#define INIT_WORDEXP COMMON_INTERCEPT_FUNCTION(wordexp); +#else +#define INIT_WORDEXP +#endif + +#if SANITIZER_INTERCEPT_SIGWAIT +INTERCEPTOR(int, sigwait, __sanitizer_sigset_t *set, int *sig) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigwait, set, sig); + // FIXME: read sigset_t when all of sigemptyset, etc are intercepted + int res = REAL(sigwait)(set, sig); + if (!res && sig) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sig, sizeof(*sig)); + return res; +} +#define INIT_SIGWAIT COMMON_INTERCEPT_FUNCTION(sigwait); +#else +#define INIT_SIGWAIT +#endif + +#if SANITIZER_INTERCEPT_SIGWAITINFO +INTERCEPTOR(int, sigwaitinfo, __sanitizer_sigset_t *set, void *info) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigwaitinfo, set, info); + // FIXME: read sigset_t when all of sigemptyset, etc are intercepted + int res = REAL(sigwaitinfo)(set, info); + if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); + return res; +} +#define INIT_SIGWAITINFO COMMON_INTERCEPT_FUNCTION(sigwaitinfo); +#else +#define INIT_SIGWAITINFO +#endif + +#if SANITIZER_INTERCEPT_SIGTIMEDWAIT +INTERCEPTOR(int, sigtimedwait, __sanitizer_sigset_t *set, void *info, + void *timeout) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigtimedwait, set, info, timeout); + if (timeout) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout, struct_timespec_sz); + // FIXME: read sigset_t when all of sigemptyset, etc are intercepted + int res = REAL(sigtimedwait)(set, info, timeout); + if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); + return res; +} +#define INIT_SIGTIMEDWAIT COMMON_INTERCEPT_FUNCTION(sigtimedwait); +#else +#define INIT_SIGTIMEDWAIT +#endif + +#if SANITIZER_INTERCEPT_SIGSETOPS +INTERCEPTOR(int, sigemptyset, __sanitizer_sigset_t *set) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigemptyset, set); + int res = REAL(sigemptyset)(set); + if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); + return res; +} + +INTERCEPTOR(int, sigfillset, __sanitizer_sigset_t *set) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigfillset, set); + int res = REAL(sigfillset)(set); + if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); + return res; +} +#define INIT_SIGSETOPS \ + COMMON_INTERCEPT_FUNCTION(sigemptyset); \ + COMMON_INTERCEPT_FUNCTION(sigfillset); +#else +#define INIT_SIGSETOPS +#endif + +#if SANITIZER_INTERCEPT_SIGPENDING +INTERCEPTOR(int, sigpending, __sanitizer_sigset_t *set) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigpending, set); + int res = REAL(sigpending)(set); + if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); + return res; +} +#define INIT_SIGPENDING COMMON_INTERCEPT_FUNCTION(sigpending); +#else +#define INIT_SIGPENDING +#endif + +#if SANITIZER_INTERCEPT_SIGPROCMASK +INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset); + // FIXME: read sigset_t when all of sigemptyset, etc are intercepted + int res = REAL(sigprocmask)(how, set, oldset); + if (!res && oldset) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset)); + return res; +} +#define INIT_SIGPROCMASK COMMON_INTERCEPT_FUNCTION(sigprocmask); +#else +#define INIT_SIGPROCMASK +#endif + +#if SANITIZER_INTERCEPT_BACKTRACE +INTERCEPTOR(int, backtrace, void **buffer, int size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, backtrace, buffer, size); + int res = REAL(backtrace)(buffer, size); + if (res && buffer) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buffer, res * sizeof(*buffer)); + return res; +} + +INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, backtrace_symbols, buffer, size); + if (buffer && size) + COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, size * sizeof(*buffer)); + char **res = REAL(backtrace_symbols)(buffer, size); + if (res && size) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, size * sizeof(*res)); + for (int i = 0; i < size; ++i) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res[i], REAL(strlen(res[i])) + 1); + } + return res; +} +#define INIT_BACKTRACE \ + COMMON_INTERCEPT_FUNCTION(backtrace); \ + COMMON_INTERCEPT_FUNCTION(backtrace_symbols); +#else +#define INIT_BACKTRACE +#endif + +#if SANITIZER_INTERCEPT__EXIT +INTERCEPTOR(void, _exit, int status) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, _exit, status); + int status1 = COMMON_INTERCEPTOR_ON_EXIT(ctx); + if (status == 0) status = status1; + REAL(_exit)(status); +} +#define INIT__EXIT COMMON_INTERCEPT_FUNCTION(_exit); +#else +#define INIT__EXIT +#endif + +#if SANITIZER_INTERCEPT_PHTREAD_MUTEX +INTERCEPTOR(int, pthread_mutex_lock, void *m) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_lock, m); + int res = REAL(pthread_mutex_lock)(m); + if (res == errno_EOWNERDEAD) + COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m); + if (res == 0 || res == errno_EOWNERDEAD) + COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m); + return res; +} + +INTERCEPTOR(int, pthread_mutex_unlock, void *m) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_unlock, m); + COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m); + return REAL(pthread_mutex_unlock)(m); +} + +#define INIT_PTHREAD_MUTEX_LOCK COMMON_INTERCEPT_FUNCTION(pthread_mutex_lock) +#define INIT_PTHREAD_MUTEX_UNLOCK \ + COMMON_INTERCEPT_FUNCTION(pthread_mutex_unlock) +#else +#define INIT_PTHREAD_MUTEX_LOCK +#define INIT_PTHREAD_MUTEX_UNLOCK +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_COND +INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_wait, c, m); + COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m); + COMMON_INTERCEPTOR_READ_RANGE(ctx, c, pthread_cond_t_sz); + int res = REAL(pthread_cond_wait)(c, m); + COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m); + return res; +} + +INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_init, c, a); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, pthread_cond_t_sz); + return REAL(pthread_cond_init)(c, a); +} + +INTERCEPTOR(int, pthread_cond_signal, void *c) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_signal, c); + COMMON_INTERCEPTOR_READ_RANGE(ctx, c, pthread_cond_t_sz); + return REAL(pthread_cond_signal)(c); +} + +INTERCEPTOR(int, pthread_cond_broadcast, void *c) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_broadcast, c); + COMMON_INTERCEPTOR_READ_RANGE(ctx, c, pthread_cond_t_sz); + return REAL(pthread_cond_broadcast)(c); +} + +#define INIT_PTHREAD_COND_WAIT \ + INTERCEPT_FUNCTION_VER(pthread_cond_wait, "GLIBC_2.3.2") +#define INIT_PTHREAD_COND_INIT \ + INTERCEPT_FUNCTION_VER(pthread_cond_init, "GLIBC_2.3.2") +#define INIT_PTHREAD_COND_SIGNAL \ + INTERCEPT_FUNCTION_VER(pthread_cond_signal, "GLIBC_2.3.2") +#define INIT_PTHREAD_COND_BROADCAST \ + INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, "GLIBC_2.3.2") +#else +#define INIT_PTHREAD_COND_WAIT +#define INIT_PTHREAD_COND_INIT +#define INIT_PTHREAD_COND_SIGNAL +#define INIT_PTHREAD_COND_BROADCAST +#endif + +#if SANITIZER_INTERCEPT_GETMNTENT || SANITIZER_INTERCEPT_GETMNTENT_R +static void write_mntent(void *ctx, __sanitizer_mntent *mnt) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt, sizeof(*mnt)); + if (mnt->mnt_fsname) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_fsname, + REAL(strlen)(mnt->mnt_fsname) + 1); + if (mnt->mnt_dir) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_dir, + REAL(strlen)(mnt->mnt_dir) + 1); + if (mnt->mnt_type) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_type, + REAL(strlen)(mnt->mnt_type) + 1); + if (mnt->mnt_opts) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_opts, + REAL(strlen)(mnt->mnt_opts) + 1); +} +#endif + +#if SANITIZER_INTERCEPT_GETMNTENT +INTERCEPTOR(__sanitizer_mntent *, getmntent, void *fp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getmntent, fp); + __sanitizer_mntent *res = REAL(getmntent)(fp); + if (res) write_mntent(ctx, res); + return res; +} +#define INIT_GETMNTENT COMMON_INTERCEPT_FUNCTION(getmntent); +#else +#define INIT_GETMNTENT +#endif + +#if SANITIZER_INTERCEPT_GETMNTENT_R +INTERCEPTOR(__sanitizer_mntent *, getmntent_r, void *fp, + __sanitizer_mntent *mntbuf, char *buf, int buflen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getmntent_r, fp, mntbuf, buf, buflen); + __sanitizer_mntent *res = REAL(getmntent_r)(fp, mntbuf, buf, buflen); + if (res) write_mntent(ctx, res); + return res; +} +#define INIT_GETMNTENT_R COMMON_INTERCEPT_FUNCTION(getmntent_r); +#else +#define INIT_GETMNTENT_R +#endif + +#if SANITIZER_INTERCEPT_STATFS +INTERCEPTOR(int, statfs, char *path, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, statfs, path, buf); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + int res = REAL(statfs)(path, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); + return res; +} +INTERCEPTOR(int, fstatfs, int fd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fstatfs, fd, buf); + int res = REAL(fstatfs)(fd, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); + return res; +} +#define INIT_STATFS \ + COMMON_INTERCEPT_FUNCTION(statfs); \ + COMMON_INTERCEPT_FUNCTION(fstatfs); +#else +#define INIT_STATFS +#endif + +#if SANITIZER_INTERCEPT_STATFS64 +INTERCEPTOR(int, statfs64, char *path, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, statfs64, path, buf); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + int res = REAL(statfs64)(path, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); + return res; +} +INTERCEPTOR(int, fstatfs64, int fd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fstatfs64, fd, buf); + int res = REAL(fstatfs64)(fd, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); + return res; +} +#define INIT_STATFS64 \ + COMMON_INTERCEPT_FUNCTION(statfs64); \ + COMMON_INTERCEPT_FUNCTION(fstatfs64); +#else +#define INIT_STATFS64 +#endif + +#if SANITIZER_INTERCEPT_STATVFS +INTERCEPTOR(int, statvfs, char *path, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, statvfs, path, buf); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + int res = REAL(statvfs)(path, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); + return res; +} +INTERCEPTOR(int, fstatvfs, int fd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf); + int res = REAL(fstatvfs)(fd, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); + return res; +} +#define INIT_STATVFS \ + COMMON_INTERCEPT_FUNCTION(statvfs); \ + COMMON_INTERCEPT_FUNCTION(fstatvfs); +#else +#define INIT_STATVFS +#endif + +#if SANITIZER_INTERCEPT_STATVFS64 +INTERCEPTOR(int, statvfs64, char *path, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, statvfs64, path, buf); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + int res = REAL(statvfs64)(path, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); + return res; +} +INTERCEPTOR(int, fstatvfs64, int fd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs64, fd, buf); + int res = REAL(fstatvfs64)(fd, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); + return res; +} +#define INIT_STATVFS64 \ + COMMON_INTERCEPT_FUNCTION(statvfs64); \ + COMMON_INTERCEPT_FUNCTION(fstatvfs64); +#else +#define INIT_STATVFS64 +#endif + +#if SANITIZER_INTERCEPT_INITGROUPS +INTERCEPTOR(int, initgroups, char *user, u32 group) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, initgroups, user, group); + if (user) COMMON_INTERCEPTOR_READ_RANGE(ctx, user, REAL(strlen)(user) + 1); + int res = REAL(initgroups)(user, group); + return res; +} +#define INIT_INITGROUPS COMMON_INTERCEPT_FUNCTION(initgroups); +#else +#define INIT_INITGROUPS +#endif + +#if SANITIZER_INTERCEPT_ETHER +INTERCEPTOR(char *, ether_ntoa, __sanitizer_ether_addr *addr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa, addr); + if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); + char *res = REAL(ether_ntoa)(addr); + if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +INTERCEPTOR(__sanitizer_ether_addr *, ether_aton, char *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_aton, buf); + if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1); + __sanitizer_ether_addr *res = REAL(ether_aton)(buf); + if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, sizeof(*res)); + return res; +} +INTERCEPTOR(int, ether_ntohost, char *hostname, __sanitizer_ether_addr *addr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_ntohost, hostname, addr); + if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); + int res = REAL(ether_ntohost)(hostname, addr); + if (!res && hostname) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); + return res; +} +INTERCEPTOR(int, ether_hostton, char *hostname, __sanitizer_ether_addr *addr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_hostton, hostname, addr); + if (hostname) + COMMON_INTERCEPTOR_READ_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); + int res = REAL(ether_hostton)(hostname, addr); + if (!res && addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); + return res; +} +INTERCEPTOR(int, ether_line, char *line, __sanitizer_ether_addr *addr, + char *hostname) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_line, line, addr, hostname); + if (line) COMMON_INTERCEPTOR_READ_RANGE(ctx, line, REAL(strlen)(line) + 1); + int res = REAL(ether_line)(line, addr, hostname); + if (!res) { + if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); + if (hostname) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); + } + return res; +} +#define INIT_ETHER \ + COMMON_INTERCEPT_FUNCTION(ether_ntoa); \ + COMMON_INTERCEPT_FUNCTION(ether_aton); \ + COMMON_INTERCEPT_FUNCTION(ether_ntohost); \ + COMMON_INTERCEPT_FUNCTION(ether_hostton); \ + COMMON_INTERCEPT_FUNCTION(ether_line); +#else +#define INIT_ETHER +#endif + +#if SANITIZER_INTERCEPT_ETHER_R +INTERCEPTOR(char *, ether_ntoa_r, __sanitizer_ether_addr *addr, char *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa_r, addr, buf); + if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); + char *res = REAL(ether_ntoa_r)(addr, buf); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +INTERCEPTOR(__sanitizer_ether_addr *, ether_aton_r, char *buf, + __sanitizer_ether_addr *addr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_aton_r, buf, addr); + if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1); + __sanitizer_ether_addr *res = REAL(ether_aton_r)(buf, addr); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(*res)); + return res; +} +#define INIT_ETHER_R \ + COMMON_INTERCEPT_FUNCTION(ether_ntoa_r); \ + COMMON_INTERCEPT_FUNCTION(ether_aton_r); +#else +#define INIT_ETHER_R +#endif + +#if SANITIZER_INTERCEPT_SHMCTL +INTERCEPTOR(int, shmctl, int shmid, int cmd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, shmctl, shmid, cmd, buf); + int res = REAL(shmctl)(shmid, cmd, buf); + if (res >= 0) { + unsigned sz = 0; + if (cmd == shmctl_ipc_stat || cmd == shmctl_shm_stat) + sz = sizeof(__sanitizer_shmid_ds); + else if (cmd == shmctl_ipc_info) + sz = struct_shminfo_sz; + else if (cmd == shmctl_shm_info) + sz = struct_shm_info_sz; + if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz); + } + return res; +} +#define INIT_SHMCTL COMMON_INTERCEPT_FUNCTION(shmctl); +#else +#define INIT_SHMCTL +#endif + +#if SANITIZER_INTERCEPT_RANDOM_R +INTERCEPTOR(int, random_r, void *buf, u32 *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, random_r, buf, result); + int res = REAL(random_r)(buf, result); + if (!res && result) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + return res; +} +#define INIT_RANDOM_R COMMON_INTERCEPT_FUNCTION(random_r); +#else +#define INIT_RANDOM_R +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET || \ + SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSSCHED +#define INTERCEPTOR_PTHREAD_ATTR_GET(what, sz) \ + INTERCEPTOR(int, pthread_attr_get##what, void *attr, void *r) { \ + void *ctx; \ + COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_get##what, attr, r); \ + int res = REAL(pthread_attr_get##what)(attr, r); \ + if (!res && r) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, r, sz); \ + return res; \ + } +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET +INTERCEPTOR_PTHREAD_ATTR_GET(detachstate, sizeof(int)) +INTERCEPTOR_PTHREAD_ATTR_GET(guardsize, sizeof(SIZE_T)) +INTERCEPTOR_PTHREAD_ATTR_GET(schedparam, struct_sched_param_sz) +INTERCEPTOR_PTHREAD_ATTR_GET(schedpolicy, sizeof(int)) +INTERCEPTOR_PTHREAD_ATTR_GET(scope, sizeof(int)) +INTERCEPTOR_PTHREAD_ATTR_GET(stacksize, sizeof(SIZE_T)) +INTERCEPTOR(int, pthread_attr_getstack, void *attr, void **addr, SIZE_T *size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getstack, attr, addr, size); + int res = REAL(pthread_attr_getstack)(attr, addr, size); + if (!res) { + if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); + if (size) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, size, sizeof(*size)); + } + return res; +} + +#define INIT_PTHREAD_ATTR_GET \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getdetachstate); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getguardsize); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedparam); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedpolicy); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getscope); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getstacksize); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getstack); +#else +#define INIT_PTHREAD_ATTR_GET +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED +INTERCEPTOR_PTHREAD_ATTR_GET(inheritsched, sizeof(int)) + +#define INIT_PTHREAD_ATTR_GETINHERITSCHED \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getinheritsched); +#else +#define INIT_PTHREAD_ATTR_GETINHERITSCHED +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP +INTERCEPTOR(int, pthread_attr_getaffinity_np, void *attr, SIZE_T cpusetsize, + void *cpuset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getaffinity_np, attr, cpusetsize, + cpuset); + int res = REAL(pthread_attr_getaffinity_np)(attr, cpusetsize, cpuset); + if (!res && cpusetsize && cpuset) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cpuset, cpusetsize); + return res; +} + +#define INIT_PTHREAD_ATTR_GETAFFINITY_NP \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getaffinity_np); +#else +#define INIT_PTHREAD_ATTR_GETAFFINITY_NP +#endif + +#if SANITIZER_INTERCEPT_TMPNAM +INTERCEPTOR(char *, tmpnam, char *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, tmpnam, s); + char *res = REAL(tmpnam)(s); + if (res) { + if (s) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); + else + COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1); + } + return res; +} +#define INIT_TMPNAM COMMON_INTERCEPT_FUNCTION(tmpnam); +#else +#define INIT_TMPNAM +#endif + +#if SANITIZER_INTERCEPT_TMPNAM_R +INTERCEPTOR(char *, tmpnam_r, char *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, tmpnam_r, s); + char *res = REAL(tmpnam_r)(s); + if (res && s) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); + return res; +} +#define INIT_TMPNAM_R COMMON_INTERCEPT_FUNCTION(tmpnam_r); +#else +#define INIT_TMPNAM_R +#endif + +#if SANITIZER_INTERCEPT_TEMPNAM +INTERCEPTOR(char *, tempnam, char *dir, char *pfx) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, tempnam, dir, pfx); + if (dir) COMMON_INTERCEPTOR_READ_RANGE(ctx, dir, REAL(strlen)(dir) + 1); + if (pfx) COMMON_INTERCEPTOR_READ_RANGE(ctx, pfx, REAL(strlen)(pfx) + 1); + char *res = REAL(tempnam)(dir, pfx); + if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_TEMPNAM COMMON_INTERCEPT_FUNCTION(tempnam); +#else +#define INIT_TEMPNAM +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP +INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name); + COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name); + return REAL(pthread_setname_np)(thread, name); +} +#define INIT_PTHREAD_SETNAME_NP COMMON_INTERCEPT_FUNCTION(pthread_setname_np); +#else +#define INIT_PTHREAD_SETNAME_NP +#endif + +#if SANITIZER_INTERCEPT_SINCOS +INTERCEPTOR(void, sincos, double x, double *sin, double *cos) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sincos, x, sin, cos); + REAL(sincos)(x, sin, cos); + if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); + if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); +} +INTERCEPTOR(void, sincosf, float x, float *sin, float *cos) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sincosf, x, sin, cos); + REAL(sincosf)(x, sin, cos); + if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); + if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); +} +INTERCEPTOR(void, sincosl, long double x, long double *sin, long double *cos) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sincosl, x, sin, cos); + REAL(sincosl)(x, sin, cos); + if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); + if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); +} +#define INIT_SINCOS \ + COMMON_INTERCEPT_FUNCTION(sincos); \ + COMMON_INTERCEPT_FUNCTION(sincosf); \ + COMMON_INTERCEPT_FUNCTION(sincosl); +#else +#define INIT_SINCOS +#endif + +#if SANITIZER_INTERCEPT_REMQUO +INTERCEPTOR(double, remquo, double x, double y, int *quo) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, remquo, x, y, quo); + double res = REAL(remquo)(x, y, quo); + if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); + return res; +} +INTERCEPTOR(float, remquof, float x, float y, int *quo) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, remquof, x, y, quo); + float res = REAL(remquof)(x, y, quo); + if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); + return res; +} +INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, remquol, x, y, quo); + long double res = REAL(remquol)(x, y, quo); + if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); + return res; +} +#define INIT_REMQUO \ + COMMON_INTERCEPT_FUNCTION(remquo); \ + COMMON_INTERCEPT_FUNCTION(remquof); \ + COMMON_INTERCEPT_FUNCTION(remquol); +#else +#define INIT_REMQUO +#endif + +#if SANITIZER_INTERCEPT_LGAMMA +extern int signgam; +INTERCEPTOR(double, lgamma, double x) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgamma, x); + double res = REAL(lgamma)(x); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); + return res; +} +INTERCEPTOR(float, lgammaf, float x) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgammaf, x); + float res = REAL(lgammaf)(x); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); + return res; +} +INTERCEPTOR(long double, lgammal, long double x) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgammal, x); + long double res = REAL(lgammal)(x); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); + return res; +} +#define INIT_LGAMMA \ + COMMON_INTERCEPT_FUNCTION(lgamma); \ + COMMON_INTERCEPT_FUNCTION(lgammaf); \ + COMMON_INTERCEPT_FUNCTION(lgammal); +#else +#define INIT_LGAMMA +#endif + +#if SANITIZER_INTERCEPT_LGAMMA_R +INTERCEPTOR(double, lgamma_r, double x, int *signp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgamma_r, x, signp); + double res = REAL(lgamma_r)(x, signp); + if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); + return res; +} +INTERCEPTOR(float, lgammaf_r, float x, int *signp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgammaf_r, x, signp); + float res = REAL(lgammaf_r)(x, signp); + if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); + return res; +} +INTERCEPTOR(long double, lgammal_r, long double x, int *signp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgammal_r, x, signp); + long double res = REAL(lgammal_r)(x, signp); + if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); + return res; +} +#define INIT_LGAMMA_R \ + COMMON_INTERCEPT_FUNCTION(lgamma_r); \ + COMMON_INTERCEPT_FUNCTION(lgammaf_r); \ + COMMON_INTERCEPT_FUNCTION(lgammal_r); +#else +#define INIT_LGAMMA_R +#endif + +#if SANITIZER_INTERCEPT_DRAND48_R +INTERCEPTOR(int, drand48_r, void *buffer, double *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, drand48_r, buffer, result); + int res = REAL(drand48_r)(buffer, result); + if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + return res; +} +INTERCEPTOR(int, lrand48_r, void *buffer, long *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lrand48_r, buffer, result); + int res = REAL(lrand48_r)(buffer, result); + if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + return res; +} +#define INIT_DRAND48_R \ + COMMON_INTERCEPT_FUNCTION(drand48_r); \ + COMMON_INTERCEPT_FUNCTION(lrand48_r); +#else +#define INIT_DRAND48_R +#endif + +#if SANITIZER_INTERCEPT_GETLINE +INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getline, lineptr, n, stream); + SSIZE_T res = REAL(getline)(lineptr, n, stream); + if (res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); + } + return res; +} +INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim, + void *stream) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getdelim, lineptr, n, delim, stream); + SSIZE_T res = REAL(getdelim)(lineptr, n, delim, stream); + if (res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); + } + return res; +} +#define INIT_GETLINE \ + COMMON_INTERCEPT_FUNCTION(getline); \ + COMMON_INTERCEPT_FUNCTION(getdelim); +#else +#define INIT_GETLINE +#endif + #define SANITIZER_COMMON_INTERCEPTORS_INIT \ + INIT_STRCMP; \ + INIT_STRNCMP; \ INIT_STRCASECMP; \ INIT_STRNCASECMP; \ INIT_READ; \ INIT_PREAD; \ INIT_PREAD64; \ - INIT_PRCTL; \ + INIT_READV; \ + INIT_PREADV; \ + INIT_PREADV64; \ INIT_WRITE; \ INIT_PWRITE; \ INIT_PWRITE64; \ + INIT_WRITEV; \ + INIT_PWRITEV; \ + INIT_PWRITEV64; \ + INIT_PRCTL; \ INIT_LOCALTIME_AND_FRIENDS; \ + INIT_STRPTIME; \ INIT_SCANF; \ + INIT_ISOC99_SCANF; \ INIT_FREXP; \ INIT_FREXPF_FREXPL; \ INIT_GETPWNAM_AND_FRIENDS; \ @@ -985,7 +2857,79 @@ INTERCEPTOR(int, getsockopt, int sockfd, int level, int optname, void *optval, INIT_INET; \ INIT_PTHREAD_GETSCHEDPARAM; \ INIT_GETADDRINFO; \ + INIT_GETNAMEINFO; \ INIT_GETSOCKNAME; \ INIT_GETHOSTBYNAME; \ INIT_GETHOSTBYNAME_R; \ - INIT_GETSOCKOPT; + INIT_GETSOCKOPT; \ + INIT_ACCEPT; \ + INIT_ACCEPT4; \ + INIT_MODF; \ + INIT_RECVMSG; \ + INIT_GETPEERNAME; \ + INIT_IOCTL; \ + INIT_INET_ATON; \ + INIT_SYSINFO; \ + INIT_READDIR; \ + INIT_READDIR64; \ + INIT_PTRACE; \ + INIT_SETLOCALE; \ + INIT_GETCWD; \ + INIT_GET_CURRENT_DIR_NAME; \ + INIT_STRTOIMAX; \ + INIT_MBSTOWCS; \ + INIT_MBSNRTOWCS; \ + INIT_WCSTOMBS; \ + INIT_WCSNRTOMBS; \ + INIT_TCGETATTR; \ + INIT_REALPATH; \ + INIT_CANONICALIZE_FILE_NAME; \ + INIT_CONFSTR; \ + INIT_SCHED_GETAFFINITY; \ + INIT_STRERROR; \ + INIT_STRERROR_R; \ + INIT_SCANDIR; \ + INIT_SCANDIR64; \ + INIT_GETGROUPS; \ + INIT_POLL; \ + INIT_PPOLL; \ + INIT_WORDEXP; \ + INIT_SIGWAIT; \ + INIT_SIGWAITINFO; \ + INIT_SIGTIMEDWAIT; \ + INIT_SIGSETOPS; \ + INIT_SIGPENDING; \ + INIT_SIGPROCMASK; \ + INIT_BACKTRACE; \ + INIT__EXIT; \ + INIT_PTHREAD_MUTEX_LOCK; \ + INIT_PTHREAD_MUTEX_UNLOCK; \ + INIT_PTHREAD_COND_WAIT; \ + INIT_PTHREAD_COND_INIT; \ + INIT_PTHREAD_COND_SIGNAL; \ + INIT_PTHREAD_COND_BROADCAST; \ + INIT_GETMNTENT; \ + INIT_GETMNTENT_R; \ + INIT_STATFS; \ + INIT_STATFS64; \ + INIT_STATVFS; \ + INIT_STATVFS64; \ + INIT_INITGROUPS; \ + INIT_ETHER; \ + INIT_ETHER_R; \ + INIT_SHMCTL; \ + INIT_RANDOM_R; \ + INIT_PTHREAD_ATTR_GET; \ + INIT_PTHREAD_ATTR_GETINHERITSCHED; \ + INIT_PTHREAD_ATTR_GETAFFINITY_NP; \ + INIT_TMPNAM; \ + INIT_TMPNAM_R; \ + INIT_TEMPNAM; \ + INIT_PTHREAD_SETNAME_NP; \ + INIT_SINCOS; \ + INIT_REMQUO; \ + INIT_LGAMMA; \ + INIT_LGAMMA_R; \ + INIT_DRAND48_R; \ + INIT_GETLINE; \ +/**/ diff --git a/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc b/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc new file mode 100755 index 000000000000..4b90f8ca8a36 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc @@ -0,0 +1,568 @@ +//===-- sanitizer_common_interceptors_ioctl.inc -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Ioctl handling in common sanitizer interceptors. +//===----------------------------------------------------------------------===// + +#include "sanitizer_flags.h" + +struct ioctl_desc { + unsigned req; + // FIXME: support read+write arguments. Those are currently marked as WRITE. + enum { + NONE, + READ, + WRITE, + CUSTOM + } type : 2; + unsigned size : 30; + const char* name; +}; + +const unsigned ioctl_table_max = 500; +static ioctl_desc ioctl_table[ioctl_table_max]; +static unsigned ioctl_table_size = 0; + +// This can not be declared as a global, because references to struct_*_sz +// require a global initializer. And this table must be available before global +// initializers are run. +static void ioctl_table_fill() { +#define _(rq, tp, sz) \ + if (IOCTL_##rq != IOCTL_NOT_PRESENT) { \ + CHECK(ioctl_table_size < ioctl_table_max); \ + ioctl_table[ioctl_table_size].req = IOCTL_##rq; \ + ioctl_table[ioctl_table_size].type = ioctl_desc::tp; \ + ioctl_table[ioctl_table_size].size = sz; \ + ioctl_table[ioctl_table_size].name = #rq; \ + ++ioctl_table_size; \ + } + + _(FIOASYNC, READ, sizeof(int)); + _(FIOCLEX, NONE, 0); + _(FIOGETOWN, WRITE, sizeof(int)); + _(FIONBIO, READ, sizeof(int)); + _(FIONCLEX, NONE, 0); + _(FIOSETOWN, READ, sizeof(int)); + _(SIOCADDMULTI, READ, struct_ifreq_sz); + _(SIOCATMARK, WRITE, sizeof(int)); + _(SIOCDELMULTI, READ, struct_ifreq_sz); + _(SIOCGIFADDR, WRITE, struct_ifreq_sz); + _(SIOCGIFBRDADDR, WRITE, struct_ifreq_sz); + _(SIOCGIFCONF, CUSTOM, 0); + _(SIOCGIFDSTADDR, WRITE, struct_ifreq_sz); + _(SIOCGIFFLAGS, WRITE, struct_ifreq_sz); + _(SIOCGIFMETRIC, WRITE, struct_ifreq_sz); + _(SIOCGIFMTU, WRITE, struct_ifreq_sz); + _(SIOCGIFNETMASK, WRITE, struct_ifreq_sz); + _(SIOCGPGRP, WRITE, sizeof(int)); + _(SIOCSIFADDR, READ, struct_ifreq_sz); + _(SIOCSIFBRDADDR, READ, struct_ifreq_sz); + _(SIOCSIFDSTADDR, READ, struct_ifreq_sz); + _(SIOCSIFFLAGS, READ, struct_ifreq_sz); + _(SIOCSIFMETRIC, READ, struct_ifreq_sz); + _(SIOCSIFMTU, READ, struct_ifreq_sz); + _(SIOCSIFNETMASK, READ, struct_ifreq_sz); + _(SIOCSPGRP, READ, sizeof(int)); + _(TIOCCONS, NONE, 0); + _(TIOCEXCL, NONE, 0); + _(TIOCGETD, WRITE, sizeof(int)); + _(TIOCGPGRP, WRITE, pid_t_sz); + _(TIOCGWINSZ, WRITE, struct_winsize_sz); + _(TIOCMBIC, READ, sizeof(int)); + _(TIOCMBIS, READ, sizeof(int)); + _(TIOCMGET, WRITE, sizeof(int)); + _(TIOCMSET, READ, sizeof(int)); + _(TIOCNOTTY, NONE, 0); + _(TIOCNXCL, NONE, 0); + _(TIOCOUTQ, WRITE, sizeof(int)); + _(TIOCPKT, READ, sizeof(int)); + _(TIOCSCTTY, NONE, 0); + _(TIOCSETD, READ, sizeof(int)); + _(TIOCSPGRP, READ, pid_t_sz); + _(TIOCSTI, READ, sizeof(char)); + _(TIOCSWINSZ, READ, struct_winsize_sz); + +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) + _(SIOCGETSGCNT, WRITE, struct_sioc_sg_req_sz); + _(SIOCGETVIFCNT, WRITE, struct_sioc_vif_req_sz); +#endif + +#if SANITIZER_LINUX + // Conflicting request ids. + // _(CDROMAUDIOBUFSIZ, NONE, 0); + // _(SNDCTL_TMR_CONTINUE, NONE, 0); + // _(SNDCTL_TMR_START, NONE, 0); + // _(SNDCTL_TMR_STOP, NONE, 0); + // _(SOUND_MIXER_READ_LOUD, WRITE, sizeof(int)); // same as ...READ_ENHANCE + // _(SOUND_MIXER_READ_MUTE, WRITE, sizeof(int)); // same as ...READ_ENHANCE + // _(SOUND_MIXER_WRITE_LOUD, WRITE, sizeof(int)); // same as ...WRITE_ENHANCE + // _(SOUND_MIXER_WRITE_MUTE, WRITE, sizeof(int)); // same as ...WRITE_ENHANCE + _(BLKFLSBUF, NONE, 0); + _(BLKGETSIZE, WRITE, sizeof(uptr)); + _(BLKRAGET, WRITE, sizeof(int)); + _(BLKRASET, NONE, 0); + _(BLKROGET, WRITE, sizeof(int)); + _(BLKROSET, READ, sizeof(int)); + _(BLKRRPART, NONE, 0); + _(CDROMEJECT, NONE, 0); + _(CDROMEJECT_SW, NONE, 0); + _(CDROMMULTISESSION, WRITE, struct_cdrom_multisession_sz); + _(CDROMPAUSE, NONE, 0); + _(CDROMPLAYMSF, READ, struct_cdrom_msf_sz); + _(CDROMPLAYTRKIND, READ, struct_cdrom_ti_sz); + _(CDROMREADAUDIO, READ, struct_cdrom_read_audio_sz); + _(CDROMREADCOOKED, READ, struct_cdrom_msf_sz); + _(CDROMREADMODE1, READ, struct_cdrom_msf_sz); + _(CDROMREADMODE2, READ, struct_cdrom_msf_sz); + _(CDROMREADRAW, READ, struct_cdrom_msf_sz); + _(CDROMREADTOCENTRY, WRITE, struct_cdrom_tocentry_sz); + _(CDROMREADTOCHDR, WRITE, struct_cdrom_tochdr_sz); + _(CDROMRESET, NONE, 0); + _(CDROMRESUME, NONE, 0); + _(CDROMSEEK, READ, struct_cdrom_msf_sz); + _(CDROMSTART, NONE, 0); + _(CDROMSTOP, NONE, 0); + _(CDROMSUBCHNL, WRITE, struct_cdrom_subchnl_sz); + _(CDROMVOLCTRL, READ, struct_cdrom_volctrl_sz); + _(CDROMVOLREAD, WRITE, struct_cdrom_volctrl_sz); + _(CDROM_GET_UPC, WRITE, 8); + _(EVIOCGABS, WRITE, struct_input_absinfo_sz); // fixup + _(EVIOCGBIT, WRITE, struct_input_id_sz); // fixup + _(EVIOCGEFFECTS, WRITE, sizeof(int)); + _(EVIOCGID, WRITE, struct_input_id_sz); + _(EVIOCGKEY, WRITE, 0); + _(EVIOCGKEYCODE, WRITE, sizeof(int) * 2); + _(EVIOCGLED, WRITE, 0); + _(EVIOCGNAME, WRITE, 0); + _(EVIOCGPHYS, WRITE, 0); + _(EVIOCGRAB, READ, sizeof(int)); + _(EVIOCGREP, WRITE, sizeof(int) * 2); + _(EVIOCGSND, WRITE, 0); + _(EVIOCGSW, WRITE, 0); + _(EVIOCGUNIQ, WRITE, 0); + _(EVIOCGVERSION, WRITE, sizeof(int)); + _(EVIOCRMFF, READ, sizeof(int)); + _(EVIOCSABS, READ, struct_input_absinfo_sz); // fixup + _(EVIOCSFF, READ, struct_ff_effect_sz); + _(EVIOCSKEYCODE, READ, sizeof(int) * 2); + _(EVIOCSREP, READ, sizeof(int) * 2); + _(FDCLRPRM, NONE, 0); + _(FDDEFPRM, READ, struct_floppy_struct_sz); + _(FDFLUSH, NONE, 0); + _(FDFMTBEG, NONE, 0); + _(FDFMTEND, NONE, 0); + _(FDFMTTRK, READ, struct_format_descr_sz); + _(FDGETDRVPRM, WRITE, struct_floppy_drive_params_sz); + _(FDGETDRVSTAT, WRITE, struct_floppy_drive_struct_sz); + _(FDGETDRVTYP, WRITE, 16); + _(FDGETFDCSTAT, WRITE, struct_floppy_fdc_state_sz); + _(FDGETMAXERRS, WRITE, struct_floppy_max_errors_sz); + _(FDGETPRM, WRITE, struct_floppy_struct_sz); + _(FDMSGOFF, NONE, 0); + _(FDMSGON, NONE, 0); + _(FDPOLLDRVSTAT, WRITE, struct_floppy_drive_struct_sz); + _(FDRAWCMD, WRITE, struct_floppy_raw_cmd_sz); + _(FDRESET, NONE, 0); + _(FDSETDRVPRM, READ, struct_floppy_drive_params_sz); + _(FDSETEMSGTRESH, NONE, 0); + _(FDSETMAXERRS, READ, struct_floppy_max_errors_sz); + _(FDSETPRM, READ, struct_floppy_struct_sz); + _(FDTWADDLE, NONE, 0); + _(FDWERRORCLR, NONE, 0); + _(FDWERRORGET, WRITE, struct_floppy_write_errors_sz); + _(HDIO_DRIVE_CMD, WRITE, sizeof(int)); + _(HDIO_GETGEO, WRITE, struct_hd_geometry_sz); + _(HDIO_GET_32BIT, WRITE, sizeof(int)); + _(HDIO_GET_DMA, WRITE, sizeof(int)); + _(HDIO_GET_IDENTITY, WRITE, struct_hd_driveid_sz); + _(HDIO_GET_KEEPSETTINGS, WRITE, sizeof(int)); + _(HDIO_GET_MULTCOUNT, WRITE, sizeof(int)); + _(HDIO_GET_NOWERR, WRITE, sizeof(int)); + _(HDIO_GET_UNMASKINTR, WRITE, sizeof(int)); + _(HDIO_SET_32BIT, NONE, 0); + _(HDIO_SET_DMA, NONE, 0); + _(HDIO_SET_KEEPSETTINGS, NONE, 0); + _(HDIO_SET_MULTCOUNT, NONE, 0); + _(HDIO_SET_NOWERR, NONE, 0); + _(HDIO_SET_UNMASKINTR, NONE, 0); + _(MTIOCGET, WRITE, struct_mtget_sz); + _(MTIOCPOS, WRITE, struct_mtpos_sz); + _(MTIOCTOP, READ, struct_mtop_sz); + _(PPPIOCGASYNCMAP, WRITE, sizeof(int)); + _(PPPIOCGDEBUG, WRITE, sizeof(int)); + _(PPPIOCGFLAGS, WRITE, sizeof(int)); + _(PPPIOCGUNIT, WRITE, sizeof(int)); + _(PPPIOCGXASYNCMAP, WRITE, sizeof(int) * 8); + _(PPPIOCSASYNCMAP, READ, sizeof(int)); + _(PPPIOCSDEBUG, READ, sizeof(int)); + _(PPPIOCSFLAGS, READ, sizeof(int)); + _(PPPIOCSMAXCID, READ, sizeof(int)); + _(PPPIOCSMRU, READ, sizeof(int)); + _(PPPIOCSXASYNCMAP, READ, sizeof(int) * 8); + _(SIOCADDRT, READ, struct_rtentry_sz); + _(SIOCDARP, READ, struct_arpreq_sz); + _(SIOCDELRT, READ, struct_rtentry_sz); + _(SIOCDRARP, READ, struct_arpreq_sz); + _(SIOCGARP, WRITE, struct_arpreq_sz); + _(SIOCGIFENCAP, WRITE, sizeof(int)); + _(SIOCGIFHWADDR, WRITE, struct_ifreq_sz); + _(SIOCGIFMAP, WRITE, struct_ifreq_sz); + _(SIOCGIFMEM, WRITE, struct_ifreq_sz); + _(SIOCGIFNAME, NONE, 0); + _(SIOCGIFSLAVE, NONE, 0); + _(SIOCGRARP, WRITE, struct_arpreq_sz); + _(SIOCGSTAMP, WRITE, timeval_sz); + _(SIOCSARP, READ, struct_arpreq_sz); + _(SIOCSIFENCAP, READ, sizeof(int)); + _(SIOCSIFHWADDR, READ, struct_ifreq_sz); + _(SIOCSIFLINK, NONE, 0); + _(SIOCSIFMAP, READ, struct_ifreq_sz); + _(SIOCSIFMEM, READ, struct_ifreq_sz); + _(SIOCSIFSLAVE, NONE, 0); + _(SIOCSRARP, READ, struct_arpreq_sz); + _(SNDCTL_COPR_HALT, WRITE, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_LOAD, READ, struct_copr_buffer_sz); + _(SNDCTL_COPR_RCODE, WRITE, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_RCVMSG, WRITE, struct_copr_msg_sz); + _(SNDCTL_COPR_RDATA, WRITE, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_RESET, NONE, 0); + _(SNDCTL_COPR_RUN, WRITE, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_SENDMSG, READ, struct_copr_msg_sz); + _(SNDCTL_COPR_WCODE, READ, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_WDATA, READ, struct_copr_debug_buf_sz); + _(SNDCTL_DSP_GETBLKSIZE, WRITE, sizeof(int)); + _(SNDCTL_DSP_GETFMTS, WRITE, sizeof(int)); + _(SNDCTL_DSP_NONBLOCK, NONE, 0); + _(SNDCTL_DSP_POST, NONE, 0); + _(SNDCTL_DSP_RESET, NONE, 0); + _(SNDCTL_DSP_SETFMT, WRITE, sizeof(int)); + _(SNDCTL_DSP_SETFRAGMENT, WRITE, sizeof(int)); + _(SNDCTL_DSP_SPEED, WRITE, sizeof(int)); + _(SNDCTL_DSP_STEREO, WRITE, sizeof(int)); + _(SNDCTL_DSP_SUBDIVIDE, WRITE, sizeof(int)); + _(SNDCTL_DSP_SYNC, NONE, 0); + _(SNDCTL_FM_4OP_ENABLE, READ, sizeof(int)); + _(SNDCTL_FM_LOAD_INSTR, READ, struct_sbi_instrument_sz); + _(SNDCTL_MIDI_INFO, WRITE, struct_midi_info_sz); + _(SNDCTL_MIDI_PRETIME, WRITE, sizeof(int)); + _(SNDCTL_SEQ_CTRLRATE, WRITE, sizeof(int)); + _(SNDCTL_SEQ_GETINCOUNT, WRITE, sizeof(int)); + _(SNDCTL_SEQ_GETOUTCOUNT, WRITE, sizeof(int)); + _(SNDCTL_SEQ_NRMIDIS, WRITE, sizeof(int)); + _(SNDCTL_SEQ_NRSYNTHS, WRITE, sizeof(int)); + _(SNDCTL_SEQ_OUTOFBAND, READ, struct_seq_event_rec_sz); + _(SNDCTL_SEQ_PANIC, NONE, 0); + _(SNDCTL_SEQ_PERCMODE, NONE, 0); + _(SNDCTL_SEQ_RESET, NONE, 0); + _(SNDCTL_SEQ_RESETSAMPLES, READ, sizeof(int)); + _(SNDCTL_SEQ_SYNC, NONE, 0); + _(SNDCTL_SEQ_TESTMIDI, READ, sizeof(int)); + _(SNDCTL_SEQ_THRESHOLD, READ, sizeof(int)); + _(SNDCTL_SYNTH_INFO, WRITE, struct_synth_info_sz); + _(SNDCTL_SYNTH_MEMAVL, WRITE, sizeof(int)); + _(SNDCTL_TMR_METRONOME, READ, sizeof(int)); + _(SNDCTL_TMR_SELECT, WRITE, sizeof(int)); + _(SNDCTL_TMR_SOURCE, WRITE, sizeof(int)); + _(SNDCTL_TMR_TEMPO, WRITE, sizeof(int)); + _(SNDCTL_TMR_TIMEBASE, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_ALTPCM, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_BASS, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_CAPS, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_CD, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_DEVMASK, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_ENHANCE, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_IGAIN, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_IMIX, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_LINE, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_LINE1, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_LINE2, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_LINE3, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_MIC, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_OGAIN, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_PCM, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_RECLEV, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_RECMASK, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_RECSRC, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_SPEAKER, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_STEREODEVS, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_SYNTH, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_TREBLE, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_VOLUME, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_ALTPCM, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_BASS, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_CD, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_ENHANCE, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_IGAIN, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_IMIX, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_LINE, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_LINE1, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_LINE2, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_LINE3, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_MIC, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_OGAIN, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_PCM, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_RECLEV, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_RECSRC, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_SPEAKER, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_SYNTH, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_TREBLE, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_VOLUME, WRITE, sizeof(int)); + _(SOUND_PCM_READ_BITS, WRITE, sizeof(int)); + _(SOUND_PCM_READ_CHANNELS, WRITE, sizeof(int)); + _(SOUND_PCM_READ_FILTER, WRITE, sizeof(int)); + _(SOUND_PCM_READ_RATE, WRITE, sizeof(int)); + _(SOUND_PCM_WRITE_CHANNELS, WRITE, sizeof(int)); + _(SOUND_PCM_WRITE_FILTER, WRITE, sizeof(int)); + _(TCFLSH, NONE, 0); + _(TCGETA, WRITE, struct_termio_sz); + _(TCGETS, WRITE, struct_termios_sz); + _(TCSBRK, NONE, 0); + _(TCSBRKP, NONE, 0); + _(TCSETA, READ, struct_termio_sz); + _(TCSETAF, READ, struct_termio_sz); + _(TCSETAW, READ, struct_termio_sz); + _(TCSETS, READ, struct_termios_sz); + _(TCSETSF, READ, struct_termios_sz); + _(TCSETSW, READ, struct_termios_sz); + _(TCXONC, NONE, 0); + _(TIOCGLCKTRMIOS, WRITE, struct_termios_sz); + _(TIOCGSOFTCAR, WRITE, sizeof(int)); + _(TIOCINQ, WRITE, sizeof(int)); + _(TIOCLINUX, READ, sizeof(char)); + _(TIOCSERCONFIG, NONE, 0); + _(TIOCSERGETLSR, WRITE, sizeof(int)); + _(TIOCSERGWILD, WRITE, sizeof(int)); + _(TIOCSERSWILD, READ, sizeof(int)); + _(TIOCSLCKTRMIOS, READ, struct_termios_sz); + _(TIOCSSOFTCAR, READ, sizeof(int)); + _(VT_ACTIVATE, NONE, 0); + _(VT_DISALLOCATE, NONE, 0); + _(VT_GETMODE, WRITE, struct_vt_mode_sz); + _(VT_GETSTATE, WRITE, struct_vt_stat_sz); + _(VT_OPENQRY, WRITE, sizeof(int)); + _(VT_RELDISP, NONE, 0); + _(VT_RESIZE, READ, struct_vt_sizes_sz); + _(VT_RESIZEX, READ, struct_vt_consize_sz); + _(VT_SENDSIG, NONE, 0); + _(VT_SETMODE, READ, struct_vt_mode_sz); + _(VT_WAITACTIVE, NONE, 0); +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + // _(SIOCDEVPLIP, WRITE, struct_ifreq_sz); // the same as EQL_ENSLAVE + _(CYGETDEFTHRESH, WRITE, sizeof(int)); + _(CYGETDEFTIMEOUT, WRITE, sizeof(int)); + _(CYGETMON, WRITE, struct_cyclades_monitor_sz); + _(CYGETTHRESH, WRITE, sizeof(int)); + _(CYGETTIMEOUT, WRITE, sizeof(int)); + _(CYSETDEFTHRESH, NONE, 0); + _(CYSETDEFTIMEOUT, NONE, 0); + _(CYSETTHRESH, NONE, 0); + _(CYSETTIMEOUT, NONE, 0); + _(EQL_EMANCIPATE, WRITE, struct_ifreq_sz); + _(EQL_ENSLAVE, WRITE, struct_ifreq_sz); + _(EQL_GETMASTRCFG, WRITE, struct_ifreq_sz); + _(EQL_GETSLAVECFG, WRITE, struct_ifreq_sz); + _(EQL_SETMASTRCFG, WRITE, struct_ifreq_sz); + _(EQL_SETSLAVECFG, WRITE, struct_ifreq_sz); + _(EVIOCGKEYCODE_V2, WRITE, struct_input_keymap_entry_sz); + _(EVIOCGPROP, WRITE, 0); + _(EVIOCSKEYCODE_V2, READ, struct_input_keymap_entry_sz); + _(FS_IOC_GETFLAGS, WRITE, sizeof(int)); + _(FS_IOC_GETVERSION, WRITE, sizeof(int)); + _(FS_IOC_SETFLAGS, READ, sizeof(int)); + _(FS_IOC_SETVERSION, READ, sizeof(int)); + _(GIO_CMAP, WRITE, 48); + _(GIO_FONT, WRITE, 8192); + _(GIO_SCRNMAP, WRITE, e_tabsz); + _(GIO_UNIMAP, WRITE, struct_unimapdesc_sz); + _(GIO_UNISCRNMAP, WRITE, sizeof(short) * e_tabsz); + _(KDADDIO, NONE, 0); + _(KDDELIO, NONE, 0); + _(KDDISABIO, NONE, 0); + _(KDENABIO, NONE, 0); + _(KDGETKEYCODE, WRITE, struct_kbkeycode_sz); + _(KDGETLED, WRITE, 1); + _(KDGETMODE, WRITE, sizeof(int)); + _(KDGKBDIACR, WRITE, struct_kbdiacrs_sz); + _(KDGKBENT, WRITE, struct_kbentry_sz); + _(KDGKBLED, WRITE, sizeof(int)); + _(KDGKBMETA, WRITE, sizeof(int)); + _(KDGKBMODE, WRITE, sizeof(int)); + _(KDGKBSENT, WRITE, struct_kbsentry_sz); + _(KDGKBTYPE, WRITE, 1); + _(KDMAPDISP, NONE, 0); + _(KDMKTONE, NONE, 0); + _(KDSETKEYCODE, READ, struct_kbkeycode_sz); + _(KDSETLED, NONE, 0); + _(KDSETMODE, NONE, 0); + _(KDSIGACCEPT, NONE, 0); + _(KDSKBDIACR, READ, struct_kbdiacrs_sz); + _(KDSKBENT, READ, struct_kbentry_sz); + _(KDSKBLED, NONE, 0); + _(KDSKBMETA, NONE, 0); + _(KDSKBMODE, NONE, 0); + _(KDSKBSENT, READ, struct_kbsentry_sz); + _(KDUNMAPDISP, NONE, 0); + _(KIOCSOUND, NONE, 0); + _(LPABORT, NONE, 0); + _(LPABORTOPEN, NONE, 0); + _(LPCAREFUL, NONE, 0); + _(LPCHAR, NONE, 0); + _(LPGETIRQ, WRITE, sizeof(int)); + _(LPGETSTATUS, WRITE, sizeof(int)); + _(LPRESET, NONE, 0); + _(LPSETIRQ, NONE, 0); + _(LPTIME, NONE, 0); + _(LPWAIT, NONE, 0); + _(MTIOCGETCONFIG, WRITE, struct_mtconfiginfo_sz); + _(MTIOCSETCONFIG, READ, struct_mtconfiginfo_sz); + _(PIO_CMAP, NONE, 0); + _(PIO_FONT, READ, 8192); + _(PIO_SCRNMAP, READ, e_tabsz); + _(PIO_UNIMAP, READ, struct_unimapdesc_sz); + _(PIO_UNIMAPCLR, READ, struct_unimapinit_sz); + _(PIO_UNISCRNMAP, READ, sizeof(short) * e_tabsz); + _(SCSI_IOCTL_PROBE_HOST, READ, sizeof(int)); + _(SCSI_IOCTL_TAGGED_DISABLE, NONE, 0); + _(SCSI_IOCTL_TAGGED_ENABLE, NONE, 0); + _(SNDCTL_DSP_GETISPACE, WRITE, struct_audio_buf_info_sz); + _(SNDCTL_DSP_GETOSPACE, WRITE, struct_audio_buf_info_sz); + _(TIOCGSERIAL, WRITE, struct_serial_struct_sz); + _(TIOCSERGETMULTI, WRITE, struct_serial_multiport_struct_sz); + _(TIOCSERSETMULTI, READ, struct_serial_multiport_struct_sz); + _(TIOCSSERIAL, READ, struct_serial_struct_sz); + + // The following ioctl requests are shared between AX25, IPX, netrom and + // mrouted. + // _(SIOCAIPXITFCRT, READ, sizeof(char)); + // _(SIOCAX25GETUID, READ, struct_sockaddr_ax25_sz); + // _(SIOCNRGETPARMS, WRITE, struct_nr_parms_struct_sz); + // _(SIOCAIPXPRISLT, READ, sizeof(char)); + // _(SIOCNRSETPARMS, READ, struct_nr_parms_struct_sz); + // _(SIOCAX25ADDUID, READ, struct_sockaddr_ax25_sz); + // _(SIOCNRDECOBS, NONE, 0); + // _(SIOCAX25DELUID, READ, struct_sockaddr_ax25_sz); + // _(SIOCIPXCFGDATA, WRITE, struct_ipx_config_data_sz); + // _(SIOCAX25NOUID, READ, sizeof(int)); + // _(SIOCNRRTCTL, READ, sizeof(int)); + // _(SIOCAX25DIGCTL, READ, sizeof(int)); + // _(SIOCAX25GETPARMS, WRITE, struct_ax25_parms_struct_sz); + // _(SIOCAX25SETPARMS, READ, struct_ax25_parms_struct_sz); +#endif +#undef _ +} + +static bool ioctl_initialized = false; + +struct ioctl_desc_compare { + bool operator()(const ioctl_desc& left, const ioctl_desc& right) const { + return left.req < right.req; + } +}; + +static void ioctl_init() { + ioctl_table_fill(); + InternalSort(&ioctl_table, ioctl_table_size, ioctl_desc_compare()); + + bool bad = false; + for (unsigned i = 0; i < ioctl_table_size - 1; ++i) { + if (ioctl_table[i].req >= ioctl_table[i + 1].req) { + Printf("Duplicate or unsorted ioctl request id %x >= %x (%s vs %s)\n", + ioctl_table[i].req, ioctl_table[i + 1].req, ioctl_table[i].name, + ioctl_table[i + 1].name); + bad = true; + } + } + + if (bad) Die(); + + ioctl_initialized = true; +} + +// Handle the most evil ioctls that encode argument value as part of request id. +static unsigned ioctl_request_fixup(unsigned req) { +#if SANITIZER_LINUX + if ((req & ~0x3fff001fU) == IOCTL_EVIOCGBIT) + return IOCTL_EVIOCGBIT; + if ((req & ~0x3fU) == IOCTL_EVIOCGABS) + return IOCTL_EVIOCGABS; + if ((req & ~0x3fU) == IOCTL_EVIOCSABS) + return IOCTL_EVIOCSABS; +#endif + return req; +} + +static const ioctl_desc *ioctl_table_lookup(unsigned req) { + int left = 0; + int right = ioctl_table_size; + while (left < right) { + int mid = (left + right) / 2; + if (ioctl_table[mid].req < req) + left = mid + 1; + else + right = mid; + } + if (left == right && ioctl_table[left].req == req) + return ioctl_table + left; + else + return 0; +} + +static const ioctl_desc *ioctl_lookup(unsigned req) { + req = ioctl_request_fixup(req); + const ioctl_desc *desc = ioctl_table_lookup(req); + if (desc) return desc; + + // Try stripping access size from the request id. + desc = ioctl_table_lookup(req & ~0x3fff0000U); + // Sanity check: requests that encode access size are either read or write and + // have size of 0 in the table. + if (desc && desc->size == 0 && + (desc->type == ioctl_desc::WRITE || desc->type == ioctl_desc::READ)) + return desc; + return 0; +} + +static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d, + unsigned request, void *arg) { + if (desc->type == ioctl_desc::READ) { + unsigned size = desc->size ? desc->size : IOC_SIZE(request); + COMMON_INTERCEPTOR_READ_RANGE(ctx, arg, size); + } + if (desc->type != ioctl_desc::CUSTOM) + return; + switch (request) { + case 0x00008912: { // SIOCGIFCONF + struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg; + COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len)); + break; + } + } + return; +} + +static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d, + unsigned request, void *arg) { + if (desc->type == ioctl_desc::WRITE) { + // FIXME: add verbose output + unsigned size = desc->size ? desc->size : IOC_SIZE(request); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, arg, size); + } + if (desc->type != ioctl_desc::CUSTOM) + return; + switch (request) { + case 0x00008912: { // SIOCGIFCONF + struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len); + break; + } + } + return; +} diff --git a/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc b/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc index 8bb5cd818ac2..08752e6a3b88 100644 --- a/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc +++ b/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc @@ -278,7 +278,7 @@ static void scanf_common(void *ctx, int n_inputs, bool allowGnuMalloc, CHECK_GT(n_inputs, 0); const char *p = format; - while (*p && n_inputs) { + while (*p) { ScanfDirective dir; p = scanf_parse_next(p, allowGnuMalloc, &dir); if (!p) @@ -301,6 +301,8 @@ static void scanf_common(void *ctx, int n_inputs, bool allowGnuMalloc, void *argp = va_arg(aq, void *); if (dir.convSpecifier != 'n') --n_inputs; + if (n_inputs < 0) + break; if (size == SSS_STRLEN) { size = internal_strlen((const char *)argp) + 1; } diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc index 36f6cf0bc0db..f3430074eb0f 100644 --- a/lib/sanitizer_common/sanitizer_common_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc @@ -17,7 +17,21 @@ namespace __sanitizer { bool PrintsToTty() { MaybeOpenReportFile(); - return internal_isatty(report_fd); + return internal_isatty(report_fd) != 0; } +bool PrintsToTtyCached() { + // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color + // printing on Windows. + if (SANITIZER_WINDOWS) + return 0; + + static int cached = 0; + static bool prints_to_tty; + if (!cached) { // Not thread-safe. + prints_to_tty = PrintsToTty(); + cached = 1; + } + return prints_to_tty; +} } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_common_syscalls.inc b/lib/sanitizer_common/sanitizer_common_syscalls.inc index da25e6b6ad2c..958f12f84393 100644 --- a/lib/sanitizer_common/sanitizer_common_syscalls.inc +++ b/lib/sanitizer_common/sanitizer_common_syscalls.inc @@ -25,18 +25,41 @@ // COMMON_SYSCALL_POST_WRITE_RANGE // Called in posthook for regions that were written to by the kernel // and are now initialized. +// COMMON_SYSCALL_FD_CLOSE(fd) +// Called before closing file descriptor fd. +// COMMON_SYSCALL_PRE_FORK() +// Called before fork syscall. +// COMMON_SYSCALL_POST_FORK(long res) +// Called after fork syscall. //===----------------------------------------------------------------------===// +#include "sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_libc.h" + #define PRE_SYSCALL(name) \ - SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_pre_##name + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_pre_impl_##name #define PRE_READ(p, s) COMMON_SYSCALL_PRE_READ_RANGE(p, s) #define PRE_WRITE(p, s) COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) #define POST_SYSCALL(name) \ - SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_post_##name + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_post_impl_##name #define POST_READ(p, s) COMMON_SYSCALL_POST_READ_RANGE(p, s) #define POST_WRITE(p, s) COMMON_SYSCALL_POST_WRITE_RANGE(p, s) +#ifndef COMMON_SYSCALL_FD_CLOSE +# define COMMON_SYSCALL_FD_CLOSE(fd) +#endif + +#ifndef COMMON_SYSCALL_PRE_FORK +# define COMMON_SYSCALL_PRE_FORK() +#endif + +#ifndef COMMON_SYSCALL_POST_FORK +# define COMMON_SYSCALL_POST_FORK(res) +#endif + // FIXME: do some kind of PRE_READ for all syscall arguments (int(s) and such). extern "C" { @@ -55,6 +78,16 @@ struct sanitizer_kernel_msghdr { unsigned msg_flags; }; +struct sanitizer_kernel_mmsghdr { + struct sanitizer_kernel_msghdr msg_hdr; + unsigned msg_len; +}; + +struct sanitizer_kernel_timespec { + long tv_sec; + long tv_nsec; +}; + struct sanitizer_kernel_timeval { long tv_sec; long tv_usec; @@ -65,79 +98,2683 @@ struct sanitizer_kernel_rusage { long ru_long[14]; }; -PRE_SYSCALL(recvmsg)(int sockfd, struct sanitizer_kernel_msghdr *msg, - int flags) { +struct sanitizer_kernel_sockaddr { + unsigned short sa_family; + char sa_data[14]; +}; + +// Real sigset size is always passed as a syscall argument. +// Declare it "void" to catch sizeof(kernel_sigset_t). +typedef void kernel_sigset_t; + +static void kernel_write_iovec(const __sanitizer_iovec *iovec, + SIZE_T iovlen, SIZE_T maxlen) { + for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { + SSIZE_T sz = Min(iovec[i].iov_len, maxlen); + POST_WRITE(iovec[i].iov_base, sz); + maxlen -= sz; + } +} + +// This functions uses POST_READ, because it needs to run after syscall to know +// the real read range. +static void kernel_read_iovec(const __sanitizer_iovec *iovec, + SIZE_T iovlen, SIZE_T maxlen) { + POST_READ(iovec, sizeof(*iovec) * iovlen); + for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { + SSIZE_T sz = Min(iovec[i].iov_len, maxlen); + POST_READ(iovec[i].iov_base, sz); + maxlen -= sz; + } +} + +PRE_SYSCALL(recvmsg)(long sockfd, sanitizer_kernel_msghdr *msg, long flags) { PRE_READ(msg, sizeof(*msg)); } -POST_SYSCALL(recvmsg)(long res, int sockfd, struct sanitizer_kernel_msghdr *msg, - int flags) { - if (res > 0) - for (unsigned long i = 0; i < msg->msg_iovlen; ++i) { - POST_WRITE(msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len); +POST_SYSCALL(recvmsg)(long res, long sockfd, sanitizer_kernel_msghdr *msg, + long flags) { + if (res >= 0) { + if (msg) { + for (unsigned long i = 0; i < msg->msg_iovlen; ++i) { + POST_WRITE(msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len); + } + POST_WRITE(msg->msg_control, msg->msg_controllen); } - POST_WRITE(msg->msg_control, msg->msg_controllen); + } +} + +PRE_SYSCALL(recvmmsg)(long fd, sanitizer_kernel_mmsghdr *msg, long vlen, + long flags, void *timeout) { + PRE_READ(msg, vlen * sizeof(*msg)); +} + +POST_SYSCALL(recvmmsg)(long res, long fd, sanitizer_kernel_mmsghdr *msg, + long vlen, long flags, void *timeout) { + if (res >= 0) { + if (msg) { + for (unsigned long i = 0; i < msg->msg_hdr.msg_iovlen; ++i) { + POST_WRITE(msg->msg_hdr.msg_iov[i].iov_base, + msg->msg_hdr.msg_iov[i].iov_len); + } + POST_WRITE(msg->msg_hdr.msg_control, msg->msg_hdr.msg_controllen); + POST_WRITE(&msg->msg_len, sizeof(msg->msg_len)); + } + if (timeout) POST_WRITE(timeout, struct_timespec_sz); + } +} + +PRE_SYSCALL(read)(long fd, void *buf, uptr count) { + if (buf) { + PRE_WRITE(buf, count); + } +} + +POST_SYSCALL(read)(long res, long fd, void *buf, uptr count) { + if (res > 0 && buf) { + POST_WRITE(buf, res); + } +} + +PRE_SYSCALL(time)(void *tloc) {} + +POST_SYSCALL(time)(long res, void *tloc) { + if (res >= 0) { + if (tloc) POST_WRITE(tloc, sizeof(long)); + } +} + +PRE_SYSCALL(stime)(void *tptr) {} + +POST_SYSCALL(stime)(long res, void *tptr) { + if (res >= 0) { + if (tptr) POST_WRITE(tptr, sizeof(long)); + } +} + +PRE_SYSCALL(gettimeofday)(void *tv, void *tz) {} + +POST_SYSCALL(gettimeofday)(long res, void *tv, void *tz) { + if (res >= 0) { + if (tv) POST_WRITE(tv, timeval_sz); + if (tz) POST_WRITE(tz, struct_timezone_sz); + } +} + +PRE_SYSCALL(settimeofday)(void *tv, void *tz) {} + +POST_SYSCALL(settimeofday)(long res, void *tv, void *tz) { + if (res >= 0) { + if (tv) POST_WRITE(tv, timeval_sz); + if (tz) POST_WRITE(tz, struct_timezone_sz); + } +} + +PRE_SYSCALL(adjtimex)(void *txc_p) {} + +POST_SYSCALL(adjtimex)(long res, void *txc_p) { + if (res >= 0) { + if (txc_p) POST_WRITE(txc_p, struct_timex_sz); + } +} + +PRE_SYSCALL(times)(void *tbuf) {} + +POST_SYSCALL(times)(long res, void *tbuf) { + if (res >= 0) { + if (tbuf) POST_WRITE(tbuf, struct_tms_sz); + } +} + +PRE_SYSCALL(gettid)() {} + +POST_SYSCALL(gettid)(long res) {} + +PRE_SYSCALL(nanosleep)(void *rqtp, void *rmtp) {} + +POST_SYSCALL(nanosleep)(long res, void *rqtp, void *rmtp) { + if (res >= 0) { + if (rqtp) POST_WRITE(rqtp, struct_timespec_sz); + if (rmtp) POST_WRITE(rmtp, struct_timespec_sz); + } +} + +PRE_SYSCALL(alarm)(long seconds) {} + +POST_SYSCALL(alarm)(long res, long seconds) {} + +PRE_SYSCALL(getpid)() {} + +POST_SYSCALL(getpid)(long res) {} + +PRE_SYSCALL(getppid)() {} + +POST_SYSCALL(getppid)(long res) {} + +PRE_SYSCALL(getuid)() {} + +POST_SYSCALL(getuid)(long res) {} + +PRE_SYSCALL(geteuid)() {} + +POST_SYSCALL(geteuid)(long res) {} + +PRE_SYSCALL(getgid)() {} + +POST_SYSCALL(getgid)(long res) {} + +PRE_SYSCALL(getegid)() {} + +POST_SYSCALL(getegid)(long res) {} + +PRE_SYSCALL(getresuid)(void *ruid, void *euid, void *suid) {} + +POST_SYSCALL(getresuid)(long res, void *ruid, void *euid, void *suid) { + if (res >= 0) { + if (ruid) POST_WRITE(ruid, sizeof(unsigned)); + if (euid) POST_WRITE(euid, sizeof(unsigned)); + if (suid) POST_WRITE(suid, sizeof(unsigned)); + } +} + +PRE_SYSCALL(getresgid)(void *rgid, void *egid, void *sgid) {} + +POST_SYSCALL(getresgid)(long res, void *rgid, void *egid, void *sgid) { + if (res >= 0) { + if (rgid) POST_WRITE(rgid, sizeof(unsigned)); + if (egid) POST_WRITE(egid, sizeof(unsigned)); + if (sgid) POST_WRITE(sgid, sizeof(unsigned)); + } +} + +PRE_SYSCALL(getpgid)(long pid) {} + +POST_SYSCALL(getpgid)(long res, long pid) {} + +PRE_SYSCALL(getpgrp)() {} + +POST_SYSCALL(getpgrp)(long res) {} + +PRE_SYSCALL(getsid)(long pid) {} + +POST_SYSCALL(getsid)(long res, long pid) {} + +PRE_SYSCALL(getgroups)(long gidsetsize, void *grouplist) {} + +POST_SYSCALL(getgroups)(long res, long gidsetsize, + __sanitizer___kernel_gid_t *grouplist) { + if (res >= 0) { + if (grouplist) POST_WRITE(grouplist, res * sizeof(*grouplist)); + } +} + +PRE_SYSCALL(setregid)(long rgid, long egid) {} + +POST_SYSCALL(setregid)(long res, long rgid, long egid) {} + +PRE_SYSCALL(setgid)(long gid) {} + +POST_SYSCALL(setgid)(long res, long gid) {} + +PRE_SYSCALL(setreuid)(long ruid, long euid) {} + +POST_SYSCALL(setreuid)(long res, long ruid, long euid) {} + +PRE_SYSCALL(setuid)(long uid) {} + +POST_SYSCALL(setuid)(long res, long uid) {} + +PRE_SYSCALL(setresuid)(long ruid, long euid, long suid) {} + +POST_SYSCALL(setresuid)(long res, long ruid, long euid, long suid) {} + +PRE_SYSCALL(setresgid)(long rgid, long egid, long sgid) {} + +POST_SYSCALL(setresgid)(long res, long rgid, long egid, long sgid) {} + +PRE_SYSCALL(setfsuid)(long uid) {} + +POST_SYSCALL(setfsuid)(long res, long uid) {} + +PRE_SYSCALL(setfsgid)(long gid) {} + +POST_SYSCALL(setfsgid)(long res, long gid) {} + +PRE_SYSCALL(setpgid)(long pid, long pgid) {} + +POST_SYSCALL(setpgid)(long res, long pid, long pgid) {} + +PRE_SYSCALL(setsid)() {} + +POST_SYSCALL(setsid)(long res) {} + +PRE_SYSCALL(setgroups)(long gidsetsize, __sanitizer___kernel_gid_t *grouplist) { + if (grouplist) POST_WRITE(grouplist, gidsetsize * sizeof(*grouplist)); +} + +POST_SYSCALL(setgroups)(long res, long gidsetsize, + __sanitizer___kernel_gid_t *grouplist) {} + +PRE_SYSCALL(acct)(const void *name) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(acct)(long res, const void *name) {} + +PRE_SYSCALL(capget)(void *header, void *dataptr) {} + +POST_SYSCALL(capget)(long res, void *header, void *dataptr) { + if (res >= 0) { + if (header) POST_WRITE(header, __user_cap_header_struct_sz); + if (dataptr) POST_WRITE(dataptr, __user_cap_data_struct_sz); + } +} + +PRE_SYSCALL(capset)(void *header, const void *data) { + if (data) PRE_READ(data, __user_cap_data_struct_sz); +} + +POST_SYSCALL(capset)(long res, void *header, const void *data) { + if (res >= 0) { + if (header) POST_WRITE(header, __user_cap_header_struct_sz); + } +} + +PRE_SYSCALL(personality)(long personality) {} + +POST_SYSCALL(personality)(long res, long personality) {} + +PRE_SYSCALL(sigpending)(void *set) {} + +POST_SYSCALL(sigpending)(long res, void *set) { + if (res >= 0) { + if (set) POST_WRITE(set, old_sigset_t_sz); + } +} + +PRE_SYSCALL(sigprocmask)(long how, void *set, void *oset) {} + +POST_SYSCALL(sigprocmask)(long res, long how, void *set, void *oset) { + if (res >= 0) { + if (set) POST_WRITE(set, old_sigset_t_sz); + if (oset) POST_WRITE(oset, old_sigset_t_sz); + } } -PRE_SYSCALL(rt_sigpending)(void *p, unsigned long s) { PRE_WRITE(p, s); } +PRE_SYSCALL(getitimer)(long which, void *value) {} -POST_SYSCALL(rt_sigpending)(long res, void *p, unsigned long s) { - if (res == 0) { - POST_WRITE(p, s); +POST_SYSCALL(getitimer)(long res, long which, void *value) { + if (res >= 0) { + if (value) POST_WRITE(value, struct_itimerval_sz); } } -PRE_SYSCALL(getdents)(int fd, void *dirp, int count) { PRE_WRITE(dirp, count); } +PRE_SYSCALL(setitimer)(long which, void *value, void *ovalue) {} -POST_SYSCALL(getdents)(long res, int fd, void *dirp, int count) { - if (res > 0) { - POST_WRITE(dirp, res); +POST_SYSCALL(setitimer)(long res, long which, void *value, void *ovalue) { + if (res >= 0) { + if (value) POST_WRITE(value, struct_itimerval_sz); + if (ovalue) POST_WRITE(ovalue, struct_itimerval_sz); } } -PRE_SYSCALL(getdents64)(int fd, void *dirp, int count) { - PRE_WRITE(dirp, count); +PRE_SYSCALL(timer_create)(long which_clock, void *timer_event_spec, + void *created_timer_id) {} + +POST_SYSCALL(timer_create)(long res, long which_clock, void *timer_event_spec, + void *created_timer_id) { + if (res >= 0) { + if (timer_event_spec) POST_WRITE(timer_event_spec, struct_sigevent_sz); + if (created_timer_id) POST_WRITE(created_timer_id, sizeof(long)); + } } -POST_SYSCALL(getdents64)(long res, int fd, void *dirp, int count) { - if (res > 0) { - POST_WRITE(dirp, res); +PRE_SYSCALL(timer_gettime)(long timer_id, void *setting) {} + +POST_SYSCALL(timer_gettime)(long res, long timer_id, void *setting) { + if (res >= 0) { + if (setting) POST_WRITE(setting, struct_itimerspec_sz); } } -PRE_SYSCALL(wait4)(int pid, int *status, int options, - struct sanitizer_kernel_rusage *r) { - if (status) { - PRE_WRITE(status, sizeof(*status)); +PRE_SYSCALL(timer_getoverrun)(long timer_id) {} + +POST_SYSCALL(timer_getoverrun)(long res, long timer_id) {} + +PRE_SYSCALL(timer_settime)(long timer_id, long flags, const void *new_setting, + void *old_setting) { + if (new_setting) PRE_READ(new_setting, struct_itimerspec_sz); +} + +POST_SYSCALL(timer_settime)(long res, long timer_id, long flags, + const void *new_setting, void *old_setting) { + if (res >= 0) { + if (old_setting) POST_WRITE(old_setting, struct_itimerspec_sz); } - if (r) { - PRE_WRITE(r, sizeof(*r)); +} + +PRE_SYSCALL(timer_delete)(long timer_id) {} + +POST_SYSCALL(timer_delete)(long res, long timer_id) {} + +PRE_SYSCALL(clock_settime)(long which_clock, const void *tp) { + if (tp) PRE_READ(tp, struct_timespec_sz); +} + +POST_SYSCALL(clock_settime)(long res, long which_clock, const void *tp) {} + +PRE_SYSCALL(clock_gettime)(long which_clock, void *tp) {} + +POST_SYSCALL(clock_gettime)(long res, long which_clock, void *tp) { + if (res >= 0) { + if (tp) POST_WRITE(tp, struct_timespec_sz); } } -POST_SYSCALL(wait4)(long res, int pid, int *status, int options, - struct sanitizer_kernel_rusage *r) { - if (res > 0) { - if (status) { - POST_WRITE(status, sizeof(*status)); +PRE_SYSCALL(clock_adjtime)(long which_clock, void *tx) {} + +POST_SYSCALL(clock_adjtime)(long res, long which_clock, void *tx) { + if (res >= 0) { + if (tx) POST_WRITE(tx, struct_timex_sz); + } +} + +PRE_SYSCALL(clock_getres)(long which_clock, void *tp) {} + +POST_SYSCALL(clock_getres)(long res, long which_clock, void *tp) { + if (res >= 0) { + if (tp) POST_WRITE(tp, struct_timespec_sz); + } +} + +PRE_SYSCALL(clock_nanosleep)(long which_clock, long flags, const void *rqtp, + void *rmtp) { + if (rqtp) PRE_READ(rqtp, struct_timespec_sz); +} + +POST_SYSCALL(clock_nanosleep)(long res, long which_clock, long flags, + const void *rqtp, void *rmtp) { + if (res >= 0) { + if (rmtp) POST_WRITE(rmtp, struct_timespec_sz); + } +} + +PRE_SYSCALL(nice)(long increment) {} + +POST_SYSCALL(nice)(long res, long increment) {} + +PRE_SYSCALL(sched_setscheduler)(long pid, long policy, void *param) {} + +POST_SYSCALL(sched_setscheduler)(long res, long pid, long policy, void *param) { + if (res >= 0) { + if (param) POST_WRITE(param, struct_sched_param_sz); + } +} + +PRE_SYSCALL(sched_setparam)(long pid, void *param) { + if (param) PRE_READ(param, struct_sched_param_sz); +} + +POST_SYSCALL(sched_setparam)(long res, long pid, void *param) {} + +PRE_SYSCALL(sched_getscheduler)(long pid) {} + +POST_SYSCALL(sched_getscheduler)(long res, long pid) {} + +PRE_SYSCALL(sched_getparam)(long pid, void *param) {} + +POST_SYSCALL(sched_getparam)(long res, long pid, void *param) { + if (res >= 0) { + if (param) POST_WRITE(param, struct_sched_param_sz); + } +} + +PRE_SYSCALL(sched_setaffinity)(long pid, long len, void *user_mask_ptr) { + if (user_mask_ptr) PRE_READ(user_mask_ptr, len); +} + +POST_SYSCALL(sched_setaffinity)(long res, long pid, long len, + void *user_mask_ptr) {} + +PRE_SYSCALL(sched_getaffinity)(long pid, long len, void *user_mask_ptr) {} + +POST_SYSCALL(sched_getaffinity)(long res, long pid, long len, + void *user_mask_ptr) { + if (res >= 0) { + if (user_mask_ptr) POST_WRITE(user_mask_ptr, len); + } +} + +PRE_SYSCALL(sched_yield)() {} + +POST_SYSCALL(sched_yield)(long res) {} + +PRE_SYSCALL(sched_get_priority_max)(long policy) {} + +POST_SYSCALL(sched_get_priority_max)(long res, long policy) {} + +PRE_SYSCALL(sched_get_priority_min)(long policy) {} + +POST_SYSCALL(sched_get_priority_min)(long res, long policy) {} + +PRE_SYSCALL(sched_rr_get_interval)(long pid, void *interval) {} + +POST_SYSCALL(sched_rr_get_interval)(long res, long pid, void *interval) { + if (res >= 0) { + if (interval) POST_WRITE(interval, struct_timespec_sz); + } +} + +PRE_SYSCALL(setpriority)(long which, long who, long niceval) {} + +POST_SYSCALL(setpriority)(long res, long which, long who, long niceval) {} + +PRE_SYSCALL(getpriority)(long which, long who) {} + +POST_SYSCALL(getpriority)(long res, long which, long who) {} + +PRE_SYSCALL(shutdown)(long arg0, long arg1) {} + +POST_SYSCALL(shutdown)(long res, long arg0, long arg1) {} + +PRE_SYSCALL(reboot)(long magic1, long magic2, long cmd, void *arg) {} + +POST_SYSCALL(reboot)(long res, long magic1, long magic2, long cmd, void *arg) {} + +PRE_SYSCALL(restart_syscall)() {} + +POST_SYSCALL(restart_syscall)(long res) {} + +PRE_SYSCALL(kexec_load)(long entry, long nr_segments, void *segments, + long flags) {} + +POST_SYSCALL(kexec_load)(long res, long entry, long nr_segments, void *segments, + long flags) { + if (res >= 0) { + if (segments) POST_WRITE(segments, struct_kexec_segment_sz); + } +} + +PRE_SYSCALL(exit)(long error_code) {} + +POST_SYSCALL(exit)(long res, long error_code) {} + +PRE_SYSCALL(exit_group)(long error_code) {} + +POST_SYSCALL(exit_group)(long res, long error_code) {} + +PRE_SYSCALL(wait4)(long pid, void *stat_addr, long options, void *ru) {} + +POST_SYSCALL(wait4)(long res, long pid, void *stat_addr, long options, + void *ru) { + if (res >= 0) { + if (stat_addr) POST_WRITE(stat_addr, sizeof(int)); + if (ru) POST_WRITE(ru, struct_rusage_sz); + } +} + +PRE_SYSCALL(waitid)(long which, long pid, void *infop, long options, void *ru) { +} + +POST_SYSCALL(waitid)(long res, long which, long pid, void *infop, long options, + void *ru) { + if (res >= 0) { + if (infop) POST_WRITE(infop, siginfo_t_sz); + if (ru) POST_WRITE(ru, struct_rusage_sz); + } +} + +PRE_SYSCALL(waitpid)(long pid, void *stat_addr, long options) {} + +POST_SYSCALL(waitpid)(long res, long pid, void *stat_addr, long options) { + if (res >= 0) { + if (stat_addr) POST_WRITE(stat_addr, sizeof(int)); + } +} + +PRE_SYSCALL(set_tid_address)(void *tidptr) {} + +POST_SYSCALL(set_tid_address)(long res, void *tidptr) { + if (res >= 0) { + if (tidptr) POST_WRITE(tidptr, sizeof(int)); + } +} + +PRE_SYSCALL(init_module)(void *umod, long len, const void *uargs) { + if (uargs) + PRE_READ(uargs, __sanitizer::internal_strlen((const char *)uargs) + 1); +} + +POST_SYSCALL(init_module)(long res, void *umod, long len, const void *uargs) {} + +PRE_SYSCALL(delete_module)(const void *name_user, long flags) { + if (name_user) + PRE_READ(name_user, + __sanitizer::internal_strlen((const char *)name_user) + 1); +} + +POST_SYSCALL(delete_module)(long res, const void *name_user, long flags) {} + +PRE_SYSCALL(rt_sigprocmask)(long how, void *set, void *oset, long sigsetsize) {} + +POST_SYSCALL(rt_sigprocmask)(long res, long how, kernel_sigset_t *set, + kernel_sigset_t *oset, long sigsetsize) { + if (res >= 0) { + if (set) POST_WRITE(set, sigsetsize); + if (oset) POST_WRITE(oset, sigsetsize); + } +} + +PRE_SYSCALL(rt_sigpending)(void *set, long sigsetsize) {} + +POST_SYSCALL(rt_sigpending)(long res, kernel_sigset_t *set, long sigsetsize) { + if (res >= 0) { + if (set) POST_WRITE(set, sigsetsize); + } +} + +PRE_SYSCALL(rt_sigtimedwait)(const kernel_sigset_t *uthese, void *uinfo, + const void *uts, long sigsetsize) { + if (uthese) PRE_READ(uthese, sigsetsize); + if (uts) PRE_READ(uts, struct_timespec_sz); +} + +POST_SYSCALL(rt_sigtimedwait)(long res, const void *uthese, void *uinfo, + const void *uts, long sigsetsize) { + if (res >= 0) { + if (uinfo) POST_WRITE(uinfo, siginfo_t_sz); + } +} + +PRE_SYSCALL(rt_tgsigqueueinfo)(long tgid, long pid, long sig, void *uinfo) {} + +POST_SYSCALL(rt_tgsigqueueinfo)(long res, long tgid, long pid, long sig, + void *uinfo) { + if (res >= 0) { + if (uinfo) POST_WRITE(uinfo, siginfo_t_sz); + } +} + +PRE_SYSCALL(kill)(long pid, long sig) {} + +POST_SYSCALL(kill)(long res, long pid, long sig) {} + +PRE_SYSCALL(tgkill)(long tgid, long pid, long sig) {} + +POST_SYSCALL(tgkill)(long res, long tgid, long pid, long sig) {} + +PRE_SYSCALL(tkill)(long pid, long sig) {} + +POST_SYSCALL(tkill)(long res, long pid, long sig) {} + +PRE_SYSCALL(rt_sigqueueinfo)(long pid, long sig, void *uinfo) {} + +POST_SYSCALL(rt_sigqueueinfo)(long res, long pid, long sig, void *uinfo) { + if (res >= 0) { + if (uinfo) POST_WRITE(uinfo, siginfo_t_sz); + } +} + +PRE_SYSCALL(sgetmask)() {} + +POST_SYSCALL(sgetmask)(long res) {} + +PRE_SYSCALL(ssetmask)(long newmask) {} + +POST_SYSCALL(ssetmask)(long res, long newmask) {} + +PRE_SYSCALL(signal)(long sig, long handler) {} + +POST_SYSCALL(signal)(long res, long sig, long handler) {} + +PRE_SYSCALL(pause)() {} + +POST_SYSCALL(pause)(long res) {} + +PRE_SYSCALL(sync)() {} + +POST_SYSCALL(sync)(long res) {} + +PRE_SYSCALL(fsync)(long fd) {} + +POST_SYSCALL(fsync)(long res, long fd) {} + +PRE_SYSCALL(fdatasync)(long fd) {} + +POST_SYSCALL(fdatasync)(long res, long fd) {} + +PRE_SYSCALL(bdflush)(long func, long data) {} + +POST_SYSCALL(bdflush)(long res, long func, long data) {} + +PRE_SYSCALL(mount)(void *dev_name, void *dir_name, void *type, long flags, + void *data) {} + +POST_SYSCALL(mount)(long res, void *dev_name, void *dir_name, void *type, + long flags, void *data) { + if (res >= 0) { + if (dev_name) + POST_WRITE(dev_name, + __sanitizer::internal_strlen((const char *)dev_name) + 1); + if (dir_name) + POST_WRITE(dir_name, + __sanitizer::internal_strlen((const char *)dir_name) + 1); + if (type) + POST_WRITE(type, __sanitizer::internal_strlen((const char *)type) + 1); + } +} + +PRE_SYSCALL(umount)(void *name, long flags) {} + +POST_SYSCALL(umount)(long res, void *name, long flags) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(oldumount)(void *name) {} + +POST_SYSCALL(oldumount)(long res, void *name) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(truncate)(const void *path, long length) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(truncate)(long res, const void *path, long length) {} + +PRE_SYSCALL(ftruncate)(long fd, long length) {} + +POST_SYSCALL(ftruncate)(long res, long fd, long length) {} + +PRE_SYSCALL(stat)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(stat)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct___old_kernel_stat_sz); + } +} + +PRE_SYSCALL(statfs)(const void *path, void *buf) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(statfs)(long res, const void *path, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_statfs_sz); + } +} + +PRE_SYSCALL(statfs64)(const void *path, long sz, void *buf) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(statfs64)(long res, const void *path, long sz, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_statfs64_sz); + } +} + +PRE_SYSCALL(fstatfs)(long fd, void *buf) {} + +POST_SYSCALL(fstatfs)(long res, long fd, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_statfs_sz); + } +} + +PRE_SYSCALL(fstatfs64)(long fd, long sz, void *buf) {} + +POST_SYSCALL(fstatfs64)(long res, long fd, long sz, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_statfs64_sz); + } +} + +PRE_SYSCALL(lstat)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(lstat)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct___old_kernel_stat_sz); + } +} + +PRE_SYSCALL(fstat)(long fd, void *statbuf) {} + +POST_SYSCALL(fstat)(long res, long fd, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct___old_kernel_stat_sz); + } +} + +PRE_SYSCALL(newstat)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(newstat)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz); + } +} + +PRE_SYSCALL(newlstat)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(newlstat)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz); + } +} + +PRE_SYSCALL(newfstat)(long fd, void *statbuf) {} + +POST_SYSCALL(newfstat)(long res, long fd, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz); + } +} + +PRE_SYSCALL(ustat)(long dev, void *ubuf) {} + +POST_SYSCALL(ustat)(long res, long dev, void *ubuf) { + if (res >= 0) { + if (ubuf) POST_WRITE(ubuf, struct_ustat_sz); + } +} + +PRE_SYSCALL(stat64)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(stat64)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz); + } +} + +PRE_SYSCALL(fstat64)(long fd, void *statbuf) {} + +POST_SYSCALL(fstat64)(long res, long fd, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz); + } +} + +PRE_SYSCALL(lstat64)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(lstat64)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz); + } +} + +PRE_SYSCALL(setxattr)(const void *path, const void *name, const void *value, + long size, long flags) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); + if (value) PRE_READ(value, size); +} + +POST_SYSCALL(setxattr)(long res, const void *path, const void *name, + const void *value, long size, long flags) {} + +PRE_SYSCALL(lsetxattr)(const void *path, const void *name, const void *value, + long size, long flags) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); + if (value) PRE_READ(value, size); +} + +POST_SYSCALL(lsetxattr)(long res, const void *path, const void *name, + const void *value, long size, long flags) {} + +PRE_SYSCALL(fsetxattr)(long fd, const void *name, const void *value, long size, + long flags) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); + if (value) PRE_READ(value, size); +} + +POST_SYSCALL(fsetxattr)(long res, long fd, const void *name, const void *value, + long size, long flags) {} + +PRE_SYSCALL(getxattr)(const void *path, const void *name, void *value, + long size) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(getxattr)(long res, const void *path, const void *name, + void *value, long size) { + if (res >= 0) { + if (value) POST_WRITE(value, size); + } +} + +PRE_SYSCALL(lgetxattr)(const void *path, const void *name, void *value, + long size) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(lgetxattr)(long res, const void *path, const void *name, + void *value, long size) { + if (res >= 0) { + if (value) POST_WRITE(value, size); + } +} + +PRE_SYSCALL(fgetxattr)(long fd, const void *name, void *value, long size) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(fgetxattr)(long res, long fd, const void *name, void *value, + long size) { + if (res >= 0) { + if (value) POST_WRITE(value, size); + } +} + +PRE_SYSCALL(listxattr)(const void *path, void *list, long size) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(listxattr)(long res, const void *path, void *list, long size) { + if (res >= 0) { + if (list) POST_WRITE(list, size); + } +} + +PRE_SYSCALL(llistxattr)(const void *path, void *list, long size) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(llistxattr)(long res, const void *path, void *list, long size) { + if (res >= 0) { + if (list) POST_WRITE(list, size); + } +} + +PRE_SYSCALL(flistxattr)(long fd, void *list, long size) {} + +POST_SYSCALL(flistxattr)(long res, long fd, void *list, long size) { + if (res >= 0) { + if (list) POST_WRITE(list, size); + } +} + +PRE_SYSCALL(removexattr)(const void *path, const void *name) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(removexattr)(long res, const void *path, const void *name) {} + +PRE_SYSCALL(lremovexattr)(const void *path, const void *name) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(lremovexattr)(long res, const void *path, const void *name) {} + +PRE_SYSCALL(fremovexattr)(long fd, const void *name) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(fremovexattr)(long res, long fd, const void *name) {} + +PRE_SYSCALL(brk)(long brk) {} + +POST_SYSCALL(brk)(long res, long brk) {} + +PRE_SYSCALL(mprotect)(long start, long len, long prot) {} + +POST_SYSCALL(mprotect)(long res, long start, long len, long prot) {} + +PRE_SYSCALL(mremap)(long addr, long old_len, long new_len, long flags, + long new_addr) {} + +POST_SYSCALL(mremap)(long res, long addr, long old_len, long new_len, + long flags, long new_addr) {} + +PRE_SYSCALL(remap_file_pages)(long start, long size, long prot, long pgoff, + long flags) {} + +POST_SYSCALL(remap_file_pages)(long res, long start, long size, long prot, + long pgoff, long flags) {} + +PRE_SYSCALL(msync)(long start, long len, long flags) {} + +POST_SYSCALL(msync)(long res, long start, long len, long flags) {} + +PRE_SYSCALL(munmap)(long addr, long len) {} + +POST_SYSCALL(munmap)(long res, long addr, long len) {} + +PRE_SYSCALL(mlock)(long start, long len) {} + +POST_SYSCALL(mlock)(long res, long start, long len) {} + +PRE_SYSCALL(munlock)(long start, long len) {} + +POST_SYSCALL(munlock)(long res, long start, long len) {} + +PRE_SYSCALL(mlockall)(long flags) {} + +POST_SYSCALL(mlockall)(long res, long flags) {} + +PRE_SYSCALL(munlockall)() {} + +POST_SYSCALL(munlockall)(long res) {} + +PRE_SYSCALL(madvise)(long start, long len, long behavior) {} + +POST_SYSCALL(madvise)(long res, long start, long len, long behavior) {} + +PRE_SYSCALL(mincore)(long start, long len, void *vec) {} + +POST_SYSCALL(mincore)(long res, long start, long len, void *vec) { + if (res >= 0) { + if (vec) { + POST_WRITE(vec, (len + GetPageSizeCached() - 1) / GetPageSizeCached()); } - if (r) { - POST_WRITE(r, sizeof(*r)); + } +} + +PRE_SYSCALL(pivot_root)(const void *new_root, const void *put_old) { + if (new_root) + PRE_READ(new_root, + __sanitizer::internal_strlen((const char *)new_root) + 1); + if (put_old) + PRE_READ(put_old, __sanitizer::internal_strlen((const char *)put_old) + 1); +} + +POST_SYSCALL(pivot_root)(long res, const void *new_root, const void *put_old) {} + +PRE_SYSCALL(chroot)(const void *filename) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chroot)(long res, const void *filename) {} + +PRE_SYSCALL(mknod)(const void *filename, long mode, long dev) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(mknod)(long res, const void *filename, long mode, long dev) {} + +PRE_SYSCALL(link)(const void *oldname, const void *newname) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(link)(long res, const void *oldname, const void *newname) {} + +PRE_SYSCALL(symlink)(const void *old, const void *new_) { + if (old) PRE_READ(old, __sanitizer::internal_strlen((const char *)old) + 1); + if (new_) + PRE_READ(new_, __sanitizer::internal_strlen((const char *)new_) + 1); +} + +POST_SYSCALL(symlink)(long res, const void *old, const void *new_) {} + +PRE_SYSCALL(unlink)(const void *pathname) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(unlink)(long res, const void *pathname) {} + +PRE_SYSCALL(rename)(const void *oldname, const void *newname) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(rename)(long res, const void *oldname, const void *newname) {} + +PRE_SYSCALL(chmod)(const void *filename, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chmod)(long res, const void *filename, long mode) {} + +PRE_SYSCALL(fchmod)(long fd, long mode) {} + +POST_SYSCALL(fchmod)(long res, long fd, long mode) {} + +PRE_SYSCALL(fcntl)(long fd, long cmd, long arg) {} + +POST_SYSCALL(fcntl)(long res, long fd, long cmd, long arg) {} + +PRE_SYSCALL(fcntl64)(long fd, long cmd, long arg) {} + +POST_SYSCALL(fcntl64)(long res, long fd, long cmd, long arg) {} + +PRE_SYSCALL(pipe)(void *fildes) {} + +POST_SYSCALL(pipe)(long res, void *fildes) { + if (res >= 0) { + if (fildes) POST_WRITE(fildes, sizeof(int)); + } +} + +PRE_SYSCALL(pipe2)(void *fildes, long flags) {} + +POST_SYSCALL(pipe2)(long res, void *fildes, long flags) { + if (res >= 0) { + if (fildes) POST_WRITE(fildes, sizeof(int)); + } +} + +PRE_SYSCALL(dup)(long fildes) {} + +POST_SYSCALL(dup)(long res, long fildes) {} + +PRE_SYSCALL(dup2)(long oldfd, long newfd) {} + +POST_SYSCALL(dup2)(long res, long oldfd, long newfd) {} + +PRE_SYSCALL(dup3)(long oldfd, long newfd, long flags) {} + +POST_SYSCALL(dup3)(long res, long oldfd, long newfd, long flags) {} + +PRE_SYSCALL(ioperm)(long from, long num, long on) {} + +POST_SYSCALL(ioperm)(long res, long from, long num, long on) {} + +PRE_SYSCALL(ioctl)(long fd, long cmd, long arg) {} + +POST_SYSCALL(ioctl)(long res, long fd, long cmd, long arg) {} + +PRE_SYSCALL(flock)(long fd, long cmd) {} + +POST_SYSCALL(flock)(long res, long fd, long cmd) {} + +PRE_SYSCALL(io_setup)(long nr_reqs, void *ctx) {} + +POST_SYSCALL(io_setup)(long res, long nr_reqs, void *ctx) { + if (res >= 0) { + if (ctx) POST_WRITE(ctx, sizeof(long)); + } +} + +PRE_SYSCALL(io_destroy)(long ctx) {} + +POST_SYSCALL(io_destroy)(long res, long ctx) {} + +PRE_SYSCALL(io_getevents)(long ctx_id, long min_nr, long nr, void *events, + void *timeout) { + if (timeout) PRE_READ(timeout, struct_timespec_sz); +} + +POST_SYSCALL(io_getevents)(long res, long ctx_id, long min_nr, long nr, + void *events, void *timeout) { + if (res >= 0) { + if (events) POST_WRITE(events, res * struct_io_event_sz); + if (timeout) POST_WRITE(timeout, struct_timespec_sz); + } +} + +PRE_SYSCALL(io_submit)(long ctx_id, long nr, __sanitizer_iocb **iocbpp) { + for (long i = 0; i < nr; ++i) { + if (iocbpp[i]->aio_lio_opcode == iocb_cmd_pwrite && iocbpp[i]->aio_buf && + iocbpp[i]->aio_nbytes) + PRE_READ((void *)iocbpp[i]->aio_buf, iocbpp[i]->aio_nbytes); + } +} + +POST_SYSCALL(io_submit)(long res, long ctx_id, long nr, + __sanitizer_iocb **iocbpp) { + if (res > 0 && iocbpp) { + for (long i = 0; i < res; ++i) { + if (iocbpp[i]->aio_lio_opcode == iocb_cmd_pread && iocbpp[i]->aio_buf && + iocbpp[i]->aio_nbytes) + POST_WRITE((void *)iocbpp[i]->aio_buf, iocbpp[i]->aio_nbytes); } } } -PRE_SYSCALL(waitpid)(int pid, int *status, int options) { - if (status) { - PRE_WRITE(status, sizeof(*status)); +PRE_SYSCALL(io_cancel)(long ctx_id, void *iocb, void *result) {} + +POST_SYSCALL(io_cancel)(long res, long ctx_id, void *iocb, void *result) { + if (res >= 0) { + if (iocb) POST_WRITE(iocb, sizeof(__sanitizer_iocb)); + if (result) POST_WRITE(result, struct_io_event_sz); } } -POST_SYSCALL(waitpid)(long res, int pid, int *status, int options) { - if (res > 0 && status) { - POST_WRITE(status, sizeof(*status)); +PRE_SYSCALL(sendfile)(long out_fd, long in_fd, void *offset, long count) {} + +POST_SYSCALL(sendfile)(long res, long out_fd, long in_fd, + __sanitizer___kernel_off_t *offset, long count) { + if (res >= 0) { + if (offset) POST_WRITE(offset, sizeof(*offset)); } } + +PRE_SYSCALL(sendfile64)(long out_fd, long in_fd, void *offset, long count) {} + +POST_SYSCALL(sendfile64)(long res, long out_fd, long in_fd, + __sanitizer___kernel_loff_t *offset, long count) { + if (res >= 0) { + if (offset) POST_WRITE(offset, sizeof(*offset)); + } +} + +PRE_SYSCALL(readlink)(const void *path, void *buf, long bufsiz) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(readlink)(long res, const void *path, void *buf, long bufsiz) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(creat)(const void *pathname, long mode) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(creat)(long res, const void *pathname, long mode) {} + +PRE_SYSCALL(open)(const void *filename, long flags, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(open)(long res, const void *filename, long flags, long mode) {} + +PRE_SYSCALL(close)(long fd) { + COMMON_SYSCALL_FD_CLOSE((int)fd); +} + +POST_SYSCALL(close)(long res, long fd) {} + +PRE_SYSCALL(access)(const void *filename, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(access)(long res, const void *filename, long mode) {} + +PRE_SYSCALL(vhangup)() {} + +POST_SYSCALL(vhangup)(long res) {} + +PRE_SYSCALL(chown)(const void *filename, long user, long group) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chown)(long res, const void *filename, long user, long group) {} + +PRE_SYSCALL(lchown)(const void *filename, long user, long group) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(lchown)(long res, const void *filename, long user, long group) {} + +PRE_SYSCALL(fchown)(long fd, long user, long group) {} + +POST_SYSCALL(fchown)(long res, long fd, long user, long group) {} + +PRE_SYSCALL(chown16)(const void *filename, long user, long group) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chown16)(long res, const void *filename, long user, long group) {} + +PRE_SYSCALL(lchown16)(const void *filename, long user, long group) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(lchown16)(long res, const void *filename, long user, long group) {} + +PRE_SYSCALL(fchown16)(long fd, long user, long group) {} + +POST_SYSCALL(fchown16)(long res, long fd, long user, long group) {} + +PRE_SYSCALL(setregid16)(long rgid, long egid) {} + +POST_SYSCALL(setregid16)(long res, long rgid, long egid) {} + +PRE_SYSCALL(setgid16)(long gid) {} + +POST_SYSCALL(setgid16)(long res, long gid) {} + +PRE_SYSCALL(setreuid16)(long ruid, long euid) {} + +POST_SYSCALL(setreuid16)(long res, long ruid, long euid) {} + +PRE_SYSCALL(setuid16)(long uid) {} + +POST_SYSCALL(setuid16)(long res, long uid) {} + +PRE_SYSCALL(setresuid16)(long ruid, long euid, long suid) {} + +POST_SYSCALL(setresuid16)(long res, long ruid, long euid, long suid) {} + +PRE_SYSCALL(getresuid16)(void *ruid, void *euid, void *suid) {} + +POST_SYSCALL(getresuid16)(long res, __sanitizer___kernel_old_uid_t *ruid, + __sanitizer___kernel_old_uid_t *euid, + __sanitizer___kernel_old_uid_t *suid) { + if (res >= 0) { + if (ruid) POST_WRITE(ruid, sizeof(*ruid)); + if (euid) POST_WRITE(euid, sizeof(*euid)); + if (suid) POST_WRITE(suid, sizeof(*suid)); + } +} + +PRE_SYSCALL(setresgid16)(long rgid, long egid, long sgid) {} + +POST_SYSCALL(setresgid16)(long res, long rgid, long egid, long sgid) {} + +PRE_SYSCALL(getresgid16)(void *rgid, void *egid, void *sgid) {} + +POST_SYSCALL(getresgid16)(long res, __sanitizer___kernel_old_gid_t *rgid, + __sanitizer___kernel_old_gid_t *egid, + __sanitizer___kernel_old_gid_t *sgid) { + if (res >= 0) { + if (rgid) POST_WRITE(rgid, sizeof(*rgid)); + if (egid) POST_WRITE(egid, sizeof(*egid)); + if (sgid) POST_WRITE(sgid, sizeof(*sgid)); + } +} + +PRE_SYSCALL(setfsuid16)(long uid) {} + +POST_SYSCALL(setfsuid16)(long res, long uid) {} + +PRE_SYSCALL(setfsgid16)(long gid) {} + +POST_SYSCALL(setfsgid16)(long res, long gid) {} + +PRE_SYSCALL(getgroups16)(long gidsetsize, + __sanitizer___kernel_old_gid_t *grouplist) {} + +POST_SYSCALL(getgroups16)(long res, long gidsetsize, + __sanitizer___kernel_old_gid_t *grouplist) { + if (res >= 0) { + if (grouplist) POST_WRITE(grouplist, res * sizeof(*grouplist)); + } +} + +PRE_SYSCALL(setgroups16)(long gidsetsize, + __sanitizer___kernel_old_gid_t *grouplist) { + if (grouplist) POST_WRITE(grouplist, gidsetsize * sizeof(*grouplist)); +} + +POST_SYSCALL(setgroups16)(long res, long gidsetsize, + __sanitizer___kernel_old_gid_t *grouplist) {} + +PRE_SYSCALL(getuid16)() {} + +POST_SYSCALL(getuid16)(long res) {} + +PRE_SYSCALL(geteuid16)() {} + +POST_SYSCALL(geteuid16)(long res) {} + +PRE_SYSCALL(getgid16)() {} + +POST_SYSCALL(getgid16)(long res) {} + +PRE_SYSCALL(getegid16)() {} + +POST_SYSCALL(getegid16)(long res) {} + +PRE_SYSCALL(utime)(void *filename, void *times) {} + +POST_SYSCALL(utime)(long res, void *filename, void *times) { + if (res >= 0) { + if (filename) + POST_WRITE(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); + if (times) POST_WRITE(times, struct_utimbuf_sz); + } +} + +PRE_SYSCALL(utimes)(void *filename, void *utimes) {} + +POST_SYSCALL(utimes)(long res, void *filename, void *utimes) { + if (res >= 0) { + if (filename) + POST_WRITE(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); + if (utimes) POST_WRITE(utimes, timeval_sz); + } +} + +PRE_SYSCALL(lseek)(long fd, long offset, long origin) {} + +POST_SYSCALL(lseek)(long res, long fd, long offset, long origin) {} + +PRE_SYSCALL(llseek)(long fd, long offset_high, long offset_low, void *result, + long origin) {} + +POST_SYSCALL(llseek)(long res, long fd, long offset_high, long offset_low, + void *result, long origin) { + if (res >= 0) { + if (result) POST_WRITE(result, sizeof(long long)); + } +} + +PRE_SYSCALL(readv)(long fd, const __sanitizer_iovec *vec, long vlen) {} + +POST_SYSCALL(readv)(long res, long fd, const __sanitizer_iovec *vec, + long vlen) { + if (res >= 0) { + if (vec) kernel_write_iovec(vec, vlen, res); + } +} + +PRE_SYSCALL(write)(long fd, const void *buf, long count) { + if (buf) PRE_READ(buf, count); +} + +POST_SYSCALL(write)(long res, long fd, const void *buf, long count) {} + +PRE_SYSCALL(writev)(long fd, const __sanitizer_iovec *vec, long vlen) {} + +POST_SYSCALL(writev)(long res, long fd, const __sanitizer_iovec *vec, + long vlen) { + if (res >= 0) { + if (vec) kernel_read_iovec(vec, vlen, res); + } +} + +#ifdef _LP64 +PRE_SYSCALL(pread64)(long fd, void *buf, long count, long pos) {} + +POST_SYSCALL(pread64)(long res, long fd, void *buf, long count, long pos) { + if (res >= 0) { + if (buf) POST_WRITE(buf, res); + } +} + +PRE_SYSCALL(pwrite64)(long fd, const void *buf, long count, long pos) { + if (buf) PRE_READ(buf, count); +} + +POST_SYSCALL(pwrite64)(long res, long fd, const void *buf, long count, + long pos) {} +#else +PRE_SYSCALL(pread64)(long fd, void *buf, long count, long pos0, long pos1) {} + +POST_SYSCALL(pread64)(long res, long fd, void *buf, long count, long pos0, + long pos1) { + if (res >= 0) { + if (buf) POST_WRITE(buf, res); + } +} + +PRE_SYSCALL(pwrite64)(long fd, const void *buf, long count, long pos0, + long pos1) { + if (buf) PRE_READ(buf, count); +} + +POST_SYSCALL(pwrite64)(long res, long fd, const void *buf, long count, + long pos0, long pos1) {} +#endif + +PRE_SYSCALL(preadv)(long fd, const __sanitizer_iovec *vec, long vlen, + long pos_l, long pos_h) {} + +POST_SYSCALL(preadv)(long res, long fd, const __sanitizer_iovec *vec, long vlen, + long pos_l, long pos_h) { + if (res >= 0) { + if (vec) kernel_write_iovec(vec, vlen, res); + } +} + +PRE_SYSCALL(pwritev)(long fd, const __sanitizer_iovec *vec, long vlen, + long pos_l, long pos_h) {} + +POST_SYSCALL(pwritev)(long res, long fd, const __sanitizer_iovec *vec, + long vlen, long pos_l, long pos_h) { + if (res >= 0) { + if (vec) kernel_read_iovec(vec, vlen, res); + } +} + +PRE_SYSCALL(getcwd)(void *buf, long size) {} + +POST_SYSCALL(getcwd)(long res, void *buf, long size) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(mkdir)(const void *pathname, long mode) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(mkdir)(long res, const void *pathname, long mode) {} + +PRE_SYSCALL(chdir)(const void *filename) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chdir)(long res, const void *filename) {} + +PRE_SYSCALL(fchdir)(long fd) {} + +POST_SYSCALL(fchdir)(long res, long fd) {} + +PRE_SYSCALL(rmdir)(const void *pathname) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(rmdir)(long res, const void *pathname) {} + +PRE_SYSCALL(lookup_dcookie)(u64 cookie64, void *buf, long len) {} + +POST_SYSCALL(lookup_dcookie)(long res, u64 cookie64, void *buf, long len) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(quotactl)(long cmd, const void *special, long id, void *addr) { + if (special) + PRE_READ(special, __sanitizer::internal_strlen((const char *)special) + 1); +} + +POST_SYSCALL(quotactl)(long res, long cmd, const void *special, long id, + void *addr) {} + +PRE_SYSCALL(getdents)(long fd, void *dirent, long count) {} + +POST_SYSCALL(getdents)(long res, long fd, void *dirent, long count) { + if (res >= 0) { + if (dirent) POST_WRITE(dirent, res); + } +} + +PRE_SYSCALL(getdents64)(long fd, void *dirent, long count) {} + +POST_SYSCALL(getdents64)(long res, long fd, void *dirent, long count) { + if (res >= 0) { + if (dirent) POST_WRITE(dirent, res); + } +} + +PRE_SYSCALL(setsockopt)(long fd, long level, long optname, void *optval, + long optlen) {} + +POST_SYSCALL(setsockopt)(long res, long fd, long level, long optname, + void *optval, long optlen) { + if (res >= 0) { + if (optval) + POST_WRITE(optval, + __sanitizer::internal_strlen((const char *)optval) + 1); + } +} + +PRE_SYSCALL(getsockopt)(long fd, long level, long optname, void *optval, + void *optlen) {} + +POST_SYSCALL(getsockopt)(long res, long fd, long level, long optname, + void *optval, void *optlen) { + if (res >= 0) { + if (optval) + POST_WRITE(optval, + __sanitizer::internal_strlen((const char *)optval) + 1); + if (optlen) POST_WRITE(optlen, sizeof(int)); + } +} + +PRE_SYSCALL(bind)(long arg0, sanitizer_kernel_sockaddr *arg1, long arg2) {} + +POST_SYSCALL(bind)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + long arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + } +} + +PRE_SYSCALL(connect)(long arg0, sanitizer_kernel_sockaddr *arg1, long arg2) {} + +POST_SYSCALL(connect)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + long arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + } +} + +PRE_SYSCALL(accept)(long arg0, sanitizer_kernel_sockaddr *arg1, void *arg2) {} + +POST_SYSCALL(accept)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(unsigned)); + } +} + +PRE_SYSCALL(accept4)(long arg0, sanitizer_kernel_sockaddr *arg1, void *arg2, + long arg3) {} + +POST_SYSCALL(accept4)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2, long arg3) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(unsigned)); + } +} + +PRE_SYSCALL(getsockname)(long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) {} + +POST_SYSCALL(getsockname)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(unsigned)); + } +} + +PRE_SYSCALL(getpeername)(long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) {} + +POST_SYSCALL(getpeername)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(unsigned)); + } +} + +PRE_SYSCALL(send)(long arg0, void *arg1, long arg2, long arg3) {} + +POST_SYSCALL(send)(long res, long arg0, void *arg1, long arg2, long arg3) { + if (res) { + if (arg1) POST_READ(arg1, res); + } +} + +PRE_SYSCALL(sendto)(long arg0, void *arg1, long arg2, long arg3, + sanitizer_kernel_sockaddr *arg4, long arg5) {} + +POST_SYSCALL(sendto)(long res, long arg0, void *arg1, long arg2, long arg3, + sanitizer_kernel_sockaddr *arg4, long arg5) { + if (res >= 0) { + if (arg1) POST_READ(arg1, res); + if (arg4) POST_WRITE(arg4, sizeof(*arg4)); + } +} + +PRE_SYSCALL(sendmsg)(long fd, void *msg, long flags) {} + +POST_SYSCALL(sendmsg)(long res, long fd, void *msg, long flags) { + // FIXME: POST_READ +} + +PRE_SYSCALL(sendmmsg)(long fd, void *msg, long vlen, long flags) {} + +POST_SYSCALL(sendmmsg)(long res, long fd, void *msg, long vlen, long flags) { + // FIXME: POST_READ +} + +PRE_SYSCALL(recv)(long arg0, void *buf, long len, long flags) {} + +POST_SYSCALL(recv)(long res, void *buf, long len, long flags) { + if (res >= 0) { + if (buf) POST_WRITE(buf, res); + } +} + +PRE_SYSCALL(recvfrom)(long arg0, void *buf, long len, long flags, + sanitizer_kernel_sockaddr *arg4, void *arg5) {} + +POST_SYSCALL(recvfrom)(long res, long arg0, void *buf, long len, long flags, + sanitizer_kernel_sockaddr *arg4, void *arg5) { + if (res >= 0) { + if (buf) POST_WRITE(buf, res); + if (arg4) POST_WRITE(arg4, sizeof(*arg4)); + if (arg5) POST_WRITE(arg5, sizeof(int)); + } +} + +PRE_SYSCALL(socket)(long arg0, long arg1, long arg2) {} + +POST_SYSCALL(socket)(long res, long arg0, long arg1, long arg2) {} + +PRE_SYSCALL(socketpair)(long arg0, long arg1, long arg2, void *arg3) {} + +POST_SYSCALL(socketpair)(long res, long arg0, long arg1, long arg2, + void *arg3) { + if (res >= 0) { + if (arg3) POST_WRITE(arg3, sizeof(int)); + } +} + +PRE_SYSCALL(socketcall)(long call, void *args) {} + +POST_SYSCALL(socketcall)(long res, long call, void *args) { + if (res >= 0) { + if (args) POST_WRITE(args, sizeof(long)); + } +} + +PRE_SYSCALL(listen)(long arg0, long arg1) {} + +POST_SYSCALL(listen)(long res, long arg0, long arg1) {} + +PRE_SYSCALL(poll)(void *ufds, long nfds, long timeout) {} + +POST_SYSCALL(poll)(long res, __sanitizer_pollfd *ufds, long nfds, + long timeout) { + if (res >= 0) { + if (ufds) POST_WRITE(ufds, nfds * sizeof(*ufds)); + } +} + +PRE_SYSCALL(select)(long n, __sanitizer___kernel_fd_set *inp, + __sanitizer___kernel_fd_set *outp, + __sanitizer___kernel_fd_set *exp, void *tvp) {} + +POST_SYSCALL(select)(long res, long n, __sanitizer___kernel_fd_set *inp, + __sanitizer___kernel_fd_set *outp, + __sanitizer___kernel_fd_set *exp, void *tvp) { + if (res >= 0) { + if (inp) POST_WRITE(inp, sizeof(*inp)); + if (outp) POST_WRITE(outp, sizeof(*outp)); + if (exp) POST_WRITE(exp, sizeof(*exp)); + if (tvp) POST_WRITE(tvp, timeval_sz); + } +} + +PRE_SYSCALL(old_select)(void *arg) {} + +POST_SYSCALL(old_select)(long res, void *arg) {} + +PRE_SYSCALL(epoll_create)(long size) {} + +POST_SYSCALL(epoll_create)(long res, long size) {} + +PRE_SYSCALL(epoll_create1)(long flags) {} + +POST_SYSCALL(epoll_create1)(long res, long flags) {} + +PRE_SYSCALL(epoll_ctl)(long epfd, long op, long fd, void *event) {} + +POST_SYSCALL(epoll_ctl)(long res, long epfd, long op, long fd, void *event) { + if (res >= 0) { + if (event) POST_WRITE(event, struct_epoll_event_sz); + } +} + +PRE_SYSCALL(epoll_wait)(long epfd, void *events, long maxevents, long timeout) { +} + +POST_SYSCALL(epoll_wait)(long res, long epfd, void *events, long maxevents, + long timeout) { + if (res >= 0) { + if (events) POST_WRITE(events, struct_epoll_event_sz); + } +} + +PRE_SYSCALL(epoll_pwait)(long epfd, void *events, long maxevents, long timeout, + const kernel_sigset_t *sigmask, long sigsetsize) { + if (sigmask) PRE_READ(sigmask, sigsetsize); +} + +POST_SYSCALL(epoll_pwait)(long res, long epfd, void *events, long maxevents, + long timeout, const void *sigmask, long sigsetsize) { + if (res >= 0) { + if (events) POST_WRITE(events, struct_epoll_event_sz); + } +} + +PRE_SYSCALL(gethostname)(void *name, long len) {} + +POST_SYSCALL(gethostname)(long res, void *name, long len) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(sethostname)(void *name, long len) {} + +POST_SYSCALL(sethostname)(long res, void *name, long len) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(setdomainname)(void *name, long len) {} + +POST_SYSCALL(setdomainname)(long res, void *name, long len) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(newuname)(void *name) {} + +POST_SYSCALL(newuname)(long res, void *name) { + if (res >= 0) { + if (name) POST_WRITE(name, struct_new_utsname_sz); + } +} + +PRE_SYSCALL(uname)(void *arg0) {} + +POST_SYSCALL(uname)(long res, void *arg0) { + if (res >= 0) { + if (arg0) POST_WRITE(arg0, struct_old_utsname_sz); + } +} + +PRE_SYSCALL(olduname)(void *arg0) {} + +POST_SYSCALL(olduname)(long res, void *arg0) { + if (res >= 0) { + if (arg0) POST_WRITE(arg0, struct_oldold_utsname_sz); + } +} + +PRE_SYSCALL(getrlimit)(long resource, void *rlim) {} + +POST_SYSCALL(getrlimit)(long res, long resource, void *rlim) { + if (res >= 0) { + if (rlim) POST_WRITE(rlim, struct_rlimit_sz); + } +} + +PRE_SYSCALL(old_getrlimit)(long resource, void *rlim) {} + +POST_SYSCALL(old_getrlimit)(long res, long resource, void *rlim) { + if (res >= 0) { + if (rlim) POST_WRITE(rlim, struct_rlimit_sz); + } +} + +PRE_SYSCALL(setrlimit)(long resource, void *rlim) {} + +POST_SYSCALL(setrlimit)(long res, long resource, void *rlim) { + if (res >= 0) { + if (rlim) POST_WRITE(rlim, struct_rlimit_sz); + } +} + +#if !SANITIZER_ANDROID +PRE_SYSCALL(prlimit64)(long pid, long resource, const void *new_rlim, + void *old_rlim) { + if (new_rlim) PRE_READ(new_rlim, struct_rlimit64_sz); +} + +POST_SYSCALL(prlimit64)(long res, long pid, long resource, const void *new_rlim, + void *old_rlim) { + if (res >= 0) { + if (old_rlim) POST_WRITE(old_rlim, struct_rlimit64_sz); + } +} +#endif + +PRE_SYSCALL(getrusage)(long who, void *ru) {} + +POST_SYSCALL(getrusage)(long res, long who, void *ru) { + if (res >= 0) { + if (ru) POST_WRITE(ru, struct_rusage_sz); + } +} + +PRE_SYSCALL(umask)(long mask) {} + +POST_SYSCALL(umask)(long res, long mask) {} + +PRE_SYSCALL(msgget)(long key, long msgflg) {} + +POST_SYSCALL(msgget)(long res, long key, long msgflg) {} + +PRE_SYSCALL(msgsnd)(long msqid, void *msgp, long msgsz, long msgflg) { + if (msgp) PRE_READ(msgp, msgsz); +} + +POST_SYSCALL(msgsnd)(long res, long msqid, void *msgp, long msgsz, + long msgflg) {} + +PRE_SYSCALL(msgrcv)(long msqid, void *msgp, long msgsz, long msgtyp, + long msgflg) {} + +POST_SYSCALL(msgrcv)(long res, long msqid, void *msgp, long msgsz, long msgtyp, + long msgflg) { + if (res >= 0) { + if (msgp) POST_WRITE(msgp, res); + } +} + +PRE_SYSCALL(msgctl)(long msqid, long cmd, void *buf) {} + +POST_SYSCALL(msgctl)(long res, long msqid, long cmd, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_msqid_ds_sz); + } +} + +PRE_SYSCALL(semget)(long key, long nsems, long semflg) {} + +POST_SYSCALL(semget)(long res, long key, long nsems, long semflg) {} + +PRE_SYSCALL(semop)(long semid, void *sops, long nsops) {} + +POST_SYSCALL(semop)(long res, long semid, void *sops, long nsops) {} + +PRE_SYSCALL(semctl)(long semid, long semnum, long cmd, void *arg) {} + +POST_SYSCALL(semctl)(long res, long semid, long semnum, long cmd, void *arg) {} + +PRE_SYSCALL(semtimedop)(long semid, void *sops, long nsops, + const void *timeout) { + if (timeout) PRE_READ(timeout, struct_timespec_sz); +} + +POST_SYSCALL(semtimedop)(long res, long semid, void *sops, long nsops, + const void *timeout) {} + +PRE_SYSCALL(shmat)(long shmid, void *shmaddr, long shmflg) {} + +POST_SYSCALL(shmat)(long res, long shmid, void *shmaddr, long shmflg) { + if (res >= 0) { + if (shmaddr) + POST_WRITE(shmaddr, + __sanitizer::internal_strlen((const char *)shmaddr) + 1); + } +} + +PRE_SYSCALL(shmget)(long key, long size, long flag) {} + +POST_SYSCALL(shmget)(long res, long key, long size, long flag) {} + +PRE_SYSCALL(shmdt)(void *shmaddr) {} + +POST_SYSCALL(shmdt)(long res, void *shmaddr) { + if (res >= 0) { + if (shmaddr) + POST_WRITE(shmaddr, + __sanitizer::internal_strlen((const char *)shmaddr) + 1); + } +} + +PRE_SYSCALL(ipc)(long call, long first, long second, long third, void *ptr, + long fifth) {} + +POST_SYSCALL(ipc)(long res, long call, long first, long second, long third, + void *ptr, long fifth) {} + +#if !SANITIZER_ANDROID +PRE_SYSCALL(shmctl)(long shmid, long cmd, void *buf) {} + +POST_SYSCALL(shmctl)(long res, long shmid, long cmd, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, sizeof(__sanitizer_shmid_ds)); + } +} + +PRE_SYSCALL(mq_open)(const void *name, long oflag, long mode, void *attr) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(mq_open)(long res, const void *name, long oflag, long mode, + void *attr) { + if (res >= 0) { + if (attr) POST_WRITE(attr, struct_mq_attr_sz); + } +} + +PRE_SYSCALL(mq_unlink)(const void *name) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(mq_unlink)(long res, const void *name) {} + +PRE_SYSCALL(mq_timedsend)(long mqdes, const void *msg_ptr, long msg_len, + long msg_prio, const void *abs_timeout) { + if (msg_ptr) PRE_READ(msg_ptr, msg_len); + if (abs_timeout) PRE_READ(abs_timeout, struct_timespec_sz); +} + +POST_SYSCALL(mq_timedsend)(long res, long mqdes, const void *msg_ptr, + long msg_len, long msg_prio, + const void *abs_timeout) {} + +PRE_SYSCALL(mq_timedreceive)(long mqdes, void *msg_ptr, long msg_len, + void *msg_prio, const void *abs_timeout) { + if (abs_timeout) PRE_READ(abs_timeout, struct_timespec_sz); +} + +POST_SYSCALL(mq_timedreceive)(long res, long mqdes, void *msg_ptr, long msg_len, + int *msg_prio, const void *abs_timeout) { + if (res >= 0) { + if (msg_ptr) POST_WRITE(msg_ptr, res); + if (msg_prio) POST_WRITE(msg_prio, sizeof(*msg_prio)); + } +} + +PRE_SYSCALL(mq_notify)(long mqdes, const void *notification) { + if (notification) PRE_READ(notification, struct_sigevent_sz); +} + +POST_SYSCALL(mq_notify)(long res, long mqdes, const void *notification) {} + +PRE_SYSCALL(mq_getsetattr)(long mqdes, const void *mqstat, void *omqstat) { + if (mqstat) PRE_READ(mqstat, struct_mq_attr_sz); +} + +POST_SYSCALL(mq_getsetattr)(long res, long mqdes, const void *mqstat, + void *omqstat) { + if (res >= 0) { + if (omqstat) POST_WRITE(omqstat, struct_mq_attr_sz); + } +} +#endif // SANITIZER_ANDROID + +PRE_SYSCALL(pciconfig_iobase)(long which, long bus, long devfn) {} + +POST_SYSCALL(pciconfig_iobase)(long res, long which, long bus, long devfn) {} + +PRE_SYSCALL(pciconfig_read)(long bus, long dfn, long off, long len, void *buf) { +} + +POST_SYSCALL(pciconfig_read)(long res, long bus, long dfn, long off, long len, + void *buf) {} + +PRE_SYSCALL(pciconfig_write)(long bus, long dfn, long off, long len, + void *buf) {} + +POST_SYSCALL(pciconfig_write)(long res, long bus, long dfn, long off, long len, + void *buf) {} + +PRE_SYSCALL(swapon)(const void *specialfile, long swap_flags) { + if (specialfile) + PRE_READ(specialfile, + __sanitizer::internal_strlen((const char *)specialfile) + 1); +} + +POST_SYSCALL(swapon)(long res, const void *specialfile, long swap_flags) {} + +PRE_SYSCALL(swapoff)(const void *specialfile) { + if (specialfile) + PRE_READ(specialfile, + __sanitizer::internal_strlen((const char *)specialfile) + 1); +} + +POST_SYSCALL(swapoff)(long res, const void *specialfile) {} + +PRE_SYSCALL(sysctl)(__sanitizer___sysctl_args *args) { + if (args) { + if (args->name) PRE_READ(args->name, args->nlen * sizeof(*args->name)); + if (args->newval) PRE_READ(args->name, args->newlen); + } +} + +POST_SYSCALL(sysctl)(long res, __sanitizer___sysctl_args *args) { + if (res >= 0) { + if (args && args->oldval && args->oldlenp) { + POST_WRITE(args->oldlenp, sizeof(*args->oldlenp)); + POST_WRITE(args->oldval, *args->oldlenp); + } + } +} + +PRE_SYSCALL(sysinfo)(void *info) {} + +POST_SYSCALL(sysinfo)(long res, void *info) { + if (res >= 0) { + if (info) POST_WRITE(info, struct_sysinfo_sz); + } +} + +PRE_SYSCALL(sysfs)(long option, long arg1, long arg2) {} + +POST_SYSCALL(sysfs)(long res, long option, long arg1, long arg2) {} + +PRE_SYSCALL(syslog)(long type, void *buf, long len) {} + +POST_SYSCALL(syslog)(long res, long type, void *buf, long len) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(uselib)(const void *library) { + if (library) + PRE_READ(library, __sanitizer::internal_strlen((const char *)library) + 1); +} + +POST_SYSCALL(uselib)(long res, const void *library) {} + +PRE_SYSCALL(ni_syscall)() {} + +POST_SYSCALL(ni_syscall)(long res) {} + +PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { +#if defined(__i386) || defined (__x86_64) + if (data) { + if (request == ptrace_setregs) { + PRE_READ((void *)data, struct_user_regs_struct_sz); + } else if (request == ptrace_setfpregs) { + PRE_READ((void *)data, struct_user_fpregs_struct_sz); + } else if (request == ptrace_setfpxregs) { + PRE_READ((void *)data, struct_user_fpxregs_struct_sz); + } else if (request == ptrace_setsiginfo) { + PRE_READ((void *)data, siginfo_t_sz); + } else if (request == ptrace_setregset) { + __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + PRE_READ(iov->iov_base, iov->iov_len); + } + } +#endif +} + +POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) { +#if defined(__i386) || defined (__x86_64) + if (res >= 0 && data) { + // Note that this is different from the interceptor in + // sanitizer_common_interceptors.inc. + // PEEK* requests return resulting values through data pointer. + if (request == ptrace_getregs) { + POST_WRITE((void *)data, struct_user_regs_struct_sz); + } else if (request == ptrace_getfpregs) { + POST_WRITE((void *)data, struct_user_fpregs_struct_sz); + } else if (request == ptrace_getfpxregs) { + POST_WRITE((void *)data, struct_user_fpxregs_struct_sz); + } else if (request == ptrace_getsiginfo) { + POST_WRITE((void *)data, siginfo_t_sz); + } else if (request == ptrace_getregset) { + __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + POST_WRITE(iov->iov_base, iov->iov_len); + } else if (request == ptrace_peekdata || request == ptrace_peektext || + request == ptrace_peekuser) { + POST_WRITE((void *)data, sizeof(void *)); + } + } +#endif +} + +PRE_SYSCALL(add_key)(const void *_type, const void *_description, + const void *_payload, long plen, long destringid) { + if (_type) + PRE_READ(_type, __sanitizer::internal_strlen((const char *)_type) + 1); + if (_description) + PRE_READ(_description, + __sanitizer::internal_strlen((const char *)_description) + 1); +} + +POST_SYSCALL(add_key)(long res, const void *_type, const void *_description, + const void *_payload, long plen, long destringid) {} + +PRE_SYSCALL(request_key)(const void *_type, const void *_description, + const void *_callout_info, long destringid) { + if (_type) + PRE_READ(_type, __sanitizer::internal_strlen((const char *)_type) + 1); + if (_description) + PRE_READ(_description, + __sanitizer::internal_strlen((const char *)_description) + 1); + if (_callout_info) + PRE_READ(_callout_info, + __sanitizer::internal_strlen((const char *)_callout_info) + 1); +} + +POST_SYSCALL(request_key)(long res, const void *_type, const void *_description, + const void *_callout_info, long destringid) {} + +PRE_SYSCALL(keyctl)(long cmd, long arg2, long arg3, long arg4, long arg5) {} + +POST_SYSCALL(keyctl)(long res, long cmd, long arg2, long arg3, long arg4, + long arg5) {} + +PRE_SYSCALL(ioprio_set)(long which, long who, long ioprio) {} + +POST_SYSCALL(ioprio_set)(long res, long which, long who, long ioprio) {} + +PRE_SYSCALL(ioprio_get)(long which, long who) {} + +POST_SYSCALL(ioprio_get)(long res, long which, long who) {} + +PRE_SYSCALL(set_mempolicy)(long mode, void *nmask, long maxnode) {} + +POST_SYSCALL(set_mempolicy)(long res, long mode, void *nmask, long maxnode) { + if (res >= 0) { + if (nmask) POST_WRITE(nmask, sizeof(long)); + } +} + +PRE_SYSCALL(migrate_pages)(long pid, long maxnode, const void *from, + const void *to) { + if (from) PRE_READ(from, sizeof(long)); + if (to) PRE_READ(to, sizeof(long)); +} + +POST_SYSCALL(migrate_pages)(long res, long pid, long maxnode, const void *from, + const void *to) {} + +PRE_SYSCALL(move_pages)(long pid, long nr_pages, const void **pages, + const int *nodes, int *status, long flags) { + if (pages) PRE_READ(pages, nr_pages * sizeof(*pages)); + if (nodes) PRE_READ(nodes, nr_pages * sizeof(*nodes)); +} + +POST_SYSCALL(move_pages)(long res, long pid, long nr_pages, const void **pages, + const int *nodes, int *status, long flags) { + if (res >= 0) { + if (status) POST_WRITE(status, nr_pages * sizeof(*status)); + } +} + +PRE_SYSCALL(mbind)(long start, long len, long mode, void *nmask, long maxnode, + long flags) {} + +POST_SYSCALL(mbind)(long res, long start, long len, long mode, void *nmask, + long maxnode, long flags) { + if (res >= 0) { + if (nmask) POST_WRITE(nmask, sizeof(long)); + } +} + +PRE_SYSCALL(get_mempolicy)(void *policy, void *nmask, long maxnode, long addr, + long flags) {} + +POST_SYSCALL(get_mempolicy)(long res, void *policy, void *nmask, long maxnode, + long addr, long flags) { + if (res >= 0) { + if (policy) POST_WRITE(policy, sizeof(int)); + if (nmask) POST_WRITE(nmask, sizeof(long)); + } +} + +PRE_SYSCALL(inotify_init)() {} + +POST_SYSCALL(inotify_init)(long res) {} + +PRE_SYSCALL(inotify_init1)(long flags) {} + +POST_SYSCALL(inotify_init1)(long res, long flags) {} + +PRE_SYSCALL(inotify_add_watch)(long fd, const void *path, long mask) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(inotify_add_watch)(long res, long fd, const void *path, + long mask) {} + +PRE_SYSCALL(inotify_rm_watch)(long fd, long wd) {} + +POST_SYSCALL(inotify_rm_watch)(long res, long fd, long wd) {} + +PRE_SYSCALL(spu_run)(long fd, void *unpc, void *ustatus) {} + +POST_SYSCALL(spu_run)(long res, long fd, unsigned *unpc, unsigned *ustatus) { + if (res >= 0) { + if (unpc) POST_WRITE(unpc, sizeof(*unpc)); + if (ustatus) POST_WRITE(ustatus, sizeof(*ustatus)); + } +} + +PRE_SYSCALL(spu_create)(const void *name, long flags, long mode, long fd) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(spu_create)(long res, const void *name, long flags, long mode, + long fd) {} + +PRE_SYSCALL(mknodat)(long dfd, const void *filename, long mode, long dev) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(mknodat)(long res, long dfd, const void *filename, long mode, + long dev) {} + +PRE_SYSCALL(mkdirat)(long dfd, const void *pathname, long mode) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(mkdirat)(long res, long dfd, const void *pathname, long mode) {} + +PRE_SYSCALL(unlinkat)(long dfd, const void *pathname, long flag) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(unlinkat)(long res, long dfd, const void *pathname, long flag) {} + +PRE_SYSCALL(symlinkat)(const void *oldname, long newdfd, const void *newname) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(symlinkat)(long res, const void *oldname, long newdfd, + const void *newname) {} + +PRE_SYSCALL(linkat)(long olddfd, const void *oldname, long newdfd, + const void *newname, long flags) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(linkat)(long res, long olddfd, const void *oldname, long newdfd, + const void *newname, long flags) {} + +PRE_SYSCALL(renameat)(long olddfd, const void *oldname, long newdfd, + const void *newname) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(renameat)(long res, long olddfd, const void *oldname, long newdfd, + const void *newname) {} + +PRE_SYSCALL(futimesat)(long dfd, const void *filename, void *utimes) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(futimesat)(long res, long dfd, const void *filename, + void *utimes) { + if (res >= 0) { + if (utimes) POST_WRITE(utimes, timeval_sz); + } +} + +PRE_SYSCALL(faccessat)(long dfd, const void *filename, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(faccessat)(long res, long dfd, const void *filename, long mode) {} + +PRE_SYSCALL(fchmodat)(long dfd, const void *filename, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(fchmodat)(long res, long dfd, const void *filename, long mode) {} + +PRE_SYSCALL(fchownat)(long dfd, const void *filename, long user, long group, + long flag) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(fchownat)(long res, long dfd, const void *filename, long user, + long group, long flag) {} + +PRE_SYSCALL(openat)(long dfd, const void *filename, long flags, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(openat)(long res, long dfd, const void *filename, long flags, + long mode) {} + +PRE_SYSCALL(newfstatat)(long dfd, const void *filename, void *statbuf, + long flag) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(newfstatat)(long res, long dfd, const void *filename, + void *statbuf, long flag) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz); + } +} + +PRE_SYSCALL(fstatat64)(long dfd, const void *filename, void *statbuf, + long flag) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(fstatat64)(long res, long dfd, const void *filename, void *statbuf, + long flag) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz); + } +} + +PRE_SYSCALL(readlinkat)(long dfd, const void *path, void *buf, long bufsiz) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(readlinkat)(long res, long dfd, const void *path, void *buf, + long bufsiz) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(utimensat)(long dfd, const void *filename, void *utimes, + long flags) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(utimensat)(long res, long dfd, const void *filename, void *utimes, + long flags) { + if (res >= 0) { + if (utimes) POST_WRITE(utimes, struct_timespec_sz); + } +} + +PRE_SYSCALL(unshare)(long unshare_flags) {} + +POST_SYSCALL(unshare)(long res, long unshare_flags) {} + +PRE_SYSCALL(splice)(long fd_in, void *off_in, long fd_out, void *off_out, + long len, long flags) {} + +POST_SYSCALL(splice)(long res, long fd_in, void *off_in, long fd_out, + void *off_out, long len, long flags) { + if (res >= 0) { + if (off_in) POST_WRITE(off_in, sizeof(long long)); + if (off_out) POST_WRITE(off_out, sizeof(long long)); + } +} + +PRE_SYSCALL(vmsplice)(long fd, const __sanitizer_iovec *iov, long nr_segs, + long flags) {} + +POST_SYSCALL(vmsplice)(long res, long fd, const __sanitizer_iovec *iov, + long nr_segs, long flags) { + if (res >= 0) { + if (iov) kernel_read_iovec(iov, nr_segs, res); + } +} + +PRE_SYSCALL(tee)(long fdin, long fdout, long len, long flags) {} + +POST_SYSCALL(tee)(long res, long fdin, long fdout, long len, long flags) {} + +PRE_SYSCALL(get_robust_list)(long pid, void *head_ptr, void *len_ptr) {} + +POST_SYSCALL(get_robust_list)(long res, long pid, void *head_ptr, + void *len_ptr) {} + +PRE_SYSCALL(set_robust_list)(void *head, long len) {} + +POST_SYSCALL(set_robust_list)(long res, void *head, long len) {} + +PRE_SYSCALL(getcpu)(void *cpu, void *node, void *cache) {} + +POST_SYSCALL(getcpu)(long res, void *cpu, void *node, void *cache) { + if (res >= 0) { + if (cpu) POST_WRITE(cpu, sizeof(unsigned)); + if (node) POST_WRITE(node, sizeof(unsigned)); + // The third argument to this system call is nowadays unused. + } +} + +PRE_SYSCALL(signalfd)(long ufd, void *user_mask, long sizemask) {} + +POST_SYSCALL(signalfd)(long res, long ufd, kernel_sigset_t *user_mask, + long sizemask) { + if (res >= 0) { + if (user_mask) POST_WRITE(user_mask, sizemask); + } +} + +PRE_SYSCALL(signalfd4)(long ufd, void *user_mask, long sizemask, long flags) {} + +POST_SYSCALL(signalfd4)(long res, long ufd, kernel_sigset_t *user_mask, + long sizemask, long flags) { + if (res >= 0) { + if (user_mask) POST_WRITE(user_mask, sizemask); + } +} + +PRE_SYSCALL(timerfd_create)(long clockid, long flags) {} + +POST_SYSCALL(timerfd_create)(long res, long clockid, long flags) {} + +PRE_SYSCALL(timerfd_settime)(long ufd, long flags, const void *utmr, + void *otmr) { + if (utmr) PRE_READ(utmr, struct_itimerspec_sz); +} + +POST_SYSCALL(timerfd_settime)(long res, long ufd, long flags, const void *utmr, + void *otmr) { + if (res >= 0) { + if (otmr) POST_WRITE(otmr, struct_itimerspec_sz); + } +} + +PRE_SYSCALL(timerfd_gettime)(long ufd, void *otmr) {} + +POST_SYSCALL(timerfd_gettime)(long res, long ufd, void *otmr) { + if (res >= 0) { + if (otmr) POST_WRITE(otmr, struct_itimerspec_sz); + } +} + +PRE_SYSCALL(eventfd)(long count) {} + +POST_SYSCALL(eventfd)(long res, long count) {} + +PRE_SYSCALL(eventfd2)(long count, long flags) {} + +POST_SYSCALL(eventfd2)(long res, long count, long flags) {} + +PRE_SYSCALL(old_readdir)(long arg0, void *arg1, long arg2) {} + +POST_SYSCALL(old_readdir)(long res, long arg0, void *arg1, long arg2) { + // Missing definition of 'struct old_linux_dirent'. +} + +PRE_SYSCALL(pselect6)(long arg0, __sanitizer___kernel_fd_set *arg1, + __sanitizer___kernel_fd_set *arg2, + __sanitizer___kernel_fd_set *arg3, void *arg4, + void *arg5) {} + +POST_SYSCALL(pselect6)(long res, long arg0, __sanitizer___kernel_fd_set *arg1, + __sanitizer___kernel_fd_set *arg2, + __sanitizer___kernel_fd_set *arg3, void *arg4, + void *arg5) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(*arg2)); + if (arg3) POST_WRITE(arg3, sizeof(*arg3)); + if (arg4) POST_WRITE(arg4, struct_timespec_sz); + } +} + +PRE_SYSCALL(ppoll)(__sanitizer_pollfd *arg0, long arg1, void *arg2, + const kernel_sigset_t *arg3, long arg4) { + if (arg3) PRE_READ(arg3, arg4); +} + +POST_SYSCALL(ppoll)(long res, __sanitizer_pollfd *arg0, long arg1, void *arg2, + const void *arg3, long arg4) { + if (res >= 0) { + if (arg0) POST_WRITE(arg0, sizeof(*arg0)); + if (arg2) POST_WRITE(arg2, struct_timespec_sz); + } +} + +PRE_SYSCALL(syncfs)(long fd) {} + +POST_SYSCALL(syncfs)(long res, long fd) {} + +PRE_SYSCALL(perf_event_open)(__sanitizer_perf_event_attr *attr_uptr, long pid, + long cpu, long group_fd, long flags) { + if (attr_uptr) PRE_READ(attr_uptr, attr_uptr->size); +} + +POST_SYSCALL(perf_event_open)(long res, __sanitizer_perf_event_attr *attr_uptr, + long pid, long cpu, long group_fd, long flags) {} + +PRE_SYSCALL(mmap_pgoff)(long addr, long len, long prot, long flags, long fd, + long pgoff) {} + +POST_SYSCALL(mmap_pgoff)(long res, long addr, long len, long prot, long flags, + long fd, long pgoff) {} + +PRE_SYSCALL(old_mmap)(void *arg) {} + +POST_SYSCALL(old_mmap)(long res, void *arg) {} + +PRE_SYSCALL(name_to_handle_at)(long dfd, const void *name, void *handle, + void *mnt_id, long flag) {} + +POST_SYSCALL(name_to_handle_at)(long res, long dfd, const void *name, + void *handle, void *mnt_id, long flag) {} + +PRE_SYSCALL(open_by_handle_at)(long mountdirfd, void *handle, long flags) {} + +POST_SYSCALL(open_by_handle_at)(long res, long mountdirfd, void *handle, + long flags) {} + +PRE_SYSCALL(setns)(long fd, long nstype) {} + +POST_SYSCALL(setns)(long res, long fd, long nstype) {} + +PRE_SYSCALL(process_vm_readv)(long pid, const __sanitizer_iovec *lvec, + long liovcnt, const void *rvec, long riovcnt, + long flags) {} + +POST_SYSCALL(process_vm_readv)(long res, long pid, + const __sanitizer_iovec *lvec, long liovcnt, + const void *rvec, long riovcnt, long flags) { + if (res >= 0) { + if (lvec) kernel_write_iovec(lvec, liovcnt, res); + } +} + +PRE_SYSCALL(process_vm_writev)(long pid, const __sanitizer_iovec *lvec, + long liovcnt, const void *rvec, long riovcnt, + long flags) {} + +POST_SYSCALL(process_vm_writev)(long res, long pid, + const __sanitizer_iovec *lvec, long liovcnt, + const void *rvec, long riovcnt, long flags) { + if (res >= 0) { + if (lvec) kernel_read_iovec(lvec, liovcnt, res); + } +} + +PRE_SYSCALL(fork)() { + COMMON_SYSCALL_PRE_FORK(); +} + +POST_SYSCALL(fork)(long res) { + COMMON_SYSCALL_POST_FORK(res); +} + +PRE_SYSCALL(vfork)() { + COMMON_SYSCALL_PRE_FORK(); +} + +POST_SYSCALL(vfork)(long res) { + COMMON_SYSCALL_POST_FORK(res); +} } // extern "C" #undef PRE_SYSCALL @@ -146,3 +2783,5 @@ POST_SYSCALL(waitpid)(long res, int pid, int *status, int options) { #undef POST_SYSCALL #undef POST_READ #undef POST_WRITE + +#endif // SANITIZER_LINUX diff --git a/lib/sanitizer_common/sanitizer_coverage.cc b/lib/sanitizer_common/sanitizer_coverage.cc new file mode 100644 index 000000000000..9e7a0f8b95fb --- /dev/null +++ b/lib/sanitizer_common/sanitizer_coverage.cc @@ -0,0 +1,113 @@ +//===-- sanitizer_coverage.cc ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Sanitizer Coverage. +// This file implements run-time support for a poor man's coverage tool. +// +// Compiler instrumentation: +// For every function F the compiler injects the following code: +// if (*Guard) { +// __sanitizer_cov(&F); +// *Guard = 1; +// } +// It's fine to call __sanitizer_cov more than once for a given function. +// +// Run-time: +// - __sanitizer_cov(pc): record that we've executed a given PC. +// - __sanitizer_cov_dump: dump the coverage data to disk. +// For every module of the current process that has coverage data +// this will create a file module_name.PID.sancov. The file format is simple: +// it's just a sorted sequence of 4-byte offsets in the module. +// +// Eventually, this coverage implementation should be obsoleted by a more +// powerful general purpose Clang/LLVM coverage instrumentation. +// Consider this implementation as prototype. +// +// FIXME: support (or at least test with) dlclose. +//===----------------------------------------------------------------------===// + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_mutex.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_flags.h" + +struct CovData { + BlockingMutex mu; + InternalMmapVector<uptr> v; +}; + +static uptr cov_data_placeholder[sizeof(CovData) / sizeof(uptr)]; +COMPILER_CHECK(sizeof(cov_data_placeholder) >= sizeof(CovData)); +static CovData *cov_data = reinterpret_cast<CovData*>(cov_data_placeholder); + +namespace __sanitizer { + +// Simply add the pc into the vector under lock. If the function is called more +// than once for a given PC it will be inserted multiple times, which is fine. +static void CovAdd(uptr pc) { + BlockingMutexLock lock(&cov_data->mu); + cov_data->v.push_back(pc); +} + +static inline bool CompareLess(const uptr &a, const uptr &b) { + return a < b; +} + +// Dump the coverage on disk. +void CovDump() { +#if !SANITIZER_WINDOWS + BlockingMutexLock lock(&cov_data->mu); + InternalMmapVector<uptr> &v = cov_data->v; + InternalSort(&v, v.size(), CompareLess); + InternalMmapVector<u32> offsets(v.size()); + const uptr *vb = v.data(); + const uptr *ve = vb + v.size(); + MemoryMappingLayout proc_maps(/*cache_enabled*/false); + uptr mb, me, off, prot; + InternalScopedBuffer<char> module(4096); + InternalScopedBuffer<char> path(4096 * 2); + for (int i = 0; + proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot); + i++) { + if ((prot & MemoryMappingLayout::kProtectionExecute) == 0) + continue; + if (vb >= ve) break; + if (mb <= *vb && *vb < me) { + offsets.clear(); + const uptr *old_vb = vb; + CHECK_LE(off, *vb); + for (; vb < ve && *vb < me; vb++) { + uptr diff = *vb - (i ? mb : 0) + off; + CHECK_LE(diff, 0xffffffffU); + offsets.push_back(static_cast<u32>(diff)); + } + char *module_name = StripModuleName(module.data()); + internal_snprintf((char *)path.data(), path.size(), "%s.%zd.sancov", + module_name, internal_getpid()); + InternalFree(module_name); + uptr fd = OpenFile(path.data(), true); + internal_write(fd, offsets.data(), offsets.size() * sizeof(u32)); + internal_close(fd); + if (common_flags()->verbosity) + Report(" CovDump: %s: %zd PCs written\n", path.data(), vb - old_vb); + } + } +#endif // !SANITIZER_WINDOWS +} + +} // namespace __sanitizer + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(void *pc) { + CovAdd(reinterpret_cast<uptr>(pc)); +} +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } +} // extern "C" diff --git a/lib/sanitizer_common/sanitizer_flags.cc b/lib/sanitizer_common/sanitizer_flags.cc index b7218e5ad212..924ed4bc014f 100644 --- a/lib/sanitizer_common/sanitizer_flags.cc +++ b/lib/sanitizer_common/sanitizer_flags.cc @@ -18,15 +18,42 @@ namespace __sanitizer { -CommonFlags common_flags_dont_use_directly; +void SetCommonFlagDefaults() { + CommonFlags *f = common_flags(); + f->symbolize = true; + f->external_symbolizer_path = ""; + f->strip_path_prefix = ""; + f->fast_unwind_on_fatal = false; + f->fast_unwind_on_malloc = true; + f->handle_ioctl = false; + f->malloc_context_size = 1; + f->log_path = "stderr"; + f->verbosity = 0; + f->detect_leaks = false; + f->leak_check_at_exit = true; + f->allocator_may_return_null = false; + f->print_summary = true; +} void ParseCommonFlagsFromString(const char *str) { CommonFlags *f = common_flags(); - ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); + ParseFlag(str, &f->symbolize, "symbolize"); + ParseFlag(str, &f->external_symbolizer_path, "external_symbolizer_path"); ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix"); ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal"); ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc"); - ParseFlag(str, &f->symbolize, "symbolize"); + ParseFlag(str, &f->handle_ioctl, "handle_ioctl"); + ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); + ParseFlag(str, &f->log_path, "log_path"); + ParseFlag(str, &f->verbosity, "verbosity"); + ParseFlag(str, &f->detect_leaks, "detect_leaks"); + ParseFlag(str, &f->leak_check_at_exit, "leak_check_at_exit"); + ParseFlag(str, &f->allocator_may_return_null, "allocator_may_return_null"); + ParseFlag(str, &f->print_summary, "print_summary"); + + // Do a sanity check for certain flags. + if (f->malloc_context_size < 1) + f->malloc_context_size = 1; } static bool GetFlagValue(const char *env, const char *name, @@ -97,7 +124,7 @@ void ParseFlag(const char *env, int *flag, const char *name) { int value_length; if (!GetFlagValue(env, name, &value, &value_length)) return; - *flag = internal_atoll(value); + *flag = static_cast<int>(internal_atoll(value)); } static LowLevelAllocator allocator_for_flags; diff --git a/lib/sanitizer_common/sanitizer_flags.h b/lib/sanitizer_common/sanitizer_flags.h index e97ce6a87188..9461dff80330 100644 --- a/lib/sanitizer_common/sanitizer_flags.h +++ b/lib/sanitizer_common/sanitizer_flags.h @@ -33,16 +33,34 @@ struct CommonFlags { bool fast_unwind_on_fatal; // Use fast (frame-pointer-based) unwinder on malloc/free (if available). bool fast_unwind_on_malloc; + // Intercept and handle ioctl requests. + bool handle_ioctl; // Max number of stack frames kept for each allocation/deallocation. int malloc_context_size; + // Write logs to "log_path.pid". + // The special values are "stdout" and "stderr". + // The default is "stderr". + const char *log_path; + // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). + int verbosity; + // Enable memory leak detection. + bool detect_leaks; + // Invoke leak checking in an atexit handler. Has no effect if + // detect_leaks=false, or if __lsan_do_leak_check() is called before the + // handler has a chance to run. + bool leak_check_at_exit; + // If false, the allocator will crash instead of returning 0 on out-of-memory. + bool allocator_may_return_null; + // If false, disable printing error summaries in addition to error reports. + bool print_summary; }; -extern CommonFlags common_flags_dont_use_directly; - inline CommonFlags *common_flags() { - return &common_flags_dont_use_directly; + static CommonFlags f; + return &f; } +void SetCommonFlagDefaults(); void ParseCommonFlagsFromString(const char *str); } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h index 9a7d374bf5ad..daa724be06f1 100644 --- a/lib/sanitizer_common/sanitizer_internal_defs.h +++ b/lib/sanitizer_common/sanitizer_internal_defs.h @@ -15,9 +15,10 @@ #include "sanitizer_platform.h" +// Only use SANITIZER_*ATTRIBUTE* before the function return type! #if SANITIZER_WINDOWS -// FIXME find out what we need on Windows. __declspec(dllexport) ? -# define SANITIZER_INTERFACE_ATTRIBUTE +# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport) +// FIXME find out what we need on Windows, if anything. # define SANITIZER_WEAK_ATTRIBUTE #elif defined(SANITIZER_GO) # define SANITIZER_INTERFACE_ATTRIBUTE @@ -33,6 +34,12 @@ # define SANITIZER_SUPPORTS_WEAK_HOOKS 0 #endif +#if __LP64__ || defined(_WIN64) +# define SANITIZER_WORDSIZE 64 +#else +# define SANITIZER_WORDSIZE 32 +#endif + // GCC does not understand __has_feature #if !defined(__has_feature) # define __has_feature(x) 0 @@ -78,29 +85,37 @@ typedef u64 OFF_T; typedef uptr OFF_T; #endif typedef u64 OFF64_T; + +#if (SANITIZER_WORDSIZE == 64) || SANITIZER_MAC +typedef uptr operator_new_size_type; +#else +typedef u32 operator_new_size_type; +#endif } // namespace __sanitizer extern "C" { // Tell the tools to write their reports to "path.<pid>" instead of stderr. - void __sanitizer_set_report_path(const char *path) - SANITIZER_INTERFACE_ATTRIBUTE; - - // Tell the tools to write their reports to given file descriptor instead of - // stderr. - void __sanitizer_set_report_fd(int fd) - SANITIZER_INTERFACE_ATTRIBUTE; + // The special values are "stdout" and "stderr". + SANITIZER_INTERFACE_ATTRIBUTE + void __sanitizer_set_report_path(const char *path); // Notify the tools that the sandbox is going to be turned on. The reserved // parameter will be used in the future to hold a structure with functions // that the tools may call to bypass the sandbox. - void __sanitizer_sandbox_on_notify(void *reserved) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + void __sanitizer_sandbox_on_notify(void *reserved); // This function is called by the tool when it has just finished reporting // an error. 'error_summary' is a one-line string that summarizes // the error message. This function can be overridden by the client. - void __sanitizer_report_error_summary(const char *error_summary) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + void __sanitizer_report_error_summary(const char *error_summary); + + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(void *pc); + SANITIZER_INTERFACE_ATTRIBUTE + void __sanitizer_annotate_contiguous_container(void *beg, void *end, + void *old_mid, void *new_mid); } // extern "C" @@ -132,6 +147,8 @@ using namespace __sanitizer; // NOLINT #else // _MSC_VER # define ALWAYS_INLINE inline __attribute__((always_inline)) # define ALIAS(x) __attribute__((alias(x))) +// Please only use the ALIGNED macro before the type. +// Using ALIGNED after the variable declaration is not portable! # define ALIGNED(x) __attribute__((aligned(x))) # define FORMAT(f, a) __attribute__((format(printf, f, a))) # define NOINLINE __attribute__((noinline)) @@ -150,6 +167,14 @@ using namespace __sanitizer; // NOLINT # endif #endif // _MSC_VER +// Unaligned versions of basic types. +typedef ALIGNED(1) u16 uu16; +typedef ALIGNED(1) u32 uu32; +typedef ALIGNED(1) u64 uu64; +typedef ALIGNED(1) s16 us16; +typedef ALIGNED(1) s32 us32; +typedef ALIGNED(1) s64 us64; + #if SANITIZER_WINDOWS typedef unsigned long DWORD; // NOLINT typedef DWORD thread_return_t; @@ -160,15 +185,12 @@ typedef void* thread_return_t; #endif // _WIN32 typedef thread_return_t (THREAD_CALLING_CONV *thread_callback_t)(void* arg); -#if __LP64__ || defined(_WIN64) -# define SANITIZER_WORDSIZE 64 -#else -# define SANITIZER_WORDSIZE 32 -#endif - // NOTE: Functions below must be defined in each run-time. namespace __sanitizer { void NORETURN Die(); + +// FIXME: No, this shouldn't be in the sanitizer interface. +SANITIZER_INTERFACE_ATTRIBUTE void NORETURN CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_libc.cc b/lib/sanitizer_common/sanitizer_libc.cc index 20c03c4474a1..72ddf0fd3fbe 100644 --- a/lib/sanitizer_common/sanitizer_libc.cc +++ b/lib/sanitizer_common/sanitizer_libc.cc @@ -10,6 +10,7 @@ // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. See sanitizer_libc.h for details. //===----------------------------------------------------------------------===// +#include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" #include "sanitizer_libc.h" @@ -124,6 +125,13 @@ char* internal_strchr(const char *s, int c) { } } +char *internal_strchrnul(const char *s, int c) { + char *res = internal_strchr(s, c); + if (!res) + res = (char*)s + internal_strlen(s); + return res; +} + char *internal_strrchr(const char *s, int c) { const char *res = 0; for (uptr i = 0; s[i]; i++) { @@ -151,8 +159,7 @@ char *internal_strncpy(char *dst, const char *src, uptr n) { uptr i; for (i = 0; i < n && src[i]; i++) dst[i] = src[i]; - for (; i < n; i++) - dst[i] = '\0'; + internal_memset(dst + i, '\0', n - i); return dst; } diff --git a/lib/sanitizer_common/sanitizer_libc.h b/lib/sanitizer_common/sanitizer_libc.h index 82d809a0305a..187a714a3b2b 100644 --- a/lib/sanitizer_common/sanitizer_libc.h +++ b/lib/sanitizer_common/sanitizer_libc.h @@ -32,6 +32,7 @@ void *internal_memmove(void *dest, const void *src, uptr n); // Should not be used in performance-critical places. void *internal_memset(void *s, int c, uptr n); char* internal_strchr(const char *s, int c); +char *internal_strchrnul(const char *s, int c); int internal_strcmp(const char *s1, const char *s2); uptr internal_strcspn(const char *s, const char *reject); char *internal_strdup(const char *s); diff --git a/lib/sanitizer_common/sanitizer_libignore.cc b/lib/sanitizer_common/sanitizer_libignore.cc new file mode 100644 index 000000000000..0f193a130253 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_libignore.cc @@ -0,0 +1,105 @@ +//===-- sanitizer_libignore.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_libignore.h" +#include "sanitizer_flags.h" +#include "sanitizer_procmaps.h" + +namespace __sanitizer { + +LibIgnore::LibIgnore(LinkerInitialized) { +} + +void LibIgnore::Init(const SuppressionContext &supp) { + BlockingMutexLock lock(&mutex_); + CHECK_EQ(count_, 0); + const uptr n = supp.SuppressionCount(); + for (uptr i = 0; i < n; i++) { + const Suppression *s = supp.SuppressionAt(i); + if (s->type != SuppressionLib) + continue; + if (count_ >= kMaxLibs) { + Report("%s: too many called_from_lib suppressions (max: %d)\n", + SanitizerToolName, kMaxLibs); + Die(); + } + Lib *lib = &libs_[count_++]; + lib->templ = internal_strdup(s->templ); + lib->name = 0; + lib->loaded = false; + } +} + +void LibIgnore::OnLibraryLoaded(const char *name) { + BlockingMutexLock lock(&mutex_); + // Try to match suppressions with symlink target. + InternalScopedBuffer<char> buf(4096); + if (name != 0 && internal_readlink(name, buf.data(), buf.size() - 1) > 0 && + buf.data()[0]) { + for (uptr i = 0; i < count_; i++) { + Lib *lib = &libs_[i]; + if (!lib->loaded && lib->real_name == 0 && + TemplateMatch(lib->templ, name)) + lib->real_name = internal_strdup(buf.data()); + } + } + + // Scan suppressions list and find newly loaded and unloaded libraries. + MemoryMappingLayout proc_maps(/*cache_enabled*/false); + InternalScopedBuffer<char> module(4096); + for (uptr i = 0; i < count_; i++) { + Lib *lib = &libs_[i]; + bool loaded = false; + proc_maps.Reset(); + uptr b, e, off, prot; + while (proc_maps.Next(&b, &e, &off, module.data(), module.size(), &prot)) { + if ((prot & MemoryMappingLayout::kProtectionExecute) == 0) + continue; + if (TemplateMatch(lib->templ, module.data()) || + (lib->real_name != 0 && + internal_strcmp(lib->real_name, module.data()) == 0)) { + if (loaded) { + Report("%s: called_from_lib suppression '%s' is matched against" + " 2 libraries: '%s' and '%s'\n", + SanitizerToolName, lib->templ, lib->name, module.data()); + Die(); + } + loaded = true; + if (lib->loaded) + continue; + if (common_flags()->verbosity) + Report("Matched called_from_lib suppression '%s' against library" + " '%s'\n", lib->templ, module.data()); + lib->loaded = true; + lib->name = internal_strdup(module.data()); + const uptr idx = atomic_load(&loaded_count_, memory_order_relaxed); + code_ranges_[idx].begin = b; + code_ranges_[idx].end = e; + atomic_store(&loaded_count_, idx + 1, memory_order_release); + } + } + if (lib->loaded && !loaded) { + Report("%s: library '%s' that was matched against called_from_lib" + " suppression '%s' is unloaded\n", + SanitizerToolName, lib->name, lib->templ); + Die(); + } + } +} + +void LibIgnore::OnLibraryUnloaded() { + OnLibraryLoaded(0); +} + +} // namespace __sanitizer + +#endif // #if SANITIZER_LINUX diff --git a/lib/sanitizer_common/sanitizer_libignore.h b/lib/sanitizer_common/sanitizer_libignore.h new file mode 100644 index 000000000000..8e1d584d8e3c --- /dev/null +++ b/lib/sanitizer_common/sanitizer_libignore.h @@ -0,0 +1,84 @@ +//===-- sanitizer_libignore.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// LibIgnore allows to ignore all interceptors called from a particular set +// of dynamic libraries. LibIgnore remembers all "called_from_lib" suppressions +// from the provided SuppressionContext; finds code ranges for the libraries; +// and checks whether the provided PC value belongs to the code ranges. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_LIBIGNORE_H +#define SANITIZER_LIBIGNORE_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_common.h" +#include "sanitizer_suppressions.h" +#include "sanitizer_atomic.h" +#include "sanitizer_mutex.h" + +namespace __sanitizer { + +class LibIgnore { + public: + explicit LibIgnore(LinkerInitialized); + + // Fetches all "called_from_lib" suppressions from the SuppressionContext. + void Init(const SuppressionContext &supp); + + // Must be called after a new dynamic library is loaded. + void OnLibraryLoaded(const char *name); + + // Must be called after a dynamic library is unloaded. + void OnLibraryUnloaded(); + + // Checks whether the provided PC belongs to one of the ignored libraries. + bool IsIgnored(uptr pc) const; + + private: + struct Lib { + char *templ; + char *name; + char *real_name; // target of symlink + bool loaded; + }; + + struct LibCodeRange { + uptr begin; + uptr end; + }; + + static const uptr kMaxLibs = 128; + + // Hot part: + atomic_uintptr_t loaded_count_; + LibCodeRange code_ranges_[kMaxLibs]; + + // Cold part: + BlockingMutex mutex_; + uptr count_; + Lib libs_[kMaxLibs]; + + // Disallow copying of LibIgnore objects. + LibIgnore(const LibIgnore&); // not implemented + void operator = (const LibIgnore&); // not implemented +}; + +inline bool LibIgnore::IsIgnored(uptr pc) const { + const uptr n = atomic_load(&loaded_count_, memory_order_acquire); + for (uptr i = 0; i < n; i++) { + if (pc >= code_ranges_[i].begin && pc < code_ranges_[i].end) + return true; + } + return false; +} + +} // namespace __sanitizer + +#endif // SANITIZER_LIBIGNORE_H diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc index 6e234e5f1e1d..b98ad0ab4804 100644 --- a/lib/sanitizer_common/sanitizer_linux.cc +++ b/lib/sanitizer_common/sanitizer_linux.cc @@ -29,6 +29,9 @@ #include <dlfcn.h> #include <errno.h> #include <fcntl.h> +#if !SANITIZER_ANDROID +#include <link.h> +#endif #include <pthread.h> #include <sched.h> #include <sys/mman.h> @@ -76,14 +79,15 @@ namespace __sanitizer { uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, u64 offset) { #if SANITIZER_LINUX_USES_64BIT_SYSCALLS - return internal_syscall(__NR_mmap, addr, length, prot, flags, fd, offset); + return internal_syscall(__NR_mmap, (uptr)addr, length, prot, flags, fd, + offset); #else return internal_syscall(__NR_mmap2, addr, length, prot, flags, fd, offset); #endif } uptr internal_munmap(void *addr, uptr length) { - return internal_syscall(__NR_munmap, addr, length); + return internal_syscall(__NR_munmap, (uptr)addr, length); } uptr internal_close(fd_t fd) { @@ -91,11 +95,11 @@ uptr internal_close(fd_t fd) { } uptr internal_open(const char *filename, int flags) { - return internal_syscall(__NR_open, filename, flags); + return internal_syscall(__NR_open, (uptr)filename, flags); } uptr internal_open(const char *filename, int flags, u32 mode) { - return internal_syscall(__NR_open, filename, flags, mode); + return internal_syscall(__NR_open, (uptr)filename, flags, mode); } uptr OpenFile(const char *filename, bool write) { @@ -105,13 +109,13 @@ uptr OpenFile(const char *filename, bool write) { uptr internal_read(fd_t fd, void *buf, uptr count) { sptr res; - HANDLE_EINTR(res, (sptr)internal_syscall(__NR_read, fd, buf, count)); + HANDLE_EINTR(res, (sptr)internal_syscall(__NR_read, fd, (uptr)buf, count)); return res; } uptr internal_write(fd_t fd, const void *buf, uptr count) { sptr res; - HANDLE_EINTR(res, (sptr)internal_syscall(__NR_write, fd, buf, count)); + HANDLE_EINTR(res, (sptr)internal_syscall(__NR_write, fd, (uptr)buf, count)); return res; } @@ -137,7 +141,7 @@ static void stat64_to_stat(struct stat64 *in, struct stat *out) { uptr internal_stat(const char *path, void *buf) { #if SANITIZER_LINUX_USES_64BIT_SYSCALLS - return internal_syscall(__NR_stat, path, buf); + return internal_syscall(__NR_stat, (uptr)path, (uptr)buf); #else struct stat64 buf64; int res = internal_syscall(__NR_stat64, path, &buf64); @@ -148,7 +152,7 @@ uptr internal_stat(const char *path, void *buf) { uptr internal_lstat(const char *path, void *buf) { #if SANITIZER_LINUX_USES_64BIT_SYSCALLS - return internal_syscall(__NR_lstat, path, buf); + return internal_syscall(__NR_lstat, (uptr)path, (uptr)buf); #else struct stat64 buf64; int res = internal_syscall(__NR_lstat64, path, &buf64); @@ -159,7 +163,7 @@ uptr internal_lstat(const char *path, void *buf) { uptr internal_fstat(fd_t fd, void *buf) { #if SANITIZER_LINUX_USES_64BIT_SYSCALLS - return internal_syscall(__NR_fstat, fd, buf); + return internal_syscall(__NR_fstat, fd, (uptr)buf); #else struct stat64 buf64; int res = internal_syscall(__NR_fstat64, fd, &buf64); @@ -180,11 +184,11 @@ uptr internal_dup2(int oldfd, int newfd) { } uptr internal_readlink(const char *path, char *buf, uptr bufsize) { - return internal_syscall(__NR_readlink, path, buf, bufsize); + return internal_syscall(__NR_readlink, (uptr)path, (uptr)buf, bufsize); } uptr internal_unlink(const char *path) { - return internal_syscall(__NR_unlink, path); + return internal_syscall(__NR_unlink, (uptr)path); } uptr internal_sched_yield() { @@ -198,7 +202,7 @@ void internal__exit(int exitcode) { uptr internal_execve(const char *filename, char *const argv[], char *const envp[]) { - return internal_syscall(__NR_execve, filename, argv, envp); + return internal_syscall(__NR_execve, (uptr)filename, (uptr)argv, (uptr)envp); } // ----------------- sanitizer_common.h @@ -216,7 +220,8 @@ uptr GetTid() { u64 NanoTime() { kernel_timeval tv; - internal_syscall(__NR_gettimeofday, &tv, 0); + internal_memset(&tv, 0, sizeof(tv)); + internal_syscall(__NR_gettimeofday, (uptr)&tv, 0); return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000; } @@ -249,7 +254,7 @@ const char *GetEnv(const char *name) { } extern "C" { - extern void *__libc_stack_end SANITIZER_WEAK_ATTRIBUTE; + SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end; } #if !SANITIZER_GO @@ -307,7 +312,10 @@ void PrepareForSandboxing() { // cached mappings. MemoryMappingLayout::CacheMemoryMappings(); // Same for /proc/self/exe in the symbolizer. - SymbolizerPrepareForSandboxing(); +#if !SANITIZER_GO + if (Symbolizer *sym = Symbolizer::GetOrNull()) + sym->PrepareForSandboxing(); +#endif } // ----------------- sanitizer_procmaps.h @@ -400,6 +408,30 @@ static bool IsDecimal(char c) { return c >= '0' && c <= '9'; } +static bool IsHex(char c) { + return (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f'); +} + +static uptr ReadHex(const char *p) { + uptr v = 0; + for (; IsHex(p[0]); p++) { + if (p[0] >= '0' && p[0] <= '9') + v = v * 16 + p[0] - '0'; + else + v = v * 16 + p[0] - 'a' + 10; + } + return v; +} + +static uptr ReadDecimal(const char *p) { + uptr v = 0; + for (; IsDecimal(p[0]); p++) + v = v * 10 + p[0] - '0'; + return v; +} + + bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, char filename[], uptr filename_size, uptr *protection) { @@ -442,7 +474,9 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, CHECK_EQ(*current_++, ' '); while (IsDecimal(*current_)) current_++; - CHECK_EQ(*current_++, ' '); + // Qemu may lack the trailing space. + // http://code.google.com/p/address-sanitizer/issues/detail?id=160 + // CHECK_EQ(*current_++, ' '); // Skip spaces. while (current_ < next_line && *current_ == ' ') current_++; @@ -468,6 +502,29 @@ bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset, protection); } +void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { + char *smaps = 0; + uptr smaps_cap = 0; + uptr smaps_len = ReadFileToBuffer("/proc/self/smaps", + &smaps, &smaps_cap, 64<<20); + uptr start = 0; + bool file = false; + const char *pos = smaps; + while (pos < smaps + smaps_len) { + if (IsHex(pos[0])) { + start = ReadHex(pos); + for (; *pos != '/' && *pos > '\n'; pos++) {} + file = *pos == '/'; + } else if (internal_strncmp(pos, "Rss:", 4) == 0) { + for (; *pos < '0' || *pos > '9'; pos++) {} + uptr rss = ReadDecimal(pos) * 1024; + cb(start, rss, file, stats, stats_size); + } + while (*pos++ != '\n') {} + } + UnmapOrDie(smaps, smaps_cap); +} + enum MutexState { MtxUnlocked = 0, MtxLocked = 1, @@ -487,7 +544,7 @@ void BlockingMutex::Lock() { if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked) return; while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) - internal_syscall(__NR_futex, m, FUTEX_WAIT, MtxSleeping, 0, 0, 0); + internal_syscall(__NR_futex, (uptr)m, FUTEX_WAIT, MtxSleeping, 0, 0, 0); } void BlockingMutex::Unlock() { @@ -495,7 +552,7 @@ void BlockingMutex::Unlock() { u32 v = atomic_exchange(m, MtxUnlocked, memory_order_relaxed); CHECK_NE(v, MtxUnlocked); if (v == MtxSleeping) - internal_syscall(__NR_futex, m, FUTEX_WAKE, 1, 0, 0, 0); + internal_syscall(__NR_futex, (uptr)m, FUTEX_WAKE, 1, 0, 0, 0); } void BlockingMutex::CheckLocked() { @@ -516,11 +573,12 @@ struct linux_dirent { // Syscall wrappers. uptr internal_ptrace(int request, int pid, void *addr, void *data) { - return internal_syscall(__NR_ptrace, request, pid, addr, data); + return internal_syscall(__NR_ptrace, request, pid, (uptr)addr, (uptr)data); } uptr internal_waitpid(int pid, int *status, int options) { - return internal_syscall(__NR_wait4, pid, status, options, 0 /* rusage */); + return internal_syscall(__NR_wait4, pid, (uptr)status, options, + 0 /* rusage */); } uptr internal_getpid() { @@ -532,7 +590,7 @@ uptr internal_getppid() { } uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) { - return internal_syscall(__NR_getdents, fd, dirp, count); + return internal_syscall(__NR_getdents, fd, (uptr)dirp, count); } uptr internal_lseek(fd_t fd, OFF_T offset, int whence) { @@ -545,7 +603,32 @@ uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) { uptr internal_sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss) { - return internal_syscall(__NR_sigaltstack, ss, oss); + return internal_syscall(__NR_sigaltstack, (uptr)ss, (uptr)oss); +} + +uptr internal_sigaction(int signum, const __sanitizer_kernel_sigaction_t *act, + __sanitizer_kernel_sigaction_t *oldact) { + return internal_syscall(__NR_rt_sigaction, signum, act, oldact, + sizeof(__sanitizer_kernel_sigset_t)); +} + +uptr internal_sigprocmask(int how, __sanitizer_kernel_sigset_t *set, + __sanitizer_kernel_sigset_t *oldset) { + return internal_syscall(__NR_rt_sigprocmask, (uptr)how, &set->sig[0], + &oldset->sig[0], sizeof(__sanitizer_kernel_sigset_t)); +} + +void internal_sigfillset(__sanitizer_kernel_sigset_t *set) { + internal_memset(set, 0xff, sizeof(*set)); +} + +void internal_sigdelset(__sanitizer_kernel_sigset_t *set, int signum) { + signum -= 1; + CHECK_GE(signum, 0); + CHECK_LT(signum, sizeof(*set) * 8); + const uptr idx = signum / (sizeof(set->sig[0]) * 8); + const uptr bit = signum % (sizeof(set->sig[0]) * 8); + set->sig[idx] &= ~(1 << bit); } // ThreadLister implementation. @@ -624,6 +707,39 @@ uptr GetPageSize() { #endif } +static char proc_self_exe_cache_str[kMaxPathLength]; +static uptr proc_self_exe_cache_len = 0; + +uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { + uptr module_name_len = internal_readlink( + "/proc/self/exe", buf, buf_len); + int readlink_error; + if (internal_iserror(module_name_len, &readlink_error)) { + if (proc_self_exe_cache_len) { + // If available, use the cached module name. + CHECK_LE(proc_self_exe_cache_len, buf_len); + internal_strncpy(buf, proc_self_exe_cache_str, buf_len); + module_name_len = internal_strlen(proc_self_exe_cache_str); + } else { + // We can't read /proc/self/exe for some reason, assume the name of the + // binary is unknown. + Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, " + "some stack frames may not be symbolized\n", readlink_error); + module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe"); + } + CHECK_LT(module_name_len, buf_len); + buf[module_name_len] = '\0'; + } + return module_name_len; +} + +void CacheBinaryName() { + if (!proc_self_exe_cache_len) { + proc_self_exe_cache_len = + ReadBinaryName(proc_self_exe_cache_str, kMaxPathLength); + } +} + // Match full names of the form /path/to/base_name{-,.}* bool LibraryNameIs(const char *full_name, const char *base_name) { const char *name = full_name; @@ -636,6 +752,107 @@ bool LibraryNameIs(const char *full_name, const char *base_name) { return (name[base_name_length] == '-' || name[base_name_length] == '.'); } +#if !SANITIZER_ANDROID +// Call cb for each region mapped by map. +void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) { + typedef ElfW(Phdr) Elf_Phdr; + typedef ElfW(Ehdr) Elf_Ehdr; + char *base = (char *)map->l_addr; + Elf_Ehdr *ehdr = (Elf_Ehdr *)base; + char *phdrs = base + ehdr->e_phoff; + char *phdrs_end = phdrs + ehdr->e_phnum * ehdr->e_phentsize; + + // Find the segment with the minimum base so we can "relocate" the p_vaddr + // fields. Typically ET_DYN objects (DSOs) have base of zero and ET_EXEC + // objects have a non-zero base. + uptr preferred_base = (uptr)-1; + for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { + Elf_Phdr *phdr = (Elf_Phdr *)iter; + if (phdr->p_type == PT_LOAD && preferred_base > (uptr)phdr->p_vaddr) + preferred_base = (uptr)phdr->p_vaddr; + } + + // Compute the delta from the real base to get a relocation delta. + sptr delta = (uptr)base - preferred_base; + // Now we can figure out what the loader really mapped. + for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { + Elf_Phdr *phdr = (Elf_Phdr *)iter; + if (phdr->p_type == PT_LOAD) { + uptr seg_start = phdr->p_vaddr + delta; + uptr seg_end = seg_start + phdr->p_memsz; + // None of these values are aligned. We consider the ragged edges of the + // load command as defined, since they are mapped from the file. + seg_start = RoundDownTo(seg_start, GetPageSizeCached()); + seg_end = RoundUpTo(seg_end, GetPageSizeCached()); + cb((void *)seg_start, seg_end - seg_start); + } + } +} +#endif + +#if defined(__x86_64__) +// We cannot use glibc's clone wrapper, because it messes with the child +// task's TLS. It writes the PID and TID of the child task to its thread +// descriptor, but in our case the child task shares the thread descriptor with +// the parent (because we don't know how to allocate a new thread +// descriptor to keep glibc happy). So the stock version of clone(), when +// used with CLONE_VM, would end up corrupting the parent's thread descriptor. +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr) { + long long res; + if (!fn || !child_stack) + return -EINVAL; + CHECK_EQ(0, (uptr)child_stack % 16); + child_stack = (char *)child_stack - 2 * sizeof(unsigned long long); + ((unsigned long long *)child_stack)[0] = (uptr)fn; + ((unsigned long long *)child_stack)[1] = (uptr)arg; + register void *r8 __asm__("r8") = newtls; + register int *r10 __asm__("r10") = child_tidptr; + __asm__ __volatile__( + /* %rax = syscall(%rax = __NR_clone, + * %rdi = flags, + * %rsi = child_stack, + * %rdx = parent_tidptr, + * %r8 = new_tls, + * %r10 = child_tidptr) + */ + "syscall\n" + + /* if (%rax != 0) + * return; + */ + "testq %%rax,%%rax\n" + "jnz 1f\n" + + /* In the child. Terminate unwind chain. */ + // XXX: We should also terminate the CFI unwind chain + // here. Unfortunately clang 3.2 doesn't support the + // necessary CFI directives, so we skip that part. + "xorq %%rbp,%%rbp\n" + + /* Call "fn(arg)". */ + "popq %%rax\n" + "popq %%rdi\n" + "call *%%rax\n" + + /* Call _exit(%rax). */ + "movq %%rax,%%rdi\n" + "movq %2,%%rax\n" + "syscall\n" + + /* Return to parent. */ + "1:\n" + : "=a" (res) + : "a"(__NR_clone), "i"(__NR_exit), + "S"(child_stack), + "D"(flags), + "d"(parent_tidptr), + "r"(r8), + "r"(r10) + : "rsp", "memory", "r11", "rcx"); + return res; +} +#endif // defined(__x86_64__) } // namespace __sanitizer #endif // SANITIZER_LINUX diff --git a/lib/sanitizer_common/sanitizer_linux.h b/lib/sanitizer_common/sanitizer_linux.h index ba68e6c2dd5a..a32e9bf08bad 100644 --- a/lib/sanitizer_common/sanitizer_linux.h +++ b/lib/sanitizer_common/sanitizer_linux.h @@ -13,9 +13,13 @@ #ifndef SANITIZER_LINUX_H #define SANITIZER_LINUX_H +#include "sanitizer_platform.h" +#if SANITIZER_LINUX #include "sanitizer_common.h" #include "sanitizer_internal_defs.h" +#include "sanitizer_platform_limits_posix.h" +struct link_map; // Opaque type returned by dlopen(). struct sigaltstack; namespace __sanitizer { @@ -28,6 +32,17 @@ uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count); uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5); uptr internal_sigaltstack(const struct sigaltstack* ss, struct sigaltstack* oss); +uptr internal_sigaction(int signum, const __sanitizer_kernel_sigaction_t *act, + __sanitizer_kernel_sigaction_t *oldact); +uptr internal_sigprocmask(int how, __sanitizer_kernel_sigset_t *set, + __sanitizer_kernel_sigset_t *oldset); +void internal_sigfillset(__sanitizer_kernel_sigset_t *set); +void internal_sigdelset(__sanitizer_kernel_sigset_t *set, int signum); + +#ifdef __x86_64__ +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr); +#endif // This class reads thread IDs from /proc/<pid>/task using only syscalls. class ThreadLister { @@ -51,15 +66,25 @@ class ThreadLister { int bytes_read_; }; -void AdjustStackSizeLinux(void *attr, int verbosity); +void AdjustStackSizeLinux(void *attr); // Exposed for testing. uptr ThreadDescriptorSize(); +uptr ThreadSelf(); +uptr ThreadSelfOffset(); // Matches a library's file name against a base name (stripping path and version // information). bool LibraryNameIs(const char *full_name, const char *base_name); +// Read the name of the current binary from /proc/self/exe. +uptr ReadBinaryName(/*out*/char *buf, uptr buf_len); +// Cache the value of /proc/self/exe. +void CacheBinaryName(); + +// Call cb for each region mapped by map. +void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)); } // namespace __sanitizer +#endif // SANITIZER_LINUX #endif // SANITIZER_LINUX_H diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc index d9e2f5389606..2940686f1275 100644 --- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc @@ -16,27 +16,28 @@ #if SANITIZER_LINUX #include "sanitizer_common.h" +#include "sanitizer_flags.h" +#include "sanitizer_linux.h" +#include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" -#ifdef __x86_64__ -#include <asm/prctl.h> -#endif #include <dlfcn.h> #include <pthread.h> #include <sys/prctl.h> #include <sys/resource.h> #include <unwind.h> -#ifdef __x86_64__ -extern "C" int arch_prctl(int code, __sanitizer::uptr *addr); +#if !SANITIZER_ANDROID +#include <elf.h> +#include <link.h> #endif namespace __sanitizer { void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom) { - static const uptr kMaxThreadStackSize = 256 * (1 << 20); // 256M + static const uptr kMaxThreadStackSize = 1 << 30; // 1Gb CHECK(stack_top); CHECK(stack_bottom); if (at_initialization) { @@ -76,9 +77,9 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize); pthread_attr_destroy(&attr); + CHECK_LE(stacksize, kMaxThreadStackSize); // Sanity check. *stack_top = (uptr)stackaddr + stacksize; *stack_bottom = (uptr)stackaddr; - CHECK(stacksize < kMaxThreadStackSize); // Sanity check. } // Does not compile for Go because dlsym() requires -ldl @@ -139,35 +140,33 @@ uptr Unwind_GetIP(struct _Unwind_Context *ctx) { #endif } +struct UnwindTraceArg { + StackTrace *stack; + uptr max_depth; +}; + _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { - StackTrace *b = (StackTrace*)param; - CHECK(b->size < b->max_size); + UnwindTraceArg *arg = (UnwindTraceArg*)param; + CHECK_LT(arg->stack->size, arg->max_depth); uptr pc = Unwind_GetIP(ctx); - b->trace[b->size++] = pc; - if (b->size == b->max_size) return UNWIND_STOP; + arg->stack->trace[arg->stack->size++] = pc; + if (arg->stack->size == arg->max_depth) return UNWIND_STOP; return UNWIND_CONTINUE; } -static bool MatchPc(uptr cur_pc, uptr trace_pc) { - return cur_pc - trace_pc <= 64 || trace_pc - cur_pc <= 64; -} - void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { - this->size = 0; - this->max_size = max_depth; - if (max_depth > 1) { - _Unwind_Backtrace(Unwind_Trace, this); - // We need to pop a few frames so that pc is on top. - // trace[0] belongs to the current function so we always pop it. - int to_pop = 1; - /**/ if (size > 1 && MatchPc(pc, trace[1])) to_pop = 1; - else if (size > 2 && MatchPc(pc, trace[2])) to_pop = 2; - else if (size > 3 && MatchPc(pc, trace[3])) to_pop = 3; - else if (size > 4 && MatchPc(pc, trace[4])) to_pop = 4; - else if (size > 5 && MatchPc(pc, trace[5])) to_pop = 5; - this->PopStackFrames(to_pop); - } - this->trace[0] = pc; + size = 0; + if (max_depth == 0) + return; + UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; + _Unwind_Backtrace(Unwind_Trace, &arg); + // We need to pop a few frames so that pc is on top. + uptr to_pop = LocatePcInTrace(pc); + // trace[0] belongs to the current function so we always pop it. + if (to_pop == 0) + to_pop = 1; + PopStackFrames(to_pop); + trace[0] = pc; } #endif // !SANITIZER_GO @@ -200,20 +199,43 @@ uptr GetTlsSize() { return g_tls_size; } +#if defined(__x86_64__) || defined(__i386__) // sizeof(struct thread) from glibc. -#ifdef __x86_64__ -const uptr kThreadDescriptorSize = 2304; +// There has been a report of this being different on glibc 2.11 and 2.13. We +// don't know when this change happened, so 2.14 is a conservative estimate. +#if __GLIBC_PREREQ(2, 14) +const uptr kThreadDescriptorSize = FIRST_32_SECOND_64(1216, 2304); +#else +const uptr kThreadDescriptorSize = FIRST_32_SECOND_64(1168, 2304); +#endif uptr ThreadDescriptorSize() { return kThreadDescriptorSize; } + +// The offset at which pointer to self is located in the thread descriptor. +const uptr kThreadSelfOffset = FIRST_32_SECOND_64(8, 16); + +uptr ThreadSelfOffset() { + return kThreadSelfOffset; +} + +uptr ThreadSelf() { + uptr descr_addr; +#ifdef __i386__ + asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); +#else + asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); #endif + return descr_addr; +} +#endif // defined(__x86_64__) || defined(__i386__) void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size) { #ifndef SANITIZER_GO -#ifdef __x86_64__ - arch_prctl(ARCH_GET_FS, tls_addr); +#if defined(__x86_64__) || defined(__i386__) + *tls_addr = ThreadSelf(); *tls_size = GetTlsSize(); *tls_addr -= *tls_size; *tls_addr += kThreadDescriptorSize; @@ -244,7 +266,7 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, #endif // SANITIZER_GO } -void AdjustStackSizeLinux(void *attr_, int verbosity) { +void AdjustStackSizeLinux(void *attr_) { pthread_attr_t *attr = (pthread_attr_t *)attr_; uptr stackaddr = 0; size_t stacksize = 0; @@ -256,7 +278,7 @@ void AdjustStackSizeLinux(void *attr_, int verbosity) { const uptr minstacksize = GetTlsSize() + 128*1024; if (stacksize < minstacksize) { if (!stack_set) { - if (verbosity && stacksize != 0) + if (common_flags()->verbosity && stacksize != 0) Printf("Sanitizer: increasing stacksize %zu->%zu\n", stacksize, minstacksize); pthread_attr_setstacksize(attr, minstacksize); @@ -268,6 +290,63 @@ void AdjustStackSizeLinux(void *attr_, int verbosity) { } } +#if SANITIZER_ANDROID +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + return 0; +} +#else // SANITIZER_ANDROID +typedef ElfW(Phdr) Elf_Phdr; + +struct DlIteratePhdrData { + LoadedModule *modules; + uptr current_n; + bool first; + uptr max_n; + string_predicate_t filter; +}; + +static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { + DlIteratePhdrData *data = (DlIteratePhdrData*)arg; + if (data->current_n == data->max_n) + return 0; + InternalScopedBuffer<char> module_name(kMaxPathLength); + module_name.data()[0] = '\0'; + if (data->first) { + data->first = false; + // First module is the binary itself. + ReadBinaryName(module_name.data(), module_name.size()); + } else if (info->dlpi_name) { + internal_strncpy(module_name.data(), info->dlpi_name, module_name.size()); + } + if (module_name.data()[0] == '\0') + return 0; + if (data->filter && !data->filter(module_name.data())) + return 0; + void *mem = &data->modules[data->current_n]; + LoadedModule *cur_module = new(mem) LoadedModule(module_name.data(), + info->dlpi_addr); + data->current_n++; + for (int i = 0; i < info->dlpi_phnum; i++) { + const Elf_Phdr *phdr = &info->dlpi_phdr[i]; + if (phdr->p_type == PT_LOAD) { + uptr cur_beg = info->dlpi_addr + phdr->p_vaddr; + uptr cur_end = cur_beg + phdr->p_memsz; + cur_module->addAddressRange(cur_beg, cur_end); + } + } + return 0; +} + +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + CHECK(modules); + DlIteratePhdrData data = {modules, 0, true, max_modules, filter}; + dl_iterate_phdr(dl_iterate_phdr_cb, &data); + return data.current_n; +} +#endif // SANITIZER_ANDROID + } // namespace __sanitizer #endif // SANITIZER_LINUX diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc index f97d1e39c5bf..87ad8b53c6c9 100644 --- a/lib/sanitizer_common/sanitizer_mac.cc +++ b/lib/sanitizer_common/sanitizer_mac.cc @@ -25,6 +25,7 @@ #include "sanitizer_common.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" +#include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include <crt_externs.h> // for _NSGetEnviron @@ -144,7 +145,11 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, const char *GetEnv(const char *name) { char ***env_ptr = _NSGetEnviron(); - CHECK(env_ptr); + if (!env_ptr) { + Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is " + "called after libSystem_initializer().\n"); + CHECK(env_ptr); + } char **environ = *env_ptr; CHECK(environ); uptr name_len = internal_strlen(name); @@ -343,6 +348,10 @@ void BlockingMutex::CheckLocked() { CHECK_EQ((uptr)pthread_self(), owner_); } +u64 NanoTime() { + return 0; +} + uptr GetTlsSize() { return 0; } @@ -352,12 +361,50 @@ void InitTlsSize() { void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size) { +#ifndef SANITIZER_GO uptr stack_top, stack_bottom; GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); *stk_addr = stack_bottom; *stk_size = stack_top - stack_bottom; *tls_addr = 0; *tls_size = 0; +#else + *stk_addr = 0; + *stk_size = 0; + *tls_addr = 0; + *tls_size = 0; +#endif +} + +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + MemoryMappingLayout memory_mapping(false); + memory_mapping.Reset(); + uptr cur_beg, cur_end, cur_offset; + InternalScopedBuffer<char> module_name(kMaxPathLength); + uptr n_modules = 0; + for (uptr i = 0; + n_modules < max_modules && + memory_mapping.Next(&cur_beg, &cur_end, &cur_offset, + module_name.data(), module_name.size(), 0); + i++) { + const char *cur_name = module_name.data(); + if (cur_name[0] == '\0') + continue; + if (filter && !filter(cur_name)) + continue; + LoadedModule *cur_module = 0; + if (n_modules > 0 && + 0 == internal_strcmp(cur_name, modules[n_modules - 1].full_name())) { + cur_module = &modules[n_modules - 1]; + } else { + void *mem = &modules[n_modules]; + cur_module = new(mem) LoadedModule(cur_name, cur_beg); + n_modules++; + } + cur_module->addAddressRange(cur_beg, cur_end); + } + return n_modules; } } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_mutex.h b/lib/sanitizer_common/sanitizer_mutex.h index 469981c35176..e812fce90598 100644 --- a/lib/sanitizer_common/sanitizer_mutex.h +++ b/lib/sanitizer_common/sanitizer_mutex.h @@ -40,6 +40,10 @@ class StaticSpinMutex { atomic_store(&state_, 0, memory_order_release); } + void CheckLocked() { + CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1); + } + private: atomic_uint8_t state_; diff --git a/lib/sanitizer_common/sanitizer_placement_new.h b/lib/sanitizer_common/sanitizer_placement_new.h index a42301aedeac..8904d1040ea7 100644 --- a/lib/sanitizer_common/sanitizer_placement_new.h +++ b/lib/sanitizer_common/sanitizer_placement_new.h @@ -18,15 +18,7 @@ #include "sanitizer_internal_defs.h" -namespace __sanitizer { -#if (SANITIZER_WORDSIZE == 64) || SANITIZER_MAC -typedef uptr operator_new_ptr_type; -#else -typedef u32 operator_new_ptr_type; -#endif -} // namespace __sanitizer - -inline void *operator new(__sanitizer::operator_new_ptr_type sz, void *p) { +inline void *operator new(__sanitizer::operator_new_size_type sz, void *p) { return p; } diff --git a/lib/sanitizer_common/sanitizer_platform.h b/lib/sanitizer_common/sanitizer_platform.h index acb997180957..fce721e300fb 100644 --- a/lib/sanitizer_common/sanitizer_platform.h +++ b/lib/sanitizer_common/sanitizer_platform.h @@ -25,8 +25,15 @@ #if defined(__APPLE__) # define SANITIZER_MAC 1 +# include <TargetConditionals.h> +# if TARGET_OS_IPHONE +# define SANITIZER_IOS 1 +# else +# define SANITIZER_IOS 0 +# endif #else # define SANITIZER_MAC 0 +# define SANITIZER_IOS 0 #endif #if defined(_WIN32) diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h index 60c7145b611a..78d1f5adef7b 100644 --- a/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -41,6 +41,13 @@ # define SI_MAC 0 #endif +#if SANITIZER_IOS +# define SI_IOS 1 +#else +# define SI_IOS 0 +#endif + +# define SANITIZER_INTERCEPT_STRCMP 1 # define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_READ SI_NOT_WINDOWS @@ -48,11 +55,21 @@ # define SANITIZER_INTERCEPT_WRITE SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_PWRITE SI_NOT_WINDOWS -# define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID -# define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID + +#define SANITIZER_INTERCEPT_READV SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_WRITEV SI_NOT_WINDOWS + +#define SANITIZER_INTERCEPT_PREADV SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PREADV64 SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PWRITEV64 SI_LINUX_NOT_ANDROID + # define SANITIZER_INTERCEPT_PRCTL SI_LINUX # define SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_STRPTIME SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX @@ -71,9 +88,83 @@ # define SANITIZER_INTERCEPT_INET SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_GETADDRINFO SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GETNAMEINFO SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_LINUX # define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX +# define SANITIZER_INTERCEPT_MODF SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_RECVMSG SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GETPEERNAME SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_IOCTL SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_INET_ATON SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SYSINFO SI_LINUX +# define SANITIZER_INTERCEPT_READDIR SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \ + (defined(__i386) || defined (__x86_64)) // NOLINT +# define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX +# define SANITIZER_INTERCEPT_STRTOIMAX SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_WCSNRTOMBS SI_MAC || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX +# define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_CONFSTR SI_MAC || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SCANDIR SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_POLL SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_WORDEXP SI_MAC || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SIGSETOPS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX +# define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_STATFS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_STATFS64 \ + (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_STATVFS SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_ETHER SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_ETHER_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SHMCTL SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_RANDOM_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \ + SI_MAC || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_TMPNAM SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SINCOS SI_LINUX +# define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_LGAMMA_R SI_LINUX +# define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID + +// FIXME: getline seems to be available on OSX 10.7 +# define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID + +# define SANITIZER_INTERCEPT__EXIT SI_LINUX + +# define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PTHREAD_COND SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H diff --git a/lib/sanitizer_common/sanitizer_platform_limits_linux.cc b/lib/sanitizer_common/sanitizer_platform_limits_linux.cc new file mode 100644 index 000000000000..4c9f12acd3b0 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_platform_limits_linux.cc @@ -0,0 +1,95 @@ +//===-- sanitizer_platform_limits_linux.cc --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of Sanitizer common code. +// +// Sizes and layouts of linux kernel data structures. +//===----------------------------------------------------------------------===// + +// This is a separate compilation unit for linux headers that conflict with +// userspace headers. +// Most "normal" includes go in sanitizer_platform_limits_posix.cc + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_internal_defs.h" +#include "sanitizer_platform_limits_posix.h" + +// For offsetof -> __builtin_offsetof definition. +#include <stddef.h> + +// With old kernels (and even new kernels on powerpc) asm/stat.h uses types that +// are not defined anywhere in userspace headers. Fake them. This seems to work +// fine with newer headers, too. +#include <asm/posix_types.h> +#define ino_t __kernel_ino_t +#define mode_t __kernel_mode_t +#define nlink_t __kernel_nlink_t +#define uid_t __kernel_uid_t +#define gid_t __kernel_gid_t +#define off_t __kernel_off_t +// This header seems to contain the definitions of _kernel_ stat* structs. +#include <asm/stat.h> +#undef ino_t +#undef mode_t +#undef nlink_t +#undef uid_t +#undef gid_t +#undef off_t + +#include <linux/aio_abi.h> + +#if SANITIZER_ANDROID +#include <asm/statfs.h> +#else +#include <sys/statfs.h> +#endif + +#if !SANITIZER_ANDROID +#include <linux/perf_event.h> +#endif + +namespace __sanitizer { + unsigned struct_statfs64_sz = sizeof(struct statfs64); +} // namespace __sanitizer + +#if !defined(__powerpc64__) +COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat)); +#endif + +COMPILER_CHECK(struct_kernel_stat_sz == sizeof(struct stat)); + +#if defined(__i386__) +COMPILER_CHECK(struct_kernel_stat64_sz == sizeof(struct stat64)); +#endif + +COMPILER_CHECK(struct_io_event_sz == sizeof(struct io_event)); + +#if !SANITIZER_ANDROID +COMPILER_CHECK(sizeof(struct __sanitizer_perf_event_attr) <= + sizeof(struct perf_event_attr)); +CHECK_SIZE_AND_OFFSET(perf_event_attr, type); +CHECK_SIZE_AND_OFFSET(perf_event_attr, size); +#endif + +COMPILER_CHECK(iocb_cmd_pread == IOCB_CMD_PREAD); +COMPILER_CHECK(iocb_cmd_pwrite == IOCB_CMD_PWRITE); + +CHECK_TYPE_SIZE(iocb); +CHECK_SIZE_AND_OFFSET(iocb, aio_data); +// Skip aio_key, it's weird. +CHECK_SIZE_AND_OFFSET(iocb, aio_lio_opcode); +CHECK_SIZE_AND_OFFSET(iocb, aio_reqprio); +CHECK_SIZE_AND_OFFSET(iocb, aio_fildes); +CHECK_SIZE_AND_OFFSET(iocb, aio_buf); +CHECK_SIZE_AND_OFFSET(iocb, aio_nbytes); +CHECK_SIZE_AND_OFFSET(iocb, aio_offset); + +#endif // SANITIZER_LINUX diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc index c269de65ca2c..e887751d60f0 100644 --- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc +++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc @@ -21,35 +21,109 @@ #include <arpa/inet.h> #include <dirent.h> +#include <errno.h> #include <grp.h> +#include <limits.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/route.h> +#include <netdb.h> +#include <poll.h> #include <pthread.h> #include <pwd.h> #include <signal.h> #include <stddef.h> -#include <sys/utsname.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <sys/time.h> #include <sys/resource.h> #include <sys/socket.h> -#include <netdb.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/times.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <termios.h> #include <time.h> +#include <wchar.h> + +#if SANITIZER_LINUX +#include <mntent.h> +#include <netinet/ether.h> +#include <utime.h> +#include <sys/mount.h> +#include <sys/ptrace.h> +#include <sys/sysinfo.h> +#include <sys/vt.h> +#include <linux/cdrom.h> +#include <linux/fd.h> +#include <linux/fs.h> +#include <linux/hdreg.h> +#include <linux/input.h> +#include <linux/ioctl.h> +#include <linux/soundcard.h> +#include <linux/sysctl.h> +#include <linux/utsname.h> +#include <linux/posix_types.h> +#endif #if !SANITIZER_ANDROID #include <sys/ucontext.h> -#endif // !SANITIZER_ANDROID +#include <wordexp.h> +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +#include <glob.h> +#include <mqueue.h> +#include <net/if_ppp.h> +#include <netax25/ax25.h> +#include <netipx/ipx.h> +#include <netrom/netrom.h> +#include <scsi/scsi.h> +#include <sys/mtio.h> +#include <sys/kd.h> +#include <sys/shm.h> +#include <sys/statvfs.h> +#include <sys/timex.h> +#include <sys/user.h> +#include <sys/ustat.h> +#include <linux/cyclades.h> +#include <linux/if_eql.h> +#include <linux/if_plip.h> +#include <linux/lp.h> +#include <linux/mroute.h> +#include <linux/mroute6.h> +#include <linux/scc.h> +#include <linux/serial.h> +#include <sys/msg.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID + +#if SANITIZER_ANDROID +#include <linux/kd.h> +#include <linux/mtio.h> +#include <linux/ppp_defs.h> +#include <linux/if_ppp.h> +#endif #if SANITIZER_LINUX #include <link.h> #include <sys/vfs.h> #include <sys/epoll.h> +#include <linux/capability.h> #endif // SANITIZER_LINUX +#if SANITIZER_MAC +#include <net/ethernet.h> +#include <sys/filio.h> +#include <sys/mount.h> +#include <sys/sockio.h> +#endif + namespace __sanitizer { unsigned struct_utsname_sz = sizeof(struct utsname); unsigned struct_stat_sz = sizeof(struct stat); +#if !SANITIZER_IOS unsigned struct_stat64_sz = sizeof(struct stat64); +#endif // !SANITIZER_IOS unsigned struct_rusage_sz = sizeof(struct rusage); unsigned struct_tm_sz = sizeof(struct tm); unsigned struct_passwd_sz = sizeof(struct passwd); @@ -58,7 +132,21 @@ namespace __sanitizer { unsigned struct_sigaction_sz = sizeof(struct sigaction); unsigned struct_itimerval_sz = sizeof(struct itimerval); unsigned pthread_t_sz = sizeof(pthread_t); - unsigned struct_sockaddr_sz = sizeof(struct sockaddr); + unsigned pthread_cond_t_sz = sizeof(pthread_cond_t); + unsigned pid_t_sz = sizeof(pid_t); + unsigned timeval_sz = sizeof(timeval); + unsigned uid_t_sz = sizeof(uid_t); + unsigned mbstate_t_sz = sizeof(mbstate_t); + unsigned sigset_t_sz = sizeof(sigset_t); + unsigned struct_timezone_sz = sizeof(struct timezone); + unsigned struct_tms_sz = sizeof(struct tms); + unsigned struct_sigevent_sz = sizeof(struct sigevent); + unsigned struct_sched_param_sz = sizeof(struct sched_param); + unsigned struct_statfs_sz = sizeof(struct statfs); + +#if SANITIZER_MAC && !SANITIZER_IOS + unsigned struct_statfs64_sz = sizeof(struct statfs64); +#endif // SANITIZER_MAC && !SANITIZER_IOS #if !SANITIZER_ANDROID unsigned ucontext_t_sz = sizeof(ucontext_t); @@ -66,51 +154,49 @@ namespace __sanitizer { #if SANITIZER_LINUX unsigned struct_rlimit_sz = sizeof(struct rlimit); - unsigned struct_dirent_sz = sizeof(struct dirent); - unsigned struct_statfs_sz = sizeof(struct statfs); unsigned struct_epoll_event_sz = sizeof(struct epoll_event); + unsigned struct_sysinfo_sz = sizeof(struct sysinfo); unsigned struct_timespec_sz = sizeof(struct timespec); + unsigned __user_cap_header_struct_sz = + sizeof(struct __user_cap_header_struct); + unsigned __user_cap_data_struct_sz = sizeof(struct __user_cap_data_struct); + unsigned struct_utimbuf_sz = sizeof(struct utimbuf); + unsigned struct_new_utsname_sz = sizeof(struct new_utsname); + unsigned struct_old_utsname_sz = sizeof(struct old_utsname); + unsigned struct_oldold_utsname_sz = sizeof(struct oldold_utsname); + unsigned struct_itimerspec_sz = sizeof(struct itimerspec); + unsigned struct_ustat_sz = sizeof(struct ustat); #endif // SANITIZER_LINUX #if SANITIZER_LINUX && !SANITIZER_ANDROID - unsigned struct_dirent64_sz = sizeof(struct dirent64); unsigned struct_rlimit64_sz = sizeof(struct rlimit64); - unsigned struct_statfs64_sz = sizeof(struct statfs64); + unsigned struct_timex_sz = sizeof(struct timex); + unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds); + unsigned struct_mq_attr_sz = sizeof(struct mq_attr); + unsigned struct_statvfs_sz = sizeof(struct statvfs); + unsigned struct_statvfs64_sz = sizeof(struct statvfs64); #endif // SANITIZER_LINUX && !SANITIZER_ANDROID uptr sig_ign = (uptr)SIG_IGN; uptr sig_dfl = (uptr)SIG_DFL; + uptr sa_siginfo = (uptr)SA_SIGINFO; - void* __sanitizer_get_msghdr_iov_iov_base(void* msg, int idx) { - return ((struct msghdr *)msg)->msg_iov[idx].iov_base; - } - - uptr __sanitizer_get_msghdr_iov_iov_len(void* msg, int idx) { - return ((struct msghdr *)msg)->msg_iov[idx].iov_len; - } +#if SANITIZER_LINUX + int e_tabsz = (int)E_TABSZ; +#endif - uptr __sanitizer_get_msghdr_iovlen(void* msg) { - return ((struct msghdr *)msg)->msg_iovlen; - } - uptr __sanitizer_get_socklen_t(void* socklen_ptr) { - return *(socklen_t*)socklen_ptr; - } +#if SANITIZER_LINUX && !SANITIZER_ANDROID + unsigned struct_shminfo_sz = sizeof(struct shminfo); + unsigned struct_shm_info_sz = sizeof(struct shm_info); + int shmctl_ipc_stat = (int)IPC_STAT; + int shmctl_ipc_info = (int)IPC_INFO; + int shmctl_shm_info = (int)SHM_INFO; + int shmctl_shm_stat = (int)SHM_INFO; +#endif - uptr __sanitizer_get_sigaction_sa_sigaction(void *act) { - struct sigaction *a = (struct sigaction *)act; - // Check that sa_sigaction and sa_handler are the same. - CHECK((void *)&(a->sa_sigaction) == (void *)&(a->sa_handler)); - return (uptr) a->sa_sigaction; - } - void __sanitizer_set_sigaction_sa_sigaction(void *act, uptr cb) { - struct sigaction *a = (struct sigaction *)act; - a->sa_sigaction = (void (*)(int, siginfo_t *, void *))cb; - } - bool __sanitizer_get_sigaction_sa_siginfo(void *act) { - struct sigaction *a = (struct sigaction *)act; - return a->sa_flags & SA_SIGINFO; - } + int af_inet = (int)AF_INET; + int af_inet6 = (int)AF_INET6; uptr __sanitizer_in_addr_sz(int af) { if (af == AF_INET) @@ -120,36 +206,742 @@ namespace __sanitizer { else return 0; } + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + int glob_nomatch = GLOB_NOMATCH; + int glob_altdirfunc = GLOB_ALTDIRFUNC; +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID && \ + (defined(__i386) || defined (__x86_64)) // NOLINT + unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct); + unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct); +#ifdef __x86_64 + unsigned struct_user_fpxregs_struct_sz = 0; +#else + unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); +#endif + + int ptrace_peektext = PTRACE_PEEKTEXT; + int ptrace_peekdata = PTRACE_PEEKDATA; + int ptrace_peekuser = PTRACE_PEEKUSER; + int ptrace_getregs = PTRACE_GETREGS; + int ptrace_setregs = PTRACE_SETREGS; + int ptrace_getfpregs = PTRACE_GETFPREGS; + int ptrace_setfpregs = PTRACE_SETFPREGS; + int ptrace_getfpxregs = PTRACE_GETFPXREGS; + int ptrace_setfpxregs = PTRACE_SETFPXREGS; + int ptrace_getsiginfo = PTRACE_GETSIGINFO; + int ptrace_setsiginfo = PTRACE_SETSIGINFO; +#if defined(PTRACE_GETREGSET) && defined(PTRACE_SETREGSET) + int ptrace_getregset = PTRACE_GETREGSET; + int ptrace_setregset = PTRACE_SETREGSET; +#else + int ptrace_getregset = -1; + int ptrace_setregset = -1; +#endif +#endif + + unsigned path_max = PATH_MAX; + + // ioctl arguments + unsigned struct_arpreq_sz = sizeof(struct arpreq); + unsigned struct_ifreq_sz = sizeof(struct ifreq); + unsigned struct_termios_sz = sizeof(struct termios); + unsigned struct_winsize_sz = sizeof(struct winsize); + +#if SANITIZER_LINUX + unsigned struct_cdrom_msf_sz = sizeof(struct cdrom_msf); + unsigned struct_cdrom_multisession_sz = sizeof(struct cdrom_multisession); + unsigned struct_cdrom_read_audio_sz = sizeof(struct cdrom_read_audio); + unsigned struct_cdrom_subchnl_sz = sizeof(struct cdrom_subchnl); + unsigned struct_cdrom_ti_sz = sizeof(struct cdrom_ti); + unsigned struct_cdrom_tocentry_sz = sizeof(struct cdrom_tocentry); + unsigned struct_cdrom_tochdr_sz = sizeof(struct cdrom_tochdr); + unsigned struct_cdrom_volctrl_sz = sizeof(struct cdrom_volctrl); +#if SOUND_VERSION >= 0x040000 + unsigned struct_copr_buffer_sz = 0; + unsigned struct_copr_debug_buf_sz = 0; + unsigned struct_copr_msg_sz = 0; +#else + unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer); + unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf); + unsigned struct_copr_msg_sz = sizeof(struct copr_msg); +#endif + unsigned struct_ff_effect_sz = sizeof(struct ff_effect); + unsigned struct_floppy_drive_params_sz = sizeof(struct floppy_drive_params); + unsigned struct_floppy_drive_struct_sz = sizeof(struct floppy_drive_struct); + unsigned struct_floppy_fdc_state_sz = sizeof(struct floppy_fdc_state); + unsigned struct_floppy_max_errors_sz = sizeof(struct floppy_max_errors); + unsigned struct_floppy_raw_cmd_sz = sizeof(struct floppy_raw_cmd); + unsigned struct_floppy_struct_sz = sizeof(struct floppy_struct); + unsigned struct_floppy_write_errors_sz = sizeof(struct floppy_write_errors); + unsigned struct_format_descr_sz = sizeof(struct format_descr); + unsigned struct_hd_driveid_sz = sizeof(struct hd_driveid); + unsigned struct_hd_geometry_sz = sizeof(struct hd_geometry); + unsigned struct_input_absinfo_sz = sizeof(struct input_absinfo); + unsigned struct_input_id_sz = sizeof(struct input_id); + unsigned struct_midi_info_sz = sizeof(struct midi_info); + unsigned struct_mtget_sz = sizeof(struct mtget); + unsigned struct_mtop_sz = sizeof(struct mtop); + unsigned struct_mtpos_sz = sizeof(struct mtpos); + unsigned struct_rtentry_sz = sizeof(struct rtentry); + unsigned struct_sbi_instrument_sz = sizeof(struct sbi_instrument); + unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec); + unsigned struct_synth_info_sz = sizeof(struct synth_info); + unsigned struct_termio_sz = sizeof(struct termio); + unsigned struct_vt_consize_sz = sizeof(struct vt_consize); + unsigned struct_vt_mode_sz = sizeof(struct vt_mode); + unsigned struct_vt_sizes_sz = sizeof(struct vt_sizes); + unsigned struct_vt_stat_sz = sizeof(struct vt_stat); +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info); + unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct); + unsigned struct_cyclades_monitor_sz = sizeof(struct cyclades_monitor); +#if EV_VERSION > (0x010000) + unsigned struct_input_keymap_entry_sz = sizeof(struct input_keymap_entry); +#else + unsigned struct_input_keymap_entry_sz = 0; +#endif + unsigned struct_ipx_config_data_sz = sizeof(struct ipx_config_data); + unsigned struct_kbdiacrs_sz = sizeof(struct kbdiacrs); + unsigned struct_kbentry_sz = sizeof(struct kbentry); + unsigned struct_kbkeycode_sz = sizeof(struct kbkeycode); + unsigned struct_kbsentry_sz = sizeof(struct kbsentry); + unsigned struct_mtconfiginfo_sz = sizeof(struct mtconfiginfo); + unsigned struct_nr_parms_struct_sz = sizeof(struct nr_parms_struct); + unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats); + unsigned struct_scc_modem_sz = sizeof(struct scc_modem); + unsigned struct_scc_stat_sz = sizeof(struct scc_stat); + unsigned struct_serial_multiport_struct_sz + = sizeof(struct serial_multiport_struct); + unsigned struct_serial_struct_sz = sizeof(struct serial_struct); + unsigned struct_sockaddr_ax25_sz = sizeof(struct sockaddr_ax25); + unsigned struct_unimapdesc_sz = sizeof(struct unimapdesc); + unsigned struct_unimapinit_sz = sizeof(struct unimapinit); +#endif + +#if !SANITIZER_ANDROID && !SANITIZER_MAC + unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req); + unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req); +#endif + + unsigned IOCTL_NOT_PRESENT = 0; + + unsigned IOCTL_FIOASYNC = FIOASYNC; + unsigned IOCTL_FIOCLEX = FIOCLEX; + unsigned IOCTL_FIOGETOWN = FIOGETOWN; + unsigned IOCTL_FIONBIO = FIONBIO; + unsigned IOCTL_FIONCLEX = FIONCLEX; + unsigned IOCTL_FIOSETOWN = FIOSETOWN; + unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI; + unsigned IOCTL_SIOCATMARK = SIOCATMARK; + unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI; + unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR; + unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR; + unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF; + unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR; + unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS; + unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC; + unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU; + unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK; + unsigned IOCTL_SIOCGPGRP = SIOCGPGRP; + unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR; + unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR; + unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR; + unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS; + unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC; + unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU; + unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK; + unsigned IOCTL_SIOCSPGRP = SIOCSPGRP; + unsigned IOCTL_TIOCCONS = TIOCCONS; + unsigned IOCTL_TIOCEXCL = TIOCEXCL; + unsigned IOCTL_TIOCGETD = TIOCGETD; + unsigned IOCTL_TIOCGPGRP = TIOCGPGRP; + unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ; + unsigned IOCTL_TIOCMBIC = TIOCMBIC; + unsigned IOCTL_TIOCMBIS = TIOCMBIS; + unsigned IOCTL_TIOCMGET = TIOCMGET; + unsigned IOCTL_TIOCMSET = TIOCMSET; + unsigned IOCTL_TIOCNOTTY = TIOCNOTTY; + unsigned IOCTL_TIOCNXCL = TIOCNXCL; + unsigned IOCTL_TIOCOUTQ = TIOCOUTQ; + unsigned IOCTL_TIOCPKT = TIOCPKT; + unsigned IOCTL_TIOCSCTTY = TIOCSCTTY; + unsigned IOCTL_TIOCSETD = TIOCSETD; + unsigned IOCTL_TIOCSPGRP = TIOCSPGRP; + unsigned IOCTL_TIOCSTI = TIOCSTI; + unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ; +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) + unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT; + unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT; +#endif +#if SANITIZER_LINUX + unsigned IOCTL_EVIOCGABS = EVIOCGABS(0); + unsigned IOCTL_EVIOCGBIT = EVIOCGBIT(0, 0); + unsigned IOCTL_EVIOCGEFFECTS = EVIOCGEFFECTS; + unsigned IOCTL_EVIOCGID = EVIOCGID; + unsigned IOCTL_EVIOCGKEY = EVIOCGKEY(0); + unsigned IOCTL_EVIOCGKEYCODE = EVIOCGKEYCODE; + unsigned IOCTL_EVIOCGLED = EVIOCGLED(0); + unsigned IOCTL_EVIOCGNAME = EVIOCGNAME(0); + unsigned IOCTL_EVIOCGPHYS = EVIOCGPHYS(0); + unsigned IOCTL_EVIOCGRAB = EVIOCGRAB; + unsigned IOCTL_EVIOCGREP = EVIOCGREP; + unsigned IOCTL_EVIOCGSND = EVIOCGSND(0); + unsigned IOCTL_EVIOCGSW = EVIOCGSW(0); + unsigned IOCTL_EVIOCGUNIQ = EVIOCGUNIQ(0); + unsigned IOCTL_EVIOCGVERSION = EVIOCGVERSION; + unsigned IOCTL_EVIOCRMFF = EVIOCRMFF; + unsigned IOCTL_EVIOCSABS = EVIOCSABS(0); + unsigned IOCTL_EVIOCSFF = EVIOCSFF; + unsigned IOCTL_EVIOCSKEYCODE = EVIOCSKEYCODE; + unsigned IOCTL_EVIOCSREP = EVIOCSREP; + unsigned IOCTL_BLKFLSBUF = BLKFLSBUF; + unsigned IOCTL_BLKGETSIZE = BLKGETSIZE; + unsigned IOCTL_BLKRAGET = BLKRAGET; + unsigned IOCTL_BLKRASET = BLKRASET; + unsigned IOCTL_BLKROGET = BLKROGET; + unsigned IOCTL_BLKROSET = BLKROSET; + unsigned IOCTL_BLKRRPART = BLKRRPART; + unsigned IOCTL_CDROMAUDIOBUFSIZ = CDROMAUDIOBUFSIZ; + unsigned IOCTL_CDROMEJECT = CDROMEJECT; + unsigned IOCTL_CDROMEJECT_SW = CDROMEJECT_SW; + unsigned IOCTL_CDROMMULTISESSION = CDROMMULTISESSION; + unsigned IOCTL_CDROMPAUSE = CDROMPAUSE; + unsigned IOCTL_CDROMPLAYMSF = CDROMPLAYMSF; + unsigned IOCTL_CDROMPLAYTRKIND = CDROMPLAYTRKIND; + unsigned IOCTL_CDROMREADAUDIO = CDROMREADAUDIO; + unsigned IOCTL_CDROMREADCOOKED = CDROMREADCOOKED; + unsigned IOCTL_CDROMREADMODE1 = CDROMREADMODE1; + unsigned IOCTL_CDROMREADMODE2 = CDROMREADMODE2; + unsigned IOCTL_CDROMREADRAW = CDROMREADRAW; + unsigned IOCTL_CDROMREADTOCENTRY = CDROMREADTOCENTRY; + unsigned IOCTL_CDROMREADTOCHDR = CDROMREADTOCHDR; + unsigned IOCTL_CDROMRESET = CDROMRESET; + unsigned IOCTL_CDROMRESUME = CDROMRESUME; + unsigned IOCTL_CDROMSEEK = CDROMSEEK; + unsigned IOCTL_CDROMSTART = CDROMSTART; + unsigned IOCTL_CDROMSTOP = CDROMSTOP; + unsigned IOCTL_CDROMSUBCHNL = CDROMSUBCHNL; + unsigned IOCTL_CDROMVOLCTRL = CDROMVOLCTRL; + unsigned IOCTL_CDROMVOLREAD = CDROMVOLREAD; + unsigned IOCTL_CDROM_GET_UPC = CDROM_GET_UPC; + unsigned IOCTL_FDCLRPRM = FDCLRPRM; + unsigned IOCTL_FDDEFPRM = FDDEFPRM; + unsigned IOCTL_FDFLUSH = FDFLUSH; + unsigned IOCTL_FDFMTBEG = FDFMTBEG; + unsigned IOCTL_FDFMTEND = FDFMTEND; + unsigned IOCTL_FDFMTTRK = FDFMTTRK; + unsigned IOCTL_FDGETDRVPRM = FDGETDRVPRM; + unsigned IOCTL_FDGETDRVSTAT = FDGETDRVSTAT; + unsigned IOCTL_FDGETDRVTYP = FDGETDRVTYP; + unsigned IOCTL_FDGETFDCSTAT = FDGETFDCSTAT; + unsigned IOCTL_FDGETMAXERRS = FDGETMAXERRS; + unsigned IOCTL_FDGETPRM = FDGETPRM; + unsigned IOCTL_FDMSGOFF = FDMSGOFF; + unsigned IOCTL_FDMSGON = FDMSGON; + unsigned IOCTL_FDPOLLDRVSTAT = FDPOLLDRVSTAT; + unsigned IOCTL_FDRAWCMD = FDRAWCMD; + unsigned IOCTL_FDRESET = FDRESET; + unsigned IOCTL_FDSETDRVPRM = FDSETDRVPRM; + unsigned IOCTL_FDSETEMSGTRESH = FDSETEMSGTRESH; + unsigned IOCTL_FDSETMAXERRS = FDSETMAXERRS; + unsigned IOCTL_FDSETPRM = FDSETPRM; + unsigned IOCTL_FDTWADDLE = FDTWADDLE; + unsigned IOCTL_FDWERRORCLR = FDWERRORCLR; + unsigned IOCTL_FDWERRORGET = FDWERRORGET; + unsigned IOCTL_HDIO_DRIVE_CMD = HDIO_DRIVE_CMD; + unsigned IOCTL_HDIO_GETGEO = HDIO_GETGEO; + unsigned IOCTL_HDIO_GET_32BIT = HDIO_GET_32BIT; + unsigned IOCTL_HDIO_GET_DMA = HDIO_GET_DMA; + unsigned IOCTL_HDIO_GET_IDENTITY = HDIO_GET_IDENTITY; + unsigned IOCTL_HDIO_GET_KEEPSETTINGS = HDIO_GET_KEEPSETTINGS; + unsigned IOCTL_HDIO_GET_MULTCOUNT = HDIO_GET_MULTCOUNT; + unsigned IOCTL_HDIO_GET_NOWERR = HDIO_GET_NOWERR; + unsigned IOCTL_HDIO_GET_UNMASKINTR = HDIO_GET_UNMASKINTR; + unsigned IOCTL_HDIO_SET_32BIT = HDIO_SET_32BIT; + unsigned IOCTL_HDIO_SET_DMA = HDIO_SET_DMA; + unsigned IOCTL_HDIO_SET_KEEPSETTINGS = HDIO_SET_KEEPSETTINGS; + unsigned IOCTL_HDIO_SET_MULTCOUNT = HDIO_SET_MULTCOUNT; + unsigned IOCTL_HDIO_SET_NOWERR = HDIO_SET_NOWERR; + unsigned IOCTL_HDIO_SET_UNMASKINTR = HDIO_SET_UNMASKINTR; + unsigned IOCTL_MTIOCGET = MTIOCGET; + unsigned IOCTL_MTIOCPOS = MTIOCPOS; + unsigned IOCTL_MTIOCTOP = MTIOCTOP; + unsigned IOCTL_PPPIOCGASYNCMAP = PPPIOCGASYNCMAP; + unsigned IOCTL_PPPIOCGDEBUG = PPPIOCGDEBUG; + unsigned IOCTL_PPPIOCGFLAGS = PPPIOCGFLAGS; + unsigned IOCTL_PPPIOCGUNIT = PPPIOCGUNIT; + unsigned IOCTL_PPPIOCGXASYNCMAP = PPPIOCGXASYNCMAP; + unsigned IOCTL_PPPIOCSASYNCMAP = PPPIOCSASYNCMAP; + unsigned IOCTL_PPPIOCSDEBUG = PPPIOCSDEBUG; + unsigned IOCTL_PPPIOCSFLAGS = PPPIOCSFLAGS; + unsigned IOCTL_PPPIOCSMAXCID = PPPIOCSMAXCID; + unsigned IOCTL_PPPIOCSMRU = PPPIOCSMRU; + unsigned IOCTL_PPPIOCSXASYNCMAP = PPPIOCSXASYNCMAP; + unsigned IOCTL_SIOCADDRT = SIOCADDRT; + unsigned IOCTL_SIOCDARP = SIOCDARP; + unsigned IOCTL_SIOCDELRT = SIOCDELRT; + unsigned IOCTL_SIOCDRARP = SIOCDRARP; + unsigned IOCTL_SIOCGARP = SIOCGARP; + unsigned IOCTL_SIOCGIFENCAP = SIOCGIFENCAP; + unsigned IOCTL_SIOCGIFHWADDR = SIOCGIFHWADDR; + unsigned IOCTL_SIOCGIFMAP = SIOCGIFMAP; + unsigned IOCTL_SIOCGIFMEM = SIOCGIFMEM; + unsigned IOCTL_SIOCGIFNAME = SIOCGIFNAME; + unsigned IOCTL_SIOCGIFSLAVE = SIOCGIFSLAVE; + unsigned IOCTL_SIOCGRARP = SIOCGRARP; + unsigned IOCTL_SIOCGSTAMP = SIOCGSTAMP; + unsigned IOCTL_SIOCSARP = SIOCSARP; + unsigned IOCTL_SIOCSIFENCAP = SIOCSIFENCAP; + unsigned IOCTL_SIOCSIFHWADDR = SIOCSIFHWADDR; + unsigned IOCTL_SIOCSIFLINK = SIOCSIFLINK; + unsigned IOCTL_SIOCSIFMAP = SIOCSIFMAP; + unsigned IOCTL_SIOCSIFMEM = SIOCSIFMEM; + unsigned IOCTL_SIOCSIFSLAVE = SIOCSIFSLAVE; + unsigned IOCTL_SIOCSRARP = SIOCSRARP; +#if SOUND_VERSION >= 0x040000 + unsigned IOCTL_SNDCTL_COPR_HALT = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_LOAD = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RCODE = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RCVMSG = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RDATA = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RESET = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RUN = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_SENDMSG = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_WCODE = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_WDATA = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_READ_BITS = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_READ_CHANNELS = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_READ_FILTER = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_READ_RATE = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_WRITE_FILTER = IOCTL_NOT_PRESENT; +#else + unsigned IOCTL_SNDCTL_COPR_HALT = SNDCTL_COPR_HALT; + unsigned IOCTL_SNDCTL_COPR_LOAD = SNDCTL_COPR_LOAD; + unsigned IOCTL_SNDCTL_COPR_RCODE = SNDCTL_COPR_RCODE; + unsigned IOCTL_SNDCTL_COPR_RCVMSG = SNDCTL_COPR_RCVMSG; + unsigned IOCTL_SNDCTL_COPR_RDATA = SNDCTL_COPR_RDATA; + unsigned IOCTL_SNDCTL_COPR_RESET = SNDCTL_COPR_RESET; + unsigned IOCTL_SNDCTL_COPR_RUN = SNDCTL_COPR_RUN; + unsigned IOCTL_SNDCTL_COPR_SENDMSG = SNDCTL_COPR_SENDMSG; + unsigned IOCTL_SNDCTL_COPR_WCODE = SNDCTL_COPR_WCODE; + unsigned IOCTL_SNDCTL_COPR_WDATA = SNDCTL_COPR_WDATA; + unsigned IOCTL_SOUND_PCM_READ_BITS = SOUND_PCM_READ_BITS; + unsigned IOCTL_SOUND_PCM_READ_CHANNELS = SOUND_PCM_READ_CHANNELS; + unsigned IOCTL_SOUND_PCM_READ_FILTER = SOUND_PCM_READ_FILTER; + unsigned IOCTL_SOUND_PCM_READ_RATE = SOUND_PCM_READ_RATE; + unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = SOUND_PCM_WRITE_CHANNELS; + unsigned IOCTL_SOUND_PCM_WRITE_FILTER = SOUND_PCM_WRITE_FILTER; +#endif + unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE; + unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS; + unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK; + unsigned IOCTL_SNDCTL_DSP_POST = SNDCTL_DSP_POST; + unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET; + unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT; + unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT; + unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED; + unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO; + unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE; + unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC; + unsigned IOCTL_SNDCTL_FM_4OP_ENABLE = SNDCTL_FM_4OP_ENABLE; + unsigned IOCTL_SNDCTL_FM_LOAD_INSTR = SNDCTL_FM_LOAD_INSTR; + unsigned IOCTL_SNDCTL_MIDI_INFO = SNDCTL_MIDI_INFO; + unsigned IOCTL_SNDCTL_MIDI_PRETIME = SNDCTL_MIDI_PRETIME; + unsigned IOCTL_SNDCTL_SEQ_CTRLRATE = SNDCTL_SEQ_CTRLRATE; + unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT = SNDCTL_SEQ_GETINCOUNT; + unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT = SNDCTL_SEQ_GETOUTCOUNT; + unsigned IOCTL_SNDCTL_SEQ_NRMIDIS = SNDCTL_SEQ_NRMIDIS; + unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS = SNDCTL_SEQ_NRSYNTHS; + unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND = SNDCTL_SEQ_OUTOFBAND; + unsigned IOCTL_SNDCTL_SEQ_PANIC = SNDCTL_SEQ_PANIC; + unsigned IOCTL_SNDCTL_SEQ_PERCMODE = SNDCTL_SEQ_PERCMODE; + unsigned IOCTL_SNDCTL_SEQ_RESET = SNDCTL_SEQ_RESET; + unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES = SNDCTL_SEQ_RESETSAMPLES; + unsigned IOCTL_SNDCTL_SEQ_SYNC = SNDCTL_SEQ_SYNC; + unsigned IOCTL_SNDCTL_SEQ_TESTMIDI = SNDCTL_SEQ_TESTMIDI; + unsigned IOCTL_SNDCTL_SEQ_THRESHOLD = SNDCTL_SEQ_THRESHOLD; + unsigned IOCTL_SNDCTL_SYNTH_INFO = SNDCTL_SYNTH_INFO; + unsigned IOCTL_SNDCTL_SYNTH_MEMAVL = SNDCTL_SYNTH_MEMAVL; + unsigned IOCTL_SNDCTL_TMR_CONTINUE = SNDCTL_TMR_CONTINUE; + unsigned IOCTL_SNDCTL_TMR_METRONOME = SNDCTL_TMR_METRONOME; + unsigned IOCTL_SNDCTL_TMR_SELECT = SNDCTL_TMR_SELECT; + unsigned IOCTL_SNDCTL_TMR_SOURCE = SNDCTL_TMR_SOURCE; + unsigned IOCTL_SNDCTL_TMR_START = SNDCTL_TMR_START; + unsigned IOCTL_SNDCTL_TMR_STOP = SNDCTL_TMR_STOP; + unsigned IOCTL_SNDCTL_TMR_TEMPO = SNDCTL_TMR_TEMPO; + unsigned IOCTL_SNDCTL_TMR_TIMEBASE = SNDCTL_TMR_TIMEBASE; + unsigned IOCTL_SOUND_MIXER_READ_ALTPCM = SOUND_MIXER_READ_ALTPCM; + unsigned IOCTL_SOUND_MIXER_READ_BASS = SOUND_MIXER_READ_BASS; + unsigned IOCTL_SOUND_MIXER_READ_CAPS = SOUND_MIXER_READ_CAPS; + unsigned IOCTL_SOUND_MIXER_READ_CD = SOUND_MIXER_READ_CD; + unsigned IOCTL_SOUND_MIXER_READ_DEVMASK = SOUND_MIXER_READ_DEVMASK; + unsigned IOCTL_SOUND_MIXER_READ_ENHANCE = SOUND_MIXER_READ_ENHANCE; + unsigned IOCTL_SOUND_MIXER_READ_IGAIN = SOUND_MIXER_READ_IGAIN; + unsigned IOCTL_SOUND_MIXER_READ_IMIX = SOUND_MIXER_READ_IMIX; + unsigned IOCTL_SOUND_MIXER_READ_LINE = SOUND_MIXER_READ_LINE; + unsigned IOCTL_SOUND_MIXER_READ_LINE1 = SOUND_MIXER_READ_LINE1; + unsigned IOCTL_SOUND_MIXER_READ_LINE2 = SOUND_MIXER_READ_LINE2; + unsigned IOCTL_SOUND_MIXER_READ_LINE3 = SOUND_MIXER_READ_LINE3; + unsigned IOCTL_SOUND_MIXER_READ_LOUD = SOUND_MIXER_READ_LOUD; + unsigned IOCTL_SOUND_MIXER_READ_MIC = SOUND_MIXER_READ_MIC; + unsigned IOCTL_SOUND_MIXER_READ_MUTE = SOUND_MIXER_READ_MUTE; + unsigned IOCTL_SOUND_MIXER_READ_OGAIN = SOUND_MIXER_READ_OGAIN; + unsigned IOCTL_SOUND_MIXER_READ_PCM = SOUND_MIXER_READ_PCM; + unsigned IOCTL_SOUND_MIXER_READ_RECLEV = SOUND_MIXER_READ_RECLEV; + unsigned IOCTL_SOUND_MIXER_READ_RECMASK = SOUND_MIXER_READ_RECMASK; + unsigned IOCTL_SOUND_MIXER_READ_RECSRC = SOUND_MIXER_READ_RECSRC; + unsigned IOCTL_SOUND_MIXER_READ_SPEAKER = SOUND_MIXER_READ_SPEAKER; + unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS = SOUND_MIXER_READ_STEREODEVS; + unsigned IOCTL_SOUND_MIXER_READ_SYNTH = SOUND_MIXER_READ_SYNTH; + unsigned IOCTL_SOUND_MIXER_READ_TREBLE = SOUND_MIXER_READ_TREBLE; + unsigned IOCTL_SOUND_MIXER_READ_VOLUME = SOUND_MIXER_READ_VOLUME; + unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM = SOUND_MIXER_WRITE_ALTPCM; + unsigned IOCTL_SOUND_MIXER_WRITE_BASS = SOUND_MIXER_WRITE_BASS; + unsigned IOCTL_SOUND_MIXER_WRITE_CD = SOUND_MIXER_WRITE_CD; + unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE = SOUND_MIXER_WRITE_ENHANCE; + unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN = SOUND_MIXER_WRITE_IGAIN; + unsigned IOCTL_SOUND_MIXER_WRITE_IMIX = SOUND_MIXER_WRITE_IMIX; + unsigned IOCTL_SOUND_MIXER_WRITE_LINE = SOUND_MIXER_WRITE_LINE; + unsigned IOCTL_SOUND_MIXER_WRITE_LINE1 = SOUND_MIXER_WRITE_LINE1; + unsigned IOCTL_SOUND_MIXER_WRITE_LINE2 = SOUND_MIXER_WRITE_LINE2; + unsigned IOCTL_SOUND_MIXER_WRITE_LINE3 = SOUND_MIXER_WRITE_LINE3; + unsigned IOCTL_SOUND_MIXER_WRITE_LOUD = SOUND_MIXER_WRITE_LOUD; + unsigned IOCTL_SOUND_MIXER_WRITE_MIC = SOUND_MIXER_WRITE_MIC; + unsigned IOCTL_SOUND_MIXER_WRITE_MUTE = SOUND_MIXER_WRITE_MUTE; + unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN = SOUND_MIXER_WRITE_OGAIN; + unsigned IOCTL_SOUND_MIXER_WRITE_PCM = SOUND_MIXER_WRITE_PCM; + unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV = SOUND_MIXER_WRITE_RECLEV; + unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC = SOUND_MIXER_WRITE_RECSRC; + unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER = SOUND_MIXER_WRITE_SPEAKER; + unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH = SOUND_MIXER_WRITE_SYNTH; + unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE = SOUND_MIXER_WRITE_TREBLE; + unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME = SOUND_MIXER_WRITE_VOLUME; + unsigned IOCTL_TCFLSH = TCFLSH; + unsigned IOCTL_TCGETA = TCGETA; + unsigned IOCTL_TCGETS = TCGETS; + unsigned IOCTL_TCSBRK = TCSBRK; + unsigned IOCTL_TCSBRKP = TCSBRKP; + unsigned IOCTL_TCSETA = TCSETA; + unsigned IOCTL_TCSETAF = TCSETAF; + unsigned IOCTL_TCSETAW = TCSETAW; + unsigned IOCTL_TCSETS = TCSETS; + unsigned IOCTL_TCSETSF = TCSETSF; + unsigned IOCTL_TCSETSW = TCSETSW; + unsigned IOCTL_TCXONC = TCXONC; + unsigned IOCTL_TIOCGLCKTRMIOS = TIOCGLCKTRMIOS; + unsigned IOCTL_TIOCGSOFTCAR = TIOCGSOFTCAR; + unsigned IOCTL_TIOCINQ = TIOCINQ; + unsigned IOCTL_TIOCLINUX = TIOCLINUX; + unsigned IOCTL_TIOCSERCONFIG = TIOCSERCONFIG; + unsigned IOCTL_TIOCSERGETLSR = TIOCSERGETLSR; + unsigned IOCTL_TIOCSERGWILD = TIOCSERGWILD; + unsigned IOCTL_TIOCSERSWILD = TIOCSERSWILD; + unsigned IOCTL_TIOCSLCKTRMIOS = TIOCSLCKTRMIOS; + unsigned IOCTL_TIOCSSOFTCAR = TIOCSSOFTCAR; + unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE; + unsigned IOCTL_VT_DISALLOCATE = VT_DISALLOCATE; + unsigned IOCTL_VT_GETMODE = VT_GETMODE; + unsigned IOCTL_VT_GETSTATE = VT_GETSTATE; + unsigned IOCTL_VT_OPENQRY = VT_OPENQRY; + unsigned IOCTL_VT_RELDISP = VT_RELDISP; + unsigned IOCTL_VT_RESIZE = VT_RESIZE; + unsigned IOCTL_VT_RESIZEX = VT_RESIZEX; + unsigned IOCTL_VT_SENDSIG = VT_SENDSIG; + unsigned IOCTL_VT_SETMODE = VT_SETMODE; + unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE; +#endif +#if SANITIZER_LINUX && !SANITIZER_ANDROID + unsigned IOCTL_CYGETDEFTHRESH = CYGETDEFTHRESH; + unsigned IOCTL_CYGETDEFTIMEOUT = CYGETDEFTIMEOUT; + unsigned IOCTL_CYGETMON = CYGETMON; + unsigned IOCTL_CYGETTHRESH = CYGETTHRESH; + unsigned IOCTL_CYGETTIMEOUT = CYGETTIMEOUT; + unsigned IOCTL_CYSETDEFTHRESH = CYSETDEFTHRESH; + unsigned IOCTL_CYSETDEFTIMEOUT = CYSETDEFTIMEOUT; + unsigned IOCTL_CYSETTHRESH = CYSETTHRESH; + unsigned IOCTL_CYSETTIMEOUT = CYSETTIMEOUT; + unsigned IOCTL_EQL_EMANCIPATE = EQL_EMANCIPATE; + unsigned IOCTL_EQL_ENSLAVE = EQL_ENSLAVE; + unsigned IOCTL_EQL_GETMASTRCFG = EQL_GETMASTRCFG; + unsigned IOCTL_EQL_GETSLAVECFG = EQL_GETSLAVECFG; + unsigned IOCTL_EQL_SETMASTRCFG = EQL_SETMASTRCFG; + unsigned IOCTL_EQL_SETSLAVECFG = EQL_SETSLAVECFG; +#if EV_VERSION > (0x010000) + unsigned IOCTL_EVIOCGKEYCODE_V2 = EVIOCGKEYCODE_V2; + unsigned IOCTL_EVIOCGPROP = EVIOCGPROP(0); + unsigned IOCTL_EVIOCSKEYCODE_V2 = EVIOCSKEYCODE_V2; +#else + unsigned IOCTL_EVIOCGKEYCODE_V2 = IOCTL_NOT_PRESENT; + unsigned IOCTL_EVIOCGPROP = IOCTL_NOT_PRESENT; + unsigned IOCTL_EVIOCSKEYCODE_V2 = IOCTL_NOT_PRESENT; +#endif + unsigned IOCTL_FS_IOC_GETFLAGS = FS_IOC_GETFLAGS; + unsigned IOCTL_FS_IOC_GETVERSION = FS_IOC_GETVERSION; + unsigned IOCTL_FS_IOC_SETFLAGS = FS_IOC_SETFLAGS; + unsigned IOCTL_FS_IOC_SETVERSION = FS_IOC_SETVERSION; + unsigned IOCTL_GIO_CMAP = GIO_CMAP; + unsigned IOCTL_GIO_FONT = GIO_FONT; + unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP; + unsigned IOCTL_GIO_UNIMAP = GIO_UNIMAP; + unsigned IOCTL_GIO_UNISCRNMAP = GIO_UNISCRNMAP; + unsigned IOCTL_KDADDIO = KDADDIO; + unsigned IOCTL_KDDELIO = KDDELIO; + unsigned IOCTL_KDDISABIO = KDDISABIO; + unsigned IOCTL_KDENABIO = KDENABIO; + unsigned IOCTL_KDGETKEYCODE = KDGETKEYCODE; + unsigned IOCTL_KDGETLED = KDGETLED; + unsigned IOCTL_KDGETMODE = KDGETMODE; + unsigned IOCTL_KDGKBDIACR = KDGKBDIACR; + unsigned IOCTL_KDGKBENT = KDGKBENT; + unsigned IOCTL_KDGKBLED = KDGKBLED; + unsigned IOCTL_KDGKBMETA = KDGKBMETA; + unsigned IOCTL_KDGKBMODE = KDGKBMODE; + unsigned IOCTL_KDGKBSENT = KDGKBSENT; + unsigned IOCTL_KDGKBTYPE = KDGKBTYPE; + unsigned IOCTL_KDMAPDISP = KDMAPDISP; + unsigned IOCTL_KDMKTONE = KDMKTONE; + unsigned IOCTL_KDSETKEYCODE = KDSETKEYCODE; + unsigned IOCTL_KDSETLED = KDSETLED; + unsigned IOCTL_KDSETMODE = KDSETMODE; + unsigned IOCTL_KDSIGACCEPT = KDSIGACCEPT; + unsigned IOCTL_KDSKBDIACR = KDSKBDIACR; + unsigned IOCTL_KDSKBENT = KDSKBENT; + unsigned IOCTL_KDSKBLED = KDSKBLED; + unsigned IOCTL_KDSKBMETA = KDSKBMETA; + unsigned IOCTL_KDSKBMODE = KDSKBMODE; + unsigned IOCTL_KDSKBSENT = KDSKBSENT; + unsigned IOCTL_KDUNMAPDISP = KDUNMAPDISP; + unsigned IOCTL_KIOCSOUND = KIOCSOUND; + unsigned IOCTL_LPABORT = LPABORT; + unsigned IOCTL_LPABORTOPEN = LPABORTOPEN; + unsigned IOCTL_LPCAREFUL = LPCAREFUL; + unsigned IOCTL_LPCHAR = LPCHAR; + unsigned IOCTL_LPGETIRQ = LPGETIRQ; + unsigned IOCTL_LPGETSTATUS = LPGETSTATUS; + unsigned IOCTL_LPRESET = LPRESET; + unsigned IOCTL_LPSETIRQ = LPSETIRQ; + unsigned IOCTL_LPTIME = LPTIME; + unsigned IOCTL_LPWAIT = LPWAIT; + unsigned IOCTL_MTIOCGETCONFIG = MTIOCGETCONFIG; + unsigned IOCTL_MTIOCSETCONFIG = MTIOCSETCONFIG; + unsigned IOCTL_PIO_CMAP = PIO_CMAP; + unsigned IOCTL_PIO_FONT = PIO_FONT; + unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP; + unsigned IOCTL_PIO_UNIMAP = PIO_UNIMAP; + unsigned IOCTL_PIO_UNIMAPCLR = PIO_UNIMAPCLR; + unsigned IOCTL_PIO_UNISCRNMAP = PIO_UNISCRNMAP; + unsigned IOCTL_SCSI_IOCTL_GET_IDLUN = SCSI_IOCTL_GET_IDLUN; + unsigned IOCTL_SCSI_IOCTL_PROBE_HOST = SCSI_IOCTL_PROBE_HOST; + unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE = SCSI_IOCTL_TAGGED_DISABLE; + unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE = SCSI_IOCTL_TAGGED_ENABLE; + unsigned IOCTL_SIOCAIPXITFCRT = SIOCAIPXITFCRT; + unsigned IOCTL_SIOCAIPXPRISLT = SIOCAIPXPRISLT; + unsigned IOCTL_SIOCAX25ADDUID = SIOCAX25ADDUID; + unsigned IOCTL_SIOCAX25DELUID = SIOCAX25DELUID; + unsigned IOCTL_SIOCAX25GETPARMS = SIOCAX25GETPARMS; + unsigned IOCTL_SIOCAX25GETUID = SIOCAX25GETUID; + unsigned IOCTL_SIOCAX25NOUID = SIOCAX25NOUID; + unsigned IOCTL_SIOCAX25SETPARMS = SIOCAX25SETPARMS; + unsigned IOCTL_SIOCDEVPLIP = SIOCDEVPLIP; + unsigned IOCTL_SIOCIPXCFGDATA = SIOCIPXCFGDATA; + unsigned IOCTL_SIOCNRDECOBS = SIOCNRDECOBS; + unsigned IOCTL_SIOCNRGETPARMS = SIOCNRGETPARMS; + unsigned IOCTL_SIOCNRRTCTL = SIOCNRRTCTL; + unsigned IOCTL_SIOCNRSETPARMS = SIOCNRSETPARMS; + unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE; + unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE; + unsigned IOCTL_TIOCGSERIAL = TIOCGSERIAL; + unsigned IOCTL_TIOCSERGETMULTI = TIOCSERGETMULTI; + unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI; + unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL; +#endif + +// EOWNERDEAD is not present in some older platforms. +#if defined(EOWNERDEAD) + extern const int errno_EOWNERDEAD = EOWNERDEAD; +#else + extern const int errno_EOWNERDEAD = -1; +#endif } // namespace __sanitizer COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t)); -COMPILER_CHECK(sizeof(__sanitizer::struct_sigaction_max_sz) >= - sizeof(__sanitizer::struct_sigaction_sz)); + +COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned)); +CHECK_TYPE_SIZE(pthread_key_t); + #if SANITIZER_LINUX -COMPILER_CHECK(offsetof(struct __sanitizer_dl_phdr_info, dlpi_addr) == - offsetof(struct dl_phdr_info, dlpi_addr)); -COMPILER_CHECK(offsetof(struct __sanitizer_dl_phdr_info, dlpi_name) == - offsetof(struct dl_phdr_info, dlpi_name)); -COMPILER_CHECK(offsetof(struct __sanitizer_dl_phdr_info, dlpi_phdr) == - offsetof(struct dl_phdr_info, dlpi_phdr)); -COMPILER_CHECK(offsetof(struct __sanitizer_dl_phdr_info, dlpi_phnum) == - offsetof(struct dl_phdr_info, dlpi_phnum)); -#endif - -COMPILER_CHECK(sizeof(struct __sanitizer_addrinfo) == sizeof(struct addrinfo)); -COMPILER_CHECK(offsetof(struct __sanitizer_addrinfo, ai_addr) == - offsetof(struct addrinfo, ai_addr)); -COMPILER_CHECK(offsetof(struct __sanitizer_addrinfo, ai_canonname) == - offsetof(struct addrinfo, ai_canonname)); -COMPILER_CHECK(offsetof(struct __sanitizer_addrinfo, ai_next) == - offsetof(struct addrinfo, ai_next)); - -COMPILER_CHECK(sizeof(struct __sanitizer_hostent) == sizeof(struct hostent)); -COMPILER_CHECK(offsetof(struct __sanitizer_hostent, h_name) == - offsetof(struct hostent, h_name)); -COMPILER_CHECK(offsetof(struct __sanitizer_hostent, h_aliases) == - offsetof(struct hostent, h_aliases)); -COMPILER_CHECK(offsetof(struct __sanitizer_hostent, h_addr_list) == - offsetof(struct hostent, h_addr_list)); +// There are more undocumented fields in dl_phdr_info that we are not interested +// in. +COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info)); +CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr); +CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name); +CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr); +CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum); + +COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678)); +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +CHECK_TYPE_SIZE(glob_t); +CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc); +CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv); +CHECK_SIZE_AND_OFFSET(glob_t, gl_offs); +CHECK_SIZE_AND_OFFSET(glob_t, gl_flags); +CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir); +CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir); +CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir); +CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat); +CHECK_SIZE_AND_OFFSET(glob_t, gl_stat); +#endif + +CHECK_TYPE_SIZE(addrinfo); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_family); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr); + +CHECK_TYPE_SIZE(hostent); +CHECK_SIZE_AND_OFFSET(hostent, h_name); +CHECK_SIZE_AND_OFFSET(hostent, h_aliases); +CHECK_SIZE_AND_OFFSET(hostent, h_addrtype); +CHECK_SIZE_AND_OFFSET(hostent, h_length); +CHECK_SIZE_AND_OFFSET(hostent, h_addr_list); + +CHECK_TYPE_SIZE(iovec); +CHECK_SIZE_AND_OFFSET(iovec, iov_base); +CHECK_SIZE_AND_OFFSET(iovec, iov_len); + +CHECK_TYPE_SIZE(msghdr); +CHECK_SIZE_AND_OFFSET(msghdr, msg_name); +CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen); +CHECK_SIZE_AND_OFFSET(msghdr, msg_iov); +CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen); +CHECK_SIZE_AND_OFFSET(msghdr, msg_control); +CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen); +CHECK_SIZE_AND_OFFSET(msghdr, msg_flags); + +CHECK_TYPE_SIZE(cmsghdr); +CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len); +CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level); +CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type); + +COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent)); +CHECK_SIZE_AND_OFFSET(dirent, d_ino); +#if SANITIZER_MAC +CHECK_SIZE_AND_OFFSET(dirent, d_seekoff); +#else +CHECK_SIZE_AND_OFFSET(dirent, d_off); +#endif +CHECK_SIZE_AND_OFFSET(dirent, d_reclen); + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +COMPILER_CHECK(sizeof(__sanitizer_dirent64) <= sizeof(dirent64)); +CHECK_SIZE_AND_OFFSET(dirent64, d_ino); +CHECK_SIZE_AND_OFFSET(dirent64, d_off); +CHECK_SIZE_AND_OFFSET(dirent64, d_reclen); +#endif + +CHECK_TYPE_SIZE(ifconf); +CHECK_SIZE_AND_OFFSET(ifconf, ifc_len); +CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu); + +CHECK_TYPE_SIZE(pollfd); +CHECK_SIZE_AND_OFFSET(pollfd, fd); +CHECK_SIZE_AND_OFFSET(pollfd, events); +CHECK_SIZE_AND_OFFSET(pollfd, revents); + +CHECK_TYPE_SIZE(nfds_t); + +CHECK_TYPE_SIZE(sigset_t); + +COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction)); +// Can't write checks for sa_handler and sa_sigaction due to them being +// preprocessor macros. +CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask); +CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags); +#if SANITIZER_LINUX +CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_restorer); +#endif + +#if SANITIZER_LINUX +CHECK_TYPE_SIZE(__sysctl_args); +CHECK_SIZE_AND_OFFSET(__sysctl_args, name); +CHECK_SIZE_AND_OFFSET(__sysctl_args, nlen); +CHECK_SIZE_AND_OFFSET(__sysctl_args, oldval); +CHECK_SIZE_AND_OFFSET(__sysctl_args, oldlenp); +CHECK_SIZE_AND_OFFSET(__sysctl_args, newval); +CHECK_SIZE_AND_OFFSET(__sysctl_args, newlen); + +CHECK_TYPE_SIZE(__kernel_uid_t); +CHECK_TYPE_SIZE(__kernel_gid_t); +CHECK_TYPE_SIZE(__kernel_old_uid_t); +CHECK_TYPE_SIZE(__kernel_old_gid_t); +CHECK_TYPE_SIZE(__kernel_off_t); +CHECK_TYPE_SIZE(__kernel_loff_t); +CHECK_TYPE_SIZE(__kernel_fd_set); +#endif + +#if !SANITIZER_ANDROID +CHECK_TYPE_SIZE(wordexp_t); +CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc); +CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv); +CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs); +#endif + +CHECK_TYPE_SIZE(tm); +CHECK_SIZE_AND_OFFSET(tm, tm_sec); +CHECK_SIZE_AND_OFFSET(tm, tm_min); +CHECK_SIZE_AND_OFFSET(tm, tm_hour); +CHECK_SIZE_AND_OFFSET(tm, tm_mday); +CHECK_SIZE_AND_OFFSET(tm, tm_mon); +CHECK_SIZE_AND_OFFSET(tm, tm_year); +CHECK_SIZE_AND_OFFSET(tm, tm_wday); +CHECK_SIZE_AND_OFFSET(tm, tm_yday); +CHECK_SIZE_AND_OFFSET(tm, tm_isdst); +CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff); +CHECK_SIZE_AND_OFFSET(tm, tm_zone); + +#if SANITIZER_LINUX +CHECK_TYPE_SIZE(mntent); +CHECK_SIZE_AND_OFFSET(mntent, mnt_fsname); +CHECK_SIZE_AND_OFFSET(mntent, mnt_dir); +CHECK_SIZE_AND_OFFSET(mntent, mnt_type); +CHECK_SIZE_AND_OFFSET(mntent, mnt_opts); +CHECK_SIZE_AND_OFFSET(mntent, mnt_freq); +CHECK_SIZE_AND_OFFSET(mntent, mnt_passno); +#endif + +CHECK_TYPE_SIZE(ether_addr); + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +CHECK_TYPE_SIZE(ipc_perm); +CHECK_SIZE_AND_OFFSET(ipc_perm, __key); +CHECK_SIZE_AND_OFFSET(ipc_perm, uid); +CHECK_SIZE_AND_OFFSET(ipc_perm, gid); +CHECK_SIZE_AND_OFFSET(ipc_perm, cuid); +CHECK_SIZE_AND_OFFSET(ipc_perm, cgid); +CHECK_SIZE_AND_OFFSET(ipc_perm, mode); +CHECK_SIZE_AND_OFFSET(ipc_perm, __seq); + +CHECK_TYPE_SIZE(shmid_ds); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch); +#endif #endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h index 37581953db24..b9a0fc98c017 100644 --- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h @@ -15,44 +15,306 @@ #ifndef SANITIZER_PLATFORM_LIMITS_POSIX_H #define SANITIZER_PLATFORM_LIMITS_POSIX_H +#include "sanitizer_internal_defs.h" #include "sanitizer_platform.h" namespace __sanitizer { extern unsigned struct_utsname_sz; extern unsigned struct_stat_sz; +#if !SANITIZER_IOS extern unsigned struct_stat64_sz; +#endif extern unsigned struct_rusage_sz; - extern unsigned struct_tm_sz; extern unsigned struct_passwd_sz; extern unsigned struct_group_sz; - extern unsigned struct_sigaction_sz; extern unsigned siginfo_t_sz; extern unsigned struct_itimerval_sz; extern unsigned pthread_t_sz; - extern unsigned struct_sockaddr_sz; + extern unsigned pthread_cond_t_sz; + extern unsigned pid_t_sz; + extern unsigned timeval_sz; + extern unsigned uid_t_sz; + extern unsigned mbstate_t_sz; + extern unsigned struct_timezone_sz; + extern unsigned struct_tms_sz; + extern unsigned struct_itimerspec_sz; + extern unsigned struct_sigevent_sz; + extern unsigned struct_sched_param_sz; + extern unsigned struct_statfs_sz; + extern unsigned struct_statfs64_sz; #if !SANITIZER_ANDROID extern unsigned ucontext_t_sz; #endif // !SANITIZER_ANDROID #if SANITIZER_LINUX + +#if defined(__x86_64__) + const unsigned struct___old_kernel_stat_sz = 32; + const unsigned struct_kernel_stat_sz = 144; + const unsigned struct_kernel_stat64_sz = 0; +#elif defined(__i386__) + const unsigned struct___old_kernel_stat_sz = 32; + const unsigned struct_kernel_stat_sz = 64; + const unsigned struct_kernel_stat64_sz = 96; +#elif defined(__arm__) + const unsigned struct___old_kernel_stat_sz = 32; + const unsigned struct_kernel_stat_sz = 64; + const unsigned struct_kernel_stat64_sz = 104; +#elif defined(__powerpc__) && !defined(__powerpc64__) + const unsigned struct___old_kernel_stat_sz = 32; + const unsigned struct_kernel_stat_sz = 72; + const unsigned struct_kernel_stat64_sz = 104; +#elif defined(__powerpc64__) + const unsigned struct___old_kernel_stat_sz = 0; + const unsigned struct_kernel_stat_sz = 144; + const unsigned struct_kernel_stat64_sz = 104; +#endif + const unsigned struct_io_event_sz = 32; + struct __sanitizer_perf_event_attr { + unsigned type; + unsigned size; + // More fields that vary with the kernel version. + }; + + extern unsigned struct_utimbuf_sz; + extern unsigned struct_new_utsname_sz; + extern unsigned struct_old_utsname_sz; + extern unsigned struct_oldold_utsname_sz; + extern unsigned struct_msqid_ds_sz; + extern unsigned struct_mq_attr_sz; + extern unsigned struct_timex_sz; + extern unsigned struct_ustat_sz; + extern unsigned struct_rlimit_sz; - extern unsigned struct_dirent_sz; - extern unsigned struct_statfs_sz; extern unsigned struct_epoll_event_sz; + extern unsigned struct_sysinfo_sz; extern unsigned struct_timespec_sz; + extern unsigned __user_cap_header_struct_sz; + extern unsigned __user_cap_data_struct_sz; + const unsigned old_sigset_t_sz = sizeof(unsigned long); + const unsigned struct_kexec_segment_sz = 4 * sizeof(unsigned long); + + struct __sanitizer_iocb { + u64 aio_data; + u32 aio_key_or_aio_reserved1; // Simply crazy. + u32 aio_reserved1_or_aio_key; // Luckily, we don't need these. + u16 aio_lio_opcode; + s16 aio_reqprio; + u32 aio_fildes; + u64 aio_buf; + u64 aio_nbytes; + s64 aio_offset; + u64 aio_reserved2; + u64 aio_reserved3; + }; + + const unsigned iocb_cmd_pread = 0; + const unsigned iocb_cmd_pwrite = 1; + + struct __sanitizer___sysctl_args { + int *name; + int nlen; + void *oldval; + uptr *oldlenp; + void *newval; + uptr newlen; + unsigned long ___unused[4]; + }; #endif // SANITIZER_LINUX #if SANITIZER_LINUX && !SANITIZER_ANDROID - extern unsigned struct_dirent64_sz; extern unsigned struct_rlimit64_sz; - extern unsigned struct_statfs64_sz; -#endif // SANITIZER_LINUX && !SANITIZER_ANDROID + extern unsigned struct_statvfs_sz; + extern unsigned struct_statvfs64_sz; + + struct __sanitizer_ipc_perm { + int __key; + int uid; + int gid; + int cuid; + int cgid; +#ifdef __powerpc__ + unsigned mode; + unsigned __seq; + u64 __unused1; + u64 __unused2; +#else + unsigned short mode; + unsigned short __pad1; + unsigned short __seq; + unsigned short __pad2; +#if defined(__x86_64__) && !defined(_LP64) + u64 __unused1; + u64 __unused2; +#else + unsigned long __unused1; + unsigned long __unused2; +#endif +#endif + }; + + struct __sanitizer_shmid_ds { + __sanitizer_ipc_perm shm_perm; + #ifndef __powerpc__ + uptr shm_segsz; + #elif !defined(__powerpc64__) + uptr __unused0; + #endif + uptr shm_atime; + #ifndef _LP64 + uptr __unused1; + #endif + uptr shm_dtime; + #ifndef _LP64 + uptr __unused2; + #endif + uptr shm_ctime; + #ifndef _LP64 + uptr __unused3; + #endif + #ifdef __powerpc__ + uptr shm_segsz; + #endif + int shm_cpid; + int shm_lpid; + uptr shm_nattch; + uptr __unused4; + uptr __unused5; + }; + #endif // SANITIZER_LINUX && !SANITIZER_ANDROID + + struct __sanitizer_iovec { + void *iov_base; + uptr iov_len; + }; + +#if SANITIZER_MAC + typedef unsigned long __sanitizer_pthread_key_t; +#else + typedef unsigned __sanitizer_pthread_key_t; +#endif + + struct __sanitizer_ether_addr { + u8 octet[6]; + }; + + struct __sanitizer_tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; + long int tm_gmtoff; + const char *tm_zone; + }; + +#if SANITIZER_LINUX + struct __sanitizer_mntent { + char *mnt_fsname; + char *mnt_dir; + char *mnt_type; + char *mnt_opts; + int mnt_freq; + int mnt_passno; + }; +#endif + +#if SANITIZER_ANDROID || SANITIZER_MAC + struct __sanitizer_msghdr { + void *msg_name; + unsigned msg_namelen; + struct __sanitizer_iovec *msg_iov; + unsigned msg_iovlen; + void *msg_control; + unsigned msg_controllen; + int msg_flags; + }; + struct __sanitizer_cmsghdr { + unsigned cmsg_len; + int cmsg_level; + int cmsg_type; + }; +#else + struct __sanitizer_msghdr { + void *msg_name; + unsigned msg_namelen; + struct __sanitizer_iovec *msg_iov; + uptr msg_iovlen; + void *msg_control; + uptr msg_controllen; + int msg_flags; + }; + struct __sanitizer_cmsghdr { + uptr cmsg_len; + int cmsg_level; + int cmsg_type; + }; +#endif + +#if SANITIZER_MAC + struct __sanitizer_dirent { + unsigned long long d_ino; + unsigned long long d_seekoff; + unsigned short d_reclen; + // more fields that we don't care about + }; +#elif SANITIZER_ANDROID || defined(__x86_64__) + struct __sanitizer_dirent { + unsigned long long d_ino; + unsigned long long d_off; + unsigned short d_reclen; + // more fields that we don't care about + }; +#else + struct __sanitizer_dirent { + uptr d_ino; + uptr d_off; + unsigned short d_reclen; + // more fields that we don't care about + }; +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + struct __sanitizer_dirent64 { + unsigned long long d_ino; + unsigned long long d_off; + unsigned short d_reclen; + // more fields that we don't care about + }; +#endif - void* __sanitizer_get_msghdr_iov_iov_base(void* msg, int idx); - uptr __sanitizer_get_msghdr_iov_iov_len(void* msg, int idx); - uptr __sanitizer_get_msghdr_iovlen(void* msg); - uptr __sanitizer_get_socklen_t(void* socklen_ptr); +#if SANITIZER_LINUX +#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__) + typedef unsigned __sanitizer___kernel_uid_t; + typedef unsigned __sanitizer___kernel_gid_t; +#else + typedef unsigned short __sanitizer___kernel_uid_t; + typedef unsigned short __sanitizer___kernel_gid_t; +#endif +#if defined(__x86_64__) && !defined(_LP64) + typedef long long __sanitizer___kernel_off_t; +#else + typedef long __sanitizer___kernel_off_t; +#endif + +#if defined(__powerpc__) + typedef unsigned int __sanitizer___kernel_old_uid_t; + typedef unsigned int __sanitizer___kernel_old_gid_t; +#else + typedef unsigned short __sanitizer___kernel_old_uid_t; + typedef unsigned short __sanitizer___kernel_old_gid_t; +#endif + + typedef long long __sanitizer___kernel_loff_t; + typedef struct { + unsigned long fds_bits[1024 / (8 * sizeof(long))]; + } __sanitizer___kernel_fd_set; +#endif // This thing depends on the platform. We are only interested in the upper // limit. Verified with a compiler assert in .cc. @@ -62,18 +324,53 @@ namespace __sanitizer { void *align; }; - uptr __sanitizer_get_sigaction_sa_sigaction(void *act); - void __sanitizer_set_sigaction_sa_sigaction(void *act, uptr cb); - bool __sanitizer_get_sigaction_sa_siginfo(void *act); +#if SANITIZER_ANDROID + typedef unsigned long __sanitizer_sigset_t; +#elif SANITIZER_MAC + typedef unsigned __sanitizer_sigset_t; +#elif SANITIZER_LINUX + struct __sanitizer_sigset_t { + // The size is determined by looking at sizeof of real sigset_t on linux. + uptr val[128 / sizeof(uptr)]; + }; +#endif + + struct __sanitizer_sigaction { + union { + void (*sa_handler)(int sig); + void (*sa_sigaction)(int sig, void *siginfo, void *uctx); + }; + __sanitizer_sigset_t sa_mask; + int sa_flags; +#if SANITIZER_LINUX + void (*sa_restorer)(); +#endif + }; + + struct __sanitizer_kernel_sigset_t { + u8 sig[8]; + }; - const unsigned struct_sigaction_max_sz = 256; - union __sanitizer_sigaction { - char size[struct_sigaction_max_sz]; // NOLINT + struct __sanitizer_kernel_sigaction_t { + union { + void (*sigaction)(int signo, void *info, void *ctx); + void (*handler)(int signo); + }; + unsigned long sa_flags; + void (*sa_restorer)(void); + __sanitizer_kernel_sigset_t sa_mask; }; extern uptr sig_ign; extern uptr sig_dfl; + extern uptr sa_siginfo; + +#if SANITIZER_LINUX + extern int e_tabsz; +#endif + extern int af_inet; + extern int af_inet6; uptr __sanitizer_in_addr_sz(int af); #if SANITIZER_LINUX @@ -95,7 +392,7 @@ namespace __sanitizer { char *ai_canonname; void *ai_addr; #else // LINUX - uptr ai_addrlen; + unsigned ai_addrlen; void *ai_addr; char *ai_canonname; #endif @@ -110,6 +407,591 @@ namespace __sanitizer { char **h_addr_list; }; + struct __sanitizer_pollfd { + int fd; + short events; + short revents; + }; + +#if SANITIZER_ANDROID || SANITIZER_MAC + typedef unsigned __sanitizer_nfds_t; +#else + typedef unsigned long __sanitizer_nfds_t; +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + struct __sanitizer_glob_t { + uptr gl_pathc; + char **gl_pathv; + uptr gl_offs; + int gl_flags; + + void (*gl_closedir)(void *dirp); + void *(*gl_readdir)(void *dirp); + void *(*gl_opendir)(const char *); + int (*gl_lstat)(const char *, void *); + int (*gl_stat)(const char *, void *); + }; + + extern int glob_nomatch; + extern int glob_altdirfunc; +#endif + + extern unsigned path_max; + + struct __sanitizer_wordexp_t { + uptr we_wordc; + char **we_wordv; + uptr we_offs; + }; + +#if SANITIZER_LINUX && !SANITIZER_ANDROID && \ + (defined(__i386) || defined (__x86_64)) // NOLINT + extern unsigned struct_user_regs_struct_sz; + extern unsigned struct_user_fpregs_struct_sz; + extern unsigned struct_user_fpxregs_struct_sz; + + extern int ptrace_peektext; + extern int ptrace_peekdata; + extern int ptrace_peekuser; + extern int ptrace_getregs; + extern int ptrace_setregs; + extern int ptrace_getfpregs; + extern int ptrace_setfpregs; + extern int ptrace_getfpxregs; + extern int ptrace_setfpxregs; + extern int ptrace_getsiginfo; + extern int ptrace_setsiginfo; + extern int ptrace_getregset; + extern int ptrace_setregset; +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + extern unsigned struct_shminfo_sz; + extern unsigned struct_shm_info_sz; + extern int shmctl_ipc_stat; + extern int shmctl_ipc_info; + extern int shmctl_shm_info; + extern int shmctl_shm_stat; +#endif + + // ioctl arguments + struct __sanitizer_ifconf { + int ifc_len; + union { + void *ifcu_req; + } ifc_ifcu; +#if SANITIZER_MAC + } __attribute__((packed)); +#else + }; +#endif + +#define IOC_SIZE(nr) (((nr) >> 16) & 0x3fff) + + extern unsigned struct_arpreq_sz; + extern unsigned struct_ifreq_sz; + extern unsigned struct_termios_sz; + extern unsigned struct_winsize_sz; + +#if SANITIZER_LINUX + extern unsigned struct_cdrom_msf_sz; + extern unsigned struct_cdrom_multisession_sz; + extern unsigned struct_cdrom_read_audio_sz; + extern unsigned struct_cdrom_subchnl_sz; + extern unsigned struct_cdrom_ti_sz; + extern unsigned struct_cdrom_tocentry_sz; + extern unsigned struct_cdrom_tochdr_sz; + extern unsigned struct_cdrom_volctrl_sz; + extern unsigned struct_copr_buffer_sz; + extern unsigned struct_copr_debug_buf_sz; + extern unsigned struct_copr_msg_sz; + extern unsigned struct_ff_effect_sz; + extern unsigned struct_floppy_drive_params_sz; + extern unsigned struct_floppy_drive_struct_sz; + extern unsigned struct_floppy_fdc_state_sz; + extern unsigned struct_floppy_max_errors_sz; + extern unsigned struct_floppy_raw_cmd_sz; + extern unsigned struct_floppy_struct_sz; + extern unsigned struct_floppy_write_errors_sz; + extern unsigned struct_format_descr_sz; + extern unsigned struct_hd_driveid_sz; + extern unsigned struct_hd_geometry_sz; + extern unsigned struct_input_absinfo_sz; + extern unsigned struct_input_id_sz; + extern unsigned struct_midi_info_sz; + extern unsigned struct_mtget_sz; + extern unsigned struct_mtop_sz; + extern unsigned struct_mtpos_sz; + extern unsigned struct_rtentry_sz; + extern unsigned struct_sbi_instrument_sz; + extern unsigned struct_seq_event_rec_sz; + extern unsigned struct_synth_info_sz; + extern unsigned struct_termio_sz; + extern unsigned struct_vt_consize_sz; + extern unsigned struct_vt_mode_sz; + extern unsigned struct_vt_sizes_sz; + extern unsigned struct_vt_stat_sz; +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + extern unsigned struct_audio_buf_info_sz; + extern unsigned struct_ax25_parms_struct_sz; + extern unsigned struct_cyclades_monitor_sz; + extern unsigned struct_input_keymap_entry_sz; + extern unsigned struct_ipx_config_data_sz; + extern unsigned struct_kbdiacrs_sz; + extern unsigned struct_kbentry_sz; + extern unsigned struct_kbkeycode_sz; + extern unsigned struct_kbsentry_sz; + extern unsigned struct_mtconfiginfo_sz; + extern unsigned struct_nr_parms_struct_sz; + extern unsigned struct_ppp_stats_sz; + extern unsigned struct_scc_modem_sz; + extern unsigned struct_scc_stat_sz; + extern unsigned struct_serial_multiport_struct_sz; + extern unsigned struct_serial_struct_sz; + extern unsigned struct_sockaddr_ax25_sz; + extern unsigned struct_unimapdesc_sz; + extern unsigned struct_unimapinit_sz; +#endif + +#if !SANITIZER_ANDROID && !SANITIZER_MAC + extern unsigned struct_sioc_sg_req_sz; + extern unsigned struct_sioc_vif_req_sz; +#endif + + // ioctl request identifiers + + // A special value to mark ioctls that are not present on the target platform, + // when it can not be determined without including any system headers. + extern unsigned IOCTL_NOT_PRESENT; + + extern unsigned IOCTL_FIOASYNC; + extern unsigned IOCTL_FIOCLEX; + extern unsigned IOCTL_FIOGETOWN; + extern unsigned IOCTL_FIONBIO; + extern unsigned IOCTL_FIONCLEX; + extern unsigned IOCTL_FIOSETOWN; + extern unsigned IOCTL_SIOCADDMULTI; + extern unsigned IOCTL_SIOCATMARK; + extern unsigned IOCTL_SIOCDELMULTI; + extern unsigned IOCTL_SIOCGIFADDR; + extern unsigned IOCTL_SIOCGIFBRDADDR; + extern unsigned IOCTL_SIOCGIFCONF; + extern unsigned IOCTL_SIOCGIFDSTADDR; + extern unsigned IOCTL_SIOCGIFFLAGS; + extern unsigned IOCTL_SIOCGIFMETRIC; + extern unsigned IOCTL_SIOCGIFMTU; + extern unsigned IOCTL_SIOCGIFNETMASK; + extern unsigned IOCTL_SIOCGPGRP; + extern unsigned IOCTL_SIOCSIFADDR; + extern unsigned IOCTL_SIOCSIFBRDADDR; + extern unsigned IOCTL_SIOCSIFDSTADDR; + extern unsigned IOCTL_SIOCSIFFLAGS; + extern unsigned IOCTL_SIOCSIFMETRIC; + extern unsigned IOCTL_SIOCSIFMTU; + extern unsigned IOCTL_SIOCSIFNETMASK; + extern unsigned IOCTL_SIOCSPGRP; + extern unsigned IOCTL_TIOCCONS; + extern unsigned IOCTL_TIOCEXCL; + extern unsigned IOCTL_TIOCGETD; + extern unsigned IOCTL_TIOCGPGRP; + extern unsigned IOCTL_TIOCGWINSZ; + extern unsigned IOCTL_TIOCMBIC; + extern unsigned IOCTL_TIOCMBIS; + extern unsigned IOCTL_TIOCMGET; + extern unsigned IOCTL_TIOCMSET; + extern unsigned IOCTL_TIOCNOTTY; + extern unsigned IOCTL_TIOCNXCL; + extern unsigned IOCTL_TIOCOUTQ; + extern unsigned IOCTL_TIOCPKT; + extern unsigned IOCTL_TIOCSCTTY; + extern unsigned IOCTL_TIOCSETD; + extern unsigned IOCTL_TIOCSPGRP; + extern unsigned IOCTL_TIOCSTI; + extern unsigned IOCTL_TIOCSWINSZ; +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) + extern unsigned IOCTL_SIOCGETSGCNT; + extern unsigned IOCTL_SIOCGETVIFCNT; +#endif +#if SANITIZER_LINUX + extern unsigned IOCTL_EVIOCGABS; + extern unsigned IOCTL_EVIOCGBIT; + extern unsigned IOCTL_EVIOCGEFFECTS; + extern unsigned IOCTL_EVIOCGID; + extern unsigned IOCTL_EVIOCGKEY; + extern unsigned IOCTL_EVIOCGKEYCODE; + extern unsigned IOCTL_EVIOCGLED; + extern unsigned IOCTL_EVIOCGNAME; + extern unsigned IOCTL_EVIOCGPHYS; + extern unsigned IOCTL_EVIOCGRAB; + extern unsigned IOCTL_EVIOCGREP; + extern unsigned IOCTL_EVIOCGSND; + extern unsigned IOCTL_EVIOCGSW; + extern unsigned IOCTL_EVIOCGUNIQ; + extern unsigned IOCTL_EVIOCGVERSION; + extern unsigned IOCTL_EVIOCRMFF; + extern unsigned IOCTL_EVIOCSABS; + extern unsigned IOCTL_EVIOCSFF; + extern unsigned IOCTL_EVIOCSKEYCODE; + extern unsigned IOCTL_EVIOCSREP; + extern unsigned IOCTL_BLKFLSBUF; + extern unsigned IOCTL_BLKGETSIZE; + extern unsigned IOCTL_BLKRAGET; + extern unsigned IOCTL_BLKRASET; + extern unsigned IOCTL_BLKROGET; + extern unsigned IOCTL_BLKROSET; + extern unsigned IOCTL_BLKRRPART; + extern unsigned IOCTL_CDROMAUDIOBUFSIZ; + extern unsigned IOCTL_CDROMEJECT; + extern unsigned IOCTL_CDROMEJECT_SW; + extern unsigned IOCTL_CDROMMULTISESSION; + extern unsigned IOCTL_CDROMPAUSE; + extern unsigned IOCTL_CDROMPLAYMSF; + extern unsigned IOCTL_CDROMPLAYTRKIND; + extern unsigned IOCTL_CDROMREADAUDIO; + extern unsigned IOCTL_CDROMREADCOOKED; + extern unsigned IOCTL_CDROMREADMODE1; + extern unsigned IOCTL_CDROMREADMODE2; + extern unsigned IOCTL_CDROMREADRAW; + extern unsigned IOCTL_CDROMREADTOCENTRY; + extern unsigned IOCTL_CDROMREADTOCHDR; + extern unsigned IOCTL_CDROMRESET; + extern unsigned IOCTL_CDROMRESUME; + extern unsigned IOCTL_CDROMSEEK; + extern unsigned IOCTL_CDROMSTART; + extern unsigned IOCTL_CDROMSTOP; + extern unsigned IOCTL_CDROMSUBCHNL; + extern unsigned IOCTL_CDROMVOLCTRL; + extern unsigned IOCTL_CDROMVOLREAD; + extern unsigned IOCTL_CDROM_GET_UPC; + extern unsigned IOCTL_FDCLRPRM; + extern unsigned IOCTL_FDDEFPRM; + extern unsigned IOCTL_FDFLUSH; + extern unsigned IOCTL_FDFMTBEG; + extern unsigned IOCTL_FDFMTEND; + extern unsigned IOCTL_FDFMTTRK; + extern unsigned IOCTL_FDGETDRVPRM; + extern unsigned IOCTL_FDGETDRVSTAT; + extern unsigned IOCTL_FDGETDRVTYP; + extern unsigned IOCTL_FDGETFDCSTAT; + extern unsigned IOCTL_FDGETMAXERRS; + extern unsigned IOCTL_FDGETPRM; + extern unsigned IOCTL_FDMSGOFF; + extern unsigned IOCTL_FDMSGON; + extern unsigned IOCTL_FDPOLLDRVSTAT; + extern unsigned IOCTL_FDRAWCMD; + extern unsigned IOCTL_FDRESET; + extern unsigned IOCTL_FDSETDRVPRM; + extern unsigned IOCTL_FDSETEMSGTRESH; + extern unsigned IOCTL_FDSETMAXERRS; + extern unsigned IOCTL_FDSETPRM; + extern unsigned IOCTL_FDTWADDLE; + extern unsigned IOCTL_FDWERRORCLR; + extern unsigned IOCTL_FDWERRORGET; + extern unsigned IOCTL_HDIO_DRIVE_CMD; + extern unsigned IOCTL_HDIO_GETGEO; + extern unsigned IOCTL_HDIO_GET_32BIT; + extern unsigned IOCTL_HDIO_GET_DMA; + extern unsigned IOCTL_HDIO_GET_IDENTITY; + extern unsigned IOCTL_HDIO_GET_KEEPSETTINGS; + extern unsigned IOCTL_HDIO_GET_MULTCOUNT; + extern unsigned IOCTL_HDIO_GET_NOWERR; + extern unsigned IOCTL_HDIO_GET_UNMASKINTR; + extern unsigned IOCTL_HDIO_SET_32BIT; + extern unsigned IOCTL_HDIO_SET_DMA; + extern unsigned IOCTL_HDIO_SET_KEEPSETTINGS; + extern unsigned IOCTL_HDIO_SET_MULTCOUNT; + extern unsigned IOCTL_HDIO_SET_NOWERR; + extern unsigned IOCTL_HDIO_SET_UNMASKINTR; + extern unsigned IOCTL_MTIOCGET; + extern unsigned IOCTL_MTIOCPOS; + extern unsigned IOCTL_MTIOCTOP; + extern unsigned IOCTL_PPPIOCGASYNCMAP; + extern unsigned IOCTL_PPPIOCGDEBUG; + extern unsigned IOCTL_PPPIOCGFLAGS; + extern unsigned IOCTL_PPPIOCGUNIT; + extern unsigned IOCTL_PPPIOCGXASYNCMAP; + extern unsigned IOCTL_PPPIOCSASYNCMAP; + extern unsigned IOCTL_PPPIOCSDEBUG; + extern unsigned IOCTL_PPPIOCSFLAGS; + extern unsigned IOCTL_PPPIOCSMAXCID; + extern unsigned IOCTL_PPPIOCSMRU; + extern unsigned IOCTL_PPPIOCSXASYNCMAP; + extern unsigned IOCTL_SIOCADDRT; + extern unsigned IOCTL_SIOCDARP; + extern unsigned IOCTL_SIOCDELRT; + extern unsigned IOCTL_SIOCDRARP; + extern unsigned IOCTL_SIOCGARP; + extern unsigned IOCTL_SIOCGIFENCAP; + extern unsigned IOCTL_SIOCGIFHWADDR; + extern unsigned IOCTL_SIOCGIFMAP; + extern unsigned IOCTL_SIOCGIFMEM; + extern unsigned IOCTL_SIOCGIFNAME; + extern unsigned IOCTL_SIOCGIFSLAVE; + extern unsigned IOCTL_SIOCGRARP; + extern unsigned IOCTL_SIOCGSTAMP; + extern unsigned IOCTL_SIOCSARP; + extern unsigned IOCTL_SIOCSIFENCAP; + extern unsigned IOCTL_SIOCSIFHWADDR; + extern unsigned IOCTL_SIOCSIFLINK; + extern unsigned IOCTL_SIOCSIFMAP; + extern unsigned IOCTL_SIOCSIFMEM; + extern unsigned IOCTL_SIOCSIFSLAVE; + extern unsigned IOCTL_SIOCSRARP; + extern unsigned IOCTL_SNDCTL_COPR_HALT; + extern unsigned IOCTL_SNDCTL_COPR_LOAD; + extern unsigned IOCTL_SNDCTL_COPR_RCODE; + extern unsigned IOCTL_SNDCTL_COPR_RCVMSG; + extern unsigned IOCTL_SNDCTL_COPR_RDATA; + extern unsigned IOCTL_SNDCTL_COPR_RESET; + extern unsigned IOCTL_SNDCTL_COPR_RUN; + extern unsigned IOCTL_SNDCTL_COPR_SENDMSG; + extern unsigned IOCTL_SNDCTL_COPR_WCODE; + extern unsigned IOCTL_SNDCTL_COPR_WDATA; + extern unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE; + extern unsigned IOCTL_SNDCTL_DSP_GETFMTS; + extern unsigned IOCTL_SNDCTL_DSP_NONBLOCK; + extern unsigned IOCTL_SNDCTL_DSP_POST; + extern unsigned IOCTL_SNDCTL_DSP_RESET; + extern unsigned IOCTL_SNDCTL_DSP_SETFMT; + extern unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT; + extern unsigned IOCTL_SNDCTL_DSP_SPEED; + extern unsigned IOCTL_SNDCTL_DSP_STEREO; + extern unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE; + extern unsigned IOCTL_SNDCTL_DSP_SYNC; + extern unsigned IOCTL_SNDCTL_FM_4OP_ENABLE; + extern unsigned IOCTL_SNDCTL_FM_LOAD_INSTR; + extern unsigned IOCTL_SNDCTL_MIDI_INFO; + extern unsigned IOCTL_SNDCTL_MIDI_PRETIME; + extern unsigned IOCTL_SNDCTL_SEQ_CTRLRATE; + extern unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT; + extern unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT; + extern unsigned IOCTL_SNDCTL_SEQ_NRMIDIS; + extern unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS; + extern unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND; + extern unsigned IOCTL_SNDCTL_SEQ_PANIC; + extern unsigned IOCTL_SNDCTL_SEQ_PERCMODE; + extern unsigned IOCTL_SNDCTL_SEQ_RESET; + extern unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES; + extern unsigned IOCTL_SNDCTL_SEQ_SYNC; + extern unsigned IOCTL_SNDCTL_SEQ_TESTMIDI; + extern unsigned IOCTL_SNDCTL_SEQ_THRESHOLD; + extern unsigned IOCTL_SNDCTL_SYNTH_INFO; + extern unsigned IOCTL_SNDCTL_SYNTH_MEMAVL; + extern unsigned IOCTL_SNDCTL_TMR_CONTINUE; + extern unsigned IOCTL_SNDCTL_TMR_METRONOME; + extern unsigned IOCTL_SNDCTL_TMR_SELECT; + extern unsigned IOCTL_SNDCTL_TMR_SOURCE; + extern unsigned IOCTL_SNDCTL_TMR_START; + extern unsigned IOCTL_SNDCTL_TMR_STOP; + extern unsigned IOCTL_SNDCTL_TMR_TEMPO; + extern unsigned IOCTL_SNDCTL_TMR_TIMEBASE; + extern unsigned IOCTL_SOUND_MIXER_READ_ALTPCM; + extern unsigned IOCTL_SOUND_MIXER_READ_BASS; + extern unsigned IOCTL_SOUND_MIXER_READ_CAPS; + extern unsigned IOCTL_SOUND_MIXER_READ_CD; + extern unsigned IOCTL_SOUND_MIXER_READ_DEVMASK; + extern unsigned IOCTL_SOUND_MIXER_READ_ENHANCE; + extern unsigned IOCTL_SOUND_MIXER_READ_IGAIN; + extern unsigned IOCTL_SOUND_MIXER_READ_IMIX; + extern unsigned IOCTL_SOUND_MIXER_READ_LINE1; + extern unsigned IOCTL_SOUND_MIXER_READ_LINE2; + extern unsigned IOCTL_SOUND_MIXER_READ_LINE3; + extern unsigned IOCTL_SOUND_MIXER_READ_LINE; + extern unsigned IOCTL_SOUND_MIXER_READ_LOUD; + extern unsigned IOCTL_SOUND_MIXER_READ_MIC; + extern unsigned IOCTL_SOUND_MIXER_READ_MUTE; + extern unsigned IOCTL_SOUND_MIXER_READ_OGAIN; + extern unsigned IOCTL_SOUND_MIXER_READ_PCM; + extern unsigned IOCTL_SOUND_MIXER_READ_RECLEV; + extern unsigned IOCTL_SOUND_MIXER_READ_RECMASK; + extern unsigned IOCTL_SOUND_MIXER_READ_RECSRC; + extern unsigned IOCTL_SOUND_MIXER_READ_SPEAKER; + extern unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS; + extern unsigned IOCTL_SOUND_MIXER_READ_SYNTH; + extern unsigned IOCTL_SOUND_MIXER_READ_TREBLE; + extern unsigned IOCTL_SOUND_MIXER_READ_VOLUME; + extern unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM; + extern unsigned IOCTL_SOUND_MIXER_WRITE_BASS; + extern unsigned IOCTL_SOUND_MIXER_WRITE_CD; + extern unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE; + extern unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN; + extern unsigned IOCTL_SOUND_MIXER_WRITE_IMIX; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE1; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE2; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE3; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LOUD; + extern unsigned IOCTL_SOUND_MIXER_WRITE_MIC; + extern unsigned IOCTL_SOUND_MIXER_WRITE_MUTE; + extern unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN; + extern unsigned IOCTL_SOUND_MIXER_WRITE_PCM; + extern unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV; + extern unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC; + extern unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER; + extern unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH; + extern unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE; + extern unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME; + extern unsigned IOCTL_SOUND_PCM_READ_BITS; + extern unsigned IOCTL_SOUND_PCM_READ_CHANNELS; + extern unsigned IOCTL_SOUND_PCM_READ_FILTER; + extern unsigned IOCTL_SOUND_PCM_READ_RATE; + extern unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS; + extern unsigned IOCTL_SOUND_PCM_WRITE_FILTER; + extern unsigned IOCTL_TCFLSH; + extern unsigned IOCTL_TCGETA; + extern unsigned IOCTL_TCGETS; + extern unsigned IOCTL_TCSBRK; + extern unsigned IOCTL_TCSBRKP; + extern unsigned IOCTL_TCSETA; + extern unsigned IOCTL_TCSETAF; + extern unsigned IOCTL_TCSETAW; + extern unsigned IOCTL_TCSETS; + extern unsigned IOCTL_TCSETSF; + extern unsigned IOCTL_TCSETSW; + extern unsigned IOCTL_TCXONC; + extern unsigned IOCTL_TIOCGLCKTRMIOS; + extern unsigned IOCTL_TIOCGSOFTCAR; + extern unsigned IOCTL_TIOCINQ; + extern unsigned IOCTL_TIOCLINUX; + extern unsigned IOCTL_TIOCSERCONFIG; + extern unsigned IOCTL_TIOCSERGETLSR; + extern unsigned IOCTL_TIOCSERGWILD; + extern unsigned IOCTL_TIOCSERSWILD; + extern unsigned IOCTL_TIOCSLCKTRMIOS; + extern unsigned IOCTL_TIOCSSOFTCAR; + extern unsigned IOCTL_VT_ACTIVATE; + extern unsigned IOCTL_VT_DISALLOCATE; + extern unsigned IOCTL_VT_GETMODE; + extern unsigned IOCTL_VT_GETSTATE; + extern unsigned IOCTL_VT_OPENQRY; + extern unsigned IOCTL_VT_RELDISP; + extern unsigned IOCTL_VT_RESIZE; + extern unsigned IOCTL_VT_RESIZEX; + extern unsigned IOCTL_VT_SENDSIG; + extern unsigned IOCTL_VT_SETMODE; + extern unsigned IOCTL_VT_WAITACTIVE; +#endif +#if SANITIZER_LINUX && !SANITIZER_ANDROID + extern unsigned IOCTL_CYGETDEFTHRESH; + extern unsigned IOCTL_CYGETDEFTIMEOUT; + extern unsigned IOCTL_CYGETMON; + extern unsigned IOCTL_CYGETTHRESH; + extern unsigned IOCTL_CYGETTIMEOUT; + extern unsigned IOCTL_CYSETDEFTHRESH; + extern unsigned IOCTL_CYSETDEFTIMEOUT; + extern unsigned IOCTL_CYSETTHRESH; + extern unsigned IOCTL_CYSETTIMEOUT; + extern unsigned IOCTL_EQL_EMANCIPATE; + extern unsigned IOCTL_EQL_ENSLAVE; + extern unsigned IOCTL_EQL_GETMASTRCFG; + extern unsigned IOCTL_EQL_GETSLAVECFG; + extern unsigned IOCTL_EQL_SETMASTRCFG; + extern unsigned IOCTL_EQL_SETSLAVECFG; + extern unsigned IOCTL_EVIOCGKEYCODE_V2; + extern unsigned IOCTL_EVIOCGPROP; + extern unsigned IOCTL_EVIOCSKEYCODE_V2; + extern unsigned IOCTL_FS_IOC_GETFLAGS; + extern unsigned IOCTL_FS_IOC_GETVERSION; + extern unsigned IOCTL_FS_IOC_SETFLAGS; + extern unsigned IOCTL_FS_IOC_SETVERSION; + extern unsigned IOCTL_GIO_CMAP; + extern unsigned IOCTL_GIO_FONT; + extern unsigned IOCTL_GIO_SCRNMAP; + extern unsigned IOCTL_GIO_UNIMAP; + extern unsigned IOCTL_GIO_UNISCRNMAP; + extern unsigned IOCTL_KDADDIO; + extern unsigned IOCTL_KDDELIO; + extern unsigned IOCTL_KDDISABIO; + extern unsigned IOCTL_KDENABIO; + extern unsigned IOCTL_KDGETKEYCODE; + extern unsigned IOCTL_KDGETLED; + extern unsigned IOCTL_KDGETMODE; + extern unsigned IOCTL_KDGKBDIACR; + extern unsigned IOCTL_KDGKBENT; + extern unsigned IOCTL_KDGKBLED; + extern unsigned IOCTL_KDGKBMETA; + extern unsigned IOCTL_KDGKBMODE; + extern unsigned IOCTL_KDGKBSENT; + extern unsigned IOCTL_KDGKBTYPE; + extern unsigned IOCTL_KDMAPDISP; + extern unsigned IOCTL_KDMKTONE; + extern unsigned IOCTL_KDSETKEYCODE; + extern unsigned IOCTL_KDSETLED; + extern unsigned IOCTL_KDSETMODE; + extern unsigned IOCTL_KDSIGACCEPT; + extern unsigned IOCTL_KDSKBDIACR; + extern unsigned IOCTL_KDSKBENT; + extern unsigned IOCTL_KDSKBLED; + extern unsigned IOCTL_KDSKBMETA; + extern unsigned IOCTL_KDSKBMODE; + extern unsigned IOCTL_KDSKBSENT; + extern unsigned IOCTL_KDUNMAPDISP; + extern unsigned IOCTL_KIOCSOUND; + extern unsigned IOCTL_LPABORT; + extern unsigned IOCTL_LPABORTOPEN; + extern unsigned IOCTL_LPCAREFUL; + extern unsigned IOCTL_LPCHAR; + extern unsigned IOCTL_LPGETIRQ; + extern unsigned IOCTL_LPGETSTATUS; + extern unsigned IOCTL_LPRESET; + extern unsigned IOCTL_LPSETIRQ; + extern unsigned IOCTL_LPTIME; + extern unsigned IOCTL_LPWAIT; + extern unsigned IOCTL_MTIOCGETCONFIG; + extern unsigned IOCTL_MTIOCSETCONFIG; + extern unsigned IOCTL_PIO_CMAP; + extern unsigned IOCTL_PIO_FONT; + extern unsigned IOCTL_PIO_SCRNMAP; + extern unsigned IOCTL_PIO_UNIMAP; + extern unsigned IOCTL_PIO_UNIMAPCLR; + extern unsigned IOCTL_PIO_UNISCRNMAP; + extern unsigned IOCTL_SCSI_IOCTL_GET_IDLUN; + extern unsigned IOCTL_SCSI_IOCTL_PROBE_HOST; + extern unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE; + extern unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE; + extern unsigned IOCTL_SIOCAIPXITFCRT; + extern unsigned IOCTL_SIOCAIPXPRISLT; + extern unsigned IOCTL_SIOCAX25ADDUID; + extern unsigned IOCTL_SIOCAX25DELUID; + extern unsigned IOCTL_SIOCAX25GETPARMS; + extern unsigned IOCTL_SIOCAX25GETUID; + extern unsigned IOCTL_SIOCAX25NOUID; + extern unsigned IOCTL_SIOCAX25SETPARMS; + extern unsigned IOCTL_SIOCDEVPLIP; + extern unsigned IOCTL_SIOCIPXCFGDATA; + extern unsigned IOCTL_SIOCNRDECOBS; + extern unsigned IOCTL_SIOCNRGETPARMS; + extern unsigned IOCTL_SIOCNRRTCTL; + extern unsigned IOCTL_SIOCNRSETPARMS; + extern unsigned IOCTL_SNDCTL_DSP_GETISPACE; + extern unsigned IOCTL_SNDCTL_DSP_GETOSPACE; + extern unsigned IOCTL_TIOCGSERIAL; + extern unsigned IOCTL_TIOCSERGETMULTI; + extern unsigned IOCTL_TIOCSERSETMULTI; + extern unsigned IOCTL_TIOCSSERIAL; +#endif + + extern const int errno_EOWNERDEAD; } // namespace __sanitizer +#define CHECK_TYPE_SIZE(TYPE) \ + COMPILER_CHECK(sizeof(__sanitizer_##TYPE) == sizeof(TYPE)) + +#define CHECK_SIZE_AND_OFFSET(CLASS, MEMBER) \ + COMPILER_CHECK(sizeof(((__sanitizer_##CLASS *) NULL)->MEMBER) == \ + sizeof(((CLASS *) NULL)->MEMBER)); \ + COMPILER_CHECK(offsetof(__sanitizer_##CLASS, MEMBER) == \ + offsetof(CLASS, MEMBER)) + +// For sigaction, which is a function and struct at the same time, +// and thus requires explicit "struct" in sizeof() expression. +#define CHECK_STRUCT_SIZE_AND_OFFSET(CLASS, MEMBER) \ + COMPILER_CHECK(sizeof(((struct __sanitizer_##CLASS *) NULL)->MEMBER) == \ + sizeof(((struct CLASS *) NULL)->MEMBER)); \ + COMPILER_CHECK(offsetof(struct __sanitizer_##CLASS, MEMBER) == \ + offsetof(struct CLASS, MEMBER)) + #endif diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc index af25b245fdd3..ffe91df1a59e 100644 --- a/lib/sanitizer_common/sanitizer_posix.cc +++ b/lib/sanitizer_common/sanitizer_posix.cc @@ -29,6 +29,24 @@ uptr GetMmapGranularity() { return GetPageSize(); } +uptr GetMaxVirtualAddress() { +#if SANITIZER_WORDSIZE == 64 +# if defined(__powerpc64__) + // On PowerPC64 we have two different address space layouts: 44- and 46-bit. + // We somehow need to figure our which one we are using now and choose + // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. + // Note that with 'ulimit -s unlimited' the stack is moved away from the top + // of the address space, so simply checking the stack address is not enough. + return (1ULL << 44) - 1; // 0x00000fffffffffffUL +# else + return (1ULL << 47) - 1; // 0x00007fffffffffffUL; +# endif +#else // SANITIZER_WORDSIZE == 32 + // FIXME: We can probably lower this on Android? + return (1ULL << 32) - 1; // 0xffffffff; +#endif // SANITIZER_WORDSIZE +} + void *MmapOrDie(uptr size, const char *mem_type) { size = RoundUpTo(size, GetPageSizeCached()); uptr res = internal_mmap(0, size, @@ -155,6 +173,77 @@ const char *GetPwd() { return GetEnv("PWD"); } +char *FindPathToBinary(const char *name) { + const char *path = GetEnv("PATH"); + if (!path) + return 0; + uptr name_len = internal_strlen(name); + InternalScopedBuffer<char> buffer(kMaxPathLength); + const char *beg = path; + while (true) { + const char *end = internal_strchrnul(beg, ':'); + uptr prefix_len = end - beg; + if (prefix_len + name_len + 2 <= kMaxPathLength) { + internal_memcpy(buffer.data(), beg, prefix_len); + buffer[prefix_len] = '/'; + internal_memcpy(&buffer[prefix_len + 1], name, name_len); + buffer[prefix_len + 1 + name_len] = '\0'; + if (FileExists(buffer.data())) + return internal_strdup(buffer.data()); + } + if (*end == '\0') break; + beg = end + 1; + } + return 0; +} + +void MaybeOpenReportFile() { + if (!log_to_file || (report_fd_pid == internal_getpid())) return; + InternalScopedBuffer<char> report_path_full(4096); + internal_snprintf(report_path_full.data(), report_path_full.size(), + "%s.%d", report_path_prefix, internal_getpid()); + uptr openrv = OpenFile(report_path_full.data(), true); + if (internal_iserror(openrv)) { + report_fd = kStderrFd; + log_to_file = false; + Report("ERROR: Can't open file: %s\n", report_path_full.data()); + Die(); + } + if (report_fd != kInvalidFd) { + // We're in the child. Close the parent's log. + internal_close(report_fd); + } + report_fd = openrv; + report_fd_pid = internal_getpid(); +} + +void RawWrite(const char *buffer) { + static const char *kRawWriteError = + "RawWrite can't output requested buffer!\n"; + uptr length = (uptr)internal_strlen(buffer); + MaybeOpenReportFile(); + if (length != internal_write(report_fd, buffer, length)) { + internal_write(report_fd, kRawWriteError, internal_strlen(kRawWriteError)); + Die(); + } +} + +bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) { + uptr s, e, off, prot; + InternalMmapVector<char> fn(4096); + fn.push_back(0); + MemoryMappingLayout proc_maps(/*cache_enabled*/false); + while (proc_maps.Next(&s, &e, &off, &fn[0], fn.capacity(), &prot)) { + if ((prot & MemoryMappingLayout::kProtectionExecute) != 0 + && internal_strcmp(module, &fn[0]) == 0) { + *start = s; + *end = e; + return true; + } + } + return false; +} + } // namespace __sanitizer #endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_posix_libcdep.cc index 43da171ba271..6032f520dee0 100644 --- a/lib/sanitizer_common/sanitizer_posix_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_posix_libcdep.cc @@ -89,28 +89,6 @@ int internal_isatty(fd_t fd) { return isatty(fd); } -#ifndef SANITIZER_GO -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, - uptr stack_top, uptr stack_bottom, bool fast) { -#if !SANITIZER_CAN_FAST_UNWIND - fast = false; -#endif -#if SANITIZER_MAC - // Always unwind fast on Mac. - (void)fast; -#else - if (!fast || (stack_top == stack_bottom)) - return stack->SlowUnwindStack(pc, max_s); -#endif // SANITIZER_MAC - stack->size = 0; - stack->trace[0] = pc; - if (max_s > 1) { - stack->max_size = max_s; - stack->FastUnwindStack(pc, bp, stack_top, stack_bottom); - } -} -#endif // SANITIZER_GO - } // namespace __sanitizer #endif diff --git a/lib/sanitizer_common/sanitizer_printf.cc b/lib/sanitizer_common/sanitizer_printf.cc index 5935d7f17a5e..bbdb24b685de 100644 --- a/lib/sanitizer_common/sanitizer_printf.cc +++ b/lib/sanitizer_common/sanitizer_printf.cc @@ -21,7 +21,7 @@ #include <stdio.h> #include <stdarg.h> -#if SANITIZER_WINDOWS +#if SANITIZER_WINDOWS && !defined(va_copy) # define va_copy(dst, src) ((dst) = (src)) #endif @@ -38,45 +38,60 @@ static int AppendChar(char **buff, const char *buff_end, char c) { } // Appends number in a given base to buffer. If its length is less than -// "minimal_num_length", it is padded with leading zeroes. -static int AppendUnsigned(char **buff, const char *buff_end, u64 num, - u8 base, u8 minimal_num_length) { +// |minimal_num_length|, it is padded with leading zeroes or spaces, depending +// on the value of |pad_with_zero|. +static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value, + u8 base, u8 minimal_num_length, bool pad_with_zero, + bool negative) { uptr const kMaxLen = 30; RAW_CHECK(base == 10 || base == 16); + RAW_CHECK(base == 10 || !negative); + RAW_CHECK(absolute_value || !negative); RAW_CHECK(minimal_num_length < kMaxLen); + int result = 0; + if (negative && minimal_num_length) + --minimal_num_length; + if (negative && pad_with_zero) + result += AppendChar(buff, buff_end, '-'); uptr num_buffer[kMaxLen]; - uptr pos = 0; + int pos = 0; do { - RAW_CHECK_MSG(pos < kMaxLen, "appendNumber buffer overflow"); - num_buffer[pos++] = num % base; - num /= base; - } while (num > 0); + RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow"); + num_buffer[pos++] = absolute_value % base; + absolute_value /= base; + } while (absolute_value > 0); if (pos < minimal_num_length) { // Make sure compiler doesn't insert call to memset here. internal_memset(&num_buffer[pos], 0, sizeof(num_buffer[0]) * (minimal_num_length - pos)); pos = minimal_num_length; } - int result = 0; - while (pos-- > 0) { - uptr digit = num_buffer[pos]; + RAW_CHECK(pos > 0); + pos--; + for (; pos >= 0 && num_buffer[pos] == 0; pos--) { + char c = (pad_with_zero || pos == 0) ? '0' : ' '; + result += AppendChar(buff, buff_end, c); + } + if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, '-'); + for (; pos >= 0; pos--) { + char digit = static_cast<char>(num_buffer[pos]); result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit : 'a' + digit - 10); } return result; } +static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base, + u8 minimal_num_length, bool pad_with_zero) { + return AppendNumber(buff, buff_end, num, base, minimal_num_length, + pad_with_zero, false /* negative */); +} + static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num, - u8 minimal_num_length) { - int result = 0; - if (num < 0) { - result += AppendChar(buff, buff_end, '-'); - num = -num; - if (minimal_num_length) - --minimal_num_length; - } - result += AppendUnsigned(buff, buff_end, (u64)num, 10, minimal_num_length); - return result; + u8 minimal_num_length, bool pad_with_zero) { + bool negative = (num < 0); + return AppendNumber(buff, buff_end, (u64)(negative ? -num : num), 10, + minimal_num_length, pad_with_zero, negative); } static int AppendString(char **buff, const char *buff_end, const char *s) { @@ -93,14 +108,14 @@ static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) { int result = 0; result += AppendString(buff, buff_end, "0x"); result += AppendUnsigned(buff, buff_end, ptr_value, 16, - (SANITIZER_WORDSIZE == 64) ? 12 : 8); + (SANITIZER_WORDSIZE == 64) ? 12 : 8, true); return result; } int VSNPrintf(char *buff, int buff_length, const char *format, va_list args) { static const char *kPrintfFormatsHelp = - "Supported Printf formats: %(0[0-9]*)?(z|ll)?{d,u,x}; %p; %s; %c\n"; + "Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x}; %p; %s; %c\n"; RAW_CHECK(format); RAW_CHECK(buff_length > 0); const char *buff_end = &buff[buff_length - 1]; @@ -112,11 +127,11 @@ int VSNPrintf(char *buff, int buff_length, continue; } cur++; - bool have_width = (*cur == '0'); + bool have_width = (*cur >= '0' && *cur <= '9'); + bool pad_with_zero = (*cur == '0'); int width = 0; if (have_width) { while (*cur >= '0' && *cur <= '9') { - have_width = true; width = width * 10 + *cur++ - '0'; } } @@ -132,7 +147,8 @@ int VSNPrintf(char *buff, int buff_length, dval = have_ll ? va_arg(args, s64) : have_z ? va_arg(args, sptr) : va_arg(args, int); - result += AppendSignedDecimal(&buff, buff_end, dval, width); + result += AppendSignedDecimal(&buff, buff_end, dval, width, + pad_with_zero); break; } case 'u': @@ -141,7 +157,7 @@ int VSNPrintf(char *buff, int buff_length, : have_z ? va_arg(args, uptr) : va_arg(args, unsigned); result += AppendUnsigned(&buff, buff_end, uval, - (*cur == 'u') ? 10 : 16, width); + (*cur == 'u') ? 10 : 16, width, pad_with_zero); break; } case 'p': { @@ -179,17 +195,22 @@ void SetPrintfAndReportCallback(void (*callback)(const char *)) { PrintfAndReportCallback = callback; } -#if SANITIZER_SUPPORTS_WEAK_HOOKS // Can be overriden in frontend. -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +#if SANITIZER_SUPPORTS_WEAK_HOOKS +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void OnPrint(const char *str) { + (void)str; +} +#elif defined(SANITIZER_GO) && defined(TSAN_EXTERNAL_HOOKS) void OnPrint(const char *str); +#else +void OnPrint(const char *str) { + (void)str; +} #endif static void CallPrintfAndReportCallback(const char *str) { -#if SANITIZER_SUPPORTS_WEAK_HOOKS - if (&OnPrint != NULL) - OnPrint(str); -#endif + OnPrint(str); if (PrintfAndReportCallback) PrintfAndReportCallback(str); } @@ -273,4 +294,13 @@ int internal_snprintf(char *buffer, uptr length, const char *format, ...) { return needed_length; } +void InternalScopedString::append(const char *format, ...) { + CHECK_LT(length_, size()); + va_list args; + va_start(args, format); + VSNPrintf(data() + length_, size() - length_, format, args); + va_end(args); + length_ += internal_strlen(data() + length_); +} + } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_procmaps.h b/lib/sanitizer_common/sanitizer_procmaps.h index b96f09ec4561..65bcac6338d5 100644 --- a/lib/sanitizer_common/sanitizer_procmaps.h +++ b/lib/sanitizer_common/sanitizer_procmaps.h @@ -32,7 +32,7 @@ class MemoryMappingLayout { } }; -#else // _WIN32 +#else // SANITIZER_WINDOWS #if SANITIZER_LINUX struct ProcSelfMapsBuff { char *data; @@ -118,7 +118,18 @@ class MemoryMappingLayout { # endif }; -#endif // _WIN32 +typedef void (*fill_profile_f)(uptr start, uptr rss, bool file, + /*out*/uptr *stats, uptr stats_size); + +// Parse the contents of /proc/self/smaps and generate a memory profile. +// |cb| is a tool-specific callback that fills the |stats| array containing +// |stats_size| elements. +void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size); + +// Returns code range for the specified module. +bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end); + +#endif // SANITIZER_WINDOWS } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_quarantine.h b/lib/sanitizer_common/sanitizer_quarantine.h index 599d13645dd7..db4eb74505f9 100644 --- a/lib/sanitizer_common/sanitizer_quarantine.h +++ b/lib/sanitizer_common/sanitizer_quarantine.h @@ -26,13 +26,15 @@ namespace __sanitizer { template<typename Node> class QuarantineCache; struct QuarantineBatch { - static const uptr kSize = 1024; + static const uptr kSize = 1021; QuarantineBatch *next; uptr size; uptr count; void *batch[kSize]; }; +COMPILER_CHECK(sizeof(QuarantineBatch) <= (1 << 13)); // 8Kb. + // The callback interface is: // void Callback::Recycle(Node *ptr); // void *cb.Allocate(uptr size); @@ -123,8 +125,10 @@ class QuarantineCache { } void Enqueue(Callback cb, void *ptr, uptr size) { - if (list_.empty() || list_.back()->count == QuarantineBatch::kSize) + if (list_.empty() || list_.back()->count == QuarantineBatch::kSize) { AllocBatch(cb); + size += sizeof(QuarantineBatch); // Count the batch in Quarantine size. + } QuarantineBatch *b = list_.back(); b->batch[b->count++] = ptr; b->size += size; @@ -147,7 +151,7 @@ class QuarantineCache { return 0; QuarantineBatch *b = list_.front(); list_.pop_front(); - SizeAdd(-b->size); + SizeSub(b->size); return b; } @@ -158,6 +162,9 @@ class QuarantineCache { void SizeAdd(uptr add) { atomic_store(&size_, Size() + add, memory_order_relaxed); } + void SizeSub(uptr sub) { + atomic_store(&size_, Size() - sub, memory_order_relaxed); + } NOINLINE QuarantineBatch* AllocBatch(Callback cb) { QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b)); diff --git a/lib/sanitizer_common/sanitizer_report_decorator.h b/lib/sanitizer_common/sanitizer_report_decorator.h index 49334d5e0c71..eef2b15ccd34 100644 --- a/lib/sanitizer_common/sanitizer_report_decorator.h +++ b/lib/sanitizer_common/sanitizer_report_decorator.h @@ -19,6 +19,8 @@ namespace __sanitizer { class AnsiColorDecorator { + // FIXME: This is not portable. It assumes the special strings are printed to + // stdout, which is not the case on Windows (see SetConsoleTextAttribute()). public: explicit AnsiColorDecorator(bool use_ansi_colors) : ansi_(use_ansi_colors) { } const char *Bold() const { return ansi_ ? "\033[1m" : ""; } diff --git a/lib/sanitizer_common/sanitizer_stackdepot.cc b/lib/sanitizer_common/sanitizer_stackdepot.cc index 08e5238325e5..2793bd0714a1 100644 --- a/lib/sanitizer_common/sanitizer_stackdepot.cc +++ b/lib/sanitizer_common/sanitizer_stackdepot.cc @@ -201,4 +201,38 @@ const uptr *StackDepotGet(u32 id, uptr *size) { return 0; } +bool StackDepotReverseMap::IdDescPair::IdComparator( + const StackDepotReverseMap::IdDescPair &a, + const StackDepotReverseMap::IdDescPair &b) { + return a.id < b.id; +} + +StackDepotReverseMap::StackDepotReverseMap() + : map_(StackDepotGetStats()->n_uniq_ids + 100) { + for (int idx = 0; idx < kTabSize; idx++) { + atomic_uintptr_t *p = &depot.tab[idx]; + uptr v = atomic_load(p, memory_order_consume); + StackDesc *s = (StackDesc*)(v & ~1); + for (; s; s = s->link) { + IdDescPair pair = {s->id, s}; + map_.push_back(pair); + } + } + InternalSort(&map_, map_.size(), IdDescPair::IdComparator); +} + +const uptr *StackDepotReverseMap::Get(u32 id, uptr *size) { + if (!map_.size()) return 0; + IdDescPair pair = {id, 0}; + uptr idx = InternalBinarySearch(map_, 0, map_.size(), pair, + IdDescPair::IdComparator); + if (idx > map_.size()) { + *size = 0; + return 0; + } + StackDesc *desc = map_[idx].desc; + *size = desc->size; + return desc->stack; +} + } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_stackdepot.h b/lib/sanitizer_common/sanitizer_stackdepot.h index 5915fdbb4310..4f77ff28eab1 100644 --- a/lib/sanitizer_common/sanitizer_stackdepot.h +++ b/lib/sanitizer_common/sanitizer_stackdepot.h @@ -13,6 +13,7 @@ #ifndef SANITIZER_STACKDEPOT_H #define SANITIZER_STACKDEPOT_H +#include "sanitizer_common.h" #include "sanitizer_internal_defs.h" namespace __sanitizer { @@ -31,6 +32,31 @@ struct StackDepotStats { StackDepotStats *StackDepotGetStats(); +struct StackDesc; + +// Instantiating this class creates a snapshot of StackDepot which can be +// efficiently queried with StackDepotGet(). You can use it concurrently with +// StackDepot, but the snapshot is only guaranteed to contain those stack traces +// which were stored before it was instantiated. +class StackDepotReverseMap { + public: + StackDepotReverseMap(); + const uptr *Get(u32 id, uptr *size); + + private: + struct IdDescPair { + u32 id; + StackDesc *desc; + + static bool IdComparator(const IdDescPair &a, const IdDescPair &b); + }; + + InternalMmapVector<IdDescPair> map_; + + // Disallow evil constructors. + StackDepotReverseMap(const StackDepotReverseMap&); + void operator=(const StackDepotReverseMap&); +}; } // namespace __sanitizer #endif // SANITIZER_STACKDEPOT_H diff --git a/lib/sanitizer_common/sanitizer_stacktrace.cc b/lib/sanitizer_common/sanitizer_stacktrace.cc index 724c29c86b66..70ce26bde0d2 100644 --- a/lib/sanitizer_common/sanitizer_stacktrace.cc +++ b/lib/sanitizer_common/sanitizer_stacktrace.cc @@ -12,20 +12,13 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common.h" +#include "sanitizer_flags.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" namespace __sanitizer { -const char *StripPathPrefix(const char *filepath, - const char *strip_file_prefix) { - if (filepath == 0) return 0; - if (filepath == internal_strstr(filepath, strip_file_prefix)) - return filepath + internal_strlen(strip_file_prefix); - return filepath; -} -// ----------------------- StackTrace ----------------------------- {{{1 uptr StackTrace::GetPreviousInstructionPc(uptr pc) { #ifdef __arm__ // Cancel Thumb bit. @@ -41,32 +34,21 @@ uptr StackTrace::GetPreviousInstructionPc(uptr pc) { #endif } -static void PrintStackFramePrefix(uptr frame_num, uptr pc) { - Printf(" #%zu 0x%zx", frame_num, pc); -} - -static void PrintSourceLocation(const char *file, int line, int column, - const char *strip_file_prefix) { - CHECK(file); - Printf(" %s", StripPathPrefix(file, strip_file_prefix)); - if (line > 0) { - Printf(":%d", line); - if (column > 0) - Printf(":%d", column); - } -} - -static void PrintModuleAndOffset(const char *module, uptr offset, - const char *strip_file_prefix) { - Printf(" (%s+0x%zx)", StripPathPrefix(module, strip_file_prefix), offset); +static void PrintStackFramePrefix(InternalScopedString *buffer, uptr frame_num, + uptr pc) { + buffer->append(" #%zu 0x%zx", frame_num, pc); } void StackTrace::PrintStack(const uptr *addr, uptr size, - bool symbolize, const char *strip_file_prefix, - SymbolizeCallback symbolize_callback ) { + SymbolizeCallback symbolize_callback) { + if (addr == 0) { + Printf("<empty stack>\n\n"); + return; + } MemoryMappingLayout proc_maps(/*cache_enabled*/true); InternalScopedBuffer<char> buff(GetPageSizeCached() * 2); InternalScopedBuffer<AddressInfo> addr_frames(64); + InternalScopedString frame_desc(GetPageSizeCached() * 2); uptr frame_num = 0; for (uptr i = 0; i < size && addr[i]; i++) { // PCs in stack traces are actually the return addresses, that is, @@ -77,50 +59,60 @@ void StackTrace::PrintStack(const uptr *addr, uptr size, if (symbolize_callback) { if (symbolize_callback((void*)pc, buff.data(), buff.size())) { addr_frames_num = 1; - PrintStackFramePrefix(frame_num, pc); + frame_desc.clear(); + PrintStackFramePrefix(&frame_desc, frame_num, pc); // We can't know anything about the string returned by external // symbolizer, but if it starts with filename, try to strip path prefix // from it. - Printf(" %s\n", StripPathPrefix(buff.data(), strip_file_prefix)); + frame_desc.append( + " %s", + StripPathPrefix(buff.data(), common_flags()->strip_path_prefix)); + Printf("%s\n", frame_desc.data()); frame_num++; } } - if (symbolize && addr_frames_num == 0 && &SymbolizeCode) { + if (common_flags()->symbolize && addr_frames_num == 0) { // Use our own (online) symbolizer, if necessary. - addr_frames_num = SymbolizeCode(pc, addr_frames.data(), - addr_frames.size()); + if (Symbolizer *sym = Symbolizer::GetOrNull()) + addr_frames_num = + sym->SymbolizeCode(pc, addr_frames.data(), addr_frames.size()); for (uptr j = 0; j < addr_frames_num; j++) { AddressInfo &info = addr_frames[j]; - PrintStackFramePrefix(frame_num, pc); + frame_desc.clear(); + PrintStackFramePrefix(&frame_desc, frame_num, pc); if (info.function) { - Printf(" in %s", info.function); + frame_desc.append(" in %s", info.function); } if (info.file) { - PrintSourceLocation(info.file, info.line, info.column, - strip_file_prefix); + frame_desc.append(" "); + PrintSourceLocation(&frame_desc, info.file, info.line, info.column); } else if (info.module) { - PrintModuleAndOffset(info.module, info.module_offset, - strip_file_prefix); + frame_desc.append(" "); + PrintModuleAndOffset(&frame_desc, info.module, info.module_offset); } - Printf("\n"); - info.Clear(); + Printf("%s\n", frame_desc.data()); frame_num++; + info.Clear(); } } if (addr_frames_num == 0) { // If online symbolization failed, try to output at least module and // offset for instruction. - PrintStackFramePrefix(frame_num, pc); + frame_desc.clear(); + PrintStackFramePrefix(&frame_desc, frame_num, pc); uptr offset; if (proc_maps.GetObjectNameAndOffset(pc, &offset, buff.data(), buff.size(), /* protection */0)) { - PrintModuleAndOffset(buff.data(), offset, strip_file_prefix); + frame_desc.append(" "); + PrintModuleAndOffset(&frame_desc, buff.data(), offset); } - Printf("\n"); + Printf("%s\n", frame_desc.data()); frame_num++; } } + // Always print a trailing empty line after stack trace. + Printf("\n"); } uptr StackTrace::GetCurrentPc() { @@ -128,17 +120,23 @@ uptr StackTrace::GetCurrentPc() { } void StackTrace::FastUnwindStack(uptr pc, uptr bp, - uptr stack_top, uptr stack_bottom) { - CHECK(size == 0 && trace[0] == pc); + uptr stack_top, uptr stack_bottom, + uptr max_depth) { + if (max_depth == 0) { + size = 0; + return; + } + trace[0] = pc; size = 1; uhwptr *frame = (uhwptr *)bp; uhwptr *prev_frame = frame - 1; + if (stack_top < 4096) return; // Sanity check for stack top. // Avoid infinite loop when frame == frame[0] by using frame > prev_frame. while (frame > prev_frame && frame < (uhwptr *)stack_top - 2 && frame > (uhwptr *)stack_bottom && IsAligned((uptr)frame, sizeof(*frame)) && - size < max_size) { + size < max_depth) { uhwptr pc1 = frame[1]; if (pc1 != pc) { trace[size++] = (uptr) pc1; @@ -156,111 +154,19 @@ void StackTrace::PopStackFrames(uptr count) { } } -// On 32-bits we don't compress stack traces. -// On 64-bits we compress stack traces: if a given pc differes slightly from -// the previous one, we record a 31-bit offset instead of the full pc. -SANITIZER_INTERFACE_ATTRIBUTE -uptr StackTrace::CompressStack(StackTrace *stack, u32 *compressed, uptr size) { -#if SANITIZER_WORDSIZE == 32 - // Don't compress, just copy. - uptr res = 0; - for (uptr i = 0; i < stack->size && i < size; i++) { - compressed[i] = stack->trace[i]; - res++; - } - if (stack->size < size) - compressed[stack->size] = 0; -#else // 64 bits, compress. - uptr prev_pc = 0; - const uptr kMaxOffset = (1ULL << 30) - 1; - uptr c_index = 0; - uptr res = 0; - for (uptr i = 0, n = stack->size; i < n; i++) { - uptr pc = stack->trace[i]; - if (!pc) break; - if ((s64)pc < 0) break; - // Printf("C pc[%zu] %zx\n", i, pc); - if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) { - uptr offset = (s64)(pc - prev_pc); - offset |= (1U << 31); - if (c_index >= size) break; - // Printf("C co[%zu] offset %zx\n", i, offset); - compressed[c_index++] = offset; - } else { - uptr hi = pc >> 32; - uptr lo = (pc << 32) >> 32; - CHECK_EQ((hi & (1 << 31)), 0); - if (c_index + 1 >= size) break; - // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo); - compressed[c_index++] = hi; - compressed[c_index++] = lo; - } - res++; - prev_pc = pc; - } - if (c_index < size) - compressed[c_index] = 0; - if (c_index + 1 < size) - compressed[c_index + 1] = 0; -#endif // SANITIZER_WORDSIZE - - // debug-only code -#if 0 - StackTrace check_stack; - UncompressStack(&check_stack, compressed, size); - if (res < check_stack.size) { - Printf("res %zu check_stack.size %zu; c_size %zu\n", res, - check_stack.size, size); - } - // |res| may be greater than check_stack.size, because - // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames. - CHECK(res >= check_stack.size); - CHECK_EQ(0, REAL(memcmp)(check_stack.trace, stack->trace, - check_stack.size * sizeof(uptr))); -#endif - - return res; +static bool MatchPc(uptr cur_pc, uptr trace_pc, uptr threshold) { + return cur_pc - trace_pc <= threshold || trace_pc - cur_pc <= threshold; } -SANITIZER_INTERFACE_ATTRIBUTE -void StackTrace::UncompressStack(StackTrace *stack, - u32 *compressed, uptr size) { -#if SANITIZER_WORDSIZE == 32 - // Don't uncompress, just copy. - stack->size = 0; - for (uptr i = 0; i < size && i < kStackTraceMax; i++) { - if (!compressed[i]) break; - stack->size++; - stack->trace[i] = compressed[i]; - } -#else // 64 bits, uncompress - uptr prev_pc = 0; - stack->size = 0; - for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) { - u32 x = compressed[i]; - uptr pc = 0; - if (x & (1U << 31)) { - // Printf("U co[%zu] offset: %x\n", i, x); - // this is an offset - s32 offset = x; - offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend. - pc = prev_pc + offset; - CHECK(pc); - } else { - // CHECK(i + 1 < size); - if (i + 1 >= size) break; - uptr hi = x; - uptr lo = compressed[i+1]; - // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo); - i++; - pc = (hi << 32) | lo; - if (!pc) break; - } - // Printf("U pc[%zu] %zx\n", stack->size, pc); - stack->trace[stack->size++] = pc; - prev_pc = pc; +uptr StackTrace::LocatePcInTrace(uptr pc) { + // Use threshold to find PC in stack trace, as PC we want to unwind from may + // slightly differ from return address in the actual unwinded stack trace. + const int kPcThreshold = 96; + for (uptr i = 0; i < size; ++i) { + if (MatchPc(pc, trace[i], kPcThreshold)) + return i; } -#endif // SANITIZER_WORDSIZE + return 0; } } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_stacktrace.h b/lib/sanitizer_common/sanitizer_stacktrace.h index fcfdd7e0b59b..5042f230188a 100644 --- a/lib/sanitizer_common/sanitizer_stacktrace.h +++ b/lib/sanitizer_common/sanitizer_stacktrace.h @@ -21,57 +21,57 @@ static const uptr kStackTraceMax = 256; #if SANITIZER_LINUX && (defined(__arm__) || \ defined(__powerpc__) || defined(__powerpc64__) || \ - defined(__sparc__)) -#define SANITIZER_CAN_FAST_UNWIND 0 + defined(__sparc__) || \ + defined(__mips__)) +# define SANITIZER_CAN_FAST_UNWIND 0 +#elif SANITIZER_WINDOWS +# define SANITIZER_CAN_FAST_UNWIND 0 #else -#define SANITIZER_CAN_FAST_UNWIND 1 +# define SANITIZER_CAN_FAST_UNWIND 1 #endif struct StackTrace { typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer, int out_size); + uptr top_frame_bp; uptr size; - uptr max_size; uptr trace[kStackTraceMax]; + + // Prints a symbolized stacktrace, followed by an empty line. static void PrintStack(const uptr *addr, uptr size, - bool symbolize, const char *strip_file_prefix, - SymbolizeCallback symbolize_callback); - void CopyTo(uptr *dst, uptr dst_size) { - for (uptr i = 0; i < size && i < dst_size; i++) - dst[i] = trace[i]; - for (uptr i = size; i < dst_size; i++) - dst[i] = 0; - } + SymbolizeCallback symbolize_callback = 0); - void CopyFrom(uptr *src, uptr src_size) { + void CopyFrom(const uptr *src, uptr src_size) { + top_frame_bp = 0; size = src_size; if (size > kStackTraceMax) size = kStackTraceMax; - for (uptr i = 0; i < size; i++) { + for (uptr i = 0; i < size; i++) trace[i] = src[i]; - } } - void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom); - void SlowUnwindStack(uptr pc, uptr max_depth); + static bool WillUseFastUnwind(bool request_fast_unwind) { + // Check if fast unwind is available. Fast unwind is the only option on Mac. + if (!SANITIZER_CAN_FAST_UNWIND) + return false; + else if (SANITIZER_MAC) + return true; + return request_fast_unwind; + } - void PopStackFrames(uptr count); + void Unwind(uptr max_depth, uptr pc, uptr bp, uptr stack_top, + uptr stack_bottom, bool request_fast_unwind); static uptr GetCurrentPc(); static uptr GetPreviousInstructionPc(uptr pc); - static uptr CompressStack(StackTrace *stack, - u32 *compressed, uptr size); - static void UncompressStack(StackTrace *stack, - u32 *compressed, uptr size); + private: + void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom, + uptr max_depth); + void SlowUnwindStack(uptr pc, uptr max_depth); + void PopStackFrames(uptr count); + uptr LocatePcInTrace(uptr pc); }; - -const char *StripPathPrefix(const char *filepath, - const char *strip_file_prefix); - -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, - uptr stack_top, uptr stack_bottom, bool fast); - } // namespace __sanitizer // Use this macro if you want to print stack trace with the caller diff --git a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc new file mode 100644 index 000000000000..58fa18251ecf --- /dev/null +++ b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc @@ -0,0 +1,28 @@ +//===-- sanitizer_stacktrace_libcdep.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 shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_stacktrace.h" + +namespace __sanitizer { + +void StackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, uptr stack_top, + uptr stack_bottom, bool request_fast_unwind) { + if (!WillUseFastUnwind(request_fast_unwind)) + SlowUnwindStack(pc, max_depth); + else + FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth); + + top_frame_bp = size ? bp : 0; +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_stoptheworld.h b/lib/sanitizer_common/sanitizer_stoptheworld.h index cc9408bb845f..a32646708d45 100644 --- a/lib/sanitizer_common/sanitizer_stoptheworld.h +++ b/lib/sanitizer_common/sanitizer_stoptheworld.h @@ -46,7 +46,7 @@ class SuspendedThreadsList { } private: - InternalVector<SuspendedThreadID> thread_ids_; + InternalMmapVector<SuspendedThreadID> thread_ids_; // Prohibit copy and assign. SuspendedThreadsList(const SuspendedThreadsList&); diff --git a/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc index e5284ee2211a..a34ddd872209 100644 --- a/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc @@ -14,12 +14,14 @@ #include "sanitizer_platform.h" -#if SANITIZER_LINUX +#if SANITIZER_LINUX && defined(__x86_64__) #include "sanitizer_stoptheworld.h" +#include "sanitizer_platform_limits_posix.h" + #include <errno.h> -#include <sched.h> // for clone +#include <sched.h> // for CLONE_* definitions #include <stddef.h> #include <sys/prctl.h> // for PR_* definitions #include <sys/ptrace.h> // for PTRACE_* definitions @@ -31,7 +33,16 @@ #endif #include <sys/wait.h> // for signal-related stuff +#ifdef sa_handler +# undef sa_handler +#endif + +#ifdef sa_sigaction +# undef sa_sigaction +#endif + #include "sanitizer_common.h" +#include "sanitizer_flags.h" #include "sanitizer_libc.h" #include "sanitizer_linux.h" #include "sanitizer_mutex.h" @@ -47,31 +58,14 @@ // clone() interface (we want to share the address space with the caller // process, so we prefer clone() over fork()). // -// We avoid the use of libc for two reasons: +// We don't use any libc functions, relying instead on direct syscalls. There +// are two reasons for this: // 1. calling a library function while threads are suspended could cause a // deadlock, if one of the treads happens to be holding a libc lock; // 2. it's generally not safe to call libc functions from the tracer task, // because clone() does not set up a thread-local storage for it. Any // thread-local variables used by libc will be shared between the tracer task // and the thread which spawned it. -// -// We deal with this by replacing libc calls with calls to our own -// implementations defined in sanitizer_libc.h and sanitizer_linux.h. However, -// there are still some libc functions which are used here: -// -// * All of the system calls ultimately go through the libc syscall() function. -// We're operating under the assumption that syscall()'s implementation does -// not acquire any locks or use any thread-local data (except for the errno -// variable, which we handle separately). -// -// * We lack custom implementations of sigfillset() and sigaction(), so we use -// the libc versions instead. The same assumptions as above apply. -// -// * It is safe to call libc functions before the cloned thread is spawned or -// after it has exited. The following functions are used in this manner: -// sigdelset() -// sigprocmask() -// clone() COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t)); @@ -106,10 +100,11 @@ bool ThreadSuspender::SuspendThread(SuspendedThreadID thread_id) { &pterrno)) { // Either the thread is dead, or something prevented us from attaching. // Log this event and move on. - Report("Could not attach to thread %d (errno %d).\n", thread_id, pterrno); + if (common_flags()->verbosity) + Report("Could not attach to thread %d (errno %d).\n", thread_id, pterrno); return false; } else { - if (SanitizerVerbosity > 0) + if (common_flags()->verbosity) Report("Attached to thread %d.\n", thread_id); // The thread is not guaranteed to stop before ptrace returns, so we must // wait on it. @@ -119,8 +114,9 @@ bool ThreadSuspender::SuspendThread(SuspendedThreadID thread_id) { if (internal_iserror(waitpid_status, &wperrno)) { // Got a ECHILD error. I don't think this situation is possible, but it // doesn't hurt to report it. - Report("Waiting on thread %d failed, detaching (errno %d).\n", thread_id, - wperrno); + if (common_flags()->verbosity) + Report("Waiting on thread %d failed, detaching (errno %d).\n", + thread_id, wperrno); internal_ptrace(PTRACE_DETACH, thread_id, NULL, NULL); return false; } @@ -135,13 +131,14 @@ void ThreadSuspender::ResumeAllThreads() { int pterrno; if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL), &pterrno)) { - if (SanitizerVerbosity > 0) + if (common_flags()->verbosity) Report("Detached from thread %d.\n", tid); } else { // Either the thread is dead, or we are already detached. // The latter case is possible, for instance, if this function was called // from a signal handler. - Report("Could not detach from thread %d (errno %d).\n", tid, pterrno); + if (common_flags()->verbosity) + Report("Could not detach from thread %d (errno %d).\n", tid, pterrno); } } } @@ -186,13 +183,16 @@ static const int kUnblockedSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, struct TracerThreadArgument { StopTheWorldCallback callback; void *callback_argument; - // The tracer thread waits on this mutex while the parent finished its + // The tracer thread waits on this mutex while the parent finishes its // preparations. BlockingMutex mutex; + uptr parent_pid; }; +static DieCallbackType old_die_callback; + // Signal handler to wake up suspended threads when the tracer thread dies. -void TracerThreadSignalHandler(int signum, siginfo_t *siginfo, void *) { +void TracerThreadSignalHandler(int signum, void *siginfo, void *) { if (thread_suspender_instance != NULL) { if (signum == SIGABRT) thread_suspender_instance->KillAllThreads(); @@ -202,6 +202,19 @@ void TracerThreadSignalHandler(int signum, siginfo_t *siginfo, void *) { internal__exit((signum == SIGABRT) ? 1 : 2); } +static void TracerThreadDieCallback() { + // Generally a call to Die() in the tracer thread should be fatal to the + // parent process as well, because they share the address space. + // This really only works correctly if all the threads are suspended at this + // point. So we correctly handle calls to Die() from within the callback, but + // not those that happen before or after the callback. Hopefully there aren't + // a lot of opportunities for that to happen... + if (thread_suspender_instance) + thread_suspender_instance->KillAllThreads(); + if (old_die_callback) + old_die_callback(); +} + // Size of alternative stack for signal handlers in the tracer thread. static const int kHandlerStackSize = 4096; @@ -210,10 +223,17 @@ static int TracerThread(void* argument) { TracerThreadArgument *tracer_thread_argument = (TracerThreadArgument *)argument; + internal_prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + // Check if parent is already dead. + if (internal_getppid() != tracer_thread_argument->parent_pid) + internal__exit(4); + // Wait for the parent thread to finish preparations. tracer_thread_argument->mutex.Lock(); tracer_thread_argument->mutex.Unlock(); + SetDieCallback(TracerThreadDieCallback); + ThreadSuspender thread_suspender(internal_getppid()); // Global pointer for the signal handler. thread_suspender_instance = &thread_suspender; @@ -230,17 +250,18 @@ static int TracerThread(void* argument) { // the mask we inherited from the caller thread. for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); signal_index++) { - struct sigaction new_sigaction; + __sanitizer_kernel_sigaction_t new_sigaction; internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); - new_sigaction.sa_sigaction = TracerThreadSignalHandler; + new_sigaction.sigaction = TracerThreadSignalHandler; new_sigaction.sa_flags = SA_ONSTACK | SA_SIGINFO; - sigfillset(&new_sigaction.sa_mask); - sigaction(kUnblockedSignals[signal_index], &new_sigaction, NULL); + internal_sigfillset(&new_sigaction.sa_mask); + internal_sigaction(kUnblockedSignals[signal_index], &new_sigaction, NULL); } int exit_code = 0; if (!thread_suspender.SuspendAllThreads()) { - Report("Failed suspending threads.\n"); + if (common_flags()->verbosity) + Report("Failed suspending threads.\n"); exit_code = 3; } else { tracer_thread_argument->callback(thread_suspender.suspended_threads_list(), @@ -278,50 +299,84 @@ class ScopedStackSpaceWithGuard { uptr guard_start_; }; -static sigset_t blocked_sigset; -static sigset_t old_sigset; -static struct sigaction old_sigactions[ARRAY_SIZE(kUnblockedSignals)]; +// We have a limitation on the stack frame size, so some stuff had to be moved +// into globals. +static __sanitizer_kernel_sigset_t blocked_sigset; +static __sanitizer_kernel_sigset_t old_sigset; +static __sanitizer_kernel_sigaction_t old_sigactions + [ARRAY_SIZE(kUnblockedSignals)]; -void StopTheWorld(StopTheWorldCallback callback, void *argument) { - // Block all signals that can be blocked safely, and install default handlers - // for the remaining signals. - // We cannot allow user-defined handlers to run while the ThreadSuspender - // thread is active, because they could conceivably call some libc functions - // which modify errno (which is shared between the two threads). - sigfillset(&blocked_sigset); - for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); - signal_index++) { - // Remove the signal from the set of blocked signals. - sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]); - // Install the default handler. - struct sigaction new_sigaction; - internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); - new_sigaction.sa_handler = SIG_DFL; - sigfillset(&new_sigaction.sa_mask); - sigaction(kUnblockedSignals[signal_index], &new_sigaction, - &old_sigactions[signal_index]); +class StopTheWorldScope { + public: + StopTheWorldScope() { + // Block all signals that can be blocked safely, and install + // default handlers for the remaining signals. + // We cannot allow user-defined handlers to run while the ThreadSuspender + // thread is active, because they could conceivably call some libc functions + // which modify errno (which is shared between the two threads). + internal_sigfillset(&blocked_sigset); + for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); + signal_index++) { + // Remove the signal from the set of blocked signals. + internal_sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]); + // Install the default handler. + __sanitizer_kernel_sigaction_t new_sigaction; + internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); + new_sigaction.handler = SIG_DFL; + internal_sigfillset(&new_sigaction.sa_mask); + internal_sigaction(kUnblockedSignals[signal_index], &new_sigaction, + &old_sigactions[signal_index]); + } + int sigprocmask_status = + internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset); + CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail + // Make this process dumpable. Processes that are not dumpable cannot be + // attached to. + process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0); + if (!process_was_dumpable_) + internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + old_die_callback = GetDieCallback(); } - int sigprocmask_status = sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset); - CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail - // Make this process dumpable. Processes that are not dumpable cannot be - // attached to. - int process_was_dumpable = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0); - if (!process_was_dumpable) - internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + + ~StopTheWorldScope() { + SetDieCallback(old_die_callback); + // Restore the dumpable flag. + if (!process_was_dumpable_) + internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); + // Restore the signal handlers. + for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); + signal_index++) { + internal_sigaction(kUnblockedSignals[signal_index], + &old_sigactions[signal_index], NULL); + } + internal_sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset); + } + + private: + int process_was_dumpable_; +}; + +void StopTheWorld(StopTheWorldCallback callback, void *argument) { + StopTheWorldScope in_stoptheworld; // Prepare the arguments for TracerThread. struct TracerThreadArgument tracer_thread_argument; tracer_thread_argument.callback = callback; tracer_thread_argument.callback_argument = argument; + tracer_thread_argument.parent_pid = internal_getpid(); const uptr kTracerStackSize = 2 * 1024 * 1024; ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize); // Block the execution of TracerThread until after we have set ptrace // permissions. tracer_thread_argument.mutex.Lock(); - pid_t tracer_pid = clone(TracerThread, tracer_stack.Bottom(), - CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, - &tracer_thread_argument, 0, 0, 0); - if (tracer_pid < 0) { - Report("Failed spawning a tracer thread (errno %d).\n", errno); + uptr tracer_pid = internal_clone( + TracerThread, tracer_stack.Bottom(), + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, + &tracer_thread_argument, 0 /* parent_tidptr */, 0 /* newtls */, 0 + /* child_tidptr */); + int local_errno = 0; + if (internal_iserror(tracer_pid, &local_errno)) { + if (common_flags()->verbosity) + Report("Failed spawning a tracer thread (errno %d).\n", local_errno); tracer_thread_argument.mutex.Unlock(); } else { // On some systems we have to explicitly declare that we want to be traced @@ -336,20 +391,12 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { // At this point, any signal will either be blocked or kill us, so waitpid // should never return (and set errno) while the tracer thread is alive. uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL); - int wperrno; - if (internal_iserror(waitpid_status, &wperrno)) - Report("Waiting on the tracer thread failed (errno %d).\n", wperrno); - } - // Restore the dumpable flag. - if (!process_was_dumpable) - internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); - // Restore the signal handlers. - for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); - signal_index++) { - sigaction(kUnblockedSignals[signal_index], - &old_sigactions[signal_index], NULL); + if (internal_iserror(waitpid_status, &local_errno)) { + if (common_flags()->verbosity) + Report("Waiting on the tracer thread failed (errno %d).\n", + local_errno); + } } - sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset); } // Platform-specific methods from SuspendedThreadsList. @@ -373,6 +420,10 @@ typedef user_regs_struct regs_struct; typedef pt_regs regs_struct; #define REG_SP gpr[PT_R1] +#elif defined(__mips__) +typedef struct user regs_struct; +#define REG_SP regs[EF_REG29] + #else #error "Unsupported architecture" #endif // SANITIZER_ANDROID && defined(__arm__) @@ -385,7 +436,8 @@ int SuspendedThreadsList::GetRegistersAndSP(uptr index, int pterrno; if (internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, NULL, ®s), &pterrno)) { - Report("Could not get registers from thread %d (errno %d).\n", + if (common_flags()->verbosity) + Report("Could not get registers from thread %d (errno %d).\n", tid, pterrno); return -1; } @@ -400,4 +452,4 @@ uptr SuspendedThreadsList::RegisterCount() { } } // namespace __sanitizer -#endif // SANITIZER_LINUX +#endif // SANITIZER_LINUX && defined(__x86_64__) diff --git a/lib/sanitizer_common/sanitizer_suppressions.cc b/lib/sanitizer_common/sanitizer_suppressions.cc new file mode 100644 index 000000000000..5f3d2cee8cef --- /dev/null +++ b/lib/sanitizer_common/sanitizer_suppressions.cc @@ -0,0 +1,153 @@ +//===-- sanitizer_suppressions.cc -----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Suppression parsing/matching code shared between TSan and LSan. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_suppressions.h" + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +static const char *const kTypeStrings[SuppressionTypeCount] = { + "none", "race", "mutex", "thread", "signal", "leak", "called_from_lib" +}; + +bool TemplateMatch(char *templ, const char *str) { + if (str == 0 || str[0] == 0) + return false; + bool start = false; + if (templ && templ[0] == '^') { + start = true; + templ++; + } + bool asterisk = false; + while (templ && templ[0]) { + if (templ[0] == '*') { + templ++; + start = false; + asterisk = true; + continue; + } + if (templ[0] == '$') + return str[0] == 0 || asterisk; + if (str[0] == 0) + return false; + char *tpos = (char*)internal_strchr(templ, '*'); + char *tpos1 = (char*)internal_strchr(templ, '$'); + if (tpos == 0 || (tpos1 && tpos1 < tpos)) + tpos = tpos1; + if (tpos != 0) + tpos[0] = 0; + const char *str0 = str; + const char *spos = internal_strstr(str, templ); + str = spos + internal_strlen(templ); + templ = tpos; + if (tpos) + tpos[0] = tpos == tpos1 ? '$' : '*'; + if (spos == 0) + return false; + if (start && spos != str0) + return false; + start = false; + asterisk = false; + } + return true; +} + +bool SuppressionContext::Match(const char *str, SuppressionType type, + Suppression **s) { + can_parse_ = false; + uptr i; + for (i = 0; i < suppressions_.size(); i++) + if (type == suppressions_[i].type && + TemplateMatch(suppressions_[i].templ, str)) + break; + if (i == suppressions_.size()) return false; + *s = &suppressions_[i]; + return true; +} + +static const char *StripPrefix(const char *str, const char *prefix) { + while (str && *str == *prefix) { + str++; + prefix++; + } + if (!*prefix) + return str; + return 0; +} + +void SuppressionContext::Parse(const char *str) { + // Context must not mutate once Match has been called. + CHECK(can_parse_); + const char *line = str; + while (line) { + while (line[0] == ' ' || line[0] == '\t') + line++; + const char *end = internal_strchr(line, '\n'); + if (end == 0) + end = line + internal_strlen(line); + if (line != end && line[0] != '#') { + const char *end2 = end; + while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t')) + end2--; + int type; + for (type = 0; type < SuppressionTypeCount; type++) { + const char *next_char = StripPrefix(line, kTypeStrings[type]); + if (next_char && *next_char == ':') { + line = ++next_char; + break; + } + } + if (type == SuppressionTypeCount) { + Printf("%s: failed to parse suppressions\n", SanitizerToolName); + Die(); + } + Suppression s; + s.type = static_cast<SuppressionType>(type); + s.templ = (char*)InternalAlloc(end2 - line + 1); + internal_memcpy(s.templ, line, end2 - line); + s.templ[end2 - line] = 0; + s.hit_count = 0; + s.weight = 0; + suppressions_.push_back(s); + } + if (end[0] == 0) + break; + line = end + 1; + } +} + +uptr SuppressionContext::SuppressionCount() const { + return suppressions_.size(); +} + +const Suppression *SuppressionContext::SuppressionAt(uptr i) const { + CHECK_LT(i, suppressions_.size()); + return &suppressions_[i]; +} + +void SuppressionContext::GetMatched( + InternalMmapVector<Suppression *> *matched) { + for (uptr i = 0; i < suppressions_.size(); i++) + if (suppressions_[i].hit_count) + matched->push_back(&suppressions_[i]); +} + +const char *SuppressionTypeString(SuppressionType t) { + CHECK(t < SuppressionTypeCount); + return kTypeStrings[t]; +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_suppressions.h b/lib/sanitizer_common/sanitizer_suppressions.h new file mode 100644 index 000000000000..92cb09365b5e --- /dev/null +++ b/lib/sanitizer_common/sanitizer_suppressions.h @@ -0,0 +1,61 @@ +//===-- sanitizer_suppressions.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Suppression parsing/matching code shared between TSan and LSan. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_SUPPRESSIONS_H +#define SANITIZER_SUPPRESSIONS_H + +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +enum SuppressionType { + SuppressionNone, + SuppressionRace, + SuppressionMutex, + SuppressionThread, + SuppressionSignal, + SuppressionLeak, + SuppressionLib, + SuppressionTypeCount +}; + +struct Suppression { + SuppressionType type; + char *templ; + unsigned hit_count; + uptr weight; +}; + +class SuppressionContext { + public: + SuppressionContext() : suppressions_(1), can_parse_(true) {} + void Parse(const char *str); + bool Match(const char* str, SuppressionType type, Suppression **s); + uptr SuppressionCount() const; + const Suppression *SuppressionAt(uptr i) const; + void GetMatched(InternalMmapVector<Suppression *> *matched); + + private: + InternalMmapVector<Suppression> suppressions_; + bool can_parse_; + + friend class SuppressionContextTest; +}; + +const char *SuppressionTypeString(SuppressionType t); + +bool TemplateMatch(char *templ, const char *str); + +} // namespace __sanitizer + +#endif // SANITIZER_SUPPRESSIONS_H diff --git a/lib/sanitizer_common/sanitizer_symbolizer.cc b/lib/sanitizer_common/sanitizer_symbolizer.cc new file mode 100644 index 000000000000..2290767b0930 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_symbolizer.cc @@ -0,0 +1,63 @@ +//===-- sanitizer_symbolizer.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 shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +Symbolizer *Symbolizer::symbolizer_; +StaticSpinMutex Symbolizer::init_mu_; +LowLevelAllocator Symbolizer::symbolizer_allocator_; + +Symbolizer *Symbolizer::GetOrNull() { + SpinMutexLock l(&init_mu_); + return symbolizer_; +} + +Symbolizer *Symbolizer::Get() { + SpinMutexLock l(&init_mu_); + RAW_CHECK_MSG(symbolizer_ != 0, "Using uninitialized symbolizer!"); + return symbolizer_; +} + +Symbolizer *Symbolizer::Disable() { + CHECK_EQ(0, symbolizer_); + // Initialize a dummy symbolizer. + symbolizer_ = new(symbolizer_allocator_) Symbolizer; + return symbolizer_; +} + +void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook, + Symbolizer::EndSymbolizationHook end_hook) { + CHECK(start_hook_ == 0 && end_hook_ == 0); + start_hook_ = start_hook; + end_hook_ = end_hook; +} + +Symbolizer::Symbolizer() : start_hook_(0), end_hook_(0) {} + +Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym) + : sym_(sym) { + if (sym_->start_hook_) + sym_->start_hook_(); +} + +Symbolizer::SymbolizerScope::~SymbolizerScope() { + if (sym_->end_hook_) + sym_->end_hook_(); +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_symbolizer.h b/lib/sanitizer_common/sanitizer_symbolizer.h index ef37fd387f98..886fed20db03 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer.h +++ b/lib/sanitizer_common/sanitizer_symbolizer.h @@ -7,26 +7,21 @@ // //===----------------------------------------------------------------------===// // -// Symbolizer is intended to be used by both -// AddressSanitizer and ThreadSanitizer to symbolize a given -// address. It is an analogue of addr2line utility and allows to map -// instruction address to a location in source code at run-time. +// Symbolizer is used by sanitizers to map instruction address to a location in +// source code at run-time. Symbolizer either uses __sanitizer_symbolize_* +// defined in the program, or (if they are missing) tries to find and +// launch "llvm-symbolizer" commandline tool in a separate process and +// communicate with it. // -// Symbolizer is planned to use debug information (in DWARF format) -// in a binary via interface defined in "llvm/DebugInfo/DIContext.h" -// -// Symbolizer code should be called from the run-time library of -// dynamic tools, and generally should not call memory allocation -// routines or other system library functions intercepted by those tools. -// Instead, Symbolizer code should use their replacements, defined in -// "compiler-rt/lib/sanitizer_common/sanitizer_libc.h". +// Generally we should try to avoid calling system library functions during +// symbolization (and use their replacements from sanitizer_libc.h instead). //===----------------------------------------------------------------------===// #ifndef SANITIZER_SYMBOLIZER_H #define SANITIZER_SYMBOLIZER_H +#include "sanitizer_allocator_internal.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" -// WARNING: Do not include system headers here. See details above. namespace __sanitizer { @@ -42,8 +37,14 @@ struct AddressInfo { AddressInfo() { internal_memset(this, 0, sizeof(AddressInfo)); } + // Deletes all strings and sets all fields to zero. - void Clear(); + void Clear() { + InternalFree(module); + InternalFree(function); + InternalFree(file); + internal_memset(this, 0, sizeof(AddressInfo)); + } void FillAddressAndModuleInfo(uptr addr, const char *mod_name, uptr mod_offset) { @@ -62,60 +63,84 @@ struct DataInfo { uptr size; }; -// Fills at most "max_frames" elements of "frames" with descriptions -// for a given address (in all inlined functions). Returns the number -// of descriptions actually filled. -// This function should NOT be called from two threads simultaneously. -uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames) - SANITIZER_WEAK_ATTRIBUTE; -bool SymbolizeData(uptr address, DataInfo *info); - -bool IsSymbolizerAvailable(); -void FlushSymbolizer(); // releases internal caches (if any) - -// Attempts to demangle the provided C++ mangled name. -const char *Demangle(const char *Name); - -// Starts external symbolizer program in a subprocess. Sanitizer communicates -// with external symbolizer via pipes. -bool InitializeExternalSymbolizer(const char *path_to_symbolizer); - -class LoadedModule { +class Symbolizer { public: - LoadedModule(const char *module_name, uptr base_address); - void addAddressRange(uptr beg, uptr end); - bool containsAddress(uptr address) const; - - const char *full_name() const { return full_name_; } - uptr base_address() const { return base_address_; } + /// Returns platform-specific implementation of Symbolizer. The symbolizer + /// must be initialized (with init or disable) before calling this function. + static Symbolizer *Get(); + /// Returns platform-specific implementation of Symbolizer, or null if not + /// initialized. + static Symbolizer *GetOrNull(); + /// Returns platform-specific implementation of Symbolizer. Will + /// automatically initialize symbolizer as if by calling Init(0) if needed. + static Symbolizer *GetOrInit(); + /// Initialize and return the symbolizer, given an optional path to an + /// external symbolizer. The path argument is only required for legacy + /// reasons as this function will check $PATH for an external symbolizer. Not + /// thread safe. + static Symbolizer *Init(const char* path_to_external = 0); + /// Initialize the symbolizer in a disabled state. Not thread safe. + static Symbolizer *Disable(); + // Fills at most "max_frames" elements of "frames" with descriptions + // for a given address (in all inlined functions). Returns the number + // of descriptions actually filled. + virtual uptr SymbolizeCode(uptr address, AddressInfo *frames, + uptr max_frames) { + return 0; + } + virtual bool SymbolizeData(uptr address, DataInfo *info) { + return false; + } + virtual bool IsAvailable() { + return false; + } + virtual bool IsExternalAvailable() { + return false; + } + // Release internal caches (if any). + virtual void Flush() {} + // Attempts to demangle the provided C++ mangled name. + virtual const char *Demangle(const char *name) { + return name; + } + virtual void PrepareForSandboxing() {} + + // Allow user to install hooks that would be called before/after Symbolizer + // does the actual file/line info fetching. Specific sanitizers may need this + // to distinguish system library calls made in user code from calls made + // during in-process symbolization. + typedef void (*StartSymbolizationHook)(); + typedef void (*EndSymbolizationHook)(); + // May be called at most once. + void AddHooks(StartSymbolizationHook start_hook, + EndSymbolizationHook end_hook); private: - struct AddressRange { - uptr beg; - uptr end; + /// Platform-specific function for creating a Symbolizer object. + static Symbolizer *PlatformInit(const char *path_to_external); + /// Create a symbolizer and store it to symbolizer_ without checking if one + /// already exists. Not thread safe. + static Symbolizer *CreateAndStore(const char *path_to_external); + + static Symbolizer *symbolizer_; + static StaticSpinMutex init_mu_; + + protected: + Symbolizer(); + + static LowLevelAllocator symbolizer_allocator_; + + StartSymbolizationHook start_hook_; + EndSymbolizationHook end_hook_; + class SymbolizerScope { + public: + explicit SymbolizerScope(const Symbolizer *sym); + ~SymbolizerScope(); + private: + const Symbolizer *sym_; }; - char *full_name_; - uptr base_address_; - static const uptr kMaxNumberOfAddressRanges = 6; - AddressRange ranges_[kMaxNumberOfAddressRanges]; - uptr n_ranges_; }; -// Creates external symbolizer connected via pipe, user should write -// to output_fd and read from input_fd. -bool StartSymbolizerSubprocess(const char *path_to_symbolizer, - int *input_fd, int *output_fd); - -// OS-dependent function that fills array with descriptions of at most -// "max_modules" currently loaded modules. Returns the number of -// initialized modules. If filter is nonzero, ignores modules for which -// filter(full_name) is false. -typedef bool (*string_predicate_t)(const char *); -uptr GetListOfModules(LoadedModule *modules, uptr max_modules, - string_predicate_t filter); - -void SymbolizerPrepareForSandboxing(); - } // namespace __sanitizer #endif // SANITIZER_SYMBOLIZER_H diff --git a/lib/sanitizer_common/sanitizer_symbolizer_itanium.cc b/lib/sanitizer_common/sanitizer_symbolizer_itanium.cc deleted file mode 100644 index e20fb91f0eb5..000000000000 --- a/lib/sanitizer_common/sanitizer_symbolizer_itanium.cc +++ /dev/null @@ -1,44 +0,0 @@ -//===-- sanitizer_symbolizer_itanium.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 shared between the sanitizer run-time libraries. -// Itanium C++ ABI-specific implementation of symbolizer parts. -//===----------------------------------------------------------------------===// - -#include "sanitizer_platform.h" -#if SANITIZER_MAC || SANITIZER_LINUX - -#include "sanitizer_symbolizer.h" - -#include <stdlib.h> - -// C++ demangling function, as required by Itanium C++ ABI. This is weak, -// because we do not require a C++ ABI library to be linked to a program -// using sanitizers; if it's not present, we'll just use the mangled name. -namespace __cxxabiv1 { - extern "C" char *__cxa_demangle(const char *mangled, char *buffer, - size_t *length, int *status) - SANITIZER_WEAK_ATTRIBUTE; -} - -const char *__sanitizer::Demangle(const char *MangledName) { - // FIXME: __cxa_demangle aggressively insists on allocating memory. - // There's not much we can do about that, short of providing our - // own demangler (libc++abi's implementation could be adapted so that - // it does not allocate). For now, we just call it anyway, and we leak - // the returned value. - if (__cxxabiv1::__cxa_demangle) - if (const char *Demangled = - __cxxabiv1::__cxa_demangle(MangledName, 0, 0, 0)) - return Demangled; - - return MangledName; -} - -#endif // SANITIZER_MAC || SANITIZER_LINUX diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc index ad339e21a927..b431e51e24d6 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc @@ -8,446 +8,32 @@ //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer -// run-time libraries. See sanitizer_symbolizer.h for details. +// run-time libraries. //===----------------------------------------------------------------------===// -#include "sanitizer_common.h" -#include "sanitizer_placement_new.h" -#include "sanitizer_procmaps.h" +#include "sanitizer_internal_defs.h" #include "sanitizer_symbolizer.h" namespace __sanitizer { -void AddressInfo::Clear() { - InternalFree(module); - InternalFree(function); - InternalFree(file); - internal_memset(this, 0, sizeof(AddressInfo)); +Symbolizer *Symbolizer::CreateAndStore(const char *path_to_external) { + Symbolizer *platform_symbolizer = PlatformInit(path_to_external); + if (!platform_symbolizer) + return Disable(); + symbolizer_ = platform_symbolizer; + return platform_symbolizer; } -LoadedModule::LoadedModule(const char *module_name, uptr base_address) { - full_name_ = internal_strdup(module_name); - base_address_ = base_address; - n_ranges_ = 0; +Symbolizer *Symbolizer::Init(const char *path_to_external) { + CHECK_EQ(0, symbolizer_); + return CreateAndStore(path_to_external); } -void LoadedModule::addAddressRange(uptr beg, uptr end) { - CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges); - ranges_[n_ranges_].beg = beg; - ranges_[n_ranges_].end = end; - n_ranges_++; -} - -bool LoadedModule::containsAddress(uptr address) const { - for (uptr i = 0; i < n_ranges_; i++) { - if (ranges_[i].beg <= address && address < ranges_[i].end) - return true; - } - return false; -} - -// Extracts the prefix of "str" that consists of any characters not -// present in "delims" string, and copies this prefix to "result", allocating -// space for it. -// Returns a pointer to "str" after skipping extracted prefix and first -// delimiter char. -static const char *ExtractToken(const char *str, const char *delims, - char **result) { - uptr prefix_len = internal_strcspn(str, delims); - *result = (char*)InternalAlloc(prefix_len + 1); - internal_memcpy(*result, str, prefix_len); - (*result)[prefix_len] = '\0'; - const char *prefix_end = str + prefix_len; - if (*prefix_end != '\0') prefix_end++; - return prefix_end; -} - -// Same as ExtractToken, but converts extracted token to integer. -static const char *ExtractInt(const char *str, const char *delims, - int *result) { - char *buff; - const char *ret = ExtractToken(str, delims, &buff); - if (buff != 0) { - *result = (int)internal_atoll(buff); - } - InternalFree(buff); - return ret; -} - -static const char *ExtractUptr(const char *str, const char *delims, - uptr *result) { - char *buff; - const char *ret = ExtractToken(str, delims, &buff); - if (buff != 0) { - *result = (uptr)internal_atoll(buff); - } - InternalFree(buff); - return ret; -} - -// ExternalSymbolizer encapsulates communication between the tool and -// external symbolizer program, running in a different subprocess, -// For now we assume the following protocol: -// For each request of the form -// <module_name> <module_offset> -// passed to STDIN, external symbolizer prints to STDOUT response: -// <function_name> -// <file_name>:<line_number>:<column_number> -// <function_name> -// <file_name>:<line_number>:<column_number> -// ... -// <empty line> -class ExternalSymbolizer { - public: - ExternalSymbolizer(const char *path, int input_fd, int output_fd) - : path_(path), - input_fd_(input_fd), - output_fd_(output_fd), - times_restarted_(0) { - CHECK(path_); - CHECK_NE(input_fd_, kInvalidFd); - CHECK_NE(output_fd_, kInvalidFd); - } - - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { - CHECK(module_name); - internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n", - is_data ? "DATA " : "", module_name, module_offset); - if (!writeToSymbolizer(buffer_, internal_strlen(buffer_))) - return 0; - if (!readFromSymbolizer(buffer_, kBufferSize)) - return 0; - return buffer_; - } - - bool Restart() { - if (times_restarted_ >= kMaxTimesRestarted) return false; - times_restarted_++; - internal_close(input_fd_); - internal_close(output_fd_); - return StartSymbolizerSubprocess(path_, &input_fd_, &output_fd_); - } - - void Flush() { - } - - private: - bool readFromSymbolizer(char *buffer, uptr max_length) { - if (max_length == 0) - return true; - uptr read_len = 0; - while (true) { - uptr just_read = internal_read(input_fd_, buffer + read_len, - max_length - read_len); - // We can't read 0 bytes, as we don't expect external symbolizer to close - // its stdout. - if (just_read == 0 || just_read == (uptr)-1) { - Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_); - return false; - } - read_len += just_read; - // Empty line marks the end of symbolizer output. - if (read_len >= 2 && buffer[read_len - 1] == '\n' && - buffer[read_len - 2] == '\n') { - break; - } - } - return true; - } - - bool writeToSymbolizer(const char *buffer, uptr length) { - if (length == 0) - return true; - uptr write_len = internal_write(output_fd_, buffer, length); - if (write_len == 0 || write_len == (uptr)-1) { - Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_); - return false; - } - return true; - } - - const char *path_; - int input_fd_; - int output_fd_; - - static const uptr kBufferSize = 16 * 1024; - char buffer_[kBufferSize]; - - static const uptr kMaxTimesRestarted = 5; - uptr times_restarted_; -}; - -static LowLevelAllocator symbolizer_allocator; // Linker initialized. - -#if SANITIZER_SUPPORTS_WEAK_HOOKS -extern "C" { -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE -bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset, - char *Buffer, int MaxLength); -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE -bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset, - char *Buffer, int MaxLength); -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_symbolize_flush(); -} // extern "C" - -class InternalSymbolizer { - public: - typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int); - - static InternalSymbolizer *get() { - if (__sanitizer_symbolize_code != 0 && - __sanitizer_symbolize_data != 0) { - void *mem = symbolizer_allocator.Allocate(sizeof(InternalSymbolizer)); - return new(mem) InternalSymbolizer(); - } - return 0; - } - - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { - SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data - : __sanitizer_symbolize_code; - if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize)) - return buffer_; - return 0; - } - - void Flush() { - if (__sanitizer_symbolize_flush) - __sanitizer_symbolize_flush(); - } - - private: - InternalSymbolizer() { } - - static const int kBufferSize = 16 * 1024; - char buffer_[kBufferSize]; -}; -#else // SANITIZER_SUPPORTS_WEAK_HOOKS - -class InternalSymbolizer { - public: - static InternalSymbolizer *get() { return 0; } - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { - return 0; - } - void Flush() { - } -}; - -#endif // SANITIZER_SUPPORTS_WEAK_HOOKS - -class Symbolizer { - // This class has no constructor, as global constructors are forbidden in - // sanitizer_common. It should be linker initialized instead. - public: - uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames) { - if (max_frames == 0) - return 0; - LoadedModule *module = FindModuleForAddress(addr); - if (module == 0) - return 0; - const char *module_name = module->full_name(); - uptr module_offset = addr - module->base_address(); - const char *str = SendCommand(false, module_name, module_offset); - if (str == 0) { - // External symbolizer was not initialized or failed. Fill only data - // about module name and offset. - AddressInfo *info = &frames[0]; - info->Clear(); - info->FillAddressAndModuleInfo(addr, module_name, module_offset); - return 1; - } - uptr frame_id = 0; - for (frame_id = 0; frame_id < max_frames; frame_id++) { - AddressInfo *info = &frames[frame_id]; - char *function_name = 0; - str = ExtractToken(str, "\n", &function_name); - CHECK(function_name); - if (function_name[0] == '\0') { - // There are no more frames. - break; - } - info->Clear(); - info->FillAddressAndModuleInfo(addr, module_name, module_offset); - info->function = function_name; - // Parse <file>:<line>:<column> buffer. - char *file_line_info = 0; - str = ExtractToken(str, "\n", &file_line_info); - CHECK(file_line_info); - const char *line_info = ExtractToken(file_line_info, ":", &info->file); - line_info = ExtractInt(line_info, ":", &info->line); - line_info = ExtractInt(line_info, "", &info->column); - InternalFree(file_line_info); - - // Functions and filenames can be "??", in which case we write 0 - // to address info to mark that names are unknown. - if (0 == internal_strcmp(info->function, "??")) { - InternalFree(info->function); - info->function = 0; - } - if (0 == internal_strcmp(info->file, "??")) { - InternalFree(info->file); - info->file = 0; - } - } - if (frame_id == 0) { - // Make sure we return at least one frame. - AddressInfo *info = &frames[0]; - info->Clear(); - info->FillAddressAndModuleInfo(addr, module_name, module_offset); - frame_id = 1; - } - return frame_id; - } - - bool SymbolizeData(uptr addr, DataInfo *info) { - LoadedModule *module = FindModuleForAddress(addr); - if (module == 0) - return false; - const char *module_name = module->full_name(); - uptr module_offset = addr - module->base_address(); - internal_memset(info, 0, sizeof(*info)); - info->address = addr; - info->module = internal_strdup(module_name); - info->module_offset = module_offset; - const char *str = SendCommand(true, module_name, module_offset); - if (str == 0) - return true; - str = ExtractToken(str, "\n", &info->name); - str = ExtractUptr(str, " ", &info->start); - str = ExtractUptr(str, "\n", &info->size); - info->start += module->base_address(); - return true; - } - - bool InitializeExternalSymbolizer(const char *path_to_symbolizer) { - int input_fd, output_fd; - if (!StartSymbolizerSubprocess(path_to_symbolizer, &input_fd, &output_fd)) - return false; - void *mem = symbolizer_allocator.Allocate(sizeof(ExternalSymbolizer)); - external_symbolizer_ = new(mem) ExternalSymbolizer(path_to_symbolizer, - input_fd, output_fd); - return true; - } - - bool IsSymbolizerAvailable() { - if (internal_symbolizer_ == 0) - internal_symbolizer_ = InternalSymbolizer::get(); - return internal_symbolizer_ || external_symbolizer_; - } - - void Flush() { - if (internal_symbolizer_) - internal_symbolizer_->Flush(); - if (external_symbolizer_) - external_symbolizer_->Flush(); - } - - private: - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { - // First, try to use internal symbolizer. - if (!IsSymbolizerAvailable()) { - return 0; - } - if (internal_symbolizer_) { - return internal_symbolizer_->SendCommand(is_data, module_name, - module_offset); - } - // Otherwise, fall back to external symbolizer. - if (external_symbolizer_ == 0) { - ReportExternalSymbolizerError( - "WARNING: Trying to symbolize code, but external " - "symbolizer is not initialized!\n"); - return 0; - } - for (;;) { - char *reply = external_symbolizer_->SendCommand(is_data, module_name, - module_offset); - if (reply) - return reply; - // Try to restart symbolizer subprocess. If we don't succeed, forget - // about it and don't try to use it later. - if (!external_symbolizer_->Restart()) { - ReportExternalSymbolizerError( - "WARNING: Failed to use and restart external symbolizer!\n"); - external_symbolizer_ = 0; - return 0; - } - } - } - - LoadedModule *FindModuleForAddress(uptr address) { - bool modules_were_reloaded = false; - if (modules_ == 0 || !modules_fresh_) { - modules_ = (LoadedModule*)(symbolizer_allocator.Allocate( - kMaxNumberOfModuleContexts * sizeof(LoadedModule))); - CHECK(modules_); - n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts, - /* filter */ 0); - // FIXME: Return this check when GetListOfModules is implemented on Mac. - // CHECK_GT(n_modules_, 0); - CHECK_LT(n_modules_, kMaxNumberOfModuleContexts); - modules_fresh_ = true; - modules_were_reloaded = true; - } - for (uptr i = 0; i < n_modules_; i++) { - if (modules_[i].containsAddress(address)) { - return &modules_[i]; - } - } - // Reload the modules and look up again, if we haven't tried it yet. - if (!modules_were_reloaded) { - // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors. - // It's too aggressive to reload the list of modules each time we fail - // to find a module for a given address. - modules_fresh_ = false; - return FindModuleForAddress(address); - } - return 0; - } - - void ReportExternalSymbolizerError(const char *msg) { - // Don't use atomics here for now, as SymbolizeCode can't be called - // from multiple threads anyway. - static bool reported; - if (!reported) { - Report(msg); - reported = true; - } - } - - // 16K loaded modules should be enough for everyone. - static const uptr kMaxNumberOfModuleContexts = 1 << 14; - LoadedModule *modules_; // Array of module descriptions is leaked. - uptr n_modules_; - // If stale, need to reload the modules before looking up addresses. - bool modules_fresh_; - - ExternalSymbolizer *external_symbolizer_; // Leaked. - InternalSymbolizer *internal_symbolizer_; // Leaked. -}; - -static Symbolizer symbolizer; // Linker initialized. - -uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames) { - return symbolizer.SymbolizeCode(address, frames, max_frames); -} - -bool SymbolizeData(uptr address, DataInfo *info) { - return symbolizer.SymbolizeData(address, info); -} - -bool InitializeExternalSymbolizer(const char *path_to_symbolizer) { - return symbolizer.InitializeExternalSymbolizer(path_to_symbolizer); -} - -bool IsSymbolizerAvailable() { - return symbolizer.IsSymbolizerAvailable(); -} - -void FlushSymbolizer() { - symbolizer.Flush(); +Symbolizer *Symbolizer::GetOrInit() { + SpinMutexLock l(&init_mu_); + if (symbolizer_ == 0) + return CreateAndStore(0); + return symbolizer_; } } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_symbolizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_linux_libcdep.cc deleted file mode 100644 index 82ce50e0aacb..000000000000 --- a/lib/sanitizer_common/sanitizer_symbolizer_linux_libcdep.cc +++ /dev/null @@ -1,227 +0,0 @@ -//===-- sanitizer_symbolizer_linux_libcdep.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 shared between AddressSanitizer and ThreadSanitizer -// run-time libraries. -// Linux-specific implementation of symbolizer parts. -//===----------------------------------------------------------------------===// - -#include "sanitizer_platform.h" -#if SANITIZER_LINUX -#include "sanitizer_common.h" -#include "sanitizer_internal_defs.h" -#include "sanitizer_libc.h" -#include "sanitizer_placement_new.h" -#include "sanitizer_symbolizer.h" - -// Android NDK r8e elf.h depends on stdint.h without including the latter. -#include <stdint.h> - -#include <elf.h> -#include <errno.h> -#include <poll.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -#if !SANITIZER_ANDROID -#include <link.h> -#endif - -namespace __sanitizer { - -static const int kSymbolizerStartupTimeMillis = 10; - -bool StartSymbolizerSubprocess(const char *path_to_symbolizer, - int *input_fd, int *output_fd) { - if (!FileExists(path_to_symbolizer)) { - Report("WARNING: invalid path to external symbolizer!\n"); - return false; - } - - int *infd = NULL; - int *outfd = NULL; - // The client program may close its stdin and/or stdout and/or stderr - // thus allowing socketpair to reuse file descriptors 0, 1 or 2. - // In this case the communication between the forked processes may be - // broken if either the parent or the child tries to close or duplicate - // these descriptors. The loop below produces two pairs of file - // descriptors, each greater than 2 (stderr). - int sock_pair[5][2]; - for (int i = 0; i < 5; i++) { - if (pipe(sock_pair[i]) == -1) { - for (int j = 0; j < i; j++) { - internal_close(sock_pair[j][0]); - internal_close(sock_pair[j][1]); - } - Report("WARNING: Can't create a socket pair to start " - "external symbolizer (errno: %d)\n", errno); - return false; - } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) { - if (infd == NULL) { - infd = sock_pair[i]; - } else { - outfd = sock_pair[i]; - for (int j = 0; j < i; j++) { - if (sock_pair[j] == infd) continue; - internal_close(sock_pair[j][0]); - internal_close(sock_pair[j][1]); - } - break; - } - } - } - CHECK(infd); - CHECK(outfd); - - int pid = fork(); - if (pid == -1) { - // Fork() failed. - internal_close(infd[0]); - internal_close(infd[1]); - internal_close(outfd[0]); - internal_close(outfd[1]); - Report("WARNING: failed to fork external symbolizer " - " (errno: %d)\n", errno); - return false; - } else if (pid == 0) { - // Child subprocess. - internal_close(STDOUT_FILENO); - internal_close(STDIN_FILENO); - internal_dup2(outfd[0], STDIN_FILENO); - internal_dup2(infd[1], STDOUT_FILENO); - internal_close(outfd[0]); - internal_close(outfd[1]); - internal_close(infd[0]); - internal_close(infd[1]); - for (int fd = getdtablesize(); fd > 2; fd--) - internal_close(fd); - execl(path_to_symbolizer, path_to_symbolizer, (char*)0); - internal__exit(1); - } - - // Continue execution in parent process. - internal_close(outfd[0]); - internal_close(infd[1]); - *input_fd = infd[0]; - *output_fd = outfd[1]; - - // Check that symbolizer subprocess started successfully. - int pid_status; - SleepForMillis(kSymbolizerStartupTimeMillis); - int exited_pid = waitpid(pid, &pid_status, WNOHANG); - if (exited_pid != 0) { - // Either waitpid failed, or child has already exited. - Report("WARNING: external symbolizer didn't start up correctly!\n"); - return false; - } - - return true; -} - -#if SANITIZER_ANDROID -uptr GetListOfModules(LoadedModule *modules, uptr max_modules, - string_predicate_t filter) { - return 0; -} - -void SymbolizerPrepareForSandboxing() { - // Do nothing on Android. -} -#else // SANITIZER_ANDROID -typedef ElfW(Phdr) Elf_Phdr; - -struct DlIteratePhdrData { - LoadedModule *modules; - uptr current_n; - bool first; - uptr max_n; - string_predicate_t filter; -}; - -static const uptr kMaxPathLength = 512; - -static char proc_self_exe_cache_str[kMaxPathLength]; -static uptr proc_self_exe_cache_len = 0; - -static uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { - uptr module_name_len = internal_readlink( - "/proc/self/exe", buf, buf_len); - int readlink_error; - if (internal_iserror(buf_len, &readlink_error)) { - if (proc_self_exe_cache_len) { - // If available, use the cached module name. - CHECK_LE(proc_self_exe_cache_len, buf_len); - internal_strncpy(buf, proc_self_exe_cache_str, buf_len); - module_name_len = internal_strlen(proc_self_exe_cache_str); - } else { - // We can't read /proc/self/exe for some reason, assume the name of the - // binary is unknown. - Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, " - "some stack frames may not be symbolized\n", readlink_error); - module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe"); - } - CHECK_LT(module_name_len, buf_len); - buf[module_name_len] = '\0'; - } - return module_name_len; -} - -static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { - DlIteratePhdrData *data = (DlIteratePhdrData*)arg; - if (data->current_n == data->max_n) - return 0; - InternalScopedBuffer<char> module_name(kMaxPathLength); - module_name.data()[0] = '\0'; - if (data->first) { - data->first = false; - // First module is the binary itself. - ReadBinaryName(module_name.data(), module_name.size()); - } else if (info->dlpi_name) { - internal_strncpy(module_name.data(), info->dlpi_name, module_name.size()); - } - if (module_name.data()[0] == '\0') - return 0; - if (data->filter && !data->filter(module_name.data())) - return 0; - void *mem = &data->modules[data->current_n]; - LoadedModule *cur_module = new(mem) LoadedModule(module_name.data(), - info->dlpi_addr); - data->current_n++; - for (int i = 0; i < info->dlpi_phnum; i++) { - const Elf_Phdr *phdr = &info->dlpi_phdr[i]; - if (phdr->p_type == PT_LOAD) { - uptr cur_beg = info->dlpi_addr + phdr->p_vaddr; - uptr cur_end = cur_beg + phdr->p_memsz; - cur_module->addAddressRange(cur_beg, cur_end); - } - } - return 0; -} - -uptr GetListOfModules(LoadedModule *modules, uptr max_modules, - string_predicate_t filter) { - CHECK(modules); - DlIteratePhdrData data = {modules, 0, true, max_modules, filter}; - dl_iterate_phdr(dl_iterate_phdr_cb, &data); - return data.current_n; -} - -void SymbolizerPrepareForSandboxing() { - if (!proc_self_exe_cache_len) { - proc_self_exe_cache_len = - ReadBinaryName(proc_self_exe_cache_str, kMaxPathLength); - } -} -#endif // SANITIZER_ANDROID - -} // namespace __sanitizer - -#endif // SANITIZER_LINUX diff --git a/lib/sanitizer_common/sanitizer_symbolizer_mac.cc b/lib/sanitizer_common/sanitizer_symbolizer_mac.cc deleted file mode 100644 index 9d96690bfda2..000000000000 --- a/lib/sanitizer_common/sanitizer_symbolizer_mac.cc +++ /dev/null @@ -1,40 +0,0 @@ -//===-- sanitizer_symbolizer_mac.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 shared between AddressSanitizer and ThreadSanitizer -// run-time libraries. -// Mac-specific implementation of symbolizer parts. -//===----------------------------------------------------------------------===// - -#include "sanitizer_platform.h" -#if SANITIZER_MAC -#include "sanitizer_internal_defs.h" -#include "sanitizer_symbolizer.h" - -namespace __sanitizer { - -bool StartSymbolizerSubprocess(const char *path_to_symbolizer, - int *input_fd, int *output_fd) { - UNIMPLEMENTED(); -} - -uptr GetListOfModules(LoadedModule *modules, uptr max_modules, - string_predicate_t filter) { - // FIXME: Actually implement this on Mac. Just using MemoryMappingLayout - // may be enough for this on Mac. - return 0; -} - -void SymbolizerPrepareForSandboxing() { - // Do nothing on Mac. -} - -} // namespace __sanitizer - -#endif // SANITIZER_MAC diff --git a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc new file mode 100644 index 000000000000..de11832870e3 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -0,0 +1,601 @@ +//===-- sanitizer_symbolizer_posix_libcdep.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 shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// POSIX-specific implementation of symbolizer parts. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_POSIX +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_linux.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_symbolizer.h" + +#include <errno.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +// C++ demangling function, as required by Itanium C++ ABI. This is weak, +// because we do not require a C++ ABI library to be linked to a program +// using sanitizers; if it's not present, we'll just use the mangled name. +namespace __cxxabiv1 { + extern "C" SANITIZER_WEAK_ATTRIBUTE + char *__cxa_demangle(const char *mangled, char *buffer, + size_t *length, int *status); +} + +namespace __sanitizer { + +// Attempts to demangle the name via __cxa_demangle from __cxxabiv1. +static const char *DemangleCXXABI(const char *name) { + // FIXME: __cxa_demangle aggressively insists on allocating memory. + // There's not much we can do about that, short of providing our + // own demangler (libc++abi's implementation could be adapted so that + // it does not allocate). For now, we just call it anyway, and we leak + // the returned value. + if (__cxxabiv1::__cxa_demangle) + if (const char *demangled_name = + __cxxabiv1::__cxa_demangle(name, 0, 0, 0)) + return demangled_name; + + return name; +} + +#if defined(__x86_64__) +static const char* const kSymbolizerArch = "--default-arch=x86_64"; +#elif defined(__i386__) +static const char* const kSymbolizerArch = "--default-arch=i386"; +#elif defined(__powerpc64__) +static const char* const kSymbolizerArch = "--default-arch=powerpc64"; +#else +static const char* const kSymbolizerArch = "--default-arch=unknown"; +#endif + +static const int kSymbolizerStartupTimeMillis = 10; + +// Creates external symbolizer connected via pipe, user should write +// to output_fd and read from input_fd. +static bool StartSymbolizerSubprocess(const char *path_to_symbolizer, + int *input_fd, int *output_fd) { + if (!FileExists(path_to_symbolizer)) { + Report("WARNING: invalid path to external symbolizer!\n"); + return false; + } + + int *infd = NULL; + int *outfd = NULL; + // The client program may close its stdin and/or stdout and/or stderr + // thus allowing socketpair to reuse file descriptors 0, 1 or 2. + // In this case the communication between the forked processes may be + // broken if either the parent or the child tries to close or duplicate + // these descriptors. The loop below produces two pairs of file + // descriptors, each greater than 2 (stderr). + int sock_pair[5][2]; + for (int i = 0; i < 5; i++) { + if (pipe(sock_pair[i]) == -1) { + for (int j = 0; j < i; j++) { + internal_close(sock_pair[j][0]); + internal_close(sock_pair[j][1]); + } + Report("WARNING: Can't create a socket pair to start " + "external symbolizer (errno: %d)\n", errno); + return false; + } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) { + if (infd == NULL) { + infd = sock_pair[i]; + } else { + outfd = sock_pair[i]; + for (int j = 0; j < i; j++) { + if (sock_pair[j] == infd) continue; + internal_close(sock_pair[j][0]); + internal_close(sock_pair[j][1]); + } + break; + } + } + } + CHECK(infd); + CHECK(outfd); + + int pid = fork(); + if (pid == -1) { + // Fork() failed. + internal_close(infd[0]); + internal_close(infd[1]); + internal_close(outfd[0]); + internal_close(outfd[1]); + Report("WARNING: failed to fork external symbolizer " + " (errno: %d)\n", errno); + return false; + } else if (pid == 0) { + // Child subprocess. + internal_close(STDOUT_FILENO); + internal_close(STDIN_FILENO); + internal_dup2(outfd[0], STDIN_FILENO); + internal_dup2(infd[1], STDOUT_FILENO); + internal_close(outfd[0]); + internal_close(outfd[1]); + internal_close(infd[0]); + internal_close(infd[1]); + for (int fd = getdtablesize(); fd > 2; fd--) + internal_close(fd); + execl(path_to_symbolizer, path_to_symbolizer, kSymbolizerArch, (char*)0); + internal__exit(1); + } + + // Continue execution in parent process. + internal_close(outfd[0]); + internal_close(infd[1]); + *input_fd = infd[0]; + *output_fd = outfd[1]; + + // Check that symbolizer subprocess started successfully. + int pid_status; + SleepForMillis(kSymbolizerStartupTimeMillis); + int exited_pid = waitpid(pid, &pid_status, WNOHANG); + if (exited_pid != 0) { + // Either waitpid failed, or child has already exited. + Report("WARNING: external symbolizer didn't start up correctly!\n"); + return false; + } + + return true; +} + +// Extracts the prefix of "str" that consists of any characters not +// present in "delims" string, and copies this prefix to "result", allocating +// space for it. +// Returns a pointer to "str" after skipping extracted prefix and first +// delimiter char. +static const char *ExtractToken(const char *str, const char *delims, + char **result) { + uptr prefix_len = internal_strcspn(str, delims); + *result = (char*)InternalAlloc(prefix_len + 1); + internal_memcpy(*result, str, prefix_len); + (*result)[prefix_len] = '\0'; + const char *prefix_end = str + prefix_len; + if (*prefix_end != '\0') prefix_end++; + return prefix_end; +} + +// Same as ExtractToken, but converts extracted token to integer. +static const char *ExtractInt(const char *str, const char *delims, + int *result) { + char *buff; + const char *ret = ExtractToken(str, delims, &buff); + if (buff != 0) { + *result = (int)internal_atoll(buff); + } + InternalFree(buff); + return ret; +} + +static const char *ExtractUptr(const char *str, const char *delims, + uptr *result) { + char *buff; + const char *ret = ExtractToken(str, delims, &buff); + if (buff != 0) { + *result = (uptr)internal_atoll(buff); + } + InternalFree(buff); + return ret; +} + +// ExternalSymbolizer encapsulates communication between the tool and +// external symbolizer program, running in a different subprocess, +// For now we assume the following protocol: +// For each request of the form +// <module_name> <module_offset> +// passed to STDIN, external symbolizer prints to STDOUT response: +// <function_name> +// <file_name>:<line_number>:<column_number> +// <function_name> +// <file_name>:<line_number>:<column_number> +// ... +// <empty line> +class ExternalSymbolizer { + public: + ExternalSymbolizer(const char *path, int input_fd, int output_fd) + : path_(path), + input_fd_(input_fd), + output_fd_(output_fd), + times_restarted_(0) { + CHECK(path_); + CHECK_NE(input_fd_, kInvalidFd); + CHECK_NE(output_fd_, kInvalidFd); + } + + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + CHECK(module_name); + internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n", + is_data ? "DATA " : "", module_name, module_offset); + if (!writeToSymbolizer(buffer_, internal_strlen(buffer_))) + return 0; + if (!readFromSymbolizer(buffer_, kBufferSize)) + return 0; + return buffer_; + } + + bool Restart() { + if (times_restarted_ >= kMaxTimesRestarted) return false; + times_restarted_++; + internal_close(input_fd_); + internal_close(output_fd_); + return StartSymbolizerSubprocess(path_, &input_fd_, &output_fd_); + } + + void Flush() { + } + + private: + bool readFromSymbolizer(char *buffer, uptr max_length) { + if (max_length == 0) + return true; + uptr read_len = 0; + while (true) { + uptr just_read = internal_read(input_fd_, buffer + read_len, + max_length - read_len); + // We can't read 0 bytes, as we don't expect external symbolizer to close + // its stdout. + if (just_read == 0 || just_read == (uptr)-1) { + Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_); + return false; + } + read_len += just_read; + // Empty line marks the end of symbolizer output. + if (read_len >= 2 && buffer[read_len - 1] == '\n' && + buffer[read_len - 2] == '\n') { + break; + } + } + return true; + } + + bool writeToSymbolizer(const char *buffer, uptr length) { + if (length == 0) + return true; + uptr write_len = internal_write(output_fd_, buffer, length); + if (write_len == 0 || write_len == (uptr)-1) { + Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_); + return false; + } + return true; + } + + const char *path_; + int input_fd_; + int output_fd_; + + static const uptr kBufferSize = 16 * 1024; + char buffer_[kBufferSize]; + + static const uptr kMaxTimesRestarted = 5; + uptr times_restarted_; +}; + +#if SANITIZER_SUPPORTS_WEAK_HOOKS +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset, + char *Buffer, int MaxLength); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset, + char *Buffer, int MaxLength); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __sanitizer_symbolize_flush(); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +int __sanitizer_symbolize_demangle(const char *Name, char *Buffer, + int MaxLength); +} // extern "C" + +class InternalSymbolizer { + public: + typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int); + + static InternalSymbolizer *get(LowLevelAllocator *alloc) { + if (__sanitizer_symbolize_code != 0 && + __sanitizer_symbolize_data != 0) { + return new(*alloc) InternalSymbolizer(); + } + return 0; + } + + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data + : __sanitizer_symbolize_code; + if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize)) + return buffer_; + return 0; + } + + void Flush() { + if (__sanitizer_symbolize_flush) + __sanitizer_symbolize_flush(); + } + + const char *Demangle(const char *name) { + if (__sanitizer_symbolize_demangle) { + for (uptr res_length = 1024; + res_length <= InternalSizeClassMap::kMaxSize;) { + char *res_buff = static_cast<char*>(InternalAlloc(res_length)); + uptr req_length = + __sanitizer_symbolize_demangle(name, res_buff, res_length); + if (req_length > res_length) { + res_length = req_length + 1; + InternalFree(res_buff); + continue; + } + return res_buff; + } + } + return name; + } + + private: + InternalSymbolizer() { } + + static const int kBufferSize = 16 * 1024; + static const int kMaxDemangledNameSize = 1024; + char buffer_[kBufferSize]; +}; +#else // SANITIZER_SUPPORTS_WEAK_HOOKS + +class InternalSymbolizer { + public: + static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; } + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + return 0; + } + void Flush() { } + const char *Demangle(const char *name) { return name; } +}; + +#endif // SANITIZER_SUPPORTS_WEAK_HOOKS + +class POSIXSymbolizer : public Symbolizer { + public: + POSIXSymbolizer(ExternalSymbolizer *external_symbolizer, + InternalSymbolizer *internal_symbolizer) + : Symbolizer(), + external_symbolizer_(external_symbolizer), + internal_symbolizer_(internal_symbolizer) {} + + uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames) { + BlockingMutexLock l(&mu_); + if (max_frames == 0) + return 0; + LoadedModule *module = FindModuleForAddress(addr); + if (module == 0) + return 0; + const char *module_name = module->full_name(); + uptr module_offset = addr - module->base_address(); + const char *str = SendCommand(false, module_name, module_offset); + if (str == 0) { + // External symbolizer was not initialized or failed. Fill only data + // about module name and offset. + AddressInfo *info = &frames[0]; + info->Clear(); + info->FillAddressAndModuleInfo(addr, module_name, module_offset); + return 1; + } + uptr frame_id = 0; + for (frame_id = 0; frame_id < max_frames; frame_id++) { + AddressInfo *info = &frames[frame_id]; + char *function_name = 0; + str = ExtractToken(str, "\n", &function_name); + CHECK(function_name); + if (function_name[0] == '\0') { + // There are no more frames. + break; + } + info->Clear(); + info->FillAddressAndModuleInfo(addr, module_name, module_offset); + info->function = function_name; + // Parse <file>:<line>:<column> buffer. + char *file_line_info = 0; + str = ExtractToken(str, "\n", &file_line_info); + CHECK(file_line_info); + const char *line_info = ExtractToken(file_line_info, ":", &info->file); + line_info = ExtractInt(line_info, ":", &info->line); + line_info = ExtractInt(line_info, "", &info->column); + InternalFree(file_line_info); + + // Functions and filenames can be "??", in which case we write 0 + // to address info to mark that names are unknown. + if (0 == internal_strcmp(info->function, "??")) { + InternalFree(info->function); + info->function = 0; + } + if (0 == internal_strcmp(info->file, "??")) { + InternalFree(info->file); + info->file = 0; + } + } + if (frame_id == 0) { + // Make sure we return at least one frame. + AddressInfo *info = &frames[0]; + info->Clear(); + info->FillAddressAndModuleInfo(addr, module_name, module_offset); + frame_id = 1; + } + return frame_id; + } + + bool SymbolizeData(uptr addr, DataInfo *info) { + BlockingMutexLock l(&mu_); + LoadedModule *module = FindModuleForAddress(addr); + if (module == 0) + return false; + const char *module_name = module->full_name(); + uptr module_offset = addr - module->base_address(); + internal_memset(info, 0, sizeof(*info)); + info->address = addr; + info->module = internal_strdup(module_name); + info->module_offset = module_offset; + const char *str = SendCommand(true, module_name, module_offset); + if (str == 0) + return true; + str = ExtractToken(str, "\n", &info->name); + str = ExtractUptr(str, " ", &info->start); + str = ExtractUptr(str, "\n", &info->size); + info->start += module->base_address(); + return true; + } + + bool IsAvailable() { + return internal_symbolizer_ != 0 || external_symbolizer_ != 0; + } + + bool IsExternalAvailable() { + return external_symbolizer_ != 0; + } + + void Flush() { + BlockingMutexLock l(&mu_); + if (internal_symbolizer_ != 0) { + SymbolizerScope sym_scope(this); + internal_symbolizer_->Flush(); + } + if (external_symbolizer_ != 0) + external_symbolizer_->Flush(); + } + + const char *Demangle(const char *name) { + BlockingMutexLock l(&mu_); + // Run hooks even if we don't use internal symbolizer, as cxxabi + // demangle may call system functions. + SymbolizerScope sym_scope(this); + if (internal_symbolizer_ != 0) + return internal_symbolizer_->Demangle(name); + return DemangleCXXABI(name); + } + + void PrepareForSandboxing() { +#if SANITIZER_LINUX && !SANITIZER_ANDROID + BlockingMutexLock l(&mu_); + // Cache /proc/self/exe on Linux. + CacheBinaryName(); +#endif + } + + private: + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + mu_.CheckLocked(); + // First, try to use internal symbolizer. + if (internal_symbolizer_) { + SymbolizerScope sym_scope(this); + return internal_symbolizer_->SendCommand(is_data, module_name, + module_offset); + } + // Otherwise, fall back to external symbolizer. + if (external_symbolizer_ == 0) { + ReportExternalSymbolizerError( + "WARNING: Trying to symbolize code, but external " + "symbolizer is not initialized!\n"); + return 0; + } + for (;;) { + char *reply = external_symbolizer_->SendCommand(is_data, module_name, + module_offset); + if (reply) + return reply; + // Try to restart symbolizer subprocess. If we don't succeed, forget + // about it and don't try to use it later. + if (!external_symbolizer_->Restart()) { + ReportExternalSymbolizerError( + "WARNING: Failed to use and restart external symbolizer!\n"); + external_symbolizer_ = 0; + return 0; + } + } + } + + LoadedModule *FindModuleForAddress(uptr address) { + mu_.CheckLocked(); + bool modules_were_reloaded = false; + if (modules_ == 0 || !modules_fresh_) { + modules_ = (LoadedModule*)(symbolizer_allocator_.Allocate( + kMaxNumberOfModuleContexts * sizeof(LoadedModule))); + CHECK(modules_); + n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts, + /* filter */ 0); + // FIXME: Return this check when GetListOfModules is implemented on Mac. + // CHECK_GT(n_modules_, 0); + CHECK_LT(n_modules_, kMaxNumberOfModuleContexts); + modules_fresh_ = true; + modules_were_reloaded = true; + } + for (uptr i = 0; i < n_modules_; i++) { + if (modules_[i].containsAddress(address)) { + return &modules_[i]; + } + } + // Reload the modules and look up again, if we haven't tried it yet. + if (!modules_were_reloaded) { + // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors. + // It's too aggressive to reload the list of modules each time we fail + // to find a module for a given address. + modules_fresh_ = false; + return FindModuleForAddress(address); + } + return 0; + } + + void ReportExternalSymbolizerError(const char *msg) { + // Don't use atomics here for now, as SymbolizeCode can't be called + // from multiple threads anyway. + static bool reported; + if (!reported) { + Report(msg); + reported = true; + } + } + + // 16K loaded modules should be enough for everyone. + static const uptr kMaxNumberOfModuleContexts = 1 << 14; + LoadedModule *modules_; // Array of module descriptions is leaked. + uptr n_modules_; + // If stale, need to reload the modules before looking up addresses. + bool modules_fresh_; + BlockingMutex mu_; + + ExternalSymbolizer *external_symbolizer_; // Leaked. + InternalSymbolizer *const internal_symbolizer_; // Leaked. +}; + +Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) { + InternalSymbolizer* internal_symbolizer = + InternalSymbolizer::get(&symbolizer_allocator_); + ExternalSymbolizer *external_symbolizer = 0; + + if (!internal_symbolizer) { + if (!path_to_external || path_to_external[0] == '\0') + path_to_external = FindPathToBinary("llvm-symbolizer"); + + int input_fd, output_fd; + if (path_to_external && + StartSymbolizerSubprocess(path_to_external, &input_fd, &output_fd)) { + external_symbolizer = new(symbolizer_allocator_) + ExternalSymbolizer(path_to_external, input_fd, output_fd); + } + } + + return new(symbolizer_allocator_) + POSIXSymbolizer(external_symbolizer, internal_symbolizer); +} + +} // namespace __sanitizer + +#endif // SANITIZER_POSIX diff --git a/lib/sanitizer_common/sanitizer_symbolizer_win.cc b/lib/sanitizer_common/sanitizer_symbolizer_win.cc index 993261aab7b0..5d451eff62bd 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer_win.cc +++ b/lib/sanitizer_common/sanitizer_symbolizer_win.cc @@ -14,30 +14,11 @@ #include "sanitizer_platform.h" #if SANITIZER_WINDOWS -#include <windows.h> - -#include "sanitizer_internal_defs.h" #include "sanitizer_symbolizer.h" namespace __sanitizer { -bool StartSymbolizerSubprocess(const char *path_to_symbolizer, - int *input_fd, int *output_fd) { - UNIMPLEMENTED(); -} - -uptr GetListOfModules(LoadedModule *modules, uptr max_modules, - string_predicate_t filter) { - UNIMPLEMENTED(); -}; - -void SymbolizerPrepareForSandboxing() { - // Do nothing on Windows. -} - -const char *Demangle(const char *MangledName) { - return MangledName; -} +Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) { return 0; } } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc b/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc index e084b84ab118..5ccb83a236bd 100644 --- a/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc +++ b/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc @@ -13,7 +13,8 @@ static uptr internal_syscall(u64 nr) { u64 retval; - asm volatile("syscall" : "=a"(retval) : "a"(nr) : "rcx", "r11"); + asm volatile("syscall" : "=a"(retval) : "a"(nr) : "rcx", "r11", + "memory", "cc"); return retval; } @@ -21,7 +22,7 @@ template <typename T1> static uptr internal_syscall(u64 nr, T1 arg1) { u64 retval; asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1) : - "rcx", "r11"); + "rcx", "r11", "memory", "cc"); return retval; } @@ -29,7 +30,7 @@ template <typename T1, typename T2> static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2) { u64 retval; asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), - "S"((u64)arg2) : "rcx", "r11"); + "S"((u64)arg2) : "rcx", "r11", "memory", "cc"); return retval; } @@ -37,7 +38,7 @@ template <typename T1, typename T2, typename T3> static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3) { u64 retval; asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), - "S"((u64)arg2), "d"((u64)arg3) : "rcx", "r11"); + "S"((u64)arg2), "d"((u64)arg3) : "rcx", "r11", "memory", "cc"); return retval; } @@ -47,7 +48,7 @@ static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { asm volatile("mov %5, %%r10;" "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4) : - "rcx", "r11", "r10"); + "rcx", "r11", "r10", "memory", "cc"); return retval; } @@ -59,7 +60,7 @@ static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4, "mov %6, %%r8;" "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4), "r"((u64)arg5) : - "rcx", "r11", "r10", "r8"); + "rcx", "r11", "r10", "r8", "memory", "cc"); return retval; } @@ -73,7 +74,8 @@ static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4, "mov %7, %%r9;" "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4), "r"((u64)arg5), - "r"((u64)arg6) : "rcx", "r11", "r10", "r8", "r9"); + "r"((u64)arg6) : "rcx", "r11", "r10", "r8", "r9", + "memory", "cc"); return retval; } diff --git a/lib/sanitizer_common/sanitizer_thread_registry.cc b/lib/sanitizer_common/sanitizer_thread_registry.cc index 466dc3b8a27f..bfa29a19cd2d 100644 --- a/lib/sanitizer_common/sanitizer_thread_registry.cc +++ b/lib/sanitizer_common/sanitizer_thread_registry.cc @@ -85,7 +85,7 @@ void ThreadContextBase::Reset() { // ThreadRegistry implementation. -const u32 ThreadRegistry::kUnknownTid = -1U; +const u32 ThreadRegistry::kUnknownTid = ~0U; ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads, u32 thread_quarantine_size) @@ -200,6 +200,18 @@ void ThreadRegistry::SetThreadName(u32 tid, const char *name) { tctx->SetName(name); } +void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) { + BlockingMutexLock l(&mtx_); + for (u32 tid = 0; tid < n_contexts_; tid++) { + ThreadContextBase *tctx = threads_[tid]; + if (tctx != 0 && tctx->user_id == user_id && + tctx->status != ThreadStatusInvalid) { + tctx->SetName(name); + return; + } + } +} + void ThreadRegistry::DetachThread(u32 tid) { BlockingMutexLock l(&mtx_); CHECK_LT(tid, n_contexts_); diff --git a/lib/sanitizer_common/sanitizer_thread_registry.h b/lib/sanitizer_common/sanitizer_thread_registry.h index 6072e7c0a002..a59bba570e88 100644 --- a/lib/sanitizer_common/sanitizer_thread_registry.h +++ b/lib/sanitizer_common/sanitizer_thread_registry.h @@ -109,6 +109,7 @@ class ThreadRegistry { ThreadContextBase *FindThreadContextByOsIDLocked(uptr os_id); void SetThreadName(u32 tid, const char *name); + void SetThreadNameByUserId(uptr user_id, const char *name); void DetachThread(u32 tid); void JoinThread(u32 tid, void *arg); void FinishThread(u32 tid); diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc index e76f1d1f7fa6..362c8c99a74c 100644 --- a/lib/sanitizer_common/sanitizer_win.cc +++ b/lib/sanitizer_common/sanitizer_win.cc @@ -40,6 +40,12 @@ uptr GetMmapGranularity() { return 1U << 16; // FIXME: is this configurable? } +uptr GetMaxVirtualAddress() { + SYSTEM_INFO si; + GetSystemInfo(&si); + return (uptr)si.lpMaximumApplicationAddress; +} + bool FileExists(const char *filename) { UNIMPLEMENTED(); } @@ -124,7 +130,7 @@ void *MapFileToMemory(const char *file_name, uptr *buff_size) { } static const int kMaxEnvNameLength = 128; -static const int kMaxEnvValueLength = 32767; +static const DWORD kMaxEnvValueLength = 32767; namespace { @@ -190,6 +196,11 @@ void SetStackSizeLimitInBytes(uptr limit) { UNIMPLEMENTED(); } +char *FindPathToBinary(const char *name) { + // Nothing here for now. + return 0; +} + void SleepForSeconds(int seconds) { Sleep(seconds * 1000); } @@ -198,11 +209,20 @@ void SleepForMillis(int millis) { Sleep(millis); } +u64 NanoTime() { + return 0; +} + void Abort() { abort(); _exit(-1); // abort is not NORETURN on Windows. } +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + UNIMPLEMENTED(); +}; + #ifndef SANITIZER_GO int Atexit(void (*function)(void)) { return atexit(function); @@ -341,39 +361,48 @@ void InitTlsSize() { void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size) { +#ifdef SANITIZER_GO + *stk_addr = 0; + *stk_size = 0; + *tls_addr = 0; + *tls_size = 0; +#else uptr stack_top, stack_bottom; GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); *stk_addr = stack_bottom; *stk_size = stack_top - stack_bottom; *tls_addr = 0; *tls_size = 0; +#endif } -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, - uptr stack_top, uptr stack_bottom, bool fast) { - (void)fast; - (void)stack_top; - (void)stack_bottom; - stack->max_size = max_s; - void *tmp[kStackTraceMax]; - +void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { // FIXME: CaptureStackBackTrace might be too slow for us. // FIXME: Compare with StackWalk64. // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc - uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0); - uptr offset = 0; + size = CaptureStackBackTrace(2, Min(max_depth, kStackTraceMax), + (void**)trace, 0); // Skip the RTL frames by searching for the PC in the stacktrace. - // FIXME: this doesn't work well for the malloc/free stacks yet. - for (uptr i = 0; i < cs_ret; i++) { - if (pc != (uptr)tmp[i]) - continue; - offset = i; - break; - } + uptr pc_location = LocatePcInTrace(pc); + PopStackFrames(pc_location); +} - stack->size = cs_ret - offset; - for (uptr i = 0; i < stack->size; i++) - stack->trace[i] = (uptr)tmp[i + offset]; +void MaybeOpenReportFile() { + // Windows doesn't have native fork, and we don't support Cygwin or other + // environments that try to fake it, so the initial report_fd will always be + // correct. +} + +void RawWrite(const char *buffer) { + static const char *kRawWriteError = + "RawWrite can't output requested buffer!\n"; + uptr length = (uptr)internal_strlen(buffer); + if (length != internal_write(report_fd, buffer, length)) { + // stderr may be closed, but we may be able to print to the debugger + // instead. This is the case when launching a program from Visual Studio, + // and the following routine should write to its console. + OutputDebugStringA(buffer); + } } } // namespace __sanitizer diff --git a/lib/sanitizer_common/scripts/check_lint.sh b/lib/sanitizer_common/scripts/check_lint.sh index 3240f6f18cee..5f1bd4ba4316 100755 --- a/lib/sanitizer_common/scripts/check_lint.sh +++ b/lib/sanitizer_common/scripts/check_lint.sh @@ -1,26 +1,21 @@ #!/bin/bash -set -e - SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # Guess path to LLVM_CHECKOUT if not provided if [ "${LLVM_CHECKOUT}" == "" ]; then LLVM_CHECKOUT="${SCRIPT_DIR}/../../../../../" - echo "LLVM Checkout: ${LLVM_CHECKOUT}" fi # Cpplint setup -cd ${SCRIPT_DIR} -if [ ! -d cpplint ]; then - svn co http://google-styleguide.googlecode.com/svn/trunk/cpplint cpplint -else - (cd cpplint && svn up) +CPPLINT=${SCRIPT_DIR}/cpplint.py +if [ "${PYTHON_EXECUTABLE}" != "" ]; then + CPPLINT="${PYTHON_EXECUTABLE} ${CPPLINT}" fi -CPPLINT=${SCRIPT_DIR}/cpplint/cpplint.py # Filters # TODO: remove some of these filters +LLVM_LINT_FILTER=-,+whitespace COMMON_LINT_FILTER=-build/include,-build/header_guard,-legal/copyright,-whitespace/comments,-readability/casting,\ -build/namespaces ASAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int @@ -34,62 +29,86 @@ LSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} LSAN_LIT_TEST_LINT_FILTER=${LSAN_RTL_LINT_FILTER},-whitespace/line_length COMMON_RTL_INC_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int,-runtime/sizeof,-runtime/printf SANITIZER_INCLUDES_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int - +MKTEMP="mktemp -q /tmp/tmp.XXXXXXXXXX" cd ${LLVM_CHECKOUT} -# LLVM Instrumentation -LLVM_INSTRUMENTATION=lib/Transforms/Instrumentation -LLVM_LINT_FILTER=-,+whitespace -${CPPLINT} --filter=${LLVM_LINT_FILTER} ${LLVM_INSTRUMENTATION}/*Sanitizer.cpp \ - ${LLVM_INSTRUMENTATION}/BlackList.* +EXITSTATUS=0 +ERROR_LOG=$(${MKTEMP}) + +run_lint() { + FILTER=$1 + shift + TASK_LOG=$(${MKTEMP}) + ${CPPLINT} --filter=${FILTER} "$@" 2>$TASK_LOG + if [ "$?" != "0" ]; then + cat $TASK_LOG | grep -v "Done processing" | grep -v "Total errors found" \ + | grep -v "Skipping input" >> $ERROR_LOG + fi + if [[ "${SILENT}" != "1" ]]; then + cat $TASK_LOG + fi +} + +run_lint ${LLVM_LINT_FILTER} --filter=${LLVM_LINT_FILTER} \ + lib/Transforms/Instrumentation/*Sanitizer.cpp \ + lib/Transforms/Utils/SpecialCaseList.cpp & COMPILER_RT=projects/compiler-rt - # Headers SANITIZER_INCLUDES=${COMPILER_RT}/include/sanitizer -${CPPLINT} --filter=${SANITIZER_INCLUDES_LINT_FILTER} ${SANITIZER_INCLUDES}/*.h +run_lint ${SANITIZER_INCLUDES_LINT_FILTER} ${SANITIZER_INCLUDES}/*.h & # Sanitizer_common COMMON_RTL=${COMPILER_RT}/lib/sanitizer_common -${CPPLINT} --filter=${COMMON_RTL_INC_LINT_FILTER} ${COMMON_RTL}/*.{cc,h} -${CPPLINT} --filter=${COMMON_RTL_INC_LINT_FILTER} ${COMMON_RTL}/tests/*.cc +run_lint ${COMMON_RTL_INC_LINT_FILTER} ${COMMON_RTL}/*.{cc,h} \ + ${COMMON_RTL}/tests/*.cc & # Interception INTERCEPTION=${COMPILER_RT}/lib/interception -${CPPLINT} --filter=${ASAN_RTL_LINT_FILTER} ${INTERCEPTION}/*.{cc,h} +run_lint ${ASAN_RTL_LINT_FILTER} ${INTERCEPTION}/*.{cc,h} & # ASan ASAN_RTL=${COMPILER_RT}/lib/asan -${CPPLINT} --filter=${ASAN_RTL_LINT_FILTER} ${ASAN_RTL}/*.{cc,h} -${CPPLINT} --filter=${ASAN_TEST_LINT_FILTER} ${ASAN_RTL}/tests/*.{cc,h} -${CPPLINT} --filter=${ASAN_LIT_TEST_LINT_FILTER} ${ASAN_RTL}/lit_tests/*.cc \ - ${ASAN_RTL}/lit_tests/*/*.cc \ +run_lint ${ASAN_RTL_LINT_FILTER} ${ASAN_RTL}/*.{cc,h} & +run_lint ${ASAN_TEST_LINT_FILTER} ${ASAN_RTL}/tests/*.{cc,h} & +run_lint ${ASAN_LIT_TEST_LINT_FILTER} ${ASAN_RTL}/lit_tests/*/*.cc & # TSan TSAN_RTL=${COMPILER_RT}/lib/tsan -${CPPLINT} --filter=${TSAN_RTL_LINT_FILTER} ${TSAN_RTL}/rtl/*.{cc,h} -${CPPLINT} --filter=${TSAN_TEST_LINT_FILTER} ${TSAN_RTL}/tests/rtl/*.{cc,h} \ - ${TSAN_RTL}/tests/unit/*.cc -${CPPLINT} --filter=${TSAN_LIT_TEST_LINT_FILTER} ${TSAN_RTL}/lit_tests/*.cc +run_lint ${TSAN_RTL_LINT_FILTER} ${TSAN_RTL}/rtl/*.{cc,h} & +run_lint ${TSAN_TEST_LINT_FILTER} ${TSAN_RTL}/tests/rtl/*.{cc,h} \ + ${TSAN_RTL}/tests/unit/*.cc & +run_lint ${TSAN_LIT_TEST_LINT_FILTER} ${TSAN_RTL}/lit_tests/*.cc & # MSan MSAN_RTL=${COMPILER_RT}/lib/msan -${CPPLINT} --filter=${MSAN_RTL_LINT_FILTER} ${MSAN_RTL}/*.{cc,h} +run_lint ${MSAN_RTL_LINT_FILTER} ${MSAN_RTL}/*.{cc,h} & # LSan LSAN_RTL=${COMPILER_RT}/lib/lsan -${CPPLINT} --filter=${LSAN_RTL_LINT_FILTER} ${LSAN_RTL}/*.{cc,h} -${CPPLINT} --filter=${LSAN_RTL_LINT_FILTER} ${LSAN_RTL}/tests/*.{cc,h} -${CPPLINT} --filter=${LSAN_LIT_TEST_LINT_FILTER} ${LSAN_RTL}/lit_tests/*.{cc,h} - -set +e +run_lint ${LSAN_RTL_LINT_FILTER} ${LSAN_RTL}/*.{cc,h} \ + ${LSAN_RTL}/tests/*.{cc,h} & +run_lint ${LSAN_LIT_TEST_LINT_FILTER} ${LSAN_RTL}/lit_tests/*/*.cc & # Misc files FILES=${COMMON_RTL}/*.inc +TMPFILES="" for FILE in $FILES; do - TMPFILE=$(mktemp -u ${FILE}.XXXXX).cc - echo "Checking $FILE" - cp -f $FILE $TMPFILE && \ - ${CPPLINT} --filter=${COMMON_RTL_INC_LINT_FILTER} $TMPFILE - rm $TMPFILE + TMPFILE="$(${MKTEMP}).$(basename ${FILE}).cc" + cp -f $FILE $TMPFILE + run_lint ${COMMON_RTL_INC_LINT_FILTER} $TMPFILE & + TMPFILES="$TMPFILES $TMPFILE" done + +wait + +for temp in $TMPFILES; do + rm -f $temp +done + +if [[ -s $ERROR_LOG ]]; then + cat $ERROR_LOG + exit 1 +fi + +exit 0 diff --git a/lib/sanitizer_common/scripts/cpplint.py b/lib/sanitizer_common/scripts/cpplint.py new file mode 100755 index 000000000000..a8c9f6784f2d --- /dev/null +++ b/lib/sanitizer_common/scripts/cpplint.py @@ -0,0 +1,4024 @@ +#!/usr/bin/python +# +# Copyright (c) 2009 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Here are some issues that I've had people identify in my code during reviews, +# that I think are possible to flag automatically in a lint tool. If these were +# caught by lint, it would save time both for myself and that of my reviewers. +# Most likely, some of these are beyond the scope of the current lint framework, +# but I think it is valuable to retain these wish-list items even if they cannot +# be immediately implemented. +# +# Suggestions +# ----------- +# - Check for no 'explicit' for multi-arg ctor +# - Check for boolean assign RHS in parens +# - Check for ctor initializer-list colon position and spacing +# - Check that if there's a ctor, there should be a dtor +# - Check accessors that return non-pointer member variables are +# declared const +# - Check accessors that return non-const pointer member vars are +# *not* declared const +# - Check for using public includes for testing +# - Check for spaces between brackets in one-line inline method +# - Check for no assert() +# - Check for spaces surrounding operators +# - Check for 0 in pointer context (should be NULL) +# - Check for 0 in char context (should be '\0') +# - Check for camel-case method name conventions for methods +# that are not simple inline getters and setters +# - Do not indent namespace contents +# - Avoid inlining non-trivial constructors in header files +# - Check for old-school (void) cast for call-sites of functions +# ignored return value +# - Check gUnit usage of anonymous namespace +# - Check for class declaration order (typedefs, consts, enums, +# ctor(s?), dtor, friend declarations, methods, member vars) +# + +"""Does google-lint on c++ files. + +The goal of this script is to identify places in the code that *may* +be in non-compliance with google style. It does not attempt to fix +up these problems -- the point is to educate. It does also not +attempt to find all problems, or to ensure that everything it does +find is legitimately a problem. + +In particular, we can get very confused by /* and // inside strings! +We do a small hack, which is to ignore //'s with "'s after them on the +same line, but it is far from perfect (in either direction). +""" + +import codecs +import copy +import getopt +import math # for log +import os +import re +import sre_compile +import string +import sys +import unicodedata + + +_USAGE = """ +Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] + [--counting=total|toplevel|detailed] + <file> [file] ... + + The style guidelines this tries to follow are those in + http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml + + Every problem is given a confidence score from 1-5, with 5 meaning we are + certain of the problem, and 1 meaning it could be a legitimate construct. + This will miss some errors, and is not a substitute for a code review. + + To suppress false-positive errors of a certain category, add a + 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) + suppresses errors of all categories on that line. + + The files passed in will be linted; at least one file must be provided. + Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. + + Flags: + + output=vs7 + By default, the output is formatted to ease emacs parsing. Visual Studio + compatible output (vs7) may also be used. Other formats are unsupported. + + verbose=# + Specify a number 0-5 to restrict errors to certain verbosity levels. + + filter=-x,+y,... + Specify a comma-separated list of category-filters to apply: only + error messages whose category names pass the filters will be printed. + (Category names are printed with the message and look like + "[whitespace/indent]".) Filters are evaluated left to right. + "-FOO" and "FOO" means "do not print categories that start with FOO". + "+FOO" means "do print categories that start with FOO". + + Examples: --filter=-whitespace,+whitespace/braces + --filter=whitespace,runtime/printf,+runtime/printf_format + --filter=-,+build/include_what_you_use + + To see a list of all the categories used in cpplint, pass no arg: + --filter= + + counting=total|toplevel|detailed + The total number of errors found is always printed. If + 'toplevel' is provided, then the count of errors in each of + the top-level categories like 'build' and 'whitespace' will + also be printed. If 'detailed' is provided, then a count + is provided for each category like 'build/class'. + + root=subdir + The root directory used for deriving header guard CPP variable. + By default, the header guard CPP variable is calculated as the relative + path to the directory that contains .git, .hg, or .svn. When this flag + is specified, the relative path is calculated from the specified + directory. If the specified directory does not exist, this flag is + ignored. + + Examples: + Assuing that src/.git exists, the header guard CPP variables for + src/chrome/browser/ui/browser.h are: + + No flag => CHROME_BROWSER_UI_BROWSER_H_ + --root=chrome => BROWSER_UI_BROWSER_H_ + --root=chrome/browser => UI_BROWSER_H_ +""" + +# We categorize each error message we print. Here are the categories. +# We want an explicit list so we can list them all in cpplint --filter=. +# If you add a new error message with a new category, add it to the list +# here! cpplint_unittest.py should tell you if you forget to do this. +# \ used for clearer layout -- pylint: disable-msg=C6013 +_ERROR_CATEGORIES = [ + 'build/class', + 'build/deprecated', + 'build/endif_comment', + 'build/explicit_make_pair', + 'build/forward_decl', + 'build/header_guard', + 'build/include', + 'build/include_alpha', + 'build/include_order', + 'build/include_what_you_use', + 'build/namespaces', + 'build/printf_format', + 'build/storage_class', + 'legal/copyright', + 'readability/alt_tokens', + 'readability/braces', + 'readability/casting', + 'readability/check', + 'readability/constructors', + 'readability/fn_size', + 'readability/function', + 'readability/multiline_comment', + 'readability/multiline_string', + 'readability/namespace', + 'readability/nolint', + 'readability/streams', + 'readability/todo', + 'readability/utf8', + 'runtime/arrays', + 'runtime/casting', + 'runtime/explicit', + 'runtime/int', + 'runtime/init', + 'runtime/invalid_increment', + 'runtime/member_string_references', + 'runtime/memset', + 'runtime/operator', + 'runtime/printf', + 'runtime/printf_format', + 'runtime/references', + 'runtime/rtti', + 'runtime/sizeof', + 'runtime/string', + 'runtime/threadsafe_fn', + 'whitespace/blank_line', + 'whitespace/braces', + 'whitespace/comma', + 'whitespace/comments', + 'whitespace/empty_loop_body', + 'whitespace/end_of_line', + 'whitespace/ending_newline', + 'whitespace/forcolon', + 'whitespace/indent', + 'whitespace/labels', + 'whitespace/line_length', + 'whitespace/newline', + 'whitespace/operators', + 'whitespace/parens', + 'whitespace/semicolon', + 'whitespace/tab', + 'whitespace/todo' + ] + +# The default state of the category filter. This is overrided by the --filter= +# flag. By default all errors are on, so only add here categories that should be +# off by default (i.e., categories that must be enabled by the --filter= flags). +# All entries here should start with a '-' or '+', as in the --filter= flag. +_DEFAULT_FILTERS = ['-build/include_alpha'] + +# We used to check for high-bit characters, but after much discussion we +# decided those were OK, as long as they were in UTF-8 and didn't represent +# hard-coded international strings, which belong in a separate i18n file. + +# Headers that we consider STL headers. +_STL_HEADERS = frozenset([ + 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', + 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', + 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', + 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', + 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', + 'utility', 'vector', 'vector.h', + ]) + + +# Non-STL C++ system headers. +_CPP_HEADERS = frozenset([ + 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', + 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', + 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', + 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', + 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', + 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', + 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream', + 'istream.h', 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', + 'numeric', 'ostream', 'ostream.h', 'parsestream.h', 'pfstream.h', + 'PlotFile.h', 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', + 'ropeimpl.h', 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', + 'stdiostream.h', 'streambuf', 'streambuf.h', 'stream.h', 'strfile.h', + 'string', 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', + 'valarray', + ]) + + +# Assertion macros. These are defined in base/logging.h and +# testing/base/gunit.h. Note that the _M versions need to come first +# for substring matching to work. +_CHECK_MACROS = [ + 'DCHECK', 'CHECK', + 'EXPECT_TRUE_M', 'EXPECT_TRUE', + 'ASSERT_TRUE_M', 'ASSERT_TRUE', + 'EXPECT_FALSE_M', 'EXPECT_FALSE', + 'ASSERT_FALSE_M', 'ASSERT_FALSE', + ] + +# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE +_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) + +for op, replacement in [('==', 'EQ'), ('!=', 'NE'), + ('>=', 'GE'), ('>', 'GT'), + ('<=', 'LE'), ('<', 'LT')]: + _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement + _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement + +for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), + ('>=', 'LT'), ('>', 'LE'), + ('<=', 'GT'), ('<', 'GE')]: + _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement + _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement + +# Alternative tokens and their replacements. For full list, see section 2.5 +# Alternative tokens [lex.digraph] in the C++ standard. +# +# Digraphs (such as '%:') are not included here since it's a mess to +# match those on a word boundary. +_ALT_TOKEN_REPLACEMENT = { + 'and': '&&', + 'bitor': '|', + 'or': '||', + 'xor': '^', + 'compl': '~', + 'bitand': '&', + 'and_eq': '&=', + 'or_eq': '|=', + 'xor_eq': '^=', + 'not': '!', + 'not_eq': '!=' + } + +# Compile regular expression that matches all the above keywords. The "[ =()]" +# bit is meant to avoid matching these keywords outside of boolean expressions. +# +# False positives include C-style multi-line comments (http://go/nsiut ) +# and multi-line strings (http://go/beujw ), but those have always been +# troublesome for cpplint. +_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( + r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') + + +# These constants define types of headers for use with +# _IncludeState.CheckNextIncludeOrder(). +_C_SYS_HEADER = 1 +_CPP_SYS_HEADER = 2 +_LIKELY_MY_HEADER = 3 +_POSSIBLE_MY_HEADER = 4 +_OTHER_HEADER = 5 + +# These constants define the current inline assembly state +_NO_ASM = 0 # Outside of inline assembly block +_INSIDE_ASM = 1 # Inside inline assembly block +_END_ASM = 2 # Last line of inline assembly block +_BLOCK_ASM = 3 # The whole block is an inline assembly block + +# Match start of assembly blocks +_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' + r'(?:\s+(volatile|__volatile__))?' + r'\s*[{(]') + + +_regexp_compile_cache = {} + +# Finds occurrences of NOLINT or NOLINT(...). +_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') + +# {str, set(int)}: a map from error categories to sets of linenumbers +# on which those errors are expected and should be suppressed. +_error_suppressions = {} + +# The root directory used for deriving header guard CPP variable. +# This is set by --root flag. +_root = None + +def ParseNolintSuppressions(filename, raw_line, linenum, error): + """Updates the global list of error-suppressions. + + Parses any NOLINT comments on the current line, updating the global + error_suppressions store. Reports an error if the NOLINT comment + was malformed. + + Args: + filename: str, the name of the input file. + raw_line: str, the line of input text, with comments. + linenum: int, the number of the current line. + error: function, an error handler. + """ + # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). + matched = _RE_SUPPRESSION.search(raw_line) + if matched: + category = matched.group(1) + if category in (None, '(*)'): # => "suppress all" + _error_suppressions.setdefault(None, set()).add(linenum) + else: + if category.startswith('(') and category.endswith(')'): + category = category[1:-1] + if category in _ERROR_CATEGORIES: + _error_suppressions.setdefault(category, set()).add(linenum) + else: + error(filename, linenum, 'readability/nolint', 5, + 'Unknown NOLINT error category: %s' % category) + + +def ResetNolintSuppressions(): + "Resets the set of NOLINT suppressions to empty." + _error_suppressions.clear() + + +def IsErrorSuppressedByNolint(category, linenum): + """Returns true if the specified error category is suppressed on this line. + + Consults the global error_suppressions map populated by + ParseNolintSuppressions/ResetNolintSuppressions. + + Args: + category: str, the category of the error. + linenum: int, the current line number. + Returns: + bool, True iff the error should be suppressed due to a NOLINT comment. + """ + return (linenum in _error_suppressions.get(category, set()) or + linenum in _error_suppressions.get(None, set())) + +def Match(pattern, s): + """Matches the string with the pattern, caching the compiled regexp.""" + # The regexp compilation caching is inlined in both Match and Search for + # performance reasons; factoring it out into a separate function turns out + # to be noticeably expensive. + if not pattern in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].match(s) + + +def Search(pattern, s): + """Searches the string for the pattern, caching the compiled regexp.""" + if not pattern in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].search(s) + + +class _IncludeState(dict): + """Tracks line numbers for includes, and the order in which includes appear. + + As a dict, an _IncludeState object serves as a mapping between include + filename and line number on which that file was included. + + Call CheckNextIncludeOrder() once for each header in the file, passing + in the type constants defined above. Calls in an illegal order will + raise an _IncludeError with an appropriate error message. + + """ + # self._section will move monotonically through this set. If it ever + # needs to move backwards, CheckNextIncludeOrder will raise an error. + _INITIAL_SECTION = 0 + _MY_H_SECTION = 1 + _C_SECTION = 2 + _CPP_SECTION = 3 + _OTHER_H_SECTION = 4 + + _TYPE_NAMES = { + _C_SYS_HEADER: 'C system header', + _CPP_SYS_HEADER: 'C++ system header', + _LIKELY_MY_HEADER: 'header this file implements', + _POSSIBLE_MY_HEADER: 'header this file may implement', + _OTHER_HEADER: 'other header', + } + _SECTION_NAMES = { + _INITIAL_SECTION: "... nothing. (This can't be an error.)", + _MY_H_SECTION: 'a header this file implements', + _C_SECTION: 'C system header', + _CPP_SECTION: 'C++ system header', + _OTHER_H_SECTION: 'other header', + } + + def __init__(self): + dict.__init__(self) + # The name of the current section. + self._section = self._INITIAL_SECTION + # The path of last found header. + self._last_header = '' + + def CanonicalizeAlphabeticalOrder(self, header_path): + """Returns a path canonicalized for alphabetical comparison. + + - replaces "-" with "_" so they both cmp the same. + - removes '-inl' since we don't require them to be after the main header. + - lowercase everything, just in case. + + Args: + header_path: Path to be canonicalized. + + Returns: + Canonicalized path. + """ + return header_path.replace('-inl.h', '.h').replace('-', '_').lower() + + def IsInAlphabeticalOrder(self, header_path): + """Check if a header is in alphabetical order with the previous header. + + Args: + header_path: Header to be checked. + + Returns: + Returns true if the header is in alphabetical order. + """ + canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) + if self._last_header > canonical_header: + return False + self._last_header = canonical_header + return True + + def CheckNextIncludeOrder(self, header_type): + """Returns a non-empty error message if the next header is out of order. + + This function also updates the internal state to be ready to check + the next include. + + Args: + header_type: One of the _XXX_HEADER constants defined above. + + Returns: + The empty string if the header is in the right order, or an + error message describing what's wrong. + + """ + error_message = ('Found %s after %s' % + (self._TYPE_NAMES[header_type], + self._SECTION_NAMES[self._section])) + + last_section = self._section + + if header_type == _C_SYS_HEADER: + if self._section <= self._C_SECTION: + self._section = self._C_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _CPP_SYS_HEADER: + if self._section <= self._CPP_SECTION: + self._section = self._CPP_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _LIKELY_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + self._section = self._OTHER_H_SECTION + elif header_type == _POSSIBLE_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + # This will always be the fallback because we're not sure + # enough that the header is associated with this file. + self._section = self._OTHER_H_SECTION + else: + assert header_type == _OTHER_HEADER + self._section = self._OTHER_H_SECTION + + if last_section != self._section: + self._last_header = '' + + return '' + + +class _CppLintState(object): + """Maintains module-wide state..""" + + def __init__(self): + self.verbose_level = 1 # global setting. + self.error_count = 0 # global count of reported errors + # filters to apply when emitting error messages + self.filters = _DEFAULT_FILTERS[:] + self.counting = 'total' # In what way are we counting errors? + self.errors_by_category = {} # string to int dict storing error counts + + # output format: + # "emacs" - format that emacs can parse (default) + # "vs7" - format that Microsoft Visual Studio 7 can parse + self.output_format = 'emacs' + + def SetOutputFormat(self, output_format): + """Sets the output format for errors.""" + self.output_format = output_format + + def SetVerboseLevel(self, level): + """Sets the module's verbosity, and returns the previous setting.""" + last_verbose_level = self.verbose_level + self.verbose_level = level + return last_verbose_level + + def SetCountingStyle(self, counting_style): + """Sets the module's counting options.""" + self.counting = counting_style + + def SetFilters(self, filters): + """Sets the error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "+whitespace/indent"). + Each filter should start with + or -; else we die. + + Raises: + ValueError: The comma-separated filters did not all start with '+' or '-'. + E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" + """ + # Default filters always have less priority than the flag ones. + self.filters = _DEFAULT_FILTERS[:] + for filt in filters.split(','): + clean_filt = filt.strip() + if clean_filt: + self.filters.append(clean_filt) + for filt in self.filters: + if not (filt.startswith('+') or filt.startswith('-')): + raise ValueError('Every filter in --filters must start with + or -' + ' (%s does not)' % filt) + + def ResetErrorCounts(self): + """Sets the module's error statistic back to zero.""" + self.error_count = 0 + self.errors_by_category = {} + + def IncrementErrorCount(self, category): + """Bumps the module's error statistic.""" + self.error_count += 1 + if self.counting in ('toplevel', 'detailed'): + if self.counting != 'detailed': + category = category.split('/')[0] + if category not in self.errors_by_category: + self.errors_by_category[category] = 0 + self.errors_by_category[category] += 1 + + def PrintErrorCounts(self): + """Print a summary of errors by category, and the total.""" + for category, count in self.errors_by_category.iteritems(): + sys.stderr.write('Category \'%s\' errors found: %d\n' % + (category, count)) + sys.stderr.write('Total errors found: %d\n' % self.error_count) + +_cpplint_state = _CppLintState() + + +def _OutputFormat(): + """Gets the module's output format.""" + return _cpplint_state.output_format + + +def _SetOutputFormat(output_format): + """Sets the module's output format.""" + _cpplint_state.SetOutputFormat(output_format) + + +def _VerboseLevel(): + """Returns the module's verbosity setting.""" + return _cpplint_state.verbose_level + + +def _SetVerboseLevel(level): + """Sets the module's verbosity, and returns the previous setting.""" + return _cpplint_state.SetVerboseLevel(level) + + +def _SetCountingStyle(level): + """Sets the module's counting options.""" + _cpplint_state.SetCountingStyle(level) + + +def _Filters(): + """Returns the module's list of output filters, as a list.""" + return _cpplint_state.filters + + +def _SetFilters(filters): + """Sets the module's error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.SetFilters(filters) + + +class _FunctionState(object): + """Tracks current function name and the number of lines in its body.""" + + _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. + _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. + + def __init__(self): + self.in_a_function = False + self.lines_in_function = 0 + self.current_function = '' + + def Begin(self, function_name): + """Start analyzing function body. + + Args: + function_name: The name of the function being tracked. + """ + self.in_a_function = True + self.lines_in_function = 0 + self.current_function = function_name + + def Count(self): + """Count line in current function body.""" + if self.in_a_function: + self.lines_in_function += 1 + + def Check(self, error, filename, linenum): + """Report if too many lines in function body. + + Args: + error: The function to call with any errors found. + filename: The name of the current file. + linenum: The number of the line to check. + """ + if Match(r'T(EST|est)', self.current_function): + base_trigger = self._TEST_TRIGGER + else: + base_trigger = self._NORMAL_TRIGGER + trigger = base_trigger * 2**_VerboseLevel() + + if self.lines_in_function > trigger: + error_level = int(math.log(self.lines_in_function / base_trigger, 2)) + # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... + if error_level > 5: + error_level = 5 + error(filename, linenum, 'readability/fn_size', error_level, + 'Small and focused functions are preferred:' + ' %s has %d non-comment lines' + ' (error triggered by exceeding %d lines).' % ( + self.current_function, self.lines_in_function, trigger)) + + def End(self): + """Stop analyzing function body.""" + self.in_a_function = False + + +class _IncludeError(Exception): + """Indicates a problem with the include order in a file.""" + pass + + +class FileInfo: + """Provides utility functions for filenames. + + FileInfo provides easy access to the components of a file's path + relative to the project root. + """ + + def __init__(self, filename): + self._filename = filename + + def FullName(self): + """Make Windows paths like Unix.""" + return os.path.abspath(self._filename).replace('\\', '/') + + def RepositoryName(self): + """FullName after removing the local path to the repository. + + If we have a real absolute path name here we can try to do something smart: + detecting the root of the checkout and truncating /path/to/checkout from + the name so that we get header guards that don't include things like + "C:\Documents and Settings\..." or "/home/username/..." in them and thus + people on different computers who have checked the source out to different + locations won't see bogus errors. + """ + fullname = self.FullName() + + if os.path.exists(fullname): + project_dir = os.path.dirname(fullname) + + if os.path.exists(os.path.join(project_dir, ".svn")): + # If there's a .svn file in the current directory, we recursively look + # up the directory tree for the top of the SVN checkout + root_dir = project_dir + one_up_dir = os.path.dirname(root_dir) + while os.path.exists(os.path.join(one_up_dir, ".svn")): + root_dir = os.path.dirname(root_dir) + one_up_dir = os.path.dirname(one_up_dir) + + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by + # searching up from the current path. + root_dir = os.path.dirname(fullname) + while (root_dir != os.path.dirname(root_dir) and + not os.path.exists(os.path.join(root_dir, ".git")) and + not os.path.exists(os.path.join(root_dir, ".hg")) and + not os.path.exists(os.path.join(root_dir, ".svn"))): + root_dir = os.path.dirname(root_dir) + + if (os.path.exists(os.path.join(root_dir, ".git")) or + os.path.exists(os.path.join(root_dir, ".hg")) or + os.path.exists(os.path.join(root_dir, ".svn"))): + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Don't know what to do; header guard warnings may be wrong... + return fullname + + def Split(self): + """Splits the file into the directory, basename, and extension. + + For 'chrome/browser/browser.cc', Split() would + return ('chrome/browser', 'browser', '.cc') + + Returns: + A tuple of (directory, basename, extension). + """ + + googlename = self.RepositoryName() + project, rest = os.path.split(googlename) + return (project,) + os.path.splitext(rest) + + def BaseName(self): + """File base name - text after the final slash, before the final period.""" + return self.Split()[1] + + def Extension(self): + """File extension - text following the final period.""" + return self.Split()[2] + + def NoExtension(self): + """File has no source file extension.""" + return '/'.join(self.Split()[0:2]) + + def IsSource(self): + """File has a source file extension.""" + return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') + + +def _ShouldPrintError(category, confidence, linenum): + """If confidence >= verbose, category passes filter and is not suppressed.""" + + # There are three ways we might decide not to print an error message: + # a "NOLINT(category)" comment appears in the source, + # the verbosity level isn't high enough, or the filters filter it out. + if IsErrorSuppressedByNolint(category, linenum): + return False + if confidence < _cpplint_state.verbose_level: + return False + + is_filtered = False + for one_filter in _Filters(): + if one_filter.startswith('-'): + if category.startswith(one_filter[1:]): + is_filtered = True + elif one_filter.startswith('+'): + if category.startswith(one_filter[1:]): + is_filtered = False + else: + assert False # should have been checked for in SetFilter. + if is_filtered: + return False + + return True + + +def Error(filename, linenum, category, confidence, message): + """Logs the fact we've found a lint error. + + We log where the error was found, and also our confidence in the error, + that is, how certain we are this is a legitimate style regression, and + not a misidentification or a use that's sometimes justified. + + False positives can be suppressed by the use of + "cpplint(category)" comments on the offending line. These are + parsed into _error_suppressions. + + Args: + filename: The name of the file containing the error. + linenum: The number of the line containing the error. + category: A string used to describe the "category" this bug + falls under: "whitespace", say, or "runtime". Categories + may have a hierarchy separated by slashes: "whitespace/indent". + confidence: A number from 1-5 representing a confidence score for + the error, with 5 meaning that we are certain of the problem, + and 1 meaning that it could be a legitimate construct. + message: The error message. + """ + if _ShouldPrintError(category, confidence, linenum): + _cpplint_state.IncrementErrorCount(category) + if _cpplint_state.output_format == 'vs7': + sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + elif _cpplint_state.output_format == 'eclipse': + sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + else: + sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + + +# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. +_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( + r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') +# Matches strings. Escape codes should already be removed by ESCAPES. +_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') +# Matches characters. Escape codes should already be removed by ESCAPES. +_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") +# Matches multi-line C++ comments. +# This RE is a little bit more complicated than one might expect, because we +# have to take care of space removals tools so we can handle comments inside +# statements better. +# The current rule is: We only clear spaces from both sides when we're at the +# end of the line. Otherwise, we try to remove spaces from the right side, +# if this doesn't work we try on left side but only if there's a non-character +# on the right. +_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( + r"""(\s*/\*.*\*/\s*$| + /\*.*\*/\s+| + \s+/\*.*\*/(?=\W)| + /\*.*\*/)""", re.VERBOSE) + + +def IsCppString(line): + """Does line terminate so, that the next symbol is in string constant. + + This function does not consider single-line nor multi-line comments. + + Args: + line: is a partial line of code starting from the 0..n. + + Returns: + True, if next character appended to 'line' is inside a + string constant. + """ + + line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" + return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 + + +def FindNextMultiLineCommentStart(lines, lineix): + """Find the beginning marker for a multiline comment.""" + while lineix < len(lines): + if lines[lineix].strip().startswith('/*'): + # Only return this marker if the comment goes beyond this line + if lines[lineix].strip().find('*/', 2) < 0: + return lineix + lineix += 1 + return len(lines) + + +def FindNextMultiLineCommentEnd(lines, lineix): + """We are inside a comment, find the end marker.""" + while lineix < len(lines): + if lines[lineix].strip().endswith('*/'): + return lineix + lineix += 1 + return len(lines) + + +def RemoveMultiLineCommentsFromRange(lines, begin, end): + """Clears a range of lines for multi-line comments.""" + # Having // dummy comments makes the lines non-empty, so we will not get + # unnecessary blank line warnings later in the code. + for i in range(begin, end): + lines[i] = '// dummy' + + +def RemoveMultiLineComments(filename, lines, error): + """Removes multiline (c-style) comments from lines.""" + lineix = 0 + while lineix < len(lines): + lineix_begin = FindNextMultiLineCommentStart(lines, lineix) + if lineix_begin >= len(lines): + return + lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) + if lineix_end >= len(lines): + error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, + 'Could not find end of multi-line comment') + return + RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) + lineix = lineix_end + 1 + + +def CleanseComments(line): + """Removes //-comments and single-line C-style /* */ comments. + + Args: + line: A line of C++ source. + + Returns: + The line with single-line comments removed. + """ + commentpos = line.find('//') + if commentpos != -1 and not IsCppString(line[:commentpos]): + line = line[:commentpos].rstrip() + # get rid of /* ... */ + return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) + + +class CleansedLines(object): + """Holds 3 copies of all lines with different preprocessing applied to them. + + 1) elided member contains lines without strings and comments, + 2) lines member contains lines without comments, and + 3) raw_lines member contains all the lines without processing. + All these three members are of <type 'list'>, and of the same length. + """ + + def __init__(self, lines): + self.elided = [] + self.lines = [] + self.raw_lines = lines + self.num_lines = len(lines) + for linenum in range(len(lines)): + self.lines.append(CleanseComments(lines[linenum])) + elided = self._CollapseStrings(lines[linenum]) + self.elided.append(CleanseComments(elided)) + + def NumLines(self): + """Returns the number of lines represented.""" + return self.num_lines + + @staticmethod + def _CollapseStrings(elided): + """Collapses strings and chars on a line to simple "" or '' blocks. + + We nix strings first so we're not fooled by text like '"http://"' + + Args: + elided: The line being processed. + + Returns: + The line with collapsed strings. + """ + if not _RE_PATTERN_INCLUDE.match(elided): + # Remove escaped characters first to make quote/single quote collapsing + # basic. Things that look like escaped characters shouldn't occur + # outside of strings and chars. + elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) + elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) + elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) + return elided + + +def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar): + """Find the position just after the matching endchar. + + Args: + line: a CleansedLines line. + startpos: start searching at this position. + depth: nesting level at startpos. + startchar: expression opening character. + endchar: expression closing character. + + Returns: + Index just after endchar. + """ + for i in xrange(startpos, len(line)): + if line[i] == startchar: + depth += 1 + elif line[i] == endchar: + depth -= 1 + if depth == 0: + return i + 1 + return -1 + + +def CloseExpression(clean_lines, linenum, pos): + """If input points to ( or { or [, finds the position that closes it. + + If lines[linenum][pos] points to a '(' or '{' or '[', finds the + linenum/pos that correspond to the closing of the expression. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *past* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + + line = clean_lines.elided[linenum] + startchar = line[pos] + if startchar not in '({[': + return (line, clean_lines.NumLines(), -1) + if startchar == '(': endchar = ')' + if startchar == '[': endchar = ']' + if startchar == '{': endchar = '}' + + # Check first line + end_pos = FindEndOfExpressionInLine(line, pos, 0, startchar, endchar) + if end_pos > -1: + return (line, linenum, end_pos) + tail = line[pos:] + num_open = tail.count(startchar) - tail.count(endchar) + while linenum < clean_lines.NumLines() - 1: + linenum += 1 + line = clean_lines.elided[linenum] + delta = line.count(startchar) - line.count(endchar) + if num_open + delta <= 0: + return (line, linenum, + FindEndOfExpressionInLine(line, 0, num_open, startchar, endchar)) + num_open += delta + + # Did not find endchar before end of file, give up + return (line, clean_lines.NumLines(), -1) + +def CheckForCopyright(filename, lines, error): + """Logs an error if no Copyright message appears at the top of the file.""" + + # We'll say it should occur by line 10. Don't forget there's a + # dummy line at the front. + for line in xrange(1, min(len(lines), 11)): + if re.search(r'Copyright', lines[line], re.I): break + else: # means no copyright line was found + error(filename, 0, 'legal/copyright', 5, + 'No copyright message found. ' + 'You should have a line: "Copyright [year] <Copyright Owner>"') + + +def GetHeaderGuardCPPVariable(filename): + """Returns the CPP variable that should be used as a header guard. + + Args: + filename: The name of a C++ header file. + + Returns: + The CPP variable that should be used as a header guard in the + named file. + + """ + + # Restores original filename in case that cpplint is invoked from Emacs's + # flymake. + filename = re.sub(r'_flymake\.h$', '.h', filename) + filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) + + fileinfo = FileInfo(filename) + file_path_from_root = fileinfo.RepositoryName() + if _root: + file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) + return re.sub(r'[-./\s]', '_', file_path_from_root).upper() + '_' + + +def CheckForHeaderGuard(filename, lines, error): + """Checks that the file contains a header guard. + + Logs an error if no #ifndef header guard is present. For other + headers, checks that the full pathname is used. + + Args: + filename: The name of the C++ header file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + cppvar = GetHeaderGuardCPPVariable(filename) + + ifndef = None + ifndef_linenum = 0 + define = None + endif = None + endif_linenum = 0 + for linenum, line in enumerate(lines): + linesplit = line.split() + if len(linesplit) >= 2: + # find the first occurrence of #ifndef and #define, save arg + if not ifndef and linesplit[0] == '#ifndef': + # set ifndef to the header guard presented on the #ifndef line. + ifndef = linesplit[1] + ifndef_linenum = linenum + if not define and linesplit[0] == '#define': + define = linesplit[1] + # find the last occurrence of #endif, save entire line + if line.startswith('#endif'): + endif = line + endif_linenum = linenum + + if not ifndef: + error(filename, 0, 'build/header_guard', 5, + 'No #ifndef header guard found, suggested CPP variable is: %s' % + cppvar) + return + + if not define: + error(filename, 0, 'build/header_guard', 5, + 'No #define header guard found, suggested CPP variable is: %s' % + cppvar) + return + + # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ + # for backward compatibility. + if ifndef != cppvar: + error_level = 0 + if ifndef != cppvar + '_': + error_level = 5 + + ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, + error) + error(filename, ifndef_linenum, 'build/header_guard', error_level, + '#ifndef header guard has wrong style, please use: %s' % cppvar) + + if define != ifndef: + error(filename, 0, 'build/header_guard', 5, + '#ifndef and #define don\'t match, suggested CPP variable is: %s' % + cppvar) + return + + if endif != ('#endif // %s' % cppvar): + error_level = 0 + if endif != ('#endif // %s' % (cppvar + '_')): + error_level = 5 + + ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, + error) + error(filename, endif_linenum, 'build/header_guard', error_level, + '#endif line should be "#endif // %s"' % cppvar) + + +def CheckForUnicodeReplacementCharacters(filename, lines, error): + """Logs an error for each line containing Unicode replacement characters. + + These indicate that either the file contained invalid UTF-8 (likely) + or Unicode replacement characters (which it shouldn't). Note that + it's possible for this to throw off line numbering if the invalid + UTF-8 occurred adjacent to a newline. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + for linenum, line in enumerate(lines): + if u'\ufffd' in line: + error(filename, linenum, 'readability/utf8', 5, + 'Line contains invalid UTF-8 (or Unicode replacement character).') + + +def CheckForNewlineAtEOF(filename, lines, error): + """Logs an error if there is no newline char at the end of the file. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + # The array lines() was created by adding two newlines to the + # original file (go figure), then splitting on \n. + # To verify that the file ends in \n, we just have to make sure the + # last-but-two element of lines() exists and is empty. + if len(lines) < 3 or lines[-2]: + error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, + 'Could not find a newline character at the end of the file.') + + +def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): + """Logs an error if we see /* ... */ or "..." that extend past one line. + + /* ... */ comments are legit inside macros, for one line. + Otherwise, we prefer // comments, so it's ok to warn about the + other. Likewise, it's ok for strings to extend across multiple + lines, as long as a line continuation character (backslash) + terminates each line. Although not currently prohibited by the C++ + style guide, it's ugly and unnecessary. We don't do well with either + in this lint program, so we warn about both. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remove all \\ (escaped backslashes) from the line. They are OK, and the + # second (escaped) slash may trigger later \" detection erroneously. + line = line.replace('\\\\', '') + + if line.count('/*') > line.count('*/'): + error(filename, linenum, 'readability/multiline_comment', 5, + 'Complex multi-line /*...*/-style comment found. ' + 'Lint may give bogus warnings. ' + 'Consider replacing these with //-style comments, ' + 'with #if 0...#endif, ' + 'or with more clearly structured multi-line comments.') + + if (line.count('"') - line.count('\\"')) % 2: + error(filename, linenum, 'readability/multiline_string', 5, + 'Multi-line string ("...") found. This lint script doesn\'t ' + 'do well with such strings, and may give bogus warnings. They\'re ' + 'ugly and unnecessary, and you should use concatenation instead".') + + +threading_list = ( + ('asctime(', 'asctime_r('), + ('ctime(', 'ctime_r('), + ('getgrgid(', 'getgrgid_r('), + ('getgrnam(', 'getgrnam_r('), + ('getlogin(', 'getlogin_r('), + ('getpwnam(', 'getpwnam_r('), + ('getpwuid(', 'getpwuid_r('), + ('gmtime(', 'gmtime_r('), + ('localtime(', 'localtime_r('), + ('rand(', 'rand_r('), + ('readdir(', 'readdir_r('), + ('strtok(', 'strtok_r('), + ('ttyname(', 'ttyname_r('), + ) + + +def CheckPosixThreading(filename, clean_lines, linenum, error): + """Checks for calls to thread-unsafe functions. + + Much code has been originally written without consideration of + multi-threading. Also, engineers are relying on their old experience; + they have learned posix before threading extensions were added. These + tests guide the engineers to use thread-safe functions (when using + posix directly). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + for single_thread_function, multithread_safe_function in threading_list: + ix = line.find(single_thread_function) + # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 + if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and + line[ix - 1] not in ('_', '.', '>'))): + error(filename, linenum, 'runtime/threadsafe_fn', 2, + 'Consider using ' + multithread_safe_function + + '...) instead of ' + single_thread_function + + '...) for improved thread safety.') + + +# Matches invalid increment: *count++, which moves pointer instead of +# incrementing a value. +_RE_PATTERN_INVALID_INCREMENT = re.compile( + r'^\s*\*\w+(\+\+|--);') + + +def CheckInvalidIncrement(filename, clean_lines, linenum, error): + """Checks for invalid increment *count++. + + For example following function: + void increment_counter(int* count) { + *count++; + } + is invalid, because it effectively does count++, moving pointer, and should + be replaced with ++*count, (*count)++ or *count += 1. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if _RE_PATTERN_INVALID_INCREMENT.match(line): + error(filename, linenum, 'runtime/invalid_increment', 5, + 'Changing pointer instead of value (or unused value of operator*).') + + +class _BlockInfo(object): + """Stores information about a generic block of code.""" + + def __init__(self, seen_open_brace): + self.seen_open_brace = seen_open_brace + self.open_parentheses = 0 + self.inline_asm = _NO_ASM + + def CheckBegin(self, filename, clean_lines, linenum, error): + """Run checks that applies to text up to the opening brace. + + This is mostly for checking the text after the class identifier + and the "{", usually where the base class is specified. For other + blocks, there isn't much to check, so we always pass. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + pass + + def CheckEnd(self, filename, clean_lines, linenum, error): + """Run checks that applies to text after the closing brace. + + This is mostly used for checking end of namespace comments. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + pass + + +class _ClassInfo(_BlockInfo): + """Stores information about a class.""" + + def __init__(self, name, class_or_struct, clean_lines, linenum): + _BlockInfo.__init__(self, False) + self.name = name + self.starting_linenum = linenum + self.is_derived = False + if class_or_struct == 'struct': + self.access = 'public' + else: + self.access = 'private' + + # Try to find the end of the class. This will be confused by things like: + # class A { + # } *x = { ... + # + # But it's still good enough for CheckSectionSpacing. + self.last_line = 0 + depth = 0 + for i in range(linenum, clean_lines.NumLines()): + line = clean_lines.elided[i] + depth += line.count('{') - line.count('}') + if not depth: + self.last_line = i + break + + def CheckBegin(self, filename, clean_lines, linenum, error): + # Look for a bare ':' + if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): + self.is_derived = True + + +class _NamespaceInfo(_BlockInfo): + """Stores information about a namespace.""" + + def __init__(self, name, linenum): + _BlockInfo.__init__(self, False) + self.name = name or '' + self.starting_linenum = linenum + + def CheckEnd(self, filename, clean_lines, linenum, error): + """Check end of namespace comments.""" + line = clean_lines.raw_lines[linenum] + + # Check how many lines is enclosed in this namespace. Don't issue + # warning for missing namespace comments if there aren't enough + # lines. However, do apply checks if there is already an end of + # namespace comment and it's incorrect. + # + # TODO(unknown): We always want to check end of namespace comments + # if a namespace is large, but sometimes we also want to apply the + # check if a short namespace contained nontrivial things (something + # other than forward declarations). There is currently no logic on + # deciding what these nontrivial things are, so this check is + # triggered by namespace size only, which works most of the time. + if (linenum - self.starting_linenum < 10 + and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)): + return + + # Look for matching comment at end of namespace. + # + # Note that we accept C style "/* */" comments for terminating + # namespaces, so that code that terminate namespaces inside + # preprocessor macros can be cpplint clean. Example: http://go/nxpiz + # + # We also accept stuff like "// end of namespace <name>." with the + # period at the end. + # + # Besides these, we don't accept anything else, otherwise we might + # get false negatives when existing comment is a substring of the + # expected namespace. Example: http://go/ldkdc, http://cl/23548205 + if self.name: + # Named namespace + if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) + + r'[\*/\.\\\s]*$'), + line): + error(filename, linenum, 'readability/namespace', 5, + 'Namespace should be terminated with "// namespace %s"' % + self.name) + else: + # Anonymous namespace + if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): + error(filename, linenum, 'readability/namespace', 5, + 'Namespace should be terminated with "// namespace"') + + +class _PreprocessorInfo(object): + """Stores checkpoints of nesting stacks when #if/#else is seen.""" + + def __init__(self, stack_before_if): + # The entire nesting stack before #if + self.stack_before_if = stack_before_if + + # The entire nesting stack up to #else + self.stack_before_else = [] + + # Whether we have already seen #else or #elif + self.seen_else = False + + +class _NestingState(object): + """Holds states related to parsing braces.""" + + def __init__(self): + # Stack for tracking all braces. An object is pushed whenever we + # see a "{", and popped when we see a "}". Only 3 types of + # objects are possible: + # - _ClassInfo: a class or struct. + # - _NamespaceInfo: a namespace. + # - _BlockInfo: some other type of block. + self.stack = [] + + # Stack of _PreprocessorInfo objects. + self.pp_stack = [] + + def SeenOpenBrace(self): + """Check if we have seen the opening brace for the innermost block. + + Returns: + True if we have seen the opening brace, False if the innermost + block is still expecting an opening brace. + """ + return (not self.stack) or self.stack[-1].seen_open_brace + + def InNamespaceBody(self): + """Check if we are currently one level inside a namespace body. + + Returns: + True if top of the stack is a namespace block, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _NamespaceInfo) + + def UpdatePreprocessor(self, line): + """Update preprocessor stack. + + We need to handle preprocessors due to classes like this: + #ifdef SWIG + struct ResultDetailsPageElementExtensionPoint { + #else + struct ResultDetailsPageElementExtensionPoint : public Extension { + #endif + (see http://go/qwddn for original example) + + We make the following assumptions (good enough for most files): + - Preprocessor condition evaluates to true from #if up to first + #else/#elif/#endif. + + - Preprocessor condition evaluates to false from #else/#elif up + to #endif. We still perform lint checks on these lines, but + these do not affect nesting stack. + + Args: + line: current line to check. + """ + if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): + # Beginning of #if block, save the nesting stack here. The saved + # stack will allow us to restore the parsing state in the #else case. + self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) + elif Match(r'^\s*#\s*(else|elif)\b', line): + # Beginning of #else block + if self.pp_stack: + if not self.pp_stack[-1].seen_else: + # This is the first #else or #elif block. Remember the + # whole nesting stack up to this point. This is what we + # keep after the #endif. + self.pp_stack[-1].seen_else = True + self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) + + # Restore the stack to how it was before the #if + self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) + else: + # TODO(unknown): unexpected #else, issue warning? + pass + elif Match(r'^\s*#\s*endif\b', line): + # End of #if or #else blocks. + if self.pp_stack: + # If we saw an #else, we will need to restore the nesting + # stack to its former state before the #else, otherwise we + # will just continue from where we left off. + if self.pp_stack[-1].seen_else: + # Here we can just use a shallow copy since we are the last + # reference to it. + self.stack = self.pp_stack[-1].stack_before_else + # Drop the corresponding #if + self.pp_stack.pop() + else: + # TODO(unknown): unexpected #endif, issue warning? + pass + + def Update(self, filename, clean_lines, linenum, error): + """Update nesting state with current line. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Update pp_stack first + self.UpdatePreprocessor(line) + + # Count parentheses. This is to avoid adding struct arguments to + # the nesting stack. + if self.stack: + inner_block = self.stack[-1] + depth_change = line.count('(') - line.count(')') + inner_block.open_parentheses += depth_change + + # Also check if we are starting or ending an inline assembly block. + if inner_block.inline_asm in (_NO_ASM, _END_ASM): + if (depth_change != 0 and + inner_block.open_parentheses == 1 and + _MATCH_ASM.match(line)): + # Enter assembly block + inner_block.inline_asm = _INSIDE_ASM + else: + # Not entering assembly block. If previous line was _END_ASM, + # we will now shift to _NO_ASM state. + inner_block.inline_asm = _NO_ASM + elif (inner_block.inline_asm == _INSIDE_ASM and + inner_block.open_parentheses == 0): + # Exit assembly block + inner_block.inline_asm = _END_ASM + + # Consume namespace declaration at the beginning of the line. Do + # this in a loop so that we catch same line declarations like this: + # namespace proto2 { namespace bridge { class MessageSet; } } + while True: + # Match start of namespace. The "\b\s*" below catches namespace + # declarations even if it weren't followed by a whitespace, this + # is so that we don't confuse our namespace checker. The + # missing spaces will be flagged by CheckSpacing. + namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) + if not namespace_decl_match: + break + + new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) + self.stack.append(new_namespace) + + line = namespace_decl_match.group(2) + if line.find('{') != -1: + new_namespace.seen_open_brace = True + line = line[line.find('{') + 1:] + + # Look for a class declaration in whatever is left of the line + # after parsing namespaces. The regexp accounts for decorated classes + # such as in: + # class LOCKABLE API Object { + # }; + # + # Templates with class arguments may confuse the parser, for example: + # template <class T + # class Comparator = less<T>, + # class Vector = vector<T> > + # class HeapQueue { + # + # Because this parser has no nesting state about templates, by the + # time it saw "class Comparator", it may think that it's a new class. + # Nested templates have a similar problem: + # template < + # typename ExportedType, + # typename TupleType, + # template <typename, typename> class ImplTemplate> + # + # To avoid these cases, we ignore classes that are followed by '=' or '>' + class_decl_match = Match( + r'\s*(template\s*<[\w\s<>,:]*>\s*)?' + '(class|struct)\s+([A-Z_]+\s+)*(\w+(?:::\w+)*)' + '(([^=>]|<[^<>]*>)*)$', line) + if (class_decl_match and + (not self.stack or self.stack[-1].open_parentheses == 0)): + self.stack.append(_ClassInfo( + class_decl_match.group(4), class_decl_match.group(2), + clean_lines, linenum)) + line = class_decl_match.group(5) + + # If we have not yet seen the opening brace for the innermost block, + # run checks here. + if not self.SeenOpenBrace(): + self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) + + # Update access control if we are inside a class/struct + if self.stack and isinstance(self.stack[-1], _ClassInfo): + access_match = Match(r'\s*(public|private|protected)\s*:', line) + if access_match: + self.stack[-1].access = access_match.group(1) + + # Consume braces or semicolons from what's left of the line + while True: + # Match first brace, semicolon, or closed parenthesis. + matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) + if not matched: + break + + token = matched.group(1) + if token == '{': + # If namespace or class hasn't seen a opening brace yet, mark + # namespace/class head as complete. Push a new block onto the + # stack otherwise. + if not self.SeenOpenBrace(): + self.stack[-1].seen_open_brace = True + else: + self.stack.append(_BlockInfo(True)) + if _MATCH_ASM.match(line): + self.stack[-1].inline_asm = _BLOCK_ASM + elif token == ';' or token == ')': + # If we haven't seen an opening brace yet, but we already saw + # a semicolon, this is probably a forward declaration. Pop + # the stack for these. + # + # Similarly, if we haven't seen an opening brace yet, but we + # already saw a closing parenthesis, then these are probably + # function arguments with extra "class" or "struct" keywords. + # Also pop these stack for these. + if not self.SeenOpenBrace(): + self.stack.pop() + else: # token == '}' + # Perform end of block checks and pop the stack. + if self.stack: + self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) + self.stack.pop() + line = matched.group(2) + + def InnermostClass(self): + """Get class info on the top of the stack. + + Returns: + A _ClassInfo object if we are inside a class, or None otherwise. + """ + for i in range(len(self.stack), 0, -1): + classinfo = self.stack[i - 1] + if isinstance(classinfo, _ClassInfo): + return classinfo + return None + + def CheckClassFinished(self, filename, error): + """Checks that all classes have been completely parsed. + + Call this when all lines in a file have been processed. + Args: + filename: The name of the current file. + error: The function to call with any errors found. + """ + # Note: This test can result in false positives if #ifdef constructs + # get in the way of brace matching. See the testBuildClass test in + # cpplint_unittest.py for an example of this. + for obj in self.stack: + if isinstance(obj, _ClassInfo): + error(filename, obj.starting_linenum, 'build/class', 5, + 'Failed to find complete declaration of class %s' % + obj.name) + + +def CheckForNonStandardConstructs(filename, clean_lines, linenum, + nesting_state, error): + """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. + + Complain about several constructs which gcc-2 accepts, but which are + not standard C++. Warning about these in lint is one way to ease the + transition to new compilers. + - put storage class first (e.g. "static const" instead of "const static"). + - "%lld" instead of %qd" in printf-type functions. + - "%1$d" is non-standard in printf-type functions. + - "\%" is an undefined character escape sequence. + - text after #endif is not allowed. + - invalid inner-style forward declaration. + - >? and <? operators, and their >?= and <?= cousins. + + Additionally, check for constructor/destructor style violations and reference + members, as it is very convenient to do so while checking for + gcc-2 compliance. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A _NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + """ + + # Remove comments from the line, but leave in strings for now. + line = clean_lines.lines[linenum] + + if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line): + error(filename, linenum, 'runtime/printf_format', 3, + '%q in format strings is deprecated. Use %ll instead.') + + if Search(r'printf\s*\(.*".*%\d+\$', line): + error(filename, linenum, 'runtime/printf_format', 2, + '%N$ formats are unconventional. Try rewriting to avoid them.') + + # Remove escaped backslashes before looking for undefined escapes. + line = line.replace('\\\\', '') + + if Search(r'("|\').*\\(%|\[|\(|{)', line): + error(filename, linenum, 'build/printf_format', 3, + '%, [, (, and { are undefined character escapes. Unescape them.') + + # For the rest, work with both comments and strings removed. + line = clean_lines.elided[linenum] + + if Search(r'\b(const|volatile|void|char|short|int|long' + r'|float|double|signed|unsigned' + r'|schar|u?int8|u?int16|u?int32|u?int64)' + r'\s+(register|static|extern|typedef)\b', + line): + error(filename, linenum, 'build/storage_class', 5, + 'Storage class (static, extern, typedef, etc) should be first.') + + if Match(r'\s*#\s*endif\s*[^/\s]+', line): + error(filename, linenum, 'build/endif_comment', 5, + 'Uncommented text after #endif is non-standard. Use a comment.') + + if Match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line): + error(filename, linenum, 'build/forward_decl', 5, + 'Inner-style forward declarations are invalid. Remove this line.') + + if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', + line): + error(filename, linenum, 'build/deprecated', 3, + '>? and <? (max and min) operators are non-standard and deprecated.') + + if Search(r'^\s*const\s*string\s*&\s*\w+\s*;', line): + # TODO(unknown): Could it be expanded safely to arbitrary references, + # without triggering too many false positives? The first + # attempt triggered 5 warnings for mostly benign code in the regtest, hence + # the restriction. + # Here's the original regexp, for the reference: + # type_name = r'\w+((\s*::\s*\w+)|(\s*<\s*\w+?\s*>))?' + # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' + error(filename, linenum, 'runtime/member_string_references', 2, + 'const string& members are dangerous. It is much better to use ' + 'alternatives, such as pointers or simple constants.') + + # Everything else in this function operates on class declarations. + # Return early if the top of the nesting stack is not a class, or if + # the class head is not completed yet. + classinfo = nesting_state.InnermostClass() + if not classinfo or not classinfo.seen_open_brace: + return + + # The class may have been declared with namespace or classname qualifiers. + # The constructor and destructor will not have those qualifiers. + base_classname = classinfo.name.split('::')[-1] + + # Look for single-argument constructors that aren't marked explicit. + # Technically a valid construct, but against style. + args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)' + % re.escape(base_classname), + line) + if (args and + args.group(1) != 'void' and + not Match(r'(const\s+)?%s\s*(?:<\w+>\s*)?&' % re.escape(base_classname), + args.group(1).strip())): + error(filename, linenum, 'runtime/explicit', 5, + 'Single-argument constructors should be marked explicit.') + + +def CheckSpacingForFunctionCall(filename, line, linenum, error): + """Checks for the correctness of various spacing around function calls. + + Args: + filename: The name of the current file. + line: The text of the line to check. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Since function calls often occur inside if/for/while/switch + # expressions - which have their own, more liberal conventions - we + # first see if we should be looking inside such an expression for a + # function call, to which we can apply more strict standards. + fncall = line # if there's no control flow construct, look at whole line + for pattern in (r'\bif\s*\((.*)\)\s*{', + r'\bfor\s*\((.*)\)\s*{', + r'\bwhile\s*\((.*)\)\s*[{;]', + r'\bswitch\s*\((.*)\)\s*{'): + match = Search(pattern, line) + if match: + fncall = match.group(1) # look inside the parens for function calls + break + + # Except in if/for/while/switch, there should never be space + # immediately inside parens (eg "f( 3, 4 )"). We make an exception + # for nested parens ( (a+b) + c ). Likewise, there should never be + # a space before a ( when it's a function argument. I assume it's a + # function argument when the char before the whitespace is legal in + # a function name (alnum + _) and we're not starting a macro. Also ignore + # pointers and references to arrays and functions coz they're too tricky: + # we use a very simple way to recognize these: + # " (something)(maybe-something)" or + # " (something)(maybe-something," or + # " (something)[something]" + # Note that we assume the contents of [] to be short enough that + # they'll never need to wrap. + if ( # Ignore control structures. + not Search(r'\b(if|for|while|switch|return|delete)\b', fncall) and + # Ignore pointers/references to functions. + not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and + # Ignore pointers/references to arrays. + not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): + if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space after ( in function call') + elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space after (') + if (Search(r'\w\s+\(', fncall) and + not Search(r'#\s*define|typedef', fncall) and + not Search(r'\w\s+\((\w+::)?\*\w+\)\(', fncall)): + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space before ( in function call') + # If the ) is followed only by a newline or a { + newline, assume it's + # part of a control statement (if/while/etc), and don't complain + if Search(r'[^)]\s+\)\s*[^{\s]', fncall): + # If the closing parenthesis is preceded by only whitespaces, + # try to give a more descriptive error message. + if Search(r'^\s+\)', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Closing ) should be moved to the previous line') + else: + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space before )') + + +def IsBlankLine(line): + """Returns true if the given line is blank. + + We consider a line to be blank if the line is empty or consists of + only white spaces. + + Args: + line: A line of a string. + + Returns: + True, if the given line is blank. + """ + return not line or line.isspace() + + +def CheckForFunctionLengths(filename, clean_lines, linenum, + function_state, error): + """Reports for long function bodies. + + For an overview why this is done, see: + http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions + + Uses a simplistic algorithm assuming other style guidelines + (especially spacing) are followed. + Only checks unindented functions, so class members are unchecked. + Trivial bodies are unchecked, so constructors with huge initializer lists + may be missed. + Blank/comment lines are not counted so as to avoid encouraging the removal + of vertical space and comments just to get through a lint check. + NOLINT *on the last line of a function* disables this check. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + function_state: Current function name and lines in body so far. + error: The function to call with any errors found. + """ + lines = clean_lines.lines + line = lines[linenum] + raw = clean_lines.raw_lines + raw_line = raw[linenum] + joined_line = '' + + starting_func = False + regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... + match_result = Match(regexp, line) + if match_result: + # If the name is all caps and underscores, figure it's a macro and + # ignore it, unless it's TEST or TEST_F. + function_name = match_result.group(1).split()[-1] + if function_name == 'TEST' or function_name == 'TEST_F' or ( + not Match(r'[A-Z_]+$', function_name)): + starting_func = True + + if starting_func: + body_found = False + for start_linenum in xrange(linenum, clean_lines.NumLines()): + start_line = lines[start_linenum] + joined_line += ' ' + start_line.lstrip() + if Search(r'(;|})', start_line): # Declarations and trivial functions + body_found = True + break # ... ignore + elif Search(r'{', start_line): + body_found = True + function = Search(r'((\w|:)*)\(', line).group(1) + if Match(r'TEST', function): # Handle TEST... macros + parameter_regexp = Search(r'(\(.*\))', joined_line) + if parameter_regexp: # Ignore bad syntax + function += parameter_regexp.group(1) + else: + function += '()' + function_state.Begin(function) + break + if not body_found: + # No body for the function (or evidence of a non-function) was found. + error(filename, linenum, 'readability/fn_size', 5, + 'Lint failed to find start of function body.') + elif Match(r'^\}\s*$', line): # function end + function_state.Check(error, filename, linenum) + function_state.End() + elif not Match(r'^\s*$', line): + function_state.Count() # Count non-blank/non-comment lines. + + +_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') + + +def CheckComment(comment, filename, linenum, error): + """Checks for common mistakes in TODO comments. + + Args: + comment: The text of the comment from the line in question. + filename: The name of the current file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + match = _RE_PATTERN_TODO.match(comment) + if match: + # One whitespace is correct; zero whitespace is handled elsewhere. + leading_whitespace = match.group(1) + if len(leading_whitespace) > 1: + error(filename, linenum, 'whitespace/todo', 2, + 'Too many spaces before TODO') + + username = match.group(2) + if not username: + error(filename, linenum, 'readability/todo', 2, + 'Missing username in TODO; it should look like ' + '"// TODO(my_username): Stuff."') + + middle_whitespace = match.group(3) + # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 + if middle_whitespace != ' ' and middle_whitespace != '': + error(filename, linenum, 'whitespace/todo', 2, + 'TODO(my_username) should be followed by a space') + +def CheckAccess(filename, clean_lines, linenum, nesting_state, error): + """Checks for improper use of DISALLOW* macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A _NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] # get rid of comments and strings + + matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' + r'DISALLOW_EVIL_CONSTRUCTORS|' + r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) + if not matched: + return + if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): + if nesting_state.stack[-1].access != 'private': + error(filename, linenum, 'readability/constructors', 3, + '%s must be in the private: section' % matched.group(1)) + + else: + # Found DISALLOW* macro outside a class declaration, or perhaps it + # was used inside a function when it should have been part of the + # class declaration. We could issue a warning here, but it + # probably resulted in a compiler error already. + pass + + +def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix): + """Find the corresponding > to close a template. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: Current line number. + init_suffix: Remainder of the current line after the initial <. + + Returns: + True if a matching bracket exists. + """ + line = init_suffix + nesting_stack = ['<'] + while True: + # Find the next operator that can tell us whether < is used as an + # opening bracket or as a less-than operator. We only want to + # warn on the latter case. + # + # We could also check all other operators and terminate the search + # early, e.g. if we got something like this "a<b+c", the "<" is + # most likely a less-than operator, but then we will get false + # positives for default arguments (e.g. http://go/prccd) and + # other template expressions (e.g. http://go/oxcjq). + match = Search(r'^[^<>(),;\[\]]*([<>(),;\[\]])(.*)$', line) + if match: + # Found an operator, update nesting stack + operator = match.group(1) + line = match.group(2) + + if nesting_stack[-1] == '<': + # Expecting closing angle bracket + if operator in ('<', '(', '['): + nesting_stack.append(operator) + elif operator == '>': + nesting_stack.pop() + if not nesting_stack: + # Found matching angle bracket + return True + elif operator == ',': + # Got a comma after a bracket, this is most likely a template + # argument. We have not seen a closing angle bracket yet, but + # it's probably a few lines later if we look for it, so just + # return early here. + return True + else: + # Got some other operator. + return False + + else: + # Expecting closing parenthesis or closing bracket + if operator in ('<', '(', '['): + nesting_stack.append(operator) + elif operator in (')', ']'): + # We don't bother checking for matching () or []. If we got + # something like (] or [), it would have been a syntax error. + nesting_stack.pop() + + else: + # Scan the next line + linenum += 1 + if linenum >= len(clean_lines.elided): + break + line = clean_lines.elided[linenum] + + # Exhausted all remaining lines and still no matching angle bracket. + # Most likely the input was incomplete, otherwise we should have + # seen a semicolon and returned early. + return True + + +def FindPreviousMatchingAngleBracket(clean_lines, linenum, init_prefix): + """Find the corresponding < that started a template. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: Current line number. + init_prefix: Part of the current line before the initial >. + + Returns: + True if a matching bracket exists. + """ + line = init_prefix + nesting_stack = ['>'] + while True: + # Find the previous operator + match = Search(r'^(.*)([<>(),;\[\]])[^<>(),;\[\]]*$', line) + if match: + # Found an operator, update nesting stack + operator = match.group(2) + line = match.group(1) + + if nesting_stack[-1] == '>': + # Expecting opening angle bracket + if operator in ('>', ')', ']'): + nesting_stack.append(operator) + elif operator == '<': + nesting_stack.pop() + if not nesting_stack: + # Found matching angle bracket + return True + elif operator == ',': + # Got a comma before a bracket, this is most likely a + # template argument. The opening angle bracket is probably + # there if we look for it, so just return early here. + return True + else: + # Got some other operator. + return False + + else: + # Expecting opening parenthesis or opening bracket + if operator in ('>', ')', ']'): + nesting_stack.append(operator) + elif operator in ('(', '['): + nesting_stack.pop() + + else: + # Scan the previous line + linenum -= 1 + if linenum < 0: + break + line = clean_lines.elided[linenum] + + # Exhausted all earlier lines and still no matching angle bracket. + return False + + +def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): + """Checks for the correctness of various spacing issues in the code. + + Things we check for: spaces around operators, spaces after + if/for/while/switch, no spaces around parens in function calls, two + spaces between code and comment, don't start a block with a blank + line, don't end a function with a blank line, don't add a blank line + after public/protected/private, don't have too many blank lines in a row. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A _NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + + raw = clean_lines.raw_lines + line = raw[linenum] + + # Before nixing comments, check if the line is blank for no good + # reason. This includes the first line after a block is opened, and + # blank lines at the end of a function (ie, right before a line like '}' + # + # Skip all the blank line checks if we are immediately inside a + # namespace body. In other words, don't issue blank line warnings + # for this block: + # namespace { + # + # } + # + # A warning about missing end of namespace comments will be issued instead. + if IsBlankLine(line) and not nesting_state.InNamespaceBody(): + elided = clean_lines.elided + prev_line = elided[linenum - 1] + prevbrace = prev_line.rfind('{') + # TODO(unknown): Don't complain if line before blank line, and line after, + # both start with alnums and are indented the same amount. + # This ignores whitespace at the start of a namespace block + # because those are not usually indented. + if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: + # OK, we have a blank line at the start of a code block. Before we + # complain, we check if it is an exception to the rule: The previous + # non-empty line has the parameters of a function header that are indented + # 4 spaces (because they did not fit in a 80 column line when placed on + # the same line as the function name). We also check for the case where + # the previous line is indented 6 spaces, which may happen when the + # initializers of a constructor do not fit into a 80 column line. + exception = False + if Match(r' {6}\w', prev_line): # Initializer list? + # We are looking for the opening column of initializer list, which + # should be indented 4 spaces to cause 6 space indentation afterwards. + search_position = linenum-2 + while (search_position >= 0 + and Match(r' {6}\w', elided[search_position])): + search_position -= 1 + exception = (search_position >= 0 + and elided[search_position][:5] == ' :') + else: + # Search for the function arguments or an initializer list. We use a + # simple heuristic here: If the line is indented 4 spaces; and we have a + # closing paren, without the opening paren, followed by an opening brace + # or colon (for initializer lists) we assume that it is the last line of + # a function header. If we have a colon indented 4 spaces, it is an + # initializer list. + exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', + prev_line) + or Match(r' {4}:', prev_line)) + + if not exception: + error(filename, linenum, 'whitespace/blank_line', 2, + 'Blank line at the start of a code block. Is this needed?') + # Ignore blank lines at the end of a block in a long if-else + # chain, like this: + # if (condition1) { + # // Something followed by a blank line + # + # } else if (condition2) { + # // Something else + # } + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + if (next_line + and Match(r'\s*}', next_line) + and next_line.find('} else ') == -1): + error(filename, linenum, 'whitespace/blank_line', 3, + 'Blank line at the end of a code block. Is this needed?') + + matched = Match(r'\s*(public|protected|private):', prev_line) + if matched: + error(filename, linenum, 'whitespace/blank_line', 3, + 'Do not leave a blank line after "%s:"' % matched.group(1)) + + # Next, we complain if there's a comment too near the text + commentpos = line.find('//') + if commentpos != -1: + # Check if the // may be in quotes. If so, ignore it + # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 + if (line.count('"', 0, commentpos) - + line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes + # Allow one space for new scopes, two spaces otherwise: + if (not Match(r'^\s*{ //', line) and + ((commentpos >= 1 and + line[commentpos-1] not in string.whitespace) or + (commentpos >= 2 and + line[commentpos-2] not in string.whitespace))): + error(filename, linenum, 'whitespace/comments', 2, + 'At least two spaces is best between code and comments') + # There should always be a space between the // and the comment + commentend = commentpos + 2 + if commentend < len(line) and not line[commentend] == ' ': + # but some lines are exceptions -- e.g. if they're big + # comment delimiters like: + # //---------------------------------------------------------- + # or are an empty C++ style Doxygen comment, like: + # /// + # or they begin with multiple slashes followed by a space: + # //////// Header comment + match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or + Search(r'^/$', line[commentend:]) or + Search(r'^/+ ', line[commentend:])) + if not match: + error(filename, linenum, 'whitespace/comments', 4, + 'Should have a space between // and comment') + CheckComment(line[commentpos:], filename, linenum, error) + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Don't try to do spacing checks for operator methods + line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) + + # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". + # Otherwise not. Note we only check for non-spaces on *both* sides; + # sometimes people put non-spaces on one side when aligning ='s among + # many lines (not that this is behavior that I approve of...) + if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): + error(filename, linenum, 'whitespace/operators', 4, + 'Missing spaces around =') + + # It's ok not to have spaces around binary operators like + - * /, but if + # there's too little whitespace, we get concerned. It's hard to tell, + # though, so we punt on this one for now. TODO. + + # You should always have whitespace around binary operators. + # + # Check <= and >= first to avoid false positives with < and >, then + # check non-include lines for spacing around < and >. + match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around %s' % match.group(1)) + # We allow no-spaces around << when used like this: 10<<20, but + # not otherwise (particularly, not when used as streams) + match = Search(r'(\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)', line) + if match and not (match.group(1).isdigit() and match.group(2).isdigit()): + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around <<') + elif not Match(r'#.*include', line): + # Avoid false positives on -> + reduced_line = line.replace('->', '') + + # Look for < that is not surrounded by spaces. This is only + # triggered if both sides are missing spaces, even though + # technically should should flag if at least one side is missing a + # space. This is done to avoid some false positives with shifts. + match = Search(r'[^\s<]<([^\s=<].*)', reduced_line) + if (match and + not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))): + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around <') + + # Look for > that is not surrounded by spaces. Similar to the + # above, we only trigger if both sides are missing spaces to avoid + # false positives with shifts. + match = Search(r'^(.*[^\s>])>[^\s=>]', reduced_line) + if (match and + not FindPreviousMatchingAngleBracket(clean_lines, linenum, + match.group(1))): + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around >') + + # We allow no-spaces around >> for almost anything. This is because + # C++11 allows ">>" to close nested templates, which accounts for + # most cases when ">>" is not followed by a space. + # + # We still warn on ">>" followed by alpha character, because that is + # likely due to ">>" being used for right shifts, e.g.: + # value >> alpha + # + # When ">>" is used to close templates, the alphanumeric letter that + # follows would be part of an identifier, and there should still be + # a space separating the template type and the identifier. + # type<type<type>> alpha + match = Search(r'>>[a-zA-Z_]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around >>') + + # There shouldn't be space around unary operators + match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) + if match: + error(filename, linenum, 'whitespace/operators', 4, + 'Extra space for operator %s' % match.group(1)) + + # A pet peeve of mine: no spaces after an if, while, switch, or for + match = Search(r' (if\(|for\(|while\(|switch\()', line) + if match: + error(filename, linenum, 'whitespace/parens', 5, + 'Missing space before ( in %s' % match.group(1)) + + # For if/for/while/switch, the left and right parens should be + # consistent about how many spaces are inside the parens, and + # there should either be zero or one spaces inside the parens. + # We don't want: "if ( foo)" or "if ( foo )". + # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. + match = Search(r'\b(if|for|while|switch)\s*' + r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', + line) + if match: + if len(match.group(2)) != len(match.group(4)): + if not (match.group(3) == ';' and + len(match.group(2)) == 1 + len(match.group(4)) or + not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): + error(filename, linenum, 'whitespace/parens', 5, + 'Mismatching spaces inside () in %s' % match.group(1)) + if not len(match.group(2)) in [0, 1]: + error(filename, linenum, 'whitespace/parens', 5, + 'Should have zero or one spaces inside ( and ) in %s' % + match.group(1)) + + # You should always have a space after a comma (either as fn arg or operator) + if Search(r',[^\s]', line): + error(filename, linenum, 'whitespace/comma', 3, + 'Missing space after ,') + + # You should always have a space after a semicolon + # except for few corner cases + # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more + # space after ; + if Search(r';[^\s};\\)/]', line): + error(filename, linenum, 'whitespace/semicolon', 3, + 'Missing space after ;') + + # Next we will look for issues with function calls. + CheckSpacingForFunctionCall(filename, line, linenum, error) + + # Except after an opening paren, or after another opening brace (in case of + # an initializer list, for instance), you should have spaces before your + # braces. And since you should never have braces at the beginning of a line, + # this is an easy test. + if Search(r'[^ ({]{', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before {') + + # Make sure '} else {' has spaces. + if Search(r'}else', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before else') + + # You shouldn't have spaces before your brackets, except maybe after + # 'delete []' or 'new char * []'. + if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Extra space before [') + + # You shouldn't have a space before a semicolon at the end of the line. + # There's a special case for "for" since the style guide allows space before + # the semicolon there. + if Search(r':\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Semicolon defining empty statement. Use {} instead.') + elif Search(r'^\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Line contains only semicolon. If this should be an empty statement, ' + 'use {} instead.') + elif (Search(r'\s+;\s*$', line) and + not Search(r'\bfor\b', line)): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Extra space before last semicolon. If this should be an empty ' + 'statement, use {} instead.') + + # In range-based for, we wanted spaces before and after the colon, but + # not around "::" tokens that might appear. + if (Search('for *\(.*[^:]:[^: ]', line) or + Search('for *\(.*[^: ]:[^:]', line)): + error(filename, linenum, 'whitespace/forcolon', 2, + 'Missing space around colon in range-based for loop') + + +def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): + """Checks for additional blank line issues related to sections. + + Currently the only thing checked here is blank line before protected/private. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + class_info: A _ClassInfo objects. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Skip checks if the class is small, where small means 25 lines or less. + # 25 lines seems like a good cutoff since that's the usual height of + # terminals, and any class that can't fit in one screen can't really + # be considered "small". + # + # Also skip checks if we are on the first line. This accounts for + # classes that look like + # class Foo { public: ... }; + # + # If we didn't find the end of the class, last_line would be zero, + # and the check will be skipped by the first condition. + if (class_info.last_line - class_info.starting_linenum <= 24 or + linenum <= class_info.starting_linenum): + return + + matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) + if matched: + # Issue warning if the line before public/protected/private was + # not a blank line, but don't do this if the previous line contains + # "class" or "struct". This can happen two ways: + # - We are at the beginning of the class. + # - We are forward-declaring an inner class that is semantically + # private, but needed to be public for implementation reasons. + # Also ignores cases where the previous line ends with a backslash as can be + # common when defining classes in C macros. + prev_line = clean_lines.lines[linenum - 1] + if (not IsBlankLine(prev_line) and + not Search(r'\b(class|struct)\b', prev_line) and + not Search(r'\\$', prev_line)): + # Try a bit harder to find the beginning of the class. This is to + # account for multi-line base-specifier lists, e.g.: + # class Derived + # : public Base { + end_class_head = class_info.starting_linenum + for i in range(class_info.starting_linenum, linenum): + if Search(r'\{\s*$', clean_lines.lines[i]): + end_class_head = i + break + if end_class_head < linenum - 1: + error(filename, linenum, 'whitespace/blank_line', 3, + '"%s:" should be preceded by a blank line' % matched.group(1)) + + +def GetPreviousNonBlankLine(clean_lines, linenum): + """Return the most recent non-blank line and its line number. + + Args: + clean_lines: A CleansedLines instance containing the file contents. + linenum: The number of the line to check. + + Returns: + A tuple with two elements. The first element is the contents of the last + non-blank line before the current line, or the empty string if this is the + first non-blank line. The second is the line number of that line, or -1 + if this is the first non-blank line. + """ + + prevlinenum = linenum - 1 + while prevlinenum >= 0: + prevline = clean_lines.elided[prevlinenum] + if not IsBlankLine(prevline): # if not a blank line... + return (prevline, prevlinenum) + prevlinenum -= 1 + return ('', -1) + + +def CheckBraces(filename, clean_lines, linenum, error): + """Looks for misplaced braces (e.g. at the end of line). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] # get rid of comments and strings + + if Match(r'\s*{\s*$', line): + # We allow an open brace to start a line in the case where someone + # is using braces in a block to explicitly create a new scope, + # which is commonly used to control the lifetime of + # stack-allocated variables. We don't detect this perfectly: we + # just don't complain if the last non-whitespace character on the + # previous non-blank line is ';', ':', '{', or '}', or if the previous + # line starts a preprocessor block. + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if (not Search(r'[;:}{]\s*$', prevline) and + not Match(r'\s*#', prevline)): + error(filename, linenum, 'whitespace/braces', 4, + '{ should almost always be at the end of the previous line') + + # An else clause should be on the same line as the preceding closing brace. + if Match(r'\s*else\s*', line): + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if Match(r'\s*}\s*$', prevline): + error(filename, linenum, 'whitespace/newline', 4, + 'An else should appear on the same line as the preceding }') + + # If braces come on one side of an else, they should be on both. + # However, we have to worry about "else if" that spans multiple lines! + if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): + if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if + # find the ( after the if + pos = line.find('else if') + pos = line.find('(', pos) + if pos > 0: + (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) + if endline[endpos:].find('{') == -1: # must be brace after if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + else: # common case: else not followed by a multi-line if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + + # Likewise, an else should never have the else clause on the same line + if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): + error(filename, linenum, 'whitespace/newline', 4, + 'Else clause should never be on same line as else (use 2 lines)') + + # In the same way, a do/while should never be on one line + if Match(r'\s*do [^\s{]', line): + error(filename, linenum, 'whitespace/newline', 4, + 'do/while clauses should not be on a single line') + + # Braces shouldn't be followed by a ; unless they're defining a struct + # or initializing an array. + # We can't tell in general, but we can for some common cases. + prevlinenum = linenum + while True: + (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) + if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): + line = prevline + line + else: + break + if (Search(r'{.*}\s*;', line) and + line.count('{') == line.count('}') and + not Search(r'struct|class|enum|\s*=\s*{', line)): + error(filename, linenum, 'readability/braces', 4, + "You don't need a ; after a }") + + +def CheckEmptyLoopBody(filename, clean_lines, linenum, error): + """Loop for empty loop body with only a single semicolon. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Search for loop keywords at the beginning of the line. Because only + # whitespaces are allowed before the keywords, this will also ignore most + # do-while-loops, since those lines should start with closing brace. + line = clean_lines.elided[linenum] + if Match(r'\s*(for|while)\s*\(', line): + # Find the end of the conditional expression + (end_line, end_linenum, end_pos) = CloseExpression( + clean_lines, linenum, line.find('(')) + + # Output warning if what follows the condition expression is a semicolon. + # No warning for all other cases, including whitespace or newline, since we + # have a separate check for semicolons preceded by whitespace. + if end_pos >= 0 and Match(r';', end_line[end_pos:]): + error(filename, end_linenum, 'whitespace/empty_loop_body', 5, + 'Empty loop bodies should use {} or continue') + + +def ReplaceableCheck(operator, macro, line): + """Determine whether a basic CHECK can be replaced with a more specific one. + + For example suggest using CHECK_EQ instead of CHECK(a == b) and + similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. + + Args: + operator: The C++ operator used in the CHECK. + macro: The CHECK or EXPECT macro being called. + line: The current source line. + + Returns: + True if the CHECK can be replaced with a more specific one. + """ + + # This matches decimal and hex integers, strings, and chars (in that order). + match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' + + # Expression to match two sides of the operator with something that + # looks like a literal, since CHECK(x == iterator) won't compile. + # This means we can't catch all the cases where a more specific + # CHECK is possible, but it's less annoying than dealing with + # extraneous warnings. + match_this = (r'\s*' + macro + r'\((\s*' + + match_constant + r'\s*' + operator + r'[^<>].*|' + r'.*[^<>]' + operator + r'\s*' + match_constant + + r'\s*\))') + + # Don't complain about CHECK(x == NULL) or similar because + # CHECK_EQ(x, NULL) won't compile (requires a cast). + # Also, don't complain about more complex boolean expressions + # involving && or || such as CHECK(a == b || c == d). + return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) + + +def CheckCheck(filename, clean_lines, linenum, error): + """Checks the use of CHECK and EXPECT macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Decide the set of replacement macros that should be suggested + raw_lines = clean_lines.raw_lines + current_macro = '' + for macro in _CHECK_MACROS: + if raw_lines[linenum].find(macro) >= 0: + current_macro = macro + break + if not current_macro: + # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' + return + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. + for operator in ['==', '!=', '>=', '>', '<=', '<']: + if ReplaceableCheck(operator, current_macro, line): + error(filename, linenum, 'readability/check', 2, + 'Consider using %s instead of %s(a %s b)' % ( + _CHECK_REPLACEMENT[current_macro][operator], + current_macro, operator)) + break + + +def CheckAltTokens(filename, clean_lines, linenum, error): + """Check alternative keywords being used in boolean expressions. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Avoid preprocessor lines + if Match(r'^\s*#', line): + return + + # Last ditch effort to avoid multi-line comments. This will not help + # if the comment started before the current line or ended after the + # current line, but it catches most of the false positives. At least, + # it provides a way to workaround this warning for people who use + # multi-line comments in preprocessor macros. + # + # TODO(unknown): remove this once cpplint has better support for + # multi-line comments. + if line.find('/*') >= 0 or line.find('*/') >= 0: + return + + for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): + error(filename, linenum, 'readability/alt_tokens', 2, + 'Use operator %s instead of %s' % ( + _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) + + +def GetLineWidth(line): + """Determines the width of the line in column positions. + + Args: + line: A string, which may be a Unicode string. + + Returns: + The width of the line in column positions, accounting for Unicode + combining characters and wide characters. + """ + if isinstance(line, unicode): + width = 0 + for uc in unicodedata.normalize('NFC', line): + if unicodedata.east_asian_width(uc) in ('W', 'F'): + width += 2 + elif not unicodedata.combining(uc): + width += 1 + return width + else: + return len(line) + + +def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, + error): + """Checks rules from the 'C++ style rules' section of cppguide.html. + + Most of these rules are hard to test (naming, comment style), but we + do what we can. In particular we check for 2-space indents, line lengths, + tab usage, spaces inside code, etc. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + nesting_state: A _NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + + raw_lines = clean_lines.raw_lines + line = raw_lines[linenum] + + if line.find('\t') != -1: + error(filename, linenum, 'whitespace/tab', 1, + 'Tab found; better to use spaces') + + # One or three blank spaces at the beginning of the line is weird; it's + # hard to reconcile that with 2-space indents. + # NOTE: here are the conditions rob pike used for his tests. Mine aren't + # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces + # if(RLENGTH > 20) complain = 0; + # if(match($0, " +(error|private|public|protected):")) complain = 0; + # if(match(prev, "&& *$")) complain = 0; + # if(match(prev, "\\|\\| *$")) complain = 0; + # if(match(prev, "[\",=><] *$")) complain = 0; + # if(match($0, " <<")) complain = 0; + # if(match(prev, " +for \\(")) complain = 0; + # if(prevodd && match(prevprev, " +for \\(")) complain = 0; + initial_spaces = 0 + cleansed_line = clean_lines.elided[linenum] + while initial_spaces < len(line) and line[initial_spaces] == ' ': + initial_spaces += 1 + if line and line[-1].isspace(): + error(filename, linenum, 'whitespace/end_of_line', 4, + 'Line ends in whitespace. Consider deleting these extra spaces.') + # There are certain situations we allow one space, notably for labels + elif ((initial_spaces == 1 or initial_spaces == 3) and + not Match(r'\s*\w+\s*:\s*$', cleansed_line)): + error(filename, linenum, 'whitespace/indent', 3, + 'Weird number of spaces at line-start. ' + 'Are you using a 2-space indent?') + # Labels should always be indented at least one space. + elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$', + line): + error(filename, linenum, 'whitespace/labels', 4, + 'Labels should always be indented at least one space. ' + 'If this is a member-initializer list in a constructor or ' + 'the base class list in a class definition, the colon should ' + 'be on the following line.') + + + # Check if the line is a header guard. + is_header_guard = False + if file_extension == 'h': + cppvar = GetHeaderGuardCPPVariable(filename) + if (line.startswith('#ifndef %s' % cppvar) or + line.startswith('#define %s' % cppvar) or + line.startswith('#endif // %s' % cppvar)): + is_header_guard = True + # #include lines and header guards can be long, since there's no clean way to + # split them. + # + # URLs can be long too. It's possible to split these, but it makes them + # harder to cut&paste. + # + # The "$Id:...$" comment may also get very long without it being the + # developers fault. + if (not line.startswith('#include') and not is_header_guard and + not Match(r'^\s*//.*http(s?)://\S*$', line) and + not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): + line_width = GetLineWidth(line) + if line_width > 100: + error(filename, linenum, 'whitespace/line_length', 4, + 'Lines should very rarely be longer than 100 characters') + elif line_width > 80: + error(filename, linenum, 'whitespace/line_length', 2, + 'Lines should be <= 80 characters long') + + if (cleansed_line.count(';') > 1 and + # for loops are allowed two ;'s (and may run over two lines). + cleansed_line.find('for') == -1 and + (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or + GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and + # It's ok to have many commands in a switch case that fits in 1 line + not ((cleansed_line.find('case ') != -1 or + cleansed_line.find('default:') != -1) and + cleansed_line.find('break;') != -1)): + error(filename, linenum, 'whitespace/newline', 0, + 'More than one command on the same line') + + # Some more style checks + CheckBraces(filename, clean_lines, linenum, error) + CheckEmptyLoopBody(filename, clean_lines, linenum, error) + CheckAccess(filename, clean_lines, linenum, nesting_state, error) + CheckSpacing(filename, clean_lines, linenum, nesting_state, error) + CheckCheck(filename, clean_lines, linenum, error) + CheckAltTokens(filename, clean_lines, linenum, error) + classinfo = nesting_state.InnermostClass() + if classinfo: + CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) + + +_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') +_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') +# Matches the first component of a filename delimited by -s and _s. That is: +# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' +_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') + + +def _DropCommonSuffixes(filename): + """Drops common suffixes like _test.cc or -inl.h from filename. + + For example: + >>> _DropCommonSuffixes('foo/foo-inl.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/bar/foo.cc') + 'foo/bar/foo' + >>> _DropCommonSuffixes('foo/foo_internal.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') + 'foo/foo_unusualinternal' + + Args: + filename: The input filename. + + Returns: + The filename with the common suffix removed. + """ + for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', + 'inl.h', 'impl.h', 'internal.h'): + if (filename.endswith(suffix) and len(filename) > len(suffix) and + filename[-len(suffix) - 1] in ('-', '_')): + return filename[:-len(suffix) - 1] + return os.path.splitext(filename)[0] + + +def _IsTestFilename(filename): + """Determines if the given filename has a suffix that identifies it as a test. + + Args: + filename: The input filename. + + Returns: + True if 'filename' looks like a test, False otherwise. + """ + if (filename.endswith('_test.cc') or + filename.endswith('_unittest.cc') or + filename.endswith('_regtest.cc')): + return True + else: + return False + + +def _ClassifyInclude(fileinfo, include, is_system): + """Figures out what kind of header 'include' is. + + Args: + fileinfo: The current file cpplint is running over. A FileInfo instance. + include: The path to a #included file. + is_system: True if the #include used <> rather than "". + + Returns: + One of the _XXX_HEADER constants. + + For example: + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) + _C_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) + _CPP_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) + _LIKELY_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), + ... 'bar/foo_other_ext.h', False) + _POSSIBLE_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) + _OTHER_HEADER + """ + # This is a list of all standard c++ header files, except + # those already checked for above. + is_stl_h = include in _STL_HEADERS + is_cpp_h = is_stl_h or include in _CPP_HEADERS + + if is_system: + if is_cpp_h: + return _CPP_SYS_HEADER + else: + return _C_SYS_HEADER + + # If the target file and the include we're checking share a + # basename when we drop common extensions, and the include + # lives in . , then it's likely to be owned by the target file. + target_dir, target_base = ( + os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) + include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) + if target_base == include_base and ( + include_dir == target_dir or + include_dir == os.path.normpath(target_dir + '/../public')): + return _LIKELY_MY_HEADER + + # If the target and include share some initial basename + # component, it's possible the target is implementing the + # include, so it's allowed to be first, but we'll never + # complain if it's not there. + target_first_component = _RE_FIRST_COMPONENT.match(target_base) + include_first_component = _RE_FIRST_COMPONENT.match(include_base) + if (target_first_component and include_first_component and + target_first_component.group(0) == + include_first_component.group(0)): + return _POSSIBLE_MY_HEADER + + return _OTHER_HEADER + + + +def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): + """Check rules that are applicable to #include lines. + + Strings on #include lines are NOT removed from elided line, to make + certain tasks easier. However, to prevent false positives, checks + applicable to #include lines in CheckLanguage must be put here. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + fileinfo = FileInfo(filename) + + line = clean_lines.lines[linenum] + + # "include" should use the new style "foo/bar.h" instead of just "bar.h" + if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line): + error(filename, linenum, 'build/include', 4, + 'Include the directory when naming .h files') + + # we shouldn't include a file more than once. actually, there are a + # handful of instances where doing so is okay, but in general it's + # not. + match = _RE_PATTERN_INCLUDE.search(line) + if match: + include = match.group(2) + is_system = (match.group(1) == '<') + if include in include_state: + error(filename, linenum, 'build/include', 4, + '"%s" already included at %s:%s' % + (include, filename, include_state[include])) + else: + include_state[include] = linenum + + # We want to ensure that headers appear in the right order: + # 1) for foo.cc, foo.h (preferred location) + # 2) c system files + # 3) cpp system files + # 4) for foo.cc, foo.h (deprecated location) + # 5) other google headers + # + # We classify each include statement as one of those 5 types + # using a number of techniques. The include_state object keeps + # track of the highest type seen, and complains if we see a + # lower type after that. + error_message = include_state.CheckNextIncludeOrder( + _ClassifyInclude(fileinfo, include, is_system)) + if error_message: + error(filename, linenum, 'build/include_order', 4, + '%s. Should be: %s.h, c system, c++ system, other.' % + (error_message, fileinfo.BaseName())) + if not include_state.IsInAlphabeticalOrder(include): + error(filename, linenum, 'build/include_alpha', 4, + 'Include "%s" not in alphabetical order' % include) + + # Look for any of the stream classes that are part of standard C++. + match = _RE_PATTERN_INCLUDE.match(line) + if match: + include = match.group(2) + if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): + # Many unit tests use cout, so we exempt them. + if not _IsTestFilename(filename): + error(filename, linenum, 'readability/streams', 3, + 'Streams are highly discouraged.') + + +def _GetTextInside(text, start_pattern): + """Retrieves all the text between matching open and close parentheses. + + Given a string of lines and a regular expression string, retrieve all the text + following the expression and between opening punctuation symbols like + (, [, or {, and the matching close-punctuation symbol. This properly nested + occurrences of the punctuations, so for the text like + printf(a(), b(c())); + a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. + start_pattern must match string having an open punctuation symbol at the end. + + Args: + text: The lines to extract text. Its comments and strings must be elided. + It can be single line and can span multiple lines. + start_pattern: The regexp string indicating where to start extracting + the text. + Returns: + The extracted text. + None if either the opening string or ending punctuation could not be found. + """ + # TODO(sugawarayu): Audit cpplint.py to see what places could be profitably + # rewritten to use _GetTextInside (and use inferior regexp matching today). + + # Give opening punctuations to get the matching close-punctuations. + matching_punctuation = {'(': ')', '{': '}', '[': ']'} + closing_punctuation = set(matching_punctuation.itervalues()) + + # Find the position to start extracting text. + match = re.search(start_pattern, text, re.M) + if not match: # start_pattern not found in text. + return None + start_position = match.end(0) + + assert start_position > 0, ( + 'start_pattern must ends with an opening punctuation.') + assert text[start_position - 1] in matching_punctuation, ( + 'start_pattern must ends with an opening punctuation.') + # Stack of closing punctuations we expect to have in text after position. + punctuation_stack = [matching_punctuation[text[start_position - 1]]] + position = start_position + while punctuation_stack and position < len(text): + if text[position] == punctuation_stack[-1]: + punctuation_stack.pop() + elif text[position] in closing_punctuation: + # A closing punctuation without matching opening punctuations. + return None + elif text[position] in matching_punctuation: + punctuation_stack.append(matching_punctuation[text[position]]) + position += 1 + if punctuation_stack: + # Opening punctuations left without matching close-punctuations. + return None + # punctuations match. + return text[start_position:position - 1] + + +def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, + error): + """Checks rules from the 'C++ language rules' section of cppguide.html. + + Some of these rules are hard to test (function overloading, using + uint32 inappropriately), but we do the best we can. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + # If the line is empty or consists of entirely a comment, no need to + # check it. + line = clean_lines.elided[linenum] + if not line: + return + + match = _RE_PATTERN_INCLUDE.search(line) + if match: + CheckIncludeLine(filename, clean_lines, linenum, include_state, error) + return + + # Create an extended_line, which is the concatenation of the current and + # next lines, for more effective checking of code that may span more than one + # line. + if linenum + 1 < clean_lines.NumLines(): + extended_line = line + clean_lines.elided[linenum + 1] + else: + extended_line = line + + # Make Windows paths like Unix. + fullname = os.path.abspath(filename).replace('\\', '/') + + # TODO(unknown): figure out if they're using default arguments in fn proto. + + # Check for non-const references in functions. This is tricky because & + # is also used to take the address of something. We allow <> for templates, + # (ignoring whatever is between the braces) and : for classes. + # These are complicated re's. They try to capture the following: + # paren (for fn-prototype start), typename, &, varname. For the const + # version, we're willing for const to be before typename or after + # Don't check the implementation on same line. + fnline = line.split('{', 1)[0] + if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > + len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' + r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + + len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', + fnline))): + + # We allow non-const references in a few standard places, like functions + # called "swap()" or iostream operators like "<<" or ">>". We also filter + # out for loops, which lint otherwise mistakenly thinks are functions. + if not Search( + r'(for|swap|Swap|operator[<>][<>])\s*\(\s*' + r'(?:(?:typename\s*)?[\w:]|<.*>)+\s*&', + fnline): + error(filename, linenum, 'runtime/references', 2, + 'Is this a non-const reference? ' + 'If so, make const or use a pointer.') + + # Check to see if they're using an conversion function cast. + # I just try to capture the most common basic types, though there are more. + # Parameterless conversion functions, such as bool(), are allowed as they are + # probably a member operator declaration or default constructor. + match = Search( + r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there + r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) + if match: + # gMock methods are defined using some variant of MOCK_METHODx(name, type) + # where type may be float(), int(string), etc. Without context they are + # virtually indistinguishable from int(x) casts. Likewise, gMock's + # MockCallback takes a template parameter of the form return_type(arg_type), + # which looks much like the cast we're trying to detect. + if (match.group(1) is None and # If new operator, then this isn't a cast + not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or + Match(r'^\s*MockCallback<.*>', line))): + # Try a bit harder to catch gmock lines: the only place where + # something looks like an old-style cast is where we declare the + # return type of the mocked method, and the only time when we + # are missing context is if MOCK_METHOD was split across + # multiple lines (for example http://go/hrfhr ), so we only need + # to check the previous line for MOCK_METHOD. + if (linenum == 0 or + not Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(\S+,\s*$', + clean_lines.elided[linenum - 1])): + error(filename, linenum, 'readability/casting', 4, + 'Using deprecated casting style. ' + 'Use static_cast<%s>(...) instead' % + match.group(2)) + + CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'static_cast', + r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) + + # This doesn't catch all cases. Consider (const char * const)"hello". + # + # (char *) "foo" should always be a const_cast (reinterpret_cast won't + # compile). + if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error): + pass + else: + # Check pointer casts for other than string constants + CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) + + # In addition, we look for people taking the address of a cast. This + # is dangerous -- casts can assign to temporaries, so the pointer doesn't + # point where you think. + if Search( + r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): + error(filename, linenum, 'runtime/casting', 4, + ('Are you taking an address of a cast? ' + 'This is dangerous: could be a temp var. ' + 'Take the address before doing the cast, rather than after')) + + # Check for people declaring static/global STL strings at the top level. + # This is dangerous because the C++ language does not guarantee that + # globals with constructors are initialized before the first access. + match = Match( + r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', + line) + # Make sure it's not a function. + # Function template specialization looks like: "string foo<Type>(...". + # Class template definitions look like: "string Foo<Type>::Method(...". + if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', + match.group(3)): + error(filename, linenum, 'runtime/string', 4, + 'For a static/global string constant, use a C style string instead: ' + '"%schar %s[]".' % + (match.group(1), match.group(2))) + + # Check that we're not using RTTI outside of testing code. + if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): + error(filename, linenum, 'runtime/rtti', 5, + 'Do not use dynamic_cast<>. If you need to cast within a class ' + "hierarchy, use static_cast<> to upcast. Google doesn't support " + 'RTTI.') + + if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): + error(filename, linenum, 'runtime/init', 4, + 'You seem to be initializing a member variable with itself.') + + if file_extension == 'h': + # TODO(unknown): check that 1-arg constructors are explicit. + # How to tell it's a constructor? + # (handled in CheckForNonStandardConstructs for now) + # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS + # (level 1 error) + pass + + # Check if people are using the verboten C basic types. The only exception + # we regularly allow is "unsigned short port" for port. + if Search(r'\bshort port\b', line): + if not Search(r'\bunsigned short port\b', line): + error(filename, linenum, 'runtime/int', 4, + 'Use "unsigned short" for ports, not "short"') + else: + match = Search(r'\b(short|long(?! +double)|long long)\b', line) + if match: + error(filename, linenum, 'runtime/int', 4, + 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) + + # When snprintf is used, the second argument shouldn't be a literal. + match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) + if match and match.group(2) != '0': + # If 2nd arg is zero, snprintf is used to calculate size. + error(filename, linenum, 'runtime/printf', 3, + 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' + 'to snprintf.' % (match.group(1), match.group(2))) + + # Check if some verboten C functions are being used. + if Search(r'\bsprintf\b', line): + error(filename, linenum, 'runtime/printf', 5, + 'Never use sprintf. Use snprintf instead.') + match = Search(r'\b(strcpy|strcat)\b', line) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Almost always, snprintf is better than %s' % match.group(1)) + + if Search(r'\bsscanf\b', line): + error(filename, linenum, 'runtime/printf', 1, + 'sscanf can be ok, but is slow and can overflow buffers.') + + # Check if some verboten operator overloading is going on + # TODO(unknown): catch out-of-line unary operator&: + # class X {}; + # int operator&(const X& x) { return 42; } // unary operator& + # The trick is it's hard to tell apart from binary operator&: + # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& + if Search(r'\boperator\s*&\s*\(\s*\)', line): + error(filename, linenum, 'runtime/operator', 4, + 'Unary operator& is dangerous. Do not use it.') + + # Check for suspicious usage of "if" like + # } if (a == b) { + if Search(r'\}\s*if\s*\(', line): + error(filename, linenum, 'readability/braces', 4, + 'Did you mean "else if"? If not, start a new line for "if".') + + # Check for potential format string bugs like printf(foo). + # We constrain the pattern not to pick things like DocidForPrintf(foo). + # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) + # TODO(sugawarayu): Catch the following case. Need to change the calling + # convention of the whole function to process multiple line to handle it. + # printf( + # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); + printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') + if printf_args: + match = Match(r'([\w.\->()]+)$', printf_args) + if match and match.group(1) != '__VA_ARGS__': + function_name = re.search(r'\b((?:string)?printf)\s*\(', + line, re.I).group(1) + error(filename, linenum, 'runtime/printf', 4, + 'Potential format string bug. Do %s("%%s", %s) instead.' + % (function_name, match.group(1))) + + # Check for potential memset bugs like memset(buf, sizeof(buf), 0). + match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) + if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): + error(filename, linenum, 'runtime/memset', 4, + 'Did you mean "memset(%s, 0, %s)"?' + % (match.group(1), match.group(2))) + + if Search(r'\busing namespace\b', line): + error(filename, linenum, 'build/namespaces', 5, + 'Do not use namespace using-directives. ' + 'Use using-declarations instead.') + + # Detect variable-length arrays. + match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) + if (match and match.group(2) != 'return' and match.group(2) != 'delete' and + match.group(3).find(']') == -1): + # Split the size using space and arithmetic operators as delimiters. + # If any of the resulting tokens are not compile time constants then + # report the error. + tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) + is_const = True + skip_next = False + for tok in tokens: + if skip_next: + skip_next = False + continue + + if Search(r'sizeof\(.+\)', tok): continue + if Search(r'arraysize\(\w+\)', tok): continue + + tok = tok.lstrip('(') + tok = tok.rstrip(')') + if not tok: continue + if Match(r'\d+', tok): continue + if Match(r'0[xX][0-9a-fA-F]+', tok): continue + if Match(r'k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue + # A catch all for tricky sizeof cases, including 'sizeof expression', + # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' + # requires skipping the next token because we split on ' ' and '*'. + if tok.startswith('sizeof'): + skip_next = True + continue + is_const = False + break + if not is_const: + error(filename, linenum, 'runtime/arrays', 1, + 'Do not use variable-length arrays. Use an appropriately named ' + "('k' followed by CamelCase) compile-time constant for the size.") + + # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or + # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing + # in the class declaration. + match = Match( + (r'\s*' + r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' + r'\(.*\);$'), + line) + if match and linenum + 1 < clean_lines.NumLines(): + next_line = clean_lines.elided[linenum + 1] + # We allow some, but not all, declarations of variables to be present + # in the statement that defines the class. The [\w\*,\s]* fragment of + # the regular expression below allows users to declare instances of + # the class or pointers to instances, but not less common types such + # as function pointers or arrays. It's a tradeoff between allowing + # reasonable code and avoiding trying to parse more C++ using regexps. + if not Search(r'^\s*}[\w\*,\s]*;', next_line): + error(filename, linenum, 'readability/constructors', 3, + match.group(1) + ' should be the last thing in the class') + + # Check for use of unnamed namespaces in header files. Registration + # macros are typically OK, so we allow use of "namespace {" on lines + # that end with backslashes. + if (file_extension == 'h' + and Search(r'\bnamespace\s*{', line) + and line[-1] != '\\'): + error(filename, linenum, 'build/namespaces', 4, + 'Do not use unnamed namespaces in header files. See ' + 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' + ' for more information.') + + +def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, + error): + """Checks for a C-style cast by looking for the pattern. + + This also handles sizeof(type) warnings, due to similarity of content. + + Args: + filename: The name of the current file. + linenum: The number of the line to check. + line: The line of code to check. + raw_line: The raw line of code to check, with comments. + cast_type: The string for the C++ cast to recommend. This is either + reinterpret_cast, static_cast, or const_cast, depending. + pattern: The regular expression used to find C-style casts. + error: The function to call with any errors found. + + Returns: + True if an error was emitted. + False otherwise. + """ + match = Search(pattern, line) + if not match: + return False + + # e.g., sizeof(int) + sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) + if sizeof_match: + error(filename, linenum, 'runtime/sizeof', 1, + 'Using sizeof(type). Use sizeof(varname) instead if possible') + return True + + # operator++(int) and operator--(int) + if (line[0:match.start(1) - 1].endswith(' operator++') or + line[0:match.start(1) - 1].endswith(' operator--')): + return False + + remainder = line[match.end(0):] + + # The close paren is for function pointers as arguments to a function. + # eg, void foo(void (*bar)(int)); + # The semicolon check is a more basic function check; also possibly a + # function pointer typedef. + # eg, void foo(int); or void foo(int) const; + # The equals check is for function pointer assignment. + # eg, void *(*foo)(int) = ... + # The > is for MockCallback<...> ... + # + # Right now, this will only catch cases where there's a single argument, and + # it's unnamed. It should probably be expanded to check for multiple + # arguments with some unnamed. + function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)|>))', remainder) + if function_match: + if (not function_match.group(3) or + function_match.group(3) == ';' or + ('MockCallback<' not in raw_line and + '/*' not in raw_line)): + error(filename, linenum, 'readability/function', 3, + 'All parameters should be named in a function') + return True + + # At this point, all that should be left is actual casts. + error(filename, linenum, 'readability/casting', 4, + 'Using C-style cast. Use %s<%s>(...) instead' % + (cast_type, match.group(1))) + + return True + + +_HEADERS_CONTAINING_TEMPLATES = ( + ('<deque>', ('deque',)), + ('<functional>', ('unary_function', 'binary_function', + 'plus', 'minus', 'multiplies', 'divides', 'modulus', + 'negate', + 'equal_to', 'not_equal_to', 'greater', 'less', + 'greater_equal', 'less_equal', + 'logical_and', 'logical_or', 'logical_not', + 'unary_negate', 'not1', 'binary_negate', 'not2', + 'bind1st', 'bind2nd', + 'pointer_to_unary_function', + 'pointer_to_binary_function', + 'ptr_fun', + 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', + 'mem_fun_ref_t', + 'const_mem_fun_t', 'const_mem_fun1_t', + 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', + 'mem_fun_ref', + )), + ('<limits>', ('numeric_limits',)), + ('<list>', ('list',)), + ('<map>', ('map', 'multimap',)), + ('<memory>', ('allocator',)), + ('<queue>', ('queue', 'priority_queue',)), + ('<set>', ('set', 'multiset',)), + ('<stack>', ('stack',)), + ('<string>', ('char_traits', 'basic_string',)), + ('<utility>', ('pair',)), + ('<vector>', ('vector',)), + + # gcc extensions. + # Note: std::hash is their hash, ::hash is our hash + ('<hash_map>', ('hash_map', 'hash_multimap',)), + ('<hash_set>', ('hash_set', 'hash_multiset',)), + ('<slist>', ('slist',)), + ) + +_RE_PATTERN_STRING = re.compile(r'\bstring\b') + +_re_pattern_algorithm_header = [] +for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', + 'transform'): + # Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or + # type::max(). + _re_pattern_algorithm_header.append( + (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), + _template, + '<algorithm>')) + +_re_pattern_templates = [] +for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: + for _template in _templates: + _re_pattern_templates.append( + (re.compile(r'(\<|\b)' + _template + r'\s*\<'), + _template + '<>', + _header)) + + +def FilesBelongToSameModule(filename_cc, filename_h): + """Check if these two filenames belong to the same module. + + The concept of a 'module' here is a as follows: + foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the + same 'module' if they are in the same directory. + some/path/public/xyzzy and some/path/internal/xyzzy are also considered + to belong to the same module here. + + If the filename_cc contains a longer path than the filename_h, for example, + '/absolute/path/to/base/sysinfo.cc', and this file would include + 'base/sysinfo.h', this function also produces the prefix needed to open the + header. This is used by the caller of this function to more robustly open the + header file. We don't have access to the real include paths in this context, + so we need this guesswork here. + + Known bugs: tools/base/bar.cc and base/bar.h belong to the same module + according to this implementation. Because of this, this function gives + some false positives. This should be sufficiently rare in practice. + + Args: + filename_cc: is the path for the .cc file + filename_h: is the path for the header path + + Returns: + Tuple with a bool and a string: + bool: True if filename_cc and filename_h belong to the same module. + string: the additional prefix needed to open the header file. + """ + + if not filename_cc.endswith('.cc'): + return (False, '') + filename_cc = filename_cc[:-len('.cc')] + if filename_cc.endswith('_unittest'): + filename_cc = filename_cc[:-len('_unittest')] + elif filename_cc.endswith('_test'): + filename_cc = filename_cc[:-len('_test')] + filename_cc = filename_cc.replace('/public/', '/') + filename_cc = filename_cc.replace('/internal/', '/') + + if not filename_h.endswith('.h'): + return (False, '') + filename_h = filename_h[:-len('.h')] + if filename_h.endswith('-inl'): + filename_h = filename_h[:-len('-inl')] + filename_h = filename_h.replace('/public/', '/') + filename_h = filename_h.replace('/internal/', '/') + + files_belong_to_same_module = filename_cc.endswith(filename_h) + common_path = '' + if files_belong_to_same_module: + common_path = filename_cc[:-len(filename_h)] + return files_belong_to_same_module, common_path + + +def UpdateIncludeState(filename, include_state, io=codecs): + """Fill up the include_state with new includes found from the file. + + Args: + filename: the name of the header to read. + include_state: an _IncludeState instance in which the headers are inserted. + io: The io factory to use to read the file. Provided for testability. + + Returns: + True if a header was succesfully added. False otherwise. + """ + headerfile = None + try: + headerfile = io.open(filename, 'r', 'utf8', 'replace') + except IOError: + return False + linenum = 0 + for line in headerfile: + linenum += 1 + clean_line = CleanseComments(line) + match = _RE_PATTERN_INCLUDE.search(clean_line) + if match: + include = match.group(2) + # The value formatting is cute, but not really used right now. + # What matters here is that the key is in include_state. + include_state.setdefault(include, '%s:%d' % (filename, linenum)) + return True + + +def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, + io=codecs): + """Reports for missing stl includes. + + This function will output warnings to make sure you are including the headers + necessary for the stl containers and functions that you use. We only give one + reason to include a header. For example, if you use both equal_to<> and + less<> in a .h file, only one (the latter in the file) of these will be + reported as a reason to include the <functional>. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + include_state: An _IncludeState instance. + error: The function to call with any errors found. + io: The IO factory to use to read the header file. Provided for unittest + injection. + """ + required = {} # A map of header name to linenumber and the template entity. + # Example of required: { '<functional>': (1219, 'less<>') } + + for linenum in xrange(clean_lines.NumLines()): + line = clean_lines.elided[linenum] + if not line or line[0] == '#': + continue + + # String is special -- it is a non-templatized type in STL. + matched = _RE_PATTERN_STRING.search(line) + if matched: + # Don't warn about strings in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:matched.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required['<string>'] = (linenum, 'string') + + for pattern, template, header in _re_pattern_algorithm_header: + if pattern.search(line): + required[header] = (linenum, template) + + # The following function is just a speed up, no semantics are changed. + if not '<' in line: # Reduces the cpu time usage by skipping lines. + continue + + for pattern, template, header in _re_pattern_templates: + if pattern.search(line): + required[header] = (linenum, template) + + # The policy is that if you #include something in foo.h you don't need to + # include it again in foo.cc. Here, we will look at possible includes. + # Let's copy the include_state so it is only messed up within this function. + include_state = include_state.copy() + + # Did we find the header for this file (if any) and succesfully load it? + header_found = False + + # Use the absolute path so that matching works properly. + abs_filename = FileInfo(filename).FullName() + + # For Emacs's flymake. + # If cpplint is invoked from Emacs's flymake, a temporary file is generated + # by flymake and that file name might end with '_flymake.cc'. In that case, + # restore original file name here so that the corresponding header file can be + # found. + # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' + # instead of 'foo_flymake.h' + abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) + + # include_state is modified during iteration, so we iterate over a copy of + # the keys. + header_keys = include_state.keys() + for header in header_keys: + (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) + fullpath = common_path + header + if same_module and UpdateIncludeState(fullpath, include_state, io): + header_found = True + + # If we can't find the header file for a .cc, assume it's because we don't + # know where to look. In that case we'll give up as we're not sure they + # didn't include it in the .h file. + # TODO(unknown): Do a better job of finding .h files so we are confident that + # not having the .h file means there isn't one. + if filename.endswith('.cc') and not header_found: + return + + # All the lines have been processed, report the errors found. + for required_header_unstripped in required: + template = required[required_header_unstripped][1] + if required_header_unstripped.strip('<>"') not in include_state: + error(filename, required[required_header_unstripped][0], + 'build/include_what_you_use', 4, + 'Add #include ' + required_header_unstripped + ' for ' + template) + + +_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') + + +def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): + """Check that make_pair's template arguments are deduced. + + G++ 4.6 in C++0x mode fails badly if make_pair's template arguments are + specified explicitly, and such use isn't intended in any case. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + raw = clean_lines.raw_lines + line = raw[linenum] + match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) + if match: + error(filename, linenum, 'build/explicit_make_pair', + 4, # 4 = high confidence + 'For C++11-compatibility, omit template arguments from make_pair' + ' OR use pair directly OR if appropriate, construct a pair directly') + + +def ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions=[]): + """Processes a single line in the file. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + clean_lines: An array of strings, each representing a line of the file, + with comments stripped. + line: Number of line being processed. + include_state: An _IncludeState instance in which the headers are inserted. + function_state: A _FunctionState instance which counts function lines, etc. + nesting_state: A _NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + raw_lines = clean_lines.raw_lines + ParseNolintSuppressions(filename, raw_lines[line], line, error) + nesting_state.Update(filename, clean_lines, line, error) + if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM: + return + CheckForFunctionLengths(filename, clean_lines, line, function_state, error) + CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) + CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) + CheckLanguage(filename, clean_lines, line, file_extension, include_state, + error) + CheckForNonStandardConstructs(filename, clean_lines, line, + nesting_state, error) + CheckPosixThreading(filename, clean_lines, line, error) + CheckInvalidIncrement(filename, clean_lines, line, error) + CheckMakePairUsesDeduction(filename, clean_lines, line, error) + for check_fn in extra_check_functions: + check_fn(filename, clean_lines, line, error) + +def ProcessFileData(filename, file_extension, lines, error, + extra_check_functions=[]): + """Performs lint checks and reports any errors to the given error function. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + lines: An array of strings, each representing a line of the file, with the + last element being empty if the file is terminated with a newline. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + lines = (['// marker so line numbers and indices both start at 1'] + lines + + ['// marker so line numbers end in a known way']) + + include_state = _IncludeState() + function_state = _FunctionState() + nesting_state = _NestingState() + + ResetNolintSuppressions() + + CheckForCopyright(filename, lines, error) + + if file_extension == 'h': + CheckForHeaderGuard(filename, lines, error) + + RemoveMultiLineComments(filename, lines, error) + clean_lines = CleansedLines(lines) + for line in xrange(clean_lines.NumLines()): + ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions) + nesting_state.CheckClassFinished(filename, error) + + CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) + + # We check here rather than inside ProcessLine so that we see raw + # lines rather than "cleaned" lines. + CheckForUnicodeReplacementCharacters(filename, lines, error) + + CheckForNewlineAtEOF(filename, lines, error) + +def ProcessFile(filename, vlevel, extra_check_functions=[]): + """Does google-lint on a single file. + + Args: + filename: The name of the file to parse. + + vlevel: The level of errors to report. Every error of confidence + >= verbose_level will be reported. 0 is a good default. + + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + + _SetVerboseLevel(vlevel) + + try: + # Support the UNIX convention of using "-" for stdin. Note that + # we are not opening the file with universal newline support + # (which codecs doesn't support anyway), so the resulting lines do + # contain trailing '\r' characters if we are reading a file that + # has CRLF endings. + # If after the split a trailing '\r' is present, it is removed + # below. If it is not expected to be present (i.e. os.linesep != + # '\r\n' as in Windows), a warning is issued below if this file + # is processed. + + if filename == '-': + lines = codecs.StreamReaderWriter(sys.stdin, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace').read().split('\n') + else: + lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') + + carriage_return_found = False + # Remove trailing '\r'. + for linenum in range(len(lines)): + if lines[linenum].endswith('\r'): + lines[linenum] = lines[linenum].rstrip('\r') + carriage_return_found = True + + except IOError: + sys.stderr.write( + "Skipping input '%s': Can't open for reading\n" % filename) + return + + # Note, if no dot is found, this will give the entire filename as the ext. + file_extension = filename[filename.rfind('.') + 1:] + + # When reading from stdin, the extension is unknown, so no cpplint tests + # should rely on the extension. + if (filename != '-' and file_extension != 'cc' and file_extension != 'h' + and file_extension != 'cpp'): + sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename) + else: + ProcessFileData(filename, file_extension, lines, Error, + extra_check_functions) + if carriage_return_found and os.linesep != '\r\n': + # Use 0 for linenum since outputting only one error for potentially + # several lines. + Error(filename, 0, 'whitespace/newline', 1, + 'One or more unexpected \\r (^M) found;' + 'better to use only a \\n') + + sys.stderr.write('Done processing %s\n' % filename) + + +def PrintUsage(message): + """Prints a brief usage string and exits, optionally with an error message. + + Args: + message: The optional error message. + """ + sys.stderr.write(_USAGE) + if message: + sys.exit('\nFATAL ERROR: ' + message) + else: + sys.exit(1) + + +def PrintCategories(): + """Prints a list of all the error-categories used by error messages. + + These are the categories used to filter messages via --filter. + """ + sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) + sys.exit(0) + + +def ParseArguments(args): + """Parses the command line arguments. + + This may set the output format and verbosity level as side-effects. + + Args: + args: The command line arguments: + + Returns: + The list of filenames to lint. + """ + try: + (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', + 'counting=', + 'filter=', + 'root=']) + except getopt.GetoptError: + PrintUsage('Invalid arguments.') + + verbosity = _VerboseLevel() + output_format = _OutputFormat() + filters = '' + counting_style = '' + + for (opt, val) in opts: + if opt == '--help': + PrintUsage(None) + elif opt == '--output': + if not val in ('emacs', 'vs7', 'eclipse'): + PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') + output_format = val + elif opt == '--verbose': + verbosity = int(val) + elif opt == '--filter': + filters = val + if not filters: + PrintCategories() + elif opt == '--counting': + if val not in ('total', 'toplevel', 'detailed'): + PrintUsage('Valid counting options are total, toplevel, and detailed') + counting_style = val + elif opt == '--root': + global _root + _root = val + + if not filenames: + PrintUsage('No files were specified.') + + _SetOutputFormat(output_format) + _SetVerboseLevel(verbosity) + _SetFilters(filters) + _SetCountingStyle(counting_style) + + return filenames + + +def main(): + filenames = ParseArguments(sys.argv[1:]) + + # Change stderr to write with replacement characters so we don't die + # if we try to print something containing non-ASCII characters. + sys.stderr = codecs.StreamReaderWriter(sys.stderr, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace') + + _cpplint_state.ResetErrorCounts() + for filename in filenames: + ProcessFile(filename, _cpplint_state.verbose_level) + _cpplint_state.PrintErrorCounts() + + sys.exit(_cpplint_state.error_count > 0) + + +if __name__ == '__main__': + main() diff --git a/lib/sanitizer_common/scripts/gen_dynamic_list.py b/lib/sanitizer_common/scripts/gen_dynamic_list.py new file mode 100755 index 000000000000..32ba9226911e --- /dev/null +++ b/lib/sanitizer_common/scripts/gen_dynamic_list.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +#===- lib/sanitizer_common/scripts/gen_dynamic_list.py ---------------------===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# +# +# Generates the list of functions that should be exported from sanitizer +# runtimes. The output format is recognized by --dynamic-list linker option. +# Usage: +# gen_dynamic_list.py libclang_rt.*san*.a [ files ... ] +# +#===------------------------------------------------------------------------===# +import os +import re +import subprocess +import sys + +new_delete = set(['_ZdaPv', '_ZdaPvRKSt9nothrow_t', + '_ZdlPv', '_ZdlPvRKSt9nothrow_t', + '_Znam', '_ZnamRKSt9nothrow_t', + '_Znwm', '_ZnwmRKSt9nothrow_t']) + +versioned_functions = set(['memcpy', 'pthread_attr_getaffinity_np', + 'pthread_cond_broadcast', + 'pthread_cond_destroy', 'pthread_cond_init', + 'pthread_cond_signal', 'pthread_cond_timedwait', + 'pthread_cond_wait', 'realpath', + 'sched_getaffinity']) + +def get_global_functions(library): + functions = [] + nm_proc = subprocess.Popen(['nm', library], stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + nm_out = nm_proc.communicate()[0].split('\n') + if nm_proc.returncode != 0: + raise subprocess.CalledProcessError(nm_proc.returncode, 'nm') + for line in nm_out: + cols = line.split(' ') + if (len(cols) == 3 and cols[1] in ('T', 'W')) : + functions.append(cols[2]) + return functions + +def main(argv): + result = [] + + library = argv[1] + all_functions = get_global_functions(library) + function_set = set(all_functions) + for func in all_functions: + # Export new/delete operators. + if func in new_delete: + result.append(func) + continue + # Export interceptors. + match = re.match('__interceptor_(.*)', func) + if match: + result.append(func) + # We have to avoid exporting the interceptors for versioned library + # functions due to gold internal error. + orig_name = match.group(1) + if orig_name in function_set and orig_name not in versioned_functions: + result.append(orig_name) + continue + # Export sanitizer interface functions. + if re.match('__sanitizer_(.*)', func): + result.append(func) + + # Additional exported functions from files. + for fname in argv[2:]: + f = open(fname, 'r') + for line in f: + result.append(line.rstrip()) + # Print the resulting list in the format recognized by ld. + print '{' + result.sort() + for f in result: + print ' ' + f + ';' + print '};' + +if __name__ == '__main__': + main(sys.argv) diff --git a/lib/sanitizer_common/scripts/sancov.py b/lib/sanitizer_common/scripts/sancov.py new file mode 100755 index 000000000000..aa791bc4eb01 --- /dev/null +++ b/lib/sanitizer_common/scripts/sancov.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# Merge or print the coverage data collected by asan's coverage. +# Input files are sequences of 4-byte integers. +# We need to merge these integers into a set and then +# either print them (as hex) or dump them into another file. +import array +import sys + +prog_name = ""; + +def Usage(): + print >> sys.stderr, "Usage: \n" + \ + " " + prog_name + " merge file1 [file2 ...] > output\n" \ + " " + prog_name + " print file1 [file2 ...]\n" + exit(1) + +def ReadOneFile(path): + f = open(path, mode="rb") + f.seek(0, 2) + size = f.tell() + f.seek(0, 0) + s = set(array.array('I', f.read(size))) + f.close() + print >>sys.stderr, "%s: read %d PCs from %s" % (prog_name, size / 4, path) + return s + +def Merge(files): + s = set() + for f in files: + s = s.union(ReadOneFile(f)) + print >> sys.stderr, "%s: %d files merged; %d PCs total" % \ + (prog_name, len(files), len(s)) + return sorted(s) + +def PrintFiles(files): + s = Merge(files) + for i in s: + print "0x%x" % i + +def MergeAndPrint(files): + if sys.stdout.isatty(): + Usage() + s = Merge(files) + a = array.array('I', s) + a.tofile(sys.stdout) + +if __name__ == '__main__': + prog_name = sys.argv[0] + if len(sys.argv) <= 2: + Usage(); + if sys.argv[1] == "print": + PrintFiles(sys.argv[2:]) + elif sys.argv[1] == "merge": + MergeAndPrint(sys.argv[2:]) + else: + Usage() diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt index 25e57507ad14..5b66917b05b0 100644 --- a/lib/sanitizer_common/tests/CMakeLists.txt +++ b/lib/sanitizer_common/tests/CMakeLists.txt @@ -5,20 +5,24 @@ set(SANITIZER_UNITTESTS sanitizer_atomic_test.cc sanitizer_common_test.cc sanitizer_flags_test.cc + sanitizer_ioctl_test.cc sanitizer_libc_test.cc sanitizer_linux_test.cc sanitizer_list_test.cc sanitizer_mutex_test.cc + sanitizer_nolibc_test.cc + sanitizer_posix_test.cc sanitizer_printf_test.cc sanitizer_scanf_interceptor_test.cc sanitizer_stackdepot_test.cc sanitizer_stacktrace_test.cc sanitizer_stoptheworld_test.cc + sanitizer_suppressions_test.cc sanitizer_test_main.cc - sanitizer_thread_registry_test.cc - ) + sanitizer_thread_registry_test.cc) -set(SANITIZER_TEST_HEADERS) +set(SANITIZER_TEST_HEADERS + sanitizer_test_utils.h) foreach(header ${SANITIZER_HEADERS}) list(APPEND SANITIZER_TEST_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../${header}) endforeach() @@ -86,6 +90,24 @@ macro(add_sanitizer_tests_for_arch arch) DEPS ${SANITIZER_TEST_OBJECTS} ${SANITIZER_COMMON_LIB} LINK_FLAGS ${SANITIZER_TEST_LINK_FLAGS_COMMON} -lpthread ${TARGET_FLAGS}) + + if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND "${arch}" STREQUAL "x86_64") + # Test that the libc-independent part of sanitizer_common is indeed + # independent of libc, by linking this binary without libc (here) and + # executing it (unit test in sanitizer_nolibc_test.cc). + clang_compile(sanitizer_nolibc_test_main.${arch}.o + sanitizer_nolibc_test_main.cc + CFLAGS ${SANITIZER_TEST_CFLAGS_COMMON} ${TARGET_FLAGS} + DEPS ${SANITIZER_RUNTIME_LIBRARIES} ${SANITIZER_TEST_HEADERS}) + add_compiler_rt_test(SanitizerUnitTests "Sanitizer-${arch}-Test-Nolibc" + OBJECTS sanitizer_nolibc_test_main.${arch}.o + -Wl,-whole-archive + libRTSanitizerCommon.test.nolibc.${arch}.a + -Wl,-no-whole-archive + DEPS sanitizer_nolibc_test_main.${arch}.o + RTSanitizerCommon.test.nolibc.${arch} + LINK_FLAGS -nostdlib ${TARGET_FLAGS}) + endif() endmacro() if(COMPILER_RT_CAN_EXECUTE_TESTS) @@ -99,6 +121,8 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS) add_sanitizer_common_lib("RTSanitizerCommon.test.x86_64" $<TARGET_OBJECTS:RTSanitizerCommon.x86_64> $<TARGET_OBJECTS:RTSanitizerCommonLibc.x86_64>) + add_sanitizer_common_lib("RTSanitizerCommon.test.nolibc.x86_64" + $<TARGET_OBJECTS:RTSanitizerCommon.x86_64>) endif() if(CAN_TARGET_i386) add_sanitizer_common_lib("RTSanitizerCommon.test.i386" diff --git a/lib/sanitizer_common/tests/lit.cfg b/lib/sanitizer_common/tests/lit.cfg deleted file mode 100644 index 303d56c91079..000000000000 --- a/lib/sanitizer_common/tests/lit.cfg +++ /dev/null @@ -1,28 +0,0 @@ -# -*- Python -*- - -import os - -def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if not attr_value: - lit.fatal("No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) - return attr_value - -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') -compiler_rt_lit_unit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.unit.cfg") -lit.load_config(config, compiler_rt_lit_unit_cfg) - -# Setup config name. -config.name = 'SanitizerCommon-Unit' - -# Setup test source and exec root. For unit tests, we define -# it as build directory with sanitizer_common unit tests. -llvm_obj_root = get_required_attr(config, "llvm_obj_root") -config.test_exec_root = os.path.join(llvm_obj_root, "projects", - "compiler-rt", "lib", - "sanitizer_common", "tests") -config.test_source_root = config.test_exec_root diff --git a/lib/sanitizer_common/tests/lit.site.cfg.in b/lib/sanitizer_common/tests/lit.site.cfg.in index 50485aa16ec2..5ceb9e4c5c28 100644 --- a/lib/sanitizer_common/tests/lit.site.cfg.in +++ b/lib/sanitizer_common/tests/lit.site.cfg.in @@ -1,16 +1,14 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! -config.llvm_obj_root = "@LLVM_BINARY_DIR@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_build_mode = "@LLVM_BUILD_MODE@" +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") -try: - config.llvm_build_mode = config.llvm_build_mode % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) +# Setup config name. +config.name = 'SanitizerCommon-Unit' -# Let the main config do the real work. -lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") +# Setup test source and exec root. For unit tests, we define +# it as build directory with sanitizer_common tests. +config.test_exec_root = os.path.join("@COMPILER_RT_BINARY_DIR@", "lib", + "sanitizer_common", "tests") +config.test_source_root = config.test_exec_root diff --git a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc index de949ca7defe..d92a07fe4c2d 100644 --- a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc @@ -12,7 +12,9 @@ // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_test_utils.h" @@ -67,6 +69,10 @@ TEST(SanitizerCommon, CompactSizeClassMap) { TestSizeClassMap<CompactSizeClassMap>(); } +TEST(SanitizerCommon, InternalSizeClassMap) { + TestSizeClassMap<InternalSizeClassMap>(); +} + template <class Allocator> void TestSizeClassAllocator() { Allocator *a = new Allocator; @@ -411,12 +417,20 @@ void TestCombinedAllocator() { memset(&cache, 0, sizeof(cache)); a->InitCache(&cache); + bool allocator_may_return_null = common_flags()->allocator_may_return_null; + common_flags()->allocator_may_return_null = true; EXPECT_EQ(a->Allocate(&cache, -1, 1), (void*)0); EXPECT_EQ(a->Allocate(&cache, -1, 1024), (void*)0); EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1), (void*)0); EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1024), (void*)0); EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1023, 1024), (void*)0); + common_flags()->allocator_may_return_null = false; + EXPECT_DEATH(a->Allocate(&cache, -1, 1), + "allocator is terminating the process"); + // Restore the original value. + common_flags()->allocator_may_return_null = allocator_may_return_null; + const uptr kNumAllocs = 100000; const uptr kNumIter = 10; for (uptr iter = 0; iter < kNumIter; iter++) { @@ -611,6 +625,11 @@ TEST(Allocator, Stress) { } } +TEST(Allocator, InternalAllocFailure) { + EXPECT_DEATH(Ident(InternalAlloc(10 << 20)), + "Unexpected mmap in InternalAllocator!"); +} + TEST(Allocator, ScopedBuffer) { const int kSize = 512; { @@ -625,16 +644,9 @@ TEST(Allocator, ScopedBuffer) { } } -class IterationTestCallback { - public: - explicit IterationTestCallback(std::set<void *> *chunks) - : chunks_(chunks) {} - void operator()(void *chunk) const { - chunks_->insert(chunk); - } - private: - std::set<void *> *chunks_; -}; +void IterationTestCallback(uptr chunk, void *arg) { + reinterpret_cast<std::set<uptr> *>(arg)->insert(chunk); +} template <class Allocator> void TestSizeClassAllocatorIteration() { @@ -663,15 +675,15 @@ void TestSizeClassAllocatorIteration() { } } - std::set<void *> reported_chunks; - IterationTestCallback callback(&reported_chunks); + std::set<uptr> reported_chunks; a->ForceLock(); - a->ForEachChunk(callback); + a->ForEachChunk(IterationTestCallback, &reported_chunks); a->ForceUnlock(); for (uptr i = 0; i < allocated.size(); i++) { // Don't use EXPECT_NE. Reporting the first mismatch is enough. - ASSERT_NE(reported_chunks.find(allocated[i]), reported_chunks.end()); + ASSERT_NE(reported_chunks.find(reinterpret_cast<uptr>(allocated[i])), + reported_chunks.end()); } a->TestOnlyUnmap(); @@ -698,22 +710,61 @@ TEST(SanitizerCommon, LargeMmapAllocatorIteration) { char *allocated[kNumAllocs]; static const uptr size = 40; // Allocate some. - for (uptr i = 0; i < kNumAllocs; i++) { + for (uptr i = 0; i < kNumAllocs; i++) allocated[i] = (char *)a.Allocate(&stats, size, 1); - } - std::set<void *> reported_chunks; - IterationTestCallback callback(&reported_chunks); + std::set<uptr> reported_chunks; a.ForceLock(); - a.ForEachChunk(callback); + a.ForEachChunk(IterationTestCallback, &reported_chunks); a.ForceUnlock(); for (uptr i = 0; i < kNumAllocs; i++) { // Don't use EXPECT_NE. Reporting the first mismatch is enough. - ASSERT_NE(reported_chunks.find(allocated[i]), reported_chunks.end()); + ASSERT_NE(reported_chunks.find(reinterpret_cast<uptr>(allocated[i])), + reported_chunks.end()); + } + for (uptr i = 0; i < kNumAllocs; i++) + a.Deallocate(&stats, allocated[i]); +} + +TEST(SanitizerCommon, LargeMmapAllocatorBlockBegin) { + LargeMmapAllocator<> a; + a.Init(); + AllocatorStats stats; + stats.Init(); + + static const uptr kNumAllocs = 1024; + static const uptr kNumExpectedFalseLookups = 10000000; + char *allocated[kNumAllocs]; + static const uptr size = 4096; + // Allocate some. + for (uptr i = 0; i < kNumAllocs; i++) { + allocated[i] = (char *)a.Allocate(&stats, size, 1); } + + a.ForceLock(); + for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) { + // if ((i & (i - 1)) == 0) fprintf(stderr, "[%zd]\n", i); + char *p1 = allocated[i % kNumAllocs]; + EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1)); + EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 + size / 2)); + EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 + size - 1)); + EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 - 100)); + } + + for (uptr i = 0; i < kNumExpectedFalseLookups; i++) { + void *p = reinterpret_cast<void *>(i % 1024); + EXPECT_EQ((void *)0, a.GetBlockBeginFastLocked(p)); + p = reinterpret_cast<void *>(~0L - (i % 1024)); + EXPECT_EQ((void *)0, a.GetBlockBeginFastLocked(p)); + } + a.ForceUnlock(); + + for (uptr i = 0; i < kNumAllocs; i++) + a.Deallocate(&stats, allocated[i]); } + #if SANITIZER_WORDSIZE == 64 // Regression test for out-of-memory condition in PopulateFreeList(). TEST(SanitizerCommon, SizeClassAllocator64PopulateFreeListOOM) { diff --git a/lib/sanitizer_common/tests/sanitizer_common_test.cc b/lib/sanitizer_common/tests/sanitizer_common_test.cc index 424c279d4ada..608f90487a7b 100644 --- a/lib/sanitizer_common/tests/sanitizer_common_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_common_test.cc @@ -10,7 +10,9 @@ // This file is a part of ThreadSanitizer/AddressSanitizer runtime. // //===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_platform.h" #include "gtest/gtest.h" @@ -97,8 +99,8 @@ TEST(SanitizerCommon, SanitizerSetThreadName) { } #endif -TEST(SanitizerCommon, InternalVector) { - InternalVector<uptr> vector(1); +TEST(SanitizerCommon, InternalMmapVector) { + InternalMmapVector<uptr> vector(1); for (uptr i = 0; i < 100; i++) { EXPECT_EQ(i, vector.size()); vector.push_back(i); @@ -158,4 +160,100 @@ TEST(SanitizerCommon, ThreadStackTlsWorker) { pthread_join(t, 0); } +bool UptrLess(uptr a, uptr b) { + return a < b; +} + +TEST(SanitizerCommon, InternalBinarySearch) { + static const uptr kSize = 5; + uptr arr[kSize]; + for (uptr i = 0; i < kSize; i++) arr[i] = i * i; + + for (uptr i = 0; i < kSize; i++) + ASSERT_EQ(InternalBinarySearch(arr, 0, kSize, i * i, UptrLess), i); + + ASSERT_EQ(InternalBinarySearch(arr, 0, kSize, 7, UptrLess), kSize + 1); +} + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +TEST(SanitizerCommon, FindPathToBinary) { + char *true_path = FindPathToBinary("true"); + EXPECT_NE((char*)0, internal_strstr(true_path, "/bin/true")); + InternalFree(true_path); + EXPECT_EQ(0, FindPathToBinary("unexisting_binary.ergjeorj")); +} +#endif + +TEST(SanitizerCommon, StripPathPrefix) { + EXPECT_EQ(0, StripPathPrefix(0, "prefix")); + EXPECT_STREQ("foo", StripPathPrefix("foo", 0)); + EXPECT_STREQ("dir/file.cc", + StripPathPrefix("/usr/lib/dir/file.cc", "/usr/lib/")); + EXPECT_STREQ("/file.cc", StripPathPrefix("/usr/myroot/file.cc", "/myroot")); + EXPECT_STREQ("file.h", StripPathPrefix("/usr/lib/./file.h", "/usr/lib/")); +} + +TEST(SanitizerCommon, InternalScopedString) { + InternalScopedString str(10); + EXPECT_EQ(0U, str.length()); + EXPECT_STREQ("", str.data()); + + str.append("foo"); + EXPECT_EQ(3U, str.length()); + EXPECT_STREQ("foo", str.data()); + + int x = 1234; + str.append("%d", x); + EXPECT_EQ(7U, str.length()); + EXPECT_STREQ("foo1234", str.data()); + + str.append("%d", x); + EXPECT_EQ(9U, str.length()); + EXPECT_STREQ("foo123412", str.data()); + + str.clear(); + EXPECT_EQ(0U, str.length()); + EXPECT_STREQ("", str.data()); + + str.append("0123456789"); + EXPECT_EQ(9U, str.length()); + EXPECT_STREQ("012345678", str.data()); +} + +TEST(SanitizerCommon, PrintSourceLocation) { + InternalScopedString str(128); + PrintSourceLocation(&str, "/dir/file.cc", 10, 5); + EXPECT_STREQ("/dir/file.cc:10:5", str.data()); + + str.clear(); + PrintSourceLocation(&str, "/dir/file.cc", 11, 0); + EXPECT_STREQ("/dir/file.cc:11", str.data()); + + str.clear(); + PrintSourceLocation(&str, "/dir/file.cc", 0, 0); + EXPECT_STREQ("/dir/file.cc", str.data()); + + // Check that we strip file prefix if necessary. + const char *old_strip_path_prefix = common_flags()->strip_path_prefix; + common_flags()->strip_path_prefix = "/dir/"; + str.clear(); + PrintSourceLocation(&str, "/dir/file.cc", 10, 5); + EXPECT_STREQ("file.cc:10:5", str.data()); + common_flags()->strip_path_prefix = old_strip_path_prefix; +} + +TEST(SanitizerCommon, PrintModuleAndOffset) { + InternalScopedString str(128); + PrintModuleAndOffset(&str, "/dir/exe", 0x123); + EXPECT_STREQ("(/dir/exe+0x123)", str.data()); + + // Check that we strip file prefix if necessary. + const char *old_strip_path_prefix = common_flags()->strip_path_prefix; + common_flags()->strip_path_prefix = "/dir/"; + str.clear(); + PrintModuleAndOffset(&str, "/dir/exe", 0x123); + EXPECT_STREQ("(exe+0x123)", str.data()); + common_flags()->strip_path_prefix = old_strip_path_prefix; +} + } // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc b/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc new file mode 100644 index 000000000000..154d9860b79f --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc @@ -0,0 +1,77 @@ +//===-- sanitizer_ioctl_test.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tests for ioctl interceptor implementation in sanitizer_common. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX + +#include <linux/input.h> +#include <vector> + +#include "interception/interception.h" +#include "sanitizer_test_utils.h" +#include "sanitizer_common/sanitizer_platform_limits_posix.h" +#include "sanitizer_common/sanitizer_common.h" +#include "gtest/gtest.h" + + +using namespace __sanitizer; + +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, sz) \ + do { \ + (void) ctx; \ + (void) ptr; \ + (void) sz; \ + } while (0) +#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sz) \ + do { \ + (void) ctx; \ + (void) ptr; \ + (void) sz; \ + } while (0) + +#include "sanitizer_common/sanitizer_common_interceptors_ioctl.inc" + +static struct IoctlInit { + IoctlInit() { + ioctl_init(); + // Avoid unused function warnings. + (void)&ioctl_common_pre; + (void)&ioctl_common_post; + } +} ioctl_static_initializer; + +TEST(SanitizerIoctl, Fixup) { + EXPECT_EQ((unsigned)FIONBIO, ioctl_request_fixup(FIONBIO)); + + EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(0, 16))); + EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(1, 16))); + EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(1, 17))); + EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(31, 16))); + EXPECT_NE(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(32, 16))); + + EXPECT_EQ(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(0))); + EXPECT_EQ(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(5))); + EXPECT_EQ(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(63))); + EXPECT_NE(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(64))); + + EXPECT_EQ(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(0))); + EXPECT_EQ(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(5))); + EXPECT_EQ(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(63))); + EXPECT_NE(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(64))); + + const ioctl_desc *desc = ioctl_lookup(EVIOCGKEY(16)); + EXPECT_NE((void *)0, desc); + EXPECT_EQ(EVIOCGKEY(0), desc->req); +} + +#endif // SANITIZER_LINUX diff --git a/lib/sanitizer_common/tests/sanitizer_libc_test.cc b/lib/sanitizer_common/tests/sanitizer_libc_test.cc index 39c29d357327..c4f3d8033c2d 100644 --- a/lib/sanitizer_common/tests/sanitizer_libc_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_libc_test.cc @@ -115,3 +115,10 @@ TEST(SanitizerCommon, FileOps) { internal_close(fd); } +TEST(SanitizerCommon, InternalStrFunctions) { + const char *haystack = "haystack"; + EXPECT_EQ(haystack + 2, internal_strchr(haystack, 'y')); + EXPECT_EQ(haystack + 2, internal_strchrnul(haystack, 'y')); + EXPECT_EQ(0, internal_strchr(haystack, 'z')); + EXPECT_EQ(haystack + 8, internal_strchrnul(haystack, 'z')); +} diff --git a/lib/sanitizer_common/tests/sanitizer_linux_test.cc b/lib/sanitizer_common/tests/sanitizer_linux_test.cc index b18aeb030acf..592d9c3eeaf5 100644 --- a/lib/sanitizer_common/tests/sanitizer_linux_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_linux_test.cc @@ -19,9 +19,6 @@ #include "sanitizer_common/sanitizer_common.h" #include "gtest/gtest.h" -#ifdef __x86_64__ -#include <asm/prctl.h> -#endif #include <pthread.h> #include <sched.h> #include <stdlib.h> @@ -29,10 +26,6 @@ #include <algorithm> #include <vector> -#ifdef __x86_64__ -extern "C" int arch_prctl(int code, __sanitizer::uptr *addr); -#endif - namespace __sanitizer { struct TidReporterArgument { @@ -202,23 +195,37 @@ TEST(SanitizerCommon, SetEnvTest) { EXPECT_EQ(0, getenv(kEnvName)); } -#ifdef __x86_64__ -// libpthread puts the thread descriptor (%fs:0x0) at the end of stack space. -void *thread_descriptor_test_func(void *arg) { - uptr fs; - arch_prctl(ARCH_GET_FS, &fs); +#if defined(__x86_64__) || defined(__i386__) +void *thread_self_offset_test_func(void *arg) { + bool result = + *(uptr *)((char *)ThreadSelf() + ThreadSelfOffset()) == ThreadSelf(); + return (void *)result; +} + +TEST(SanitizerLinux, ThreadSelfOffset) { + EXPECT_TRUE((bool)thread_self_offset_test_func(0)); + pthread_t tid; + void *result; + ASSERT_EQ(0, pthread_create(&tid, 0, thread_self_offset_test_func, 0)); + ASSERT_EQ(0, pthread_join(tid, &result)); + EXPECT_TRUE((bool)result); +} + +// libpthread puts the thread descriptor at the end of stack space. +void *thread_descriptor_size_test_func(void *arg) { + uptr descr_addr = ThreadSelf(); pthread_attr_t attr; pthread_getattr_np(pthread_self(), &attr); void *stackaddr; - uptr stacksize; + size_t stacksize; pthread_attr_getstack(&attr, &stackaddr, &stacksize); - return (void *)((uptr)stackaddr + stacksize - fs); + return (void *)((uptr)stackaddr + stacksize - descr_addr); } TEST(SanitizerLinux, ThreadDescriptorSize) { pthread_t tid; void *result; - pthread_create(&tid, 0, thread_descriptor_test_func, 0); + ASSERT_EQ(0, pthread_create(&tid, 0, thread_descriptor_size_test_func, 0)); ASSERT_EQ(0, pthread_join(tid, &result)); EXPECT_EQ((uptr)result, ThreadDescriptorSize()); } diff --git a/lib/sanitizer_common/tests/sanitizer_mutex_test.cc b/lib/sanitizer_common/tests/sanitizer_mutex_test.cc index 1dc9bef20710..06f742b41796 100644 --- a/lib/sanitizer_common/tests/sanitizer_mutex_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_mutex_test.cc @@ -65,7 +65,6 @@ class TestData { }; const int kThreads = 8; -const int kWriteRate = 1024; #if SANITIZER_DEBUG const int kIters = 16*1024; #else diff --git a/lib/sanitizer_common/tests/sanitizer_nolibc_test.cc b/lib/sanitizer_common/tests/sanitizer_nolibc_test.cc new file mode 100644 index 000000000000..d0d5a5e13898 --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_nolibc_test.cc @@ -0,0 +1,31 @@ +//===-- sanitizer_nolibc_test.cc ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// Tests for libc independence of sanitizer_common. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" + +#include "gtest/gtest.h" + +#include <stdlib.h> + +extern const char *argv0; + +#if SANITIZER_LINUX && defined(__x86_64__) +TEST(SanitizerCommon, NolibcMain) { + std::string NolibcTestPath = argv0; + NolibcTestPath += "-Nolibc"; + int status = system(NolibcTestPath.c_str()); + EXPECT_EQ(true, WIFEXITED(status)); + EXPECT_EQ(0, WEXITSTATUS(status)); +} +#endif diff --git a/lib/sanitizer_common/tests/sanitizer_nolibc_test_main.cc b/lib/sanitizer_common/tests/sanitizer_nolibc_test_main.cc new file mode 100644 index 000000000000..72df621d07ff --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_nolibc_test_main.cc @@ -0,0 +1,19 @@ +//===-- sanitizer_nolibc_test_main.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 ThreadSanitizer/AddressSanitizer runtime. +// Tests for libc independence of sanitizer_common. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_libc.h" + +extern "C" void _start() { + internal__exit(0); +} diff --git a/lib/sanitizer_common/tests/sanitizer_posix_test.cc b/lib/sanitizer_common/tests/sanitizer_posix_test.cc new file mode 100644 index 000000000000..035899c83022 --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_posix_test.cc @@ -0,0 +1,62 @@ +//===-- sanitizer_posix_test.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tests for POSIX-specific code. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_POSIX + +#include "sanitizer_common/sanitizer_common.h" +#include "gtest/gtest.h" + +#include <pthread.h> + +namespace __sanitizer { + +static pthread_key_t key; +static bool destructor_executed; + +extern "C" +void destructor(void *arg) { + uptr iter = reinterpret_cast<uptr>(arg); + if (iter > 1) { + ASSERT_EQ(0, pthread_setspecific(key, reinterpret_cast<void *>(iter - 1))); + return; + } + destructor_executed = true; +} + +extern "C" +void *thread_func(void *arg) { + return reinterpret_cast<void*>(pthread_setspecific(key, arg)); +} + +static void SpawnThread(uptr iteration) { + destructor_executed = false; + pthread_t tid; + ASSERT_EQ(0, pthread_create(&tid, 0, &thread_func, + reinterpret_cast<void *>(iteration))); + void *retval; + ASSERT_EQ(0, pthread_join(tid, &retval)); + ASSERT_EQ(0, retval); +} + +TEST(SanitizerCommon, PthreadDestructorIterations) { + ASSERT_EQ(0, pthread_key_create(&key, &destructor)); + SpawnThread(kPthreadDestructorIterations); + EXPECT_TRUE(destructor_executed); + SpawnThread(kPthreadDestructorIterations + 1); + EXPECT_FALSE(destructor_executed); +} + +} // namespace __sanitizer + +#endif // SANITIZER_POSIX diff --git a/lib/sanitizer_common/tests/sanitizer_printf_test.cc b/lib/sanitizer_common/tests/sanitizer_printf_test.cc index b1889cd8794e..2c478cc74286 100644 --- a/lib/sanitizer_common/tests/sanitizer_printf_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_printf_test.cc @@ -104,22 +104,36 @@ TEST(Printf, OverflowPtr) { } template<typename T> -static void TestMinMax(const char *fmt, T min, T max) { +static void TestAgainstLibc(const char *fmt, T arg1, T arg2) { char buf[1024]; - uptr len = internal_snprintf(buf, sizeof(buf), fmt, min, max); + uptr len = internal_snprintf(buf, sizeof(buf), fmt, arg1, arg2); char buf2[1024]; - snprintf(buf2, sizeof(buf2), fmt, min, max); + snprintf(buf2, sizeof(buf2), fmt, arg1, arg2); EXPECT_EQ(len, strlen(buf)); EXPECT_STREQ(buf2, buf); } TEST(Printf, MinMax) { - TestMinMax<int>("%d-%d", INT_MIN, INT_MAX); // NOLINT - TestMinMax<long>("%zd-%zd", LONG_MIN, LONG_MAX); // NOLINT - TestMinMax<unsigned>("%u-%u", 0, UINT_MAX); // NOLINT - TestMinMax<unsigned long>("%zu-%zu", 0, ULONG_MAX); // NOLINT - TestMinMax<unsigned>("%x-%x", 0, UINT_MAX); // NOLINT - TestMinMax<unsigned long>("%zx-%zx", 0, ULONG_MAX); // NOLINT + TestAgainstLibc<int>("%d-%d", INT_MIN, INT_MAX); // NOLINT + TestAgainstLibc<long>("%zd-%zd", LONG_MIN, LONG_MAX); // NOLINT + TestAgainstLibc<unsigned>("%u-%u", 0, UINT_MAX); // NOLINT + TestAgainstLibc<unsigned long>("%zu-%zu", 0, ULONG_MAX); // NOLINT + TestAgainstLibc<unsigned>("%x-%x", 0, UINT_MAX); // NOLINT + TestAgainstLibc<unsigned long>("%zx-%zx", 0, ULONG_MAX); // NOLINT + Report("%zd\n", LONG_MIN); +} + +TEST(Printf, Padding) { + TestAgainstLibc<int>("%3d - %3d", 1, 0); + TestAgainstLibc<int>("%3d - %3d", -1, 123); + TestAgainstLibc<int>("%3d - %3d", -1, -123); + TestAgainstLibc<int>("%3d - %3d", 12, 1234); + TestAgainstLibc<int>("%3d - %3d", -12, -1234); + TestAgainstLibc<int>("%03d - %03d", 1, 0); + TestAgainstLibc<int>("%03d - %03d", -1, 123); + TestAgainstLibc<int>("%03d - %03d", -1, -123); + TestAgainstLibc<int>("%03d - %03d", 12, 1234); + TestAgainstLibc<int>("%03d - %03d", -12, -1234); } } // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc b/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc new file mode 100644 index 000000000000..d16e2eebf98e --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc @@ -0,0 +1,30 @@ +//===-- sanitizer_procmaps_test.cc ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_procmaps.h" +//#include "sanitizer_common/sanitizer_internal_defs.h" +//#include "sanitizer_common/sanitizer_libc.h" +#include "gtest/gtest.h" + +namespace __sanitizer { + +#ifdef SANITIZER_LINUX +TEST(ProcMaps, CodeRange) { + uptr start, end; + bool res = GetCodeRangeForFile("[vdso]", &start, &end); + EXPECT_EQ(res, true); + EXPECT_GT(start, (uptr)0); + EXPECT_LT(start, end); +} +#endif + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc b/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc index 1df2bcfd4bec..e0354062508f 100644 --- a/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc @@ -169,7 +169,7 @@ TEST(SanitizerCommonInterceptors, Scanf) { testScanfPartial("%d%d%d%d //3\n", 3, 3, I, I, I); testScanfPartial("%d%d%d%d //4\n", 4, 4, I, I, I, I); - testScanfPartial("%d%n%n%d //1\n", 1, 1, I); + testScanfPartial("%d%n%n%d //1\n", 1, 3, I, I, I); testScanfPartial("%d%n%n%d //2\n", 2, 4, I, I, I, I); testScanfPartial("%d%n%n%d %s %s", 3, 5, I, I, I, I, scanf_buf_size); diff --git a/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc b/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc index 5350c2ab8dbc..5c075d53ebff 100644 --- a/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc @@ -66,4 +66,27 @@ TEST(SanitizerCommon, StackDepotSeveral) { EXPECT_NE(i1, i2); } +TEST(SanitizerCommon, StackDepotReverseMap) { + uptr s1[] = {1, 2, 3, 4, 5}; + uptr s2[] = {7, 1, 3, 0}; + uptr s3[] = {10, 2, 5, 3}; + uptr s4[] = {1, 3, 2, 5}; + u32 ids[4] = {0}; + ids[0] = StackDepotPut(s1, ARRAY_SIZE(s1)); + ids[1] = StackDepotPut(s2, ARRAY_SIZE(s2)); + ids[2] = StackDepotPut(s3, ARRAY_SIZE(s3)); + ids[3] = StackDepotPut(s4, ARRAY_SIZE(s4)); + + StackDepotReverseMap map; + + for (uptr i = 0; i < 4; i++) { + uptr sz_depot, sz_map; + const uptr *sp_depot, *sp_map; + sp_depot = StackDepotGet(ids[i], &sz_depot); + sp_map = map.Get(ids[i], &sz_map); + EXPECT_EQ(sz_depot, sz_map); + EXPECT_EQ(sp_depot, sp_map); + } +} + } // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc index 3d352cb97a5e..2b842cd8134a 100644 --- a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc @@ -20,6 +20,13 @@ namespace __sanitizer { class FastUnwindTest : public ::testing::Test { protected: virtual void SetUp(); + bool TryFastUnwind(uptr max_depth) { + if (!StackTrace::WillUseFastUnwind(true)) + return false; + trace.Unwind(max_depth, start_pc, (uptr)&fake_stack[0], fake_top, + fake_bottom, true); + return true; + } uptr fake_stack[10]; uptr start_pc; @@ -47,16 +54,11 @@ void FastUnwindTest::SetUp() { // Bottom is one slot before the start because FastUnwindStack uses >. fake_bottom = (uptr)&fake_stack[-1]; start_pc = PC(0); - - // This is common setup done by __asan::GetStackTrace(). - trace.size = 0; - trace.max_size = ARRAY_SIZE(fake_stack); - trace.trace[0] = start_pc; } TEST_F(FastUnwindTest, Basic) { - trace.FastUnwindStack(start_pc, (uptr)&fake_stack[0], - fake_top, fake_bottom); + if (!TryFastUnwind(kStackTraceMax)) + return; // Should get all on-stack retaddrs and start_pc. EXPECT_EQ(6U, trace.size); EXPECT_EQ(start_pc, trace.trace[0]); @@ -69,8 +71,8 @@ TEST_F(FastUnwindTest, Basic) { TEST_F(FastUnwindTest, FramePointerLoop) { // Make one fp point to itself. fake_stack[4] = (uptr)&fake_stack[4]; - trace.FastUnwindStack(start_pc, (uptr)&fake_stack[0], - fake_top, fake_bottom); + if (!TryFastUnwind(kStackTraceMax)) + return; // Should get all on-stack retaddrs up to the 4th slot and start_pc. EXPECT_EQ(4U, trace.size); EXPECT_EQ(start_pc, trace.trace[0]); @@ -82,8 +84,8 @@ TEST_F(FastUnwindTest, FramePointerLoop) { TEST_F(FastUnwindTest, MisalignedFramePointer) { // Make one fp misaligned. fake_stack[4] += 3; - trace.FastUnwindStack(start_pc, (uptr)&fake_stack[0], - fake_top, fake_bottom); + if (!TryFastUnwind(kStackTraceMax)) + return; // Should get all on-stack retaddrs up to the 4th slot and start_pc. EXPECT_EQ(4U, trace.size); EXPECT_EQ(start_pc, trace.trace[0]); @@ -92,5 +94,12 @@ TEST_F(FastUnwindTest, MisalignedFramePointer) { } } +TEST_F(FastUnwindTest, OneFrameStackTrace) { + if (!TryFastUnwind(1)) + return; + EXPECT_EQ(1U, trace.size); + EXPECT_EQ(start_pc, trace.trace[0]); + EXPECT_EQ((uptr)&fake_stack[0], trace.top_frame_bp); +} } // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc b/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc index a5f8516df575..b6786ba8b013 100644 --- a/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc @@ -12,7 +12,7 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_LINUX +#if SANITIZER_LINUX && defined(__x86_64__) #include "sanitizer_common/sanitizer_stoptheworld.h" #include "gtest/gtest.h" @@ -191,4 +191,4 @@ TEST(StopTheWorld, SuspendThreadsAdvanced) { } // namespace __sanitizer -#endif // SANITIZER_LINUX +#endif // SANITIZER_LINUX && defined(__x86_64__) diff --git a/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc new file mode 100644 index 000000000000..ea8741d4f01a --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc @@ -0,0 +1,152 @@ +//===-- sanitizer_suppressions_test.cc ------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_suppressions.h" +#include "gtest/gtest.h" + +#include <string.h> + +namespace __sanitizer { + +static bool MyMatch(const char *templ, const char *func) { + char tmp[1024]; + strcpy(tmp, templ); // NOLINT + return TemplateMatch(tmp, func); +} + +TEST(Suppressions, Match) { + EXPECT_TRUE(MyMatch("foobar$", "foobar")); + + EXPECT_TRUE(MyMatch("foobar", "foobar")); + EXPECT_TRUE(MyMatch("*foobar*", "foobar")); + EXPECT_TRUE(MyMatch("foobar", "prefix_foobar_postfix")); + EXPECT_TRUE(MyMatch("*foobar*", "prefix_foobar_postfix")); + EXPECT_TRUE(MyMatch("foo*bar", "foo_middle_bar")); + EXPECT_TRUE(MyMatch("foo*bar", "foobar")); + EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_bar_another_baz")); + EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_barbaz")); + EXPECT_TRUE(MyMatch("^foobar", "foobar")); + EXPECT_TRUE(MyMatch("^foobar", "foobar_postfix")); + EXPECT_TRUE(MyMatch("^*foobar", "foobar")); + EXPECT_TRUE(MyMatch("^*foobar", "prefix_foobar")); + EXPECT_TRUE(MyMatch("foobar$", "foobar")); + EXPECT_TRUE(MyMatch("foobar$", "prefix_foobar")); + EXPECT_TRUE(MyMatch("*foobar*$", "foobar")); + EXPECT_TRUE(MyMatch("*foobar*$", "foobar_postfix")); + EXPECT_TRUE(MyMatch("^foobar$", "foobar")); + + EXPECT_FALSE(MyMatch("foo", "baz")); + EXPECT_FALSE(MyMatch("foobarbaz", "foobar")); + EXPECT_FALSE(MyMatch("foobarbaz", "barbaz")); + EXPECT_FALSE(MyMatch("foo*bar", "foobaz")); + EXPECT_FALSE(MyMatch("foo*bar", "foo_baz")); + EXPECT_FALSE(MyMatch("^foobar", "prefix_foobar")); + EXPECT_FALSE(MyMatch("foobar$", "foobar_postfix")); + EXPECT_FALSE(MyMatch("^foobar$", "prefix_foobar")); + EXPECT_FALSE(MyMatch("^foobar$", "foobar_postfix")); + EXPECT_FALSE(MyMatch("foo^bar", "foobar")); + EXPECT_FALSE(MyMatch("foo$bar", "foobar")); + EXPECT_FALSE(MyMatch("foo$^bar", "foobar")); +} + +TEST(Suppressions, TypeStrings) { + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionNone), "none")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionRace), "race")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionMutex), "mutex")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionThread), "thread")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionSignal), "signal")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLeak), "leak")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLib), + "called_from_lib")); + // Ensure this test is up-to-date when suppression types are added. + CHECK_EQ(SuppressionTypeCount, 7); +} + +class SuppressionContextTest : public ::testing::Test { + public: + virtual void SetUp() { ctx_ = new(placeholder_) SuppressionContext; } + virtual void TearDown() { ctx_->~SuppressionContext(); } + + protected: + InternalMmapVector<Suppression> *Suppressions() { + return &ctx_->suppressions_; + } + SuppressionContext *ctx_; + ALIGNED(64) char placeholder_[sizeof(SuppressionContext)]; +}; + +TEST_F(SuppressionContextTest, Parse) { + ctx_->Parse( + "race:foo\n" + " race:bar\n" // NOLINT + "race:baz \n" // NOLINT + "# a comment\n" + "race:quz\n" + ); // NOLINT + EXPECT_EQ((unsigned)4, ctx_->SuppressionCount()); + EXPECT_EQ((*Suppressions())[3].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[3].templ, "quz")); + EXPECT_EQ((*Suppressions())[2].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[2].templ, "baz")); + EXPECT_EQ((*Suppressions())[1].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar")); + EXPECT_EQ((*Suppressions())[0].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo")); +} + +TEST_F(SuppressionContextTest, Parse2) { + ctx_->Parse( + " # first line comment\n" // NOLINT + " race:bar \n" // NOLINT + "race:baz* *baz\n" + "# a comment\n" + "# last line comment\n" + ); // NOLINT + EXPECT_EQ((unsigned)2, ctx_->SuppressionCount()); + EXPECT_EQ((*Suppressions())[1].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "baz* *baz")); + EXPECT_EQ((*Suppressions())[0].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "bar")); +} + +TEST_F(SuppressionContextTest, Parse3) { + ctx_->Parse( + "# last suppression w/o line-feed\n" + "race:foo\n" + "race:bar" + ); // NOLINT + EXPECT_EQ((unsigned)2, ctx_->SuppressionCount()); + EXPECT_EQ((*Suppressions())[1].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar")); + EXPECT_EQ((*Suppressions())[0].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo")); +} + +TEST_F(SuppressionContextTest, ParseType) { + ctx_->Parse( + "race:foo\n" + "thread:bar\n" + "mutex:baz\n" + "signal:quz\n" + ); // NOLINT + EXPECT_EQ((unsigned)4, ctx_->SuppressionCount()); + EXPECT_EQ((*Suppressions())[3].type, SuppressionSignal); + EXPECT_EQ(0, strcmp((*Suppressions())[3].templ, "quz")); + EXPECT_EQ((*Suppressions())[2].type, SuppressionMutex); + EXPECT_EQ(0, strcmp((*Suppressions())[2].templ, "baz")); + EXPECT_EQ((*Suppressions())[1].type, SuppressionThread); + EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar")); + EXPECT_EQ((*Suppressions())[0].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo")); +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_test_main.cc b/lib/sanitizer_common/tests/sanitizer_test_main.cc index 12d1d15af917..b7fd3dafab26 100644 --- a/lib/sanitizer_common/tests/sanitizer_test_main.cc +++ b/lib/sanitizer_common/tests/sanitizer_test_main.cc @@ -12,7 +12,10 @@ //===----------------------------------------------------------------------===// #include "gtest/gtest.h" +const char *argv0; + int main(int argc, char **argv) { + argv0 = argv[0]; testing::GTEST_FLAG(death_test_style) = "threadsafe"; testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/lib/sanitizer_common/tests/sanitizer_test_utils.h b/lib/sanitizer_common/tests/sanitizer_test_utils.h index a770d0fbd39e..17adb2647656 100644 --- a/lib/sanitizer_common/tests/sanitizer_test_utils.h +++ b/lib/sanitizer_common/tests/sanitizer_test_utils.h @@ -36,14 +36,14 @@ typedef __int64 int64_t; #define __has_feature(x) 0 #endif -#ifndef ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS +#ifndef ATTRIBUTE_NO_SANITIZE_ADDRESS # if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) -# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \ +# define ATTRIBUTE_NO_SANITIZE_ADDRESS \ __attribute__((no_sanitize_address)) # else -# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS +# define ATTRIBUTE_NO_SANITIZE_ADDRESS # endif -#endif // ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS +#endif // ATTRIBUTE_NO_SANITIZE_ADDRESS #if __LP64__ || defined(_WIN64) # define SANITIZER_WORDSIZE 64 diff --git a/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc b/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc index e080403fb56c..ddc8dba5d3e0 100644 --- a/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc @@ -23,8 +23,7 @@ static LowLevelAllocator tctx_allocator; template<typename TCTX> static ThreadContextBase *GetThreadContext(u32 tid) { BlockingMutexLock l(&tctx_allocator_lock); - void *mem = tctx_allocator.Allocate(sizeof(TCTX)); - return new(mem) TCTX(tid); + return new(tctx_allocator) TCTX(tid); } static const u32 kMaxRegistryThreads = 1000; diff --git a/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt index 282889567509..fc1944b02fa5 100644 --- a/lib/tsan/CMakeLists.txt +++ b/lib/tsan/CMakeLists.txt @@ -8,10 +8,14 @@ set(TSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS} -fPIE -fno-rtti) -# FIXME: Add support for compile flags: -# -Wframe-larger-than=512, -# -Wglobal-constructors, -# --sysroot=. + +set(TSAN_RTL_CFLAGS + ${TSAN_CFLAGS} + -Wframe-larger-than=512) +if(SUPPORTS_GLOBAL_CONSTRUCTORS_FLAG) + list(APPEND TSAN_RTL_CFLAGS -Wglobal-constructors) +endif() +# FIXME: Add support for --sysroot=. compile flag: if("${CMAKE_BUILD_TYPE}" EQUAL "Release") set(TSAN_COMMON_DEFINITIONS DEBUG=0) @@ -19,7 +23,57 @@ else() set(TSAN_COMMON_DEFINITIONS DEBUG=1) endif() -add_subdirectory(rtl) +set(TSAN_SOURCES + rtl/tsan_clock.cc + rtl/tsan_flags.cc + rtl/tsan_fd.cc + rtl/tsan_interceptors.cc + rtl/tsan_interface_ann.cc + rtl/tsan_interface_atomic.cc + rtl/tsan_interface.cc + rtl/tsan_interface_java.cc + rtl/tsan_md5.cc + rtl/tsan_mman.cc + rtl/tsan_mutex.cc + rtl/tsan_mutexset.cc + rtl/tsan_report.cc + rtl/tsan_rtl.cc + rtl/tsan_rtl_mutex.cc + rtl/tsan_rtl_report.cc + rtl/tsan_rtl_thread.cc + rtl/tsan_stat.cc + rtl/tsan_suppressions.cc + rtl/tsan_symbolize.cc + rtl/tsan_sync.cc) + +if(APPLE) + list(APPEND TSAN_SOURCES rtl/tsan_platform_mac.cc) +elseif(UNIX) + # Assume Linux + list(APPEND TSAN_SOURCES + rtl/tsan_platform_linux.cc + rtl/tsan_symbolize_addr2line_linux.cc) +endif() + +set(TSAN_RUNTIME_LIBRARIES) +# TSan is currently supported on 64-bit Linux only. +if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE) + set(TSAN_ASM_SOURCES rtl/tsan_rtl_amd64.S) + # Pass ASM file directly to the C++ compiler. + set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES + LANGUAGE C) + set(arch "x86_64") + add_compiler_rt_static_runtime(clang_rt.tsan-${arch} ${arch} + SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES} + $<TARGET_OBJECTS:RTInterception.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + CFLAGS ${TSAN_RTL_CFLAGS} + DEFS ${TSAN_COMMON_DEFINITIONS}) + add_sanitizer_rt_symbols(clang_rt.tsan-${arch} rtl/tsan.syms.extra) + list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch} + clang_rt.tsan-${arch}-symbols) +endif() if(LLVM_INCLUDE_TESTS) add_subdirectory(tests) diff --git a/lib/tsan/Makefile.old b/lib/tsan/Makefile.old index b548f5d2f6ee..c10d49842cac 100644 --- a/lib/tsan/Makefile.old +++ b/lib/tsan/Makefile.old @@ -21,8 +21,11 @@ GTEST_BUILD_DIR=$(GTEST_ROOT)/build GTEST_LIB_NAME=gtest-all.o GTEST_LIB=$(GTEST_BUILD_DIR)/$(GTEST_LIB_NAME) -SANITIZER_COMMON_TESTS_SRC=$(wildcard ../sanitizer_common/tests/*_test.cc) -SANITIZER_COMMON_TESTS_OBJ=$(patsubst %.cc,%.o,$(SANITIZER_COMMON_TESTS_SRC)) +SANITIZER_TESTS_PATH=../sanitizer_common/tests +SANITIZER_COMMON_TESTS_SRC=$(wildcard $(SANITIZER_TESTS_PATH)/*_test.cc) +SANITIZER_COMMON_EXCLUDED_TESTS=$(SANITIZER_TESTS_PATH)/sanitizer_nolibc_test.cc +SANITIZER_COMMON_GOOD_TESTS=$(filter-out $(SANITIZER_COMMON_EXCLUDED_TESTS), $(SANITIZER_COMMON_TESTS_SRC)) +SANITIZER_COMMON_TESTS_OBJ=$(patsubst %.cc,%.o,$(SANITIZER_COMMON_GOOD_TESTS)) RTL_TEST_SRC=$(wildcard tests/rtl/*.cc) RTL_TEST_OBJ=$(patsubst %.cc,%.o,$(RTL_TEST_SRC)) UNIT_TEST_SRC=$(wildcard tests/unit/*_test.cc) @@ -51,7 +54,7 @@ libtsan: tsan_test: $(UNIT_TEST_OBJ) $(RTL_TEST_OBJ) \ $(SANITIZER_COMMON_TESTS_OBJ) $(LIBTSAN) $(GTEST_LIB) - $(CXX) $^ -o $@ $(LDFLAGS) + $(CXX) -Wl,--whole-archive $^ -Wl,--no-whole-archive -o $@ $(LDFLAGS) test: libtsan tsan_test diff --git a/lib/tsan/check_cmake.sh b/lib/tsan/check_cmake.sh index 52c97c339096..7e858efee913 100755 --- a/lib/tsan/check_cmake.sh +++ b/lib/tsan/check_cmake.sh @@ -3,10 +3,16 @@ set -u set -e ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -mkdir -p $ROOT/build -cd $ROOT/build -CC=clang CXX=clang++ cmake -DLLVM_ENABLE_WERROR=ON -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON $ROOT/../../../.. -make -j64 -make check-sanitizer -j64 -make check-tsan -j64 -make check-asan -j64 +if [ -d "$ROOT/build" ]; then + cd $ROOT/build +else + mkdir -p $ROOT/build + cd $ROOT/build + CC=clang CXX=clang++ cmake -G Ninja -DLLVM_ENABLE_WERROR=ON -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON $ROOT/../../../.. +fi +ninja +ninja check-sanitizer +ninja check-tsan +ninja check-asan +ninja check-msan +ninja check-lsan diff --git a/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh index 51f1a7975b57..bd03fc0b65e0 100755 --- a/lib/tsan/go/buildgo.sh +++ b/lib/tsan/go/buildgo.sh @@ -20,12 +20,13 @@ SRCS=" ../../sanitizer_common/sanitizer_flags.cc ../../sanitizer_common/sanitizer_libc.cc ../../sanitizer_common/sanitizer_printf.cc + ../../sanitizer_common/sanitizer_suppressions.cc ../../sanitizer_common/sanitizer_thread_registry.cc " if [ "`uname -a | grep Linux`" != "" ]; then SUFFIX="linux_amd64" - OSCFLAGS="-fPIC -ffreestanding" + OSCFLAGS="-fPIC -ffreestanding -Wno-maybe-uninitialized -Werror" OSLDFLAGS="-lpthread -fPIC -fpie" SRCS+=" ../rtl/tsan_platform_linux.cc @@ -33,6 +34,7 @@ if [ "`uname -a | grep Linux`" != "" ]; then ../../sanitizer_common/sanitizer_posix_libcdep.cc ../../sanitizer_common/sanitizer_linux.cc ../../sanitizer_common/sanitizer_linux_libcdep.cc + ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc " elif [ "`uname -a | grep Darwin`" != "" ]; then SUFFIX="darwin_amd64" @@ -42,6 +44,7 @@ elif [ "`uname -a | grep Darwin`" != "" ]; then ../rtl/tsan_platform_mac.cc ../../sanitizer_common/sanitizer_posix.cc ../../sanitizer_common/sanitizer_mac.cc + ../../sanitizer_common/sanitizer_posix_libcdep.cc " elif [ "`uname -a | grep MINGW`" != "" ]; then SUFFIX="windows_amd64" @@ -63,7 +66,7 @@ for F in $SRCS; do cat $F >> gotsan.cc done -FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -m64 -Wall -Werror -fno-exceptions -fno-rtti -DTSAN_GO -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 $OSCFLAGS" +FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -m64 -Wall -fno-exceptions -fno-rtti -DTSAN_GO -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 $OSCFLAGS" if [ "$DEBUG" == "" ]; then FLAGS+=" -DTSAN_DEBUG=0 -O3 -fomit-frame-pointer" else diff --git a/lib/tsan/go/test.c b/lib/tsan/go/test.c index 902dfc915582..859b35d348e8 100644 --- a/lib/tsan/go/test.c +++ b/lib/tsan/go/test.c @@ -34,20 +34,32 @@ int __tsan_symbolize(void *pc, char **img, char **rtn, char **file, int *l) { char buf[10]; +void foobar() {} +void barfoo() {} + int main(void) { void *thr0 = 0; __tsan_init(&thr0); __tsan_map_shadow(buf, sizeof(buf) + 4096); - __tsan_func_enter(thr0, &main); + __tsan_func_enter(thr0, (char*)&main + 1); __tsan_malloc(thr0, buf, 10, 0); __tsan_release(thr0, buf); __tsan_release_merge(thr0, buf); void *thr1 = 0; - __tsan_go_start(thr0, &thr1, 0); - __tsan_write(thr1, buf, 0); + __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1); + void *thr2 = 0; + __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_write(thr1, buf, (char*)&barfoo + 1); __tsan_acquire(thr1, buf); + __tsan_func_exit(thr1); + __tsan_func_exit(thr1); __tsan_go_end(thr1); - __tsan_read(thr0, buf, 0); + __tsan_func_enter(thr2, (char*)&foobar + 1); + __tsan_read(thr2, buf, (char*)&barfoo + 1); + __tsan_func_exit(thr2); + __tsan_go_end(thr2); __tsan_free(buf); __tsan_func_exit(thr0); __tsan_fini(); diff --git a/lib/tsan/go/tsan_go.cc b/lib/tsan/go/tsan_go.cc index 957d58211281..df54bb8e216c 100644 --- a/lib/tsan/go/tsan_go.cc +++ b/lib/tsan/go/tsan_go.cc @@ -116,12 +116,14 @@ void __tsan_write(ThreadState *thr, void *addr, void *pc) { void __tsan_read_range(ThreadState *thr, void *addr, uptr size, uptr step, void *pc) { - MemoryAccessRangeStep(thr, (uptr)pc, (uptr)addr, size, step, false); + (void)step; + MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, false); } void __tsan_write_range(ThreadState *thr, void *addr, uptr size, uptr step, void *pc) { - MemoryAccessRangeStep(thr, (uptr)pc, (uptr)addr, size, step, true); + (void)step; + MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, true); } void __tsan_func_enter(ThreadState *thr, void *pc) { @@ -184,40 +186,6 @@ void __tsan_finalizer_goroutine(ThreadState *thr) { AcquireGlobal(thr, 0); } -#if SANITIZER_WINDOWS -// MinGW gcc emits calls to the function. -void ___chkstk_ms(void) { -// The implementation must be along the lines of: -// .code64 -// PUBLIC ___chkstk_ms -// //cfi_startproc() -// ___chkstk_ms: -// push rcx -// //cfi_push(%rcx) -// push rax -// //cfi_push(%rax) -// cmp rax, PAGE_SIZE -// lea rcx, [rsp + 24] -// jb l_LessThanAPage -// .l_MoreThanAPage: -// sub rcx, PAGE_SIZE -// or rcx, 0 -// sub rax, PAGE_SIZE -// cmp rax, PAGE_SIZE -// ja l_MoreThanAPage -// .l_LessThanAPage: -// sub rcx, rax -// or [rcx], 0 -// pop rax -// //cfi_pop(%rax) -// pop rcx -// //cfi_pop(%rcx) -// ret -// //cfi_endproc() -// END -} -#endif - } // extern "C" } // namespace __tsan diff --git a/lib/tsan/lit_tests/CMakeLists.txt b/lib/tsan/lit_tests/CMakeLists.txt index 53e5015d1bc4..1f2fbf98e080 100644 --- a/lib/tsan/lit_tests/CMakeLists.txt +++ b/lib/tsan/lit_tests/CMakeLists.txt @@ -8,7 +8,7 @@ configure_lit_site_cfg( ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg ) -if(COMPILER_RT_CAN_EXECUTE_TESTS) +if(COMPILER_RT_CAN_EXECUTE_TESTS AND CAN_TARGET_x86_64) # Run TSan output tests only if we're sure we can produce working binaries. set(TSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS} @@ -25,7 +25,7 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS) DEPENDS ${TSAN_TEST_DEPS} ) set_target_properties(check-tsan PROPERTIES FOLDER "TSan unittests") -elseif(LLVM_INCLUDE_TESTS) +elseif(LLVM_INCLUDE_TESTS AND CAN_TARGET_x86_64) # Otherwise run only TSan unit tests (they are linked using the # host compiler). add_lit_testsuite(check-tsan "Running ThreadSanitizer tests" diff --git a/lib/tsan/lit_tests/Unit/lit.cfg b/lib/tsan/lit_tests/Unit/lit.cfg index 0a0dbbfa5495..36585df1c671 100644 --- a/lib/tsan/lit_tests/Unit/lit.cfg +++ b/lib/tsan/lit_tests/Unit/lit.cfg @@ -5,17 +5,12 @@ import os def get_required_attr(config, attr_name): attr_value = getattr(config, attr_name, None) if not attr_value: - lit.fatal("No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) return attr_value -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') -compiler_rt_lit_unit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.unit.cfg") -lit.load_config(config, compiler_rt_lit_unit_cfg) - # Setup config name. config.name = 'ThreadSanitizer-Unit' @@ -26,11 +21,3 @@ config.test_exec_root = os.path.join(llvm_obj_root, "projects", "compiler-rt", "lib", "tsan", "tests") config.test_source_root = config.test_exec_root - -# Get path to external LLVM symbolizer to run ThreadSanitizer unit tests. -llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) -if llvm_tools_dir: - llvm_symbolizer_path = os.path.join(llvm_tools_dir, "llvm-symbolizer") - config.environment['TSAN_OPTIONS'] = ("external_symbolizer_path=" + - llvm_symbolizer_path) - diff --git a/lib/tsan/lit_tests/Unit/lit.site.cfg.in b/lib/tsan/lit_tests/Unit/lit.site.cfg.in index 6eedc2180876..3701a2cad74c 100644 --- a/lib/tsan/lit_tests/Unit/lit.site.cfg.in +++ b/lib/tsan/lit_tests/Unit/lit.site.cfg.in @@ -1,20 +1,8 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! -config.llvm_obj_root = "@LLVM_BINARY_DIR@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" -config.llvm_build_mode = "@LLVM_BUILD_MODE@" +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") -# LLVM tools dir can be passed in lit parameters, so try to -# apply substitution. -try: - config.llvm_tools_dir = config.llvm_tools_dir % lit.params - config.llvm_build_mode = config.llvm_build_mode % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) - -# Let the main config do the real work. -lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/Unit/lit.cfg") +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/Unit/lit.cfg") diff --git a/lib/tsan/lit_tests/allocator_returns_null.cc b/lib/tsan/lit_tests/allocator_returns_null.cc new file mode 100644 index 000000000000..4b5eb5504c27 --- /dev/null +++ b/lib/tsan/lit_tests/allocator_returns_null.cc @@ -0,0 +1,64 @@ +// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// By default (allocator_may_return_null=0) the process shoudl crash. +// With allocator_may_return_null=1 the allocator should return 0. +// +// RUN: %clangxx_tsan -O0 %s -o %t +// RUN: not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <limits> +int main(int argc, char **argv) { + volatile size_t size = std::numeric_limits<size_t>::max() - 10000; + assert(argc == 2); + char *x = 0; + if (!strcmp(argv[1], "malloc")) { + fprintf(stderr, "malloc:\n"); + x = (char*)malloc(size); + } + if (!strcmp(argv[1], "calloc")) { + fprintf(stderr, "calloc:\n"); + x = (char*)calloc(size / 4, 4); + } + + if (!strcmp(argv[1], "calloc-overflow")) { + fprintf(stderr, "calloc-overflow:\n"); + volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = (char*)calloc(kArraySize, kArraySize2); + } + + if (!strcmp(argv[1], "realloc")) { + fprintf(stderr, "realloc:\n"); + x = (char*)realloc(0, size); + } + if (!strcmp(argv[1], "realloc-after-malloc")) { + fprintf(stderr, "realloc-after-malloc:\n"); + char *t = (char*)malloc(100); + *t = 42; + x = (char*)realloc(t, size); + assert(*t == 42); + } + fprintf(stderr, "x: %p\n", x); + return x != 0; +} +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: ThreadSanitizer's allocator is terminating the process + diff --git a/lib/tsan/lit_tests/atomic_free.cc b/lib/tsan/lit_tests/atomic_free.cc index ba9bd5ac4aed..87d559362af4 100644 --- a/lib/tsan/lit_tests/atomic_free.cc +++ b/lib/tsan/lit_tests/atomic_free.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/atomic_free2.cc b/lib/tsan/lit_tests/atomic_free2.cc index 5517bf7ce902..961ff38c843b 100644 --- a/lib/tsan/lit_tests/atomic_free2.cc +++ b/lib/tsan/lit_tests/atomic_free2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/atomic_race.cc b/lib/tsan/lit_tests/atomic_race.cc index 360b81238889..0dfe4d93df6e 100644 --- a/lib/tsan/lit_tests/atomic_race.cc +++ b/lib/tsan/lit_tests/atomic_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/atomic_stack.cc b/lib/tsan/lit_tests/atomic_stack.cc index 50f6a8a889ca..841f74b891ab 100644 --- a/lib/tsan/lit_tests/atomic_stack.cc +++ b/lib/tsan/lit_tests/atomic_stack.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/cond.c b/lib/tsan/lit_tests/cond.c new file mode 100644 index 000000000000..52c87a413eb7 --- /dev/null +++ b/lib/tsan/lit_tests/cond.c @@ -0,0 +1,53 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK-NOT: ThreadSanitizer WARNING: double lock +// CHECK-NOT: ThreadSanitizer WARNING: mutex unlock by another thread +// CHECK: OK + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +pthread_mutex_t m; +pthread_cond_t c; +int x; + +void *thr1(void *p) { + int i; + + for (i = 0; i < 10; i += 2) { + pthread_mutex_lock(&m); + while (x != i) + pthread_cond_wait(&c, &m); + x = i + 1; + pthread_cond_signal(&c); + pthread_mutex_unlock(&m); + } + return 0; +} + +void *thr2(void *p) { + int i; + + for (i = 1; i < 10; i += 2) { + pthread_mutex_lock(&m); + while (x != i) + pthread_cond_wait(&c, &m); + x = i + 1; + pthread_mutex_unlock(&m); + pthread_cond_broadcast(&c); + } + return 0; +} + +int main() { + pthread_t th1, th2; + + pthread_mutex_init(&m, 0); + pthread_cond_init(&c, 0); + pthread_create(&th1, 0, thr1, 0); + pthread_create(&th2, 0, thr2, 0); + pthread_join(th1, 0); + pthread_join(th2, 0); + fprintf(stderr, "OK\n"); +} diff --git a/lib/tsan/lit_tests/cond_race.cc b/lib/tsan/lit_tests/cond_race.cc new file mode 100644 index 000000000000..1e2acb243279 --- /dev/null +++ b/lib/tsan/lit_tests/cond_race.cc @@ -0,0 +1,36 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// CHECK: ThreadSanitizer: data race +// CHECK: pthread_cond_signal + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +struct Ctx { + pthread_mutex_t m; + pthread_cond_t c; + bool done; +}; + +void *thr(void *p) { + Ctx *c = (Ctx*)p; + pthread_mutex_lock(&c->m); + c->done = true; + pthread_mutex_unlock(&c->m); + pthread_cond_signal(&c->c); + return 0; +} + +int main() { + Ctx *c = new Ctx(); + pthread_mutex_init(&c->m, 0); + pthread_cond_init(&c->c, 0); + pthread_t th; + pthread_create(&th, 0, thr, c); + pthread_mutex_lock(&c->m); + while (!c->done) + pthread_cond_wait(&c->c, &c->m); + pthread_mutex_unlock(&c->m); + delete c; + pthread_join(th, 0); +} diff --git a/lib/tsan/lit_tests/cond_version.c b/lib/tsan/lit_tests/cond_version.c new file mode 100644 index 000000000000..1f966bfacb8d --- /dev/null +++ b/lib/tsan/lit_tests/cond_version.c @@ -0,0 +1,44 @@ +// RUN: %clang_tsan -O1 %s -o %t -lrt && %t 2>&1 | FileCheck %s +// Test that pthread_cond is properly intercepted, +// previously there were issues with versioned symbols. +// CHECK: OK + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <time.h> +#include <errno.h> + +int main() { + typedef unsigned long long u64; + pthread_mutex_t m; + pthread_cond_t c; + pthread_condattr_t at; + struct timespec ts0, ts1, ts2; + int res; + u64 sleep; + + pthread_mutex_init(&m, 0); + pthread_condattr_init(&at); + pthread_condattr_setclock(&at, CLOCK_MONOTONIC); + pthread_cond_init(&c, &at); + + clock_gettime(CLOCK_MONOTONIC, &ts0); + ts1 = ts0; + ts1.tv_sec += 2; + + pthread_mutex_lock(&m); + do { + res = pthread_cond_timedwait(&c, &m, &ts1); + } while (res == 0); + pthread_mutex_unlock(&m); + + clock_gettime(CLOCK_MONOTONIC, &ts2); + sleep = (u64)ts2.tv_sec * 1000000000 + ts2.tv_nsec - + ((u64)ts0.tv_sec * 1000000000 + ts0.tv_nsec); + if (res != ETIMEDOUT) + exit(printf("bad return value %d, want %d\n", res, ETIMEDOUT)); + if (sleep < 1000000000) + exit(printf("bad sleep duration %lluns, want %dns\n", sleep, 1000000000)); + fprintf(stderr, "OK\n"); +} diff --git a/lib/tsan/lit_tests/deep_stack1.cc b/lib/tsan/lit_tests/deep_stack1.cc new file mode 100644 index 000000000000..3048aa8745bb --- /dev/null +++ b/lib/tsan/lit_tests/deep_stack1.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_tsan -O1 %s -o %t -DORDER1 && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t -DORDER2 && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +volatile int X; +volatile int N; +void (*volatile F)(); + +static void foo() { + if (--N == 0) + X = 42; + else + F(); +} + +void *Thread(void *p) { +#ifdef ORDER1 + sleep(1); +#endif + F(); + return 0; +} + +int main() { + N = 50000; + F = foo; + pthread_t t; + pthread_attr_t a; + pthread_attr_init(&a); + pthread_attr_setstacksize(&a, N * 256 + (1 << 20)); + pthread_create(&t, &a, Thread, 0); +#ifdef ORDER2 + sleep(1); +#endif + X = 43; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #100 foo +// We must output suffucuently large stack (at least 100 frames) + diff --git a/lib/tsan/lit_tests/default_options.cc b/lib/tsan/lit_tests/default_options.cc new file mode 100644 index 000000000000..62c6c028f9e4 --- /dev/null +++ b/lib/tsan/lit_tests/default_options.cc @@ -0,0 +1,32 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +extern "C" const char *__tsan_default_options() { + return "report_bugs=0"; +} + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/lib/tsan/lit_tests/fd_close_norace2.cc b/lib/tsan/lit_tests/fd_close_norace2.cc new file mode 100644 index 000000000000..b42b334a27c0 --- /dev/null +++ b/lib/tsan/lit_tests/fd_close_norace2.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int pipes[2]; + +void *Thread(void *x) { + // wait for shutown signal + while (read(pipes[0], &x, 1) != 1) { + } + close(pipes[0]); + close(pipes[1]); + return 0; +} + +int main() { + if (pipe(pipes)) + return 1; + pthread_t t; + pthread_create(&t, 0, Thread, 0); + // send shutdown signal + while (write(pipes[1], &t, 1) != 1) { + } + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/lib/tsan/lit_tests/fd_location.cc b/lib/tsan/lit_tests/fd_location.cc index 35f9aabb0377..2b1e9c56e361 100644 --- a/lib/tsan/lit_tests/fd_location.cc +++ b/lib/tsan/lit_tests/fd_location.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/fd_pipe_race.cc b/lib/tsan/lit_tests/fd_pipe_race.cc index dfdb7795aae6..4dd2b77861ab 100644 --- a/lib/tsan/lit_tests/fd_pipe_race.cc +++ b/lib/tsan/lit_tests/fd_pipe_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/fd_stdout_race.cc b/lib/tsan/lit_tests/fd_stdout_race.cc index 6581fc503a1b..4b512bb78874 100644 --- a/lib/tsan/lit_tests/fd_stdout_race.cc +++ b/lib/tsan/lit_tests/fd_stdout_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/free_race.c b/lib/tsan/lit_tests/free_race.c index ff71a4d2116b..d1db9fece90a 100644 --- a/lib/tsan/lit_tests/free_race.c +++ b/lib/tsan/lit_tests/free_race.c @@ -1,4 +1,7 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOZUPP +// RUN: TSAN_OPTIONS="suppressions=%s.supp print_suppressions=1" %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUPP + #include <pthread.h> #include <stdlib.h> #include <stdio.h> @@ -34,11 +37,13 @@ int main() { return 0; } -// CHECK: WARNING: ThreadSanitizer: heap-use-after-free -// CHECK: Write of size 4 at {{.*}} by main thread{{.*}}: -// CHECK: #0 Thread2 -// CHECK: #1 main -// CHECK: Previous write of size 8 at {{.*}} by thread T1{{.*}}: -// CHECK: #0 free -// CHECK: #{{(1|2)}} Thread1 -// CHECK: SUMMARY: ThreadSanitizer: heap-use-after-free{{.*}}Thread2 +// CHECK-NOZUPP: WARNING: ThreadSanitizer: heap-use-after-free +// CHECK-NOZUPP: Write of size 4 at {{.*}} by main thread{{.*}}: +// CHECK-NOZUPP: #0 Thread2 +// CHECK-NOZUPP: #1 main +// CHECK-NOZUPP: Previous write of size 8 at {{.*}} by thread T1{{.*}}: +// CHECK-NOZUPP: #0 free +// CHECK-NOZUPP: #{{(1|2)}} Thread1 +// CHECK-NOZUPP: SUMMARY: ThreadSanitizer: heap-use-after-free{{.*}}Thread2 +// CHECK-SUPP: ThreadSanitizer: Matched 1 suppressions +// CHECK-SUPP: 1 race:^Thread2$ diff --git a/lib/tsan/lit_tests/free_race.c.supp b/lib/tsan/lit_tests/free_race.c.supp new file mode 100644 index 000000000000..f5d6a4969a41 --- /dev/null +++ b/lib/tsan/lit_tests/free_race.c.supp @@ -0,0 +1,2 @@ +# Suppression for a use-after-free in free_race.c +race:^Thread2$ diff --git a/lib/tsan/lit_tests/free_race2.c b/lib/tsan/lit_tests/free_race2.c index f20774b2d8d4..2b9a41927a47 100644 --- a/lib/tsan/lit_tests/free_race2.c +++ b/lib/tsan/lit_tests/free_race2.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <stdlib.h> void __attribute__((noinline)) foo(int *mem) { diff --git a/lib/tsan/lit_tests/global_race.cc b/lib/tsan/lit_tests/global_race.cc index 0892d07da2cb..ac2016155575 100644 --- a/lib/tsan/lit_tests/global_race.cc +++ b/lib/tsan/lit_tests/global_race.cc @@ -1,25 +1,42 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stddef.h> int GlobalData[10]; +int x; +namespace XXX { + struct YYY { + static int ZZZ[10]; + }; + int YYY::ZZZ[10]; +} void *Thread(void *a) { GlobalData[2] = 42; + x = 1; + XXX::YYY::ZZZ[0] = 1; return 0; } int main() { fprintf(stderr, "addr=%p\n", GlobalData); + fprintf(stderr, "addr2=%p\n", &x); + fprintf(stderr, "addr3=%p\n", XXX::YYY::ZZZ); pthread_t t; pthread_create(&t, 0, Thread, 0); GlobalData[2] = 43; + x = 0; + XXX::YYY::ZZZ[0] = 0; pthread_join(t, 0); } // CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: addr2=[[ADDR2:0x[0-9,a-f]+]] +// CHECK: addr3=[[ADDR3:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'GlobalData' of size 40 at [[ADDR]] ({{.*}}+0x{{[0-9,a-f]+}}) +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'x' of size 4 at [[ADDR2]] ({{.*}}+0x{{[0-9,a-f]+}}) // CHECK: WARNING: ThreadSanitizer: data race -// Requires llvm-symbolizer, so disabled for now. -// CHECK0: Location is global 'GlobalData' of size 40 at [[ADDR]] -// CHECK0: (global_race.cc.exe+0x[0-9,a-f]+) +// CHECK: Location is global 'XXX::YYY::ZZZ' of size 40 at [[ADDR3]] ({{.*}}+0x{{[0-9,a-f]+}}) diff --git a/lib/tsan/lit_tests/halt_on_error.cc b/lib/tsan/lit_tests/halt_on_error.cc new file mode 100644 index 000000000000..fddaffff29aa --- /dev/null +++ b/lib/tsan/lit_tests/halt_on_error.cc @@ -0,0 +1,25 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS halt_on_error=1" not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +int X; + +void *Thread(void *x) { + X = 42; + return 0; +} + +int main() { + fprintf(stderr, "BEFORE\n"); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + X = 43; + pthread_join(t, 0); + fprintf(stderr, "AFTER\n"); + return 0; +} + +// CHECK: BEFORE +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: AFTER + diff --git a/lib/tsan/lit_tests/heap_race.cc b/lib/tsan/lit_tests/heap_race.cc index 297f8dbdec7d..cc2c1fee532b 100644 --- a/lib/tsan/lit_tests/heap_race.cc +++ b/lib/tsan/lit_tests/heap_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/ignore_free.cc b/lib/tsan/lit_tests/ignore_free.cc new file mode 100644 index 000000000000..60369cc1baa5 --- /dev/null +++ b/lib/tsan/lit_tests/ignore_free.cc @@ -0,0 +1,35 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" { +void AnnotateIgnoreReadsBegin(const char *f, int l); +void AnnotateIgnoreReadsEnd(const char *f, int l); +void AnnotateIgnoreWritesBegin(const char *f, int l); +void AnnotateIgnoreWritesEnd(const char *f, int l); +} + +void *Thread(void *p) { + *(int*)p = 42; + return 0; +} + +int main() { + int *p = new int(0); + pthread_t t; + pthread_create(&t, 0, Thread, p); + sleep(1); + AnnotateIgnoreReadsBegin(__FILE__, __LINE__); + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + free(p); + AnnotateIgnoreReadsEnd(__FILE__, __LINE__); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + pthread_join(t, 0); + fprintf(stderr, "OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/lib/tsan/lit_tests/ignore_lib0.cc b/lib/tsan/lit_tests/ignore_lib0.cc new file mode 100644 index 000000000000..ea0f061e609d --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib0.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib0.so +// RUN: %clangxx_tsan -O1 %s -L%T -lignore_lib0 -o %t +// RUN: echo running w/o suppressions: +// RUN: LD_LIBRARY_PATH=%T not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP +// RUN: echo running with suppressions: +// RUN: LD_LIBRARY_PATH=%T TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP + +// Tests that interceptors coming from a library specified in called_from_lib +// suppression are ignored. + +#ifndef LIB + +extern "C" void libfunc(); + +int main() { + libfunc(); +} + +#else // #ifdef LIB + +#include "ignore_lib_lib.h" + +#endif // #ifdef LIB + +// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race +// CHECK-NOSUPP: OK + +// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race +// CHECK-WITHSUPP: OK + diff --git a/lib/tsan/lit_tests/ignore_lib0.cc.supp b/lib/tsan/lit_tests/ignore_lib0.cc.supp new file mode 100644 index 000000000000..7728c926b7de --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib0.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:/libignore_lib0.so + diff --git a/lib/tsan/lit_tests/ignore_lib1.cc b/lib/tsan/lit_tests/ignore_lib1.cc new file mode 100644 index 000000000000..c4f2e7344135 --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib1.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib1.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: echo running w/o suppressions: +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP +// RUN: echo running with suppressions: +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP + +// Tests that interceptors coming from a dynamically loaded library specified +// in called_from_lib suppression are ignored. + +#ifndef LIB + +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <libgen.h> +#include <string> + +int main(int argc, char **argv) { + std::string lib = std::string(dirname(argv[0])) + "/libignore_lib1.so"; + void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW); + if (h == 0) + exit(printf("failed to load the library (%d)\n", errno)); + void (*f)() = (void(*)())dlsym(h, "libfunc"); + if (f == 0) + exit(printf("failed to find the func (%d)\n", errno)); + f(); +} + +#else // #ifdef LIB + +#include "ignore_lib_lib.h" + +#endif // #ifdef LIB + +// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race +// CHECK-NOSUPP: OK + +// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race +// CHECK-WITHSUPP: OK + diff --git a/lib/tsan/lit_tests/ignore_lib1.cc.supp b/lib/tsan/lit_tests/ignore_lib1.cc.supp new file mode 100644 index 000000000000..9f4119ec0bc4 --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib1.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:/libignore_lib1.so$ + diff --git a/lib/tsan/lit_tests/ignore_lib2.cc b/lib/tsan/lit_tests/ignore_lib2.cc new file mode 100644 index 000000000000..97f9419e4d89 --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib2.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_0.so +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_1.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" not %t 2>&1 | FileCheck %s + +// Tests that called_from_lib suppression matched against 2 libraries +// causes program crash (this is not supported). + +#ifndef LIB + +#include <dlfcn.h> +#include <stdio.h> +#include <libgen.h> +#include <string> + +int main(int argc, char **argv) { + std::string lib0 = std::string(dirname(argv[0])) + "/libignore_lib2_0.so"; + std::string lib1 = std::string(dirname(argv[0])) + "/libignore_lib2_1.so"; + dlopen(lib0.c_str(), RTLD_GLOBAL | RTLD_NOW); + dlopen(lib1.c_str(), RTLD_GLOBAL | RTLD_NOW); + fprintf(stderr, "OK\n"); +} + +#else // #ifdef LIB + +extern "C" void libfunc() { +} + +#endif // #ifdef LIB + +// CHECK: ThreadSanitizer: called_from_lib suppression 'ignore_lib2' is matched against 2 libraries +// CHECK-NOT: OK + diff --git a/lib/tsan/lit_tests/ignore_lib2.cc.supp b/lib/tsan/lit_tests/ignore_lib2.cc.supp new file mode 100644 index 000000000000..1419c71c67ef --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib2.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:ignore_lib2 + diff --git a/lib/tsan/lit_tests/ignore_lib3.cc b/lib/tsan/lit_tests/ignore_lib3.cc new file mode 100644 index 000000000000..8f237fcc81fd --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib3.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib3.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" not %t 2>&1 | FileCheck %s + +// Tests that unloading of a library matched against called_from_lib suppression +// causes program crash (this is not supported). + +#ifndef LIB + +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <libgen.h> +#include <string> + +int main(int argc, char **argv) { + std::string lib = std::string(dirname(argv[0])) + "/libignore_lib3.so"; + void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW); + dlclose(h); + fprintf(stderr, "OK\n"); +} + +#else // #ifdef LIB + +extern "C" void libfunc() { +} + +#endif // #ifdef LIB + +// CHECK: ThreadSanitizer: library {{.*}} that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded +// CHECK-NOT: OK + diff --git a/lib/tsan/lit_tests/ignore_lib3.cc.supp b/lib/tsan/lit_tests/ignore_lib3.cc.supp new file mode 100644 index 000000000000..975dbcef99fe --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib3.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:ignore_lib3.so + diff --git a/lib/tsan/lit_tests/ignore_lib_lib.h b/lib/tsan/lit_tests/ignore_lib_lib.h new file mode 100644 index 000000000000..2bfe84dfc0ec --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib_lib.h @@ -0,0 +1,25 @@ +#include <pthread.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +void *volatile mem; +volatile int len; + +void *Thread(void *p) { + while ((p = __atomic_load_n(&mem, __ATOMIC_ACQUIRE)) == 0) + usleep(100); + memset(p, 0, len); + return 0; +} + +extern "C" void libfunc() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + len = 10; + __atomic_store_n(&mem, malloc(len), __ATOMIC_RELEASE); + pthread_join(t, 0); + free(mem); + fprintf(stderr, "OK\n"); +} diff --git a/lib/tsan/lit_tests/ignore_malloc.cc b/lib/tsan/lit_tests/ignore_malloc.cc new file mode 100644 index 000000000000..63bd4241b59e --- /dev/null +++ b/lib/tsan/lit_tests/ignore_malloc.cc @@ -0,0 +1,38 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" { +void AnnotateIgnoreReadsBegin(const char *f, int l); +void AnnotateIgnoreReadsEnd(const char *f, int l); +void AnnotateIgnoreWritesBegin(const char *f, int l); +void AnnotateIgnoreWritesEnd(const char *f, int l); +} + +int *g; + +void *Thread(void *a) { + int *p = 0; + while ((p = __atomic_load_n(&g, __ATOMIC_RELAXED)) == 0) + usleep(100); + *p = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + int *p = new int(0); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + __atomic_store_n(&g, p, __ATOMIC_RELAXED); + pthread_join(t, 0); + delete p; + fprintf(stderr, "OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/lib/tsan/lit_tests/ignore_sync.cc b/lib/tsan/lit_tests/ignore_sync.cc new file mode 100644 index 000000000000..67f2d906d9c7 --- /dev/null +++ b/lib/tsan/lit_tests/ignore_sync.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +extern "C" void AnnotateIgnoreSyncBegin(const char*, int); +extern "C" void AnnotateIgnoreSyncEnd(const char*, int); + +int Global; +pthread_mutex_t Mutex = PTHREAD_MUTEX_INITIALIZER; + +void *Thread(void *x) { + AnnotateIgnoreSyncBegin(0, 0); + pthread_mutex_lock(&Mutex); + Global++; + pthread_mutex_unlock(&Mutex); + AnnotateIgnoreSyncEnd(0, 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_mutex_lock(&Mutex); + Global++; + pthread_mutex_unlock(&Mutex); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race + diff --git a/lib/tsan/lit_tests/inlined_memcpy_race.cc b/lib/tsan/lit_tests/inlined_memcpy_race.cc index 6efe5a956e9d..5dda36e4b9e7 100644 --- a/lib/tsan/lit_tests/inlined_memcpy_race.cc +++ b/lib/tsan/lit_tests/inlined_memcpy_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/java.h b/lib/tsan/lit_tests/java.h index 04094197edb7..7aa0bca32cec 100644 --- a/lib/tsan/lit_tests/java.h +++ b/lib/tsan/lit_tests/java.h @@ -5,6 +5,7 @@ extern "C" { typedef unsigned long jptr; // NOLINT +void __tsan_java_preinit(const char *libjvm_path); void __tsan_java_init(jptr heap_begin, jptr heap_size); int __tsan_java_fini(); void __tsan_java_alloc(jptr ptr, jptr size); diff --git a/lib/tsan/lit_tests/java_lock_rec_race.cc b/lib/tsan/lit_tests/java_lock_rec_race.cc index 61626aaddc0d..a868e260c86d 100644 --- a/lib/tsan/lit_tests/java_lock_rec_race.cc +++ b/lib/tsan/lit_tests/java_lock_rec_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include "java.h" #include <unistd.h> diff --git a/lib/tsan/lit_tests/java_race.cc b/lib/tsan/lit_tests/java_race.cc index 722bb6e8d09c..4841a7db0a9c 100644 --- a/lib/tsan/lit_tests/java_race.cc +++ b/lib/tsan/lit_tests/java_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include "java.h" void *Thread(void *p) { diff --git a/lib/tsan/lit_tests/java_race_move.cc b/lib/tsan/lit_tests/java_race_move.cc index bb63ea985c58..6da8a106483c 100644 --- a/lib/tsan/lit_tests/java_race_move.cc +++ b/lib/tsan/lit_tests/java_race_move.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include "java.h" jptr varaddr; diff --git a/lib/tsan/lit_tests/lit.cfg b/lib/tsan/lit_tests/lit.cfg index d483d2fcbdc6..c4193639f493 100644 --- a/lib/tsan/lit_tests/lit.cfg +++ b/lib/tsan/lit_tests/lit.cfg @@ -2,12 +2,15 @@ import os +import lit.util + def get_required_attr(config, attr_name): attr_value = getattr(config, attr_name, None) if not attr_value: - lit.fatal("No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) return attr_value # Setup config name. @@ -17,9 +20,9 @@ config.name = 'ThreadSanitizer' config.test_source_root = os.path.dirname(__file__) def DisplayNoConfigMessage(): - lit.fatal("No site specific configuration available! " + - "Try running your test from the build tree or running " + - "make check-tsan") + lit_config.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-tsan") # Figure out LLVM source root. llvm_src_root = getattr(config, 'llvm_src_root', None) @@ -27,9 +30,9 @@ if llvm_src_root is None: # We probably haven't loaded the site-specific configuration: the user # is likely trying to run a test file directly, and the site configuration # wasn't created by the build system. - tsan_site_cfg = lit.params.get('tsan_site_config', None) + tsan_site_cfg = lit_config.params.get('tsan_site_config', None) if (tsan_site_cfg) and (os.path.exists(tsan_site_cfg)): - lit.load_config(config, tsan_site_cfg) + lit_config.load_config(config, tsan_site_cfg) raise SystemExit # Try to guess the location of site-specific configuration using llvm-config @@ -45,25 +48,11 @@ if llvm_src_root is None: if (not tsan_site_cfg) or (not os.path.exists(tsan_site_cfg)): DisplayNoConfigMessage() - lit.load_config(config, tsan_site_cfg) + lit_config.load_config(config, tsan_site_cfg) raise SystemExit -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') -compiler_rt_lit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.cfg") -if (not compiler_rt_lit_cfg) or (not os.path.exists(compiler_rt_lit_cfg)): - lit.fatal("Can't find common compiler-rt lit config at: %r" - % compiler_rt_lit_cfg) -lit.load_config(config, compiler_rt_lit_cfg) - # Setup environment variables for running ThreadSanitizer. tsan_options = "atexit_sleep_ms=0" -# Get path to external LLVM symbolizer to run ThreadSanitizer output tests. -llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) -if llvm_tools_dir: - llvm_symbolizer_path = os.path.join(llvm_tools_dir, "llvm-symbolizer") - tsan_options += " " + "external_symbolizer_path=" + llvm_symbolizer_path config.environment['TSAN_OPTIONS'] = tsan_options @@ -73,8 +62,9 @@ clang_tsan_cflags = ("-fsanitize=thread " + "-g " + "-Wall " + "-lpthread " - + "-ldl ") -clang_tsan_cxxflags = "-ccc-cxx " + clang_tsan_cflags + + "-ldl " + + "-m64 ") +clang_tsan_cxxflags = "--driver-mode=g++ " + clang_tsan_cflags config.substitutions.append( ("%clangxx_tsan ", (" " + config.clang + " " + clang_tsan_cxxflags + " ")) ) config.substitutions.append( ("%clang_tsan ", (" " + config.clang + " " + diff --git a/lib/tsan/lit_tests/lit.site.cfg.in b/lib/tsan/lit_tests/lit.site.cfg.in index 07b521af061f..b0e427446eaa 100644 --- a/lib/tsan/lit_tests/lit.site.cfg.in +++ b/lib/tsan/lit_tests/lit.site.cfg.in @@ -1,20 +1,8 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! -config.clang = "@LLVM_BINARY_DIR@/bin/clang" -config.host_os = "@HOST_OS@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" -config.target_triple = "@TARGET_TRIPLE@" +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") -# LLVM tools dir can be passed in lit parameters, so try to -# apply substitution. -try: - config.llvm_tools_dir = config.llvm_tools_dir % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) - -# Let the main config do the real work. -lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/lib/tsan/lit_tests/load_shared_lib.cc b/lib/tsan/lit_tests/load_shared_lib.cc index dd6fa0964f4a..d60cd5700a8a 100644 --- a/lib/tsan/lit_tests/load_shared_lib.cc +++ b/lib/tsan/lit_tests/load_shared_lib.cc @@ -4,7 +4,7 @@ // RUN: %clangxx_tsan -O1 %p/SharedLibs/load_shared_lib-so.cc \ // RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <dlfcn.h> #include <pthread.h> diff --git a/lib/tsan/lit_tests/longjmp3.cc b/lib/tsan/lit_tests/longjmp3.cc index 87fabd0b3be2..ae2cfd05fe1a 100644 --- a/lib/tsan/lit_tests/longjmp3.cc +++ b/lib/tsan/lit_tests/longjmp3.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/lib/tsan/lit_tests/longjmp4.cc b/lib/tsan/lit_tests/longjmp4.cc index a8764dda5a6b..6b0526ef3a66 100644 --- a/lib/tsan/lit_tests/longjmp4.cc +++ b/lib/tsan/lit_tests/longjmp4.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/lib/tsan/lit_tests/malloc_overflow.cc b/lib/tsan/lit_tests/malloc_overflow.cc index 19423c5f93f1..afbebc8bec44 100644 --- a/lib/tsan/lit_tests/malloc_overflow.cc +++ b/lib/tsan/lit_tests/malloc_overflow.cc @@ -1,4 +1,5 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS=allocator_may_return_null=1 %t 2>&1 | FileCheck %s #include <stdio.h> #include <stdlib.h> diff --git a/lib/tsan/lit_tests/malloc_stack.cc b/lib/tsan/lit_tests/malloc_stack.cc index c185623ff5ca..3603497ef311 100644 --- a/lib/tsan/lit_tests/malloc_stack.cc +++ b/lib/tsan/lit_tests/malloc_stack.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/memcpy_race.cc b/lib/tsan/lit_tests/memcpy_race.cc index 857728ba0540..8f39113674d6 100644 --- a/lib/tsan/lit_tests/memcpy_race.cc +++ b/lib/tsan/lit_tests/memcpy_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/mop_with_offset.cc b/lib/tsan/lit_tests/mop_with_offset.cc index 0c11ef6b9187..2b6a4ff50aaf 100644 --- a/lib/tsan/lit_tests/mop_with_offset.cc +++ b/lib/tsan/lit_tests/mop_with_offset.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/mop_with_offset2.cc b/lib/tsan/lit_tests/mop_with_offset2.cc index ee0d64a0afbf..037c4db5f524 100644 --- a/lib/tsan/lit_tests/mop_with_offset2.cc +++ b/lib/tsan/lit_tests/mop_with_offset2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/mutex_destroy_locked.cc b/lib/tsan/lit_tests/mutex_destroy_locked.cc index 27a04248b172..9b020d31b94c 100644 --- a/lib/tsan/lit_tests/mutex_destroy_locked.cc +++ b/lib/tsan/lit_tests/mutex_destroy_locked.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutex_robust.cc b/lib/tsan/lit_tests/mutex_robust.cc new file mode 100644 index 000000000000..b826616076ae --- /dev/null +++ b/lib/tsan/lit_tests/mutex_robust.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +pthread_mutex_t m; + +void *thr(void *p) { + pthread_mutex_lock(&m); + return 0; +} + +int main() { + pthread_mutexattr_t a; + pthread_mutexattr_init(&a); + pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST); + pthread_mutex_init(&m, &a); + pthread_t th; + pthread_create(&th, 0, thr, 0); + sleep(1); + if (pthread_mutex_lock(&m) != EOWNERDEAD) { + fprintf(stderr, "not EOWNERDEAD\n"); + exit(1); + } + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); +} + +// This is a correct code, and tsan must not bark. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK-NOT: EOWNERDEAD +// CHECK: DONE +// CHECK-NOT: WARNING: ThreadSanitizer + diff --git a/lib/tsan/lit_tests/mutex_robust2.cc b/lib/tsan/lit_tests/mutex_robust2.cc new file mode 100644 index 000000000000..5bd7ff682d1b --- /dev/null +++ b/lib/tsan/lit_tests/mutex_robust2.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +pthread_mutex_t m; +int x; + +void *thr(void *p) { + pthread_mutex_lock(&m); + x = 42; + return 0; +} + +int main() { + pthread_mutexattr_t a; + pthread_mutexattr_init(&a); + pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST); + pthread_mutex_init(&m, &a); + pthread_t th; + pthread_create(&th, 0, thr, 0); + sleep(1); + if (pthread_mutex_trylock(&m) != EOWNERDEAD) { + fprintf(stderr, "not EOWNERDEAD\n"); + exit(1); + } + x = 43; + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); +} + +// This is a false positive, tsan must not bark at the data race. +// But currently it does. +// CHECK-NOT: WARNING: ThreadSanitizer WARNING: double lock of mutex +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: EOWNERDEAD +// CHECK: DONE +// CHECK-NOT: WARNING: ThreadSanitizer + diff --git a/lib/tsan/lit_tests/mutexset1.cc b/lib/tsan/lit_tests/mutexset1.cc index f32a770ab075..ca87a7ba047d 100644 --- a/lib/tsan/lit_tests/mutexset1.cc +++ b/lib/tsan/lit_tests/mutexset1.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset2.cc b/lib/tsan/lit_tests/mutexset2.cc index 15d230332512..9ccf952b0050 100644 --- a/lib/tsan/lit_tests/mutexset2.cc +++ b/lib/tsan/lit_tests/mutexset2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset3.cc b/lib/tsan/lit_tests/mutexset3.cc index 6ac7ad15e4f9..272ddafb3c4a 100644 --- a/lib/tsan/lit_tests/mutexset3.cc +++ b/lib/tsan/lit_tests/mutexset3.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset4.cc b/lib/tsan/lit_tests/mutexset4.cc index 75684cf9ae5b..be751fa92bf0 100644 --- a/lib/tsan/lit_tests/mutexset4.cc +++ b/lib/tsan/lit_tests/mutexset4.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset5.cc b/lib/tsan/lit_tests/mutexset5.cc index 6e75810aff22..e013edb4656a 100644 --- a/lib/tsan/lit_tests/mutexset5.cc +++ b/lib/tsan/lit_tests/mutexset5.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset6.cc b/lib/tsan/lit_tests/mutexset6.cc index 4b19a12e0434..f5e6e66becf8 100644 --- a/lib/tsan/lit_tests/mutexset6.cc +++ b/lib/tsan/lit_tests/mutexset6.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset7.cc b/lib/tsan/lit_tests/mutexset7.cc index 3ec1b5202983..51451b215490 100644 --- a/lib/tsan/lit_tests/mutexset7.cc +++ b/lib/tsan/lit_tests/mutexset7.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset8.cc b/lib/tsan/lit_tests/mutexset8.cc index 6db63f7d16db..8822b050e939 100644 --- a/lib/tsan/lit_tests/mutexset8.cc +++ b/lib/tsan/lit_tests/mutexset8.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/oob_race.cc b/lib/tsan/lit_tests/oob_race.cc index 2e7f0593fd8d..9d8e2220d9a5 100644 --- a/lib/tsan/lit_tests/oob_race.cc +++ b/lib/tsan/lit_tests/oob_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/race_on_barrier.c b/lib/tsan/lit_tests/race_on_barrier.c index 3e76f8bf5e20..3c0199dec22e 100644 --- a/lib/tsan/lit_tests/race_on_barrier.c +++ b/lib/tsan/lit_tests/race_on_barrier.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/race_on_barrier2.c b/lib/tsan/lit_tests/race_on_barrier2.c index 46a4f50b133d..62773d43e66e 100644 --- a/lib/tsan/lit_tests/race_on_barrier2.c +++ b/lib/tsan/lit_tests/race_on_barrier2.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/race_on_heap.cc b/lib/tsan/lit_tests/race_on_heap.cc index 35434eac1850..a84c0de96558 100644 --- a/lib/tsan/lit_tests/race_on_heap.cc +++ b/lib/tsan/lit_tests/race_on_heap.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdlib.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/race_on_mutex.c b/lib/tsan/lit_tests/race_on_mutex.c index aff32f9bb1a2..e66341414831 100644 --- a/lib/tsan/lit_tests/race_on_mutex.c +++ b/lib/tsan/lit_tests/race_on_mutex.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/race_on_mutex2.c b/lib/tsan/lit_tests/race_on_mutex2.c index 84bef75a3449..80c395e1f9ce 100644 --- a/lib/tsan/lit_tests/race_on_mutex2.c +++ b/lib/tsan/lit_tests/race_on_mutex2.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/race_on_read.cc b/lib/tsan/lit_tests/race_on_read.cc index 7d226814816e..4ca4b25bfa81 100644 --- a/lib/tsan/lit_tests/race_on_read.cc +++ b/lib/tsan/lit_tests/race_on_read.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/race_on_write.cc b/lib/tsan/lit_tests/race_on_write.cc index f1b0bb1cbd6e..8a56c8464b91 100644 --- a/lib/tsan/lit_tests/race_on_write.cc +++ b/lib/tsan/lit_tests/race_on_write.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/race_with_finished_thread.cc b/lib/tsan/lit_tests/race_with_finished_thread.cc index a267290e661e..c713c67a398e 100644 --- a/lib/tsan/lit_tests/race_with_finished_thread.cc +++ b/lib/tsan/lit_tests/race_with_finished_thread.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/signal_errno.cc b/lib/tsan/lit_tests/signal_errno.cc index 8181555f6f63..2febca38294e 100644 --- a/lib/tsan/lit_tests/signal_errno.cc +++ b/lib/tsan/lit_tests/signal_errno.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/lib/tsan/lit_tests/signal_malloc.cc b/lib/tsan/lit_tests/signal_malloc.cc index 4dbc2f78ab17..ef180b8a25b6 100644 --- a/lib/tsan/lit_tests/signal_malloc.cc +++ b/lib/tsan/lit_tests/signal_malloc.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <stdio.h> #include <stdlib.h> #include <signal.h> diff --git a/lib/tsan/lit_tests/sigsuspend.cc b/lib/tsan/lit_tests/sigsuspend.cc new file mode 100644 index 000000000000..78d507fa0af5 --- /dev/null +++ b/lib/tsan/lit_tests/sigsuspend.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <assert.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <stdio.h> + +static bool signal_handler_ran = false; + +void do_nothing_signal_handler(int signum) { + write(1, "HANDLER\n", 8); + signal_handler_ran = true; +} + +int main() { + const int kSignalToTest = SIGSYS; + assert(SIG_ERR != signal(kSignalToTest, do_nothing_signal_handler)); + sigset_t empty_set; + assert(0 == sigemptyset(&empty_set)); + sigset_t one_signal = empty_set; + assert(0 == sigaddset(&one_signal, kSignalToTest)); + sigset_t old_set; + assert(0 == sigprocmask(SIG_BLOCK, &one_signal, &old_set)); + raise(kSignalToTest); + assert(!signal_handler_ran); + sigset_t all_but_one; + assert(0 == sigfillset(&all_but_one)); + assert(0 == sigdelset(&all_but_one, kSignalToTest)); + sigsuspend(&all_but_one); + assert(signal_handler_ran); + + // Restore the original set. + assert(0 == sigprocmask(SIG_SETMASK, &old_set, NULL)); + printf("DONE"); +} + +// CHECK: HANDLER +// CHECK: DONE diff --git a/lib/tsan/lit_tests/simple_race.c b/lib/tsan/lit_tests/simple_race.c index 44aff897406a..80a83e01a294 100644 --- a/lib/tsan/lit_tests/simple_race.c +++ b/lib/tsan/lit_tests/simple_race.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/simple_race.cc b/lib/tsan/lit_tests/simple_race.cc index 99cf228ac2f2..47854cfd9a3e 100644 --- a/lib/tsan/lit_tests/simple_race.cc +++ b/lib/tsan/lit_tests/simple_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/simple_stack.c b/lib/tsan/lit_tests/simple_stack.c index 4539cb7c1f37..a447e2880447 100644 --- a/lib/tsan/lit_tests/simple_stack.c +++ b/lib/tsan/lit_tests/simple_stack.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/simple_stack2.cc b/lib/tsan/lit_tests/simple_stack2.cc index bf27a15ffad5..7a034c4cd6ed 100644 --- a/lib/tsan/lit_tests/simple_stack2.cc +++ b/lib/tsan/lit_tests/simple_stack2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/sleep_sync.cc b/lib/tsan/lit_tests/sleep_sync.cc index c3d47d311749..217a52a097ce 100644 --- a/lib/tsan/lit_tests/sleep_sync.cc +++ b/lib/tsan/lit_tests/sleep_sync.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/sleep_sync2.cc b/lib/tsan/lit_tests/sleep_sync2.cc index d9961bccc808..e22999279f9f 100644 --- a/lib/tsan/lit_tests/sleep_sync2.cc +++ b/lib/tsan/lit_tests/sleep_sync2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/stack_race.cc b/lib/tsan/lit_tests/stack_race.cc index beeb57353bf3..7fabce22a85c 100644 --- a/lib/tsan/lit_tests/stack_race.cc +++ b/lib/tsan/lit_tests/stack_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/stack_race2.cc b/lib/tsan/lit_tests/stack_race2.cc index 5bdf1bd664a1..c759ec92774f 100644 --- a/lib/tsan/lit_tests/stack_race2.cc +++ b/lib/tsan/lit_tests/stack_race2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/static_init3.cc b/lib/tsan/lit_tests/static_init3.cc index 40fd4b940f55..70a3c16878ca 100644 --- a/lib/tsan/lit_tests/static_init3.cc +++ b/lib/tsan/lit_tests/static_init3.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdlib.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/suppress_same_address.cc b/lib/tsan/lit_tests/suppress_same_address.cc index 174d1cc8fcb3..c516f89529f3 100644 --- a/lib/tsan/lit_tests/suppress_same_address.cc +++ b/lib/tsan/lit_tests/suppress_same_address.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> int X; diff --git a/lib/tsan/lit_tests/suppress_same_stacks.cc b/lib/tsan/lit_tests/suppress_same_stacks.cc index 32bff9d50071..f0ab8b30435e 100644 --- a/lib/tsan/lit_tests/suppress_same_stacks.cc +++ b/lib/tsan/lit_tests/suppress_same_stacks.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> volatile int N; // Prevent loop unrolling. diff --git a/lib/tsan/lit_tests/suppressions_global.cc b/lib/tsan/lit_tests/suppressions_global.cc new file mode 100644 index 000000000000..181cb56cf2e6 --- /dev/null +++ b/lib/tsan/lit_tests/suppressions_global.cc @@ -0,0 +1,29 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +int RacyGlobal; + +void *Thread1(void *x) { + RacyGlobal = 42; + return NULL; +} + +void *Thread2(void *x) { + RacyGlobal = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/lib/tsan/lit_tests/suppressions_global.cc.supp b/lib/tsan/lit_tests/suppressions_global.cc.supp new file mode 100644 index 000000000000..5fa8a2e43a93 --- /dev/null +++ b/lib/tsan/lit_tests/suppressions_global.cc.supp @@ -0,0 +1,2 @@ +race:RacyGlobal + diff --git a/lib/tsan/lit_tests/suppressions_race.cc b/lib/tsan/lit_tests/suppressions_race.cc new file mode 100644 index 000000000000..c88e69bec6a3 --- /dev/null +++ b/lib/tsan/lit_tests/suppressions_race.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/lib/tsan/lit_tests/suppressions_race.cc.supp b/lib/tsan/lit_tests/suppressions_race.cc.supp new file mode 100644 index 000000000000..cbdba76ea93a --- /dev/null +++ b/lib/tsan/lit_tests/suppressions_race.cc.supp @@ -0,0 +1,2 @@ +race:Thread1 + diff --git a/lib/tsan/lit_tests/suppressions_race2.cc b/lib/tsan/lit_tests/suppressions_race2.cc new file mode 100644 index 000000000000..57146f96a428 --- /dev/null +++ b/lib/tsan/lit_tests/suppressions_race2.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/lib/tsan/lit_tests/suppressions_race2.cc.supp b/lib/tsan/lit_tests/suppressions_race2.cc.supp new file mode 100644 index 000000000000..b3c4dbc59363 --- /dev/null +++ b/lib/tsan/lit_tests/suppressions_race2.cc.supp @@ -0,0 +1,2 @@ +race:Thread2 + diff --git a/lib/tsan/lit_tests/test_output.sh b/lib/tsan/lit_tests/test_output.sh index 1eedf6eb20a3..79e773aa2c98 100755 --- a/lib/tsan/lit_tests/test_output.sh +++ b/lib/tsan/lit_tests/test_output.sh @@ -13,7 +13,7 @@ BLACKLIST=$ROOTDIR/lit_tests/Helpers/blacklist.txt # TODO: add testing for all of -O0...-O3 CFLAGS="-fsanitize=thread -fsanitize-blacklist=$BLACKLIST -fPIE -O1 -g -Wall" -LDFLAGS="-pie -lpthread -ldl $ROOTDIR/rtl/libtsan.a" +LDFLAGS="-pie -lpthread -ldl -lrt -Wl,--whole-archive $ROOTDIR/rtl/libtsan.a -Wl,--no-whole-archive" test_file() { SRC=$1 @@ -40,6 +40,10 @@ if [ "$1" == "" ]; then echo TEST $c is not supported continue fi + if [ "`grep "TSAN_OPTIONS" $c`" ]; then + echo SKIPPING $c -- requires TSAN_OPTIONS + continue + fi COMPILER=$CXX case $c in *.c) COMPILER=$CC diff --git a/lib/tsan/lit_tests/thread_leak3.c b/lib/tsan/lit_tests/thread_leak3.c index 3577164cad4a..5f447dbdbdf3 100644 --- a/lib/tsan/lit_tests/thread_leak3.c +++ b/lib/tsan/lit_tests/thread_leak3.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/thread_leak5.c b/lib/tsan/lit_tests/thread_leak5.c index fc72b149ec25..329f7233a38a 100644 --- a/lib/tsan/lit_tests/thread_leak5.c +++ b/lib/tsan/lit_tests/thread_leak5.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/thread_name.cc b/lib/tsan/lit_tests/thread_name.cc index 37f308ffbc0c..646ab5836241 100644 --- a/lib/tsan/lit_tests/thread_name.cc +++ b/lib/tsan/lit_tests/thread_name.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/thread_name2.cc b/lib/tsan/lit_tests/thread_name2.cc new file mode 100644 index 000000000000..8c5cb741f61f --- /dev/null +++ b/lib/tsan/lit_tests/thread_name2.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global++; + return 0; +} + +void *Thread2(void *x) { + pthread_setname_np(pthread_self(), "foobar2"); + Global--; + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread2, 0); + pthread_setname_np(t[0], "foobar1"); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Thread T1 'foobar1' +// CHECK: Thread T2 'foobar2' + diff --git a/lib/tsan/lit_tests/tiny_race.c b/lib/tsan/lit_tests/tiny_race.c index 44cc1332f2ab..f77e1606c1dd 100644 --- a/lib/tsan/lit_tests/tiny_race.c +++ b/lib/tsan/lit_tests/tiny_race.c @@ -1,15 +1,21 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> +#include <unistd.h> + int Global; + void *Thread1(void *x) { + sleep(1); Global = 42; return x; } + int main() { pthread_t t; - pthread_create(&t, NULL, Thread1, NULL); + pthread_create(&t, 0, Thread1, 0); Global = 43; - pthread_join(t, NULL); + pthread_join(t, 0); return Global; } + // CHECK: WARNING: ThreadSanitizer: data race diff --git a/lib/tsan/lit_tests/tls_race.cc b/lib/tsan/lit_tests/tls_race.cc index bed6aafaacfc..3cbcc9dbba42 100644 --- a/lib/tsan/lit_tests/tls_race.cc +++ b/lib/tsan/lit_tests/tls_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/tls_race2.cc b/lib/tsan/lit_tests/tls_race2.cc index 110abaa6a9df..136087065c12 100644 --- a/lib/tsan/lit_tests/tls_race2.cc +++ b/lib/tsan/lit_tests/tls_race2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/unaligned_race.cc b/lib/tsan/lit_tests/unaligned_race.cc index 18bed8555cc5..6ac87b577ec5 100644 --- a/lib/tsan/lit_tests/unaligned_race.cc +++ b/lib/tsan/lit_tests/unaligned_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/lib/tsan/lit_tests/vptr_harmful_race.cc b/lib/tsan/lit_tests/vptr_harmful_race.cc index 76d31c00ad4f..0105c4cedd99 100644 --- a/lib/tsan/lit_tests/vptr_harmful_race.cc +++ b/lib/tsan/lit_tests/vptr_harmful_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <semaphore.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/vptr_harmful_race2.cc b/lib/tsan/lit_tests/vptr_harmful_race2.cc index d7e1d19a11bd..378790c62340 100644 --- a/lib/tsan/lit_tests/vptr_harmful_race2.cc +++ b/lib/tsan/lit_tests/vptr_harmful_race2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <semaphore.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/write_in_reader_lock.cc b/lib/tsan/lit_tests/write_in_reader_lock.cc index db8bac32b6e4..e872fe3ff960 100644 --- a/lib/tsan/lit_tests/write_in_reader_lock.cc +++ b/lib/tsan/lit_tests/write_in_reader_lock.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/rtl/CMakeLists.txt b/lib/tsan/rtl/CMakeLists.txt deleted file mode 100644 index f1a8ff4d6558..000000000000 --- a/lib/tsan/rtl/CMakeLists.txt +++ /dev/null @@ -1,51 +0,0 @@ -set(TSAN_SOURCES - tsan_clock.cc - tsan_flags.cc - tsan_fd.cc - tsan_interceptors.cc - tsan_interface_ann.cc - tsan_interface_atomic.cc - tsan_interface.cc - tsan_interface_java.cc - tsan_md5.cc - tsan_mman.cc - tsan_mutex.cc - tsan_mutexset.cc - tsan_report.cc - tsan_rtl.cc - tsan_rtl_mutex.cc - tsan_rtl_report.cc - tsan_rtl_thread.cc - tsan_stat.cc - tsan_suppressions.cc - tsan_symbolize.cc - tsan_sync.cc - ) - -if(APPLE) - list(APPEND TSAN_SOURCES tsan_platform_mac.cc) -elseif(UNIX) - # Assume Linux - list(APPEND TSAN_SOURCES - tsan_platform_linux.cc - tsan_symbolize_addr2line_linux.cc) -endif() - -set(TSAN_RUNTIME_LIBRARIES) -# TSan is currently supported on 64-bit Linux only. -if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE) - set(TSAN_ASM_SOURCES tsan_rtl_amd64.S) - # Pass ASM file directly to the C++ compiler. - set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES - LANGUAGE C) - set(arch "x86_64") - add_compiler_rt_static_runtime(clang_rt.tsan-${arch} ${arch} - SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES} - $<TARGET_OBJECTS:RTInterception.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> - CFLAGS ${TSAN_CFLAGS} - DEFS ${TSAN_COMMON_DEFINITIONS} - SYMS tsan.syms) - list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch}) -endif() diff --git a/lib/tsan/rtl/tsan.syms b/lib/tsan/rtl/tsan.syms deleted file mode 100644 index 4464a0a231c9..000000000000 --- a/lib/tsan/rtl/tsan.syms +++ /dev/null @@ -1,5 +0,0 @@ -{ - __tsan_*; - __sanitizer_syscall_pre_*; - __sanitizer_syscall_post_*; -}; diff --git a/lib/tsan/rtl/tsan.syms.extra b/lib/tsan/rtl/tsan.syms.extra new file mode 100644 index 000000000000..49ed6b46629e --- /dev/null +++ b/lib/tsan/rtl/tsan.syms.extra @@ -0,0 +1,14 @@ +__tsan_init +__tsan_read* +__tsan_write* +__tsan_vptr* +__tsan_func* +__tsan_atomic* +__tsan_java* +__tsan_unaligned* +__tsan_release +__tsan_acquire +Annotate* +WTFAnnotate* +RunningOnValgrind +ValgrindSlowdown diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h index 7150e2e255d8..1e53a8e02f0a 100644 --- a/lib/tsan/rtl/tsan_defs.h +++ b/lib/tsan/rtl/tsan_defs.h @@ -41,10 +41,8 @@ const int kTidBits = 13; const unsigned kMaxTid = 1 << kTidBits; const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit. const int kClkBits = 42; -#ifndef TSAN_GO -const int kShadowStackSize = 4 * 1024; -const int kTraceStackSize = 256; -#endif +const uptr kShadowStackSize = 64 * 1024; +const uptr kTraceStackSize = 256; #ifdef TSAN_SHADOW_COUNT # if TSAN_SHADOW_COUNT == 2 \ @@ -162,7 +160,6 @@ class ReportDesc; class RegionAlloc; class StackTrace; struct MBlock; -struct Suppression; } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_fd.cc b/lib/tsan/rtl/tsan_fd.cc index 14bdbb53b322..86db119fc918 100644 --- a/lib/tsan/rtl/tsan_fd.cc +++ b/lib/tsan/rtl/tsan_fd.cc @@ -42,6 +42,11 @@ struct FdContext { static FdContext fdctx; +static bool bogusfd(int fd) { + // Apparently a bogus fd value. + return fd < 0 || fd >= (1 << 30); +} + static FdSync *allocsync() { FdSync *s = (FdSync*)internal_alloc(MBlockFD, sizeof(FdSync)); atomic_store(&s->rc, 1, memory_order_relaxed); @@ -69,6 +74,7 @@ static void unref(ThreadState *thr, uptr pc, FdSync *s) { } static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) { + CHECK_GE(fd, 0); CHECK_LT(fd, kTableSize); atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2]; uptr l1 = atomic_load(pl1, memory_order_consume); @@ -148,6 +154,8 @@ bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) { } void FdAcquire(ThreadState *thr, uptr pc, int fd) { + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); FdSync *s = d->sync; DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s); @@ -157,22 +165,28 @@ void FdAcquire(ThreadState *thr, uptr pc, int fd) { } void FdRelease(ThreadState *thr, uptr pc, int fd) { + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); FdSync *s = d->sync; DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); if (s) Release(thr, pc, (uptr)s); - MemoryRead(thr, pc, (uptr)d, kSizeLog8); } void FdAccess(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); MemoryRead(thr, pc, (uptr)d, kSizeLog8); } void FdClose(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdClose(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); // To catch races between fd usage and close. MemoryWrite(thr, pc, (uptr)d, kSizeLog8); @@ -187,11 +201,15 @@ void FdClose(ThreadState *thr, uptr pc, int fd) { void FdFileCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, &fdctx.filesync); } void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) { DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd); + if (bogusfd(oldfd) || bogusfd(newfd)) + return; // Ignore the case when user dups not yet connected socket. FdDesc *od = fddesc(thr, pc, oldfd); MemoryRead(thr, pc, (uptr)od, kSizeLog8); @@ -209,32 +227,44 @@ void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) { void FdEventCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, allocsync()); } void FdSignalCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, 0); } void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, 0); } void FdPollCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, allocsync()); } void FdSocketCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; // It can be a UDP socket. init(thr, pc, fd, &fdctx.socksync); } void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) { DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd); + if (bogusfd(fd)) + return; // Synchronize connect->accept. Acquire(thr, pc, (uptr)&fdctx.connectsync); init(thr, pc, newfd, &fdctx.socksync); @@ -242,12 +272,16 @@ void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) { void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; // Synchronize connect->accept. Release(thr, pc, (uptr)&fdctx.connectsync); } void FdSocketConnect(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, &fdctx.socksync); } diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc index c062592f482d..c6f24bf49abd 100644 --- a/lib/tsan/rtl/tsan_flags.cc +++ b/lib/tsan/rtl/tsan_flags.cc @@ -26,13 +26,42 @@ Flags *flags() { // Can be overriden in frontend. #ifdef TSAN_EXTERNAL_HOOKS void OverrideFlags(Flags *f); +extern "C" const char* __tsan_default_options(); #else -SANITIZER_INTERFACE_ATTRIBUTE void WEAK OverrideFlags(Flags *f) { (void)f; } +extern "C" const char *WEAK __tsan_default_options() { + return ""; +} #endif +static void ParseFlags(Flags *f, const char *env) { + ParseFlag(env, &f->enable_annotations, "enable_annotations"); + ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks"); + ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses"); + ParseFlag(env, &f->suppress_java, "suppress_java"); + ParseFlag(env, &f->report_bugs, "report_bugs"); + ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks"); + ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked"); + ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe"); + ParseFlag(env, &f->report_atomic_races, "report_atomic_races"); + ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics"); + ParseFlag(env, &f->suppressions, "suppressions"); + ParseFlag(env, &f->print_suppressions, "print_suppressions"); + ParseFlag(env, &f->print_benign, "print_benign"); + ParseFlag(env, &f->exitcode, "exitcode"); + ParseFlag(env, &f->halt_on_error, "halt_on_error"); + ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms"); + ParseFlag(env, &f->profile_memory, "profile_memory"); + ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms"); + ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms"); + ParseFlag(env, &f->memory_limit_mb, "memory_limit_mb"); + ParseFlag(env, &f->stop_on_start, "stop_on_start"); + ParseFlag(env, &f->history_size, "history_size"); + ParseFlag(env, &f->io_sync, "io_sync"); +} + void InitializeFlags(Flags *f, const char *env) { internal_memset(f, 0, sizeof(*f)); @@ -47,53 +76,35 @@ void InitializeFlags(Flags *f, const char *env) { f->report_signal_unsafe = true; f->report_atomic_races = true; f->force_seq_cst_atomics = false; - f->strip_path_prefix = ""; f->suppressions = ""; f->print_suppressions = false; f->print_benign = false; f->exitcode = 66; - f->log_path = "stderr"; + f->halt_on_error = false; f->atexit_sleep_ms = 1000; - f->verbosity = 0; f->profile_memory = ""; f->flush_memory_ms = 0; f->flush_symbolizer_ms = 5000; + f->memory_limit_mb = 0; f->stop_on_start = false; f->running_on_valgrind = false; - f->external_symbolizer_path = ""; f->history_size = kGoMode ? 1 : 2; // There are a lot of goroutines in Go. f->io_sync = 1; + CommonFlags *cf = common_flags(); + SetCommonFlagDefaults(); + *static_cast<CommonFlags*>(f) = *cf; + // Let a frontend override. OverrideFlags(f); - + ParseFlags(f, __tsan_default_options()); + ParseCommonFlagsFromString(__tsan_default_options()); // Override from command line. - ParseFlag(env, &f->enable_annotations, "enable_annotations"); - ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks"); - ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses"); - ParseFlag(env, &f->suppress_java, "suppress_java"); - ParseFlag(env, &f->report_bugs, "report_bugs"); - ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks"); - ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked"); - ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe"); - ParseFlag(env, &f->report_atomic_races, "report_atomic_races"); - ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics"); - ParseFlag(env, &f->strip_path_prefix, "strip_path_prefix"); - ParseFlag(env, &f->suppressions, "suppressions"); - ParseFlag(env, &f->print_suppressions, "print_suppressions"); - ParseFlag(env, &f->print_benign, "print_benign"); - ParseFlag(env, &f->exitcode, "exitcode"); - ParseFlag(env, &f->log_path, "log_path"); - ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms"); - ParseFlag(env, &f->verbosity, "verbosity"); - ParseFlag(env, &f->profile_memory, "profile_memory"); - ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms"); - ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms"); - ParseFlag(env, &f->stop_on_start, "stop_on_start"); - ParseFlag(env, &f->external_symbolizer_path, "external_symbolizer_path"); - ParseFlag(env, &f->history_size, "history_size"); - ParseFlag(env, &f->io_sync, "io_sync"); + ParseFlags(f, env); + ParseCommonFlagsFromString(env); + *static_cast<CommonFlags*>(f) = *cf; + // Sanity check. if (!f->report_bugs) { f->report_thread_leaks = false; f->report_destroy_locked = false; diff --git a/lib/tsan/rtl/tsan_flags.h b/lib/tsan/rtl/tsan_flags.h index aaacd98a6223..3916df3cc9e1 100644 --- a/lib/tsan/rtl/tsan_flags.h +++ b/lib/tsan/rtl/tsan_flags.h @@ -20,9 +20,11 @@ // header may be included in the user code, and shouldn't include // other headers from TSan or common sanitizer runtime. +#include "sanitizer_common/sanitizer_flags.h" + namespace __tsan { -struct Flags { +struct Flags : CommonFlags { // Enable dynamic annotations, otherwise they are no-ops. bool enable_annotations; // Supress a race report if we've already output another race report @@ -48,8 +50,6 @@ struct Flags { // If set, all atomics are effectively sequentially consistent (seq_cst), // regardless of what user actually specified. bool force_seq_cst_atomics; - // Strip that prefix from file paths in reports. - const char *strip_path_prefix; // Suppressions filename. const char *suppressions; // Print matched suppressions at exit. @@ -58,27 +58,24 @@ struct Flags { bool print_benign; // Override exit status if something was reported. int exitcode; - // Write logs to "log_path.pid". - // The special values are "stdout" and "stderr". - // The default is "stderr". - const char *log_path; + // Exit after first reported error. + bool halt_on_error; // Sleep in main thread before exiting for that many ms // (useful to catch "at exit" races). int atexit_sleep_ms; - // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). - int verbosity; // If set, periodically write memory profile to that file. const char *profile_memory; // Flush shadow memory every X ms. int flush_memory_ms; // Flush symbolizer caches every X ms. int flush_symbolizer_ms; + // Resident memory limit in MB to aim at. + // If the process consumes more memory, then TSan will flush shadow memory. + int memory_limit_mb; // Stops on start until __tsan_resume() is called (for debugging). bool stop_on_start; // Controls whether RunningOnValgrind() returns true or false. bool running_on_valgrind; - // Path to external symbolizer. - const char *external_symbolizer_path; // Per-thread history size, controls how many previous memory accesses // are remembered per thread. Possible values are [0..7]. // history_size=0 amounts to 32K memory accesses. Each next value doubles diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc index f18b26f6abe4..ef38f7987cde 100644 --- a/lib/tsan/rtl/tsan_interceptors.cc +++ b/lib/tsan/rtl/tsan_interceptors.cc @@ -22,6 +22,7 @@ #include "interception/interception.h" #include "tsan_interface.h" #include "tsan_platform.h" +#include "tsan_suppressions.h" #include "tsan_rtl.h" #include "tsan_mman.h" #include "tsan_fd.h" @@ -35,11 +36,6 @@ struct my_siginfo_t { u64 opaque[128 / sizeof(u64)]; }; -struct sigset_t { - // The size is determined by looking at sizeof of real sigset_t on linux. - u64 val[128 / sizeof(u64)]; -}; - struct ucontext_t { // The size is determined by looking at sizeof of real ucontext_t on linux. u64 opaque[936 / sizeof(u64) + 1]; @@ -47,15 +43,16 @@ struct ucontext_t { extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); -extern "C" int pthread_attr_getdetachstate(void *attr, int *v); +DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *) extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); -extern "C" int pthread_attr_getstacksize(void *attr, uptr *stacksize); extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v)); extern "C" int pthread_setspecific(unsigned key, const void *v); extern "C" int pthread_mutexattr_gettype(void *a, int *type); extern "C" int pthread_yield(); -extern "C" int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset); -extern "C" int sigfillset(sigset_t *set); +extern "C" int pthread_sigmask(int how, const __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset); +// REAL(sigfillset) defined in common interceptors. +DECLARE_REAL(int, sigfillset, __sanitizer_sigset_t *set) extern "C" void *pthread_self(); extern "C" void _exit(int status); extern "C" int *__errno_location(); @@ -67,9 +64,9 @@ extern "C" void __libc_free(void *ptr); extern "C" int mallopt(int param, int value); const int PTHREAD_MUTEX_RECURSIVE = 1; const int PTHREAD_MUTEX_RECURSIVE_NP = 1; -const int kPthreadAttrSize = 56; const int EINVAL = 22; const int EBUSY = 16; +const int EOWNERDEAD = 130; const int EPOLL_CTL_ADD = 1; const int SIGILL = 4; const int SIGABRT = 6; @@ -77,6 +74,7 @@ const int SIGFPE = 8; const int SIGSEGV = 11; const int SIGPIPE = 13; const int SIGBUS = 7; +const int SIGSYS = 31; void *const MAP_FAILED = (void*)-1; const int PTHREAD_BARRIER_SERIAL_THREAD = -1; const int MAP_FIXED = 0x10; @@ -97,7 +95,7 @@ struct sigaction_t { sighandler_t sa_handler; void (*sa_sigaction)(int sig, my_siginfo_t *siginfo, void *uctx); }; - sigset_t sa_mask; + __sanitizer_sigset_t sa_mask; int sa_flags; void (*sa_restorer)(); }; @@ -128,6 +126,19 @@ struct SignalContext { int pending_signal_count; SignalDesc pending_signals[kSigCount]; }; + +// The object is 64-byte aligned, because we want hot data to be located in +// a single cache line if possible (it's accessed in every interceptor). +static ALIGNED(64) char libignore_placeholder[sizeof(LibIgnore)]; +static LibIgnore *libignore() { + return reinterpret_cast<LibIgnore*>(&libignore_placeholder[0]); +} + +void InitializeLibIgnore() { + libignore()->Init(*GetSuppressionContext()); + libignore()->OnLibraryLoaded(0); +} + } // namespace __tsan static SignalContext *SigCtx(ThreadState *thr) { @@ -150,12 +161,14 @@ class ScopedInterceptor { private: ThreadState *const thr_; const int in_rtl_; + bool in_ignored_lib_; }; ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc) : thr_(thr) - , in_rtl_(thr->in_rtl) { + , in_rtl_(thr->in_rtl) + , in_ignored_lib_(false) { if (thr_->in_rtl == 0) { Initialize(thr); FuncEntry(thr, pc); @@ -164,9 +177,18 @@ ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname, } else { thr_->in_rtl++; } + if (!thr_->in_ignored_lib && libignore()->IsIgnored(pc)) { + in_ignored_lib_ = true; + thr_->in_ignored_lib = true; + ThreadIgnoreBegin(thr_); + } } ScopedInterceptor::~ScopedInterceptor() { + if (in_ignored_lib_) { + thr_->in_ignored_lib = false; + ThreadIgnoreEnd(thr_); + } thr_->in_rtl--; if (thr_->in_rtl == 0) { FuncExit(thr_); @@ -181,8 +203,7 @@ ScopedInterceptor::~ScopedInterceptor() { StatInc(thr, StatInt_##func); \ const uptr caller_pc = GET_CALLER_PC(); \ ScopedInterceptor si(thr, #func, caller_pc); \ - const uptr pc = __sanitizer::StackTrace::GetPreviousInstructionPc( \ - __sanitizer::StackTrace::GetCurrentPc()); \ + const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ (void)pc; \ /**/ @@ -192,7 +213,7 @@ ScopedInterceptor::~ScopedInterceptor() { Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ Die(); \ } \ - if (thr->in_rtl > 1) \ + if (thr->in_rtl > 1 || thr->in_ignored_lib) \ return REAL(func)(__VA_ARGS__); \ /**/ @@ -235,6 +256,28 @@ TSAN_INTERCEPTOR(int, nanosleep, void *req, void *rem) { return res; } +TSAN_INTERCEPTOR(void*, dlopen, const char *filename, int flag) { + SCOPED_INTERCEPTOR_RAW(dlopen, filename, flag); + // dlopen will execute global constructors, so it must be not in rtl. + CHECK_EQ(thr->in_rtl, 1); + thr->in_rtl = 0; + void *res = REAL(dlopen)(filename, flag); + thr->in_rtl = 1; + libignore()->OnLibraryLoaded(filename); + return res; +} + +TSAN_INTERCEPTOR(int, dlclose, void *handle) { + SCOPED_INTERCEPTOR_RAW(dlclose, handle); + // dlclose will execute global destructors, so it must be not in rtl. + CHECK_EQ(thr->in_rtl, 1); + thr->in_rtl = 0; + int res = REAL(dlclose)(handle); + thr->in_rtl = 1; + libignore()->OnLibraryUnloaded(); + return res; +} + class AtExitContext { public: AtExitContext() @@ -314,8 +357,14 @@ TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { if (cur_thread()->in_symbolizer) return 0; SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso); - if (dso) - return REAL(__cxa_atexit)(f, arg, dso); + if (dso) { + // Memory allocation in __cxa_atexit will race with free during exit, + // because we do not see synchronization around atexit callback list. + ThreadIgnoreBegin(thr); + int res = REAL(__cxa_atexit)(f, arg, dso); + ThreadIgnoreEnd(thr); + return res; + } return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg); } @@ -362,27 +411,37 @@ static void LongJmp(ThreadState *thr, uptr *env) { CHECK(0); } +// FIXME: put everything below into a common extern "C" block? extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) { ScopedInRtl in_rtl; SetJmp(cur_thread(), sp, mangled_sp); } // Not called. Merely to satisfy TSAN_INTERCEPT(). +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor_setjmp(void *env); extern "C" int __interceptor_setjmp(void *env) { CHECK(0); return 0; } +// FIXME: any reason to have a separate declaration? +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor__setjmp(void *env); extern "C" int __interceptor__setjmp(void *env) { CHECK(0); return 0; } +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor_sigsetjmp(void *env); extern "C" int __interceptor_sigsetjmp(void *env) { CHECK(0); return 0; } +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor___sigsetjmp(void *env); extern "C" int __interceptor___sigsetjmp(void *env) { CHECK(0); return 0; @@ -433,7 +492,8 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { if (cur_thread()->in_symbolizer) return __libc_calloc(size, n); - if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) return 0; + if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) + return AllocatorReturnNull(); void *p = 0; { SCOPED_INTERCEPTOR_RAW(calloc, size, n); @@ -494,15 +554,26 @@ TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) { invoke_malloc_hook(p, size); \ return p; +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new(__sanitizer::uptr size); void *operator new(__sanitizer::uptr size) { OPERATOR_NEW_BODY(_Znwm); } + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new[](__sanitizer::uptr size); void *operator new[](__sanitizer::uptr size) { OPERATOR_NEW_BODY(_Znam); } + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new(__sanitizer::uptr size, std::nothrow_t const&); void *operator new(__sanitizer::uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t); } + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new[](__sanitizer::uptr size, std::nothrow_t const&); void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t); } @@ -515,15 +586,26 @@ void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ user_free(thr, pc, ptr); +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete(void *ptr); void operator delete(void *ptr) { OPERATOR_DELETE_BODY(_ZdlPv); } + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete[](void *ptr); void operator delete[](void *ptr) { OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t); } + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const&); void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(_ZdaPv); } + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const&); void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t); } @@ -561,30 +643,6 @@ TSAN_INTERCEPTOR(int, memcmp, const void *s1, const void *s2, uptr n) { return res; } -TSAN_INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { - SCOPED_TSAN_INTERCEPTOR(strcmp, s1, s2); - uptr len = 0; - for (; s1[len] && s2[len]; len++) { - if (s1[len] != s2[len]) - break; - } - MemoryAccessRange(thr, pc, (uptr)s1, len + 1, false); - MemoryAccessRange(thr, pc, (uptr)s2, len + 1, false); - return s1[len] - s2[len]; -} - -TSAN_INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr n) { - SCOPED_TSAN_INTERCEPTOR(strncmp, s1, s2, n); - uptr len = 0; - for (; len < n && s1[len] && s2[len]; len++) { - if (s1[len] != s2[len]) - break; - } - MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false); - MemoryAccessRange(thr, pc, (uptr)s2, len < n ? len + 1 : n, false); - return len == n ? 0 : s1[len] - s2[len]; -} - TSAN_INTERCEPTOR(void*, memchr, void *s, int c, uptr n) { SCOPED_TSAN_INTERCEPTOR(memchr, s, c, n); void *res = REAL(memchr)(s, c, n); @@ -654,6 +712,12 @@ TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) { return res; } +TSAN_INTERCEPTOR(char*, strdup, const char *str) { + SCOPED_TSAN_INTERCEPTOR(strdup, str); + // strdup will call malloc, so no instrumentation is required here. + return REAL(strdup)(str); +} + static bool fix_mmap_addr(void **addr, long_t sz, int flags) { if (*addr) { if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) { @@ -704,23 +768,23 @@ TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) { } TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(memalign, align, sz); + SCOPED_INTERCEPTOR_RAW(memalign, align, sz); return user_alloc(thr, pc, sz, align); } TSAN_INTERCEPTOR(void*, valloc, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(valloc, sz); + SCOPED_INTERCEPTOR_RAW(valloc, sz); return user_alloc(thr, pc, sz, GetPageSizeCached()); } TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(pvalloc, sz); + SCOPED_INTERCEPTOR_RAW(pvalloc, sz); sz = RoundUp(sz, GetPageSizeCached()); return user_alloc(thr, pc, sz, GetPageSizeCached()); } TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(posix_memalign, memptr, align, sz); + SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz); *memptr = user_alloc(thr, pc, sz, align); return 0; } @@ -789,7 +853,8 @@ extern "C" void *__tsan_thread_start_func(void *arg) { { ThreadState *thr = cur_thread(); ScopedInRtl in_rtl; - if (pthread_setspecific(g_thread_finalize_key, (void*)4)) { + if (pthread_setspecific(g_thread_finalize_key, + (void *)kPthreadDestructorIterations)) { Printf("ThreadSanitizer: failed to set thread key\n"); Die(); } @@ -809,21 +874,15 @@ extern "C" void *__tsan_thread_start_func(void *arg) { TSAN_INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), void * param) { - SCOPED_TSAN_INTERCEPTOR(pthread_create, th, attr, callback, param); + SCOPED_INTERCEPTOR_RAW(pthread_create, th, attr, callback, param); __sanitizer_pthread_attr_t myattr; if (attr == 0) { pthread_attr_init(&myattr); attr = &myattr; } int detached = 0; - pthread_attr_getdetachstate(attr, &detached); - -#if defined(TSAN_DEBUG_OUTPUT) - int verbosity = (TSAN_DEBUG_OUTPUT); -#else - int verbosity = 0; -#endif - AdjustStackSizeLinux(attr, verbosity); + REAL(pthread_attr_getdetachstate)(attr, &detached); + AdjustStackSizeLinux(attr); ThreadParam p; p.callback = callback; @@ -843,7 +902,7 @@ TSAN_INTERCEPTOR(int, pthread_create, } TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) { - SCOPED_TSAN_INTERCEPTOR(pthread_join, th, ret); + SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret); int tid = ThreadTid(thr, pc, (uptr)th); int res = BLOCK_REAL(pthread_join)(th, ret); if (res == 0) { @@ -887,21 +946,13 @@ TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) { return res; } -TSAN_INTERCEPTOR(int, pthread_mutex_lock, void *m) { - SCOPED_TSAN_INTERCEPTOR(pthread_mutex_lock, m); - int res = REAL(pthread_mutex_lock)(m); - if (res == 0) { - MutexLock(thr, pc, (uptr)m); - } - return res; -} - TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m); int res = REAL(pthread_mutex_trylock)(m); - if (res == 0) { + if (res == EOWNERDEAD) + MutexRepair(thr, pc, (uptr)m); + if (res == 0 || res == EOWNERDEAD) MutexLock(thr, pc, (uptr)m); - } return res; } @@ -914,13 +965,6 @@ TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) { return res; } -TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) { - SCOPED_TSAN_INTERCEPTOR(pthread_mutex_unlock, m); - MutexUnlock(thr, pc, (uptr)m); - int res = REAL(pthread_mutex_unlock)(m); - return res; -} - TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared); int res = REAL(pthread_spin_init)(m, pshared); @@ -1043,45 +1087,18 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) { return res; } -// libpthread.so contains several versions of pthread_cond_init symbol. -// When we just dlsym() it, we get the wrong (old) version. -/* -TSAN_INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, c, a); - int res = REAL(pthread_cond_init)(c, a); - return res; -} -*/ - TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) { SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, c); + MemoryWrite(thr, pc, (uptr)c, kSizeLog1); int res = REAL(pthread_cond_destroy)(c); return res; } -TSAN_INTERCEPTOR(int, pthread_cond_signal, void *c) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, c); - int res = REAL(pthread_cond_signal)(c); - return res; -} - -TSAN_INTERCEPTOR(int, pthread_cond_broadcast, void *c) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, c); - int res = REAL(pthread_cond_broadcast)(c); - return res; -} - -TSAN_INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, c, m); - MutexUnlock(thr, pc, (uptr)m); - int res = REAL(pthread_cond_wait)(c, m); - MutexLock(thr, pc, (uptr)m); - return res; -} - -TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { +TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, + void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, c, m, abstime); MutexUnlock(thr, pc, (uptr)m); + MemoryRead(thr, pc, (uptr)c, kSizeLog1); int res = REAL(pthread_cond_timedwait)(c, m, abstime); MutexLock(thr, pc, (uptr)m); return res; @@ -1114,7 +1131,9 @@ TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) { } TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { - SCOPED_TSAN_INTERCEPTOR(pthread_once, o, f); + SCOPED_INTERCEPTOR_RAW(pthread_once, o, f); + // Using SCOPED_INTERCEPTOR_RAW, because if we are called from an ignored lib, + // the user callback must be executed with thr->in_rtl == 0. if (o == 0 || f == 0) return EINVAL; atomic_uint32_t *a = static_cast<atomic_uint32_t*>(o); @@ -1126,14 +1145,16 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { (*f)(); CHECK_EQ(thr->in_rtl, 0); thr->in_rtl = old_in_rtl; - Release(thr, pc, (uptr)o); + if (!thr->in_ignored_lib) + Release(thr, pc, (uptr)o); atomic_store(a, 2, memory_order_release); } else { while (v != 2) { pthread_yield(); v = atomic_load(a, memory_order_acquire); } - Acquire(thr, pc, (uptr)o); + if (!thr->in_ignored_lib) + Acquire(thr, pc, (uptr)o); } return 0; } @@ -1392,22 +1413,6 @@ TSAN_INTERCEPTOR(int, listen, int fd, int backlog) { return res; } -TSAN_INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { - SCOPED_TSAN_INTERCEPTOR(accept, fd, addr, addrlen); - int fd2 = REAL(accept)(fd, addr, addrlen); - if (fd >= 0 && fd2 >= 0) - FdSocketAccept(thr, pc, fd, fd2); - return fd2; -} - -TSAN_INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { - SCOPED_TSAN_INTERCEPTOR(accept4, fd, addr, addrlen, f); - int fd2 = REAL(accept4)(fd, addr, addrlen, f); - if (fd >= 0 && fd2 >= 0) - FdSocketAccept(thr, pc, fd, fd2); - return fd2; -} - TSAN_INTERCEPTOR(int, epoll_create, int size) { SCOPED_TSAN_INTERCEPTOR(epoll_create, size); int fd = REAL(epoll_create)(size); @@ -1466,58 +1471,30 @@ TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) { return res; } -TSAN_INTERCEPTOR(long_t, readv, int fd, void *vec, int cnt) { - SCOPED_TSAN_INTERCEPTOR(readv, fd, vec, cnt); - int res = REAL(readv)(fd, vec, cnt); - if (res >= 0 && fd >= 0) { - FdAcquire(thr, pc, fd); - } - return res; -} - -TSAN_INTERCEPTOR(long_t, preadv64, int fd, void *vec, int cnt, u64 off) { - SCOPED_TSAN_INTERCEPTOR(preadv64, fd, vec, cnt, off); - int res = REAL(preadv64)(fd, vec, cnt, off); - if (res >= 0 && fd >= 0) { - FdAcquire(thr, pc, fd); - } - return res; -} - -TSAN_INTERCEPTOR(long_t, writev, int fd, void *vec, int cnt) { - SCOPED_TSAN_INTERCEPTOR(writev, fd, vec, cnt); - if (fd >= 0) - FdRelease(thr, pc, fd); - int res = REAL(writev)(fd, vec, cnt); - return res; -} - -TSAN_INTERCEPTOR(long_t, pwritev64, int fd, void *vec, int cnt, u64 off) { - SCOPED_TSAN_INTERCEPTOR(pwritev64, fd, vec, cnt, off); - if (fd >= 0) - FdRelease(thr, pc, fd); - int res = REAL(pwritev64)(fd, vec, cnt, off); - return res; -} - TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) { SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags); - if (fd >= 0) + if (fd >= 0) { + FdAccess(thr, pc, fd); FdRelease(thr, pc, fd); + } int res = REAL(send)(fd, buf, len, flags); return res; } TSAN_INTERCEPTOR(long_t, sendmsg, int fd, void *msg, int flags) { SCOPED_TSAN_INTERCEPTOR(sendmsg, fd, msg, flags); - if (fd >= 0) + if (fd >= 0) { + FdAccess(thr, pc, fd); FdRelease(thr, pc, fd); + } int res = REAL(sendmsg)(fd, msg, flags); return res; } TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) { SCOPED_TSAN_INTERCEPTOR(recv, fd, buf, len, flags); + if (fd >= 0) + FdAccess(thr, pc, fd); int res = REAL(recv)(fd, buf, len, flags); if (res >= 0 && fd >= 0) { FdAcquire(thr, pc, fd); @@ -1525,15 +1502,6 @@ TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) { return res; } -TSAN_INTERCEPTOR(long_t, recvmsg, int fd, void *msg, int flags) { - SCOPED_TSAN_INTERCEPTOR(recvmsg, fd, msg, flags); - int res = REAL(recvmsg)(fd, msg, flags); - if (res >= 0 && fd >= 0) { - FdAcquire(thr, pc, fd); - } - return res; -} - TSAN_INTERCEPTOR(int, unlink, char *path) { SCOPED_TSAN_INTERCEPTOR(unlink, path); Release(thr, pc, File2addr(path)); @@ -1571,6 +1539,7 @@ TSAN_INTERCEPTOR(void*, freopen, char *path, char *mode, void *stream) { } TSAN_INTERCEPTOR(int, fclose, void *stream) { + // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fclose, stream); if (stream) { @@ -1583,6 +1552,7 @@ TSAN_INTERCEPTOR(int, fclose, void *stream) { } TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { + // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f); MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true); @@ -1591,6 +1561,7 @@ TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { } TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { + // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f); MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false); @@ -1599,7 +1570,10 @@ TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { } TSAN_INTERCEPTOR(int, fflush, void *stream) { - SCOPED_TSAN_INTERCEPTOR(fflush, stream); + // libc file streams can call user-supplied functions, see fopencookie. + { + SCOPED_TSAN_INTERCEPTOR(fflush, stream); + } return REAL(fflush)(stream); } @@ -1632,27 +1606,23 @@ TSAN_INTERCEPTOR(void*, opendir, char *path) { TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) { SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev); - if (op == EPOLL_CTL_ADD && epfd >= 0) { + if (epfd >= 0) + FdAccess(thr, pc, epfd); + if (epfd >= 0 && fd >= 0) + FdAccess(thr, pc, fd); + if (op == EPOLL_CTL_ADD && epfd >= 0) FdRelease(thr, pc, epfd); - } int res = REAL(epoll_ctl)(epfd, op, fd, ev); - if (fd >= 0) - FdAccess(thr, pc, fd); return res; } TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) { SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout); + if (epfd >= 0) + FdAccess(thr, pc, epfd); int res = BLOCK_REAL(epoll_wait)(epfd, ev, cnt, timeout); - if (res > 0 && epfd >= 0) { + if (res > 0 && epfd >= 0) FdAcquire(thr, pc, epfd); - } - return res; -} - -TSAN_INTERCEPTOR(int, poll, void *fds, long_t nfds, int timeout) { - SCOPED_TSAN_INTERCEPTOR(poll, fds, nfds, timeout); - int res = BLOCK_REAL(poll)(fds, nfds, timeout); return res; } @@ -1662,7 +1632,7 @@ void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig, SignalContext *sctx = SigCtx(thr); // Don't mess with synchronous signals. if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL || - sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || + sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS || // If we are sending signal to ourselves, we must process it now. (sctx && sig == sctx->int_signal_send) || // If we are in blocking function, we can safely process it now @@ -1714,7 +1684,7 @@ TSAN_INTERCEPTOR(int, sigaction, int sig, sigaction_t *act, sigaction_t *old) { internal_memcpy(&sigactions[sig], act, sizeof(*act)); sigaction_t newact; internal_memcpy(&newact, act, sizeof(newact)); - sigfillset(&newact.sa_mask); + REAL(sigfillset)(&newact.sa_mask); if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL) { if (newact.sa_flags & SA_SIGINFO) newact.sa_sigaction = rtl_sigaction; @@ -1737,6 +1707,11 @@ TSAN_INTERCEPTOR(sighandler_t, signal, int sig, sighandler_t h) { return old.sa_handler; } +TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) { + SCOPED_TSAN_INTERCEPTOR(sigsuspend, mask); + return REAL(sigsuspend)(mask); +} + TSAN_INTERCEPTOR(int, raise, int sig) { SCOPED_TSAN_INTERCEPTOR(raise, sig); SignalContext *sctx = SigCtx(thr); @@ -1787,13 +1762,30 @@ TSAN_INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { return REAL(gettimeofday)(tv, tz); } +TSAN_INTERCEPTOR(int, getaddrinfo, void *node, void *service, + void *hints, void *rv) { + SCOPED_TSAN_INTERCEPTOR(getaddrinfo, node, service, hints, rv); + // We miss atomic synchronization in getaddrinfo, + // and can report false race between malloc and free + // inside of getaddrinfo. So ignore memory accesses. + ThreadIgnoreBegin(thr); + // getaddrinfo calls fopen, which can be intercepted by user. + thr->in_rtl--; + CHECK_EQ(thr->in_rtl, 0); + int res = REAL(getaddrinfo)(node, service, hints, rv); + thr->in_rtl++; + ThreadIgnoreEnd(thr); + return res; +} + // Linux kernel has a bug that leads to kernel deadlock if a process // maps TBs of memory and then calls mlock(). static void MlockIsUnsupported() { static atomic_uint8_t printed; if (atomic_exchange(&printed, 1, memory_order_relaxed)) return; - Printf("INFO: ThreadSanitizer ignores mlock/mlockall/munlock/munlockall\n"); + if (flags()->verbosity > 0) + Printf("INFO: ThreadSanitizer ignores mlock/mlockall/munlock/munlockall\n"); } TSAN_INTERCEPTOR(int, mlock, const void *addr, uptr len) { @@ -1817,8 +1809,7 @@ TSAN_INTERCEPTOR(int, munlockall, void) { } TSAN_INTERCEPTOR(int, fork, int fake) { - SCOPED_TSAN_INTERCEPTOR(fork, fake); - // It's intercepted merely to process pending signals. + SCOPED_INTERCEPTOR_RAW(fork, fake); int pid = REAL(fork)(fake); if (pid == 0) { // child @@ -1829,6 +1820,12 @@ TSAN_INTERCEPTOR(int, fork, int fake) { return pid; } +static int OnExit(ThreadState *thr) { + int status = Finalize(thr); + REAL(fflush)(0); + return status; +} + struct TsanInterceptorContext { ThreadState *thr; const uptr caller_pc; @@ -1839,39 +1836,150 @@ struct TsanInterceptorContext { // Causes interceptor recursion (getpwuid_r() calls fopen()) #undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS #undef SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS +// Causes interceptor recursion (getaddrinfo() and fopen()) +#undef SANITIZER_INTERCEPT_GETADDRINFO +#undef SANITIZER_INTERCEPT_GETNAMEINFO // Causes interceptor recursion (glob64() calls lstat64()) #undef SANITIZER_INTERCEPT_GLOB -#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ - MemoryAccessRange(((TsanInterceptorContext*)ctx)->thr, \ - ((TsanInterceptorContext*)ctx)->pc, \ - (uptr)ptr, size, true) -#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ - MemoryAccessRange(((TsanInterceptorContext*)ctx)->thr, \ - ((TsanInterceptorContext*)ctx)->pc, \ - (uptr)ptr, size, false) -#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ - SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__) \ - TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \ - ctx = (void*)&_ctx; \ - (void)ctx; +#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name) +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ + do { \ + } while (false) + +#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ + MemoryAccessRange(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)ptr, size, \ + true) + +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ + MemoryAccessRange(((TsanInterceptorContext *) ctx)->thr, \ + ((TsanInterceptorContext *) ctx)->pc, (uptr) ptr, size, \ + false) + +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__); \ + TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \ + ctx = (void *)&_ctx; \ + (void) ctx; + #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ - FdAcquire(((TsanInterceptorContext*)ctx)->thr, pc, fd) + FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd) + #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ - FdRelease(((TsanInterceptorContext*)ctx)->thr, pc, fd) + FdRelease(((TsanInterceptorContext *) ctx)->thr, pc, fd) + +#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) \ + FdAccess(((TsanInterceptorContext *) ctx)->thr, pc, fd) + +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + FdSocketAccept(((TsanInterceptorContext *) ctx)->thr, pc, fd, newfd) + #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ - ThreadSetName(((TsanInterceptorContext*)ctx)->thr, name) + ThreadSetName(((TsanInterceptorContext *) ctx)->thr, name) + +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + CTX()->thread_registry->SetThreadNameByUserId(thread, name) + +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) BLOCK_REAL(name) + +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) \ + OnExit(((TsanInterceptorContext *) ctx)->thr) + +#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) \ + MutexLock(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \ + MutexUnlock(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \ + MutexRepair(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + #include "sanitizer_common/sanitizer_common_interceptors.inc" -// FIXME: Implement these with MemoryAccessRange(). -#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) -#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) -#define COMMON_SYSCALL_POST_READ_RANGE(p, s) -#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) +#define TSAN_SYSCALL() \ + ThreadState *thr = cur_thread(); \ + ScopedSyscall scoped_syscall(thr) \ +/**/ + +struct ScopedSyscall { + ThreadState *thr; + + explicit ScopedSyscall(ThreadState *thr) + : thr(thr) { + if (thr->in_rtl == 0) + Initialize(thr); + thr->in_rtl++; + } + + ~ScopedSyscall() { + thr->in_rtl--; + if (thr->in_rtl == 0) + ProcessPendingSignals(thr); + } +}; + +static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) { + TSAN_SYSCALL(); + MemoryAccessRange(thr, pc, p, s, write); +} + +static void syscall_fd_close(uptr pc, int fd) { + TSAN_SYSCALL(); + if (fd >= 0) + FdClose(thr, pc, fd); +} + +static void syscall_pre_fork(uptr pc) { + TSAN_SYSCALL(); +} + +static void syscall_post_fork(uptr pc, int res) { + TSAN_SYSCALL(); + if (res == 0) { + // child + FdOnFork(thr, pc); + } else if (res > 0) { + // parent + } +} + +#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \ + syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ + syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), true) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#define COMMON_SYSCALL_FD_CLOSE(fd) syscall_fd_close(GET_CALLER_PC(), fd) +#define COMMON_SYSCALL_PRE_FORK() \ + syscall_pre_fork(GET_CALLER_PC()) +#define COMMON_SYSCALL_POST_FORK(res) \ + syscall_post_fork(GET_CALLER_PC(), res) #include "sanitizer_common/sanitizer_common_syscalls.inc" namespace __tsan { +static void finalize(void *arg) { + ThreadState *thr = cur_thread(); + uptr pc = 0; + atexit_ctx->exit(thr, pc); + int status = Finalize(thr); + REAL(fflush)(0); + if (status) + REAL(_exit)(status); +} + void ProcessPendingSignals(ThreadState *thr) { CHECK_EQ(thr->in_rtl, 0); SignalContext *sctx = SigCtx(thr); @@ -1881,8 +1989,8 @@ void ProcessPendingSignals(ThreadState *thr) { thr->in_signal_handler = true; sctx->pending_signal_count = 0; // These are too big for stack. - static THREADLOCAL sigset_t emptyset, oldset; - sigfillset(&emptyset); + static THREADLOCAL __sanitizer_sigset_t emptyset, oldset; + REAL(sigfillset)(&emptyset); pthread_sigmask(SIG_SETMASK, &emptyset, &oldset); for (int sig = 0; sig < kSigCount; sig++) { SignalDesc *signal = &sctx->pending_signals[sig]; @@ -1903,6 +2011,7 @@ void ProcessPendingSignals(ThreadState *thr) { uptr pc = signal->sigaction ? (uptr)sigactions[sig].sa_sigaction : (uptr)sigactions[sig].sa_handler; + pc += 1; // return address is expected, OutputReport() will undo this stack.Init(&pc, 1); ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeErrnoInSignal); @@ -1920,16 +2029,6 @@ void ProcessPendingSignals(ThreadState *thr) { thr->in_signal_handler = false; } -static void finalize(void *arg) { - ThreadState * thr = cur_thread(); - uptr pc = 0; - atexit_ctx->exit(thr, pc); - int status = Finalize(cur_thread()); - REAL(fflush)(0); - if (status) - _exit(status); -} - static void unreachable() { Printf("FATAL: ThreadSanitizer: unreachable called\n"); Die(); @@ -1973,7 +2072,6 @@ void InitializeInterceptors() { TSAN_INTERCEPT(strlen); TSAN_INTERCEPT(memset); TSAN_INTERCEPT(memcpy); - TSAN_INTERCEPT(strcmp); TSAN_INTERCEPT(memchr); TSAN_INTERCEPT(memrchr); TSAN_INTERCEPT(memmove); @@ -1981,10 +2079,10 @@ void InitializeInterceptors() { TSAN_INTERCEPT(strchr); TSAN_INTERCEPT(strchrnul); TSAN_INTERCEPT(strrchr); - TSAN_INTERCEPT(strncmp); TSAN_INTERCEPT(strcpy); // NOLINT TSAN_INTERCEPT(strncpy); TSAN_INTERCEPT(strstr); + TSAN_INTERCEPT(strdup); TSAN_INTERCEPT(pthread_create); TSAN_INTERCEPT(pthread_join); @@ -1992,10 +2090,8 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_mutex_init); TSAN_INTERCEPT(pthread_mutex_destroy); - TSAN_INTERCEPT(pthread_mutex_lock); TSAN_INTERCEPT(pthread_mutex_trylock); TSAN_INTERCEPT(pthread_mutex_timedlock); - TSAN_INTERCEPT(pthread_mutex_unlock); TSAN_INTERCEPT(pthread_spin_init); TSAN_INTERCEPT(pthread_spin_destroy); @@ -2013,12 +2109,8 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_rwlock_timedwrlock); TSAN_INTERCEPT(pthread_rwlock_unlock); - // TSAN_INTERCEPT(pthread_cond_init); - TSAN_INTERCEPT(pthread_cond_destroy); - TSAN_INTERCEPT(pthread_cond_signal); - TSAN_INTERCEPT(pthread_cond_broadcast); - TSAN_INTERCEPT(pthread_cond_wait); - TSAN_INTERCEPT(pthread_cond_timedwait); + INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2"); TSAN_INTERCEPT(pthread_barrier_init); TSAN_INTERCEPT(pthread_barrier_destroy); @@ -2062,8 +2154,6 @@ void InitializeInterceptors() { TSAN_INTERCEPT(connect); TSAN_INTERCEPT(bind); TSAN_INTERCEPT(listen); - TSAN_INTERCEPT(accept); - TSAN_INTERCEPT(accept4); TSAN_INTERCEPT(epoll_create); TSAN_INTERCEPT(epoll_create1); TSAN_INTERCEPT(close); @@ -2072,14 +2162,9 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pipe); TSAN_INTERCEPT(pipe2); - TSAN_INTERCEPT(readv); - TSAN_INTERCEPT(preadv64); - TSAN_INTERCEPT(writev); - TSAN_INTERCEPT(pwritev64); TSAN_INTERCEPT(send); TSAN_INTERCEPT(sendmsg); TSAN_INTERCEPT(recv); - TSAN_INTERCEPT(recvmsg); TSAN_INTERCEPT(unlink); TSAN_INTERCEPT(fopen); @@ -2095,10 +2180,10 @@ void InitializeInterceptors() { TSAN_INTERCEPT(epoll_ctl); TSAN_INTERCEPT(epoll_wait); - TSAN_INTERCEPT(poll); TSAN_INTERCEPT(sigaction); TSAN_INTERCEPT(signal); + TSAN_INTERCEPT(sigsuspend); TSAN_INTERCEPT(raise); TSAN_INTERCEPT(kill); TSAN_INTERCEPT(pthread_kill); @@ -2106,6 +2191,7 @@ void InitializeInterceptors() { TSAN_INTERCEPT(usleep); TSAN_INTERCEPT(nanosleep); TSAN_INTERCEPT(gettimeofday); + TSAN_INTERCEPT(getaddrinfo); TSAN_INTERCEPT(mlock); TSAN_INTERCEPT(munlock); @@ -2113,8 +2199,11 @@ void InitializeInterceptors() { TSAN_INTERCEPT(munlockall); TSAN_INTERCEPT(fork); + TSAN_INTERCEPT(dlopen); + TSAN_INTERCEPT(dlclose); TSAN_INTERCEPT(on_exit); TSAN_INTERCEPT(__cxa_atexit); + TSAN_INTERCEPT(_exit); // Need to setup it, because interceptors check that the function is resolved. // But atexit is emitted directly into the module, so can't be resolved. @@ -2136,9 +2225,15 @@ void InitializeInterceptors() { } void internal_start_thread(void(*func)(void *arg), void *arg) { + // Start the thread with signals blocked, otherwise it can steal users + // signals. + __sanitizer_kernel_sigset_t set, old; + internal_sigfillset(&set); + internal_sigprocmask(SIG_SETMASK, &set, &old); void *th; REAL(pthread_create)(&th, 0, (void*(*)(void *arg))func, arg); REAL(pthread_detach)(th); + internal_sigprocmask(SIG_SETMASK, &old, 0); } } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_interface.cc b/lib/tsan/rtl/tsan_interface.cc index efad8c192d6e..9de3808e79ff 100644 --- a/lib/tsan/rtl/tsan_interface.cc +++ b/lib/tsan/rtl/tsan_interface.cc @@ -38,49 +38,55 @@ void __tsan_write16(void *addr) { MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); } -u16 __tsan_unaligned_read2(void *addr) { +u16 __tsan_unaligned_read2(const uu16 *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, false, false); - return *(u16*)addr; + return *addr; } -u32 __tsan_unaligned_read4(void *addr) { +u32 __tsan_unaligned_read4(const uu32 *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, false, false); - return *(u32*)addr; + return *addr; } -u64 __tsan_unaligned_read8(void *addr) { +u64 __tsan_unaligned_read8(const uu64 *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, false, false); - return *(u64*)addr; + return *addr; } -void __tsan_unaligned_write2(void *addr, u16 v) { +void __tsan_unaligned_write2(uu16 *addr, u16 v) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, true, false); - *(u16*)addr = v; + *addr = v; } -void __tsan_unaligned_write4(void *addr, u32 v) { +void __tsan_unaligned_write4(uu32 *addr, u32 v) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, true, false); - *(u32*)addr = v; + *addr = v; } -void __tsan_unaligned_write8(void *addr, u64 v) { +void __tsan_unaligned_write8(uu64 *addr, u64 v) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, true, false); - *(u64*)addr = v; + *addr = v; } extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE uint16_t __sanitizer_unaligned_load16(void *addr) - ALIAS("__tsan_unaligned_read2") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_read2"); +SANITIZER_INTERFACE_ATTRIBUTE uint32_t __sanitizer_unaligned_load32(void *addr) - ALIAS("__tsan_unaligned_read4") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_read4"); +SANITIZER_INTERFACE_ATTRIBUTE uint64_t __sanitizer_unaligned_load64(void *addr) - ALIAS("__tsan_unaligned_read8") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_read8"); +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store16(void *addr, uint16_t v) - ALIAS("__tsan_unaligned_write2") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_write2"); +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store32(void *addr, uint32_t v) - ALIAS("__tsan_unaligned_write4") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_write4"); +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store64(void *addr, uint64_t v) - ALIAS("__tsan_unaligned_write8") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_write8"); } void __tsan_acquire(void *addr) { diff --git a/lib/tsan/rtl/tsan_interface.h b/lib/tsan/rtl/tsan_interface.h index 457fb55e0d2d..70450697d480 100644 --- a/lib/tsan/rtl/tsan_interface.h +++ b/lib/tsan/rtl/tsan_interface.h @@ -27,38 +27,38 @@ extern "C" { // This function should be called at the very beginning of the process, // before any instrumented code is executed and before any call to malloc. -void __tsan_init() SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_init(); -void __tsan_read1(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read16(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read1(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read2(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read4(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read8(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read16(void *addr); -void __tsan_write1(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write16(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write1(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write2(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write4(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write8(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write16(void *addr); -u16 __tsan_unaligned_read2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -u32 __tsan_unaligned_read4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -u64 __tsan_unaligned_read8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_unaligned_write2(void *addr, u16 v) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_unaligned_write4(void *addr, u32 v) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_unaligned_write8(void *addr, u64 v) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE u16 __tsan_unaligned_read2(const uu16 *addr); +SANITIZER_INTERFACE_ATTRIBUTE u32 __tsan_unaligned_read4(const uu32 *addr); +SANITIZER_INTERFACE_ATTRIBUTE u64 __tsan_unaligned_read8(const uu64 *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write2(uu16 *addr, u16 v); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(uu32 *addr, u32 v); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(uu64 *addr, u64 v); -void __tsan_vptr_read(void **vptr_p) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_vptr_update(void **vptr_p, void *new_val) - SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_read(void **vptr_p); +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_vptr_update(void **vptr_p, void *new_val); -void __tsan_func_entry(void *call_pc) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_func_exit() SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_entry(void *call_pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_exit(); -void __tsan_read_range(void *addr, unsigned long size) // NOLINT - SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write_range(void *addr, unsigned long size) // NOLINT - SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_read_range(void *addr, unsigned long size); // NOLINT +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_write_range(void *addr, unsigned long size); // NOLINT #ifdef __cplusplus } // extern "C" diff --git a/lib/tsan/rtl/tsan_interface_ann.cc b/lib/tsan/rtl/tsan_interface_ann.cc index 04b4b455d15e..cacbc0281d0f 100644 --- a/lib/tsan/rtl/tsan_interface_ann.cc +++ b/lib/tsan/rtl/tsan_interface_ann.cc @@ -13,6 +13,7 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" #include "tsan_interface_ann.h" #include "tsan_mutex.h" #include "tsan_report.h" @@ -229,12 +230,12 @@ using namespace __tsan; // NOLINT extern "C" { void INTERFACE_ATTRIBUTE AnnotateHappensBefore(char *f, int l, uptr addr) { SCOPED_ANNOTATION(AnnotateHappensBefore); - Release(cur_thread(), CALLERPC, addr); + Release(thr, pc, addr); } void INTERFACE_ATTRIBUTE AnnotateHappensAfter(char *f, int l, uptr addr) { SCOPED_ANNOTATION(AnnotateHappensAfter); - Acquire(cur_thread(), CALLERPC, addr); + Acquire(thr, pc, addr); } void INTERFACE_ATTRIBUTE AnnotateCondVarSignal(char *f, int l, uptr cv) { @@ -382,22 +383,32 @@ void INTERFACE_ATTRIBUTE AnnotateBenignRace( void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsBegin(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreReadsBegin); - IgnoreCtl(cur_thread(), false, true); + ThreadIgnoreBegin(thr); } void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsEnd(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreReadsEnd); - IgnoreCtl(cur_thread(), false, false); + ThreadIgnoreEnd(thr); } void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesBegin(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreWritesBegin); - IgnoreCtl(cur_thread(), true, true); + ThreadIgnoreBegin(thr); } void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesEnd(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreWritesEnd); - IgnoreCtl(thr, true, false); + ThreadIgnoreEnd(thr); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncBegin(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreSyncBegin); + ThreadIgnoreSyncBegin(thr); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncEnd(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreSyncEnd); + ThreadIgnoreSyncEnd(thr); } void INTERFACE_ATTRIBUTE AnnotatePublishMemoryRange( @@ -447,4 +458,7 @@ const char INTERFACE_ATTRIBUTE* ThreadSanitizerQuery(const char *query) { else return "0"; } + +void INTERFACE_ATTRIBUTE +AnnotateMemoryIsInitialized(char *f, int l, uptr mem, uptr sz) {} } // extern "C" diff --git a/lib/tsan/rtl/tsan_interface_ann.h b/lib/tsan/rtl/tsan_interface_ann.h index 8e45328e7ec1..963bcc55afaa 100644 --- a/lib/tsan/rtl/tsan_interface_ann.h +++ b/lib/tsan/rtl/tsan_interface_ann.h @@ -23,8 +23,8 @@ extern "C" { #endif -void __tsan_acquire(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_release(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_acquire(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_release(void *addr); #ifdef __cplusplus } // extern "C" diff --git a/lib/tsan/rtl/tsan_interface_atomic.cc b/lib/tsan/rtl/tsan_interface_atomic.cc index 80266969849a..d9f8cdf5b106 100644 --- a/lib/tsan/rtl/tsan_interface_atomic.cc +++ b/lib/tsan/rtl/tsan_interface_atomic.cc @@ -30,23 +30,37 @@ using namespace __tsan; // NOLINT #define SCOPED_ATOMIC(func, ...) \ const uptr callpc = (uptr)__builtin_return_address(0); \ uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ - pc = __sanitizer::StackTrace::GetPreviousInstructionPc(pc); \ mo = ConvertOrder(mo); \ mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \ ThreadState *const thr = cur_thread(); \ AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \ - ScopedAtomic sa(thr, callpc, __FUNCTION__); \ + ScopedAtomic sa(thr, callpc, a, mo, __FUNCTION__); \ return Atomic##func(thr, pc, __VA_ARGS__); \ /**/ +// Some shortcuts. +typedef __tsan_memory_order morder; +typedef __tsan_atomic8 a8; +typedef __tsan_atomic16 a16; +typedef __tsan_atomic32 a32; +typedef __tsan_atomic64 a64; +typedef __tsan_atomic128 a128; +const morder mo_relaxed = __tsan_memory_order_relaxed; +const morder mo_consume = __tsan_memory_order_consume; +const morder mo_acquire = __tsan_memory_order_acquire; +const morder mo_release = __tsan_memory_order_release; +const morder mo_acq_rel = __tsan_memory_order_acq_rel; +const morder mo_seq_cst = __tsan_memory_order_seq_cst; + class ScopedAtomic { public: - ScopedAtomic(ThreadState *thr, uptr pc, const char *func) + ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a, + morder mo, const char *func) : thr_(thr) { CHECK_EQ(thr_->in_rtl, 0); ProcessPendingSignals(thr); FuncEntry(thr_, pc); - DPrintf("#%d: %s\n", thr_->tid, func); + DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo); thr_->in_rtl++; } ~ScopedAtomic() { @@ -58,20 +72,6 @@ class ScopedAtomic { ThreadState *thr_; }; -// Some shortcuts. -typedef __tsan_memory_order morder; -typedef __tsan_atomic8 a8; -typedef __tsan_atomic16 a16; -typedef __tsan_atomic32 a32; -typedef __tsan_atomic64 a64; -typedef __tsan_atomic128 a128; -const morder mo_relaxed = __tsan_memory_order_relaxed; -const morder mo_consume = __tsan_memory_order_consume; -const morder mo_acquire = __tsan_memory_order_acquire; -const morder mo_release = __tsan_memory_order_release; -const morder mo_acq_rel = __tsan_memory_order_acq_rel; -const morder mo_seq_cst = __tsan_memory_order_seq_cst; - static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) { StatInc(thr, StatAtomic); StatInc(thr, t); @@ -251,11 +251,10 @@ static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a, // Assume the access is atomic. if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) { MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>()); - return *a; + return *a; // as if atomic } SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); T v = *a; s->mtx.ReadUnlock(); __sync_synchronize(); @@ -273,13 +272,15 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v, // Strictly saying even relaxed store cuts off release sequence, // so must reset the clock. if (!IsReleaseOrder(mo) && sizeof(T) <= sizeof(a)) { - *a = v; + *a = v; // as if atomic return; } __sync_synchronize(); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.ReleaseStore(&s->clock); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseImpl(thr, pc, &s->clock); *a = v; s->mtx.Unlock(); // Trainling memory barrier to provide sequential consistency @@ -293,13 +294,15 @@ static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) { SyncVar *s = 0; if (mo != mo_relaxed) { s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); if (IsAcqRelOrder(mo)) - thr->clock.acq_rel(&s->clock); + AcquireReleaseImpl(thr, pc, &s->clock); else if (IsReleaseOrder(mo)) - thr->clock.release(&s->clock); + ReleaseImpl(thr, pc, &s->clock); else if (IsAcquireOrder(mo)) - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); } v = F(a, v); if (s) @@ -357,13 +360,15 @@ static bool AtomicCAS(ThreadState *thr, uptr pc, SyncVar *s = 0; if (mo != mo_relaxed) { s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); if (IsAcqRelOrder(mo)) - thr->clock.acq_rel(&s->clock); + AcquireReleaseImpl(thr, pc, &s->clock); else if (IsReleaseOrder(mo)) - thr->clock.release(&s->clock); + ReleaseImpl(thr, pc, &s->clock); else if (IsAcquireOrder(mo)) - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); } T cc = *c; T pr = func_cas(a, cc, v); @@ -659,14 +664,14 @@ a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v, } #if __TSAN_HAS_INT128 -a128 __tsan_atomic64_compare_exchange_val(volatile a128 *a, a128 c, a128 v, +a128 __tsan_atomic128_compare_exchange_val(volatile a128 *a, a128 c, a128 v, morder mo, morder fmo) { SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); } #endif void __tsan_atomic_thread_fence(morder mo) { - char* a; + char* a = 0; SCOPED_ATOMIC(Fence, mo); } diff --git a/lib/tsan/rtl/tsan_interface_java.cc b/lib/tsan/rtl/tsan_interface_java.cc index 71e0747c3646..53f14cf07b59 100644 --- a/lib/tsan/rtl/tsan_interface_java.cc +++ b/lib/tsan/rtl/tsan_interface_java.cc @@ -17,6 +17,8 @@ #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_procmaps.h" using namespace __tsan; // NOLINT @@ -157,7 +159,7 @@ SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) { #define SCOPED_JAVA_FUNC(func) \ ThreadState *thr = cur_thread(); \ const uptr caller_pc = GET_CALLER_PC(); \ - const uptr pc = (uptr)&func; \ + const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ (void)pc; \ ScopedJavaFunc scoped(thr, caller_pc); \ /**/ diff --git a/lib/tsan/rtl/tsan_mman.cc b/lib/tsan/rtl/tsan_mman.cc index b6671b1abf09..8547f714be13 100644 --- a/lib/tsan/rtl/tsan_mman.cc +++ b/lib/tsan/rtl/tsan_mman.cc @@ -75,10 +75,12 @@ void InitializeAllocator() { void AllocatorThreadStart(ThreadState *thr) { allocator()->InitCache(&thr->alloc_cache); + internal_allocator()->InitCache(&thr->internal_alloc_cache); } void AllocatorThreadFinish(ThreadState *thr) { allocator()->DestroyCache(&thr->alloc_cache); + internal_allocator()->DestroyCache(&thr->internal_alloc_cache); } void AllocatorPrintStats() { @@ -102,14 +104,18 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) { void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) { CHECK_GT(thr->in_rtl, 0); if ((sz >= (1ull << 40)) || (align >= (1ull << 40))) - return 0; + return AllocatorReturnNull(); void *p = allocator()->Allocate(&thr->alloc_cache, sz, align); if (p == 0) return 0; MBlock *b = new(allocator()->GetMetaData(p)) MBlock; b->Init(sz, thr->tid, CurrentStackId(thr, pc)); - if (CTX() && CTX()->initialized) - MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); + if (CTX() && CTX()->initialized) { + if (thr->ignore_reads_and_writes == 0) + MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); + else + MemoryResetRange(thr, pc, (uptr)p, sz); + } DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p); SignalUnsafeCall(thr, pc); return p; @@ -132,8 +138,10 @@ void user_free(ThreadState *thr, uptr pc, void *p) { } b->ListReset(); } - if (CTX() && CTX()->initialized && thr->in_rtl == 1) - MemoryRangeFreed(thr, pc, (uptr)p, b->Size()); + if (CTX() && CTX()->initialized && thr->in_rtl == 1) { + if (thr->ignore_reads_and_writes == 0) + MemoryRangeFreed(thr, pc, (uptr)p, b->Size()); + } allocator()->Deallocate(&thr->alloc_cache, p); SignalUnsafeCall(thr, pc); } @@ -194,11 +202,12 @@ void invoke_free_hook(void *ptr) { void *internal_alloc(MBlockType typ, uptr sz) { ThreadState *thr = cur_thread(); CHECK_GT(thr->in_rtl, 0); + CHECK_LE(sz, InternalSizeClassMap::kMaxSize); if (thr->nomalloc) { thr->nomalloc = 0; // CHECK calls internal_malloc(). CHECK(0); } - return InternalAlloc(sz); + return InternalAlloc(sz, &thr->internal_alloc_cache); } void internal_free(void *p) { @@ -208,7 +217,7 @@ void internal_free(void *p) { thr->nomalloc = 0; // CHECK calls internal_malloc(). CHECK(0); } - InternalFree(p); + InternalFree(p, &thr->internal_alloc_cache); } } // namespace __tsan @@ -261,5 +270,6 @@ uptr __tsan_get_allocated_size(void *p) { void __tsan_on_thread_idle() { ThreadState *thr = cur_thread(); allocator()->SwallowCache(&thr->alloc_cache); + internal_allocator()->SwallowCache(&thr->internal_alloc_cache); } } // extern "C" diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h index 666b4d0c482f..32e22ba60434 100644 --- a/lib/tsan/rtl/tsan_platform.h +++ b/lib/tsan/rtl/tsan_platform.h @@ -134,17 +134,24 @@ static inline uptr AlternativeAddress(uptr addr) { void FlushShadowMemory(); void WriteMemoryProfile(char *buf, uptr buf_size); +uptr GetRSS(); const char *InitializePlatform(); void FinalizePlatform(); + +// The additional page is to catch shadow stack overflow as paging fault. +const uptr kTotalTraceSize = (kTraceSize * sizeof(Event) + sizeof(Trace) + 4096 + + 4095) & ~4095; + uptr ALWAYS_INLINE GetThreadTrace(int tid) { - uptr p = kTraceMemBegin + (uptr)(tid * 2) * kTraceSize * sizeof(Event); + uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize; DCHECK_LT(p, kTraceMemBegin + kTraceMemSize); return p; } uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) { - uptr p = kTraceMemBegin + (uptr)(tid * 2 + 1) * kTraceSize * sizeof(Event); + uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize + + kTraceSize * sizeof(Event); DCHECK_LT(p, kTraceMemBegin + kTraceMemSize); return p; } diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc index a0d71e8589d6..906b5dc73be4 100644 --- a/lib/tsan/rtl/tsan_platform_linux.cc +++ b/lib/tsan/rtl/tsan_platform_linux.cc @@ -19,6 +19,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" +#include "sanitizer_common/sanitizer_stoptheworld.h" #include "tsan_platform.h" #include "tsan_rtl.h" #include "tsan_flags.h" @@ -45,6 +46,14 @@ #include <resolv.h> #include <malloc.h> +#ifdef sa_handler +# undef sa_handler +#endif + +#ifdef sa_sigaction +# undef sa_sigaction +#endif + extern "C" struct mallinfo __libc_mallinfo(); namespace __tsan { @@ -72,70 +81,33 @@ ScopedInRtl::~ScopedInRtl() { } #endif -static bool ishex(char c) { - return (c >= '0' && c <= '9') - || (c >= 'a' && c <= 'f'); -} - -static uptr readhex(const char *p) { - uptr v = 0; - for (; ishex(p[0]); p++) { - if (p[0] >= '0' && p[0] <= '9') - v = v * 16 + p[0] - '0'; - else - v = v * 16 + p[0] - 'a' + 10; - } - return v; -} - -static uptr readdec(const char *p) { - uptr v = 0; - for (; p[0] >= '0' && p[0] <= '9' ; p++) - v = v * 10 + p[0] - '0'; - return v; +void FillProfileCallback(uptr start, uptr rss, bool file, + uptr *mem, uptr stats_size) { + CHECK_EQ(7, stats_size); + mem[6] += rss; // total + start >>= 40; + if (start < 0x10) // shadow + mem[0] += rss; + else if (start >= 0x20 && start < 0x30) // compat modules + mem[file ? 1 : 2] += rss; + else if (start >= 0x7e) // modules + mem[file ? 1 : 2] += rss; + else if (start >= 0x60 && start < 0x62) // traces + mem[3] += rss; + else if (start >= 0x7d && start < 0x7e) // heap + mem[4] += rss; + else // other + mem[5] += rss; } void WriteMemoryProfile(char *buf, uptr buf_size) { - char *smaps = 0; - uptr smaps_cap = 0; - uptr smaps_len = ReadFileToBuffer("/proc/self/smaps", - &smaps, &smaps_cap, 64<<20); - uptr mem[6] = {}; - uptr total = 0; - uptr start = 0; - bool file = false; - const char *pos = smaps; - while (pos < smaps + smaps_len) { - if (ishex(pos[0])) { - start = readhex(pos); - for (; *pos != '/' && *pos > '\n'; pos++) {} - file = *pos == '/'; - } else if (internal_strncmp(pos, "Rss:", 4) == 0) { - for (; *pos < '0' || *pos > '9'; pos++) {} - uptr rss = readdec(pos) * 1024; - total += rss; - start >>= 40; - if (start < 0x10) // shadow - mem[0] += rss; - else if (start >= 0x20 && start < 0x30) // compat modules - mem[file ? 1 : 2] += rss; - else if (start >= 0x7e) // modules - mem[file ? 1 : 2] += rss; - else if (start >= 0x60 && start < 0x62) // traces - mem[3] += rss; - else if (start >= 0x7d && start < 0x7e) // heap - mem[4] += rss; - else // other - mem[5] += rss; - } - while (*pos++ != '\n') {} - } - UnmapOrDie(smaps, smaps_cap); + uptr mem[7] = {}; + __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7); char *buf_pos = buf; char *buf_end = buf + buf_size; buf_pos += internal_snprintf(buf_pos, buf_end - buf_pos, "RSS %zd MB: shadow:%zd file:%zd mmap:%zd trace:%zd heap:%zd other:%zd\n", - total >> 20, mem[0] >> 20, mem[1] >> 20, mem[2] >> 20, + mem[6] >> 20, mem[0] >> 20, mem[1] >> 20, mem[2] >> 20, mem[3] >> 20, mem[4] >> 20, mem[5] >> 20); struct mallinfo mi = __libc_mallinfo(); buf_pos += internal_snprintf(buf_pos, buf_end - buf_pos, @@ -143,10 +115,23 @@ void WriteMemoryProfile(char *buf, uptr buf_size) { mi.arena >> 20, mi.hblkhd >> 20, mi.fordblks >> 20, mi.keepcost >> 20); } -void FlushShadowMemory() { +uptr GetRSS() { + uptr mem[7] = {}; + __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7); + return mem[6]; +} + + +void FlushShadowMemoryCallback( + const SuspendedThreadsList &suspended_threads_list, + void *argument) { FlushUnneededShadowMemory(kLinuxShadowBeg, kLinuxShadowEnd - kLinuxShadowBeg); } +void FlushShadowMemory() { + StopTheWorld(FlushShadowMemoryCallback, 0); +} + #ifndef TSAN_GO static void ProtectRange(uptr beg, uptr end) { ScopedInRtl in_rtl; diff --git a/lib/tsan/rtl/tsan_report.cc b/lib/tsan/rtl/tsan_report.cc index c95c5c86be69..66307ae09dcd 100644 --- a/lib/tsan/rtl/tsan_report.cc +++ b/lib/tsan/rtl/tsan_report.cc @@ -13,9 +13,27 @@ #include "tsan_report.h" #include "tsan_platform.h" #include "tsan_rtl.h" +#include "sanitizer_common/sanitizer_report_decorator.h" namespace __tsan { +class Decorator: private __sanitizer::AnsiColorDecorator { + public: + Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + const char *Warning() { return Red(); } + const char *EndWarning() { return Default(); } + const char *Access() { return Blue(); } + const char *EndAccess() { return Default(); } + const char *ThreadDescription() { return Cyan(); } + const char *EndThreadDescription() { return Default(); } + const char *Location() { return Green(); } + const char *EndLocation() { return Default(); } + const char *Sleep() { return Yellow(); } + const char *EndSleep() { return Default(); } + const char *Mutex() { return Magenta(); } + const char *EndMutex() { return Default(); } +}; + ReportDesc::ReportDesc() : stacks(MBlockReportStack) , mops(MBlockReportMop) @@ -97,26 +115,32 @@ static const char *MopDesc(bool first, bool write, bool atomic) { } static void PrintMop(const ReportMop *mop, bool first) { + Decorator d; char thrbuf[kThreadBufSize]; + Printf("%s", d.Access()); Printf(" %s of size %d at %p by %s", MopDesc(first, mop->write, mop->atomic), mop->size, (void*)mop->addr, thread_name(thrbuf, mop->tid)); PrintMutexSet(mop->mset); Printf(":\n"); + Printf("%s", d.EndAccess()); PrintStack(mop->stack); } static void PrintLocation(const ReportLocation *loc) { + Decorator d; char thrbuf[kThreadBufSize]; + bool print_stack = false; + Printf("%s", d.Location()); if (loc->type == ReportLocationGlobal) { - Printf(" Location is global '%s' of size %zu at %zx (%s+%p)\n\n", + Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n", loc->name, loc->size, loc->addr, loc->module, loc->offset); } else if (loc->type == ReportLocationHeap) { char thrbuf[kThreadBufSize]; Printf(" Location is heap block of size %zu at %p allocated by %s:\n", loc->size, loc->addr, thread_name(thrbuf, loc->tid)); - PrintStack(loc->stack); + print_stack = true; } else if (loc->type == ReportLocationStack) { Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid)); } else if (loc->type == ReportLocationTLS) { @@ -124,22 +148,32 @@ static void PrintLocation(const ReportLocation *loc) { } else if (loc->type == ReportLocationFD) { Printf(" Location is file descriptor %d created by %s at:\n", loc->fd, thread_name(thrbuf, loc->tid)); - PrintStack(loc->stack); + print_stack = true; } + Printf("%s", d.EndLocation()); + if (print_stack) + PrintStack(loc->stack); } static void PrintMutex(const ReportMutex *rm) { + Decorator d; if (rm->destroyed) { + Printf("%s", d.Mutex()); Printf(" Mutex M%llu is already destroyed.\n\n", rm->id); + Printf("%s", d.EndMutex()); } else { + Printf("%s", d.Mutex()); Printf(" Mutex M%llu created at:\n", rm->id); + Printf("%s", d.EndMutex()); PrintStack(rm->stack); } } static void PrintThread(const ReportThread *rt) { + Decorator d; if (rt->id == 0) // Little sense in describing the main thread. return; + Printf("%s", d.ThreadDescription()); Printf(" Thread T%d", rt->id); if (rt->name && rt->name[0] != '\0') Printf(" '%s'", rt->name); @@ -150,11 +184,15 @@ static void PrintThread(const ReportThread *rt) { if (rt->stack) Printf(" at:"); Printf("\n"); + Printf("%s", d.EndThreadDescription()); PrintStack(rt->stack); } static void PrintSleep(const ReportStack *s) { + Decorator d; + Printf("%s", d.Sleep()); Printf(" As if synchronized via sleep:\n"); + Printf("%s", d.EndSleep()); PrintStack(s); } @@ -177,10 +215,13 @@ ReportStack *SkipTsanInternalFrames(ReportStack *ent) { } void PrintReport(const ReportDesc *rep) { + Decorator d; Printf("==================\n"); const char *rep_typ_str = ReportTypeString(rep->typ); + Printf("%s", d.Warning()); Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str, (int)internal_getpid()); + Printf("%s", d.EndWarning()); for (uptr i = 0; i < rep->stacks.Size(); i++) { if (i) @@ -212,31 +253,37 @@ void PrintReport(const ReportDesc *rep) { Printf("==================\n"); } -#else +#else // #ifndef TSAN_GO + +const int kMainThreadId = 1; void PrintStack(const ReportStack *ent) { if (ent == 0) { - Printf(" [failed to restore the stack]\n\n"); + Printf(" [failed to restore the stack]\n"); return; } for (int i = 0; ent; ent = ent->next, i++) { Printf(" %s()\n %s:%d +0x%zx\n", ent->func, ent->file, ent->line, (void*)ent->offset); } - Printf("\n"); } static void PrintMop(const ReportMop *mop, bool first) { - Printf("%s by goroutine %d:\n", + Printf("\n"); + Printf("%s by ", (first ? (mop->write ? "Write" : "Read") - : (mop->write ? "Previous write" : "Previous read")), - mop->tid); + : (mop->write ? "Previous write" : "Previous read"))); + if (mop->tid == kMainThreadId) + Printf("main goroutine:\n"); + else + Printf("goroutine %d:\n", mop->tid); PrintStack(mop->stack); } static void PrintThread(const ReportThread *rt) { - if (rt->id == 0) // Little sense in describing the main thread. + if (rt->id == kMainThreadId) return; + Printf("\n"); Printf("Goroutine %d (%s) created at:\n", rt->id, rt->running ? "running" : "finished"); PrintStack(rt->stack); @@ -244,7 +291,7 @@ static void PrintThread(const ReportThread *rt) { void PrintReport(const ReportDesc *rep) { Printf("==================\n"); - Printf("WARNING: DATA RACE\n"); + Printf("WARNING: DATA RACE"); for (uptr i = 0; i < rep->mops.Size(); i++) PrintMop(rep->mops[i], i == 0); for (uptr i = 0; i < rep->threads.Size(); i++) diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc index 5924858c84c5..1f66d29a741c 100644 --- a/lib/tsan/rtl/tsan_rtl.cc +++ b/lib/tsan/rtl/tsan_rtl.cc @@ -39,9 +39,13 @@ THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64); static char ctx_placeholder[sizeof(Context)] ALIGNED(64); // Can be overriden by a front-end. -bool CPP_WEAK OnFinalize(bool failed) { +#ifdef TSAN_EXTERNAL_HOOKS +bool OnFinalize(bool failed); +#else +bool WEAK OnFinalize(bool failed) { return failed; } +#endif static Context *ctx; Context *CTX() { @@ -74,7 +78,7 @@ Context::Context() CreateThreadContext, kMaxTid, kThreadQuarantineSize)) , racy_stacks(MBlockRacyStacks) , racy_addresses(MBlockRacyAddresses) - , fired_suppressions(MBlockRacyAddresses) { + , fired_suppressions(8) { } // The objects are allocated in TLS, so one may rely on zero-initialization. @@ -86,7 +90,6 @@ ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, // they may be accessed before the ctor. // , ignore_reads_and_writes() // , in_rtl() - , shadow_stack_pos(&shadow_stack[0]) #ifndef TSAN_GO , jmp_bufs(MBlockJmpBuf) #endif @@ -130,17 +133,38 @@ static void BackgroundThread(void *arg) { } u64 last_flush = NanoTime(); + uptr last_rss = 0; for (int i = 0; ; i++) { SleepForSeconds(1); u64 now = NanoTime(); // Flush memory if requested. - if (flags()->flush_memory_ms) { + if (flags()->flush_memory_ms > 0) { if (last_flush + flags()->flush_memory_ms * kMs2Ns < now) { + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: periodic memory flush\n"); FlushShadowMemory(); last_flush = NanoTime(); } } + if (flags()->memory_limit_mb > 0) { + uptr rss = GetRSS(); + uptr limit = uptr(flags()->memory_limit_mb) << 20; + if (flags()->verbosity > 0) { + Printf("ThreadSanitizer: memory flush check" + " RSS=%llu LAST=%llu LIMIT=%llu\n", + (u64)rss>>20, (u64)last_rss>>20, (u64)limit>>20); + } + if (2 * rss > limit + last_rss) { + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: flushing memory due to RSS\n"); + FlushShadowMemory(); + rss = GetRSS(); + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20); + } + last_rss = rss; + } // Write memory profile if requested. if (mprof_fd != kInvalidFd) @@ -176,8 +200,10 @@ void MapThreadTrace(uptr addr, uptr size) { DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size); CHECK_GE(addr, kTraceMemBegin); CHECK_LE(addr + size, kTraceMemBegin + kTraceMemSize); - if (addr != (uptr)MmapFixedNoReserve(addr, size)) { - Printf("FATAL: ThreadSanitizer can not mmap thread trace\n"); + uptr addr1 = (uptr)MmapFixedNoReserve(addr, size); + if (addr1 != addr) { + Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p->%p)\n", + addr, size, addr1); Die(); } } @@ -206,23 +232,21 @@ void Initialize(ThreadState *thr) { #endif InitializeFlags(&ctx->flags, env); // Setup correct file descriptor for error reports. - if (internal_strcmp(flags()->log_path, "stdout") == 0) - __sanitizer_set_report_fd(kStdoutFd); - else if (internal_strcmp(flags()->log_path, "stderr") == 0) - __sanitizer_set_report_fd(kStderrFd); - else - __sanitizer_set_report_path(flags()->log_path); + __sanitizer_set_report_path(flags()->log_path); InitializeSuppressions(); #ifndef TSAN_GO + InitializeLibIgnore(); // Initialize external symbolizer before internal threads are started. const char *external_symbolizer = flags()->external_symbolizer_path; - if (external_symbolizer != 0 && external_symbolizer[0] != '\0') { - if (!InitializeExternalSymbolizer(external_symbolizer)) { - Printf("Failed to start external symbolizer: '%s'\n", - external_symbolizer); - Die(); - } + bool external_symbolizer_started = + Symbolizer::Init(external_symbolizer)->IsExternalAvailable(); + if (external_symbolizer != 0 && external_symbolizer[0] != '\0' && + !external_symbolizer_started) { + Printf("Failed to start external symbolizer: '%s'\n", + external_symbolizer); + Die(); } + Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer); #endif internal_start_thread(&BackgroundThread, 0); @@ -556,7 +580,9 @@ static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size, // Don't want to touch lots of shadow memory. // If a program maps 10MB stack, there is no need reset the whole range. size = (size + (kShadowCell - 1)) & ~(kShadowCell - 1); - if (size < 64*1024) { + // UnmapOrDie/MmapFixedNoReserve does not work on Windows, + // so we do it only for C/C++. + if (kGoMode || size < 64*1024) { u64 *p = (u64*)MemToShadow(addr); CHECK(IsShadowMem((uptr)p)); CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1))); @@ -636,9 +662,9 @@ void FuncEntry(ThreadState *thr, uptr pc) { // Shadow stack maintenance can be replaced with // stack unwinding during trace switch (which presumably must be faster). - DCHECK_GE(thr->shadow_stack_pos, &thr->shadow_stack[0]); + DCHECK_GE(thr->shadow_stack_pos, thr->shadow_stack); #ifndef TSAN_GO - DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]); + DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #else if (thr->shadow_stack_pos == thr->shadow_stack_end) { const int sz = thr->shadow_stack_end - thr->shadow_stack; @@ -664,23 +690,40 @@ void FuncExit(ThreadState *thr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeFuncExit, 0); - DCHECK_GT(thr->shadow_stack_pos, &thr->shadow_stack[0]); + DCHECK_GT(thr->shadow_stack_pos, thr->shadow_stack); #ifndef TSAN_GO - DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]); + DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #endif thr->shadow_stack_pos--; } -void IgnoreCtl(ThreadState *thr, bool write, bool begin) { - DPrintf("#%d: IgnoreCtl(%d, %d)\n", thr->tid, write, begin); - thr->ignore_reads_and_writes += begin ? 1 : -1; +void ThreadIgnoreBegin(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreBegin\n", thr->tid); + thr->ignore_reads_and_writes++; + CHECK_GT(thr->ignore_reads_and_writes, 0); + thr->fast_state.SetIgnoreBit(); +} + +void ThreadIgnoreEnd(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreEnd\n", thr->tid); + thr->ignore_reads_and_writes--; CHECK_GE(thr->ignore_reads_and_writes, 0); - if (thr->ignore_reads_and_writes) - thr->fast_state.SetIgnoreBit(); - else + if (thr->ignore_reads_and_writes == 0) thr->fast_state.ClearIgnoreBit(); } +void ThreadIgnoreSyncBegin(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid); + thr->ignore_sync++; + CHECK_GT(thr->ignore_sync, 0); +} + +void ThreadIgnoreSyncEnd(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreSyncEnd\n", thr->tid); + thr->ignore_sync--; + CHECK_GE(thr->ignore_sync, 0); +} + bool MD5Hash::operator==(const MD5Hash &other) const { return hash[0] == other.hash[0] && hash[1] == other.hash[1]; } diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h index f1a73e457331..4ee667543a6e 100644 --- a/lib/tsan/rtl/tsan_rtl.h +++ b/lib/tsan/rtl/tsan_rtl.h @@ -27,7 +27,10 @@ #define TSAN_RTL_H #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libignore.h" +#include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_thread_registry.h" #include "tsan_clock.h" #include "tsan_defs.h" @@ -409,21 +412,19 @@ struct ThreadState { // We do not distinguish beteween ignoring reads and writes // for better performance. int ignore_reads_and_writes; + int ignore_sync; + // C/C++ uses fixed size shadow stack embed into Trace. + // Go uses malloc-allocated shadow stack with dynamic size. + uptr *shadow_stack; + uptr *shadow_stack_end; uptr *shadow_stack_pos; u64 *racy_shadow_addr; u64 racy_state[2]; -#ifndef TSAN_GO - // C/C++ uses embed shadow stack of fixed size. - uptr shadow_stack[kShadowStackSize]; -#else - // Go uses satellite shadow stack with dynamic size. - uptr *shadow_stack; - uptr *shadow_stack_end; -#endif MutexSet mset; ThreadClock clock; #ifndef TSAN_GO AllocatorCache alloc_cache; + InternalAllocatorCache internal_alloc_cache; Vector<JmpBuf> jmp_bufs; #endif u64 stat[StatCnt]; @@ -431,6 +432,7 @@ struct ThreadState { const int unique_id; int in_rtl; bool in_symbolizer; + bool in_ignored_lib; bool is_alive; bool is_freeing; bool is_vptr_access; @@ -531,7 +533,8 @@ struct Context { Vector<RacyStacks> racy_stacks; Vector<RacyAddress> racy_addresses; - Vector<FiredSuppression> fired_suppressions; + // Number of fired suppressions may be large enough. + InternalMmapVector<FiredSuppression> fired_suppressions; Flags flags; @@ -594,13 +597,15 @@ void MapThreadTrace(uptr addr, uptr size); void DontNeedShadowFor(uptr addr, uptr size); void InitializeShadowMemory(); void InitializeInterceptors(); +void InitializeLibIgnore(); void InitializeDynamicAnnotations(); void ReportRace(ThreadState *thr); bool OutputReport(Context *ctx, const ScopedReport &srep, const ReportStack *suppress_stack1 = 0, - const ReportStack *suppress_stack2 = 0); + const ReportStack *suppress_stack2 = 0, + const ReportLocation *suppress_loc = 0); bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, const StackTrace &trace); @@ -672,7 +677,11 @@ void ALWAYS_INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc, void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size); -void IgnoreCtl(ThreadState *thr, bool write, bool begin); + +void ThreadIgnoreBegin(ThreadState *thr); +void ThreadIgnoreEnd(ThreadState *thr); +void ThreadIgnoreSyncBegin(ThreadState *thr); +void ThreadIgnoreSyncEnd(ThreadState *thr); void FuncEntry(ThreadState *thr, uptr pc); void FuncExit(ThreadState *thr); @@ -696,12 +705,17 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all = false); void MutexReadLock(ThreadState *thr, uptr pc, uptr addr); void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr); +void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD void Acquire(ThreadState *thr, uptr pc, uptr addr); void AcquireGlobal(ThreadState *thr, uptr pc); void Release(ThreadState *thr, uptr pc, uptr addr); void ReleaseStore(ThreadState *thr, uptr pc, uptr addr); void AfterSleep(ThreadState *thr, uptr pc); +void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c); +void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); +void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c); +void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); // The hacky call uses custom calling convention and an assembly thunk. // It is considerably faster that a normal call for the caller @@ -714,11 +728,11 @@ void AfterSleep(ThreadState *thr, uptr pc); // so we create a reserve stack frame for it (1024b must be enough). #define HACKY_CALL(f) \ __asm__ __volatile__("sub $1024, %%rsp;" \ - "/*.cfi_adjust_cfa_offset 1024;*/" \ + ".cfi_adjust_cfa_offset 1024;" \ ".hidden " #f "_thunk;" \ "call " #f "_thunk;" \ "add $1024, %%rsp;" \ - "/*.cfi_adjust_cfa_offset -1024;*/" \ + ".cfi_adjust_cfa_offset -1024;" \ ::: "memory", "cc"); #else #define HACKY_CALL(f) f() diff --git a/lib/tsan/rtl/tsan_rtl_mutex.cc b/lib/tsan/rtl/tsan_rtl_mutex.cc index cf2e44dd09ee..409391678f01 100644 --- a/lib/tsan/rtl/tsan_rtl_mutex.cc +++ b/lib/tsan/rtl/tsan_rtl_mutex.cc @@ -95,16 +95,13 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) { } else if (s->owner_tid == thr->tid) { CHECK_GT(s->recursion, 0); } else { - Printf("ThreadSanitizer WARNING: double lock\n"); + Printf("ThreadSanitizer WARNING: double lock of mutex %p\n", addr); PrintCurrentStack(thr, pc); } if (s->recursion == 0) { StatInc(thr, StatMutexLock); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); - StatInc(thr, StatSyncAcquire); - thr->clock.acquire(&s->read_clock); - StatInc(thr, StatSyncAcquire); + AcquireImpl(thr, pc, &s->clock); + AcquireImpl(thr, pc, &s->read_clock); } else if (!s->is_recursive) { StatInc(thr, StatMutexRecLock); } @@ -125,13 +122,14 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { if (s->recursion == 0) { if (!s->is_broken) { s->is_broken = true; - Printf("ThreadSanitizer WARNING: unlock of unlocked mutex\n"); + Printf("ThreadSanitizer WARNING: unlock of unlocked mutex %p\n", addr); PrintCurrentStack(thr, pc); } } else if (s->owner_tid != thr->tid) { if (!s->is_broken) { s->is_broken = true; - Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n"); + Printf("ThreadSanitizer WARNING: mutex %p is unlocked by wrong thread\n", + addr); PrintCurrentStack(thr, pc); } } else { @@ -140,10 +138,7 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); s->owner_tid = SyncVar::kInvalidTid; - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.ReleaseStore(&s->clock); - StatInc(thr, StatSyncRelease); + ReleaseStoreImpl(thr, pc, &s->clock); } else { StatInc(thr, StatMutexRecUnlock); } @@ -163,13 +158,12 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId()); if (s->owner_tid != SyncVar::kInvalidTid) { - Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n"); + Printf("ThreadSanitizer WARNING: read lock of a write locked mutex %p\n", + addr); PrintCurrentStack(thr, pc); } - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); s->last_lock = thr->fast_state.raw(); - StatInc(thr, StatSyncAcquire); thr->mset.Add(s->GetId(), false, thr->fast_state.epoch()); s->mtx.ReadUnlock(); } @@ -184,14 +178,11 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); if (s->owner_tid != SyncVar::kInvalidTid) { - Printf("ThreadSanitizer WARNING: read unlock of a write " - "locked mutex\n"); + Printf("ThreadSanitizer WARNING: read unlock of a write locked mutex %p\n", + addr); PrintCurrentStack(thr, pc); } - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&s->read_clock); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, pc, &s->read_clock); s->mtx.Unlock(); thr->mset.Del(s->GetId(), false); } @@ -209,10 +200,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { StatInc(thr, StatMutexReadUnlock); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&s->read_clock); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, pc, &s->read_clock); } else if (s->owner_tid == thr->tid) { // Seems to be write unlock. thr->fast_state.IncrementEpoch(); @@ -222,33 +210,37 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); s->owner_tid = SyncVar::kInvalidTid; - // FIXME: Refactor me, plz. - // The sequence of events is quite tricky and doubled in several places. - // First, it's a bug to increment the epoch w/o writing to the trace. - // Then, the acquire/release logic can be factored out as well. - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.ReleaseStore(&s->clock); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, pc, &s->clock); } else { StatInc(thr, StatMutexRecUnlock); } } else if (!s->is_broken) { s->is_broken = true; - Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n"); + Printf("ThreadSanitizer WARNING: mutex %p is unlock by wrong thread\n", + addr); PrintCurrentStack(thr, pc); } thr->mset.Del(s->GetId(), write); s->mtx.Unlock(); } +void MutexRepair(ThreadState *thr, uptr pc, uptr addr) { + Context *ctx = CTX(); + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr); + SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true); + s->owner_tid = SyncVar::kInvalidTid; + s->recursion = 0; + s->mtx.Unlock(); +} + void Acquire(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: Acquire %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); - StatInc(thr, StatSyncAcquire); + AcquireImpl(thr, pc, &s->clock); s->mtx.ReadUnlock(); } @@ -262,6 +254,9 @@ static void UpdateClockCallback(ThreadContextBase *tctx_base, void *arg) { } void AcquireGlobal(ThreadState *thr, uptr pc) { + DPrintf("#%d: AcquireGlobal\n", thr->tid); + if (thr->ignore_sync) + return; ThreadRegistryLock l(CTX()->thread_registry); CTX()->thread_registry->RunCallbackForEachThreadLocked( UpdateClockCallback, thr); @@ -270,20 +265,26 @@ void AcquireGlobal(ThreadState *thr, uptr pc) { void Release(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: Release %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.release(&s->clock); - StatInc(thr, StatSyncRelease); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseImpl(thr, pc, &s->clock); s->mtx.Unlock(); } void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.ReleaseStore(&s->clock); - StatInc(thr, StatSyncRelease); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseStoreImpl(thr, pc, &s->clock); s->mtx.Unlock(); } @@ -298,6 +299,9 @@ static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) { } void AfterSleep(ThreadState *thr, uptr pc) { + DPrintf("#%d: AfterSleep %zx\n", thr->tid); + if (thr->ignore_sync) + return; thr->last_sleep_stack_id = CurrentStackId(thr, pc); ThreadRegistryLock l(CTX()->thread_registry); CTX()->thread_registry->RunCallbackForEachThreadLocked( @@ -305,4 +309,40 @@ void AfterSleep(ThreadState *thr, uptr pc) { } #endif +void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->clock.acquire(c); + StatInc(thr, StatSyncAcquire); +} + +void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.release(c); + StatInc(thr, StatSyncRelease); +} + +void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.ReleaseStore(c); + StatInc(thr, StatSyncRelease); +} + +void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.acq_rel(c); + StatInc(thr, StatSyncAcquire); + StatInc(thr, StatSyncRelease); +} + } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_rtl_report.cc b/lib/tsan/rtl/tsan_rtl_report.cc index f77a7a2efa96..4fed43faf25f 100644 --- a/lib/tsan/rtl/tsan_rtl_report.cc +++ b/lib/tsan/rtl/tsan_rtl_report.cc @@ -95,8 +95,9 @@ static void StackStripMain(ReportStack *stack) { DPrintf("Bottom stack frame of stack %zx is missed\n", stack->pc); } #else - if (last && 0 == internal_strcmp(last, "schedunlock")) - last_frame2->next = 0; + // The last frame always point into runtime (gosched0, goexit0, runtime.main). + last_frame2->next = 0; + (void)last; #endif } @@ -105,17 +106,25 @@ static ReportStack *SymbolizeStack(const StackTrace& trace) { return 0; ReportStack *stack = 0; for (uptr si = 0; si < trace.Size(); si++) { + const uptr pc = trace.Get(si); +#ifndef TSAN_GO // We obtain the return address, that is, address of the next instruction, // so offset it by 1 byte. - bool is_last = (si == trace.Size() - 1); - ReportStack *ent = SymbolizeCode(trace.Get(si) - !is_last); + const uptr pc1 = __sanitizer::StackTrace::GetPreviousInstructionPc(pc); +#else + // FIXME(dvyukov): Go sometimes uses address of a function as top pc. + uptr pc1 = pc; + if (si != trace.Size() - 1) + pc1 -= 1; +#endif + ReportStack *ent = SymbolizeCode(pc1); CHECK_NE(ent, 0); ReportStack *last = ent; while (last->next) { - last->pc += !is_last; + last->pc = pc; // restore original pc for report last = last->next; } - last->pc += !is_last; + last->pc = pc; // restore original pc for report last->next = stack; stack = ent; } @@ -401,7 +410,7 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) { const u64 ebegin = RoundDown(eend, kTracePartSize); DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n", tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx); - InternalScopedBuffer<uptr> stack(1024); // FIXME: de-hardcode 1024 + InternalScopedBuffer<uptr> stack(kShadowStackSize); for (uptr i = 0; i < hdr->stack0.Size(); i++) { stack[i] = hdr->stack0.Get(i); DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]); @@ -501,28 +510,33 @@ static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2], bool OutputReport(Context *ctx, const ScopedReport &srep, const ReportStack *suppress_stack1, - const ReportStack *suppress_stack2) { + const ReportStack *suppress_stack2, + const ReportLocation *suppress_loc) { atomic_store(&ctx->last_symbolize_time_ns, NanoTime(), memory_order_relaxed); const ReportDesc *rep = srep.GetReport(); Suppression *supp = 0; uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack1, &supp); if (suppress_pc == 0) suppress_pc = IsSuppressed(rep->typ, suppress_stack2, &supp); + if (suppress_pc == 0) + suppress_pc = IsSuppressed(rep->typ, suppress_loc, &supp); if (suppress_pc != 0) { FiredSuppression s = {srep.GetReport()->typ, suppress_pc, supp}; - ctx->fired_suppressions.PushBack(s); + ctx->fired_suppressions.push_back(s); } if (OnReport(rep, suppress_pc != 0)) return false; PrintReport(rep); - CTX()->nreported++; + ctx->nreported++; + if (flags()->halt_on_error) + internal__exit(flags()->exitcode); return true; } bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, const StackTrace &trace) { - for (uptr k = 0; k < ctx->fired_suppressions.Size(); k++) { + for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) continue; for (uptr j = 0; j < trace.Size(); j++) { @@ -537,6 +551,22 @@ bool IsFiredSuppression(Context *ctx, return false; } +static bool IsFiredSuppression(Context *ctx, + const ScopedReport &srep, + uptr addr) { + for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { + if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) + continue; + FiredSuppression *s = &ctx->fired_suppressions[k]; + if (addr == s->pc) { + if (s->supp) + s->supp->hit_count++; + return true; + } + } + return false; +} + bool FrameIsInternal(const ReportStack *frame) { return frame != 0 && frame->file != 0 && (internal_strstr(frame->file, "tsan_interceptors.cc") || @@ -569,7 +599,7 @@ static bool IsJavaNonsense(const ReportDesc *rep) { && frame->module == 0)) { if (frame) { FiredSuppression supp = {rep->typ, frame->pc, 0}; - CTX()->fired_suppressions.PushBack(supp); + CTX()->fired_suppressions.push_back(supp); } return true; } @@ -630,6 +660,8 @@ void ReportRace(ThreadState *thr) { else if (freed) typ = ReportTypeUseAfterFree; ScopedReport rep(typ); + if (IsFiredSuppression(ctx, rep, addr)) + return; const uptr kMop = 2; StackTrace traces[kMop]; const uptr toppc = TraceTopPC(thr); @@ -640,6 +672,8 @@ void ReportRace(ThreadState *thr) { new(mset2.data()) MutexSet(); Shadow s2(thr->racy_state[1]); RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2.data()); + if (IsFiredSuppression(ctx, rep, traces[1])) + return; if (HandleRacyStacks(thr, traces, addr_min, addr_max)) return; @@ -672,8 +706,11 @@ void ReportRace(ThreadState *thr) { } #endif + ReportLocation *suppress_loc = rep.GetReport()->locs.Size() ? + rep.GetReport()->locs[0] : 0; if (!OutputReport(ctx, rep, rep.GetReport()->mops[0]->stack, - rep.GetReport()->mops[1]->stack)) + rep.GetReport()->mops[1]->stack, + suppress_loc)) return; AddRacyStacks(thr, traces, addr_min, addr_max); @@ -689,8 +726,8 @@ void PrintCurrentStackSlow() { #ifndef TSAN_GO __sanitizer::StackTrace *ptrace = new(internal_alloc(MBlockStackTrace, sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace; - ptrace->SlowUnwindStack(__sanitizer::StackTrace::GetCurrentPc(), - kStackTraceMax); + ptrace->Unwind(kStackTraceMax, __sanitizer::StackTrace::GetCurrentPc(), + 0, 0, 0, false); for (uptr i = 0; i < ptrace->size / 2; i++) { uptr tmp = ptrace->trace[i]; ptrace->trace[i] = ptrace->trace[ptrace->size - i - 1]; diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc index ee13fa18db3f..4e451b042947 100644 --- a/lib/tsan/rtl/tsan_rtl_thread.cc +++ b/lib/tsan/rtl/tsan_rtl_thread.cc @@ -41,8 +41,7 @@ void ThreadContext::OnDead() { void ThreadContext::OnJoined(void *arg) { ThreadState *caller_thr = static_cast<ThreadState *>(arg); - caller_thr->clock.acquire(&sync); - StatInc(caller_thr, StatSyncAcquire); + AcquireImpl(caller_thr, 0, &sync); sync.Reset(); } @@ -59,10 +58,7 @@ void ThreadContext::OnCreated(void *arg) { args->thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0); - args->thr->clock.set(args->thr->tid, args->thr->fast_state.epoch()); - args->thr->fast_synch_epoch = args->thr->fast_state.epoch(); - args->thr->clock.release(&sync); - StatInc(args->thr, StatSyncRelease); + ReleaseImpl(args->thr, 0, &sync); #ifdef TSAN_GO creation_stack.ObtainCurrent(args->thr, args->pc); #else @@ -95,21 +91,23 @@ void ThreadContext::OnStarted(void *arg) { epoch1 = (u64)-1; new(thr) ThreadState(CTX(), tid, unique_id, epoch0, args->stk_addr, args->stk_size, args->tls_addr, args->tls_size); -#ifdef TSAN_GO +#ifndef TSAN_GO + thr->shadow_stack = &ThreadTrace(thr->tid)->shadow_stack[0]; + thr->shadow_stack_pos = thr->shadow_stack; + thr->shadow_stack_end = thr->shadow_stack + kShadowStackSize; +#else // Setup dynamic shadow stack. const int kInitStackSize = 8; - args->thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, + thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, kInitStackSize * sizeof(uptr)); - args->thr->shadow_stack_pos = thr->shadow_stack; - args->thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; + thr->shadow_stack_pos = thr->shadow_stack; + thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; #endif #ifndef TSAN_GO - AllocatorThreadStart(args->thr); + AllocatorThreadStart(thr); #endif - thr = args->thr; thr->fast_synch_epoch = epoch0; - thr->clock.set(tid, epoch0); - thr->clock.acquire(&sync); + AcquireImpl(thr, 0, &sync); thr->fast_state.SetHistorySize(flags()->history_size); const uptr trace = (epoch0 / kTracePartSize) % TraceParts(); Trace *thr_trace = ThreadTrace(thr->tid); @@ -128,10 +126,7 @@ void ThreadContext::OnFinished() { thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&sync); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, 0, &sync); } epoch1 = thr->fast_state.epoch(); @@ -170,6 +165,10 @@ static void ThreadCheckIgnore(ThreadState *thr) { Printf("ThreadSanitizer: thread T%d finished with ignores enabled.\n", thr->tid); } + if (thr->ignore_sync) { + Printf("ThreadSanitizer: thread T%d finished with sync ignores enabled.\n", + thr->tid); + } } void ThreadFinalize(ThreadState *thr) { @@ -374,25 +373,4 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, } } -void MemoryAccessRangeStep(ThreadState *thr, uptr pc, uptr addr, - uptr size, uptr step, bool is_write) { - if (size == 0) - return; - FastState fast_state = thr->fast_state; - if (fast_state.GetIgnoreBit()) - return; - StatInc(thr, StatMopRange); - fast_state.IncrementEpoch(); - thr->fast_state = fast_state; - TraceAddEvent(thr, fast_state, EventTypeMop, pc); - - for (uptr addr_end = addr + size; addr < addr_end; addr += step) { - u64 *shadow_mem = (u64*)MemToShadow(addr); - Shadow cur(fast_state); - cur.SetWrite(is_write); - cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kSizeLog1); - MemoryAccessImpl(thr, addr, kSizeLog1, is_write, false, - shadow_mem, cur); - } -} } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc index 9676e0872e08..f9dbf121cef4 100644 --- a/lib/tsan/rtl/tsan_stat.cc +++ b/lib/tsan/rtl/tsan_stat.cc @@ -138,9 +138,11 @@ void StatOutput(u64 *stat) { name[StatInt_strcpy] = " strcpy "; name[StatInt_strncpy] = " strncpy "; name[StatInt_strstr] = " strstr "; + name[StatInt_strdup] = " strdup "; name[StatInt_strcasecmp] = " strcasecmp "; name[StatInt_strncasecmp] = " strncasecmp "; name[StatInt_atexit] = " atexit "; + name[StatInt__exit] = " _exit "; name[StatInt___cxa_guard_acquire] = " __cxa_guard_acquire "; name[StatInt___cxa_guard_release] = " __cxa_guard_release "; name[StatInt___cxa_guard_abort] = " __cxa_guard_abort "; @@ -180,6 +182,7 @@ void StatOutput(u64 *stat) { name[StatInt_pthread_barrier_wait] = " pthread_barrier_wait "; name[StatInt_pthread_once] = " pthread_once "; name[StatInt_pthread_getschedparam] = " pthread_getschedparam "; + name[StatInt_pthread_setname_np] = " pthread_setname_np "; name[StatInt_sem_init] = " sem_init "; name[StatInt_sem_destroy] = " sem_destroy "; name[StatInt_sem_wait] = " sem_wait "; @@ -229,11 +232,13 @@ void StatOutput(u64 *stat) { name[StatInt_pread] = " pread "; name[StatInt_pread64] = " pread64 "; name[StatInt_readv] = " readv "; + name[StatInt_preadv] = " preadv "; name[StatInt_preadv64] = " preadv64 "; name[StatInt_write] = " write "; name[StatInt_pwrite] = " pwrite "; name[StatInt_pwrite64] = " pwrite64 "; name[StatInt_writev] = " writev "; + name[StatInt_pwritev] = " pwritev "; name[StatInt_pwritev64] = " pwritev64 "; name[StatInt_send] = " send "; name[StatInt_sendmsg] = " sendmsg "; @@ -253,8 +258,10 @@ void StatOutput(u64 *stat) { name[StatInt_epoll_ctl] = " epoll_ctl "; name[StatInt_epoll_wait] = " epoll_wait "; name[StatInt_poll] = " poll "; + name[StatInt_ppoll] = " ppoll "; name[StatInt_sigaction] = " sigaction "; name[StatInt_signal] = " signal "; + name[StatInt_sigsuspend] = " sigsuspend "; name[StatInt_raise] = " raise "; name[StatInt_kill] = " kill "; name[StatInt_pthread_kill] = " pthread_kill "; @@ -285,6 +292,7 @@ void StatOutput(u64 *stat) { name[StatInt_ctime_r] = " ctime_r "; name[StatInt_asctime] = " asctime "; name[StatInt_asctime_r] = " asctime_r "; + name[StatInt_strptime] = " strptime "; name[StatInt_frexp] = " frexp "; name[StatInt_frexpf] = " frexpf "; name[StatInt_frexpl] = " frexpl "; @@ -311,7 +319,9 @@ void StatOutput(u64 *stat) { name[StatInt_wait4] = " wait4 "; name[StatInt_inet_ntop] = " inet_ntop "; name[StatInt_inet_pton] = " inet_pton "; + name[StatInt_inet_aton] = " inet_aton "; name[StatInt_getaddrinfo] = " getaddrinfo "; + name[StatInt_getnameinfo] = " getnameinfo "; name[StatInt_getsockname] = " getsockname "; name[StatInt_gethostent] = " gethostent "; name[StatInt_gethostbyname] = " gethostbyname "; @@ -322,6 +332,99 @@ void StatOutput(u64 *stat) { name[StatInt_gethostbyname2_r] = " gethostbyname2_r "; name[StatInt_gethostbyaddr_r] = " gethostbyaddr_r "; name[StatInt_getsockopt] = " getsockopt "; + name[StatInt_modf] = " modf "; + name[StatInt_modff] = " modff "; + name[StatInt_modfl] = " modfl "; + name[StatInt_getpeername] = " getpeername "; + name[StatInt_ioctl] = " ioctl "; + name[StatInt_sysinfo] = " sysinfo "; + name[StatInt_readdir] = " readdir "; + name[StatInt_readdir64] = " readdir64 "; + name[StatInt_readdir_r] = " readdir_r "; + name[StatInt_readdir64_r] = " readdir64_r "; + name[StatInt_ptrace] = " ptrace "; + name[StatInt_setlocale] = " setlocale "; + name[StatInt_getcwd] = " getcwd "; + name[StatInt_get_current_dir_name] = " get_current_dir_name "; + name[StatInt_strtoimax] = " strtoimax "; + name[StatInt_strtoumax] = " strtoumax "; + name[StatInt_mbstowcs] = " mbstowcs "; + name[StatInt_mbsrtowcs] = " mbsrtowcs "; + name[StatInt_mbsnrtowcs] = " mbsnrtowcs "; + name[StatInt_wcstombs] = " wcstombs "; + name[StatInt_wcsrtombs] = " wcsrtombs "; + name[StatInt_wcsnrtombs] = " wcsnrtombs "; + name[StatInt_tcgetattr] = " tcgetattr "; + name[StatInt_realpath] = " realpath "; + name[StatInt_canonicalize_file_name] = " canonicalize_file_name "; + name[StatInt_confstr] = " confstr "; + name[StatInt_sched_getaffinity] = " sched_getaffinity "; + name[StatInt_strerror] = " strerror "; + name[StatInt_strerror_r] = " strerror_r "; + name[StatInt_scandir] = " scandir "; + name[StatInt_scandir64] = " scandir64 "; + name[StatInt_getgroups] = " getgroups "; + name[StatInt_wordexp] = " wordexp "; + name[StatInt_sigwait] = " sigwait "; + name[StatInt_sigwaitinfo] = " sigwaitinfo "; + name[StatInt_sigtimedwait] = " sigtimedwait "; + name[StatInt_sigemptyset] = " sigemptyset "; + name[StatInt_sigfillset] = " sigfillset "; + name[StatInt_sigpending] = " sigpending "; + name[StatInt_sigprocmask] = " sigprocmask "; + name[StatInt_backtrace] = " backtrace "; + name[StatInt_backtrace_symbols] = " backtrace_symbols "; + name[StatInt_dlopen] = " dlopen "; + name[StatInt_dlclose] = " dlclose "; + name[StatInt_getmntent] = " getmntent "; + name[StatInt_getmntent_r] = " getmntent_r "; + name[StatInt_statfs] = " statfs "; + name[StatInt_statfs64] = " statfs64 "; + name[StatInt_fstatfs] = " fstatfs "; + name[StatInt_fstatfs64] = " fstatfs64 "; + name[StatInt_statvfs] = " statvfs "; + name[StatInt_statvfs64] = " statvfs64 "; + name[StatInt_fstatvfs] = " fstatvfs "; + name[StatInt_fstatvfs64] = " fstatvfs64 "; + name[StatInt_initgroups] = " initgroups "; + name[StatInt_ether_ntoa] = " ether_ntoa "; + name[StatInt_ether_aton] = " ether_aton "; + name[StatInt_ether_ntoa_r] = " ether_ntoa_r "; + name[StatInt_ether_aton_r] = " ether_aton_r "; + name[StatInt_ether_ntohost] = " ether_ntohost "; + name[StatInt_ether_hostton] = " ether_hostton "; + name[StatInt_ether_line] = " ether_line "; + name[StatInt_shmctl] = " shmctl "; + name[StatInt_random_r] = " random_r "; + name[StatInt_tmpnam] = " tmpnam "; + name[StatInt_tmpnam_r] = " tmpnam_r "; + name[StatInt_tempnam] = " tempnam "; + name[StatInt_sincos] = " sincos "; + name[StatInt_sincosf] = " sincosf "; + name[StatInt_sincosl] = " sincosl "; + name[StatInt_remquo] = " remquo "; + name[StatInt_remquof] = " remquof "; + name[StatInt_remquol] = " remquol "; + name[StatInt_lgamma] = " lgamma "; + name[StatInt_lgammaf] = " lgammaf "; + name[StatInt_lgammal] = " lgammal "; + name[StatInt_lgamma_r] = " lgamma_r "; + name[StatInt_lgammaf_r] = " lgammaf_r "; + name[StatInt_lgammal_r] = " lgammal_r "; + name[StatInt_drand48_r] = " drand48_r "; + name[StatInt_lrand48_r] = " lrand48_r "; + name[StatInt_getline] = " getline "; + name[StatInt_getdelim] = " getdelim "; + + name[StatInt_pthread_attr_getdetachstate] = " pthread_addr_getdetachstate "; // NOLINT + name[StatInt_pthread_attr_getguardsize] = " pthread_addr_getguardsize "; // NOLINT + name[StatInt_pthread_attr_getschedparam] = " pthread_addr_getschedparam "; // NOLINT + name[StatInt_pthread_attr_getschedpolicy] = " pthread_addr_getschedpolicy "; // NOLINT + name[StatInt_pthread_attr_getinheritsched] = " pthread_addr_getinheritsched "; // NOLINT + name[StatInt_pthread_attr_getscope] = " pthread_addr_getscope "; // NOLINT + name[StatInt_pthread_attr_getstacksize] = " pthread_addr_getstacksize "; // NOLINT + name[StatInt_pthread_attr_getstack] = " pthread_addr_getstack "; // NOLINT + name[StatInt_pthread_attr_getaffinity_np] = " pthread_addr_getaffinity_np "; // NOLINT name[StatAnnotation] = "Dynamic annotations "; name[StatAnnotateHappensBefore] = " HappensBefore "; @@ -353,6 +456,8 @@ void StatOutput(u64 *stat) { name[StatAnnotateIgnoreReadsEnd] = " IgnoreReadsEnd "; name[StatAnnotateIgnoreWritesBegin] = " IgnoreWritesBegin "; name[StatAnnotateIgnoreWritesEnd] = " IgnoreWritesEnd "; + name[StatAnnotateIgnoreSyncBegin] = " IgnoreSyncBegin "; + name[StatAnnotateIgnoreSyncEnd] = " IgnoreSyncEnd "; name[StatAnnotatePublishMemoryRange] = " PublishMemoryRange "; name[StatAnnotateUnpublishMemoryRange] = " UnpublishMemoryRange "; name[StatAnnotateThreadName] = " ThreadName "; diff --git a/lib/tsan/rtl/tsan_stat.h b/lib/tsan/rtl/tsan_stat.h index d5c8b4389394..a44edfcd65c6 100644 --- a/lib/tsan/rtl/tsan_stat.h +++ b/lib/tsan/rtl/tsan_stat.h @@ -137,7 +137,9 @@ enum StatType { StatInt_strcasecmp, StatInt_strncasecmp, StatInt_strstr, + StatInt_strdup, StatInt_atexit, + StatInt__exit, StatInt___cxa_guard_acquire, StatInt___cxa_guard_release, StatInt___cxa_guard_abort, @@ -175,6 +177,7 @@ enum StatType { StatInt_pthread_barrier_wait, StatInt_pthread_once, StatInt_pthread_getschedparam, + StatInt_pthread_setname_np, StatInt_sem_init, StatInt_sem_destroy, StatInt_sem_wait, @@ -224,11 +227,13 @@ enum StatType { StatInt_pread, StatInt_pread64, StatInt_readv, + StatInt_preadv, StatInt_preadv64, StatInt_write, StatInt_pwrite, StatInt_pwrite64, StatInt_writev, + StatInt_pwritev, StatInt_pwritev64, StatInt_send, StatInt_sendmsg, @@ -248,8 +253,10 @@ enum StatType { StatInt_epoll_ctl, StatInt_epoll_wait, StatInt_poll, + StatInt_ppoll, StatInt_sigaction, StatInt_signal, + StatInt_sigsuspend, StatInt_raise, StatInt_kill, StatInt_pthread_kill, @@ -280,6 +287,7 @@ enum StatType { StatInt_ctime_r, StatInt_asctime, StatInt_asctime_r, + StatInt_strptime, StatInt_frexp, StatInt_frexpf, StatInt_frexpl, @@ -306,7 +314,9 @@ enum StatType { StatInt_wait4, StatInt_inet_ntop, StatInt_inet_pton, + StatInt_inet_aton, StatInt_getaddrinfo, + StatInt_getnameinfo, StatInt_getsockname, StatInt_gethostent, StatInt_gethostbyname, @@ -317,6 +327,99 @@ enum StatType { StatInt_gethostbyname2_r, StatInt_gethostbyaddr_r, StatInt_getsockopt, + StatInt_modf, + StatInt_modff, + StatInt_modfl, + StatInt_getpeername, + StatInt_ioctl, + StatInt_sysinfo, + StatInt_readdir, + StatInt_readdir64, + StatInt_readdir_r, + StatInt_readdir64_r, + StatInt_ptrace, + StatInt_setlocale, + StatInt_getcwd, + StatInt_get_current_dir_name, + StatInt_strtoimax, + StatInt_strtoumax, + StatInt_mbstowcs, + StatInt_mbsrtowcs, + StatInt_mbsnrtowcs, + StatInt_wcstombs, + StatInt_wcsrtombs, + StatInt_wcsnrtombs, + StatInt_tcgetattr, + StatInt_realpath, + StatInt_canonicalize_file_name, + StatInt_confstr, + StatInt_sched_getaffinity, + StatInt_strerror, + StatInt_strerror_r, + StatInt_scandir, + StatInt_scandir64, + StatInt_getgroups, + StatInt_wordexp, + StatInt_sigwait, + StatInt_sigwaitinfo, + StatInt_sigtimedwait, + StatInt_sigemptyset, + StatInt_sigfillset, + StatInt_sigpending, + StatInt_sigprocmask, + StatInt_backtrace, + StatInt_backtrace_symbols, + StatInt_dlopen, + StatInt_dlclose, + StatInt_getmntent, + StatInt_getmntent_r, + StatInt_statfs, + StatInt_statfs64, + StatInt_fstatfs, + StatInt_fstatfs64, + StatInt_statvfs, + StatInt_statvfs64, + StatInt_fstatvfs, + StatInt_fstatvfs64, + StatInt_initgroups, + StatInt_ether_ntoa, + StatInt_ether_aton, + StatInt_ether_ntoa_r, + StatInt_ether_aton_r, + StatInt_ether_ntohost, + StatInt_ether_hostton, + StatInt_ether_line, + StatInt_shmctl, + StatInt_random_r, + StatInt_tmpnam, + StatInt_tmpnam_r, + StatInt_tempnam, + StatInt_sincos, + StatInt_sincosf, + StatInt_sincosl, + StatInt_remquo, + StatInt_remquof, + StatInt_remquol, + StatInt_lgamma, + StatInt_lgammaf, + StatInt_lgammal, + StatInt_lgamma_r, + StatInt_lgammaf_r, + StatInt_lgammal_r, + StatInt_drand48_r, + StatInt_lrand48_r, + StatInt_getline, + StatInt_getdelim, + + StatInt_pthread_attr_getdetachstate, + StatInt_pthread_attr_getguardsize, + StatInt_pthread_attr_getschedparam, + StatInt_pthread_attr_getschedpolicy, + StatInt_pthread_attr_getinheritsched, + StatInt_pthread_attr_getscope, + StatInt_pthread_attr_getstacksize, + StatInt_pthread_attr_getstack, + StatInt_pthread_attr_getaffinity_np, // Dynamic annotations. StatAnnotation, @@ -349,6 +452,8 @@ enum StatType { StatAnnotateIgnoreReadsEnd, StatAnnotateIgnoreWritesBegin, StatAnnotateIgnoreWritesEnd, + StatAnnotateIgnoreSyncBegin, + StatAnnotateIgnoreSyncEnd, StatAnnotatePublishMemoryRange, StatAnnotateUnpublishMemoryRange, StatAnnotateThreadName, diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc index 6c49355bed88..4b0ac04c48a6 100644 --- a/lib/tsan/rtl/tsan_suppressions.cc +++ b/lib/tsan/rtl/tsan_suppressions.cc @@ -13,12 +13,25 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_suppressions.h" #include "tsan_suppressions.h" #include "tsan_rtl.h" #include "tsan_flags.h" #include "tsan_mman.h" #include "tsan_platform.h" +// Suppressions for true/false positives in standard libraries. +static const char *const std_suppressions = +// Libstdc++ 4.4 has data races in std::string. +// See http://crbug.com/181502 for an example. +"race:^_M_rep$\n" +"race:^_M_is_leaked$\n" +// False positive when using std <thread>. +// Happens because we miss atomic synchronization in libstdc++. +// See http://llvm.org/bugs/show_bug.cgi?id=17066 for details. +"race:std::_Sp_counted_ptr_inplace<std::thread::_Impl\n"; + // Can be overriden in frontend. #ifndef TSAN_GO extern "C" const char *WEAK __tsan_default_suppressions() { @@ -28,7 +41,7 @@ extern "C" const char *WEAK __tsan_default_suppressions() { namespace __tsan { -static Suppression *g_suppressions; +static SuppressionContext* g_ctx; static char *ReadFile(const char *filename) { if (filename == 0 || filename[0] == 0) @@ -62,143 +75,96 @@ static char *ReadFile(const char *filename) { return buf; } -bool SuppressionMatch(char *templ, const char *str) { - if (str == 0 || str[0] == 0) - return false; - char *tpos; - const char *spos; - while (templ && templ[0]) { - if (templ[0] == '*') { - templ++; - continue; - } - if (str[0] == 0) - return false; - tpos = (char*)internal_strchr(templ, '*'); - if (tpos != 0) - tpos[0] = 0; - spos = internal_strstr(str, templ); - str = spos + internal_strlen(templ); - templ = tpos; - if (tpos) - tpos[0] = '*'; - if (spos == 0) - return false; - } - return true; -} - -Suppression *SuppressionParse(Suppression *head, const char* supp) { - const char *line = supp; - while (line) { - while (line[0] == ' ' || line[0] == '\t') - line++; - const char *end = internal_strchr(line, '\n'); - if (end == 0) - end = line + internal_strlen(line); - if (line != end && line[0] != '#') { - const char *end2 = end; - while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t')) - end2--; - SuppressionType stype; - if (0 == internal_strncmp(line, "race:", sizeof("race:") - 1)) { - stype = SuppressionRace; - line += sizeof("race:") - 1; - } else if (0 == internal_strncmp(line, "thread:", - sizeof("thread:") - 1)) { - stype = SuppressionThread; - line += sizeof("thread:") - 1; - } else if (0 == internal_strncmp(line, "mutex:", - sizeof("mutex:") - 1)) { - stype = SuppressionMutex; - line += sizeof("mutex:") - 1; - } else if (0 == internal_strncmp(line, "signal:", - sizeof("signal:") - 1)) { - stype = SuppressionSignal; - line += sizeof("signal:") - 1; - } else { - Printf("ThreadSanitizer: failed to parse suppressions file\n"); - Die(); - } - Suppression *s = (Suppression*)internal_alloc(MBlockSuppression, - sizeof(Suppression)); - s->next = head; - head = s; - s->type = stype; - s->templ = (char*)internal_alloc(MBlockSuppression, end2 - line + 1); - internal_memcpy(s->templ, line, end2 - line); - s->templ[end2 - line] = 0; - s->hit_count = 0; - } - if (end[0] == 0) - break; - line = end + 1; - } - return head; -} - void InitializeSuppressions() { + ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)]; + g_ctx = new(placeholder_) SuppressionContext; const char *supp = ReadFile(flags()->suppressions); - g_suppressions = SuppressionParse(0, supp); + g_ctx->Parse(supp); #ifndef TSAN_GO supp = __tsan_default_suppressions(); - g_suppressions = SuppressionParse(g_suppressions, supp); + g_ctx->Parse(supp); + g_ctx->Parse(std_suppressions); #endif } -uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { - if (g_suppressions == 0 || stack == 0) - return 0; - SuppressionType stype; +SuppressionContext *GetSuppressionContext() { + CHECK_NE(g_ctx, 0); + return g_ctx; +} + +SuppressionType conv(ReportType typ) { if (typ == ReportTypeRace) - stype = SuppressionRace; + return SuppressionRace; + else if (typ == ReportTypeVptrRace) + return SuppressionRace; + else if (typ == ReportTypeUseAfterFree) + return SuppressionRace; else if (typ == ReportTypeThreadLeak) - stype = SuppressionThread; + return SuppressionThread; else if (typ == ReportTypeMutexDestroyLocked) - stype = SuppressionMutex; + return SuppressionMutex; else if (typ == ReportTypeSignalUnsafe) - stype = SuppressionSignal; - else + return SuppressionSignal; + else if (typ == ReportTypeErrnoInSignal) + return SuppressionNone; + Printf("ThreadSanitizer: unknown report type %d\n", typ), + Die(); +} + +uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { + CHECK(g_ctx); + if (!g_ctx->SuppressionCount() || stack == 0) return 0; + SuppressionType stype = conv(typ); + if (stype == SuppressionNone) return 0; + Suppression *s; for (const ReportStack *frame = stack; frame; frame = frame->next) { - for (Suppression *supp = g_suppressions; supp; supp = supp->next) { - if (stype == supp->type && - (SuppressionMatch(supp->templ, frame->func) || - SuppressionMatch(supp->templ, frame->file) || - SuppressionMatch(supp->templ, frame->module))) { - DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ); - supp->hit_count++; - *sp = supp; - return frame->pc; - } + if (g_ctx->Match(frame->func, stype, &s) || + g_ctx->Match(frame->file, stype, &s) || + g_ctx->Match(frame->module, stype, &s)) { + DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ); + s->hit_count++; + *sp = s; + return frame->pc; } } return 0; } -static const char *SuppTypeStr(SuppressionType t) { - switch (t) { - case SuppressionRace: return "race"; - case SuppressionMutex: return "mutex"; - case SuppressionThread: return "thread"; - case SuppressionSignal: return "signal"; +uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) { + CHECK(g_ctx); + if (!g_ctx->SuppressionCount() || loc == 0 || + loc->type != ReportLocationGlobal) + return 0; + SuppressionType stype = conv(typ); + if (stype == SuppressionNone) + return 0; + Suppression *s; + if (g_ctx->Match(loc->name, stype, &s) || + g_ctx->Match(loc->file, stype, &s) || + g_ctx->Match(loc->module, stype, &s)) { + DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ); + s->hit_count++; + *sp = s; + return loc->addr; } - CHECK(0); - return "unknown"; + return 0; } void PrintMatchedSuppressions() { - int hit_count = 0; - for (Suppression *supp = g_suppressions; supp; supp = supp->next) - hit_count += supp->hit_count; - if (hit_count == 0) + CHECK(g_ctx); + InternalMmapVector<Suppression *> matched(1); + g_ctx->GetMatched(&matched); + if (!matched.size()) return; - Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", - hit_count, (int)internal_getpid()); - for (Suppression *supp = g_suppressions; supp; supp = supp->next) { - if (supp->hit_count == 0) - continue; - Printf("%d %s:%s\n", supp->hit_count, SuppTypeStr(supp->type), supp->templ); + int hit_count = 0; + for (uptr i = 0; i < matched.size(); i++) + hit_count += matched[i]->hit_count; + Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count, + (int)internal_getpid()); + for (uptr i = 0; i < matched.size(); i++) { + Printf("%d %s:%s\n", matched[i]->hit_count, + SuppressionTypeString(matched[i]->type), matched[i]->templ); } } } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_suppressions.h b/lib/tsan/rtl/tsan_suppressions.h index 1c98363383dc..fe7db588f50f 100644 --- a/lib/tsan/rtl/tsan_suppressions.h +++ b/lib/tsan/rtl/tsan_suppressions.h @@ -13,31 +13,16 @@ #ifndef TSAN_SUPPRESSIONS_H #define TSAN_SUPPRESSIONS_H +#include "sanitizer_common/sanitizer_suppressions.h" #include "tsan_report.h" namespace __tsan { -// Exposed for testing. -enum SuppressionType { - SuppressionRace, - SuppressionMutex, - SuppressionThread, - SuppressionSignal -}; - -struct Suppression { - Suppression *next; - SuppressionType type; - char *templ; - int hit_count; -}; - void InitializeSuppressions(); -void FinalizeSuppressions(); void PrintMatchedSuppressions(); uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp); -Suppression *SuppressionParse(Suppression *head, const char* supp); -bool SuppressionMatch(char *templ, const char *str); +uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp); +SuppressionContext *GetSuppressionContext(); } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_symbolize.cc b/lib/tsan/rtl/tsan_symbolize.cc index 12226064f5a4..75acf1d8438b 100644 --- a/lib/tsan/rtl/tsan_symbolize.cc +++ b/lib/tsan/rtl/tsan_symbolize.cc @@ -22,19 +22,17 @@ namespace __tsan { -struct ScopedInSymbolizer { - ScopedInSymbolizer() { - ThreadState *thr = cur_thread(); - CHECK(!thr->in_symbolizer); - thr->in_symbolizer = true; - } +void EnterSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(!thr->in_symbolizer); + thr->in_symbolizer = true; +} - ~ScopedInSymbolizer() { - ThreadState *thr = cur_thread(); - CHECK(thr->in_symbolizer); - thr->in_symbolizer = false; - } -}; +void ExitSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(thr->in_symbolizer); + thr->in_symbolizer = false; +} ReportStack *NewReportStackEntry(uptr addr) { ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack, @@ -44,18 +42,6 @@ ReportStack *NewReportStackEntry(uptr addr) { return ent; } -// Strip module path to make output shorter. -static char *StripModuleName(const char *module) { - if (module == 0) - return 0; - const char *short_module_name = internal_strrchr(module, '/'); - if (short_module_name) - short_module_name += 1; - else - short_module_name = module; - return internal_strdup(short_module_name); -} - static ReportStack *NewReportStackEntry(const AddressInfo &info) { ReportStack *ent = NewReportStackEntry(info.address); ent->module = StripModuleName(info.module); @@ -69,16 +55,64 @@ static ReportStack *NewReportStackEntry(const AddressInfo &info) { return ent; } + + ReportStack *next; + char *module; + uptr offset; + uptr pc; + char *func; + char *file; + int line; + int col; + + +// Denotes fake PC values that come from JIT/JAVA/etc. +// For such PC values __tsan_symbolize_external() will be called. +const uptr kExternalPCBit = 1ULL << 60; + +// May be overriden by JIT/JAVA/etc, +// whatever produces PCs marked with kExternalPCBit. +extern "C" bool __tsan_symbolize_external(uptr pc, + char *func_buf, uptr func_siz, + char *file_buf, uptr file_siz, + int *line, int *col) + SANITIZER_WEAK_ATTRIBUTE; + +bool __tsan_symbolize_external(uptr pc, + char *func_buf, uptr func_siz, + char *file_buf, uptr file_siz, + int *line, int *col) { + return false; +} + ReportStack *SymbolizeCode(uptr addr) { - if (!IsSymbolizerAvailable()) + // Check if PC comes from non-native land. + if (addr & kExternalPCBit) { + // Declare static to not consume too much stack space. + // We symbolize reports in a single thread, so this is fine. + static char func_buf[1024]; + static char file_buf[1024]; + int line, col; + if (!__tsan_symbolize_external(addr, func_buf, sizeof(func_buf), + file_buf, sizeof(file_buf), &line, &col)) + return NewReportStackEntry(addr); + ReportStack *ent = NewReportStackEntry(addr); + ent->module = 0; + ent->offset = 0; + ent->func = internal_strdup(func_buf); + ent->file = internal_strdup(file_buf); + ent->line = line; + ent->col = col; + return ent; + } + if (!Symbolizer::Get()->IsAvailable()) return SymbolizeCodeAddr2Line(addr); - ScopedInSymbolizer in_symbolizer; static const uptr kMaxAddrFrames = 16; InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames); for (uptr i = 0; i < kMaxAddrFrames; i++) new(&addr_frames[i]) AddressInfo(); - uptr addr_frames_num = __sanitizer::SymbolizeCode(addr, addr_frames.data(), - kMaxAddrFrames); + uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode( + addr, addr_frames.data(), kMaxAddrFrames); if (addr_frames_num == 0) return NewReportStackEntry(addr); ReportStack *top = 0; @@ -97,11 +131,10 @@ ReportStack *SymbolizeCode(uptr addr) { } ReportLocation *SymbolizeData(uptr addr) { - if (!IsSymbolizerAvailable()) + if (!Symbolizer::Get()->IsAvailable()) return 0; - ScopedInSymbolizer in_symbolizer; DataInfo info; - if (!__sanitizer::SymbolizeData(addr, &info)) + if (!Symbolizer::Get()->SymbolizeData(addr, &info)) return 0; ReportLocation *ent = (ReportLocation*)internal_alloc(MBlockReportStack, sizeof(ReportLocation)); @@ -117,10 +150,9 @@ ReportLocation *SymbolizeData(uptr addr) { } void SymbolizeFlush() { - if (!IsSymbolizerAvailable()) + if (!Symbolizer::Get()->IsAvailable()) return; - ScopedInSymbolizer in_symbolizer; - __sanitizer::FlushSymbolizer(); + Symbolizer::Get()->Flush(); } } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_symbolize.h b/lib/tsan/rtl/tsan_symbolize.h index 7bc6123df57d..6282e45b40d5 100644 --- a/lib/tsan/rtl/tsan_symbolize.h +++ b/lib/tsan/rtl/tsan_symbolize.h @@ -18,6 +18,8 @@ namespace __tsan { +void EnterSymbolizer(); +void ExitSymbolizer(); ReportStack *SymbolizeCode(uptr addr); ReportLocation *SymbolizeData(uptr addr); void SymbolizeFlush(); diff --git a/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc b/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc index 47f9e1fbf418..b186d3b38ec9 100644 --- a/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc +++ b/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc @@ -60,7 +60,6 @@ static void NOINLINE InitModule(ModuleDesc *m) { } int pid = fork(); if (pid == 0) { - __sanitizer_set_report_fd(STDERR_FILENO); internal_close(STDOUT_FILENO); internal_close(STDIN_FILENO); internal_dup2(outfd[0], STDIN_FILENO); diff --git a/lib/tsan/rtl/tsan_sync.cc b/lib/tsan/rtl/tsan_sync.cc index c6ddcdb37426..f8f3c40fab04 100644 --- a/lib/tsan/rtl/tsan_sync.cc +++ b/lib/tsan/rtl/tsan_sync.cc @@ -265,6 +265,11 @@ void StackTrace::ObtainCurrent(ThreadState *thr, uptr toppc) { n_ = c_ - !!toppc; } } else { + // Cap potentially huge stacks. + if (n_ + !!toppc > kTraceStackSize) { + start = n_ - kTraceStackSize + !!toppc; + n_ = kTraceStackSize - !!toppc; + } s_ = (uptr*)internal_alloc(MBlockStackTrace, (n_ + !!toppc) * sizeof(s_[0])); } diff --git a/lib/tsan/rtl/tsan_trace.h b/lib/tsan/rtl/tsan_trace.h index 7df716046567..5ed0356e2bf5 100644 --- a/lib/tsan/rtl/tsan_trace.h +++ b/lib/tsan/rtl/tsan_trace.h @@ -62,6 +62,11 @@ struct TraceHeader { struct Trace { TraceHeader headers[kTraceParts]; Mutex mtx; +#ifndef TSAN_GO + // Must be last to catch overflow as paging fault. + // Go shadow stack is dynamically allocated. + uptr shadow_stack[kShadowStackSize]; +#endif Trace() : mtx(MutexTypeTrace, StatMtxTrace) { diff --git a/lib/tsan/rtl/tsan_update_shadow_word_inl.h b/lib/tsan/rtl/tsan_update_shadow_word_inl.h index e7c036c5dea8..a11c9bc52509 100644 --- a/lib/tsan/rtl/tsan_update_shadow_word_inl.h +++ b/lib/tsan/rtl/tsan_update_shadow_word_inl.h @@ -57,8 +57,7 @@ do { goto RACE; } // Do the memory access intersect? - // In Go all memory accesses are 1 byte, so there can be no intersections. - if (kCppMode && Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { + if (Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { StatInc(thr, StatShadowIntersect); if (Shadow::TidsAreEqual(old, cur)) { StatInc(thr, StatShadowSameThread); diff --git a/lib/tsan/tests/CMakeLists.txt b/lib/tsan/tests/CMakeLists.txt index 7cc079f3d27a..f73a89242859 100644 --- a/lib/tsan/tests/CMakeLists.txt +++ b/lib/tsan/tests/CMakeLists.txt @@ -3,22 +3,44 @@ include_directories(../rtl) add_custom_target(TsanUnitTests) set_target_properties(TsanUnitTests PROPERTIES FOLDER "TSan unittests") -function(add_tsan_unittest testname) - # Build unit tests only on 64-bit Linux. - if(UNIX AND NOT APPLE - AND CAN_TARGET_x86_64 - AND CMAKE_SIZEOF_VOID_P EQUAL 8 - AND NOT LLVM_BUILD_32_BITS) - add_unittest(TsanUnitTests ${testname} ${ARGN}) - # Link with TSan runtime. - target_link_libraries(${testname} clang_rt.tsan-x86_64) - # Compile tests with the same flags as TSan runtime. - set_target_compile_flags(${testname} ${TSAN_CFLAGS}) - # Link tests with -pie. - set_property(TARGET ${testname} APPEND_STRING - PROPERTY LINK_FLAGS " -pie") + +set(TSAN_UNITTEST_CFLAGS + ${TSAN_CFLAGS} + ${COMPILER_RT_GTEST_INCLUDE_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/lib + -I${COMPILER_RT_SOURCE_DIR}/lib/tsan/rtl + -DGTEST_HAS_RTTI=0) + +# tsan_compile(obj_list, source, arch, {headers}) +macro(tsan_compile obj_list source arch) + get_filename_component(basename ${source} NAME) + set(output_obj "${basename}.${arch}.o") + get_target_flags_for_arch(${arch} TARGET_CFLAGS) + clang_compile(${output_obj} ${source} + CFLAGS ${TSAN_UNITTEST_CFLAGS} ${TARGET_CFLAGS} + DEPS gtest ${TSAN_RUNTIME_LIBRARIES} ${ARGN}) + list(APPEND ${obj_list} ${output_obj}) +endmacro() + +macro(add_tsan_unittest testname) + # Build unit tests only for 64-bit Linux. + if(UNIX AND NOT APPLE AND CAN_TARGET_x86_64) + parse_arguments(TEST "SOURCES;HEADERS" "" ${ARGN}) + set(TEST_OBJECTS) + foreach(SOURCE ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}) + tsan_compile(TEST_OBJECTS ${SOURCE} x86_64 ${TEST_HEADERS}) + endforeach() + get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + add_compiler_rt_test(TsanUnitTests ${testname} + OBJECTS ${TEST_OBJECTS} + DEPS ${TSAN_RUNTIME_LIBRARIES} ${TEST_OBJECTS} + LINK_FLAGS ${TARGET_LINK_FLAGS} + -fsanitize=thread + -lstdc++ -lm) endif() -endfunction() +endmacro() -add_subdirectory(rtl) -add_subdirectory(unit) +if(COMPILER_RT_CAN_EXECUTE_TESTS) + add_subdirectory(rtl) + add_subdirectory(unit) +endif() diff --git a/lib/tsan/tests/rtl/CMakeLists.txt b/lib/tsan/tests/rtl/CMakeLists.txt index b585660e8b4a..989566d9e041 100644 --- a/lib/tsan/tests/rtl/CMakeLists.txt +++ b/lib/tsan/tests/rtl/CMakeLists.txt @@ -1,15 +1,19 @@ -set(TSAN_RTL_TESTS +set(TSAN_RTL_TEST_SOURCES tsan_bench.cc tsan_mop.cc tsan_mutex.cc tsan_posix.cc tsan_string.cc tsan_test.cc - tsan_thread.cc - ) + tsan_thread.cc) if(UNIX AND NOT APPLE) - list(APPEND TSAN_RTL_TESTS tsan_test_util_linux.cc) + list(APPEND TSAN_RTL_TEST_SOURCES tsan_test_util_linux.cc) endif() -add_tsan_unittest(TsanRtlTest ${TSAN_RTL_TESTS}) +set(TSAN_RTL_TEST_HEADERS + tsan_test_util.h) + +add_tsan_unittest(TsanRtlTest + SOURCES ${TSAN_RTL_TEST_SOURCES} + HEADERS ${TSAN_RTL_TEST_HEADERS}) diff --git a/lib/tsan/tests/unit/CMakeLists.txt b/lib/tsan/tests/unit/CMakeLists.txt index b25a56d8d55c..6898f641d6a0 100644 --- a/lib/tsan/tests/unit/CMakeLists.txt +++ b/lib/tsan/tests/unit/CMakeLists.txt @@ -1,13 +1,13 @@ -set(TSAN_UNIT_TESTS +set(TSAN_UNIT_TEST_SOURCES tsan_clock_test.cc tsan_flags_test.cc tsan_mman_test.cc tsan_mutex_test.cc tsan_shadow_test.cc tsan_stack_test.cc - tsan_suppressions_test.cc tsan_sync_test.cc - tsan_vector_test.cc - ) + tsan_unit_test_main.cc + tsan_vector_test.cc) -add_tsan_unittest(TsanUnitTest ${TSAN_UNIT_TESTS}) +add_tsan_unittest(TsanUnitTest + SOURCES ${TSAN_UNIT_TEST_SOURCES}) diff --git a/lib/tsan/tests/unit/tsan_mman_test.cc b/lib/tsan/tests/unit/tsan_mman_test.cc index 0961d2b75d11..e1ad7ac51ad6 100644 --- a/lib/tsan/tests/unit/tsan_mman_test.cc +++ b/lib/tsan/tests/unit/tsan_mman_test.cc @@ -164,7 +164,9 @@ TEST(Mman, CallocOverflow) { size_t kArraySize = 4096; volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; - volatile void *p = calloc(kArraySize, kArraySize2); // Should return 0. + volatile void *p = NULL; + EXPECT_DEATH(p = calloc(kArraySize, kArraySize2), + "allocator is terminating the process instead of returning 0"); EXPECT_EQ(0L, p); } diff --git a/lib/tsan/tests/unit/tsan_stack_test.cc b/lib/tsan/tests/unit/tsan_stack_test.cc index d5392959c48c..9aa2967628cf 100644 --- a/lib/tsan/tests/unit/tsan_stack_test.cc +++ b/lib/tsan/tests/unit/tsan_stack_test.cc @@ -19,6 +19,10 @@ namespace __tsan { static void TestStackTrace(StackTrace *trace) { ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0); + uptr stack[128]; + thr.shadow_stack = &stack[0]; + thr.shadow_stack_pos = &stack[0]; + thr.shadow_stack_end = &stack[128]; trace->ObtainCurrent(&thr, 0); EXPECT_EQ(trace->Size(), (uptr)0); @@ -60,7 +64,12 @@ TEST(StackTrace, StaticTrim) { ScopedInRtl in_rtl; uptr buf[2]; StackTrace trace(buf, 2); + ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0); + uptr stack[128]; + thr.shadow_stack = &stack[0]; + thr.shadow_stack_pos = &stack[0]; + thr.shadow_stack_end = &stack[128]; *thr.shadow_stack_pos++ = 100; *thr.shadow_stack_pos++ = 101; diff --git a/lib/tsan/tests/unit/tsan_suppressions_test.cc b/lib/tsan/tests/unit/tsan_suppressions_test.cc deleted file mode 100644 index decfa3214d23..000000000000 --- a/lib/tsan/tests/unit/tsan_suppressions_test.cc +++ /dev/null @@ -1,128 +0,0 @@ -//===-- tsan_suppressions_test.cc -----------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_suppressions.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" - -#include <string.h> - -namespace __tsan { - -TEST(Suppressions, Parse) { - ScopedInRtl in_rtl; - Suppression *supp0 = SuppressionParse(0, - "race:foo\n" - " race:bar\n" // NOLINT - "race:baz \n" // NOLINT - "# a comment\n" - "race:quz\n" - ); // NOLINT - Suppression *supp = supp0; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "quz")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "baz")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "bar")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "foo")); - supp = supp->next; - EXPECT_EQ((Suppression*)0, supp); -} - -TEST(Suppressions, Parse2) { - ScopedInRtl in_rtl; - Suppression *supp0 = SuppressionParse(0, - " # first line comment\n" // NOLINT - " race:bar \n" // NOLINT - "race:baz* *baz\n" - "# a comment\n" - "# last line comment\n" - ); // NOLINT - Suppression *supp = supp0; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "baz* *baz")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "bar")); - supp = supp->next; - EXPECT_EQ((Suppression*)0, supp); -} - -TEST(Suppressions, Parse3) { - ScopedInRtl in_rtl; - Suppression *supp0 = SuppressionParse(0, - "# last suppression w/o line-feed\n" - "race:foo\n" - "race:bar" - ); // NOLINT - Suppression *supp = supp0; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "bar")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "foo")); - supp = supp->next; - EXPECT_EQ((Suppression*)0, supp); -} - -TEST(Suppressions, ParseType) { - ScopedInRtl in_rtl; - Suppression *supp0 = SuppressionParse(0, - "race:foo\n" - "thread:bar\n" - "mutex:baz\n" - "signal:quz\n" - ); // NOLINT - Suppression *supp = supp0; - EXPECT_EQ(supp->type, SuppressionSignal); - EXPECT_EQ(0, strcmp(supp->templ, "quz")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionMutex); - EXPECT_EQ(0, strcmp(supp->templ, "baz")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionThread); - EXPECT_EQ(0, strcmp(supp->templ, "bar")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "foo")); - supp = supp->next; - EXPECT_EQ((Suppression*)0, supp); -} - -static bool MyMatch(const char *templ, const char *func) { - char tmp[1024]; - strcpy(tmp, templ); // NOLINT - return SuppressionMatch(tmp, func); -} - -TEST(Suppressions, Match) { - EXPECT_TRUE(MyMatch("foobar", "foobar")); - EXPECT_TRUE(MyMatch("foobar", "prefix_foobar_postfix")); - EXPECT_TRUE(MyMatch("*foobar*", "prefix_foobar_postfix")); - EXPECT_TRUE(MyMatch("foo*bar", "foo_middle_bar")); - EXPECT_TRUE(MyMatch("foo*bar", "foobar")); - EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_bar_another_baz")); - EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_barbaz")); - - EXPECT_FALSE(MyMatch("foo", "baz")); - EXPECT_FALSE(MyMatch("foobarbaz", "foobar")); - EXPECT_FALSE(MyMatch("foobarbaz", "barbaz")); - EXPECT_FALSE(MyMatch("foo*bar", "foobaz")); - EXPECT_FALSE(MyMatch("foo*bar", "foo_baz")); -} - -} // namespace __tsan diff --git a/lib/tsan/tests/unit/tsan_unit_test_main.cc b/lib/tsan/tests/unit/tsan_unit_test_main.cc new file mode 100644 index 000000000000..84d94dd03748 --- /dev/null +++ b/lib/tsan/tests/unit/tsan_unit_test_main.cc @@ -0,0 +1,19 @@ +//===-- tsan_unit_test_main.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 ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "gtest/gtest.h" + +int main(int argc, char **argv) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/lib/ubsan/CMakeLists.txt b/lib/ubsan/CMakeLists.txt index c8470bc6d353..675c47f6a65e 100644 --- a/lib/ubsan/CMakeLists.txt +++ b/lib/ubsan/CMakeLists.txt @@ -34,17 +34,22 @@ else() # Main UBSan runtime. add_compiler_rt_static_runtime(clang_rt.ubsan-${arch} ${arch} SOURCES ${UBSAN_SOURCES} - CFLAGS ${UBSAN_CFLAGS} - SYMS ubsan.syms) + CFLAGS ${UBSAN_CFLAGS}) # C++-specific parts of UBSan runtime. Requires a C++ ABI library. add_compiler_rt_static_runtime(clang_rt.ubsan_cxx-${arch} ${arch} SOURCES ${UBSAN_CXX_SOURCES} - CFLAGS ${UBSAN_CFLAGS} - SYMS ubsan.syms) + CFLAGS ${UBSAN_CFLAGS}) list(APPEND UBSAN_RUNTIME_LIBRARIES - clang_rt.san-${arch} - clang_rt.ubsan-${arch} - clang_rt.ubsan_cxx-${arch}) + clang_rt.san-${arch} + clang_rt.ubsan-${arch} + clang_rt.ubsan_cxx-${arch}) + if (UNIX AND NOT ${arch} STREQUAL "i386") + add_sanitizer_rt_symbols(clang_rt.ubsan-${arch} ubsan.syms.extra) + add_sanitizer_rt_symbols(clang_rt.ubsan_cxx-${arch} ubsan.syms.extra) + list(APPEND UBSAN_RUNTIME_LIBRARIES + clang_rt.ubsan-${arch}-symbols + clang_rt.ubsan_cxx-${arch}-symbols) + endif() endforeach() endif() diff --git a/lib/ubsan/lit_tests/AsanConfig/lit.cfg b/lib/ubsan/lit_tests/AsanConfig/lit.cfg new file mode 100644 index 000000000000..407e5ec32e60 --- /dev/null +++ b/lib/ubsan/lit_tests/AsanConfig/lit.cfg @@ -0,0 +1,25 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +ubsan_lit_tests_dir = get_required_attr(config, "ubsan_lit_tests_dir") +ubsan_lit_cfg = os.path.join(ubsan_lit_tests_dir, "lit.common.cfg") +lit_config.load_config(config, ubsan_lit_cfg) + +config.name = 'UndefinedBehaviorSanitizer-AddressSanitizer' + +# Define %clang and %clangxx substitutions to use in test RUN lines. +config.substitutions.append( ("%clang ", (" " + config.clang + + " -fsanitize=address ")) ) +config.substitutions.append( ("%clangxx ", (" " + config.clang + + " -fsanitize=address" + + " --driver-mode=g++ ")) ) diff --git a/lib/ubsan/lit_tests/AsanConfig/lit.site.cfg.in b/lib/ubsan/lit_tests/AsanConfig/lit.site.cfg.in new file mode 100644 index 000000000000..f75741838f83 --- /dev/null +++ b/lib/ubsan/lit_tests/AsanConfig/lit.site.cfg.in @@ -0,0 +1,9 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Tool-specific config options. +config.ubsan_lit_tests_dir = "@UBSAN_LIT_TESTS_DIR@" + +# Load tool-specific config that would do the real work. +print config.ubsan_lit_tests_dir +lit_config.load_config(config, "@UBSAN_LIT_TESTS_DIR@/AsanConfig/lit.cfg") diff --git a/lib/ubsan/lit_tests/CMakeLists.txt b/lib/ubsan/lit_tests/CMakeLists.txt index 7e1a13c782d2..36d8dc1f9205 100644 --- a/lib/ubsan/lit_tests/CMakeLists.txt +++ b/lib/ubsan/lit_tests/CMakeLists.txt @@ -1,21 +1,23 @@ +set(UBSAN_LIT_TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/UbsanConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/UbsanConfig/lit.site.cfg) + configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg - ) + ${CMAKE_CURRENT_SOURCE_DIR}/AsanConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig/lit.site.cfg) if(COMPILER_RT_CAN_EXECUTE_TESTS) # Run UBSan output tests only if we're sure that clang would produce # working binaries. set(UBSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS} - ${UBSAN_RUNTIME_LIBRARIES}) - set(UBSAN_TEST_PARAMS - ubsan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg - ) + ${UBSAN_RUNTIME_LIBRARIES} + asan_runtime_libraries) add_lit_testsuite(check-ubsan "Running UndefinedBehaviorSanitizer tests" - ${CMAKE_CURRENT_BINARY_DIR} - PARAMS ${UBSAN_TEST_PARAMS} - DEPENDS ${UBSAN_TEST_DEPS} - ) + ${CMAKE_CURRENT_BINARY_DIR}/UbsanConfig + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig + DEPENDS ${UBSAN_TEST_DEPS}) set_target_properties(check-ubsan PROPERTIES FOLDER "UBSan unittests") endif() diff --git a/lib/ubsan/lit_tests/Integer/div-zero.cpp b/lib/ubsan/lit_tests/Integer/div-zero.cpp deleted file mode 100644 index b2a839566c5f..000000000000 --- a/lib/ubsan/lit_tests/Integer/div-zero.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %clang -fsanitize=integer-divide-by-zero -DDIVIDEND=0 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clang -fsanitize=integer-divide-by-zero -DDIVIDEND=1U %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clang -fsanitize=float-divide-by-zero -DDIVIDEND=1.5 %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clang -fsanitize=integer-divide-by-zero -DDIVIDEND='intmax(123)' %s -o %t && %t 2>&1 | FileCheck %s - -#ifdef __SIZEOF_INT128__ -typedef __int128 intmax; -#else -typedef long long intmax; -#endif - -int main() { - // CHECK: div-zero.cpp:[[@LINE+1]]:12: runtime error: division by zero - DIVIDEND / 0; -} diff --git a/lib/ubsan/lit_tests/Integer/incdec-overflow.cpp b/lib/ubsan/lit_tests/Integer/incdec-overflow.cpp deleted file mode 100644 index 48b68b6365c6..000000000000 --- a/lib/ubsan/lit_tests/Integer/incdec-overflow.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %clang -DOP=n++ -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clang -DOP=++n -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clang -DOP=m-- -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s -// RUN: %clang -DOP=--m -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s - -#include <stdint.h> - -int main() { - int n = 0x7ffffffd; - n++; - n++; - int m = -n - 1; - // CHECK: incdec-overflow.cpp:15:3: runtime error: signed integer overflow: [[MINUS:-?]]214748364 - // CHECK: + [[MINUS]]1 cannot be represented in type 'int' - OP; -} diff --git a/lib/ubsan/lit_tests/Integer/shift.cpp b/lib/ubsan/lit_tests/Integer/shift.cpp deleted file mode 100644 index 19101c53e75e..000000000000 --- a/lib/ubsan/lit_tests/Integer/shift.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// RUN: %clang -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=LSH_OVERFLOW -// RUN: %clang -DLSH_OVERFLOW -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=LSH_OVERFLOW -// RUN: %clang -DTOO_LOW -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW -// RUN: %clang -DTOO_LOW -DOP='>>' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW -// RUN: %clang -DTOO_LOW -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW -// RUN: %clang -DTOO_LOW -DOP='>>=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW -// RUN: %clang -DTOO_HIGH -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH -// RUN: %clang -DTOO_HIGH -DOP='>>' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH -// RUN: %clang -DTOO_HIGH -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH -// RUN: %clang -DTOO_HIGH -DOP='>>=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH - -#include <stdint.h> - -int main() { - int a = 1; - unsigned b = 1; - - a <<= 31; // ok in C++11, not ok in C99/C11 - b <<= 31; // ok - b <<= 1; // still ok, unsigned - -#ifdef LSH_OVERFLOW - // CHECK-LSH_OVERFLOW: shift.cpp:24:5: runtime error: left shift of negative value -2147483648 - a OP 1; -#endif - -#ifdef TOO_LOW - // CHECK-TOO_LOW: shift.cpp:29:5: runtime error: shift exponent -3 is negative - a OP (-3); -#endif - -#ifdef TOO_HIGH - a = 0; - // CHECK-TOO_HIGH: shift.cpp:35:5: runtime error: shift exponent 32 is too large for 32-bit type 'int' - a OP 32; -#endif -} diff --git a/lib/ubsan/lit_tests/Integer/uincdec-overflow.cpp b/lib/ubsan/lit_tests/Integer/uincdec-overflow.cpp deleted file mode 100644 index 6b677ca5bd35..000000000000 --- a/lib/ubsan/lit_tests/Integer/uincdec-overflow.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %clang -DOP=n++ -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=INC %s -// RUN: %clang -DOP=++n -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=INC %s -// RUN: %clang -DOP=m-- -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=DEC %s -// RUN: %clang -DOP=--m -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=DEC %s - -#include <stdint.h> - -int main() { - unsigned n = 0xfffffffd; - n++; - n++; - unsigned m = 0; - // CHECK-INC: uincdec-overflow.cpp:15:3: runtime error: unsigned integer overflow: 4294967295 + 1 cannot be represented in type 'unsigned int' - // CHECK-DEC: uincdec-overflow.cpp:15:3: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'unsigned int' - OP; -} diff --git a/lib/ubsan/lit_tests/Misc/bool.cpp b/lib/ubsan/lit_tests/Misc/bool.cpp deleted file mode 100644 index 8fafe7eac053..000000000000 --- a/lib/ubsan/lit_tests/Misc/bool.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %clang -fsanitize=bool %s -O3 -o %T/bool.exe && %T/bool.exe 2>&1 | FileCheck %s - -unsigned char NotABool = 123; - -int main(int argc, char **argv) { - bool *p = (bool*)&NotABool; - - // FIXME: Provide a better source location here. - // CHECK: bool.exe:0x{{[0-9a-f]*}}: runtime error: load of value 123, which is not a valid value for type 'bool' - return *p; -} diff --git a/lib/ubsan/lit_tests/Float/cast-overflow.cpp b/lib/ubsan/lit_tests/TestCases/Float/cast-overflow.cpp index 8d9120d586a1..35f9336c904a 100644 --- a/lib/ubsan/lit_tests/Float/cast-overflow.cpp +++ b/lib/ubsan/lit_tests/TestCases/Float/cast-overflow.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -fsanitize=float-cast-overflow %s -o %t +// RUN: %clangxx -fsanitize=float-cast-overflow %s -o %t // RUN: %t _ // RUN: %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0 // RUN: %t 1 2>&1 | FileCheck %s --check-prefix=CHECK-1 diff --git a/lib/ubsan/lit_tests/Integer/add-overflow.cpp b/lib/ubsan/lit_tests/TestCases/Integer/add-overflow.cpp index 80543524f51d..412eb7621033 100644 --- a/lib/ubsan/lit_tests/Integer/add-overflow.cpp +++ b/lib/ubsan/lit_tests/TestCases/Integer/add-overflow.cpp @@ -1,6 +1,6 @@ -// RUN: %clang -DADD_I32 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I32 -// RUN: %clang -DADD_I64 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I64 -// RUN: %clang -DADD_I128 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I128 +// RUN: %clangxx -DADD_I32 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 +// RUN: %clangxx -DADD_I64 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 +// RUN: %clangxx -DADD_I128 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 #include <stdint.h> #include <stdio.h> diff --git a/lib/ubsan/lit_tests/Integer/div-overflow.cpp b/lib/ubsan/lit_tests/TestCases/Integer/div-overflow.cpp index dd82427f9d5b..83aa854485b4 100644 --- a/lib/ubsan/lit_tests/Integer/div-overflow.cpp +++ b/lib/ubsan/lit_tests/TestCases/Integer/div-overflow.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s #include <stdint.h> diff --git a/lib/ubsan/lit_tests/TestCases/Integer/div-zero.cpp b/lib/ubsan/lit_tests/TestCases/Integer/div-zero.cpp new file mode 100644 index 000000000000..6b8aadfe15e6 --- /dev/null +++ b/lib/ubsan/lit_tests/TestCases/Integer/div-zero.cpp @@ -0,0 +1,15 @@ +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND=0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND=1U %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=float-divide-by-zero -DDIVIDEND=1.5 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND='intmax(123)' %s -o %t && %t 2>&1 | FileCheck %s + +#ifdef __SIZEOF_INT128__ +typedef __int128 intmax; +#else +typedef long long intmax; +#endif + +int main() { + // CHECK: div-zero.cpp:[[@LINE+1]]:12: runtime error: division by zero + DIVIDEND / 0; +} diff --git a/lib/ubsan/lit_tests/TestCases/Integer/incdec-overflow.cpp b/lib/ubsan/lit_tests/TestCases/Integer/incdec-overflow.cpp new file mode 100644 index 000000000000..904250a76c79 --- /dev/null +++ b/lib/ubsan/lit_tests/TestCases/Integer/incdec-overflow.cpp @@ -0,0 +1,16 @@ +// RUN: %clangxx -DOP=n++ -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=++n -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=m-- -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=--m -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s + +#include <stdint.h> + +int main() { + int n = 0x7ffffffd; + n++; + n++; + int m = -n - 1; + // CHECK: incdec-overflow.cpp:15:3: runtime error: signed integer overflow: [[MINUS:-?]]214748364 + // CHECK: + [[MINUS]]1 cannot be represented in type 'int' + OP; +} diff --git a/lib/ubsan/lit_tests/Integer/mul-overflow.cpp b/lib/ubsan/lit_tests/TestCases/Integer/mul-overflow.cpp index 8d1e70d6ad48..1cfe23f57213 100644 --- a/lib/ubsan/lit_tests/Integer/mul-overflow.cpp +++ b/lib/ubsan/lit_tests/TestCases/Integer/mul-overflow.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s #include <stdint.h> diff --git a/lib/ubsan/lit_tests/Integer/negate-overflow.cpp b/lib/ubsan/lit_tests/TestCases/Integer/negate-overflow.cpp index 2ee4f10115e8..6bee3eea2980 100644 --- a/lib/ubsan/lit_tests/Integer/negate-overflow.cpp +++ b/lib/ubsan/lit_tests/TestCases/Integer/negate-overflow.cpp @@ -1,5 +1,5 @@ -// RUN: %clang -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECKS -// RUN: %clang -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECKU +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECKS +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECKU int main() { // CHECKS-NOT: runtime error diff --git a/lib/ubsan/lit_tests/Integer/no-recover.cpp b/lib/ubsan/lit_tests/TestCases/Integer/no-recover.cpp index e200feaa79ae..64787b7cfbf5 100644 --- a/lib/ubsan/lit_tests/Integer/no-recover.cpp +++ b/lib/ubsan/lit_tests/TestCases/Integer/no-recover.cpp @@ -1,6 +1,6 @@ -// RUN: %clang -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=RECOVER -// RUN: %clang -fsanitize=unsigned-integer-overflow -fsanitize-recover %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=RECOVER -// RUN: %clang -fsanitize=unsigned-integer-overflow -fno-sanitize-recover %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ABORT +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=RECOVER +// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fsanitize-recover %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=RECOVER +// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fno-sanitize-recover %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ABORT #include <stdint.h> @@ -17,6 +17,6 @@ int main() { // ABORT: no-recover.cpp:[[@LINE-2]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int' (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); - // RECOVER: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned long' + // RECOVER: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' // ABORT-NOT: runtime error } diff --git a/lib/ubsan/lit_tests/TestCases/Integer/shift.cpp b/lib/ubsan/lit_tests/TestCases/Integer/shift.cpp new file mode 100644 index 000000000000..f35fa1f959ae --- /dev/null +++ b/lib/ubsan/lit_tests/TestCases/Integer/shift.cpp @@ -0,0 +1,37 @@ +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW +// RUN: %clangxx -DTOO_LOW -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='>>' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='>>=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_HIGH -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='>>' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='>>=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH + +#include <stdint.h> + +int main() { + int a = 1; + unsigned b = 1; + + a <<= 31; // ok in C++11, not ok in C99/C11 + b <<= 31; // ok + b <<= 1; // still ok, unsigned + +#ifdef LSH_OVERFLOW + // CHECK-LSH_OVERFLOW: shift.cpp:24:5: runtime error: left shift of negative value -2147483648 + a OP 1; +#endif + +#ifdef TOO_LOW + // CHECK-TOO_LOW: shift.cpp:29:5: runtime error: shift exponent -3 is negative + a OP (-3); +#endif + +#ifdef TOO_HIGH + a = 0; + // CHECK-TOO_HIGH: shift.cpp:35:5: runtime error: shift exponent 32 is too large for 32-bit type 'int' + a OP 32; +#endif +} diff --git a/lib/ubsan/lit_tests/Integer/sub-overflow.cpp b/lib/ubsan/lit_tests/TestCases/Integer/sub-overflow.cpp index b43a69bee4e6..bf33d293799b 100644 --- a/lib/ubsan/lit_tests/Integer/sub-overflow.cpp +++ b/lib/ubsan/lit_tests/TestCases/Integer/sub-overflow.cpp @@ -1,6 +1,6 @@ -// RUN: %clang -DSUB_I32 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I32 -// RUN: %clang -DSUB_I64 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I64 -// RUN: %clang -DSUB_I128 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I128 +// RUN: %clangxx -DSUB_I32 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I32 +// RUN: %clangxx -DSUB_I64 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I64 +// RUN: %clangxx -DSUB_I128 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I128 #include <stdint.h> #include <stdio.h> diff --git a/lib/ubsan/lit_tests/Integer/uadd-overflow.cpp b/lib/ubsan/lit_tests/TestCases/Integer/uadd-overflow.cpp index 0edb10092e2c..2ef31c0640d1 100644 --- a/lib/ubsan/lit_tests/Integer/uadd-overflow.cpp +++ b/lib/ubsan/lit_tests/TestCases/Integer/uadd-overflow.cpp @@ -1,6 +1,6 @@ -// RUN: %clang -DADD_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I32 -// RUN: %clang -DADD_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I64 -// RUN: %clang -DADD_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I128 +// RUN: %clangxx -DADD_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 +// RUN: %clangxx -DADD_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 +// RUN: %clangxx -DADD_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 #include <stdint.h> #include <stdio.h> @@ -18,7 +18,7 @@ int main() { #ifdef ADD_I64 (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); - // CHECK-ADD_I64: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned long' + // CHECK-ADD_I64: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' #endif #ifdef ADD_I128 diff --git a/lib/ubsan/lit_tests/TestCases/Integer/uincdec-overflow.cpp b/lib/ubsan/lit_tests/TestCases/Integer/uincdec-overflow.cpp new file mode 100644 index 000000000000..a14bd6a776f5 --- /dev/null +++ b/lib/ubsan/lit_tests/TestCases/Integer/uincdec-overflow.cpp @@ -0,0 +1,16 @@ +// RUN: %clangxx -DOP=n++ -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=CHECK-INC %s +// RUN: %clangxx -DOP=++n -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=CHECK-INC %s +// RUN: %clangxx -DOP=m-- -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=CHECK-DEC %s +// RUN: %clangxx -DOP=--m -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=CHECK-DEC %s + +#include <stdint.h> + +int main() { + unsigned n = 0xfffffffd; + n++; + n++; + unsigned m = 0; + // CHECK-INC: uincdec-overflow.cpp:15:3: runtime error: unsigned integer overflow: 4294967295 + 1 cannot be represented in type 'unsigned int' + // CHECK-DEC: uincdec-overflow.cpp:15:3: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'unsigned int' + OP; +} diff --git a/lib/ubsan/lit_tests/Integer/umul-overflow.cpp b/lib/ubsan/lit_tests/TestCases/Integer/umul-overflow.cpp index 42cf3a780ed0..c84bb39ef2f7 100644 --- a/lib/ubsan/lit_tests/Integer/umul-overflow.cpp +++ b/lib/ubsan/lit_tests/TestCases/Integer/umul-overflow.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s #include <stdint.h> diff --git a/lib/ubsan/lit_tests/Integer/usub-overflow.cpp b/lib/ubsan/lit_tests/TestCases/Integer/usub-overflow.cpp index 357d662ad63e..78f745578583 100644 --- a/lib/ubsan/lit_tests/Integer/usub-overflow.cpp +++ b/lib/ubsan/lit_tests/TestCases/Integer/usub-overflow.cpp @@ -1,6 +1,6 @@ -// RUN: %clang -DSUB_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I32 -// RUN: %clang -DSUB_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I64 -// RUN: %clang -DSUB_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I128 +// RUN: %clangxx -DSUB_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I32 +// RUN: %clangxx -DSUB_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I64 +// RUN: %clangxx -DSUB_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I128 #include <stdint.h> #include <stdio.h> @@ -17,7 +17,7 @@ int main() { #ifdef SUB_I64 (void)(uint64_t(8000000000000000000ll) - uint64_t(9000000000000000000ll)); - // CHECK-SUB_I64: 8000000000000000000 - 9000000000000000000 cannot be represented in type 'unsigned long' + // CHECK-SUB_I64: 8000000000000000000 - 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' #endif #ifdef SUB_I128 diff --git a/lib/ubsan/lit_tests/TestCases/Misc/bool.cpp b/lib/ubsan/lit_tests/TestCases/Misc/bool.cpp new file mode 100644 index 000000000000..e916e7fb3c1d --- /dev/null +++ b/lib/ubsan/lit_tests/TestCases/Misc/bool.cpp @@ -0,0 +1,10 @@ +// RUN: %clangxx -fsanitize=bool %s -O3 -o %T/bool.exe && %T/bool.exe 2>&1 | FileCheck %s + +unsigned char NotABool = 123; + +int main(int argc, char **argv) { + bool *p = (bool*)&NotABool; + + // CHECK: bool.cpp:9:10: runtime error: load of value 123, which is not a valid value for type 'bool' + return *p; +} diff --git a/lib/ubsan/lit_tests/Misc/bounds.cpp b/lib/ubsan/lit_tests/TestCases/Misc/bounds.cpp index 07b30d384df9..dc4c4a513c1c 100644 --- a/lib/ubsan/lit_tests/Misc/bounds.cpp +++ b/lib/ubsan/lit_tests/TestCases/Misc/bounds.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -fsanitize=bounds %s -O3 -o %T/bounds.exe +// RUN: %clangxx -fsanitize=bounds %s -O3 -o %T/bounds.exe // RUN: %T/bounds.exe 0 0 0 // RUN: %T/bounds.exe 1 2 3 // RUN: %T/bounds.exe 2 0 0 2>&1 | FileCheck %s --check-prefix=CHECK-A-2 diff --git a/lib/ubsan/lit_tests/Misc/deduplication.cpp b/lib/ubsan/lit_tests/TestCases/Misc/deduplication.cpp index d9c909f9af4f..d325bf6dd899 100644 --- a/lib/ubsan/lit_tests/Misc/deduplication.cpp +++ b/lib/ubsan/lit_tests/TestCases/Misc/deduplication.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -fsanitize=undefined %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=undefined %s -o %t && %t 2>&1 | FileCheck %s // Verify deduplication works by ensuring only one diag is emitted. #include <limits.h> #include <stdio.h> diff --git a/lib/ubsan/lit_tests/Misc/enum.cpp b/lib/ubsan/lit_tests/TestCases/Misc/enum.cpp index b363fea3487f..c5642507ad42 100644 --- a/lib/ubsan/lit_tests/Misc/enum.cpp +++ b/lib/ubsan/lit_tests/TestCases/Misc/enum.cpp @@ -1,6 +1,6 @@ -// RUN: %clang -fsanitize=enum %s -O3 -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-PLAIN -// RUN: %clang -fsanitize=enum -std=c++11 -DE="class E" %s -O3 -o %t && %t -// RUN: %clang -fsanitize=enum -std=c++11 -DE="class E : bool" %s -O3 -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-BOOL +// RUN: %clangxx -fsanitize=enum %s -O3 -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-PLAIN +// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E" %s -O3 -o %t && %t +// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E : bool" %s -O3 -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-BOOL enum E { a = 1 } e; #undef E diff --git a/lib/ubsan/lit_tests/Misc/missing_return.cpp b/lib/ubsan/lit_tests/TestCases/Misc/missing_return.cpp index 9997b8386f21..7da238e25dca 100644 --- a/lib/ubsan/lit_tests/Misc/missing_return.cpp +++ b/lib/ubsan/lit_tests/TestCases/Misc/missing_return.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -fsanitize=return %s -O3 -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=return %s -O3 -o %t && %t 2>&1 | FileCheck %s // CHECK: missing_return.cpp:4:5: runtime error: execution reached the end of a value-returning function without returning a value int f() { diff --git a/lib/ubsan/lit_tests/Misc/unreachable.cpp b/lib/ubsan/lit_tests/TestCases/Misc/unreachable.cpp index 5ca4e5fd8b0c..75fc3e5bd9a6 100644 --- a/lib/ubsan/lit_tests/Misc/unreachable.cpp +++ b/lib/ubsan/lit_tests/TestCases/Misc/unreachable.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -fsanitize=unreachable %s -O3 -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=unreachable %s -O3 -o %t && %t 2>&1 | FileCheck %s int main(int, char **argv) { // CHECK: unreachable.cpp:5:3: runtime error: execution reached a __builtin_unreachable() call diff --git a/lib/ubsan/lit_tests/Misc/vla.c b/lib/ubsan/lit_tests/TestCases/Misc/vla.c index 2fa88addc0d3..2fa88addc0d3 100644 --- a/lib/ubsan/lit_tests/Misc/vla.c +++ b/lib/ubsan/lit_tests/TestCases/Misc/vla.c diff --git a/lib/ubsan/lit_tests/TestCases/TypeCheck/Function/function.cpp b/lib/ubsan/lit_tests/TestCases/TypeCheck/Function/function.cpp new file mode 100644 index 000000000000..8106ae47ee4f --- /dev/null +++ b/lib/ubsan/lit_tests/TestCases/TypeCheck/Function/function.cpp @@ -0,0 +1,17 @@ +// RUN: %clangxx -fsanitize=function %s -O3 -g -o %t +// RUN: %t 2>&1 | FileCheck %s + +#include <stdint.h> + +void f() {} + +void g(int x) {} + +int main(void) { + // CHECK: runtime error: call to function f() through pointer to incorrect function type 'void (*)(int)' + // CHECK-NEXT: function.cpp:6: note: f() defined here + reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(f))(42); + + // CHECK-NOT: runtime error: call to function g + reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(g))(42); +} diff --git a/lib/ubsan/lit_tests/TestCases/TypeCheck/Function/lit.local.cfg b/lib/ubsan/lit_tests/TestCases/TypeCheck/Function/lit.local.cfg new file mode 100644 index 000000000000..27c61a34387c --- /dev/null +++ b/lib/ubsan/lit_tests/TestCases/TypeCheck/Function/lit.local.cfg @@ -0,0 +1,3 @@ +# The function type checker is only supported on x86 and x86_64 for now. +if config.root.host_arch not in ['x86', 'x86_64']: + config.unsupported = True diff --git a/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp b/lib/ubsan/lit_tests/TestCases/TypeCheck/misaligned.cpp index 3abacae8be89..9b0b9a1197c0 100644 --- a/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp +++ b/lib/ubsan/lit_tests/TestCases/TypeCheck/misaligned.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -fsanitize=alignment %s -O3 -o %t +// RUN: %clangxx -fsanitize=alignment %s -O3 -o %t // RUN: %t l0 && %t s0 && %t r0 && %t m0 && %t f0 && %t n0 // RUN: %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --strict-whitespace // RUN: %t s1 2>&1 | FileCheck %s --check-prefix=CHECK-STORE @@ -64,7 +64,7 @@ int main(int, char **argv) { case 'n': // FIXME: Provide a better source location here. - // CHECK-NEW: misaligned{{.*}}:0x{{[0-9a-f]*}}: runtime error: constructor call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-NEW: misaligned{{.*}}+0x{{[0-9a-f]*}}): runtime error: constructor call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment // CHECK-NEW-NEXT: [[PTR]]: note: pointer points here // CHECK-NEW-NEXT: {{^ 00 00 00 01 02 03 04 05}} // CHECK-NEW-NEXT: {{^ \^}} diff --git a/lib/ubsan/lit_tests/TypeCheck/null.cpp b/lib/ubsan/lit_tests/TestCases/TypeCheck/null.cpp index f72af28ce160..79726924d213 100644 --- a/lib/ubsan/lit_tests/TypeCheck/null.cpp +++ b/lib/ubsan/lit_tests/TestCases/TypeCheck/null.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -fsanitize=null %s -O3 -o %t +// RUN: %clangxx -fsanitize=null %s -O3 -o %t // RUN: %t l 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD // RUN: %t s 2>&1 | FileCheck %s --check-prefix=CHECK-STORE // RUN: %t r 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE diff --git a/lib/ubsan/lit_tests/TypeCheck/vptr.cpp b/lib/ubsan/lit_tests/TestCases/TypeCheck/vptr.cpp index 109e7a824f58..9095f7279a66 100644 --- a/lib/ubsan/lit_tests/TypeCheck/vptr.cpp +++ b/lib/ubsan/lit_tests/TestCases/TypeCheck/vptr.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -ccc-cxx -fsanitize=vptr %s -O3 -o %t +// RUN: %clangxx -fsanitize=vptr %s -O3 -o %t // RUN: %t rT && %t mT && %t fT && %t cT // RUN: %t rU && %t mU && %t fU && %t cU // RUN: %t rS && %t rV && %t oV @@ -101,7 +101,7 @@ int main(int, char **argv) { // CHECK-OFFSET: vptr.cpp:[[@LINE+5]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'U' // CHECK-OFFSET-NEXT: 0x{{[0-9a-f]*}}: note: object is base class subobject at offset {{8|16}} within object of type [[DYN_TYPE:'U']] // CHECK-OFFSET-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. }} - // CHECK-OFFSET-NEXT: {{^ \^ ( ~~~~~~~~~~~~)~~~~~~~~~~~ *$}} + // CHECK-OFFSET-NEXT: {{^ \^ ( ~~~~~~~~~~~~)?~~~~~~~~~~~ *$}} // CHECK-OFFSET-NEXT: {{^ ( )?vptr for}} 'T' base class of [[DYN_TYPE]] return reinterpret_cast<U*>(p)->v() - 2; diff --git a/lib/ubsan/lit_tests/UbsanConfig/lit.cfg b/lib/ubsan/lit_tests/UbsanConfig/lit.cfg new file mode 100644 index 000000000000..fcc93035c600 --- /dev/null +++ b/lib/ubsan/lit_tests/UbsanConfig/lit.cfg @@ -0,0 +1,23 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +ubsan_lit_tests_dir = get_required_attr(config, "ubsan_lit_tests_dir") +ubsan_lit_cfg = os.path.join(ubsan_lit_tests_dir, "lit.common.cfg") +lit_config.load_config(config, ubsan_lit_cfg) + +config.name = 'UndefinedBehaviorSanitizer-Standalone' + +# Define %clang and %clangxx substitutions to use in test RUN lines. +config.substitutions.append( ("%clang ", (" " + config.clang + " ")) ) +config.substitutions.append( ("%clangxx ", (" " + config.clang + + " --driver-mode=g++ ")) ) diff --git a/lib/ubsan/lit_tests/UbsanConfig/lit.site.cfg.in b/lib/ubsan/lit_tests/UbsanConfig/lit.site.cfg.in new file mode 100644 index 000000000000..c08fc30d0042 --- /dev/null +++ b/lib/ubsan/lit_tests/UbsanConfig/lit.site.cfg.in @@ -0,0 +1,8 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Tool-specific config options. +config.ubsan_lit_tests_dir = "@UBSAN_LIT_TESTS_DIR@" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@UBSAN_LIT_TESTS_DIR@/UbsanConfig/lit.cfg") diff --git a/lib/ubsan/lit_tests/lit.cfg b/lib/ubsan/lit_tests/lit.cfg deleted file mode 100644 index ea6ebdf9001f..000000000000 --- a/lib/ubsan/lit_tests/lit.cfg +++ /dev/null @@ -1,66 +0,0 @@ -# -*- Python -*- - -import os - -def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if not attr_value: - lit.fatal("No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) - return attr_value - -# Setup config name. -config.name = 'UndefinedBehaviorSanitizer' - -# Setup source root. -config.test_source_root = os.path.dirname(__file__) - -def DisplayNoConfigMessage(): - lit.fatal("No site specific configuration available! " + - "Try running your test from the build tree or running " + - "make check-ubsan") - -# Figure out LLVM source root. -llvm_src_root = getattr(config, 'llvm_src_root', None) -if llvm_src_root is None: - # We probably haven't loaded the site-specific configuration: the user - # is likely trying to run a test file directly, and the site configuration - # wasn't created by the build system or we're performing an out-of-tree build. - ubsan_site_cfg = lit.params.get('ubsan_site_config', None) - if ubsan_site_cfg and os.path.exists(ubsan_site_cfg): - lit.load_config(config, ubsan_site_cfg) - raise SystemExit - - # Try to guess the location of site-specific configuration using llvm-config - # util that can point where the build tree is. - llvm_config = lit.util.which("llvm-config", config.environment["PATH"]) - if not llvm_config: - DisplayNoConfigMessage() - - # Find out the presumed location of generated site config. - llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip() - ubsan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt", - "lib", "ubsan", "lit_tests", "lit.site.cfg") - if not ubsan_site_cfg or not os.path.exists(ubsan_site_cfg): - DisplayNoConfigMessage() - - lit.load_config(config, ubsan_site_cfg) - raise SystemExit - -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') -compiler_rt_lit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.cfg") -if not compiler_rt_lit_cfg or not os.path.exists(compiler_rt_lit_cfg): - lit.fatal("Can't find common compiler-rt lit config at: %r" - % compiler_rt_lit_cfg) -lit.load_config(config, compiler_rt_lit_cfg) - -# Default test suffixes. -config.suffixes = ['.c', '.cc', '.cpp'] - -# UndefinedBehaviorSanitizer tests are currently supported on -# Linux and Darwin only. -if config.host_os not in ['Linux', 'Darwin']: - config.unsupported = True diff --git a/lib/ubsan/lit_tests/lit.common.cfg b/lib/ubsan/lit_tests/lit.common.cfg new file mode 100644 index 000000000000..23c85e9fa065 --- /dev/null +++ b/lib/ubsan/lit_tests/lit.common.cfg @@ -0,0 +1,31 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup source root. +ubsan_lit_tests_dir = get_required_attr(config, 'ubsan_lit_tests_dir') +config.test_source_root = os.path.join(ubsan_lit_tests_dir, 'TestCases') + +def DisplayNoConfigMessage(): + lit_config.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-ubsan") + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# UndefinedBehaviorSanitizer tests are currently supported on +# Linux and Darwin only. +if config.host_os not in ['Linux', 'Darwin']: + config.unsupported = True + +config.pipefail = False diff --git a/lib/ubsan/lit_tests/lit.site.cfg.in b/lib/ubsan/lit_tests/lit.site.cfg.in deleted file mode 100644 index 07b521af061f..000000000000 --- a/lib/ubsan/lit_tests/lit.site.cfg.in +++ /dev/null @@ -1,20 +0,0 @@ -## Autogenerated by LLVM/Clang configuration. -# Do not edit! - -config.clang = "@LLVM_BINARY_DIR@/bin/clang" -config.host_os = "@HOST_OS@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" -config.target_triple = "@TARGET_TRIPLE@" - -# LLVM tools dir can be passed in lit parameters, so try to -# apply substitution. -try: - config.llvm_tools_dir = config.llvm_tools_dir % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) - -# Let the main config do the real work. -lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/lib/ubsan/ubsan.syms b/lib/ubsan/ubsan.syms deleted file mode 100644 index e74de33f012c..000000000000 --- a/lib/ubsan/ubsan.syms +++ /dev/null @@ -1 +0,0 @@ -{ __ubsan_*; }; diff --git a/lib/ubsan/ubsan.syms.extra b/lib/ubsan/ubsan.syms.extra new file mode 100644 index 000000000000..7f8be694401a --- /dev/null +++ b/lib/ubsan/ubsan.syms.extra @@ -0,0 +1 @@ +__ubsan_* diff --git a/lib/ubsan/ubsan_diag.cc b/lib/ubsan/ubsan_diag.cc index 3f92761465de..fa643658ea36 100644 --- a/lib/ubsan/ubsan_diag.cc +++ b/lib/ubsan/ubsan_diag.cc @@ -26,11 +26,21 @@ Location __ubsan::getCallerLocation(uptr CallerLoc) { return Location(); uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc); + return getFunctionLocation(Loc, 0); +} + +Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) { + if (!Loc) + return Location(); AddressInfo Info; - if (!SymbolizeCode(Loc, &Info, 1) || !Info.module || !*Info.module) + if (!Symbolizer::GetOrInit()->SymbolizeCode(Loc, &Info, 1) || + !Info.module || !*Info.module) return Location(Loc); + if (FName && Info.function) + *FName = Info.function; + if (!Info.file) return ModuleLocation(Info.module, Info.module_offset); @@ -67,29 +77,29 @@ static void PrintHex(UIntMax Val) { } static void renderLocation(Location Loc) { + InternalScopedString LocBuffer(1024); switch (Loc.getKind()) { case Location::LK_Source: { SourceLocation SLoc = Loc.getSourceLocation(); if (SLoc.isInvalid()) - Printf("<unknown>:"); - else { - Printf("%s:%d:", SLoc.getFilename(), SLoc.getLine()); - if (SLoc.getColumn()) - Printf("%d:", SLoc.getColumn()); - } + LocBuffer.append("<unknown>"); + else + PrintSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(), + SLoc.getColumn()); break; } case Location::LK_Module: - Printf("%s:0x%zx:", Loc.getModuleLocation().getModuleName(), - Loc.getModuleLocation().getOffset()); + PrintModuleAndOffset(&LocBuffer, Loc.getModuleLocation().getModuleName(), + Loc.getModuleLocation().getOffset()); break; case Location::LK_Memory: - Printf("%p:", Loc.getMemoryLocation()); + LocBuffer.append("%p", Loc.getMemoryLocation()); break; case Location::LK_Null: - Printf("<unknown>:"); + LocBuffer.append("<unknown>"); break; } + Printf("%s:", LocBuffer.data()); } static void renderText(const char *Message, const Diag::Arg *Args) { @@ -109,7 +119,7 @@ static void renderText(const char *Message, const Diag::Arg *Args) { Printf("%s", A.String); break; case Diag::AK_Mangled: { - Printf("'%s'", Demangle(A.String)); + Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String)); break; } case Diag::AK_SInt: diff --git a/lib/ubsan/ubsan_diag.h b/lib/ubsan/ubsan_diag.h index 16afffdb0a76..54d15a0cc1ba 100644 --- a/lib/ubsan/ubsan_diag.h +++ b/lib/ubsan/ubsan_diag.h @@ -80,6 +80,12 @@ public: /// an invalid location or a module location for the caller. Location getCallerLocation(uptr CallerLoc = GET_CALLER_PC()); +/// Try to obtain a location for the given function pointer. This might fail, +/// and produce either an invalid location or a module location for the caller. +/// If FName is non-null and the name of the function is known, set *FName to +/// the function name, otherwise *FName is unchanged. +Location getFunctionLocation(uptr Loc, const char **FName); + /// A diagnostic severity level. enum DiagLevel { DL_Error, ///< An error. diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc index fa93b095d0bb..d55643143295 100644 --- a/lib/ubsan/ubsan_handlers.cc +++ b/lib/ubsan/ubsan_handlers.cc @@ -246,15 +246,36 @@ void __ubsan::__ubsan_handle_float_cast_overflow_abort( void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data, ValueHandle Val) { - // TODO: Add deduplication once a SourceLocation is generated for this check. - Diag(getCallerLocation(), DL_Error, + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Diag(Loc, DL_Error, "load of value %0, which is not a valid value for type %1") << Value(Data->Type, Val) << Data->Type; } void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data, ValueHandle Val) { - Diag(getCallerLocation(), DL_Error, - "load of value %0, which is not a valid value for type %1") - << Value(Data->Type, Val) << Data->Type; + __ubsan_handle_load_invalid_value(Data, Val); + Die(); +} + +void __ubsan::__ubsan_handle_function_type_mismatch( + FunctionTypeMismatchData *Data, + ValueHandle Function) { + const char *FName = "(unknown)"; + + Location Loc = getFunctionLocation(Function, &FName); + + Diag(Data->Loc, DL_Error, + "call to function %0 through pointer to incorrect function type %1") + << FName << Data->Type; + Diag(Loc, DL_Note, "%0 defined here") << FName; +} + +void __ubsan::__ubsan_handle_function_type_mismatch_abort( + FunctionTypeMismatchData *Data, + ValueHandle Function) { + __ubsan_handle_function_type_mismatch(Data, Function); Die(); } diff --git a/lib/ubsan/ubsan_handlers.h b/lib/ubsan/ubsan_handlers.h index 5e237e1aa2de..14e6f04c2a1b 100644 --- a/lib/ubsan/ubsan_handlers.h +++ b/lib/ubsan/ubsan_handlers.h @@ -105,13 +105,22 @@ struct FloatCastOverflowData { RECOVERABLE(float_cast_overflow, FloatCastOverflowData *Data, ValueHandle From) struct InvalidValueData { - // FIXME: SourceLocation Loc; + SourceLocation Loc; const TypeDescriptor &Type; }; /// \brief Handle a load of an invalid value for the type. RECOVERABLE(load_invalid_value, InvalidValueData *Data, ValueHandle Val) +struct FunctionTypeMismatchData { + SourceLocation Loc; + const TypeDescriptor &Type; +}; + +RECOVERABLE(function_type_mismatch, + FunctionTypeMismatchData *Data, + ValueHandle Val) + } #endif // UBSAN_HANDLERS_H diff --git a/lib/ubsan/ubsan_type_hash.cc b/lib/ubsan/ubsan_type_hash.cc index b27aefc16821..a388bcc6d72e 100644 --- a/lib/ubsan/ubsan_type_hash.cc +++ b/lib/ubsan/ubsan_type_hash.cc @@ -85,16 +85,18 @@ namespace abi = __cxxabiv1; // reused as needed. The second caching layer is a large hash table with open // chaining. We can freely evict from either layer since this is just a cache. // -// FIXME: Make these hash table accesses thread-safe. The races here are benign -// (worst-case, we could miss a bug or see a slowdown) but we should -// avoid upsetting race detectors. +// FIXME: Make these hash table accesses thread-safe. The races here are benign: +// assuming the unsequenced loads and stores don't misbehave too badly, +// the worst case is false negatives or poor cache behavior, not false +// positives or crashes. /// Find a bucket to store the given hash value in. static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) { static const unsigned HashTableSize = 65537; - static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize] = { 1 }; + static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize]; - unsigned Probe = V & 65535; + unsigned First = (V & 65535) ^ 1; + unsigned Probe = First; for (int Tries = 5; Tries; --Tries) { if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V) return &__ubsan_vptr_hash_set[Probe]; @@ -104,12 +106,12 @@ static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) { } // FIXME: Pick a random entry from the probe sequence to evict rather than // just taking the first. - return &__ubsan_vptr_hash_set[V]; + return &__ubsan_vptr_hash_set[First]; } /// A cache of recently-checked hashes. Mini hash table with "random" evictions. __ubsan::HashValue -__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize] = { 1 }; +__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize]; /// \brief Determine whether \p Derived has a \p Base base class subobject at /// offset \p Offset. diff --git a/make/AppleBI.mk b/make/AppleBI.mk index d3d4771309c4..b236152158ff 100644 --- a/make/AppleBI.mk +++ b/make/AppleBI.mk @@ -12,10 +12,14 @@ else ProjObjRoot := $(ProjSrcRoot) endif -ifeq (,$(SDKROOT)) +ifeq (,$(RC_PURPLE)) INSTALL_TARGET = install-MacOSX else - INSTALL_TARGET = install-iOS + ifeq (,$(RC_INDIGO)) + INSTALL_TARGET = install-iOS + else + INSTALL_TARGET = install-iOS-Simulator + endif endif @@ -61,9 +65,9 @@ $(OBJROOT)/libcompiler_rt-%.dylib : $(OBJROOT)/darwin_bni/Release/%/libcompiler_ -Wl,-upward-lunwind \ -Wl,-upward-lsystem_m \ -Wl,-upward-lsystem_c \ + -Wl,-upward-lsystem_kernel \ -Wl,-upward-lsystem_platform \ -Wl,-ldyld \ - -Wl,-lsystem_kernel \ -L$(SDKROOT)/usr/lib/system \ $(DYLIB_FLAGS) -Wl,-force_load,$^ -o $@ @@ -88,7 +92,6 @@ install-iOS: $(SYMROOT)/libcompiler_rt-static.a \ $(call GetCNAVar,STRIP,Platform.darwin_bni,Release,) -S $(SYMROOT)/libcompiler_rt.dylib \ -o $(DSTROOT)/usr/lib/system/libcompiler_rt.dylib - # Rule to make fat archive $(SYMROOT)/libcompiler_rt-static.a : $(foreach arch,$(RC_ARCHS), \ $(OBJROOT)/darwin_bni/Static/$(arch)/libcompiler_rt.a) @@ -109,3 +112,38 @@ $(SYMROOT)/libcompiler_rt-dyld.a : $(foreach arch,$(RC_ARCHS), \ $(OBJROOT)/libcompiler_rt-dyld-$(arch).a) $(call GetCNAVar,LIPO,Platform.darwin_bni,Release,) -create $^ -o $@ + + +# Copy results to DSTROOT. +install-iOS-Simulator: $(SYMROOT)/libcompiler_rt_sim.dylib \ + $(SYMROOT)/libcompiler_rt-dyld.a + mkdir -p $(DSTROOT)/$(SDKROOT)/usr/lib/system + $(call GetCNAVar,STRIP,Platform.darwin_bni,Release,) -S $(SYMROOT)/libcompiler_rt_sim.dylib \ + -o $(DSTROOT)/$(SDKROOT)/usr/lib/system/libcompiler_rt_sim.dylib + mkdir -p $(DSTROOT)/$(SDKROOT)/usr/local/lib/dyld + cp $(SYMROOT)/libcompiler_rt-dyld.a \ + $(DSTROOT)/$(SDKROOT)/usr/local/lib/dyld/libcompiler_rt.a + +# Rule to make fat dylib +$(SYMROOT)/libcompiler_rt_sim.dylib: $(foreach arch,$(RC_ARCHS), \ + $(OBJROOT)/libcompiler_rt_sim-$(arch).dylib) + $(call GetCNAVar,LIPO,Platform.darwin_bni,Release,) -create $^ -o $@ + $(call GetCNAVar,DSYMUTIL,Platform.darwin_bni,Release,) $@ + +# Rule to make each dylib slice +$(OBJROOT)/libcompiler_rt_sim-%.dylib : $(OBJROOT)/darwin_bni/Release/%/libcompiler_rt.a + echo "const char vers[] = \"@(#) $(RC_ProjectName)-$(RC_ProjectSourceVersion)\"; " > $(OBJROOT)/version.c + $(call GetCNAVar,CC,Platform.darwin_bni,Release,$*) \ + $(OBJROOT)/version.c -arch $* -dynamiclib \ + -install_name /usr/lib/system/libcompiler_rt_sim.dylib \ + -compatibility_version 1 -current_version $(RC_ProjectSourceVersion) \ + -Wl,-unexported_symbol,___enable_execute_stack \ + -nostdlib \ + -Wl,-upward-lunwind_sim \ + -Wl,-upward-lsystem_sim_m \ + -Wl,-upward-lsystem_sim_c \ + -ldyld_sim \ + -Wl,-upward-lSystem \ + -umbrella System -Wl,-no_implicit_dylibs -L$(SDKROOT)/usr/lib/system -dead_strip \ + $(DYLIB_FLAGS) -Wl,-force_load,$^ -o $@ + diff --git a/make/platform/clang_darwin.mk b/make/platform/clang_darwin.mk index cb61744e5b7e..ddb702944874 100644 --- a/make/platform/clang_darwin.mk +++ b/make/platform/clang_darwin.mk @@ -25,9 +25,24 @@ CheckArches = \ done; \ echo $$result) +XCRun = \ + $(shell \ + result=`xcrun -find $(1) 2> /dev/null`; \ + if [ "$$?" != "0" ]; then result=$(1); fi; \ + echo $$result) +XCRunSdkPath = \ + $(shell \ + result=`xcrun --sdk $(1) --show-sdk-path 2> /dev/null`; \ + if [ "$$?" != "0" ]; then result=""; fi; \ + echo $$result) ### -CC := clang +CC := $(call XCRun,clang) +AR := $(call XCRun,ar) +RANLIB := $(call XCRun,ranlib) +STRIP := $(call XCRun,strip) +LIPO := $(call XCRun,lipo) +DSYMUTIL := $(call XCRun,dsymutil) Configs := UniversalArchs := @@ -73,6 +88,12 @@ UniversalArchs.profile_ios := $(call CheckArches,i386 x86_64 armv7,profile_ios) Configs += asan_osx_dynamic UniversalArchs.asan_osx_dynamic := $(call CheckArches,i386 x86_64,asan_osx_dynamic) +IOSSIM_SDK_PATH := $(call XCRunSdkPath,iphonesimulator) +ifneq ($(IOSSIM_SDK_PATH),) +Configs += asan_iossim_dynamic +UniversalArchs.asan_iossim_dynamic := $(call CheckArches,i386 x86_64,asan_iossim_dynamic) +endif + Configs += ubsan_osx UniversalArchs.ubsan_osx := $(call CheckArches,i386 x86_64,ubsan_osx) @@ -127,12 +148,20 @@ CFLAGS.eprintf := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) CFLAGS.10.4 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) # FIXME: We can't build ASAN with our stub SDK yet. CFLAGS.asan_osx_dynamic := \ - $(CFLAGS) -mmacosx-version-min=10.5 -fno-builtin \ + $(CFLAGS) -mmacosx-version-min=10.6 -fno-builtin \ -gline-tables-only \ -DMAC_INTERPOSE_FUNCTIONS=1 \ -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=1 -CFLAGS.ubsan_osx := $(CFLAGS) -mmacosx-version-min=10.5 -fno-builtin +CFLAGS.asan_iossim_dynamic := \ + $(CFLAGS) -mios-simulator-version-min=7.0 \ + -isysroot $(IOSSIM_SDK_PATH) \ + -fno-builtin \ + -gline-tables-only \ + -DMAC_INTERPOSE_FUNCTIONS=1 \ + -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=1 + +CFLAGS.ubsan_osx := $(CFLAGS) -mmacosx-version-min=10.6 -fno-builtin CFLAGS.ios.i386 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS) CFLAGS.ios.x86_64 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS) @@ -163,7 +192,15 @@ CFLAGS.profile_ios.armv7s := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) # Configure the asan_osx_dynamic library to be built shared. SHARED_LIBRARY.asan_osx_dynamic := 1 -LDFLAGS.asan_osx_dynamic := -framework Foundation -lstdc++ -undefined dynamic_lookup +LDFLAGS.asan_osx_dynamic := -lstdc++ -undefined dynamic_lookup + +# Configure the asan_iossim_dynamic library to be built shared. +SHARED_LIBRARY.asan_iossim_dynamic := 1 +# configure+make uses Clang, so we're using isysroot instead of --sysroot +# or -Wl,-syslibroot. +LDFLAGS.asan_iossim_dynamic := -undefined dynamic_lookup \ + -Wl,-ios_simulator_version_min,7.0.0 \ + -mios-simulator-version-min=7.0 -isysroot $(IOSSIM_SDK_PATH) FUNCTIONS.eprintf := eprintf FUNCTIONS.10.4 := eprintf floatundidf floatundisf floatundixf @@ -184,6 +221,10 @@ FUNCTIONS.asan_osx_dynamic := $(AsanFunctions) $(InterceptionFunctions) \ $(SanitizerCommonFunctions) \ $(AsanDynamicFunctions) +FUNCTIONS.asan_iossim_dynamic := $(AsanFunctions) $(InterceptionFunctions) \ + $(SanitizerCommonFunctions) \ + $(AsanDynamicFunctions) + FUNCTIONS.ubsan_osx := $(UbsanFunctions) $(UbsanCXXFunctions) \ $(SanitizerCommonFunctions) diff --git a/make/platform/clang_darwin_embedded.mk b/make/platform/clang_darwin_embedded.mk new file mode 100644 index 000000000000..3cea2e45c8ab --- /dev/null +++ b/make/platform/clang_darwin_embedded.mk @@ -0,0 +1,250 @@ +# These are the functions which clang needs when it is targetting a previous +# version of the OS. The issue is that the backend may use functions which were +# not present in the libgcc that shipped on the platform. In such cases, we link +# with a version of the library which contains private_extern definitions of all +# the extra functions which might be referenced. + +Description := Static runtime libraries for embedded clang/Darwin + +XCRun = \ + $(shell \ + result=`xcrun -find $(1) 2> /dev/null`; \ + if [ "$$?" != "0" ]; then result=$(1); fi; \ + echo $$result) + +### + +CC := $(call XCRun,clang) +AR := $(call XCRun,ar) +RANLIB := $(call XCRun,ranlib) +STRIP := $(call XCRun,strip) +LIPO := $(call XCRun,lipo) +DSYMUTIL := $(call XCRun,dsymutil) + +Configs := +UniversalArchs := + +# Soft-float version of the runtime. No floating-point instructions will be used +# and the ABI (out of necessity) passes floating values in normal registers: +# non-VFP variant of the AAPCS. +Configs += soft_static +UniversalArchs.soft_static := armv6m armv7m armv7em armv7 + +# Hard-float version of the runtime. On ARM VFP instructions and registers are +# allowed, and floating point values get passed in them. VFP variant of the +# AAPCS. +Configs += hard_static +UniversalArchs.hard_static := armv7em armv7 i386 x86_64 + +Configs += soft_pic +UniversalArchs.soft_pic := armv6m armv7m armv7em armv7 + +Configs += hard_pic +UniversalArchs.hard_pic := armv7em armv7 i386 x86_64 + +CFLAGS := -Wall -Werror -Oz -fomit-frame-pointer -ffreestanding + +PIC_CFLAGS := -fPIC +STATIC_CFLAGS := -static + +CFLAGS_SOFT := -mfloat-abi=soft +CFLAGS_HARD := -mfloat-abi=hard + +CFLAGS_ARMV7 := -target thumbv7-apple-darwin-eabi +CFLAGS_I386 := -march=pentium + +CFLAGS.soft_static := $(CFLAGS) $(STATIC_CFLAGS) $(CFLAGS_SOFT) +CFLAGS.hard_static := $(CFLAGS) $(STATIC_CFLAGS) $(CFLAGS_HARD) +CFLAGS.soft_pic := $(CFLAGS) $(PIC_CFLAGS) $(CFLAGS_SOFT) +CFLAGS.hard_pic := $(CFLAGS) $(PIC_CFLAGS) $(CFLAGS_HARD) + +CFLAGS.soft_static.armv7 := $(CFLAGS.soft_static) $(CFLAGS_ARMV7) +CFLAGS.hard_static.armv7 := $(CFLAGS.hard_static) $(CFLAGS_ARMV7) +CFLAGS.soft_pic.armv7 := $(CFLAGS.soft_pic) $(CFLAGS_ARMV7) +CFLAGS.hard_pic.armv7 := $(CFLAGS.hard_pic) $(CFLAGS_ARMV7) + +# x86 platforms ignore -mfloat-abi options and complain about doing so. Despite +# this they're hard-float. +CFLAGS.hard_static.i386 := $(CFLAGS) $(STATIC_CFLAGS) $(CFLAGS_I386) +CFLAGS.hard_pic.i386 := $(CFLAGS) $(PIC_CFLAGS) $(CFLAGS_I386) +CFLAGS.hard_static.x86_64 := $(CFLAGS) $(STATIC_CFLAGS) +CFLAGS.hard_pic.x86_64 := $(CFLAGS) $(PIC_CFLAGS) + +# Functions not wanted: +# + eprintf is obsolete anyway +# + *vfp: designed for Thumb1 CPUs with VFPv2 + +COMMON_FUNCTIONS := \ + absvdi2 \ + absvsi2 \ + addvdi3 \ + addvsi3 \ + ashldi3 \ + ashrdi3 \ + bswapdi2 \ + bswapsi2 \ + clzdi2 \ + clzsi2 \ + cmpdi2 \ + ctzdi2 \ + ctzsi2 \ + divdc3 \ + divdi3 \ + divsc3 \ + divmodsi4 \ + udivmodsi4 \ + do_global_dtors \ + ffsdi2 \ + fixdfdi \ + fixsfdi \ + fixunsdfdi \ + fixunsdfsi \ + fixunssfdi \ + fixunssfsi \ + floatdidf \ + floatdisf \ + floatundidf \ + floatundisf \ + gcc_bcmp \ + lshrdi3 \ + moddi3 \ + muldc3 \ + muldi3 \ + mulsc3 \ + mulvdi3 \ + mulvsi3 \ + negdi2 \ + negvdi2 \ + negvsi2 \ + paritydi2 \ + paritysi2 \ + popcountdi2 \ + popcountsi2 \ + powidf2 \ + powisf2 \ + subvdi3 \ + subvsi3 \ + ucmpdi2 \ + udiv_w_sdiv \ + udivdi3 \ + udivmoddi4 \ + umoddi3 \ + adddf3 \ + addsf3 \ + cmpdf2 \ + cmpsf2 \ + div0 \ + divdf3 \ + divsf3 \ + divsi3 \ + extendsfdf2 \ + ffssi2 \ + fixdfsi \ + fixsfsi \ + floatsidf \ + floatsisf \ + floatunsidf \ + floatunsisf \ + comparedf2 \ + comparesf2 \ + modsi3 \ + muldf3 \ + mulsf3 \ + negdf2 \ + negsf2 \ + subdf3 \ + subsf3 \ + truncdfsf2 \ + udivsi3 \ + umodsi3 \ + unorddf2 \ + unordsf2 + +ARM_FUNCTIONS := \ + aeabi_cdcmpeq \ + aeabi_cdrcmple \ + aeabi_cfcmpeq \ + aeabi_cfrcmple \ + aeabi_dcmpeq \ + aeabi_dcmpge \ + aeabi_dcmpgt \ + aeabi_dcmple \ + aeabi_dcmplt \ + aeabi_drsub \ + aeabi_fcmpeq \ + aeabi_fcmpge \ + aeabi_fcmpgt \ + aeabi_fcmple \ + aeabi_fcmplt \ + aeabi_frsub \ + aeabi_idivmod \ + aeabi_uidivmod \ + +# ARM Assembly implementation which requires Thumb2 (i.e. won't work on v6M). +THUMB2_FUNCTIONS := \ + switch16 \ + switch32 \ + switch8 \ + switchu8 \ + +I386_FUNCTIONS := \ + i686.get_pc_thunk.eax \ + i686.get_pc_thunk.ebp \ + i686.get_pc_thunk.ebx \ + i686.get_pc_thunk.ecx \ + i686.get_pc_thunk.edi \ + i686.get_pc_thunk.edx \ + i686.get_pc_thunk.esi + +# FIXME: Currently, compiler-rt is missing implementations for a number of the +# functions. Filter them out for now. +MISSING_FUNCTIONS := \ + cmpdf2 cmpsf2 div0 \ + ffssi2 \ + udiv_w_sdiv unorddf2 unordsf2 bswapdi2 \ + bswapsi2 \ + gcc_bcmp \ + do_global_dtors \ + i686.get_pc_thunk.eax i686.get_pc_thunk.ebp i686.get_pc_thunk.ebx \ + i686.get_pc_thunk.ecx i686.get_pc_thunk.edi i686.get_pc_thunk.edx \ + i686.get_pc_thunk.esi \ + aeabi_cdcmpeq aeabi_cdrcmple aeabi_cfcmpeq aeabi_cfrcmple aeabi_dcmpeq \ + aeabi_dcmpge aeabi_dcmpgt aeabi_dcmple aeabi_dcmplt aeabi_drsub \ + aeabi_fcmpeq \ aeabi_fcmpge aeabi_fcmpgt aeabi_fcmple aeabi_fcmplt \ + aeabi_frsub aeabi_idivmod aeabi_uidivmod + +FUNCTIONS_ARMV6M := $(COMMON_FUNCTIONS) $(ARM_FUNCTIONS) +FUNCTIONS_ARM_ALL := $(COMMON_FUNCTIONS) $(ARM_FUNCTIONS) $(THUMB2_FUNCTIONS) +FUNCTIONS_I386 := $(COMMON_FUNCTIONS) $(I386_FUNCTIONS) +FUNCTIONS_X86_64 := $(COMMON_FUNCTIONS) + +FUNCTIONS_ARMV6M := \ + $(filter-out $(MISSING_FUNCTIONS),$(FUNCTIONS_ARMV6M)) +FUNCTIONS_ARM_ALL := \ + $(filter-out $(MISSING_FUNCTIONS),$(FUNCTIONS_ARM_ALL)) +FUNCTIONS_I386 := \ + $(filter-out $(MISSING_FUNCTIONS),$(FUNCTIONS_I386)) +FUNCTIONS_X86_64 := \ + $(filter-out $(MISSING_FUNCTIONS),$(FUNCTIONS_X86_64)) + +FUNCTIONS.soft_static.armv6m := $(FUNCTIONS_ARMV6M) +FUNCTIONS.soft_pic.armv6m := $(FUNCTIONS_ARMV6M) + +FUNCTIONS.soft_static.armv7m := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.soft_pic.armv7m := $(FUNCTIONS_ARM_ALL) + +FUNCTIONS.soft_static.armv7em := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.hard_static.armv7em := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.soft_pic.armv7em := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.hard_pic.armv7em := $(FUNCTIONS_ARM_ALL) + +FUNCTIONS.soft_static.armv7 := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.hard_static.armv7 := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.soft_pic.armv7 := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.hard_pic.armv7 := $(FUNCTIONS_ARM_ALL) + +FUNCTIONS.hard_static.i386 := $(FUNCTIONS_I386) +FUNCTIONS.hard_pic.i386 := $(FUNCTIONS_I386) + +FUNCTIONS.hard_static.x86_64 := $(FUNCTIONS_X86_64) +FUNCTIONS.hard_pic.x86_64 := $(FUNCTIONS_X86_64) diff --git a/make/platform/clang_linux.mk b/make/platform/clang_linux.mk index 05efdb6d67ea..5796e8671fc9 100644 --- a/make/platform/clang_linux.mk +++ b/make/platform/clang_linux.mk @@ -12,11 +12,9 @@ Configs := # compiler and only define configurations we know that compiler can generate. CompilerTargetTriple := $(shell \ $(CC) -v 2>&1 | grep 'Target:' | cut -d' ' -f2) -ifneq ($(DEBUGMAKE),) ifeq ($(CompilerTargetTriple),) $(error "unable to infer compiler target triple for $(CC)") endif -endif # Only define configs if we detected a linux target. ifneq ($(findstring -linux-,$(CompilerTargetTriple)),) @@ -63,7 +61,7 @@ endif # Build runtime libraries for x86_64. ifeq ($(call contains,$(SupportedArches),x86_64),true) Configs += full-x86_64 profile-x86_64 san-x86_64 asan-x86_64 tsan-x86_64 \ - msan-x86_64 ubsan-x86_64 ubsan_cxx-x86_64 + msan-x86_64 ubsan-x86_64 ubsan_cxx-x86_64 dfsan-x86_64 lsan-x86_64 Arch.full-x86_64 := x86_64 Arch.profile-x86_64 := x86_64 Arch.san-x86_64 := x86_64 @@ -72,6 +70,10 @@ Arch.tsan-x86_64 := x86_64 Arch.msan-x86_64 := x86_64 Arch.ubsan-x86_64 := x86_64 Arch.ubsan_cxx-x86_64 := x86_64 +Arch.dfsan-x86_64 := x86_64 +Arch.lsan-x86_64 := x86_64 +endif + endif ifneq ($(LLVM_ANDROID_TOOLCHAIN_DIR),) @@ -80,7 +82,6 @@ Arch.asan-arm-android := arm-android endif endif -endif ### @@ -103,6 +104,8 @@ CFLAGS.ubsan-i386 := $(CFLAGS) -m32 $(SANITIZER_CFLAGS) -fno-rtti CFLAGS.ubsan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti CFLAGS.ubsan_cxx-i386 := $(CFLAGS) -m32 $(SANITIZER_CFLAGS) CFLAGS.ubsan_cxx-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) +CFLAGS.dfsan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) +CFLAGS.lsan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti SHARED_LIBRARY.asan-arm-android := 1 ANDROID_COMMON_FLAGS := -target arm-linux-androideabi \ @@ -114,12 +117,10 @@ LDFLAGS.asan-arm-android := $(LDFLAGS) $(ANDROID_COMMON_FLAGS) -ldl \ -Wl,-soname=libclang_rt.asan-arm-android.so # Use our stub SDK as the sysroot to support more portable building. For now we -# just do this for the non-ASAN modules, because the stub SDK doesn't have -# enough support to build ASAN. +# just do this for the core module, because the stub SDK doesn't have +# enough support to build the sanitizers or profile runtimes. CFLAGS.full-i386 += --sysroot=$(ProjSrcRoot)/SDKs/linux CFLAGS.full-x86_64 += --sysroot=$(ProjSrcRoot)/SDKs/linux -CFLAGS.profile-i386 += --sysroot=$(ProjSrcRoot)/SDKs/linux -CFLAGS.profile-x86_64 += --sysroot=$(ProjSrcRoot)/SDKs/linux FUNCTIONS.full-i386 := $(CommonFunctions) $(ArchFunctions.i386) FUNCTIONS.full-x86_64 := $(CommonFunctions) $(ArchFunctions.x86_64) @@ -141,6 +142,9 @@ FUNCTIONS.ubsan-i386 := $(UbsanFunctions) FUNCTIONS.ubsan-x86_64 := $(UbsanFunctions) FUNCTIONS.ubsan_cxx-i386 := $(UbsanCXXFunctions) FUNCTIONS.ubsan_cxx-x86_64 := $(UbsanCXXFunctions) +FUNCTIONS.dfsan-x86_64 := $(DfsanFunctions) $(SanitizerCommonFunctions) +FUNCTIONS.lsan-x86_64 := $(LsanFunctions) $(InterceptionFunctions) \ + $(SanitizerCommonFunctions) # Always use optimized variants. OPTIMIZED := 1 diff --git a/make/platform/darwin_bni.mk b/make/platform/darwin_bni.mk index afd04313e62d..03e8d290cffe 100644 --- a/make/platform/darwin_bni.mk +++ b/make/platform/darwin_bni.mk @@ -9,12 +9,12 @@ Configs := Debug Release Profile Static UniversalArchs := $(RC_ARCHS) ifneq (,$(SDKROOT)) - override CC := $(shell xcrun -sdk $(SDKROOT) -find clang) - AR := $(shell xcrun -sdk $(SDKROOT) -find ar) - RANLIB := $(shell xcrun -sdk $(SDKROOT) -find ranlib) - STRIP := $(shell xcrun -sdk $(SDKROOT) -find strip) - LIPO := $(shell xcrun -sdk $(SDKROOT) -find lipo) - DSYMUTIL := $(shell xcrun -sdk $(SDKROOT) -find dsymutil) + override CC := $(shell xcrun -sdk $(SDKROOT) -find clang || echo "false") + AR := $(shell xcrun -sdk $(SDKROOT) -find ar || echo "false") + RANLIB := $(shell xcrun -sdk $(SDKROOT) -find ranlib || echo "false") + STRIP := $(shell xcrun -sdk $(SDKROOT) -find strip || echo "false") + LIPO := $(shell xcrun -sdk $(SDKROOT) -find lipo || echo "false") + DSYMUTIL := $(shell xcrun -sdk $(SDKROOT) -find dsymutil || echo "false") endif ifneq ($(IPHONEOS_DEPLOYMENT_TARGET),) |