libunwind: data race in logAPIs when exception is thrown
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
When libunwind is built with `-fsanitize=thread` there is a data race reported on these static variables in `logAPIs`:
https://github.com/llvm/llvm-project/blob/b9eb974e7e0a91605e70d60ad3256f96aa55bc8b/libunwind/src/libunwind.cpp#L531-L542
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`.
https://github.com/llvm/llvm-project/blob/b9eb974e7e0a91605e70d60ad3256f96aa55bc8b/libunwind/CMakeLists.txt#L41
And the cmake build uses `LIBUNWIND_ENABLE_ASSERTIONS` to turn off `NDEBUG`:
https://github.com/llvm/llvm-project/blob/b9eb974e7e0a91605e70d60ad3256f96aa55bc8b/libunwind/CMakeLists.txt#L268-L278
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 (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 (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):
```C++
#include
#include
#include
static void *ThrowException(void *) {
try {
throw std::runtime_error("nope");
} catch (...) {
}
return nullptr;
}
int main() {
std::vector 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;
}
```
Contributor guide
Research direction
Start in libunwind/src/libunwind.cpp at logAPIs and review the related assertion settings in libunwind/CMakeLists.txt. Build libunwind with ThreadSanitizer and run the provided two-thread exception test, setting LIBUNWIND_PRINT_UNWINDING=1 if needed. Done means the race is no longer reported while the existing logging behavior remains intact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cmake, cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100