https://github.com/PiJoules updated https://github.com/llvm/llvm-project/pull/170725
>From d72b45611983a1d306e193b1469cddeb3251005b Mon Sep 17 00:00:00 2001 From: Leonard Chan <[email protected]> Date: Thu, 4 Dec 2025 11:20:16 -0800 Subject: [PATCH] [clang] Apply cfi_unchecked_callee rules to -fsanitize=function Allow the normal rules for preventing instrumentation on indirect calls to `cfi_unchecked_callee` function types and `cfi_unchecked_callee` functions when using `-fsanitize=function`. While it's technically separate from `-fsanitize=cfi`, this particular UBSan mode checks for similar control flow bugs so it makes sense to also prevent those control flow checks from being added onto `cfi_unchecked_callee` functions. --- clang/docs/ReleaseNotes.rst | 3 +++ clang/include/clang/Basic/AttrDocs.td | 4 +++- clang/lib/CodeGen/CGExpr.cpp | 7 ++++--- clang/lib/CodeGen/CodeGenFunction.cpp | 3 ++- clang/test/CodeGen/ubsan-function.cpp | 17 +++++++++++++++++ 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c153c9c77b09f..abc6dab2f0614 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -370,6 +370,9 @@ Attribute Changes in Clang attribute, allowing the attribute to only be attached to the declaration. Prior, this would be treated as an error where the definition and declaration would have differing types. +- Instrumentation added by ``-fsanitize=function`` will also be omitted for indirect calls to function + pointers and function declarations marked with ``[[clang::cfi_unchecked_callee]]``. + - New format attributes ``gnu_printf``, ``gnu_scanf``, ``gnu_strftime`` and ``gnu_strfmon`` are added as aliases for ``printf``, ``scanf``, ``strftime`` and ``strfmon``. (#GH16219) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 2b00e43d381eb..812b48058d189 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -7065,7 +7065,9 @@ def CFIUncheckedCalleeDocs : Documentation { let Content = [{ ``cfi_unchecked_callee`` is a function type attribute which prevents the compiler from instrumenting `Control Flow Integrity <https://clang.llvm.org/docs/ControlFlowIntegrity.html>`_ checks on indirect -function calls. Specifically, the attribute has the following semantics: +function calls. This also includes control flow checks added by +`-fsanitize=function <https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#available-checks>`. +Specifically, the attribute has the following semantics: 1. Indirect calls to a function type with this attribute will not be instrumented with CFI. That is, the indirect call will not be checked. Note that this only changes the behavior for indirect calls diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 27ee96cb6dc82..f1fc73bdcb3bd 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -6616,9 +6616,12 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, CGCallee Callee = OrigCallee; + bool CFIUnchecked = + CalleeType->hasPointeeToCFIUncheckedCalleeFunctionType(); + if (SanOpts.has(SanitizerKind::Function) && (!TargetDecl || !isa<FunctionDecl>(TargetDecl)) && - !isa<FunctionNoProtoType>(PointeeType)) { + !isa<FunctionNoProtoType>(PointeeType) && !CFIUnchecked) { if (llvm::Constant *PrefixSig = CGM.getTargetCodeGenInfo().getUBSanFunctionSignature(CGM)) { auto CheckOrdinal = SanitizerKind::SO_Function; @@ -6697,8 +6700,6 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, FD && DeviceKernelAttr::isOpenCLSpelling(FD->getAttr<DeviceKernelAttr>())) CGM.getTargetCodeGenInfo().setOCLKernelStubCallingConvention(FnType); - bool CFIUnchecked = CalleeType->hasPointeeToCFIUncheckedCalleeFunctionType(); - // If we are checking indirect calls and this call is indirect, check that the // function pointer is a member of the bit set for the function type. if (SanOpts.has(SanitizerKind::CFIICall) && diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index ac25bd95f0463..0e3e7b11c4a26 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1037,7 +1037,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, // If we are checking function types, emit a function type signature as // prologue data. - if (FD && SanOpts.has(SanitizerKind::Function)) { + if (FD && SanOpts.has(SanitizerKind::Function) && + !FD->getType()->isCFIUncheckedCalleeFunctionType()) { if (llvm::Constant *PrologueSig = getPrologueSignature(CGM, FD)) { llvm::LLVMContext &Ctx = Fn->getContext(); llvm::MDBuilder MDB(Ctx); diff --git a/clang/test/CodeGen/ubsan-function.cpp b/clang/test/CodeGen/ubsan-function.cpp index 76d4237383f83..bc43942635a7b 100644 --- a/clang/test/CodeGen/ubsan-function.cpp +++ b/clang/test/CodeGen/ubsan-function.cpp @@ -41,5 +41,22 @@ void fun() {} // CHECK-NEXT: ret void void caller(void (*f)()) { f(); } +// GNU: define{{.*}} void @_Z4fun2v() #0 { +// MSVC: define{{.*}} void @"?fun2@@YAXXZ"() #0 { +[[clang::cfi_unchecked_callee]] +void fun2() {} + +typedef void (*unchecked_t)() [[clang::cfi_unchecked_callee]]; + +// GNU-LABEL: define{{.*}} void @_Z7caller2PFvvE(ptr noundef %f) +// MSVC-LABEL: define{{.*}} void @"?caller2@@YAXP6AXXZ@Z"(ptr noundef %f) +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ADDR:%.*]] = alloca ptr +// CHECK-NEXT: store ptr %f, ptr [[ADDR]] +// CHECK-NEXT: [[FUNC:%.*]] = load ptr, ptr [[ADDR]] +// CHECK-NEXT: call void [[FUNC]]() +// CHECK-NEXT: ret void +void caller2(unchecked_t f) { f(); } + // GNU: ![[FUNCSAN]] = !{i32 -1056584962, i32 905068220} // MSVC: ![[FUNCSAN]] = !{i32 -1056584962, i32 -1600339357} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
