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`.

Reply via email to