https://github.com/ahatanak updated https://github.com/llvm/llvm-project/pull/96992
>From cf22a4be007f7e6fdc6e4c17c1f32fa70440b123 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahata...@gmail.com> Date: Wed, 26 Jun 2024 13:02:31 -0700 Subject: [PATCH 1/2] [clang] Implement function pointer type discrimination Give users an option to sign a function pointer using a non-zero discrimiantor based on the type of the destination. Co-authored-by: John McCall <rjmcc...@apple.com> --- clang/include/clang/AST/ASTContext.h | 3 + clang/include/clang/AST/Type.h | 3 + clang/include/clang/Basic/Features.def | 1 + clang/include/clang/Basic/LangOptions.def | 3 + clang/include/clang/CodeGen/CodeGenABITypes.h | 4 + clang/include/clang/Driver/Options.td | 2 + clang/lib/AST/ASTContext.cpp | 263 ++++++++++++++++++ clang/lib/CodeGen/CGExprConstant.cpp | 5 + clang/lib/CodeGen/CGPointerAuth.cpp | 68 ++++- clang/lib/CodeGen/CodeGenModule.h | 4 + clang/lib/Driver/ToolChains/Clang.cpp | 3 + clang/lib/Frontend/CompilerInvocation.cpp | 11 +- .../ptrauth-function-type-discriminator.c | 137 +++++++++ clang/test/Preprocessor/ptrauth_feature.c | 34 ++- 14 files changed, 528 insertions(+), 13 deletions(-) create mode 100644 clang/test/CodeGen/ptrauth-function-type-discriminator.c diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index a99f2dc6eb3f2..8ba0c943a9c86 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1283,6 +1283,9 @@ class ASTContext : public RefCountedBase<ASTContext> { uint16_t getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD); + /// Return the "other" type-specific discriminator for the given type. + uint16_t getPointerAuthTypeDiscriminator(QualType T); + /// Apply Objective-C protocol qualifiers to the given type. /// \param allowOnPointerType specifies if we can apply protocol /// qualifiers on ObjCObjectPointerType. It can be set to true when diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index a98899f7f4222..3aa0f05b0ab60 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2506,6 +2506,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isFunctionNoProtoType() const { return getAs<FunctionNoProtoType>(); } bool isFunctionProtoType() const { return getAs<FunctionProtoType>(); } bool isPointerType() const; + bool isSignableType() const; bool isAnyPointerType() const; // Any C pointer or ObjC object pointer bool isCountAttributedType() const; bool isBlockPointerType() const; @@ -8001,6 +8002,8 @@ inline bool Type::isAnyPointerType() const { return isPointerType() || isObjCObjectPointerType(); } +inline bool Type::isSignableType() const { return isPointerType(); } + inline bool Type::isBlockPointerType() const { return isa<BlockPointerType>(CanonicalType); } diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 53f410d3cb4bd..3c9d0364880c5 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -110,6 +110,7 @@ FEATURE(ptrauth_vtable_pointer_address_discrimination, LangOpts.PointerAuthVTPtr FEATURE(ptrauth_vtable_pointer_type_discrimination, LangOpts.PointerAuthVTPtrTypeDiscrimination) FEATURE(ptrauth_member_function_pointer_type_discrimination, LangOpts.PointerAuthCalls) FEATURE(ptrauth_init_fini, LangOpts.PointerAuthInitFini) +FEATURE(ptrauth_function_pointer_type_discrimination, LangOpts.FunctionPointerTypeDiscrimination) EXTENSION(swiftcc, PP.getTargetInfo().checkCallingConvention(CC_Swift) == clang::TargetInfo::CCCR_OK) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 6dd6b5614f44c..554f8643812b5 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -470,6 +470,9 @@ ENUM_LANGOPT(StrictFlexArraysLevel, StrictFlexArraysLevelKind, 2, COMPATIBLE_VALUE_LANGOPT(MaxTokens, 32, 0, "Max number of tokens per TU or 0") +BENIGN_LANGOPT(FunctionPointerTypeDiscrimination, 1, 0, + "Use type discrimination when signing function pointers") + ENUM_LANGOPT(SignReturnAddressScope, SignReturnAddressScopeKind, 2, SignReturnAddressScopeKind::None, "Scope of return address signing") ENUM_LANGOPT(SignReturnAddressKey, SignReturnAddressKeyKind, 1, SignReturnAddressKeyKind::AKey, diff --git a/clang/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h index d4822dc160082..9cbc5a8a2a3f4 100644 --- a/clang/include/clang/CodeGen/CodeGenABITypes.h +++ b/clang/include/clang/CodeGen/CodeGenABITypes.h @@ -108,6 +108,10 @@ unsigned getLLVMFieldNumber(CodeGenModule &CGM, /// Return a declaration discriminator for the given global decl. uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD); +/// Return a type discriminator for the given function type. +uint16_t getPointerAuthTypeDiscriminator(CodeGenModule &CGM, + QualType FunctionType); + /// Given the language and code-generation options that Clang was configured /// with, set the default LLVM IR attributes for a function definition. /// The attributes set here are mostly global target-configuration and diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index dd55838dcf384..a6f960e42d23f 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4228,6 +4228,8 @@ defm ptrauth_vtable_pointer_address_discrimination : defm ptrauth_vtable_pointer_type_discrimination : OptInCC1FFlag<"ptrauth-vtable-pointer-type-discrimination", "Enable type discrimination of vtable pointers">; defm ptrauth_init_fini : OptInCC1FFlag<"ptrauth-init-fini", "Enable signing of function pointers in init/fini arrays">; +defm ptrauth_function_pointer_type_discrimination : OptInCC1FFlag<"ptrauth-function-pointer-type-discrimination", + "Enabling type discrimination on C function pointers">; } def fenable_matrix : Flag<["-"], "fenable-matrix">, Group<f_Group>, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 84deaf5429df7..74e0ae0a58e9f 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3140,6 +3140,269 @@ ASTContext::getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD) { return llvm::getPointerAuthStableSipHash(Str); } +/// Encode a function type for use in the discriminator of a function pointer +/// type. We can't use the itanium scheme for this since C has quite permissive +/// rules for type compatibility that we need to be compatible with. +/// +/// Formally, this function associates every function pointer type T with an +/// encoded string E(T). Let the equivalence relation T1 ~ T2 be defined as +/// E(T1) == E(T2). E(T) is part of the ABI of values of type T. C type +/// compatibility requires equivalent treatment under the ABI, so +/// CCompatible(T1, T2) must imply E(T1) == E(T2), that is, CCompatible must be +/// a subset of ~. Crucially, however, it must be a proper subset because +/// CCompatible is not an equivalence relation: for example, int[] is compatible +/// with both int[1] and int[2], but the latter are not compatible with each +/// other. Therefore this encoding function must be careful to only distinguish +/// types if there is no third type with which they are both required to be +/// compatible. +static void encodeTypeForFunctionPointerAuth(ASTContext &Ctx, raw_ostream &OS, + QualType QT) { + // FIXME: Consider address space qualifiers. + const Type *T = QT.getCanonicalType().getTypePtr(); + + // FIXME: Consider using the C++ type mangling when we encounter a construct + // that is incompatible with C. + + switch (T->getTypeClass()) { + case Type::Atomic: + return encodeTypeForFunctionPointerAuth( + Ctx, OS, cast<AtomicType>(T)->getValueType()); + + case Type::LValueReference: + OS << "R"; + encodeTypeForFunctionPointerAuth(Ctx, OS, + cast<ReferenceType>(T)->getPointeeType()); + return; + case Type::RValueReference: + OS << "O"; + encodeTypeForFunctionPointerAuth(Ctx, OS, + cast<ReferenceType>(T)->getPointeeType()); + return; + + case Type::Pointer: + // C11 6.7.6.1p2: + // For two pointer types to be compatible, both shall be identically + // qualified and both shall be pointers to compatible types. + // FIXME: we should also consider pointee types. + OS << "P"; + return; + + case Type::ObjCObjectPointer: + case Type::BlockPointer: + OS << "P"; + return; + + case Type::Complex: + OS << "C"; + return encodeTypeForFunctionPointerAuth( + Ctx, OS, cast<ComplexType>(T)->getElementType()); + + case Type::VariableArray: + case Type::ConstantArray: + case Type::IncompleteArray: + case Type::ArrayParameter: + // C11 6.7.6.2p6: + // For two array types to be compatible, both shall have compatible + // element types, and if both size specifiers are present, and are integer + // constant expressions, then both size specifiers shall have the same + // constant value [...] + // + // So since ElemType[N] has to be compatible ElemType[], we can't encode the + // width of the array. + OS << "A"; + return encodeTypeForFunctionPointerAuth( + Ctx, OS, cast<ArrayType>(T)->getElementType()); + + case Type::ObjCInterface: + case Type::ObjCObject: + OS << "<objc_object>"; + return; + + case Type::Enum: + // C11 6.7.2.2p4: + // Each enumerated type shall be compatible with char, a signed integer + // type, or an unsigned integer type. + // + // So we have to treat enum types as integers. + OS << "i"; + return; + + case Type::FunctionNoProto: + case Type::FunctionProto: { + // C11 6.7.6.3p15: + // For two function types to be compatible, both shall specify compatible + // return types. Moreover, the parameter type lists, if both are present, + // shall agree in the number of parameters and in the use of the ellipsis + // terminator; corresponding parameters shall have compatible types. + // + // That paragraph goes on to describe how unprototyped functions are to be + // handled, which we ignore here. Unprototyped function pointers are hashed + // as though they were prototyped nullary functions since thats probably + // what the user meant. This behavior is non-conforming. + // FIXME: If we add a "custom discriminator" function type attribute we + // should encode functions as their discriminators. + OS << "F"; + auto *FuncType = cast<FunctionType>(T); + encodeTypeForFunctionPointerAuth(Ctx, OS, FuncType->getReturnType()); + if (auto *FPT = dyn_cast<FunctionProtoType>(FuncType)) { + for (QualType Param : FPT->param_types()) { + Param = Ctx.getSignatureParameterType(Param); + encodeTypeForFunctionPointerAuth(Ctx, OS, Param); + } + if (FPT->isVariadic()) + OS << "z"; + } + OS << "E"; + return; + } + + case Type::MemberPointer: { + OS << "M"; + auto *MPT = T->getAs<MemberPointerType>(); + encodeTypeForFunctionPointerAuth(Ctx, OS, QualType(MPT->getClass(), 0)); + encodeTypeForFunctionPointerAuth(Ctx, OS, MPT->getPointeeType()); + return; + } + case Type::ExtVector: + case Type::Vector: + OS << "Dv" << Ctx.getTypeSizeInChars(T).getQuantity(); + break; + + // Don't bother discriminating based on these types. + case Type::Pipe: + case Type::BitInt: + case Type::ConstantMatrix: + OS << "?"; + return; + + case Type::Builtin: { + const BuiltinType *BTy = T->getAs<BuiltinType>(); + switch (BTy->getKind()) { +#define SIGNED_TYPE(Id, SingletonId) \ + case BuiltinType::Id: \ + OS << "i"; \ + return; +#define UNSIGNED_TYPE(Id, SingletonId) \ + case BuiltinType::Id: \ + OS << "i"; \ + return; +#define PLACEHOLDER_TYPE(Id, SingletonId) case BuiltinType::Id: +#define BUILTIN_TYPE(Id, SingletonId) +#include "clang/AST/BuiltinTypes.def" + llvm_unreachable("placeholder types should not appear here."); + + case BuiltinType::Half: + OS << "Dh"; + return; + case BuiltinType::Float: + OS << "f"; + return; + case BuiltinType::Double: + OS << "d"; + return; + case BuiltinType::LongDouble: + OS << "e"; + return; + case BuiltinType::Float16: + OS << "DF16_"; + return; + case BuiltinType::Float128: + OS << "g"; + return; + + case BuiltinType::Void: + OS << "v"; + return; + + case BuiltinType::ObjCId: + case BuiltinType::ObjCClass: + case BuiltinType::ObjCSel: + case BuiltinType::NullPtr: + OS << "P"; + return; + + // Don't bother discriminating based on OpenCL types. + case BuiltinType::OCLSampler: + case BuiltinType::OCLEvent: + case BuiltinType::OCLClkEvent: + case BuiltinType::OCLQueue: + case BuiltinType::OCLReserveID: + case BuiltinType::BFloat16: + case BuiltinType::VectorQuad: + case BuiltinType::VectorPair: + OS << "?"; + return; + + // Don't bother discriminating based on these seldom-used types. + case BuiltinType::Ibm128: + return; +#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ + case BuiltinType::Id: \ + return; +#include "clang/Basic/OpenCLImageTypes.def" +#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ + case BuiltinType::Id: \ + return; +#include "clang/Basic/OpenCLExtensionTypes.def" +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: \ + return; +#include "clang/Basic/AArch64SVEACLETypes.def" + case BuiltinType::Dependent: + llvm_unreachable("should never get here"); + case BuiltinType::AMDGPUBufferRsrc: + case BuiltinType::WasmExternRef: +#define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id: +#include "clang/Basic/RISCVVTypes.def" + llvm_unreachable("not yet implemented"); + } + } + case Type::Record: { + RecordDecl *RD = T->getAs<RecordType>()->getDecl(); + IdentifierInfo *II = RD->getIdentifier(); + if (!II) + if (const TypedefNameDecl *Typedef = RD->getTypedefNameForAnonDecl()) + II = Typedef->getDeclName().getAsIdentifierInfo(); + + if (!II) { + OS << "<anonymous_record>"; + return; + } + OS << II->getLength() << II->getName(); + return; + } + case Type::DeducedTemplateSpecialization: + case Type::Auto: +#define NON_CANONICAL_TYPE(Class, Base) case Type::Class: +#define DEPENDENT_TYPE(Class, Base) case Type::Class: +#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) case Type::Class: +#define ABSTRACT_TYPE(Class, Base) +#define TYPE(Class, Base) +#include "clang/AST/TypeNodes.inc" + llvm_unreachable("unexpected non-canonical or dependent type!"); + return; + } +} + +uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) { + assert(!T->isDependentType() && + "cannot compute type discriminator of a dependent type"); + + SmallString<256> Str; + llvm::raw_svector_ostream Out(Str); + + if (T->isFunctionPointerType() || T->isFunctionReferenceType()) + T = T->getPointeeType(); + + if (T->isFunctionType()) + encodeTypeForFunctionPointerAuth(*this, Out, T); + else + llvm_unreachable( + "type discrimination of non-function type not implemented yet"); + + return llvm::getPointerAuthStableSipHash(Str); +} + QualType ASTContext::getObjCGCQualType(QualType T, Qualifiers::GC GCAttr) const { QualType CanT = getCanonicalType(T); diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 1fec587b5c4c7..6d412af5cc994 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -2220,6 +2220,11 @@ llvm::Constant *ConstantLValueEmitter::emitPointerAuthPointer(const Expr *E) { // The assertions here are all checked by Sema. assert(Result.Val.isLValue()); + auto *Base = Result.Val.getLValueBase().get<const ValueDecl *>(); + if (auto *Decl = dyn_cast_or_null<FunctionDecl>(Base)) { + assert(Result.Val.getLValueOffset().isZero()); + return CGM.getRawFunctionPointer(Decl); + } return ConstantEmitter(CGM, Emitter.CGF) .emitAbstract(E->getExprLoc(), Result.Val, E->getType()); } diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index 673f6e60bfc31..33b421437c74c 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -15,6 +15,7 @@ #include "CodeGenModule.h" #include "clang/CodeGen/CodeGenABITypes.h" #include "clang/CodeGen/ConstantInitBuilder.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/Support/SipHash.h" using namespace clang; @@ -29,7 +30,9 @@ llvm::ConstantInt *CodeGenModule::getPointerAuthOtherDiscriminator( return nullptr; case PointerAuthSchema::Discrimination::Type: - llvm_unreachable("type discrimination not implemented yet"); + assert(!Type.isNull() && "type not provided for type-discriminated schema"); + return llvm::ConstantInt::get( + IntPtrTy, getContext().getPointerAuthTypeDiscriminator(Type)); case PointerAuthSchema::Discrimination::Decl: assert(Decl.getDecl() && @@ -43,6 +46,11 @@ llvm::ConstantInt *CodeGenModule::getPointerAuthOtherDiscriminator( llvm_unreachable("bad discrimination kind"); } +uint16_t CodeGen::getPointerAuthTypeDiscriminator(CodeGenModule &CGM, + QualType FunctionType) { + return CGM.getContext().getPointerAuthTypeDiscriminator(FunctionType); +} + uint16_t CodeGen::getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl Declaration) { return CGM.getPointerAuthDeclDiscriminator(Declaration); @@ -71,12 +79,15 @@ CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) { assert(!Schema.isAddressDiscriminated() && "function pointers cannot use address-specific discrimination"); - assert(!Schema.hasOtherDiscrimination() && - "function pointers don't support any discrimination yet"); + llvm::Constant *Discriminator = nullptr; + if (T->isFunctionPointerType() || T->isFunctionReferenceType()) + T = T->getPointeeType(); + if (T->isFunctionType()) + Discriminator = getPointerAuthOtherDiscriminator(Schema, GlobalDecl(), T); return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(), /*IsaPointer=*/false, /*AuthenticatesNull=*/false, - /*Discriminator=*/nullptr); + Discriminator); } llvm::Value * @@ -114,6 +125,47 @@ CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo( Schema.authenticatesNullValues(), Discriminator); } +/// Return the natural pointer authentication for values of the given +/// pointee type. +static CGPointerAuthInfo +getPointerAuthInfoForPointeeType(CodeGenModule &CGM, QualType PointeeType) { + if (PointeeType.isNull()) + return CGPointerAuthInfo(); + + // Function pointers use the function-pointer schema by default. + if (PointeeType->isFunctionType()) + return CGM.getFunctionPointerAuthInfo(PointeeType); + + // Normal data pointers never use direct pointer authentication by default. + return CGPointerAuthInfo(); +} + +CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForPointeeType(QualType T) { + return ::getPointerAuthInfoForPointeeType(*this, T); +} + +/// Return the natural pointer authentication for values of the given +/// pointer type. +static CGPointerAuthInfo getPointerAuthInfoForType(CodeGenModule &CGM, + QualType PointerType) { + assert(PointerType->isSignableType()); + + // Block pointers are currently not signed. + if (PointerType->isBlockPointerType()) + return CGPointerAuthInfo(); + + auto PointeeType = PointerType->getPointeeType(); + + if (PointeeType.isNull()) + return CGPointerAuthInfo(); + + return ::getPointerAuthInfoForPointeeType(CGM, PointeeType); +} + +CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForType(QualType T) { + return ::getPointerAuthInfoForType(*this, T); +} + llvm::Constant * CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, llvm::Constant *StorageAddress, @@ -180,6 +232,14 @@ llvm::Constant *CodeGenModule::getFunctionPointer(GlobalDecl GD, llvm::Type *Ty) { const auto *FD = cast<FunctionDecl>(GD.getDecl()); QualType FuncType = FD->getType(); + + // Annoyingly, K&R functions have prototypes in the clang AST, but + // expressions referring to them are unprototyped. + if (!FD->hasPrototype()) + if (const auto *Proto = FuncType->getAs<FunctionProtoType>()) + FuncType = Context.getFunctionNoProtoType(Proto->getReturnType(), + Proto->getExtInfo()); + return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType); } diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 22b2b314c316c..8a0a2c8731e72 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -964,6 +964,10 @@ class CodeGenModule : public CodeGenTypeCache { CGPointerAuthInfo getFunctionPointerAuthInfo(QualType T); + CGPointerAuthInfo getPointerAuthInfoForPointeeType(QualType type); + + CGPointerAuthInfo getPointerAuthInfoForType(QualType type); + bool shouldSignPointer(const PointerAuthSchema &Schema); llvm::Constant *getConstantSignedPointer(llvm::Constant *Pointer, const PointerAuthSchema &Schema, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index c0f6bc0c2e45a..35dc95c5524d3 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1789,6 +1789,9 @@ void Clang::AddAArch64TargetArgs(const ArgList &Args, options::OPT_fno_ptrauth_vtable_pointer_type_discrimination); Args.addOptInFlag(CmdArgs, options::OPT_fptrauth_init_fini, options::OPT_fno_ptrauth_init_fini); + Args.addOptInFlag( + CmdArgs, options::OPT_fptrauth_function_pointer_type_discrimination, + options::OPT_fno_ptrauth_function_pointer_type_discrimination); } void Clang::AddLoongArchTargetArgs(const ArgList &Args, diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index f42e28ba7e629..5301802875ab0 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1466,8 +1466,10 @@ void CompilerInvocation::setDefaultPointerAuthOptions( using Key = PointerAuthSchema::ARM8_3Key; using Discrimination = PointerAuthSchema::Discrimination; // If you change anything here, be sure to update <ptrauth.h>. - Opts.FunctionPointers = - PointerAuthSchema(Key::ASIA, false, Discrimination::None); + Opts.FunctionPointers = PointerAuthSchema( + Key::ASIA, false, + LangOpts.FunctionPointerTypeDiscrimination ? Discrimination::Type + : Discrimination::None); Opts.CXXVTablePointers = PointerAuthSchema( Key::ASDA, LangOpts.PointerAuthVTPtrAddressDiscrimination, @@ -3398,6 +3400,8 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts, GenerateArg(Consumer, OPT_fptrauth_vtable_pointer_type_discrimination); if (Opts.PointerAuthInitFini) GenerateArg(Consumer, OPT_fptrauth_init_fini); + if (Opts.FunctionPointerTypeDiscrimination) + GenerateArg(Consumer, OPT_fptrauth_function_pointer_type_discrimination); } static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, @@ -4779,6 +4783,9 @@ bool CompilerInvocation::CreateFromArgsImpl( if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) LangOpts.ObjCExceptions = 1; + LangOpts.FunctionPointerTypeDiscrimination = + Args.hasArg(OPT_fptrauth_function_pointer_type_discrimination); + for (auto Warning : Res.getDiagnosticOpts().Warnings) { if (Warning == "misexpect" && !Diags.isIgnored(diag::warn_profile_data_misexpect, SourceLocation())) { diff --git a/clang/test/CodeGen/ptrauth-function-type-discriminator.c b/clang/test/CodeGen/ptrauth-function-type-discriminator.c new file mode 100644 index 0000000000000..5dea48fe5915b --- /dev/null +++ b/clang/test/CodeGen/ptrauth-function-type-discriminator.c @@ -0,0 +1,137 @@ +// RUN: %clang_cc1 %s -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKC +// RUN: %clang_cc1 -xc++ %s -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefix=CHECK +// RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-pch %s -o %t.ast +// RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -x ast -o - %t.ast | FileCheck -check-prefix=CHECK --check-prefix=CHECKC %s + +#ifdef __cplusplus +extern "C" { +#endif + +void f(void); +void f2(int); +void (*fnptr)(void); +void *opaque; +unsigned long uintptr; + +// CHECK: @test_constant_null = global ptr null +void (*test_constant_null)(int) = 0; + +// CHECK: @test_constant_cast = global ptr ptrauth (ptr @f, i32 0, i64 2712) +void (*test_constant_cast)(int) = (void (*)(int))f; + +// CHECK: @test_opaque = global ptr ptrauth (ptr @f, i32 0) +void *test_opaque = +#ifdef __cplusplus + (void *) +#endif + (void (*)(int))(double (*)(double))f; + +// CHECK: @test_intptr_t = global i64 ptrtoint (ptr ptrauth (ptr @f, i32 0) to i64) +unsigned long test_intptr_t = (unsigned long)f; + +// CHECK: @test_through_long = global ptr ptrauth (ptr @f, i32 0, i64 2712) +void (*test_through_long)(int) = (void (*)(int))(long)f; + +// CHECK: @test_to_long = global i64 ptrtoint (ptr ptrauth (ptr @f, i32 0) to i64) +long test_to_long = (long)(double (*)())f; + +extern void external_function(void); +// CHECK: @fptr1 = global ptr ptrauth (ptr @external_function, i32 0, i64 18983) +void (*fptr1)(void) = external_function; +// CHECK: @fptr2 = global ptr ptrauth (ptr @external_function, i32 0, i64 18983) +void (*fptr2)(void) = &external_function; + +// CHECK: @fptr3 = global ptr ptrauth (ptr @external_function, i32 2, i64 26) +void (*fptr3)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, 26); + +// CHECK: @fptr4 = global ptr ptrauth (ptr @external_function, i32 2, i64 26, ptr @fptr4) +void (*fptr4)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, __builtin_ptrauth_blend_discriminator(&fptr4, 26)); + +// CHECK-LABEL: define void @test_call() +void test_call() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 0, i64 18983) ] + fnptr(); +} + +// CHECK-LABEL: define ptr @test_function_pointer() +// CHECK: ret ptr ptrauth (ptr @external_function, i32 0, i64 18983) +void (*test_function_pointer())(void) { + return external_function; +} + +struct InitiallyIncomplete; +extern struct InitiallyIncomplete returns_initially_incomplete(void); +// CHECK-LABEL: define void @use_while_incomplete() +void use_while_incomplete() { + // CHECK: [[VAR:%.*]] = alloca ptr, + // CHECK-NEXT: store ptr ptrauth (ptr @returns_initially_incomplete, i32 0, i64 25106), ptr [[VAR]] + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} +struct InitiallyIncomplete { int x; }; +// CHECK-LABEL: define void @use_while_complete() +void use_while_complete() { + // CHECK: [[VAR:%.*]] = alloca ptr, + // CHECK-NEXT: store ptr ptrauth (ptr @returns_initially_incomplete, i32 0, i64 25106), ptr [[VAR]] + // CHECK-NEXT: ret void + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} + +#ifndef __cplusplus + +void knr(param) + int param; +{} + +// CHECKC-LABEL: define void @test_knr +void test_knr() { + void (*p)() = knr; + p(0); + + // CHECKC: [[P:%.*]] = alloca ptr + // CHECKC: store ptr ptrauth (ptr @knr, i32 0, i64 18983), ptr [[P]] + // CHECKC: [[LOAD:%.*]] = load ptr, ptr [[P]] + // CHECKC: call void [[LOAD]](i32 noundef 0) [ "ptrauth"(i32 0, i64 18983) ] +} + +// CHECKC-LABEL: define void @test_redeclaration +void test_redeclaration() { + void redecl(); + void (*ptr)() = redecl; + void redecl(int); + void (*ptr2)(int) = redecl; + ptr(); + ptr2(0); + + // CHECKC: store ptr ptrauth (ptr @redecl, i32 0, i64 18983), ptr %ptr + // CHECKC: store ptr ptrauth (ptr @redecl, i32 0, i64 2712), ptr %ptr2 + // CHECKC: call void {{.*}}() [ "ptrauth"(i32 0, i64 18983) ] + // CHECKC: call void {{.*}}(i32 noundef 0) [ "ptrauth"(i32 0, i64 2712) ] +} + +void knr2(param) + int param; +{} + +// CHECKC-LABEL: define void @test_redecl_knr +void test_redecl_knr() { + void (*p)() = knr2; + p(); + + // CHECKC: store ptr ptrauth (ptr @knr2, i32 0, i64 18983) + // CHECKC: call void {{.*}}() [ "ptrauth"(i32 0, i64 18983) ] + + void knr2(int); + + void (*p2)(int) = knr2; + p2(0); + + // CHECKC: store ptr ptrauth (ptr @knr2, i32 0, i64 2712) + // CHECKC: call void {{.*}}(i32 noundef 0) [ "ptrauth"(i32 0, i64 2712) ] +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/clang/test/Preprocessor/ptrauth_feature.c b/clang/test/Preprocessor/ptrauth_feature.c index 80e239110ffc7..88b6982c01657 100644 --- a/clang/test/Preprocessor/ptrauth_feature.c +++ b/clang/test/Preprocessor/ptrauth_feature.c @@ -5,7 +5,7 @@ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI,NOFUNC // RUN: %clang_cc1 -E %s -triple=aarch64 \ // RUN: -fptrauth-calls \ @@ -13,7 +13,7 @@ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=NOINTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI +// RUN: FileCheck %s --check-prefixes=NOINTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI,NOFUNC // RUN: %clang_cc1 -E %s -triple=aarch64 \ // RUN: -fptrauth-intrinsics \ @@ -21,7 +21,7 @@ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,NOCALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI +// RUN: FileCheck %s --check-prefixes=INTRIN,NOCALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI,NOFUNC // RUN: %clang_cc1 -E %s -triple=aarch64 \ // RUN: -fptrauth-intrinsics \ @@ -29,7 +29,7 @@ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,NORETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,NORETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI,NOFUNC // RUN: %clang_cc1 -E %s -triple=aarch64 \ // RUN: -fptrauth-intrinsics \ @@ -37,7 +37,7 @@ // RUN: -fptrauth-returns \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,NOVPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,NOVPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI,NOFUNC // RUN: %clang_cc1 -E %s -triple=aarch64 \ // RUN: -fptrauth-intrinsics \ @@ -45,7 +45,7 @@ // RUN: -fptrauth-returns \ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,INITFINI +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,INITFINI,NOFUNC // RUN: %clang_cc1 -E %s -triple=aarch64 \ // RUN: -fptrauth-intrinsics \ @@ -53,7 +53,17 @@ // RUN: -fptrauth-returns \ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: -fptrauth-vtable-pointer-type-discrimination | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,NOINITFINI +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,NOINITFINI,NOFUNC + +// RUN: %clang_cc1 -E %s -triple=aarch64 \ +// RUN: -fptrauth-intrinsics \ +// RUN: -fptrauth-calls \ +// RUN: -fptrauth-returns \ +// RUN: -fptrauth-vtable-pointer-address-discrimination \ +// RUN: -fptrauth-vtable-pointer-type-discrimination \ +// RUN: -fptrauth-function-pointer-type-discrimination | \ +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,NOINITFINI,FUNC + #if __has_feature(ptrauth_intrinsics) // INTRIN: has_ptrauth_intrinsics @@ -111,3 +121,13 @@ void has_ptrauth_init_fini() {} // NOINITFINI: no_ptrauth_init_fini void no_ptrauth_init_fini() {} #endif + +#include <ptrauth.h> + +#if __has_feature(ptrauth_function_pointer_type_discrimination) +// FUNC: has_ptrauth_function_pointer_type_discrimination +void has_ptrauth_function_pointer_type_discrimination() {} +#else +// NOFUNC: no_ptrauth_function_pointer_type_discrimination +void no_ptrauth_function_pointer_type_discrimination() {} +#endif >From 1d5757a4ba1aad6cedaae239b62128c9f9ab0c43 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahata...@gmail.com> Date: Fri, 28 Jun 2024 08:02:36 -0700 Subject: [PATCH 2/2] Rename langopt --- clang/include/clang/Basic/Features.def | 2 +- clang/include/clang/Basic/LangOptions.def | 2 +- clang/lib/Frontend/CompilerInvocation.cpp | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 3c9d0364880c5..2f864ff1c0edf 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -110,7 +110,7 @@ FEATURE(ptrauth_vtable_pointer_address_discrimination, LangOpts.PointerAuthVTPtr FEATURE(ptrauth_vtable_pointer_type_discrimination, LangOpts.PointerAuthVTPtrTypeDiscrimination) FEATURE(ptrauth_member_function_pointer_type_discrimination, LangOpts.PointerAuthCalls) FEATURE(ptrauth_init_fini, LangOpts.PointerAuthInitFini) -FEATURE(ptrauth_function_pointer_type_discrimination, LangOpts.FunctionPointerTypeDiscrimination) +FEATURE(ptrauth_function_pointer_type_discrimination, LangOpts.PointerAuthFunctionTypeDiscrimination) EXTENSION(swiftcc, PP.getTargetInfo().checkCallingConvention(CC_Swift) == clang::TargetInfo::CCCR_OK) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 554f8643812b5..7521ab85a9b70 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -470,7 +470,7 @@ ENUM_LANGOPT(StrictFlexArraysLevel, StrictFlexArraysLevelKind, 2, COMPATIBLE_VALUE_LANGOPT(MaxTokens, 32, 0, "Max number of tokens per TU or 0") -BENIGN_LANGOPT(FunctionPointerTypeDiscrimination, 1, 0, +BENIGN_LANGOPT(PointerAuthFunctionTypeDiscrimination, 1, 0, "Use type discrimination when signing function pointers") ENUM_LANGOPT(SignReturnAddressScope, SignReturnAddressScopeKind, 2, SignReturnAddressScopeKind::None, diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 5301802875ab0..b441baa414900 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1468,8 +1468,8 @@ void CompilerInvocation::setDefaultPointerAuthOptions( // If you change anything here, be sure to update <ptrauth.h>. Opts.FunctionPointers = PointerAuthSchema( Key::ASIA, false, - LangOpts.FunctionPointerTypeDiscrimination ? Discrimination::Type - : Discrimination::None); + LangOpts.PointerAuthFunctionTypeDiscrimination ? Discrimination::Type + : Discrimination::None); Opts.CXXVTablePointers = PointerAuthSchema( Key::ASDA, LangOpts.PointerAuthVTPtrAddressDiscrimination, @@ -3400,7 +3400,7 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts, GenerateArg(Consumer, OPT_fptrauth_vtable_pointer_type_discrimination); if (Opts.PointerAuthInitFini) GenerateArg(Consumer, OPT_fptrauth_init_fini); - if (Opts.FunctionPointerTypeDiscrimination) + if (Opts.PointerAuthFunctionTypeDiscrimination) GenerateArg(Consumer, OPT_fptrauth_function_pointer_type_discrimination); } @@ -4783,7 +4783,7 @@ bool CompilerInvocation::CreateFromArgsImpl( if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) LangOpts.ObjCExceptions = 1; - LangOpts.FunctionPointerTypeDiscrimination = + LangOpts.PointerAuthFunctionTypeDiscrimination = Args.hasArg(OPT_fptrauth_function_pointer_type_discrimination); for (auto Warning : Res.getDiagnosticOpts().Warnings) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits