samitolvanen created this revision.
samitolvanen added reviewers: aaron.ballman, pcc, nickdesaulniers, ardb, kees, 
joaomoreira.
Herald added subscribers: jdoerfert, martong.
Herald added a project: All.
samitolvanen requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

With `-fsanitize=kcfi`, disabling indirect call checking using the
`no_sanitize` attribute or the sanitizer special case list is not
fine-grained enough in cases where we want to disable checking
only for specific calls. The `kcfi_unchecked` attribute applies to
functions and function pointers and allows disabling indirect calls
made using an annotated pointer.

Depends on D119296 <https://reviews.llvm.org/D119296>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D122673

Files:
  clang/include/clang/AST/Type.h
  clang/include/clang/AST/TypeProperties.td
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/CodeGen/CGFunctionInfo.h
  clang/lib/AST/ASTContext.cpp
  clang/lib/AST/ASTStructuralEquivalence.cpp
  clang/lib/AST/TypePrinter.cpp
  clang/lib/CodeGen/CGCall.cpp
  clang/lib/CodeGen/CGExpr.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaType.cpp
  clang/lib/Serialization/ASTWriter.cpp
  clang/test/CodeGen/kcfi_unchecked.c
  clang/test/CodeGen/kcfi_unchecked.cpp
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/Sema/attr-kcfi_unchecked.c
  clang/test/Sema/attr-kcfi_unchecked.cpp

Index: clang/test/Sema/attr-kcfi_unchecked.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/attr-kcfi_unchecked.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify -std=c++11 -fsyntax-only %s
+
+typedef void (*fn_unchecked_t)(void) [[clang::kcfi_unchecked]]; // no-warning
+typedef void (*fn_t)(void);
+
+int [[clang::kcfi_unchecked]] i;               // expected-error {{'kcfi_unchecked' attribute cannot be applied to types}}
+void f1(double i [[clang::kcfi_unchecked]]) {} // expected-warning {{'kcfi_unchecked' attribute only applies to functions and function pointers}}
+
+void f2(fn_t f) {
+  fn_unchecked_t p = f; // expected-error {{cannot initialize a variable of type}}
+  p();                  // no-warning
+}
+
+[[clang::kcfi_unchecked("argument")]] int f3(); // expected-error {{'kcfi_unchecked' attribute takes no arguments}}
Index: clang/test/Sema/attr-kcfi_unchecked.c
===================================================================
--- /dev/null
+++ clang/test/Sema/attr-kcfi_unchecked.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 %s -verify -fsyntax-only
+
+int a __attribute__((kcfi_unchecked));   // expected-warning {{'kcfi_unchecked' attribute only applies to functions and function pointers}}
+void *p __attribute__((kcfi_unchecked)); // expected-warning {{'kcfi_unchecked' attribute only applies to functions and function pointers}}
+void (*f)(void) __attribute__((kcfi_unchecked));
+void (*g)(void) __attribute__((kcfi_unchecked("argument"))); // expected-error {{'kcfi_unchecked' attribute takes no arguments}}
+
+typedef unsigned long l_unchecked_t __attribute__((kcfi_unchecked)); // expected-warning {{'kcfi_unchecked' attribute only applies to functions and function pointers}}
+typedef int (*f_unchecked_t)(void) __attribute__((kcfi_unchecked));
+
+void f1(unsigned long p __attribute__((kcfi_unchecked))) {} // expected-warning {{'kcfi_unchecked' attribute only applies to functions and function pointers}}
+void f2(void *p __attribute__((kcfi_unchecked))) {}         // expected-warning {{'kcfi_unchecked' attribute only applies to functions and function pointers}}
+void f3(void (*p)(void) __attribute__((kcfi_unchecked))) {}
+
+void f4(void) __attribute__((kcfi_unchecked)) {}
+void test(void) {
+  ((void (*__attribute__((kcfi_unchecked)))(void))f4)();
+}
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -76,6 +76,7 @@
 // CHECK-NEXT: IFunc (SubjectMatchRule_function)
 // CHECK-NEXT: InitPriority (SubjectMatchRule_variable)
 // CHECK-NEXT: InternalLinkage (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
