【问题标题】:C++ UBSAN produces false positives with derived objectsC++ UBSAN 对派生对象产生误报
【发布时间】:2019-12-09 05:04:07
【问题描述】:

我想使用 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 编译并观看

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

使用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”类型的对象

【问题讨论】:

  • 您使用的是什么编译器和版本? span>
  • 您是否在计算机上尝试过,或者仅在godbolt.org上?可以配置Godbolt.org上的系统,使UBSAN失败。 span>
  • 这很有趣。我也有ubsan遇到困难,但在我的程序上,只在GCC下。 clang 的消毒剂工作正常我发现很多关于类似主题的讨论,例如:bugs.llvm.org/show_bug.cgi?id=39191
  • 我无法重现这个。
  • 它发生在所有编译器上,尽管我注意到 MWE 只在 Godbolt 上工作。我使用我的原始代码生成了 MWE,随后将其剥离为仍然在 Godbolt 上产生错误的基本要素,以尝试不同的编译器并供人们使用。似乎那里的行为确实有所不同,所以我附上了原始的 MWE,它与链接一起引导我找到原因。对“升级提升”作为解决方案并不真正满意,但嗯... span>

标签: c++ undefined-behavior ubsan


【解决方案1】:

尝试在 cmets 之后自己回答这个问题并创建另一个 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 #include "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”类型的对象

这与https://bugs.llvm.org/show_bug.cgi?id=39191 中描述的错误相同(感谢@Nikita Petrenko)

总结:fvisibility=hidden 未导出符号(函数、未使用属性 __attribute__((visibility("default"))) 修饰的类在用于不同的 DSO(例如可执行文件和共享库)时被认为是不同的。因此基类 Foo 在共享库和可执行文件是不同的(它们有不同的 vtables),UBSAN 检测到:可执行文件“期望”Exe::Foo 的对象 witg vtable 而是得到 Library::Foo

在 Boost 案例中,sp_counted_base 类是罪魁祸首,因为直到 Boost 1.69 添加了 BOOST_SYMBOL_EXPORT,它才会导出,因此切换到 Boost 1.69+ 可以解决此问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多