我想使用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();
}用-fsanitize=undefined和watch编译
example.cpp:15:16:运行时错误:对地址0x000000726e70的成员调用,它没有指向'Bar‘类型的对象 0x000000726e70:注意:对象具有无效的vptr
见https://godbolt.org/z/0UiVtu。
这些简单的个案,如何处理得不好呢?我错过什么了吗?如何正确地使用UBSAN检查我的代码?(这几乎不需要假阳性)
编辑:由于MWE似乎只适用于戈德螺栓,原始代码如下所示:
#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;
}使用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”类型的对象
发布于 2019-08-01 07:44:38
试着在评论后自己回答,并创建另一个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();foo.cpp #包括"foo.h“
Foo* create(){
return new Bar();
}用clang++-8 foo.cpp -shared -fPIC -o foo.so编译
以及使用虚拟函数(但使用-fvisibility )链接的可执行文件
main.cpp:
#include "foo.h"
int main(){
Foo* f = create();
return f->g() != 42;
}用clang++-8 -fsanitize=undefined -fvisibility=hidden main.cpp foo.so编译
这将报告
运行时错误:对地址0x00000290cea0的成员调用,它没有指向“Foo”类型的对象
这与bug.cgi?id=39191中描述的错误相同(谢谢@Nikita )
摘要:由于fvisibility=hidden没有导出符号(函数,没有使用属性__attribute__((visibility("default")))修饰的类在不同的DSO中使用时被认为是不同的)(例如可执行库和共享库)。因此,共享库中的基类Foo和可执行文件是不同的(它们有不同的vtable ),UBSAN检测到:可执行文件“期望”Exe::Foo的vtable对象,但得到Library::Foo。
在boost的情况下,类sp_counted_base是罪魁祸首,因为它直到Boost 1.69才被导出,这增加了BOOST_SYMBOL_EXPORT,因此切换到Boost 1.69+解决了这个问题。
https://stackoverflow.com/questions/57294792
复制相似问题