ajpaverd created this revision. Herald added subscribers: llvm-commits, cfe-commits, dexonsmith, steven_wu, hiraditya, kristof.beyls, javed.absar, mgorny, mehdi_amini. Herald added projects: clang, LLVM.
A new module pass (Transforms/CFGuard/CFGuard.cpp) inserts CFGuard checks on indirect function calls, using either the check or dispatch mechanism. These checks require new calling conventions for the supported targets (currently x86, ARM, and AArch64). An additional pass (Target/X86/X86FixupCFGuard.cpp) is used on x86 to prevent stack spills between loading and calling the check, which could be exploited. Another pass (CodeGen/CFGuardLongjmp.cpp) is used to identify and emit valid longjmp targets, as required by /guard:cf. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D65761 Files: clang/docs/ClangCommandLineReference.rst clang/include/clang/Basic/CodeGenOptions.def clang/include/clang/Driver/Options.td clang/lib/CodeGen/CodeGenModule.cpp clang/lib/Driver/ToolChains/Clang.cpp clang/lib/Driver/ToolChains/MSVC.cpp clang/lib/Frontend/CompilerInvocation.cpp clang/test/CodeGen/cfguardtable.c clang/test/Driver/cl-options.c llvm/docs/LangRef.rst llvm/include/llvm/CodeGen/MachineBasicBlock.h llvm/include/llvm/CodeGen/MachineFunction.h llvm/include/llvm/CodeGen/Passes.h llvm/include/llvm/IR/CallingConv.h llvm/include/llvm/InitializePasses.h llvm/include/llvm/MC/MCObjectFileInfo.h llvm/include/llvm/Transforms/CFGuard.h llvm/lib/AsmParser/LLLexer.cpp llvm/lib/AsmParser/LLParser.cpp llvm/lib/AsmParser/LLToken.h llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h llvm/lib/CodeGen/CFGuardLongjmp.cpp llvm/lib/CodeGen/CMakeLists.txt llvm/lib/CodeGen/CodeGen.cpp llvm/lib/CodeGen/MachineBasicBlock.cpp llvm/lib/IR/AsmWriter.cpp llvm/lib/MC/MCObjectFileInfo.cpp llvm/lib/Target/AArch64/AArch64CallingConvention.h llvm/lib/Target/AArch64/AArch64CallingConvention.td llvm/lib/Target/AArch64/AArch64FastISel.cpp llvm/lib/Target/AArch64/AArch64ISelLowering.cpp llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp llvm/lib/Target/AArch64/AArch64TargetMachine.cpp llvm/lib/Target/AArch64/LLVMBuild.txt llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp llvm/lib/Target/ARM/ARMCallingConv.h llvm/lib/Target/ARM/ARMCallingConv.td llvm/lib/Target/ARM/ARMFastISel.cpp llvm/lib/Target/ARM/ARMISelLowering.cpp llvm/lib/Target/ARM/ARMTargetMachine.cpp llvm/lib/Target/ARM/LLVMBuild.txt llvm/lib/Target/X86/CMakeLists.txt llvm/lib/Target/X86/LLVMBuild.txt llvm/lib/Target/X86/X86.h llvm/lib/Target/X86/X86AsmPrinter.cpp llvm/lib/Target/X86/X86CallingConv.td llvm/lib/Target/X86/X86FastISel.cpp llvm/lib/Target/X86/X86FixupCFGuard.cpp llvm/lib/Target/X86/X86RegisterInfo.cpp llvm/lib/Target/X86/X86Subtarget.h llvm/lib/Target/X86/X86TargetMachine.cpp llvm/lib/Transforms/CFGuard/CFGuard.cpp llvm/lib/Transforms/CFGuard/CMakeLists.txt llvm/lib/Transforms/CFGuard/LLVMBuild.txt llvm/lib/Transforms/CMakeLists.txt llvm/lib/Transforms/LLVMBuild.txt llvm/test/Bitcode/calling-conventions.3.2.ll llvm/test/Bitcode/calling-conventions.3.2.ll.bc llvm/test/CodeGen/AArch64/cfguard-checks.ll llvm/test/CodeGen/AArch64/cfguard-module-flag.ll llvm/test/CodeGen/ARM/cfguard-checks.ll llvm/test/CodeGen/ARM/cfguard-module-flag.ll llvm/test/CodeGen/WinCFGuard/cfguard-setjmp.ll llvm/test/CodeGen/WinCFGuard/cfguard.ll llvm/test/CodeGen/X86/cfguard-checks.ll llvm/test/CodeGen/X86/cfguard-module-flag.ll
Index: llvm/test/CodeGen/X86/cfguard-module-flag.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/X86/cfguard-module-flag.ll @@ -0,0 +1,28 @@ + +; RUN: llc < %s -mtriple=i686-pc-windows-msvc | FileCheck %s -check-prefix=X32 +; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s -check-prefix=X64 + +; This test is disabled on Linux +; UNSUPPORTED: linux + +; Test that Control Flow Guard checks are not added in modules with the +; cfguard=1 flag (emit tables but no checks). + + +declare void @target_func() + +define void @func_in_module_without_cfguard() #0 { +entry: + %func_ptr = alloca void ()*, align 8 + store void ()* @target_func, void ()** %func_ptr, align 8 + %0 = load void ()*, void ()** %func_ptr, align 8 + + call void %0() + ret void + + ; X32-NOT: __guard_check_icall_fptr + ; X64-NOT: __guard_dispatch_icall_fptr +} + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 1} \ No newline at end of file Index: llvm/test/CodeGen/X86/cfguard-checks.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/X86/cfguard-checks.ll @@ -0,0 +1,124 @@ +; RUN: llc < %s -mtriple=i686-pc-windows-msvc | FileCheck %s -check-prefix=X32 +; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s -check-prefix=X64 + +; This test is disabled on Linux +; UNSUPPORTED: linux + +; Test that Control Flow Guard checks are correctly added when required. + + +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 { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = call i32 %0() + ret i32 %1 + + ; X32-LABEL: func_nocf_checks + ; X32: movl $_target_func, %eax + ; X32-NOT: __guard_check_icall_fptr + ; X32: calll *%eax + + ; X64-LABEL: func_nocf_checks + ; X64: leaq target_func(%rip), %rax + ; X64-NOT: __guard_dispatch_icall_fptr + ; X64: callq *%rax +} +attributes #0 = { nocf_check } + + +; Test that Control Flow Guard checks are correctly added as single instructions even at -O0. +define i32 @func_optnone_cf() #1 { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = call i32 %0() + ret i32 %1 + + ; On i686, the call to __guard_check_icall_fptr should come immediately before the call to the target function. + ; X32-LABEL: func_optnone_cf + ; X32: leal _target_func, %eax + ; X32: movl %eax, (%esp) + ; X32: movl (%esp), %ecx + ; X32: calll *___guard_check_icall_fptr + ; X32-NEXT: calll *%ecx + + ; On x86_64, __guard_dispatch_icall_fptr tail calls the function, so there should be only one call instruction. + ; X64-LABEL: func_optnone_cf + ; X64: leaq target_func(%rip), %rax + ; X64: callq *__guard_dispatch_icall_fptr(%rip) + ; X64-NOT: callq +} +attributes #1 = { noinline optnone } + + +; Test that Control Flow Guard checks are correctly added in optimized code (common case). +define i32 @func_cf() { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = call i32 %0() + ret i32 %1 + + ; On i686, the call to __guard_check_icall_fptr should come immediately before the call to the target function. + ; X32-LABEL: func_cf + ; X32: movl $_target_func, %esi + ; X32: movl $_target_func, %ecx + ; X32: calll *___guard_check_icall_fptr + ; X32-NEXT: calll *%esi + + ; On x86_64, __guard_dispatch_icall_fptr tail calls the function, so there should be only one call instruction. + ; X64-LABEL: func_cf + ; X64: leaq target_func(%rip), %rax + ; X64: callq *__guard_dispatch_icall_fptr(%rip) + ; X64-NOT: callq +} + + +; Test that Control Flow Guard checks are correctly added on invoke instructions. +define i32 @func_cf_invoke() personality i8* bitcast (void ()* @h to i8*) { +entry: + %0 = alloca i32, align 4 + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %1 = load i32 ()*, i32 ()** %func_ptr, align 8 + %2 = invoke i32 %1() + to label %invoke.cont unwind label %lpad +invoke.cont: ; preds = %entry + ret i32 %2 + +lpad: ; preds = %entry + %tmp = landingpad { i8*, i32 } + catch i8* null + ret i32 -1 + + ; On i686, the call to __guard_check_icall_fptr should come immediately before the call to the target function. + ; X32-LABEL: func_cf_invoke + ; X32: movl $_target_func, %esi + ; X32: movl $_target_func, %ecx + ; X32: calll *___guard_check_icall_fptr + ; X32-NEXT: calll *%esi + ; X32: # %invoke.cont + ; X32: # %lpad + + ; On x86_64, __guard_dispatch_icall_fptr tail calls the function, so there should be only one call instruction. + ; X64-LABEL: func_cf_invoke + ; X64: leaq target_func(%rip), %rax + ; X64: callq *__guard_dispatch_icall_fptr(%rip) + ; X64-NOT: callq + ; X64: # %invoke.cont + ; X64: # %lpad +} + +declare void @h() + + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 2} \ No newline at end of file Index: llvm/test/CodeGen/WinCFGuard/cfguard.ll =================================================================== --- llvm/test/CodeGen/WinCFGuard/cfguard.ll +++ llvm/test/CodeGen/WinCFGuard/cfguard.ll @@ -159,6 +159,6 @@ !llvm.module.flags = !{!0, !1} !llvm.ident = !{!2} -!0 = !{i32 2, !"cfguardtable", i32 1} +!0 = !{i32 2, !"cfguard", i32 1} !1 = !{i32 1, !"wchar_size", i32 2} !2 = !{!"clang version 6.0.0 "} Index: llvm/test/CodeGen/WinCFGuard/cfguard-setjmp.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/WinCFGuard/cfguard-setjmp.ll @@ -0,0 +1,45 @@ +; RUN: llc < %s | FileCheck %s + +; This test is disabled on Linux +; UNSUPPORTED: linux + +; Test that longjmp targets have public labels and are included in the .gljmp section. + +%struct._SETJMP_FLOAT128 = type { [2 x i64] } +@buf1 = internal global [16 x %struct._SETJMP_FLOAT128] zeroinitializer, align 16 + + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { + %1 = alloca i32, align 4 + %2 = alloca i32, align 4 + store i32 0, i32* %1, align 4 + store i32 -1, i32* %2, align 4 + %3 = call i8* @llvm.frameaddress(i32 0) + %4 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %3) #2 + + ; CHECK: callq _setjmp + ; CHECK-NEXT: BB0_1: + + store i32 1, i32* %2, align 4 + %5 = load i32, i32* %2, align 4 + ret i32 %5 + + ; CHECK: .section .gljmp$y,"dr" + ; CHECK-NEXT: .symidx BB0_1 + +} + +; Function Attrs: nounwind readnone +declare i8* @llvm.frameaddress(i32) #1 + +; Function Attrs: returns_twice +declare dso_local i32 @_setjmp(i8*, i8*) #2 + +attributes #0 = { noinline nounwind } +attributes #1 = { nounwind readnone } +attributes #2 = { returns_twice } + + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 2} \ No newline at end of file Index: llvm/test/CodeGen/ARM/cfguard-module-flag.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/ARM/cfguard-module-flag.ll @@ -0,0 +1,28 @@ + +; RUN: llc < %s -mtriple=arm-pc-windows-msvc | FileCheck %s + +; This test is disabled on Linux +; UNSUPPORTED: linux + +; Test that Control Flow Guard checks are not added in modules with the +; cfguard=1 flag (emit tables but no checks). + + +declare void @target_func() + +define void @func_in_module_without_cfguard() #0 { +entry: + %func_ptr = alloca void ()*, align 8 + store void ()* @target_func, void ()** %func_ptr, align 8 + %0 = load void ()*, void ()** %func_ptr, align 8 + + call void %0() + ret void + + ; CHECK-NOT: __guard_check_icall_fptr + ; CHECK-NOT: __guard_dispatch_icall_fptr +} +attributes #0 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3" "unsafe-fp-math"="false" "use-soft-float"="false"} + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 1} \ No newline at end of file Index: llvm/test/CodeGen/ARM/cfguard-checks.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/ARM/cfguard-checks.ll @@ -0,0 +1,115 @@ +; RUN: llc < %s -mtriple=arm-pc-windows-msvc | FileCheck %s + +; This test is disabled on Linux +; UNSUPPORTED: linux + +; Test that Control Flow Guard checks are correctly added when required. + + +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 { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = call arm_aapcs_vfpcc i32 %0() + ret i32 %1 + + ; CHECK-LABEL: func_nocf_checks + ; 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 "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3" "unsafe-fp-math"="false" "use-soft-float"="false"} + + +; Test that Control Flow Guard checks are correctly added as single instructions even at -O0. +define i32 @func_optnone_cf() #1 { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = call i32 %0() + ret i32 %1 + + ; The call to __guard_check_icall_fptr should come immediately before the call to the target function. + ; CHECK-LABEL: func_optnone_cf + ; CHECK: movw r0, :lower16:target_func + ; CHECK: movt r0, :upper16:target_func + ; CHECK: str r0, [sp] + ; CHECK: ldr r4, [sp] + ; CHECK: movw r0, :lower16:__guard_check_icall_fptr + ; CHECK: movt r0, :upper16:__guard_check_icall_fptr + ; CHECK: ldr r1, [r0] + ; CHECK: mov r0, r4 + ; CHECK: blx r1 + ; CHECK-NEXT: blx r4 +} +attributes #1 = { noinline optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3" "unsafe-fp-math"="false" "use-soft-float"="false"} + + +; Test that Control Flow Guard checks are correctly added in optimized code (common case). +define i32 @func_cf() #2 { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = call i32 %0() + ret i32 %1 + + ; The call to __guard_check_icall_fptr should come immediately before the call to the target function. + ; CHECK-LABEL: func_cf + ; CHECK: movw r0, :lower16:__guard_check_icall_fptr + ; CHECK: movt r0, :upper16:__guard_check_icall_fptr + ; CHECK: ldr r1, [r0] + ; CHECK: movw r4, :lower16:target_func + ; CHECK: movt r4, :upper16:target_func + ; CHECK: mov r0, r4 + ; CHECK: blx r1 + ; CHECK-NEXT: blx r4 +} +attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3" "unsafe-fp-math"="false" "use-soft-float"="false"} + + +; Test that Control Flow Guard checks are correctly added on invoke instructions. +define i32 @func_cf_invoke() #3 personality i8* bitcast (void ()* @h to i8*) { +entry: + %0 = alloca i32, align 4 + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %1 = load i32 ()*, i32 ()** %func_ptr, align 8 + %2 = invoke i32 %1() + to label %invoke.cont unwind label %lpad +invoke.cont: ; preds = %entry + ret i32 %2 + +lpad: ; preds = %entry + %tmp = landingpad { i8*, i32 } + catch i8* null + ret i32 -1 + + ; The call to __guard_check_icall_fptr should come immediately before the call to the target function. + ; CHECK-LABEL: func_cf_invoke + ; CHECK: movw r0, :lower16:__guard_check_icall_fptr + ; CHECK: movt r0, :upper16:__guard_check_icall_fptr + ; CHECK: ldr r1, [r0] + ; CHECK: movw r4, :lower16:target_func + ; CHECK: movt r4, :upper16:target_func + ; CHECK: mov r0, r4 + ; CHECK: blx r1 + ; CHECK-NEXT: $Mtmp0: + ; CHECK-NEXT: blx r4 + ; CHECK: ; %invoke.cont + ; CHECK: ; %lpad +} +attributes #3 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3" "unsafe-fp-math"="false" "use-soft-float"="false"} + +declare void @h() + + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 2} \ No newline at end of file Index: llvm/test/CodeGen/AArch64/cfguard-module-flag.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/cfguard-module-flag.ll @@ -0,0 +1,27 @@ + +; RUN: llc < %s -mtriple=aarch64-pc-windows-msvc | FileCheck %s + +; This test is disabled on Linux +; UNSUPPORTED: linux + +; Test that Control Flow Guard checks are not added in modules with the +; cfguard=1 flag (emit tables but no checks). + + +declare void @target_func() + +define void @func_in_module_without_cfguard() #0 { +entry: + %func_ptr = alloca void ()*, align 8 + store void ()* @target_func, void ()** %func_ptr, align 8 + %0 = load void ()*, void ()** %func_ptr, align 8 + + call void %0() + ret void + + ; CHECK-NOT: __guard_check_icall_fptr + ; CHECK-NOT: __guard_dispatch_icall_fptr +} + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 1} \ No newline at end of file Index: llvm/test/CodeGen/AArch64/cfguard-checks.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/cfguard-checks.ll @@ -0,0 +1,109 @@ +; RUN: llc < %s -mtriple=aarch64-pc-windows-msvc | FileCheck %s + +; This test is disabled on Linux +; UNSUPPORTED: linux + +; Test that Control Flow Guard checks are correctly added when required. + + +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 { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = call i32 %0() + ret i32 %1 + + ; CHECK-LABEL: func_nocf_checks + ; CHECK: adrp x8, target_func + ; CHECK: add x8, x8, target_func + ; CHECK-NOT: __guard_check_icall_fptr + ; CHECK: blr x8 +} +attributes #0 = { nocf_check } + + +; Test that Control Flow Guard checks are correctly added as single instructions even at -O0. +define i32 @func_optnone_cf() #1 { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = call i32 %0() + ret i32 %1 + + ; The call to __guard_check_icall_fptr should come immediately before the call to the target function. + ; CHECK-LABEL: func_optnone_cf + ; CHECK: adrp x8, __guard_check_icall_fptr + ; CHECK: add x9, x8, __guard_check_icall_fptr + ; CHECK: adrp x8, target_func + ; CHECK: add x8, x8, target_func + ; CHECK: ldr x9, [x9] + ; CHECK: mov x15, x8 + ; CHECK: blr x9 + ; CHECK-NEXT: blr x8 +} +attributes #1 = { noinline optnone } + + +; Test that Control Flow Guard checks are correctly added in optimized code (common case). +define i32 @func_cf() { +entry: + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + %1 = call i32 %0() + ret i32 %1 + + ; The call to __guard_check_icall_fptr should come immediately before the call to the target function. + ; CHECK-LABEL: func_cf + ; CHECK: adrp x8, __guard_check_icall_fptr + ; CHECK: ldr x9, [x8, __guard_check_icall_fptr] + ; CHECK: adrp x8, target_func + ; CHECK: add x8, x8, target_func + ; CHECK: mov x15, x8 + ; CHECK: blr x9 + ; CHECK-NEXT: blr x8 +} + + +; Test that Control Flow Guard checks are correctly added on invoke instructions. +define i32 @func_cf_invoke() personality i8* bitcast (void ()* @h to i8*) { +entry: + %0 = alloca i32, align 4 + %func_ptr = alloca i32 ()*, align 8 + store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + %1 = load i32 ()*, i32 ()** %func_ptr, align 8 + %2 = invoke i32 %1() + to label %invoke.cont unwind label %lpad +invoke.cont: ; preds = %entry + ret i32 %2 + +lpad: ; preds = %entry + %tmp = landingpad { i8*, i32 } + catch i8* null + ret i32 -1 + + ; The call to __guard_check_icall_fptr should come immediately before the call to the target function. + ; CHECK-LABEL: func_cf_invoke + ; CHECK: adrp x8, __guard_check_icall_fptr + ; CHECK: ldr x9, [x8, __guard_check_icall_fptr] + ; CHECK: adrp x8, target_func + ; CHECK: add x8, x8, target_func + ; CHECK: mov x15, x8 + ; CHECK: blr x9 + ; CHECK-NEXT: .Ltmp0: + ; CHECK-NEXT: blr x8 + ; CHECK: ; %invoke.cont + ; CHECK: ; %lpad +} + +declare void @h() + + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 2} \ No newline at end of file Index: llvm/test/Bitcode/calling-conventions.3.2.ll =================================================================== --- llvm/test/Bitcode/calling-conventions.3.2.ll +++ llvm/test/Bitcode/calling-conventions.3.2.ll @@ -26,6 +26,12 @@ declare intel_ocl_bicc void @intel_ocl_bicc() ; CHECK: declare intel_ocl_bicc void @intel_ocl_bicc +declare cfguard_checkcc void @cfguard_checkcc() +; CHECK: declare cfguard_checkcc void @cfguard_checkcc + +declare cfguard_dispatchcc void @cfguard_dispatchcc() +; CHECK: declare cfguard_dispatchcc void @cfguard_dispatchcc + declare x86_stdcallcc void @x86_stdcallcc() ; CHECK: declare x86_stdcallcc void @x86_stdcallcc @@ -95,6 +101,18 @@ ret void } +define void @call_cfguard_checkcc() { +; CHECK: call cfguard_checkcc void @cfguard_checkcc +call cfguard_checkcc void @cfguard_checkcc() +ret void +} + +define void @call_cfguard_dispatchcc() { +; CHECK: call cfguard_dispatchcc void @cfguard_dispatchcc +call cfguard_dispatchcc void @cfguard_dispatchcc() +ret void +} + define void @call_x86_stdcallcc() { ; CHECK: call x86_stdcallcc void @x86_stdcallcc call x86_stdcallcc void @x86_stdcallcc() Index: llvm/lib/Transforms/CMakeLists.txt =================================================================== --- llvm/lib/Transforms/CMakeLists.txt +++ llvm/lib/Transforms/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory(Hello) add_subdirectory(ObjCARC) add_subdirectory(Coroutines) +add_subdirectory(CFGuard) Index: llvm/lib/Transforms/LLVMBuild.txt =================================================================== --- llvm/lib/Transforms/LLVMBuild.txt +++ llvm/lib/Transforms/LLVMBuild.txt @@ -15,7 +15,7 @@ ;===------------------------------------------------------------------------===; [common] -subdirectories = AggressiveInstCombine Coroutines IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC +subdirectories = AggressiveInstCombine Coroutines IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC CFGuard [component_0] type = Group Index: llvm/lib/Transforms/CFGuard/LLVMBuild.txt =================================================================== --- llvm/lib/Transforms/CFGuard/LLVMBuild.txt +++ llvm/lib/Transforms/CFGuard/LLVMBuild.txt @@ -1,4 +1,4 @@ -;===- ./lib/Transforms/LLVMBuild.txt ---------------------------*- Conf -*--===; +;===- ./lib/Transforms/CFGuard/LLVMBuild.txt -------------------*- Conf -*--===; ; ; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ; See https://llvm.org/LICENSE.txt for license information. @@ -14,10 +14,8 @@ ; ;===------------------------------------------------------------------------===; -[common] -subdirectories = AggressiveInstCombine Coroutines IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC - [component_0] -type = Group -name = Transforms -parent = Libraries +type = Library +name = CFGuard +parent = Transforms +required_libraries = Core Support Index: llvm/lib/Transforms/CFGuard/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/lib/Transforms/CFGuard/CMakeLists.txt @@ -0,0 +1,7 @@ +add_llvm_library(LLVMCFGuard + CFGuard.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms + +) Index: llvm/lib/Transforms/CFGuard/CFGuard.cpp =================================================================== --- /dev/null +++ llvm/lib/Transforms/CFGuard/CFGuard.cpp @@ -0,0 +1,343 @@ +//===-- CFGuard.cpp - Control Flow Guard checks -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the IR transform to add Microsoft's Control Flow Guard +/// checks on Windows targets. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/CFGuard.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/Triple.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" + +using namespace llvm; + +#define DEBUG_TYPE "cfguard" + +STATISTIC(CFGuardCounter, "Number of Control Flow Guard checks added"); + +namespace { + +/// Adds Control Flow Guard (CFG) checks on indirect function calls/invokes. +/// These checks ensure that the target address corresponds to the start of an +/// address-taken function. Different mechanisms are used for specific +/// architectures (e.g. X86_32 uses cf_check, X86_64 uses cf_dispatch). +class CFGuard : public ModulePass { +public: + static char ID; + + enum Mechanism { cf_check, cf_dispatch }; + + // Default constructor required for the INITIALIZE_PASS macro + CFGuard() : ModulePass(ID) { + initializeCFGuardPass(*PassRegistry::getPassRegistry()); + // By default, use the guard check mechanism + GuardMechanism = cf_check; + } + + // Recommended constructor used to specify the type of guard mechanism + CFGuard(Mechanism Var) : ModulePass(ID) { + initializeCFGuardPass(*PassRegistry::getPassRegistry()); + GuardMechanism = Var; + } + + /// Inserts a Control Flow Guard (CFG) check on an indirect call using the CFG + /// check mechanism. When the image is loaded, the loader puts the appropriate + /// guard check function pointer in the __guard_check_icall_fptr global + /// symbol. This checks that the target address is a valid address-taken + /// function. The address of the target function is passed to the guard check + /// function in an architecture-specific register (e.g. ECX on 32-bit X86, + /// X15 on Aarch64, and R0 on ARM). The guard check function has no return + /// value (if the target is invalid, the guard check funtion will raise an + /// error). + /// + /// For example, the following LLVM IR: + /// \code + /// %func_ptr = alloca i32 ()*, align 8 + /// store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + /// %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + /// %1 = call i32 %0() + /// \endcode + /// + /// is transformed to: + /// \code + /// %func_ptr = alloca i32 ()*, align 8 + /// store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + /// %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + /// %1 = load void (i8*)*, void (i8*)** @__guard_check_icall_fptr + /// %2 = bitcast i32 ()* %0 to i8* + /// call cfguard_checkcc void %1(i8* %2) + /// %3 = call i32 %0() + /// \endcode + /// + /// For example, the following X86 assembly code: + /// \code + /// movl $_target_func, %eax + /// calll *%eax + /// \endcode + /// + /// is transformed to: + /// \code + /// movl $_target_func, %ecx + /// calll *___guard_check_icall_fptr + /// calll *%ecx + /// \endcode + /// + /// \param CB indirect call to instrument. + void insertCFGuardCheck(CallBase *CB); + + /// Inserts a Control Flow Guard (CFG) check on an indirect call using the CFG + /// dispatch mechanism. When the image is loaded, the loader puts the + /// appropriate guard check function pointer in the + /// __guard_dispatch_icall_fptr global symbol. This checks that the target + /// address is a valid address-taken function and, if so, tail calls the + /// target. The target address is passed in an architecture-specific register + /// (e.g. RAX on X86_64), with all other arguments for the target function + /// passed as usual. + /// + /// For example, the following LLVM IR: + /// \code + /// %func_ptr = alloca i32 ()*, align 8 + /// store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + /// %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + /// %1 = call i32 %0() + /// \endcode + /// + /// is transformed to: + /// \code + /// %func_ptr = alloca i32 ()*, align 8 + /// store i32 ()* @target_func, i32 ()** %func_ptr, align 8 + /// %0 = load i32 ()*, i32 ()** %func_ptr, align 8 + /// %1 = load i32 (i32 ()*)*, i32 (i32 ()*)** @__guard_dispatch_icall_fptr + /// %2 = call cfguard_dispatchcc i32 %1(i32 ()* %0) + /// \endcode + /// + /// For example, the following X86_64 assembly code: + /// \code + /// leaq target_func(%rip), %rax + /// callq *%rax + /// \endcode + /// + /// is transformed to: + /// \code + /// leaq target_func(%rip), %rax + /// callq *__guard_dispatch_icall_fptr(%rip) + /// \endcode + /// + /// \param CB indirect call to instrument. + void insertCFGuardDispatch(CallBase *CB); + + bool runOnModule(Module &M) override; + +private: + Mechanism GuardMechanism = cf_check; + FunctionType *GuardFnType = nullptr; + PointerType *GuardFnPtrType = nullptr; + Constant *GuardFnGlobal = nullptr; +}; + +} // end anonymous namespace + +void CFGuard::insertCFGuardCheck(CallBase *CB) { + + assert(Triple(CB->getModule()->getTargetTriple()).isOSWindows() && + "Only applicable for Windows targets"); + assert(CB->isIndirectCall() && + "Control Flow Guard checks can only be added to indirect calls"); + + IRBuilder<> B(CB); + Value *CalledOperand = CB->getCalledOperand(); + + // Load the global symbol as a pointer to the check function + LoadInst *GuardCheckLoad = B.CreateLoad(GuardFnPtrType, GuardFnGlobal); + + // Create new call instruction. The CFGuard check should always be a call, + // even if the original CallBase is an Invoke or CallBr instruction. + CallInst *GuardCheck = + B.CreateCall(GuardFnType, GuardCheckLoad, + {B.CreateBitCast(CalledOperand, B.getInt8PtrTy())}); + + // Ensure that the first argument is passed in the correct register + // (e.g. ECX on 32-bit X86 targets) + GuardCheck->setCallingConv(CallingConv::CFGuard_Check); +} + +void CFGuard::insertCFGuardDispatch(CallBase *CB) { + + assert(Triple(CB->getModule()->getTargetTriple()).isOSWindows() && + "Only applicable for Windows targets"); + assert(CB->isIndirectCall() && + "Control Flow Guard checks can only be added to indirect calls"); + + IRBuilder<> B(CB); + Value *CalledOperand = CB->getCalledOperand(); + + Type *CalledOperandType = CalledOperand->getType(); + FunctionType *CalledFunctionType = cast<FunctionType>( + cast<PointerType>(CalledOperandType)->getElementType()); + + // Set up the new function type + SmallVector<Type *, 8> ArgTypes; + ArgTypes.push_back(CalledOperandType); + ArgTypes.append(CalledFunctionType->param_begin(), + CalledFunctionType->param_end()); + FunctionType *GuardDispatchType = + FunctionType::get(CalledFunctionType->getReturnType(), ArgTypes, + CalledFunctionType->isVarArg()); + PointerType *GuardDispatchPtrType = PointerType::get(GuardDispatchType, 0U); + + // Get or insert the global symbol "__guard_dispatch_icall_fptr" + Constant *GuardDispatchGlobal = CB->getModule()->getOrInsertGlobal( + "__guard_dispatch_icall_fptr", GuardDispatchPtrType); + + // Load the global symbol as a pointer to a function of the new type + LoadInst *GuardDispatchLoad = + B.CreateLoad(GuardDispatchPtrType, GuardDispatchGlobal); + + // Prepare the arguments for the new call + SmallVector<Value *, 8> GuardDispatchArgs; + GuardDispatchArgs.push_back(CalledOperand); + GuardDispatchArgs.append(CB->arg_begin(), CB->arg_end()); + + // Depending on the type of the original instruction, create new + // call/invoke/callbr instruction + CallBase *GuardDispatch; + if (CallInst::classof(CB)) { + GuardDispatch = + B.CreateCall(GuardDispatchType, GuardDispatchLoad, GuardDispatchArgs); + } else if (InvokeInst::classof(CB)) { + InvokeInst *CBInvoke = cast<InvokeInst>(CB); + GuardDispatch = B.CreateInvoke( + GuardDispatchType, GuardDispatchLoad, CBInvoke->getNormalDest(), + CBInvoke->getUnwindDest(), GuardDispatchArgs); + } else { + assert(CallBrInst::classof(CB) && "Unsupported indirect call instruction."); + CallBrInst *CBCallBr = cast<CallBrInst>(CB); + GuardDispatch = B.CreateCallBr( + GuardDispatchType, GuardDispatchLoad, CBCallBr->getDefaultDest(), + CBCallBr->getIndirectDests(), GuardDispatchArgs); + } + + assert(GuardDispatch->getNumArgOperands() == CB->getNumArgOperands() + 1 && + "Incorrect number of arguments!"); + + // Create new attribute list and copy function attributes, return attributes, + // and parameter attributes (shifting parameter attributes one to the right). + AttributeList OldAttrs = CB->getAttributes(); + AttributeList NewAttrs = GuardDispatch->getAttributes(); + NewAttrs.addAttributes(CB->getContext(), AttributeList::FunctionIndex, + CB->getAttributes().getFnAttributes()); + NewAttrs.addAttributes(CB->getContext(), AttributeList::ReturnIndex, + CB->getAttributes().getRetAttributes()); + for (unsigned int i = 0; i < CB->getNumArgOperands(); i++) { + NewAttrs.addParamAttributes(CB->getContext(), i + 1, + OldAttrs.getParamAttributes(i)); + } + GuardDispatch->setAttributes(NewAttrs); + + // Use a custom calling convention to pass the address of the original + // function as the first argument to the guard dispatch call in the correct + // register (i.e. RAX on 64-bit X86 targets) + GuardDispatch->setCallingConv(CallingConv::CFGuard_Dispatch); + + // Replace all uses of the original call/invoke with the new dispatch + CB->replaceAllUsesWith(GuardDispatch); + + // Delete the original call/invoke + CB->eraseFromParent(); +} + +bool CFGuard::runOnModule(Module &M) { + + // Only add checks if the module has the cfguard=2 flag + int cfguard_flag = 0; + if (auto *MD = + mdconst::extract_or_null<ConstantInt>(M.getModuleFlag("cfguard"))) + cfguard_flag = MD->getZExtValue(); + if (cfguard_flag != 2) + return false; + + // The guard check function has the same type for every indirect call in the + // module, so this can be initialized once per module. + if (GuardMechanism == cf_check) { + GuardFnType = + FunctionType::get(Type::getVoidTy(M.getContext()), + {Type::getInt8PtrTy(M.getContext())}, false); + GuardFnPtrType = PointerType::get(GuardFnType, 0); + GuardFnGlobal = + M.getOrInsertGlobal("__guard_check_icall_fptr", GuardFnPtrType); + } + + for (Function &F : M) { + + bool ChecksAdded = false; + + // Skip functions for which CFG checks have been disabled + if (F.hasFnAttribute(Attribute::NoCfCheck)) + continue; + + // Iterate over the instructions to find all indirect call/invoke/callbr + // instructions. Make a separate list of pointers to indirect + // call/invoke/callbr instructions because the original instructions will be + // deleted as the checks are added + SmallVector<CallBase *, 8> IndirectCalls; + for (BasicBlock &BB : F.getBasicBlockList()) { + for (Instruction &I : BB.getInstList()) { + + auto *CB = dyn_cast<CallBase>(&I); + if (!CB) continue; + + if (CB->isIndirectCall()) { + IndirectCalls.push_back(CB); + ChecksAdded = true; + CFGuardCounter++; + } + + } + } + + // For each indirect call/invoke/callbr, add the appropriate dispatch or + // check + if (GuardMechanism == cf_dispatch) { + for (CallBase *CB : IndirectCalls) { + insertCFGuardDispatch(CB); + } + } else { + for (CallBase *CB : IndirectCalls) { + insertCFGuardCheck(CB); + } + } + + // If no checks were added, add this attribute to indicate that subsequent + // CFGuard passes do not need to consider this function + if (!ChecksAdded) + F.addFnAttr(Attribute::NoCfCheck); + } + + return true; +} + + +char CFGuard::ID = 0; +INITIALIZE_PASS(CFGuard, "CFGuard", "CFGuard", false, false) + +ModulePass *llvm::createCFGuardCheckPass() { + return new CFGuard(CFGuard::cf_check); +} + +ModulePass *llvm::createCFGuardDispatchPass() { + return new CFGuard(CFGuard::cf_dispatch); +} \ No newline at end of file Index: llvm/lib/Target/X86/X86TargetMachine.cpp =================================================================== --- llvm/lib/Target/X86/X86TargetMachine.cpp +++ llvm/lib/Target/X86/X86TargetMachine.cpp @@ -46,6 +46,7 @@ #include "llvm/Support/TargetRegistry.h" #include "llvm/Target/TargetLoweringObjectFile.h" #include "llvm/Target/TargetOptions.h" +#include "llvm/Transforms/CFGuard.h" #include <memory> #include <string> @@ -418,6 +419,17 @@ // thunk. These will be a no-op unless a function subtarget has the retpoline // feature enabled. addPass(createIndirectBrExpandPass()); + + // Add Control Flow Guard checks + const Triple &TT = TM->getTargetTriple(); + if (TT.isOSWindows()) { + //addPass(createCFGuardLongjmpFPass()); + if(TT.getArch() == Triple::x86_64) { + addPass(createCFGuardDispatchPass()); + } else { + addPass(createCFGuardCheckPass()); + } + } } bool X86PassConfig::addInstSelector() { @@ -483,6 +495,11 @@ addPass(createX86SpeculativeLoadHardeningPass()); addPass(createX86FlagsCopyLoweringPass()); addPass(createX86WinAllocaExpander()); + if (TM->getTargetTriple().isOSWindows()) + // Fixup Control Flow Guard checks. + addPass(createX86FixupCFGuardPass()); + // Emit longjmp targets for Control Flow Guard + //addPass(createCFGuardLongjmpPass()); } void X86PassConfig::addMachineSSAOptimization() { addPass(createX86DomainReassignmentPass()); @@ -527,6 +544,9 @@ (!TT.isOSWindows() || MAI->getExceptionHandlingType() == ExceptionHandling::DwarfCFI)) addPass(createCFIInstrInserter()); + // Identify valid longjmp targets for Windows Control Flow Guard + if (TT.isOSWindows()) + addPass(createCFGuardLongjmpPass()); } std::unique_ptr<CSEConfigBase> X86PassConfig::getCSEConfig() const { Index: llvm/lib/Target/X86/X86Subtarget.h =================================================================== --- llvm/lib/Target/X86/X86Subtarget.h +++ llvm/lib/Target/X86/X86Subtarget.h @@ -815,6 +815,7 @@ case CallingConv::X86_ThisCall: case CallingConv::X86_VectorCall: case CallingConv::Intel_OCL_BI: + case CallingConv::CFGuard_Dispatch: return isTargetWin64(); // This convention allows using the Win64 convention on other targets. case CallingConv::Win64: Index: llvm/lib/Target/X86/X86RegisterInfo.cpp =================================================================== --- llvm/lib/Target/X86/X86RegisterInfo.cpp +++ llvm/lib/Target/X86/X86RegisterInfo.cpp @@ -341,6 +341,10 @@ return (HasSSE ? CSR_32_RegCall_SaveList : CSR_32_RegCall_NoSSE_SaveList); } + case CallingConv::CFGuard_Check: + assert(!Is64Bit && "CFGuard check mechanism only used on 32-bit X86"); + return (HasSSE ? CSR_Win32_CFGuard_Check_SaveList : + CSR_Win32_CFGuard_Check_NoSSE_SaveList); case CallingConv::Cold: if (Is64Bit) return CSR_64_MostRegs_SaveList; @@ -349,9 +353,14 @@ if (!HasSSE) return CSR_Win64_NoSSE_SaveList; return CSR_Win64_SaveList; - case CallingConv::X86_64_SysV: - if (CallsEHReturn) - return CSR_64EHRet_SaveList; + case CallingConv::CFGuard_Dispatch: + assert(IsWin64 && "CFGuard dispatch mechanism only used on X86_64"); + if (!HasSSE) + return CSR_Win64_NoSSE_SaveList; + return CSR_Win64_SaveList; + case CallingConv::X86_64_SysV: + if (CallsEHReturn) + return CSR_64EHRet_SaveList; return CSR_64_SaveList; case CallingConv::X86_INTR: if (Is64Bit) { @@ -455,12 +464,19 @@ return (HasSSE ? CSR_32_RegCall_RegMask : CSR_32_RegCall_NoSSE_RegMask); } + case CallingConv::CFGuard_Check: + assert(!Is64Bit && "CFGuard check mechanism only used on 32-bit X86"); + return (HasSSE ? CSR_Win32_CFGuard_Check_RegMask : + CSR_Win32_CFGuard_Check_NoSSE_RegMask); case CallingConv::Cold: if (Is64Bit) return CSR_64_MostRegs_RegMask; break; case CallingConv::Win64: return CSR_Win64_RegMask; + case CallingConv::CFGuard_Dispatch: + assert(IsWin64 && "CFGuard dispatch mechanism only used on X86_64"); + return CSR_Win64_RegMask; case CallingConv::X86_64_SysV: return CSR_64_RegMask; case CallingConv::X86_INTR: Index: llvm/lib/Target/X86/X86FixupCFGuard.cpp =================================================================== --- /dev/null +++ llvm/lib/Target/X86/X86FixupCFGuard.cpp @@ -0,0 +1,218 @@ +//===-- X86FixupCFGuard.cpp - Control Flow Guard checks ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the machine instruction transform to fixup the Control +/// Flow Guard checks inserted by /X86CFGuard.cpp. This pass searches +/// for such cases and replaces the pair of instructions with a single +/// call/invoke. For example: +/// +/// For 32-bit targets, the following assembly code: +/// \code +/// movl ___guard_check_icall_fptr, %eax +/// calll *%eax +/// \endcode +/// +/// is transformed to: +/// \code +/// calll *___guard_check_icall_fptr +/// \endcode +/// +/// For 64-bit targets, the following assembly code: +/// \code +/// movq __guard_dispatch_icall_fptr(%rip), %rcx +/// callq *%rcx +/// \endcode +/// +/// is transformed to: +/// \code +/// callq *__guard_dispatch_icall_fptr(%rip) +/// \endcode +/// +//===----------------------------------------------------------------------===// + +#include "X86InstrInfo.h" +#include "X86Subtarget.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" + +using namespace llvm; + +#define DEBUG_TYPE "x86-fixup-cfguard" + +STATISTIC(X86CFGuardFixed, "Number of X86 Control Flow Guard checks fixed"); + +namespace { + +/// MachineFunction pass to fix x86 Control Flow Guard checks that have been +/// generated as separate load and call/invoke instructions. +class X86FixupCFGuard : public MachineFunctionPass { +public: + static char ID; + + X86FixupCFGuard() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { + return "X86 Fixup Control Flow Guard"; + } + + /// Check if an instruction is loading a Guard function pointer by comparing + /// the instruction's global value to our cached pointers to the Guard + /// globals. + bool isGuardLoad(MachineInstr &MI); + + /// Given a guard load instruction, find all subsequent calls/invokes of the + /// loaded value in the basic block and replace each of these load-call/invoke + /// pairs with a single call/invoke. This function returns true if any + /// instructions were modified. + bool fixupGuardLoad(MachineInstr &LoadMI, const X86InstrInfo *TII); + + bool runOnMachineFunction(MachineFunction &MF) override; + +private: + const GlobalVariable *GuardCheckGlobal = nullptr; + const GlobalVariable *GuardDispatchGlobal = nullptr; +}; + +} // end anonymous namespace + +FunctionPass *llvm::createX86FixupCFGuardPass() { + return new X86FixupCFGuard(); +} + +char X86FixupCFGuard::ID = 0; + +bool X86FixupCFGuard::isGuardLoad(MachineInstr &MI) { + return (MI.getOpcode() == X86::MOV32rm && MI.getNumOperands() > 4 && + MI.getOperand(4).isGlobal() && + MI.getOperand(4).getGlobal() == GuardCheckGlobal) || + (MI.getOpcode() == X86::MOV64rm && MI.getNumOperands() > 4 && + MI.getOperand(4).isGlobal() && + MI.getOperand(4).getGlobal() == GuardDispatchGlobal); +} + +bool X86FixupCFGuard::fixupGuardLoad(MachineInstr &LoadMI, + const X86InstrInfo *TII) { + + // Select the correct opcodes based on the load instruction + unsigned int OldCallOpcode, NewCallOpcode; + if (LoadMI.getOpcode() == X86::MOV32rm) { + OldCallOpcode = X86::CALL32r; + NewCallOpcode = X86::CALL32m; + } else if (LoadMI.getOpcode() == X86::MOV64rm) { + OldCallOpcode = X86::CALL64r; + NewCallOpcode = X86::CALL64m; + } else { + // Not a supported load instruction + return false; + } + + // List of call/invoke machine instructions to erase + SmallVector<MachineInstr *, 2> MIsToErase; + + // Store the register into which the guard function pointer was loaded + unsigned int LoadReg = LoadMI.getOperand(0).getReg(); + assert(Register::isPhysicalRegister(LoadReg) == false && + "X86CFGuardFixup pass must be run before register allocation."); + + // Get an iterator starting from the load instruction + MachineBasicBlock *MBB = LoadMI.getParent(); + MachineBasicBlock::instr_iterator MIIter = LoadMI.getIterator(); + + // Iterate through the remaining instructions in the basic block after the + // load instruction + for (auto I = LoadMI.getIterator(), E = MBB->instr_end(); I != E; ++I) { + MachineInstr &MI = cast<MachineInstr>(*I); + + // Check if the instruction is a call/invoke through the loaded register + if (MI.getOpcode() == OldCallOpcode && + MI.getOperand(0).getReg() == LoadReg) { + + // Add new call/invoke instruction with same external symbol as preceding + // load. Omit operand 0 of the load instruction (destination register) and + // shift other operands one to the left. + MachineInstrBuilder B = + BuildMI(*MBB, MI, MI.getDebugLoc(), TII->get(NewCallOpcode)) + .add(LoadMI.getOperand(1)) + .add(LoadMI.getOperand(2)) + .add(LoadMI.getOperand(3)) + .add(LoadMI.getOperand(4)) + .add(LoadMI.getOperand(5)); + + // Copy implicit operands from the original call/invoke. This also copies + // the register mask from the original call/invoke. + B.copyImplicitOps(MI); + + // Mark original call/invoke instruction to be erased + MIsToErase.push_back(&MI); + + // Increment counter of guard calls/invokes fixed + X86CFGuardFixed++; + } + } + + // Instructions have not been changed + if (MIsToErase.empty()) + return false; + + // Erase all marked call/invoke instructions + for (MachineInstr *MI : MIsToErase) { + MI->eraseFromParent(); + } + + // Instructions have been changed + return true; +} + +bool X86FixupCFGuard::runOnMachineFunction(MachineFunction &MF) { + + // Skip functions that should not have CF checks + if (MF.getFunction().hasFnAttribute(Attribute::NoCfCheck)) + return false; + + // Look up the guard global variables from the parent module + GuardCheckGlobal = + MF.getFunction().getParent()->getNamedGlobal("__guard_check_icall_fptr"); + GuardDispatchGlobal = MF.getFunction().getParent()->getNamedGlobal( + "__guard_dispatch_icall_fptr"); + + // Skip functions that do not contain either of the guard globals + if (!GuardCheckGlobal && !GuardDispatchGlobal) + return false; + + const X86Subtarget &STI = MF.getSubtarget<X86Subtarget>(); + const X86InstrInfo *TII = STI.getInstrInfo(); + + SmallVector<MachineInstr *, 16> GuardLoads; + + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (isGuardLoad(MI)) + GuardLoads.push_back(&MI); + } + } + + if (GuardLoads.empty()) + return false; + + for (MachineInstr *LoadMI : GuardLoads) { + fixupGuardLoad(*LoadMI, TII); + + assert(MF.getRegInfo().use_empty(LoadMI->getOperand(0).getReg()) && + "Register containing guard function pointer is still in use."); + + LoadMI->eraseFromParent(); + } + + return true; +} \ No newline at end of file Index: llvm/lib/Target/X86/X86FastISel.cpp =================================================================== --- llvm/lib/Target/X86/X86FastISel.cpp +++ llvm/lib/Target/X86/X86FastISel.cpp @@ -1164,6 +1164,7 @@ CC != CallingConv::X86_StdCall && CC != CallingConv::X86_ThisCall && CC != CallingConv::X86_64_SysV && + CC != CallingConv::CFGuard_Dispatch && CC != CallingConv::Win64) return false; @@ -3215,6 +3216,8 @@ case CallingConv::X86_ThisCall: case CallingConv::Win64: case CallingConv::X86_64_SysV: + case CallingConv::CFGuard_Check: + case CallingConv::CFGuard_Dispatch: break; } Index: llvm/lib/Target/X86/X86CallingConv.td =================================================================== --- llvm/lib/Target/X86/X86CallingConv.td +++ llvm/lib/Target/X86/X86CallingConv.td @@ -433,6 +433,7 @@ def RetCC_X86_32 : CallingConv<[ // If FastCC, use RetCC_X86_32_Fast. CCIfCC<"CallingConv::Fast", CCDelegateTo<RetCC_X86_32_Fast>>, + // CFGuard_Check never returns a value so does not need a RetCC. // If HiPE, use RetCC_X86_32_HiPE. CCIfCC<"CallingConv::HiPE", CCDelegateTo<RetCC_X86_32_HiPE>>, CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<RetCC_X86_32_VectorCall>>, @@ -456,6 +457,7 @@ // Handle explicit CC selection CCIfCC<"CallingConv::Win64", CCDelegateTo<RetCC_X86_Win64_C>>, + CCIfCC<"CallingConv::CFGuard_Dispatch", CCDelegateTo<RetCC_X86_Win64_C>>, CCIfCC<"CallingConv::X86_64_SysV", CCDelegateTo<RetCC_X86_64_C>>, // Handle Vectorcall CC @@ -648,6 +650,14 @@ CCIfType<[i8, i16, i32, i64, f32, f64], CCAssignToStack<8, 8>> ]>; +def CC_X86_Win64_CFGuard_Dispatch : CallingConv<[ + // The function address (i.e. first integer argument) is passed in RAX + CCIfType<[i64], CCAssignToReg<[RAX]>>, + + // Otherwise, same as everything else. + CCDelegateTo<CC_X86_Win64_C> +]>; + def CC_X86_Win64_VectorCall : CallingConv<[ CCCustom<"CC_X86_64_VectorCall">, @@ -935,6 +945,12 @@ CCDelegateTo<CC_X86_32_Common> ]>; +def CC_X86_Win32_CFGuard_Check : CallingConv<[ + // The CFGuard check call takes exactly one integer argument + // (i.e. the target function address), which is passed in ECX. + CCIfType<[i32], CCAssignToReg<[ECX]>> +]>; + def CC_X86_32_GHC : CallingConv<[ // Promote i8/i16 arguments to i32. CCIfType<[i8, i16], CCPromoteToType<i32>>, @@ -999,6 +1015,7 @@ CCIfCC<"CallingConv::X86_FastCall", CCDelegateTo<CC_X86_32_FastCall>>, CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<CC_X86_Win32_VectorCall>>, CCIfCC<"CallingConv::X86_ThisCall", CCDelegateTo<CC_X86_32_ThisCall>>, + CCIfCC<"CallingConv::CFGuard_Check", CCDelegateTo<CC_X86_Win32_CFGuard_Check>>, CCIfCC<"CallingConv::Fast", CCDelegateTo<CC_X86_32_FastCC>>, CCIfCC<"CallingConv::GHC", CCDelegateTo<CC_X86_32_GHC>>, CCIfCC<"CallingConv::HiPE", CCDelegateTo<CC_X86_32_HiPE>>, @@ -1015,6 +1032,7 @@ CCIfCC<"CallingConv::WebKit_JS", CCDelegateTo<CC_X86_64_WebKit_JS>>, CCIfCC<"CallingConv::AnyReg", CCDelegateTo<CC_X86_64_AnyReg>>, CCIfCC<"CallingConv::Win64", CCDelegateTo<CC_X86_Win64_C>>, + CCIfCC<"CallingConv::CFGuard_Dispatch", CCDelegateTo<CC_X86_Win64_CFGuard_Dispatch>>, CCIfCC<"CallingConv::X86_64_SysV", CCDelegateTo<CC_X86_64_C>>, CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<CC_X86_Win64_VectorCall>>, CCIfCC<"CallingConv::HHVM", CCDelegateTo<CC_X86_64_HHVM>>, @@ -1134,7 +1152,9 @@ // Register calling convention preserves few GPR and XMM8-15 def CSR_32_RegCall_NoSSE : CalleeSavedRegs<(add ESI, EDI, EBX, EBP, ESP)>; def CSR_32_RegCall : CalleeSavedRegs<(add CSR_32_RegCall_NoSSE, - (sequence "XMM%u", 4, 7))>; + (sequence "XMM%u", 4, 7))>; +def CSR_Win32_CFGuard_Check_NoSSE : CalleeSavedRegs<(add CSR_32_RegCall_NoSSE, ECX)>; +def CSR_Win32_CFGuard_Check : CalleeSavedRegs<(add CSR_32_RegCall, ECX)>; def CSR_Win64_RegCall_NoSSE : CalleeSavedRegs<(add RBX, RBP, RSP, (sequence "R%u", 10, 15))>; def CSR_Win64_RegCall : CalleeSavedRegs<(add CSR_Win64_RegCall_NoSSE, Index: llvm/lib/Target/X86/X86AsmPrinter.cpp =================================================================== --- llvm/lib/Target/X86/X86AsmPrinter.cpp +++ llvm/lib/Target/X86/X86AsmPrinter.cpp @@ -614,7 +614,7 @@ Feat00Flags |= 1; } - if (M.getModuleFlag("cfguardtable")) + if (M.getModuleFlag("cfguard")) Feat00Flags |= 0x800; // Object is CFG-aware. OutStreamer->EmitSymbolAttribute(S, MCSA_Global); Index: llvm/lib/Target/X86/X86.h =================================================================== --- llvm/lib/Target/X86/X86.h +++ llvm/lib/Target/X86/X86.h @@ -123,6 +123,10 @@ /// This pass applies profiling information to insert cache prefetches. FunctionPass *createX86InsertPrefetchPass(); +/// This pass fixes up Control Flow Guard checks by merging a load and +/// subsequent call into a single call instruction. See X86FixupCFGuard.cpp +FunctionPass *createX86FixupCFGuardPass(); + InstructionSelector *createX86InstructionSelector(const X86TargetMachine &TM, X86Subtarget &, X86RegisterBankInfo &); @@ -143,6 +147,7 @@ void initializeX86ExecutionDomainFixPass(PassRegistry &); void initializeX86FlagsCopyLoweringPassPass(PassRegistry &); void initializeX86SpeculativeLoadHardeningPassPass(PassRegistry &); +void initializeX86CFGuardPass(PassRegistry &); } // End llvm namespace Index: llvm/lib/Target/X86/LLVMBuild.txt =================================================================== --- llvm/lib/Target/X86/LLVMBuild.txt +++ llvm/lib/Target/X86/LLVMBuild.txt @@ -30,5 +30,5 @@ type = Library name = X86CodeGen parent = X86 -required_libraries = Analysis AsmPrinter CodeGen Core MC SelectionDAG Support Target X86Desc X86Info X86Utils GlobalISel ProfileData +required_libraries = Analysis AsmPrinter CodeGen Core MC SelectionDAG Support Target X86Desc X86Info X86Utils GlobalISel ProfileData CFGuard add_to_library_groups = X86 Index: llvm/lib/Target/X86/CMakeLists.txt =================================================================== --- llvm/lib/Target/X86/CMakeLists.txt +++ llvm/lib/Target/X86/CMakeLists.txt @@ -33,6 +33,7 @@ X86ExpandPseudo.cpp X86FastISel.cpp X86FixupBWInsts.cpp + X86FixupCFGuard.cpp X86FixupLEAs.cpp X86AvoidStoreForwardingBlocks.cpp X86FixupSetCC.cpp Index: llvm/lib/Target/ARM/LLVMBuild.txt =================================================================== --- llvm/lib/Target/ARM/LLVMBuild.txt +++ llvm/lib/Target/ARM/LLVMBuild.txt @@ -30,5 +30,5 @@ type = Library name = ARMCodeGen parent = ARM -required_libraries = ARMDesc ARMInfo Analysis AsmPrinter CodeGen Core MC Scalar SelectionDAG Support Target GlobalISel ARMUtils TransformUtils +required_libraries = ARMDesc ARMInfo Analysis AsmPrinter CodeGen Core MC Scalar SelectionDAG Support Target GlobalISel ARMUtils TransformUtils CFGuard add_to_library_groups = ARM Index: llvm/lib/Target/ARM/ARMTargetMachine.cpp =================================================================== --- llvm/lib/Target/ARM/ARMTargetMachine.cpp +++ llvm/lib/Target/ARM/ARMTargetMachine.cpp @@ -46,6 +46,7 @@ #include "llvm/Support/TargetRegistry.h" #include "llvm/Target/TargetLoweringObjectFile.h" #include "llvm/Target/TargetOptions.h" +#include "llvm/Transforms/CFGuard.h" #include "llvm/Transforms/Scalar.h" #include <cassert> #include <memory> @@ -419,6 +420,10 @@ // Match interleaved memory accesses to ldN/stN intrinsics. if (TM->getOptLevel() != CodeGenOpt::None) addPass(createInterleavedAccessPass()); + + // Add Control Flow Guard checks + if (TM->getTargetTriple().isOSWindows()) + addPass(createCFGuardCheckPass()); } void ARMPassConfig::addCodeGenPrepare() { Index: llvm/lib/Target/ARM/ARMISelLowering.cpp =================================================================== --- llvm/lib/Target/ARM/ARMISelLowering.cpp +++ llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -1798,11 +1798,13 @@ ARMTargetLowering::getEffectiveCallingConv(CallingConv::ID CC, bool isVarArg) const { switch (CC) { + case CallingConv::CFGuard_Dispatch: default: report_fatal_error("Unsupported calling convention"); case CallingConv::ARM_AAPCS: case CallingConv::ARM_APCS: case CallingConv::GHC: + case CallingConv::CFGuard_Check: return CC; case CallingConv::PreserveMost: return CallingConv::PreserveMost; @@ -1862,6 +1864,8 @@ return (Return ? RetCC_ARM_APCS : CC_ARM_APCS_GHC); case CallingConv::PreserveMost: return (Return ? RetCC_ARM_AAPCS : CC_ARM_AAPCS); + case CallingConv::CFGuard_Check: + return (Return ? RetCC_ARM_AAPCS : CC_ARM_Win32_CFGuard_Check); } } Index: llvm/lib/Target/ARM/ARMFastISel.cpp =================================================================== --- llvm/lib/Target/ARM/ARMFastISel.cpp +++ llvm/lib/Target/ARM/ARMFastISel.cpp @@ -1889,6 +1889,8 @@ report_fatal_error("Can't return in GHC call convention"); else return CC_ARM_APCS_GHC; + case CallingConv::CFGuard_Check: + return (Return ? RetCC_ARM_AAPCS: CC_ARM_Win32_CFGuard_Check); } } Index: llvm/lib/Target/ARM/ARMCallingConv.td =================================================================== --- llvm/lib/Target/ARM/ARMCallingConv.td +++ llvm/lib/Target/ARM/ARMCallingConv.td @@ -246,6 +246,16 @@ CCDelegateTo<RetCC_ARM_AAPCS_Common> ]>; + +// Windows Control Flow Guard checks take a single argument (the target function +// address) and have no return value. +let Entry = 1 in +def CC_ARM_Win32_CFGuard_Check : CallingConv<[ + CCIfType<[i32], CCAssignToReg<[R0]>> +]>; + + + //===----------------------------------------------------------------------===// // Callee-saved register lists. //===----------------------------------------------------------------------===// @@ -256,6 +266,11 @@ def CSR_AAPCS : CalleeSavedRegs<(add LR, R11, R10, R9, R8, R7, R6, R5, R4, (sequence "D%u", 15, 8))>; +// The Windows Control Flow Guard Check function preserves the same registers as +// AAPCS, and also preserves all floating point registers. +def CSR_Win_AAPCS_CFGuard_Check : CalleeSavedRegs<(add LR, R11, R10, R9, R8, R7, + R6, R5, R4, (sequence "D%u", 15, 0))>; + // R8 is used to pass swifterror, remove it from CSR. def CSR_AAPCS_SwiftError : CalleeSavedRegs<(sub CSR_AAPCS, R8)>; Index: llvm/lib/Target/ARM/ARMCallingConv.h =================================================================== --- llvm/lib/Target/ARM/ARMCallingConv.h +++ llvm/lib/Target/ARM/ARMCallingConv.h @@ -32,6 +32,9 @@ bool FastCC_ARM_APCS(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State); +bool CC_ARM_Win32_CFGuard_Check(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State); bool RetCC_ARM_AAPCS(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State); Index: llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp =================================================================== --- llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp +++ llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp @@ -75,6 +75,8 @@ // GHC set of callee saved regs is empty as all those regs are // used for passing STG regs around return CSR_NoRegs_SaveList; + } else if (F.getCallingConv() == CallingConv::CFGuard_Check) { + return CSR_Win_AAPCS_CFGuard_Check_SaveList; } else if (F.hasFnAttribute("interrupt")) { if (STI.isMClass()) { // M-class CPUs have hardware which saves the registers needed to allow a @@ -123,7 +125,8 @@ if (CC == CallingConv::GHC) // This is academic because all GHC calls are (supposed to be) tail calls return CSR_NoRegs_RegMask; - + if (CC == CallingConv::CFGuard_Check) + return CSR_Win_AAPCS_CFGuard_Check_RegMask; if (STI.getTargetLowering()->supportSwiftError() && MF.getFunction().getAttributes().hasAttrSomewhere(Attribute::SwiftError)) return STI.isTargetDarwin() ? CSR_iOS_SwiftError_RegMask Index: llvm/lib/Target/AArch64/LLVMBuild.txt =================================================================== --- llvm/lib/Target/AArch64/LLVMBuild.txt +++ llvm/lib/Target/AArch64/LLVMBuild.txt @@ -30,5 +30,5 @@ type = Library name = AArch64CodeGen parent = AArch64 -required_libraries = AArch64Desc AArch64Info AArch64Utils Analysis AsmPrinter CodeGen Core MC Scalar SelectionDAG Support Target TransformUtils GlobalISel +required_libraries = AArch64Desc AArch64Info AArch64Utils Analysis AsmPrinter CodeGen Core MC Scalar SelectionDAG Support Target TransformUtils GlobalISel CFGuard add_to_library_groups = AArch64 Index: llvm/lib/Target/AArch64/AArch64TargetMachine.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ llvm/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -39,6 +39,7 @@ #include "llvm/Support/TargetRegistry.h" #include "llvm/Target/TargetLoweringObjectFile.h" #include "llvm/Target/TargetOptions.h" +#include "llvm/Transforms/CFGuard.h" #include "llvm/Transforms/Scalar.h" #include <memory> #include <string> @@ -449,6 +450,10 @@ } addPass(createAArch64StackTaggingPass()); + + // Add Control Flow Guard checks + if (TM->getTargetTriple().isOSWindows()) + addPass(createCFGuardCheckPass()); } // Pass Pipeline Configuration Index: llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -42,6 +42,8 @@ const MCPhysReg * AArch64RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { assert(MF && "Invalid MachineFunction pointer."); + if (MF->getFunction().getCallingConv() == CallingConv::CFGuard_Check) + return CSR_Win_AArch64_CFGuard_Check_SaveList; if (MF->getSubtarget<AArch64Subtarget>().isTargetWindows()) return CSR_Win_AArch64_AAPCS_SaveList; if (MF->getFunction().getCallingConv() == CallingConv::GHC) @@ -122,6 +124,8 @@ return SCS ? CSR_AArch64_AAVPCS_SCS_RegMask : CSR_AArch64_AAVPCS_RegMask; if (CC == CallingConv::AArch64_SVE_VectorCall) return CSR_AArch64_SVE_AAPCS_RegMask; + if (CC == CallingConv::CFGuard_Check) + return CSR_Win_AArch64_CFGuard_Check_RegMask; if (MF.getSubtarget<AArch64Subtarget>().getTargetLowering() ->supportSwiftError() && MF.getFunction().getAttributes().hasAttrSomewhere(Attribute::SwiftError)) Index: llvm/lib/Target/AArch64/AArch64ISelLowering.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -3048,6 +3048,7 @@ CCAssignFn *AArch64TargetLowering::CCAssignFnForCall(CallingConv::ID CC, bool IsVarArg) const { switch (CC) { + case CallingConv::CFGuard_Dispatch: default: report_fatal_error("Unsupported calling convention."); case CallingConv::WebKit_JS: @@ -3066,6 +3067,8 @@ return IsVarArg ? CC_AArch64_DarwinPCS_VarArg : CC_AArch64_DarwinPCS; case CallingConv::Win64: return IsVarArg ? CC_AArch64_Win64_VarArg : CC_AArch64_AAPCS; + case CallingConv::CFGuard_Check: + return CC_AArch64_Win64_CFGuard_Check; case CallingConv::AArch64_VectorCall: return CC_AArch64_AAPCS; } Index: llvm/lib/Target/AArch64/AArch64FastISel.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64FastISel.cpp +++ llvm/lib/Target/AArch64/AArch64FastISel.cpp @@ -348,6 +348,8 @@ return CC_AArch64_WebKit_JS; if (CC == CallingConv::GHC) return CC_AArch64_GHC; + if (CC == CallingConv::CFGuard_Check) + return CC_AArch64_Win64_CFGuard_Check; return Subtarget->isTargetDarwin() ? CC_AArch64_DarwinPCS : CC_AArch64_AAPCS; } Index: llvm/lib/Target/AArch64/AArch64CallingConvention.td =================================================================== --- llvm/lib/Target/AArch64/AArch64CallingConvention.td +++ llvm/lib/Target/AArch64/AArch64CallingConvention.td @@ -165,6 +165,13 @@ CCDelegateTo<CC_AArch64_AAPCS> ]>; +// Windows Control Flow Guard checks take a single argument (the target function +// address) and have no return value. +let Entry = 1 in +def CC_AArch64_Win64_CFGuard_Check : CallingConv<[ + CCIfType<[i64], CCAssignToReg<[X15]>> +]>; + // Darwin uses a calling convention which differs in only two ways // from the standard one at this level: @@ -338,6 +345,12 @@ D8, D9, D10, D11, D12, D13, D14, D15)>; +// The Control Flow Guard check call uses a custom calling convention that also +// preserves X0-X8 and Q0-Q7. +def CSR_Win_AArch64_CFGuard_Check : CalleeSavedRegs<(add CSR_Win_AArch64_AAPCS, + (sequence "X%u", 0, 8), + (sequence "Q%u", 0, 7))>; + // AArch64 PCS for vector functions (VPCS) // must (additionally) preserve full Q8-Q23 registers def CSR_AArch64_AAVPCS : CalleeSavedRegs<(add LR, FP, X19, X20, X21, X22, Index: llvm/lib/Target/AArch64/AArch64CallingConvention.h =================================================================== --- llvm/lib/Target/AArch64/AArch64CallingConvention.h +++ llvm/lib/Target/AArch64/AArch64CallingConvention.h @@ -28,6 +28,9 @@ bool CC_AArch64_Win64_VarArg(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State); +bool CC_AArch64_Win64_CFGuard_Check(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, + ISD::ArgFlagsTy ArgFlags, CCState &State); bool CC_AArch64_WebKit_JS(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State); Index: llvm/lib/MC/MCObjectFileInfo.cpp =================================================================== --- llvm/lib/MC/MCObjectFileInfo.cpp +++ llvm/lib/MC/MCObjectFileInfo.cpp @@ -714,6 +714,11 @@ COFF::IMAGE_SCN_MEM_READ, SectionKind::getMetadata()); + GLJMPSection = Ctx->getCOFFSection(".gljmp$y", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ, + SectionKind::getMetadata()); + TLSDataSection = Ctx->getCOFFSection( ".tls$", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE, Index: llvm/lib/IR/AsmWriter.cpp =================================================================== --- llvm/lib/IR/AsmWriter.cpp +++ llvm/lib/IR/AsmWriter.cpp @@ -352,6 +352,8 @@ case CallingConv::PreserveAll: Out << "preserve_allcc"; break; case CallingConv::CXX_FAST_TLS: Out << "cxx_fast_tlscc"; break; case CallingConv::GHC: Out << "ghccc"; break; + case CallingConv::CFGuard_Check: Out << "cfguard_checkcc"; break; + case CallingConv::CFGuard_Dispatch: Out << "cfguard_dispatchcc"; break; case CallingConv::X86_StdCall: Out << "x86_stdcallcc"; break; case CallingConv::X86_FastCall: Out << "x86_fastcallcc"; break; case CallingConv::X86_ThisCall: Out << "x86_thiscallcc"; break; Index: llvm/lib/CodeGen/MachineBasicBlock.cpp =================================================================== --- llvm/lib/CodeGen/MachineBasicBlock.cpp +++ llvm/lib/CodeGen/MachineBasicBlock.cpp @@ -54,7 +54,8 @@ if (!CachedMCSymbol) { const MachineFunction *MF = getParent(); MCContext &Ctx = MF->getContext(); - auto Prefix = Ctx.getAsmInfo()->getPrivateLabelPrefix(); + auto Prefix = + LabelIsPublic ? "" : Ctx.getAsmInfo()->getPrivateLabelPrefix(); assert(getNumber() >= 0 && "cannot get label for unreachable MBB"); CachedMCSymbol = Ctx.getOrCreateSymbol(Twine(Prefix) + "BB" + Twine(MF->getFunctionNumber()) + Index: llvm/lib/CodeGen/CodeGen.cpp =================================================================== --- llvm/lib/CodeGen/CodeGen.cpp +++ llvm/lib/CodeGen/CodeGen.cpp @@ -22,6 +22,7 @@ initializeAtomicExpandPass(Registry); initializeBranchFolderPassPass(Registry); initializeBranchRelaxationPass(Registry); + initializeCFGuardLongjmpPass(Registry); initializeCFIInstrInserterPass(Registry); initializeCodeGenPreparePass(Registry); initializeDeadMachineInstructionElimPass(Registry); Index: llvm/lib/CodeGen/CMakeLists.txt =================================================================== --- llvm/lib/CodeGen/CMakeLists.txt +++ llvm/lib/CodeGen/CMakeLists.txt @@ -10,6 +10,7 @@ BuiltinGCs.cpp CalcSpillWeights.cpp CallingConvLower.cpp + CFGuardLongjmp.cpp CFIInstrInserter.cpp CodeGen.cpp CodeGenPrepare.cpp Index: llvm/lib/CodeGen/CFGuardLongjmp.cpp =================================================================== --- /dev/null +++ llvm/lib/CodeGen/CFGuardLongjmp.cpp @@ -0,0 +1,118 @@ +//===-- CFGuardLongjmp.cpp - Longjmp symbols for CFGuard --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains a machine function pass to insert a symbol after each +/// call to _setjmp and store this in the MachineFunction's LongjmpTargets +/// vector. This will be used to emit the table of valid longjmp targets used +/// by Control Flow Guard. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/Passes.h" + +using namespace llvm; + +#define DEBUG_TYPE "cfguard-longjmp" + +STATISTIC(CFGuardLongjmpTargets, + "Number of Control Flow Guard longjmp targets"); + +namespace { + +/// MachineFunction pass to insert a symbol after each call to _setjmp and store +/// this in the MachineFunction's LongjmpTargets vector. +class CFGuardLongjmp : public MachineFunctionPass { +public: + static char ID; + + CFGuardLongjmp() : MachineFunctionPass(ID) { + initializeCFGuardLongjmpPass(*PassRegistry::getPassRegistry()); + } + + StringRef getPassName() const override { + return "Control Flow Guard longjmp targets"; + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; + +} // end anonymous namespace + +char CFGuardLongjmp::ID = 0; + +INITIALIZE_PASS(CFGuardLongjmp, "CFGuardLongjmp", + "Insert symbols at valid longjmp targets for /guard:cf", false, + false) +FunctionPass *llvm::createCFGuardLongjmpPass() { return new CFGuardLongjmp(); } + +bool CFGuardLongjmp::runOnMachineFunction(MachineFunction &MF) { + + // Skip modules for which the cfguard flag is not set + if (!mdconst::extract_or_null<ConstantInt>( + MF.getMMI().getModule()->getModuleFlag("cfguard"))) + return false; + + // Skip functions that do not have calls to _setjmp + if (!MF.getFunction().callsFunctionThatReturnsTwice()) + return false; + + SmallVector<MachineInstr *, 16> SetjmpCalls; + + // Iterate over all instructions in the function and add calls to functions + // that return twice to the list of targets. + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + + // Skip instructions that are not calls to global functions + if (!MI.isCall() || MI.getNumOperands() < 1 || !MI.getOperand(0).isGlobal()) + continue; + + auto *F = dyn_cast<Function>(MI.getOperand(0).getGlobal()); + if (!F) continue; + + // If the instruction calls a function that returns twice, add + // it to the list of targets. + if (F->hasFnAttribute(Attribute::ReturnsTwice)) { + SetjmpCalls.push_back(&MI); + } + + } + } + + if (SetjmpCalls.empty()) + return false; + + // For each possible target, split the basic block after the call to setjmp. + // Ensure that the label of the second basic block will be emitted and does + // not have a private prefix. Add this label to the function's list of longjmp + // targets. + for (MachineInstr *Setjmp : SetjmpCalls) { + MachineBasicBlock *OldMBB = Setjmp->getParent(); + MachineBasicBlock *NewMBB = + MF.CreateMachineBasicBlock(OldMBB->getBasicBlock()); + NewMBB->setLabelIsPublic(); + NewMBB->setLabelMustBeEmitted(); + MF.insert(++OldMBB->getIterator(), NewMBB); + NewMBB->splice(NewMBB->begin(), OldMBB, + std::next(MachineBasicBlock::iterator(Setjmp)), + OldMBB->end()); + NewMBB->transferSuccessorsAndUpdatePHIs(OldMBB); + OldMBB->addSuccessor(NewMBB); + MF.addLongjmpTarget(NewMBB->getSymbol()); + CFGuardLongjmpTargets++; + } + + return true; +} \ No newline at end of file Index: llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h +++ llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h @@ -6,7 +6,8 @@ // //===----------------------------------------------------------------------===// // -// This file contains support for writing windows exception info into asm files. +// This file contains support for writing the metadata for Windows Control Flow +// Guard, including address-taken functions, and valid longjmp targets. // //===----------------------------------------------------------------------===// @@ -15,12 +16,14 @@ #include "llvm/CodeGen/AsmPrinterHandler.h" #include "llvm/Support/Compiler.h" +#include <vector> namespace llvm { class LLVM_LIBRARY_VISIBILITY WinCFGuard : public AsmPrinterHandler { /// Target of directive emission. AsmPrinter *Asm; + std::vector<const MCSymbol *> LongjmpTargets; public: WinCFGuard(AsmPrinter *A); @@ -39,7 +42,7 @@ /// Gather post-function debug information. /// Please note that some AsmPrinter implementations may not call /// beginFunction at all. - void endFunction(const MachineFunction *MF) override {} + void endFunction(const MachineFunction *MF) override; /// Process beginning of an instruction. void beginInstruction(const MachineInstr *MI) override {} Index: llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp +++ llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp @@ -6,7 +6,8 @@ // //===----------------------------------------------------------------------===// // -// This file contains support for writing Win64 exception info into asm files. +// This file contains support for writing the metadata for Windows Control Flow +// Guard, including address-taken functions, and valid longjmp targets. // //===----------------------------------------------------------------------===// @@ -29,16 +30,33 @@ WinCFGuard::~WinCFGuard() {} +void WinCFGuard::endFunction(const MachineFunction *MF) { + + // Skip functions without any longjmp targets + if (MF->getLongjmpTargets().empty()) + return; + + // Copy the function's longjmp targets to a module-level list + LongjmpTargets.insert(LongjmpTargets.end(), MF->getLongjmpTargets().begin(), + MF->getLongjmpTargets().end()); +} + void WinCFGuard::endModule() { const Module *M = Asm->MMI->getModule(); std::vector<const Function *> Functions; for (const Function &F : *M) if (F.hasAddressTaken()) Functions.push_back(&F); - if (Functions.empty()) + if (Functions.empty() && LongjmpTargets.empty()) return; auto &OS = *Asm->OutStreamer; OS.SwitchSection(Asm->OutContext.getObjectFileInfo()->getGFIDsSection()); for (const Function *F : Functions) OS.EmitCOFFSymbolIndex(Asm->getSymbol(F)); + + // Emit the symbol index of each longjmp target + OS.SwitchSection(Asm->OutContext.getObjectFileInfo()->getGLJMPSection()); + for (const MCSymbol *S : LongjmpTargets) { + OS.EmitCOFFSymbolIndex(S); + } } Index: llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -137,7 +137,7 @@ static const char *const EHTimerName = "write_exception"; static const char *const EHTimerDescription = "DWARF Exception Writer"; static const char *const CFGuardName = "Control Flow Guard"; -static const char *const CFGuardDescription = "Control Flow Guard Tables"; +static const char *const CFGuardDescription = "Control Flow Guard"; static const char *const CodeViewLineTablesGroupName = "linetables"; static const char *const CodeViewLineTablesGroupDescription = "CodeView Line Tables"; @@ -378,12 +378,12 @@ EHTimerDescription, DWARFGroupName, DWARFGroupDescription); + // Emit tables for any value of cfguard flag (i.e. cfguard=1 or cfguard=2) if (mdconst::extract_or_null<ConstantInt>( - MMI->getModule()->getModuleFlag("cfguardtable"))) + MMI->getModule()->getModuleFlag("cfguard"))) Handlers.emplace_back(llvm::make_unique<WinCFGuard>(this), CFGuardName, CFGuardDescription, DWARFGroupName, DWARFGroupDescription); - return false; } Index: llvm/lib/AsmParser/LLToken.h =================================================================== --- llvm/lib/AsmParser/LLToken.h +++ llvm/lib/AsmParser/LLToken.h @@ -132,6 +132,8 @@ kw_fastcc, kw_coldcc, kw_intel_ocl_bicc, + kw_cfguard_checkcc, + kw_cfguard_dispatchcc, kw_x86_stdcallcc, kw_x86_fastcallcc, kw_x86_thiscallcc, Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ llvm/lib/AsmParser/LLParser.cpp @@ -1920,6 +1920,8 @@ /// ::= 'fastcc' /// ::= 'intel_ocl_bicc' /// ::= 'coldcc' +/// ::= 'cfguard_checkcc' +/// ::= 'cfguard_dispatchcc' /// ::= 'x86_stdcallcc' /// ::= 'x86_fastcallcc' /// ::= 'x86_thiscallcc' @@ -1963,6 +1965,8 @@ case lltok::kw_ccc: CC = CallingConv::C; break; case lltok::kw_fastcc: CC = CallingConv::Fast; break; case lltok::kw_coldcc: CC = CallingConv::Cold; break; + case lltok::kw_cfguard_checkcc: CC = CallingConv::CFGuard_Check; break; + case lltok::kw_cfguard_dispatchcc: CC = CallingConv::CFGuard_Dispatch; break; case lltok::kw_x86_stdcallcc: CC = CallingConv::X86_StdCall; break; case lltok::kw_x86_fastcallcc: CC = CallingConv::X86_FastCall; break; case lltok::kw_x86_regcallcc: CC = CallingConv::X86_RegCall; break; Index: llvm/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/lib/AsmParser/LLLexer.cpp +++ llvm/lib/AsmParser/LLLexer.cpp @@ -585,6 +585,8 @@ KEYWORD(ccc); KEYWORD(fastcc); KEYWORD(coldcc); + KEYWORD(cfguard_checkcc); + KEYWORD(cfguard_dispatchcc); KEYWORD(x86_stdcallcc); KEYWORD(x86_fastcallcc); KEYWORD(x86_thiscallcc); Index: llvm/include/llvm/Transforms/CFGuard.h =================================================================== --- /dev/null +++ llvm/include/llvm/Transforms/CFGuard.h @@ -0,0 +1,26 @@ +//===-- CFGuard.h - CFGuard Transformations ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Windows Control Flow Guard passes (/guard:cf). +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_CFGUARD_H +#define LLVM_TRANSFORMS_CFGUARD_H + +namespace llvm { + +class ModulePass; + +/// Insert Control FLow Guard checks on indirect function calls. +ModulePass *createCFGuardCheckPass(); + +/// Insert Control FLow Guard dispatches on indirect function calls. +ModulePass *createCFGuardDispatchPass(); + +} + +#endif Index: llvm/include/llvm/MC/MCObjectFileInfo.h =================================================================== --- llvm/include/llvm/MC/MCObjectFileInfo.h +++ llvm/include/llvm/MC/MCObjectFileInfo.h @@ -211,6 +211,7 @@ MCSection *XDataSection; MCSection *SXDataSection; MCSection *GFIDsSection; + MCSection *GLJMPSection; public: void InitMCObjectFileInfo(const Triple &TT, bool PIC, MCContext &ctx, @@ -379,6 +380,7 @@ MCSection *getXDataSection() const { return XDataSection; } MCSection *getSXDataSection() const { return SXDataSection; } MCSection *getGFIDsSection() const { return GFIDsSection; } + MCSection *getGLJMPSection() const { return GLJMPSection; } MCSection *getEHFrameSection() { return EHFrameSection; Index: llvm/include/llvm/InitializePasses.h =================================================================== --- llvm/include/llvm/InitializePasses.h +++ llvm/include/llvm/InitializePasses.h @@ -91,6 +91,8 @@ void initializeCFGOnlyViewerLegacyPassPass(PassRegistry&); void initializeCFGPrinterLegacyPassPass(PassRegistry&); void initializeCFGSimplifyPassPass(PassRegistry&); +void initializeCFGuardPass(PassRegistry&); +void initializeCFGuardLongjmpPass(PassRegistry&); void initializeCFGViewerLegacyPassPass(PassRegistry&); void initializeCFIInstrInserterPass(PassRegistry&); void initializeCFLAndersAAWrapperPassPass(PassRegistry&); Index: llvm/include/llvm/IR/CallingConv.h =================================================================== --- llvm/include/llvm/IR/CallingConv.h +++ llvm/include/llvm/IR/CallingConv.h @@ -75,6 +75,18 @@ // CXX_FAST_TLS - Calling convention for access functions. CXX_FAST_TLS = 17, + /// Special calling convention on Windows for calling the Control + /// Guard Check ICall funtion. The function take exactly one argument + /// (address of the target function) passed in the first argument register, + /// and has no return value. All register values are preserved. + CFGuard_Check = 18, + + /// Special calling convention on Windows for calling the Control + /// Guard Dispatch ICall funtion. The address of the target function to + /// tail-call is passed in a register (RAX on X86_64 targets), and all other + /// arguments are the same as if they were calling the target function. + CFGuard_Dispatch = 19, + // Target - This is the start of the target-specific calling conventions, // e.g. fastcall and thiscall on X86. FirstTargetCC = 64, Index: llvm/include/llvm/CodeGen/Passes.h =================================================================== --- llvm/include/llvm/CodeGen/Passes.h +++ llvm/include/llvm/CodeGen/Passes.h @@ -447,6 +447,10 @@ /// Creates CFI Instruction Inserter pass. \see CFIInstrInserter.cpp FunctionPass *createCFIInstrInserter(); + /// Creates CFGuard longjmp target identification pass. + /// \see CFGuardLongjmp.cpp + FunctionPass *createCFGuardLongjmpPass(); + /// Create Hardware Loop pass. \see HardwareLoops.cpp FunctionPass *createHardwareLoopsPass(); Index: llvm/include/llvm/CodeGen/MachineFunction.h =================================================================== --- llvm/include/llvm/CodeGen/MachineFunction.h +++ llvm/include/llvm/CodeGen/MachineFunction.h @@ -303,6 +303,10 @@ /// by debug and exception handling consumers. std::vector<MCCFIInstruction> FrameInstructions; + /// List of basic blocks immediately following calls to _setjmp. Used to + /// construct a table of valid longjmp targets for Windows Control Flow Guard. + std::vector<MCSymbol*> LongjmpTargets; + /// \name Exception Handling /// \{ @@ -817,6 +821,19 @@ LLVM_NODISCARD unsigned addFrameInst(const MCCFIInstruction &Inst); + /// Returns a reference to a list of symbols immediately following calls to + /// _setjmp in the function. Used to construct the longjmp target table used + /// by Windows Control Flow Guard. + const std::vector<MCSymbol*> &getLongjmpTargets() const { + return LongjmpTargets; + } + + /// Add the specified symbol to the list of valid longjmp targets for Windows + /// Control Flow Guard. + void addLongjmpTarget(MCSymbol *Target) { + LongjmpTargets.push_back(Target); + } + /// \name Exception Handling /// \{ Index: llvm/include/llvm/CodeGen/MachineBasicBlock.h =================================================================== --- llvm/include/llvm/CodeGen/MachineBasicBlock.h +++ llvm/include/llvm/CodeGen/MachineBasicBlock.h @@ -118,6 +118,10 @@ /// whether the flow just falls-through to it. bool LabelMustBeEmitted = false; + /// Indicate that this basic block's symbol will not have a private label + /// prefix, and will thus be included in the symbol table. + bool LabelIsPublic = false; + /// Indicate that this basic block is the entry block of an EH scope, i.e., /// the block that used to have a catchpad or cleanuppad instruction in the /// LLVM IR. @@ -169,6 +173,12 @@ /// its label be emitted. void setLabelMustBeEmitted() { LabelMustBeEmitted = true; } + /// Set this block to reflect that its label will not have a private prefix. + void setLabelIsPublic() { + assert((!CachedMCSymbol || LabelIsPublic == true) && "Cannot set label to public after it has been accessed."); + LabelIsPublic = true; + } + /// Return the MachineFunction containing this basic block. const MachineFunction *getParent() const { return xParent; } MachineFunction *getParent() { return xParent; } Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -436,6 +436,26 @@ - On X86-64 RCX and R8 are available for additional integer returns, and XMM2 and XMM3 are available for additional FP/vector returns. - On iOS platforms, we use AAPCS-VFP calling convention. +"``cfguard_checkcc``" - Windows Control Flow Guard (Check mechanism) + This calling convention is used for the Control Flow Guard check function, + which can be inserted before indirect calls to check that the call target + is a valid function address. The check function has no return value, but + it will trigger an OS-level error if the address is not a valid target. + The set of registers preserved by the check function, and the register + containing the target address are architecture-specific. + + - On X86_32 the target address is passed in ECX. + - On ARM the target address is passed in R0. + - On Aarch64 the target address is passed in X15. +"``cfguard_dispatchcc``" - Windows Control Flow Guard (Dispatch mechanism) + This calling convention is used for the Control Flow Guard dispatch + function, which can be inserted in place of indirect calls to check + that the call target is a valid function address and then tail-call the + function. It passes the target address in the appropriate + (architecture-specific) register. All other function arguments are + passed as usual. + + - On X86-64 the target address is passed in RAX. "``cc <n>``" - Numbered convention Any calling convention may be specified by number, allowing target-specific calling conventions to be used. Target specific Index: clang/test/Driver/cl-options.c =================================================================== --- clang/test/Driver/cl-options.c +++ clang/test/Driver/cl-options.c @@ -594,9 +594,12 @@ // NOCFGUARD-NOT: -cfguard // RUN: %clang_cl /guard:cf -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARD %s -// RUN: %clang_cl /guard:cf,nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARD %s -// RUN: %clang_cl /guard:nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARD %s // CFGUARD: -cfguard +// CFGUARD-NOT: -cfguard-no-checks + +// RUN: %clang_cl /guard:cf,nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARDNOCHECKS %s +// RUN: %clang_cl /guard:nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARDNOCHECKS %s +// CFGUARDNOCHECKS: -cfguard-no-checks // RUN: %clang_cl /guard:foo -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARDINVALID %s // CFGUARDINVALID: invalid value 'foo' in '/guard:' Index: clang/test/CodeGen/cfguardtable.c =================================================================== --- clang/test/CodeGen/cfguardtable.c +++ clang/test/CodeGen/cfguardtable.c @@ -1,6 +1,10 @@ -// RUN: %clang_cc1 -cfguard -emit-llvm %s -o - | FileCheck %s - -void f() {} - -// Check that the cfguardtable metadata flag gets set on the module. -// CHECK: !"cfguardtable", i32 1} +// RUN: %clang_cc1 -cfguard-no-checks -emit-llvm %s -o - | FileCheck %s -check-prefix=CFGUARDNOCHECKS +// RUN: %clang_cc1 -cfguard -emit-llvm %s -o - | FileCheck %s -check-prefix=CFGUARD + + +void f() {} + +// Check that the cfguard metadata flag gets correctly set on the module. +// CFGUARDNOCHECKS: !"cfguard", i32 1} +// CFGUARD: !"cfguard", i32 2} + Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -967,6 +967,7 @@ Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name); Opts.VerifyModule = !Args.hasArg(OPT_disable_llvm_verifier); + Opts.ControlFlowGuardNoChecks = Args.hasArg(OPT_cfguard_no_checks); Opts.ControlFlowGuard = Args.hasArg(OPT_cfguard); Opts.DisableGCov = Args.hasArg(OPT_test_coverage); Index: clang/lib/Driver/ToolChains/MSVC.cpp =================================================================== --- clang/lib/Driver/ToolChains/MSVC.cpp +++ clang/lib/Driver/ToolChains/MSVC.cpp @@ -416,6 +416,8 @@ } Args.AddAllArgValues(CmdArgs, options::OPT__SLASH_link); + // Flags that can simply be passed through. + Args.AddAllArgs(CmdArgs, options::OPT__SLASH_guard); if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, options::OPT_fno_openmp, false)) { Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -5967,10 +5967,13 @@ else D.Diag(diag::err_drv_invalid_value) << A->getSpelling() << Arg; } - // Currently there's no support emitting CFG instrumentation; the flag only - // emits the table of address-taken functions. - if (Instrument || NoChecks) + + if (Instrument && !NoChecks) + //Emit CFG instrumentation and the table of address-taken functions. CmdArgs.push_back("-cfguard"); + else if (NoChecks) + //Emit only the table of address-taken functions. + CmdArgs.push_back("-cfguard-no-checks"); } } Index: clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- clang/lib/CodeGen/CodeGenModule.cpp +++ clang/lib/CodeGen/CodeGenModule.cpp @@ -486,9 +486,13 @@ if (CodeGenOpts.CodeViewGHash) { getModule().addModuleFlag(llvm::Module::Warning, "CodeViewGHash", 1); } + if (CodeGenOpts.ControlFlowGuardNoChecks) { + // Function ID tables for Control Flow Guard (cfguard=1). + getModule().addModuleFlag(llvm::Module::Warning, "cfguard", 1); + } if (CodeGenOpts.ControlFlowGuard) { - // We want function ID tables for Control Flow Guard. - getModule().addModuleFlag(llvm::Module::Warning, "cfguardtable", 1); + // Function ID tables and checks for Control Flow Guard (cfguard=2). + getModule().addModuleFlag(llvm::Module::Warning, "cfguard", 2); } if (CodeGenOpts.OptimizationLevel > 0 && CodeGenOpts.StrictVTablePointers) { // We don't support LTO with 2 with different StrictVTablePointers Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -497,8 +497,10 @@ def bundle__loader : Separate<["-"], "bundle_loader">; def bundle : Flag<["-"], "bundle">; def b : JoinedOrSeparate<["-"], "b">, Flags<[Unsupported]>; +def cfguard_no_checks : Flag<["-"], "cfguard-no-checks">, Flags<[CC1Option]>, + HelpText<"Emit Windows Control Flow Guard tables only (no checks).">; def cfguard : Flag<["-"], "cfguard">, Flags<[CC1Option]>, - HelpText<"Emit tables required for Windows Control Flow Guard.">; + HelpText<"Emit Windows Control Flow Guard tables and checks.">; def cl_opt_disable : Flag<["-"], "cl-opt-disable">, Group<opencl_Group>, Flags<[CC1Option]>, HelpText<"OpenCL only. This option disables all optimizations. By default optimizations are enabled.">; def cl_strict_aliasing : Flag<["-"], "cl-strict-aliasing">, Group<opencl_Group>, Flags<[CC1Option]>, Index: clang/include/clang/Basic/CodeGenOptions.def =================================================================== --- clang/include/clang/Basic/CodeGenOptions.def +++ clang/include/clang/Basic/CodeGenOptions.def @@ -37,6 +37,7 @@ CODEGENOPT(Autolink , 1, 1) ///< -fno-autolink CODEGENOPT(ObjCAutoRefCountExceptions , 1, 0) ///< Whether ARC should be EH-safe. CODEGENOPT(Backchain , 1, 0) ///< -mbackchain +CODEGENOPT(ControlFlowGuardNoChecks , 1, 0) ///< -cfguard-no-checks CODEGENOPT(ControlFlowGuard , 1, 0) ///< -cfguard CODEGENOPT(CoverageExtraChecksum, 1, 0) ///< Whether we need a second checksum for functions in GCNO files. CODEGENOPT(CoverageNoFunctionNamesInData, 1, 0) ///< Do not include function names in GCDA files. Index: clang/docs/ClangCommandLineReference.rst =================================================================== --- clang/docs/ClangCommandLineReference.rst +++ clang/docs/ClangCommandLineReference.rst @@ -126,7 +126,11 @@ .. option:: -cfguard -Emit tables required for Windows Control Flow Guard. +Emit tables and checks for Windows Control Flow Guard. + +.. option:: -cfguard-no-checks + +Emit tables required for Windows Control Flow Guard without checks. .. option:: -client\_name<arg>
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits