https://github.com/ojhunt updated https://github.com/llvm/llvm-project/pull/136828
>From 8d42eca9da4afd53b9d51fda5a8d285ed2cdeb72 Mon Sep 17 00:00:00 2001 From: Oliver Hunt <oli...@apple.com> Date: Wed, 30 Apr 2025 22:26:59 -0700 Subject: [PATCH 1/2] [clang][PAC] 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" 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. --- clang/include/clang/Basic/Attr.td | 6 +- .../clang/Basic/DiagnosticParseKinds.td | 4 +- .../clang/Basic/DiagnosticSemaKinds.td | 19 +- clang/include/clang/Basic/LangOptions.h | 11 + clang/lib/CodeGen/CGExprConstant.cpp | 24 +- clang/lib/Parse/ParseDecl.cpp | 2 +- clang/lib/Sema/SemaType.cpp | 157 ++++++++- clang/test/CodeGen/ptrauth-stripping.c | 327 ++++++++++++++++++ clang/test/Parser/ptrauth-qualifier.c | 2 +- clang/test/Sema/ptrauth-qualifier-options.c | 65 ++++ clang/test/Sema/ptrauth-qualifier.c | 39 ++- .../ptrauth-qualifier-constexpr-options.cpp | 65 ++++ 12 files changed, 691 insertions(+), 30 deletions(-) create mode 100644 clang/test/CodeGen/ptrauth-stripping.c create mode 100644 clang/test/Sema/ptrauth-qualifier-options.c create mode 100644 clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 37c80ac90182c..1941bb2d1febc 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3595,9 +3595,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 3bbdc49946dac..8e0f818714c42 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1735,8 +1735,8 @@ def warn_pragma_unroll_cuda_value_in_parens : Warning< "argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">, InGroup<CudaCompat>; -def err_ptrauth_qualifier_bad_arg_count : Error< - "'__ptrauth' qualifier must take between 1 and 3 arguments">; +def err_ptrauth_qualifier_bad_arg_count + : Error<"'__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 89b2d664d66a0..0da30d0d98061 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1045,6 +1045,24 @@ 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<"'%0' options parameter %1">; +def err_ptrauth_unknown_authentication_option + : Error<"unknown '%0' authentication option '%1'">; +def err_ptrauth_repeated_authentication_option + : Error<"repeated '%0' authentication %select{mode|option}1%select{, prior " + "mode was '%3'| '%2'}1">; +def note_ptrauth_previous_authentication_option + : Note<"previous '%0' authentication %select{mode|option}1">; +def err_ptrauth_unexpected_option_end + : Error<"unexpected end of options parameter for %0">; +def err_ptrauth_option_unexpected_token + : Error<"unexpected character '%0' in '%1' options">; +def err_ptrauth_option_missing_comma + : Error<"missing comma after '%1' option in '%0' qualifier">; + /// main() // static main() is not an error in C, just in C++. def warn_static_main : Warning<"'main' should not be declared static">, @@ -1735,7 +1753,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">; diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 491e8bee9fd5c..3944946374d30 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -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 { diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 6ba45f42db4d1..1de3d8a181343 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) { @@ -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); } } } diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 4fe3565687905..259ce5029271e 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3428,7 +3428,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 a8e85c885069e..deb894b53455d 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -8350,14 +8350,16 @@ 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"); + StringRef AttrName = Attr.getAttrName()->getName(); 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)) { @@ -8373,10 +8375,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) + << AttrName << 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) + << AttrName << !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) + << AttrName << LeadingOption; + } else { + S.Diag(AuthenticationOptionsRange.getBegin(), + diag::err_ptrauth_unknown_authentication_option) + << AttrName << Option; + } + return false; + }); + if (!SelectedHandler(Option)) + IsInvalid = true; + } } if (!T->isSignableType(Ctx) && !T->isDependentType()) { @@ -8385,6 +8515,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(); @@ -8397,13 +8530,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" attribute is diff --git a/clang/test/CodeGen/ptrauth-stripping.c b/clang/test/CodeGen/ptrauth-stripping.c new file mode 100644 index 0000000000000..4e187d8debdc7 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-stripping.c @@ -0,0 +1,327 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s + +typedef void *NonePointer; +typedef void *__ptrauth(1, 1, 101, "strip") StripPointer; +typedef void *__ptrauth(1, 1, 102, "sign-and-strip") SignAndStripPointer; +typedef void *__ptrauth(1, 1, 103, "sign-and-auth") SignAndAuthPointer; +typedef __UINT64_TYPE__ NoneIntptr; +typedef __UINT64_TYPE__ __ptrauth(1, 0, 105, "strip") StripIntptr; +typedef __UINT64_TYPE__ __ptrauth(1, 0, 106, "sign-and-strip") SignAndStripIntptr; +typedef __UINT64_TYPE__ __ptrauth(1, 0, 107, "sign-and-auth") SignAndAuthIntptr; + +NonePointer globalNonePointer = "foo0"; +StripPointer globalStripPointer = "foo1"; +SignAndStripPointer globalSignAndStripPointer = "foo2"; +SignAndAuthPointer globalSignAndAuthPointer = "foo3"; +NoneIntptr globalNoneIntptr = (__UINT64_TYPE__)&globalNonePointer; +StripIntptr globalStripIntptr = (__UINT64_TYPE__)&globalStripPointer; +SignAndStripIntptr globalSignAndStripIntptr = (__UINT64_TYPE__)&globalSignAndStripPointer; +SignAndAuthIntptr globalSignAndAuthIntptr = (__UINT64_TYPE__)&globalSignAndAuthPointer; + +// CHECK: @.str = private unnamed_addr constant [5 x i8] c"foo0\00", align 1 +// CHECK: @globalNonePointer = global ptr @.str, align 8 +// CHECK: @.str.1 = private unnamed_addr constant [5 x i8] c"foo1\00", align 1 +// CHECK: @globalStripPointer = global ptr @.str.1, align 8 +// CHECK: @.str.2 = private unnamed_addr constant [5 x i8] c"foo2\00", align 1 +// CHECK: @globalSignAndStripPointer = global ptr ptrauth (ptr @.str.2, i32 1, i64 102, ptr @globalSignAndStripPointer), align 8 +// CHECK: @.str.3 = private unnamed_addr constant [5 x i8] c"foo3\00", align 1 +// CHECK: @globalSignAndAuthPointer = global ptr ptrauth (ptr @.str.3, i32 1, i64 103, ptr @globalSignAndAuthPointer), align 8 +// CHECK: @globalNoneIntptr = global i64 ptrtoint (ptr @globalNonePointer to i64), align 8 +// CHECK: @globalStripIntptr = global i64 ptrtoint (ptr @globalStripPointer to i64), align 8 +// CHECK: @globalSignAndStripIntptr = global i64 ptrtoint (ptr ptrauth (ptr @globalSignAndStripPointer, i32 1, i64 106) to i64), align 8 +// CHECK: @globalSignAndAuthIntptr = global i64 ptrtoint (ptr ptrauth (ptr @globalSignAndAuthPointer, i32 1, i64 107) to i64), align 8 + +typedef struct { + NonePointer ptr; + NoneIntptr i; +} NoneStruct; +typedef struct { + StripPointer ptr; + StripIntptr i; +} StripStruct; +typedef struct { + SignAndStripPointer ptr; + SignAndStripIntptr i; +} SignAndStripStruct; +typedef struct { + SignAndAuthPointer ptr; + SignAndAuthIntptr i; +} SignAndAuthStruct; + +// CHECK-LABEL: @testNone +NoneStruct testNone(NoneStruct *a, NoneStruct *b, NoneStruct c) { + globalNonePointer += 1; + // CHECK: [[GLOBALP:%.*]] = load ptr, ptr @globalNonePointer + // CHECK: [[GLOBALPP:%.*]] = getelementptr inbounds i8, ptr [[GLOBALP]], i64 1 + // CHECK: store ptr [[GLOBALPP]], ptr @globalNonePointer + globalNoneIntptr += 1; + // CHECK: [[GLOBALI:%.*]] = load i64, ptr @globalNoneIntptr + // CHECK: [[GLOBALIP:%.*]] = add i64 [[GLOBALI]], 1 + // CHECK: store i64 [[GLOBALIP]], ptr @globalNoneIntptr + a->ptr += 1; + // CHECK: [[PTR:%.*]] = load ptr, ptr %a.addr, align 8 + // CHECK: [[PTR_PTR:%.*]] = getelementptr inbounds nuw %struct.NoneStruct, ptr [[PTR]], i32 0, i32 0 + // CHECK: [[PTR:%.*]] = load ptr, ptr [[PTR_PTR]], align 8 + // CHECK: [[AP:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 1 + // CHECK: store ptr [[AP]], ptr [[PTR_PTR]], align 8 + a->i += 1; + // CHECK: [[PTR:%.*]] = load ptr, ptr %a.addr, align 8 + // CHECK: [[I_PTR:%.*]] = getelementptr inbounds nuw %struct.NoneStruct, ptr [[PTR]], i32 0, i32 1 + // CHECK: [[I:%.*]] = load i64, ptr [[I_PTR]], align 8 + // CHECK: [[IP:%.*]] = add i64 [[I]], 1 + // CHECK: store i64 [[IP]], ptr [[I_PTR]], align 8 + *b = *a; + // CHECK: [[B_ADDR:%.*]] = load ptr, ptr %b.addr, align 8 + // CHECK: [[A_ADDR:%.*]] = load ptr, ptr %a.addr, align 8 + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[B_ADDR]], ptr align 8 [[A_ADDR]], i64 16, i1 false) + return c; +} + +// CHECK-LABEL: @testStrip1 +void testStrip1() { + globalStripPointer += 1; + // CHECK: [[GLOBALP:%.*]] = load ptr, ptr @globalStripPointer + // CHECK: [[GLOBALPI:%.*]] = ptrtoint ptr [[GLOBALP]] to i64 + // CHECK: {{%.*}} = call i64 @llvm.ptrauth.strip(i64 [[GLOBALPI]], i32 1) +} +// CHECK-LABEL: @testStrip2 +void testStrip2(StripStruct *a) { + a->ptr += 1; + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr + // CHECK: [[PTR:%.*]] = getelementptr inbounds nuw %struct.StripStruct, ptr [[A]], i32 0, i32 0 + // CHECK: [[APTR:%.*]] = load ptr, ptr [[PTR]] + // CHECK: [[APTRI:%.*]] = ptrtoint ptr [[APTR]] to i64 + // CHECK: {{%.*}} = call i64 @llvm.ptrauth.strip(i64 [[APTRI]], i32 1) +} +// CHECK-LABEL: @testStrip3 +void testStrip3(StripStruct *a) { + a->i += 1; + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr + // CHECK: [[I:%.*]] = getelementptr inbounds nuw %struct.StripStruct, ptr [[A]], i32 0, i32 1 + // CHECK: [[I64:%.*]] = load i64, ptr [[I]] + // CHECK: {{%.*}} = call i64 @llvm.ptrauth.strip(i64 [[I64]], i32 1) +} +// CHECK-LABEL: @testStrip4 +void testStrip4(StripStruct *a, StripStruct *b) { + *b = *a; + // CHECK: call void @__copy_assignment_8_8_pa1_101_0_t8w8(ptr %0, ptr %1) +} + +// CHECK-LABEL: @testStrip5 +StripStruct testStrip5(StripStruct a) { + return a; + // CHECK: call void @__copy_constructor_8_8_pa1_101_0_t8w8(ptr %agg.result, ptr %a) +} + +// CHECK-LABEL: @testSignAndStrip1 +void testSignAndStrip1(void) { + globalSignAndStripPointer += 1; + // CHECK: [[GP:%.*]] = load ptr, ptr @globalSignAndStripPointer + // CHECK: [[GPI:%.*]] = ptrtoint ptr [[GP]] to i64 + // CHECK: [[STRIPPED:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[GPI]], i32 1) + // CHECK: [[STRIPPEDP:%.*]] = inttoptr i64 [[STRIPPED]] to ptr + // CHECK: [[PHI:%.*]] = phi ptr [ null, %entry ], [ [[STRIPPEDP]], %resign.nonnull ] + // CHECK: [[ADDPTR:%.*]] = getelementptr inbounds i8, ptr [[PHI]], i64 1 + // CHECK: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @globalSignAndStripPointer to i64), i64 102) + // CHECK: [[ADDPTRI:%.*]] = ptrtoint ptr [[ADDPTR]] to i64 + // CHECK: {{%.*}} = call i64 @llvm.ptrauth.sign(i64 [[ADDPTRI]], i32 1, i64 [[DISC]]) +} + +// CHECK-LABEL: @testSignAndStrip2 +void testSignAndStrip2(SignAndStripStruct *a) { + a->ptr += 1; + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr + // CHECK: %ptr = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr [[A]], i32 0, i32 0 + // CHECK: [[APTR:%.*]] = load ptr, ptr %ptr + // CHECK: [[APTRI:%.*]] = ptrtoint ptr [[APTR]] to i64 + // CHECK: [[STRIPPED:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[APTRI]], i32 1) + // CHECK: [[STRIPPEDP:%.*]] = inttoptr i64 [[STRIPPED]] to ptr + // CHECK: [[PHI:%.*]] = phi ptr [ null, %entry ], [ [[STRIPPEDP]], %resign.nonnull ] + // CHECK: %add.ptr = getelementptr inbounds i8, ptr [[PHI]], i64 1 + // CHECK: [[PTRI:%.*]] = ptrtoint ptr %ptr to i64 + // CHECK: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[PTRI]], i64 102) + // CHECK: [[APTRI:%.*]] = ptrtoint ptr %add.ptr to i64 + // CHECK: call i64 @llvm.ptrauth.sign(i64 [[APTRI]], i32 1, i64 [[DISC]]) +} + +// CHECK-LABEL: @testSignAndStrip3 +void testSignAndStrip3(SignAndStripStruct *a) { + a->i += 1; + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr + // CHECK: [[I:%.*]] = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr [[A]], i32 0, i32 1 + // CHECK: [[I64:%.*]] = load i64, ptr [[I]] + // CHECK: [[STRIPPED:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[I64]], i32 1) + // CHECK: [[PHI:%.*]] = phi i64 [ 0, %entry ], [ [[STRIPPED]], %resign.nonnull ] + // CHECK: %add = add i64 [[PHI]], 1 + // CHECK: {{%.*}} = call i64 @llvm.ptrauth.sign(i64 %add, i32 1, i64 106) +} + +// CHECK-LABEL: @testSignAndStrip4 +void testSignAndStrip4(SignAndStripStruct *a, SignAndStripStruct *b) { + *b = *a; + // CHECK: call void @__copy_assignment_8_8_pa1_102_0_t8w8(ptr %0, ptr %1) +} + +// CHECK-LABEL: @testSignAndStrip5 +SignAndStripStruct testSignAndStrip5(SignAndStripStruct a) { + return a; + // CHECK: call void @__copy_constructor_8_8_pa1_102_0_t8w8(ptr %agg.result, ptr %a) +} + +// CHECK-LABEL: @testSignAndAuth1 +void testSignAndAuth1() { + globalSignAndAuthPointer += 1; + // CHECK: %0 = load ptr, ptr @globalSignAndAuthPointer + // CHECK: %1 = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @globalSignAndAuthPointer to i64), i64 103) + // CHECK: %3 = ptrtoint ptr %0 to i64 + // CHECK: %4 = call i64 @llvm.ptrauth.auth(i64 %3, i32 1, i64 %1) + // CHECK: %5 = inttoptr i64 %4 to ptr + // CHECK: %6 = phi ptr [ null, %entry ], [ %5, %resign.nonnull ] + // CHECK: %add.ptr = getelementptr inbounds i8, ptr %6, i64 1 + // CHECK: %7 = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @globalSignAndAuthPointer to i64), i64 103) + // CHECK: %8 = ptrtoint ptr %add.ptr to i64 + // CHECK: %9 = call i64 @llvm.ptrauth.sign(i64 %8, i32 1, i64 %7) +} + +// CHECK-LABEL: @testSignAndAuth2 +void testSignAndAuth2(SignAndAuthStruct *a) { + a->i += 1; + // CHECK: %0 = load ptr, ptr %a.addr + // CHECK: %i = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %0, i32 0, i32 1 + // CHECK: %1 = load i64, ptr %i + // CHECK: %3 = call i64 @llvm.ptrauth.auth(i64 %1, i32 1, i64 107) + // CHECK: %4 = phi i64 [ 0, %entry ], [ %3, %resign.nonnull ] + // CHECK: %add = add i64 %4, 1 + // CHECK: %6 = call i64 @llvm.ptrauth.sign(i64 %add, i32 1, i64 107) + // CHECK: %7 = phi i64 [ 0, %resign.cont ], [ %6, %resign.nonnull1 ] +} + +// CHECK-LABEL: @testSignAndAuth3 +void testSignAndAuth3(SignAndAuthStruct *a) { + a->ptr += 1; + // CHECK: %0 = load ptr, ptr %a.addr + // CHECK: %ptr = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %0, i32 0, i32 0 + // CHECK: %1 = load ptr, ptr %ptr + // CHECK: %2 = ptrtoint ptr %ptr to i64 + // CHECK: %3 = call i64 @llvm.ptrauth.blend(i64 %2, i64 103) + // CHECK: %5 = ptrtoint ptr %1 to i64 + // CHECK: %6 = call i64 @llvm.ptrauth.auth(i64 %5, i32 1, i64 %3) + // CHECK: %7 = inttoptr i64 %6 to ptr + // CHECK: %8 = phi ptr [ null, %entry ], [ %7, %resign.nonnull ] + // CHECK: %add.ptr = getelementptr inbounds i8, ptr %8, i64 1 + // CHECK: %9 = ptrtoint ptr %ptr to i64 + // CHECK: %10 = call i64 @llvm.ptrauth.blend(i64 %9, i64 103) + // CHECK: %11 = ptrtoint ptr %add.ptr to i64 + // CHECK: %12 = call i64 @llvm.ptrauth.sign(i64 %11, i32 1, i64 %10) +} + +// CHECK-LABEL: @testSignAndAuth4 +void testSignAndAuth4(SignAndAuthStruct *a, SignAndAuthStruct *b) { + *b = *a; + // CHECK: call void @__copy_assignment_8_8_pa1_103_0_t8w8(ptr %0, ptr %1) +} + +// CHECK-LABEL: @testSignAndAuth5 +SignAndAuthStruct testSignAndAuth5(SignAndAuthStruct a) { + return a; + // CHECK: call void @__copy_constructor_8_8_pa1_103_0_t8w8(ptr %agg.result, ptr %a) +} + +// CHECK-LABEL: @testCoercions1 +void testCoercions1(StripStruct *a, SignAndStripStruct *b) { + a->ptr = b->ptr; + // CHECK: %0 = load ptr, ptr %a.addr + // CHECK: %ptr = getelementptr inbounds nuw %struct.StripStruct, ptr %0, i32 0, i32 0 + // CHECK: %1 = load ptr, ptr %b.addr + // CHECK: %ptr1 = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr %1, i32 0, i32 0 + // CHECK: %2 = load ptr, ptr %ptr1 + // CHECK: %3 = ptrtoint ptr %ptr1 to i64 + // CHECK: %4 = call i64 @llvm.ptrauth.blend(i64 %3, i64 102) + // CHECK: %8 = ptrtoint ptr %2 to i64 + // CHECK: %9 = call i64 @llvm.ptrauth.strip(i64 %8, i32 1) +} + +// CHECK-LABEL: @testCoercions2 +void testCoercions2(StripStruct *a, SignAndAuthStruct *b) { + b->ptr = a->ptr; + // CHECK: store ptr %a, ptr %a.addr + // CHECK: store ptr %b, ptr %b.addr + // CHECK: %0 = load ptr, ptr %b.addr + // CHECK: %ptr = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %0, i32 0, i32 0 + // CHECK: %1 = load ptr, ptr %a.addr + // CHECK: %ptr1 = getelementptr inbounds nuw %struct.StripStruct, ptr %1, i32 0, i32 0 + // CHECK: %2 = load ptr, ptr %ptr1 + // CHECK: %3 = ptrtoint ptr %ptr1 to i64 + // CHECK: %4 = call i64 @llvm.ptrauth.blend(i64 %3, i64 101) + // CHECK: %5 = ptrtoint ptr %ptr to i64 + // CHECK: %6 = call i64 @llvm.ptrauth.blend(i64 %5, i64 103) + // CHECK: %7 = icmp ne ptr %2, null + // CHECK: %8 = ptrtoint ptr %2 to i64 + // CHECK: %9 = call i64 @llvm.ptrauth.strip(i64 %8, i32 1) + // CHECK: %10 = inttoptr i64 %9 to ptr + // CHECK: %11 = ptrtoint ptr %10 to i64 + // CHECK: %12 = call i64 @llvm.ptrauth.sign(i64 %11, i32 1, i64 %6) +} + +// CHECK-LABEL: @testCoercions3 +void testCoercions3(SignAndStripStruct *a, SignAndAuthStruct *b) { + a->ptr = b->ptr; + // CHECK: %0 = load ptr, ptr %a.addr + // CHECK: %ptr = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr %0, i32 0, i32 0 + // CHECK: %1 = load ptr, ptr %b.addr + // CHECK: %ptr1 = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %1, i32 0, i32 0 + // CHECK: %2 = load ptr, ptr %ptr1 + // CHECK: %3 = ptrtoint ptr %ptr1 to i64 + // CHECK: %4 = call i64 @llvm.ptrauth.blend(i64 %3, i64 103) + // CHECK: %5 = ptrtoint ptr %ptr to i64 + // CHECK: %6 = call i64 @llvm.ptrauth.blend(i64 %5, i64 102) + // CHECK: %8 = ptrtoint ptr %2 to i64 + // CHECK: %9 = call i64 @llvm.ptrauth.auth(i64 %8, i32 1, i64 %4) + // CHECK: %10 = inttoptr i64 %9 to ptr + // CHECK: %11 = ptrtoint ptr %10 to i64 + // CHECK: %12 = call i64 @llvm.ptrauth.sign(i64 %11, i32 1, i64 %6) + // CHECK: %13 = inttoptr i64 %12 to ptr + // CHECK: %14 = phi ptr [ null, %entry ], [ %13, %resign.nonnull ] +} + +// CHECK-LABEL: @testCoercions4 +void testCoercions4(StripStruct *a, SignAndStripStruct *b) { + a->i = b->i; + // CHECK: store ptr %a, ptr %a.addr + // CHECK: store ptr %b, ptr %b.addr + // CHECK: %0 = load ptr, ptr %a.addr + // CHECK: %i = getelementptr inbounds nuw %struct.StripStruct, ptr %0, i32 0, i32 1 + // CHECK: %1 = load ptr, ptr %b.addr + // CHECK: %i1 = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr %1, i32 0, i32 1 + // CHECK: %2 = load i64, ptr %i1 + // CHECK: %4 = call i64 @llvm.ptrauth.strip(i64 %2, i32 1) + // CHECK: %5 = phi i64 [ 0, %entry ], [ %4, %resign.nonnull ] +} + +// CHECK-LABEL: @testCoercions5 +void testCoercions5(StripStruct *a, SignAndAuthStruct *b) { + b->i = a->i; + // CHECK: %0 = load ptr, ptr %b.addr + // CHECK: %i = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %0, i32 0, i32 1 + // CHECK: %1 = load ptr, ptr %a.addr + // CHECK: %i1 = getelementptr inbounds nuw %struct.StripStruct, ptr %1, i32 0, i32 1 + // CHECK: %2 = load i64, ptr %i1 + // CHECK: %4 = call i64 @llvm.ptrauth.strip(i64 %2, i32 1) + // CHECK: %5 = call i64 @llvm.ptrauth.sign(i64 %4, i32 1, i64 107) + // CHECK: %6 = phi i64 [ 0, %entry ], [ %5, %resign.nonnull ] + // CHECK: store i64 %6, ptr %i +} + +// CHECK-LABEL: @testCoercions6 +void testCoercions6(SignAndStripStruct *a, SignAndAuthStruct *b) { + a->i = b->i; + // CHECK: %0 = load ptr, ptr %a.addr + // CHECK: %i = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr %0, i32 0, i32 1 + // CHECK: %1 = load ptr, ptr %b.addr + // CHECK: %i1 = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %1, i32 0, i32 1 + // CHECK: %2 = load i64, ptr %i1 + // CHECK: %3 = icmp ne i64 %2, 0 + // CHECK: %4 = call i64 @llvm.ptrauth.auth(i64 %2, i32 1, i64 107) + // CHECK: %5 = call i64 @llvm.ptrauth.sign(i64 %4, i32 1, i64 106) + // CHECK: %6 = phi i64 [ 0, %entry ], [ %5, %resign.nonnull ] +} diff --git a/clang/test/Parser/ptrauth-qualifier.c b/clang/test/Parser/ptrauth-qualifier.c index 2071ac6c2d661..014613e0a3bbd 100644 --- a/clang/test/Parser/ptrauth-qualifier.c +++ b/clang/test/Parser/ptrauth-qualifier.c @@ -15,4 +15,4 @@ int nonConstantGlobal = 5; __ptrauth int invalid0; // expected-error{{expected '('}} __ptrauth() int invalid1; // expected-error{{expected expression}} -int * __ptrauth(VALID_DATA_KEY, 1, 1000, 12) invalid12; // expected-error{{qualifier must take between 1 and 3 arguments}} +int * __ptrauth(VALID_DATA_KEY, 1, 1000, 12, 24) invalid12; // expected-error{{qualifier must take between 1 and 4 arguments}} diff --git a/clang/test/Sema/ptrauth-qualifier-options.c b/clang/test/Sema/ptrauth-qualifier-options.c new file mode 100644 index 0000000000000..b079b264ce16a --- /dev/null +++ b/clang/test/Sema/ptrauth-qualifier-options.c @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -std=c23 -fsyntax-only -verify -fptrauth-intrinsics %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c23 -fsyntax-only -verify -fptrauth-intrinsics %s + +_Static_assert(__has_extension(ptrauth_qualifier), "the ptrauth qualifier should be available"); + +#if __aarch64__ +#define VALID_CODE_KEY 0 +#define VALID_DATA_KEY 2 +#define INVALID_KEY 200 +#else +#error Provide these constants if you port this test +#endif + + +typedef int *intp; + +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "Foo") invalid13_1; +// expected-error@-1 {{unknown '__ptrauth' authentication option 'Foo'}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip", 41) invalid14_1; +// expected-error@-1 {{'__ptrauth' qualifier must take between 1 and 4 arguments}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,sign-and-strip") invalid15; +// expected-error@-1 {{repeated '__ptrauth' authentication mode, prior mode was 'strip'}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "isa-pointer,isa-pointer") invalid16; +// expected-error@-1 {{repeated '__ptrauth' authentication option}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip, , isa-pointer") invalid18; +// expected-error@-1 {{'__ptrauth' options parameter contains an empty option}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,") invalid19; +// expected-error@-1 {{'__ptrauth' options parameter has a trailing comma}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",") invalid20; +// expected-error@-1 {{'__ptrauth' options parameter contains an empty option}} +// expected-error@-2 {{'__ptrauth' options parameter has a trailing comma}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",,") invalid21; +// expected-error@-1 2 {{'__ptrauth' options parameter contains an empty option}} +// expected-error@-2 {{'__ptrauth' options parameter has a trailing comma}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip isa-pointer") invalid22; +// expected-error@-1 {{missing comma after 'strip' option in '__ptrauth' qualifier}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip\nisa-pointer") invalid23; +// expected-error@-1 {{missing comma after 'strip' option in '__ptrauth' qualifier}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip" + " isa-pointer") invalid24; +// expected-error@-2{{missing comma after 'strip' option in '__ptrauth' qualifier}} +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\n,isa-pointer") invalid25; // expected-error{{'__ptrauth' options parameter contains an empty option}} +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\t,isa-pointer") invalid26; // expected-error{{'__ptrauth' options parameter contains an empty option}} + +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip") valid12; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip") valid13; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-auth") valid14; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "isa-pointer") valid15; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-auth,isa-pointer") valid15; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,isa-pointer") valid16; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip,isa-pointer") valid17; +int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip,isa-pointer") valid18; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip ,isa-pointer") valid19; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip, isa-pointer") valid20; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip,isa-pointer ") valid21; +int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip") valid22; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip ") valid23; +int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip ") valid24; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip," + "isa-pointer") valid25; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip" + ",isa-pointer") valid26; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip\n,isa-pointer") valid27; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip\t,isa-pointer") valid28; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "") valid29; diff --git a/clang/test/Sema/ptrauth-qualifier.c b/clang/test/Sema/ptrauth-qualifier.c index 5d932b724f07a..54f9334b212dc 100644 --- a/clang/test/Sema/ptrauth-qualifier.c +++ b/clang/test/Sema/ptrauth-qualifier.c @@ -1,6 +1,8 @@ // RUN: %clang_cc1 -triple arm64-apple-ios -std=c23 -fsyntax-only -verify -fptrauth-intrinsics %s // RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c23 -fsyntax-only -verify -fptrauth-intrinsics %s +#include <ptrauth.h> + #if !__has_extension(ptrauth_qualifier) // This error means that the __ptrauth qualifier availability test says that it // is not available. This error is not expected in the output, if it is seen @@ -34,7 +36,25 @@ int * __ptrauth(VALID_DATA_KEY, 1, -1) invalid9; // expected-error {{invalid ext int * __ptrauth(VALID_DATA_KEY, 1, 100000) invalid10; // expected-error {{invalid extra discriminator flag '100000'; '__ptrauth' requires a value between '0' and '65535'}} int * __ptrauth(VALID_DATA_KEY, 1, nonConstantGlobal) invalid12; // expected-error {{argument to '__ptrauth' must be an integer constant expression}} int * __ptrauth(VALID_DATA_KEY, nonConstantGlobal, 1000) invalid13; // expected-error {{argument to '__ptrauth' must be an integer constant expression}} -int * __ptrauth(nonConstantGlobal, 1, 1000) invalid14; // expected-error{{expression is not an integer constant expression}} +int * __ptrauth(nonConstantGlobal, 1, 1000) invalid14; // expected-error {{expression is not an integer constant expression}} + +int *__ptrauth(VALID_DATA_KEY, 1, 65535, 41) invalid11; // expected-error {{'__ptrauth' options parameter must be a string of comma separated flags}} +int *__ptrauth(VALID_DATA_KEY, 1, nonConstantGlobal) invalid12; // expected-error {{argument to '__ptrauth' must be an integer constant expression}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "Foo") invalid13; // expected-error {{unknown '__ptrauth' authentication option 'Foo'}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip", 41) invalid14; // expected-error {{'__ptrauth' qualifier must take between 1 and 4 arguments}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,sign-and-strip") invalid15; // expected-error {{repeated '__ptrauth' authentication mode, prior mode was 'strip'}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,") invalid19; // expected-error {{'__ptrauth' options parameter has a trailing comma}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",") invalid20; // expected-error {{'__ptrauth' options parameter contains an empty option}} + // expected-error@-1 {{'__ptrauth' options parameter has a trailing comma}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",,") invalid21; // expected-error {{'__ptrauth' options parameter contains an empty option}} + // expected-error@-1 {{'__ptrauth' options parameter contains an empty option}} + // expected-error@-2 {{'__ptrauth' options parameter has a trailing comma}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip isa-pointer") invalid22; // expected-error {{missing comma after 'strip' option in '__ptrauth' qualifier}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip\nisa-pointer") invalid23; // expected-error {{missing comma after 'strip' option in '__ptrauth' qualifier}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip" // expected-error {{missing comma after 'strip' option in '__ptrauth' qualifier}} + " isa-pointer") invalid24; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\n,isa-pointer") invalid25; // expected-error {{'__ptrauth' options parameter contains an empty option}} +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\t,isa-pointer") invalid26; // expected-error {{'__ptrauth' options parameter contains an empty option}} int * __ptrauth(VALID_DATA_KEY) valid0; int * __ptrauth(VALID_DATA_KEY) *valid1; @@ -42,11 +62,18 @@ __ptrauth(VALID_DATA_KEY) intp valid2; __ptrauth(VALID_DATA_KEY) intp *valid3; intp __ptrauth(VALID_DATA_KEY) valid4; intp __ptrauth(VALID_DATA_KEY) *valid5; -int * __ptrauth(VALID_DATA_KEY, 0) valid6; -int * __ptrauth(VALID_DATA_KEY, 1) valid7; -int * __ptrauth(VALID_DATA_KEY, (_Bool) 1) valid8; -int * __ptrauth(VALID_DATA_KEY, 1, 0) valid9; -int * __ptrauth(VALID_DATA_KEY, 1, 65535) valid10; +int *__ptrauth(VALID_DATA_KEY, 0) valid6; +int *__ptrauth(VALID_DATA_KEY, 1) valid7; +int *__ptrauth(VALID_DATA_KEY, (_Bool) 1) valid8; +int *__ptrauth(VALID_DATA_KEY, 1, 0) valid9; +int *__ptrauth(VALID_DATA_KEY, 1, 65535) valid10; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip") valid12; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip") valid13; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-auth") valid14; +int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip") valid22; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip ") valid23; +int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip ") valid24; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "") valid29; int * __ptrauth(VALID_DATA_KEY) array0[10]; int (* __ptrauth(VALID_DATA_KEY) array1)[10]; diff --git a/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp b/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp new file mode 100644 index 0000000000000..941cf1c57e1ba --- /dev/null +++ b/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++2b -Wno-string-plus-int -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s + +struct S { + static constexpr auto options = "strip"; +}; + +constexpr const char* const_options(int i) { + const char* local_const = "isa-pointer"; + constexpr auto local_constexpr = ","; + static constexpr auto static_const = "strip"; + static constexpr auto static_constexpr = "sign-and-strip"; + + switch (i) { + case 0: + return ""; + case 1: + return "authenticates-null-values"; + case 2: + return local_const; + case 3: + return local_constexpr; + case 4: + return static_const; + case 5: + return static_constexpr; + case 6: + return "some characters"; + case 7: + return S::options; + case 8: + return const_options(3)+1; + default: + #ifdef __EXCEPTIONS + throw "invalid index"; + #else + __builtin_trap(); + #endif + } +} + +void test_func() { + int * __ptrauth(1,1,1,const_options(0)) zero; + int * __ptrauth(1,1,1,const_options(1)) one; + int * __ptrauth(1,1,1,const_options(2)) two; + int * __ptrauth(1,1,1,const_options(3)) three; + // expected-error@-1 {{'__ptrauth' options parameter contains an empty option}} + // expected-error@-2 {{'__ptrauth' options parameter has a trailing comma}} + // expected-note@-3 {{options parameter evaluated to ','}} + int * __ptrauth(1,1,1,const_options(4)) four; + int * __ptrauth(1,1,1,const_options(5)) five; + int * __ptrauth(1,1,1,const_options(6)) six; + // expected-error@-1 {{missing comma after 'some' option in '__ptrauth' qualifier}} + int * __ptrauth(1,1,1,const_options(7)) seven; + int * __ptrauth(1,1,1,const_options(8)) eight; + int * __ptrauth(1,1,1,S::options) struct_access; + int * __ptrauth(1,1,1,2 * 3) ice; + // expected-error@-1 {{'__ptrauth' options parameter must be a string of comma separated flags}} + int * __ptrauth(1,1,1,4 + "wat,strip") arithmetic_string; + int * __ptrauth(1,1,1,5 + "wat,strip") arithmetic_string2; + // expected-error@-1 {{unknown '__ptrauth' authentication option 'trip'}} + + // Handle evaluation failing + int * __ptrauth(1,1,1,const_options(50)) fifty; + // expected-error@-1 {{'__ptrauth' options parameter must be a string of comma separated flags}} +} >From f6f11a5e528856773aa4e5d1aa9b791007919be6 Mon Sep 17 00:00:00 2001 From: Oliver Hunt <oli...@apple.com> Date: Fri, 9 May 2025 23:32:16 -0700 Subject: [PATCH 2/2] --amend --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 16 +++++----------- clang/lib/Sema/SemaType.cpp | 10 ++++------ 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0da30d0d98061..ffc2ef7a06385 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1048,20 +1048,14 @@ def err_ptrauth_extra_discriminator_invalid : Error< // __ptrauth qualifier options string def note_ptrauth_evaluating_options : Note<"options parameter evaluated to '%0'">; -def err_ptrauth_invalid_option : Error<"'%0' options parameter %1">; +def err_ptrauth_invalid_option : Error<"'__ptrauth' options parameter %0">; def err_ptrauth_unknown_authentication_option - : Error<"unknown '%0' authentication option '%1'">; + : Error<"unknown '__ptrauth' authentication option '%0'">; def err_ptrauth_repeated_authentication_option - : Error<"repeated '%0' authentication %select{mode|option}1%select{, prior " - "mode was '%3'| '%2'}1">; -def note_ptrauth_previous_authentication_option - : Note<"previous '%0' authentication %select{mode|option}1">; -def err_ptrauth_unexpected_option_end - : Error<"unexpected end of options parameter for %0">; -def err_ptrauth_option_unexpected_token - : Error<"unexpected character '%0' in '%1' options">; + : 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 '%1' option in '%0' qualifier">; + : Error<"missing comma after '%0' option in '__ptrauth' qualifier">; /// main() // static main() is not an error in C, just in C++. diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index deb894b53455d..6a6e0c055029a 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -8352,7 +8352,6 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T, const ParsedAttr &Attr, Sema &S) { assert((Attr.getNumArgs() > 0 && Attr.getNumArgs() <= 4) && "__ptrauth qualifier takes between 1 and 4 arguments"); - StringRef AttrName = Attr.getAttrName()->getName(); Expr *KeyArg = Attr.getArgAsExpr(0); Expr *IsAddressDiscriminatedArg = Attr.getNumArgs() >= 2 ? Attr.getArgAsExpr(1) : nullptr; @@ -8400,8 +8399,7 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T, }; auto DiagnoseInvalidOptionsParameter = [&](llvm::StringRef Reason) { S.Diag(AuthenticationOptionsRange.getBegin(), - diag::err_ptrauth_invalid_option) - << AttrName << Reason; + diag::err_ptrauth_invalid_option) << Reason; Attr.setInvalid(); IsInvalid = true; ReportEvaluationOfExpressionIfNeeded(); @@ -8460,7 +8458,7 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T, std::is_same_v<decltype(Value), PointerAuthenticationMode>; S.Diag(AuthenticationOptionsRange.getBegin(), diag::err_ptrauth_repeated_authentication_option) - << AttrName << !IsAuthenticationMode << OptionString + << !IsAuthenticationMode << OptionString << (LastOption ? *LastOption : ""); return false; }; @@ -8496,11 +8494,11 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T, StringRef LeadingOption = Option.slice(0, WhitespaceIndex); S.Diag(AuthenticationOptionsRange.getBegin(), diag::err_ptrauth_option_missing_comma) - << AttrName << LeadingOption; + << LeadingOption; } else { S.Diag(AuthenticationOptionsRange.getBegin(), diag::err_ptrauth_unknown_authentication_option) - << AttrName << Option; + << Option; } return false; }); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits