diff --git a/CMakeLists.txt b/CMakeLists.txt index f58a7bb0..104e50cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,6 +268,16 @@ if(CFI) set(OPENSSL_NO_ASM "1") endif() +if(TSAN) + if(NOT CLANG) + message(FATAL_ERROR "Cannot enable TSAN unless using Clang") + endif() + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=thread") +endif() + if (GCOV) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") diff --git a/crypto/refcount_test.cc b/crypto/refcount_test.cc index efa501a7..6ce0746d 100644 --- a/crypto/refcount_test.cc +++ b/crypto/refcount_test.cc @@ -16,6 +16,10 @@ #include +#if !defined(OPENSSL_NO_THREADS) +#include +#endif + TEST(RefCountTest, Basic) { CRYPTO_refcount_t count = 0; @@ -38,3 +42,38 @@ TEST(RefCountTest, Basic) { EXPECT_FALSE(CRYPTO_refcount_dec_and_test_zero(&count)); EXPECT_EQ(1u, count); } + +#if !defined(OPENSSL_NO_THREADS) +// This test is primarily intended to run under ThreadSanitizer. +TEST(RefCountTest, Threads) { + CRYPTO_refcount_t count = 0; + + // Race two increments. + { + std::thread thread([&] { CRYPTO_refcount_inc(&count); }); + CRYPTO_refcount_inc(&count); + thread.join(); + EXPECT_EQ(2u, count); + } + + // Race an increment with a decrement. + { + std::thread thread([&] { CRYPTO_refcount_inc(&count); }); + EXPECT_FALSE(CRYPTO_refcount_dec_and_test_zero(&count)); + thread.join(); + EXPECT_EQ(2u, count); + } + + // Race two decrements. + { + bool thread_saw_zero; + std::thread thread( + [&] { thread_saw_zero = CRYPTO_refcount_dec_and_test_zero(&count); }); + bool saw_zero = CRYPTO_refcount_dec_and_test_zero(&count); + thread.join(); + EXPECT_EQ(0u, count); + // Exactly one thread should see zero. + EXPECT_NE(saw_zero, thread_saw_zero); + } +} +#endif