如何在让 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;
}
与 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;
}