如果涉及 MAP_FIXED,无限堆栈不能超过初始的 132KiB?
Unlimited stack cannot grow beyond the initial 132KiB if MAP_FIXED involved?
我正在 运行 进行一些堆栈实验,但以下内容让我卡住了。
可以看出Linux有初始[stack]
映射大小132KiB
。在 ulimit -s unlimited
的情况下,如果我们相应地调整 rsp
,我们可以进一步扩展堆栈。所以我设置 ulimit -s unlimited
和 运行 如下程序:
PAGE_SIZE equ 0x1000
;mmap staff
PROT_READ equ 0x01
PROT_WRITE equ 0x02
MAP_ANONYMOUS equ 0x20
MAP_PRIVATE equ 0x02
MAP_FIXED equ 0x10
;syscall numbers
SYS_mmap equ 0x09
SYS_exit equ 0x3c
section .text
global _start
_start:
; page alignment
and rsp, -0x1000
; call mmap 0x101 pages below the rsp with fixed mapping
mov rax, SYS_mmap
lea rdi, [rsp - 0x101 * PAGE_SIZE]
mov rsi, PAGE_SIZE
mov rdx, PROT_READ | PROT_WRITE
mov r10, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED
mov r8, -1
mov r9, 0
syscall
sub rsp, 0x80 * PAGE_SIZE
mov qword [rsp], -1 ; SEGV
mov rax, SYS_exit
mov rdi, 0
syscall
即使调整了 rsp
,它仍然会出现段错误。我真的不明白这一点。我在rsp
下面的地址rsp - 0x101 * PAGE_SIZE
101页手动创建了一个固定映射。
我的期望是在我们达到固定映射 rsp - 0x101 * PAGE_SIZE
.
之前它不会干扰扩展堆栈(rsp - 0x80
在我的例子中)
顺便说一句,如果我从映射中删除 MAP_FIXED
,它不会被接受并且不会发生段错误(如预期的那样)。这是 strace 输出:
mmap(0x7ffe4e0fe000, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x1526e3f3a000
但是 MAP_FIXED
可以做到:
mmap(0x7ffd8979c000, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffd8979c000
UPD:如果 lea rdi, [rsp - 0x101 * PAGE_SIZE]
则不会触发段错误
替换为 lea rdi, [rsp - 0x200 * PAGE_SIZE]
.
Linux 内核强制堆栈和其他映射之间存在间隙。如果不能维持这个差距,那么堆栈将不会增长。
相关源码在mm/mmap.c, 来自line 2498
/* enforced gap between the expanding stack and other mappings. */
unsigned long stack_guard_gap = 256UL<<PAGE_SHIFT;
static int __init cmdline_parse_stack_guard_gap(char *p)
{
unsigned long val;
char *endptr;
val = simple_strtoul(p, &endptr, 10);
if (!*endptr)
stack_guard_gap = val << PAGE_SHIFT;
return 0;
}
__setup("stack_guard_gap=", cmdline_parse_stack_guard_gap);
int expand_downwards(struct vm_area_struct *vma,
unsigned long address)
{
struct mm_struct *mm = vma->vm_mm;
struct vm_area_struct *prev;
int error = 0;
address &= PAGE_MASK;
if (address < mmap_min_addr)
return -EPERM;
/* Enforce stack_guard_gap */
prev = vma->vm_prev;
/* Check that both stack segments have the same anon_vma? */
if (prev && !(prev->vm_flags & VM_GROWSDOWN) &&
(prev->vm_flags & (VM_WRITE|VM_READ|VM_EXEC))) {
if (address - prev->vm_end < stack_guard_gap)
return -ENOMEM;
}
您可以看到它可以通过内核参数进行调整,但默认值为 256。因此,此间隙不适合 0x80 和 0x101 页面,但如果您使用 0x200 则适合。
我正在 运行 进行一些堆栈实验,但以下内容让我卡住了。
可以看出Linux有初始[stack]
映射大小132KiB
。在 ulimit -s unlimited
的情况下,如果我们相应地调整 rsp
,我们可以进一步扩展堆栈。所以我设置 ulimit -s unlimited
和 运行 如下程序:
PAGE_SIZE equ 0x1000
;mmap staff
PROT_READ equ 0x01
PROT_WRITE equ 0x02
MAP_ANONYMOUS equ 0x20
MAP_PRIVATE equ 0x02
MAP_FIXED equ 0x10
;syscall numbers
SYS_mmap equ 0x09
SYS_exit equ 0x3c
section .text
global _start
_start:
; page alignment
and rsp, -0x1000
; call mmap 0x101 pages below the rsp with fixed mapping
mov rax, SYS_mmap
lea rdi, [rsp - 0x101 * PAGE_SIZE]
mov rsi, PAGE_SIZE
mov rdx, PROT_READ | PROT_WRITE
mov r10, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED
mov r8, -1
mov r9, 0
syscall
sub rsp, 0x80 * PAGE_SIZE
mov qword [rsp], -1 ; SEGV
mov rax, SYS_exit
mov rdi, 0
syscall
即使调整了 rsp
,它仍然会出现段错误。我真的不明白这一点。我在rsp
下面的地址rsp - 0x101 * PAGE_SIZE
101页手动创建了一个固定映射。
我的期望是在我们达到固定映射 rsp - 0x101 * PAGE_SIZE
.
rsp - 0x80
在我的例子中)
顺便说一句,如果我从映射中删除 MAP_FIXED
,它不会被接受并且不会发生段错误(如预期的那样)。这是 strace 输出:
mmap(0x7ffe4e0fe000, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x1526e3f3a000
但是 MAP_FIXED
可以做到:
mmap(0x7ffd8979c000, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffd8979c000
UPD:如果 lea rdi, [rsp - 0x101 * PAGE_SIZE]
则不会触发段错误
替换为 lea rdi, [rsp - 0x200 * PAGE_SIZE]
.
Linux 内核强制堆栈和其他映射之间存在间隙。如果不能维持这个差距,那么堆栈将不会增长。
相关源码在mm/mmap.c, 来自line 2498
/* enforced gap between the expanding stack and other mappings. */
unsigned long stack_guard_gap = 256UL<<PAGE_SHIFT;
static int __init cmdline_parse_stack_guard_gap(char *p)
{
unsigned long val;
char *endptr;
val = simple_strtoul(p, &endptr, 10);
if (!*endptr)
stack_guard_gap = val << PAGE_SHIFT;
return 0;
}
__setup("stack_guard_gap=", cmdline_parse_stack_guard_gap);
int expand_downwards(struct vm_area_struct *vma,
unsigned long address)
{
struct mm_struct *mm = vma->vm_mm;
struct vm_area_struct *prev;
int error = 0;
address &= PAGE_MASK;
if (address < mmap_min_addr)
return -EPERM;
/* Enforce stack_guard_gap */
prev = vma->vm_prev;
/* Check that both stack segments have the same anon_vma? */
if (prev && !(prev->vm_flags & VM_GROWSDOWN) &&
(prev->vm_flags & (VM_WRITE|VM_READ|VM_EXEC))) {
if (address - prev->vm_end < stack_guard_gap)
return -ENOMEM;
}
您可以看到它可以通过内核参数进行调整,但默认值为 256。因此,此间隙不适合 0x80 和 0x101 页面,但如果您使用 0x200 则适合。