RFC: Emulated PAC

Consider a large heterogeneous fleet of AArch64 machines with various microarchitectures, only some of which support pointer authentication. We would like to be able to use these machines for CI testing of programs that will be deployed using pointer authentication features, such as PAuth ABI and pointer field protection. By disabling pointer authentication features in test binaries, or by using the hint variants of the pointer authentication instructions during testing, we create the possibility of bugs causing incompatibilities with these features not being caught in testing before production deployment. And by restricting testing to a subset of machines with pointer authentication support we lose some CI capacity especially as these features become more widely adopted.

To address these problems, it is proposed to provide emulations of the pointer authentication instructions that may be used on the machines without pointer authentication support, so that the test programs are not restricted by microarchitecture and are more or less functionally equivalent in terms of bug finding capability whether run on a pointer authentication supporting machine or not. The idea is that when targeting the CI machines, programs are built without -march flags, but when targeting production, programs are built with a -march that includes pointer authentication support such as -march=armv8.3a.

The proposal is to add emulation of the PAC instructions to compiler-rt. The PAC emulation functions will use regular PAC instructions if PAC is supported, or the emulation if not. Each instruction will be implemented using a separate function. For the time being, only the following functions are implemented, as this is sufficient for the PFP implementation:

  • __emupac_pacda: PACDA
  • __emupac_autda: AUTDA

The emulations will implement an IMPDEF variant of the hashing scheme that is based on XXH128. As such, the xxhash.h header will be added to compiler-rt. Alternatively, we may add the header under third-party and eventually move llvm/lib/Support/xxhash.cpp over to using it instead of its own copy of the code.

The emulations will make the following assumptions:

  • TBI enabled (conservatively correct if TBI disabled).
  • 48-bit VA size (conservatively correct if < 48-bit VA size). The architecture as of ARMv8.2, which was the last architecture version in which PAC was not mandatory, permitted VA size up to 52 bits via ARMv8.2-LVA, but we are unaware of an ARMv8.2 CPU that implemented ARMv8.2-LVA.

Because calling the emulated PAC functions requires saving and restoring registers for the call, constraints on registers used for the arguments and return value as well as checks for PAC support inside the functions, not to mention the emulation itself, they are much slower than regular PAC instructions. In one experiment on an Apple M2 Ultra involving the Fleetbench benchmark known as BM_RPC_Fleet/process_time, which is the Fleetbench benchmark that is most highly affected by PFP, there was a 16% perf hit with -march=armv8a and 38% hit with -march=armv8a and a runtime library hacked to force the non-PAC branch. However, this is less important for the use case, which is more concerned with functionality than performance.

Emulated PAC is implemented as part of the PFP pull request here.

1 Like

To clarify: you mean that you would have:

  • -march and pointer field protection enabled
  • -march=armv8.3-a and pointer field protection enabled

So existing “default” AArch64 Arvm8-a builds are unchanged, and -march=armv8.3-a without pointer field protection would not call into these emulation routines at all.

Correct?

(I assume this was meant to read -march=armv8-a.)

Correct. We may also want to call into the emulated PAC functions when PAuth ABI is enabled and building with -march=armv8-a (for similar reasons), but that’s not implemented yet.

Yes that’s right. Thanks for clarifying.