Я знаю, что Undefined Behavior, как только это произошло, не позволяет больше думать о коде. Я убежден, полностью. Я даже думаю, что я не должен слишком много разбираться в понимании UB: разумная программа на С++ не должна играть с UB, Period.
Но чтобы убедить моих коллег и менеджеров в реальной опасности этого, я пытаюсь найти конкретный пример, с ошибкой, которую мы имеем в продукте (о котором они думают, что это не опасно, в худшем случае это будет всегда сбой с нарушением доступа).
Моя основная проблема заключается в вызове виртуальной функции-члена на оборванных указателях на полиморфный класс.
Когда указатель удаляется, ОС Windows записывает несколько байтов в заголовок блока кучи и обычно перезаписывает также первые байты самого блока кучи. Это его способ отслеживать блоки кучи, управлять ими как связанным списком... Загрузки ОС.
Хотя он не определен в стандарте С++, полиморфизм реализуется с использованием виртуальных таблиц AFAIK. Под окнами указатель на виртуальную таблицу находится в первых байтах блока кучи, учитывая класс, наследующий только один базовый класс. (Это может быть сложнее с множественным наследованием, но я не буду учитывать это. Давайте рассмотрим только базовый класс 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
- память в случайном addres
0x09876543
будет интерпретироваться как действительный двоичный код, а EXECUTED для реального - это может привести к ЛЮБЫМ, что можно вообразить.
Я не хочу преувеличивать, чтобы убедить. Итак, что я говорю правильно, возможно и вероятно?