关于linux:堆栈上的segfault溢出

关于linux:堆栈上的segfault溢出

Segfault on stack overflow

为什么Linux内核在堆栈溢出时生成segfault?当c或alltran中的临时数组创建溢出时,这会使调试变得很尴尬。当然,运行时肯定有可能产生更有用的错误。


您实际上可以使用信号处理程序来捕获堆栈溢出的条件。

为此,您必须做两件事:

  • 使用sigaction为SIGSEGV(segfault)设置信号处理程序,为此设置SO_ONSTACK标志。这指示内核在传递信号时使用备用堆栈。

  • 调用sigaltstack()设置SIGSEGV的处理程序将使用的备用堆栈。

然后,当您使堆栈溢出时,内核将在传递信号之前切换到备用堆栈。进入信号处理程序后,您可以检查导致错误的地址,并确定是堆栈溢出还是常规错误。


"内核"(实际上不是运行代码的内核,是CPU)不知道您的代码是如何引用不应接触的内存的。它只知道您尝试这样做。

代码:

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

当您尝试访问x的边界之外时,

不能真正被CPU评估。

您可能会使用以下相同的地址:

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

顺便说一句,我不鼓励使用alloca,因为堆栈比堆更受限制(请使用malloc代替)。


堆栈溢出是分段错误。就像您打破了最初分配给您的给定内存范围一样。有限大小的堆栈,您已经超过了它。您可以在Wikipedia

上阅读有关此内容的更多信息。

此外,我过去对项目所做的一件事是将自己的信号处理程序编写到segfault(请参见手册页信号(2))。我通常会捕获信号,然后在控制台上写出"发生致命错误"。我做了其他一些检查点标记和调试的工作。

为了调试段错误,您可以在GDB中运行一个程序。例如,以下C程序将出现段错误:
#segfault.c
#包括
#include

1
2
3
4
5
6
7
8
int main()
{
        printf("Starting\
");
        void *foo=malloc(1000);
        memcpy(foo, 0, 100); //this line will segfault
        exit(0);
}

如果我这样编译它:

1
gcc -g -o segfault segfault.c

,然后像这样运行它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ 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行存在分段错误。当然,有更复杂的方法来处理堆栈溢出和其他内存错误,但这足够了。


只需使用Valgrind。它会以极高的精确度指出您所有的内存分配错误。


一些注释是有帮助的,但是问题不在于内存分配错误。那是没有错误的代码。在fortran中,运行时会在堆栈上分配临时值,这很麻烦。因此,例如
写(fp)x,y,z
可以触发没有错误的段错误。对Intel Fortran编译器的技术支持说,运行时库无法打印出更有用的消息。但是,如果米格尔(Miguel)是对的,那么他将建议这样做。非常感谢。接下来剩下的问题是我该如何首先找到seg错误的地址,并弄清它是否来自堆栈溢出或其他问题。

对于其他发现此问题的人,有一个编译器标志,该标志将临时变量放在堆上一定大小以上。


堆栈溢出不一定会导致崩溃。它可能会悄无声息地破坏程序的数据,但会继续执行。

我不会使用SIGSEGV处理程序错误,而是要解决原始问题。

如果需要自动帮助,可以使用gcc的-Wstack-protector选项,该选项将在运行时发现一些溢出并中止程序。

valgrind适用于动态内存分配错误,但不适用于堆栈错误。


推荐阅读

    linux下进程调试命令?

    linux下进程调试命令?,系统,工作,软件,信息,命令,基础,地址,状态,进程,实时,L

    linux查看信号值命令?

    linux查看信号值命令?,时间,手机,检测,信息,工作,系统,软件,市场,命令,标准,L

    linux命令行调试代码?

    linux命令行调试代码?,环境,代码,信息,平台,程序,编辑,版本,步骤,体系结构,

    linux下单步调试命令?

    linux下单步调试命令?,信息,系统,代码,工程,地址,工具,工作,数据,管理,环境,l

    linux串口调试命令?

    linux串口调试命令?,设备,数据,信息,数字,系统,标准,通讯,软件,通用,状态,lin

    linux查看信号表命令?

    linux查看信号表命令?,网络,地址,信息,系统,电脑,命令,数字,通信,服务,信号,l

    linux保留堆栈命令?

    linux保留堆栈命令?,地址,工作,系统,信息,管理,命令,目录,代码,名称,连续,lin

    linux创建数组命令?

    linux创建数组命令?,地址,工作,系统,信息,命令,代码,目录,情况,标准,文件,Lin

    linux调试终端命令?

    linux调试终端命令?,系统,工作,地址,首页,电脑,终端,命令,标准,信息,基础,求L

    linux脚本调试的命令?

    linux脚本调试的命令?,工作,系统,管理,命令,地址,标准,脚本,底部,代码,官网,l

    linux命令调试模式?

    linux命令调试模式?,系统,工作,信息,地址,工程,命令,工具,环境,设备,地方,lin

    linux调试驱动的命令?

    linux调试驱动的命令?,系统,网络,官网,百度,地址,下来,第一,官方网站,软件,

    linux命令调试过程?

    linux命令调试过程?,代码,通用,地方,信息,系统,程序,进程,命令,编辑,断点,如

    python调试的几种方式

    python调试的几种方式,代码,位置,信息,状态,培训,数据,分析,变量,函数,方式

    Python 信号量对象

    Python 信号量对象,时间,培训,计数器,对象,机制,内部,参数,线程,剩余,数量,

    Python 数组ndarray

    Python 数组ndarray,数据,一致,培训,项目,数组,元素,类型,数据类型,复数,坐

    python 参数组合

    python 参数组合,位置,参数,培训,关键字,函数,定义,顺序,后面,形式,以上,在P