Я знаю, что неопределенное поведение, как только оно произошло, делает невозможным больше думать о коде. Я убежден, полностью. Я даже думаю, что не должен слишком углубляться в понимание 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
как будто это был виртуальный стол0x09876543
0x09876543
будет интерпретироваться как действительный двоичный код и ИСПОЛНЕНО для реальногоЯ не хочу преувеличивать, чтобы убедить.
Итак, то, что я говорю, правильно, возможно и вероятно?
Ваш пример возможен.
Однако ситуация намного, намного хуже.
Если кто-то атакует пользователей вашего приложения, то в памяти не будет случайных данных. Злоумышленник попытается повлиять на то, какими будут эти данные. Как только это произойдет, злоумышленник сможет определить, какой код будет выполнен. И как только это произойдет, если ваше приложение не будет должным образом помещено в изолированную программную среду (что, я уверен, не соответствует позиции ваших соавторов), злоумышленник может захватить компьютер пользователя.
И это не гипотетическая возможность, а то, что произошло и произойдет снова.