I'm running some experiments with stack and the following got me stuck.
It can be seen that Linux has initial [stack]
mapping 132KiB
in size. In case of ulimit -s unlimited
we can expand the stack any further if we adjust rsp
accordingly. So I set ulimit -s unlimited
and ran the following program:
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
Even in spite of adjusting the rsp
it segfaults anyway. I don't really get the point. I manually created a fixed mapping at the address rsp - 0x101 * PAGE_SIZE
101 pages below the rsp
.
My expectation was that it would not interfere with expanding the stack (rsp - 0x80
in my case) till we hit the fixed mapping rsp - 0x101 * PAGE_SIZE
.
Btw, If I remove MAP_FIXED
from the mapping it is not honored and no segfault occurs (as expected). Here is the strace output:
mmap(0x7ffe4e0fe000, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x1526e3f3a000
But MAP_FIXED
does the job:
mmap(0x7ffd8979c000, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffd8979c000
UPD: The segfault is not triggered if lea rdi, [rsp - 0x101 * PAGE_SIZE]
is replaced with lea rdi, [rsp - 0x200 * PAGE_SIZE]
.
Linux kernel enforces a gap between the stack and other mappings. If that gap can not be maintained then the stack will not grow.
Relevant source code in mm/mmap.c, from line 2498
and line 2424:
You can see it's adjustable via kernel parameter but the default is 256. Thus this gap does not fit between 0x80 and 0x101 pages, but does fit if you use 0x200.