Skip to content

[clang][PAC] add support for options parameter to __ptrauth #136828

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 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
32 changes: 28 additions & 4 deletions clang/docs/PointerAuthentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ a number of different tests.
__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.
Expand All @@ -303,6 +303,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
Expand All @@ -314,9 +317,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``;

Expand All @@ -327,6 +330,27 @@ a discriminator determined as follows:
is ``ptrauth_blend_discriminator(&x, discriminator)``; see
`ptrauth_blend_discriminator`_.

``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
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This PR does not actually provide the implementation of this option, as it touches a number of places unrelated to parsing the option.

``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>``
~~~~~~~~~~~~~~~

Expand Down
6 changes: 3 additions & 3 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -3606,9 +3606,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>];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should there be an update to AttrDocs.td for the new 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.

I'll see if there's any existing documentation as the attribute can't be specified by attribute syntax

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated the PointerAuthentication document

let Documentation = [PtrAuthDocs];
}

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1737,7 +1737,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">,
Expand Down
13 changes: 12 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,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">,
Expand Down Expand Up @@ -1737,7 +1749,6 @@ def err_static_assert_requirement_failed : Error<
def note_expr_evaluates_to : Note<
"expression evaluates to '%0 %1 %2'">;


def subst_user_defined_msg : TextSubstitution<
"%select{the message|the expression}0 in "
"%select{a static assertion|this asm operand}0">;
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";
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@cor3ntin @AaronBallman is there a better/more idiomatic way of doing these?

Copy link
Collaborator

Choose a reason for hiding this comment

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

At the language option level, I think it's more idiomatic to use an enumeration instead of string literals and doing string processing.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd suggest doing a string-switch on it, then storing an enum in the AST. We can then print it/whatever on the way out. But a pair of conversion functions is typically all you should need. Though, it doesn't seem that these are leaving the handle function anyway, so IDK.

Either way, LangOpts is a little bit of an odd place for this to live.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not stored as a string (or even an attribute), they're stored as flags and enums in the pointer auth qualifier on 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.

I was meaning "is there a more idiomatic way to have these string constants specified?" :D

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, I hadn't included the updated to stringifying the qualifier so these were only used in one place.

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
34 changes: 33 additions & 1 deletion clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2537,7 +2537,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 {
Expand Down
24 changes: 17 additions & 7 deletions clang/lib/CodeGen/CGExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -2210,16 +2218,17 @@ 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);
}

CGPointerAuthInfo AuthInfo;

if (EnablePtrAuthFunctionTypeDiscrimination)
if (IsFunction && EnablePtrAuthFunctionTypeDiscrimination)
AuthInfo = CGM.getFunctionPointerAuthInfo(DestType);

if (AuthInfo) {
Expand All @@ -2237,17 +2246,18 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
};

if (const auto *FD = dyn_cast<FunctionDecl>(D))
return PtrAuthSign(CGM.getRawFunctionPointer(FD));
return PtrAuthSign(CGM.getRawFunctionPointer(FD), /*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);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3176,7 +3176,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;
}
Expand Down
Loading
Loading