Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

Чтобы сделать это, вы должны сделать две вещи:

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

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

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

Ответ 2

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

Код:

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

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

Вы можете получить тот же адрес:

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

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

Ответ 3

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

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

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

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 произошла ошибка сегментации. Конечно, есть более сложные способы обработки и других ошибок памяти, но этого будет достаточно.

Ответ 4

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

Ответ 5

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

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

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

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

Ответ 6

Некоторые из комментариев полезны, но проблема заключается не в ошибках выделения памяти. Это не ошибка в коде. Это довольно неприятно в fortran, где среда выполнения выделяет временные значения в стеке. Таким образом, команда, такая как записи (FP) х, у, г могут вызывать segfault без предупреждения. Техническая поддержка компилятора Intel Fortran говорит о том, что библиотека времени выполнения не может печатать более полезное сообщение. Однако, если Мигель прав, это должно быть возможно, как он предлагает. Так что спасибо большое. Остается вопрос, каким образом я могу сначала найти адрес seg fault и выяснить, произошел ли он из или какой-либо другой проблемы.

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