Skip to content

[clang] Implement function pointer signing and authenticated function calls #93906

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 21, 2024

Conversation

ahmedbougacha
Copy link
Member

@ahmedbougacha ahmedbougacha commented May 31, 2024

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

Co-Authored-By: John McCall [email protected]

@ahatanak ahatanak changed the title [clang] Implement function pointer signing. [clang] Implement function pointer signing and authenticated function calls Jun 3, 2024
@ahatanak ahatanak marked this pull request as ready for review June 3, 2024 17:31
@llvmbot llvmbot added clang Clang issues not falling into any other category backend:X86 clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:headers Headers provided by Clang, e.g. for intrinsics clang:codegen IR generation bugs: mangling, exceptions, etc. labels Jun 3, 2024
@llvmbot
Copy link
Member

llvmbot commented Jun 3, 2024

@llvm/pr-subscribers-clang-driver
@llvm/pr-subscribers-backend-x86
@llvm/pr-subscribers-backend-aarch64
@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-codegen

Author: Ahmed Bougacha (ahmedbougacha)

Changes

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

Co-Authored-By: John McCall <[email protected]>


Patch is 37.62 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/93906.diff

24 Files Affected:

  • (modified) clang/include/clang/Basic/CodeGenOptions.h (+4)
  • (modified) clang/include/clang/Basic/DiagnosticDriverKinds.td (+3)
  • (modified) clang/include/clang/Basic/LangOptions.h (+2)
  • (modified) clang/include/clang/Basic/PointerAuthOptions.h (+136)
  • (modified) clang/include/clang/Frontend/CompilerInvocation.h (+10)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+1-2)
  • (modified) clang/lib/CodeGen/CGCall.cpp (+3)
  • (modified) clang/lib/CodeGen/CGCall.h (+22-6)
  • (modified) clang/lib/CodeGen/CGExpr.cpp (+9-8)
  • (modified) clang/lib/CodeGen/CGExprConstant.cpp (+18-1)
  • (modified) clang/lib/CodeGen/CGPointerAuth.cpp (+51)
  • (added) clang/lib/CodeGen/CGPointerAuthInfo.h (+96)
  • (modified) clang/lib/CodeGen/CodeGenFunction.cpp (+22)
  • (modified) clang/lib/CodeGen/CodeGenFunction.h (+7)
  • (modified) clang/lib/CodeGen/CodeGenModule.h (+34)
  • (modified) clang/lib/Frontend/CompilerInvocation.cpp (+36)
  • (modified) clang/lib/Headers/ptrauth.h (+34)
  • (added) clang/test/CodeGen/ptrauth-function-attributes.c (+13)
  • (added) clang/test/CodeGen/ptrauth-function-init-fail.c (+5)
  • (added) clang/test/CodeGen/ptrauth-function-init.c (+31)
  • (added) clang/test/CodeGen/ptrauth-function-lvalue-cast.c (+23)
  • (added) clang/test/CodeGen/ptrauth-function.c (+28)
  • (added) clang/test/CodeGen/ptrauth-weak_import.c (+10)
  • (added) clang/test/CodeGenCXX/ptrauth.cpp (+24)
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...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Jun 3, 2024

@llvm/pr-subscribers-backend-x86

Author: Ahmed Bougacha (ahmedbougacha)

Changes

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

Co-Authored-By: John McCall <[email protected]>


Patch is 37.62 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/93906.diff

