简体   繁体   English

使用 boost 序列化时解决消毒剂错误

[英]Address sanitizer error when using boost serialization

I've recently tried to use boost serialization to serialize a class which contains a std::vector<std::unique_ptr<Base>>> as a member.我最近尝试使用 boost 序列化来序列化一个包含std::vector<std::unique_ptr<Base>>>作为成员的类。 According to the boost documentation ( https://www.boost.org/doc/libs/1_71_0/libs/serialization/doc/serialization.html#derivedpointers ), we must register the derived classes using method register_type of the archive for the serialization to work properly.根据 boost 文档( https://www.boost.org/doc/libs/1_71_0/libs/serialization/doc/serialization.html#derivedpointers ),我们必须使用存档的register_type方法register_type派生类以进行序列化正常工作。 Everything indeed builds and run fine but address sanitizer build (which runs in our CI) is failing with the following error:一切确实构建并运行良好,但地址消毒器构建(在我们的 CI 中运行)失败并出现以下错误:

ASAN:DEADLYSIGNAL
=================================================================
==3==ERROR: AddressSanitizer: SEGV on unknown address 0x000000100000 (pc 0x559bc5f18288 bp 0x7ffe74fd8d30 sp 0x7ffe74fd8d10 T0)
==3==The signal is caused by a READ memory access.
==3==Hint: address points to the zero page.
    #0 0x559bc5f18287 in boost::serialization::void_cast_detail::void_caster_primitive<Derived, Base>::void_caster_primitive() /usr/include/boost/serialization/void_cast.hpp:188
    #1 0x559bc5f1714a in boost::serialization::singleton<boost::serialization::void_cast_detail::void_caster_primitive<Derived, Base> >::get_instance()::singleton_wrapper::singleton_wrapper() /usr/include/boost/serialization/singleton.hpp:117
    #2 0x559bc5f173be in boost::serialization::singleton<boost::serialization::void_cast_detail::void_caster_primitive<Derived, Base> >::get_instance() /usr/include/boost/serialization/singleton.hpp:118
    #3 0x559bc5ef3294 in __static_initialization_and_destruction_0 /usr/include/boost/serialization/singleton.hpp:155
    ...

Upon inspecting what is in void_cast.hpp , I've discovered the problematic code in the constructor of void_caster_primitive :经检查是什么void_cast.hpp ,我发现在构造函数中有问题的代码void_caster_primitive

/* note about displacement:
 * displace 0: at least one compiler treated 0 by not shifting it at all
 * displace by small value (8): caused ICE on certain mingw gcc versions */
reinterpret_cast<std::ptrdiff_t>(
    static_cast<Derived *>(
        reinterpret_cast<Base *>(1 << 20)
    )
) - (1 << 20)

Judging by the code and the comment, this expression is calculating the displacement of Base class within Derived .从代码和注释来看,这个表达式是计算Base类在Derived的位移。 However, it still looks like magic, especially with casting a (seemingly) random number to a Base pointer.但是,它看起来仍然很神奇,尤其是将(看似)随机数转换为Base指针时。 It would be great if someone could shed some light on why this actually calculates the displacement.如果有人能够解释为什么这实际上计算了位移,那就太好了。

EDIT: Here is a simple example that uses the displacement calculation method shown above: https://godbolt.org/z/Bmp7zH It compiles and runs if sanitizer is turned off, but after it is turned on, the program terminates abnormally.编辑:这里是一个简单的例子,它使用了上面显示的位移计算方法: https ://godbolt.org/z/Bmp7zH 如果关闭消毒剂,它会编译并运行,但打开后,程序异常终止。

This is also an attempt to reproduce the original issue with SEGV on compiler explorer: https://godbolt.org/z/w8ZNx8 However, linking boost serialization doesn't seem to work, and also if I turn on the sanitizer options, the build sometimes times out.这也是尝试在编译器资源管理器上重现 SEGV 的原始问题: https ://godbolt.org/z/w8ZNx8 但是,链接 boost 序列化似乎不起作用,而且如果我打开消毒剂选项,构建有时会超时。

The issue is actually caused not by Asan but by UB san which performs verification of class type during casting (by reading and analyzing object's vptr).这个问题实际上不是由 Asan 引起的,而是由UB san 引起的,它在转换期间执行类类型的验证(通过读取和分析对象的 vptr)。 Trying to read memory at fake address would cause a crash in your case.尝试在假地址读取内存会导致您的情况崩溃。

This is a bug in compiler so I strongly suggest to report this to sanitizer developers:这是编译器中的一个错误,所以我强烈建议将此报告给消毒剂开发人员:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM