https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128300
Bug ID: 128300
Summary: ipa-strub: STRUB_INLINABLE emits unscrubbed
out-of-line body when address-taken and misses
late-inlined VLA watermark
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: ipa
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: *-*-*
Summary
-------
In `gcc/ipa-strub.cc:1206-1219, 2781-2793`, functions annotated with both
`__attribute__((strub("internal")))` and `always_inline` are classified as
`STRUB_INLINABLE` instead of `STRUB_WRAPPED`, which causes two stack-scrubbing
bypasses:
1. Unscrubbed out-of-line body when address-taken:
`pass_ipa_strub::execute` (`ipa-strub.cc:2792`) skips wrapping for
`STRUB_INLINABLE`. If the function has external linkage or its address is
taken (`&wipe_inline_fn`), GCC still emits a standalone out-of-line function
body, but that body contains zero `__strub_enter`, `__strub_update`, or
`__strub_leave` calls. Any indirect call leaves its stack frame completely
unscrubbed.
2. Late-inlined VLA escapes watermark:
When a `STRUB_INLINABLE` helper containing a VLA or `__builtin_alloca` is
called via a local function pointer that `pass_ipa_inline` (running after
`pass_ipa_strub`) devirtualizes and inlines into a `strub("internal")`
caller, the caller's `__strub_update` watermark update remains before the
inlined `subq %rax, %rsp` VLA allocation, leaving the entire VLA below the
scrub watermark.
Reproducer
----------
$ cat trigger.c
void use_buf(volatile char *, int);
__attribute__((always_inline, strub("internal")))
int wipe_inline_fn(int n) {
volatile char secret[32];
secret[0] = (char)n;
use_buf(secret, 32);
return secret[0];
}
int (*export_ptr(void))(int) {
return &wipe_inline_fn;
}
Compile flag: gcc -O2 -S trigger.c
$ cat trigger.s
wipe_inline_fn:
subq $40, %rsp # allocates 32-byte secret buffer
movl $32, %esi
movb %dil, (%rsp)
movq %rsp, %rdi
call use_buf
movsbl (%rsp), %eax
addq $40, %rsp
ret # ZERO __strub_enter/update/leave!
Suggested Fix
-------------
In `gcc/ipa-strub.cc:1206-1219`, reject or wrap `STRUB_INLINABLE` functions
whose address is taken / that remain externally callable out-of-line, and emit
a `__builtin___strub_update` call when inlining a `STRUB_INLINABLE` callee that
allocates dynamic stack space.