24 Files Affected:

  • (modified) clang/include/clang/Basic/CodeGenOptions.h (+4)
  • (modified) clang/include/clang/Basic/DiagnosticDriverKinds.td (+3)
  • (modified) clang/include/clang/Basic/LangOptions.h (+2)
  • (modified) clang/include/clang/Basic/PointerAuthOptions.h (+136)
  • (modified) clang/include/clang/Frontend/CompilerInvocation.h (+10)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+1-2)
  • (modified) clang/lib/CodeGen/CGCall.cpp (+3)
  • (modified) clang/lib/CodeGen/CGCall.h (+22-6)
  • (modified) clang/lib/CodeGen/CGExpr.cpp (+9-8)
  • (modified) clang/lib/CodeGen/CGExprConstant.cpp (+18-1)
  • (modified) clang/lib/CodeGen/CGPointerAuth.cpp (+51)
  • (added) clang/lib/CodeGen/CGPointerAuthInfo.h (+96)
  • (modified) clang/lib/CodeGen/CodeGenFunction.cpp (+22)
  • (modified) clang/lib/CodeGen/CodeGenFunction.h (+7)
  • (modified) clang/lib/CodeGen/CodeGenModule.h (+34)
  • (modified) clang/lib/Frontend/CompilerInvocation.cpp (+36)
  • (modified) clang/lib/Headers/ptrauth.h (+34)
  • (added) clang/test/CodeGen/ptrauth-function-attributes.c (+13)
  • (added) clang/test/CodeGen/ptrauth-function-init-fail.c (+5)
  • (added) clang/test/CodeGen/ptrauth-function-init.c (+31)
  • (added) clang/test/CodeGen/ptrauth-function-lvalue-cast.c (+23)
  • (added) clang/test/CodeGen/ptrauth-function.c (+28)
  • (added) clang/test/CodeGen/ptrauth-weak_import.c (+10)
  • (added) clang/test/CodeGenCXX/ptrauth.cpp (+24)
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...
[truncated]

@ahatanak ahatanak requested review from asl, atrosinenko and kovdan01 June 3, 2024 17:33
@asl asl requested review from kbeyls and smithp35 June 3, 2024 17:41
@llvmbot llvmbot added backend:X86 clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' labels Jun 12, 2024
@asl
Copy link
Collaborator

asl commented Jun 13, 2024

@ahatanak @ahmedbougacha Looks like there are some conflicts that should be resolved

// 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
Copy link
Contributor

@kovdan01 kovdan01 Jun 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm OK with any Apple-specific RUN lines if you really want to have all of them, but this one and the next one seem to duplicate each other. I suppose there should be a separate test which checks that arm64e target sets desired ptrauth flags, and then you can just use either arm64e or arm64 with explicitly specified flags in RUN lines in codegen tests.

Feel free to ignore - everything Apple-related is not what I'm going to debate :)

@ahmedbougacha ahmedbougacha force-pushed the users/ahmedbougacha/ptrauth-sign-constant branch from 33cdfdd to 6e7ba73 Compare June 19, 2024 01:02
@asl asl requested a review from kovdan01 June 20, 2024 01:27
Copy link
Contributor

@kovdan01 kovdan01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but this needs a rebase - the base branch was updated, and now the PR contains unrelated changes.

@ahmedbougacha ahmedbougacha force-pushed the users/ahmedbougacha/ptrauth-sign-constant branch from 1201821 to 68d0bb2 Compare June 20, 2024 19:02
Base automatically changed from users/ahmedbougacha/ptrauth-sign-constant to main June 20, 2024 19:09
ahatanak and others added 6 commits June 20, 2024 12:15
- Remove unneeded declarations.
- Fix includes.
- Use `llvm::to_underlying`.
- Fix variable names.
- Fix size and type of bit-fields.
- Add test case for `err_drv_ptrauth_not_supported`.
- Remove unused diagnostic.
- Change the size of Key fields to 2 bits.
- Stop passing the default argument to CGCallee's constructor.
- Remove unused functions and variables.
- Add a test for indirect calls.
- Run tests on linux too.
@ahmedbougacha ahmedbougacha force-pushed the users/ahmedbougacha/ptrauth-function-pointers branch from 06529d3 to 99b2bf2 Compare June 20, 2024 19:43
Copy link

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff 7c814c13d0df6dbd0ef6a8b2be214d3f6edbb566 99b2bf2e1f70ed6f374a380f91a707c2acd8a318 --extensions 'cpp,h,c' -- 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 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
View the diff from clang-format here.
diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h
index aaad4a2b2b..2851958a80 100644
--- a/clang/include/clang/Basic/PointerAuthOptions.h
+++ b/clang/include/clang/Basic/PointerAuthOptions.h
@@ -35,12 +35,7 @@ public:
   /// 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
