Like libstdc++, we're checking in some places for abi::__forced_unwind
, and just re-throw it instead of taking some other action. Like libstdc++, we catch it by reference:
try {
/* ... */
} catch (abi::__forced_unwind&) {
throw;
} catch (...) {
/* ... */
}
But if we actually pthread_cancel to exercise the code, ubsan complains:
runtime error: reference binding to null pointer of type 'struct __forced_unwind'
Here, it doesn't matter whether we catch by const-ref or mutable ref.
Are we (and libstdc++) actually running into UB here, or is it a False Positive in GCC's UBSan implementation?
This is a bug in the forced unwinding implementation, which has been filed in the GCC Bugzilla as ticket #100415. (Arguably it should be fixed in GCC itself, as you’re not creating an actual reference binding, only matching on the exception type.)
While it is being fixed, you can decorate the function containing the catch
clause with the [[gnu::no_sanitize("null")]]
attribute, which will suppress null checking for references and nothing else:
#include <pthread.h>
#include <cxxabi.h>
#include <iostream>
int main() {
pthread_t thrd;
pthread_create(
&thrd, nullptr,
[] [[gnu::no_sanitize("null")]] (void *) -> void * {
try {
pthread_exit(nullptr);
} catch (abi::__forced_unwind const &fu) {
std::cerr << "forced unwind with " << &fu << std::endl;
// does not trigger UBSan
int &x = *static_cast<int *>(nullptr);
// triggers UBSan
1 / 0;
throw;
}
}, nullptr);
pthread_join(thrd, nullptr);
// triggers UBSan
int &x = *static_cast<int *>(nullptr);
return 0;
}