Hi Paul, you wrote on 2020-09-22: > I am thinking of some combination of gcc -fstack-check and/or > -fstack-clash-protection and/or related ideas (not that I've looked into all > the > details). That is, I'm expecting help from the hardware, the kernel, and from > GCC. Stack-exhaustion checking should "just work". I realize it's a > nontrivial ask.
gcc -fstack-clash-protection does exactly what most people expect; as you say, with the help from the hardware, the kernel, and GCC. - The hardware provides the MMU and the concept of page tables, which are the basis of mprotect(). - The kernel provides mprotect; organizes a guard page at the bottom of the stack; and grows the stack if an access to the guard page is made. - 'gcc -fstack-clash-protection' arranges that a stack frame that is larger than one page is accessed in single-page steps, from top to bottom, so that the kernel will grow the stack and not report a SIGSEGV. Test code below. This is how it's done on Windows. On Linux, the kernel allows the stack to grow by any amount, if it does not become closer than 1 MB to another VMA and does not violate the set limits. See linux/mm/mmap.c:expand_downwards and linux/mm/mmap.c:acct_stack_growth. Therefore on Linux, there is no need for a guard page and no need for 'gcc -fstack-clash-protection'. In both cases, it "just works". What other features around stack bounds checking are you thinking of? gcc -fstack-check is unrelated: It merely adds a canary word in each stack frame and checks that this word is still present upon return. So, it's merely a defense against buffer overflows on the stack. Bruno ========================= foo.c ===================== void bar (int *array); void foo (int n) { int array[n]; for (int i = 0; i < n; i++) array[i] = i*i; bar (array); } ===================================================== gcc -fstack-clash-protection -O2 -S foo.c ========================= foo.s ===================== .file "foo.c" .text .p2align 4 .globl foo .type foo, @function foo: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movslq %edi, %rcx leaq 15(,%rcx,4), %rax movq %rcx, %rdx movq %rax, %rsi andq $-4096, %rax movq %rsp, %rdi movq %rsp, %rbp .cfi_def_cfa_register 6 andq $-16, %rsi subq %rax, %rdi cmpq %rdi, %rsp je .L3 .L13: subq $4096, %rsp orq $0, 4088(%rsp) cmpq %rdi, %rsp jne .L13 .L3: andl $4095, %esi subq %rsi, %rsp testq %rsi, %rsi jne .L14 .L4: movq %rsp, %rdi testl %edx, %edx jle .L5 xorl %eax, %eax .p2align 4,,10 .p2align 3 .L6: movl %eax, %edx imull %eax, %edx movl %edx, (%rdi,%rax,4) addq $1, %rax cmpq %rcx, %rax jne .L6 .L5: call bar leave .cfi_remember_state .cfi_def_cfa 7, 8 ret .p2align 4,,10 .p2align 3 .L14: .cfi_restore_state orq $0, -8(%rsp,%rsi) jmp .L4 .cfi_endproc .LFE0: .size foo, .-foo .ident "GCC: (GNU) 10.2.0" .section .note.GNU-stack,"",@progbits =====================================================