Enabling and Verifying Pointer Authentication on ARM64 (AArch64)
Learn how to enable PAC on ARMv8.5‑A CPUs, compile with GCC/Clang, verify runtime support, and fall back if the hardware lacks the feature.
08 Jul 2025, 17:12 UTC

Desired Outcome
Enable Pointer Authentication (PAC) on an ARM64 system so that all user‑space code is signed with a cryptographic key. Verify that the CPU supports the feature, that the compiler emits PAC instructions, and that the binary runs correctly. If the target CPU lacks PAC, fall back to a non‑PAC build without compromising stability.
Prerequisites
- Linux distribution with a recent kernel (5.8+ recommended for PAC support in the kernel).
- Root privileges or the ability to run
sudofor hardware checks. - GCC 10+ or Clang 10+ installed.
- lld linker (required for Clang’s PAC support).
- Basic understanding of compiler flags and sysreg reading.
Procedure
1. Verify CPU PAC Capability
PAC is exposed through the ID_AA64PFR0_EL1 register. The bits that matter are:
PFR0[25:24]– PAC-A1 support.PFR0[27:26]– PAC-A2 support.
On most distributions you can read this via the read_sysreg helper or by inspecting /proc/cpuinfo.
# As root or with sudo
sudo read_sysreg ID_AA64PFR0_EL1
# Or a quick text check
grep -i pac /proc/cpuinfo
Interpret the output: if either PAC-A1 or PAC-A2 shows a non‑zero value, the core supports PAC. If both are zero, PAC is unavailable and you should skip the rest of the steps or use the fallback strategy.
2. Compile with PAC Flags
Both GCC and Clang require the target architecture to be explicitly set to ARMv8.5‑A and the PAC feature enabled. The following examples compile a minimal test program.
- GCC example:
cat > pac_test.c <<'EOF'
#include
#include
int main(void) {
// Create a function pointer and sign it
void (*func)(void) = &printf;
func = __builtin_pacg((uintptr_t)func);
printf("PAC test succeeded\n");
return 0;
}
EOF
# Compile with PAC enabled
gcc -march=armv8.5-a+pac -Wl,--no-relax -o pac_test pac_test.c
# Run
./pac_test
- Clang example (requires lld):
cat > pac_test.c <<'EOF'
#include
#include
int main(void) {
void (*func)(void) = &printf;
func = __builtin_pacg((uintptr_t)func);
printf("PAC test succeeded\n");
return 0;
}
EOF
# Compile with PAC enabled
clang -march=armv8.5-a+pac -fuse-ld=lld -o pac_test pac_test.c
# Run
./pac_test
Both compilers will emit PACGA or similar instructions. If the compiler refuses to compile, double‑check the architecture string and that the toolchain was built with PAC support.
3. Runtime Verification
When the binary starts, the kernel will verify that the PAC bits in the register match the binary’s expectations. If the CPU lacks PAC, the program will fault with FEAT_PAC_FAULT and exit with a non‑zero status. On a PAC‑capable CPU, the program should print PAC test succeeded and exit with status 0.
To confirm no hard faults occur, you can watch the kernel log:
sudo dmesg | grep -i pac
Absence of entries like FEAT_PAC_FAULT indicates successful execution.
4. Fallback and Recovery
If the target CPU does not support PAC, you must build a non‑PAC binary. The simplest approach is to omit the PAC flags or set the environment variable PAC_DISABLE=1 before compilation. This tells the compiler to skip PAC intrinsics and use the standard calling convention.
# Disable PAC via environment
export PAC_DISABLE=1
# Compile without PAC flags
gcc -march=armv8.5-a -Wl,--no-relax -o pac_test pac_test.c
# Or with Clang
clang -march=armv8.5-a -fuse-ld=lld -o pac_test pac_test.c
When running the fallback binary on a PAC‑capable CPU, the kernel will allow execution because the binary does not request PAC. However, you lose the security benefits. Always document the build configuration used for each binary.
Expected Checks and Limits
- CPU PAC bits non‑zero → proceed with PAC build.
- Compilation succeeds and binary runs → PAC is functional.
- Kernel logs show no
FEAT_PAC_FAULT→ PAC enforcement passed. - On older cores (e.g., Cortex‑A57),
pac_testwill fault; fallback build is required.
Limitations: PAC changes the ABI by adding a hidden authentication byte to pointers. All linked libraries and the OS kernel must also be built with matching PAC flags for full protection. Mixing PAC and non‑PAC binaries in the same process can lead to crashes. Verify the entire toolchain and runtime environment before deploying to production.
Practical Verification Checklist
- Run
sudo read_sysreg ID_AA64PFR0_EL1– confirm PAC bits. - Compile
pac_test.cwith the appropriate flags. - Execute the binary – expect “PAC test succeeded”.
- Check
dmesgfor PAC faults. - If any step fails, rebuild without PAC or upgrade the kernel/CPU.
By following this guide, you can confidently enable PAC on ARM64, validate the feature at runtime, and have a clear fallback path when the hardware does not support it.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.