如何在让瑞银开心的同时抓住“abi::__forced_unwind”?

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

提问人:Marc Mutz - mmutz 提问时间:12/16/2021 最后编辑:user3840170Marc Mutz - mmutz 更新时间:12/27/2021 访问量:542

问:

像 libstdc++ 一样,我们正在检查某些地方的 ,然后重新抛出它,而不是采取其他一些操作。像 libstdc++ 一样,我们通过引用来捕获它:abi::__forced_unwind

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

但是,如果我们真的pthread_cancel执行代码,ubsan 抱怨道:

运行时错误:引用绑定到类型为“struct __forced_unwind”的 null 指针

在这里,我们是通过 const-ref 还是可变 ref 捕获并不重要。

我们(和 libstdc++)是否真的在这里遇到了 UB,或者它是 GCC 的 UBSan 实现中的误报?

C G++ Pthreads 乌布桑

评论

0赞 Jarod42 12/16/2021
请注意,有权使用保留标识符,这与用户代码相反...libstdc++
1赞 Marc Mutz - mmutz 12/21/2021
@Jarod42这种技术有时是必要的,因为吞咽 a 会终止应用程序。你必须重新扔掉它。否则我不知道有什么办法解决这个问题。也就是说,我有 95% 的把握会从使用该类型的 libstc++ 标头中获得相同的 ubsan 错误。例如,在 中,有几次 libstdc++ 会吞下所有异常,但 .__forced_unwind<iomanip>__forced_unwind
1赞 Jonathan Wakely 12/28/2021
@Jarod42,该名称保留给要使用的实现。在这种情况下,它使用该名称来定义非标准扩展,该扩展也可供用户使用。并非所有保留名称都不可用。使用保留名称声明自己的事物与使用实现提供的扩展之间存在很大差异。
0赞 Jeff Garrett 12/29/2021
迂腐的是,libstdc++ 不能遇到未定义的行为。它是实现的一部分。

答:

4赞 user3840170 12/21/2021 #1

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

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

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

评论

0赞 Jeff Garrett 12/29/2021
由于该属性仅适用于该特定函数,因此如果您调用分配 null 引用的函数,UBSan 仍然可以捕获该函数。换句话说,您可以非常狭隘地定制它。