| //===--- CGPointerAuth.cpp - IR generation for 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file contains common routines relating to the emission of |
| // pointer authentication operations. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "CodeGenFunction.h" |
| #include "CodeGenModule.h" |
| #include "clang/CodeGen/CodeGenABITypes.h" |
| #include "clang/CodeGen/ConstantInitBuilder.h" |
| #include "llvm/Support/SipHash.h" |
| |
| using namespace clang; |
| using namespace CodeGen; |
| |
| /// Given a pointer-authentication schema, return a concrete "other" |
| /// discriminator for it. |
| llvm::ConstantInt *CodeGenModule::getPointerAuthOtherDiscriminator( |
| const PointerAuthSchema &Schema, GlobalDecl Decl, QualType Type) { |
| switch (Schema.getOtherDiscrimination()) { |
| case PointerAuthSchema::Discrimination::None: |
| return nullptr; |
| |
| case PointerAuthSchema::Discrimination::Type: |
| 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() && |
| "declaration not provided for decl-discriminated schema"); |
| return llvm::ConstantInt::get(IntPtrTy, |
| getPointerAuthDeclDiscriminator(Decl)); |
| |
| case PointerAuthSchema::Discrimination::Constant: |
| return llvm::ConstantInt::get(IntPtrTy, Schema.getConstantDiscrimination()); |
| } |
| 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); |
| } |
| |
| /// Return the "other" decl-specific discriminator for the given decl. |
| uint16_t |
| CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl Declaration) { |
| uint16_t &EntityHash = PtrAuthDiscriminatorHashes[Declaration]; |
| |
| if (EntityHash == 0) { |
| StringRef Name = getMangledName(Declaration); |
| EntityHash = llvm::getPointerAuthStableSipHash(Name); |
| } |
| |
| return EntityHash; |
| } |
| |
| /// Return the abstract pointer authentication schema for a pointer to the given |
| /// function type. |
| CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) { |
| const auto &Schema = getCodeGenOpts().PointerAuth.FunctionPointers; |
| if (!Schema) |
| return CGPointerAuthInfo(); |
| |
| assert(!Schema.isAddressDiscriminated() && |
| "function pointers cannot use address-specific discrimination"); |
| |
| 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); |
| } |
| |
| llvm::Value * |
| CodeGenFunction::EmitPointerAuthBlendDiscriminator(llvm::Value *StorageAddress, |
| llvm::Value *Discriminator) { |
| StorageAddress = Builder.CreatePtrToInt(StorageAddress, IntPtrTy); |
| auto Intrinsic = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_blend); |
| return Builder.CreateCall(Intrinsic, {StorageAddress, Discriminator}); |
| } |
| |
| /// Emit the concrete pointer authentication informaton for the |
| /// given authentication schema. |
| CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo( |
| const PointerAuthSchema &Schema, llvm::Value *StorageAddress, |
| GlobalDecl SchemaDecl, QualType SchemaType) { |
| if (!Schema) |
| return CGPointerAuthInfo(); |
| |
| llvm::Value *Discriminator = |
| CGM.getPointerAuthOtherDiscriminator(Schema, SchemaDecl, SchemaType); |
| |
| if (Schema.isAddressDiscriminated()) { |
| assert(StorageAddress && |
| "address not provided for address-discriminated schema"); |
| |
| if (Discriminator) |
| Discriminator = |
| EmitPointerAuthBlendDiscriminator(StorageAddress, Discriminator); |
| else |
| Discriminator = Builder.CreatePtrToInt(StorageAddress, IntPtrTy); |
| } |
| |
| return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(), |
| Schema.isIsaPointer(), |
| 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, |
| llvm::ConstantInt *OtherDiscriminator) { |
| llvm::Constant *AddressDiscriminator; |
| if (StorageAddress) { |
| assert(StorageAddress->getType() == UnqualPtrTy); |
| AddressDiscriminator = StorageAddress; |
| } else { |
| AddressDiscriminator = llvm::Constant::getNullValue(UnqualPtrTy); |
| } |
| |
| llvm::ConstantInt *IntegerDiscriminator; |
| if (OtherDiscriminator) { |
| assert(OtherDiscriminator->getType() == Int64Ty); |
| IntegerDiscriminator = OtherDiscriminator; |
| } else { |
| IntegerDiscriminator = llvm::ConstantInt::get(Int64Ty, 0); |
| } |
| |
| return llvm::ConstantPtrAuth::get(Pointer, |
| llvm::ConstantInt::get(Int32Ty, Key), |
| IntegerDiscriminator, AddressDiscriminator); |
| } |
| |
| /// Does a given PointerAuthScheme require us to sign a value |
| bool CodeGenModule::shouldSignPointer(const PointerAuthSchema &Schema) { |
| auto AuthenticationMode = Schema.getAuthenticationMode(); |
| return AuthenticationMode == PointerAuthenticationMode::SignAndStrip || |
| AuthenticationMode == PointerAuthenticationMode::SignAndAuth; |
| } |
| |
| /// Sign a constant pointer using the given scheme, producing a constant |
| /// with the same IR type. |
| llvm::Constant *CodeGenModule::getConstantSignedPointer( |
| llvm::Constant *Pointer, const PointerAuthSchema &Schema, |
| llvm::Constant *StorageAddress, GlobalDecl SchemaDecl, |
| QualType SchemaType) { |
| assert(shouldSignPointer(Schema)); |
| llvm::ConstantInt *OtherDiscriminator = |
| getPointerAuthOtherDiscriminator(Schema, SchemaDecl, SchemaType); |
| |
| return getConstantSignedPointer(Pointer, Schema.getKey(), StorageAddress, |
| OtherDiscriminator); |
| } |
| |
| /// If applicable, sign a given constant function pointer with the ABI rules for |
| /// functionType. |
| llvm::Constant *CodeGenModule::getFunctionPointer(llvm::Constant *Pointer, |
| QualType FunctionType) { |
| assert(FunctionType->isFunctionType() || |
| FunctionType->isFunctionReferenceType() || |
| FunctionType->isFunctionPointerType()); |
| |
| if (auto PointerAuth = getFunctionPointerAuthInfo(FunctionType)) |
| return getConstantSignedPointer( |
| Pointer, PointerAuth.getKey(), /*StorageAddress=*/nullptr, |
| cast_or_null<llvm::ConstantInt>(PointerAuth.getDiscriminator())); |
| |
| return Pointer; |
| } |
| |
| 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); |
| } |
| |
| std::optional<PointerAuthQualifier> |
| CodeGenModule::computeVTPointerAuthentication(const CXXRecordDecl *ThisClass) { |
| auto DefaultAuthentication = getCodeGenOpts().PointerAuth.CXXVTablePointers; |
| if (!DefaultAuthentication) |
| return std::nullopt; |
| const CXXRecordDecl *PrimaryBase = |
| Context.baseForVTableAuthentication(ThisClass); |
| |
| unsigned Key = DefaultAuthentication.getKey(); |
| bool AddressDiscriminated = DefaultAuthentication.isAddressDiscriminated(); |
| auto DefaultDiscrimination = DefaultAuthentication.getOtherDiscrimination(); |
| unsigned TypeBasedDiscriminator = |
| Context.getPointerAuthVTablePointerDiscriminator(PrimaryBase); |
| unsigned Discriminator; |
| if (DefaultDiscrimination == PointerAuthSchema::Discrimination::Type) { |
| Discriminator = TypeBasedDiscriminator; |
| } else if (DefaultDiscrimination == |
| PointerAuthSchema::Discrimination::Constant) { |
| Discriminator = DefaultAuthentication.getConstantDiscrimination(); |
| } else { |
| assert(DefaultDiscrimination == PointerAuthSchema::Discrimination::None); |
| Discriminator = 0; |
| } |
| if (auto ExplicitAuthentication = |
| PrimaryBase->getAttr<VTablePointerAuthenticationAttr>()) { |
| auto ExplicitAddressDiscrimination = |
| ExplicitAuthentication->getAddressDiscrimination(); |
| auto ExplicitDiscriminator = |
| ExplicitAuthentication->getExtraDiscrimination(); |
| |
| unsigned ExplicitKey = ExplicitAuthentication->getKey(); |
| if (ExplicitKey == VTablePointerAuthenticationAttr::NoKey) |
| return std::nullopt; |
| |
| if (ExplicitKey != VTablePointerAuthenticationAttr::DefaultKey) { |
| if (ExplicitKey == VTablePointerAuthenticationAttr::ProcessIndependent) |
| Key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDA; |
| else { |
| assert(ExplicitKey == |
| VTablePointerAuthenticationAttr::ProcessDependent); |
| Key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDB; |
| } |
| } |
| |
| if (ExplicitAddressDiscrimination != |
| VTablePointerAuthenticationAttr::DefaultAddressDiscrimination) |
| AddressDiscriminated = |
| ExplicitAddressDiscrimination == |
| VTablePointerAuthenticationAttr::AddressDiscrimination; |
| |
| if (ExplicitDiscriminator == |
| VTablePointerAuthenticationAttr::TypeDiscrimination) |
| Discriminator = TypeBasedDiscriminator; |
| else if (ExplicitDiscriminator == |
| VTablePointerAuthenticationAttr::CustomDiscrimination) |
| Discriminator = ExplicitAuthentication->getCustomDiscriminationValue(); |
| else if (ExplicitDiscriminator == |
| VTablePointerAuthenticationAttr::NoExtraDiscrimination) |
| Discriminator = 0; |
| } |
| return PointerAuthQualifier::Create(Key, AddressDiscriminated, Discriminator, |
| PointerAuthenticationMode::SignAndAuth, |
| /* IsIsaPointer */ false, |
| /* AuthenticatesNullValues */ false); |
| } |
| |
| std::optional<PointerAuthQualifier> |
| CodeGenModule::getVTablePointerAuthentication(const CXXRecordDecl *Record) { |
| if (!Record->getDefinition() || !Record->isPolymorphic()) |
| return std::nullopt; |
| |
| auto Existing = VTablePtrAuthInfos.find(Record); |
| std::optional<PointerAuthQualifier> Authentication; |
| if (Existing != VTablePtrAuthInfos.end()) { |
| Authentication = Existing->getSecond(); |
| } else { |
| Authentication = computeVTPointerAuthentication(Record); |
| VTablePtrAuthInfos.insert(std::make_pair(Record, Authentication)); |
| } |
| return Authentication; |
| } |
| |
| std::optional<CGPointerAuthInfo> |
| CodeGenModule::getVTablePointerAuthInfo(CodeGenFunction *CGF, |
| const CXXRecordDecl *Record, |
| llvm::Value *StorageAddress) { |
| auto Authentication = getVTablePointerAuthentication(Record); |
| if (!Authentication) |
| return std::nullopt; |
| |
| llvm::Value *Discriminator = nullptr; |
| if (auto ExtraDiscriminator = Authentication->getExtraDiscriminator()) |
| Discriminator = llvm::ConstantInt::get(IntPtrTy, ExtraDiscriminator); |
| |
| if (Authentication->isAddressDiscriminated()) { |
| assert(StorageAddress && |
| "address not provided for address-discriminated schema"); |
| if (Discriminator) |
| Discriminator = |
| CGF->EmitPointerAuthBlendDiscriminator(StorageAddress, Discriminator); |
| else |
| Discriminator = CGF->Builder.CreatePtrToInt(StorageAddress, IntPtrTy); |
| } |
| |
| return CGPointerAuthInfo(Authentication->getKey(), |
| PointerAuthenticationMode::SignAndAuth, |
| /* IsIsaPointer */ false, |
| /* AuthenticatesNullValues */ false, Discriminator); |
| } |