硬件或软件是否检测到堆栈溢出?


26

是软件(操作系统)的任务是检测堆栈溢出,还是在硬件中检测到堆栈溢出,从而导致CPU异常?


我要说的是硬件,通过大多数CPU上的MMU故障。32位模式下的x86除分页外还具有分段功能,并且关联了“堆栈分段”,例如具有基地址和大小的Code,Data,Ext ...等分段。任何超出该范围的访问都会触发故障。
TEMLIB

Answers:


25

它可以是软件,也可以是硬件,也可以是两者,或者都不是。

有两种溢出:增长堆栈时(进入函数时)溢出,以及访问堆栈上的数组时溢出。可以通过对函数入口进行边界检查来检测扩展堆栈时的溢出,以验证是否有足够的空间(如果没有错误,则发出错误信号或增大堆栈)。在访问堆栈中的数组时,溢出仅是在不验证数组边界的低级语言中的一个问题。解决方案是验证数组边界。

这些软件方法的优势在于它们可以完全可靠地工作:您可以确保检测到任何堆栈溢出。它们的缺点是它们增加了代码大小和执行时间。只要没有溢出发生,硬件就可以通过提供一种免费检测大多数溢出的方法来提供帮助。在具有MMU¹的体系结构上,运行时环境可以安排将堆栈映射到页面边界,而下一页面保持未映射状态。

+---------------+---------------+---------------+---------------+
| stack                         | unmapped      | other stuff   |
|    ----> direction of growth  |               |               |
+---------------+---------------+---------------+---------------+
^               ^               ^               ^               ^  page boundaries

这样,如果软件尝试访问超出页面边界的数据(无论是因为堆栈指针已移出边界还是因为数组访问超出边界并超出边界),它将通过访问未映射的区域而导致错误。这仅在溢出量足够小的情况下才会发生:如果溢出量太大,则程序可能最终访问地址空间中间隙另一侧的其他内容。

硬件方法的缺点是它不完全可靠,因为可能无法检测到大量的溢出,并且它无法检测到可寻址空间内残留的阵列溢出。

为了检测数组溢出,另一种软件技术是canary:在堆栈的顶部或帧之间放置一个特殊值,并检查函数返回时canary值没有改变。这也是一种不完善的技术,因为溢出可能完全避开了金丝雀,或者由于在检查时已经恢复了金丝雀值而可能无法检测到。尽管如此,使难以利用某些安全漏洞还是很有用的。

避免堆栈溢出的最安全,最便宜的方法是,通过静态分析来计算程序在开始执行之前所需的堆栈量。但是,这并不总是可行的:程序所需的堆栈量通常是不确定的,并且取决于程序处理的数据。

¹ 如果只有一个线程的堆栈位于现有物理映射的边缘,则仅在MPU或没有内存保护的情况下也可以应用相同的原理。


4
即使阵列在堆栈上,我也不会调用越界数组访问堆栈溢出。这只是缓冲区溢出的一种特殊情况。
CodesInChaos

不分配给相对于非堆栈/帧指针或非恒定偏移量引用的主堆栈项,将避免传统堆栈溢出。使用单独的返回地址堆栈(或类似Itanium的RA /寄存器溢出堆栈)将至少大大减少面向返回的编程机会。
Paul A. Clayton

越界访问更恰当地称为溢出,而不是溢出。
Ben Voigt,2015年

3
@ awesomebing1并非每个平台都能检测到堆栈溢出。您可能最终只会覆盖堆栈之后的内容(类似于任何其他缓冲区溢出)。
user253751

1
@CodesInChaos,有时在堆栈分配的缓冲区中的缓冲区溢出称为“堆栈溢出”(简称)。当然,如果没有上下文,该术语可能会有些混乱。
DW
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.