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

JingYao Zhang <220245569 at seu dot edu.cn> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |220245569 at seu dot edu.cn

--- Comment #2 from JingYao Zhang <220245569 at seu dot edu.cn> ---
This still reproduces with GCC 16.2.0 (aarch64-linux-gnu), and the same root
cause now affects PAC and GCS as well as BTI. It also happens in the opposite
direction, where a protected object ends up with no property note at all.

aarch64_file_end_indicate_exec_stack() calls aarch_bti_enabled(),
aarch64_pacret_enabled() and aarch64_gcs_enabled() once, at end of file. Those
return whatever target options are active at that point. They do not reflect
the functions that were actually emitted. Both the .note.gnu.property path and
the AEABI build-attributes path use the same values.

Case 1: the note claims protection that one function does not have.

  int ext_work(int);
  __attribute__((target("branch-protection=none")))
  int cb(int x) { return x + 1; }
  int entry(int x) { return ext_work(x); }

  $ aarch64-linux-gnu-gcc -O2 -mbranch-protection=standard -S t1.c

  cb:
          add     w0, w0, 1        // no bti c
          ret
  entry:
          hint    34 // bti c
          b       ext_work
          ...
          .word   0xc0000000
          .word   4
          .word   0x7              // BTI | PAC | GCS

If cb is called indirectly after the loader maps the object with PROT_BTI, the
call faults.

Case 2: the whole object loses its protection markings.

  int ext_work(int);
  int entry(int x) { return ext_work(x); }
  #pragma GCC target("branch-protection=none")
  int trailing(int x) { return x + 1; }

  $ aarch64-linux-gnu-gcc -O2 -mbranch-protection=standard -S t2.c

entry still gets "hint 34 // bti c", but no .note.gnu.property is emitted,
because the options in effect at end of file are "none". The linker ANDs the
notes of all inputs, so one such object silently clears BTI/PAC/GCS for the
whole linked binary.

A possible fix is to keep a running AND of the per-function BTI/PAC/GCS state
as functions are emitted, and write that mask at end of file instead of the
end-of-file option state.

For comparison, RISC-V avoids the problem by rejecting per-function "arch="
changes that would drop Zicfilp/Zicfiss under -fcf-protection, so
riscv_file_end() cannot see a mixed translation unit.

Reply via email to