https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128296

            Bug ID: 128296
           Summary: [RISC-V] -fstack-clash-protection uses initial 3 KiB
                    probe threshold for final below-saved-regs allocation
           Product: gcc
           Version: 16.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: 220245569 at seu dot edu.cn
                CC: 220255623 at seu dot edu.cn, jianhao.xu at seu dot edu.cn
  Target Milestone: ---
            Target: riscv64-*-*

Summary
-------
Under `-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection`, the RISC-V
prologue uses the initial probe threshold (`guard_size - guard_used_by_caller`
= 3072 bytes) instead of `guard_used_by_caller` (1024 bytes) for the final
below-saved-registers stack allocation.

In `gcc/config/riscv/riscv.cc:9886-9945`
(`riscv_allocate_and_probe_stack_space`),
`min_probe_threshold` is unconditionally set to `guard_size -
guard_used_by_caller`
(3072 bytes for the default 4 KiB guard). Unlike AArch64 (`aarch64.cc:10164`,
which switches `min_probe_threshold` to `guard_used_by_caller +
byte_sp_alignment`
for the final adjustment), RISC-V leaves up to 3056 bytes unprobed below the
register-save slot at the end of `caller`'s prologue.

When `caller` calls a `leaf` function with a 3040-byte frame (`< 3072`),
neither function probes the residual allocation, and `call` on RISC-V writes
the
return address to register `ra` rather than memory. The two frames traverse
6088 bytes without touching memory, jumping completely over a 4096-byte stack
guard page.

Reproducer
----------
$ cat trigger.c
__attribute__((noinline)) void leaf (void) {
    volatile char b[3040];
    b[0] = 1;
}

void caller (void) {
    volatile char pad[3040];
    leaf ();
    pad[0] = 1;
}

Compile flag: riscv64-linux-gnu-gcc -O2 -march=rv64gc -mabi=lp64d
-fstack-clash-protection -S trigger.c

$ cat trigger.s
caller:
        addi    sp,sp,-2032
        sd      ra,2024(sp)             # deepest touch in caller = sp0 - 8
        addi    sp,sp,-1024             # sp -= 1024 (NO PROBE; leaves 3048B
unprobed below sp0-8)
        call    leaf

leaf:
        addi    sp,sp,-2048             # NO PROBE
        addi    sp,sp,-992              # NO PROBE (total frame 3040 < 3072)
        li      a5,1
        sb      a5,0(sp)                # first touch in leaf = sp0 - 6096
(6088B unprobed gap!)

Suggested Fix
-------------
Pass `final_adjustment_p` to `riscv_allocate_and_probe_stack_space` (matching
`aarch64_allocate_and_probe_stack_space`) so that the final allocation below
saved registers uses `guard_used_by_caller` (1024) as its probe threshold.

Reply via email to