llvm / llvm/llvm-project

libunwind: data race in logAPIs when exception is thrown

Open
#167,732 0 comments 0 reactions 0 assignees View on GitHub
libunwind
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.