Skip to content

Commit e16b7b1

Browse files
committed
[clang] Implement the ptrauth_struct type attribute.
1 parent aee9b48 commit e16b7b1

28 files changed

+2070
-14
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,15 @@ class ASTContext : public RefCountedBase<ASTContext> {
12391239
uint16_t
12401240
getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *record);
12411241

1242+
bool typeContainsAuthenticatedNull(QualType) const;
1243+
bool typeContainsAuthenticatedNull(const Type *) const;
1244+
std::optional<bool> tryTypeContainsAuthenticatedNull(QualType) const;
1245+
1246+
/// Return the key of attribute ptrauth_struct on the record. If the attribute
1247+
/// isn't on the record, return the none key. If the key argument is value
1248+
/// dependent, set the boolean flag to false.
1249+
std::pair<bool, int> getPointerAuthStructKey(const RecordDecl *RD) const;
1250+
12421251
/// Return the discriminator of attribute ptrauth_struct on the record. If the
12431252
/// key argument is value dependent, set the boolean flag to false.
12441253
std::pair<bool, unsigned>
@@ -1253,12 +1262,19 @@ class ASTContext : public RefCountedBase<ASTContext> {
12531262
if (PointeeType->isFunctionType())
12541263
return false;
12551264

1265+
// Disallow the qualifer on classes annotated with attribute ptrauth_struct
1266+
// with a key that isn't the none key.
1267+
if (auto *RD = PointeeType->getAsRecordDecl()) {
1268+
std::pair<bool, int> P = getPointerAuthStructKey(RD);
1269+
if (P.first && P.second != PointerAuthKeyNone)
1270+
return false;
1271+
}
1272+
12561273
return true;
12571274
}
12581275

1259-
bool typeContainsAuthenticatedNull(QualType) const;
1260-
bool typeContainsAuthenticatedNull(const Type *) const;
1261-
std::optional<bool> tryTypeContainsAuthenticatedNull(QualType) const;
1276+
bool hasPointerAuthStructMismatch(const RecordDecl *RD0,
1277+
const RecordDecl *RD1) const;
12621278

12631279
/// Apply Objective-C protocol qualifiers to the given type.
12641280
/// \param allowOnPointerType specifies if we can apply protocol

clang/include/clang/AST/CXXRecordDeclDefinitionBits.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,4 +249,8 @@ FIELD(HasDeclaredCopyAssignmentWithConstParam, 1, MERGE_OR)
249249
/// base classes or fields have a no-return destructor
250250
FIELD(IsAnyDestructorNoReturn, 1, NO_MERGE)
251251

252+
/// Whether this class or any of its direct or indirect base classes are
253+
/// annotated with `ptrauth_struct` that uses a key that isn't the none key.
254+
FIELD(HasSignedClassInHierarchy, 1, NO_MERGE)
255+
252256
#undef FIELD

clang/include/clang/AST/DeclCXX.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,14 @@ class CXXRecordDecl : public RecordDecl {
10051005
return data().NeedOverloadResolutionForDestructor;
10061006
}
10071007

1008+
bool hasSignedClassInHierarchy() const {
1009+
return data().HasSignedClassInHierarchy;
1010+
}
1011+
1012+
void setHasSignedClassInHierarchy() {
1013+
data().HasSignedClassInHierarchy = true;
1014+
}
1015+
10081016
/// Determine whether this class describes a lambda function object.
10091017
bool isLambda() const {
10101018
// An update record can't turn a non-lambda into a lambda.

clang/include/clang/Basic/Attr.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2994,6 +2994,13 @@ def PointerAuth : TypeAttr {
29942994
let Documentation = [PtrAuthDocs];
29952995
}
29962996

2997+
def PointerAuthStruct : InheritableAttr {
2998+
let Spellings = [Clang<"ptrauth_struct">];
2999+
let Args = [ExprArgument<"Key">, ExprArgument<"Discriminator">];
3000+
let Subjects = SubjectList<[Record]>;
3001+
let Documentation = [PointerAuthStructDocs];
3002+
}
3003+
29973004
def Unused : InheritableAttr {
29983005
let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">,
29993006
C23<"", "maybe_unused", 202106>];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1887,6 +1887,47 @@ features that implicitly use pointer authentication.
18871887
}];
18881888
}
18891889

