[AArch64][PAC] Support ptrauth builtins and -fptrauth-intrinsics. (#65996)
This defines the basic set of pointer authentication clang builtins
(provided in a new header, ptrauth.h), with diagnostics and IRGen
support. The availability of the builtins is gated on a new flag,
`-fptrauth-intrinsics`.
Note that this only includes the basic intrinsics, and notably excludes
`ptrauth_sign_constant`, `ptrauth_type_discriminator`, and
`ptrauth_string_discriminator`, which need extra logic to be fully
supported.
This also introduces clang/docs/PointerAuthentication.rst, which
describes the ptrauth model in general, in addition to these builtins.
Co-Authored-By: Akira Hatanaka <ahatanaka@apple.com>
Co-Authored-By: John McCall <rjmccall@apple.com>
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 2257360..13d7261 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -13,6 +13,7 @@
BlockLanguageSpec
Block-ABI-Apple
AutomaticReferenceCounting
+ PointerAuthentication
MatrixTypes
Introduction
@@ -4318,6 +4319,10 @@
like simple arithmetic may be reordered around the intrinsic. If you expect to
have no reordering at all, use inline assembly instead.
+Pointer Authentication
+^^^^^^^^^^^^^^^^^^^^^^
+See :doc:`PointerAuthentication`.
+
X86/X86-64 Language Extensions
------------------------------
diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst
new file mode 100644
index 0000000..19b3384
--- /dev/null
+++ b/clang/docs/PointerAuthentication.rst
@@ -0,0 +1,485 @@
+Pointer Authentication
+======================
+
+.. contents::
+ :local:
+
+Introduction
+------------
+
+Pointer authentication is a technology which offers strong probabilistic
+protection against exploiting a broad class of memory bugs to take control of
+program execution. When adopted consistently in a language ABI, it provides
+a form of relatively fine-grained control flow integrity (CFI) check that
+resists both return-oriented programming (ROP) and jump-oriented programming
+(JOP) attacks.
+
+While pointer authentication can be implemented purely in software, direct
+hardware support (e.g. as provided by Armv8.3 PAuth) can dramatically improve
+performance and code size. Similarly, while pointer authentication
+can be implemented on any architecture, taking advantage of the (typically)
+excess addressing range of a target with 64-bit pointers minimizes the impact
+on memory performance and can allow interoperation with existing code (by
+disabling pointer authentication dynamically). This document will generally
+attempt to present the pointer authentication feature independent of any
+hardware implementation or ABI. Considerations that are
+implementation-specific are clearly identified throughout.
+
+Note that there are several different terms in use:
+
+- **Pointer authentication** is a target-independent language technology.
+
+- **PAuth** (sometimes referred to as **PAC**, for Pointer Authentication
+ Codes) is an AArch64 architecture extension that provides hardware support
+ for pointer authentication. Additional extensions either modify some of the
+ PAuth instruction behavior (notably FPAC), or provide new instruction
+ variants (PAuth_LR).
+
+- **Armv8.3** is an AArch64 architecture revision that makes PAuth mandatory.
+
+- **arm64e** is a specific ABI (not yet fully stable) for implementing pointer
+ authentication using PAuth on certain Apple operating systems.
+
+This document serves four purposes:
+
+- It describes the basic ideas of pointer authentication.
+
+- It documents several language extensions that are useful on targets using
+ pointer authentication.
+
+- It will eventually present a theory of operation for the security mitigation,
+ describing the basic requirements for correctness, various weaknesses in the
+ mechanism, and ways in which programmers can strengthen its protections
+ (including recommendations for language implementors).
+
+- It will eventually document the language ABIs currently used for C, C++,
+ Objective-C, and Swift on arm64e, although these are not yet stable on any
+ target.
+
+Basic Concepts
+--------------
+
+The simple address of an object or function is a **raw pointer**. A raw
+pointer can be **signed** to produce a **signed pointer**. A signed pointer
+can be then **authenticated** in order to verify that it was **validly signed**
+and extract the original raw pointer. These terms reflect the most likely
+implementation technique: computing and storing a cryptographic signature along
+with the pointer.
+
+An **abstract signing key** is a name which refers to a secret key which is
+used to sign and authenticate pointers. The concrete key value for a
+particular name is consistent throughout a process.
+
+A **discriminator** is an arbitrary value used to **diversify** signed pointers
+so that one validly-signed pointer cannot simply be copied over another.
+A discriminator is simply opaque data of some implementation-defined size that
+is included in the signature as a salt (see `Discriminators`_ for details.)
+
+Nearly all aspects of pointer authentication use just these two primary
+operations:
+
+- ``sign(raw_pointer, key, discriminator)`` produces a signed pointer given
+ a raw pointer, an abstract signing key, and a discriminator.
+
+- ``auth(signed_pointer, key, discriminator)`` produces a raw pointer given
+ a signed pointer, an abstract signing key, and a discriminator.
+
+``auth(sign(raw_pointer, key, discriminator), key, discriminator)`` must
+succeed and produce ``raw_pointer``. ``auth`` applied to a value that was
+ultimately produced in any other way is expected to fail, which halts the
+program either:
+
+- immediately, on implementations that enforce ``auth`` success (e.g., when
+ using compiler-generated ``auth`` failure checks, or Armv8.3 with the FPAC
+ extension), or
+
+- when the resulting pointer value is used, on implementations that don't.
+
+However, regardless of the implementation's handling of ``auth`` failures, it
+is permitted for ``auth`` to fail to detect that a signed pointer was not
+produced in this way, in which case it may return anything; this is what makes
+pointer authentication a probabilistic mitigation rather than a perfect one.
+
+There are two secondary operations which are required only to implement certain
+intrinsics in ``<ptrauth.h>``:
+
+- ``strip(signed_pointer, key)`` produces a raw pointer given a signed pointer
+ and a key without verifying its validity, unlike ``auth``. This is useful
+ for certain kinds of tooling, such as crash backtraces; it should generally
+ not be used in the basic language ABI except in very careful ways.
+
+- ``sign_generic(value)`` produces a cryptographic signature for arbitrary
+ data, not necessarily a pointer. This is useful for efficiently verifying
+ that non-pointer data has not been tampered with.
+
+Whenever any of these operations is called for, the key value must be known
+statically. This is because the layout of a signed pointer may vary according
+to the signing key. (For example, in Armv8.3, the layout of a signed pointer
+depends on whether Top Byte Ignore (TBI) is enabled, which can be set
+independently for I and D keys.)
+
+.. admonition:: Note for API designers and language implementors
+
+ These are the *primitive* operations of pointer authentication, provided for
+ clarity of description. They are not suitable either as high-level
+ interfaces or as primitives in a compiler IR because they expose raw
+ pointers. Raw pointers require special attention in the language
+ implementation to avoid the accidental creation of exploitable code
+ sequences.
+
+The following details are all implementation-defined:
+
+- the nature of a signed pointer
+- the size of a discriminator
+- the number and nature of the signing keys
+- the implementation of the ``sign``, ``auth``, ``strip``, and ``sign_generic``
+ operations
+
+While the use of the terms "sign" and "signed pointer" suggest the use of
+a cryptographic signature, other implementations may be possible. See
+`Alternative implementations`_ for an exploration of implementation options.
+
+.. admonition:: Implementation example: Armv8.3
+
+ Readers may find it helpful to know how these terms map to Armv8.3 PAuth:
+
+ - A signed pointer is a pointer with a signature stored in the
+ otherwise-unused high bits. The kernel configures the address width based
+ on the system's addressing needs, and enables TBI for I or D keys as
+ needed. The bits above the address bits and below the TBI bits (if
+ enabled) are unused. The signature width then depends on this addressing
+ configuration.
+
+ - A discriminator is a 64-bit integer. Constant discriminators are 16-bit
+ integers. Blending a constant discriminator into an address consists of
+ replacing the top 16 bits of the pointer containing the address with the
+ constant. Pointers used for blending purposes should only have address
+ bits, since higher bits will be at least partially overwritten with the
+ constant discriminator.
+
+ - There are five 128-bit signing-key registers, each of which can only be
+ directly read or set by privileged code. Of these, four are used for
+ signing pointers, and the fifth is used only for ``sign_generic``. The key
+ data is simply a pepper added to the hash, not an encryption key, and so
+ can be initialized using random data.
+
+ - ``sign`` computes a cryptographic hash of the pointer, discriminator, and
+ signing key, and stores it in the high bits as the signature. ``auth``
+ removes the signature, computes the same hash, and compares the result with
+ the stored signature. ``strip`` removes the signature without
+ authenticating it. While ``aut*`` instructions do not themselves trap on
+ failure in Armv8.3 PAuth, they do with the later optional FPAC extension.
+ An implementation can also choose to emulate this trapping behavior by
+ emitting additional instructions around ``aut*``.
+
+ - ``sign_generic`` corresponds to the ``pacga`` instruction, which takes two
+ 64-bit values and produces a 64-bit cryptographic hash. Implementations of
+ this instruction are not required to produce meaningful data in all bits of
+ the result.
+
+Discriminators
+~~~~~~~~~~~~~~
+
+A discriminator is arbitrary extra data which alters the signature calculated
+for a pointer. When two pointers are signed differently --- either with
+different keys or with different discriminators --- an attacker cannot simply
+replace one pointer with the other.
+
+To use standard cryptographic terminology, a discriminator acts as a
+`salt <https://en.wikipedia.org/wiki/Salt_(cryptography)>`_ in the signing of a
+pointer, and the key data acts as a
+`pepper <https://en.wikipedia.org/wiki/Pepper_(cryptography)>`_. That is,
+both the discriminator and key data are ultimately just added as inputs to the
+signing algorithm along with the pointer, but they serve significantly
+different roles. The key data is a common secret added to every signature,
+whereas the discriminator is a value that can be derived from
+the context in which a specific pointer is signed. However, unlike a password
+salt, it's important that discriminators be *independently* derived from the
+circumstances of the signing; they should never simply be stored alongside
+a pointer. Discriminators are then re-derived in authentication operations.
+
+The intrinsic interface in ``<ptrauth.h>`` allows an arbitrary discriminator
+value to be provided, but can only be used when running normal code. The
+discriminators used by language ABIs must be restricted to make it feasible for
+the loader to sign pointers stored in global memory without needing excessive
+amounts of metadata. Under these restrictions, a discriminator may consist of
+either or both of the following:
+
+- The address at which the pointer is stored in memory. A pointer signed with
+ a discriminator which incorporates its storage address is said to have
+ **address diversity**. In general, using address diversity means that
+ a pointer cannot be reliably copied by an attacker to or from a different
+ memory location. However, an attacker may still be able to attack a larger
+ call sequence if they can alter the address through which the pointer is
+ accessed. Furthermore, some situations cannot use address diversity because
+ of language or other restrictions.
+
+- A constant integer, called a **constant discriminator**. A pointer signed
+ with a non-zero constant discriminator is said to have **constant
+ diversity**. If the discriminator is specific to a single declaration, it is
+ said to have **declaration diversity**; if the discriminator is specific to
+ a type of value, it is said to have **type diversity**. For example, C++
+ v-tables on arm64e sign their component functions using a hash of their
+ method names and signatures, which provides declaration diversity; similarly,
+ C++ member function pointers sign their invocation functions using a hash of
+ the member pointer type, which provides type diversity.
+
+The implementation may need to restrict constant discriminators to be
+significantly smaller than the full size of a discriminator. For example, on
+arm64e, constant discriminators are only 16-bit values. This is believed to
+not significantly weaken the mitigation, since collisions remain uncommon.
+
+The algorithm for blending a constant discriminator with a storage address is
+implementation-defined.
+
+.. _Signing schemas:
+
+Signing Schemas
+~~~~~~~~~~~~~~~
+
+Correct use of pointer authentication requires the signing code and the
+authenticating code to agree about the **signing schema** for the pointer:
+
+- the abstract signing key with which the pointer should be signed and
+- an algorithm for computing the discriminator.
+
+As described in the section above on `Discriminators`_, in most situations, the
+discriminator is produced by taking a constant discriminator and optionally
+blending it with the storage address of the pointer. In these situations, the
+signing schema breaks down even more simply:
+
+- the abstract signing key,
+- a constant discriminator, and
+- whether to use address diversity.
+
+It is important that the signing schema be independently derived at all signing
+and authentication sites. Preferably, the schema should be hard-coded
+everywhere it is needed, but at the very least, it must not be derived by
+inspecting information stored along with the pointer.
+
+Language Features
+-----------------
+
+There is currently one main pointer authentication language feature:
+
+- The language provides the ``<ptrauth.h>`` intrinsic interface for manually
+ signing and authenticating pointers in code. These can be used in
+ circumstances where very specific behavior is required.
+
+
+Language Extensions
+~~~~~~~~~~~~~~~~~~~
+
+Feature Testing
+^^^^^^^^^^^^^^^
+
+Whether the current target uses pointer authentication can be tested for with
+a number of different tests.
+
+- ``__has_feature(ptrauth_intrinsics)`` is true if ``<ptrauth.h>`` provides its
+ normal interface. This may be true even on targets where pointer
+ authentication is not enabled by default.
+
+``<ptrauth.h>``
+~~~~~~~~~~~~~~~
+
+This header defines the following types and operations:
+
+``ptrauth_key``
+^^^^^^^^^^^^^^^
+
+This ``enum`` is the type of abstract signing keys. In addition to defining
+the set of implementation-specific signing keys (for example, Armv8.3 defines
+``ptrauth_key_asia``), it also defines some portable aliases for those keys.
+For example, ``ptrauth_key_function_pointer`` is the key generally used for
+C function pointers, which will generally be suitable for other
+function-signing schemas.
+
+In all the operation descriptions below, key values must be constant values
+corresponding to one of the implementation-specific abstract signing keys from
+this ``enum``.
+
+``ptrauth_extra_data_t``
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+This is a ``typedef`` of a standard integer type of the correct size to hold
+a discriminator value.
+
+In the signing and authentication operation descriptions below, discriminator
+values must have either pointer type or integer type. If the discriminator is
+an integer, it will be coerced to ``ptrauth_extra_data_t``.
+
+``ptrauth_blend_discriminator``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+ ptrauth_blend_discriminator(pointer, integer)
+
+Produce a discriminator value which blends information from the given pointer
+and the given integer.
+
+Implementations may ignore some bits from each value, which is to say, the
+blending algorithm may be chosen for speed and convenience over theoretical
+strength as a hash-combining algorithm. For example, arm64e simply overwrites
+the high 16 bits of the pointer with the low 16 bits of the integer, which can
+be done in a single instruction with an immediate integer.
+
+``pointer`` must have pointer type, and ``integer`` must have integer type. The
+result has type ``ptrauth_extra_data_t``.
+
+``ptrauth_strip``
+^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+ ptrauth_strip(signedPointer, key)
+
+Given that ``signedPointer`` matches the layout for signed pointers signed with
+the given key, extract the raw pointer from it. This operation does not trap
+and cannot fail, even if the pointer is not validly signed.
+
+``ptrauth_sign_unauthenticated``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+ ptrauth_sign_unauthenticated(pointer, key, discriminator)
+
+Produce a signed pointer for the given raw pointer without applying any
+authentication or extra treatment. This operation is not required to have the
+same behavior on a null pointer that the language implementation would.
+
+This is a treacherous operation that can easily result in signing oracles.
+Programs should use it seldom and carefully.
+
+``ptrauth_auth_and_resign``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+ ptrauth_auth_and_resign(pointer, oldKey, oldDiscriminator, newKey, newDiscriminator)
+
+Authenticate that ``pointer`` is signed with ``oldKey`` and
+``oldDiscriminator`` and then resign the raw-pointer result of that
+authentication with ``newKey`` and ``newDiscriminator``.
+
+``pointer`` must have pointer type. The result will have the same type as
+``pointer``. This operation is not required to have the same behavior on
+a null pointer that the language implementation would.
+
+The code sequence produced for this operation must not be directly attackable.
+However, if the discriminator values are not constant integers, their
+computations may still be attackable. In the future, Clang should be enhanced
+to guaranteed non-attackability if these expressions are safely-derived.
+
+``ptrauth_auth_data``
+^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+ ptrauth_auth_data(pointer, key, discriminator)
+
+Authenticate that ``pointer`` is signed with ``key`` and ``discriminator`` and
+remove the signature.
+
+``pointer`` must have object pointer type. The result will have the same type
+as ``pointer``. This operation is not required to have the same behavior on
+a null pointer that the language implementation would.
+
+In the future when Clang makes safe derivation guarantees, the result of
+this operation should be considered safely-derived.
+
+``ptrauth_sign_generic_data``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+ ptrauth_sign_generic_data(value1, value2)
+
+Computes a signature for the given pair of values, incorporating a secret
+signing key.
+
+This operation can be used to verify that arbitrary data has not been tampered
+with by computing a signature for the data, storing that signature, and then
+repeating this process and verifying that it yields the same result. This can
+be reasonably done in any number of ways; for example, a library could compute
+an ordinary checksum of the data and just sign the result in order to get the
+tamper-resistance advantages of the secret signing key (since otherwise an
+attacker could reliably overwrite both the data and the checksum).
+
+``value1`` and ``value2`` must be either pointers or integers. If the integers
+are larger than ``uintptr_t`` then data not representable in ``uintptr_t`` may
+be discarded.
+
+The result will have type ``ptrauth_generic_signature_t``, which is an integer
+type. Implementations are not required to make all bits of the result equally
+significant; in particular, some implementations are known to not leave
+meaningful data in the low bits.
+
+
+
+Alternative Implementations
+---------------------------
+
+Signature Storage
+~~~~~~~~~~~~~~~~~
+
+It is not critical for the security of pointer authentication that the
+signature be stored "together" with the pointer, as it is in Armv8.3. An
+implementation could just as well store the signature in a separate word, so
+that the ``sizeof`` a signed pointer would be larger than the ``sizeof`` a raw
+pointer.
+
+Storing the signature in the high bits, as Armv8.3 does, has several trade-offs:
+
+- Disadvantage: there are substantially fewer bits available for the signature,
+ weakening the mitigation by making it much easier for an attacker to simply
+ guess the correct signature.
+
+- Disadvantage: future growth of the address space will necessarily further
+ weaken the mitigation.
+
+- Advantage: memory layouts don't change, so it's possible for
+ pointer-authentication-enabled code (for example, in a system library) to
+ efficiently interoperate with existing code, as long as pointer
+ authentication can be disabled dynamically.
+
+- Advantage: the size of a signed pointer doesn't grow, which might
+ significantly increase memory requirements, code size, and register pressure.
+
+- Advantage: the size of a signed pointer is the same as a raw pointer, so
+ generic APIs which work in types like `void *` (such as `dlsym`) can still
+ return signed pointers. This means that clients of these APIs will not
+ require insecure code in order to correctly receive a function pointer.
+
+Hashing vs. Encrypting Pointers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Armv8.3 implements ``sign`` by computing a cryptographic hash and storing that
+in the spare bits of the pointer. This means that there are relatively few
+possible values for the valid signed pointer, since the bits corresponding to
+the raw pointer are known. Together with an ``auth`` oracle, this can make it
+computationally feasible to discover the correct signature with brute force.
+(The implementation should of course endeavor not to introduce ``auth``
+oracles, but this can be difficult, and attackers can be devious.)
+
+If the implementation can instead *encrypt* the pointer during ``sign`` and
+*decrypt* it during ``auth``, this brute-force attack becomes far less
+feasible, even with an ``auth`` oracle. However, there are several problems
+with this idea:
+
+- It's unclear whether this kind of encryption is even possible without
+ increasing the storage size of a signed pointer. If the storage size can be
+ increased, brute-force atacks can be equally well mitigated by simply storing
+ a larger signature.
+
+- It would likely be impossible to implement a ``strip`` operation, which might
+ make debuggers and other out-of-process tools far more difficult to write, as
+ well as generally making primitive debugging more challenging.
+
+- Implementations can benefit from being able to extract the raw pointer
+ immediately from a signed pointer. An Armv8.3 processor executing an
+ ``auth``-and-load instruction can perform the load and ``auth`` in parallel;
+ a processor which instead encrypted the pointer would be forced to perform
+ these operations serially.
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 58a2d22..eae41b5 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4354,6 +4354,43 @@
let Prototype = "char(_Constant bool)";
}
+// Pointer authentication builtins.
+def PtrauthStrip : Builtin {
+ let Spellings = ["__builtin_ptrauth_strip"];
+ let Attributes = [CustomTypeChecking, NoThrow, Const];
+ let Prototype = "void*(void*,int)";
+}
+
+def PtrauthBlendDiscriminator : Builtin {
+ let Spellings = ["__builtin_ptrauth_blend_discriminator"];
+ let Attributes = [CustomTypeChecking, NoThrow, Const];
+ let Prototype = "size_t(void*,int)";
+}
+
+def PtrauthSignUnauthenticated : Builtin {
+ let Spellings = ["__builtin_ptrauth_sign_unauthenticated"];
+ let Attributes = [CustomTypeChecking, NoThrow, Const];
+ let Prototype = "void*(void*,int,void*)";
+}
+
+def PtrauthSignGenericData : Builtin {
+ let Spellings = ["__builtin_ptrauth_sign_generic_data"];
+ let Attributes = [CustomTypeChecking, NoThrow, Const];
+ let Prototype = "size_t(void*,void*)";
+}
+
+def PtrauthAuthAndResign : Builtin {
+ let Spellings = ["__builtin_ptrauth_auth_and_resign"];
+ let Attributes = [CustomTypeChecking, NoThrow];
+ let Prototype = "void*(void*,int,void*,int,void*)";
+}
+
+def PtrauthAuth : Builtin {
+ let Spellings = ["__builtin_ptrauth_auth"];
+ let Attributes = [CustomTypeChecking, NoThrow];
+ let Prototype = "void*(void*,int,void*)";
+}
+
// OpenCL v2.0 s6.13.16, s9.17.3.5 - Pipe functions.
// We need the generic prototype, since the packet type could be anything.
def ReadPipe : OCLPipeLangBuiltin {
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 3f14167..f5ff891 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -875,6 +875,7 @@
def GNUZeroLineDirective : DiagGroup<"gnu-zero-line-directive">;
def GNUZeroVariadicMacroArguments : DiagGroup<"gnu-zero-variadic-macro-arguments">;
def MisleadingIndentation : DiagGroup<"misleading-indentation">;
+def PtrAuthNullPointers : DiagGroup<"ptrauth-null-pointers">;
// This covers both the deprecated case (in C++98)
// and the extension case (in C++11 onwards).
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d7ab163..8e97902 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -911,6 +911,22 @@
def err_function_start_invalid_type: Error<
"argument must be a function">;
+def err_ptrauth_disabled :
+ Error<"this target does not support pointer authentication">;
+def err_ptrauth_invalid_key :
+ Error<"%0 does not identify a valid pointer authentication key for "
+ "the current target">;
+def err_ptrauth_value_bad_type :
+ Error<"%select{signed value|extra discriminator|blended pointer|blended "
+ "integer}0 must have %select{pointer|integer|pointer or integer}1 "
+ "type; type here is %2">;
+def warn_ptrauth_sign_null_pointer :
+ Warning<"signing a null pointer will yield a non-null pointer">,
+ InGroup<PtrAuthNullPointers>;
+def warn_ptrauth_auth_null_pointer :
+ Warning<"authenticating a null pointer will almost certainly trap">,
+ InGroup<PtrAuthNullPointers>;
+
/// main()
// static main() is not an error in C, just in C++.
def warn_static_main : Warning<"'main' should not be declared static">,
diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index 5fad5fc..eeed5f4 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -101,6 +101,7 @@
FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread))
FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow))
FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo))
+FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics)
FEATURE(swiftasynccc,
PP.getTargetInfo().checkCallingConvention(CC_SwiftAsync) ==
clang::TargetInfo::CCCR_OK)
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 472fd9f..8ef6700 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -161,6 +161,8 @@
LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments")
LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features")
+LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics")
+
LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes")
COMPATIBLE_LANGOPT(RecoveryAST, 1, 1, "Preserve expressions in AST when encountering errors")
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 7682f84..374595e 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -24,6 +24,7 @@
#include "clang/Basic/TargetOptions.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/SmallSet.h"
@@ -1571,6 +1572,11 @@
return getAddressSpaceMap()[(unsigned)AS];
}
+ /// Determine whether the given pointer-authentication key is valid.
+ ///
+ /// The value has been coerced to type 'int'.
+ virtual bool validatePointerAuthKey(const llvm::APSInt &value) const;
+
/// Map from the address space field in builtin description strings to the
/// language address space.
virtual LangAS getOpenCLBuiltinAddressSpace(unsigned AS) const {
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 493c0de..acb7592 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4106,6 +4106,14 @@
" of a non-void function as unreachable">,
PosFlag<SetTrue>>;
+let Group = f_Group in {
+ let Visibility = [ClangOption,CC1Option] in {
+ def fptrauth_intrinsics : Flag<["-"], "fptrauth-intrinsics">,
+ HelpText<"Enable pointer authentication intrinsics">;
+ }
+ def fno_ptrauth_intrinsics : Flag<["-"], "fno-ptrauth-intrinsics">;
+}
+
def fenable_matrix : Flag<["-"], "fenable-matrix">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable matrix data type and related builtin functions">,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 69d5709..95ea5eb 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3060,6 +3060,8 @@
TemplateIdAnnotation *TemplateId,
bool IsMemberSpecialization);
+ bool checkConstantPointerAuthKey(Expr *keyExpr, unsigned &key);
+
void DiagnoseFunctionSpecifiers(const DeclSpec &DS);
NamedDecl *getShadowedDeclaration(const TypedefNameDecl *D,
const LookupResult &R);
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index 96b3ad9..5d90551 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -925,6 +925,10 @@
return true;
}
+bool TargetInfo::validatePointerAuthKey(const llvm::APSInt &value) const {
+ return false;
+}
+
void TargetInfo::CheckFixedPointBits() const {
// Check that the number of fractional and integral bits (and maybe sign) can
// fit into the bits given for a fixed point type.
diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp
index 5abb060..fa8b5a8 100644
--- a/clang/lib/Basic/Targets/AArch64.cpp
+++ b/clang/lib/Basic/Targets/AArch64.cpp
@@ -14,6 +14,7 @@
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/TargetBuiltins.h"
#include "clang/Basic/TargetInfo.h"
+#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
@@ -1450,6 +1451,11 @@
return -1;
}
+bool AArch64TargetInfo::validatePointerAuthKey(
+ const llvm::APSInt &value) const {
+ return 0 <= value && value <= 3;
+}
+
bool AArch64TargetInfo::hasInt128Type() const { return true; }
AArch64leTargetInfo::AArch64leTargetInfo(const llvm::Triple &Triple,
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index c1ba156..2dd6b21 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -195,6 +195,8 @@
int getEHDataRegisterNumber(unsigned RegNo) const override;
+ bool validatePointerAuthKey(const llvm::APSInt &value) const override;
+
const char *getBFloat16Mangling() const override { return "u6__bf16"; };
bool hasInt128Type() const override;
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index b09bf56..e708bf3 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -5208,6 +5208,73 @@
case Builtin::BI__iso_volatile_store64:
return RValue::get(EmitISOVolatileStore(*this, E));
+ case Builtin::BI__builtin_ptrauth_auth:
+ case Builtin::BI__builtin_ptrauth_auth_and_resign:
+ case Builtin::BI__builtin_ptrauth_blend_discriminator:
+ case Builtin::BI__builtin_ptrauth_sign_generic_data:
+ case Builtin::BI__builtin_ptrauth_sign_unauthenticated:
+ case Builtin::BI__builtin_ptrauth_strip: {
+ // Emit the arguments.
+ SmallVector<llvm::Value *, 5> Args;
+ for (auto argExpr : E->arguments())
+ Args.push_back(EmitScalarExpr(argExpr));
+
+ // Cast the value to intptr_t, saving its original type.
+ llvm::Type *OrigValueType = Args[0]->getType();
+ if (OrigValueType->isPointerTy())
+ Args[0] = Builder.CreatePtrToInt(Args[0], IntPtrTy);
+
+ switch (BuiltinID) {
+ case Builtin::BI__builtin_ptrauth_auth_and_resign:
+ if (Args[4]->getType()->isPointerTy())
+ Args[4] = Builder.CreatePtrToInt(Args[4], IntPtrTy);
+ LLVM_FALLTHROUGH;
+
+ case Builtin::BI__builtin_ptrauth_auth:
+ case Builtin::BI__builtin_ptrauth_sign_unauthenticated:
+ if (Args[2]->getType()->isPointerTy())
+ Args[2] = Builder.CreatePtrToInt(Args[2], IntPtrTy);
+ break;
+
+ case Builtin::BI__builtin_ptrauth_sign_generic_data:
+ if (Args[1]->getType()->isPointerTy())
+ Args[1] = Builder.CreatePtrToInt(Args[1], IntPtrTy);
+ break;
+
+ case Builtin::BI__builtin_ptrauth_blend_discriminator:
+ case Builtin::BI__builtin_ptrauth_strip:
+ break;
+ }
+
+ // Call the intrinsic.
+ auto IntrinsicID = [&]() -> unsigned {
+ switch (BuiltinID) {
+ case Builtin::BI__builtin_ptrauth_auth:
+ return llvm::Intrinsic::ptrauth_auth;
+ case Builtin::BI__builtin_ptrauth_auth_and_resign:
+ return llvm::Intrinsic::ptrauth_resign;
+ case Builtin::BI__builtin_ptrauth_blend_discriminator:
+ return llvm::Intrinsic::ptrauth_blend;
+ case Builtin::BI__builtin_ptrauth_sign_generic_data:
+ return llvm::Intrinsic::ptrauth_sign_generic;
+ case Builtin::BI__builtin_ptrauth_sign_unauthenticated:
+ return llvm::Intrinsic::ptrauth_sign;
+ case Builtin::BI__builtin_ptrauth_strip:
+ return llvm::Intrinsic::ptrauth_strip;
+ }
+ llvm_unreachable("bad ptrauth intrinsic");
+ }();
+ auto Intrinsic = CGM.getIntrinsic(IntrinsicID);
+ llvm::Value *Result = EmitRuntimeCall(Intrinsic, Args);
+
+ if (BuiltinID != Builtin::BI__builtin_ptrauth_sign_generic_data &&
+ BuiltinID != Builtin::BI__builtin_ptrauth_blend_discriminator &&
+ OrigValueType->isPointerTy()) {
+ Result = Builder.CreateIntToPtr(Result, OrigValueType);
+ }
+ return RValue::get(Result);
+ }
+
case Builtin::BI__exception_code:
case Builtin::BI_exception_code:
return RValue::get(EmitSEHExceptionCode());
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 3a7a1cf..055884d 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7199,6 +7199,10 @@
// -fno-common is the default, set -fcommon only when that flag is set.
Args.addOptInFlag(CmdArgs, options::OPT_fcommon, options::OPT_fno_common);
+ if (Args.hasFlag(options::OPT_fptrauth_intrinsics,
+ options::OPT_fno_ptrauth_intrinsics, false))
+ CmdArgs.push_back("-fptrauth-intrinsics");
+
// -fsigned-bitfields is default, and clang doesn't yet support
// -funsigned-bitfields.
if (!Args.hasFlag(options::OPT_fsigned_bitfields,
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 2d3cbb9..2a21a9d 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -3293,6 +3293,17 @@
Opts.ModuleSearchPaths.push_back(A->getValue());
}
+static void GeneratePointerAuthArgs(const LangOptions &Opts,
+ ArgumentConsumer Consumer) {
+ if (Opts.PointerAuthIntrinsics)
+ GenerateArg(Consumer, OPT_fptrauth_intrinsics);
+}
+
+static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args,
+ DiagnosticsEngine &Diags) {
+ Opts.PointerAuthIntrinsics = Args.hasArg(OPT_fptrauth_intrinsics);
+}
+
/// Check if input file kind and language standard are compatible.
static bool IsInputCompatibleWithStandard(InputKind IK,
const LangStandard &S) {
@@ -4613,6 +4624,8 @@
Res.getFileSystemOpts().WorkingDir);
ParseAPINotesArgs(Res.getAPINotesOpts(), Args, Diags);
+ ParsePointerAuthArgs(LangOpts, Args, Diags);
+
ParseLangArgs(LangOpts, Args, DashX, T, Res.getPreprocessorOpts().Includes,
Diags);
if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC)
@@ -4843,6 +4856,7 @@
GenerateTargetArgs(getTargetOpts(), Consumer);
GenerateHeaderSearchArgs(getHeaderSearchOpts(), Consumer);
GenerateAPINotesArgs(getAPINotesOpts(), Consumer);
+ GeneratePointerAuthArgs(getLangOpts(), Consumer);
GenerateLangArgs(getLangOpts(), Consumer, T, getFrontendOpts().DashX);
GenerateCodeGenArgs(getCodeGenOpts(), Consumer, T,
getFrontendOpts().OutputFile, &getLangOpts());
diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt
index b9a966b..902e33b 100644
--- a/clang/lib/Headers/CMakeLists.txt
+++ b/clang/lib/Headers/CMakeLists.txt
@@ -214,6 +214,7 @@
popcntintrin.h
prfchiintrin.h
prfchwintrin.h
+ ptrauth.h
ptwriteintrin.h
raointintrin.h
rdpruintrin.h
diff --git a/clang/lib/Headers/module.modulemap b/clang/lib/Headers/module.modulemap
index 56a13f6..8741968f 100644
--- a/clang/lib/Headers/module.modulemap
+++ b/clang/lib/Headers/module.modulemap
@@ -315,3 +315,8 @@
header "opencl-c.h"
header "opencl-c-base.h"
}
+
+module ptrauth {
+ header "ptrauth.h"
+ export *
+}
diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h
new file mode 100644
index 0000000..56c3c36
--- /dev/null
+++ b/clang/lib/Headers/ptrauth.h
@@ -0,0 +1,185 @@
+/*===---- ptrauth.h - Pointer authentication -------------------------------===
+ *
+ * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+ * See https://llvm.org/LICENSE.txt for license information.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __PTRAUTH_H
+#define __PTRAUTH_H
+
+typedef enum {
+ ptrauth_key_asia = 0,
+ ptrauth_key_asib = 1,
+ ptrauth_key_asda = 2,
+ ptrauth_key_asdb = 3,
+} ptrauth_key;
+
+/* An integer type of the appropriate size for a discriminator argument. */
+typedef __UINTPTR_TYPE__ ptrauth_extra_data_t;
+
+/* An integer type of the appropriate size for a generic signature. */
+typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
+
+/* A signed pointer value embeds the original pointer together with
+ a signature that attests to the validity of that pointer. Because
+ this signature must use only "spare" bits of the pointer, a
+ signature's validity is probabilistic in practice: it is unlikely
+ but still plausible that an invalidly-derived signature will
+ somehow equal the correct signature and therefore successfully
+ authenticate. Nonetheless, this scheme provides a strong degree
+ of protection against certain kinds of attacks. */
+
+/* Authenticating a pointer that was not signed with the given key
+ and extra-data value will (likely) fail by trapping. */
+
+#if __has_feature(ptrauth_intrinsics)
+
+/* Strip the signature from a value without authenticating it.
+
+ If the value is a function pointer, the result will not be a
+ legal function pointer because of the missing signature, and
+ attempting to call it will result in an authentication failure.
+
+ The value must be an expression of pointer type.
+ The key must be a constant expression of type ptrauth_key.
+ The result will have the same type as the original value. */
+#define ptrauth_strip(__value, __key) __builtin_ptrauth_strip(__value, __key)
+
+/* Blend a constant discriminator into the given pointer-like value
+ to form a new discriminator. Not all bits of the inputs are
+ guaranteed to contribute to the result.
+
+ On arm64e, the integer must fall within the range of a uint16_t;
+ other bits may be ignored.
+
+ The first argument must be an expression of pointer type.
+ The second argument must be an expression of integer type.
+ The result will have type uintptr_t. */
+#define ptrauth_blend_discriminator(__pointer, __integer) \
+ __builtin_ptrauth_blend_discriminator(__pointer, __integer)
+
+/* Add a signature to the given pointer value using a specific key,
+ using the given extra data as a salt to the signing process.
+
+ This operation does not authenticate the original value and is
+ therefore potentially insecure if an attacker could possibly
+ control that value.
+
+ The value must be an expression of pointer type.
+ The key must be a constant expression of type ptrauth_key.
+ The extra data must be an expression of pointer or integer type;
+ if an integer, it will be coerced to ptrauth_extra_data_t.
+ The result will have the same type as the original value. */
+#define ptrauth_sign_unauthenticated(__value, __key, __data) \
+ __builtin_ptrauth_sign_unauthenticated(__value, __key, __data)
+
+/* Authenticate a pointer using one scheme and resign it using another.
+
+ If the result is subsequently authenticated using the new scheme, that
+ authentication is guaranteed to fail if and only if the initial
+ authentication failed.
+
+ The value must be an expression of pointer type.
+ The key must be a constant expression of type ptrauth_key.
+ The extra data must be an expression of pointer or integer type;
+ if an integer, it will be coerced to ptrauth_extra_data_t.
+ The result will have the same type as the original value.
+
+ This operation is guaranteed to not leave the intermediate value
+ available for attack before it is re-signed.
+
+ Do not pass a null pointer to this function. A null pointer
+ will not successfully authenticate.
+
+ This operation traps if the authentication fails. */
+#define ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, \
+ __new_data) \
+ __builtin_ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, \
+ __new_data)
+
+/* Authenticate a data pointer.
+
+ The value must be an expression of non-function pointer type.
+ The key must be a constant expression of type ptrauth_key.
+ The extra data must be an expression of pointer or integer type;
+ if an integer, it will be coerced to ptrauth_extra_data_t.
+ The result will have the same type as the original value.
+
+ This operation traps if the authentication fails. */
+#define ptrauth_auth_data(__value, __old_key, __old_data) \
+ __builtin_ptrauth_auth(__value, __old_key, __old_data)
+
+/* Compute a signature for the given pair of pointer-sized values.
+ The order of the arguments is significant.
+
+ Like a pointer signature, the resulting signature depends on
+ private key data and therefore should not be reliably reproducible
+ by attackers. That means that this can be used to validate the
+ integrity of arbitrary data by storing a signature for that data
+ alongside it, then checking that the signature is still valid later.
+ Data which exceeds two pointers in size can be signed by either
+ computing a tree of generic signatures or just signing an ordinary
+ cryptographic hash of the data.
+
+ The result has type ptrauth_generic_signature_t. However, it may
+ not have as many bits of entropy as that type's width would suggest;
+ some implementations are known to compute a compressed signature as
+ if the arguments were a pointer and a discriminator.
+
+ The arguments must be either pointers or integers; if integers, they
+ will be coerce to uintptr_t. */
+#define ptrauth_sign_generic_data(__value, __data) \
+ __builtin_ptrauth_sign_generic_data(__value, __data)
+
+#else
+
+#define ptrauth_strip(__value, __key) \
+ ({ \
+ (void)__key; \
+ __value; \
+ })
+
+#define ptrauth_blend_discriminator(__pointer, __integer) \
+ ({ \
+ (void)__pointer; \
+ (void)__integer; \
+ ((ptrauth_extra_data_t)0); \
+ })
+
+#define ptrauth_sign_unauthenticated(__value, __key, __data) \
+ ({ \
+ (void)__key; \
+ (void)__data; \
+ __value; \
+ })
+
+#define ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, \
+ __new_data) \
+ ({ \
+ (void)__old_key; \
+ (void)__old_data; \
+ (void)__new_key; \
+ (void)__new_data; \
+ __value; \
+ })
+
+#define ptrauth_auth_data(__value, __old_key, __old_data) \
+ ({ \
+ (void)__old_key; \
+ (void)__old_data; \
+ __value; \
+ })
+
+#define ptrauth_sign_generic_data(__value, __data) \
+ ({ \
+ (void)__value; \
+ (void)__data; \
+ ((ptrauth_generic_signature_t)0); \
+ })
+
+#endif /* __has_feature(ptrauth_intrinsics) */
+
+#endif /* __PTRAUTH_H */
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 290afd9..e303a7c 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1971,6 +1971,191 @@
return false;
}
+namespace {
+enum PointerAuthOpKind {
+ PAO_Strip,
+ PAO_Sign,
+ PAO_Auth,
+ PAO_SignGeneric,
+ PAO_Discriminator,
+ PAO_BlendPointer,
+ PAO_BlendInteger
+};
+}
+
+static bool checkPointerAuthEnabled(Sema &S, Expr *E) {
+ if (S.getLangOpts().PointerAuthIntrinsics)
+ return false;
+
+ S.Diag(E->getExprLoc(), diag::err_ptrauth_disabled) << E->getSourceRange();
+ return true;
+}
+
+static bool checkPointerAuthKey(Sema &S, Expr *&Arg) {
+ // Convert it to type 'int'.
+ if (convertArgumentToType(S, Arg, S.Context.IntTy))
+ return true;
+
+ // Value-dependent expressions are okay; wait for template instantiation.
+ if (Arg->isValueDependent())
+ return false;
+
+ unsigned KeyValue;
+ return S.checkConstantPointerAuthKey(Arg, KeyValue);
+}
+
+bool Sema::checkConstantPointerAuthKey(Expr *Arg, unsigned &Result) {
+ // Attempt to constant-evaluate the expression.
+ std::optional<llvm::APSInt> KeyValue = Arg->getIntegerConstantExpr(Context);
+ if (!KeyValue) {
+ Diag(Arg->getExprLoc(), diag::err_expr_not_ice)
+ << 0 << Arg->getSourceRange();
+ return true;
+ }
+
+ // Ask the target to validate the key parameter.
+ if (!Context.getTargetInfo().validatePointerAuthKey(*KeyValue)) {
+ llvm::SmallString<32> Value;
+ {
+ llvm::raw_svector_ostream Str(Value);
+ Str << *KeyValue;
+ }
+
+ Diag(Arg->getExprLoc(), diag::err_ptrauth_invalid_key)
+ << Value << Arg->getSourceRange();
+ return true;
+ }
+
+ Result = KeyValue->getZExtValue();
+ return false;
+}
+
+static bool checkPointerAuthValue(Sema &S, Expr *&Arg,
+ PointerAuthOpKind OpKind) {
+ if (Arg->hasPlaceholderType()) {
+ ExprResult R = S.CheckPlaceholderExpr(Arg);
+ if (R.isInvalid())
+ return true;
+ Arg = R.get();
+ }
+
+ auto AllowsPointer = [](PointerAuthOpKind OpKind) {
+ return OpKind != PAO_BlendInteger;
+ };
+ auto AllowsInteger = [](PointerAuthOpKind OpKind) {
+ return OpKind == PAO_Discriminator || OpKind == PAO_BlendInteger ||
+ OpKind == PAO_SignGeneric;
+ };
+
+ // Require the value to have the right range of type.
+ QualType ExpectedTy;
+ if (AllowsPointer(OpKind) && Arg->getType()->isPointerType()) {
+ ExpectedTy = Arg->getType().getUnqualifiedType();
+ } else if (AllowsPointer(OpKind) && Arg->getType()->isNullPtrType()) {
+ ExpectedTy = S.Context.VoidPtrTy;
+ } else if (AllowsInteger(OpKind) &&
+ Arg->getType()->isIntegralOrUnscopedEnumerationType()) {
+ ExpectedTy = S.Context.getUIntPtrType();
+
+ } else {
+ // Diagnose the failures.
+ S.Diag(Arg->getExprLoc(), diag::err_ptrauth_value_bad_type)
+ << unsigned(OpKind == PAO_Discriminator ? 1
+ : OpKind == PAO_BlendPointer ? 2
+ : OpKind == PAO_BlendInteger ? 3
+ : 0)
+ << unsigned(AllowsInteger(OpKind) ? (AllowsPointer(OpKind) ? 2 : 1) : 0)
+ << Arg->getType() << Arg->getSourceRange();
+ return true;
+ }
+
+ // Convert to that type. This should just be an lvalue-to-rvalue
+ // conversion.
+ if (convertArgumentToType(S, Arg, ExpectedTy))
+ return true;
+
+ // Warn about null pointers for non-generic sign and auth operations.
+ if ((OpKind == PAO_Sign || OpKind == PAO_Auth) &&
+ Arg->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNull)) {
+ S.Diag(Arg->getExprLoc(), OpKind == PAO_Sign
+ ? diag::warn_ptrauth_sign_null_pointer
+ : diag::warn_ptrauth_auth_null_pointer)
+ << Arg->getSourceRange();
+ }
+
+ return false;
+}
+
+static ExprResult SemaPointerAuthStrip(Sema &S, CallExpr *Call) {
+ if (checkArgCount(S, Call, 2))
+ return ExprError();
+ if (checkPointerAuthEnabled(S, Call))
+ return ExprError();
+ if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Strip) ||
+ checkPointerAuthKey(S, Call->getArgs()[1]))
+ return ExprError();
+
+ Call->setType(Call->getArgs()[0]->getType());
+ return Call;
+}
+
+static ExprResult SemaPointerAuthBlendDiscriminator(Sema &S, CallExpr *Call) {
+ if (checkArgCount(S, Call, 2))
+ return ExprError();
+ if (checkPointerAuthEnabled(S, Call))
+ return ExprError();
+ if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_BlendPointer) ||
+ checkPointerAuthValue(S, Call->getArgs()[1], PAO_BlendInteger))
+ return ExprError();
+
+ Call->setType(S.Context.getUIntPtrType());
+ return Call;
+}
+
+static ExprResult SemaPointerAuthSignGenericData(Sema &S, CallExpr *Call) {
+ if (checkArgCount(S, Call, 2))
+ return ExprError();
+ if (checkPointerAuthEnabled(S, Call))
+ return ExprError();
+ if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_SignGeneric) ||
+ checkPointerAuthValue(S, Call->getArgs()[1], PAO_Discriminator))
+ return ExprError();
+
+ Call->setType(S.Context.getUIntPtrType());
+ return Call;
+}
+
+static ExprResult SemaPointerAuthSignOrAuth(Sema &S, CallExpr *Call,
+ PointerAuthOpKind OpKind) {
+ if (checkArgCount(S, Call, 3))
+ return ExprError();
+ if (checkPointerAuthEnabled(S, Call))
+ return ExprError();
+ if (checkPointerAuthValue(S, Call->getArgs()[0], OpKind) ||
+ checkPointerAuthKey(S, Call->getArgs()[1]) ||
+ checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator))
+ return ExprError();
+
+ Call->setType(Call->getArgs()[0]->getType());
+ return Call;
+}
+
+static ExprResult SemaPointerAuthAuthAndResign(Sema &S, CallExpr *Call) {
+ if (checkArgCount(S, Call, 5))
+ return ExprError();
+ if (checkPointerAuthEnabled(S, Call))
+ return ExprError();
+ if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Auth) ||
+ checkPointerAuthKey(S, Call->getArgs()[1]) ||
+ checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator) ||
+ checkPointerAuthKey(S, Call->getArgs()[3]) ||
+ checkPointerAuthValue(S, Call->getArgs()[4], PAO_Discriminator))
+ return ExprError();
+
+ Call->setType(Call->getArgs()[0]->getType());
+ return Call;
+}
+
static ExprResult SemaBuiltinLaunder(Sema &S, CallExpr *TheCall) {
if (checkArgCount(S, TheCall, 1))
return ExprError();
@@ -2683,6 +2868,18 @@
}
break;
}
+ case Builtin::BI__builtin_ptrauth_strip:
+ return SemaPointerAuthStrip(*this, TheCall);
+ case Builtin::BI__builtin_ptrauth_blend_discriminator:
+ return SemaPointerAuthBlendDiscriminator(*this, TheCall);
+ case Builtin::BI__builtin_ptrauth_sign_unauthenticated:
+ return SemaPointerAuthSignOrAuth(*this, TheCall, PAO_Sign);
+ case Builtin::BI__builtin_ptrauth_auth:
+ return SemaPointerAuthSignOrAuth(*this, TheCall, PAO_Auth);
+ case Builtin::BI__builtin_ptrauth_sign_generic_data:
+ return SemaPointerAuthSignGenericData(*this, TheCall);
+ case Builtin::BI__builtin_ptrauth_auth_and_resign:
+ return SemaPointerAuthAuthAndResign(*this, TheCall);
// OpenCL v2.0, s6.13.16 - Pipe functions
case Builtin::BIread_pipe:
case Builtin::BIwrite_pipe:
diff --git a/clang/test/CodeGen/ptrauth-intrinsics.c b/clang/test/CodeGen/ptrauth-intrinsics.c
new file mode 100644
index 0000000..17f28dd
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-intrinsics.c
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s
+
+void (*fnptr)(void);
+long int_discriminator;
+void *ptr_discriminator;
+long signature;
+
+// CHECK-LABEL: define void @test_auth()
+void test_auth() {
+ // CHECK: [[PTR:%.*]] = load ptr, ptr @fnptr,
+ // CHECK-NEXT: [[DISC0:%.*]] = load ptr, ptr @ptr_discriminator,
+ // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[PTR]] to i64
+ // CHECK-NEXT: [[DISC:%.*]] = ptrtoint ptr [[DISC0]] to i64
+ // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T0]], i32 0, i64 [[DISC]])
+ // CHECK-NEXT: [[RESULT:%.*]] = inttoptr i64 [[T1]] to ptr
+ // CHECK-NEXT: store ptr [[RESULT]], ptr @fnptr,
+ fnptr = __builtin_ptrauth_auth(fnptr, 0, ptr_discriminator);
+}
+
+// CHECK-LABEL: define void @test_strip()
+void test_strip() {
+ // CHECK: [[PTR:%.*]] = load ptr, ptr @fnptr,
+ // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[PTR]] to i64
+ // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[T0]], i32 0)
+ // CHECK-NEXT: [[RESULT:%.*]] = inttoptr i64 [[T1]] to ptr
+ // CHECK-NEXT: store ptr [[RESULT]], ptr @fnptr,
+ fnptr = __builtin_ptrauth_strip(fnptr, 0);
+}
+
+// CHECK-LABEL: define void @test_sign_unauthenticated()
+void test_sign_unauthenticated() {
+ // CHECK: [[PTR:%.*]] = load ptr, ptr @fnptr,
+ // CHECK-NEXT: [[DISC0:%.*]] = load ptr, ptr @ptr_discriminator,
+ // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[PTR]] to i64
+ // CHECK-NEXT: [[DISC:%.*]] = ptrtoint ptr [[DISC0]] to i64
+ // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[T0]], i32 0, i64 [[DISC]])
+ // CHECK-NEXT: [[RESULT:%.*]] = inttoptr i64 [[T1]] to ptr
+ // CHECK-NEXT: store ptr [[RESULT]], ptr @fnptr,
+ fnptr = __builtin_ptrauth_sign_unauthenticated(fnptr, 0, ptr_discriminator);
+}
+
+// CHECK-LABEL: define void @test_auth_and_resign()
+void test_auth_and_resign() {
+ // CHECK: [[PTR:%.*]] = load ptr, ptr @fnptr,
+ // CHECK-NEXT: [[DISC0:%.*]] = load ptr, ptr @ptr_discriminator,
+ // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[PTR]] to i64
+ // CHECK-NEXT: [[DISC:%.*]] = ptrtoint ptr [[DISC0]] to i64
+ // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 [[DISC]], i32 3, i64 15)
+ // CHECK-NEXT: [[RESULT:%.*]] = inttoptr i64 [[T1]] to ptr
+ // CHECK-NEXT: store ptr [[RESULT]], ptr @fnptr,
+ fnptr = __builtin_ptrauth_auth_and_resign(fnptr, 0, ptr_discriminator, 3, 15);
+}
+
+// CHECK-LABEL: define void @test_blend_discriminator()
+void test_blend_discriminator() {
+ // CHECK: [[PTR:%.*]] = load ptr, ptr @fnptr,
+ // CHECK-NEXT: [[DISC:%.*]] = load i64, ptr @int_discriminator,
+ // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[PTR]] to i64
+ // CHECK-NEXT: [[RESULT:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 [[DISC]])
+ // CHECK-NEXT: store i64 [[RESULT]], ptr @int_discriminator,
+ int_discriminator = __builtin_ptrauth_blend_discriminator(fnptr, int_discriminator);
+}
+
+// CHECK-LABEL: define void @test_sign_generic_data()
+void test_sign_generic_data() {
+ // CHECK: [[PTR:%.*]] = load ptr, ptr @fnptr,
+ // CHECK-NEXT: [[DISC0:%.*]] = load ptr, ptr @ptr_discriminator,
+ // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[PTR]] to i64
+ // CHECK-NEXT: [[DISC:%.*]] = ptrtoint ptr [[DISC0]] to i64
+ // CHECK-NEXT: [[RESULT:%.*]] = call i64 @llvm.ptrauth.sign.generic(i64 [[T0]], i64 [[DISC]])
+ // CHECK-NEXT: store i64 [[RESULT]], ptr @signature,
+ signature = __builtin_ptrauth_sign_generic_data(fnptr, ptr_discriminator);
+}
diff --git a/clang/test/Preprocessor/ptrauth_feature.c b/clang/test/Preprocessor/ptrauth_feature.c
new file mode 100644
index 0000000..e45c6ea
--- /dev/null
+++ b/clang/test/Preprocessor/ptrauth_feature.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 %s -E -triple=arm64-- | FileCheck %s --check-prefixes=NOINTRIN
+// RUN: %clang_cc1 %s -E -triple=arm64-- -fptrauth-intrinsics | FileCheck %s --check-prefixes=INTRIN
+
+#if __has_feature(ptrauth_intrinsics)
+// INTRIN: has_ptrauth_intrinsics
+void has_ptrauth_intrinsics() {}
+#else
+// NOINTRIN: no_ptrauth_intrinsics
+void no_ptrauth_intrinsics() {}
+#endif
diff --git a/clang/test/Sema/ptrauth-intrinsics-macro.c b/clang/test/Sema/ptrauth-intrinsics-macro.c
new file mode 100644
index 0000000..07d6374
--- /dev/null
+++ b/clang/test/Sema/ptrauth-intrinsics-macro.c
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -Wall -fsyntax-only -verify -fptrauth-intrinsics %s
+// RUN: %clang_cc1 -triple arm64-apple-ios -Wall -fsyntax-only -verify %s
+
+// expected-no-diagnostics
+
+#include <ptrauth.h>
+
+#define VALID_CODE_KEY 0
+#define VALID_DATA_KEY 2
+
+extern int dv;
+
+void test(int *dp, int value) {
+ dp = ptrauth_strip(dp, VALID_DATA_KEY);
+ ptrauth_extra_data_t t0 = ptrauth_blend_discriminator(dp, value);
+ (void)t0;
+ dp = ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, 0);
+ dp = ptrauth_auth_and_resign(dp, VALID_DATA_KEY, dp, VALID_DATA_KEY, dp);
+ dp = ptrauth_auth_data(dp, VALID_DATA_KEY, 0);
+ int pu0 = 0, pu1 = 0, pu2 = 0, pu3 = 0, pu4 = 0, pu5 = 0, pu6 = 0, pu7 = 0;
+ ptrauth_blend_discriminator(&pu0, value);
+ ptrauth_auth_and_resign(&pu1, VALID_DATA_KEY, dp, VALID_DATA_KEY, dp);
+ ptrauth_auth_and_resign(dp, VALID_DATA_KEY, &pu2, VALID_DATA_KEY, dp);
+ ptrauth_auth_and_resign(dp, VALID_DATA_KEY, dp, VALID_DATA_KEY, &pu3);
+ ptrauth_sign_generic_data(pu4, dp);
+ ptrauth_sign_generic_data(dp, pu5);
+ ptrauth_auth_data(&pu6, VALID_DATA_KEY, value);
+ ptrauth_auth_data(dp, VALID_DATA_KEY, pu7);
+
+
+
+ int t2 = ptrauth_sign_generic_data(dp, 0);
+ (void)t2;
+}
diff --git a/clang/test/Sema/ptrauth.c b/clang/test/Sema/ptrauth.c
new file mode 100644
index 0000000..3ad3d70
--- /dev/null
+++ b/clang/test/Sema/ptrauth.c
@@ -0,0 +1,126 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify -fptrauth-intrinsics %s
+
+#if __has_feature(ptrauth_intrinsics)
+#warning Pointer authentication enabled!
+// expected-warning@-1 {{Pointer authentication enabled!}}
+#endif
+
+#if __aarch64__
+#define VALID_CODE_KEY 0
+#define VALID_DATA_KEY 2
+#define INVALID_KEY 200
+#else
+#error Provide these constants if you port this test
+#endif
+
+#define NULL ((void*) 0)
+struct A { int x; } mismatched_type;
+
+extern int dv;
+extern int fv(int);
+
+void test_strip(int *dp, int (*fp)(int)) {
+ __builtin_ptrauth_strip(dp); // expected-error {{too few arguments}}
+ __builtin_ptrauth_strip(dp, VALID_DATA_KEY, dp); // expected-error {{too many arguments}}
+ (void) __builtin_ptrauth_strip(NULL, VALID_DATA_KEY); // no warning
+
+ __builtin_ptrauth_strip(mismatched_type, VALID_DATA_KEY); // expected-error {{signed value must have pointer type; type here is 'struct A'}}
+ __builtin_ptrauth_strip(dp, mismatched_type); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}}
+
+ int *dr = __builtin_ptrauth_strip(dp, VALID_DATA_KEY);
+ dr = __builtin_ptrauth_strip(dp, INVALID_KEY); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+
+ int (*fr)(int) = __builtin_ptrauth_strip(fp, VALID_CODE_KEY);
+ fr = __builtin_ptrauth_strip(fp, INVALID_KEY); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+
+ float *mismatch = __builtin_ptrauth_strip(dp, VALID_DATA_KEY); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}}
+}
+
+void test_blend_discriminator(int *dp, int (*fp)(int), int value) {
+ __builtin_ptrauth_blend_discriminator(dp); // expected-error {{too few arguments}}
+ __builtin_ptrauth_blend_discriminator(dp, dp, dp); // expected-error {{too many arguments}}
+ (void) __builtin_ptrauth_blend_discriminator(dp, value); // no warning
+
+ __builtin_ptrauth_blend_discriminator(mismatched_type, value); // expected-error {{blended pointer must have pointer type; type here is 'struct A'}}
+ __builtin_ptrauth_blend_discriminator(dp, mismatched_type); // expected-error {{blended integer must have integer type; type here is 'struct A'}}
+
+ float *mismatch = __builtin_ptrauth_blend_discriminator(dp, value); // expected-error {{incompatible integer to pointer conversion initializing 'float *' with an expression of type}}
+}
+
+void test_sign_unauthenticated(int *dp, int (*fp)(int)) {
+ __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY); // expected-error {{too few arguments}}
+ __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, dp, dp); // expected-error {{too many arguments}}
+
+ __builtin_ptrauth_sign_unauthenticated(mismatched_type, VALID_DATA_KEY, 0); // expected-error {{signed value must have pointer type; type here is 'struct A'}}
+ __builtin_ptrauth_sign_unauthenticated(dp, mismatched_type, 0); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}}
+ __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}}
+
+ (void) __builtin_ptrauth_sign_unauthenticated(NULL, VALID_DATA_KEY, 0); // expected-warning {{signing a null pointer will yield a non-null pointer}}
+
+ int *dr = __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, 0);
+ dr = __builtin_ptrauth_sign_unauthenticated(dp, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+
+ int (*fr)(int) = __builtin_ptrauth_sign_unauthenticated(fp, VALID_CODE_KEY, 0);
+ fr = __builtin_ptrauth_sign_unauthenticated(fp, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+
+ float *mismatch = __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, 0); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}}
+}
+
+void test_auth(int *dp, int (*fp)(int)) {
+ __builtin_ptrauth_auth(dp, VALID_DATA_KEY); // expected-error {{too few arguments}}
+ __builtin_ptrauth_auth(dp, VALID_DATA_KEY, dp, dp); // expected-error {{too many arguments}}
+
+ __builtin_ptrauth_auth(mismatched_type, VALID_DATA_KEY, 0); // expected-error {{signed value must have pointer type; type here is 'struct A'}}
+ __builtin_ptrauth_auth(dp, mismatched_type, 0); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}}
+ __builtin_ptrauth_auth(dp, VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}}
+
+ (void) __builtin_ptrauth_auth(NULL, VALID_DATA_KEY, 0); // expected-warning {{authenticating a null pointer will almost certainly trap}}
+
+ int *dr = __builtin_ptrauth_auth(dp, VALID_DATA_KEY, 0);
+ dr = __builtin_ptrauth_auth(dp, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+
+ int (*fr)(int) = __builtin_ptrauth_auth(fp, VALID_CODE_KEY, 0);
+ fr = __builtin_ptrauth_auth(fp, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+
+ float *mismatch = __builtin_ptrauth_auth(dp, VALID_DATA_KEY, 0); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}}
+}
+
+void test_auth_and_resign(int *dp, int (*fp)(int)) {
+ __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, VALID_DATA_KEY); // expected-error {{too few arguments}}
+ __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, dp, VALID_DATA_KEY, dp, 0); // expected-error {{too many arguments}}
+
+ __builtin_ptrauth_auth_and_resign(mismatched_type, VALID_DATA_KEY, 0, VALID_DATA_KEY, dp); // expected-error {{signed value must have pointer type; type here is 'struct A'}}
+ __builtin_ptrauth_auth_and_resign(dp, mismatched_type, 0, VALID_DATA_KEY, dp); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}}
+ __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, mismatched_type, VALID_DATA_KEY, dp); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}}
+ __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, mismatched_type, dp); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}}
+ __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}}
+
+ (void) __builtin_ptrauth_auth_and_resign(NULL, VALID_DATA_KEY, 0, VALID_DATA_KEY, dp); // expected-warning {{authenticating a null pointer will almost certainly trap}}
+
+ int *dr = __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, VALID_DATA_KEY, dp);
+ dr = __builtin_ptrauth_auth_and_resign(dp, INVALID_KEY, 0, VALID_DATA_KEY, dp); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+ dr = __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, INVALID_KEY, dp); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+
+ int (*fr)(int) = __builtin_ptrauth_auth_and_resign(fp, VALID_CODE_KEY, 0, VALID_CODE_KEY, dp);
+ fr = __builtin_ptrauth_auth_and_resign(fp, INVALID_KEY, 0, VALID_CODE_KEY, dp); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+ fr = __builtin_ptrauth_auth_and_resign(fp, VALID_CODE_KEY, 0, INVALID_KEY, dp); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+
+ float *mismatch = __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, VALID_DATA_KEY, dp); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}}
+}
+
+void test_sign_generic_data(int *dp) {
+ __builtin_ptrauth_sign_generic_data(dp); // expected-error {{too few arguments}}
+ __builtin_ptrauth_sign_generic_data(dp, 0, 0); // expected-error {{too many arguments}}
+
+ __builtin_ptrauth_sign_generic_data(mismatched_type, 0); // expected-error {{signed value must have pointer or integer type; type here is 'struct A'}}
+ __builtin_ptrauth_sign_generic_data(dp, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}}
+
+ (void) __builtin_ptrauth_sign_generic_data(NULL, 0); // no warning
+
+ unsigned long dr = __builtin_ptrauth_sign_generic_data(dp, 0);
+ dr = __builtin_ptrauth_sign_generic_data(dp, &dv);
+ dr = __builtin_ptrauth_sign_generic_data(12314, 0);
+ dr = __builtin_ptrauth_sign_generic_data(12314, &dv);
+
+ int *mismatch = __builtin_ptrauth_sign_generic_data(dp, 0); // expected-error {{incompatible integer to pointer conversion initializing 'int *' with an expression of type}}
+}
diff --git a/llvm/docs/PointerAuth.md b/llvm/docs/PointerAuth.md
index 41266b4..a8d2b4d 100644
--- a/llvm/docs/PointerAuth.md
+++ b/llvm/docs/PointerAuth.md
@@ -10,6 +10,9 @@
signature checked. This prevents pointer values of unknown origin from being
used to replace the signed pointer value.
+For more details, see the clang documentation page for
+[Pointer Authentication](https://clang.llvm.org/docs/PointerAuthentication.html).
+
At the IR level, it is represented using:
* a [set of intrinsics](#intrinsics) (to sign/authenticate pointers)