https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128276
Bug ID: 128276
Summary: lto-streamer-in.cc checks only SANITIZE_ADDRESS and
drops IFN_ASAN_MARK under -fsanitize=hwaddress -flto
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: lto
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: aarch64-*-*, x86_64-*-*
Summary
-------
Under `-fsanitize=hwaddress -flto` (or `-fsanitize=memtag-stack -flto`), LTO
stream-in replaces 100% of `IFN_ASAN_MARK` calls with `IFN_NOP`, completely
disabling stack use-after-scope detection across the linked binary.
During GIMPLE gimplification, GCC emits `IFN_ASAN_MARK (UNPOISON / POISON,
...)`
to mark lexical scope entry and exit for ASan, HWASan, and MTE
(`SANITIZE_ADDRESS |
SANITIZE_HWADDRESS | SANITIZE_MEMTAG_STACK`). `IFN_ASAN_MARK` is only lowered
to
`IFN_HWASAN_MARK` after LTO stream-in during `pass_sanopt` (`sanopt.cc:1405`).
However, `input_function` in `gcc/lto-streamer-in.cc:1521-1538` checks only
`SANITIZE_ADDRESS`:
if (gimple_call_internal_p (stmt, IFN_ASAN_MARK)
&& (DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fn->decl)
? ! (opts->x_flag_sanitize & SANITIZE_ADDRESS)
: ! (flag_sanitize & SANITIZE_ADDRESS)))
gimple_call_set_internal_fn (as_a <gcall *> (stmt), IFN_NOP);
Under `-fsanitize=hwaddress -flto`, `SANITIZE_ADDRESS` is 0, so LTO stream-in
turns every `IFN_ASAN_MARK` into `IFN_NOP` before `pass_sanopt` runs.
Reproducer
----------
$ cat trigger.c
void escape_ptr(int *);
int use_after_scope_probe(int cond) {
int *dangling = 0;
if (cond) {
int scoped_var = 42;
escape_ptr(&scoped_var);
dangling = &scoped_var;
}
return dangling ? *dangling : 0;
}
Compile flag: aarch64-linux-gnu-gcc -O2 -fsanitize=hwaddress -flto
-fdump-tree-sanopt trigger.c -c && aarch64-linux-gnu-gcc -O2
-fsanitize=hwaddress -flto -shared -nostdlib trigger.o -fdump-tree-sanopt -o
trigger.so
$ cat a-trigger.c.246t.sanopt
use_after_scope_probe (int cond)
{
...
.NOP (UNPOISON, &scoped_var, 4); # IFN_ASAN_MARK replaced by .NOP during
LTO stream-in!
scoped_var = 42;
escape_ptr (&scoped_var);
.NOP (POISON, &scoped_var, 4); # scope exit poison wiped out!
Suggested Fix
-------------
In `gcc/lto-streamer-in.cc:1525`, check
`sanitize_flags_p (SANITIZE_ADDRESS | SANITIZE_HWADDRESS |
SANITIZE_MEMTAG_STACK, fn->decl)`
instead of testing only `SANITIZE_ADDRESS`.