+// CHECK-NEXT: KCFIUnchecked (SubjectMatchRule_hasType_functionType)
 // CHECK-NEXT: LTOVisibilityPublic (SubjectMatchRule_record)
 // CHECK-NEXT: Leaf (SubjectMatchRule_function)
 // CHECK-NEXT: LoaderUninitialized (SubjectMatchRule_variable_is_global)
Index: clang/test/CodeGen/kcfi_unchecked.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGen/kcfi_unchecked.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -o - %s | FileCheck %s
+
+#if !__has_feature(kcfi)
+#error Missing kcfi
+#endif
+
+// CHECK-LABEL: define{{.*}} i32 @_Z2f1v(){{.*}} prefix i32 [[#%d,HASH:]]
+int f1(void) { return 0; }
+
+// CHECK-LABEL: define{{.*}} i32 @_Z2f2v(){{.*}} prefix i32 [[#%d,HASH2:]]
+unsigned int f2(void) { return 1; }
+
+template <typename T> int call(T p) { return p(); }
+
+// CHECK-LABEL: define{{.*}} i32 @_Z7checkedv()
+int checked() {
+  // CHECK: call void @llvm.kcfi.check(i8* %[[#]], i32 [[#HASH]])
+  // CHECK-NEXT: call{{.*}} i32 %[[#]]()
+  return ({ &f1; })();
+}
+
+// CHECK-LABEL: define{{.*}} i32 @_Z16checked_templatev()
+int checked_template() {
+  // CHECK: call{{.*}} i32 @_Z4callIPFjvEEiT_(i32 ()*{{.*}} @_Z2f2v)
+  return call(f2);
+}
+
+// CHECK-LABEL: define{{.*}} i32 @_Z4callIPFjvEEiT_(i32 ()*{{.*}} %p)
+// CHECK: call void @llvm.kcfi.check(i8* %[[#]], i32 [[#HASH2]])
+// CHECK-NEXT: call{{.*}} i32 %[[#]]()
+
+// CHECK-LABEL: define{{.*}} i32 @_Z19unchecked_template1v()
+int unchecked_template1() {
+  using unchecked_t = int (*)() [[clang::kcfi_unchecked]];
+  unchecked_t p = (unchecked_t)f1;
+  // CHECK: call{{.*}} i32 @_Z4callIPFivEEiT_(i32 ()*{{.*}} %[[#]])
+  return call(p);
+}
+
+// CHECK-LABEL: define{{.*}} i32 @_Z4callIPFivEEiT_(i32 ()*{{.*}} %p)
+// CHECK-NOT: call void @llvm.kcfi.check
+// CHECK: call{{.*}} i32 %[[#]]()
+
+// CHECK-LABEL: define{{.*}} i32 @_Z19unchecked_template2v()
+int unchecked_template2() {
+  using unchecked_t = int (*)() [[clang::kcfi_unchecked]];
+  // CHECK: call{{.*}} i32 @_Z4callIPFivEEiT_(i32 ()*{{.*}} @_Z2f2v)
+  return call((unchecked_t)f2);
+}
Index: clang/test/CodeGen/kcfi_unchecked.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/kcfi_unchecked.c
@@ -0,0 +1,126 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -o - %s | FileCheck %s
+
+#if !__has_feature(kcfi)
+#error Missing kcfi
+#endif
+
+// CHECK-LABEL: define dso_local i32 @f1(){{.*}} prefix i32 [[#%d,HASH:]]
+int f1(void) { return 0; }
+
+typedef int (*fn_t)(void);
+typedef int (*fn_unchecked_t)(void) __attribute__((kcfi_unchecked));
+
+typedef typeof(f1) *fn_typeof_t;
+typedef typeof(f1) *__attribute__((kcfi_unchecked)) fn_typeof_unchecked_t;
+
+// CHECK-LABEL: define{{.*}} i32 @checked()
+int checked(void) {
+  // CHECK: call void @llvm.kcfi.check(i8* %[[#]], i32 [[#HASH]])
+  // CHECK-NEXT: call i32 %
+  return ({ &f1; })();
+}
+// CHECK-LABEL: define{{.*}} i32 @checked_typedef_cast()
+int checked_typedef_cast(void) {
+  // CHECK: call void @llvm.kcfi.check(i8* %[[#]], i32 [[#HASH]])
+  // CHECK-NEXT: call i32 %
+  return ({ (fn_t) & f1; })();
+}
+// CHECK-LABEL: define{{.*}} i32 @checked_outside_typedef_cast()
+int checked_outside_typedef_cast(void) {
+  // CHECK: call void @llvm.kcfi.check(i8* %[[#]], i32 [[#HASH]])
+  // CHECK-NEXT: call i32 %
+  return ((fn_t)({ &f1; }))();
+}
+// CHECK-LABEL: define{{.*}} i32 @checked_typeof_typedef_cast()
+int checked_typeof_typedef_cast(void) {
+  // CHECK: call void @llvm.kcfi.check(i8* %[[#]], i32 [[#HASH]])
+  // CHECK-NEXT: call i32 %
+  return ({ (fn_typeof_t) & f1; })();
+}
+// CHECK-LABEL: define{{.*}} i32 @checked_var()
+int checked_var(void) {
+  // CHECK: call void @llvm.kcfi.check(i8* %[[#]], i32 [[#HASH]])
+  // CHECK-NEXT: call i32 %
+  fn_t p = f1;
+  return p();
+}
+// CHECK-LABEL: define{{.*}} i32 @checked_param(i32 ()*
+int checked_param(fn_t p) {
+  // CHECK: call void @llvm.kcfi.check(i8* %[[#]], i32 [[#HASH]])
+  // CHECK-NEXT: call i32 %
+  return p();
+}
+// CHECK-LABEL: define{{.*}} i32 @unchecked_typedef_cast()
+int unchecked_typedef_cast(void) {
+  // CHECK-NOT: call void @llvm.kcfi.check
+  // CHECK: call i32 %
+  return ({ (fn_unchecked_t) & f1; })();
+}
+// CHECK-LABEL: define{{.*}} i32 @unchecked_outside_typedef_cast()
+int unchecked_outside_typedef_cast(void) {
+  // CHECK-NOT: call void @llvm.kcfi.check
+  // CHECK: call i32 %
+  return ((fn_unchecked_t)({ &f1; }))();
+}
+// CHECK-LABEL: define{{.*}} i32 @unchecked_typeof_typedef_cast()
+int unchecked_typeof_typedef_cast(void) {
+  // CHECK-NOT: call void @llvm.kcfi.check
+  // CHECK: call i32 %
+  return ({ (fn_typeof_unchecked_t) & f1; })();
+}
+// CHECK-LABEL: define{{.*}} i32 @unchecked_compound_var()
+int unchecked_compound_var(void) {
+  // CHECK-NOT: call void @llvm.kcfi.check
+  // CHECK: call i32 %
+  return ({
+    fn_unchecked_t p = (fn_unchecked_t)f1;
+    p;
+  })();
+}
+// CHECK-LABEL: define{{.*}} i32 @unchecked_compound_local_typedef_cast()
+int unchecked_compound_local_typedef_cast(void) {
+  // CHECK-NOT: call void @llvm.kcfi.check
+  // CHECK: call i32 %
+  return ({
+    typedef typeof(f1) *__attribute__((kcfi_unchecked)) fn_local_unchecked_t;
+    (fn_local_unchecked_t) & f1;
+  })();
+}
+// CHECK-LABEL: define{{.*}} i32 @unchecked_var()
+int unchecked_var(void) {
+  // CHECK-NOT: call void @llvm.kcfi.check
+  // CHECK: call i32 %
+  fn_unchecked_t p = (fn_unchecked_t)f1;
+  return p();
+}
+// CHECK-LABEL: define{{.*}} i32 @unchecked_var_attr()
+int unchecked_var_attr(void) {
+  // CHECK-NOT: call void @llvm.kcfi.check
+  // CHECK: call i32 %
+  fn_t __attribute__((kcfi_unchecked)) p = (fn_t __attribute__((kcfi_unchecked)))f1;
+  return p();
+}
+// CHECK-LABEL: define{{.*}} i32 @unchecked_type_cast()
+int unchecked_type_cast(void) {
+  // CHECK-NOT: call void @llvm.kcfi.check
+  // CHECK: call i32 @f1
+  return ((fn_unchecked_t)f1)();
+}
+// CHECK-LABEL: define{{.*}} i32 @unchecked_var_cast()
+int unchecked_var_cast(void) {
+  // CHECK-NOT: call void @llvm.kcfi.check
+  // CHECK: call i32 @f1
+  return ((fn_t __attribute__((kcfi_unchecked)))f1)();
+}
+// CHECK-LABEL: define{{.*}} i32 @unchecked_cast()
+int unchecked_cast(void) {
+  // CHECK-NOT: call void @llvm.kcfi.check
+  // CHECK: call i32 @f1
+  return ((int (*__attribute__((kcfi_unchecked)))(void))f1)();
+}
+// CHECK-LABEL: define{{.*}} i32 @unchecked_param(i32 ()*
+int unchecked_param(fn_t __attribute__((kcfi_unchecked)) p) {
+  // CHECK-NOT: call void @llvm.kcfi.check
+  // CHECK: call i32 %
+  return p();
+}
Index: clang/lib/Serialization/ASTWriter.cpp
===================================================================
--- clang/lib/Serialization/ASTWriter.cpp
+++ clang/lib/Serialization/ASTWriter.cpp
@@ -612,6 +612,7 @@
   Abv->Add(BitCodeAbbrevOp(0));                         // NoCallerSavedRegs
   Abv->Add(BitCodeAbbrevOp(0));                         // NoCfCheck
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // CmseNSCall
+  Abv->Add(BitCodeAbbrevOp(0));                         // KCFIUnchecked
   // FunctionProtoType
   Abv->Add(BitCodeAbbrevOp(0));                         // IsVariadic
   Abv->Add(BitCodeAbbrevOp(0));                         // HasTrailingReturn
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -136,6 +136,7 @@
   case ParsedAttr::AT_CmseNSCall:                                              \
   case ParsedAttr::AT_AnyX86NoCallerSavedRegisters:                            \
   case ParsedAttr::AT_AnyX86NoCfCheck:                                         \
