[llvm-branch-commits] [clang] [clang] Implement function pointer signing. (PR #93906)
Ahmed Bougacha via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu May 30 17:36:57 PDT 2024
https://github.com/ahmedbougacha created https://github.com/llvm/llvm-project/pull/93906
None
>From 0e85001f6d53e63beca77a76eaba1875ec84000d Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Fri, 24 May 2024 20:23:36 -0700
Subject: [PATCH] [clang] Implement function pointer signing.
Co-Authored-By: John McCall <rjmccall at apple.com>
---
clang/include/clang/Basic/CodeGenOptions.h | 4 +
.../clang/Basic/DiagnosticDriverKinds.td | 3 +
clang/include/clang/Basic/LangOptions.h | 2 +
.../include/clang/Basic/PointerAuthOptions.h | 136 ++++++++++++++++++
.../clang/Frontend/CompilerInvocation.h | 10 ++
clang/lib/CodeGen/CGBuiltin.cpp | 3 +-
clang/lib/CodeGen/CGCall.cpp | 3 +
clang/lib/CodeGen/CGCall.h | 28 +++-
clang/lib/CodeGen/CGExpr.cpp | 17 +--
clang/lib/CodeGen/CGExprConstant.cpp | 19 ++-
clang/lib/CodeGen/CGPointerAuth.cpp | 51 +++++++
clang/lib/CodeGen/CGPointerAuthInfo.h | 96 +++++++++++++
clang/lib/CodeGen/CodeGenFunction.cpp | 58 ++++++++
clang/lib/CodeGen/CodeGenFunction.h | 10 ++
clang/lib/CodeGen/CodeGenModule.h | 34 +++++
clang/lib/Frontend/CompilerInvocation.cpp | 36 +++++
clang/lib/Headers/ptrauth.h | 34 +++++
.../CodeGen/ptrauth-function-attributes.c | 13 ++
.../test/CodeGen/ptrauth-function-init-fail.c | 5 +
clang/test/CodeGen/ptrauth-function-init.c | 31 ++++
.../CodeGen/ptrauth-function-lvalue-cast.c | 23 +++
clang/test/CodeGen/ptrauth-weak_import.c | 10 ++
clang/test/CodeGenCXX/ptrauth.cpp | 24 ++++
23 files changed, 633 insertions(+), 17 deletions(-)
create mode 100644 clang/lib/CodeGen/CGPointerAuthInfo.h
create mode 100644 clang/test/CodeGen/ptrauth-function-attributes.c
create mode 100644 clang/test/CodeGen/ptrauth-function-init-fail.c
create mode 100644 clang/test/CodeGen/ptrauth-function-init.c
create mode 100644 clang/test/CodeGen/ptrauth-function-lvalue-cast.c
create mode 100644 clang/test/CodeGen/ptrauth-weak_import.c
create mode 100644 clang/test/CodeGenCXX/ptrauth.cpp
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 9469a424045bb..502722a6ec4eb 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_BASIC_CODEGENOPTIONS_H
#define LLVM_CLANG_BASIC_CODEGENOPTIONS_H
+#include "clang/Basic/PointerAuthOptions.h"
#include "clang/Basic/Sanitizers.h"
#include "clang/Basic/XRayInstr.h"
#include "llvm/ADT/FloatingPointMode.h"
@@ -388,6 +389,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
std::vector<std::string> Reciprocals;
+ /// Configuration for pointer-signing.
+ PointerAuthOptions PointerAuth;
+
/// The preferred width for auto-vectorization transforms. This is intended to
/// override default transforms based on the width of the architected vector
/// registers.
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 773b234cd68fe..6cbb0c8401c15 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -351,6 +351,9 @@ def err_drv_omp_host_ir_file_not_found : Error<
"target regions but cannot be found">;
def err_drv_omp_host_target_not_supported : Error<
"target '%0' is not a supported OpenMP host target">;
+def err_drv_ptrauth_not_supported : Error<
+ "target '%0' does not support native pointer authentication">;
+
def err_drv_expecting_fopenmp_with_fopenmp_targets : Error<
"'-fopenmp-targets' must be used in conjunction with a '-fopenmp' option "
"compatible with offloading; e.g., '-fopenmp=libomp' or '-fopenmp=libiomp5'">;
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 75e88afbd9705..5216822e45b1b 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -346,6 +346,8 @@ class LangOptionsBase {
BKey
};
+ using PointerAuthenticationMode = ::clang::PointerAuthenticationMode;
+
enum class ThreadModelKind {
/// POSIX Threads.
POSIX,
diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h
index e5cdcc31ebfb7..32b179e3f9460 100644
--- a/clang/include/clang/Basic/PointerAuthOptions.h
+++ b/clang/include/clang/Basic/PointerAuthOptions.h
@@ -14,10 +14,146 @@
#ifndef LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H
#define LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Target/TargetOptions.h"
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
namespace clang {
constexpr unsigned PointerAuthKeyNone = -1;
+class PointerAuthSchema {
+public:
+ enum class Kind : unsigned {
+ None,
+ ARM8_3,
+ };
+
+ /// Hardware pointer-signing keys in ARM8.3.
+ ///
+ /// These values are the same used in ptrauth.h.
+ enum class ARM8_3Key : unsigned {
+ ASIA = 0,
+ ASIB = 1,
+ ASDA = 2,
+ ASDB = 3
+ };
+
+ /// Forms of extra discrimination.
+ enum class Discrimination : unsigned {
+ /// No additional discrimination.
+ None,
+
+ /// Discriminate using a constant value.
+ Constant,
+ };
+
+private:
+ Kind TheKind : 2;
+ unsigned IsAddressDiscriminated : 1;
+ unsigned IsIsaPointer : 1;
+ unsigned AuthenticatesNullValues : 1;
+ PointerAuthenticationMode SelectedAuthenticationMode : 2;
+ Discrimination DiscriminationKind : 2;
+ unsigned Key : 4;
+ unsigned ConstantDiscriminator : 16;
+
+public:
+ PointerAuthSchema() : TheKind(Kind::None) {}
+
+ PointerAuthSchema(
+ ARM8_3Key Key, bool IsAddressDiscriminated,
+ PointerAuthenticationMode AuthenticationMode,
+ Discrimination OtherDiscrimination,
+ std::optional<uint16_t> ConstantDiscriminatorOrNone = std::nullopt,
+ bool IsIsaPointer = false, bool AuthenticatesNullValues = false)
+ : TheKind(Kind::ARM8_3), IsAddressDiscriminated(IsAddressDiscriminated),
+ IsIsaPointer(IsIsaPointer),
+ AuthenticatesNullValues(AuthenticatesNullValues),
+ SelectedAuthenticationMode(AuthenticationMode),
+ DiscriminationKind(OtherDiscrimination), Key(unsigned(Key)) {
+ assert((getOtherDiscrimination() != Discrimination::Constant ||
+ ConstantDiscriminatorOrNone) &&
+ "constant discrimination requires a constant!");
+ if (ConstantDiscriminatorOrNone)
+ ConstantDiscriminator = *ConstantDiscriminatorOrNone;
+ }
+
+ PointerAuthSchema(
+ ARM8_3Key Key, bool IsAddressDiscriminated,
+ Discrimination OtherDiscrimination,
+ std::optional<uint16_t> ConstantDiscriminatorOrNone = std::nullopt,
+ bool IsIsaPointer = false, bool AuthenticatesNullValues = false)
+ : PointerAuthSchema(Key, IsAddressDiscriminated,
+ PointerAuthenticationMode::SignAndAuth,
+ OtherDiscrimination, ConstantDiscriminatorOrNone,
+ IsIsaPointer, AuthenticatesNullValues) {}
+
+ Kind getKind() const { return TheKind; }
+
+ explicit operator bool() const { return isEnabled(); }
+
+ bool isEnabled() const { return getKind() != Kind::None; }
+
+ bool isAddressDiscriminated() const {
+ assert(getKind() != Kind::None);
+ return IsAddressDiscriminated;
+ }
+
+ bool isIsaPointer() const {
+ assert(getKind() != Kind::None);
+ return IsIsaPointer;
+ }
+
+ bool authenticatesNullValues() const {
+ assert(getKind() != Kind::None);
+ return AuthenticatesNullValues;
+ }
+
+ bool hasOtherDiscrimination() const {
+ return getOtherDiscrimination() != Discrimination::None;
+ }
+
+ Discrimination getOtherDiscrimination() const {
+ assert(getKind() != Kind::None);
+ return DiscriminationKind;
+ }
+
+ uint16_t getConstantDiscrimination() const {
+ assert(getOtherDiscrimination() == Discrimination::Constant);
+ return (uint16_t)ConstantDiscriminator;
+ }
+
+ unsigned getKey() const {
+ switch (getKind()) {
+ case Kind::None:
+ llvm_unreachable("calling getKey() on disabled schema");
+ case Kind::ARM8_3:
+ return unsigned(getARM8_3Key());
+ }
+ llvm_unreachable("bad key kind");
+ }
+
+ PointerAuthenticationMode getAuthenticationMode() const {
+ return SelectedAuthenticationMode;
+ }
+
+ ARM8_3Key getARM8_3Key() const {
+ assert(getKind() == Kind::ARM8_3);
+ return ARM8_3Key(Key);
+ }
+};
+
+struct PointerAuthOptions {
+ /// The ABI for C function pointers.
+ PointerAuthSchema FunctionPointers;
+};
+
} // end namespace clang
#endif
diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h
index 1a2a39411e58d..e60e5aad6c70d 100644
--- a/clang/include/clang/Frontend/CompilerInvocation.h
+++ b/clang/include/clang/Frontend/CompilerInvocation.h
@@ -305,6 +305,16 @@ class CompilerInvocation : public CompilerInvocationBase {
/// executable), for finding the builtin compiler path.
static std::string GetResourcesPath(const char *Argv0, void *MainAddr);
+ /// Populate \p Opts with the default set of pointer authentication-related
+ /// options given \p LangOpts and \p Triple. Return true if defaults are
+ /// available.
+ ///
+ /// Note: This is intended to be used by tools which must be aware of
+ /// pointer authentication-related code generation, e.g. lldb.
+ static bool setDefaultPointerAuthOptions(PointerAuthOptions &Opts,
+ const LangOptions &LangOpts,
+ const llvm::Triple &Triple);
+
/// Retrieve a module hash string that is suitable for uniquely
/// identifying the conditions under which the module was built.
std::string getModuleHash() const;
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index b2e3b6fa64284..1f528f9490cd2 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -5999,8 +5999,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
// If this is a predefined lib function (e.g. malloc), emit the call
// using exactly the normal call path.
if (getContext().BuiltinInfo.isPredefinedLibFunction(BuiltinID))
- return emitLibraryCall(
- *this, FD, E, cast<llvm::Constant>(EmitScalarExpr(E->getCallee())));
+ return emitLibraryCall(*this, FD, E, CGM.getRawFunctionPointer(FD));
// Check that a call to a target specific builtin has the correct target
// features.
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 97449a5e51e73..c33f37bf5b8c4 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5677,6 +5677,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
!isa_and_nonnull<FunctionDecl>(TargetDecl))
EmitKCFIOperandBundle(ConcreteCallee, BundleList);
+ // Add the pointer-authentication bundle.
+ EmitPointerAuthOperandBundle(ConcreteCallee.getPointerAuthInfo(), BundleList);
+
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurFuncDecl))
if (FD->hasAttr<StrictFPAttr>())
// All calls within a strictfp function are marked strictfp
diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h
index 6b676ac196db2..4b0e1561b4ef5 100644
--- a/clang/lib/CodeGen/CGCall.h
+++ b/clang/lib/CodeGen/CGCall.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_LIB_CODEGEN_CGCALL_H
#define LLVM_CLANG_LIB_CODEGEN_CGCALL_H
+#include "CGPointerAuthInfo.h"
#include "CGValue.h"
#include "EHScopeStack.h"
#include "clang/AST/ASTFwd.h"
@@ -69,6 +70,10 @@ class CGCallee {
Last = Virtual
};
+ struct OrdinaryInfoStorage {
+ CGCalleeInfo AbstractInfo;
+ CGPointerAuthInfo PointerAuthInfo;
+ };
struct BuiltinInfoStorage {
const FunctionDecl *Decl;
unsigned ID;
@@ -85,7 +90,7 @@ class CGCallee {
SpecialKind KindOrFunctionPointer;
union {
- CGCalleeInfo AbstractInfo;
+ OrdinaryInfoStorage OrdinaryInfo;
BuiltinInfoStorage BuiltinInfo;
PseudoDestructorInfoStorage PseudoDestructorInfo;
VirtualInfoStorage VirtualInfo;
@@ -104,10 +109,13 @@ class CGCallee {
/// Construct a callee. Call this constructor directly when this
/// isn't a direct call.
- CGCallee(const CGCalleeInfo &abstractInfo, llvm::Value *functionPtr)
+ CGCallee(
+ const CGCalleeInfo &abstractInfo, llvm::Value *functionPtr,
+ const CGPointerAuthInfo &pointerAuthInfo = /*FIXME*/ CGPointerAuthInfo())
: KindOrFunctionPointer(
SpecialKind(reinterpret_cast<uintptr_t>(functionPtr))) {
- AbstractInfo = abstractInfo;
+ OrdinaryInfo.AbstractInfo = abstractInfo;
+ OrdinaryInfo.PointerAuthInfo = pointerAuthInfo;
assert(functionPtr && "configuring callee without function pointer");
assert(functionPtr->getType()->isPointerTy());
}
@@ -128,12 +136,12 @@ class CGCallee {
static CGCallee forDirect(llvm::Constant *functionPtr,
const CGCalleeInfo &abstractInfo = CGCalleeInfo()) {
- return CGCallee(abstractInfo, functionPtr);
+ return CGCallee(abstractInfo, functionPtr, CGPointerAuthInfo());
}
static CGCallee forDirect(llvm::FunctionCallee functionPtr,
const CGCalleeInfo &abstractInfo = CGCalleeInfo()) {
- return CGCallee(abstractInfo, functionPtr.getCallee());
+ return CGCallee(abstractInfo, functionPtr.getCallee(), CGPointerAuthInfo());
}
static CGCallee forVirtual(const CallExpr *CE, GlobalDecl MD, Address Addr,
@@ -173,7 +181,11 @@ class CGCallee {
if (isVirtual())
return VirtualInfo.MD;
assert(isOrdinary());
- return AbstractInfo;
+ return OrdinaryInfo.AbstractInfo;
+ }
+ const CGPointerAuthInfo &getPointerAuthInfo() const {
+ assert(isOrdinary());
+ return OrdinaryInfo.PointerAuthInfo;
}
llvm::Value *getFunctionPointer() const {
assert(isOrdinary());
@@ -184,6 +196,10 @@ class CGCallee {
KindOrFunctionPointer =
SpecialKind(reinterpret_cast<uintptr_t>(functionPtr));
}
+ void setPointerAuthInfo(CGPointerAuthInfo pointerAuth) {
+ assert(isOrdinary());
+ OrdinaryInfo.PointerAuthInfo = pointerAuth;
+ }
bool isVirtual() const {
return KindOrFunctionPointer == SpecialKind::Virtual;
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index d6478cc6835d8..152f51f5865e0 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2850,22 +2850,22 @@ static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF,
return LV;
}
-static llvm::Constant *EmitFunctionDeclPointer(CodeGenModule &CGM,
- GlobalDecl GD) {
+llvm::Constant *CodeGenModule::getRawFunctionPointer(GlobalDecl GD,
+ llvm::Type *Ty) {
const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
if (FD->hasAttr<WeakRefAttr>()) {
- ConstantAddress aliasee = CGM.GetWeakRefReference(FD);
+ ConstantAddress aliasee = GetWeakRefReference(FD);
return aliasee.getPointer();
}
- llvm::Constant *V = CGM.GetAddrOfFunction(GD);
+ llvm::Constant *V = GetAddrOfFunction(GD, Ty);
return V;
}
static LValue EmitFunctionDeclLValue(CodeGenFunction &CGF, const Expr *E,
GlobalDecl GD) {
const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
- llvm::Value *V = EmitFunctionDeclPointer(CGF.CGM, GD);
+ llvm::Constant *V = CGF.CGM.getFunctionPointer(GD);
CharUnits Alignment = CGF.getContext().getDeclAlign(FD);
return CGF.MakeAddrLValue(V, E->getType(), Alignment,
AlignmentSource::Decl);
@@ -5501,7 +5501,7 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) {
// name to make it clear it's not the actual builtin.
if (CGF.CurFn->getName() != FDInlineName &&
OnlyHasInlineBuiltinDeclaration(FD)) {
- llvm::Constant *CalleePtr = EmitFunctionDeclPointer(CGF.CGM, GD);
+ llvm::Constant *CalleePtr = CGF.CGM.getRawFunctionPointer(GD);
llvm::Function *Fn = llvm::cast<llvm::Function>(CalleePtr);
llvm::Module *M = Fn->getParent();
llvm::Function *Clone = M->getFunction(FDInlineName);
@@ -5524,7 +5524,7 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) {
return CGCallee::forBuiltin(builtinID, FD);
}
- llvm::Constant *CalleePtr = EmitFunctionDeclPointer(CGF.CGM, GD);
+ llvm::Constant *CalleePtr = CGF.CGM.getRawFunctionPointer(GD);
if (CGF.CGM.getLangOpts().CUDA && !CGF.CGM.getLangOpts().CUDAIsDevice &&
FD->hasAttr<CUDAGlobalAttr>())
CalleePtr = CGF.CGM.getCUDARuntime().getKernelStub(
@@ -5581,7 +5581,8 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) {
GD = GlobalDecl(VD);
CGCalleeInfo calleeInfo(functionType->getAs<FunctionProtoType>(), GD);
- CGCallee callee(calleeInfo, calleePtr);
+ CGPointerAuthInfo pointerAuth = CGM.getFunctionPointerAuthInfo(functionType);
+ CGCallee callee(calleeInfo, calleePtr, pointerAuth);
return callee;
}
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index de9380c0e63be..bfb545a2fe1f6 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1956,8 +1956,25 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
if (D->hasAttr<WeakRefAttr>())
return CGM.GetWeakRefReference(D).getPointer();
+ auto PtrAuthSign = [&](llvm::Constant *C) {
+ CGPointerAuthInfo AuthInfo = CGM.getFunctionPointerAuthInfo(DestType);
+
+ if (AuthInfo) {
+ if (hasNonZeroOffset())
+ return ConstantLValue(nullptr);
+
+ C = applyOffset(C);
+ C = CGM.getConstantSignedPointer(
+ C, AuthInfo.getKey(), nullptr,
+ cast_or_null<llvm::Constant>(AuthInfo.getDiscriminator()));
+ return ConstantLValue(C, /*applied offset*/ true);
+ }
+
+ return ConstantLValue(C);
+ };
+
if (auto FD = dyn_cast<FunctionDecl>(D))
- return CGM.GetAddrOfFunction(FD);
+ return PtrAuthSign(CGM.getRawFunctionPointer(FD));
if (auto VD = dyn_cast<VarDecl>(D)) {
// We can never refer to a variable with local storage.
diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp
index 756c00aa42c8c..adfa721ac89d3 100644
--- a/clang/lib/CodeGen/CGPointerAuth.cpp
+++ b/clang/lib/CodeGen/CGPointerAuth.cpp
@@ -28,6 +28,24 @@
using namespace clang;
using namespace CodeGen;
+/// Return the abstract pointer authentication schema for a pointer to the given
+/// function type.
+CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) {
+ auto &Schema = getCodeGenOpts().PointerAuth.FunctionPointers;
+ if (!Schema)
+ return CGPointerAuthInfo();
+
+ assert(!Schema.isAddressDiscriminated() &&
+ "function pointers cannot use address-specific discrimination");
+
+ assert(!Schema.hasOtherDiscrimination() &&
+ "function pointers don't support any discrimination yet");
+
+ return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(),
+ /*IsaPointer=*/false, /*AuthenticatesNull=*/false,
+ /*Discriminator=*/nullptr);
+}
+
/// Build a signed-pointer "ptrauth" constant.
static llvm::ConstantPtrAuth *
buildConstantAddress(CodeGenModule &CGM, llvm::Constant *pointer, unsigned key,
@@ -75,3 +93,36 @@ CodeGen::getConstantSignedPointer(CodeGenModule &CGM,
return CGM.getConstantSignedPointer(pointer, key, 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,
+ GlobalDecl GD) {
+ assert(functionType->isFunctionType() ||
+ functionType->isFunctionReferenceType() ||
+ functionType->isFunctionPointerType());
+
+ if (auto pointerAuth = getFunctionPointerAuthInfo(functionType)) {
+ return getConstantSignedPointer(
+ pointer, pointerAuth.getKey(), nullptr,
+ cast_or_null<llvm::Constant>(pointerAuth.getDiscriminator()));
+ }
+
+ return pointer;
+}
+
+llvm::Constant *CodeGenModule::getFunctionPointer(GlobalDecl GD,
+ llvm::Type *Ty) {
+ const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
+
+ // Annoyingly, K&R functions have prototypes in the clang AST, but
+ // expressions referring to them are unprototyped.
+ QualType FuncType = FD->getType();
+ if (!FD->hasPrototype())
+ if (const auto *Proto = FuncType->getAs<FunctionProtoType>())
+ FuncType = Context.getFunctionNoProtoType(Proto->getReturnType(),
+ Proto->getExtInfo());
+
+ return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType, GD);
+}
diff --git a/clang/lib/CodeGen/CGPointerAuthInfo.h b/clang/lib/CodeGen/CGPointerAuthInfo.h
new file mode 100644
index 0000000000000..e870c3145acba
--- /dev/null
+++ b/clang/lib/CodeGen/CGPointerAuthInfo.h
@@ -0,0 +1,96 @@
+//===----- CGPointerAuthInfo.h - -------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Pointer auth info class.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_CODEGEN_CGPOINTERAUTHINFO_H
+#define LLVM_CLANG_LIB_CODEGEN_CGPOINTERAUTHINFO_H
+
+#include "clang/AST/Type.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Value.h"
+
+namespace clang {
+namespace CodeGen {
+
+class CGPointerAuthInfo {
+private:
+ PointerAuthenticationMode AuthenticationMode : 2;
+ bool IsIsaPointer : 1;
+ bool AuthenticatesNullValues : 1;
+ unsigned Key : 28;
+ llvm::Value *Discriminator;
+
+public:
+ CGPointerAuthInfo()
+ : AuthenticationMode(PointerAuthenticationMode::None),
+ IsIsaPointer(false), AuthenticatesNullValues(false), Key(0),
+ Discriminator(nullptr) {}
+ CGPointerAuthInfo(unsigned Key, PointerAuthenticationMode AuthenticationMode,
+ bool IsIsaPointer, bool AuthenticatesNullValues,
+ llvm::Value *Discriminator)
+ : AuthenticationMode(AuthenticationMode), IsIsaPointer(IsIsaPointer),
+ AuthenticatesNullValues(AuthenticatesNullValues), Key(Key),
+ Discriminator(Discriminator) {
+ assert(!Discriminator || Discriminator->getType()->isIntegerTy() ||
+ Discriminator->getType()->isPointerTy());
+ }
+
+ explicit operator bool() const { return isSigned(); }
+
+ bool isSigned() const {
+ return AuthenticationMode != PointerAuthenticationMode::None;
+ }
+
+ unsigned getKey() const {
+ assert(isSigned());
+ return Key;
+ }
+ llvm::Value *getDiscriminator() const {
+ assert(isSigned());
+ return Discriminator;
+ }
+
+ PointerAuthenticationMode getAuthenticationMode() const {
+ return AuthenticationMode;
+ }
+
+ bool isIsaPointer() const { return IsIsaPointer; }
+
+ bool authenticatesNullValues() const { return AuthenticatesNullValues; }
+
+ bool shouldStrip() const {
+ return AuthenticationMode == PointerAuthenticationMode::Strip ||
+ AuthenticationMode == PointerAuthenticationMode::SignAndStrip;
+ }
+
+ bool shouldSign() const {
+ return AuthenticationMode == PointerAuthenticationMode::SignAndStrip ||
+ AuthenticationMode == PointerAuthenticationMode::SignAndAuth;
+ }
+
+ bool shouldAuth() const {
+ return AuthenticationMode == PointerAuthenticationMode::SignAndAuth;
+ }
+
+ bool operator!=(const CGPointerAuthInfo &Other) const {
+ return Key != Other.Key || Discriminator != Other.Discriminator ||
+ AuthenticationMode != Other.AuthenticationMode;
+ }
+
+ bool operator==(const CGPointerAuthInfo &Other) const {
+ return !(*this != Other);
+ }
+};
+
+} // end namespace CodeGen
+} // end namespace clang
+
+#endif
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index f0345f3b191b8..cb5acf1a1abb7 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -859,6 +859,11 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
FD->getBody()->getStmtClass() == Stmt::CoroutineBodyStmtClass)
SanOpts.Mask &= ~SanitizerKind::Null;
+ // Add pointer authentication attributes.
+ const CodeGenOptions &CodeGenOpts = CGM.getCodeGenOpts();
+ if (CodeGenOpts.PointerAuth.FunctionPointers)
+ Fn->addFnAttr("ptrauth-calls");
+
// Apply xray attributes to the function (as a string, for now)
bool AlwaysXRayAttr = false;
if (const auto *XRayAttr = D ? D->getAttr<XRayInstrumentAttr>() : nullptr) {
@@ -3041,3 +3046,56 @@ llvm::Value *CodeGenFunction::emitBoolVecConversion(llvm::Value *SrcVec,
return Builder.CreateShuffleVector(SrcVec, ShuffleMask, Name);
}
+
+void CodeGenFunction::EmitPointerAuthOperandBundle(
+ const CGPointerAuthInfo &pointerAuth,
+ SmallVectorImpl<llvm::OperandBundleDef> &bundles) {
+ if (!pointerAuth.isSigned())
+ return;
+
+ auto key = Builder.getInt32(pointerAuth.getKey());
+
+ llvm::Value *discriminator = pointerAuth.getDiscriminator();
+ if (!discriminator) {
+ discriminator = Builder.getSize(0);
+ }
+
+ llvm::Value *args[] = {key, discriminator};
+ bundles.emplace_back("ptrauth", args);
+}
+
+static llvm::Value *EmitPointerAuthCommon(CodeGenFunction &CGF,
+ const CGPointerAuthInfo &pointerAuth,
+ llvm::Value *pointer,
+ unsigned intrinsicID) {
+ if (!pointerAuth)
+ return pointer;
+
+ auto key = CGF.Builder.getInt32(pointerAuth.getKey());
+
+ llvm::Value *discriminator = pointerAuth.getDiscriminator();
+ if (!discriminator) {
+ discriminator = CGF.Builder.getSize(0);
+ }
+
+ // Convert the pointer to intptr_t before signing it.
+ auto origType = pointer->getType();
+ pointer = CGF.Builder.CreatePtrToInt(pointer, CGF.IntPtrTy);
+
+ // call i64 @llvm.ptrauth.sign.i64(i64 %pointer, i32 %key, i64 %discriminator)
+ auto intrinsic = CGF.CGM.getIntrinsic(intrinsicID);
+ pointer = CGF.EmitRuntimeCall(intrinsic, {pointer, key, discriminator});
+
+ // Convert back to the original type.
+ pointer = CGF.Builder.CreateIntToPtr(pointer, origType);
+ return pointer;
+}
+
+llvm::Value *
+CodeGenFunction::EmitPointerAuthSign(const CGPointerAuthInfo &pointerAuth,
+ llvm::Value *pointer) {
+ if (!pointerAuth.shouldSign())
+ return pointer;
+ return EmitPointerAuthCommon(*this, pointerAuth, pointer,
+ llvm::Intrinsic::ptrauth_sign);
+}
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 45585361a4fc9..a0518b7525df1 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4406,6 +4406,16 @@ class CodeGenFunction : public CodeGenTypeCache {
}
bool isPointerKnownNonNull(const Expr *E);
+ CGPointerAuthInfo EmitPointerAuthInfo(const PointerAuthSchema &schema,
+ llvm::Value *storageAddress,
+ GlobalDecl calleeDecl,
+ QualType calleeType);
+ llvm::Value *EmitPointerAuthSign(QualType pointeeType, llvm::Value *pointer);
+ llvm::Value *EmitPointerAuthSign(const CGPointerAuthInfo &info,
+ llvm::Value *pointer);
+ void EmitPointerAuthOperandBundle(
+ const CGPointerAuthInfo &info,
+ SmallVectorImpl<llvm::OperandBundleDef> &bundles);
// Return the copy constructor name with the prefix "__copy_constructor_"
// removed.
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 194ac180171e0..5e30c5740b2f1 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -26,6 +26,7 @@
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/NoSanitizeList.h"
+#include "clang/Basic/PointerAuthOptions.h"
#include "clang/Basic/ProfileList.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/XRayLists.h"
@@ -70,6 +71,7 @@ class Expr;
class Stmt;
class StringLiteral;
class NamedDecl;
+class PointerAuthSchema;
class ValueDecl;
class VarDecl;
class LangOptions;
@@ -938,11 +940,43 @@ class CodeGenModule : public CodeGenTypeCache {
// Return the function body address of the given function.
llvm::Constant *GetFunctionStart(const ValueDecl *Decl);
+ /// Return a function pointer for a reference to the given function.
+ /// This correctly handles weak references, but does not apply a
+ /// pointer signature.
+ llvm::Constant *getRawFunctionPointer(GlobalDecl GD,
+ llvm::Type *Ty = nullptr);
+
+ /// Return the ABI-correct function pointer value for a reference
+ /// to the given function. This will apply a pointer signature if
+ /// necessary, caching the result for the given function.
+ llvm::Constant *getFunctionPointer(GlobalDecl GD, llvm::Type *Ty = nullptr);
+
+ /// Return the ABI-correct function pointer value for a reference
+ /// to the given function. This will apply a pointer signature if
+ /// necessary, but will only cache the result if \p FD is passed.
+ llvm::Constant *getFunctionPointer(llvm::Constant *pointer,
+ QualType functionType,
+ GlobalDecl GD = GlobalDecl());
+
+ CGPointerAuthInfo getFunctionPointerAuthInfo(QualType functionType);
+
+ CGPointerAuthInfo getPointerAuthInfoForPointeeType(QualType type);
+
+ CGPointerAuthInfo getPointerAuthInfoForType(QualType type);
+
+ llvm::Constant *getConstantSignedPointer(llvm::Constant *pointer,
+ const PointerAuthSchema &schema,
+ llvm::Constant *storageAddress,
+ GlobalDecl schemaDecl,
+ QualType schemaType);
+
llvm::Constant *getConstantSignedPointer(llvm::Constant *pointer,
unsigned key,
llvm::Constant *storageAddress,
llvm::Constant *extraDiscrim);
+ CGPointerAuthInfo EmitPointerAuthInfo(const RecordDecl *RD);
+
// Return whether RTTI information should be emitted for this target.
bool shouldEmitRTTI(bool ForEH = false) {
return (ForEH || getLangOpts().RTTI) && !getLangOpts().CUDAIsDevice &&
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 14ee02c4cd582..b4ca3e413b2c0 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1458,6 +1458,39 @@ static void setPGOUseInstrumentor(CodeGenOptions &Opts,
Opts.setProfileUse(CodeGenOptions::ProfileClangInstr);
}
+bool CompilerInvocation::setDefaultPointerAuthOptions(
+ PointerAuthOptions &Opts, const LangOptions &LangOpts,
+ const llvm::Triple &Triple) {
+ if (Triple.getArch() == llvm::Triple::aarch64) {
+ if (LangOpts.PointerAuthCalls) {
+ 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);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+static bool parsePointerAuthOptions(PointerAuthOptions &Opts,
+ ArgList &Args,
+ const LangOptions &LangOpts,
+ const llvm::Triple &Triple,
+ DiagnosticsEngine &Diags) {
+ if (!LangOpts.PointerAuthCalls)
+ return true;
+
+ if (CompilerInvocation::setDefaultPointerAuthOptions(Opts, LangOpts, Triple))
+ return true;
+
+ Diags.Report(diag::err_drv_ptrauth_not_supported)
+ << Triple.str();
+ return false;
+}
+
void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
ArgumentConsumer Consumer,
const llvm::Triple &T,
@@ -2153,6 +2186,9 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
+ if (!LangOpts->CUDAIsDevice)
+ parsePointerAuthOptions(Opts.PointerAuth, Args, *LangOpts, T, Diags);
+
if (Args.hasArg(options::OPT_ffinite_loops))
Opts.FiniteLoops = CodeGenOptions::FiniteLoopsKind::Always;
else if (Args.hasArg(options::OPT_fno_finite_loops))
diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h
index 069bd6ad03b03..aec387153d87c 100644
--- a/clang/lib/Headers/ptrauth.h
+++ b/clang/lib/Headers/ptrauth.h
@@ -38,6 +38,12 @@ typedef enum {
ptrauth_key_process_dependent_data = ptrauth_key_asdb,
#endif /* __APPLE__ */
+ /* The key used to sign C function pointers.
+ The extra data is always 0. */
+ ptrauth_key_function_pointer = ptrauth_key_process_independent_code,
+
+ /* Other pointers signed under the ABI use private ABI rules. */
+
} ptrauth_key;
/* An integer type of the appropriate size for a discriminator argument. */
@@ -141,6 +147,27 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
__builtin_ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, \
__new_data)
+/* Authenticate a pointer using one scheme and resign it as a C
+ function pointer.
+
+ 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 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.
+
+ This operation is guaranteed to not leave the intermediate value
+ available for attack before it is re-signed. Additionally, if this
+ expression is used syntactically as the function expression in a
+ call, only a single authentication will be performed. */
+#define ptrauth_auth_function(__value, __old_key, __old_data) \
+ ptrauth_auth_and_resign(__value, __old_key, __old_data, \
+ ptrauth_key_function_pointer, 0)
+
/* Authenticate a data pointer.
The value must be an expression of non-function pointer type.
@@ -225,6 +252,13 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
__value; \
})
+#define ptrauth_auth_function(__value, __old_key, __old_data) \
+ ({ \
+ (void)__old_key; \
+ (void)__old_data; \
+ __value; \
+ })
+
#define ptrauth_auth_data(__value, __old_key, __old_data) \
({ \
(void)__old_key; \
diff --git a/clang/test/CodeGen/ptrauth-function-attributes.c b/clang/test/CodeGen/ptrauth-function-attributes.c
new file mode 100644
index 0000000000000..eda9c8813ed1e
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-function-attributes.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,OFF
+// RUN: %clang_cc1 -triple arm64e-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,OFF
+
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,CALLS
+// RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-calls -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,CALLS
+
+// ALL-LABEL: define void @test() #0
+void test() {
+}
+
+// CALLS: attributes #0 = {{{.*}} "ptrauth-calls" {{.*}}}
+
+// OFF-NOT: attributes {{.*}} "ptrauth-
diff --git a/clang/test/CodeGen/ptrauth-function-init-fail.c b/clang/test/CodeGen/ptrauth-function-init-fail.c
new file mode 100644
index 0000000000000..012d4f7368eaf
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-function-init-fail.c
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-calls %s -verify -emit-llvm -o -
+
+void f(void);
+
+int *pf = (int *)&f + 1; // expected-error{{cannot compile this static initializer yet}}
diff --git a/clang/test/CodeGen/ptrauth-function-init.c b/clang/test/CodeGen/ptrauth-function-init.c
new file mode 100644
index 0000000000000..7d75f5ff2a0c3
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-function-init.c
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 %s -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s
+// RUN: %clang_cc1 -xc++ %s -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,CXX
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void f(void);
+
+#ifdef __cplusplus
+
+// CXX-LABEL: define internal void @__cxx_global_var_init()
+// CXX: store ptr getelementptr inbounds (i32, ptr ptrauth (ptr @f, i32 0), i64 2), ptr @_ZL2fp, align 8
+
+__attribute__((used))
+void (*const fp)(void) = (void (*)(void))((int *)&f + 2); // Error in C mode.
+
+#endif
+
+// CHECK-LABEL: define void @t1()
+void t1() {
+ // CHECK: [[PF:%.*]] = alloca ptr
+ // CHECK: store ptr getelementptr inbounds (i32, ptr ptrauth (ptr @f, i32 0), i64 2), ptr [[PF]]
+
+ void (*pf)(void) = (void (*)(void))((int *)&f + 2);
+ (void)pf;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/clang/test/CodeGen/ptrauth-function-lvalue-cast.c b/clang/test/CodeGen/ptrauth-function-lvalue-cast.c
new file mode 100644
index 0000000000000..8ca5490195bac
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-function-lvalue-cast.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 %s -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -emit-llvm -o- | FileCheck %s
+
+typedef void (*fptr_t)(void);
+
+char *cptr;
+void (*fptr)(void);
+
+// CHECK-LABEL: define void @test1
+void test1() {
+ // CHECK: [[LOAD:%.*]] = load ptr, ptr @cptr
+ // CHECK: call void [[LOAD]]() [ "ptrauth"(i32 0, i64 0) ]
+ // CHECK: ret void
+
+ (*(fptr_t)cptr)();
+}
+
+// CHECK-LABEL: define i8 @test2
+char test2() {
+ return *(char *)fptr;
+ // CHECK: [[LOAD:%.*]] = load ptr, ptr @fptr
+ // CHECK: [[LOAD1:%.*]] = load i8, ptr [[LOAD]]
+ // CHECK: ret i8 [[LOAD1]]
+}
diff --git a/clang/test/CodeGen/ptrauth-weak_import.c b/clang/test/CodeGen/ptrauth-weak_import.c
new file mode 100644
index 0000000000000..2fc8530d0677f
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-weak_import.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s
+
+extern void foo() __attribute__((weak_import));
+
+// CHECK-LABEL: define void @bar()
+// CHECK: br i1 icmp ne (ptr ptrauth (ptr @foo, i32 0), ptr null), label
+void bar() {
+ if (foo)
+ foo();
+}
diff --git a/clang/test/CodeGenCXX/ptrauth.cpp b/clang/test/CodeGenCXX/ptrauth.cpp
new file mode 100644
index 0000000000000..d2d3317eb4a35
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth.cpp
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 -fexceptions -fcxx-exceptions -o - %s | FileCheck %s
+
+void f(void);
+auto &f_ref = f;
+
+// CHECK-LABEL: define void @_Z1gv(
+// CHECK: call void ptrauth (ptr @_Z1fv, i32 0)() [ "ptrauth"(i32 0, i64 0) ]
+
+void g() { f_ref(); }
+
+void foo1();
+
+void test_terminate() noexcept {
+ foo1();
+}
+
+// CHECK: define void @_ZSt9terminatev() #[[ATTR4:.*]] {
+
+namespace std {
+ void terminate() noexcept {
+ }
+}
+
+// CHECK: attributes #[[ATTR4]] = {{{.*}}"ptrauth-calls"{{.*}}}
More information about the llvm-branch-commits
mailing list