Skip to content

AD3018: EnableArmPAC

Summary

Property Value
ID AD3018
Name EnableArmPAC
Category Security
Severity Warning
Applies to ELF (Linux/Unix) - AArch64

Description

This rule checks that AArch64 ELF binaries have ARM Pointer Authentication Codes (PAC) enabled. PAC provides hardware-based protection against Return-Oriented Programming (ROP) attacks by cryptographically signing return addresses.

Why This Matters

ARM Pointer Authentication (PAC) provides cryptographic protection for pointers, making ROP attacks cryptographically expensive to execute. Unlike software canaries, PAC uses hardware-accelerated cryptography that cannot be bypassed through information leaks alone.

Cryptographic vs. Randomization Defense

Stack Canary (randomization):
  - Random value on stack
  - If attacker leaks it, defense broken
  - One leak = complete bypass

PAC (cryptographic):
  - Cryptographic signature per pointer
  - Signature depends on: pointer, context, secret key
  - Leaking one signature doesn't help with others
  - Key is in hardware, cannot be extracted

How PAC Protects Return Addresses

; Function prologue
paciasp         ; Sign x30 (link register) with SP context
stp x29, x30, [sp, #-16]!

; Function body...

; Function epilogue
ldp x29, x30, [sp], #16
retaa           ; Verify signature, then return
                ; If tampered → exception

PAC Key Architecture

Key Purpose Scope
APIAKey Instruction addresses Per-process
APDAKey Data pointers Per-process
APIBKey Alt instruction auth Per-process
APDBKey Alt data auth Per-process
APGAKey Generic auth Per-process

Keys are managed by the kernel and inaccessible to userspace.

Security Properties

Property Benefit
Context binding Signature includes SP, prevents reuse
Hardware keys Cannot be extracted
Per-pointer signature Each pointer independently protected
Speculation safety PAC operations are speculation barriers

Attack Difficulty with PAC

Without PAC:
  1. Overflow buffer
  2. Overwrite return address with ROP chain
  3. Done

With PAC:
  1. Overflow buffer
  2. Overwrite return address
  3. Need valid PAC for new address
  4. PAC = f(address, context, secret_key)
  5. Can't compute without key
  6. Attack fails

PAC + BTI Combined Protection

Attack Type PAC Alone BTI Alone PAC + BTI
ROP Blocked Partial Blocked
JOP Partial Blocked Blocked
COP Partial Blocked Blocked

Using both provides comprehensive protection.

Performance Characteristics

Operation Overhead
PACIA/AUTIA 1-3 cycles
Per function call ~2 extra instructions
Overall application 1-5%

Hardware acceleration makes PAC practical for production.

Deployment Status

Platform PAC Support
Apple iOS 12+ Mandatory
Apple macOS 11+ Mandatory
Android 12+ Optional, encouraged
Linux 5.0+ Kernel support
  • ROP protection: PAC signs return addresses to detect tampering
  • Cryptographic security: Uses hardware keys for signing
  • Low overhead: Minimal performance impact with hardware support
  • Defense in depth: Complements BTI for comprehensive control-flow protection

Performance Considerations

PAC is designed for production use with minimal overhead:

Metric Impact
Runtime overhead 1-5% typical
Per-function cost 2 extra instructions
Memory overhead None (uses pointer bits)
Hardware requirement ARMv8.3-A+

Operation costs:

Instruction Cycles
PACIA/PACDA 1-3 cycles
AUTIA/AUTDA 1-3 cycles
RETAA Same as RET

Workload impact:

Workload Type Overhead
Compute-bound 0.5-1%
Call-heavy 2-5%
I/O-bound Negligible

Trade-offs: - All code in the call chain should be PAC-enabled for full protection - Mixing PAC and non-PAC code reduces security benefit - Libraries should be recompiled with PAC support

How PAC Works

  1. On function entry: The return address is signed using PACIASP instruction
  2. Signature stored: The PAC is stored in unused upper bits of the pointer
  3. On function return: The signature is verified using AUTIASP or RETAA
  4. Tampering detected: If the PAC doesn't match, an authentication failure occurs
function:
    paciasp              ; Sign return address with key A and SP
    stp x29, x30, [sp, #-16]!
    ...
    ldp x29, x30, [sp], #16
    retaa                ; Return with authentication

How to Fix

Compile with branch protection

# Enable PAC for return addresses
gcc -mbranch-protection=pac-ret -o myapp myapp.c

# Enable both BTI and PAC (recommended)
gcc -mbranch-protection=standard -o myapp myapp.c

# Clang equivalent
clang -mbranch-protection=pac-ret+bti -o myapp myapp.c

Verify the fix

# Check for PAC instructions
objdump -d myapp | grep -E "paciasp|autiasp|retaa"

# Check ELF properties
readelf -n myapp | grep -i "PAC"

Example

Fail: Binary does not have PAC enabled

# Standard return instruction
ret

Pass: Binary has PAC enabled

# PAC-protected return
paciasp
...
retaa

Requirements

  • Compiler: GCC 9+ or Clang 8+
  • CPU: ARMv8.3-A or later (with FEAT_PAuth)
  • Kernel: Linux 5.0+ for PAC support

See Also