profile picture

PACMAN Paper Review

November 12, 2022 - paper-review speculative-execution computer-architecture hardware-security

PACMAN: Attacking ARM Pointer Authentication with Speculative Execution is a paper that was published in ISCA 2022 by MIT professor Mengjia Yan's students. It details an exploit that allows bypassing the pointer authentication security feature of recent ARM processors (introduced for ARMv8.3, 2017).

Pointer Authentication

Pointer Authentication is a hardware technique meant to prevent software exploits that modify a pointer address, such as a buffer overflow that overwrites a stack pointer return address. It works by assigning a Pointer Authentication Code (PAC) to protected pointers. A PAC consists of a cryptographic hash of the pointer, some of the context surrounding the pointer, and a salt. The salt and context combined together form a key, which is stored in a privileged register inaccessible from userspace. When the pointer is dereferenced, the PAC is compared against a new hash calculated with the stored key; if the PAC matches the hash, it is unlikely the pointer was modified and code execution continues. If the PAC does not match the hash, then the hardware traps, generates a virtual address fault, and the OS likely crashes the program.

PACs are stored as part of the virtual address for protected pointers. Even though 64 bit instruction set architectures ostensibly support 64 bit virtual addresses, physical addresses don't extend nearly that high, and most 64 bit operating systems provide between 40 and 50 bits of virtual address space (the version of macOS tested in this paper takes 48 bits). Therefore the PAC is a 16 bit hash stored in the upper bits of every protected virtual address.

To support PACs, ARM instroduces an ISA extension with instructions for both signing pointers, and verifying pointers. The mnemomic for signing uses the prefix pac, and the mnemomic for authentication uses aut. Thus a common usage would be to create a PAC of the stack pointer on a function call entry using the signature instruction, and then calling the authentication before jumping to the return address on function call exit. The ISA provides five distinct key registers.

Threat Model

PACMAN is a method for bypassing a hardware security mechanism. It assumes the victim program has an existing flaw, such as a buffer overflow, that allows an attacker to overwrite a pointer in the victim's address space. If victim software is written in a memory-safe language, then it is safe from PACMAN.

The reader might say why bother with PAC at all then? Just write correct software, or use a language the enforces correctness. In a perfect world that is true, however in practice writing correct software is hard. Memory-safe languages still have buffer flows (albeit less often) via bugs in their implementation, or faulty threat models. Even in a formally verified program with no buffer overflows, an attacker could in theory use a hardware exploit to overwrite memory, such as changing a pointer address in RAM with Rowhammer.

PACMAN also assumes that an attacker has the ability to run non-privileged code on the same system as the victim program, and the attacker has access to a sufficiently precise timer to measure side channels in the microarchitecture. The authors show in that userspace timing mechanisms are reasonably accesible to a non-privileged user on macOS on an M1 processor.

Finally PACMAN assumes the attacker has access to some source code of the victim process, and is able to locate a "PAC oracle" (more on this later). This is pretty reasonable given the OS itself is an attack surface, and the victim program probably loads things like the C standard library or makes a system call.

Background information

The next couple sections provide some background information on mechanisms required PACMAN relies. Both side channels and speculative execution are involved topics in computer architecture, and the reader is encouraged to seek further detail.

Micro-architectural Side Channels

The first step towards executing a PACMAN attack is exploiting a micro-architectural side channel to read the virtual address of another program running on the same system. Typically this involves running a userspace program, and attempting to snoop on a privileged process, such as the operating system kernel. There are many widely-studied side channels, and methods to measure them, which is out of scope for this blog post. The specific attack discussed uses a prime and probe exploit on the translation lookaside buffer (TLB).

Prime and probe works in three steps: first the attacker "primes" by filling the affected cache with as many writes as its associativity (known as the eviction set), waits for the victim program to access the eviction set, and then checks for the victim access by measuring the latency of re-accessing the eviction set. In the case of the TLB, this involves allocating a bunch of virtual pages in order to fill the TLB, and then leaking the victim process pointer address by measuring the latency and figuring out which set the victim belonged to.

Speculative Execution

Modern processors use a feature called speculative execution to significantly speed up their execution, and mitigate the penalty for using long pipelines when branching. Again a full discussion is outside the scope of this blog post. The basic idea though is that a processor maintains a branch predictor, which guesses whether a branch is taken or not. Whenever a branch is reached, the branch predictor is immediately able to speculate if the branch is taken, and the processor is able to continue loading instructions down the speculated path without waiting to verify branch condition. If the branch is later verified, execution continues as normal and any architecturally visible actions are committed. If the prediction was incorrect, however, the pipeline is flushed, speculative instructions are discarded before they commit, and execution resumes from the proper branch. The alternative would be to wait for the pipeline to verify the branch condition, which would force the processor to stall for as many clock cycles as the pipeline depth between instruction fetch and branch verification.

A Spectre-style attack exploits the speculative execution mechanism by taking many branches and forcing the shared branch prediction engine to make bad predictions. Ordinarily this wouldn't be so bad, because although it would slow down the victim program by forcing many pipeline flushes, it wouldn't affect the program output because speculative instructions aren't committed. Micro-architectural channels, however, leak information from speculative instructions, such as speculative memory accesses.

PACMAN Exploit

PAC protections rely heavily on the fact that hardware will create an exception on the first attempted pointer dereference that fails. A 16 bit hash is not nearly enough to prevent relatively quick brute force attacks running on modern hardware, but that doesn't matter if you only get one guess before the program crashes. PACMAN exploits both speculative execution and micro-architectural side channels to give an attacker unlimited guesses at the PAC, ensuring when they overwrite the PAC protected pointer they also fill in the proper PAC.

The high level description for PACMAN is to force the victim program to speculatively dereference the target pointer, watch a side channel, and read the virtual address.