Неограниченный стек не может превышать начальные 132 КБ, если задействован MAP_FIXED? - PullRequest
0 голосов
/ 07 июля 2019

Я провожу некоторые эксперименты со стеком, и следующее застряло.

Видно, что 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 - 0x101 * PAGE_SIZE 101 страниц ниже rsp.

Я ожидал, что это не помешает расширению стека (rsp - 0x80 в моем случае), пока мы не достигнем фиксированного отображения rsp - 0x101 * PAGE_SIZE.

Кстати, если я удаляю MAP_FIXED из сопоставления, это не учитывается и не происходит никакого сбоя (как и ожидалось). Вот результат вывода:

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: segfault не срабатывает, если lea rdi, [rsp - 0x101 * PAGE_SIZE] заменяется на lea rdi, [rsp - 0x200 * PAGE_SIZE].

1 Ответ

4 голосов
/ 07 июля 2019

Ядро Linux обеспечивает разрыв между стеком и другими отображениями. Если этот разрыв не может быть сохранен, то стек не будет расти.

Соответствующий исходный код в mm / mmap.c, от строка 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);

и строка 2424 :

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.

...