1890+
def PointerAuthStructDocs : Documentation {
1891+
let Category = DocCatDecl;
1892+
let Content = [{
1893+
The ``ptrauth_struct`` attribute allows programmers to opt in to using signed
1894+
pointers for all pointers to a particular struct, union, or C++ class type. In
1895+
C++, this also includes references to the type.
1896+
1897+
The attribute takes two arguments:
1898+
1899+
- The first argument is the abstract signing key, which should be a constant
1900+
from ``<ptrauth.h>``.
1901+
- The second argument is a constant discriminator.
1902+
1903+
These arguments are identical to the arguments to the ``__ptrauth`` qualifier
1904+
except that ``ptrauth_struct`` does not take an argument for enabling address
1905+
diversity.
1906+
1907+
.. code-block:: c++
1908+
1909+
// key=ptrauth_key_process_dependent_data,discriminator=100
1910+
struct __attribute__((ptrauth_struct(ptrauth_key_process_dependent_data,100))) S0 {
1911+
int f0[4];
1912+
};
1913+
1914+
If a particular pointer to the type is qualified with a ``__ptrauth`` qualifier,
1915+
that qualifier takes precedence over ptrauth_struct:
1916+
1917+
.. code-block:: c++
1918+
1919+
struct __attribute__((ptrauth_struct(ptrauth_key_process_dependent_data,100))) S0 {
1920+
int f0[4];
1921+
};
1922+
1923+
void test(S0 *s0) {
1924+
// `p` is signed using `__ptrauth(ptrauth_key_process_dependent_data, 1, 200)`.
1925+
S0 * __ptrauth(ptrauth_key_process_dependent_data, 1, 200) p = s0;
1926+
}
1927+
1928+
}];
1929+
}
1930+
18901931
def ExternalSourceSymbolDocs : Documentation {
18911932
let Category = DocCatDecl;
18921933
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,15 @@ def note_ptrauth_virtual_function_incomplete_arg_ret_type :
904904
def err_ptrauth_type_disc_undiscriminated : Error<
905905
"cannot pass undiscriminated type %0 to "
906906
"'__builtin_ptrauth_type_discriminator'">;
907+
def err_ptrauth_struct_key_disc_not_record : Error<
908+
"argument to '__builtin_ptrauth_type_discriminator' isn't a record type">;
909+
def err_ptrauth_struct_signing_mismatch : Error<
910+
"%0 is signed differently from the previous declaration">;
911+
def note_previous_ptrauth_struct_declaration : Note<
912+
"previous declaration of %0 is here">;
913+
def err_ptrauth_invalid_struct_attr_dynamic_class : Error<
914+
"attribute ptrauth_struct cannot be used on class %0 or its subclasses "
915+
"because it is a dynamic class">;
907916
def err_ptrauth_non_string_authentication_option : Error<
908917
"__ptrauth options must be a string of comma separated flags, found %0">;
909918
def err_ptrauth_unknown_authentication_option : Error<
@@ -939,11 +948,12 @@ def err_ptrauth_qualifier_signed_pointer_type : Error<
939948
def err_ptrauth_qualifier_bad_arg_count : Error<
940949
"%0 qualifier must take between 1 and 4 arguments">;
941950
def err_ptrauth_arg_not_ice : Error<
942-
"argument to __ptrauth must be an integer constant expression">;
951+
"argument to %select{__ptrauth|ptrauth_struct}0 must be an integer constant "
952+
"expression">;
943953
def err_ptrauth_address_discrimination_invalid : Error<
944954
"address discrimination flag for __ptrauth must be 0 or 1; value is %0">;
945955
def err_ptrauth_extra_discriminator_invalid : Error<
946-
"extra discriminator for __ptrauth must be between "
956+
"extra discriminator for %select{__ptrauth|ptrauth_struct}2 must be between "
947957
"0 and %1; value is %0">;
948958

949959
/// main()

clang/include/clang/Basic/TokenKinds.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,8 @@ KEYWORD(__private_extern__ , KEYALL)
576576
KEYWORD(__module_private__ , KEYALL)
577577

578578
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_type_discriminator, PtrAuthTypeDiscriminator, KEYALL)
579+
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_struct_key, PtrAuthStructKey, KEYALL)
580+
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_struct_disc, PtrAuthStructDiscriminator, KEYALL)
579581

580582
// Extension that will be enabled for Microsoft, Borland and PS4, but can be
581583
// disabled via '-fno-declspec'.

