Skip to content

Commit e84310f

Browse files
committed
[clang][ptrauth] Add support for querying the ptrauth schema of a type
This adds a number of builtins to query the ptrauth schema of an arbitrary type in a way that can be fed into other ptrauth qualifiers.
1 parent aafbdde commit e84310f

17 files changed

+518
-5
lines changed

clang/docs/PointerAuthentication.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,24 @@ type. Implementations are not required to make all bits of the result equally
499499
significant; in particular, some implementations are known to not leave
500500
meaningful data in the low bits.
501501

502+
``__ptrauth type queries``
503+
^^^^^^^^^^^^^^^^^^^^^^^^^^
504+
505+
There are a number of builtins that can be used to query the ptrauth qualifier
506+
parameters of a type, including those configured implicitly. These are:
507+
508+
.. code-block:: c
509+
__builtin_ptrauth_has_authentication(type)
510+
__builtin_ptrauth_schema_key(type)
511+
__builtin_ptrauth_schema_is_address_discriminated(type)
512+
__builtin_ptrauth_schema_extra_discriminator(type)
513+
__builtin_ptrauth_schema_options(type)
514+
515+
All these builtins are compile time constants. The schema queries are only valid
516+
on types that have some form of pointer authentication, including implicit
517+
authentication as is present of function pointers. Each schema query returns a
518+
value of the appropriate type for the relevant parameter to the __ptrauth
519+
qualifier.
502520

503521

504522
Alternative Implementations

clang/include/clang/AST/ASTContext.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
13441344
/// Return the "other" type-specific discriminator for the given type.
13451345
uint16_t getPointerAuthTypeDiscriminator(QualType T);
13461346

1347+
/// Produces the canonical "options" string for the given PointerAuthQualifier
1348+
/// such that using it explicitly in a __ptrauth qualifier would result in as
1349+
/// identical configuration
1350+
std::string getPointerAuthOptionsString(const PointerAuthQualifier &PAQ);
1351+
13471352
/// Apply Objective-C protocol qualifiers to the given type.
13481353
/// \param allowOnPointerType specifies if we can apply protocol
13491354
/// qualifiers on ObjCObjectPointerType. It can be set to true when
@@ -1682,6 +1687,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
16821687

16831688
QualType adjustStringLiteralBaseType(QualType StrLTy) const;
16841689

1690+
/// Synthesizes a PointerAuthQualifier representing the actual authentication
1691+
/// policy for the given type. This may be either the schema specified
1692+
/// explicitly via the __ptrauth qualified in the source, or the implicit
1693+
/// schema associated with function pointers and similar.
1694+
std::optional<PointerAuthQualifier>
1695+
getExplicitOrImplicitPointerAuth(QualType T);
1696+
16851697
private:
16861698
/// Return a normal function type with a typed argument list.
16871699
QualType getFunctionTypeInternal(QualType ResultTy, ArrayRef<QualType> Args,

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,8 @@ def err_ptrauth_address_discrimination_invalid : Error<
10391039
"invalid address discrimination flag '%0'; '__ptrauth' requires '0' or '1'">;
10401040
def err_ptrauth_extra_discriminator_invalid : Error<
10411041
"invalid extra discriminator flag '%0'; '__ptrauth' requires a value between '0' and '%1'">;
1042+
def err_ptrauth_query_type_has_no_pointer_authentication
1043+
: Error<"argument to %0 parameter is not an authenticated value">;
10421044

10431045
/// main()
10441046
// static main() is not an error in C, just in C++.

clang/include/clang/Basic/LangOptions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ LANGOPT(PointerAuthInitFiniAddressDiscrimination, 1, 0,
177177
"incorporate address discrimination in authenticated function pointers in init/fini arrays")
178178
LANGOPT(PointerAuthELFGOT, 1, 0, "authenticate pointers from GOT")
179179
LANGOPT(AArch64JumpTableHardening, 1, 0, "use hardened lowering for jump-table dispatch")
180+
LANGOPT(PointerAuthFunctionKey, 16, 0, "authentication key for function pointers")
180181

181182
LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes")
182183
LANGOPT(ExperimentalLateParseAttributes, 1, 0, "experimental late parsing of attributes")

clang/include/clang/Basic/LangOptions.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,17 @@ enum class PointerAuthenticationMode : unsigned {
6565
SignAndAuth
6666
};
6767

68+
static constexpr llvm::StringLiteral PointerAuthenticationOptionStrip = "strip";
69+
static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndStrip =
70+
"sign-and-strip";
71+
static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndAuth =
72+
"sign-and-auth";
73+
static constexpr llvm::StringLiteral PointerAuthenticationOptionIsaPointer =
74+
"isa-pointer";
75+
static constexpr llvm::StringLiteral
76+
PointerAuthenticationOptionAuthenticatesNullValues =
77+
"authenticates-null-values";
78+
6879
/// Bitfields of LangOptions, split out from LangOptions in order to ensure that
6980
/// this large collection of bitfields is a trivial class type.
7081
class LangOptionsBase {

clang/include/clang/Basic/TokenKinds.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,11 @@ KEYWORD(__private_extern__ , KEYALL)
601601
KEYWORD(__module_private__ , KEYALL)
602602

603603
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_type_discriminator, PtrAuthTypeDiscriminator, KEYALL)
604+
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_has_authentication, PtrAuthHasAuthentication, KEYALL)
605+
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_schema_key, PtrAuthSchemaKey, KEYALL)
606+
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_schema_is_address_discriminated, PtrAuthSchemaIsAddressDiscriminated, KEYALL)
607+
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_schema_extra_discriminator, PtrAuthSchemaExtraDiscriminator, KEYALL)
608+
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_schema_options, PtrAuthSchemaOptions, KEYALL)
604609

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

