-
Notifications
You must be signed in to change notification settings - Fork 15.5k
Description
When libunwind is built with -fsanitize=thread there is a data race reported on these static variables in logAPIs:
llvm-project/libunwind/src/libunwind.cpp
Lines 531 to 542 in b9eb974
| // Add logging hooks in Debug builds only | |
| #ifndef NDEBUG | |
| #include <stdlib.h> | |
| _LIBUNWIND_HIDDEN | |
| bool logAPIs() { | |
| // do manual lock to avoid use of _cxa_guard_acquire or initializers | |
| static bool checked = false; | |
| static bool log = false; | |
| if (!checked) { | |
| log = (getenv("LIBUNWIND_PRINT_APIS") != NULL); | |
| checked = true; |
Additionally, the comment says "Add logging hooks in Debug builds only" however libunwind always builds without NDEBUG defined. This is because LIBUNWIND_ENABLE_ASSERTIONS defaults to ON.
llvm-project/libunwind/CMakeLists.txt
Line 41 in b9eb974
| option(LIBUNWIND_ENABLE_ASSERTIONS "Enable assertions independent of build mode." ON) |
And the cmake build uses LIBUNWIND_ENABLE_ASSERTIONS to turn off NDEBUG:
llvm-project/libunwind/CMakeLists.txt
Lines 268 to 278 in b9eb974
| if (LIBUNWIND_ENABLE_ASSERTIONS) | |
| # MSVC doesn't like _DEBUG on release builds. See PR 4379. | |
| if (NOT MSVC) | |
| add_compile_flags(-D_DEBUG) | |
| endif() | |
| # On Release builds cmake automatically defines NDEBUG, so we | |
| # explicitly undefine it: | |
| if (NOT uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG") | |
| add_compile_flags(-UNDEBUG) | |
| endif() |
Here's the report from tsan:
WARNING: ThreadSanitizer: data race (pid=8520)
Read of size 1 at 0x55e25a3ef811 by thread T2:
#0 logAPIs /install/llvm-project-21.1.5.src/libunwind/src/libunwind.cpp:443:8 (a.out+0x18937a)
#1 _Unwind_RaiseException /install/llvm-project-21.1.5.src/libunwind/src/UnwindLevel1.c:450:3 (a.out+0x187d94)
#2 __cxa_throw /install/llvm-project-21.1.5.src/libcxxabi/src/cxa_exception.cpp:295:5 (a.out+0x185879)
#3 ThrowException(void*) test.cpp (a.out+0x143ab5)
Previous write of size 1 at 0x55e25a3ef811 by thread T1:
#0 logAPIs /install/llvm-project-21.1.5.src/libunwind/src/libunwind.cpp:445:13 (a.out+0x1893cc)
#1 _Unwind_RaiseException /install/llvm-project-21.1.5.src/libunwind/src/UnwindLevel1.c:450:3 (a.out+0x187d94)
#2 __cxa_throw /install/llvm-project-21.1.5.src/libcxxabi/src/cxa_exception.cpp:295:5 (a.out+0x185879)
#3 ThrowException(void*) test.cpp (a.out+0x143ab5)
Location is global 'logAPIs::checked' of size 1 at 0x55e25a3ef811 (a.out+0x150a811)
Thread T2 (tid=8523, running) created by main thread at:
#0 pthread_create /install/llvm-project-21.1.5.src/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp:1090:3 (a.out+0x9341a)
#1 main <null> (a.out+0x14394b)
Thread T1 (tid=8522, running) created by main thread at:
#0 pthread_create /install/llvm-project-21.1.5.src/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp:1090:3 (a.out+0x9341a)
#1 main <null> (a.out+0x14394b)
SUMMARY: ThreadSanitizer: data race /install/llvm-project-21.1.5.src/libunwind/src/libunwind.cpp:443:8 in logAPIs
And here's a test program that can reproduce it (though tsan does not always flag it). I had to also set the env var LIBUNWIND_PRINT_UNWINDING=1 to get this test program to reliably reproduce (though I do not have to set that environment variable in the actual build where I first saw this problem):
#include <pthread.h>
#include <stdexcept>
#include <vector>
static void *ThrowException(void *) {
try {
throw std::runtime_error("nope");
} catch (...) {
}
return nullptr;
}
int main() {
std::vector<pthread_t> threads;
for (int i = 0; i < 2; i++) {
pthread_t thread;
pthread_create(&thread, nullptr, ThrowException, nullptr);
threads.push_back(thread);
}
for (auto thread : threads) {
pthread_join(thread, nullptr);
}
return 0;
}