Видящий указатель на полиморфный класс приводит к Undefined Поведение. Верно ли, что это может быть источником любой возможной коррупции?

Я знаю, что 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 для реального
  • это может привести к ЛЮБЫМ, что можно вообразить.

Я не хочу преувеличивать, чтобы убедить. Итак, что я говорю правильно, возможно и вероятно?

Ответ 1

Ваш пример - возможность.

Однако ситуация намного, намного хуже.

Если кто-то атакует пользователей вашего приложения, тогда память не будет содержать случайных данных. Злоумышленник попытается и, вероятно, сумеет повлиять на то, что эти данные будут. Как только это произойдет, злоумышленник сможет определить, какой код будет выполнен. И как только это произойдет, если ваше приложение не будет правильно изолировано (что, я уверен, это не с тем отношением ваших со-разработчиков), злоумышленник может взять на себя компьютер пользователя.

И это не гипотетическая возможность, а то, что произошло и произойдет снова.