diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2014-11-06 22:49:13 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2014-11-06 22:49:13 +0000 |
commit | 8ef50bf3d1c287b5013c3168de77a462dfce3495 (patch) | |
tree | 3467f3372c1195b1546172d89af2205a50b1866d /lib/tsan | |
parent | 11023dc647fd8f41418da90d59db138400d0f334 (diff) | |
download | src-8ef50bf3d1c287b5013c3168de77a462dfce3495.tar.gz src-8ef50bf3d1c287b5013c3168de77a462dfce3495.zip |
Import compiler-rt release_34 branch r197381.vendor/compiler-rt/compiler-rt-r197381
Notes
Notes:
svn path=/vendor/compiler-rt/dist/; revision=274201
svn path=/vendor/compiler-rt/compiler-rt-r197381/; revision=274202; tag=vendor/compiler-rt/compiler-rt-r197381
Diffstat (limited to 'lib/tsan')
148 files changed, 2477 insertions, 1185 deletions
diff --git a/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt index 282889567509..fc1944b02fa5 100644 --- a/lib/tsan/CMakeLists.txt +++ b/lib/tsan/CMakeLists.txt @@ -8,10 +8,14 @@ set(TSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS} -fPIE -fno-rtti) -# FIXME: Add support for compile flags: -# -Wframe-larger-than=512, -# -Wglobal-constructors, -# --sysroot=. + +set(TSAN_RTL_CFLAGS + ${TSAN_CFLAGS} + -Wframe-larger-than=512) +if(SUPPORTS_GLOBAL_CONSTRUCTORS_FLAG) + list(APPEND TSAN_RTL_CFLAGS -Wglobal-constructors) +endif() +# FIXME: Add support for --sysroot=. compile flag: if("${CMAKE_BUILD_TYPE}" EQUAL "Release") set(TSAN_COMMON_DEFINITIONS DEBUG=0) @@ -19,7 +23,57 @@ else() set(TSAN_COMMON_DEFINITIONS DEBUG=1) endif() -add_subdirectory(rtl) +set(TSAN_SOURCES + rtl/tsan_clock.cc + rtl/tsan_flags.cc + rtl/tsan_fd.cc + rtl/tsan_interceptors.cc + rtl/tsan_interface_ann.cc + rtl/tsan_interface_atomic.cc + rtl/tsan_interface.cc + rtl/tsan_interface_java.cc + rtl/tsan_md5.cc + rtl/tsan_mman.cc + rtl/tsan_mutex.cc + rtl/tsan_mutexset.cc + rtl/tsan_report.cc + rtl/tsan_rtl.cc + rtl/tsan_rtl_mutex.cc + rtl/tsan_rtl_report.cc + rtl/tsan_rtl_thread.cc + rtl/tsan_stat.cc + rtl/tsan_suppressions.cc + rtl/tsan_symbolize.cc + rtl/tsan_sync.cc) + +if(APPLE) + list(APPEND TSAN_SOURCES rtl/tsan_platform_mac.cc) +elseif(UNIX) + # Assume Linux + list(APPEND TSAN_SOURCES + rtl/tsan_platform_linux.cc + rtl/tsan_symbolize_addr2line_linux.cc) +endif() + +set(TSAN_RUNTIME_LIBRARIES) +# TSan is currently supported on 64-bit Linux only. +if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE) + set(TSAN_ASM_SOURCES rtl/tsan_rtl_amd64.S) + # Pass ASM file directly to the C++ compiler. + set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES + LANGUAGE C) + set(arch "x86_64") + add_compiler_rt_static_runtime(clang_rt.tsan-${arch} ${arch} + SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES} + $<TARGET_OBJECTS:RTInterception.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + CFLAGS ${TSAN_RTL_CFLAGS} + DEFS ${TSAN_COMMON_DEFINITIONS}) + add_sanitizer_rt_symbols(clang_rt.tsan-${arch} rtl/tsan.syms.extra) + list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch} + clang_rt.tsan-${arch}-symbols) +endif() if(LLVM_INCLUDE_TESTS) add_subdirectory(tests) diff --git a/lib/tsan/Makefile.old b/lib/tsan/Makefile.old index b548f5d2f6ee..c10d49842cac 100644 --- a/lib/tsan/Makefile.old +++ b/lib/tsan/Makefile.old @@ -21,8 +21,11 @@ GTEST_BUILD_DIR=$(GTEST_ROOT)/build GTEST_LIB_NAME=gtest-all.o GTEST_LIB=$(GTEST_BUILD_DIR)/$(GTEST_LIB_NAME) -SANITIZER_COMMON_TESTS_SRC=$(wildcard ../sanitizer_common/tests/*_test.cc) -SANITIZER_COMMON_TESTS_OBJ=$(patsubst %.cc,%.o,$(SANITIZER_COMMON_TESTS_SRC)) +SANITIZER_TESTS_PATH=../sanitizer_common/tests +SANITIZER_COMMON_TESTS_SRC=$(wildcard $(SANITIZER_TESTS_PATH)/*_test.cc) +SANITIZER_COMMON_EXCLUDED_TESTS=$(SANITIZER_TESTS_PATH)/sanitizer_nolibc_test.cc +SANITIZER_COMMON_GOOD_TESTS=$(filter-out $(SANITIZER_COMMON_EXCLUDED_TESTS), $(SANITIZER_COMMON_TESTS_SRC)) +SANITIZER_COMMON_TESTS_OBJ=$(patsubst %.cc,%.o,$(SANITIZER_COMMON_GOOD_TESTS)) RTL_TEST_SRC=$(wildcard tests/rtl/*.cc) RTL_TEST_OBJ=$(patsubst %.cc,%.o,$(RTL_TEST_SRC)) UNIT_TEST_SRC=$(wildcard tests/unit/*_test.cc) @@ -51,7 +54,7 @@ libtsan: tsan_test: $(UNIT_TEST_OBJ) $(RTL_TEST_OBJ) \ $(SANITIZER_COMMON_TESTS_OBJ) $(LIBTSAN) $(GTEST_LIB) - $(CXX) $^ -o $@ $(LDFLAGS) + $(CXX) -Wl,--whole-archive $^ -Wl,--no-whole-archive -o $@ $(LDFLAGS) test: libtsan tsan_test diff --git a/lib/tsan/check_cmake.sh b/lib/tsan/check_cmake.sh index 52c97c339096..7e858efee913 100755 --- a/lib/tsan/check_cmake.sh +++ b/lib/tsan/check_cmake.sh @@ -3,10 +3,16 @@ set -u set -e ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -mkdir -p $ROOT/build -cd $ROOT/build -CC=clang CXX=clang++ cmake -DLLVM_ENABLE_WERROR=ON -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON $ROOT/../../../.. -make -j64 -make check-sanitizer -j64 -make check-tsan -j64 -make check-asan -j64 +if [ -d "$ROOT/build" ]; then + cd $ROOT/build +else + mkdir -p $ROOT/build + cd $ROOT/build + CC=clang CXX=clang++ cmake -G Ninja -DLLVM_ENABLE_WERROR=ON -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON $ROOT/../../../.. +fi +ninja +ninja check-sanitizer +ninja check-tsan +ninja check-asan +ninja check-msan +ninja check-lsan diff --git a/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh index 51f1a7975b57..bd03fc0b65e0 100755 --- a/lib/tsan/go/buildgo.sh +++ b/lib/tsan/go/buildgo.sh @@ -20,12 +20,13 @@ SRCS=" ../../sanitizer_common/sanitizer_flags.cc ../../sanitizer_common/sanitizer_libc.cc ../../sanitizer_common/sanitizer_printf.cc + ../../sanitizer_common/sanitizer_suppressions.cc ../../sanitizer_common/sanitizer_thread_registry.cc " if [ "`uname -a | grep Linux`" != "" ]; then SUFFIX="linux_amd64" - OSCFLAGS="-fPIC -ffreestanding" + OSCFLAGS="-fPIC -ffreestanding -Wno-maybe-uninitialized -Werror" OSLDFLAGS="-lpthread -fPIC -fpie" SRCS+=" ../rtl/tsan_platform_linux.cc @@ -33,6 +34,7 @@ if [ "`uname -a | grep Linux`" != "" ]; then ../../sanitizer_common/sanitizer_posix_libcdep.cc ../../sanitizer_common/sanitizer_linux.cc ../../sanitizer_common/sanitizer_linux_libcdep.cc + ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc " elif [ "`uname -a | grep Darwin`" != "" ]; then SUFFIX="darwin_amd64" @@ -42,6 +44,7 @@ elif [ "`uname -a | grep Darwin`" != "" ]; then ../rtl/tsan_platform_mac.cc ../../sanitizer_common/sanitizer_posix.cc ../../sanitizer_common/sanitizer_mac.cc + ../../sanitizer_common/sanitizer_posix_libcdep.cc " elif [ "`uname -a | grep MINGW`" != "" ]; then SUFFIX="windows_amd64" @@ -63,7 +66,7 @@ for F in $SRCS; do cat $F >> gotsan.cc done -FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -m64 -Wall -Werror -fno-exceptions -fno-rtti -DTSAN_GO -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 $OSCFLAGS" +FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -m64 -Wall -fno-exceptions -fno-rtti -DTSAN_GO -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 $OSCFLAGS" if [ "$DEBUG" == "" ]; then FLAGS+=" -DTSAN_DEBUG=0 -O3 -fomit-frame-pointer" else diff --git a/lib/tsan/go/test.c b/lib/tsan/go/test.c index 902dfc915582..859b35d348e8 100644 --- a/lib/tsan/go/test.c +++ b/lib/tsan/go/test.c @@ -34,20 +34,32 @@ int __tsan_symbolize(void *pc, char **img, char **rtn, char **file, int *l) { char buf[10]; +void foobar() {} +void barfoo() {} + int main(void) { void *thr0 = 0; __tsan_init(&thr0); __tsan_map_shadow(buf, sizeof(buf) + 4096); - __tsan_func_enter(thr0, &main); + __tsan_func_enter(thr0, (char*)&main + 1); __tsan_malloc(thr0, buf, 10, 0); __tsan_release(thr0, buf); __tsan_release_merge(thr0, buf); void *thr1 = 0; - __tsan_go_start(thr0, &thr1, 0); - __tsan_write(thr1, buf, 0); + __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1); + void *thr2 = 0; + __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_write(thr1, buf, (char*)&barfoo + 1); __tsan_acquire(thr1, buf); + __tsan_func_exit(thr1); + __tsan_func_exit(thr1); __tsan_go_end(thr1); - __tsan_read(thr0, buf, 0); + __tsan_func_enter(thr2, (char*)&foobar + 1); + __tsan_read(thr2, buf, (char*)&barfoo + 1); + __tsan_func_exit(thr2); + __tsan_go_end(thr2); __tsan_free(buf); __tsan_func_exit(thr0); __tsan_fini(); diff --git a/lib/tsan/go/tsan_go.cc b/lib/tsan/go/tsan_go.cc index 957d58211281..df54bb8e216c 100644 --- a/lib/tsan/go/tsan_go.cc +++ b/lib/tsan/go/tsan_go.cc @@ -116,12 +116,14 @@ void __tsan_write(ThreadState *thr, void *addr, void *pc) { void __tsan_read_range(ThreadState *thr, void *addr, uptr size, uptr step, void *pc) { - MemoryAccessRangeStep(thr, (uptr)pc, (uptr)addr, size, step, false); + (void)step; + MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, false); } void __tsan_write_range(ThreadState *thr, void *addr, uptr size, uptr step, void *pc) { - MemoryAccessRangeStep(thr, (uptr)pc, (uptr)addr, size, step, true); + (void)step; + MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, true); } void __tsan_func_enter(ThreadState *thr, void *pc) { @@ -184,40 +186,6 @@ void __tsan_finalizer_goroutine(ThreadState *thr) { AcquireGlobal(thr, 0); } -#if SANITIZER_WINDOWS -// MinGW gcc emits calls to the function. -void ___chkstk_ms(void) { -// The implementation must be along the lines of: -// .code64 -// PUBLIC ___chkstk_ms -// //cfi_startproc() -// ___chkstk_ms: -// push rcx -// //cfi_push(%rcx) -// push rax -// //cfi_push(%rax) -// cmp rax, PAGE_SIZE -// lea rcx, [rsp + 24] -// jb l_LessThanAPage -// .l_MoreThanAPage: -// sub rcx, PAGE_SIZE -// or rcx, 0 -// sub rax, PAGE_SIZE -// cmp rax, PAGE_SIZE -// ja l_MoreThanAPage -// .l_LessThanAPage: -// sub rcx, rax -// or [rcx], 0 -// pop rax -// //cfi_pop(%rax) -// pop rcx -// //cfi_pop(%rcx) -// ret -// //cfi_endproc() -// END -} -#endif - } // extern "C" } // namespace __tsan diff --git a/lib/tsan/lit_tests/CMakeLists.txt b/lib/tsan/lit_tests/CMakeLists.txt index 53e5015d1bc4..1f2fbf98e080 100644 --- a/lib/tsan/lit_tests/CMakeLists.txt +++ b/lib/tsan/lit_tests/CMakeLists.txt @@ -8,7 +8,7 @@ configure_lit_site_cfg( ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg ) -if(COMPILER_RT_CAN_EXECUTE_TESTS) +if(COMPILER_RT_CAN_EXECUTE_TESTS AND CAN_TARGET_x86_64) # Run TSan output tests only if we're sure we can produce working binaries. set(TSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS} @@ -25,7 +25,7 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS) DEPENDS ${TSAN_TEST_DEPS} ) set_target_properties(check-tsan PROPERTIES FOLDER "TSan unittests") -elseif(LLVM_INCLUDE_TESTS) +elseif(LLVM_INCLUDE_TESTS AND CAN_TARGET_x86_64) # Otherwise run only TSan unit tests (they are linked using the # host compiler). add_lit_testsuite(check-tsan "Running ThreadSanitizer tests" diff --git a/lib/tsan/lit_tests/Unit/lit.cfg b/lib/tsan/lit_tests/Unit/lit.cfg index 0a0dbbfa5495..36585df1c671 100644 --- a/lib/tsan/lit_tests/Unit/lit.cfg +++ b/lib/tsan/lit_tests/Unit/lit.cfg @@ -5,17 +5,12 @@ import os def get_required_attr(config, attr_name): attr_value = getattr(config, attr_name, None) if not attr_value: - lit.fatal("No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) return attr_value -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') -compiler_rt_lit_unit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.unit.cfg") -lit.load_config(config, compiler_rt_lit_unit_cfg) - # Setup config name. config.name = 'ThreadSanitizer-Unit' @@ -26,11 +21,3 @@ config.test_exec_root = os.path.join(llvm_obj_root, "projects", "compiler-rt", "lib", "tsan", "tests") config.test_source_root = config.test_exec_root - -# Get path to external LLVM symbolizer to run ThreadSanitizer unit tests. -llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) -if llvm_tools_dir: - llvm_symbolizer_path = os.path.join(llvm_tools_dir, "llvm-symbolizer") - config.environment['TSAN_OPTIONS'] = ("external_symbolizer_path=" + - llvm_symbolizer_path) - diff --git a/lib/tsan/lit_tests/Unit/lit.site.cfg.in b/lib/tsan/lit_tests/Unit/lit.site.cfg.in index 6eedc2180876..3701a2cad74c 100644 --- a/lib/tsan/lit_tests/Unit/lit.site.cfg.in +++ b/lib/tsan/lit_tests/Unit/lit.site.cfg.in @@ -1,20 +1,8 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! -config.llvm_obj_root = "@LLVM_BINARY_DIR@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" -config.llvm_build_mode = "@LLVM_BUILD_MODE@" +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") -# LLVM tools dir can be passed in lit parameters, so try to -# apply substitution. -try: - config.llvm_tools_dir = config.llvm_tools_dir % lit.params - config.llvm_build_mode = config.llvm_build_mode % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) - -# Let the main config do the real work. -lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/Unit/lit.cfg") +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/Unit/lit.cfg") diff --git a/lib/tsan/lit_tests/allocator_returns_null.cc b/lib/tsan/lit_tests/allocator_returns_null.cc new file mode 100644 index 000000000000..4b5eb5504c27 --- /dev/null +++ b/lib/tsan/lit_tests/allocator_returns_null.cc @@ -0,0 +1,64 @@ +// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// By default (allocator_may_return_null=0) the process shoudl crash. +// With allocator_may_return_null=1 the allocator should return 0. +// +// RUN: %clangxx_tsan -O0 %s -o %t +// RUN: not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <limits> +int main(int argc, char **argv) { + volatile size_t size = std::numeric_limits<size_t>::max() - 10000; + assert(argc == 2); + char *x = 0; + if (!strcmp(argv[1], "malloc")) { + fprintf(stderr, "malloc:\n"); + x = (char*)malloc(size); + } + if (!strcmp(argv[1], "calloc")) { + fprintf(stderr, "calloc:\n"); + x = (char*)calloc(size / 4, 4); + } + + if (!strcmp(argv[1], "calloc-overflow")) { + fprintf(stderr, "calloc-overflow:\n"); + volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = (char*)calloc(kArraySize, kArraySize2); + } + + if (!strcmp(argv[1], "realloc")) { + fprintf(stderr, "realloc:\n"); + x = (char*)realloc(0, size); + } + if (!strcmp(argv[1], "realloc-after-malloc")) { + fprintf(stderr, "realloc-after-malloc:\n"); + char *t = (char*)malloc(100); + *t = 42; + x = (char*)realloc(t, size); + assert(*t == 42); + } + fprintf(stderr, "x: %p\n", x); + return x != 0; +} +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: ThreadSanitizer's allocator is terminating the process + diff --git a/lib/tsan/lit_tests/atomic_free.cc b/lib/tsan/lit_tests/atomic_free.cc index ba9bd5ac4aed..87d559362af4 100644 --- a/lib/tsan/lit_tests/atomic_free.cc +++ b/lib/tsan/lit_tests/atomic_free.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/atomic_free2.cc b/lib/tsan/lit_tests/atomic_free2.cc index 5517bf7ce902..961ff38c843b 100644 --- a/lib/tsan/lit_tests/atomic_free2.cc +++ b/lib/tsan/lit_tests/atomic_free2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/atomic_race.cc b/lib/tsan/lit_tests/atomic_race.cc index 360b81238889..0dfe4d93df6e 100644 --- a/lib/tsan/lit_tests/atomic_race.cc +++ b/lib/tsan/lit_tests/atomic_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/atomic_stack.cc b/lib/tsan/lit_tests/atomic_stack.cc index 50f6a8a889ca..841f74b891ab 100644 --- a/lib/tsan/lit_tests/atomic_stack.cc +++ b/lib/tsan/lit_tests/atomic_stack.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/cond.c b/lib/tsan/lit_tests/cond.c new file mode 100644 index 000000000000..52c87a413eb7 --- /dev/null +++ b/lib/tsan/lit_tests/cond.c @@ -0,0 +1,53 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK-NOT: ThreadSanitizer WARNING: double lock +// CHECK-NOT: ThreadSanitizer WARNING: mutex unlock by another thread +// CHECK: OK + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +pthread_mutex_t m; +pthread_cond_t c; +int x; + +void *thr1(void *p) { + int i; + + for (i = 0; i < 10; i += 2) { + pthread_mutex_lock(&m); + while (x != i) + pthread_cond_wait(&c, &m); + x = i + 1; + pthread_cond_signal(&c); + pthread_mutex_unlock(&m); + } + return 0; +} + +void *thr2(void *p) { + int i; + + for (i = 1; i < 10; i += 2) { + pthread_mutex_lock(&m); + while (x != i) + pthread_cond_wait(&c, &m); + x = i + 1; + pthread_mutex_unlock(&m); + pthread_cond_broadcast(&c); + } + return 0; +} + +int main() { + pthread_t th1, th2; + + pthread_mutex_init(&m, 0); + pthread_cond_init(&c, 0); + pthread_create(&th1, 0, thr1, 0); + pthread_create(&th2, 0, thr2, 0); + pthread_join(th1, 0); + pthread_join(th2, 0); + fprintf(stderr, "OK\n"); +} diff --git a/lib/tsan/lit_tests/cond_race.cc b/lib/tsan/lit_tests/cond_race.cc new file mode 100644 index 000000000000..1e2acb243279 --- /dev/null +++ b/lib/tsan/lit_tests/cond_race.cc @@ -0,0 +1,36 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// CHECK: ThreadSanitizer: data race +// CHECK: pthread_cond_signal + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +struct Ctx { + pthread_mutex_t m; + pthread_cond_t c; + bool done; +}; + +void *thr(void *p) { + Ctx *c = (Ctx*)p; + pthread_mutex_lock(&c->m); + c->done = true; + pthread_mutex_unlock(&c->m); + pthread_cond_signal(&c->c); + return 0; +} + +int main() { + Ctx *c = new Ctx(); + pthread_mutex_init(&c->m, 0); + pthread_cond_init(&c->c, 0); + pthread_t th; + pthread_create(&th, 0, thr, c); + pthread_mutex_lock(&c->m); + while (!c->done) + pthread_cond_wait(&c->c, &c->m); + pthread_mutex_unlock(&c->m); + delete c; + pthread_join(th, 0); +} diff --git a/lib/tsan/lit_tests/cond_version.c b/lib/tsan/lit_tests/cond_version.c new file mode 100644 index 000000000000..1f966bfacb8d --- /dev/null +++ b/lib/tsan/lit_tests/cond_version.c @@ -0,0 +1,44 @@ +// RUN: %clang_tsan -O1 %s -o %t -lrt && %t 2>&1 | FileCheck %s +// Test that pthread_cond is properly intercepted, +// previously there were issues with versioned symbols. +// CHECK: OK + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <time.h> +#include <errno.h> + +int main() { + typedef unsigned long long u64; + pthread_mutex_t m; + pthread_cond_t c; + pthread_condattr_t at; + struct timespec ts0, ts1, ts2; + int res; + u64 sleep; + + pthread_mutex_init(&m, 0); + pthread_condattr_init(&at); + pthread_condattr_setclock(&at, CLOCK_MONOTONIC); + pthread_cond_init(&c, &at); + + clock_gettime(CLOCK_MONOTONIC, &ts0); + ts1 = ts0; + ts1.tv_sec += 2; + + pthread_mutex_lock(&m); + do { + res = pthread_cond_timedwait(&c, &m, &ts1); + } while (res == 0); + pthread_mutex_unlock(&m); + + clock_gettime(CLOCK_MONOTONIC, &ts2); + sleep = (u64)ts2.tv_sec * 1000000000 + ts2.tv_nsec - + ((u64)ts0.tv_sec * 1000000000 + ts0.tv_nsec); + if (res != ETIMEDOUT) + exit(printf("bad return value %d, want %d\n", res, ETIMEDOUT)); + if (sleep < 1000000000) + exit(printf("bad sleep duration %lluns, want %dns\n", sleep, 1000000000)); + fprintf(stderr, "OK\n"); +} diff --git a/lib/tsan/lit_tests/deep_stack1.cc b/lib/tsan/lit_tests/deep_stack1.cc new file mode 100644 index 000000000000..3048aa8745bb --- /dev/null +++ b/lib/tsan/lit_tests/deep_stack1.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_tsan -O1 %s -o %t -DORDER1 && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t -DORDER2 && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +volatile int X; +volatile int N; +void (*volatile F)(); + +static void foo() { + if (--N == 0) + X = 42; + else + F(); +} + +void *Thread(void *p) { +#ifdef ORDER1 + sleep(1); +#endif + F(); + return 0; +} + +int main() { + N = 50000; + F = foo; + pthread_t t; + pthread_attr_t a; + pthread_attr_init(&a); + pthread_attr_setstacksize(&a, N * 256 + (1 << 20)); + pthread_create(&t, &a, Thread, 0); +#ifdef ORDER2 + sleep(1); +#endif + X = 43; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #100 foo +// We must output suffucuently large stack (at least 100 frames) + diff --git a/lib/tsan/lit_tests/default_options.cc b/lib/tsan/lit_tests/default_options.cc new file mode 100644 index 000000000000..62c6c028f9e4 --- /dev/null +++ b/lib/tsan/lit_tests/default_options.cc @@ -0,0 +1,32 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +extern "C" const char *__tsan_default_options() { + return "report_bugs=0"; +} + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/lib/tsan/lit_tests/fd_close_norace2.cc b/lib/tsan/lit_tests/fd_close_norace2.cc new file mode 100644 index 000000000000..b42b334a27c0 --- /dev/null +++ b/lib/tsan/lit_tests/fd_close_norace2.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int pipes[2]; + +void *Thread(void *x) { + // wait for shutown signal + while (read(pipes[0], &x, 1) != 1) { + } + close(pipes[0]); + close(pipes[1]); + return 0; +} + +int main() { + if (pipe(pipes)) + return 1; + pthread_t t; + pthread_create(&t, 0, Thread, 0); + // send shutdown signal + while (write(pipes[1], &t, 1) != 1) { + } + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/lib/tsan/lit_tests/fd_location.cc b/lib/tsan/lit_tests/fd_location.cc index 35f9aabb0377..2b1e9c56e361 100644 --- a/lib/tsan/lit_tests/fd_location.cc +++ b/lib/tsan/lit_tests/fd_location.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/fd_pipe_race.cc b/lib/tsan/lit_tests/fd_pipe_race.cc index dfdb7795aae6..4dd2b77861ab 100644 --- a/lib/tsan/lit_tests/fd_pipe_race.cc +++ b/lib/tsan/lit_tests/fd_pipe_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/fd_stdout_race.cc b/lib/tsan/lit_tests/fd_stdout_race.cc index 6581fc503a1b..4b512bb78874 100644 --- a/lib/tsan/lit_tests/fd_stdout_race.cc +++ b/lib/tsan/lit_tests/fd_stdout_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/free_race.c b/lib/tsan/lit_tests/free_race.c index ff71a4d2116b..d1db9fece90a 100644 --- a/lib/tsan/lit_tests/free_race.c +++ b/lib/tsan/lit_tests/free_race.c @@ -1,4 +1,7 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOZUPP +// RUN: TSAN_OPTIONS="suppressions=%s.supp print_suppressions=1" %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUPP + #include <pthread.h> #include <stdlib.h> #include <stdio.h> @@ -34,11 +37,13 @@ int main() { return 0; } -// CHECK: WARNING: ThreadSanitizer: heap-use-after-free -// CHECK: Write of size 4 at {{.*}} by main thread{{.*}}: -// CHECK: #0 Thread2 -// CHECK: #1 main -// CHECK: Previous write of size 8 at {{.*}} by thread T1{{.*}}: -// CHECK: #0 free -// CHECK: #{{(1|2)}} Thread1 -// CHECK: SUMMARY: ThreadSanitizer: heap-use-after-free{{.*}}Thread2 +// CHECK-NOZUPP: WARNING: ThreadSanitizer: heap-use-after-free +// CHECK-NOZUPP: Write of size 4 at {{.*}} by main thread{{.*}}: +// CHECK-NOZUPP: #0 Thread2 +// CHECK-NOZUPP: #1 main +// CHECK-NOZUPP: Previous write of size 8 at {{.*}} by thread T1{{.*}}: +// CHECK-NOZUPP: #0 free +// CHECK-NOZUPP: #{{(1|2)}} Thread1 +// CHECK-NOZUPP: SUMMARY: ThreadSanitizer: heap-use-after-free{{.*}}Thread2 +// CHECK-SUPP: ThreadSanitizer: Matched 1 suppressions +// CHECK-SUPP: 1 race:^Thread2$ diff --git a/lib/tsan/lit_tests/free_race.c.supp b/lib/tsan/lit_tests/free_race.c.supp new file mode 100644 index 000000000000..f5d6a4969a41 --- /dev/null +++ b/lib/tsan/lit_tests/free_race.c.supp @@ -0,0 +1,2 @@ +# Suppression for a use-after-free in free_race.c +race:^Thread2$ diff --git a/lib/tsan/lit_tests/free_race2.c b/lib/tsan/lit_tests/free_race2.c index f20774b2d8d4..2b9a41927a47 100644 --- a/lib/tsan/lit_tests/free_race2.c +++ b/lib/tsan/lit_tests/free_race2.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <stdlib.h> void __attribute__((noinline)) foo(int *mem) { diff --git a/lib/tsan/lit_tests/global_race.cc b/lib/tsan/lit_tests/global_race.cc index 0892d07da2cb..ac2016155575 100644 --- a/lib/tsan/lit_tests/global_race.cc +++ b/lib/tsan/lit_tests/global_race.cc @@ -1,25 +1,42 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stddef.h> int GlobalData[10]; +int x; +namespace XXX { + struct YYY { + static int ZZZ[10]; + }; + int YYY::ZZZ[10]; +} void *Thread(void *a) { GlobalData[2] = 42; + x = 1; + XXX::YYY::ZZZ[0] = 1; return 0; } int main() { fprintf(stderr, "addr=%p\n", GlobalData); + fprintf(stderr, "addr2=%p\n", &x); + fprintf(stderr, "addr3=%p\n", XXX::YYY::ZZZ); pthread_t t; pthread_create(&t, 0, Thread, 0); GlobalData[2] = 43; + x = 0; + XXX::YYY::ZZZ[0] = 0; pthread_join(t, 0); } // CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: addr2=[[ADDR2:0x[0-9,a-f]+]] +// CHECK: addr3=[[ADDR3:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'GlobalData' of size 40 at [[ADDR]] ({{.*}}+0x{{[0-9,a-f]+}}) +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'x' of size 4 at [[ADDR2]] ({{.*}}+0x{{[0-9,a-f]+}}) // CHECK: WARNING: ThreadSanitizer: data race -// Requires llvm-symbolizer, so disabled for now. -// CHECK0: Location is global 'GlobalData' of size 40 at [[ADDR]] -// CHECK0: (global_race.cc.exe+0x[0-9,a-f]+) +// CHECK: Location is global 'XXX::YYY::ZZZ' of size 40 at [[ADDR3]] ({{.*}}+0x{{[0-9,a-f]+}}) diff --git a/lib/tsan/lit_tests/halt_on_error.cc b/lib/tsan/lit_tests/halt_on_error.cc new file mode 100644 index 000000000000..fddaffff29aa --- /dev/null +++ b/lib/tsan/lit_tests/halt_on_error.cc @@ -0,0 +1,25 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS halt_on_error=1" not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +int X; + +void *Thread(void *x) { + X = 42; + return 0; +} + +int main() { + fprintf(stderr, "BEFORE\n"); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + X = 43; + pthread_join(t, 0); + fprintf(stderr, "AFTER\n"); + return 0; +} + +// CHECK: BEFORE +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: AFTER + diff --git a/lib/tsan/lit_tests/heap_race.cc b/lib/tsan/lit_tests/heap_race.cc index 297f8dbdec7d..cc2c1fee532b 100644 --- a/lib/tsan/lit_tests/heap_race.cc +++ b/lib/tsan/lit_tests/heap_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/ignore_free.cc b/lib/tsan/lit_tests/ignore_free.cc new file mode 100644 index 000000000000..60369cc1baa5 --- /dev/null +++ b/lib/tsan/lit_tests/ignore_free.cc @@ -0,0 +1,35 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" { +void AnnotateIgnoreReadsBegin(const char *f, int l); +void AnnotateIgnoreReadsEnd(const char *f, int l); +void AnnotateIgnoreWritesBegin(const char *f, int l); +void AnnotateIgnoreWritesEnd(const char *f, int l); +} + +void *Thread(void *p) { + *(int*)p = 42; + return 0; +} + +int main() { + int *p = new int(0); + pthread_t t; + pthread_create(&t, 0, Thread, p); + sleep(1); + AnnotateIgnoreReadsBegin(__FILE__, __LINE__); + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + free(p); + AnnotateIgnoreReadsEnd(__FILE__, __LINE__); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + pthread_join(t, 0); + fprintf(stderr, "OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/lib/tsan/lit_tests/ignore_lib0.cc b/lib/tsan/lit_tests/ignore_lib0.cc new file mode 100644 index 000000000000..ea0f061e609d --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib0.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib0.so +// RUN: %clangxx_tsan -O1 %s -L%T -lignore_lib0 -o %t +// RUN: echo running w/o suppressions: +// RUN: LD_LIBRARY_PATH=%T not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP +// RUN: echo running with suppressions: +// RUN: LD_LIBRARY_PATH=%T TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP + +// Tests that interceptors coming from a library specified in called_from_lib +// suppression are ignored. + +#ifndef LIB + +extern "C" void libfunc(); + +int main() { + libfunc(); +} + +#else // #ifdef LIB + +#include "ignore_lib_lib.h" + +#endif // #ifdef LIB + +// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race +// CHECK-NOSUPP: OK + +// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race +// CHECK-WITHSUPP: OK + diff --git a/lib/tsan/lit_tests/ignore_lib0.cc.supp b/lib/tsan/lit_tests/ignore_lib0.cc.supp new file mode 100644 index 000000000000..7728c926b7de --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib0.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:/libignore_lib0.so + diff --git a/lib/tsan/lit_tests/ignore_lib1.cc b/lib/tsan/lit_tests/ignore_lib1.cc new file mode 100644 index 000000000000..c4f2e7344135 --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib1.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib1.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: echo running w/o suppressions: +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP +// RUN: echo running with suppressions: +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP + +// Tests that interceptors coming from a dynamically loaded library specified +// in called_from_lib suppression are ignored. + +#ifndef LIB + +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <libgen.h> +#include <string> + +int main(int argc, char **argv) { + std::string lib = std::string(dirname(argv[0])) + "/libignore_lib1.so"; + void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW); + if (h == 0) + exit(printf("failed to load the library (%d)\n", errno)); + void (*f)() = (void(*)())dlsym(h, "libfunc"); + if (f == 0) + exit(printf("failed to find the func (%d)\n", errno)); + f(); +} + +#else // #ifdef LIB + +#include "ignore_lib_lib.h" + +#endif // #ifdef LIB + +// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race +// CHECK-NOSUPP: OK + +// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race +// CHECK-WITHSUPP: OK + diff --git a/lib/tsan/lit_tests/ignore_lib1.cc.supp b/lib/tsan/lit_tests/ignore_lib1.cc.supp new file mode 100644 index 000000000000..9f4119ec0bc4 --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib1.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:/libignore_lib1.so$ + diff --git a/lib/tsan/lit_tests/ignore_lib2.cc b/lib/tsan/lit_tests/ignore_lib2.cc new file mode 100644 index 000000000000..97f9419e4d89 --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib2.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_0.so +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_1.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" not %t 2>&1 | FileCheck %s + +// Tests that called_from_lib suppression matched against 2 libraries +// causes program crash (this is not supported). + +#ifndef LIB + +#include <dlfcn.h> +#include <stdio.h> +#include <libgen.h> +#include <string> + +int main(int argc, char **argv) { + std::string lib0 = std::string(dirname(argv[0])) + "/libignore_lib2_0.so"; + std::string lib1 = std::string(dirname(argv[0])) + "/libignore_lib2_1.so"; + dlopen(lib0.c_str(), RTLD_GLOBAL | RTLD_NOW); + dlopen(lib1.c_str(), RTLD_GLOBAL | RTLD_NOW); + fprintf(stderr, "OK\n"); +} + +#else // #ifdef LIB + +extern "C" void libfunc() { +} + +#endif // #ifdef LIB + +// CHECK: ThreadSanitizer: called_from_lib suppression 'ignore_lib2' is matched against 2 libraries +// CHECK-NOT: OK + diff --git a/lib/tsan/lit_tests/ignore_lib2.cc.supp b/lib/tsan/lit_tests/ignore_lib2.cc.supp new file mode 100644 index 000000000000..1419c71c67ef --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib2.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:ignore_lib2 + diff --git a/lib/tsan/lit_tests/ignore_lib3.cc b/lib/tsan/lit_tests/ignore_lib3.cc new file mode 100644 index 000000000000..8f237fcc81fd --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib3.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib3.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" not %t 2>&1 | FileCheck %s + +// Tests that unloading of a library matched against called_from_lib suppression +// causes program crash (this is not supported). + +#ifndef LIB + +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <libgen.h> +#include <string> + +int main(int argc, char **argv) { + std::string lib = std::string(dirname(argv[0])) + "/libignore_lib3.so"; + void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW); + dlclose(h); + fprintf(stderr, "OK\n"); +} + +#else // #ifdef LIB + +extern "C" void libfunc() { +} + +#endif // #ifdef LIB + +// CHECK: ThreadSanitizer: library {{.*}} that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded +// CHECK-NOT: OK + diff --git a/lib/tsan/lit_tests/ignore_lib3.cc.supp b/lib/tsan/lit_tests/ignore_lib3.cc.supp new file mode 100644 index 000000000000..975dbcef99fe --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib3.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:ignore_lib3.so + diff --git a/lib/tsan/lit_tests/ignore_lib_lib.h b/lib/tsan/lit_tests/ignore_lib_lib.h new file mode 100644 index 000000000000..2bfe84dfc0ec --- /dev/null +++ b/lib/tsan/lit_tests/ignore_lib_lib.h @@ -0,0 +1,25 @@ +#include <pthread.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +void *volatile mem; +volatile int len; + +void *Thread(void *p) { + while ((p = __atomic_load_n(&mem, __ATOMIC_ACQUIRE)) == 0) + usleep(100); + memset(p, 0, len); + return 0; +} + +extern "C" void libfunc() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + len = 10; + __atomic_store_n(&mem, malloc(len), __ATOMIC_RELEASE); + pthread_join(t, 0); + free(mem); + fprintf(stderr, "OK\n"); +} diff --git a/lib/tsan/lit_tests/ignore_malloc.cc b/lib/tsan/lit_tests/ignore_malloc.cc new file mode 100644 index 000000000000..63bd4241b59e --- /dev/null +++ b/lib/tsan/lit_tests/ignore_malloc.cc @@ -0,0 +1,38 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" { +void AnnotateIgnoreReadsBegin(const char *f, int l); +void AnnotateIgnoreReadsEnd(const char *f, int l); +void AnnotateIgnoreWritesBegin(const char *f, int l); +void AnnotateIgnoreWritesEnd(const char *f, int l); +} + +int *g; + +void *Thread(void *a) { + int *p = 0; + while ((p = __atomic_load_n(&g, __ATOMIC_RELAXED)) == 0) + usleep(100); + *p = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + int *p = new int(0); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + __atomic_store_n(&g, p, __ATOMIC_RELAXED); + pthread_join(t, 0); + delete p; + fprintf(stderr, "OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/lib/tsan/lit_tests/ignore_sync.cc b/lib/tsan/lit_tests/ignore_sync.cc new file mode 100644 index 000000000000..67f2d906d9c7 --- /dev/null +++ b/lib/tsan/lit_tests/ignore_sync.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +extern "C" void AnnotateIgnoreSyncBegin(const char*, int); +extern "C" void AnnotateIgnoreSyncEnd(const char*, int); + +int Global; +pthread_mutex_t Mutex = PTHREAD_MUTEX_INITIALIZER; + +void *Thread(void *x) { + AnnotateIgnoreSyncBegin(0, 0); + pthread_mutex_lock(&Mutex); + Global++; + pthread_mutex_unlock(&Mutex); + AnnotateIgnoreSyncEnd(0, 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_mutex_lock(&Mutex); + Global++; + pthread_mutex_unlock(&Mutex); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race + diff --git a/lib/tsan/lit_tests/inlined_memcpy_race.cc b/lib/tsan/lit_tests/inlined_memcpy_race.cc index 6efe5a956e9d..5dda36e4b9e7 100644 --- a/lib/tsan/lit_tests/inlined_memcpy_race.cc +++ b/lib/tsan/lit_tests/inlined_memcpy_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/java.h b/lib/tsan/lit_tests/java.h index 04094197edb7..7aa0bca32cec 100644 --- a/lib/tsan/lit_tests/java.h +++ b/lib/tsan/lit_tests/java.h @@ -5,6 +5,7 @@ extern "C" { typedef unsigned long jptr; // NOLINT +void __tsan_java_preinit(const char *libjvm_path); void __tsan_java_init(jptr heap_begin, jptr heap_size); int __tsan_java_fini(); void __tsan_java_alloc(jptr ptr, jptr size); diff --git a/lib/tsan/lit_tests/java_lock_rec_race.cc b/lib/tsan/lit_tests/java_lock_rec_race.cc index 61626aaddc0d..a868e260c86d 100644 --- a/lib/tsan/lit_tests/java_lock_rec_race.cc +++ b/lib/tsan/lit_tests/java_lock_rec_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include "java.h" #include <unistd.h> diff --git a/lib/tsan/lit_tests/java_race.cc b/lib/tsan/lit_tests/java_race.cc index 722bb6e8d09c..4841a7db0a9c 100644 --- a/lib/tsan/lit_tests/java_race.cc +++ b/lib/tsan/lit_tests/java_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include "java.h" void *Thread(void *p) { diff --git a/lib/tsan/lit_tests/java_race_move.cc b/lib/tsan/lit_tests/java_race_move.cc index bb63ea985c58..6da8a106483c 100644 --- a/lib/tsan/lit_tests/java_race_move.cc +++ b/lib/tsan/lit_tests/java_race_move.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include "java.h" jptr varaddr; diff --git a/lib/tsan/lit_tests/lit.cfg b/lib/tsan/lit_tests/lit.cfg index d483d2fcbdc6..c4193639f493 100644 --- a/lib/tsan/lit_tests/lit.cfg +++ b/lib/tsan/lit_tests/lit.cfg @@ -2,12 +2,15 @@ import os +import lit.util + def get_required_attr(config, attr_name): attr_value = getattr(config, attr_name, None) if not attr_value: - lit.fatal("No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) return attr_value # Setup config name. @@ -17,9 +20,9 @@ config.name = 'ThreadSanitizer' config.test_source_root = os.path.dirname(__file__) def DisplayNoConfigMessage(): - lit.fatal("No site specific configuration available! " + - "Try running your test from the build tree or running " + - "make check-tsan") + lit_config.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-tsan") # Figure out LLVM source root. llvm_src_root = getattr(config, 'llvm_src_root', None) @@ -27,9 +30,9 @@ if llvm_src_root is None: # We probably haven't loaded the site-specific configuration: the user # is likely trying to run a test file directly, and the site configuration # wasn't created by the build system. - tsan_site_cfg = lit.params.get('tsan_site_config', None) + tsan_site_cfg = lit_config.params.get('tsan_site_config', None) if (tsan_site_cfg) and (os.path.exists(tsan_site_cfg)): - lit.load_config(config, tsan_site_cfg) + lit_config.load_config(config, tsan_site_cfg) raise SystemExit # Try to guess the location of site-specific configuration using llvm-config @@ -45,25 +48,11 @@ if llvm_src_root is None: if (not tsan_site_cfg) or (not os.path.exists(tsan_site_cfg)): DisplayNoConfigMessage() - lit.load_config(config, tsan_site_cfg) + lit_config.load_config(config, tsan_site_cfg) raise SystemExit -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') -compiler_rt_lit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.cfg") -if (not compiler_rt_lit_cfg) or (not os.path.exists(compiler_rt_lit_cfg)): - lit.fatal("Can't find common compiler-rt lit config at: %r" - % compiler_rt_lit_cfg) -lit.load_config(config, compiler_rt_lit_cfg) - # Setup environment variables for running ThreadSanitizer. tsan_options = "atexit_sleep_ms=0" -# Get path to external LLVM symbolizer to run ThreadSanitizer output tests. -llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) -if llvm_tools_dir: - llvm_symbolizer_path = os.path.join(llvm_tools_dir, "llvm-symbolizer") - tsan_options += " " + "external_symbolizer_path=" + llvm_symbolizer_path config.environment['TSAN_OPTIONS'] = tsan_options @@ -73,8 +62,9 @@ clang_tsan_cflags = ("-fsanitize=thread " + "-g " + "-Wall " + "-lpthread " - + "-ldl ") -clang_tsan_cxxflags = "-ccc-cxx " + clang_tsan_cflags + + "-ldl " + + "-m64 ") +clang_tsan_cxxflags = "--driver-mode=g++ " + clang_tsan_cflags config.substitutions.append( ("%clangxx_tsan ", (" " + config.clang + " " + clang_tsan_cxxflags + " ")) ) config.substitutions.append( ("%clang_tsan ", (" " + config.clang + " " + diff --git a/lib/tsan/lit_tests/lit.site.cfg.in b/lib/tsan/lit_tests/lit.site.cfg.in index 07b521af061f..b0e427446eaa 100644 --- a/lib/tsan/lit_tests/lit.site.cfg.in +++ b/lib/tsan/lit_tests/lit.site.cfg.in @@ -1,20 +1,8 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! -config.clang = "@LLVM_BINARY_DIR@/bin/clang" -config.host_os = "@HOST_OS@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" -config.target_triple = "@TARGET_TRIPLE@" +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") -# LLVM tools dir can be passed in lit parameters, so try to -# apply substitution. -try: - config.llvm_tools_dir = config.llvm_tools_dir % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) - -# Let the main config do the real work. -lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/lib/tsan/lit_tests/load_shared_lib.cc b/lib/tsan/lit_tests/load_shared_lib.cc index dd6fa0964f4a..d60cd5700a8a 100644 --- a/lib/tsan/lit_tests/load_shared_lib.cc +++ b/lib/tsan/lit_tests/load_shared_lib.cc @@ -4,7 +4,7 @@ // RUN: %clangxx_tsan -O1 %p/SharedLibs/load_shared_lib-so.cc \ // RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <dlfcn.h> #include <pthread.h> diff --git a/lib/tsan/lit_tests/longjmp3.cc b/lib/tsan/lit_tests/longjmp3.cc index 87fabd0b3be2..ae2cfd05fe1a 100644 --- a/lib/tsan/lit_tests/longjmp3.cc +++ b/lib/tsan/lit_tests/longjmp3.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/lib/tsan/lit_tests/longjmp4.cc b/lib/tsan/lit_tests/longjmp4.cc index a8764dda5a6b..6b0526ef3a66 100644 --- a/lib/tsan/lit_tests/longjmp4.cc +++ b/lib/tsan/lit_tests/longjmp4.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/lib/tsan/lit_tests/malloc_overflow.cc b/lib/tsan/lit_tests/malloc_overflow.cc index 19423c5f93f1..afbebc8bec44 100644 --- a/lib/tsan/lit_tests/malloc_overflow.cc +++ b/lib/tsan/lit_tests/malloc_overflow.cc @@ -1,4 +1,5 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS=allocator_may_return_null=1 %t 2>&1 | FileCheck %s #include <stdio.h> #include <stdlib.h> diff --git a/lib/tsan/lit_tests/malloc_stack.cc b/lib/tsan/lit_tests/malloc_stack.cc index c185623ff5ca..3603497ef311 100644 --- a/lib/tsan/lit_tests/malloc_stack.cc +++ b/lib/tsan/lit_tests/malloc_stack.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/memcpy_race.cc b/lib/tsan/lit_tests/memcpy_race.cc index 857728ba0540..8f39113674d6 100644 --- a/lib/tsan/lit_tests/memcpy_race.cc +++ b/lib/tsan/lit_tests/memcpy_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/mop_with_offset.cc b/lib/tsan/lit_tests/mop_with_offset.cc index 0c11ef6b9187..2b6a4ff50aaf 100644 --- a/lib/tsan/lit_tests/mop_with_offset.cc +++ b/lib/tsan/lit_tests/mop_with_offset.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/mop_with_offset2.cc b/lib/tsan/lit_tests/mop_with_offset2.cc index ee0d64a0afbf..037c4db5f524 100644 --- a/lib/tsan/lit_tests/mop_with_offset2.cc +++ b/lib/tsan/lit_tests/mop_with_offset2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/mutex_destroy_locked.cc b/lib/tsan/lit_tests/mutex_destroy_locked.cc index 27a04248b172..9b020d31b94c 100644 --- a/lib/tsan/lit_tests/mutex_destroy_locked.cc +++ b/lib/tsan/lit_tests/mutex_destroy_locked.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutex_robust.cc b/lib/tsan/lit_tests/mutex_robust.cc new file mode 100644 index 000000000000..b826616076ae --- /dev/null +++ b/lib/tsan/lit_tests/mutex_robust.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +pthread_mutex_t m; + +void *thr(void *p) { + pthread_mutex_lock(&m); + return 0; +} + +int main() { + pthread_mutexattr_t a; + pthread_mutexattr_init(&a); + pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST); + pthread_mutex_init(&m, &a); + pthread_t th; + pthread_create(&th, 0, thr, 0); + sleep(1); + if (pthread_mutex_lock(&m) != EOWNERDEAD) { + fprintf(stderr, "not EOWNERDEAD\n"); + exit(1); + } + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); +} + +// This is a correct code, and tsan must not bark. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK-NOT: EOWNERDEAD +// CHECK: DONE +// CHECK-NOT: WARNING: ThreadSanitizer + diff --git a/lib/tsan/lit_tests/mutex_robust2.cc b/lib/tsan/lit_tests/mutex_robust2.cc new file mode 100644 index 000000000000..5bd7ff682d1b --- /dev/null +++ b/lib/tsan/lit_tests/mutex_robust2.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +pthread_mutex_t m; +int x; + +void *thr(void *p) { + pthread_mutex_lock(&m); + x = 42; + return 0; +} + +int main() { + pthread_mutexattr_t a; + pthread_mutexattr_init(&a); + pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST); + pthread_mutex_init(&m, &a); + pthread_t th; + pthread_create(&th, 0, thr, 0); + sleep(1); + if (pthread_mutex_trylock(&m) != EOWNERDEAD) { + fprintf(stderr, "not EOWNERDEAD\n"); + exit(1); + } + x = 43; + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); +} + +// This is a false positive, tsan must not bark at the data race. +// But currently it does. +// CHECK-NOT: WARNING: ThreadSanitizer WARNING: double lock of mutex +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: EOWNERDEAD +// CHECK: DONE +// CHECK-NOT: WARNING: ThreadSanitizer + diff --git a/lib/tsan/lit_tests/mutexset1.cc b/lib/tsan/lit_tests/mutexset1.cc index f32a770ab075..ca87a7ba047d 100644 --- a/lib/tsan/lit_tests/mutexset1.cc +++ b/lib/tsan/lit_tests/mutexset1.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset2.cc b/lib/tsan/lit_tests/mutexset2.cc index 15d230332512..9ccf952b0050 100644 --- a/lib/tsan/lit_tests/mutexset2.cc +++ b/lib/tsan/lit_tests/mutexset2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset3.cc b/lib/tsan/lit_tests/mutexset3.cc index 6ac7ad15e4f9..272ddafb3c4a 100644 --- a/lib/tsan/lit_tests/mutexset3.cc +++ b/lib/tsan/lit_tests/mutexset3.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset4.cc b/lib/tsan/lit_tests/mutexset4.cc index 75684cf9ae5b..be751fa92bf0 100644 --- a/lib/tsan/lit_tests/mutexset4.cc +++ b/lib/tsan/lit_tests/mutexset4.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset5.cc b/lib/tsan/lit_tests/mutexset5.cc index 6e75810aff22..e013edb4656a 100644 --- a/lib/tsan/lit_tests/mutexset5.cc +++ b/lib/tsan/lit_tests/mutexset5.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset6.cc b/lib/tsan/lit_tests/mutexset6.cc index 4b19a12e0434..f5e6e66becf8 100644 --- a/lib/tsan/lit_tests/mutexset6.cc +++ b/lib/tsan/lit_tests/mutexset6.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset7.cc b/lib/tsan/lit_tests/mutexset7.cc index 3ec1b5202983..51451b215490 100644 --- a/lib/tsan/lit_tests/mutexset7.cc +++ b/lib/tsan/lit_tests/mutexset7.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/mutexset8.cc b/lib/tsan/lit_tests/mutexset8.cc index 6db63f7d16db..8822b050e939 100644 --- a/lib/tsan/lit_tests/mutexset8.cc +++ b/lib/tsan/lit_tests/mutexset8.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/oob_race.cc b/lib/tsan/lit_tests/oob_race.cc index 2e7f0593fd8d..9d8e2220d9a5 100644 --- a/lib/tsan/lit_tests/oob_race.cc +++ b/lib/tsan/lit_tests/oob_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/race_on_barrier.c b/lib/tsan/lit_tests/race_on_barrier.c index 3e76f8bf5e20..3c0199dec22e 100644 --- a/lib/tsan/lit_tests/race_on_barrier.c +++ b/lib/tsan/lit_tests/race_on_barrier.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/race_on_barrier2.c b/lib/tsan/lit_tests/race_on_barrier2.c index 46a4f50b133d..62773d43e66e 100644 --- a/lib/tsan/lit_tests/race_on_barrier2.c +++ b/lib/tsan/lit_tests/race_on_barrier2.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/race_on_heap.cc b/lib/tsan/lit_tests/race_on_heap.cc index 35434eac1850..a84c0de96558 100644 --- a/lib/tsan/lit_tests/race_on_heap.cc +++ b/lib/tsan/lit_tests/race_on_heap.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdlib.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/race_on_mutex.c b/lib/tsan/lit_tests/race_on_mutex.c index aff32f9bb1a2..e66341414831 100644 --- a/lib/tsan/lit_tests/race_on_mutex.c +++ b/lib/tsan/lit_tests/race_on_mutex.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/race_on_mutex2.c b/lib/tsan/lit_tests/race_on_mutex2.c index 84bef75a3449..80c395e1f9ce 100644 --- a/lib/tsan/lit_tests/race_on_mutex2.c +++ b/lib/tsan/lit_tests/race_on_mutex2.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/race_on_read.cc b/lib/tsan/lit_tests/race_on_read.cc index 7d226814816e..4ca4b25bfa81 100644 --- a/lib/tsan/lit_tests/race_on_read.cc +++ b/lib/tsan/lit_tests/race_on_read.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/race_on_write.cc b/lib/tsan/lit_tests/race_on_write.cc index f1b0bb1cbd6e..8a56c8464b91 100644 --- a/lib/tsan/lit_tests/race_on_write.cc +++ b/lib/tsan/lit_tests/race_on_write.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/race_with_finished_thread.cc b/lib/tsan/lit_tests/race_with_finished_thread.cc index a267290e661e..c713c67a398e 100644 --- a/lib/tsan/lit_tests/race_with_finished_thread.cc +++ b/lib/tsan/lit_tests/race_with_finished_thread.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/signal_errno.cc b/lib/tsan/lit_tests/signal_errno.cc index 8181555f6f63..2febca38294e 100644 --- a/lib/tsan/lit_tests/signal_errno.cc +++ b/lib/tsan/lit_tests/signal_errno.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/lib/tsan/lit_tests/signal_malloc.cc b/lib/tsan/lit_tests/signal_malloc.cc index 4dbc2f78ab17..ef180b8a25b6 100644 --- a/lib/tsan/lit_tests/signal_malloc.cc +++ b/lib/tsan/lit_tests/signal_malloc.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <stdio.h> #include <stdlib.h> #include <signal.h> diff --git a/lib/tsan/lit_tests/sigsuspend.cc b/lib/tsan/lit_tests/sigsuspend.cc new file mode 100644 index 000000000000..78d507fa0af5 --- /dev/null +++ b/lib/tsan/lit_tests/sigsuspend.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <assert.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <stdio.h> + +static bool signal_handler_ran = false; + +void do_nothing_signal_handler(int signum) { + write(1, "HANDLER\n", 8); + signal_handler_ran = true; +} + +int main() { + const int kSignalToTest = SIGSYS; + assert(SIG_ERR != signal(kSignalToTest, do_nothing_signal_handler)); + sigset_t empty_set; + assert(0 == sigemptyset(&empty_set)); + sigset_t one_signal = empty_set; + assert(0 == sigaddset(&one_signal, kSignalToTest)); + sigset_t old_set; + assert(0 == sigprocmask(SIG_BLOCK, &one_signal, &old_set)); + raise(kSignalToTest); + assert(!signal_handler_ran); + sigset_t all_but_one; + assert(0 == sigfillset(&all_but_one)); + assert(0 == sigdelset(&all_but_one, kSignalToTest)); + sigsuspend(&all_but_one); + assert(signal_handler_ran); + + // Restore the original set. + assert(0 == sigprocmask(SIG_SETMASK, &old_set, NULL)); + printf("DONE"); +} + +// CHECK: HANDLER +// CHECK: DONE diff --git a/lib/tsan/lit_tests/simple_race.c b/lib/tsan/lit_tests/simple_race.c index 44aff897406a..80a83e01a294 100644 --- a/lib/tsan/lit_tests/simple_race.c +++ b/lib/tsan/lit_tests/simple_race.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/simple_race.cc b/lib/tsan/lit_tests/simple_race.cc index 99cf228ac2f2..47854cfd9a3e 100644 --- a/lib/tsan/lit_tests/simple_race.cc +++ b/lib/tsan/lit_tests/simple_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/simple_stack.c b/lib/tsan/lit_tests/simple_stack.c index 4539cb7c1f37..a447e2880447 100644 --- a/lib/tsan/lit_tests/simple_stack.c +++ b/lib/tsan/lit_tests/simple_stack.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/simple_stack2.cc b/lib/tsan/lit_tests/simple_stack2.cc index bf27a15ffad5..7a034c4cd6ed 100644 --- a/lib/tsan/lit_tests/simple_stack2.cc +++ b/lib/tsan/lit_tests/simple_stack2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/sleep_sync.cc b/lib/tsan/lit_tests/sleep_sync.cc index c3d47d311749..217a52a097ce 100644 --- a/lib/tsan/lit_tests/sleep_sync.cc +++ b/lib/tsan/lit_tests/sleep_sync.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/sleep_sync2.cc b/lib/tsan/lit_tests/sleep_sync2.cc index d9961bccc808..e22999279f9f 100644 --- a/lib/tsan/lit_tests/sleep_sync2.cc +++ b/lib/tsan/lit_tests/sleep_sync2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/stack_race.cc b/lib/tsan/lit_tests/stack_race.cc index beeb57353bf3..7fabce22a85c 100644 --- a/lib/tsan/lit_tests/stack_race.cc +++ b/lib/tsan/lit_tests/stack_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/stack_race2.cc b/lib/tsan/lit_tests/stack_race2.cc index 5bdf1bd664a1..c759ec92774f 100644 --- a/lib/tsan/lit_tests/stack_race2.cc +++ b/lib/tsan/lit_tests/stack_race2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/static_init3.cc b/lib/tsan/lit_tests/static_init3.cc index 40fd4b940f55..70a3c16878ca 100644 --- a/lib/tsan/lit_tests/static_init3.cc +++ b/lib/tsan/lit_tests/static_init3.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdlib.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/suppress_same_address.cc b/lib/tsan/lit_tests/suppress_same_address.cc index 174d1cc8fcb3..c516f89529f3 100644 --- a/lib/tsan/lit_tests/suppress_same_address.cc +++ b/lib/tsan/lit_tests/suppress_same_address.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> int X; diff --git a/lib/tsan/lit_tests/suppress_same_stacks.cc b/lib/tsan/lit_tests/suppress_same_stacks.cc index 32bff9d50071..f0ab8b30435e 100644 --- a/lib/tsan/lit_tests/suppress_same_stacks.cc +++ b/lib/tsan/lit_tests/suppress_same_stacks.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> volatile int N; // Prevent loop unrolling. diff --git a/lib/tsan/lit_tests/suppressions_global.cc b/lib/tsan/lit_tests/suppressions_global.cc new file mode 100644 index 000000000000..181cb56cf2e6 --- /dev/null +++ b/lib/tsan/lit_tests/suppressions_global.cc @@ -0,0 +1,29 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +int RacyGlobal; + +void *Thread1(void *x) { + RacyGlobal = 42; + return NULL; +} + +void *Thread2(void *x) { + RacyGlobal = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/lib/tsan/lit_tests/suppressions_global.cc.supp b/lib/tsan/lit_tests/suppressions_global.cc.supp new file mode 100644 index 000000000000..5fa8a2e43a93 --- /dev/null +++ b/lib/tsan/lit_tests/suppressions_global.cc.supp @@ -0,0 +1,2 @@ +race:RacyGlobal + diff --git a/lib/tsan/lit_tests/suppressions_race.cc b/lib/tsan/lit_tests/suppressions_race.cc new file mode 100644 index 000000000000..c88e69bec6a3 --- /dev/null +++ b/lib/tsan/lit_tests/suppressions_race.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/lib/tsan/lit_tests/suppressions_race.cc.supp b/lib/tsan/lit_tests/suppressions_race.cc.supp new file mode 100644 index 000000000000..cbdba76ea93a --- /dev/null +++ b/lib/tsan/lit_tests/suppressions_race.cc.supp @@ -0,0 +1,2 @@ +race:Thread1 + diff --git a/lib/tsan/lit_tests/suppressions_race2.cc b/lib/tsan/lit_tests/suppressions_race2.cc new file mode 100644 index 000000000000..57146f96a428 --- /dev/null +++ b/lib/tsan/lit_tests/suppressions_race2.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/lib/tsan/lit_tests/suppressions_race2.cc.supp b/lib/tsan/lit_tests/suppressions_race2.cc.supp new file mode 100644 index 000000000000..b3c4dbc59363 --- /dev/null +++ b/lib/tsan/lit_tests/suppressions_race2.cc.supp @@ -0,0 +1,2 @@ +race:Thread2 + diff --git a/lib/tsan/lit_tests/test_output.sh b/lib/tsan/lit_tests/test_output.sh index 1eedf6eb20a3..79e773aa2c98 100755 --- a/lib/tsan/lit_tests/test_output.sh +++ b/lib/tsan/lit_tests/test_output.sh @@ -13,7 +13,7 @@ BLACKLIST=$ROOTDIR/lit_tests/Helpers/blacklist.txt # TODO: add testing for all of -O0...-O3 CFLAGS="-fsanitize=thread -fsanitize-blacklist=$BLACKLIST -fPIE -O1 -g -Wall" -LDFLAGS="-pie -lpthread -ldl $ROOTDIR/rtl/libtsan.a" +LDFLAGS="-pie -lpthread -ldl -lrt -Wl,--whole-archive $ROOTDIR/rtl/libtsan.a -Wl,--no-whole-archive" test_file() { SRC=$1 @@ -40,6 +40,10 @@ if [ "$1" == "" ]; then echo TEST $c is not supported continue fi + if [ "`grep "TSAN_OPTIONS" $c`" ]; then + echo SKIPPING $c -- requires TSAN_OPTIONS + continue + fi COMPILER=$CXX case $c in *.c) COMPILER=$CC diff --git a/lib/tsan/lit_tests/thread_leak3.c b/lib/tsan/lit_tests/thread_leak3.c index 3577164cad4a..5f447dbdbdf3 100644 --- a/lib/tsan/lit_tests/thread_leak3.c +++ b/lib/tsan/lit_tests/thread_leak3.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/thread_leak5.c b/lib/tsan/lit_tests/thread_leak5.c index fc72b149ec25..329f7233a38a 100644 --- a/lib/tsan/lit_tests/thread_leak5.c +++ b/lib/tsan/lit_tests/thread_leak5.c @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/thread_name.cc b/lib/tsan/lit_tests/thread_name.cc index 37f308ffbc0c..646ab5836241 100644 --- a/lib/tsan/lit_tests/thread_name.cc +++ b/lib/tsan/lit_tests/thread_name.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/thread_name2.cc b/lib/tsan/lit_tests/thread_name2.cc new file mode 100644 index 000000000000..8c5cb741f61f --- /dev/null +++ b/lib/tsan/lit_tests/thread_name2.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global++; + return 0; +} + +void *Thread2(void *x) { + pthread_setname_np(pthread_self(), "foobar2"); + Global--; + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread2, 0); + pthread_setname_np(t[0], "foobar1"); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Thread T1 'foobar1' +// CHECK: Thread T2 'foobar2' + diff --git a/lib/tsan/lit_tests/tiny_race.c b/lib/tsan/lit_tests/tiny_race.c index 44cc1332f2ab..f77e1606c1dd 100644 --- a/lib/tsan/lit_tests/tiny_race.c +++ b/lib/tsan/lit_tests/tiny_race.c @@ -1,15 +1,21 @@ -// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> +#include <unistd.h> + int Global; + void *Thread1(void *x) { + sleep(1); Global = 42; return x; } + int main() { pthread_t t; - pthread_create(&t, NULL, Thread1, NULL); + pthread_create(&t, 0, Thread1, 0); Global = 43; - pthread_join(t, NULL); + pthread_join(t, 0); return Global; } + // CHECK: WARNING: ThreadSanitizer: data race diff --git a/lib/tsan/lit_tests/tls_race.cc b/lib/tsan/lit_tests/tls_race.cc index bed6aafaacfc..3cbcc9dbba42 100644 --- a/lib/tsan/lit_tests/tls_race.cc +++ b/lib/tsan/lit_tests/tls_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> diff --git a/lib/tsan/lit_tests/tls_race2.cc b/lib/tsan/lit_tests/tls_race2.cc index 110abaa6a9df..136087065c12 100644 --- a/lib/tsan/lit_tests/tls_race2.cc +++ b/lib/tsan/lit_tests/tls_race2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stddef.h> #include <unistd.h> diff --git a/lib/tsan/lit_tests/unaligned_race.cc b/lib/tsan/lit_tests/unaligned_race.cc index 18bed8555cc5..6ac87b577ec5 100644 --- a/lib/tsan/lit_tests/unaligned_race.cc +++ b/lib/tsan/lit_tests/unaligned_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/lib/tsan/lit_tests/vptr_harmful_race.cc b/lib/tsan/lit_tests/vptr_harmful_race.cc index 76d31c00ad4f..0105c4cedd99 100644 --- a/lib/tsan/lit_tests/vptr_harmful_race.cc +++ b/lib/tsan/lit_tests/vptr_harmful_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <semaphore.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/vptr_harmful_race2.cc b/lib/tsan/lit_tests/vptr_harmful_race2.cc index d7e1d19a11bd..378790c62340 100644 --- a/lib/tsan/lit_tests/vptr_harmful_race2.cc +++ b/lib/tsan/lit_tests/vptr_harmful_race2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <semaphore.h> #include <stdio.h> diff --git a/lib/tsan/lit_tests/write_in_reader_lock.cc b/lib/tsan/lit_tests/write_in_reader_lock.cc index db8bac32b6e4..e872fe3ff960 100644 --- a/lib/tsan/lit_tests/write_in_reader_lock.cc +++ b/lib/tsan/lit_tests/write_in_reader_lock.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s #include <pthread.h> #include <unistd.h> diff --git a/lib/tsan/rtl/CMakeLists.txt b/lib/tsan/rtl/CMakeLists.txt deleted file mode 100644 index f1a8ff4d6558..000000000000 --- a/lib/tsan/rtl/CMakeLists.txt +++ /dev/null @@ -1,51 +0,0 @@ -set(TSAN_SOURCES - tsan_clock.cc - tsan_flags.cc - tsan_fd.cc - tsan_interceptors.cc - tsan_interface_ann.cc - tsan_interface_atomic.cc - tsan_interface.cc - tsan_interface_java.cc - tsan_md5.cc - tsan_mman.cc - tsan_mutex.cc - tsan_mutexset.cc - tsan_report.cc - tsan_rtl.cc - tsan_rtl_mutex.cc - tsan_rtl_report.cc - tsan_rtl_thread.cc - tsan_stat.cc - tsan_suppressions.cc - tsan_symbolize.cc - tsan_sync.cc - ) - -if(APPLE) - list(APPEND TSAN_SOURCES tsan_platform_mac.cc) -elseif(UNIX) - # Assume Linux - list(APPEND TSAN_SOURCES - tsan_platform_linux.cc - tsan_symbolize_addr2line_linux.cc) -endif() - -set(TSAN_RUNTIME_LIBRARIES) -# TSan is currently supported on 64-bit Linux only. -if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE) - set(TSAN_ASM_SOURCES tsan_rtl_amd64.S) - # Pass ASM file directly to the C++ compiler. - set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES - LANGUAGE C) - set(arch "x86_64") - add_compiler_rt_static_runtime(clang_rt.tsan-${arch} ${arch} - SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES} - $<TARGET_OBJECTS:RTInterception.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> - CFLAGS ${TSAN_CFLAGS} - DEFS ${TSAN_COMMON_DEFINITIONS} - SYMS tsan.syms) - list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch}) -endif() diff --git a/lib/tsan/rtl/tsan.syms b/lib/tsan/rtl/tsan.syms deleted file mode 100644 index 4464a0a231c9..000000000000 --- a/lib/tsan/rtl/tsan.syms +++ /dev/null @@ -1,5 +0,0 @@ -{ - __tsan_*; - __sanitizer_syscall_pre_*; - __sanitizer_syscall_post_*; -}; diff --git a/lib/tsan/rtl/tsan.syms.extra b/lib/tsan/rtl/tsan.syms.extra new file mode 100644 index 000000000000..49ed6b46629e --- /dev/null +++ b/lib/tsan/rtl/tsan.syms.extra @@ -0,0 +1,14 @@ +__tsan_init +__tsan_read* +__tsan_write* +__tsan_vptr* +__tsan_func* +__tsan_atomic* +__tsan_java* +__tsan_unaligned* +__tsan_release +__tsan_acquire +Annotate* +WTFAnnotate* +RunningOnValgrind +ValgrindSlowdown diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h index 7150e2e255d8..1e53a8e02f0a 100644 --- a/lib/tsan/rtl/tsan_defs.h +++ b/lib/tsan/rtl/tsan_defs.h @@ -41,10 +41,8 @@ const int kTidBits = 13; const unsigned kMaxTid = 1 << kTidBits; const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit. const int kClkBits = 42; -#ifndef TSAN_GO -const int kShadowStackSize = 4 * 1024; -const int kTraceStackSize = 256; -#endif +const uptr kShadowStackSize = 64 * 1024; +const uptr kTraceStackSize = 256; #ifdef TSAN_SHADOW_COUNT # if TSAN_SHADOW_COUNT == 2 \ @@ -162,7 +160,6 @@ class ReportDesc; class RegionAlloc; class StackTrace; struct MBlock; -struct Suppression; } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_fd.cc b/lib/tsan/rtl/tsan_fd.cc index 14bdbb53b322..86db119fc918 100644 --- a/lib/tsan/rtl/tsan_fd.cc +++ b/lib/tsan/rtl/tsan_fd.cc @@ -42,6 +42,11 @@ struct FdContext { static FdContext fdctx; +static bool bogusfd(int fd) { + // Apparently a bogus fd value. + return fd < 0 || fd >= (1 << 30); +} + static FdSync *allocsync() { FdSync *s = (FdSync*)internal_alloc(MBlockFD, sizeof(FdSync)); atomic_store(&s->rc, 1, memory_order_relaxed); @@ -69,6 +74,7 @@ static void unref(ThreadState *thr, uptr pc, FdSync *s) { } static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) { + CHECK_GE(fd, 0); CHECK_LT(fd, kTableSize); atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2]; uptr l1 = atomic_load(pl1, memory_order_consume); @@ -148,6 +154,8 @@ bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) { } void FdAcquire(ThreadState *thr, uptr pc, int fd) { + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); FdSync *s = d->sync; DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s); @@ -157,22 +165,28 @@ void FdAcquire(ThreadState *thr, uptr pc, int fd) { } void FdRelease(ThreadState *thr, uptr pc, int fd) { + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); FdSync *s = d->sync; DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); if (s) Release(thr, pc, (uptr)s); - MemoryRead(thr, pc, (uptr)d, kSizeLog8); } void FdAccess(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); MemoryRead(thr, pc, (uptr)d, kSizeLog8); } void FdClose(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdClose(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); // To catch races between fd usage and close. MemoryWrite(thr, pc, (uptr)d, kSizeLog8); @@ -187,11 +201,15 @@ void FdClose(ThreadState *thr, uptr pc, int fd) { void FdFileCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, &fdctx.filesync); } void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) { DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd); + if (bogusfd(oldfd) || bogusfd(newfd)) + return; // Ignore the case when user dups not yet connected socket. FdDesc *od = fddesc(thr, pc, oldfd); MemoryRead(thr, pc, (uptr)od, kSizeLog8); @@ -209,32 +227,44 @@ void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) { void FdEventCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, allocsync()); } void FdSignalCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, 0); } void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, 0); } void FdPollCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, allocsync()); } void FdSocketCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; // It can be a UDP socket. init(thr, pc, fd, &fdctx.socksync); } void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) { DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd); + if (bogusfd(fd)) + return; // Synchronize connect->accept. Acquire(thr, pc, (uptr)&fdctx.connectsync); init(thr, pc, newfd, &fdctx.socksync); @@ -242,12 +272,16 @@ void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) { void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; // Synchronize connect->accept. Release(thr, pc, (uptr)&fdctx.connectsync); } void FdSocketConnect(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, &fdctx.socksync); } diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc index c062592f482d..c6f24bf49abd 100644 --- a/lib/tsan/rtl/tsan_flags.cc +++ b/lib/tsan/rtl/tsan_flags.cc @@ -26,13 +26,42 @@ Flags *flags() { // Can be overriden in frontend. #ifdef TSAN_EXTERNAL_HOOKS void OverrideFlags(Flags *f); +extern "C" const char* __tsan_default_options(); #else -SANITIZER_INTERFACE_ATTRIBUTE void WEAK OverrideFlags(Flags *f) { (void)f; } +extern "C" const char *WEAK __tsan_default_options() { + return ""; +} #endif +static void ParseFlags(Flags *f, const char *env) { + ParseFlag(env, &f->enable_annotations, "enable_annotations"); + ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks"); + ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses"); + ParseFlag(env, &f->suppress_java, "suppress_java"); + ParseFlag(env, &f->report_bugs, "report_bugs"); + ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks"); + ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked"); + ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe"); + ParseFlag(env, &f->report_atomic_races, "report_atomic_races"); + ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics"); + ParseFlag(env, &f->suppressions, "suppressions"); + ParseFlag(env, &f->print_suppressions, "print_suppressions"); + ParseFlag(env, &f->print_benign, "print_benign"); + ParseFlag(env, &f->exitcode, "exitcode"); + ParseFlag(env, &f->halt_on_error, "halt_on_error"); + ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms"); + ParseFlag(env, &f->profile_memory, "profile_memory"); + ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms"); + ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms"); + ParseFlag(env, &f->memory_limit_mb, "memory_limit_mb"); + ParseFlag(env, &f->stop_on_start, "stop_on_start"); + ParseFlag(env, &f->history_size, "history_size"); + ParseFlag(env, &f->io_sync, "io_sync"); +} + void InitializeFlags(Flags *f, const char *env) { internal_memset(f, 0, sizeof(*f)); @@ -47,53 +76,35 @@ void InitializeFlags(Flags *f, const char *env) { f->report_signal_unsafe = true; f->report_atomic_races = true; f->force_seq_cst_atomics = false; - f->strip_path_prefix = ""; f->suppressions = ""; f->print_suppressions = false; f->print_benign = false; f->exitcode = 66; - f->log_path = "stderr"; + f->halt_on_error = false; f->atexit_sleep_ms = 1000; - f->verbosity = 0; f->profile_memory = ""; f->flush_memory_ms = 0; f->flush_symbolizer_ms = 5000; + f->memory_limit_mb = 0; f->stop_on_start = false; f->running_on_valgrind = false; - f->external_symbolizer_path = ""; f->history_size = kGoMode ? 1 : 2; // There are a lot of goroutines in Go. f->io_sync = 1; + CommonFlags *cf = common_flags(); + SetCommonFlagDefaults(); + *static_cast<CommonFlags*>(f) = *cf; + // Let a frontend override. OverrideFlags(f); - + ParseFlags(f, __tsan_default_options()); + ParseCommonFlagsFromString(__tsan_default_options()); // Override from command line. - ParseFlag(env, &f->enable_annotations, "enable_annotations"); - ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks"); - ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses"); - ParseFlag(env, &f->suppress_java, "suppress_java"); - ParseFlag(env, &f->report_bugs, "report_bugs"); - ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks"); - ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked"); - ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe"); - ParseFlag(env, &f->report_atomic_races, "report_atomic_races"); - ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics"); - ParseFlag(env, &f->strip_path_prefix, "strip_path_prefix"); - ParseFlag(env, &f->suppressions, "suppressions"); - ParseFlag(env, &f->print_suppressions, "print_suppressions"); - ParseFlag(env, &f->print_benign, "print_benign"); - ParseFlag(env, &f->exitcode, "exitcode"); - ParseFlag(env, &f->log_path, "log_path"); - ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms"); - ParseFlag(env, &f->verbosity, "verbosity"); - ParseFlag(env, &f->profile_memory, "profile_memory"); - ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms"); - ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms"); - ParseFlag(env, &f->stop_on_start, "stop_on_start"); - ParseFlag(env, &f->external_symbolizer_path, "external_symbolizer_path"); - ParseFlag(env, &f->history_size, "history_size"); - ParseFlag(env, &f->io_sync, "io_sync"); + ParseFlags(f, env); + ParseCommonFlagsFromString(env); + *static_cast<CommonFlags*>(f) = *cf; + // Sanity check. if (!f->report_bugs) { f->report_thread_leaks = false; f->report_destroy_locked = false; diff --git a/lib/tsan/rtl/tsan_flags.h b/lib/tsan/rtl/tsan_flags.h index aaacd98a6223..3916df3cc9e1 100644 --- a/lib/tsan/rtl/tsan_flags.h +++ b/lib/tsan/rtl/tsan_flags.h @@ -20,9 +20,11 @@ // header may be included in the user code, and shouldn't include // other headers from TSan or common sanitizer runtime. +#include "sanitizer_common/sanitizer_flags.h" + namespace __tsan { -struct Flags { +struct Flags : CommonFlags { // Enable dynamic annotations, otherwise they are no-ops. bool enable_annotations; // Supress a race report if we've already output another race report @@ -48,8 +50,6 @@ struct Flags { // If set, all atomics are effectively sequentially consistent (seq_cst), // regardless of what user actually specified. bool force_seq_cst_atomics; - // Strip that prefix from file paths in reports. - const char *strip_path_prefix; // Suppressions filename. const char *suppressions; // Print matched suppressions at exit. @@ -58,27 +58,24 @@ struct Flags { bool print_benign; // Override exit status if something was reported. int exitcode; - // Write logs to "log_path.pid". - // The special values are "stdout" and "stderr". - // The default is "stderr". - const char *log_path; + // Exit after first reported error. + bool halt_on_error; // Sleep in main thread before exiting for that many ms // (useful to catch "at exit" races). int atexit_sleep_ms; - // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). - int verbosity; // If set, periodically write memory profile to that file. const char *profile_memory; // Flush shadow memory every X ms. int flush_memory_ms; // Flush symbolizer caches every X ms. int flush_symbolizer_ms; + // Resident memory limit in MB to aim at. + // If the process consumes more memory, then TSan will flush shadow memory. + int memory_limit_mb; // Stops on start until __tsan_resume() is called (for debugging). bool stop_on_start; // Controls whether RunningOnValgrind() returns true or false. bool running_on_valgrind; - // Path to external symbolizer. - const char *external_symbolizer_path; // Per-thread history size, controls how many previous memory accesses // are remembered per thread. Possible values are [0..7]. // history_size=0 amounts to 32K memory accesses. Each next value doubles diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc index f18b26f6abe4..ef38f7987cde 100644 --- a/lib/tsan/rtl/tsan_interceptors.cc +++ b/lib/tsan/rtl/tsan_interceptors.cc @@ -22,6 +22,7 @@ #include "interception/interception.h" #include "tsan_interface.h" #include "tsan_platform.h" +#include "tsan_suppressions.h" #include "tsan_rtl.h" #include "tsan_mman.h" #include "tsan_fd.h" @@ -35,11 +36,6 @@ struct my_siginfo_t { u64 opaque[128 / sizeof(u64)]; }; -struct sigset_t { - // The size is determined by looking at sizeof of real sigset_t on linux. - u64 val[128 / sizeof(u64)]; -}; - struct ucontext_t { // The size is determined by looking at sizeof of real ucontext_t on linux. u64 opaque[936 / sizeof(u64) + 1]; @@ -47,15 +43,16 @@ struct ucontext_t { extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); -extern "C" int pthread_attr_getdetachstate(void *attr, int *v); +DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *) extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); -extern "C" int pthread_attr_getstacksize(void *attr, uptr *stacksize); extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v)); extern "C" int pthread_setspecific(unsigned key, const void *v); extern "C" int pthread_mutexattr_gettype(void *a, int *type); extern "C" int pthread_yield(); -extern "C" int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset); -extern "C" int sigfillset(sigset_t *set); +extern "C" int pthread_sigmask(int how, const __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset); +// REAL(sigfillset) defined in common interceptors. +DECLARE_REAL(int, sigfillset, __sanitizer_sigset_t *set) extern "C" void *pthread_self(); extern "C" void _exit(int status); extern "C" int *__errno_location(); @@ -67,9 +64,9 @@ extern "C" void __libc_free(void *ptr); extern "C" int mallopt(int param, int value); const int PTHREAD_MUTEX_RECURSIVE = 1; const int PTHREAD_MUTEX_RECURSIVE_NP = 1; -const int kPthreadAttrSize = 56; const int EINVAL = 22; const int EBUSY = 16; +const int EOWNERDEAD = 130; const int EPOLL_CTL_ADD = 1; const int SIGILL = 4; const int SIGABRT = 6; @@ -77,6 +74,7 @@ const int SIGFPE = 8; const int SIGSEGV = 11; const int SIGPIPE = 13; const int SIGBUS = 7; +const int SIGSYS = 31; void *const MAP_FAILED = (void*)-1; const int PTHREAD_BARRIER_SERIAL_THREAD = -1; const int MAP_FIXED = 0x10; @@ -97,7 +95,7 @@ struct sigaction_t { sighandler_t sa_handler; void (*sa_sigaction)(int sig, my_siginfo_t *siginfo, void *uctx); }; - sigset_t sa_mask; + __sanitizer_sigset_t sa_mask; int sa_flags; void (*sa_restorer)(); }; @@ -128,6 +126,19 @@ struct SignalContext { int pending_signal_count; SignalDesc pending_signals[kSigCount]; }; + +// The object is 64-byte aligned, because we want hot data to be located in +// a single cache line if possible (it's accessed in every interceptor). +static ALIGNED(64) char libignore_placeholder[sizeof(LibIgnore)]; +static LibIgnore *libignore() { + return reinterpret_cast<LibIgnore*>(&libignore_placeholder[0]); +} + +void InitializeLibIgnore() { + libignore()->Init(*GetSuppressionContext()); + libignore()->OnLibraryLoaded(0); +} + } // namespace __tsan static SignalContext *SigCtx(ThreadState *thr) { @@ -150,12 +161,14 @@ class ScopedInterceptor { private: ThreadState *const thr_; const int in_rtl_; + bool in_ignored_lib_; }; ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc) : thr_(thr) - , in_rtl_(thr->in_rtl) { + , in_rtl_(thr->in_rtl) + , in_ignored_lib_(false) { if (thr_->in_rtl == 0) { Initialize(thr); FuncEntry(thr, pc); @@ -164,9 +177,18 @@ ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname, } else { thr_->in_rtl++; } + if (!thr_->in_ignored_lib && libignore()->IsIgnored(pc)) { + in_ignored_lib_ = true; + thr_->in_ignored_lib = true; + ThreadIgnoreBegin(thr_); + } } ScopedInterceptor::~ScopedInterceptor() { + if (in_ignored_lib_) { + thr_->in_ignored_lib = false; + ThreadIgnoreEnd(thr_); + } thr_->in_rtl--; if (thr_->in_rtl == 0) { FuncExit(thr_); @@ -181,8 +203,7 @@ ScopedInterceptor::~ScopedInterceptor() { StatInc(thr, StatInt_##func); \ const uptr caller_pc = GET_CALLER_PC(); \ ScopedInterceptor si(thr, #func, caller_pc); \ - const uptr pc = __sanitizer::StackTrace::GetPreviousInstructionPc( \ - __sanitizer::StackTrace::GetCurrentPc()); \ + const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ (void)pc; \ /**/ @@ -192,7 +213,7 @@ ScopedInterceptor::~ScopedInterceptor() { Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ Die(); \ } \ - if (thr->in_rtl > 1) \ + if (thr->in_rtl > 1 || thr->in_ignored_lib) \ return REAL(func)(__VA_ARGS__); \ /**/ @@ -235,6 +256,28 @@ TSAN_INTERCEPTOR(int, nanosleep, void *req, void *rem) { return res; } +TSAN_INTERCEPTOR(void*, dlopen, const char *filename, int flag) { + SCOPED_INTERCEPTOR_RAW(dlopen, filename, flag); + // dlopen will execute global constructors, so it must be not in rtl. + CHECK_EQ(thr->in_rtl, 1); + thr->in_rtl = 0; + void *res = REAL(dlopen)(filename, flag); + thr->in_rtl = 1; + libignore()->OnLibraryLoaded(filename); + return res; +} + +TSAN_INTERCEPTOR(int, dlclose, void *handle) { + SCOPED_INTERCEPTOR_RAW(dlclose, handle); + // dlclose will execute global destructors, so it must be not in rtl. + CHECK_EQ(thr->in_rtl, 1); + thr->in_rtl = 0; + int res = REAL(dlclose)(handle); + thr->in_rtl = 1; + libignore()->OnLibraryUnloaded(); + return res; +} + class AtExitContext { public: AtExitContext() @@ -314,8 +357,14 @@ TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { if (cur_thread()->in_symbolizer) return 0; SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso); - if (dso) - return REAL(__cxa_atexit)(f, arg, dso); + if (dso) { + // Memory allocation in __cxa_atexit will race with free during exit, + // because we do not see synchronization around atexit callback list. + ThreadIgnoreBegin(thr); + int res = REAL(__cxa_atexit)(f, arg, dso); + ThreadIgnoreEnd(thr); + return res; + } return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg); } @@ -362,27 +411,37 @@ static void LongJmp(ThreadState *thr, uptr *env) { CHECK(0); } +// FIXME: put everything below into a common extern "C" block? extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) { ScopedInRtl in_rtl; SetJmp(cur_thread(), sp, mangled_sp); } // Not called. Merely to satisfy TSAN_INTERCEPT(). +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor_setjmp(void *env); extern "C" int __interceptor_setjmp(void *env) { CHECK(0); return 0; } +// FIXME: any reason to have a separate declaration? +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor__setjmp(void *env); extern "C" int __interceptor__setjmp(void *env) { CHECK(0); return 0; } +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor_sigsetjmp(void *env); extern "C" int __interceptor_sigsetjmp(void *env) { CHECK(0); return 0; } +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor___sigsetjmp(void *env); extern "C" int __interceptor___sigsetjmp(void *env) { CHECK(0); return 0; @@ -433,7 +492,8 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { if (cur_thread()->in_symbolizer) return __libc_calloc(size, n); - if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) return 0; + if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) + return AllocatorReturnNull(); void *p = 0; { SCOPED_INTERCEPTOR_RAW(calloc, size, n); @@ -494,15 +554,26 @@ TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) { invoke_malloc_hook(p, size); \ return p; +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new(__sanitizer::uptr size); void *operator new(__sanitizer::uptr size) { OPERATOR_NEW_BODY(_Znwm); } + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new[](__sanitizer::uptr size); void *operator new[](__sanitizer::uptr size) { OPERATOR_NEW_BODY(_Znam); } + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new(__sanitizer::uptr size, std::nothrow_t const&); void *operator new(__sanitizer::uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t); } + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new[](__sanitizer::uptr size, std::nothrow_t const&); void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t); } @@ -515,15 +586,26 @@ void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ user_free(thr, pc, ptr); +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete(void *ptr); void operator delete(void *ptr) { OPERATOR_DELETE_BODY(_ZdlPv); } + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete[](void *ptr); void operator delete[](void *ptr) { OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t); } + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const&); void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(_ZdaPv); } + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const&); void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t); } @@ -561,30 +643,6 @@ TSAN_INTERCEPTOR(int, memcmp, const void *s1, const void *s2, uptr n) { return res; } -TSAN_INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { - SCOPED_TSAN_INTERCEPTOR(strcmp, s1, s2); - uptr len = 0; - for (; s1[len] && s2[len]; len++) { - if (s1[len] != s2[len]) - break; - } - MemoryAccessRange(thr, pc, (uptr)s1, len + 1, false); - MemoryAccessRange(thr, pc, (uptr)s2, len + 1, false); - return s1[len] - s2[len]; -} - -TSAN_INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr n) { - SCOPED_TSAN_INTERCEPTOR(strncmp, s1, s2, n); - uptr len = 0; - for (; len < n && s1[len] && s2[len]; len++) { - if (s1[len] != s2[len]) - break; - } - MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false); - MemoryAccessRange(thr, pc, (uptr)s2, len < n ? len + 1 : n, false); - return len == n ? 0 : s1[len] - s2[len]; -} - TSAN_INTERCEPTOR(void*, memchr, void *s, int c, uptr n) { SCOPED_TSAN_INTERCEPTOR(memchr, s, c, n); void *res = REAL(memchr)(s, c, n); @@ -654,6 +712,12 @@ TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) { return res; } +TSAN_INTERCEPTOR(char*, strdup, const char *str) { + SCOPED_TSAN_INTERCEPTOR(strdup, str); + // strdup will call malloc, so no instrumentation is required here. + return REAL(strdup)(str); +} + static bool fix_mmap_addr(void **addr, long_t sz, int flags) { if (*addr) { if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) { @@ -704,23 +768,23 @@ TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) { } TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(memalign, align, sz); + SCOPED_INTERCEPTOR_RAW(memalign, align, sz); return user_alloc(thr, pc, sz, align); } TSAN_INTERCEPTOR(void*, valloc, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(valloc, sz); + SCOPED_INTERCEPTOR_RAW(valloc, sz); return user_alloc(thr, pc, sz, GetPageSizeCached()); } TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(pvalloc, sz); + SCOPED_INTERCEPTOR_RAW(pvalloc, sz); sz = RoundUp(sz, GetPageSizeCached()); return user_alloc(thr, pc, sz, GetPageSizeCached()); } TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(posix_memalign, memptr, align, sz); + SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz); *memptr = user_alloc(thr, pc, sz, align); return 0; } @@ -789,7 +853,8 @@ extern "C" void *__tsan_thread_start_func(void *arg) { { ThreadState *thr = cur_thread(); ScopedInRtl in_rtl; - if (pthread_setspecific(g_thread_finalize_key, (void*)4)) { + if (pthread_setspecific(g_thread_finalize_key, + (void *)kPthreadDestructorIterations)) { Printf("ThreadSanitizer: failed to set thread key\n"); Die(); } @@ -809,21 +874,15 @@ extern "C" void *__tsan_thread_start_func(void *arg) { TSAN_INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), void * param) { - SCOPED_TSAN_INTERCEPTOR(pthread_create, th, attr, callback, param); + SCOPED_INTERCEPTOR_RAW(pthread_create, th, attr, callback, param); __sanitizer_pthread_attr_t myattr; if (attr == 0) { pthread_attr_init(&myattr); attr = &myattr; } int detached = 0; - pthread_attr_getdetachstate(attr, &detached); - -#if defined(TSAN_DEBUG_OUTPUT) - int verbosity = (TSAN_DEBUG_OUTPUT); -#else - int verbosity = 0; -#endif - AdjustStackSizeLinux(attr, verbosity); + REAL(pthread_attr_getdetachstate)(attr, &detached); + AdjustStackSizeLinux(attr); ThreadParam p; p.callback = callback; @@ -843,7 +902,7 @@ TSAN_INTERCEPTOR(int, pthread_create, } TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) { - SCOPED_TSAN_INTERCEPTOR(pthread_join, th, ret); + SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret); int tid = ThreadTid(thr, pc, (uptr)th); int res = BLOCK_REAL(pthread_join)(th, ret); if (res == 0) { @@ -887,21 +946,13 @@ TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) { return res; } -TSAN_INTERCEPTOR(int, pthread_mutex_lock, void *m) { - SCOPED_TSAN_INTERCEPTOR(pthread_mutex_lock, m); - int res = REAL(pthread_mutex_lock)(m); - if (res == 0) { - MutexLock(thr, pc, (uptr)m); - } - return res; -} - TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m); int res = REAL(pthread_mutex_trylock)(m); - if (res == 0) { + if (res == EOWNERDEAD) + MutexRepair(thr, pc, (uptr)m); + if (res == 0 || res == EOWNERDEAD) MutexLock(thr, pc, (uptr)m); - } return res; } @@ -914,13 +965,6 @@ TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) { return res; } -TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) { - SCOPED_TSAN_INTERCEPTOR(pthread_mutex_unlock, m); - MutexUnlock(thr, pc, (uptr)m); - int res = REAL(pthread_mutex_unlock)(m); - return res; -} - TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared); int res = REAL(pthread_spin_init)(m, pshared); @@ -1043,45 +1087,18 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) { return res; } -// libpthread.so contains several versions of pthread_cond_init symbol. -// When we just dlsym() it, we get the wrong (old) version. -/* -TSAN_INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, c, a); - int res = REAL(pthread_cond_init)(c, a); - return res; -} -*/ - TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) { SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, c); + MemoryWrite(thr, pc, (uptr)c, kSizeLog1); int res = REAL(pthread_cond_destroy)(c); return res; } -TSAN_INTERCEPTOR(int, pthread_cond_signal, void *c) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, c); - int res = REAL(pthread_cond_signal)(c); - return res; -} - -TSAN_INTERCEPTOR(int, pthread_cond_broadcast, void *c) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, c); - int res = REAL(pthread_cond_broadcast)(c); - return res; -} - -TSAN_INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, c, m); - MutexUnlock(thr, pc, (uptr)m); - int res = REAL(pthread_cond_wait)(c, m); - MutexLock(thr, pc, (uptr)m); - return res; -} - -TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { +TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, + void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, c, m, abstime); MutexUnlock(thr, pc, (uptr)m); + MemoryRead(thr, pc, (uptr)c, kSizeLog1); int res = REAL(pthread_cond_timedwait)(c, m, abstime); MutexLock(thr, pc, (uptr)m); return res; @@ -1114,7 +1131,9 @@ TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) { } TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { - SCOPED_TSAN_INTERCEPTOR(pthread_once, o, f); + SCOPED_INTERCEPTOR_RAW(pthread_once, o, f); + // Using SCOPED_INTERCEPTOR_RAW, because if we are called from an ignored lib, + // the user callback must be executed with thr->in_rtl == 0. if (o == 0 || f == 0) return EINVAL; atomic_uint32_t *a = static_cast<atomic_uint32_t*>(o); @@ -1126,14 +1145,16 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { (*f)(); CHECK_EQ(thr->in_rtl, 0); thr->in_rtl = old_in_rtl; - Release(thr, pc, (uptr)o); + if (!thr->in_ignored_lib) + Release(thr, pc, (uptr)o); atomic_store(a, 2, memory_order_release); } else { while (v != 2) { pthread_yield(); v = atomic_load(a, memory_order_acquire); } - Acquire(thr, pc, (uptr)o); + if (!thr->in_ignored_lib) + Acquire(thr, pc, (uptr)o); } return 0; } @@ -1392,22 +1413,6 @@ TSAN_INTERCEPTOR(int, listen, int fd, int backlog) { return res; } -TSAN_INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { - SCOPED_TSAN_INTERCEPTOR(accept, fd, addr, addrlen); - int fd2 = REAL(accept)(fd, addr, addrlen); - if (fd >= 0 && fd2 >= 0) - FdSocketAccept(thr, pc, fd, fd2); - return fd2; -} - -TSAN_INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { - SCOPED_TSAN_INTERCEPTOR(accept4, fd, addr, addrlen, f); - int fd2 = REAL(accept4)(fd, addr, addrlen, f); - if (fd >= 0 && fd2 >= 0) - FdSocketAccept(thr, pc, fd, fd2); - return fd2; -} - TSAN_INTERCEPTOR(int, epoll_create, int size) { SCOPED_TSAN_INTERCEPTOR(epoll_create, size); int fd = REAL(epoll_create)(size); @@ -1466,58 +1471,30 @@ TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) { return res; } -TSAN_INTERCEPTOR(long_t, readv, int fd, void *vec, int cnt) { - SCOPED_TSAN_INTERCEPTOR(readv, fd, vec, cnt); - int res = REAL(readv)(fd, vec, cnt); - if (res >= 0 && fd >= 0) { - FdAcquire(thr, pc, fd); - } - return res; -} - -TSAN_INTERCEPTOR(long_t, preadv64, int fd, void *vec, int cnt, u64 off) { - SCOPED_TSAN_INTERCEPTOR(preadv64, fd, vec, cnt, off); - int res = REAL(preadv64)(fd, vec, cnt, off); - if (res >= 0 && fd >= 0) { - FdAcquire(thr, pc, fd); - } - return res; -} - -TSAN_INTERCEPTOR(long_t, writev, int fd, void *vec, int cnt) { - SCOPED_TSAN_INTERCEPTOR(writev, fd, vec, cnt); - if (fd >= 0) - FdRelease(thr, pc, fd); - int res = REAL(writev)(fd, vec, cnt); - return res; -} - -TSAN_INTERCEPTOR(long_t, pwritev64, int fd, void *vec, int cnt, u64 off) { - SCOPED_TSAN_INTERCEPTOR(pwritev64, fd, vec, cnt, off); - if (fd >= 0) - FdRelease(thr, pc, fd); - int res = REAL(pwritev64)(fd, vec, cnt, off); - return res; -} - TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) { SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags); - if (fd >= 0) + if (fd >= 0) { + FdAccess(thr, pc, fd); FdRelease(thr, pc, fd); + } int res = REAL(send)(fd, buf, len, flags); return res; } TSAN_INTERCEPTOR(long_t, sendmsg, int fd, void *msg, int flags) { SCOPED_TSAN_INTERCEPTOR(sendmsg, fd, msg, flags); - if (fd >= 0) + if (fd >= 0) { + FdAccess(thr, pc, fd); FdRelease(thr, pc, fd); + } int res = REAL(sendmsg)(fd, msg, flags); return res; } TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) { SCOPED_TSAN_INTERCEPTOR(recv, fd, buf, len, flags); + if (fd >= 0) + FdAccess(thr, pc, fd); int res = REAL(recv)(fd, buf, len, flags); if (res >= 0 && fd >= 0) { FdAcquire(thr, pc, fd); @@ -1525,15 +1502,6 @@ TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) { return res; } -TSAN_INTERCEPTOR(long_t, recvmsg, int fd, void *msg, int flags) { - SCOPED_TSAN_INTERCEPTOR(recvmsg, fd, msg, flags); - int res = REAL(recvmsg)(fd, msg, flags); - if (res >= 0 && fd >= 0) { - FdAcquire(thr, pc, fd); - } - return res; -} - TSAN_INTERCEPTOR(int, unlink, char *path) { SCOPED_TSAN_INTERCEPTOR(unlink, path); Release(thr, pc, File2addr(path)); @@ -1571,6 +1539,7 @@ TSAN_INTERCEPTOR(void*, freopen, char *path, char *mode, void *stream) { } TSAN_INTERCEPTOR(int, fclose, void *stream) { + // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fclose, stream); if (stream) { @@ -1583,6 +1552,7 @@ TSAN_INTERCEPTOR(int, fclose, void *stream) { } TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { + // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f); MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true); @@ -1591,6 +1561,7 @@ TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { } TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { + // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f); MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false); @@ -1599,7 +1570,10 @@ TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { } TSAN_INTERCEPTOR(int, fflush, void *stream) { - SCOPED_TSAN_INTERCEPTOR(fflush, stream); + // libc file streams can call user-supplied functions, see fopencookie. + { + SCOPED_TSAN_INTERCEPTOR(fflush, stream); + } return REAL(fflush)(stream); } @@ -1632,27 +1606,23 @@ TSAN_INTERCEPTOR(void*, opendir, char *path) { TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) { SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev); - if (op == EPOLL_CTL_ADD && epfd >= 0) { + if (epfd >= 0) + FdAccess(thr, pc, epfd); + if (epfd >= 0 && fd >= 0) + FdAccess(thr, pc, fd); + if (op == EPOLL_CTL_ADD && epfd >= 0) FdRelease(thr, pc, epfd); - } int res = REAL(epoll_ctl)(epfd, op, fd, ev); - if (fd >= 0) - FdAccess(thr, pc, fd); return res; } TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) { SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout); + if (epfd >= 0) + FdAccess(thr, pc, epfd); int res = BLOCK_REAL(epoll_wait)(epfd, ev, cnt, timeout); - if (res > 0 && epfd >= 0) { + if (res > 0 && epfd >= 0) FdAcquire(thr, pc, epfd); - } - return res; -} - -TSAN_INTERCEPTOR(int, poll, void *fds, long_t nfds, int timeout) { - SCOPED_TSAN_INTERCEPTOR(poll, fds, nfds, timeout); - int res = BLOCK_REAL(poll)(fds, nfds, timeout); return res; } @@ -1662,7 +1632,7 @@ void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig, SignalContext *sctx = SigCtx(thr); // Don't mess with synchronous signals. if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL || - sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || + sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS || // If we are sending signal to ourselves, we must process it now. (sctx && sig == sctx->int_signal_send) || // If we are in blocking function, we can safely process it now @@ -1714,7 +1684,7 @@ TSAN_INTERCEPTOR(int, sigaction, int sig, sigaction_t *act, sigaction_t *old) { internal_memcpy(&sigactions[sig], act, sizeof(*act)); sigaction_t newact; internal_memcpy(&newact, act, sizeof(newact)); - sigfillset(&newact.sa_mask); + REAL(sigfillset)(&newact.sa_mask); if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL) { if (newact.sa_flags & SA_SIGINFO) newact.sa_sigaction = rtl_sigaction; @@ -1737,6 +1707,11 @@ TSAN_INTERCEPTOR(sighandler_t, signal, int sig, sighandler_t h) { return old.sa_handler; } +TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) { + SCOPED_TSAN_INTERCEPTOR(sigsuspend, mask); + return REAL(sigsuspend)(mask); +} + TSAN_INTERCEPTOR(int, raise, int sig) { SCOPED_TSAN_INTERCEPTOR(raise, sig); SignalContext *sctx = SigCtx(thr); @@ -1787,13 +1762,30 @@ TSAN_INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { return REAL(gettimeofday)(tv, tz); } +TSAN_INTERCEPTOR(int, getaddrinfo, void *node, void *service, + void *hints, void *rv) { + SCOPED_TSAN_INTERCEPTOR(getaddrinfo, node, service, hints, rv); + // We miss atomic synchronization in getaddrinfo, + // and can report false race between malloc and free + // inside of getaddrinfo. So ignore memory accesses. + ThreadIgnoreBegin(thr); + // getaddrinfo calls fopen, which can be intercepted by user. + thr->in_rtl--; + CHECK_EQ(thr->in_rtl, 0); + int res = REAL(getaddrinfo)(node, service, hints, rv); + thr->in_rtl++; + ThreadIgnoreEnd(thr); + return res; +} + // Linux kernel has a bug that leads to kernel deadlock if a process // maps TBs of memory and then calls mlock(). static void MlockIsUnsupported() { static atomic_uint8_t printed; if (atomic_exchange(&printed, 1, memory_order_relaxed)) return; - Printf("INFO: ThreadSanitizer ignores mlock/mlockall/munlock/munlockall\n"); + if (flags()->verbosity > 0) + Printf("INFO: ThreadSanitizer ignores mlock/mlockall/munlock/munlockall\n"); } TSAN_INTERCEPTOR(int, mlock, const void *addr, uptr len) { @@ -1817,8 +1809,7 @@ TSAN_INTERCEPTOR(int, munlockall, void) { } TSAN_INTERCEPTOR(int, fork, int fake) { - SCOPED_TSAN_INTERCEPTOR(fork, fake); - // It's intercepted merely to process pending signals. + SCOPED_INTERCEPTOR_RAW(fork, fake); int pid = REAL(fork)(fake); if (pid == 0) { // child @@ -1829,6 +1820,12 @@ TSAN_INTERCEPTOR(int, fork, int fake) { return pid; } +static int OnExit(ThreadState *thr) { + int status = Finalize(thr); + REAL(fflush)(0); + return status; +} + struct TsanInterceptorContext { ThreadState *thr; const uptr caller_pc; @@ -1839,39 +1836,150 @@ struct TsanInterceptorContext { // Causes interceptor recursion (getpwuid_r() calls fopen()) #undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS #undef SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS +// Causes interceptor recursion (getaddrinfo() and fopen()) +#undef SANITIZER_INTERCEPT_GETADDRINFO +#undef SANITIZER_INTERCEPT_GETNAMEINFO // Causes interceptor recursion (glob64() calls lstat64()) #undef SANITIZER_INTERCEPT_GLOB -#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ - MemoryAccessRange(((TsanInterceptorContext*)ctx)->thr, \ - ((TsanInterceptorContext*)ctx)->pc, \ - (uptr)ptr, size, true) -#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ - MemoryAccessRange(((TsanInterceptorContext*)ctx)->thr, \ - ((TsanInterceptorContext*)ctx)->pc, \ - (uptr)ptr, size, false) -#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ - SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__) \ - TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \ - ctx = (void*)&_ctx; \ - (void)ctx; +#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name) +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ + do { \ + } while (false) + +#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ + MemoryAccessRange(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)ptr, size, \ + true) + +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ + MemoryAccessRange(((TsanInterceptorContext *) ctx)->thr, \ + ((TsanInterceptorContext *) ctx)->pc, (uptr) ptr, size, \ + false) + +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__); \ + TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \ + ctx = (void *)&_ctx; \ + (void) ctx; + #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ - FdAcquire(((TsanInterceptorContext*)ctx)->thr, pc, fd) + FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd) + #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ - FdRelease(((TsanInterceptorContext*)ctx)->thr, pc, fd) + FdRelease(((TsanInterceptorContext *) ctx)->thr, pc, fd) + +#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) \ + FdAccess(((TsanInterceptorContext *) ctx)->thr, pc, fd) + +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + FdSocketAccept(((TsanInterceptorContext *) ctx)->thr, pc, fd, newfd) + #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ - ThreadSetName(((TsanInterceptorContext*)ctx)->thr, name) + ThreadSetName(((TsanInterceptorContext *) ctx)->thr, name) + +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + CTX()->thread_registry->SetThreadNameByUserId(thread, name) + +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) BLOCK_REAL(name) + +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) \ + OnExit(((TsanInterceptorContext *) ctx)->thr) + +#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) \ + MutexLock(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \ + MutexUnlock(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \ + MutexRepair(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + #include "sanitizer_common/sanitizer_common_interceptors.inc" -// FIXME: Implement these with MemoryAccessRange(). -#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) -#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) -#define COMMON_SYSCALL_POST_READ_RANGE(p, s) -#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) +#define TSAN_SYSCALL() \ + ThreadState *thr = cur_thread(); \ + ScopedSyscall scoped_syscall(thr) \ +/**/ + +struct ScopedSyscall { + ThreadState *thr; + + explicit ScopedSyscall(ThreadState *thr) + : thr(thr) { + if (thr->in_rtl == 0) + Initialize(thr); + thr->in_rtl++; + } + + ~ScopedSyscall() { + thr->in_rtl--; + if (thr->in_rtl == 0) + ProcessPendingSignals(thr); + } +}; + +static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) { + TSAN_SYSCALL(); + MemoryAccessRange(thr, pc, p, s, write); +} + +static void syscall_fd_close(uptr pc, int fd) { + TSAN_SYSCALL(); + if (fd >= 0) + FdClose(thr, pc, fd); +} + +static void syscall_pre_fork(uptr pc) { + TSAN_SYSCALL(); +} + +static void syscall_post_fork(uptr pc, int res) { + TSAN_SYSCALL(); + if (res == 0) { + // child + FdOnFork(thr, pc); + } else if (res > 0) { + // parent + } +} + +#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \ + syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ + syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), true) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#define COMMON_SYSCALL_FD_CLOSE(fd) syscall_fd_close(GET_CALLER_PC(), fd) +#define COMMON_SYSCALL_PRE_FORK() \ + syscall_pre_fork(GET_CALLER_PC()) +#define COMMON_SYSCALL_POST_FORK(res) \ + syscall_post_fork(GET_CALLER_PC(), res) #include "sanitizer_common/sanitizer_common_syscalls.inc" namespace __tsan { +static void finalize(void *arg) { + ThreadState *thr = cur_thread(); + uptr pc = 0; + atexit_ctx->exit(thr, pc); + int status = Finalize(thr); + REAL(fflush)(0); + if (status) + REAL(_exit)(status); +} + void ProcessPendingSignals(ThreadState *thr) { CHECK_EQ(thr->in_rtl, 0); SignalContext *sctx = SigCtx(thr); @@ -1881,8 +1989,8 @@ void ProcessPendingSignals(ThreadState *thr) { thr->in_signal_handler = true; sctx->pending_signal_count = 0; // These are too big for stack. - static THREADLOCAL sigset_t emptyset, oldset; - sigfillset(&emptyset); + static THREADLOCAL __sanitizer_sigset_t emptyset, oldset; + REAL(sigfillset)(&emptyset); pthread_sigmask(SIG_SETMASK, &emptyset, &oldset); for (int sig = 0; sig < kSigCount; sig++) { SignalDesc *signal = &sctx->pending_signals[sig]; @@ -1903,6 +2011,7 @@ void ProcessPendingSignals(ThreadState *thr) { uptr pc = signal->sigaction ? (uptr)sigactions[sig].sa_sigaction : (uptr)sigactions[sig].sa_handler; + pc += 1; // return address is expected, OutputReport() will undo this stack.Init(&pc, 1); ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeErrnoInSignal); @@ -1920,16 +2029,6 @@ void ProcessPendingSignals(ThreadState *thr) { thr->in_signal_handler = false; } -static void finalize(void *arg) { - ThreadState * thr = cur_thread(); - uptr pc = 0; - atexit_ctx->exit(thr, pc); - int status = Finalize(cur_thread()); - REAL(fflush)(0); - if (status) - _exit(status); -} - static void unreachable() { Printf("FATAL: ThreadSanitizer: unreachable called\n"); Die(); @@ -1973,7 +2072,6 @@ void InitializeInterceptors() { TSAN_INTERCEPT(strlen); TSAN_INTERCEPT(memset); TSAN_INTERCEPT(memcpy); - TSAN_INTERCEPT(strcmp); TSAN_INTERCEPT(memchr); TSAN_INTERCEPT(memrchr); TSAN_INTERCEPT(memmove); @@ -1981,10 +2079,10 @@ void InitializeInterceptors() { TSAN_INTERCEPT(strchr); TSAN_INTERCEPT(strchrnul); TSAN_INTERCEPT(strrchr); - TSAN_INTERCEPT(strncmp); TSAN_INTERCEPT(strcpy); // NOLINT TSAN_INTERCEPT(strncpy); TSAN_INTERCEPT(strstr); + TSAN_INTERCEPT(strdup); TSAN_INTERCEPT(pthread_create); TSAN_INTERCEPT(pthread_join); @@ -1992,10 +2090,8 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_mutex_init); TSAN_INTERCEPT(pthread_mutex_destroy); - TSAN_INTERCEPT(pthread_mutex_lock); TSAN_INTERCEPT(pthread_mutex_trylock); TSAN_INTERCEPT(pthread_mutex_timedlock); - TSAN_INTERCEPT(pthread_mutex_unlock); TSAN_INTERCEPT(pthread_spin_init); TSAN_INTERCEPT(pthread_spin_destroy); @@ -2013,12 +2109,8 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_rwlock_timedwrlock); TSAN_INTERCEPT(pthread_rwlock_unlock); - // TSAN_INTERCEPT(pthread_cond_init); - TSAN_INTERCEPT(pthread_cond_destroy); - TSAN_INTERCEPT(pthread_cond_signal); - TSAN_INTERCEPT(pthread_cond_broadcast); - TSAN_INTERCEPT(pthread_cond_wait); - TSAN_INTERCEPT(pthread_cond_timedwait); + INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2"); TSAN_INTERCEPT(pthread_barrier_init); TSAN_INTERCEPT(pthread_barrier_destroy); @@ -2062,8 +2154,6 @@ void InitializeInterceptors() { TSAN_INTERCEPT(connect); TSAN_INTERCEPT(bind); TSAN_INTERCEPT(listen); - TSAN_INTERCEPT(accept); - TSAN_INTERCEPT(accept4); TSAN_INTERCEPT(epoll_create); TSAN_INTERCEPT(epoll_create1); TSAN_INTERCEPT(close); @@ -2072,14 +2162,9 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pipe); TSAN_INTERCEPT(pipe2); - TSAN_INTERCEPT(readv); - TSAN_INTERCEPT(preadv64); - TSAN_INTERCEPT(writev); - TSAN_INTERCEPT(pwritev64); TSAN_INTERCEPT(send); TSAN_INTERCEPT(sendmsg); TSAN_INTERCEPT(recv); - TSAN_INTERCEPT(recvmsg); TSAN_INTERCEPT(unlink); TSAN_INTERCEPT(fopen); @@ -2095,10 +2180,10 @@ void InitializeInterceptors() { TSAN_INTERCEPT(epoll_ctl); TSAN_INTERCEPT(epoll_wait); - TSAN_INTERCEPT(poll); TSAN_INTERCEPT(sigaction); TSAN_INTERCEPT(signal); + TSAN_INTERCEPT(sigsuspend); TSAN_INTERCEPT(raise); TSAN_INTERCEPT(kill); TSAN_INTERCEPT(pthread_kill); @@ -2106,6 +2191,7 @@ void InitializeInterceptors() { TSAN_INTERCEPT(usleep); TSAN_INTERCEPT(nanosleep); TSAN_INTERCEPT(gettimeofday); + TSAN_INTERCEPT(getaddrinfo); TSAN_INTERCEPT(mlock); TSAN_INTERCEPT(munlock); @@ -2113,8 +2199,11 @@ void InitializeInterceptors() { TSAN_INTERCEPT(munlockall); TSAN_INTERCEPT(fork); + TSAN_INTERCEPT(dlopen); + TSAN_INTERCEPT(dlclose); TSAN_INTERCEPT(on_exit); TSAN_INTERCEPT(__cxa_atexit); + TSAN_INTERCEPT(_exit); // Need to setup it, because interceptors check that the function is resolved. // But atexit is emitted directly into the module, so can't be resolved. @@ -2136,9 +2225,15 @@ void InitializeInterceptors() { } void internal_start_thread(void(*func)(void *arg), void *arg) { + // Start the thread with signals blocked, otherwise it can steal users + // signals. + __sanitizer_kernel_sigset_t set, old; + internal_sigfillset(&set); + internal_sigprocmask(SIG_SETMASK, &set, &old); void *th; REAL(pthread_create)(&th, 0, (void*(*)(void *arg))func, arg); REAL(pthread_detach)(th); + internal_sigprocmask(SIG_SETMASK, &old, 0); } } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_interface.cc b/lib/tsan/rtl/tsan_interface.cc index efad8c192d6e..9de3808e79ff 100644 --- a/lib/tsan/rtl/tsan_interface.cc +++ b/lib/tsan/rtl/tsan_interface.cc @@ -38,49 +38,55 @@ void __tsan_write16(void *addr) { MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); } -u16 __tsan_unaligned_read2(void *addr) { +u16 __tsan_unaligned_read2(const uu16 *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, false, false); - return *(u16*)addr; + return *addr; } -u32 __tsan_unaligned_read4(void *addr) { +u32 __tsan_unaligned_read4(const uu32 *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, false, false); - return *(u32*)addr; + return *addr; } -u64 __tsan_unaligned_read8(void *addr) { +u64 __tsan_unaligned_read8(const uu64 *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, false, false); - return *(u64*)addr; + return *addr; } -void __tsan_unaligned_write2(void *addr, u16 v) { +void __tsan_unaligned_write2(uu16 *addr, u16 v) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, true, false); - *(u16*)addr = v; + *addr = v; } -void __tsan_unaligned_write4(void *addr, u32 v) { +void __tsan_unaligned_write4(uu32 *addr, u32 v) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, true, false); - *(u32*)addr = v; + *addr = v; } -void __tsan_unaligned_write8(void *addr, u64 v) { +void __tsan_unaligned_write8(uu64 *addr, u64 v) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, true, false); - *(u64*)addr = v; + *addr = v; } extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE uint16_t __sanitizer_unaligned_load16(void *addr) - ALIAS("__tsan_unaligned_read2") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_read2"); +SANITIZER_INTERFACE_ATTRIBUTE uint32_t __sanitizer_unaligned_load32(void *addr) - ALIAS("__tsan_unaligned_read4") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_read4"); +SANITIZER_INTERFACE_ATTRIBUTE uint64_t __sanitizer_unaligned_load64(void *addr) - ALIAS("__tsan_unaligned_read8") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_read8"); +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store16(void *addr, uint16_t v) - ALIAS("__tsan_unaligned_write2") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_write2"); +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store32(void *addr, uint32_t v) - ALIAS("__tsan_unaligned_write4") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_write4"); +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store64(void *addr, uint64_t v) - ALIAS("__tsan_unaligned_write8") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_write8"); } void __tsan_acquire(void *addr) { diff --git a/lib/tsan/rtl/tsan_interface.h b/lib/tsan/rtl/tsan_interface.h index 457fb55e0d2d..70450697d480 100644 --- a/lib/tsan/rtl/tsan_interface.h +++ b/lib/tsan/rtl/tsan_interface.h @@ -27,38 +27,38 @@ extern "C" { // This function should be called at the very beginning of the process, // before any instrumented code is executed and before any call to malloc. -void __tsan_init() SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_init(); -void __tsan_read1(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read16(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read1(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read2(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read4(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read8(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read16(void *addr); -void __tsan_write1(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write16(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write1(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write2(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write4(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write8(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write16(void *addr); -u16 __tsan_unaligned_read2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -u32 __tsan_unaligned_read4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -u64 __tsan_unaligned_read8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_unaligned_write2(void *addr, u16 v) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_unaligned_write4(void *addr, u32 v) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_unaligned_write8(void *addr, u64 v) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE u16 __tsan_unaligned_read2(const uu16 *addr); +SANITIZER_INTERFACE_ATTRIBUTE u32 __tsan_unaligned_read4(const uu32 *addr); +SANITIZER_INTERFACE_ATTRIBUTE u64 __tsan_unaligned_read8(const uu64 *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write2(uu16 *addr, u16 v); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(uu32 *addr, u32 v); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(uu64 *addr, u64 v); -void __tsan_vptr_read(void **vptr_p) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_vptr_update(void **vptr_p, void *new_val) - SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_read(void **vptr_p); +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_vptr_update(void **vptr_p, void *new_val); -void __tsan_func_entry(void *call_pc) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_func_exit() SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_entry(void *call_pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_exit(); -void __tsan_read_range(void *addr, unsigned long size) // NOLINT - SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write_range(void *addr, unsigned long size) // NOLINT - SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_read_range(void *addr, unsigned long size); // NOLINT +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_write_range(void *addr, unsigned long size); // NOLINT #ifdef __cplusplus } // extern "C" diff --git a/lib/tsan/rtl/tsan_interface_ann.cc b/lib/tsan/rtl/tsan_interface_ann.cc index 04b4b455d15e..cacbc0281d0f 100644 --- a/lib/tsan/rtl/tsan_interface_ann.cc +++ b/lib/tsan/rtl/tsan_interface_ann.cc @@ -13,6 +13,7 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" #include "tsan_interface_ann.h" #include "tsan_mutex.h" #include "tsan_report.h" @@ -229,12 +230,12 @@ using namespace __tsan; // NOLINT extern "C" { void INTERFACE_ATTRIBUTE AnnotateHappensBefore(char *f, int l, uptr addr) { SCOPED_ANNOTATION(AnnotateHappensBefore); - Release(cur_thread(), CALLERPC, addr); + Release(thr, pc, addr); } void INTERFACE_ATTRIBUTE AnnotateHappensAfter(char *f, int l, uptr addr) { SCOPED_ANNOTATION(AnnotateHappensAfter); - Acquire(cur_thread(), CALLERPC, addr); + Acquire(thr, pc, addr); } void INTERFACE_ATTRIBUTE AnnotateCondVarSignal(char *f, int l, uptr cv) { @@ -382,22 +383,32 @@ void INTERFACE_ATTRIBUTE AnnotateBenignRace( void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsBegin(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreReadsBegin); - IgnoreCtl(cur_thread(), false, true); + ThreadIgnoreBegin(thr); } void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsEnd(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreReadsEnd); - IgnoreCtl(cur_thread(), false, false); + ThreadIgnoreEnd(thr); } void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesBegin(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreWritesBegin); - IgnoreCtl(cur_thread(), true, true); + ThreadIgnoreBegin(thr); } void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesEnd(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreWritesEnd); - IgnoreCtl(thr, true, false); + ThreadIgnoreEnd(thr); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncBegin(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreSyncBegin); + ThreadIgnoreSyncBegin(thr); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncEnd(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreSyncEnd); + ThreadIgnoreSyncEnd(thr); } void INTERFACE_ATTRIBUTE AnnotatePublishMemoryRange( @@ -447,4 +458,7 @@ const char INTERFACE_ATTRIBUTE* ThreadSanitizerQuery(const char *query) { else return "0"; } + +void INTERFACE_ATTRIBUTE +AnnotateMemoryIsInitialized(char *f, int l, uptr mem, uptr sz) {} } // extern "C" diff --git a/lib/tsan/rtl/tsan_interface_ann.h b/lib/tsan/rtl/tsan_interface_ann.h index 8e45328e7ec1..963bcc55afaa 100644 --- a/lib/tsan/rtl/tsan_interface_ann.h +++ b/lib/tsan/rtl/tsan_interface_ann.h @@ -23,8 +23,8 @@ extern "C" { #endif -void __tsan_acquire(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_release(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_acquire(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_release(void *addr); #ifdef __cplusplus } // extern "C" diff --git a/lib/tsan/rtl/tsan_interface_atomic.cc b/lib/tsan/rtl/tsan_interface_atomic.cc index 80266969849a..d9f8cdf5b106 100644 --- a/lib/tsan/rtl/tsan_interface_atomic.cc +++ b/lib/tsan/rtl/tsan_interface_atomic.cc @@ -30,23 +30,37 @@ using namespace __tsan; // NOLINT #define SCOPED_ATOMIC(func, ...) \ const uptr callpc = (uptr)__builtin_return_address(0); \ uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ - pc = __sanitizer::StackTrace::GetPreviousInstructionPc(pc); \ mo = ConvertOrder(mo); \ mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \ ThreadState *const thr = cur_thread(); \ AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \ - ScopedAtomic sa(thr, callpc, __FUNCTION__); \ + ScopedAtomic sa(thr, callpc, a, mo, __FUNCTION__); \ return Atomic##func(thr, pc, __VA_ARGS__); \ /**/ +// Some shortcuts. +typedef __tsan_memory_order morder; +typedef __tsan_atomic8 a8; +typedef __tsan_atomic16 a16; +typedef __tsan_atomic32 a32; +typedef __tsan_atomic64 a64; +typedef __tsan_atomic128 a128; +const morder mo_relaxed = __tsan_memory_order_relaxed; +const morder mo_consume = __tsan_memory_order_consume; +const morder mo_acquire = __tsan_memory_order_acquire; +const morder mo_release = __tsan_memory_order_release; +const morder mo_acq_rel = __tsan_memory_order_acq_rel; +const morder mo_seq_cst = __tsan_memory_order_seq_cst; + class ScopedAtomic { public: - ScopedAtomic(ThreadState *thr, uptr pc, const char *func) + ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a, + morder mo, const char *func) : thr_(thr) { CHECK_EQ(thr_->in_rtl, 0); ProcessPendingSignals(thr); FuncEntry(thr_, pc); - DPrintf("#%d: %s\n", thr_->tid, func); + DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo); thr_->in_rtl++; } ~ScopedAtomic() { @@ -58,20 +72,6 @@ class ScopedAtomic { ThreadState *thr_; }; -// Some shortcuts. -typedef __tsan_memory_order morder; -typedef __tsan_atomic8 a8; -typedef __tsan_atomic16 a16; -typedef __tsan_atomic32 a32; -typedef __tsan_atomic64 a64; -typedef __tsan_atomic128 a128; -const morder mo_relaxed = __tsan_memory_order_relaxed; -const morder mo_consume = __tsan_memory_order_consume; -const morder mo_acquire = __tsan_memory_order_acquire; -const morder mo_release = __tsan_memory_order_release; -const morder mo_acq_rel = __tsan_memory_order_acq_rel; -const morder mo_seq_cst = __tsan_memory_order_seq_cst; - static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) { StatInc(thr, StatAtomic); StatInc(thr, t); @@ -251,11 +251,10 @@ static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a, // Assume the access is atomic. if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) { MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>()); - return *a; + return *a; // as if atomic } SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); T v = *a; s->mtx.ReadUnlock(); __sync_synchronize(); @@ -273,13 +272,15 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v, // Strictly saying even relaxed store cuts off release sequence, // so must reset the clock. if (!IsReleaseOrder(mo) && sizeof(T) <= sizeof(a)) { - *a = v; + *a = v; // as if atomic return; } __sync_synchronize(); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.ReleaseStore(&s->clock); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseImpl(thr, pc, &s->clock); *a = v; s->mtx.Unlock(); // Trainling memory barrier to provide sequential consistency @@ -293,13 +294,15 @@ static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) { SyncVar *s = 0; if (mo != mo_relaxed) { s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); if (IsAcqRelOrder(mo)) - thr->clock.acq_rel(&s->clock); + AcquireReleaseImpl(thr, pc, &s->clock); else if (IsReleaseOrder(mo)) - thr->clock.release(&s->clock); + ReleaseImpl(thr, pc, &s->clock); else if (IsAcquireOrder(mo)) - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); } v = F(a, v); if (s) @@ -357,13 +360,15 @@ static bool AtomicCAS(ThreadState *thr, uptr pc, SyncVar *s = 0; if (mo != mo_relaxed) { s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); if (IsAcqRelOrder(mo)) - thr->clock.acq_rel(&s->clock); + AcquireReleaseImpl(thr, pc, &s->clock); else if (IsReleaseOrder(mo)) - thr->clock.release(&s->clock); + ReleaseImpl(thr, pc, &s->clock); else if (IsAcquireOrder(mo)) - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); } T cc = *c; T pr = func_cas(a, cc, v); @@ -659,14 +664,14 @@ a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v, } #if __TSAN_HAS_INT128 -a128 __tsan_atomic64_compare_exchange_val(volatile a128 *a, a128 c, a128 v, +a128 __tsan_atomic128_compare_exchange_val(volatile a128 *a, a128 c, a128 v, morder mo, morder fmo) { SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); } #endif void __tsan_atomic_thread_fence(morder mo) { - char* a; + char* a = 0; SCOPED_ATOMIC(Fence, mo); } diff --git a/lib/tsan/rtl/tsan_interface_java.cc b/lib/tsan/rtl/tsan_interface_java.cc index 71e0747c3646..53f14cf07b59 100644 --- a/lib/tsan/rtl/tsan_interface_java.cc +++ b/lib/tsan/rtl/tsan_interface_java.cc @@ -17,6 +17,8 @@ #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_procmaps.h" using namespace __tsan; // NOLINT @@ -157,7 +159,7 @@ SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) { #define SCOPED_JAVA_FUNC(func) \ ThreadState *thr = cur_thread(); \ const uptr caller_pc = GET_CALLER_PC(); \ - const uptr pc = (uptr)&func; \ + const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ (void)pc; \ ScopedJavaFunc scoped(thr, caller_pc); \ /**/ diff --git a/lib/tsan/rtl/tsan_mman.cc b/lib/tsan/rtl/tsan_mman.cc index b6671b1abf09..8547f714be13 100644 --- a/lib/tsan/rtl/tsan_mman.cc +++ b/lib/tsan/rtl/tsan_mman.cc @@ -75,10 +75,12 @@ void InitializeAllocator() { void AllocatorThreadStart(ThreadState *thr) { allocator()->InitCache(&thr->alloc_cache); + internal_allocator()->InitCache(&thr->internal_alloc_cache); } void AllocatorThreadFinish(ThreadState *thr) { allocator()->DestroyCache(&thr->alloc_cache); + internal_allocator()->DestroyCache(&thr->internal_alloc_cache); } void AllocatorPrintStats() { @@ -102,14 +104,18 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) { void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) { CHECK_GT(thr->in_rtl, 0); if ((sz >= (1ull << 40)) || (align >= (1ull << 40))) - return 0; + return AllocatorReturnNull(); void *p = allocator()->Allocate(&thr->alloc_cache, sz, align); if (p == 0) return 0; MBlock *b = new(allocator()->GetMetaData(p)) MBlock; b->Init(sz, thr->tid, CurrentStackId(thr, pc)); - if (CTX() && CTX()->initialized) - MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); + if (CTX() && CTX()->initialized) { + if (thr->ignore_reads_and_writes == 0) + MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); + else + MemoryResetRange(thr, pc, (uptr)p, sz); + } DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p); SignalUnsafeCall(thr, pc); return p; @@ -132,8 +138,10 @@ void user_free(ThreadState *thr, uptr pc, void *p) { } b->ListReset(); } - if (CTX() && CTX()->initialized && thr->in_rtl == 1) - MemoryRangeFreed(thr, pc, (uptr)p, b->Size()); + if (CTX() && CTX()->initialized && thr->in_rtl == 1) { + if (thr->ignore_reads_and_writes == 0) + MemoryRangeFreed(thr, pc, (uptr)p, b->Size()); + } allocator()->Deallocate(&thr->alloc_cache, p); SignalUnsafeCall(thr, pc); } @@ -194,11 +202,12 @@ void invoke_free_hook(void *ptr) { void *internal_alloc(MBlockType typ, uptr sz) { ThreadState *thr = cur_thread(); CHECK_GT(thr->in_rtl, 0); + CHECK_LE(sz, InternalSizeClassMap::kMaxSize); if (thr->nomalloc) { thr->nomalloc = 0; // CHECK calls internal_malloc(). CHECK(0); } - return InternalAlloc(sz); + return InternalAlloc(sz, &thr->internal_alloc_cache); } void internal_free(void *p) { @@ -208,7 +217,7 @@ void internal_free(void *p) { thr->nomalloc = 0; // CHECK calls internal_malloc(). CHECK(0); } - InternalFree(p); + InternalFree(p, &thr->internal_alloc_cache); } } // namespace __tsan @@ -261,5 +270,6 @@ uptr __tsan_get_allocated_size(void *p) { void __tsan_on_thread_idle() { ThreadState *thr = cur_thread(); allocator()->SwallowCache(&thr->alloc_cache); + internal_allocator()->SwallowCache(&thr->internal_alloc_cache); } } // extern "C" diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h index 666b4d0c482f..32e22ba60434 100644 --- a/lib/tsan/rtl/tsan_platform.h +++ b/lib/tsan/rtl/tsan_platform.h @@ -134,17 +134,24 @@ static inline uptr AlternativeAddress(uptr addr) { void FlushShadowMemory(); void WriteMemoryProfile(char *buf, uptr buf_size); +uptr GetRSS(); const char *InitializePlatform(); void FinalizePlatform(); + +// The additional page is to catch shadow stack overflow as paging fault. +const uptr kTotalTraceSize = (kTraceSize * sizeof(Event) + sizeof(Trace) + 4096 + + 4095) & ~4095; + uptr ALWAYS_INLINE GetThreadTrace(int tid) { - uptr p = kTraceMemBegin + (uptr)(tid * 2) * kTraceSize * sizeof(Event); + uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize; DCHECK_LT(p, kTraceMemBegin + kTraceMemSize); return p; } uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) { - uptr p = kTraceMemBegin + (uptr)(tid * 2 + 1) * kTraceSize * sizeof(Event); + uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize + + kTraceSize * sizeof(Event); DCHECK_LT(p, kTraceMemBegin + kTraceMemSize); return p; } diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc index a0d71e8589d6..906b5dc73be4 100644 --- a/lib/tsan/rtl/tsan_platform_linux.cc +++ b/lib/tsan/rtl/tsan_platform_linux.cc @@ -19,6 +19,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" +#include "sanitizer_common/sanitizer_stoptheworld.h" #include "tsan_platform.h" #include "tsan_rtl.h" #include "tsan_flags.h" @@ -45,6 +46,14 @@ #include <resolv.h> #include <malloc.h> +#ifdef sa_handler +# undef sa_handler +#endif + +#ifdef sa_sigaction +# undef sa_sigaction +#endif + extern "C" struct mallinfo __libc_mallinfo(); namespace __tsan { @@ -72,70 +81,33 @@ ScopedInRtl::~ScopedInRtl() { } #endif -static bool ishex(char c) { - return (c >= '0' && c <= '9') - || (c >= 'a' && c <= 'f'); -} - -static uptr readhex(const char *p) { - uptr v = 0; - for (; ishex(p[0]); p++) { - if (p[0] >= '0' && p[0] <= '9') - v = v * 16 + p[0] - '0'; - else - v = v * 16 + p[0] - 'a' + 10; - } - return v; -} - -static uptr readdec(const char *p) { - uptr v = 0; - for (; p[0] >= '0' && p[0] <= '9' ; p++) - v = v * 10 + p[0] - '0'; - return v; +void FillProfileCallback(uptr start, uptr rss, bool file, + uptr *mem, uptr stats_size) { + CHECK_EQ(7, stats_size); + mem[6] += rss; // total + start >>= 40; + if (start < 0x10) // shadow + mem[0] += rss; + else if (start >= 0x20 && start < 0x30) // compat modules + mem[file ? 1 : 2] += rss; + else if (start >= 0x7e) // modules + mem[file ? 1 : 2] += rss; + else if (start >= 0x60 && start < 0x62) // traces + mem[3] += rss; + else if (start >= 0x7d && start < 0x7e) // heap + mem[4] += rss; + else // other + mem[5] += rss; } void WriteMemoryProfile(char *buf, uptr buf_size) { - char *smaps = 0; - uptr smaps_cap = 0; - uptr smaps_len = ReadFileToBuffer("/proc/self/smaps", - &smaps, &smaps_cap, 64<<20); - uptr mem[6] = {}; - uptr total = 0; - uptr start = 0; - bool file = false; - const char *pos = smaps; - while (pos < smaps + smaps_len) { - if (ishex(pos[0])) { - start = readhex(pos); - for (; *pos != '/' && *pos > '\n'; pos++) {} - file = *pos == '/'; - } else if (internal_strncmp(pos, "Rss:", 4) == 0) { - for (; *pos < '0' || *pos > '9'; pos++) {} - uptr rss = readdec(pos) * 1024; - total += rss; - start >>= 40; - if (start < 0x10) // shadow - mem[0] += rss; - else if (start >= 0x20 && start < 0x30) // compat modules - mem[file ? 1 : 2] += rss; - else if (start >= 0x7e) // modules - mem[file ? 1 : 2] += rss; - else if (start >= 0x60 && start < 0x62) // traces - mem[3] += rss; - else if (start >= 0x7d && start < 0x7e) // heap - mem[4] += rss; - else // other - mem[5] += rss; - } - while (*pos++ != '\n') {} - } - UnmapOrDie(smaps, smaps_cap); + uptr mem[7] = {}; + __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7); char *buf_pos = buf; char *buf_end = buf + buf_size; buf_pos += internal_snprintf(buf_pos, buf_end - buf_pos, "RSS %zd MB: shadow:%zd file:%zd mmap:%zd trace:%zd heap:%zd other:%zd\n", - total >> 20, mem[0] >> 20, mem[1] >> 20, mem[2] >> 20, + mem[6] >> 20, mem[0] >> 20, mem[1] >> 20, mem[2] >> 20, mem[3] >> 20, mem[4] >> 20, mem[5] >> 20); struct mallinfo mi = __libc_mallinfo(); buf_pos += internal_snprintf(buf_pos, buf_end - buf_pos, @@ -143,10 +115,23 @@ void WriteMemoryProfile(char *buf, uptr buf_size) { mi.arena >> 20, mi.hblkhd >> 20, mi.fordblks >> 20, mi.keepcost >> 20); } -void FlushShadowMemory() { +uptr GetRSS() { + uptr mem[7] = {}; + __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7); + return mem[6]; +} + + +void FlushShadowMemoryCallback( + const SuspendedThreadsList &suspended_threads_list, + void *argument) { FlushUnneededShadowMemory(kLinuxShadowBeg, kLinuxShadowEnd - kLinuxShadowBeg); } +void FlushShadowMemory() { + StopTheWorld(FlushShadowMemoryCallback, 0); +} + #ifndef TSAN_GO static void ProtectRange(uptr beg, uptr end) { ScopedInRtl in_rtl; diff --git a/lib/tsan/rtl/tsan_report.cc b/lib/tsan/rtl/tsan_report.cc index c95c5c86be69..66307ae09dcd 100644 --- a/lib/tsan/rtl/tsan_report.cc +++ b/lib/tsan/rtl/tsan_report.cc @@ -13,9 +13,27 @@ #include "tsan_report.h" #include "tsan_platform.h" #include "tsan_rtl.h" +#include "sanitizer_common/sanitizer_report_decorator.h" namespace __tsan { +class Decorator: private __sanitizer::AnsiColorDecorator { + public: + Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + const char *Warning() { return Red(); } + const char *EndWarning() { return Default(); } + const char *Access() { return Blue(); } + const char *EndAccess() { return Default(); } + const char *ThreadDescription() { return Cyan(); } + const char *EndThreadDescription() { return Default(); } + const char *Location() { return Green(); } + const char *EndLocation() { return Default(); } + const char *Sleep() { return Yellow(); } + const char *EndSleep() { return Default(); } + const char *Mutex() { return Magenta(); } + const char *EndMutex() { return Default(); } +}; + ReportDesc::ReportDesc() : stacks(MBlockReportStack) , mops(MBlockReportMop) @@ -97,26 +115,32 @@ static const char *MopDesc(bool first, bool write, bool atomic) { } static void PrintMop(const ReportMop *mop, bool first) { + Decorator d; char thrbuf[kThreadBufSize]; + Printf("%s", d.Access()); Printf(" %s of size %d at %p by %s", MopDesc(first, mop->write, mop->atomic), mop->size, (void*)mop->addr, thread_name(thrbuf, mop->tid)); PrintMutexSet(mop->mset); Printf(":\n"); + Printf("%s", d.EndAccess()); PrintStack(mop->stack); } static void PrintLocation(const ReportLocation *loc) { + Decorator d; char thrbuf[kThreadBufSize]; + bool print_stack = false; + Printf("%s", d.Location()); if (loc->type == ReportLocationGlobal) { - Printf(" Location is global '%s' of size %zu at %zx (%s+%p)\n\n", + Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n", loc->name, loc->size, loc->addr, loc->module, loc->offset); } else if (loc->type == ReportLocationHeap) { char thrbuf[kThreadBufSize]; Printf(" Location is heap block of size %zu at %p allocated by %s:\n", loc->size, loc->addr, thread_name(thrbuf, loc->tid)); - PrintStack(loc->stack); + print_stack = true; } else if (loc->type == ReportLocationStack) { Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid)); } else if (loc->type == ReportLocationTLS) { @@ -124,22 +148,32 @@ static void PrintLocation(const ReportLocation *loc) { } else if (loc->type == ReportLocationFD) { Printf(" Location is file descriptor %d created by %s at:\n", loc->fd, thread_name(thrbuf, loc->tid)); - PrintStack(loc->stack); + print_stack = true; } + Printf("%s", d.EndLocation()); + if (print_stack) + PrintStack(loc->stack); } static void PrintMutex(const ReportMutex *rm) { + Decorator d; if (rm->destroyed) { + Printf("%s", d.Mutex()); Printf(" Mutex M%llu is already destroyed.\n\n", rm->id); + Printf("%s", d.EndMutex()); } else { + Printf("%s", d.Mutex()); Printf(" Mutex M%llu created at:\n", rm->id); + Printf("%s", d.EndMutex()); PrintStack(rm->stack); } } static void PrintThread(const ReportThread *rt) { + Decorator d; if (rt->id == 0) // Little sense in describing the main thread. return; + Printf("%s", d.ThreadDescription()); Printf(" Thread T%d", rt->id); if (rt->name && rt->name[0] != '\0') Printf(" '%s'", rt->name); @@ -150,11 +184,15 @@ static void PrintThread(const ReportThread *rt) { if (rt->stack) Printf(" at:"); Printf("\n"); + Printf("%s", d.EndThreadDescription()); PrintStack(rt->stack); } static void PrintSleep(const ReportStack *s) { + Decorator d; + Printf("%s", d.Sleep()); Printf(" As if synchronized via sleep:\n"); + Printf("%s", d.EndSleep()); PrintStack(s); } @@ -177,10 +215,13 @@ ReportStack *SkipTsanInternalFrames(ReportStack *ent) { } void PrintReport(const ReportDesc *rep) { + Decorator d; Printf("==================\n"); const char *rep_typ_str = ReportTypeString(rep->typ); + Printf("%s", d.Warning()); Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str, (int)internal_getpid()); + Printf("%s", d.EndWarning()); for (uptr i = 0; i < rep->stacks.Size(); i++) { if (i) @@ -212,31 +253,37 @@ void PrintReport(const ReportDesc *rep) { Printf("==================\n"); } -#else +#else // #ifndef TSAN_GO + +const int kMainThreadId = 1; void PrintStack(const ReportStack *ent) { if (ent == 0) { - Printf(" [failed to restore the stack]\n\n"); + Printf(" [failed to restore the stack]\n"); return; } for (int i = 0; ent; ent = ent->next, i++) { Printf(" %s()\n %s:%d +0x%zx\n", ent->func, ent->file, ent->line, (void*)ent->offset); } - Printf("\n"); } static void PrintMop(const ReportMop *mop, bool first) { - Printf("%s by goroutine %d:\n", + Printf("\n"); + Printf("%s by ", (first ? (mop->write ? "Write" : "Read") - : (mop->write ? "Previous write" : "Previous read")), - mop->tid); + : (mop->write ? "Previous write" : "Previous read"))); + if (mop->tid == kMainThreadId) + Printf("main goroutine:\n"); + else + Printf("goroutine %d:\n", mop->tid); PrintStack(mop->stack); } static void PrintThread(const ReportThread *rt) { - if (rt->id == 0) // Little sense in describing the main thread. + if (rt->id == kMainThreadId) return; + Printf("\n"); Printf("Goroutine %d (%s) created at:\n", rt->id, rt->running ? "running" : "finished"); PrintStack(rt->stack); @@ -244,7 +291,7 @@ static void PrintThread(const ReportThread *rt) { void PrintReport(const ReportDesc *rep) { Printf("==================\n"); - Printf("WARNING: DATA RACE\n"); + Printf("WARNING: DATA RACE"); for (uptr i = 0; i < rep->mops.Size(); i++) PrintMop(rep->mops[i], i == 0); for (uptr i = 0; i < rep->threads.Size(); i++) diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc index 5924858c84c5..1f66d29a741c 100644 --- a/lib/tsan/rtl/tsan_rtl.cc +++ b/lib/tsan/rtl/tsan_rtl.cc @@ -39,9 +39,13 @@ THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64); static char ctx_placeholder[sizeof(Context)] ALIGNED(64); // Can be overriden by a front-end. -bool CPP_WEAK OnFinalize(bool failed) { +#ifdef TSAN_EXTERNAL_HOOKS +bool OnFinalize(bool failed); +#else +bool WEAK OnFinalize(bool failed) { return failed; } +#endif static Context *ctx; Context *CTX() { @@ -74,7 +78,7 @@ Context::Context() CreateThreadContext, kMaxTid, kThreadQuarantineSize)) , racy_stacks(MBlockRacyStacks) , racy_addresses(MBlockRacyAddresses) - , fired_suppressions(MBlockRacyAddresses) { + , fired_suppressions(8) { } // The objects are allocated in TLS, so one may rely on zero-initialization. @@ -86,7 +90,6 @@ ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, // they may be accessed before the ctor. // , ignore_reads_and_writes() // , in_rtl() - , shadow_stack_pos(&shadow_stack[0]) #ifndef TSAN_GO , jmp_bufs(MBlockJmpBuf) #endif @@ -130,17 +133,38 @@ static void BackgroundThread(void *arg) { } u64 last_flush = NanoTime(); + uptr last_rss = 0; for (int i = 0; ; i++) { SleepForSeconds(1); u64 now = NanoTime(); // Flush memory if requested. - if (flags()->flush_memory_ms) { + if (flags()->flush_memory_ms > 0) { if (last_flush + flags()->flush_memory_ms * kMs2Ns < now) { + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: periodic memory flush\n"); FlushShadowMemory(); last_flush = NanoTime(); } } + if (flags()->memory_limit_mb > 0) { + uptr rss = GetRSS(); + uptr limit = uptr(flags()->memory_limit_mb) << 20; + if (flags()->verbosity > 0) { + Printf("ThreadSanitizer: memory flush check" + " RSS=%llu LAST=%llu LIMIT=%llu\n", + (u64)rss>>20, (u64)last_rss>>20, (u64)limit>>20); + } + if (2 * rss > limit + last_rss) { + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: flushing memory due to RSS\n"); + FlushShadowMemory(); + rss = GetRSS(); + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20); + } + last_rss = rss; + } // Write memory profile if requested. if (mprof_fd != kInvalidFd) @@ -176,8 +200,10 @@ void MapThreadTrace(uptr addr, uptr size) { DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size); CHECK_GE(addr, kTraceMemBegin); CHECK_LE(addr + size, kTraceMemBegin + kTraceMemSize); - if (addr != (uptr)MmapFixedNoReserve(addr, size)) { - Printf("FATAL: ThreadSanitizer can not mmap thread trace\n"); + uptr addr1 = (uptr)MmapFixedNoReserve(addr, size); + if (addr1 != addr) { + Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p->%p)\n", + addr, size, addr1); Die(); } } @@ -206,23 +232,21 @@ void Initialize(ThreadState *thr) { #endif InitializeFlags(&ctx->flags, env); // Setup correct file descriptor for error reports. - if (internal_strcmp(flags()->log_path, "stdout") == 0) - __sanitizer_set_report_fd(kStdoutFd); - else if (internal_strcmp(flags()->log_path, "stderr") == 0) - __sanitizer_set_report_fd(kStderrFd); - else - __sanitizer_set_report_path(flags()->log_path); + __sanitizer_set_report_path(flags()->log_path); InitializeSuppressions(); #ifndef TSAN_GO + InitializeLibIgnore(); // Initialize external symbolizer before internal threads are started. const char *external_symbolizer = flags()->external_symbolizer_path; - if (external_symbolizer != 0 && external_symbolizer[0] != '\0') { - if (!InitializeExternalSymbolizer(external_symbolizer)) { - Printf("Failed to start external symbolizer: '%s'\n", - external_symbolizer); - Die(); - } + bool external_symbolizer_started = + Symbolizer::Init(external_symbolizer)->IsExternalAvailable(); + if (external_symbolizer != 0 && external_symbolizer[0] != '\0' && + !external_symbolizer_started) { + Printf("Failed to start external symbolizer: '%s'\n", + external_symbolizer); + Die(); } + Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer); #endif internal_start_thread(&BackgroundThread, 0); @@ -556,7 +580,9 @@ static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size, // Don't want to touch lots of shadow memory. // If a program maps 10MB stack, there is no need reset the whole range. size = (size + (kShadowCell - 1)) & ~(kShadowCell - 1); - if (size < 64*1024) { + // UnmapOrDie/MmapFixedNoReserve does not work on Windows, + // so we do it only for C/C++. + if (kGoMode || size < 64*1024) { u64 *p = (u64*)MemToShadow(addr); CHECK(IsShadowMem((uptr)p)); CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1))); @@ -636,9 +662,9 @@ void FuncEntry(ThreadState *thr, uptr pc) { // Shadow stack maintenance can be replaced with // stack unwinding during trace switch (which presumably must be faster). - DCHECK_GE(thr->shadow_stack_pos, &thr->shadow_stack[0]); + DCHECK_GE(thr->shadow_stack_pos, thr->shadow_stack); #ifndef TSAN_GO - DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]); + DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #else if (thr->shadow_stack_pos == thr->shadow_stack_end) { const int sz = thr->shadow_stack_end - thr->shadow_stack; @@ -664,23 +690,40 @@ void FuncExit(ThreadState *thr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeFuncExit, 0); - DCHECK_GT(thr->shadow_stack_pos, &thr->shadow_stack[0]); + DCHECK_GT(thr->shadow_stack_pos, thr->shadow_stack); #ifndef TSAN_GO - DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]); + DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #endif thr->shadow_stack_pos--; } -void IgnoreCtl(ThreadState *thr, bool write, bool begin) { - DPrintf("#%d: IgnoreCtl(%d, %d)\n", thr->tid, write, begin); - thr->ignore_reads_and_writes += begin ? 1 : -1; +void ThreadIgnoreBegin(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreBegin\n", thr->tid); + thr->ignore_reads_and_writes++; + CHECK_GT(thr->ignore_reads_and_writes, 0); + thr->fast_state.SetIgnoreBit(); +} + +void ThreadIgnoreEnd(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreEnd\n", thr->tid); + thr->ignore_reads_and_writes--; CHECK_GE(thr->ignore_reads_and_writes, 0); - if (thr->ignore_reads_and_writes) - thr->fast_state.SetIgnoreBit(); - else + if (thr->ignore_reads_and_writes == 0) thr->fast_state.ClearIgnoreBit(); } +void ThreadIgnoreSyncBegin(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid); + thr->ignore_sync++; + CHECK_GT(thr->ignore_sync, 0); +} + +void ThreadIgnoreSyncEnd(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreSyncEnd\n", thr->tid); + thr->ignore_sync--; + CHECK_GE(thr->ignore_sync, 0); +} + bool MD5Hash::operator==(const MD5Hash &other) const { return hash[0] == other.hash[0] && hash[1] == other.hash[1]; } diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h index f1a73e457331..4ee667543a6e 100644 --- a/lib/tsan/rtl/tsan_rtl.h +++ b/lib/tsan/rtl/tsan_rtl.h @@ -27,7 +27,10 @@ #define TSAN_RTL_H #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libignore.h" +#include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_thread_registry.h" #include "tsan_clock.h" #include "tsan_defs.h" @@ -409,21 +412,19 @@ struct ThreadState { // We do not distinguish beteween ignoring reads and writes // for better performance. int ignore_reads_and_writes; + int ignore_sync; + // C/C++ uses fixed size shadow stack embed into Trace. + // Go uses malloc-allocated shadow stack with dynamic size. + uptr *shadow_stack; + uptr *shadow_stack_end; uptr *shadow_stack_pos; u64 *racy_shadow_addr; u64 racy_state[2]; -#ifndef TSAN_GO - // C/C++ uses embed shadow stack of fixed size. - uptr shadow_stack[kShadowStackSize]; -#else - // Go uses satellite shadow stack with dynamic size. - uptr *shadow_stack; - uptr *shadow_stack_end; -#endif MutexSet mset; ThreadClock clock; #ifndef TSAN_GO AllocatorCache alloc_cache; + InternalAllocatorCache internal_alloc_cache; Vector<JmpBuf> jmp_bufs; #endif u64 stat[StatCnt]; @@ -431,6 +432,7 @@ struct ThreadState { const int unique_id; int in_rtl; bool in_symbolizer; + bool in_ignored_lib; bool is_alive; bool is_freeing; bool is_vptr_access; @@ -531,7 +533,8 @@ struct Context { Vector<RacyStacks> racy_stacks; Vector<RacyAddress> racy_addresses; - Vector<FiredSuppression> fired_suppressions; + // Number of fired suppressions may be large enough. + InternalMmapVector<FiredSuppression> fired_suppressions; Flags flags; @@ -594,13 +597,15 @@ void MapThreadTrace(uptr addr, uptr size); void DontNeedShadowFor(uptr addr, uptr size); void InitializeShadowMemory(); void InitializeInterceptors(); +void InitializeLibIgnore(); void InitializeDynamicAnnotations(); void ReportRace(ThreadState *thr); bool OutputReport(Context *ctx, const ScopedReport &srep, const ReportStack *suppress_stack1 = 0, - const ReportStack *suppress_stack2 = 0); + const ReportStack *suppress_stack2 = 0, + const ReportLocation *suppress_loc = 0); bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, const StackTrace &trace); @@ -672,7 +677,11 @@ void ALWAYS_INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc, void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size); -void IgnoreCtl(ThreadState *thr, bool write, bool begin); + +void ThreadIgnoreBegin(ThreadState *thr); +void ThreadIgnoreEnd(ThreadState *thr); +void ThreadIgnoreSyncBegin(ThreadState *thr); +void ThreadIgnoreSyncEnd(ThreadState *thr); void FuncEntry(ThreadState *thr, uptr pc); void FuncExit(ThreadState *thr); @@ -696,12 +705,17 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all = false); void MutexReadLock(ThreadState *thr, uptr pc, uptr addr); void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr); +void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD void Acquire(ThreadState *thr, uptr pc, uptr addr); void AcquireGlobal(ThreadState *thr, uptr pc); void Release(ThreadState *thr, uptr pc, uptr addr); void ReleaseStore(ThreadState *thr, uptr pc, uptr addr); void AfterSleep(ThreadState *thr, uptr pc); +void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c); +void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); +void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c); +void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); // The hacky call uses custom calling convention and an assembly thunk. // It is considerably faster that a normal call for the caller @@ -714,11 +728,11 @@ void AfterSleep(ThreadState *thr, uptr pc); // so we create a reserve stack frame for it (1024b must be enough). #define HACKY_CALL(f) \ __asm__ __volatile__("sub $1024, %%rsp;" \ - "/*.cfi_adjust_cfa_offset 1024;*/" \ + ".cfi_adjust_cfa_offset 1024;" \ ".hidden " #f "_thunk;" \ "call " #f "_thunk;" \ "add $1024, %%rsp;" \ - "/*.cfi_adjust_cfa_offset -1024;*/" \ + ".cfi_adjust_cfa_offset -1024;" \ ::: "memory", "cc"); #else #define HACKY_CALL(f) f() diff --git a/lib/tsan/rtl/tsan_rtl_mutex.cc b/lib/tsan/rtl/tsan_rtl_mutex.cc index cf2e44dd09ee..409391678f01 100644 --- a/lib/tsan/rtl/tsan_rtl_mutex.cc +++ b/lib/tsan/rtl/tsan_rtl_mutex.cc @@ -95,16 +95,13 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) { } else if (s->owner_tid == thr->tid) { CHECK_GT(s->recursion, 0); } else { - Printf("ThreadSanitizer WARNING: double lock\n"); + Printf("ThreadSanitizer WARNING: double lock of mutex %p\n", addr); PrintCurrentStack(thr, pc); } if (s->recursion == 0) { StatInc(thr, StatMutexLock); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); - StatInc(thr, StatSyncAcquire); - thr->clock.acquire(&s->read_clock); - StatInc(thr, StatSyncAcquire); + AcquireImpl(thr, pc, &s->clock); + AcquireImpl(thr, pc, &s->read_clock); } else if (!s->is_recursive) { StatInc(thr, StatMutexRecLock); } @@ -125,13 +122,14 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { if (s->recursion == 0) { if (!s->is_broken) { s->is_broken = true; - Printf("ThreadSanitizer WARNING: unlock of unlocked mutex\n"); + Printf("ThreadSanitizer WARNING: unlock of unlocked mutex %p\n", addr); PrintCurrentStack(thr, pc); } } else if (s->owner_tid != thr->tid) { if (!s->is_broken) { s->is_broken = true; - Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n"); + Printf("ThreadSanitizer WARNING: mutex %p is unlocked by wrong thread\n", + addr); PrintCurrentStack(thr, pc); } } else { @@ -140,10 +138,7 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); s->owner_tid = SyncVar::kInvalidTid; - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.ReleaseStore(&s->clock); - StatInc(thr, StatSyncRelease); + ReleaseStoreImpl(thr, pc, &s->clock); } else { StatInc(thr, StatMutexRecUnlock); } @@ -163,13 +158,12 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId()); if (s->owner_tid != SyncVar::kInvalidTid) { - Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n"); + Printf("ThreadSanitizer WARNING: read lock of a write locked mutex %p\n", + addr); PrintCurrentStack(thr, pc); } - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); s->last_lock = thr->fast_state.raw(); - StatInc(thr, StatSyncAcquire); thr->mset.Add(s->GetId(), false, thr->fast_state.epoch()); s->mtx.ReadUnlock(); } @@ -184,14 +178,11 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); if (s->owner_tid != SyncVar::kInvalidTid) { - Printf("ThreadSanitizer WARNING: read unlock of a write " - "locked mutex\n"); + Printf("ThreadSanitizer WARNING: read unlock of a write locked mutex %p\n", + addr); PrintCurrentStack(thr, pc); } - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&s->read_clock); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, pc, &s->read_clock); s->mtx.Unlock(); thr->mset.Del(s->GetId(), false); } @@ -209,10 +200,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { StatInc(thr, StatMutexReadUnlock); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&s->read_clock); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, pc, &s->read_clock); } else if (s->owner_tid == thr->tid) { // Seems to be write unlock. thr->fast_state.IncrementEpoch(); @@ -222,33 +210,37 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); s->owner_tid = SyncVar::kInvalidTid; - // FIXME: Refactor me, plz. - // The sequence of events is quite tricky and doubled in several places. - // First, it's a bug to increment the epoch w/o writing to the trace. - // Then, the acquire/release logic can be factored out as well. - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.ReleaseStore(&s->clock); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, pc, &s->clock); } else { StatInc(thr, StatMutexRecUnlock); } } else if (!s->is_broken) { s->is_broken = true; - Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n"); + Printf("ThreadSanitizer WARNING: mutex %p is unlock by wrong thread\n", + addr); PrintCurrentStack(thr, pc); } thr->mset.Del(s->GetId(), write); s->mtx.Unlock(); } +void MutexRepair(ThreadState *thr, uptr pc, uptr addr) { + Context *ctx = CTX(); + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr); + SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true); + s->owner_tid = SyncVar::kInvalidTid; + s->recursion = 0; + s->mtx.Unlock(); +} + void Acquire(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: Acquire %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); - StatInc(thr, StatSyncAcquire); + AcquireImpl(thr, pc, &s->clock); s->mtx.ReadUnlock(); } @@ -262,6 +254,9 @@ static void UpdateClockCallback(ThreadContextBase *tctx_base, void *arg) { } void AcquireGlobal(ThreadState *thr, uptr pc) { + DPrintf("#%d: AcquireGlobal\n", thr->tid); + if (thr->ignore_sync) + return; ThreadRegistryLock l(CTX()->thread_registry); CTX()->thread_registry->RunCallbackForEachThreadLocked( UpdateClockCallback, thr); @@ -270,20 +265,26 @@ void AcquireGlobal(ThreadState *thr, uptr pc) { void Release(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: Release %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.release(&s->clock); - StatInc(thr, StatSyncRelease); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseImpl(thr, pc, &s->clock); s->mtx.Unlock(); } void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.ReleaseStore(&s->clock); - StatInc(thr, StatSyncRelease); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseStoreImpl(thr, pc, &s->clock); s->mtx.Unlock(); } @@ -298,6 +299,9 @@ static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) { } void AfterSleep(ThreadState *thr, uptr pc) { + DPrintf("#%d: AfterSleep %zx\n", thr->tid); + if (thr->ignore_sync) + return; thr->last_sleep_stack_id = CurrentStackId(thr, pc); ThreadRegistryLock l(CTX()->thread_registry); CTX()->thread_registry->RunCallbackForEachThreadLocked( @@ -305,4 +309,40 @@ void AfterSleep(ThreadState *thr, uptr pc) { } #endif +void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->clock.acquire(c); + StatInc(thr, StatSyncAcquire); +} + +void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.release(c); + StatInc(thr, StatSyncRelease); +} + +void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.ReleaseStore(c); + StatInc(thr, StatSyncRelease); +} + +void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.acq_rel(c); + StatInc(thr, StatSyncAcquire); + StatInc(thr, StatSyncRelease); +} + } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_rtl_report.cc b/lib/tsan/rtl/tsan_rtl_report.cc index f77a7a2efa96..4fed43faf25f 100644 --- a/lib/tsan/rtl/tsan_rtl_report.cc +++ b/lib/tsan/rtl/tsan_rtl_report.cc @@ -95,8 +95,9 @@ static void StackStripMain(ReportStack *stack) { DPrintf("Bottom stack frame of stack %zx is missed\n", stack->pc); } #else - if (last && 0 == internal_strcmp(last, "schedunlock")) - last_frame2->next = 0; + // The last frame always point into runtime (gosched0, goexit0, runtime.main). + last_frame2->next = 0; + (void)last; #endif } @@ -105,17 +106,25 @@ static ReportStack *SymbolizeStack(const StackTrace& trace) { return 0; ReportStack *stack = 0; for (uptr si = 0; si < trace.Size(); si++) { + const uptr pc = trace.Get(si); +#ifndef TSAN_GO // We obtain the return address, that is, address of the next instruction, // so offset it by 1 byte. - bool is_last = (si == trace.Size() - 1); - ReportStack *ent = SymbolizeCode(trace.Get(si) - !is_last); + const uptr pc1 = __sanitizer::StackTrace::GetPreviousInstructionPc(pc); +#else + // FIXME(dvyukov): Go sometimes uses address of a function as top pc. + uptr pc1 = pc; + if (si != trace.Size() - 1) + pc1 -= 1; +#endif + ReportStack *ent = SymbolizeCode(pc1); CHECK_NE(ent, 0); ReportStack *last = ent; while (last->next) { - last->pc += !is_last; + last->pc = pc; // restore original pc for report last = last->next; } - last->pc += !is_last; + last->pc = pc; // restore original pc for report last->next = stack; stack = ent; } @@ -401,7 +410,7 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) { const u64 ebegin = RoundDown(eend, kTracePartSize); DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n", tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx); - InternalScopedBuffer<uptr> stack(1024); // FIXME: de-hardcode 1024 + InternalScopedBuffer<uptr> stack(kShadowStackSize); for (uptr i = 0; i < hdr->stack0.Size(); i++) { stack[i] = hdr->stack0.Get(i); DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]); @@ -501,28 +510,33 @@ static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2], bool OutputReport(Context *ctx, const ScopedReport &srep, const ReportStack *suppress_stack1, - const ReportStack *suppress_stack2) { + const ReportStack *suppress_stack2, + const ReportLocation *suppress_loc) { atomic_store(&ctx->last_symbolize_time_ns, NanoTime(), memory_order_relaxed); const ReportDesc *rep = srep.GetReport(); Suppression *supp = 0; uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack1, &supp); if (suppress_pc == 0) suppress_pc = IsSuppressed(rep->typ, suppress_stack2, &supp); + if (suppress_pc == 0) + suppress_pc = IsSuppressed(rep->typ, suppress_loc, &supp); if (suppress_pc != 0) { FiredSuppression s = {srep.GetReport()->typ, suppress_pc, supp}; - ctx->fired_suppressions.PushBack(s); + ctx->fired_suppressions.push_back(s); } if (OnReport(rep, suppress_pc != 0)) return false; PrintReport(rep); - CTX()->nreported++; + ctx->nreported++; + if (flags()->halt_on_error) + internal__exit(flags()->exitcode); return true; } bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, const StackTrace &trace) { - for (uptr k = 0; k < ctx->fired_suppressions.Size(); k++) { + for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) continue; for (uptr j = 0; j < trace.Size(); j++) { @@ -537,6 +551,22 @@ bool IsFiredSuppression(Context *ctx, return false; } +static bool IsFiredSuppression(Context *ctx, + const ScopedReport &srep, + uptr addr) { + for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { + if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) + continue; + FiredSuppression *s = &ctx->fired_suppressions[k]; + if (addr == s->pc) { + if (s->supp) + s->supp->hit_count++; + return true; + } + } + return false; +} + bool FrameIsInternal(const ReportStack *frame) { return frame != 0 && frame->file != 0 && (internal_strstr(frame->file, "tsan_interceptors.cc") || @@ -569,7 +599,7 @@ static bool IsJavaNonsense(const ReportDesc *rep) { && frame->module == 0)) { if (frame) { FiredSuppression supp = {rep->typ, frame->pc, 0}; - CTX()->fired_suppressions.PushBack(supp); + CTX()->fired_suppressions.push_back(supp); } return true; } @@ -630,6 +660,8 @@ void ReportRace(ThreadState *thr) { else if (freed) typ = ReportTypeUseAfterFree; ScopedReport rep(typ); + if (IsFiredSuppression(ctx, rep, addr)) + return; const uptr kMop = 2; StackTrace traces[kMop]; const uptr toppc = TraceTopPC(thr); @@ -640,6 +672,8 @@ void ReportRace(ThreadState *thr) { new(mset2.data()) MutexSet(); Shadow s2(thr->racy_state[1]); RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2.data()); + if (IsFiredSuppression(ctx, rep, traces[1])) + return; if (HandleRacyStacks(thr, traces, addr_min, addr_max)) return; @@ -672,8 +706,11 @@ void ReportRace(ThreadState *thr) { } #endif + ReportLocation *suppress_loc = rep.GetReport()->locs.Size() ? + rep.GetReport()->locs[0] : 0; if (!OutputReport(ctx, rep, rep.GetReport()->mops[0]->stack, - rep.GetReport()->mops[1]->stack)) + rep.GetReport()->mops[1]->stack, + suppress_loc)) return; AddRacyStacks(thr, traces, addr_min, addr_max); @@ -689,8 +726,8 @@ void PrintCurrentStackSlow() { #ifndef TSAN_GO __sanitizer::StackTrace *ptrace = new(internal_alloc(MBlockStackTrace, sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace; - ptrace->SlowUnwindStack(__sanitizer::StackTrace::GetCurrentPc(), - kStackTraceMax); + ptrace->Unwind(kStackTraceMax, __sanitizer::StackTrace::GetCurrentPc(), + 0, 0, 0, false); for (uptr i = 0; i < ptrace->size / 2; i++) { uptr tmp = ptrace->trace[i]; ptrace->trace[i] = ptrace->trace[ptrace->size - i - 1]; diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc index ee13fa18db3f..4e451b042947 100644 --- a/lib/tsan/rtl/tsan_rtl_thread.cc +++ b/lib/tsan/rtl/tsan_rtl_thread.cc @@ -41,8 +41,7 @@ void ThreadContext::OnDead() { void ThreadContext::OnJoined(void *arg) { ThreadState *caller_thr = static_cast<ThreadState *>(arg); - caller_thr->clock.acquire(&sync); - StatInc(caller_thr, StatSyncAcquire); + AcquireImpl(caller_thr, 0, &sync); sync.Reset(); } @@ -59,10 +58,7 @@ void ThreadContext::OnCreated(void *arg) { args->thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0); - args->thr->clock.set(args->thr->tid, args->thr->fast_state.epoch()); - args->thr->fast_synch_epoch = args->thr->fast_state.epoch(); - args->thr->clock.release(&sync); - StatInc(args->thr, StatSyncRelease); + ReleaseImpl(args->thr, 0, &sync); #ifdef TSAN_GO creation_stack.ObtainCurrent(args->thr, args->pc); #else @@ -95,21 +91,23 @@ void ThreadContext::OnStarted(void *arg) { epoch1 = (u64)-1; new(thr) ThreadState(CTX(), tid, unique_id, epoch0, args->stk_addr, args->stk_size, args->tls_addr, args->tls_size); -#ifdef TSAN_GO +#ifndef TSAN_GO + thr->shadow_stack = &ThreadTrace(thr->tid)->shadow_stack[0]; + thr->shadow_stack_pos = thr->shadow_stack; + thr->shadow_stack_end = thr->shadow_stack + kShadowStackSize; +#else // Setup dynamic shadow stack. const int kInitStackSize = 8; - args->thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, + thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, kInitStackSize * sizeof(uptr)); - args->thr->shadow_stack_pos = thr->shadow_stack; - args->thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; + thr->shadow_stack_pos = thr->shadow_stack; + thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; #endif #ifndef TSAN_GO - AllocatorThreadStart(args->thr); + AllocatorThreadStart(thr); #endif - thr = args->thr; thr->fast_synch_epoch = epoch0; - thr->clock.set(tid, epoch0); - thr->clock.acquire(&sync); + AcquireImpl(thr, 0, &sync); thr->fast_state.SetHistorySize(flags()->history_size); const uptr trace = (epoch0 / kTracePartSize) % TraceParts(); Trace *thr_trace = ThreadTrace(thr->tid); @@ -128,10 +126,7 @@ void ThreadContext::OnFinished() { thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&sync); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, 0, &sync); } epoch1 = thr->fast_state.epoch(); @@ -170,6 +165,10 @@ static void ThreadCheckIgnore(ThreadState *thr) { Printf("ThreadSanitizer: thread T%d finished with ignores enabled.\n", thr->tid); } + if (thr->ignore_sync) { + Printf("ThreadSanitizer: thread T%d finished with sync ignores enabled.\n", + thr->tid); + } } void ThreadFinalize(ThreadState *thr) { @@ -374,25 +373,4 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, } } -void MemoryAccessRangeStep(ThreadState *thr, uptr pc, uptr addr, - uptr size, uptr step, bool is_write) { - if (size == 0) - return; - FastState fast_state = thr->fast_state; - if (fast_state.GetIgnoreBit()) - return; - StatInc(thr, StatMopRange); - fast_state.IncrementEpoch(); - thr->fast_state = fast_state; - TraceAddEvent(thr, fast_state, EventTypeMop, pc); - - for (uptr addr_end = addr + size; addr < addr_end; addr += step) { - u64 *shadow_mem = (u64*)MemToShadow(addr); - Shadow cur(fast_state); - cur.SetWrite(is_write); - cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kSizeLog1); - MemoryAccessImpl(thr, addr, kSizeLog1, is_write, false, - shadow_mem, cur); - } -} } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc index 9676e0872e08..f9dbf121cef4 100644 --- a/lib/tsan/rtl/tsan_stat.cc +++ b/lib/tsan/rtl/tsan_stat.cc @@ -138,9 +138,11 @@ void StatOutput(u64 *stat) { name[StatInt_strcpy] = " strcpy "; name[StatInt_strncpy] = " strncpy "; name[StatInt_strstr] = " strstr "; + name[StatInt_strdup] = " strdup "; name[StatInt_strcasecmp] = " strcasecmp "; name[StatInt_strncasecmp] = " strncasecmp "; name[StatInt_atexit] = " atexit "; + name[StatInt__exit] = " _exit "; name[StatInt___cxa_guard_acquire] = " __cxa_guard_acquire "; name[StatInt___cxa_guard_release] = " __cxa_guard_release "; name[StatInt___cxa_guard_abort] = " __cxa_guard_abort "; @@ -180,6 +182,7 @@ void StatOutput(u64 *stat) { name[StatInt_pthread_barrier_wait] = " pthread_barrier_wait "; name[StatInt_pthread_once] = " pthread_once "; name[StatInt_pthread_getschedparam] = " pthread_getschedparam "; + name[StatInt_pthread_setname_np] = " pthread_setname_np "; name[StatInt_sem_init] = " sem_init "; name[StatInt_sem_destroy] = " sem_destroy "; name[StatInt_sem_wait] = " sem_wait "; @@ -229,11 +232,13 @@ void StatOutput(u64 *stat) { name[StatInt_pread] = " pread "; name[StatInt_pread64] = " pread64 "; name[StatInt_readv] = " readv "; + name[StatInt_preadv] = " preadv "; name[StatInt_preadv64] = " preadv64 "; name[StatInt_write] = " write "; name[StatInt_pwrite] = " pwrite "; name[StatInt_pwrite64] = " pwrite64 "; name[StatInt_writev] = " writev "; + name[StatInt_pwritev] = " pwritev "; name[StatInt_pwritev64] = " pwritev64 "; name[StatInt_send] = " send "; name[StatInt_sendmsg] = " sendmsg "; @@ -253,8 +258,10 @@ void StatOutput(u64 *stat) { name[StatInt_epoll_ctl] = " epoll_ctl "; name[StatInt_epoll_wait] = " epoll_wait "; name[StatInt_poll] = " poll "; + name[StatInt_ppoll] = " ppoll "; name[StatInt_sigaction] = " sigaction "; name[StatInt_signal] = " signal "; + name[StatInt_sigsuspend] = " sigsuspend "; name[StatInt_raise] = " raise "; name[StatInt_kill] = " kill "; name[StatInt_pthread_kill] = " pthread_kill "; @@ -285,6 +292,7 @@ void StatOutput(u64 *stat) { name[StatInt_ctime_r] = " ctime_r "; name[StatInt_asctime] = " asctime "; name[StatInt_asctime_r] = " asctime_r "; + name[StatInt_strptime] = " strptime "; name[StatInt_frexp] = " frexp "; name[StatInt_frexpf] = " frexpf "; name[StatInt_frexpl] = " frexpl "; @@ -311,7 +319,9 @@ void StatOutput(u64 *stat) { name[StatInt_wait4] = " wait4 "; name[StatInt_inet_ntop] = " inet_ntop "; name[StatInt_inet_pton] = " inet_pton "; + name[StatInt_inet_aton] = " inet_aton "; name[StatInt_getaddrinfo] = " getaddrinfo "; + name[StatInt_getnameinfo] = " getnameinfo "; name[StatInt_getsockname] = " getsockname "; name[StatInt_gethostent] = " gethostent "; name[StatInt_gethostbyname] = " gethostbyname "; @@ -322,6 +332,99 @@ void StatOutput(u64 *stat) { name[StatInt_gethostbyname2_r] = " gethostbyname2_r "; name[StatInt_gethostbyaddr_r] = " gethostbyaddr_r "; name[StatInt_getsockopt] = " getsockopt "; + name[StatInt_modf] = " modf "; + name[StatInt_modff] = " modff "; + name[StatInt_modfl] = " modfl "; + name[StatInt_getpeername] = " getpeername "; + name[StatInt_ioctl] = " ioctl "; + name[StatInt_sysinfo] = " sysinfo "; + name[StatInt_readdir] = " readdir "; + name[StatInt_readdir64] = " readdir64 "; + name[StatInt_readdir_r] = " readdir_r "; + name[StatInt_readdir64_r] = " readdir64_r "; + name[StatInt_ptrace] = " ptrace "; + name[StatInt_setlocale] = " setlocale "; + name[StatInt_getcwd] = " getcwd "; + name[StatInt_get_current_dir_name] = " get_current_dir_name "; + name[StatInt_strtoimax] = " strtoimax "; + name[StatInt_strtoumax] = " strtoumax "; + name[StatInt_mbstowcs] = " mbstowcs "; + name[StatInt_mbsrtowcs] = " mbsrtowcs "; + name[StatInt_mbsnrtowcs] = " mbsnrtowcs "; + name[StatInt_wcstombs] = " wcstombs "; + name[StatInt_wcsrtombs] = " wcsrtombs "; + name[StatInt_wcsnrtombs] = " wcsnrtombs "; + name[StatInt_tcgetattr] = " tcgetattr "; + name[StatInt_realpath] = " realpath "; + name[StatInt_canonicalize_file_name] = " canonicalize_file_name "; + name[StatInt_confstr] = " confstr "; + name[StatInt_sched_getaffinity] = " sched_getaffinity "; + name[StatInt_strerror] = " strerror "; + name[StatInt_strerror_r] = " strerror_r "; + name[StatInt_scandir] = " scandir "; + name[StatInt_scandir64] = " scandir64 "; + name[StatInt_getgroups] = " getgroups "; + name[StatInt_wordexp] = " wordexp "; + name[StatInt_sigwait] = " sigwait "; + name[StatInt_sigwaitinfo] = " sigwaitinfo "; + name[StatInt_sigtimedwait] = " sigtimedwait "; + name[StatInt_sigemptyset] = " sigemptyset "; + name[StatInt_sigfillset] = " sigfillset "; + name[StatInt_sigpending] = " sigpending "; + name[StatInt_sigprocmask] = " sigprocmask "; + name[StatInt_backtrace] = " backtrace "; + name[StatInt_backtrace_symbols] = " backtrace_symbols "; + name[StatInt_dlopen] = " dlopen "; + name[StatInt_dlclose] = " dlclose "; + name[StatInt_getmntent] = " getmntent "; + name[StatInt_getmntent_r] = " getmntent_r "; + name[StatInt_statfs] = " statfs "; + name[StatInt_statfs64] = " statfs64 "; + name[StatInt_fstatfs] = " fstatfs "; + name[StatInt_fstatfs64] = " fstatfs64 "; + name[StatInt_statvfs] = " statvfs "; + name[StatInt_statvfs64] = " statvfs64 "; + name[StatInt_fstatvfs] = " fstatvfs "; + name[StatInt_fstatvfs64] = " fstatvfs64 "; + name[StatInt_initgroups] = " initgroups "; + name[StatInt_ether_ntoa] = " ether_ntoa "; + name[StatInt_ether_aton] = " ether_aton "; + name[StatInt_ether_ntoa_r] = " ether_ntoa_r "; + name[StatInt_ether_aton_r] = " ether_aton_r "; + name[StatInt_ether_ntohost] = " ether_ntohost "; + name[StatInt_ether_hostton] = " ether_hostton "; + name[StatInt_ether_line] = " ether_line "; + name[StatInt_shmctl] = " shmctl "; + name[StatInt_random_r] = " random_r "; + name[StatInt_tmpnam] = " tmpnam "; + name[StatInt_tmpnam_r] = " tmpnam_r "; + name[StatInt_tempnam] = " tempnam "; + name[StatInt_sincos] = " sincos "; + name[StatInt_sincosf] = " sincosf "; + name[StatInt_sincosl] = " sincosl "; + name[StatInt_remquo] = " remquo "; + name[StatInt_remquof] = " remquof "; + name[StatInt_remquol] = " remquol "; + name[StatInt_lgamma] = " lgamma "; + name[StatInt_lgammaf] = " lgammaf "; + name[StatInt_lgammal] = " lgammal "; + name[StatInt_lgamma_r] = " lgamma_r "; + name[StatInt_lgammaf_r] = " lgammaf_r "; + name[StatInt_lgammal_r] = " lgammal_r "; + name[StatInt_drand48_r] = " drand48_r "; + name[StatInt_lrand48_r] = " lrand48_r "; + name[StatInt_getline] = " getline "; + name[StatInt_getdelim] = " getdelim "; + + name[StatInt_pthread_attr_getdetachstate] = " pthread_addr_getdetachstate "; // NOLINT + name[StatInt_pthread_attr_getguardsize] = " pthread_addr_getguardsize "; // NOLINT + name[StatInt_pthread_attr_getschedparam] = " pthread_addr_getschedparam "; // NOLINT + name[StatInt_pthread_attr_getschedpolicy] = " pthread_addr_getschedpolicy "; // NOLINT + name[StatInt_pthread_attr_getinheritsched] = " pthread_addr_getinheritsched "; // NOLINT + name[StatInt_pthread_attr_getscope] = " pthread_addr_getscope "; // NOLINT + name[StatInt_pthread_attr_getstacksize] = " pthread_addr_getstacksize "; // NOLINT + name[StatInt_pthread_attr_getstack] = " pthread_addr_getstack "; // NOLINT + name[StatInt_pthread_attr_getaffinity_np] = " pthread_addr_getaffinity_np "; // NOLINT name[StatAnnotation] = "Dynamic annotations "; name[StatAnnotateHappensBefore] = " HappensBefore "; @@ -353,6 +456,8 @@ void StatOutput(u64 *stat) { name[StatAnnotateIgnoreReadsEnd] = " IgnoreReadsEnd "; name[StatAnnotateIgnoreWritesBegin] = " IgnoreWritesBegin "; name[StatAnnotateIgnoreWritesEnd] = " IgnoreWritesEnd "; + name[StatAnnotateIgnoreSyncBegin] = " IgnoreSyncBegin "; + name[StatAnnotateIgnoreSyncEnd] = " IgnoreSyncEnd "; name[StatAnnotatePublishMemoryRange] = " PublishMemoryRange "; name[StatAnnotateUnpublishMemoryRange] = " UnpublishMemoryRange "; name[StatAnnotateThreadName] = " ThreadName "; diff --git a/lib/tsan/rtl/tsan_stat.h b/lib/tsan/rtl/tsan_stat.h index d5c8b4389394..a44edfcd65c6 100644 --- a/lib/tsan/rtl/tsan_stat.h +++ b/lib/tsan/rtl/tsan_stat.h @@ -137,7 +137,9 @@ enum StatType { StatInt_strcasecmp, StatInt_strncasecmp, StatInt_strstr, + StatInt_strdup, StatInt_atexit, + StatInt__exit, StatInt___cxa_guard_acquire, StatInt___cxa_guard_release, StatInt___cxa_guard_abort, @@ -175,6 +177,7 @@ enum StatType { StatInt_pthread_barrier_wait, StatInt_pthread_once, StatInt_pthread_getschedparam, + StatInt_pthread_setname_np, StatInt_sem_init, StatInt_sem_destroy, StatInt_sem_wait, @@ -224,11 +227,13 @@ enum StatType { StatInt_pread, StatInt_pread64, StatInt_readv, + StatInt_preadv, StatInt_preadv64, StatInt_write, StatInt_pwrite, StatInt_pwrite64, StatInt_writev, + StatInt_pwritev, StatInt_pwritev64, StatInt_send, StatInt_sendmsg, @@ -248,8 +253,10 @@ enum StatType { StatInt_epoll_ctl, StatInt_epoll_wait, StatInt_poll, + StatInt_ppoll, StatInt_sigaction, StatInt_signal, + StatInt_sigsuspend, StatInt_raise, StatInt_kill, StatInt_pthread_kill, @@ -280,6 +287,7 @@ enum StatType { StatInt_ctime_r, StatInt_asctime, StatInt_asctime_r, + StatInt_strptime, StatInt_frexp, StatInt_frexpf, StatInt_frexpl, @@ -306,7 +314,9 @@ enum StatType { StatInt_wait4, StatInt_inet_ntop, StatInt_inet_pton, + StatInt_inet_aton, StatInt_getaddrinfo, + StatInt_getnameinfo, StatInt_getsockname, StatInt_gethostent, StatInt_gethostbyname, @@ -317,6 +327,99 @@ enum StatType { StatInt_gethostbyname2_r, StatInt_gethostbyaddr_r, StatInt_getsockopt, + StatInt_modf, + StatInt_modff, + StatInt_modfl, + StatInt_getpeername, + StatInt_ioctl, + StatInt_sysinfo, + StatInt_readdir, + StatInt_readdir64, + StatInt_readdir_r, + StatInt_readdir64_r, + StatInt_ptrace, + StatInt_setlocale, + StatInt_getcwd, + StatInt_get_current_dir_name, + StatInt_strtoimax, + StatInt_strtoumax, + StatInt_mbstowcs, + StatInt_mbsrtowcs, + StatInt_mbsnrtowcs, + StatInt_wcstombs, + StatInt_wcsrtombs, + StatInt_wcsnrtombs, + StatInt_tcgetattr, + StatInt_realpath, + StatInt_canonicalize_file_name, + StatInt_confstr, + StatInt_sched_getaffinity, + StatInt_strerror, + StatInt_strerror_r, + StatInt_scandir, + StatInt_scandir64, + StatInt_getgroups, + StatInt_wordexp, + StatInt_sigwait, + StatInt_sigwaitinfo, + StatInt_sigtimedwait, + StatInt_sigemptyset, + StatInt_sigfillset, + StatInt_sigpending, + StatInt_sigprocmask, + StatInt_backtrace, + StatInt_backtrace_symbols, + StatInt_dlopen, + StatInt_dlclose, + StatInt_getmntent, + StatInt_getmntent_r, + StatInt_statfs, + StatInt_statfs64, + StatInt_fstatfs, + StatInt_fstatfs64, + StatInt_statvfs, + StatInt_statvfs64, + StatInt_fstatvfs, + StatInt_fstatvfs64, + StatInt_initgroups, + StatInt_ether_ntoa, + StatInt_ether_aton, + StatInt_ether_ntoa_r, + StatInt_ether_aton_r, + StatInt_ether_ntohost, + StatInt_ether_hostton, + StatInt_ether_line, + StatInt_shmctl, + StatInt_random_r, + StatInt_tmpnam, + StatInt_tmpnam_r, + StatInt_tempnam, + StatInt_sincos, + StatInt_sincosf, + StatInt_sincosl, + StatInt_remquo, + StatInt_remquof, + StatInt_remquol, + StatInt_lgamma, + StatInt_lgammaf, + StatInt_lgammal, + StatInt_lgamma_r, + StatInt_lgammaf_r, + StatInt_lgammal_r, + StatInt_drand48_r, + StatInt_lrand48_r, + StatInt_getline, + StatInt_getdelim, + + StatInt_pthread_attr_getdetachstate, + StatInt_pthread_attr_getguardsize, + StatInt_pthread_attr_getschedparam, + StatInt_pthread_attr_getschedpolicy, + StatInt_pthread_attr_getinheritsched, + StatInt_pthread_attr_getscope, + StatInt_pthread_attr_getstacksize, + StatInt_pthread_attr_getstack, + StatInt_pthread_attr_getaffinity_np, // Dynamic annotations. StatAnnotation, @@ -349,6 +452,8 @@ enum StatType { StatAnnotateIgnoreReadsEnd, StatAnnotateIgnoreWritesBegin, StatAnnotateIgnoreWritesEnd, + StatAnnotateIgnoreSyncBegin, + StatAnnotateIgnoreSyncEnd, StatAnnotatePublishMemoryRange, StatAnnotateUnpublishMemoryRange, StatAnnotateThreadName, diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc index 6c49355bed88..4b0ac04c48a6 100644 --- a/lib/tsan/rtl/tsan_suppressions.cc +++ b/lib/tsan/rtl/tsan_suppressions.cc @@ -13,12 +13,25 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_suppressions.h" #include "tsan_suppressions.h" #include "tsan_rtl.h" #include "tsan_flags.h" #include "tsan_mman.h" #include "tsan_platform.h" +// Suppressions for true/false positives in standard libraries. +static const char *const std_suppressions = +// Libstdc++ 4.4 has data races in std::string. +// See http://crbug.com/181502 for an example. +"race:^_M_rep$\n" +"race:^_M_is_leaked$\n" +// False positive when using std <thread>. +// Happens because we miss atomic synchronization in libstdc++. +// See http://llvm.org/bugs/show_bug.cgi?id=17066 for details. +"race:std::_Sp_counted_ptr_inplace<std::thread::_Impl\n"; + // Can be overriden in frontend. #ifndef TSAN_GO extern "C" const char *WEAK __tsan_default_suppressions() { @@ -28,7 +41,7 @@ extern "C" const char *WEAK __tsan_default_suppressions() { namespace __tsan { -static Suppression *g_suppressions; +static SuppressionContext* g_ctx; static char *ReadFile(const char *filename) { if (filename == 0 || filename[0] == 0) @@ -62,143 +75,96 @@ static char *ReadFile(const char *filename) { return buf; } -bool SuppressionMatch(char *templ, const char *str) { - if (str == 0 || str[0] == 0) - return false; - char *tpos; - const char *spos; - while (templ && templ[0]) { - if (templ[0] == '*') { - templ++; - continue; - } - if (str[0] == 0) - return false; - tpos = (char*)internal_strchr(templ, '*'); - if (tpos != 0) - tpos[0] = 0; - spos = internal_strstr(str, templ); - str = spos + internal_strlen(templ); - templ = tpos; - if (tpos) - tpos[0] = '*'; - if (spos == 0) - return false; - } - return true; -} - -Suppression *SuppressionParse(Suppression *head, const char* supp) { - const char *line = supp; - while (line) { - while (line[0] == ' ' || line[0] == '\t') - line++; - const char *end = internal_strchr(line, '\n'); - if (end == 0) - end = line + internal_strlen(line); - if (line != end && line[0] != '#') { - const char *end2 = end; - while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t')) - end2--; - SuppressionType stype; - if (0 == internal_strncmp(line, "race:", sizeof("race:") - 1)) { - stype = SuppressionRace; - line += sizeof("race:") - 1; - } else if (0 == internal_strncmp(line, "thread:", - sizeof("thread:") - 1)) { - stype = SuppressionThread; - line += sizeof("thread:") - 1; - } else if (0 == internal_strncmp(line, "mutex:", - sizeof("mutex:") - 1)) { - stype = SuppressionMutex; - line += sizeof("mutex:") - 1; - } else if (0 == internal_strncmp(line, "signal:", - sizeof("signal:") - 1)) { - stype = SuppressionSignal; - line += sizeof("signal:") - 1; - } else { - Printf("ThreadSanitizer: failed to parse suppressions file\n"); - Die(); - } - Suppression *s = (Suppression*)internal_alloc(MBlockSuppression, - sizeof(Suppression)); - s->next = head; - head = s; - s->type = stype; - s->templ = (char*)internal_alloc(MBlockSuppression, end2 - line + 1); - internal_memcpy(s->templ, line, end2 - line); - s->templ[end2 - line] = 0; - s->hit_count = 0; - } - if (end[0] == 0) - break; - line = end + 1; - } - return head; -} - void InitializeSuppressions() { + ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)]; + g_ctx = new(placeholder_) SuppressionContext; const char *supp = ReadFile(flags()->suppressions); - g_suppressions = SuppressionParse(0, supp); + g_ctx->Parse(supp); #ifndef TSAN_GO supp = __tsan_default_suppressions(); - g_suppressions = SuppressionParse(g_suppressions, supp); + g_ctx->Parse(supp); + g_ctx->Parse(std_suppressions); #endif } -uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { - if (g_suppressions == 0 || stack == 0) - return 0; - SuppressionType stype; +SuppressionContext *GetSuppressionContext() { + CHECK_NE(g_ctx, 0); + return g_ctx; +} + +SuppressionType conv(ReportType typ) { if (typ == ReportTypeRace) - stype = SuppressionRace; + return SuppressionRace; + else if (typ == ReportTypeVptrRace) + return SuppressionRace; + else if (typ == ReportTypeUseAfterFree) + return SuppressionRace; else if (typ == ReportTypeThreadLeak) - stype = SuppressionThread; + return SuppressionThread; else if (typ == ReportTypeMutexDestroyLocked) - stype = SuppressionMutex; + return SuppressionMutex; else if (typ == ReportTypeSignalUnsafe) - stype = SuppressionSignal; - else + return SuppressionSignal; + else if (typ == ReportTypeErrnoInSignal) + return SuppressionNone; + Printf("ThreadSanitizer: unknown report type %d\n", typ), + Die(); +} + +uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { + CHECK(g_ctx); + if (!g_ctx->SuppressionCount() || stack == 0) return 0; + SuppressionType stype = conv(typ); + if (stype == SuppressionNone) return 0; + Suppression *s; for (const ReportStack *frame = stack; frame; frame = frame->next) { - for (Suppression *supp = g_suppressions; supp; supp = supp->next) { - if (stype == supp->type && - (SuppressionMatch(supp->templ, frame->func) || - SuppressionMatch(supp->templ, frame->file) || - SuppressionMatch(supp->templ, frame->module))) { - DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ); - supp->hit_count++; - *sp = supp; - return frame->pc; - } + if (g_ctx->Match(frame->func, stype, &s) || + g_ctx->Match(frame->file, stype, &s) || + g_ctx->Match(frame->module, stype, &s)) { + DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ); + s->hit_count++; + *sp = s; + return frame->pc; } } return 0; } -static const char *SuppTypeStr(SuppressionType t) { - switch (t) { - case SuppressionRace: return "race"; - case SuppressionMutex: return "mutex"; - case SuppressionThread: return "thread"; - case SuppressionSignal: return "signal"; +uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) { + CHECK(g_ctx); + if (!g_ctx->SuppressionCount() || loc == 0 || + loc->type != ReportLocationGlobal) + return 0; + SuppressionType stype = conv(typ); + if (stype == SuppressionNone) + return 0; + Suppression *s; + if (g_ctx->Match(loc->name, stype, &s) || + g_ctx->Match(loc->file, stype, &s) || + g_ctx->Match(loc->module, stype, &s)) { + DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ); + s->hit_count++; + *sp = s; + return loc->addr; } - CHECK(0); - return "unknown"; + return 0; } void PrintMatchedSuppressions() { - int hit_count = 0; - for (Suppression *supp = g_suppressions; supp; supp = supp->next) - hit_count += supp->hit_count; - if (hit_count == 0) + CHECK(g_ctx); + InternalMmapVector<Suppression *> matched(1); + g_ctx->GetMatched(&matched); + if (!matched.size()) return; - Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", - hit_count, (int)internal_getpid()); - for (Suppression *supp = g_suppressions; supp; supp = supp->next) { - if (supp->hit_count == 0) - continue; - Printf("%d %s:%s\n", supp->hit_count, SuppTypeStr(supp->type), supp->templ); + int hit_count = 0; + for (uptr i = 0; i < matched.size(); i++) + hit_count += matched[i]->hit_count; + Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count, + (int)internal_getpid()); + for (uptr i = 0; i < matched.size(); i++) { + Printf("%d %s:%s\n", matched[i]->hit_count, + SuppressionTypeString(matched[i]->type), matched[i]->templ); } } } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_suppressions.h b/lib/tsan/rtl/tsan_suppressions.h index 1c98363383dc..fe7db588f50f 100644 --- a/lib/tsan/rtl/tsan_suppressions.h +++ b/lib/tsan/rtl/tsan_suppressions.h @@ -13,31 +13,16 @@ #ifndef TSAN_SUPPRESSIONS_H #define TSAN_SUPPRESSIONS_H +#include "sanitizer_common/sanitizer_suppressions.h" #include "tsan_report.h" namespace __tsan { -// Exposed for testing. -enum SuppressionType { - SuppressionRace, - SuppressionMutex, - SuppressionThread, - SuppressionSignal -}; - -struct Suppression { - Suppression *next; - SuppressionType type; - char *templ; - int hit_count; -}; - void InitializeSuppressions(); -void FinalizeSuppressions(); void PrintMatchedSuppressions(); uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp); -Suppression *SuppressionParse(Suppression *head, const char* supp); -bool SuppressionMatch(char *templ, const char *str); +uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp); +SuppressionContext *GetSuppressionContext(); } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_symbolize.cc b/lib/tsan/rtl/tsan_symbolize.cc index 12226064f5a4..75acf1d8438b 100644 --- a/lib/tsan/rtl/tsan_symbolize.cc +++ b/lib/tsan/rtl/tsan_symbolize.cc @@ -22,19 +22,17 @@ namespace __tsan { -struct ScopedInSymbolizer { - ScopedInSymbolizer() { - ThreadState *thr = cur_thread(); - CHECK(!thr->in_symbolizer); - thr->in_symbolizer = true; - } +void EnterSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(!thr->in_symbolizer); + thr->in_symbolizer = true; +} - ~ScopedInSymbolizer() { - ThreadState *thr = cur_thread(); - CHECK(thr->in_symbolizer); - thr->in_symbolizer = false; - } -}; +void ExitSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(thr->in_symbolizer); + thr->in_symbolizer = false; +} ReportStack *NewReportStackEntry(uptr addr) { ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack, @@ -44,18 +42,6 @@ ReportStack *NewReportStackEntry(uptr addr) { return ent; } -// Strip module path to make output shorter. -static char *StripModuleName(const char *module) { - if (module == 0) - return 0; - const char *short_module_name = internal_strrchr(module, '/'); - if (short_module_name) - short_module_name += 1; - else - short_module_name = module; - return internal_strdup(short_module_name); -} - static ReportStack *NewReportStackEntry(const AddressInfo &info) { ReportStack *ent = NewReportStackEntry(info.address); ent->module = StripModuleName(info.module); @@ -69,16 +55,64 @@ static ReportStack *NewReportStackEntry(const AddressInfo &info) { return ent; } + + ReportStack *next; + char *module; + uptr offset; + uptr pc; + char *func; + char *file; + int line; + int col; + + +// Denotes fake PC values that come from JIT/JAVA/etc. +// For such PC values __tsan_symbolize_external() will be called. +const uptr kExternalPCBit = 1ULL << 60; + +// May be overriden by JIT/JAVA/etc, +// whatever produces PCs marked with kExternalPCBit. +extern "C" bool __tsan_symbolize_external(uptr pc, + char *func_buf, uptr func_siz, + char *file_buf, uptr file_siz, + int *line, int *col) + SANITIZER_WEAK_ATTRIBUTE; + +bool __tsan_symbolize_external(uptr pc, + char *func_buf, uptr func_siz, + char *file_buf, uptr file_siz, + int *line, int *col) { + return false; +} + ReportStack *SymbolizeCode(uptr addr) { - if (!IsSymbolizerAvailable()) + // Check if PC comes from non-native land. + if (addr & kExternalPCBit) { + // Declare static to not consume too much stack space. + // We symbolize reports in a single thread, so this is fine. + static char func_buf[1024]; + static char file_buf[1024]; + int line, col; + if (!__tsan_symbolize_external(addr, func_buf, sizeof(func_buf), + file_buf, sizeof(file_buf), &line, &col)) + return NewReportStackEntry(addr); + ReportStack *ent = NewReportStackEntry(addr); + ent->module = 0; + ent->offset = 0; + ent->func = internal_strdup(func_buf); + ent->file = internal_strdup(file_buf); + ent->line = line; + ent->col = col; + return ent; + } + if (!Symbolizer::Get()->IsAvailable()) return SymbolizeCodeAddr2Line(addr); - ScopedInSymbolizer in_symbolizer; static const uptr kMaxAddrFrames = 16; InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames); for (uptr i = 0; i < kMaxAddrFrames; i++) new(&addr_frames[i]) AddressInfo(); - uptr addr_frames_num = __sanitizer::SymbolizeCode(addr, addr_frames.data(), - kMaxAddrFrames); + uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode( + addr, addr_frames.data(), kMaxAddrFrames); if (addr_frames_num == 0) return NewReportStackEntry(addr); ReportStack *top = 0; @@ -97,11 +131,10 @@ ReportStack *SymbolizeCode(uptr addr) { } ReportLocation *SymbolizeData(uptr addr) { - if (!IsSymbolizerAvailable()) + if (!Symbolizer::Get()->IsAvailable()) return 0; - ScopedInSymbolizer in_symbolizer; DataInfo info; - if (!__sanitizer::SymbolizeData(addr, &info)) + if (!Symbolizer::Get()->SymbolizeData(addr, &info)) return 0; ReportLocation *ent = (ReportLocation*)internal_alloc(MBlockReportStack, sizeof(ReportLocation)); @@ -117,10 +150,9 @@ ReportLocation *SymbolizeData(uptr addr) { } void SymbolizeFlush() { - if (!IsSymbolizerAvailable()) + if (!Symbolizer::Get()->IsAvailable()) return; - ScopedInSymbolizer in_symbolizer; - __sanitizer::FlushSymbolizer(); + Symbolizer::Get()->Flush(); } } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_symbolize.h b/lib/tsan/rtl/tsan_symbolize.h index 7bc6123df57d..6282e45b40d5 100644 --- a/lib/tsan/rtl/tsan_symbolize.h +++ b/lib/tsan/rtl/tsan_symbolize.h @@ -18,6 +18,8 @@ namespace __tsan { +void EnterSymbolizer(); +void ExitSymbolizer(); ReportStack *SymbolizeCode(uptr addr); ReportLocation *SymbolizeData(uptr addr); void SymbolizeFlush(); diff --git a/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc b/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc index 47f9e1fbf418..b186d3b38ec9 100644 --- a/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc +++ b/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc @@ -60,7 +60,6 @@ static void NOINLINE InitModule(ModuleDesc *m) { } int pid = fork(); if (pid == 0) { - __sanitizer_set_report_fd(STDERR_FILENO); internal_close(STDOUT_FILENO); internal_close(STDIN_FILENO); internal_dup2(outfd[0], STDIN_FILENO); diff --git a/lib/tsan/rtl/tsan_sync.cc b/lib/tsan/rtl/tsan_sync.cc index c6ddcdb37426..f8f3c40fab04 100644 --- a/lib/tsan/rtl/tsan_sync.cc +++ b/lib/tsan/rtl/tsan_sync.cc @@ -265,6 +265,11 @@ void StackTrace::ObtainCurrent(ThreadState *thr, uptr toppc) { n_ = c_ - !!toppc; } } else { + // Cap potentially huge stacks. + if (n_ + !!toppc > kTraceStackSize) { + start = n_ - kTraceStackSize + !!toppc; + n_ = kTraceStackSize - !!toppc; + } s_ = (uptr*)internal_alloc(MBlockStackTrace, (n_ + !!toppc) * sizeof(s_[0])); } diff --git a/lib/tsan/rtl/tsan_trace.h b/lib/tsan/rtl/tsan_trace.h index 7df716046567..5ed0356e2bf5 100644 --- a/lib/tsan/rtl/tsan_trace.h +++ b/lib/tsan/rtl/tsan_trace.h @@ -62,6 +62,11 @@ struct TraceHeader { struct Trace { TraceHeader headers[kTraceParts]; Mutex mtx; +#ifndef TSAN_GO + // Must be last to catch overflow as paging fault. + // Go shadow stack is dynamically allocated. + uptr shadow_stack[kShadowStackSize]; +#endif Trace() : mtx(MutexTypeTrace, StatMtxTrace) { diff --git a/lib/tsan/rtl/tsan_update_shadow_word_inl.h b/lib/tsan/rtl/tsan_update_shadow_word_inl.h index e7c036c5dea8..a11c9bc52509 100644 --- a/lib/tsan/rtl/tsan_update_shadow_word_inl.h +++ b/lib/tsan/rtl/tsan_update_shadow_word_inl.h @@ -57,8 +57,7 @@ do { goto RACE; } // Do the memory access intersect? - // In Go all memory accesses are 1 byte, so there can be no intersections. - if (kCppMode && Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { + if (Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { StatInc(thr, StatShadowIntersect); if (Shadow::TidsAreEqual(old, cur)) { StatInc(thr, StatShadowSameThread); diff --git a/lib/tsan/tests/CMakeLists.txt b/lib/tsan/tests/CMakeLists.txt index 7cc079f3d27a..f73a89242859 100644 --- a/lib/tsan/tests/CMakeLists.txt +++ b/lib/tsan/tests/CMakeLists.txt @@ -3,22 +3,44 @@ include_directories(../rtl) add_custom_target(TsanUnitTests) set_target_properties(TsanUnitTests PROPERTIES FOLDER "TSan unittests") -function(add_tsan_unittest testname) - # Build unit tests only on 64-bit Linux. - if(UNIX AND NOT APPLE - AND CAN_TARGET_x86_64 - AND CMAKE_SIZEOF_VOID_P EQUAL 8 - AND NOT LLVM_BUILD_32_BITS) - add_unittest(TsanUnitTests ${testname} ${ARGN}) - # Link with TSan runtime. - target_link_libraries(${testname} clang_rt.tsan-x86_64) - # Compile tests with the same flags as TSan runtime. - set_target_compile_flags(${testname} ${TSAN_CFLAGS}) - # Link tests with -pie. - set_property(TARGET ${testname} APPEND_STRING - PROPERTY LINK_FLAGS " -pie") + +set(TSAN_UNITTEST_CFLAGS + ${TSAN_CFLAGS} + ${COMPILER_RT_GTEST_INCLUDE_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/lib + -I${COMPILER_RT_SOURCE_DIR}/lib/tsan/rtl + -DGTEST_HAS_RTTI=0) + +# tsan_compile(obj_list, source, arch, {headers}) +macro(tsan_compile obj_list source arch) + get_filename_component(basename ${source} NAME) + set(output_obj "${basename}.${arch}.o") + get_target_flags_for_arch(${arch} TARGET_CFLAGS) + clang_compile(${output_obj} ${source} + CFLAGS ${TSAN_UNITTEST_CFLAGS} ${TARGET_CFLAGS} + DEPS gtest ${TSAN_RUNTIME_LIBRARIES} ${ARGN}) + list(APPEND ${obj_list} ${output_obj}) +endmacro() + +macro(add_tsan_unittest testname) + # Build unit tests only for 64-bit Linux. + if(UNIX AND NOT APPLE AND CAN_TARGET_x86_64) + parse_arguments(TEST "SOURCES;HEADERS" "" ${ARGN}) + set(TEST_OBJECTS) + foreach(SOURCE ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}) + tsan_compile(TEST_OBJECTS ${SOURCE} x86_64 ${TEST_HEADERS}) + endforeach() + get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + add_compiler_rt_test(TsanUnitTests ${testname} + OBJECTS ${TEST_OBJECTS} + DEPS ${TSAN_RUNTIME_LIBRARIES} ${TEST_OBJECTS} + LINK_FLAGS ${TARGET_LINK_FLAGS} + -fsanitize=thread + -lstdc++ -lm) endif() -endfunction() +endmacro() -add_subdirectory(rtl) -add_subdirectory(unit) +if(COMPILER_RT_CAN_EXECUTE_TESTS) + add_subdirectory(rtl) + add_subdirectory(unit) +endif() diff --git a/lib/tsan/tests/rtl/CMakeLists.txt b/lib/tsan/tests/rtl/CMakeLists.txt index b585660e8b4a..989566d9e041 100644 --- a/lib/tsan/tests/rtl/CMakeLists.txt +++ b/lib/tsan/tests/rtl/CMakeLists.txt @@ -1,15 +1,19 @@ -set(TSAN_RTL_TESTS +set(TSAN_RTL_TEST_SOURCES tsan_bench.cc tsan_mop.cc tsan_mutex.cc tsan_posix.cc tsan_string.cc tsan_test.cc - tsan_thread.cc - ) + tsan_thread.cc) if(UNIX AND NOT APPLE) - list(APPEND TSAN_RTL_TESTS tsan_test_util_linux.cc) + list(APPEND TSAN_RTL_TEST_SOURCES tsan_test_util_linux.cc) endif() -add_tsan_unittest(TsanRtlTest ${TSAN_RTL_TESTS}) +set(TSAN_RTL_TEST_HEADERS + tsan_test_util.h) + +add_tsan_unittest(TsanRtlTest + SOURCES ${TSAN_RTL_TEST_SOURCES} + HEADERS ${TSAN_RTL_TEST_HEADERS}) diff --git a/lib/tsan/tests/unit/CMakeLists.txt b/lib/tsan/tests/unit/CMakeLists.txt index b25a56d8d55c..6898f641d6a0 100644 --- a/lib/tsan/tests/unit/CMakeLists.txt +++ b/lib/tsan/tests/unit/CMakeLists.txt @@ -1,13 +1,13 @@ -set(TSAN_UNIT_TESTS +set(TSAN_UNIT_TEST_SOURCES tsan_clock_test.cc tsan_flags_test.cc tsan_mman_test.cc tsan_mutex_test.cc tsan_shadow_test.cc tsan_stack_test.cc - tsan_suppressions_test.cc tsan_sync_test.cc - tsan_vector_test.cc - ) + tsan_unit_test_main.cc + tsan_vector_test.cc) -add_tsan_unittest(TsanUnitTest ${TSAN_UNIT_TESTS}) +add_tsan_unittest(TsanUnitTest + SOURCES ${TSAN_UNIT_TEST_SOURCES}) diff --git a/lib/tsan/tests/unit/tsan_mman_test.cc b/lib/tsan/tests/unit/tsan_mman_test.cc index 0961d2b75d11..e1ad7ac51ad6 100644 --- a/lib/tsan/tests/unit/tsan_mman_test.cc +++ b/lib/tsan/tests/unit/tsan_mman_test.cc @@ -164,7 +164,9 @@ TEST(Mman, CallocOverflow) { size_t kArraySize = 4096; volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; - volatile void *p = calloc(kArraySize, kArraySize2); // Should return 0. + volatile void *p = NULL; + EXPECT_DEATH(p = calloc(kArraySize, kArraySize2), + "allocator is terminating the process instead of returning 0"); EXPECT_EQ(0L, p); } diff --git a/lib/tsan/tests/unit/tsan_stack_test.cc b/lib/tsan/tests/unit/tsan_stack_test.cc index d5392959c48c..9aa2967628cf 100644 --- a/lib/tsan/tests/unit/tsan_stack_test.cc +++ b/lib/tsan/tests/unit/tsan_stack_test.cc @@ -19,6 +19,10 @@ namespace __tsan { static void TestStackTrace(StackTrace *trace) { ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0); + uptr stack[128]; + thr.shadow_stack = &stack[0]; + thr.shadow_stack_pos = &stack[0]; + thr.shadow_stack_end = &stack[128]; trace->ObtainCurrent(&thr, 0); EXPECT_EQ(trace->Size(), (uptr)0); @@ -60,7 +64,12 @@ TEST(StackTrace, StaticTrim) { ScopedInRtl in_rtl; uptr buf[2]; StackTrace trace(buf, 2); + ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0); + uptr stack[128]; + thr.shadow_stack = &stack[0]; + thr.shadow_stack_pos = &stack[0]; + thr.shadow_stack_end = &stack[128]; *thr.shadow_stack_pos++ = 100; *thr.shadow_stack_pos++ = 101; diff --git a/lib/tsan/tests/unit/tsan_suppressions_test.cc b/lib/tsan/tests/unit/tsan_suppressions_test.cc deleted file mode 100644 index decfa3214d23..000000000000 --- a/lib/tsan/tests/unit/tsan_suppressions_test.cc +++ /dev/null @@ -1,128 +0,0 @@ -//===-- tsan_suppressions_test.cc -----------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_suppressions.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" - -#include <string.h> - -namespace __tsan { - -TEST(Suppressions, Parse) { - ScopedInRtl in_rtl; - Suppression *supp0 = SuppressionParse(0, - "race:foo\n" - " race:bar\n" // NOLINT - "race:baz \n" // NOLINT - "# a comment\n" - "race:quz\n" - ); // NOLINT - Suppression *supp = supp0; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "quz")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "baz")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "bar")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "foo")); - supp = supp->next; - EXPECT_EQ((Suppression*)0, supp); -} - -TEST(Suppressions, Parse2) { - ScopedInRtl in_rtl; - Suppression *supp0 = SuppressionParse(0, - " # first line comment\n" // NOLINT - " race:bar \n" // NOLINT - "race:baz* *baz\n" - "# a comment\n" - "# last line comment\n" - ); // NOLINT - Suppression *supp = supp0; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "baz* *baz")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "bar")); - supp = supp->next; - EXPECT_EQ((Suppression*)0, supp); -} - -TEST(Suppressions, Parse3) { - ScopedInRtl in_rtl; - Suppression *supp0 = SuppressionParse(0, - "# last suppression w/o line-feed\n" - "race:foo\n" - "race:bar" - ); // NOLINT - Suppression *supp = supp0; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "bar")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "foo")); - supp = supp->next; - EXPECT_EQ((Suppression*)0, supp); -} - -TEST(Suppressions, ParseType) { - ScopedInRtl in_rtl; - Suppression *supp0 = SuppressionParse(0, - "race:foo\n" - "thread:bar\n" - "mutex:baz\n" - "signal:quz\n" - ); // NOLINT - Suppression *supp = supp0; - EXPECT_EQ(supp->type, SuppressionSignal); - EXPECT_EQ(0, strcmp(supp->templ, "quz")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionMutex); - EXPECT_EQ(0, strcmp(supp->templ, "baz")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionThread); - EXPECT_EQ(0, strcmp(supp->templ, "bar")); - supp = supp->next; - EXPECT_EQ(supp->type, SuppressionRace); - EXPECT_EQ(0, strcmp(supp->templ, "foo")); - supp = supp->next; - EXPECT_EQ((Suppression*)0, supp); -} - -static bool MyMatch(const char *templ, const char *func) { - char tmp[1024]; - strcpy(tmp, templ); // NOLINT - return SuppressionMatch(tmp, func); -} - -TEST(Suppressions, Match) { - EXPECT_TRUE(MyMatch("foobar", "foobar")); - EXPECT_TRUE(MyMatch("foobar", "prefix_foobar_postfix")); - EXPECT_TRUE(MyMatch("*foobar*", "prefix_foobar_postfix")); - EXPECT_TRUE(MyMatch("foo*bar", "foo_middle_bar")); - EXPECT_TRUE(MyMatch("foo*bar", "foobar")); - EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_bar_another_baz")); - EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_barbaz")); - - EXPECT_FALSE(MyMatch("foo", "baz")); - EXPECT_FALSE(MyMatch("foobarbaz", "foobar")); - EXPECT_FALSE(MyMatch("foobarbaz", "barbaz")); - EXPECT_FALSE(MyMatch("foo*bar", "foobaz")); - EXPECT_FALSE(MyMatch("foo*bar", "foo_baz")); -} - -} // namespace __tsan diff --git a/lib/tsan/tests/unit/tsan_unit_test_main.cc b/lib/tsan/tests/unit/tsan_unit_test_main.cc new file mode 100644 index 000000000000..84d94dd03748 --- /dev/null +++ b/lib/tsan/tests/unit/tsan_unit_test_main.cc @@ -0,0 +1,19 @@ +//===-- tsan_unit_test_main.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "gtest/gtest.h" + +int main(int argc, char **argv) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} |