Skip to content

Commit a3bdd61

Browse files
ahmedbougachadtapuska
authored andcommitted
[clang] Implement the ptrauth_struct type attribute.
FIXME: the ptrauth-struct-attr.cpp/.m tests regressed after 7eca38c, which uses CreateGEP (that eagerly emits a GEP instr to put a nuw flag on it, which in turn causes the null-checked resign to be emitted as the raw address), whereas it previously used CreateInBoundsGEP (which lazily emits the GEP when it's needed, by accumulating the offset into the Address, thus allowing the later dereference use to mark the Address as known-non-null). I have hacks to fix it, and we can obviously just update the test to include the null-checks in resigns, but we should come up with a long term solution.
1 parent 1b9ff09 commit a3bdd61

31 files changed

+2112
-16
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
13471347
/// Return the "other" type-specific discriminator for the given type.
13481348
uint16_t getPointerAuthTypeDiscriminator(QualType T);
13491349

1350+
/// Return the key of attribute ptrauth_struct on the record. If the attribute
1351+
/// isn't on the record, return the none key. If the key argument is value
1352+
/// dependent, set the boolean flag to false.
1353+
std::pair<bool, unsigned> getPointerAuthStructKey(const RecordDecl *RD) const;
1354+
1355+
/// Return the discriminator of attribute ptrauth_struct on the record. If the
1356+
/// key argument is value dependent, set the boolean flag to false.
1357+
std::pair<bool, unsigned>
1358+
getPointerAuthStructDisc(const RecordDecl *RD) const;
1359+
13501360
// Determine whether the type can have qualifier __ptrauth with key
13511361
// ptrauth_key_none.
13521362
bool canQualifyWithPtrAuthKeyNone(QualType T) const {
@@ -1359,9 +1369,20 @@ class ASTContext : public RefCountedBase<ASTContext> {
13591369
if (PointeeType->isFunctionType())
13601370
return false;
13611371

1372+
// Disallow the qualifer on classes annotated with attribute ptrauth_struct
1373+
// with a key that isn't the none key.
1374+
if (auto *RD = PointeeType->getAsRecordDecl()) {
1375+
std::pair<bool, unsigned> P = getPointerAuthStructKey(RD);
1376+
if (P.first && P.second != PointerAuthKeyNone)
1377+
return false;
1378+
}
1379+
13621380
return true;
13631381
}
13641382

1383+
bool hasPointerAuthStructMismatch(const RecordDecl *RD0,
1384+
const RecordDecl *RD1) const;
1385+
13651386
bool typeContainsAuthenticatedNull(QualType) const;
13661387
bool typeContainsAuthenticatedNull(const Type *) const;
13671388
std::optional<bool> tryTypeContainsAuthenticatedNull(QualType) const;

clang/include/clang/AST/CXXRecordDeclDefinitionBits.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,4 +253,8 @@ FIELD(IsAnyDestructorNoReturn, 1, NO_MERGE)
253253
/// type that is intangible). HLSL only.
254254
FIELD(IsHLSLIntangible, 1, NO_MERGE)
255255

256+
/// Whether this class or any of its direct or indirect base classes are
257+
/// annotated with `ptrauth_struct` that uses a key that isn't the none key.
258+
FIELD(HasSignedClassInHierarchy, 1, NO_MERGE)
259+
256260
#undef FIELD

clang/include/clang/AST/DeclCXX.h

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

1029+
bool hasSignedClassInHierarchy() const {
1030+
return data().HasSignedClassInHierarchy;
1031+
}
1032+
1033+
void setHasSignedClassInHierarchy() {
1034+
data().HasSignedClassInHierarchy = true;
1035+
}
1036+
10291037
/// Determine whether this class describes a lambda function object.
10301038
bool isLambda() const {
10311039
// 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
@@ -3550,6 +3550,13 @@ def PointerAuth : TypeAttr {
35503550
let Documentation = [PtrAuthDocs];
35513551
}
35523552

3553+
def PointerAuthStruct : InheritableAttr {
3554+
let Spellings = [Clang<"ptrauth_struct">];
3555+
let Args = [ExprArgument<"Key">, ExprArgument<"Discriminator">];
3556+
let Subjects = SubjectList<[Record]>;
3557+
let Documentation = [PointerAuthStructDocs];
3558+
}
3559+
35533560
def Unused : InheritableAttr {
35543561
let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">,
35553562
C23<"", "maybe_unused", 202106>];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2339,6 +2339,47 @@ features that implicitly use pointer authentication.
23392339
}];
23402340
}
23412341

