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:

  • PAC instructions: create a PAC.
  • AUT instructions: authenticate a PAC.

Example with a return address:

  1. On function call, the return address is saved.
  2. 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 LR

Here, PACIASP means: *Pointer Authenticate Code using key A, SP as modifier

  1. On return, the CPU checks the signature:
LDP        x29, x30, [sp], #16     ; Load FP and signed LR
AUTIASP    ; Authenticate LR using SP as context
RET

If 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.