Skip to content

[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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions clang/docs/PointerAuthentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parameters of a type, including those configured implicitly. These are:
arguments for 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.. 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)
.. 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)

This should unbreak the documentation build, I expect.


All these builtins are compile time constants. The schema queries are only valid
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
All these builtins are compile time constants. The schema queries are only valid
All these builtins are usable within a constant expression. The schema queries are only valid

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
Expand Down
12 changes: 12 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Produces the canonical "options" string for the given PointerAuthQualifier
/// such that using it explicitly in a __ptrauth qualifier would result in as
/// identical configuration
/// Produces the canonical "options" string for the given PointerAuthQualifier
/// such that using it explicitly in a __ptrauth qualifier would result in an
/// identical configuration.

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
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
: Error<"argument to %0 parameter is not an authenticated value">;
: Error<"%0 requires a '__ptrauth'-qualified type; type here is %1">;

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 __ptrauth and it's not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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++.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ LANGOPT(PointerAuthInitFiniAddressDiscrimination, 1, 0,
"incorporate address discrimination in authenticated function pointers in init/fini arrays")
LANGOPT(PointerAuthELFGOT, 1, 0, "authenticate pointers from GOT")
LANGOPT(AArch64JumpTableHardening, 1, 0, "use hardened lowering for jump-table dispatch")
LANGOPT(PointerAuthFunctionKey, 16, 0, "authentication key for function pointers")

LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes")
LANGOPT(ExperimentalLateParseAttributes, 1, 0, "experimental late parsing of attributes")
Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ enum class PointerAuthenticationMode : unsigned {
SignAndAuth
};

static constexpr llvm::StringLiteral PointerAuthenticationOptionStrip = "strip";
static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndStrip =
"sign-and-strip";
static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndAuth =
"sign-and-auth";
static constexpr llvm::StringLiteral PointerAuthenticationOptionIsaPointer =
"isa-pointer";
static constexpr llvm::StringLiteral
PointerAuthenticationOptionAuthenticatesNullValues =
"authenticates-null-values";

/// Bitfields of LangOptions, split out from LangOptions in order to ensure that
/// this large collection of bitfields is a trivial class type.
class LangOptionsBase {
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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'.
Expand Down
22 changes: 20 additions & 2 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -4358,8 +4358,6 @@ class Parser : public CodeCompletionHandler {
/// \endverbatim
bool tryParseOpenMPArrayShapingCastPart();

ExprResult ParseBuiltinPtrauthTypeDiscriminator();

///@}

//
Expand Down Expand Up @@ -5135,6 +5133,26 @@ class Parser : public CodeCompletionHandler {
///
ExprResult ParseExpressionTrait();

//===--------------------------------------------------------------------===//
// Pointer Authentication: Builtins to help work with pointer authentication
// qualifiers

/// ParseBuiltinPtrauthTypeTrait - Parse the pointer authentication
/// pseudo-functions. These consist of a number of operations to query the
/// authentication schema of a type, or to generate information for the
/// construction of authentication schemas.
///
/// \verbatim
/// primary-expression:
/// '__builtin_ptrauth_type_discriminator' '(' type-id ')'
/// '__builtin_ptrauth_has_authentication' '(' type-id ')'
/// '__builtin_ptrauth_schema_key' '(' type-id ')'
/// '__builtin_ptrauth_schema_is_address_discriminated' '(' type-id ')'
/// '__builtin_ptrauth_schema_extra_discriminator' '(' type-id ')'
/// '__builtin_ptrauth_schema_options' '(' type-id ')'
/// \endverbatim
ExprResult ParseBuiltinPtrauthTypeTrait();

///@}

//
Expand Down
60 changes: 60 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
#include <map>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <tuple>
#include <utility>
Expand Down Expand Up @@ -9710,6 +9711,65 @@ ObjCInterfaceDecl *ASTContext::getObjCProtocolDecl() const {
return ObjCProtocolClassDecl;
}

std::optional<PointerAuthQualifier>
ASTContext::getExplicitOrImplicitPointerAuth(QualType T) {
auto ExplicitQualifier = T.getPointerAuth();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please spell out the type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 auto ... = <no explicit mention of type> that would not be too noisy?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • trying to make a task once again

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// the codegen option's PointerAuth info belongs in LangOpts
// the codegen option's PointerAuth info belongs in LangOpts.

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (LangOpts.PointerAuthFunctionTypeDiscrimination) {
ExtraDiscriminator = getPointerAuthTypeDiscriminator(T);
}
if (LangOpts.PointerAuthFunctionTypeDiscrimination)
ExtraDiscriminator = getPointerAuthTypeDiscriminator(T);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dammit I'd swear I got rid of all of these

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add /* Comment= */ to the false argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

llvm::raw_string_ostream instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 PointerAuthenticationOptionSignAndStrip be placed?"

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
//===----------------------------------------------------------------------===//
Expand Down
72 changes: 72 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the if form is more clear until we get a second option, then the switch makes more sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • todo

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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 auto is only used when the type is explicitly spelled out on the right-hand side or is otherwise "super obvious".

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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);

Expand Down Expand Up @@ -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();

Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5466,6 +5466,14 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
Diags.Report(E->getExprLoc(), DiagID) << getTraitSpelling(SAE->getKind());
return;
}
case UETT_PtrAuthHasAuthentication:
case UETT_PtrAuthSchemaKey:
case UETT_PtrAuthSchemaIsAddressDiscriminated:
case UETT_PtrAuthSchemaExtraDiscriminator:
case UETT_PtrAuthSchemaOptions: {
MangleExtensionBuiltin(SAE);
break;
}
}
break;
}
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3600,6 +3600,16 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
} else if (E->getKind() == UETT_VectorElements) {
auto *VecTy = cast<llvm::VectorType>(ConvertType(E->getTypeOfArgument()));
return Builder.CreateElementCount(CGF.SizeTy, VecTy->getElementCount());
} else if (E->getKind() == clang::UETT_PtrAuthSchemaOptions) {
auto PointerAuth =
CGF.getContext().getExplicitOrImplicitPointerAuth(E->getArgumentType());
assert(PointerAuth);
auto OptionsString =
CGF.getContext().getPointerAuthOptionsString(*PointerAuth);
ConstantAddress C =
CGF.CGM.GetAddrOfConstantCString(OptionsString, OptionsString.c_str());
return CGF.Builder.CreateBitCast(C.getPointer(),
CGF.getTypes().ConvertType(E->getType()));
}

// If this isn't sizeof(vla), the result must be constant; use the constant
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1508,7 +1508,7 @@ void CompilerInvocation::setDefaultPointerAuthOptions(
using Discrimination = PointerAuthSchema::Discrimination;
// If you change anything here, be sure to update <ptrauth.h>.
Opts.FunctionPointers = PointerAuthSchema(
Key::ASIA, false,
(Key)LangOpts.PointerAuthFunctionKey, false,
LangOpts.PointerAuthFunctionTypeDiscrimination ? Discrimination::Type
: Discrimination::None);

Expand Down Expand Up @@ -3575,6 +3575,8 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args,
Opts.PointerAuthELFGOT = Args.hasArg(OPT_fptrauth_elf_got);
Opts.AArch64JumpTableHardening =
Args.hasArg(OPT_faarch64_jump_table_hardening);
Opts.PointerAuthFunctionKey =
static_cast<unsigned>(PointerAuthSchema::ARM8_3Key::ASIA);
}

/// Check if input file kind and language standard are compatible.
Expand Down
Loading
Loading