悬空指向多态类的指针会导致未定义的行为.它真的可以成为任何可以想象的腐败的根源吗?
Dangling pointer to polymorphic class leads to Undefined Behaviour. Is it true that it can be the source of any corruption imaginable?
我知道,一旦发生,未定义的行为就不可能再考虑代码了。我完全相信。我甚至认为我不应该过多地理解UB:一个理智的C++程序不应该玩UB,Period。
但是,为了让我的同事和经理相信它的真正危险,我试图找到一个具体的例子,其中包含我们在产品中确实存在的错误(他们认为它并不危险,最坏的情况是它总是会因访问违规而崩溃)。
我主要关心的是关于在指向多态类的悬空指针上调用虚拟成员函数。
删除指针时,Windows 操作系统将在堆块的标头中写入几个字节,并且通常还会覆盖堆块本身的第一个字节。这是它跟踪堆块的方式,将它们作为链表进行管理......操作系统的东西。
虽然它没有在C++标准中定义,但多态性是使用虚拟表 AFAIK 实现的。在 windows 下,指向虚拟表的指针位于堆块的第一个字节中,给定一个只继承一个基类的类。 (使用多重继承可能会更复杂,但我不会考虑这一点。让我们只考虑基类 A,以及继承 A 的几个 B、C、D )。
现在让我们考虑我有一个指向 A 的指针,该指针被实例化为 D 对象。并且该 D 对象已在代码中的其他地方被删除:因此堆块现在是一个空闲堆块,并且它的第一个字节已被覆盖,因此虚拟表指针几乎随机指向内存中的某个位置,假设地址0x01234567
。
在代码中的某个地方,我们调用:
void test(A * pA)
{
# here we do not know that pA is dangling pointer
# that memory address has been deleted by another thread, in another part of the code
pA->SomeVirtualFunction();
}
我说得对吗:
- 运行时会将地址
0x01234567
处的内存解释为虚拟表 - 如果在像 vtable 一样错误地解释此内存地址时,它没有进入禁止的内存区域,则不会有任何访问冲突
- 被误解的虚拟表将为虚拟函数执行提供一个随机地址,假设
0x09876543
- 随机地址
0x09876543
处的内存将被解释为有效的二进制代码,并实际执行 - 这可能导致任何可以想象的腐败
我不想夸大其词,以说服人。那么,我所说的是正确的、可能的和可能的吗?
你的例子是可能的。
然而,情况要糟糕得多。
如果有人攻击应用程序的用户,则内存将不包含随机数据。攻击者将尝试并可能设法影响该数据的内容。一旦发生这种情况,攻击者可能能够确定将执行哪些代码。一旦发生这种情况,除非你的应用程序被正确沙盒化(我敢打赌,你的共同开发人员没有这种态度),否则攻击者可能能够接管用户的计算机。
这不是一个假设的可能性,而是已经发生并将再次发生的事情。
- 字节真的是最小可寻址单元吗
- 当可输入框在窗口中处于活动状态时获得通知的任何方法
- 检查模板中 nullptr 的函数指针,了解任何类型的可调用对象
- 在调用其析构函数之前,是否有任何实际理由检查某些东西是否可破坏?
- 在C++中获取命名空间名称的任何可移植技巧
- 如何从 pjsip 发送电子邮件.是否有任何可用于发送电子邮件的默认方法
- C 功能接受给定模板签名的任何可呼叫
- std::函数,返回任何可求和类型
- 任何编译器真的会删除这些副本吗
- 如何从任何可调用的函数中获得有意义的函数签名
- 在c++ 11中,是否有一种方法可以在调用以任何可调用对象(包括绑定方法)作为参数的函数时不需要模板参数
- 如何将任何可迭代类型作为函数参数传递
- 用于MFC应用程序的任何可测试的架构或设计模式
- c++ std::sort():仅使用一个比较函数的任何可比较成员的对象向量
- 任何可在facebook上共享/发送邮件的cocos2d-X功能
- 用宏生成文件,不生成任何可执行文件
- 有任何可观察的Qt容器类吗?
- C++系统头是否有任何可保证的依赖性
- 是否有一种统一的方法来存储和传递任何可调用的对象
- 我是否应该声明任何可以为const的方法为const方法?