使调用堆栈向上增长会使缓冲区溢出更安全吗? [英] Would having the call stack grow upward make buffer overruns safer?

查看:49
本文介绍了使调用堆栈向上增长会使缓冲区溢出更安全吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每个线程都有自己的堆栈来存储局部变量.但是,在调用函数时,堆栈还用于存储返回地址.

Each thread has its own stack to store local variables. But stacks are also used to store return addresses when calling a function.

在x86程序集中, esp 指向堆栈中最近分配的末尾.如今,大多数CPU的堆栈增长都为负数.此行为通过溢出缓冲区并覆盖保存的返回地址来启用任意代码执行.如果堆栈要积极增长,那么这样的攻击将是不可行的.

In x86 assembly, esp points to the most-recently allocated end of the stack. Today, most CPUs have stack grow negatively. This behavior enables arbitrary code execution by overflowing the buffer and overwriting the saved return address. If the stack was to grow positively, such attacks would not be feasible.

使调用堆栈向上增长是否更安全?为什么英特尔在堆栈向下增长的情况下设计8086?他们是否可以在以后的任何CPU中进行更改,以使现代x86的堆栈向上增长?

Is it safer to have the call stack grow upwards? Why did Intel design 8086 with the stack growing downward? Could they have changed things in any later CPUs to let modern x86 have stacks that grow upwards?

推荐答案

有趣的地方;大多数缓冲区溢出都超过了结束时间,而不是开始之前,因此几乎可以肯定会有所帮助.编译器可以将本地数组放在堆栈帧的最高地址处,因此在数组之后不会覆盖任何标量本地变量.

Interesting point; most buffer overruns do go past the end, not before the beginning, so this would almost certainly help. Compilers could put local arrays at the highest address in a stack frame, so there wouldn't be any scalar locals to overwrite located after an array.

但是,如果将本地数组的地址传递给另一个函数,仍然存在危险.因为被调用函数的返回地址将位于数组的末尾.

There's still danger if you pass the address of a local array to another function, though. Because the return address of the called function would be located just past the end of the array.

unsafe() {
    char buf[128];
    gets(buf);      // stack grows upward: exploit happens when gets executes `ret`
    // stack grows down: exploit happens when the `ret` at the end of *this* function executes.
}

因此可能仍有很多个缓冲区溢出.仅当内联不安全的数组写入代码时,此思想才可以消除缓冲区溢出,因此,溢出不会在数组上方发生任何重要的事情.

So probably a lot of buffer overruns will still be possible. This idea only defeats buffer overruns when the unsafe array-writing code is inlined, so the overrun happens with nothing important above the array.

但是,可以很容易地内联一些其他导致缓冲区溢出的常见原因,例如 strcat .向上增长的堆栈有时会有所帮助.

However, some other common causes of buffer overruns can easily be inlined, like strcat. Upward growing stacks will help sometimes.

安全措施不一定要万无一失才能有用,因此有时肯定会有所帮助.对于任何想更改x86之类的现有体系结构的人来说,可能还不够,但是对新体系结构一个有趣的想法.不过,Stack-grows-down是CPU中几乎通用的标准.有什么东西使用向上增长的调用堆栈吗?实际上有多少软件取决于该假设?希望不会...

Security measures don't have to be foolproof to be useful, so this would definitely help sometimes. Probably not enough for anyone to want to change an existing architecture like x86, but an interesting idea for new architectures. Stack-grows-down is a nearly universal standard in CPUs, though. Does anything use an upward-growing call stack? How much software actually depends on that assumption? Hopefully not much...

传统布局为堆和/或堆增加了空间,仅当它们在中间相遇时才引起问题.

可预测的代码/数据地址比可预测的堆栈地址更重要,因此具有更多RAM的计算机可以使堆栈远离数据/代码,同时仍将代码/数据加载到恒定地址.(这非常麻烦.我认为自己很幸运,没有编写实际的16位程序,只了解但没有使用过分段.也许还记得DOS的人可以在这里阐明为什么拥有DOS可以很好地工作的原因.堆栈位于高位地址,而不是段底部向上增长的堆栈,数据/代码位于顶部(例如,采用微小"代码模型,其中所有内容都在一个段中).

Predictable code/data addresses are more important than predictable stack addresses, so a computer with more RAM could put the stack farther from data/code, while still loading code/data at a constant address. (This is very hand-wavy. I consider myself lucky not to have written actual 16-bit programs, and only learned about but not used segmentation. Perhaps someone that still remembers DOS can shed some light here on why it works well to have the stack at a high address, instead of an upward-growing stack at the bottom of your segment and data/code at the top. e.g. with a "tiny" code model where everything is in one segment).

更改此行为的唯一真正机会是使用AMD64 ,这是x86首次真正打破向后兼容性.现代的Intel CPU仍支持8086未记录的操作码,例如 D6 :SALC (从进位标志设置AL),从而限制了ISA扩展的编码空间.(例如,如果 SSSE3和SSE4指令要短1个字节放弃了对未记录的操作码的支持.

The only real chance to change this behaviour was with AMD64, which is the first time x86 has ever really broken backwards compatibility. Modern Intel CPUs still support 8086 undocumented opcodes like D6: SALC (Set AL from Carry Flag), limiting the coding space for ISA extensions. (e.g. SSSE3 and SSE4 instructions would be 1 byte shorter if Intel dropped support for undocumented opcodes.

即使那样,也仅适用于新模式;AMD64 CPU仍必须支持传统模式,并且在64位模式下,它们必须将长模式与兼容模式混合使用(通常是从32位二进制文​​件运行32位用户空间进程).

Even then, it would only be for the new mode; AMD64 CPUs still have to support legacy mode, and when in 64-bit mode they have to mix long mode with compat mode (usually to run 32-bit user-space processes from 32-bit binaries).

AMD64可能已经添加了堆栈方向标志,但这会使硬件更加复杂.如上所述,我认为这对安全性不会有太大好处.否则,也许AMD架构师会考虑这一点,但仍然不太可能.他们的目标绝对是最小限度的侵入,并且不确定它是否会流行.如果世界上大多数人只是继续运行32位OS和32位代码,他们就不想再为维护CPU的AMD64兼容性而费劲了.

AMD64 could maybe have added a stack-direction flag, but that would have made the hardware more complex. As I argued above, I don't think it would have been a big benefit for security. Otherwise, perhaps AMD architects would have considered it, but still unlikely. They were definitely aiming for minimally intrusive, and weren't sure it would catch on. They didn't want to be stuck with extra baggage to maintain AMD64 compatibility in their CPUs if the world mostly just kept running 32-bit OSes and 32-bit code.

这太可惜了,因为他们可以做很多次要的事情,而在执行单元中可能并不需要太多的晶体管.(例如,在长模式下,将 setcc r/m8 替换为 setcc r/m32 ).

That's a shame, because there are a lot of minor things they could have done that would probably not have required too many extra transistors in the execution units. (e.g. in long mode, replace setcc r/m8 with setcc r/m32).

这篇关于使调用堆栈向上增长会使缓冲区溢出更安全吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