Если у вас MMU, вы можете получить четко определенное безопасное поведение: переполнение стека вызывает ошибку неверной страницы (Ошибка сегментации в POSIX). Для этого требуется некоторая помощь от ОС или ручное отображение Страница только для чтения ниже предела роста стека. Просто убедитесь, что вы касаетесь каждой страницы пространства стека по мере его увеличения. (Или один зонд на 64 кБ, если вы резервируете больше места для защиты). Вы можете поймать SIGSEGV, если хотите в POSIX OS. Другие ОС могут иметь другие механизмы.
G CC -fstack-check
делает это довольно дешево, в сочетании с ОС, имеющей «защитную область» непопечатанных страниц под отображением стека. (Точнее говоря, ниже максимального предела роста для стека, поэтому стек может продолжать расти, но не за пределами этой защитной области.)
Защитная область 1 МБ (текущее значение Linux по умолчанию) обычно достаточно, чтобы вам даже не нужны стековые зонды для предотвращения ошибок stack cla sh, когда стек перекрывается с динамическим выделением c под стеком. Но глючная / уязвимая программа, которая использует неконтролируемый пользовательский ввод в качестве размера для alloca или C99 VLA, может пропустить весь путь через защитную область.
И Windows всегда требуют «стековые зонды» ( касание памяти на каждой странице размером 4 КБ для увеличения стека большого или переменного размера, как gcc -fstack-protector
). Windows требует, чтобы это вообще вызывало рост стека; он не увеличит ваш стек, если вы коснетесь нескольких страниц ниже последней использованной страницы стека.
Linux переполнение стека процесса локальными переменными (защита стека) содержит больше подробностей.
Пробники стеков - это, по сути, надежный способ убедиться, что ваша программа работает с ошибками, прикоснувшись к неотображенной странице (которая не вызовет увеличения стека), прежде чем она сделает что-нибудь опасное. Это может работать на любой ОС и любой ISA с MMU.
Общая стоимость времени выполнения равна всего лишь oop при входе в функцию (и при каждом выделении или области, включающей VLA), которая затрагивает память шагом 4 кБ, пока не пройдет расстояние в стеке. рост. Если этот размер известен во время компиляции, он может быть полностью развернут / очищен только для одной или нескольких инструкций.
Или в большинстве функций, которые имеют только несколько локальных объектов, не включая массив огромного или переменного размера, нет наверху вообще. Выполнение другого вызова функции включает запись в стековую память для сохранения адреса возврата, либо как часть x86 call
, либо при входе в функцию для ISA RIS C, которые передают адрес возврата в регистр ссылок. Таким образом, даже целая цепочка функций, которые выделяют небольшие и средние массивы и не касаются их, не могут пропустить указатель стека за защитной страницей. Сохранение / восстановление адреса возврата в / из стека фактически является пробной задачей.