我知道未定义的行为一旦发生,就不可能再考虑代码了。我确信,完全。我什至认为我不应该深入了解 UB:一个理智的 C++ 程序不应该与 UB 一起玩,期间。
但是为了让我的同事和经理相信它的真正危险,我试图找到一个具体的例子,我们在产品中确实存在一个错误(他们认为这并不危险,最坏的情况下它总是会崩溃访问冲突)。
我主要关心的是在指向多态类的悬空指针上调用虚拟成员函数。
当一个指针被删除时,windows操作系统会在堆块的头部写入几个字节,通常也会覆盖堆块本身的第一个字节。这是跟踪堆块,将它们作为链表管理的方式......操作系统的东西。
虽然它没有在 C++ 标准中定义,但多态性是使用虚拟表 AFAIK 实现的。在 windows 下,虚拟表的指针位于堆块的第一个字节中,给定一个只继承一个基类的类。(多继承可能会更复杂,但我不会考虑这个。我们只考虑基类A,以及几个B,C,D继承A)。
现在让我们考虑我有一个指向 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
将被解释为有效的二进制代码,并执行为真实的 - 这可能导致任何可以想象的腐败
我不想夸大其词,以便说服。那么,我所说的是正确的、可能的和可能的吗?