-  };
+  enum class ARM8_3Key : unsigned { ASIA = 0, ASIB = 1, ASDA = 2, ASDB = 3 };
 
   /// Forms of extra discrimination.
   enum class Discrimination : unsigned {

@ahatanak ahatanak merged commit e23250e into main Jun 21, 2024
6 of 7 checks passed
@ahatanak ahatanak deleted the users/ahmedbougacha/ptrauth-function-pointers branch June 21, 2024 17:20
@ahmedbougacha ahmedbougacha restored the users/ahmedbougacha/ptrauth-function-pointers branch June 21, 2024 17:44
@ahmedbougacha ahmedbougacha deleted the users/ahmedbougacha/ptrauth-function-pointers branch June 21, 2024 17:45
AlexisPerry pushed a commit to llvm-project-tlp/llvm-project that referenced this pull request Jul 9, 2024
… calls (llvm#93906)

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

Co-Authored-By: John McCall <[email protected]>
Comment on lines +81 to +82
if (ConstantDiscriminatorOrNone)
ConstantDiscriminator = *ConstantDiscriminatorOrNone;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ahmedbougacha @ahatanak The static verifier is reporting that 'ConstantDiscriminator' is not initialized when taking the false branch of that if. Is that possible?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikerice1969 The logic is a bit convoluted here. But essentially this field is only initialized when getOtherDiscrimination() == Discrimination::Constant. And there is assert above for that and below in getConstantDiscrimination(). But probably it would be better to initialize it regardless.

@hanickadot
Copy link
Contributor

When this change is building at Compiler Explorer I noticed it spams build output with warnings:

In file included from /root/llvm-project/clang/include/clang/AST/Type.h:31,
                 from /root/llvm-project/clang/include/clang/AST/DeclarationName.h:16,
                 from /root/llvm-project/clang/include/clang/AST/DeclBase.h:19,
                 from /root/llvm-project/clang/include/clang/AST/Decl.h:20,
                 from /root/llvm-project/clang/tools/libclang/CXExtractAPI.cpp:20:
/root/llvm-project/clang/include/clang/Basic/PointerAuthOptions.h:61:8: warning: 'clang::PointerAuthSchema::TheKind' is too small to hold all values of 'enum class clang::PointerAuthSchema::Kind'
   61 |   Kind TheKind : 2;
      |        ^~~~~~~
/root/llvm-project/clang/include/clang/Basic/PointerAuthOptions.h:65:29: warning: 'clang::PointerAuthSchema::SelectedAuthenticationMode' is too small to hold all values of 'enum class clang::PointerAuthenticationMode'
   65 |   PointerAuthenticationMode SelectedAuthenticationMode : 2;
      |                             ^~~~~~~~~~~~~~~~~~~~~~~~~~
/root/llvm-project/clang/include/clang/Basic/PointerAuthOptions.h:66:18: warning: 'clang::PointerAuthSchema::DiscriminationKind' is too small to hold all values of 'enum class clang::PointerAuthSchema::Discrimination'
   66 |   Discrimination DiscriminationKind : 2;
      |                  ^~~~~~~~~~~~~~~~~~
      ```
compiler-explorar pipe line builds it with GCC 9.2

@asl
Copy link
Collaborator

asl commented Jul 22, 2024

@ahatanak Will you please take a look into all these issues?

@ahatanak
Copy link
Collaborator

Do we have to fix this just to appease gcc 9.2? It looks like newer versions of gcc (after 9.3) don't emit the warning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:AArch64 backend:X86 clang:codegen IR generation bugs: mangling, exceptions, etc. clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:headers Headers provided by Clang, e.g. for intrinsics clang Clang issues not falling into any other category
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

7 participants