Skip to content

Commit b3de41c

Browse files
committed
[clang][ptrauth] add support for options parameter to __ptrauth
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" There are other options that will be added in future, but for now these are the modes already representable by PointerAuthQualifier. 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.
1 parent 8c74dc1 commit b3de41c

File tree

11 files changed

+543
-14
lines changed

11 files changed

+543
-14
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3568,7 +3568,8 @@ def PointerAuth : TypeAttr {
35683568
let Spellings = [CustomKeyword<"__ptrauth">];
35693569
let Args = [IntArgument<"Key">,
35703570
BoolArgument<"AddressDiscriminated", 1>,
3571-
IntArgument<"ExtraDiscriminator", 1>];
3571+
IntArgument<"ExtraDiscriminator", 1>,
3572+
StringArgument<"Options", 1>];
35723573
let Documentation = [PtrAuthDocs];
35733574
}
35743575

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1722,7 +1722,7 @@ def warn_pragma_unroll_cuda_value_in_parens : Warning<
17221722
InGroup<CudaCompat>;
17231723

17241724
def err_ptrauth_qualifier_bad_arg_count : Error<
1725-
"'__ptrauth' qualifier must take between 1 and 3 arguments">;
1725+
"'__ptrauth' qualifier must take between 1 and 4 arguments">;
17261726

17271727
def warn_cuda_attr_lambda_position : Warning<
17281728
"nvcc does not allow '__%0__' to appear after the parameter list in lambdas">,

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,24 @@ def err_ptrauth_address_discrimination_invalid : Error<
10361036
def err_ptrauth_extra_discriminator_invalid : Error<
10371037
"invalid extra discriminator flag '%0'; '__ptrauth' requires a value between '0' and '%1'">;
10381038

1039+
// __ptrauth qualifier options string
1040+
def note_ptrauth_evaluating_options
1041+
: Note<"options parameter evaluated to '%0'">;
1042+
def err_ptrauth_invalid_option
1043+
: Error<"'%0' options parameter %1">;
1044+
def err_ptrauth_unknown_authentication_option
1045+
: Error<"unknown '%0' authentication option '%1'">;
1046+
def err_ptrauth_repeated_authentication_option
1047+
: Error<"repeated '%0' authentication %select{mode|option}1">;
1048+
def note_ptrauth_previous_authentication_option
1049+
: Note<"previous '%0' authentication %select{mode|option}1">;
1050+
def err_ptrauth_unexpected_option_end
1051+
: Error<"unexpected end of options parameter for %0">;
1052+
def err_ptrauth_option_unexpected_token
1053+
: Error<"unexpected character '%0' in '%1' options">;
1054+
def err_ptrauth_option_missing_comma
1055+
: Error<"missing comma between '%0' options">;
1056+
10391057
/// main()
10401058
// static main() is not an error in C, just in C++.
10411059
def warn_static_main : Warning<"'main' should not be declared static">,
@@ -1728,8 +1746,8 @@ def note_expr_evaluates_to : Note<
17281746

17291747

17301748
def subst_user_defined_msg : TextSubstitution<
1731-
"%select{the message|the expression}0 in "
1732-
"%select{a static assertion|this asm operand}0">;
1749+
"%select{the message|the expression|the expression}0 in "
1750+
"%select{a static assertion|this asm operand|a pointer authentication option}0">;
17331751

17341752
def err_user_defined_msg_invalid : Error<
17351753
"%sub{subst_user_defined_msg}0 must be a string literal or an "

