代码之家  ›  专栏  ›  技术社区  ›  Jeremy Friesner

堆栈溢出是安全漏洞吗?

  •  7
  • Jeremy Friesner  · 技术社区  · 15 年前

    注意:这个问题涉及堆栈溢出(考虑无限递归),而不是缓冲区溢出。

    我知道有人可能会导致堆栈溢出而导致进程崩溃,但他们可以注入代码吗?或者c运行时是否检测到堆栈溢出情况并干净地中止?

    只是好奇。。。

    5 回复  |  直到 15 年前
        1
  •  4
  •   Donal Fellows    15 年前

    快速刷新

    首先,您需要了解现代操作系统中的基本保护单元是进程和内存页。进程是内存保护域;它们是操作系统执行安全策略的级别,因此它们与正在运行的程序强烈对应。(如果没有,可能是因为程序在多个进程中运行,或者是因为程序在某种框架中共享;后一种情况有可能是安全有趣的,但那是‘另一回事’)虚拟内存页是硬件应用安全规则的级别;进程内存中的每一页都有一些属性,这些属性决定了进程可以对该页执行什么操作:它是否可以读取该页,它是否可以写入该页,以及它是否可以在该页上执行程序代码(尽管第三个属性很少使用,也许它应该使用)。编译后的程序代码被映射到内存中,生成可读和可执行但不可写的页面,而堆栈应该可读和可写,但不可执行。 大多数内存页根本不可读、不可写或不可执行; 操作系统只允许一个进程使用它明确要求的所有页面,这就是内存分配库( malloc() 等)帮你搞定。

    分析

    如果每个堆栈帧小于内存页 [一] 因此,当程序通过堆栈前进时,它会写入每个页面,操作系统(即运行时的特权部分)至少在原则上可以可靠地检测堆栈溢出,并在发生这种情况时终止程序。基本上,执行此检测的所有操作都是在堆栈的末尾有一个程序无法写入的页;如果程序试图写入该页,内存管理硬件将捕获该页,操作系统将有机会进行干预。

    alloca() 在将控制权返回给程序之前,总是写入它们分配的空间,从而检测到崩溃的堆栈,这将在速度上以一定的代价阻止第一个堆栈,尽管写入可以相当稀疏,以保持开销相当小。)

    后果

    这有什么后果?好吧,操作系统必须在内存管理方面做正确的事情。(@Michael's link演示了出错时可能发生的情况。)但让攻击者确定内存分配大小也是危险的,如果不立即强制写入整个分配; alloca 而C99可变尺寸阵列是一个特殊的威胁。此外,我更怀疑C++代码,因为它倾向于做更多的基于堆栈的内存分配;它可能是好的,但是事情出错的可能性更大。

    就我个人而言,我更喜欢保持堆栈大小和堆栈帧大小较小,并在堆上执行所有可变大小的分配。在某种程度上,这是在某些类型的嵌入式系统上工作的遗留问题,并且使用了使用大量线程的代码,但这确实使防止堆栈溢出攻击变得简单得多;操作系统可以可靠地捕获它们,然后攻击者所拥有的只是拒绝服务(令人讨厌,但很少致命)。我不知道这是否是所有程序员的解决方案。


    典型的页面大小:32位系统为4kB,64位系统为16kB。检查您的系统文档以了解它在您的环境中是什么。

        2
  •  3
  •   Steve Rowe    15 年前

    大多数系统(如Windows)在堆栈溢出时退出。我认为你不太可能在这里看到安全问题。至少,不是特权安全问题的提升。您可能会遇到一些拒绝服务问题。

        3
  •  2
  •   Tony Delroy    15 年前

    值得一提的是,使用计数器设置递归深度的任意但宽泛的限制通常也相当容易:如果是单线程的,则可以使用静态局部变量,或者使用最终参数(如果语言允许,则可以方便地默认为0,否则,第一次使用外部调用方提供0)。

        4
  •  1
  •   Gábor Lipták    15 年前

    是的。有一些算法可以避免递归性。例如,在case算术表达式中 reverse polish notation 使您能够避免递归性。背后的主要思想是改变原来的表达方式。可能有一些算法也能帮助你。

    堆栈溢出的另一个问题是,如果错误处理不适当,则可能导致任何问题。以Java为例解释 StackOverflowError 是个错误,如果有人抓到 Throwable ,这是一个常见的错误。因此,在堆栈溢出的情况下,错误处理是一个关键问题。

        5
  •  0
  •   Michael F    15 年前

    是的,是的。可用性是安全性的一个重要方面,这一点通常被忽略。

    编辑

    作为现代开放源码软件中安全意识不被理解的一个例子,看看一个相对新发现的 vulnerability