[clang] e23250e - [clang] Implement function pointer signing and authenticated function calls (#93906)

via cfe-commits cfe-commits at lists.llvm.org
Fri Jun 21 10:20:21 PDT 2024


Author: Ahmed Bougacha
Date: 2024-06-21T10:20:15-07:00
New Revision: e23250ecb7e09170e584db60375100790f39fac9

URL: https://github.com/llvm/llvm-project/commit/e23250ecb7e09170e584db60375100790f39fac9
DIFF: https://github.com/llvm/llvm-project/commit/e23250ecb7e09170e584db60375100790f39fac9.diff

LOG: [clang] Implement function pointer signing and authenticated function calls (#93906)

The functions are currently always signed/authenticated with zero
discriminator.

Co-Authored-By: John McCall <rjmccall at apple.com>

Added: 
    clang/lib/CodeGen/CGPointerAuthInfo.h
    clang/test/CodeGen/ptrauth-function-attributes.c
    clang/test/CodeGen/ptrauth-function-init-fail.c
    clang/test/CodeGen/ptrauth-function-init.c
    clang/test/CodeGen/ptrauth-function-lvalue-cast.c
    clang/test/CodeGen/ptrauth-function.c
    clang/test/CodeGen/ptrauth-weak_import.c
    clang/test/CodeGenCXX/ptrauth.cpp

Modified: 
    clang/include/clang/Basic/CodeGenOptions.h
    clang/include/clang/Basic/PointerAuthOptions.h
    clang/include/clang/Frontend/CompilerInvocation.h
    clang/lib/CodeGen/CGBuiltin.cpp
    clang/lib/CodeGen/CGCall.cpp
    clang/lib/CodeGen/CGCall.h
    clang/lib/CodeGen/CGExpr.cpp
    clang/lib/CodeGen/CGExprConstant.cpp
    clang/lib/CodeGen/CGPointerAuth.cpp
    clang/lib/CodeGen/CodeGenFunction.cpp
    clang/lib/CodeGen/CodeGenFunction.h
    clang/lib/CodeGen/CodeGenModule.h
    clang/lib/Frontend/CompilerInvocation.cpp
    clang/lib/Headers/ptrauth.h

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 00523a84d3895..f2a707a8ba8d7 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"
@@ -391,6 +392,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/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h
index e5cdcc31ebfb7..aaad4a2b2b5ae 100644
--- a/clang/include/clang/Basic/PointerAuthOptions.h
+++ b/clang/include/clang/Basic/PointerAuthOptions.h
@@ -14,10 +14,144 @@
 #ifndef LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H
 #define LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H
 
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "llvm/ADT/STLForwardCompat.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Target/TargetOptions.h"
+#include <optional>
+
 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 : 2;
+  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(llvm::to_underlying(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 ConstantDiscriminator;
+  }
+
+  unsigned getKey() const {
+    switch (getKind()) {
+    case Kind::None:
+      llvm_unreachable("calling getKey() on disabled schema");
+    case Kind::ARM8_3:
+      return llvm::to_underlying(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..9daa0a1ecf948 100644
--- a/clang/include/clang/Frontend/CompilerInvocation.h
+++ b/clang/include/clang/Frontend/CompilerInvocation.h
@@ -305,6 +305,15 @@ 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.
+  ///
+  /// Note: This is intended to be used by tools which must be aware of
+  /// pointer authentication-related code generation, e.g. lldb.
+  static void 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 2516ed4508242..d11e7a95d833d 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -6015,8 +6015,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 a071b16fc37f9..2b301130ef7b7 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5678,6 +5678,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..412b44a8c753a 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,
+           /* FIXME: make parameter pointerAuthInfo mandatory */
+           const CGPointerAuthInfo &pointerAuthInfo = 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());
   }
@@ -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 3dfe5e09c778d..534f46da74862 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2856,22 +2856,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);
@@ -5506,7 +5506,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);
@@ -5529,7 +5529,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(
@@ -5586,7 +5586,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 bc5f42d632bca..dffb8ce83b643 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2024,8 +2024,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::ConstantInt>(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 e1fb0bdb85cfb..f0819b0467489 100644
--- a/clang/lib/CodeGen/CGPointerAuth.cpp
+++ b/clang/lib/CodeGen/CGPointerAuth.cpp
@@ -17,6 +17,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) {
+  const 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);
+}
+
 llvm::Constant *
 CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
                                         llvm::Constant *StorageAddress,
@@ -41,3 +59,24 @@ CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
                                     llvm::ConstantInt::get(Int32Ty, Key),
                                     IntegerDiscriminator, AddressDiscriminator);
 }
