Skip to content

Commit a3283a9

Browse files
authored
[PAC] Add support for __ptrauth type qualifier (#100830)
The qualifier allows programmer to directly control how pointers are signed when they are stored in a particular variable. The qualifier takes three arguments: the signing key, a flag specifying whether address discrimination should be used, and a non-negative integer that is used for additional discrimination. ``` typedef void (*my_callback)(const void*); my_callback __ptrauth(ptrauth_key_process_dependent_code, 1, 0xe27a) callback; ``` Co-Authored-By: John McCall [email protected]
1 parent e6e56f5 commit a3283a9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2801
-39
lines changed

clang/docs/PointerAuthentication.rst

+46
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,52 @@ a number of different tests.
280280
normal interface. This may be true even on targets where pointer
281281
authentication is not enabled by default.
282282

283+
__ptrauth Qualifier
284+
^^^^^^^^^^^^^^^^^^^
285+
286+
``__ptrauth(key, address, discriminator)`` is an extended type
287+
qualifier which causes so-qualified objects to hold pointers signed using the
288+
specified schema rather than the default schema for such types.
289+
290+
In the current implementation in Clang, the qualified type must be a C pointer
291+
type, either to a function or to an object. It currently cannot be an
292+
Objective-C pointer type, a C++ reference type, or a block pointer type; these
293+
restrictions may be lifted in the future.
294+
295+
The qualifier's operands are as follows:
296+
297+
- ``key`` - an expression evaluating to a key value from ``<ptrauth.h>``; must
298+
be a constant expression
299+
300+
- ``address`` - whether to use address diversity (1) or not (0); must be
301+
a constant expression with one of these two values
302+
303+
- ``discriminator`` - a constant discriminator; must be a constant expression
304+
305+
See `Discriminators`_ for more information about discriminators.
306+
307+
Currently the operands must be constant-evaluable even within templates. In the
308+
future this restriction may be lifted to allow value-dependent expressions as
309+
long as they instantiate to a constant expression.
310+
311+
Consistent with the ordinary C/C++ rule for parameters, top-level ``__ptrauth``
312+
qualifiers on a parameter (after parameter type adjustment) are ignored when
313+
deriving the type of the function. The parameter will be passed using the
314+
default ABI for the unqualified pointer type.
315+
316+
If ``x`` is an object of type ``__ptrauth(key, address, discriminator) T``,
317+
then the signing schema of the value stored in ``x`` is a key of ``key`` and
318+
a discriminator determined as follows:
319+
320+
- if ``address`` is 0, then the discriminator is ``discriminator``;
321+
322+
- if ``address`` is 1 and ``discriminator`` is 0, then the discriminator is
323+
``&x``; otherwise
324+
325+
- if ``address`` is 1 and ``discriminator`` is non-zero, then the discriminator
326+
is ``ptrauth_blend_discriminator(&x, discriminator)``; see
327+
`ptrauth_blend_discriminator`_.
328+
283329
``<ptrauth.h>``
284330
~~~~~~~~~~~~~~~
285331

clang/docs/ReleaseNotes.rst

+2
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,8 @@ Arm and AArch64 Support
552552
ARM targets, however this will now disable NEON instructions being generated. The ``simd`` option is
553553
also now printed when the ``--print-supported-extensions`` option is used.
554554

555+
- Support for __ptrauth type qualifier has been added.
556+
555557
Android Support
556558
^^^^^^^^^^^^^^^
557559

clang/include/clang/AST/Type.h

+17-4
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,12 @@ class PointerAuthQualifier {
312312
return Result;
313313
}
314314

315+
std::string getAsString() const;
316+
std::string getAsString(const PrintingPolicy &Policy) const;
317+
318+
bool isEmptyWhenPrinted(const PrintingPolicy &Policy) const;
319+
void print(raw_ostream &OS, const PrintingPolicy &Policy) const;
320+
315321
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Data); }
316322
};
317323