2342+
def PointerAuthStructDocs : Documentation {
2343+
let Category = DocCatDecl;
2344+
let Content = [{
2345+
The ``ptrauth_struct`` attribute allows programmers to opt in to using signed
2346+
pointers for all pointers to a particular struct, union, or C++ class type. In
2347+
C++, this also includes references to the type.
2348+
2349+
The attribute takes two arguments:
2350+
2351+
- The first argument is the abstract signing key, which should be a constant
2352+
from ``<ptrauth.h>``.
2353+
- The second argument is a constant discriminator.
2354+
2355+
These arguments are identical to the arguments to the ``__ptrauth`` qualifier
2356+
except that ``ptrauth_struct`` does not take an argument for enabling address
2357+
diversity.
2358+
2359+
.. code-block:: c++
2360+
2361+
// key=ptrauth_key_process_dependent_data,discriminator=100
2362+
struct __attribute__((ptrauth_struct(ptrauth_key_process_dependent_data,100))) S0 {
2363+
int f0[4];
2364+
};
2365+
2366+
If a particular pointer to the type is qualified with a ``__ptrauth`` qualifier,
2367+
that qualifier takes precedence over ptrauth_struct:
2368+
2369+
.. code-block:: c++
2370+
2371+
struct __attribute__((ptrauth_struct(ptrauth_key_process_dependent_data,100))) S0 {
2372+
int f0[4];
2373+
};
2374+
2375+
void test(S0 *s0) {
2376+
// `p` is signed using `__ptrauth(ptrauth_key_process_dependent_data, 1, 200)`.
2377+
S0 * __ptrauth(ptrauth_key_process_dependent_data, 1, 200) p = s0;
2378+
}
2379+
2380+
}];
2381+
}
2382+
23422383
def ExternalSourceSymbolDocs : Documentation {
23432384
let Category = DocCatDecl;
23442385
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,11 +1023,12 @@ def err_ptrauth_qualifier_redundant : Error<
10231023
def err_ptrauth_qualifier_bad_arg_count : Error<
10241024
"%0 qualifier must take between 1 and 4 arguments">;
10251025
def err_ptrauth_arg_not_ice : Error<
1026-
"argument to __ptrauth must be an integer constant expression">;
1026+
"argument to %select{__ptrauth|ptrauth_struct}0 must be an integer constant "
1027+
"expression">;
10271028
def err_ptrauth_address_discrimination_invalid : Error<
10281029
"address discrimination flag for __ptrauth must be 0 or 1; value is %0">;
10291030
def err_ptrauth_extra_discriminator_invalid : Error<
1030-
"extra discriminator for __ptrauth must be between "
1031+
"extra discriminator for %select{__ptrauth|ptrauth_struct}2 must be between "
10311032
"0 and %1; value is %0">;
10321033

10331034
def err_ptrauth_restricted_intptr_qualifier_pointer : Error<
@@ -1056,6 +1057,16 @@ def err_ptrauth_invalid_authenticated_null_global : Error<
10561057
"%select{static locals|globals}0 with authenticated null values are currently unsupported"
10571058
>;
10581059

1060+
// ptrauth_struct attribute.
1061+
def err_ptrauth_struct_key_disc_not_record : Error<
1062+
"attribute ptrauth_struct can only be used on a record type">;
1063+
def err_ptrauth_struct_signing_mismatch : Error<
1064+
"%0 is signed differently from the previous declaration">;
1065+
def note_previous_ptrauth_struct_declaration : Note<
1066+
"previous declaration of %0 is here">;
1067+
def err_ptrauth_invalid_struct_attr_dynamic_class : Error<
1068+
"attribute ptrauth_struct cannot be used on class %0 or its subclasses "
1069+
"because it is a dynamic class">;
10591070

10601071
/// main()
10611072
// static main() is not an error in C, just in C++.

clang/include/clang/Basic/TokenKinds.def

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

604604
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_type_discriminator, PtrAuthTypeDiscriminator, KEYALL)
605+
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_struct_key, PtrAuthStructKey, KEYALL)
606+
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_struct_disc, PtrAuthStructDiscriminator, KEYALL)
605607

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

