From b4a52f46b641525e626897593c0a7a19cfc05ded Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Thu, 11 Sep 2025 17:57:49 +0000 Subject: [PATCH 1/2] [X86][KCFI] Do not emit a type prefix for nocf_check functions With indirect branch protection, the `nocf_check` attribute prevents a function from being called indirectly by omitting the ENDBR instruction from the beginning of the function body. As KCFI type prefixes are only needed for indirectly callable functions, don't emit the unnecessary prefix for `nocf_check` functions. --- llvm/lib/Target/X86/X86AsmPrinter.cpp | 5 +-- llvm/test/CodeGen/X86/kcfi-nocf-check.ll | 39 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 llvm/test/CodeGen/X86/kcfi-nocf-check.ll diff --git a/llvm/lib/Target/X86/X86AsmPrinter.cpp b/llvm/lib/Target/X86/X86AsmPrinter.cpp index ff22ee8c86fac..486bf3986cf5a 100644 --- a/llvm/lib/Target/X86/X86AsmPrinter.cpp +++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp @@ -170,8 +170,9 @@ void X86AsmPrinter::emitKCFITypeId(const MachineFunction &MF) { Type = mdconst::extract(MD->getOperand(0)); // If we don't have a type to emit, just emit padding if needed to maintain - // the same alignment for all functions. - if (!Type) { + // the same alignment for all functions. Also skip `nocf_check` functions as + // they are not indirectly callable due to a missing ENDBR. + if (!Type || F.doesNoCfCheck()) { EmitKCFITypePadding(MF, /*HasType=*/false); return; } diff --git a/llvm/test/CodeGen/X86/kcfi-nocf-check.ll b/llvm/test/CodeGen/X86/kcfi-nocf-check.ll new file mode 100644 index 0000000000000..1ce886c1587f8 --- /dev/null +++ b/llvm/test/CodeGen/X86/kcfi-nocf-check.ll @@ -0,0 +1,39 @@ +; RUN: llc -mtriple=x86_64-unknown-unknown -x86-indirect-branch-tracking < %s | FileCheck %s + +; CHECK-LABEL: __cfi_cf_check_func: +; CHECK: movl $12345678, %eax +define void @cf_check_func() !kcfi_type !2 { +; CHECK-LABEL: cf_check_func: +; CHECK: endbr64 +; CHECK: retq +entry: + ret void +} + +; CHECK-NOT: __cfi_notype_cf_check_func: +; CHECK-NOT: movl +define void @notype_cf_check_func() { +; CHECK-LABEL: notype_cf_check_func: +; CHECK: endbr64 +; CHECK: retq +entry: + ret void +} + +; CHECK-NOT: __cfi_nocf_check_func: +; CHECK-NOT: movl +define void @nocf_check_func() #0 !kcfi_type !2 { +; CHECK-LABEL: nocf_check_func: +; CHECK-NOT: endbr64 +; CHECK: retq +entry: + ret void +} + +attributes #0 = { nocf_check } + +!llvm.module.flags = !{!0, !1} + +!0 = !{i32 8, !"cf-protection-branch", i32 1} +!1 = !{i32 4, !"kcfi", i32 1} +!2 = !{i32 12345678} From 55c9356cc687edb97764e014880bfef70a5d901e Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Mon, 15 Sep 2025 19:36:08 +0000 Subject: [PATCH 2/2] Also allow nocf_check only with -fsanitize=kcfi --- clang/lib/Sema/SemaDeclAttr.cpp | 3 ++- clang/lib/Sema/SemaType.cpp | 3 ++- clang/test/Sema/attr-nocf_check.c | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 44906456f3371..3ec74ce0e46e1 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2028,7 +2028,8 @@ static void handleStandardNoReturnAttr(Sema &S, Decl *D, const ParsedAttr &A) { } static void handleNoCfCheckAttr(Sema &S, Decl *D, const ParsedAttr &Attrs) { - if (!S.getLangOpts().CFProtectionBranch) + if (!S.getLangOpts().CFProtectionBranch && + !S.getLangOpts().Sanitize.has(SanitizerKind::KCFI)) S.Diag(Attrs.getLoc(), diag::warn_nocf_check_attribute_ignored); else handleSimpleAttribute(S, D, Attrs); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 0f655d7f684a5..12384aadc1cad 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -7936,7 +7936,8 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr, } if (attr.getKind() == ParsedAttr::AT_AnyX86NoCfCheck) { - if (!S.getLangOpts().CFProtectionBranch) { + if (!S.getLangOpts().CFProtectionBranch && + !S.getLangOpts().Sanitize.has(SanitizerKind::KCFI)) { S.Diag(attr.getLoc(), diag::warn_nocf_check_attribute_ignored); attr.setInvalid(); return true; diff --git a/clang/test/Sema/attr-nocf_check.c b/clang/test/Sema/attr-nocf_check.c index 59ec85ed2629b..1262b87928089 100644 --- a/clang/test/Sema/attr-nocf_check.c +++ b/clang/test/Sema/attr-nocf_check.c @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -triple=x86_64-unknown-unknown -verify -fcf-protection=branch -fsyntax-only %s +// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -verify -fsanitize=kcfi -fsyntax-only %s // Function pointer definition. typedef void (*FuncPointerWithNoCfCheck)(void) __attribute__((nocf_check)); // no-warning