gpt4 book ai didi

c - Linux进程堆栈被局部变量溢出(堆栈保护)

转载 作者:行者123 更新时间:2023-12-04 21:01:16 25 4
gpt4 key购买 nike

What is the purpose of the _chkstk() function?:

At the end of the stack, there is one guard page mapped as inaccessible memory -- if the program accesses it (because it is trying to use more stack than is currently mapped), there's an access violation.



_chkstk() 是一个特殊的编译器-帮助器函数,

ensures that there is enough space for the local variables



即它正在做一些堆栈探测(这是 LLVM example)。
这种情况是Windows特定的。因此Windows有解决该问题的方法。

让我们考虑一下Linux(或其他类似Unix)下的类似情况:我们有很多函数的局部变量。第一个堆栈变量访问位于堆栈段的后面(例如 mov eax, [esp-LARGE_NUMBER],此处esp-LARGE_NUMBER位于堆栈段的后面)。是否有任何功能可以防止可能的页面错误,或者在Linux(也许其他与Unix类似)或开发工具(如 等)中具有任何功能? -fstack-check( GCC stack checking)是否可以解决此问题? This answer表示它与 _chkstk()非常相似。

P.S.这些帖子 12并没有太大帮助。

P.P.S.总的来说,问题在于操作系统(最重要的 Linux 与Windows)在 与大量堆栈变量进行挣扎的的方法之间的实现差异,这些方法在堆栈段之后。添加了C++和C标签,因为它与Linux native 二进制生成有关,但是汇编代码与编译器相关。

最佳答案

_chkstk会进行堆栈探测以确保在(可能)较大的分配(例如, alloca 。因为Windows一次最多只能将堆栈增加一页,直到堆栈大小限制。

触摸该“防护页”将触发堆栈增长。它不能防止堆栈溢出。我认为您在这种用法中误解了“保护页”的含义。

函数名称也可能会引起误解。 _chkstk 文档只是说:当您的函数中有多于一页的局部变量时,由编译器调用。它并没有真正检查任何内容,它只是确保在使用esp/rsp周围的内存之前,已触摸中间页面。即,唯一可能的影响是:没有任何结果(可能包括有效的软页面错误)或堆栈溢出时的无效页面错误(试图触摸Windows拒绝使堆栈包括在内的页面)。通过无条件地编写它们来分配。

我想您可以将其视为检查堆栈冲突,方法是在继续发生堆栈溢出的情况下确保先触摸不可映射的页面,然后再继续操作。

当您触摸旧堆栈页面下方的内存(如果它位于当前堆栈指针上方)时,Linux将在任意数量的页面上扩展主线程stack1(最大为ulimit -s设置的堆栈大小限制;默认为8MiB)

如果您在增长限制之外触摸内存,或者不先移动堆栈指针,则只会出现段错误。 因此,Linux不需要堆栈探针,仅需将堆栈指针移动所需保留的字节数即可。编译器知道这一点并相应地发出代码。

另请参阅How is Stack memory allocated when using 'push' or 'sub' x86 instructions?,以获取有关Linux内核功能以及Linux上的glibc pthreads功能的更多低级详细信息。

在Linux上,足够大的alloca可以将堆栈一直移动到堆栈增长区域的底部,超出其下方的保护页面,并进入另一个映射;这是堆栈冲突。 https://blog.qualys.com/securitylabs/2017/06/19/the-stack-clash当然,它要求程序根据用户输入为alloca使用可能很大的大小。 mitigation for CVE-2017-1000364将离开1MiB保护区域,比正常情况下需要更大的分配空间才能通过保护页面。

该1MiB保护区低于ulimit -s(8MiB)增长限制,但不低于当前堆栈指针。它与Linux的常规堆栈增长机制是分开的。
gcc -fstack-check
gcc -fstack-check的效果与Windows (MSVC通过调用_chkstk进行操作)始终需要的效果基本相同:在将其移动较大或运行时可变量时,触摸上一个和新堆栈指针之间的堆栈页面。

但是这些探针的目的/好处在Linux上是不同的。在GNU/Linux上的无错误程序中,它永远不需要正确性。它“仅”防御堆栈冲突错误/漏洞。

在x86-64 GNU/Linux上,gcc -fstack-check将(对于具有VLA或大型fixe-size数组的函数)添加一个循环,该循环不会将or qword ptr [rsp], 0sub rsp,4096一起堆叠探针。对于已知的固定阵列大小,它可以只是一个探针。代码生成看起来效率不高。通常从未在此目标上使用过。 ( Godbolt 编译器资源管理器示例,该示例将堆栈数组传递给非内联函数。)

https://gcc.gnu.org/onlinedocs/gccint/Stack-Checking.html描述了一些GCC内部参数,这些参数控制-fstack-check的功能。

如果您想要绝对安全的堆栈冲突攻击,则应该这样做。但是,对于正常操作而言并不需要它,并且1MiB保护页对于大多数人来说就足够了。

请注意,-fstack-protector-strong完全不同,它可以防止本地数组上的缓冲区溢出来覆盖返回地址。 与堆栈冲突无关,攻击的对象是小型本地数组上方的堆栈中已存在的东西,而不是通过大量移动堆栈来攻击其他内存区域。

脚注1:Linux上的线程堆栈(用于最初线程以外的线程)必须预先分配好,因为不可思议的增长功能不起作用。只有进程的初始主线程才能拥有该线程。

(有一个mmap(MAP_GROWSDOWN)功能,但它并不安全,因为没有限制,并且因为没有其他阻止动态分配的操作会随机选择关闭当前堆栈下方的页面,从而在堆栈冲突之前将将来的增长限制在很小的范围内。还因为它只会在以下情况下增长您触摸防护页面,因此将需要堆栈探针。出于这些原因, MAP_GROWSDOWN不用于线程堆栈。主堆栈的内部机制依赖于内核中的不同魔术,它可以防止其他分配窃取空间)

关于c - Linux进程堆栈被局部变量溢出(堆栈保护),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60058873/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com