linux内核如何避免堆栈覆盖文本(指令)



我很好奇内核是如何防止堆栈过大的,我发现了这个Q/A:

Q:linux内核如何强制执行堆栈大小限制?

A: 由于虚拟内存,内核可以控制这一点。虚拟内存(也称为内存映射(,基本上是一个虚拟的列表存储器区域(基本+大小(和目标物理存储器区域内核可以对每个程序唯一的内容进行操作。当程序试图访问不在此列表中的地址发生异常。此异常将导致上下文切换到内核模式。内核可以查找故障。如果内存是在程序生效之前,它将被放置到位continue(例如,交换和mmap尚未从磁盘读取(或可以生成SEGFAULT。

为了决定堆栈大小的限制,内核只需操作虚拟内存映射。-Stian Skjelstad

但我觉得这个答案不太令人满意"当程序试图访问不在此列表中的地址时,会发生异常"-但是程序的文本部分(指令(难道不是虚拟内存映射的一部分吗?

我在问内核如何强制用户程序的堆栈大小。

主堆栈有一个用ulimit -s设置的增长限制,它将阻止堆栈接近.text。(以及下面的保护页,如果堆栈溢出超过增长限制,则确保存在segfault。(请参阅使用';推';或者';sub';x86指令?。(或者对于线程堆栈(而不是主线程(,堆栈内存只是一个正常的mmap分配,没有增长;唯一的惰性分配是支持虚拟页面的物理页面。(

此外,.text是可执行文件的read+exec映射,因此如果不首先调用mprotect,就无法修改它。(这是一个私有映射,所以这样做只会影响内存中的页面,而不会影响实际文件。这就是文本重定位的工作方式:绝对地址的运行时修复,由动态链接器修复。(

限制增长的实际机制是当进程在现有堆栈区域下方触发堆栈指针的硬件页面故障时,不扩展映射并分配新页面因此,页面错误是无效的,而不是正常堆栈增长情况下的软错误(也称为次要错误(,因此会传递SIGSEGV。


如果程序使用未检查大小的alloca或C99 VLA,恶意输入可能会使其跳过任何保护页,进入其他读/写映射,如.data或动态分配的东西。

为了强化有缺陷的代码,使其分段故障,而不是真正允许堆栈冲突攻击,有一些编译器选项可以使其在堆栈增长时接触到每一个中间页面,因此肯定会引发";"绊网";以低于堆栈增长限制的未映射保护页的形式。请参阅Linux进程堆栈被本地变量溢出(堆栈保护(

如果您设置了ulimit -s unlimited,您可以将堆栈扩展到其他映射中吗?如果Linux在这种情况下确实允许无限增长,而在接近另一个映射时不保留保护页。

最新更新