Skip to content

Conversation

@ojhunt
Copy link
Contributor

@ojhunt ojhunt commented Sep 27, 2025

This introduces a number of additional macros to support the use of manual pointer authentication.

@llvmbot llvmbot added clang Clang issues not falling into any other category backend:X86 clang:headers Headers provided by Clang, e.g. for intrinsics labels Sep 27, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 27, 2025

@llvm/pr-subscribers-backend-x86

Author: Oliver Hunt (ojhunt)

Changes

This introduces a number of additional macros to support the use of manual pointer authentication.


Full diff: https://github.com/llvm/llvm-project/pull/161027.diff

2 Files Affected:

  • (modified) clang/docs/PointerAuthentication.rst (+34)
  • (modified) clang/lib/Headers/ptrauth.h (+73)
diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst
index 96eb498bc48b6..eb4eaf08b854e 100644
--- a/clang/docs/PointerAuthentication.rst
+++ b/clang/docs/PointerAuthentication.rst
@@ -722,6 +722,40 @@ 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.
 
+``ptrauth_nop_cast``
+^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+  ptrauth_nop_cast(__type, __value)
+
+Cast a pointer to the given type without changing any signature.
+
+This operation can be used to convert a value from one type to another without
+attempting to re-sign the value. This makes it possible to view a signed value
+of one type as another type signed with the same schema. This can be used to
+convert implicit schemas to explicit schemas, to convert to or from opaque
+types, or simply to change the effective underlying type of a signed value.
+
+The `__type` must be a pointer sized value compatible with the `__ptrauth`
+qualifier. The authentication schema must not include address diversity.
+
+The result is a bitwise identical value with the type passed as the `__type`
+argument.
+
+``ptrauth_function_pointer_type_discriminator``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+  ptrauth_function_pointer_type_discriminator(__type)
+
+Compute the constant discriminator used by Clang to sign pointers with the
+given C function pointer type.
+
+A call to this function is an integer constant expression.
+
+
 Standard ``__ptrauth`` qualifiers
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h
index f902ca1e3bbd3..1544456e189b4 100644
--- a/clang/lib/Headers/ptrauth.h
+++ b/clang/lib/Headers/ptrauth.h
@@ -199,6 +199,23 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
   ptrauth_auth_and_resign(__value, __old_key, __old_data,                      \
                           ptrauth_key_function_pointer, 0)
 
+/* Cast a value to the given type without changing any signature.
+
+   The type must be a pointer sized type compatible with the __ptrauth
+   qualifier.
+   The value must be an expression with a non-address diversified pointer
+   authentication schema, and will be converted to an rvalue prior to the cast.
+   The result has type given by the first argument.
+
+   The result has an identical bit-pattern to the input pointer. */
+#define ptrauth_nop_cast(__type, __value)                                      \
+  ({ union {                                                                   \
+      typeof(*(__value)) *__fptr;                                              \
+      typeof(__type) __opaque;                                                 \
+  } __storage;                                                                 \
+  __storage.__fptr = (__value);                                                \
+  __storage.__opaque; })
+
 /* Authenticate a data pointer.
 
    The value must be an expression of non-function pointer type.
@@ -241,6 +258,18 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
 #define ptrauth_type_discriminator(__type)                                     \
   __builtin_ptrauth_type_discriminator(__type)
 
+/* Compute the constant discriminator used by Clang to sign pointers with the
+   given C function pointer type.
+
+   A call to this function is an integer constant expression. */
+#if __has_feature(ptrauth_function_pointer_type_discrimination)
+#define ptrauth_function_pointer_type_discriminator(__type)                    \
+  __builtin_ptrauth_type_discriminator(__type)
+#else
+#define ptrauth_function_pointer_type_discriminator(__type)                    \
+  ((ptrauth_extra_data_t)0)
+#endif
+
 /* Compute a signature for the given pair of pointer-sized values.
    The order of the arguments is significant.
 
@@ -263,6 +292,33 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
 #define ptrauth_sign_generic_data(__value, __data)                             \
   __builtin_ptrauth_sign_generic_data(__value, __data)
 
+/* Define some standard __ptrauth qualifiers used in the ABI. */
+#define __ptrauth_function_pointer(__typekey)                                  \
+  __ptrauth(ptrauth_key_function_pointer, 0, __typekey)
+#define __ptrauth_return_address                                               \
+  __ptrauth(ptrauth_key_return_address, 1, 0)
+#define __ptrauth_block_invocation_pointer                                     \
+  __ptrauth(ptrauth_key_function_pointer, 1, 0)
+#define __ptrauth_block_copy_helper                                            \
+  __ptrauth(ptrauth_key_function_pointer, 1, 0)
+#define __ptrauth_block_destroy_helper                                         \
+  __ptrauth(ptrauth_key_function_pointer, 1, 0)
+#define __ptrauth_block_byref_copy_helper                                      \
+  __ptrauth(ptrauth_key_function_pointer, 1, 0)
+#define __ptrauth_block_byref_destroy_helper                                   \
+  __ptrauth(ptrauth_key_function_pointer, 1, 0)
+#if __has_feature(ptrauth_signed_block_descriptors)
+#define __ptrauth_block_descriptor_pointer                                     \
+  __ptrauth(ptrauth_key_block_descriptor_pointer, 1, 0xC0BB)
+#else
+#define __ptrauth_block_descriptor_pointer
+#endif
+
+#define __ptrauth_cxx_vtable_pointer                                           \
+  __ptrauth(ptrauth_key_cxx_vtable_pointer, 0, 0)
+#define __ptrauth_cxx_vtt_vtable_pointer                                       \
+  __ptrauth(ptrauth_key_cxx_vtable_pointer, 0, 0)
+
 /* C++ vtable pointer signing class attribute */
 #define ptrauth_cxx_vtable_pointer(key, address_discrimination,                \
                                    extra_discrimination...)                    \
