Устранение ошибки дезинфицирующего средства при использовании ускоренной сериализации

Недавно я пытался использовать ускоренную сериализацию для сериализации класса, который содержит std::vector<std::unique_ptr<Base>>> в качестве члена. Согласно документации по ускорению (https://www.boost.org/doc/libs/1_71_0/libs/serialization/doc/serialization.html#derivedpointers), мы должны зарегистрировать производные классы, используя метод register_type из архива, чтобы сериализация работала правильно. Все действительно собирается и работает нормально, но сборка дезинфицирующего средства для адресов (которая работает в нашем 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
    ...

Проверив, что находится в 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)

Судя по коду и комментарию, это выражение вычисляет смещение класса Base в пределах Derived. Тем не менее, это все еще выглядит как волшебство, особенно с приведением (на первый взгляд) случайного числа к указателю Base. Было бы здорово, если бы кто-то мог пролить свет на то, почему это на самом деле вычисляет смещение.

РЕДАКТИРОВАТЬ: Вот простой пример, в котором используется метод расчета смещения, показанный выше: https://godbolt.org/z/Bmp7zH Он компилируется и запускается, если дезинфицирующее средство выключено, но после его включения программа аварийно завершает работу.

Это также попытка воспроизвести исходную проблему с SEGV в проводнике компилятора: https://godbolt.org/z/w8ZNx8 Однако привязка ускоренной сериализации, похоже, не работает, а также, если я включаю параметры дезинфицирующего средства, время сборки иногда истекает.


person Marko Popovic    schedule 06.12.2019    source источник
comment
Этот код не обращается к памяти (если где-то нет виртуального наследования?), поэтому не должен вызывать SEGV. Предоставьте MVCE.   -  person yugr    schedule 06.12.2019
comment
@yugr Я разместил ссылки на MVCE в Compiler Explorer. Взгляни, пожалуйста.   -  person Marko Popovic    schedule 06.12.2019
comment
Спасибо, это помогло.   -  person yugr    schedule 06.12.2019
comment
Пожалуйста, добавьте ссылку на ошибку, если вы ее зарегистрируете.   -  person yugr    schedule 09.12.2019
comment
Спасибо, люди, столкнувшиеся с этой проблемой, смогут поставить +1 к ошибкам, что обычно мотивирует сопровождающих.   -  person yugr    schedule 13.12.2019


Ответы (1)


Проблема на самом деле вызвана не Asan, а UBsan, который выполняет проверку типа класса во время приведения (путем чтения и анализа vptr объекта). Попытка чтения памяти по поддельному адресу приведет к сбою в вашем случае.

Это ошибка в компиляторе, поэтому я настоятельно рекомендую сообщить об этом разработчикам дезинфицирующего средства:

person yugr    schedule 06.12.2019