clang/include/clang/Parse/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3969,7 +3969,9 @@ class Parser : public CodeCompletionHandler {
39693969
ExprResult ParseArrayTypeTrait();
39703970
ExprResult ParseExpressionTrait();
39713971

3972+
ExprResult ParseBuiltinUnaryExprOrTypeTrait(UnaryExprOrTypeTrait ExprKind);
39723973
ExprResult ParseBuiltinPtrauthTypeDiscriminator();
3974+
ExprResult ParseBuiltinPtrauthQuery(tok::TokenKind Token);
39733975

39743976
//===--------------------------------------------------------------------===//
39753977
// Preprocessor code-completion pass-through

clang/lib/AST/ASTContext.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
#include <map>
9797
#include <memory>
9898
#include <optional>
99+
#include <sstream>
99100
#include <string>
100101
#include <tuple>
101102
#include <utility>
@@ -9601,6 +9602,65 @@ ObjCInterfaceDecl *ASTContext::getObjCProtocolDecl() const {
96019602
return ObjCProtocolClassDecl;
96029603
}
96039604

9605+
std::optional<PointerAuthQualifier>
9606+
ASTContext::getExplicitOrImplicitPointerAuth(QualType T) {
9607+
auto ExplicitQualifier = T.getPointerAuth();
9608+
if (ExplicitQualifier.isPresent())
9609+
return ExplicitQualifier;
9610+
if (T->isDependentType()) {
9611+
return std::nullopt;
9612+
}
9613+
// FIXME: The more we expand pointer auth support, the more it becomes clear
9614+
// the codegen option's PointerAuth info belongs in LangOpts
9615+
if (!LangOpts.PointerAuthCalls)
9616+
return PointerAuthQualifier();
9617+
if (T->isFunctionPointerType() || T->isFunctionReferenceType())
9618+
T = T->getPointeeType();
9619+
if (!T->isFunctionType())
9620+
return PointerAuthQualifier();
9621+
int ExtraDiscriminator = 0;
9622+
if (LangOpts.PointerAuthFunctionTypeDiscrimination) {
9623+
ExtraDiscriminator = getPointerAuthTypeDiscriminator(T);
9624+
}
9625+
return PointerAuthQualifier::Create(
9626+
LangOpts.PointerAuthFunctionKey, false, ExtraDiscriminator,
9627+
PointerAuthenticationMode::SignAndAuth,
9628+
/*isIsaPointer=*/false, /*authenticatesNullValues=*/false);
9629+
}
9630+
9631+
std::string
9632+
ASTContext::getPointerAuthOptionsString(const PointerAuthQualifier &PAQ) {
9633+
llvm::SmallVector<llvm::StringLiteral, 4> Options;
9634+
switch (PAQ.getAuthenticationMode()) {
9635+
case PointerAuthenticationMode::Strip:
9636+
Options.push_back(PointerAuthenticationOptionStrip);
9637+
break;
9638+
case PointerAuthenticationMode::SignAndStrip:
9639+
Options.push_back(PointerAuthenticationOptionSignAndStrip);
9640+
break;
9641+
case PointerAuthenticationMode::SignAndAuth:
9642+
// Just default to not listing this explicitly
9643+
break;
9644+
default:
9645+
llvm_unreachable("Invalid authentication mode");
9646+
}
9647+
if (PAQ.isIsaPointer())
9648+
Options.push_back(PointerAuthenticationOptionIsaPointer);
9649+
if (PAQ.authenticatesNullValues())
9650+
Options.push_back(PointerAuthenticationOptionAuthenticatesNullValues);
9651+
if (Options.empty())
9652+
return std::string();
9653+
if (Options.size() == 1)
9654+
return Options[0].str();
9655+
std::ostringstream Buffer;
9656+
Buffer << Options[0].str();
9657+
for (unsigned i = 1; i < Options.size(); i++) {
9658+
Buffer << ',';
9659+
Buffer << Options[i].str();
9660+
}
9661+
return Buffer.str();
9662+
}
9663+
96049664
//===----------------------------------------------------------------------===//
96059665
// __builtin_va_list Construction Functions
96069666
//===----------------------------------------------------------------------===//

clang/lib/AST/ExprConstant.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9561,6 +9561,41 @@ class PointerExprEvaluator
95619561
return true;
95629562
}
95639563