@@ -371,7 +427,10 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
     ((ptrauth_extra_data_t)0);                                                 \
   })
 
+#define ptrauth_nop_cast(__type, __value) (__type)(__value)
 #define ptrauth_type_discriminator(__type) ((ptrauth_extra_data_t)0)
+#define ptrauth_function_pointer_type_discriminator(__type)                    \
+  ((ptrauth_extra_data_t)0)
 
 #define ptrauth_sign_generic_data(__value, __data)                             \
   ({                                                                           \
@@ -384,9 +443,23 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
 #define ptrauth_cxx_vtable_pointer(key, address_discrimination,                \
                                    extra_discrimination...)
 
+#define __ptrauth_function_pointer(__typekey)
+#define __ptrauth_return_address
+#define __ptrauth_block_invocation_pointer
+#define __ptrauth_block_copy_helper
+#define __ptrauth_block_destroy_helper
+#define __ptrauth_block_byref_copy_helper
+#define __ptrauth_block_byref_destroy_helper
+#define __ptrauth_block_descriptor_pointer
+#define __ptrauth_objc_method_list_imp
+#define __ptrauth_objc_method_list_pointer
 #define __ptrauth_objc_isa_pointer
 #define __ptrauth_objc_isa_uintptr
 #define __ptrauth_objc_super_pointer
+#define __ptrauth_cxx_vtable_pointer
+#define __ptrauth_cxx_vtt_vtable_pointer
+#define __ptrauth_objc_sel
+#define __ptrauth_objc_class_ro
 
 #endif /* __has_feature(ptrauth_intrinsics) || defined(__PTRAUTH__) */
 

@llvmbot
Copy link
Member

llvmbot commented Sep 27, 2025

@llvm/pr-subscribers-clang

Author: Oliver Hunt (ojhunt)

Changes

This introduces a number of additional macros to support the use of manual pointer authentication.


Full diff: https://github.com/llvm/llvm-project/pull/161027.diff

2 Files Affected:

  • (modified) clang/docs/PointerAuthentication.rst (+34)
  • (modified) clang/lib/Headers/ptrauth.h (+73)
diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst
index 96eb498bc48b6..eb4eaf08b854e 100644
--- a/clang/docs/PointerAuthentication.rst
+++ b/clang/docs/PointerAuthentication.rst
@@ -722,6 +722,40 @@ 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.
 
+``ptrauth_nop_cast``
+^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+  ptrauth_nop_cast(__type, __value)
+
+Cast a pointer to the given type without changing any signature.
+
+This operation can be used to convert a value from one type to another without
+attempting to re-sign the value. This makes it possible to view a signed value
+of one type as another type signed with the same schema. This can be used to
+convert implicit schemas to explicit schemas, to convert to or from opaque
+types, or simply to change the effective underlying type of a signed value.
+
+The `__type` must be a pointer sized value compatible with the `__ptrauth`
+qualifier. The authentication schema must not include address diversity.
+
+The result is a bitwise identical value with the type passed as the `__type`
+argument.
+
+``ptrauth_function_pointer_type_discriminator``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+  ptrauth_function_pointer_type_discriminator(__type)
+
+Compute the constant discriminator used by Clang to sign pointers with the
+given C function pointer type.
+
+A call to this function is an integer constant expression.
+
+
 Standard ``__ptrauth`` qualifiers
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h
index f902ca1e3bbd3..1544456e189b4 100644
--- a/clang/lib/Headers/ptrauth.h
+++ b/clang/lib/Headers/ptrauth.h
@@ -199,6 +199,23 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
   ptrauth_auth_and_resign(__value, __old_key, __old_data,                      \
                           ptrauth_key_function_pointer, 0)
 
+/* Cast a value to the given type without changing any signature.
+
+   The type must be a pointer sized type compatible with the __ptrauth
+   qualifier.
+   The value must be an expression with a non-address diversified pointer
+   authentication schema, and will be converted to an rvalue prior to the cast.
+   The result has type given by the first argument.
+
+   The result has an identical bit-pattern to the input pointer. */
+#define ptrauth_nop_cast(__type, __value)                                      \
+  ({ union {                                                                   \
+      typeof(*(__value)) *__fptr;                                              \
+      typeof(__type) __opaque;                                                 \
+  } __storage;                                                                 \
+  __storage.__fptr = (__value);                                                \
+  __storage.__opaque; })
+
 /* Authenticate a data pointer.
 
    The value must be an expression of non-function pointer type.
@@ -241,6 +258,18 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
 #define ptrauth_type_discriminator(__type)                                     \
   __builtin_ptrauth_type_discriminator(__type)
 
+/* Compute the constant discriminator used by Clang to sign pointers with the
+   given C function pointer type.
+
+   A call to this function is an integer constant expression. */
+#if __has_feature(ptrauth_function_pointer_type_discrimination)
+#define ptrauth_function_pointer_type_discriminator(__type)                    \
+  __builtin_ptrauth_type_discriminator(__type)
+#else
+#define ptrauth_function_pointer_type_discriminator(__type)                    \
+  ((ptrauth_extra_data_t)0)
+#endif
+
 /* Compute a signature for the given pair of pointer-sized values.
    The order of the arguments is significant.
 
@@ -263,6 +292,33 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
 #define ptrauth_sign_generic_data(__value, __data)                             \
   __builtin_ptrauth_sign_generic_data(__value, __data)
 
+/* Define some standard __ptrauth qualifiers used in the ABI. */
+#define __ptrauth_function_pointer(__typekey)                                  \
+  __ptrauth(ptrauth_key_function_pointer, 0, __typekey)
+#define __ptrauth_return_address                                               \
+  __ptrauth(ptrauth_key_return_address, 1, 0)
+#define __ptrauth_block_invocation_pointer                                     \
+  __ptrauth(ptrauth_key_function_pointer, 1, 0)
+#define __ptrauth_block_copy_helper                                            \
+  __ptrauth(ptrauth_key_function_pointer, 1, 0)
+#define __ptrauth_block_destroy_helper                                         \
+  __ptrauth(ptrauth_key_function_pointer, 1, 0)
+#define __ptrauth_block_byref_copy_helper                                      \
+  __ptrauth(ptrauth_key_function_pointer, 1, 0)
+#define __ptrauth_block_byref_destroy_helper                                   \
+  __ptrauth(ptrauth_key_function_pointer, 1, 0)
+#if __has_feature(ptrauth_signed_block_descriptors)
+#define __ptrauth_block_descriptor_pointer                                     \
+  __ptrauth(ptrauth_key_block_descriptor_pointer, 1, 0xC0BB)
+#else
+#define __ptrauth_block_descriptor_pointer
+#endif
+
+#define __ptrauth_cxx_vtable_pointer                                           \
+  __ptrauth(ptrauth_key_cxx_vtable_pointer, 0, 0)
+#define __ptrauth_cxx_vtt_vtable_pointer                                       \
+  __ptrauth(ptrauth_key_cxx_vtable_pointer, 0, 0)
+
 /* C++ vtable pointer signing class attribute */
 #define ptrauth_cxx_vtable_pointer(key, address_discrimination,                \
                                    extra_discrimination...)                    \
