-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang][ptrauth] Add support for querying the ptrauth schema of a type #138482
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -500,6 +500,24 @@ 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 type queries`` | ||||||||||||||||||||||||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
There are a number of builtins that can be used to query the ptrauth qualifier | ||||||||||||||||||||||||||||
parameters of a type, including those configured implicitly. These are: | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
.. code-block:: c | ||||||||||||||||||||||||||||
__builtin_ptrauth_has_authentication(type) | ||||||||||||||||||||||||||||
__builtin_ptrauth_schema_key(type) | ||||||||||||||||||||||||||||
__builtin_ptrauth_schema_is_address_discriminated(type) | ||||||||||||||||||||||||||||
__builtin_ptrauth_schema_extra_discriminator(type) | ||||||||||||||||||||||||||||
__builtin_ptrauth_schema_options(type) | ||||||||||||||||||||||||||||
Comment on lines
+509
to
+514
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This should unbreak the documentation build, I expect. |
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
All these builtins are compile time constants. The schema queries are only valid | ||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||
on types that have some form of pointer authentication, including implicit | ||||||||||||||||||||||||||||
authentication as is present of function pointers. Each schema query returns a | ||||||||||||||||||||||||||||
value of the appropriate type for the relevant parameter to the __ptrauth | ||||||||||||||||||||||||||||
qualifier. | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
Alternative Implementations | ||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1360,6 +1360,11 @@ class ASTContext : public RefCountedBase<ASTContext> { | |||||||||||||
/// Return the "other" type-specific discriminator for the given type. | ||||||||||||||
uint16_t getPointerAuthTypeDiscriminator(QualType T); | ||||||||||||||
|
||||||||||||||
/// Produces the canonical "options" string for the given PointerAuthQualifier | ||||||||||||||
/// such that using it explicitly in a __ptrauth qualifier would result in as | ||||||||||||||
/// identical configuration | ||||||||||||||
Comment on lines
+1363
to
+1365
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
std::string getPointerAuthOptionsString(const PointerAuthQualifier &PAQ); | ||||||||||||||
|
||||||||||||||
/// Apply Objective-C protocol qualifiers to the given type. | ||||||||||||||
/// \param allowOnPointerType specifies if we can apply protocol | ||||||||||||||
/// qualifiers on ObjCObjectPointerType. It can be set to true when | ||||||||||||||
|
@@ -1698,6 +1703,13 @@ class ASTContext : public RefCountedBase<ASTContext> { | |||||||||||||
|
||||||||||||||
QualType adjustStringLiteralBaseType(QualType StrLTy) const; | ||||||||||||||
|
||||||||||||||
/// Synthesizes a PointerAuthQualifier representing the actual authentication | ||||||||||||||
/// policy for the given type. This may be either the schema specified | ||||||||||||||
/// explicitly via the __ptrauth qualified in the source, or the implicit | ||||||||||||||
/// schema associated with function pointers and similar. | ||||||||||||||
std::optional<PointerAuthQualifier> | ||||||||||||||
getExplicitOrImplicitPointerAuth(QualType T); | ||||||||||||||
|
||||||||||||||
private: | ||||||||||||||
/// Return a normal function type with a typed argument list. | ||||||||||||||
QualType getFunctionTypeInternal(QualType ResultTy, ArrayRef<QualType> Args, | ||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -1046,6 +1046,8 @@ def err_ptrauth_address_discrimination_invalid : Error< | |||||
def err_ptrauth_extra_discriminator_invalid : Error< | ||||||
"invalid extra discriminator flag '%0'; '__ptrauth' requires a value between " | ||||||
"'0' and '%1'">; | ||||||
def err_ptrauth_query_type_has_no_pointer_authentication | ||||||
: Error<"argument to %0 parameter is not an authenticated value">; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Would something like this work? I'm trying to avoid "authenticated value" which is a novel term of art. I think it's more clear to tell the user explicitly that we expect the type to be qualified with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. righto, I'll try to go through this and other PRs to see if there's anything similar |
||||||
|
||||||
/// main() | ||||||
// static main() is not an error in C, just in C++. | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -607,6 +607,11 @@ KEYWORD(__private_extern__ , KEYALL) | |
KEYWORD(__module_private__ , KEYALL) | ||
|
||
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_type_discriminator, PtrAuthTypeDiscriminator, KEYALL) | ||
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_has_authentication, PtrAuthHasAuthentication, KEYALL) | ||
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_schema_key, PtrAuthSchemaKey, KEYALL) | ||
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_schema_is_address_discriminated, PtrAuthSchemaIsAddressDiscriminated, KEYALL) | ||
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_schema_extra_discriminator, PtrAuthSchemaExtraDiscriminator, KEYALL) | ||
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_schema_options, PtrAuthSchemaOptions, KEYALL) | ||
Comment on lines
+610
to
+614
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a bit weird to use UNARY_EXPR_OR_TYPE_TRAIT for these if they don't accept expressions (and i don't see tests with expressions). Should they be limited to taking types? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we have a type trait macro? (I actually thought that they did support expressions, but I realize that all the code I've written using them always uses decltype anyway, and it's not as if there's existing code that could break - probably) |
||
|
||
// Extension that will be enabled for Microsoft, Borland and PS4, but can be | ||
// disabled via '-fno-declspec'. | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -96,6 +96,7 @@ | |||||||||||||
#include <map> | ||||||||||||||
#include <memory> | ||||||||||||||
#include <optional> | ||||||||||||||
#include <sstream> | ||||||||||||||
#include <string> | ||||||||||||||
#include <tuple> | ||||||||||||||
#include <utility> | ||||||||||||||
|
@@ -9710,6 +9711,65 @@ ObjCInterfaceDecl *ASTContext::getObjCProtocolDecl() const { | |||||||||||||
return ObjCProtocolClassDecl; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
std::optional<PointerAuthQualifier> | ||||||||||||||
ASTContext::getExplicitOrImplicitPointerAuth(QualType T) { | ||||||||||||||
auto ExplicitQualifier = T.getPointerAuth(); | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please spell out the type. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. goddammit. I wonder if we can do something in clang-format that could identify There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||
if (ExplicitQualifier.isPresent()) | ||||||||||||||
return ExplicitQualifier; | ||||||||||||||
if (T->isDependentType()) { | ||||||||||||||
return std::nullopt; | ||||||||||||||
} | ||||||||||||||
// FIXME: The more we expand pointer auth support, the more it becomes clear | ||||||||||||||
// the codegen option's PointerAuth info belongs in LangOpts | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
if (!LangOpts.PointerAuthCalls) | ||||||||||||||
return PointerAuthQualifier(); | ||||||||||||||
if (T->isFunctionPointerType() || T->isFunctionReferenceType()) | ||||||||||||||
T = T->getPointeeType(); | ||||||||||||||
if (!T->isFunctionType()) | ||||||||||||||
return PointerAuthQualifier(); | ||||||||||||||
int ExtraDiscriminator = 0; | ||||||||||||||
if (LangOpts.PointerAuthFunctionTypeDiscrimination) { | ||||||||||||||
ExtraDiscriminator = getPointerAuthTypeDiscriminator(T); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+9731
to
+9733
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dammit I'd swear I got rid of all of these There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another thing I feel clang-format should be able to handle. |
||||||||||||||
return PointerAuthQualifier::Create( | ||||||||||||||
LangOpts.PointerAuthFunctionKey, false, ExtraDiscriminator, | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add /* Comment= */ to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup (another "I wonder if we can make clang-tidy complain") |
||||||||||||||
PointerAuthenticationMode::SignAndAuth, | ||||||||||||||
/*isIsaPointer=*/false, /*authenticatesNullValues=*/false); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
std::string | ||||||||||||||
ASTContext::getPointerAuthOptionsString(const PointerAuthQualifier &PAQ) { | ||||||||||||||
llvm::SmallVector<llvm::StringLiteral, 4> Options; | ||||||||||||||
switch (PAQ.getAuthenticationMode()) { | ||||||||||||||
case PointerAuthenticationMode::Strip: | ||||||||||||||
Options.push_back(PointerAuthenticationOptionStrip); | ||||||||||||||
break; | ||||||||||||||
case PointerAuthenticationMode::SignAndStrip: | ||||||||||||||
Options.push_back(PointerAuthenticationOptionSignAndStrip); | ||||||||||||||
break; | ||||||||||||||
case PointerAuthenticationMode::SignAndAuth: | ||||||||||||||
// Just default to not listing this explicitly | ||||||||||||||
break; | ||||||||||||||
default: | ||||||||||||||
llvm_unreachable("Invalid authentication mode"); | ||||||||||||||
} | ||||||||||||||
if (PAQ.isIsaPointer()) | ||||||||||||||
Options.push_back(PointerAuthenticationOptionIsaPointer); | ||||||||||||||
if (PAQ.authenticatesNullValues()) | ||||||||||||||
Options.push_back(PointerAuthenticationOptionAuthenticatesNullValues); | ||||||||||||||
if (Options.empty()) | ||||||||||||||
return std::string(); | ||||||||||||||
if (Options.size() == 1) | ||||||||||||||
return Options[0].str(); | ||||||||||||||
std::ostringstream Buffer; | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh sigh, that's why those strings are defined, because we need them here. I need to work out how to approach this patch and the options patch, as there's a bit, but not much, overlap. But this again goes back to the "where should the string constants like |
||||||||||||||
Buffer << Options[0].str(); | ||||||||||||||
for (unsigned i = 1; i < Options.size(); i++) { | ||||||||||||||
Buffer << ','; | ||||||||||||||
Buffer << Options[i].str(); | ||||||||||||||
} | ||||||||||||||
return Buffer.str(); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
//===----------------------------------------------------------------------===// | ||||||||||||||
// __builtin_va_list Construction Functions | ||||||||||||||
//===----------------------------------------------------------------------===// | ||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9573,6 +9573,41 @@ class PointerExprEvaluator | |
return true; | ||
} | ||
|
||
bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) { | ||
// This is the only UETT we evaluate here. | ||
assert(E->getKind() == UETT_PtrAuthSchemaOptions && | ||
"Unknown UnaryExprOrTypeTraitExpr"); | ||
|
||
// Note for review: there are other UETTs down the road | ||
// that make a switch make sense, but for now this is the only | ||
// one should this just be an | ||
// if (E->getKind() != UETT_PtrAuthSchemaOptions) | ||
// return false; | ||
Comment on lines
+9581
to
+9585
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
ASTContext &Ctx = Info.Ctx; | ||
switch (E->getKind()) { | ||
case UETT_PtrAuthSchemaOptions: { | ||
auto ArgumentType = E->getArgumentType(); | ||
auto Qualifier = Ctx.getExplicitOrImplicitPointerAuth(ArgumentType); | ||
if (!Qualifier) | ||
return false; | ||
if (!Qualifier->isPresent()) | ||
return false; | ||
auto OptionsString = Ctx.getPointerAuthOptionsString(*Qualifier); | ||
Comment on lines
+9589
to
+9595
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please spell out types. I'll stop leaving that as a comment; just take a pass through the whole PR and make sure There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ugh, yeah, I know that rule and thought I had removed a bunch of the auto usage, will do another scan once I've addressed the other comments. I wonder if there's something we can do to the clang-tidy check to make detecting this automatic |
||
QualType StrTy = | ||
Ctx.getStringLiteralArrayType(Ctx.CharTy, OptionsString.length()); | ||
StringLiteral *OptionsLit = | ||
StringLiteral::Create(Ctx, OptionsString, StringLiteralKind::Ordinary, | ||
/*Pascal=*/false, StrTy, SourceLocation()); | ||
APValue OptionsVal(OptionsLit, CharUnits::Zero(), | ||
{APValue::LValuePathEntry::ArrayIndex(0)}, | ||
/*OnePastTheEnd=*/false); | ||
return Success(OptionsVal, E); | ||
} | ||
default: | ||
return false; | ||
} | ||
} | ||
|
||
bool VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E) { | ||
std::string ResultStr = E->ComputeName(Info.Ctx); | ||
|
||
|
@@ -14891,6 +14926,43 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( | |
return Success( | ||
Info.Ctx.getPointerAuthTypeDiscriminator(E->getArgumentType()), E); | ||
} | ||
case UETT_PtrAuthHasAuthentication: { | ||
auto ArgumentType = E->getArgumentType(); | ||
auto Qualifier = Info.Ctx.getExplicitOrImplicitPointerAuth(ArgumentType); | ||
if (!Qualifier) | ||
return false; | ||
return Success(Qualifier->isPresent(), E); | ||
} | ||
case UETT_PtrAuthSchemaKey: { | ||
auto ArgumentType = E->getArgumentType(); | ||
auto Qualifier = Info.Ctx.getExplicitOrImplicitPointerAuth(ArgumentType); | ||
if (!Qualifier) | ||
return false; | ||
if (!Qualifier->isPresent()) | ||
return false; | ||
return Success(Qualifier->getKey(), E); | ||
} | ||
case UETT_PtrAuthSchemaIsAddressDiscriminated: { | ||
auto ArgumentType = E->getArgumentType(); | ||
auto Qualifier = Info.Ctx.getExplicitOrImplicitPointerAuth(ArgumentType); | ||
if (!Qualifier) | ||
return false; | ||
if (!Qualifier->isPresent()) | ||
return false; | ||
return Success(Qualifier->isAddressDiscriminated(), E); | ||
} | ||
case UETT_PtrAuthSchemaExtraDiscriminator: { | ||
auto ArgumentType = E->getArgumentType(); | ||
auto Qualifier = Info.Ctx.getExplicitOrImplicitPointerAuth(ArgumentType); | ||
if (!Qualifier) | ||
return false; | ||
if (!Qualifier->isPresent()) | ||
return false; | ||
return Success(Qualifier->getExtraDiscriminator(), E); | ||
} | ||
case UETT_PtrAuthSchemaOptions: | ||
llvm_unreachable( | ||
"UETT_PtrAuthSchemaOptions should be evaluated as a pointer"); | ||
case UETT_VecStep: { | ||
QualType Ty = E->getTypeOfArgument(); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.