9564+
bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) {
9565+
// This is the only UETT we evaluate here.
9566+
assert(E->getKind() == UETT_PtrAuthSchemaOptions &&
9567+
"Unknown UnaryExprOrTypeTraitExpr");
9568+
9569+
// Note for review: there are other UETTs down the road
9570+
// that make a switch make sense, but for now this is the only
9571+
// one should this just be an
9572+
// if (E->getKind() != UETT_PtrAuthSchemaOptions)
9573+
// return false;
9574+
ASTContext &Ctx = Info.Ctx;
9575+
switch (E->getKind()) {
9576+
case UETT_PtrAuthSchemaOptions: {
9577+
auto ArgumentType = E->getArgumentType();
9578+
auto Qualifier = Ctx.getExplicitOrImplicitPointerAuth(ArgumentType);
9579+
if (!Qualifier)
9580+
return false;
9581+
if (!Qualifier->isPresent())
9582+
return false;
9583+
auto OptionsString = Ctx.getPointerAuthOptionsString(*Qualifier);
9584+
QualType StrTy =
9585+
Ctx.getStringLiteralArrayType(Ctx.CharTy, OptionsString.length());
9586+
StringLiteral *OptionsLit =
9587+
StringLiteral::Create(Ctx, OptionsString, StringLiteralKind::Ordinary,
9588+
/*Pascal=*/false, StrTy, SourceLocation());
9589+
APValue OptionsVal(OptionsLit, CharUnits::Zero(),
9590+
{APValue::LValuePathEntry::ArrayIndex(0)},
9591+
/*OnePastTheEnd=*/false);
9592+
return Success(OptionsVal, E);
9593+
}
9594+
default:
9595+
return false;
9596+
}
9597+
}
9598+
95649599
bool VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E) {
95659600
std::string ResultStr = E->ComputeName(Info.Ctx);
95669601

@@ -14878,6 +14913,43 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
1487814913
return Success(
1487914914
Info.Ctx.getPointerAuthTypeDiscriminator(E->getArgumentType()), E);
1488014915
}
14916+
case UETT_PtrAuthHasAuthentication: {
14917+
auto ArgumentType = E->getArgumentType();
14918+
auto Qualifier = Info.Ctx.getExplicitOrImplicitPointerAuth(ArgumentType);
14919+
if (!Qualifier)
14920+
return false;
14921+
return Success(Qualifier->isPresent(), E);
14922+
}
14923+
case UETT_PtrAuthSchemaKey: {
14924+
auto ArgumentType = E->getArgumentType();
14925+
auto Qualifier = Info.Ctx.getExplicitOrImplicitPointerAuth(ArgumentType);
14926+
if (!Qualifier)
14927+
return false;
14928+
if (!Qualifier->isPresent())
14929+
return false;
14930+
return Success(Qualifier->getKey(), E);
14931+
}
14932+
case UETT_PtrAuthSchemaIsAddressDiscriminated: {
14933+
auto ArgumentType = E->getArgumentType();
14934+
auto Qualifier = Info.Ctx.getExplicitOrImplicitPointerAuth(ArgumentType);
14935+
if (!Qualifier)
14936+
return false;
14937+
if (!Qualifier->isPresent())
14938+
return false;
14939+
return Success(Qualifier->isAddressDiscriminated(), E);
14940+
}
14941+
case UETT_PtrAuthSchemaExtraDiscriminator: {
14942+
auto ArgumentType = E->getArgumentType();
14943+
auto Qualifier = Info.Ctx.getExplicitOrImplicitPointerAuth(ArgumentType);
14944+
if (!Qualifier)
14945+
return false;
14946+
if (!Qualifier->isPresent())
14947+
return false;
14948+
return Success(Qualifier->getExtraDiscriminator(), E);
14949+
}
14950+
case UETT_PtrAuthSchemaOptions:
14951+
llvm_unreachable(
14952+
"UETT_PtrAuthSchemaOptions should be evaluated as a pointer");
1488114953
case UETT_VecStep: {
1488214954
QualType Ty = E->getTypeOfArgument();
1488314955

clang/lib/AST/ItaniumMangle.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5426,6 +5426,14 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
54265426
Diags.Report(E->getExprLoc(), DiagID) << getTraitSpelling(SAE->getKind());
54275427
return;
54285428
}
5429+
case UETT_PtrAuthHasAuthentication:
5430+
case UETT_PtrAuthSchemaKey:
5431+
case UETT_PtrAuthSchemaIsAddressDiscriminated:
5432+
case UETT_PtrAuthSchemaExtraDiscriminator:
5433+
case UETT_PtrAuthSchemaOptions: {
5434+
MangleExtensionBuiltin(SAE);
5435+
break;
5436+
}
54295437
}
54305438
break;
54315439
}

