I've recently tried to use boost serialization to serialize a class which contains a std::vector<std::unique_ptr<Base>>>
as a member. 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. Everything indeed builds and run fine but address sanitizer build (which runs in our CI) is failing with the following error:
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
:
/* 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
. However, it still looks like magic, especially with casting a (seemingly) random number to a Base
pointer. 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.
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.
The issue is actually caused not by Asan but by UBsan which performs verification of class type during casting (by reading and analyzing object's 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: