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

Я знаю, что неопределенное поведение, как только оно произошло, делает невозможным больше думать о коде. Я убежден, полностью. Я даже думаю, что не должен слишком углубляться в понимание UB: нормальная программа на C ++ не должна играть с UB, Period.

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


Моя главная задача — вызвать виртуальную функцию-член для висячих указателей на полиморфный класс.

Когда указатель удаляется, ОС Windows записывает несколько байтов в заголовок блока кучи и обычно перезаписывает также первые байты самого блока кучи.
Это способ отслеживать блоки кучи, управлять ими как связанным списком … ОС.

Хотя это не определено в стандарте C ++, полиморфизм реализован с использованием виртуальных таблиц 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
  • память на случайных адресах 0x09876543 будет интерпретироваться как действительный двоичный код и ИСПОЛНЕНО для реального
  • это может привести к ЛЮБОЙ коррупции

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

10

Решение

Ваш пример возможен.

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

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

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

3

Другие решения