clang/include/clang/Parse/Parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3628,6 +3628,7 @@ class Parser : public CodeCompletionHandler {
36283628
ExprResult ParseExpressionTrait();
36293629

36303630
ExprResult ParseBuiltinPtrauthTypeDiscriminator();
3631+
ExprResult ParseBuiltinPtrauthStructKeyOrDisc(bool ParseKey);
36313632

36323633
//===--------------------------------------------------------------------===//
36333634
// Preprocessor code-completion pass-through

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2936,6 +2936,9 @@ class Sema final {
29362936

29372937
// Extra discriminator argument of __ptrauth.
29382938
PADAK_ExtraDiscPtrAuth,
2939+
2940+
// Type discriminator argument of ptrauth_struct.
2941+
PADAK_TypeDiscPtrAuthStruct,
29392942
};
29402943

29412944
bool checkPointerAuthDiscriminatorArg(Expr *arg, PointerAuthDiscArgKind kind,
@@ -4766,6 +4769,9 @@ class Sema final {
47664769
bool checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A,
47674770
bool SkipArgCountCheck = false);
47684771

4772+
void addPointerAuthStructAttr(Decl *D, const AttributeCommonInfo &CI,
4773+
Expr *Key, Expr *Disc);
4774+
47694775
/// Determine if type T is a valid subject for a nonnull and similar
47704776
/// attributes. By default, we look through references (the behavior used by
47714777
/// nonnull), but if the second parameter is true, then we treat a reference

clang/lib/AST/ASTContext.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3404,6 +3404,62 @@ uint16_t ASTContext::getPointerAuthVTablePointerDiscriminator(
34043404
return getPointerAuthStringDiscriminator(*this, Str.c_str());
34053405
}
34063406

3407+
std::pair<bool, int>
3408+
ASTContext::getPointerAuthStructKey(const RecordDecl *RD) const {
3409+
if (auto *Attr = RD->getAttr<PointerAuthStructAttr>()) {
3410+
Expr *E = Attr->getKey();
3411+
if (E->isValueDependent())
3412+
return {false, 0};
3413+
std::optional<llvm::APSInt> V = E->getIntegerConstantExpr(*this);
3414+
return {true, V->getExtValue()};
3415+
}
3416+
3417+
return {true, PointerAuthKeyNone};
3418+
}
3419+
3420+
std::pair<bool, unsigned>
3421+
ASTContext::getPointerAuthStructDisc(const RecordDecl *RD) const {
3422+
if (auto *Attr = RD->getAttr<PointerAuthStructAttr>()) {
3423+
Expr *E = Attr->getDiscriminator();
3424+
if (E->isValueDependent())
3425+
return {false, 0};
3426+
std::optional<llvm::APSInt> V = E->getIntegerConstantExpr(*this);
3427+
return {true, V->getExtValue()};
3428+
}
3429+
3430+
return {true, 0};
3431+
}
3432+
3433+
bool ASTContext::hasPointerAuthStructMismatch(const RecordDecl *RD0,
3434+
const RecordDecl *RD1) const {
3435+
if (RD0 == RD1)
3436+
return true;
3437+
3438+
int Key0, Key1;
3439+
unsigned Disc0, Disc1;
3440+
bool Known0, Known1;
3441+
std::tie(Known0, Key0) = getPointerAuthStructKey(RD0);
3442+
std::tie(Known1, Key1) = getPointerAuthStructKey(RD1);
3443+
3444+
if (!Known0 || !Known1)
3445+
return false;
3446+
3447+
if (Key0 != Key1)
3448+
return true;
3449+
3450+
// If the key is none, skip checking the discriminators.
3451+
if (Key0 == PointerAuthKeyNone)
3452+
return false;
3453+
3454+
std::tie(Known0, Disc0) = getPointerAuthStructDisc(RD0);
3455+
std::tie(Known1, Disc1) = getPointerAuthStructDisc(RD1);
3456+
3457+
if (!Known0 || !Known1)
3458+
return false;
3459+
3460+
return Disc0 != Disc1;
3461+
}
3462+
34073463
bool ASTContext::typeContainsAuthenticatedNull(const Type *type) const {
34083464
return typeContainsAuthenticatedNull(QualType(type, 0));
34093465
}

0 commit comments

Comments
 (0)