-
Notifications
You must be signed in to change notification settings - Fork 15.5k
[clang][PAC] add support for options parameter to __ptrauth #172187
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
Conversation
This PR adds support for an 'options' parameter for the __ptrauth qualifier. The initial version only exposes the authehntication modes: * "strip" * "sign-and-strip" * "sign-and-auth" We also support parsing the options but not yet the implementation * "isa-pointer" * "authenticates-null-values" The initial support for authentication mode controls exist to support ABI changes over time, and as a byproduct support basic initial tests for option parsing.
|
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-clang Author: Oliver Hunt (ojhunt) ChangesThis PR adds support for an 'options' parameter for the __ptrauth qualifier.
We also support parsing the options but not yet the implementation
The initial support for authentication mode controls exist to support ABI changes over time, and as a byproduct support basic initial tests for option parsing. Patch is 51.03 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/172187.diff 14 Files Affected:
diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst
index bf2520b32a3a4..899bca203a137 100644
--- a/clang/docs/PointerAuthentication.rst
+++ b/clang/docs/PointerAuthentication.rst
@@ -427,7 +427,7 @@ purposes they are all equivalent to ``ptrauth_calls``.
``__ptrauth`` qualifier
^^^^^^^^^^^^^^^^^^^^^^^
-``__ptrauth(key, address, discriminator)`` is an extended type
+``__ptrauth(key, address, discriminator, options)`` is an extended type
qualifier which causes so-qualified objects to hold pointers or pointer sized
integers signed using the specified schema rather than the default schema for
such types.
@@ -452,6 +452,9 @@ The qualifier's operands are as follows:
- ``discriminator`` - a constant discriminator; must be a constant expression
+- ``options`` - a constant string expression containing a list of comma
+ separated authentication options; see ``ptrauth_qualifier_options``_
+
See `Discriminators`_ for more information about discriminators.
Currently the operands must be constant-evaluable even within templates. In the
@@ -463,9 +466,9 @@ qualifiers on a parameter (after parameter type adjustment) are ignored when
deriving the type of the function. The parameter will be passed using the
default ABI for the unqualified pointer type.
-If ``x`` is an object of type ``__ptrauth(key, address, discriminator) T``,
-then the signing schema of the value stored in ``x`` is a key of ``key`` and
-a discriminator determined as follows:
+If ``x`` is an object of type ``__ptrauth(key, address, discriminator, options) T``,
+then the signing schema of the value stored in ``x`` is a key of ``key`` and a
+discriminator determined as follows:
- if ``address`` is 0, then the discriminator is ``discriminator``;
@@ -527,6 +530,27 @@ rules of C++:
indirectly. Thus, changing the address-sensitivity of a type may be
ABI-breaking even if its size and alignment do not change.
+``ptrauth_qualifier_options``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The options parameter to the ``__ptrauth`` qualifier is a string of comma
+separated modifiers to the normal authentication behavior. Currently supported
+options are
+
+- Authentication mode: This is one of ``strip``, ``sign-and-strip``, and
+ ``sign-and-auth``. The ability to modify this behavior is intended to support
+ staging ABI changes. The ``strip`` mode results in the PAC bits of a value
+ being stripped from any value and disabled any other authentication
+ operations. ``sign-and-strip`` strips an authenticated on read, but will
+ ensure a correct signature is set on assignment. Finally ``sign-and-auth`` is
+ the default mode, and provides full protection for the value.
+
+- ``authenticates-null-values``: By default the __ptrauth qualifier does not
+ sign the zero value. This permits fast implementation of null checks in the
+ common case where a null value is safe. The ``authenticates-null-values``
+ option overrides this behavior, and permits null values to be protected with
+ pointer authentication.
+
``<ptrauth.h>``
~~~~~~~~~~~~~~~
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 0b006142cbb74..d0d65fbe166f0 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3750,9 +3750,9 @@ def ObjCRequiresPropertyDefs : InheritableAttr {
def PointerAuth : TypeAttr {
let Spellings = [CustomKeyword<"__ptrauth">];
- let Args = [IntArgument<"Key">,
- BoolArgument<"AddressDiscriminated", 1>,
- IntArgument<"ExtraDiscriminator", 1>];
+ let Args = [IntArgument<"Key">, BoolArgument<"AddressDiscriminated", 1>,
+ IntArgument<"ExtraDiscriminator", 1>,
+ StringArgument<"Options", 1>];
let Documentation = [PtrAuthDocs];
}
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 442a90ec2472d..6abadc6d4655e 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1771,7 +1771,7 @@ def warn_pragma_unroll_cuda_value_in_parens : Warning<
InGroup<CudaCompat>;
def err_ptrauth_qualifier_bad_arg_count : Error<
- "'__ptrauth' qualifier must take between 1 and 3 arguments">;
+ "'__ptrauth' qualifier must take between 1 and 4 arguments">;
def warn_cuda_attr_lambda_position : Warning<
"nvcc does not allow '__%0__' to appear after the parameter list in lambdas">,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 381d1fb063eba..c285e2d1e2a0d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1048,6 +1048,18 @@ def err_ptrauth_extra_discriminator_invalid : Error<
"invalid extra discriminator flag '%0'; '__ptrauth' requires a value between "
"'0' and '%1'">;
+// __ptrauth qualifier options string
+def note_ptrauth_evaluating_options
+ : Note<"options parameter evaluated to '%0'">;
+def err_ptrauth_invalid_option : Error<"'__ptrauth' options parameter %0">;
+def err_ptrauth_unknown_authentication_option
+ : Error<"unknown '__ptrauth' authentication option '%0'">;
+def err_ptrauth_repeated_authentication_option
+ : Error<"repeated '__ptrauth' authentication %select{mode|option}0%select{, prior "
+ "mode was '%2'| '%1'}0">;
+def err_ptrauth_option_missing_comma
+ : Error<"missing comma after '%0' option in '__ptrauth' qualifier">;
+
/// main()
// static main() is not an error in C, just in C++.
def warn_static_main : Warning<"'main' should not be declared static">,
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 4fa2dcffc75b6..0d1d94d63d076 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -67,6 +67,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 {
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index d2881d5ac518a..030168849c7e2 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2610,7 +2610,39 @@ void PointerAuthQualifier::print(raw_ostream &OS,
OS << "__ptrauth(";
OS << getKey();
OS << "," << unsigned(isAddressDiscriminated()) << ","
- << getExtraDiscriminator() << ")";
+ << getExtraDiscriminator();
+
+ bool HasAppendedOption = false;
+ auto AppendOption = [&](StringRef Option) {
+ OS << ",";
+ if (!HasAppendedOption)
+ OS << '"';
+ HasAppendedOption = true;
+ OS << Option;
+ };
+ switch (getAuthenticationMode()) {
+ case PointerAuthenticationMode::None:
+ llvm_unreachable("Mode is unauthenticated but claims to be present");
+ return;
+ case PointerAuthenticationMode::Strip:
+ AppendOption(PointerAuthenticationOptionStrip);
+ break;
+ case PointerAuthenticationMode::SignAndStrip:
+ AppendOption(PointerAuthenticationOptionSignAndStrip);
+ break;
+ case clang::PointerAuthenticationMode::SignAndAuth:
+ // Don't emit default auth
+ break;
+ }
+ if (isIsaPointer())
+ AppendOption(PointerAuthenticationOptionIsaPointer);
+ if (authenticatesNullValues())
+ AppendOption(PointerAuthenticationOptionAuthenticatesNullValues);
+
+ if (HasAppendedOption)
+ OS << '"';
+
+ OS << ")";
}
std::string Qualifiers::getAsString() const {
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 0eec4dba4824a..15ae2bf6f28c6 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2129,6 +2129,13 @@ class ConstantLValueEmitter : public ConstStmtVisitor<ConstantLValueEmitter,
}
+static bool shouldSignPointer(const PointerAuthQualifier &PointerAuth) {
+ PointerAuthenticationMode AuthenticationMode =
+ PointerAuth.getAuthenticationMode();
+ return AuthenticationMode == PointerAuthenticationMode::SignAndStrip ||
+ AuthenticationMode == PointerAuthenticationMode::SignAndAuth;
+}
+
llvm::Constant *ConstantLValueEmitter::tryEmit() {
const APValue::LValueBase &base = Value.getLValueBase();
@@ -2162,7 +2169,8 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() {
// Apply pointer-auth signing from the destination type.
if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth();
- PointerAuth && !result.HasDestPointerAuth) {
+ PointerAuth && !result.HasDestPointerAuth &&
+ shouldSignPointer(PointerAuth)) {
value = Emitter.tryEmitConstantSignedPointer(value, PointerAuth);
if (!value)
return nullptr;
@@ -2210,8 +2218,9 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
if (D->hasAttr<WeakRefAttr>())
return CGM.GetWeakRefReference(D).getPointer();
- auto PtrAuthSign = [&](llvm::Constant *C) {
- if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth()) {
+ auto PtrAuthSign = [&](llvm::Constant *C, bool IsFunction) {
+ if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth();
+ PointerAuth && shouldSignPointer(PointerAuth)) {
C = applyOffset(C);
C = Emitter.tryEmitConstantSignedPointer(C, PointerAuth);
return ConstantLValue(C, /*applied offset*/ true, /*signed*/ true);
@@ -2219,7 +2228,7 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
CGPointerAuthInfo AuthInfo;
- if (EnablePtrAuthFunctionTypeDiscrimination)
+ if (IsFunction && EnablePtrAuthFunctionTypeDiscrimination)
AuthInfo = CGM.getFunctionPointerAuthInfo(DestType);
if (AuthInfo) {
@@ -2240,18 +2249,19 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
llvm::Constant *C = CGM.getRawFunctionPointer(FD);
if (FD->getType()->isCFIUncheckedCalleeFunctionType())
C = llvm::NoCFIValue::get(cast<llvm::GlobalValue>(C));
- return PtrAuthSign(C);
+ return PtrAuthSign(C, /*IsFunction=*/true);
}
if (const auto *VD = dyn_cast<VarDecl>(D)) {
// We can never refer to a variable with local storage.
if (!VD->hasLocalStorage()) {
if (VD->isFileVarDecl() || VD->hasExternalStorage())
- return CGM.GetAddrOfGlobalVar(VD);
+ return PtrAuthSign(CGM.GetAddrOfGlobalVar(VD), /*IsFunction=*/false);
if (VD->isLocalVarDecl()) {
- return CGM.getOrCreateStaticVarDecl(
+ llvm::Constant *C = CGM.getOrCreateStaticVarDecl(
*VD, CGM.getLLVMLinkageVarDefinition(VD));
+ return PtrAuthSign(C, /*IsFunction=*/false);
}
}
}
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 8688ccf41acb5..270c039dc6f40 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3172,7 +3172,7 @@ void Parser::ParsePtrauthQualifier(ParsedAttributes &Attrs) {
T.consumeClose();
SourceLocation EndLoc = T.getCloseLocation();
- if (ArgExprs.empty() || ArgExprs.size() > 3) {
+ if (ArgExprs.empty() || ArgExprs.size() > 4) {
Diag(KwLoc, diag::err_ptrauth_qualifier_bad_arg_count);
return;
}
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index fd64d4456cbfa..880a30ea55b54 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -8456,14 +8456,15 @@ static void HandleNeonVectorTypeAttr(QualType &CurType, const ParsedAttr &Attr,
/// Handle the __ptrauth qualifier.
static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
const ParsedAttr &Attr, Sema &S) {
-
- assert((Attr.getNumArgs() > 0 && Attr.getNumArgs() <= 3) &&
- "__ptrauth qualifier takes between 1 and 3 arguments");
+ assert((Attr.getNumArgs() > 0 && Attr.getNumArgs() <= 4) &&
+ "__ptrauth qualifier takes between 1 and 4 arguments");
Expr *KeyArg = Attr.getArgAsExpr(0);
Expr *IsAddressDiscriminatedArg =
Attr.getNumArgs() >= 2 ? Attr.getArgAsExpr(1) : nullptr;
Expr *ExtraDiscriminatorArg =
Attr.getNumArgs() >= 3 ? Attr.getArgAsExpr(2) : nullptr;
+ Expr *AuthenticationOptionsArg =
+ Attr.getNumArgs() >= 4 ? Attr.getArgAsExpr(3) : nullptr;
unsigned Key;
if (S.checkConstantPointerAuthKey(KeyArg, Key)) {
@@ -8479,10 +8480,138 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
IsAddressDiscriminated);
IsInvalid |= !S.checkPointerAuthDiscriminatorArg(
ExtraDiscriminatorArg, PointerAuthDiscArgKind::Extra, ExtraDiscriminator);
+ std::string LastAuthenticationMode;
+ std::optional<PointerAuthenticationMode> AuthenticationMode = std::nullopt;
+ bool IsIsaPointer = false;
+ bool AuthenticatesNullValues = false;
+
+ if (AuthenticationOptionsArg && !AuthenticationOptionsArg->containsErrors()) {
+ StringRef OptionsString;
+ std::string EvaluatedString;
+ bool HasEvaluatedOptionsString = false;
+ const StringLiteral *OptionsStringLiteral =
+ dyn_cast<StringLiteral>(AuthenticationOptionsArg);
+ SourceRange AuthenticationOptionsRange =
+ AuthenticationOptionsArg->getSourceRange();
+ bool ReportedEvaluation = false;
+ auto ReportEvaluationOfExpressionIfNeeded = [&]() {
+ if (OptionsStringLiteral || !HasEvaluatedOptionsString ||
+ ReportedEvaluation)
+ return;
+ ReportedEvaluation = true;
+ S.Diag(AuthenticationOptionsRange.getBegin(),
+ diag::note_ptrauth_evaluating_options)
+ << OptionsString << AuthenticationOptionsRange;
+ };
+ auto DiagnoseInvalidOptionsParameter = [&](llvm::StringRef Reason) {
+ S.Diag(AuthenticationOptionsRange.getBegin(),
+ diag::err_ptrauth_invalid_option)
+ << Reason;
+ Attr.setInvalid();
+ IsInvalid = true;
+ ReportEvaluationOfExpressionIfNeeded();
+ };
+ if (AuthenticationOptionsArg->isValueDependent() ||
+ AuthenticationOptionsArg->isTypeDependent()) {
+ DiagnoseInvalidOptionsParameter("is dependent");
+ return;
+ }
+ if (OptionsStringLiteral) {
+ OptionsString = OptionsStringLiteral->getString();
+ HasEvaluatedOptionsString = true;
+ } else {
+ Expr::EvalResult Eval;
+ bool Result = AuthenticationOptionsArg->EvaluateAsRValue(Eval, Ctx);
+ if (Result && Eval.Val.isLValue()) {
+ auto *BaseExpr = Eval.Val.getLValueBase().dyn_cast<const Expr *>();
+ const StringLiteral *EvaluatedStringLiteral =
+ dyn_cast<StringLiteral>(const_cast<Expr *>(BaseExpr));
+ if (EvaluatedStringLiteral) {
+ CharUnits StartOffset = Eval.Val.getLValueOffset();
+ EvaluatedString = EvaluatedStringLiteral->getString().drop_front(
+ StartOffset.getQuantity());
+ OptionsString = EvaluatedString;
+ HasEvaluatedOptionsString = true;
+ }
+ }
+ }
+ if (!HasEvaluatedOptionsString) {
+ DiagnoseInvalidOptionsParameter(
+ "must be a string of comma separated flags");
+ return;
+ }
+ for (char Ch : OptionsString) {
+ if (Ch != '-' && Ch != ',' && !isWhitespace(Ch) && !isalpha(Ch)) {
+ DiagnoseInvalidOptionsParameter("contains invalid characters");
+ return;
+ }
+ }
+ HasEvaluatedOptionsString = true;
+ OptionsString = OptionsString.trim();
+ llvm::SmallVector<StringRef> Options;
+ if (!OptionsString.empty())
+ OptionsString.split(Options, ',');
+
+ auto OptionHandler = [&](auto Value, auto *Option,
+ std::string *LastOption = nullptr) {
+ return [&, Value, Option, LastOption](StringRef OptionString) {
+ if (!*Option) {
+ *Option = Value;
+ if (LastOption)
+ *LastOption = OptionString;
+ return true;
+ }
+ bool IsAuthenticationMode =
+ std::is_same_v<decltype(Value), PointerAuthenticationMode>;
+ S.Diag(AuthenticationOptionsRange.getBegin(),
+ diag::err_ptrauth_repeated_authentication_option)
+ << !IsAuthenticationMode << OptionString
+ << (LastOption ? *LastOption : "");
+ return false;
+ };
+ };
- if (IsInvalid) {
- Attr.setInvalid();
- return;
+ for (unsigned Idx = 0; Idx < Options.size(); ++Idx) {
+ StringRef Option = Options[Idx].trim();
+ if (Option.empty()) {
+ bool IsLastOption = Idx == (Options.size() - 1);
+ DiagnoseInvalidOptionsParameter(
+ IsLastOption ? "has a trailing comma" : "contains an empty option");
+ continue;
+ }
+ auto SelectedHandler =
+ llvm::StringSwitch<std::function<bool(StringRef)>>(Option)
+ .Case(PointerAuthenticationOptionStrip,
+ OptionHandler(PointerAuthenticationMode::Strip,
+ &AuthenticationMode, &LastAuthenticationMode))
+ .Case(PointerAuthenticationOptionSignAndStrip,
+ OptionHandler(PointerAuthenticationMode::SignAndStrip,
+ &AuthenticationMode, &LastAuthenticationMode))
+ .Case(PointerAuthenticationOptionSignAndAuth,
+ OptionHandler(PointerAuthenticationMode::SignAndAuth,
+ &AuthenticationMode, &LastAuthenticationMode))
+ .Case(PointerAuthenticationOptionIsaPointer,
+ OptionHandler(true, &IsIsaPointer))
+ .Case(PointerAuthenticationOptionAuthenticatesNullValues,
+ OptionHandler(true, &AuthenticatesNullValues))
+ .Default([&](StringRef Option) {
+ if (size_t WhitespaceIndex =
+ Option.find_first_of(" \t\n\v\f\r");
+ WhitespaceIndex != Option.npos) {
+ StringRef LeadingOption = Option.slice(0, WhitespaceIndex);
+ S.Diag(AuthenticationOptionsRange.getBegin(),
+ diag::err_ptrauth_option_missing_comma)
+ << LeadingOption;
+ } else {
+ S.Diag(AuthenticationOptionsRange.getBegin(),
+ diag::err_ptrauth_unknown_authentication_option)
+ << Option;
+ }
+ return false;
+ });
+ if (!SelectedHandler(Option))
+ IsInvalid = true;
+ }
}
if (!T->isSignableType(Ctx) && !T->isDependentType()) {
@@ -8491,6 +8620,9 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
return;
}
+ if (!AuthenticationMode)
+ AuthenticationMode = PointerAuthenticationMode::SignAndAuth;
+
if (T.getPointerAuth()) {
S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_redundant) << T;
Attr.setInvalid();
@@ -8503,13 +8635,19 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
return;
}
+ if (IsInvalid) {
+ Attr.setInvalid();
+ return;
+ }
+
assert((!IsAddressDiscriminatedArg || IsAddressDiscriminated <= 1) &&
"address discriminator arg should be either 0 or 1");
PointerAuthQualifier Qual = PointerAuthQualifier::Create(
- Key, IsAddressDiscriminated, ExtraDiscriminator,
- PointerAuthenticationMode::SignAndAuth, /*IsIsaPointer=*/false,
- /*AuthenticatesNullValues=*/false);
+ Key, IsAddressDiscriminated, ExtraDiscriminator, *AuthenticationMode,
+ IsIsaPointer, AuthenticatesNullValues);
+ assert(Qual.getAuthenticationMode() == *AuthenticationMode);
T = S.Context.getPointerAuthType(T, Qual);
+ assert(T.getPointerAuth().getAuthenticationMode() == *AuthenticationMode);
}
/// HandleArmSveVectorBitsTypeAttr - The "arm_sve_vector_bits...
[truncated]
|
|
(messed up updating the existing PR) |
This PR adds support for an 'options' parameter for the __ptrauth qualifier.
The initial version only exposes the authehntication modes:
We also support parsing the options but not yet the implementation
The initial support for authentication mode controls exist to support ABI changes over time, and as a byproduct support basic initial tests for option parsing.