在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]
映射?>
答案 0 :(得分:5)
您会混淆2个不同的概念,除了它们都涉及堆栈之外,红色区域和堆栈存储区的扩展无关。如果调用信号处理程序且未指定其他信号处理程序堆栈,则将更改红色区域下方但堆栈内的内存位置。
我怀疑分配给mmap
的{{1}}区域的增长失败是因为另一个区域就在下面,MAP_GROWSDOWN
通常会连续向下分配虚拟地址。
答案 1 :(得分:2)
与红色区域无关,因为您没有移动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 kb,所以最初分配132 kiB有时就足够了,具体取决于ASLR和args + env在堆栈上需要多少空间。
Why is MAP_GROWSDOWN mapping does not grow?是有趣的部分:MAP_GROWSDOWN可能不适用于1页映射。但是同样,这与堆栈没有任何关系。手册页上写着“此标志用于堆栈”。是100%错误的。这是添加功能时的意图,但是设计实际上并不可用,因此即使与文档相比,实现也可能有问题。