diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2907,6 +2907,13 @@ let Documentation = [MSAllocatorDocs]; } +def CFGuard : InheritableAttr { + let Spellings = [Declspec<"guard">]; + let Subjects = SubjectList<[Function]>; + let Args = [EnumArgument<"Guard", "GuardArg", ["nocf"], ["nocf"]>]; + let Documentation = [CFGuardDocs]; +} + def MSStruct : InheritableAttr { let Spellings = [GCC<"ms_struct">]; let Subjects = SubjectList<[Record]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4558,6 +4558,27 @@ }]; } +def CFGuardDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Code can indicate CFG checks are not wanted with the ``__declspec(guard(nocf))`` +attribute. This directs the compiler to not insert any CFG checks for the entire +function. This approach is typically used only sparingly in specific situations +where the programmer has manually inserted "CFG-equivalent" protection. The +programmer knows that they are calling through some read only function table +whose address is obtained through read only memory references and for which the +index is masked to the function table limit. This approach may also be applied +to small wrapper functions that are not inlined and that do nothing more than +make a call through a function pointer. Since incorrect usage of this directive +can compromise the security of CFG, the programmer must be very careful using +the directive. Typically, this usage is limited to very small functions that +only call one function. + +`Control Flow Guard documentation ` +}]; +} + + def HIPPinnedShadowDocs : Documentation { let Category = DocCatType; let Content = [{ diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -836,6 +836,12 @@ if (D && D->hasAttr()) Fn->addFnAttr("cfi-canonical-jump-table"); + if (D && D->hasAttr()) { + // Add the "guard_nocf" attribute to the function. + if (D->getAttr()->getGuard() == CFGuardAttr::GuardArg::nocf) + Fn->addFnAttr("guard_nocf"); + } + if (getLangOpts().OpenCL) { // Add metadata for a kernel function. if (const FunctionDecl *FD = dyn_cast_or_null(D)) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6628,6 +6628,25 @@ D->addAttr(Attr::Create(S.Context, Argument, AL)); } +static void handleCFGuardAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + // The guard attribute takes a single argument. + + if (!AL.isArgIdent(0)) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_n_type) + << AL << 0 << AANT_ArgumentIdentifier; + return; + } + + CFGuardAttr::GuardArg Arg; + IdentifierInfo *II = AL.getArgAsIdent(0)->Ident; + if (!CFGuardAttr::ConvertStrToGuardArg(II->getName(), Arg)) { + S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) << AL << II; + return; + } + + D->addAttr(::new (S.Context) CFGuardAttr(S.Context, AL, Arg)); +} + //===----------------------------------------------------------------------===// // Top Level Sema Entry Points //===----------------------------------------------------------------------===// @@ -7256,6 +7275,9 @@ case ParsedAttr::AT_AbiTag: handleAbiTagAttr(S, D, AL); break; + case ParsedAttr::AT_CFGuard: + handleCFGuardAttr(S, D, AL); + break; // Thread safety attributes: case ParsedAttr::AT_AssertExclusiveLock: diff --git a/clang/test/CodeGen/guard_nocf.c b/clang/test/CodeGen/guard_nocf.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/guard_nocf.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple %ms_abi_triple -fms-extensions -emit-llvm -O0 -o - %s | FileCheck %s + +// The "guard_nocf" attribute should be added to this function +__declspec(guard(nocf)) void Func_guard_nocf() { +} + +// The "guard_nocf" attribute should not be added to this function. +void Func_guard_cf() { +} + +// CHECK: @{{.*}}Func_guard_nocf{{.*}}[[NOCF:#[0-9]+]] +// CHECK: @{{.*}}Func_guard_cf{{.*}}[[CF:#[0-9]+]] +// CHECK: attributes [[NOCF]] = { {{.*}}"guard_nocf"{{.*}} } +// CHECK-NOT: attributes [[CF]] = { {{.*}}"guard_nocf"{{.*}} } \ No newline at end of file diff --git a/clang/test/CodeGenCXX/guard_nocf.cpp b/clang/test/CodeGenCXX/guard_nocf.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/guard_nocf.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple %ms_abi_triple -fms-extensions -emit-llvm -O0 -o - %s | FileCheck %s + +// The "guard_nocf" attribute should be added to this function +__declspec(guard(nocf)) void Func_guard_nocf() { +} + +// The "guard_nocf" attribute should not be added to this function. +void Func_guard_cf() { +} + +// CHECK: @{{.*}}Func_guard_nocf{{.*}}[[NOCF:#[0-9]+]] +// CHECK: @{{.*}}Func_guard_cf{{.*}}[[CF:#[0-9]+]] +// CHECK: attributes [[NOCF]] = { {{.*}}"guard_nocf"{{.*}} } +// CHECK-NOT: attributes [[CF]] = { {{.*}}"guard_nocf"{{.*}} } \ No newline at end of file diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1840,6 +1840,9 @@ the function. The instrumentation checks that the return address for the function has not changed between the function prolog and eiplog. It is currently x86_64-specific. +``"guard_nocf"`` + This attribute indicates that no Control Flow Guard checks will be added + within this function. .. _glattrs: diff --git a/llvm/lib/Transforms/CFGuard/CFGuard.cpp b/llvm/lib/Transforms/CFGuard/CFGuard.cpp --- a/llvm/lib/Transforms/CFGuard/CFGuard.cpp +++ b/llvm/lib/Transforms/CFGuard/CFGuard.cpp @@ -255,7 +255,7 @@ bool CFGuard::runOnFunction(Function &F) { // Skip modules and functions for which CFGuard checks have been disabled. - if (cfguard_module_flag != 2 || F.hasFnAttribute(Attribute::NoCfCheck)) + if (cfguard_module_flag != 2 || F.hasFnAttribute("guard_nocf")) return false; SmallVector IndirectCalls; @@ -277,7 +277,7 @@ // If no checks are needed, return early and add this attribute to indicate // that subsequent CFGuard passes can skip this function. if (IndirectCalls.empty()) { - F.addFnAttr(Attribute::NoCfCheck); + F.addFnAttr("guard_nocf"); return false; } diff --git a/llvm/test/CodeGen/AArch64/cfguard-checks.ll b/llvm/test/CodeGen/AArch64/cfguard-checks.ll --- a/llvm/test/CodeGen/AArch64/cfguard-checks.ll +++ b/llvm/test/CodeGen/AArch64/cfguard-checks.ll @@ -7,8 +7,8 @@ declare i32 @target_func() -; Test that Control Flow Guard checks are not added to functions with nocf_checks attribute. -define i32 @func_nocf_checks() #0 { +; Test that Control Flow Guard checks are not added to functions with the "guard_nocf" attribute. +define i32 @func_guard_nocf() #0 { entry: %func_ptr = alloca i32 ()*, align 8 store i32 ()* @target_func, i32 ()** %func_ptr, align 8 @@ -16,13 +16,13 @@ %1 = call i32 %0() ret i32 %1 - ; CHECK-LABEL: func_nocf_checks + ; CHECK-LABEL: func_guard_nocf ; CHECK: adrp x8, target_func ; CHECK: add x8, x8, target_func ; CHECK-NOT: __guard_check_icall_fptr ; CHECK: blr x8 } -attributes #0 = { nocf_check } +attributes #0 = { "guard_nocf" } ; Test that Control Flow Guard checks are added even at -O0. diff --git a/llvm/test/CodeGen/ARM/cfguard-checks.ll b/llvm/test/CodeGen/ARM/cfguard-checks.ll --- a/llvm/test/CodeGen/ARM/cfguard-checks.ll +++ b/llvm/test/CodeGen/ARM/cfguard-checks.ll @@ -7,8 +7,8 @@ declare i32 @target_func() -; Test that Control Flow Guard checks are not added to functions with nocf_checks attribute. -define i32 @func_nocf_checks() #0 { +; Test that Control Flow Guard checks are not added to functions with the "guard_nocf" attribute. +define i32 @func_guard_nocf() #0 { entry: %func_ptr = alloca i32 ()*, align 8 store i32 ()* @target_func, i32 ()** %func_ptr, align 8 @@ -16,13 +16,13 @@ %1 = call arm_aapcs_vfpcc i32 %0() ret i32 %1 - ; CHECK-LABEL: func_nocf_checks + ; CHECK-LABEL: func_guard_nocf ; CHECK: movw r0, :lower16:target_func ; CHECK: movt r0, :upper16:target_func ; CHECK-NOT: __guard_check_icall_fptr ; CHECK: blx r0 } -attributes #0 = { nocf_check "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} +attributes #0 = { "guard_nocf" "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} ; Test that Control Flow Guard checks are added even at -O0. diff --git a/llvm/test/CodeGen/X86/cfguard-checks.ll b/llvm/test/CodeGen/X86/cfguard-checks.ll --- a/llvm/test/CodeGen/X86/cfguard-checks.ll +++ b/llvm/test/CodeGen/X86/cfguard-checks.ll @@ -8,8 +8,8 @@ declare i32 @target_func() -; Test that Control Flow Guard checks are not added to functions with nocf_checks attribute. -define i32 @func_nocf_checks() #0 { +; Test that Control Flow Guard checks are not added to functions with the "guard_nocf" attribute. +define i32 @func_guard_nocf() #0 { entry: %func_ptr = alloca i32 ()*, align 8 store i32 ()* @target_func, i32 ()** %func_ptr, align 8 @@ -17,17 +17,17 @@ %1 = call i32 %0() ret i32 %1 - ; X32-LABEL: func_nocf_checks + ; X32-LABEL: func_guard_nocf ; X32: movl $_target_func, %eax ; X32-NOT: __guard_check_icall_fptr ; X32: calll *%eax - ; X64-LABEL: func_nocf_checks + ; X64-LABEL: func_guard_nocf ; X64: leaq target_func(%rip), %rax ; X64-NOT: __guard_dispatch_icall_fptr ; X64: callq *%rax } -attributes #0 = { nocf_check } +attributes #0 = { "guard_nocf" } ; Test that Control Flow Guard checks are added even at -O0.