Fla*_*ire 6 c++ undefined-behavior ubsan
我想使用 UBSAN(未定义行为消毒剂),但发现它完全没有价值,因为它会报告许多误报。
例如,一个简单的std::make_shared<int>(42);就足以触发警告,如
地址 0x00000236de70 内的成员访问不指向类型为“_Sp_counted_base”的对象
将此示例简化为 MWE 表明该问题在基类和继承中更为普遍:
例子:
struct Foo{
int f(){ return g(); }
virtual int g() = 0;
};
struct Bar: Foo{
int g(){ return 42; }
};
int main(){
auto f = new Bar();
return f->g();
}
Run Code Online (Sandbox Code Playgroud)
编译-fsanitize=undefined并观看
example.cpp:15:16: 运行时错误:地址 0x000000726e70 上的成员调用不指向“Bar”类型的对象
0x000000726e70:注意:对象具有无效的 vptr
请参阅https://godbolt.org/z/0UiVtu。
连这些简单的案件都没有妥善处理?我错过了什么吗?我应该如何正确使用 UBSAN 来检查我的代码?(这需要[几乎]没有误报)
编辑:由于 MWE 似乎只适用于 Godbolt,原始代码如下所示:
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/iostreams/stream.hpp>
using MMStream = boost::iostreams::stream<boost::iostreams::mapped_file_source>;
int main(){
MMStream stream;
stream.open("a.out");
return !stream;
}
Run Code Online (Sandbox Code Playgroud)
编译clang++-8 -fsanitize=undefined -fvisibility=hidden -I /opt/boost_1_64_0/include/ test.cpp /opt/boost_1_64_0/lib/libboost_iostreams.so并运行这会导致类似的错误
运行时错误:地址 0x00000126ef30 上的成员调用未指向类型为“boost::detail::sp_counted_base”的对象
在评论后尝试自己回答这个问题并创建另一个 MWE。
TLDR:确保在编译时导出所有包含虚函数的类 -fvisibility=hidden
考虑一个共享库 Foo
foo.h
#define EXPORT __attribute__((visibility("default")))
struct Foo{
virtual int g() = 0;
};
struct Bar: Foo{
int g(){ return 42; }
};
EXPORT Foo* create();
Run Code Online (Sandbox Code Playgroud)
foo.cpp #include "foo.h"
Foo* create(){
return new Bar();
}
Run Code Online (Sandbox Code Playgroud)
编译为 clang++-8 foo.cpp -shared -fPIC -o foo.so
和一个使用虚拟函数链接的可执行文件,但具有-fvisibility:
主.cpp:
#include "foo.h"
int main(){
Foo* f = create();
return f->g() != 42;
}
Run Code Online (Sandbox Code Playgroud)
编译为 clang++-8 -fsanitize=undefined -fvisibility=hidden main.cpp foo.so
这将报告
运行时错误:地址 0x00000290cea0 上的成员调用不指向“Foo”类型的对象
这与https://bugs.llvm.org/show_bug.cgi?id=39191 中描述的错误相同(感谢@Nikita Petrenko)
总结:对于fvisibility=hidden未导出的符号(函数、未用属性修饰的类__attribute__((visibility("default")))在不同的 DSO(例如可执行文件和共享库)中使用时不被认为是相同的。因此共享库中的基类Foo和可执行文件是不同的(它们有不同的vtables) UBSAN 检测到: 可执行文件“期望”一个对象 witg vtable,Exe::Foo但是却得到Library::Foo
在 boost 的情况下,类sp_counted_base是罪魁祸首,因为它直到 Boost 1.69 才被导出,它增加了BOOST_SYMBOL_EXPORT,所以切换到 Boost 1.69+ 解决了这个问题。
| 归档时间: |
|
| 查看次数: |
1975 次 |
| 最近记录: |