linux - 如果涉及 MAP_FIXED,无限堆栈不能超过初始的 132KiB?

标签 linux assembly x86-64 mmap callstack

我正在用堆栈运行一些实验,但以下让我卡住了。

可以看出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 - 0x80),直到我们达到固定映射 rsp - 0x101 * PAGE_SIZE

顺便说一句,如果我从映射中删除 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);

line 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 则适合。

关于linux - 如果涉及 MAP_FIXED,无限堆栈不能超过初始的 132KiB?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56924725/

相关文章:

linux - 请求nasm中sys_fork的例子

assembly - 我不理解 Microsoft x64 调用约定或 FASM 的实现

assembly - NASM x86_64 scanf 段错误

linux - 查看预处理后的Linux模块源文件

linux - 如何将延迟添加到 http 音频流

memory - 低级编程: How to find data in a memory of another running process?

c - 如何8字节对齐静态数组中的每个字符串?

assembly - 当您提前知道路径时,是否可以将分支预测器设置为 "skip"?

python - Gtk 消息 : Failed to load module

linux - 安装带有 undefined symbol 的 R 包