@@ -371,7 +427,10 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
     ((ptrauth_extra_data_t)0);                                                 \
   })
 
+#define ptrauth_nop_cast(__type, __value) (__type)(__value)
 #define ptrauth_type_discriminator(__type) ((ptrauth_extra_data_t)0)
+#define ptrauth_function_pointer_type_discriminator(__type)                    \
+  ((ptrauth_extra_data_t)0)
 
 #define ptrauth_sign_generic_data(__value, __data)                             \
   ({                                                                           \
@@ -384,9 +443,23 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
 #define ptrauth_cxx_vtable_pointer(key, address_discrimination,                \
                                    extra_discrimination...)
 
+#define __ptrauth_function_pointer(__typekey)
+#define __ptrauth_return_address
+#define __ptrauth_block_invocation_pointer
+#define __ptrauth_block_copy_helper
+#define __ptrauth_block_destroy_helper
+#define __ptrauth_block_byref_copy_helper
+#define __ptrauth_block_byref_destroy_helper
+#define __ptrauth_block_descriptor_pointer
+#define __ptrauth_objc_method_list_imp
+#define __ptrauth_objc_method_list_pointer
 #define __ptrauth_objc_isa_pointer
 #define __ptrauth_objc_isa_uintptr
 #define __ptrauth_objc_super_pointer
+#define __ptrauth_cxx_vtable_pointer
+#define __ptrauth_cxx_vtt_vtable_pointer
+#define __ptrauth_objc_sel
+#define __ptrauth_objc_class_ro
 
 #endif /* __has_feature(ptrauth_intrinsics) || defined(__PTRAUTH__) */
 

@github-actions
Copy link

github-actions bot commented Sep 27, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

This introduces a number of additional macros to support the
use of manual pointer authentication.
@ojhunt ojhunt force-pushed the users/ojhunt/ptrauth-additions branch from d9c3119 to 6bc5400 Compare September 27, 2025 23:32
@ojhunt
Copy link
Contributor Author

ojhunt commented Sep 27, 2025

Style differences a sufficiently minor that I'll take them and deal with future merge issues when they come up.

typeof(__type) __opaque; \
} __storage; \
__storage.__fptr = (__value); \
__storage.__opaque; \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this should be valid in C, in C++ reading from the member of the union that wasn't most recently written is technically UB: https://en.cppreference.com/w/cpp/language/union.html.