clang/lib/CodeGen/CGExprScalar.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3573,6 +3573,16 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
35733573
} else if (E->getKind() == UETT_VectorElements) {
35743574
auto *VecTy = cast<llvm::VectorType>(ConvertType(E->getTypeOfArgument()));
35753575
return Builder.CreateElementCount(CGF.SizeTy, VecTy->getElementCount());
3576+
} else if (E->getKind() == clang::UETT_PtrAuthSchemaOptions) {
3577+
auto PointerAuth =
3578+
CGF.getContext().getExplicitOrImplicitPointerAuth(E->getArgumentType());
3579+
assert(PointerAuth);
3580+
auto OptionsString =
3581+
CGF.getContext().getPointerAuthOptionsString(*PointerAuth);
3582+
ConstantAddress C =
3583+
CGF.CGM.GetAddrOfConstantCString(OptionsString, OptionsString.c_str());
3584+
return CGF.Builder.CreateBitCast(C.getPointer(),
3585+
CGF.getTypes().ConvertType(E->getType()));
35763586
}
35773587

35783588
// If this isn't sizeof(vla), the result must be constant; use the constant

clang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1530,7 +1530,7 @@ void CompilerInvocation::setDefaultPointerAuthOptions(
15301530
using Discrimination = PointerAuthSchema::Discrimination;
15311531
// If you change anything here, be sure to update <ptrauth.h>.
15321532
Opts.FunctionPointers = PointerAuthSchema(
1533-
Key::ASIA, false,
1533+
(Key)LangOpts.PointerAuthFunctionKey, false,
15341534
LangOpts.PointerAuthFunctionTypeDiscrimination ? Discrimination::Type
15351535
: Discrimination::None);
15361536

@@ -3581,6 +3581,8 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args,
35813581
Opts.PointerAuthELFGOT = Args.hasArg(OPT_fptrauth_elf_got);
35823582
Opts.AArch64JumpTableHardening =
35833583
Args.hasArg(OPT_faarch64_jump_table_hardening);
3584+
Opts.PointerAuthFunctionKey =
3585+
static_cast<unsigned>(PointerAuthSchema::ARM8_3Key::ASIA);
35843586
}
35853587