+  case ParsedAttr::AT_KCFIUnchecked:                                           \
     CALLING_CONV_ATTRS_CASELIST
 
 // Microsoft-specific type qualifiers.
@@ -7605,6 +7606,21 @@
     return true;
   }
 
+  if (attr.getKind() == ParsedAttr::AT_KCFIUnchecked) {
+    if (S.CheckAttrTarget(attr) || S.CheckAttrNoArgs(attr))
+      return true;
+
+    // If this is not a function type, warning will be asserted by subject
+    // check.
+    if (!unwrapped.isFunctionType())
+      return true;
+
+    FunctionType::ExtInfo EI =
+        unwrapped.get()->getExtInfo().withKCFIUnchecked(true);
+    type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
+    return true;
+  }
+
   if (attr.getKind() == ParsedAttr::AT_Regparm) {
     unsigned value;
     if (S.CheckRegparmAttr(attr, value))
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -8467,6 +8467,9 @@
   case ParsedAttr::AT_AnyX86NoCfCheck:
     handleNoCfCheckAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_KCFIUnchecked:
+    handleSimpleAttribute<KCFIUncheckedAttr>(S, D, AL);
+    break;
   case ParsedAttr::AT_NoThrow:
     if (!AL.isUsedAsTypeAttr())
       handleSimpleAttribute<NoThrowAttr>(S, D, AL);
Index: clang/lib/CodeGen/CGExpr.cpp
===================================================================
--- clang/lib/CodeGen/CGExpr.cpp
+++ clang/lib/CodeGen/CGExpr.cpp
@@ -5315,9 +5315,12 @@
     }
   }
 
