Pointer Authentication Code (PAC)
A short cryptographic signature that Arm inserts into unused bits of a pointer for the purpose of Pointer Authentication.
How PAC is Generated
Variables
A PAC is generated using
- The pointer value
- A secret key (only the CPU knows it)
- A “context” value (like a stack pointer, function ID, or arbitrary modifier)
Assembly
ARMv8.3+ has new instructions:
PACinstructions: create a PAC.AUTinstructions: authenticate a PAC.
Example with a return address:
- On function call, the return address is saved.
- Before saving, the CPU signs it:
PACIASP ; Sign LR (link register) using SP (stack pointer) as context
STP x29, x30, [sp, #-16]! ; Save FP and signed LRHere, PACIASP means: *Pointer Authenticate Code using key A, SP as modifier
- On return, the CPU checks the signature:
LDP x29, x30, [sp], #16 ; Load FP and signed LR
AUTIASP ; Authenticate LR using SP as context
RETIf the LR was modified by an attacker, the PAC check fails → program aborts.
Bitwise
- ARM64 pointers are 64 bits.
- In user space, usually only the lower 48 bits are used for addresses.
- The top unused bits store the PAC.
That means the PAC is “hidden” inside the pointer itself without extra memory.
Keys
There are multiple hardware PAC keys:
- Instruction keys (A, B) for code pointers (return addresses, function pointers).
- Data keys (C, D, G) for data pointers.
This separation means even if one kind of pointer is leaked, you can’t reuse the PAC for another type.