boost::serialization 中的派生类偏移量计算。有效吗?

2024-02-09

boost::serialization包含这段代码 https://github.com/boostorg/serialization/blob/76df6df23ff9ad081099a0288e943535626aeb08/include/boost/serialization/void_cast.hpp#L187:

reinterpret_cast<std::ptrdiff_t>(
    static_cast<Derived *>(
        reinterpret_cast<Base *>(1 << 20)
    )
) - (1 << 20)

其目的是计算基类和派生类之间的偏移量。这段代码没有未定义的行为吗?


我之所以这么问是因为ASAN+UBSAN抱怨。例如,这段代码

#include <iostream>

class Foo { public: virtual void foo() {} };
class Base { public: virtual void base() {} };
class Derived: public Foo, public Base {};

int main()
{
    std::cout <<
       (reinterpret_cast<std::ptrdiff_t>(
            static_cast<Derived *>(
                reinterpret_cast<Base *>(1 << 20)
            )
       ) - (1 << 20));
}

编译为(gcc 版本 9.2.1)

g++ -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer -g main.cpp

产生这个输出

AddressSanitizer:DEADLYSIGNAL
=================================================================
==72613==ERROR: AddressSanitizer: SEGV on unknown address 0x0000000ffff8 (pc 0x0000004012d9 bp 0x7ffd5b3eecf0 sp 0x7ffd5b3eece0 T0)
==72613==The signal is caused by a READ memory access.
    #0 0x4012d8 in main main.cpp:13
    #1 0x7f74a90d5f42 in __libc_start_main (/lib64/libc.so.6+0x23f42)
    #2 0x40112d in _start (/home/.../a.out+0x40112d)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV main.cpp:13 in main

是误报还是这段代码确实有问题?


2019 年 12 月 9 日更新:根据 Filipp 的提议和我的实验,这段代码似乎可以工作并且不会产生任何警告:

std::aligned_storage<sizeof(Derived)>::type data;
reinterpret_cast<char*>(&data)
  - reinterpret_cast<char*>(
        static_cast<Base*>(
            reinterpret_cast<Derived*>(&data)));

有人看到这个片段有什么问题吗?如果没有的话,我会建议boost.


2019 年 12 月 16 日更新:修复被合并 https://github.com/boostorg/serialization/commit/489eef0e178574c9cd8f2e508da6261aea9b0093 to boost::serialization develop branch.


正如另一个答案所示,问题在于(1 << 20)不是任何对象的地址。用一个char[]原则上可以存储一个Derived似乎可以解决这个问题:

#include <stdint.h>
#include <stddef.h>
#include <stdio.h>

class Foo { public: virtual void foo() {} };
class Base { public: virtual void base() {} };
class Derived: public Foo, public Base {};

int main() {
    alignas (Derived) char const buffer[sizeof(Derived)] = {};
    Derived const* const derived = reinterpret_cast<Derived const*>(buffer);
    Base const* const base = derived;
    ptrdiff_t const delta =
        reinterpret_cast<char const*>(derived) -
        reinterpret_cast<char const*>(base);
    ::printf("%td\n", delta);
    return 0;
}

是误报还是这段代码确实有问题?

根据对标准的严格解读,该代码确实表现出 UB,因此从这个意义上说,它不是一个false积极的。在实践中,boost 作者和编译器作者都同意这只是指​​针数学,所以无论如何它应该做正确的事情。

编辑:除非涉及的基地之一是virtual。然后强制转换将尝试从 vtable 中读取偏移量。

编辑2:使用nullptr产生 0。更改为使用本地对齐缓冲区。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

boost::serialization 中的派生类偏移量计算。有效吗? 的相关文章

随机推荐