如何在让 UBSan 开心的同时抓住 `abi::__forced_unwind`?

How to catch `abi::__forced_unwind` while keeping UBSan happy?

与 libstdc++ 一样,我们在某些地方检查 abi::__forced_unwind,然后重新抛出它,而不是采取其他操作。和libstdc++一样,我们引用抓取:

try {
    /* ... */
} catch (abi::__forced_unwind&) {
    throw;
} catch (...) {
    /* ... */
} 

但是如果我们真的 pthread_cancel 去运行代码,ubsan 会抱怨:

runtime error: reference binding to null pointer of type 'struct __forced_unwind'

在这里,我们用const-ref 还是mutable ref 来捕获并不重要

我们(和 libstdc++)实际上 运行 进入 UB,还是 GCC 的 UBSan 实现中的误报?

这是强制展开实现中的一个错误,已在 GCC Bugzilla 中作为票证归档 #100415。 (可以说它应该在 GCC 本身中修复,因为您没有创建实际的引用绑定,只是匹配异常类型。)

在修复它的同时,您可以使用 [[gnu::no_sanitize("null")]] 属性修饰包含 catch 子句的函数,这将禁止对引用进行空检查,而不会执行其他任何操作:

#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;
}