I think we need to conform to C++ standard and use smth like memcpy for doing such a trick.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using memcpy isn't an option as this may be used in environments where they're not available, hence the awkward union

The result has type given by the first argument.
The result has an identical bit-pattern to the input pointer. */
#define ptrauth_nop_cast(__type, __value) \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably worth adding tests at least for this macro since it's not trivial. Tests for other macros would be nice to have as well.

#define ptrauth_cxx_vtable_pointer(key, address_discrimination, \
extra_discrimination...)

#define __ptrauth_function_pointer(__typekey)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please explain what purpose do these empty macros serve for?

Copy link
Contributor

@atrosinenko atrosinenko Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kovdan01 As far as I understand, they are intended to be used along the lines

callback_t __ptrauth_function_pointer(ptrauth_function_pointer_type_discriminator(callback_t)) *cb;

or as a better example

struct state {
  // ...
  uintptr_t __ptrauth_return_address saved_ret_addr;
  // ...
};

You have to "annotate" the variables with such macros at least once anyway, so without these empty defines you would have to duplicate variable definitions under #ifdefs (or even define empty macroses yourself and return to the original approach).

PS: Same as above :)

Suggested change
#define __ptrauth_function_pointer(__typekey)
#define __ptrauth_function_pointer(__type_discriminator)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as @atrosinenko it's to support general headers using standard qualifiers without having to duplicate every declaration :D