-  if (SanOpts.has(SanitizerKind::KCFI) && IsIndirectCall)
-    EmitKCFICheck(Callee.getFunctionPointer(),
-                  CGM.CreateKCFITypeId(QualType(FnType, 0)));
+  if (SanOpts.has(SanitizerKind::KCFI) && IsIndirectCall) {
+    if ((!TargetDecl || !TargetDecl->hasAttr<KCFIUncheckedAttr>()) &&
+        !getFunctionExtInfo(CalleeType).getKCFIUnchecked())
+      EmitKCFICheck(Callee.getFunctionPointer(),
+                    CGM.CreateKCFITypeId(QualType(FnType, 0)));
+  }
 
   CallArgList Args;
   if (Chain)
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ clang/lib/CodeGen/CGCall.cpp
@@ -825,6 +825,7 @@
   FI->ReturnsRetained = info.getProducesResult();
   FI->NoCallerSavedRegs = info.getNoCallerSavedRegs();
   FI->NoCfCheck = info.getNoCfCheck();
+  FI->KCFIUnchecked = info.getKCFIUnchecked();
   FI->Required = required;
   FI->HasRegParm = info.getHasRegParm();
   FI->RegParm = info.getRegParm();
Index: clang/lib/AST/TypePrinter.cpp
===================================================================
--- clang/lib/AST/TypePrinter.cpp
+++ clang/lib/AST/TypePrinter.cpp
@@ -1004,6 +1004,8 @@
     OS << " __attribute__((no_caller_saved_registers))";
   if (Info.getNoCfCheck())
     OS << " __attribute__((nocf_check))";
