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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits