Может ли переполнение стека привести к чему-то, кроме ошибки сегментации? - PullRequest
0 голосов
/ 06 июня 2018

В скомпилированной программе (скажем, C или C ++, но я предполагаю, что этот вопрос может распространиться на любой язык не-VM-ish со стеком вызовов) - очень часто, когда вы переполняете свой стек, вы получаете сегментациюошибка :

Переполнение стека является [a] причиной, результатом является ошибка сегментации.

Однако всегда ли это так?Может ли переполнение стека привести к другим видам поведения программ / ОС?

Я также спрашиваю о не-Linux, не-Windows-ОС и не-X86-оборудовании.(Конечно, если у вас нет аппаратной защиты памяти или поддержки ОС для нее (например, MS-DOS), то нет такой вещи, как ошибка сегментации; я спрашиваю о случаях, когда вы могли бы получитьошибка сегментации, но происходит что-то еще).

Примечание. Предположим, что, кроме переполнения стека, программа действительна и не пытается получить доступ к массивам за их пределами, разыменовывать недействительные указатели и т. д.

Ответы [ 4 ]

0 голосов
/ 07 июня 2018

Другие ответы довольно хорошо охватили сторону ПК.Я коснусь некоторых проблем во встроенном мире.

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

Нет никакой гарантии, что это произойдет при переполнении стека.Стек может быть размещен в любом месте в ОЗУ, и это обычно будет рядом с другими переменными.Результатом переполнения стека обычно является повреждение этих переменных.

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

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

Внедренный код имеет общий шаблон для решения этой проблемы, который заключается в «водяном знаке» стека путем инициализации каждого байта известным значением.Иногда компилятор может сделать это;или иногда вам может понадобиться реализовать это самостоятельно в коде запуска перед main ().Вы можете оглянуться назад с конца стека, чтобы найти, где оно больше не установлено в это значение, и в этот момент вы знаете верхний предел использования стека;или если все неверно, значит, вы переполнены.Встраиваемые приложения обычно (и эффективная практика) постоянно опрашивают это как фоновую операцию и сообщают об этом в диагностических целях.

После того, как стало возможным отслеживать использование стека, большинство компаний установитдопустимый запас в худшем случае, чтобы избежать переполнения.Обычно это где-то от 75% до 90%, но всегда будет запасной.Это не только позволяет предположить, что существует худший наихудший случай, которого вы еще не видели, но также облегчает жизнь для будущей разработки, когда необходимо добавить новый код, который использует больше стека.

0 голосов
/ 06 июня 2018

Stackoverflow - одна из многих причин для неопределенного поведения программы.В этом случае вы можете получить ожидаемый результат или ошибку сегментации, или ваш жесткий диск может быть удален и т. Д. Не ожидайте какого-либо определенного поведения, потому что это неопределенное поведение.

0 голосов
/ 06 июня 2018

Одна вещь - это то, что происходит во время выполнения, когда вы переполняете стек, что может быть много вещей.В том числе, но не ограничивается;ошибка сегментации, перезапись переменных после того, что вы переполняете, вызывая недопустимые инструкции, вообще ничего и многое другое.«Старая» классическая статья Smashing The Stack для удовольствия и получения прибыли описывает множество способов, которыми можно «повеселиться» с этим материалом.

Другое дело, что может произойти во время компиляции.Как в C, так и в C ++ запись за пределы массива или превышение размера стека является неопределенным поведением, и когда программа содержит UB в любом месте , компилятор в основном может свободно делать все, что он хочет для любая часть вашей программы.И современные компиляторы становятся очень агрессивными в использовании UB для целей оптимизации - часто предполагая, что UB никогда не происходит, что приводит к тому, что они просто удаляют код, содержащий UB, или заставляет переход всегда или никогда не выполняться, потому что альтернатива вызовет UB.Иногда компилятор вводит путешествие во времени или , вызывая функцию, которая никогда не вызывалась в исходном коде и многие, многие другие вещи, которые могут вызвать действительно запутанное поведение во время выполнения.

См. Также:

Что должен знать каждый программист C о неопределенном поведении # 1/3

Что должен знать каждый программист C о неопределенном поведении #2/3

Что должен знать каждый программист C о неопределенном поведении # 3/3

Руководство по неопределенному поведению в C и C ++,Часть 1

Руководство по неопределенному поведению в C и C ++, часть 2

Руководство по неопределенному поведению в C и C ++, часть 3

0 голосов
/ 06 июня 2018

Да, даже на стандартной ОС (Linux) и стандартном оборудовании (x86).

void f(void) {
    char arr[BIG_NUMBER];
    arr[0] = 0; // stack overflow
}

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

Если BIG_NUMBER едва достаточен для переполнения, вы столкнетесь сЗащитите стек и получите ошибку сегментации.Вот для чего нужна защита стека, и она может быть такой же маленькой, как одна страница размером 4 КБ (но не меньше, и этот размер 4 КБ используется до Linux 4.12), или может быть больше (по умолчанию 1 МБ в Linux 4.12)см. мм: большой защитный зазор в стеке ), но он всегда имеет определенный размер.

Если BIG_NUMBER достаточно велик, переполнение может пропустить защитный элемент стека и приземлиться на какой-то другой элемент.памяти, потенциально памяти, которая действительна.Это может привести к тому, что ваша программа будет работать некорректно, но не сбоит, что в основном является наихудшим сценарием: мы хотим, чтобы наши программы аварийно завершали работу, если они некорректны, а не совершали непреднамеренные действия.

...