+
+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();
+  return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType);
+}

diff  --git a/clang/lib/CodeGen/CGPointerAuthInfo.h b/clang/lib/CodeGen/CGPointerAuthInfo.h
new file mode 100644
index 0000000000000..0a0c11fb423f2
--- /dev/null
+++ b/clang/lib/CodeGen/CGPointerAuthInfo.h
@@ -0,0 +1,99 @@
+//===----- 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 "clang/Basic/LangOptions.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Value.h"
+
+namespace clang {
+namespace CodeGen {
+
+class CGPointerAuthInfo {
+private:
+  PointerAuthenticationMode AuthenticationMode : 2;
+  unsigned IsIsaPointer : 1;
+  unsigned AuthenticatesNullValues : 1;
+  unsigned Key : 2;
+  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;
+  }
+
+  friend bool operator!=(const CGPointerAuthInfo &LHS,
+                         const CGPointerAuthInfo &RHS) {
+    return LHS.Key != RHS.Key || LHS.Discriminator != RHS.Discriminator ||
+           LHS.AuthenticationMode != RHS.AuthenticationMode;
+  }
+
+  friend bool operator==(const CGPointerAuthInfo &LHS,
+                         const CGPointerAuthInfo &RHS) {
+    return !(LHS != RHS);
+  }
+};
+
+} // end namespace CodeGen
+} // end namespace clang
+
+#endif

diff  --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 200c40da8bc43..650c5662539f9 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -861,6 +861,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) {
@@ -3042,3 +3047,19 @@ 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);
+}