+  if (Info.getKCFIUnchecked())
+    OS << " __attribute__((kcfi_unchecked))";
 }
 
 void TypePrinter::printFunctionNoProtoBefore(const FunctionNoProtoType *T,
@@ -1728,6 +1730,9 @@
   // FIXME: When Sema learns to form this AttributedType, avoid printing the
   // attribute again in printFunctionProtoAfter.
   case attr::AnyX86NoCfCheck: OS << "nocf_check"; break;
+  case attr::KCFIUnchecked:
+    OS << "kcfi_unchecked";
+    break;
   case attr::CDecl: OS << "cdecl"; break;
   case attr::FastCall: OS << "fastcall"; break;
   case attr::StdCall: OS << "stdcall"; break;
Index: clang/lib/AST/ASTStructuralEquivalence.cpp
===================================================================
--- clang/lib/AST/ASTStructuralEquivalence.cpp
+++ clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -619,6 +619,8 @@
     return false;
   if (EI1.getNoCfCheck() != EI2.getNoCfCheck())
     return false;
+  if (EI1.getKCFIUnchecked() != EI2.getKCFIUnchecked())
+    return false;
 
   return true;
 }
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -10043,6 +10043,8 @@
     return {};
   if (lbaseInfo.getNoCfCheck() != rbaseInfo.getNoCfCheck())
     return {};
+  if (lbaseInfo.getKCFIUnchecked() != rbaseInfo.getKCFIUnchecked())
+    return {};
 
   // FIXME: some uses, e.g. conditional exprs, really want this to be 'both'.
   bool NoReturn = lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn();
Index: clang/include/clang/CodeGen/CGFunctionInfo.h
===================================================================
--- clang/include/clang/CodeGen/CGFunctionInfo.h
+++ clang/include/clang/CodeGen/CGFunctionInfo.h
@@ -589,6 +589,9 @@
   /// Log 2 of the maximum vector width.
   unsigned MaxVectorWidth : 4;
 
+  /// Whether this function has kcfi_unchecked attribute.
+  unsigned KCFIUnchecked : 1;
+
   RequiredArgs Required;
 
   /// The struct representing all arguments passed in memory.  Only used when
@@ -677,6 +680,9 @@
   /// Whether this function has nocf_check attribute.
   bool isNoCfCheck() const { return NoCfCheck; }
 
