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.