Как обнаружить
Заполнение стека шаблонами. Стек заполняется известными значениями (например, 0xAA или 0xDEADBEEF) при инициализации. При обнаружении изменения шаблона фиксируется переполнение.
Пример:
// Заполнение стека шаблоном
memset(&stack_base, 0xAA, stack_size);
// Проверка целостности шаблона
for (uint8_t *ptr = &stack_base; ptr < &stack_top; ptr++) {
if (*ptr != 0xAA) {
error_handler();
}
}
Проверка указателя стека. Регулярная проверка текущего значения указателя стека на соответствие границам выделенной области.
Использование аппаратных методов:
- MPU (Memory Protection Unit). Конфигурируется для защиты области памяти, выделенной под стек, от записи за пределы. При нарушении генерируется исключение, которое можно обработать в обработчике ошибок. Пример: на Cortex-M процессорах MPU позволяет задать границы стека и сгенерировать HardFault при выходе за пределы.
- Аппаратные watchpoints. Настройка CPU для отслеживания доступа к памяти в критической зоне (например, конец стека). При записи в защищённую область генерируется прерывание, позволяющее определить момент переполнения.
- Регистры ограничения стека. Некоторые процессоры (например, ARMv8-M) имеют специальные регистры для установки максимального значения указателя стека. При превышении границы генерируется исключение.
Как исправить?
-
Для критических систем:
- используйте комбинацию аппаратных (MPU) и программных методов (canaries),
- настройте watchpoints на запись в область ниже стека.
-
Для RTOS-приложений:
- регулярно проверяйте использование стека через uxTaskGetStackHighWaterMark(),
- активируйте встроенные проверки стека (например, в FreeRTOS через configCHECK_FOR_STACK_OVERFLOW).
-
Для bare-metal:
- реализуйте ручное заполнение стека шаблонами и периодическую проверку,
- используйте инструменты статического анализа (например, gcc -fstack-check).