+  /// Whether this function has kcfi_unchecked attribute.
+  bool isKCFIUnchecked() const { return KCFIUnchecked; }
+
   /// getASTCallingConvention() - Return the AST-specified calling
   /// convention.
   CallingConv getASTCallingConvention() const {
@@ -703,7 +709,7 @@
     return FunctionType::ExtInfo(isNoReturn(), getHasRegParm(), getRegParm(),
                                  getASTCallingConvention(), isReturnsRetained(),
                                  isNoCallerSavedRegs(), isNoCfCheck(),
-                                 isCmseNSCall());
+                                 isCmseNSCall(), isKCFIUnchecked());
   }
 
   CanQualType getReturnType() const { return getArgsBuffer()[0].type; }
@@ -756,6 +762,7 @@
     ID.AddInteger(RegParm);
     ID.AddBoolean(NoCfCheck);
     ID.AddBoolean(CmseNSCall);
+    ID.AddBoolean(KCFIUnchecked);
     ID.AddInteger(Required.getOpaqueData());
     ID.AddBoolean(HasExtParameterInfos);
     if (HasExtParameterInfos) {
@@ -784,6 +791,7 @@
     ID.AddInteger(info.getRegParm());
     ID.AddBoolean(info.getNoCfCheck());
     ID.AddBoolean(info.getCmseNSCall());
+    ID.AddBoolean(info.getKCFIUnchecked());
     ID.AddInteger(required.getOpaqueData());
     ID.AddBoolean(!paramInfos.empty());
     if (!paramInfos.empty()) {
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -5434,6 +5434,31 @@
 }];
 }
 
+def KCFIUncheckedDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+The ``kcfi_unchecked`` attribute causes Clang to not emit :ref:`KCFI<kcfi>`
+checks for indirect calls made through annotated function types.
+
+.. code-block:: c
+
+  int f1(void) { return 0; }
+
+  int (*p1)(void) = f1;
+  p1();  // checked
+
+  int (*p2)(void) __attribute__((kcfi_unchecked)) = f1;
+  p2();  // unchecked
+
+  typedef typeof(f1) * __attribute__((kcfi_unchecked) unchecked_t;
+  ((unchecked_t)f1)();  // unchecked
+
+  unchecked_t p3 = f1;
+  p3();  // unchecked
+
+}];
+}
+
 def ReinitializesDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -3036,6 +3036,12 @@
   let SimpleHandler = 1;
 }
 
+def KCFIUnchecked : DeclOrTypeAttr, TargetSpecificAttr<TargetArch<["aarch64", "aarch64_be", "x86_64"]>> {
+  let Spellings = [Clang<"kcfi_unchecked">];
+  let Subjects = SubjectList<[FunctionLike]>;
+  let Documentation = [KCFIUncheckedDocs];
+}
+
 // C/C++ Thread safety attributes (e.g. for deadlock, data race checking)
 // Not all of these attributes will be given a [[]] spelling. The attributes
 // which require access to function parameter names cannot use the [[]] spelling
Index: clang/include/clang/AST/TypeProperties.td
===================================================================
--- clang/include/clang/AST/TypeProperties.td
+++ clang/include/clang/AST/TypeProperties.td
@@ -287,6 +287,9 @@
   def : Property<"cmseNSCall", Bool> {
     let Read = [{ node->getExtInfo().getCmseNSCall() }];
   }
+  def : Property<"kcfiUnchecked", Bool> {
+    let Read = [{ node->getExtInfo().getKCFIUnchecked() }];
+  }
 }
 
 let Class = FunctionNoProtoType in {
@@ -294,7 +297,7 @@
     auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
                                          callingConvention, producesResult,
                                          noCallerSavedRegs, noCfCheck,
-                                         cmseNSCall);
+                                         cmseNSCall, kcfiUnchecked);
     return ctx.getFunctionNoProtoType(returnType, extInfo);
   }]>;
 }
@@ -328,7 +331,7 @@
     auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
                                          callingConvention, producesResult,
                                          noCallerSavedRegs, noCfCheck,
-                                         cmseNSCall);
+                                         cmseNSCall, kcfiUnchecked);
     FunctionProtoType::ExtProtoInfo epi;
     epi.ExtInfo = extInfo;
     epi.Variadic = variadic;
