Return Address Authentication Hardening#
Introduction#
Return Address Authentication Hardening is a mitigation against the PACMAN attack, which aims to bypass Pointer Authentication on AArch64 targets. The hardening mechanism described here is specific to pointer authentication of return addresses.
Return Address Signing (or Authentication), also known as pac-ret, is a feature devised to protect programs against Return Oriented Programming (ROP), in which attackers may hijack the return address of functions in order to direct execution to malicious code.
pac-ret can be enabled via different command-line options:
-mbranch-protection=withpac-ret,pac-ret+leaforstandardas value.-msign-return-address=withnon-leaforallas value.-fptrauth-returns.
More information can be found in Pointer Authentication.
Return Address Authentication Hardening is a mechanism to strengthen
Return Address Signing against the PACMAN attack in AArch64 targets. It
can be enabled with -mharden-pac-ret=load-return-address.
PACMAN attack#
PACMAN is an attack that aims to extract valuable information about pointer authentication codes using side-channels in speculative execution.
It is performed with the use of gadgets to try and guess PAC codes. These guesses raise no faults because they are done in speculation. By observing the effects of the guessed PAC code on the processor’s cache, it might be possible to determine the valid PAC code for the address to which the attacker wants the program to return.
A usual PACMAN gadget looks like this:
void function() {
...
if (condition)
return;
...
}
Such code would be compiled to:
paciasp
...
cbz w0, .LBB0_2
autiasp
ret
...
This code fragment may be used as a gadget. A speculative execution of this fragment can happen as follows:
If the Link Register (LR) has the right PAC code,
autiaspwill succeed and strip the PAC code out of it. The processor’s instruction fetcher will then bring the code after the return into the cache (that is, the instructions located at the address pointed by LR).If the LR has the wrong PAC code,
autiaspwill not succeed and hence will write a predefined error value to the LR’s higher bits. Because of this, the instruction fetcher will not bring the code after the return into the cache.
This difference in behavior is what drives the PACMAN attack. An attacker can try to guess PAC codes and monitor cache behavior until the code after the return is observed to have been brought into the cache.
Details can be found at pacmanattack.com.
Hardening#
In order to mitigate the PACMAN attack, a hardening mechanism can be
enabled with -mharden-pac-ret=load-return-address.
paciasp
...
cbz w0, .LBB0_2
autiasp
mov x8, x30
xpaclri
ldr w30, [x30]
ret x8
...
The idea is to always bring the code after the return into cache (the instructions located at the address pointed by LR), therefore minimizing the difference between a speculative execution with a correct PAC code and with an incorrect one.
autiaspperforms the authentication step.mov x8, x30copies the return address (LR and x30 are synonyms) to a temporary.xpaclristrips the PAC code out of the return address in x30.ldr w30, [x30]performs a load from the return address in x30.ret x8returns to the authenticated return address.
The load operation brings the code into the cache even if the authentication step fails. As a consequence, in either case the code is loaded into the cache. Furthermore, the return operation uses the original return address before stripping, so the return address protection is still kept in place in a normal non-speculative execution.
If FEAT_PAuth is present, the code sequence can make use of instructions available with said feature with no change in semantics:
autiasp
mov x8, x30
xpaci x8
ldr w8, [x8]
ret
Command-line option#
Return address authentication hardening can be enabled at translation
unit level with -mharden-pac-ret=load-return-address. It requires
pac-ret to be enabled at translation unit level as well.
Function attribute#
In addition to the command-line option
-mharden-pac-ret=load-return-address, the developer can enable the
mitigation at function level with the use of the corresponding function
attribute.
__attribute__((target(
"branch-protection=pac-ret,harden-pac-ret=load-return-address")))
void function() {
...
}
Caveats#
The load from the return address brings the code into the shared instruction/data cache, therefore this cache level can’t be used as an oracle to find out whether the authentication succeeded or not. However, in the case of authentication success, the code is also fetched into the instruction cache. An attacker who is able to measure this cache level specifically may still be able to carry out the exploit.
Another caveat is if the code at the return address contains a load operation within the speculation window. If this is the case, this load will only execute speculatively if authentication succeeds, thus opening the program up for exploitation despite the mitigation.