diff options
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(); +}  | 