Index: clang/include/clang/AST/Type.h
===================================================================
--- clang/include/clang/AST/Type.h
+++ clang/include/clang/AST/Type.h
@@ -1591,7 +1591,7 @@
 
     /// Extra information which affects how the function is called, like
     /// regparm and the calling convention.
-    unsigned ExtInfo : 13;
+    unsigned ExtInfo : 14;
 
     /// The ref-qualifier associated with a \c FunctionProtoType.
     ///
@@ -1829,7 +1829,7 @@
   Type(TypeClass tc, QualType canon, TypeDependence Dependence)
       : ExtQualsTypeCommonBase(this,
                                canon.isNull() ? QualType(this_(), 0) : canon) {
-    static_assert(sizeof(*this) <= 8 + sizeof(ExtQualsTypeCommonBase),
+    static_assert(sizeof(*this) <= 16 + sizeof(ExtQualsTypeCommonBase),
                   "changing bitfields changed sizeof(Type)!");
     static_assert(alignof(decltype(*this)) % sizeof(void *) == 0,
                   "Insufficient alignment!");
@@ -3672,6 +3672,8 @@
 
     // |  CC  |noreturn|produces|nocallersavedregs|regparm|nocfcheck|cmsenscall|
     // |0 .. 4|   5    |    6   |       7         |8 .. 10|    11   |    12    |
+    // |kcfiunchecked|
+    // |     13      |
     //
     // regparm is either 0 (no regparm attribute) or the regparm value+1.
     enum { CallConvMask = 0x1F };
@@ -3684,6 +3686,7 @@
     };
     enum { NoCfCheckMask = 0x800 };
     enum { CmseNSCallMask = 0x1000 };
+    enum { KCFIUncheckedMask = 0x2000 };
     uint16_t Bits = CC_C;
 
     ExtInfo(unsigned Bits) : Bits(static_cast<uint16_t>(Bits)) {}
@@ -3693,14 +3696,15 @@
     // have all the elements (when reading an AST file for example).
     ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc,
             bool producesResult, bool noCallerSavedRegs, bool NoCfCheck,
-            bool cmseNSCall) {
+            bool cmseNSCall, bool kcfiUnchecked) {
       assert((!hasRegParm || regParm < 7) && "Invalid regparm value");
       Bits = ((unsigned)cc) | (noReturn ? NoReturnMask : 0) |
              (producesResult ? ProducesResultMask : 0) |
              (noCallerSavedRegs ? NoCallerSavedRegsMask : 0) |
              (hasRegParm ? ((regParm + 1) << RegParmOffset) : 0) |
              (NoCfCheck ? NoCfCheckMask : 0) |
-             (cmseNSCall ? CmseNSCallMask : 0);
+             (cmseNSCall ? CmseNSCallMask : 0) |
+             (kcfiUnchecked ? KCFIUncheckedMask : 0);
     }
 
     // Constructor with all defaults. Use when for example creating a
@@ -3716,6 +3720,7 @@
     bool getCmseNSCall() const { return Bits & CmseNSCallMask; }
     bool getNoCallerSavedRegs() const { return Bits & NoCallerSavedRegsMask; }
     bool getNoCfCheck() const { return Bits & NoCfCheckMask; }
+    bool getKCFIUnchecked() const { return Bits & KCFIUncheckedMask; }
     bool getHasRegParm() const { return ((Bits & RegParmMask) >> RegParmOffset) != 0; }
 
     unsigned getRegParm() const {
@@ -3772,6 +3777,13 @@
         return ExtInfo(Bits & ~NoCfCheckMask);
     }
 
+    ExtInfo withKCFIUnchecked(bool kcfiUnchecked) const {
+      if (kcfiUnchecked)
+        return ExtInfo(Bits | KCFIUncheckedMask);
+      else
+        return ExtInfo(Bits & ~KCFIUncheckedMask);
+    }
+
     ExtInfo withRegParm(unsigned RegParm) const {
       assert(RegParm < 7 && "Invalid regparm value");
       return ExtInfo((Bits & ~RegParmMask) |
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to