diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -17616,6 +17616,13 @@ ``llvm.eh.`` prefix), are described in the `LLVM Exception Handling `_ document. +Pointer Authentication Intrinsics +--------------------------------- + +The LLVM pointer authentication intrinsics (which all start with +``llvm.ptrauth.`` prefix), are described in the `Pointer Authentication +`_ document. + .. _int_trampoline: Trampoline Intrinsics diff --git a/llvm/docs/PointerAuth.md b/llvm/docs/PointerAuth.md new file mode 100644 --- /dev/null +++ b/llvm/docs/PointerAuth.md @@ -0,0 +1,260 @@ +# 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 its +signature checked. This prevents pointer values of unknown origin from being +used to replace the signed pointer value. + +At the IR level, it is represented using a [set of intrinsics](#intrinsics) +(to sign/authenticate pointers). + +The current implementation leverages the +[Armv8.3-A PAuth/Pointer Authentication Code](#armv8-3-a-pauth-pointer-authentication-code) +instructions in the [AArch64 backend](#aarch64-support). +This support is used to implement the Darwin arm64e ABI, as well as the +[PAuth ABI Extension to ELF](https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst). + + +## LLVM IR Representation + +### Intrinsics + +These intrinsics are provided by LLVM to expose pointer authentication +operations. + + +#### '``llvm.ptrauth.sign``' + +##### Syntax: + +```llvm +declare i64 @llvm.ptrauth.sign(i64 , i32 , i64 ) +``` + +##### Overview: + +The '``llvm.ptrauth.sign``' intrinsic signs a raw pointer. + + +##### Arguments: + +The ``value`` argument is the raw pointer value to be signed. +The ``key`` argument is the identifier of the key to be used to generate the +signed value. +The ``discriminator`` argument is the additional diversity data to be used as a +discriminator (an integer, an address, or a blend of the two). + +##### Semantics: + +The '``llvm.ptrauth.sign``' intrinsic implements the `sign`_ operation. +It returns a signed value. + +If ``value`` is already a signed value, the behavior is undefined. + +If ``value`` is not a pointer value for which ``key`` is appropriate, the +behavior is undefined. + + +#### '``llvm.ptrauth.auth``' + +##### Syntax: + +```llvm +declare i64 @llvm.ptrauth.auth(i64 , i32 , i64 ) +``` + +##### Overview: + +The '``llvm.ptrauth.auth``' intrinsic authenticates a signed pointer. + +##### Arguments: + +The ``value`` argument is the signed pointer value to be authenticated. +The ``key`` argument is the identifier of the key that was used to generate +the signed value. +The ``discriminator`` argument is the additional diversity data to be used as a +discriminator. + +##### Semantics: + +The '``llvm.ptrauth.auth``' intrinsic implements the `auth`_ operation. +It returns a raw pointer value. +If ``value`` does not have a correct signature for ``key`` and ``discriminator``, +the intrinsic traps in a target-specific way. + + +#### '``llvm.ptrauth.strip``' + +##### Syntax: + +```llvm +declare i64 @llvm.ptrauth.strip(i64 , i32 ) +``` + +##### Overview: + +The '``llvm.ptrauth.strip``' intrinsic strips the embedded signature out of a +possibly-signed pointer. + + +##### Arguments: + +The ``value`` argument is the signed pointer value to be stripped. +The ``key`` argument is the identifier of the key that was used to generate +the signed value. + +##### Semantics: + +The '``llvm.ptrauth.strip``' intrinsic implements the `strip`_ operation. +It returns a raw pointer value. It does **not** check that the +signature is valid. + +``key`` should identify a key that is appropriate for ``value``, as defined +by the target-specific [keys](#key)). + +If ``value`` is a raw pointer value, it is returned as-is (provided the ``key`` +is appropriate for the pointer). + +If ``value`` is not a pointer value for which ``key`` is appropriate, the +behavior is target-specific. + +If ``value`` is a signed pointer value, but ``key`` does not identify the +same key that was used to generate ``value``, the behavior is +target-specific. + + +#### '``llvm.ptrauth.resign``' + +##### Syntax: + +```llvm +declare i64 @llvm.ptrauth.resign(i64 , + i32 , i64 , + i32 , i64 ) +``` + +##### Overview: + +The '``llvm.ptrauth.resign``' intrinsic re-signs a signed pointer using +a different key and diversity data. + +##### Arguments: + +The ``value`` argument is the signed pointer value to be authenticated. +The ``old key`` argument is the identifier of the key that was used to generate +the signed value. +The ``old discriminator`` argument is the additional diversity data to be used +as a discriminator in the auth operation. +The ``new key`` argument is the identifier of the key to use to generate the +resigned value. +The ``new discriminator`` argument is the additional diversity data to be used +as a discriminator in the sign operation. + +##### Semantics: + +The '``llvm.ptrauth.resign``' intrinsic performs a combined `auth`_ and `sign`_ +operation, without exposing the intermediate raw pointer. +It returns a signed pointer value. +If ``value`` does not have a correct signature for ``old key`` and +``old discriminator``, the intrinsic traps in a target-specific way. + +#### '``llvm.ptrauth.sign_generic``' + +##### Syntax: + +```llvm +declare i64 @llvm.ptrauth.sign_generic(i64 , i64 ) +``` + +##### Overview: + +The '``llvm.ptrauth.sign_generic``' intrinsic computes a generic signature of +arbitrary data. + +##### Arguments: + +The ``value`` argument is the arbitrary data value to be signed. +The ``discriminator`` argument is the additional diversity data to be used as a +discriminator. + +##### Semantics: + +The '``llvm.ptrauth.sign_generic``' intrinsic computes the signature of a given +combination of value and additional diversity data. + +It returns a full signature value (as opposed to a signed pointer value, with +an embedded partial signature). + +As opposed to [``llvm.ptrauth.sign``](#llvm-ptrauth-sign), it does not interpret +``value`` as a pointer value. Instead, it is an arbitrary data value. + + +#### '``llvm.ptrauth.blend``' + +##### Syntax: + +```llvm +declare i64 @llvm.ptrauth.blend(i64
, i64 ) +``` + +##### Overview: + +The '``llvm.ptrauth.blend``' intrinsic blends a pointer address discriminator +with a small integer discriminator to produce a new "blended" discriminator. + +##### Arguments: + +The ``address discriminator`` argument is a pointer value. +The ``integer discriminator`` argument is a small integer, as specified by the +target. + +##### Semantics: + +The '``llvm.ptrauth.blend``' intrinsic combines a small integer discriminator +with a pointer address discriminator, in a way that is specified by the target +implementation. + + +## AArch64 Support + +AArch64 is currently the only architecture with full support of the pointer +authentication 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 provides +support 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 IR +constructs: +* ``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 ever +used implicitly, to implement the +[``llvm.ptrauth.sign_generic``](#llvm-ptrauth-sign-generic) intrinsic. + +#### Instructions + +The IR [Intrinsics](#intrinsics) described above map onto these +instructions as such: +* [``llvm.ptrauth.sign``](#llvm-ptrauth-sign): ``PAC{I,D}{A,B}{Z,SP,}`` +* [``llvm.ptrauth.auth``](#llvm-ptrauth-auth): ``AUT{I,D}{A,B}{Z,SP,}`` +* [``llvm.ptrauth.strip``](#llvm-ptrauth-strip): ``XPAC{I,D}`` +* [``llvm.ptrauth.blend``](#llvm-ptrauth-blend): The semantics of the blend + operation are specified by the ABI. In both the ELF PAuth ABI Extension and + arm64e, it's a ``MOVK`` into the high 16 bits. Consequently, this limits + the width of the integer discriminator used in blends to 16 bits. +* [``llvm.ptrauth.sign_generic``](#llvm-ptrauth-sign-generic): ``PACGA`` +* [``llvm.ptrauth.resign``](#llvm-ptrauth-resign): ``AUT*+PAC*``. These are + represented as a single pseudo-instruction in the backend to guarantee that + the intermediate raw pointer value is not spilled and attackable. diff --git a/llvm/docs/Reference.rst b/llvm/docs/Reference.rst --- a/llvm/docs/Reference.rst +++ b/llvm/docs/Reference.rst @@ -34,6 +34,7 @@ MIRLangRef OptBisect PDB/index + PointerAuth ScudoHardenedAllocator MemTagSanitizer Security @@ -208,5 +209,9 @@ :doc:`Coroutines` LLVM support for coroutines. +:doc:`PointerAuth` + A description of pointer authentication, its LLVM IR representation, and its + support in the backend. + :doc:`YamlIO` A reference guide for using LLVM's YAML I/O library. diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -1850,6 +1850,61 @@ llvm_i32_ty], [IntrNoMem, ImmArg>]>; + +//===----------------- Pointer Authentication Intrinsics ------------------===// +// + +// Sign an unauthenticated pointer using the specified key and discriminator, +// passed in that order. +// Returns the first argument, with some known bits replaced with a signature. +def int_ptrauth_sign : Intrinsic<[llvm_i64_ty], + [llvm_i64_ty, llvm_i32_ty, llvm_i64_ty], + [IntrNoMem, ImmArg>]>; + +// Authenticate a signed pointer, using the specified key and discriminator. +// Returns the first argument, with the signature bits removed. +// The signature must be valid. +def int_ptrauth_auth : Intrinsic<[llvm_i64_ty], + [llvm_i64_ty, llvm_i32_ty, llvm_i64_ty], + [IntrNoMem,ImmArg>]>; + +// Authenticate a signed pointer and resign it. +// The second (key) and third (discriminator) arguments specify the signing +// schema used for authenticating. +// The fourth and fifth arguments specify the schema used for signing. +// The signature must be valid. +// This is a combined form of @llvm.ptrauth.sign and @llvm.ptrauth.auth, with +// an additional integrity guarantee on the intermediate value. +def int_ptrauth_resign : Intrinsic<[llvm_i64_ty], + [llvm_i64_ty, llvm_i32_ty, llvm_i64_ty, + llvm_i32_ty, llvm_i64_ty], + [IntrNoMem, ImmArg>, + ImmArg>]>; + +// Strip the embedded signature out of a signed pointer. +// The second argument specifies the key. +// This behaves like @llvm.ptrauth.auth, but doesn't require the signature to +// be valid. +def int_ptrauth_strip : Intrinsic<[llvm_i64_ty], + [llvm_i64_ty, llvm_i32_ty], + [IntrNoMem, ImmArg>]>; + +// Blend a small integer discriminator with an address discriminator, producing +// a new discriminator value. +def int_ptrauth_blend : Intrinsic<[llvm_i64_ty], + [llvm_i64_ty, llvm_i64_ty], + [IntrNoMem]>; + +// Compute the signature of a value, using a given discriminator. +// This differs from @llvm.ptrauth.sign in that it doesn't embed the computed +// signature in the pointer, but instead returns the signature as a value. +// That allows it to be used to sign non-pointer data: in that sense, it is +// generic. There is no generic @llvm.ptrauth.auth: instead, the signature +// can be computed using @llvm.ptrauth.sign_generic, and compared with icmp. +def int_ptrauth_sign_generic : Intrinsic<[llvm_i64_ty], + [llvm_i64_ty, llvm_i64_ty], + [IntrNoMem]>; + //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//