[clang] [llvm] [AArch64][PAC] Support ptrauth builtins and -fptrauth-intrinsics. (PR #65996)
Ahmed Bougacha via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 26 09:43:29 PST 2024
https://github.com/ahmedbougacha updated https://github.com/llvm/llvm-project/pull/65996
>From c5cde09a5ea4a4cfcf3287f7462c38335493c02d Mon Sep 17 00:00:00 2001
From: Ahmed Bougacha <ahmed at bougacha.org>
Date: Wed, 16 Aug 2023 09:17:12 -0700
Subject: [PATCH] [AArch64][PAC] Support and document ptrauth builtins and
-fptrauth-intrinsics.
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 at apple.com>
Co-Authored-By: John McCall <rjmccall at apple.com>
---
clang/docs/LanguageExtensions.rst | 5 +
clang/docs/PointerAuthentication.rst | 485 ++++++++++++++++++
clang/include/clang/Basic/Builtins.td | 37 ++
clang/include/clang/Basic/DiagnosticGroups.td | 1 +
.../clang/Basic/DiagnosticSemaKinds.td | 16 +
clang/include/clang/Basic/Features.def | 1 +
clang/include/clang/Basic/LangOptions.def | 2 +
clang/include/clang/Basic/TargetInfo.h | 6 +
clang/include/clang/Driver/Options.td | 8 +
clang/include/clang/Sema/Sema.h | 2 +
clang/lib/Basic/TargetInfo.cpp | 4 +
clang/lib/Basic/Targets/AArch64.cpp | 6 +
clang/lib/Basic/Targets/AArch64.h | 2 +
clang/lib/CodeGen/CGBuiltin.cpp | 67 +++
clang/lib/Driver/ToolChains/Clang.cpp | 4 +
clang/lib/Frontend/CompilerInvocation.cpp | 14 +
clang/lib/Headers/CMakeLists.txt | 1 +
clang/lib/Headers/module.modulemap | 5 +
clang/lib/Headers/ptrauth.h | 185 +++++++
clang/lib/Sema/SemaChecking.cpp | 197 +++++++
clang/test/CodeGen/ptrauth-intrinsics.c | 73 +++
clang/test/Preprocessor/ptrauth_feature.c | 10 +
clang/test/Sema/ptrauth-intrinsics-macro.c | 34 ++
clang/test/Sema/ptrauth.c | 126 +++++
llvm/docs/PointerAuth.md | 3 +
25 files changed, 1294 insertions(+)
create mode 100644 clang/docs/PointerAuthentication.rst
create mode 100644 clang/lib/Headers/ptrauth.h
create mode 100644 clang/test/CodeGen/ptrauth-intrinsics.c
create mode 100644 clang/test/Preprocessor/ptrauth_feature.c
create mode 100644 clang/test/Sema/ptrauth-intrinsics-macro.c
create mode 100644 clang/test/Sema/ptrauth.c
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 711baf45f449a05..c818a1d805f51e1 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -13,6 +13,7 @@ Clang Language Extensions
BlockLanguageSpec
Block-ABI-Apple
AutomaticReferenceCounting
+ PointerAuthentication
MatrixTypes
Introduction
@@ -4244,6 +4245,10 @@ reordering of memory accesses and side effect instructions. Other instructions
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 000000000000000..0ecdfb34e8cf72a
--- /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 can
+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 e3432f7925ba141..dcc91771f64b4ef 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4324,6 +4324,43 @@ def CoroSuspend : CoroLangBuiltin {
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 e8b4139d7893ce8..b1a014d3abfa62a 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -865,6 +865,7 @@ def ZeroLengthArray : DiagGroup<"zero-length-array">;
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 57784a4ba2e3882..ec55d396d15bb4d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -911,6 +911,22 @@ def warn_fortify_scanf_overflow : Warning<
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 5fad5fc3623cb63..eeed5f4751f2f4e 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -101,6 +101,7 @@ FEATURE(memory_sanitizer,
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 2b42b521a303633..cdefad828b7b85f 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -161,6 +161,8 @@ LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods"
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 48e9cec482755c3..5e4af1248b34837 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"
@@ -1549,6 +1550,11 @@ class TargetInfo : public TransferrableTargetInfo,
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 3a028fadb25b18b..a3dd13451c502e7 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4068,6 +4068,14 @@ defm strict_return : BoolFOption<"strict-return",
" 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 ef4b93fac95ce53..1684d173ab6687a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3041,6 +3041,8 @@ class Sema final {
SourceLocation AtomicQualLoc = SourceLocation(),
SourceLocation UnalignedQualLoc = SourceLocation());
+ bool checkConstantPointerAuthKey(Expr *keyExpr, unsigned &key);
+
static bool adjustContextForLocalExternDecl(DeclContext *&DC);
void DiagnoseFunctionSpecifiers(const DeclSpec &DS);
NamedDecl *getShadowedDeclaration(const TypedefNameDecl *D,
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index 96b3ad9ba2f2731..5d9055174c089ad 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -925,6 +925,10 @@ bool TargetInfo::validateInputConstraint(
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 5abb060073c517e..fa8b5a869594b3a 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 @@ int AArch64TargetInfo::getEHDataRegisterNumber(unsigned RegNo) const {
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 c1ba156860a1220..2dd6b2181e87df6 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -195,6 +195,8 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
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 54d7451a9d6221f..50b68251941a595 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -5207,6 +5207,73 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
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 6e1b7e8657d0dc9..a7c1f2f078224fd 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7171,6 +7171,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
// -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 8d7b75b56d61290..dd9c301e1e5370d 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -3288,6 +3288,17 @@ static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args,
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) {
@@ -4606,6 +4617,8 @@ bool CompilerInvocation::CreateFromArgsImpl(
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)
@@ -4836,6 +4849,7 @@ void CompilerInvocationBase::generateCC1CommandLine(
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 b9a966be73ee97b..902e33bb95897cf 100644
--- a/clang/lib/Headers/CMakeLists.txt
+++ b/clang/lib/Headers/CMakeLists.txt
@@ -214,6 +214,7 @@ set(x86_files
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 a786689d391773c..e3a75711f14658f 100644
--- a/clang/lib/Headers/module.modulemap
+++ b/clang/lib/Headers/module.modulemap
@@ -317,3 +317,8 @@ module opencl_c {
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 000000000000000..56c3c3636c9bcd9
--- /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 984088e345c806f..748feebbbf84d18 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1971,6 +1971,191 @@ static bool SemaOpenCLBuiltinToAddr(Sema &S, unsigned BuiltinID,
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();
@@ -2658,6 +2843,18 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
}
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 000000000000000..17f28dddb380153
--- /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 000000000000000..e45c6ea90fd11f6
--- /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 000000000000000..07d637404514523
--- /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 000000000000000..3ad3d70c24e4130
--- /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 at -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 41266b43bc29ab7..a8d2b4d8f5f0bdc 100644
--- a/llvm/docs/PointerAuth.md
+++ b/llvm/docs/PointerAuth.md
@@ -10,6 +10,9 @@ 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.
+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)
More information about the cfe-commits
mailing list