Pointer Authentication

Introduction

Pointer Authentication is a mechanism by which certain pointers are signed.When a pointer gets signed, a cryptographic hash of its value and other values(pepper and salt) is stored in unused bits of that pointer.

Before the pointer is used, it needs to be authenticated, i.e., have itssignature checked. This prevents pointer values of unknown origin from beingused to replace the signed pointer value.

For more details, see the clang documentation page forPointer Authentication.

At the IR level, it is represented using:

The current implementation leverages theArmv8.3-A PAuth/Pointer Authentication Codeinstructions in theAArch64 backend.This support is used to implement the Darwin arm64e ABI, as well as thePAuth ABI Extension to ELF.

LLVM IR Representation

Intrinsics

These intrinsics are provided by LLVM to expose pointer authenticationoperations.

llvm.ptrauth.sign

Syntax:
declarei64@llvm.ptrauth.sign(i64<value>,i32<key>,i64<discriminator>)
Overview:

The ‘llvm.ptrauth.sign’ intrinsic signs a raw pointer.

Arguments:

Thevalue argument is the raw pointer value to be signed.Thekey argument is the identifier of the key to be used to generate thesigned value.Thediscriminator argument is the additional diversity data to be used as adiscriminator (an integer, an address, or a blend of the two).

Semantics:

The ‘llvm.ptrauth.sign’ intrinsic implements thesign_ operation.It returns a signed value.

Ifvalue is already a signed value, the behavior is undefined.

Ifvalue is not a pointer value for whichkey is appropriate, thebehavior is undefined.

llvm.ptrauth.auth

Syntax:
declarei64@llvm.ptrauth.auth(i64<value>,i32<key>,i64<discriminator>)
Overview:

The ‘llvm.ptrauth.auth’ intrinsic authenticates a signed pointer.

Arguments:

Thevalue argument is the signed pointer value to be authenticated.Thekey argument is the identifier of the key that was used to generatethe signed value.Thediscriminator argument is the additional diversity data to be used as adiscriminator.

Semantics:

The ‘llvm.ptrauth.auth’ intrinsic implements theauth_ operation.It returns a raw pointer value.Ifvalue does not have a correct signature forkey anddiscriminator,the intrinsic traps in a target-specific way.

llvm.ptrauth.strip

Syntax:
declarei64@llvm.ptrauth.strip(i64<value>,i32<key>)
Overview:

The ‘llvm.ptrauth.strip’ intrinsic strips the embedded signature out of apossibly-signed pointer.

Arguments:

Thevalue argument is the signed pointer value to be stripped.Thekey argument is the identifier of the key that was used to generatethe signed value.

Semantics:

The ‘llvm.ptrauth.strip’ intrinsic implements thestrip_ operation.It returns a raw pointer value. It doesnot check that thesignature is valid.

key should identify a key that is appropriate forvalue, as definedby the target-specifickeys).

Ifvalue is a raw pointer value, it is returned as-is (provided thekeyis appropriate for the pointer).

Ifvalue is not a pointer value for whichkey is appropriate, thebehavior is target-specific.

Ifvalue is a signed pointer value, butkey does not identify thesame key that was used to generatevalue, the behavior istarget-specific.

llvm.ptrauth.resign

Syntax:
declarei64@llvm.ptrauth.resign(i64<value>,i32<oldkey>,i64<olddiscriminator>,i32<newkey>,i64<newdiscriminator>)
Overview:

The ‘llvm.ptrauth.resign’ intrinsic re-signs a signed pointer usinga different key and diversity data.

Arguments:

Thevalue argument is the signed pointer value to be authenticated.Theoldkey argument is the identifier of the key that was used to generatethe signed value.Theolddiscriminator argument is the additional diversity data to be usedas a discriminator in the auth operation.Thenewkey argument is the identifier of the key to use to generate theresigned value.Thenewdiscriminator argument is the additional diversity data to be usedas a discriminator in the sign operation.

Semantics:

The ‘llvm.ptrauth.resign’ intrinsic performs a combinedauth_ andsign_operation, without exposing the intermediate raw pointer.It returns a signed pointer value.Ifvalue does not have a correct signature foroldkey andolddiscriminator, the intrinsic traps in a target-specific way.

llvm.ptrauth.sign_generic

Syntax:
declarei64@llvm.ptrauth.sign_generic(i64<value>,i64<discriminator>)
Overview:

The ‘llvm.ptrauth.sign_generic’ intrinsic computes a generic signature ofarbitrary data.

Arguments:

Thevalue argument is the arbitrary data value to be signed.Thediscriminator argument is the additional diversity data to be used as adiscriminator.

Semantics:

The ‘llvm.ptrauth.sign_generic’ intrinsic computes the signature of a givencombination of value and additional diversity data.

It returns a full signature value (as opposed to a signed pointer value, withan embedded partial signature).

As opposed tollvm.ptrauth.sign, it does not interpretvalue as a pointer value. Instead, it is an arbitrary data value.

llvm.ptrauth.blend

Syntax:
declarei64@llvm.ptrauth.blend(i64<addressdiscriminator>,i64<integerdiscriminator>)
Overview:

The ‘llvm.ptrauth.blend’ intrinsic blends a pointer address discriminatorwith a small integer discriminator to produce a new “blended” discriminator.

Arguments:

Theaddressdiscriminator argument is a pointer value.Theintegerdiscriminator argument is a small integer, as specified by thetarget.

Semantics:

