Сегфоут при переполнении стека - PullRequest
12 голосов
/ 17 сентября 2008

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

Ответы [ 6 ]

41 голосов
/ 18 сентября 2008

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

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

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

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

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

7 голосов
/ 17 сентября 2008

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

код:

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

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

Вы можете нажать тот же адрес с:

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

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

5 голосов
/ 17 сентября 2008

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

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

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

1 голос
/ 17 сентября 2008

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

0 голосов
/ 10 ноября 2008

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

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

0 голосов
/ 19 сентября 2008

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

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

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

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

...