堆栈溢出时出现段错误

发布于 2024-07-04 20:55:20 字数 94 浏览 13 评论 0原文

为什么Linux内核会在堆栈溢出时产生段错误? 当用 c 或 fortran 创建临时数组时发生分配溢出时,这会使调试变得非常困难。 当然,运行时可能会产生更有用的错误。

Why does the linux kernel generate a segfault on stack overflow? This can make debugging very awkward when alloca in c or fortran creation of temporary arrays overflows. Surely it mjust be possible for the runtime to produce a more helpful error.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

无人接听 2024-07-11 20:55:20

实际上,您可以使用信号处理程序捕获堆栈溢出的情况。

为此,您必须执行两件事:

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

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

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

You can actually catch the condition for a stack overflow using signal handlers.

To do this, you must do two things:

  • Setup a signal handler for SIGSEGV (the segfault) using sigaction, to do this set the SO_ONSTACK flag. This instructs the kernel to use an alternative stack when delivering the signal.

  • Call sigaltstack() to setup the alternate stack that the handler for SIGSEGV will use.

Then when you overflow the stack, the kernel will switch to your alternate stack before delivering the signal. Once in your signal handler, you can examine the address that caused the fault and determine if it was a stack overflow, or a regular fault.

指尖微凉心微凉 2024-07-11 20:55:20

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

代码:

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

当您尝试访问 x 范围之外时,CPU 无法真正对其进行评估。

您可能会使用以下命令访问完全相同的地址:

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

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

The "kernel" (it's actually not the kernel running your code, it's the CPU) doesn't know how your code is referencing the memory it's not supposed to be touching. It only knows that you tried to do it.

The code:

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

can't really be evaluated by the CPU as you trying to access beyond the bounds of x.

You may hit the exact same address with:

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

BTW, I would discourage the use of alloca since stack tends to be much more limited than heap (use malloc instead).

望她远 2024-07-11 20:55:20

有些评论很有帮助,但问题不在于内存分配错误。 也就是说代码没有错误。 这在 Fortran 中是相当麻烦的,因为运行时在堆栈上分配临时值。 因此,诸如这样的命令
写(fp)x,y,z
可以触发段错误且没有警告。 intel Fortran 编译器的技术支持表示,运行时库无法打印更有用的消息。 然而,如果米格尔是对的,那么正如他所建议的那样,这应该是可能的。 非常感谢。 剩下的问题是我如何首先找到段错误的地址,并确定它是否来自堆栈溢出或其他问题。

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

Some of the comments are helpful, but the problem is not of memory allocation errors. That is there is no mistake in the code. It's quite a nuisance in fortran where the runtime allocates temporary values on the stack. Thus a command such as
write(fp)x,y,z
can trigger are segfault with no warning. The technical support for the intel Fortran compiler say that there is no way that the runtime library can print a more helpful message. However if Miguel is right than this should be possible as he suggests. So thanks a lot. The remaining question then is how do I firstly find the address of the seg fault and the figure out if it came from a stack overflow or some other problem.

For others who find this problem there is a compiler flag which puts temporary varibles above a certain size on the heap.

落叶缤纷 2024-07-11 20:55:20

只需使用Valgrind。 它会极其精确地指出你所有的内存分配错误。

Simply use Valgrind. It will point out all your memory allocation mistakes with excruciating preciseness.

只想待在家 2024-07-11 20:55:20

堆栈溢出并不一定会导致崩溃。 它可能会默默地丢弃程序的数据,但会继续执行。

我不会使用 SIGSEGV 处理程序拼凑,而是修复原始问题。

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

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

A stack overflow does not necessarily yield a crash. It may silently trash data of your program but continue to execute.

I wouldn't use SIGSEGV handler kludges but instead fix the original problem.

If you want automated help, you can use gcc's -Wstack-protector option, which will spot some overflows at runtime and abort the program.

valgrind is good for dynamic memory allocation bugs, but not for stack errors.

饮惑 2024-07-11 20:55:20

堆栈溢出是分段错误。 就像您打破了最初分配的给定内存边界一样。 堆栈的大小有限,而您已经超出了它。 您可以在 wikipedia 阅读更多相关信息

此外,我过去为项目做过一件事是将我自己的信号处理程序写入段错误(请参阅手册页信号(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 行存在分段错误。当然,还有更复杂的方法来处理堆栈溢出和其他内存错误,但这会够了。

A stack overflow is a segmentation fault. As in you've broken the given bounds of memory that the you were initially allocated. The stack of of finite size, and you have exceeded it. You can read more about it at wikipedia

Additionally, one thing I've done for projects in the past is write my own signal handler to segfault (look at man page signal (2)). I usually caught the signal and wrote out "Fatal error has occured" to the console. I did some further stuff with checkpoint flags, and debugging.

In order to debug segfaults you can run a program in GDB. For example, the following C program will segfault:
#segfault.c
#include
#include

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

If I compile it like so:

gcc -g -o segfault segfault.c 

and then run it like so:

$ 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) 

I find out from GDB that there was a segmentation fault on line 8. Of course there are more complex ways of handling stack overflows and other memory errors, but this will suffice.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文