clang/include/clang/Basic/LangOptions.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ 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+
6874
/// Bitfields of LangOptions, split out from LangOptions in order to ensure that
6975
/// this large collection of bitfields is a trivial class type.
7076
class LangOptionsBase {

clang/include/clang/Sema/Sema.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5691,7 +5691,7 @@ class Sema final : public SemaBase {
56915691
void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method);
56925692
void ActOnFinishDelayedMemberInitializers(Decl *Record);
56935693

5694-
enum class StringEvaluationContext { StaticAssert = 0, Asm = 1 };
5694+
enum class StringEvaluationContext { StaticAssert = 0, Asm = 1, PtrauthOptions = 2 };
56955695

56965696
bool EvaluateAsString(Expr *Message, APValue &Result, ASTContext &Ctx,
56975697
StringEvaluationContext EvalContext,

clang/lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3427,7 +3427,7 @@ void Parser::ParsePtrauthQualifier(ParsedAttributes &Attrs) {
34273427
T.consumeClose();
34283428
SourceLocation EndLoc = T.getCloseLocation();
34293429

3430-
if (ArgExprs.empty() || ArgExprs.size() > 3) {
3430+
if (ArgExprs.empty() || ArgExprs.size() > 4) {
34313431
Diag(KwLoc, diag::err_ptrauth_qualifier_bad_arg_count);
34323432
return;
34333433
}

clang/lib/Sema/SemaType.cpp

Lines changed: 180 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8350,14 +8350,16 @@ static void HandleNeonVectorTypeAttr(QualType &CurType, const ParsedAttr &Attr,
83508350
/// Handle the __ptrauth qualifier.
83518351
static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
83528352
const ParsedAttr &Attr, Sema &S) {
8353-
8354-
assert((Attr.getNumArgs() > 0 && Attr.getNumArgs() <= 3) &&
8355-
"__ptrauth qualifier takes between 1 and 3 arguments");
8353+
assert((Attr.getNumArgs() > 0 && Attr.getNumArgs() <= 4) &&
8354+
"__ptrauth qualifier takes between 1 and 4 arguments");
8355+
StringRef AttrName = Attr.getAttrName()->getName();
83568356
Expr *KeyArg = Attr.getArgAsExpr(0);
83578357
Expr *IsAddressDiscriminatedArg =
83588358
Attr.getNumArgs() >= 2 ? Attr.getArgAsExpr(1) : nullptr;
83598359
Expr *ExtraDiscriminatorArg =
83608360
Attr.getNumArgs() >= 3 ? Attr.getArgAsExpr(2) : nullptr;
8361+
Expr *AuthenticationOptionsArg =
8362+
Attr.getNumArgs() >= 4 ? Attr.getArgAsExpr(3) : nullptr;
83618363

83628364
unsigned Key;
83638365
if (S.checkConstantPointerAuthKey(KeyArg, Key)) {
@@ -8374,20 +8376,191 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
83748376
IsInvalid |= !S.checkPointerAuthDiscriminatorArg(
83758377
ExtraDiscriminatorArg, Sema::PADAK_ExtraDiscPtrAuth, ExtraDiscriminator);
83768378

8377-
if (IsInvalid) {
8378-
Attr.setInvalid();
8379-
return;
8379+
std::optional<PointerAuthenticationMode> AuthenticationMode = std::nullopt;
8380+
SourceRange AuthenticationModeRange;
8381+
8382+
if (AuthenticationOptionsArg && !AuthenticationOptionsArg->containsErrors() ) {
8383+
std::string OptionsString;
8384+
bool IsInitialized = false;
8385+
const StringLiteral *OptionsStringLiteral = dyn_cast<StringLiteral>(AuthenticationOptionsArg);
8386+
auto ReportEvaluationOfExpressionIfNeeded = [&](){
8387+
if (OptionsStringLiteral || !IsInitialized)
8388+
return;
8389+
S.Diag(AuthenticationOptionsArg->getBeginLoc(),
8390+
diag::note_ptrauth_evaluating_options) << OptionsString << AuthenticationOptionsArg->getSourceRange();
8391+
};
8392+
auto DiagnoseInvalidOptionsParameter = [&](llvm::StringRef Reason, std::optional<char> InvalidCh, auto Location) {
8393+
S.Diag(AuthenticationOptionsArg->getExprLoc(),
8394+
diag::err_ptrauth_invalid_option)
8395+
<< AttrName << Reason << Location << !!InvalidCh << (InvalidCh ? *InvalidCh : '\0');
8396+
Attr.setInvalid();
8397+
ReportEvaluationOfExpressionIfNeeded();
8398+
};
8399+
if (AuthenticationOptionsArg->isValueDependent() || AuthenticationOptionsArg->isTypeDependent()) {
8400+
DiagnoseInvalidOptionsParameter("is dependent", std::nullopt, AuthenticationOptionsArg->getSourceRange());
8401+
return;
8402+
}
8403+
if (OptionsStringLiteral) {
8404+
if (OptionsStringLiteral->containsNonAsciiOrNull()) {
8405+
DiagnoseInvalidOptionsParameter("contains invalid characters", std::nullopt, AuthenticationOptionsArg->getSourceRange());
8406+
return;
8407+
}
8408+
OptionsString = OptionsStringLiteral->getString();
8409+
} else if (S.EvaluateAsString(AuthenticationOptionsArg, OptionsString, S.Context, Sema::StringEvaluationContext::PtrauthOptions, /*ErrorOnInvalidMessage=*/false)) {
8410+
for (char Ch : OptionsString) {
8411+
if (!Ch || !isascii(Ch)) {
8412+
DiagnoseInvalidOptionsParameter("contains invalid characters", Ch, AuthenticationOptionsArg->getSourceRange());
8413+
return;
8414+
}
8415+
}
8416+
} else {
8417+
Attr.setInvalid();
8418+
return;
8419+
}
8420+
IsInitialized = true;
8421+
bool HasSeenOption = false;
8422+
unsigned CurrentIdx = 0;
8423+
8424+
auto OptionStringIdxLocation = [&](unsigned Idx) {
8425+
if (OptionsStringLiteral)
8426+
return OptionsStringLiteral->getLocationOfByte(Idx, Ctx.getSourceManager(), Ctx.getLangOpts(), Ctx.getTargetInfo());
8427+
return AuthenticationOptionsArg->getBeginLoc();
8428+
};
8429+
auto OptionStringRange = [&](unsigned StartIdx, unsigned EndIdx) {
8430+
if (!OptionsStringLiteral)
8431+
return AuthenticationOptionsArg->getSourceRange();
8432+
return SourceRange(OptionStringIdxLocation(StartIdx),
8433+
OptionStringIdxLocation(EndIdx));
8434+
};
8435+
auto NextOption = [&]() -> std::optional<std::pair<unsigned, unsigned>> {
8436+
auto ConsumeChar = [&](auto Filter) -> char {
8437+
if (CurrentIdx >= OptionsString.size())
8438+
return 0;
8439+
char Current = OptionsString[CurrentIdx];
8440+
if (!Filter(Current))
8441+
return 0;
8442+
++CurrentIdx;
8443+
return Current;
8444+
};
8445+
auto SkipWhiteSpace = [&]() {
8446+
while (ConsumeChar(isWhitespace)) {
8447+
// this space is intentionally left blank
8448+
}
8449+
};
8450+
auto MatchCharacter = [](char MatchChar) {
8451+
return [MatchChar](char Ch){ return MatchChar == Ch; };
8452+
};
8453+
SkipWhiteSpace();
8454+
if (CurrentIdx == OptionsString.size())
8455+
return std::nullopt;
8456+
if (HasSeenOption) {
8457+
if (!ConsumeChar(MatchCharacter(','))) {
8458+
SourceLocation ErrorLocation = OptionStringIdxLocation(CurrentIdx);
8459+
S.Diag(ErrorLocation, diag::err_ptrauth_option_missing_comma)
8460+
<< AttrName << ErrorLocation;
8461+
ReportEvaluationOfExpressionIfNeeded();
8462+
return std::nullopt;
8463+
}
8464+
SkipWhiteSpace();
8465+
}
8466+
HasSeenOption = true;
8467+
if (CurrentIdx == OptionsString.size()) {
8468+
SourceLocation ErrorLocation = OptionStringIdxLocation(CurrentIdx);
8469+
S.Diag(ErrorLocation, diag::err_ptrauth_unexpected_option_end)
8470+
<< AttrName << ErrorLocation;
8471+
ReportEvaluationOfExpressionIfNeeded();
8472+
}
8473+
unsigned OptionStartIdx = CurrentIdx;
8474+
while (ConsumeChar(isalpha) || ConsumeChar(MatchCharacter('-'))) {
8475+
// this space is intentionally left blank
8476+
}
8477+
unsigned OptionEndIdx = CurrentIdx;
8478+
if (OptionStartIdx == OptionEndIdx) {
8479+
StringRef ErrorString(&OptionsString[CurrentIdx], 1);
8480+
SourceLocation ErrorLocation = OptionStringIdxLocation(OptionStartIdx);
8481+
S.Diag(ErrorLocation, diag::err_ptrauth_option_unexpected_token) << ErrorString << AttrName << ErrorLocation;
8482+
ReportEvaluationOfExpressionIfNeeded();
8483+
IsInvalid = true;
8484+
return std::nullopt;
8485+
}
8486+
return std::make_pair(OptionStartIdx, OptionEndIdx);
8487+
};
8488+
8489+
auto OptionHandler = [&](StringRef TokenStr, SourceRange TokenRange,
8490+
auto Value, auto *Option, SourceRange *OptionRange) {
8491+
SourceRange DiagnosedRange = TokenRange;
8492+
if (!OptionsStringLiteral)
8493+
DiagnosedRange = AuthenticationOptionsArg->getSourceRange();
8494+
if (!*Option) {
8495+
*Option = Value;
8496+
*OptionRange = DiagnosedRange;
8497+
return true;
8498+
}
8499+
bool IsAuthenticationMode =
8500+
std::is_same_v<decltype(Value), PointerAuthenticationMode>;
8501+
S.Diag(OptionRange->getBegin(), diag::err_ptrauth_repeated_authentication_option)
8502+
<< AttrName << !IsAuthenticationMode << *OptionRange;
8503+
IsInvalid = true;
8504+
if (OptionsStringLiteral)
8505+
S.Diag(OptionRange->getBegin(),
8506+
diag::note_ptrauth_previous_authentication_option)
8507+
<< AttrName << !IsAuthenticationMode << *OptionRange;
8508+
return false;
8509+
};
8510+
llvm::DenseMap<StringRef, std::function<bool(llvm::StringRef, SourceRange)>> OptionHandlers = {
8511+
{PointerAuthenticationOptionStrip,
8512+
[&](StringRef TokenStr, SourceRange Range) {
8513+
return OptionHandler(TokenStr, Range,
8514+
PointerAuthenticationMode::Strip,
8515+
&AuthenticationMode, &AuthenticationModeRange);
8516+
}},
8517+
{PointerAuthenticationOptionSignAndStrip,
8518+
[&](StringRef TokenStr, SourceRange Range) {
8519+
return OptionHandler(TokenStr, Range,
8520+
PointerAuthenticationMode::SignAndStrip,
8521+
&AuthenticationMode, &AuthenticationModeRange);
8522+
}},
8523+
{PointerAuthenticationOptionSignAndAuth,
8524+
[&](StringRef TokenStr, SourceRange Range) {
8525+
return OptionHandler(TokenStr, Range,
8526+
PointerAuthenticationMode::SignAndAuth,
8527+
&AuthenticationMode, &AuthenticationModeRange);
8528+
}}};
8529+
while (std::optional<std::pair<unsigned, unsigned>> Option = NextOption()) {
8530+
StringRef OptionString(&OptionsString[Option->first],
8531+
Option->second - Option->first);
8532+
SourceRange OptionRange = OptionStringRange(Option->first,
8533+
Option->second);
8534+
auto Handler = OptionHandlers.find(OptionString);
8535+
if (Handler == OptionHandlers.end()) {
8536+
S.Diag(OptionStringIdxLocation(Option->first),
8537+
diag::err_ptrauth_unknown_authentication_option)
8538+
<< AttrName << OptionString << OptionRange;
8539+
IsInvalid = true;
8540+
break;
8541+
}
8542+
if (!Handler->second(OptionString, OptionRange)) {
8543+
IsInvalid = true;
8544+
break;
8545+
}
8546+
}
83808547
}
83818548

83828549
if (!T->isSignableType() && !T->isDependentType()) {
83838550
S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_nonpointer) << T;
8551+
IsInvalid = true;
8552+
}
8553+
8554+
if (IsInvalid) {
83848555
Attr.setInvalid();
83858556
return;
83868557
}
8558+
if (!AuthenticationMode)
8559+
AuthenticationMode = PointerAuthenticationMode::SignAndAuth;
83878560

83888561
if (T.getPointerAuth()) {
83898562
S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_redundant)
8390-
<< T << Attr.getAttrName()->getName();
8563+
<< T << AttrName;
83918564
Attr.setInvalid();
83928565
return;
83938566
}

0 commit comments

Comments
 (0)