为什么堆栈溢出在 Linux 中不会导致分段错误而是导致堆栈溢出?

22

可能的重复问题:
什么是分段错误和堆栈溢出之间的区别?

我想知道为什么堆栈溢出会导致分段错误而不是堆栈溢出。

这是因为超过了栈限制的范围,导致了SIGSEGV吗?为什么在Linux中我们不会遇到堆栈溢出,而是遇到了分段错误?

int foo()
{
  return foo();
}

这段小代码本应该会导致栈溢出,但实际上在Linux中导致了分段错误。


1
@Justin:又有一个语言问题的人了 :-( 第一个人问两个东西的区别,而我的问题是一个错误如何导致另一个错误。请仔细阅读。 - RajSanpui
2
当您使用gcc时,请查找选项“-fstack-check”。 - Nobody moving away from SE
为什么人们标记它要关闭?难道 Stack Overflow 不是编程的一部分吗? - RajSanpui
2
@kingsmasher1,重复帖子的第一个答案就是您问题的答案。 - Justin M. Keyes
@Justin:另外,如果CPU堆栈寄存器溢出怎么办?你觉得在这种情况下使用SIGSEGV合理吗? - RajSanpui
显示剩余5条评论
2个回答

12

堆栈溢出可能导致几种不同类型的硬件错误。

  • 它可能会尝试访问程序没有适当权限的内存,导致内核为该进程引发 SIGSEGV(段错误)信号。
  • 它可能会尝试执行非法指令(例如:您覆盖了返回地址以指向无效指令),导致内核为该进程引发 SIGILL(非法指令)信号。
  • 在某些平台上可能会引发 SIGBUS(例如:对齐异常)。

所有这些错误都发生在堆栈溢出之后。一种方法是添加堆栈溢出保护(ProPolice 等),以便在它们导致更严重的问题之前捕获堆栈溢出。

编辑:

你指的是“真正的堆栈溢出”。嗯,这种情况已经被 SEGV(尝试访问进程没有权限的内存)所覆盖,因此它会收到 SEGV,而不是特殊处理更通用的 SEGV 的每个单独情况。


1
在Linux中没有像其他平台那样出现“堆栈溢出”的方式(至少对用户而言是这样),因此不能像非法内存访问一样容易区分真正的堆栈溢出。 - RajSanpui
如果您覆盖了与堆栈相邻的其他对象,它也可能导致随机的错误行为。 - Chris Dodd
1
@kingsmasher:您可以查看导致SEGV的地址和/或%esp的值——如果它指向堆栈保护页,则可能存在某种堆栈溢出。总的来说,这不是非常有用的事情,因为除了终止进程外,没有通用的从堆栈溢出恢复的方法。 - Chris Dodd
@Chris:谢谢,这是一个很好的提示来理解。但不幸的是,没有直接的错误信息。 - RajSanpui
@kingsmasher1:嗯,我实际上对所有的SEGV都进行相同的调试,对于堆栈溢出也同样适用:查看有问题的指令,并查看在那里用作内存引用的寄存器。 - ninjalj
@Chris:你确定吗?与 SIGSEGV 的其他原因不同,似乎堆栈溢出通过 longjmp 恢复相对安全,前提是正在执行的代码本身就可以安全地跳出 longjmp - R.. GitHub STOP HELPING ICE

4

Stackoverflow并不是一个错误,它是一种情况,从语言到平台不同,它抛出的错误也会有所不同。

在维基百科上了解更多关于分段错误的信息。

编辑:

更明确地说,在您的情况下,调用堆栈已溢出,程序尝试将下一个调用写入无效地址,导致发生分段错误。


2
但是,当包含的段比堆栈大时,堆栈可能会溢出,尽管尚未发生segfault。 - Nobody moving away from SE
@Nobody:非常好的观点,Nobody。 - RajSanpui
堆栈不在内存中,而是在CPU寄存器中。 - Justin M. Keyes
@Justin:不!CPU 中与堆栈相关的唯一事物是指向堆栈顶部的 ESP(和 EBP)(以及可能从堆栈中加载的值)。或者你是指浮点堆栈吗? - Nobody moving away from SE
@Justin M. Keyes - 大多数CPU的寄存器不足以容纳最小程序的调用栈。 - MByD
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接