boost :: serialization中的派生类偏移量计算.有效吗 [英] Derived class offset calculation in boost::serialization. Is it valid?

查看:54
本文介绍了boost :: serialization中的派生类偏移量计算.有效吗的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

boost :: serialization 包含此代码:

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

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

Its purpose is to calculate an offset between base and derived classes. Is this code free of undefined behaviour?

我问的原因是ASAN + UBSAN抱怨.例如,这段代码

The reason why I am asking is that ASAN+UBSAN complain. For example, this code

#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)

compiled as (gcc version 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

是假阳性还是此代码确实存在问题?

Is it a false-positive or does this code really have problems?

更新09.12.2019 :根据Filipp的建议和我的实验,此代码似乎有效,并且不会产生任何警告:

Update 09.12.2019: based on Filipp's proposal and my experiments, this code seems to work and does not produce any warnings:

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

有人看到此代码段有任何问题吗?如果没有,我建议将其推荐给 boost .

Does anybody see any problems with this snippet? If not, I would propose it to boost.

更新16.12.2019::修复程序已合并转到 boost :: serialization develop 分支.

Update 16.12.2019: the fix was merged to boost::serialization develop branch.

推荐答案

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

As indicated by another answer, the problem is that (1 << 20) isn't the address of any object. Using a char[] that could in principle store a Derived seems to work around the problem:

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

是假阳性还是此代码确实存在问题?

Is it a false-positive or does this code really have problems?

根据对该标准的严格阅读,该代码确实具有UB,因此从某种意义上讲,它不是 false 肯定的.实际上,boost作者和编译器作者都同意这只是指​​针数学,因此无论如何它应该做正确的事情.

According to a strict reading of the standard the code does exhibit UB, so in that sense it is not a false positive. In practice, both boost authors and compiler writers agree this is just pointer math, so it should do the right thing anyway.

除非涉及的基础之一是 virtual .然后强制转换会尝试从vtable读取偏移量.

Unless one of the bases involved is virtual. Then the cast would try to read the offset from the vtable.

使用 nullptr 会产生0.已更改为使用本地对齐缓冲区.

Edit 2: Using nullptr produces 0. Changed to using local aligned buffer.

这篇关于boost :: serialization中的派生类偏移量计算.有效吗的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