diff  --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index cdb5ae6663405..a9c497bde6871 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4417,6 +4417,10 @@ class CodeGenFunction : public CodeGenTypeCache {
 
   bool isPointerKnownNonNull(const Expr *E);
 
+  void EmitPointerAuthOperandBundle(
+      const CGPointerAuthInfo &Info,
+      SmallVectorImpl<llvm::OperandBundleDef> &Bundles);
+
   // Return the copy constructor name with the prefix "__copy_constructor_"
   // removed.
   static std::string getNonTrivialCopyConstructorStr(QualType QT,

diff  --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 04e1a392ef5d1..9913304757caa 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -69,6 +69,7 @@ class Expr;
 class Stmt;
 class StringLiteral;
 class NamedDecl;
+class PointerAuthSchema;
 class ValueDecl;
 class VarDecl;
 class LangOptions;
@@ -937,6 +938,25 @@ 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.
+  llvm::Constant *getFunctionPointer(llvm::Constant *Pointer,
+                                     QualType FunctionType);
+
+  CGPointerAuthInfo getFunctionPointerAuthInfo(QualType T);
+
   llvm::Constant *
   getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
                            llvm::Constant *StorageAddress,

diff  --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index cde4a84673b6e..a6d9f42ace9cc 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1458,6 +1458,29 @@ static void setPGOUseInstrumentor(CodeGenOptions &Opts,
     Opts.setProfileUse(CodeGenOptions::ProfileClangInstr);
 }
 
+void CompilerInvocation::setDefaultPointerAuthOptions(
+    PointerAuthOptions &Opts, const LangOptions &LangOpts,
+    const llvm::Triple &Triple) {
+  assert(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);
+  }
+}
+
+static void parsePointerAuthOptions(PointerAuthOptions &Opts,
+                                    const LangOptions &LangOpts,
+                                    const llvm::Triple &Triple,
+                                    DiagnosticsEngine &Diags) {
+  if (!LangOpts.PointerAuthCalls)
+    return;
+
+  CompilerInvocation::setDefaultPointerAuthOptions(Opts, LangOpts, Triple);
+}
+
 void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
                                                  ArgumentConsumer Consumer,
                                                  const llvm::Triple &T,
@@ -2153,6 +2176,9 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
 
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
 
+  if (!LangOpts->CUDAIsDevice)
+    parsePointerAuthOptions(Opts.PointerAuth, *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 6aad9efb7a652..1a4bd02933ea2 100644
--- a/clang/lib/Headers/ptrauth.h
+++ b/clang/lib/Headers/ptrauth.h
@@ -28,6 +28,12 @@ typedef enum {
   /* A process-specific key which can be used to sign data pointers. */
   ptrauth_key_process_dependent_data = ptrauth_key_asdb,
 
+  /* 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. */
@@ -131,6 +137,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.
@@ -217,6 +244,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..7ec30498b9d35
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-function-attributes.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple arm64e-apple-ios                   -emit-llvm %s  -o - | FileCheck %s --check-prefixes=ALL,OFF
+// RUN: %clang_cc1 -triple aarch64-linux-gnu                  -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 aarch64-linux-gnu -fptrauth-calls  -emit-llvm %s  -o - | FileCheck %s --check-prefixes=ALL,CALLS
+
+// ALL: define {{(dso_local )?}}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..4522f6a14c4a8
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-function-init-fail.c
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-calls %s -verify -emit-llvm -o -
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls %s -verify -emit-llvm -o -
+
+void f(void);
+
+// FIXME: We need a better diagnostic here.
+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..bf8ee53364ecc
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-function-init.c
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 %s       -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s
+// RUN: %clang_cc1 %s       -triple aarch64-linux-gnu -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
+// RUN: %clang_cc1 -xc++ %s -triple aarch64-linux-gnu -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: define {{(dso_local )?}}internal void @__cxx_global_var_init()
+// CXX: store ptr getelementptr inbounds (i32, ptr ptrauth (ptr @f, i32 0), i64 2), ptr @_ZL2fp, align 8
+
+// This is rejected in C mode as adding a non-zero constant to a signed pointer
+// is unrepresentable in relocations. In C++ mode, this can be done dynamically
+// by the global constructor.
+__attribute__((used))
+void (*const fp)(void) = (void (*)(void))((int *)&f + 2);
+
+#endif
+
+// CHECK: define {{(dso_local )?}}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..8d8af18fcafbe
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-function-lvalue-cast.c
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 %s -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -emit-llvm -o- | FileCheck %s
+// RUN: %clang_cc1 %s -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -o- | FileCheck %s
+
+typedef void (*fptr_t)(void);
+
+char *cptr;
+void (*fptr)(void);
+
+// CHECK: define {{(dso_local )?}}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: define {{(dso_local )?}}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-function.c b/clang/test/CodeGen/ptrauth-function.c
new file mode 100644
index 0000000000000..eea3f7ed73747
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-function.c
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s  -o - | FileCheck -check-prefix=CHECK %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s  -o - | FileCheck -check-prefix=CHECK %s
+
+void test_call();
+
+// CHECK: define {{(dso_local )?}}void @test_direct_call()
+void test_direct_call() {
+  // CHECK: call void @test_call(){{$}}
+  test_call();
+}
+
+// CHECK: define {{(dso_local )?}}void @test_indirect_call(ptr noundef %[[FP:.*]])
+void test_indirect_call(void (*fp(void))) {
+  // CHECK: %[[FP_ADDR:.*]] = alloca ptr, align 8
+  // CHECK: store ptr %[[FP]], ptr %[[FP_ADDR]], align 8
+  // CHECK: %[[V0:.*]] = load ptr, ptr %[[FP_ADDR]], align 8
+  // CHECK: %[[CALL:.*]] = call ptr %[[V0]]() [ "ptrauth"(i32 0, i64 0) ]
+  fp();
+}
+
+void abort();
+// CHECK: define {{(dso_local )?}}void @test_direct_builtin_call()
+void test_direct_builtin_call() {
+  // CHECK: call void @abort() {{#[0-9]+$}}
+  abort();
+}
+
+// CHECK-LABEL: define {{(dso_local )?}}void @test_memcpy_inline(
+// CHECK-NOT: call{{.*}}memcpy
+
+extern inline __attribute__((__always_inline__))
+void *memcpy(void *d, const void *s, unsigned long) {
+  return 0;
+}
+
+void test_memcpy_inline(char *d, char *s) {
+  memcpy(d, s, 4);
+}

diff  --git a/clang/test/CodeGen/ptrauth-weak_import.c b/clang/test/CodeGen/ptrauth-weak_import.c
new file mode 100644
index 0000000000000..1f53747a2640e
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-weak_import.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s  -o - | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s  -o - | FileCheck %s
+
+extern void foo() __attribute__((weak_import));
+
+// CHECK: define {{(dso_local )?}}void @bar()
+// CHECK: [[TMP1:%.*]] =  icmp ne ptr ptrauth (ptr @foo, i32 0), null
+// CHECK: br i1 [[TMP1]], 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..b0c069f43969b
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 -fexceptions -fcxx-exceptions -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -emit-llvm -std=c++11 -fexceptions -fcxx-exceptions -o - %s | FileCheck %s
+
+void f(void);
+auto &f_ref = f;
+
+// CHECK: define {{(dso_local )?}}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 {{(dso_local )?}}void @_ZSt9terminatev() #[[ATTR4:.*]] {
+
+namespace std {
+  void terminate() noexcept {
+  }
+}
+
+// CHECK: attributes #[[ATTR4]] = {{{.*}}"ptrauth-calls"{{.*}}}


        


More information about the cfe-commits mailing list