This post isn’t a formal RFC, it has been written to flush out options and their trade offs. Alternatives, comments and suggestions welcome.
The Arm v8.3-A extension FEAT_PAuth (for pointer authentication) permits pointers to be signed and authenticated. In ELF targets a subset of this extension called PACRET is used when the -mbranch-protection=standard (or pac-ret). PACRET signs and authenticates the return address. It can be implemented using instructions in the hint space so it can be deployed on CPUs without the extension, and excluding stack-unwinding is ABI neutral.
Pointer authentication can be used for much more than just the return address, it can be extended to other pointers such as code-pointers or even data-pointers. Apple’s ARM64E ABI has pioneered this concept. For more details on ARM64E I recommend the the talk at the 2019 US Dev meeting (see references).
The decision on which pointers to sign and how they are signed is known as a signing-schema. There are many possible schemas each with a different set of trade-offs. To a first approximation every signing-schema is its own incompatible ABI.
ELF PAuthABI command line options
The Apple ARM64E ABI is enabled using the architecture part of the target, for example --target=arm64e-blah-blah
For non Apple targets this approach doesn’t fit well with the existing AArch64 conventions and options. For example the architecture part of the triple on AArch64 ELF platforms is used to enable hardware features and is orthogonal to software ABI. This post is an attempt to list some options and some trade-offs.
Our compatibility model for PAuthABI in ELF is coarse grained (see pauthabielf64 in references). We expect there to be a platform namespace so that Linux/Android/*BSD/Bare-metal can choose the signing schema for their platform. On each namespace there will be a coarse grained signing schema version number, with 0 meaning no signing. A signing schema is therefore identified by the tuple (platform-id, signing-schema-version).
Our command line options need to derive the information needed to write the tuple.
We expect that the number of tuples to be low. In a PAuthABI in system software entirely controlled by the platform owner (also most embedded systems) will be changeable over time as the platform owner can ensure that all software is built with a consistent ABI. It is possible that a system signing schema could evolve over time using the same version number. Any attempt to roll this out to user-space will need a stable ABI or a very understanding user base!
Enabling PAuthABI and setting the signing schema
Using the target triple is possible, for example the hard-float ABI in AArch32 is recorded in the triple with EABIHF rather than EABI. We could have a triple with -pauthabi in it. We don’t think that this is a particularly good option as while there is only one hard-float procedure call standard, there can be multiple signing schemas.
AArch64 already has the -mbranch-protection= which enables pac-ret when options include standard or pac-ret. There are some further tweaks to the ABI such as +bkey for pac-ret to use the B key rather than the A-Key. We can consider pac-ret and pac-ret+bkey as two existing signing schemas.
The first option is to add options to -mbranch-protection. With each signing schema version being an individually named option for example -mbranch-protection=pauthabi1 with a later version having pauthabi2. This would override pac-ret or pac-ret+bkey if both were present.
Using -mbranch-protection has the advantage of reusing the existing option for hardware control-flow integrity features (consistent with most likely use of PAuthABI to protect code but not data pointers). A potential downside is that the existing uses of -mbranch-protection are (mostly) in the hint space, PAuthABI would require -march=armv8.3-a or higher. We would need to give an error message if the hardware requirements weren’t met.
An alternative is to add another command line -msigning-schema (with 0 meaning none) which can be given independently of branch-protection, but interacts with it. This might have some advantages if using branch-protection gets too confusing but then we have to work out what happens when both options are used at once which may be likely if there is a platform default -mbranch-protection.
Obtaining the platform
The existing target triple has an entry for the platform, and to a first approximation this can be used to identify platforms such as linux and Android. If PAuthABI is a niche feature then this will be sufficient, it may break down on platforms such as bare-metal or even linux, where there is no clear platform owner to mandate a signing schema. In this case a command line option like -mpauthabi-platform= can be used to override it. It is possible that -mpauth-platform only becomes necessary at a later time.
Author’s personal preference at time of writing
The signing schema is added to the existing -mbranch-protection option
The platform is inferred from the target triple by default. If needed we add a -mpauthabi-platform= but this may not be required, and probably shouldn’t be unless the target triple proves insufficient.
An Alpha specification for applying PAuthABI to ELF is available at https://github.com/ARM-software/abi-aa/tree/main/pauthabielf64
The Clang Apple documentation for ARM64E can be found at https://github.com/apple/llvm-project/blob/next/clang/docs/PointerAuthentication.rst (best reference that I know of).
2019 US LLVM Developer Meeting Presentation https://www.youtube.com/watch?v=C1nZvpEBfYA