A call to this function is an integer constant expression. */
#if __has_feature(ptrauth_function_pointer_type_discrimination)
#define ptrauth_function_pointer_type_discriminator(__type) \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: it's very subjective, but I find the naming a bit misleading. When I first read this, I though that this unconditionally defines a discriminator which is dependent on function type, while the discriminator is either type-based or constant zero depending on ptrauth_function_pointer_type_discrimination feature.

Maybe we can just avoid the word "type" in macro name, like ptrauth_function_pointer_discriminator. Or think of other alternatives in naming.

Feel free to ignore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's kind of awkwardly named, but is used extensively :-/

Copy link
Contributor

@atrosinenko atrosinenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR, added a few nit-picky comments.

Comment on lines +740 to +741
The `__type` must be a pointer sized value compatible with the `__ptrauth`
qualifier. The authentication schema must not include address diversity.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The authentication schema must not include address diversity.

It is probably worth clarifying explicitly whether this applies to "input" type (implicitly provided via __value), "output" type (explicitly specified by __type argument) or both.

Comment on lines +298 to +299
#define __ptrauth_function_pointer(__typekey) \
__ptrauth(ptrauth_key_function_pointer, 0, __typekey)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__type_discriminator was probably intended here, as the key is hardcoded

Suggested change
#define __ptrauth_function_pointer(__typekey) \
__ptrauth(ptrauth_key_function_pointer, 0, __typekey)
#define __ptrauth_function_pointer(__type_discriminator) \
__ptrauth(ptrauth_key_function_pointer, 0, __type_discriminator)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

#define ptrauth_cxx_vtable_pointer(key, address_discrimination, \
extra_discrimination...)

#define __ptrauth_function_pointer(__typekey)
Copy link
Contributor

@atrosinenko atrosinenko Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kovdan01 As far as I understand, they are intended to be used along the lines

callback_t __ptrauth_function_pointer(ptrauth_function_pointer_type_discriminator(callback_t)) *cb;

or as a better example

struct state {
  // ...
  uintptr_t __ptrauth_return_address saved_ret_addr;
  // ...
};

You have to "annotate" the variables with such macros at least once anyway, so without these empty defines you would have to duplicate variable definitions under #ifdefs (or even define empty macroses yourself and return to the original approach).

PS: Same as above :)

Suggested change
#define __ptrauth_function_pointer(__typekey)
#define __ptrauth_function_pointer(__type_discriminator)

Comment on lines +206 to +208
The value must be an expression with a non-address diversified pointer
authentication schema, and will be converted to an rvalue prior to the cast.
The result has type given by the first argument.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably neither value nor result type can be address-diversified? Or is it possible to request address diversity on the result by some С++ trick?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alas no, and these have to work in C

@kovdan01
Copy link
Contributor

@ojhunt Can we consider this PR obsolete and close it since we no longer use this union approach and things are already (almost) fixed in the main unwinding PR #143230?

@ojhunt
Copy link
Contributor Author

ojhunt commented Oct 20, 2025

@ojhunt Can we consider this PR obsolete and close it since we no longer use this union approach and things are already (almost) fixed in the main unwinding PR #143230?

They're macros that are used, and are useful on their own so upstreaming still seems sensible? they were just for the unwind work I had just genuinely assumed they were already upstreamed :D

@kovdan01
Copy link
Contributor

They're macros that are used, and are useful on their own so upstreaming still seems sensible? they were just for the unwind work I had just genuinely assumed they were already upstreamed :D

@ojhunt Sure, we can do that. But if doing that, it's at least worth deleting ptrauth_function_pointer_type_discriminator from this PR since it was already introduced in #163456. And other comments, e.g. adding tests for non-trivial functionality and avoiding UB when using unions, should also be addressed prior to merging :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:X86 clang:headers Headers provided by Clang, e.g. for intrinsics clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants