Guide: Enable PAC and BTI Linux ARM64 Control-Flow Hardening
PAC and BTI harden ARM64 control-flow, but only when every linked object carries the GNU_PROPERTY note. This guide covers flags, verification, and recovery.
05 Aug 2025, 08:40 UTC

Problem: Hardening silently drops when libraries disagree
\nYou compile an ARM64 Linux binary with -mbranch-protection=standard expecting Pointer Authentication (PAC) and Branch Target Identification (BTI) to harden control-flow, but the binary either faults or—worse—runs without the protection. PAC signs return addresses and pointers with a keyed hash stored in unused high address bits; BTI restricts indirect branch targets to marked landing pads, together mitigating ROP and JOP-style hijacking. The Linux dynamic loader and kernel only enforce these checks when every loaded object carries a GNU_PROPERTY_AARCH64_FEATURE_1_AND note; a single unmarked shared library silently disables enforcement process-wide.
Useful takeaway
\nPAC and BTI are only as strong as the weakest linked object. Verify the property note on every binary and library before deploying hardened builds fleet-wide.
\n\nPrerequisites
\n- \n
- A recent GCC or Clang release that emits the GNU_PROPERTY note (GCC 10+, Clang 10+ are good starting points). \n
- Binutils and glibc new enough to honor and act on the note (glibc 2.34+, binutils 2.34+). \n
- A 64-bit ARMv8.5+ kernel and hardware that implements PAC and BTI (e.g., Amazon Graviton4, Apple M-series, or custom server silicon). \n
- C/C++ source code that does not embed hand-written assembly or JITs without BTI landing pads. \n
Compiler and linker procedure
\nApply the hardening flag consistently across all translation units and static libraries. The flag -mbranch-protection=standard enables both PAC return-address signing and BTI; for leaf-function-only hardening use -mbranch-protection=pac-ret+leaf.
gcc -mbranch-protection=standard -fPIC -O2 -c main.c -o main.o\ngcc -mbranch-protection=standard -fPIC -O2 -c lib.c -o lib.o\nar rcs libcrypto.a crypto.o\ngcc -mbranch-protection=standard -o myprog main.o libcrypto.a\nNotice the flag appears on the compile step and the link step. Omitting it from any object file that participates in the final link will cause the linker to omit the property note, dropping PAC/BTI for the whole process.
\n\nExpected checks
\n- \n
- Run readelf -n ./myprog and confirm the output contains AArch64 program property with both PAC and BTI enabled under AND flags. \n
- Run objdump -d ./myprog and grep for paciasp, autiasp, or bti in the output of hot functions. Their presence confirms the compiler emitted the instructions. \n
- Read /proc/cpuinfo and look for PAC- and BTI-related flags, or use getauxval(AT_HWCAP2) in a small C program to check for HWCAP2_PACA, HWCAP2_PACG, and HWCAP2_BTI bits. \n
Recovery when binaries fault
\nIf a hardened binary terminates with a SIGSEGV and the supplementary info points to a PAC authentication failure (the kernel may encode a PAC-specific si_code), do not immediately assume a control-flow attack. Rebuild the same binary without -mbranch-protection and run the workload again. If the fault disappears, the original crash was a true memory-safety bug that PAC caught—a desirable outcome, but one that requires debugging the underlying code defect. If the fault persists, the issue is unrelated to PAC/BTI (e.g., a genuine memory corruption or hardware bug).
\nOn legacy ARMv8.3-A hardware without PAC/BTI support, the paciasp and bti instructions execute as NOP-like hints; the binary remains runnable but receives no protection. Always verify HWCAP2 bits before assuming enforcement is active.
\n\nLimitations and rollout strategy
\n- \n
- PAC/BTI availability is hardware- and kernel-dependent; behavior described here is version-sensitive across GCC, glibc, and kernel releases. \n
- Assembly code and JITs must be manually annotated with BTI landing pads or they will fault when BTI is enforced. \n
- A single shared library lacking the GNU property note can disable enforcement for the whole process, weakening the expected protection silently. \n
- PAC catches some memory-safety bugs as crashes in production; plan your debugging strategy and consider a staged rollout. \n
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.