【问题标题】:Does a stack overflow always lead to a segmentation fault error?堆栈溢出是否总是导致分段错误错误?
【发布时间】:2020-01-07 16:24:42
【问题描述】:

在 Linux 中,当正在运行的程序尝试使用超过限制的堆栈空间(堆栈溢出)时,通常会导致“分段错误”错误并中止执行。

是否保证超过堆栈空间限制总是会导致分段错误错误?或者程序是否会继续运行,可能由于数据已损坏而出现一些错误行为?

另一种说法:如果程序行为不端,产生错误的结果但没有崩溃,原因仍然是堆栈溢出吗?

编辑:澄清一下,这个问题不是关于“堆栈缓冲区溢出”,而是关于堆栈溢出,当程序使用的堆栈空间超过堆栈大小限制(Linux 中的限制由ulimit -s )。

【问题讨论】:

  • 至少理论上 posix 说没有办法:RLIMIT_STACK .... If this limit is exceeded, SIGSEGV shall be generated ...。但实践就是实践。

标签: linux segmentation-fault runtime-error stack-overflow


【解决方案1】:

堆栈溢出变成访问冲突需要某种内存管理硬件。如果没有硬件辅助的内存保护,过度增长的堆栈将与其他一些内存分配发生冲突,从而导致相互损坏。

在按需分页的虚拟内存操作系统上,堆栈的上限受 保护页面 保护:保留(不会分配给任何东西)并标记的虚拟内存页面“不存在”,以便访问它会产生违规行为。保护页只有这么多字节宽;堆栈指针仍然可能意外地在保护页面上递增并落在一些不相关的可写内存中(例如属于堆分配的映射),在那里将造成严重破坏而不必触发任何内存访问冲突。

在 C 语言中,我们可以通过声明大的、未初始化的非static 块范围数组(如char array[8192]; // (twice as large as a 4096 byte guard page))来轻松地导致大的堆栈增量。使用 alloca 或 C99 可变长度数组之类的功能,我们可以动态地做到这一点:我们可以编写一个程序,它读取一个整数值作为运行时输入,并将堆栈增加这么多。

多年前我调试了一个问题,第三方代码有调试日志记录宏,在其扩展中有一个临时数组,如char print_buf[8192],用于格式化消息。这用于具有许多线程的多线程应用程序,其堆栈大小减少到仅 64 KB。多亏了这个print_buf,一个线程溢出的堆栈直接跳过了保护页,并降落在另一个线程的堆栈中,破坏了它的局部变量,导致了众所周知的“热闹”。

【讨论】:

  • 此外,堆栈溢出可能会被用来破坏应用程序的安全性,这是另一种不一定会导致段错误的情况。
  • 太棒了!如果我使用的是一些常见的 Linux 变体,比如 Fedora 或 Ubuntu 之类的,那是不是一个按需分页的虚拟内存操作系统,以便使用你描述的保护页面保护?
  • 是的,它被使用了。甚至还有一个关于保护页面“小”的 CVE - cvedetails.com/cve/CVE-2017-1000364。 LWN 上也讨论了这个问题 - lwn.net/Articles/725832
猜你喜欢
  • 2019-12-08
  • 2011-10-22
  • 2011-10-30
  • 2011-02-08
  • 2012-05-21
  • 2020-05-04
  • 2017-09-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多