@@ -562,7 +568,7 @@ class Qualifiers {
562568

563569
bool hasAddressSpace() const { return Mask & AddressSpaceMask; }
564570
LangAS getAddressSpace() const {
565-
return static_cast<LangAS>(Mask >> AddressSpaceShift);
571+
return static_cast<LangAS>((Mask & AddressSpaceMask) >> AddressSpaceShift);
566572
}
567573
bool hasTargetSpecificAddressSpace() const {
568574
return isTargetAddressSpace(getAddressSpace());
@@ -803,17 +809,18 @@ class Qualifiers {
803809
static_assert(sizeof(PointerAuthQualifier) == sizeof(uint32_t),
804810
"PointerAuthQualifier must be 32 bits");
805811

812+
static constexpr uint64_t PtrAuthShift = 32;
813+
static constexpr uint64_t PtrAuthMask = UINT64_C(0xffffffff) << PtrAuthShift;
814+
806815
static constexpr uint64_t UMask = 0x8;
807816
static constexpr uint64_t UShift = 3;
808817
static constexpr uint64_t GCAttrMask = 0x30;
809818
static constexpr uint64_t GCAttrShift = 4;
810819
static constexpr uint64_t LifetimeMask = 0x1C0;
811820
static constexpr uint64_t LifetimeShift = 6;
812821
static constexpr uint64_t AddressSpaceMask =
813-
~(CVRMask | UMask | GCAttrMask | LifetimeMask);
822+
~(CVRMask | UMask | GCAttrMask | LifetimeMask | PtrAuthMask);
814823
static constexpr uint64_t AddressSpaceShift = 9;
815-
static constexpr uint64_t PtrAuthShift = 32;
816-
static constexpr uint64_t PtrAuthMask = uint64_t(0xffffffff) << PtrAuthShift;
817824
};
818825

819826
class QualifiersAndAtomic {
@@ -1449,6 +1456,12 @@ class QualType {
14491456
return getQualifiers().getPointerAuth();
14501457
}
14511458

1459+
bool hasAddressDiscriminatedPointerAuth() const {
1460+
if (PointerAuthQualifier PtrAuth = getPointerAuth())
1461+
return PtrAuth.isAddressDiscriminated();
1462+
return false;
1463+
}
1464+
14521465
enum PrimitiveDefaultInitializeKind {
14531466
/// The type does not fall into any of the following categories. Note that
14541467
/// this case is zero-valued so that values of this enum can be used as a

clang/include/clang/Basic/Attr.td

+8
Original file line numberDiff line numberDiff line change
@@ -3548,6 +3548,14 @@ def ObjCRequiresPropertyDefs : InheritableAttr {
35483548
let SimpleHandler = 1;
35493549
}
35503550

3551+
def PointerAuth : TypeAttr {
3552+
let Spellings = [CustomKeyword<"__ptrauth">];
3553+
let Args = [IntArgument<"Key">,
3554+
BoolArgument<"AddressDiscriminated", 1>,
3555+
IntArgument<"ExtraDiscriminator", 1>];
3556+
let Documentation = [PtrAuthDocs];
3557+
}
3558+
35513559
def Unused : InheritableAttr {
35523560
let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">,
35533561
C23<"", "maybe_unused", 202106>];

clang/include/clang/Basic/AttrDocs.td

+28
Original file line numberDiff line numberDiff line change
@@ -2179,6 +2179,34 @@ Also see the documentation for `@available
21792179
}];
21802180
}
21812181

2182+
def PtrAuthDocs : Documentation {
2183+
let Category = DocCatVariable;
2184+
let Heading = "__ptrauth";
2185+
let Content = [{
2186+
The ``__ptrauth`` qualifier allows the programmer to directly control
2187+
how pointers are signed when they are stored in a particular variable.
2188+
This can be used to strengthen the default protections of pointer
2189+
authentication and make it more difficult for an attacker to escalate
2190+
an ability to alter memory into full control of a process.
2191+
2192+
.. code-block:: c
2193+
2194+
#include <ptrauth.h>
2195+
2196+
typedef void (*my_callback)(const void*);
2197+
my_callback __ptrauth(ptrauth_key_process_dependent_code, 1, 0xe27a) callback;
2198+
2199+
The first argument to ``__ptrauth`` is the name of the signing key.
2200+
Valid key names for the target are defined in ``<ptrauth.h>``.
2201+
2202+
The second argument to ``__ptrauth`` is a flag (0 or 1) specifying whether
2203+
the object should use address discrimination.
2204+
2205+
The third argument to ``__ptrauth`` is a 16-bit non-negative integer which
2206+
allows additional discrimination between objects.
2207+
}];
2208+
}
2209+
21822210
def ExternalSourceSymbolDocs : Documentation {
21832211
let Category = DocCatDecl;
21842212
let Content = [{

clang/include/clang/Basic/DiagnosticParseKinds.td

+3
Original file line numberDiff line numberDiff line change
@@ -1721,6 +1721,9 @@ def warn_pragma_unroll_cuda_value_in_parens : Warning<
17211721
"argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">,
17221722
InGroup<CudaCompat>;
17231723

1724+
def err_ptrauth_qualifier_bad_arg_count : Error<
1725+
"'__ptrauth' qualifier must take between 1 and 3 arguments">;
1726+
17241727
def warn_cuda_attr_lambda_position : Warning<
17251728
"nvcc does not allow '__%0__' to appear after the parameter list in lambdas">,
17261729
InGroup<CudaCompat>;

clang/include/clang/Basic/DiagnosticSemaKinds.td

+41-2
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,22 @@ def err_ptrauth_indirect_goto_addrlabel_arithmetic : Error<
10141014
"%select{subtraction|addition}0 of address-of-label expressions is not "
10151015
"supported with ptrauth indirect gotos">;
10161016

1017+
// __ptrauth qualifier
1018+
def err_ptrauth_qualifier_invalid : Error<
1019+
"%select{return type|parameter type|property}1 may not be qualified with '__ptrauth'; type is %0">;
1020+
def err_ptrauth_qualifier_cast : Error<
1021+
"cannot cast to '__ptrauth'-qualified type %0">;
1022+
def err_ptrauth_qualifier_nonpointer : Error<
1023+
"'__ptrauth' qualifier only applies to pointer types; %0 is invalid">;
1024+
def err_ptrauth_qualifier_redundant : Error<
1025+
"type %0 is already %1-qualified">;
1026+
def err_ptrauth_arg_not_ice : Error<
1027+
"argument to '__ptrauth' must be an integer constant expression">;
1028+
def err_ptrauth_address_discrimination_invalid : Error<
1029+
"invalid address discrimination flag '%0'; '__ptrauth' requires '0' or '1'">;
1030+
def err_ptrauth_extra_discriminator_invalid : Error<
1031+
"invalid extra discriminator flag '%0'; '__ptrauth' requires a value between '0' and '%1'">;
1032+
10171033
/// main()
10181034
// static main() is not an error in C, just in C++.
10191035
def warn_static_main : Warning<"'main' should not be declared static">,
@@ -3923,7 +3939,8 @@ def note_cannot_use_trivial_abi_reason : Note<
39233939
"its copy constructors and move constructors are all deleted|"
39243940
"it is polymorphic|"
39253941
"it has a base of a non-trivial class type|it has a virtual base|"
3926-
"it has a __weak field|it has a field of a non-trivial class type}1">;
3942+
"it has a __weak field|it has a field of a non-trivial class type|"
3943+
"it has an address-discriminated '__ptrauth' field}1">;
39273944

39283945
// Availability attribute
39293946
def warn_availability_unknown_platform : Warning<
@@ -5021,6 +5038,10 @@ def note_ovl_candidate_bad_ownership : Note<
50215038
"%select{no|__unsafe_unretained|__strong|__weak|__autoreleasing}4 ownership,"
50225039
" but parameter has %select{no|__unsafe_unretained|__strong|__weak|"
50235040
"__autoreleasing}5 ownership">;
5041+
def note_ovl_candidate_bad_ptrauth : Note<
5042+
"candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
5043+
"%ordinal8 argument (%3) has %select{no '__ptrauth'|%5}4 qualifier,"
5044+
" but parameter has %select{no '__ptrauth'|%7}6 qualifier">;
50245045
def note_ovl_candidate_bad_cvr_this : Note<
50255046
"candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
50265047
"'this' argument has type %3, but method is not marked "
@@ -6092,7 +6113,7 @@ def note_deleted_special_member_class_subobject : Note<
60926113
"%select{default|corresponding|default|default|default}4 constructor}0|"
60936114
"destructor}5"
60946115
"%select{||s||}4"
6095-
"|is an ObjC pointer}6">;
6116+
"|is an ObjC pointer|has an address-discriminated '__ptrauth' qualifier}6">;
60966117
def note_default_constructed_field
60976118
: Note<"default constructed field %0 declared here">;
60986119
def note_deleted_default_ctor_uninit_field : Note<
@@ -8938,6 +8959,19 @@ def err_typecheck_incompatible_ownership : Error<
89388959
"sending to parameter of different type}0,1"
89398960
"|%diff{casting $ to type $|casting between types}0,1}2"
89408961
" changes retain/release properties of pointer">;
8962+
def err_typecheck_incompatible_ptrauth : Error<
8963+
"%enum_select<AssignmentAction>{%Assigning{%diff{assigning $ to $|assigning to different types}1,0}"
8964+
"|%Passing{%diff{passing $ to parameter of type $|"
8965+
"passing to parameter of different type}0,1}"
8966+
"|%Returning{%diff{returning $ from a function with result type $|"
8967+
"returning from function with different return type}0,1}"
8968+
"|%Converting{%diff{converting $ to type $|converting between types}0,1}"
8969+
"|%Initializing{%diff{initializing $ with an expression of type $|"
8970+
"initializing with expression of different type}0,1}"
8971+
"|%Sending{%diff{sending $ to parameter of type $|"
8972+
"sending to parameter of different type}0,1}"
8973+
"|%Casting{%diff{casting $ to type $|casting between types}0,1}}2"
8974+
" changes pointer authentication of pointee type">;
89418975
def err_typecheck_comparison_of_distinct_blocks : Error<
89428976
"comparison of distinct block types%diff{ ($ and $)|}0,1">;
89438977

@@ -9066,6 +9100,9 @@ def err_atomic_op_needs_non_const_atomic : Error<
90669100
def err_atomic_op_needs_non_const_pointer : Error<
90679101
"address argument to atomic operation must be a pointer to non-const "
90689102
"type (%0 invalid)">;
9103+
def err_atomic_op_needs_non_address_discriminated_pointer : Error<
9104+
"address argument to %select{atomic|__sync}0 operation must be a pointer to a non address discriminated "
9105+
"type (%1 invalid)">;
90699106
def err_atomic_op_needs_trivial_copy : Error<
90709107
"address argument to atomic operation must be a pointer to a "
90719108
"trivially-copyable type (%0 invalid)">;
@@ -9343,6 +9380,8 @@ def ext_typecheck_cond_pointer_integer_mismatch : ExtWarn<
93439380
"pointer/integer type mismatch in conditional expression"
93449381
"%diff{ ($ and $)|}0,1">,
93459382
InGroup<DiagGroup<"conditional-type-mismatch">>;
9383+
def err_typecheck_cond_incompatible_ptrauth : Error<
9384+
"'__ptrauth' qualification mismatch%diff{ ($ and $)|}0,1">;
93469385
def err_typecheck_choose_expr_requires_constant : Error<
93479386
"'__builtin_choose_expr' requires a constant expression">;
93489387
def warn_unused_expr : Warning<"expression result unused">,

clang/include/clang/Basic/Features.def

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread))
107107
FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow))
108108
FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo))
109109
FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics)
110+
EXTENSION(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics)
110111
FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls)
111112
FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns)
112113
FEATURE(ptrauth_vtable_pointer_address_discrimination, LangOpts.PointerAuthVTPtrAddressDiscrimination)

clang/include/clang/Basic/TokenKinds.def

+1
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ KEYWORD(_Thread_local , KEYALL)
348348
KEYWORD(__func__ , KEYALL)
349349
KEYWORD(__objc_yes , KEYALL)
350350
KEYWORD(__objc_no , KEYALL)
351+
KEYWORD(__ptrauth , KEYALL)
351352

352353
// C2y
353354
UNARY_EXPR_OR_TYPE_TRAIT(_Countof, CountOf, KEYNOCXX)

clang/include/clang/Parse/Parser.h

+2
Original file line numberDiff line numberDiff line change
@@ -3169,6 +3169,8 @@ class Parser : public CodeCompletionHandler {
31693169
SourceLocation *endLoc = nullptr);
31703170
ExprResult ParseExtIntegerArgument();
31713171

3172+
void ParsePtrauthQualifier(ParsedAttributes &Attrs);
3173+
31723174
VirtSpecifiers::Specifier isCXX11VirtSpecifier(const Token &Tok) const;
31733175
VirtSpecifiers::Specifier isCXX11VirtSpecifier() const {
31743176
return isCXX11VirtSpecifier(Tok);

clang/include/clang/Sema/Sema.h

+11
Original file line numberDiff line numberDiff line change
@@ -3547,6 +3547,17 @@ class Sema final : public SemaBase {
35473547

35483548
bool checkConstantPointerAuthKey(Expr *keyExpr, unsigned &key);
35493549

3550+
enum PointerAuthDiscArgKind {
3551+
// Address discrimination argument of __ptrauth.
3552+
PADAK_AddrDiscPtrAuth,
3553+
3554+
// Extra discriminator argument of __ptrauth.
3555+
PADAK_ExtraDiscPtrAuth,
3556+
};
3557+
3558+
bool checkPointerAuthDiscriminatorArg(Expr *Arg, PointerAuthDiscArgKind Kind,
3559+
unsigned &IntVal);
3560+
35503561
/// Diagnose function specifiers on a declaration of an identifier that
35513562
/// does not identify a function.
35523563
void DiagnoseFunctionSpecifiers(const DeclSpec &DS);

clang/lib/AST/ASTContext.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -11454,6 +11454,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer,
1145411454
if (LQuals.getCVRQualifiers() != RQuals.getCVRQualifiers() ||
1145511455
LQuals.getAddressSpace() != RQuals.getAddressSpace() ||
1145611456
LQuals.getObjCLifetime() != RQuals.getObjCLifetime() ||
11457+
!LQuals.getPointerAuth().isEquivalent(RQuals.getPointerAuth()) ||
1145711458
LQuals.hasUnaligned() != RQuals.hasUnaligned())
1145811459
return {};
1145911460

clang/lib/AST/DeclCXX.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,33 @@ void CXXRecordDecl::addedMember(Decl *D) {
11181118
} else if (!T.isCXX98PODType(Context))
11191119
data().PlainOldData = false;
11201120

1121+
// If a class has an address-discriminated signed pointer member, it is a
1122+
// non-POD type and its copy constructor, move constructor, copy assignment
1123+
// operator, move assignment operator are non-trivial.
1124+
if (PointerAuthQualifier Q = T.getPointerAuth()) {
1125+
if (Q.isAddressDiscriminated()) {
1126+
struct DefinitionData &Data = data();
1127+
Data.PlainOldData = false;
1128+
Data.HasTrivialSpecialMembers &=
1129+
~(SMF_CopyConstructor | SMF_MoveConstructor | SMF_CopyAssignment |
1130+
SMF_MoveAssignment);
1131+
setArgPassingRestrictions(RecordArgPassingKind::CanNeverPassInRegs);
1132+
1133+
// Copy/move constructors/assignment operators of a union are deleted by
1134+
// default if it has an address-discriminated ptrauth field.
1135+
if (isUnion()) {
1136+
data().DefaultedCopyConstructorIsDeleted = true;
1137+
data().DefaultedMoveConstructorIsDeleted = true;
1138+
data().DefaultedCopyAssignmentIsDeleted = true;
1139+
data().DefaultedMoveAssignmentIsDeleted = true;
1140+
data().NeedOverloadResolutionForCopyConstructor = true;
1141+
data().NeedOverloadResolutionForMoveConstructor = true;
1142+
data().NeedOverloadResolutionForCopyAssignment = true;
1143+
data().NeedOverloadResolutionForMoveAssignment = true;
1144+
}
1145+
}
1146+
}
1147+
11211148
if (Field->hasAttr<ExplicitInitAttr>())
11221149
setHasUninitializedExplicitInitFields(true);
11231150

clang/lib/AST/ItaniumMangle.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -2895,6 +2895,26 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals, const DependentAddressSp
28952895
if (Quals.hasUnaligned())
28962896
mangleVendorQualifier("__unaligned");
28972897

2898+
// __ptrauth. Note that this is parameterized.
2899+
if (PointerAuthQualifier PtrAuth = Quals.getPointerAuth()) {
2900+
mangleVendorQualifier("__ptrauth");
2901+
// For now, since we only allow non-dependent arguments, we can just
2902+
// inline the mangling of those arguments as literals. We treat the
2903+
// key and extra-discriminator arguments as 'unsigned int' and the
2904+
// address-discriminated argument as 'bool'.
2905+
Out << "I"
2906+
"Lj"
2907+
<< PtrAuth.getKey()
2908+
<< "E"
2909+
"Lb"
2910+
<< unsigned(PtrAuth.isAddressDiscriminated())
2911+
<< "E"
2912+
"Lj"
2913+
<< PtrAuth.getExtraDiscriminator()
2914+
<< "E"
2915+
"E";
2916+
}
2917+
28982918
// Remaining ARC ownership qualifiers.
28992919
switch (Quals.getObjCLifetime()) {
29002920
case Qualifiers::OCL_None:

0 commit comments

Comments
 (0)