diff --git a/c/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def --- a/c/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -1598,6 +1598,14 @@ LANGBUILTIN(__builtin_coro_suspend, "cIb", "n", COR_LANG) LANGBUILTIN(__builtin_coro_param, "bv*v*", "n", COR_LANG) +// Pointer authentication builtins. +BUILTIN(__builtin_ptrauth_strip, "v*v*i", "tnc") +BUILTIN(__builtin_ptrauth_blend_discriminator, "zv*i", "tnc") +BUILTIN(__builtin_ptrauth_sign_unauthenticated, "v*v*iv*", "tnc") +BUILTIN(__builtin_ptrauth_sign_generic_data, "zv*v*", "tnc") +BUILTIN(__builtin_ptrauth_auth_and_resign, "v*v*iv*iv*", "tn") +BUILTIN(__builtin_ptrauth_auth, "v*v*iv*", "tn") + // OpenCL v2.0 s6.13.16, s9.17.3.5 - Pipe functions. // We need the generic prototype, since the packet type could be anything. LANGBUILTIN(read_pipe, "i.", "tn", OCLC20_LANG) diff --git a/c/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/c/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -797,6 +797,7 @@ def GNUZeroLineDirective : DiagGroup<"gnu-zero-line-directive">; def GNUZeroVariadicMacroArguments : DiagGroup<"gnu-zero-variadic-macro-arguments">; def MisleadingIndentation : DiagGroup<"misleading-indentation">; +def PtrAuthNullPointers : DiagGroup<"ptrauth-null-pointers">; // This covers both the deprecated case (in C++98) // and the extension case (in C++11 onwards). diff --git a/c/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/c/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -833,6 +833,23 @@ " but format string expands to at least %2">, InGroup; +def err_ptrauth_disabled_target : + Error<"this target does not support pointer authentication">; +def err_ptrauth_disabled : + Error<"pointer authentication is disabled for the current target">; +def err_ptrauth_invalid_key : + Error<"%0 does not identify a valid pointer authentication key for " + "the current target">; +def err_ptrauth_value_bad_type : + Error<"%select{signed value|extra discriminator|blended pointer|blended " + "integer}0 must have %select{pointer|integer|pointer or integer}1 " + "type; type here is %2">; +def warn_ptrauth_sign_null_pointer : + Warning<"signing a null pointer will yield a non-null pointer">, + InGroup; +def warn_ptrauth_auth_null_pointer : + Warning<"authenticating a null pointer will almost certainly trap">, + InGroup; /// main() // static main() is not an error in C, just in C++. diff --git a/c/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def --- a/c/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -93,6 +93,7 @@ FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread)) FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow)) FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo)) +FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics) FEATURE(swiftasynccc, PP.getTargetInfo().checkCallingConvention(CC_SwiftAsync) == clang::TargetInfo::CCCR_OK) diff --git a/c/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def --- a/c/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -150,6 +150,7 @@ LANGOPT(Coroutines , 1, 0, "C++20 coroutines") LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods") LANGOPT(RelaxedTemplateTemplateArgs, 1, 1, "C++17 relaxed matching of template template arguments") +LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics") LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") diff --git a/c/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h --- a/c/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -23,6 +23,7 @@ #include "clang/Basic/TargetOptions.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" +#include "llvm/ADT/APSInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/Optional.h" @@ -233,6 +234,8 @@ unsigned ARMCDECoprocMask : 8; + unsigned PointerAuthSupported : 1; + unsigned MaxOpenCLWorkGroupSize; // TargetInfo Constructor. Default initializes all fields. @@ -1362,6 +1365,14 @@ return TLSSupported; } + /// \brief Whether the target supports pointer authentication at all. + /// + /// Whether pointer authentication is actually being used is determined + /// by the language option. + bool isPointerAuthSupported() const { + return PointerAuthSupported; + } + /// Return the maximum alignment (in bits) of a TLS variable /// /// Gets the maximum alignment (in bits) of a TLS variable on this target. @@ -1403,6 +1414,11 @@ const LangASMap &getAddressSpaceMap() const { return *AddrSpaceMap; } + /// Determine whether the given pointer-authentication key is valid. + /// + /// The value has been coerced to type 'int'. + virtual bool validatePointerAuthKey(const llvm::APSInt &value) const; + /// Map from the address space field in builtin description strings to the /// language address space. virtual LangAS getOpenCLBuiltinAddressSpace(unsigned AS) const { diff --git a/c/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/c/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2862,6 +2862,14 @@ " of a non-void function as unreachable">, PosFlag>; +let Group = f_Group in { + let Flags = [CC1Option] in { + def fptrauth_intrinsics : Flag<["-"], "fptrauth-intrinsics">, + HelpText<"Enable pointer-authentication intrinsics">; + } + def fno_ptrauth_intrinsics : Flag<["-"], "fno-ptrauth-intrinsics">; +} + def fenable_matrix : Flag<["-"], "fenable-matrix">, Group, Flags<[CC1Option]>, HelpText<"Enable matrix data type and related builtin functions">, diff --git a/c/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/c/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2669,6 +2669,9 @@ SourceLocation AtomicQualLoc = SourceLocation(), SourceLocation UnalignedQualLoc = SourceLocation()); + void diagnosePointerAuthDisabled(SourceLocation loc, SourceRange range); + bool checkConstantPointerAuthKey(Expr *keyExpr, unsigned &key); + static bool adjustContextForLocalExternDecl(DeclContext *&DC); void DiagnoseFunctionSpecifiers(const DeclSpec &DS); NamedDecl *getShadowedDeclaration(const TypedefNameDecl *D, diff --git a/c/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp --- a/c/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -282,6 +282,10 @@ if (!Requested->Parent && Requested->Name == "_Builtin_stddef_max_align_t") return true; + // Anyone is allowed to use our builtin ptrauth.h and its accompanying module. + if (!Requested->Parent && Requested->Name == "ptrauth") + return true; + return false; } diff --git a/c/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp --- a/c/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -127,6 +127,7 @@ HasRISCVVTypes = false; AllowAMDGPUUnsafeFPAtomics = false; ARMCDECoprocMask = 0; + PointerAuthSupported = false; // Default to no types using fpret. RealTypeUsesObjCFPRet = 0; @@ -813,6 +814,10 @@ return true; } +bool TargetInfo::validatePointerAuthKey(const llvm::APSInt &value) const { + return false; +} + void TargetInfo::CheckFixedPointBits() const { // Check that the number of fractional and integral bits (and maybe sign) can // fit into the bits given for a fixed point type. diff --git a/c/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h --- a/c/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -147,6 +147,8 @@ int getEHDataRegisterNumber(unsigned RegNo) const override; + bool validatePointerAuthKey(const llvm::APSInt &value) const override; + const char *getBFloat16Mangling() const override { return "u6__bf16"; }; bool hasInt128Type() const override; diff --git a/c/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp --- a/c/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -14,6 +14,7 @@ #include "clang/Basic/LangOptions.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/APSInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" @@ -125,6 +126,9 @@ else if (Triple.getOS() == llvm::Triple::UnknownOS) this->MCountName = Opts.EABIVersion == llvm::EABI::GNU ? "\01_mcount" : "mcount"; + + if (Triple.isArm64e()) + PointerAuthSupported = true; } StringRef AArch64TargetInfo::getABI() const { return ABI; } @@ -838,6 +842,11 @@ return -1; } +bool AArch64TargetInfo::validatePointerAuthKey( + const llvm::APSInt &value) const { + return 0 <= value && value <= 3; +} + bool AArch64TargetInfo::hasInt128Type() const { return true; } AArch64leTargetInfo::AArch64leTargetInfo(const llvm::Triple &Triple, diff --git a/c/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/c/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4568,6 +4568,73 @@ case Builtin::BI__iso_volatile_store64: return RValue::get(EmitISOVolatileStore(*this, E)); + case Builtin::BI__builtin_ptrauth_auth: + case Builtin::BI__builtin_ptrauth_auth_and_resign: + case Builtin::BI__builtin_ptrauth_blend_discriminator: + case Builtin::BI__builtin_ptrauth_sign_generic_data: + case Builtin::BI__builtin_ptrauth_sign_unauthenticated: + case Builtin::BI__builtin_ptrauth_strip: { + // Emit the arguments. + SmallVector args; + for (auto argExpr : E->arguments()) + args.push_back(EmitScalarExpr(argExpr)); + + // Cast the value to intptr_t, saving its original type. + llvm::Type *origValueType = args[0]->getType(); + if (origValueType->isPointerTy()) + args[0] = Builder.CreatePtrToInt(args[0], IntPtrTy); + + switch (BuiltinID) { + case Builtin::BI__builtin_ptrauth_auth_and_resign: + if (args[4]->getType()->isPointerTy()) + args[4] = Builder.CreatePtrToInt(args[4], IntPtrTy); + LLVM_FALLTHROUGH; + + case Builtin::BI__builtin_ptrauth_auth: + case Builtin::BI__builtin_ptrauth_sign_unauthenticated: + if (args[2]->getType()->isPointerTy()) + args[2] = Builder.CreatePtrToInt(args[2], IntPtrTy); + break; + + case Builtin::BI__builtin_ptrauth_sign_generic_data: + if (args[1]->getType()->isPointerTy()) + args[1] = Builder.CreatePtrToInt(args[1], IntPtrTy); + break; + + case Builtin::BI__builtin_ptrauth_blend_discriminator: + case Builtin::BI__builtin_ptrauth_strip: + break; + } + + // Call the intrinsic. + auto intrinsicID = [&]() -> unsigned { + switch (BuiltinID) { + case Builtin::BI__builtin_ptrauth_auth: + return llvm::Intrinsic::ptrauth_auth; + case Builtin::BI__builtin_ptrauth_auth_and_resign: + return llvm::Intrinsic::ptrauth_resign; + case Builtin::BI__builtin_ptrauth_blend_discriminator: + return llvm::Intrinsic::ptrauth_blend; + case Builtin::BI__builtin_ptrauth_sign_generic_data: + return llvm::Intrinsic::ptrauth_sign_generic; + case Builtin::BI__builtin_ptrauth_sign_unauthenticated: + return llvm::Intrinsic::ptrauth_sign; + case Builtin::BI__builtin_ptrauth_strip: + return llvm::Intrinsic::ptrauth_strip; + } + llvm_unreachable("bad ptrauth intrinsic"); + }(); + auto intrinsic = CGM.getIntrinsic(intrinsicID); + llvm::Value *result = EmitRuntimeCall(intrinsic, args); + + if (BuiltinID != Builtin::BI__builtin_ptrauth_sign_generic_data && + BuiltinID != Builtin::BI__builtin_ptrauth_blend_discriminator && + origValueType->isPointerTy()) { + result = Builder.CreateIntToPtr(result, origValueType); + } + return RValue::get(result); + } + case Builtin::BI__exception_code: case Builtin::BI_exception_code: return RValue::get(EmitSEHExceptionCode()); diff --git a/c/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/c/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6470,6 +6470,11 @@ if (Args.hasFlag(options::OPT_fcommon, options::OPT_fno_common, false)) CmdArgs.push_back("-fcommon"); + if (Args.hasFlag(options::OPT_fptrauth_intrinsics, + options::OPT_fno_ptrauth_intrinsics, false)) + CmdArgs.push_back("-fptrauth-intrinsics"); + + // -fsigned-bitfields is default, and clang doesn't yet support // -funsigned-bitfields. if (!Args.hasFlag(options::OPT_fsigned_bitfields, diff --git a/c/clang/lib/Driver/ToolChains/Darwin.h b/clang/lib/Driver/ToolChains/Darwin.h --- a/c/clang/lib/Driver/ToolChains/Darwin.h +++ b/clang/lib/Driver/ToolChains/Darwin.h @@ -579,6 +579,10 @@ void addClangWarningOptions(llvm::opt::ArgStringList &CC1Args) const override; + void addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args, + Action::OffloadKind DeviceOffloadKind) const override; + void AddLinkARCArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const override; diff --git a/c/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp --- a/c/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -1061,6 +1061,20 @@ } } +void DarwinClang::addClangTargetOptions( + const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, + Action::OffloadKind DeviceOffloadKind) const{ + + Darwin::addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadKind); + + // On arm64e, enable pointer authentication intrinsics. + if (getTriple().isArm64e()) { + if (!DriverArgs.hasArg(options::OPT_fptrauth_intrinsics, + options::OPT_fno_ptrauth_intrinsics)) + CC1Args.push_back("-fptrauth-intrinsics"); + } +} + /// Take a path that speculatively points into Xcode and return the /// `XCODE/Contents/Developer` path if it is an Xcode path, or an empty path /// otherwise. diff --git a/c/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/c/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3103,6 +3103,18 @@ return Diags.getNumErrors() == NumErrorsBefore; } +static void GeneratePointerAuthArgs(LangOptions &Opts, + SmallVectorImpl &Args, + CompilerInvocation::StringAllocator SA) { + if (Opts.PointerAuthIntrinsics) + GenerateArg(Args, OPT_fptrauth_intrinsics, SA); +} + +static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags) { + Opts.PointerAuthIntrinsics = Args.hasArg(OPT_fptrauth_intrinsics); +} + void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, const llvm::Triple &T, std::vector &Includes, @@ -4464,6 +4476,7 @@ llvm::Triple T(Res.getTargetOpts().Triple); ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, Diags, Res.getFileSystemOpts().WorkingDir); + ParsePointerAuthArgs(LangOpts, Args, Diags); ParseLangArgs(LangOpts, Args, DashX, T, Res.getPreprocessorOpts().Includes, Diags); @@ -4645,6 +4658,7 @@ GenerateFrontendArgs(FrontendOpts, Args, SA, LangOpts->IsHeaderFile); GenerateTargetArgs(*TargetOpts, Args, SA); GenerateHeaderSearchArgs(*HeaderSearchOpts, Args, SA); + GeneratePointerAuthArgs(*LangOpts, Args, SA); GenerateLangArgs(*LangOpts, Args, SA, T, FrontendOpts.DashX); GenerateCodeGenArgs(CodeGenOpts, Args, SA, T, FrontendOpts.OutputFile, &*LangOpts); diff --git a/c/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt --- a/c/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -103,6 +103,7 @@ pconfigintrin.h popcntintrin.h prfchwintrin.h + ptrauth.h ptwriteintrin.h rdseedintrin.h rtmintrin.h diff --git a/c/clang/lib/Headers/module.modulemap b/clang/lib/Headers/module.modulemap --- a/c/clang/lib/Headers/module.modulemap +++ b/clang/lib/Headers/module.modulemap @@ -162,3 +162,8 @@ header "opencl-c.h" header "opencl-c-base.h" } + +module ptrauth { + header "ptrauth.h" + export * +} diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h new file mode 100644 --- /dev/null +++ b/clang/lib/Headers/ptrauth.h @@ -0,0 +1,167 @@ +/*===---- ptrauth.h - Pointer authentication -------------------------------=== + * + * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://llvm.org/LICENSE.txt for license information. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + *===-----------------------------------------------------------------------=== + */ + +#ifndef __PTRAUTH_H +#define __PTRAUTH_H + +typedef enum { + ptrauth_key_asia = 0, + ptrauth_key_asib = 1, + ptrauth_key_asda = 2, + ptrauth_key_asdb = 3, + + /* A process-independent key which can be used to sign code pointers. + Signing and authenticating with this key is a no-op in processes + which disable ABI pointer authentication. */ + ptrauth_key_process_independent_code = ptrauth_key_asia, + + /* A process-specific key which can be used to sign code pointers. + Signing and authenticating with this key is enforced even in processes + which disable ABI pointer authentication. */ + ptrauth_key_process_dependent_code = ptrauth_key_asib, + + /* A process-independent key which can be used to sign data pointers. + Signing and authenticating with this key is a no-op in processes + which disable ABI pointer authentication. */ + ptrauth_key_process_independent_data = ptrauth_key_asda, + + /* A process-specific key which can be used to sign data pointers. + Signing and authenticating with this key is a no-op in processes + which disable ABI pointer authentication. */ + ptrauth_key_process_dependent_data = ptrauth_key_asdb, + +} ptrauth_key; + +/* An integer type of the appropriate size for a discriminator argument. */ +typedef __UINTPTR_TYPE__ ptrauth_extra_data_t; + +/* An integer type of the appropriate size for a generic signature. */ +typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; + +/* A signed pointer value embeds the original pointer together with + a signature that attests to the validity of that pointer. Because + this signature must use only "spare" bits of the pointer, a + signature's validity is probabilistic in practice: it is unlikely + but still plausible that an invalidly-derived signature will + somehow equal the correct signature and therefore successfully + authenticate. Nonetheless, this scheme provides a strong degree + of protection against certain kinds of attacks. */ + +/* Authenticating a pointer that was not signed with the given key + and extra-data value will (likely) fail by trapping. */ + +#if __has_feature(ptrauth_intrinsics) + +/* Strip the signature from a value without authenticating it. + + If the value is a function pointer, the result will not be a + legal function pointer because of the missing signature, and + attempting to call it will result in an authentication failure. + + The value must be an expression of pointer type. + The key must be a constant expression of type ptrauth_key. + The result will have the same type as the original value. */ +#define ptrauth_strip(__value, __key) \ + __builtin_ptrauth_strip(__value, __key) + +/* Blend a constant discriminator into the given pointer-like value + to form a new discriminator. Not all bits of the inputs are + guaranteed to contribute to the result. + + On arm64e, the integer must fall within the range of a uint16_t; + other bits may be ignored. + + The first argument must be an expression of pointer type. + The second argument must be an expression of integer type. + The result will have type uintptr_t. */ +#define ptrauth_blend_discriminator(__pointer, __integer) \ + __builtin_ptrauth_blend_discriminator(__pointer, __integer) + +/* Add a signature to the given pointer value using a specific key, + using the given extra data as a salt to the signing process. + + This operation does not authenticate the original value and is + therefore potentially insecure if an attacker could possibly + control that value. + + The value must be an expression of pointer type. + The key must be a constant expression of type ptrauth_key. + The extra data must be an expression of pointer or integer type; + if an integer, it will be coerced to ptrauth_extra_data_t. + The result will have the same type as the original value. */ +#define ptrauth_sign_unauthenticated(__value, __key, __data) \ + __builtin_ptrauth_sign_unauthenticated(__value, __key, __data) + +/* Authenticate a pointer using one scheme and resign it using another. + + If the result is subsequently authenticated using the new scheme, that + authentication is guaranteed to fail if and only if the initial + authentication failed. + + The value must be an expression of pointer type. + The key must be a constant expression of type ptrauth_key. + The extra data must be an expression of pointer or integer type; + if an integer, it will be coerced to ptrauth_extra_data_t. + The result will have the same type as the original value. + + This operation is guaranteed to not leave the intermediate value + available for attack before it is re-signed. + + Do not pass a null pointer to this function. A null pointer + will not successfully authenticate. */ +#define ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, __new_data) \ + __builtin_ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, __new_data) + +/* Authenticate a data pointer. + + The value must be an expression of non-function pointer type. + The key must be a constant expression of type ptrauth_key. + The extra data must be an expression of pointer or integer type; + if an integer, it will be coerced to ptrauth_extra_data_t. + The result will have the same type as the original value. + + If the authentication fails, dereferencing the resulting pointer + will fail. */ +#define ptrauth_auth_data(__value, __old_key, __old_data) \ + __builtin_ptrauth_auth(__value, __old_key, __old_data) + +/* Compute a signature for the given pair of pointer-sized values. + The order of the arguments is significant. + + Like a pointer signature, the resulting signature depends on + private key data and therefore should not be reliably reproducible + by attackers. That means that this can be used to validate the + integrity of arbitrary data by storing a signature for that data + alongside it, then checking that the signature is still valid later. + Data which exceeds two pointers in size can be signed by either + computing a tree of generic signatures or just signing an ordinary + cryptographic hash of the data. + + The result has type ptrauth_generic_signature_t. However, it may + not have as many bits of entropy as that type's width would suggest; + some implementations are known to compute a compressed signature as + if the arguments were a pointer and a discriminator. + + The arguments must be either pointers or integers; if integers, they + will be coerce to uintptr_t. */ +#define ptrauth_sign_generic_data(__value, __data) \ + __builtin_ptrauth_sign_generic_data(__value, __data) + +#else + +#define ptrauth_strip(__value, __key) ({(void)__key; __value;}) +#define ptrauth_blend_discriminator(__pointer, __integer) ({(void)__pointer; (void)__integer; ((ptrauth_extra_data_t)0);}) +#define ptrauth_sign_unauthenticated(__value, __key, __data) ({(void)__key; (void)__data; __value;}) +#define ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, __new_data) ({(void)__old_key; (void)__old_data; (void)__new_key; (void)__new_data; __value;}) +#define ptrauth_auth_data(__value, __old_key, __old_data) ({(void)__old_key;(void)__old_data;__value;}) +#define ptrauth_sign_generic_data(__value, __data) ({(void)__value;(void)__data;((ptrauth_generic_signature_t)0);}) + +#endif /* __has_feature(ptrauth_intrinsics) */ + +#endif /* __PTRAUTH_H */ diff --git a/c/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/c/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -129,6 +129,20 @@ << call->getArg(1)->getSourceRange(); } +static bool convertArgumentToType(Sema &S, Expr *&Value, QualType Ty) { + if (Value->isTypeDependent()) + return false; + + InitializedEntity Entity = + InitializedEntity::InitializeParameter(S.Context, Ty, false); + ExprResult Result = + S.PerformCopyInitialization(Entity, SourceLocation(), Value); + if (Result.isInvalid()) + return true; + Value = Result.get(); + return false; +} + /// Check that the first argument to __builtin_annotation is an integer /// and the second argument is a non-wide string literal. static bool SemaBuiltinAnnotation(Sema &S, CallExpr *TheCall) { @@ -1329,6 +1343,184 @@ return false; } +namespace { + enum PointerAuthOpKind { + PAO_Strip, PAO_Sign, PAO_Auth, PAO_SignGeneric, PAO_Discriminator, + PAO_BlendPointer, PAO_BlendInteger + }; +} + +static bool checkPointerAuthEnabled(Sema &S, Expr *E) { + if (S.getLangOpts().PointerAuthIntrinsics) + return false; + + S.diagnosePointerAuthDisabled(E->getExprLoc(), E->getSourceRange()); + return true; +} + +void Sema::diagnosePointerAuthDisabled(SourceLocation loc, SourceRange range) { + if (!Context.getTargetInfo().isPointerAuthSupported()) { + Diag(loc, diag::err_ptrauth_disabled_target) << range; + } else { + Diag(loc, diag::err_ptrauth_disabled) << range; + } +} + +static bool checkPointerAuthKey(Sema &S, Expr *&Arg) { + // Convert it to type 'int'. + if (convertArgumentToType(S, Arg, S.Context.IntTy)) + return true; + + // Value-dependent expressions are okay; wait for template instantiation. + if (Arg->isValueDependent()) + return false; + + unsigned KeyValue; + return S.checkConstantPointerAuthKey(Arg, KeyValue); +} + +bool Sema::checkConstantPointerAuthKey(Expr *Arg, unsigned &Result) { + // Attempt to constant-evaluate the expression. + Optional KeyValue = Arg->getIntegerConstantExpr(Context); + if (!KeyValue) { + Diag(Arg->getExprLoc(), diag::err_expr_not_ice) << 0 + << Arg->getSourceRange(); + return true; + } + + // Ask the target to validate the key parameter. + if (!Context.getTargetInfo().validatePointerAuthKey(*KeyValue)) { + llvm::SmallString<32> Value; { + llvm::raw_svector_ostream Str(Value); + Str << *KeyValue; + } + + Diag(Arg->getExprLoc(), diag::err_ptrauth_invalid_key) + << Value << Arg->getSourceRange(); + return true; + } + + Result = KeyValue->getZExtValue(); + return false; +} + +static bool checkPointerAuthValue(Sema &S, Expr *&Arg, + PointerAuthOpKind OpKind) { + if (Arg->hasPlaceholderType()) { + ExprResult R = S.CheckPlaceholderExpr(Arg); + if (R.isInvalid()) return true; + Arg = R.get(); + } + + auto allowsPointer = [](PointerAuthOpKind OpKind) { + return OpKind != PAO_BlendInteger; + }; + auto allowsInteger = [](PointerAuthOpKind OpKind) { + return OpKind == PAO_Discriminator || + OpKind == PAO_BlendInteger || + OpKind == PAO_SignGeneric; + }; + + // Require the value to have the right range of type. + QualType ExpectedTy; + if (allowsPointer(OpKind) && Arg->getType()->isPointerType()) { + ExpectedTy = Arg->getType().getUnqualifiedType(); + } else if (allowsPointer(OpKind) && Arg->getType()->isNullPtrType()) { + ExpectedTy = S.Context.VoidPtrTy; + } else if (allowsInteger(OpKind) && + Arg->getType()->isIntegralOrUnscopedEnumerationType()) { + ExpectedTy = S.Context.getUIntPtrType(); + + // Diagnose the failures. + } else { + S.Diag(Arg->getExprLoc(), diag::err_ptrauth_value_bad_type) + << unsigned(OpKind == PAO_Discriminator ? 1 : + OpKind == PAO_BlendPointer ? 2 : + OpKind == PAO_BlendInteger ? 3 : 0) + << unsigned(allowsInteger(OpKind) ? + (allowsPointer(OpKind) ? 2 : 1) : 0) + << Arg->getType() + << Arg->getSourceRange(); + return true; + } + + // Convert to that type. This should just be an lvalue-to-rvalue + // conversion. + if (convertArgumentToType(S, Arg, ExpectedTy)) + return true; + + // Warn about null pointers for non-generic sign and auth operations. + if ((OpKind == PAO_Sign || OpKind == PAO_Auth) && + Arg->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNull)) { + S.Diag(Arg->getExprLoc(), + OpKind == PAO_Sign ? diag::warn_ptrauth_sign_null_pointer + : diag::warn_ptrauth_auth_null_pointer) + << Arg->getSourceRange(); + } + + return false; +} + +static ExprResult SemaPointerAuthStrip(Sema &S, CallExpr *Call) { + if (checkArgCount(S, Call, 2)) return ExprError(); + if (checkPointerAuthEnabled(S, Call)) return ExprError(); + if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Strip) | + checkPointerAuthKey(S, Call->getArgs()[1])) + return ExprError(); + + Call->setType(Call->getArgs()[0]->getType()); + return Call; +} + +static ExprResult SemaPointerAuthBlendDiscriminator(Sema &S, CallExpr *Call) { + if (checkArgCount(S, Call, 2)) return ExprError(); + if (checkPointerAuthEnabled(S, Call)) return ExprError(); + if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_BlendPointer) | + checkPointerAuthValue(S, Call->getArgs()[1], PAO_BlendInteger)) + return ExprError(); + + Call->setType(S.Context.getUIntPtrType()); + return Call; +} + +static ExprResult SemaPointerAuthSignGenericData(Sema &S, CallExpr *Call) { + if (checkArgCount(S, Call, 2)) return ExprError(); + if (checkPointerAuthEnabled(S, Call)) return ExprError(); + if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_SignGeneric) | + checkPointerAuthValue(S, Call->getArgs()[1], PAO_Discriminator)) + return ExprError(); + + Call->setType(S.Context.getUIntPtrType()); + return Call; +} + +static ExprResult SemaPointerAuthSignOrAuth(Sema &S, CallExpr *Call, + PointerAuthOpKind OpKind) { + if (checkArgCount(S, Call, 3)) return ExprError(); + if (checkPointerAuthEnabled(S, Call)) return ExprError(); + if (checkPointerAuthValue(S, Call->getArgs()[0], OpKind) | + checkPointerAuthKey(S, Call->getArgs()[1]) | + checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator)) + return ExprError(); + + Call->setType(Call->getArgs()[0]->getType()); + return Call; +} + +static ExprResult SemaPointerAuthAuthAndResign(Sema &S, CallExpr *Call) { + if (checkArgCount(S, Call, 5)) return ExprError(); + if (checkPointerAuthEnabled(S, Call)) return ExprError(); + if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Auth) | + checkPointerAuthKey(S, Call->getArgs()[1]) | + checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator) | + checkPointerAuthKey(S, Call->getArgs()[3]) | + checkPointerAuthValue(S, Call->getArgs()[4], PAO_Discriminator)) + return ExprError(); + + Call->setType(Call->getArgs()[0]->getType()); + return Call; +} + static ExprResult SemaBuiltinLaunder(Sema &S, CallExpr *TheCall) { if (checkArgCount(S, TheCall, 1)) return ExprError(); @@ -1890,6 +2082,18 @@ TheCall->setType(Context.VoidPtrTy); break; + case Builtin::BI__builtin_ptrauth_strip: + return SemaPointerAuthStrip(*this, TheCall); + case Builtin::BI__builtin_ptrauth_blend_discriminator: + return SemaPointerAuthBlendDiscriminator(*this, TheCall); + case Builtin::BI__builtin_ptrauth_sign_unauthenticated: + return SemaPointerAuthSignOrAuth(*this, TheCall, PAO_Sign); + case Builtin::BI__builtin_ptrauth_auth: + return SemaPointerAuthSignOrAuth(*this, TheCall, PAO_Auth); + case Builtin::BI__builtin_ptrauth_sign_generic_data: + return SemaPointerAuthSignGenericData(*this, TheCall); + case Builtin::BI__builtin_ptrauth_auth_and_resign: + return SemaPointerAuthAuthAndResign(*this, TheCall); // OpenCL v2.0, s6.13.16 - Pipe functions case Builtin::BIread_pipe: case Builtin::BIwrite_pipe: diff --git a/clang/test/CodeGen/ptrauth-intrinsics.c b/clang/test/CodeGen/ptrauth-intrinsics.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-intrinsics.c @@ -0,0 +1,73 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s + +void (*fnptr)(void); +long int_discriminator; +void *ptr_discriminator; +long signature; + +// CHECK-LABEL: define{{.*}} void @test_auth() +void test_auth() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[DISC0:%.*]] = load i8*, i8** @ptr_discriminator, + // CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()* [[PTR]] to i64 + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint i8* [[DISC0]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T0]], i32 0, i64 [[DISC]]) + // CHECK-NEXT: [[RESULT:%.*]] = inttoptr i64 [[T1]] to void ()* + // CHECK-NEXT: store void ()* [[RESULT]], void ()** @fnptr, + fnptr = __builtin_ptrauth_auth(fnptr, 0, ptr_discriminator); +} + +// CHECK-LABEL: define{{.*}} void @test_strip() +void test_strip() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()* [[PTR]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[T0]], i32 0) + // CHECK-NEXT: [[RESULT:%.*]] = inttoptr i64 [[T1]] to void ()* + // CHECK-NEXT: store void ()* [[RESULT]], void ()** @fnptr, + fnptr = __builtin_ptrauth_strip(fnptr, 0); +} + +// CHECK-LABEL: define{{.*}} void @test_sign_unauthenticated() +void test_sign_unauthenticated() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[DISC0:%.*]] = load i8*, i8** @ptr_discriminator, + // CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()* [[PTR]] to i64 + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint i8* [[DISC0]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[T0]], i32 0, i64 [[DISC]]) + // CHECK-NEXT: [[RESULT:%.*]] = inttoptr i64 [[T1]] to void ()* + // CHECK-NEXT: store void ()* [[RESULT]], void ()** @fnptr, + fnptr = __builtin_ptrauth_sign_unauthenticated(fnptr, 0, ptr_discriminator); +} + +// CHECK-LABEL: define{{.*}} void @test_auth_and_resign() +void test_auth_and_resign() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[DISC0:%.*]] = load i8*, i8** @ptr_discriminator, + // CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()* [[PTR]] to i64 + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint i8* [[DISC0]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 [[DISC]], i32 3, i64 15) + // CHECK-NEXT: [[RESULT:%.*]] = inttoptr i64 [[T1]] to void ()* + // CHECK-NEXT: store void ()* [[RESULT]], void ()** @fnptr, + fnptr = __builtin_ptrauth_auth_and_resign(fnptr, 0, ptr_discriminator, 3, 15); +} + +// CHECK-LABEL: define{{.*}} void @test_blend_discriminator() +void test_blend_discriminator() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[DISC:%.*]] = load i64, i64* @int_discriminator, + // CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()* [[PTR]] to i64 + // CHECK-NEXT: [[RESULT:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 [[DISC]]) + // CHECK-NEXT: store i64 [[RESULT]], i64* @int_discriminator, + int_discriminator = __builtin_ptrauth_blend_discriminator(fnptr, int_discriminator); +} + +// CHECK-LABEL: define{{.*}} void @test_sign_generic_data() +void test_sign_generic_data() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[DISC0:%.*]] = load i8*, i8** @ptr_discriminator, + // CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()* [[PTR]] to i64 + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint i8* [[DISC0]] to i64 + // CHECK-NEXT: [[RESULT:%.*]] = call i64 @llvm.ptrauth.sign.generic(i64 [[T0]], i64 [[DISC]]) + // CHECK-NEXT: store i64 [[RESULT]], i64* @signature, + signature = __builtin_ptrauth_sign_generic_data(fnptr, ptr_discriminator); +} diff --git a/clang/test/Driver/arch-arm64e.c b/clang/test/Driver/arch-arm64e.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/arch-arm64e.c @@ -0,0 +1,22 @@ +// Check that we can manually enable specific ptrauth features. + +// RUN: %clang -target arm64-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix NONE +// NONE: "-cc1" +// NONE-NOT: "-fptrauth-intrinsics" + +// RUN: %clang -target arm64-apple-ios -fptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix INTRIN +// INTRIN: "-cc1"{{.*}} {{.*}} "-fptrauth-intrinsics" + + +// Check the arm64e defaults. + +// RUN: %clang -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT +// RUN: %clang -mkernel -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-KERN +// RUN: %clang -fapple-kext -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-KERN +// DEFAULT: "-fptrauth-intrinsics" "-target-cpu" "apple-a12"{{.*}} +// DEFAULT-KERN: "-fptrauth-intrinsics" "-target-cpu" "apple-a12"{{.*}} + +// RUN: %clang -target arm64e-apple-ios -fno-ptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix NOINTRIN + +// NOINTRIN-NOT: "-fptrauth-intrinsics" +// NOINTRIN: "-target-cpu" "apple-a12"{{.*}} diff --git a/clang/test/Modules/Inputs/ptrauth-include-from-darwin/module.modulemap b/clang/test/Modules/Inputs/ptrauth-include-from-darwin/module.modulemap new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/ptrauth-include-from-darwin/module.modulemap @@ -0,0 +1,8 @@ +module libc [no_undeclared_includes] { + module stddef { header "stddef.h" export * } +} + +module ptrauth { + header "ptrauth.h" + export * +} diff --git a/clang/test/Modules/Inputs/ptrauth-include-from-darwin/ptrauth.h b/clang/test/Modules/Inputs/ptrauth-include-from-darwin/ptrauth.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/ptrauth-include-from-darwin/ptrauth.h @@ -0,0 +1 @@ +void foo(); diff --git a/clang/test/Modules/Inputs/ptrauth-include-from-darwin/stddef.h b/clang/test/Modules/Inputs/ptrauth-include-from-darwin/stddef.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/ptrauth-include-from-darwin/stddef.h @@ -0,0 +1 @@ +@import ptrauth; diff --git a/clang/test/Modules/ptrauth-include-from-darwin.m b/clang/test/Modules/ptrauth-include-from-darwin.m new file mode 100644 --- /dev/null +++ b/clang/test/Modules/ptrauth-include-from-darwin.m @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs/ptrauth-include-from-darwin %s -verify +// expected-no-diagnostics + +@import libc; +void bar() { foo(); } diff --git a/clang/test/Preprocessor/ptrauth_feature.c b/clang/test/Preprocessor/ptrauth_feature.c new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/ptrauth_feature.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 %s -E -triple=arm64-- | FileCheck %s --check-prefixes=NOINTRIN +// RUN: %clang_cc1 %s -E -triple=arm64-- -fptrauth-intrinsics | FileCheck %s --check-prefixes=INTRIN + +#if __has_feature(ptrauth_intrinsics) +// INTRIN: has_ptrauth_intrinsics +void has_ptrauth_intrinsics() {} +#else +// NOINTRIN: no_ptrauth_intrinsics +void no_ptrauth_intrinsics() {} +#endif diff --git a/clang/test/Sema/ptrauth-intrinsics-macro.c b/clang/test/Sema/ptrauth-intrinsics-macro.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/ptrauth-intrinsics-macro.c @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -Wall -fsyntax-only -verify -fptrauth-intrinsics %s +// RUN: %clang_cc1 -triple arm64-apple-ios -Wall -fsyntax-only -verify %s + +// expected-no-diagnostics + +#include + +#define VALID_CODE_KEY 0 +#define VALID_DATA_KEY 2 + +extern int dv; + +void test(int *dp, int value) { + dp = ptrauth_strip(dp, VALID_DATA_KEY); + ptrauth_extra_data_t t0 = ptrauth_blend_discriminator(dp, value); + (void)t0; + dp = ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, 0); + dp = ptrauth_auth_and_resign(dp, VALID_DATA_KEY, dp, VALID_DATA_KEY, dp); + dp = ptrauth_auth_data(dp, VALID_DATA_KEY, 0); + int pu0 = 0, pu1 = 0, pu2 = 0, pu3 = 0, pu4 = 0, pu5 = 0, pu6 = 0, pu7 = 0; + ptrauth_blend_discriminator(&pu0, value); + ptrauth_auth_and_resign(&pu1, VALID_DATA_KEY, dp, VALID_DATA_KEY, dp); + ptrauth_auth_and_resign(dp, VALID_DATA_KEY, &pu2, VALID_DATA_KEY, dp); + ptrauth_auth_and_resign(dp, VALID_DATA_KEY, dp, VALID_DATA_KEY, &pu3); + ptrauth_sign_generic_data(pu4, dp); + ptrauth_sign_generic_data(dp, pu5); + ptrauth_auth_data(&pu6, VALID_DATA_KEY, value); + ptrauth_auth_data(dp, VALID_DATA_KEY, pu7); + + + + int t2 = ptrauth_sign_generic_data(dp, 0); + (void)t2; +} diff --git a/clang/test/Sema/ptrauth.c b/clang/test/Sema/ptrauth.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/ptrauth.c @@ -0,0 +1,126 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify -fptrauth-intrinsics %s + +#if __has_feature(ptrauth_intrinsics) +#warning Pointer authentication enabled! +// expected-warning@-1 {{Pointer authentication enabled!}} +#endif + +#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 + +#define NULL ((void*) 0) +struct A { int x; } mismatched_type; + +extern int dv; +extern int fv(int); + +void test_strip(int *dp, int (*fp)(int)) { + __builtin_ptrauth_strip(dp); // expected-error {{too few arguments}} + __builtin_ptrauth_strip(dp, VALID_DATA_KEY, dp); // expected-error {{too many arguments}} + (void) __builtin_ptrauth_strip(NULL, VALID_DATA_KEY); // no warning + + __builtin_ptrauth_strip(mismatched_type, VALID_DATA_KEY); // expected-error {{signed value must have pointer type; type here is 'struct A'}} + __builtin_ptrauth_strip(dp, mismatched_type); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}} + + int *dr = __builtin_ptrauth_strip(dp, VALID_DATA_KEY); + dr = __builtin_ptrauth_strip(dp, INVALID_KEY); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + int (*fr)(int) = __builtin_ptrauth_strip(fp, VALID_CODE_KEY); + fr = __builtin_ptrauth_strip(fp, INVALID_KEY); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + float *mismatch = __builtin_ptrauth_strip(dp, VALID_DATA_KEY); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}} +} + +void test_blend_discriminator(int *dp, int (*fp)(int), int value) { + __builtin_ptrauth_blend_discriminator(dp); // expected-error {{too few arguments}} + __builtin_ptrauth_blend_discriminator(dp, dp, dp); // expected-error {{too many arguments}} + (void) __builtin_ptrauth_blend_discriminator(dp, value); // no warning + + __builtin_ptrauth_blend_discriminator(mismatched_type, value); // expected-error {{blended pointer must have pointer type; type here is 'struct A'}} + __builtin_ptrauth_blend_discriminator(dp, mismatched_type); // expected-error {{blended integer must have integer type; type here is 'struct A'}} + + float *mismatch = __builtin_ptrauth_blend_discriminator(dp, value); // expected-warning {{incompatible integer to pointer conversion initializing 'float *' with an expression of type}} +} + +void test_sign_unauthenticated(int *dp, int (*fp)(int)) { + __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY); // expected-error {{too few arguments}} + __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, dp, dp); // expected-error {{too many arguments}} + + __builtin_ptrauth_sign_unauthenticated(mismatched_type, VALID_DATA_KEY, 0); // expected-error {{signed value must have pointer type; type here is 'struct A'}} + __builtin_ptrauth_sign_unauthenticated(dp, mismatched_type, 0); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}} + __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}} + + (void) __builtin_ptrauth_sign_unauthenticated(NULL, VALID_DATA_KEY, 0); // expected-warning {{signing a null pointer will yield a non-null pointer}} + + int *dr = __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, 0); + dr = __builtin_ptrauth_sign_unauthenticated(dp, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + int (*fr)(int) = __builtin_ptrauth_sign_unauthenticated(fp, VALID_CODE_KEY, 0); + fr = __builtin_ptrauth_sign_unauthenticated(fp, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + float *mismatch = __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, 0); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}} +} + +void test_auth(int *dp, int (*fp)(int)) { + __builtin_ptrauth_auth(dp, VALID_DATA_KEY); // expected-error {{too few arguments}} + __builtin_ptrauth_auth(dp, VALID_DATA_KEY, dp, dp); // expected-error {{too many arguments}} + + __builtin_ptrauth_auth(mismatched_type, VALID_DATA_KEY, 0); // expected-error {{signed value must have pointer type; type here is 'struct A'}} + __builtin_ptrauth_auth(dp, mismatched_type, 0); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}} + __builtin_ptrauth_auth(dp, VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}} + + (void) __builtin_ptrauth_auth(NULL, VALID_DATA_KEY, 0); // expected-warning {{authenticating a null pointer will almost certainly trap}} + + int *dr = __builtin_ptrauth_auth(dp, VALID_DATA_KEY, 0); + dr = __builtin_ptrauth_auth(dp, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + int (*fr)(int) = __builtin_ptrauth_auth(fp, VALID_CODE_KEY, 0); + fr = __builtin_ptrauth_auth(fp, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + float *mismatch = __builtin_ptrauth_auth(dp, VALID_DATA_KEY, 0); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}} +} + +void test_auth_and_resign(int *dp, int (*fp)(int)) { + __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, VALID_DATA_KEY); // expected-error {{too few arguments}} + __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, dp, VALID_DATA_KEY, dp, 0); // expected-error {{too many arguments}} + + __builtin_ptrauth_auth_and_resign(mismatched_type, VALID_DATA_KEY, 0, VALID_DATA_KEY, dp); // expected-error {{signed value must have pointer type; type here is 'struct A'}} + __builtin_ptrauth_auth_and_resign(dp, mismatched_type, 0, VALID_DATA_KEY, dp); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}} + __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, mismatched_type, VALID_DATA_KEY, dp); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}} + __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, mismatched_type, dp); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}} + __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}} + + (void) __builtin_ptrauth_auth_and_resign(NULL, VALID_DATA_KEY, 0, VALID_DATA_KEY, dp); // expected-warning {{authenticating a null pointer will almost certainly trap}} + + int *dr = __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, VALID_DATA_KEY, dp); + dr = __builtin_ptrauth_auth_and_resign(dp, INVALID_KEY, 0, VALID_DATA_KEY, dp); // expected-error {{does not identify a valid pointer authentication key for the current target}} + dr = __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, INVALID_KEY, dp); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + int (*fr)(int) = __builtin_ptrauth_auth_and_resign(fp, VALID_CODE_KEY, 0, VALID_CODE_KEY, dp); + fr = __builtin_ptrauth_auth_and_resign(fp, INVALID_KEY, 0, VALID_CODE_KEY, dp); // expected-error {{does not identify a valid pointer authentication key for the current target}} + fr = __builtin_ptrauth_auth_and_resign(fp, VALID_CODE_KEY, 0, INVALID_KEY, dp); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + float *mismatch = __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, VALID_DATA_KEY, dp); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}} +} + +void test_sign_generic_data(int *dp) { + __builtin_ptrauth_sign_generic_data(dp); // expected-error {{too few arguments}} + __builtin_ptrauth_sign_generic_data(dp, 0, 0); // expected-error {{too many arguments}} + + __builtin_ptrauth_sign_generic_data(mismatched_type, 0); // expected-error {{signed value must have pointer or integer type; type here is 'struct A'}} + __builtin_ptrauth_sign_generic_data(dp, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}} + + (void) __builtin_ptrauth_sign_generic_data(NULL, 0); // no warning + + unsigned long dr = __builtin_ptrauth_sign_generic_data(dp, 0); + dr = __builtin_ptrauth_sign_generic_data(dp, &dv); + dr = __builtin_ptrauth_sign_generic_data(12314, 0); + dr = __builtin_ptrauth_sign_generic_data(12314, &dv); + + int *mismatch = __builtin_ptrauth_sign_generic_data(dp, 0); // expected-warning {{incompatible integer to pointer conversion initializing 'int *' with an expression of type}} +}