Segfault при переполнении стека

Почему ядро ​​linux генерирует segfault при переполнении стека? Это может сделать отладку очень неудобной, когда alloca в c или fortran создает переполнение временных массивов. Несомненно, среда выполнения может выдать более полезную ошибку.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
12
0
4 142
6

Ответы 6

«Ядро» (на самом деле это не ядро, выполняющее ваш код, это процессор) не знает, как ваш код ссылается на память, к которой оно не должно прикасаться. Он знает только, что вы пытались это сделать.

Код:

char *x = alloca(100);
char y = x[150];

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

Вы можете попасть по тому же адресу с помощью:

char y = *((char*)(0xdeadbeef));

Кстати, я бы не рекомендовал использовать alloca, поскольку стек, как правило, гораздо более ограничен, чем куча (вместо этого используйте malloc).

Хотя стековое пространство намного быстрее для жестких манипуляций. Просто используйте экономно. Кроме того, проверьте "Getrlimit" перед выполнением любого выделения. Убедитесь, что у вас осталось достаточно места!

Sargun Dhillon 17.09.2008 13:11

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

Кроме того, в прошлом для проектов я писал свой собственный обработчик сигналов в segfault (см. Страницу руководства signal (2)). Обычно ловил сигнал и писал на консоли «Произошла фатальная ошибка». Я проделал еще кое-что с флагами контрольных точек и отладкой.

Для отладки ошибок сегментации вы можете запустить программу в GDB. Например, следующая программа на C будет segfault: # segfault.c #включают #включают

int main() 
{
        printf("Starting\n");
        void *foo=malloc(1000);
        memcpy(foo, 0, 100); //this line will segfault
        exit(0);
}

Если я скомпилирую так:

gcc -g -o segfault segfault.c 

а затем запустите его так:

$ gdb ./segfault
GNU gdb 6.7.1
Copyright (C) 2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) run
Starting program: /tmp/segfault 
Starting

Program received signal SIGSEGV, Segmentation fault.
0x4ea43cbc in memcpy () from /lib/libc.so.6
(gdb) bt
#0  0x4ea43cbc in memcpy () from /lib/libc.so.6
#1  0x080484cb in main () at segfault.c:8
(gdb) 

Я узнал от GDB, что в строке 8 произошла ошибка сегментации. Конечно, есть более сложные способы обработки переполнения стека и других ошибок памяти, но этого будет достаточно.

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

Фактически вы можете поймать условие переполнения стека с помощью обработчиков сигналов.

Для этого вы должны сделать две вещи:

  • Настройте обработчик сигнала для SIGSEGV (segfault) с помощью sigaction, для этого установите флаг SO_ONSTACK. Это указывает ядру использовать альтернативный стек при доставке сигнала.

  • Вызовите sigaltstack (), чтобы настроить альтернативный стек, который будет использовать обработчик SIGSEGV.

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

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

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

Если вам нужна автоматическая помощь, вы можете использовать параметр gcc -Wstack-protector, который обнаружит некоторые переполнения во время выполнения и прервет программу.

valgrind хорош для ошибок динамического распределения памяти, но не для ошибок стека.

Некоторые комментарии полезны, но проблема не в ошибках выделения памяти. То есть в коде нет ошибки. Это довольно неприятно в fortran, где среда выполнения размещает временные значения в стеке. Таким образом, такая команда, как написать (fp) x, y, z может запускаться без предупреждения. Служба технической поддержки компилятора Intel Fortran сообщает, что библиотека времени выполнения не может распечатать более полезное сообщение. Однако, если Мигель прав, это должно быть возможно, как он предполагает. Так что большое спасибо. Остается вопрос: как мне сначала найти адрес ошибки сегмента и выяснить, возникла ли она из-за переполнения стека или какой-либо другой проблемы.

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

Другие вопросы по теме