//===--- 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(PointerAuth.getDiscriminator())); return Pointer; } llvm::Constant *CodeGenModule::getFunctionPointer(GlobalDecl GD, llvm::Type *Ty) { const auto *FD = cast(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()) FuncType = Context.getFunctionNoProtoType(Proto->getReturnType(), Proto->getExtInfo()); return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType); } std::optional 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()) { 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 CodeGenModule::getVTablePointerAuthentication(const CXXRecordDecl *Record) { if (!Record->getDefinition() || !Record->isPolymorphic()) return std::nullopt; auto Existing = VTablePtrAuthInfos.find(Record); std::optional Authentication; if (Existing != VTablePtrAuthInfos.end()) { Authentication = Existing->getSecond(); } else { Authentication = computeVTPointerAuthentication(Record); VTablePtrAuthInfos.insert(std::make_pair(Record, Authentication)); } return Authentication; } std::optional 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); }