Update aosp/master compiler-rt for rebase to r222486.
Change-Id: I38047809dbac0425193c82e810315998adbb380d
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f35a096..472d49c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,7 +9,7 @@
# Check if compiler-rt is built as a standalone project.
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
- project(CompilerRT)
+ project(CompilerRT C CXX)
set(COMPILER_RT_STANDALONE_BUILD TRUE)
else()
set(COMPILER_RT_STANDALONE_BUILD FALSE)
@@ -160,7 +160,6 @@
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)
set(COMPILER_RT_EXTRA_ANDROID_HEADERS ${COMPILER_RT_SOURCE_DIR}/android/include)
@@ -179,9 +178,6 @@
set(TARGET_32_BIT_CFLAGS "")
endif()
-# List of architectures we can target.
-set(COMPILER_RT_SUPPORTED_ARCH)
-
function(get_target_flags_for_arch arch out_var)
list(FIND COMPILER_RT_SUPPORTED_ARCH ${arch} ARCH_INDEX)
if(ARCH_INDEX EQUAL -1)
@@ -191,52 +187,6 @@
endif()
endfunction()
-# Try to compile a very simple source file to ensure we can target the given
-# platform. We use the results of these tests to build only the various target
-# runtime libraries supported by our current compilers cross-compiling
-# abilities.
-set(SIMPLE_SOURCE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple.cc)
-file(WRITE ${SIMPLE_SOURCE} "#include <stdlib.h>\n#include <limits>\nint main() {}\n")
-
-# test_target_arch(<arch> <target flags...>)
-# Sets the target flags for a given architecture and determines if this
-# architecture is supported by trying to build a simple file.
-macro(test_target_arch arch)
- set(TARGET_${arch}_CFLAGS ${ARGN})
- try_compile(CAN_TARGET_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE}
- COMPILE_DEFINITIONS "${TARGET_${arch}_CFLAGS}"
- OUTPUT_VARIABLE TARGET_${arch}_OUTPUT
- CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${TARGET_${arch}_CFLAGS}")
- if(${CAN_TARGET_${arch}})
- list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
- elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "${arch}" OR
- "${arch}" STREQUAL "arm_android")
- # Bail out if we cannot target the architecture we plan to test.
- message(FATAL_ERROR "Cannot compile for ${arch}:\n${TARGET_${arch}_OUTPUT}")
- endif()
-endmacro()
-
-if(ANDROID)
- test_target_arch(arm_android "")
-else()
- if("${LLVM_NATIVE_ARCH}" STREQUAL "X86")
- 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")
- test_target_arch(powerpc64 ${TARGET_64_BIT_CFLAGS})
- elseif("${LLVM_NATIVE_ARCH}" STREQUAL "Mips")
- test_target_arch(mips "")
- endif()
-
- # Build ARM libraries if we are configured to test on ARM
- if("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "arm|aarch64")
- test_target_arch(arm "-march=armv7-a")
- test_target_arch(aarch64 "-march=armv8-a")
- endif()
-endif()
-
# We support running instrumented tests when we're not cross compiling
# and target a UNIX-like system or Windows.
# We can run tests on Android even when we are cross-compiling.
@@ -247,26 +197,17 @@
option(COMPILER_RT_CAN_EXECUTE_TESTS "Can we execute instrumented tests" OFF)
endif()
-# Check if compiler-rt is built with libc++.
-find_flag_in_string("${CMAKE_CXX_FLAGS}" "-stdlib=libc++"
- COMPILER_RT_USES_LIBCXX)
-
-function(filter_available_targets out_var)
- set(archs)
- foreach(arch ${ARGN})
- list(FIND COMPILER_RT_SUPPORTED_ARCH ${arch} ARCH_INDEX)
- if(NOT (ARCH_INDEX EQUAL -1) AND CAN_TARGET_${arch})
- list(APPEND archs ${arch})
- endif()
- endforeach()
- 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)
+# We have to support both static and dynamic/shared runtime on Windows.
+# Android only works with dynamic runtime.
+if(WIN32 OR ANDROID)
+option(COMPILER_RT_BUILD_SHARED_ASAN "Build shared version of AddressSanitizer runtime" ON)
+else()
option(COMPILER_RT_BUILD_SHARED_ASAN "Build shared version of AddressSanitizer runtime" OFF)
+endif()
#================================
# Setup Compiler Flags
@@ -291,23 +232,31 @@
endif()
# Provide some common commmandline flags for Sanitizer runtimes.
-append_if(COMPILER_RT_HAS_FPIC_FLAG -fPIC SANITIZER_COMMON_CFLAGS)
-append_if(COMPILER_RT_HAS_FNO_BUILTIN_FLAG -fno-builtin SANITIZER_COMMON_CFLAGS)
-append_if(COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG -fno-exceptions SANITIZER_COMMON_CFLAGS)
-append_if(COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG -fomit-frame-pointer SANITIZER_COMMON_CFLAGS)
-append_if(COMPILER_RT_HAS_FUNWIND_TABLES_FLAG -funwind-tables SANITIZER_COMMON_CFLAGS)
-append_if(COMPILER_RT_HAS_FNO_STACK_PROTECTOR_FLAG -fno-stack-protector SANITIZER_COMMON_CFLAGS)
-append_if(COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG -fvisibility=hidden SANITIZER_COMMON_CFLAGS)
-append_if(COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG -fno-function-sections SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FPIC_FLAG -fPIC SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FNO_BUILTIN_FLAG -fno-builtin SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG -fno-exceptions SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG -fomit-frame-pointer SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FUNWIND_TABLES_FLAG -funwind-tables SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FNO_STACK_PROTECTOR_FLAG -fno-stack-protector SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG -fvisibility=hidden SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG -fno-function-sections SANITIZER_COMMON_CFLAGS)
if(MSVC)
- # Remove /MD flag so that it doesn't conflict with /MT.
+ # Replace the /MD[d] flags with /MT.
+ # FIXME: In fact, sanitizers should support both /MT and /MD, see PR20214.
if(COMPILER_RT_HAS_MT_FLAG)
- string(REGEX REPLACE "(^| ) */MDd? *( |$)" "\\1 \\2" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
- list(APPEND SANITIZER_COMMON_CFLAGS /MT)
+ foreach(flag_var
+ CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+ if(${flag_var} MATCHES "/MD")
+ string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
+ elseif(${flag_var} MATCHES "/MDd")
+ string(REGEX REPLACE "/MDd" "/MT" ${flag_var} "${${flag_var}}")
+ endif()
+ endforeach()
endif()
- append_if(COMPILER_RT_HAS_Oy_FLAG /Oy- SANITIZER_COMMON_CFLAGS)
- append_if(COMPILER_RT_HAS_GS_FLAG /GS- SANITIZER_COMMON_CFLAGS)
+ append_list_if(COMPILER_RT_HAS_Oy_FLAG /Oy- SANITIZER_COMMON_CFLAGS)
+ append_list_if(COMPILER_RT_HAS_GS_FLAG /GS- SANITIZER_COMMON_CFLAGS)
endif()
# Build with optimization, unless we're in debug mode. If we're using MSVC,
@@ -326,12 +275,15 @@
endif()
# Turn off several warnings.
-append_if(COMPILER_RT_HAS_WNO_GNU_FLAG -Wno-gnu SANITIZER_COMMON_CFLAGS)
-append_if(COMPILER_RT_HAS_WNO_VARIADIC_MACROS_FLAG -Wno-variadic-macros SANITIZER_COMMON_CFLAGS)
-append_if(COMPILER_RT_HAS_WNO_C99_EXTENSIONS_FLAG -Wno-c99-extensions SANITIZER_COMMON_CFLAGS)
-append_if(COMPILER_RT_HAS_WNO_NON_VIRTUAL_DTOR_FLAG -Wno-non-virtual-dtor SANITIZER_COMMON_CFLAGS)
-append_if(COMPILER_RT_HAS_WD4722_FLAG /wd4722 SANITIZER_COMMON_CFLAGS)
-
+append_list_if(COMPILER_RT_HAS_WGNU_FLAG -Wno-gnu SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WVARIADIC_MACROS_FLAG -Wno-variadic-macros SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WC99_EXTENSIONS_FLAG -Wno-c99-extensions SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WNON_VIRTUAL_DTOR_FLAG -Wno-non-virtual-dtor SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WD4146_FLAG /wd4146 SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WD4291_FLAG /wd4291 SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WD4391_FLAG /wd4391 SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WD4722_FLAG /wd4722 SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WD4800_FLAG /wd4800 SANITIZER_COMMON_CFLAGS)
if(APPLE)
# Obtain the iOS Simulator SDK path from xcodebuild.
execute_process(
@@ -346,13 +298,10 @@
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.6)
- endif()
+ set(SANITIZER_MIN_OSX_VERSION 10.7)
+ set(CMAKE_OSX_DEPLOYMENT_TARGET "") # We're setting the flag manually below.
set(DARWIN_osx_CFLAGS -mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION})
- set(DARWIN_iossim_CFLAGS
+ set(DARWIN_iossim_CFLAGS
-mios-simulator-version-min=7.0 -isysroot ${IOSSIM_SDK_DIR})
set(DARWIN_osx_LINKFLAGS)
set(DARWIN_iossim_LINKFLAGS
@@ -361,18 +310,6 @@
-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 arm aarch64 mips)
-filter_available_targets(ASAN_SUPPORTED_ARCH x86_64 i386 powerpc64 arm mips)
-filter_available_targets(DFSAN_SUPPORTED_ARCH x86_64)
-filter_available_targets(LSAN_SUPPORTED_ARCH x86_64)
-filter_available_targets(MSAN_SUPPORTED_ARCH x86_64)
-filter_available_targets(PROFILE_SUPPORTED_ARCH x86_64 i386 arm aarch64)
-filter_available_targets(TSAN_SUPPORTED_ARCH x86_64)
-filter_available_targets(UBSAN_SUPPORTED_ARCH x86_64 i386 arm aarch64)
-
add_subdirectory(include)
set(COMPILER_RT_LIBCXX_PATH ${LLVM_MAIN_SRC_DIR}/projects/libcxx)
diff --git a/SDKs/darwin/README.txt b/SDKs/darwin/README.txt
deleted file mode 100644
index ea30af3..0000000
--- a/SDKs/darwin/README.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-The Darwin platforms are all similar enough we roll them into one SDK, and use
-preprocessor tricks to get the right definitions for the few things which
-diverge between OS X and iOS.
diff --git a/SDKs/darwin/usr/include/errno.h b/SDKs/darwin/usr/include/errno.h
deleted file mode 100644
index f06e537..0000000
--- a/SDKs/darwin/usr/include/errno.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* ===-- 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/fcntl.h b/SDKs/darwin/usr/include/fcntl.h
deleted file mode 100644
index a5f91e3..0000000
--- a/SDKs/darwin/usr/include/fcntl.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* ===-- fcntl.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/fcntl.h>
diff --git a/SDKs/darwin/usr/include/inttypes.h b/SDKs/darwin/usr/include/inttypes.h
deleted file mode 100644
index 406fa6f..0000000
--- a/SDKs/darwin/usr/include/inttypes.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/* ===-- inttypes.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 __INTTYPES_H__
-#define __INTTYPES_H__
-
-#if __WORDSIZE == 64
-#define __INTTYPE_PRI64__ "l"
-#else
-#define __INTTYPE_PRI64__ "ll"
-#endif
-
-#define PRId8 "hhd"
-#define PRId16 "hd"
-#define PRId32 "d"
-#define PRId64 __INTTYPE_PRI64__ "d"
-
-#define PRIi8 "hhi"
-#define PRIi16 "hi"
-#define PRIi32 "i"
-#define PRIi64 __INTTYPE_PRI64__ "i"
-
-#define PRIo8 "hho"
-#define PRIo16 "ho"
-#define PRIo32 "o"
-#define PRIo64 __INTTYPE_PRI64__ "o"
-
-#define PRIu8 "hhu"
-#define PRIu16 "hu"
-#define PRIu32 "u"
-#define PRIu64 __INTTYPE_PRI64__ "u"
-
-#define PRIx8 "hhx"
-#define PRIx16 "hx"
-#define PRIx32 "x"
-#define PRIx64 __INTTYPE_PRI64__ "x"
-
-#define PRIX8 "hhX"
-#define PRIX16 "hX"
-#define PRIX32 "X"
-#define PRIX64 __INTTYPE_PRI64__ "X"
-
-#define SCNd8 "hhd"
-#define SCNd16 "hd"
-#define SCNd32 "d"
-#define SCNd64 __INTTYPE_PRI64__ "d"
-
-#define SCNi8 "hhi"
-#define SCNi16 "hi"
-#define SCNi32 "i"
-#define SCNi64 __INTTYPE_PRI64__ "i"
-
-#define SCNo8 "hho"
-#define SCNo16 "ho"
-#define SCNo32 "o"
-#define SCNo64 __INTTYPE_PRI64__ "o"
-
-#define SCNu8 "hhu"
-#define SCNu16 "hu"
-#define SCNu32 "u"
-#define SCNu64 __INTTYPE_PRI64__ "u"
-
-#define SCNx8 "hhx"
-#define SCNx16 "hx"
-#define SCNx32 "x"
-#define SCNx64 __INTTYPE_PRI64__ "x"
-
-#define SCNX8 "hhX"
-#define SCNX16 "hX"
-#define SCNX32 "X"
-#define SCNX64 __INTTYPE_PRI64__ "X"
-
-#endif /* __INTTYPES_H__ */
diff --git a/SDKs/darwin/usr/include/limits.h b/SDKs/darwin/usr/include/limits.h
deleted file mode 100644
index 5495a78..0000000
--- a/SDKs/darwin/usr/include/limits.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* ===-- limits.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 __LIMITS_H__
-#define __LIMITS_H__
-
-/* This is only here as a landing pad for the include_next from the compiler's
- built-in limits.h. */
-
-#endif /* __LIMITS_H__ */
diff --git a/SDKs/darwin/usr/include/stdio.h b/SDKs/darwin/usr/include/stdio.h
deleted file mode 100644
index 1a8781f..0000000
--- a/SDKs/darwin/usr/include/stdio.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/* ===-- stdio.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 __STDIO_H__
-#define __STDIO_H__
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-typedef struct __sFILE FILE;
-typedef __SIZE_TYPE__ size_t;
-
-/* Determine the appropriate fdopen, fopen(), and fwrite() functions. */
-#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
-# if defined(__i386)
-# define __FDOPEN_NAME "_fdopen$UNIX2003"
-# define __FOPEN_NAME "_fopen$UNIX2003"
-# define __FWRITE_NAME "_fwrite$UNIX2003"
-# elif defined(__x86_64__)
-# define __FDOPEN_NAME "_fdopen"
-# define __FOPEN_NAME "_fopen"
-# define __FWRITE_NAME "_fwrite"
-# elif defined(__arm) || defined(__arm64)
-# define __FDOPEN_NAME "_fdopen"
-# define __FOPEN_NAME "_fopen"
-# define __FWRITE_NAME "_fwrite"
-# else
-# error "unrecognized architecture for targeting OS X"
-# endif
-#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
-# if defined(__i386) || defined (__x86_64)
-# define __FDOPEN_NAME "_fdopen"
-# define __FOPEN_NAME "_fopen"
-# define __FWRITE_NAME "_fwrite"
-# elif defined(__arm) || defined(__arm64)
-# define __FDOPEN_NAME "_fdopen"
-# define __FOPEN_NAME "_fopen"
-# define __FWRITE_NAME "_fwrite"
-# else
-# error "unrecognized architecture for targeting iOS"
-# endif
-#else
-# error "unrecognized architecture for targeting Darwin"
-#endif
-
-# define stderr __stderrp
-extern FILE *__stderrp;
-
-#ifndef SEEK_SET
-#define SEEK_SET 0 /* set file offset to offset */
-#endif
-#ifndef SEEK_CUR
-#define SEEK_CUR 1 /* set file offset to current plus offset */
-#endif
-#ifndef SEEK_END
-#define SEEK_END 2 /* set file offset to EOF plus offset */
-#endif
-
-int fclose(FILE *);
-int fflush(FILE *);
-FILE *fopen(const char * __restrict, const char * __restrict) __asm(__FOPEN_NAME);
-FILE *fdopen(int, const char *) __asm(__FDOPEN_NAME);
-int fprintf(FILE * __restrict, const char * __restrict, ...);
-int fputc(int, FILE *);
-size_t fwrite(const void * __restrict, size_t, size_t, FILE * __restrict)
- __asm(__FWRITE_NAME);
-size_t fread(void * __restrict, size_t, size_t, FILE * __restrict);
-long ftell(FILE *);
-int fseek(FILE *, long, int);
-int snprintf(char * __restrict, size_t, const char * __restrict, ...);
-
-#if defined(__cplusplus)
-}
-#endif
-
-#endif /* __STDIO_H__ */
diff --git a/SDKs/darwin/usr/include/stdlib.h b/SDKs/darwin/usr/include/stdlib.h
deleted file mode 100644
index b6d3171..0000000
--- a/SDKs/darwin/usr/include/stdlib.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* ===-- stdlib.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 __STDLIB_H__
-#define __STDLIB_H__
-
-#define NULL ((void *)0)
-
-typedef __SIZE_TYPE__ size_t;
-
-void abort(void) __attribute__((__noreturn__));
-int atexit(void (*)(void));
-int atoi(const char *);
-void free(void *);
-char *getenv(const char *);
-void *malloc(size_t);
-void *realloc(void *, size_t);
-
-#endif /* __STDLIB_H__ */
diff --git a/SDKs/darwin/usr/include/string.h b/SDKs/darwin/usr/include/string.h
deleted file mode 100644
index 048fdba..0000000
--- a/SDKs/darwin/usr/include/string.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* ===-- string.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 __STRING_H__
-#define __STRING_H__
-
-typedef __SIZE_TYPE__ size_t;
-
-int memcmp(const void *, const void *, size_t);
-void *memcpy(void *, const void *, size_t);
-void *memset(void *, int, size_t);
-char *strcat(char *, const char *);
-char *strcpy(char *, const char *);
-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) || defined(__arm64)
-# define __STRERROR_NAME "_strerror"
-# else
-# error "unrecognized architecture for targeting OS X"
-# endif
-#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
-# if defined(__i386) || defined (__x86_64) || defined(__arm) || defined(__arm64)
-# define __STRERROR_NAME "_strerror"
-# else
-# error "unrecognized architecture for targeting iOS"
-# endif
-#else
-# error "unrecognized architecture for targeting 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
deleted file mode 100644
index 4befe38..0000000
--- a/SDKs/darwin/usr/include/sys/errno.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* ===-- 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/SDKs/darwin/usr/include/sys/fcntl.h b/SDKs/darwin/usr/include/sys/fcntl.h
deleted file mode 100644
index 96b2438..0000000
--- a/SDKs/darwin/usr/include/sys/fcntl.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* ===-- fcntl.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_FCNTL_H_
-#define _SYS_FCNTL_H_
-
-/* Determine the appropriate open function. */
-#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
-# if defined(__i386)
-# define __OPEN_NAME "_open$UNIX2003"
-# elif defined(__x86_64__)
-# define __OPEN_NAME "_open"
-# elif defined(__arm) || defined(__arm64)
-# define __OPEN_NAME "_open"
-# else
-# error "unrecognized architecture for targeting OS X"
-# endif
-#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
-# if defined(__i386) || defined (__x86_64)
-# define __OPEN_NAME "_open"
-# elif defined(__arm) || defined(__arm64)
-# define __OPEN_NAME "_open"
-# else
-# error "unrecognized architecture for targeting iOS"
-# endif
-#else
-# error "unrecognized architecture for targeting Darwin"
-#endif
-
-#define O_RDONLY 0x0000 /* open for reading only */
-#define O_WRONLY 0x0001 /* open for writing only */
-#define O_RDWR 0x0002 /* open for reading and writing */
-#define O_ACCMODE 0x0003 /* mask for above modes */
-
-#define O_CREAT 0x0200 /* create if nonexistent */
-
-int open(const char *, int, ...) __asm(__OPEN_NAME);
-
-#endif /* !_SYS_FCNTL_H_ */
diff --git a/SDKs/darwin/usr/include/sys/mman.h b/SDKs/darwin/usr/include/sys/mman.h
deleted file mode 100644
index 84561f1..0000000
--- a/SDKs/darwin/usr/include/sys/mman.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* ===-- mman.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_MMAN_H__
-#define __SYS_MMAN_H__
-
-typedef __SIZE_TYPE__ size_t;
-
-#define PROT_NONE 0x00
-#define PROT_READ 0x01
-#define PROT_WRITE 0x02
-#define PROT_EXEC 0x04
-
-#define MAP_SHARED 0x0001
-#define MAP_PRIVATE 0x0002
-
-#define MAP_FILE 0x0000
-#define MAP_ANON 0x1000
-
-#define MS_ASYNC 0x0001
-#define MS_INVALIDATE 0x0002
-#define MS_SYNC 0x0010
-
-void *mmap(void *addr, size_t len, int prot, int flags, int fd,
- long long offset);
-int munmap(void *addr, size_t len);
-int msync(void *addr, size_t len, int flags);
-
-#endif /* __SYS_MMAN_H__ */
diff --git a/SDKs/darwin/usr/include/sys/stat.h b/SDKs/darwin/usr/include/sys/stat.h
deleted file mode 100644
index 6225f90..0000000
--- a/SDKs/darwin/usr/include/sys/stat.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* ===-- stat.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_STAT_H__
-#define __SYS_STAT_H__
-
-typedef unsigned short uint16_t;
-typedef uint16_t mode_t;
-
-int mkdir(const char *, mode_t);
-
-#endif /* __SYS_STAT_H__ */
diff --git a/SDKs/darwin/usr/include/sys/types.h b/SDKs/darwin/usr/include/sys/types.h
deleted file mode 100644
index b425767..0000000
--- a/SDKs/darwin/usr/include/sys/types.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* ===-- types.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_TYPES_H__
-#define __SYS_TYPES_H__
-
-#endif /* __SYS_TYPES_H__ */
diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake
index 0f6260a..aafccd4 100644
--- a/cmake/Modules/AddCompilerRT.cmake
+++ b/cmake/Modules/AddCompilerRT.cmake
@@ -43,7 +43,8 @@
# add_compiler_rt_runtime(<name> <arch> {STATIC,SHARED}
# SOURCES <source files>
# CFLAGS <compile flags>
-# DEFS <compile definitions>)
+# DEFS <compile definitions>
+# OUTPUT_NAME <output library name>)
macro(add_compiler_rt_runtime name arch type)
if(CAN_TARGET_${arch})
parse_arguments(LIB "SOURCES;CFLAGS;DEFS;OUTPUT_NAME" "" ${ARGN})
@@ -59,7 +60,10 @@
set_target_properties(${name} PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}
LIBRARY_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR})
- if (LIB_OUTPUT_NAME)
+ if ("${LIB_OUTPUT_NAME}" STREQUAL "")
+ set_target_properties(${name} PROPERTIES
+ OUTPUT_NAME ${name}${COMPILER_RT_OS_SUFFIX})
+ else()
set_target_properties(${name} PROPERTIES
OUTPUT_NAME ${LIB_OUTPUT_NAME})
endif()
@@ -138,6 +142,12 @@
# gtest use a lot of stuff marked as deprecated on Windows.
list(APPEND COMPILER_RT_GTEST_CFLAGS -Wno-deprecated-declarations)
+
+ # Visual Studio 2012 only supports up to 8 template parameters in
+ # std::tr1::tuple by default, but gtest requires 10
+ if(MSVC_VERSION EQUAL 1700)
+ list(APPEND COMPILER_RT_GTEST_CFLAGS -D_VARIADIC_MAX=10)
+ endif()
endif()
# Link objects into a single executable with COMPILER_RT_TEST_COMPILER,
diff --git a/cmake/Modules/CompilerRTCompile.cmake b/cmake/Modules/CompilerRTCompile.cmake
index 2d1dd22..af3df8f 100644
--- a/cmake/Modules/CompilerRTCompile.cmake
+++ b/cmake/Modules/CompilerRTCompile.cmake
@@ -11,6 +11,9 @@
if(NOT COMPILER_RT_STANDALONE_BUILD)
list(APPEND SOURCE_DEPS clang)
endif()
+ if (TARGET CompilerRTUnitTestCheckCxx)
+ list(APPEND SOURCE_DEPS CompilerRTUnitTestCheckCxx)
+ endif()
string(REGEX MATCH "[.](cc|cpp)$" is_cxx ${source_rpath})
if(is_cxx)
string(REPLACE " " ";" global_flags "${CMAKE_CXX_FLAGS}")
@@ -36,3 +39,43 @@
MAIN_DEPENDENCY ${source}
DEPENDS ${SOURCE_DEPS})
endmacro()
+
+# On Darwin, there are no system-wide C++ headers and the just-built clang is
+# therefore not able to compile C++ files unless they are copied/symlinked into
+# ${LLVM_BINARY_DIR}/include/c++
+# The just-built clang is used to build compiler-rt unit tests. Let's detect
+# this before we try to build the tests and print out a suggestion how to fix
+# it.
+# On other platforms, this is currently not an issue.
+macro(clang_compiler_add_cxx_check)
+ if (APPLE)
+ set(CMD
+ "echo '#include <iostream>' | ${COMPILER_RT_TEST_COMPILER} -E -x c++ - > /dev/null"
+ "if [ $? != 0 ] "
+ " then echo"
+ " echo 'Your just-built clang cannot find C++ headers, which are needed to build and run compiler-rt tests.'"
+ " echo 'You should copy or symlink your system C++ headers into ${LLVM_BINARY_DIR}/include/c++'"
+ " if [ -d $(dirname $(dirname $(xcrun -f clang)))/include/c++ ]"
+ " then echo 'e.g. with:'"
+ " echo ' cp -r' $(dirname $(dirname $(xcrun -f clang)))/include/c++ '${LLVM_BINARY_DIR}/include/'"
+ " elif [ -d $(dirname $(dirname $(xcrun -f clang)))/lib/c++ ]"
+ " then echo 'e.g. with:'"
+ " echo ' cp -r' $(dirname $(dirname $(xcrun -f clang)))/lib/c++ '${LLVM_BINARY_DIR}/include/'"
+ " fi"
+ " echo 'This can also be fixed by checking out the libcxx project from llvm.org and installing the headers'"
+ " echo 'into your build directory:'"
+ " echo ' cd ${LLVM_SOURCE_DIR}/projects && svn co http://llvm.org/svn/llvm-project/libcxx/trunk libcxx'"
+ " echo ' cd ${LLVM_BINARY_DIR} && make -C ${LLVM_SOURCE_DIR}/projects/libcxx installheaders HEADER_DIR=${LLVM_BINARY_DIR}/include'"
+ " echo"
+ " false"
+ "fi"
+ )
+ add_custom_target(CompilerRTUnitTestCheckCxx
+ COMMAND bash -c "${CMD}"
+ COMMENT "Checking that just-built clang can find C++ headers..."
+ VERBATIM)
+ if (TARGET clang)
+ ADD_DEPENDENCIES(CompilerRTUnitTestCheckCxx clang)
+ endif()
+ endif()
+endmacro()
diff --git a/cmake/Modules/CompilerRTUtils.cmake b/cmake/Modules/CompilerRTUtils.cmake
index e22e775..ae59732 100644
--- a/cmake/Modules/CompilerRTUtils.cmake
+++ b/cmake/Modules/CompilerRTUtils.cmake
@@ -2,6 +2,7 @@
# define a handy helper function for it. The compile flags setting in CMake
# has serious issues that make its syntax challenging at best.
function(set_target_compile_flags target)
+ set(argstring "")
foreach(arg ${ARGN})
set(argstring "${argstring} ${arg}")
endforeach()
@@ -9,24 +10,13 @@
endfunction()
function(set_target_link_flags target)
+ set(argstring "")
foreach(arg ${ARGN})
set(argstring "${argstring} ${arg}")
endforeach()
set_property(TARGET ${target} PROPERTY LINK_FLAGS "${argstring}")
endfunction()
-# Check if a given flag is present in a space-separated flag_string.
-# Store the result in out_var.
-function(find_flag_in_string flag_string flag out_var)
- string(REPLACE " " ";" flag_list "${flag_string}")
- list(FIND flag_list ${flag} flag_pos)
- if(NOT flag_pos EQUAL -1)
- set(${out_var} TRUE PARENT_SCOPE)
- else()
- 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)
@@ -38,7 +28,7 @@
endmacro()
# Appends value to all lists in ARGN, if the condition is true.
-macro(append_if condition value)
+macro(append_list_if condition value)
if(${condition})
foreach(list ${ARGN})
list(APPEND ${list} ${value})
@@ -56,6 +46,6 @@
endmacro()
macro(append_no_rtti_flag list)
- append_if(COMPILER_RT_HAS_FNO_RTTI_FLAG -fno-rtti ${list})
- append_if(COMPILER_RT_HAS_GR_FLAG /GR- ${list})
+ append_list_if(COMPILER_RT_HAS_FNO_RTTI_FLAG -fno-rtti ${list})
+ append_list_if(COMPILER_RT_HAS_GR_FLAG /GR- ${list})
endmacro()
diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake
index 9902b44..90ab7fb 100644
--- a/cmake/config-ix.cmake
+++ b/cmake/config-ix.cmake
@@ -32,19 +32,227 @@
check_cxx_compiler_flag(-Werror COMPILER_RT_HAS_WERROR_FLAG)
check_cxx_compiler_flag("-Werror -Wframe-larger-than=512" COMPILER_RT_HAS_WFRAME_LARGER_THAN_FLAG)
check_cxx_compiler_flag("-Werror -Wglobal-constructors" COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG)
-check_cxx_compiler_flag("-Werror -Wno-c99-extensions" COMPILER_RT_HAS_WNO_C99_EXTENSIONS_FLAG)
-check_cxx_compiler_flag("-Werror -Wno-gnu" COMPILER_RT_HAS_WNO_GNU_FLAG)
-check_cxx_compiler_flag("-Werror -Wno-non-virtual-dtor" COMPILER_RT_HAS_WNO_NON_VIRTUAL_DTOR_FLAG)
-check_cxx_compiler_flag("-Werror -Wno-variadic-macros" COMPILER_RT_HAS_WNO_VARIADIC_MACROS_FLAG)
+check_cxx_compiler_flag("-Werror -Wc99-extensions" COMPILER_RT_HAS_WC99_EXTENSIONS_FLAG)
+check_cxx_compiler_flag("-Werror -Wgnu" COMPILER_RT_HAS_WGNU_FLAG)
+check_cxx_compiler_flag("-Werror -Wnon-virtual-dtor" COMPILER_RT_HAS_WNON_VIRTUAL_DTOR_FLAG)
+check_cxx_compiler_flag("-Werror -Wvariadic-macros" COMPILER_RT_HAS_WVARIADIC_MACROS_FLAG)
check_cxx_compiler_flag(/W3 COMPILER_RT_HAS_W3_FLAG)
check_cxx_compiler_flag(/WX COMPILER_RT_HAS_WX_FLAG)
+check_cxx_compiler_flag(/wd4146 COMPILER_RT_HAS_WD4146_FLAG)
+check_cxx_compiler_flag(/wd4291 COMPILER_RT_HAS_WD4291_FLAG)
+check_cxx_compiler_flag(/wd4391 COMPILER_RT_HAS_WD4391_FLAG)
check_cxx_compiler_flag(/wd4722 COMPILER_RT_HAS_WD4722_FLAG)
+check_cxx_compiler_flag(/wd4800 COMPILER_RT_HAS_WD4800_FLAG)
# Symbols.
check_symbol_exists(__func__ "" COMPILER_RT_HAS_FUNC_SYMBOL)
# Libraries.
-check_library_exists(m pow "" COMPILER_RT_HAS_LIBM)
+check_library_exists(c printf "" COMPILER_RT_HAS_LIBC)
check_library_exists(dl dlopen "" COMPILER_RT_HAS_LIBDL)
+check_library_exists(m pow "" COMPILER_RT_HAS_LIBM)
check_library_exists(pthread pthread_create "" COMPILER_RT_HAS_LIBPTHREAD)
+check_library_exists(stdc++ __cxa_throw "" COMPILER_RT_HAS_LIBSTDCXX)
+
+# Architectures.
+
+# List of all architectures we can target.
+set(COMPILER_RT_SUPPORTED_ARCH)
+
+# Try to compile a very simple source file to ensure we can target the given
+# platform. We use the results of these tests to build only the various target
+# runtime libraries supported by our current compilers cross-compiling
+# abilities.
+set(SIMPLE_SOURCE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple.cc)
+file(WRITE ${SIMPLE_SOURCE} "#include <stdlib.h>\n#include <limits>\nint main() {}\n")
+
+# test_target_arch(<arch> <target flags...>)
+# Sets the target flags for a given architecture and determines if this
+# architecture is supported by trying to build a simple file.
+macro(test_target_arch arch)
+ set(TARGET_${arch}_CFLAGS ${ARGN})
+ set(argstring "${CMAKE_EXE_LINKER_FLAGS}")
+ foreach(arg ${ARGN})
+ set(argstring "${argstring} ${arg}")
+ endforeach()
+ try_compile(CAN_TARGET_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE}
+ COMPILE_DEFINITIONS "${TARGET_${arch}_CFLAGS}"
+ OUTPUT_VARIABLE TARGET_${arch}_OUTPUT
+ CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${argstring}")
+ if(${CAN_TARGET_${arch}})
+ list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
+ elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "${arch}")
+ # Bail out if we cannot target the architecture we plan to test.
+ message(FATAL_ERROR "Cannot compile for ${arch}:\n${TARGET_${arch}_OUTPUT}")
+ endif()
+endmacro()
+
+# Add $arch as supported with no additional flags.
+macro(add_default_target_arch arch)
+ set(TARGET_${arch}_CFLAGS "")
+ set(CAN_TARGET_${arch} 1)
+ list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
+endmacro()
+
+macro(detect_target_arch)
+ check_symbol_exists(__arm__ "" __ARM)
+ check_symbol_exists(__aarch64__ "" __AARCH64)
+ check_symbol_exists(__x86_64__ "" __X86_64)
+ check_symbol_exists(__i686__ "" __I686)
+ check_symbol_exists(__i386__ "" __I386)
+ check_symbol_exists(__mips__ "" __MIPS)
+ check_symbol_exists(__mips64__ "" __MIPS64)
+ if(__ARM)
+ add_default_target_arch(arm)
+ elseif(__AARCH64)
+ add_default_target_arch(aarch64)
+ elseif(__X86_64)
+ add_default_target_arch(x86_64)
+ elseif(__I686)
+ add_default_target_arch(i686)
+ elseif(__I386)
+ add_default_target_arch(i386)
+ elseif(__MIPS64) # must be checked before __MIPS
+ add_default_target_arch(mips64)
+ elseif(__MIPS)
+ add_default_target_arch(mips)
+ endif()
+endmacro()
+
+# Generate the COMPILER_RT_SUPPORTED_ARCH list.
+if(ANDROID)
+ # Can't rely on LLVM_NATIVE_ARCH in cross-compilation.
+ # Examine compiler output instead.
+ detect_target_arch()
+ set(COMPILER_RT_OS_SUFFIX "-android")
+else()
+ if("${LLVM_NATIVE_ARCH}" STREQUAL "X86")
+ 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")
+ test_target_arch(powerpc64 ${TARGET_64_BIT_CFLAGS})
+ test_target_arch(powerpc64le ${TARGET_64_BIT_CFLAGS})
+ elseif("${LLVM_NATIVE_ARCH}" STREQUAL "Mips")
+ if("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "mipsel|mips64el")
+ # regex for mipsel, mips64el
+ test_target_arch(mipsel ${TARGET_32_BIT_CFLAGS})
+ test_target_arch(mips64el ${TARGET_64_BIT_CFLAGS})
+ else()
+ test_target_arch(mips ${TARGET_32_BIT_CFLAGS})
+ test_target_arch(mips64 ${TARGET_64_BIT_CFLAGS})
+ endif()
+ elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "arm")
+ test_target_arch(arm "-march=armv7-a")
+ elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "aarch32")
+ test_target_arch(aarch32 "-march=armv8-a")
+ elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "aarch64")
+ test_target_arch(aarch64 "-march=aarch64")
+ endif()
+ set(COMPILER_RT_OS_SUFFIX "")
+endif()
+
+message(STATUS "Compiler-RT supported architectures: ${COMPILER_RT_SUPPORTED_ARCH}")
+
+# Takes ${ARGN} and puts only supported architectures in @out_var list.
+function(filter_available_targets out_var)
+ set(archs)
+ foreach(arch ${ARGN})
+ list(FIND COMPILER_RT_SUPPORTED_ARCH ${arch} ARCH_INDEX)
+ if(NOT (ARCH_INDEX EQUAL -1) AND CAN_TARGET_${arch})
+ list(APPEND archs ${arch})
+ endif()
+ endforeach()
+ set(${out_var} ${archs} PARENT_SCOPE)
+endfunction()
+
+# Arhcitectures supported by compiler-rt libraries.
+filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH
+ x86_64 i386 i686 powerpc64 powerpc64le arm aarch64 mips mips64 mipsel mips64el)
+filter_available_targets(ASAN_SUPPORTED_ARCH
+ x86_64 i386 i686 powerpc64 powerpc64le arm mips mipsel mips64 mips64el)
+filter_available_targets(DFSAN_SUPPORTED_ARCH x86_64)
+filter_available_targets(LSAN_SUPPORTED_ARCH x86_64)
+# LSan common files should be available on all architectures supported
+# by other sanitizers (even if they build into dummy object files).
+filter_available_targets(LSAN_COMMON_SUPPORTED_ARCH
+ ${SANITIZER_COMMON_SUPPORTED_ARCH})
+filter_available_targets(MSAN_SUPPORTED_ARCH x86_64 mips64 mips64el)
+filter_available_targets(PROFILE_SUPPORTED_ARCH x86_64 i386 i686 arm mips mips64
+ mipsel mips64el aarch64 powerpc64 powerpc64le)
+filter_available_targets(TSAN_SUPPORTED_ARCH x86_64)
+filter_available_targets(UBSAN_SUPPORTED_ARCH x86_64 i386 i686 arm aarch64 mips mipsel)
+
+if(ANDROID)
+ set(OS_NAME "Android")
+else()
+ set(OS_NAME "${CMAKE_SYSTEM_NAME}")
+endif()
+
+if (SANITIZER_COMMON_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND
+ (OS_NAME MATCHES "Android|Darwin|Linux|FreeBSD" OR
+ (OS_NAME MATCHES "Windows" AND MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 4)))
+ set(COMPILER_RT_HAS_SANITIZER_COMMON TRUE)
+else()
+ set(COMPILER_RT_HAS_SANITIZER_COMMON FALSE)
+endif()
+
+if (COMPILER_RT_HAS_SANITIZER_COMMON AND ASAN_SUPPORTED_ARCH)
+ set(COMPILER_RT_HAS_ASAN TRUE)
+else()
+ set(COMPILER_RT_HAS_ASAN FALSE)
+endif()
+
+# TODO: Add builtins support.
+
+if (COMPILER_RT_HAS_SANITIZER_COMMON AND DFSAN_SUPPORTED_ARCH AND
+ OS_NAME MATCHES "Linux")
+ set(COMPILER_RT_HAS_DFSAN TRUE)
+else()
+ set(COMPILER_RT_HAS_DFSAN FALSE)
+endif()
+
+if (COMPILER_RT_HAS_SANITIZER_COMMON AND LSAN_SUPPORTED_ARCH AND
+ OS_NAME MATCHES "Darwin|Linux|FreeBSD")
+ set(COMPILER_RT_HAS_LSAN TRUE)
+else()
+ set(COMPILER_RT_HAS_LSAN FALSE)
+endif()
+
+if (COMPILER_RT_HAS_SANITIZER_COMMON AND LSAN_COMMON_SUPPORTED_ARCH AND
+ OS_NAME MATCHES "Darwin|Linux|FreeBSD|Android")
+ set(COMPILER_RT_HAS_LSAN_COMMON TRUE)
+else()
+ set(COMPILER_RT_HAS_LSAN_COMMON FALSE)
+endif()
+
+if (COMPILER_RT_HAS_SANITIZER_COMMON AND MSAN_SUPPORTED_ARCH AND
+ OS_NAME MATCHES "Linux")
+ set(COMPILER_RT_HAS_MSAN TRUE)
+else()
+ set(COMPILER_RT_HAS_MSAN FALSE)
+endif()
+
+if (PROFILE_SUPPORTED_ARCH AND
+ OS_NAME MATCHES "Darwin|Linux|FreeBSD")
+ set(COMPILER_RT_HAS_PROFILE TRUE)
+else()
+ set(COMPILER_RT_HAS_PROFILE FALSE)
+endif()
+
+if (COMPILER_RT_HAS_SANITIZER_COMMON AND TSAN_SUPPORTED_ARCH AND
+ OS_NAME MATCHES "Linux|FreeBSD")
+ set(COMPILER_RT_HAS_TSAN TRUE)
+else()
+ set(COMPILER_RT_HAS_TSAN FALSE)
+endif()
+
+if (COMPILER_RT_HAS_SANITIZER_COMMON AND UBSAN_SUPPORTED_ARCH AND
+ OS_NAME MATCHES "Darwin|Linux|FreeBSD")
+ set(COMPILER_RT_HAS_UBSAN TRUE)
+else()
+ set(COMPILER_RT_HAS_UBSAN FALSE)
+endif()
+
diff --git a/include/sanitizer/asan_interface.h b/include/sanitizer/asan_interface.h
index 23fc178..4353914 100644
--- a/include/sanitizer/asan_interface.h
+++ b/include/sanitizer/asan_interface.h
@@ -55,13 +55,55 @@
// Otherwise returns 0.
int __asan_address_is_poisoned(void const volatile *addr);
- // If at least on byte in [beg, beg+size) is poisoned, return the address
+ // If at least one byte in [beg, beg+size) is poisoned, return the address
// of the first such byte. Otherwise return 0.
void *__asan_region_is_poisoned(void *beg, size_t size);
// Print the description of addr (useful when debugging in gdb).
void __asan_describe_address(void *addr);
+ // Useful for calling from a debugger to get information about an ASan error.
+ // Returns 1 if an error has been (or is being) reported, otherwise returns 0.
+ int __asan_report_present();
+
+ // Useful for calling from a debugger to get information about an ASan error.
+ // If an error has been (or is being) reported, the following functions return
+ // the pc, bp, sp, address, access type (0 = read, 1 = write), access size and
+ // bug description (e.g. "heap-use-after-free"). Otherwise they return 0.
+ void *__asan_get_report_pc();
+ void *__asan_get_report_bp();
+ void *__asan_get_report_sp();
+ void *__asan_get_report_address();
+ int __asan_get_report_access_type();
+ size_t __asan_get_report_access_size();
+ const char *__asan_get_report_description();
+
+ // Useful for calling from the debugger to get information about a pointer.
+ // Returns the category of the given pointer as a constant string.
+ // Possible return values are "global", "stack", "stack-fake", "heap",
+ // "heap-invalid", "shadow-low", "shadow-gap", "shadow-high", "unknown".
+ // If global or stack, tries to also return the variable name, address and
+ // size. If heap, tries to return the chunk address and size. 'name' should
+ // point to an allocated buffer of size 'name_size'.
+ const char *__asan_locate_address(void *addr, char *name, size_t name_size,
+ void **region_address, size_t *region_size);
+
+ // Useful for calling from the debugger to get the allocation stack trace
+ // and thread ID for a heap address. Stores up to 'size' frames into 'trace',
+ // returns the number of stored frames or 0 on error.
+ size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size,
+ int *thread_id);
+
+ // Useful for calling from the debugger to get the free stack trace
+ // and thread ID for a heap address. Stores up to 'size' frames into 'trace',
+ // returns the number of stored frames or 0 on error.
+ size_t __asan_get_free_stack(void *addr, void **trace, size_t size,
+ int *thread_id);
+
+ // Useful for calling from the debugger to get the current shadow memory
+ // mapping.
+ void __asan_get_shadow_mapping(size_t *shadow_scale, size_t *shadow_offset);
+
// This is an internal function that is called to report an error.
// However it is still a part of the interface because users may want to
// set a breakpoint on this function in a debugger.
@@ -83,46 +125,6 @@
// the program crashes before ASan report is printed.
void __asan_on_error();
- // Returns the estimated number of bytes that will be reserved by allocator
- // for request of "size" bytes. If ASan allocator can't allocate that much
- // memory, returns the maximal possible allocation size, otherwise returns
- // "size".
- /* DEPRECATED: Use __sanitizer_get_estimated_allocated_size instead. */
- size_t __asan_get_estimated_allocated_size(size_t size);
-
- // Returns 1 if p was returned by the ASan allocator and is not yet freed.
- // Otherwise returns 0.
- /* DEPRECATED: Use __sanitizer_get_ownership instead. */
- int __asan_get_ownership(const void *p);
-
- // Returns the number of bytes reserved for the pointer p.
- // Requires (get_ownership(p) == true) or (p == 0).
- /* DEPRECATED: Use __sanitizer_get_allocated_size instead. */
- size_t __asan_get_allocated_size(const void *p);
-
- // Number of bytes, allocated and not yet freed by the application.
- /* DEPRECATED: Use __sanitizer_get_current_allocated_bytes instead. */
- size_t __asan_get_current_allocated_bytes();
-
- // Number of bytes, mmaped by asan allocator to fulfill allocation requests.
- // Generally, for request of X bytes, allocator can reserve and add to free
- // lists a large number of chunks of size X to use them for future requests.
- // All these chunks count toward the heap size. Currently, allocator never
- // releases memory to OS (instead, it just puts freed chunks to free lists).
- /* DEPRECATED: Use __sanitizer_get_heap_size instead. */
- size_t __asan_get_heap_size();
-
- // Number of bytes, mmaped by asan allocator, which can be used to fulfill
- // allocation requests. When a user program frees memory chunk, it can first
- // fall into quarantine and will count toward __asan_get_free_bytes() later.
- /* DEPRECATED: Use __sanitizer_get_free_bytes instead. */
- size_t __asan_get_free_bytes();
-
- // Number of bytes in unmapped pages, that are released to OS. Currently,
- // always returns 0.
- /* DEPRECATED: Use __sanitizer_get_unmapped_bytes instead. */
- size_t __asan_get_unmapped_bytes();
-
// Prints accumulated stats to stderr. Used for debugging.
void __asan_print_accumulated_stats();
@@ -130,15 +132,6 @@
// a string containing ASan runtime options. See asan_flags.h for details.
const char* __asan_default_options();
- // Malloc hooks that may be optionally provided by user.
- // __asan_malloc_hook(ptr, size) is called immediately after
- // allocation of "size" bytes, which returned "ptr".
- // __asan_free_hook(ptr) is called immediately before
- // deallocation of "ptr".
- /* DEPRECATED: Use __sanitizer_malloc_hook / __sanitizer_free_hook instead. */
- void __asan_malloc_hook(void *ptr, size_t size);
- void __asan_free_hook(void *ptr);
-
// The following 2 functions facilitate garbage collection in presence of
// asan's fake stack.
diff --git a/include/sanitizer/common_interface_defs.h b/include/sanitizer/common_interface_defs.h
index 082a931..9cb5ad8 100644
--- a/include/sanitizer/common_interface_defs.h
+++ b/include/sanitizer/common_interface_defs.h
@@ -70,6 +70,9 @@
// descriptor. Returns -1 on failure, or if coverage dumping is disabled.
// This is intended for use by sandboxing code.
intptr_t __sanitizer_maybe_open_cov_file(const char *name);
+ // Get the number of total unique covered entities (blocks, edges, calls).
+ // This can be useful for coverage-directed in-process fuzzers.
+ uintptr_t __sanitizer_get_total_unique_coverage();
// Annotate the current state of a contiguous container, such as
// std::vector, std::string or similar.
@@ -105,7 +108,7 @@
const void *end,
const void *old_mid,
const void *new_mid);
- // Returns true if the contiguous container [beg, end) ir properly poisoned
+ // Returns true if the contiguous container [beg, end) is properly poisoned
// (e.g. with __sanitizer_annotate_contiguous_container), i.e. if
// - [beg, mid) is addressable,
// - [mid, end) is unaddressable.
diff --git a/include/sanitizer/dfsan_interface.h b/include/sanitizer/dfsan_interface.h
index bcd4ae0..79dbf2f 100644
--- a/include/sanitizer/dfsan_interface.h
+++ b/include/sanitizer/dfsan_interface.h
@@ -85,6 +85,12 @@
/// callback executes. Pass in NULL to remove any callback.
void dfsan_set_write_callback(dfsan_write_callback_t labeled_write_callback);
+/// Writes the labels currently used by the program to the given file
+/// descriptor. The lines of the output have the following format:
+///
+/// <label> <parent label 1> <parent label 2> <label description if any>
+void dfsan_dump_labels(int fd);
+
#ifdef __cplusplus
} // extern "C"
diff --git a/include/sanitizer/msan_interface.h b/include/sanitizer/msan_interface.h
index f6a62be..5be5860 100644
--- a/include/sanitizer/msan_interface.h
+++ b/include/sanitizer/msan_interface.h
@@ -93,60 +93,6 @@
Passing 0 will unset the callback. */
void __msan_set_death_callback(void (*callback)(void));
- /***********************************/
- /* 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". */
- /* DEPRECATED: Use __sanitizer_get_estimated_allocated_size instead. */
- 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. */
- /* DEPRECATED: Use __sanitizer_get_ownership instead. */
- 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). */
- /* DEPRECATED: Use __sanitizer_get_allocated_size instead. */
- size_t __msan_get_allocated_size(const volatile void *p);
-
- /* Number of bytes, allocated and not yet freed by the application. */
- /* DEPRECATED: Use __sanitizer_get_current_allocated_bytes instead. */
- 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). */
- /* DEPRECATED: Use __sanitizer_get_heap_size instead. */
- 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. */
- /* DEPRECATED: Use __sanitizer_get_free_bytes instead. */
- size_t __msan_get_free_bytes();
-
- /* Number of bytes in unmapped pages, that are released to OS. Currently,
- always returns 0. */
- /* DEPRECATED: Use __sanitizer_get_unmapped_bytes instead. */
- 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". */
- /* DEPRECATED: Use __sanitizer_malloc_hook / __sanitizer_free_hook instead. */
- void __msan_malloc_hook(const volatile void *ptr, size_t size);
- void __msan_free_hook(const volatile void *ptr);
-
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index fcde9a2..6929e68 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -3,37 +3,40 @@
include(AddCompilerRT)
include(SanitizerUtils)
-# Don't build sanitizers in the bootstrap build.
-if(NOT LLVM_USE_SANITIZER)
- # AddressSanitizer is supported on Linux, FreeBSD and Mac OS X.
- # 32-bit Windows support is experimental.
- if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux|FreeBSD")
- 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|FreeBSD" AND NOT ANDROID)
- # LSan, UBsan and profile can be built on Mac OS, FreeBSD and Linux.
- add_subdirectory(lsan)
- add_subdirectory(profile)
- add_subdirectory(ubsan)
- endif()
- if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT ANDROID)
- # ThreadSanitizer and MemorySanitizer are supported on Linux only.
- add_subdirectory(tsan)
- add_subdirectory(tsan/dd)
- add_subdirectory(msan)
- add_subdirectory(msandr)
- add_subdirectory(dfsan)
- endif()
+
+if(COMPILER_RT_HAS_SANITIZER_COMMON)
+ add_subdirectory(interception)
+ add_subdirectory(sanitizer_common)
+endif()
+
+if(COMPILER_RT_HAS_ASAN)
+ add_subdirectory(asan)
endif()
add_subdirectory(builtins)
+
+if(COMPILER_RT_HAS_DFSAN)
+ add_subdirectory(dfsan)
+endif()
+
+if(COMPILER_RT_HAS_LSAN OR COMPILER_RT_HAS_LSAN_COMMON)
+ add_subdirectory(lsan)
+endif()
+
+if(COMPILER_RT_HAS_MSAN)
+ add_subdirectory(msan)
+endif()
+
+if(COMPILER_RT_HAS_PROFILE)
+ add_subdirectory(profile)
+endif()
+
+if(COMPILER_RT_HAS_TSAN)
+ add_subdirectory(tsan)
+ add_subdirectory(tsan/dd)
+endif()
+
+if(COMPILER_RT_HAS_UBSAN)
+ add_subdirectory(ubsan)
+endif()
+
diff --git a/lib/asan/Android.mk b/lib/asan/Android.mk
index 3dd5959..322e07f 100644
--- a/lib/asan/Android.mk
+++ b/lib/asan/Android.mk
@@ -22,62 +22,66 @@
ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
asan_rtl_files := \
- asan_activation.cc \
- asan_allocator2.cc \
- asan_fake_stack.cc \
- asan_globals.cc \
- asan_interceptors.cc \
- asan_linux.cc \
- asan_mac.cc \
- asan_malloc_linux.cc \
- asan_malloc_mac.cc \
- asan_malloc_win.cc \
- asan_new_delete.cc \
- asan_poisoning.cc \
- asan_posix.cc \
- asan_report.cc \
- asan_rtl.cc \
- asan_stack.cc \
- asan_stats.cc \
- asan_thread.cc \
- asan_win.cc \
- ../interception/interception_linux.cc \
- ../lsan/lsan_common.cc \
- ../lsan/lsan_common_linux.cc \
- ../sanitizer_common/sanitizer_allocator.cc \
- ../sanitizer_common/sanitizer_common.cc \
- ../sanitizer_common/sanitizer_common_libcdep.cc \
- ../sanitizer_common/sanitizer_coverage_libcdep.cc \
- ../sanitizer_common/sanitizer_coverage_mapping_libcdep.cc \
- ../sanitizer_common/sanitizer_deadlock_detector1.cc \
- ../sanitizer_common/sanitizer_deadlock_detector2.cc \
- ../sanitizer_common/sanitizer_flags.cc \
- ../sanitizer_common/sanitizer_libc.cc \
- ../sanitizer_common/sanitizer_libignore.cc \
- ../sanitizer_common/sanitizer_linux.cc \
- ../sanitizer_common/sanitizer_linux_libcdep.cc \
- ../sanitizer_common/sanitizer_mac.cc \
- ../sanitizer_common/sanitizer_persistent_allocator.cc \
- ../sanitizer_common/sanitizer_platform_limits_linux.cc \
- ../sanitizer_common/sanitizer_platform_limits_posix.cc \
- ../sanitizer_common/sanitizer_posix.cc \
- ../sanitizer_common/sanitizer_posix_libcdep.cc \
- ../sanitizer_common/sanitizer_printf.cc \
- ../sanitizer_common/sanitizer_procmaps_linux.cc \
- ../sanitizer_common/sanitizer_procmaps_mac.cc \
- ../sanitizer_common/sanitizer_stackdepot.cc \
- ../sanitizer_common/sanitizer_stacktrace.cc \
- ../sanitizer_common/sanitizer_stacktrace_libcdep.cc \
- ../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc \
- ../sanitizer_common/sanitizer_suppressions.cc \
- ../sanitizer_common/sanitizer_symbolizer.cc \
- ../sanitizer_common/sanitizer_symbolizer_libbacktrace.cc \
- ../sanitizer_common/sanitizer_symbolizer_libcdep.cc \
- ../sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc \
- ../sanitizer_common/sanitizer_symbolizer_win.cc \
- ../sanitizer_common/sanitizer_thread_registry.cc \
- ../sanitizer_common/sanitizer_tls_get_addr.cc \
- ../sanitizer_common/sanitizer_win.cc
+ asan_activation.cc \
+ asan_allocator2.cc \
+ asan_fake_stack.cc \
+ asan_globals.cc \
+ asan_interceptors.cc \
+ asan_linux.cc \
+ asan_mac.cc \
+ asan_malloc_linux.cc \
+ asan_malloc_mac.cc \
+ asan_malloc_win.cc \
+ asan_new_delete.cc \
+ asan_poisoning.cc \
+ asan_posix.cc \
+ asan_report.cc \
+ asan_rtl.cc \
+ asan_stack.cc \
+ asan_stats.cc \
+ asan_thread.cc \
+ asan_win.cc \
+ ../interception/interception_linux.cc \
+ ../lsan/lsan_common.cc \
+ ../lsan/lsan_common_linux.cc \
+ ../sanitizer_common/sanitizer_allocator.cc \
+ ../sanitizer_common/sanitizer_common.cc \
+ ../sanitizer_common/sanitizer_common_libcdep.cc \
+ ../sanitizer_common/sanitizer_coverage_libcdep.cc \
+ ../sanitizer_common/sanitizer_coverage_mapping_libcdep.cc \
+ ../sanitizer_common/sanitizer_deadlock_detector1.cc \
+ ../sanitizer_common/sanitizer_deadlock_detector2.cc \
+ ../sanitizer_common/sanitizer_flags.cc \
+ ../sanitizer_common/sanitizer_libc.cc \
+ ../sanitizer_common/sanitizer_libignore.cc \
+ ../sanitizer_common/sanitizer_linux.cc \
+ ../sanitizer_common/sanitizer_linux_libcdep.cc \
+ ../sanitizer_common/sanitizer_mac.cc \
+ ../sanitizer_common/sanitizer_persistent_allocator.cc \
+ ../sanitizer_common/sanitizer_platform_limits_linux.cc \
+ ../sanitizer_common/sanitizer_platform_limits_posix.cc \
+ ../sanitizer_common/sanitizer_posix.cc \
+ ../sanitizer_common/sanitizer_posix_libcdep.cc \
+ ../sanitizer_common/sanitizer_printf.cc \
+ ../sanitizer_common/sanitizer_procmaps_common.cc \
+ ../sanitizer_common/sanitizer_procmaps_freebsd.cc \
+ ../sanitizer_common/sanitizer_procmaps_linux.cc \
+ ../sanitizer_common/sanitizer_procmaps_mac.cc \
+ ../sanitizer_common/sanitizer_stackdepot.cc \
+ ../sanitizer_common/sanitizer_stacktrace.cc \
+ ../sanitizer_common/sanitizer_stacktrace_libcdep.cc \
+ ../sanitizer_common/sanitizer_stacktrace_printer.cc \
+ ../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc \
+ ../sanitizer_common/sanitizer_suppressions.cc \
+ ../sanitizer_common/sanitizer_symbolizer.cc \
+ ../sanitizer_common/sanitizer_symbolizer_libbacktrace.cc \
+ ../sanitizer_common/sanitizer_symbolizer_libcdep.cc \
+ ../sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc \
+ ../sanitizer_common/sanitizer_symbolizer_win.cc \
+ ../sanitizer_common/sanitizer_thread_registry.cc \
+ ../sanitizer_common/sanitizer_tls_get_addr.cc \
+ ../sanitizer_common/sanitizer_unwind_posix_libcdep.cc \
+ ../sanitizer_common/sanitizer_win.cc \
asan_rtl_cflags := \
-fvisibility=hidden \
@@ -90,6 +94,7 @@
-Wno-non-virtual-dtor \
-Wno-sign-compare \
-Wno-unused-parameter \
+ -std=c++11
asan_test_files := \
tests/asan_globals_test.cc \
@@ -181,7 +186,8 @@
-DASAN_UAR=0 \
-DASAN_HAS_BLACKLIST=1 \
-DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \
- -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV)
+ -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \
+ -std=c++11
LOCAL_SRC_FILES := tests/asan_noinst_test.cc tests/asan_test_main.cc
LOCAL_CPP_EXTENSION := .cc
LOCAL_CLANG := true
@@ -261,7 +267,8 @@
-DASAN_UAR=0 \
-DASAN_HAS_BLACKLIST=1 \
-DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \
- -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV)
+ -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \
+ -std=c++11
LOCAL_SRC_FILES := tests/asan_noinst_test.cc tests/asan_test_main.cc
LOCAL_CPP_EXTENSION := .cc
LOCAL_CLANG := true
diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt
index b23a7a2..6251f06 100644
--- a/lib/asan/CMakeLists.txt
+++ b/lib/asan/CMakeLists.txt
@@ -1,15 +1,9 @@
# Build for the AddressSanitizer runtime support library.
-if(APPLE)
-# Don't set rpath for the ASan libraries. Developers are encouraged to ship
-# their binaries together with the corresponding ASan runtime libraries,
-# so they'll anyway need to fix the rpath and the install name.
-set(CMAKE_BUILD_WITH_INSTALL_RPATH OFF)
-endif()
-
set(ASAN_SOURCES
asan_allocator2.cc
asan_activation.cc
+ asan_debugging.cc
asan_fake_stack.cc
asan_globals.cc
asan_interceptors.cc
@@ -52,14 +46,20 @@
set(ASAN_DYNAMIC_DEFINITIONS
${ASAN_COMMON_DEFINITIONS} ASAN_DYNAMIC=1)
+append_list_if(WIN32 INTERCEPTION_DYNAMIC_CRT ASAN_DYNAMIC_DEFINITIONS)
set(ASAN_DYNAMIC_CFLAGS ${ASAN_CFLAGS})
-append_if(COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC
+append_list_if(COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC
-ftls-model=initial-exec ASAN_DYNAMIC_CFLAGS)
+append_list_if(MSVC /DEBUG ASAN_DYNAMIC_CFLAGS)
-set(ASAN_DYNAMIC_LIBS stdc++ m c)
-append_if(COMPILER_RT_HAS_LIBPTHREAD pthread ASAN_DYNAMIC_LIBS)
-append_if(COMPILER_RT_HAS_LIBDL dl ASAN_DYNAMIC_LIBS)
+append_list_if(COMPILER_RT_HAS_LIBC c ASAN_DYNAMIC_LIBS)
+append_list_if(COMPILER_RT_HAS_LIBDL dl ASAN_DYNAMIC_LIBS)
+append_list_if(COMPILER_RT_HAS_LIBM m ASAN_DYNAMIC_LIBS)
+append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread ASAN_DYNAMIC_LIBS)
+append_list_if(COMPILER_RT_HAS_LIBSTDCXX stdc++ ASAN_DYNAMIC_LIBS)
+
+append_list_if(ANDROID log ASAN_DYNAMIC_LIBS)
# Compile ASan sources into an object library.
if(APPLE)
@@ -70,11 +70,6 @@
CFLAGS ${ASAN_CFLAGS}
DEFS ${ASAN_COMMON_DEFINITIONS})
endforeach()
-elseif(ANDROID)
- add_library(RTAsan.arm.android OBJECT ${ASAN_SOURCES} ${ASAN_CXX_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}
@@ -109,21 +104,6 @@
DEFS ${ASAN_COMMON_DEFINITIONS})
add_dependencies(asan clang_rt.asan_${os}_dynamic)
endforeach()
-
-elseif(ANDROID)
- add_library(clang_rt.asan-arm-android SHARED
- $<TARGET_OBJECTS:RTAsan.arm.android>
- $<TARGET_OBJECTS:RTInterception.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
- COMPILE_DEFINITIONS ${ASAN_COMMON_DEFINITIONS})
- target_link_libraries(clang_rt.asan-arm-android dl log)
- add_dependencies(asan clang_rt.asan-arm-android)
- install(TARGETS clang_rt.asan-arm-android
- ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}
- LIBRARY DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR})
else()
# Build separate libraries for each target.
foreach(arch ${ASAN_SUPPORTED_ARCH})
@@ -131,7 +111,7 @@
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>)
- if (NOT WIN32)
+ if(NOT WIN32)
# We can't build Leak Sanitizer on Windows yet.
list(APPEND ASAN_COMMON_RUNTIME_OBJECTS
$<TARGET_OBJECTS:RTLSanCommon.${arch}>)
@@ -158,8 +138,14 @@
DEFS ${ASAN_COMMON_DEFINITIONS})
add_dependencies(asan clang_rt.asan-preinit-${arch})
+ if (WIN32)
+ set(SHARED_ASAN_NAME clang_rt.asan_dynamic-${arch}${COMPILER_RT_OS_SUFFIX})
+ else()
+ set(SHARED_ASAN_NAME clang_rt.asan-${arch}${COMPILER_RT_OS_SUFFIX})
+ endif()
+
add_compiler_rt_runtime(clang_rt.asan-dynamic-${arch} ${arch} SHARED
- OUTPUT_NAME clang_rt.asan-${arch}
+ OUTPUT_NAME ${SHARED_ASAN_NAME}
SOURCES $<TARGET_OBJECTS:RTAsan_dynamic.${arch}>
${ASAN_COMMON_RUNTIME_OBJECTS}
CFLAGS ${ASAN_DYNAMIC_CFLAGS}
@@ -168,7 +154,7 @@
add_dependencies(asan clang_rt.asan-dynamic-${arch})
endif()
- if (UNIX AND NOT ${arch} STREQUAL "i386")
+ if (UNIX AND NOT ${arch} STREQUAL "i386" AND NOT ${arch} STREQUAL "i686")
add_sanitizer_rt_symbols(clang_rt.asan_cxx-${arch})
add_dependencies(asan clang_rt.asan_cxx-${arch}-symbols)
add_sanitizer_rt_symbols(clang_rt.asan-${arch} asan.syms.extra)
@@ -177,11 +163,17 @@
if (WIN32)
add_compiler_rt_runtime(clang_rt.asan_dll_thunk-${arch} ${arch} STATIC
- SOURCES asan_dll_thunk.cc
+ SOURCES asan_win_dll_thunk.cc
$<TARGET_OBJECTS:RTInterception.${arch}>
CFLAGS ${ASAN_CFLAGS} -DASAN_DLL_THUNK
DEFS ${ASAN_COMMON_DEFINITIONS})
add_dependencies(asan clang_rt.asan_dll_thunk-${arch})
+ add_compiler_rt_runtime(clang_rt.asan_dynamic_runtime_thunk-${arch} ${arch}
+ STATIC
+ SOURCES asan_win_dynamic_runtime_thunk.cc
+ CFLAGS ${ASAN_CFLAGS} -DASAN_DYNAMIC_RUNTIME_THUNK -Zl
+ DEFS ${ASAN_COMMON_DEFINITIONS})
+ add_dependencies(asan clang_rt.asan_dynamic_runtime_thunk-${arch})
endif()
endforeach()
endif()
diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h
index 6b2324a..6d3a992 100644
--- a/lib/asan/asan_allocator.h
+++ b/lib/asan/asan_allocator.h
@@ -45,8 +45,8 @@
uptr AllocTid();
uptr FreeTid();
bool Eq(const AsanChunkView &c) const { return chunk_ == c.chunk_; }
- void GetAllocStack(StackTrace *stack);
- void GetFreeStack(StackTrace *stack);
+ StackTrace GetAllocStack();
+ StackTrace GetFreeStack();
bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) {
if (addr >= Beg() && (addr + access_size) <= End()) {
*offset = addr - Beg();
@@ -139,18 +139,20 @@
AsanThreadLocalMallocStorage() {}
};
-void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
+void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack,
AllocType alloc_type);
-void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type);
+void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type);
+void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack,
+ AllocType alloc_type);
-void *asan_malloc(uptr size, StackTrace *stack);
-void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack);
-void *asan_realloc(void *p, uptr size, StackTrace *stack);
-void *asan_valloc(uptr size, StackTrace *stack);
-void *asan_pvalloc(uptr size, StackTrace *stack);
+void *asan_malloc(uptr size, BufferedStackTrace *stack);
+void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack);
+void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack);
+void *asan_valloc(uptr size, BufferedStackTrace *stack);
+void *asan_pvalloc(uptr size, BufferedStackTrace *stack);
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
- StackTrace *stack);
+ BufferedStackTrace *stack);
uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp);
uptr asan_mz_size(const void *ptr);
diff --git a/lib/asan/asan_allocator2.cc b/lib/asan/asan_allocator2.cc
index f07b0f0..52bdcf6 100644
--- a/lib/asan/asan_allocator2.cc
+++ b/lib/asan/asan_allocator2.cc
@@ -168,23 +168,6 @@
}
return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
}
- // If we don't use stack depot, we store the alloc/free stack traces
- // in the chunk itself.
- u32 *AllocStackBeg() {
- return (u32*)(Beg() - RZLog2Size(rz_log));
- }
- uptr AllocStackSize() {
- CHECK_LE(RZLog2Size(rz_log), kChunkHeaderSize);
- return (RZLog2Size(rz_log) - kChunkHeaderSize) / sizeof(u32);
- }
- u32 *FreeStackBeg() {
- return (u32*)(Beg() + kChunkHeader2Size);
- }
- uptr FreeStackSize() {
- if (user_requested_size < kChunkHeader2Size) return 0;
- uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY);
- return (available - kChunkHeader2Size) / sizeof(u32);
- }
bool AddrIsInside(uptr addr, bool locked_version = false) {
return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version));
}
@@ -199,20 +182,19 @@
uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
-static void GetStackTraceFromId(u32 id, StackTrace *stack) {
+static StackTrace GetStackTraceFromId(u32 id) {
CHECK(id);
- uptr size = 0;
- const uptr *trace = StackDepotGet(id, &size);
- CHECK(trace);
- stack->CopyFrom(trace, size);
+ StackTrace res = StackDepotGet(id);
+ CHECK(res.trace);
+ return res;
}
-void AsanChunkView::GetAllocStack(StackTrace *stack) {
- GetStackTraceFromId(chunk_->alloc_context_id, stack);
+StackTrace AsanChunkView::GetAllocStack() {
+ return GetStackTraceFromId(chunk_->alloc_context_id);
}
-void AsanChunkView::GetFreeStack(StackTrace *stack) {
- GetStackTraceFromId(chunk_->free_context_id, stack);
+StackTrace AsanChunkView::GetFreeStack() {
+ return GetStackTraceFromId(chunk_->free_context_id);
}
struct QuarantineCallback;
@@ -280,7 +262,7 @@
quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine);
}
-static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
+static void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack,
AllocType alloc_type, bool can_fill) {
if (UNLIKELY(!asan_inited))
AsanInitFromRtl();
@@ -372,7 +354,7 @@
meta[1] = chunk_beg;
}
- m->alloc_context_id = StackDepotPut(stack->trace, stack->size);
+ m->alloc_context_id = StackDepotPut(*stack);
uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY);
// Unpoison the bulk of the memory region.
@@ -408,15 +390,16 @@
return res;
}
-static void ReportInvalidFree(void *ptr, u8 chunk_state, StackTrace *stack) {
+static void ReportInvalidFree(void *ptr, u8 chunk_state,
+ BufferedStackTrace *stack) {
if (chunk_state == CHUNK_QUARANTINE)
ReportDoubleFree((uptr)ptr, stack);
else
ReportFreeNotMalloced((uptr)ptr, stack);
}
-static void AtomicallySetQuarantineFlag(AsanChunk *m,
- void *ptr, StackTrace *stack) {
+static void AtomicallySetQuarantineFlag(AsanChunk *m, void *ptr,
+ BufferedStackTrace *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,
@@ -427,8 +410,8 @@
// Expects the chunk to already be marked as quarantined by using
// AtomicallySetQuarantineFlag.
-static void QuarantineChunk(AsanChunk *m, void *ptr,
- StackTrace *stack, AllocType alloc_type) {
+static void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack,
+ AllocType alloc_type) {
CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch)
@@ -440,7 +423,7 @@
CHECK_EQ(m->free_tid, kInvalidTid);
AsanThread *t = GetCurrentThread();
m->free_tid = t ? t->tid() : 0;
- m->free_context_id = StackDepotPut(stack->trace, stack->size);
+ m->free_context_id = StackDepotPut(*stack);
// Poison the region.
PoisonShadow(m->Beg(),
RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
@@ -464,19 +447,25 @@
}
}
-static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
+static void Deallocate(void *ptr, uptr delete_size, BufferedStackTrace *stack,
+ AllocType alloc_type) {
uptr p = reinterpret_cast<uptr>(ptr);
if (p == 0) return;
uptr chunk_beg = p - kChunkHeaderSize;
AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+ if (delete_size && flags()->new_delete_type_mismatch &&
+ delete_size != m->UsedSize()) {
+ ReportNewDeleteSizeMismatch(p, delete_size, stack);
+ }
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);
}
-static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) {
+static void *Reallocate(void *old_ptr, uptr new_size,
+ BufferedStackTrace *stack) {
CHECK(old_ptr && new_size);
uptr p = reinterpret_cast<uptr>(old_ptr);
uptr chunk_beg = p - kChunkHeaderSize;
@@ -496,7 +485,7 @@
// If realloc() races with free(), we may start copying freed memory.
// However, we will report racy double-free later anyway.
REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
- Deallocate(old_ptr, stack, FROM_MALLOC);
+ Deallocate(old_ptr, 0, stack, FROM_MALLOC);
}
return new_ptr;
}
@@ -589,20 +578,25 @@
allocator.PrintStats();
}
-void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
+void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack,
AllocType alloc_type) {
return Allocate(size, alignment, stack, alloc_type, true);
}
-void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
- Deallocate(ptr, stack, alloc_type);
+void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) {
+ Deallocate(ptr, 0, stack, alloc_type);
}
-void *asan_malloc(uptr size, StackTrace *stack) {
+void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack,
+ AllocType alloc_type) {
+ Deallocate(ptr, size, stack, alloc_type);
+}
+
+void *asan_malloc(uptr size, BufferedStackTrace *stack) {
return Allocate(size, 8, stack, FROM_MALLOC, true);
}
-void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
+void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
if (CallocShouldReturnNullDueToOverflow(size, nmemb))
return AllocatorReturnNull();
void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false);
@@ -613,21 +607,21 @@
return ptr;
}
-void *asan_realloc(void *p, uptr size, StackTrace *stack) {
+void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) {
if (p == 0)
return Allocate(size, 8, stack, FROM_MALLOC, true);
if (size == 0) {
- Deallocate(p, stack, FROM_MALLOC);
+ Deallocate(p, 0, stack, FROM_MALLOC);
return 0;
}
return Reallocate(p, size, stack);
}
-void *asan_valloc(uptr size, StackTrace *stack) {
+void *asan_valloc(uptr size, BufferedStackTrace *stack) {
return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true);
}
-void *asan_pvalloc(uptr size, StackTrace *stack) {
+void *asan_pvalloc(uptr size, BufferedStackTrace *stack) {
uptr PageSize = GetPageSizeCached();
size = RoundUpTo(size, PageSize);
if (size == 0) {
@@ -638,7 +632,7 @@
}
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
- StackTrace *stack) {
+ BufferedStackTrace *stack) {
void *ptr = Allocate(size, alignment, stack, FROM_MALLOC, true);
CHECK(IsAligned((uptr)ptr, alignment));
*memptr = ptr;
@@ -764,17 +758,11 @@
uptr __sanitizer_get_estimated_allocated_size(uptr size) {
return size;
}
-uptr __asan_get_estimated_allocated_size(uptr size) {
- return __sanitizer_get_estimated_allocated_size(size);
-}
int __sanitizer_get_ownership(const void *p) {
uptr ptr = reinterpret_cast<uptr>(p);
return (AllocationSize(ptr) > 0);
}
-int __asan_get_ownership(const void *p) {
- return __sanitizer_get_ownership(p);
-}
uptr __sanitizer_get_allocated_size(const void *p) {
if (p == 0) return 0;
@@ -787,23 +775,11 @@
}
return allocated_size;
}
-uptr __asan_get_allocated_size(const void *p) {
- return __sanitizer_get_allocated_size(p);
-}
#if !SANITIZER_SUPPORTS_WEAK_HOOKS
// Provide default (no-op) implementation of malloc hooks.
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-void __asan_malloc_hook(void *ptr, uptr size) {
- (void)ptr;
- (void)size;
-}
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-void __asan_free_hook(void *ptr) {
- (void)ptr;
-}
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
void __sanitizer_malloc_hook(void *ptr, uptr size) {
(void)ptr;
(void)size;
diff --git a/lib/asan/asan_debugging.cc b/lib/asan/asan_debugging.cc
new file mode 100644
index 0000000..2b66dd5
--- /dev/null
+++ b/lib/asan/asan_debugging.cc
@@ -0,0 +1,141 @@
+//===-- asan_debugging.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 contains various functions that are generally useful to call when
+// using a debugger (LLDB, GDB).
+//===----------------------------------------------------------------------===//
+
+#include "asan_allocator.h"
+#include "asan_flags.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_thread.h"
+
+namespace __asan {
+
+void GetInfoForStackVar(uptr addr, AddressDescription *descr, AsanThread *t) {
+ descr->name[0] = 0;
+ descr->region_address = 0;
+ descr->region_size = 0;
+ descr->region_kind = "stack";
+
+ AsanThread::StackFrameAccess access;
+ if (!t->GetStackFrameAccessByAddr(addr, &access))
+ return;
+ InternalMmapVector<StackVarDescr> vars(16);
+ if (!ParseFrameDescription(access.frame_descr, &vars)) {
+ return;
+ }
+
+ for (uptr i = 0; i < vars.size(); i++) {
+ if (access.offset <= vars[i].beg + vars[i].size) {
+ internal_strncat(descr->name, vars[i].name_pos,
+ Min(descr->name_size, vars[i].name_len));
+ descr->region_address = addr - (access.offset - vars[i].beg);
+ descr->region_size = vars[i].size;
+ return;
+ }
+ }
+}
+
+void GetInfoForHeapAddress(uptr addr, AddressDescription *descr) {
+ AsanChunkView chunk = FindHeapChunkByAddress(addr);
+
+ descr->name[0] = 0;
+ descr->region_address = 0;
+ descr->region_size = 0;
+
+ if (!chunk.IsValid()) {
+ descr->region_kind = "heap-invalid";
+ return;
+ }
+
+ descr->region_address = chunk.Beg();
+ descr->region_size = chunk.UsedSize();
+ descr->region_kind = "heap";
+}
+
+void AsanLocateAddress(uptr addr, AddressDescription *descr) {
+ if (DescribeAddressIfShadow(addr, descr, /* print */ false)) {
+ return;
+ }
+ if (GetInfoForAddressIfGlobal(addr, descr)) {
+ return;
+ }
+ asanThreadRegistry().Lock();
+ AsanThread *thread = FindThreadByStackAddress(addr);
+ asanThreadRegistry().Unlock();
+ if (thread) {
+ GetInfoForStackVar(addr, descr, thread);
+ return;
+ }
+ GetInfoForHeapAddress(addr, descr);
+}
+
+uptr AsanGetStack(uptr addr, uptr *trace, uptr size, u32 *thread_id,
+ bool alloc_stack) {
+ AsanChunkView chunk = FindHeapChunkByAddress(addr);
+ if (!chunk.IsValid()) return 0;
+
+ StackTrace stack(nullptr, 0);
+ if (alloc_stack) {
+ if (chunk.AllocTid() == kInvalidTid) return 0;
+ stack = chunk.GetAllocStack();
+ if (thread_id) *thread_id = chunk.AllocTid();
+ } else {
+ if (chunk.FreeTid() == kInvalidTid) return 0;
+ stack = chunk.GetFreeStack();
+ if (thread_id) *thread_id = chunk.FreeTid();
+ }
+
+ if (trace && size) {
+ size = Min(size, Min(stack.size, kStackTraceMax));
+ for (uptr i = 0; i < size; i++)
+ trace[i] = StackTrace::GetPreviousInstructionPc(stack.trace[i]);
+
+ return size;
+ }
+
+ return 0;
+}
+
+} // namespace __asan
+
+using namespace __asan;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+const char *__asan_locate_address(uptr addr, char *name, uptr name_size,
+ uptr *region_address, uptr *region_size) {
+ AddressDescription descr = { name, name_size, 0, 0, 0 };
+ AsanLocateAddress(addr, &descr);
+ if (region_address) *region_address = descr.region_address;
+ if (region_size) *region_size = descr.region_size;
+ return descr.region_kind;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
+ return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ true);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
+ return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ false);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset) {
+ if (shadow_scale)
+ *shadow_scale = SHADOW_SCALE;
+ if (shadow_offset)
+ *shadow_offset = SHADOW_OFFSET;
+}
diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h
index 1139204..3df4dd3 100644
--- a/lib/asan/asan_flags.h
+++ b/lib/asan/asan_flags.h
@@ -52,18 +52,20 @@
bool print_stats;
bool print_legend;
bool atexit;
- bool disable_core;
bool allow_reexec;
bool print_full_thread_history;
bool poison_heap;
bool poison_partial;
+ bool poison_array_cookie;
bool alloc_dealloc_mismatch;
+ bool new_delete_type_mismatch;
bool strict_memcmp;
bool strict_init_order;
bool start_deactivated;
int detect_invalid_pointer_pairs;
bool detect_container_overflow;
int detect_odr_violation;
+ bool dump_instruction_bytes;
};
extern Flags asan_flags_dont_use_directly;
diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc
index a844201..be111d4 100644
--- a/lib/asan/asan_globals.cc
+++ b/lib/asan/asan_globals.cc
@@ -71,25 +71,64 @@
}
}
-static void ReportGlobal(const Global &g, const char *prefix) {
- Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
- prefix, &g, (void*)g.beg, g.size, g.size_with_redzone, g.name,
- g.module_name, g.has_dynamic_init);
+const uptr kMinimalDistanceFromAnotherGlobal = 64;
+
+bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
+ if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
+ if (addr >= g.beg + g.size_with_redzone) return false;
+ return true;
}
-bool DescribeAddressIfGlobal(uptr addr, uptr size) {
+static void ReportGlobal(const Global &g, const char *prefix) {
+ Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
+ prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
+ g.module_name, g.has_dynamic_init);
+ if (g.location) {
+ Report(" location (%p): name=%s[%p], %d %d\n", g.location,
+ g.location->filename, g.location->filename, g.location->line_no,
+ g.location->column_no);
+ }
+}
+
+static bool DescribeOrGetInfoIfGlobal(uptr addr, uptr size, bool print,
+ Global *output_global) {
if (!flags()->report_globals) return false;
BlockingMutexLock lock(&mu_for_globals);
bool res = false;
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
const Global &g = *l->g;
- if (flags()->report_globals >= 2)
- ReportGlobal(g, "Search");
- res |= DescribeAddressRelativeToGlobal(addr, size, g);
+ if (print) {
+ if (flags()->report_globals >= 2)
+ ReportGlobal(g, "Search");
+ res |= DescribeAddressRelativeToGlobal(addr, size, g);
+ } else {
+ if (IsAddressNearGlobal(addr, g)) {
+ CHECK(output_global);
+ *output_global = g;
+ return true;
+ }
+ }
}
return res;
}
+bool DescribeAddressIfGlobal(uptr addr, uptr size) {
+ return DescribeOrGetInfoIfGlobal(addr, size, /* print */ true,
+ /* output_global */ nullptr);
+}
+
+bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
+ Global g = {};
+ if (DescribeOrGetInfoIfGlobal(addr, /* size */ 1, /* print */ false, &g)) {
+ internal_strncpy(descr->name, g.name, descr->name_size);
+ descr->region_address = g.beg;
+ descr->region_size = g.size;
+ descr->region_kind = "global";
+ return true;
+ }
+ return false;
+}
+
u32 FindRegistrationSite(const Global *g) {
CHECK(global_registration_site_vector);
for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) {
@@ -178,7 +217,7 @@
void __asan_register_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return;
GET_STACK_TRACE_FATAL_HERE;
- u32 stack_id = StackDepotPut(stack.trace, stack.size);
+ u32 stack_id = StackDepotPut(stack);
BlockingMutexLock lock(&mu_for_globals);
if (!global_registration_site_vector)
global_registration_site_vector =
diff --git a/lib/asan/asan_init_version.h b/lib/asan/asan_init_version.h
index 88eb80f..77aea81 100644
--- a/lib/asan/asan_init_version.h
+++ b/lib/asan/asan_init_version.h
@@ -15,21 +15,16 @@
#ifndef ASAN_INIT_VERSION_H
#define ASAN_INIT_VERSION_H
-#include "sanitizer_common/sanitizer_internal_defs.h"
-
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.
- // Every time the ASan ABI changes we also change the version number in this
- // name. Objects build with incompatible ASan ABI version
- // will not link with run-time.
+ // Every time the ASan ABI changes we also change the version number in the
+ // __asan_init function name. Objects built with incompatible ASan ABI
+ // versions will not link with run-time.
// Changes between ABI versions:
// v1=>v2: added 'module_name' to __asan_global
// v2=>v3: stack frame description (created by the compiler)
// contains the function PC as the 3-rd field (see
// DescribeAddressIfStack).
// v3=>v4: added '__asan_global_source_location' to __asan_global.
- SANITIZER_INTERFACE_ATTRIBUTE void __asan_init_v4();
#define __asan_init __asan_init_v4
#define __asan_init_name "__asan_init_v4"
}
diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc
index 4ae03ec..deac034 100644
--- a/lib/asan/asan_interceptors.cc
+++ b/lib/asan/asan_interceptors.cc
@@ -148,6 +148,7 @@
#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res) CovUpdateMapping()
#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CovUpdateMapping()
+#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited)
#include "sanitizer_common/sanitizer_common_interceptors.inc"
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s)
@@ -221,8 +222,8 @@
namespace __sanitizer {
int real_sigaction(int signum, const void *act, void *oldact) {
- return REAL(sigaction)(signum,
- (struct sigaction *)act, (struct sigaction *)oldact);
+ return REAL(sigaction)(signum, (const struct sigaction *)act,
+ (struct sigaction *)oldact);
}
} // namespace __sanitizer
@@ -295,37 +296,27 @@
}
#endif
-#if ASAN_INTERCEPT_MLOCKX
-// intercept mlock and friends.
-// Since asan maps 16T of RAM, mlock is completely unfriendly to asan.
-// All functions return 0 (success).
-static void MlockIsUnsupported() {
- static bool printed = false;
- if (printed) return;
- printed = true;
- VPrintf(1,
- "INFO: AddressSanitizer ignores "
- "mlock/mlockall/munlock/munlockall\n");
+#if SANITIZER_WINDOWS
+INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
+ CHECK(REAL(RaiseException));
+ __asan_handle_no_return();
+ REAL(RaiseException)(a, b, c, d);
}
-INTERCEPTOR(int, mlock, const void *addr, uptr len) {
- MlockIsUnsupported();
- return 0;
+INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
+ CHECK(REAL(_except_handler3));
+ __asan_handle_no_return();
+ return REAL(_except_handler3)(a, b, c, d);
}
-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;
+#if ASAN_DYNAMIC
+// This handler is named differently in -MT and -MD CRTs.
+#define _except_handler4 _except_handler4_common
+#endif
+INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
+ CHECK(REAL(_except_handler4));
+ __asan_handle_no_return();
+ return REAL(_except_handler4)(a, b, c, d);
}
#endif
@@ -529,7 +520,7 @@
}
#endif
-INTERCEPTOR(uptr, strlen, const char *s) {
+INTERCEPTOR(SIZE_T, strlen, const char *s) {
if (UNLIKELY(!asan_inited)) return internal_strlen(s);
// strlen is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac.
@@ -537,15 +528,15 @@
return REAL(strlen)(s);
}
ENSURE_ASAN_INITED();
- uptr length = REAL(strlen)(s);
+ SIZE_T length = REAL(strlen)(s);
if (flags()->replace_str) {
ASAN_READ_RANGE(s, length + 1);
}
return length;
}
-INTERCEPTOR(uptr, wcslen, const wchar_t *s) {
- uptr length = REAL(wcslen)(s);
+INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) {
+ SIZE_T length = REAL(wcslen)(s);
if (!asan_init_is_running) {
ENSURE_ASAN_INITED();
ASAN_READ_RANGE(s, (length + 1) * sizeof(wchar_t));
@@ -587,7 +578,7 @@
// We get this symbol by skipping leading blanks and optional +/- sign.
while (IsSpace(*nptr)) nptr++;
if (*nptr == '+' || *nptr == '-') nptr++;
- *endptr = (char*)nptr;
+ *endptr = const_cast<char *>(nptr);
}
CHECK(*endptr >= nptr);
}
@@ -728,6 +719,9 @@
namespace __asan {
void InitializeWindowsInterceptors() {
ASAN_INTERCEPT_FUNC(CreateThread);
+ ASAN_INTERCEPT_FUNC(RaiseException);
+ ASAN_INTERCEPT_FUNC(_except_handler3);
+ ASAN_INTERCEPT_FUNC(_except_handler4);
}
} // namespace __asan
@@ -775,14 +769,6 @@
ASAN_INTERCEPT_FUNC(strtoll);
#endif
-#if ASAN_INTERCEPT_MLOCKX
- // Intercept mlock/munlock.
- ASAN_INTERCEPT_FUNC(mlock);
- ASAN_INTERCEPT_FUNC(munlock);
- ASAN_INTERCEPT_FUNC(mlockall);
- ASAN_INTERCEPT_FUNC(munlockall);
-#endif
-
// Intecept signal- and jump-related functions.
ASAN_INTERCEPT_FUNC(longjmp);
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
@@ -805,7 +791,7 @@
// Intercept exception handling functions.
#if ASAN_INTERCEPT___CXA_THROW
- INTERCEPT_FUNCTION(__cxa_throw);
+ ASAN_INTERCEPT_FUNC(__cxa_throw);
#endif
// Intercept threading-related functions
diff --git a/lib/asan/asan_interceptors.h b/lib/asan/asan_interceptors.h
index c5d1af0..ee3b82a 100644
--- a/lib/asan/asan_interceptors.h
+++ b/lib/asan/asan_interceptors.h
@@ -15,7 +15,7 @@
#define ASAN_INTERCEPTORS_H
#include "asan_internal.h"
-#include "sanitizer_common/sanitizer_interception.h"
+#include "interception/interception.h"
#include "sanitizer_common/sanitizer_platform_interceptors.h"
// Use macro to describe if specific function should be
@@ -26,7 +26,6 @@
# define ASAN_INTERCEPT_STRDUP 1
# define ASAN_INTERCEPT_INDEX 1
# define ASAN_INTERCEPT_PTHREAD_CREATE 1
-# define ASAN_INTERCEPT_MLOCKX 1
# define ASAN_INTERCEPT_FORK 1
#else
# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0
@@ -34,7 +33,6 @@
# define ASAN_INTERCEPT_STRDUP 0
# define ASAN_INTERCEPT_INDEX 0
# define ASAN_INTERCEPT_PTHREAD_CREATE 0
-# define ASAN_INTERCEPT_MLOCKX 0
# define ASAN_INTERCEPT_FORK 0
#endif
@@ -86,7 +84,7 @@
DECLARE_REAL(void*, memcpy, void *to, const void *from, uptr size)
DECLARE_REAL(void*, memset, void *block, int c, uptr size)
DECLARE_REAL(char*, strchr, const char *str, int c)
-DECLARE_REAL(uptr, strlen, const char *s)
+DECLARE_REAL(SIZE_T, strlen, const char *s)
DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size)
DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen)
DECLARE_REAL(char*, strstr, const char *s1, const char *s2)
diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h
index 32629ea..edaf44d 100644
--- a/lib/asan/asan_interface_internal.h
+++ b/lib/asan/asan_interface_internal.h
@@ -22,6 +22,12 @@
using __sanitizer::uptr;
extern "C" {
+ // This function should be called at the very beginning of the process,
+ // before any instrumented code is executed and before any call to malloc.
+ // Please note that __asan_init is a macro that is replaced with
+ // __asan_init_vXXX at compile-time.
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_init();
+
// This structure is used to describe the source location of a place where
// global was defined.
struct __asan_global_source_location {
@@ -85,6 +91,39 @@
void __asan_describe_address(uptr addr);
SANITIZER_INTERFACE_ATTRIBUTE
+ int __asan_report_present();
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_report_pc();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_report_bp();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_report_sp();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_report_address();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ int __asan_get_report_access_type();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_report_access_size();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ const char * __asan_get_report_description();
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ const char * __asan_locate_address(uptr addr, char *name, uptr name_size,
+ uptr *region_address, uptr *region_size);
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size,
+ u32 *thread_id);
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size,
+ u32 *thread_id);
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset);
+
+ SANITIZER_INTERFACE_ATTRIBUTE
void __asan_report_error(uptr pc, uptr bp, uptr sp,
uptr addr, int is_write, uptr access_size);
@@ -98,22 +137,6 @@
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
/* OPTIONAL */ void __asan_on_error();
- // ---------------------------
- // FIXME: Replace these functions with __sanitizer equivalent.
- SANITIZER_INTERFACE_ATTRIBUTE
- uptr __asan_get_estimated_allocated_size(uptr size);
- SANITIZER_INTERFACE_ATTRIBUTE int __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 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);
- // ---------------------------
-
SANITIZER_INTERFACE_ATTRIBUTE void __asan_print_accumulated_stats();
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
@@ -145,6 +168,15 @@
void* __asan_memset(void *s, int c, uptr n);
SANITIZER_INTERFACE_ATTRIBUTE
void* __asan_memmove(void* dest, const void* src, uptr n);
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_poison_cxx_array_cookie(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_load_cxx_array_cookie(uptr *p);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_poison_intra_object_redzone(uptr p, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_unpoison_intra_object_redzone(uptr p, uptr size);
} // extern "C"
#endif // ASAN_INTERFACE_INTERNAL_H
diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h
index 0782789..f9f9243 100644
--- a/lib/asan/asan_internal.h
+++ b/lib/asan/asan_internal.h
@@ -45,10 +45,6 @@
# endif
#endif
-#ifndef ASAN_USE_PREINIT_ARRAY
-# define ASAN_USE_PREINIT_ARRAY (SANITIZER_LINUX && !SANITIZER_ANDROID)
-#endif
-
#ifndef ASAN_DYNAMIC
# ifdef PIC
# define ASAN_DYNAMIC 1
@@ -112,10 +108,8 @@
// Add convenient macro for interface functions that may be represented as
// weak hooks.
#define ASAN_MALLOC_HOOK(ptr, size) \
- if (&__asan_malloc_hook) __asan_malloc_hook(ptr, size); \
if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size)
#define ASAN_FREE_HOOK(ptr) \
- if (&__asan_free_hook) __asan_free_hook(ptr); \
if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr)
#define ASAN_ON_ERROR() \
if (&__asan_on_error) __asan_on_error()
@@ -140,6 +134,8 @@
const int kAsanStackUseAfterScopeMagic = 0xf8;
const int kAsanGlobalRedzoneMagic = 0xf9;
const int kAsanInternalHeapMagic = 0xfe;
+const int kAsanArrayCookieMagic = 0xac;
+const int kAsanIntraObjectRedzone = 0xbb;
static const uptr kCurrentStackFrameMagic = 0x41B58AB3;
static const uptr kRetiredStackFrameMagic = 0x45E0360E;
diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc
index ed7d9ce..4014357 100644
--- a/lib/asan/asan_mac.cc
+++ b/lib/asan/asan_mac.cc
@@ -114,7 +114,7 @@
internal_strlen(dyld_insert_libraries) : 0;
uptr fname_len = internal_strlen(info.dli_fname);
if (!dyld_insert_libraries ||
- !REAL(strstr)(dyld_insert_libraries, info.dli_fname)) {
+ !REAL(strstr)(dyld_insert_libraries, StripModuleName(info.dli_fname))) {
// DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
// library.
char program_name[1024];
@@ -297,7 +297,7 @@
// The caller retains control of the allocated context.
extern "C"
asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
- StackTrace *stack) {
+ BufferedStackTrace *stack) {
asan_block_context_t *asan_ctxt =
(asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
asan_ctxt->block = ctxt;
diff --git a/lib/asan/asan_malloc_linux.cc b/lib/asan/asan_malloc_linux.cc
index 077a50c..46a6a9d 100644
--- a/lib/asan/asan_malloc_linux.cc
+++ b/lib/asan/asan_malloc_linux.cc
@@ -23,45 +23,6 @@
#include "asan_internal.h"
#include "asan_stack.h"
-#if SANITIZER_ANDROID
-DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size)
-DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
-DECLARE_REAL_AND_INTERCEPTOR(void*, calloc, uptr nmemb, uptr size)
-DECLARE_REAL_AND_INTERCEPTOR(void*, realloc, void *ptr, uptr size)
-DECLARE_REAL_AND_INTERCEPTOR(void*, memalign, uptr boundary, uptr size)
-DECLARE_REAL_AND_INTERCEPTOR(uptr, malloc_usable_size, void *mem)
-
-struct MallocDebug {
- void *(*malloc)(uptr bytes);
- void (*free)(void *mem);
- void *(*calloc)(uptr n_elements, uptr elem_size);
- void *(*realloc)(void *oldMem, uptr bytes);
- void *(*memalign)(uptr alignment, uptr bytes);
- uptr (*malloc_usable_size)(void *mem);
-};
-
-ALIGNED(32) const MallocDebug asan_malloc_dispatch = {
- WRAP(malloc), WRAP(free), WRAP(calloc),
- WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)};
-
-namespace __asan {
-void ReplaceSystemMalloc() {
- const MallocDebug** __libc_malloc_dispatch_p;
- __libc_malloc_dispatch_p =
- (const MallocDebug **)AsanDlSymNext("__libc_malloc_dispatch");
- if (__libc_malloc_dispatch_p)
- *__libc_malloc_dispatch_p = &asan_malloc_dispatch;
-}
-} // namespace __asan
-
-#else // SANITIZER_ANDROID
-
-namespace __asan {
-void ReplaceSystemMalloc() {
-}
-} // namespace __asan
-#endif // SANITIZER_ANDROID
-
// ---------------------- Replacement functions ---------------- {{{1
using namespace __asan; // NOLINT
@@ -162,4 +123,64 @@
__asan_print_accumulated_stats();
}
+#if SANITIZER_ANDROID
+// Format of __libc_malloc_dispatch has changed in Android L.
+// While we are moving towards a solution that does not depend on bionic
+// internals, here is something to support both K* and L releases.
+struct MallocDebugK {
+ void *(*malloc)(uptr bytes);
+ void (*free)(void *mem);
+ void *(*calloc)(uptr n_elements, uptr elem_size);
+ void *(*realloc)(void *oldMem, uptr bytes);
+ void *(*memalign)(uptr alignment, uptr bytes);
+ uptr (*malloc_usable_size)(void *mem);
+};
+
+struct MallocDebugL {
+ void *(*calloc)(uptr n_elements, uptr elem_size);
+ void (*free)(void *mem);
+ fake_mallinfo (*mallinfo)(void);
+ void *(*malloc)(uptr bytes);
+ uptr (*malloc_usable_size)(void *mem);
+ void *(*memalign)(uptr alignment, uptr bytes);
+ int (*posix_memalign)(void **memptr, uptr alignment, uptr size);
+ void* (*pvalloc)(uptr size);
+ void *(*realloc)(void *oldMem, uptr bytes);
+ void* (*valloc)(uptr size);
+};
+
+ALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = {
+ WRAP(malloc), WRAP(free), WRAP(calloc),
+ WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)};
+
+ALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = {
+ WRAP(calloc), WRAP(free), WRAP(mallinfo),
+ WRAP(malloc), WRAP(malloc_usable_size), WRAP(memalign),
+ WRAP(posix_memalign), WRAP(pvalloc), WRAP(realloc),
+ WRAP(valloc)};
+
+namespace __asan {
+void ReplaceSystemMalloc() {
+ void **__libc_malloc_dispatch_p =
+ (void **)AsanDlSymNext("__libc_malloc_dispatch");
+ if (__libc_malloc_dispatch_p) {
+ // Decide on K vs L dispatch format by the presence of
+ // __libc_malloc_default_dispatch export in libc.
+ void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch");
+ if (default_dispatch_p)
+ *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k;
+ else
+ *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l;
+ }
+}
+} // namespace __asan
+
+#else // SANITIZER_ANDROID
+
+namespace __asan {
+void ReplaceSystemMalloc() {
+}
+} // namespace __asan
+#endif // SANITIZER_ANDROID
+
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc
index b6d20d8..c99e312 100644
--- a/lib/asan/asan_malloc_win.cc
+++ b/lib/asan/asan_malloc_win.cc
@@ -19,75 +19,81 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stack.h"
-#include "sanitizer_common/sanitizer_interception.h"
+#include "interception/interception.h"
#include <stddef.h>
-// ---------------------- Replacement functions ---------------- {{{1
using namespace __asan; // NOLINT
-// FIXME: Simply defining functions with the same signature in *.obj
-// files overrides the standard functions in *.lib
-// This works well for simple helloworld-like tests but might need to be
-// revisited in the future.
+// MT: Simply defining functions with the same signature in *.obj
+// files overrides the standard functions in the CRT.
+// MD: Memory allocation functions are defined in the CRT .dll,
+// so we have to intercept them before they are called for the first time.
+
+#if ASAN_DYNAMIC
+# define ALLOCATION_FUNCTION_ATTRIBUTE
+#else
+# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+#endif
extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_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) {
+ALLOCATION_FUNCTION_ATTRIBUTE
+void _free_dbg(void *ptr, int) {
free(ptr);
}
+ALLOCATION_FUNCTION_ATTRIBUTE
void cfree(void *ptr) {
- CHECK(!"cfree() should not be used on Windows?");
+ CHECK(!"cfree() should not be used on Windows");
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_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) {
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_malloc_dbg(size_t size, int, const char *, int) {
return malloc(size);
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_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);
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
+ return calloc(nmemb, size);
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_ATTRIBUTE
void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
return calloc(nmemb, size);
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_ATTRIBUTE
void *realloc(void *ptr, size_t size) {
GET_STACK_TRACE_MALLOC;
return asan_realloc(ptr, size, &stack);
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_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) {
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_recalloc(void *p, size_t n, size_t elem_size) {
if (!p)
return calloc(n, elem_size);
const size_t size = n * elem_size;
@@ -96,23 +102,23 @@
return realloc(p, size);
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_ATTRIBUTE
size_t _msize(void *ptr) {
GET_CURRENT_PC_BP_SP;
(void)sp;
return asan_malloc_usable_size(ptr, pc, bp);
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_ATTRIBUTE
void *_expand(void *memblock, size_t size) {
// _expand is used in realloc-like functions to resize the buffer if possible.
// We don't want memory to stand still while resizing buffers, so return 0.
return 0;
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_ATTRIBUTE
void *_expand_dbg(void *memblock, size_t size) {
- return 0;
+ return _expand(memblock, size);
}
// TODO(timurrrr): Might want to add support for _aligned_* allocation
@@ -133,37 +139,38 @@
}
} // extern "C"
-using __interception::GetRealFunctionAddress;
-
-// We don't want to include "windows.h" in this file to avoid extra attributes
-// set on malloc/free etc (e.g. dllimport), so declare a few things manually:
-extern "C" int __stdcall VirtualProtect(void* addr, size_t size,
- DWORD prot, DWORD *old_prot);
-const int PAGE_EXECUTE_READWRITE = 0x40;
-
namespace __asan {
void ReplaceSystemMalloc() {
-#if defined(_DLL)
-# ifdef _WIN64
-# error ReplaceSystemMalloc was not tested on x64
-# endif
- char *crt_malloc;
- if (GetRealFunctionAddress("malloc", (void**)&crt_malloc)) {
- // Replace malloc in the CRT dll with a jump to our malloc.
- DWORD old_prot, unused;
- CHECK(VirtualProtect(crt_malloc, 16, PAGE_EXECUTE_READWRITE, &old_prot));
- REAL(memset)(crt_malloc, 0xCC /* int 3 */, 16); // just in case.
+#if defined(ASAN_DYNAMIC)
+ // We don't check the result because CRT might not be used in the process.
+ __interception::OverrideFunction("free", (uptr)free);
+ __interception::OverrideFunction("malloc", (uptr)malloc);
+ __interception::OverrideFunction("_malloc_crt", (uptr)malloc);
+ __interception::OverrideFunction("calloc", (uptr)calloc);
+ __interception::OverrideFunction("_calloc_crt", (uptr)calloc);
+ __interception::OverrideFunction("realloc", (uptr)realloc);
+ __interception::OverrideFunction("_realloc_crt", (uptr)realloc);
+ __interception::OverrideFunction("_recalloc", (uptr)_recalloc);
+ __interception::OverrideFunction("_recalloc_crt", (uptr)_recalloc);
+ __interception::OverrideFunction("_msize", (uptr)_msize);
+ __interception::OverrideFunction("_expand", (uptr)_expand);
- ptrdiff_t jmp_offset = (char*)malloc - (char*)crt_malloc - 5;
- crt_malloc[0] = 0xE9; // jmp, should be followed by an offset.
- REAL(memcpy)(crt_malloc + 1, &jmp_offset, sizeof(jmp_offset));
-
- CHECK(VirtualProtect(crt_malloc, 16, old_prot, &unused));
-
- // FYI: FlushInstructionCache is needed on Itanium etc but not on x86/x64.
- }
-
- // FIXME: investigate whether anything else is needed.
+ // Override different versions of 'operator new' and 'operator delete'.
+ // No need to override the nothrow versions as they just wrap the throw
+ // versions.
+ // FIXME: Unfortunately, MSVC miscompiles the statements that take the
+ // addresses of the array versions of these operators,
+ // see https://connect.microsoft.com/VisualStudio/feedbackdetail/view/946992
+ // We might want to try to work around this by [inline] assembly or compiling
+ // parts of the RTL with Clang.
+ void *(*op_new)(size_t sz) = operator new;
+ void (*op_delete)(void *p) = operator delete;
+ void *(*op_array_new)(size_t sz) = operator new[];
+ void (*op_array_delete)(void *p) = operator delete[];
+ __interception::OverrideFunction("??2@YAPAXI@Z", (uptr)op_new);
+ __interception::OverrideFunction("??3@YAXPAX@Z", (uptr)op_delete);
+ __interception::OverrideFunction("??_U@YAPAXI@Z", (uptr)op_array_new);
+ __interception::OverrideFunction("??_V@YAXPAX@Z", (uptr)op_array_delete);
#endif
}
} // namespace __asan
diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h
index 8acc99a..2746754 100644
--- a/lib/asan/asan_mapping.h
+++ b/lib/asan/asan_mapping.h
@@ -60,11 +60,11 @@
// || `[0x00000000, 0x1fffffff]` || LowMem ||
//
// Default Linux/MIPS mapping:
-// || `[0x2aaa8000, 0xffffffff]` || HighMem ||
-// || `[0x0fffd000, 0x2aaa7fff]` || HighShadow ||
-// || `[0x0bffd000, 0x0fffcfff]` || ShadowGap ||
-// || `[0x0aaa8000, 0x0bffcfff]` || LowShadow ||
-// || `[0x00000000, 0x0aaa7fff]` || LowMem ||
+// || `[0x2aaa0000, 0xffffffff]` || HighMem ||
+// || `[0x0fff4000, 0x2aa9ffff]` || HighShadow ||
+// || `[0x0bff4000, 0x0fff3fff]` || ShadowGap ||
+// || `[0x0aaa0000, 0x0bff3fff]` || LowShadow ||
+// || `[0x00000000, 0x0aa9ffff]` || LowMem ||
//
// Shadow mapping on FreeBSD/x86-64 with SHADOW_OFFSET == 0x400000000000:
// || `[0x500000000000, 0x7fffffffffff]` || HighMem ||
@@ -86,7 +86,8 @@
static const u64 kDefaultShadowOffset64 = 1ULL << 44;
static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G.
static const u64 kAArch64_ShadowOffset64 = 1ULL << 36;
-static const u64 kMIPS32_ShadowOffset32 = 0x0aaa8000;
+static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000;
+static const u64 kMIPS64_ShadowOffset64 = 1ULL << 36;
static const u64 kPPC64_ShadowOffset64 = 1ULL << 41;
static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000
static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000
@@ -116,6 +117,8 @@
# define SHADOW_OFFSET kFreeBSD_ShadowOffset64
# elif SANITIZER_MAC
# define SHADOW_OFFSET kDefaultShadowOffset64
+# elif defined(__mips64)
+# define SHADOW_OFFSET kMIPS64_ShadowOffset64
# else
# define SHADOW_OFFSET kDefaultShort64bitShadowOffset
# endif
diff --git a/lib/asan/asan_new_delete.cc b/lib/asan/asan_new_delete.cc
index 86b9f28..e48bdaf 100644
--- a/lib/asan/asan_new_delete.cc
+++ b/lib/asan/asan_new_delete.cc
@@ -16,7 +16,7 @@
#include "asan_internal.h"
#include "asan_stack.h"
-#include "sanitizer_common/sanitizer_interception.h"
+#include "interception/interception.h"
#include <stddef.h>
@@ -105,6 +105,16 @@
void operator delete[](void *ptr, std::nothrow_t const&) {
OPERATOR_DELETE_BODY(FROM_NEW_BR);
}
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr, size_t size) throw() {
+ GET_STACK_TRACE_FREE;
+ asan_sized_free(ptr, size, &stack, FROM_NEW);
+}
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr, size_t size) throw() {
+ GET_STACK_TRACE_FREE;
+ asan_sized_free(ptr, size, &stack, FROM_NEW_BR);
+}
#else // SANITIZER_MAC
INTERCEPTOR(void, _ZdlPv, void *ptr) {
diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc
index b356e40..1c6e92f 100644
--- a/lib/asan/asan_poisoning.cc
+++ b/lib/asan/asan_poisoning.cc
@@ -61,6 +61,27 @@
FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
}
+void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) {
+ uptr end = ptr + size;
+ if (common_flags()->verbosity) {
+ Printf("__asan_%spoison_intra_object_redzone [%p,%p) %zd\n",
+ poison ? "" : "un", ptr, end, size);
+ if (common_flags()->verbosity >= 2)
+ PRINT_CURRENT_STACK();
+ }
+ CHECK(size);
+ CHECK_LE(size, 4096);
+ CHECK(IsAligned(end, SHADOW_GRANULARITY));
+ if (!IsAligned(ptr, SHADOW_GRANULARITY)) {
+ *(u8 *)MemToShadow(ptr) =
+ poison ? static_cast<u8>(ptr % SHADOW_GRANULARITY) : 0;
+ ptr |= SHADOW_GRANULARITY - 1;
+ ptr++;
+ }
+ for (; ptr < end; ptr += SHADOW_GRANULARITY)
+ *(u8*)MemToShadow(ptr) = poison ? kAsanIntraObjectRedzone : 0;
+}
+
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
@@ -227,6 +248,36 @@
*p = x;
}
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_poison_cxx_array_cookie(uptr p) {
+ if (SANITIZER_WORDSIZE != 64) return;
+ if (!flags()->poison_array_cookie) return;
+ uptr s = MEM_TO_SHADOW(p);
+ *reinterpret_cast<u8*>(s) = kAsanArrayCookieMagic;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_load_cxx_array_cookie(uptr *p) {
+ if (SANITIZER_WORDSIZE != 64) return *p;
+ if (!flags()->poison_array_cookie) return *p;
+ uptr s = MEM_TO_SHADOW(reinterpret_cast<uptr>(p));
+ u8 sval = *reinterpret_cast<u8*>(s);
+ if (sval == kAsanArrayCookieMagic) return *p;
+ // If sval is not kAsanArrayCookieMagic it can only be freed memory,
+ // which means that we are going to get double-free. So, return 0 to avoid
+ // infinite loop of destructors. We don't want to report a double-free here
+ // though, so print a warning just in case.
+ // CHECK_EQ(sval, kAsanHeapFreeMagic);
+ if (sval == kAsanHeapFreeMagic) {
+ Report("AddressSanitizer: loaded array cookie from free-d memory; "
+ "expect a double-free report\n");
+ return 0;
+ }
+ // The cookie may remain unpoisoned if e.g. it comes from a custom
+ // operator new defined inside a class.
+ return *p;
+}
+
// This is a simplified version of __asan_(un)poison_memory_region, which
// assumes that left border of region to be poisoned is properly aligned.
static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
@@ -345,6 +396,17 @@
return 0;
return 1;
}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_poison_intra_object_redzone(uptr ptr, uptr size) {
+ AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, true);
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_unpoison_intra_object_redzone(uptr ptr, uptr size) {
+ AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, false);
+}
+
// --- Implementation of LSan-specific functions --- {{{1
namespace __lsan {
bool WordIsPoisoned(uptr addr) {
diff --git a/lib/asan/asan_poisoning.h b/lib/asan/asan_poisoning.h
index bd680ae..feda1a9 100644
--- a/lib/asan/asan_poisoning.h
+++ b/lib/asan/asan_poisoning.h
@@ -35,7 +35,6 @@
ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
u8 value) {
DCHECK(flags()->poison_heap);
- uptr PageSize = GetPageSizeCached();
uptr shadow_beg = MEM_TO_SHADOW(aligned_beg);
uptr shadow_end = MEM_TO_SHADOW(
aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1;
@@ -48,8 +47,9 @@
shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
} else {
- uptr page_beg = RoundUpTo(shadow_beg, PageSize);
- uptr page_end = RoundDownTo(shadow_end, PageSize);
+ uptr page_size = GetPageSizeCached();
+ uptr page_beg = RoundUpTo(shadow_beg, page_size);
+ uptr page_end = RoundDownTo(shadow_end, page_size);
if (page_beg >= page_end) {
REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg);
diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc
index 57c9581..c910e23 100644
--- a/lib/asan/asan_posix.cc
+++ b/lib/asan/asan_posix.cc
@@ -33,6 +33,7 @@
namespace __asan {
void AsanOnSIGSEGV(int, void *siginfo, void *context) {
+ ScopedDeadlySignal signal_scope(GetCurrentThread());
uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr;
int code = (int)((siginfo_t*)siginfo)->si_code;
// Write the first message using the bullet-proof write.
@@ -41,16 +42,42 @@
GetPcSpBp(context, &pc, &sp, &bp);
// Access at a reasonable offset above SP, or slightly below it (to account
- // for x86_64 redzone, ARM push of multiple registers, etc) is probably a
- // stack overflow.
+ // for x86_64 or PowerPC redzone, ARM push of multiple registers, etc) is
+ // probably a stack overflow.
+ bool IsStackAccess = addr + 512 > sp && addr < sp + 0xFFFF;
+
+#if __powerpc__
+ // Large stack frames can be allocated with e.g.
+ // lis r0,-10000
+ // stdux r1,r1,r0 # store sp to [sp-10000] and update sp by -10000
+ // If the store faults then sp will not have been updated, so test above
+ // will not work, becase the fault address will be more than just "slightly"
+ // below sp.
+ if (!IsStackAccess && IsAccessibleMemoryRange(pc, 4)) {
+ u32 inst = *(unsigned *)pc;
+ u32 ra = (inst >> 16) & 0x1F;
+ u32 opcd = inst >> 26;
+ u32 xo = (inst >> 1) & 0x3FF;
+ // Check for store-with-update to sp. The instructions we accept are:
+ // stbu rs,d(ra) stbux rs,ra,rb
+ // sthu rs,d(ra) sthux rs,ra,rb
+ // stwu rs,d(ra) stwux rs,ra,rb
+ // stdu rs,ds(ra) stdux rs,ra,rb
+ // where ra is r1 (the stack pointer).
+ if (ra == 1 &&
+ (opcd == 39 || opcd == 45 || opcd == 37 || opcd == 62 ||
+ (opcd == 31 && (xo == 247 || xo == 439 || xo == 183 || xo == 181))))
+ IsStackAccess = true;
+ }
+#endif // __powerpc__
+
// We also check si_code to filter out SEGV caused by something else other
// then hitting the guard page or unmapped memory, like, for example,
// unaligned memory access.
- if (addr + 128 > sp && addr < sp + 0xFFFF &&
- (code == si_SEGV_MAPERR || code == si_SEGV_ACCERR))
+ if (IsStackAccess && (code == si_SEGV_MAPERR || code == si_SEGV_ACCERR))
ReportStackOverflow(pc, sp, bp, context, addr);
else
- ReportSIGSEGV(pc, sp, bp, context, addr);
+ ReportSIGSEGV("SEGV", pc, sp, bp, context, addr);
}
// ---------------------- TSD ---------------- {{{1
diff --git a/lib/asan/asan_preinit.cc b/lib/asan/asan_preinit.cc
index 586f551..a3986d2 100644
--- a/lib/asan/asan_preinit.cc
+++ b/lib/asan/asan_preinit.cc
@@ -10,22 +10,16 @@
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Call __asan_init at the very early stage of process startup.
-// On Linux we use .preinit_array section (unless PIC macro is defined).
//===----------------------------------------------------------------------===//
#include "asan_internal.h"
-#if ASAN_USE_PREINIT_ARRAY && !defined(PIC)
- // On Linux, we force __asan_init to be called before anyone else
- // by placing it into .preinit_array section.
- // FIXME: do we have anything like this on Mac?
+using namespace __asan;
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
// The symbol is called __local_asan_preinit, because it's not intended to be
// exported.
+ // This code linked into the main executable when -fsanitize=address is in
+ // the link flags. It can only use exported interface functions.
__attribute__((section(".preinit_array"), used))
void (*__local_asan_preinit)(void) = __asan_init;
-#elif SANITIZER_WINDOWS && defined(_DLL)
- // On Windows, when using dynamic CRT (/MD), we can put a pointer
- // to __asan_init into the global list of C initializers.
- // See crt0dat.c in the CRT sources for the details.
- #pragma section(".CRT$XIB", long, read) // NOLINT
- __declspec(allocate(".CRT$XIB")) void (*__asan_preinit)() = __asan_init;
#endif
diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc
index e13d59f..2ca11a3 100644
--- a/lib/asan/asan_report.cc
+++ b/lib/asan/asan_report.cc
@@ -31,6 +31,19 @@
static uptr error_message_buffer_pos = 0;
static uptr error_message_buffer_size = 0;
+struct ReportData {
+ uptr pc;
+ uptr sp;
+ uptr bp;
+ uptr addr;
+ bool is_write;
+ uptr access_size;
+ const char *description;
+};
+
+static bool report_happened = false;
+static ReportData report_data = {};
+
void AppendToErrorMessageBuffer(const char *buffer) {
if (error_message_buffer) {
uptr length = internal_strlen(buffer);
@@ -59,6 +72,7 @@
switch (byte) {
case kAsanHeapLeftRedzoneMagic:
case kAsanHeapRightRedzoneMagic:
+ case kAsanArrayCookieMagic:
return Red();
case kAsanHeapFreeMagic:
return Magenta();
@@ -80,20 +94,31 @@
return Red();
case kAsanInternalHeapMagic:
return Yellow();
+ case kAsanIntraObjectRedzone:
+ return Yellow();
default:
return Default();
}
}
const char *EndShadowByte() { return Default(); }
+ const char *MemoryByte() { return Magenta(); }
+ const char *EndMemoryByte() { return Default(); }
};
// ---------------------- Helper functions ----------------------- {{{1
-static void PrintShadowByte(InternalScopedString *str, const char *before,
- u8 byte, const char *after = "\n") {
+static void PrintMemoryByte(InternalScopedString *str, const char *before,
+ u8 byte, bool in_shadow, const char *after = "\n") {
Decorator d;
- str->append("%s%s%x%x%s%s", before, d.ShadowByte(byte), byte >> 4, byte & 15,
- d.EndShadowByte(), after);
+ str->append("%s%s%x%x%s%s", before,
+ in_shadow ? d.ShadowByte(byte) : d.MemoryByte(),
+ byte >> 4, byte & 15,
+ in_shadow ? d.EndShadowByte() : d.EndMemoryByte(), after);
+}
+
+static void PrintShadowByte(InternalScopedString *str, const char *before,
+ u8 byte, const char *after = "\n") {
+ PrintMemoryByte(str, before, byte, /*in_shadow*/true, after);
}
static void PrintShadowBytes(InternalScopedString *str, const char *before,
@@ -143,9 +168,29 @@
kAsanUserPoisonedMemoryMagic);
PrintShadowByte(str, " Container overflow: ",
kAsanContiguousContainerOOBMagic);
+ PrintShadowByte(str, " Array cookie: ",
+ kAsanArrayCookieMagic);
+ PrintShadowByte(str, " Intra object redzone: ",
+ kAsanIntraObjectRedzone);
PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
}
+void MaybeDumpInstructionBytes(uptr pc) {
+ if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached()))
+ return;
+ InternalScopedString str(1024);
+ str.append("First 16 instruction bytes at pc: ");
+ if (IsAccessibleMemoryRange(pc, 16)) {
+ for (int i = 0; i < 16; ++i) {
+ PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/false, " ");
+ }
+ str.append("\n");
+ } else {
+ str.append("unaccessible\n");
+ }
+ Report("%s", str.data());
+}
+
static void PrintShadowMemoryForAddress(uptr addr) {
if (!AddrIsInMem(addr)) return;
uptr shadow_addr = MemToShadow(addr);
@@ -197,7 +242,7 @@
else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
should_demangle = true;
- return should_demangle ? Symbolizer::Get()->Demangle(name) : name;
+ return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
}
// Check if the global is a zero-terminated ASCII string. If so, print it.
@@ -234,9 +279,7 @@
bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
const __asan_global &g) {
- static const uptr kMinimalDistanceFromAnotherGlobal = 64;
- if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
- if (addr >= g.beg + g.size_with_redzone) return false;
+ if (!IsAddressNearGlobal(addr, g)) return false;
InternalScopedString str(4096);
Decorator d;
str.append("%s", d.Location());
@@ -262,21 +305,20 @@
return true;
}
-bool DescribeAddressIfShadow(uptr addr) {
+bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr, bool print) {
if (AddrIsInMem(addr))
return false;
- static const char kAddrInShadowReport[] =
- "Address %p is located in the %s.\n";
- if (AddrIsInShadowGap(addr)) {
- Printf(kAddrInShadowReport, addr, "shadow gap area");
- return true;
- }
- if (AddrIsInHighShadow(addr)) {
- Printf(kAddrInShadowReport, addr, "high shadow area");
- return true;
- }
- if (AddrIsInLowShadow(addr)) {
- Printf(kAddrInShadowReport, addr, "low shadow area");
+ const char *area_type = nullptr;
+ if (AddrIsInShadowGap(addr)) area_type = "shadow gap";
+ else if (AddrIsInHighShadow(addr)) area_type = "high shadow";
+ else if (AddrIsInLowShadow(addr)) area_type = "low shadow";
+ if (area_type != nullptr) {
+ if (print) {
+ Printf("Address %p is located in the %s area.\n", addr, area_type);
+ } else {
+ CHECK(descr);
+ descr->region_kind = area_type;
+ }
return true;
}
CHECK(0 && "Address is not in memory and not in shadow?");
@@ -303,16 +345,15 @@
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;
+static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
+ uptr access_size, uptr prev_var_end,
+ uptr next_var_beg) {
+ uptr var_end = var.beg + var.size;
uptr addr_end = addr + access_size;
const char *pos_descr = 0;
- // If the variable [var_beg, var_end) is the nearest variable to the
+ // 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 >= 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)
@@ -321,14 +362,20 @@
next_var_beg - addr_end >= addr - var_end)
pos_descr = "overflows";
} else {
- if (addr_end > var_beg)
+ if (addr_end > var.beg)
pos_descr = "partially underflows";
else if (addr >= prev_var_end &&
- addr - prev_var_end >= var_beg - addr_end)
+ addr - prev_var_end >= var.beg - addr_end)
pos_descr = "underflows";
}
InternalScopedString str(1024);
- str.append(" [%zd, %zd) '%s'", var_beg, var_beg + var_size, var_name);
+ str.append(" [%zd, %zd)", var.beg, var_end);
+ // Render variable name.
+ str.append(" '");
+ for (uptr i = 0; i < var.name_len; ++i) {
+ str.append("%c", var.name_pos[i]);
+ }
+ str.append("'");
if (pos_descr) {
Decorator d;
// FIXME: we may want to also print the size of the access here,
@@ -341,41 +388,51 @@
Printf("%s", str.data());
}
-struct StackVarDescr {
- uptr beg;
- uptr size;
- const char *name_pos;
- uptr name_len;
-};
+bool ParseFrameDescription(const char *frame_descr,
+ InternalMmapVector<StackVarDescr> *vars) {
+ CHECK(frame_descr);
+ char *p;
+ // This string is created by the compiler and has the following form:
+ // "n alloc_1 alloc_2 ... alloc_n"
+ // where alloc_i looks like "offset size len ObjectName".
+ uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
+ if (n_objects == 0)
+ return false;
+
+ for (uptr i = 0; i < n_objects; i++) {
+ uptr beg = (uptr)internal_simple_strtoll(p, &p, 10);
+ uptr size = (uptr)internal_simple_strtoll(p, &p, 10);
+ uptr len = (uptr)internal_simple_strtoll(p, &p, 10);
+ if (beg == 0 || size == 0 || *p != ' ') {
+ return false;
+ }
+ p++;
+ StackVarDescr var = {beg, size, p, len};
+ vars->push_back(var);
+ p += len;
+ }
+
+ return true;
+}
bool DescribeAddressIfStack(uptr addr, uptr access_size) {
AsanThread *t = FindThreadByStackAddress(addr);
if (!t) return false;
- const uptr kBufSize = 4095;
- char buf[kBufSize];
- uptr offset = 0;
- uptr frame_pc = 0;
- char tname[128];
- const char *frame_descr = t->GetFrameNameByAddr(addr, &offset, &frame_pc);
-#ifdef __powerpc64__
- // On PowerPC64, the address of a function actually points to a
- // three-doubleword data structure with the first field containing
- // the address of the function's code.
- frame_pc = *reinterpret_cast<uptr *>(frame_pc);
-#endif
-
- // This string is created by the compiler and has the following form:
- // "n alloc_1 alloc_2 ... alloc_n"
- // where alloc_i looks like "offset size len ObjectName ".
- CHECK(frame_descr);
Decorator d;
+ char tname[128];
Printf("%s", d.Location());
- Printf("Address %p is located in stack of thread T%d%s "
- "at offset %zu in frame\n",
- addr, t->tid(),
- ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)),
- offset);
+ Printf("Address %p is located in stack of thread T%d%s", addr, t->tid(),
+ ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)));
+
+ // Try to fetch precise stack frame for this access.
+ AsanThread::StackFrameAccess access;
+ if (!t->GetStackFrameAccessByAddr(addr, &access)) {
+ Printf("%s\n", d.EndLocation());
+ return true;
+ }
+ Printf(" at offset %zu in frame%s\n", access.offset, d.EndLocation());
+
// Now we print the frame where the alloca has happened.
// We print this frame as a stack trace with one element.
// The symbolizer may print more than one frame if inlining was involved.
@@ -383,50 +440,42 @@
// previously. That's unfortunate, but I have no better solution,
// especially given that the alloca may be from entirely different place
// (e.g. use-after-scope, or different thread's stack).
- StackTrace alloca_stack;
- alloca_stack.trace[0] = frame_pc + 16;
- alloca_stack.size = 1;
+#if defined(__powerpc64__) && defined(__BIG_ENDIAN__)
+ // On PowerPC64 ELFv1, the address of a function actually points to a
+ // three-doubleword data structure with the first field containing
+ // the address of the function's code.
+ access.frame_pc = *reinterpret_cast<uptr *>(access.frame_pc);
+#endif
+ access.frame_pc += 16;
Printf("%s", d.EndLocation());
+ StackTrace alloca_stack(&access.frame_pc, 1);
alloca_stack.Print();
+
+ InternalMmapVector<StackVarDescr> vars(16);
+ if (!ParseFrameDescription(access.frame_descr, &vars)) {
+ Printf("AddressSanitizer can't parse the stack frame "
+ "descriptor: |%s|\n", access.frame_descr);
+ // 'addr' is a stack address, so return true even if we can't parse frame
+ return true;
+ }
+ uptr n_objects = vars.size();
// Report the number of stack objects.
- char *p;
- 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;
- 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++;
- vars[i].beg = beg;
- vars[i].size = size;
- vars[i].name_pos = p;
- vars[i].name_len = len;
- p += len;
- }
- 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,
+ PrintAccessAndVarIntersection(vars[i], access.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");
+ "some custom stack unwind mechanism or swapcontext\n");
+ if (SANITIZER_WINDOWS)
+ Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");
+ else
+ Printf(" (longjmp and C++ exceptions *are* supported)\n");
+
DescribeThread(t);
return true;
}
@@ -469,8 +518,7 @@
asanThreadRegistry().CheckLocked();
AsanThreadContext *alloc_thread =
GetThreadContextByTidLocked(chunk.AllocTid());
- StackTrace alloc_stack;
- chunk.GetAllocStack(&alloc_stack);
+ StackTrace alloc_stack = chunk.GetAllocStack();
char tname[128];
Decorator d;
AsanThreadContext *free_thread = 0;
@@ -480,8 +528,7 @@
free_thread->tid,
ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
d.EndAllocation());
- StackTrace free_stack;
- chunk.GetFreeStack(&free_stack);
+ StackTrace free_stack = chunk.GetFreeStack();
free_stack.Print();
Printf("%spreviously allocated by thread T%d%s here:%s\n",
d.Allocation(), alloc_thread->tid,
@@ -531,9 +578,7 @@
" created by T%d%s here:\n", context->parent_tid,
ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname)));
Printf("%s", str.data());
- uptr stack_size;
- const uptr *stack_trace = StackDepotGet(context->stack_id, &stack_size);
- StackTrace::PrintStack(stack_trace, stack_size);
+ StackDepotGet(context->stack_id).Print();
// Recursively described parent thread if needed.
if (flags()->print_full_thread_history) {
AsanThreadContext *parent_context =
@@ -548,14 +593,14 @@
// immediately after printing error report.
class ScopedInErrorReport {
public:
- ScopedInErrorReport() {
+ explicit ScopedInErrorReport(ReportData *report = nullptr) {
static atomic_uint32_t num_calls;
static u32 reporting_thread_tid;
if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
// Do not print more than one report, otherwise they will mix up.
// Error reporting functions shouldn't return at this situation, as
// they are defined as no-return.
- Report("AddressSanitizer: while reporting a bug found another one."
+ Report("AddressSanitizer: while reporting a bug found another one. "
"Ignoring.\n");
u32 current_tid = GetCurrentTidOrInvalid();
if (current_tid != reporting_thread_tid) {
@@ -568,6 +613,8 @@
// Die() to bypass any additional checks.
internal__exit(flags()->exitcode);
}
+ if (report) report_data = *report;
+ report_happened = true;
ASAN_ON_ERROR();
// Make sure the registry and sanitizer report mutexes are locked while
// we're printing an error report.
@@ -602,8 +649,8 @@
Printf("%s", d.Warning());
Report(
"ERROR: AddressSanitizer: stack-overflow on address %p"
- " (pc %p sp %p bp %p T%d)\n",
- (void *)addr, (void *)pc, (void *)sp, (void *)bp,
+ " (pc %p bp %p sp %p T%d)\n",
+ (void *)addr, (void *)pc, (void *)bp, (void *)sp,
GetCurrentTidOrInvalid());
Printf("%s", d.EndWarning());
GET_STACK_TRACE_SIGNAL(pc, bp, context);
@@ -611,23 +658,28 @@
ReportErrorSummary("stack-overflow", &stack);
}
-void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, void *context, uptr addr) {
+void ReportSIGSEGV(const char *description, uptr pc, uptr sp, uptr bp,
+ void *context, uptr addr) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
Report(
- "ERROR: AddressSanitizer: SEGV on unknown address %p"
- " (pc %p sp %p bp %p T%d)\n",
- (void *)addr, (void *)pc, (void *)sp, (void *)bp,
+ "ERROR: AddressSanitizer: %s on unknown address %p"
+ " (pc %p bp %p sp %p T%d)\n",
+ description, (void *)addr, (void *)pc, (void *)bp, (void *)sp,
GetCurrentTidOrInvalid());
+ if (pc < GetPageSizeCached()) {
+ Report("Hint: pc points to the zero page.\n");
+ }
Printf("%s", d.EndWarning());
GET_STACK_TRACE_SIGNAL(pc, bp, context);
stack.Print();
+ MaybeDumpInstructionBytes(pc);
Printf("AddressSanitizer can not provide additional info.\n");
ReportErrorSummary("SEGV", &stack);
}
-void ReportDoubleFree(uptr addr, StackTrace *free_stack) {
+void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
@@ -645,7 +697,31 @@
ReportErrorSummary("double-free", &stack);
}
-void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) {
+void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
+ BufferedStackTrace *free_stack) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ char tname[128];
+ u32 curr_tid = GetCurrentTidOrInvalid();
+ Report("ERROR: AddressSanitizer: new-delete-type-mismatch on %p in "
+ "thread T%d%s:\n",
+ addr, curr_tid,
+ ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
+ Printf("%s object passed to delete has wrong type:\n", d.EndWarning());
+ Printf(" size of the allocated type: %zd bytes;\n"
+ " size of the deallocated type: %zd bytes.\n",
+ asan_mz_size(reinterpret_cast<void*>(addr)), delete_size);
+ CHECK_GT(free_stack->size, 0);
+ GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
+ stack.Print();
+ DescribeHeapAddress(addr, 1);
+ ReportErrorSummary("new-delete-type-mismatch", &stack);
+ Report("HINT: if you don't care about these warnings you may set "
+ "ASAN_OPTIONS=new_delete_type_mismatch=0\n");
+}
+
+void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
@@ -662,7 +738,7 @@
ReportErrorSummary("bad-free", &stack);
}
-void ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
+void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
AllocType alloc_type,
AllocType dealloc_type) {
static const char *alloc_names[] =
@@ -685,7 +761,7 @@
"ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
}
-void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) {
+void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
@@ -698,7 +774,8 @@
ReportErrorSummary("bad-malloc_usable_size", stack);
}
-void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
+void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
+ BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
@@ -711,9 +788,10 @@
ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack);
}
-void ReportStringFunctionMemoryRangesOverlap(
- const char *function, const char *offset1, uptr length1,
- const char *offset2, uptr length2, StackTrace *stack) {
+void ReportStringFunctionMemoryRangesOverlap(const char *function,
+ const char *offset1, uptr length1,
+ const char *offset2, uptr length2,
+ BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
char bug_type[100];
@@ -730,7 +808,7 @@
}
void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
- StackTrace *stack) {
+ BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
const char *bug_type = "negative-size-param";
@@ -744,7 +822,7 @@
void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
uptr old_mid, uptr new_mid,
- StackTrace *stack) {
+ BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Report("ERROR: AddressSanitizer: bad parameters to "
"__sanitizer_annotate_contiguous_container:\n"
@@ -767,21 +845,23 @@
InternalScopedString g1_loc(256), g2_loc(256);
PrintGlobalLocation(&g1_loc, *g1);
PrintGlobalLocation(&g2_loc, *g2);
- Printf(" [1] size=%zd %s %s\n", g1->size, g1->name, g1_loc.data());
- Printf(" [2] size=%zd %s %s\n", g2->size, g2->name, g2_loc.data());
+ Printf(" [1] size=%zd '%s' %s\n", g1->size,
+ MaybeDemangleGlobalName(g1->name), g1_loc.data());
+ Printf(" [2] size=%zd '%s' %s\n", g2->size,
+ MaybeDemangleGlobalName(g2->name), g2_loc.data());
if (stack_id1 && stack_id2) {
Printf("These globals were registered at these points:\n");
Printf(" [1]:\n");
- uptr stack_size;
- const uptr *stack_trace = StackDepotGet(stack_id1, &stack_size);
- StackTrace::PrintStack(stack_trace, stack_size);
+ StackDepotGet(stack_id1).Print();
Printf(" [2]:\n");
- stack_trace = StackDepotGet(stack_id2, &stack_size);
- StackTrace::PrintStack(stack_trace, stack_size);
+ StackDepotGet(stack_id2).Print();
}
Report("HINT: if you don't care about these warnings you may set "
"ASAN_OPTIONS=detect_odr_violation=0\n");
- ReportErrorSummary("odr-violation", g1_loc.data(), 0, g1->name);
+ InternalScopedString error_msg(256);
+ error_msg.append("odr-violation: global '%s' at %s",
+ MaybeDemangleGlobalName(g1->name), g1_loc.data());
+ ReportErrorSummary(error_msg.data());
}
// ----------------------- CheckForInvalidPointerPair ----------- {{{1
@@ -814,8 +894,8 @@
}
// ----------------------- Mac-specific reports ----------------- {{{1
-void WarnMacFreeUnallocated(
- uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
+void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
+ BufferedStackTrace *stack) {
// Just print a warning here.
Printf("free_common(%p) -- attempting to free unallocated memory.\n"
"AddressSanitizer is ignoring this error on Mac OS now.\n",
@@ -825,8 +905,8 @@
DescribeHeapAddress(addr, 1);
}
-void ReportMacMzReallocUnknown(
- uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
+void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
+ BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
"This is an unrecoverable problem, exiting now.\n",
@@ -836,8 +916,8 @@
DescribeHeapAddress(addr, 1);
}
-void ReportMacCfReallocUnknown(
- uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
+void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
+ BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
"This is an unrecoverable problem, exiting now.\n",
@@ -854,8 +934,6 @@
void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
uptr access_size) {
- ScopedInErrorReport in_report;
-
// Determine the error type.
const char *bug_descr = "unknown-crash";
if (AddrIsInMem(addr)) {
@@ -869,6 +947,7 @@
switch (*shadow_addr) {
case kAsanHeapLeftRedzoneMagic:
case kAsanHeapRightRedzoneMagic:
+ case kAsanArrayCookieMagic:
bug_descr = "heap-buffer-overflow";
break;
case kAsanHeapFreeMagic:
@@ -900,12 +979,20 @@
case kAsanGlobalRedzoneMagic:
bug_descr = "global-buffer-overflow";
break;
+ case kAsanIntraObjectRedzone:
+ bug_descr = "intra-object-overflow";
+ break;
}
}
+
+ ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size,
+ bug_descr };
+ ScopedInErrorReport in_report(&report);
+
Decorator d;
Printf("%s", d.Warning());
Report("ERROR: AddressSanitizer: %s on address "
- "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n",
+ "%p at pc %p bp %p sp %p\n",
bug_descr, (void*)addr, pc, bp, sp);
Printf("%s", d.EndWarning());
@@ -937,7 +1024,42 @@
}
void __asan_describe_address(uptr addr) {
+ // Thread registry must be locked while we're describing an address.
+ asanThreadRegistry().Lock();
DescribeAddress(addr, 1);
+ asanThreadRegistry().Unlock();
+}
+
+int __asan_report_present() {
+ return report_happened ? 1 : 0;
+}
+
+uptr __asan_get_report_pc() {
+ return report_data.pc;
+}
+
+uptr __asan_get_report_bp() {
+ return report_data.bp;
+}
+
+uptr __asan_get_report_sp() {
+ return report_data.sp;
+}
+
+uptr __asan_get_report_address() {
+ return report_data.addr;
+}
+
+int __asan_get_report_access_type() {
+ return report_data.is_write ? 1 : 0;
+}
+
+uptr __asan_get_report_access_size() {
+ return report_data.access_size;
+}
+
+const char *__asan_get_report_description() {
+ return report_data.description;
}
extern "C" {
diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h
index 374ebfb..fd65bad 100644
--- a/lib/asan/asan_report.h
+++ b/lib/asan/asan_report.h
@@ -18,13 +18,33 @@
namespace __asan {
+struct StackVarDescr {
+ uptr beg;
+ uptr size;
+ const char *name_pos;
+ uptr name_len;
+};
+
+struct AddressDescription {
+ char *name;
+ uptr name_size;
+ uptr region_address;
+ uptr region_size;
+ const char *region_kind;
+};
+
// The following functions prints address description depending
// on the memory type (shadow/heap/stack/global).
void DescribeHeapAddress(uptr addr, uptr access_size);
bool DescribeAddressIfGlobal(uptr addr, uptr access_size);
bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
const __asan_global &g);
-bool DescribeAddressIfShadow(uptr addr);
+bool IsAddressNearGlobal(uptr addr, const __asan_global &g);
+bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr);
+bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr = nullptr,
+ bool print = true);
+bool ParseFrameDescription(const char *frame_descr,
+ InternalMmapVector<StackVarDescr> *vars);
bool DescribeAddressIfStack(uptr addr, uptr access_size);
// Determines memory type on its own.
void DescribeAddress(uptr addr, uptr access_size);
@@ -34,36 +54,44 @@
// Different kinds of error reports.
void NORETURN
ReportStackOverflow(uptr pc, uptr sp, uptr bp, void *context, uptr addr);
-void NORETURN
- ReportSIGSEGV(uptr pc, uptr sp, uptr bp, void *context, uptr addr);
-void NORETURN ReportDoubleFree(uptr addr, StackTrace *free_stack);
-void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *free_stack);
-void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
+void NORETURN ReportSIGSEGV(const char *description, uptr pc, uptr sp, uptr bp,
+ void *context, uptr addr);
+void NORETURN ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
+ BufferedStackTrace *free_stack);
+void NORETURN ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
+void NORETURN ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);
+void NORETURN ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
AllocType alloc_type,
AllocType dealloc_type);
-void NORETURN ReportMallocUsableSizeNotOwned(uptr addr,
- StackTrace *stack);
void NORETURN
-ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack);
-void NORETURN ReportStringFunctionMemoryRangesOverlap(
- const char *function, const char *offset1, uptr length1,
- const char *offset2, uptr length2, StackTrace *stack);
+ ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack);
void NORETURN
-ReportStringFunctionSizeOverflow(uptr offset, uptr size, StackTrace *stack);
+ ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
+ BufferedStackTrace *stack);
void NORETURN
-ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, uptr old_mid,
- uptr new_mid, StackTrace *stack);
+ ReportStringFunctionMemoryRangesOverlap(const char *function,
+ const char *offset1, uptr length1,
+ const char *offset2, uptr length2,
+ BufferedStackTrace *stack);
+void NORETURN ReportStringFunctionSizeOverflow(uptr offset, uptr size,
+ BufferedStackTrace *stack);
+void NORETURN
+ ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
+ uptr old_mid, uptr new_mid,
+ BufferedStackTrace *stack);
void NORETURN
ReportODRViolation(const __asan_global *g1, u32 stack_id1,
const __asan_global *g2, u32 stack_id2);
// Mac-specific errors and warnings.
-void WarnMacFreeUnallocated(
- uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
-void NORETURN ReportMacMzReallocUnknown(
- uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
-void NORETURN ReportMacCfReallocUnknown(
- uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
+void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
+ BufferedStackTrace *stack);
+void NORETURN ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr,
+ const char *zone_name,
+ BufferedStackTrace *stack);
+void NORETURN ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr,
+ const char *zone_name,
+ BufferedStackTrace *stack);
} // namespace __asan
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc
index 8f02257..f87d84f 100644
--- a/lib/asan/asan_rtl.cc
+++ b/lib/asan/asan_rtl.cc
@@ -67,7 +67,7 @@
Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file,
line, cond, (uptr)v1, (uptr)v2);
// FIXME: check for infinite recursion without a thread-local counter here.
- PRINT_CURRENT_STACK();
+ PRINT_CURRENT_STACK_CHECK();
Die();
}
@@ -173,11 +173,6 @@
"If set, prints ASan exit stats even after program terminates "
"successfully.");
- ParseFlag(str, &f->disable_core, "disable_core",
- "Disable core dumping. By default, disable_core=1 on 64-bit to avoid "
- "dumping a 16T+ core file. "
- "Ignored on OSes that don't dump core by default.");
-
ParseFlag(str, &f->allow_reexec, "allow_reexec",
"Allow the tool to re-exec the program. This may interfere badly with "
"the debugger.");
@@ -191,6 +186,9 @@
"Poison (or not) the heap memory on [de]allocation. Zero value is useful "
"for benchmarking the allocator or instrumentator.");
+ ParseFlag(str, &f->poison_array_cookie, "poison_array_cookie",
+ "Poison (or not) the array cookie after operator new[].");
+
ParseFlag(str, &f->poison_partial, "poison_partial",
"If true, poison partially addressable 8-byte aligned words "
"(default=true). This flag affects heap and global buffers, but not "
@@ -198,6 +196,10 @@
ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch",
"Report errors on malloc/delete, new/free, new/delete[], etc.");
+
+ ParseFlag(str, &f->new_delete_type_mismatch, "new_delete_type_mismatch",
+ "Report errors on mismatch betwen size of new and delete.");
+
ParseFlag(str, &f->strict_memcmp, "strict_memcmp",
"If true, assume that memcmp(p1, p2, n) always reads n bytes before "
"comparing p1 and p2.");
@@ -228,6 +230,9 @@
"If >=2, detect violation of One-Definition-Rule (ODR); "
"If ==1, detect ODR-violation only if the two variables "
"have different sizes");
+
+ ParseFlag(str, &f->dump_instruction_bytes, "dump_instruction_bytes",
+ "If true, dump 16 bytes starting at the instruction that caused SEGV");
}
void InitializeFlags(Flags *f, const char *env) {
@@ -264,22 +269,24 @@
f->print_stats = false;
f->print_legend = true;
f->atexit = false;
- f->disable_core = (SANITIZER_WORDSIZE == 64);
f->allow_reexec = true;
f->print_full_thread_history = true;
f->poison_heap = true;
+ f->poison_array_cookie = true;
f->poison_partial = true;
// Turn off alloc/dealloc mismatch checker on Mac and Windows for now.
// https://code.google.com/p/address-sanitizer/issues/detail?id=131
// https://code.google.com/p/address-sanitizer/issues/detail?id=309
// TODO(glider,timurrrr): Fix known issues and enable this back.
f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0);
+ f->new_delete_type_mismatch = true;
f->strict_memcmp = true;
f->strict_init_order = false;
f->start_deactivated = false;
f->detect_invalid_pointer_pairs = 0;
f->detect_container_overflow = true;
f->detect_odr_violation = 2;
+ f->dump_instruction_bytes = false;
// Override from compile definition.
ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefinition());
@@ -459,13 +466,6 @@
case 15: __asan_set_error_report_callback(0); break;
case 16: __asan_handle_no_return(); break;
case 17: __asan_address_is_poisoned(0); break;
- case 18: __asan_get_allocated_size(0); break;
- case 19: __asan_get_current_allocated_bytes(); break;
- case 20: __asan_get_estimated_allocated_size(0); break;
- case 21: __asan_get_free_bytes(); break;
- case 22: __asan_get_heap_size(); break;
- case 23: __asan_get_ownership(0); break;
- case 24: __asan_get_unmapped_bytes(); break;
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;
@@ -609,7 +609,8 @@
bool full_shadow_is_available =
MemoryRangeIsAvailable(shadow_start, kHighShadowEnd);
-#if SANITIZER_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING
+#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \
+ !ASAN_FIXED_MAPPING
if (!full_shadow_is_available) {
kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
@@ -619,9 +620,7 @@
if (common_flags()->verbosity)
PrintAddressSpaceLayout();
- if (flags()->disable_core) {
- DisableCoreDumper();
- }
+ DisableCoreDumperIfNecessary();
if (full_shadow_is_available) {
// mmap the low shadow plus at least one page at the left.
@@ -656,12 +655,8 @@
AsanTSDInit(PlatformTSDDtor);
InstallDeadlySignalHandlers(AsanOnSIGSEGV);
- // Allocator should be initialized before starting external symbolizer, as
- // fork() on Mac locks the allocator.
InitializeAllocator();
- Symbolizer::Init(common_flags()->external_symbolizer_path);
-
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
// should be set to 1 prior to initializing the threads.
asan_inited = 1;
@@ -690,7 +685,7 @@
SanitizerInitializeUnwinder();
#if CAN_SANITIZE_LEAKS
- __lsan::InitCommonLsan();
+ __lsan::InitCommonLsan(false);
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
Atexit(__lsan::DoLeakCheck);
}
diff --git a/lib/asan/asan_stack.h b/lib/asan/asan_stack.h
index 032f729..8610ee4 100644
--- a/lib/asan/asan_stack.h
+++ b/lib/asan/asan_stack.h
@@ -25,8 +25,9 @@
// The pc will be in the position 0 of the resulting stack trace.
// The bp may refer to the current frame or to the caller's frame.
ALWAYS_INLINE
-void GetStackTraceWithPcBpAndContext(StackTrace *stack, uptr max_depth, uptr pc,
- uptr bp, void *context, bool fast) {
+void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth,
+ uptr pc, uptr bp, void *context,
+ bool fast) {
#if SANITIZER_WINDOWS
stack->Unwind(max_depth, pc, bp, context, 0, 0, fast);
#else
@@ -34,6 +35,10 @@
stack->size = 0;
if (LIKELY(asan_inited)) {
if ((t = GetCurrentThread()) && !t->isUnwinding()) {
+ // On FreeBSD the slow unwinding that leverages _Unwind_Backtrace()
+ // yields the call stack of the signal's handler and not of the code
+ // that raised the signal (as it does on Linux).
+ if (SANITIZER_FREEBSD && t->isInDeadlySignal()) fast = true;
uptr stack_top = t->stack_top();
uptr stack_bottom = t->stack_bottom();
ScopedUnwinding unwind_scope(t);
@@ -53,14 +58,14 @@
// don't want stack trace to contain functions from ASan internals.
#define GET_STACK_TRACE(max_size, fast) \
- StackTrace stack; \
+ BufferedStackTrace stack; \
if (max_size <= 2) { \
stack.size = max_size; \
if (max_size > 0) { \
stack.top_frame_bp = GET_CURRENT_FRAME(); \
- stack.trace[0] = StackTrace::GetCurrentPc(); \
+ stack.trace_buffer[0] = StackTrace::GetCurrentPc(); \
if (max_size > 1) \
- stack.trace[1] = GET_CALLER_PC(); \
+ stack.trace_buffer[1] = GET_CALLER_PC(); \
} \
} else { \
GetStackTraceWithPcBpAndContext(&stack, max_size, \
@@ -69,18 +74,21 @@
}
#define GET_STACK_TRACE_FATAL(pc, bp) \
- StackTrace stack; \
+ BufferedStackTrace stack; \
GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, 0, \
common_flags()->fast_unwind_on_fatal)
#define GET_STACK_TRACE_SIGNAL(pc, bp, context) \
- StackTrace stack; \
+ BufferedStackTrace stack; \
GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context, \
common_flags()->fast_unwind_on_fatal)
#define GET_STACK_TRACE_FATAL_HERE \
GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)
+#define GET_STACK_TRACE_CHECK_HERE \
+ GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_check)
+
#define GET_STACK_TRACE_THREAD \
GET_STACK_TRACE(kStackTraceMax, true)
@@ -96,4 +104,10 @@
stack.Print(); \
}
+#define PRINT_CURRENT_STACK_CHECK() \
+ { \
+ GET_STACK_TRACE_CHECK_HERE; \
+ stack.Print(); \
+ }
+
#endif // ASAN_STACK_H
diff --git a/lib/asan/asan_stats.cc b/lib/asan/asan_stats.cc
index 0837bc8..a78b7b1 100644
--- a/lib/asan/asan_stats.cc
+++ b/lib/asan/asan_stats.cc
@@ -149,18 +149,12 @@
// way we update accumulated stats.
return (malloced > freed) ? malloced - freed : 1;
}
-uptr __asan_get_current_allocated_bytes() {
- return __sanitizer_get_current_allocated_bytes();
-}
uptr __sanitizer_get_heap_size() {
AsanStats stats;
GetAccumulatedStats(&stats);
return stats.mmaped - stats.munmaped;
}
-uptr __asan_get_heap_size() {
- return __sanitizer_get_heap_size();
-}
uptr __sanitizer_get_free_bytes() {
AsanStats stats;
@@ -175,16 +169,10 @@
// way we update accumulated stats.
return (total_free > total_used) ? total_free - total_used : 1;
}
-uptr __asan_get_free_bytes() {
- return __sanitizer_get_free_bytes();
-}
uptr __sanitizer_get_unmapped_bytes() {
return 0;
}
-uptr __asan_get_unmapped_bytes() {
- return __sanitizer_get_unmapped_bytes();
-}
void __asan_print_accumulated_stats() {
PrintAccumulatedStats();
diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc
index 48ff401..ce53bea 100644
--- a/lib/asan/asan_thread.cc
+++ b/lib/asan/asan_thread.cc
@@ -30,7 +30,7 @@
void AsanThreadContext::OnCreated(void *arg) {
CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg);
if (args->stack)
- stack_id = StackDepotPut(args->stack->trace, args->stack->size);
+ stack_id = StackDepotPut(*args->stack);
thread = args->thread;
thread->set_context(this);
}
@@ -198,17 +198,18 @@
PoisonShadow(tls_begin_, tls_end_ - tls_begin_, 0);
}
-const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset,
- uptr *frame_pc) {
+bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
+ StackFrameAccess *access) {
uptr bottom = 0;
if (AddrIsInStack(addr)) {
bottom = stack_bottom();
} else if (has_fake_stack()) {
bottom = fake_stack()->AddrIsInFakeStack(addr);
CHECK(bottom);
- *offset = addr - bottom;
- *frame_pc = ((uptr*)bottom)[2];
- return (const char *)((uptr*)bottom)[1];
+ access->offset = addr - bottom;
+ access->frame_pc = ((uptr*)bottom)[2];
+ access->frame_descr = (const char *)((uptr*)bottom)[1];
+ return true;
}
uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr.
u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr);
@@ -225,15 +226,15 @@
}
if (shadow_ptr < shadow_bottom) {
- *offset = 0;
- return "UNKNOWN";
+ return false;
}
uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1));
CHECK(ptr[0] == kCurrentStackFrameMagic);
- *offset = addr - (uptr)ptr;
- *frame_pc = ptr[2];
- return (const char*)ptr[1];
+ access->offset = addr - (uptr)ptr;
+ access->frame_pc = ptr[2];
+ access->frame_descr = (const char*)ptr[1];
+ return true;
}
static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h
index 1bce25c..bf23728 100644
--- a/lib/asan/asan_thread.h
+++ b/lib/asan/asan_thread.h
@@ -71,7 +71,12 @@
AsanThreadContext *context() { return context_; }
void set_context(AsanThreadContext *context) { context_ = context; }
- const char *GetFrameNameByAddr(uptr addr, uptr *offset, uptr *frame_pc);
+ struct StackFrameAccess {
+ uptr offset;
+ uptr frame_pc;
+ const char *frame_descr;
+ };
+ bool GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access);
bool AddrIsInStack(uptr addr) {
return addr >= stack_bottom_ && addr < stack_top_;
@@ -103,6 +108,10 @@
bool isUnwinding() const { return unwinding_; }
void setUnwinding(bool b) { unwinding_ = b; }
+ // True if we are in a deadly signal handler.
+ bool isInDeadlySignal() const { return in_deadly_signal_; }
+ void setInDeadlySignal(bool b) { in_deadly_signal_ = b; }
+
AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; }
AsanStats &stats() { return stats_; }
@@ -128,6 +137,7 @@
AsanThreadLocalMallocStorage malloc_storage_;
AsanStats stats_;
bool unwinding_;
+ bool in_deadly_signal_;
};
// ScopedUnwinding is a scope for stacktracing member of a context
@@ -142,6 +152,20 @@
AsanThread *thread;
};
+// ScopedDeadlySignal is a scope for handling deadly signals.
+class ScopedDeadlySignal {
+ public:
+ explicit ScopedDeadlySignal(AsanThread *t) : thread(t) {
+ if (thread) thread->setInDeadlySignal(true);
+ }
+ ~ScopedDeadlySignal() {
+ if (thread) thread->setInDeadlySignal(false);
+ }
+
+ private:
+ AsanThread *thread;
+};
+
struct CreateThreadContextArgs {
AsanThread *thread;
StackTrace *stack;
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index da26e98..5303d1b 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -21,6 +21,7 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
+#include "asan_report.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_mutex.h"
@@ -70,7 +71,7 @@
return 0;
}
-void AsanCheckDynamicRTPrereqs() { UNIMPLEMENTED(); }
+void AsanCheckDynamicRTPrereqs() {}
void AsanCheckIncompatibleRT() {}
@@ -86,6 +87,67 @@
UNIMPLEMENTED();
}
+static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;
+
+static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
+ EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
+ CONTEXT *context = info->ContextRecord;
+ uptr pc = (uptr)exception_record->ExceptionAddress;
+#ifdef _WIN64
+ uptr bp = (uptr)context->Rbp, sp = (uptr)context->Rsp;
+#else
+ uptr bp = (uptr)context->Ebp, sp = (uptr)context->Esp;
+#endif
+
+ if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
+ exception_record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) {
+ const char *description =
+ (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+ ? "access-violation"
+ : "in-page-error";
+ uptr access_addr = exception_record->ExceptionInformation[1];
+ ReportSIGSEGV(description, pc, sp, bp, context, access_addr);
+ }
+
+ // FIXME: Handle EXCEPTION_STACK_OVERFLOW here.
+
+ return default_seh_handler(info);
+}
+
+// We want to install our own exception handler (EH) to print helpful reports
+// on access violations and whatnot. Unfortunately, the CRT initializers assume
+// they are run before any user code and drop any previously-installed EHs on
+// the floor, so we can't install our handler inside __asan_init.
+// (See crt0dat.c in the CRT sources for the details)
+//
+// Things get even more complicated with the dynamic runtime, as it finishes its
+// initialization before the .exe module CRT begins to initialize.
+//
+// For the static runtime (-MT), it's enough to put a callback to
+// __asan_set_seh_filter in the last section for C initializers.
+//
+// For the dynamic runtime (-MD), we want link the same
+// asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter
+// will be called for each instrumented module. This ensures that at least one
+// __asan_set_seh_filter call happens after the .exe module CRT is initialized.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+int __asan_set_seh_filter() {
+ // We should only store the previous handler if it's not our own handler in
+ // order to avoid loops in the EH chain.
+ auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler);
+ if (prev_seh_handler != &SEHHandler)
+ default_seh_handler = prev_seh_handler;
+ return 0;
+}
+
+#if !ASAN_DYNAMIC
+// Put a pointer to __asan_set_seh_filter at the end of the global list
+// of C initializers, after the default EH is set by the CRT.
+#pragma section(".CRT$XIZ", long, read) // NOLINT
+static __declspec(allocate(".CRT$XIZ"))
+ int (*__intercept_seh)() = __asan_set_seh_filter;
+#endif
+
} // namespace __asan
#endif // _WIN32
diff --git a/lib/asan/asan_dll_thunk.cc b/lib/asan/asan_win_dll_thunk.cc
similarity index 93%
rename from lib/asan/asan_dll_thunk.cc
rename to lib/asan/asan_win_dll_thunk.cc
index a3fbb27..b38a2d1 100644
--- a/lib/asan/asan_dll_thunk.cc
+++ b/lib/asan/asan_win_dll_thunk.cc
@@ -1,4 +1,4 @@
-//===-- asan_dll_thunk.cc -------------------------------------------------===//
+//===-- asan_win_dll_thunk.cc ---------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -21,7 +21,7 @@
// simplifies the build procedure.
#ifdef ASAN_DLL_THUNK
#include "asan_init_version.h"
-#include "sanitizer_common/sanitizer_interception.h"
+#include "interception/interception.h"
// ---------- Function interception helper functions and macros ----------- {{{1
extern "C" {
@@ -75,7 +75,7 @@
// Special case of hooks -- ASan own interface functions. Those are only called
// after __asan_init, thus an empty implementation is sufficient.
#define INTERFACE_FUNCTION(name) \
- extern "C" void name() { \
+ extern "C" __declspec(noinline) void name() { \
volatile int prevent_icf = (__LINE__ << 8); (void)prevent_icf; \
__debugbreak(); \
} \
@@ -235,6 +235,20 @@
INTERFACE_FUNCTION(__asan_report_load16)
INTERFACE_FUNCTION(__asan_report_load_n)
+INTERFACE_FUNCTION(__asan_store1)
+INTERFACE_FUNCTION(__asan_store2)
+INTERFACE_FUNCTION(__asan_store4)
+INTERFACE_FUNCTION(__asan_store8)
+INTERFACE_FUNCTION(__asan_store16)
+INTERFACE_FUNCTION(__asan_storeN)
+
+INTERFACE_FUNCTION(__asan_load1)
+INTERFACE_FUNCTION(__asan_load2)
+INTERFACE_FUNCTION(__asan_load4)
+INTERFACE_FUNCTION(__asan_load8)
+INTERFACE_FUNCTION(__asan_load16)
+INTERFACE_FUNCTION(__asan_loadN)
+
INTERFACE_FUNCTION(__asan_memcpy);
INTERFACE_FUNCTION(__asan_memset);
INTERFACE_FUNCTION(__asan_memmove);
@@ -251,6 +265,9 @@
INTERFACE_FUNCTION(__asan_poison_memory_region)
INTERFACE_FUNCTION(__asan_unpoison_memory_region)
+INTERFACE_FUNCTION(__asan_address_is_poisoned)
+INTERFACE_FUNCTION(__asan_region_is_poisoned)
+
INTERFACE_FUNCTION(__asan_get_current_fake_stack)
INTERFACE_FUNCTION(__asan_addr_is_in_fake_stack)
@@ -307,6 +324,15 @@
INTERCEPT_LIBRARY_FUNCTION(atoi);
INTERCEPT_LIBRARY_FUNCTION(atol);
+INTERCEPT_LIBRARY_FUNCTION(_except_handler3);
+
+// _except_handler4 checks -GS cookie which is different for each module, so we
+// can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4).
+INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
+ __asan_handle_no_return();
+ return REAL(_except_handler4)(a, b, c, d);
+}
+
INTERCEPT_LIBRARY_FUNCTION(frexp);
INTERCEPT_LIBRARY_FUNCTION(longjmp);
INTERCEPT_LIBRARY_FUNCTION(memchr);
@@ -330,6 +356,7 @@
// is defined.
void InterceptHooks() {
INTERCEPT_HOOKS();
+ INTERCEPT_FUNCTION(_except_handler4);
}
// We want to call __asan_init before C/C++ initializers/constructors are
diff --git a/lib/asan/asan_win_dynamic_runtime_thunk.cc b/lib/asan/asan_win_dynamic_runtime_thunk.cc
new file mode 100644
index 0000000..3a4de7d
--- /dev/null
+++ b/lib/asan/asan_win_dynamic_runtime_thunk.cc
@@ -0,0 +1,52 @@
+//===-- asan_win_uar_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 things that need to be present in the application modules
+// to interact with the ASan DLL runtime correctly and can't be implemented
+// using the default "import library" generated when linking the DLL RTL.
+//
+// This includes:
+// - forwarding the detect_stack_use_after_return runtime option
+// - installing a custom SEH handler
+//
+//===----------------------------------------------------------------------===//
+
+// Only compile this code when buidling asan_dynamic_runtime_thunk.lib
+// Using #ifdef rather than relying on Makefiles etc.
+// simplifies the build procedure.
+#ifdef ASAN_DYNAMIC_RUNTIME_THUNK
+extern "C" {
+__declspec(dllimport) int __asan_set_seh_filter();
+__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
+
+// Define a copy of __asan_option_detect_stack_use_after_return that should be
+// used when linking an MD runtime with a set of object files on Windows.
+//
+// The ASan MD runtime dllexports '__asan_option_detect_stack_use_after_return',
+// so normally we would just dllimport it. Unfortunately, the dllimport
+// attribute adds __imp_ prefix to the symbol name of a variable.
+// Since in general we don't know if a given TU is going to be used
+// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows
+// just to work around this issue, let's clone the a variable that is
+// constant after initialization anyways.
+int __asan_option_detect_stack_use_after_return =
+ __asan_should_detect_stack_use_after_return();
+
+// Set the ASan-specific SEH handler at the end of CRT initialization of each
+// module (see asan_win.cc for the details).
+//
+// Unfortunately, putting a pointer to __asan_set_seh_filter into
+// __asan_intercept_seh gets optimized out, so we have to use an extra function.
+static int SetSEHFilter() { return __asan_set_seh_filter(); }
+#pragma section(".CRT$XIZ", long, read) // NOLINT
+__declspec(allocate(".CRT$XIZ")) int (*__asan_seh_interceptor)() = SetSEHFilter;
+}
+#endif // ASAN_DYNAMIC_RUNTIME_THUNK
diff --git a/lib/asan/scripts/asan_device_setup b/lib/asan/scripts/asan_device_setup
index 261e2c6..a620f51 100755
--- a/lib/asan/scripts/asan_device_setup
+++ b/lib/asan/scripts/asan_device_setup
@@ -1,4 +1,4 @@
-#!/bin/bash -e
+#!/bin/bash
#===- lib/asan/scripts/asan_device_setup -----------------------------------===#
#
# The LLVM Compiler Infrastructure
@@ -10,6 +10,7 @@
#
#===------------------------------------------------------------------------===#
+set -e
HERE="$(cd "$(dirname "$0")" && pwd)"
@@ -29,6 +30,21 @@
exit 1
}
+function get_device_arch { # OUTVAR
+ local _outvar=$1
+ local _ABI=$($ADB shell getprop ro.product.cpu.abi)
+ local _ARCH=
+ if [[ $_ABI == x86* ]]; then
+ _ARCH=i686
+ elif [[ $_ABI == armeabi* ]]; then
+ _ARCH=arm
+ else
+ echo "Unrecognized device ABI: $_ABI"
+ exit 1
+ fi
+ eval $_outvar=\$_ARCH
+}
+
while [[ $# > 0 ]]; do
case $1 in
--revert)
@@ -70,16 +86,31 @@
ADB="$ADB -s $device"
fi
-ASAN_RT="libclang_rt.asan-arm-android.so"
+echo '>> Remounting /system rw'
+$ADB root
+$ADB wait-for-device
+$ADB remount
+$ADB wait-for-device
+
+get_device_arch ARCH
+echo "Target architecture: $ARCH"
+ASAN_RT="libclang_rt.asan-$ARCH-android.so"
if [[ x$revert == xyes ]]; then
echo '>> Uninstalling ASan'
- $ADB root
- $ADB wait-for-device
- $ADB remount
- $ADB shell mv /system/bin/app_process.real /system/bin/app_process
- $ADB shell rm /system/bin/asanwrapper
- $ADB shell rm /system/lib/$ASAN_RT
+
+ if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then
+ echo '>> Pre-L device detected.'
+ $ADB shell mv /system/bin/app_process.real /system/bin/app_process
+ $ADB shell rm /system/bin/asanwrapper
+ $ADB shell rm /system/lib/$ASAN_RT
+ else
+ $ADB shell rm /system/bin/app_process.wrap
+ $ADB shell rm /system/bin/asanwrapper
+ $ADB shell rm /system/lib/$ASAN_RT
+ $ADB shell rm /system/bin/app_process
+ $ADB shell ln -s /system/bin/app_process32 /system/bin/app_process
+ fi
echo '>> Restarting shell'
$ADB shell stop
@@ -97,15 +128,15 @@
ASAN_RT_PATH="$HERE"
elif [[ $(basename "$HERE") == "bin" ]]; then
# We could be in the toolchain's base directory.
- # Consider ../lib, ../lib/asan and ../lib/clang/$VERSION/lib/linux.
- P=$(ls "$HERE"/../lib/"$ASAN_RT" "$HERE"/../lib/asan/"$ASAN_RT" "$HERE"/../lib/clang/*/lib/linux/"$ASAN_RT" 2>/dev/null | sort | tail -1)
+ # Consider ../lib, ../lib/asan, ../lib/linux and ../lib/clang/$VERSION/lib/linux.
+ P=$(ls "$HERE"/../lib/"$ASAN_RT" "$HERE"/../lib/asan/"$ASAN_RT" "$HERE"/../lib/linux/"$ASAN_RT" "$HERE"/../lib/clang/*/lib/linux/"$ASAN_RT" 2>/dev/null | sort | tail -1)
if [[ -n "$P" ]]; then
ASAN_RT_PATH="$(dirname "$P")"
fi
fi
if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then
- echo "ASan runtime library not found"
+ echo ">> ASan runtime library not found"
exit 1
fi
@@ -114,28 +145,34 @@
TMPDIR="$TMPDIRBASE/new"
mkdir "$TMPDIROLD"
-echo '>> Remounting /system rw'
-$ADB root
-$ADB wait-for-device
-$ADB remount
-
-echo '>> Copying files from the device'
-$ADB pull /system/bin/app_process "$TMPDIROLD"
-$ADB pull /system/bin/app_process.real "$TMPDIROLD" || true
-$ADB pull /system/bin/asanwrapper "$TMPDIROLD" || true
-$ADB pull /system/lib/libclang_rt.asan-arm-android.so "$TMPDIROLD" || true
-cp -r "$TMPDIROLD" "$TMPDIR"
-
-if ! [[ -f "$TMPDIR/app_process" ]]; then
- echo "app_process missing???"
- exit 1
+RELEASE=$($ADB shell getprop ro.build.version.release)
+PRE_L=0
+if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
+ PRE_L=1
fi
-if [[ -f "$TMPDIR/app_process.real" ]]; then
- echo "app_process.real exists, updating the wrapper"
+if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then
+
+ if $ADB pull /system/bin/app_process.real /dev/null >&/dev/null; then
+ echo '>> Old-style ASan installation detected. Reverting.'
+ $ADB shell mv /system/bin/app_process.real /system/bin/app_process
+ fi
+
+ echo '>> Pre-L device detected. Setting up app_process symlink.'
+ $ADB shell mv /system/bin/app_process /system/bin/app_process32
+ $ADB shell ln -s /system/bin/app_process32 /system/bin/app_process
+fi
+
+echo '>> Copying files from the device'
+$ADB pull /system/bin/app_process.wrap "$TMPDIROLD" || true
+$ADB pull /system/bin/asanwrapper "$TMPDIROLD" || true
+$ADB pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
+cp -r "$TMPDIROLD" "$TMPDIR"
+
+if [[ -f "$TMPDIR/app_process.wrap" ]]; then
+ echo ">> Previous installation detected"
else
- echo "app_process.real missing, new installation"
- mv "$TMPDIR/app_process" "$TMPDIR/app_process.real"
+ echo ">> New installation"
fi
echo '>> Generating wrappers'
@@ -145,16 +182,22 @@
# FIXME: alloc_dealloc_mismatch=0 prevents a failure in libdvm startup,
# which may or may not be a real bug (probably not).
ASAN_OPTIONS=start_deactivated=1,alloc_dealloc_mismatch=0
+
+# On Android-L not allowing user segv handler breaks some applications.
+if $ADB shell 'echo $LD_PRELOAD' | grep libsigchain.so >&/dev/null; then
+ ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1"
+fi
+
if [[ x$extra_options != x ]] ; then
ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options"
fi
# Zygote wrapper.
-cat <<EOF >"$TMPDIR/app_process"
-#!/system/bin/sh
+cat <<EOF >"$TMPDIR/app_process.wrap"
+#!/system/bin/sh-from-zygote
ASAN_OPTIONS=$ASAN_OPTIONS \\
-LD_PRELOAD=libclang_rt.asan-arm-android.so \\
-exec /system/bin/app_process.real \$@
+LD_PRELOAD=\$LD_PRELOAD:$ASAN_RT \\
+exec /system/bin/app_process32 \$@
EOF
@@ -162,7 +205,7 @@
# zygote).
cat <<EOF >"$TMPDIR/asanwrapper"
#!/system/bin/sh
-LD_PRELOAD=libclang_rt.asan-arm-android.so \\
+LD_PRELOAD=$ASAN_RT \\
exec \$@
EOF
@@ -170,18 +213,48 @@
if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
echo '>> Pushing files to the device'
$ADB push "$TMPDIR/$ASAN_RT" /system/lib/
- $ADB push "$TMPDIR/app_process" /system/bin/app_process
- $ADB push "$TMPDIR/app_process.real" /system/bin/app_process.real
+ $ADB push "$TMPDIR/app_process.wrap" /system/bin/app_process.wrap
$ADB push "$TMPDIR/asanwrapper" /system/bin/asanwrapper
+
+ $ADB shell rm /system/bin/app_process
+ $ADB shell ln -s /system/bin/app_process.wrap /system/bin/app_process
+
$ADB shell chown root.shell \
- /system/bin/app_process \
- /system/bin/app_process.real \
+ /system/lib/"$ASAN_RT" \
+ /system/bin/app_process.wrap \
/system/bin/asanwrapper
+ $ADB shell chmod 644 \
+ /system/lib/"$ASAN_RT"
$ADB shell chmod 755 \
- /system/bin/app_process \
- /system/bin/app_process.real \
+ /system/bin/app_process.wrap \
/system/bin/asanwrapper
+ # Make SELinux happy by keeping app_process wrapper and the shell
+ # it runs on in zygote domain.
+ ENFORCING=0
+ if $ADB shell getenforce | grep Enforcing >/dev/null; then
+ # Sometimes shell is not allowed to change file contexts.
+ # Temporarily switch to permissive.
+ ENFORCING=1
+ $ADB shell setenforce 0
+ fi
+
+ $ADB shell cp /system/bin/sh /system/bin/sh-from-zygote
+
+ if [[ PRE_L -eq 1 ]]; then
+ CTX=u:object_r:system_file:s0
+ else
+ CTX=u:object_r:zygote_exec:s0
+ fi
+ $ADB shell chcon $CTX \
+ /system/bin/sh-from-zygote \
+ /system/bin/app_process.wrap \
+ /system/bin/app_process32
+
+ if [ $ENFORCING == 1 ]; then
+ $ADB shell setenforce 1
+ fi
+
echo '>> Restarting shell (asynchronous)'
$ADB shell stop
$ADB shell start
diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py
index a2f34f6..76de60a 100755
--- a/lib/asan/scripts/asan_symbolize.py
+++ b/lib/asan/scripts/asan_symbolize.py
@@ -7,6 +7,7 @@
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
+import argparse
import bisect
import getopt
import os
@@ -18,18 +19,26 @@
symbolizers = {}
DEBUG = False
-demangle = False;
-
+demangle = False
+binutils_prefix = None
+sysroot_path = None
+binary_name_filter = None
+fix_filename_patterns = None
+logfile = sys.stdin
# FIXME: merge the code that calls fix_filename().
def fix_filename(file_name):
- for path_to_cut in sys.argv[1:]:
- file_name = re.sub('.*' + path_to_cut, '', file_name)
+ if fix_filename_patterns:
+ for path_to_cut in fix_filename_patterns:
+ file_name = re.sub('.*' + path_to_cut, '', file_name)
file_name = re.sub('.*asan_[a-z_]*.cc:[0-9]*', '_asan_rtl_', file_name)
file_name = re.sub('.*crtstuff.c:0', '???:0', file_name)
return file_name
-def GuessArch(addr):
+def sysroot_path_filter(binary_name):
+ return sysroot_path + binary_name
+
+def guess_arch(addr):
# Guess which arch we're running. 10 = len('0x') + 8 hex digits.
if len(addr) > 10:
return 'x86_64'
@@ -60,7 +69,7 @@
def __init__(self, symbolizer_path, addr):
super(LLVMSymbolizer, self).__init__()
self.symbolizer_path = symbolizer_path
- self.default_arch = GuessArch(addr)
+ self.default_arch = guess_arch(addr)
self.pipe = self.open_llvm_symbolizer()
def open_llvm_symbolizer(self):
@@ -124,7 +133,10 @@
self.pipe = self.open_addr2line()
def open_addr2line(self):
- cmd = ['addr2line', '-f']
+ addr2line_tool = 'addr2line'
+ if binutils_prefix:
+ addr2line_tool = binutils_prefix + addr2line_tool
+ cmd = [addr2line_tool, '-f']
if demangle:
cmd += ['--demangle']
cmd += ['-e', self.binary]
@@ -182,7 +194,7 @@
def __init__(self, addr, binary):
super(DarwinSymbolizer, self).__init__()
self.binary = binary
- self.arch = GuessArch(addr)
+ self.arch = guess_arch(addr)
self.open_atos()
def open_atos(self):
@@ -328,9 +340,10 @@
# E.g. in Chrome several binaries may share a single .dSYM.
self.binary_name_filter = binary_name_filter
self.system = os.uname()[0]
- if self.system not in ['Linux', 'Darwin']:
+ if self.system not in ['Linux', 'Darwin', 'FreeBSD']:
raise Exception('Unknown system')
self.llvm_symbolizer = None
+ self.frame_no = 0
def symbolize_address(self, addr, binary, offset):
# Initialize llvm-symbolizer lazily.
@@ -352,48 +365,77 @@
assert result
return result
- def print_symbolized_lines(self, symbolized_lines):
+ def get_symbolized_lines(self, symbolized_lines):
if not symbolized_lines:
- print self.current_line
+ return [self.current_line]
else:
+ result = []
for symbolized_frame in symbolized_lines:
- print ' #' + str(self.frame_no) + ' ' + symbolized_frame.rstrip()
+ result.append(' #%s %s' % (str(self.frame_no), symbolized_frame.rstrip()))
self.frame_no += 1
+ return result
- def process_stdin(self):
+ def process_logfile(self):
self.frame_no = 0
while True:
- line = sys.stdin.readline()
+ line = logfile.readline()
if not line:
break
- self.current_line = line.rstrip()
- #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
- stack_trace_line_format = (
- '^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)')
- match = re.match(stack_trace_line_format, line)
- if not match:
- print self.current_line
- continue
- if DEBUG:
- print line
- _, frameno_str, addr, binary, offset = match.groups()
- if frameno_str == '0':
- # Assume that frame #0 is the first frame of new stack trace.
- self.frame_no = 0
- original_binary = binary
- if self.binary_name_filter:
- binary = self.binary_name_filter(binary)
- symbolized_line = self.symbolize_address(addr, binary, offset)
- if not symbolized_line:
- if original_binary != binary:
- symbolized_line = self.symbolize_address(addr, binary, offset)
- self.print_symbolized_lines(symbolized_line)
+ processed = self.process_line(line)
+ print '\n'.join(processed)
+
+ def process_line(self, line):
+ self.current_line = line.rstrip()
+ #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
+ stack_trace_line_format = (
+ '^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)')
+ match = re.match(stack_trace_line_format, line)
+ if not match:
+ return [self.current_line]
+ if DEBUG:
+ print line
+ _, frameno_str, addr, binary, offset = match.groups()
+ if frameno_str == '0':
+ # Assume that frame #0 is the first frame of new stack trace.
+ self.frame_no = 0
+ original_binary = binary
+ if self.binary_name_filter:
+ binary = self.binary_name_filter(binary)
+ symbolized_line = self.symbolize_address(addr, binary, offset)
+ if not symbolized_line:
+ if original_binary != binary:
+ symbolized_line = self.symbolize_address(addr, binary, offset)
+ return self.get_symbolized_lines(symbolized_line)
if __name__ == '__main__':
- opts, args = getopt.getopt(sys.argv[1:], "d", ["demangle"])
- for o, a in opts:
- if o in ("-d", "--demangle"):
- demangle = True;
- loop = SymbolizationLoop()
- loop.process_stdin()
+ parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
+ description='ASan symbolization script',
+ epilog='''Example of use:
+ asan_symbolize.py -c "$HOME/opt/cross/bin/arm-linux-gnueabi-" -s "$HOME/SymbolFiles" < asan.log''')
+ parser.add_argument('path_to_cut', nargs='*',
+ help='pattern to be cut from the result file path ')
+ parser.add_argument('-d','--demangle', action='store_true',
+ help='demangle function names')
+ parser.add_argument('-s', metavar='SYSROOT',
+ help='set path to sysroot for sanitized binaries')
+ parser.add_argument('-c', metavar='CROSS_COMPILE',
+ help='set prefix for binutils')
+ parser.add_argument('-l','--logfile', default=sys.stdin, type=argparse.FileType('r'),
+ help='set log file name to parse, default is stdin')
+ args = parser.parse_args()
+ if args.path_to_cut:
+ fix_filename_patterns = args.path_to_cut
+ if args.demangle:
+ demangle = True
+ if args.s:
+ binary_name_filter = sysroot_path_filter
+ sysroot_path = args.s
+ if args.c:
+ binutils_prefix = args.c
+ if args.logfile:
+ logfile = args.logfile
+ else:
+ logfile = sys.stdin
+ loop = SymbolizationLoop(binary_name_filter)
+ loop.process_logfile()
diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt
index c6a7041..7b36371 100644
--- a/lib/asan/tests/CMakeLists.txt
+++ b/lib/asan/tests/CMakeLists.txt
@@ -31,7 +31,7 @@
-O2
-Wno-format
-Werror=sign-compare)
-append_if(COMPILER_RT_HAS_WNO_VARIADIC_MACROS_FLAG -Wno-variadic-macros ASAN_UNITTEST_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WVARIADIC_MACROS_FLAG -Wno-variadic-macros ASAN_UNITTEST_COMMON_CFLAGS)
# -gline-tables-only must be enough for ASan, so use it if possible.
if(COMPILER_RT_TEST_COMPILER_ID MATCHES "Clang")
@@ -51,8 +51,10 @@
${ASAN_UNITTEST_COMMON_CFLAGS}
-fsanitize=address
"-fsanitize-blacklist=${ASAN_BLACKLIST_FILE}"
- -mllvm -asan-instrument-assembly
)
+if(CAN_TARGET_x86_64 OR CAN_TARGET_i386)
+ list(APPEND ASAN_UNITTEST_INSTRUMENTED_CFLAGS -mllvm -asan-instrument-assembly)
+endif()
if(NOT MSVC)
list(APPEND ASAN_UNITTEST_COMMON_LINKFLAGS --driver-mode=g++)
@@ -73,23 +75,29 @@
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()
+list(APPEND ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS -fsanitize=address)
set(ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS
${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}
-shared-libasan)
+set(ASAN_UNITTEST_INSTRUMENTED_LIBS)
+# NDK r10 requires -latomic almost always.
+append_list_if(ANDROID atomic ASAN_UNITTEST_INSTRUMENTED_LIBS)
+
set(ASAN_UNITTEST_NOINST_LINKFLAGS ${ASAN_UNITTEST_COMMON_LINKFLAGS})
-append_if(COMPILER_RT_HAS_LIBM -lm ASAN_UNITTEST_NOINST_LINKFLAGS)
-append_if(COMPILER_RT_HAS_LIBDL -ldl ASAN_UNITTEST_NOINST_LINKFLAGS)
-append_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ASAN_UNITTEST_NOINST_LINKFLAGS)
-append_if(COMPILER_RT_HAS_LIBPTHREAD -pthread
+append_list_if(COMPILER_RT_HAS_LIBM -lm ASAN_UNITTEST_NOINST_LINKFLAGS)
+append_list_if(COMPILER_RT_HAS_LIBDL -ldl ASAN_UNITTEST_NOINST_LINKFLAGS)
+append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ASAN_UNITTEST_NOINST_LINKFLAGS)
+append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread
ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS)
+# TODO(eugenis): move all -l flags above to _LIBS?
+set(ASAN_UNITTEST_NOINST_LIBS)
+append_list_if(ANDROID log ASAN_UNITTEST_NOINST_LIBS)
+# NDK r10 requires -latomic almost always.
+append_list_if(ANDROID atomic ASAN_UNITTEST_NOINST_LIBS)
+
# Compile source for the given architecture, using compiler
# options in ${ARGN}, and add it to the object list.
macro(asan_compile obj_list source arch kind)
@@ -198,7 +206,7 @@
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>)
- if(NOT MSVC)
+ if(NOT WIN32)
list(APPEND ASAN_TEST_RUNTIME_OBJECTS
$<TARGET_OBJECTS:RTLSanCommon.${arch}>)
endif()
@@ -233,7 +241,7 @@
endif()
endmacro()
-if(COMPILER_RT_CAN_EXECUTE_TESTS)
+if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID)
foreach(arch ${ASAN_SUPPORTED_ARCH})
add_asan_tests_for_arch_and_kind(${arch} "-inline")
add_asan_tests_for_arch_and_kind(${arch} "-with-calls"
@@ -242,31 +250,31 @@
endif()
if(ANDROID)
- # We assume that unit tests on Android are built in a build
- # tree with fresh Clang as a host compiler.
-
- # 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})
- target_link_libraries(AsanNoinstTest log)
+ foreach(arch ${ASAN_SUPPORTED_ARCH})
+ # Test w/o ASan instrumentation. Link it with ASan statically.
+ add_executable(AsanNoinstTest # FIXME: .arch?
+ $<TARGET_OBJECTS:RTAsan.${arch}>
+ $<TARGET_OBJECTS:RTInterception.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
+ ${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})
+ target_link_libraries(AsanNoinstTest ${ASAN_UNITTEST_NOINST_LIBS})
- # Test with ASan instrumentation. Link with ASan dynamic runtime.
- add_executable(AsanTest
- ${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)
+ # Test with ASan instrumentation. Link with ASan dynamic runtime.
+ add_executable(AsanTest
+ ${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 ${ASAN_UNITTEST_INSTRUMENTED_LIBS})
- # Setup correct output directory and link flags.
- set_target_properties(AsanNoinstTest AsanTest PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
- # Add unit test to test suite.
- add_dependencies(AsanUnitTests AsanNoinstTest AsanTest)
+ # Setup correct output directory and link flags.
+ set_target_properties(AsanNoinstTest AsanTest PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ # Add unit tests to the test suite.
+ add_dependencies(AsanUnitTests AsanNoinstTest AsanTest)
+ endforeach()
endif()
diff --git a/lib/asan/tests/asan_asm_test.cc b/lib/asan/tests/asan_asm_test.cc
index 86f806d..1d8b04d 100644
--- a/lib/asan/tests/asan_asm_test.cc
+++ b/lib/asan/tests/asan_asm_test.cc
@@ -22,6 +22,7 @@
template<typename T> void asm_write(T *ptr, T val);
template<typename T> T asm_read(T *ptr);
+template<typename T> void asm_rep_movs(T *dst, T *src, size_t n);
} // End of anonymous namespace
@@ -53,8 +54,17 @@
return res; \
}
+#define DECLARE_ASM_REP_MOVS(Type, Movs) \
+ template <> void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \
+ __asm__("rep " Movs " \n\t" \
+ : \
+ : "D"(dst), "S"(src), "c"(size) \
+ : "rsi", "rdi", "rcx", "memory"); \
+ }
+
DECLARE_ASM_WRITE(U8, "8", "movq", "r");
DECLARE_ASM_READ(U8, "8", "movq", "=r");
+DECLARE_ASM_REP_MOVS(U8, "movsq");
} // End of anonymous namespace
@@ -86,6 +96,14 @@
return res; \
}
+#define DECLARE_ASM_REP_MOVS(Type, Movs) \
+ template <> void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \
+ __asm__("rep " Movs " \n\t" \
+ : \
+ : "D"(dst), "S"(src), "c"(size) \
+ : "esi", "edi", "ecx", "memory"); \
+ }
+
} // End of anonymous namespace
#endif // defined(__i386__) && defined(__SSE2__)
@@ -104,6 +122,10 @@
DECLARE_ASM_READ(U4, "4", "movl", "=r");
DECLARE_ASM_READ(__m128i, "16", "movaps", "=x");
+DECLARE_ASM_REP_MOVS(U1, "movsb");
+DECLARE_ASM_REP_MOVS(U2, "movsw");
+DECLARE_ASM_REP_MOVS(U4, "movsl");
+
template<typename T> void TestAsmWrite(const char *DeathPattern) {
T *buf = new T;
EXPECT_DEATH(asm_write(&buf[1], static_cast<T>(0)), DeathPattern);
@@ -157,6 +179,28 @@
__asm__("movl %[r], (%[a]) \n\t" : : [a] "r" (a), [r] "r" (r) : "memory");
}
+template <typename T>
+void TestAsmRepMovs(const char *DeathPatternRead,
+ const char *DeathPatternWrite) {
+ T src_good[4] = { 0x0, 0x1, 0x2, 0x3 };
+ T dst_good[4] = {};
+ asm_rep_movs(dst_good, src_good, 4);
+ ASSERT_EQ(static_cast<T>(0x0), dst_good[0]);
+ ASSERT_EQ(static_cast<T>(0x1), dst_good[1]);
+ ASSERT_EQ(static_cast<T>(0x2), dst_good[2]);
+ ASSERT_EQ(static_cast<T>(0x3), dst_good[3]);
+
+ T dst_bad[3];
+ EXPECT_DEATH(asm_rep_movs(dst_bad, src_good, 4), DeathPatternWrite);
+
+ T src_bad[3] = { 0x0, 0x1, 0x2 };
+ EXPECT_DEATH(asm_rep_movs(dst_good, src_bad, 4), DeathPatternRead);
+
+ T* dp = dst_bad + 4;
+ T* sp = src_bad + 4;
+ asm_rep_movs(dp, sp, 0);
+}
+
} // End of anonymous namespace
TEST(AddressSanitizer, asm_load_store) {
@@ -209,6 +253,15 @@
ASSERT_EQ(0x1, r);
}
+TEST(AddressSanitizer, asm_rep_movs) {
+ TestAsmRepMovs<U1>("READ of size 1", "WRITE of size 1");
+ TestAsmRepMovs<U2>("READ of size 2", "WRITE of size 2");
+ TestAsmRepMovs<U4>("READ of size 4", "WRITE of size 4");
+#if defined(__x86_64__)
+ TestAsmRepMovs<U8>("READ of size 8", "WRITE of size 8");
+#endif // defined(__x86_64__)
+}
+
#endif // defined(__x86_64__) || (defined(__i386__) && defined(__SSE2__))
#endif // defined(__linux__)
diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc
index 7fae462..bb6af45 100644
--- a/lib/asan/tests/asan_noinst_test.cc
+++ b/lib/asan/tests/asan_noinst_test.cc
@@ -52,19 +52,19 @@
static void MallocStress(size_t n) {
u32 seed = my_rand();
- StackTrace stack1;
- stack1.trace[0] = 0xa123;
- stack1.trace[1] = 0xa456;
+ BufferedStackTrace stack1;
+ stack1.trace_buffer[0] = 0xa123;
+ stack1.trace_buffer[1] = 0xa456;
stack1.size = 2;
- StackTrace stack2;
- stack2.trace[0] = 0xb123;
- stack2.trace[1] = 0xb456;
+ BufferedStackTrace stack2;
+ stack2.trace_buffer[0] = 0xb123;
+ stack2.trace_buffer[1] = 0xb456;
stack2.size = 2;
- StackTrace stack3;
- stack3.trace[0] = 0xc123;
- stack3.trace[1] = 0xc456;
+ BufferedStackTrace stack3;
+ stack3.trace_buffer[0] = 0xc123;
+ stack3.trace_buffer[1] = 0xc456;
stack3.size = 2;
std::vector<void *> vec;
@@ -140,8 +140,8 @@
}
TEST(AddressSanitizer, QuarantineTest) {
- StackTrace stack;
- stack.trace[0] = 0x890;
+ BufferedStackTrace stack;
+ stack.trace_buffer[0] = 0x890;
stack.size = 1;
const int size = 1024;
@@ -161,8 +161,8 @@
void *ThreadedQuarantineTestWorker(void *unused) {
(void)unused;
u32 seed = my_rand();
- StackTrace stack;
- stack.trace[0] = 0x890;
+ BufferedStackTrace stack;
+ stack.trace_buffer[0] = 0x890;
stack.size = 1;
for (size_t i = 0; i < 1000; i++) {
@@ -188,8 +188,8 @@
void *ThreadedOneSizeMallocStress(void *unused) {
(void)unused;
- StackTrace stack;
- stack.trace[0] = 0x890;
+ BufferedStackTrace stack;
+ stack.trace_buffer[0] = 0x890;
stack.size = 1;
const size_t kNumMallocs = 1000;
for (int iter = 0; iter < 1000; iter++) {
@@ -241,8 +241,8 @@
uptr buggy_ptr;
__asan_test_only_reported_buggy_pointer = &buggy_ptr;
- StackTrace stack;
- stack.trace[0] = 0x890;
+ BufferedStackTrace stack;
+ stack.trace_buffer[0] = 0x890;
stack.size = 1;
for (uptr len = 16; len <= 32; len++) {
diff --git a/lib/asan/tests/asan_str_test.cc b/lib/asan/tests/asan_str_test.cc
index 3e96997..1cd2a08b 100644
--- a/lib/asan/tests/asan_str_test.cc
+++ b/lib/asan/tests/asan_str_test.cc
@@ -221,7 +221,8 @@
TEST(AddressSanitizer, StrChrAndIndexOOBTest) {
RunStrChrTest(&strchr);
-#if !defined(_WIN32) // no index() on Windows.
+// No index() on Windows and on Android L.
+#if !defined(_WIN32) && !defined(__ANDROID__)
RunStrChrTest(&index);
#endif
}
@@ -498,15 +499,6 @@
free(str);
}
-void CallAtoi(const char *nptr) {
- Ident(atoi(nptr));
-}
-void CallAtol(const char *nptr) {
- Ident(atol(nptr));
-}
-void CallAtoll(const char *nptr) {
- Ident(atoll(nptr));
-}
typedef void(*PointerToCallAtoi)(const char*);
void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
@@ -534,6 +526,15 @@
}
#if !defined(_WIN32) // FIXME: Fix and enable on Windows.
+void CallAtoi(const char *nptr) {
+ Ident(atoi(nptr));
+}
+void CallAtol(const char *nptr) {
+ Ident(atol(nptr));
+}
+void CallAtoll(const char *nptr) {
+ Ident(atoll(nptr));
+}
TEST(AddressSanitizer, AtoiAndFriendsOOBTest) {
RunAtoiOOBTest(&CallAtoi);
RunAtoiOOBTest(&CallAtol);
@@ -541,12 +542,6 @@
}
#endif
-void CallStrtol(const char *nptr, char **endptr, int base) {
- Ident(strtol(nptr, endptr, base));
-}
-void CallStrtoll(const char *nptr, char **endptr, int base) {
- Ident(strtoll(nptr, endptr, base));
-}
typedef void(*PointerToCallStrtol)(const char*, char**, int);
void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
@@ -590,6 +585,12 @@
}
#if !defined(_WIN32) // FIXME: Fix and enable on Windows.
+void CallStrtol(const char *nptr, char **endptr, int base) {
+ Ident(strtol(nptr, endptr, base));
+}
+void CallStrtoll(const char *nptr, char **endptr, int base) {
+ Ident(strtoll(nptr, endptr, base));
+}
TEST(AddressSanitizer, StrtollOOBTest) {
RunStrtolOOBTest(&CallStrtoll);
}
diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc
index 7760486..67bcbac 100644
--- a/lib/asan/tests/asan_test.cc
+++ b/lib/asan/tests/asan_test.cc
@@ -602,9 +602,9 @@
siglongjmp(buf, 1);
}
-#if !defined(__ANDROID__) && \
+#if !defined(__ANDROID__) && !defined(__arm__) && \
!defined(__powerpc64__) && !defined(__powerpc__)
-// Does not work on Power:
+// Does not work on Power and ARM:
// https://code.google.com/p/address-sanitizer/issues/detail?id=185
TEST(AddressSanitizer, BuiltinLongJmpTest) {
static jmp_buf buf;
@@ -615,7 +615,7 @@
}
}
#endif // !defined(__ANDROID__) && !defined(__powerpc64__) &&
- // !defined(__powerpc__)
+ // !defined(__powerpc__) && !defined(__arm__)
TEST(AddressSanitizer, UnderscopeLongJmpTest) {
static jmp_buf buf;
@@ -832,7 +832,7 @@
x[18]++;
x[19]++;
- delete x;
+ delete[] x;
return res;
}
diff --git a/lib/builtins/CMakeLists.txt b/lib/builtins/CMakeLists.txt
index bab3ce3..999faa8 100644
--- a/lib/builtins/CMakeLists.txt
+++ b/lib/builtins/CMakeLists.txt
@@ -163,6 +163,9 @@
i386/umoddi3.S
${GENERIC_SOURCES})
+set(i686_SOURCES
+ ${i386_SOURCES})
+
set(arm_SOURCES
arm/adddf3vfp.S
arm/addsf3vfp.S
@@ -250,14 +253,26 @@
add_custom_target(builtins)
if (NOT WIN32)
- foreach(arch x86_64 i386 arm)
- if(CAN_TARGET_${arch})
+ foreach (arch x86_64 i386 i686 arm)
+ if (CAN_TARGET_${arch})
+ # Filter out generic versions of routines that are re-implemented in
+ # architecture specific manner. This prevents multiple definitions of the
+ # same symbols, making the symbol selection non-deterministic.
+ foreach (_file ${${arch}_SOURCES})
+ if (${_file} MATCHES ${arch}/*)
+ get_filename_component(_name ${_file} NAME)
+ string(REPLACE ".S" ".c" _cname "${_name}")
+ list(REMOVE_ITEM ${arch}_SOURCES ${_cname})
+ endif ()
+ endforeach ()
+
+ set_source_files_properties(${${arch}_SOURCES} PROPERTIES LANGUAGE C)
add_compiler_rt_runtime(clang_rt.builtins-${arch} ${arch} STATIC
- SOURCES ${${arch}_SOURCES}
- CFLAGS "-std=c99")
+ SOURCES ${${arch}_SOURCES}
+ CFLAGS "-std=c99")
add_dependencies(builtins clang_rt.builtins-${arch})
- endif()
- endforeach()
-endif()
+ endif ()
+ endforeach ()
+endif ()
add_dependencies(compiler-rt builtins)
diff --git a/lib/builtins/addvdi3.c b/lib/builtins/addvdi3.c
index db45a27..0da3894 100644
--- a/lib/builtins/addvdi3.c
+++ b/lib/builtins/addvdi3.c
@@ -21,7 +21,7 @@
COMPILER_RT_ABI di_int
__addvdi3(di_int a, di_int b)
{
- di_int s = a + b;
+ di_int s = (du_int) a + (du_int) b;
if (b >= 0)
{
if (s < a)
diff --git a/lib/builtins/addvsi3.c b/lib/builtins/addvsi3.c
index 81f515c..94ca726 100644
--- a/lib/builtins/addvsi3.c
+++ b/lib/builtins/addvsi3.c
@@ -21,7 +21,7 @@
COMPILER_RT_ABI si_int
__addvsi3(si_int a, si_int b)
{
- si_int s = a + b;
+ si_int s = (su_int) a + (su_int) b;
if (b >= 0)
{
if (s < a)
diff --git a/lib/builtins/addvti3.c b/lib/builtins/addvti3.c
index 79b9611..c224de6 100644
--- a/lib/builtins/addvti3.c
+++ b/lib/builtins/addvti3.c
@@ -23,7 +23,7 @@
COMPILER_RT_ABI ti_int
__addvti3(ti_int a, ti_int b)
{
- ti_int s = a + b;
+ ti_int s = (tu_int) a + (tu_int) b;
if (b >= 0)
{
if (s < a)
diff --git a/lib/builtins/arm/bswapdi2.S b/lib/builtins/arm/bswapdi2.S
index c2e2ce9..86f3bba 100644
--- a/lib/builtins/arm/bswapdi2.S
+++ b/lib/builtins/arm/bswapdi2.S
@@ -21,7 +21,11 @@
// Reverse all the bytes in a 64-bit integer.
//
.p2align 2
+#if __ARM_ARCH_ISA_THUMB == 2
+DEFINE_COMPILERRT_THUMB_FUNCTION(__bswapdi2)
+#else
DEFINE_COMPILERRT_FUNCTION(__bswapdi2)
+#endif
#if __ARM_ARCH < 6
// before armv6 does not have "rev" instruction
// r2 = rev(r0)
diff --git a/lib/builtins/arm/bswapsi2.S b/lib/builtins/arm/bswapsi2.S
index ad09655..59ba815 100644
--- a/lib/builtins/arm/bswapsi2.S
+++ b/lib/builtins/arm/bswapsi2.S
@@ -21,7 +21,11 @@
// Reverse all the bytes in a 32-bit integer.
//
.p2align 2
+#if __ARM_ARCH_ISA_THUMB == 2
+DEFINE_COMPILERRT_THUMB_FUNCTION(__bswapsi2)
+#else
DEFINE_COMPILERRT_FUNCTION(__bswapsi2)
+#endif
#if __ARM_ARCH < 6
// before armv6 does not have "rev" instruction
eor r1, r0, r0, ror #16
diff --git a/lib/builtins/arm/clzdi2.S b/lib/builtins/arm/clzdi2.S
index bcea485..a55abac 100644
--- a/lib/builtins/arm/clzdi2.S
+++ b/lib/builtins/arm/clzdi2.S
@@ -21,7 +21,11 @@
.p2align 2
+#if __ARM_ARCH_ISA_THUMB == 2
+DEFINE_COMPILERRT_THUMB_FUNCTION(__clzdi2)
+#else
DEFINE_COMPILERRT_FUNCTION(__clzdi2)
+#endif
#ifdef __ARM_FEATURE_CLZ
#ifdef __ARMEB__
cmp r0, 0
diff --git a/lib/builtins/arm/clzsi2.S b/lib/builtins/arm/clzsi2.S
index f0240b0..1cd379b 100644
--- a/lib/builtins/arm/clzsi2.S
+++ b/lib/builtins/arm/clzsi2.S
@@ -20,7 +20,11 @@
#endif
.p2align 2
+#if __ARM_ARCH_ISA_THUMB == 2
+DEFINE_COMPILERRT_THUMB_FUNCTION(__clzsi2)
+#else
DEFINE_COMPILERRT_FUNCTION(__clzsi2)
+#endif
#ifdef __ARM_FEATURE_CLZ
clz r0, r0
JMP(lr)
diff --git a/lib/builtins/arm/divmodsi4.S b/lib/builtins/arm/divmodsi4.S
index 91bb2a5..646b9ab 100644
--- a/lib/builtins/arm/divmodsi4.S
+++ b/lib/builtins/arm/divmodsi4.S
@@ -27,8 +27,16 @@
.thumb
#endif
+@ int __divmodsi4(int divident, int divisor, int *remainder)
+@ Calculate the quotient and remainder of the (signed) division. The return
+@ value is the quotient, the remainder is placed in the variable.
+
.p2align 3
+#if __ARM_ARCH_ISA_THUMB == 2
+DEFINE_COMPILERRT_THUMB_FUNCTION(__divmodsi4)
+#else
DEFINE_COMPILERRT_FUNCTION(__divmodsi4)
+#endif
#if __ARM_ARCH_EXT_IDIV__
tst r1, r1
beq LOCAL_LABEL(divzero)
diff --git a/lib/builtins/arm/divsi3.S b/lib/builtins/arm/divsi3.S
index db47165..adf8f94 100644
--- a/lib/builtins/arm/divsi3.S
+++ b/lib/builtins/arm/divsi3.S
@@ -29,7 +29,15 @@
.p2align 3
// 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)
+
+@ int __divsi3(int divident, int divisor)
+@ Calculate and return the quotient of the (signed) division.
+
+#if __ARM_ARCH_ISA_THUMB == 2
+DEFINE_COMPILERRT_THUMB_FUNCTION(__divsi3)
+#else
DEFINE_COMPILERRT_FUNCTION(__divsi3)
+#endif
#if __ARM_ARCH_EXT_IDIV__
tst r1,r1
beq LOCAL_LABEL(divzero)
diff --git a/lib/builtins/arm/modsi3.S b/lib/builtins/arm/modsi3.S
index 7ed305e..295a227 100644
--- a/lib/builtins/arm/modsi3.S
+++ b/lib/builtins/arm/modsi3.S
@@ -26,8 +26,15 @@
.thumb
#endif
+@ int __modsi3(int divident, int divisor)
+@ Calculate and return the remainder of the (signed) division.
+
.p2align 3
+#if __ARM_ARCH_ISA_THUMB == 2
+DEFINE_COMPILERRT_THUMB_FUNCTION(__modsi3)
+#else
DEFINE_COMPILERRT_FUNCTION(__modsi3)
+#endif
#if __ARM_ARCH_EXT_IDIV__
tst r1, r1
beq LOCAL_LABEL(divzero)
diff --git a/lib/builtins/arm/sync-ops.h b/lib/builtins/arm/sync-ops.h
index 87cc3c2..ee02c30 100644
--- a/lib/builtins/arm/sync-ops.h
+++ b/lib/builtins/arm/sync-ops.h
@@ -18,21 +18,24 @@
#define SYNC_OP_4(op) \
.p2align 2 ; \
.thumb ; \
- DEFINE_COMPILERRT_FUNCTION(__sync_fetch_and_ ## op) \
+ .syntax unified ; \
+ DEFINE_COMPILERRT_THUMB_FUNCTION(__sync_fetch_and_ ## op) \
dmb ; \
mov r12, r0 ; \
LOCAL_LABEL(tryatomic_ ## op): \
ldrex r0, [r12] ; \
op(r2, r0, r1) ; \
strex r3, r2, [r12] ; \
- cbnz r3, LOCAL_LABEL(tryatomic_ ## op) ; \
+ cmp r3, #0 ; \
+ bne LOCAL_LABEL(tryatomic_ ## op) ; \
dmb ; \
bx lr
#define SYNC_OP_8(op) \
.p2align 2 ; \
.thumb ; \
- DEFINE_COMPILERRT_FUNCTION(__sync_fetch_and_ ## op) \
+ .syntax unified ; \
+ DEFINE_COMPILERRT_THUMB_FUNCTION(__sync_fetch_and_ ## op) \
push {r4, r5, r6, lr} ; \
dmb ; \
mov r12, r0 ; \
@@ -40,7 +43,8 @@
ldrexd r0, r1, [r12] ; \
op(r4, r5, r0, r1, r2, r3) ; \
strexd r6, r4, r5, [r12] ; \
- cbnz r6, LOCAL_LABEL(tryatomic_ ## op) ; \
+ cmp r6, #0 ; \
+ bne LOCAL_LABEL(tryatomic_ ## op) ; \
dmb ; \
pop {r4, r5, r6, pc}
diff --git a/lib/builtins/arm/sync_fetch_and_add_8.S b/lib/builtins/arm/sync_fetch_and_add_8.S
index db59e05..5724bb1 100644
--- a/lib/builtins/arm/sync_fetch_and_add_8.S
+++ b/lib/builtins/arm/sync_fetch_and_add_8.S
@@ -14,9 +14,11 @@
#include "sync-ops.h"
+#if __ARM_ARCH_PROFILE != 'M'
#define add_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI) \
adds rD_LO, rN_LO, rM_LO ; \
adc rD_HI, rN_HI, rM_HI
SYNC_OP_8(add_8)
+#endif
diff --git a/lib/builtins/arm/sync_fetch_and_and_8.S b/lib/builtins/arm/sync_fetch_and_and_8.S
index 845c74f..a74163a 100644
--- a/lib/builtins/arm/sync_fetch_and_and_8.S
+++ b/lib/builtins/arm/sync_fetch_and_and_8.S
@@ -14,8 +14,10 @@
#include "sync-ops.h"
+#if __ARM_ARCH_PROFILE != 'M'
#define and_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI) \
and rD_LO, rN_LO, rM_LO ; \
and rD_HI, rN_HI, rM_HI
SYNC_OP_8(and_8)
+#endif
diff --git a/lib/builtins/arm/sync_fetch_and_max_8.S b/lib/builtins/arm/sync_fetch_and_max_8.S
index 12dd46c..1eef2b2 100644
--- a/lib/builtins/arm/sync_fetch_and_max_8.S
+++ b/lib/builtins/arm/sync_fetch_and_max_8.S
@@ -14,6 +14,8 @@
#include "sync-ops.h"
+#if __ARM_ARCH_PROFILE != 'M'
#define max_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI) MINMAX_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI, gt)
SYNC_OP_8(max_8)
+#endif
diff --git a/lib/builtins/arm/sync_fetch_and_min_8.S b/lib/builtins/arm/sync_fetch_and_min_8.S
index 97af2aa..ad5cce0 100644
--- a/lib/builtins/arm/sync_fetch_and_min_8.S
+++ b/lib/builtins/arm/sync_fetch_and_min_8.S
@@ -14,6 +14,8 @@
#include "sync-ops.h"
+#if __ARM_ARCH_PROFILE != 'M'
#define min_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI) MINMAX_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI, lt)
SYNC_OP_8(min_8)
+#endif
diff --git a/lib/builtins/arm/sync_fetch_and_nand_8.S b/lib/builtins/arm/sync_fetch_and_nand_8.S
index 80e8c00..a2c17c0 100644
--- a/lib/builtins/arm/sync_fetch_and_nand_8.S
+++ b/lib/builtins/arm/sync_fetch_and_nand_8.S
@@ -14,9 +14,11 @@
#include "sync-ops.h"
+#if __ARM_ARCH_PROFILE != 'M'
#define nand_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI) \
bic rD_LO, rN_LO, rM_LO ; \
bic rD_HI, rN_HI, rM_HI
SYNC_OP_8(nand_8)
+#endif
diff --git a/lib/builtins/arm/sync_fetch_and_or_8.S b/lib/builtins/arm/sync_fetch_and_or_8.S
index 0f77f02..87b940b 100644
--- a/lib/builtins/arm/sync_fetch_and_or_8.S
+++ b/lib/builtins/arm/sync_fetch_and_or_8.S
@@ -14,9 +14,11 @@
#include "sync-ops.h"
+#if __ARM_ARCH_PROFILE != 'M'
#define or_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI) \
orr rD_LO, rN_LO, rM_LO ; \
orr rD_HI, rN_HI, rM_HI
SYNC_OP_8(or_8)
+#endif
diff --git a/lib/builtins/arm/sync_fetch_and_sub_8.S b/lib/builtins/arm/sync_fetch_and_sub_8.S
index f4e1c4a..a8035a2 100644
--- a/lib/builtins/arm/sync_fetch_and_sub_8.S
+++ b/lib/builtins/arm/sync_fetch_and_sub_8.S
@@ -14,9 +14,11 @@
#include "sync-ops.h"
+#if __ARM_ARCH_PROFILE != 'M'
#define sub_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI) \
subs rD_LO, rN_LO, rM_LO ; \
sbc rD_HI, rN_HI, rM_HI
SYNC_OP_8(sub_8)
+#endif
diff --git a/lib/builtins/arm/sync_fetch_and_umax_8.S b/lib/builtins/arm/sync_fetch_and_umax_8.S
index 7716cfe..d9b7965 100644
--- a/lib/builtins/arm/sync_fetch_and_umax_8.S
+++ b/lib/builtins/arm/sync_fetch_and_umax_8.S
@@ -14,6 +14,8 @@
#include "sync-ops.h"
+#if __ARM_ARCH_PROFILE != 'M'
#define umax_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI) MINMAX_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI, hi)
SYNC_OP_8(umax_8)
+#endif
diff --git a/lib/builtins/arm/sync_fetch_and_umin_8.S b/lib/builtins/arm/sync_fetch_and_umin_8.S
index b099726..7bf5e23 100644
--- a/lib/builtins/arm/sync_fetch_and_umin_8.S
+++ b/lib/builtins/arm/sync_fetch_and_umin_8.S
@@ -14,6 +14,8 @@
#include "sync-ops.h"
+#if __ARM_ARCH_PROFILE != 'M'
#define umin_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI) MINMAX_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI, lo)
SYNC_OP_8(umin_8)
+#endif
diff --git a/lib/builtins/arm/sync_fetch_and_xor_8.S b/lib/builtins/arm/sync_fetch_and_xor_8.S
index 91b6a4c..ea9aa6d 100644
--- a/lib/builtins/arm/sync_fetch_and_xor_8.S
+++ b/lib/builtins/arm/sync_fetch_and_xor_8.S
@@ -14,9 +14,11 @@
#include "sync-ops.h"
+#if __ARM_ARCH_PROFILE != 'M'
#define xor_8(rD_LO, rD_HI, rN_LO, rN_HI, rM_LO, rM_HI) \
eor rD_LO, rN_LO, rM_LO ; \
eor rD_HI, rN_HI, rM_HI
SYNC_OP_8(xor_8)
+#endif
diff --git a/lib/builtins/arm/udivmodsi4.S b/lib/builtins/arm/udivmodsi4.S
index ddc8752..85b8493 100644
--- a/lib/builtins/arm/udivmodsi4.S
+++ b/lib/builtins/arm/udivmodsi4.S
@@ -17,8 +17,21 @@
.syntax unified
.text
+#if __ARM_ARCH_ISA_THUMB == 2
+ .thumb
+#endif
+
+@ unsigned int __udivmodsi4(unsigned int divident, unsigned int divisor,
+@ unsigned int *remainder)
+@ Calculate the quotient and remainder of the (unsigned) division. The return
+@ value is the quotient, the remainder is placed in the variable.
+
.p2align 2
+#if __ARM_ARCH_ISA_THUMB == 2
+DEFINE_COMPILERRT_THUMB_FUNCTION(__udivmodsi4)
+#else
DEFINE_COMPILERRT_FUNCTION(__udivmodsi4)
+#endif
#if __ARM_ARCH_EXT_IDIV__
tst r1, r1
beq LOCAL_LABEL(divby0)
@@ -42,6 +55,7 @@
* r0 and (r1 << I) have the highest bit set in the same position.
* At the time of JMP, ip := .Ldiv0block - 12 * I.
* This depends on the fixed instruction size of block.
+ * For ARM mode, this is 12 Bytes, for THUMB mode 14 Bytes.
*
* block(shift) implements the test-and-update-quotient core.
* It assumes (r0 << shift) can be computed without overflow and
@@ -53,12 +67,20 @@
clz r3, r1
/* r0 >= r1 implies clz(r0) <= clz(r1), so ip <= r3. */
sub r3, r3, ip
+# if __ARM_ARCH_ISA_THUMB == 2
+ adr ip, LOCAL_LABEL(div0block) + 1
+ sub ip, ip, r3, lsl #1
+# else
adr ip, LOCAL_LABEL(div0block)
+# endif
sub ip, ip, r3, lsl #2
sub ip, ip, r3, lsl #3
mov r3, #0
bx ip
# else
+# if __ARM_ARCH_ISA_THUMB == 2
+# error THUMB mode requires CLZ or UDIV
+# endif
str r4, [sp, #-8]!
mov r4, r0
@@ -96,10 +118,11 @@
#define IMM #
-#define block(shift) \
- cmp r0, r1, lsl IMM shift; \
- addhs r3, r3, IMM (1 << shift); \
- subhs r0, r0, r1, lsl IMM shift
+#define block(shift) \
+ cmp r0, r1, lsl IMM shift; \
+ ITT(hs); \
+ WIDE(addhs) r3, r3, IMM (1 << shift); \
+ WIDE(subhs) r0, r0, r1, lsl IMM shift
block(31)
block(30)
diff --git a/lib/builtins/arm/udivsi3.S b/lib/builtins/arm/udivsi3.S
index 8fb1dca..165b2b5 100644
--- a/lib/builtins/arm/udivsi3.S
+++ b/lib/builtins/arm/udivsi3.S
@@ -17,21 +17,33 @@
.syntax unified
.text
+#if __ARM_ARCH_ISA_THUMB == 2
+ .thumb
+#endif
+
.p2align 2
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_uidiv, __udivsi3)
+
+@ unsigned int __udivsi3(unsigned int divident, unsigned int divisor)
+@ Calculate and return the quotient of the (unsigned) division.
+
+#if __ARM_ARCH_ISA_THUMB == 2
+DEFINE_COMPILERRT_THUMB_FUNCTION(__udivsi3)
+#else
DEFINE_COMPILERRT_FUNCTION(__udivsi3)
+#endif
#if __ARM_ARCH_EXT_IDIV__
tst r1, r1
beq LOCAL_LABEL(divby0)
- mov r3, r0
- udiv r0, r3, r1
- mls r1, r0, r1, r3
+ udiv r0, r0, r1
bx lr
#else
cmp r1, #1
bcc LOCAL_LABEL(divby0)
+ IT(eq)
JMPc(lr, eq)
cmp r0, r1
+ ITT(cc)
movcc r0, #0
JMPc(lr, cc)
/*
@@ -43,6 +55,7 @@
* r0 and (r1 << I) have the highest bit set in the same position.
* At the time of JMP, ip := .Ldiv0block - 12 * I.
* This depends on the fixed instruction size of block.
+ * For ARM mode, this is 12 Bytes, for THUMB mode 14 Bytes.
*
* block(shift) implements the test-and-update-quotient core.
* It assumes (r0 << shift) can be computed without overflow and
@@ -54,12 +67,20 @@
clz r3, r1
/* r0 >= r1 implies clz(r0) <= clz(r1), so ip <= r3. */
sub r3, r3, ip
+# if __ARM_ARCH_ISA_THUMB == 2
+ adr ip, LOCAL_LABEL(div0block) + 1
+ sub ip, ip, r3, lsl #1
+# else
adr ip, LOCAL_LABEL(div0block)
+# endif
sub ip, ip, r3, lsl #2
sub ip, ip, r3, lsl #3
mov r3, #0
bx ip
# else
+# if __ARM_ARCH_ISA_THUMB == 2
+# error THUMB mode requires CLZ or UDIV
+# endif
mov r2, r0
adr ip, LOCAL_LABEL(div0block)
@@ -94,10 +115,11 @@
#define IMM #
-#define block(shift) \
- cmp r0, r1, lsl IMM shift; \
- addhs r3, r3, IMM (1 << shift); \
- subhs r0, r0, r1, lsl IMM shift
+#define block(shift) \
+ cmp r0, r1, lsl IMM shift; \
+ ITT(hs); \
+ WIDE(addhs) r3, r3, IMM (1 << shift); \
+ WIDE(subhs) r0, r0, r1, lsl IMM shift
block(31)
block(30)
diff --git a/lib/builtins/arm/umodsi3.S b/lib/builtins/arm/umodsi3.S
index 164646b..9e7a148 100644
--- a/lib/builtins/arm/umodsi3.S
+++ b/lib/builtins/arm/umodsi3.S
@@ -16,23 +16,33 @@
.syntax unified
.text
+#if __ARM_ARCH_ISA_THUMB == 2
+ .thumb
+#endif
+
+@ unsigned int __umodsi3(unsigned int divident, unsigned int divisor)
+@ Calculate and return the remainder of the (unsigned) division.
.p2align 2
+#if __ARM_ARCH_ISA_THUMB == 2
+DEFINE_COMPILERRT_THUMB_FUNCTION(__umodsi3)
+#else
DEFINE_COMPILERRT_FUNCTION(__umodsi3)
+#endif
#if __ARM_ARCH_EXT_IDIV__
tst r1, r1
beq LOCAL_LABEL(divby0)
- mov r3, r0
- udiv r0, r3, r1
- mls r1, r0, r1, r3
- str r1, [r2]
+ udiv r2, r0, r1
+ mls r0, r2, r1, r0
bx lr
#else
cmp r1, #1
bcc LOCAL_LABEL(divby0)
+ ITT(eq)
moveq r0, #0
JMPc(lr, eq)
cmp r0, r1
+ IT(cc)
JMPc(lr, cc)
/*
* Implement division using binary long division algorithm.
@@ -43,6 +53,7 @@
* r0 and (r1 << I) have the highest bit set in the same position.
* At the time of JMP, ip := .Ldiv0block - 8 * I.
* This depends on the fixed instruction size of block.
+ * For ARM mode, this is 8 Bytes, for THUMB mode 10 Bytes.
*
* block(shift) implements the test-and-update-quotient core.
* It assumes (r0 << shift) can be computed without overflow and
@@ -54,10 +65,18 @@
clz r3, r1
/* r0 >= r1 implies clz(r0) <= clz(r1), so ip <= r3. */
sub r3, r3, ip
+# if __ARM_ARCH_ISA_THUMB == 2
+ adr ip, LOCAL_LABEL(div0block) + 1
+ sub ip, ip, r3, lsl #1
+# else
adr ip, LOCAL_LABEL(div0block)
+# endif
sub ip, ip, r3, lsl #3
bx ip
# else
+# if __ARM_ARCH_ISA_THUMB == 2
+# error THUMB mode requires CLZ or UDIV
+# endif
mov r2, r0
adr ip, LOCAL_LABEL(div0block)
@@ -90,9 +109,10 @@
#define IMM #
-#define block(shift) \
- cmp r0, r1, lsl IMM shift; \
- subhs r0, r0, r1, lsl IMM shift
+#define block(shift) \
+ cmp r0, r1, lsl IMM shift; \
+ IT(hs); \
+ WIDE(subhs) r0, r0, r1, lsl IMM shift
block(31)
block(30)
diff --git a/lib/builtins/assembly.h b/lib/builtins/assembly.h
index d415a5f..8688a9b 100644
--- a/lib/builtins/assembly.h
+++ b/lib/builtins/assembly.h
@@ -28,7 +28,9 @@
// tell linker it can break up file at label boundaries
#define FILE_LEVEL_DIRECTIVE .subsections_via_symbols
#define SYMBOL_IS_FUNC(name)
+
#elif defined(__ELF__)
+
#define HIDDEN(name) .hidden name
#define LOCAL_LABEL(name) .L_##name
#define FILE_LEVEL_DIRECTIVE
@@ -37,45 +39,21 @@
#else
#define SYMBOL_IS_FUNC(name) .type name,@function
#endif
-#else
+
+#else // !__APPLE__ && !__ELF__
+
#define HIDDEN_DIRECTIVE(name)
#define LOCAL_LABEL(name) .L ## name
+#define FILE_LEVEL_DIRECTIVE
#define SYMBOL_IS_FUNC(name) \
.def name SEPARATOR \
.scl 2 SEPARATOR \
.type 32 SEPARATOR \
.endef
-#define FILE_LEVEL_DIRECTIVE
+
#endif
#if defined(__arm__)
-#ifndef __ARM_ARCH
-#if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || \
- defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || \
- defined(__ARM_ARCH_7EM__)
-#define __ARM_ARCH 7
-#endif
-#endif
-
-#ifndef __ARM_ARCH
-#if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \
- defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \
- defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6ZM__)
-#define __ARM_ARCH 6
-#endif
-#endif
-
-#ifndef __ARM_ARCH
-#if defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) || \
- defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_5TEJ__)
-#define __ARM_ARCH 5
-#endif
-#endif
-
-#ifndef __ARM_ARCH
-#define __ARM_ARCH 4
-#endif
-
#if defined(__ARM_ARCH_4T__) || __ARM_ARCH >= 5
#define ARM_HAS_BX
#endif
@@ -91,6 +69,20 @@
#define JMP(r) mov pc, r
#define JMPc(r, c) mov##c pc, r
#endif
+
+#if __ARM_ARCH_ISA_THUMB == 2
+#define IT(cond) it cond
+#define ITT(cond) itt cond
+#else
+#define IT(cond)
+#define ITT(cond)
+#endif
+
+#if __ARM_ARCH_ISA_THUMB == 2
+#define WIDE(op) op.w
+#else
+#define WIDE(op) op
+#endif
#endif
#define GLUE2(a, b) a##b
@@ -111,6 +103,14 @@
DECLARE_SYMBOL_VISIBILITY(name) \
SYMBOL_NAME(name):
+#define DEFINE_COMPILERRT_THUMB_FUNCTION(name) \
+ FILE_LEVEL_DIRECTIVE SEPARATOR \
+ .globl SYMBOL_NAME(name) SEPARATOR \
+ SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \
+ DECLARE_SYMBOL_VISIBILITY(name) SEPARATOR \
+ .thumb_func SEPARATOR \
+ SYMBOL_NAME(name):
+
#define DEFINE_COMPILERRT_PRIVATE_FUNCTION(name) \
FILE_LEVEL_DIRECTIVE SEPARATOR \
.globl SYMBOL_NAME(name) SEPARATOR \
diff --git a/lib/builtins/clear_cache.c b/lib/builtins/clear_cache.c
index 66f9fcc..86e68af 100644
--- a/lib/builtins/clear_cache.c
+++ b/lib/builtins/clear_cache.c
@@ -32,9 +32,7 @@
* specified range.
*/
-COMPILER_RT_EXPORT void
-__clear_cache(void* start, void* end)
-{
+void __clear_cache(void *start, void *end) {
#if __i386__ || __x86_64__
/*
* Intel processors have a unified instruction and data cache
diff --git a/lib/builtins/i386/floatdidf.S b/lib/builtins/i386/floatdidf.S
index 5c45ee9..f4f5d01 100644
--- a/lib/builtins/i386/floatdidf.S
+++ b/lib/builtins/i386/floatdidf.S
@@ -7,12 +7,21 @@
#ifdef __i386__
-#ifndef __ELF__
-.const
+#if defined(__APPLE__)
+ .const
+#elif defined(__ELF__)
+ .section .rodata
+#else
+ .section .rdata,"rd"
#endif
-.balign 4
-twop52: .quad 0x4330000000000000
-twop32: .quad 0x41f0000000000000
+
+ .balign 16
+twop52:
+ .quad 0x4330000000000000
+
+ .balign 16
+twop32:
+ .quad 0x41f0000000000000
#define REL_ADDR(_a) (_a)-0b(%eax)
diff --git a/lib/builtins/i386/floatundidf.S b/lib/builtins/i386/floatundidf.S
index b006278..676fed0 100644
--- a/lib/builtins/i386/floatundidf.S
+++ b/lib/builtins/i386/floatundidf.S
@@ -17,14 +17,25 @@
#ifdef __i386__
-#ifndef __ELF__
-.const
+#if defined(__APPLE__)
+ .const
+#elif defined(__ELF__)
+ .section .rodata
+#else
+ .section .rdata,"rd"
#endif
-.balign 4
-twop52: .quad 0x4330000000000000
+
+ .balign 16
+twop52:
+ .quad 0x4330000000000000
+
+ .balign 16
twop84_plus_twop52:
- .quad 0x4530000000100000
-twop84: .quad 0x4530000000000000
+ .quad 0x4530000000100000
+
+ .balign 16
+twop84:
+ .quad 0x4530000000000000
#define REL_ADDR(_a) (_a)-0b(%eax)
diff --git a/lib/builtins/i386/floatundisf.S b/lib/builtins/i386/floatundisf.S
index 8984627..5b81620 100644
--- a/lib/builtins/i386/floatundisf.S
+++ b/lib/builtins/i386/floatundisf.S
@@ -52,15 +52,27 @@
#ifdef __i386__
-#ifndef __ELF__
-.const
+#if defined(__APPLE__)
+ .const
+#elif defined(__ELF__)
+ .section .rodata
+#else
+ .section .rdata,"rd"
#endif
-.balign 8
-twop52: .quad 0x4330000000000000
- .quad 0x0000000000000fff
-sticky: .quad 0x0000000000000000
- .long 0x00000012
-twelve: .long 0x00000000
+
+ .balign 16
+twop52:
+ .quad 0x4330000000000000
+ .quad 0x0000000000000fff
+
+ .balign 16
+sticky:
+ .quad 0x0000000000000000
+ .long 0x00000012
+
+ .balign 16
+twelve:
+ .long 0x00000000
#define TWOp52 twop52-0b(%ecx)
#define STICKY sticky-0b(%ecx,%eax,8)
diff --git a/lib/builtins/i386/floatundixf.S b/lib/builtins/i386/floatundixf.S
index 9d2f31f..d60ad7d 100644
--- a/lib/builtins/i386/floatundixf.S
+++ b/lib/builtins/i386/floatundixf.S
@@ -7,14 +7,25 @@
#ifdef __i386__
-#ifndef __ELF__
-.const
+#if defined(__APPLE__)
+ .const
+#elif defined(__ELF__)
+ .section .rodata
+#else
+ .section .rdata,"rd"
#endif
-.balign 4
-twop52: .quad 0x4330000000000000
+
+ .balign 16
+twop52:
+ .quad 0x4330000000000000
+
+ .balign 16
twop84_plus_twop52_neg:
- .quad 0xc530000000100000
-twop84: .quad 0x4530000000000000
+ .quad 0xc530000000100000
+
+ .balign 16
+twop84:
+ .quad 0x4530000000000000
#define REL_ADDR(_a) (_a)-0b(%eax)
diff --git a/lib/builtins/int_endianness.h b/lib/builtins/int_endianness.h
index c465a98..4b35bde 100644
--- a/lib/builtins/int_endianness.h
+++ b/lib/builtins/int_endianness.h
@@ -33,7 +33,8 @@
/* .. */
-#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__minix)
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
+ defined(__minix)
#include <sys/endian.h>
#if _BYTE_ORDER == _BIG_ENDIAN
@@ -61,7 +62,8 @@
/* .. */
-/* Mac OSX has __BIG_ENDIAN__ or __LITTLE_ENDIAN__ automatically set by the compiler (at least with GCC) */
+/* Mac OSX has __BIG_ENDIAN__ or __LITTLE_ENDIAN__ automatically set by the
+ * compiler (at least with GCC) */
#if defined(__APPLE__) || defined(__ellcc__ )
#ifdef __BIG_ENDIAN__
@@ -83,15 +85,14 @@
/* .. */
#if defined(__linux__)
-#include <endian.h>
-#if __BYTE_ORDER == __BIG_ENDIAN
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define _YUGA_LITTLE_ENDIAN 0
#define _YUGA_BIG_ENDIAN 1
-#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define _YUGA_LITTLE_ENDIAN 1
#define _YUGA_BIG_ENDIAN 0
-#endif /* __BYTE_ORDER */
+#endif /* __BYTE_ORDER__ */
#endif /* GNU/Linux */
diff --git a/lib/builtins/int_lib.h b/lib/builtins/int_lib.h
index 75ab975..ff314da 100644
--- a/lib/builtins/int_lib.h
+++ b/lib/builtins/int_lib.h
@@ -22,19 +22,13 @@
/* ABI macro definitions */
-/*
- * TODO define this appropriately for targets that require explicit export
- * declarations (i.e. Windows)
- */
-#define COMPILER_RT_EXPORT
-
#if __ARM_EABI__
# define ARM_EABI_FNALIAS(aeabi_name, name) \
void __aeabi_##aeabi_name() __attribute__((alias("__" #name)));
-# define COMPILER_RT_ABI COMPILER_RT_EXPORT __attribute__((pcs("aapcs")))
+# define COMPILER_RT_ABI __attribute__((pcs("aapcs")))
#else
# define ARM_EABI_FNALIAS(aeabi_name, name)
-# define COMPILER_RT_ABI COMPILER_RT_EXPORT
+# define COMPILER_RT_ABI
#endif
#if defined(__NetBSD__) && (defined(_KERNEL) || defined(_STANDALONE))
diff --git a/lib/builtins/subvdi3.c b/lib/builtins/subvdi3.c
index 0f1f924..71fc70f 100644
--- a/lib/builtins/subvdi3.c
+++ b/lib/builtins/subvdi3.c
@@ -21,7 +21,7 @@
COMPILER_RT_ABI di_int
__subvdi3(di_int a, di_int b)
{
- di_int s = a - b;
+ di_int s = (du_int) a - (du_int) b;
if (b >= 0)
{
if (s > a)
diff --git a/lib/builtins/subvsi3.c b/lib/builtins/subvsi3.c
index ec4594c..e6c0fb6 100644
--- a/lib/builtins/subvsi3.c
+++ b/lib/builtins/subvsi3.c
@@ -21,7 +21,7 @@
COMPILER_RT_ABI si_int
__subvsi3(si_int a, si_int b)
{
- si_int s = a - b;
+ si_int s = (su_int) a - (su_int) b;
if (b >= 0)
{
if (s > a)
diff --git a/lib/builtins/subvti3.c b/lib/builtins/subvti3.c
index 8f11714..a6804d2 100644
--- a/lib/builtins/subvti3.c
+++ b/lib/builtins/subvti3.c
@@ -23,7 +23,7 @@
COMPILER_RT_ABI ti_int
__subvti3(ti_int a, ti_int b)
{
- ti_int s = a - b;
+ ti_int s = (tu_int) a - (tu_int) b;
if (b >= 0)
{
if (s > a)
diff --git a/lib/builtins/x86_64/floatundidf.S b/lib/builtins/x86_64/floatundidf.S
index 7c94231..d54b974 100644
--- a/lib/builtins/x86_64/floatundidf.S
+++ b/lib/builtins/x86_64/floatundidf.S
@@ -20,15 +20,20 @@
#if defined(__APPLE__)
.const
#elif defined(__ELF__)
- .rodata
+ .section .rodata
#else
.section .rdata,"rd"
#endif
- .balign 4
+
+ .balign 16
twop52:
.quad 0x4330000000000000
+
+ .balign 16
twop84_plus_twop52:
.quad 0x4530000000100000
+
+ .balign 16
twop84:
.quad 0x4530000000000000
diff --git a/lib/builtins/x86_64/floatundisf.S b/lib/builtins/x86_64/floatundisf.S
index c840913..e41f118 100644
--- a/lib/builtins/x86_64/floatundisf.S
+++ b/lib/builtins/x86_64/floatundisf.S
@@ -10,10 +10,12 @@
#if defined(__APPLE__)
.literal4
#elif defined(__ELF__)
- .rodata
+ .section .rodata
#else
.section .rdata,"rd"
#endif
+
+ .balign 16
two:
.single 2.0
diff --git a/lib/builtins/x86_64/floatundixf.S b/lib/builtins/x86_64/floatundixf.S
index 6603935..91bdc8a 100644
--- a/lib/builtins/x86_64/floatundixf.S
+++ b/lib/builtins/x86_64/floatundixf.S
@@ -10,11 +10,12 @@
#if defined(__APPLE__)
.const
#elif defined(__ELF__)
- .rodata
+ .section .rodata
#else
.section .rdata,"rd"
#endif
- .balign 4
+
+ .balign 16
twop64:
.quad 0x43f0000000000000
diff --git a/lib/dfsan/CMakeLists.txt b/lib/dfsan/CMakeLists.txt
index bb73d29..daad07f 100644
--- a/lib/dfsan/CMakeLists.txt
+++ b/lib/dfsan/CMakeLists.txt
@@ -7,14 +7,14 @@
dfsan_interceptors.cc)
set(DFSAN_COMMON_CFLAGS ${SANITIZER_COMMON_CFLAGS})
# Prevent clang from generating libc calls.
-append_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding DFSAN_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding DFSAN_COMMON_CFLAGS)
# Static runtime library.
add_custom_target(dfsan)
set(arch "x86_64")
if(CAN_TARGET_${arch})
set(DFSAN_CFLAGS ${DFSAN_COMMON_CFLAGS})
- append_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE DFSAN_CFLAGS)
+ append_list_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE DFSAN_CFLAGS)
add_compiler_rt_runtime(clang_rt.dfsan-${arch} ${arch} STATIC
SOURCES ${DFSAN_RTL_SOURCES}
$<TARGET_OBJECTS:RTInterception.${arch}>
@@ -39,9 +39,9 @@
VERBATIM
COMMAND
cat ${CMAKE_CURRENT_SOURCE_DIR}/done_abilist.txt
- ${CMAKE_CURRENT_SOURCE_DIR}/libc_ubuntu1204_abilist.txt
+ ${CMAKE_CURRENT_SOURCE_DIR}/libc_ubuntu1404_abilist.txt
> ${dfsan_abilist_filename}
- DEPENDS done_abilist.txt libc_ubuntu1204_abilist.txt)
+ DEPENDS done_abilist.txt libc_ubuntu1404_abilist.txt)
add_dependencies(dfsan dfsan_abilist)
install(FILES ${dfsan_abilist_filename}
DESTINATION ${COMPILER_RT_INSTALL_PATH})
diff --git a/lib/dfsan/dfsan.cc b/lib/dfsan/dfsan.cc
index 076ec58..dcc52b1 100644
--- a/lib/dfsan/dfsan.cc
+++ b/lib/dfsan/dfsan.cc
@@ -74,6 +74,14 @@
return &(*(dfsan_union_table_t *) kUnionTableAddr)[l1][l2];
}
+// Checks we do not run out of labels.
+static void dfsan_check_label(dfsan_label label) {
+ if (label == kInitializingLabel) {
+ Report("FATAL: DataFlowSanitizer: out of labels\n");
+ Die();
+ }
+}
+
// 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
@@ -106,7 +114,7 @@
} else {
label =
atomic_fetch_add(&__dfsan_last_label, 1, memory_order_relaxed) + 1;
- CHECK_NE(label, kInitializingLabel);
+ dfsan_check_label(label);
__dfsan_label_info[label].l1 = l1;
__dfsan_label_info[label].l2 = l2;
}
@@ -147,6 +155,15 @@
Report("WARNING: DataFlowSanitizer: saw nonzero label\n");
}
+// Indirect call to an uninstrumented vararg function. We don't have a way of
+// handling these at the moment.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
+__dfsan_vararg_wrapper(const char *fname) {
+ Report("FATAL: DataFlowSanitizer: unsupported indirect call to vararg "
+ "function %s\n", fname);
+ Die();
+}
+
// 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
@@ -160,7 +177,7 @@
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_check_label(label);
__dfsan_label_info[label].l1 = __dfsan_label_info[label].l2 = 0;
__dfsan_label_info[label].desc = desc;
__dfsan_label_info[label].userdata = userdata;
@@ -169,8 +186,20 @@
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)
+ for (dfsan_label *labelp = shadow_for(addr); size != 0; --size, ++labelp) {
+ // Don't write the label if it is already the value we need it to be.
+ // In a program where most addresses are not labeled, it is common that
+ // a page of shadow memory is entirely zeroed. The Linux copy-on-write
+ // implementation will share all of the zeroed pages, making a copy of a
+ // page when any value is written. The un-sharing will happen even if
+ // the value written does not change the value in memory. Avoiding the
+ // write when both |label| and |*labelp| are zero dramatically reduces
+ // the amount of real memory used by large programs.
+ if (label == *labelp)
+ continue;
+
*labelp = label;
+ }
}
SANITIZER_INTERFACE_ATTRIBUTE
@@ -238,14 +267,50 @@
return static_cast<uptr>(max_label_allocated);
}
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
+dfsan_dump_labels(int fd) {
+ dfsan_label last_label =
+ atomic_load(&__dfsan_last_label, memory_order_relaxed);
+
+ for (uptr l = 1; l <= last_label; ++l) {
+ char buf[64];
+ internal_snprintf(buf, sizeof(buf), "%u %u %u ", l,
+ __dfsan_label_info[l].l1, __dfsan_label_info[l].l2);
+ internal_write(fd, buf, internal_strlen(buf));
+ if (__dfsan_label_info[l].l1 == 0 && __dfsan_label_info[l].desc) {
+ internal_write(fd, __dfsan_label_info[l].desc,
+ internal_strlen(__dfsan_label_info[l].desc));
+ }
+ internal_write(fd, "\n", 1);
+ }
+}
+
static void InitializeFlags(Flags &f, const char *env) {
f.warn_unimplemented = true;
f.warn_nonzero_labels = false;
f.strict_data_dependencies = true;
+ f.dump_labels_at_exit = "";
ParseFlag(env, &f.warn_unimplemented, "warn_unimplemented", "");
ParseFlag(env, &f.warn_nonzero_labels, "warn_nonzero_labels", "");
ParseFlag(env, &f.strict_data_dependencies, "strict_data_dependencies", "");
+ ParseFlag(env, &f.dump_labels_at_exit, "dump_labels_at_exit", "");
+}
+
+static void dfsan_fini() {
+ if (internal_strcmp(flags().dump_labels_at_exit, "") != 0) {
+ fd_t fd = OpenFile(flags().dump_labels_at_exit, true /* write */);
+ if (fd == kInvalidFd) {
+ Report("WARNING: DataFlowSanitizer: unable to open output file %s\n",
+ flags().dump_labels_at_exit);
+ return;
+ }
+
+ Report("INFO: DataFlowSanitizer: dumping labels to %s\n",
+ flags().dump_labels_at_exit);
+ dfsan_dump_labels(fd);
+ internal_close(fd);
+ }
}
#ifdef DFSAN_NOLIBC
@@ -267,9 +332,16 @@
InitializeFlags(flags(), GetEnv("DFSAN_OPTIONS"));
InitializeInterceptors();
+
+ // Register the fini callback to run when the program terminates successfully
+ // or it is killed by the runtime.
+ Atexit(dfsan_fini);
+ SetDieCallback(dfsan_fini);
+
+ __dfsan_label_info[kInitializingLabel].desc = "<init label>";
}
-#ifndef DFSAN_NOLIBC
+#if !defined(DFSAN_NOLIBC) && SANITIZER_CAN_USE_PREINIT_ARRAY
__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
index ffa98d8..1b6c150 100644
--- a/lib/dfsan/dfsan.h
+++ b/lib/dfsan/dfsan.h
@@ -61,6 +61,8 @@
// comparison might be data-dependent on the content of the strings). This
// applies only to the custom functions defined in 'custom.c'.
bool strict_data_dependencies;
+ // The path of the file where to dump the labels when the program terminates.
+ const char* dump_labels_at_exit;
};
extern Flags flags_data;
diff --git a/lib/dfsan/dfsan_custom.cc b/lib/dfsan/dfsan_custom.cc
index d06a003..839a399 100644
--- a/lib/dfsan/dfsan_custom.cc
+++ b/lib/dfsan/dfsan_custom.cc
@@ -12,12 +12,14 @@
// This file defines the custom functions listed in done_abilist.txt.
//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_linux.h"
#include "dfsan/dfsan.h"
#include <arpa/inet.h>
+#include <assert.h>
#include <ctype.h>
#include <dlfcn.h>
#include <link.h>
@@ -26,6 +28,8 @@
#include <pwd.h>
#include <sched.h>
#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -211,8 +215,9 @@
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));
+ dfsan_label *sdest = shadow_for(dest);
+ const dfsan_label *ssrc = shadow_for(src);
+ internal_memcpy((void *)sdest, (const void *)ssrc, n * sizeof(dfsan_label));
return internal_memcpy(dest, src, n);
}
@@ -361,9 +366,11 @@
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_set_label(0, const_cast<char *>(info->dlpi_name),
+ strlen(info->dlpi_name) + 1);
+ dfsan_set_label(
+ 0, const_cast<char *>(reinterpret_cast<const char *>(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);
@@ -839,4 +846,279 @@
*ret_label = 0;
return write(fd, buf, count);
}
+
+// Type used to extract a dfsan_label with va_arg()
+typedef int dfsan_label_va;
+
+// A chunk of data representing the output of formatting either a constant
+// string or a single format directive.
+struct Chunk {
+ // Address of the beginning of the formatted string
+ const char *ptr;
+ // Size of the formatted string
+ size_t size;
+
+ // Type of DFSan label (depends on the format directive)
+ enum {
+ // Constant string, no argument and thus no label
+ NONE = 0,
+ // Label for an argument of '%n'
+ IGNORED,
+ // Label for a '%s' argument
+ STRING,
+ // Label for any other type of argument
+ NUMERIC,
+ } label_type;
+
+ // Value of the argument (if label_type == STRING)
+ const char *arg;
+};
+
+// Formats the input. The output is stored in 'str' starting from offset
+// 'off'. The format directive is represented by the first 'format_size' bytes
+// of 'format'. If 'has_size' is true, 'size' bounds the number of output
+// bytes. Returns the return value of the vsnprintf call used to format the
+// input.
+static int format_chunk(char *str, size_t off, bool has_size, size_t size,
+ const char *format, size_t format_size, ...) {
+ char *chunk_format = (char *) malloc(format_size + 1);
+ assert(chunk_format);
+ internal_memcpy(chunk_format, format, format_size);
+ chunk_format[format_size] = '\0';
+
+ va_list ap;
+ va_start(ap, format_size);
+ int r = 0;
+ if (has_size) {
+ r = vsnprintf(str + off, off < size ? size - off : 0, chunk_format, ap);
+ } else {
+ r = vsprintf(str + off, chunk_format, ap);
+ }
+ va_end(ap);
+
+ free(chunk_format);
+ return r;
+}
+
+// Formats the input and propagates the input labels to the output. The output
+// is stored in 'str'. If 'has_size' is true, 'size' bounds the number of
+// output bytes. 'format' and 'ap' are the format string and the list of
+// arguments for formatting. Returns the return value vsnprintf would return.
+//
+// The function tokenizes the format string in chunks representing either a
+// constant string or a single format directive (e.g., '%.3f') and formats each
+// chunk independently into the output string. This approach allows to figure
+// out which bytes of the output string depends on which argument and thus to
+// propagate labels more precisely.
+static int format_buffer(char *str, bool has_size, size_t size,
+ const char *format, dfsan_label *va_labels,
+ dfsan_label *ret_label, va_list ap) {
+ InternalMmapVector<Chunk> chunks(8);
+ size_t off = 0;
+
+ while (*format) {
+ chunks.push_back(Chunk());
+ Chunk& chunk = chunks.back();
+ chunk.ptr = str + off;
+ chunk.arg = nullptr;
+
+ int status = 0;
+
+ if (*format != '%') {
+ // Ordinary character. Consume all the characters until a '%' or the end
+ // of the string.
+ size_t format_size = 0;
+ for (; *format && *format != '%'; ++format, ++format_size) {}
+ status = format_chunk(str, off, has_size, size, format - format_size,
+ format_size);
+ chunk.label_type = Chunk::NONE;
+ } else {
+ // Conversion directive. Consume all the characters until a conversion
+ // specifier or the end of the string.
+ bool end_format = false;
+#define FORMAT_CHUNK(t) \
+ format_chunk(str, off, has_size, size, format - format_size, \
+ format_size + 1, va_arg(ap, t))
+
+ for (size_t format_size = 1; *++format && !end_format; ++format_size) {
+ switch (*format) {
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ switch (*(format - 1)) {
+ case 'h':
+ // Also covers the 'hh' case (since the size of the arg is still
+ // an int).
+ status = FORMAT_CHUNK(int);
+ break;
+ case 'l':
+ if (format_size >= 2 && *(format - 2) == 'l') {
+ status = FORMAT_CHUNK(long long int);
+ } else {
+ status = FORMAT_CHUNK(long int);
+ }
+ break;
+ case 'q':
+ status = FORMAT_CHUNK(long long int);
+ break;
+ case 'j':
+ status = FORMAT_CHUNK(intmax_t);
+ break;
+ case 'z':
+ status = FORMAT_CHUNK(size_t);
+ break;
+ case 't':
+ status = FORMAT_CHUNK(size_t);
+ break;
+ default:
+ status = FORMAT_CHUNK(int);
+ }
+ chunk.label_type = Chunk::NUMERIC;
+ end_format = true;
+ break;
+
+ case 'a':
+ case 'A':
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'F':
+ case 'g':
+ case 'G':
+ if (*(format - 1) == 'L') {
+ status = FORMAT_CHUNK(long double);
+ } else {
+ status = FORMAT_CHUNK(double);
+ }
+ chunk.label_type = Chunk::NUMERIC;
+ end_format = true;
+ break;
+
+ case 'c':
+ status = FORMAT_CHUNK(int);
+ chunk.label_type = Chunk::NUMERIC;
+ end_format = true;
+ break;
+
+ case 's':
+ chunk.arg = va_arg(ap, char *);
+ status =
+ format_chunk(str, off, has_size, size,
+ format - format_size, format_size + 1,
+ chunk.arg);
+ chunk.label_type = Chunk::STRING;
+ end_format = true;
+ break;
+
+ case 'p':
+ status = FORMAT_CHUNK(void *);
+ chunk.label_type = Chunk::NUMERIC;
+ end_format = true;
+ break;
+
+ case 'n':
+ *(va_arg(ap, int *)) = (int)off;
+ chunk.label_type = Chunk::IGNORED;
+ end_format = true;
+ break;
+
+ case '%':
+ status = format_chunk(str, off, has_size, size,
+ format - format_size, format_size + 1);
+ chunk.label_type = Chunk::NONE;
+ end_format = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+#undef FORMAT_CHUNK
+ }
+
+ if (status < 0) {
+ return status;
+ }
+
+ // A return value of {v,}snprintf of size or more means that the output was
+ // truncated.
+ if (has_size) {
+ if (off < size) {
+ size_t ustatus = (size_t) status;
+ chunk.size = ustatus >= (size - off) ?
+ ustatus - (size - off) : ustatus;
+ } else {
+ chunk.size = 0;
+ }
+ } else {
+ chunk.size = status;
+ }
+ off += status;
+ }
+
+ // TODO(martignlo): Decide how to combine labels (e.g., whether to ignore or
+ // not the label of the format string).
+
+ // Label each output chunk according to the label supplied as argument to the
+ // function. We need to go through all the chunks and arguments even if the
+ // string was only partially printed ({v,}snprintf case).
+ for (size_t i = 0; i < chunks.size(); ++i) {
+ const Chunk& chunk = chunks[i];
+ void *chunk_ptr = const_cast<char *>(chunk.ptr);
+
+ switch (chunk.label_type) {
+ case Chunk::NONE:
+ dfsan_set_label(0, chunk_ptr, chunk.size);
+ break;
+ case Chunk::IGNORED:
+ va_labels++;
+ dfsan_set_label(0, chunk_ptr, chunk.size);
+ break;
+ case Chunk::NUMERIC: {
+ dfsan_label label = *va_labels++;
+ dfsan_set_label(label, chunk_ptr, chunk.size);
+ break;
+ }
+ case Chunk::STRING: {
+ // Consume the label of the pointer to the string
+ va_labels++;
+ internal_memcpy(shadow_for(chunk_ptr),
+ shadow_for(chunk.arg),
+ sizeof(dfsan_label) * (strlen(chunk.arg)));
+ break;
+ }
+ }
+ }
+
+ *ret_label = 0;
+
+ // Number of bytes written in total.
+ return off;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __dfsw_sprintf(char *str, const char *format, dfsan_label str_label,
+ dfsan_label format_label, dfsan_label *va_labels,
+ dfsan_label *ret_label, ...) {
+ va_list ap;
+ va_start(ap, ret_label);
+ int ret = format_buffer(str, false, 0, format, va_labels, ret_label, ap);
+ va_end(ap);
+ return ret;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __dfsw_snprintf(char *str, size_t size, const char *format,
+ dfsan_label str_label, dfsan_label size_label,
+ dfsan_label format_label, dfsan_label *va_labels,
+ dfsan_label *ret_label, ...) {
+ va_list ap;
+ va_start(ap, ret_label);
+ int ret = format_buffer(str, true, size, format, va_labels, ret_label, ap);
+ va_end(ap);
+ return ret;
+}
}
diff --git a/lib/dfsan/done_abilist.txt b/lib/dfsan/done_abilist.txt
index 44507bf..15c6d1b 100644
--- a/lib/dfsan/done_abilist.txt
+++ b/lib/dfsan/done_abilist.txt
@@ -208,9 +208,11 @@
fun:sigaction=custom
fun:gettimeofday=custom
+# sprintf-like
+fun:sprintf=custom
+fun:snprintf=custom
+
# TODO: custom
-fun:snprintf=discard
-fun:vsnprintf=discard
fun:asprintf=discard
fun:qsort=discard
@@ -235,3 +237,24 @@
# Functions that take a callback (wrap the callback manually).
fun:pthread_create=custom
+
+###############################################################################
+# libffi/libgo
+###############################################################################
+# Functions that are written in asm or are called from asm.
+fun:ffi_call_unix64=uninstrumented
+fun:ffi_call_unix64=discard
+fun:ffi_closure_unix64_inner=uninstrumented
+fun:ffi_closure_unix64_inner=discard
+fun:ffi_closure_unix64=uninstrumented
+fun:ffi_closure_unix64=discard
+fun:__go_get_closure=uninstrumented
+fun:__go_get_closure=discard
+fun:__go_makefunc_can_recover=uninstrumented
+fun:__go_makefunc_can_recover=discard
+fun:__go_makefunc_returning=uninstrumented
+fun:__go_makefunc_returning=discard
+fun:reflect.MakeFuncStubGo=uninstrumented
+fun:reflect.MakeFuncStubGo=discard
+fun:reflect.makeFuncStub=uninstrumented
+fun:reflect.makeFuncStub=discard
diff --git a/lib/dfsan/libc_ubuntu1204_abilist.txt b/lib/dfsan/libc_ubuntu1404_abilist.txt
similarity index 90%
rename from lib/dfsan/libc_ubuntu1204_abilist.txt
rename to lib/dfsan/libc_ubuntu1404_abilist.txt
index 5fc8194..a1ea0a0 100644
--- a/lib/dfsan/libc_ubuntu1204_abilist.txt
+++ b/lib/dfsan/libc_ubuntu1404_abilist.txt
@@ -123,83 +123,6 @@
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
@@ -218,18 +141,9 @@
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
@@ -602,27 +516,31 @@
fun:__bswapdi2=uninstrumented
fun:__bswapsi2=uninstrumented
fun:__bzero=uninstrumented
+fun:__call_tls_dtors=uninstrumented
fun:__chk_fail=uninstrumented
fun:__clear_cache=uninstrumented
+fun:__clock_getcpuclockid=uninstrumented
+fun:__clock_getres=uninstrumented
+fun:__clock_gettime=uninstrumented
+fun:__clock_nanosleep=uninstrumented
+fun:__clock_settime=uninstrumented
fun:__clog10=uninstrumented
fun:__clog10f=uninstrumented
fun:__clog10l=uninstrumented
fun:__clone=uninstrumented
fun:__close=uninstrumented
-fun:__close_nocancel=uninstrumented
+fun:__clrsbdi2=uninstrumented
+fun:__clrsbti2=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:__cpu_indicator_init=uninstrumented
fun:__create_ib_request=uninstrumented
fun:__ctype_b_loc=uninstrumented
fun:__ctype_get_mb_cur_max=uninstrumented
@@ -634,15 +552,14 @@
fun:__cxa_at_quick_exit=uninstrumented
fun:__cxa_atexit=uninstrumented
fun:__cxa_finalize=uninstrumented
+fun:__cxa_thread_atexit_impl=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
@@ -659,13 +576,10 @@
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
@@ -687,7 +601,6 @@
fun:__extendxftf2=uninstrumented
fun:__fbufsize=uninstrumented
fun:__fcntl=uninstrumented
-fun:__fcntl_nocancel=uninstrumented
fun:__fdelt_chk=uninstrumented
fun:__fdelt_warn=uninstrumented
fun:__fentry__=uninstrumented
@@ -698,8 +611,6 @@
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
@@ -731,7 +642,6 @@
fun:__floatuntisf=uninstrumented
fun:__floatuntitf=uninstrumented
fun:__floatuntixf=uninstrumented
-fun:__flockfile=uninstrumented
fun:__fmod_finite=uninstrumented
fun:__fmodf_finite=uninstrumented
fun:__fmodl_finite=uninstrumented
@@ -752,14 +662,9 @@
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
@@ -781,6 +686,7 @@
fun:__generic_morestack_set_initial_sp=uninstrumented
fun:__generic_releasestack=uninstrumented
fun:__get_cpu_features=uninstrumented
+fun:__getauxval=uninstrumented
fun:__getcwd_chk=uninstrumented
fun:__getdelim=uninstrumented
fun:__getdomainname_chk=uninstrumented
@@ -802,7 +708,6 @@
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
@@ -835,6 +740,9 @@
fun:__isoc99_wscanf=uninstrumented
fun:__isprint_l=uninstrumented
fun:__ispunct_l=uninstrumented
+fun:__issignaling=uninstrumented
+fun:__issignalingf=uninstrumented
+fun:__issignalingl=uninstrumented
fun:__isspace_l=uninstrumented
fun:__isupper_l=uninstrumented
fun:__iswalnum_l=uninstrumented
@@ -866,14 +774,11 @@
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
@@ -885,62 +790,30 @@
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_ifunc_impl_list=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_secure_getenv=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
@@ -954,14 +827,12 @@
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:__madvise=uninstrumented
fun:__mbrlen=uninstrumented
fun:__mbrtowc=uninstrumented
fun:__mbsnrtowcs_chk=uninstrumented
@@ -974,23 +845,22 @@
fun:__mempcpy_small=uninstrumented
fun:__memset_chk=uninstrumented
fun:__mknod=uninstrumented
+fun:__mktemp=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_get_guard=uninstrumented
fun:__morestack_large_model=uninstrumented
fun:__morestack_load_mmap=uninstrumented
+fun:__morestack_make_guard=uninstrumented
fun:__morestack_non_split=uninstrumented
fun:__morestack_release_segments=uninstrumented
+fun:__morestack_set_guard=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
@@ -1001,16 +871,12 @@
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
@@ -1023,12 +889,6 @@
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
@@ -1055,7 +915,6 @@
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
@@ -1072,9 +931,9 @@
fun:__p_type=uninstrumented
fun:__paritydi2=uninstrumented
fun:__parityti2=uninstrumented
-fun:__pause_nocancel=uninstrumented
fun:__pipe=uninstrumented
fun:__poll=uninstrumented
+fun:__poll_chk=uninstrumented
fun:__popcountdi2=uninstrumented
fun:__popcountti2=uninstrumented
fun:__posix_getopt=uninstrumented
@@ -1085,121 +944,41 @@
fun:__powitf2=uninstrumented
fun:__powixf2=uninstrumented
fun:__powl_finite=uninstrumented
-fun:__pread=uninstrumented
+fun:__ppoll_chk=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
@@ -1207,24 +986,15 @@
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
@@ -1261,7 +1031,6 @@
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
@@ -1281,14 +1050,11 @@
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:__sendmmsg=uninstrumented
fun:__setmntent=uninstrumented
fun:__setpgid=uninstrumented
+fun:__sfp_handle_exceptions=uninstrumented
fun:__sigaction=uninstrumented
fun:__sigaddset=uninstrumented
fun:__sigdelset=uninstrumented
@@ -1299,13 +1065,19 @@
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_block_signals=uninstrumented
+fun:__splitstack_block_signals_context=uninstrumented
fun:__splitstack_find=uninstrumented
+fun:__splitstack_find_context=uninstrumented
+fun:__splitstack_getcontext=uninstrumented
+fun:__splitstack_makecontext=uninstrumented
+fun:__splitstack_releasecontext=uninstrumented
+fun:__splitstack_resetcontext=uninstrumented
+fun:__splitstack_setcontext=uninstrumented
fun:__sprintf_chk=uninstrumented
fun:__sqrt_finite=uninstrumented
fun:__sqrtf_finite=uninstrumented
@@ -1397,7 +1169,6 @@
fun:__umodti3=uninstrumented
fun:__underflow=uninstrumented
fun:__unordtf2=uninstrumented
-fun:__unwind_freeres=uninstrumented
fun:__uselocale=uninstrumented
fun:__vasprintf_chk=uninstrumented
fun:__vdprintf_chk=uninstrumented
@@ -1414,7 +1185,6 @@
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
@@ -1449,7 +1219,6 @@
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
@@ -1458,7 +1227,6 @@
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
@@ -1484,6 +1252,7 @@
fun:_dl_allocate_tls_init=uninstrumented
fun:_dl_deallocate_tls=uninstrumented
fun:_dl_debug_state=uninstrumented
+fun:_dl_find_dso_for_object=uninstrumented
fun:_dl_get_tls_static_info=uninstrumented
fun:_dl_make_stack_executable=uninstrumented
fun:_dl_mcount=uninstrumented
@@ -1494,7 +1263,6 @@
fun:_dl_tls_setup=uninstrumented
fun:_dl_vsym=uninstrumented
fun:_exit=uninstrumented
-fun:_fini=uninstrumented
fun:_flushlbf=uninstrumented
fun:_gethtbyaddr=uninstrumented
fun:_gethtbyname=uninstrumented
@@ -1502,7 +1270,6 @@
fun:_gethtent=uninstrumented
fun:_getlong=uninstrumented
fun:_getshort=uninstrumented
-fun:_init=uninstrumented
fun:_longjmp=uninstrumented
fun:_mcleanup=uninstrumented
fun:_mcount=uninstrumented
@@ -1542,7 +1309,6 @@
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
@@ -1564,6 +1330,7 @@
fun:aio_write=uninstrumented
fun:aio_write64=uninstrumented
fun:alarm=uninstrumented
+fun:aligned_alloc=uninstrumented
fun:alphasort=uninstrumented
fun:alphasort64=uninstrumented
fun:arch_prctl=uninstrumented
@@ -1622,7 +1389,6 @@
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
@@ -1632,6 +1398,8 @@
fun:bsearch=uninstrumented
fun:btowc=uninstrumented
fun:bzero=uninstrumented
+fun:c16rtomb=uninstrumented
+fun:c32rtomb=uninstrumented
fun:cabs=uninstrumented
fun:cabsf=uninstrumented
fun:cabsl=uninstrumented
@@ -1688,7 +1456,6 @@
fun:cfsetospeed=uninstrumented
fun:cfsetspeed=uninstrumented
fun:chdir=uninstrumented
-fun:check_add_mapping=uninstrumented
fun:chflags=uninstrumented
fun:chmod=uninstrumented
fun:chown=uninstrumented
@@ -1696,8 +1463,6 @@
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
@@ -1756,7 +1521,6 @@
fun:creall=uninstrumented
fun:creat=uninstrumented
fun:creat64=uninstrumented
-fun:create_key=uninstrumented
fun:create_module=uninstrumented
fun:crypt=uninstrumented
fun:crypt_r=uninstrumented
@@ -1800,8 +1564,6 @@
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
@@ -2002,12 +1764,9 @@
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
@@ -2065,7 +1824,6 @@
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
@@ -2079,6 +1837,7 @@
fun:getaliasbyname_r=uninstrumented
fun:getaliasent=uninstrumented
fun:getaliasent_r=uninstrumented
+fun:getauxval=uninstrumented
fun:getc=uninstrumented
fun:getc_unlocked=uninstrumented
fun:getchar=uninstrumented
@@ -2492,6 +2251,8 @@
fun:matherr=uninstrumented
fun:mblen=uninstrumented
fun:mbrlen=uninstrumented
+fun:mbrtoc16=uninstrumented
+fun:mbrtoc32=uninstrumented
fun:mbrtowc=uninstrumented
fun:mbsinit=uninstrumented
fun:mbsnrtowcs=uninstrumented
@@ -2653,9 +2414,6 @@
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
@@ -2802,7 +2560,6 @@
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
@@ -2820,6 +2577,7 @@
fun:pthread_equal=uninstrumented
fun:pthread_exit=uninstrumented
fun:pthread_getaffinity_np=uninstrumented
+fun:pthread_getattr_default_np=uninstrumented
fun:pthread_getattr_np=uninstrumented
fun:pthread_getconcurrency=uninstrumented
fun:pthread_getcpuclockid=uninstrumented
@@ -2875,6 +2633,7 @@
fun:pthread_rwlockattr_setpshared=uninstrumented
fun:pthread_self=uninstrumented
fun:pthread_setaffinity_np=uninstrumented
+fun:pthread_setattr_default_np=uninstrumented
fun:pthread_setcancelstate=uninstrumented
fun:pthread_setcanceltype=uninstrumented
fun:pthread_setconcurrency=uninstrumented
@@ -3003,7 +2762,6 @@
fun:rmdir=uninstrumented
fun:round=uninstrumented
fun:roundf=uninstrumented
-fun:rounding_correction.constprop.1=uninstrumented
fun:roundl=uninstrumented
fun:rpmatch=uninstrumented
fun:rresvport=uninstrumented
@@ -3038,6 +2796,7 @@
fun:sched_setparam=uninstrumented
fun:sched_setscheduler=uninstrumented
fun:sched_yield=uninstrumented
+fun:secure_getenv=uninstrumented
fun:seed48=uninstrumented
fun:seed48_r=uninstrumented
fun:seekdir=uninstrumented
@@ -3049,12 +2808,9 @@
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
@@ -3123,7 +2879,6 @@
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
@@ -3140,12 +2895,10 @@
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
@@ -3200,8 +2953,6 @@
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
@@ -3272,7 +3023,6 @@
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
@@ -3392,6 +3142,7 @@
fun:timerfd_gettime=uninstrumented
fun:timerfd_settime=uninstrumented
fun:times=uninstrumented
+fun:timespec_get=uninstrumented
fun:tmpfile=uninstrumented
fun:tmpfile64=uninstrumented
fun:tmpnam=uninstrumented
@@ -3433,8 +3184,6 @@
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
@@ -3480,7 +3229,6 @@
fun:wait4=uninstrumented
fun:waitid=uninstrumented
fun:waitpid=uninstrumented
-fun:walker=uninstrumented
fun:warn=uninstrumented
fun:warnx=uninstrumented
fun:wcpcpy=uninstrumented
diff --git a/lib/dfsan/scripts/build-libc-list.py b/lib/dfsan/scripts/build-libc-list.py
index eb4c619..eddb6c0 100755
--- a/lib/dfsan/scripts/build-libc-list.py
+++ b/lib/dfsan/scripts/build-libc-list.py
@@ -26,6 +26,7 @@
raise subprocess.CalledProcessError(readelf_proc.returncode, 'readelf')
for line in readelf:
if (line[31:35] == 'FUNC' or line[31:36] == 'IFUNC') and \
+ line[39:44] != 'LOCAL' and \
line[55:58] != 'UND':
function_name = line[59:].split('@')[0]
functions.append(function_name)
diff --git a/lib/dfsan/scripts/check_custom_wrappers.sh b/lib/dfsan/scripts/check_custom_wrappers.sh
index 87e8a09..50bc85d 100755
--- a/lib/dfsan/scripts/check_custom_wrappers.sh
+++ b/lib/dfsan/scripts/check_custom_wrappers.sh
@@ -1,26 +1,28 @@
-#!/usr/bin/env bash
+#!/bin/sh
DFSAN_DIR=$(dirname "$0")/../
-DFSAN_CUSTOM_TESTS=${DFSAN_DIR}/../../test/dfsan/custom.c
+DFSAN_CUSTOM_TESTS=${DFSAN_DIR}/../../test/dfsan/custom.cc
DFSAN_CUSTOM_WRAPPERS=${DFSAN_DIR}/dfsan_custom.cc
DFSAN_ABI_LIST=${DFSAN_DIR}/done_abilist.txt
DIFFOUT=$(mktemp -q /tmp/tmp.XXXXXXXXXX)
ERRORLOG=$(mktemp -q /tmp/tmp.XXXXXXXXXX)
+DIFF_A=$(mktemp -q /tmp/tmp.XXXXXXXXXX)
+DIFF_B=$(mktemp -q /tmp/tmp.XXXXXXXXXX)
on_exit() {
rm -f ${DIFFOUT} 2> /dev/null
rm -f ${ERRORLOG} 2> /dev/null
+ rm -f ${DIFF_A} 2> /dev/null
+ rm -f ${DIFF_B} 2> /dev/null
}
trap on_exit EXIT
-
-diff -u \
- <(grep -E "^fun:.*=custom" ${DFSAN_ABI_LIST} | grep -v "dfsan_get_label" \
- | sed "s/^fun:\(.*\)=custom.*/\1/" | sort ) \
- <(grep -E "__dfsw.*\(" ${DFSAN_CUSTOM_WRAPPERS} \
- | sed "s/.*__dfsw_\(.*\)(.*/\1/" \
- | sort) > ${DIFFOUT}
+grep -E "^fun:.*=custom" ${DFSAN_ABI_LIST} | grep -v "dfsan_get_label" \
+ | sed "s/^fun:\(.*\)=custom.*/\1/" | sort > $DIFF_A
+grep -E "__dfsw.*\(" ${DFSAN_CUSTOM_WRAPPERS} \
+ | sed "s/.*__dfsw_\(.*\)(.*/\1/" | sort > $DIFF_B
+diff -u $DIFF_A $DIFF_B > ${DIFFOUT}
if [ $? -ne 0 ]
then
echo -n "The following differences between the ABI list and ">> ${ERRORLOG}
@@ -28,13 +30,11 @@
cat ${DIFFOUT} >> ${ERRORLOG}
fi
-diff -u \
- <(grep -E __dfsw_ ${DFSAN_CUSTOM_WRAPPERS} \
- | sed "s/.*__dfsw_\([^(]*\).*/\1/" \
- | sort) \
- <(grep -E "^\\s*test_.*\(\);" ${DFSAN_CUSTOM_TESTS} \
- | sed "s/.*test_\(.*\)();/\1/" \
- | sort) > ${DIFFOUT}
+grep -E __dfsw_ ${DFSAN_CUSTOM_WRAPPERS} \
+ | sed "s/.*__dfsw_\([^(]*\).*/\1/" | sort > $DIFF_A
+grep -E "^\\s*test_.*\(\);" ${DFSAN_CUSTOM_TESTS} \
+ | sed "s/.*test_\(.*\)();/\1/" | sort > $DIFF_B
+diff -u $DIFF_A $DIFF_B > ${DIFFOUT}
if [ $? -ne 0 ]
then
echo -n "The following differences between the implemented " >> ${ERRORLOG}
@@ -42,7 +42,7 @@
cat ${DIFFOUT} >> ${ERRORLOG}
fi
-if [[ -s ${ERRORLOG} ]]
+if [ -s ${ERRORLOG} ]
then
cat ${ERRORLOG}
exit 1
diff --git a/lib/interception/CMakeLists.txt b/lib/interception/CMakeLists.txt
index cf8b3b4..b77f2d1 100644
--- a/lib/interception/CMakeLists.txt
+++ b/lib/interception/CMakeLists.txt
@@ -20,10 +20,6 @@
SOURCES ${INTERCEPTION_SOURCES}
CFLAGS ${INTERCEPTION_CFLAGS})
endforeach()
-elseif(ANDROID)
- add_library(RTInterception.arm.android OBJECT ${INTERCEPTION_SOURCES})
- set_target_compile_flags(RTInterception.arm.android
- ${INTERCEPTION_CFLAGS})
else()
# Otherwise, build separate libraries for each target.
foreach(arch ${SANITIZER_COMMON_SUPPORTED_ARCH})
diff --git a/lib/interception/interception.h b/lib/interception/interception.h
index 743c88d..5257325 100644
--- a/lib/interception/interception.h
+++ b/lib/interception/interception.h
@@ -122,15 +122,9 @@
# define DECLARE_WRAPPER(ret_type, func, ...)
#elif defined(_WIN32)
-# if defined(_DLL) // DLL CRT
-# define WRAP(x) x
-# define WRAPPER_NAME(x) #x
-# define INTERCEPTOR_ATTRIBUTE
-# else // Static CRT
-# define WRAP(x) __asan_wrap_##x
-# define WRAPPER_NAME(x) "__asan_wrap_"#x
-# define INTERCEPTOR_ATTRIBUTE __declspec(dllexport)
-# endif
+# define WRAP(x) __asan_wrap_##x
+# define WRAPPER_NAME(x) "__asan_wrap_"#x
+# define INTERCEPTOR_ATTRIBUTE __declspec(dllexport)
# define DECLARE_WRAPPER(ret_type, func, ...) \
extern "C" ret_type func(__VA_ARGS__);
# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \
diff --git a/lib/interception/interception_win.cc b/lib/interception/interception_win.cc
index 9eabe52..cd241c3 100644
--- a/lib/interception/interception_win.cc
+++ b/lib/interception/interception_win.cc
@@ -19,20 +19,6 @@
namespace __interception {
-bool GetRealFunctionAddress(const char *func_name, uptr *func_addr) {
- const char *DLLS[] = {
- "msvcr80.dll",
- "msvcr90.dll",
- "kernel32.dll",
- NULL
- };
- *func_addr = 0;
- for (size_t i = 0; *func_addr == 0 && DLLS[i]; ++i) {
- *func_addr = (uptr)GetProcAddress(GetModuleHandleA(DLLS[i]), func_name);
- }
- return (*func_addr != 0);
-}
-
// FIXME: internal_str* and internal_mem* functions should be moved from the
// ASan sources into interception/.
@@ -110,9 +96,11 @@
case 0x458B: // 8B 45 XX = mov eax, dword ptr [ebp+XXh]
case 0x5D8B: // 8B 5D XX = mov ebx, dword ptr [ebp+XXh]
case 0xEC83: // 83 EC XX = sub esp, XX
+ case 0x75FF: // FF 75 XX = push dword ptr [ebp+XXh]
cursor += 3;
continue;
case 0xC1F7: // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX
+ case 0x25FF: // FF 25 XX YY ZZ WW = jmp dword ptr ds:[WWZZYYXX]
cursor += 6;
continue;
case 0x3D83: // 83 3D XX YY ZZ WW TT = cmp TT, WWZZYYXX
@@ -193,6 +181,36 @@
return true;
}
+static const void **InterestingDLLsAvailable() {
+ const char *InterestingDLLs[] = {"kernel32.dll",
+ "msvcr110.dll", // VS2012
+ "msvcr120.dll", // VS2013
+ NULL};
+ static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
+ if (!result[0]) {
+ for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
+ if (HMODULE h = GetModuleHandleA(InterestingDLLs[i]))
+ result[j++] = (void *)h;
+ }
+ }
+ return (const void **)&result[0];
+}
+
+static bool GetFunctionAddressInDLLs(const char *func_name, uptr *func_addr) {
+ *func_addr = 0;
+ const void **DLLs = InterestingDLLsAvailable();
+ for (size_t i = 0; *func_addr == 0 && DLLs[i]; ++i)
+ *func_addr = (uptr)GetProcAddress((HMODULE)DLLs[i], func_name);
+ return (*func_addr != 0);
+}
+
+bool OverrideFunction(const char *name, uptr new_func, uptr *orig_old_func) {
+ uptr orig_func;
+ if (!GetFunctionAddressInDLLs(name, &orig_func))
+ return false;
+ return OverrideFunction(orig_func, new_func, orig_old_func);
+}
+
} // namespace __interception
#endif // _WIN32
diff --git a/lib/interception/interception_win.h b/lib/interception/interception_win.h
index f2727c9..ba768a7 100644
--- a/lib/interception/interception_win.h
+++ b/lib/interception/interception_win.h
@@ -22,27 +22,29 @@
#define INTERCEPTION_WIN_H
namespace __interception {
-// returns true if a function with the given name was found.
-bool GetRealFunctionAddress(const char *func_name, uptr *func_addr);
+// All the functions in the OverrideFunction() family return true on success,
+// false on failure (including "couldn't find the function").
-// returns true if the old function existed, false on failure.
-bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func);
+// Overrides a function by its address.
+bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func = 0);
+
+// Overrides a function in a system DLL or DLL CRT by its exported name.
+bool OverrideFunction(const char *name, uptr new_func, uptr *orig_old_func = 0);
} // namespace __interception
-#if defined(_DLL)
-# define INTERCEPT_FUNCTION_WIN(func) \
- ::__interception::GetRealFunctionAddress( \
- #func, (::__interception::uptr*)&REAL(func))
+#if defined(INTERCEPTION_DYNAMIC_CRT)
+#define INTERCEPT_FUNCTION_WIN(func) \
+ ::__interception::OverrideFunction(#func, \
+ (::__interception::uptr)WRAP(func), \
+ (::__interception::uptr *)&REAL(func))
#else
-# define INTERCEPT_FUNCTION_WIN(func) \
- ::__interception::OverrideFunction( \
- (::__interception::uptr)func, \
- (::__interception::uptr)WRAP(func), \
- (::__interception::uptr*)&REAL(func))
+#define INTERCEPT_FUNCTION_WIN(func) \
+ ::__interception::OverrideFunction((::__interception::uptr)func, \
+ (::__interception::uptr)WRAP(func), \
+ (::__interception::uptr *)&REAL(func))
#endif
-#define INTERCEPT_FUNCTION_VER_WIN(func, symver) \
- INTERCEPT_FUNCTION_WIN(func)
+#define INTERCEPT_FUNCTION_VER_WIN(func, symver) INTERCEPT_FUNCTION_WIN(func)
#endif // INTERCEPTION_WIN_H
#endif // _WIN32
diff --git a/lib/lsan/CMakeLists.txt b/lib/lsan/CMakeLists.txt
index 82e9aa7..2ea765d 100644
--- a/lib/lsan/CMakeLists.txt
+++ b/lib/lsan/CMakeLists.txt
@@ -16,11 +16,6 @@
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 arm aarch64 mips)
-
add_custom_target(lsan)
if(APPLE)
@@ -30,7 +25,7 @@
SOURCES ${LSAN_COMMON_SOURCES}
CFLAGS ${LSAN_CFLAGS})
endforeach()
-elseif(NOT ANDROID)
+else()
foreach(arch ${LSAN_COMMON_SUPPORTED_ARCH})
add_compiler_rt_object_library(RTLSanCommon ${arch}
SOURCES ${LSAN_COMMON_SOURCES}
diff --git a/lib/lsan/lsan.cc b/lib/lsan/lsan.cc
index 1b30b4f..1598fca 100644
--- a/lib/lsan/lsan.cc
+++ b/lib/lsan/lsan.cc
@@ -25,16 +25,6 @@
namespace __lsan {
-static void InitializeCommonFlags() {
- CommonFlags *cf = common_flags();
- SetCommonFlagsDefaults(cf);
- cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
- cf->malloc_context_size = 30;
- cf->detect_leaks = true;
-
- ParseCommonFlagsFromString(cf, GetEnv("LSAN_OPTIONS"));
-}
-
///// Interface to the common LSan module. /////
bool WordIsPoisoned(uptr addr) {
return false;
@@ -50,7 +40,7 @@
return;
lsan_init_is_running = true;
SanitizerToolName = "LeakSanitizer";
- InitializeCommonFlags();
+ InitCommonLsan(true);
InitializeAllocator();
InitTlsSize();
InitializeInterceptors();
@@ -60,12 +50,14 @@
ThreadStart(tid, GetTid());
SetCurrentThread(tid);
- Symbolizer::Init(common_flags()->external_symbolizer_path);
-
- InitCommonLsan();
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit)
Atexit(DoLeakCheck);
lsan_inited = true;
lsan_init_is_running = false;
}
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_stack_trace() {
+ GET_STACK_TRACE_FATAL;
+ stack.Print();
+}
diff --git a/lib/lsan/lsan.h b/lib/lsan/lsan.h
index 3e7f76b..53783cd 100644
--- a/lib/lsan/lsan.h
+++ b/lib/lsan/lsan.h
@@ -15,6 +15,26 @@
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
+#define GET_STACK_TRACE(max_size, fast) \
+ BufferedStackTrace stack; \
+ { \
+ uptr stack_top = 0, stack_bottom = 0; \
+ ThreadContext *t; \
+ if (fast && (t = CurrentThreadContext())) { \
+ stack_top = t->stack_end(); \
+ stack_bottom = t->stack_begin(); \
+ } \
+ stack.Unwind(max_size, StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \
+ /* context */ 0, stack_top, stack_bottom, fast); \
+ }
+
+#define GET_STACK_TRACE_FATAL \
+ GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)
+
+#define GET_STACK_TRACE_MALLOC \
+ GET_STACK_TRACE(__sanitizer::common_flags()->malloc_context_size, \
+ common_flags()->fast_unwind_on_malloc)
+
namespace __lsan {
void InitializeInterceptors();
diff --git a/lib/lsan/lsan_allocator.cc b/lib/lsan/lsan_allocator.cc
index 2340a12..8be2a2a 100644
--- a/lib/lsan/lsan_allocator.cc
+++ b/lib/lsan/lsan_allocator.cc
@@ -15,6 +15,7 @@
#include "lsan_allocator.h"
#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
@@ -53,7 +54,7 @@
allocator.SwallowCache(&cache);
}
-static ChunkMetadata *Metadata(void *p) {
+static ChunkMetadata *Metadata(const void *p) {
return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p));
}
@@ -62,7 +63,7 @@
ChunkMetadata *m = Metadata(p);
CHECK(m);
m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked;
- m->stack_trace_id = StackDepotPut(stack.trace, stack.size);
+ m->stack_trace_id = StackDepotPut(stack);
m->requested_size = size;
atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 1, memory_order_relaxed);
}
@@ -87,10 +88,12 @@
if (cleared && allocator.FromPrimary(p))
memset(p, 0, size);
RegisterAllocation(stack, p, size);
+ if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size);
return p;
}
void Deallocate(void *p) {
+ if (&__sanitizer_free_hook) __sanitizer_free_hook(p);
RegisterDeallocation(p);
allocator.Deallocate(&cache, p);
}
@@ -113,7 +116,7 @@
*end = *begin + sizeof(cache);
}
-uptr GetMallocUsableSize(void *p) {
+uptr GetMallocUsableSize(const void *p) {
ChunkMetadata *m = Metadata(p);
if (!m) return 0;
return m->requested_size;
@@ -200,3 +203,38 @@
}
}
} // namespace __lsan
+
+using namespace __lsan;
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_current_allocated_bytes() {
+ uptr stats[AllocatorStatCount];
+ allocator.GetStats(stats);
+ return stats[AllocatorStatAllocated];
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_heap_size() {
+ uptr stats[AllocatorStatCount];
+ allocator.GetStats(stats);
+ return stats[AllocatorStatMapped];
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_free_bytes() { return 0; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_unmapped_bytes() { return 0; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_get_ownership(const void *p) { return Metadata(p) != 0; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_allocated_size(const void *p) {
+ return GetMallocUsableSize(p);
+}
+} // extern "C"
diff --git a/lib/lsan/lsan_allocator.h b/lib/lsan/lsan_allocator.h
index 00c55ae..f564601 100644
--- a/lib/lsan/lsan_allocator.h
+++ b/lib/lsan/lsan_allocator.h
@@ -25,7 +25,7 @@
void Deallocate(void *p);
void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
uptr alignment);
-uptr GetMallocUsableSize(void *p);
+uptr GetMallocUsableSize(const void *p);
template<typename Callable>
void ForEachChunk(const Callable &callback);
diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc
index 92da6b0..746244c 100644
--- a/lib/lsan/lsan_common.cc
+++ b/lib/lsan/lsan_common.cc
@@ -36,15 +36,13 @@
Flags lsan_flags;
-static void InitializeFlags() {
+static void InitializeFlags(bool standalone) {
Flags *f = flags();
// Default values.
f->report_objects = false;
f->resolution = 0;
f->max_leaks = 0;
f->exitcode = 23;
- f->print_suppressions = true;
- f->suppressions="";
f->use_registers = true;
f->use_globals = true;
f->use_stacks = true;
@@ -72,9 +70,18 @@
ParseFlag(options, &f->log_pointers, "log_pointers", "");
ParseFlag(options, &f->log_threads, "log_threads", "");
ParseFlag(options, &f->exitcode, "exitcode", "");
- ParseFlag(options, &f->print_suppressions, "print_suppressions", "");
- ParseFlag(options, &f->suppressions, "suppressions", "");
}
+
+ // Set defaults for common flags (only in standalone mode) and parse
+ // them from LSAN_OPTIONS.
+ CommonFlags *cf = common_flags();
+ if (standalone) {
+ SetCommonFlagsDefaults(cf);
+ cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
+ cf->malloc_context_size = 30;
+ cf->detect_leaks = true;
+ }
+ ParseCommonFlagsFromString(cf, options);
}
#define LOG_POINTERS(...) \
@@ -87,24 +94,14 @@
if (flags()->log_threads) Report(__VA_ARGS__); \
} while (0);
-SuppressionContext *suppression_ctx;
+static bool suppressions_inited = false;
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();
- }
+ CHECK(!suppressions_inited);
+ SuppressionContext::InitIfNecessary();
if (&__lsan_default_suppressions)
- suppression_ctx->Parse(__lsan_default_suppressions());
+ SuppressionContext::Get()->Parse(__lsan_default_suppressions());
+ suppressions_inited = true;
}
struct RootRegion {
@@ -120,8 +117,8 @@
root_regions = new(placeholder) InternalMmapVector<RootRegion>(1);
}
-void InitCommonLsan() {
- InitializeFlags();
+void InitCommonLsan(bool standalone) {
+ InitializeFlags(standalone);
InitializeRootRegions();
if (common_flags()->detect_leaks) {
// Initialization which can fail or print warnings should only be done if
@@ -358,9 +355,7 @@
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);
+ StackDepotGet(stack_trace_id).Print();
}
// ForEachChunk callback. Aggregates information about unreachable chunks into
@@ -375,10 +370,9 @@
uptr resolution = flags()->resolution;
u32 stack_trace_id = 0;
if (resolution > 0) {
- uptr size = 0;
- const uptr *trace = StackDepotGet(m.stack_trace_id(), &size);
- size = Min(size, resolution);
- stack_trace_id = StackDepotPut(trace, size);
+ StackTrace stack = StackDepotGet(m.stack_trace_id());
+ stack.size = Min(stack.size, resolution);
+ stack_trace_id = StackDepotPut(stack);
} else {
stack_trace_id = m.stack_trace_id();
}
@@ -389,7 +383,7 @@
static void PrintMatchedSuppressions() {
InternalMmapVector<Suppression *> matched(1);
- suppression_ctx->GetMatched(&matched);
+ SuppressionContext::Get()->GetMatched(&matched);
if (!matched.size())
return;
const char *line = "-----------------------------------------------------";
@@ -450,12 +444,15 @@
Printf("%s", d.End());
param.leak_report.ReportTopLeaks(flags()->max_leaks);
}
- if (flags()->print_suppressions)
+ if (common_flags()->print_suppressions)
PrintMatchedSuppressions();
if (unsuppressed_count > 0) {
param.leak_report.PrintSummary();
- if (flags()->exitcode)
+ if (flags()->exitcode) {
+ if (common_flags()->coverage)
+ __sanitizer_cov_dump();
internal__exit(flags()->exitcode);
+ }
}
}
@@ -465,31 +462,32 @@
// Suppress by module name.
const char *module_name;
uptr module_offset;
- if (Symbolizer::Get()->GetModuleNameAndOffsetForPC(addr, &module_name,
- &module_offset) &&
- suppression_ctx->Match(module_name, SuppressionLeak, &s))
+ if (Symbolizer::GetOrInit()
+ ->GetModuleNameAndOffsetForPC(addr, &module_name, &module_offset) &&
+ SuppressionContext::Get()->Match(module_name, SuppressionLeak, &s))
return s;
// Suppress by file or function name.
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()->SymbolizePC(
+ uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(
addr, addr_frames.data(), kMaxAddrFrames);
for (uptr i = 0; i < addr_frames_num; i++) {
- if (suppression_ctx->Match(addr_frames[i].function, SuppressionLeak, &s) ||
- suppression_ctx->Match(addr_frames[i].file, SuppressionLeak, &s))
+ if (SuppressionContext::Get()->Match(addr_frames[i].function,
+ SuppressionLeak, &s) ||
+ SuppressionContext::Get()->Match(addr_frames[i].file, 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]));
+ StackTrace stack = StackDepotGet(stack_trace_id);
+ for (uptr i = 0; i < stack.size; i++) {
+ Suppression *s = GetSuppressionForAddr(
+ StackTrace::GetPreviousInstructionPc(stack.trace[i]));
if (s) return s;
}
return 0;
diff --git a/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h
index c0f12e6..86ff12d 100644
--- a/lib/lsan/lsan_common.h
+++ b/lib/lsan/lsan_common.h
@@ -51,10 +51,6 @@
int max_leaks;
// If nonzero kill the process with this exit code upon finding leaks.
int exitcode;
- // Print matched suppressions after leak checking.
- bool print_suppressions;
- // Suppressions file name.
- const char* suppressions;
// Flags controlling the root set of reachable memory.
// Global variables (.data and .bss).
@@ -135,7 +131,7 @@
};
// Functions called from the parent tool.
-void InitCommonLsan();
+void InitCommonLsan(bool standalone);
void DoLeakCheck();
bool DisabledInThisThread();
diff --git a/lib/lsan/lsan_common_linux.cc b/lib/lsan/lsan_common_linux.cc
index faa24d7..ba51868 100644
--- a/lib/lsan/lsan_common_linux.cc
+++ b/lib/lsan/lsan_common_linux.cc
@@ -94,11 +94,10 @@
static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) {
CHECK(stack_id);
- uptr size = 0;
- const uptr *trace = map->Get(stack_id, &size);
+ StackTrace stack = map->Get(stack_id);
// The top frame is our malloc/calloc/etc. The next frame is the caller.
- if (size >= 2)
- return trace[1];
+ if (stack.size >= 2)
+ return stack.trace[1];
return 0;
}
diff --git a/lib/lsan/lsan_interceptors.cc b/lib/lsan/lsan_interceptors.cc
index ad8ca90..b01bbf8 100644
--- a/lib/lsan/lsan_interceptors.cc
+++ b/lib/lsan/lsan_interceptors.cc
@@ -12,11 +12,11 @@
//
//===----------------------------------------------------------------------===//
+#include "interception/interception.h"
#include "sanitizer_common/sanitizer_allocator.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_interception.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_linux.h"
#include "sanitizer_common/sanitizer_platform_limits_posix.h"
@@ -34,21 +34,6 @@
int pthread_setspecific(unsigned key, const void *v);
}
-#define GET_STACK_TRACE \
- StackTrace stack; \
- { \
- uptr stack_top = 0, stack_bottom = 0; \
- ThreadContext *t; \
- bool fast = common_flags()->fast_unwind_on_malloc; \
- if (fast && (t = CurrentThreadContext())) { \
- stack_top = t->stack_end(); \
- stack_bottom = t->stack_begin(); \
- } \
- stack.Unwind(__sanitizer::common_flags()->malloc_context_size, \
- StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), 0, \
- stack_top, stack_bottom, fast); \
- }
-
#define ENSURE_LSAN_INITED do { \
CHECK(!lsan_init_is_running); \
if (!lsan_inited) \
@@ -65,7 +50,7 @@
INTERCEPTOR(void*, malloc, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
return Allocate(stack, size, 1, kAlwaysClearMemory);
}
@@ -88,32 +73,32 @@
}
if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0;
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
size *= nmemb;
return Allocate(stack, size, 1, true);
}
INTERCEPTOR(void*, realloc, void *q, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
return Reallocate(stack, q, size, 1);
}
INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
return Allocate(stack, size, alignment, kAlwaysClearMemory);
}
INTERCEPTOR(void*, aligned_alloc, uptr alignment, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
return Allocate(stack, size, alignment, kAlwaysClearMemory);
}
INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
*memptr = Allocate(stack, size, alignment, kAlwaysClearMemory);
// FIXME: Return ENOMEM if user requested more than max alloc size.
return 0;
@@ -121,7 +106,7 @@
INTERCEPTOR(void*, valloc, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
if (size == 0)
size = GetPageSizeCached();
return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory);
@@ -148,7 +133,7 @@
INTERCEPTOR(void*, pvalloc, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
uptr PageSize = GetPageSizeCached();
size = RoundUpTo(size, PageSize);
if (size == 0) {
@@ -162,7 +147,7 @@
#define OPERATOR_NEW_BODY \
ENSURE_LSAN_INITED; \
- GET_STACK_TRACE; \
+ GET_STACK_TRACE_MALLOC; \
return Allocate(stack, size, 1, kAlwaysClearMemory);
INTERCEPTOR_ATTRIBUTE
diff --git a/lib/lsan/lsan_preinit.cc b/lib/lsan/lsan_preinit.cc
index e663951..5a19095 100644
--- a/lib/lsan/lsan_preinit.cc
+++ b/lib/lsan/lsan_preinit.cc
@@ -14,11 +14,7 @@
#include "lsan.h"
-#ifndef LSAN_USE_PREINIT_ARRAY
-#define LSAN_USE_PREINIT_ARRAY 1
-#endif
-
-#if LSAN_USE_PREINIT_ARRAY && !defined(PIC)
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
// We force __lsan_init to be called before anyone else by placing it into
// .preinit_array section.
__attribute__((section(".preinit_array"), used))
diff --git a/lib/msan/CMakeLists.txt b/lib/msan/CMakeLists.txt
index 4d69a25..90d9fac 100644
--- a/lib/msan/CMakeLists.txt
+++ b/lib/msan/CMakeLists.txt
@@ -14,16 +14,15 @@
set(MSAN_RTL_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_no_rtti_flag(MSAN_RTL_CFLAGS)
-append_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE MSAN_RTL_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE MSAN_RTL_CFLAGS)
# Prevent clang from generating libc calls.
-append_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding MSAN_RTL_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding MSAN_RTL_CFLAGS)
set(MSAN_RUNTIME_LIBRARIES)
# Static runtime library.
add_custom_target(msan)
-set(arch "x86_64")
-if(CAN_TARGET_${arch})
+foreach(arch ${MSAN_SUPPORTED_ARCH})
add_compiler_rt_runtime(clang_rt.msan-${arch} ${arch} STATIC
SOURCES ${MSAN_RTL_SOURCES}
$<TARGET_OBJECTS:RTInterception.${arch}>
@@ -36,7 +35,7 @@
add_sanitizer_rt_symbols(clang_rt.msan-${arch} msan.syms.extra)
add_dependencies(msan clang_rt.msan-${arch}-symbols)
endif()
-endif()
+endforeach()
add_compiler_rt_resource_file(msan_blacklist msan_blacklist.txt)
add_dependencies(msan msan_blacklist)
diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc
index fd7fdbb..09622c4 100644
--- a/lib/msan/msan.cc
+++ b/lib/msan/msan.cc
@@ -34,27 +34,25 @@
static THREADLOCAL int msan_expect_umr = 0;
static THREADLOCAL int msan_expected_umr_found = 0;
-static bool msan_running_under_dr;
-
// Function argument shadow. Each argument starts at the next available 8-byte
// aligned address.
SANITIZER_INTERFACE_ATTRIBUTE
-THREADLOCAL u64 __msan_param_tls[kMsanParamTlsSizeInWords];
+THREADLOCAL u64 __msan_param_tls[kMsanParamTlsSize / sizeof(u64)];
// Function argument origin. Each argument starts at the same offset as the
// corresponding shadow in (__msan_param_tls). Slightly weird, but changing this
// would break compatibility with older prebuilt binaries.
SANITIZER_INTERFACE_ATTRIBUTE
-THREADLOCAL u32 __msan_param_origin_tls[kMsanParamTlsSizeInWords];
+THREADLOCAL u32 __msan_param_origin_tls[kMsanParamTlsSize / sizeof(u32)];
SANITIZER_INTERFACE_ATTRIBUTE
-THREADLOCAL u64 __msan_retval_tls[kMsanRetvalTlsSizeInWords];
+THREADLOCAL u64 __msan_retval_tls[kMsanRetvalTlsSize / sizeof(u64)];
SANITIZER_INTERFACE_ATTRIBUTE
THREADLOCAL u32 __msan_retval_origin_tls;
SANITIZER_INTERFACE_ATTRIBUTE
-THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSizeInWords];
+THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSize / sizeof(u64)];
SANITIZER_INTERFACE_ATTRIBUTE
THREADLOCAL u64 __msan_va_arg_overflow_size_tls;
@@ -63,7 +61,6 @@
THREADLOCAL u32 __msan_origin_tls;
static THREADLOCAL int is_in_symbolizer;
-static THREADLOCAL int is_in_loader;
extern "C" SANITIZER_WEAK_ATTRIBUTE const int __msan_track_origins;
@@ -79,14 +76,6 @@
void ExitSymbolizer() { --is_in_symbolizer; }
bool IsInSymbolizer() { return is_in_symbolizer; }
-void EnterLoader() { ++is_in_loader; }
-void ExitLoader() { --is_in_loader; }
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-bool __msan_is_in_loader() { return is_in_loader; }
-}
-
static Flags msan_flags;
Flags *flags() {
@@ -187,7 +176,7 @@
ParseFlagsFromString(f, options);
}
-void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp,
+void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp,
bool request_fast_unwind) {
MsanThread *t = GetCurrentThread();
if (!t || !StackTrace::WillUseFastUnwind(request_fast_unwind)) {
@@ -280,16 +269,19 @@
}
}
- StackDepotHandle h = StackDepotPut_WithHandle(stack->trace, stack->size);
+ StackDepotHandle h = StackDepotPut_WithHandle(*stack);
if (!h.valid()) return id;
- int use_count = h.use_count();
- if (use_count > flags()->origin_history_per_stack_limit)
- return id;
+
+ if (flags()->origin_history_per_stack_limit > 0) {
+ int use_count = h.use_count();
+ if (use_count > flags()->origin_history_per_stack_limit) return id;
+ }
u32 chained_id;
bool inserted = ChainedOriginDepotPut(h.id(), o.id(), &chained_id);
- if (inserted) h.inc_use_count_unsafe();
+ if (inserted && flags()->origin_history_per_stack_limit > 0)
+ h.inc_use_count_unsafe();
return Origin(chained_id, depth).raw_id();
}
@@ -377,6 +369,7 @@
if (MSAN_REPLACE_OPERATORS_NEW_AND_DELETE)
ReplaceOperatorsNewAndDelete();
+ DisableCoreDumperIfNecessary();
if (StackSizeIsUnlimited()) {
VPrintf(1, "Unlimited stack, doing reexec\n");
// A reasonably large stack size. It is bigger than the usual 8Mb, because,
@@ -390,7 +383,7 @@
__msan_clear_on_return();
if (__msan_get_track_origins())
VPrintf(1, "msan_track_origins\n");
- if (!InitShadow(/* prot1 */ !msan_running_under_dr, /* prot2 */ true,
+ if (!InitShadow(/* prot1 */ true, /* prot2 */ true,
/* map_shadow */ true, __msan_get_track_origins())) {
Printf("FATAL: MemorySanitizer can not mmap the shadow memory.\n");
Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
@@ -401,8 +394,7 @@
Die();
}
- Symbolizer::Init(common_flags()->external_symbolizer_path);
- Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer);
+ Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer);
MsanTSDInit(MsanTSDDtor);
@@ -482,7 +474,7 @@
(void)sp;
ReportUMRInsideAddressRange(__func__, x, size, offset);
__msan::PrintWarningWithOrigin(pc, bp,
- __msan_get_origin(((char *)x) + offset));
+ __msan_get_origin(((const char *)x) + offset));
if (__msan::flags()->halt_on_error) {
Printf("Exiting\n");
Die();
@@ -495,40 +487,13 @@
return old;
}
-int __msan_has_dynamic_component() {
- return msan_running_under_dr;
-}
+int __msan_has_dynamic_component() { return false; }
NOINLINE
void __msan_clear_on_return() {
__msan_param_tls[0] = 0;
}
-static void* get_tls_base() {
- u64 p;
- asm("mov %%fs:0, %0"
- : "=r"(p) ::);
- return (void*)p;
-}
-
-int __msan_get_retval_tls_offset() {
- // volatile here is needed to avoid UB, because the compiler thinks that we
- // are doing address arithmetics on unrelated pointers, and takes some
- // shortcuts
- volatile sptr retval_tls_p = (sptr)&__msan_retval_tls;
- volatile sptr tls_base_p = (sptr)get_tls_base();
- return retval_tls_p - tls_base_p;
-}
-
-int __msan_get_param_tls_offset() {
- // volatile here is needed to avoid UB, because the compiler thinks that we
- // are doing address arithmetics on unrelated pointers, and takes some
- // shortcuts
- volatile sptr param_tls_p = (sptr)&__msan_param_tls;
- volatile sptr tls_base_p = (sptr)get_tls_base();
- return param_tls_p - tls_base_p;
-}
-
void __msan_partial_poison(const void* data, void* shadow, uptr size) {
internal_memcpy((void*)MEM_TO_SHADOW((uptr)data), shadow, size);
}
@@ -562,11 +527,11 @@
// 'descr' is created at compile time and contains '----' in the beginning.
// 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) {
+void __msan_set_alloca_origin(void *a, uptr size, char *descr) {
__msan_set_alloca_origin4(a, size, descr, 0);
}
-void __msan_set_alloca_origin4(void *a, uptr size, const char *descr, uptr pc) {
+void __msan_set_alloca_origin4(void *a, uptr size, char *descr, uptr pc) {
static const u32 dash = '-';
static const u32 first_timer =
dash + (dash << 8) + (dash << 16) + (dash << 24);
@@ -654,18 +619,6 @@
death_callback = callback;
}
-void *__msan_wrap_indirect_call(void *target) {
- return IndirectExternCall(target);
-}
-
-void __msan_dr_is_initialized() {
- msan_running_under_dr = true;
-}
-
-void __msan_set_indirect_call_wrapper(uptr wrapper) {
- SetIndirectCallWrapper(wrapper);
-}
-
#if !SANITIZER_SUPPORTS_WEAK_HOOKS
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
diff --git a/lib/msan/msan.h b/lib/msan/msan.h
index 05a8c47..aed8738 100644
--- a/lib/msan/msan.h
+++ b/lib/msan/msan.h
@@ -25,15 +25,25 @@
# define MSAN_REPLACE_OPERATORS_NEW_AND_DELETE 1
#endif
+#if defined(__mips64)
+#define MEM_TO_SHADOW(mem) (((uptr)mem) & ~0x4000000000ULL)
+#define SHADOW_TO_ORIGIN(shadow) (((uptr)shadow) + 0x2000000000ULL)
+#define MEM_TO_ORIGIN(mem) (SHADOW_TO_ORIGIN(MEM_TO_SHADOW(mem)))
+#define MEM_IS_APP(mem) ((uptr)mem >= 0xe000000000ULL)
+#define MEM_IS_SHADOW(mem) \
+ ((uptr)mem >= 0xa000000000ULL && (uptr)mem <= 0xc000000000ULL)
+#elif defined(__x86_64__)
#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)
+#endif
-const int kMsanParamTlsSizeInWords = 100;
-const int kMsanRetvalTlsSizeInWords = 100;
+// These constants must be kept in sync with the ones in MemorySanitizer.cc.
+const int kMsanParamTlsSize = 800;
+const int kMsanRetvalTlsSize = 800;
namespace __msan {
extern int msan_inited;
@@ -64,14 +74,11 @@
~SymbolizerScope() { ExitSymbolizer(); }
};
-void EnterLoader();
-void ExitLoader();
-
void MsanDie();
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,
+void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp,
bool request_fast_unwind);
void ReportUMR(StackTrace *stack, u32 origin);
@@ -96,27 +103,24 @@
// the previous origin id.
u32 ChainOrigin(u32 id, StackTrace *stack);
-#define GET_MALLOC_STACK_TRACE \
- StackTrace stack; \
- stack.size = 0; \
- if (__msan_get_track_origins() && msan_inited) \
- GetStackTrace(&stack, common_flags()->malloc_context_size, \
- StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \
- common_flags()->fast_unwind_on_malloc)
-
-#define GET_STORE_STACK_TRACE_PC_BP(pc, bp) \
- StackTrace stack; \
- stack.size = 0; \
- if (__msan_get_track_origins() > 1 && msan_inited) \
- GetStackTrace(&stack, flags()->store_context_size, pc, bp, \
+#define GET_MALLOC_STACK_TRACE \
+ BufferedStackTrace stack; \
+ if (__msan_get_track_origins() && msan_inited) \
+ GetStackTrace(&stack, common_flags()->malloc_context_size, \
+ StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \
common_flags()->fast_unwind_on_malloc)
-#define GET_FATAL_STACK_TRACE_PC_BP(pc, bp) \
- StackTrace stack; \
- stack.size = 0; \
- if (msan_inited) \
- GetStackTrace(&stack, kStackTraceMax, pc, bp, \
- common_flags()->fast_unwind_on_fatal)
+#define GET_STORE_STACK_TRACE_PC_BP(pc, bp) \
+ BufferedStackTrace stack; \
+ if (__msan_get_track_origins() > 1 && msan_inited) \
+ GetStackTrace(&stack, flags()->store_context_size, pc, bp, \
+ common_flags()->fast_unwind_on_malloc)
+
+#define GET_FATAL_STACK_TRACE_PC_BP(pc, bp) \
+ BufferedStackTrace stack; \
+ if (msan_inited) \
+ GetStackTrace(&stack, kStackTraceMax, pc, bp, \
+ common_flags()->fast_unwind_on_fatal)
#define GET_STORE_STACK_TRACE \
GET_STORE_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME())
@@ -141,10 +145,8 @@
} // namespace __msan
#define MSAN_MALLOC_HOOK(ptr, size) \
- if (&__msan_malloc_hook) __msan_malloc_hook(ptr, size); \
if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size)
#define MSAN_FREE_HOOK(ptr) \
- if (&__msan_free_hook) __msan_free_hook(ptr); \
if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr)
#endif // MSAN_H
diff --git a/lib/msan/msan_allocator.cc b/lib/msan/msan_allocator.cc
index fb1788f..aa1ea1d 100644
--- a/lib/msan/msan_allocator.cc
+++ b/lib/msan/msan_allocator.cc
@@ -40,14 +40,26 @@
}
};
-static const uptr kAllocatorSpace = 0x600000000000ULL;
-static const uptr kAllocatorSize = 0x80000000000; // 8T.
-static const uptr kMetadataSize = sizeof(Metadata);
-static const uptr kMaxAllowedMallocSize = 8UL << 30;
+#if defined(__mips64)
+ static const uptr kMaxAllowedMallocSize = 2UL << 30;
+ static const uptr kRegionSizeLog = 20;
+ static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
+ typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
+ typedef CompactSizeClassMap SizeClassMap;
-typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize,
+ typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, sizeof(Metadata),
+ SizeClassMap, kRegionSizeLog, ByteMap,
+ MsanMapUnmapCallback> PrimaryAllocator;
+#elif defined(__x86_64__)
+ 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,
MsanMapUnmapCallback> PrimaryAllocator;
+#endif
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
typedef LargeMmapAllocator<MsanMapUnmapCallback> SecondaryAllocator;
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
@@ -102,7 +114,7 @@
} else if (flags()->poison_in_malloc) {
__msan_poison(allocated, size);
if (__msan_get_track_origins()) {
- u32 stack_id = StackDepotPut(stack->trace, stack->size);
+ u32 stack_id = StackDepotPut(*stack);
CHECK(stack_id);
u32 id;
ChainedOriginDepotPut(stack_id, Origin::kHeapRoot, &id);
@@ -125,7 +137,7 @@
if (flags()->poison_in_free) {
__msan_poison(p, size);
if (__msan_get_track_origins()) {
- u32 stack_id = StackDepotPut(stack->trace, stack->size);
+ u32 stack_id = StackDepotPut(*stack);
CHECK(stack_id);
u32 id;
ChainedOriginDepotPut(stack_id, Origin::kHeapRoot, &id);
@@ -188,40 +200,19 @@
allocator.GetStats(stats);
return stats[AllocatorStatAllocated];
}
-uptr __msan_get_current_allocated_bytes() {
- return __sanitizer_get_current_allocated_bytes();
-}
uptr __sanitizer_get_heap_size() {
uptr stats[AllocatorStatCount];
allocator.GetStats(stats);
return stats[AllocatorStatMapped];
}
-uptr __msan_get_heap_size() {
- return __sanitizer_get_heap_size();
-}
uptr __sanitizer_get_free_bytes() { return 1; }
-uptr __msan_get_free_bytes() {
- return __sanitizer_get_free_bytes();
-}
uptr __sanitizer_get_unmapped_bytes() { return 1; }
-uptr __msan_get_unmapped_bytes() {
- return __sanitizer_get_unmapped_bytes();
-}
uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
-uptr __msan_get_estimated_allocated_size(uptr size) {
- return __sanitizer_get_estimated_allocated_size(size);
-}
int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; }
-int __msan_get_ownership(const void *p) {
- return __sanitizer_get_ownership(p);
-}
uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); }
-uptr __msan_get_allocated_size(const void *p) {
- return __sanitizer_get_allocated_size(p);
-}
diff --git a/lib/msan/msan_chained_origin_depot.cc b/lib/msan/msan_chained_origin_depot.cc
index faf0461..f3fb3c8 100644
--- a/lib/msan/msan_chained_origin_depot.cc
+++ b/lib/msan/msan_chained_origin_depot.cc
@@ -19,31 +19,6 @@
struct ChainedOriginDepotDesc {
u32 here_id;
u32 prev_id;
- u32 hash() const {
- const u32 m = 0x5bd1e995;
- const u32 seed = 0x9747b28c;
- const u32 r = 24;
- u32 h = seed;
- u32 k = here_id;
- k *= m;
- k ^= k >> r;
- k *= m;
- h *= m;
- h ^= k;
-
- k = prev_id;
- k *= m;
- k ^= k >> r;
- k *= m;
- h *= m;
- h ^= k;
-
- h ^= h >> 13;
- h *= m;
- h ^= h >> 15;
- return h;
- }
- bool is_valid() { return true; }
};
struct ChainedOriginDepotNode {
@@ -59,6 +34,44 @@
static uptr storage_size(const args_type &args) {
return sizeof(ChainedOriginDepotNode);
}
+ /* This is murmur2 hash for the 64->32 bit case.
+ It does not behave all that well because the keys have a very biased
+ distribution (I've seen 7-element buckets with the table only 14% full).
+
+ here_id is built of
+ * (1 bits) Reserved, zero.
+ * (8 bits) Part id = bits 13..20 of the hash value of here_id's key.
+ * (23 bits) Sequential number (each part has each own sequence).
+
+ prev_id has either the same distribution as here_id (but with 3:8:21)
+ split, or one of two reserved values (-1) or (-2). Either case can
+ dominate depending on the workload.
+ */
+ static u32 hash(const args_type &args) {
+ const u32 m = 0x5bd1e995;
+ const u32 seed = 0x9747b28c;
+ const u32 r = 24;
+ u32 h = seed;
+ u32 k = args.here_id;
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+ h *= m;
+ h ^= k;
+
+ k = args.prev_id;
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+ h *= m;
+ h ^= k;
+
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+ return h;
+ }
+ static bool is_valid(const args_type &args) { return true; }
void store(const args_type &args, u32 other_hash) {
here_id = args.here_id;
prev_id = args.prev_id;
@@ -103,4 +116,12 @@
return desc.here_id;
}
+void ChainedOriginDepotLockAll() {
+ chainedOriginDepot.LockAll();
+}
+
+void ChainedOriginDepotUnlockAll() {
+ chainedOriginDepot.UnlockAll();
+}
+
} // namespace __msan
diff --git a/lib/msan/msan_chained_origin_depot.h b/lib/msan/msan_chained_origin_depot.h
index db427b0..f7a71ce 100644
--- a/lib/msan/msan_chained_origin_depot.h
+++ b/lib/msan/msan_chained_origin_depot.h
@@ -21,6 +21,9 @@
// Retrieves a stored stack trace by the id.
u32 ChainedOriginDepotGet(u32 id, u32 *other);
+void ChainedOriginDepotLockAll();
+void ChainedOriginDepotUnlockAll();
+
} // namespace __msan
#endif // MSAN_CHAINED_ORIGIN_DEPOT_H
diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc
index 3394690..aa6b1ff 100644
--- a/lib/msan/msan_interceptors.cc
+++ b/lib/msan/msan_interceptors.cc
@@ -15,6 +15,7 @@
// sanitizer_common/sanitizer_common_interceptors.h
//===----------------------------------------------------------------------===//
+#include "interception/interception.h"
#include "msan.h"
#include "msan_chained_origin_depot.h"
#include "msan_origin.h"
@@ -25,7 +26,6 @@
#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_interception.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_linux.h"
@@ -64,21 +64,22 @@
} 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; \
- ReportUMRInsideAddressRange(__func__, x, n, offset); \
- __msan::PrintWarningWithOrigin(pc, bp, \
- __msan_get_origin((char *)x + offset)); \
- if (__msan::flags()->halt_on_error) { \
- 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; \
+ ReportUMRInsideAddressRange(__func__, x, n, offset); \
+ __msan::PrintWarningWithOrigin( \
+ pc, bp, __msan_get_origin((const 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
@@ -88,9 +89,6 @@
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);
-
INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) {
ENSURE_MSAN_INITED();
SIZE_T res = REAL(fread)(ptr, size, nmemb, file);
@@ -263,6 +261,7 @@
copy_size++; // trailing \0
char *res = REAL(strncpy)(dest, src, n); // NOLINT
CopyPoison(dest, src, copy_size, &stack);
+ __msan_unpoison(dest + copy_size, n - copy_size);
return res;
}
@@ -316,11 +315,8 @@
INTERCEPTOR(char *, gcvt, double number, SIZE_T ndigit, char *buf) {
ENSURE_MSAN_INITED();
char *res = REAL(gcvt)(number, ndigit, buf);
- // DynamoRio tool will take care of unpoisoning gcvt result for us.
- if (!__msan_has_dynamic_component()) {
- SIZE_T n = REAL(strlen)(buf);
- __msan_unpoison(buf, n + 1);
- }
+ SIZE_T n = REAL(strlen)(buf);
+ __msan_unpoison(buf, n + 1);
return res;
}
@@ -350,9 +346,7 @@
#define INTERCEPTOR_STRTO_BODY(ret_type, func, ...) \
ENSURE_MSAN_INITED(); \
ret_type res = REAL(func)(__VA_ARGS__); \
- if (!__msan_has_dynamic_component()) { \
- __msan_unpoison(endptr, sizeof(*endptr)); \
- } \
+ __msan_unpoison(endptr, sizeof(*endptr)); \
return res;
#define INTERCEPTOR_STRTO(ret_type, func) \
@@ -409,7 +403,7 @@
INTERCEPTOR(int, vswprintf, void *str, uptr size, void *format, va_list ap) {
ENSURE_MSAN_INITED();
int res = REAL(vswprintf)(str, size, format, ap);
- if (res >= 0 && !__msan_has_dynamic_component()) {
+ if (res >= 0) {
__msan_unpoison(str, 4 * (res + 1));
}
return res;
@@ -537,7 +531,7 @@
INTERCEPTOR(wchar_t *, wmemset, wchar_t *s, wchar_t c, SIZE_T n) {
CHECK(MEM_IS_APP(s));
ENSURE_MSAN_INITED();
- wchar_t *res = (wchar_t *)fast_memset(s, c, n * sizeof(wchar_t));
+ wchar_t *res = (wchar_t *)REAL(memset)(s, c, n * sizeof(wchar_t));
__msan_unpoison(s, n * sizeof(wchar_t));
return res;
}
@@ -576,20 +570,16 @@
INTERCEPTOR(char *, fcvt, double x, int a, int *b, int *c) {
ENSURE_MSAN_INITED();
char *res = REAL(fcvt)(x, a, b, c);
- if (!__msan_has_dynamic_component()) {
- __msan_unpoison(b, sizeof(*b));
- __msan_unpoison(c, sizeof(*c));
- }
+ __msan_unpoison(b, sizeof(*b));
+ __msan_unpoison(c, sizeof(*c));
+ if (res) __msan_unpoison(res, REAL(strlen)(res) + 1);
return res;
}
INTERCEPTOR(char *, getenv, char *name) {
ENSURE_MSAN_INITED();
char *res = REAL(getenv)(name);
- if (!__msan_has_dynamic_component()) {
- if (res)
- __msan_unpoison(res, REAL(strlen)(res) + 1);
- }
+ if (res) __msan_unpoison(res, REAL(strlen)(res) + 1);
return res;
}
@@ -843,7 +833,7 @@
if (flags()->poison_in_malloc)
__msan_poison(data, size);
if (__msan_get_track_origins()) {
- u32 stack_id = StackDepotPut(stack.trace, stack.size);
+ u32 stack_id = StackDepotPut(stack);
u32 id;
ChainedOriginDepotPut(stack_id, Origin::kHeapRoot, &id);
__msan_set_origin(data, size, Origin(id, 1).raw_id());
@@ -927,17 +917,15 @@
}
dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data;
UnpoisonParam(3);
- return IndirectExternCall(cbdata->callback)(info, size, cbdata->data);
+ return cbdata->callback(info, size, cbdata->data);
}
INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb callback, void *data) {
ENSURE_MSAN_INITED();
- EnterLoader();
dl_iterate_phdr_data cbdata;
cbdata.callback = callback;
cbdata.data = data;
int res = REAL(dl_iterate_phdr)(msan_dl_iterate_phdr_cb, (void *)&cbdata);
- ExitLoader();
return res;
}
@@ -977,7 +965,7 @@
typedef void (*signal_cb)(int x);
signal_cb cb =
(signal_cb)atomic_load(&sigactions[signo], memory_order_relaxed);
- IndirectExternCall(cb)(signo);
+ cb(signo);
}
static void SignalAction(int signo, void *si, void *uc) {
@@ -990,7 +978,7 @@
typedef void (*sigaction_cb)(int, void *, void *);
sigaction_cb cb =
(sigaction_cb)atomic_load(&sigactions[signo], memory_order_relaxed);
- IndirectExternCall(cb)(signo, si, uc);
+ cb(signo, si, uc);
}
INTERCEPTOR(int, sigaction, int signo, const __sanitizer_sigaction *act,
@@ -1006,7 +994,7 @@
__sanitizer_sigaction new_act;
__sanitizer_sigaction *pnew_act = act ? &new_act : 0;
if (act) {
- internal_memcpy(pnew_act, act, sizeof(__sanitizer_sigaction));
+ REAL(memcpy)(pnew_act, act, sizeof(__sanitizer_sigaction));
uptr cb = (uptr)pnew_act->sigaction;
uptr new_cb = (pnew_act->sa_flags & __sanitizer::sa_siginfo)
? (uptr)SignalAction
@@ -1118,7 +1106,7 @@
void MSanAtExitWrapper(void *arg) {
UnpoisonParam(1);
MSanAtExitRecord *r = (MSanAtExitRecord *)arg;
- IndirectExternCall(r->func)(r->arg);
+ r->func(r->arg);
InternalFree(r);
}
@@ -1149,34 +1137,22 @@
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;
- VPrintf(1,
- "INFO: MemorySanitizer ignores mlock/mlockall/munlock/munlockall\n");
+static void BeforeFork() {
+ StackDepotLockAll();
+ ChainedOriginDepotLockAll();
}
-INTERCEPTOR(int, mlock, const void *addr, uptr len) {
- MlockIsUnsupported();
- return 0;
+static void AfterFork() {
+ ChainedOriginDepotUnlockAll();
+ StackDepotUnlockAll();
}
-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;
+INTERCEPTOR(int, fork, void) {
+ ENSURE_MSAN_INITED();
+ BeforeFork();
+ int pid = REAL(fork)();
+ AfterFork();
+ return pid;
}
struct MSanInterceptorContext {
@@ -1240,12 +1216,8 @@
} while (false) // FIXME
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
-#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, map) \
- if (!__msan_has_dynamic_component() && map) { \
- /* If msandr didn't clear the shadow before the initializers ran, we do */ \
- /* it ourselves afterwards. */ \
- ForEachMappedRegion((link_map *)map, __msan_unpoison); \
- }
+#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, map) \
+ if (map) ForEachMappedRegion((link_map *)map, __msan_unpoison);
#include "sanitizer_common/sanitizer_common_interceptors.inc"
@@ -1259,59 +1231,25 @@
#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) __msan_unpoison(p, s)
#include "sanitizer_common/sanitizer_common_syscalls.inc"
-// static
-void *fast_memset(void *ptr, int c, SIZE_T n) {
- // hack until we have a really fast internal_memset
- if (sizeof(uptr) == 8 &&
- (n % 8) == 0 &&
- ((uptr)ptr % 8) == 0) {
- uptr c8 = (unsigned)c & 0xFF;
- c8 = (c8 << 8) | c8;
- c8 = (c8 << 16) | c8;
- c8 = (c8 << 32) | c8;
- uptr *p = (uptr*)ptr;
- for (SIZE_T i = 0; i < n / 8; i++)
- p[i] = c8;
- return ptr;
- }
- return internal_memset(ptr, c, n);
-}
-
-// static
-void *fast_memcpy(void *dst, const void *src, SIZE_T n) {
- // Same hack as in fast_memset above.
- if (sizeof(uptr) == 8 &&
- (n % 8) == 0 &&
- ((uptr)dst % 8) == 0 &&
- ((uptr)src % 8) == 0) {
- uptr *d = (uptr*)dst;
- uptr *s = (uptr*)src;
- for (SIZE_T i = 0; i < n / 8; i++)
- d[i] = s[i];
- return dst;
- }
- return internal_memcpy(dst, src, n);
-}
-
static void PoisonShadow(uptr ptr, uptr size, u8 value) {
uptr PageSize = GetPageSizeCached();
uptr shadow_beg = MEM_TO_SHADOW(ptr);
uptr shadow_end = MEM_TO_SHADOW(ptr + size);
if (value ||
shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
- fast_memset((void*)shadow_beg, value, shadow_end - shadow_beg);
+ REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
} else {
uptr page_beg = RoundUpTo(shadow_beg, PageSize);
uptr page_end = RoundDownTo(shadow_end, PageSize);
if (page_beg >= page_end) {
- fast_memset((void *)shadow_beg, 0, shadow_end - shadow_beg);
+ REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg);
} else {
if (page_beg != shadow_beg) {
- fast_memset((void *)shadow_beg, 0, page_beg - shadow_beg);
+ REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg);
}
if (page_end != shadow_end) {
- fast_memset((void *)page_end, 0, shadow_end - page_end);
+ REAL(memset)((void *)page_end, 0, shadow_end - page_end);
}
MmapFixedNoReserve(page_beg, page_end - page_beg);
}
@@ -1319,7 +1257,7 @@
}
// These interface functions reside here so that they can use
-// fast_memset, etc.
+// REAL(memset), etc.
void __msan_unpoison(const void *a, uptr size) {
if (!MEM_IS_APP(a)) return;
PoisonShadow((uptr)a, size, 0);
@@ -1338,7 +1276,7 @@
}
void __msan_clear_and_unpoison(void *a, uptr size) {
- fast_memset(a, 0, size);
+ REAL(memset)(a, 0, size);
PoisonShadow((uptr)a, size, 0);
}
@@ -1347,7 +1285,7 @@
if (msan_init_is_running) return REAL(memcpy)(dest, src, n);
ENSURE_MSAN_INITED();
GET_STORE_STACK_TRACE;
- void *res = fast_memcpy(dest, src, n);
+ void *res = REAL(memcpy)(dest, src, n);
CopyPoison(dest, src, n, &stack);
return res;
}
@@ -1356,7 +1294,7 @@
if (!msan_inited) return internal_memset(s, c, n);
if (msan_init_is_running) return REAL(memset)(s, c, n);
ENSURE_MSAN_INITED();
- void *res = fast_memset(s, c, n);
+ void *res = REAL(memset)(s, c, n);
__msan_unpoison(s, n);
return res;
}
@@ -1445,7 +1383,7 @@
*dst = dst_o;
}
} else {
- fast_memcpy((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s),
+ REAL(memcpy)((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s),
end - beg);
}
}
@@ -1455,15 +1393,15 @@
if (!MEM_IS_APP(dst)) return;
if (!MEM_IS_APP(src)) return;
if (src == dst) return;
- internal_memmove((void *)MEM_TO_SHADOW((uptr)dst),
- (void *)MEM_TO_SHADOW((uptr)src), size);
+ REAL(memmove)((void *)MEM_TO_SHADOW((uptr)dst),
+ (void *)MEM_TO_SHADOW((uptr)src), size);
CopyOrigin(dst, src, size, stack);
}
void CopyPoison(void *dst, const void *src, uptr size, StackTrace *stack) {
if (!MEM_IS_APP(dst)) return;
if (!MEM_IS_APP(src)) return;
- fast_memcpy((void *)MEM_TO_SHADOW((uptr)dst),
+ REAL(memcpy)((void *)MEM_TO_SHADOW((uptr)dst),
(void *)MEM_TO_SHADOW((uptr)src), size);
CopyOrigin(dst, src, size, stack);
}
@@ -1597,6 +1535,7 @@
INTERCEPT_FUNCTION(tzset);
INTERCEPT_FUNCTION(__cxa_atexit);
INTERCEPT_FUNCTION(shmat);
+ INTERCEPT_FUNCTION(fork);
inited = 1;
}
diff --git a/lib/msan/msan_interface_internal.h b/lib/msan/msan_interface_internal.h
index 47b47dc..8641f81 100644
--- a/lib/msan/msan_interface_internal.h
+++ b/lib/msan/msan_interface_internal.h
@@ -88,9 +88,9 @@
SANITIZER_INTERFACE_ATTRIBUTE
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);
+void __msan_set_alloca_origin(void *a, uptr size, char *descr);
SANITIZER_INTERFACE_ATTRIBUTE
-void __msan_set_alloca_origin4(void *a, uptr size, const char *descr, uptr pc);
+void __msan_set_alloca_origin4(void *a, uptr size, char *descr, uptr pc);
SANITIZER_INTERFACE_ATTRIBUTE
u32 __msan_chain_origin(u32 id);
SANITIZER_INTERFACE_ATTRIBUTE
@@ -122,16 +122,6 @@
SANITIZER_INTERFACE_ATTRIBUTE
int __msan_has_dynamic_component();
-// Returns x such that %fs:x is the first byte of __msan_retval_tls.
-SANITIZER_INTERFACE_ATTRIBUTE
-int __msan_get_retval_tls_offset();
-SANITIZER_INTERFACE_ATTRIBUTE
-int __msan_get_param_tls_offset();
-
-// For intercepting mmap from ld.so in msandr.
-SANITIZER_INTERFACE_ATTRIBUTE
-bool __msan_is_in_loader();
-
// For testing.
SANITIZER_INTERFACE_ATTRIBUTE
u32 __msan_get_umr_origin();
@@ -161,37 +151,6 @@
SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_unaligned_store64(uu64 *p, u64 x);
-// ---------------------------
-// FIXME: Replace these functions with __sanitizer equivalent.
-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);
-// ---------------------------
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __msan_dr_is_initialized();
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *__msan_wrap_indirect_call(void *target);
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __msan_set_indirect_call_wrapper(uptr wrapper);
-
SANITIZER_INTERFACE_ATTRIBUTE
void __msan_set_death_callback(void (*callback)(void));
} // extern "C"
diff --git a/lib/msan/msan_linux.cc b/lib/msan/msan_linux.cc
index a8fbabb..2a970c0 100644
--- a/lib/msan/msan_linux.cc
+++ b/lib/msan/msan_linux.cc
@@ -35,8 +35,14 @@
namespace __msan {
+#if defined(__mips64)
+static const uptr kMemBeg = 0xe000000000;
+static const uptr kMemEnd = 0xffffffffff;
+#elif defined(__x86_64__)
static const uptr kMemBeg = 0x600000000000;
static const uptr kMemEnd = 0x7fffffffffff;
+#endif
+
static const uptr kShadowBeg = MEM_TO_SHADOW(kMemBeg);
static const uptr kShadowEnd = MEM_TO_SHADOW(kMemEnd);
static const uptr kBad1Beg = 0;
diff --git a/lib/msan/msan_new_delete.cc b/lib/msan/msan_new_delete.cc
index 3bae49c..9a8e56e 100644
--- a/lib/msan/msan_new_delete.cc
+++ b/lib/msan/msan_new_delete.cc
@@ -13,7 +13,7 @@
//===----------------------------------------------------------------------===//
#include "msan.h"
-#include "sanitizer_common/sanitizer_interception.h"
+#include "interception/interception.h"
#if MSAN_REPLACE_OPERATORS_NEW_AND_DELETE
diff --git a/lib/msan/msan_report.cc b/lib/msan/msan_report.cc
index 85e61e2..f4978c7 100644
--- a/lib/msan/msan_report.cc
+++ b/lib/msan/msan_report.cc
@@ -46,15 +46,15 @@
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(),
- Symbolizer::Get()->Demangle(sep + 1), d.Origin(), d.End());
+ d.Origin(), d.Name(), s, d.Origin(), d.Name(), 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);
+ pc = StackTrace::GetNextInstructionPc(pc);
+ StackTrace(&pc, 1).Print();
}
}
@@ -77,20 +77,16 @@
DescribeStackOrigin(so, pc);
break;
} else if (prev_o.isHeapRoot()) {
- uptr size = 0;
- const uptr *trace = StackDepotGet(stack_id, &size);
Printf(" %sUninitialized value was created by a heap allocation%s\n",
d.Origin(), d.End());
- StackTrace::PrintStack(trace, size);
+ StackDepotGet(stack_id).Print();
break;
} else {
// chained origin
- uptr size = 0;
- const uptr *trace = StackDepotGet(stack_id, &size);
// FIXME: copied? modified? passed through? observed?
Printf(" %sUninitialized value was stored to memory at%s\n", d.Origin(),
d.End());
- StackTrace::PrintStack(trace, size);
+ StackDepotGet(stack_id).Print();
id = prev_id;
}
}
diff --git a/lib/msan/msan_thread.cc b/lib/msan/msan_thread.cc
index 5fe99f6..2a1e05a 100644
--- a/lib/msan/msan_thread.cc
+++ b/lib/msan/msan_thread.cc
@@ -73,7 +73,7 @@
return 0;
}
- thread_return_t res = IndirectExternCall(start_routine_)(arg_);
+ thread_return_t res = start_routine_(arg_);
return res;
}
diff --git a/lib/msan/tests/CMakeLists.txt b/lib/msan/tests/CMakeLists.txt
index c654615..f3c11ba 100644
--- a/lib/msan/tests/CMakeLists.txt
+++ b/lib/msan/tests/CMakeLists.txt
@@ -48,7 +48,7 @@
-lstdc++
)
-append_if(COMPILER_RT_HAS_LIBDL -ldl MSAN_UNITTEST_LINK_FLAGS)
+append_list_if(COMPILER_RT_HAS_LIBDL -ldl MSAN_UNITTEST_LINK_FLAGS)
set(MSAN_LOADABLE_LINK_FLAGS
-fsanitize=memory
-shared
diff --git a/lib/msan/tests/msan_loadable.cc b/lib/msan/tests/msan_loadable.cc
index db3bf48..06e880f 100644
--- a/lib/msan/tests/msan_loadable.cc
+++ b/lib/msan/tests/msan_loadable.cc
@@ -20,24 +20,6 @@
// No name mangling.
extern "C" {
-__attribute__((constructor))
-void loadable_module_init(void) {
- if (!__msan_has_dynamic_component())
- return;
- // The real test is that this compare should not make an uninit.
- if (dso_global == NULL)
- dso_global = malloc(4);
-}
-
-__attribute__((destructor))
-void loadable_module_fini(void) {
- if (!__msan_has_dynamic_component())
- return;
- free(dso_global);
- // *Don't* overwrite it with NULL! That would unpoison it, but our test
- // relies on reloading at the same address and keeping the poison.
-}
-
void **get_dso_global() {
return &dso_global;
}
diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc
index f7268d6..12012a0 100644
--- a/lib/msan/tests/msan_test.cc
+++ b/lib/msan/tests/msan_test.cc
@@ -251,7 +251,6 @@
TEST(MemorySanitizer, CallAndRet) {
- if (!__msan_has_dynamic_component()) return;
ReturnPoisoned<S1>();
ReturnPoisoned<S2>();
ReturnPoisoned<S4>();
@@ -494,14 +493,12 @@
static char *DynRetTestStr;
TEST(MemorySanitizer, DynRet) {
- if (!__msan_has_dynamic_component()) return;
ReturnPoisoned<S8>();
EXPECT_NOT_POISONED(clearenv());
}
TEST(MemorySanitizer, DynRet1) {
- if (!__msan_has_dynamic_component()) return;
ReturnPoisoned<S8>();
}
@@ -570,7 +567,7 @@
EXPECT_NOT_POISONED(x[16]);
EXPECT_NOT_POISONED(x[31]);
fclose(f);
- delete x;
+ delete[] x;
}
TEST(MemorySanitizer, read) {
@@ -583,7 +580,7 @@
EXPECT_NOT_POISONED(x[16]);
EXPECT_NOT_POISONED(x[31]);
close(fd);
- delete x;
+ delete[] x;
}
TEST(MemorySanitizer, pread) {
@@ -596,7 +593,7 @@
EXPECT_NOT_POISONED(x[16]);
EXPECT_NOT_POISONED(x[31]);
close(fd);
- delete x;
+ delete[] x;
}
TEST(MemorySanitizer, readv) {
@@ -1452,13 +1449,8 @@
x[2] = 0;
memmove(x, x + 1, (size - 1) * sizeof(T));
EXPECT_NOT_POISONED(x[1]);
- if (!__msan_has_dynamic_component()) {
- // FIXME: under DR we will lose this information
- // because accesses in memmove will unpoisin the shadow.
- // We need to use our own memove implementation instead of libc's.
- EXPECT_POISONED(x[0]);
- EXPECT_POISONED(x[2]);
- }
+ EXPECT_POISONED(x[0]);
+ EXPECT_POISONED(x[2]);
delete [] x;
}
@@ -1483,14 +1475,16 @@
TEST(MemorySanitizer, strncpy) { // NOLINT
char* x = new char[3];
- char* y = new char[3];
+ char* y = new char[5];
x[0] = 'a';
x[1] = *GetPoisoned<char>(1, 1);
- x[2] = 0;
- strncpy(y, x, 2); // NOLINT
+ x[2] = '\0';
+ strncpy(y, x, 4); // NOLINT
EXPECT_NOT_POISONED(y[0]);
EXPECT_POISONED(y[1]);
- EXPECT_POISONED(y[2]);
+ EXPECT_NOT_POISONED(y[2]);
+ EXPECT_NOT_POISONED(y[3]);
+ EXPECT_POISONED(y[4]);
}
TEST(MemorySanitizer, stpcpy) { // NOLINT
@@ -2062,8 +2056,26 @@
char *str = fcvt(12345.6789, 10, &a, &b);
EXPECT_NOT_POISONED(a);
EXPECT_NOT_POISONED(b);
+ ASSERT_NE(nullptr, str);
+ EXPECT_NOT_POISONED(str[0]);
+ ASSERT_NE(0U, strlen(str));
}
+TEST(MemorySanitizer, fcvt_long) {
+ int a, b;
+ break_optimization(&a);
+ break_optimization(&b);
+ EXPECT_POISONED(a);
+ EXPECT_POISONED(b);
+ char *str = fcvt(111111112345.6789, 10, &a, &b);
+ EXPECT_NOT_POISONED(a);
+ EXPECT_NOT_POISONED(b);
+ ASSERT_NE(nullptr, str);
+ EXPECT_NOT_POISONED(str[0]);
+ ASSERT_NE(0U, strlen(str));
+}
+
+
TEST(MemorySanitizer, memchr) {
char x[10];
break_optimization(x);
@@ -2787,7 +2799,7 @@
EXPECT_NOT_POISONED(s[4]);
EXPECT_NOT_POISONED(s[5]);
EXPECT_POISONED(s[6]);
- delete s;
+ delete[] s;
delete d;
}
@@ -3712,56 +3724,6 @@
}
#endif // defined(__clang__)
-TEST(MemorySanitizerDr, StoreInDSOTest) {
- if (!__msan_has_dynamic_component()) return;
- char* s = new char[10];
- dso_memfill(s, 9);
- EXPECT_NOT_POISONED(s[5]);
- EXPECT_POISONED(s[9]);
-}
-
-int return_poisoned_int() {
- return ReturnPoisoned<U8>();
-}
-
-TEST(MemorySanitizerDr, ReturnFromDSOTest) {
- if (!__msan_has_dynamic_component()) return;
- EXPECT_NOT_POISONED(dso_callfn(return_poisoned_int));
-}
-
-NOINLINE int TrashParamTLS(long long x, long long y, long long z) { //NOLINT
- EXPECT_POISONED(x);
- EXPECT_POISONED(y);
- EXPECT_POISONED(z);
- return 0;
-}
-
-static int CheckParamTLS(long long x, long long y, long long z) { //NOLINT
- EXPECT_NOT_POISONED(x);
- EXPECT_NOT_POISONED(y);
- EXPECT_NOT_POISONED(z);
- return 0;
-}
-
-TEST(MemorySanitizerDr, CallFromDSOTest) {
- if (!__msan_has_dynamic_component()) return;
- S8* x = GetPoisoned<S8>();
- S8* y = GetPoisoned<S8>();
- S8* z = GetPoisoned<S8>();
- EXPECT_NOT_POISONED(TrashParamTLS(*x, *y, *z));
- EXPECT_NOT_POISONED(dso_callfn1(CheckParamTLS));
-}
-
-static void StackStoreInDSOFn(int* x, int* y) {
- EXPECT_NOT_POISONED(*x);
- EXPECT_NOT_POISONED(*y);
-}
-
-TEST(MemorySanitizerDr, StackStoreInDSOTest) {
- if (!__msan_has_dynamic_component()) return;
- dso_stack_store(StackStoreInDSOFn, 1);
-}
-
TEST(MemorySanitizerOrigins, SetGet) {
EXPECT_EQ(TrackingOrigins(), __msan_get_track_origins());
if (!TrackingOrigins()) return;
diff --git a/lib/msandr/CMakeLists.txt b/lib/msandr/CMakeLists.txt
deleted file mode 100644
index 5a96a9d..0000000
--- a/lib/msandr/CMakeLists.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-
-if(DynamoRIO_DIR AND DrMemoryFramework_DIR)
- set(CMAKE_COMPILER_IS_GNUCC 1)
- find_package(DynamoRIO)
- find_package(DrMemoryFramework)
-
- set(arch "x86_64")
- add_library(clang_rt.msandr-${arch} SHARED msandr.cc)
- configure_DynamoRIO_client(clang_rt.msandr-${arch})
-
- function(append_target_cflags tgt cflags)
- get_property(old_cflags TARGET clang_rt.msandr-${arch} PROPERTY COMPILE_FLAGS)
- set_property(TARGET clang_rt.msandr-${arch} PROPERTY COMPILE_FLAGS "${old_cflags} ${cflags}")
- endfunction(append_target_cflags)
-
- append_target_cflags(clang_rt.msandr-${arch} "-Wno-c++11-extensions")
-
- use_DynamoRIO_extension(clang_rt.msandr-${arch} drutil)
- use_DynamoRIO_extension(clang_rt.msandr-${arch} drmgr)
- use_DynamoRIO_extension(clang_rt.msandr-${arch} drsyscall)
-
- set_target_properties(clang_rt.msandr-${arch} PROPERTIES
- LIBRARY_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR})
- install(TARGETS clang_rt.msandr-${arch}
- LIBRARY DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR})
-endif()
diff --git a/lib/msandr/README.txt b/lib/msandr/README.txt
deleted file mode 100644
index 81a87c6..0000000
--- a/lib/msandr/README.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-Experimental DynamoRIO-MSAN plugin (codename "MSanDR").
-Supports Linux/x86_64 only.
-
-Building:
- 1. First, download and build DynamoRIO:
- (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_DRX_STATIC=ON .. && \
- make -j10 && make install)
-
- 2. Download and build DrMemory (for DrSyscall extension)
- (svn co http://drmemory.googlecode.com/svn/trunk/ drmemory && \
- cd drmemory && mkdir build && cd build && \
- cmake -DDynamoRIO_DIR=`pwd`/../../dr/exports/cmake .. && \
- make -j10 && make install)
-
- NOTE: The line above will build a shared DrSyscall library in a non-standard
- location. This will require the use of LD_LIBRARY_PATH when running MSanDR.
- To build a static DrSyscall library (and link it into MSanDR), add
- -DDR_EXT_DRSYSCALL_STATIC=ON to the CMake invocation above, but
- beware: DrSyscall is LGPL.
-
- 3. Now, build LLVM with two extra CMake flags:
- -DDynamoRIO_DIR=<path_to_dynamorio>/exports/cmake
- -DDrMemoryFramework_DIR=<path_to_drmemory>/exports64/drmf
-
- This will build a lib/clang/$VERSION/lib/linux/libclang_rt.msandr-x86_64.so
-
-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
deleted file mode 100644
index 5159ddb..0000000
--- a/lib/msandr/msandr.cc
+++ /dev/null
@@ -1,900 +0,0 @@
-//===-- msandr.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 MemorySanitizer.
-//
-// DynamoRio client for MemorySanitizer.
-//
-// MemorySanitizer requires that all program code is instrumented. Any memory
-// store that can turn an uninitialized value into an initialized value must be
-// observed by the tool, otherwise we risk reporting a false UMR.
-//
-// This also includes any libraries that the program depends on.
-//
-// In the case when rebuilding all program dependencies with MemorySanitizer is
-// problematic, an experimental MSanDR tool (the code you are currently looking
-// at) can be used. It is a DynamoRio-based tool that uses dynamic
-// instrumentation to
-// * Unpoison all memory stores.
-// * Unpoison TLS slots used by MemorySanitizer to pass function arguments and
-// return value shadow on anything that looks like a function call or a return
-// from a function.
-//
-// This tool does not detect the use of uninitialized values in uninstrumented
-// libraries. It merely gets rid of false positives by marking all data that
-// passes through uninstrumented code as fully initialized.
-//===----------------------------------------------------------------------===//
-
-#include <dr_api.h>
-#include <drutil.h>
-#include <drmgr.h>
-#include <drsyscall.h>
-
-#include <sys/mman.h>
-#include <sys/syscall.h> /* for SYS_mmap */
-
-#include <string.h>
-
-// 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
-
-#ifndef MSANDR_NATIVE_EXEC
-#include <algorithm>
-#include <set>
-#include <string>
-#include <vector>
-#endif
-
-#define TESTALL(mask, var) (((mask) & (var)) == (mask))
-#define TESTANY(mask, var) (((mask) & (var)) != 0)
-
-#define CHECK_IMPL(condition, file, line) \
- do { \
- if (!(condition)) { \
- dr_printf("Check failed: `%s`\nat %s:%d\n", #condition, file, line); \
- dr_abort(); \
- } \
- } while (0) // TODO: stacktrace
-
-#define CHECK(condition) CHECK_IMPL(condition, __FILE__, __LINE__)
-
-#define VERBOSITY 0
-
-// 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 */
-
-typedef void *(*WrapperFn)(void *);
-extern "C" void __msan_set_indirect_call_wrapper(WrapperFn wrapper);
-extern "C" void __msan_dr_is_initialized();
-
-namespace {
-
-int msan_retval_tls_offset;
-int msan_param_tls_offset;
-
-#ifndef MSANDR_NATIVE_EXEC
-class ModuleData {
-public:
- ModuleData();
- ModuleData(const module_data_t *info);
- // Yes, we want default copy, assign, and dtor semantics.
-
-public:
- app_pc start_;
- app_pc end_;
- // Full path to the module.
- std::string path_;
- module_handle_t handle_;
- bool should_instrument_;
- bool executed_;
-};
-
-// 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.
-std::vector<ModuleData> g_module_list;
-
-ModuleData::ModuleData()
- : start_(NULL), end_(NULL), path_(""), handle_(NULL),
- should_instrument_(false), executed_(false) {
-}
-
-ModuleData::ModuleData(const module_data_t *info)
- : start_(info->start), end_(info->end), path_(info->full_path),
- handle_(info->handle),
- // 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() {
- module_data_t *app = dr_lookup_module_by_name(dr_get_application_name());
- if (!app) {
- dr_printf("%s - oops, dr_lookup_module_by_name failed!\n",
- dr_get_application_name());
- CHECK(app);
- }
-
- __msan_get_retval_tls_offset = (int (*)())
- LookupCallback(app, "__msan_get_retval_tls_offset");
- __msan_get_param_tls_offset = (int (*)())
- LookupCallback(app, "__msan_get_param_tls_offset");
- __msan_unpoison = (void(*)(void *, size_t))
- LookupCallback(app, "__msan_unpoison");
- __msan_is_in_loader = (bool (*)())
- LookupCallback(app, "__msan_is_in_loader");
-
- dr_free_module_data(app);
-}
-
-// FIXME: Handle absolute addresses and PC-relative addresses.
-// FIXME: Handle TLS accesses via FS or GS. DR assumes all other segments have
-// a zero base anyway.
-bool OperandIsInteresting(opnd_t opnd) {
- return (opnd_is_base_disp(opnd) && opnd_get_segment(opnd) != DR_SEG_FS &&
- opnd_get_segment(opnd) != DR_SEG_GS);
-}
-
-bool WantToInstrument(instr_t *instr) {
- // TODO: skip push instructions?
- switch (instr_get_opcode(instr)) {
- // FIXME: support the instructions excluded below:
- case OP_rep_cmps:
- // f3 a6 rep cmps %ds:(%rsi) %es:(%rdi) %rsi %rdi %rcx -> %rsi %rdi %rcx
- return false;
- }
-
- // Labels appear due to drutil_expand_rep_string()
- if (instr_is_label(instr))
- return false;
-
- CHECK(instr_ok_to_mangle(instr) == true);
-
- if (instr_writes_memory(instr)) {
- for (int d = 0; d < instr_num_dsts(instr); d++) {
- opnd_t op = instr_get_dst(instr, d);
- if (OperandIsInteresting(op))
- return true;
- }
- }
-
- return false;
-}
-
-#define PRE(at, what) instrlist_meta_preinsert(bb, at, INSTR_CREATE_##what);
-#define PREF(at, what) instrlist_meta_preinsert(bb, at, what);
-
-void InstrumentMops(void *drcontext, instrlist_t *bb, instr_t *instr, opnd_t op,
- bool is_write) {
- bool need_to_restore_eflags = false;
- uint flags = instr_get_arith_flags(instr);
- // TODO: do something smarter with flags and spills in general?
- // For example, spill them only once for a sequence of instrumented
- // instructions that don't change/read flags.
-
- if (!TESTALL(EFLAGS_WRITE_6, flags) || TESTANY(EFLAGS_READ_6, flags)) {
- if (VERBOSITY > 1)
- dr_printf("Spilling eflags...\n");
- need_to_restore_eflags = true;
- // TODO: Maybe sometimes don't need to 'seto'.
- // TODO: Maybe sometimes don't want to spill XAX here?
- // TODO: No need to spill XAX here if XAX is not used in the BB.
- dr_save_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1);
- dr_save_arith_flags_to_xax(drcontext, bb, instr);
- dr_save_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_3);
- dr_restore_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1);
- }
-
-#if 0
- dr_printf("==DRMSAN== DEBUG: %d %d %d %d %d %d\n",
- opnd_is_memory_reference(op), opnd_is_base_disp(op),
- opnd_is_base_disp(op) ? opnd_get_index(op) : -1,
- opnd_is_far_memory_reference(op), opnd_is_reg_pointer_sized(op),
- opnd_is_base_disp(op) ? opnd_get_disp(op) : -1);
-#endif
-
- reg_id_t R1;
- bool address_in_R1 = false;
- if (opnd_is_base_disp(op) && opnd_get_index(op) == DR_REG_NULL &&
- opnd_get_disp(op) == 0) {
- // If this is a simple access with no offset or index, we can just use the
- // base for R1.
- address_in_R1 = true;
- R1 = opnd_get_base(op);
- } else {
- // Otherwise, we need to compute the addr into R1.
- // TODO: reuse some spare register? e.g. r15 on x64
- // TODO: might be used as a non-mem-ref register?
- R1 = DR_REG_XAX;
- }
- CHECK(reg_is_pointer_sized(R1)); // otherwise R2 may be wrong.
-
- // 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((R2 <= DR_REG_R15) && R1 != R2);
-
- // Save the current values of R1 and R2.
- dr_save_reg(drcontext, bb, instr, R1, SPILL_SLOT_1);
- // TODO: Something smarter than spilling a "fixed" register R2?
- dr_save_reg(drcontext, bb, instr, R2, SPILL_SLOT_2);
-
- 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(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 || 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) {
- 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)
- }
- }
-
- // Restore the registers and flags.
- 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");
- // TODO: Check if it's reverse to the dr_restore_reg above and optimize.
- dr_save_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1);
- dr_restore_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_3);
- dr_restore_arith_flags_from_xax(drcontext, bb, instr);
- dr_restore_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1);
- }
-
- // The original instruction is left untouched. The above instrumentation is just
- // a prefix.
-}
-
-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 */
-# ifdef MSANDR_NATIVE_EXEC
- /* For optimized native exec, -mangle_app_seg and -private_loader are turned off,
- * so we can reference msan_retval_tls_offset directly.
- */
- PRE(instr,
- mov_st(drcontext,
- opnd_create_far_base_disp(DR_SEG_FS, DR_REG_NULL, DR_REG_NULL, 0,
- msan_retval_tls_offset, OPSZ_PTR),
- OPND_CREATE_INT32(0)));
-# else /* !MSANDR_NATIVE_EXEC */
- /* 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.
- bool res =
- dr_insert_get_seg_base(drcontext, bb, instr, DR_SEG_FS, DR_REG_XAX);
- CHECK(res);
-
- // TODO: unpoison more bytes?
- PRE(instr,
- mov_st(drcontext, OPND_CREATE_MEM64(DR_REG_XAX, msan_retval_tls_offset),
- OPND_CREATE_INT32(0)));
-
- dr_restore_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1);
-# endif /* !MSANDR_NATIVE_EXEC */
- // 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 */
-# ifdef MSANDR_NATIVE_EXEC
- for (int i = 0; i < NUM_TLS_PARAM; ++i) {
- PRE(instr,
- mov_st(drcontext,
- opnd_create_far_base_disp(DR_SEG_FS, DR_REG_NULL, DR_REG_NULL, 0,
- msan_param_tls_offset + i*sizeof(void*),
- OPSZ_PTR),
- OPND_CREATE_INT32(0)));
- }
-# else /* !MSANDR_NATIVE_EXEC */
- /* 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.
- bool res =
- dr_insert_get_seg_base(drcontext, bb, instr, DR_SEG_FS, DR_REG_XAX);
- CHECK(res);
-
- // TODO: unpoison more bytes?
- 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 *)),
- OPND_CREATE_INT32(0)));
- }
-
- dr_restore_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1);
-# endif /* !MSANDR_NATIVE_EXEC */
- // 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) {
- return left.start_ < right.start_;
-}
-
-// Look up the module containing PC. Should be relatively fast, as its called
-// for each bb instrumentation.
-ModuleData *LookupModuleByPC(app_pc pc) {
- ModuleData fake_mod_data;
- fake_mod_data.start_ = pc;
- std::vector<ModuleData>::iterator it =
- lower_bound(g_module_list.begin(), g_module_list.end(), fake_mod_data,
- ModuleDataCompareStart);
- // if (it == g_module_list.end())
- // return NULL;
- if (it == g_module_list.end() || pc < it->start_)
- --it;
- CHECK(it->start_ <= pc);
- if (pc >= it->end_) {
- // We're past the end of this module. We shouldn't be in the next module,
- // or lower_bound lied to us.
- ++it;
- CHECK(it == g_module_list.end() || pc < it->start_);
- return NULL;
- }
-
- // OK, we found the module.
- return &*it;
-}
-
-bool ShouldInstrumentNonModuleCode() { return true; }
-
-bool ShouldInstrumentModule(ModuleData *mod_data) {
- // TODO(rnk): Flags for blacklist would get wired in here.
- generic_func_t p =
- dr_get_proc_address(mod_data->handle_, "__msan_track_origins");
- return !p;
-}
-
-bool ShouldInstrumentPc(app_pc pc, ModuleData **pmod_data) {
- ModuleData *mod_data = LookupModuleByPC(pc);
- if (pmod_data)
- *pmod_data = mod_data;
- if (mod_data != NULL) {
- // This module is on a blacklist.
- if (!mod_data->should_instrument_) {
- return false;
- }
- } else if (!ShouldInstrumentNonModuleCode()) {
- return false;
- }
- 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))
- return DR_EMIT_PERSISTABLE;
-
- if (VERBOSITY > 1)
- dr_printf("============================================================\n");
- if (VERBOSITY > 0) {
- std::string mod_path = (mod_data ? mod_data->path_ : "<no module, JITed?>");
- if (mod_data && !mod_data->executed_) {
- mod_data->executed_ = true; // Nevermind this race.
- dr_printf("Executing from new module: %s\n", mod_path.c_str());
- }
- dr_printf("BB to be instrumented: %p [from %s]; translating = %s\n", pc,
- mod_path.c_str(), translating ? "true" : "false");
- if (mod_data) {
- // Match standard sanitizer trace format for free symbols.
- // #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
- dr_printf(" #0 %p (%s+%p)\n", pc, mod_data->path_.c_str(),
- pc - mod_data->start_);
- }
- }
-#endif /* !MSANDR_NATIVE_EXEC */
-
- if (VERBOSITY > 1) {
- instrlist_disassemble(drcontext, pc, bb, STDOUT);
- instr_t *instr;
- for (instr = instrlist_first(bb); instr; instr = instr_get_next(instr)) {
- dr_printf("opcode: %d\n", instr_get_opcode(instr));
- }
- }
-
- for (instr_t *i = instrlist_first(bb); i != NULL; i = instr_get_next(i)) {
- int opcode = instr_get_opcode(i);
- if (opcode == OP_ret || opcode == OP_ret_far) {
- InstrumentReturn(drcontext, bb, i);
- continue;
- }
-
- // These instructions hopefully cover all cases where control is transferred
- // to a function in a different module (we only care about calls into
- // compiler-instrumented modules).
- // * call_ind is used for normal indirect calls.
- // * jmp_ind is used for indirect tail calls, and calls through PLT (PLT
- // stub includes a jump to an address from GOT).
- if (opcode == OP_call_ind || opcode == OP_call_far_ind ||
- opcode == OP_jmp_ind || opcode == OP_jmp_far_ind) {
- InstrumentIndirectBranch(drcontext, bb, i);
- continue;
- }
-
- if (!WantToInstrument(i))
- continue;
-
- if (VERBOSITY > 1) {
- app_pc orig_pc = dr_fragment_app_pc(tag);
- uint flags = instr_get_arith_flags(i);
- dr_printf("+%d -> to be instrumented! [opcode=%d, flags = 0x%08X]\n",
- instr_get_app_pc(i) - orig_pc, instr_get_opcode(i), flags);
- }
-
- if (instr_writes_memory(i)) {
- // Instrument memory writes
- // bool instrumented_anything = false;
- for (int d = 0; d < instr_num_dsts(i); d++) {
- opnd_t op = instr_get_dst(i, d);
- if (!OperandIsInteresting(op))
- continue;
-
- // CHECK(!instrumented_anything);
- // instrumented_anything = true;
- InstrumentMops(drcontext, bb, i, op, true);
- break; // only instrumenting the first dst
- }
- }
- }
-
-// TODO: optimize away redundant restore-spill pairs?
-
- if (VERBOSITY > 1) {
- pc = dr_fragment_app_pc(tag);
- dr_printf("\nFinished instrumenting dynamorio_basic_block(PC=" PFX ")\n", pc);
- instrlist_disassemble(drcontext, pc, bb, STDOUT);
- }
- 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.
- ModuleData mod_data(info);
- std::vector<ModuleData>::iterator it =
- upper_bound(g_module_list.begin(), g_module_list.end(), mod_data,
- ModuleDataCompareStart);
- it = g_module_list.insert(it, mod_data);
- // Check if we should instrument this module.
- it->should_instrument_ = ShouldInstrumentModule(&*it);
- dr_module_set_should_instrument(info->handle, it->should_instrument_);
-
- if (VERBOSITY > 0)
- dr_printf("==DRMSAN== Loaded module: %s [%p...%p], instrumentation is %s\n",
- info->full_path, info->start, info->end,
- it->should_instrument_ ? "on" : "off");
-}
-
-void event_module_unload(void *drcontext, const module_data_t *info) {
- if (VERBOSITY > 0)
- dr_printf("==DRMSAN== Unloaded module: %s [%p...%p]\n", info->full_path,
- info->start, info->end);
-
- // Remove the module from the list.
- ModuleData mod_data(info);
- std::vector<ModuleData>::iterator it =
- lower_bound(g_module_list.begin(), g_module_list.end(), mod_data,
- ModuleDataCompareStart);
- // It's a bug if we didn't actually find the module.
- CHECK(it != g_module_list.end() && it->start_ == mod_data.start_ &&
- 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.
- drsys_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");
-}
-
-bool event_filter_syscall(void *drcontext, int sysnum) {
- // FIXME: only intercept syscalls with memory effects.
- return true; /* intercept everything */
-}
-
-bool drsys_iter_memarg_cb(drsys_arg_t *arg, void *user_data) {
- CHECK(arg->valid);
-
- if (arg->pre)
- return true;
- if (!TESTANY(DRSYS_PARAM_OUT, arg->mode))
- return true;
-
- size_t sz = arg->size;
-
- if (sz > 0xFFFFFFFF) {
- drmf_status_t res;
- drsys_syscall_t *syscall = (drsys_syscall_t *)user_data;
- const char *name;
- res = drsys_syscall_name(syscall, &name);
- CHECK(res == DRMF_SUCCESS);
-
- dr_printf("SANITY: syscall '%s' arg %d writes %llu bytes memory?!"
- " Clipping to %llu.\n",
- name, arg->ordinal, (unsigned long long) sz,
- (unsigned long long)(sz & 0xFFFFFFFF));
- }
-
- if (VERBOSITY > 0) {
- drmf_status_t res;
- 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);
- }
-
- // We don't switch to the app context because __msan_unpoison() doesn't need
- // TLS segments.
- __msan_unpoison(arg->start_addr, sz);
-
- return true; /* keep going */
-}
-
-bool event_pre_syscall(void *drcontext, int sysnum) {
- drsys_syscall_t *syscall;
- drsys_sysnum_t sysnum_full;
- bool known;
- drsys_param_type_t ret_type;
- drmf_status_t res;
- const char *name;
-
- res = drsys_cur_syscall(drcontext, &syscall);
- CHECK(res == DRMF_SUCCESS);
-
- res = drsys_syscall_number(syscall, &sysnum_full);
- CHECK(res == DRMF_SUCCESS);
- CHECK(sysnum == sysnum_full.number);
-
- res = drsys_syscall_is_known(syscall, &known);
- CHECK(res == DRMF_SUCCESS);
-
- res = drsys_syscall_name(syscall, &name);
- CHECK(res == DRMF_SUCCESS);
-
- res = drsys_syscall_return_type(syscall, &ret_type);
- CHECK(res == DRMF_SUCCESS);
- CHECK(ret_type != DRSYS_TYPE_INVALID);
- CHECK(!known || ret_type != DRSYS_TYPE_UNKNOWN);
-
- res = drsys_iterate_memargs(drcontext, drsys_iter_memarg_cb, NULL);
- CHECK(res == DRMF_SUCCESS);
-
- return true;
-}
-
-static bool IsInLoader(void *drcontext) {
- // TODO: This segment swap is inefficient. DR should just let us query the
- // app segment base, which it has. Alternatively, if we disable
- // -mangle_app_seg, then we won't need the swap.
- bool need_swap = !dr_using_app_state(drcontext);
- if (need_swap)
- dr_switch_to_app_state(drcontext);
- bool is_in_loader = __msan_is_in_loader();
- if (need_swap)
- dr_switch_to_dr_state(drcontext);
- return is_in_loader;
-}
-
-void event_post_syscall(void *drcontext, int sysnum) {
- drsys_syscall_t *syscall;
- drsys_sysnum_t sysnum_full;
- bool success = false;
- drmf_status_t res;
-
- res = drsys_cur_syscall(drcontext, &syscall);
- CHECK(res == DRMF_SUCCESS);
-
- res = drsys_syscall_number(syscall, &sysnum_full);
- CHECK(res == DRMF_SUCCESS);
- CHECK(sysnum == sysnum_full.number);
-
- res = drsys_syscall_succeeded(syscall, dr_syscall_get_result(drcontext),
- &success);
- CHECK(res == DRMF_SUCCESS);
-
- if (success) {
- res =
- drsys_iterate_memargs(drcontext, drsys_iter_memarg_cb, (void *)syscall);
- CHECK(res == DRMF_SUCCESS);
- }
-
- // Our normal mmap interceptor can't intercept calls from the loader itself.
- // This means we don't clear the shadow for calls to dlopen. For now, we
- // solve this by intercepting mmap from ld.so here, but ideally we'd have a
- // solution that doesn't rely on msandr.
- //
- // Be careful not to intercept maps done by the msan rtl. Otherwise we end up
- // unpoisoning vast regions of memory and OOMing.
- // TODO: __msan_unpoison() could "flush" large regions of memory like tsan
- // does instead of doing a large memset. However, we need the memory to be
- // zeroed, where as tsan does not, so plain madvise is not enough.
- if (success && (sysnum == SYS_mmap IF_NOT_X64(|| sysnum == SYS_mmap2))) {
- if (IsInLoader(drcontext)) {
- app_pc base = (app_pc)dr_syscall_get_result(drcontext);
- ptr_uint_t size;
- drmf_status_t res = drsys_pre_syscall_arg(drcontext, 1, &size);
- CHECK(res == DRMF_SUCCESS);
- if (VERBOSITY > 0)
- dr_printf("unpoisoning for dlopen: [%p-%p]\n", base, base + size);
- // We don't switch to the app context because __msan_unpoison() doesn't
- // need TLS segments.
- __msan_unpoison(base, size);
- }
- }
-}
-
-} // namespace
-
-DR_EXPORT void dr_init(client_id_t id) {
- drmf_status_t res;
-
- drmgr_init();
- drutil_init();
-
-#ifndef MSANDR_NATIVE_EXEC
- // We should use drconfig to ignore these applications.
- std::string app_name = dr_get_application_name();
- // This blacklist will still run these apps through DR's code cache. On the
- // other hand, we are able to follow children of these apps.
- // FIXME: Once DR has detach, we could just detach here. Alternatively,
- // if DR had a fork or exec hook to let us decide there, that would be nice.
- // FIXME: make the blacklist cmd-adjustable.
- if (app_name == "python" || app_name == "python2.7" || app_name == "bash" ||
- app_name == "sh" || app_name == "true" || app_name == "exit" ||
- app_name == "yes" || app_name == "echo")
- return;
-#endif /* !MSANDR_NATIVE_EXEC */
-
- drsys_options_t ops;
- memset(&ops, 0, sizeof(ops));
- ops.struct_size = sizeof(ops);
- ops.analyze_unknown_syscalls = false;
-
- res = drsys_init(id, &ops);
- CHECK(res == DRMF_SUCCESS);
-
- dr_register_filter_syscall_event(event_filter_syscall);
- drmgr_register_pre_syscall_event(event_pre_syscall);
- drmgr_register_post_syscall_event(event_post_syscall);
- 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
- // functions. This may change one day.
- // TODO: make this more robust.
-
- void *drcontext = dr_get_current_drcontext();
-
- dr_switch_to_app_state(drcontext);
- msan_retval_tls_offset = __msan_get_retval_tls_offset();
- msan_param_tls_offset = __msan_get_param_tls_offset();
- dr_switch_to_dr_state(drcontext);
- if (VERBOSITY > 0) {
- dr_printf("__msan_retval_tls offset: %d\n", msan_retval_tls_offset);
- dr_printf("__msan_param_tls offset: %d\n", msan_param_tls_offset);
- }
-
- // Standard DR events.
- dr_register_exit_event(event_exit);
-
- drmgr_priority_t priority = {
- sizeof(priority), /* size of struct */
- "msandr", /* name of our operation */
- NULL, /* optional name of operation we should precede */
- NULL, /* optional name of operation we should follow */
- 0
- }; /* numeric priority */
-
- 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 */
- __msan_dr_is_initialized();
- __msan_set_indirect_call_wrapper(dr_app_handle_mbr_target);
- if (VERBOSITY > 0)
- dr_printf("==MSANDR== Starting!\n");
-}
diff --git a/lib/profile/CMakeLists.txt b/lib/profile/CMakeLists.txt
index ea4712d..420d766 100644
--- a/lib/profile/CMakeLists.txt
+++ b/lib/profile/CMakeLists.txt
@@ -17,13 +17,9 @@
else()
foreach(arch ${PROFILE_SUPPORTED_ARCH})
add_compiler_rt_runtime(clang_rt.profile-${arch} ${arch} STATIC
- SOURCES ${PROFILE_SOURCES})
- add_dependencies(profile clang_rt.profile-${arch})
-
- add_compiler_rt_runtime(clang_rt.profile-pic-${arch} ${arch} STATIC
CFLAGS -fPIC
SOURCES ${PROFILE_SOURCES})
- add_dependencies(profile clang_rt.profile-pic-${arch})
+ add_dependencies(profile clang_rt.profile-${arch})
endforeach()
endif()
diff --git a/lib/profile/GCDAProfiling.c b/lib/profile/GCDAProfiling.c
index 5cd22c7..45fbd07 100644
--- a/lib/profile/GCDAProfiling.c
+++ b/lib/profile/GCDAProfiling.c
@@ -389,13 +389,17 @@
if (val != (uint32_t)-1) {
/* There are counters present in the file. Merge them. */
if (val != 0x01a10000) {
- fprintf(stderr, "profiling:invalid arc tag (0x%08x)\n", val);
+ fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: "
+ "corrupt arc tag (0x%08x)\n",
+ filename, val);
return;
}
val = read_32bit_value();
if (val == (uint32_t)-1 || val / 2 != num_counters) {
- fprintf(stderr, "profiling:invalid number of counters (%d)\n", val);
+ fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: "
+ "mismatched number of counters (%d)\n",
+ filename, val);
return;
}
@@ -437,13 +441,17 @@
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);
+ fprintf(stderr, "profiling: %s: cannot merge previous run count: "
+ "corrupt object tag (0x%08x)\n",
+ filename, val);
return;
}
val = read_32bit_value(); /* length */
if (val != obj_summary_len) {
- fprintf(stderr, "profiling:invalid object length (%d)\n", val);
+ fprintf(stderr, "profiling: %s: cannot merge previous run count: "
+ "mismatched object length (%d)\n",
+ filename, val);
return;
}
diff --git a/lib/profile/InstrProfiling.c b/lib/profile/InstrProfiling.c
index aeb3681..8d010df 100644
--- a/lib/profile/InstrProfiling.c
+++ b/lib/profile/InstrProfiling.c
@@ -41,8 +41,8 @@
__attribute__((visibility("hidden")))
void __llvm_profile_reset_counters(void) {
- uint64_t *I = __llvm_profile_counters_begin();
- uint64_t *E = __llvm_profile_counters_end();
+ uint64_t *I = __llvm_profile_begin_counters();
+ uint64_t *E = __llvm_profile_end_counters();
memset(I, 0, sizeof(uint64_t)*(E - I));
}
diff --git a/lib/profile/InstrProfiling.h b/lib/profile/InstrProfiling.h
index c5dd641..a086f3d 100644
--- a/lib/profile/InstrProfiling.h
+++ b/lib/profile/InstrProfiling.h
@@ -50,15 +50,15 @@
*/
int __llvm_profile_write_buffer(char *Buffer);
-const __llvm_profile_data *__llvm_profile_data_begin(void);
-const __llvm_profile_data *__llvm_profile_data_end(void);
-const char *__llvm_profile_names_begin(void);
-const char *__llvm_profile_names_end(void);
-uint64_t *__llvm_profile_counters_begin(void);
-uint64_t *__llvm_profile_counters_end(void);
+const __llvm_profile_data *__llvm_profile_begin_data(void);
+const __llvm_profile_data *__llvm_profile_end_data(void);
+const char *__llvm_profile_begin_names(void);
+const char *__llvm_profile_end_names(void);
+uint64_t *__llvm_profile_begin_counters(void);
+uint64_t *__llvm_profile_end_counters(void);
#define PROFILE_RANGE_SIZE(Range) \
- (__llvm_profile_ ## Range ## _end() - __llvm_profile_ ## Range ## _begin())
+ (__llvm_profile_end_ ## Range () - __llvm_profile_begin_ ## Range ())
/*!
* \brief Write instrumentation data to the current file.
diff --git a/lib/profile/InstrProfilingBuffer.c b/lib/profile/InstrProfilingBuffer.c
index b53d4f7..3351b07 100644
--- a/lib/profile/InstrProfilingBuffer.c
+++ b/lib/profile/InstrProfilingBuffer.c
@@ -26,12 +26,12 @@
/* Match logic in __llvm_profile_get_size_for_buffer().
* Match logic in __llvm_profile_write_file().
*/
- const __llvm_profile_data *DataBegin = __llvm_profile_data_begin();
- const __llvm_profile_data *DataEnd = __llvm_profile_data_end();
- const uint64_t *CountersBegin = __llvm_profile_counters_begin();
- const uint64_t *CountersEnd = __llvm_profile_counters_end();
- const char *NamesBegin = __llvm_profile_names_begin();
- const char *NamesEnd = __llvm_profile_names_end();
+ const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
+ const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
+ const uint64_t *CountersBegin = __llvm_profile_begin_counters();
+ const uint64_t *CountersEnd = __llvm_profile_end_counters();
+ const char *NamesBegin = __llvm_profile_begin_names();
+ const char *NamesEnd = __llvm_profile_end_names();
/* Calculate size of sections. */
const uint64_t DataSize = DataEnd - DataBegin;
diff --git a/lib/profile/InstrProfilingFile.c b/lib/profile/InstrProfilingFile.c
index 5fb78e3..5aef390 100644
--- a/lib/profile/InstrProfilingFile.c
+++ b/lib/profile/InstrProfilingFile.c
@@ -16,12 +16,12 @@
static int writeFile(FILE *File) {
/* Match logic in __llvm_profile_write_buffer(). */
- const __llvm_profile_data *DataBegin = __llvm_profile_data_begin();
- const __llvm_profile_data *DataEnd = __llvm_profile_data_end();
- const uint64_t *CountersBegin = __llvm_profile_counters_begin();
- const uint64_t *CountersEnd = __llvm_profile_counters_end();
- const char *NamesBegin = __llvm_profile_names_begin();
- const char *NamesEnd = __llvm_profile_names_end();
+ const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
+ const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
+ const uint64_t *CountersBegin = __llvm_profile_begin_counters();
+ const uint64_t *CountersEnd = __llvm_profile_end_counters();
+ const char *NamesBegin = __llvm_profile_begin_names();
+ const char *NamesEnd = __llvm_profile_end_names();
/* Calculate size of sections. */
const uint64_t DataSize = DataEnd - DataBegin;
diff --git a/lib/profile/InstrProfilingPlatformDarwin.c b/lib/profile/InstrProfilingPlatformDarwin.c
index 7401977..02299cc 100644
--- a/lib/profile/InstrProfilingPlatformDarwin.c
+++ b/lib/profile/InstrProfilingPlatformDarwin.c
@@ -25,19 +25,19 @@
extern uint64_t CountersEnd __asm("section$end$__DATA$__llvm_prf_cnts");
__attribute__((visibility("hidden")))
-const __llvm_profile_data *__llvm_profile_data_begin(void) {
+const __llvm_profile_data *__llvm_profile_begin_data(void) {
return &DataStart;
}
__attribute__((visibility("hidden")))
-const __llvm_profile_data *__llvm_profile_data_end(void) {
+const __llvm_profile_data *__llvm_profile_end_data(void) {
return &DataEnd;
}
__attribute__((visibility("hidden")))
-const char *__llvm_profile_names_begin(void) { return &NamesStart; }
+const char *__llvm_profile_begin_names(void) { return &NamesStart; }
__attribute__((visibility("hidden")))
-const char *__llvm_profile_names_end(void) { return &NamesEnd; }
+const char *__llvm_profile_end_names(void) { return &NamesEnd; }
__attribute__((visibility("hidden")))
-uint64_t *__llvm_profile_counters_begin(void) { return &CountersStart; }
+uint64_t *__llvm_profile_begin_counters(void) { return &CountersStart; }
__attribute__((visibility("hidden")))
-uint64_t *__llvm_profile_counters_end(void) { return &CountersEnd; }
+uint64_t *__llvm_profile_end_counters(void) { return &CountersEnd; }
#endif
diff --git a/lib/profile/InstrProfilingPlatformOther.c b/lib/profile/InstrProfilingPlatformOther.c
index 404c1a8..548d6a3 100644
--- a/lib/profile/InstrProfilingPlatformOther.c
+++ b/lib/profile/InstrProfilingPlatformOther.c
@@ -56,19 +56,19 @@
}
__attribute__((visibility("hidden")))
-const __llvm_profile_data *__llvm_profile_data_begin(void) {
+const __llvm_profile_data *__llvm_profile_begin_data(void) {
return DataFirst;
}
__attribute__((visibility("hidden")))
-const __llvm_profile_data *__llvm_profile_data_end(void) {
+const __llvm_profile_data *__llvm_profile_end_data(void) {
return DataLast;
}
__attribute__((visibility("hidden")))
-const char *__llvm_profile_names_begin(void) { return NamesFirst; }
+const char *__llvm_profile_begin_names(void) { return NamesFirst; }
__attribute__((visibility("hidden")))
-const char *__llvm_profile_names_end(void) { return NamesLast; }
+const char *__llvm_profile_end_names(void) { return NamesLast; }
__attribute__((visibility("hidden")))
-uint64_t *__llvm_profile_counters_begin(void) { return CountersFirst; }
+uint64_t *__llvm_profile_begin_counters(void) { return CountersFirst; }
__attribute__((visibility("hidden")))
-uint64_t *__llvm_profile_counters_end(void) { return CountersLast; }
+uint64_t *__llvm_profile_end_counters(void) { return CountersLast; }
#endif
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index 7ad3f31..fe4418c 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -16,10 +16,13 @@
sanitizer_platform_limits_posix.cc
sanitizer_posix.cc
sanitizer_printf.cc
+ sanitizer_procmaps_common.cc
+ sanitizer_procmaps_freebsd.cc
sanitizer_procmaps_linux.cc
sanitizer_procmaps_mac.cc
sanitizer_stackdepot.cc
sanitizer_stacktrace.cc
+ sanitizer_stacktrace_printer.cc
sanitizer_suppressions.cc
sanitizer_symbolizer.cc
sanitizer_symbolizer_libbacktrace.cc
@@ -37,7 +40,8 @@
sanitizer_stacktrace_libcdep.cc
sanitizer_stoptheworld_linux_libcdep.cc
sanitizer_symbolizer_libcdep.cc
- sanitizer_symbolizer_posix_libcdep.cc)
+ sanitizer_symbolizer_posix_libcdep.cc
+ sanitizer_unwind_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
@@ -79,6 +83,7 @@
sanitizer_stackdepot.h
sanitizer_stackdepotbase.h
sanitizer_stacktrace.h
+ sanitizer_stacktrace_printer.h
sanitizer_stoptheworld.h
sanitizer_suppressions.h
sanitizer_symbolizer.h
@@ -100,8 +105,11 @@
set(SANITIZER_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_no_rtti_flag(SANITIZER_CFLAGS)
-append_if(COMPILER_RT_HAS_WFRAME_LARGER_THAN_FLAG -Wframe-larger-than=512 SANITIZER_CFLAGS)
-append_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors SANITIZER_CFLAGS)
+# Stack frames on PowerPC are much larger than anticipated.
+if(NOT ${LLVM_NATIVE_ARCH} STREQUAL "PowerPC")
+ append_list_if(COMPILER_RT_HAS_WFRAME_LARGER_THAN_FLAG -Wframe-larger-than=512 SANITIZER_CFLAGS)
+endif()
+append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors SANITIZER_CFLAGS)
add_custom_target(sanitizer_common)
set(SANITIZER_RUNTIME_LIBRARIES)
@@ -115,14 +123,6 @@
DEFS ${SANITIZER_COMMON_DEFINITIONS})
list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.${os})
endforeach()
-elseif(ANDROID)
- add_library(RTSanitizerCommon.arm.android OBJECT
- ${SANITIZER_SOURCES} ${SANITIZER_LIBCDEP_SOURCES})
- set_target_compile_flags(RTSanitizerCommon.arm.android
- ${SANITIZER_CFLAGS})
- set_property(TARGET RTSanitizerCommon.arm.android APPEND PROPERTY
- COMPILE_DEFINITIONS ${SANITIZER_COMMON_DEFINITIONS})
- list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.arm.android)
else()
# Otherwise, build separate libraries for each target.
foreach(arch ${SANITIZER_COMMON_SUPPORTED_ARCH})
diff --git a/lib/sanitizer_common/sanitizer_allocator.h b/lib/sanitizer_common/sanitizer_allocator.h
index c83c672..2321801 100644
--- a/lib/sanitizer_common/sanitizer_allocator.h
+++ b/lib/sanitizer_common/sanitizer_allocator.h
@@ -461,6 +461,11 @@
}
}
+ static uptr AdditionalSize() {
+ return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded,
+ GetPageSizeCached());
+ }
+
typedef SizeClassMap SizeClassMapT;
static const uptr kNumClasses = SizeClassMap::kNumClasses;
static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded;
@@ -490,11 +495,6 @@
};
COMPILER_CHECK(sizeof(RegionInfo) >= kCacheLineSize);
- static uptr AdditionalSize() {
- return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded,
- GetPageSizeCached());
- }
-
RegionInfo *GetRegionInfo(uptr class_id) {
CHECK_LT(class_id, kNumClasses);
RegionInfo *regions = reinterpret_cast<RegionInfo*>(kSpaceBeg + kSpaceSize);
@@ -1015,12 +1015,15 @@
if (map_size < size) return AllocatorReturnNull(); // Overflow.
uptr map_beg = reinterpret_cast<uptr>(
MmapOrDie(map_size, "LargeMmapAllocator"));
+ CHECK(IsAligned(map_beg, page_size_));
MapUnmapCallback().OnMap(map_beg, map_size);
uptr map_end = map_beg + map_size;
uptr res = map_beg + page_size_;
if (res & (alignment - 1)) // Align.
res += alignment - (res & (alignment - 1));
- CHECK_EQ(0, res & (alignment - 1));
+ CHECK(IsAligned(res, alignment));
+ CHECK(IsAligned(res, page_size_));
+ CHECK_GE(res + size, map_beg);
CHECK_LE(res + size, map_end);
Header *h = GetHeader(res);
h->size = size;
diff --git a/lib/sanitizer_common/sanitizer_allocator_internal.h b/lib/sanitizer_common/sanitizer_allocator_internal.h
index c5f9028..4409fd6 100644
--- a/lib/sanitizer_common/sanitizer_allocator_internal.h
+++ b/lib/sanitizer_common/sanitizer_allocator_internal.h
@@ -25,38 +25,25 @@
static const uptr kInternalAllocatorSpace = 0;
static const u64 kInternalAllocatorSize = SANITIZER_MMAP_RANGE_SIZE;
-#if SANITIZER_WORDSIZE == 32
static const uptr kInternalAllocatorRegionSizeLog = 20;
+#if SANITIZER_WORDSIZE == 32
static const uptr kInternalAllocatorNumRegions =
kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
typedef FlatByteMap<kInternalAllocatorNumRegions> ByteMap;
#else
-static const uptr kInternalAllocatorRegionSizeLog = 24;
static const uptr kInternalAllocatorNumRegions =
kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
typedef TwoLevelByteMap<(kInternalAllocatorNumRegions >> 12), 1 << 12> ByteMap;
#endif
typedef SizeClassAllocator32<
- kInternalAllocatorSpace, kInternalAllocatorSize, 16, InternalSizeClassMap,
+ kInternalAllocatorSpace, kInternalAllocatorSize, 0, InternalSizeClassMap,
kInternalAllocatorRegionSizeLog, ByteMap> PrimaryInternalAllocator;
typedef SizeClassAllocatorLocalCache<PrimaryInternalAllocator>
InternalAllocatorCache;
-// We don't want our internal allocator to do any map/unmap operations from
-// LargeMmapAllocator.
-struct CrashOnMapUnmap {
- void OnMap(uptr p, uptr size) const {
- RAW_CHECK_MSG(0, "Unexpected mmap in InternalAllocator!\n");
- }
- void OnUnmap(uptr p, uptr size) const {
- RAW_CHECK_MSG(0, "Unexpected munmap in InternalAllocator!\n");
- }
-};
-
typedef CombinedAllocator<PrimaryInternalAllocator, InternalAllocatorCache,
- LargeMmapAllocator<CrashOnMapUnmap> >
- InternalAllocator;
+ LargeMmapAllocator<> > InternalAllocator;
void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0);
void InternalFree(void *p, InternalAllocatorCache *cache = 0);
diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc
index 6b76714..c77e50e 100644
--- a/lib/sanitizer_common/sanitizer_common.cc
+++ b/lib/sanitizer_common/sanitizer_common.cc
@@ -14,8 +14,6 @@
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
#include "sanitizer_libc.h"
-#include "sanitizer_stacktrace.h"
-#include "sanitizer_symbolizer.h"
namespace __sanitizer {
@@ -157,23 +155,12 @@
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);
+const char *StripModuleName(const char *module) {
+ if (module == 0)
+ return 0;
+ if (const char *slash_pos = internal_strrchr(module, '/'))
+ return slash_pos + 1;
+ return module;
}
void ReportErrorSummary(const char *error_message) {
@@ -197,21 +184,6 @@
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()->CanReturnFileLineInfo()) {
- // 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()->SymbolizePC(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;
@@ -234,17 +206,6 @@
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);
-}
-
static atomic_uintptr_t g_total_mmaped;
void IncreaseTotalMmap(uptr size) {
diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h
index 2481cb1..c1e2101 100644
--- a/lib/sanitizer_common/sanitizer_common.h
+++ b/lib/sanitizer_common/sanitizer_common.h
@@ -167,16 +167,16 @@
void *MapFileToMemory(const char *file_name, uptr *buff_size);
void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset);
+bool IsAccessibleMemoryRange(uptr beg, uptr 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);
+// Strip the directories from the module name.
+const char *StripModuleName(const char *module);
// OS
-void DisableCoreDumper();
+void DisableCoreDumperIfNecessary();
void DumpProcessMap();
bool FileExists(const char *filename);
const char *GetEnv(const char *name);
@@ -187,6 +187,8 @@
void ReExec();
bool StackSizeIsUnlimited();
void SetStackSizeLimitInBytes(uptr limit);
+bool AddressSpaceIsUnlimited();
+void SetAddressSpaceUnlimited();
void AdjustStackSize(void *attr);
void PrepareForSandboxing(__sanitizer_sandbox_arguments *args);
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args);
@@ -205,9 +207,6 @@
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();
@@ -246,7 +245,7 @@
// 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
+// 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);
@@ -524,23 +523,6 @@
// Callback type for iterating over a set of memory ranges.
typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg);
-#if (SANITIZER_FREEBSD || SANITIZER_LINUX) && !defined(SANITIZER_GO)
-extern uptr indirect_call_wrapper;
-void SetIndirectCallWrapper(uptr wrapper);
-
-template <typename F>
-F IndirectExternCall(F f) {
- typedef F (*WrapF)(F);
- return indirect_call_wrapper ? ((WrapF)indirect_call_wrapper)(f) : f;
-}
-#else
-INLINE void SetIndirectCallWrapper(uptr wrapper) {}
-template <typename F>
-F IndirectExternCall(F f) {
- return f;
-}
-#endif
-
#if SANITIZER_ANDROID
// Initialize Android logging. Any writes before this are silently lost.
void AndroidLogInit();
diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 64ddeed..274e87c 100644
--- a/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -27,6 +27,7 @@
// COMMON_INTERCEPTOR_MUTEX_REPAIR
// COMMON_INTERCEPTOR_SET_PTHREAD_NAME
// COMMON_INTERCEPTOR_HANDLE_RECVMSG
+// COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED
//===----------------------------------------------------------------------===//
#include "interception/interception.h"
#include "sanitizer_addrhashmap.h"
@@ -40,6 +41,10 @@
#define va_copy(dst, src) ((dst) = (src))
#endif // _WIN32
+#if SANITIZER_FREEBSD
+#define pthread_setname_np pthread_set_name_np
+#endif
+
#ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE
#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(p, size) {}
#endif
@@ -89,6 +94,10 @@
COMMON_INTERCEPTOR_ENTER(ctx, __VA_ARGS__)
#endif
+#ifndef COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED
+#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0)
+#endif
+
struct FileMetadata {
// For open_memstream().
char **addr;
@@ -174,6 +183,8 @@
}
INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) {
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_strncmp(s1, s2, size);
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, strncmp, s1, s2, size);
unsigned char c1 = 0, c2 = 0;
@@ -244,7 +255,7 @@
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, memchr, s, c, n);
void *res = REAL(memchr)(s, c, n);
- uptr len = res ? (char*)res - (char*)s + 1 : n;
+ uptr len = res ? (char *)res - (const char *)s + 1 : n;
COMMON_INTERCEPTOR_READ_RANGE(ctx, s, len);
return res;
}
@@ -1030,7 +1041,8 @@
#endif
#if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS || \
- SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT
+ SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT || \
+ SANITIZER_INTERCEPT_GETPWENT_R || SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
static void unpoison_passwd(void *ctx, __sanitizer_passwd *pwd) {
if (pwd) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, sizeof(*pwd));
@@ -1077,7 +1089,9 @@
}
}
#endif // SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS ||
- // SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT
+ // SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT ||
+ // SANITIZER_INTERCEPT_GETPWENT_R ||
+ // SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
#if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS
INTERCEPTOR(__sanitizer_passwd *, getpwnam, const char *name) {
@@ -1428,30 +1442,30 @@
static void wrapped_gl_closedir(void *dir) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
- IndirectExternCall(pglob_copy->gl_closedir)(dir);
+ pglob_copy->gl_closedir(dir);
}
static void *wrapped_gl_readdir(void *dir) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
- return IndirectExternCall(pglob_copy->gl_readdir)(dir);
+ return pglob_copy->gl_readdir(dir);
}
static void *wrapped_gl_opendir(const char *s) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1);
- return IndirectExternCall(pglob_copy->gl_opendir)(s);
+ return pglob_copy->gl_opendir(s);
}
static int wrapped_gl_lstat(const char *s, void *st) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1);
- return IndirectExternCall(pglob_copy->gl_lstat)(s, st);
+ return pglob_copy->gl_lstat(s, st);
}
static int wrapped_gl_stat(const char *s, void *st) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1);
- return IndirectExternCall(pglob_copy->gl_stat)(s, st);
+ return pglob_copy->gl_stat(s, st);
}
INTERCEPTOR(int, glob, const char *pattern, int flags,
@@ -1535,8 +1549,14 @@
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
return res;
}
+// On FreeBSD id_t is always 64-bit wide.
+#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
+INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, long long id, void *infop,
+ int options) {
+#else
INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop,
int options) {
+#endif
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, waitid, idtype, id, infop, options);
// FIXME: under ASan the call below may write to freed memory and corrupt
@@ -2613,7 +2633,7 @@
static int wrapped_scandir_filter(const struct __sanitizer_dirent *dir) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen);
- return IndirectExternCall(scandir_filter)(dir);
+ return scandir_filter(dir);
}
static int wrapped_scandir_compar(const struct __sanitizer_dirent **a,
@@ -2623,7 +2643,7 @@
COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b));
COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen);
- return IndirectExternCall(scandir_compar)(a, b);
+ return scandir_compar(a, b);
}
INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist,
@@ -2665,7 +2685,7 @@
static int wrapped_scandir64_filter(const struct __sanitizer_dirent64 *dir) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen);
- return IndirectExternCall(scandir64_filter)(dir);
+ return scandir64_filter(dir);
}
static int wrapped_scandir64_compar(const struct __sanitizer_dirent64 **a,
@@ -2675,7 +2695,7 @@
COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b));
COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen);
- return IndirectExternCall(scandir64_compar)(a, b);
+ return scandir64_compar(a, b);
}
INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist,
@@ -4628,6 +4648,91 @@
#define INIT_DLOPEN_DLCLOSE
#endif
+#if SANITIZER_INTERCEPT_GETPASS
+INTERCEPTOR(char *, getpass, const char *prompt) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getpass, prompt);
+ if (prompt)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, prompt, REAL(strlen)(prompt)+1);
+ char *res = REAL(getpass)(prompt);
+ if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res)+1);
+ return res;
+}
+
+#define INIT_GETPASS COMMON_INTERCEPT_FUNCTION(getpass);
+#else
+#define INIT_GETPASS
+#endif
+
+#if SANITIZER_INTERCEPT_TIMERFD
+INTERCEPTOR(int, timerfd_settime, int fd, int flags, void *new_value,
+ void *old_value) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, timerfd_settime, fd, flags, new_value,
+ old_value);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerspec_sz);
+ int res = REAL(timerfd_settime)(fd, flags, new_value, old_value);
+ if (res != -1 && old_value)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerspec_sz);
+ return res;
+}
+
+INTERCEPTOR(int, timerfd_gettime, int fd, void *curr_value) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, timerfd_gettime, fd, curr_value);
+ int res = REAL(timerfd_gettime)(fd, curr_value);
+ if (res != -1 && curr_value)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerspec_sz);
+ return res;
+}
+#define INIT_TIMERFD \
+ COMMON_INTERCEPT_FUNCTION(timerfd_settime); \
+ COMMON_INTERCEPT_FUNCTION(timerfd_gettime);
+#else
+#define INIT_TIMERFD
+#endif
+
+#if SANITIZER_INTERCEPT_MLOCKX
+// 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;
+ VPrintf(1, "INFO: %s ignores mlock/mlockall/munlock/munlockall\n",
+ SanitizerToolName);
+}
+
+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;
+}
+
+#define INIT_MLOCKX \
+ COMMON_INTERCEPT_FUNCTION(mlock); \
+ COMMON_INTERCEPT_FUNCTION(munlock); \
+ COMMON_INTERCEPT_FUNCTION(mlockall); \
+ COMMON_INTERCEPT_FUNCTION(munlockall);
+
+#else
+#define INIT_MLOCKX
+#endif // SANITIZER_INTERCEPT_MLOCKX
+
static void InitializeCommonInterceptors() {
static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1];
interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap();
@@ -4786,4 +4891,7 @@
INIT_FFLUSH;
INIT_FCLOSE;
INIT_DLOPEN_DLCLOSE;
+ INIT_GETPASS;
+ INIT_TIMERFD;
+ INIT_MLOCKX;
}
diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc
index e4b2d76..4374f56 100644
--- a/lib/sanitizer_common/sanitizer_common_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc
@@ -13,6 +13,8 @@
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
namespace __sanitizer {
@@ -47,6 +49,21 @@
sandboxing_callback = f;
}
+void ReportErrorSummary(const char *error_type, StackTrace *stack) {
+ if (!common_flags()->print_summary)
+ return;
+ AddressInfo ai;
+#if !SANITIZER_GO
+ if (stack->size > 0 && Symbolizer::GetOrInit()->CanReturnFileLineInfo()) {
+ // 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::GetOrInit()->SymbolizePC(pc, &ai, 1);
+ }
+#endif
+ ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
+}
+
} // namespace __sanitizer
void NOINLINE
diff --git a/lib/sanitizer_common/sanitizer_common_syscalls.inc b/lib/sanitizer_common/sanitizer_common_syscalls.inc
index 23da703..a52338b 100644
--- a/lib/sanitizer_common/sanitizer_common_syscalls.inc
+++ b/lib/sanitizer_common/sanitizer_common_syscalls.inc
@@ -1326,13 +1326,13 @@
} else if (op == iocb_cmd_pread && buf && len) {
POST_WRITE(buf, len);
} else if (op == iocb_cmd_pwritev) {
- __sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf;
+ __sanitizer_iovec *iovec = (__sanitizer_iovec*)buf;
for (uptr v = 0; v < len; v++)
- PRE_READ(iovec[i].iov_base, iovec[i].iov_len);
+ PRE_READ(iovec[v].iov_base, iovec[v].iov_len);
} else if (op == iocb_cmd_preadv) {
- __sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf;
+ __sanitizer_iovec *iovec = (__sanitizer_iovec*)buf;
for (uptr v = 0; v < len; v++)
- POST_WRITE(iovec[i].iov_base, iovec[i].iov_len);
+ POST_WRITE(iovec[v].iov_base, iovec[v].iov_len);
}
// See comment in io_getevents.
COMMON_SYSCALL_RELEASE(data);
diff --git a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
index c22de97..bd98adb 100644
--- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
@@ -38,9 +38,12 @@
#include "sanitizer_mutex.h"
#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
#include "sanitizer_flags.h"
-atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once.
+static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once.
+
+static atomic_uintptr_t coverage_counter;
// pc_array is the array containing the covered PCs.
// To make the pc_array thread- and async-signal-safe it has to be large enough.
@@ -63,6 +66,13 @@
void AfterFork(int child_pid);
void Extend(uptr npcs);
void Add(uptr pc);
+ void IndirCall(uptr caller, uptr callee, uptr callee_cache[],
+ uptr cache_size);
+ void DumpCallerCalleePairs();
+ void DumpTrace();
+
+ ALWAYS_INLINE
+ void TraceBasicaBlock(uptr *cache);
uptr *data();
uptr size();
@@ -85,6 +95,34 @@
uptr pc_array_mapped_size;
// Descriptor of the file mapped pc array.
int pc_fd;
+
+ // Caller-Callee (cc) array, size and current index.
+ static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24);
+ uptr **cc_array;
+ atomic_uintptr_t cc_array_index;
+ atomic_uintptr_t cc_array_size;
+
+ // Tracing (tr) pc and event arrays, their size and current index.
+ // We record all events (basic block entries) in a global buffer of u32
+ // values. Each such value is an index in the table of TracedPc objects.
+ // So far the tracing is highly experimental:
+ // - not thread-safe;
+ // - does not support long traces;
+ // - not tuned for performance.
+ struct TracedPc {
+ uptr pc;
+ const char *module_name;
+ uptr module_offset;
+ };
+ static const uptr kTrEventArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 30);
+ u32 *tr_event_array;
+ uptr tr_event_array_size;
+ uptr tr_event_array_index;
+ static const uptr kTrPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27);
+ TracedPc *tr_pc_array;
+ uptr tr_pc_array_size;
+ uptr tr_pc_array_index;
+
StaticSpinMutex mu;
void DirectOpen();
@@ -118,6 +156,22 @@
atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed);
atomic_store(&pc_array_index, 0, memory_order_relaxed);
}
+
+ cc_array = reinterpret_cast<uptr **>(MmapNoReserveOrDie(
+ sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array"));
+ atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed);
+ atomic_store(&cc_array_index, 0, memory_order_relaxed);
+
+ tr_event_array = reinterpret_cast<u32 *>(
+ MmapNoReserveOrDie(sizeof(tr_event_array[0]) * kTrEventArrayMaxSize,
+ "CovInit::tr_event_array"));
+ tr_event_array_size = kTrEventArrayMaxSize;
+ tr_event_array_index = 0;
+
+ tr_pc_array = reinterpret_cast<TracedPc *>(MmapNoReserveOrDie(
+ sizeof(tr_pc_array[0]) * kTrEventArrayMaxSize, "CovInit::tr_pc_array"));
+ tr_pc_array_size = kTrEventArrayMaxSize;
+ tr_pc_array_index = 0;
}
void CoverageData::ReInit() {
@@ -184,6 +238,41 @@
CHECK_LT(idx * sizeof(uptr),
atomic_load(&pc_array_size, memory_order_acquire));
pc_array[idx] = pc;
+ atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+}
+
+// Registers a pair caller=>callee.
+// When a given caller is seen for the first time, the callee_cache is added
+// to the global array cc_array, callee_cache[0] is set to caller and
+// callee_cache[1] is set to cache_size.
+// Then we are trying to add callee to callee_cache [2,cache_size) if it is
+// not there yet.
+// If the cache is full we drop the callee (may want to fix this later).
+void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[],
+ uptr cache_size) {
+ if (!cc_array) return;
+ atomic_uintptr_t *atomic_callee_cache =
+ reinterpret_cast<atomic_uintptr_t *>(callee_cache);
+ uptr zero = 0;
+ if (atomic_compare_exchange_strong(&atomic_callee_cache[0], &zero, caller,
+ memory_order_seq_cst)) {
+ uptr idx = atomic_fetch_add(&cc_array_index, 1, memory_order_relaxed);
+ CHECK_LT(idx * sizeof(uptr),
+ atomic_load(&cc_array_size, memory_order_acquire));
+ callee_cache[1] = cache_size;
+ cc_array[idx] = callee_cache;
+ }
+ CHECK_EQ(atomic_load(&atomic_callee_cache[0], memory_order_relaxed), caller);
+ for (uptr i = 2; i < cache_size; i++) {
+ uptr was = 0;
+ if (atomic_compare_exchange_strong(&atomic_callee_cache[i], &was, callee,
+ memory_order_seq_cst)) {
+ atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+ return;
+ }
+ if (was == callee) // Already have this callee.
+ return;
+ }
}
uptr *CoverageData::data() {
@@ -228,7 +317,7 @@
internal_memcpy(block_pos, module, module_name_length);
block_pos += module_name_length;
char *block_data_begin = block_pos;
- char *blob_pos = (char *)blob;
+ const char *blob_pos = (const char *)blob;
while (blob_size > 0) {
unsigned int payload_size = Min(blob_size, max_payload_size);
blob_size -= payload_size;
@@ -249,7 +338,6 @@
InternalScopedBuffer<char> path(1024);
if (!packed) {
CHECK(name);
- Printf("%s\n", common_flags()->coverage_dir);
internal_snprintf((char *)path.data(), path.size(), "%s/%s.%zd.sancov",
common_flags()->coverage_dir, name, internal_getpid());
} else {
@@ -269,6 +357,97 @@
return fd;
}
+// Dump trace PCs and trace events into two separate files.
+void CoverageData::DumpTrace() {
+ uptr max_idx = tr_event_array_index;
+ if (!max_idx) return;
+ auto sym = Symbolizer::GetOrInit();
+ if (!sym)
+ return;
+ InternalScopedString out(32 << 20);
+ for (uptr i = 0; i < max_idx; i++) {
+ u32 pc_idx = tr_event_array[i];
+ TracedPc *t = &tr_pc_array[pc_idx];
+ if (!t->module_name) {
+ const char *module_name = "<unknown>";
+ uptr module_address = 0;
+ sym->GetModuleNameAndOffsetForPC(t->pc, &module_name, &module_address);
+ t->module_name = internal_strdup(module_name);
+ t->module_offset = module_address;
+ out.append("%s 0x%zx\n", t->module_name, t->module_offset);
+ }
+ }
+ int fd = CovOpenFile(false, "trace-points");
+ if (fd < 0) return;
+ internal_write(fd, out.data(), out.length());
+ internal_close(fd);
+
+ fd = CovOpenFile(false, "trace-events");
+ if (fd < 0) return;
+ internal_write(fd, tr_event_array, max_idx * sizeof(tr_event_array[0]));
+ internal_close(fd);
+ VReport(1, " CovDump: Trace: %zd PCs written\n", tr_pc_array_index);
+ VReport(1, " CovDump: Trace: %zd Events written\n", tr_event_array_index);
+}
+
+// This function dumps the caller=>callee pairs into a file as a sequence of
+// lines like "module_name offset".
+void CoverageData::DumpCallerCalleePairs() {
+ uptr max_idx = atomic_load(&cc_array_index, memory_order_relaxed);
+ if (!max_idx) return;
+ auto sym = Symbolizer::GetOrInit();
+ if (!sym)
+ return;
+ InternalScopedString out(32 << 20);
+ uptr total = 0;
+ for (uptr i = 0; i < max_idx; i++) {
+ uptr *cc_cache = cc_array[i];
+ CHECK(cc_cache);
+ uptr caller = cc_cache[0];
+ uptr n_callees = cc_cache[1];
+ const char *caller_module_name = "<unknown>";
+ uptr caller_module_address = 0;
+ sym->GetModuleNameAndOffsetForPC(caller, &caller_module_name,
+ &caller_module_address);
+ for (uptr j = 2; j < n_callees; j++) {
+ uptr callee = cc_cache[j];
+ if (!callee) break;
+ total++;
+ const char *callee_module_name = "<unknown>";
+ uptr callee_module_address = 0;
+ sym->GetModuleNameAndOffsetForPC(callee, &callee_module_name,
+ &callee_module_address);
+ out.append("%s 0x%zx\n%s 0x%zx\n", caller_module_name,
+ caller_module_address, callee_module_name,
+ callee_module_address);
+ }
+ }
+ int fd = CovOpenFile(false, "caller-callee");
+ if (fd < 0) return;
+ internal_write(fd, out.data(), out.length());
+ internal_close(fd);
+ VReport(1, " CovDump: %zd caller-callee pairs written\n", total);
+}
+
+// Record the current PC into the event buffer.
+// Every event is a u32 value (index in tr_pc_array_index) so we compute
+// it once and then cache in the provided 'cache' storage.
+void CoverageData::TraceBasicaBlock(uptr *cache) {
+ CHECK(common_flags()->coverage);
+ uptr idx = *cache;
+ if (!idx) {
+ CHECK_LT(tr_pc_array_index, kTrPcArrayMaxSize);
+ idx = tr_pc_array_index++;
+ TracedPc *t = &tr_pc_array[idx];
+ t->pc = GET_CALLER_PC();
+ *cache = idx;
+ CHECK_LT(idx, 1U << 31);
+ }
+ CHECK_LT(tr_event_array_index, tr_event_array_size);
+ tr_event_array[tr_event_array_index] = static_cast<u32>(idx);
+ tr_event_array_index++;
+}
+
// Dump the coverage on disk.
static void CovDump() {
if (!common_flags()->coverage || common_flags()->coverage_direct) return;
@@ -300,7 +479,7 @@
CHECK_LE(diff, 0xffffffffU);
offsets.push_back(static_cast<u32>(diff));
}
- char *module_name = StripModuleName(module.data());
+ const char *module_name = StripModuleName(module.data());
if (cov_sandboxed) {
if (cov_fd >= 0) {
CovWritePacked(internal_getpid(), module_name, offsets.data(),
@@ -320,11 +499,12 @@
vb - old_vb);
}
}
- InternalFree(module_name);
}
}
if (cov_fd >= 0)
internal_close(cov_fd);
+ coverage_data.DumpCallerCalleePairs();
+ coverage_data.DumpTrace();
#endif // !SANITIZER_WINDOWS
}
@@ -360,6 +540,11 @@
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov() {
coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()));
}
+SANITIZER_INTERFACE_ATTRIBUTE void
+__sanitizer_cov_indir_call16(uptr callee, uptr callee_cache16[]) {
+ coverage_data.IndirCall(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
+ callee, callee_cache16, 16);
+}
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); }
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() {
coverage_data.Init();
@@ -377,4 +562,17 @@
sptr __sanitizer_maybe_open_cov_file(const char *name) {
return MaybeOpenCovFile(name);
}
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_total_unique_coverage() {
+ return atomic_load(&coverage_counter, memory_order_relaxed);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cov_trace_func_enter(uptr *cache) {
+ coverage_data.TraceBasicaBlock(cache);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cov_trace_basic_block(uptr *cache) {
+ coverage_data.TraceBasicaBlock(cache);
+}
} // extern "C"
diff --git a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
index e4ee875..dddf2f0 100644
--- a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
@@ -80,7 +80,7 @@
text.append("%d\n", sizeof(uptr) * 8);
for (int i = 0; i < n_modules; ++i) {
- char *module_name = StripModuleName(modules[i].full_name());
+ const char *module_name = StripModuleName(modules[i].full_name());
for (unsigned j = 0; j < modules[i].n_ranges(); ++j) {
if (modules[i].address_range_executable(j)) {
uptr start = modules[i].address_range_start(j);
@@ -91,7 +91,6 @@
cached_mapping.SetModuleRange(start, end);
}
}
- InternalFree(module_name);
}
int err;
diff --git a/lib/sanitizer_common/sanitizer_flags.cc b/lib/sanitizer_common/sanitizer_flags.cc
index 0a70e16..40b6ec0 100644
--- a/lib/sanitizer_common/sanitizer_flags.cc
+++ b/lib/sanitizer_common/sanitizer_flags.cc
@@ -39,6 +39,7 @@
f->external_symbolizer_path = 0;
f->allow_addr2line = false;
f->strip_path_prefix = "";
+ f->fast_unwind_on_check = false;
f->fast_unwind_on_fatal = false;
f->fast_unwind_on_malloc = true;
f->handle_ioctl = false;
@@ -63,6 +64,11 @@
f->coverage_direct = SANITIZER_ANDROID;
f->coverage_dir = ".";
f->full_address_space = false;
+ f->suppressions = "";
+ f->print_suppressions = true;
+ f->disable_coredump = (SANITIZER_WORDSIZE == 64);
+ f->symbolize_inline_frames = true;
+ f->stack_trace_format = "DEFAULT";
}
void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
@@ -78,6 +84,9 @@
"unavailable.");
ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix",
"Strips this prefix from file paths in error reports.");
+ ParseFlag(str, &f->fast_unwind_on_check, "fast_unwind_on_check",
+ "If available, use the fast frame-pointer-based unwinder on "
+ "internal CHECK failures.");
ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal",
"If available, use the fast frame-pointer-based unwinder on fatal "
"errors.");
@@ -144,6 +153,19 @@
ParseFlag(str, &f->full_address_space, "full_address_space",
"Sanitize complete address space; "
"by default kernel area on 32-bit platforms will not be sanitized");
+ ParseFlag(str, &f->suppressions, "suppressions", "Suppressions file name.");
+ ParseFlag(str, &f->print_suppressions, "print_suppressions",
+ "Print matched suppressions at exit.");
+ ParseFlag(str, &f->disable_coredump, "disable_coredump",
+ "Disable core dumping. By default, disable_core=1 on 64-bit to avoid "
+ "dumping a 16T+ core file. Ignored on OSes that don't dump core by"
+ "default and for sanitizers that don't reserve lots of virtual memory.");
+ ParseFlag(str, &f->symbolize_inline_frames, "symbolize_inline_frames",
+ "Print inlined frames in stacktraces. Defaults to true.");
+ ParseFlag(str, &f->stack_trace_format, "stack_trace_format",
+ "Format string used to render stack frames. "
+ "See sanitizer_stacktrace_printer.h for the format description. "
+ "Use DEFAULT to get default format.");
// Do a sanity check for certain flags.
if (f->malloc_context_size < 1)
diff --git a/lib/sanitizer_common/sanitizer_flags.h b/lib/sanitizer_common/sanitizer_flags.h
index 41dc218..4791397 100644
--- a/lib/sanitizer_common/sanitizer_flags.h
+++ b/lib/sanitizer_common/sanitizer_flags.h
@@ -32,6 +32,7 @@
const char *external_symbolizer_path;
bool allow_addr2line;
const char *strip_path_prefix;
+ bool fast_unwind_on_check;
bool fast_unwind_on_fatal;
bool fast_unwind_on_malloc;
bool handle_ioctl;
@@ -57,6 +58,11 @@
bool coverage_direct;
const char *coverage_dir;
bool full_address_space;
+ const char *suppressions;
+ bool print_suppressions;
+ bool disable_coredump;
+ bool symbolize_inline_frames;
+ const char *stack_trace_format;
};
inline CommonFlags *common_flags() {
diff --git a/lib/sanitizer_common/sanitizer_freebsd.h b/lib/sanitizer_common/sanitizer_freebsd.h
index 52a2a85..c9bba80 100644
--- a/lib/sanitizer_common/sanitizer_freebsd.h
+++ b/lib/sanitizer_common/sanitizer_freebsd.h
@@ -22,10 +22,14 @@
#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
# include <osreldate.h>
# if __FreeBSD_version <= 902001 // v9.2
+# include <link.h>
+# include <sys/param.h>
# include <ucontext.h>
namespace __sanitizer {
+typedef unsigned long long __xuint64_t;
+
typedef __int32_t __xregister_t;
typedef struct __xmcontext {
@@ -74,6 +78,57 @@
int __spare__[4];
} xucontext_t;
+struct xkinfo_vmentry {
+ int kve_structsize;
+ int kve_type;
+ __xuint64_t kve_start;
+ __xuint64_t kve_end;
+ __xuint64_t kve_offset;
+ __xuint64_t kve_vn_fileid;
+ __uint32_t kve_vn_fsid;
+ int kve_flags;
+ int kve_resident;
+ int kve_private_resident;
+ int kve_protection;
+ int kve_ref_count;
+ int kve_shadow_count;
+ int kve_vn_type;
+ __xuint64_t kve_vn_size;
+ __uint32_t kve_vn_rdev;
+ __uint16_t kve_vn_mode;
+ __uint16_t kve_status;
+ int _kve_ispare[12];
+ char kve_path[PATH_MAX];
+};
+
+typedef struct {
+ __uint32_t p_type;
+ __uint32_t p_offset;
+ __uint32_t p_vaddr;
+ __uint32_t p_paddr;
+ __uint32_t p_filesz;
+ __uint32_t p_memsz;
+ __uint32_t p_flags;
+ __uint32_t p_align;
+} XElf32_Phdr;
+
+struct xdl_phdr_info {
+ Elf_Addr dlpi_addr;
+ const char *dlpi_name;
+ const XElf32_Phdr *dlpi_phdr;
+ Elf_Half dlpi_phnum;
+ unsigned long long int dlpi_adds;
+ unsigned long long int dlpi_subs;
+ size_t dlpi_tls_modid;
+ void *dlpi_tls_data;
+};
+
+typedef int (*__xdl_iterate_hdr_callback)(struct xdl_phdr_info*, size_t, void*);
+typedef int xdl_iterate_phdr_t(__xdl_iterate_hdr_callback, void*);
+
+#define xdl_iterate_phdr(callback, param) \
+ (((xdl_iterate_phdr_t*) dl_iterate_phdr)((callback), (param)))
+
} // namespace __sanitizer
# endif // __FreeBSD_version <= 902001
diff --git a/lib/sanitizer_common/sanitizer_interception.h b/lib/sanitizer_common/sanitizer_interception.h
deleted file mode 100644
index b63462d..0000000
--- a/lib/sanitizer_common/sanitizer_interception.h
+++ /dev/null
@@ -1,25 +0,0 @@
-//===-- sanitizer_interception.h --------------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Common macro definitions for interceptors.
-// Always use this headers instead of interception/interception.h.
-//
-//===----------------------------------------------------------------------===//
-#ifndef SANITIZER_INTERCEPTION_H
-#define SANITIZER_INTERCEPTION_H
-
-#include "interception/interception.h"
-#include "sanitizer_common.h"
-
-#if SANITIZER_LINUX && !defined(SANITIZER_GO)
-#undef REAL
-#define REAL(x) IndirectExternCall(__interception::PTR_TO_REAL(x))
-#endif
-
-#endif // SANITIZER_INTERCEPTION_H
diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h
index c8985b4..d77ca8f 100644
--- a/lib/sanitizer_common/sanitizer_internal_defs.h
+++ b/lib/sanitizer_common/sanitizer_internal_defs.h
@@ -34,6 +34,15 @@
# define SANITIZER_SUPPORTS_WEAK_HOOKS 0
#endif
+// We can use .preinit_array section on Linux to call sanitizer initialization
+// functions very early in the process startup (unless PIC macro is defined).
+// FIXME: do we have anything like this on Mac?
+#if SANITIZER_LINUX && !SANITIZER_ANDROID && !defined(PIC)
+# define SANITIZER_CAN_USE_PREINIT_ARRAY 1
+#else
+# define SANITIZER_CAN_USE_PREINIT_ARRAY 0
+#endif
+
// GCC does not understand __has_feature
#if !defined(__has_feature)
# define __has_feature(x) 0
diff --git a/lib/sanitizer_common/sanitizer_libc.cc b/lib/sanitizer_common/sanitizer_libc.cc
index 00596f5..8d8ad59 100644
--- a/lib/sanitizer_common/sanitizer_libc.cc
+++ b/lib/sanitizer_common/sanitizer_libc.cc
@@ -31,16 +31,16 @@
}
void *internal_memchr(const void *s, int c, uptr n) {
- const char* t = (char*)s;
+ const char *t = (const char *)s;
for (uptr i = 0; i < n; ++i, ++t)
if (*t == c)
- return (void*)t;
+ return reinterpret_cast<void *>(const_cast<char *>(t));
return 0;
}
int internal_memcmp(const void* s1, const void* s2, uptr n) {
- const char* t1 = (char*)s1;
- const char* t2 = (char*)s2;
+ const char *t1 = (const char *)s1;
+ const char *t2 = (const char *)s2;
for (uptr i = 0; i < n; ++i, ++t1, ++t2)
if (*t1 != *t2)
return *t1 < *t2 ? -1 : 1;
@@ -49,7 +49,7 @@
void *internal_memcpy(void *dest, const void *src, uptr n) {
char *d = (char*)dest;
- char *s = (char*)src;
+ const char *s = (const char *)src;
for (uptr i = 0; i < n; ++i)
d[i] = s[i];
return dest;
@@ -57,7 +57,7 @@
void *internal_memmove(void *dest, const void *src, uptr n) {
char *d = (char*)dest;
- char *s = (char*)src;
+ const char *s = (const char *)src;
sptr i, signed_n = (sptr)n;
CHECK_GE(signed_n, 0);
if (d < s) {
@@ -138,7 +138,7 @@
char* internal_strchr(const char *s, int c) {
while (true) {
if (*s == (char)c)
- return (char*)s;
+ return const_cast<char *>(s);
if (*s == 0)
return 0;
s++;
@@ -148,7 +148,7 @@
char *internal_strchrnul(const char *s, int c) {
char *res = internal_strchr(s, c);
if (!res)
- res = (char*)s + internal_strlen(s);
+ res = const_cast<char *>(s) + internal_strlen(s);
return res;
}
@@ -157,7 +157,7 @@
for (uptr i = 0; s[i]; i++) {
if (s[i] == c) res = s + i;
}
- return (char*)res;
+ return const_cast<char *>(res);
}
uptr internal_strlen(const char *s) {
@@ -196,7 +196,7 @@
if (len1 < len2) return 0;
for (uptr pos = 0; pos <= len1 - len2; pos++) {
if (internal_memcmp(haystack + pos, needle, len2) == 0)
- return (char*)haystack + pos;
+ return const_cast<char *>(haystack) + pos;
}
return 0;
}
@@ -207,7 +207,7 @@
int sgn = 1;
u64 res = 0;
bool have_digits = false;
- char *old_nptr = (char*)nptr;
+ char *old_nptr = const_cast<char *>(nptr);
if (*nptr == '+') {
sgn = 1;
nptr++;
@@ -223,7 +223,7 @@
nptr++;
}
if (endptr != 0) {
- *endptr = (have_digits) ? (char*)nptr : old_nptr;
+ *endptr = (have_digits) ? const_cast<char *>(nptr) : old_nptr;
}
if (sgn > 0) {
return (s64)(Min((u64)INT64_MAX, res));
diff --git a/lib/sanitizer_common/sanitizer_libignore.cc b/lib/sanitizer_common/sanitizer_libignore.cc
index a0bb871..44e4529 100644
--- a/lib/sanitizer_common/sanitizer_libignore.cc
+++ b/lib/sanitizer_common/sanitizer_libignore.cc
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
-#if SANITIZER_LINUX
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
#include "sanitizer_libignore.h"
#include "sanitizer_flags.h"
@@ -103,4 +103,4 @@
} // namespace __sanitizer
-#endif // #if SANITIZER_LINUX
+#endif // #if SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc
index 98640f7..acae5bb 100644
--- a/lib/sanitizer_common/sanitizer_linux.cc
+++ b/lib/sanitizer_common/sanitizer_linux.cc
@@ -537,7 +537,7 @@
__sanitizer_kernel_sigaction_t k_act, k_oldact;
internal_memset(&k_act, 0, sizeof(__sanitizer_kernel_sigaction_t));
internal_memset(&k_oldact, 0, sizeof(__sanitizer_kernel_sigaction_t));
- const __sanitizer_sigaction *u_act = (__sanitizer_sigaction *)act;
+ const __sanitizer_sigaction *u_act = (const __sanitizer_sigaction *)act;
__sanitizer_sigaction *u_oldact = (__sanitizer_sigaction *)oldact;
if (u_act) {
k_act.handler = u_act->handler;
diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
index 92e6b78..2ca55b4 100644
--- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
@@ -17,6 +17,7 @@
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
+#include "sanitizer_freebsd.h"
#include "sanitizer_linux.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h"
@@ -24,17 +25,17 @@
#include "sanitizer_atomic.h"
#include "sanitizer_symbolizer.h"
-#include <dlfcn.h>
+#if SANITIZER_ANDROID || SANITIZER_FREEBSD
+#include <dlfcn.h> // for dlsym()
+#endif
+
#include <pthread.h>
#include <signal.h>
#include <sys/resource.h>
-#if SANITIZER_FREEBSD
-#define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
-#endif
-#include <unwind.h>
#if SANITIZER_FREEBSD
#include <pthread_np.h>
+#include <osreldate.h>
#define pthread_getattr_np pthread_attr_get_np
#endif
@@ -68,7 +69,8 @@
int internal_sigaction(int signum, const void *act, void *oldact) {
if (real_sigaction)
return real_sigaction(signum, act, oldact);
- return sigaction(signum, (struct sigaction *)act, (struct sigaction *)oldact);
+ return sigaction(signum, (const struct sigaction *)act,
+ (struct sigaction *)oldact);
}
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
@@ -126,7 +128,7 @@
setenv_ft setenv_f;
CHECK_EQ(sizeof(setenv_f), sizeof(f));
internal_memcpy(&setenv_f, &f, sizeof(f));
- return IndirectExternCall(setenv_f)(name, value, 1) == 0;
+ return setenv_f(name, value, 1) == 0;
}
bool SanitizerSetThreadName(const char *name) {
@@ -150,127 +152,6 @@
#endif
}
-//------------------------- SlowUnwindStack -----------------------------------
-
-typedef struct {
- uptr absolute_pc;
- uptr stack_top;
- uptr stack_size;
-} backtrace_frame_t;
-
-extern "C" {
-typedef void *(*acquire_my_map_info_list_func)();
-typedef void (*release_my_map_info_list_func)(void *map);
-typedef sptr (*unwind_backtrace_signal_arch_func)(
- void *siginfo, void *sigcontext, void *map_info_list,
- backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
-acquire_my_map_info_list_func acquire_my_map_info_list;
-release_my_map_info_list_func release_my_map_info_list;
-unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
-} // extern "C"
-
-#if SANITIZER_ANDROID
-void SanitizerInitializeUnwinder() {
- void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
- if (!p) {
- VReport(1,
- "Failed to open libcorkscrew.so. You may see broken stack traces "
- "in SEGV reports.");
- return;
- }
- acquire_my_map_info_list =
- (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
- release_my_map_info_list =
- (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
- unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
- p, "unwind_backtrace_signal_arch");
- if (!acquire_my_map_info_list || !release_my_map_info_list ||
- !unwind_backtrace_signal_arch) {
- VReport(1,
- "Failed to find one of the required symbols in libcorkscrew.so. "
- "You may see broken stack traces in SEGV reports.");
- acquire_my_map_info_list = NULL;
- unwind_backtrace_signal_arch = NULL;
- release_my_map_info_list = NULL;
- }
-}
-#endif
-
-#ifdef __arm__
-#define UNWIND_STOP _URC_END_OF_STACK
-#define UNWIND_CONTINUE _URC_NO_REASON
-#else
-#define UNWIND_STOP _URC_NORMAL_STOP
-#define UNWIND_CONTINUE _URC_NO_REASON
-#endif
-
-uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
-#ifdef __arm__
- uptr val;
- _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
- 15 /* r15 = PC */, _UVRSD_UINT32, &val);
- CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
- // Clear the Thumb bit.
- return val & ~(uptr)1;
-#else
- return _Unwind_GetIP(ctx);
-#endif
-}
-
-struct UnwindTraceArg {
- StackTrace *stack;
- uptr max_depth;
-};
-
-_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
- UnwindTraceArg *arg = (UnwindTraceArg*)param;
- CHECK_LT(arg->stack->size, arg->max_depth);
- uptr pc = Unwind_GetIP(ctx);
- arg->stack->trace[arg->stack->size++] = pc;
- if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
- return UNWIND_CONTINUE;
-}
-
-void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
- CHECK_GE(max_depth, 2);
- size = 0;
- 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;
-}
-
-void StackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
- uptr max_depth) {
- CHECK_GE(max_depth, 2);
- if (!unwind_backtrace_signal_arch) {
- SlowUnwindStack(pc, max_depth);
- return;
- }
-
- void *map = acquire_my_map_info_list();
- CHECK(map);
- InternalScopedBuffer<backtrace_frame_t> frames(kStackTraceMax);
- // siginfo argument appears to be unused.
- sptr res = unwind_backtrace_signal_arch(/* siginfo */ NULL, context, map,
- frames.data(),
- /* ignore_depth */ 0, max_depth);
- release_my_map_info_list(map);
- if (res < 0) return;
- CHECK_LE((uptr)res, kStackTraceMax);
-
- size = 0;
- // +2 compensate for libcorkscrew unwinder returning addresses of call
- // instructions instead of raw return addresses.
- for (sptr i = 0; i < res; ++i)
- trace[size++] = frames[i].absolute_pc + 2;
-}
-
#if !SANITIZER_FREEBSD
static uptr g_tls_size;
#endif
@@ -292,7 +173,7 @@
CHECK_NE(get_tls, 0);
size_t tls_size = 0;
size_t tls_align = 0;
- IndirectExternCall(get_tls)(&tls_size, &tls_align);
+ get_tls(&tls_size, &tls_align);
g_tls_size = tls_size;
#endif // !SANITIZER_FREEBSD && !SANITIZER_ANDROID
}
@@ -471,6 +352,10 @@
#else // SANITIZER_ANDROID
# if !SANITIZER_FREEBSD
typedef ElfW(Phdr) Elf_Phdr;
+# elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2
+# define Elf_Phdr XElf32_Phdr
+# define dl_phdr_info xdl_phdr_info
+# define dl_iterate_phdr(c, b) xdl_iterate_phdr((c), (b))
# endif
struct DlIteratePhdrData {
@@ -523,14 +408,6 @@
}
#endif // SANITIZER_ANDROID
-uptr indirect_call_wrapper;
-
-void SetIndirectCallWrapper(uptr wrapper) {
- CHECK(!indirect_call_wrapper);
- CHECK(wrapper);
- indirect_call_wrapper = wrapper;
-}
-
void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
// Some kinds of sandboxes may forbid filesystem access, so we won't be able
// to read the file mappings from /proc/self/maps. Luckily, neither the
@@ -539,8 +416,7 @@
MemoryMappingLayout::CacheMemoryMappings();
// Same for /proc/self/exe in the symbolizer.
#if !SANITIZER_GO
- if (Symbolizer *sym = Symbolizer::GetOrNull())
- sym->PrepareForSandboxing();
+ Symbolizer::GetOrInit()->PrepareForSandboxing();
CovPrepareForSandboxing(args);
#endif
}
diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc
index 5985bd2..1b77087 100644
--- a/lib/sanitizer_common/sanitizer_mac.cc
+++ b/lib/sanitizer_common/sanitizer_mac.cc
@@ -160,7 +160,7 @@
// pthread_get_stacksize_np() returns an incorrect stack size for the main
// thread on Mavericks. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=261
- if ((GetMacosVersion() == MACOS_VERSION_MAVERICKS) && at_initialization &&
+ if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization &&
stacksize == (1 << 19)) {
struct rlimit rl;
CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
@@ -297,6 +297,7 @@
case '1': return MACOS_VERSION_LION;
case '2': return MACOS_VERSION_MOUNTAIN_LION;
case '3': return MACOS_VERSION_MAVERICKS;
+ case '4': return MACOS_VERSION_YOSEMITE;
default: return MACOS_VERSION_UNKNOWN;
}
}
diff --git a/lib/sanitizer_common/sanitizer_mac.h b/lib/sanitizer_common/sanitizer_mac.h
index fae784a..3ed0ed3 100644
--- a/lib/sanitizer_common/sanitizer_mac.h
+++ b/lib/sanitizer_common/sanitizer_mac.h
@@ -25,7 +25,8 @@
MACOS_VERSION_SNOW_LEOPARD,
MACOS_VERSION_LION,
MACOS_VERSION_MOUNTAIN_LION,
- MACOS_VERSION_MAVERICKS
+ MACOS_VERSION_MAVERICKS,
+ MACOS_VERSION_YOSEMITE,
};
MacosVersion GetMacosVersion();
diff --git a/lib/sanitizer_common/sanitizer_platform.h b/lib/sanitizer_common/sanitizer_platform.h
index a369599..6f8cd30 100644
--- a/lib/sanitizer_common/sanitizer_platform.h
+++ b/lib/sanitizer_common/sanitizer_platform.h
@@ -81,7 +81,7 @@
// For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or
// change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here.
#ifndef SANITIZER_CAN_USE_ALLOCATOR64
-# if defined(__aarch64__)
+# if defined(__aarch64__) || defined(__mips64)
# define SANITIZER_CAN_USE_ALLOCATOR64 0
# else
# define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64)
@@ -94,6 +94,8 @@
// but will consume more memory for TwoLevelByteMap.
#if defined(__aarch64__)
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 39)
+#elif defined(__mips__)
+# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40)
#else
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47)
#endif
@@ -109,4 +111,10 @@
# endif
#endif
+#ifdef __mips__
+# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10)
+#else
+# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12)
+#endif
+
#endif // SANITIZER_PLATFORM_H
diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h
index e9d5c35..95c2e9d 100644
--- a/lib/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h
@@ -29,6 +29,12 @@
# define SI_LINUX_NOT_ANDROID 0
#endif
+#if SANITIZER_FREEBSD
+# define SI_FREEBSD 1
+#else
+# define SI_FREEBSD 0
+#endif
+
#if SANITIZER_LINUX
# define SI_LINUX 1
#else
@@ -145,7 +151,8 @@
#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_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SIGSETOPS \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID
@@ -196,10 +203,11 @@
// 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__EXIT SI_LINUX || SI_FREEBSD
#define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS
-#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_TLS_GET_ADDR SI_LINUX_NOT_ANDROID
@@ -221,6 +229,11 @@
#define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_FFLUSH SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_FCLOSE SI_NOT_WINDOWS
-#define SANITIZER_INTERCEPT_DLOPEN_DLCLOSE SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_DLOPEN_DLCLOSE \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_GETPASS SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID
+
+#define SANITIZER_INTERCEPT_MLOCKX SI_NOT_WINDOWS
#endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
index fa963e6..fc09522 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
@@ -412,7 +412,7 @@
unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
#endif
- unsigned IOCTL_NOT_PRESENT = 0;
+ const unsigned IOCTL_NOT_PRESENT = 0;
unsigned IOCTL_FIOASYNC = FIOASYNC;
unsigned IOCTL_FIOCLEX = FIOCLEX;
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
index 8ec1c58..80a3ddb 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
@@ -472,7 +472,7 @@
typedef long __sanitizer___kernel_off_t;
#endif
-#if defined(__powerpc__) || defined(__aarch64__) || defined(__mips__)
+#if defined(__powerpc__) || defined(__mips__)
typedef unsigned int __sanitizer___kernel_old_uid_t;
typedef unsigned int __sanitizer___kernel_old_gid_t;
#else
@@ -861,7 +861,7 @@
// 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 const unsigned IOCTL_NOT_PRESENT;
extern unsigned IOCTL_FIOASYNC;
extern unsigned IOCTL_FIOCLEX;
diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc
index 8df1fa7..eb2497b 100644
--- a/lib/sanitizer_common/sanitizer_posix.cc
+++ b/lib/sanitizer_common/sanitizer_posix.cc
@@ -84,9 +84,12 @@
// 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
+ // This should (does) work for both PowerPC64 Endian modes.
+ return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1;
# elif defined(__aarch64__)
return (1ULL << 39) - 1;
+# elif defined(__mips64)
+ return (1ULL << 40) - 1; // 0x000000ffffffffffUL;
# else
return (1ULL << 47) - 1; // 0x00007fffffffffffUL;
# endif
diff --git a/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_posix_libcdep.cc
index e859959..ed1e372 100644
--- a/lib/sanitizer_common/sanitizer_posix_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_posix_libcdep.cc
@@ -44,30 +44,49 @@
madvise((void*)addr, size, MADV_DONTNEED);
}
-void DisableCoreDumper() {
- struct rlimit nocore;
- nocore.rlim_cur = 0;
- nocore.rlim_max = 0;
- setrlimit(RLIMIT_CORE, &nocore);
+static rlim_t getlim(int res) {
+ rlimit rlim;
+ CHECK_EQ(0, getrlimit(res, &rlim));
+ return rlim.rlim_cur;
}
-bool StackSizeIsUnlimited() {
- struct rlimit rlim;
- CHECK_EQ(0, getrlimit(RLIMIT_STACK, &rlim));
- return ((uptr)rlim.rlim_cur == (uptr)-1);
-}
-
-void SetStackSizeLimitInBytes(uptr limit) {
- struct rlimit rlim;
- rlim.rlim_cur = limit;
- rlim.rlim_max = limit;
- if (setrlimit(RLIMIT_STACK, &rlim)) {
+static void setlim(int res, rlim_t lim) {
+ // The following magic is to prevent clang from replacing it with memset.
+ volatile struct rlimit rlim;
+ rlim.rlim_cur = lim;
+ rlim.rlim_max = lim;
+ if (setrlimit(res, const_cast<struct rlimit *>(&rlim))) {
Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
Die();
}
+}
+
+void DisableCoreDumperIfNecessary() {
+ if (common_flags()->disable_coredump) {
+ setlim(RLIMIT_CORE, 0);
+ }
+}
+
+bool StackSizeIsUnlimited() {
+ rlim_t stack_size = getlim(RLIMIT_STACK);
+ return (stack_size == RLIM_INFINITY);
+}
+
+void SetStackSizeLimitInBytes(uptr limit) {
+ setlim(RLIMIT_STACK, (rlim_t)limit);
CHECK(!StackSizeIsUnlimited());
}
+bool AddressSpaceIsUnlimited() {
+ rlim_t as_size = getlim(RLIMIT_AS);
+ return (as_size == RLIM_INFINITY);
+}
+
+void SetAddressSpaceUnlimited() {
+ setlim(RLIMIT_AS, RLIM_INFINITY);
+ CHECK(AddressSpaceIsUnlimited());
+}
+
void SleepForSeconds(int seconds) {
sleep(seconds);
}
@@ -129,7 +148,9 @@
struct sigaction sigact;
internal_memset(&sigact, 0, sizeof(sigact));
sigact.sa_sigaction = (sa_sigaction_t)handler;
- sigact.sa_flags = SA_SIGINFO;
+ // Do not block the signal from being received in that signal's handler.
+ // Clients are responsible for handling this correctly.
+ sigact.sa_flags = SA_SIGINFO | SA_NODEFER;
if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
CHECK_EQ(0, internal_sigaction(signum, &sigact, 0));
VReport(1, "Installed the sigaction for signal %d\n", signum);
@@ -145,6 +166,28 @@
}
#endif // SANITIZER_GO
+bool IsAccessibleMemoryRange(uptr beg, uptr size) {
+ uptr page_size = GetPageSizeCached();
+ // Checking too large memory ranges is slow.
+ CHECK_LT(size, page_size * 10);
+ int sock_pair[2];
+ if (pipe(sock_pair))
+ return false;
+ uptr bytes_written =
+ internal_write(sock_pair[1], reinterpret_cast<void *>(beg), size);
+ int write_errno;
+ bool result;
+ if (internal_iserror(bytes_written, &write_errno)) {
+ CHECK_EQ(EFAULT, write_errno);
+ result = false;
+ } else {
+ result = (bytes_written == size);
+ }
+ internal_close(sock_pair[0]);
+ internal_close(sock_pair[1]);
+ return result;
+}
+
} // namespace __sanitizer
#endif // SANITIZER_POSIX
diff --git a/lib/sanitizer_common/sanitizer_printf.cc b/lib/sanitizer_common/sanitizer_printf.cc
index 8018131..3be6723 100644
--- a/lib/sanitizer_common/sanitizer_printf.cc
+++ b/lib/sanitizer_common/sanitizer_printf.cc
@@ -22,7 +22,8 @@
#include <stdio.h>
#include <stdarg.h>
-#if SANITIZER_WINDOWS && !defined(va_copy)
+#if SANITIZER_WINDOWS && defined(_MSC_VER) && _MSC_VER < 1800 && \
+ !defined(va_copy)
# define va_copy(dst, src) ((dst) = (src))
#endif
@@ -112,7 +113,7 @@
int result = 0;
result += AppendString(buff, buff_end, -1, "0x");
result += AppendUnsigned(buff, buff_end, ptr_value, 16,
- (SANITIZER_WORDSIZE == 64) ? 12 : 8, true);
+ SANITIZER_POINTER_FORMAT_LENGTH, true);
return result;
}
diff --git a/lib/sanitizer_common/sanitizer_procmaps.h b/lib/sanitizer_common/sanitizer_procmaps.h
index c59a27c..94e3871 100644
--- a/lib/sanitizer_common/sanitizer_procmaps.h
+++ b/lib/sanitizer_common/sanitizer_procmaps.h
@@ -26,6 +26,9 @@
uptr mmaped_size;
uptr len;
};
+
+// Reads process memory map in an OS-specific way.
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps);
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
class MemoryMappingLayout {
@@ -57,7 +60,7 @@
// platform-specific files.
# if SANITIZER_FREEBSD || SANITIZER_LINUX
ProcSelfMapsBuff proc_self_maps_;
- char *current_;
+ const char *current_;
// Static mappings cache.
static ProcSelfMapsBuff cached_proc_self_maps_;
@@ -86,6 +89,11 @@
// Returns code range for the specified module.
bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end);
+bool IsDecimal(char c);
+uptr ParseDecimal(const char **p);
+bool IsHex(char c);
+uptr ParseHex(const char **p);
+
} // namespace __sanitizer
#endif // SANITIZER_PROCMAPS_H
diff --git a/lib/sanitizer_common/sanitizer_procmaps_common.cc b/lib/sanitizer_common/sanitizer_procmaps_common.cc
new file mode 100644
index 0000000..3b1a311
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_procmaps_common.cc
@@ -0,0 +1,178 @@
+//===-- sanitizer_procmaps_common.cc --------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (common parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
+#include "sanitizer_common.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+
+namespace __sanitizer {
+
+// Linker initialized.
+ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
+StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized.
+
+static int TranslateDigit(char c) {
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return -1;
+}
+
+// Parse a number and promote 'p' up to the first non-digit character.
+static uptr ParseNumber(const char **p, int base) {
+ uptr n = 0;
+ int d;
+ CHECK(base >= 2 && base <= 16);
+ while ((d = TranslateDigit(**p)) >= 0 && d < base) {
+ n = n * base + d;
+ (*p)++;
+ }
+ return n;
+}
+
+bool IsDecimal(char c) {
+ int d = TranslateDigit(c);
+ return d >= 0 && d < 10;
+}
+
+uptr ParseDecimal(const char **p) {
+ return ParseNumber(p, 10);
+}
+
+bool IsHex(char c) {
+ int d = TranslateDigit(c);
+ return d >= 0 && d < 16;
+}
+
+uptr ParseHex(const char **p) {
+ return ParseNumber(p, 16);
+}
+
+MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
+ ReadProcMaps(&proc_self_maps_);
+ if (cache_enabled) {
+ if (proc_self_maps_.mmaped_size == 0) {
+ LoadFromCache();
+ CHECK_GT(proc_self_maps_.len, 0);
+ }
+ } else {
+ CHECK_GT(proc_self_maps_.mmaped_size, 0);
+ }
+ Reset();
+ // FIXME: in the future we may want to cache the mappings on demand only.
+ if (cache_enabled)
+ CacheMemoryMappings();
+}
+
+MemoryMappingLayout::~MemoryMappingLayout() {
+ // Only unmap the buffer if it is different from the cached one. Otherwise
+ // it will be unmapped when the cache is refreshed.
+ if (proc_self_maps_.data != cached_proc_self_maps_.data) {
+ UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
+ }
+}
+
+void MemoryMappingLayout::Reset() {
+ current_ = proc_self_maps_.data;
+}
+
+// static
+void MemoryMappingLayout::CacheMemoryMappings() {
+ SpinMutexLock l(&cache_lock_);
+ // Don't invalidate the cache if the mappings are unavailable.
+ ProcSelfMapsBuff old_proc_self_maps;
+ old_proc_self_maps = cached_proc_self_maps_;
+ ReadProcMaps(&cached_proc_self_maps_);
+ if (cached_proc_self_maps_.mmaped_size == 0) {
+ cached_proc_self_maps_ = old_proc_self_maps;
+ } else {
+ if (old_proc_self_maps.mmaped_size) {
+ UnmapOrDie(old_proc_self_maps.data,
+ old_proc_self_maps.mmaped_size);
+ }
+ }
+}
+
+void MemoryMappingLayout::LoadFromCache() {
+ SpinMutexLock l(&cache_lock_);
+ if (cached_proc_self_maps_.data) {
+ proc_self_maps_ = cached_proc_self_maps_;
+ }
+}
+
+uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
+ uptr max_modules,
+ string_predicate_t filter) {
+ Reset();
+ uptr cur_beg, cur_end, cur_offset, prot;
+ InternalScopedBuffer<char> module_name(kMaxPathLength);
+ uptr n_modules = 0;
+ for (uptr i = 0; n_modules < max_modules &&
+ Next(&cur_beg, &cur_end, &cur_offset, module_name.data(),
+ module_name.size(), &prot);
+ i++) {
+ const char *cur_name = module_name.data();
+ if (cur_name[0] == '\0')
+ continue;
+ if (filter && !filter(cur_name))
+ continue;
+ void *mem = &modules[n_modules];
+ // Don't subtract 'cur_beg' from the first entry:
+ // * If a binary is compiled w/o -pie, then the first entry in
+ // process maps is likely the binary itself (all dynamic libs
+ // are mapped higher in address space). For such a binary,
+ // instruction offset in binary coincides with the actual
+ // instruction address in virtual memory (as code section
+ // is mapped to a fixed memory range).
+ // * If a binary is compiled with -pie, all the modules are
+ // mapped high at address space (in particular, higher than
+ // shadow memory of the tool), so the module can't be the
+ // first entry.
+ uptr base_address = (i ? cur_beg : 0) - cur_offset;
+ LoadedModule *cur_module = new(mem) LoadedModule(cur_name, base_address);
+ cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
+ n_modules++;
+ }
+ return n_modules;
+}
+
+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 = ParseHex(&pos);
+ for (; *pos != '/' && *pos > '\n'; pos++) {}
+ file = *pos == '/';
+ } else if (internal_strncmp(pos, "Rss:", 4) == 0) {
+ while (!IsDecimal(*pos)) pos++;
+ uptr rss = ParseDecimal(&pos) * 1024;
+ cb(start, rss, file, stats, stats_size);
+ }
+ while (*pos++ != '\n') {}
+ }
+ UnmapOrDie(smaps, smaps_cap);
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/lib/sanitizer_common/sanitizer_procmaps_freebsd.cc b/lib/sanitizer_common/sanitizer_procmaps_freebsd.cc
new file mode 100644
index 0000000..5011b1f
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_procmaps_freebsd.cc
@@ -0,0 +1,88 @@
+//===-- sanitizer_procmaps_freebsd.cc -------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (FreeBSD-specific parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_FREEBSD
+#include "sanitizer_common.h"
+#include "sanitizer_freebsd.h"
+#include "sanitizer_procmaps.h"
+
+#include <unistd.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+// Fix 'kinfo_vmentry' definition on FreeBSD prior v9.2 in 32-bit mode.
+#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
+# include <osreldate.h>
+# if __FreeBSD_version <= 902001 // v9.2
+# define kinfo_vmentry xkinfo_vmentry
+# endif
+#endif
+
+namespace __sanitizer {
+
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
+ const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid() };
+ size_t Size = 0;
+ int Err = sysctl(Mib, 4, NULL, &Size, NULL, 0);
+ CHECK_EQ(Err, 0);
+ CHECK_GT(Size, 0);
+
+ size_t MmapedSize = Size * 4 / 3;
+ void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
+ Size = MmapedSize;
+ Err = sysctl(Mib, 4, VmMap, &Size, NULL, 0);
+ CHECK_EQ(Err, 0);
+
+ proc_maps->data = (char*)VmMap;
+ proc_maps->mmaped_size = MmapedSize;
+ proc_maps->len = Size;
+}
+
+bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
+ char filename[], uptr filename_size,
+ uptr *protection) {
+ char *last = proc_self_maps_.data + proc_self_maps_.len;
+ if (current_ >= last) return false;
+ uptr dummy;
+ if (!start) start = &dummy;
+ if (!end) end = &dummy;
+ if (!offset) offset = &dummy;
+ if (!protection) protection = &dummy;
+ struct kinfo_vmentry *VmEntry = (struct kinfo_vmentry*)current_;
+
+ *start = (uptr)VmEntry->kve_start;
+ *end = (uptr)VmEntry->kve_end;
+ *offset = (uptr)VmEntry->kve_offset;
+
+ *protection = 0;
+ if ((VmEntry->kve_protection & KVME_PROT_READ) != 0)
+ *protection |= kProtectionRead;
+ if ((VmEntry->kve_protection & KVME_PROT_WRITE) != 0)
+ *protection |= kProtectionWrite;
+ if ((VmEntry->kve_protection & KVME_PROT_EXEC) != 0)
+ *protection |= kProtectionExecute;
+
+ if (filename != NULL && filename_size > 0) {
+ internal_snprintf(filename,
+ Min(filename_size, (uptr)PATH_MAX),
+ "%s", VmEntry->kve_path);
+ }
+
+ current_ += VmEntry->kve_structsize;
+
+ return true;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FREEBSD
diff --git a/lib/sanitizer_common/sanitizer_procmaps_linux.cc b/lib/sanitizer_common/sanitizer_procmaps_linux.cc
index c647765..79ca4df 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_linux.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_linux.cc
@@ -11,151 +11,20 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
-#if SANITIZER_FREEBSD || SANITIZER_LINUX
+#if SANITIZER_LINUX
#include "sanitizer_common.h"
-#include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h"
-#if SANITIZER_FREEBSD
-#include <unistd.h>
-#include <sys/sysctl.h>
-#include <sys/user.h>
-#endif
-
namespace __sanitizer {
-// Linker initialized.
-ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
-StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized.
-
-static void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
-#if SANITIZER_FREEBSD
- const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid() };
- size_t Size = 0;
- int Err = sysctl(Mib, 4, NULL, &Size, NULL, 0);
- CHECK_EQ(Err, 0);
- CHECK_GT(Size, 0);
-
- size_t MmapedSize = Size * 4 / 3;
- void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
- Size = MmapedSize;
- Err = sysctl(Mib, 4, VmMap, &Size, NULL, 0);
- CHECK_EQ(Err, 0);
-
- proc_maps->data = (char*)VmMap;
- proc_maps->mmaped_size = MmapedSize;
- proc_maps->len = Size;
-#else
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
proc_maps->len = ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
&proc_maps->mmaped_size, 1 << 26);
-#endif
-}
-
-MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
- ReadProcMaps(&proc_self_maps_);
- if (cache_enabled) {
- if (proc_self_maps_.mmaped_size == 0) {
- LoadFromCache();
- CHECK_GT(proc_self_maps_.len, 0);
- }
- } else {
- CHECK_GT(proc_self_maps_.mmaped_size, 0);
- }
- Reset();
- // FIXME: in the future we may want to cache the mappings on demand only.
- if (cache_enabled)
- CacheMemoryMappings();
-}
-
-MemoryMappingLayout::~MemoryMappingLayout() {
- // Only unmap the buffer if it is different from the cached one. Otherwise
- // it will be unmapped when the cache is refreshed.
- if (proc_self_maps_.data != cached_proc_self_maps_.data) {
- UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
- }
-}
-
-void MemoryMappingLayout::Reset() {
- current_ = proc_self_maps_.data;
-}
-
-// static
-void MemoryMappingLayout::CacheMemoryMappings() {
- SpinMutexLock l(&cache_lock_);
- // Don't invalidate the cache if the mappings are unavailable.
- ProcSelfMapsBuff old_proc_self_maps;
- old_proc_self_maps = cached_proc_self_maps_;
- ReadProcMaps(&cached_proc_self_maps_);
- if (cached_proc_self_maps_.mmaped_size == 0) {
- cached_proc_self_maps_ = old_proc_self_maps;
- } else {
- if (old_proc_self_maps.mmaped_size) {
- UnmapOrDie(old_proc_self_maps.data,
- old_proc_self_maps.mmaped_size);
- }
- }
-}
-
-void MemoryMappingLayout::LoadFromCache() {
- SpinMutexLock l(&cache_lock_);
- if (cached_proc_self_maps_.data) {
- proc_self_maps_ = cached_proc_self_maps_;
- }
-}
-
-#if !SANITIZER_FREEBSD
-// Parse a hex value in str and update str.
-static uptr ParseHex(char **str) {
- uptr x = 0;
- char *s;
- for (s = *str; ; s++) {
- char c = *s;
- uptr v = 0;
- if (c >= '0' && c <= '9')
- v = c - '0';
- else if (c >= 'a' && c <= 'f')
- v = c - 'a' + 10;
- else if (c >= 'A' && c <= 'F')
- v = c - 'A' + 10;
- else
- break;
- x = x * 16 + v;
- }
- *str = s;
- return x;
}
static bool IsOneOf(char c, char c1, char c2) {
return c == c1 || c == c2;
}
-#endif
-
-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,
@@ -167,29 +36,6 @@
if (!end) end = &dummy;
if (!offset) offset = &dummy;
if (!protection) protection = &dummy;
-#if SANITIZER_FREEBSD
- struct kinfo_vmentry *VmEntry = (struct kinfo_vmentry*)current_;
-
- *start = (uptr)VmEntry->kve_start;
- *end = (uptr)VmEntry->kve_end;
- *offset = (uptr)VmEntry->kve_offset;
-
- *protection = 0;
- if ((VmEntry->kve_protection & KVME_PROT_READ) != 0)
- *protection |= kProtectionRead;
- if ((VmEntry->kve_protection & KVME_PROT_WRITE) != 0)
- *protection |= kProtectionWrite;
- if ((VmEntry->kve_protection & KVME_PROT_EXEC) != 0)
- *protection |= kProtectionExecute;
-
- if (filename != NULL && filename_size > 0) {
- internal_snprintf(filename,
- Min(filename_size, (uptr)PATH_MAX),
- "%s", VmEntry->kve_path);
- }
-
- current_ += VmEntry->kve_structsize;
-#else // !SANITIZER_FREEBSD
char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
if (next_line == 0)
next_line = last;
@@ -236,69 +82,9 @@
if (filename && i < filename_size)
filename[i] = 0;
current_ = next_line + 1;
-#endif // !SANITIZER_FREEBSD
return true;
}
-uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
- uptr max_modules,
- string_predicate_t filter) {
- Reset();
- uptr cur_beg, cur_end, cur_offset, prot;
- InternalScopedBuffer<char> module_name(kMaxPathLength);
- uptr n_modules = 0;
- for (uptr i = 0; n_modules < max_modules &&
- Next(&cur_beg, &cur_end, &cur_offset, module_name.data(),
- module_name.size(), &prot);
- i++) {
- const char *cur_name = module_name.data();
- if (cur_name[0] == '\0')
- continue;
- if (filter && !filter(cur_name))
- continue;
- void *mem = &modules[n_modules];
- // Don't subtract 'cur_beg' from the first entry:
- // * If a binary is compiled w/o -pie, then the first entry in
- // process maps is likely the binary itself (all dynamic libs
- // are mapped higher in address space). For such a binary,
- // instruction offset in binary coincides with the actual
- // instruction address in virtual memory (as code section
- // is mapped to a fixed memory range).
- // * If a binary is compiled with -pie, all the modules are
- // mapped high at address space (in particular, higher than
- // shadow memory of the tool), so the module can't be the
- // first entry.
- uptr base_address = (i ? cur_beg : 0) - cur_offset;
- LoadedModule *cur_module = new(mem) LoadedModule(cur_name, base_address);
- cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
- n_modules++;
- }
- return n_modules;
-}
-
-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);
-}
-
} // namespace __sanitizer
-#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
+#endif // SANITIZER_LINUX
diff --git a/lib/sanitizer_common/sanitizer_stackdepot.cc b/lib/sanitizer_common/sanitizer_stackdepot.cc
index 5b70dfc..f10f1f9 100644
--- a/lib/sanitizer_common/sanitizer_stackdepot.cc
+++ b/lib/sanitizer_common/sanitizer_stackdepot.cc
@@ -18,31 +18,6 @@
namespace __sanitizer {
-struct StackDepotDesc {
- const uptr *stack;
- uptr size;
- u32 hash() const {
- // murmur2
- const u32 m = 0x5bd1e995;
- const u32 seed = 0x9747b28c;
- const u32 r = 24;
- u32 h = seed ^ (size * sizeof(uptr));
- for (uptr i = 0; i < size; i++) {
- u32 k = stack[i];
- k *= m;
- k ^= k >> r;
- k *= m;
- h *= m;
- h ^= k;
- }
- h ^= h >> 13;
- h *= m;
- h ^= h >> 15;
- return h;
- }
- bool is_valid() { return size > 0 && stack; }
-};
-
struct StackDepotNode {
StackDepotNode *link;
u32 id;
@@ -58,28 +33,49 @@
static const u32 kUseCountMask = (1 << kUseCountBits) - 1;
static const u32 kHashMask = ~kUseCountMask;
- typedef StackDepotDesc args_type;
+ typedef StackTrace args_type;
bool eq(u32 hash, const args_type &args) const {
u32 hash_bits =
atomic_load(&hash_and_use_count, memory_order_relaxed) & kHashMask;
if ((hash & kHashMask) != hash_bits || args.size != size) return false;
uptr i = 0;
for (; i < size; i++) {
- if (stack[i] != args.stack[i]) return false;
+ if (stack[i] != args.trace[i]) return false;
}
return true;
}
static uptr storage_size(const args_type &args) {
return sizeof(StackDepotNode) + (args.size - 1) * sizeof(uptr);
}
+ static u32 hash(const args_type &args) {
+ // murmur2
+ const u32 m = 0x5bd1e995;
+ const u32 seed = 0x9747b28c;
+ const u32 r = 24;
+ u32 h = seed ^ (args.size * sizeof(uptr));
+ for (uptr i = 0; i < args.size; i++) {
+ u32 k = args.trace[i];
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+ h *= m;
+ h ^= k;
+ }
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+ return h;
+ }
+ static bool is_valid(const args_type &args) {
+ return args.size > 0 && args.trace;
+ }
void store(const args_type &args, u32 hash) {
atomic_store(&hash_and_use_count, hash & kHashMask, memory_order_relaxed);
size = args.size;
- internal_memcpy(stack, args.stack, size * sizeof(uptr));
+ internal_memcpy(stack, args.trace, size * sizeof(uptr));
}
args_type load() const {
- args_type ret = {&stack[0], size};
- return ret;
+ return args_type(&stack[0], size);
}
StackDepotHandle get_handle() { return StackDepotHandle(this); }
@@ -99,8 +95,6 @@
StackDepotNode::kUseCountMask;
CHECK_LT(prev + 1, StackDepotNode::kMaxUseCount);
}
-uptr StackDepotHandle::size() { return node_->size; }
-uptr *StackDepotHandle::stack() { return &node_->stack[0]; }
// FIXME(dvyukov): this single reserved bit is used in TSan.
typedef StackDepotBase<StackDepotNode, 1, StackDepotNode::kTabSizeLog>
@@ -111,21 +105,25 @@
return theDepot.GetStats();
}
-u32 StackDepotPut(const uptr *stack, uptr size) {
- StackDepotDesc desc = {stack, size};
- StackDepotHandle h = theDepot.Put(desc);
+u32 StackDepotPut(StackTrace stack) {
+ StackDepotHandle h = theDepot.Put(stack);
return h.valid() ? h.id() : 0;
}
-StackDepotHandle StackDepotPut_WithHandle(const uptr *stack, uptr size) {
- StackDepotDesc desc = {stack, size};
- return theDepot.Put(desc);
+StackDepotHandle StackDepotPut_WithHandle(StackTrace stack) {
+ return theDepot.Put(stack);
}
-const uptr *StackDepotGet(u32 id, uptr *size) {
- StackDepotDesc desc = theDepot.Get(id);
- *size = desc.size;
- return desc.stack;
+StackTrace StackDepotGet(u32 id) {
+ return theDepot.Get(id);
+}
+
+void StackDepotLockAll() {
+ theDepot.LockAll();
+}
+
+void StackDepotUnlockAll() {
+ theDepot.UnlockAll();
}
bool StackDepotReverseMap::IdDescPair::IdComparator(
@@ -148,18 +146,15 @@
InternalSort(&map_, map_.size(), IdDescPair::IdComparator);
}
-const uptr *StackDepotReverseMap::Get(u32 id, uptr *size) {
- if (!map_.size()) return 0;
+StackTrace StackDepotReverseMap::Get(u32 id) {
+ if (!map_.size())
+ return StackTrace();
IdDescPair pair = {id, 0};
uptr idx = InternalBinarySearch(map_, 0, map_.size(), pair,
IdDescPair::IdComparator);
- if (idx > map_.size()) {
- *size = 0;
- return 0;
- }
- StackDepotNode *desc = map_[idx].desc;
- *size = desc->size;
- return desc->stack;
+ if (idx > map_.size())
+ return StackTrace();
+ return map_[idx].desc->load();
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_stackdepot.h b/lib/sanitizer_common/sanitizer_stackdepot.h
index 58cc91f..5e3a8b7 100644
--- a/lib/sanitizer_common/sanitizer_stackdepot.h
+++ b/lib/sanitizer_common/sanitizer_stackdepot.h
@@ -15,6 +15,7 @@
#include "sanitizer_common.h"
#include "sanitizer_internal_defs.h"
+#include "sanitizer_stacktrace.h"
namespace __sanitizer {
@@ -28,17 +29,18 @@
u32 id();
int use_count();
void inc_use_count_unsafe();
- uptr size();
- uptr *stack();
};
const int kStackDepotMaxUseCount = 1U << 20;
StackDepotStats *StackDepotGetStats();
-u32 StackDepotPut(const uptr *stack, uptr size);
-StackDepotHandle StackDepotPut_WithHandle(const uptr *stack, uptr size);
+u32 StackDepotPut(StackTrace stack);
+StackDepotHandle StackDepotPut_WithHandle(StackTrace stack);
// Retrieves a stored stack trace by the id.
-const uptr *StackDepotGet(u32 id, uptr *size);
+StackTrace StackDepotGet(u32 id);
+
+void StackDepotLockAll();
+void StackDepotUnlockAll();
// Instantiating this class creates a snapshot of StackDepot which can be
// efficiently queried with StackDepotGet(). You can use it concurrently with
@@ -47,7 +49,7 @@
class StackDepotReverseMap {
public:
StackDepotReverseMap();
- const uptr *Get(u32 id, uptr *size);
+ StackTrace Get(u32 id);
private:
struct IdDescPair {
diff --git a/lib/sanitizer_common/sanitizer_stackdepotbase.h b/lib/sanitizer_common/sanitizer_stackdepotbase.h
index b4fa875..5de2e71 100644
--- a/lib/sanitizer_common/sanitizer_stackdepotbase.h
+++ b/lib/sanitizer_common/sanitizer_stackdepotbase.h
@@ -32,6 +32,9 @@
StackDepotStats *GetStats() { return &stats; }
+ void LockAll();
+ void UnlockAll();
+
private:
static Node *find(Node *s, args_type args, u32 hash);
static Node *lock(atomic_uintptr_t *p);
@@ -94,8 +97,8 @@
StackDepotBase<Node, kReservedBits, kTabSizeLog>::Put(args_type args,
bool *inserted) {
if (inserted) *inserted = false;
- if (!args.is_valid()) return handle_type();
- uptr h = args.hash();
+ if (!Node::is_valid(args)) return handle_type();
+ uptr h = Node::hash(args);
atomic_uintptr_t *p = &tab[h % kTabSize];
uptr v = atomic_load(p, memory_order_consume);
Node *s = (Node *)(v & ~1);
@@ -153,5 +156,21 @@
return args_type();
}
+template <class Node, int kReservedBits, int kTabSizeLog>
+void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockAll() {
+ for (int i = 0; i < kTabSize; ++i) {
+ lock(&tab[i]);
+ }
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAll() {
+ for (int i = 0; i < kTabSize; ++i) {
+ atomic_uintptr_t *p = &tab[i];
+ uptr s = atomic_load(p, memory_order_relaxed);
+ unlock(p, (Node *)(s & ~1UL));
+ }
+}
+
} // namespace __sanitizer
#endif // SANITIZER_STACKDEPOTBASE_H
diff --git a/lib/sanitizer_common/sanitizer_stacktrace.cc b/lib/sanitizer_common/sanitizer_stacktrace.cc
index 3539639..cf061fb 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace.cc
+++ b/lib/sanitizer_common/sanitizer_stacktrace.cc
@@ -25,38 +25,83 @@
#if defined(__powerpc__) || defined(__powerpc64__)
// PCs are always 4 byte aligned.
return pc - 4;
-#elif defined(__sparc__)
+#elif defined(__sparc__) || defined(__mips__)
return pc - 8;
#else
return pc - 1;
#endif
}
+uptr StackTrace::GetNextInstructionPc(uptr pc) {
+#if defined(__mips__)
+ return pc + 8;
+#else
+ return pc + 1;
+#endif
+}
+
uptr StackTrace::GetCurrentPc() {
return GET_CALLER_PC();
}
-void StackTrace::FastUnwindStack(uptr pc, uptr bp,
- uptr stack_top, uptr stack_bottom,
- uptr max_depth) {
+void BufferedStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) {
+ size = cnt + !!extra_top_pc;
+ CHECK_LE(size, kStackTraceMax);
+ internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0]));
+ if (extra_top_pc)
+ trace_buffer[cnt] = extra_top_pc;
+ top_frame_bp = 0;
+}
+
+// Check if given pointer points into allocated stack area.
+static inline bool IsValidFrame(uptr frame, uptr stack_top, uptr stack_bottom) {
+ return frame > stack_bottom && frame < stack_top - 2 * sizeof (uhwptr);
+}
+
+// In GCC on ARM bp points to saved lr, not fp, so we should check the next
+// cell in stack to be a saved frame pointer. GetCanonicFrame returns the
+// pointer to saved frame pointer in any case.
+static inline uhwptr *GetCanonicFrame(uptr bp,
+ uptr stack_top,
+ uptr stack_bottom) {
+#ifdef __arm__
+ if (!IsValidFrame(bp, stack_top, stack_bottom)) return 0;
+ uhwptr *bp_prev = (uhwptr *)bp;
+ if (IsValidFrame((uptr)bp_prev[0], stack_top, stack_bottom)) return bp_prev;
+ // The next frame pointer does not look right. This could be a GCC frame, step
+ // back by 1 word and try again.
+ if (IsValidFrame((uptr)bp_prev[-1], stack_top, stack_bottom))
+ return bp_prev - 1;
+ // Nope, this does not look right either. This means the frame after next does
+ // not have a valid frame pointer, but we can still extract the caller PC.
+ // Unfortunately, there is no way to decide between GCC and LLVM frame
+ // layouts. Assume LLVM.
+ return bp_prev;
+#else
+ return (uhwptr*)bp;
+#endif
+}
+
+void BufferedStackTrace::FastUnwindStack(uptr pc, uptr bp, uptr stack_top,
+ uptr stack_bottom, uptr max_depth) {
CHECK_GE(max_depth, 2);
- trace[0] = pc;
+ trace_buffer[0] = pc;
size = 1;
- uhwptr *frame = (uhwptr *)bp;
- uhwptr *prev_frame = frame - 1;
if (stack_top < 4096) return; // Sanity check for stack top.
+ uhwptr *frame = GetCanonicFrame(bp, stack_top, stack_bottom);
+ // Lowest possible address that makes sense as the next frame pointer.
+ // Goes up as we walk the stack.
+ uptr bottom = stack_bottom;
// 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 &&
+ while (IsValidFrame((uptr)frame, stack_top, bottom) &&
IsAligned((uptr)frame, sizeof(*frame)) &&
size < max_depth) {
uhwptr pc1 = frame[1];
if (pc1 != pc) {
- trace[size++] = (uptr) pc1;
+ trace_buffer[size++] = (uptr) pc1;
}
- prev_frame = frame;
- frame = (uhwptr *)frame[0];
+ bottom = (uptr)frame;
+ frame = GetCanonicFrame((uptr)frame[0], stack_top, bottom);
}
}
@@ -64,15 +109,15 @@
return cur_pc - trace_pc <= threshold || trace_pc - cur_pc <= threshold;
}
-void StackTrace::PopStackFrames(uptr count) {
+void BufferedStackTrace::PopStackFrames(uptr count) {
CHECK_LT(count, size);
size -= count;
for (uptr i = 0; i < size; ++i) {
- trace[i] = trace[i + count];
+ trace_buffer[i] = trace_buffer[i + count];
}
}
-uptr StackTrace::LocatePcInTrace(uptr pc) {
+uptr BufferedStackTrace::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 = 288;
diff --git a/lib/sanitizer_common/sanitizer_stacktrace.h b/lib/sanitizer_common/sanitizer_stacktrace.h
index fcaa777..e755c05 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace.h
+++ b/lib/sanitizer_common/sanitizer_stacktrace.h
@@ -29,44 +29,50 @@
# define SANITIZER_CAN_FAST_UNWIND 1
#endif
+// Fast unwind is the only option on Mac for now; we will need to
+// revisit this macro when slow unwind works on Mac, see
+// https://code.google.com/p/address-sanitizer/issues/detail?id=137
+#if SANITIZER_MAC
+# define SANITIZER_CAN_SLOW_UNWIND 0
+#else
+# define SANITIZER_CAN_SLOW_UNWIND 1
+#endif
+
struct StackTrace {
- typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer,
- int out_size);
- uptr top_frame_bp;
+ const uptr *trace;
uptr size;
- uptr trace[kStackTraceMax];
+
+ StackTrace() : trace(nullptr), size(0) {}
+ StackTrace(const uptr *trace, uptr size) : trace(trace), size(size) {}
// Prints a symbolized stacktrace, followed by an empty line.
- static void PrintStack(const uptr *addr, uptr size);
- void Print() const {
- PrintStack(trace, 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++)
- trace[i] = src[i];
- }
+ void Print() const;
static bool WillUseFastUnwind(bool request_fast_unwind) {
- // Check if fast unwind is available. Fast unwind is the only option on Mac.
- // It is also the only option on FreeBSD as the slow unwinding that
- // leverages _Unwind_Backtrace() yields the call stack of the signal's
- // handler and not of the code that raised the signal (as it does on Linux).
if (!SANITIZER_CAN_FAST_UNWIND)
return false;
- else if (SANITIZER_MAC != 0 || SANITIZER_FREEBSD != 0)
+ else if (!SANITIZER_CAN_SLOW_UNWIND)
return true;
return request_fast_unwind;
}
- void Unwind(uptr max_depth, uptr pc, uptr bp, void *context, uptr stack_top,
- uptr stack_bottom, bool request_fast_unwind);
-
static uptr GetCurrentPc();
static uptr GetPreviousInstructionPc(uptr pc);
+ static uptr GetNextInstructionPc(uptr pc);
+ typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer,
+ int out_size);
+};
+
+// StackTrace that owns the buffer used to store the addresses.
+struct BufferedStackTrace : public StackTrace {
+ uptr trace_buffer[kStackTraceMax];
+ uptr top_frame_bp; // Optional bp of a top frame.
+
+ BufferedStackTrace() : StackTrace(trace_buffer, 0), top_frame_bp(0) {}
+
+ void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0);
+ void Unwind(uptr max_depth, uptr pc, uptr bp, void *context, uptr stack_top,
+ uptr stack_bottom, bool request_fast_unwind);
private:
void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom,
@@ -76,6 +82,9 @@
uptr max_depth);
void PopStackFrames(uptr count);
uptr LocatePcInTrace(uptr pc);
+
+ BufferedStackTrace(const BufferedStackTrace &);
+ void operator=(const BufferedStackTrace &);
};
} // namespace __sanitizer
@@ -88,6 +97,10 @@
uptr local_stack; \
uptr sp = (uptr)&local_stack
+#define GET_CALLER_PC_BP \
+ uptr bp = GET_CURRENT_FRAME(); \
+ uptr pc = GET_CALLER_PC();
+
// Use this macro if you want to print stack trace with the current
// function in the top frame.
#define GET_CURRENT_PC_BP_SP \
diff --git a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
index c68149d..13fb01f 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
@@ -12,58 +12,40 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_common.h"
+#include "sanitizer_placement_new.h"
#include "sanitizer_stacktrace.h"
+#include "sanitizer_stacktrace_printer.h"
#include "sanitizer_symbolizer.h"
namespace __sanitizer {
-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) {
- if (addr == 0 || size == 0) {
+void StackTrace::Print() const {
+ if (trace == nullptr || size == 0) {
Printf(" <empty stack>\n\n");
return;
}
- InternalScopedBuffer<char> buff(GetPageSizeCached() * 2);
- InternalScopedBuffer<AddressInfo> addr_frames(64);
+ const int kMaxAddrFrames = 64;
+ InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
+ for (uptr i = 0; i < kMaxAddrFrames; i++)
+ new(&addr_frames[i]) AddressInfo();
InternalScopedString frame_desc(GetPageSizeCached() * 2);
uptr frame_num = 0;
- for (uptr i = 0; i < size && addr[i]; i++) {
+ for (uptr i = 0; i < size && trace[i]; i++) {
// PCs in stack traces are actually the return addresses, that is,
// addresses of the next instructions after the call.
- uptr pc = GetPreviousInstructionPc(addr[i]);
+ uptr pc = GetPreviousInstructionPc(trace[i]);
uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(
- pc, addr_frames.data(), addr_frames.size());
+ pc, addr_frames.data(), kMaxAddrFrames);
if (addr_frames_num == 0) {
- frame_desc.clear();
- PrintStackFramePrefix(&frame_desc, frame_num, pc);
- frame_desc.append(" (<unknown module>)");
- Printf("%s\n", frame_desc.data());
- frame_num++;
- continue;
+ addr_frames[0].address = pc;
+ addr_frames_num = 1;
}
for (uptr j = 0; j < addr_frames_num; j++) {
AddressInfo &info = addr_frames[j];
frame_desc.clear();
- PrintStackFramePrefix(&frame_desc, frame_num, pc);
- if (info.function) {
- frame_desc.append(" in %s", info.function);
- // Print offset in function if we don't know the source file.
- if (!info.file && info.function_offset != AddressInfo::kUnknown)
- frame_desc.append("+0x%zx", info.function_offset);
- }
- if (info.file) {
- frame_desc.append(" ");
- PrintSourceLocation(&frame_desc, info.file, info.line, info.column);
- } else if (info.module) {
- frame_desc.append(" ");
- PrintModuleAndOffset(&frame_desc, info.module, info.module_offset);
- }
+ RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++,
+ info, common_flags()->strip_path_prefix);
Printf("%s\n", frame_desc.data());
- frame_num++;
info.Clear();
}
}
@@ -71,9 +53,9 @@
Printf("\n");
}
-void StackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, void *context,
- uptr stack_top, uptr stack_bottom,
- bool request_fast_unwind) {
+void BufferedStackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, void *context,
+ uptr stack_top, uptr stack_bottom,
+ bool request_fast_unwind) {
top_frame_bp = (max_depth > 0) ? bp : 0;
// Avoid doing any work for small max_depth.
if (max_depth == 0) {
@@ -82,7 +64,7 @@
}
if (max_depth == 1) {
size = 1;
- trace[0] = pc;
+ trace_buffer[0] = pc;
return;
}
if (!WillUseFastUnwind(request_fast_unwind)) {
diff --git a/lib/sanitizer_common/sanitizer_stacktrace_printer.cc b/lib/sanitizer_common/sanitizer_stacktrace_printer.cc
new file mode 100644
index 0000000..7b37dbc
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_stacktrace_printer.cc
@@ -0,0 +1,132 @@
+//===-- sanitizer_common.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 sanitizers' run-time libraries.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_stacktrace_printer.h"
+
+namespace __sanitizer {
+
+static const char *StripFunctionName(const char *function, const char *prefix) {
+ if (function == 0) return 0;
+ if (prefix == 0) return function;
+ uptr prefix_len = internal_strlen(prefix);
+ if (0 == internal_strncmp(function, prefix, prefix_len))
+ return function + prefix_len;
+ return function;
+}
+
+static const char kDefaultFormat[] = " #%n %p %F %L";
+
+void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
+ const AddressInfo &info, const char *strip_path_prefix,
+ const char *strip_func_prefix) {
+ if (0 == internal_strcmp(format, "DEFAULT"))
+ format = kDefaultFormat;
+ for (const char *p = format; *p != '\0'; p++) {
+ if (*p != '%') {
+ buffer->append("%c", *p);
+ continue;
+ }
+ p++;
+ switch (*p) {
+ case '%':
+ buffer->append("%%");
+ break;
+ // Frame number and all fields of AddressInfo structure.
+ case 'n':
+ buffer->append("%zu", frame_no);
+ break;
+ case 'p':
+ buffer->append("0x%zx", info.address);
+ break;
+ case 'm':
+ buffer->append("%s", StripPathPrefix(info.module, strip_path_prefix));
+ break;
+ case 'o':
+ buffer->append("0x%zx", info.module_offset);
+ break;
+ case 'f':
+ buffer->append("%s", StripFunctionName(info.function, strip_func_prefix));
+ break;
+ case 'q':
+ buffer->append("0x%zx", info.function_offset != AddressInfo::kUnknown
+ ? info.function_offset
+ : 0x0);
+ break;
+ case 's':
+ buffer->append("%s", StripPathPrefix(info.file, strip_path_prefix));
+ break;
+ case 'l':
+ buffer->append("%d", info.line);
+ break;
+ case 'c':
+ buffer->append("%d", info.column);
+ break;
+ // Smarter special cases.
+ case 'F':
+ // Function name and offset, if file is unknown.
+ if (info.function) {
+ buffer->append("in %s",
+ StripFunctionName(info.function, strip_func_prefix));
+ if (!info.file && info.function_offset != AddressInfo::kUnknown)
+ buffer->append("+0x%zx", info.function_offset);
+ }
+ break;
+ case 'S':
+ // File/line information.
+ RenderSourceLocation(buffer, info.file, info.line, info.column,
+ strip_path_prefix);
+ break;
+ case 'L':
+ // Source location, or module location.
+ if (info.file) {
+ RenderSourceLocation(buffer, info.file, info.line, info.column,
+ strip_path_prefix);
+ } else if (info.module) {
+ RenderModuleLocation(buffer, info.module, info.module_offset,
+ strip_path_prefix);
+ } else {
+ buffer->append("(<unknown module>)");
+ }
+ break;
+ case 'M':
+ // Module basename and offset, or PC.
+ if (info.module)
+ buffer->append("(%s+%p)", StripModuleName(info.module),
+ (void *)info.module_offset);
+ else
+ buffer->append("(%p)", (void *)info.address);
+ break;
+ default:
+ Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n",
+ *p, *p);
+ Die();
+ }
+ }
+}
+
+void RenderSourceLocation(InternalScopedString *buffer, const char *file,
+ int line, int column, const char *strip_path_prefix) {
+ buffer->append("%s", StripPathPrefix(file, strip_path_prefix));
+ if (line > 0) {
+ buffer->append(":%d", line);
+ if (column > 0)
+ buffer->append(":%d", column);
+ }
+}
+
+void RenderModuleLocation(InternalScopedString *buffer, const char *module,
+ uptr offset, const char *strip_path_prefix) {
+ buffer->append("(%s+0x%zx)", StripPathPrefix(module, strip_path_prefix),
+ offset);
+}
+
+} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_stacktrace_printer.h b/lib/sanitizer_common/sanitizer_stacktrace_printer.h
new file mode 100644
index 0000000..9356988
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_stacktrace_printer.h
@@ -0,0 +1,62 @@
+//===-- sanitizer_stacktrace_printer.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 shared between sanitizers' run-time libraries.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_STACKTRACE_PRINTER_H
+#define SANITIZER_STACKTRACE_PRINTER_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+// Render the contents of "info" structure, which represents the contents of
+// stack frame "frame_no" and appends it to the "buffer". "format" is a
+// string with placeholders, which is copied to the output with
+// placeholders substituted with the contents of "info". For example,
+// format string
+// " frame %n: function %F at %S"
+// will be turned into
+// " frame 10: function foo::bar() at my/file.cc:10"
+// You may additionally pass "strip_path_prefix" to strip prefixes of paths to
+// source files and modules, and "strip_func_prefix" to strip prefixes of
+// function names.
+// Here's the full list of available placeholders:
+// %% - represents a '%' character;
+// %n - frame number (copy of frame_no);
+// %p - PC in hex format;
+// %m - path to module (binary or shared object);
+// %o - offset in the module in hex format;
+// %f - function name;
+// %q - offset in the function in hex format (*if available*);
+// %s - path to source file;
+// %l - line in the source file;
+// %c - column in the source file;
+// %F - if function is known to be <foo>, prints "in <foo>", possibly
+// followed by the offset in this function, but only if source file
+// is unknown;
+// %S - prints file/line/column information;
+// %L - prints location information: file/line/column, if it is known, or
+// module+offset if it is known, or (<unknown module>) string.
+// %M - prints module basename and offset, if it is known, or PC.
+void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
+ const AddressInfo &info, const char *strip_path_prefix = "",
+ const char *strip_func_prefix = "");
+
+void RenderSourceLocation(InternalScopedString *buffer, const char *file,
+ int line, int column, const char *strip_path_prefix);
+
+void RenderModuleLocation(InternalScopedString *buffer, const char *module,
+ uptr offset, const char *strip_path_prefix);
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_STACKTRACE_PRINTER_H
diff --git a/lib/sanitizer_common/sanitizer_suppressions.cc b/lib/sanitizer_common/sanitizer_suppressions.cc
index 87ccf7f..7f76693 100644
--- a/lib/sanitizer_common/sanitizer_suppressions.cc
+++ b/lib/sanitizer_common/sanitizer_suppressions.cc
@@ -15,13 +15,15 @@
#include "sanitizer_allocator_internal.h"
#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
namespace __sanitizer {
static const char *const kTypeStrings[SuppressionTypeCount] = {
- "none", "race", "mutex", "thread",
- "signal", "leak", "called_from_lib", "deadlock"};
+ "none", "race", "mutex", "thread", "signal",
+ "leak", "called_from_lib", "deadlock", "vptr_check"};
bool TemplateMatch(char *templ, const char *str) {
if (str == 0 || str[0] == 0)
@@ -65,6 +67,33 @@
return true;
}
+ALIGNED(64) static char placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = 0;
+
+SuppressionContext *SuppressionContext::Get() {
+ CHECK(suppression_ctx);
+ return suppression_ctx;
+}
+
+void SuppressionContext::InitIfNecessary() {
+ if (suppression_ctx)
+ return;
+ suppression_ctx = new(placeholder) SuppressionContext;
+ if (common_flags()->suppressions[0] == '\0')
+ return;
+ char *suppressions_from_file;
+ uptr buffer_size;
+ uptr contents_size =
+ ReadFileToBuffer(common_flags()->suppressions, &suppressions_from_file,
+ &buffer_size, 1 << 26 /* max_len */);
+ if (contents_size == 0) {
+ Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
+ common_flags()->suppressions);
+ Die();
+ }
+ suppression_ctx->Parse(suppressions_from_file);
+}
+
bool SuppressionContext::Match(const char *str, SuppressionType type,
Suppression **s) {
can_parse_ = false;
diff --git a/lib/sanitizer_common/sanitizer_suppressions.h b/lib/sanitizer_common/sanitizer_suppressions.h
index 772b9aa..37fd3c4 100644
--- a/lib/sanitizer_common/sanitizer_suppressions.h
+++ b/lib/sanitizer_common/sanitizer_suppressions.h
@@ -27,6 +27,7 @@
SuppressionLeak,
SuppressionLib,
SuppressionDeadlock,
+ SuppressionVptrCheck,
SuppressionTypeCount
};
@@ -39,14 +40,21 @@
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);
+ // Create a SuppressionContext singleton if it hasn't been created earlier.
+ // Not thread safe. Must be called early during initialization (but after
+ // runtime flags are parsed).
+ static void InitIfNecessary();
+ // Returns a SuppressionContext singleton.
+ static SuppressionContext *Get();
+
private:
+ SuppressionContext() : suppressions_(1), can_parse_(true) {}
InternalMmapVector<Suppression> suppressions_;
bool can_parse_;
diff --git a/lib/sanitizer_common/sanitizer_symbolizer.cc b/lib/sanitizer_common/sanitizer_symbolizer.cc
index 2290767..8aa9de0 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer.cc
@@ -22,17 +22,6 @@
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.
diff --git a/lib/sanitizer_common/sanitizer_symbolizer.h b/lib/sanitizer_common/sanitizer_symbolizer.h
index 7057a89..82093e4 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer.h
+++ b/lib/sanitizer_common/sanitizer_symbolizer.h
@@ -61,31 +61,30 @@
}
};
+// For now, DataInfo is used to describe global variable.
struct DataInfo {
- uptr address;
char *module;
uptr module_offset;
char *name;
uptr start;
uptr size;
+
+ DataInfo() {
+ internal_memset(this, 0, sizeof(DataInfo));
+ }
+
+ void Clear() {
+ InternalFree(module);
+ InternalFree(name);
+ internal_memset(this, 0, sizeof(DataInfo));
+ }
};
class Symbolizer {
public:
- /// 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.
+ /// Initialize and return platform-specific implementation of symbolizer
+ /// (if it wasn't already initialized).
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);
// 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.
@@ -122,10 +121,7 @@
private:
/// 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 *PlatformInit();
/// Initialize the symbolizer in a disabled state. Not thread safe.
static Symbolizer *Disable();
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc
index b431e51..63c9356 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc
@@ -16,24 +16,13 @@
namespace __sanitizer {
-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;
-}
-
-Symbolizer *Symbolizer::Init(const char *path_to_external) {
- CHECK_EQ(0, symbolizer_);
- return CreateAndStore(path_to_external);
-}
-
Symbolizer *Symbolizer::GetOrInit() {
SpinMutexLock l(&init_mu_);
- if (symbolizer_ == 0)
- return CreateAndStore(0);
- return symbolizer_;
+ if (symbolizer_)
+ return symbolizer_;
+ if ((symbolizer_ = PlatformInit()))
+ return symbolizer_;
+ return Disable();
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
index ddb9e17..eb2b707 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
@@ -341,12 +341,19 @@
const char* const kSymbolizerArch = "--default-arch=x86_64";
#elif defined(__i386__)
const char* const kSymbolizerArch = "--default-arch=i386";
-#elif defined(__powerpc64__)
+#elif defined(__powerpc64__) && defined(__BIG_ENDIAN__)
const char* const kSymbolizerArch = "--default-arch=powerpc64";
+#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__)
+ const char* const kSymbolizerArch = "--default-arch=powerpc64le";
#else
const char* const kSymbolizerArch = "--default-arch=unknown";
#endif
- execl(path_to_binary, path_to_binary, kSymbolizerArch, (char *)0);
+
+ const char *const inline_flag = common_flags()->symbolize_inline_frames
+ ? "--inlining=true"
+ : "--inlining=false";
+ execl(path_to_binary, path_to_binary, inline_flag, kSymbolizerArch,
+ (char *)0);
}
};
@@ -582,8 +589,7 @@
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->Clear();
info->module = internal_strdup(module_name);
info->module_offset = module_offset;
// First, try to use libbacktrace symbolizer (if it's available).
@@ -716,7 +722,7 @@
LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked.
};
-Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) {
+Symbolizer *Symbolizer::PlatformInit() {
if (!common_flags()->symbolize) {
return new(symbolizer_allocator_) POSIXSymbolizer(0, 0, 0);
}
@@ -729,6 +735,7 @@
libbacktrace_symbolizer =
LibbacktraceSymbolizer::get(&symbolizer_allocator_);
if (!libbacktrace_symbolizer) {
+ const char *path_to_external = common_flags()->external_symbolizer_path;
if (path_to_external && path_to_external[0] == '\0') {
// External symbolizer is explicitly disabled. Do nothing.
} else {
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_win.cc b/lib/sanitizer_common/sanitizer_symbolizer_win.cc
index dc4816b..e8cf0a8 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_win.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_win.cc
@@ -32,11 +32,22 @@
BlockingMutexLock l(&dbghelp_mu_);
if (!initialized_) {
- SymSetOptions(SYMOPT_DEFERRED_LOADS |
- SYMOPT_UNDNAME |
- SYMOPT_LOAD_LINES);
- CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE));
- // FIXME: We don't call SymCleanup() on exit yet - should we?
+ if (!TrySymInitialize()) {
+ // OK, maybe the client app has called SymInitialize already.
+ // That's a bit unfortunate for us as all the DbgHelp functions are
+ // single-threaded and we can't coordinate with the app.
+ // FIXME: Can we stop the other threads at this point?
+ // Anyways, we have to reconfigure stuff to make sure that SymInitialize
+ // has all the appropriate options set.
+ // Cross our fingers and reinitialize DbgHelp.
+ Report("*** WARNING: Failed to initialize DbgHelp! ***\n");
+ Report("*** Most likely this means that the app is already ***\n");
+ Report("*** using DbgHelp, possibly with incompatible flags. ***\n");
+ Report("*** Due to technical reasons, symbolization might crash ***\n");
+ Report("*** or produce wrong results. ***\n");
+ SymCleanup(GetCurrentProcess());
+ TrySymInitialize();
+ }
initialized_ = true;
}
@@ -92,13 +103,19 @@
// FIXME: Implement GetModuleNameAndOffsetForPC().
private:
+ bool TrySymInitialize() {
+ SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
+ return SymInitialize(GetCurrentProcess(), 0, TRUE);
+ // FIXME: We don't call SymCleanup() on exit yet - should we?
+ }
+
// All DbgHelp functions are single threaded, so we should use a mutex to
// serialize accesses.
BlockingMutex dbghelp_mu_;
bool initialized_;
};
-Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) {
+Symbolizer *Symbolizer::PlatformInit() {
static bool called_once = false;
CHECK(!called_once && "Shouldn't create more than one symbolizer");
called_once = true;
diff --git a/lib/sanitizer_common/sanitizer_thread_registry.cc b/lib/sanitizer_common/sanitizer_thread_registry.cc
index ed2c601..2ec92ff 100644
--- a/lib/sanitizer_common/sanitizer_thread_registry.cc
+++ b/lib/sanitizer_common/sanitizer_thread_registry.cc
@@ -218,7 +218,7 @@
}
}
-void ThreadRegistry::DetachThread(u32 tid) {
+void ThreadRegistry::DetachThread(u32 tid, void *arg) {
BlockingMutexLock l(&mtx_);
CHECK_LT(tid, n_contexts_);
ThreadContextBase *tctx = threads_[tid];
@@ -227,6 +227,7 @@
Report("%s: Detach of non-existent thread\n", SanitizerToolName);
return;
}
+ tctx->OnDetached(arg);
if (tctx->status == ThreadStatusFinished) {
tctx->SetDead();
QuarantinePush(tctx);
diff --git a/lib/sanitizer_common/sanitizer_thread_registry.h b/lib/sanitizer_common/sanitizer_thread_registry.h
index 8bb7ff3..5d9c3b9 100644
--- a/lib/sanitizer_common/sanitizer_thread_registry.h
+++ b/lib/sanitizer_common/sanitizer_thread_registry.h
@@ -68,6 +68,7 @@
virtual void OnStarted(void *arg) {}
virtual void OnCreated(void *arg) {}
virtual void OnReset() {}
+ virtual void OnDetached(void *arg) {}
};
typedef ThreadContextBase* (*ThreadContextFactory)(u32 tid);
@@ -110,7 +111,7 @@
void SetThreadName(u32 tid, const char *name);
void SetThreadNameByUserId(uptr user_id, const char *name);
- void DetachThread(u32 tid);
+ void DetachThread(u32 tid, void *arg);
void JoinThread(u32 tid, void *arg);
void FinishThread(u32 tid);
void StartThread(u32 tid, uptr os_id, void *arg);
diff --git a/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc
new file mode 100644
index 0000000..a98e617
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc
@@ -0,0 +1,158 @@
+//===-- sanitizer_unwind_posix.cc ----------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the unwind.h-based (aka "slow") stack unwinding routines
+// available to the tools on Linux, Android, FreeBSD and OS X.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_POSIX
+#include "sanitizer_common.h"
+#include "sanitizer_stacktrace.h"
+
+#if SANITIZER_ANDROID
+#include <dlfcn.h> // for dlopen()
+#endif
+
+#if SANITIZER_FREEBSD
+#define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
+#endif
+#include <unwind.h>
+
+namespace __sanitizer {
+
+//------------------------- SlowUnwindStack -----------------------------------
+
+typedef struct {
+ uptr absolute_pc;
+ uptr stack_top;
+ uptr stack_size;
+} backtrace_frame_t;
+
+extern "C" {
+typedef void *(*acquire_my_map_info_list_func)();
+typedef void (*release_my_map_info_list_func)(void *map);
+typedef sptr (*unwind_backtrace_signal_arch_func)(
+ void *siginfo, void *sigcontext, void *map_info_list,
+ backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
+acquire_my_map_info_list_func acquire_my_map_info_list;
+release_my_map_info_list_func release_my_map_info_list;
+unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
+} // extern "C"
+
+#if SANITIZER_ANDROID
+void SanitizerInitializeUnwinder() {
+ void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
+ if (!p) {
+ VReport(1,
+ "Failed to open libcorkscrew.so. You may see broken stack traces "
+ "in SEGV reports.");
+ return;
+ }
+ acquire_my_map_info_list =
+ (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
+ release_my_map_info_list =
+ (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
+ unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
+ p, "unwind_backtrace_signal_arch");
+ if (!acquire_my_map_info_list || !release_my_map_info_list ||
+ !unwind_backtrace_signal_arch) {
+ VReport(1,
+ "Failed to find one of the required symbols in libcorkscrew.so. "
+ "You may see broken stack traces in SEGV reports.");
+ acquire_my_map_info_list = 0;
+ unwind_backtrace_signal_arch = 0;
+ release_my_map_info_list = 0;
+ }
+}
+#endif
+
+#ifdef __arm__
+#define UNWIND_STOP _URC_END_OF_STACK
+#define UNWIND_CONTINUE _URC_NO_REASON
+#else
+#define UNWIND_STOP _URC_NORMAL_STOP
+#define UNWIND_CONTINUE _URC_NO_REASON
+#endif
+
+uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
+#ifdef __arm__
+ uptr val;
+ _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
+ 15 /* r15 = PC */, _UVRSD_UINT32, &val);
+ CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
+ // Clear the Thumb bit.
+ return val & ~(uptr)1;
+#else
+ return _Unwind_GetIP(ctx);
+#endif
+}
+
+struct UnwindTraceArg {
+ BufferedStackTrace *stack;
+ uptr max_depth;
+};
+
+_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
+ UnwindTraceArg *arg = (UnwindTraceArg*)param;
+ CHECK_LT(arg->stack->size, arg->max_depth);
+ uptr pc = Unwind_GetIP(ctx);
+ arg->stack->trace_buffer[arg->stack->size++] = pc;
+ if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
+ return UNWIND_CONTINUE;
+}
+
+void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
+ CHECK_GE(max_depth, 2);
+ size = 0;
+ 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_buffer[0] belongs to the current function so we always pop it,
+ // unless there is only 1 frame in the stack trace (1 frame is always better
+ // than 0!).
+ // 1-frame stacks don't normally happen, but this depends on the actual
+ // unwinder implementation (libgcc, libunwind, etc) which is outside of our
+ // control.
+ if (to_pop == 0 && size > 1)
+ to_pop = 1;
+ PopStackFrames(to_pop);
+ trace_buffer[0] = pc;
+}
+
+void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
+ uptr max_depth) {
+ CHECK_GE(max_depth, 2);
+ if (!unwind_backtrace_signal_arch) {
+ SlowUnwindStack(pc, max_depth);
+ return;
+ }
+
+ void *map = acquire_my_map_info_list();
+ CHECK(map);
+ InternalScopedBuffer<backtrace_frame_t> frames(kStackTraceMax);
+ // siginfo argument appears to be unused.
+ sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
+ frames.data(),
+ /* ignore_depth */ 0, max_depth);
+ release_my_map_info_list(map);
+ if (res < 0) return;
+ CHECK_LE((uptr)res, kStackTraceMax);
+
+ size = 0;
+ // +2 compensate for libcorkscrew unwinder returning addresses of call
+ // instructions instead of raw return addresses.
+ for (sptr i = 0; i < res; ++i)
+ trace_buffer[size++] = frames[i].absolute_pc + 2;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_POSIX
diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc
index da24df6..2f9b158 100644
--- a/lib/sanitizer_common/sanitizer_win.cc
+++ b/lib/sanitizer_common/sanitizer_win.cc
@@ -17,9 +17,10 @@
#define WIN32_LEAN_AND_MEAN
#define NOGDI
-#include <stdlib.h>
-#include <io.h>
#include <windows.h>
+#include <dbghelp.h>
+#include <io.h>
+#include <stdlib.h>
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
@@ -64,6 +65,7 @@
return GetTid();
}
+#if !SANITIZER_GO
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
uptr *stack_bottom) {
CHECK(stack_top);
@@ -76,6 +78,7 @@
*stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize;
*stack_bottom = (uptr)mbi.AllocationBase;
}
+#endif // #if !SANITIZER_GO
void *MmapOrDie(uptr size, const char *mem_type) {
void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
@@ -188,7 +191,7 @@
UNIMPLEMENTED();
}
-void DisableCoreDumper() {
+void DisableCoreDumperIfNecessary() {
// Do nothing.
}
@@ -209,6 +212,14 @@
UNIMPLEMENTED();
}
+bool AddressSpaceIsUnlimited() {
+ UNIMPLEMENTED();
+}
+
+void SetAddressSpaceUnlimited() {
+ UNIMPLEMENTED();
+}
+
char *FindPathToBinary(const char *name) {
// Nothing here for now.
return 0;
@@ -432,7 +443,8 @@
#endif
}
-void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
+#if !SANITIZER_GO
+void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
CHECK_GE(max_depth, 2);
// FIXME: CaptureStackBackTrace might be too slow for us.
// FIXME: Compare with StackWalk64.
@@ -447,10 +459,34 @@
PopStackFrames(pc_location);
}
-void StackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
- uptr max_depth) {
- UNREACHABLE("no signal context on windows");
+void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
+ uptr max_depth) {
+ CONTEXT ctx = *(CONTEXT *)context;
+ STACKFRAME64 stack_frame;
+ memset(&stack_frame, 0, sizeof(stack_frame));
+ size = 0;
+#if defined(_WIN64)
+ int machine_type = IMAGE_FILE_MACHINE_AMD64;
+ stack_frame.AddrPC.Offset = ctx.Rip;
+ stack_frame.AddrFrame.Offset = ctx.Rbp;
+ stack_frame.AddrStack.Offset = ctx.Rsp;
+#else
+ int machine_type = IMAGE_FILE_MACHINE_I386;
+ stack_frame.AddrPC.Offset = ctx.Eip;
+ stack_frame.AddrFrame.Offset = ctx.Ebp;
+ stack_frame.AddrStack.Offset = ctx.Esp;
+#endif
+ stack_frame.AddrPC.Mode = AddrModeFlat;
+ stack_frame.AddrFrame.Mode = AddrModeFlat;
+ stack_frame.AddrStack.Mode = AddrModeFlat;
+ while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(),
+ &stack_frame, &ctx, NULL, &SymFunctionTableAccess64,
+ &SymGetModuleBase64, NULL) &&
+ size < Min(max_depth, kStackTraceMax)) {
+ trace_buffer[size++] = (uptr)stack_frame.AddrPC.Offset;
+ }
}
+#endif // #if !SANITIZER_GO
void MaybeOpenReportFile() {
// Windows doesn't have native fork, and we don't support Cygwin or other
@@ -486,6 +522,11 @@
return false;
}
+bool IsAccessibleMemoryRange(uptr beg, uptr size) {
+ // FIXME: Actually implement this function.
+ return true;
+}
+
} // namespace __sanitizer
#endif // _WIN32
diff --git a/lib/sanitizer_common/scripts/check_lint.sh b/lib/sanitizer_common/scripts/check_lint.sh
index 33ab883..267273d 100755
--- a/lib/sanitizer_common/scripts/check_lint.sh
+++ b/lib/sanitizer_common/scripts/check_lint.sh
@@ -1,9 +1,9 @@
-#!/usr/bin/env bash
+#!/bin/sh
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Guess path to LLVM_CHECKOUT if not provided
-if [ "${LLVM_CHECKOUT}" == "" ]; then
+if [ "${LLVM_CHECKOUT}" = "" ]; then
LLVM_CHECKOUT="${SCRIPT_DIR}/../../../../../"
fi
@@ -29,7 +29,7 @@
MSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER}
LSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER}
LSAN_LIT_TEST_LINT_FILTER=${LSAN_RTL_LINT_FILTER},-whitespace/line_length
-DFSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int,-runtime/printf,-runtime/references
+DFSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int,-runtime/printf,-runtime/references,-readability/function
COMMON_RTL_INC_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int,-runtime/sizeof,-runtime/printf,-readability/fn_size
SANITIZER_INCLUDES_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int
MKTEMP="mktemp -q /tmp/tmp.XXXXXXXXXX"
@@ -47,7 +47,7 @@
cat $TASK_LOG | grep -v "Done processing" | grep -v "Total errors found" \
| grep -v "Skipping input" >> $ERROR_LOG
fi
- if [[ "${SILENT}" != "1" ]]; then
+ if [ "${SILENT}" != "1" ]; then
cat $TASK_LOG
fi
${LITLINT} "$@" 2>>$ERROR_LOG
@@ -56,7 +56,7 @@
run_lint ${LLVM_LINT_FILTER} --filter=${LLVM_LINT_FILTER} \
lib/Transforms/Instrumentation/*Sanitizer.cpp &
-if [ "${COMPILER_RT}" == "" ]; then
+if [ "${COMPILER_RT}" = "" ]; then
COMPILER_RT=projects/compiler-rt
fi
LIT_TESTS=${COMPILER_RT}/test
@@ -66,38 +66,47 @@
# Sanitizer_common
COMMON_RTL=${COMPILER_RT}/lib/sanitizer_common
-run_lint ${COMMON_RTL_INC_LINT_FILTER} ${COMMON_RTL}/*.{cc,h} \
+run_lint ${COMMON_RTL_INC_LINT_FILTER} ${COMMON_RTL}/*.cc \
+ ${COMMON_RTL}/*.h \
${COMMON_RTL}/tests/*.cc &
# Interception
INTERCEPTION=${COMPILER_RT}/lib/interception
-run_lint ${ASAN_RTL_LINT_FILTER} ${INTERCEPTION}/*.{cc,h} &
+run_lint ${ASAN_RTL_LINT_FILTER} ${INTERCEPTION}/*.cc \
+ ${INTERCEPTION}/*.h &
# ASan
ASAN_RTL=${COMPILER_RT}/lib/asan
-run_lint ${ASAN_RTL_LINT_FILTER} ${ASAN_RTL}/*.{cc,h} &
-run_lint ${ASAN_TEST_LINT_FILTER} ${ASAN_RTL}/tests/*.{cc,h} &
+run_lint ${ASAN_RTL_LINT_FILTER} ${ASAN_RTL}/*.cc \
+ ${ASAN_RTL}/*.h &
+run_lint ${ASAN_TEST_LINT_FILTER} ${ASAN_RTL}/tests/*.cc \
+ ${ASAN_RTL}/tests/*.h &
run_lint ${ASAN_LIT_TEST_LINT_FILTER} ${LIT_TESTS}/asan/*/*.cc &
# TSan
TSAN_RTL=${COMPILER_RT}/lib/tsan
-run_lint ${TSAN_RTL_LINT_FILTER} ${TSAN_RTL}/rtl/*.{cc,h} &
-run_lint ${TSAN_TEST_LINT_FILTER} ${TSAN_RTL}/tests/rtl/*.{cc,h} \
+run_lint ${TSAN_RTL_LINT_FILTER} ${TSAN_RTL}/rtl/*.cc \
+ ${TSAN_RTL}/rtl/*.h &
+run_lint ${TSAN_TEST_LINT_FILTER} ${TSAN_RTL}/tests/rtl/*.cc \
+ ${TSAN_RTL}/tests/rtl/*.h \
${TSAN_RTL}/tests/unit/*.cc &
run_lint ${TSAN_LIT_TEST_LINT_FILTER} ${LIT_TESTS}/tsan/*.cc &
# MSan
MSAN_RTL=${COMPILER_RT}/lib/msan
-run_lint ${MSAN_RTL_LINT_FILTER} ${MSAN_RTL}/*.{cc,h} &
+run_lint ${MSAN_RTL_LINT_FILTER} ${MSAN_RTL}/*.cc \
+ ${MSAN_RTL}/*.h &
# LSan
LSAN_RTL=${COMPILER_RT}/lib/lsan
-run_lint ${LSAN_RTL_LINT_FILTER} ${LSAN_RTL}/*.{cc,h}
+run_lint ${LSAN_RTL_LINT_FILTER} ${LSAN_RTL}/*.cc \
+ ${LSAN_RTL}/*.h &
run_lint ${LSAN_LIT_TEST_LINT_FILTER} ${LIT_TESTS}/lsan/*/*.cc &
# DFSan
DFSAN_RTL=${COMPILER_RT}/lib/dfsan
-run_lint ${DFSAN_RTL_LINT_FILTER} ${DFSAN_RTL}/*.{cc,h} &
+run_lint ${DFSAN_RTL_LINT_FILTER} ${DFSAN_RTL}/*.cc \
+ ${DFSAN_RTL}/*.h &
${DFSAN_RTL}/scripts/check_custom_wrappers.sh >> $ERROR_LOG
# Misc files
@@ -116,7 +125,7 @@
rm -f $temp
done
-if [[ -s $ERROR_LOG ]]; then
+if [ -s $ERROR_LOG ]; then
cat $ERROR_LOG
exit 1
fi
diff --git a/lib/sanitizer_common/scripts/gen_dynamic_list.py b/lib/sanitizer_common/scripts/gen_dynamic_list.py
index fdc442a..7bab230 100755
--- a/lib/sanitizer_common/scripts/gen_dynamic_list.py
+++ b/lib/sanitizer_common/scripts/gen_dynamic_list.py
@@ -38,9 +38,13 @@
nm_out = nm_proc.communicate()[0].decode().split('\n')
if nm_proc.returncode != 0:
raise subprocess.CalledProcessError(nm_proc.returncode, 'nm')
+ func_symbols = ['T', 'W']
+ # On PowerPC, nm prints function descriptors from .data section.
+ if os.uname()[4] in ["powerpc", "ppc64"]:
+ func_symbols += ['D']
for line in nm_out:
cols = line.split(' ')
- if (len(cols) == 3 and cols[1] in ('T', 'W')) :
+ if len(cols) == 3 and cols[1] in func_symbols :
functions.append(cols[2])
return functions
diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt
index cbc0c25..bb7a399 100644
--- a/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/lib/sanitizer_common/tests/CMakeLists.txt
@@ -1,5 +1,7 @@
include(CompilerRTCompile)
+clang_compiler_add_cxx_check()
+
set(SANITIZER_UNITTESTS
sanitizer_allocator_test.cc
sanitizer_atomic_test.cc
@@ -19,6 +21,7 @@
sanitizer_printf_test.cc
sanitizer_procmaps_test.cc
sanitizer_stackdepot_test.cc
+ sanitizer_stacktrace_printer_test.cc
sanitizer_stacktrace_test.cc
sanitizer_stoptheworld_test.cc
sanitizer_suppressions_test.cc
@@ -55,8 +58,13 @@
list(APPEND SANITIZER_TEST_LINK_FLAGS_COMMON --driver-mode=g++)
endif()
-append_if(COMPILER_RT_HAS_LIBDL -ldl SANITIZER_TEST_LINK_FLAGS_COMMON)
-append_if(COMPILER_RT_HAS_LIBPTHREAD -pthread SANITIZER_TEST_LINK_FLAGS_COMMON)
+set(SANITIZER_TEST_LINK_LIBS)
+append_list_if(ANDROID log SANITIZER_TEST_LINK_LIBS)
+# NDK r10 requires -latomic almost always.
+append_list_if(ANDROID atomic SANITIZER_TEST_LINK_LIBS)
+
+append_list_if(COMPILER_RT_HAS_LIBDL -ldl SANITIZER_TEST_LINK_FLAGS_COMMON)
+append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread SANITIZER_TEST_LINK_FLAGS_COMMON)
# x86_64 FreeBSD 9.2 additionally requires libc++ to build the tests. Also,
# 'libm' shall be specified explicitly to build i386 tests.
if(CMAKE_SYSTEM MATCHES "FreeBSD-9.2-RELEASE")
@@ -141,7 +149,7 @@
endif()
endmacro()
-if(COMPILER_RT_CAN_EXECUTE_TESTS)
+if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID)
# We use just-built clang to build sanitizer_common unittests, so we must
# be sure that produced binaries would work.
if(APPLE)
@@ -170,20 +178,21 @@
endif()
if(ANDROID)
- # We assume that unit tests on Android are built in a build
- # tree with fresh Clang as a host compiler.
- add_executable(SanitizerTest
- ${SANITIZER_UNITTESTS}
- ${COMPILER_RT_GTEST_SOURCE}
- $<TARGET_OBJECTS:RTSanitizerCommon.arm.android>)
- set_target_compile_flags(SanitizerTest
- ${SANITIZER_COMMON_CFLAGS}
- ${SANITIZER_TEST_CFLAGS_COMMON})
- # Setup correct output directory and link flags.
- set_target_properties(SanitizerTest PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
- set_target_link_flags(SanitizerTest ${SANITIZER_TEST_LINK_FLAGS_COMMON})
- target_link_libraries(SanitizerTest log)
- # Add unit test to test suite.
- add_dependencies(SanitizerUnitTests SanitizerTest)
+ foreach(arch ${SANITIZER_COMMON_SUPPORTED_ARCH})
+ add_executable(SanitizerTest
+ ${SANITIZER_UNITTESTS}
+ ${COMPILER_RT_GTEST_SOURCE}
+ $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>)
+ set_target_compile_flags(SanitizerTest
+ ${SANITIZER_COMMON_CFLAGS}
+ ${SANITIZER_TEST_CFLAGS_COMMON})
+ # Setup correct output directory and link flags.
+ set_target_properties(SanitizerTest PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ set_target_link_flags(SanitizerTest ${SANITIZER_TEST_LINK_FLAGS_COMMON})
+ target_link_libraries(SanitizerTest ${SANITIZER_TEST_LINK_LIBS})
+ # Add unit test to test suite.
+ add_dependencies(SanitizerUnitTests SanitizerTest)
+ endforeach()
endif()
diff --git a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
index 4340f37..f61d58d 100644
--- a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
@@ -629,9 +629,9 @@
}
}
-TEST(Allocator, InternalAllocFailure) {
- EXPECT_DEATH(Ident(InternalAlloc(10 << 20)),
- "Unexpected mmap in InternalAllocator!");
+TEST(Allocator, LargeAlloc) {
+ void *p = InternalAlloc(10 << 20);
+ InternalFree(p);
}
TEST(Allocator, ScopedBuffer) {
diff --git a/lib/sanitizer_common/tests/sanitizer_common_test.cc b/lib/sanitizer_common/tests/sanitizer_common_test.cc
index 8dcecaf..e08a38c 100644
--- a/lib/sanitizer_common/tests/sanitizer_common_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_common_test.cc
@@ -226,40 +226,4 @@
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_posix_test.cc b/lib/sanitizer_common/tests/sanitizer_posix_test.cc
index 035899c..56ce416 100644
--- a/lib/sanitizer_common/tests/sanitizer_posix_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_posix_test.cc
@@ -18,6 +18,7 @@
#include "gtest/gtest.h"
#include <pthread.h>
+#include <sys/mman.h>
namespace __sanitizer {
@@ -57,6 +58,23 @@
EXPECT_FALSE(destructor_executed);
}
+TEST(SanitizerCommon, IsAccessibleMemoryRange) {
+ const int page_size = GetPageSize();
+ uptr mem = (uptr)mmap(0, 3 * page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ // Protect the middle page.
+ mprotect((void *)(mem + page_size), page_size, PROT_NONE);
+ EXPECT_TRUE(IsAccessibleMemoryRange(mem, page_size - 1));
+ EXPECT_TRUE(IsAccessibleMemoryRange(mem, page_size));
+ EXPECT_FALSE(IsAccessibleMemoryRange(mem, page_size + 1));
+ EXPECT_TRUE(IsAccessibleMemoryRange(mem + page_size - 1, 1));
+ EXPECT_FALSE(IsAccessibleMemoryRange(mem + page_size - 1, 2));
+ EXPECT_FALSE(IsAccessibleMemoryRange(mem + 2 * page_size - 1, 1));
+ EXPECT_TRUE(IsAccessibleMemoryRange(mem + 2 * page_size, page_size));
+ EXPECT_FALSE(IsAccessibleMemoryRange(mem, 3 * page_size));
+ EXPECT_FALSE(IsAccessibleMemoryRange(0x0, 2));
+}
+
} // namespace __sanitizer
#endif // SANITIZER_POSIX
diff --git a/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc b/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc
index 5c075d5..513432f 100644
--- a/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc
@@ -18,74 +18,75 @@
namespace __sanitizer {
TEST(SanitizerCommon, StackDepotBasic) {
- uptr s1[] = {1, 2, 3, 4, 5};
- u32 i1 = StackDepotPut(s1, ARRAY_SIZE(s1));
- uptr sz1 = 0;
- const uptr *sp1 = StackDepotGet(i1, &sz1);
- EXPECT_NE(sp1, (uptr*)0);
- EXPECT_EQ(sz1, ARRAY_SIZE(s1));
- EXPECT_EQ(internal_memcmp(sp1, s1, sizeof(s1)), 0);
+ uptr array[] = {1, 2, 3, 4, 5};
+ StackTrace s1(array, ARRAY_SIZE(array));
+ u32 i1 = StackDepotPut(s1);
+ StackTrace stack = StackDepotGet(i1);
+ EXPECT_NE(stack.trace, (uptr*)0);
+ EXPECT_EQ(ARRAY_SIZE(array), stack.size);
+ EXPECT_EQ(0, internal_memcmp(stack.trace, array, sizeof(array)));
}
TEST(SanitizerCommon, StackDepotAbsent) {
- uptr sz1 = 0;
- const uptr *sp1 = StackDepotGet((1 << 30) - 1, &sz1);
- EXPECT_EQ(sp1, (uptr*)0);
+ StackTrace stack = StackDepotGet((1 << 30) - 1);
+ EXPECT_EQ((uptr*)0, stack.trace);
}
TEST(SanitizerCommon, StackDepotEmptyStack) {
- u32 i1 = StackDepotPut(0, 0);
- uptr sz1 = 0;
- const uptr *sp1 = StackDepotGet(i1, &sz1);
- EXPECT_EQ(sp1, (uptr*)0);
+ u32 i1 = StackDepotPut(StackTrace());
+ StackTrace stack = StackDepotGet(i1);
+ EXPECT_EQ((uptr*)0, stack.trace);
}
TEST(SanitizerCommon, StackDepotZeroId) {
- uptr sz1 = 0;
- const uptr *sp1 = StackDepotGet(0, &sz1);
- EXPECT_EQ(sp1, (uptr*)0);
+ StackTrace stack = StackDepotGet(0);
+ EXPECT_EQ((uptr*)0, stack.trace);
}
TEST(SanitizerCommon, StackDepotSame) {
- uptr s1[] = {1, 2, 3, 4, 6};
- u32 i1 = StackDepotPut(s1, ARRAY_SIZE(s1));
- u32 i2 = StackDepotPut(s1, ARRAY_SIZE(s1));
+ uptr array[] = {1, 2, 3, 4, 6};
+ StackTrace s1(array, ARRAY_SIZE(array));
+ u32 i1 = StackDepotPut(s1);
+ u32 i2 = StackDepotPut(s1);
EXPECT_EQ(i1, i2);
- uptr sz1 = 0;
- const uptr *sp1 = StackDepotGet(i1, &sz1);
- EXPECT_NE(sp1, (uptr*)0);
- EXPECT_EQ(sz1, ARRAY_SIZE(s1));
- EXPECT_EQ(internal_memcmp(sp1, s1, sizeof(s1)), 0);
+ StackTrace stack = StackDepotGet(i1);
+ EXPECT_NE(stack.trace, (uptr*)0);
+ EXPECT_EQ(ARRAY_SIZE(array), stack.size);
+ EXPECT_EQ(0, internal_memcmp(stack.trace, array, sizeof(array)));
}
TEST(SanitizerCommon, StackDepotSeveral) {
- uptr s1[] = {1, 2, 3, 4, 7};
- u32 i1 = StackDepotPut(s1, ARRAY_SIZE(s1));
- uptr s2[] = {1, 2, 3, 4, 8, 9};
- u32 i2 = StackDepotPut(s2, ARRAY_SIZE(s2));
+ uptr array1[] = {1, 2, 3, 4, 7};
+ StackTrace s1(array1, ARRAY_SIZE(array1));
+ u32 i1 = StackDepotPut(s1);
+ uptr array2[] = {1, 2, 3, 4, 8, 9};
+ StackTrace s2(array2, ARRAY_SIZE(array2));
+ u32 i2 = StackDepotPut(s2);
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};
+ uptr array1[] = {1, 2, 3, 4, 5};
+ uptr array2[] = {7, 1, 3, 0};
+ uptr array3[] = {10, 2, 5, 3};
+ uptr array4[] = {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));
+ StackTrace s1(array1, ARRAY_SIZE(array1));
+ StackTrace s2(array2, ARRAY_SIZE(array2));
+ StackTrace s3(array3, ARRAY_SIZE(array3));
+ StackTrace s4(array4, ARRAY_SIZE(array4));
+ ids[0] = StackDepotPut(s1);
+ ids[1] = StackDepotPut(s2);
+ ids[2] = StackDepotPut(s3);
+ ids[3] = StackDepotPut(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);
+ StackTrace stack = StackDepotGet(ids[i]);
+ StackTrace from_map = map.Get(ids[i]);
+ EXPECT_EQ(stack.size, from_map.size);
+ EXPECT_EQ(stack.trace, from_map.trace);
}
}
diff --git a/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc b/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc
new file mode 100644
index 0000000..cc9a9ed
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc
@@ -0,0 +1,122 @@
+//===-- sanitizer_common_printer_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 sanitizer_common test suite.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_stacktrace_printer.h"
+
+#include "gtest/gtest.h"
+
+namespace __sanitizer {
+
+TEST(SanitizerStacktracePrinter, RenderSourceLocation) {
+ InternalScopedString str(128);
+ RenderSourceLocation(&str, "/dir/file.cc", 10, 5, "");
+ EXPECT_STREQ("/dir/file.cc:10:5", str.data());
+
+ str.clear();
+ RenderSourceLocation(&str, "/dir/file.cc", 11, 0, "");
+ EXPECT_STREQ("/dir/file.cc:11", str.data());
+
+ str.clear();
+ RenderSourceLocation(&str, "/dir/file.cc", 0, 0, "");
+ EXPECT_STREQ("/dir/file.cc", str.data());
+
+ str.clear();
+ RenderSourceLocation(&str, "/dir/file.cc", 10, 5, "/dir/");
+ EXPECT_STREQ("file.cc:10:5", str.data());
+}
+
+TEST(SanitizerStacktracePrinter, RenderModuleLocation) {
+ InternalScopedString str(128);
+ RenderModuleLocation(&str, "/dir/exe", 0x123, "");
+ EXPECT_STREQ("(/dir/exe+0x123)", str.data());
+
+ // Check that we strip file prefix if necessary.
+ str.clear();
+ RenderModuleLocation(&str, "/dir/exe", 0x123, "/dir/");
+ EXPECT_STREQ("(exe+0x123)", str.data());
+}
+
+TEST(SanitizerStacktracePrinter, RenderFrame) {
+ int frame_no = 42;
+ AddressInfo info;
+ info.address = 0x400000;
+ info.module = internal_strdup("/path/to/my/module");
+ info.module_offset = 0x200;
+ info.function = internal_strdup("function_foo");
+ info.function_offset = 0x100;
+ info.file = internal_strdup("/path/to/my/source");
+ info.line = 10;
+ info.column = 5;
+ InternalScopedString str(256);
+
+ // Dump all the AddressInfo fields.
+ RenderFrame(&str, "%% Frame:%n PC:%p Module:%m ModuleOffset:%o "
+ "Function:%f FunctionOffset:%q Source:%s Line:%l "
+ "Column:%c",
+ frame_no, info, "/path/to/", "function_");
+ EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 "
+ "Function:foo FunctionOffset:0x100 Source:my/source Line:10 "
+ "Column:5",
+ str.data());
+ info.Clear();
+ str.clear();
+
+ // Test special format specifiers.
+ info.address = 0x400000;
+ RenderFrame(&str, "%M", frame_no, info);
+ EXPECT_NE(nullptr, internal_strstr(str.data(), "400000"));
+ str.clear();
+
+ RenderFrame(&str, "%L", frame_no, info);
+ EXPECT_STREQ("(<unknown module>)", str.data());
+ str.clear();
+
+ info.module = internal_strdup("/path/to/module");
+ info.module_offset = 0x200;
+ RenderFrame(&str, "%M", frame_no, info);
+ EXPECT_NE(nullptr, internal_strstr(str.data(), "(module+0x"));
+ EXPECT_NE(nullptr, internal_strstr(str.data(), "200"));
+ str.clear();
+
+ RenderFrame(&str, "%L", frame_no, info);
+ EXPECT_STREQ("(/path/to/module+0x200)", str.data());
+ str.clear();
+
+ info.function = internal_strdup("my_function");
+ RenderFrame(&str, "%F", frame_no, info);
+ EXPECT_STREQ("in my_function", str.data());
+ str.clear();
+
+ info.function_offset = 0x100;
+ RenderFrame(&str, "%F %S", frame_no, info);
+ EXPECT_STREQ("in my_function+0x100 <null>", str.data());
+ str.clear();
+
+ info.file = internal_strdup("my_file");
+ RenderFrame(&str, "%F %S", frame_no, info);
+ EXPECT_STREQ("in my_function my_file", str.data());
+ str.clear();
+
+ info.line = 10;
+ RenderFrame(&str, "%F %S", frame_no, info);
+ EXPECT_STREQ("in my_function my_file:10", str.data());
+ str.clear();
+
+ info.column = 5;
+ RenderFrame(&str, "%S %L", frame_no, info);
+ EXPECT_STREQ("my_file:10:5 my_file:10:5", str.data());
+ str.clear();
+
+ info.Clear();
+}
+
+} // namespace __sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc
index b71044a..ac820c2 100644
--- a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc
@@ -20,6 +20,7 @@
class FastUnwindTest : public ::testing::Test {
protected:
virtual void SetUp();
+ virtual void TearDown();
bool TryFastUnwind(uptr max_depth) {
if (!StackTrace::WillUseFastUnwind(true))
return false;
@@ -28,11 +29,13 @@
return true;
}
- uptr fake_stack[10];
+ void *mapping;
+ uptr *fake_stack;
+ const uptr fake_stack_size = 10;
uptr start_pc;
uptr fake_top;
uptr fake_bottom;
- StackTrace trace;
+ BufferedStackTrace trace;
};
static uptr PC(uptr idx) {
@@ -40,22 +43,34 @@
}
void FastUnwindTest::SetUp() {
+ size_t ps = GetPageSize();
+ mapping = MmapOrDie(2 * ps, "FastUnwindTest");
+ Mprotect((uptr)mapping, ps);
+
+ // Unwinder may peek 1 word down from the starting FP.
+ fake_stack = (uptr *)((uptr)mapping + ps + sizeof(uptr));
+
// Fill an array of pointers with fake fp+retaddr pairs. Frame pointers have
// even indices.
- for (uptr i = 0; i+1 < ARRAY_SIZE(fake_stack); i += 2) {
+ for (uptr i = 0; i + 1 < fake_stack_size; i += 2) {
fake_stack[i] = (uptr)&fake_stack[i+2]; // fp
fake_stack[i+1] = PC(i + 1); // retaddr
}
- // Mark the last fp as zero to terminate the stack trace.
- fake_stack[RoundDownTo(ARRAY_SIZE(fake_stack) - 1, 2)] = 0;
+ // Mark the last fp point back up to terminate the stack trace.
+ fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uptr)&fake_stack[0];
// Top is two slots past the end because FastUnwindStack subtracts two.
- fake_top = (uptr)&fake_stack[ARRAY_SIZE(fake_stack) + 2];
+ fake_top = (uptr)&fake_stack[fake_stack_size + 2];
// Bottom is one slot before the start because FastUnwindStack uses >.
- fake_bottom = (uptr)&fake_stack[-1];
+ fake_bottom = (uptr)mapping;
start_pc = PC(0);
}
+void FastUnwindTest::TearDown() {
+ size_t ps = GetPageSize();
+ UnmapOrDie(mapping, 2 * ps);
+}
+
TEST_F(FastUnwindTest, Basic) {
if (!TryFastUnwind(kStackTraceMax))
return;
@@ -109,10 +124,22 @@
EXPECT_EQ(0U, trace.top_frame_bp);
}
+TEST_F(FastUnwindTest, FPBelowPrevFP) {
+ // The next FP points to unreadable memory inside the stack limits, but below
+ // current FP.
+ fake_stack[0] = (uptr)&fake_stack[-50];
+ fake_stack[1] = PC(1);
+ if (!TryFastUnwind(3))
+ return;
+ EXPECT_EQ(2U, trace.size);
+ EXPECT_EQ(PC(0), trace.trace[0]);
+ EXPECT_EQ(PC(1), trace.trace[1]);
+}
+
TEST(SlowUnwindTest, ShortStackTrace) {
if (StackTrace::WillUseFastUnwind(false))
return;
- StackTrace stack;
+ BufferedStackTrace stack;
uptr pc = StackTrace::GetCurrentPc();
uptr bp = GET_CURRENT_FRAME();
stack.Unwind(0, pc, bp, 0, 0, 0, false);
diff --git a/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
index 93fc8a3..2a1e356 100644
--- a/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
@@ -66,11 +66,13 @@
CHECK(!internal_strcmp(SuppressionTypeString(SuppressionSignal), "signal"));
CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLeak), "leak"));
CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLib),
- "called_from_lib"));
+ "called_from_lib"));
CHECK(
!internal_strcmp(SuppressionTypeString(SuppressionDeadlock), "deadlock"));
+ CHECK(!internal_strcmp(SuppressionTypeString(SuppressionVptrCheck),
+ "vptr_check"));
// Ensure this test is up-to-date when suppression types are added.
- CHECK_EQ(SuppressionTypeCount, 8);
+ CHECK_EQ(9, SuppressionTypeCount);
}
class SuppressionContextTest : public ::testing::Test {
diff --git a/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc b/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc
index b6a60d5..58c627a 100644
--- a/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc
@@ -107,13 +107,13 @@
registry->FindThread(HasUid, (void*)0x1234));
// Detach and finish and join remaining threads.
for (u32 i = 6; i <= 10; i++) {
- registry->DetachThread(i);
+ registry->DetachThread(i, 0);
registry->FinishThread(i);
}
for (u32 i = 0; i < new_tids.size(); i++) {
u32 tid = new_tids[i];
registry->StartThread(tid, 0, 0);
- registry->DetachThread(tid);
+ registry->DetachThread(tid, 0);
registry->FinishThread(tid);
}
CheckThreadQuantity(registry, exp_total, 1, 1);
diff --git a/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt
index 19efb54..6d51faa 100644
--- a/lib/tsan/CMakeLists.txt
+++ b/lib/tsan/CMakeLists.txt
@@ -5,12 +5,12 @@
set(TSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
# SANITIZER_COMMON_CFLAGS contains -fPIC, but it's performance-critical for
# TSan runtime to be built with -fPIE to reduce the number of register spills.
-append_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE TSAN_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE TSAN_CFLAGS)
append_no_rtti_flag(TSAN_CFLAGS)
set(TSAN_RTL_CFLAGS ${TSAN_CFLAGS})
-append_if(COMPILER_RT_HAS_WFRAME_LARGER_THAN_FLAG -Wframe-larger-than=512 TSAN_RTL_CFLAGS)
-append_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors TSAN_RTL_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WFRAME_LARGER_THAN_FLAG -Wframe-larger-than=512 TSAN_RTL_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors TSAN_RTL_CFLAGS)
# FIXME: Add support for --sysroot=. compile flag:
if("${CMAKE_BUILD_TYPE}" EQUAL "Release")
@@ -103,7 +103,8 @@
add_dependencies(compiler-rt tsan)
# Build libcxx instrumented with TSan.
-if(COMPILER_RT_HAS_LIBCXX_SOURCES AND
+if(TSAN_SUPPORTED_ARCH AND
+ COMPILER_RT_HAS_LIBCXX_SOURCES AND
COMPILER_RT_TEST_COMPILER_ID STREQUAL "Clang")
set(LIBCXX_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libcxx_tsan)
add_custom_libcxx(libcxx_tsan ${LIBCXX_PREFIX}
diff --git a/lib/tsan/check_cmake.sh b/lib/tsan/check_cmake.sh
index 7e858ef..7668c5b 100755
--- a/lib/tsan/check_cmake.sh
+++ b/lib/tsan/check_cmake.sh
@@ -3,11 +3,11 @@
set -e
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-if [ -d "$ROOT/build" ]; then
- cd $ROOT/build
+if [ -d "$ROOT/llvm-build" ]; then
+ cd $ROOT/llvm-build
else
- mkdir -p $ROOT/build
- cd $ROOT/build
+ mkdir -p $ROOT/llvm-build
+ cd $ROOT/llvm-build
CC=clang CXX=clang++ cmake -G Ninja -DLLVM_ENABLE_WERROR=ON -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON $ROOT/../../../..
fi
ninja
diff --git a/lib/tsan/dd/CMakeLists.txt b/lib/tsan/dd/CMakeLists.txt
index 9328721..aa7d63d 100644
--- a/lib/tsan/dd/CMakeLists.txt
+++ b/lib/tsan/dd/CMakeLists.txt
@@ -22,6 +22,10 @@
dd_rtl.h
)
+set(DD_LINKLIBS)
+append_list_if(COMPILER_RT_HAS_LIBDL dl DD_LINKLIBS)
+append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread DD_LINKLIBS)
+
add_custom_target(dd)
# Deadlock detector is currently supported on 64-bit Linux only.
if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE AND NOT ANDROID)
@@ -43,7 +47,7 @@
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>)
- target_link_libraries(clang_rt.dyndd-${arch} pthread dl)
+ target_link_libraries(clang_rt.dyndd-${arch} ${DD_LINKLIBS})
endif()
add_dependencies(compiler-rt dd)
diff --git a/lib/tsan/dd/dd_rtl.cc b/lib/tsan/dd/dd_rtl.cc
index 729e79e..44de617 100644
--- a/lib/tsan/dd/dd_rtl.cc
+++ b/lib/tsan/dd/dd_rtl.cc
@@ -19,20 +19,19 @@
static Context *ctx;
static u32 CurrentStackTrace(Thread *thr, uptr skip) {
- StackTrace trace;
+ BufferedStackTrace stack;
thr->ignore_interceptors = true;
- trace.Unwind(1000, 0, 0, 0, 0, 0, false);
+ stack.Unwind(1000, 0, 0, 0, 0, 0, false);
thr->ignore_interceptors = false;
- if (trace.size <= skip)
+ if (stack.size <= skip)
return 0;
- return StackDepotPut(trace.trace + skip, trace.size - skip);
+ return StackDepotPut(StackTrace(stack.trace + skip, stack.size - skip));
}
static void PrintStackTrace(Thread *thr, u32 stk) {
- uptr size = 0;
- const uptr *trace = StackDepotGet(stk, &size);
+ StackTrace stack = StackDepotGet(stk);
thr->ignore_interceptors = true;
- StackTrace::PrintStack(trace, size);
+ stack.Print();
thr->ignore_interceptors = false;
}
diff --git a/lib/tsan/go/build.bat b/lib/tsan/go/build.bat
index 4480e66..7156b7d 100644
--- a/lib/tsan/go/build.bat
+++ b/lib/tsan/go/build.bat
@@ -1,4 +1,4 @@
-type tsan_go.cc ..\rtl\tsan_clock.cc ..\rtl\tsan_flags.cc ..\rtl\tsan_md5.cc ..\rtl\tsan_mutex.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_sync.cc ..\rtl\tsan_stack_trace.cc ..\..\sanitizer_common\sanitizer_allocator.cc ..\..\sanitizer_common\sanitizer_common.cc ..\..\sanitizer_common\sanitizer_flags.cc ..\..\sanitizer_common\sanitizer_stacktrace.cc ..\..\sanitizer_common\sanitizer_libc.cc ..\..\sanitizer_common\sanitizer_printf.cc ..\..\sanitizer_common\sanitizer_suppressions.cc ..\..\sanitizer_common\sanitizer_thread_registry.cc ..\rtl\tsan_platform_windows.cc ..\..\sanitizer_common\sanitizer_win.cc ..\..\sanitizer_common\sanitizer_deadlock_detector1.cc ..\..\sanitizer_common\sanitizer_stackdepot.cc ..\..\sanitizer_common\sanitizer_persistent_allocator.cc > gotsan.cc
+type tsan_go.cc ..\rtl\tsan_interface_atomic.cc ..\rtl\tsan_clock.cc ..\rtl\tsan_flags.cc ..\rtl\tsan_md5.cc ..\rtl\tsan_mutex.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_sync.cc ..\rtl\tsan_stack_trace.cc ..\..\sanitizer_common\sanitizer_allocator.cc ..\..\sanitizer_common\sanitizer_common.cc ..\..\sanitizer_common\sanitizer_flags.cc ..\..\sanitizer_common\sanitizer_stacktrace.cc ..\..\sanitizer_common\sanitizer_libc.cc ..\..\sanitizer_common\sanitizer_printf.cc ..\..\sanitizer_common\sanitizer_suppressions.cc ..\..\sanitizer_common\sanitizer_thread_registry.cc ..\rtl\tsan_platform_windows.cc ..\..\sanitizer_common\sanitizer_win.cc ..\..\sanitizer_common\sanitizer_deadlock_detector1.cc ..\..\sanitizer_common\sanitizer_stackdepot.cc ..\..\sanitizer_common\sanitizer_persistent_allocator.cc > gotsan.cc
gcc -c -o race_windows_amd64.syso gotsan.cc -I..\rtl -I..\.. -I..\..\sanitizer_common -I..\..\..\include -m64 -Wall -fno-exceptions -fno-rtti -DTSAN_GO -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 -Wno-error=attributes -Wno-attributes -Wno-format -DTSAN_DEBUG=0 -O3 -fomit-frame-pointer
diff --git a/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh
index f8eb081..dbb92f3 100755
--- a/lib/tsan/go/buildgo.sh
+++ b/lib/tsan/go/buildgo.sh
@@ -4,6 +4,7 @@
tsan_go.cc
../rtl/tsan_clock.cc
../rtl/tsan_flags.cc
+ ../rtl/tsan_interface_atomic.cc
../rtl/tsan_md5.cc
../rtl/tsan_mutex.cc
../rtl/tsan_report.cc
@@ -25,6 +26,7 @@
../../sanitizer_common/sanitizer_suppressions.cc
../../sanitizer_common/sanitizer_thread_registry.cc
../../sanitizer_common/sanitizer_stackdepot.cc
+ ../../sanitizer_common/sanitizer_stacktrace.cc
"
if [ "`uname -a | grep Linux`" != "" ]; then
@@ -35,6 +37,7 @@
../rtl/tsan_platform_linux.cc
../../sanitizer_common/sanitizer_posix.cc
../../sanitizer_common/sanitizer_posix_libcdep.cc
+ ../../sanitizer_common/sanitizer_procmaps_common.cc
../../sanitizer_common/sanitizer_procmaps_linux.cc
../../sanitizer_common/sanitizer_linux.cc
../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
@@ -47,7 +50,8 @@
../rtl/tsan_platform_linux.cc
../../sanitizer_common/sanitizer_posix.cc
../../sanitizer_common/sanitizer_posix_libcdep.cc
- ../../sanitizer_common/sanitizer_procmaps_linux.cc
+ ../../sanitizer_common/sanitizer_procmaps_common.cc
+ ../../sanitizer_common/sanitizer_procmaps_freebsd.cc
../../sanitizer_common/sanitizer_linux.cc
../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
"
@@ -82,7 +86,7 @@
cat $F >> gotsan.cc
done
-FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -m64 -Wall -fno-exceptions -fno-rtti -DTSAN_GO -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 -DSANITIZER_DEADLOCK_DETECTOR_VERSION=2 $OSCFLAGS"
+FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -std=c++11 -m64 -Wall -fno-exceptions -fno-rtti -DTSAN_GO -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 -DSANITIZER_DEADLOCK_DETECTOR_VERSION=2 $OSCFLAGS"
if [ "$DEBUG" == "" ]; then
FLAGS+=" -DTSAN_DEBUG=0 -O3 -msse3 -fomit-frame-pointer"
else
diff --git a/lib/tsan/go/tsan_go.cc b/lib/tsan/go/tsan_go.cc
index 5e22092..c1d401f 100644
--- a/lib/tsan/go/tsan_go.cc
+++ b/lib/tsan/go/tsan_go.cc
@@ -39,14 +39,6 @@
return 0;
}
-ReportStack *NewReportStackEntry(uptr addr) {
- ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack,
- sizeof(ReportStack));
- internal_memset(ent, 0, sizeof(*ent));
- ent->pc = addr;
- return ent;
-}
-
void *internal_alloc(MBlockType typ, uptr sz) {
return InternalAlloc(sz);
}
@@ -68,20 +60,17 @@
static void (*symbolize_cb)(SymbolizeContext *ctx);
ReportStack *SymbolizeCode(uptr addr) {
- ReportStack *s = (ReportStack*)internal_alloc(MBlockReportStack,
- sizeof(ReportStack));
- internal_memset(s, 0, sizeof(*s));
- s->pc = addr;
+ ReportStack *s = ReportStack::New(addr);
SymbolizeContext ctx;
internal_memset(&ctx, 0, sizeof(ctx));
ctx.pc = addr;
symbolize_cb(&ctx);
if (ctx.res) {
- s->offset = ctx.off;
- s->func = internal_strdup(ctx.func ? ctx.func : "??");
- s->file = internal_strdup(ctx.file ? ctx.file : "-");
- s->line = ctx.line;
- s->col = 0;
+ s->info.module_offset = ctx.off;
+ s->info.function = internal_strdup(ctx.func ? ctx.func : "??");
+ s->info.file = internal_strdup(ctx.file ? ctx.file : "-");
+ s->info.line = ctx.line;
+ s->info.column = 0;
}
return s;
}
@@ -208,6 +197,14 @@
MutexReadUnlock(thr, 0, addr);
}
+void __tsan_go_ignore_sync_begin(ThreadState *thr) {
+ ThreadIgnoreSyncBegin(thr, 0);
+}
+
+void __tsan_go_ignore_sync_end(ThreadState *thr) {
+ ThreadIgnoreSyncEnd(thr, 0);
+}
+
} // extern "C"
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_clock.cc b/lib/tsan/rtl/tsan_clock.cc
index e140a3c..1855f05 100644
--- a/lib/tsan/rtl/tsan_clock.cc
+++ b/lib/tsan/rtl/tsan_clock.cc
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "tsan_clock.h"
#include "tsan_rtl.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
// SyncClock and ThreadClock implement vector clocks for sync variables
// (mutexes, atomic variables, file descriptors, etc) and threads, respectively.
@@ -102,13 +103,13 @@
clk_[tid_].reused = reused_;
}
-void ThreadClock::acquire(const SyncClock *src) {
+void ThreadClock::acquire(ClockCache *c, const SyncClock *src) {
DCHECK(nclk_ <= kMaxTid);
- DCHECK(src->clk_.Size() <= kMaxTid);
+ DCHECK(src->size_ <= kMaxTid);
CPP_STAT_INC(StatClockAcquire);
// Check if it's empty -> no need to do anything.
- const uptr nclk = src->clk_.Size();
+ const uptr nclk = src->size_;
if (nclk == 0) {
CPP_STAT_INC(StatClockAcquireEmpty);
return;
@@ -118,12 +119,12 @@
bool acquired = false;
if (nclk > tid_) {
CPP_STAT_INC(StatClockAcquireLarge);
- if (src->clk_[tid_].reused == reused_) {
+ if (src->elem(tid_).reused == reused_) {
CPP_STAT_INC(StatClockAcquireRepeat);
for (unsigned i = 0; i < kDirtyTids; i++) {
unsigned tid = src->dirty_tids_[i];
if (tid != kInvalidTid) {
- u64 epoch = src->clk_[tid].epoch;
+ u64 epoch = src->elem(tid).epoch;
if (clk_[tid].epoch < epoch) {
clk_[tid].epoch = epoch;
acquired = true;
@@ -142,7 +143,7 @@
CPP_STAT_INC(StatClockAcquireFull);
nclk_ = max(nclk_, nclk);
for (uptr i = 0; i < nclk; i++) {
- u64 epoch = src->clk_[i].epoch;
+ u64 epoch = src->elem(i).epoch;
if (clk_[i].epoch < epoch) {
clk_[i].epoch = epoch;
acquired = true;
@@ -151,7 +152,7 @@
// Remember that this thread has acquired this clock.
if (nclk > tid_)
- src->clk_[tid_].reused = reused_;
+ src->elem(tid_).reused = reused_;
if (acquired) {
CPP_STAT_INC(StatClockAcquiredSomething);
@@ -159,28 +160,26 @@
}
}
-void ThreadClock::release(SyncClock *dst) const {
+void ThreadClock::release(ClockCache *c, SyncClock *dst) const {
DCHECK_LE(nclk_, kMaxTid);
- DCHECK_LE(dst->clk_.Size(), kMaxTid);
+ DCHECK_LE(dst->size_, kMaxTid);
- if (dst->clk_.Size() == 0) {
+ if (dst->size_ == 0) {
// ReleaseStore will correctly set release_store_tid_,
// which can be important for future operations.
- ReleaseStore(dst);
+ ReleaseStore(c, dst);
return;
}
CPP_STAT_INC(StatClockRelease);
// Check if we need to resize dst.
- if (dst->clk_.Size() < nclk_) {
- CPP_STAT_INC(StatClockReleaseResize);
- dst->clk_.Resize(nclk_);
- }
+ if (dst->size_ < nclk_)
+ dst->Resize(c, nclk_);
// Check if we had not acquired anything from other threads
// since the last release on dst. If so, we need to update
- // only dst->clk_[tid_].
- if (dst->clk_[tid_].epoch > last_acquire_) {
+ // only dst->elem(tid_).
+ if (dst->elem(tid_).epoch > last_acquire_) {
UpdateCurrentThread(dst);
if (dst->release_store_tid_ != tid_ ||
dst->release_store_reused_ != reused_)
@@ -196,14 +195,15 @@
CPP_STAT_INC(StatClockReleaseAcquired);
// Update dst->clk_.
for (uptr i = 0; i < nclk_; i++) {
- dst->clk_[i].epoch = max(dst->clk_[i].epoch, clk_[i].epoch);
- dst->clk_[i].reused = 0;
+ ClockElem &ce = dst->elem(i);
+ ce.epoch = max(ce.epoch, clk_[i].epoch);
+ ce.reused = 0;
}
// Clear 'acquired' flag in the remaining elements.
- if (nclk_ < dst->clk_.Size())
+ if (nclk_ < dst->size_)
CPP_STAT_INC(StatClockReleaseClearTail);
- for (uptr i = nclk_; i < dst->clk_.Size(); i++)
- dst->clk_[i].reused = 0;
+ for (uptr i = nclk_; i < dst->size_; i++)
+ dst->elem(i).reused = 0;
for (unsigned i = 0; i < kDirtyTids; i++)
dst->dirty_tids_[i] = kInvalidTid;
dst->release_store_tid_ = kInvalidTid;
@@ -211,23 +211,21 @@
// If we've acquired dst, remember this fact,
// so that we don't need to acquire it on next acquire.
if (acquired)
- dst->clk_[tid_].reused = reused_;
+ dst->elem(tid_).reused = reused_;
}
-void ThreadClock::ReleaseStore(SyncClock *dst) const {
+void ThreadClock::ReleaseStore(ClockCache *c, SyncClock *dst) const {
DCHECK(nclk_ <= kMaxTid);
- DCHECK(dst->clk_.Size() <= kMaxTid);
+ DCHECK(dst->size_ <= kMaxTid);
CPP_STAT_INC(StatClockStore);
// Check if we need to resize dst.
- if (dst->clk_.Size() < nclk_) {
- CPP_STAT_INC(StatClockStoreResize);
- dst->clk_.Resize(nclk_);
- }
+ if (dst->size_ < nclk_)
+ dst->Resize(c, nclk_);
if (dst->release_store_tid_ == tid_ &&
dst->release_store_reused_ == reused_ &&
- dst->clk_[tid_].epoch > last_acquire_) {
+ dst->elem(tid_).epoch > last_acquire_) {
CPP_STAT_INC(StatClockStoreFast);
UpdateCurrentThread(dst);
return;
@@ -236,13 +234,17 @@
// O(N) release-store.
CPP_STAT_INC(StatClockStoreFull);
for (uptr i = 0; i < nclk_; i++) {
- dst->clk_[i].epoch = clk_[i].epoch;
- dst->clk_[i].reused = 0;
+ ClockElem &ce = dst->elem(i);
+ ce.epoch = clk_[i].epoch;
+ ce.reused = 0;
}
// Clear the tail of dst->clk_.
- if (nclk_ < dst->clk_.Size()) {
- internal_memset(&dst->clk_[nclk_], 0,
- (dst->clk_.Size() - nclk_) * sizeof(dst->clk_[0]));
+ if (nclk_ < dst->size_) {
+ for (uptr i = nclk_; i < dst->size_; i++) {
+ ClockElem &ce = dst->elem(i);
+ ce.epoch = 0;
+ ce.reused = 0;
+ }
CPP_STAT_INC(StatClockStoreTail);
}
for (unsigned i = 0; i < kDirtyTids; i++)
@@ -250,19 +252,19 @@
dst->release_store_tid_ = tid_;
dst->release_store_reused_ = reused_;
// Rememeber that we don't need to acquire it in future.
- dst->clk_[tid_].reused = reused_;
+ dst->elem(tid_).reused = reused_;
}
-void ThreadClock::acq_rel(SyncClock *dst) {
+void ThreadClock::acq_rel(ClockCache *c, SyncClock *dst) {
CPP_STAT_INC(StatClockAcquireRelease);
- acquire(dst);
- ReleaseStore(dst);
+ acquire(c, dst);
+ ReleaseStore(c, dst);
}
// Updates only single element related to the current thread in dst->clk_.
void ThreadClock::UpdateCurrentThread(SyncClock *dst) const {
// Update the threads time, but preserve 'acquired' flag.
- dst->clk_[tid_].epoch = clk_[tid_].epoch;
+ dst->elem(tid_).epoch = clk_[tid_].epoch;
for (unsigned i = 0; i < kDirtyTids; i++) {
if (dst->dirty_tids_[i] == tid_) {
@@ -277,27 +279,73 @@
}
// Reset all 'acquired' flags, O(N).
CPP_STAT_INC(StatClockReleaseSlow);
- for (uptr i = 0; i < dst->clk_.Size(); i++) {
- dst->clk_[i].reused = 0;
- }
+ for (uptr i = 0; i < dst->size_; i++)
+ dst->elem(i).reused = 0;
for (unsigned i = 0; i < kDirtyTids; i++)
dst->dirty_tids_[i] = kInvalidTid;
}
// Checks whether the current threads has already acquired src.
bool ThreadClock::IsAlreadyAcquired(const SyncClock *src) const {
- if (src->clk_[tid_].reused != reused_)
+ if (src->elem(tid_).reused != reused_)
return false;
for (unsigned i = 0; i < kDirtyTids; i++) {
unsigned tid = src->dirty_tids_[i];
if (tid != kInvalidTid) {
- if (clk_[tid].epoch < src->clk_[tid].epoch)
+ if (clk_[tid].epoch < src->elem(tid).epoch)
return false;
}
}
return true;
}
+void SyncClock::Resize(ClockCache *c, uptr nclk) {
+ CPP_STAT_INC(StatClockReleaseResize);
+ if (RoundUpTo(nclk, ClockBlock::kClockCount) <=
+ RoundUpTo(size_, ClockBlock::kClockCount)) {
+ // Growing within the same block.
+ // Memory is already allocated, just increase the size.
+ size_ = nclk;
+ return;
+ }
+ if (nclk <= ClockBlock::kClockCount) {
+ // Grow from 0 to one-level table.
+ CHECK_EQ(size_, 0);
+ CHECK_EQ(tab_, 0);
+ CHECK_EQ(tab_idx_, 0);
+ size_ = nclk;
+ tab_idx_ = ctx->clock_alloc.Alloc(c);
+ tab_ = ctx->clock_alloc.Map(tab_idx_);
+ internal_memset(tab_, 0, sizeof(*tab_));
+ return;
+ }
+ // Growing two-level table.
+ if (size_ == 0) {
+ // Allocate first level table.
+ tab_idx_ = ctx->clock_alloc.Alloc(c);
+ tab_ = ctx->clock_alloc.Map(tab_idx_);
+ internal_memset(tab_, 0, sizeof(*tab_));
+ } else if (size_ <= ClockBlock::kClockCount) {
+ // Transform one-level table to two-level table.
+ u32 old = tab_idx_;
+ tab_idx_ = ctx->clock_alloc.Alloc(c);
+ tab_ = ctx->clock_alloc.Map(tab_idx_);
+ internal_memset(tab_, 0, sizeof(*tab_));
+ tab_->table[0] = old;
+ }
+ // At this point we have first level table allocated.
+ // Add second level tables as necessary.
+ for (uptr i = RoundUpTo(size_, ClockBlock::kClockCount);
+ i < nclk; i += ClockBlock::kClockCount) {
+ u32 idx = ctx->clock_alloc.Alloc(c);
+ ClockBlock *cb = ctx->clock_alloc.Map(idx);
+ internal_memset(cb, 0, sizeof(*cb));
+ CHECK_EQ(tab_->table[i/ClockBlock::kClockCount], 0);
+ tab_->table[i/ClockBlock::kClockCount] = idx;
+ }
+ size_ = nclk;
+}
+
// Sets a single element in the vector clock.
// This function is called only from weird places like AcquireGlobal.
void ThreadClock::set(unsigned tid, u64 v) {
@@ -321,33 +369,59 @@
}
SyncClock::SyncClock()
- : clk_(MBlockClock) {
+ : release_store_tid_(kInvalidTid)
+ , release_store_reused_()
+ , tab_()
+ , tab_idx_()
+ , size_() {
+ for (uptr i = 0; i < kDirtyTids; i++)
+ dirty_tids_[i] = kInvalidTid;
+}
+
+SyncClock::~SyncClock() {
+ // Reset must be called before dtor.
+ CHECK_EQ(size_, 0);
+ CHECK_EQ(tab_, 0);
+ CHECK_EQ(tab_idx_, 0);
+}
+
+void SyncClock::Reset(ClockCache *c) {
+ if (size_ == 0) {
+ // nothing
+ } else if (size_ <= ClockBlock::kClockCount) {
+ // One-level table.
+ ctx->clock_alloc.Free(c, tab_idx_);
+ } else {
+ // Two-level table.
+ for (uptr i = 0; i < size_; i += ClockBlock::kClockCount)
+ ctx->clock_alloc.Free(c, tab_->table[i / ClockBlock::kClockCount]);
+ ctx->clock_alloc.Free(c, tab_idx_);
+ }
+ tab_ = 0;
+ tab_idx_ = 0;
+ size_ = 0;
release_store_tid_ = kInvalidTid;
release_store_reused_ = 0;
for (uptr i = 0; i < kDirtyTids; i++)
dirty_tids_[i] = kInvalidTid;
}
-void SyncClock::Reset() {
- clk_.Reset();
- Zero();
-}
-
-void SyncClock::Zero() {
- clk_.Resize(0);
- release_store_tid_ = kInvalidTid;
- release_store_reused_ = 0;
- for (uptr i = 0; i < kDirtyTids; i++)
- dirty_tids_[i] = kInvalidTid;
+ClockElem &SyncClock::elem(unsigned tid) const {
+ DCHECK_LT(tid, size_);
+ if (size_ <= ClockBlock::kClockCount)
+ return tab_->clock[tid];
+ u32 idx = tab_->table[tid / ClockBlock::kClockCount];
+ ClockBlock *cb = ctx->clock_alloc.Map(idx);
+ return cb->clock[tid % ClockBlock::kClockCount];
}
void SyncClock::DebugDump(int(*printf)(const char *s, ...)) {
printf("clock=[");
- for (uptr i = 0; i < clk_.Size(); i++)
- printf("%s%llu", i == 0 ? "" : ",", clk_[i].epoch);
+ for (uptr i = 0; i < size_; i++)
+ printf("%s%llu", i == 0 ? "" : ",", elem(i).epoch);
printf("] reused=[");
- for (uptr i = 0; i < clk_.Size(); i++)
- printf("%s%llu", i == 0 ? "" : ",", clk_[i].reused);
+ for (uptr i = 0; i < size_; i++)
+ printf("%s%llu", i == 0 ? "" : ",", elem(i).reused);
printf("] release_store_tid=%d/%d dirty_tids=%d/%d",
release_store_tid_, release_store_reused_,
dirty_tids_[0], dirty_tids_[1]);
diff --git a/lib/tsan/rtl/tsan_clock.h b/lib/tsan/rtl/tsan_clock.h
index f7ab69a..4e352cb 100644
--- a/lib/tsan/rtl/tsan_clock.h
+++ b/lib/tsan/rtl/tsan_clock.h
@@ -14,7 +14,7 @@
#define TSAN_CLOCK_H
#include "tsan_defs.h"
-#include "tsan_vector.h"
+#include "tsan_dense_alloc.h"
namespace __tsan {
@@ -23,37 +23,65 @@
u64 reused : 64 - kClkBits;
};
+struct ClockBlock {
+ static const uptr kSize = 512;
+ static const uptr kTableSize = kSize / sizeof(u32);
+ static const uptr kClockCount = kSize / sizeof(ClockElem);
+
+ union {
+ u32 table[kTableSize];
+ ClockElem clock[kClockCount];
+ };
+
+ ClockBlock() {
+ }
+};
+
+typedef DenseSlabAlloc<ClockBlock, 1<<16, 1<<10> ClockAlloc;
+typedef DenseSlabAllocCache ClockCache;
+
// The clock that lives in sync variables (mutexes, atomics, etc).
class SyncClock {
public:
SyncClock();
+ ~SyncClock();
uptr size() const {
- return clk_.Size();
+ return size_;
}
u64 get(unsigned tid) const {
- DCHECK_LT(tid, clk_.Size());
- return clk_[tid].epoch;
+ return elem(tid).epoch;
}
- void Reset();
- void Zero();
+ void Resize(ClockCache *c, uptr nclk);
+ void Reset(ClockCache *c);
void DebugDump(int(*printf)(const char *s, ...));
private:
+ friend struct ThreadClock;
+ static const uptr kDirtyTids = 2;
+
unsigned release_store_tid_;
unsigned release_store_reused_;
- static const uptr kDirtyTids = 2;
unsigned dirty_tids_[kDirtyTids];
- mutable Vector<ClockElem> clk_;
- friend struct ThreadClock;
+ // tab_ contains indirect pointer to a 512b block using DenseSlabAlloc.
+ // If size_ <= 64, then tab_ points to an array with 64 ClockElem's.
+ // Otherwise, tab_ points to an array with 128 u32 elements,
+ // each pointing to the second-level 512b block with 64 ClockElem's.
+ ClockBlock *tab_;
+ u32 tab_idx_;
+ u32 size_;
+
+ ClockElem &elem(unsigned tid) const;
};
// The clock that lives in threads.
struct ThreadClock {
public:
+ typedef DenseSlabAllocCache Cache;
+
explicit ThreadClock(unsigned tid, unsigned reused = 0);
u64 get(unsigned tid) const {
@@ -76,10 +104,10 @@
return nclk_;
}
- void acquire(const SyncClock *src);
- void release(SyncClock *dst) const;
- void acq_rel(SyncClock *dst);
- void ReleaseStore(SyncClock *dst) const;
+ void acquire(ClockCache *c, const SyncClock *src);
+ void release(ClockCache *c, SyncClock *dst) const;
+ void acq_rel(ClockCache *c, SyncClock *dst);
+ void ReleaseStore(ClockCache *c, SyncClock *dst) const;
void DebugReset();
void DebugDump(int(*printf)(const char *s, ...));
diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h
index 969d09f..0a356fb 100644
--- a/lib/tsan/rtl/tsan_defs.h
+++ b/lib/tsan/rtl/tsan_defs.h
@@ -43,7 +43,6 @@
const int kClkBits = 42;
const unsigned kMaxTidReuse = (1 << (64 - kClkBits)) - 1;
const uptr kShadowStackSize = 64 * 1024;
-const uptr kTraceStackSize = 256;
#ifdef TSAN_SHADOW_COUNT
# if TSAN_SHADOW_COUNT == 2 \
@@ -174,7 +173,6 @@
struct ReportStack;
class ReportDesc;
class RegionAlloc;
-class StackTrace;
// Descriptor of user's memory block.
struct MBlock {
diff --git a/lib/tsan/rtl/tsan_dense_alloc.h b/lib/tsan/rtl/tsan_dense_alloc.h
index 2c2e75e..a1cf84b 100644
--- a/lib/tsan/rtl/tsan_dense_alloc.h
+++ b/lib/tsan/rtl/tsan_dense_alloc.h
@@ -65,6 +65,7 @@
}
void Free(Cache *c, IndexT idx) {
+ DCHECK_NE(idx, 0);
if (c->pos == Cache::kSize)
Drain(c);
c->cache[c->pos++] = idx;
diff --git a/lib/tsan/rtl/tsan_fd.cc b/lib/tsan/rtl/tsan_fd.cc
index 68242e0..d18502f 100644
--- a/lib/tsan/rtl/tsan_fd.cc
+++ b/lib/tsan/rtl/tsan_fd.cc
@@ -48,7 +48,8 @@
}
static FdSync *allocsync(ThreadState *thr, uptr pc) {
- FdSync *s = (FdSync*)user_alloc(thr, pc, sizeof(FdSync));
+ FdSync *s = (FdSync*)user_alloc(thr, pc, sizeof(FdSync), kDefaultAlignment,
+ false);
atomic_store(&s->rc, 1, memory_order_relaxed);
return s;
}
@@ -65,7 +66,7 @@
CHECK_NE(s, &fdctx.globsync);
CHECK_NE(s, &fdctx.filesync);
CHECK_NE(s, &fdctx.socksync);
- user_free(thr, pc, s);
+ user_free(thr, pc, s, false);
}
}
}
@@ -78,13 +79,13 @@
if (l1 == 0) {
uptr size = kTableSizeL2 * sizeof(FdDesc);
// We need this to reside in user memory to properly catch races on it.
- void *p = user_alloc(thr, pc, size);
+ void *p = user_alloc(thr, pc, size, kDefaultAlignment, false);
internal_memset(p, 0, size);
MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size);
if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel))
l1 = (uptr)p;
else
- user_free(thr, pc, p);
+ user_free(thr, pc, p, false);
}
return &((FdDesc*)l1)[fd % kTableSizeL2]; // NOLINT
}
diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc
index 123df49..5dc331f 100644
--- a/lib/tsan/rtl/tsan_flags.cc
+++ b/lib/tsan/rtl/tsan_flags.cc
@@ -37,7 +37,6 @@
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", "");
@@ -45,8 +44,6 @@
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", "");
@@ -72,7 +69,6 @@
f->enable_annotations = true;
f->suppress_equal_stacks = true;
f->suppress_equal_addresses = true;
- f->suppress_java = false;
f->report_bugs = true;
f->report_thread_leaks = true;
f->report_destroy_locked = true;
@@ -80,8 +76,6 @@
f->report_signal_unsafe = true;
f->report_atomic_races = true;
f->force_seq_cst_atomics = false;
- f->suppressions = "";
- f->print_suppressions = false;
f->print_benign = false;
f->exitcode = 66;
f->halt_on_error = false;
@@ -99,20 +93,20 @@
// DDFlags
f->second_deadlock_stack = false;
- SetCommonFlagsDefaults(f);
+ CommonFlags *cf = common_flags();
+ SetCommonFlagsDefaults(cf);
// Override some common flags defaults.
- f->allow_addr2line = true;
- f->detect_deadlocks = true;
+ cf->allow_addr2line = true;
+ cf->detect_deadlocks = true;
+ cf->print_suppressions = false;
+ cf->stack_trace_format = " #%n %f %S %M";
// Let a frontend override.
ParseFlags(f, __tsan_default_options());
- ParseCommonFlagsFromString(f, __tsan_default_options());
+ ParseCommonFlagsFromString(cf, __tsan_default_options());
// Override from command line.
ParseFlags(f, env);
- ParseCommonFlagsFromString(f, env);
-
- // Copy back to common flags.
- *common_flags() = *f;
+ ParseCommonFlagsFromString(cf, env);
// Sanity check.
if (!f->report_bugs) {
@@ -121,7 +115,7 @@
f->report_signal_unsafe = false;
}
- if (f->help) PrintFlagDescriptions();
+ if (cf->help) PrintFlagDescriptions();
if (f->history_size < 0 || f->history_size > 7) {
Printf("ThreadSanitizer: incorrect value for history_size"
diff --git a/lib/tsan/rtl/tsan_flags.h b/lib/tsan/rtl/tsan_flags.h
index c6b4bbf..621ca13 100644
--- a/lib/tsan/rtl/tsan_flags.h
+++ b/lib/tsan/rtl/tsan_flags.h
@@ -19,7 +19,7 @@
namespace __tsan {
-struct Flags : CommonFlags, DDFlags {
+struct Flags : DDFlags {
// Enable dynamic annotations, otherwise they are no-ops.
bool enable_annotations;
// Suppress a race report if we've already output another race report
@@ -28,9 +28,6 @@
// Suppress a race report if we've already output another race report
// on the same address.
bool suppress_equal_addresses;
- // Suppress weird race reports that can be seen if JVM is embed
- // into the process.
- bool suppress_java;
// Turns off bug reporting entirely (useful for benchmarking).
bool report_bugs;
// Report thread leaks at exit?
@@ -47,10 +44,6 @@
// If set, all atomics are effectively sequentially consistent (seq_cst),
// regardless of what user actually specified.
bool force_seq_cst_atomics;
- // Suppressions filename.
- const char *suppressions;
- // Print matched suppressions at exit.
- bool print_suppressions;
// Print matched "benign" races at exit.
bool print_benign;
// Override exit status if something was reported.
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index 100834e..7889942 100644
--- a/lib/tsan/rtl/tsan_interceptors.cc
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -29,6 +29,16 @@
using namespace __tsan; // NOLINT
+#if SANITIZER_FREEBSD
+#define __errno_location __error
+#define __libc_malloc __malloc
+#define __libc_realloc __realloc
+#define __libc_calloc __calloc
+#define __libc_free __free
+#define stdout __stdoutp
+#define stderr __stderrp
+#endif
+
const int kSigCount = 65;
struct my_siginfo_t {
@@ -62,7 +72,9 @@
extern "C" void *__libc_calloc(uptr size, uptr n);
extern "C" void *__libc_realloc(void *ptr, uptr size);
extern "C" void __libc_free(void *ptr);
+#if !SANITIZER_FREEBSD
extern "C" int mallopt(int param, int value);
+#endif
extern __sanitizer_FILE *stdout, *stderr;
const int PTHREAD_MUTEX_RECURSIVE = 1;
const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
@@ -98,9 +110,14 @@
sighandler_t sa_handler;
void (*sa_sigaction)(int sig, my_siginfo_t *siginfo, void *uctx);
};
+#if SANITIZER_FREEBSD
+ int sa_flags;
+ __sanitizer_sigset_t sa_mask;
+#else
__sanitizer_sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)();
+#endif
};
const sighandler_t SIG_DFL = (sighandler_t)0;
@@ -124,9 +141,9 @@
};
struct SignalContext {
- int in_blocking_func;
int int_signal_send;
- int pending_signal_count;
+ atomic_uintptr_t in_blocking_func;
+ atomic_uintptr_t have_pending_signals;
SignalDesc pending_signals[kSigCount];
};
@@ -138,7 +155,7 @@
}
void InitializeLibIgnore() {
- libignore()->Init(*GetSuppressionContext());
+ libignore()->Init(*SuppressionContext::Get());
libignore()->OnLibraryLoaded(0);
}
@@ -146,7 +163,7 @@
static SignalContext *SigCtx(ThreadState *thr) {
SignalContext *ctx = (SignalContext*)thr->signal_ctx;
- if (ctx == 0 && thr->is_alive) {
+ if (ctx == 0 && !thr->is_dead) {
ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext");
MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx));
thr->signal_ctx = ctx;
@@ -199,7 +216,7 @@
ThreadState *thr = cur_thread(); \
const uptr caller_pc = GET_CALLER_PC(); \
ScopedInterceptor si(thr, #func, caller_pc); \
- const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
+ const uptr pc = StackTrace::GetCurrentPc(); \
(void)pc; \
/**/
@@ -215,28 +232,40 @@
#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
-#define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver)
+#if SANITIZER_FREEBSD
+# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
+#else
+# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver)
+#endif
#define BLOCK_REAL(name) (BlockingCall(thr), REAL(name))
struct BlockingCall {
explicit BlockingCall(ThreadState *thr)
- : ctx(SigCtx(thr)) {
- ctx->in_blocking_func++;
+ : thr(thr)
+ , ctx(SigCtx(thr)) {
+ for (;;) {
+ atomic_store(&ctx->in_blocking_func, 1, memory_order_relaxed);
+ if (atomic_load(&ctx->have_pending_signals, memory_order_relaxed) == 0)
+ break;
+ atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
+ ProcessPendingSignals(thr);
+ }
+ // When we are in a "blocking call", we process signals asynchronously
+ // (right when they arrive). In this context we do not expect to be
+ // executing any user/runtime code. The known interceptor sequence when
+ // this is not true is: pthread_join -> munmap(stack). It's fine
+ // to ignore munmap in this case -- we handle stack shadow separately.
+ thr->ignore_interceptors++;
}
~BlockingCall() {
- ctx->in_blocking_func--;
+ thr->ignore_interceptors--;
+ atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
}
+ ThreadState *thr;
SignalContext *ctx;
-
- // When we are in a "blocking call", we process signals asynchronously
- // (right when they arrive). In this context we do not expect to be
- // executing any user/runtime code. The known interceptor sequence when
- // this is not true is: pthread_join -> munmap(stack). It's fine
- // to ignore munmap in this case -- we handle stack shadow separately.
- ScopedIgnoreInterceptors ignore_interceptors;
};
TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) {
@@ -260,63 +289,24 @@
return res;
}
-class AtExitContext {
- public:
- AtExitContext()
- : mtx_(MutexTypeAtExit, StatMtxAtExit)
- , pos_() {
- }
-
- typedef void(*atexit_t)();
-
- int atexit(ThreadState *thr, uptr pc, bool is_on_exit,
- atexit_t f, void *arg) {
- Lock l(&mtx_);
- if (pos_ == kMaxAtExit)
- return 1;
- Release(thr, pc, (uptr)this);
- stack_[pos_] = f;
- args_[pos_] = arg;
- is_on_exits_[pos_] = is_on_exit;
- pos_++;
- return 0;
- }
-
- void exit(ThreadState *thr, uptr pc) {
- for (;;) {
- atexit_t f = 0;
- void *arg = 0;
- bool is_on_exit = false;
- {
- Lock l(&mtx_);
- if (pos_) {
- pos_--;
- f = stack_[pos_];
- arg = args_[pos_];
- is_on_exit = is_on_exits_[pos_];
- Acquire(thr, pc, (uptr)this);
- }
- }
- if (f == 0)
- break;
- DPrintf("#%d: executing atexit func %p\n", thr->tid, f);
- if (is_on_exit)
- ((void(*)(int status, void *arg))f)(0, arg);
- else
- ((void(*)(void *arg, void *dso))f)(arg, 0);
- }
- }
-
- private:
- static const int kMaxAtExit = 128;
- Mutex mtx_;
- atexit_t stack_[kMaxAtExit];
- void *args_[kMaxAtExit];
- bool is_on_exits_[kMaxAtExit];
- int pos_;
+// The sole reason tsan wraps atexit callbacks is to establish synchronization
+// between callback setup and callback execution.
+struct AtExitCtx {
+ void (*f)();
+ void *arg;
};
-static AtExitContext *atexit_ctx;
+static void at_exit_wrapper(void *arg) {
+ ThreadState *thr = cur_thread();
+ uptr pc = 0;
+ Acquire(thr, pc, (uptr)arg);
+ AtExitCtx *ctx = (AtExitCtx*)arg;
+ ((void(*)(void *arg))ctx->f)(ctx->arg);
+ __libc_free(ctx);
+}
+
+static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
+ void *arg, void *dso);
TSAN_INTERCEPTOR(int, atexit, void (*f)()) {
if (cur_thread()->in_symbolizer)
@@ -324,29 +314,53 @@
// We want to setup the atexit callback even if we are in ignored lib
// or after fork.
SCOPED_INTERCEPTOR_RAW(atexit, f);
- return atexit_ctx->atexit(thr, pc, false, (void(*)())f, 0);
-}
-
-TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) {
- if (cur_thread()->in_symbolizer)
- return 0;
- SCOPED_TSAN_INTERCEPTOR(on_exit, f, arg);
- return atexit_ctx->atexit(thr, pc, true, (void(*)())f, arg);
+ return setup_at_exit_wrapper(thr, pc, (void(*)())f, 0, 0);
}
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) {
- // Memory allocation in __cxa_atexit will race with free during exit,
- // because we do not see synchronization around atexit callback list.
- ThreadIgnoreBegin(thr, pc);
- int res = REAL(__cxa_atexit)(f, arg, dso);
- ThreadIgnoreEnd(thr, pc);
- return res;
- }
- return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg);
+ return setup_at_exit_wrapper(thr, pc, (void(*)())f, arg, dso);
+}
+
+static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
+ void *arg, void *dso) {
+ AtExitCtx *ctx = (AtExitCtx*)__libc_malloc(sizeof(AtExitCtx));
+ ctx->f = f;
+ ctx->arg = arg;
+ Release(thr, pc, (uptr)ctx);
+ // Memory allocation in __cxa_atexit will race with free during exit,
+ // because we do not see synchronization around atexit callback list.
+ ThreadIgnoreBegin(thr, pc);
+ int res = REAL(__cxa_atexit)(at_exit_wrapper, ctx, dso);
+ ThreadIgnoreEnd(thr, pc);
+ return res;
+}
+
+static void on_exit_wrapper(int status, void *arg) {
+ ThreadState *thr = cur_thread();
+ uptr pc = 0;
+ Acquire(thr, pc, (uptr)arg);
+ AtExitCtx *ctx = (AtExitCtx*)arg;
+ ((void(*)(int status, void *arg))ctx->f)(status, ctx->arg);
+ __libc_free(ctx);
+}
+
+TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) {
+ if (cur_thread()->in_symbolizer)
+ return 0;
+ SCOPED_TSAN_INTERCEPTOR(on_exit, f, arg);
+ AtExitCtx *ctx = (AtExitCtx*)__libc_malloc(sizeof(AtExitCtx));
+ ctx->f = (void(*)())f;
+ ctx->arg = arg;
+ Release(thr, pc, (uptr)ctx);
+ // Memory allocation in __cxa_atexit will race with free during exit,
+ // because we do not see synchronization around atexit callback list.
+ ThreadIgnoreBegin(thr, pc);
+ int res = REAL(on_exit)(on_exit_wrapper, ctx);
+ ThreadIgnoreEnd(thr, pc);
+ return res;
}
// Cleanup old bufs.
@@ -372,10 +386,21 @@
buf->sp = sp;
buf->mangled_sp = mangled_sp;
buf->shadow_stack_pos = thr->shadow_stack_pos;
+ SignalContext *sctx = SigCtx(thr);
+ buf->int_signal_send = sctx ? sctx->int_signal_send : 0;
+ buf->in_blocking_func = sctx ?
+ atomic_load(&sctx->in_blocking_func, memory_order_relaxed) :
+ false;
+ buf->in_signal_handler = atomic_load(&thr->in_signal_handler,
+ memory_order_relaxed);
}
static void LongJmp(ThreadState *thr, uptr *env) {
+#if SANITIZER_FREEBSD
+ uptr mangled_sp = env[2];
+#else
uptr mangled_sp = env[6];
+#endif // SANITIZER_FREEBSD
// Find the saved buf by mangled_sp.
for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) {
JmpBuf *buf = &thr->jmp_bufs[i];
@@ -384,6 +409,14 @@
// Unwind the stack.
while (thr->shadow_stack_pos > buf->shadow_stack_pos)
FuncExit(thr);
+ SignalContext *sctx = SigCtx(thr);
+ if (sctx) {
+ sctx->int_signal_send = buf->int_signal_send;
+ atomic_store(&sctx->in_blocking_func, buf->in_blocking_func,
+ memory_order_relaxed);
+ }
+ atomic_store(&thr->in_signal_handler, buf->in_signal_handler,
+ memory_order_relaxed);
JmpBufGarbageCollect(thr, buf->sp - 1); // do not collect buf->sp
return;
}
@@ -615,7 +648,8 @@
int res = 0;
uptr len = 0;
for (; len < n; len++) {
- if ((res = ((unsigned char*)s1)[len] - ((unsigned char*)s2)[len]))
+ if ((res = ((const unsigned char *)s1)[len] -
+ ((const unsigned char *)s2)[len]))
break;
}
MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false);
@@ -712,6 +746,7 @@
return res;
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot,
int flags, int fd, u64 off) {
SCOPED_TSAN_INTERCEPTOR(mmap64, addr, sz, prot, flags, fd, off);
@@ -725,6 +760,10 @@
}
return res;
}
+#define TSAN_MAYBE_INTERCEPT_MMAP64 TSAN_INTERCEPT(mmap64)
+#else
+#define TSAN_MAYBE_INTERCEPT_MMAP64
+#endif
TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz);
@@ -733,10 +772,15 @@
return res;
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
return user_alloc(thr, pc, sz, align);
}
+#define TSAN_MAYBE_INTERCEPT_MEMALIGN TSAN_INTERCEPT(memalign)
+#else
+#define TSAN_MAYBE_INTERCEPT_MEMALIGN
+#endif
TSAN_INTERCEPTOR(void*, aligned_alloc, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
@@ -748,11 +792,16 @@
return user_alloc(thr, pc, sz, GetPageSizeCached());
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
SCOPED_INTERCEPTOR_RAW(pvalloc, sz);
sz = RoundUp(sz, GetPageSizeCached());
return user_alloc(thr, pc, sz, GetPageSizeCached());
}
+#define TSAN_MAYBE_INTERCEPT_PVALLOC TSAN_INTERCEPT(pvalloc)
+#else
+#define TSAN_MAYBE_INTERCEPT_PVALLOC
+#endif
TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz);
@@ -824,11 +873,13 @@
ThreadState *thr = cur_thread();
// Thread-local state is not initialized yet.
ScopedIgnoreInterceptors ignore;
+ ThreadIgnoreBegin(thr, 0);
if (pthread_setspecific(g_thread_finalize_key,
(void *)kPthreadDestructorIterations)) {
Printf("ThreadSanitizer: failed to set thread key\n");
Die();
}
+ ThreadIgnoreEnd(thr, 0);
while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
pthread_yield();
atomic_store(&p->tid, 0, memory_order_release);
@@ -1297,73 +1348,135 @@
return res;
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf);
return REAL(__xstat)(version, path, buf);
}
+#define TSAN_MAYBE_INTERCEPT___XSTAT TSAN_INTERCEPT(__xstat)
+#else
+#define TSAN_MAYBE_INTERCEPT___XSTAT
+#endif
TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
+#if SANITIZER_FREEBSD
+ SCOPED_TSAN_INTERCEPTOR(stat, path, buf);
+ return REAL(stat)(path, buf);
+#else
SCOPED_TSAN_INTERCEPTOR(__xstat, 0, path, buf);
return REAL(__xstat)(0, path, buf);
+#endif
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf);
return REAL(__xstat64)(version, path, buf);
}
+#define TSAN_MAYBE_INTERCEPT___XSTAT64 TSAN_INTERCEPT(__xstat64)
+#else
+#define TSAN_MAYBE_INTERCEPT___XSTAT64
+#endif
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf);
return REAL(__xstat64)(0, path, buf);
}
+#define TSAN_MAYBE_INTERCEPT_STAT64 TSAN_INTERCEPT(stat64)
+#else
+#define TSAN_MAYBE_INTERCEPT_STAT64
+#endif
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf);
return REAL(__lxstat)(version, path, buf);
}
+#define TSAN_MAYBE_INTERCEPT___LXSTAT TSAN_INTERCEPT(__lxstat)
+#else
+#define TSAN_MAYBE_INTERCEPT___LXSTAT
+#endif
TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
+#if SANITIZER_FREEBSD
+ SCOPED_TSAN_INTERCEPTOR(lstat, path, buf);
+ return REAL(lstat)(path, buf);
+#else
SCOPED_TSAN_INTERCEPTOR(__lxstat, 0, path, buf);
return REAL(__lxstat)(0, path, buf);
+#endif
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf);
return REAL(__lxstat64)(version, path, buf);
}
+#define TSAN_MAYBE_INTERCEPT___LXSTAT64 TSAN_INTERCEPT(__lxstat64)
+#else
+#define TSAN_MAYBE_INTERCEPT___LXSTAT64
+#endif
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf);
return REAL(__lxstat64)(0, path, buf);
}
+#define TSAN_MAYBE_INTERCEPT_LSTAT64 TSAN_INTERCEPT(lstat64)
+#else
+#define TSAN_MAYBE_INTERCEPT_LSTAT64
+#endif
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(__fxstat)(version, fd, buf);
}
+#define TSAN_MAYBE_INTERCEPT___FXSTAT TSAN_INTERCEPT(__fxstat)
+#else
+#define TSAN_MAYBE_INTERCEPT___FXSTAT
+#endif
TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) {
+#if SANITIZER_FREEBSD
+ SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf);
+ if (fd > 0)
+ FdAccess(thr, pc, fd);
+ return REAL(fstat)(fd, buf);
+#else
SCOPED_TSAN_INTERCEPTOR(__fxstat, 0, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(__fxstat)(0, fd, buf);
+#endif
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(__fxstat64)(version, fd, buf);
}
+#define TSAN_MAYBE_INTERCEPT___FXSTAT64 TSAN_INTERCEPT(__fxstat64)
+#else
+#define TSAN_MAYBE_INTERCEPT___FXSTAT64
+#endif
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(__fxstat64)(0, fd, buf);
}
+#define TSAN_MAYBE_INTERCEPT_FSTAT64 TSAN_INTERCEPT(fstat64)
+#else
+#define TSAN_MAYBE_INTERCEPT_FSTAT64
+#endif
TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode);
@@ -1373,6 +1486,7 @@
return fd;
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode);
int fd = REAL(open64)(name, flags, mode);
@@ -1380,6 +1494,10 @@
FdFileCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_OPEN64 TSAN_INTERCEPT(open64)
+#else
+#define TSAN_MAYBE_INTERCEPT_OPEN64
+#endif
TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat, name, mode);
@@ -1389,6 +1507,7 @@
return fd;
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat64, name, mode);
int fd = REAL(creat64)(name, mode);
@@ -1396,6 +1515,10 @@
FdFileCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_CREAT64 TSAN_INTERCEPT(creat64)
+#else
+#define TSAN_MAYBE_INTERCEPT_CREAT64
+#endif
TSAN_INTERCEPTOR(int, dup, int oldfd) {
SCOPED_TSAN_INTERCEPTOR(dup, oldfd);
@@ -1421,6 +1544,7 @@
return newfd2;
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) {
SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags);
int fd = REAL(eventfd)(initval, flags);
@@ -1428,7 +1552,12 @@
FdEventCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_EVENTFD TSAN_INTERCEPT(eventfd)
+#else
+#define TSAN_MAYBE_INTERCEPT_EVENTFD
+#endif
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) {
SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags);
if (fd >= 0)
@@ -1438,7 +1567,12 @@
FdSignalCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_SIGNALFD TSAN_INTERCEPT(signalfd)
+#else
+#define TSAN_MAYBE_INTERCEPT_SIGNALFD
+#endif
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, inotify_init, int fake) {
SCOPED_TSAN_INTERCEPTOR(inotify_init, fake);
int fd = REAL(inotify_init)(fake);
@@ -1446,7 +1580,12 @@
FdInotifyCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT TSAN_INTERCEPT(inotify_init)
+#else
+#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT
+#endif
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, inotify_init1, int flags) {
SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags);
int fd = REAL(inotify_init1)(flags);
@@ -1454,6 +1593,10 @@
FdInotifyCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1 TSAN_INTERCEPT(inotify_init1)
+#else
+#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1
+#endif
TSAN_INTERCEPTOR(int, socket, int domain, int type, int protocol) {
SCOPED_TSAN_INTERCEPTOR(socket, domain, type, protocol);
@@ -1496,6 +1639,7 @@
return res;
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, epoll_create, int size) {
SCOPED_TSAN_INTERCEPTOR(epoll_create, size);
int fd = REAL(epoll_create)(size);
@@ -1503,7 +1647,12 @@
FdPollCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE TSAN_INTERCEPT(epoll_create)
+#else
+#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE
+#endif
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, epoll_create1, int flags) {
SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags);
int fd = REAL(epoll_create1)(flags);
@@ -1511,6 +1660,10 @@
FdPollCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE1 TSAN_INTERCEPT(epoll_create1)
+#else
+#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE1
+#endif
TSAN_INTERCEPTOR(int, close, int fd) {
SCOPED_TSAN_INTERCEPTOR(close, fd);
@@ -1519,14 +1672,20 @@
return REAL(close)(fd);
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __close, int fd) {
SCOPED_TSAN_INTERCEPTOR(__close, fd);
if (fd >= 0)
FdClose(thr, pc, fd);
return REAL(__close)(fd);
}
+#define TSAN_MAYBE_INTERCEPT___CLOSE TSAN_INTERCEPT(__close)
+#else
+#define TSAN_MAYBE_INTERCEPT___CLOSE
+#endif
// glibc guts
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) {
SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr);
int fds[64];
@@ -1537,6 +1696,10 @@
}
REAL(__res_iclose)(state, free_addr);
}
+#define TSAN_MAYBE_INTERCEPT___RES_ICLOSE TSAN_INTERCEPT(__res_iclose)
+#else
+#define TSAN_MAYBE_INTERCEPT___RES_ICLOSE
+#endif
TSAN_INTERCEPTOR(int, pipe, int *pipefd) {
SCOPED_TSAN_INTERCEPTOR(pipe, pipefd);
@@ -1603,6 +1766,7 @@
return res;
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(void*, tmpfile64, int fake) {
SCOPED_TSAN_INTERCEPTOR(tmpfile64, fake);
void *res = REAL(tmpfile64)(fake);
@@ -1613,6 +1777,10 @@
}
return res;
}
+#define TSAN_MAYBE_INTERCEPT_TMPFILE64 TSAN_INTERCEPT(tmpfile64)
+#else
+#define TSAN_MAYBE_INTERCEPT_TMPFILE64
+#endif
TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) {
// libc file streams can call user-supplied functions, see fopencookie.
@@ -1659,6 +1827,7 @@
return res;
}
+#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev);
if (epfd >= 0)
@@ -1670,7 +1839,12 @@
int res = REAL(epoll_ctl)(epfd, op, fd, ev);
return res;
}
+#define TSAN_MAYBE_INTERCEPT_EPOLL_CTL TSAN_INTERCEPT(epoll_ctl)
+#else
+#define TSAN_MAYBE_INTERCEPT_EPOLL_CTL
+#endif
+#if !SANITIZER_FREEBSD
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)
@@ -1680,11 +1854,17 @@
FdAcquire(thr, pc, epfd);
return res;
}
+#define TSAN_MAYBE_INTERCEPT_EPOLL_WAIT TSAN_INTERCEPT(epoll_wait)
+#else
+#define TSAN_MAYBE_INTERCEPT_EPOLL_WAIT
+#endif
namespace __tsan {
-static void CallUserSignalHandler(ThreadState *thr, bool sync, bool sigact,
- int sig, my_siginfo_t *info, void *uctx) {
+static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,
+ bool sigact, int sig, my_siginfo_t *info, void *uctx) {
+ if (acquire)
+ Acquire(thr, 0, (uptr)&sigactions[sig]);
// Ensure that the handler does not spoil errno.
const int saved_errno = errno;
errno = 99;
@@ -1705,12 +1885,12 @@
// from rtl_generic_sighandler) we have not yet received the reraised
// signal; and it looks too fragile to intercept all ways to reraise a signal.
if (flags()->report_bugs && !sync && sig != SIGTERM && errno != 99) {
- __tsan::StackTrace stack;
- stack.ObtainCurrent(thr, pc);
+ VarSizeStackTrace stack;
+ ObtainCurrentStack(thr, pc, &stack);
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(ReportTypeErrnoInSignal);
if (!IsFiredSuppression(ctx, rep, stack)) {
- rep.AddStack(&stack, true);
+ rep.AddStack(stack, true);
OutputReport(thr, rep);
}
}
@@ -1719,10 +1899,11 @@
void ProcessPendingSignals(ThreadState *thr) {
SignalContext *sctx = SigCtx(thr);
- if (sctx == 0 || sctx->pending_signal_count == 0 || thr->in_signal_handler)
+ if (sctx == 0 ||
+ atomic_load(&sctx->have_pending_signals, memory_order_relaxed) == 0)
return;
- thr->in_signal_handler = true;
- sctx->pending_signal_count = 0;
+ atomic_store(&sctx->have_pending_signals, 0, memory_order_relaxed);
+ atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed);
// These are too big for stack.
static THREADLOCAL __sanitizer_sigset_t emptyset, oldset;
REAL(sigfillset)(&emptyset);
@@ -1733,14 +1914,13 @@
signal->armed = false;
if (sigactions[sig].sa_handler != SIG_DFL
&& sigactions[sig].sa_handler != SIG_IGN) {
- CallUserSignalHandler(thr, false, signal->sigaction,
+ CallUserSignalHandler(thr, false, true, signal->sigaction,
sig, &signal->siginfo, &signal->ctx);
}
}
}
pthread_sigmask(SIG_SETMASK, &oldset, 0);
- CHECK_EQ(thr->in_signal_handler, true);
- thr->in_signal_handler = false;
+ atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed);
}
} // namespace __tsan
@@ -1766,21 +1946,27 @@
// If we are in blocking function, we can safely process it now
// (but check if we are in a recursive interceptor,
// i.e. pthread_join()->munmap()).
- (sctx && sctx->in_blocking_func == 1)) {
- CHECK_EQ(thr->in_signal_handler, false);
- thr->in_signal_handler = true;
- if (sctx && sctx->in_blocking_func == 1) {
+ (sctx && atomic_load(&sctx->in_blocking_func, memory_order_relaxed))) {
+ atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed);
+ if (sctx && atomic_load(&sctx->in_blocking_func, memory_order_relaxed)) {
// We ignore interceptors in blocking functions,
// temporary enbled them again while we are calling user function.
int const i = thr->ignore_interceptors;
thr->ignore_interceptors = 0;
- CallUserSignalHandler(thr, sync, sigact, sig, info, ctx);
+ atomic_store(&sctx->in_blocking_func, 0, memory_order_relaxed);
+ CallUserSignalHandler(thr, sync, true, sigact, sig, info, ctx);
thr->ignore_interceptors = i;
+ atomic_store(&sctx->in_blocking_func, 1, memory_order_relaxed);
} else {
- CallUserSignalHandler(thr, sync, sigact, sig, info, ctx);
+ // Be very conservative with when we do acquire in this case.
+ // It's unsafe to do acquire in async handlers, because ThreadState
+ // can be in inconsistent state.
+ // SIGSYS looks relatively safe -- it's synchronous and can actually
+ // need some global state.
+ bool acq = (sig == SIGSYS);
+ CallUserSignalHandler(thr, sync, acq, sigact, sig, info, ctx);
}
- CHECK_EQ(thr->in_signal_handler, true);
- thr->in_signal_handler = false;
+ atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed);
return;
}
@@ -1794,7 +1980,7 @@
internal_memcpy(&signal->siginfo, info, sizeof(*info));
if (ctx)
internal_memcpy(&signal->ctx, ctx, sizeof(signal->ctx));
- sctx->pending_signal_count++;
+ atomic_store(&sctx->have_pending_signals, 1, memory_order_relaxed);
}
}
@@ -1822,6 +2008,7 @@
else
newact.sa_handler = rtl_sighandler;
}
+ ReleaseStore(thr, pc, (uptr)&sigactions[sig]);
int res = REAL(sigaction)(sig, &newact, 0);
return res;
}
@@ -1905,35 +2092,6 @@
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;
- VPrintf(1, "INFO: ThreadSanitizer ignores mlock/munlock[all]\n");
-}
-
-TSAN_INTERCEPTOR(int, mlock, const void *addr, uptr len) {
- MlockIsUnsupported();
- return 0;
-}
-
-TSAN_INTERCEPTOR(int, munlock, const void *addr, uptr len) {
- MlockIsUnsupported();
- return 0;
-}
-
-TSAN_INTERCEPTOR(int, mlockall, int flags) {
- MlockIsUnsupported();
- return 0;
-}
-
-TSAN_INTERCEPTOR(int, munlockall, void) {
- MlockIsUnsupported();
- return 0;
-}
-
TSAN_INTERCEPTOR(int, fork, int fake) {
if (cur_thread()->in_symbolizer)
return REAL(fork)(fake);
@@ -1994,6 +2152,18 @@
#include "sanitizer_common/sanitizer_platform_interceptors.h"
// Causes interceptor recursion (getaddrinfo() and fopen())
#undef SANITIZER_INTERCEPT_GETADDRINFO
+// There interceptors do not seem to be strictly necessary for tsan.
+// But we see cases where the interceptors consume 70% of execution time.
+// Memory blocks passed to fgetgrent_r are "written to" by tsan several times.
+// First, there is some recursion (getgrnam_r calls fgetgrent_r), and each
+// function "writes to" the buffer. Then, the same memory is "written to"
+// twice, first as buf and then as pwbufp (both of them refer to the same
+// addresses).
+#undef SANITIZER_INTERCEPT_GETPWENT
+#undef SANITIZER_INTERCEPT_GETPWENT_R
+#undef SANITIZER_INTERCEPT_FGETPWENT
+#undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS
+#undef SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
@@ -2195,8 +2365,6 @@
static void finalize(void *arg) {
ThreadState *thr = cur_thread();
- uptr pc = 0;
- atexit_ctx->exit(thr, pc);
int status = Finalize(thr);
// Make sure the output is not lost.
// Flushing all the streams here may freeze the process if a child thread is
@@ -2219,8 +2387,10 @@
REAL(memcmp) = internal_memcmp;
// Instruct libc malloc to consume less memory.
+#if !SANITIZER_FREEBSD
mallopt(1, 0); // M_MXFAST
mallopt(-3, 32*1024); // M_MMAP_THRESHOLD
+#endif
InitializeCommonInterceptors();
@@ -2242,11 +2412,11 @@
TSAN_INTERCEPT(free);
TSAN_INTERCEPT(cfree);
TSAN_INTERCEPT(mmap);
- TSAN_INTERCEPT(mmap64);
+ TSAN_MAYBE_INTERCEPT_MMAP64;
TSAN_INTERCEPT(munmap);
- TSAN_INTERCEPT(memalign);
+ TSAN_MAYBE_INTERCEPT_MEMALIGN;
TSAN_INTERCEPT(valloc);
- TSAN_INTERCEPT(pvalloc);
+ TSAN_MAYBE_INTERCEPT_PVALLOC;
TSAN_INTERCEPT(posix_memalign);
TSAN_INTERCEPT(strlen);
@@ -2309,38 +2479,38 @@
TSAN_INTERCEPT(sem_getvalue);
TSAN_INTERCEPT(stat);
- TSAN_INTERCEPT(__xstat);
- TSAN_INTERCEPT(stat64);
- TSAN_INTERCEPT(__xstat64);
+ TSAN_MAYBE_INTERCEPT___XSTAT;
+ TSAN_MAYBE_INTERCEPT_STAT64;
+ TSAN_MAYBE_INTERCEPT___XSTAT64;
TSAN_INTERCEPT(lstat);
- TSAN_INTERCEPT(__lxstat);
- TSAN_INTERCEPT(lstat64);
- TSAN_INTERCEPT(__lxstat64);
+ TSAN_MAYBE_INTERCEPT___LXSTAT;
+ TSAN_MAYBE_INTERCEPT_LSTAT64;
+ TSAN_MAYBE_INTERCEPT___LXSTAT64;
TSAN_INTERCEPT(fstat);
- TSAN_INTERCEPT(__fxstat);
- TSAN_INTERCEPT(fstat64);
- TSAN_INTERCEPT(__fxstat64);
+ TSAN_MAYBE_INTERCEPT___FXSTAT;
+ TSAN_MAYBE_INTERCEPT_FSTAT64;
+ TSAN_MAYBE_INTERCEPT___FXSTAT64;
TSAN_INTERCEPT(open);
- TSAN_INTERCEPT(open64);
+ TSAN_MAYBE_INTERCEPT_OPEN64;
TSAN_INTERCEPT(creat);
- TSAN_INTERCEPT(creat64);
+ TSAN_MAYBE_INTERCEPT_CREAT64;
TSAN_INTERCEPT(dup);
TSAN_INTERCEPT(dup2);
TSAN_INTERCEPT(dup3);
- TSAN_INTERCEPT(eventfd);
- TSAN_INTERCEPT(signalfd);
- TSAN_INTERCEPT(inotify_init);
- TSAN_INTERCEPT(inotify_init1);
+ TSAN_MAYBE_INTERCEPT_EVENTFD;
+ TSAN_MAYBE_INTERCEPT_SIGNALFD;
+ TSAN_MAYBE_INTERCEPT_INOTIFY_INIT;
+ TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1;
TSAN_INTERCEPT(socket);
TSAN_INTERCEPT(socketpair);
TSAN_INTERCEPT(connect);
TSAN_INTERCEPT(bind);
TSAN_INTERCEPT(listen);
- TSAN_INTERCEPT(epoll_create);
- TSAN_INTERCEPT(epoll_create1);
+ TSAN_MAYBE_INTERCEPT_EPOLL_CREATE;
+ TSAN_MAYBE_INTERCEPT_EPOLL_CREATE1;
TSAN_INTERCEPT(close);
- TSAN_INTERCEPT(__close);
- TSAN_INTERCEPT(__res_iclose);
+ TSAN_MAYBE_INTERCEPT___CLOSE;
+ TSAN_MAYBE_INTERCEPT___RES_ICLOSE;
TSAN_INTERCEPT(pipe);
TSAN_INTERCEPT(pipe2);
@@ -2350,7 +2520,7 @@
TSAN_INTERCEPT(unlink);
TSAN_INTERCEPT(tmpfile);
- TSAN_INTERCEPT(tmpfile64);
+ TSAN_MAYBE_INTERCEPT_TMPFILE64;
TSAN_INTERCEPT(fread);
TSAN_INTERCEPT(fwrite);
TSAN_INTERCEPT(abort);
@@ -2358,8 +2528,8 @@
TSAN_INTERCEPT(rmdir);
TSAN_INTERCEPT(opendir);
- TSAN_INTERCEPT(epoll_ctl);
- TSAN_INTERCEPT(epoll_wait);
+ TSAN_MAYBE_INTERCEPT_EPOLL_CTL;
+ TSAN_MAYBE_INTERCEPT_EPOLL_WAIT;
TSAN_INTERCEPT(sigaction);
TSAN_INTERCEPT(signal);
@@ -2373,11 +2543,6 @@
TSAN_INTERCEPT(gettimeofday);
TSAN_INTERCEPT(getaddrinfo);
- TSAN_INTERCEPT(mlock);
- TSAN_INTERCEPT(munlock);
- TSAN_INTERCEPT(mlockall);
- TSAN_INTERCEPT(munlockall);
-
TSAN_INTERCEPT(fork);
TSAN_INTERCEPT(vfork);
TSAN_INTERCEPT(on_exit);
@@ -2387,9 +2552,6 @@
// 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.
REAL(atexit) = (int(*)(void(*)()))unreachable;
- atexit_ctx = new(internal_alloc(MBlockAtExit, sizeof(AtExitContext)))
- AtExitContext();
-
if (REAL(__cxa_atexit)(&finalize, 0, 0)) {
Printf("ThreadSanitizer: failed to setup atexit callback\n");
Die();
diff --git a/lib/tsan/rtl/tsan_interface_ann.cc b/lib/tsan/rtl/tsan_interface_ann.cc
index a1725cb..fd3c846 100644
--- a/lib/tsan/rtl/tsan_interface_ann.cc
+++ b/lib/tsan/rtl/tsan_interface_ann.cc
@@ -54,7 +54,7 @@
StatInc(thr, StatAnnotation); \
StatInc(thr, Stat##typ); \
ScopedAnnotation sa(thr, __func__, f, l, caller_pc); \
- const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
+ const uptr pc = StackTrace::GetCurrentPc(); \
(void)pc; \
/**/
@@ -126,8 +126,6 @@
static bool CheckContains(ExpectRace *list, uptr addr, uptr size) {
ExpectRace *race = FindRace(list, addr, size);
- if (race == 0 && AlternativeAddress(addr))
- race = FindRace(list, AlternativeAddress(addr), size);
if (race == 0)
return false;
DPrintf("Hit expected/benign race: %s addr=%zx:%d %s:%d\n",
@@ -456,4 +454,6 @@
void INTERFACE_ATTRIBUTE
AnnotateMemoryIsInitialized(char *f, int l, uptr mem, uptr sz) {}
+void INTERFACE_ATTRIBUTE
+AnnotateMemoryIsUninitialized(char *f, int l, uptr mem, uptr sz) {}
} // extern "C"
diff --git a/lib/tsan/rtl/tsan_interface_atomic.cc b/lib/tsan/rtl/tsan_interface_atomic.cc
index 7fbc9c6..ceb32bd 100644
--- a/lib/tsan/rtl/tsan_interface_atomic.cc
+++ b/lib/tsan/rtl/tsan_interface_atomic.cc
@@ -27,33 +27,23 @@
using namespace __tsan; // NOLINT
-#define SCOPED_ATOMIC(func, ...) \
- const uptr callpc = (uptr)__builtin_return_address(0); \
- uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
- mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \
- ThreadState *const thr = cur_thread(); \
- if (thr->ignore_interceptors) \
- return NoTsanAtomic##func(__VA_ARGS__); \
- AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \
- ScopedAtomic sa(thr, callpc, a, mo, __func__); \
- return Atomic##func(thr, pc, __VA_ARGS__); \
-/**/
-
// These should match declarations from public tsan_interface_atomic.h header.
typedef unsigned char a8;
typedef unsigned short a16; // NOLINT
typedef unsigned int a32;
typedef unsigned long long a64; // NOLINT
-#if defined(__SIZEOF_INT128__) \
- || (__clang_major__ * 100 + __clang_minor__ >= 302)
+#if !defined(TSAN_GO) && (defined(__SIZEOF_INT128__) \
+ || (__clang_major__ * 100 + __clang_minor__ >= 302))
__extension__ typedef __int128 a128;
# define __TSAN_HAS_INT128 1
#else
# define __TSAN_HAS_INT128 0
#endif
+#ifndef TSAN_GO
// Protects emulation of 128-bit atomic operations.
static StaticSpinMutex mutex128;
+#endif
// Part of ABI, do not change.
// http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/atomic?view=markup
@@ -66,38 +56,6 @@
mo_seq_cst
} morder;
-class ScopedAtomic {
- public:
- ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a,
- morder mo, const char *func)
- : thr_(thr) {
- FuncEntry(thr_, pc);
- DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo);
- }
- ~ScopedAtomic() {
- ProcessPendingSignals(thr_);
- FuncExit(thr_);
- }
- private:
- ThreadState *thr_;
-};
-
-static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) {
- StatInc(thr, StatAtomic);
- StatInc(thr, t);
- StatInc(thr, size == 1 ? StatAtomic1
- : size == 2 ? StatAtomic2
- : size == 4 ? StatAtomic4
- : size == 8 ? StatAtomic8
- : StatAtomic16);
- StatInc(thr, mo == mo_relaxed ? StatAtomicRelaxed
- : mo == mo_consume ? StatAtomicConsume
- : mo == mo_acquire ? StatAtomicAcquire
- : mo == mo_release ? StatAtomicRelease
- : mo == mo_acq_rel ? StatAtomicAcq_Rel
- : StatAtomicSeq_Cst);
-}
-
static bool IsLoadOrder(morder mo) {
return mo == mo_relaxed || mo == mo_consume
|| mo == mo_acquire || mo == mo_seq_cst;
@@ -167,7 +125,7 @@
// Atomic ops are executed under tsan internal mutex,
// here we assume that the atomic variables are not accessed
// from non-instrumented code.
-#ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16
+#if !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) && !defined(TSAN_GO)
a128 func_xchg(volatile a128 *v, a128 op) {
SpinMutexLock lock(&mutex128);
a128 cmp = *v;
@@ -240,20 +198,22 @@
// this leads to false negatives only in very obscure cases.
}
+#ifndef TSAN_GO
static atomic_uint8_t *to_atomic(const volatile a8 *a) {
- return (atomic_uint8_t*)a;
+ return reinterpret_cast<atomic_uint8_t *>(const_cast<a8 *>(a));
}
static atomic_uint16_t *to_atomic(const volatile a16 *a) {
- return (atomic_uint16_t*)a;
+ return reinterpret_cast<atomic_uint16_t *>(const_cast<a16 *>(a));
}
+#endif
static atomic_uint32_t *to_atomic(const volatile a32 *a) {
- return (atomic_uint32_t*)a;
+ return reinterpret_cast<atomic_uint32_t *>(const_cast<a32 *>(a));
}
static atomic_uint64_t *to_atomic(const volatile a64 *a) {
- return (atomic_uint64_t*)a;
+ return reinterpret_cast<atomic_uint64_t *>(const_cast<a64 *>(a));
}
static memory_order to_mo(morder mo) {
@@ -274,7 +234,7 @@
return atomic_load(to_atomic(a), to_mo(mo));
}
-#if __TSAN_HAS_INT128
+#if __TSAN_HAS_INT128 && !defined(TSAN_GO)
static a128 NoTsanAtomicLoad(const volatile a128 *a, morder mo) {
SpinMutexLock lock(&mutex128);
return *a;
@@ -304,7 +264,7 @@
atomic_store(to_atomic(a), v, to_mo(mo));
}
-#if __TSAN_HAS_INT128
+#if __TSAN_HAS_INT128 && !defined(TSAN_GO)
static void NoTsanAtomicStore(volatile a128 *a, a128 v, morder mo) {
SpinMutexLock lock(&mutex128);
*a = v;
@@ -451,8 +411,9 @@
#endif
template<typename T>
-static bool NoTsanAtomicCAS(volatile T *a, T c, T v, morder mo, morder fmo) {
- return NoTsanAtomicCAS(a, &c, v, mo, fmo);
+static T NoTsanAtomicCAS(volatile T *a, T c, T v, morder mo, morder fmo) {
+ NoTsanAtomicCAS(a, &c, v, mo, fmo);
+ return c;
}
template<typename T>
@@ -495,6 +456,7 @@
return c;
}
+#ifndef TSAN_GO
static void NoTsanAtomicFence(morder mo) {
__sync_synchronize();
}
@@ -503,6 +465,56 @@
// FIXME(dvyukov): not implemented.
__sync_synchronize();
}
+#endif
+
+// Interface functions follow.
+#ifndef TSAN_GO
+
+// C/C++
+
+#define SCOPED_ATOMIC(func, ...) \
+ const uptr callpc = (uptr)__builtin_return_address(0); \
+ uptr pc = StackTrace::GetCurrentPc(); \
+ mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \
+ ThreadState *const thr = cur_thread(); \
+ if (thr->ignore_interceptors) \
+ return NoTsanAtomic##func(__VA_ARGS__); \
+ AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \
+ ScopedAtomic sa(thr, callpc, a, mo, __func__); \
+ return Atomic##func(thr, pc, __VA_ARGS__); \
+/**/
+
+class ScopedAtomic {
+ public:
+ ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a,
+ morder mo, const char *func)
+ : thr_(thr) {
+ FuncEntry(thr_, pc);
+ DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo);
+ }
+ ~ScopedAtomic() {
+ ProcessPendingSignals(thr_);
+ FuncExit(thr_);
+ }
+ private:
+ ThreadState *thr_;
+};
+
+static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) {
+ StatInc(thr, StatAtomic);
+ StatInc(thr, t);
+ StatInc(thr, size == 1 ? StatAtomic1
+ : size == 2 ? StatAtomic2
+ : size == 4 ? StatAtomic4
+ : size == 8 ? StatAtomic8
+ : StatAtomic16);
+ StatInc(thr, mo == mo_relaxed ? StatAtomicRelaxed
+ : mo == mo_consume ? StatAtomicConsume
+ : mo == mo_acquire ? StatAtomicAcquire
+ : mo == mo_release ? StatAtomicRelease
+ : mo == mo_acq_rel ? StatAtomicAcq_Rel
+ : StatAtomicSeq_Cst);
+}
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
@@ -854,3 +866,88 @@
void __tsan_atomic_signal_fence(morder mo) {
}
} // extern "C"
+
+#else // #ifndef TSAN_GO
+
+// Go
+
+#define ATOMIC(func, ...) \
+ if (thr->ignore_sync) { \
+ NoTsanAtomic##func(__VA_ARGS__); \
+ } else { \
+ FuncEntry(thr, cpc); \
+ Atomic##func(thr, pc, __VA_ARGS__); \
+ FuncExit(thr); \
+ } \
+/**/
+
+#define ATOMIC_RET(func, ret, ...) \
+ if (thr->ignore_sync) { \
+ (ret) = NoTsanAtomic##func(__VA_ARGS__); \
+ } else { \
+ FuncEntry(thr, cpc); \
+ (ret) = Atomic##func(thr, pc, __VA_ARGS__); \
+ FuncExit(thr); \
+ } \
+/**/
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(Load, *(a32*)(a+8), *(a32**)a, mo_acquire);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(Load, *(a64*)(a+8), *(a64**)a, mo_acquire);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC(Store, *(a32**)a, *(a32*)(a+8), mo_release);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC(Store, *(a64**)a, *(a64*)(a+8), mo_release);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_fetch_add(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(FetchAdd, *(a32*)(a+16), *(a32**)a, *(a32*)(a+8), mo_acq_rel);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_fetch_add(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(FetchAdd, *(a64*)(a+16), *(a64**)a, *(a64*)(a+8), mo_acq_rel);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(Exchange, *(a32*)(a+16), *(a32**)a, *(a32*)(a+8), mo_acq_rel);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(Exchange, *(a64*)(a+16), *(a64**)a, *(a64*)(a+8), mo_acq_rel);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_compare_exchange(
+ ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ a32 cur = 0;
+ a32 cmp = *(a32*)(a+8);
+ ATOMIC_RET(CAS, cur, *(a32**)a, cmp, *(a32*)(a+12), mo_acq_rel, mo_acquire);
+ *(bool*)(a+16) = (cur == cmp);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_compare_exchange(
+ ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ a64 cur = 0;
+ a64 cmp = *(a64*)(a+8);
+ ATOMIC_RET(CAS, cur, *(a64**)a, cmp, *(a64*)(a+16), mo_acq_rel, mo_acquire);
+ *(bool*)(a+24) = (cur == cmp);
+}
+} // extern "C"
+#endif // #ifndef TSAN_GO
diff --git a/lib/tsan/rtl/tsan_interface_java.cc b/lib/tsan/rtl/tsan_interface_java.cc
index 5dfb476..8615349 100644
--- a/lib/tsan/rtl/tsan_interface_java.cc
+++ b/lib/tsan/rtl/tsan_interface_java.cc
@@ -61,7 +61,7 @@
#define SCOPED_JAVA_FUNC(func) \
ThreadState *thr = cur_thread(); \
const uptr caller_pc = GET_CALLER_PC(); \
- const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
+ const uptr pc = StackTrace::GetCurrentPc(); \
(void)pc; \
ScopedJavaFunc scoped(thr, caller_pc); \
/**/
diff --git a/lib/tsan/rtl/tsan_md5.cc b/lib/tsan/rtl/tsan_md5.cc
index 66e8240..51279c1 100644
--- a/lib/tsan/rtl/tsan_md5.cc
+++ b/lib/tsan/rtl/tsan_md5.cc
@@ -25,7 +25,7 @@
(a) += (b);
#define SET(n) \
- (*(MD5_u32plus *)&ptr[(n) * 4])
+ (*(const MD5_u32plus *)&ptr[(n) * 4])
#define GET(n) \
SET(n)
@@ -39,13 +39,11 @@
MD5_u32plus block[16];
} MD5_CTX;
-static void *body(MD5_CTX *ctx, void *data, ulong_t size) {
- unsigned char *ptr;
+static const void *body(MD5_CTX *ctx, const void *data, ulong_t size) {
+ const unsigned char *ptr = (const unsigned char *)data;
MD5_u32plus a, b, c, d;
MD5_u32plus saved_a, saved_b, saved_c, saved_d;
- ptr = (unsigned char*)data;
-
a = ctx->a;
b = ctx->b;
c = ctx->c;
@@ -151,7 +149,7 @@
ctx->hi = 0;
}
-void MD5_Update(MD5_CTX *ctx, void *data, ulong_t size) {
+void MD5_Update(MD5_CTX *ctx, const void *data, ulong_t size) {
MD5_u32plus saved_lo;
ulong_t used, free;
@@ -171,7 +169,7 @@
}
internal_memcpy(&ctx->buffer[used], data, free);
- data = (unsigned char *)data + free;
+ data = (const unsigned char *)data + free;
size -= free;
body(ctx, ctx->buffer, 64);
}
@@ -238,7 +236,7 @@
MD5Hash res;
MD5_CTX ctx;
MD5_Init(&ctx);
- MD5_Update(&ctx, (void*)data, size);
+ MD5_Update(&ctx, data, size);
MD5_Final((unsigned char*)&res.hash[0], &ctx);
return res;
}
diff --git a/lib/tsan/rtl/tsan_mman.cc b/lib/tsan/rtl/tsan_mman.cc
index 8542a8f..285bdb3 100644
--- a/lib/tsan/rtl/tsan_mman.cc
+++ b/lib/tsan/rtl/tsan_mman.cc
@@ -19,18 +19,11 @@
#include "tsan_flags.h"
// May be overriden by front-end.
-extern "C" void WEAK __tsan_malloc_hook(void *ptr, uptr size) {
- (void)ptr;
- (void)size;
-}
extern "C" void WEAK __sanitizer_malloc_hook(void *ptr, uptr size) {
(void)ptr;
(void)size;
}
-extern "C" void WEAK __tsan_free_hook(void *ptr) {
- (void)ptr;
-}
extern "C" void WEAK __sanitizer_free_hook(void *ptr) {
(void)ptr;
}
@@ -70,19 +63,20 @@
}
static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
- if (!thr->in_signal_handler || !flags()->report_signal_unsafe)
+ if (atomic_load(&thr->in_signal_handler, memory_order_relaxed) == 0 ||
+ !flags()->report_signal_unsafe)
return;
- StackTrace stack;
- stack.ObtainCurrent(thr, pc);
+ VarSizeStackTrace stack;
+ ObtainCurrentStack(thr, pc, &stack);
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(ReportTypeSignalUnsafe);
if (!IsFiredSuppression(ctx, rep, stack)) {
- rep.AddStack(&stack, true);
+ rep.AddStack(stack, true);
OutputReport(thr, rep);
}
}
-void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
+void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align, bool signal) {
if ((sz >= (1ull << 40)) || (align >= (1ull << 40)))
return AllocatorReturnNull();
void *p = allocator()->Allocate(&thr->alloc_cache, sz, align);
@@ -90,15 +84,17 @@
return 0;
if (ctx && ctx->initialized)
OnUserAlloc(thr, pc, (uptr)p, sz, true);
- SignalUnsafeCall(thr, pc);
+ if (signal)
+ SignalUnsafeCall(thr, pc);
return p;
}
-void user_free(ThreadState *thr, uptr pc, void *p) {
+void user_free(ThreadState *thr, uptr pc, void *p, bool signal) {
if (ctx && ctx->initialized)
OnUserFree(thr, pc, (uptr)p, true);
allocator()->Deallocate(&thr->alloc_cache, p);
- SignalUnsafeCall(thr, pc);
+ if (signal)
+ SignalUnsafeCall(thr, pc);
}
void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) {
@@ -147,7 +143,6 @@
ThreadState *thr = cur_thread();
if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
return;
- __tsan_malloc_hook(ptr, size);
__sanitizer_malloc_hook(ptr, size);
}
@@ -155,13 +150,11 @@
ThreadState *thr = cur_thread();
if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
return;
- __tsan_free_hook(ptr);
__sanitizer_free_hook(ptr);
}
void *internal_alloc(MBlockType typ, uptr sz) {
ThreadState *thr = cur_thread();
- CHECK_LE(sz, InternalSizeClassMap::kMaxSize);
if (thr->nomalloc) {
thr->nomalloc = 0; // CHECK calls internal_malloc().
CHECK(0);
@@ -188,53 +181,32 @@
allocator()->GetStats(stats);
return stats[AllocatorStatAllocated];
}
-uptr __tsan_get_current_allocated_bytes() {
- return __sanitizer_get_current_allocated_bytes();
-}
uptr __sanitizer_get_heap_size() {
uptr stats[AllocatorStatCount];
allocator()->GetStats(stats);
return stats[AllocatorStatMapped];
}
-uptr __tsan_get_heap_size() {
- return __sanitizer_get_heap_size();
-}
uptr __sanitizer_get_free_bytes() {
return 1;
}
-uptr __tsan_get_free_bytes() {
- return __sanitizer_get_free_bytes();
-}
uptr __sanitizer_get_unmapped_bytes() {
return 1;
}
-uptr __tsan_get_unmapped_bytes() {
- return __sanitizer_get_unmapped_bytes();
-}
uptr __sanitizer_get_estimated_allocated_size(uptr size) {
return size;
}
-uptr __tsan_get_estimated_allocated_size(uptr size) {
- return __sanitizer_get_estimated_allocated_size(size);
-}
int __sanitizer_get_ownership(const void *p) {
return allocator()->GetBlockBegin(p) != 0;
}
-int __tsan_get_ownership(const void *p) {
- return __sanitizer_get_ownership(p);
-}
uptr __sanitizer_get_allocated_size(const void *p) {
return user_alloc_usable_size(p);
}
-uptr __tsan_get_allocated_size(const void *p) {
- return __sanitizer_get_allocated_size(p);
-}
void __tsan_on_thread_idle() {
ThreadState *thr = cur_thread();
diff --git a/lib/tsan/rtl/tsan_mman.h b/lib/tsan/rtl/tsan_mman.h
index 4f87ad6..7d41fa8 100644
--- a/lib/tsan/rtl/tsan_mman.h
+++ b/lib/tsan/rtl/tsan_mman.h
@@ -26,9 +26,9 @@
// For user allocations.
void *user_alloc(ThreadState *thr, uptr pc, uptr sz,
- uptr align = kDefaultAlignment);
+ uptr align = kDefaultAlignment, bool signal = true);
// Does not accept NULL.
-void user_free(ThreadState *thr, uptr pc, void *p);
+void user_free(ThreadState *thr, uptr pc, void *p, bool signal = true);
void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz);
void *user_alloc_aligned(ThreadState *thr, uptr pc, uptr sz, uptr align);
uptr user_alloc_usable_size(const void *p);
diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h
index 7d8d977..45f8631 100644
--- a/lib/tsan/rtl/tsan_platform.h
+++ b/lib/tsan/rtl/tsan_platform.h
@@ -12,162 +12,223 @@
// Platform-specific code.
//===----------------------------------------------------------------------===//
-/*
-C++ linux memory layout:
-0000 0000 0000 - 03c0 0000 0000: protected
-03c0 0000 0000 - 1000 0000 0000: shadow
-1000 0000 0000 - 3000 0000 0000: protected
-3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects)
-4000 0000 0000 - 6000 0000 0000: protected
-6000 0000 0000 - 6200 0000 0000: traces
-6200 0000 0000 - 7d00 0000 0000: -
-7d00 0000 0000 - 7e00 0000 0000: heap
-7e00 0000 0000 - 7fff ffff ffff: modules and main thread stack
-
-C++ COMPAT linux memory layout:
-0000 0000 0000 - 0400 0000 0000: protected
-0400 0000 0000 - 1000 0000 0000: shadow
-1000 0000 0000 - 2900 0000 0000: protected
-2900 0000 0000 - 2c00 0000 0000: modules
-2c00 0000 0000 - 3000 0000 0000: -
-3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects)
-4000 0000 0000 - 6000 0000 0000: -
-6000 0000 0000 - 6200 0000 0000: traces
-6200 0000 0000 - 7d00 0000 0000: -
-7d00 0000 0000 - 7e00 0000 0000: heap
-7e00 0000 0000 - 7f00 0000 0000: -
-7f00 0000 0000 - 7fff ffff ffff: main thread stack
-
-Go linux and darwin memory layout:
-0000 0000 0000 - 0000 1000 0000: executable
-0000 1000 0000 - 00f8 0000 0000: -
-00c0 0000 0000 - 00e0 0000 0000: heap
-00e0 0000 0000 - 1000 0000 0000: -
-1000 0000 0000 - 1380 0000 0000: shadow
-1460 0000 0000 - 2000 0000 0000: -
-3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects)
-4000 0000 0000 - 6000 0000 0000: -
-6000 0000 0000 - 6200 0000 0000: traces
-6200 0000 0000 - 7fff ffff ffff: -
-
-Go windows memory layout:
-0000 0000 0000 - 0000 1000 0000: executable
-0000 1000 0000 - 00f8 0000 0000: -
-00c0 0000 0000 - 00e0 0000 0000: heap
-00e0 0000 0000 - 0100 0000 0000: -
-0100 0000 0000 - 0560 0000 0000: shadow
-0560 0000 0000 - 0760 0000 0000: traces
-0760 0000 0000 - 07d0 0000 0000: metainfo (memory blocks and sync objects)
-07d0 0000 0000 - 07ff ffff ffff: -
-*/
-
#ifndef TSAN_PLATFORM_H
#define TSAN_PLATFORM_H
+#if !defined(__LP64__) && !defined(_WIN64)
+# error "Only 64-bit is supported"
+#endif
+
#include "tsan_defs.h"
#include "tsan_trace.h"
-#if defined(__LP64__) || defined(_WIN64)
namespace __tsan {
-#if defined(TSAN_GO)
-static const uptr kLinuxAppMemBeg = 0x000000000000ULL;
-static const uptr kLinuxAppMemEnd = 0x04dfffffffffULL;
-# if SANITIZER_WINDOWS
-static const uptr kLinuxShadowMsk = 0x010000000000ULL;
-static const uptr kMetaShadow = 0x076000000000ULL;
-static const uptr kMetaSize = 0x007000000000ULL;
-# else // if SANITIZER_WINDOWS
-static const uptr kLinuxShadowMsk = 0x200000000000ULL;
-static const uptr kMetaShadow = 0x300000000000ULL;
-static const uptr kMetaSize = 0x100000000000ULL;
-# endif // if SANITIZER_WINDOWS
-#else // defined(TSAN_GO)
-static const uptr kMetaShadow = 0x300000000000ULL;
-static const uptr kMetaSize = 0x100000000000ULL;
-// TSAN_COMPAT_SHADOW is intended for COMPAT virtual memory layout,
-// when memory addresses are of the 0x2axxxxxxxxxx form.
-// The option is enabled with 'setarch x86_64 -L'.
-# if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
-static const uptr kLinuxAppMemBeg = 0x290000000000ULL;
-static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL;
-static const uptr kAppMemGapBeg = 0x2c0000000000ULL;
-static const uptr kAppMemGapEnd = 0x7d0000000000ULL;
-# else
-static const uptr kLinuxAppMemBeg = 0x7cf000000000ULL;
-static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL;
-# endif
-#endif
+#if !defined(TSAN_GO)
-static const uptr kLinuxAppMemMsk = 0x7c0000000000ULL;
+/*
+C/C++ on linux and freebsd
+0000 0000 1000 - 0100 0000 0000: main binary and/or MAP_32BIT mappings
+0100 0000 0000 - 0200 0000 0000: -
+0200 0000 0000 - 1000 0000 0000: shadow
+1000 0000 0000 - 3000 0000 0000: -
+3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects)
+4000 0000 0000 - 6000 0000 0000: -
+6000 0000 0000 - 6200 0000 0000: traces
+6200 0000 0000 - 7d00 0000 0000: -
+7d00 0000 0000 - 7e00 0000 0000: heap
+7e00 0000 0000 - 7e80 0000 0000: -
+7e80 0000 0000 - 8000 0000 0000: modules and main thread stack
+*/
-#if SANITIZER_WINDOWS
-const uptr kTraceMemBegin = 0x056000000000ULL;
-#else
-const uptr kTraceMemBegin = 0x600000000000ULL;
-#endif
-const uptr kTraceMemSize = 0x020000000000ULL;
+const uptr kMetaShadowBeg = 0x300000000000ull;
+const uptr kMetaShadowEnd = 0x400000000000ull;
+const uptr kTraceMemBeg = 0x600000000000ull;
+const uptr kTraceMemEnd = 0x620000000000ull;
+const uptr kShadowBeg = 0x020000000000ull;
+const uptr kShadowEnd = 0x100000000000ull;
+const uptr kHeapMemBeg = 0x7d0000000000ull;
+const uptr kHeapMemEnd = 0x7e0000000000ull;
+const uptr kLoAppMemBeg = 0x000000001000ull;
+const uptr kLoAppMemEnd = 0x010000000000ull;
+const uptr kHiAppMemBeg = 0x7e8000000000ull;
+const uptr kHiAppMemEnd = 0x800000000000ull;
+const uptr kAppMemMsk = 0x7c0000000000ull;
+const uptr kAppMemXor = 0x020000000000ull;
-// This has to be a macro to allow constant initialization of constants below.
-#ifndef TSAN_GO
-#define MemToShadow(addr) \
- ((((uptr)addr) & ~(kLinuxAppMemMsk | (kShadowCell - 1))) * kShadowCnt)
-#define MemToMeta(addr) \
- (u32*)(((((uptr)addr) & ~(kLinuxAppMemMsk | (kMetaShadowCell - 1))) \
- / kMetaShadowCell * kMetaShadowSize) | kMetaShadow)
-#else
-#define MemToShadow(addr) \
- (((((uptr)addr) & ~(kShadowCell - 1)) * kShadowCnt) | kLinuxShadowMsk)
-#define MemToMeta(addr) \
- (u32*)(((((uptr)addr) & ~(kMetaShadowCell - 1)) \
- / kMetaShadowCell * kMetaShadowSize) | kMetaShadow)
-#endif
-
-static const uptr kLinuxShadowBeg = MemToShadow(kLinuxAppMemBeg);
-static const uptr kLinuxShadowEnd =
- MemToShadow(kLinuxAppMemEnd) | 0xff;
-
-static inline bool IsAppMem(uptr mem) {
-#if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
- return (mem >= kLinuxAppMemBeg && mem < kAppMemGapBeg) ||
- (mem >= kAppMemGapEnd && mem <= kLinuxAppMemEnd);
-#elif defined(TSAN_GO)
- return mem <= kLinuxAppMemEnd;
-#else
- return mem >= kLinuxAppMemBeg && mem <= kLinuxAppMemEnd;
-#endif
+ALWAYS_INLINE
+bool IsAppMem(uptr mem) {
+ return (mem >= kHeapMemBeg && mem < kHeapMemEnd) ||
+ (mem >= kLoAppMemBeg && mem < kLoAppMemEnd) ||
+ (mem >= kHiAppMemBeg && mem < kHiAppMemEnd);
}
-static inline bool IsShadowMem(uptr mem) {
- return mem >= kLinuxShadowBeg && mem <= kLinuxShadowEnd;
+ALWAYS_INLINE
+bool IsShadowMem(uptr mem) {
+ return mem >= kShadowBeg && mem <= kShadowEnd;
}
-static inline uptr ShadowToMem(uptr shadow) {
- CHECK(IsShadowMem(shadow));
-#ifdef TSAN_GO
- return (shadow & ~kLinuxShadowMsk) / kShadowCnt;
+ALWAYS_INLINE
+bool IsMetaMem(uptr mem) {
+ return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd;
+}
+
+ALWAYS_INLINE
+uptr MemToShadow(uptr x) {
+ DCHECK(IsAppMem(x));
+ return (((x) & ~(kAppMemMsk | (kShadowCell - 1)))
+ ^ kAppMemXor) * kShadowCnt;
+}
+
+ALWAYS_INLINE
+u32 *MemToMeta(uptr x) {
+ DCHECK(IsAppMem(x));
+ return (u32*)(((((x) & ~(kAppMemMsk | (kMetaShadowCell - 1)))
+ ^ kAppMemXor) / kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg);
+}
+
+ALWAYS_INLINE
+uptr ShadowToMem(uptr s) {
+ CHECK(IsShadowMem(s));
+ if (s >= MemToShadow(kLoAppMemBeg) && s <= MemToShadow(kLoAppMemEnd - 1))
+ return (s / kShadowCnt) ^ kAppMemXor;
+ else
+ return ((s / kShadowCnt) ^ kAppMemXor) | kAppMemMsk;
+}
+
+static USED uptr UserRegions[] = {
+ kLoAppMemBeg, kLoAppMemEnd,
+ kHiAppMemBeg, kHiAppMemEnd,
+ kHeapMemBeg, kHeapMemEnd,
+};
+
+#elif defined(TSAN_GO) && !SANITIZER_WINDOWS
+
+/* Go on linux, darwin and freebsd
+0000 0000 1000 - 0000 1000 0000: executable
+0000 1000 0000 - 00c0 0000 0000: -
+00c0 0000 0000 - 00e0 0000 0000: heap
+00e0 0000 0000 - 2000 0000 0000: -
+2000 0000 0000 - 2380 0000 0000: shadow
+2380 0000 0000 - 3000 0000 0000: -
+3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects)
+4000 0000 0000 - 6000 0000 0000: -
+6000 0000 0000 - 6200 0000 0000: traces
+6200 0000 0000 - 8000 0000 0000: -
+*/
+
+const uptr kMetaShadowBeg = 0x300000000000ull;
+const uptr kMetaShadowEnd = 0x400000000000ull;
+const uptr kTraceMemBeg = 0x600000000000ull;
+const uptr kTraceMemEnd = 0x620000000000ull;
+const uptr kShadowBeg = 0x200000000000ull;
+const uptr kShadowEnd = 0x238000000000ull;
+const uptr kAppMemBeg = 0x000000001000ull;
+const uptr kAppMemEnd = 0x00e000000000ull;
+
+ALWAYS_INLINE
+bool IsAppMem(uptr mem) {
+ return mem >= kAppMemBeg && mem < kAppMemEnd;
+}
+
+ALWAYS_INLINE
+bool IsShadowMem(uptr mem) {
+ return mem >= kShadowBeg && mem <= kShadowEnd;
+}
+
+ALWAYS_INLINE
+bool IsMetaMem(uptr mem) {
+ return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd;
+}
+
+ALWAYS_INLINE
+uptr MemToShadow(uptr x) {
+ DCHECK(IsAppMem(x));
+ return ((x & ~(kShadowCell - 1)) * kShadowCnt) | kShadowBeg;
+}
+
+ALWAYS_INLINE
+u32 *MemToMeta(uptr x) {
+ DCHECK(IsAppMem(x));
+ return (u32*)(((x & ~(kMetaShadowCell - 1)) / \
+ kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg);
+}
+
+ALWAYS_INLINE
+uptr ShadowToMem(uptr s) {
+ CHECK(IsShadowMem(s));
+ return (s & ~kShadowBeg) / kShadowCnt;
+}
+
+static USED uptr UserRegions[] = {
+ kAppMemBeg, kAppMemEnd,
+};
+
+#elif defined(TSAN_GO) && SANITIZER_WINDOWS
+
+/* Go on windows
+0000 0000 1000 - 0000 1000 0000: executable
+0000 1000 0000 - 00f8 0000 0000: -
+00c0 0000 0000 - 00e0 0000 0000: heap
+00e0 0000 0000 - 0100 0000 0000: -
+0100 0000 0000 - 0380 0000 0000: shadow
+0380 0000 0000 - 0560 0000 0000: -
+0560 0000 0000 - 0760 0000 0000: traces
+0760 0000 0000 - 07d0 0000 0000: metainfo (memory blocks and sync objects)
+07d0 0000 0000 - 8000 0000 0000: -
+*/
+
+const uptr kMetaShadowBeg = 0x076000000000ull;
+const uptr kMetaShadowEnd = 0x07d000000000ull;
+const uptr kTraceMemBeg = 0x056000000000ull;
+const uptr kTraceMemEnd = 0x076000000000ull;
+const uptr kShadowBeg = 0x010000000000ull;
+const uptr kShadowEnd = 0x038000000000ull;
+const uptr kAppMemBeg = 0x000000001000ull;
+const uptr kAppMemEnd = 0x00e000000000ull;
+
+ALWAYS_INLINE
+bool IsAppMem(uptr mem) {
+ return mem >= kAppMemBeg && mem < kAppMemEnd;
+}
+
+ALWAYS_INLINE
+bool IsShadowMem(uptr mem) {
+ return mem >= kShadowBeg && mem <= kShadowEnd;
+}
+
+ALWAYS_INLINE
+bool IsMetaMem(uptr mem) {
+ return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd;
+}
+
+ALWAYS_INLINE
+uptr MemToShadow(uptr x) {
+ DCHECK(IsAppMem(x));
+ return ((x & ~(kShadowCell - 1)) * kShadowCnt) | kShadowBeg;
+}
+
+ALWAYS_INLINE
+u32 *MemToMeta(uptr x) {
+ DCHECK(IsAppMem(x));
+ return (u32*)(((x & ~(kMetaShadowCell - 1)) / \
+ kMetaShadowCell * kMetaShadowSize) | kMetaShadowEnd);
+}
+
+ALWAYS_INLINE
+uptr ShadowToMem(uptr s) {
+ CHECK(IsShadowMem(s));
+ // FIXME(dvyukov): this is most likely wrong as the mapping is not bijection.
+ return (x & ~kShadowBeg) / kShadowCnt;
+}
+
+static USED uptr UserRegions[] = {
+ kAppMemBeg, kAppMemEnd,
+};
+
#else
- return (shadow / kShadowCnt) | kLinuxAppMemMsk;
+# error "Unknown platform"
#endif
-}
-
-// For COMPAT mapping returns an alternative address
-// that mapped to the same shadow address.
-// COMPAT mapping is not quite one-to-one.
-static inline uptr AlternativeAddress(uptr addr) {
-#if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
- return (addr & ~kLinuxAppMemMsk) | 0x280000000000ULL;
-#else
- return 0;
-#endif
-}
-
-void FlushShadowMemory();
-void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive);
-uptr GetRSS();
-
-const char *InitializePlatform();
-void FinalizePlatform();
// The additional page is to catch shadow stack overflow as paging fault.
// Windows wants 64K alignment for mmaps.
@@ -175,18 +236,23 @@
+ (64 << 10) + (64 << 10) - 1) & ~((64 << 10) - 1);
uptr ALWAYS_INLINE GetThreadTrace(int tid) {
- uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize;
- DCHECK_LT(p, kTraceMemBegin + kTraceMemSize);
+ uptr p = kTraceMemBeg + (uptr)tid * kTotalTraceSize;
+ DCHECK_LT(p, kTraceMemEnd);
return p;
}
uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) {
- uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize
+ uptr p = kTraceMemBeg + (uptr)tid * kTotalTraceSize
+ kTraceSize * sizeof(Event);
- DCHECK_LT(p, kTraceMemBegin + kTraceMemSize);
+ DCHECK_LT(p, kTraceMemEnd);
return p;
}
+void InitializePlatform();
+void FlushShadowMemory();
+void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive);
+uptr GetRSS();
+
void *internal_start_thread(void(*func)(void*), void *arg);
void internal_join_thread(void *th);
@@ -202,8 +268,4 @@
} // namespace __tsan
-#else // defined(__LP64__) || defined(_WIN64)
-# error "Only 64-bit is supported"
-#endif
-
#endif // TSAN_PLATFORM_H
diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc
index 53ecfc6..46b648c 100644
--- a/lib/tsan/rtl/tsan_platform_linux.cc
+++ b/lib/tsan/rtl/tsan_platform_linux.cc
@@ -9,7 +9,7 @@
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
-// Linux-specific code.
+// Linux- and FreeBSD-specific code.
//===----------------------------------------------------------------------===//
@@ -20,6 +20,7 @@
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stoptheworld.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
#include "tsan_flags.h"
@@ -62,6 +63,9 @@
namespace __tsan {
+static uptr g_data_start;
+static uptr g_data_end;
+
const uptr kPageSize = 4096;
enum {
@@ -76,22 +80,26 @@
MemCount = 8,
};
-void FillProfileCallback(uptr start, uptr rss, bool file,
+void FillProfileCallback(uptr p, uptr rss, bool file,
uptr *mem, uptr stats_size) {
mem[MemTotal] += rss;
- start >>= 40;
- if (start < 0x10)
+ if (p >= kShadowBeg && p < kShadowEnd)
mem[MemShadow] += rss;
- else if (start >= 0x20 && start < 0x30)
- mem[file ? MemFile : MemMmap] += rss;
- else if (start >= 0x30 && start < 0x40)
+ else if (p >= kMetaShadowBeg && p < kMetaShadowEnd)
mem[MemMeta] += rss;
- else if (start >= 0x7e)
- mem[file ? MemFile : MemMmap] += rss;
- else if (start >= 0x60 && start < 0x62)
- mem[MemTrace] += rss;
- else if (start >= 0x7d && start < 0x7e)
+#ifndef TSAN_GO
+ else if (p >= kHeapMemBeg && p < kHeapMemEnd)
mem[MemHeap] += rss;
+ else if (p >= kLoAppMemBeg && p < kLoAppMemEnd)
+ mem[file ? MemFile : MemMmap] += rss;
+ else if (p >= kHiAppMemBeg && p < kHiAppMemEnd)
+ mem[file ? MemFile : MemMmap] += rss;
+#else
+ else if (p >= kAppMemBeg && p < kAppMemEnd)
+ mem[file ? MemFile : MemMmap] += rss;
+#endif
+ else if (p >= kTraceMemBeg && p < kTraceMemEnd)
+ mem[MemTrace] += rss;
else
mem[MemOther] += rss;
}
@@ -99,26 +107,49 @@
void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
uptr mem[MemCount] = {};
__sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7);
+ StackDepotStats *stacks = StackDepotGetStats();
internal_snprintf(buf, buf_size,
"RSS %zd MB: shadow:%zd meta:%zd file:%zd mmap:%zd"
- " trace:%zd heap:%zd other:%zd nthr=%zd/%zd\n",
+ " trace:%zd heap:%zd other:%zd stacks=%zd[%zd] nthr=%zd/%zd\n",
mem[MemTotal] >> 20, mem[MemShadow] >> 20, mem[MemMeta] >> 20,
mem[MemFile] >> 20, mem[MemMmap] >> 20, mem[MemTrace] >> 20,
mem[MemHeap] >> 20, mem[MemOther] >> 20,
+ stacks->allocated >> 20, stacks->n_uniq_ids,
nlive, nthread);
}
uptr GetRSS() {
- uptr mem[7] = {};
- __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7);
- return mem[6];
+ uptr fd = OpenFile("/proc/self/statm", false);
+ if ((sptr)fd < 0)
+ return 0;
+ char buf[64];
+ uptr len = internal_read(fd, buf, sizeof(buf) - 1);
+ internal_close(fd);
+ if ((sptr)len <= 0)
+ return 0;
+ buf[len] = 0;
+ // The format of the file is:
+ // 1084 89 69 11 0 79 0
+ // We need the second number which is RSS in 4K units.
+ char *pos = buf;
+ // Skip the first number.
+ while (*pos >= '0' && *pos <= '9')
+ pos++;
+ // Skip whitespaces.
+ while (!(*pos >= '0' && *pos <= '9') && *pos != 0)
+ pos++;
+ // Read the number.
+ uptr rss = 0;
+ while (*pos >= '0' && *pos <= '9')
+ rss = rss * 10 + *pos++ - '0';
+ return rss * 4096;
}
#if SANITIZER_LINUX
void FlushShadowMemoryCallback(
const SuspendedThreadsList &suspended_threads_list,
void *argument) {
- FlushUnneededShadowMemory(kLinuxShadowBeg, kLinuxShadowEnd - kLinuxShadowBeg);
+ FlushUnneededShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
}
#endif
@@ -199,87 +230,59 @@
void InitializeShadowMemory() {
// Map memory shadow.
- uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg,
- kLinuxShadowEnd - kLinuxShadowBeg);
- if (shadow != kLinuxShadowBeg) {
+ uptr shadow = (uptr)MmapFixedNoReserve(kShadowBeg,
+ kShadowEnd - kShadowBeg);
+ if (shadow != kShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
- "to link with -pie (%p, %p).\n", shadow, kLinuxShadowBeg);
+ "to link with -pie (%p, %p).\n", shadow, kShadowBeg);
Die();
}
+ // This memory range is used for thread stacks and large user mmaps.
+ // Frequently a thread uses only a small part of stack and similarly
+ // a program uses a small part of large mmap. On some programs
+ // we see 20% memory usage reduction without huge pages for this range.
+#ifdef MADV_NOHUGEPAGE
+ madvise((void*)MemToShadow(0x7f0000000000ULL),
+ 0x10000000000ULL * kShadowMultiplier, MADV_NOHUGEPAGE);
+#endif
DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
- kLinuxShadowBeg, kLinuxShadowEnd,
- (kLinuxShadowEnd - kLinuxShadowBeg) >> 30);
+ kShadowBeg, kShadowEnd,
+ (kShadowEnd - kShadowBeg) >> 30);
// Map meta shadow.
- if (MemToMeta(kLinuxAppMemBeg) < (u32*)kMetaShadow) {
- Printf("ThreadSanitizer: bad meta shadow (%p -> %p < %p)\n",
- kLinuxAppMemBeg, MemToMeta(kLinuxAppMemBeg), kMetaShadow);
- Die();
- }
- if (MemToMeta(kLinuxAppMemEnd) >= (u32*)(kMetaShadow + kMetaSize)) {
- Printf("ThreadSanitizer: bad meta shadow (%p -> %p >= %p)\n",
- kLinuxAppMemEnd, MemToMeta(kLinuxAppMemEnd), kMetaShadow + kMetaSize);
- Die();
- }
- uptr meta = (uptr)MmapFixedNoReserve(kMetaShadow, kMetaSize);
- if (meta != kMetaShadow) {
+ uptr meta_size = kMetaShadowEnd - kMetaShadowBeg;
+ uptr meta = (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size);
+ if (meta != kMetaShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
- "to link with -pie (%p, %p).\n", meta, kMetaShadow);
+ "to link with -pie (%p, %p).\n", meta, kMetaShadowBeg);
Die();
}
DPrintf("meta shadow: %zx-%zx (%zuGB)\n",
- kMetaShadow, kMetaShadow + kMetaSize, kMetaSize >> 30);
-
- // Protect gaps.
- const uptr kClosedLowBeg = 0x200000;
- const uptr kClosedLowEnd = kLinuxShadowBeg - 1;
- const uptr kClosedMidBeg = kLinuxShadowEnd + 1;
- const uptr kClosedMidEnd = min(min(kLinuxAppMemBeg, kTraceMemBegin),
- kMetaShadow);
-
- ProtectRange(kClosedLowBeg, kClosedLowEnd);
- ProtectRange(kClosedMidBeg, kClosedMidEnd);
- VPrintf(2, "kClosedLow %zx-%zx (%zuGB)\n",
- kClosedLowBeg, kClosedLowEnd, (kClosedLowEnd - kClosedLowBeg) >> 30);
- VPrintf(2, "kClosedMid %zx-%zx (%zuGB)\n",
- kClosedMidBeg, kClosedMidEnd, (kClosedMidEnd - kClosedMidBeg) >> 30);
- VPrintf(2, "app mem: %zx-%zx (%zuGB)\n",
- kLinuxAppMemBeg, kLinuxAppMemEnd,
- (kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
- VPrintf(2, "stack: %zx\n", (uptr)&shadow);
+ meta, meta + meta_size, meta_size >> 30);
MapRodata();
}
-#endif
-
-static uptr g_data_start;
-static uptr g_data_end;
-
-#ifndef TSAN_GO
-static void CheckPIE() {
- // Ensure that the binary is indeed compiled with -pie.
- MemoryMappingLayout proc_maps(true);
- uptr start, end;
- if (proc_maps.Next(&start, &end,
- /*offset*/0, /*filename*/0, /*filename_size*/0,
- /*protection*/0)) {
- if ((u64)start < kLinuxAppMemBeg) {
- Printf("FATAL: ThreadSanitizer can not mmap the shadow memory ("
- "something is mapped at 0x%zx < 0x%zx)\n",
- start, kLinuxAppMemBeg);
- Printf("FATAL: Make sure to compile with -fPIE"
- " and to link with -pie.\n");
- Die();
- }
- }
-}
static void InitDataSeg() {
MemoryMappingLayout proc_maps(true);
uptr start, end, offset;
char name[128];
+#if SANITIZER_FREEBSD
+ // On FreeBSD BSS is usually the last block allocated within the
+ // low range and heap is the last block allocated within the range
+ // 0x800000000-0x8ffffffff.
+ while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name),
+ /*protection*/ 0)) {
+ DPrintf("%p-%p %p %s\n", start, end, offset, name);
+ if ((start & 0xffff00000000ULL) == 0 && (end & 0xffff00000000ULL) == 0 &&
+ name[0] == '\0') {
+ g_data_start = start;
+ g_data_end = end;
+ }
+ }
+#else
bool prev_is_data = false;
while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name),
/*protection*/ 0)) {
@@ -295,34 +298,39 @@
g_data_end = end;
prev_is_data = is_data;
}
+#endif
DPrintf("guessed data_start=%p data_end=%p\n", g_data_start, g_data_end);
CHECK_LT(g_data_start, g_data_end);
CHECK_GE((uptr)&g_data_start, g_data_start);
CHECK_LT((uptr)&g_data_start, g_data_end);
}
+static void CheckAndProtect() {
+ // Ensure that the binary is indeed compiled with -pie.
+ MemoryMappingLayout proc_maps(true);
+ uptr p, end;
+ while (proc_maps.Next(&p, &end, 0, 0, 0, 0)) {
+ if (IsAppMem(p))
+ continue;
+ if (p >= kHeapMemEnd &&
+ p < kHeapMemEnd + PrimaryAllocator::AdditionalSize())
+ continue;
+ if (p >= 0xf000000000000000ull) // vdso
+ break;
+ Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end);
+ Die();
+ }
+
+ ProtectRange(kLoAppMemEnd, kShadowBeg);
+ ProtectRange(kShadowEnd, kMetaShadowBeg);
+ ProtectRange(kMetaShadowEnd, kTraceMemBeg);
+ ProtectRange(kTraceMemEnd, kHeapMemBeg);
+ ProtectRange(kHeapMemEnd + PrimaryAllocator::AdditionalSize(), kHiAppMemBeg);
+}
#endif // #ifndef TSAN_GO
-static rlim_t getlim(int res) {
- rlimit rlim;
- CHECK_EQ(0, getrlimit(res, &rlim));
- return rlim.rlim_cur;
-}
-
-static void setlim(int res, rlim_t lim) {
- // The following magic is to prevent clang from replacing it with memset.
- volatile rlimit rlim;
- rlim.rlim_cur = lim;
- rlim.rlim_max = lim;
- setrlimit(res, (rlimit*)&rlim);
-}
-
-const char *InitializePlatform() {
- void *p = 0;
- if (sizeof(p) == 8) {
- // Disable core dumps, dumping of 16TB usually takes a bit long.
- setlim(RLIMIT_CORE, 0);
- }
+void InitializePlatform() {
+ DisableCoreDumperIfNecessary();
// Go maps shadow memory lazily and works fine with limited address space.
// Unlimited stack is not a problem as well, because the executable
@@ -332,7 +340,7 @@
// TSan doesn't play well with unlimited stack size (as stack
// overlaps with shadow memory). If we detect unlimited stack size,
// we re-exec the program with limited stack size as a best effort.
- if (getlim(RLIMIT_STACK) == (rlim_t)-1) {
+ if (StackSizeIsUnlimited()) {
const uptr kMaxStackSize = 32 * 1024 * 1024;
VReport(1, "Program is run with unlimited stack size, which wouldn't "
"work with ThreadSanitizer.\n"
@@ -342,11 +350,11 @@
reexec = true;
}
- if (getlim(RLIMIT_AS) != (rlim_t)-1) {
+ if (!AddressSpaceIsUnlimited()) {
Report("WARNING: Program is run with limited virtual address space,"
" which wouldn't work with ThreadSanitizer.\n");
Report("Re-execing with unlimited virtual address space.\n");
- setlim(RLIMIT_AS, -1);
+ SetAddressSpaceUnlimited();
reexec = true;
}
if (reexec)
@@ -354,11 +362,10 @@
}
#ifndef TSAN_GO
- CheckPIE();
+ CheckAndProtect();
InitTlsSize();
InitDataSeg();
#endif
- return GetEnv(kTsanOptionsEnv);
}
bool IsGlobalVar(uptr addr) {
@@ -418,4 +425,4 @@
} // namespace __tsan
-#endif // SANITIZER_LINUX
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
diff --git a/lib/tsan/rtl/tsan_platform_mac.cc b/lib/tsan/rtl/tsan_platform_mac.cc
index 15d0688..fd71eb3 100644
--- a/lib/tsan/rtl/tsan_platform_mac.cc
+++ b/lib/tsan/rtl/tsan_platform_mac.cc
@@ -56,39 +56,25 @@
#ifndef TSAN_GO
void InitializeShadowMemory() {
- uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg,
- kLinuxShadowEnd - kLinuxShadowBeg);
- if (shadow != kLinuxShadowBeg) {
+ uptr shadow = (uptr)MmapFixedNoReserve(kShadowBeg,
+ kShadowEnd - kShadowBeg);
+ if (shadow != kShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
"to link with -pie.\n");
Die();
}
- DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n",
- kLinuxShadowBeg, kLinuxShadowEnd,
- (kLinuxShadowEnd - kLinuxShadowBeg) >> 30);
- DPrintf("kLinuxAppMem %zx-%zx (%zuGB)\n",
- kLinuxAppMemBeg, kLinuxAppMemEnd,
- (kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
+ DPrintf("kShadow %zx-%zx (%zuGB)\n",
+ kShadowBeg, kShadowEnd,
+ (kShadowEnd - kShadowBeg) >> 30);
+ DPrintf("kAppMem %zx-%zx (%zuGB)\n",
+ kAppMemBeg, kAppMemEnd,
+ (kAppMemEnd - kAppMemBeg) >> 30);
}
#endif
-const char *InitializePlatform() {
- void *p = 0;
- if (sizeof(p) == 8) {
- // Disable core dumps, dumping of 16TB usually takes a bit long.
- // The following magic is to prevent clang from replacing it with memset.
- volatile rlimit lim;
- lim.rlim_cur = 0;
- lim.rlim_max = 0;
- setrlimit(RLIMIT_CORE, (rlimit*)&lim);
- }
-
- return GetEnv(kTsanOptionsEnv);
-}
-
-void FinalizePlatform() {
- fflush(0);
+void InitializePlatform() {
+ DisableCoreDumperIfNecessary();
}
#ifndef TSAN_GO
diff --git a/lib/tsan/rtl/tsan_platform_windows.cc b/lib/tsan/rtl/tsan_platform_windows.cc
index 8b9d20e..ae9f050 100644
--- a/lib/tsan/rtl/tsan_platform_windows.cc
+++ b/lib/tsan/rtl/tsan_platform_windows.cc
@@ -35,12 +35,7 @@
return 0;
}
-const char *InitializePlatform() {
- return GetEnv(kTsanOptionsEnv);
-}
-
-void FinalizePlatform() {
- fflush(0);
+void InitializePlatform() {
}
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_report.cc b/lib/tsan/rtl/tsan_report.cc
index e14d0b9..f4a1ddb 100644
--- a/lib/tsan/rtl/tsan_report.cc
+++ b/lib/tsan/rtl/tsan_report.cc
@@ -13,10 +13,30 @@
#include "tsan_report.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
+#include "sanitizer_common/sanitizer_stacktrace_printer.h"
namespace __tsan {
+ReportStack::ReportStack() : next(nullptr), info(), suppressable(false) {}
+
+ReportStack *ReportStack::New(uptr addr) {
+ void *mem = internal_alloc(MBlockReportStack, sizeof(ReportStack));
+ ReportStack *res = new(mem) ReportStack();
+ res->info.address = addr;
+ return res;
+}
+
+ReportLocation::ReportLocation(ReportLocationType type)
+ : type(type), global(), heap_chunk_start(0), heap_chunk_size(0), tid(0),
+ fd(0), suppressable(false), stack(nullptr) {}
+
+ReportLocation *ReportLocation::New(ReportLocationType type) {
+ void *mem = internal_alloc(MBlockReportStack, sizeof(ReportLocation));
+ return new(mem) ReportLocation(type);
+}
+
class Decorator: public __sanitizer::SanitizerCommonDecorator {
public:
Decorator() : SanitizerCommonDecorator() { }
@@ -70,6 +90,8 @@
return "data race on vptr (ctor/dtor vs virtual call)";
if (typ == ReportTypeUseAfterFree)
return "heap-use-after-free";
+ if (typ == ReportTypeVptrUseAfterFree)
+ return "heap-use-after-free (virtual call vs free)";
if (typ == ReportTypeThreadLeak)
return "thread leak";
if (typ == ReportTypeMutexDestroyLocked)
@@ -96,14 +118,11 @@
Printf(" [failed to restore the stack]\n\n");
return;
}
- for (int i = 0; ent; ent = ent->next, i++) {
- Printf(" #%d %s %s:%d", i, ent->func, ent->file, ent->line);
- if (ent->col)
- Printf(":%d", ent->col);
- if (ent->module && ent->offset)
- Printf(" (%s+%p)\n", ent->module, (void*)ent->offset);
- else
- Printf(" (%p)\n", (void*)ent->pc);
+ for (int i = 0; ent && ent->info.address; ent = ent->next, i++) {
+ InternalScopedString res(2 * GetPageSizeCached());
+ RenderFrame(&res, common_flags()->stack_trace_format, i, ent->info,
+ common_flags()->strip_path_prefix, "__interceptor_");
+ Printf("%s\n", res.data());
}
Printf("\n");
}
@@ -145,12 +164,15 @@
bool print_stack = false;
Printf("%s", d.Location());
if (loc->type == ReportLocationGlobal) {
+ const DataInfo &global = loc->global;
Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n",
- loc->name, loc->size, loc->addr, loc->module, loc->offset);
+ global.name, global.size, global.start,
+ StripModuleName(global.module), global.module_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));
+ loc->heap_chunk_size, loc->heap_chunk_start,
+ thread_name(thrbuf, loc->tid));
print_stack = true;
} else if (loc->type == ReportLocationStack) {
Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
@@ -303,8 +325,10 @@
if (rep->typ == ReportTypeThreadLeak && rep->count > 1)
Printf(" And %d more similar thread leaks.\n\n", rep->count - 1);
- if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep)))
- ReportErrorSummary(rep_typ_str, ent->file, ent->line, ent->func);
+ if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep))) {
+ const AddressInfo &info = ent->info;
+ ReportErrorSummary(rep_typ_str, info.file, info.line, info.function);
+ }
Printf("==================\n");
}
@@ -319,8 +343,9 @@
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);
+ const AddressInfo &info = ent->info;
+ Printf(" %s()\n %s:%d +0x%zx\n", info.function, info.file, info.line,
+ (void *)info.module_offset);
}
}
diff --git a/lib/tsan/rtl/tsan_report.h b/lib/tsan/rtl/tsan_report.h
index 8ea9774..e6d8535 100644
--- a/lib/tsan/rtl/tsan_report.h
+++ b/lib/tsan/rtl/tsan_report.h
@@ -13,6 +13,7 @@
#ifndef TSAN_REPORT_H
#define TSAN_REPORT_H
+#include "sanitizer_common/sanitizer_symbolizer.h"
#include "tsan_defs.h"
#include "tsan_vector.h"
@@ -22,6 +23,7 @@
ReportTypeRace,
ReportTypeVptrRace,
ReportTypeUseAfterFree,
+ ReportTypeVptrUseAfterFree,
ReportTypeThreadLeak,
ReportTypeMutexDestroyLocked,
ReportTypeMutexDoubleLock,
@@ -35,14 +37,12 @@
struct ReportStack {
ReportStack *next;
- char *module;
- uptr offset;
- uptr pc;
- char *func;
- char *file;
- int line;
- int col;
+ AddressInfo info;
bool suppressable;
+ static ReportStack *New(uptr addr);
+
+ private:
+ ReportStack();
};
struct ReportMopMutex {
@@ -72,17 +72,17 @@
struct ReportLocation {
ReportLocationType type;
- uptr addr;
- uptr size;
- char *module;
- uptr offset;
+ DataInfo global;
+ uptr heap_chunk_start;
+ uptr heap_chunk_size;
int tid;
int fd;
- char *name;
- char *file;
- int line;
bool suppressable;
ReportStack *stack;
+
+ static ReportLocation *New(ReportLocationType type);
+ private:
+ explicit ReportLocation(ReportLocationType type);
};
struct ReportThread {
diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc
index 3e3e339..79320cb 100644
--- a/lib/tsan/rtl/tsan_rtl.cc
+++ b/lib/tsan/rtl/tsan_rtl.cc
@@ -157,7 +157,6 @@
}
u64 last_flush = NanoTime();
- u64 last_rss_check = NanoTime();
uptr last_rss = 0;
for (int i = 0;
atomic_load(&ctx->stop_background_thread, memory_order_relaxed) == 0;
@@ -168,29 +167,23 @@
// Flush memory if requested.
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");
+ VPrintf(1, "ThreadSanitizer: periodic memory flush\n");
FlushShadowMemory();
last_flush = NanoTime();
}
}
// GetRSS can be expensive on huge programs, so don't do it every 100ms.
- if (flags()->memory_limit_mb > 0 && last_rss_check + 1000 * kMs2Ns < now) {
- last_rss_check = now;
+ 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);
- }
+ VPrintf(1, "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");
+ VPrintf(1, "ThreadSanitizer: flushing memory due to RSS\n");
FlushShadowMemory();
rss = GetRSS();
- if (flags()->verbosity > 0)
- Printf("ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20);
+ VPrintf(1, "ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20);
}
last_rss = rss;
}
@@ -268,8 +261,8 @@
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);
+ CHECK_GE(addr, kTraceMemBeg);
+ CHECK_LE(addr + size, kTraceMemEnd);
CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
uptr addr1 = (uptr)MmapFixedNoReserve(addr, size);
if (addr1 != addr) {
@@ -279,6 +272,28 @@
}
}
+static void CheckShadowMapping() {
+ for (uptr i = 0; i < ARRAY_SIZE(UserRegions); i += 2) {
+ const uptr beg = UserRegions[i];
+ const uptr end = UserRegions[i + 1];
+ VPrintf(3, "checking shadow region %p-%p\n", beg, end);
+ for (uptr p0 = beg; p0 <= end; p0 += (end - beg) / 4) {
+ for (int x = -1; x <= 1; x++) {
+ const uptr p = p0 + x;
+ if (p < beg || p >= end)
+ continue;
+ const uptr s = MemToShadow(p);
+ VPrintf(3, " checking pointer %p -> %p\n", p, s);
+ CHECK(IsAppMem(p));
+ CHECK(IsShadowMem(s));
+ CHECK_EQ(p & ~(kShadowCell - 1), ShadowToMem(s));
+ const uptr m = (uptr)MemToMeta(p);
+ CHECK(IsMetaMem(m));
+ }
+ }
+ }
+}
+
void Initialize(ThreadState *thr) {
// Thread safe because done before all threads exist.
static bool is_initialized = false;
@@ -291,36 +306,36 @@
// Install tool-specific callbacks in sanitizer_common.
SetCheckFailedCallback(TsanCheckFailed);
+ ctx = new(ctx_placeholder) Context;
+ const char *options = GetEnv(kTsanOptionsEnv);
+ InitializeFlags(&ctx->flags, options);
#ifndef TSAN_GO
InitializeAllocator();
#endif
InitializeInterceptors();
- const char *env = InitializePlatform();
+ CheckShadowMapping();
+ InitializePlatform();
InitializeMutex();
InitializeDynamicAnnotations();
- ctx = new(ctx_placeholder) Context;
#ifndef TSAN_GO
InitializeShadowMemory();
#endif
- InitializeFlags(&ctx->flags, env);
// Setup correct file descriptor for error reports.
- __sanitizer_set_report_path(flags()->log_path);
+ __sanitizer_set_report_path(common_flags()->log_path);
InitializeSuppressions();
#ifndef TSAN_GO
InitializeLibIgnore();
- Symbolizer::Init(common_flags()->external_symbolizer_path);
- Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer);
+ Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer);
#endif
StartBackgroundThread();
#ifndef TSAN_GO
SetSandboxingCallback(StopBackgroundThread);
#endif
- if (flags()->detect_deadlocks)
+ if (common_flags()->detect_deadlocks)
ctx->dd = DDetector::Create(flags());
- if (ctx->flags.verbosity)
- Printf("***** Running under ThreadSanitizer v2 (pid %d) *****\n",
- (int)internal_getpid());
+ VPrintf(1, "***** Running under ThreadSanitizer v2 (pid %d) *****\n",
+ (int)internal_getpid());
// Initialize thread 0.
int tid = ThreadCreate(thr, 0, 0, true);
@@ -339,7 +354,6 @@
}
int Finalize(ThreadState *thr) {
- Context *ctx = __tsan::ctx;
bool failed = false;
if (flags()->atexit_sleep_ms > 0 && ThreadCount(thr) > 1)
@@ -352,7 +366,7 @@
ctx->report_mtx.Unlock();
#ifndef TSAN_GO
- if (ctx->flags.verbosity)
+ if (common_flags()->verbosity)
AllocatorPrintStats();
#endif
@@ -373,7 +387,7 @@
ctx->nmissed_expected);
}
- if (flags()->print_suppressions)
+ if (common_flags()->print_suppressions)
PrintMatchedSuppressions();
#ifndef TSAN_GO
if (flags()->print_benign)
@@ -448,8 +462,8 @@
thr->shadow_stack_pos[0] = pc;
thr->shadow_stack_pos++;
}
- u32 id = StackDepotPut(thr->shadow_stack,
- thr->shadow_stack_pos - thr->shadow_stack);
+ u32 id = StackDepotPut(
+ StackTrace(thr->shadow_stack, thr->shadow_stack_pos - thr->shadow_stack));
if (pc != 0)
thr->shadow_stack_pos--;
return id;
@@ -462,7 +476,7 @@
unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % TraceParts();
TraceHeader *hdr = &thr_trace->headers[trace];
hdr->epoch0 = thr->fast_state.epoch();
- hdr->stack0.ObtainCurrent(thr, 0);
+ ObtainCurrentStack(thr, 0, &hdr->stack0);
hdr->mset0 = thr->mset;
thr->nomalloc--;
}
@@ -608,13 +622,13 @@
while (size) {
int size1 = 1;
int kAccessSizeLog = kSizeLog1;
- if (size >= 8 && (addr & ~7) == ((addr + 8) & ~7)) {
+ if (size >= 8 && (addr & ~7) == ((addr + 7) & ~7)) {
size1 = 8;
kAccessSizeLog = kSizeLog8;
- } else if (size >= 4 && (addr & ~7) == ((addr + 4) & ~7)) {
+ } else if (size >= 4 && (addr & ~7) == ((addr + 3) & ~7)) {
size1 = 4;
kAccessSizeLog = kSizeLog4;
- } else if (size >= 2 && (addr & ~7) == ((addr + 2) & ~7)) {
+ } else if (size >= 2 && (addr & ~7) == ((addr + 1) & ~7)) {
size1 = 2;
kAccessSizeLog = kSizeLog2;
}
@@ -701,6 +715,8 @@
bool ContainsSameAccess(u64 *s, u64 a, u64 sync_epoch, bool is_write) {
#if defined(__SSE3__) && TSAN_SHADOW_COUNT == 4
bool res = ContainsSameAccessFast(s, a, sync_epoch, is_write);
+ // NOTE: this check can fail if the shadow is concurrently mutated
+ // by other threads.
DCHECK_EQ(res, ContainsSameAccessSlow(s, a, sync_epoch, is_write));
return res;
#else
diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h
index 1b590b8..591d10d 100644
--- a/lib/tsan/rtl/tsan_rtl.h
+++ b/lib/tsan/rtl/tsan_rtl.h
@@ -53,15 +53,8 @@
namespace __tsan {
#ifndef TSAN_GO
-#if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
-const uptr kAllocatorSpace = 0x7d0000000000ULL;
-#else
-const uptr kAllocatorSpace = 0x7d0000000000ULL;
-#endif
-const uptr kAllocatorSize = 0x10000000000ULL; // 1T.
-
struct MapUnmapCallback;
-typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0,
+typedef SizeClassAllocator64<kHeapMemBeg, kHeapMemEnd - kHeapMemBeg, 0,
DefaultSizeClassMap, MapUnmapCallback> PrimaryAllocator;
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
typedef LargeMmapAllocator<MapUnmapCallback> SecondaryAllocator;
@@ -312,6 +305,9 @@
struct JmpBuf {
uptr sp;
uptr mangled_sp;
+ int int_signal_send;
+ bool in_blocking_func;
+ uptr in_signal_handler;
uptr *shadow_stack_pos;
};
@@ -360,7 +356,7 @@
const int unique_id;
bool in_symbolizer;
bool in_ignored_lib;
- bool is_alive;
+ bool is_dead;
bool is_freeing;
bool is_vptr_access;
const uptr stk_addr;
@@ -373,11 +369,12 @@
DDPhysicalThread *dd_pt;
DDLogicalThread *dd_lt;
- bool in_signal_handler;
+ atomic_uintptr_t in_signal_handler;
SignalContext *signal_ctx;
DenseSlabAllocCache block_cache;
DenseSlabAllocCache sync_cache;
+ DenseSlabAllocCache clock_cache;
#ifndef TSAN_GO
u32 last_sleep_stack_id;
@@ -422,6 +419,7 @@
void OnStarted(void *arg);
void OnCreated(void *arg);
void OnReset();
+ void OnDetached(void *arg);
};
struct RacyStacks {
@@ -470,6 +468,8 @@
InternalMmapVector<FiredSuppression> fired_suppressions;
DDetector *dd;
+ ClockAlloc clock_alloc;
+
Flags flags;
u64 stat[StatCnt];
@@ -498,9 +498,9 @@
explicit ScopedReport(ReportType typ);
~ScopedReport();
- void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack,
+ void AddMemoryAccess(uptr addr, Shadow s, StackTrace stack,
const MutexSet *mset);
- void AddStack(const StackTrace *stack, bool suppressable = false);
+ void AddStack(StackTrace stack, bool suppressable = false);
void AddThread(const ThreadContext *tctx, bool suppressable = false);
void AddThread(int unique_tid, bool suppressable = false);
void AddUniqueTid(int unique_tid);
@@ -524,7 +524,20 @@
void operator = (const ScopedReport&);
};
-void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset);
+void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
+ MutexSet *mset);
+
+template<typename StackTraceTy>
+void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack) {
+ uptr size = thr->shadow_stack_pos - thr->shadow_stack;
+ uptr start = 0;
+ if (size + !!toppc > kStackTraceMax) {
+ start = size + !!toppc - kStackTraceMax;
+ size = kStackTraceMax - !!toppc;
+ }
+ stack->Init(&thr->shadow_stack[start], size, toppc);
+}
+
void StatAggregate(u64 *dst, u64 *src);
void StatOutput(u64 *stat);
@@ -551,9 +564,8 @@
void ReportRace(ThreadState *thr);
bool OutputReport(ThreadState *thr, const ScopedReport &srep);
-bool IsFiredSuppression(Context *ctx,
- const ScopedReport &srep,
- const StackTrace &trace);
+bool IsFiredSuppression(Context *ctx, const ScopedReport &srep,
+ StackTrace trace);
bool IsExpectedReport(uptr addr, uptr size);
void PrintMatchedBenignRaces();
bool FrameIsInternal(const ReportStack *frame);
@@ -574,7 +586,7 @@
u32 CurrentStackId(ThreadState *thr, uptr pc);
ReportStack *SymbolizeStackId(u32 stack_id);
void PrintCurrentStack(ThreadState *thr, uptr pc);
-void PrintCurrentStackSlow(); // uses libunwind
+void PrintCurrentStackSlow(uptr pc); // uses libunwind
void Initialize(ThreadState *thr);
int Finalize(ThreadState *thr);
@@ -654,6 +666,12 @@
void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD
void Acquire(ThreadState *thr, uptr pc, uptr addr);
+// AcquireGlobal synchronizes the current thread with all other threads.
+// In terms of happens-before relation, it draws a HB edge from all threads
+// (where they happen to execute right now) to the current thread. We use it to
+// handle Go finalizers. Namely, finalizer goroutine executes AcquireGlobal
+// right before executing finalizers. This provides a coarse, but simple
+// approximation of the actual required synchronization.
void AcquireGlobal(ThreadState *thr, uptr pc);
void Release(ThreadState *thr, uptr pc, uptr addr);
void ReleaseStore(ThreadState *thr, uptr pc, uptr addr);
diff --git a/lib/tsan/rtl/tsan_rtl_amd64.S b/lib/tsan/rtl/tsan_rtl_amd64.S
index 11c19a7..8db62f9 100644
--- a/lib/tsan/rtl/tsan_rtl_amd64.S
+++ b/lib/tsan/rtl/tsan_rtl_amd64.S
@@ -172,10 +172,15 @@
CFI_ADJUST_CFA_OFFSET(8)
CFI_REL_OFFSET(%rdi, 0)
// obtain %rsp
+#if defined(__FreeBSD__)
+ lea 8(%rsp), %rdi
+ mov %rdi, %rsi
+#else
lea 16(%rsp), %rdi
mov %rdi, %rsi
xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
rol $0x11, %rsi
+#endif
// call tsan interceptor
call __tsan_setjmp
// restore env parameter
@@ -199,10 +204,15 @@
CFI_ADJUST_CFA_OFFSET(8)
CFI_REL_OFFSET(%rdi, 0)
// obtain %rsp
+#if defined(__FreeBSD__)
+ lea 8(%rsp), %rdi
+ mov %rdi, %rsi
+#else
lea 16(%rsp), %rdi
mov %rdi, %rsi
xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
rol $0x11, %rsi
+#endif
// call tsan interceptor
call __tsan_setjmp
// restore env parameter
@@ -233,10 +243,15 @@
sub $8, %rsp
CFI_ADJUST_CFA_OFFSET(8)
// obtain %rsp
+#if defined(__FreeBSD__)
+ lea 24(%rsp), %rdi
+ mov %rdi, %rsi
+#else
lea 32(%rsp), %rdi
mov %rdi, %rsi
xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
rol $0x11, %rsi
+#endif
// call tsan interceptor
call __tsan_setjmp
// unalign stack frame
@@ -274,10 +289,15 @@
sub $8, %rsp
CFI_ADJUST_CFA_OFFSET(8)
// obtain %rsp
+#if defined(__FreeBSD__)
+ lea 24(%rsp), %rdi
+ mov %rdi, %rsi
+#else
lea 32(%rsp), %rdi
mov %rdi, %rsi
xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
rol $0x11, %rsi
+#endif
// call tsan interceptor
call __tsan_setjmp
// unalign stack frame
@@ -298,7 +318,7 @@
CFI_ENDPROC
.size __sigsetjmp, .-__sigsetjmp
-#ifdef __linux__
+#if defined(__FreeBSD__) || defined(__linux__)
/* We do not need executable stack. */
.section .note.GNU-stack,"",@progbits
#endif
diff --git a/lib/tsan/rtl/tsan_rtl_mutex.cc b/lib/tsan/rtl/tsan_rtl_mutex.cc
index 4cf47ec..0807869 100644
--- a/lib/tsan/rtl/tsan_rtl_mutex.cc
+++ b/lib/tsan/rtl/tsan_rtl_mutex.cc
@@ -59,9 +59,9 @@
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(typ);
rep.AddMutex(mid);
- StackTrace trace;
- trace.ObtainCurrent(thr, pc);
- rep.AddStack(&trace, true);
+ VarSizeStackTrace trace;
+ ObtainCurrentStack(thr, pc, &trace);
+ rep.AddStack(trace, true);
rep.AddLocation(addr, 1);
OutputReport(thr, rep);
}
@@ -103,7 +103,7 @@
SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr);
if (s == 0)
return;
- if (flags()->detect_deadlocks) {
+ if (common_flags()->detect_deadlocks) {
Callback cb(thr, pc);
ctx->dd->MutexDestroy(&cb, &s->dd);
ctx->dd->MutexInit(&cb, &s->dd);
@@ -118,25 +118,25 @@
u64 mid = s->GetId();
u32 last_lock = s->last_lock;
if (!unlock_locked)
- s->Reset(); // must not reset it before the report is printed
+ s->Reset(thr); // must not reset it before the report is printed
s->mtx.Unlock();
if (unlock_locked) {
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(ReportTypeMutexDestroyLocked);
rep.AddMutex(mid);
- StackTrace trace;
- trace.ObtainCurrent(thr, pc);
- rep.AddStack(&trace);
+ VarSizeStackTrace trace;
+ ObtainCurrentStack(thr, pc, &trace);
+ rep.AddStack(trace);
FastState last(last_lock);
RestoreStack(last.tid(), last.epoch(), &trace, 0);
- rep.AddStack(&trace, true);
+ rep.AddStack(trace, true);
rep.AddLocation(addr, 1);
OutputReport(thr, rep);
}
if (unlock_locked) {
SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr);
if (s != 0) {
- s->Reset();
+ s->Reset(thr);
s->mtx.Unlock();
}
}
@@ -172,7 +172,7 @@
}
s->recursion += rec;
thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
- if (flags()->detect_deadlocks && (s->recursion - rec) == 0) {
+ if (common_flags()->detect_deadlocks && (s->recursion - rec) == 0) {
Callback cb(thr, pc);
if (!try_lock)
ctx->dd->MutexBeforeLock(&cb, &s->dd, true);
@@ -183,7 +183,7 @@
// Can't touch s after this point.
if (report_double_lock)
ReportMutexMisuse(thr, pc, ReportTypeMutexDoubleLock, addr, mid);
- if (flags()->detect_deadlocks) {
+ if (common_flags()->detect_deadlocks) {
Callback cb(thr, pc);
ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
}
@@ -215,7 +215,8 @@
}
}
thr->mset.Del(s->GetId(), true);
- if (flags()->detect_deadlocks && s->recursion == 0 && !report_bad_unlock) {
+ if (common_flags()->detect_deadlocks && s->recursion == 0 &&
+ !report_bad_unlock) {
Callback cb(thr, pc);
ctx->dd->MutexBeforeUnlock(&cb, &s->dd, true);
}
@@ -224,7 +225,7 @@
// Can't touch s after this point.
if (report_bad_unlock)
ReportMutexMisuse(thr, pc, ReportTypeMutexBadUnlock, addr, mid);
- if (flags()->detect_deadlocks && !report_bad_unlock) {
+ if (common_flags()->detect_deadlocks && !report_bad_unlock) {
Callback cb(thr, pc);
ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
}
@@ -249,7 +250,7 @@
AcquireImpl(thr, pc, &s->clock);
s->last_lock = thr->fast_state.raw();
thr->mset.Add(s->GetId(), false, thr->fast_state.epoch());
- if (flags()->detect_deadlocks && s->recursion == 0) {
+ if (common_flags()->detect_deadlocks && s->recursion == 0) {
Callback cb(thr, pc);
if (!trylock)
ctx->dd->MutexBeforeLock(&cb, &s->dd, false);
@@ -260,7 +261,7 @@
// Can't touch s after this point.
if (report_bad_lock)
ReportMutexMisuse(thr, pc, ReportTypeMutexBadReadLock, addr, mid);
- if (flags()->detect_deadlocks) {
+ if (common_flags()->detect_deadlocks) {
Callback cb(thr, pc);
ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
}
@@ -282,7 +283,7 @@
}
}
ReleaseImpl(thr, pc, &s->read_clock);
- if (flags()->detect_deadlocks && s->recursion == 0) {
+ if (common_flags()->detect_deadlocks && s->recursion == 0) {
Callback cb(thr, pc);
ctx->dd->MutexBeforeUnlock(&cb, &s->dd, false);
}
@@ -292,7 +293,7 @@
thr->mset.Del(mid, false);
if (report_bad_unlock)
ReportMutexMisuse(thr, pc, ReportTypeMutexBadReadUnlock, addr, mid);
- if (flags()->detect_deadlocks) {
+ if (common_flags()->detect_deadlocks) {
Callback cb(thr, pc);
ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
}
@@ -330,7 +331,7 @@
report_bad_unlock = true;
}
thr->mset.Del(s->GetId(), write);
- if (flags()->detect_deadlocks && s->recursion == 0) {
+ if (common_flags()->detect_deadlocks && s->recursion == 0) {
Callback cb(thr, pc);
ctx->dd->MutexBeforeUnlock(&cb, &s->dd, write);
}
@@ -339,7 +340,7 @@
// Can't touch s after this point.
if (report_bad_unlock)
ReportMutexMisuse(thr, pc, ReportTypeMutexBadUnlock, addr, mid);
- if (flags()->detect_deadlocks) {
+ if (common_flags()->detect_deadlocks) {
Callback cb(thr, pc);
ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
}
@@ -429,7 +430,7 @@
if (thr->ignore_sync)
return;
thr->clock.set(thr->fast_state.epoch());
- thr->clock.acquire(c);
+ thr->clock.acquire(&thr->clock_cache, c);
StatInc(thr, StatSyncAcquire);
}
@@ -438,7 +439,7 @@
return;
thr->clock.set(thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.release(c);
+ thr->clock.release(&thr->clock_cache, c);
StatInc(thr, StatSyncRelease);
}
@@ -447,7 +448,7 @@
return;
thr->clock.set(thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.ReleaseStore(c);
+ thr->clock.ReleaseStore(&thr->clock_cache, c);
StatInc(thr, StatSyncRelease);
}
@@ -456,7 +457,7 @@
return;
thr->clock.set(thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.acq_rel(c);
+ thr->clock.acq_rel(&thr->clock_cache, c);
StatInc(thr, StatSyncAcquire);
StatInc(thr, StatSyncRelease);
}
@@ -471,21 +472,17 @@
rep.AddUniqueTid((int)r->loop[i].thr_ctx);
rep.AddThread((int)r->loop[i].thr_ctx);
}
- InternalScopedBuffer<StackTrace> stacks(2 * DDReport::kMaxLoopSize);
uptr dummy_pc = 0x42;
for (int i = 0; i < r->n; i++) {
- uptr size;
for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
u32 stk = r->loop[i].stk[j];
if (stk) {
- const uptr *trace = StackDepotGet(stk, &size);
- stacks[i].Init(const_cast<uptr *>(trace), size);
+ rep.AddStack(StackDepotGet(stk), true);
} else {
// Sometimes we fail to extract the stack trace (FIXME: investigate),
// but we should still produce some stack trace in the report.
- stacks[i].Init(&dummy_pc, 1);
+ rep.AddStack(StackTrace(&dummy_pc, 1), true);
}
- rep.AddStack(&stacks[i], true);
}
}
OutputReport(thr, rep);
diff --git a/lib/tsan/rtl/tsan_rtl_report.cc b/lib/tsan/rtl/tsan_rtl_report.cc
index b75c319..c7a00c9 100644
--- a/lib/tsan/rtl/tsan_rtl_report.cc
+++ b/lib/tsan/rtl/tsan_rtl_report.cc
@@ -30,7 +30,7 @@
using namespace __sanitizer; // NOLINT
-static ReportStack *SymbolizeStack(const StackTrace& trace);
+static ReportStack *SymbolizeStack(StackTrace trace);
void TsanCheckFailed(const char *file, int line, const char *cond,
u64 v1, u64 v2) {
@@ -41,7 +41,7 @@
Printf("FATAL: ThreadSanitizer CHECK failed: "
"%s:%d \"%s\" (0x%zx, 0x%zx)\n",
file, line, cond, (uptr)v1, (uptr)v2);
- PrintCurrentStackSlow();
+ PrintCurrentStackSlow(StackTrace::GetCurrentPc());
Die();
}
@@ -59,27 +59,16 @@
static void StackStripMain(ReportStack *stack) {
ReportStack *last_frame = 0;
ReportStack *last_frame2 = 0;
- const char *prefix = "__interceptor_";
- uptr prefix_len = internal_strlen(prefix);
- const char *path_prefix = flags()->strip_path_prefix;
- uptr path_prefix_len = internal_strlen(path_prefix);
- char *pos;
for (ReportStack *ent = stack; ent; ent = ent->next) {
- if (ent->func && 0 == internal_strncmp(ent->func, prefix, prefix_len))
- ent->func += prefix_len;
- if (ent->file && (pos = internal_strstr(ent->file, path_prefix)))
- ent->file = pos + path_prefix_len;
- if (ent->file && ent->file[0] == '.' && ent->file[1] == '/')
- ent->file += 2;
last_frame2 = last_frame;
last_frame = ent;
}
if (last_frame2 == 0)
return;
- const char *last = last_frame->func;
+ const char *last = last_frame->info.function;
#ifndef TSAN_GO
- const char *last2 = last_frame2->func;
+ const char *last2 = last_frame2->info.function;
// Strip frame above 'main'
if (last2 && 0 == internal_strcmp(last2, "main")) {
last_frame2->next = 0;
@@ -107,39 +96,36 @@
ReportStack *SymbolizeStackId(u32 stack_id) {
if (stack_id == 0)
return 0;
- uptr ssz = 0;
- const uptr *stack = StackDepotGet(stack_id, &ssz);
- if (stack == 0)
- return 0;
- StackTrace trace;
- trace.Init(stack, ssz);
- return SymbolizeStack(trace);
+ StackTrace stack = StackDepotGet(stack_id);
+ if (stack.trace == nullptr)
+ return nullptr;
+ return SymbolizeStack(stack);
}
-static ReportStack *SymbolizeStack(const StackTrace& trace) {
- if (trace.IsEmpty())
+static ReportStack *SymbolizeStack(StackTrace trace) {
+ if (trace.size == 0)
return 0;
ReportStack *stack = 0;
- for (uptr si = 0; si < trace.Size(); si++) {
- const uptr pc = trace.Get(si);
+ for (uptr si = 0; si < trace.size; si++) {
+ const uptr pc = trace.trace[si];
#ifndef TSAN_GO
// We obtain the return address, that is, address of the next instruction,
// so offset it by 1 byte.
- const uptr pc1 = __sanitizer::StackTrace::GetPreviousInstructionPc(pc);
+ const uptr pc1 = StackTrace::GetPreviousInstructionPc(pc);
#else
// FIXME(dvyukov): Go sometimes uses address of a function as top pc.
uptr pc1 = pc;
- if (si != trace.Size() - 1)
+ if (si != trace.size - 1)
pc1 -= 1;
#endif
ReportStack *ent = SymbolizeCode(pc1);
CHECK_NE(ent, 0);
ReportStack *last = ent;
while (last->next) {
- last->pc = pc; // restore original pc for report
+ last->info.address = pc; // restore original pc for report
last = last->next;
}
- last->pc = pc; // restore original pc for report
+ last->info.address = pc; // restore original pc for report
last->next = stack;
stack = ent;
}
@@ -162,14 +148,14 @@
DestroyAndFree(rep_);
}
-void ScopedReport::AddStack(const StackTrace *stack, bool suppressable) {
+void ScopedReport::AddStack(StackTrace stack, bool suppressable) {
ReportStack **rs = rep_->stacks.PushBack();
- *rs = SymbolizeStack(*stack);
+ *rs = SymbolizeStack(stack);
(*rs)->suppressable = suppressable;
}
-void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
- const StackTrace *stack, const MutexSet *mset) {
+void ScopedReport::AddMemoryAccess(uptr addr, Shadow s, StackTrace stack,
+ const MutexSet *mset) {
void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
ReportMop *mop = new(mem) ReportMop;
rep_->mops.PushBack(mop);
@@ -178,7 +164,7 @@
mop->size = s.size();
mop->write = s.IsWrite();
mop->atomic = s.IsAtomic();
- mop->stack = SymbolizeStack(*stack);
+ mop->stack = SymbolizeStack(stack);
if (mop->stack)
mop->stack->suppressable = true;
for (uptr i = 0; i < mset->Size(); i++) {
@@ -316,15 +302,12 @@
int fd = -1;
int creat_tid = -1;
u32 creat_stack = 0;
- if (FdLocation(addr, &fd, &creat_tid, &creat_stack)
- || FdLocation(AlternativeAddress(addr), &fd, &creat_tid, &creat_stack)) {
- void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
- ReportLocation *loc = new(mem) ReportLocation();
- rep_->locs.PushBack(loc);
- loc->type = ReportLocationFD;
+ if (FdLocation(addr, &fd, &creat_tid, &creat_stack)) {
+ ReportLocation *loc = ReportLocation::New(ReportLocationFD);
loc->fd = fd;
loc->tid = creat_tid;
loc->stack = SymbolizeStackId(creat_stack);
+ rep_->locs.PushBack(loc);
ThreadContext *tctx = FindThreadByUidLocked(creat_tid);
if (tctx)
AddThread(tctx);
@@ -339,33 +322,25 @@
}
if (b != 0) {
ThreadContext *tctx = FindThreadByTidLocked(b->tid);
- void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
- ReportLocation *loc = new(mem) ReportLocation();
- rep_->locs.PushBack(loc);
- loc->type = ReportLocationHeap;
- loc->addr = (uptr)allocator()->GetBlockBegin((void*)addr);
- loc->size = b->siz;
+ ReportLocation *loc = ReportLocation::New(ReportLocationHeap);
+ loc->heap_chunk_start = (uptr)allocator()->GetBlockBegin((void *)addr);
+ loc->heap_chunk_size = b->siz;
loc->tid = tctx ? tctx->tid : b->tid;
- loc->name = 0;
- loc->file = 0;
- loc->line = 0;
- loc->stack = 0;
loc->stack = SymbolizeStackId(b->stk);
+ rep_->locs.PushBack(loc);
if (tctx)
AddThread(tctx);
return;
}
bool is_stack = false;
if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) {
- void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
- ReportLocation *loc = new(mem) ReportLocation();
- rep_->locs.PushBack(loc);
- loc->type = is_stack ? ReportLocationStack : ReportLocationTLS;
+ ReportLocation *loc =
+ ReportLocation::New(is_stack ? ReportLocationStack : ReportLocationTLS);
loc->tid = tctx->tid;
+ rep_->locs.PushBack(loc);
AddThread(tctx);
}
- ReportLocation *loc = SymbolizeData(addr);
- if (loc) {
+ if (ReportLocation *loc = SymbolizeData(addr)) {
loc->suppressable = true;
rep_->locs.PushBack(loc);
return;
@@ -387,7 +362,8 @@
return rep_;
}
-void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) {
+void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
+ MutexSet *mset) {
// This function restores stack trace and mutex set for the thread/epoch.
// It does so by getting stack trace and mutex set at the beginning of
// trace part, and then replaying the trace till the given epoch.
@@ -412,13 +388,13 @@
DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx);
InternalScopedBuffer<uptr> stack(kShadowStackSize);
- for (uptr i = 0; i < hdr->stack0.Size(); i++) {
- stack[i] = hdr->stack0.Get(i);
+ for (uptr i = 0; i < hdr->stack0.size; i++) {
+ stack[i] = hdr->stack0.trace[i];
DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]);
}
if (mset)
*mset = hdr->mset0;
- uptr pos = hdr->stack0.Size();
+ uptr pos = hdr->stack0.size;
Event *events = (Event*)GetThreadTrace(tid);
for (uptr i = ebegin; i <= eend; i++) {
Event ev = events[i];
@@ -453,13 +429,13 @@
stk->Init(stack.data(), pos);
}
-static bool HandleRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
- uptr addr_min, uptr addr_max) {
+static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
+ uptr addr_min, uptr addr_max) {
bool equal_stack = false;
RacyStacks hash;
if (flags()->suppress_equal_stacks) {
- hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr));
- hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr));
+ hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
+ hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) {
if (hash == ctx->racy_stacks[i]) {
DPrintf("ThreadSanitizer: suppressing report as doubled (stack)\n");
@@ -492,12 +468,12 @@
return false;
}
-static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
- uptr addr_min, uptr addr_max) {
+static void AddRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
+ uptr addr_min, uptr addr_max) {
if (flags()->suppress_equal_stacks) {
RacyStacks hash;
- hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr));
- hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr));
+ hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
+ hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
ctx->racy_stacks.PushBack(hash);
}
if (flags()->suppress_equal_addresses) {
@@ -538,15 +514,14 @@
return true;
}
-bool IsFiredSuppression(Context *ctx,
- const ScopedReport &srep,
- const StackTrace &trace) {
+bool IsFiredSuppression(Context *ctx, const ScopedReport &srep,
+ StackTrace trace) {
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++) {
+ for (uptr j = 0; j < trace.size; j++) {
FiredSuppression *s = &ctx->fired_suppressions[k];
- if (trace.Get(j) == s->pc) {
+ if (trace.trace[j] == s->pc) {
if (s->supp)
s->supp->hit_count++;
return true;
@@ -573,45 +548,13 @@
}
bool FrameIsInternal(const ReportStack *frame) {
- return frame != 0 && frame->file != 0
- && (internal_strstr(frame->file, "tsan_interceptors.cc") ||
- internal_strstr(frame->file, "sanitizer_common_interceptors.inc") ||
- internal_strstr(frame->file, "tsan_interface_"));
-}
-
-// On programs that use Java we see weird reports like:
-// WARNING: ThreadSanitizer: data race (pid=22512)
-// Read of size 8 at 0x7d2b00084318 by thread 100:
-// #0 memcpy tsan_interceptors.cc:406 (foo+0x00000d8dfae3)
-// #1 <null> <null>:0 (0x7f7ad9b40193)
-// Previous write of size 8 at 0x7d2b00084318 by thread 105:
-// #0 strncpy tsan_interceptors.cc:501 (foo+0x00000d8e0919)
-// #1 <null> <null>:0 (0x7f7ad9b42707)
-static bool IsJavaNonsense(const ReportDesc *rep) {
-#ifndef TSAN_GO
- for (uptr i = 0; i < rep->mops.Size(); i++) {
- ReportMop *mop = rep->mops[i];
- ReportStack *frame = mop->stack;
- if (frame == 0
- || (frame->func == 0 && frame->file == 0 && frame->line == 0
- && frame->module == 0)) {
- return true;
- }
- if (FrameIsInternal(frame)) {
- frame = frame->next;
- if (frame == 0
- || (frame->func == 0 && frame->file == 0 && frame->line == 0
- && frame->module == 0)) {
- if (frame) {
- FiredSuppression supp = {rep->typ, frame->pc, 0};
- ctx->fired_suppressions.push_back(supp);
- }
- return true;
- }
- }
- }
-#endif
- return false;
+ if (frame == 0)
+ return false;
+ const char *file = frame->info.file;
+ return file != 0 &&
+ (internal_strstr(file, "tsan_interceptors.cc") ||
+ internal_strstr(file, "sanitizer_common_interceptors.inc") ||
+ internal_strstr(file, "tsan_interface_"));
}
static bool RaceBetweenAtomicAndFree(ThreadState *thr) {
@@ -663,7 +606,9 @@
ThreadRegistryLock l0(ctx->thread_registry);
ReportType typ = ReportTypeRace;
- if (thr->is_vptr_access)
+ if (thr->is_vptr_access && freed)
+ typ = ReportTypeVptrUseAfterFree;
+ else if (thr->is_vptr_access)
typ = ReportTypeVptrRace;
else if (freed)
typ = ReportTypeUseAfterFree;
@@ -671,9 +616,9 @@
if (IsFiredSuppression(ctx, rep, addr))
return;
const uptr kMop = 2;
- StackTrace traces[kMop];
+ VarSizeStackTrace traces[kMop];
const uptr toppc = TraceTopPC(thr);
- traces[0].ObtainCurrent(thr, toppc);
+ ObtainCurrentStack(thr, toppc, &traces[0]);
if (IsFiredSuppression(ctx, rep, traces[0]))
return;
InternalScopedBuffer<MutexSet> mset2(1);
@@ -688,13 +633,10 @@
for (uptr i = 0; i < kMop; i++) {
Shadow s(thr->racy_state[i]);
- rep.AddMemoryAccess(addr, s, &traces[i],
+ rep.AddMemoryAccess(addr, s, traces[i],
i == 0 ? &thr->mset : mset2.data());
}
- if (flags()->suppress_java && IsJavaNonsense(rep.GetReport()))
- return;
-
for (uptr i = 0; i < kMop; i++) {
FastState s(thr->racy_state[i]);
ThreadContext *tctx = static_cast<ThreadContext*>(
@@ -721,26 +663,33 @@
}
void PrintCurrentStack(ThreadState *thr, uptr pc) {
- StackTrace trace;
- trace.ObtainCurrent(thr, pc);
+ VarSizeStackTrace trace;
+ ObtainCurrentStack(thr, pc, &trace);
PrintStack(SymbolizeStack(trace));
}
-void PrintCurrentStackSlow() {
+void PrintCurrentStackSlow(uptr pc) {
#ifndef TSAN_GO
- __sanitizer::StackTrace *ptrace = new(internal_alloc(MBlockStackTrace,
- sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace;
- ptrace->Unwind(kStackTraceMax, __sanitizer::StackTrace::GetCurrentPc(), 0, 0,
- 0, 0, false);
+ BufferedStackTrace *ptrace =
+ new(internal_alloc(MBlockStackTrace, sizeof(BufferedStackTrace)))
+ BufferedStackTrace();
+ ptrace->Unwind(kStackTraceMax, pc, 0, 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];
- ptrace->trace[ptrace->size - i - 1] = tmp;
+ uptr tmp = ptrace->trace_buffer[i];
+ ptrace->trace_buffer[i] = ptrace->trace_buffer[ptrace->size - i - 1];
+ ptrace->trace_buffer[ptrace->size - i - 1] = tmp;
}
- StackTrace trace;
- trace.Init(ptrace->trace, ptrace->size);
- PrintStack(SymbolizeStack(trace));
+ PrintStack(SymbolizeStack(*ptrace));
#endif
}
} // namespace __tsan
+
+using namespace __tsan;
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_stack_trace() {
+ PrintCurrentStackSlow(StackTrace::GetCurrentPc());
+}
+} // extern "C"
diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc
index 94bf754..256a95c 100644
--- a/lib/tsan/rtl/tsan_rtl_thread.cc
+++ b/lib/tsan/rtl/tsan_rtl_thread.cc
@@ -36,13 +36,13 @@
#endif
void ThreadContext::OnDead() {
- sync.Reset();
+ CHECK_EQ(sync.size(), 0);
}
void ThreadContext::OnJoined(void *arg) {
ThreadState *caller_thr = static_cast<ThreadState *>(arg);
AcquireImpl(caller_thr, 0, &sync);
- sync.Reset();
+ sync.Reset(&caller_thr->clock_cache);
}
struct OnCreatedArgs {
@@ -65,11 +65,16 @@
}
void ThreadContext::OnReset() {
- sync.Reset();
+ CHECK_EQ(sync.size(), 0);
FlushUnneededShadowMemory(GetThreadTrace(tid), TraceSize() * sizeof(Event));
//!!! FlushUnneededShadowMemory(GetThreadTraceHeader(tid), sizeof(Trace));
}
+void ThreadContext::OnDetached(void *arg) {
+ ThreadState *thr1 = static_cast<ThreadState*>(arg);
+ sync.Reset(&thr1->clock_cache);
+}
+
struct OnStartedArgs {
ThreadState *thr;
uptr stk_addr;
@@ -102,7 +107,7 @@
#ifndef TSAN_GO
AllocatorThreadStart(thr);
#endif
- if (flags()->detect_deadlocks) {
+ if (common_flags()->detect_deadlocks) {
thr->dd_pt = ctx->dd->CreatePhysicalThread();
thr->dd_lt = ctx->dd->CreateLogicalThread(unique_id);
}
@@ -113,12 +118,11 @@
Trace *thr_trace = ThreadTrace(thr->tid);
thr_trace->headers[trace].epoch0 = epoch0;
StatInc(thr, StatSyncAcquire);
- sync.Reset();
+ sync.Reset(&thr->clock_cache);
DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx "
"tls_addr=%zx tls_size=%zx\n",
tid, (uptr)epoch0, args->stk_addr, args->stk_size,
args->tls_addr, args->tls_size);
- thr->is_alive = true;
}
void ThreadContext::OnFinished() {
@@ -130,10 +134,11 @@
}
epoch1 = thr->fast_state.epoch();
- if (flags()->detect_deadlocks) {
+ if (common_flags()->detect_deadlocks) {
ctx->dd->DestroyPhysicalThread(thr->dd_pt);
ctx->dd->DestroyLogicalThread(thr->dd_lt);
}
+ ctx->clock_alloc.FlushCache(&thr->clock_cache);
ctx->metamap.OnThreadIdle(thr);
#ifndef TSAN_GO
AllocatorThreadFinish(thr);
@@ -278,7 +283,7 @@
DontNeedShadowFor(thr->stk_addr, thr->stk_size);
if (thr->tls_addr && thr->tls_size)
DontNeedShadowFor(thr->tls_addr, thr->tls_size);
- thr->is_alive = false;
+ thr->is_dead = true;
ctx->thread_registry->FinishThread(thr->tid);
}
@@ -307,7 +312,7 @@
void ThreadDetach(ThreadState *thr, uptr pc, int tid) {
CHECK_GT(tid, 0);
CHECK_LT(tid, kMaxTid);
- ctx->thread_registry->DetachThread(tid);
+ ctx->thread_registry->DetachThread(tid, thr);
}
void ThreadSetName(ThreadState *thr, const char *name) {
diff --git a/lib/tsan/rtl/tsan_stack_trace.cc b/lib/tsan/rtl/tsan_stack_trace.cc
index a8374f4..ceca3f8 100644
--- a/lib/tsan/rtl/tsan_stack_trace.cc
+++ b/lib/tsan/rtl/tsan_stack_trace.cc
@@ -10,103 +10,37 @@
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
//===----------------------------------------------------------------------===//
-//#include "sanitizer_common/sanitizer_placement_new.h"
#include "tsan_stack_trace.h"
#include "tsan_rtl.h"
#include "tsan_mman.h"
namespace __tsan {
-StackTrace::StackTrace()
- : n_()
- , s_()
- , c_() {
+VarSizeStackTrace::VarSizeStackTrace()
+ : StackTrace(nullptr, 0), trace_buffer(nullptr) {}
+
+VarSizeStackTrace::~VarSizeStackTrace() {
+ ResizeBuffer(0);
}
-StackTrace::StackTrace(uptr *buf, uptr cnt)
- : n_()
- , s_(buf)
- , c_(cnt) {
- CHECK_NE(buf, 0);
- CHECK_NE(cnt, 0);
-}
-
-StackTrace::~StackTrace() {
- Reset();
-}
-
-void StackTrace::Reset() {
- if (s_ && !c_) {
- CHECK_NE(n_, 0);
- internal_free(s_);
- s_ = 0;
+void VarSizeStackTrace::ResizeBuffer(uptr new_size) {
+ if (trace_buffer) {
+ internal_free(trace_buffer);
}
- n_ = 0;
+ trace_buffer =
+ (new_size > 0)
+ ? (uptr *)internal_alloc(MBlockStackTrace,
+ new_size * sizeof(trace_buffer[0]))
+ : nullptr;
+ trace = trace_buffer;
+ size = new_size;
}
-void StackTrace::Init(const uptr *pcs, uptr cnt) {
- Reset();
- if (cnt == 0)
- return;
- if (c_) {
- CHECK_NE(s_, 0);
- CHECK_LE(cnt, c_);
- } else {
- s_ = (uptr*)internal_alloc(MBlockStackTrace, cnt * sizeof(s_[0]));
- }
- n_ = cnt;
- internal_memcpy(s_, pcs, cnt * sizeof(s_[0]));
-}
-
-void StackTrace::ObtainCurrent(ThreadState *thr, uptr toppc) {
- Reset();
- n_ = thr->shadow_stack_pos - thr->shadow_stack;
- if (n_ + !!toppc == 0)
- return;
- uptr start = 0;
- if (c_) {
- CHECK_NE(s_, 0);
- if (n_ + !!toppc > c_) {
- start = n_ - c_ + !!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]));
- }
- for (uptr i = 0; i < n_; i++)
- s_[i] = thr->shadow_stack[start + i];
- if (toppc) {
- s_[n_] = toppc;
- n_++;
- }
-}
-
-void StackTrace::CopyFrom(const StackTrace& other) {
- Reset();
- Init(other.Begin(), other.Size());
-}
-
-bool StackTrace::IsEmpty() const {
- return n_ == 0;
-}
-
-uptr StackTrace::Size() const {
- return n_;
-}
-
-uptr StackTrace::Get(uptr i) const {
- CHECK_LT(i, n_);
- return s_[i];
-}
-
-const uptr *StackTrace::Begin() const {
- return s_;
+void VarSizeStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) {
+ ResizeBuffer(cnt + !!extra_top_pc);
+ internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0]));
+ if (extra_top_pc)
+ trace_buffer[cnt] = extra_top_pc;
}
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_stack_trace.h b/lib/tsan/rtl/tsan_stack_trace.h
index fe82f6e..5bf89bb 100644
--- a/lib/tsan/rtl/tsan_stack_trace.h
+++ b/lib/tsan/rtl/tsan_stack_trace.h
@@ -13,40 +13,25 @@
#ifndef TSAN_STACK_TRACE_H
#define TSAN_STACK_TRACE_H
-//#include "sanitizer_common/sanitizer_atomic.h"
-//#include "sanitizer_common/sanitizer_common.h"
-//#include "sanitizer_common/sanitizer_deadlock_detector_interface.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
#include "tsan_defs.h"
-//#include "tsan_clock.h"
-//#include "tsan_mutex.h"
-//#include "tsan_dense_alloc.h"
namespace __tsan {
-class StackTrace {
- public:
- StackTrace();
- // Initialized the object in "static mode",
- // in this mode it never calls malloc/free but uses the provided buffer.
- StackTrace(uptr *buf, uptr cnt);
- ~StackTrace();
- void Reset();
+// StackTrace which calls malloc/free to allocate the buffer for
+// addresses in stack traces.
+struct VarSizeStackTrace : public StackTrace {
+ uptr *trace_buffer; // Owned.
- void Init(const uptr *pcs, uptr cnt);
- void ObtainCurrent(ThreadState *thr, uptr toppc);
- bool IsEmpty() const;
- uptr Size() const;
- uptr Get(uptr i) const;
- const uptr *Begin() const;
- void CopyFrom(const StackTrace& other);
+ VarSizeStackTrace();
+ ~VarSizeStackTrace();
+ void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0);
private:
- uptr n_;
- uptr *s_;
- const uptr c_;
+ void ResizeBuffer(uptr new_size);
- StackTrace(const StackTrace&);
- void operator = (const StackTrace&);
+ VarSizeStackTrace(const VarSizeStackTrace &);
+ void operator=(const VarSizeStackTrace &);
};
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc
index 0670396..1c5bea0 100644
--- a/lib/tsan/rtl/tsan_suppressions.cc
+++ b/lib/tsan/rtl/tsan_suppressions.cc
@@ -41,55 +41,16 @@
namespace __tsan {
-static SuppressionContext* g_ctx;
-
-static char *ReadFile(const char *filename) {
- if (filename == 0 || filename[0] == 0)
- return 0;
- InternalScopedBuffer<char> tmp(4*1024);
- if (filename[0] == '/' || GetPwd() == 0)
- internal_snprintf(tmp.data(), tmp.size(), "%s", filename);
- else
- internal_snprintf(tmp.data(), tmp.size(), "%s/%s", GetPwd(), filename);
- uptr openrv = OpenFile(tmp.data(), false);
- if (internal_iserror(openrv)) {
- Printf("ThreadSanitizer: failed to open suppressions file '%s'\n",
- tmp.data());
- Die();
- }
- fd_t fd = openrv;
- const uptr fsize = internal_filesize(fd);
- if (fsize == (uptr)-1) {
- Printf("ThreadSanitizer: failed to stat suppressions file '%s'\n",
- tmp.data());
- Die();
- }
- char *buf = (char*)internal_alloc(MBlockSuppression, fsize + 1);
- if (fsize != internal_read(fd, buf, fsize)) {
- Printf("ThreadSanitizer: failed to read suppressions file '%s'\n",
- tmp.data());
- Die();
- }
- internal_close(fd);
- buf[fsize] = 0;
- return buf;
-}
+static bool suppressions_inited = false;
void InitializeSuppressions() {
- ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)];
- g_ctx = new(placeholder_) SuppressionContext;
- const char *supp = ReadFile(flags()->suppressions);
- g_ctx->Parse(supp);
+ CHECK(!suppressions_inited);
+ SuppressionContext::InitIfNecessary();
#ifndef TSAN_GO
- supp = __tsan_default_suppressions();
- g_ctx->Parse(supp);
- g_ctx->Parse(std_suppressions);
+ SuppressionContext::Get()->Parse(__tsan_default_suppressions());
+ SuppressionContext::Get()->Parse(std_suppressions);
#endif
-}
-
-SuppressionContext *GetSuppressionContext() {
- CHECK_NE(g_ctx, 0);
- return g_ctx;
+ suppressions_inited = true;
}
SuppressionType conv(ReportType typ) {
@@ -99,6 +60,8 @@
return SuppressionRace;
else if (typ == ReportTypeUseAfterFree)
return SuppressionRace;
+ else if (typ == ReportTypeVptrUseAfterFree)
+ return SuppressionRace;
else if (typ == ReportTypeThreadLeak)
return SuppressionThread;
else if (typ == ReportTypeMutexDestroyLocked)
@@ -122,50 +85,49 @@
}
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
- CHECK(g_ctx);
- if (!g_ctx->SuppressionCount() || stack == 0 || !stack->suppressable)
+ if (!SuppressionContext::Get()->SuppressionCount() || stack == 0 ||
+ !stack->suppressable)
return 0;
SuppressionType stype = conv(typ);
if (stype == SuppressionNone)
return 0;
Suppression *s;
for (const ReportStack *frame = stack; frame; frame = frame->next) {
- if (g_ctx->Match(frame->func, stype, &s) ||
- g_ctx->Match(frame->file, stype, &s) ||
- g_ctx->Match(frame->module, stype, &s)) {
+ const AddressInfo &info = frame->info;
+ if (SuppressionContext::Get()->Match(info.function, stype, &s) ||
+ SuppressionContext::Get()->Match(info.file, stype, &s) ||
+ SuppressionContext::Get()->Match(info.module, stype, &s)) {
DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ);
s->hit_count++;
*sp = s;
- return frame->pc;
+ return info.address;
}
}
return 0;
}
uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) {
- CHECK(g_ctx);
- if (!g_ctx->SuppressionCount() || loc == 0 ||
+ if (!SuppressionContext::Get()->SuppressionCount() || loc == 0 ||
loc->type != ReportLocationGlobal || !loc->suppressable)
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)) {
+ const DataInfo &global = loc->global;
+ if (SuppressionContext::Get()->Match(global.name, stype, &s) ||
+ SuppressionContext::Get()->Match(global.module, stype, &s)) {
DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ);
s->hit_count++;
*sp = s;
- return loc->addr;
+ return global.start;
}
return 0;
}
void PrintMatchedSuppressions() {
- CHECK(g_ctx);
InternalMmapVector<Suppression *> matched(1);
- g_ctx->GetMatched(&matched);
+ SuppressionContext::Get()->GetMatched(&matched);
if (!matched.size())
return;
int hit_count = 0;
diff --git a/lib/tsan/rtl/tsan_suppressions.h b/lib/tsan/rtl/tsan_suppressions.h
index fe7db58..c618b3d 100644
--- a/lib/tsan/rtl/tsan_suppressions.h
+++ b/lib/tsan/rtl/tsan_suppressions.h
@@ -22,7 +22,6 @@
void PrintMatchedSuppressions();
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp);
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 943aeb0..c08de6a 100644
--- a/lib/tsan/rtl/tsan_symbolize.cc
+++ b/lib/tsan/rtl/tsan_symbolize.cc
@@ -36,38 +36,6 @@
thr->ignore_interceptors--;
}
-ReportStack *NewReportStackEntry(uptr addr) {
- ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack,
- sizeof(ReportStack));
- internal_memset(ent, 0, sizeof(*ent));
- ent->pc = addr;
- return ent;
-}
-
-static ReportStack *NewReportStackEntry(const AddressInfo &info) {
- ReportStack *ent = NewReportStackEntry(info.address);
- ent->module = StripModuleName(info.module);
- ent->offset = info.module_offset;
- if (info.function)
- ent->func = internal_strdup(info.function);
- if (info.file)
- ent->file = internal_strdup(info.file);
- ent->line = info.line;
- ent->col = info.column;
- 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;
@@ -95,32 +63,29 @@
static char func_buf[1024];
static char file_buf[1024];
int line, col;
+ ReportStack *ent = ReportStack::New(addr);
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;
+ ent->info.function = internal_strdup(func_buf);
+ ent->info.file = internal_strdup(file_buf);
+ ent->info.line = line;
+ ent->info.column = col;
return ent;
}
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()->SymbolizePC(
+ uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(
addr, addr_frames.data(), kMaxAddrFrames);
if (addr_frames_num == 0)
- return NewReportStackEntry(addr);
+ return ReportStack::New(addr);
ReportStack *top = 0;
ReportStack *bottom = 0;
for (uptr i = 0; i < addr_frames_num; i++) {
- ReportStack *cur_entry = NewReportStackEntry(addr_frames[i]);
- CHECK(cur_entry);
- addr_frames[i].Clear();
+ ReportStack *cur_entry = ReportStack::New(addr);
+ cur_entry->info = addr_frames[i];
if (i == 0)
top = cur_entry;
else
@@ -132,23 +97,15 @@
ReportLocation *SymbolizeData(uptr addr) {
DataInfo info;
- if (!Symbolizer::Get()->SymbolizeData(addr, &info))
+ if (!Symbolizer::GetOrInit()->SymbolizeData(addr, &info))
return 0;
- ReportLocation *ent = (ReportLocation*)internal_alloc(MBlockReportStack,
- sizeof(ReportLocation));
- internal_memset(ent, 0, sizeof(*ent));
- ent->type = ReportLocationGlobal;
- ent->module = StripModuleName(info.module);
- ent->offset = info.module_offset;
- if (info.name)
- ent->name = internal_strdup(info.name);
- ent->addr = info.start;
- ent->size = info.size;
+ ReportLocation *ent = ReportLocation::New(ReportLocationGlobal);
+ ent->global = info;
return ent;
}
void SymbolizeFlush() {
- Symbolizer::Get()->Flush();
+ Symbolizer::GetOrInit()->Flush();
}
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_sync.cc b/lib/tsan/rtl/tsan_sync.cc
index 10f52b4..1041073 100644
--- a/lib/tsan/rtl/tsan_sync.cc
+++ b/lib/tsan/rtl/tsan_sync.cc
@@ -21,7 +21,7 @@
SyncVar::SyncVar()
: mtx(MutexTypeSyncVar, StatMtxSyncVar) {
- Reset();
+ Reset(0);
}
void SyncVar::Init(ThreadState *thr, uptr pc, uptr addr, u64 uid) {
@@ -32,11 +32,11 @@
creation_stack_id = 0;
if (kCppMode) // Go does not use them
creation_stack_id = CurrentStackId(thr, pc);
- if (flags()->detect_deadlocks)
+ if (common_flags()->detect_deadlocks)
DDMutexInit(thr, pc, this);
}
-void SyncVar::Reset() {
+void SyncVar::Reset(ThreadState *thr) {
uid = 0;
creation_stack_id = 0;
owner_tid = kInvalidTid;
@@ -47,8 +47,13 @@
is_broken = 0;
is_linker_init = 0;
- clock.Zero();
- read_clock.Reset();
+ if (thr == 0) {
+ CHECK_EQ(clock.size(), 0);
+ CHECK_EQ(read_clock.size(), 0);
+ } else {
+ clock.Reset(&thr->clock_cache);
+ read_clock.Reset(&thr->clock_cache);
+ }
}
MetaMap::MetaMap() {
@@ -93,7 +98,7 @@
DCHECK(idx & kFlagSync);
SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
u32 next = s->next;
- s->Reset();
+ s->Reset(thr);
sync_alloc_.Free(&thr->sync_cache, idx & ~kFlagMask);
idx = next;
} else {
@@ -143,7 +148,7 @@
SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
if (s->addr == addr) {
if (myidx != 0) {
- mys->Reset();
+ mys->Reset(thr);
sync_alloc_.Free(&thr->sync_cache, myidx);
}
if (write_lock)
diff --git a/lib/tsan/rtl/tsan_sync.h b/lib/tsan/rtl/tsan_sync.h
index 7c8682f..574810d 100644
--- a/lib/tsan/rtl/tsan_sync.h
+++ b/lib/tsan/rtl/tsan_sync.h
@@ -47,7 +47,7 @@
SyncClock clock;
void Init(ThreadState *thr, uptr pc, uptr addr, u64 uid);
- void Reset();
+ void Reset(ThreadState *thr);
u64 GetId() const {
// 47 lsb is addr, then 14 bits is low part of uid, then 3 zero bits.
diff --git a/lib/tsan/rtl/tsan_trace.h b/lib/tsan/rtl/tsan_trace.h
index 686160c..7fb5ae3 100644
--- a/lib/tsan/rtl/tsan_trace.h
+++ b/lib/tsan/rtl/tsan_trace.h
@@ -42,21 +42,15 @@
typedef u64 Event;
struct TraceHeader {
- StackTrace stack0; // Start stack for the trace.
+#ifndef TSAN_GO
+ BufferedStackTrace stack0; // Start stack for the trace.
+#else
+ VarSizeStackTrace stack0;
+#endif
u64 epoch0; // Start epoch for the trace.
MutexSet mset0;
-#ifndef TSAN_GO
- uptr stack0buf[kTraceStackSize];
-#endif
- TraceHeader()
-#ifndef TSAN_GO
- : stack0(stack0buf, kTraceStackSize)
-#else
- : stack0()
-#endif
- , epoch0() {
- }
+ TraceHeader() : stack0(), epoch0() {}
};
struct Trace {
diff --git a/lib/tsan/tests/rtl/tsan_test_util_linux.cc b/lib/tsan/tests/rtl/tsan_test_util_linux.cc
index a260148..9298bf0 100644
--- a/lib/tsan/tests/rtl/tsan_test_util_linux.cc
+++ b/lib/tsan/tests/rtl/tsan_test_util_linux.cc
@@ -10,7 +10,7 @@
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
-// Test utils, linux implementation.
+// Test utils, Linux and FreeBSD implementation.
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_atomic.h"
@@ -263,9 +263,14 @@
}
}
CHECK_NE(tsan_mop, 0);
- errno = ECHRNG;
+#if defined(__FreeBSD__)
+ const int ErrCode = ESOCKTNOSUPPORT;
+#else
+ const int ErrCode = ECHRNG;
+#endif
+ errno = ErrCode;
tsan_mop(ev->ptr);
- CHECK_EQ(errno, ECHRNG); // In no case must errno be changed.
+ CHECK_EQ(ErrCode, errno); // In no case must errno be changed.
break;
}
case Event::VPTR_UPDATE:
diff --git a/lib/tsan/tests/unit/tsan_clock_test.cc b/lib/tsan/tests/unit/tsan_clock_test.cc
index 49e7f3f..a1fd2b7 100644
--- a/lib/tsan/tests/unit/tsan_clock_test.cc
+++ b/lib/tsan/tests/unit/tsan_clock_test.cc
@@ -17,6 +17,8 @@
namespace __tsan {
+ClockCache cache;
+
TEST(Clock, VectorBasic) {
ThreadClock clk(0);
ASSERT_EQ(clk.size(), 1U);
@@ -38,30 +40,32 @@
SyncClock chunked;
ASSERT_EQ(vector.size(), 1U);
ASSERT_EQ(chunked.size(), 0U);
- vector.acquire(&chunked);
+ vector.acquire(&cache, &chunked);
ASSERT_EQ(vector.size(), 1U);
ASSERT_EQ(chunked.size(), 0U);
- vector.release(&chunked);
+ vector.release(&cache, &chunked);
ASSERT_EQ(vector.size(), 1U);
ASSERT_EQ(chunked.size(), 1U);
- vector.acq_rel(&chunked);
+ vector.acq_rel(&cache, &chunked);
ASSERT_EQ(vector.size(), 1U);
ASSERT_EQ(chunked.size(), 1U);
+ chunked.Reset(&cache);
}
TEST(Clock, AcquireRelease) {
ThreadClock vector1(100);
vector1.tick();
SyncClock chunked;
- vector1.release(&chunked);
+ vector1.release(&cache, &chunked);
ASSERT_EQ(chunked.size(), 101U);
ThreadClock vector2(0);
- vector2.acquire(&chunked);
+ vector2.acquire(&cache, &chunked);
ASSERT_EQ(vector2.size(), 101U);
ASSERT_EQ(vector2.get(0), 0U);
ASSERT_EQ(vector2.get(1), 0U);
ASSERT_EQ(vector2.get(99), 0U);
ASSERT_EQ(vector2.get(100), 1U);
+ chunked.Reset(&cache);
}
TEST(Clock, RepeatedAcquire) {
@@ -71,10 +75,12 @@
thr2.tick();
SyncClock sync;
- thr1.ReleaseStore(&sync);
+ thr1.ReleaseStore(&cache, &sync);
- thr2.acquire(&sync);
- thr2.acquire(&sync);
+ thr2.acquire(&cache, &sync);
+ thr2.acquire(&cache, &sync);
+
+ sync.Reset(&cache);
}
TEST(Clock, ManyThreads) {
@@ -83,9 +89,9 @@
ThreadClock vector(0);
vector.tick();
vector.set(i, 1);
- vector.release(&chunked);
+ vector.release(&cache, &chunked);
ASSERT_EQ(i + 1, chunked.size());
- vector.acquire(&chunked);
+ vector.acquire(&cache, &chunked);
ASSERT_EQ(i + 1, vector.size());
}
@@ -93,10 +99,12 @@
ASSERT_EQ(1U, chunked.get(i));
ThreadClock vector(1);
- vector.acquire(&chunked);
+ vector.acquire(&cache, &chunked);
ASSERT_EQ(100U, vector.size());
for (unsigned i = 0; i < 100; i++)
ASSERT_EQ(1U, vector.get(i));
+
+ chunked.Reset(&cache);
}
TEST(Clock, DifferentSizes) {
@@ -107,33 +115,102 @@
vector2.tick();
{
SyncClock chunked;
- vector1.release(&chunked);
+ vector1.release(&cache, &chunked);
ASSERT_EQ(chunked.size(), 11U);
- vector2.release(&chunked);
+ vector2.release(&cache, &chunked);
ASSERT_EQ(chunked.size(), 21U);
+ chunked.Reset(&cache);
}
{
SyncClock chunked;
- vector2.release(&chunked);
+ vector2.release(&cache, &chunked);
ASSERT_EQ(chunked.size(), 21U);
- vector1.release(&chunked);
+ vector1.release(&cache, &chunked);
ASSERT_EQ(chunked.size(), 21U);
+ chunked.Reset(&cache);
}
{
SyncClock chunked;
- vector1.release(&chunked);
- vector2.acquire(&chunked);
+ vector1.release(&cache, &chunked);
+ vector2.acquire(&cache, &chunked);
ASSERT_EQ(vector2.size(), 21U);
+ chunked.Reset(&cache);
}
{
SyncClock chunked;
- vector2.release(&chunked);
- vector1.acquire(&chunked);
+ vector2.release(&cache, &chunked);
+ vector1.acquire(&cache, &chunked);
ASSERT_EQ(vector1.size(), 21U);
+ chunked.Reset(&cache);
}
}
}
+TEST(Clock, Growth) {
+ {
+ ThreadClock vector(10);
+ vector.tick();
+ vector.set(5, 42);
+ SyncClock sync;
+ vector.release(&cache, &sync);
+ ASSERT_EQ(sync.size(), 11U);
+ ASSERT_EQ(sync.get(0), 0ULL);
+ ASSERT_EQ(sync.get(1), 0ULL);
+ ASSERT_EQ(sync.get(5), 42ULL);
+ ASSERT_EQ(sync.get(9), 0ULL);
+ ASSERT_EQ(sync.get(10), 1ULL);
+ sync.Reset(&cache);
+ }
+ {
+ ThreadClock vector1(10);
+ vector1.tick();
+ ThreadClock vector2(20);
+ vector2.tick();
+ SyncClock sync;
+ vector1.release(&cache, &sync);
+ vector2.release(&cache, &sync);
+ ASSERT_EQ(sync.size(), 21U);
+ ASSERT_EQ(sync.get(0), 0ULL);
+ ASSERT_EQ(sync.get(10), 1ULL);
+ ASSERT_EQ(sync.get(19), 0ULL);
+ ASSERT_EQ(sync.get(20), 1ULL);
+ sync.Reset(&cache);
+ }
+ {
+ ThreadClock vector(100);
+ vector.tick();
+ vector.set(5, 42);
+ vector.set(90, 84);
+ SyncClock sync;
+ vector.release(&cache, &sync);
+ ASSERT_EQ(sync.size(), 101U);
+ ASSERT_EQ(sync.get(0), 0ULL);
+ ASSERT_EQ(sync.get(1), 0ULL);
+ ASSERT_EQ(sync.get(5), 42ULL);
+ ASSERT_EQ(sync.get(60), 0ULL);
+ ASSERT_EQ(sync.get(70), 0ULL);
+ ASSERT_EQ(sync.get(90), 84ULL);
+ ASSERT_EQ(sync.get(99), 0ULL);
+ ASSERT_EQ(sync.get(100), 1ULL);
+ sync.Reset(&cache);
+ }
+ {
+ ThreadClock vector1(10);
+ vector1.tick();
+ ThreadClock vector2(100);
+ vector2.tick();
+ SyncClock sync;
+ vector1.release(&cache, &sync);
+ vector2.release(&cache, &sync);
+ ASSERT_EQ(sync.size(), 101U);
+ ASSERT_EQ(sync.get(0), 0ULL);
+ ASSERT_EQ(sync.get(10), 1ULL);
+ ASSERT_EQ(sync.get(99), 0ULL);
+ ASSERT_EQ(sync.get(100), 1ULL);
+ sync.Reset(&cache);
+ }
+}
+
const int kThreads = 4;
const int kClocks = 4;
@@ -257,31 +334,31 @@
if (printing)
printf("acquire thr%d <- clk%d\n", tid, cid);
thr0[tid]->acquire(sync0[cid]);
- thr1[tid]->acquire(sync1[cid]);
+ thr1[tid]->acquire(&cache, sync1[cid]);
break;
case 1:
if (printing)
printf("release thr%d -> clk%d\n", tid, cid);
thr0[tid]->release(sync0[cid]);
- thr1[tid]->release(sync1[cid]);
+ thr1[tid]->release(&cache, sync1[cid]);
break;
case 2:
if (printing)
printf("acq_rel thr%d <> clk%d\n", tid, cid);
thr0[tid]->acq_rel(sync0[cid]);
- thr1[tid]->acq_rel(sync1[cid]);
+ thr1[tid]->acq_rel(&cache, sync1[cid]);
break;
case 3:
if (printing)
printf("rel_str thr%d >> clk%d\n", tid, cid);
thr0[tid]->ReleaseStore(sync0[cid]);
- thr1[tid]->ReleaseStore(sync1[cid]);
+ thr1[tid]->ReleaseStore(&cache, sync1[cid]);
break;
case 4:
if (printing)
printf("reset clk%d\n", cid);
sync0[cid]->Reset();
- sync1[cid]->Reset();
+ sync1[cid]->Reset(&cache);
break;
case 5:
if (printing)
@@ -331,6 +408,10 @@
return false;
}
}
+
+ for (unsigned i = 0; i < kClocks; i++) {
+ sync1[i]->Reset(&cache);
+ }
return true;
}
diff --git a/lib/tsan/tests/unit/tsan_dense_alloc_test.cc b/lib/tsan/tests/unit/tsan_dense_alloc_test.cc
index fc9e4cb..e848e48 100644
--- a/lib/tsan/tests/unit/tsan_dense_alloc_test.cc
+++ b/lib/tsan/tests/unit/tsan_dense_alloc_test.cc
@@ -36,7 +36,7 @@
for (int i = 0; i < N; i++) {
IndexT idx = alloc.Alloc(&cache);
blocks[i] = idx;
- EXPECT_NE(idx, 0);
+ EXPECT_NE(idx, 0U);
int *v = alloc.Map(idx);
*v = i;
}
diff --git a/lib/tsan/tests/unit/tsan_flags_test.cc b/lib/tsan/tests/unit/tsan_flags_test.cc
index 3227d27..22610c0 100644
--- a/lib/tsan/tests/unit/tsan_flags_test.cc
+++ b/lib/tsan/tests/unit/tsan_flags_test.cc
@@ -38,7 +38,6 @@
" enable_annotations=0"
" suppress_equal_stacks=0"
" suppress_equal_addresses=0"
- " suppress_java=0"
" report_bugs=0"
" report_thread_leaks=0"
" report_destroy_locked=0"
@@ -46,8 +45,6 @@
" report_signal_unsafe=0"
" report_atomic_races=0"
" force_seq_cst_atomics=0"
- " suppressions=qwerty"
- " print_suppressions=0"
" print_benign=0"
" exitcode=111"
" halt_on_error=0"
@@ -61,29 +58,12 @@
" history_size=5"
" io_sync=1"
" die_after_fork=true"
-
- " symbolize=0"
- " external_symbolizer_path=asdfgh"
- " allow_addr2line=true"
- " strip_path_prefix=zxcvb"
- " fast_unwind_on_fatal=0"
- " fast_unwind_on_malloc=0"
- " handle_ioctl=0"
- " malloc_context_size=777"
- " log_path=aaa"
- " verbosity=2"
- " detect_leaks=0"
- " leak_check_at_exit=0"
- " allocator_may_return_null=0"
- " print_summary=0"
- " legacy_pthread_cond=0"
"";
static const char *options2 =
" enable_annotations=true"
" suppress_equal_stacks=true"
" suppress_equal_addresses=true"
- " suppress_java=true"
" report_bugs=true"
" report_thread_leaks=true"
" report_destroy_locked=true"
@@ -91,8 +71,6 @@
" report_signal_unsafe=true"
" report_atomic_races=true"
" force_seq_cst_atomics=true"
- " suppressions=aaaaa"
- " print_suppressions=true"
" print_benign=true"
" exitcode=222"
" halt_on_error=true"
@@ -106,29 +84,12 @@
" history_size=6"
" io_sync=2"
" die_after_fork=false"
-
- " symbolize=true"
- " external_symbolizer_path=cccccc"
- " allow_addr2line=false"
- " strip_path_prefix=ddddddd"
- " fast_unwind_on_fatal=true"
- " fast_unwind_on_malloc=true"
- " handle_ioctl=true"
- " malloc_context_size=567"
- " log_path=eeeeeee"
- " verbosity=0"
- " detect_leaks=true"
- " leak_check_at_exit=true"
- " allocator_may_return_null=true"
- " print_summary=true"
- " legacy_pthread_cond=true"
"";
void VerifyOptions1(Flags *f) {
EXPECT_EQ(f->enable_annotations, 0);
EXPECT_EQ(f->suppress_equal_stacks, 0);
EXPECT_EQ(f->suppress_equal_addresses, 0);
- EXPECT_EQ(f->suppress_java, 0);
EXPECT_EQ(f->report_bugs, 0);
EXPECT_EQ(f->report_thread_leaks, 0);
EXPECT_EQ(f->report_destroy_locked, 0);
@@ -136,8 +97,6 @@
EXPECT_EQ(f->report_signal_unsafe, 0);
EXPECT_EQ(f->report_atomic_races, 0);
EXPECT_EQ(f->force_seq_cst_atomics, 0);
- EXPECT_EQ(f->suppressions, std::string("qwerty"));
- EXPECT_EQ(f->print_suppressions, 0);
EXPECT_EQ(f->print_benign, 0);
EXPECT_EQ(f->exitcode, 111);
EXPECT_EQ(f->halt_on_error, 0);
@@ -151,29 +110,12 @@
EXPECT_EQ(f->history_size, 5);
EXPECT_EQ(f->io_sync, 1);
EXPECT_EQ(f->die_after_fork, true);
-
- EXPECT_EQ(f->symbolize, 0);
- EXPECT_EQ(f->external_symbolizer_path, std::string("asdfgh"));
- EXPECT_EQ(f->allow_addr2line, true);
- EXPECT_EQ(f->strip_path_prefix, std::string("zxcvb"));
- EXPECT_EQ(f->fast_unwind_on_fatal, 0);
- EXPECT_EQ(f->fast_unwind_on_malloc, 0);
- EXPECT_EQ(f->handle_ioctl, 0);
- EXPECT_EQ(f->malloc_context_size, 777);
- EXPECT_EQ(f->log_path, std::string("aaa"));
- EXPECT_EQ(f->verbosity, 2);
- EXPECT_EQ(f->detect_leaks, 0);
- EXPECT_EQ(f->leak_check_at_exit, 0);
- EXPECT_EQ(f->allocator_may_return_null, 0);
- EXPECT_EQ(f->print_summary, 0);
- EXPECT_EQ(f->legacy_pthread_cond, false);
}
void VerifyOptions2(Flags *f) {
EXPECT_EQ(f->enable_annotations, true);
EXPECT_EQ(f->suppress_equal_stacks, true);
EXPECT_EQ(f->suppress_equal_addresses, true);
- EXPECT_EQ(f->suppress_java, true);
EXPECT_EQ(f->report_bugs, true);
EXPECT_EQ(f->report_thread_leaks, true);
EXPECT_EQ(f->report_destroy_locked, true);
@@ -181,8 +123,6 @@
EXPECT_EQ(f->report_signal_unsafe, true);
EXPECT_EQ(f->report_atomic_races, true);
EXPECT_EQ(f->force_seq_cst_atomics, true);
- EXPECT_EQ(f->suppressions, std::string("aaaaa"));
- EXPECT_EQ(f->print_suppressions, true);
EXPECT_EQ(f->print_benign, true);
EXPECT_EQ(f->exitcode, 222);
EXPECT_EQ(f->halt_on_error, true);
@@ -196,22 +136,6 @@
EXPECT_EQ(f->history_size, 6);
EXPECT_EQ(f->io_sync, 2);
EXPECT_EQ(f->die_after_fork, false);
-
- EXPECT_EQ(f->symbolize, true);
- EXPECT_EQ(f->external_symbolizer_path, std::string("cccccc"));
- EXPECT_EQ(f->allow_addr2line, false);
- EXPECT_EQ(f->strip_path_prefix, std::string("ddddddd"));
- EXPECT_EQ(f->fast_unwind_on_fatal, true);
- EXPECT_EQ(f->fast_unwind_on_malloc, true);
- EXPECT_EQ(f->handle_ioctl, true);
- EXPECT_EQ(f->malloc_context_size, 567);
- EXPECT_EQ(f->log_path, std::string("eeeeeee"));
- EXPECT_EQ(f->verbosity, 0);
- EXPECT_EQ(f->detect_leaks, true);
- EXPECT_EQ(f->leak_check_at_exit, true);
- EXPECT_EQ(f->allocator_may_return_null, true);
- EXPECT_EQ(f->print_summary, true);
- EXPECT_EQ(f->legacy_pthread_cond, true);
}
static const char *test_default_options;
diff --git a/lib/tsan/tests/unit/tsan_mman_test.cc b/lib/tsan/tests/unit/tsan_mman_test.cc
index 0c4a8ff..d969989 100644
--- a/lib/tsan/tests/unit/tsan_mman_test.cc
+++ b/lib/tsan/tests/unit/tsan_mman_test.cc
@@ -103,7 +103,7 @@
EXPECT_EQ(20U, user_alloc_usable_size(p2));
user_free(thr, pc, p);
user_free(thr, pc, p2);
- EXPECT_EQ(0U, user_alloc_usable_size((void*)0x123));
+ EXPECT_EQ(0U, user_alloc_usable_size((void*)0x4123));
}
TEST(Mman, Stats) {
diff --git a/lib/tsan/tests/unit/tsan_stack_test.cc b/lib/tsan/tests/unit/tsan_stack_test.cc
index fc4d6c3..92e035d 100644
--- a/lib/tsan/tests/unit/tsan_stack_test.cc
+++ b/lib/tsan/tests/unit/tsan_stack_test.cc
@@ -17,70 +17,79 @@
namespace __tsan {
-static void TestStackTrace(StackTrace *trace) {
+template <typename StackTraceTy>
+static void TestStackTrace(StackTraceTy *trace) {
ThreadState thr(0, 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);
+ ObtainCurrentStack(&thr, 0, trace);
+ EXPECT_EQ(0U, trace->size);
- trace->ObtainCurrent(&thr, 42);
- EXPECT_EQ(trace->Size(), (uptr)1);
- EXPECT_EQ(trace->Get(0), (uptr)42);
+ ObtainCurrentStack(&thr, 42, trace);
+ EXPECT_EQ(1U, trace->size);
+ EXPECT_EQ(42U, trace->trace[0]);
*thr.shadow_stack_pos++ = 100;
*thr.shadow_stack_pos++ = 101;
- trace->ObtainCurrent(&thr, 0);
- EXPECT_EQ(trace->Size(), (uptr)2);
- EXPECT_EQ(trace->Get(0), (uptr)100);
- EXPECT_EQ(trace->Get(1), (uptr)101);
+ ObtainCurrentStack(&thr, 0, trace);
+ EXPECT_EQ(2U, trace->size);
+ EXPECT_EQ(100U, trace->trace[0]);
+ EXPECT_EQ(101U, trace->trace[1]);
- trace->ObtainCurrent(&thr, 42);
- EXPECT_EQ(trace->Size(), (uptr)3);
- EXPECT_EQ(trace->Get(0), (uptr)100);
- EXPECT_EQ(trace->Get(1), (uptr)101);
- EXPECT_EQ(trace->Get(2), (uptr)42);
+ ObtainCurrentStack(&thr, 42, trace);
+ EXPECT_EQ(3U, trace->size);
+ EXPECT_EQ(100U, trace->trace[0]);
+ EXPECT_EQ(101U, trace->trace[1]);
+ EXPECT_EQ(42U, trace->trace[2]);
}
-TEST(StackTrace, Basic) {
- StackTrace trace;
+template<typename StackTraceTy>
+static void TestTrim(StackTraceTy *trace) {
+ ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0, 0);
+ const uptr kShadowStackSize = 2 * kStackTraceMax;
+ uptr stack[kShadowStackSize];
+ thr.shadow_stack = &stack[0];
+ thr.shadow_stack_pos = &stack[0];
+ thr.shadow_stack_end = &stack[kShadowStackSize];
+
+ for (uptr i = 0; i < kShadowStackSize; ++i)
+ *thr.shadow_stack_pos++ = 100 + i;
+
+ ObtainCurrentStack(&thr, 0, trace);
+ EXPECT_EQ(kStackTraceMax, trace->size);
+ for (uptr i = 0; i < kStackTraceMax; i++) {
+ EXPECT_EQ(100 + kStackTraceMax + i, trace->trace[i]);
+ }
+
+ ObtainCurrentStack(&thr, 42, trace);
+ EXPECT_EQ(kStackTraceMax, trace->size);
+ for (uptr i = 0; i < kStackTraceMax - 1; i++) {
+ EXPECT_EQ(101 + kStackTraceMax + i, trace->trace[i]);
+ }
+ EXPECT_EQ(42U, trace->trace[kStackTraceMax - 1]);
+}
+
+TEST(StackTrace, BasicVarSize) {
+ VarSizeStackTrace trace;
TestStackTrace(&trace);
}
-TEST(StackTrace, StaticBasic) {
- uptr buf[10];
- StackTrace trace1(buf, 10);
- TestStackTrace(&trace1);
- StackTrace trace2(buf, 3);
- TestStackTrace(&trace2);
+TEST(StackTrace, BasicBuffered) {
+ BufferedStackTrace trace;
+ TestStackTrace(&trace);
}
-TEST(StackTrace, StaticTrim) {
- uptr buf[2];
- StackTrace trace(buf, 2);
-
- ThreadState thr(0, 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;
- *thr.shadow_stack_pos++ = 102;
- trace.ObtainCurrent(&thr, 0);
- EXPECT_EQ(trace.Size(), (uptr)2);
- EXPECT_EQ(trace.Get(0), (uptr)101);
- EXPECT_EQ(trace.Get(1), (uptr)102);
-
- trace.ObtainCurrent(&thr, 42);
- EXPECT_EQ(trace.Size(), (uptr)2);
- EXPECT_EQ(trace.Get(0), (uptr)102);
- EXPECT_EQ(trace.Get(1), (uptr)42);
+TEST(StackTrace, TrimVarSize) {
+ VarSizeStackTrace trace;
+ TestTrim(&trace);
}
+TEST(StackTrace, TrimBuffered) {
+ BufferedStackTrace trace;
+ TestTrim(&trace);
+}
} // namespace __tsan
diff --git a/lib/tsan/tests/unit/tsan_sync_test.cc b/lib/tsan/tests/unit/tsan_sync_test.cc
index 6f36c64..d3616a1 100644
--- a/lib/tsan/tests/unit/tsan_sync_test.cc
+++ b/lib/tsan/tests/unit/tsan_sync_test.cc
@@ -114,7 +114,7 @@
u64 block[1] = {}; // fake malloc block
m->AllocBlock(thr, 0, (uptr)&block[0], 1 * sizeof(u64));
SyncVar *s = m->GetOrCreateAndLock(thr, 0, (uptr)&block[0], true);
- s->Reset();
+ s->Reset(thr);
s->mtx.Unlock();
uptr sz = m->FreeBlock(thr, 0, (uptr)&block[0]);
EXPECT_EQ(sz, 1 * sizeof(u64));
diff --git a/lib/ubsan/CMakeLists.txt b/lib/ubsan/CMakeLists.txt
index 78c0d70..09c7a85 100644
--- a/lib/ubsan/CMakeLists.txt
+++ b/lib/ubsan/CMakeLists.txt
@@ -2,6 +2,8 @@
set(UBSAN_SOURCES
ubsan_diag.cc
+ ubsan_init.cc
+ ubsan_flags.cc
ubsan_handlers.cc
ubsan_value.cc
)
@@ -14,6 +16,8 @@
include_directories(..)
set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+append_no_rtti_flag(UBSAN_CFLAGS)
+set(UBSAN_CXXFLAGS ${SANITIZER_COMMON_CFLAGS})
add_custom_target(ubsan)
@@ -23,7 +27,7 @@
ARCH ${UBSAN_SUPPORTED_ARCH}
SOURCES ${UBSAN_SOURCES} ${UBSAN_CXX_SOURCES}
$<TARGET_OBJECTS:RTSanitizerCommon.osx>
- CFLAGS ${UBSAN_CFLAGS})
+ CFLAGS ${UBSAN_CXXFLAGS})
add_dependencies(ubsan clang_rt.ubsan_osx)
else()
# Build separate libraries for each target.
@@ -35,12 +39,12 @@
# C++-specific parts of UBSan runtime. Requires a C++ ABI library.
add_compiler_rt_runtime(clang_rt.ubsan_cxx-${arch} ${arch} STATIC
SOURCES ${UBSAN_CXX_SOURCES}
- CFLAGS ${UBSAN_CFLAGS})
+ CFLAGS ${UBSAN_CXXFLAGS})
add_dependencies(ubsan
clang_rt.san-${arch}
clang_rt.ubsan-${arch}
clang_rt.ubsan_cxx-${arch})
- if (UNIX AND NOT ${arch} STREQUAL "i386")
+ if (UNIX AND NOT ${arch} STREQUAL "i386" AND NOT ${arch} STREQUAL "i686")
add_sanitizer_rt_symbols(clang_rt.ubsan-${arch} ubsan.syms.extra)
add_sanitizer_rt_symbols(clang_rt.ubsan_cxx-${arch} ubsan.syms.extra)
add_dependencies(ubsan
diff --git a/lib/ubsan/ubsan_diag.cc b/lib/ubsan/ubsan_diag.cc
index fb5cd4b..f562508 100644
--- a/lib/ubsan/ubsan_diag.cc
+++ b/lib/ubsan/ubsan_diag.cc
@@ -12,30 +12,47 @@
//===----------------------------------------------------------------------===//
#include "ubsan_diag.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_libc.h"
+#include "ubsan_init.h"
+#include "ubsan_flags.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_stacktrace_printer.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include <stdio.h>
using namespace __ubsan;
-static void InitializeSanitizerCommon() {
- static StaticSpinMutex init_mu;
- SpinMutexLock l(&init_mu);
- static bool initialized;
- if (initialized)
- return;
- if (0 == internal_strcmp(SanitizerToolName, "SanitizerTool")) {
- // UBSan is run in a standalone mode. Initialize it now.
- SanitizerToolName = "UndefinedBehaviorSanitizer";
- CommonFlags *cf = common_flags();
- SetCommonFlagsDefaults(cf);
- cf->print_summary = false;
+static void MaybePrintStackTrace(uptr pc, uptr bp) {
+ // We assume that flags are already parsed: InitIfNecessary
+ // will definitely be called when we print the first diagnostics message.
+ if (!flags()->print_stacktrace)
+ return;
+ // We can only use slow unwind, as we don't have any information about stack
+ // top/bottom.
+ // FIXME: It's better to respect "fast_unwind_on_fatal" runtime flag and
+ // fetch stack top/bottom information if we have it (e.g. if we're running
+ // under ASan).
+ if (StackTrace::WillUseFastUnwind(false))
+ return;
+ BufferedStackTrace stack;
+ stack.Unwind(kStackTraceMax, pc, bp, 0, 0, 0, false);
+ stack.Print();
+}
+
+static void MaybeReportErrorSummary(Location Loc) {
+ if (!common_flags()->print_summary)
+ return;
+ // Don't try to unwind the stack trace in UBSan summaries: just use the
+ // provided location.
+ if (Loc.isSourceLocation()) {
+ SourceLocation SLoc = Loc.getSourceLocation();
+ if (!SLoc.isInvalid()) {
+ ReportErrorSummary("undefined-behavior", SLoc.getFilename(),
+ SLoc.getLine(), "");
+ return;
+ }
}
- initialized = true;
+ ReportErrorSummary("undefined-behavior");
}
namespace {
@@ -60,11 +77,11 @@
Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) {
if (!Loc)
return Location();
- InitializeSanitizerCommon();
+ InitIfNecessary();
AddressInfo Info;
- if (!Symbolizer::GetOrInit()->SymbolizePC(Loc, &Info, 1) ||
- !Info.module || !*Info.module)
+ if (!Symbolizer::GetOrInit()->SymbolizePC(Loc, &Info, 1) || !Info.module ||
+ !*Info.module)
return Location(Loc);
if (FName && Info.function)
@@ -113,14 +130,16 @@
if (SLoc.isInvalid())
LocBuffer.append("<unknown>");
else
- PrintSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(),
- SLoc.getColumn());
+ RenderSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(),
+ SLoc.getColumn(), common_flags()->strip_path_prefix);
break;
}
- case Location::LK_Module:
- PrintModuleAndOffset(&LocBuffer, Loc.getModuleLocation().getModuleName(),
- Loc.getModuleLocation().getOffset());
+ case Location::LK_Module: {
+ ModuleLocation MLoc = Loc.getModuleLocation();
+ RenderModuleLocation(&LocBuffer, MLoc.getModuleName(), MLoc.getOffset(),
+ common_flags()->strip_path_prefix);
break;
+ }
case Location::LK_Memory:
LocBuffer.append("%p", Loc.getMemoryLocation());
break;
@@ -193,28 +212,42 @@
return Best;
}
+static inline uptr subtractNoOverflow(uptr LHS, uptr RHS) {
+ return (LHS < RHS) ? 0 : LHS - RHS;
+}
+
+static inline uptr addNoOverflow(uptr LHS, uptr RHS) {
+ const uptr Limit = (uptr)-1;
+ return (LHS > Limit - RHS) ? Limit : LHS + RHS;
+}
+
/// Render a snippet of the address space near a location.
static void renderMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
Range *Ranges, unsigned NumRanges,
const Diag::Arg *Args) {
- const unsigned BytesToShow = 32;
- const unsigned MinBytesNearLoc = 4;
-
// Show at least the 8 bytes surrounding Loc.
- MemoryLocation Min = Loc - MinBytesNearLoc, Max = Loc + MinBytesNearLoc;
+ const unsigned MinBytesNearLoc = 4;
+ MemoryLocation Min = subtractNoOverflow(Loc, MinBytesNearLoc);
+ MemoryLocation Max = addNoOverflow(Loc, MinBytesNearLoc);
+ MemoryLocation OrigMin = Min;
for (unsigned I = 0; I < NumRanges; ++I) {
Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min);
Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max);
}
// If we have too many interesting bytes, prefer to show bytes after Loc.
+ const unsigned BytesToShow = 32;
if (Max - Min > BytesToShow)
- Min = __sanitizer::Min(Max - BytesToShow, Loc - MinBytesNearLoc);
- Max = Min + BytesToShow;
+ Min = __sanitizer::Min(Max - BytesToShow, OrigMin);
+ Max = addNoOverflow(Min, BytesToShow);
+
+ if (!IsAccessibleMemoryRange(Min, Max - Min)) {
+ Printf("<memory cannot be printed>\n");
+ return;
+ }
// Emit data.
for (uptr P = Min; P != Max; ++P) {
- // FIXME: Check that the address is readable before printing it.
unsigned char C = *reinterpret_cast<const unsigned char*>(P);
Printf("%s%02x", (P % 8 == 0) ? " " : " ", C);
}
@@ -274,9 +307,9 @@
}
Diag::~Diag() {
- InitializeSanitizerCommon();
+ // All diagnostics should be printed under report mutex.
+ CommonSanitizerReportMutex.CheckLocked();
Decorator Decor;
- SpinMutexLock l(&CommonSanitizerReportMutex);
Printf(Decor.Bold());
renderLocation(Loc);
@@ -300,3 +333,26 @@
renderMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges,
NumRanges, Args);
}
+
+ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc)
+ : Opts(Opts), SummaryLoc(SummaryLoc) {
+ InitIfNecessary();
+ CommonSanitizerReportMutex.Lock();
+}
+
+ScopedReport::~ScopedReport() {
+ MaybePrintStackTrace(Opts.pc, Opts.bp);
+ MaybeReportErrorSummary(SummaryLoc);
+ CommonSanitizerReportMutex.Unlock();
+ if (Opts.DieAfterReport || flags()->halt_on_error)
+ Die();
+}
+
+bool __ubsan::MatchSuppression(const char *Str, SuppressionType Type) {
+ Suppression *s;
+ // If .preinit_array is not used, it is possible that the UBSan runtime is not
+ // initialized.
+ if (!SANITIZER_CAN_USE_PREINIT_ARRAY)
+ InitIfNecessary();
+ return SuppressionContext::Get()->Match(Str, Type, &s);
+}
diff --git a/lib/ubsan/ubsan_diag.h b/lib/ubsan/ubsan_diag.h
index 54d15a0..296ec0d 100644
--- a/lib/ubsan/ubsan_diag.h
+++ b/lib/ubsan/ubsan_diag.h
@@ -14,6 +14,8 @@
#define UBSAN_DIAG_H
#include "ubsan_value.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
namespace __ubsan {
@@ -203,6 +205,33 @@
Diag &operator<<(const Range &R) { return AddRange(R); }
};
+struct ReportOptions {
+ /// If DieAfterReport is specified, UBSan will terminate the program after the
+ /// report is printed.
+ bool DieAfterReport;
+ /// pc/bp are used to unwind the stack trace.
+ uptr pc;
+ uptr bp;
+};
+
+#define GET_REPORT_OPTIONS(die_after_report) \
+ GET_CALLER_PC_BP; \
+ ReportOptions Opts = {die_after_report, pc, bp}
+
+/// \brief Instantiate this class before printing diagnostics in the error
+/// report. This class ensures that reports from different threads and from
+/// different sanitizers won't be mixed.
+class ScopedReport {
+ ReportOptions Opts;
+ Location SummaryLoc;
+
+public:
+ ScopedReport(ReportOptions Opts, Location SummaryLoc);
+ ~ScopedReport();
+};
+
+bool MatchSuppression(const char *Str, SuppressionType Type);
+
} // namespace __ubsan
#endif // UBSAN_DIAG_H
diff --git a/lib/ubsan/ubsan_flags.cc b/lib/ubsan/ubsan_flags.cc
new file mode 100644
index 0000000..eda11f1
--- /dev/null
+++ b/lib/ubsan/ubsan_flags.cc
@@ -0,0 +1,63 @@
+//===-- ubsan_flags.cc ----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Runtime flags for UndefinedBehaviorSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_flags.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+
+namespace __ubsan {
+
+static const char *MaybeCallUbsanDefaultOptions() {
+ return (&__ubsan_default_options) ? __ubsan_default_options() : "";
+}
+
+void InitializeCommonFlags() {
+ CommonFlags *cf = common_flags();
+ SetCommonFlagsDefaults(cf);
+ cf->print_summary = false;
+ // Override from user-specified string.
+ ParseCommonFlagsFromString(cf, MaybeCallUbsanDefaultOptions());
+ // Override from environment variable.
+ ParseCommonFlagsFromString(cf, GetEnv("UBSAN_OPTIONS"));
+}
+
+Flags ubsan_flags;
+
+static void ParseFlagsFromString(Flags *f, const char *str) {
+ if (!str)
+ return;
+ ParseFlag(str, &f->halt_on_error, "halt_on_error",
+ "Crash the program after printing the first error report");
+ ParseFlag(str, &f->print_stacktrace, "print_stacktrace",
+ "Include full stacktrace into an error report");
+}
+
+void InitializeFlags() {
+ Flags *f = flags();
+ // Default values.
+ f->halt_on_error = false;
+ f->print_stacktrace = false;
+ // Override from user-specified string.
+ ParseFlagsFromString(f, MaybeCallUbsanDefaultOptions());
+ // Override from environment variable.
+ ParseFlagsFromString(f, GetEnv("UBSAN_OPTIONS"));
+}
+
+} // namespace __ubsan
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+const char *__ubsan_default_options() { return ""; }
+} // extern "C"
+#endif
diff --git a/lib/ubsan/ubsan_flags.h b/lib/ubsan/ubsan_flags.h
new file mode 100644
index 0000000..c496469
--- /dev/null
+++ b/lib/ubsan/ubsan_flags.h
@@ -0,0 +1,40 @@
+//===-- ubsan_flags.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Runtime flags for UndefinedBehaviorSanitizer.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_FLAGS_H
+#define UBSAN_FLAGS_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+namespace __ubsan {
+
+struct Flags {
+ bool halt_on_error;
+ bool print_stacktrace;
+};
+
+extern Flags ubsan_flags;
+inline Flags *flags() { return &ubsan_flags; }
+
+void InitializeCommonFlags();
+void InitializeFlags();
+
+} // namespace __ubsan
+
+extern "C" {
+// Users may provide their own implementation of __ubsan_default_options to
+// override the default flag values.
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+const char *__ubsan_default_options();
+} // extern "C"
+
+#endif // UBSAN_FLAGS_H
diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc
index d556431..a0ecff9 100644
--- a/lib/ubsan/ubsan_handlers.cc
+++ b/lib/ubsan/ubsan_handlers.cc
@@ -19,23 +19,35 @@
using namespace __sanitizer;
using namespace __ubsan;
+static bool ignoreReport(SourceLocation SLoc, ReportOptions Opts) {
+ // If source location is already acquired, we don't need to print an error
+ // report for the second time. However, if we're in an unrecoverable handler,
+ // it's possible that location was required by concurrently running thread.
+ // In this case, we should continue the execution to ensure that any of
+ // threads will grab the report mutex and print the report before
+ // crashing the program.
+ return SLoc.isDisabled() && !Opts.DieAfterReport;
+}
+
namespace __ubsan {
- const char *TypeCheckKinds[] = {
+const char *TypeCheckKinds[] = {
"load of", "store to", "reference binding to", "member access within",
- "member call on", "constructor call on", "downcast of", "downcast of"
- };
+ "member call on", "constructor call on", "downcast of", "downcast of",
+ "upcast of", "cast to virtual base of"};
}
static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
- Location FallbackLoc) {
+ Location FallbackLoc, ReportOptions Opts) {
Location Loc = Data->Loc.acquire();
-
// Use the SourceLocation from Data to track deduplication, even if 'invalid'
- if (Loc.getSourceLocation().isDisabled())
+ if (ignoreReport(Loc.getSourceLocation(), Opts))
return;
+
if (Data->Loc.isInvalid())
Loc = FallbackLoc;
+ ScopedReport R(Opts, Loc);
+
if (!Pointer)
Diag(Loc, DL_Error, "%0 null pointer of type %1")
<< TypeCheckKinds[Data->TypeCheckKind] << Data->Type;
@@ -51,70 +63,59 @@
if (Pointer)
Diag(Pointer, DL_Note, "pointer points here");
}
+
void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data,
ValueHandle Pointer) {
- handleTypeMismatchImpl(Data, Pointer, getCallerLocation());
+ GET_REPORT_OPTIONS(false);
+ handleTypeMismatchImpl(Data, Pointer, getCallerLocation(), Opts);
}
void __ubsan::__ubsan_handle_type_mismatch_abort(TypeMismatchData *Data,
ValueHandle Pointer) {
- handleTypeMismatchImpl(Data, Pointer, getCallerLocation());
+ GET_REPORT_OPTIONS(true);
+ handleTypeMismatchImpl(Data, Pointer, getCallerLocation(), Opts);
Die();
}
/// \brief Common diagnostic emission for various forms of integer overflow.
-template<typename T> static void HandleIntegerOverflow(OverflowData *Data,
- ValueHandle LHS,
- const char *Operator,
- T RHS) {
+template <typename T>
+static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
+ const char *Operator, T RHS,
+ ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ ScopedReport R(Opts, Loc);
+
Diag(Loc, DL_Error, "%0 integer overflow: "
"%1 %2 %3 cannot be represented in type %4")
<< (Data->Type.isSignedIntegerTy() ? "signed" : "unsigned")
<< Value(Data->Type, LHS) << Operator << RHS << Data->Type;
}
-void __ubsan::__ubsan_handle_add_overflow(OverflowData *Data,
- ValueHandle LHS, ValueHandle RHS) {
- HandleIntegerOverflow(Data, LHS, "+", Value(Data->Type, RHS));
-}
-void __ubsan::__ubsan_handle_add_overflow_abort(OverflowData *Data,
- ValueHandle LHS,
- ValueHandle RHS) {
- __ubsan_handle_add_overflow(Data, LHS, RHS);
- Die();
-}
+#define UBSAN_OVERFLOW_HANDLER(handler_name, op, abort) \
+ void __ubsan::handler_name(OverflowData *Data, ValueHandle LHS, \
+ ValueHandle RHS) { \
+ GET_REPORT_OPTIONS(abort); \
+ handleIntegerOverflowImpl(Data, LHS, op, Value(Data->Type, RHS), Opts); \
+ if (abort) Die(); \
+ }
-void __ubsan::__ubsan_handle_sub_overflow(OverflowData *Data,
- ValueHandle LHS, ValueHandle RHS) {
- HandleIntegerOverflow(Data, LHS, "-", Value(Data->Type, RHS));
-}
-void __ubsan::__ubsan_handle_sub_overflow_abort(OverflowData *Data,
- ValueHandle LHS,
- ValueHandle RHS) {
- __ubsan_handle_sub_overflow(Data, LHS, RHS);
- Die();
-}
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow, "+", false)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow_abort, "+", true)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow, "-", false)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow_abort, "-", true)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow, "*", false)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow_abort, "*", true)
-void __ubsan::__ubsan_handle_mul_overflow(OverflowData *Data,
- ValueHandle LHS, ValueHandle RHS) {
- HandleIntegerOverflow(Data, LHS, "*", Value(Data->Type, RHS));
-}
-void __ubsan::__ubsan_handle_mul_overflow_abort(OverflowData *Data,
- ValueHandle LHS,
- ValueHandle RHS) {
- __ubsan_handle_mul_overflow(Data, LHS, RHS);
- Die();
-}
-
-void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data,
- ValueHandle OldVal) {
+static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal,
+ ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ ScopedReport R(Opts, Loc);
+
if (Data->Type.isSignedIntegerTy())
Diag(Loc, DL_Error,
"negation of %0 cannot be represented in type %1; "
@@ -125,18 +126,27 @@
"negation of %0 cannot be represented in type %1")
<< Value(Data->Type, OldVal) << Data->Type;
}
+
+void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data,
+ ValueHandle OldVal) {
+ GET_REPORT_OPTIONS(false);
+ handleNegateOverflowImpl(Data, OldVal, Opts);
+}
void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data,
ValueHandle OldVal) {
- __ubsan_handle_negate_overflow(Data, OldVal);
+ GET_REPORT_OPTIONS(true);
+ handleNegateOverflowImpl(Data, OldVal, Opts);
Die();
}
-void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data,
- ValueHandle LHS, ValueHandle RHS) {
+static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS,
+ ValueHandle RHS, ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ ScopedReport R(Opts, Loc);
+
Value LHSVal(Data->Type, LHS);
Value RHSVal(Data->Type, RHS);
if (RHSVal.isMinusOne())
@@ -146,20 +156,29 @@
else
Diag(Loc, DL_Error, "division by zero");
}
+
+void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data,
+ ValueHandle LHS, ValueHandle RHS) {
+ GET_REPORT_OPTIONS(false);
+ handleDivremOverflowImpl(Data, LHS, RHS, Opts);
+}
void __ubsan::__ubsan_handle_divrem_overflow_abort(OverflowData *Data,
ValueHandle LHS,
ValueHandle RHS) {
- __ubsan_handle_divrem_overflow(Data, LHS, RHS);
+ GET_REPORT_OPTIONS(true);
+ handleDivremOverflowImpl(Data, LHS, RHS, Opts);
Die();
}
-void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data,
- ValueHandle LHS,
- ValueHandle RHS) {
+static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data,
+ ValueHandle LHS, ValueHandle RHS,
+ ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ ScopedReport R(Opts, Loc);
+
Value LHSVal(Data->LHSType, LHS);
Value RHSVal(Data->RHSType, RHS);
if (RHSVal.isNegative())
@@ -175,107 +194,219 @@
"left shift of %0 by %1 places cannot be represented in type %2")
<< LHSVal << RHSVal << Data->LHSType;
}
+
+void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data,
+ ValueHandle LHS,
+ ValueHandle RHS) {
+ GET_REPORT_OPTIONS(false);
+ handleShiftOutOfBoundsImpl(Data, LHS, RHS, Opts);
+}
void __ubsan::__ubsan_handle_shift_out_of_bounds_abort(
ShiftOutOfBoundsData *Data,
ValueHandle LHS,
ValueHandle RHS) {
- __ubsan_handle_shift_out_of_bounds(Data, LHS, RHS);
+ GET_REPORT_OPTIONS(true);
+ handleShiftOutOfBoundsImpl(Data, LHS, RHS, Opts);
Die();
}
-void __ubsan::__ubsan_handle_out_of_bounds(OutOfBoundsData *Data,
- ValueHandle Index) {
+static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index,
+ ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ ScopedReport R(Opts, Loc);
+
Value IndexVal(Data->IndexType, Index);
Diag(Loc, DL_Error, "index %0 out of bounds for type %1")
<< IndexVal << Data->ArrayType;
}
+
+void __ubsan::__ubsan_handle_out_of_bounds(OutOfBoundsData *Data,
+ ValueHandle Index) {
+ GET_REPORT_OPTIONS(false);
+ handleOutOfBoundsImpl(Data, Index, Opts);
+}
void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data,
ValueHandle Index) {
- __ubsan_handle_out_of_bounds(Data, Index);
+ GET_REPORT_OPTIONS(true);
+ handleOutOfBoundsImpl(Data, Index, Opts);
Die();
}
+static void handleBuiltinUnreachableImpl(UnreachableData *Data,
+ ReportOptions Opts) {
+ ScopedReport R(Opts, Data->Loc);
+ Diag(Data->Loc, DL_Error, "execution reached a __builtin_unreachable() call");
+}
+
void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) {
- Diag(Data->Loc, DL_Error, "execution reached a __builtin_unreachable() call");
+ GET_REPORT_OPTIONS(true);
+ handleBuiltinUnreachableImpl(Data, Opts);
Die();
}
-void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) {
+static void handleMissingReturnImpl(UnreachableData *Data, ReportOptions Opts) {
+ ScopedReport R(Opts, Data->Loc);
Diag(Data->Loc, DL_Error,
"execution reached the end of a value-returning function "
"without returning a value");
+}
+
+void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleMissingReturnImpl(Data, Opts);
Die();
}
-void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data,
- ValueHandle Bound) {
+static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound,
+ ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ ScopedReport R(Opts, Loc);
+
Diag(Loc, DL_Error, "variable length array bound evaluates to "
"non-positive value %0")
<< Value(Data->Type, Bound);
}
+
+void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data,
+ ValueHandle Bound) {
+ GET_REPORT_OPTIONS(false);
+ handleVLABoundNotPositive(Data, Bound, Opts);
+}
void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data,
- ValueHandle Bound) {
- __ubsan_handle_vla_bound_not_positive(Data, Bound);
+ ValueHandle Bound) {
+ GET_REPORT_OPTIONS(true);
+ handleVLABoundNotPositive(Data, Bound, Opts);
Die();
}
+static void handleFloatCastOverflow(FloatCastOverflowData *Data,
+ ValueHandle From, ReportOptions Opts) {
+ // TODO: Add deduplication once a SourceLocation is generated for this check.
+ Location Loc = getCallerLocation();
+ ScopedReport R(Opts, Loc);
+
+ Diag(Loc, DL_Error,
+ "value %0 is outside the range of representable values of type %2")
+ << Value(Data->FromType, From) << Data->FromType << Data->ToType;
+}
void __ubsan::__ubsan_handle_float_cast_overflow(FloatCastOverflowData *Data,
ValueHandle From) {
- // TODO: Add deduplication once a SourceLocation is generated for this check.
- Diag(getCallerLocation(), DL_Error,
- "value %0 is outside the range of representable values of type %2")
- << Value(Data->FromType, From) << Data->FromType << Data->ToType;
+ GET_REPORT_OPTIONS(false);
+ handleFloatCastOverflow(Data, From, Opts);
}
-void __ubsan::__ubsan_handle_float_cast_overflow_abort(
- FloatCastOverflowData *Data,
- ValueHandle From) {
- Diag(getCallerLocation(), DL_Error,
- "value %0 is outside the range of representable values of type %2")
- << Value(Data->FromType, From) << Data->FromType << Data->ToType;
+void
+__ubsan::__ubsan_handle_float_cast_overflow_abort(FloatCastOverflowData *Data,
+ ValueHandle From) {
+ GET_REPORT_OPTIONS(true);
+ handleFloatCastOverflow(Data, From, Opts);
Die();
}
-void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data,
- ValueHandle Val) {
+static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val,
+ ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ ScopedReport R(Opts, Loc);
+
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(InvalidValueData *Data,
+ ValueHandle Val) {
+ GET_REPORT_OPTIONS(false);
+ handleLoadInvalidValue(Data, Val, Opts);
+}
void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data,
ValueHandle Val) {
- __ubsan_handle_load_invalid_value(Data, Val);
+ GET_REPORT_OPTIONS(true);
+ handleLoadInvalidValue(Data, Val, Opts);
Die();
}
-void __ubsan::__ubsan_handle_function_type_mismatch(
- FunctionTypeMismatchData *Data,
- ValueHandle Function) {
+static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data,
+ ValueHandle Function,
+ ReportOptions Opts) {
const char *FName = "(unknown)";
Location Loc = getFunctionLocation(Function, &FName);
+ ScopedReport R(Opts, Loc);
+
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(FunctionTypeMismatchData *Data,
+ ValueHandle Function) {
+ GET_REPORT_OPTIONS(false);
+ handleFunctionTypeMismatch(Data, Function, Opts);
+}
+
void __ubsan::__ubsan_handle_function_type_mismatch_abort(
- FunctionTypeMismatchData *Data,
- ValueHandle Function) {
- __ubsan_handle_function_type_mismatch(Data, Function);
+ FunctionTypeMismatchData *Data, ValueHandle Function) {
+ GET_REPORT_OPTIONS(true);
+ handleFunctionTypeMismatch(Data, Function, Opts);
+ Die();
+}
+
+static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ if (ignoreReport(Loc, Opts))
+ return;
+
+ ScopedReport R(Opts, Loc);
+
+ Diag(Loc, DL_Error, "null pointer returned from function declared to never "
+ "return null");
+ if (!Data->AttrLoc.isInvalid())
+ Diag(Data->AttrLoc, DL_Note, "returns_nonnull attribute specified here");
+}
+
+void __ubsan::__ubsan_handle_nonnull_return(NonNullReturnData *Data) {
+ GET_REPORT_OPTIONS(false);
+ handleNonNullReturn(Data, Opts);
+}
+
+void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleNonNullReturn(Data, Opts);
+ Die();
+}
+
+static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ if (ignoreReport(Loc, Opts))
+ return;
+
+ ScopedReport R(Opts, Loc);
+
+ Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to "
+ "never be null") << Data->ArgIndex;
+ if (!Data->AttrLoc.isInvalid())
+ Diag(Data->AttrLoc, DL_Note, "nonnull attribute specified here");
+}
+
+void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData *Data) {
+ GET_REPORT_OPTIONS(false);
+ handleNonNullArg(Data, Opts);
+}
+
+void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleNonNullArg(Data, Opts);
Die();
}
diff --git a/lib/ubsan/ubsan_handlers.h b/lib/ubsan/ubsan_handlers.h
index 14e6f04..87149f2 100644
--- a/lib/ubsan/ubsan_handlers.h
+++ b/lib/ubsan/ubsan_handlers.h
@@ -24,10 +24,14 @@
unsigned char TypeCheckKind;
};
+#define UNRECOVERABLE(checkname, ...) \
+ extern "C" SANITIZER_INTERFACE_ATTRIBUTE NORETURN \
+ void __ubsan_handle_ ## checkname( __VA_ARGS__ );
+
#define RECOVERABLE(checkname, ...) \
extern "C" SANITIZER_INTERFACE_ATTRIBUTE \
void __ubsan_handle_ ## checkname( __VA_ARGS__ ); \
- extern "C" SANITIZER_INTERFACE_ATTRIBUTE \
+ extern "C" SANITIZER_INTERFACE_ATTRIBUTE NORETURN \
void __ubsan_handle_ ## checkname ## _abort( __VA_ARGS__ );
/// \brief Handle a runtime type check failure, caused by either a misaligned
@@ -81,11 +85,9 @@
};
/// \brief Handle a __builtin_unreachable which is reached.
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __ubsan_handle_builtin_unreachable(UnreachableData *Data);
+UNRECOVERABLE(builtin_unreachable, UnreachableData *Data)
/// \brief Handle reaching the end of a value-returning function.
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __ubsan_handle_missing_return(UnreachableData *Data);
+UNRECOVERABLE(missing_return, UnreachableData *Data)
struct VLABoundData {
SourceLocation Loc;
@@ -121,6 +123,23 @@
FunctionTypeMismatchData *Data,
ValueHandle Val)
+struct NonNullReturnData {
+ SourceLocation Loc;
+ SourceLocation AttrLoc;
+};
+
+/// \brief Handle returning null from function with returns_nonnull attribute.
+RECOVERABLE(nonnull_return, NonNullReturnData *Data)
+
+struct NonNullArgData {
+ SourceLocation Loc;
+ SourceLocation AttrLoc;
+ int ArgIndex;
+};
+
+/// \brief Handle passing null pointer to function with nonnull attribute.
+RECOVERABLE(nonnull_arg, NonNullArgData *Data)
+
}
#endif // UBSAN_HANDLERS_H
diff --git a/lib/ubsan/ubsan_handlers_cxx.cc b/lib/ubsan/ubsan_handlers_cxx.cc
index b6cddef..5704c1e 100644
--- a/lib/ubsan/ubsan_handlers_cxx.cc
+++ b/lib/ubsan/ubsan_handlers_cxx.cc
@@ -18,6 +18,7 @@
#include "ubsan_type_hash.h"
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
using namespace __sanitizer;
using namespace __ubsan;
@@ -28,47 +29,54 @@
static void HandleDynamicTypeCacheMiss(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash,
- bool Abort) {
+ ReportOptions Opts) {
if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash))
// Just a cache miss. The type matches after all.
return;
+ // Check if error report should be suppressed.
+ DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer);
+ if (DTI.isValid() &&
+ MatchSuppression(DTI.getMostDerivedTypeName(), SuppressionVptrCheck))
+ return;
+
SourceLocation Loc = Data->Loc.acquire();
if (Loc.isDisabled())
return;
+ ScopedReport R(Opts, Loc);
+
Diag(Loc, DL_Error,
"%0 address %1 which does not point to an object of type %2")
<< TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
// If possible, say what type it actually points to.
- DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer);
if (!DTI.isValid())
Diag(Pointer, DL_Note, "object has invalid vptr")
- << MangledName(DTI.getMostDerivedTypeName())
- << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
+ << MangledName(DTI.getMostDerivedTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
else if (!DTI.getOffset())
Diag(Pointer, DL_Note, "object is of type %0")
- << MangledName(DTI.getMostDerivedTypeName())
- << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
+ << MangledName(DTI.getMostDerivedTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
else
// FIXME: Find the type at the specified offset, and include that
// in the note.
Diag(Pointer - DTI.getOffset(), DL_Note,
"object is base class subobject at offset %0 within object of type %1")
- << DTI.getOffset() << MangledName(DTI.getMostDerivedTypeName())
- << MangledName(DTI.getSubobjectTypeName())
- << Range(Pointer, Pointer + sizeof(uptr), "vptr for %2 base class of %1");
-
- if (Abort)
- Die();
+ << DTI.getOffset() << MangledName(DTI.getMostDerivedTypeName())
+ << MangledName(DTI.getSubobjectTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr),
+ "vptr for %2 base class of %1");
}
void __ubsan::__ubsan_handle_dynamic_type_cache_miss(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
- HandleDynamicTypeCacheMiss(Data, Pointer, Hash, false);
+ GET_REPORT_OPTIONS(false);
+ HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts);
}
void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
- HandleDynamicTypeCacheMiss(Data, Pointer, Hash, true);
+ GET_REPORT_OPTIONS(true);
+ HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts);
}
diff --git a/lib/ubsan/ubsan_init.cc b/lib/ubsan/ubsan_init.cc
new file mode 100644
index 0000000..6080e30
--- /dev/null
+++ b/lib/ubsan/ubsan_init.cc
@@ -0,0 +1,61 @@
+//===-- ubsan_init.cc -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Initialization of UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_init.h"
+#include "ubsan_flags.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+using namespace __ubsan;
+
+static bool ubsan_inited;
+
+void __ubsan::InitIfNecessary() {
+#if !SANITIZER_CAN_USE_PREINIT_ARRAY
+ // No need to lock mutex if we're initializing from preinit array.
+ static StaticSpinMutex init_mu;
+ SpinMutexLock l(&init_mu);
+#endif
+ if (LIKELY(ubsan_inited))
+ return;
+ if (0 == internal_strcmp(SanitizerToolName, "SanitizerTool")) {
+ // WARNING: If this condition holds, then either UBSan runs in a standalone
+ // mode, or initializer for another sanitizer hasn't run yet. In a latter
+ // case, another sanitizer will overwrite "SanitizerToolName" and reparse
+ // common flags. It means, that we are not allowed to *use* common flags
+ // in this function.
+ SanitizerToolName = "UndefinedBehaviorSanitizer";
+ InitializeCommonFlags();
+ }
+ // Initialize UBSan-specific flags.
+ InitializeFlags();
+ SuppressionContext::InitIfNecessary();
+ ubsan_inited = true;
+}
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
+__attribute__((section(".preinit_array"), used))
+void (*__local_ubsan_preinit)(void) = __ubsan::InitIfNecessary;
+#else
+// Use a dynamic initializer.
+class UbsanInitializer {
+ public:
+ UbsanInitializer() {
+ InitIfNecessary();
+ }
+};
+static UbsanInitializer ubsan_initializer;
+#endif // SANITIZER_CAN_USE_PREINIT_ARRAY
diff --git a/lib/ubsan/ubsan_init.h b/lib/ubsan/ubsan_init.h
new file mode 100644
index 0000000..18356cf
--- /dev/null
+++ b/lib/ubsan/ubsan_init.h
@@ -0,0 +1,24 @@
+//===-- ubsan_init.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Initialization function for UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_INIT_H
+#define UBSAN_INIT_H
+
+namespace __ubsan {
+
+// NOTE: This function might take a lock (if .preinit_array initialization is
+// not used). It's generally a bad idea to call it on a fast path.
+void InitIfNecessary();
+
+} // namespace __ubsan
+
+#endif // UBSAN_INIT_H
diff --git a/lib/ubsan/ubsan_type_hash.cc b/lib/ubsan/ubsan_type_hash.cc
index a388bcc..808a433 100644
--- a/lib/ubsan/ubsan_type_hash.cc
+++ b/lib/ubsan/ubsan_type_hash.cc
@@ -115,7 +115,8 @@
/// \brief Determine whether \p Derived has a \p Base base class subobject at
/// offset \p Offset.
-static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
+static bool isDerivedFromAtOffset(sptr Object,
+ const abi::__class_type_info *Derived,
const abi::__class_type_info *Base,
sptr Offset) {
if (Derived->__type_name == Base->__type_name)
@@ -123,7 +124,7 @@
if (const abi::__si_class_type_info *SI =
dynamic_cast<const abi::__si_class_type_info*>(Derived))
- return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
+ return isDerivedFromAtOffset(Object, SI->__base_type, Base, Offset);
const abi::__vmi_class_type_info *VTI =
dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
@@ -138,13 +139,13 @@
sptr OffsetHere = VTI->base_info[base].__offset_flags >>
abi::__base_class_type_info::__offset_shift;
if (VTI->base_info[base].__offset_flags &
- abi::__base_class_type_info::__virtual_mask)
- // For now, just punt on virtual bases and say 'yes'.
- // FIXME: OffsetHere is the offset in the vtable of the virtual base
- // offset. Read the vbase offset out of the vtable and use it.
- return true;
- if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
- Base, Offset - OffsetHere))
+ abi::__base_class_type_info::__virtual_mask) {
+ sptr VTable = *reinterpret_cast<const sptr *>(Object);
+ OffsetHere = *reinterpret_cast<const sptr *>(VTable + OffsetHere);
+ }
+ if (isDerivedFromAtOffset(Object + OffsetHere,
+ VTI->base_info[base].__base_type, Base,
+ Offset - OffsetHere))
return true;
}
@@ -153,14 +154,15 @@
/// \brief Find the derived-most dynamic base class of \p Derived at offset
/// \p Offset.
-static const abi::__class_type_info *findBaseAtOffset(
- const abi::__class_type_info *Derived, sptr Offset) {
+static const abi::__class_type_info *
+findBaseAtOffset(sptr Object, const abi::__class_type_info *Derived,
+ sptr Offset) {
if (!Offset)
return Derived;
if (const abi::__si_class_type_info *SI =
dynamic_cast<const abi::__si_class_type_info*>(Derived))
- return findBaseAtOffset(SI->__base_type, Offset);
+ return findBaseAtOffset(Object, SI->__base_type, Offset);
const abi::__vmi_class_type_info *VTI =
dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
@@ -172,12 +174,13 @@
sptr OffsetHere = VTI->base_info[base].__offset_flags >>
abi::__base_class_type_info::__offset_shift;
if (VTI->base_info[base].__offset_flags &
- abi::__base_class_type_info::__virtual_mask)
- // FIXME: Can't handle virtual bases yet.
- continue;
- if (const abi::__class_type_info *Base =
- findBaseAtOffset(VTI->base_info[base].__base_type,
- Offset - OffsetHere))
+ abi::__base_class_type_info::__virtual_mask) {
+ sptr VTable = *reinterpret_cast<const sptr *>(Object);
+ OffsetHere = *reinterpret_cast<const sptr *>(VTable + OffsetHere);
+ }
+ if (const abi::__class_type_info *Base = findBaseAtOffset(
+ Object + OffsetHere, VTI->base_info[base].__base_type,
+ Offset - OffsetHere))
return Base;
}
@@ -229,7 +232,8 @@
return false;
abi::__class_type_info *Base = (abi::__class_type_info*)Type;
- if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
+ if (!isDerivedFromAtOffset(reinterpret_cast<sptr>(Object), Derived, Base,
+ -Vtable->Offset))
return false;
// Success. Cache this result.
@@ -243,8 +247,9 @@
if (!Vtable)
return DynamicTypeInfo(0, 0, 0);
const abi::__class_type_info *ObjectType = findBaseAtOffset(
- static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
- -Vtable->Offset);
+ reinterpret_cast<sptr>(Object),
+ static_cast<const abi::__class_type_info *>(Vtable->TypeInfo),
+ -Vtable->Offset);
return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset,
ObjectType ? ObjectType->__type_name : "<unknown>");
}
diff --git a/make/platform/clang_darwin.mk b/make/platform/clang_darwin.mk
index d920fa8..6ed3230 100644
--- a/make/platform/clang_darwin.mk
+++ b/make/platform/clang_darwin.mk
@@ -6,31 +6,37 @@
Description := Static runtime libraries for clang/Darwin.
-# A function that ensures we don't try to build for architectures that we
-# don't have working toolchains for.
+# A function that ensures we don't try to build for architectures and SDKs
+# that we don't have working toolchains for. Arguments:
+# (1): List of architectures
+# (2): Library name
+# (3): SDK path
+# The result is a possibly empty subset of the architectures from argument 1.
CheckArches = \
$(shell \
result=""; \
- for arch in $(1); do \
- if $(CC) -arch $$arch -c \
- -integrated-as \
- $(ProjSrcRoot)/make/platform/clang_darwin_test_input.c \
- -isysroot $(ProjSrcRoot)/SDKs/darwin \
- -o /dev/null > /dev/null 2> /dev/null; then \
- if $(LD) -v 2>&1 | grep "configured to support" \
- | tr ' ' '\n' | grep "^$$arch$$" >/dev/null 2>/dev/null; then \
- result="$$result$$arch "; \
+ if [ "X$(3)" != X ]; then \
+ for arch in $(1); do \
+ if $(CC) -arch $$arch -c \
+ -integrated-as \
+ $(ProjSrcRoot)/make/platform/clang_darwin_test_input.c \
+ -isysroot $(3) \
+ -o /dev/null > /dev/null 2> /dev/null; then \
+ if $(LD) -v 2>&1 | grep "configured to support" \
+ | tr ' ' '\n' | grep "^$$arch$$" >/dev/null 2>/dev/null; then \
+ result="$$result$$arch "; \
+ else \
+ printf 1>&2 \
+ "warning: clang_darwin.mk: dropping arch '$$arch' from lib '$(2)'";\
+ printf 1>&2 " (ld does not support it)\n"; \
+ fi; \
else \
printf 1>&2 \
- "warning: clang_darwin.mk: dropping arch '$$arch' from lib '$(2)'"; \
- printf 1>&2 " (ld does not support it)\n"; \
+ "warning: clang_darwin.mk: dropping arch '$$arch' from lib '$(2)'"; \
+ printf 1>&2 " (clang does not support it)\n"; \
fi; \
- else \
- printf 1>&2 \
- "warning: clang_darwin.mk: dropping arch '$$arch' from lib '$(2)'"; \
- printf 1>&2 " (clang does not support it)\n"; \
- fi; \
- done; \
+ done; \
+ fi; \
echo $$result)
XCRun = \
@@ -53,6 +59,10 @@
LIPO := $(call XCRun,lipo)
DSYMUTIL := $(call XCRun,dsymutil)
+OSX_SDK := $(call XCRunSdkPath,macosx)
+IOS_SDK := $(call XCRunSdkPath,iphoneos)
+IOSSIM_SDK := $(call XCRunSdkPath,iphonesimulator)
+
Configs :=
UniversalArchs :=
@@ -60,51 +70,53 @@
# still be referenced from Darwin system headers. This symbol is only ever
# needed on i386.
Configs += eprintf
-UniversalArchs.eprintf := $(call CheckArches,i386,eprintf)
+UniversalArchs.eprintf := $(call CheckArches,i386,eprintf,$(OSX_SDK))
# Configuration for targeting 10.4. We need a few functions missing from
# libgcc_s.10.4.dylib. We only build x86 slices since clang doesn't really
# support targeting PowerPC.
Configs += 10.4
-UniversalArchs.10.4 := $(call CheckArches,i386 x86_64,10.4)
+UniversalArchs.10.4 := $(call CheckArches,i386 x86_64,10.4,$(OSX_SDK))
# Configuration for targeting iOS for a couple of functions that didn't
# make it into libSystem.
Configs += ios
-UniversalArchs.ios := $(call CheckArches,i386 x86_64 x86_64h armv7,ios)
+UniversalArchs.ios := $(call CheckArches,i386 x86_64,ios,$(IOSSIM_SDK))
+UniversalArchs.ios += $(call CheckArches,armv7 arm64,ios,$(IOS_SDK))
# Configuration for targeting OSX. These functions may not be in libSystem
# so we should provide our own.
Configs += osx
-UniversalArchs.osx := $(call CheckArches,i386 x86_64 x86_64h,osx)
+UniversalArchs.osx := $(call CheckArches,i386 x86_64 x86_64h,osx,$(OSX_SDK))
# Configuration for use with kernel/kexts.
Configs += cc_kext
-UniversalArchs.cc_kext := $(call CheckArches,armv7 i386 x86_64 x86_64h,cc_kext)
+UniversalArchs.cc_kext := $(call CheckArches,i386 x86_64 x86_64h,cc_kext,$(OSX_SDK))
+UniversalArchs.cc_kext += $(call CheckArches,armv7 arm64,cc_kext,$(IOS_SDK))
# Configuration for use with kernel/kexts for iOS 5.0 and earlier (which used
-# a different code generation strategy).
+# a different code generation strategy). Note: the x86_64 slice is unused but
+# it avoids build problems (see pr14013).
Configs += cc_kext_ios5
-UniversalArchs.cc_kext_ios5 := $(call CheckArches,x86_64 x86_64h armv7,cc_kext_ios5)
+UniversalArchs.cc_kext_ios5 := $(call CheckArches,x86_64,cc_kext_ios5,$(IOSSIM_SDK))
+UniversalArchs.cc_kext_ios5 += $(call CheckArches,armv7,cc_kext_ios5,$(IOS_SDK))
# Configurations which define the profiling support functions.
Configs += profile_osx
-UniversalArchs.profile_osx := $(call CheckArches,i386 x86_64 x86_64h,profile_osx)
+UniversalArchs.profile_osx := $(call CheckArches,i386 x86_64 x86_64h,profile_osx,$(OSX_SDK))
Configs += profile_ios
-UniversalArchs.profile_ios := $(call CheckArches,i386 x86_64 x86_64h armv7,profile_ios)
+UniversalArchs.profile_ios := $(call CheckArches,i386 x86_64,profile_ios,$(IOSSIM_SDK))
+UniversalArchs.profile_ios += $(call CheckArches,armv7 arm64,profile_ios,$(IOS_SDK))
# Configurations which define the ASAN support functions.
Configs += asan_osx_dynamic
-UniversalArchs.asan_osx_dynamic := $(call CheckArches,i386 x86_64 x86_64h,asan_osx_dynamic)
+UniversalArchs.asan_osx_dynamic := $(call CheckArches,i386 x86_64 x86_64h,asan_osx_dynamic,$(OSX_SDK))
-IOSSIM_SDK_PATH := $(call XCRunSdkPath,iphonesimulator)
-ifneq ($(IOSSIM_SDK_PATH),)
Configs += asan_iossim_dynamic
-UniversalArchs.asan_iossim_dynamic := $(call CheckArches,i386 x86_64 x86_64h,asan_iossim_dynamic)
-endif
+UniversalArchs.asan_iossim_dynamic := $(call CheckArches,i386 x86_64,asan_iossim_dynamic,$(IOSSIM_SDK))
Configs += ubsan_osx
-UniversalArchs.ubsan_osx := $(call CheckArches,i386 x86_64 x86_64h,ubsan_osx)
+UniversalArchs.ubsan_osx := $(call CheckArches,i386 x86_64 x86_64h,ubsan_osx,$(OSX_SDK))
# Darwin 10.6 has a bug in cctools that makes it unable to use ranlib on our ARM
# object files. If we are on that platform, strip out all ARM archs. We still
@@ -117,26 +129,20 @@
UniversalArchs.profile_ios := $(filter-out armv7, $(UniversalArchs.profile_ios))
endif
-### ARM64 Support ###
-# Explicitly add these, to workaround CheckArches function not including the
-# CFLAGS, and not wanting to require an ARM64 assembler be installed.
-UniversalArchs.ios += arm64
-UniversalArchs.cc_kext += arm64
-UniversalArchs.profile_ios += arm64
-
# If RC_SUPPORTED_ARCHS is defined, treat it as a list of the architectures we
# are intended to support and limit what we try to build to that.
-#
-# We make sure to remove empty configs if we end up dropping all the requested
-# archs for a particular config.
ifneq ($(RC_SUPPORTED_ARCHS),)
$(foreach config,$(Configs),\
$(call Set,UniversalArchs.$(config),\
- $(filter $(RC_SUPPORTED_ARCHS),$(UniversalArchs.$(config))))\
- $(if $(UniversalArchs.$(config)),,\
- $(call Set,Configs,$(filter-out $(config),$(Configs)))))
+ $(filter $(RC_SUPPORTED_ARCHS),$(UniversalArchs.$(config)))))
endif
+# Remove empty configs if we end up dropping all the requested
+# archs for a particular config.
+$(foreach config,$(Configs),\
+ $(if $(strip $(UniversalArchs.$(config))),,\
+ $(call Set,Configs,$(filter-out $(config),$(Configs)))))
+
###
# Forcibly strip off any -arch, as that totally breaks our universal support.
@@ -154,32 +160,34 @@
IOS6_DEPLOYMENT_ARGS := -mios-version-min=6.0
IOSSIM_DEPLOYMENT_ARGS := -mios-simulator-version-min=1.0
-# Use our stub SDK as the sysroot to support more portable building.
-OSX_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin
-IOS_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin
-IOS6_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin
-IOSSIM_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin
+OSX_DEPLOYMENT_ARGS += -isysroot $(OSX_SDK)
+IOS_DEPLOYMENT_ARGS += -isysroot $(IOS_SDK)
+IOS6_DEPLOYMENT_ARGS += -isysroot $(IOS_SDK)
+IOSSIM_DEPLOYMENT_ARGS += -isysroot $(IOSSIM_SDK)
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.6 -fno-builtin \
+ $(CFLAGS) -mmacosx-version-min=10.7 \
+ -isysroot $(OSX_SDK) \
+ -fno-builtin \
-gline-tables-only \
-DMAC_INTERPOSE_FUNCTIONS=1
CFLAGS.asan_iossim_dynamic := \
$(CFLAGS) -mios-simulator-version-min=7.0 \
- -isysroot $(IOSSIM_SDK_PATH) \
+ -isysroot $(IOSSIM_SDK) \
-fno-builtin \
-gline-tables-only \
-DMAC_INTERPOSE_FUNCTIONS=1
-CFLAGS.ubsan_osx := $(CFLAGS) -mmacosx-version-min=10.6 -fno-builtin
+CFLAGS.ubsan_osx := $(CFLAGS) -mmacosx-version-min=10.6 \
+ -isysroot $(OSX_SDK) \
+ -fno-builtin
CFLAGS.ios.i386 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
CFLAGS.ios.x86_64 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
-CFLAGS.ios.x86_64h := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
CFLAGS.ios.armv7 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
CFLAGS.ios.armv7k := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
CFLAGS.ios.armv7s := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
@@ -202,7 +210,6 @@
CFLAGS.profile_osx.x86_64h := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
CFLAGS.profile_ios.i386 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
CFLAGS.profile_ios.x86_64 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
-CFLAGS.profile_ios.x86_64h := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
CFLAGS.profile_ios.armv7 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
CFLAGS.profile_ios.armv7k := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
CFLAGS.profile_ios.armv7s := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
@@ -210,15 +217,15 @@
# Configure the asan_osx_dynamic library to be built shared.
SHARED_LIBRARY.asan_osx_dynamic := 1
-LDFLAGS.asan_osx_dynamic := -lstdc++ -undefined dynamic_lookup
+LDFLAGS.asan_osx_dynamic := -lstdc++ -undefined dynamic_lookup -install_name @rpath/libclang_rt.asan_osx_dynamic.dylib
# 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 \
+LDFLAGS.asan_iossim_dynamic := -undefined dynamic_lookup -install_name @rpath/libclang_rt.asan_iossim_dynamic.dylib \
-Wl,-ios_simulator_version_min,7.0.0 \
- -mios-simulator-version-min=7.0 -isysroot $(IOSSIM_SDK_PATH)
+ -mios-simulator-version-min=7.0 -isysroot $(IOSSIM_SDK)
FUNCTIONS.eprintf := eprintf
FUNCTIONS.10.4 := eprintf floatundidf floatundisf floatundixf
@@ -228,8 +235,7 @@
FUNCTIONS.ios.i386 := $(FUNCTIONS.ios) \
divsi3 udivsi3
FUNCTIONS.ios.x86_64 := $(FUNCTIONS.ios.i386)
-FUNCTIONS.ios.x86_64h := $(FUNCTIONS.ios.x86_64)
-FUNCTIONS.ios.arm64 := dummy
+FUNCTIONS.ios.arm64 := mulsc3 muldc3 divsc3 divdc3
FUNCTIONS.osx := mulosi4 mulodi4 muloti4
@@ -399,7 +405,7 @@
FUNCTIONS.cc_kext.armv7 := $(CCKEXT_ARMVFP_FUNCTIONS)
FUNCTIONS.cc_kext.armv7k := $(CCKEXT_ARMVFP_FUNCTIONS)
FUNCTIONS.cc_kext.armv7s := $(CCKEXT_ARMVFP_FUNCTIONS)
-FUNCTIONS.cc_kext.arm64 := dummy
+FUNCTIONS.cc_kext.arm64 := mulsc3 muldc3 divsc3 divdc3
FUNCTIONS.cc_kext_ios5.armv7 := $(CCKEXT_ARMVFP_FUNCTIONS)
FUNCTIONS.cc_kext_ios5.armv7k := $(CCKEXT_ARMVFP_FUNCTIONS)
FUNCTIONS.cc_kext_ios5.armv7s := $(CCKEXT_ARMVFP_FUNCTIONS)
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 8477216..007ac3f 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -14,7 +14,7 @@
# Use LLVM utils and Clang from the same build tree.
list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS
clang clang-headers FileCheck count not llvm-nm llvm-symbolizer
- compiler-rt-headers)
+ compiler-rt-headers profile)
endif()
if(UNIX)
list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS SanitizerLintCheck)
@@ -24,28 +24,28 @@
# Run sanitizer tests only if we're sure that clang would produce
# working binaries.
if(COMPILER_RT_CAN_EXECUTE_TESTS)
- if(ASAN_SUPPORTED_ARCH OR ANDROID)
+ if(COMPILER_RT_HAS_ASAN)
add_subdirectory(asan)
endif()
- if(DFSAN_SUPPORTED_ARCH)
+ if(COMPILER_RT_HAS_DFSAN)
add_subdirectory(dfsan)
endif()
- if(LSAN_SUPPORTED_ARCH)
+ if(COMPILER_RT_HAS_LSAN)
add_subdirectory(lsan)
endif()
- if(MSAN_SUPPORTED_ARCH)
+ if(COMPILER_RT_HAS_MSAN)
add_subdirectory(msan)
endif()
- if(PROFILE_SUPPORTED_ARCH)
+ if(COMPILER_RT_HAS_PROFILE)
add_subdirectory(profile)
endif()
- if(SANITIZER_COMMON_SUPPORTED_ARCH)
+ if(COMPILER_RT_HAS_SANITIZER_COMMON)
add_subdirectory(sanitizer_common)
endif()
- if(TSAN_SUPPORTED_ARCH)
+ if(COMPILER_RT_HAS_TSAN)
add_subdirectory(tsan)
endif()
- if(UBSAN_SUPPORTED_ARCH)
+ if(COMPILER_RT_HAS_UBSAN)
add_subdirectory(ubsan)
endif()
endif()
diff --git a/test/asan/CMakeLists.txt b/test/asan/CMakeLists.txt
index cca7ccb..14f7f50 100644
--- a/test/asan/CMakeLists.txt
+++ b/test/asan/CMakeLists.txt
@@ -2,113 +2,133 @@
set(ASAN_TESTSUITES)
-if(CAN_TARGET_arm_android)
- # This is only true if we are cross-compiling.
- # Build all tests with host compiler and use host tools.
- set(ASAN_TEST_TARGET_CC ${CMAKE_C_COMPILER})
- set(ASAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS})
- get_filename_component(ASAN_TEST_LLVM_TOOLS_DIR ${CMAKE_C_COMPILER} PATH)
- set(ASAN_TEST_CONFIG_SUFFIX "-arm-android")
- set(ASAN_TEST_BITS "32")
- set(ASAN_TEST_DYNAMIC True)
- set(ASAN_TEST_TARGET_ARCH "arm-android")
- configure_lit_site_cfg(
- ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/ARMAndroidConfig/lit.site.cfg
- )
- list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/ARMAndroidConfig)
-endif()
+macro(get_bits_for_arch arch bits)
+ if (${arch} STREQUAL "arm" OR
+ ${arch} STREQUAL "i386" OR
+ ${arch} STREQUAL "i686" OR
+ ${arch} STREQUAL "mips")
+ set(bits 32)
+ elseif (${arch} STREQUAL "aarch64" OR
+ ${arch} STREQUAL "x86_64" OR
+ ${arch} STREQUAL "mips64")
+ set(bits 64)
+ else()
+ message(FATAL_ERROR "Unknown target architecture: ${arch}")
+ endif()
+endmacro()
-if(CAN_TARGET_arm)
- # This is only true if we are cross-compiling.
- # Build all tests with host compiler and use host tools.
- set(ASAN_TEST_TARGET_CC ${COMPILER_RT_TEST_COMPILER})
- set(ASAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS})
- set(ASAN_TEST_CONFIG_SUFFIX "-arm-linux")
- set(ASAN_TEST_BITS "32")
- set(ASAN_TEST_DYNAMIC False)
- configure_lit_site_cfg(
- ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/ARMLinuxConfig/lit.site.cfg
- )
- list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/ARMLinuxConfig)
-endif()
-
-if(CAN_TARGET_aarch64)
- # This is only true if we are cross-compiling.
- # Build all tests with host compiler and use host tools.
- set(ASAN_TEST_TARGET_CC ${COMPILER_RT_TEST_COMPILER})
- set(ASAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS})
- set(ASAN_TEST_CONFIG_SUFFIX "-aarch64-linux")
- set(ASAN_TEST_BITS "64")
- set(ASAN_TEST_DYNAMIC False)
- configure_lit_site_cfg(
- ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/AArch64LinuxConfig/lit.site.cfg
- )
- list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/AArch64LinuxConfig)
-endif()
-
-if(CAN_TARGET_x86_64 OR CAN_TARGET_powerpc64)
- set(ASAN_TEST_CONFIG_SUFFIX "64")
- set(ASAN_TEST_BITS "64")
- set(ASAN_TEST_TARGET_CFLAGS ${TARGET_64_BIT_CFLAGS})
- set(ASAN_TEST_DYNAMIC False)
- configure_lit_site_cfg(
- ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig/lit.site.cfg
- )
- list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig)
- if(COMPILER_RT_BUILD_SHARED_ASAN)
- set(ASAN_TEST_CONFIG_SUFFIX "64-Dynamic")
+# TODO: merge with non-ANDROID case
+if(ANDROID)
+ foreach(arch ${ASAN_SUPPORTED_ARCH})
+ set(ASAN_TEST_TARGET_CC ${CMAKE_CXX_COMPILER})
+ set(ASAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS})
+ set(ASAN_TEST_CONFIG_SUFFIX "-${arch}-android")
+ get_bits_for_arch(${arch} ASAN_TEST_BITS)
set(ASAN_TEST_DYNAMIC True)
+ set(ASAN_TEST_TARGET_ARCH "${arch}-android")
+ string(TOUPPER ${arch} ARCH_UPPER_CASE)
+ set(CONFIG ${ARCH_UPPER_CASE}AndroidConfig)
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig-dynamic/lit.site.cfg)
- list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig-dynamic)
- endif()
-endif()
+ ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/lit.site.cfg
+ )
+ list(APPEND ASAN_TESTSUITES
+ ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG})
+ endforeach()
-if(CAN_TARGET_i386)
- set(ASAN_TEST_CONFIG_SUFFIX "32")
- set(ASAN_TEST_BITS "32")
- set(ASAN_TEST_TARGET_CFLAGS ${TARGET_32_BIT_CFLAGS})
- set(ASAN_TEST_DYNAMIC False)
- set(ASAN_TEST_TARGET_ARCH "i386")
- configure_lit_site_cfg(
- ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg
- )
- list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig)
- if(COMPILER_RT_BUILD_SHARED_ASAN)
- set(ASAN_TEST_CONFIG_SUFFIX "32-Dynamic")
- set(ASAN_TEST_DYNAMIC True)
+else() # Not Android
+
+ if(CAN_TARGET_arm)
+ # This is only true if we are cross-compiling.
+ # Build all tests with host compiler and use host tools.
+ set(ASAN_TEST_TARGET_CC ${COMPILER_RT_TEST_COMPILER})
+ set(ASAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS})
+ set(ASAN_TEST_CONFIG_SUFFIX "-arm-linux")
+ set(ASAN_TEST_BITS "32")
+ set(ASAN_TEST_DYNAMIC False)
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic/lit.site.cfg)
- list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic)
+ ${CMAKE_CURRENT_BINARY_DIR}/ARMLinuxConfig/lit.site.cfg
+ )
+ list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/ARMLinuxConfig)
endif()
-endif()
-if(CAN_TARGET_mips)
- set(ASAN_TEST_CONFIG_SUFFIX "32")
- set(ASAN_TEST_BITS "32")
- set(ASAN_TEST_TARGET_CFLAGS ${TARGET_32_BIT_CFLAGS})
- set(ASAN_TEST_DYNAMIC False)
- configure_lit_site_cfg(
- ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg
- )
- list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig)
- if(COMPILER_RT_BUILD_SHARED_ASAN)
- set(ASAN_TEST_CONFIG_SUFFIX "32-Dynamic")
- set(ASAN_TEST_DYNAMIC True)
+ if(CAN_TARGET_aarch64)
+ # This is only true if we are cross-compiling.
+ # Build all tests with host compiler and use host tools.
+ set(ASAN_TEST_TARGET_CC ${COMPILER_RT_TEST_COMPILER})
+ set(ASAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS})
+ set(ASAN_TEST_CONFIG_SUFFIX "-aarch64-linux")
+ set(ASAN_TEST_BITS "64")
+ set(ASAN_TEST_DYNAMIC False)
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic/lit.site.cfg)
- list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic)
+ ${CMAKE_CURRENT_BINARY_DIR}/AArch64LinuxConfig/lit.site.cfg
+ )
+ list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/AArch64LinuxConfig)
endif()
-endif()
+
+ if(CAN_TARGET_x86_64 OR CAN_TARGET_powerpc64 OR CAN_TARGET_mips64 OR CAN_TARGET_mips64el)
+ set(ASAN_TEST_CONFIG_SUFFIX "64")
+ set(ASAN_TEST_BITS "64")
+ set(ASAN_TEST_TARGET_CFLAGS ${TARGET_64_BIT_CFLAGS})
+ set(ASAN_TEST_DYNAMIC False)
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig/lit.site.cfg
+ )
+ list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig)
+ if(COMPILER_RT_BUILD_SHARED_ASAN)
+ set(ASAN_TEST_CONFIG_SUFFIX "64-Dynamic")
+ set(ASAN_TEST_DYNAMIC True)
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig-dynamic/lit.site.cfg)
+ list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig-dynamic)
+ endif()
+ endif()
+
+ if(CAN_TARGET_i386)
+ set(ASAN_TEST_CONFIG_SUFFIX "32")
+ set(ASAN_TEST_BITS "32")
+ set(ASAN_TEST_TARGET_CFLAGS ${TARGET_32_BIT_CFLAGS})
+ set(ASAN_TEST_DYNAMIC False)
+ set(ASAN_TEST_TARGET_ARCH "i386")
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg
+ )
+ list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig)
+ if(COMPILER_RT_BUILD_SHARED_ASAN)
+ set(ASAN_TEST_CONFIG_SUFFIX "32-Dynamic")
+ set(ASAN_TEST_DYNAMIC True)
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic/lit.site.cfg)
+ list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic)
+ endif()
+ endif()
+
+ if(CAN_TARGET_mips OR CAN_TARGET_mipsel)
+ set(ASAN_TEST_CONFIG_SUFFIX "32")
+ set(ASAN_TEST_BITS "32")
+ set(ASAN_TEST_TARGET_CFLAGS ${TARGET_32_BIT_CFLAGS})
+ set(ASAN_TEST_DYNAMIC False)
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg
+ )
+ list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig)
+ if(COMPILER_RT_BUILD_SHARED_ASAN)
+ set(ASAN_TEST_CONFIG_SUFFIX "32-Dynamic")
+ set(ASAN_TEST_DYNAMIC True)
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic/lit.site.cfg)
+ list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic)
+ endif()
+ endif()
+endif() # Not Android
if(COMPILER_RT_INCLUDE_TESTS)
configure_lit_site_cfg(
@@ -118,13 +138,15 @@
set(ASAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
if(COMPILER_RT_STANDALONE_BUILD)
- list(APPEND ASAN_TEST_DEPS ${LLVM_TOOLS_BINARY_DIR}/FileCheck)
+ add_executable(FileCheck IMPORTED GLOBAL)
+ set_property(TARGET FileCheck PROPERTY IMPORTED_LOCATION ${LLVM_TOOLS_BINARY_DIR}/FileCheck)
+ list(APPEND ASAN_TEST_DEPS FileCheck)
else()
list(APPEND ASAN_TEST_DEPS asan)
endif()
# FIXME: support unit test in the android test runner
-if(COMPILER_RT_INCLUDE_TESTS AND NOT CAN_TARGET_arm_android)
+if(COMPILER_RT_INCLUDE_TESTS AND NOT ANDROID)
list(APPEND ASAN_TEST_DEPS AsanUnitTests)
list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit)
endif()
diff --git a/test/asan/TestCases/Darwin/dyld_insert_libraries_reexec.cc b/test/asan/TestCases/Darwin/dyld_insert_libraries_reexec.cc
new file mode 100644
index 0000000..b1bb456
--- /dev/null
+++ b/test/asan/TestCases/Darwin/dyld_insert_libraries_reexec.cc
@@ -0,0 +1,33 @@
+// When DYLD-inserting the ASan dylib from a different location than the
+// original, make sure we don't try to reexec.
+
+// RUN: mkdir -p %T/dyld_insert_libraries_reexec
+// RUN: cp `%clang_asan %s -fsanitize=address -### 2>&1 \
+// RUN: | grep "libclang_rt.asan_osx_dynamic.dylib" \
+// RUN: | sed -e 's/.*"\(.*libclang_rt.asan_osx_dynamic.dylib\)".*/\1/'` \
+// RUN: %T/dyld_insert_libraries_reexec/libclang_rt.asan_osx_dynamic.dylib
+// RUN: %clangxx_asan %s -o %T/dyld_insert_libraries_reexec/a.out
+// RUN: DYLD_INSERT_LIBRARIES=@executable_path/libclang_rt.asan_osx_dynamic.dylib \
+// RUN: ASAN_OPTIONS=verbosity=1 %run %T/dyld_insert_libraries_reexec/a.out 2>&1 \
+// RUN: | FileCheck %s
+// RUN: ASAN_OPTIONS=verbosity=1 %run %T/dyld_insert_libraries_reexec/a.out 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-NOINSERT %s
+
+#include <stdio.h>
+
+int main() {
+ printf("Passed\n");
+ return 0;
+}
+
+// CHECK-NOINSERT: Parsed ASAN_OPTIONS: verbosity=1
+// CHECK-NOINSERT: exec()-ing the program with
+// CHECK-NOINSERT: DYLD_INSERT_LIBRARIES
+// CHECK-NOINSERT: to enable ASan wrappers.
+// CHECK-NOINSERT: Passed
+
+// CHECK: Parsed ASAN_OPTIONS: verbosity=1
+// CHECK-NOT: exec()-ing the program with
+// CHECK-NOT: DYLD_INSERT_LIBRARIES
+// CHECK-NOT: to enable ASan wrappers.
+// CHECK: Passed
diff --git a/test/asan/TestCases/Darwin/interface_symbols_darwin.c b/test/asan/TestCases/Darwin/interface_symbols_darwin.c
index e513954..8680d67 100644
--- a/test/asan/TestCases/Darwin/interface_symbols_darwin.c
+++ b/test/asan/TestCases/Darwin/interface_symbols_darwin.c
@@ -5,18 +5,14 @@
// 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" | \
-// RUN: tr -d '\011' | \
-// RUN: sed "s/.dylib.*/.dylib/"` \
+// RUN: nm -g `%clang_asan %s -fsanitize=address -### 2>&1 | grep "libclang_rt.asan_osx_dynamic.dylib" | sed -e 's/.*"\(.*libclang_rt.asan_osx_dynamic.dylib\)".*/\1/'` \
// RUN: | 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: | sed -E "s/__asan_init_v[0-9]+/__asan_init/" \
// RUN: | grep -v "__asan_default_options" \
// RUN: | grep -v "__asan_on_error" > %t.symbols
// RUN: cat %p/../../../../lib/asan/asan_interface_internal.h \
-// RUN: %p/../../../../lib/asan/asan_init_version.h \
// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \
// RUN: | grep -v "OPTIONAL" \
// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \
diff --git a/test/asan/TestCases/Linux/asan-asm-stacktrace-test.cc b/test/asan/TestCases/Linux/asan-asm-stacktrace-test.cc
new file mode 100644
index 0000000..5332c99
--- /dev/null
+++ b/test/asan/TestCases/Linux/asan-asm-stacktrace-test.cc
@@ -0,0 +1,33 @@
+// Check that a stack unwinding algorithm works corretly even with the assembly
+// instrumentation.
+
+// REQUIRES: x86_64-supported-target
+// RUN: %clangxx_asan -g -O1 %s -fno-inline-functions -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -mllvm -asan-instrument-assembly -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -g -O1 %s -fno-inline-functions -fomit-frame-pointer -momit-leaf-frame-pointer -mllvm -asan-instrument-assembly -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -g0 -O1 %s -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-exceptions -fno-inline-functions -fomit-frame-pointer -momit-leaf-frame-pointer -mllvm -asan-instrument-assembly -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-nounwind
+
+#include <cstddef>
+
+// CHECK: READ of size 4
+// CHECK-NEXT: {{#0 0x[0-9a-fA-F]+ in foo}}
+// CHECK-NEXT: {{#1 0x[0-9a-fA-F]+ in main}}
+
+// CHECK-nounwind: READ of size 4
+// CHECK-nounwind-NEXT: {{#0 0x[0-9a-fA-F]+ in foo}}
+
+__attribute__((noinline)) int foo(size_t n, int *buffer) {
+ int r;
+ __asm__("movl (%[buffer], %[n], 4), %[r] \n\t"
+ : [r] "=r"(r)
+ : [buffer] "r"(buffer), [n] "r"(n)
+ : "memory");
+ return r;
+}
+
+int main() {
+ const size_t n = 16;
+ int *buffer = new int[n];
+ foo(n, buffer);
+ delete[] buffer;
+ return 0;
+}
diff --git a/test/asan/TestCases/Linux/clang_gcc_abi.cc b/test/asan/TestCases/Linux/clang_gcc_abi.cc
new file mode 100644
index 0000000..e833881
--- /dev/null
+++ b/test/asan/TestCases/Linux/clang_gcc_abi.cc
@@ -0,0 +1,44 @@
+// RUN: %clangxx_asan -O0 -x c %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O1 -x c %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O2 -x c %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O3 -x c %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+// REQUIRES: arm-supported-target
+// XFAIL: armv7l-unknown-linux-gnueabihf
+
+#include <stdlib.h>
+
+int boom() {
+ volatile int three = 3;
+ char *s = (char *)malloc(three);
+// CHECK: #1 0x{{.*}} in boom {{.*}}clang_gcc_abi.cc:[[@LINE-1]]
+ return s[three]; //BOOM
+}
+
+__attribute__((naked, noinline)) void gcc_abi() {
+// CHECK: #2 0x{{.*}} in gcc_abi {{.*}}clang_gcc_abi.cc:[[@LINE+1]]
+ asm volatile("str fp, [sp, #-8]!\n\t"
+ "str lr, [sp, #4]\n\t"
+ "add fp, sp, #4\n\t"
+ "bl boom\n\t"
+ "sub sp, fp, #4\n\t"
+ "ldr fp, [sp]\n\t"
+ "add sp, sp, #4\n\t"
+ "ldr pc, [sp], #4\n\t"
+ );
+}
+
+__attribute__((naked, noinline)) void clang_abi() {
+// CHECK: #3 0x{{.*}} in clang_abi {{.*}}clang_gcc_abi.cc:[[@LINE+1]]
+ asm volatile("push {r11, lr}\n\t"
+ "mov r11, sp\n\t"
+ "bl gcc_abi\n\t"
+ "add r0, r0, #1\n\t"
+ "pop {r11, pc}\n\t"
+ );
+}
+
+int main() {
+ clang_abi();
+// CHECK: #4 0x{{.*}} in main {{.*}}clang_gcc_abi.cc:[[@LINE-1]]
+}
diff --git a/test/asan/TestCases/Linux/coverage-and-lsan.cc b/test/asan/TestCases/Linux/coverage-and-lsan.cc
new file mode 100644
index 0000000..f0d371f
--- /dev/null
+++ b/test/asan/TestCases/Linux/coverage-and-lsan.cc
@@ -0,0 +1,20 @@
+// Make sure coverage is dumped even if there are reported leaks.
+//
+// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t
+//
+// RUN: rm -rf %T/coverage-and-lsan
+//
+// RUN: mkdir -p %T/coverage-and-lsan/normal
+// RUN: ASAN_OPTIONS=coverage=1:coverage_dir=%T/coverage-and-lsan:verbosity=1 not %run %t 2>&1 | FileCheck %s
+// RUN: %sancov print %T/coverage-and-lsan/*.sancov 2>&1
+//
+// REQUIRES: asan-64-bits
+
+int *g = new int;
+int main(int argc, char **argv) {
+ g = 0;
+ return 0;
+}
+
+// CHECK: LeakSanitizer: detected memory leaks
+// CHECK: CovDump:
diff --git a/test/asan/TestCases/Linux/coverage-caller-callee-total-count.cc b/test/asan/TestCases/Linux/coverage-caller-callee-total-count.cc
new file mode 100644
index 0000000..0201425
--- /dev/null
+++ b/test/asan/TestCases/Linux/coverage-caller-callee-total-count.cc
@@ -0,0 +1,41 @@
+// Test __sanitizer_get_total_unique_coverage for caller-callee coverage
+
+// RUN: %clangxx_asan -fsanitize-coverage=4 %s -o %t
+// RUN: ASAN_OPTIONS=coverage=1 %run %t
+// RUN: rm -f caller-callee*.sancov
+//
+// REQUIRES: asan-64-bits
+
+#include <sanitizer/common_interface_defs.h>
+#include <stdio.h>
+#include <assert.h>
+int P = 0;
+struct Foo {virtual void f() {if (P) printf("Foo::f()\n");}};
+struct Foo1 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo2 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+
+Foo *foo[3] = {new Foo, new Foo1, new Foo2};
+
+uintptr_t CheckNewTotalUniqueCoverageIsLargerAndReturnIt(uintptr_t old_total) {
+ uintptr_t new_total = __sanitizer_get_total_unique_coverage();
+ assert(new_total > old_total);
+ return new_total;
+}
+
+int main(int argc, char **argv) {
+ uintptr_t total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(0);
+ foo[0]->f();
+ total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total);
+ foo[1]->f();
+ total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total);
+ foo[2]->f();
+ total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total);
+ // Ok, called every function once.
+ // Now call them again from another call site. Should get new coverage.
+ foo[0]->f();
+ total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total);
+ foo[1]->f();
+ total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total);
+ foo[2]->f();
+ total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total);
+}
diff --git a/test/asan/TestCases/Linux/coverage-caller-callee.cc b/test/asan/TestCases/Linux/coverage-caller-callee.cc
new file mode 100644
index 0000000..cd31896
--- /dev/null
+++ b/test/asan/TestCases/Linux/coverage-caller-callee.cc
@@ -0,0 +1,74 @@
+// Test caller-callee coverage with large number of threads
+// and various numbers of callers and callees.
+
+// RUN: %clangxx_asan -fsanitize-coverage=4 %s -o %t
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 10 1 2>&1 | FileCheck %s --check-prefix=CHECK-10-1
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 9 2 2>&1 | FileCheck %s --check-prefix=CHECK-9-2
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 7 3 2>&1 | FileCheck %s --check-prefix=CHECK-7-3
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 17 1 2>&1 | FileCheck %s --check-prefix=CHECK-17-1
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 15 2 2>&1 | FileCheck %s --check-prefix=CHECK-15-2
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 18 3 2>&1 | FileCheck %s --check-prefix=CHECK-18-3
+// RUN: rm -f caller-callee*.sancov
+//
+// REQUIRES: asan-64-bits
+//
+// CHECK-10-1: CovDump: 10 caller-callee pairs written
+// CHECK-9-2: CovDump: 18 caller-callee pairs written
+// CHECK-7-3: CovDump: 21 caller-callee pairs written
+// CHECK-17-1: CovDump: 14 caller-callee pairs written
+// CHECK-15-2: CovDump: 28 caller-callee pairs written
+// CHECK-18-3: CovDump: 42 caller-callee pairs written
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+int P = 0;
+struct Foo {virtual void f() {if (P) printf("Foo::f()\n");}};
+struct Foo1 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo2 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo3 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo4 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo5 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo6 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo7 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo8 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo9 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo10 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo11 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo12 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo13 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo14 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo15 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo16 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo17 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo18 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo19 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+
+Foo *foo[20] = {
+ new Foo, new Foo1, new Foo2, new Foo3, new Foo4, new Foo5, new Foo6,
+ new Foo7, new Foo8, new Foo9, new Foo10, new Foo11, new Foo12, new Foo13,
+ new Foo14, new Foo15, new Foo16, new Foo17, new Foo18, new Foo19,
+};
+
+int n_functions = 10;
+int n_callers = 2;
+
+void *Thread(void *arg) {
+ if (n_callers >= 1) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f();
+ if (n_callers >= 2) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f();
+ if (n_callers >= 3) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f();
+ return arg;
+}
+
+int main(int argc, char **argv) {
+ if (argc >= 2)
+ n_functions = atoi(argv[1]);
+ if (argc >= 3)
+ n_callers = atoi(argv[2]);
+ const int kNumThreads = 16;
+ pthread_t t[kNumThreads];
+ for (int i = 0; i < kNumThreads; i++)
+ pthread_create(&t[i], 0, Thread, 0);
+ for (int i = 0; i < kNumThreads; i++)
+ pthread_join(t[i], 0);
+}
diff --git a/test/asan/TestCases/Linux/coverage-direct-large.cc b/test/asan/TestCases/Linux/coverage-direct-large.cc
index b340da5..78aa686 100644
--- a/test/asan/TestCases/Linux/coverage-direct-large.cc
+++ b/test/asan/TestCases/Linux/coverage-direct-large.cc
@@ -1,7 +1,7 @@
// Test for direct coverage writing with lots of data.
// Current implementation maps output file in chunks of 64K. This test overflows
// 1 chunk.
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 -O0 %s -o %t
+// RUN: %clangxx_asan -fsanitize-coverage=1 -O0 %s -o %t
// RUN: rm -rf %T/coverage-direct-large
diff --git a/test/asan/TestCases/Linux/coverage-direct.cc b/test/asan/TestCases/Linux/coverage-direct.cc
index 7fe2514..2cc1aed 100644
--- a/test/asan/TestCases/Linux/coverage-direct.cc
+++ b/test/asan/TestCases/Linux/coverage-direct.cc
@@ -1,6 +1,6 @@
// Test for direct coverage writing with dlopen.
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %T/libcoverage_direct_test_1.so -fPIC
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSO_DIR=\"%T\" %s -o %t
+// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_direct_test_1.so -fPIC
+// RUN: %clangxx_asan -fsanitize-coverage=1 -DSO_DIR=\"%T\" %s -o %t
// RUN: rm -rf %T/coverage-direct
diff --git a/test/asan/TestCases/Linux/coverage-disabled.cc b/test/asan/TestCases/Linux/coverage-disabled.cc
index 315c312..a75b26d 100644
--- a/test/asan/TestCases/Linux/coverage-disabled.cc
+++ b/test/asan/TestCases/Linux/coverage-disabled.cc
@@ -1,6 +1,6 @@
// Test that no data is collected without a runtime flag.
//
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s -o %t
+// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t
//
// RUN: rm -rf %T/coverage-disabled
//
diff --git a/test/asan/TestCases/Linux/coverage-fork-direct.cc b/test/asan/TestCases/Linux/coverage-fork-direct.cc
index 7489b72..51cbbd8 100644
--- a/test/asan/TestCases/Linux/coverage-fork-direct.cc
+++ b/test/asan/TestCases/Linux/coverage-fork-direct.cc
@@ -1,4 +1,4 @@
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s -o %t
+// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t
// RUN: rm -rf %T/coverage-fork-direct
// RUN: mkdir -p %T/coverage-fork-direct && cd %T/coverage-fork-direct
// RUN: (ASAN_OPTIONS=coverage=1:coverage_direct=1:verbosity=1 %run %t; \
diff --git a/test/asan/TestCases/Linux/coverage-fork.cc b/test/asan/TestCases/Linux/coverage-fork.cc
index 28b2a49..38c2009 100644
--- a/test/asan/TestCases/Linux/coverage-fork.cc
+++ b/test/asan/TestCases/Linux/coverage-fork.cc
@@ -1,4 +1,4 @@
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s -o %t
+// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t
// RUN: export ASAN_OPTIONS=coverage=1:coverage_direct=0:verbosity=1
// RUN: rm -rf %T/coverage-fork
// RUN: mkdir -p %T/coverage-fork && cd %T/coverage-fork
diff --git a/test/asan/TestCases/Linux/coverage-levels.cc b/test/asan/TestCases/Linux/coverage-levels.cc
new file mode 100644
index 0000000..748ef1f
--- /dev/null
+++ b/test/asan/TestCases/Linux/coverage-levels.cc
@@ -0,0 +1,20 @@
+// Test various levels of coverage
+//
+// RUN: %clangxx_asan -O1 -fsanitize-coverage=1 %s -o %t
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1
+// RUN: %clangxx_asan -O1 -fsanitize-coverage=2 %s -o %t
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2
+// RUN: %clangxx_asan -O1 -fsanitize-coverage=3 %s -o %t
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3
+//
+// REQUIRES: asan-64-bits
+
+volatile int sink;
+int main(int argc, char **argv) {
+ if (argc == 0)
+ sink = 0;
+}
+
+// CHECK1: 1 PCs written
+// CHECK2: 2 PCs written
+// CHECK3: 3 PCs written
diff --git a/test/asan/TestCases/Linux/coverage-maybe-open-file.cc b/test/asan/TestCases/Linux/coverage-maybe-open-file.cc
index 1cd2253..4664cef 100644
--- a/test/asan/TestCases/Linux/coverage-maybe-open-file.cc
+++ b/test/asan/TestCases/Linux/coverage-maybe-open-file.cc
@@ -1,7 +1,7 @@
// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316
// XFAIL: android
//
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s -o %t
+// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t
// RUN: rm -rf %T/coverage-maybe-open-file
// RUN: mkdir -p %T/coverage-maybe-open-file && cd %T/coverage-maybe-open-file
// RUN: ASAN_OPTIONS=coverage=1 %run %t | FileCheck %s --check-prefix=CHECK-success
diff --git a/test/asan/TestCases/Linux/coverage-module-unloaded.cc b/test/asan/TestCases/Linux/coverage-module-unloaded.cc
index 4b64a03..449841e 100644
--- a/test/asan/TestCases/Linux/coverage-module-unloaded.cc
+++ b/test/asan/TestCases/Linux/coverage-module-unloaded.cc
@@ -1,8 +1,8 @@
// Check that unloading a module doesn't break coverage dumping for remaining
// modules.
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %T/libcoverage_module_unloaded_test_1.so -fPIC
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %T/libcoverage_module_unloaded_test_2.so -fPIC
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSO_DIR=\"%T\" %s -o %t
+// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_module_unloaded_test_1.so -fPIC
+// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_module_unloaded_test_2.so -fPIC
+// RUN: %clangxx_asan -fsanitize-coverage=1 -DSO_DIR=\"%T\" %s -o %t
// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1
// RUN: mkdir -p %T/coverage-module-unloaded && cd %T/coverage-module-unloaded
// RUN: %run %t 2>&1 | FileCheck %s
diff --git a/test/asan/TestCases/Linux/coverage-sandboxing.cc b/test/asan/TestCases/Linux/coverage-sandboxing.cc
index 6af9865..56f9c40 100644
--- a/test/asan/TestCases/Linux/coverage-sandboxing.cc
+++ b/test/asan/TestCases/Linux/coverage-sandboxing.cc
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -mllvm -asan-coverage=2 -DSHARED %s -shared -o %T/libcoverage_sandboxing_test.so -fPIC
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s -o %t -Wl,-R,\$ORIGIN -L%T -lcoverage_sandboxing_test
+// RUN: %clangxx_asan -fsanitize-coverage=2 -DSHARED %s -shared -o %T/libcoverage_sandboxing_test.so -fPIC
+// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t -Wl,-R,\$ORIGIN -L%T -lcoverage_sandboxing_test
// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1
// RUN: rm -rf %T/coverage_sandboxing_test
// RUN: mkdir %T/coverage_sandboxing_test && cd %T/coverage_sandboxing_test
diff --git a/test/asan/TestCases/Linux/coverage-tracing.cc b/test/asan/TestCases/Linux/coverage-tracing.cc
new file mode 100644
index 0000000..89ab0d2
--- /dev/null
+++ b/test/asan/TestCases/Linux/coverage-tracing.cc
@@ -0,0 +1,22 @@
+// Test -mllvm -sanitizer-coverage-experimental-tracing
+//
+// RUN: %clangxx_asan -O1 -fsanitize-coverage=1 -mllvm -sanitizer-coverage-experimental-tracing %s -o %t
+// RUN: rm -rf %T/coverage-tracing
+// RUN: mkdir -p %T/coverage-tracing
+// RUN: ASAN_OPTIONS=coverage=1:coverage_dir=%T/coverage-tracing:verbosity=1 %run %t 1 2 3 4 2>&1 | FileCheck %s
+// RUN: rm -rf %T/coverage-tracing
+//
+// REQUIRES: asan-64-bits
+
+volatile int sink;
+int main(int argc, char **argv) {
+ volatile int i = 0;
+ do {
+ sink = 0;
+ i++;
+ } while (i < argc);
+ return 0;
+}
+
+// CHECK: CovDump: Trace: {{[3-9]}} PCs written
+// CHECK: CovDump: Trace: {{[6-9]}} Events written
diff --git a/test/asan/TestCases/Linux/coverage.cc b/test/asan/TestCases/Linux/coverage.cc
index 5af4d69..f6eb0ae 100644
--- a/test/asan/TestCases/Linux/coverage.cc
+++ b/test/asan/TestCases/Linux/coverage.cc
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %T/libcoverage_test.so -fPIC
-// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s -o %t -Wl,-R,\$ORIGIN -L%T -lcoverage_test
+// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_test.so -fPIC
+// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t -Wl,-R,\$ORIGIN -L%T -lcoverage_test
// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1
// RUN: mkdir -p %T/coverage && cd %T/coverage
// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-main
@@ -13,6 +13,8 @@
// https://code.google.com/p/address-sanitizer/issues/detail?id=263
// XFAIL: android
+#include "sanitizer/common_interface_defs.h"
+#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
@@ -29,8 +31,12 @@
int main(int argc, char **argv) {
fprintf(stderr, "PID: %d\n", getpid());
for (int i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "foo"))
+ if (!strcmp(argv[i], "foo")) {
+ uintptr_t old_coverage = __sanitizer_get_total_unique_coverage();
foo();
+ uintptr_t new_coverage = __sanitizer_get_total_unique_coverage();
+ assert(new_coverage > old_coverage);
+ }
if (!strcmp(argv[i], "bar"))
bar();
}
diff --git a/test/asan/TestCases/Linux/heap-overflow-large.cc b/test/asan/TestCases/Linux/heap-overflow-large.cc
deleted file mode 100644
index 26c7015..0000000
--- a/test/asan/TestCases/Linux/heap-overflow-large.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Regression test for
-// https://code.google.com/p/address-sanitizer/issues/detail?id=183
-
-// RUN: %clangxx_asan -O2 %s -o %t
-// RUN: not %run %t 12 2>&1 | FileCheck %s
-// RUN: not %run %t 100 2>&1 | FileCheck %s
-// RUN: not %run %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/test/asan/TestCases/Linux/interface_symbols_linux.c b/test/asan/TestCases/Linux/interface_symbols_linux.c
index 72416f1..a616732 100644
--- a/test/asan/TestCases/Linux/interface_symbols_linux.c
+++ b/test/asan/TestCases/Linux/interface_symbols_linux.c
@@ -3,13 +3,11 @@
// 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: | sed -E "s/__asan_init_v[0-9]+/__asan_init/" \
// RUN: | grep -v "__asan_default_options" \
// RUN: | grep -v "__asan_stack_" \
// RUN: | grep -v "__asan_on_error" > %t.symbols
// RUN: cat %p/../../../../lib/asan/asan_interface_internal.h \
-// RUN: %p/../../../../lib/asan/asan_init_version.h \
// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \
// RUN: | grep -v "OPTIONAL" \
// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \
diff --git a/test/asan/TestCases/Linux/malloc_delete_mismatch.cc b/test/asan/TestCases/Linux/malloc_delete_mismatch.cc
index 085eb15..18d65ce 100644
--- a/test/asan/TestCases/Linux/malloc_delete_mismatch.cc
+++ b/test/asan/TestCases/Linux/malloc_delete_mismatch.cc
@@ -13,6 +13,7 @@
// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s
// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <stdlib.h>
static volatile char *x;
diff --git a/test/asan/TestCases/Linux/odr-violation.cc b/test/asan/TestCases/Linux/odr-violation.cc
index 48e0907..ddc68a2 100644
--- a/test/asan/TestCases/Linux/odr-violation.cc
+++ b/test/asan/TestCases/Linux/odr-violation.cc
@@ -23,19 +23,20 @@
#endif
#if BUILD_SO
-char G[SZ];
+namespace foo { char G[SZ]; }
#else
#include <stdio.h>
-char G[100];
+namespace foo { char G[100]; }
+// CHECK: ERROR: AddressSanitizer: odr-violation
+// CHECK: size=100 'foo::G' {{.*}}odr-violation.cc:[[@LINE-2]]:22
+// CHECK: size={{4|100}} 'foo::G'
int main(int argc, char **argv) {
- printf("PASS: %p\n", &G);
+ printf("PASS: %p\n", &foo::G);
}
#endif
-// CHECK: ERROR: AddressSanitizer: odr-violation
-// CHECK: size=100 G
-// CHECK: size={{4|100}} G
// CHECK: These globals were registered at these points:
// CHECK: ODR-EXE
// CHECK: ODR-SO
+// CHECK: SUMMARY: AddressSanitizer: odr-violation: global 'foo::G' at {{.*}}odr-violation.cc
// DISABLED: PASS
diff --git a/test/asan/TestCases/Linux/ptrace.cc b/test/asan/TestCases/Linux/ptrace.cc
index 45febd3..7e5acb6 100644
--- a/test/asan/TestCases/Linux/ptrace.cc
+++ b/test/asan/TestCases/Linux/ptrace.cc
@@ -3,7 +3,7 @@
//
// RUN: %clangxx_asan -O0 %s -o %t && %run %t
// RUN: %clangxx_asan -DPOSITIVE -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
-// XFAIL: arm-linux-gnueabi
+// REQUIRES: x86_64-supported-target,i386-supported-target
#include <assert.h>
#include <stdio.h>
diff --git a/test/asan/TestCases/Linux/sized_delete_test.cc b/test/asan/TestCases/Linux/sized_delete_test.cc
new file mode 100644
index 0000000..823e3c0
--- /dev/null
+++ b/test/asan/TestCases/Linux/sized_delete_test.cc
@@ -0,0 +1,93 @@
+// RUN: %clangxx_asan -Xclang -fsized-deallocation -O0 %s -o %t
+// RUN: not %run %t scalar 2>&1 | FileCheck %s -check-prefix=SCALAR
+// RUN: ASAN_OPTIONS=new_delete_type_mismatch=1 not %run %t scalar 2>&1 | FileCheck %s -check-prefix=SCALAR
+// RUN: not %run %t array 2>&1 | FileCheck %s -check-prefix=ARRAY
+// RUN: ASAN_OPTIONS=new_delete_type_mismatch=1 not %run %t array 2>&1 | FileCheck %s -check-prefix=ARRAY
+// RUN: ASAN_OPTIONS=new_delete_type_mismatch=0 %run %t scalar
+// RUN: ASAN_OPTIONS=new_delete_type_mismatch=0 %run %t array
+
+// Sized-delete is implemented with a weak delete() definition.
+// Weak symbols are kind of broken on Android.
+// XFAIL: android
+
+#include <new>
+#include <stdio.h>
+#include <string>
+
+inline void break_optimization(void *arg) {
+ __asm__ __volatile__("" : : "r" (arg) : "memory");
+}
+
+struct S12 {
+ int a, b, c;
+};
+
+struct S20 {
+ int a, b, c, d, e;
+};
+
+struct D1 {
+ int a, b, c;
+ ~D1() { fprintf(stderr, "D1::~D1\n"); }
+};
+
+struct D2 {
+ int a, b, c, d, e;
+ ~D2() { fprintf(stderr, "D2::~D2\n"); }
+};
+
+void Del12(S12 *x) {
+ break_optimization(x);
+ delete x;
+}
+void Del12NoThrow(S12 *x) {
+ break_optimization(x);
+ operator delete(x, std::nothrow);
+}
+void Del12Ar(S12 *x) {
+ break_optimization(x);
+ delete [] x;
+}
+void Del12ArNoThrow(S12 *x) {
+ break_optimization(x);
+ operator delete[](x, std::nothrow);
+}
+
+int main(int argc, char **argv) {
+ if (argc != 2) return 1;
+ std::string flag = argv[1];
+ // These are correct.
+ Del12(new S12);
+ Del12NoThrow(new S12);
+ Del12Ar(new S12[100]);
+ Del12ArNoThrow(new S12[100]);
+
+ // Here we pass wrong type of pointer to delete,
+ // but [] and nothrow variants of delete are not sized.
+ Del12Ar(reinterpret_cast<S12*>(new S20[100]));
+ Del12NoThrow(reinterpret_cast<S12*>(new S20));
+ Del12ArNoThrow(reinterpret_cast<S12*>(new S20[100]));
+ fprintf(stderr, "OK SO FAR\n");
+ // SCALAR: OK SO FAR
+ // ARRAY: OK SO FAR
+ if (flag == "scalar") {
+ // Here asan should bark as we are passing a wrong type of pointer
+ // to sized delete.
+ Del12(reinterpret_cast<S12*>(new S20));
+ // SCALAR: AddressSanitizer: new-delete-type-mismatch
+ // SCALAR: object passed to delete has wrong type:
+ // SCALAR: size of the allocated type: 20 bytes;
+ // SCALAR: size of the deallocated type: 12 bytes.
+ // SCALAR: is located 0 bytes inside of 20-byte region
+ // SCALAR: SUMMARY: AddressSanitizer: new-delete-type-mismatch
+ } else if (flag == "array") {
+ D1 *d1 = reinterpret_cast<D1*>(new D2[10]);
+ break_optimization(d1);
+ delete [] d1;
+ // ARRAY-NOT: D2::~D2
+ // ARRAY: D1::~D1
+ // ARRAY: AddressSanitizer: new-delete-type-mismatch
+ // ARRAY: size of the allocated type: 20{{4|8}} bytes;
+ // ARRAY: size of the deallocated type: 12{{4|8}} bytes.
+ }
+}
diff --git a/test/asan/TestCases/Linux/stack-trace-dlclose.cc b/test/asan/TestCases/Linux/stack-trace-dlclose.cc
index 44c8a03..e494e56 100644
--- a/test/asan/TestCases/Linux/stack-trace-dlclose.cc
+++ b/test/asan/TestCases/Linux/stack-trace-dlclose.cc
@@ -5,6 +5,7 @@
// RUN: %clangxx_asan -DSO_DIR=\"%T\" %s -o %t
// RUN: ASAN_OPTIONS=exitcode=0 %run %t 2>&1 | FileCheck %s
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <assert.h>
#include <dlfcn.h>
diff --git a/test/asan/TestCases/Posix/allow_user_segv.cc b/test/asan/TestCases/Posix/allow_user_segv.cc
index 1768643..b6443fa 100644
--- a/test/asan/TestCases/Posix/allow_user_segv.cc
+++ b/test/asan/TestCases/Posix/allow_user_segv.cc
@@ -6,12 +6,22 @@
#include <signal.h>
#include <stdio.h>
+#include <stdlib.h>
-struct sigaction user_sigaction;
-struct sigaction original_sigaction;
+struct sigaction original_sigaction_sigbus;
+struct sigaction original_sigaction_sigsegv;
void User_OnSIGSEGV(int signum, siginfo_t *siginfo, void *context) {
fprintf(stderr, "User sigaction called\n");
+ struct sigaction original_sigaction;
+ if (signum == SIGBUS)
+ original_sigaction = original_sigaction_sigbus;
+ else if (signum == SIGSEGV)
+ original_sigaction = original_sigaction_sigsegv;
+ else {
+ printf("Invalid signum");
+ exit(1);
+ }
if (original_sigaction.sa_flags | SA_SIGINFO)
original_sigaction.sa_sigaction(signum, siginfo, context);
else
@@ -23,21 +33,22 @@
return *x;
}
-int main() {
+int InstallHandler(int signum, struct sigaction *original_sigaction) {
+ struct sigaction user_sigaction;
user_sigaction.sa_sigaction = User_OnSIGSEGV;
user_sigaction.sa_flags = SA_SIGINFO;
-#if defined(__APPLE__) && !defined(__LP64__)
- // On 32-bit Darwin KERN_PROTECTION_FAILURE (SIGBUS) is delivered.
- int signum = SIGBUS;
-#else
- // On 64-bit Darwin KERN_INVALID_ADDRESS (SIGSEGV) is delivered.
- // On Linux SIGSEGV is delivered as well.
- int signum = SIGSEGV;
-#endif
- if (sigaction(signum, &user_sigaction, &original_sigaction)) {
+ if (sigaction(signum, &user_sigaction, original_sigaction)) {
perror("sigaction");
return 1;
}
+ return 0;
+}
+
+int main() {
+ // Let's install handlers for both SIGSEGV and SIGBUS, since pre-Yosemite
+ // 32-bit Darwin triggers SIGBUS instead.
+ if (InstallHandler(SIGSEGV, &original_sigaction_sigsegv)) return 1;
+ if (InstallHandler(SIGBUS, &original_sigaction_sigbus)) return 1;
fprintf(stderr, "User sigaction installed\n");
return DoSEGV();
}
diff --git a/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cc b/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cc
index 84dc1c2..6ed02f4 100644
--- a/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cc
+++ b/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cc
@@ -5,9 +5,10 @@
// shared object files.
// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
-// RUN: %clangxx_asan -O0 %s -ldl -o %t
+// RUN: %clangxx_asan -O0 %s -o %t
// RUN: env ASAN_OPTIONS=symbolize=0 not %run %t 2>&1 | %asan_symbolize | FileCheck %s
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
#if !defined(SHARED_LIB)
#include <dlfcn.h>
diff --git a/test/asan/TestCases/Posix/asprintf.cc b/test/asan/TestCases/Posix/asprintf.cc
new file mode 100644
index 0000000..6946e50
--- /dev/null
+++ b/test/asan/TestCases/Posix/asprintf.cc
@@ -0,0 +1,20 @@
+// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O3 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv) {
+ char *p;
+ int res = asprintf(&p, "%d", argc);
+ fprintf(stderr, "x%d %sx\n", res, p);
+ // CHECK: x1 1x
+ free(p);
+ fprintf(stderr, "DONE\n");
+ // CHECK: DONE
+ return 0;
+}
diff --git a/test/asan/TestCases/Posix/assign_large_valloc_to_global.cc b/test/asan/TestCases/Posix/assign_large_valloc_to_global.cc
index 15096e5..ad547ce 100644
--- a/test/asan/TestCases/Posix/assign_large_valloc_to_global.cc
+++ b/test/asan/TestCases/Posix/assign_large_valloc_to_global.cc
@@ -1,8 +1,9 @@
// Make sure we don't report a leak nor hang.
// RUN: %clangxx_asan -O3 %s -o %t && %run %t
#include <stdlib.h>
-#ifndef __APPLE__
+#include <unistd.h>
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
# include <malloc.h>
-#endif // __APPLE__
+#endif // !__APPLE__ && !__FreeBSD__
int *p = (int*)valloc(1 << 20);
int main() { }
diff --git a/test/asan/TestCases/Linux/glob.cc b/test/asan/TestCases/Posix/glob.cc
similarity index 100%
rename from test/asan/TestCases/Linux/glob.cc
rename to test/asan/TestCases/Posix/glob.cc
diff --git a/test/asan/TestCases/Linux/glob_test_root/aa b/test/asan/TestCases/Posix/glob_test_root/aa
similarity index 100%
rename from test/asan/TestCases/Linux/glob_test_root/aa
rename to test/asan/TestCases/Posix/glob_test_root/aa
diff --git a/test/asan/TestCases/Linux/glob_test_root/ab b/test/asan/TestCases/Posix/glob_test_root/ab
similarity index 100%
rename from test/asan/TestCases/Linux/glob_test_root/ab
rename to test/asan/TestCases/Posix/glob_test_root/ab
diff --git a/test/asan/TestCases/Linux/glob_test_root/ba b/test/asan/TestCases/Posix/glob_test_root/ba
similarity index 100%
rename from test/asan/TestCases/Linux/glob_test_root/ba
rename to test/asan/TestCases/Posix/glob_test_root/ba
diff --git a/test/asan/TestCases/Posix/init-order-dlopen.cc b/test/asan/TestCases/Posix/init-order-dlopen.cc
index 2b86ace..6f20477 100644
--- a/test/asan/TestCases/Posix/init-order-dlopen.cc
+++ b/test/asan/TestCases/Posix/init-order-dlopen.cc
@@ -10,8 +10,8 @@
// 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 -O0 %s -pthread -ldl -o %t -Wl,--export-dynamic || \
-// RUN: %clangxx_asan -O0 %s -pthread -ldl -o %t
+// RUN: %clangxx_asan -O0 %s -pthread -o %t -Wl,--export-dynamic || \
+// RUN: %clangxx_asan -O0 %s -pthread -o %t
// RUN: ASAN_OPTIONS=strict_init_order=true %run %t 2>&1 | FileCheck %s
#if !defined(SHARED_LIB)
#include <dlfcn.h>
diff --git a/test/asan/TestCases/Posix/large_allocator_unpoisons_on_free.cc b/test/asan/TestCases/Posix/large_allocator_unpoisons_on_free.cc
index e467f76..0a49980 100644
--- a/test/asan/TestCases/Posix/large_allocator_unpoisons_on_free.cc
+++ b/test/asan/TestCases/Posix/large_allocator_unpoisons_on_free.cc
@@ -8,6 +8,7 @@
#include <string.h>
#include <sys/mman.h>
#include <stdlib.h>
+#include <unistd.h>
#ifdef __ANDROID__
#include <malloc.h>
@@ -23,11 +24,12 @@
#endif
int main() {
- const int kPageSize = 4096;
+ const long kPageSize = sysconf(_SC_PAGESIZE);
void *p = my_memalign(kPageSize, 1024 * 1024);
free(p);
- char *q = (char *)mmap(p, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0);
+ char *q = (char *)mmap(p, kPageSize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
assert(q == p);
memset(q, 42, kPageSize);
diff --git a/test/asan/TestCases/Posix/new_array_cookie_test.cc b/test/asan/TestCases/Posix/new_array_cookie_test.cc
new file mode 100644
index 0000000..85d51f3
--- /dev/null
+++ b/test/asan/TestCases/Posix/new_array_cookie_test.cc
@@ -0,0 +1,24 @@
+// REQUIRES: asan-64-bits
+// RUN: %clangxx_asan -O3 %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+// RUN: ASAN_OPTIONS=poison_array_cookie=1 not %run %t 2>&1 | FileCheck %s
+// RUN: ASAN_OPTIONS=poison_array_cookie=0 not %run %t 2>&1 | FileCheck %s --check-prefix=NO_COOKIE
+#include <stdio.h>
+#include <stdlib.h>
+struct C {
+ int x;
+ ~C() {
+ fprintf(stderr, "ZZZZZZZZ\n");
+ exit(1);
+ }
+};
+
+int main(int argc, char **argv) {
+ C *buffer = new C[argc];
+ buffer[-2].x = 10;
+// CHECK: AddressSanitizer: heap-buffer-overflow
+// CHECK: in main {{.*}}new_array_cookie_test.cc:[[@LINE-2]]
+// CHECK: is located 0 bytes inside of 12-byte region
+// NO_COOKIE: ZZZZZZZZ
+ delete [] buffer;
+}
diff --git a/test/asan/TestCases/Posix/new_array_cookie_uaf_test.cc b/test/asan/TestCases/Posix/new_array_cookie_uaf_test.cc
new file mode 100644
index 0000000..c35cceb
--- /dev/null
+++ b/test/asan/TestCases/Posix/new_array_cookie_uaf_test.cc
@@ -0,0 +1,38 @@
+// REQUIRES: asan-64-bits
+// RUN: %clangxx_asan -O3 %s -o %t
+// RUN: ASAN_OPTIONS=poison_array_cookie=1 not %run %t 2>&1 | FileCheck %s --check-prefix=COOKIE
+// RUN: ASAN_OPTIONS=poison_array_cookie=0 not %run %t 2>&1 | FileCheck %s --check-prefix=NO_COOKIE
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+int dtor_counter;
+struct C {
+ int x;
+ ~C() {
+ dtor_counter++;
+ fprintf(stderr, "DTOR %d\n", dtor_counter);
+ }
+};
+
+__attribute__((noinline)) void Delete(C *c) { delete[] c; }
+__attribute__((no_sanitize_address)) void Write42ToCookie(C *c) {
+ long *p = reinterpret_cast<long*>(c);
+ p[-1] = 42;
+}
+
+int main(int argc, char **argv) {
+ C *buffer = new C[argc];
+ delete [] buffer;
+ Write42ToCookie(buffer);
+ delete [] buffer;
+// COOKIE: DTOR 1
+// COOKIE-NOT: DTOR 2
+// COOKIE: AddressSanitizer: loaded array cookie from free-d memory
+// COOKIE: AddressSanitizer: attempting double-free
+// NO_COOKIE: DTOR 1
+// NO_COOKIE: DTOR 43
+// NO_COOKIE-NOT: DTOR 44
+// NO_COOKIE-NOT: AddressSanitizer: loaded array cookie from free-d memory
+// NO_COOKIE: AddressSanitizer: attempting double-free
+
+}
diff --git a/test/asan/TestCases/Posix/new_array_cookie_with_new_from_class.cc b/test/asan/TestCases/Posix/new_array_cookie_with_new_from_class.cc
new file mode 100644
index 0000000..1cea6f6
--- /dev/null
+++ b/test/asan/TestCases/Posix/new_array_cookie_with_new_from_class.cc
@@ -0,0 +1,38 @@
+// Test that we do not poison the array cookie if the operator new is defined
+// inside the class.
+// RUN: %clangxx_asan %s -o %t && %run %t
+//
+// XFAIL: android
+// XFAIL: armv7l-unknown-linux-gnueabihf
+#include <new>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+struct Foo {
+ void *operator new(size_t s) { return Allocate(s); }
+ void *operator new[] (size_t s) { return Allocate(s); }
+ ~Foo();
+ static void *allocated;
+ static void *Allocate(size_t s) {
+ assert(!allocated);
+ return allocated = ::new char[s];
+ }
+};
+
+Foo::~Foo() {}
+void *Foo::allocated;
+
+Foo *getFoo(size_t n) {
+ return new Foo[n];
+}
+
+int main() {
+ Foo *foo = getFoo(10);
+ fprintf(stderr, "foo : %p\n", foo);
+ fprintf(stderr, "alloc: %p\n", Foo::allocated);
+ assert(reinterpret_cast<uintptr_t>(foo) ==
+ reinterpret_cast<uintptr_t>(Foo::allocated) + sizeof(void*));
+ *reinterpret_cast<uintptr_t*>(Foo::allocated) = 42;
+ return 0;
+}
diff --git a/test/asan/TestCases/Posix/shared-lib-test.cc b/test/asan/TestCases/Posix/shared-lib-test.cc
index 21f26b3..a0827b5 100644
--- a/test/asan/TestCases/Posix/shared-lib-test.cc
+++ b/test/asan/TestCases/Posix/shared-lib-test.cc
@@ -1,11 +1,11 @@
// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
-// RUN: %clangxx_asan -O0 %s -ldl -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -O1 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
-// RUN: %clangxx_asan -O1 %s -ldl -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -O2 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
-// RUN: %clangxx_asan -O2 %s -ldl -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -O3 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
-// RUN: %clangxx_asan -O3 %s -ldl -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s
// XFAIL: arm-linux-gnueabi
#if !defined(SHARED_LIB)
@@ -38,7 +38,7 @@
// CHECK: {{ #1 0x.* in main .*shared-lib-test.cc:}}[[@LINE-4]]
return 0;
}
-#else // SHARED_LIBS
+#else // SHARED_LIB
#include <stdio.h>
#include <string.h>
@@ -54,4 +54,4 @@
void inc2(int *a, int index) {
a[index]++;
}
-#endif // SHARED_LIBS
+#endif // SHARED_LIB
diff --git a/test/asan/TestCases/Posix/start-deactivated.cc b/test/asan/TestCases/Posix/start-deactivated.cc
index 43023fb..d60677a 100644
--- a/test/asan/TestCases/Posix/start-deactivated.cc
+++ b/test/asan/TestCases/Posix/start-deactivated.cc
@@ -4,9 +4,10 @@
// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
// RUN: %clangxx -O0 %s -c -o %t.o
-// RUN: %clangxx_asan -O0 %t.o -ldl -o %t
+// RUN: %clangxx_asan -O0 %t.o -o %t
// RUN: ASAN_OPTIONS=start_deactivated=1 not %run %t 2>&1 | FileCheck %s
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
#if !defined(SHARED_LIB)
#include <dlfcn.h>
diff --git a/test/asan/TestCases/Linux/tsd_dtor_leak.cc b/test/asan/TestCases/Posix/tsd_dtor_leak.cc
similarity index 100%
rename from test/asan/TestCases/Linux/tsd_dtor_leak.cc
rename to test/asan/TestCases/Posix/tsd_dtor_leak.cc
diff --git a/test/asan/TestCases/Posix/wait.cc b/test/asan/TestCases/Posix/wait.cc
index 56a03bd..99d0212 100644
--- a/test/asan/TestCases/Posix/wait.cc
+++ b/test/asan/TestCases/Posix/wait.cc
@@ -7,14 +7,9 @@
// RUN: %clangxx_asan -DWAIT3 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -DWAIT3 -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s
-// RUN: %clangxx_asan -DWAIT4 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
-// RUN: %clangxx_asan -DWAIT4 -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s
-
// RUN: %clangxx_asan -DWAIT3_RUSAGE -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -DWAIT3_RUSAGE -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s
-// RUN: %clangxx_asan -DWAIT4_RUSAGE -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
-// RUN: %clangxx_asan -DWAIT4_RUSAGE -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s
#include <assert.h>
#include <sys/wait.h>
@@ -32,16 +27,10 @@
res = waitpid(pid, status, 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)
+#elif defined(WAIT3_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}}
diff --git a/test/asan/TestCases/Posix/wait4.cc b/test/asan/TestCases/Posix/wait4.cc
new file mode 100644
index 0000000..b95246e
--- /dev/null
+++ b/test/asan/TestCases/Posix/wait4.cc
@@ -0,0 +1,43 @@
+// RUN: %clangxx_asan -DWAIT4 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -DWAIT4 -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+// RUN: %clangxx_asan -DWAIT4_RUSAGE -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -DWAIT4_RUSAGE -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+// XFAIL: android
+
+#include <assert.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+int main(int argc, char **argv) {
+ // This test passes on some versions of Android NDK and fails on other.
+ // https://code.google.com/p/memory-sanitizer/issues/detail?id=64
+ // Make it fail unconditionally on Android.
+#ifdef __ANDROID__
+ return 0;
+#endif
+
+ pid_t pid = fork();
+ if (pid) { // parent
+ int x[3];
+ int *status = x + argc * 3;
+ int res;
+#if defined(WAIT4)
+ res = wait4(pid, status, WNOHANG, NULL);
+#elif defined(WAIT4_RUSAGE)
+ struct rusage *ru = (struct rusage*)(x + argc * 3);
+ int good_status;
+ res = wait4(pid, &good_status, WNOHANG, ru);
+#endif
+ // CHECK: stack-buffer-overflow
+ // CHECK: {{WRITE of size .* at 0x.* thread T0}}
+ // CHECK: {{in .*wait}}
+ // CHECK: {{in main .*wait4.cc:}}
+ // CHECK: is located in stack of thread T0 at offset
+ // CHECK: {{in main}}
+ return res == -1 ? 1 : 0;
+ }
+ // child
+ return 0;
+}
diff --git a/test/asan/TestCases/Posix/waitid.cc b/test/asan/TestCases/Posix/waitid.cc
index edfe3ed..8b516dc 100644
--- a/test/asan/TestCases/Posix/waitid.cc
+++ b/test/asan/TestCases/Posix/waitid.cc
@@ -4,6 +4,7 @@
#include <assert.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <signal.h>
int main(int argc, char **argv) {
pid_t pid = fork();
diff --git a/test/asan/TestCases/Windows/demangled_names.cc b/test/asan/TestCases/Windows/demangled_names.cc
new file mode 100644
index 0000000..a528555
--- /dev/null
+++ b/test/asan/TestCases/Windows/demangled_names.cc
@@ -0,0 +1,50 @@
+// RUN: %clang_cl_asan -O0 %s -Fe%t
+// RUN: not %run %t 2>&1 | FileCheck %s
+//
+// This test makes sure ASan symbolizes stack traces the way they are typically
+// symbolized on Windows.
+#include <malloc.h>
+
+namespace foo {
+// A template function in a namespace.
+template<int x>
+void bar(char *p) {
+ *p = x;
+}
+
+// A regular function in a namespace.
+void spam(char *p) {
+ bar<42>(p);
+}
+}
+
+// A multi-argument template with a bool template parameter.
+template<typename T, bool U>
+void baz(T t) {
+ if (U)
+ foo::spam(t);
+}
+
+template<typename T>
+struct A {
+ A(T v) { v_ = v; }
+ ~A();
+ char *v_;
+};
+
+// A destructor of a template class.
+template<>
+A<char*>::~A() {
+ baz<char*, true>(v_);
+}
+
+int main() {
+ char *buffer = (char*)malloc(42);
+ free(buffer);
+ A<char*> a(buffer);
+// CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]]
+// CHECK: foo::bar<42> {{.*}}demangled_names.cc
+// CHECK: foo::spam {{.*}}demangled_names.cc
+// CHECK: baz<char *,1> {{.*}}demangled_names.cc
+// CHECK: A<char *>::~A<char *> {{.*}}demangled_names.cc
+}
diff --git a/test/asan/TestCases/Windows/dll_and_lib.cc b/test/asan/TestCases/Windows/dll_and_lib.cc
new file mode 100644
index 0000000..bddaa32
--- /dev/null
+++ b/test/asan/TestCases/Windows/dll_and_lib.cc
@@ -0,0 +1,19 @@
+// Just make sure we can link an implib into another DLL
+// This used to fail between r212699 and r212814.
+// RUN: %clang_cl_asan -DCONFIG=1 %s -c -Fo%t.1.obj
+// RUN: link /nologo /DLL /OUT:%t.1.dll %t.1.obj %asan_dll_thunk
+// RUN: %clang_cl_asan -DCONFIG=2 %s -c -Fo%t.2.obj
+// RUN: link /nologo /DLL /OUT:%t.2.dll %t.2.obj %t.1.lib %asan_dll_thunk
+// REQUIRES: asan-static-runtime
+
+#if CONFIG==1
+extern "C" __declspec(dllexport) int f1() {
+ int x = 0;
+ return 1;
+}
+#else
+extern "C" __declspec(dllexport) int f2() {
+ int x = 0;
+ return 2;
+}
+#endif
diff --git a/test/asan/TestCases/Windows/dll_host.cc b/test/asan/TestCases/Windows/dll_host.cc
index 5eb710e..d3b4c14 100644
--- a/test/asan/TestCases/Windows/dll_host.cc
+++ b/test/asan/TestCases/Windows/dll_host.cc
@@ -8,12 +8,13 @@
// RUN: dumpbin /EXPORTS %t | grep -o "__asan_wrap[^ ]*" | grep -v @ | sort | uniq > %t.exported_wrappers
//
// Get the list of ASan wrappers imported by the DLL RTL:
-// RUN: grep INTERCEPT_LIBRARY_FUNCTION %p/../../../../lib/asan/asan_dll_thunk.cc | grep -v define | sed "s/.*(\(.*\)).*/__asan_wrap_\1/" | sort | uniq > %t.dll_imports
+// RUN: grep INTERCEPT_LIBRARY_FUNCTION %p/../../../../lib/asan/asan_win_dll_thunk.cc | grep -v define | sed "s/.*(\(.*\)).*/__asan_wrap_\1/" | sort | uniq > %t.dll_imports
//
// Now make sure the DLL thunk imports everything:
// RUN: echo
-// RUN: echo "=== NOTE === If you see a mismatch below, please update asan_dll_thunk.cc"
+// RUN: echo "=== NOTE === If you see a mismatch below, please update asan_win_dll_thunk.cc"
// RUN: diff %t.dll_imports %t.exported_wrappers
+// REQUIRES: asan-static-runtime
#include <stdio.h>
#include <windows.h>
diff --git a/test/asan/TestCases/Windows/dll_large_function.cc b/test/asan/TestCases/Windows/dll_large_function.cc
new file mode 100644
index 0000000..039d01f
--- /dev/null
+++ b/test/asan/TestCases/Windows/dll_large_function.cc
@@ -0,0 +1,12 @@
+// Make sure we can link a DLL with large functions which would mean
+// functions such as __asan_loadN and __asan_storeN will be called
+// from the DLL. We simulate the large function with
+// -mllvm -asan-instrumentation-with-call-threshold=0.
+// RUN: %clang_cl_asan %s -c -Fo%t.obj -mllvm -asan-instrumentation-with-call-threshold=0
+// RUN: link /nologo /DLL /OUT:%t.dll %t.obj %asan_dll_thunk
+// REQUIRES: asan-static-runtime
+
+void f(long* foo, long* bar) {
+ // One load and one store
+ *foo = *bar;
+}
diff --git a/test/asan/TestCases/Windows/dll_null_deref.cc b/test/asan/TestCases/Windows/dll_null_deref.cc
new file mode 100644
index 0000000..0fb18de
--- /dev/null
+++ b/test/asan/TestCases/Windows/dll_null_deref.cc
@@ -0,0 +1,18 @@
+// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t
+// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll
+// RUN: not %run %t %t.dll 2>&1 | FileCheck %s
+
+__attribute__((noinline))
+static void NullDeref(int *ptr) {
+ // CHECK: ERROR: AddressSanitizer: access-violation on unknown address
+ // CHECK: {{0x0*000.. .*pc 0x.*}}
+ ptr[10]++; // BOOM
+}
+
+extern "C" __declspec(dllexport)
+int test_function() {
+ NullDeref((int*)0);
+ // CHECK: {{ #1 0x.* in test_function .*\dll_null_deref.cc:}}[[@LINE-1]]
+ // CHECK: AddressSanitizer can not provide additional info.
+ return 0;
+}
diff --git a/test/asan/TestCases/Windows/dll_operator_array_new_left_oob.cc b/test/asan/TestCases/Windows/dll_operator_array_new_left_oob.cc
index e52345e..736ce80 100644
--- a/test/asan/TestCases/Windows/dll_operator_array_new_left_oob.cc
+++ b/test/asan/TestCases/Windows/dll_operator_array_new_left_oob.cc
@@ -13,10 +13,10 @@
//
// CHECK: [[ADDR]] is located 1 bytes to the left of 42-byte region
// CHECK-LABEL: allocated by thread T0 here:
-// FIXME: should get rid of the malloc/free frames called from the inside of
-// operator new/delete in DLLs. Also, the operator new frame should have [].
-// CHECK-NEXT: malloc
-// CHECK-NEXT: operator new
+// FIXME: Should get rid of the malloc/free frames called from the inside of
+// operator new/delete in DLLs when using -MT CRT.
+// FIXME: The 'operator new' frame should have [].
+// CHECK: operator new
// CHECK-NEXT: test_function {{.*}}dll_operator_array_new_left_oob.cc:[[@LINE-13]]
// CHECK-NEXT: main {{.*}}dll_host.cc
// CHECK-LABEL: SUMMARY
diff --git a/test/asan/TestCases/Windows/dll_operator_array_new_with_dtor_left_oob.cc b/test/asan/TestCases/Windows/dll_operator_array_new_with_dtor_left_oob.cc
index c61d4eb..8306a73 100644
--- a/test/asan/TestCases/Windows/dll_operator_array_new_with_dtor_left_oob.cc
+++ b/test/asan/TestCases/Windows/dll_operator_array_new_with_dtor_left_oob.cc
@@ -20,11 +20,11 @@
// should be "8 bytes ... left of 168-byte region", see
// https://code.google.com/p/address-sanitizer/issues/detail?id=314
// CHECK: [[ADDR]] is located {{.*}} bytes to the left of 172-byte region
-// FIXME: should get rid of the malloc/free frames called from the inside of
-// operator new/delete in DLLs. Also, the operator new frame should have [].
+// FIXME: Should get rid of the malloc/free frames called from the inside of
+// operator new/delete in DLLs when using -MT CRT.
+// FIXME: The operator new frame should have [].
// CHECK-LABEL: allocated by thread T0 here:
-// CHECK-NEXT: malloc
-// CHECK-NEXT: operator new
+// CHECK: operator new
// CHECK-NEXT: test_function {{.*}}dll_operator_array_new_with_dtor_left_oob.cc:[[@LINE-16]]
// CHECK-NEXT: main {{.*}}dll_host.cc
// CHECK-LABEL: SUMMARY
diff --git a/test/asan/TestCases/Windows/dll_seh.cc b/test/asan/TestCases/Windows/dll_seh.cc
new file mode 100644
index 0000000..6e4c724
--- /dev/null
+++ b/test/asan/TestCases/Windows/dll_seh.cc
@@ -0,0 +1,60 @@
+// Clang doesn't support SEH on Windows yet, so for the time being we
+// build this program in two parts: the code with SEH is built with CL,
+// the rest is built with Clang. This represents the typical scenario when we
+// build a large project using "clang-cl -fallback -fsanitize=address".
+//
+// RUN: %clang_cl_asan -O0 %p/dll_host.cc -Fe%t
+//
+// Check both -GS and -GS- builds:
+// RUN: cl -LD -c %s -Fo%t.obj
+// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll %t.obj
+// RUN: %run %t %t.dll
+//
+// RUN: cl -LD -GS- -c %s -Fo%t.obj
+// RUN: %clang_cl_asan -LD -O0 %s -Fe%t.dll %t.obj
+// RUN: %run %t %t.dll
+
+#include <windows.h>
+#include <assert.h>
+#include <stdio.h>
+
+// Should just "#include <sanitizer/asan_interface.h>" when C++ exceptions are
+// supported and we don't need to use CL.
+extern "C" bool __asan_address_is_poisoned(void *p);
+
+void ThrowAndCatch();
+
+#if !defined(__clang__)
+__declspec(noinline)
+void Throw() {
+ int local, zero = 0;
+ fprintf(stderr, "Throw: %p\n", &local);
+ local = 5 / zero;
+}
+
+__declspec(noinline)
+void ThrowAndCatch() {
+ int local;
+ __try {
+ Throw();
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ fprintf(stderr, "__except: %p\n", &local);
+ }
+}
+#else
+
+extern "C" __declspec(dllexport)
+int test_function() {
+ char x[32];
+ fprintf(stderr, "Before: %p poisoned: %d\n", &x,
+ __asan_address_is_poisoned(x + 32));
+ assert(__asan_address_is_poisoned(x + 32));
+ ThrowAndCatch();
+ fprintf(stderr, "After: %p poisoned: %d\n", &x,
+ __asan_address_is_poisoned(x + 32));
+ // FIXME: Invert this assertion once we fix
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=258
+ assert(!__asan_address_is_poisoned(x + 32));
+ return 0;
+}
+#endif
diff --git a/test/asan/TestCases/Windows/double_operator_delete.cc b/test/asan/TestCases/Windows/double_operator_delete.cc
index 55a6d09..eae4a64 100644
--- a/test/asan/TestCases/Windows/double_operator_delete.cc
+++ b/test/asan/TestCases/Windows/double_operator_delete.cc
@@ -8,15 +8,18 @@
delete [] x;
delete [] x;
// CHECK: AddressSanitizer: attempting double-free on [[ADDR:0x[0-9a-f]+]]
-// CHECK-NEXT: {{#0 .* operator delete}}[]
-// CHECK-NEXT: {{#1 .* main .*double_operator_delete.cc}}:[[@LINE-3]]
+// FIXME: The 'operator delete' frame should have [].
+// CHECK-NEXT: {{#0 .* operator delete}}
+// CHECK-NEXT: {{#1 .* main .*double_operator_delete.cc}}:[[@LINE-4]]
// CHECK: [[ADDR]] is located 0 bytes inside of 168-byte region
// CHECK-LABEL: freed by thread T0 here:
-// CHECK-NEXT: {{#0 .* operator delete}}[]
-// CHECK-NEXT: {{#1 .* main .*double_operator_delete.cc}}:[[@LINE-8]]
+// FIXME: The 'operator delete' frame should have [].
+// CHECK-NEXT: {{#0 .* operator delete}}
+// CHECK-NEXT: {{#1 .* main .*double_operator_delete.cc}}:[[@LINE-10]]
// CHECK-LABEL: previously allocated by thread T0 here:
-// CHECK-NEXT: {{#0 .* operator new}}[]
-// CHECK-NEXT: {{#1 .* main .*double_operator_delete.cc}}:[[@LINE-12]]
+// FIXME: The 'operator new' frame should have [].
+// CHECK-NEXT: {{#0 .* operator new}}
+// CHECK-NEXT: {{#1 .* main .*double_operator_delete.cc}}:[[@LINE-15]]
return 0;
}
diff --git a/test/asan/TestCases/Windows/intercept_strdup.cc b/test/asan/TestCases/Windows/intercept_strdup.cc
index 0a40d86..edb1f2f 100644
--- a/test/asan/TestCases/Windows/intercept_strdup.cc
+++ b/test/asan/TestCases/Windows/intercept_strdup.cc
@@ -21,7 +21,7 @@
// CHECK: [[ADDR]] is located 1 bytes to the left of 6-byte region
// CHECK: allocated by thread T0 here:
// CHECK: {{#0 .* malloc }}
-// CHECK: {{#1 .* _strdup }}
+// CHECK: {{#1 .*strdup}}
// CHECK: {{#2 .* main .*}}intercept_strdup.cc:[[@LINE-16]]
free(ptr);
}
diff --git a/test/asan/TestCases/Windows/longjmp.cc b/test/asan/TestCases/Windows/longjmp.cc
new file mode 100644
index 0000000..443933e
--- /dev/null
+++ b/test/asan/TestCases/Windows/longjmp.cc
@@ -0,0 +1,26 @@
+// RUN: %clangxx_asan -O %s -o %t && %run %t
+
+// FIXME: merge this with the common longjmp test when we can run common
+// tests on Windows.
+
+#include <assert.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <sanitizer/asan_interface.h>
+
+static jmp_buf buf;
+
+int main() {
+ char x[32];
+ fprintf(stderr, "\nTestLongJmp\n");
+ fprintf(stderr, "Before: %p poisoned: %d\n", &x,
+ __asan_address_is_poisoned(x + 32));
+ assert(__asan_address_is_poisoned(x + 32));
+ if (0 == setjmp(buf))
+ longjmp(buf, 1);
+ fprintf(stderr, "After: %p poisoned: %d\n", &x,
+ __asan_address_is_poisoned(x + 32));
+ // FIXME: Invert this assertion once we fix
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=258
+ assert(!__asan_address_is_poisoned(x + 32));
+}
diff --git a/test/asan/TestCases/Windows/null_deref.cc b/test/asan/TestCases/Windows/null_deref.cc
new file mode 100644
index 0000000..202000f
--- /dev/null
+++ b/test/asan/TestCases/Windows/null_deref.cc
@@ -0,0 +1,15 @@
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// FIXME: merge this with the common null_deref test when we can run common
+// tests on Windows.
+
+__attribute__((noinline))
+static void NullDeref(int *ptr) {
+ // CHECK: ERROR: AddressSanitizer: access-violation on unknown address
+ // CHECK: {{0x0*000.. .*pc 0x.*}}
+ ptr[10]++; // BOOM
+}
+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/test/asan/TestCases/Windows/null_deref_multiple_dlls.cc b/test/asan/TestCases/Windows/null_deref_multiple_dlls.cc
new file mode 100644
index 0000000..62fe544
--- /dev/null
+++ b/test/asan/TestCases/Windows/null_deref_multiple_dlls.cc
@@ -0,0 +1,40 @@
+// Make sure everything works even if the main module doesn't have any stack
+// variables, thus doesn't explicitly reference any symbol exported by the
+// runtime thunk.
+//
+// RUN: %clang_cl_asan -LD -O0 -DDLL1 %s -Fe%t1.dll
+// RUN: %clang_cl_asan -LD -O0 -DDLL2 %s -Fe%t2.dll
+// RUN: %clang_cl_asan -O0 -DEXE %s %t1.lib %t2.lib -Fe%t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+#include <malloc.h>
+#include <string.h>
+
+extern "C" {
+#if defined(EXE)
+__declspec(dllimport) void foo1();
+__declspec(dllimport) void foo2();
+
+int main() {
+ foo1();
+ foo2();
+}
+#elif defined(DLL1)
+__declspec(dllexport) void foo1() {}
+#elif defined(DLL2)
+__attribute__((noinline))
+static void NullDeref(int *ptr) {
+ // CHECK: ERROR: AddressSanitizer: access-violation on unknown address
+ // CHECK: {{0x0*000.. .*pc 0x.*}}
+ ptr[10]++; // BOOM
+}
+
+__declspec(dllexport) void foo2() {
+ NullDeref((int*)0);
+ // CHECK: {{ #1 0x.* in foo2.*null_deref_multiple_dlls.cc:}}[[@LINE-1]]
+ // CHECK: AddressSanitizer can not provide additional info.
+}
+#else
+# error oops!
+#endif
+}
diff --git a/test/asan/TestCases/Windows/operator_array_new_left_oob.cc b/test/asan/TestCases/Windows/operator_array_new_left_oob.cc
index 81b709f..20a0f19 100644
--- a/test/asan/TestCases/Windows/operator_array_new_left_oob.cc
+++ b/test/asan/TestCases/Windows/operator_array_new_left_oob.cc
@@ -10,7 +10,8 @@
//
// CHECK: [[ADDR]] is located 1 bytes to the left of 42-byte region
// CHECK-LABEL: allocated by thread T0 here:
-// CHECK-NEXT: {{#0 .* operator new}}[]
-// CHECK-NEXT: {{#1 .* main .*operator_array_new_left_oob.cc}}:[[@LINE-9]]
+// FIXME: The 'operator new' frame should have [].
+// CHECK-NEXT: {{#0 .* operator new}}
+// CHECK-NEXT: {{#1 .* main .*operator_array_new_left_oob.cc}}:[[@LINE-10]]
delete [] buffer;
}
diff --git a/test/asan/TestCases/Windows/operator_array_new_right_oob.cc b/test/asan/TestCases/Windows/operator_array_new_right_oob.cc
index 079c78e..23775ef 100644
--- a/test/asan/TestCases/Windows/operator_array_new_right_oob.cc
+++ b/test/asan/TestCases/Windows/operator_array_new_right_oob.cc
@@ -11,7 +11,8 @@
// CHECK: {{#0 .* main .*operator_array_new_right_oob.cc}}:[[@LINE-3]]
// CHECK: [[ADDR]] is located 0 bytes to the right of 42-byte region
// CHECK: allocated by thread T0 here:
-// CHECK: {{#0 .* operator new}}[]
-// CHECK: {{#1 .* main .*operator_array_new_right_oob.cc}}:[[@LINE-8]]
+// FIXME: The 'operator new' frame should have [].
+// CHECK: {{#0 .* operator new}}
+// CHECK: {{#1 .* main .*operator_array_new_right_oob.cc}}:[[@LINE-9]]
delete [] buffer;
}
diff --git a/test/asan/TestCases/Windows/operator_array_new_uaf.cc b/test/asan/TestCases/Windows/operator_array_new_uaf.cc
index 1817996..b638ef1 100644
--- a/test/asan/TestCases/Windows/operator_array_new_uaf.cc
+++ b/test/asan/TestCases/Windows/operator_array_new_uaf.cc
@@ -12,11 +12,13 @@
// CHECK: {{#0 .* main .*operator_array_new_uaf.cc}}:[[@LINE-3]]
// CHECK: [[ADDR]] is located 0 bytes inside of 42-byte region
// CHECK-LABEL: freed by thread T0 here:
-// CHECK: {{#0 .* operator delete}}[]
-// CHECK: {{#1 .* main .*operator_array_new_uaf.cc}}:[[@LINE-8]]
+// FIXME: The 'operator delete' frame should have [].
+// CHECK: {{#0 .* operator delete}}
+// CHECK: {{#1 .* main .*operator_array_new_uaf.cc}}:[[@LINE-9]]
// CHECK-LABEL: previously allocated by thread T0 here:
-// CHECK: {{#0 .* operator new}}[]
-// CHECK: {{#1 .* main .*operator_array_new_uaf.cc}}:[[@LINE-12]]
+// FIXME: The 'operator new' frame should have [].
+// CHECK: {{#0 .* operator new}}
+// CHECK: {{#1 .* main .*operator_array_new_uaf.cc}}:[[@LINE-14]]
return 0;
}
diff --git a/test/asan/TestCases/Windows/operator_array_new_with_dtor_left_oob.cc b/test/asan/TestCases/Windows/operator_array_new_with_dtor_left_oob.cc
index c5bdba5..63f2929 100644
--- a/test/asan/TestCases/Windows/operator_array_new_with_dtor_left_oob.cc
+++ b/test/asan/TestCases/Windows/operator_array_new_with_dtor_left_oob.cc
@@ -18,7 +18,8 @@
// https://code.google.com/p/address-sanitizer/issues/detail?id=314
// CHECK: [[ADDR]] is located {{.*}} bytes to the left of 172-byte region
// CHECK-LABEL: allocated by thread T0 here:
-// CHECK-NEXT: {{#0 .* operator new}}[]
-// CHECK-NEXT: {{#1 .* main .*operator_array_new_with_dtor_left_oob.cc}}:[[@LINE-12]]
+// FIXME: The 'operator new' frame should have [].
+// CHECK-NEXT: {{#0 .* operator new}}
+// CHECK-NEXT: {{#1 .* main .*operator_array_new_with_dtor_left_oob.cc}}:[[@LINE-13]]
delete [] buffer;
}
diff --git a/test/asan/TestCases/Windows/report_after_syminitialize.cc b/test/asan/TestCases/Windows/report_after_syminitialize.cc
new file mode 100644
index 0000000..faf5e35
--- /dev/null
+++ b/test/asan/TestCases/Windows/report_after_syminitialize.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+#include <windows.h>
+#include <dbghelp.h>
+
+int main() {
+ // Make sure the RTL recovers from "no options enabled" dbghelp setup.
+ SymSetOptions(0);
+
+ // Make sure the RTL recovers from "fInvadeProcess=FALSE".
+ if (!SymInitialize(GetCurrentProcess(), 0, FALSE))
+ return 42;
+
+ *(volatile int*)0 = 42;
+ // CHECK: ERROR: AddressSanitizer: access-violation on unknown address
+ // CHECK-NEXT: {{WARNING: .*DbgHelp}}
+ // CHECK: {{#0 0x.* in main.*report_after_syminitialize.cc:}}[[@LINE-3]]
+ // CHECK: AddressSanitizer can not provide additional info.
+}
diff --git a/test/asan/TestCases/Windows/seh.cc b/test/asan/TestCases/Windows/seh.cc
new file mode 100644
index 0000000..50cf6dd
--- /dev/null
+++ b/test/asan/TestCases/Windows/seh.cc
@@ -0,0 +1,56 @@
+// Clang doesn't support SEH on Windows yet, so for the time being we
+// build this program in two parts: the code with SEH is built with CL,
+// the rest is built with Clang. This represents the typical scenario when we
+// build a large project using "clang-cl -fallback -fsanitize=address".
+//
+// Check both -GS and -GS- builds:
+// RUN: cl -c %s -Fo%t.obj
+// RUN: %clangxx_asan -o %t.exe %s %t.obj
+// RUN: %run %t.exe
+//
+// RUN: cl -GS- -c %s -Fo%t.obj
+// RUN: %clangxx_asan -o %t.exe %s %t.obj
+// RUN: %run %t.exe
+
+#include <windows.h>
+#include <assert.h>
+#include <stdio.h>
+
+// Should just "#include <sanitizer/asan_interface.h>" when C++ exceptions are
+// supported and we don't need to use CL.
+extern "C" bool __asan_address_is_poisoned(void *p);
+
+void ThrowAndCatch();
+
+#if !defined(__clang__)
+__declspec(noinline)
+void Throw() {
+ int local, zero = 0;
+ fprintf(stderr, "Throw: %p\n", &local);
+ local = 5 / zero;
+}
+
+__declspec(noinline)
+void ThrowAndCatch() {
+ int local;
+ __try {
+ Throw();
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ fprintf(stderr, "__except: %p\n", &local);
+ }
+}
+#else
+
+int main() {
+ char x[32];
+ fprintf(stderr, "Before: %p poisoned: %d\n", &x,
+ __asan_address_is_poisoned(x + 32));
+ assert(__asan_address_is_poisoned(x + 32));
+ ThrowAndCatch();
+ fprintf(stderr, "After: %p poisoned: %d\n", &x,
+ __asan_address_is_poisoned(x + 32));
+ // FIXME: Invert this assertion once we fix
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=258
+ assert(!__asan_address_is_poisoned(x + 32));
+}
+#endif
diff --git a/test/asan/TestCases/Windows/throw_catch.cc b/test/asan/TestCases/Windows/throw_catch.cc
new file mode 100644
index 0000000..5313d25
--- /dev/null
+++ b/test/asan/TestCases/Windows/throw_catch.cc
@@ -0,0 +1,73 @@
+// Clang doesn't support exceptions on Windows yet, so for the time being we
+// build this program in two parts: the code with exceptions is built with CL,
+// the rest is built with Clang. This represents the typical scenario when we
+// build a large project using "clang-cl -fallback -fsanitize=address".
+//
+// RUN: cl -c %s -Fo%t.obj
+// RUN: %clangxx_asan -o %t.exe %s %t.obj
+// RUN: %run %t.exe
+
+#include <assert.h>
+#include <stdio.h>
+
+// Should just "#include <sanitizer/asan_interface.h>" when C++ exceptions are
+// supported and we don't need to use CL.
+extern "C" bool __asan_address_is_poisoned(void *p);
+
+void ThrowAndCatch();
+void TestThrowInline();
+
+#if !defined(__clang__)
+__declspec(noinline)
+void Throw() {
+ int local;
+ fprintf(stderr, "Throw: %p\n", &local);
+ throw 1;
+}
+
+__declspec(noinline)
+void ThrowAndCatch() {
+ int local;
+ try {
+ Throw();
+ } catch(...) {
+ fprintf(stderr, "Catch: %p\n", &local);
+ }
+}
+
+void TestThrowInline() {
+ char x[32];
+ fprintf(stderr, "Before: %p poisoned: %d\n", &x,
+ __asan_address_is_poisoned(x + 32));
+ try {
+ Throw();
+ } catch(...) {
+ fprintf(stderr, "Catch\n");
+ }
+ fprintf(stderr, "After: %p poisoned: %d\n", &x,
+ __asan_address_is_poisoned(x + 32));
+ // FIXME: Invert this assertion once we fix
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=258
+ assert(!__asan_address_is_poisoned(x + 32));
+}
+
+#else
+
+void TestThrow() {
+ char x[32];
+ fprintf(stderr, "Before: %p poisoned: %d\n", &x,
+ __asan_address_is_poisoned(x + 32));
+ assert(__asan_address_is_poisoned(x + 32));
+ ThrowAndCatch();
+ fprintf(stderr, "After: %p poisoned: %d\n", &x,
+ __asan_address_is_poisoned(x + 32));
+ // FIXME: Invert this assertion once we fix
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=258
+ assert(!__asan_address_is_poisoned(x + 32));
+}
+
+int main(int argc, char **argv) {
+ TestThrowInline();
+ TestThrow();
+}
+#endif
diff --git a/test/asan/TestCases/Windows/use_after_return_linkage.cc b/test/asan/TestCases/Windows/use_after_return_linkage.cc
new file mode 100644
index 0000000..48c5065
--- /dev/null
+++ b/test/asan/TestCases/Windows/use_after_return_linkage.cc
@@ -0,0 +1,12 @@
+// Make sure LIBCMT doesn't accidentally get added to the list of DEFAULTLIB
+// directives. REQUIRES: asan-dynamic-runtime
+// RUN: %clang_cl_asan -LD %s | FileCheck %s
+// CHECK: Creating library
+// CHECK-NOT: LIBCMT
+
+void foo(int *p) { *p = 42; }
+
+__declspec(dllexport) void bar() {
+ int x;
+ foo(&x);
+}
diff --git a/test/asan/TestCases/asan_and_llvm_coverage_test.cc b/test/asan/TestCases/asan_and_llvm_coverage_test.cc
new file mode 100644
index 0000000..35bdfcb
--- /dev/null
+++ b/test/asan/TestCases/asan_and_llvm_coverage_test.cc
@@ -0,0 +1,10 @@
+// RUN: %clangxx_asan -coverage -O0 %s -o %t
+// RUN: env ASAN_OPTIONS=check_initialization_order=1 %run %t 2>&1 | FileCheck %s
+// XFAIL: android
+#include <stdio.h>
+int foo() { return 1; }
+int XXX = foo();
+int main() {
+ printf("PASS\n");
+// CHECK: PASS
+}
diff --git a/test/asan/TestCases/atexit_stats.cc b/test/asan/TestCases/atexit_stats.cc
index b5a5fad..be65344 100644
--- a/test/asan/TestCases/atexit_stats.cc
+++ b/test/asan/TestCases/atexit_stats.cc
@@ -7,7 +7,7 @@
// XFAIL: android
#include <stdlib.h>
-#if !defined(__APPLE__)
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
#include <malloc.h>
#endif
int *p1 = (int*)malloc(900);
diff --git a/test/asan/TestCases/debug_locate.cc b/test/asan/TestCases/debug_locate.cc
new file mode 100644
index 0000000..5971a77
--- /dev/null
+++ b/test/asan/TestCases/debug_locate.cc
@@ -0,0 +1,80 @@
+// Checks the ASan memory address type debugging API, makes sure it returns
+// the correct memory type for heap, stack, global and shadow addresses and
+// that it correctly finds out which region (and name and size) the address
+// belongs to.
+// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1
+
+#include <assert.h>
+#include <sanitizer/asan_interface.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int global_var;
+
+int main() {
+ int local_var;
+ char *heap_ptr = (char *)malloc(10);
+
+ char name[100];
+ void *region_address;
+ size_t region_size;
+ const char *type;
+
+ type = __asan_locate_address(&global_var, name, 100,
+ ®ion_address, ®ion_size);
+ assert(0 == strcmp(name, "global_var"));
+ assert(0 == strcmp(type, "global"));
+ assert(region_address == &global_var);
+ assert(region_size == sizeof(global_var));
+
+ type = __asan_locate_address((char *)(&global_var)+1, name, 100,
+ ®ion_address, ®ion_size);
+ assert(0 == strcmp(name, "global_var"));
+ assert(0 == strcmp(type, "global"));
+ assert(region_address == &global_var);
+ assert(region_size == sizeof(global_var));
+
+ type = __asan_locate_address(&local_var, name, 100,
+ ®ion_address, ®ion_size);
+ assert(0 == strcmp(name, "local_var"));
+ assert(0 == strcmp(type, "stack"));
+ assert(region_address == &local_var);
+ assert(region_size == sizeof(local_var));
+
+ type = __asan_locate_address((char *)(&local_var)+1, name, 100,
+ ®ion_address, ®ion_size);
+ assert(0 == strcmp(name, "local_var"));
+ assert(0 == strcmp(type, "stack"));
+ assert(region_address == &local_var);
+ assert(region_size == sizeof(local_var));
+
+ type = __asan_locate_address(heap_ptr, name, 100,
+ ®ion_address, ®ion_size);
+ assert(0 == strcmp(type, "heap"));
+ assert(region_address == heap_ptr);
+ assert(10 == region_size);
+
+ type = __asan_locate_address(heap_ptr+1, name, 100,
+ ®ion_address, ®ion_size);
+ assert(0 == strcmp(type, "heap"));
+ assert(region_address == heap_ptr);
+ assert(10 == region_size);
+
+ size_t shadow_scale;
+ size_t shadow_offset;
+ __asan_get_shadow_mapping(&shadow_scale, &shadow_offset);
+
+ uintptr_t shadow_ptr = (((uintptr_t)heap_ptr) >> shadow_scale)
+ + shadow_offset;
+ type = __asan_locate_address((void *)shadow_ptr, NULL, 0, NULL, NULL);
+ assert((0 == strcmp(type, "high shadow")) || 0 == strcmp(type, "low shadow"));
+
+ uintptr_t shadow_gap = (shadow_ptr >> shadow_scale) + shadow_offset;
+ type = __asan_locate_address((void *)shadow_gap, NULL, 0, NULL, NULL);
+ assert(0 == strcmp(type, "shadow gap"));
+
+ free(heap_ptr);
+
+ return 0;
+}
diff --git a/test/asan/TestCases/debug_mapping.cc b/test/asan/TestCases/debug_mapping.cc
new file mode 100644
index 0000000..f96abf6
--- /dev/null
+++ b/test/asan/TestCases/debug_mapping.cc
@@ -0,0 +1,24 @@
+// Checks that the debugging API returns correct shadow scale and offset.
+// RUN: %clangxx_asan -O %s -o %t
+// RUN: env ASAN_OPTIONS=verbosity=1 %run %t 2>&1 | FileCheck %s
+
+#include <sanitizer/asan_interface.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// printed because of verbosity=1
+// CHECK: SHADOW_SCALE: [[SCALE:[0-9]+]]
+// CHECK: SHADOW_OFFSET: [[OFFSET:[0-9]+]]
+
+int main() {
+ size_t scale, offset;
+ __asan_get_shadow_mapping(&scale, &offset);
+
+ fprintf(stderr, "scale: %lx\n", scale);
+ fprintf(stderr, "offset: %lx\n", offset);
+
+ // CHECK: scale: [[SCALE]]
+ // CHECK: offset: [[OFFSET]]
+
+ return 0;
+}
diff --git a/test/asan/TestCases/debug_ppc64_mapping.cc b/test/asan/TestCases/debug_ppc64_mapping.cc
new file mode 100644
index 0000000..3ddd3e1
--- /dev/null
+++ b/test/asan/TestCases/debug_ppc64_mapping.cc
@@ -0,0 +1,37 @@
+// RUN: %clang_asan -O0 %s -o %t
+// RUN: env ASAN_OPTIONS=verbosity=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-PPC64-V0
+// RUN: env ASAN_OPTIONS=verbosity=2 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-PPC64
+// REQUIRES: powerpc64-supported-target
+
+#include <stdio.h>
+
+int main() {
+// CHECK-PPC64: || `[{{0x0a0|0x040}}000000000, {{0x3ff|0x0ff}}fffffffff]` || HighMem ||
+// CHECK-PPC64: || `[{{0x034|0x028}}000000000, {{0x09f|0x03f}}fffffffff]` || HighShadow ||
+// CHECK-PPC64: || `[{{0x024|0x024}}000000000, {{0x033|0x027}}fffffffff]` || ShadowGap ||
+// CHECK-PPC64: || `[0x020000000000, 0x023fffffffff]` || LowShadow ||
+// CHECK-PPC64: || `[0x000000000000, 0x01ffffffffff]` || LowMem ||
+//
+ printf("ppc64 eyecatcher \n");
+// CHECK-PPC64-V0: ppc64 eyecatcher
+
+ return 0;
+}
+
+/*
+ * Two different signatures noted at the time of writing.
+Newish kernel: (64TB address range support, starting with kernel version 3.7)
+|| `[0x0a0000000000, 0x3fffffffffff]` || HighMem ||
+|| `[0x034000000000, 0x09ffffffffff]` || HighShadow ||
+|| `[0x024000000000, 0x033fffffffff]` || ShadowGap ||
+|| `[0x020000000000, 0x023fffffffff]` || LowShadow ||
+|| `[0x000000000000, 0x01ffffffffff]` || LowMem ||
+
+Oldish kernel:
+|| `[0x040000000000, 0x0fffffffffff]` || HighMem ||
+|| `[0x028000000000, 0x03ffffffffff]` || HighShadow ||
+|| `[0x024000000000, 0x027fffffffff]` || ShadowGap ||
+|| `[0x020000000000, 0x023fffffffff]` || LowShadow ||
+|| `[0x000000000000, 0x01ffffffffff]` || LowMem ||
+*/
+
diff --git a/test/asan/TestCases/debug_report.cc b/test/asan/TestCases/debug_report.cc
new file mode 100644
index 0000000..acf52f9
--- /dev/null
+++ b/test/asan/TestCases/debug_report.cc
@@ -0,0 +1,48 @@
+// Checks that the ASan debugging API for getting report information
+// returns correct values.
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+#include <sanitizer/asan_interface.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main() {
+ char *heap_ptr = (char *)malloc(10);
+ free(heap_ptr);
+ int present = __asan_report_present();
+ fprintf(stderr, "%s\n", (present == 0) ? "no report" : "");
+ // CHECK: no report
+ heap_ptr[0] = 'A'; // BOOM
+ return 0;
+}
+
+void __asan_on_error() {
+ int present = __asan_report_present();
+ void *pc = __asan_get_report_pc();
+ void *bp = __asan_get_report_bp();
+ void *sp = __asan_get_report_sp();
+ void *addr = __asan_get_report_address();
+ int is_write = __asan_get_report_access_type();
+ size_t access_size = __asan_get_report_access_size();
+ const char *description = __asan_get_report_description();
+
+ fprintf(stderr, "%s\n", (present == 1) ? "report" : "");
+ // CHECK: report
+ fprintf(stderr, "pc: %p\n", pc);
+ // CHECK: pc: 0x[[PC:[0-9a-f]+]]
+ fprintf(stderr, "bp: %p\n", bp);
+ // CHECK: bp: 0x[[BP:[0-9a-f]+]]
+ fprintf(stderr, "sp: %p\n", sp);
+ // CHECK: sp: 0x[[SP:[0-9a-f]+]]
+ fprintf(stderr, "addr: %p\n", addr);
+ // CHECK: addr: 0x[[ADDR:[0-9a-f]+]]
+ fprintf(stderr, "type: %s\n", (is_write ? "write" : "read"));
+ // CHECK: type: write
+ fprintf(stderr, "access_size: %ld\n", access_size);
+ // CHECK: access_size: 1
+ fprintf(stderr, "description: %s\n", description);
+ // CHECK: description: heap-use-after-free
+}
+
+// CHECK: AddressSanitizer: heap-use-after-free on address {{0x0*}}[[ADDR]] at pc {{0x0*}}[[PC]] bp {{0x0*}}[[BP]] sp {{0x0*}}[[SP]]
+// CHECK: WRITE of size 1 at {{0x0*}}[[ADDR]] thread T0
diff --git a/test/asan/TestCases/debug_stacks.cc b/test/asan/TestCases/debug_stacks.cc
new file mode 100644
index 0000000..57bb546
--- /dev/null
+++ b/test/asan/TestCases/debug_stacks.cc
@@ -0,0 +1,62 @@
+// Check that the stack trace debugging API works and returns correct
+// malloc and free stacks.
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+#include <sanitizer/asan_interface.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+char *mem;
+void func1() {
+ mem = (char *)malloc(10);
+}
+
+void func2() {
+ free(mem);
+}
+
+int main() {
+ func1();
+ func2();
+
+ void *trace[100];
+ size_t num_frames = 100;
+ int thread_id;
+ num_frames = __asan_get_alloc_stack(mem, trace, num_frames, &thread_id);
+
+ fprintf(stderr, "alloc stack retval %s\n", (num_frames > 0 && num_frames < 10)
+ ? "ok" : "");
+ // CHECK: alloc stack retval ok
+ fprintf(stderr, "thread id = %d\n", thread_id);
+ // CHECK: thread id = 0
+ fprintf(stderr, "0x%lx\n", trace[0]);
+ // CHECK: [[ALLOC_FRAME_0:0x[0-9a-f]+]]
+ fprintf(stderr, "0x%lx\n", trace[1]);
+ // CHECK: [[ALLOC_FRAME_1:0x[0-9a-f]+]]
+
+ num_frames = 100;
+ num_frames = __asan_get_free_stack(mem, trace, num_frames, &thread_id);
+
+ fprintf(stderr, "free stack retval %s\n", (num_frames > 0 && num_frames < 10)
+ ? "ok" : "");
+ // CHECK: free stack retval ok
+ fprintf(stderr, "thread id = %d\n", thread_id);
+ // CHECK: thread id = 0
+ fprintf(stderr, "0x%lx\n", trace[0]);
+ // CHECK: [[FREE_FRAME_0:0x[0-9a-f]+]]
+ fprintf(stderr, "0x%lx\n", trace[1]);
+ // CHECK: [[FREE_FRAME_1:0x[0-9a-f]+]]
+
+ mem[0] = 'A'; // BOOM
+
+ // CHECK: ERROR: AddressSanitizer: heap-use-after-free
+ // CHECK: WRITE of size 1 at 0x{{.*}}
+ // CHECK: freed by thread T0 here:
+ // CHECK: #0 [[FREE_FRAME_0]]
+ // CHECK: #1 [[FREE_FRAME_1]]
+ // CHECK: previously allocated by thread T0 here:
+ // CHECK: #0 [[ALLOC_FRAME_0]]
+ // CHECK: #1 [[ALLOC_FRAME_1]]
+
+ return 0;
+}
diff --git a/test/asan/TestCases/deep_call_stack.cc b/test/asan/TestCases/deep_call_stack.cc
index c306775..789f234 100644
--- a/test/asan/TestCases/deep_call_stack.cc
+++ b/test/asan/TestCases/deep_call_stack.cc
@@ -1,7 +1,7 @@
// 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: %run %t 2>&1 | FileCheck %s
+// RUN: (ulimit -s 4096; %run %t) 2>&1 | FileCheck %s
// Also check that use_sigaltstack+verbosity doesn't crash.
// RUN: env ASAN_OPTIONS=verbosity=1:use_sigaltstack=1 %run %t | FileCheck %s
#include <stdio.h>
@@ -17,9 +17,9 @@
}
int main(int argc, char **argv) {
- RecursiveFunc(40000, 0);
+ RecursiveFunc(15000, 0);
return 0;
}
-// CHECK: [40000] ptr:
-// CHECK: [20000] ptr:
-// CHECK: [00000] ptr
+// CHECK: [15000] ptr:
+// CHECK: [07000] ptr:
+// CHECK: [00000] ptr:
diff --git a/test/asan/TestCases/deep_stack_uaf.cc b/test/asan/TestCases/deep_stack_uaf.cc
index accb70c..3e88d69 100644
--- a/test/asan/TestCases/deep_stack_uaf.cc
+++ b/test/asan/TestCases/deep_stack_uaf.cc
@@ -3,6 +3,7 @@
// RUN: %clangxx_asan -O0 %s -o %t 2>&1
// RUN: env ASAN_OPTIONS=malloc_context_size=120:redzone=512 not %run %t 2>&1 | FileCheck %s
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <stdlib.h>
#include <stdio.h>
@@ -26,7 +27,10 @@
DeepFree<200>::free(x);
return x[5];
// CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
- // CHECK: DeepFree<36>
- // CHECK: DeepFree<98>
- // CHECK: DeepFree<115>
+ // The libcxxrt demangling procedure on FreeBSD 9.2 incorrectly appends
+ // extra 'E' characters to the end of template arguments; see:
+ // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=192115
+ // CHECK: {{DeepFree<36>|DeepFree<36E>}}
+ // CHECK: {{DeepFree<98>|DeepFree<98E>}}
+ // CHECK: {{DeepFree<115>|DeepFree<115E>}}
}
diff --git a/test/asan/TestCases/describe_address.cc b/test/asan/TestCases/describe_address.cc
new file mode 100644
index 0000000..868c0eb
--- /dev/null
+++ b/test/asan/TestCases/describe_address.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <sanitizer/asan_interface.h>
+
+int global;
+
+int main(int argc, char *argv[]) {
+ int stack;
+ int *heap = new int[100];
+ __asan_describe_address(heap);
+ // CHECK: {{.*}} is located 0 bytes inside of 400-byte region
+ // CHECK: allocated by thread T{{.*}} here
+ __asan_describe_address(&stack);
+ // CHECK: Address {{.*}} is located in stack of thread T{{.*}} at offset {{.*}}
+ __asan_describe_address(&global);
+ // CHECK: {{.*}} is located 0 bytes inside of global variable 'global'
+ delete[] heap;
+ return 0;
+}
diff --git a/test/asan/TestCases/dlclose-test.cc b/test/asan/TestCases/dlclose-test.cc
index 07d57b1..094453f 100644
--- a/test/asan/TestCases/dlclose-test.cc
+++ b/test/asan/TestCases/dlclose-test.cc
@@ -15,13 +15,13 @@
// REQUIRES: x86_64-supported-target,i386-supported-target
// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
-// RUN: %clangxx_asan -O0 %s -ldl -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -O1 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
-// RUN: %clangxx_asan -O1 %s -ldl -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -O2 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
-// RUN: %clangxx_asan -O2 %s -ldl -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O2 %s -o %t && %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -O3 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
-// RUN: %clangxx_asan -O3 %s -ldl -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O3 %s -o %t && %run %t 2>&1 | FileCheck %s
#if !defined(SHARED_LIB)
#include <assert.h>
@@ -66,7 +66,7 @@
size_t page_beg = ((size_t)addr) & ~(PageSize - 1);
void *res = mmap((void*)(page_beg), PageSize,
PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, 0, 0);
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, -1, 0);
if (res == (char*)-1L) {
printf("failed to mmap\n");
return 1;
diff --git a/test/asan/TestCases/double-free.cc b/test/asan/TestCases/double-free.cc
index 212d7ea..f0dd291 100644
--- a/test/asan/TestCases/double-free.cc
+++ b/test/asan/TestCases/double-free.cc
@@ -5,6 +5,7 @@
// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s
// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <stdlib.h>
#include <string.h>
diff --git a/test/asan/TestCases/dump_instruction_bytes.cc b/test/asan/TestCases/dump_instruction_bytes.cc
new file mode 100644
index 0000000..981e3c3
--- /dev/null
+++ b/test/asan/TestCases/dump_instruction_bytes.cc
@@ -0,0 +1,20 @@
+// Check that ASan prints the faulting instruction bytes on
+// dump_instruction_bytes=1
+// RUN: %clangxx_asan %s -o %t
+// RUN: env ASAN_OPTIONS=dump_instruction_bytes=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-DUMP
+// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NODUMP
+//
+// REQUIRES: x86_64-supported-target,i386-supported-target
+
+int main() {
+#if defined(__x86_64__)
+ asm("movq $0, %rax");
+ asm("movl $0xcafebabe, 0x0(%rax)");
+#elif defined(i386)
+ asm("movl $0, %eax");
+ asm("movl $0xcafebabe, 0x0(%eax)");
+#endif
+ // CHECK-DUMP: First 16 instruction bytes at pc: c7 00 be ba fe ca
+ // CHECK-NODUMP-NOT: First 16 instruction bytes
+ return 0;
+}
diff --git a/test/asan/TestCases/gc-test.cc b/test/asan/TestCases/gc-test.cc
index ffbea85..08efd4f 100644
--- a/test/asan/TestCases/gc-test.cc
+++ b/test/asan/TestCases/gc-test.cc
@@ -2,6 +2,7 @@
// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1
// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK0
// REQUIRES: stable-runtime
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <assert.h>
#include <stdio.h>
diff --git a/test/asan/TestCases/global-demangle.cc b/test/asan/TestCases/global-demangle.cc
index 2bfa0d1..5f7ff91 100644
--- a/test/asan/TestCases/global-demangle.cc
+++ b/test/asan/TestCases/global-demangle.cc
@@ -1,6 +1,3 @@
-// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=264
-// XFAIL: android
-//
// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
namespace XXX {
diff --git a/test/asan/TestCases/global-location.cc b/test/asan/TestCases/global-location.cc
index 54f2055..795e50b 100644
--- a/test/asan/TestCases/global-location.cc
+++ b/test/asan/TestCases/global-location.cc
@@ -1,6 +1,3 @@
-// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=264
-// XFAIL: android
-
// RUN: %clangxx_asan -O2 %s -o %t
// RUN: not %run %t g 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=GLOB
// RUN: not %run %t c 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CLASS_STATIC
@@ -27,7 +24,7 @@
case 'c': return C::array[one * 11];
case 'f':
static int array[10];
- // FUNC_STATIC: 0x{{.*}} is located 4 bytes to the right of global variable 'main::array' defined in '{{.*}}global-location.cc:[[@LINE-1]]:16' {{.*}} of size 40
+ // FUNC_STATIC: 0x{{.*}} is located 4 bytes to the right of global variable 'array' defined in '{{.*}}global-location.cc:[[@LINE-1]]:16' {{.*}} of size 40
memset(array, 0, 10);
return array[one * 11];
case 'l':
diff --git a/test/asan/TestCases/heap-overflow-large.cc b/test/asan/TestCases/heap-overflow-large.cc
new file mode 100644
index 0000000..eb2fcc3
--- /dev/null
+++ b/test/asan/TestCases/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 %run %t 12 2>&1 | FileCheck %s
+// RUN: not %run %t 100 2>&1 | FileCheck %s
+// RUN: not %run %t 10000 2>&1 | FileCheck %s
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+ fprintf(stderr, "main\n");
+ int *x = new int[5];
+ memset(x, 0, sizeof(x[0]) * 5);
+ int index = atoi(argv[1]);
+ int res = x[index];
+ // CHECK: main
+ // CHECK-NOT: CHECK failed
+ delete[] x;
+ return res ? res : 1;
+}
diff --git a/test/asan/TestCases/Linux/heavy_uar_test.cc b/test/asan/TestCases/heavy_uar_test.cc
similarity index 97%
rename from test/asan/TestCases/Linux/heavy_uar_test.cc
rename to test/asan/TestCases/heavy_uar_test.cc
index bfea520..9068da2 100644
--- a/test/asan/TestCases/Linux/heavy_uar_test.cc
+++ b/test/asan/TestCases/heavy_uar_test.cc
@@ -7,6 +7,7 @@
// FIXME: Fix this test under GCC.
// REQUIRES: Clang
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <stdio.h>
#include <string.h>
diff --git a/test/asan/TestCases/Linux/interception_failure_test.cc b/test/asan/TestCases/interception_failure_test.cc
similarity index 100%
rename from test/asan/TestCases/Linux/interception_failure_test.cc
rename to test/asan/TestCases/interception_failure_test.cc
diff --git a/test/asan/TestCases/intra-object-overflow.cc b/test/asan/TestCases/intra-object-overflow.cc
new file mode 100644
index 0000000..e48a261
--- /dev/null
+++ b/test/asan/TestCases/intra-object-overflow.cc
@@ -0,0 +1,31 @@
+// RUN: %clangxx_asan -O0 -fsanitize-address-field-padding=1 %s -o %t
+// RUN: not %run %t 11 2>&1 | FileCheck %s
+// RUN: %run %t 10
+//
+// FIXME: fix 32-bits.
+// REQUIRES: asan-64-bits
+#include <stdio.h>
+#include <stdlib.h>
+class Foo {
+ public:
+ Foo() : pre1(1), pre2(2), post1(3), post2(4) {
+ }
+ virtual ~Foo() {
+ }
+ void set(int i, int val) { a[i] = val; }
+// CHECK: ERROR: AddressSanitizer: intra-object-overflow
+// CHECK: #0 {{.*}}Foo::set{{.*}}intra-object-overflow.cc:[[@LINE-2]]
+ private:
+ int pre1, pre2;
+ int a[11];
+ int post1, post2;
+};
+
+int main(int argc, char **argv) {
+ int idx = argc == 2 ? atoi(argv[1]) : 0;
+ Foo *foo = new Foo;
+ foo->set(idx, 42);
+// CHECK: #1 {{.*}}main{{.*}}intra-object-overflow.cc:[[@LINE-1]]
+// CHECK: is located 84 bytes inside of 128-byte region
+ delete foo;
+}
diff --git a/test/asan/TestCases/invalid-free.cc b/test/asan/TestCases/invalid-free.cc
index 34018fb..cb545cc 100644
--- a/test/asan/TestCases/invalid-free.cc
+++ b/test/asan/TestCases/invalid-free.cc
@@ -5,6 +5,7 @@
// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s
// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <stdlib.h>
#include <string.h>
diff --git a/test/asan/TestCases/large_func_test.cc b/test/asan/TestCases/large_func_test.cc
index 0d651f6..6b592f8 100644
--- a/test/asan/TestCases/large_func_test.cc
+++ b/test/asan/TestCases/large_func_test.cc
@@ -3,6 +3,7 @@
// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <stdlib.h>
__attribute__((noinline))
diff --git a/test/asan/TestCases/longjmp.cc b/test/asan/TestCases/longjmp.cc
new file mode 100644
index 0000000..5472330
--- /dev/null
+++ b/test/asan/TestCases/longjmp.cc
@@ -0,0 +1,23 @@
+// RUN: %clangxx_asan -O %s -o %t && %run %t
+
+#include <assert.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <sanitizer/asan_interface.h>
+
+static jmp_buf buf;
+
+int main() {
+ char x[32];
+ fprintf(stderr, "\nTestLongJmp\n");
+ fprintf(stderr, "Before: %p poisoned: %d\n", &x,
+ __asan_address_is_poisoned(x + 32));
+ assert(__asan_address_is_poisoned(x + 32));
+ if (0 == setjmp(buf))
+ longjmp(buf, 1);
+ fprintf(stderr, "After: %p poisoned: %d\n", &x,
+ __asan_address_is_poisoned(x + 32));
+ // FIXME: Invert this assertion once we fix
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=258
+ assert(!__asan_address_is_poisoned(x + 32));
+}
diff --git a/test/asan/TestCases/lsan_annotations.cc b/test/asan/TestCases/lsan_annotations.cc
index 84c2878..f52b0ff 100644
--- a/test/asan/TestCases/lsan_annotations.cc
+++ b/test/asan/TestCases/lsan_annotations.cc
@@ -1,6 +1,3 @@
-// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316
-// XFAIL: android
-//
// Check that LSan annotations work fine.
// RUN: %clangxx_asan -O0 %s -o %t && %run %t
// RUN: %clangxx_asan -O3 %s -o %t && %run %t
diff --git a/test/asan/TestCases/malloc_context_size.cc b/test/asan/TestCases/malloc_context_size.cc
index fb158c6..0d9f315 100644
--- a/test/asan/TestCases/malloc_context_size.cc
+++ b/test/asan/TestCases/malloc_context_size.cc
@@ -1,31 +1,21 @@
// RUN: %clangxx_asan -O0 %s -o %t
-// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os
-// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os
-// RUN: env ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os
-// RUN: env ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os
+// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s
+// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s
+// RUN: env ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s
+// RUN: env ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s
// RUN: env ASAN_OPTIONS=malloc_context_size=2 not %run %t 2>&1 | FileCheck %s --check-prefix=TWO
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-Windows: freed by thread T{{.*}} here:
- // CHECK-Windows-NEXT: #0 0x{{.*}} in operator delete[]
+ // CHECK: freed by thread T{{.*}} here:
+ // CHECK-NEXT: #0 0x{{.*}} in {{operator delete( )?\[\]|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-Windows: previously allocated by thread T{{.*}} here:
- // CHECK-Windows-NEXT: #0 0x{{.*}} in operator new[]
+ // CHECK: previously allocated by thread T{{.*}} here:
+ // CHECK-NEXT: #0 0x{{.*}} in {{operator new( )?\[\]|wrap__Znam}}
// CHECK-NOT: #1 0x{{.*}}
// CHECK: SUMMARY: AddressSanitizer: heap-use-after-free
diff --git a/test/asan/TestCases/mmap_limit_mb.cc b/test/asan/TestCases/mmap_limit_mb.cc
index 1d697ef..d4ffb2e 100644
--- a/test/asan/TestCases/mmap_limit_mb.cc
+++ b/test/asan/TestCases/mmap_limit_mb.cc
@@ -1,12 +1,12 @@
// Test the mmap_limit_mb flag.
//
// RUN: %clangxx_asan -O2 %s -o %t
-// RUN: %run %t 100 16
-// RUN: %run %t 100 1000000
-// RUN: env ASAN_OPTIONS=mmap_limit_mb=500 %run %t 50 16
-// RUN: env ASAN_OPTIONS=mmap_limit_mb=500 %run %t 50 1000000
-// RUN: env ASAN_OPTIONS=mmap_limit_mb=500 not %run %t 500 16 2>&1 | FileCheck %s
-// RUN: env ASAN_OPTIONS=mmap_limit_mb=500 not %run %t 500 1000000 2>&1 | FileCheck %s
+// RUN: %run %t 20 16
+// RUN: %run %t 30 1000000
+// RUN: env ASAN_OPTIONS=mmap_limit_mb=300 %run %t 20 16
+// RUN: env ASAN_OPTIONS=mmap_limit_mb=300 %run %t 20 1000000
+// RUN: env ASAN_OPTIONS=mmap_limit_mb=300 not %run %t 500 16 2>&1 | FileCheck %s
+// RUN: env ASAN_OPTIONS=mmap_limit_mb=300 not %run %t 500 1000000 2>&1 | FileCheck %s
// XFAIL: arm-linux-gnueabi
#include <assert.h>
diff --git a/test/asan/TestCases/null_deref.cc b/test/asan/TestCases/null_deref.cc
index e80657f..875d65f 100644
--- a/test/asan/TestCases/null_deref.cc
+++ b/test/asan/TestCases/null_deref.cc
@@ -1,19 +1,19 @@
-// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
-// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
-// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
-// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s
__attribute__((noinline))
static void NullDeref(int *ptr) {
// CHECK: ERROR: AddressSanitizer: SEGV on unknown address
// CHECK: {{0x0*000.. .*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]]
+ // atos on Mac cannot extract the symbol name correctly. Also, on FreeBSD 9.2
+ // the demangling function rejects local names with 'L' in front of them.
+ // CHECK: {{ #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.}}
+ // CHECK: AddressSanitizer can not provide additional info.
}
diff --git a/test/asan/TestCases/sanity_check_pure_c.c b/test/asan/TestCases/sanity_check_pure_c.c
index 01d87e7..c3a43c8 100644
--- a/test/asan/TestCases/sanity_check_pure_c.c
+++ b/test/asan/TestCases/sanity_check_pure_c.c
@@ -6,6 +6,7 @@
// RUN: %clang_asan -O2 %s -pie -fPIE -o %t
// RUN: not %run %t 2>&1 | FileCheck %s
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <stdlib.h>
int main() {
diff --git a/test/asan/TestCases/stack-overflow.cc b/test/asan/TestCases/stack-overflow.cc
index 234e3c7..9d7c72c 100644
--- a/test/asan/TestCases/stack-overflow.cc
+++ b/test/asan/TestCases/stack-overflow.cc
@@ -74,7 +74,7 @@
if (y)
recursive_func(buf);
x = 1; // prevent tail call optimization
- // CHECK: {{stack-overflow on address 0x.* \(pc 0x.* sp 0x.* bp 0x.* T.*\)}}
+ // CHECK: {{stack-overflow on address 0x.* \(pc 0x.* bp 0x.* sp 0x.* T.*\)}}
// If stack overflow happens during function prologue, stack trace may be
// corrupted. Unwind tables are not always 100% exact there.
// For this reason, we don't do any further checks.
diff --git a/test/asan/TestCases/strncpy-overflow.cc b/test/asan/TestCases/strncpy-overflow.cc
index 8001047..651ae22 100644
--- a/test/asan/TestCases/strncpy-overflow.cc
+++ b/test/asan/TestCases/strncpy-overflow.cc
@@ -5,6 +5,7 @@
// REQUIRES: compiler-rt-optimized
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <string.h>
#include <stdlib.h>
diff --git a/test/asan/TestCases/throw_catch.cc b/test/asan/TestCases/throw_catch.cc
index f35378d..7e0d76d 100644
--- a/test/asan/TestCases/throw_catch.cc
+++ b/test/asan/TestCases/throw_catch.cc
@@ -4,10 +4,7 @@
// XFAIL: win32
#include <assert.h>
-#include <setjmp.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <string.h>
#include <sanitizer/asan_interface.h>
__attribute__((noinline))
@@ -31,6 +28,7 @@
char x[32];
fprintf(stderr, "Before: %p poisoned: %d\n", &x,
__asan_address_is_poisoned(x + 32));
+ assert(__asan_address_is_poisoned(x + 32));
ThrowAndCatch();
fprintf(stderr, "After: %p poisoned: %d\n", &x,
__asan_address_is_poisoned(x + 32));
@@ -43,6 +41,7 @@
char x[32];
fprintf(stderr, "Before: %p poisoned: %d\n", &x,
__asan_address_is_poisoned(x + 32));
+ assert(__asan_address_is_poisoned(x + 32));
try {
Throw();
} catch(...) {
@@ -55,24 +54,7 @@
assert(!__asan_address_is_poisoned(x + 32));
}
-static jmp_buf buf;
-
-void TestLongJmp() {
- char x[32];
- fprintf(stderr, "\nTestLongJmp\n");
- fprintf(stderr, "Before: %p poisoned: %d\n", &x,
- __asan_address_is_poisoned(x + 32));
- if (0 == setjmp(buf))
- longjmp(buf, 1);
- fprintf(stderr, "After: %p poisoned: %d\n", &x,
- __asan_address_is_poisoned(x + 32));
- // FIXME: Invert this assertion once we fix
- // https://code.google.com/p/address-sanitizer/issues/detail?id=258
- assert(!__asan_address_is_poisoned(x + 32));
-}
-
int main(int argc, char **argv) {
- TestThrow();
TestThrowInline();
- TestLongJmp();
+ TestThrow();
}
diff --git a/test/asan/TestCases/use-after-delete.cc b/test/asan/TestCases/use-after-delete.cc
index f22e9e5..8fdec8d 100644
--- a/test/asan/TestCases/use-after-delete.cc
+++ b/test/asan/TestCases/use-after-delete.cc
@@ -3,6 +3,7 @@
// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <stdlib.h>
int main() {
diff --git a/test/asan/TestCases/use-after-free-right.cc b/test/asan/TestCases/use-after-free-right.cc
index 68ac158..f714b44 100644
--- a/test/asan/TestCases/use-after-free-right.cc
+++ b/test/asan/TestCases/use-after-free-right.cc
@@ -3,6 +3,7 @@
// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
// Test use-after-free report in the case when access is at the right border of
// the allocation.
diff --git a/test/asan/TestCases/use-after-free.cc b/test/asan/TestCases/use-after-free.cc
index 0cd87ee..7bc225b 100644
--- a/test/asan/TestCases/use-after-free.cc
+++ b/test/asan/TestCases/use-after-free.cc
@@ -3,6 +3,7 @@
// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
// XFAIL: arm-linux-gnueabi
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <stdlib.h>
int main() {
diff --git a/test/asan/TestCases/zero_page_pc.cc b/test/asan/TestCases/zero_page_pc.cc
new file mode 100644
index 0000000..5810a9f
--- /dev/null
+++ b/test/asan/TestCases/zero_page_pc.cc
@@ -0,0 +1,12 @@
+// Check that ASan correctly detects SEGV on the zero page.
+// RUN: %clangxx_asan %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+typedef void void_f();
+int main() {
+ void_f *func = (void_f *)0x4;
+ func();
+ // x86 reports the SEGV with both address=4 and pc=4.
+ // PowerPC64 reports it with address=4 but pc still in main().
+ // CHECK: {{AddressSanitizer: SEGV.*(address|pc) 0x0*4}}
+ return 0;
+}
diff --git a/test/asan/lit.cfg b/test/asan/lit.cfg
index db2459f..2acc542 100644
--- a/test/asan/lit.cfg
+++ b/test/asan/lit.cfg
@@ -26,18 +26,23 @@
# GCC-ASan doesn't link in all the necessary libraries automatically, so
# we have to do it ourselves.
if config.compiler_id == 'GNU':
- extra_linkflags = ["-pthread", "-lstdc++", "-ldl"]
+ extra_linkflags = ["-pthread", "-lstdc++"]
else:
extra_linkflags = []
+
+# There is no libdl on FreeBSD.
+if config.compiler_id == 'GNU' and config.host_os != 'FreeBSD':
+ extra_linkflags += ["-ldl"]
+
# Setup default compiler flags used with -fsanitize=address option.
# FIXME: Review the set of required flags and check if it can be reduced.
target_cflags = [get_required_attr(config, "target_cflags")] + extra_linkflags
target_cxxflags = config.cxx_mode_flags + target_cflags
-clang_asan_static_cflags = ["-fsanitize=address",
+clang_asan_static_cflags = (["-fsanitize=address",
"-mno-omit-leaf-frame-pointer",
"-fno-omit-frame-pointer",
- "-fno-optimize-sibling-calls",
- "-g"] + target_cflags
+ "-fno-optimize-sibling-calls"] +
+ config.debug_info_flags + target_cflags)
clang_asan_static_cxxflags = config.cxx_mode_flags + clang_asan_static_cflags
if config.asan_dynamic:
@@ -50,7 +55,7 @@
config.available_features.add("asan-static-runtime")
asan_lit_source_dir = get_required_attr(config, "asan_lit_source_dir")
-if config.android == "TRUE":
+if config.android == "1":
config.available_features.add('android')
clang_wrapper = os.path.join(asan_lit_source_dir,
"android_commands", "android_compile.py") + " "
@@ -76,9 +81,13 @@
"-WX",
"-D_HAS_EXCEPTIONS=0",
"-Zi"] + target_cflags
+ if config.asan_dynamic:
+ clang_cl_asan_cxxflags.append("-MD")
clang_invocation = build_invocation(clang_cl_asan_cxxflags)
clang_cl_invocation = clang_invocation.replace("clang.exe","clang-cl.exe")
config.substitutions.append( ("%clang_cl_asan ", clang_cl_invocation) )
+ config.substitutions.append( ("%asan_dll_thunk",
+ os.path.join(config.compiler_rt_libdir, "clang_rt.asan_dll_thunk-i386.lib")))
# FIXME: De-hardcode this path.
asan_source_dir = os.path.join(
@@ -99,13 +108,11 @@
config.substitutions.append( ("%sancov", python_exec + " " + sancov + " ") )
# Determine kernel bitness
-if config.host_arch.find('64') != -1 and config.android != "TRUE":
+if config.host_arch.find('64') != -1 and config.android != "1":
kernel_bits = '64'
else:
kernel_bits = '32'
-# Define CHECK-%os to check for OS-dependent output.
-config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os)))
config.substitutions.append( ('CHECK-%kernel_bits', ("CHECK-kernel-" + kernel_bits + "-bits")))
config.available_features.add("asan-" + config.bits + "-bits")
@@ -134,6 +141,7 @@
if config.host_os == 'Darwin':
config.suffixes.append('.mm')
-# AddressSanitizer tests are currently supported on Linux and Darwin only.
-if config.host_os not in ['Linux', 'Darwin']:
+# AddressSanitizer tests are currently supported on Linux, Darwin and
+# FreeBSD only.
+if config.host_os not in ['Linux', 'Darwin', 'FreeBSD']:
config.unsupported = True
diff --git a/test/asan/lit.site.cfg.in b/test/asan/lit.site.cfg.in
index 76e0c55..332f9ad 100644
--- a/test/asan/lit.site.cfg.in
+++ b/test/asan/lit.site.cfg.in
@@ -6,9 +6,9 @@
config.asan_lit_source_dir = "@ASAN_LIT_SOURCE_DIR@"
config.target_cflags = "@ASAN_TEST_TARGET_CFLAGS@"
config.clang = "@ASAN_TEST_TARGET_CC@"
-config.llvm_tools_dir = "@ASAN_TEST_LLVM_TOOLS_DIR@"
+config.llvm_tools_dir = "@LLVM_TOOLS_BINARY_DIR@"
config.bits = "@ASAN_TEST_BITS@"
-config.android = "@CAN_TARGET_arm_android@"
+config.android = "@ANDROID@"
config.asan_dynamic = @ASAN_TEST_DYNAMIC@
config.target_arch = "@ASAN_TEST_TARGET_ARCH@"
diff --git a/test/dfsan/custom.c b/test/dfsan/custom.cc
similarity index 80%
rename from test/dfsan/custom.c
rename to test/dfsan/custom.cc
index 8a7a548..d7bb3e3 100644
--- a/test/dfsan/custom.c
+++ b/test/dfsan/custom.cc
@@ -5,7 +5,6 @@
// Tests custom implementations of various glibc functions.
-#define _GNU_SOURCE
#include <sanitizer/dfsan_interface.h>
#include <arpa/inet.h>
@@ -18,6 +17,7 @@
#include <sched.h>
#include <signal.h>
#include <stdio.h>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
@@ -256,12 +256,12 @@
// 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.
- char *crv = calloc(4096, 1);
+ char *crv = (char *) calloc(4096, 1);
ASSERT_ZERO_LABEL(crv[0]);
dfsan_set_label(i_label, crv, 100);
free(crv);
- crv = calloc(4096, 1);
+ crv = (char *) calloc(4096, 1);
ASSERT_ZERO_LABEL(crv[0]);
free(crv);
}
@@ -342,14 +342,14 @@
static int write_callback_count = 0;
static int last_fd;
-static const void *last_buf;
+static const unsigned char *last_buf;
static size_t last_count;
void write_callback(int fd, const void *buf, size_t count) {
write_callback_count++;
last_fd = fd;
- last_buf = buf;
+ last_buf = (const unsigned char*) buf;
last_count = count;
}
@@ -376,7 +376,7 @@
dfsan_set_label(i_label, &fd, sizeof(fd));
dfsan_set_label(j_label, &(buf[3]), 1);
dfsan_set_label(k_label, &buf_len, sizeof(buf_len));
-
+
res = write(fd, buf, buf_len);
assert(write_callback_count == 2);
ASSERT_READ_ZERO_LABEL(&res, sizeof(res));
@@ -694,11 +694,11 @@
dfsan_set_label(i_label, &str1[3], 1);
dfsan_set_label(j_label, &str1[4], 1);
- char *crv = memchr(str1, 'r', sizeof(str1));
+ char *crv = (char *) memchr(str1, 'r', sizeof(str1));
assert(crv == &str1[2]);
ASSERT_ZERO_LABEL(crv);
- crv = memchr(str1, '1', sizeof(str1));
+ crv = (char *) memchr(str1, '1', sizeof(str1));
assert(crv == &str1[3]);
#ifdef STRICT_DATA_DEPENDENCIES
ASSERT_ZERO_LABEL(crv);
@@ -706,7 +706,7 @@
ASSERT_LABEL(crv, i_label);
#endif
- crv = memchr(str1, 'x', sizeof(str1));
+ crv = (char *) memchr(str1, 'x', sizeof(str1));
assert(!crv);
#ifdef STRICT_DATA_DEPENDENCIES
ASSERT_ZERO_LABEL(crv);
@@ -774,6 +774,129 @@
close(fd);
}
+template <class T>
+void test_sprintf_chunk(const char* expected, const char* format, T arg) {
+ char buf[512];
+ memset(buf, 'a', sizeof(buf));
+
+ char padded_expected[512];
+ strcpy(padded_expected, "foo ");
+ strcat(padded_expected, expected);
+ strcat(padded_expected, " bar");
+
+ char padded_format[512];
+ strcpy(padded_format, "foo ");
+ strcat(padded_format, format);
+ strcat(padded_format, " bar");
+
+ // Non labelled arg.
+ assert(sprintf(buf, padded_format, arg) == strlen(padded_expected));
+ assert(strcmp(buf, padded_expected) == 0);
+ ASSERT_READ_LABEL(buf, strlen(padded_expected), 0);
+ memset(buf, 'a', sizeof(buf));
+
+ // Labelled arg.
+ dfsan_set_label(i_label, &arg, sizeof(arg));
+ assert(sprintf(buf, padded_format, arg) == strlen(padded_expected));
+ assert(strcmp(buf, padded_expected) == 0);
+ ASSERT_READ_LABEL(buf, 4, 0);
+ ASSERT_READ_LABEL(buf + 4, strlen(padded_expected) - 8, i_label);
+ ASSERT_READ_LABEL(buf + (strlen(padded_expected) - 4), 4, 0);
+}
+
+void test_sprintf() {
+ char buf[2048];
+ memset(buf, 'a', sizeof(buf));
+
+ // Test formatting (no conversion specifier).
+ assert(sprintf(buf, "Hello world!") == 12);
+ assert(strcmp(buf, "Hello world!") == 0);
+ ASSERT_READ_LABEL(buf, sizeof(buf), 0);
+
+ // Test for extra arguments.
+ assert(sprintf(buf, "Hello world!", 42, "hello") == 12);
+ assert(strcmp(buf, "Hello world!") == 0);
+ ASSERT_READ_LABEL(buf, sizeof(buf), 0);
+
+ // Test formatting & label propagation (multiple conversion specifiers): %s,
+ // %d, %n, %f, and %%.
+ const char* s = "world";
+ int m = 8;
+ int d = 27;
+ dfsan_set_label(k_label, (void *) (s + 1), 2);
+ dfsan_set_label(i_label, &m, sizeof(m));
+ dfsan_set_label(j_label, &d, sizeof(d));
+ int n;
+ int r = sprintf(buf, "hello %s, %-d/%d/%d %f %% %n%d", s, 2014, m, d,
+ 12345.6781234, &n, 1000);
+ assert(r == 42);
+ assert(strcmp(buf, "hello world, 2014/8/27 12345.678123 % 1000") == 0);
+ ASSERT_READ_LABEL(buf, 7, 0);
+ ASSERT_READ_LABEL(buf + 7, 2, k_label);
+ ASSERT_READ_LABEL(buf + 9, 9, 0);
+ ASSERT_READ_LABEL(buf + 18, 1, i_label);
+ ASSERT_READ_LABEL(buf + 19, 1, 0);
+ ASSERT_READ_LABEL(buf + 20, 2, j_label);
+ ASSERT_READ_LABEL(buf + 22, 15, 0);
+ ASSERT_LABEL(r, 0);
+ assert(n == 38);
+
+ // Test formatting & label propagation (single conversion specifier, with
+ // additional length and precision modifiers).
+ test_sprintf_chunk("-559038737", "%d", 0xdeadbeef);
+ test_sprintf_chunk("3735928559", "%u", 0xdeadbeef);
+ test_sprintf_chunk("12345", "%i", 12345);
+ test_sprintf_chunk("751", "%o", 0751);
+ test_sprintf_chunk("babe", "%x", 0xbabe);
+ test_sprintf_chunk("0000BABE", "%.8X", 0xbabe);
+ test_sprintf_chunk("-17", "%hhd", 0xdeadbeef);
+ test_sprintf_chunk("-16657", "%hd", 0xdeadbeef);
+ test_sprintf_chunk("deadbeefdeadbeef", "%lx", 0xdeadbeefdeadbeef);
+ test_sprintf_chunk("0xdeadbeefdeadbeef", "%p",
+ (void *) 0xdeadbeefdeadbeef);
+ test_sprintf_chunk("18446744073709551615", "%ju", (intmax_t) -1);
+ test_sprintf_chunk("18446744073709551615", "%zu", (size_t) -1);
+ test_sprintf_chunk("18446744073709551615", "%tu", (size_t) -1);
+
+ test_sprintf_chunk("0x1.f9acffa7eb6bfp-4", "%a", 0.123456);
+ test_sprintf_chunk("0X1.F9ACFFA7EB6BFP-4", "%A", 0.123456);
+ test_sprintf_chunk("0.12346", "%.5f", 0.123456);
+ test_sprintf_chunk("0.123456", "%g", 0.123456);
+ test_sprintf_chunk("1.234560e-01", "%e", 0.123456);
+ test_sprintf_chunk("1.234560E-01", "%E", 0.123456);
+ test_sprintf_chunk("0.1234567891234560", "%.16Lf",
+ (long double) 0.123456789123456);
+
+ test_sprintf_chunk("z", "%c", 'z');
+
+ // %n, %s, %d, %f, and %% already tested
+}
+
+void test_snprintf() {
+ char buf[2048];
+ memset(buf, 'a', sizeof(buf));
+ dfsan_set_label(0, buf, sizeof(buf));
+ const char* s = "world";
+ int y = 2014;
+ int m = 8;
+ int d = 27;
+ dfsan_set_label(k_label, (void *) (s + 1), 2);
+ dfsan_set_label(i_label, &y, sizeof(y));
+ dfsan_set_label(j_label, &m, sizeof(m));
+ int r = snprintf(buf, 19, "hello %s, %-d/%d/%d %f", s, y, m, d,
+ 12345.6781234);
+ // The return value is the number of bytes that would have been written to
+ // the final string if enough space had been available.
+ assert(r == 35);
+ assert(memcmp(buf, "hello world, 2014/", 19) == 0);
+ ASSERT_READ_LABEL(buf, 7, 0);
+ ASSERT_READ_LABEL(buf + 7, 2, k_label);
+ ASSERT_READ_LABEL(buf + 9, 4, 0);
+ ASSERT_READ_LABEL(buf + 13, 4, i_label);
+ ASSERT_READ_LABEL(buf + 17, 2, 0);
+ ASSERT_LABEL(r, 0);
+}
+
int main(void) {
i_label = dfsan_create_label("i", 0);
j_label = dfsan_create_label("j", 0);
@@ -810,7 +933,9 @@
test_select();
test_sigaction();
test_sigemptyset();
+ test_snprintf();
test_socketpair();
+ test_sprintf();
test_stat();
test_strcasecmp();
test_strchr();
diff --git a/test/dfsan/dump_labels.c b/test/dfsan/dump_labels.c
new file mode 100644
index 0000000..67801af
--- /dev/null
+++ b/test/dfsan/dump_labels.c
@@ -0,0 +1,69 @@
+// RUN: %clang_dfsan -m64 %s -o %t
+// RUN: DFSAN_OPTIONS=dump_labels_at_exit=/dev/stdout %run %t 2>&1 | FileCheck %s
+// RUN: DFSAN_OPTIONS=dump_labels_at_exit=/dev/stdout not %run %t c 2>&1 | FileCheck %s --check-prefix=CHECK-OOL
+// RUN: DFSAN_OPTIONS=dump_labels_at_exit=/dev/stdout not %run %t u 2>&1 | FileCheck %s --check-prefix=CHECK-OOL
+
+// Tests that labels are properly dumped at program termination.
+
+#include <sanitizer/dfsan_interface.h>
+#include <assert.h>
+#include <stdio.h>
+
+int main(int argc, char** argv) {
+ 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));
+
+ dfsan_label ij_label = dfsan_get_label(i + j);
+ dfsan_label ijk_label = dfsan_get_label(i + j + k);
+
+ fprintf(stderr, "i %d j %d k %d ij %d ijk %d\n", i_label, j_label, k_label,
+ ij_label, ijk_label);
+
+ // CHECK: 1 0 0 i
+ // CHECK: 2 0 0 j
+ // CHECK: 3 0 0 k
+ // CHECK: 4 1 2
+ // CHECK: 5 3 4
+
+ if (argc > 1) {
+ // Exhaust the labels.
+ unsigned long num_labels = 1 << (sizeof(dfsan_label) * 8);
+ for (unsigned long i = ijk_label + 1; i < num_labels - 2; ++i) {
+ dfsan_label l = dfsan_create_label("l", 0);
+ assert(l == i);
+ }
+
+ // Consume the last available label.
+ dfsan_label l = dfsan_union(5, 6);
+ assert(l == num_labels - 2);
+
+ // Try to allocate another label (either explicitly or by unioning two
+ // existing labels), but expect a crash.
+ if (argv[1][0] == 'c') {
+ l = dfsan_create_label("l", 0);
+ } else {
+ l = dfsan_union(6, 7);
+ }
+
+ // CHECK-OOL: FATAL: DataFlowSanitizer: out of labels
+ // CHECK-OOL: 1 0 0 i
+ // CHECK-OOL: 2 0 0 j
+ // CHECK-OOL: 3 0 0 k
+ // CHECK-OOL: 4 1 2
+ // CHECK-OOL: 5 3 4
+ // CHECK-OOL: 6 0 0
+ // CHECK-OOL: 65534 5 6
+ // CHECK-OOL: 65535 0 0 <init label>
+ }
+
+ return 0;
+}
diff --git a/test/dfsan/vararg.c b/test/dfsan/vararg.c
new file mode 100644
index 0000000..2227ba7
--- /dev/null
+++ b/test/dfsan/vararg.c
@@ -0,0 +1,24 @@
+// RUN: %clang_dfsan -m64 %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+// RUN: %run %t foo
+// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+// RUN: %run %t foo
+
+#include <stdio.h>
+
+int do_nothing(const char *format, ...) {
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int (*fp)(const char *, ...);
+
+ if (argc > 1)
+ fp = do_nothing;
+ else
+ fp = printf;
+
+ // CHECK: FATAL: DataFlowSanitizer: unsupported indirect call to vararg function printf
+ fp("hello %s\n", "world");
+}
diff --git a/test/lit.common.cfg b/test/lit.common.cfg
index adf65ee..0ee2b84 100644
--- a/test/lit.common.cfg
+++ b/test/lit.common.cfg
@@ -25,8 +25,12 @@
config.cxx_mode_flags = ["--driver-mode=g++"]
else:
config.cxx_mode_flags = []
+ # We assume that sanitizers should provide good enough error
+ # reports and stack traces even with minimal debug info.
+ config.debug_info_flags = ["-gline-tables-only"]
elif compiler_id == 'GNU':
config.cxx_mode_flags = ["-x c++"]
+ config.debug_info_flags = ["-g"]
else:
lit_config.fatal("Unsupported compiler id: %r" % compiler_id)
# Add compiler ID to the list of available features.
@@ -69,6 +73,9 @@
# Allow tests to be executed on a simulator or remotely.
config.substitutions.append( ('%run', config.emulator) )
+# Define CHECK-%os to check for OS-dependent output.
+config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os)))
+
# Add supported compiler_rt architectures to a list of available features.
compiler_rt_arch = getattr(config, 'compiler_rt_arch', None)
if compiler_rt_arch:
diff --git a/test/lsan/TestCases/ignore_object.cc b/test/lsan/TestCases/ignore_object.cc
index 133ccc7..38d76e6 100644
--- a/test/lsan/TestCases/ignore_object.cc
+++ b/test/lsan/TestCases/ignore_object.cc
@@ -1,7 +1,7 @@
// Test for __lsan_ignore_object().
// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0:verbosity=2"
// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE ASAN_OPTIONS=$ASAN_OPTIONS:"verbosity=2" not %run %t 2>&1 | FileCheck %s
+// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
#include <stdio.h>
#include <stdlib.h>
diff --git a/test/lsan/TestCases/ignore_object_errors.cc b/test/lsan/TestCases/ignore_object_errors.cc
index 7ca2a4f..39b9b02 100644
--- a/test/lsan/TestCases/ignore_object_errors.cc
+++ b/test/lsan/TestCases/ignore_object_errors.cc
@@ -1,7 +1,7 @@
// Test for incorrect use of __lsan_ignore_object().
// RUN: LSAN_BASE="verbosity=2"
// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=2 %run %t 2>&1 | FileCheck %s
+// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 | FileCheck %s
#include <stdio.h>
#include <stdlib.h>
diff --git a/test/lsan/TestCases/leak_check_at_exit.cc b/test/lsan/TestCases/leak_check_at_exit.cc
index 3e4ccd7..fe3f70e 100644
--- a/test/lsan/TestCases/leak_check_at_exit.cc
+++ b/test/lsan/TestCases/leak_check_at_exit.cc
@@ -3,8 +3,8 @@
// RUN: %clangxx_lsan %s -o %t
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-do
-// RUN: LSAN_OPTIONS=$LSAN_BASE:"leak_check_at_exit=0" ASAN_OPTIONS="$ASAN_OPTIONS:leak_check_at_exit=0" not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do
-// RUN: LSAN_OPTIONS=%LSAN_BASE:"leak_check_at_exit=0" ASAN_OPTIONS="$ASAN_OPTIONS:leak_check_at_exit=0" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont
+// RUN: LSAN_OPTIONS=$LSAN_BASE:"leak_check_at_exit=0" not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do
+// RUN: LSAN_OPTIONS=%LSAN_BASE:"leak_check_at_exit=0" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont
#include <stdio.h>
#include <stdlib.h>
diff --git a/test/lsan/TestCases/register_root_region.cc b/test/lsan/TestCases/register_root_region.cc
index acc8e1b..6fc84c2 100644
--- a/test/lsan/TestCases/register_root_region.cc
+++ b/test/lsan/TestCases/register_root_region.cc
@@ -16,7 +16,7 @@
int main(int argc, char *argv[]) {
size_t size = getpagesize() * 2;
void *p =
- mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
assert(p);
// Make half of the memory inaccessible. LSan must not crash trying to read it.
assert(0 == mprotect((char *)p + size / 2, size / 2, PROT_NONE));
diff --git a/test/lsan/TestCases/swapcontext.cc b/test/lsan/TestCases/swapcontext.cc
index 1bb6057..f7e95ed 100644
--- a/test/lsan/TestCases/swapcontext.cc
+++ b/test/lsan/TestCases/swapcontext.cc
@@ -6,6 +6,11 @@
// RUN: not %run %t foo 2>&1 | FileCheck %s
#include <stdio.h>
+#if defined(__APPLE__)
+// Note: ucontext.h is deprecated on OSX, so this test may stop working
+// someday. We define _XOPEN_SOURCE to keep using ucontext.h for now.
+#define _XOPEN_SOURCE 1
+#endif
#include <ucontext.h>
#include <unistd.h>
diff --git a/test/lsan/TestCases/use_registers.cc b/test/lsan/TestCases/use_registers.cc
index d436144..ce11c3f 100644
--- a/test/lsan/TestCases/use_registers.cc
+++ b/test/lsan/TestCases/use_registers.cc
@@ -7,6 +7,7 @@
#include <assert.h>
#include <pthread.h>
+#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
@@ -33,7 +34,7 @@
fflush(stderr);
__sync_fetch_and_xor(sync, 1);
while (true)
- pthread_yield();
+ sched_yield();
}
int main() {
@@ -42,7 +43,7 @@
int res = pthread_create(&thread_id, 0, registers_thread_func, &sync);
assert(res == 0);
while (!__sync_fetch_and_xor(&sync, 0))
- pthread_yield();
+ sched_yield();
return 0;
}
// CHECK: Test alloc: [[ADDR:.*]].
diff --git a/test/lsan/TestCases/use_stacks_threaded.cc b/test/lsan/TestCases/use_stacks_threaded.cc
index fc4e661..a1d4383 100644
--- a/test/lsan/TestCases/use_stacks_threaded.cc
+++ b/test/lsan/TestCases/use_stacks_threaded.cc
@@ -7,6 +7,7 @@
#include <assert.h>
#include <pthread.h>
+#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
@@ -18,7 +19,7 @@
fflush(stderr);
__sync_fetch_and_xor(sync, 1);
while (true)
- pthread_yield();
+ sched_yield();
}
int main() {
@@ -27,7 +28,7 @@
int res = pthread_create(&thread_id, 0, stacks_thread_func, &sync);
assert(res == 0);
while (!__sync_fetch_and_xor(&sync, 0))
- pthread_yield();
+ sched_yield();
return 0;
}
// CHECK: Test alloc: [[ADDR:.*]].
diff --git a/test/lsan/lit.common.cfg b/test/lsan/lit.common.cfg
index 7411d7a..e11c90a 100644
--- a/test/lsan/lit.common.cfg
+++ b/test/lsan/lit.common.cfg
@@ -29,7 +29,7 @@
else:
lit_config.fatal("Unknown LSan test mode: %r" % lsan_lit_test_mode)
-clang_cflags = ["-g", "-O0", "-m64"]
+clang_cflags = ["-O0", "-m64"] + config.debug_info_flags
clang_cxxflags = config.cxx_mode_flags + clang_cflags
clang_lsan_cflags = clang_cflags + lsan_cflags
clang_lsan_cxxflags = clang_cxxflags + lsan_cflags
diff --git a/test/msan/Linux/syscalls.cc b/test/msan/Linux/syscalls.cc
index 39b893b..4dd97e7 100644
--- a/test/msan/Linux/syscalls.cc
+++ b/test/msan/Linux/syscalls.cc
@@ -10,6 +10,7 @@
#include <linux/aio_abi.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
+#include <sys/uio.h>
#include <sanitizer/linux_syscall_hooks.h>
#include <sanitizer/msan_interface.h>
@@ -84,17 +85,24 @@
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] };
+ struct iocb iocb[3];
+ struct iocb *iocbp[3] = { &iocb[0], &iocb[1], &iocb[2] };
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[0].aio_nbytes = 10;
iocb[1].aio_lio_opcode = IOCB_CMD_PREAD;
- iocb[1].aio_buf = (__u64)(&buf[kFortyTwo]);
- iocb[1].aio_nbytes = kFortyTwo;
- __sanitizer_syscall_pre_io_submit(0, 2, &iocbp);
- assert(__msan_test_shadow(buf, sizeof(buf)) == 2 * kFortyTwo);
+ iocb[1].aio_buf = (__u64)(&buf[20]);
+ iocb[1].aio_nbytes = 15;
+ struct iovec vec[2] = { {&buf[40], 3}, {&buf[50], 20} };
+ iocb[2].aio_lio_opcode = IOCB_CMD_PREADV;
+ iocb[2].aio_buf = (__u64)(&vec);
+ iocb[2].aio_nbytes = 2;
+ __sanitizer_syscall_pre_io_submit(0, 3, &iocbp);
+ assert(__msan_test_shadow(buf, sizeof(buf)) == 10);
+ assert(__msan_test_shadow(buf + 20, sizeof(buf) - 20) == 15);
+ assert(__msan_test_shadow(buf + 40, sizeof(buf) - 40) == 3);
+ assert(__msan_test_shadow(buf + 50, sizeof(buf) - 50) == 20);
__msan_poison(buf, sizeof(buf));
char *p = buf;
diff --git a/test/msan/chained_origin_limits.cc b/test/msan/chained_origin_limits.cc
index a8621f3..0cc57f3 100644
--- a/test/msan/chained_origin_limits.cc
+++ b/test/msan/chained_origin_limits.cc
@@ -12,6 +12,9 @@
// RUN: MSAN_OPTIONS=origin_history_per_stack_limit=1 not %run %t >%t.out 2>&1
// RUN: FileCheck %s --check-prefix=CHECK-PER-STACK < %t.out
+// RUN: MSAN_OPTIONS=origin_history_size=7,origin_history_per_stack_limit=0 not %run %t >%t.out 2>&1
+// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out
+
// Stack origin.
// RUN: %clangxx_msan -DSTACK -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t
@@ -24,6 +27,9 @@
// RUN: MSAN_OPTIONS=origin_history_per_stack_limit=1 not %run %t >%t.out 2>&1
// RUN: FileCheck %s --check-prefix=CHECK-PER-STACK < %t.out
+// RUN: MSAN_OPTIONS=origin_history_size=7,origin_history_per_stack_limit=0 not %run %t >%t.out 2>&1
+// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out
+
// Heap origin, with calls.
// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t
@@ -37,6 +43,9 @@
// RUN: MSAN_OPTIONS=origin_history_per_stack_limit=1 not %run %t >%t.out 2>&1
// RUN: FileCheck %s --check-prefix=CHECK-PER-STACK < %t.out
+// RUN: MSAN_OPTIONS=origin_history_size=7,origin_history_per_stack_limit=0 not %run %t >%t.out 2>&1
+// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out
+
// Stack origin, with calls.
// RUN: %clangxx_msan -DSTACK -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t
@@ -50,6 +59,9 @@
// RUN: MSAN_OPTIONS=origin_history_per_stack_limit=1 not %run %t >%t.out 2>&1
// RUN: FileCheck %s --check-prefix=CHECK-PER-STACK < %t.out
+// RUN: MSAN_OPTIONS=origin_history_size=7,origin_history_per_stack_limit=0 not %run %t >%t.out 2>&1
+// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -143,3 +155,24 @@
// CHECK-PER-STACK: Uninitialized value was stored to memory at
// CHECK-PER-STACK: in fn1
// CHECK-PER-STACK: Uninitialized value was created
+
+// CHECK-UNLIMITED: WARNING: MemorySanitizer: use-of-uninitialized-value
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was stored to memory at
+// CHECK-UNLIMITED: Uninitialized value was created
diff --git a/test/msan/chained_origin_with_signals.cc b/test/msan/chained_origin_with_signals.cc
index ef98385..2841e34 100644
--- a/test/msan/chained_origin_with_signals.cc
+++ b/test/msan/chained_origin_with_signals.cc
@@ -25,9 +25,9 @@
int volatile z;
x = z;
- signal(SIGUSR1, SignalHandler);
- kill(getpid(), SIGUSR1);
- signal(SIGUSR1, SIG_DFL);
+ signal(SIGHUP, SignalHandler);
+ kill(getpid(), SIGHUP);
+ signal(SIGHUP, SIG_DFL);
return y;
}
diff --git a/test/msan/fork.cc b/test/msan/fork.cc
new file mode 100644
index 0000000..10de8a9
--- /dev/null
+++ b/test/msan/fork.cc
@@ -0,0 +1,121 @@
+// Test that chained origins are fork-safe.
+// Run a number of threads that create new chained origins, then fork
+// and verify that origin reads do not deadlock in the child process.
+
+// RUN: %clangxx_msan -std=c++11 -fsanitize-memory-track-origins=2 -g -m64 -O3 %s -o %t
+// RUN: MSAN_OPTIONS=store_context_size=1000,origin_history_size=0,origin_history_per_stack_limit=0 %run %t |& FileCheck %s
+
+// Fun fact: if test output is redirected to a file (as opposed to
+// being piped directly to FileCheck), we may lose some "done"s due to
+// a kernel bug:
+// https://lkml.org/lkml/2014/2/17/324
+
+
+#include <pthread.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <sanitizer/msan_interface.h>
+
+int done;
+
+void copy_uninit_thread2() {
+ volatile int x;
+ volatile int v;
+ while (true) {
+ v = x;
+ x = v;
+ if (__atomic_load_n(&done, __ATOMIC_RELAXED))
+ return;
+ }
+}
+
+void copy_uninit_thread1(int level) {
+ if (!level)
+ copy_uninit_thread2();
+ else
+ copy_uninit_thread1(level - 1);
+}
+
+void *copy_uninit_thread(void *id) {
+ copy_uninit_thread1((long)id);
+ return 0;
+}
+
+// Run through stackdepot in the child process.
+// If any of the hash table cells are locked, this may deadlock.
+void child() {
+ volatile int x;
+ volatile int v;
+ for (int i = 0; i < 10000; ++i) {
+ v = x;
+ x = v;
+ }
+ write(2, "done\n", 5);
+}
+
+void test() {
+ const int kThreads = 10;
+ pthread_t t[kThreads];
+ for (int i = 0; i < kThreads; ++i)
+ pthread_create(&t[i], NULL, copy_uninit_thread, (void*)(long)i);
+ usleep(100000);
+ pid_t pid = fork();
+ if (pid) {
+ // parent
+ __atomic_store_n(&done, 1, __ATOMIC_RELAXED);
+ pid_t p;
+ while ((p = wait(NULL)) == -1) { }
+ } else {
+ // child
+ child();
+ }
+}
+
+int main() {
+ const int kChildren = 20;
+ for (int i = 0; i < kChildren; ++i) {
+ pid_t pid = fork();
+ if (pid) {
+ // parent
+ } else {
+ test();
+ exit(0);
+ }
+ }
+
+ for (int i = 0; i < kChildren; ++i) {
+ pid_t p;
+ while ((p = wait(NULL)) == -1) { }
+ }
+
+ return 0;
+}
+
+// Expect 20 (== kChildren) "done" messages.
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
diff --git a/test/msan/lit.cfg b/test/msan/lit.cfg
index 3143608..f425e25 100644
--- a/test/msan/lit.cfg
+++ b/test/msan/lit.cfg
@@ -13,8 +13,7 @@
"-mno-omit-leaf-frame-pointer",
"-fno-omit-frame-pointer",
"-fno-optimize-sibling-calls",
- "-g",
- "-m64"]
+ "-m64"] + config.debug_info_flags
clang_msan_cxxflags = config.cxx_mode_flags + clang_msan_cflags
def build_invocation(compile_flags):
diff --git a/test/msan/param_tls_limit.cc b/test/msan/param_tls_limit.cc
new file mode 100644
index 0000000..869afc9
--- /dev/null
+++ b/test/msan/param_tls_limit.cc
@@ -0,0 +1,74 @@
+// ParamTLS has limited size. Everything that does not fit is considered fully
+// initialized.
+
+// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t
+// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && %run %t
+// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 %s -o %t && %run %t
+
+#include <sanitizer/msan_interface.h>
+#include <assert.h>
+
+// This test assumes that ParamTLS size is 800 bytes.
+
+// This test passes poisoned values through function argument list.
+// In case of overflow, argument is unpoisoned.
+#define OVERFLOW(x) assert(__msan_test_shadow(&x, sizeof(x)) == -1)
+// In case of no overflow, it is still poisoned.
+#define NO_OVERFLOW(x) assert(__msan_test_shadow(&x, sizeof(x)) == 0)
+
+template<int N>
+struct S {
+ char x[N];
+};
+
+void f100(S<100> s) {
+ NO_OVERFLOW(s);
+}
+
+void f800(S<800> s) {
+ NO_OVERFLOW(s);
+}
+
+void f801(S<801> s) {
+ OVERFLOW(s);
+}
+
+void f1000(S<1000> s) {
+ OVERFLOW(s);
+}
+
+void f_many(int a, double b, S<800> s, int c, double d) {
+ NO_OVERFLOW(a);
+ NO_OVERFLOW(b);
+ OVERFLOW(s);
+ OVERFLOW(c);
+ OVERFLOW(d);
+}
+
+// -8 bytes for "int a", aligned by 8
+// -2 to make "int c" a partial fit
+void f_many2(int a, S<800 - 8 - 2> s, int c, double d) {
+ NO_OVERFLOW(a);
+ NO_OVERFLOW(s);
+ OVERFLOW(c);
+ OVERFLOW(d);
+}
+
+int main(void) {
+ S<100> s100;
+ S<800> s800;
+ S<801> s801;
+ S<1000> s1000;
+ f100(s100);
+ f800(s800);
+ f801(s801);
+ f1000(s1000);
+
+ int i;
+ double d;
+ f_many(i, d, s800, i, d);
+
+ S<800 - 8 - 2> s788;
+ f_many2(i, s788, i, d);
+ return 0;
+}
diff --git a/test/msan/report-demangling.cc b/test/msan/report-demangling.cc
new file mode 100644
index 0000000..e6d5c27
--- /dev/null
+++ b/test/msan/report-demangling.cc
@@ -0,0 +1,19 @@
+// Test that function name is mangled in the "created by an allocation" line,
+// and demangled in the single-frame "stack trace" that follows.
+
+// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1
+// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out
+
+__attribute__((noinline))
+int f() {
+ int x;
+ int *volatile p = &x;
+ return *p;
+}
+
+int main(int argc, char **argv) {
+ return f();
+ // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value
+ // CHECK: Uninitialized value was created by an allocation of 'x' in the stack frame of function '_Z1fv'
+ // CHECK: #0 {{.*}} in f() {{.*}}report-demangling.cc:[[@LINE-10]]
+}
diff --git a/test/msan/wrap_indirect_calls.cc b/test/msan/wrap_indirect_calls.cc
deleted file mode 100644
index be17bd8..0000000
--- a/test/msan/wrap_indirect_calls.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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: %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: %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: %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: %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: %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/test/msan/wrap_indirect_calls/caller.cc b/test/msan/wrap_indirect_calls/caller.cc
deleted file mode 100644
index a0af8b7..0000000
--- a/test/msan/wrap_indirect_calls/caller.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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/test/msan/wrap_indirect_calls/lit.local.cfg b/test/msan/wrap_indirect_calls/lit.local.cfg
deleted file mode 100644
index 5e01230..0000000
--- a/test/msan/wrap_indirect_calls/lit.local.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-# Sources in this directory are used by tests in parent directory.
-
-config.suffixes = []
diff --git a/test/msan/wrap_indirect_calls/one.cc b/test/msan/wrap_indirect_calls/one.cc
deleted file mode 100644
index ab7bf41..0000000
--- a/test/msan/wrap_indirect_calls/one.cc
+++ /dev/null
@@ -1,3 +0,0 @@
-int f_other_object(int x, int y) {
- return x * y;
-}
diff --git a/test/msan/wrap_indirect_calls/two.cc b/test/msan/wrap_indirect_calls/two.cc
deleted file mode 100644
index c939a99..0000000
--- a/test/msan/wrap_indirect_calls/two.cc
+++ /dev/null
@@ -1,11 +0,0 @@
-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/test/msan/wrap_indirect_calls/wrapper.cc b/test/msan/wrap_indirect_calls/wrapper.cc
deleted file mode 100644
index 8fcd0c6..0000000
--- a/test/msan/wrap_indirect_calls/wrapper.cc
+++ /dev/null
@@ -1,11 +0,0 @@
-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/test/msan/wrap_indirect_calls2.cc b/test/msan/wrap_indirect_calls2.cc
deleted file mode 100644
index fb4e6c7..0000000
--- a/test/msan/wrap_indirect_calls2.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Test __msan_set_indirect_call_wrapper.
-
-// RUN: %clangxx_msan -mllvm -msan-wrap-indirect-calls=__msan_wrap_indirect_call \
-// RUN: -mllvm -msan-wrap-indirect-calls-fast=0 \
-// RUN: -O0 -g -rdynamic -Wl,--defsym=__executable_start=0 %s -o %t && %run %t
-
-// This test disables -msan-wrap-indirect-calls-fast, otherwise indirect calls
-// inside the same module are short-circuited and are never seen by the wrapper.
-
-#include <assert.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdint.h>
-
-extern "C" void __msan_set_indirect_call_wrapper(uintptr_t);
-
-bool done_f, done_g;
-
-void f(void) {
- assert(!done_g);
- done_f = true;
-}
-
-void g(void) {
- assert(done_f);
- done_g = true;
-}
-
-typedef void (*Fn)(void);
-extern "C" Fn my_wrapper(Fn target) {
- if (target == f) return g;
- return target;
-}
-
-int main(void) {
- volatile Fn fp;
- fp = &f;
- fp();
- __msan_set_indirect_call_wrapper((uintptr_t)my_wrapper);
- fp();
- return !(done_f && done_g);
-}
diff --git a/test/msan/wrap_indirect_calls_in_rtl.cc b/test/msan/wrap_indirect_calls_in_rtl.cc
deleted file mode 100644
index 040ff13..0000000
--- a/test/msan/wrap_indirect_calls_in_rtl.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Test indirect call wrapping in MemorySanitizer runtime.
-
-// RUN: %clangxx_msan -O0 -g -rdynamic %s -o %t && %run %t
-
-#include <assert.h>
-#include <math.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <sys/time.h>
-
-extern "C" void __msan_set_indirect_call_wrapper(uintptr_t);
-
-bool pthread_create_done;
-
-void *ThreadFn(void *) {
- printf("bad threadfn\n");
- return 0;
-}
-
-void *ThreadFn2(void *) {
- printf("good threadfn\n");
- pthread_create_done = true;
- return 0;
-}
-
-bool in_gettimeofday;
-bool in_lgamma;
-
-int my_gettimeofday(struct timeval *p, void *q) {
- p->tv_sec = 1;
- p->tv_usec = 2;
- return 42;
-}
-
-double my_lgamma(double x) {
- return x;
-}
-
-extern "C" uintptr_t my_wrapper(uintptr_t f) {
- if (f == (uintptr_t)ThreadFn)
- return (uintptr_t)&ThreadFn2;
- if (in_gettimeofday)
- return (uintptr_t)my_gettimeofday;
- if (in_lgamma)
- return (uintptr_t)my_lgamma;
- return f;
-}
-
-int main(void) {
- __msan_set_indirect_call_wrapper((uintptr_t)my_wrapper);
-
- // ThreadFn is called indirectly from a wrapper function in MSan rtl and
- // is subject to indirect call wrapping (it could be an native-to-translated
- // edge).
- pthread_t t;
- pthread_create(&t, 0, ThreadFn, 0);
- pthread_join(t, 0);
- assert(pthread_create_done);
-
- // gettimeofday is intercepted in msan_interceptors.cc and the real one (from
- // libc) is called indirectly.
- struct timeval tv;
- in_gettimeofday = true;
- int res = gettimeofday(&tv, NULL);
- in_gettimeofday = false;
- assert(tv.tv_sec == 1);
- assert(tv.tv_usec == 2);
- assert(res == 42);
-
- // lgamma is intercepted in sanitizer_common_interceptors.inc and is also
- // called indirectly.
- in_lgamma = true;
- double dres = lgamma(1.1);
- in_lgamma = false;
- assert(dres == 1.1);
-
- return 0;
-}
diff --git a/test/profile/instrprof-basic.c b/test/profile/instrprof-basic.c
index 3db0831..fd3516c 100644
--- a/test/profile/instrprof-basic.c
+++ b/test/profile/instrprof-basic.c
@@ -3,10 +3,29 @@
// RUN: llvm-profdata merge -o %t.profdata %t.profraw
// RUN: %clang_profuse=%t.profdata -o - -S -emit-llvm %s | FileCheck %s
+int begin(int i) {
+ // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]]
+ if (i)
+ return 0;
+ return 1;
+}
+
+int end(int i) {
+ // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD2:[0-9]+]]
+ if (i)
+ return 0;
+ return 1;
+}
+
int main(int argc, const char *argv[]) {
- // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof !1
+ begin(0);
+ end(1);
+
+ // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD2:[0-9]+]]
if (argc)
return 0;
return 1;
}
-// CHECK: !1 = metadata !{metadata !"branch_weights", i32 2, i32 1}
+
+// CHECK: ![[PD1]] = metadata !{metadata !"branch_weights", i32 1, i32 2}
+// CHECK: ![[PD2]] = metadata !{metadata !"branch_weights", i32 2, i32 1}
diff --git a/test/profile/instrprof-dlopen.test b/test/profile/instrprof-dlopen.test
index f0c067f..ba386e3 100644
--- a/test/profile/instrprof-dlopen.test
+++ b/test/profile/instrprof-dlopen.test
@@ -1,8 +1,8 @@
RUN: mkdir -p %t.d
RUN: %clang_profgen -o %t.d/func.shared -fPIC -shared %S/Inputs/instrprof-dlopen-func.c
RUN: %clang_profgen -o %t.d/func2.shared -fPIC -shared %S/Inputs/instrprof-dlopen-func2.c
-RUN: %clang -o %t-local -fPIC -DDLOPEN_FUNC_DIR=\"%t.d\" -DDLOPEN_FLAGS="RTLD_LAZY | RTLD_LOCAL" %S/Inputs/instrprof-dlopen-main.c -ldl
-RUN: %clang -o %t-global -fPIC -DDLOPEN_FUNC_DIR=\"%t.d\" -DDLOPEN_FLAGS="RTLD_LAZY | RTLD_GLOBAL" %S/Inputs/instrprof-dlopen-main.c -ldl
+RUN: %clang -o %t-local -fPIC -DDLOPEN_FUNC_DIR=\"%t.d\" -DDLOPEN_FLAGS="RTLD_LAZY | RTLD_LOCAL" %S/Inputs/instrprof-dlopen-main.c
+RUN: %clang -o %t-global -fPIC -DDLOPEN_FUNC_DIR=\"%t.d\" -DDLOPEN_FLAGS="RTLD_LAZY | RTLD_GLOBAL" %S/Inputs/instrprof-dlopen-main.c
RUN: %clang -c -o %t.d/main.o %S/Inputs/instrprof-dlopen-main.c
RUN: %clang_profgen -o %t-static %S/Inputs/instrprof-dlopen-func.c %S/Inputs/instrprof-dlopen-func2.c %t.d/main.o
diff --git a/test/profile/instrprof-set-filename.c b/test/profile/instrprof-set-filename.c
index 51de14f..0458218 100644
--- a/test/profile/instrprof-set-filename.c
+++ b/test/profile/instrprof-set-filename.c
@@ -5,10 +5,10 @@
void __llvm_profile_set_filename(const char *);
int main(int argc, const char *argv[]) {
- // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof !1
+ // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]]
if (argc < 2)
return 1;
__llvm_profile_set_filename(argv[1]);
return 0;
}
-// CHECK: !1 = metadata !{metadata !"branch_weights", i32 1, i32 2}
+// CHECK: ![[PD1]] = metadata !{metadata !"branch_weights", i32 1, i32 2}
diff --git a/test/profile/instrprof-without-libc.c b/test/profile/instrprof-without-libc.c
index ca83d46..60ca949 100644
--- a/test/profile/instrprof-without-libc.c
+++ b/test/profile/instrprof-without-libc.c
@@ -17,8 +17,8 @@
int __llvm_profile_write_buffer(char *);
int write_buffer(uint64_t, const char *);
int main(int argc, const char *argv[]) {
- // CHECK-LABEL: define i32 @main(
- // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof !1
+ // CHECK-LABEL: define {{.*}} @main(
+ // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]]
if (argc < 2)
return 1;
@@ -46,7 +46,7 @@
return fclose(File);
#endif
}
-// CHECK: !1 = metadata !{metadata !"branch_weights", i32 1, i32 2}
+// CHECK: ![[PD1]] = metadata !{metadata !"branch_weights", i32 1, i32 2}
// CHECK-SYMBOLS-NOT: ___cxx_global_var_init
// CHECK-SYMBOLS-NOT: ___llvm_profile_register_write_file_atexit
diff --git a/test/profile/instrprof-write-file-atexit-explicitly.c b/test/profile/instrprof-write-file-atexit-explicitly.c
index 931a48b..ba229b9 100644
--- a/test/profile/instrprof-write-file-atexit-explicitly.c
+++ b/test/profile/instrprof-write-file-atexit-explicitly.c
@@ -8,10 +8,10 @@
void __llvm_profile_set_filename(const char *);
int main(int argc, const char *argv[]) {
__llvm_profile_register_write_file_atexit();
- // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof !1
+ // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]]
if (argc < 2)
return 1;
__llvm_profile_set_filename(argv[1]);
return 0;
}
-// CHECK: !1 = metadata !{metadata !"branch_weights", i32 1, i32 2}
+// CHECK: ![[PD1]] = metadata !{metadata !"branch_weights", i32 1, i32 2}
diff --git a/test/profile/instrprof-write-file-only.c b/test/profile/instrprof-write-file-only.c
index 65a2226..0dd61de 100644
--- a/test/profile/instrprof-write-file-only.c
+++ b/test/profile/instrprof-write-file-only.c
@@ -9,8 +9,8 @@
void __llvm_profile_set_filename(const char *);
int foo(int);
int main(int argc, const char *argv[]) {
- // CHECK-LABEL: define i32 @main
- // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof !1
+ // CHECK-LABEL: define {{.*}} @main(
+ // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]]
if (argc > 1)
return 1;
@@ -28,8 +28,8 @@
// There should be no profiling information for @foo, since it was called
// after the profile was written (and the atexit was suppressed by defining
// profile_runtime).
- // CHECK-LABEL: define i32 @foo
+ // CHECK-LABEL: define {{.*}} @foo(
// CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{[^,]+$}}
return X <= 0 ? -X : X;
}
-// CHECK: !1 = metadata !{metadata !"branch_weights", i32 1, i32 2}
+// CHECK: ![[PD1]] = metadata !{metadata !"branch_weights", i32 1, i32 2}
diff --git a/test/profile/instrprof-write-file.c b/test/profile/instrprof-write-file.c
index f5c2958..12967cb 100644
--- a/test/profile/instrprof-write-file.c
+++ b/test/profile/instrprof-write-file.c
@@ -9,8 +9,8 @@
void __llvm_profile_set_filename(const char *);
int foo(int);
int main(int argc, const char *argv[]) {
- // CHECK-LABEL: define i32 @main
- // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof !1
+ // CHECK-LABEL: define {{.*}} @main(
+ // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]]
if (argc < 2)
return 1;
@@ -25,10 +25,10 @@
return Ret;
}
int foo(int X) {
- // CHECK-LABEL: define i32 @foo
+ // CHECK-LABEL: define {{.*}} @foo(
// CHECK1: br i1 %{{.*}}, label %{{.*}}, label %{{[^,]+$}}
- // CHECK2: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof !2
+ // CHECK2: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD2:[0-9]+]]
return X <= 0 ? -X : X;
}
-// CHECK: !1 = metadata !{metadata !"branch_weights", i32 1, i32 2}
-// CHECK2: !2 = metadata !{metadata !"branch_weights", i32 2, i32 1}
+// CHECK: ![[PD1]] = metadata !{metadata !"branch_weights", i32 1, i32 2}
+// CHECK2: ![[PD2]] = metadata !{metadata !"branch_weights", i32 2, i32 1}
diff --git a/test/profile/lit.cfg b/test/profile/lit.cfg
index 6e53b50..e4910ab 100644
--- a/test/profile/lit.cfg
+++ b/test/profile/lit.cfg
@@ -24,6 +24,11 @@
lit_config.load_config(config, site_cfg)
raise SystemExit
+if config.host_os in ['Linux']:
+ extra_linkflags = ["-ldl"]
+else:
+ extra_linkflags = []
+
# Test suffixes.
config.suffixes = ['.c', '.cc', '.cpp', '.m', '.mm', '.ll', '.test']
@@ -31,7 +36,7 @@
config.excludes = ['Inputs']
# Clang flags.
-clang_cflags = [config.target_cflags]
+clang_cflags = [config.target_cflags] + extra_linkflags
def build_invocation(compile_flags):
return " " + " ".join([config.clang] + compile_flags) + " "
@@ -41,6 +46,8 @@
config.substitutions.append( ("%clang_profgen ", build_invocation(clang_cflags) + " -fprofile-instr-generate ") )
config.substitutions.append( ("%clang_profuse=", build_invocation(clang_cflags) + " -fprofile-instr-use=") )
-# Profile tests are currently supported on Linux and Darwin only.
-if config.host_os not in ['Linux', 'Darwin']:
+if config.host_os not in ['Darwin', 'FreeBSD', 'Linux']:
+ config.unsupported = True
+
+if config.target_arch in ['armv7l']:
config.unsupported = True
diff --git a/test/sanitizer_common/TestCases/Linux/clock_gettime.c b/test/sanitizer_common/TestCases/Linux/clock_gettime.c
new file mode 100644
index 0000000..ec1386e
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Linux/clock_gettime.c
@@ -0,0 +1,11 @@
+// RUN: %clang %s -Wl,-as-needed -o %t && %run %t
+// Regression test for PR15823
+// (http://llvm.org/bugs/show_bug.cgi?id=15823).
+#include <stdio.h>
+#include <time.h>
+
+int main() {
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Linux/getpass.cc b/test/sanitizer_common/TestCases/Linux/getpass.cc
new file mode 100644
index 0000000..c9a2276
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Linux/getpass.cc
@@ -0,0 +1,32 @@
+// RUN: %clangxx -O0 -g %s -lutil -o %t && %run %t | FileCheck %s
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <pty.h>
+
+int
+main (int argc, char** argv)
+{
+ int master;
+ int pid = forkpty(&master, NULL, NULL, NULL);
+
+ if(pid == -1) {
+ fprintf(stderr, "forkpty failed\n");
+ return 1;
+ } else if (pid > 0) {
+ char buf[1024];
+ int res = read(master, buf, sizeof(buf));
+ write(1, buf, res);
+ write(master, "password\n", 9);
+ while ((res = read(master, buf, sizeof(buf))) > 0) write(1, buf, res);
+ } else {
+ char *s = getpass("prompt");
+ assert(strcmp(s, "password") == 0);
+ write(1, "done\n", 5);
+ }
+ return 0;
+}
+
+// CHECK: prompt
+// CHECK: done
diff --git a/test/sanitizer_common/TestCases/Linux/mlock_test.cc b/test/sanitizer_common/TestCases/Linux/mlock_test.cc
new file mode 100644
index 0000000..69ea7cb
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Linux/mlock_test.cc
@@ -0,0 +1,13 @@
+// RUN: %clang %s -o %t && %run %t
+// XFAIL: lsan
+
+#include <assert.h>
+#include <sys/mman.h>
+
+int main() {
+ assert(0 == mlockall(MCL_CURRENT));
+ assert(0 == mlock((void *)0x12345, 0x5678));
+ assert(0 == munlockall());
+ assert(0 == munlock((void *)0x987, 0x654));
+}
+
diff --git a/test/sanitizer_common/TestCases/Linux/ptrace.cc b/test/sanitizer_common/TestCases/Linux/ptrace.cc
index 797e7b4..2bf0fd2 100644
--- a/test/sanitizer_common/TestCases/Linux/ptrace.cc
+++ b/test/sanitizer_common/TestCases/Linux/ptrace.cc
@@ -1,5 +1,4 @@
// RUN: %clangxx -O0 %s -o %t && %run %t
-// XFAIL: arm-linux-gnueabi
#include <assert.h>
#include <signal.h>
@@ -18,8 +17,10 @@
execl("/bin/true", "true", NULL);
} else {
wait(NULL);
- user_regs_struct regs;
int res;
+
+#if __x86_64__
+ user_regs_struct regs;
res = ptrace(PTRACE_GETREGS, pid, NULL, ®s);
assert(!res);
if (regs.rip)
@@ -30,6 +31,21 @@
assert(!res);
if (fpregs.mxcsr)
printf("%x\n", fpregs.mxcsr);
+#endif // __x86_64__
+
+#if __powerpc64__
+ struct pt_regs regs;
+ res = ptrace((enum __ptrace_request)PTRACE_GETREGS, pid, NULL, ®s);
+ assert(!res);
+ if (regs.nip)
+ printf("%lx\n", regs.nip);
+
+ elf_fpregset_t fpregs;
+ res = ptrace((enum __ptrace_request)PTRACE_GETFPREGS, pid, NULL, &fpregs);
+ assert(!res);
+ if ((elf_greg_t)fpregs[32]) // fpscr
+ printf("%lx\n", (elf_greg_t)fpregs[32]);
+#endif // __powerpc64__
siginfo_t siginfo;
res = ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo);
diff --git a/test/sanitizer_common/TestCases/Linux/timerfd.cc b/test/sanitizer_common/TestCases/Linux/timerfd.cc
new file mode 100644
index 0000000..e7613bb
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Linux/timerfd.cc
@@ -0,0 +1,52 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t | FileCheck %s
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/timerfd.h>
+#include <unistd.h>
+
+int main (int argc, char** argv)
+{
+ int fd = timerfd_create(CLOCK_REALTIME, 0);
+ assert(fd >= 0);
+
+ struct itimerspec its;
+ its.it_value.tv_sec = 0;
+ its.it_value.tv_nsec = 1000000;
+ its.it_interval.tv_sec = its.it_value.tv_sec;
+ its.it_interval.tv_nsec = its.it_value.tv_nsec;
+
+ int res = timerfd_settime(fd, 0, &its, NULL);
+ assert(res != -1);
+
+ struct itimerspec its2;
+ res = timerfd_settime(fd, 0, &its, &its2);
+ assert(res != -1);
+ assert(its2.it_interval.tv_sec == its.it_interval.tv_sec);
+ assert(its2.it_interval.tv_nsec == its.it_interval.tv_nsec);
+ assert(its2.it_value.tv_sec <= its.it_value.tv_sec);
+ assert(its2.it_value.tv_nsec <= its.it_value.tv_nsec);
+
+ struct itimerspec its3;
+ res = timerfd_gettime(fd, &its3);
+ assert(res != -1);
+ assert(its3.it_interval.tv_sec == its.it_interval.tv_sec);
+ assert(its3.it_interval.tv_nsec == its.it_interval.tv_nsec);
+ assert(its3.it_value.tv_sec <= its.it_value.tv_sec);
+ assert(its3.it_value.tv_nsec <= its.it_value.tv_nsec);
+
+
+ unsigned long long buf;
+ res = read(fd, &buf, sizeof(buf));
+ assert(res == 8);
+ assert(buf >= 1);
+
+ res = close(fd);
+ assert(res != -1);
+
+ printf("DONE\n");
+ // CHECK: DONE
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/corelimit.cc b/test/sanitizer_common/TestCases/corelimit.cc
new file mode 100644
index 0000000..8f54940
--- /dev/null
+++ b/test/sanitizer_common/TestCases/corelimit.cc
@@ -0,0 +1,16 @@
+// RUN: %clangxx -O0 %s -o %t && %run %t
+// XFAIL: lsan
+
+#include <assert.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+int main() {
+ struct rlimit lim_core;
+ getrlimit(RLIMIT_CORE, &lim_core);
+ void *p;
+ if (sizeof(p) == 8) {
+ assert(0 == lim_core.rlim_max);
+ }
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/malloc_hook.cc b/test/sanitizer_common/TestCases/malloc_hook.cc
index 686e098..9702249 100644
--- a/test/sanitizer_common/TestCases/malloc_hook.cc
+++ b/test/sanitizer_common/TestCases/malloc_hook.cc
@@ -1,7 +1,7 @@
// RUN: %clangxx -O2 %s -o %t && %run %t 2>&1 | FileCheck %s
-// Malloc/free hooks are not supported on Windows and doesn't work in LSan.
-// XFAIL: win32, lsan
+// Malloc/free hooks are not supported on Windows.
+// XFAIL: win32
#include <stdlib.h>
#include <unistd.h>
diff --git a/test/sanitizer_common/TestCases/print-stack-trace.cc b/test/sanitizer_common/TestCases/print-stack-trace.cc
index c84d0da..1251f67 100644
--- a/test/sanitizer_common/TestCases/print-stack-trace.cc
+++ b/test/sanitizer_common/TestCases/print-stack-trace.cc
@@ -1,13 +1,11 @@
-// RUN: %clangxx -O0 %s -o %t && %run %t 2>&1 | FileCheck %s
-// RUN: %clangxx -O3 %s -o %t && %run %t 2>&1 | FileCheck %s
-//
-// Not yet implemented for TSan.
-// https://code.google.com/p/address-sanitizer/issues/detail?id=243
-// XFAIL: tsan,lsan
+// RUN: %clangxx -O0 %s -o %t && %tool_options=stack_trace_format=DEFAULT %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx -O3 %s -o %t && %tool_options=stack_trace_format=DEFAULT %run %t 2>&1 | FileCheck %s
+// RUN: %tool_options='stack_trace_format="frame:%n lineno:%l"' %run %t 2>&1 | FileCheck %s --check-prefix=CUSTOM
+// RUN: %tool_options=symbolize_inline_frames=false:stack_trace_format=DEFAULT %run %t 2>&1 | FileCheck %s --check-prefix=NOINLINE
#include <sanitizer/common_interface_defs.h>
-void FooBarBaz() {
+static inline void FooBarBaz() {
__sanitizer_print_stack_trace();
}
@@ -16,5 +14,11 @@
return 0;
}
// CHECK: {{ #0 0x.* in __sanitizer_print_stack_trace}}
-// CHECK: {{ #1 0x.* in FooBarBaz\(\) .*print-stack-trace.cc:11}}
-// CHECK: {{ #2 0x.* in main.*print-stack-trace.cc:15}}
+// CHECK: {{ #1 0x.* in FooBarBaz(\(\))? .*print-stack-trace.cc:9}}
+// CHECK: {{ #2 0x.* in main.*print-stack-trace.cc:13}}
+
+// CUSTOM: frame:1 lineno:9
+// CUSTOM: frame:2 lineno:13
+
+// NOINLINE: #0 0x{{.*}} in __sanitizer_print_stack_trace
+// NOINLINE: #1 0x{{.*}} in main{{.*}}print-stack-trace.cc:9
diff --git a/test/sanitizer_common/lit.common.cfg b/test/sanitizer_common/lit.common.cfg
index 6e768b1..fb37815 100644
--- a/test/sanitizer_common/lit.common.cfg
+++ b/test/sanitizer_common/lit.common.cfg
@@ -7,18 +7,22 @@
if config.tool_name == "asan":
tool_cflags = ["-fsanitize=address"]
+ tool_options = "ASAN_OPTIONS"
elif config.tool_name == "tsan":
tool_cflags = ["-fsanitize=thread"]
+ tool_options = "TSAN_OPTIONS"
elif config.tool_name == "msan":
tool_cflags = ["-fsanitize=memory"]
+ tool_options = "MSAN_OPTIONS"
elif config.tool_name == "lsan":
tool_cflags = ["-fsanitize=leak"]
+ tool_options = "LSAN_OPTIONS"
else:
lit_config.fatal("Unknown tool for sanitizer_common tests: %r" % config.tool_name)
config.available_features.add(config.tool_name)
-clang_cflags = ["-g"] + tool_cflags + [config.target_cflags]
+clang_cflags = config.debug_info_flags + tool_cflags + [config.target_cflags]
clang_cxxflags = config.cxx_mode_flags + clang_cflags
def build_invocation(compile_flags):
@@ -26,9 +30,9 @@
config.substitutions.append( ("%clang ", build_invocation(clang_cflags)) )
config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) )
+config.substitutions.append( ("%tool_options", tool_options) )
config.suffixes = ['.c', '.cc', '.cpp']
if config.host_os not in ['Linux', 'Darwin']:
config.unsupported = True
-
diff --git a/test/tsan/Linux/lit.local.cfg b/test/tsan/Linux/lit.local.cfg
new file mode 100644
index 0000000..57271b8
--- /dev/null
+++ b/test/tsan/Linux/lit.local.cfg
@@ -0,0 +1,9 @@
+def getRoot(config):
+ if not config.parent:
+ return config
+ return getRoot(config.parent)
+
+root = getRoot(config)
+
+if root.host_os not in ['Linux']:
+ config.unsupported = True
diff --git a/test/tsan/mutex_robust.cc b/test/tsan/Linux/mutex_robust.cc
similarity index 100%
rename from test/tsan/mutex_robust.cc
rename to test/tsan/Linux/mutex_robust.cc
diff --git a/test/tsan/mutex_robust2.cc b/test/tsan/Linux/mutex_robust2.cc
similarity index 100%
rename from test/tsan/mutex_robust2.cc
rename to test/tsan/Linux/mutex_robust2.cc
diff --git a/test/tsan/user_fopen.cc b/test/tsan/Linux/user_fopen.cc
similarity index 97%
rename from test/tsan/user_fopen.cc
rename to test/tsan/Linux/user_fopen.cc
index f350a99..c0ff267 100644
--- a/test/tsan/user_fopen.cc
+++ b/test/tsan/Linux/user_fopen.cc
@@ -2,7 +2,7 @@
#include <stdio.h>
#include <stdlib.h>
-// defined by tsan.
+// Defined by tsan.
extern "C" FILE *__interceptor_fopen(const char *file, const char *mode);
extern "C" int __interceptor_fileno(FILE *f);
diff --git a/test/tsan/user_malloc.cc b/test/tsan/Linux/user_malloc.cc
similarity index 96%
rename from test/tsan/user_malloc.cc
rename to test/tsan/Linux/user_malloc.cc
index 2106770..c671bfc 100644
--- a/test/tsan/user_malloc.cc
+++ b/test/tsan/Linux/user_malloc.cc
@@ -1,7 +1,7 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
#include <stdio.h>
-// defined by tsan.
+// Defined by tsan.
extern "C" void *__interceptor_malloc(unsigned long size);
extern "C" void __interceptor_free(void *p);
diff --git a/test/tsan/atexit.cc b/test/tsan/atexit.cc
new file mode 100644
index 0000000..69acb4d
--- /dev/null
+++ b/test/tsan/atexit.cc
@@ -0,0 +1,29 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+class Logger {
+ public:
+ Logger() {
+ fprintf(stderr, "Logger ctor\n");
+ }
+
+ ~Logger() {
+ fprintf(stderr, "Logger dtor\n");
+ }
+};
+
+Logger logger;
+
+void log_from_atexit() {
+ fprintf(stderr, "In log_from_atexit\n");
+}
+
+int main() {
+ atexit(log_from_atexit);
+}
+
+// CHECK: Logger ctor
+// CHECK: In log_from_atexit
+// CHECK: Logger dtor
diff --git a/test/tsan/atexit2.cc b/test/tsan/atexit2.cc
new file mode 100644
index 0000000..6f74c5f
--- /dev/null
+++ b/test/tsan/atexit2.cc
@@ -0,0 +1,26 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int n;
+const int N = 10000;
+
+static void atexit1() {
+ n++;
+}
+
+static void atexit0() {
+ fprintf(stderr, "run count: %d\n", n);
+}
+
+int main() {
+ atexit(atexit0);
+ for (int i = 0; i < N; i++)
+ atexit(atexit1);
+}
+
+// CHECK-NOT: FATAL: ThreadSanitizer
+// CHECK-NOT: WARNING: ThreadSanitizer
+// CHECK: run count: 10000
+
diff --git a/test/tsan/bench_shadow_flush.cc b/test/tsan/bench_shadow_flush.cc
index cdad477..0f412bb 100644
--- a/test/tsan/bench_shadow_flush.cc
+++ b/test/tsan/bench_shadow_flush.cc
@@ -11,6 +11,7 @@
const long kSmallPage = 4 << 10;
const long kLargePage = 2 << 20;
+const long kStride = 1 << 10;
typedef unsigned long uptr;
@@ -24,15 +25,20 @@
int niter = 1;
if (argc > 3)
niter = atoi(argv[3]);
+ int stride2 = 1;
+ if (argc > 4)
+ stride2 = atoi(argv[4]);
- void *p = mmap(0, mem_size + kLargePage, PROT_READ | PROT_WRITE,
- MAP_ANON | MAP_PRIVATE, -1, 0);
+ uptr sz = mem_size + stride2 * kStride + kLargePage;
+ void *p = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
uptr a = ((uptr)p + kLargePage - 1) & ~(kLargePage - 1);
volatile char *mem = (volatile char *)a;
for (int i = 0; i < niter; i++) {
- for (uptr off = 0; off < mem_size; off += stride)
- mem[off] = 42;
+ for (uptr off = 0; off < mem_size; off += stride) {
+ for (uptr off2 = 0; off2 < stride2; off2++)
+ mem[off + off2 * kStride] = 42;
+ }
}
fprintf(stderr, "DONE\n");
diff --git a/test/tsan/blacklist2.cc b/test/tsan/blacklist2.cc
index 1258208..1092561 100644
--- a/test/tsan/blacklist2.cc
+++ b/test/tsan/blacklist2.cc
@@ -22,7 +22,7 @@
void TouchGlobal() {
// CHECK: Previous write of size 4
- // CHECK: #0 TouchGlobal(){{.*}}blacklist2.cc:[[@LINE+1]]
+ // CHECK: #0 TouchGlobal{{.*}}blacklist2.cc:[[@LINE+1]]
Global--;
}
diff --git a/test/tsan/dlclose.cc b/test/tsan/dlclose.cc
new file mode 100644
index 0000000..1a93fe6
--- /dev/null
+++ b/test/tsan/dlclose.cc
@@ -0,0 +1,58 @@
+// RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// If we mention TSAN_OPTIONS, the test won't run from test_output.sh script.
+
+// Test case for
+// https://code.google.com/p/thread-sanitizer/issues/detail?id=80
+
+#ifdef BUILD_SO
+
+#include <stdio.h>
+
+extern "C"
+void sofunc() {
+ fprintf(stderr, "HELLO FROM SO\n");
+}
+
+#else // BUILD_SO
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <string>
+
+void *lib;
+void *lib2;
+
+struct Closer {
+ ~Closer() {
+ dlclose(lib);
+ fprintf(stderr, "CLOSED SO\n");
+ }
+};
+static Closer c;
+
+int main(int argc, char *argv[]) {
+ lib = dlopen((std::string(argv[0]) + std::string("-so.so")).c_str(),
+ RTLD_NOW|RTLD_NODELETE);
+ if (lib == 0) {
+ printf("error in dlopen: %s\n", dlerror());
+ return 1;
+ }
+ void *f = dlsym(lib, "sofunc");
+ if (f == 0) {
+ printf("error in dlsym: %s\n", dlerror());
+ return 1;
+ }
+ ((void(*)())f)();
+ return 0;
+}
+
+#endif // BUILD_SO
+
+// CHECK: HELLO FROM SO
+// CHECK-NOT: Inconsistency detected by ld.so
+// CHECK: CLOSED SO
+
diff --git a/test/tsan/getline_nohang.cc b/test/tsan/getline_nohang.cc
index ce9d358..89afbe1 100644
--- a/test/tsan/getline_nohang.cc
+++ b/test/tsan/getline_nohang.cc
@@ -2,6 +2,10 @@
// Make sure TSan doesn't deadlock on a file stream lock at program shutdown.
// See https://code.google.com/p/thread-sanitizer/issues/detail?id=47
+#ifdef __FreeBSD__
+#define _WITH_GETLINE // to declare getline()
+#endif
+
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
diff --git a/test/tsan/global_race.cc b/test/tsan/global_race.cc
index 224ab22..e12bb1d 100644
--- a/test/tsan/global_race.cc
+++ b/test/tsan/global_race.cc
@@ -1,4 +1,4 @@
-// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+// RUN: %clangxx_tsan -O1 %s -o %T/global_race.cc.exe && %deflake %run %T/global_race.cc.exe | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <stddef.h>
@@ -13,7 +13,9 @@
}
int main() {
- fprintf(stderr, "addr=%p\n", GlobalData);
+ // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not
+ // match to the format used in the diagnotic message.
+ fprintf(stderr, "addr=0x%012lx\n", (unsigned long) GlobalData);
pthread_t t;
pthread_create(&t, 0, Thread, 0);
GlobalData[2] = 43;
@@ -22,5 +24,5 @@
// CHECK: addr=[[ADDR: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: Location is global 'GlobalData' of size 40 at [[ADDR]] (global_race.cc.exe+0x{{[0-9,a-f]+}})
diff --git a/test/tsan/global_race2.cc b/test/tsan/global_race2.cc
index b8352ba..ac994cc 100644
--- a/test/tsan/global_race2.cc
+++ b/test/tsan/global_race2.cc
@@ -13,7 +13,9 @@
}
int main() {
- fprintf(stderr, "addr2=%p\n", &x);
+ // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not
+ // match to the format used in the diagnotic message.
+ fprintf(stderr, "addr2=0x%012lx\n", (unsigned long) &x);
pthread_t t;
pthread_create(&t, 0, Thread, 0);
x = 0;
diff --git a/test/tsan/global_race3.cc b/test/tsan/global_race3.cc
index e37bf78..a3222bb 100644
--- a/test/tsan/global_race3.cc
+++ b/test/tsan/global_race3.cc
@@ -18,7 +18,9 @@
}
int main() {
- fprintf(stderr, "addr3=%p\n", XXX::YYY::ZZZ);
+ // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not
+ // match to the format used in the diagnotic message.
+ fprintf(stderr, "addr3=0x%012lx\n", (unsigned long) XXX::YYY::ZZZ);
pthread_t t;
pthread_create(&t, 0, Thread, 0);
XXX::YYY::ZZZ[0] = 0;
diff --git a/test/tsan/lit.cfg b/test/tsan/lit.cfg
index f7051b3..d27500f 100644
--- a/test/tsan/lit.cfg
+++ b/test/tsan/lit.cfg
@@ -29,11 +29,9 @@
extra_cflags = []
# Setup default compiler flags used with -fsanitize=thread option.
-# FIXME: Review the set of required flags and check if it can be reduced.
clang_tsan_cflags = ["-fsanitize=thread",
- "-g",
"-Wall",
- "-m64"] + extra_cflags
+ "-m64"] + config.debug_info_flags + extra_cflags
clang_tsan_cxxflags = config.cxx_mode_flags + clang_tsan_cflags
# Add additional flags if we're using instrumented libc++.
if config.has_libcxx:
@@ -62,6 +60,6 @@
# Default test suffixes.
config.suffixes = ['.c', '.cc', '.cpp']
-# ThreadSanitizer tests are currently supported on Linux only.
-if config.host_os not in ['Linux']:
+# ThreadSanitizer tests are currently supported on FreeBSD and Linux only.
+if config.host_os not in ['FreeBSD', 'Linux']:
config.unsupported = True
diff --git a/test/tsan/map32bit.cc b/test/tsan/map32bit.cc
new file mode 100644
index 0000000..3a76fa2
--- /dev/null
+++ b/test/tsan/map32bit.cc
@@ -0,0 +1,41 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+// Test for issue:
+// https://code.google.com/p/thread-sanitizer/issues/detail?id=5
+
+void *Thread(void *ptr) {
+ *(int*)ptr = 42;
+ return 0;
+}
+
+int main() {
+ void *ptr = mmap(0, 128 << 10, PROT_READ|PROT_WRITE,
+ MAP_32BIT|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ fprintf(stderr, "ptr=%p\n", ptr);
+ if (ptr == MAP_FAILED) {
+ fprintf(stderr, "mmap failed: %d\n", errno);
+ return 1;
+ }
+ if ((uintptr_t)ptr >= (1ull << 32)) {
+ fprintf(stderr, "ptr is too high\n");
+ return 1;
+ }
+ pthread_t t;
+ pthread_create(&t, 0, Thread, ptr);
+ sleep(1);
+ *(int*)ptr = 42;
+ pthread_join(t, 0);
+ munmap(ptr, 128 << 10);
+ fprintf(stderr, "DONE\n");
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
+
diff --git a/test/tsan/must_deadlock.cc b/test/tsan/must_deadlock.cc
new file mode 100644
index 0000000..1409800
--- /dev/null
+++ b/test/tsan/must_deadlock.cc
@@ -0,0 +1,50 @@
+// Test that the deadlock detector can find a deadlock that actually happened.
+// Currently we will fail to report such a deadlock because we check for
+// cycles in lock-order graph after pthread_mutex_lock.
+
+// RUN: %clangxx_tsan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+// XFAIL: *
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+pthread_mutex_t mu1, mu2;
+pthread_barrier_t barrier;
+
+void *Thread(void *p) {
+ // mu2 => mu1
+ pthread_mutex_lock(&mu2);
+ pthread_barrier_wait(&barrier);
+ pthread_mutex_lock(&mu1);
+ // CHECK: ThreadSanitizer: lock-order-inversion (potential deadlock)
+ pthread_mutex_unlock(&mu1);
+ pthread_mutex_unlock(&mu2);
+ return p;
+}
+
+int main() {
+ pthread_mutex_init(&mu1, NULL);
+ pthread_mutex_init(&mu2, NULL);
+ pthread_barrier_init(&barrier, 0, 2);
+
+ fprintf(stderr, "This test is going to deadlock and die in 3 seconds\n");
+ alarm(3);
+
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+
+ // mu1 => mu2
+ pthread_mutex_lock(&mu1);
+ pthread_barrier_wait(&barrier);
+ pthread_mutex_lock(&mu2);
+ pthread_mutex_unlock(&mu2);
+ pthread_mutex_unlock(&mu1);
+
+ pthread_join(t, 0);
+
+ pthread_mutex_destroy(&mu1);
+ pthread_mutex_destroy(&mu2);
+ pthread_barrier_destroy(&barrier);
+ fprintf(stderr, "FAILED\n");
+}
diff --git a/test/tsan/pthread_atfork_deadlock.c b/test/tsan/pthread_atfork_deadlock.c
index 965de05..0f33b90 100644
--- a/test/tsan/pthread_atfork_deadlock.c
+++ b/test/tsan/pthread_atfork_deadlock.c
@@ -1,4 +1,4 @@
-// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+// RUN: %clang_tsan -O1 %s -lpthread -o %t && %deflake %run %t | FileCheck %s
// Regression test for
// https://code.google.com/p/thread-sanitizer/issues/detail?id=61
// When the data race was reported, pthread_atfork() handler used to be
diff --git a/test/tsan/signal_errno.cc b/test/tsan/signal_errno.cc
index 27d4ecd..1fa20f3 100644
--- a/test/tsan/signal_errno.cc
+++ b/test/tsan/signal_errno.cc
@@ -43,7 +43,7 @@
}
// CHECK: WARNING: ThreadSanitizer: signal handler spoils errno
-// CHECK: #0 MyHandler(int, siginfo{{(_t)?}}*, void*) {{.*}}signal_errno.cc
+// CHECK: #0 MyHandler(int, {{(__)?}}siginfo{{(_t)?}}*, void*) {{.*}}signal_errno.cc
// CHECK: main
// CHECK: SUMMARY: ThreadSanitizer: signal handler spoils errno{{.*}}MyHandler
diff --git a/test/tsan/signal_longjmp.cc b/test/tsan/signal_longjmp.cc
new file mode 100644
index 0000000..84b0682
--- /dev/null
+++ b/test/tsan/signal_longjmp.cc
@@ -0,0 +1,69 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// Test case for longjumping out of signal handler:
+// https://code.google.com/p/thread-sanitizer/issues/detail?id=75
+
+#include <setjmp.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/mman.h>
+
+sigjmp_buf fault_jmp;
+volatile int fault_expected;
+
+void sigfault_handler(int sig) {
+ if (!fault_expected)
+ abort();
+
+ /* just return from sighandler to proper place */
+ fault_expected = 0;
+ siglongjmp(fault_jmp, 1);
+}
+
+#define MUST_FAULT(code) do { \
+ fault_expected = 1; \
+ if (!sigsetjmp(fault_jmp, 1)) { \
+ code; /* should pagefault -> sihandler does longjmp */ \
+ fprintf(stderr, "%s not faulted\n", #code); \
+ abort(); \
+ } else { \
+ fprintf(stderr, "%s faulted ok\n", #code); \
+ } \
+} while (0)
+
+int main() {
+ struct sigaction act;
+ act.sa_handler = sigfault_handler;
+ act.sa_flags = 0;
+ if (sigemptyset(&act.sa_mask)) {
+ perror("sigemptyset");
+ exit(1);
+ }
+
+ if (sigaction(SIGSEGV, &act, NULL)) {
+ perror("sigaction");
+ exit(1);
+ }
+
+ void *mem = mmap(0, 4096, PROT_NONE, MAP_PRIVATE | MAP_ANON,
+ -1, 0);
+
+ MUST_FAULT(((volatile int *volatile)mem)[0] = 0);
+ MUST_FAULT(((volatile int *volatile)mem)[1] = 1);
+ MUST_FAULT(((volatile int *volatile)mem)[3] = 1);
+
+ // Ensure that tsan does not think that we are
+ // in a signal handler.
+ void *volatile p = malloc(10);
+ ((volatile int*)p)[1] = 1;
+ free((void*)p);
+
+ munmap(p, 4096);
+
+ fprintf(stderr, "DONE\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer
+// CHECK: DONE
diff --git a/test/tsan/signal_malloc.cc b/test/tsan/signal_malloc.cc
index db5e79b..06932fb 100644
--- a/test/tsan/signal_malloc.cc
+++ b/test/tsan/signal_malloc.cc
@@ -8,7 +8,7 @@
static void handler(int, siginfo_t*, void*) {
// CHECK: WARNING: ThreadSanitizer: signal-unsafe call inside of a signal
// CHECK: #0 malloc
- // CHECK: #{{(1|2)}} handler(int, siginfo{{(_t)?}}*, void*) {{.*}}signal_malloc.cc:[[@LINE+2]]
+ // CHECK: #{{(1|2)}} handler(int, {{(__)?}}siginfo{{(_t)?}}*, void*) {{.*}}signal_malloc.cc:[[@LINE+2]]
// CHECK: SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal{{.*}}handler
volatile char *p = (char*)malloc(1);
p[0] = 0;
diff --git a/test/tsan/signal_recursive.cc b/test/tsan/signal_recursive.cc
new file mode 100644
index 0000000..d92ba97
--- /dev/null
+++ b/test/tsan/signal_recursive.cc
@@ -0,0 +1,131 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// Test case for recursive signal handlers, adopted from:
+// https://code.google.com/p/thread-sanitizer/issues/detail?id=71
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static const int kSigSuspend = SIGUSR1;
+static const int kSigRestart = SIGUSR2;
+static sigset_t g_suspend_handler_mask;
+
+static sem_t g_thread_suspend_ack_sem;
+
+static bool g_busy_thread_received_restart;
+
+static volatile bool g_busy_thread_garbage_collected;
+
+static void SaveRegistersInStack() {
+ // Mono walks thread stacks to detect unreferenced objects.
+ // If last object reference is kept in register the object will be collected
+ // This is why threads can't be suspended with something like pthread_suspend
+};
+
+static void fail(const char *what) {
+ fprintf(stderr, "FAILED: %s (errno=%d)\n", what, errno);
+ exit(1);
+}
+
+static void SuspendHandler(int sig) {
+ int old_errno = errno;
+ SaveRegistersInStack();
+ // Acknowledge that thread is saved and suspended
+ if (sem_post(&g_thread_suspend_ack_sem) != 0)
+ fail("sem_post failed");
+
+ do {
+ g_busy_thread_received_restart = false;
+ if (sigsuspend(&g_suspend_handler_mask) != -1 || errno != EINTR)
+ fail("sigsuspend failed");
+ } while (!g_busy_thread_received_restart);
+
+ // Acknowledge that thread restarted
+ if (sem_post(&g_thread_suspend_ack_sem) != 0)
+ fail("sem_post failed");
+
+ g_busy_thread_garbage_collected = true;
+
+ errno = old_errno;
+}
+
+static void RestartHandler(int sig) {
+ g_busy_thread_received_restart = true;
+}
+
+static void StopWorld(pthread_t thread) {
+ int result = pthread_kill(thread, kSigSuspend);
+ if (result != 0)
+ fail("pthread_kill failed");
+
+ while ((result = sem_wait(&g_thread_suspend_ack_sem)) != 0) {
+ if (result != EINTR) {
+ fail("sem_wait failed");
+ }
+ }
+}
+
+static void StartWorld(pthread_t thread) {
+ int result = pthread_kill(thread, kSigRestart);
+ if (result != 0)
+ fail("pthread_kill failed");
+
+ while ((result = sem_wait(&g_thread_suspend_ack_sem)) != 0) {
+ if (result != EINTR) {
+ fail("sem_wait failed");
+ }
+ }
+}
+
+static void CollectGarbage(pthread_t thread) {
+ StopWorld(thread);
+ // Walk stacks
+ StartWorld(thread);
+}
+
+static void Init() {
+ if (sigfillset(&g_suspend_handler_mask) != 0)
+ fail("sigfillset failed");
+ if (sigdelset(&g_suspend_handler_mask, kSigRestart) != 0)
+ fail("sigdelset failed");
+ if (sem_init(&g_thread_suspend_ack_sem, 0, 0) != 0)
+ fail("sem_init failed");
+
+ struct sigaction act = {};
+ act.sa_flags = SA_RESTART;
+ sigfillset(&act.sa_mask);
+ act.sa_handler = &SuspendHandler;
+ if (sigaction(kSigSuspend, &act, NULL) != 0)
+ fail("sigaction failed");
+ act.sa_handler = &RestartHandler;
+ if (sigaction(kSigRestart, &act, NULL) != 0)
+ fail("sigaction failed");
+}
+
+void* BusyThread(void *arg) {
+ (void)arg;
+ while (!g_busy_thread_garbage_collected) {
+ usleep(100); // Tsan deadlocks without these sleeps
+ }
+ return NULL;
+}
+
+int main(int argc, const char *argv[]) {
+ Init();
+ pthread_t busy_thread;
+ pthread_create(&busy_thread, NULL, &BusyThread, NULL);
+ CollectGarbage(busy_thread);
+ pthread_join(busy_thread, 0);
+ fprintf(stderr, "DONE\n");
+ return 0;
+}
+
+// CHECK-NOT: FAILED
+// CHECK-NOT: ThreadSanitizer CHECK failed
+// CHECK-NOT: WARNING: ThreadSanitizer:
+// CHECK: DONE
diff --git a/test/tsan/signal_sync.cc b/test/tsan/signal_sync.cc
new file mode 100644
index 0000000..15387b7
--- /dev/null
+++ b/test/tsan/signal_sync.cc
@@ -0,0 +1,58 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <errno.h>
+
+volatile int X;
+
+static void handler(int sig) {
+ (void)sig;
+ if (X != 42)
+ printf("bad");
+}
+
+static void* thr(void *p) {
+ for (int i = 0; i != 1000; i++)
+ usleep(1000);
+ return 0;
+}
+
+int main() {
+ const int kThreads = 10;
+ pthread_t th[kThreads];
+ for (int i = 0; i < kThreads; i++)
+ pthread_create(&th[i], 0, thr, 0);
+
+ X = 42;
+
+ struct sigaction act = {};
+ act.sa_handler = &handler;
+ if (sigaction(SIGPROF, &act, 0)) {
+ perror("sigaction");
+ exit(1);
+ }
+
+ itimerval t;
+ t.it_value.tv_sec = 0;
+ t.it_value.tv_usec = 10;
+ t.it_interval = t.it_value;
+ if (setitimer(ITIMER_PROF, &t, 0)) {
+ perror("setitimer");
+ exit(1);
+ }
+
+ for (int i = 0; i < kThreads; i++)
+ pthread_join(th[i], 0);
+
+ fprintf(stderr, "DONE\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer:
+// CHECK: DONE
+// CHECK-NOT: WARNING: ThreadSanitizer:
diff --git a/test/tsan/signal_write.cc b/test/tsan/signal_write.cc
new file mode 100644
index 0000000..626d87a
--- /dev/null
+++ b/test/tsan/signal_write.cc
@@ -0,0 +1,27 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static void handler(int, siginfo_t*, void*) {
+ const char *str = "HELLO FROM SIGNAL\n";
+ write(2, str, strlen(str));
+}
+
+int main() {
+ struct sigaction act = {};
+ act.sa_sigaction = &handler;
+ sigaction(SIGPROF, &act, 0);
+ kill(getpid(), SIGPROF);
+ sleep(1);
+ fprintf(stderr, "DONE\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer
+// CHECK: HELLO FROM SIGNAL
+// CHECK: DONE
+
diff --git a/test/tsan/sigsuspend.cc b/test/tsan/sigsuspend.cc
index f614c12..a5930d4 100644
--- a/test/tsan/sigsuspend.cc
+++ b/test/tsan/sigsuspend.cc
@@ -1,4 +1,4 @@
-// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
// Always enable asserts.
#ifdef NDEBUG
@@ -37,7 +37,7 @@
// Restore the original set.
assert(0 == sigprocmask(SIG_SETMASK, &old_set, NULL));
- printf("DONE");
+ printf("DONE\n");
}
// CHECK: HANDLER
diff --git a/test/tsan/simple_stack.c b/test/tsan/simple_stack.c
index 899277f..8736703 100644
--- a/test/tsan/simple_stack.c
+++ b/test/tsan/simple_stack.c
@@ -53,7 +53,7 @@
// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack.c:14{{(:3)?}} ({{.*}})
// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack.c:28{{(:3)?}} ({{.*}})
// CHECK: Previous read of size 4 at {{.*}} by thread T2:
-// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack.c:18{{(:26)?}} ({{.*}})
+// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack.c:18{{(:3)?}} ({{.*}})
// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack.c:23{{(:3)?}} ({{.*}})
// CHECK-NEXT: #2 Thread2{{.*}} {{.*}}simple_stack.c:33{{(:3)?}} ({{.*}})
// CHECK: Thread T1 (tid={{.*}}, running) created by main thread at:
diff --git a/test/tsan/simple_stack2.cc b/test/tsan/simple_stack2.cc
index ba0303c..b07d863 100644
--- a/test/tsan/simple_stack2.cc
+++ b/test/tsan/simple_stack2.cc
@@ -1,4 +1,4 @@
-// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+// RUN: %clangxx_tsan -O1 %s -o %T/simple_stack2.cc.exe && %deflake %run %T/simple_stack2.cc.exe | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
@@ -44,10 +44,10 @@
// CHECK: WARNING: ThreadSanitizer: data race
// CHECK-NEXT: Write of size 4 at {{.*}} by thread T1:
-// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack2.cc:9{{(:3)?}} ({{.*}})
-// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack2.cc:16{{(:3)?}} ({{.*}})
-// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack2.cc:9{{(:3)?}} (simple_stack2.cc.exe+{{.*}})
+// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack2.cc:16{{(:3)?}} (simple_stack2.cc.exe+{{.*}})
+// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} (simple_stack2.cc.exe+{{.*}})
// CHECK: Previous read of size 4 at {{.*}} by main thread:
-// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack2.cc:20{{(:28)?}} ({{.*}})
-// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack2.cc:29{{(:3)?}} ({{.*}})
-// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack2.cc:41{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack2.cc:20{{(:3)?}} (simple_stack2.cc.exe+{{.*}})
+// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack2.cc:29{{(:3)?}} (simple_stack2.cc.exe+{{.*}})
+// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack2.cc:41{{(:3)?}} (simple_stack2.cc.exe+{{.*}})
diff --git a/test/tsan/sunrpc.cc b/test/tsan/sunrpc.cc
index 579eb72..579816d 100644
--- a/test/tsan/sunrpc.cc
+++ b/test/tsan/sunrpc.cc
@@ -1,6 +1,7 @@
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
#include <pthread.h>
+#include <rpc/types.h>
#include <rpc/xdr.h>
#include <stdio.h>
diff --git a/test/tsan/test_output.sh b/test/tsan/test_output.sh
index 8b286f4..bce0fe8 100755
--- a/test/tsan/test_output.sh
+++ b/test/tsan/test_output.sh
@@ -12,8 +12,8 @@
: ${FILECHECK:=FileCheck}
# TODO: add testing for all of -O0...-O3
-CFLAGS="-fsanitize=thread -fPIE -O1 -g -Wall"
-LDFLAGS="-pie -pthread -ldl -lrt -lm -Wl,--whole-archive $TSAN_DIR/rtl/libtsan.a -Wl,--no-whole-archive"
+CFLAGS="-fsanitize=thread -O2 -g -Wall"
+LDFLAGS="-pthread -ldl -lrt -lm -Wl,--whole-archive $TSAN_DIR/rtl/libtsan.a -Wl,--no-whole-archive"
test_file() {
SRC=$1
@@ -48,6 +48,10 @@
echo SKIPPING $c -- requires TSAN_OPTIONS
continue
fi
+ if [ "`grep "XFAIL" $c`" ]; then
+ echo SKIPPING $c -- has XFAIL
+ continue
+ fi
COMPILER=$CXX
case $c in
*.c) COMPILER=$CC
diff --git a/test/tsan/thread_detach.c b/test/tsan/thread_detach.c
new file mode 100644
index 0000000..32cf641
--- /dev/null
+++ b/test/tsan/thread_detach.c
@@ -0,0 +1,20 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void *Thread(void *x) {
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ sleep(1);
+ pthread_detach(t);
+ printf("PASS\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
+// CHECK: PASS
diff --git a/test/tsan/thread_name.cc b/test/tsan/thread_name.cc
index 05b0a35..a790c66 100644
--- a/test/tsan/thread_name.cc
+++ b/test/tsan/thread_name.cc
@@ -3,6 +3,16 @@
#include <stdio.h>
#include <unistd.h>
+#if defined(__linux__)
+#define USE_PTHREAD_SETNAME_NP __GLIBC_PREREQ(2, 12)
+#elif defined(__FreeBSD__)
+#include <pthread_np.h>
+#define USE_PTHREAD_SETNAME_NP 1
+#define pthread_setname_np pthread_set_name_np
+#else
+#define USE_PTHREAD_SETNAME_NP 0
+#endif
+
extern "C" void AnnotateThreadName(const char *f, int l, const char *name);
int Global;
@@ -15,7 +25,7 @@
}
void *Thread2(void *x) {
-#if SANITIZER_LINUX && __GLIBC_PREREQ(2, 12)
+#if USE_PTHREAD_SETNAME_NP
pthread_setname_np(pthread_self(), "Thread2");
#else
AnnotateThreadName(__FILE__, __LINE__, "Thread2");
@@ -35,4 +45,3 @@
// CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Thread T1 'Thread1'
// CHECK: Thread T2 'Thread2'
-
diff --git a/test/tsan/thread_name2.cc b/test/tsan/thread_name2.cc
index b9a5746..6a3dafe 100644
--- a/test/tsan/thread_name2.cc
+++ b/test/tsan/thread_name2.cc
@@ -3,6 +3,11 @@
#include <stdio.h>
#include <unistd.h>
+#if defined(__FreeBSD__)
+#include <pthread_np.h>
+#define pthread_setname_np pthread_set_name_np
+#endif
+
int Global;
void *Thread1(void *x) {
@@ -29,4 +34,3 @@
// CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Thread T1 'foobar1'
// CHECK: Thread T2 'foobar2'
-
diff --git a/test/tsan/vptr_harmful_race4.cc b/test/tsan/vptr_harmful_race4.cc
new file mode 100644
index 0000000..969c9d5
--- /dev/null
+++ b/test/tsan/vptr_harmful_race4.cc
@@ -0,0 +1,34 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+struct A {
+ virtual void F() {
+ }
+
+ virtual ~A() {
+ }
+};
+
+struct B : A {
+ virtual void F() {
+ }
+};
+
+void *Thread(void *x) {
+ sleep(1);
+ ((A*)x)->F();
+ return 0;
+}
+
+int main() {
+ A *obj = new B;
+ pthread_t t;
+ pthread_create(&t, 0, Thread, obj);
+ delete obj;
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: heap-use-after-free (virtual call vs free)
+
diff --git a/test/ubsan/CMakeLists.txt b/test/ubsan/CMakeLists.txt
index 4c6b0bc..1c0c929 100644
--- a/test/ubsan/CMakeLists.txt
+++ b/test/ubsan/CMakeLists.txt
@@ -6,7 +6,7 @@
${CMAKE_CURRENT_BINARY_DIR}/UbsanConfig/lit.site.cfg)
set(UBSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/UbsanConfig)
-if(ASAN_SUPPORTED_ARCH)
+if(COMPILER_RT_HAS_ASAN)
set(UBSAN_LIT_TEST_MODE "AddressSanitizer")
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
diff --git a/test/ubsan/TestCases/Float/cast-overflow.cpp b/test/ubsan/TestCases/Float/cast-overflow.cpp
index cc6f9ad..22991e0 100644
--- a/test/ubsan/TestCases/Float/cast-overflow.cpp
+++ b/test/ubsan/TestCases/Float/cast-overflow.cpp
@@ -1,3 +1,4 @@
+// FIXME: run this (and other) UBSan tests in both 32- and 64-bit modes (?).
// RUN: %clangxx -fsanitize=float-cast-overflow %s -o %t
// RUN: %run %t _
// RUN: %run %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0
@@ -8,25 +9,28 @@
// RUN: %run %t 5 2>&1 | FileCheck %s --check-prefix=CHECK-5
// RUN: %run %t 6 2>&1 | FileCheck %s --check-prefix=CHECK-6
// FIXME: %run %t 7 2>&1 | FileCheck %s --check-prefix=CHECK-7
-// RUN: %run %t 8 2>&1 | FileCheck %s --check-prefix=CHECK-8
-// RUN: %run %t 9 2>&1 | FileCheck %s --check-prefix=CHECK-9
-
-// FIXME: run all ubsan tests in 32- and 64-bit modes (?).
-// FIXME: %clangxx -fsanitize=float-cast-overflow -m32 %s -o %t
-// FIXME: %run %t _
-// FIXME: %run %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0
-// FIXME: %run %t 1 2>&1 | FileCheck %s --check-prefix=CHECK-1
-// FIXME: %run %t 2 2>&1 | FileCheck %s --check-prefix=CHECK-2
-// FIXME: %run %t 3 2>&1 | FileCheck %s --check-prefix=CHECK-3
-// FIXME: %run %t 4 2>&1 | FileCheck %s --check-prefix=CHECK-4
-// FIXME: %run %t 5 2>&1 | FileCheck %s --check-prefix=CHECK-5
-// FIXME: %run %t 6 2>&1 | FileCheck %s --check-prefix=CHECK-6
-// FIXME: %run %t 7 2>&1 | FileCheck %s --check-prefix=CHECK-7
-// FIXME: %run %t 8 2>&1 | FileCheck %s --check-prefix=CHECK-8
-// FIXME: %run %t 9 2>&1 | FileCheck %s --check-prefix=CHECK-9
+// FIXME: not %run %t 8 2>&1 | FileCheck %s --check-prefix=CHECK-8
+// RUN: not %run %t 9 2>&1 | FileCheck %s --check-prefix=CHECK-9
// This test assumes float and double are IEEE-754 single- and double-precision.
+// XFAIL: armv7l-unknown-linux-gnueabihf
+#if defined(__APPLE__)
+# include <machine/endian.h>
+# define BYTE_ORDER __DARWIN_BYTE_ORDER
+# define BIG_ENDIAN __DARWIN_BIG_ENDIAN
+# define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN
+#elif defined(__FreeBSD__)
+# include <sys/endian.h>
+# define BYTE_ORDER _BYTE_ORDER
+# define BIG_ENDIAN _BIG_ENDIAN
+# define LITTLE_ENDIAN _LITTLE_ENDIAN
+#else
+# include <endian.h>
+# define BYTE_ORDER __BYTE_ORDER
+# define BIG_ENDIAN __BIG_ENDIAN
+# define LITTLE_ENDIAN __LITTLE_ENDIAN
+#endif // __APPLE__
#include <stdint.h>
#include <stdio.h>
#include <string.h>
@@ -54,12 +58,20 @@
unsigned Zero = NearlyMinusOne; // ok
// Build a '+Inf'.
+#if BYTE_ORDER == LITTLE_ENDIAN
char InfVal[] = { 0x00, 0x00, 0x80, 0x7f };
+#else
+ char InfVal[] = { 0x7f, 0x80, 0x00, 0x00 };
+#endif
float Inf;
memcpy(&Inf, InfVal, 4);
// Build a 'NaN'.
+#if BYTE_ORDER == LITTLE_ENDIAN
char NaNVal[] = { 0x01, 0x00, 0x80, 0x7f };
+#else
+ char NaNVal[] = { 0x7f, 0x80, 0x00, 0x01 };
+#endif
float NaN;
memcpy(&NaN, NaNVal, 4);
@@ -77,9 +89,12 @@
case '1':
// CHECK-1: runtime error: value -2.14748{{.*}} is outside the range of representable values of type 'int'
return MinFloatRepresentableAsInt - 0x100;
- case '2':
+ case '2': {
// CHECK-2: runtime error: value -1 is outside the range of representable values of type 'unsigned int'
- return (unsigned)-1.0;
+ volatile float f = -1.0;
+ volatile unsigned u = (unsigned)f;
+ return 0;
+ }
case '3':
// CHECK-3: runtime error: value 4.2949{{.*}} is outside the range of representable values of type 'unsigned int'
return (unsigned)(MaxFloatRepresentableAsUInt + 0x100);
diff --git a/test/ubsan/TestCases/Integer/no-recover.cpp b/test/ubsan/TestCases/Integer/no-recover.cpp
index 90dfd2e..575bd0a 100644
--- a/test/ubsan/TestCases/Integer/no-recover.cpp
+++ b/test/ubsan/TestCases/Integer/no-recover.cpp
@@ -1,6 +1,6 @@
// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER
// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fsanitize-recover %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER
-// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fno-sanitize-recover %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=ABORT
+// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fno-sanitize-recover %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=ABORT
#include <stdint.h>
diff --git a/test/ubsan/TestCases/Integer/summary.cpp b/test/ubsan/TestCases/Integer/summary.cpp
new file mode 100644
index 0000000..6e9aec6
--- /dev/null
+++ b/test/ubsan/TestCases/Integer/summary.cpp
@@ -0,0 +1,10 @@
+// RUN: %clangxx -fsanitize=integer %s -o %t && %t 2>&1 | FileCheck %s
+// REQUIRES: ubsan-asan
+
+#include <stdint.h>
+
+int main() {
+ (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull));
+ // CHECK: SUMMARY: AddressSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Misc/bool.cpp b/test/ubsan/TestCases/Misc/bool.cpp
index 5fe5cf1..37ecea2 100644
--- a/test/ubsan/TestCases/Misc/bool.cpp
+++ b/test/ubsan/TestCases/Misc/bool.cpp
@@ -1,4 +1,4 @@
-// RUN: %clangxx -fsanitize=bool %s -O3 -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx -fsanitize=bool %s -O3 -o %t && not %run %t 2>&1 | FileCheck %s
unsigned char NotABool = 123;
diff --git a/test/ubsan/TestCases/Misc/bounds.cpp b/test/ubsan/TestCases/Misc/bounds.cpp
index ea59b02..ffcac52 100644
--- a/test/ubsan/TestCases/Misc/bounds.cpp
+++ b/test/ubsan/TestCases/Misc/bounds.cpp
@@ -1,7 +1,7 @@
// RUN: %clangxx -fsanitize=bounds %s -O3 -o %t
// RUN: %run %t 0 0 0
// RUN: %run %t 1 2 3
-// RUN: %run %t 2 0 0 2>&1 | FileCheck %s --check-prefix=CHECK-A-2
+// RUN: not --crash %run %t 2 0 0 2>&1 | FileCheck %s --check-prefix=CHECK-A-2
// RUN: %run %t 0 3 0 2>&1 | FileCheck %s --check-prefix=CHECK-B-3
// RUN: %run %t 0 0 4 2>&1 | FileCheck %s --check-prefix=CHECK-C-4
@@ -9,7 +9,7 @@
int arr[2][3][4] = {};
return arr[argv[1][0] - '0'][argv[2][0] - '0'][argv[3][0] - '0'];
- // CHECK-A-2: bounds.cpp:11:10: runtime error: index 2 out of bounds for type 'int [2][3][4]'
- // CHECK-B-3: bounds.cpp:11:10: runtime error: index 3 out of bounds for type 'int [3][4]'
- // CHECK-C-4: bounds.cpp:11:10: runtime error: index 4 out of bounds for type 'int [4]'
+ // CHECK-A-2: bounds.cpp:[[@LINE-1]]:10: runtime error: index 2 out of bounds for type 'int [2][3][4]'
+ // CHECK-B-3: bounds.cpp:[[@LINE-2]]:10: runtime error: index 3 out of bounds for type 'int [3][4]'
+ // CHECK-C-4: bounds.cpp:[[@LINE-3]]:10: runtime error: index 4 out of bounds for type 'int [4]'
}
diff --git a/test/ubsan/TestCases/Misc/enum.cpp b/test/ubsan/TestCases/Misc/enum.cpp
index c95ce82..49ac7c6 100644
--- a/test/ubsan/TestCases/Misc/enum.cpp
+++ b/test/ubsan/TestCases/Misc/enum.cpp
@@ -1,6 +1,6 @@
// RUN: %clangxx -fsanitize=enum %s -O3 -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-PLAIN
// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E" %s -O3 -o %t && %run %t
-// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E : bool" %s -O3 -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-BOOL
+// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E : bool" %s -O3 -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-BOOL
enum E { a = 1 } e;
#undef E
diff --git a/test/ubsan/TestCases/Misc/missing_return.cpp b/test/ubsan/TestCases/Misc/missing_return.cpp
index 04bc791..5d3d54d 100644
--- a/test/ubsan/TestCases/Misc/missing_return.cpp
+++ b/test/ubsan/TestCases/Misc/missing_return.cpp
@@ -1,7 +1,15 @@
-// RUN: %clangxx -fsanitize=return %s -O3 -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx -fsanitize=return -g %s -O3 -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+// RUN: UBSAN_OPTIONS=print_stacktrace=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os-STACKTRACE
-// CHECK: missing_return.cpp:4:5: runtime error: execution reached the end of a value-returning function without returning a value
+// CHECK: missing_return.cpp:[[@LINE+1]]:5: runtime error: execution reached the end of a value-returning function without returning a value
int f() {
+// Slow stack unwinding is disabled on Darwin for now, see
+// https://code.google.com/p/address-sanitizer/issues/detail?id=137
+// CHECK-Linux-STACKTRACE: #0 {{.*}} in f(){{.*}}missing_return.cpp:[[@LINE-3]]
+// CHECK-FreeBSD-STACKTRACE: #0 {{.*}} in f(void){{.*}}missing_return.cpp:[[@LINE-4]]
+// Check for already checked line to avoid lit error reports.
+// CHECK-Darwin-STACKTRACE: missing_return.cpp
}
int main(int, char **argv) {
diff --git a/test/ubsan/TestCases/Misc/nonnull-arg.cpp b/test/ubsan/TestCases/Misc/nonnull-arg.cpp
new file mode 100644
index 0000000..b1061b7
--- /dev/null
+++ b/test/ubsan/TestCases/Misc/nonnull-arg.cpp
@@ -0,0 +1,58 @@
+// RUN: %clangxx -fsanitize=nonnull-attribute -fno-sanitize-recover %s -O3 -o %t
+// RUN: %run %t nc
+// RUN: %run %t nm
+// RUN: %run %t nf
+// RUN: %run %t nv
+// RUN: not %run %t 0c 2>&1 | FileCheck %s --check-prefix=CTOR
+// RUN: not %run %t 0m 2>&1 | FileCheck %s --check-prefix=METHOD
+// RUN: not %run %t 0f 2>&1 | FileCheck %s --check-prefix=FUNC
+// RUN: not %run %t 0v 2>&1 | FileCheck %s --check-prefix=VARIADIC
+
+class C {
+ int *null_;
+ int *nonnull_;
+
+public:
+ C(int *null, __attribute__((nonnull)) int *nonnull)
+ : null_(null), nonnull_(nonnull) {}
+ int value() { return *nonnull_; }
+ int method(int *nonnull, int *null) __attribute__((nonnull(2))) {
+ return *nonnull_ + *nonnull;
+ }
+};
+
+__attribute__((nonnull)) int func(int *nonnull) { return *nonnull; }
+
+#include <stdarg.h>
+__attribute__((nonnull)) int variadic(int x, ...) {
+ va_list args;
+ va_start(args, x);
+ int *nonnull = va_arg(args, int*);
+ int res = *nonnull;
+ va_end(args);
+ return res;
+}
+
+int main(int argc, char *argv[]) {
+ int local = 0;
+ int *arg = (argv[1][0] == '0') ? 0x0 : &local;
+ switch (argv[1][1]) {
+ case 'c':
+ return C(0x0, arg).value();
+ // CTOR: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:21: runtime error: null pointer passed as argument 2, which is declared to never be null
+ // CTOR-NEXT: {{.*}}nonnull-arg.cpp:16:31: note: nonnull attribute specified here
+ case 'm':
+ return C(0x0, &local).method(arg, 0x0);
+ // METHOD: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:36: runtime error: null pointer passed as argument 1, which is declared to never be null
+ // METHOD-NEXT: {{.*}}nonnull-arg.cpp:19:54: note: nonnull attribute specified here
+ case 'f':
+ return func(arg);
+ // FUNC: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:19: runtime error: null pointer passed as argument 1, which is declared to never be null
+ // FUNC-NEXT: {{.*}}nonnull-arg.cpp:24:16: note: nonnull attribute specified here
+ case 'v':
+ return variadic(42, arg);
+ // VARIADIC: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:27: runtime error: null pointer passed as argument 2, which is declared to never be null
+ // VARIADIC-NEXT: {{.*}}nonnull-arg.cpp:27:16: note: nonnull attribute specified here
+ }
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Misc/nonnull.cpp b/test/ubsan/TestCases/Misc/nonnull.cpp
new file mode 100644
index 0000000..c3ab49c
--- /dev/null
+++ b/test/ubsan/TestCases/Misc/nonnull.cpp
@@ -0,0 +1,15 @@
+// RUN: %clangxx -fsanitize=returns-nonnull-attribute %s -O3 -o %t
+// RUN: %run %t foo
+// RUN: %run %t 2>&1 | FileCheck %s
+
+__attribute__((returns_nonnull)) char *foo(char *a);
+
+char *foo(char *a) {
+ return a;
+ // CHECK: nonnull.cpp:[[@LINE+2]]:1: runtime error: null pointer returned from function declared to never return null
+ // CHECK-NEXT: nonnull.cpp:[[@LINE-5]]:16: note: returns_nonnull attribute specified here
+}
+
+int main(int argc, char **argv) {
+ return foo(argv[1]) == 0;
+}
diff --git a/test/ubsan/TestCases/Misc/unreachable.cpp b/test/ubsan/TestCases/Misc/unreachable.cpp
index ba76063..e1206ed 100644
--- a/test/ubsan/TestCases/Misc/unreachable.cpp
+++ b/test/ubsan/TestCases/Misc/unreachable.cpp
@@ -1,4 +1,4 @@
-// RUN: %clangxx -fsanitize=unreachable %s -O3 -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx -fsanitize=unreachable %s -O3 -o %t && not %run %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/test/ubsan/TestCases/TypeCheck/Function/function.cpp b/test/ubsan/TestCases/TypeCheck/Function/function.cpp
index 33c2d1c..deca77d 100644
--- a/test/ubsan/TestCases/TypeCheck/Function/function.cpp
+++ b/test/ubsan/TestCases/TypeCheck/Function/function.cpp
@@ -1,5 +1,10 @@
// RUN: %clangxx -fsanitize=function %s -O3 -g -o %t
// RUN: %run %t 2>&1 | FileCheck %s
+// Verify that we can disable symbolization if needed:
+// RUN: UBSAN_OPTIONS=symbolize=0 ASAN_OPTIONS=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM
+
+// -fsanitize=function is unsupported on Darwin yet.
+// XFAIL: darwin
#include <stdint.h>
@@ -9,7 +14,9 @@
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
+ // CHECK-NEXT: function.cpp:11: note: f() defined here
+ // NOSYM: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)'
+ // NOSYM-NEXT: ({{.*}}+0x{{.*}}): note: (unknown) defined here
reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(f))(42);
// CHECK-NOT: runtime error: call to function g
diff --git a/test/ubsan/TestCases/TypeCheck/misaligned.cpp b/test/ubsan/TestCases/TypeCheck/misaligned.cpp
index 71d82e0..79f5136 100644
--- a/test/ubsan/TestCases/TypeCheck/misaligned.cpp
+++ b/test/ubsan/TestCases/TypeCheck/misaligned.cpp
@@ -1,11 +1,17 @@
-// RUN: %clangxx -fsanitize=alignment %s -O3 -o %t
-// RUN: %run %t l0 && %run %t s0 && %run %t r0 && %run %t m0 && %run %t f0 && %run %t n0
+// RUN: %clangxx -fsanitize=alignment -g %s -O3 -o %t
+// RUN: %run %t l0 && %run %t s0 && %run %t r0 && %run %t m0 && %run %t f0 && %run %t n0 && %run %t u0
// RUN: %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --strict-whitespace
// RUN: %run %t s1 2>&1 | FileCheck %s --check-prefix=CHECK-STORE
// RUN: %run %t r1 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE
// RUN: %run %t m1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER
// RUN: %run %t f1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN
// RUN: %run %t n1 2>&1 | FileCheck %s --check-prefix=CHECK-NEW
+// RUN: %run %t u1 2>&1 | FileCheck %s --check-prefix=CHECK-UPCAST
+// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --check-prefix=CHECK-%os-STACK-LOAD
+
+// RUN: %clangxx -fsanitize=alignment -fno-sanitize-recover %s -O3 -o %t
+// RUN: not %run %t w1 2>&1 | FileCheck %s --check-prefix=CHECK-WILD
+// XFAIL: armv7l-unknown-linux-gnueabihf
#include <new>
@@ -15,12 +21,19 @@
int k;
};
+struct T : S {
+ int t;
+};
+
int main(int, char **argv) {
char c[] __attribute__((aligned(8))) = { 0, 0, 0, 0, 1, 2, 3, 4, 5 };
// Pointer value may be unspecified here, but behavior is not undefined.
int *p = (int*)&c[4 + argv[1][1] - '0'];
S *s = (S*)p;
+ T *t = (T*)p;
+
+ void *wild = reinterpret_cast<void *>(0x123L);
(void)*p; // ok!
@@ -31,6 +44,11 @@
// CHECK-LOAD-NEXT: {{^ 00 00 00 01 02 03 04 05}}
// CHECK-LOAD-NEXT: {{^ \^}}
return *p && 0;
+ // Slow stack unwinding is disabled on Darwin for now, see
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=137
+ // CHECK-Linux-STACK-LOAD: #0 {{.*}} in main{{.*}}misaligned.cpp
+ // Check for the already checked line to avoid lit error reports.
+ // CHECK-Darwin-STACK-LOAD: {{ }}
case 's':
// CHECK-STORE: misaligned.cpp:[[@LINE+4]]:5: runtime error: store to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment
@@ -63,11 +81,25 @@
return s->f() && 0;
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.cpp:[[@LINE+4]]:5: 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: {{^ \^}}
return (new (s) S)->k && 0;
+
+ case 'u': {
+ // CHECK-UPCAST: misaligned.cpp:[[@LINE+4]]:17: runtime error: upcast of misaligned address [[PTR:0x[0-9a-f]*]] for type 'T', which requires 4 byte alignment
+ // CHECK-UPCAST-NEXT: [[PTR]]: note: pointer points here
+ // CHECK-UPCAST-NEXT: {{^ 00 00 00 01 02 03 04 05}}
+ // CHECK-UPCAST-NEXT: {{^ \^}}
+ S *s2 = (S*)t;
+ return s2->f();
+ }
+
+ case 'w':
+ // CHECK-WILD: misaligned.cpp:[[@LINE+3]]:35: runtime error: member access within misaligned address 0x000000000123 for type 'S', which requires 4 byte alignment
+ // CHECK-WILD-NEXT: 0x000000000123: note: pointer points here
+ // CHECK-WILD-NEXT: <memory cannot be printed>
+ return static_cast<S*>(wild)->k;
}
}
diff --git a/test/ubsan/TestCases/TypeCheck/null.cpp b/test/ubsan/TestCases/TypeCheck/null.cpp
index 47f5270..2a90f7f 100644
--- a/test/ubsan/TestCases/TypeCheck/null.cpp
+++ b/test/ubsan/TestCases/TypeCheck/null.cpp
@@ -1,6 +1,6 @@
// RUN: %clangxx -fsanitize=null %s -O3 -o %t
// RUN: %run %t l 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD
-// RUN: %run %t s 2>&1 | FileCheck %s --check-prefix=CHECK-STORE
+// RUN: not --crash %run %t s 2>&1 | FileCheck %s --check-prefix=CHECK-STORE
// RUN: %run %t r 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE
// RUN: %run %t m 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER
// RUN: %run %t f 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN
diff --git a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp
new file mode 100644
index 0000000..5261e71
--- /dev/null
+++ b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp
@@ -0,0 +1,19 @@
+// RUN: %clangxx -fsanitize=vptr -fno-sanitize-recover -g %s -O3 -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// FIXME: This test produces linker errors on Darwin.
+// XFAIL: darwin
+
+struct S { virtual int f() { return 0; } };
+struct T : virtual S {};
+
+struct Foo { virtual int f() { return 0; } };
+
+int main(int argc, char **argv) {
+ Foo foo;
+ T *t = (T*)&foo;
+ S *s = t;
+ // CHECK: vptr-virtual-base.cpp:[[@LINE-1]]:10: runtime error: cast to virtual base of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
+ // CHECK-NEXT: [[PTR]]: note: object is of type 'Foo'
+ return s->f();
+}
diff --git a/test/ubsan/TestCases/TypeCheck/vptr.cpp b/test/ubsan/TestCases/TypeCheck/vptr.cpp
index 4a8910c..3a6b155 100644
--- a/test/ubsan/TestCases/TypeCheck/vptr.cpp
+++ b/test/ubsan/TestCases/TypeCheck/vptr.cpp
@@ -1,4 +1,4 @@
-// RUN: %clangxx -fsanitize=vptr %s -O3 -o %t
+// RUN: %clangxx -fsanitize=vptr -g %s -O3 -o %t
// RUN: %run %t rT && %run %t mT && %run %t fT && %run %t cT
// RUN: %run %t rU && %run %t mU && %run %t fU && %run %t cU
// RUN: %run %t rS && %run %t rV && %run %t oV
@@ -11,8 +11,27 @@
// RUN: %run %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --strict-whitespace
// RUN: %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace
+// RUN: (echo "vptr_check:S"; echo "vptr_check:T"; echo "vptr_check:U") > %t.supp
+// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t mS 2>&1
+// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t fS 2>&1
+// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t cS 2>&1
+// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t mV 2>&1
+// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t fV 2>&1
+// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t cV 2>&1
+// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t oU 2>&1
+
+// RUN: echo "vptr_check:S" > %t.loc-supp
+// RUN: ASAN_OPTIONS=suppressions=%t.loc-supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.loc-supp:halt_on_error=1 not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS
+
// FIXME: This test produces linker errors on Darwin.
// XFAIL: darwin
+// REQUIRES: stable-runtime
+
+extern "C" {
+const char *__ubsan_default_options() {
+ return "print_stacktrace=1";
+}
+}
struct S {
S() : a(0) {}
@@ -29,9 +48,15 @@
virtual int v() { return 1; }
};
-struct U : S, T { virtual int v() { return 2; } };
+struct X {};
+struct U : S, T, virtual X { virtual int v() { return 2; } };
-T *p = 0; // Make p global so that lsan does not complain.
+struct V : S {};
+
+// Make p global so that lsan does not complain.
+T *p = 0;
+
+int access_p(T *p, char type);
int main(int, char **argv) {
T t;
@@ -70,18 +95,37 @@
break;
}
- switch (argv[1][0]) {
+ access_p(p, argv[1][0]);
+ return 0;
+}
+
+int access_p(T *p, char type) {
+ switch (type) {
case 'r':
// Binding a reference to storage of appropriate size and alignment is OK.
{T &r = *p;}
break;
+ case 'x':
+ for (int i = 0; i < 2; i++) {
+ // Check that the first iteration ("S") succeeds, while the second ("V") fails.
+ p = reinterpret_cast<T*>((i == 0) ? new S : new V);
+ // CHECK-LOC-SUPPRESS: vptr.cpp:[[@LINE+5]]:7: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
+ // CHECK-LOC-SUPPRESS-NEXT: [[PTR]]: note: object is of type 'V'
+ // CHECK-LOC-SUPPRESS-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
+ // CHECK-LOC-SUPPRESS-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
+ // CHECK-LOC-SUPPRESS-NEXT: {{^ vptr for 'V'}}
+ p->g();
+ }
+ return 0;
+
case 'm':
- // CHECK-MEMBER: vptr.cpp:[[@LINE+5]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
+ // CHECK-MEMBER: vptr.cpp:[[@LINE+6]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
// CHECK-MEMBER-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]
// CHECK-MEMBER-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
// CHECK-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
// CHECK-MEMBER-NEXT: {{^ vptr for}} [[DYN_TYPE]]
+ // CHECK-MEMBER-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE+1]]
return p->b;
// CHECK-NULL-MEMBER: vptr.cpp:[[@LINE-2]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
@@ -89,29 +133,33 @@
// CHECK-NULL-MEMBER-NEXT: {{^ ?.. .. .. .. ?00 00 00 00 ?00 00 00 00 ?}}
// CHECK-NULL-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
// CHECK-NULL-MEMBER-NEXT: {{^ invalid vptr}}
+ // CHECK-NULL-MEMBER-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE-7]]
case 'f':
- // CHECK-MEMFUN: 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 'T'
+ // CHECK-MEMFUN: vptr.cpp:[[@LINE+6]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
// CHECK-MEMFUN-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]
// CHECK-MEMFUN-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
// CHECK-MEMFUN-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
// CHECK-MEMFUN-NEXT: {{^ vptr for}} [[DYN_TYPE]]
+ // TODO: Add check for stacktrace here.
return p->g();
case 'o':
- // 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: vptr.cpp:[[@LINE+6]]: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: {{^ ( )?vptr for}} 'T' base class of [[DYN_TYPE]]
+ // CHECK-OFFSET-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE+1]]
return reinterpret_cast<U*>(p)->v() - 2;
case 'c':
- // CHECK-DOWNCAST: vptr.cpp:[[@LINE+5]]:5: runtime error: downcast of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
+ // CHECK-DOWNCAST: vptr.cpp:[[@LINE+6]]:5: runtime error: downcast of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
// CHECK-DOWNCAST-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]
// CHECK-DOWNCAST-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
// CHECK-DOWNCAST-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
// CHECK-DOWNCAST-NEXT: {{^ vptr for}} [[DYN_TYPE]]
+ // CHECK-DOWNCAST-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE+1]]
static_cast<T*>(reinterpret_cast<S*>(p));
return 0;
}
diff --git a/test/ubsan/lit.common.cfg b/test/ubsan/lit.common.cfg
index a34a191..d28733a 100644
--- a/test/ubsan/lit.common.cfg
+++ b/test/ubsan/lit.common.cfg
@@ -18,10 +18,17 @@
ubsan_lit_test_mode = get_required_attr(config, 'ubsan_lit_test_mode')
if ubsan_lit_test_mode == "Standalone":
config.name = 'UndefinedBehaviorSanitizer-Standalone'
+ config.available_features.add("ubsan-standalone")
clang_ubsan_cflags = []
elif ubsan_lit_test_mode == "AddressSanitizer":
+ if config.host_os == 'Darwin':
+ # ubsan-asan doesn't yet work on Darwin,
+ # see http://llvm.org/bugs/show_bug.cgi?id=21112.
+ config.unsupported = True
config.name = 'UndefinedBehaviorSanitizer-AddressSanitizer'
+ config.available_features.add("ubsan-asan")
clang_ubsan_cflags = ["-fsanitize=address"]
+ config.environment['ASAN_OPTIONS'] = 'detect_leaks=0'
else:
lit_config.fatal("Unknown UBSan test mode: %r" % ubsan_lit_test_mode)
@@ -39,9 +46,11 @@
# 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']:
+# Check that the host supports UndefinedBehaviorSanitizer tests
+if config.host_os not in ['Linux', 'Darwin', 'FreeBSD']:
config.unsupported = True
-config.pipefail = False
+# Allow tests to use REQUIRES=stable-runtime. For use when you cannot use XFAIL
+# because the test hangs or fails on one configuration and not the other.
+if config.target_arch.startswith('arm') == False:
+ config.available_features.add('stable-runtime')