1# Pointer Authentication 2 3## Introduction 4 5Pointer Authentication is a mechanism by which certain pointers are signed. 6When a pointer gets signed, a cryptographic hash of its value and other values 7(pepper and salt) is stored in unused bits of that pointer. 8 9Before the pointer is used, it needs to be authenticated, i.e., have its 10signature checked. This prevents pointer values of unknown origin from being 11used to replace the signed pointer value. 12 13At the IR level, it is represented using: 14 15* a [set of intrinsics](#intrinsics) (to sign/authenticate pointers) 16* a [call operand bundle](#operand-bundle) (to authenticate called pointers) 17 18The current implementation leverages the 19[Armv8.3-A PAuth/Pointer Authentication Code](#armv8-3-a-pauth-pointer-authentication-code) 20instructions in the [AArch64 backend](#aarch64-support). 21This support is used to implement the Darwin arm64e ABI, as well as the 22[PAuth ABI Extension to ELF](https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst). 23 24 25## LLVM IR Representation 26 27### Intrinsics 28 29These intrinsics are provided by LLVM to expose pointer authentication 30operations. 31 32 33#### '``llvm.ptrauth.sign``' 34 35##### Syntax: 36 37```llvm 38declare i64 @llvm.ptrauth.sign(i64 <value>, i32 <key>, i64 <discriminator>) 39``` 40 41##### Overview: 42 43The '``llvm.ptrauth.sign``' intrinsic signs a raw pointer. 44 45 46##### Arguments: 47 48The ``value`` argument is the raw pointer value to be signed. 49The ``key`` argument is the identifier of the key to be used to generate the 50signed value. 51The ``discriminator`` argument is the additional diversity data to be used as a 52discriminator (an integer, an address, or a blend of the two). 53 54##### Semantics: 55 56The '``llvm.ptrauth.sign``' intrinsic implements the `sign`_ operation. 57It returns a signed value. 58 59If ``value`` is already a signed value, the behavior is undefined. 60 61If ``value`` is not a pointer value for which ``key`` is appropriate, the 62behavior is undefined. 63 64 65#### '``llvm.ptrauth.auth``' 66 67##### Syntax: 68 69```llvm 70declare i64 @llvm.ptrauth.auth(i64 <value>, i32 <key>, i64 <discriminator>) 71``` 72 73##### Overview: 74 75The '``llvm.ptrauth.auth``' intrinsic authenticates a signed pointer. 76 77##### Arguments: 78 79The ``value`` argument is the signed pointer value to be authenticated. 80The ``key`` argument is the identifier of the key that was used to generate 81the signed value. 82The ``discriminator`` argument is the additional diversity data to be used as a 83discriminator. 84 85##### Semantics: 86 87The '``llvm.ptrauth.auth``' intrinsic implements the `auth`_ operation. 88It returns a raw pointer value. 89If ``value`` does not have a correct signature for ``key`` and ``discriminator``, 90the intrinsic traps in a target-specific way. 91 92 93#### '``llvm.ptrauth.strip``' 94 95##### Syntax: 96 97```llvm 98declare i64 @llvm.ptrauth.strip(i64 <value>, i32 <key>) 99``` 100 101##### Overview: 102 103The '``llvm.ptrauth.strip``' intrinsic strips the embedded signature out of a 104possibly-signed pointer. 105 106 107##### Arguments: 108 109The ``value`` argument is the signed pointer value to be stripped. 110The ``key`` argument is the identifier of the key that was used to generate 111the signed value. 112 113##### Semantics: 114 115The '``llvm.ptrauth.strip``' intrinsic implements the `strip`_ operation. 116It returns a raw pointer value. It does **not** check that the 117signature is valid. 118 119``key`` should identify a key that is appropriate for ``value``, as defined 120by the target-specific [keys](#key)). 121 122If ``value`` is a raw pointer value, it is returned as-is (provided the ``key`` 123is appropriate for the pointer). 124 125If ``value`` is not a pointer value for which ``key`` is appropriate, the 126behavior is target-specific. 127 128If ``value`` is a signed pointer value, but ``key`` does not identify the 129same key that was used to generate ``value``, the behavior is 130target-specific. 131 132 133#### '``llvm.ptrauth.resign``' 134 135##### Syntax: 136 137```llvm 138declare i64 @llvm.ptrauth.resign(i64 <value>, 139 i32 <old key>, i64 <old discriminator>, 140 i32 <new key>, i64 <new discriminator>) 141``` 142 143##### Overview: 144 145The '``llvm.ptrauth.resign``' intrinsic re-signs a signed pointer using 146a different key and diversity data. 147 148##### Arguments: 149 150The ``value`` argument is the signed pointer value to be authenticated. 151The ``old key`` argument is the identifier of the key that was used to generate 152the signed value. 153The ``old discriminator`` argument is the additional diversity data to be used 154as a discriminator in the auth operation. 155The ``new key`` argument is the identifier of the key to use to generate the 156resigned value. 157The ``new discriminator`` argument is the additional diversity data to be used 158as a discriminator in the sign operation. 159 160##### Semantics: 161 162The '``llvm.ptrauth.resign``' intrinsic performs a combined `auth`_ and `sign`_ 163operation, without exposing the intermediate raw pointer. 164It returns a signed pointer value. 165If ``value`` does not have a correct signature for ``old key`` and 166``old discriminator``, the intrinsic traps in a target-specific way. 167 168#### '``llvm.ptrauth.sign_generic``' 169 170##### Syntax: 171 172```llvm 173declare i64 @llvm.ptrauth.sign_generic(i64 <value>, i64 <discriminator>) 174``` 175 176##### Overview: 177 178The '``llvm.ptrauth.sign_generic``' intrinsic computes a generic signature of 179arbitrary data. 180 181##### Arguments: 182 183The ``value`` argument is the arbitrary data value to be signed. 184The ``discriminator`` argument is the additional diversity data to be used as a 185discriminator. 186 187##### Semantics: 188 189The '``llvm.ptrauth.sign_generic``' intrinsic computes the signature of a given 190combination of value and additional diversity data. 191 192It returns a full signature value (as opposed to a signed pointer value, with 193an embedded partial signature). 194 195As opposed to [``llvm.ptrauth.sign``](#llvm-ptrauth-sign), it does not interpret 196``value`` as a pointer value. Instead, it is an arbitrary data value. 197 198 199#### '``llvm.ptrauth.blend``' 200 201##### Syntax: 202 203```llvm 204declare i64 @llvm.ptrauth.blend(i64 <address discriminator>, i64 <integer discriminator>) 205``` 206 207##### Overview: 208 209The '``llvm.ptrauth.blend``' intrinsic blends a pointer address discriminator 210with a small integer discriminator to produce a new "blended" discriminator. 211 212##### Arguments: 213 214The ``address discriminator`` argument is a pointer value. 215The ``integer discriminator`` argument is a small integer, as specified by the 216target. 217 218##### Semantics: 219 220The '``llvm.ptrauth.blend``' intrinsic combines a small integer discriminator 221with a pointer address discriminator, in a way that is specified by the target 222implementation. 223 224 225### Operand Bundle 226 227Function pointers used as indirect call targets can be signed when materialized, 228and authenticated before calls. This can be accomplished with the 229[``llvm.ptrauth.auth``](#llvm-ptrauth-auth) intrinsic, feeding its result to 230an indirect call. 231 232However, that exposes the intermediate, unauthenticated pointer, e.g., if it 233gets spilled to the stack. An attacker can then overwrite the pointer in 234memory, negating the security benefit provided by pointer authentication. 235To prevent that, the ``ptrauth`` operand bundle may be used: it guarantees that 236the intermediate call target is kept in a register and never stored to memory. 237This hardening benefit is similar to that provided by 238[``llvm.ptrauth.resign``](#llvm-ptrauth-resign)). 239 240Concretely: 241 242```llvm 243define void @f(void ()* %fp) { 244 call void %fp() [ "ptrauth"(i32 <key>, i64 <data>) ] 245 ret void 246} 247``` 248 249is functionally equivalent to: 250 251```llvm 252define void @f(void ()* %fp) { 253 %fp_i = ptrtoint void ()* %fp to i64 254 %fp_auth = call i64 @llvm.ptrauth.auth(i64 %fp_i, i32 <key>, i64 <data>) 255 %fp_auth_p = inttoptr i64 %fp_auth to void ()* 256 call void %fp_auth_p() 257 ret void 258} 259``` 260 261but with the added guarantee that ``%fp_i``, ``%fp_auth``, and ``%fp_auth_p`` 262are not stored to (and reloaded from) memory. 263 264 265## AArch64 Support 266 267AArch64 is currently the only architecture with full support of the pointer 268authentication primitives, based on Armv8.3-A instructions. 269 270### Armv8.3-A PAuth Pointer Authentication Code 271 272The Armv8.3-A architecture extension defines the PAuth feature, which provides 273support for instructions that manipulate Pointer Authentication Codes (PAC). 274 275#### Keys 276 2775 keys are supported by the PAuth feature. 278 279Of those, 4 keys are interchangeably usable to specify the key used in IR 280constructs: 281* ``ASIA``/``ASIB`` are instruction keys (encoded as respectively 0 and 1). 282* ``ASDA``/``ASDB`` are data keys (encoded as respectively 2 and 3). 283 284``ASGA`` is a special key that cannot be explicitly specified, and is only ever 285used implicitly, to implement the 286[``llvm.ptrauth.sign_generic``](#llvm-ptrauth-sign-generic) intrinsic. 287 288#### Instructions 289 290The IR [Intrinsics](#intrinsics) described above map onto these 291instructions as such: 292* [``llvm.ptrauth.sign``](#llvm-ptrauth-sign): ``PAC{I,D}{A,B}{Z,SP,}`` 293* [``llvm.ptrauth.auth``](#llvm-ptrauth-auth): ``AUT{I,D}{A,B}{Z,SP,}`` 294* [``llvm.ptrauth.strip``](#llvm-ptrauth-strip): ``XPAC{I,D}`` 295* [``llvm.ptrauth.blend``](#llvm-ptrauth-blend): The semantics of the blend 296 operation are specified by the ABI. In both the ELF PAuth ABI Extension and 297 arm64e, it's a ``MOVK`` into the high 16 bits. Consequently, this limits 298 the width of the integer discriminator used in blends to 16 bits. 299* [``llvm.ptrauth.sign_generic``](#llvm-ptrauth-sign-generic): ``PACGA`` 300* [``llvm.ptrauth.resign``](#llvm-ptrauth-resign): ``AUT*+PAC*``. These are 301 represented as a single pseudo-instruction in the backend to guarantee that 302 the intermediate raw pointer value is not spilled and attackable. 303