The ‘llvm.ptrauth.blend’ intrinsic combines a small integer discriminatorwith a pointer address discriminator, in a way that is specified by the targetimplementation.

Constant

Intrinsics can be used to produce signed pointers dynamically,in code, but not for signed pointers referenced by constants, in, e.g., globalinitializers.

The latter are represented using aptrauth constant,which describes an authenticated relocation producing a signed pointer.

ptrauth(ptrCST,i32KEY,i64DISC,ptrADDRDISC)

is equivalent to:

%disc=calli64@llvm.ptrauth.blend(i64ptrtoint(ptrADDRDISCtoi64),i64DISC)%signedval=calli64@llvm.ptrauth.sign(ptrCST,i32KEY,i64%disc)

Operand Bundle

Function pointers used as indirect call targets can be signed when materialized,and authenticated before calls. This can be accomplished with thellvm.ptrauth.auth intrinsic, feeding its result toan indirect call.

However, that exposes the intermediate, unauthenticated pointer, e.g., if itgets spilled to the stack. An attacker can then overwrite the pointer inmemory, negating the security benefit provided by pointer authentication.To prevent that, theptrauth operand bundle may be used: it guarantees thatthe intermediate call target is kept in a register and never stored to memory.This hardening benefit is similar to that provided byllvm.ptrauth.resign).

Concretely:

definevoid@f(void()*%fp){callvoid%fp()["ptrauth"(i32<key>,i64<data>)]retvoid}

is functionally equivalent to:

definevoid@f(void()*%fp){%fp_i=ptrtointvoid()*%fptoi64%fp_auth=calli64@llvm.ptrauth.auth(i64%fp_i,i32<key>,i64<data>)%fp_auth_p=inttoptri64%fp_authtovoid()*callvoid%fp_auth_p()retvoid}

but with the added guarantee that%fp_i,%fp_auth, and%fp_auth_pare not stored to (and reloaded from) memory.

Function Attributes

Some function attributes are used to describe other pointer authenticationoperations that are not otherwise explicitly expressed in IR.

ptrauth-indirect-gotos

ptrauth-indirect-gotos specifies that indirect gotos in this functionshould authenticate their target. At the IR level, no other change is needed.When loweringblockaddress constants,andindirectbr instructions,this tells the backend to respectively sign and authenticate the pointers.

The specific scheme isn’t ABI-visible. Currently, the AArch64 backendsigns blockaddresses using theASIA key, with an integer discriminatorderived from the parent function’s name, using the SipHash stable discriminator:

ptrauth_string_discriminator("<function_name> blockaddress")

AArch64 Support

AArch64 is currently the only architecture with full support of the pointerauthentication primitives, based on Armv8.3-A instructions.

Armv8.3-A PAuth Pointer Authentication Code

The Armv8.3-A architecture extension defines the PAuth feature, which providessupport for instructions that manipulate Pointer Authentication Codes (PAC).

Keys

5 keys are supported by the PAuth feature.

Of those, 4 keys are interchangeably usable to specify the key used in IRconstructs:

  • ASIA/ASIB are instruction keys (encoded as respectively 0 and 1).

  • ASDA/ASDB are data keys (encoded as respectively 2 and 3).

ASGA is a special key that cannot be explicitly specified, and is only everused implicitly, to implement thellvm.ptrauth.sign_generic intrinsic.

Instructions

The IRIntrinsics described above map onto theseinstructions as such:

  • llvm.ptrauth.sign:PAC{I,D}{A,B}{Z,SP,}

  • llvm.ptrauth.auth:AUT{I,D}{A,B}{Z,SP,}

  • llvm.ptrauth.strip:XPAC{I,D}

  • llvm.ptrauth.blend: The semantics of the blendoperation are specified by the ABI. In both the ELF PAuth ABI Extension andarm64e, it’s aMOVK into the high 16 bits. Consequently, this limitsthe width of the integer discriminator used in blends to 16 bits.

  • llvm.ptrauth.sign_generic:PACGA

  • llvm.ptrauth.resign:AUT*+PAC*. These arerepresented as a single pseudo-instruction in the backend to guarantee thatthe intermediate raw pointer value is not spilled and attackable.

Assembly Representation

At the assembly level, authenticated relocations are representedusing the@AUTH modifier:

.quad_target@AUTH(<key>,<discriminator>[,addr])

where:

  • key is the Armv8.3-A key identifier (ia,ib,da,db)

  • discriminator is the 16-bit unsigned discriminator value

  • addr signifies that the authenticated pointer is address-discriminated(that is, that the relocation’s target address is to be blended into thediscriminator before it is used in the sign operation.

For example:

_authenticated_reference_to_sym:.quad_sym@AUTH(db,0)_authenticated_reference_to_sym_addr_disc:.quad_sym@AUTH(ia,12,addr)

MachO Object File Representation

At the object file level, authenticated relocations are represented using theARM64_RELOC_AUTHENTICATED_POINTER relocation kind (with value11).

The pointer authentication information is encoded into the addend as follows:

|63|62|61-51|50-49|48|47-32|31-0||--|--|-----|-----|------|---------------|--------||1|0|0|key|addr|discriminator|addend|

ELF Object File Representation

At the object file level, authenticated relocations are representedusing theR_AARCH64_AUTH_ABS64 relocation kind (with value0xE100).

The signing schema is encoded in the place of relocation to be appliedas follows:

|63|62|61:60|59:48|47:32|31:0||-----------------|--------|--------|--------|-------------|-------------------||addressdiversity|reserved|key|reserved|discriminator|reservedforaddend|