35863588
/// Check if input file kind and language standard are compatible.

clang/lib/Parse/ParseExpr.cpp

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,14 @@ bool Parser::isRevertibleTypeTrait(const IdentifierInfo *II,
861861
return false;
862862
}
863863

864-
ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() {
864+
ExprResult
865+
Parser::ParseBuiltinUnaryExprOrTypeTrait(UnaryExprOrTypeTrait ExprKind) {
866+
assert(ExprKind == UETT_PtrAuthTypeDiscriminator ||
867+
ExprKind == UETT_PtrAuthHasAuthentication ||
868+
ExprKind == UETT_PtrAuthSchemaKey ||
869+
ExprKind == UETT_PtrAuthSchemaIsAddressDiscriminated ||
870+
ExprKind == UETT_PtrAuthSchemaExtraDiscriminator ||
871+
ExprKind == UETT_PtrAuthSchemaOptions);
865872
SourceLocation Loc = ConsumeToken();
866873

867874
BalancedDelimiterTracker T(*this, tok::l_paren);
@@ -877,10 +884,33 @@ ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() {
877884
SourceLocation EndLoc = Tok.getLocation();
878885
T.consumeClose();
879886
return Actions.ActOnUnaryExprOrTypeTraitExpr(
880-
Loc, UETT_PtrAuthTypeDiscriminator,
887+
Loc, ExprKind,
881888
/*isType=*/true, Ty.get().getAsOpaquePtr(), SourceRange(Loc, EndLoc));
882889
}
883890

891+
ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() {
892+
return ParseBuiltinUnaryExprOrTypeTrait(UETT_PtrAuthTypeDiscriminator);
893+
}
894+
895+
ExprResult Parser::ParseBuiltinPtrauthQuery(tok::TokenKind Token) {
896+
switch (Token) {
897+
case tok::kw___builtin_ptrauth_has_authentication:
898+
return ParseBuiltinUnaryExprOrTypeTrait(UETT_PtrAuthHasAuthentication);
899+
case tok::kw___builtin_ptrauth_schema_key:
900+
return ParseBuiltinUnaryExprOrTypeTrait(UETT_PtrAuthSchemaKey);
901+
case tok::kw___builtin_ptrauth_schema_is_address_discriminated:
902+
return ParseBuiltinUnaryExprOrTypeTrait(
903+
UETT_PtrAuthSchemaIsAddressDiscriminated);
904+
case tok::kw___builtin_ptrauth_schema_extra_discriminator:
905+
return ParseBuiltinUnaryExprOrTypeTrait(
906+
UETT_PtrAuthSchemaExtraDiscriminator);
907+
case tok::kw___builtin_ptrauth_schema_options:
908+
return ParseBuiltinUnaryExprOrTypeTrait(UETT_PtrAuthSchemaOptions);
909+
default:
910+
llvm_unreachable("Unexpected token");
911+
}
912+
}
913+
884914
/// Parse a cast-expression, or, if \pisUnaryExpression is true, parse
885915
/// a unary-expression.
886916
///
@@ -1858,6 +1888,13 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
18581888
case tok::kw___builtin_ptrauth_type_discriminator:
18591889
return ParseBuiltinPtrauthTypeDiscriminator();
18601890

1891+
case tok::kw___builtin_ptrauth_has_authentication:
1892+
case tok::kw___builtin_ptrauth_schema_key:
1893+
case tok::kw___builtin_ptrauth_schema_is_address_discriminated:
1894+
case tok::kw___builtin_ptrauth_schema_extra_discriminator:
1895+
case tok::kw___builtin_ptrauth_schema_options:
1896+
return ParseBuiltinPtrauthQuery(SavedKind);
1897+
18611898
case tok::kw___is_lvalue_expr:
18621899
case tok::kw___is_rvalue_expr:
18631900
if (NotPrimaryExpression)

0 commit comments

Comments
 (0)