指向多态 class 的悬挂指针会导致未定义的行为。它真的可以成为任何可以想象的腐败的根源吗?
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。
但是为了让我的同事和经理相信它的真正危险,我试图找到一个具体的例子,我们的产品确实存在一个错误(他们认为最坏的情况下这并不危险它总是会因访问冲突而崩溃。
我主要关心的是在指向多态 class 的悬空指针上调用虚拟成员函数。
当一个指针被删除时,windows OS 会在堆块的头部写入几个字节,通常也会覆盖堆块本身的第一个字节。
这是它跟踪堆块的方式,将它们作为链表进行管理... OS stuffs.
虽然在 C++ 标准中没有定义,但多态性是使用虚拟 tables 实现的,AFAIK。
在 windows 下,指向虚拟 table 的指针位于堆块的第一个字节,假设 class 仅继承一个基 class。 (多重继承可能会更复杂,但我不会考虑这一点。我们只考虑base class A,以及几个继承A的B,C,D)。
现在让我们考虑一下我有一个指向 A 的指针,它被实例化为 D 对象。那个 D 对象已经在代码的其他地方被删除了:所以堆块现在是一个空闲堆块,它的第一个字节被覆盖了,结果虚拟 table 指针几乎随机指向了某个地方内存,比方说地址 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
处的内存解释为虚拟内存 table
- 如果将此内存地址错误地解释为 vtable,它不会进入禁止内存区域,则不会出现任何访问冲突
- 被误解的virtualtable会提供一个随机地址给virtual函数执行,比方说
0x09876543
- 随机地址
0x09876543
处的内存将被解释为有效的二进制代码,并真正执行
- 这可能导致任何可以想象的腐败
我不想夸大其词,以说服。
那么,我说的是正确的、可能的、可能的吗?
你的例子是有可能的。
然而,情况要糟糕得多。
如果有人在攻击您的应用程序的用户,那么内存将不会包含随机数据。攻击者将尝试并可能设法影响该数据的内容。一旦发生这种情况,攻击者就可以确定要执行的代码。一旦发生这种情况,除非您的应用程序被适当地沙箱化(我敢打赌您的合作开发人员不会采取这种态度),否则攻击者可能能够接管用户的计算机。
这不是假设的可能性,而是已经发生并将再次发生的事情。
我知道未定义行为一旦发生,就无法再考虑代码了。我完全相信。我什至认为我不应该深入了解 UB:一个理智的 C++ 程序不应该玩 UB,Period。
但是为了让我的同事和经理相信它的真正危险,我试图找到一个具体的例子,我们的产品确实存在一个错误(他们认为最坏的情况下这并不危险它总是会因访问冲突而崩溃。
我主要关心的是在指向多态 class 的悬空指针上调用虚拟成员函数。
当一个指针被删除时,windows OS 会在堆块的头部写入几个字节,通常也会覆盖堆块本身的第一个字节。 这是它跟踪堆块的方式,将它们作为链表进行管理... OS stuffs.
虽然在 C++ 标准中没有定义,但多态性是使用虚拟 tables 实现的,AFAIK。 在 windows 下,指向虚拟 table 的指针位于堆块的第一个字节,假设 class 仅继承一个基 class。 (多重继承可能会更复杂,但我不会考虑这一点。我们只考虑base class A,以及几个继承A的B,C,D)。
现在让我们考虑一下我有一个指向 A 的指针,它被实例化为 D 对象。那个 D 对象已经在代码的其他地方被删除了:所以堆块现在是一个空闲堆块,它的第一个字节被覆盖了,结果虚拟 table 指针几乎随机指向了某个地方内存,比方说地址 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
处的内存解释为虚拟内存 table - 如果将此内存地址错误地解释为 vtable,它不会进入禁止内存区域,则不会出现任何访问冲突
- 被误解的virtualtable会提供一个随机地址给virtual函数执行,比方说
0x09876543
- 随机地址
0x09876543
处的内存将被解释为有效的二进制代码,并真正执行 - 这可能导致任何可以想象的腐败
我不想夸大其词,以说服。 那么,我说的是正确的、可能的、可能的吗?
你的例子是有可能的。
然而,情况要糟糕得多。
如果有人在攻击您的应用程序的用户,那么内存将不会包含随机数据。攻击者将尝试并可能设法影响该数据的内容。一旦发生这种情况,攻击者就可以确定要执行的代码。一旦发生这种情况,除非您的应用程序被适当地沙箱化(我敢打赌您的合作开发人员不会采取这种态度),否则攻击者可能能够接管用户的计算机。
这不是假设的可能性,而是已经发生并将再次发生的事情。