clang/include/clang/Parse/Parser.h

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

39603960
ExprResult ParseBuiltinPtrauthTypeDiscriminator();
3961+
ExprResult ParseBuiltinPtrauthStructKeyOrDisc(bool ParseKey);
39613962

39623963
//===--------------------------------------------------------------------===//
39633964
// 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
@@ -3544,6 +3544,9 @@ class Sema final : public SemaBase {
35443544

35453545
// Extra discriminator argument of __ptrauth.
35463546
PADAK_ExtraDiscPtrAuth,
3547+
3548+
// Type discriminator argument of ptrauth_struct.
3549+
PADAK_TypeDiscPtrAuthStruct,
35473550
};
35483551

35493552
bool checkPointerAuthDiscriminatorArg(Expr *Arg, PointerAuthDiscArgKind Kind,
@@ -4588,6 +4591,9 @@ class Sema final : public SemaBase {
45884591
StringRef &Str,
45894592
SourceLocation *ArgLocation = nullptr);
45904593

4594+
void addPointerAuthStructAttr(Decl *D, const AttributeCommonInfo &CI,
4595+
Expr *Key, Expr *Disc);
4596+
45914597
/// Determine if type T is a valid subject for a nonnull and similar
45924598
/// attributes. Dependent types are considered valid so they can be checked
45934599
/// during instantiation time. By default, we look through references (the

clang/lib/AST/ASTContext.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3523,6 +3523,62 @@ uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) {
35233523
return llvm::getPointerAuthStableSipHash(Str);
35243524
}
35253525

3526+
std::pair<bool, unsigned>
3527+
ASTContext::getPointerAuthStructKey(const RecordDecl *RD) const {
3528+
if (auto *Attr = RD->getAttr<PointerAuthStructAttr>()) {
3529+
Expr *E = Attr->getKey();
3530+
if (E->isValueDependent())
3531+
return {false, 0};
3532+
std::optional<llvm::APSInt> V = E->getIntegerConstantExpr(*this);
3533+
return {true, static_cast<unsigned>(V->getExtValue())};
3534+
}
3535+
3536+
return {true, PointerAuthKeyNone};
3537+
}
3538+
3539+
std::pair<bool, unsigned>
3540+
ASTContext::getPointerAuthStructDisc(const RecordDecl *RD) const {
3541+
if (auto *Attr = RD->getAttr<PointerAuthStructAttr>()) {
3542+
Expr *E = Attr->getDiscriminator();
3543+
if (E->isValueDependent())
3544+
return {false, 0};
3545+
std::optional<llvm::APSInt> V = E->getIntegerConstantExpr(*this);
3546+
return {true, V->getExtValue()};
3547+
}
3548+
3549+
return {true, 0};
3550+
}
3551+
3552+
bool ASTContext::hasPointerAuthStructMismatch(const RecordDecl *RD0,
3553+
const RecordDecl *RD1) const {
3554+
if (RD0 == RD1)
3555+
return true;
3556+
3557+
unsigned Key0, Key1;
3558+
unsigned Disc0, Disc1;
3559+
bool Known0, Known1;
3560+
std::tie(Known0, Key0) = getPointerAuthStructKey(RD0);
3561+
std::tie(Known1, Key1) = getPointerAuthStructKey(RD1);
3562+
3563+
if (!Known0 || !Known1)
3564+
return false;
3565+
3566+
if (Key0 != Key1)
3567+
return true;
3568+
3569+
// If the key is none, skip checking the discriminators.
3570+
if (Key0 == PointerAuthKeyNone)
3571+
return false;
3572+
3573+
std::tie(Known0, Disc0) = getPointerAuthStructDisc(RD0);
3574+
std::tie(Known1, Disc1) = getPointerAuthStructDisc(RD1);
3575+
3576+
if (!Known0 || !Known1)
3577+
return false;
3578+
3579+
return Disc0 != Disc1;
3580+
}
3581+
35263582
std::optional<bool>
35273583
ASTContext::tryTypeContainsAuthenticatedNull(QualType T) const {
35283584
if (!LangOpts.PointerAuthIntrinsics)

0 commit comments

Comments
 (0)