堆栈红色区域的实际大小是多少? [复制]
Posted
技术标签:
【中文标题】堆栈红色区域的实际大小是多少? [复制]【英文标题】:What is the actual size of stack red zone? [duplicate] 【发布时间】:2019-11-16 19:41:09 【问题描述】:在x86-64 System V ABI 中指定$rsp - 128
后面的空间是所谓的红色区域,没有被任何信号处理程序触及。在我的机器上
$ ulimit -s
8192
我预计堆栈中只有 2 页。所以我写了下面的程序来测试红色区域可以扩大到多大:
PAGE_SIZE equ 0x1000
SYS_exit equ 0x3C
section .text
global _start
_start:
lea rcx, [rsp - 0x1f * PAGE_SIZE]
mov rax, rsp
loop:
sub rax, PAGE_SIZE
mov qword [rax], -1
cmp rax, rcx
jne loop
mov rax, SYS_exit
mov rdi, 0x20
所以我预计程序总是会失败。但该程序有时会因SEGV
而失败,有时完成得很好。
行为与MAP_GROWSDOWN
记录的完全一样:
此标志用于堆栈。它向内核表示虚拟 映射应该在内存中向下扩展的内存系统。这 返回地址比实际的内存区域低一页 在进程的虚拟地址空间中创建。触摸地址 映射下方的“守卫”页面将导致映射增长 页面。这种增长可以重复,直到映射增长到 在下一个较低映射的高端的一页内,其中 点触“守卫”页面将产生
SIGSEGV
信号。
正如所讨论的,使用MAP_GROWSDOWN
和PROT_GROWSDOWN
创建的in this question 映射不会以这种方式增长:
volatile char *mapped_ptr = mmap(NULL, 4096,
PROT_READ | PROT_WRITE | PROT_GROWSDOWN,
MAP_GROWSDOWN | MAP_ANONYMOUS | MAP_PRIVATE,
-1, 0);
mapped_ptr[4095] = 'a'; //OK!
mapped_ptr[0] = 'b'; //OK!
mapped_ptr[-1] = 'c'; //SEGV
问题:结合上面的推理,唯一使用MAP_GROWSDOWN
的映射是主线程的[stack]
映射吗?
【问题讨论】:
红色区域始终是 RSP 当前值下方的 128 个字节。它根本不基于堆栈的大小。 @MichaelPetchThe red zone is always the 128 bytes
- 是不是同一个红区 Raymond Chen discussed?
请注意,ulimit
内置以 1024 字节为增量打印大小。所以当ulimit -s
打印 8192 时,就意味着你的堆栈限制是 8MiB -- 2048 页。
由于没有标记操作系统,需要注意的是Windows没有红色区域。
与sub rax, 4096
相比,为什么循环如此复杂?我认为你通过每次重做乘法来做同样的事情,但它更难遵循。
【参考方案1】:
这些都与红色区域无关,因为您没有移动 RSP。内存保护适用于页面粒度,但红色区域始终仅比 RSP 低 128 字节,这对于读/写是安全的以及,并且不受异步破坏。
不,除非您手动使用,否则不会使用 MAP_GROWSDOWN
。主线程的栈使用了一个不可破坏的机制,不会让其他mmap
调用随机窃取它的增长空间。在Analyzing memory mapping of a process with pmap. [stack]上查看我的回答
您的 asm 代码的有时成功与 Why does this code crash with address randomization on? - 您在 RSP 下最多触及 124 kiB 的内存,因此 132 kiB 的初始分配有时恰好足够,具体取决于 ASLR 以及 args + env 在堆栈上占用的空间。
Why is MAP_GROWSDOWN mapping does not grow? 是有趣的部分:MAP_GROWSDOWN 可能不适用于 1 页映射。但同样,这与堆栈无关。手册页显示“此标志用于堆栈”。是 100% 错误的。这是添加功能时的意图,但设计实际上并不可用,因此即使与文档相比,实现也可能存在错误。
【讨论】:
总结一下。只要保留ulimit -s
,内核就可以增加堆栈,但是如果我们触摸初始映射的132KiB
之外的页面并且正常的#PF
处理机制将SIGSEGV
发送到进程,因为我们尝试过访问未被进程映射的内存。我检查了sub rsp, 0x10000 * PAGE_SIZE\n mov qword [rsp], -1
和ulimit -s unlimited
,即使在没有调整rsp
结果SIGSEGV
的情况下触摸页面时没有触摸中间页面,它也能正常工作。
@St.Antario:是的,只要红色区域(或 RSP)的底部低于或在出错的页面内。否则它只是一个无效的#PF -> SIGSEGV。 (您评论的第一句话省略了有关 RSP 必须移动的部分,所以这不是一个很好的总结。)【参考方案2】:
您混淆了2个不同的概念,除了它们都涉及堆栈,红色区域和堆栈内存区域的扩展无关。如果调用了信号处理程序并且未指定替代信号处理程序堆栈,则会更改红色区域下方但位于堆栈内的内存位置。
我怀疑mmap
分配的MAP_GROWSDOWN
区域未能增长是另一个区域在下方,mmap
通常会连续向下分配虚拟地址。
【讨论】:
感谢红区区域的解释。根据MAP_GROWSDON
失败,我检查了进程的内存映射,发现mmap
调用返回的指针等于0x7fb361abc000
,但是它下面最近的映射区域是7fb361aa2000
所以这不太可能就是这样。我还尝试将特定地址指定为mmap
的参数,但结果相同。
@St.Antario: 主线程的栈是not MAP_GROWSDOWN
。它可以一步增长到最大堆栈大小,而无需接触保护页面的“堆栈探测器”(但only if RSP is decremented first),并且保留潜在的堆栈增长区域,因此其他分配不会' t不小心偷了它。 (对于MAP_GROWSDOWN
,这些都不正确,这就是为什么它对线程堆栈不安全。pthreads 分配完整大小的线程堆栈,因为 Linux 无论如何都会对物理页面进行惰性分配。请参阅this。以上是关于堆栈红色区域的实际大小是多少? [复制]的主要内容,如果未能解决你的问题,请参考以下文章