From 694ff2b0555b4818cc619154eeee261fc8d803a7 Mon Sep 17 00:00:00 2001 From: Andrew Paverd Date: Mon, 28 Oct 2019 13:22:19 +0000 Subject: [PATCH 1/4] Add Windows Control Flow Guard checks (/guard:cf). Summary: A new function pass (Transforms/CFGuard/CFGuard.cpp) inserts CFGuard checks on indirect function calls, using either the check mechanism (X86, ARM, AArch64) or or the dispatch mechanism (X86-64). The check mechanism requires a new calling convention for the supported targets. The dispatch mechanism adds the target as an operand bundle, which is processed by SelectionDAG. Another pass (CodeGen/CFGuardLongjmp.cpp) identifies and emits valid longjmp targets, as required by /guard:cf. This feature is enabled using the `cfguard` CC1 option. Reviewers: thakis, rnk, theraven, pcc Subscribers: ychen, hans, metalcanine, dmajor, tomrittervg, alex, mehdi_amini, mgorny, javed.absar, kristof.beyls, hiraditya, steven_wu, dexonsmith, cfe-commits, llvm-commits Tags: #clang, #llvm Differential Revision: https://reviews.llvm.org/D65761 --- clang/docs/ClangCommandLineReference.rst | 6 +- clang/include/clang/Basic/CodeGenOptions.def | 1 + clang/include/clang/Driver/CC1Options.td | 4 + clang/include/clang/Driver/Options.td | 2 - clang/lib/CodeGen/CodeGenModule.cpp | 7 +- clang/lib/Driver/ToolChains/Clang.cpp | 31 +- clang/lib/Driver/ToolChains/MSVC.cpp | 22 ++ clang/lib/Frontend/CompilerInvocation.cpp | 1 + clang/test/CodeGen/cfguardtable.c | 14 +- clang/test/Driver/cl-fallback.c | 3 +- clang/test/Driver/cl-options.c | 9 +- llvm/docs/LangRef.rst | 11 + llvm/docs/ReleaseNotes.rst | 5 + llvm/include/llvm/CodeGen/MachineFunction.h | 15 + llvm/include/llvm/CodeGen/Passes.h | 4 + llvm/include/llvm/CodeGen/TargetCallingConv.h | 8 +- llvm/include/llvm/CodeGen/TargetLowering.h | 3 +- llvm/include/llvm/IR/CallingConv.h | 7 +- llvm/include/llvm/IR/InstrTypes.h | 5 + llvm/include/llvm/IR/LLVMContext.h | 1 + llvm/include/llvm/InitializePasses.h | 2 + llvm/include/llvm/MC/MCObjectFileInfo.h | 2 + llvm/include/llvm/Target/TargetCallingConv.td | 5 + llvm/include/llvm/Transforms/CFGuard.h | 26 ++ llvm/lib/AsmParser/LLLexer.cpp | 1 + llvm/lib/AsmParser/LLParser.cpp | 2 + llvm/lib/AsmParser/LLToken.h | 1 + llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 6 +- llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp | 22 +- llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h | 9 +- llvm/lib/CodeGen/CFGuardLongjmp.cpp | 119 +++++++ llvm/lib/CodeGen/CMakeLists.txt | 1 + llvm/lib/CodeGen/CodeGen.cpp | 1 + llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 8 + llvm/lib/CodeGen/SelectionDAG/FastISel.cpp | 2 + .../SelectionDAG/SelectionDAGBuilder.cpp | 26 +- llvm/lib/IR/AsmWriter.cpp | 1 + llvm/lib/IR/LLVMContext.cpp | 5 + llvm/lib/IR/Verifier.cpp | 12 +- llvm/lib/MC/MCObjectFileInfo.cpp | 5 + .../Target/AArch64/AArch64CallingConvention.h | 3 + .../AArch64/AArch64CallingConvention.td | 13 + llvm/lib/Target/AArch64/AArch64FastISel.cpp | 2 + .../Target/AArch64/AArch64ISelLowering.cpp | 6 +- .../Target/AArch64/AArch64RegisterInfo.cpp | 4 + .../Target/AArch64/AArch64TargetMachine.cpp | 9 + llvm/lib/Target/AArch64/LLVMBuild.txt | 2 +- llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp | 5 +- llvm/lib/Target/ARM/ARMCallingConv.h | 3 + llvm/lib/Target/ARM/ARMCallingConv.td | 15 + llvm/lib/Target/ARM/ARMFastISel.cpp | 2 + llvm/lib/Target/ARM/ARMISelLowering.cpp | 3 + llvm/lib/Target/ARM/ARMTargetMachine.cpp | 9 + llvm/lib/Target/ARM/LLVMBuild.txt | 2 +- llvm/lib/Target/X86/LLVMBuild.txt | 2 +- llvm/lib/Target/X86/X86AsmPrinter.cpp | 2 +- llvm/lib/Target/X86/X86CallingConv.td | 15 +- llvm/lib/Target/X86/X86FastISel.cpp | 1 + llvm/lib/Target/X86/X86RegisterInfo.cpp | 8 + llvm/lib/Target/X86/X86TargetMachine.cpp | 14 + llvm/lib/Transforms/CFGuard/CFGuard.cpp | 307 ++++++++++++++++++ llvm/lib/Transforms/CFGuard/CMakeLists.txt | 9 + llvm/lib/Transforms/CFGuard/LLVMBuild.txt | 21 ++ llvm/lib/Transforms/CMakeLists.txt | 1 + llvm/lib/Transforms/LLVMBuild.txt | 2 +- llvm/test/Bitcode/calling-conventions.3.2.ll | 9 + .../Bitcode/calling-conventions.3.2.ll.bc | Bin 1236 -> 2568 bytes .../Bitcode/operand-bundles-bc-analyzer.ll | 1 + llvm/test/CodeGen/AArch64/cfguard-checks.ll | 147 +++++++++ .../CodeGen/AArch64/cfguard-module-flag.ll | 25 ++ llvm/test/CodeGen/ARM/cfguard-checks.ll | 151 +++++++++ llvm/test/CodeGen/ARM/cfguard-module-flag.ll | 26 ++ llvm/test/CodeGen/WinCFGuard/cfguard.ll | 5 +- llvm/test/CodeGen/X86/cfguard-checks.ll | 231 +++++++++++++ llvm/test/CodeGen/X86/cfguard-module-flag.ll | 26 ++ .../CodeGen/X86/cfguard-x86-64-vectorcall.ll | 38 +++ .../CodeGen/X86/cfguard-x86-vectorcall.ll | 43 +++ 77 files changed, 1514 insertions(+), 63 deletions(-) create mode 100644 llvm/include/llvm/Transforms/CFGuard.h create mode 100644 llvm/lib/CodeGen/CFGuardLongjmp.cpp create mode 100644 llvm/lib/Transforms/CFGuard/CFGuard.cpp create mode 100644 llvm/lib/Transforms/CFGuard/CMakeLists.txt create mode 100644 llvm/lib/Transforms/CFGuard/LLVMBuild.txt create mode 100644 llvm/test/CodeGen/AArch64/cfguard-checks.ll create mode 100644 llvm/test/CodeGen/AArch64/cfguard-module-flag.ll create mode 100644 llvm/test/CodeGen/ARM/cfguard-checks.ll create mode 100644 llvm/test/CodeGen/ARM/cfguard-module-flag.ll create mode 100644 llvm/test/CodeGen/X86/cfguard-checks.ll create mode 100644 llvm/test/CodeGen/X86/cfguard-module-flag.ll create mode 100644 llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll create mode 100644 llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll diff --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst index 5f8264762c853..976ac3b25ff65 100644 --- a/clang/docs/ClangCommandLineReference.rst +++ b/clang/docs/ClangCommandLineReference.rst @@ -126,7 +126,11 @@ Output path for the plist report .. 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 diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index cd7a84548765c..35880917b20e5 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -37,6 +37,7 @@ CODEGENOPT(AssumeSaneOperatorNew , 1, 1) ///< implicit __attribute__((malloc)) o 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. diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index 1f6c000ecf6a1..cf2c56c211685 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -399,6 +399,10 @@ def msign_return_address_key_EQ : Joined<["-"], "msign-return-address-key=">, Values<"a_key,b_key">; def mbranch_target_enforce : Flag<["-"], "mbranch-target-enforce">; def fno_dllexport_inlines : Flag<["-"], "fno-dllexport-inlines">; +def cfguard_no_checks : Flag<["-"], "cfguard-no-checks">, + HelpText<"Emit Windows Control Flow Guard tables only (no checks)">; +def cfguard : Flag<["-"], "cfguard">, + HelpText<"Emit Windows Control Flow Guard tables and checks">; //===----------------------------------------------------------------------===// // Dependency Output Options diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 4ea8bfff0973b..83d3dc9cc3d18 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -497,8 +497,6 @@ def bind__at__load : Flag<["-"], "bind_at_load">; def bundle__loader : Separate<["-"], "bundle_loader">; def bundle : Flag<["-"], "bundle">; def b : JoinedOrSeparate<["-"], "b">, Flags<[Unsupported]>; -def cfguard : Flag<["-"], "cfguard">, Flags<[CC1Option]>, - HelpText<"Emit tables required for Windows Control Flow Guard.">; def cl_opt_disable : Flag<["-"], "cl-opt-disable">, 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, Flags<[CC1Option]>, diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 1fd4e4cf8b8fe..19b935ffab361 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -487,8 +487,11 @@ void CodeGenModule::Release() { getModule().addModuleFlag(llvm::Module::Warning, "CodeViewGHash", 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); + } else if (CodeGenOpts.ControlFlowGuardNoChecks) { + // Function ID tables for Control Flow Guard (cfguard=1). + getModule().addModuleFlag(llvm::Module::Warning, "cfguard", 1); } if (CodeGenOpts.OptimizationLevel > 0 && CodeGenOpts.StrictVTablePointers) { // We don't support LTO with 2 with different StrictVTablePointers diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index dd461a1976d93..682db213d1180 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5916,26 +5916,19 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType, } if (Arg *A = Args.getLastArg(options::OPT__SLASH_guard)) { - SmallVector SplitArgs; - StringRef(A->getValue()).split(SplitArgs, ","); - bool Instrument = false; - bool NoChecks = false; - for (StringRef Arg : SplitArgs) { - if (Arg.equals_lower("cf")) - Instrument = true; - else if (Arg.equals_lower("cf-")) - Instrument = false; - else if (Arg.equals_lower("nochecks")) - NoChecks = true; - else if (Arg.equals_lower("nochecks-")) - NoChecks = false; - 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) + StringRef GuardArgs = A->getValue(); + // The only valid options are "cf", "cf,nochecks", and "cf-". + if (GuardArgs.equals_lower("cf")) { + // Emit CFG instrumentation and the table of address-taken functions. CmdArgs.push_back("-cfguard"); + } else if (GuardArgs.equals_lower("cf,nochecks")) { + // Emit only the table of address-taken functions. + CmdArgs.push_back("-cfguard-no-checks"); + } else if (GuardArgs.equals_lower("cf-")) { + // Do nothing, but we might want to emit a security warning in future. + } else { + D.Diag(diag::err_drv_invalid_value) << A->getSpelling() << GuardArgs; + } } } diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp index 6ed80a8f47523..ca2c538c3d3d1 100644 --- a/clang/lib/Driver/ToolChains/MSVC.cpp +++ b/clang/lib/Driver/ToolChains/MSVC.cpp @@ -417,6 +417,17 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, Args.AddAllArgValues(CmdArgs, options::OPT__SLASH_link); + // Control Flow Guard checks + if (Arg *A = Args.getLastArg(options::OPT__SLASH_guard)) { + StringRef GuardArgs = A->getValue(); + if (GuardArgs.equals_lower("cf") || GuardArgs.equals_lower("cf,nochecks")) { + // MSVC doesn't yet support the "nochecks" modifier. + CmdArgs.push_back("-guard:cf"); + } else if (GuardArgs.equals_lower("cf-")) { + CmdArgs.push_back("-guard:cf-"); + } + } + if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, options::OPT_fno_openmp, false)) { CmdArgs.push_back("-nodefaultlib:vcomp.lib"); @@ -674,6 +685,17 @@ std::unique_ptr visualstudio::Compiler::GetCommand( : "/Zc:threadSafeInit-"); } + // Control Flow Guard checks + if (Arg *A = Args.getLastArg(options::OPT__SLASH_guard)) { + StringRef GuardArgs = A->getValue(); + if (GuardArgs.equals_lower("cf") || GuardArgs.equals_lower("cf,nochecks")) { + // MSVC doesn't yet support the "nochecks" modifier. + CmdArgs.push_back("/guard:cf"); + } else if (GuardArgs.equals_lower("cf-")) { + CmdArgs.push_back("/guard:cf-"); + } + } + // Pass through all unknown arguments so that the fallback command can see // them too. Args.AddAllArgs(CmdArgs, options::OPT_UNKNOWN); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index bc54e38a1a63b..4e16eca961dc9 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -944,6 +944,7 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, 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); diff --git a/clang/test/CodeGen/cfguardtable.c b/clang/test/CodeGen/cfguardtable.c index 964929261297b..05284f6d23a69 100644 --- a/clang/test/CodeGen/cfguardtable.c +++ b/clang/test/CodeGen/cfguardtable.c @@ -1,6 +1,8 @@ -// 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} diff --git a/clang/test/Driver/cl-fallback.c b/clang/test/Driver/cl-fallback.c index 2e5698ce9f9fe..a840883255fc0 100644 --- a/clang/test/Driver/cl-fallback.c +++ b/clang/test/Driver/cl-fallback.c @@ -2,7 +2,7 @@ // command-line option, e.g. on Mac where %s is commonly under /Users. // RUN: %clang_cl --target=i686-pc-win32 /fallback /Dfoo=bar /Ubaz /Ifoo /O0 /Ox /GR /GR- /GS /GS- /Gy /Gy- \ -// RUN: /Gw /Gw- /LD /LDd /EHs /EHs- /Zl /MD /MDd /MTd /MT /FImyheader.h /Zi \ +// RUN: /Gw /Gw- /LD /LDd /EHs /EHs- /Zl /MD /MDd /MTd /MT /guard:cf /guard:cf- /FImyheader.h /Zi \ // RUN: -garbage -moregarbage \ // RUN: -### -- %s 2>&1 \ // RUN: | FileCheck %s @@ -33,6 +33,7 @@ // CHECK: "/EHs-" // CHECK: "/Zl" // CHECK: "/MT" +// CHECK: "/guard:cf-" // CHECK: "-garbage" // CHECK: "-moregarbage" // CHECK: "/Tc" "{{.*cl-fallback.c}}" diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c index 04aa08a5576e4..687ce59d4e5e0 100644 --- a/clang/test/Driver/cl-options.c +++ b/clang/test/Driver/cl-options.c @@ -598,9 +598,14 @@ // 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 +// CFGUARDNOCHECKS: -cfguard-no-checks + +// RUN: %clang_cl /guard:nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARDNOCHECKSINVALID %s +// CFGUARDNOCHECKSINVALID: invalid value 'nochecks' in '/guard:' // RUN: %clang_cl /guard:foo -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARDINVALID %s // CFGUARDINVALID: invalid value 'foo' in '/guard:' diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 87e8a557504a2..d0484189d97fe 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -436,6 +436,17 @@ added in the future: - 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, + calls to 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 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. "``cc ``" - Numbered convention Any calling convention may be specified by number, allowing target-specific calling conventions to be used. Target specific diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst index 6ef3ff90e836e..d96f2745f9926 100644 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -145,6 +145,11 @@ Noteworthy optimizations lookup table. +* Windows Control Flow Guard: the ``-cfguard`` option now emits CFG checks on + indirect function calls. The previous behavior is still available with the + ``-cfguard-nochecks`` option. Note that this feature should always be used + with optimizations enabled. + Changes to the LLVM IR ---------------------- diff --git a/llvm/include/llvm/CodeGen/MachineFunction.h b/llvm/include/llvm/CodeGen/MachineFunction.h index 60c13468542af..7b21d35c82063 100644 --- a/llvm/include/llvm/CodeGen/MachineFunction.h +++ b/llvm/include/llvm/CodeGen/MachineFunction.h @@ -303,6 +303,10 @@ class MachineFunction { /// by debug and exception handling consumers. std::vector 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 LongjmpTargets; + /// \name Exception Handling /// \{ @@ -821,6 +825,17 @@ class MachineFunction { 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 &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 /// \{ diff --git a/llvm/include/llvm/CodeGen/Passes.h b/llvm/include/llvm/CodeGen/Passes.h index d92ee93268e7b..a96db623ad961 100644 --- a/llvm/include/llvm/CodeGen/Passes.h +++ b/llvm/include/llvm/CodeGen/Passes.h @@ -447,6 +447,10 @@ namespace llvm { /// 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(); diff --git a/llvm/include/llvm/CodeGen/TargetCallingConv.h b/llvm/include/llvm/CodeGen/TargetCallingConv.h index aebeeecbe5065..c963617c48fb3 100644 --- a/llvm/include/llvm/CodeGen/TargetCallingConv.h +++ b/llvm/include/llvm/CodeGen/TargetCallingConv.h @@ -37,6 +37,7 @@ namespace ISD { unsigned IsSplitEnd : 1; ///< Last part of a split unsigned IsSwiftSelf : 1; ///< Swift self parameter unsigned IsSwiftError : 1; ///< Swift error parameter + unsigned IsCFGuardTarget : 1; ///< Control Flow Guard target unsigned IsHva : 1; ///< HVA field for unsigned IsHvaStart : 1; ///< HVA structure start unsigned IsSecArgPass : 1; ///< Second argument @@ -55,8 +56,8 @@ namespace ISD { ArgFlagsTy() : IsZExt(0), IsSExt(0), IsInReg(0), IsSRet(0), IsByVal(0), IsNest(0), IsReturned(0), IsSplit(0), IsInAlloca(0), IsSplitEnd(0), - IsSwiftSelf(0), IsSwiftError(0), IsHva(0), IsHvaStart(0), - IsSecArgPass(0), ByValAlign(0), OrigAlign(0), + IsSwiftSelf(0), IsSwiftError(0), IsCFGuardTarget(0), IsHva(0), + IsHvaStart(0), IsSecArgPass(0), ByValAlign(0), OrigAlign(0), IsInConsecutiveRegsLast(0), IsInConsecutiveRegs(0), IsCopyElisionCandidate(0), IsPointer(0), ByValSize(0), PointerAddrSpace(0) { @@ -87,6 +88,9 @@ namespace ISD { bool isSwiftError() const { return IsSwiftError; } void setSwiftError() { IsSwiftError = 1; } + bool isCFGuardTarget() const { return IsCFGuardTarget; } + void setCFGuardTarget() { IsCFGuardTarget = 1; } + bool isHva() const { return IsHva; } void setHva() { IsHva = 1; } diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h index ca7548cd8d6f5..96491b33d03b9 100644 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -187,13 +187,14 @@ class TargetLoweringBase { bool IsReturned : 1; bool IsSwiftSelf : 1; bool IsSwiftError : 1; + bool IsCFGuardTarget : 1; uint16_t Alignment = 0; Type *ByValType = nullptr; ArgListEntry() : IsSExt(false), IsZExt(false), IsInReg(false), IsSRet(false), IsNest(false), IsByVal(false), IsInAlloca(false), IsReturned(false), - IsSwiftSelf(false), IsSwiftError(false) {} + IsSwiftSelf(false), IsSwiftError(false), IsCFGuardTarget(false) {} void setAttributes(const CallBase *Call, unsigned ArgIdx); diff --git a/llvm/include/llvm/IR/CallingConv.h b/llvm/include/llvm/IR/CallingConv.h index 6f4989268fa35..5690f1cd944b1 100644 --- a/llvm/include/llvm/IR/CallingConv.h +++ b/llvm/include/llvm/IR/CallingConv.h @@ -75,7 +75,12 @@ namespace CallingConv { // CXX_FAST_TLS - Calling convention for access functions. CXX_FAST_TLS = 17, - // Target - This is the start of the target-specific calling conventions, + /// Special calling convention on Windows for calling the Control + /// Guard Check ICall funtion. The function takes 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 = 19, + // e.g. fastcall and thiscall on X86. FirstTargetCC = 64, diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h index ca419b50da6b7..9894e07ecf785 100644 --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -1039,6 +1039,11 @@ struct OperandBundleUse { return getTagID() == LLVMContext::OB_funclet; } + /// Return true if this is a "cfguardtarget" operand bundle. + bool isCFGuardTargetOperandBundle() const { + return getTagID() == LLVMContext::OB_cfguardtarget; + } + private: /// Pointer to an entry in LLVMContextImpl::getOrInsertBundleTag. StringMapEntry *Tag; diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h index c805045004187..ee6ec0d6e7f29 100644 --- a/llvm/include/llvm/IR/LLVMContext.h +++ b/llvm/include/llvm/IR/LLVMContext.h @@ -110,6 +110,7 @@ class LLVMContext { OB_deopt = 0, // "deopt" OB_funclet = 1, // "funclet" OB_gc_transition = 2, // "gc-transition" + OB_cfguardtarget = 3, // "cfguardtarget" }; /// getMDKindID - Return a unique non-zero ID for the specified metadata kind. diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h index 164d0be2855ad..0f02f7ee2a532 100644 --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -91,6 +91,8 @@ void initializeCFGOnlyPrinterLegacyPassPass(PassRegistry&); void initializeCFGOnlyViewerLegacyPassPass(PassRegistry&); void initializeCFGPrinterLegacyPassPass(PassRegistry&); void initializeCFGSimplifyPassPass(PassRegistry&); +void initializeCFGuardPass(PassRegistry&); +void initializeCFGuardLongjmpPass(PassRegistry&); void initializeCFGViewerLegacyPassPass(PassRegistry&); void initializeCFIInstrInserterPass(PassRegistry&); void initializeCFLAndersAAWrapperPassPass(PassRegistry&); diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h index abc87bf277488..a28ddd7fa45f7 100644 --- a/llvm/include/llvm/MC/MCObjectFileInfo.h +++ b/llvm/include/llvm/MC/MCObjectFileInfo.h @@ -211,6 +211,7 @@ class MCObjectFileInfo { MCSection *XDataSection; MCSection *SXDataSection; MCSection *GFIDsSection; + MCSection *GLJMPSection; public: void InitMCObjectFileInfo(const Triple &TT, bool PIC, MCContext &ctx, @@ -379,6 +380,7 @@ class MCObjectFileInfo { MCSection *getXDataSection() const { return XDataSection; } MCSection *getSXDataSection() const { return SXDataSection; } MCSection *getGFIDsSection() const { return GFIDsSection; } + MCSection *getGLJMPSection() const { return GLJMPSection; } MCSection *getEHFrameSection() { return EHFrameSection; diff --git a/llvm/include/llvm/Target/TargetCallingConv.td b/llvm/include/llvm/Target/TargetCallingConv.td index 1bc03cf8a49dd..812ae9bd6cf91 100644 --- a/llvm/include/llvm/Target/TargetCallingConv.td +++ b/llvm/include/llvm/Target/TargetCallingConv.td @@ -51,6 +51,11 @@ class CCIfSwiftSelf : CCIf<"ArgFlags.isSwiftSelf()", A> { class CCIfSwiftError : CCIf<"ArgFlags.isSwiftError()", A> { } +/// CCIfCFGuardTarget - If the current argument has cfguardtarget parameter +/// attribute, apply Action A. +class CCIfCFGuardTarget : CCIf<"ArgFlags.isCFGuardTarget()", A> { +} + /// CCIfConsecutiveRegs - If the current argument has InConsecutiveRegs /// parameter attribute, apply Action A. class CCIfConsecutiveRegs : CCIf<"ArgFlags.isInConsecutiveRegs()", A> { diff --git a/llvm/include/llvm/Transforms/CFGuard.h b/llvm/include/llvm/Transforms/CFGuard.h new file mode 100644 index 0000000000000..86fcbc3c13e8b --- /dev/null +++ b/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 FunctionPass; + +/// Insert Control FLow Guard checks on indirect function calls. +FunctionPass *createCFGuardCheckPass(); + +/// Insert Control FLow Guard dispatches on indirect function calls. +FunctionPass *createCFGuardDispatchPass(); + +} // namespace llvm + +#endif diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index 72d2357c29333..0f1d245872336 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -585,6 +585,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(ccc); KEYWORD(fastcc); KEYWORD(coldcc); + KEYWORD(cfguard_checkcc); KEYWORD(x86_stdcallcc); KEYWORD(x86_fastcallcc); KEYWORD(x86_thiscallcc); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 87dff6468f2d0..9d0a18dd0ad8b 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1920,6 +1920,7 @@ void LLParser::ParseOptionalDLLStorageClass(unsigned &Res) { /// ::= 'fastcc' /// ::= 'intel_ocl_bicc' /// ::= 'coldcc' +/// ::= 'cfguard_checkcc' /// ::= 'x86_stdcallcc' /// ::= 'x86_fastcallcc' /// ::= 'x86_thiscallcc' @@ -1963,6 +1964,7 @@ bool LLParser::ParseOptionalCallingConv(unsigned &CC) { 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_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; diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h index 0e9ba4db47427..21a7c4d1848af 100644 --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -132,6 +132,7 @@ enum Kind { kw_fastcc, kw_coldcc, kw_intel_ocl_bicc, + kw_cfguard_checkcc, kw_x86_stdcallcc, kw_x86_fastcallcc, kw_x86_thiscallcc, diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 3e102f4732a2c..1fd95ad5c1bc0 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -137,7 +137,7 @@ static const char *const DbgTimerDescription = "Debug Info Emission"; 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 @@ bool AsmPrinter::doInitialization(Module &M) { EHTimerDescription, DWARFGroupName, DWARFGroupDescription); + // Emit tables for any value of cfguard flag (i.e. cfguard=1 or cfguard=2). if (mdconst::extract_or_null( - MMI->getModule()->getModuleFlag("cfguardtable"))) + MMI->getModule()->getModuleFlag("cfguard"))) Handlers.emplace_back(llvm::make_unique(this), CFGuardName, CFGuardDescription, DWARFGroupName, DWARFGroupDescription); - return false; } diff --git a/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp b/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp index 290be81c6baab..f071a2583e5a4 100644 --- a/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp +++ b/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(AsmPrinter *A) : AsmPrinterHandler(), Asm(A) {} 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 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); + } } diff --git a/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h b/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h index def0a59ab0075..494a153b05bab 100644 --- a/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h +++ b/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 namespace llvm { class LLVM_LIBRARY_VISIBILITY WinCFGuard : public AsmPrinterHandler { /// Target of directive emission. AsmPrinter *Asm; + std::vector LongjmpTargets; public: WinCFGuard(AsmPrinter *A); @@ -28,7 +31,7 @@ class LLVM_LIBRARY_VISIBILITY WinCFGuard : public AsmPrinterHandler { void setSymbolSize(const MCSymbol *Sym, uint64_t Size) override {} - /// Emit the Control Flow Guard function ID table + /// Emit the Control Flow Guard function ID table. void endModule() override; /// Gather pre-function debug information. @@ -39,7 +42,7 @@ class LLVM_LIBRARY_VISIBILITY WinCFGuard : public AsmPrinterHandler { /// 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 {} diff --git a/llvm/lib/CodeGen/CFGuardLongjmp.cpp b/llvm/lib/CodeGen/CFGuardLongjmp.cpp new file mode 100644 index 0000000000000..42ad22b6cfab6 --- /dev/null +++ b/llvm/lib/CodeGen/CFGuardLongjmp.cpp @@ -0,0 +1,119 @@ +//===-- 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 (!MF.getMMI().getModule()->getModuleFlag("cfguard")) + return false; + + // Skip functions that do not have calls to _setjmp. + if (!MF.getFunction().callsFunctionThatReturnsTwice()) + return false; + + SmallVector 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. + if (!MI.isCall() || MI.getNumOperands() < 1) + continue; + + // Iterate over operands to find calls to global functions. + for (MachineOperand &MO : MI.operands()) { + if (!MO.isGlobal()) + continue; + + auto *F = dyn_cast(MO.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); + break; + } + } + } + } + + if (SetjmpCalls.empty()) + return false; + + unsigned SetjmpNum = 0; + + // For each possible target, create a new symbol and insert it immediately + // after the call to setjmp. Add this symbol to the MachineFunction's list + // of longjmp targets. + for (MachineInstr *Setjmp : SetjmpCalls) { + SmallString<128> SymbolName; + raw_svector_ostream(SymbolName) << "$cfgsj_" << MF.getName() << SetjmpNum++; + MCSymbol *SjSymbol = MF.getContext().getOrCreateSymbol(SymbolName); + + Setjmp->setPostInstrSymbol(MF, SjSymbol); + MF.addLongjmpTarget(SjSymbol); + CFGuardLongjmpTargets++; + } + + return true; +} diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt index 5d8a13828673e..327e4ffcbb607 100644 --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -10,6 +10,7 @@ add_llvm_library(LLVMCodeGen BuiltinGCs.cpp CalcSpillWeights.cpp CallingConvLower.cpp + CFGuardLongjmp.cpp CFIInstrInserter.cpp CodeGen.cpp CodeGenPrepare.cpp diff --git a/llvm/lib/CodeGen/CodeGen.cpp b/llvm/lib/CodeGen/CodeGen.cpp index c37ed57781d44..feaaa17ec3af0 100644 --- a/llvm/lib/CodeGen/CodeGen.cpp +++ b/llvm/lib/CodeGen/CodeGen.cpp @@ -22,6 +22,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) { initializeAtomicExpandPass(Registry); initializeBranchFolderPassPass(Registry); initializeBranchRelaxationPass(Registry); + initializeCFGuardLongjmpPass(Registry); initializeCFIInstrInserterPass(Registry); initializeCodeGenPreparePass(Registry); initializeDeadMachineInstructionElimPass(Registry); diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp index 16ac45398d06a..3edc045085bf1 100644 --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -1560,6 +1560,10 @@ bool IRTranslator::translateCall(const User &U, MachineIRBuilder &MIRBuilder) { if (F && F->hasDLLImportStorageClass()) return false; + // FIXME: support control flow guard targets. + if (CI.countOperandBundlesOfType(LLVMContext::OB_cfguardtarget)) + return false; + if (CI.isInlineAsm()) return translateInlineAsm(CI, MIRBuilder); @@ -1663,6 +1667,10 @@ bool IRTranslator::translateInvoke(const User &U, if (I.countOperandBundlesOfType(LLVMContext::OB_deopt)) return false; + // FIXME: support control flow guard targets. + if (I.countOperandBundlesOfType(LLVMContext::OB_cfguardtarget)) + return false; + // FIXME: support Windows exception handling. if (!isa(EHPadBB->front())) return false; diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp index 2638b1e8a05cc..2c9be9f507c76 100644 --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -1205,6 +1205,8 @@ bool FastISel::lowerCallTo(CallLoweringInfo &CLI) { Flags.setSwiftSelf(); if (Arg.IsSwiftError) Flags.setSwiftError(); + if (Arg.IsCFGuardTarget) + Flags.setCFGuardTarget(); if (Arg.IsByVal) Flags.setByVal(); if (Arg.IsInAlloca) { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 4120a401b696b..97a7ffb30c2ca 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -2741,8 +2741,9 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) { // Deopt bundles are lowered in LowerCallSiteWithDeoptBundle, and we don't // have to do anything here to lower funclet bundles. - assert(!I.hasOperandBundlesOtherThan( - {LLVMContext::OB_deopt, LLVMContext::OB_funclet}) && + assert(!I.hasOperandBundlesOtherThan({LLVMContext::OB_deopt, + LLVMContext::OB_funclet, + LLVMContext::OB_cfguardtarget}) && "Cannot lower invokes with arbitrary operand bundles yet!"); const Value *Callee(I.getCalledValue()); @@ -7072,6 +7073,18 @@ void SelectionDAGBuilder::LowerCallTo(ImmutableCallSite CS, SDValue Callee, isTailCall = false; } + // If call site has a cfguardtarget operand bundle, create and add an + // additional ArgListEntry. + if (auto Bundle = CS.getOperandBundle(LLVMContext::OB_cfguardtarget)) { + TargetLowering::ArgListEntry Entry; + Value *V = Bundle->Inputs[0]; + SDValue ArgNode = getValue(V); + Entry.Node = ArgNode; + Entry.Ty = V->getType(); + Entry.IsCFGuardTarget = true; + Args.push_back(Entry); + } + // Check if target-independent constraints permit a tail call here. // Target-dependent constraints are checked within TLI->LowerCallTo. if (isTailCall && !isInTailCallPosition(CS, DAG.getTarget())) @@ -7613,8 +7626,10 @@ void SelectionDAGBuilder::visitCall(const CallInst &I) { // Deopt bundles are lowered in LowerCallSiteWithDeoptBundle, and we don't // have to do anything here to lower funclet bundles. - assert(!I.hasOperandBundlesOtherThan( - {LLVMContext::OB_deopt, LLVMContext::OB_funclet}) && + // CFGuardTarget bundles are lowered in LowerCallTo. + assert(!I.hasOperandBundlesOtherThan({LLVMContext::OB_deopt, + LLVMContext::OB_funclet, + LLVMContext::OB_cfguardtarget}) && "Cannot lower calls with arbitrary operand bundles!"); SDValue Callee = getValue(I.getCalledValue()); @@ -8957,6 +8972,7 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const { Entry.IsReturned = false; Entry.IsSwiftSelf = false; Entry.IsSwiftError = false; + Entry.IsCFGuardTarget = false; Entry.Alignment = Align; CLI.getArgs().insert(CLI.getArgs().begin(), Entry); CLI.NumFixedArgs += 1; @@ -9069,6 +9085,8 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const { Flags.setSwiftSelf(); if (Args[i].IsSwiftError) Flags.setSwiftError(); + if (Args[i].IsCFGuardTarget) + Flags.setCFGuardTarget(); if (Args[i].IsByVal) Flags.setByVal(); if (Args[i].IsInAlloca) { diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index eb5760daecb3c..c76c1ee1b1c1a 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -352,6 +352,7 @@ static void PrintCallingConv(unsigned cc, raw_ostream &Out) { 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::X86_StdCall: Out << "x86_stdcallcc"; break; case CallingConv::X86_FastCall: Out << "x86_fastcallcc"; break; case CallingConv::X86_ThisCall: Out << "x86_thiscallcc"; break; diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp index e1cdf6b539dbf..340e0f56dd2dc 100644 --- a/llvm/lib/IR/LLVMContext.cpp +++ b/llvm/lib/IR/LLVMContext.cpp @@ -87,6 +87,11 @@ LLVMContext::LLVMContext() : pImpl(new LLVMContextImpl(*this)) { "gc-transition operand bundle id drifted!"); (void)GCTransitionEntry; + auto *CFGuardTargetEntry = pImpl->getOrInsertBundleTag("cfguardtarget"); + assert(CFGuardTargetEntry->second == LLVMContext::OB_cfguardtarget && + "cfguardtarget operand bundle id drifted!"); + (void)CFGuardTargetEntry; + SyncScope::ID SingleThreadSSID = pImpl->getOrInsertSyncScopeID("singlethread"); assert(SingleThreadSSID == SyncScope::SingleThread && diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 9346c8bda75dd..494d6a93c5d76 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -2958,10 +2958,10 @@ void Verifier::visitCallBase(CallBase &Call) { if (Intrinsic::ID ID = (Intrinsic::ID)F->getIntrinsicID()) visitIntrinsicCall(ID, Call); - // Verify that a callsite has at most one "deopt", at most one "funclet" and - // at most one "gc-transition" operand bundle. + // Verify that a callsite has at most one "deopt", at most one "funclet", at + // most one "gc-transition", and at most one "cfguardtarget" operand bundle. bool FoundDeoptBundle = false, FoundFuncletBundle = false, - FoundGCTransitionBundle = false; + FoundGCTransitionBundle = false, FoundCFGuardTargetBundle = false; for (unsigned i = 0, e = Call.getNumOperandBundles(); i < e; ++i) { OperandBundleUse BU = Call.getOperandBundleAt(i); uint32_t Tag = BU.getTagID(); @@ -2980,6 +2980,12 @@ void Verifier::visitCallBase(CallBase &Call) { Assert(isa(BU.Inputs.front()), "Funclet bundle operands should correspond to a FuncletPadInst", Call); + } else if (Tag == LLVMContext::OB_cfguardtarget) { + Assert(!FoundCFGuardTargetBundle, + "Multiple CFGuardTarget operand bundles", Call); + FoundCFGuardTargetBundle = true; + Assert(BU.Inputs.size() == 1, + "Expected exactly one cfguardtarget bundle operand", Call); } } diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index 9f555abe14045..f5b21b4f638e8 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -714,6 +714,11 @@ void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) { 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, diff --git a/llvm/lib/Target/AArch64/AArch64CallingConvention.h b/llvm/lib/Target/AArch64/AArch64CallingConvention.h index 13cc0c583fd24..9e57401b7a9f8 100644 --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.h +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.h @@ -28,6 +28,9 @@ bool CC_AArch64_DarwinPCS(unsigned ValNo, MVT ValVT, MVT LocVT, 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); diff --git a/llvm/lib/Target/AArch64/AArch64CallingConvention.td b/llvm/lib/Target/AArch64/AArch64CallingConvention.td index d969a9e1ab3a4..907647a05c4df 100644 --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.td +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.td @@ -146,6 +146,13 @@ def CC_AArch64_Win64_VarArg : CallingConv<[ CCDelegateTo ]>; +// 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: @@ -319,6 +326,12 @@ def CSR_Win_AArch64_AAPCS : CalleeSavedRegs<(add FP, LR, X19, X20, X21, X22, 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, diff --git a/llvm/lib/Target/AArch64/AArch64FastISel.cpp b/llvm/lib/Target/AArch64/AArch64FastISel.cpp index 8dc2768b95976..d4503310a0e8c 100644 --- a/llvm/lib/Target/AArch64/AArch64FastISel.cpp +++ b/llvm/lib/Target/AArch64/AArch64FastISel.cpp @@ -348,6 +348,8 @@ CCAssignFn *AArch64FastISel::CCAssignFnForCall(CallingConv::ID CC) const { 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; } diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index 03923878fd516..1e62e4f95a777 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -3021,8 +3021,10 @@ CCAssignFn *AArch64TargetLowering::CCAssignFnForCall(CallingConv::ID CC, return IsVarArg ? CC_AArch64_DarwinPCS_VarArg : CC_AArch64_DarwinPCS; case CallingConv::Win64: return IsVarArg ? CC_AArch64_Win64_VarArg : CC_AArch64_AAPCS; - case CallingConv::AArch64_VectorCall: - return CC_AArch64_AAPCS; + case CallingConv::CFGuard_Check: + return CC_AArch64_Win64_CFGuard_Check; + case CallingConv::AArch64_VectorCall: + return CC_AArch64_AAPCS; } } diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp index 6d5a4e3d2f767..f4b6b4dc1c1c9 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -42,6 +42,8 @@ AArch64RegisterInfo::AArch64RegisterInfo(const Triple &TT) 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().isTargetWindows()) return CSR_Win_AArch64_AAPCS_SaveList; if (MF->getFunction().getCallingConv() == CallingConv::GHC) @@ -120,6 +122,8 @@ AArch64RegisterInfo::getCallPreservedMask(const MachineFunction &MF, : CSR_AArch64_CXX_TLS_Darwin_RegMask; if (CC == CallingConv::AArch64_VectorCall) return SCS ? CSR_AArch64_AAVPCS_SCS_RegMask : CSR_AArch64_AAVPCS_RegMask; + if (CC == CallingConv::CFGuard_Check) + return CSR_Win_AArch64_CFGuard_Check_RegMask; if (MF.getSubtarget().getTargetLowering() ->supportSwiftError() && MF.getFunction().getAttributes().hasAttrSomewhere(Attribute::SwiftError)) diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp index 8654614804997..bb392c17e8675 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ b/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 #include @@ -449,6 +450,10 @@ void AArch64PassConfig::addIRPasses() { } addPass(createAArch64StackTaggingPass()); + + // Add Control Flow Guard checks. + if (TM->getTargetTriple().isOSWindows()) + addPass(createCFGuardCheckPass()); } // Pass Pipeline Configuration @@ -606,6 +611,10 @@ void AArch64PassConfig::addPreEmitPass() { if (EnableBranchTargets) addPass(createAArch64BranchTargetsPass()); + // Identify valid longjmp targets for Windows Control Flow Guard. + if (TM->getTargetTriple().isOSWindows()) + addPass(createCFGuardLongjmpPass()); + if (TM->getOptLevel() != CodeGenOpt::None && EnableCompressJumpTables) addPass(createAArch64CompressJumpTablesPass()); diff --git a/llvm/lib/Target/AArch64/LLVMBuild.txt b/llvm/lib/Target/AArch64/LLVMBuild.txt index 620d3a857e8ca..08ff464545195 100644 --- a/llvm/lib/Target/AArch64/LLVMBuild.txt +++ b/llvm/lib/Target/AArch64/LLVMBuild.txt @@ -30,5 +30,5 @@ has_jit = 1 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 diff --git a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp index dc99b37742daf..884d6fee0a4bd 100644 --- a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp @@ -75,6 +75,8 @@ ARMBaseRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { // 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 @@ ARMBaseRegisterInfo::getCallPreservedMask(const MachineFunction &MF, 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 diff --git a/llvm/lib/Target/ARM/ARMCallingConv.h b/llvm/lib/Target/ARM/ARMCallingConv.h index 615634551d904..7c692f03b4405 100644 --- a/llvm/lib/Target/ARM/ARMCallingConv.h +++ b/llvm/lib/Target/ARM/ARMCallingConv.h @@ -32,6 +32,9 @@ bool CC_ARM_APCS_GHC(unsigned ValNo, MVT ValVT, MVT LocVT, 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); diff --git a/llvm/lib/Target/ARM/ARMCallingConv.td b/llvm/lib/Target/ARM/ARMCallingConv.td index 61d2d83ddc404..55111f544ff77 100644 --- a/llvm/lib/Target/ARM/ARMCallingConv.td +++ b/llvm/lib/Target/ARM/ARMCallingConv.td @@ -246,6 +246,16 @@ def RetCC_ARM_AAPCS_VFP : CallingConv<[ CCDelegateTo ]>; + +// 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_FPRegs : CalleeSavedRegs<(add (sequence "D%u", 0, 31))>; 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)>; diff --git a/llvm/lib/Target/ARM/ARMFastISel.cpp b/llvm/lib/Target/ARM/ARMFastISel.cpp index 6e274d269bf29..ad8cdfb5811d9 100644 --- a/llvm/lib/Target/ARM/ARMFastISel.cpp +++ b/llvm/lib/Target/ARM/ARMFastISel.cpp @@ -1889,6 +1889,8 @@ CCAssignFn *ARMFastISel::CCAssignFnForCall(CallingConv::ID CC, 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); } } diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp index d390c9e237e6a..3a753b8016702 100644 --- a/llvm/lib/Target/ARM/ARMISelLowering.cpp +++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -1784,6 +1784,7 @@ ARMTargetLowering::getEffectiveCallingConv(CallingConv::ID CC, case CallingConv::ARM_AAPCS: case CallingConv::ARM_APCS: case CallingConv::GHC: + case CallingConv::CFGuard_Check: return CC; case CallingConv::PreserveMost: return CallingConv::PreserveMost; @@ -1843,6 +1844,8 @@ CCAssignFn *ARMTargetLowering::CCAssignFnForNode(CallingConv::ID CC, 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); } } diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp b/llvm/lib/Target/ARM/ARMTargetMachine.cpp index 7f0aae1739b33..3d7dc32b98370 100644 --- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp +++ b/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 #include @@ -419,6 +420,10 @@ void ARMPassConfig::addIRPasses() { // 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() { @@ -531,4 +536,8 @@ void ARMPassConfig::addPreEmitPass() { addPass(createARMConstantIslandPass()); addPass(createARMLowOverheadLoopsPass()); + + // Identify valid longjmp targets for Windows Control Flow Guard. + if (TM->getTargetTriple().isOSWindows()) + addPass(createCFGuardLongjmpPass()); } diff --git a/llvm/lib/Target/ARM/LLVMBuild.txt b/llvm/lib/Target/ARM/LLVMBuild.txt index 7a6b3a8401efd..40f8c2c93b07d 100644 --- a/llvm/lib/Target/ARM/LLVMBuild.txt +++ b/llvm/lib/Target/ARM/LLVMBuild.txt @@ -30,5 +30,5 @@ has_jit = 1 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 diff --git a/llvm/lib/Target/X86/LLVMBuild.txt b/llvm/lib/Target/X86/LLVMBuild.txt index bfc0eda21faf2..5d09ced3d9462 100644 --- a/llvm/lib/Target/X86/LLVMBuild.txt +++ b/llvm/lib/Target/X86/LLVMBuild.txt @@ -30,5 +30,5 @@ has_jit = 1 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 diff --git a/llvm/lib/Target/X86/X86AsmPrinter.cpp b/llvm/lib/Target/X86/X86AsmPrinter.cpp index c2ed1ae83b574..3e9436b98769a 100644 --- a/llvm/lib/Target/X86/X86AsmPrinter.cpp +++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp @@ -673,7 +673,7 @@ void X86AsmPrinter::EmitStartOfAsmFile(Module &M) { Feat00Flags |= 1; } - if (M.getModuleFlag("cfguardtable")) + if (M.getModuleFlag("cfguard")) Feat00Flags |= 0x800; // Object is CFG-aware. OutStreamer->EmitSymbolAttribute(S, MCSA_Global); diff --git a/llvm/lib/Target/X86/X86CallingConv.td b/llvm/lib/Target/X86/X86CallingConv.td index 1c3034a5116ab..142925fdd2afc 100644 --- a/llvm/lib/Target/X86/X86CallingConv.td +++ b/llvm/lib/Target/X86/X86CallingConv.td @@ -433,6 +433,7 @@ defm X86_SysV64_RegCall : def RetCC_X86_32 : CallingConv<[ // If FastCC, use RetCC_X86_32_Fast. CCIfCC<"CallingConv::Fast", CCDelegateTo>, + // CFGuard_Check never returns a value so does not need a RetCC. // If HiPE, use RetCC_X86_32_HiPE. CCIfCC<"CallingConv::HiPE", CCDelegateTo>, CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo>, @@ -605,6 +606,9 @@ def CC_X86_Win64_C : CallingConv<[ // A SwiftError is passed in R12. CCIfSwiftError>>, + // The 'CFGuardTarget' parameter, if any, is passed in RAX. + CCIfCFGuardTarget>, + // 128 bit vectors are passed by pointer CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64], CCPassIndirect>, @@ -935,6 +939,12 @@ def CC_X86_32_FastCC : CallingConv<[ CCDelegateTo ]>; +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>, @@ -999,6 +1009,7 @@ def CC_X86_32 : CallingConv<[ CCIfCC<"CallingConv::X86_FastCall", CCDelegateTo>, CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo>, CCIfCC<"CallingConv::X86_ThisCall", CCDelegateTo>, + CCIfCC<"CallingConv::CFGuard_Check", CCDelegateTo>, CCIfCC<"CallingConv::Fast", CCDelegateTo>, CCIfCC<"CallingConv::GHC", CCDelegateTo>, CCIfCC<"CallingConv::HiPE", CCDelegateTo>, @@ -1134,7 +1145,9 @@ def CSR_64_HHVM : CalleeSavedRegs<(add R12)>; // 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, diff --git a/llvm/lib/Target/X86/X86FastISel.cpp b/llvm/lib/Target/X86/X86FastISel.cpp index 7b9ce02712053..7361d990f9492 100644 --- a/llvm/lib/Target/X86/X86FastISel.cpp +++ b/llvm/lib/Target/X86/X86FastISel.cpp @@ -3215,6 +3215,7 @@ bool X86FastISel::fastLowerCall(CallLoweringInfo &CLI) { case CallingConv::X86_ThisCall: case CallingConv::Win64: case CallingConv::X86_64_SysV: + case CallingConv::CFGuard_Check: break; } diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp b/llvm/lib/Target/X86/X86RegisterInfo.cpp index c8966dfffa0cd..accfb9d19a8a1 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.cpp +++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp @@ -341,6 +341,10 @@ X86RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { 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; @@ -455,6 +459,10 @@ X86RegisterInfo::getCallPreservedMask(const MachineFunction &MF, 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; diff --git a/llvm/lib/Target/X86/X86TargetMachine.cpp b/llvm/lib/Target/X86/X86TargetMachine.cpp index 0cbf13899a294..5733e8123cd6b 100644 --- a/llvm/lib/Target/X86/X86TargetMachine.cpp +++ b/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 #include @@ -418,6 +419,16 @@ void X86PassConfig::addIRPasses() { // 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()) { + if (TT.getArch() == Triple::x86_64) { + addPass(createCFGuardDispatchPass()); + } else { + addPass(createCFGuardCheckPass()); + } + } } bool X86PassConfig::addInstSelector() { @@ -527,6 +538,9 @@ void X86PassConfig::addPreEmitPass2() { (!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 X86PassConfig::getCSEConfig() const { diff --git a/llvm/lib/Transforms/CFGuard/CFGuard.cpp b/llvm/lib/Transforms/CFGuard/CFGuard.cpp new file mode 100644 index 0000000000000..9517e21ffdefd --- /dev/null +++ b/llvm/lib/Transforms/CFGuard/CFGuard.cpp @@ -0,0 +1,307 @@ +//===-- 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/IRBuilder.h" +#include "llvm/IR/Instruction.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" + +using namespace llvm; + +using OperandBundleDef = OperandBundleDefT; + +#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. X86_64 targets use the CF_Dispatch mechanism. X86, +/// ARM, and AArch64 targets use the CF_Check machanism. +class CFGuard : public FunctionPass { +public: + static char ID; + + enum Mechanism { CF_Check, CF_Dispatch }; + + // Default constructor required for the INITIALIZE_PASS macro. + CFGuard() : FunctionPass(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) : FunctionPass(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 ()** @__guard_dispatch_icall_fptr + /// %2 = call i32 %1() [ "cfguardtarget"(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 doInitialization(Module &M) override; + bool runOnFunction(Function &F) override; + +private: + // Only add checks if the module has the cfguard=2 flag. + int cfguard_module_flag = 0; + 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(); + + // Cast the guard dispatch global to the type of the called operand. + PointerType *PTy = PointerType::get(CalledOperandType, 0); + if (GuardFnGlobal->getType() != PTy) + GuardFnGlobal = ConstantExpr::getBitCast(GuardFnGlobal, PTy); + + // Load the global as a pointer to a function of the same type. + LoadInst *GuardDispatchLoad = B.CreateLoad(CalledOperandType, GuardFnGlobal); + + // Add the original call target as a cfguardtarget operand bundle. + SmallVector Bundles; + CB->getOperandBundlesAsDefs(Bundles); + Bundles.emplace_back("cfguardtarget", CalledOperand); + + // Create a copy of the call/invoke instruction and add the new bundle. + CallBase *NewCB; + if (CallInst *CI = dyn_cast(CB)) { + NewCB = CallInst::Create(CI, Bundles, CB); + } else { + assert(isa(CB) && "Unknown indirect call type"); + InvokeInst *II = cast(CB); + NewCB = llvm::InvokeInst::Create(II, Bundles, CB); + } + + // Change the target of the call to be the guard dispatch function. + NewCB->setCalledOperand(GuardDispatchLoad); + + // Replace the original call/invoke with the new instruction. + CB->replaceAllUsesWith(NewCB); + + // Delete the original call/invoke. + CB->eraseFromParent(); +} + +bool CFGuard::doInitialization(Module &M) { + + // Check if this module has the cfguard flag and read its value. + if (auto *MD = + mdconst::extract_or_null(M.getModuleFlag("cfguard"))) + cfguard_module_flag = MD->getZExtValue(); + + // Skip modules for which CFGuard checks have been disabled. + if (cfguard_module_flag != 2) + return false; + + // Set up prototypes for the guard check and dispatch functions. + GuardFnType = FunctionType::get(Type::getVoidTy(M.getContext()), + {Type::getInt8PtrTy(M.getContext())}, false); + GuardFnPtrType = PointerType::get(GuardFnType, 0); + + // Get or insert the guard check or dispatch global symbols. + if (GuardMechanism == CF_Check) { + GuardFnGlobal = + M.getOrInsertGlobal("__guard_check_icall_fptr", GuardFnPtrType); + } else { + assert(GuardMechanism == CF_Dispatch && "Invalid CFGuard mechanism"); + GuardFnGlobal = + M.getOrInsertGlobal("__guard_dispatch_icall_fptr", GuardFnPtrType); + } + + return true; +} + +bool CFGuard::runOnFunction(Function &F) { + + // Skip modules and functions for which CFGuard checks have been disabled. + if (cfguard_module_flag != 2 || F.hasFnAttribute(Attribute::NoCfCheck)) + return false; + + SmallVector IndirectCalls; + + // 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. + for (BasicBlock &BB : F.getBasicBlockList()) { + for (Instruction &I : BB.getInstList()) { + auto *CB = dyn_cast(&I); + if (CB && CB->isIndirectCall()) { + IndirectCalls.push_back(CB); + CFGuardCounter++; + } + } + } + + // If no checks are needed, return early and add this attribute to indicate + // that subsequent CFGuard passes can skip this function. + if (IndirectCalls.empty()) { + F.addFnAttr(Attribute::NoCfCheck); + return false; + } + + // For each indirect call/invoke, add the appropriate dispatch or check. + if (GuardMechanism == CF_Dispatch) { + for (CallBase *CB : IndirectCalls) { + insertCFGuardDispatch(CB); + } + } else { + for (CallBase *CB : IndirectCalls) { + insertCFGuardCheck(CB); + } + } + + return true; +} + +char CFGuard::ID = 0; +INITIALIZE_PASS(CFGuard, "CFGuard", "CFGuard", false, false) + +FunctionPass *llvm::createCFGuardCheckPass() { + return new CFGuard(CFGuard::CF_Check); +} + +FunctionPass *llvm::createCFGuardDispatchPass() { + return new CFGuard(CFGuard::CF_Dispatch); +} \ No newline at end of file diff --git a/llvm/lib/Transforms/CFGuard/CMakeLists.txt b/llvm/lib/Transforms/CFGuard/CMakeLists.txt new file mode 100644 index 0000000000000..65d3a0f0b6f8e --- /dev/null +++ b/llvm/lib/Transforms/CFGuard/CMakeLists.txt @@ -0,0 +1,9 @@ +add_llvm_library(LLVMCFGuard + CFGuard.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms + + DEPENDS + intrinsics_gen + ) diff --git a/llvm/lib/Transforms/CFGuard/LLVMBuild.txt b/llvm/lib/Transforms/CFGuard/LLVMBuild.txt new file mode 100644 index 0000000000000..d20cdb443eb30 --- /dev/null +++ b/llvm/lib/Transforms/CFGuard/LLVMBuild.txt @@ -0,0 +1,21 @@ +;===- ./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. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = CFGuard +parent = Transforms +required_libraries = Core Support diff --git a/llvm/lib/Transforms/CMakeLists.txt b/llvm/lib/Transforms/CMakeLists.txt index 74db9e53304da..dda5f6de11e32 100644 --- a/llvm/lib/Transforms/CMakeLists.txt +++ b/llvm/lib/Transforms/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory(Vectorize) add_subdirectory(Hello) add_subdirectory(ObjCARC) add_subdirectory(Coroutines) +add_subdirectory(CFGuard) diff --git a/llvm/lib/Transforms/LLVMBuild.txt b/llvm/lib/Transforms/LLVMBuild.txt index 75c614fd2c47d..5fb5efcc068c8 100644 --- a/llvm/lib/Transforms/LLVMBuild.txt +++ b/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 diff --git a/llvm/test/Bitcode/calling-conventions.3.2.ll b/llvm/test/Bitcode/calling-conventions.3.2.ll index b60f1d70ca042..d968d802b9cd4 100644 --- a/llvm/test/Bitcode/calling-conventions.3.2.ll +++ b/llvm/test/Bitcode/calling-conventions.3.2.ll @@ -26,6 +26,9 @@ declare spir_func void @spir_func() 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 x86_stdcallcc void @x86_stdcallcc() ; CHECK: declare x86_stdcallcc void @x86_stdcallcc @@ -95,6 +98,12 @@ define void @call_intel_ocl_bicc() { 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_x86_stdcallcc() { ; CHECK: call x86_stdcallcc void @x86_stdcallcc call x86_stdcallcc void @x86_stdcallcc() diff --git a/llvm/test/Bitcode/calling-conventions.3.2.ll.bc b/llvm/test/Bitcode/calling-conventions.3.2.ll.bc index b3fad967db0e04778d63fcd3dda2a0e3777aea91..b7ac769a82b34aeabe70edab8562f53bc0601f31 100644 GIT binary patch literal 2568 zcmZvde{2(F7{}l4x?LIdT42LY+S+p?nMB8}MQGR7xE<;QrbQvaWTx$|Evt0ht*vFC zF*n+I1!q#m(5k2`5*3X}jTkZUM@Iqc(ir@isfe2!6UjyvMJ4JVK6f2!ckx~N-ury+ zd7t-rzR%mPh0a{KqY9yNgiwc)s%v}rV_(}pCmz_my>_F_SdK^~LNhXimX?vI4189? z2k)yr*qoE!SD`jq24!*O>T*SmUjB3kr@XtYWpHVWM{TS!D>R!cjmAVPdz!4>xSStQ zHxHJ1a*B7V4D>w-!)G$1F|Jutzb4uEM7sT;A)AA^`wa+H!qa$5PGRu$%MX#q;xJ9d zhm%#yw>&klH164=R@b7{$P1nb-9&C7e$1TlzB}91@R9BHaKHcWYi$kTPu=USvvq^=PNsRVZF8n_vCrt{ecKg9Cn3(xarxWoBFZ4;@yp{&UMmt*;cC6RvJFW>tyy`* zP$lR*KQtWq&02Hct@7&tAqvrNMjd5aB`5xsmcJ@FapTWRTFK%@A^9^KR}8fj5dsl@ zTe8~RUxFa9{*{|JNn)ECk|jQ(R(szm$qEUf);v?96+)1Fb)ZyFBiH6uPAkbls0v}TuNN1ZoQ(aT7^*J4DdtnF)29h=?Sf`N@h>c=6H6X zg~Rvs;^9<}avG^-)x=efFezK~r$%(24{Lo?x#$*KmB;Kk+CG5U^SuKR7JpoU?`h>( z7T?Qa(O#Z4@HP2m#7vTypj1IhH91GjIS7;bwC)0_9V2z8+9` zr1pHD{`|OhYC?BeruS*Kn$vXCk(f2_sGBdu#qaLLdsADtTurJ31@w>VN8Q>>r0&9q z?n#+mNSM^kxhuBy$ISVxHBH+=kbh6vrf6#d%T~p+=(q-KvnNeKw8z5Ah7Be2XR%nvR}Y&M+H3byzN-bdITDV*^kC- z(7a?;_`0|a2nk>J74ngUOvV{B@{XfmN_Ymi`+s;2xNCu9u&)s39Pn4l+y?mdSYYFI z8u`l-HU{h^3A+UBh=ip`jr>IkIk!zQ=-$KN#@_oZU>o+2I$q?0n1}@;I4JFSCnbdd=&d;fRNE2Lc5{ zjJRM$I{o3oJ9ntd>1LTQ!-y-%(i!!!4AT{6*O@3M92jC4HqtwviLml`qkhIKgmMa* z3eWl6MN}xD@V1c>Uze0pw1s*^OH!1$b8%&&+oJLoNU_ERR4i0P#cGRYq2$GOp>B~a zDqbW-1s2SL42@I+{Dq@IgU4htGA5(fL|Hr@(*|E-hZl%t{W^=8F*6%H-VTN_dl;&r vJrIa?wMV?+NPCMv!gPDQ?Ltt0aOdqzcQEP=M*Q8uaD%DA*boR5t_%7PVM^52 literal 1236 zcmbu8PfXKL9LIm#I`%fIE3m*&hV3?t#wFr5bS7Iiw_-}fWEhQH2!$Bp#RHlcIVhzR zEHdSv2acc~IG8x};=zOglMq1@nK3&Vj0y2#f;uk-#Mcf7q(_@<@0-v2zVGk*mNgnn zePT!euMVJCq(Kh=rTAm&agCf=BpQk+@99yUMAjHlWi>I_E}(jSbhJ3iQeImC*&8fg z*3DUl8CUv(Kvpu>-KIsEYM+{ziY%<-NEo02%y{)WWkg@;Bg&X368`!kO%B^i8>K|C z#5Pi-3u**C#B4f*tpN5%h*L$1s^^qO0R2Zqr4~RfThh#^ikF>FqpHHTt9_~xn0sJR z<^oP*Y=yH&36+seL{nkJV@hQ>kShc}ECdD$fkGiLAFR|O&5}50ck!F6--^XOgF|m$u+QkqxR>7{XQ~U zJvZxkH(AwDYgBE$2bT?@#aJj|2;q!Q8lV-gCr41~`Gy$J9G+Ii3sU4M89YnJk$5Sf zPkLr@^!wKMeg41@hE{gj8Wi2As1}L5abSDP`o`C?6}c!!#(R4GWz!oEk{dcFy2+Sv za>cW)6+cc!V!a2ab?dk>+P>h1r5~-Y{!|El>_MW#zhmnPV%wIlWhTn*o3zzz^rE!GL7AfNlB|i-*7bo%-3w0vAjz;P1oE~2shG?_c8OQa)m_ty^ zU4HFC<-85wLSzE?3P$=o-W7S$y34RO5zPEL#(tm%YZOnWhf_TGr-wEgz<}2>Vpg67 z;03JP=-n)GNcpSnWn8tE#T{C*p=HUm^5*RAH!Nw{IICq7 TgqBUEB~9K-Ny}2K!j$JP{_!k4 diff --git a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll index 8f7aa37cbed80..ac7feec77aa1b 100644 --- a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll +++ b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll @@ -6,6 +6,7 @@ ; CHECK-NEXT: Date: Mon, 28 Oct 2019 14:39:45 -0400 Subject: [PATCH 2/4] Convert files added in d157a9bc8ba1 to unix line endings. Ran: git show --diff-filter=A --stat d157a9bc8ba1 | grep '|' | \ awk '{ print $1 }' | xargs dos2unix --- llvm/lib/CodeGen/CFGuardLongjmp.cpp | 238 +++---- llvm/lib/Transforms/CFGuard/CFGuard.cpp | 612 +++++++++--------- llvm/lib/Transforms/CFGuard/CMakeLists.txt | 18 +- llvm/test/CodeGen/AArch64/cfguard-checks.ll | 294 ++++----- .../CodeGen/AArch64/cfguard-module-flag.ll | 50 +- llvm/test/CodeGen/ARM/cfguard-checks.ll | 302 ++++----- llvm/test/CodeGen/ARM/cfguard-module-flag.ll | 52 +- llvm/test/CodeGen/X86/cfguard-checks.ll | 462 ++++++------- llvm/test/CodeGen/X86/cfguard-module-flag.ll | 52 +- .../CodeGen/X86/cfguard-x86-64-vectorcall.ll | 76 +-- .../CodeGen/X86/cfguard-x86-vectorcall.ll | 86 +-- 11 files changed, 1121 insertions(+), 1121 deletions(-) diff --git a/llvm/lib/CodeGen/CFGuardLongjmp.cpp b/llvm/lib/CodeGen/CFGuardLongjmp.cpp index 42ad22b6cfab6..1ae84714625f8 100644 --- a/llvm/lib/CodeGen/CFGuardLongjmp.cpp +++ b/llvm/lib/CodeGen/CFGuardLongjmp.cpp @@ -1,119 +1,119 @@ -//===-- 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 (!MF.getMMI().getModule()->getModuleFlag("cfguard")) - return false; - - // Skip functions that do not have calls to _setjmp. - if (!MF.getFunction().callsFunctionThatReturnsTwice()) - return false; - - SmallVector 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. - if (!MI.isCall() || MI.getNumOperands() < 1) - continue; - - // Iterate over operands to find calls to global functions. - for (MachineOperand &MO : MI.operands()) { - if (!MO.isGlobal()) - continue; - - auto *F = dyn_cast(MO.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); - break; - } - } - } - } - - if (SetjmpCalls.empty()) - return false; - - unsigned SetjmpNum = 0; - - // For each possible target, create a new symbol and insert it immediately - // after the call to setjmp. Add this symbol to the MachineFunction's list - // of longjmp targets. - for (MachineInstr *Setjmp : SetjmpCalls) { - SmallString<128> SymbolName; - raw_svector_ostream(SymbolName) << "$cfgsj_" << MF.getName() << SetjmpNum++; - MCSymbol *SjSymbol = MF.getContext().getOrCreateSymbol(SymbolName); - - Setjmp->setPostInstrSymbol(MF, SjSymbol); - MF.addLongjmpTarget(SjSymbol); - CFGuardLongjmpTargets++; - } - - return true; -} +//===-- 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 (!MF.getMMI().getModule()->getModuleFlag("cfguard")) + return false; + + // Skip functions that do not have calls to _setjmp. + if (!MF.getFunction().callsFunctionThatReturnsTwice()) + return false; + + SmallVector 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. + if (!MI.isCall() || MI.getNumOperands() < 1) + continue; + + // Iterate over operands to find calls to global functions. + for (MachineOperand &MO : MI.operands()) { + if (!MO.isGlobal()) + continue; + + auto *F = dyn_cast(MO.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); + break; + } + } + } + } + + if (SetjmpCalls.empty()) + return false; + + unsigned SetjmpNum = 0; + + // For each possible target, create a new symbol and insert it immediately + // after the call to setjmp. Add this symbol to the MachineFunction's list + // of longjmp targets. + for (MachineInstr *Setjmp : SetjmpCalls) { + SmallString<128> SymbolName; + raw_svector_ostream(SymbolName) << "$cfgsj_" << MF.getName() << SetjmpNum++; + MCSymbol *SjSymbol = MF.getContext().getOrCreateSymbol(SymbolName); + + Setjmp->setPostInstrSymbol(MF, SjSymbol); + MF.addLongjmpTarget(SjSymbol); + CFGuardLongjmpTargets++; + } + + return true; +} diff --git a/llvm/lib/Transforms/CFGuard/CFGuard.cpp b/llvm/lib/Transforms/CFGuard/CFGuard.cpp index 9517e21ffdefd..3eca00691e082 100644 --- a/llvm/lib/Transforms/CFGuard/CFGuard.cpp +++ b/llvm/lib/Transforms/CFGuard/CFGuard.cpp @@ -1,307 +1,307 @@ -//===-- 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/IRBuilder.h" -#include "llvm/IR/Instruction.h" -#include "llvm/InitializePasses.h" -#include "llvm/Pass.h" - -using namespace llvm; - -using OperandBundleDef = OperandBundleDefT; - -#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. X86_64 targets use the CF_Dispatch mechanism. X86, -/// ARM, and AArch64 targets use the CF_Check machanism. -class CFGuard : public FunctionPass { -public: - static char ID; - - enum Mechanism { CF_Check, CF_Dispatch }; - - // Default constructor required for the INITIALIZE_PASS macro. - CFGuard() : FunctionPass(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) : FunctionPass(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 ()** @__guard_dispatch_icall_fptr - /// %2 = call i32 %1() [ "cfguardtarget"(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 doInitialization(Module &M) override; - bool runOnFunction(Function &F) override; - -private: - // Only add checks if the module has the cfguard=2 flag. - int cfguard_module_flag = 0; - 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(); - - // Cast the guard dispatch global to the type of the called operand. - PointerType *PTy = PointerType::get(CalledOperandType, 0); - if (GuardFnGlobal->getType() != PTy) - GuardFnGlobal = ConstantExpr::getBitCast(GuardFnGlobal, PTy); - - // Load the global as a pointer to a function of the same type. - LoadInst *GuardDispatchLoad = B.CreateLoad(CalledOperandType, GuardFnGlobal); - - // Add the original call target as a cfguardtarget operand bundle. - SmallVector Bundles; - CB->getOperandBundlesAsDefs(Bundles); - Bundles.emplace_back("cfguardtarget", CalledOperand); - - // Create a copy of the call/invoke instruction and add the new bundle. - CallBase *NewCB; - if (CallInst *CI = dyn_cast(CB)) { - NewCB = CallInst::Create(CI, Bundles, CB); - } else { - assert(isa(CB) && "Unknown indirect call type"); - InvokeInst *II = cast(CB); - NewCB = llvm::InvokeInst::Create(II, Bundles, CB); - } - - // Change the target of the call to be the guard dispatch function. - NewCB->setCalledOperand(GuardDispatchLoad); - - // Replace the original call/invoke with the new instruction. - CB->replaceAllUsesWith(NewCB); - - // Delete the original call/invoke. - CB->eraseFromParent(); -} - -bool CFGuard::doInitialization(Module &M) { - - // Check if this module has the cfguard flag and read its value. - if (auto *MD = - mdconst::extract_or_null(M.getModuleFlag("cfguard"))) - cfguard_module_flag = MD->getZExtValue(); - - // Skip modules for which CFGuard checks have been disabled. - if (cfguard_module_flag != 2) - return false; - - // Set up prototypes for the guard check and dispatch functions. - GuardFnType = FunctionType::get(Type::getVoidTy(M.getContext()), - {Type::getInt8PtrTy(M.getContext())}, false); - GuardFnPtrType = PointerType::get(GuardFnType, 0); - - // Get or insert the guard check or dispatch global symbols. - if (GuardMechanism == CF_Check) { - GuardFnGlobal = - M.getOrInsertGlobal("__guard_check_icall_fptr", GuardFnPtrType); - } else { - assert(GuardMechanism == CF_Dispatch && "Invalid CFGuard mechanism"); - GuardFnGlobal = - M.getOrInsertGlobal("__guard_dispatch_icall_fptr", GuardFnPtrType); - } - - return true; -} - -bool CFGuard::runOnFunction(Function &F) { - - // Skip modules and functions for which CFGuard checks have been disabled. - if (cfguard_module_flag != 2 || F.hasFnAttribute(Attribute::NoCfCheck)) - return false; - - SmallVector IndirectCalls; - - // 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. - for (BasicBlock &BB : F.getBasicBlockList()) { - for (Instruction &I : BB.getInstList()) { - auto *CB = dyn_cast(&I); - if (CB && CB->isIndirectCall()) { - IndirectCalls.push_back(CB); - CFGuardCounter++; - } - } - } - - // If no checks are needed, return early and add this attribute to indicate - // that subsequent CFGuard passes can skip this function. - if (IndirectCalls.empty()) { - F.addFnAttr(Attribute::NoCfCheck); - return false; - } - - // For each indirect call/invoke, add the appropriate dispatch or check. - if (GuardMechanism == CF_Dispatch) { - for (CallBase *CB : IndirectCalls) { - insertCFGuardDispatch(CB); - } - } else { - for (CallBase *CB : IndirectCalls) { - insertCFGuardCheck(CB); - } - } - - return true; -} - -char CFGuard::ID = 0; -INITIALIZE_PASS(CFGuard, "CFGuard", "CFGuard", false, false) - -FunctionPass *llvm::createCFGuardCheckPass() { - return new CFGuard(CFGuard::CF_Check); -} - -FunctionPass *llvm::createCFGuardDispatchPass() { - return new CFGuard(CFGuard::CF_Dispatch); +//===-- 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/IRBuilder.h" +#include "llvm/IR/Instruction.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" + +using namespace llvm; + +using OperandBundleDef = OperandBundleDefT; + +#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. X86_64 targets use the CF_Dispatch mechanism. X86, +/// ARM, and AArch64 targets use the CF_Check machanism. +class CFGuard : public FunctionPass { +public: + static char ID; + + enum Mechanism { CF_Check, CF_Dispatch }; + + // Default constructor required for the INITIALIZE_PASS macro. + CFGuard() : FunctionPass(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) : FunctionPass(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 ()** @__guard_dispatch_icall_fptr + /// %2 = call i32 %1() [ "cfguardtarget"(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 doInitialization(Module &M) override; + bool runOnFunction(Function &F) override; + +private: + // Only add checks if the module has the cfguard=2 flag. + int cfguard_module_flag = 0; + 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(); + + // Cast the guard dispatch global to the type of the called operand. + PointerType *PTy = PointerType::get(CalledOperandType, 0); + if (GuardFnGlobal->getType() != PTy) + GuardFnGlobal = ConstantExpr::getBitCast(GuardFnGlobal, PTy); + + // Load the global as a pointer to a function of the same type. + LoadInst *GuardDispatchLoad = B.CreateLoad(CalledOperandType, GuardFnGlobal); + + // Add the original call target as a cfguardtarget operand bundle. + SmallVector Bundles; + CB->getOperandBundlesAsDefs(Bundles); + Bundles.emplace_back("cfguardtarget", CalledOperand); + + // Create a copy of the call/invoke instruction and add the new bundle. + CallBase *NewCB; + if (CallInst *CI = dyn_cast(CB)) { + NewCB = CallInst::Create(CI, Bundles, CB); + } else { + assert(isa(CB) && "Unknown indirect call type"); + InvokeInst *II = cast(CB); + NewCB = llvm::InvokeInst::Create(II, Bundles, CB); + } + + // Change the target of the call to be the guard dispatch function. + NewCB->setCalledOperand(GuardDispatchLoad); + + // Replace the original call/invoke with the new instruction. + CB->replaceAllUsesWith(NewCB); + + // Delete the original call/invoke. + CB->eraseFromParent(); +} + +bool CFGuard::doInitialization(Module &M) { + + // Check if this module has the cfguard flag and read its value. + if (auto *MD = + mdconst::extract_or_null(M.getModuleFlag("cfguard"))) + cfguard_module_flag = MD->getZExtValue(); + + // Skip modules for which CFGuard checks have been disabled. + if (cfguard_module_flag != 2) + return false; + + // Set up prototypes for the guard check and dispatch functions. + GuardFnType = FunctionType::get(Type::getVoidTy(M.getContext()), + {Type::getInt8PtrTy(M.getContext())}, false); + GuardFnPtrType = PointerType::get(GuardFnType, 0); + + // Get or insert the guard check or dispatch global symbols. + if (GuardMechanism == CF_Check) { + GuardFnGlobal = + M.getOrInsertGlobal("__guard_check_icall_fptr", GuardFnPtrType); + } else { + assert(GuardMechanism == CF_Dispatch && "Invalid CFGuard mechanism"); + GuardFnGlobal = + M.getOrInsertGlobal("__guard_dispatch_icall_fptr", GuardFnPtrType); + } + + return true; +} + +bool CFGuard::runOnFunction(Function &F) { + + // Skip modules and functions for which CFGuard checks have been disabled. + if (cfguard_module_flag != 2 || F.hasFnAttribute(Attribute::NoCfCheck)) + return false; + + SmallVector IndirectCalls; + + // 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. + for (BasicBlock &BB : F.getBasicBlockList()) { + for (Instruction &I : BB.getInstList()) { + auto *CB = dyn_cast(&I); + if (CB && CB->isIndirectCall()) { + IndirectCalls.push_back(CB); + CFGuardCounter++; + } + } + } + + // If no checks are needed, return early and add this attribute to indicate + // that subsequent CFGuard passes can skip this function. + if (IndirectCalls.empty()) { + F.addFnAttr(Attribute::NoCfCheck); + return false; + } + + // For each indirect call/invoke, add the appropriate dispatch or check. + if (GuardMechanism == CF_Dispatch) { + for (CallBase *CB : IndirectCalls) { + insertCFGuardDispatch(CB); + } + } else { + for (CallBase *CB : IndirectCalls) { + insertCFGuardCheck(CB); + } + } + + return true; +} + +char CFGuard::ID = 0; +INITIALIZE_PASS(CFGuard, "CFGuard", "CFGuard", false, false) + +FunctionPass *llvm::createCFGuardCheckPass() { + return new CFGuard(CFGuard::CF_Check); +} + +FunctionPass *llvm::createCFGuardDispatchPass() { + return new CFGuard(CFGuard::CF_Dispatch); } \ No newline at end of file diff --git a/llvm/lib/Transforms/CFGuard/CMakeLists.txt b/llvm/lib/Transforms/CFGuard/CMakeLists.txt index 65d3a0f0b6f8e..b7518019b1add 100644 --- a/llvm/lib/Transforms/CFGuard/CMakeLists.txt +++ b/llvm/lib/Transforms/CFGuard/CMakeLists.txt @@ -1,9 +1,9 @@ -add_llvm_library(LLVMCFGuard - CFGuard.cpp - - ADDITIONAL_HEADER_DIRS - ${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms - - DEPENDS - intrinsics_gen - ) +add_llvm_library(LLVMCFGuard + CFGuard.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms + + DEPENDS + intrinsics_gen + ) diff --git a/llvm/test/CodeGen/AArch64/cfguard-checks.ll b/llvm/test/CodeGen/AArch64/cfguard-checks.ll index 4b6a7ebc243d2..627741c4b6fed 100644 --- a/llvm/test/CodeGen/AArch64/cfguard-checks.ll +++ b/llvm/test/CodeGen/AArch64/cfguard-checks.ll @@ -1,147 +1,147 @@ -; RUN: llc < %s -mtriple=aarch64-pc-windows-msvc | FileCheck %s -; Control Flow Guard is currently only available on Windows - -; 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 added 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() - - -; 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 - -define i32 @func_cf_setjmp() { - %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-LABEL: func_cf_setjmp - ; CHECK: bl _setjmp - ; CHECK-NEXT: $cfgsj_func_cf_setjmp0: - - %5 = call i8* @llvm.frameaddress(i32 0) - %6 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %5) #3 - - ; CHECK: bl _setjmp - ; CHECK-NEXT: $cfgsj_func_cf_setjmp1: - - store i32 1, i32* %2, align 4 - %7 = load i32, i32* %2, align 4 - ret i32 %7 - - ; CHECK: .section .gljmp$y,"dr" - ; CHECK-NEXT: .symidx $cfgsj_func_cf_setjmp0 - ; CHECK-NEXT: .symidx $cfgsj_func_cf_setjmp1 -} - -declare i8* @llvm.frameaddress(i32) - -; Function Attrs: returns_twice -declare dso_local i32 @_setjmp(i8*, i8*) #2 - -attributes #2 = { returns_twice } -attributes #3 = { returns_twice } - - -!llvm.module.flags = !{!0} -!0 = !{i32 2, !"cfguard", i32 2} +; RUN: llc < %s -mtriple=aarch64-pc-windows-msvc | FileCheck %s +; Control Flow Guard is currently only available on Windows + +; 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 added 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() + + +; 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 + +define i32 @func_cf_setjmp() { + %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-LABEL: func_cf_setjmp + ; CHECK: bl _setjmp + ; CHECK-NEXT: $cfgsj_func_cf_setjmp0: + + %5 = call i8* @llvm.frameaddress(i32 0) + %6 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %5) #3 + + ; CHECK: bl _setjmp + ; CHECK-NEXT: $cfgsj_func_cf_setjmp1: + + store i32 1, i32* %2, align 4 + %7 = load i32, i32* %2, align 4 + ret i32 %7 + + ; CHECK: .section .gljmp$y,"dr" + ; CHECK-NEXT: .symidx $cfgsj_func_cf_setjmp0 + ; CHECK-NEXT: .symidx $cfgsj_func_cf_setjmp1 +} + +declare i8* @llvm.frameaddress(i32) + +; Function Attrs: returns_twice +declare dso_local i32 @_setjmp(i8*, i8*) #2 + +attributes #2 = { returns_twice } +attributes #3 = { returns_twice } + + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 2} diff --git a/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll index 25c53019d93c1..8eb77b6adba3b 100644 --- a/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll +++ b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll @@ -1,25 +1,25 @@ - -; RUN: llc < %s -mtriple=aarch64-pc-windows-msvc | FileCheck %s -; Control Flow Guard is currently only available on Windows - -; 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} + +; RUN: llc < %s -mtriple=aarch64-pc-windows-msvc | FileCheck %s +; Control Flow Guard is currently only available on Windows + +; 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} diff --git a/llvm/test/CodeGen/ARM/cfguard-checks.ll b/llvm/test/CodeGen/ARM/cfguard-checks.ll index 1835bcfc1b58c..c75afc614c7f3 100644 --- a/llvm/test/CodeGen/ARM/cfguard-checks.ll +++ b/llvm/test/CodeGen/ARM/cfguard-checks.ll @@ -1,151 +1,151 @@ -; RUN: llc < %s -mtriple=arm-pc-windows-msvc | FileCheck %s -; Control Flow Guard is currently only available on Windows - -; 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 "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} - - -; Test that Control Flow Guard checks are added even at -O0. -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 "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} - - -; 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 = { "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} - - -; Test that Control Flow Guard checks are correctly added on invoke instructions. -define i32 @func_cf_invoke() #2 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 -} - -declare void @h() - - -; 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 - -define i32 @func_cf_setjmp() #2 { - %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) #3 - - ; CHECK-LABEL: func_cf_setjmp - ; CHECK: bl _setjmp - ; CHECK-NEXT: $cfgsj_func_cf_setjmp0: - - %5 = call i8* @llvm.frameaddress(i32 0) - %6 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %5) #3 - - ; CHECK: bl _setjmp - ; CHECK-NEXT: $cfgsj_func_cf_setjmp1: - - store i32 1, i32* %2, align 4 - %7 = load i32, i32* %2, align 4 - ret i32 %7 - - ; CHECK: .section .gljmp$y,"dr" - ; CHECK-NEXT: .symidx $cfgsj_func_cf_setjmp0 - ; CHECK-NEXT: .symidx $cfgsj_func_cf_setjmp1 -} - -declare i8* @llvm.frameaddress(i32) - -; Function Attrs: returns_twice -declare dso_local i32 @_setjmp(i8*, i8*) #3 - -attributes #3 = { returns_twice } - - -!llvm.module.flags = !{!0} -!0 = !{i32 2, !"cfguard", i32 2} +; RUN: llc < %s -mtriple=arm-pc-windows-msvc | FileCheck %s +; Control Flow Guard is currently only available on Windows + +; 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 "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} + + +; Test that Control Flow Guard checks are added even at -O0. +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 "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} + + +; 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 = { "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} + + +; Test that Control Flow Guard checks are correctly added on invoke instructions. +define i32 @func_cf_invoke() #2 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 +} + +declare void @h() + + +; 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 + +define i32 @func_cf_setjmp() #2 { + %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) #3 + + ; CHECK-LABEL: func_cf_setjmp + ; CHECK: bl _setjmp + ; CHECK-NEXT: $cfgsj_func_cf_setjmp0: + + %5 = call i8* @llvm.frameaddress(i32 0) + %6 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %5) #3 + + ; CHECK: bl _setjmp + ; CHECK-NEXT: $cfgsj_func_cf_setjmp1: + + store i32 1, i32* %2, align 4 + %7 = load i32, i32* %2, align 4 + ret i32 %7 + + ; CHECK: .section .gljmp$y,"dr" + ; CHECK-NEXT: .symidx $cfgsj_func_cf_setjmp0 + ; CHECK-NEXT: .symidx $cfgsj_func_cf_setjmp1 +} + +declare i8* @llvm.frameaddress(i32) + +; Function Attrs: returns_twice +declare dso_local i32 @_setjmp(i8*, i8*) #3 + +attributes #3 = { returns_twice } + + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 2} diff --git a/llvm/test/CodeGen/ARM/cfguard-module-flag.ll b/llvm/test/CodeGen/ARM/cfguard-module-flag.ll index 87878a084dcce..72f4b026ae09b 100644 --- a/llvm/test/CodeGen/ARM/cfguard-module-flag.ll +++ b/llvm/test/CodeGen/ARM/cfguard-module-flag.ll @@ -1,26 +1,26 @@ - -; RUN: llc < %s -mtriple=arm-pc-windows-msvc | FileCheck %s -; Control Flow Guard is currently only available on Windows - -; 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} + +; RUN: llc < %s -mtriple=arm-pc-windows-msvc | FileCheck %s +; Control Flow Guard is currently only available on Windows + +; 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} diff --git a/llvm/test/CodeGen/X86/cfguard-checks.ll b/llvm/test/CodeGen/X86/cfguard-checks.ll index 5a930afef1336..d2cd3c9f3eea0 100644 --- a/llvm/test/CodeGen/X86/cfguard-checks.ll +++ b/llvm/test/CodeGen/X86/cfguard-checks.ll @@ -1,231 +1,231 @@ -; 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 -; Control Flow Guard is currently only available on Windows - -; 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 added even at -O0. -; FIXME Ideally these checks should be added as a single call instruction, as in the optimized case. -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: movl ___guard_check_icall_fptr, %eax - ; X32: calll *%eax - ; 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: movq __guard_dispatch_icall_fptr(%rip), %rcx - ; X64: callq *%rcx - ; 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() - - -; Test that Control Flow Guard preserves floating point arguments. -declare double @target_func_doubles(double, double, double, double) - -define double @func_cf_doubles() { -entry: - %func_ptr = alloca double (double, double, double, double)*, align 8 - store double (double, double, double, double)* @target_func_doubles, double (double, double, double, double)** %func_ptr, align 8 - %0 = load double (double, double, double, double)*, double (double, double, double, double)** %func_ptr, align 8 - %1 = call double %0(double 1.000000e+00, double 2.000000e+00, double 3.000000e+00, double 4.000000e+00) - ret double %1 - - ; On i686, the call to __guard_check_icall_fptr should come immediately before the call to the target function. - ; X32-LABEL: func_cf_doubles - ; X32: movl $_target_func_doubles, %esi - ; X32: movl $_target_func_doubles, %ecx - ; X32: calll *___guard_check_icall_fptr - ; X32: 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_doubles - ; X64: leaq target_func_doubles(%rip), %rax - ; X64: movsd __real@3ff0000000000000(%rip), %xmm0 - ; X64: movsd __real@4000000000000000(%rip), %xmm1 - ; X64: movsd __real@4008000000000000(%rip), %xmm2 - ; X64: movsd __real@4010000000000000(%rip), %xmm3 - ; X64: callq *__guard_dispatch_icall_fptr(%rip) - ; X64-NOT: callq -} - - -; Test that Control Flow Guard checks are correctly added for tail calls. -define i32 @func_cf_tail() { -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 = musttail 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_tail - ; X32: movl $_target_func, %ecx - ; X32: calll *___guard_check_icall_fptr - ; X32: movl $_target_func, %eax - ; X32: jmpl *%eax # TAILCALL - ; X32-NOT: calll - - ; X64-LABEL: func_cf_tail - ; X64: leaq target_func(%rip), %rax - ; X64: movq __guard_dispatch_icall_fptr(%rip), %rcx - ; X64: rex64 jmpq *%rcx # TAILCALL - ; X64-NOT: callq -} - - -; 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 - -define i32 @func_cf_setjmp() { - %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 - - ; X32-LABEL: func_cf_setjmp - ; X32: calll __setjmp - ; X32-NEXT: $cfgsj_func_cf_setjmp0: - - ; X64-LABEL: func_cf_setjmp - ; X64: callq _setjmp - ; X64-NEXT: $cfgsj_func_cf_setjmp0: - - %5 = call i8* @llvm.frameaddress(i32 0) - %6 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %5) #2 - - ; X32: calll __setjmp - ; X32-NEXT: $cfgsj_func_cf_setjmp1: - - ; X64: callq _setjmp - ; X64-NEXT: $cfgsj_func_cf_setjmp1: - - store i32 1, i32* %2, align 4 - %7 = load i32, i32* %2, align 4 - ret i32 %7 - - ; X32: .section .gljmp$y,"dr" - ; X32-NEXT: .symidx $cfgsj_func_cf_setjmp0 - ; X32-NEXT: .symidx $cfgsj_func_cf_setjmp1 - - ; X64: .section .gljmp$y,"dr" - ; X64-NEXT: .symidx $cfgsj_func_cf_setjmp0 - ; X64-NEXT: .symidx $cfgsj_func_cf_setjmp1 -} - -declare i8* @llvm.frameaddress(i32) - -; Function Attrs: returns_twice -declare dso_local i32 @_setjmp(i8*, i8*) #2 - -attributes #2 = { returns_twice } - - -!llvm.module.flags = !{!0} -!0 = !{i32 2, !"cfguard", i32 2} +; 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 +; Control Flow Guard is currently only available on Windows + +; 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 added even at -O0. +; FIXME Ideally these checks should be added as a single call instruction, as in the optimized case. +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: movl ___guard_check_icall_fptr, %eax + ; X32: calll *%eax + ; 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: movq __guard_dispatch_icall_fptr(%rip), %rcx + ; X64: callq *%rcx + ; 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() + + +; Test that Control Flow Guard preserves floating point arguments. +declare double @target_func_doubles(double, double, double, double) + +define double @func_cf_doubles() { +entry: + %func_ptr = alloca double (double, double, double, double)*, align 8 + store double (double, double, double, double)* @target_func_doubles, double (double, double, double, double)** %func_ptr, align 8 + %0 = load double (double, double, double, double)*, double (double, double, double, double)** %func_ptr, align 8 + %1 = call double %0(double 1.000000e+00, double 2.000000e+00, double 3.000000e+00, double 4.000000e+00) + ret double %1 + + ; On i686, the call to __guard_check_icall_fptr should come immediately before the call to the target function. + ; X32-LABEL: func_cf_doubles + ; X32: movl $_target_func_doubles, %esi + ; X32: movl $_target_func_doubles, %ecx + ; X32: calll *___guard_check_icall_fptr + ; X32: 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_doubles + ; X64: leaq target_func_doubles(%rip), %rax + ; X64: movsd __real@3ff0000000000000(%rip), %xmm0 + ; X64: movsd __real@4000000000000000(%rip), %xmm1 + ; X64: movsd __real@4008000000000000(%rip), %xmm2 + ; X64: movsd __real@4010000000000000(%rip), %xmm3 + ; X64: callq *__guard_dispatch_icall_fptr(%rip) + ; X64-NOT: callq +} + + +; Test that Control Flow Guard checks are correctly added for tail calls. +define i32 @func_cf_tail() { +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 = musttail 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_tail + ; X32: movl $_target_func, %ecx + ; X32: calll *___guard_check_icall_fptr + ; X32: movl $_target_func, %eax + ; X32: jmpl *%eax # TAILCALL + ; X32-NOT: calll + + ; X64-LABEL: func_cf_tail + ; X64: leaq target_func(%rip), %rax + ; X64: movq __guard_dispatch_icall_fptr(%rip), %rcx + ; X64: rex64 jmpq *%rcx # TAILCALL + ; X64-NOT: callq +} + + +; 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 + +define i32 @func_cf_setjmp() { + %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 + + ; X32-LABEL: func_cf_setjmp + ; X32: calll __setjmp + ; X32-NEXT: $cfgsj_func_cf_setjmp0: + + ; X64-LABEL: func_cf_setjmp + ; X64: callq _setjmp + ; X64-NEXT: $cfgsj_func_cf_setjmp0: + + %5 = call i8* @llvm.frameaddress(i32 0) + %6 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %5) #2 + + ; X32: calll __setjmp + ; X32-NEXT: $cfgsj_func_cf_setjmp1: + + ; X64: callq _setjmp + ; X64-NEXT: $cfgsj_func_cf_setjmp1: + + store i32 1, i32* %2, align 4 + %7 = load i32, i32* %2, align 4 + ret i32 %7 + + ; X32: .section .gljmp$y,"dr" + ; X32-NEXT: .symidx $cfgsj_func_cf_setjmp0 + ; X32-NEXT: .symidx $cfgsj_func_cf_setjmp1 + + ; X64: .section .gljmp$y,"dr" + ; X64-NEXT: .symidx $cfgsj_func_cf_setjmp0 + ; X64-NEXT: .symidx $cfgsj_func_cf_setjmp1 +} + +declare i8* @llvm.frameaddress(i32) + +; Function Attrs: returns_twice +declare dso_local i32 @_setjmp(i8*, i8*) #2 + +attributes #2 = { returns_twice } + + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 2} diff --git a/llvm/test/CodeGen/X86/cfguard-module-flag.ll b/llvm/test/CodeGen/X86/cfguard-module-flag.ll index 1b2e71c1a455a..0ed58b392aa41 100644 --- a/llvm/test/CodeGen/X86/cfguard-module-flag.ll +++ b/llvm/test/CodeGen/X86/cfguard-module-flag.ll @@ -1,26 +1,26 @@ - -; 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 -; Control Flow Guard is currently only available on Windows - -; 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} + +; 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 +; Control Flow Guard is currently only available on Windows + +; 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} diff --git a/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll b/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll index a554ebeeb9024..be7acc5313705 100644 --- a/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll +++ b/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll @@ -1,38 +1,38 @@ -; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s -check-prefix=X64 -; Control Flow Guard is currently only available on Windows - - -; Test that Control Flow Guard checks are correctly added for x86_64 vector calls. -define void @func_cf_vector_x64(void (%struct.HVA)* %0, %struct.HVA* %1) #0 { -entry: - %2 = alloca %struct.HVA, align 8 - %3 = bitcast %struct.HVA* %2 to i8* - %4 = bitcast %struct.HVA* %1 to i8* - call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %3, i8* align 8 %4, i64 32, i1 false) - %5 = load %struct.HVA, %struct.HVA* %2, align 8 - call x86_vectorcallcc void %0(%struct.HVA inreg %5) - ret void - - ; X64-LABEL: func_cf_vector_x64 - ; X64: movq %rcx, %rax - ; X64: movups (%rdx), %xmm0 - ; X64: movups 16(%rdx), %xmm1 - ; X64: movaps %xmm0, 32(%rsp) - ; X64: movaps %xmm1, 48(%rsp) - ; X64: movsd 32(%rsp), %xmm0 # xmm0 = mem[0],zero - ; X64: movsd 40(%rsp), %xmm1 # xmm1 = mem[0],zero - ; X64: movsd 48(%rsp), %xmm2 # xmm2 = mem[0],zero - ; X64: movsd 56(%rsp), %xmm3 # xmm3 = mem[0],zero - ; X64: callq *__guard_dispatch_icall_fptr(%rip) - ; X64-NOT: callq -} -attributes #0 = { "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" } - -%struct.HVA = type { double, double, double, double } - -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1 immarg) #1 -attributes #1 = { argmemonly nounwind willreturn } - - -!llvm.module.flags = !{!0} -!0 = !{i32 2, !"cfguard", i32 2} +; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s -check-prefix=X64 +; Control Flow Guard is currently only available on Windows + + +; Test that Control Flow Guard checks are correctly added for x86_64 vector calls. +define void @func_cf_vector_x64(void (%struct.HVA)* %0, %struct.HVA* %1) #0 { +entry: + %2 = alloca %struct.HVA, align 8 + %3 = bitcast %struct.HVA* %2 to i8* + %4 = bitcast %struct.HVA* %1 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %3, i8* align 8 %4, i64 32, i1 false) + %5 = load %struct.HVA, %struct.HVA* %2, align 8 + call x86_vectorcallcc void %0(%struct.HVA inreg %5) + ret void + + ; X64-LABEL: func_cf_vector_x64 + ; X64: movq %rcx, %rax + ; X64: movups (%rdx), %xmm0 + ; X64: movups 16(%rdx), %xmm1 + ; X64: movaps %xmm0, 32(%rsp) + ; X64: movaps %xmm1, 48(%rsp) + ; X64: movsd 32(%rsp), %xmm0 # xmm0 = mem[0],zero + ; X64: movsd 40(%rsp), %xmm1 # xmm1 = mem[0],zero + ; X64: movsd 48(%rsp), %xmm2 # xmm2 = mem[0],zero + ; X64: movsd 56(%rsp), %xmm3 # xmm3 = mem[0],zero + ; X64: callq *__guard_dispatch_icall_fptr(%rip) + ; X64-NOT: callq +} +attributes #0 = { "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" } + +%struct.HVA = type { double, double, double, double } + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1 immarg) #1 +attributes #1 = { argmemonly nounwind willreturn } + + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 2} diff --git a/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll b/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll index 0f31f5ba4b6b4..25e3691913c8c 100644 --- a/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll +++ b/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll @@ -1,43 +1,43 @@ -; RUN: llc < %s -mtriple=i686-pc-windows-msvc | FileCheck %s -check-prefix=X32 -; Control Flow Guard is currently only available on Windows - - -; Test that Control Flow Guard checks are correctly added for x86 vector calls. -define void @func_cf_vector_x86(void (%struct.HVA)* %0, %struct.HVA* %1) #0 { -entry: - %2 = alloca %struct.HVA, align 8 - %3 = bitcast %struct.HVA* %2 to i8* - %4 = bitcast %struct.HVA* %1 to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %3, i8* align 8 %4, i32 32, i1 false) - %5 = load %struct.HVA, %struct.HVA* %2, align 8 - call x86_vectorcallcc void %0(%struct.HVA inreg %5) - ret void - - ; X32-LABEL: func_cf_vector_x86 - ; X32: movl 12(%ebp), %eax - ; X32: movl 8(%ebp), %ecx - ; X32: movsd 24(%eax), %xmm4 # xmm4 = mem[0],zero - ; X32: movsd %xmm4, 24(%esp) - ; X32: movsd 16(%eax), %xmm5 # xmm5 = mem[0],zero - ; X32: movsd %xmm5, 16(%esp) - ; X32: movsd (%eax), %xmm6 # xmm6 = mem[0],zero - ; X32: movsd 8(%eax), %xmm7 # xmm7 = mem[0],zero - ; X32: movsd %xmm7, 8(%esp) - ; X32: movsd %xmm6, (%esp) - ; X32: calll *___guard_check_icall_fptr - ; X32: movaps %xmm6, %xmm0 - ; X32: movaps %xmm7, %xmm1 - ; X32: movaps %xmm5, %xmm2 - ; X32: movaps %xmm4, %xmm3 - ; X32: calll *%ecx -} -attributes #0 = { "target-cpu"="pentium4" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" } - -%struct.HVA = type { double, double, double, double } - -declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture writeonly, i8* nocapture readonly, i32, i1 immarg) #1 -attributes #1 = { argmemonly nounwind willreturn } - - -!llvm.module.flags = !{!0} -!0 = !{i32 2, !"cfguard", i32 2} +; RUN: llc < %s -mtriple=i686-pc-windows-msvc | FileCheck %s -check-prefix=X32 +; Control Flow Guard is currently only available on Windows + + +; Test that Control Flow Guard checks are correctly added for x86 vector calls. +define void @func_cf_vector_x86(void (%struct.HVA)* %0, %struct.HVA* %1) #0 { +entry: + %2 = alloca %struct.HVA, align 8 + %3 = bitcast %struct.HVA* %2 to i8* + %4 = bitcast %struct.HVA* %1 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %3, i8* align 8 %4, i32 32, i1 false) + %5 = load %struct.HVA, %struct.HVA* %2, align 8 + call x86_vectorcallcc void %0(%struct.HVA inreg %5) + ret void + + ; X32-LABEL: func_cf_vector_x86 + ; X32: movl 12(%ebp), %eax + ; X32: movl 8(%ebp), %ecx + ; X32: movsd 24(%eax), %xmm4 # xmm4 = mem[0],zero + ; X32: movsd %xmm4, 24(%esp) + ; X32: movsd 16(%eax), %xmm5 # xmm5 = mem[0],zero + ; X32: movsd %xmm5, 16(%esp) + ; X32: movsd (%eax), %xmm6 # xmm6 = mem[0],zero + ; X32: movsd 8(%eax), %xmm7 # xmm7 = mem[0],zero + ; X32: movsd %xmm7, 8(%esp) + ; X32: movsd %xmm6, (%esp) + ; X32: calll *___guard_check_icall_fptr + ; X32: movaps %xmm6, %xmm0 + ; X32: movaps %xmm7, %xmm1 + ; X32: movaps %xmm5, %xmm2 + ; X32: movaps %xmm4, %xmm3 + ; X32: calll *%ecx +} +attributes #0 = { "target-cpu"="pentium4" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" } + +%struct.HVA = type { double, double, double, double } + +declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture writeonly, i8* nocapture readonly, i32, i1 immarg) #1 +attributes #1 = { argmemonly nounwind willreturn } + + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"cfguard", i32 2} From f9823fa57d24b82449f656638b46c54dd9e8de15 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Mon, 18 Nov 2019 15:31:37 -0800 Subject: [PATCH 3/4] [musttail] Don't forward AL on Win64 AL is only used for varargs on SysV platforms. Don't forward it on Windows. This allows control flow guard to set up an extra hidden parameter in RAX, as described in PR44049. This also has the effect of freeing up RAX for use in virtual member pointer thunks, which may also be a nice little code size improvement on Win64. Fixes PR44049 Reviewers: ajpaverd, efriedma, hans Differential Revision: https://reviews.llvm.org/D70413 --- llvm/lib/Target/X86/X86ISelLowering.cpp | 4 +-- llvm/test/CodeGen/X86/cfguard-checks.ll | 33 +++++++++++++++++++++++ llvm/test/CodeGen/X86/musttail-varargs.ll | 30 +++++++++------------ 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index 920cdd7e625e1..aba4cdeb8c06c 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -3426,8 +3426,8 @@ SDValue X86TargetLowering::LowerFormalArguments( FuncInfo->getForwardedMustTailRegParms(); CCInfo.analyzeMustTailForwardedRegisters(Forwards, RegParmTypes, CC_X86); - // Conservatively forward AL on x86_64, since it might be used for varargs. - if (Is64Bit && !CCInfo.isAllocated(X86::AL)) { + // Forward AL for SysV x86_64 targets, since it is used for varargs. + if (Is64Bit && !IsWin64 && !CCInfo.isAllocated(X86::AL)) { unsigned ALVReg = MF.addLiveIn(X86::AL, &X86::GR8RegClass); Forwards.push_back(ForwardedRegister(ALVReg, X86::AL, MVT::i8)); } diff --git a/llvm/test/CodeGen/X86/cfguard-checks.ll b/llvm/test/CodeGen/X86/cfguard-checks.ll index d2cd3c9f3eea0..efd6e7bc11b63 100644 --- a/llvm/test/CodeGen/X86/cfguard-checks.ll +++ b/llvm/test/CodeGen/X86/cfguard-checks.ll @@ -176,6 +176,39 @@ entry: ; X64-NOT: callq } +%struct.Foo = type { i32 (%struct.Foo*)** } + +; Test that Control Flow Guard checks are correctly added for variadic musttail +; calls. These are used for MS C++ ABI virtual member pointer thunks. +; PR44049 +define i32 @vmptr_thunk(%struct.Foo* inreg %p) { +entry: + %vptr.addr = getelementptr inbounds %struct.Foo, %struct.Foo* %p, i32 0, i32 0 + %vptr = load i32 (%struct.Foo*)**, i32 (%struct.Foo*)*** %vptr.addr + %slot = getelementptr inbounds i32 (%struct.Foo*)*, i32 (%struct.Foo*)** %vptr, i32 1 + %vmethod = load i32 (%struct.Foo*)*, i32 (%struct.Foo*)** %slot + %rv = musttail call i32 %vmethod(%struct.Foo* inreg %p) + ret i32 %rv + + ; On i686, the call to __guard_check_icall_fptr should come immediately before the call to the target function. + ; X32-LABEL: _vmptr_thunk: + ; X32: movl %eax, %esi + ; X32: movl (%eax), %eax + ; X32: movl 4(%eax), %ecx + ; X32: calll *___guard_check_icall_fptr + ; X32: movl %esi, %eax + ; X32: jmpl *%ecx # TAILCALL + ; X32-NOT: calll + + ; Use NEXT here because we previously had an extra instruction in this sequence. + ; X64-LABEL: vmptr_thunk: + ; X64: movq (%rcx), %rax + ; X64-NEXT: movq 8(%rax), %rax + ; X64-NEXT: movq __guard_dispatch_icall_fptr(%rip), %rdx + ; X64-NEXT: addq $40, %rsp + ; X64-NEXT: rex64 jmpq *%rdx # TAILCALL + ; X64-NOT: callq +} ; Test that longjmp targets have public labels and are included in the .gljmp section. %struct._SETJMP_FLOAT128 = type { [2 x i64] } diff --git a/llvm/test/CodeGen/X86/musttail-varargs.ll b/llvm/test/CodeGen/X86/musttail-varargs.ll index b62343fc82a53..b0212f6c0efeb 100644 --- a/llvm/test/CodeGen/X86/musttail-varargs.ll +++ b/llvm/test/CodeGen/X86/musttail-varargs.ll @@ -211,37 +211,31 @@ define void @f_thunk(i8* %this, ...) { ; WINDOWS-NEXT: .seh_pushreg 6 ; WINDOWS-NEXT: pushq %rdi ; WINDOWS-NEXT: .seh_pushreg 7 -; WINDOWS-NEXT: pushq %rbp -; WINDOWS-NEXT: .seh_pushreg 5 ; WINDOWS-NEXT: pushq %rbx ; WINDOWS-NEXT: .seh_pushreg 3 -; WINDOWS-NEXT: subq $64, %rsp -; WINDOWS-NEXT: .seh_stackalloc 64 +; WINDOWS-NEXT: subq $72, %rsp +; WINDOWS-NEXT: .seh_stackalloc 72 ; WINDOWS-NEXT: .seh_endprologue -; WINDOWS-NEXT: movl %eax, %r14d -; WINDOWS-NEXT: movq %r9, %rsi +; WINDOWS-NEXT: movq %r9, %r14 ; WINDOWS-NEXT: movq %r8, %rdi ; WINDOWS-NEXT: movq %rdx, %rbx -; WINDOWS-NEXT: movq %rcx, %rbp +; WINDOWS-NEXT: movq %rcx, %rsi ; WINDOWS-NEXT: movq %rdx, {{[0-9]+}}(%rsp) ; WINDOWS-NEXT: movq %r8, {{[0-9]+}}(%rsp) ; WINDOWS-NEXT: movq %r9, {{[0-9]+}}(%rsp) ; WINDOWS-NEXT: leaq {{[0-9]+}}(%rsp), %rax ; WINDOWS-NEXT: movq %rax, {{[0-9]+}}(%rsp) ; WINDOWS-NEXT: callq get_f -; WINDOWS-NEXT: movq %rax, %r10 -; WINDOWS-NEXT: movq %rbp, %rcx +; WINDOWS-NEXT: movq %rsi, %rcx ; WINDOWS-NEXT: movq %rbx, %rdx ; WINDOWS-NEXT: movq %rdi, %r8 -; WINDOWS-NEXT: movq %rsi, %r9 -; WINDOWS-NEXT: movl %r14d, %eax -; WINDOWS-NEXT: addq $64, %rsp +; WINDOWS-NEXT: movq %r14, %r9 +; WINDOWS-NEXT: addq $72, %rsp ; WINDOWS-NEXT: popq %rbx -; WINDOWS-NEXT: popq %rbp ; WINDOWS-NEXT: popq %rdi ; WINDOWS-NEXT: popq %rsi ; WINDOWS-NEXT: popq %r14 -; WINDOWS-NEXT: rex64 jmpq *%r10 # TAILCALL +; WINDOWS-NEXT: rex64 jmpq *%rax # TAILCALL ; WINDOWS-NEXT: .seh_handlerdata ; WINDOWS-NEXT: .text ; WINDOWS-NEXT: .seh_endproc @@ -397,14 +391,14 @@ define void @h_thunk(%struct.Foo* %this, ...) { ; WINDOWS-NEXT: cmpb $1, (%rcx) ; WINDOWS-NEXT: jne .LBB2_2 ; WINDOWS-NEXT: # %bb.1: # %then -; WINDOWS-NEXT: movq 8(%rcx), %r10 +; WINDOWS-NEXT: movq 8(%rcx), %rax ; WINDOWS-NEXT: addq $40, %rsp -; WINDOWS-NEXT: rex64 jmpq *%r10 # TAILCALL +; WINDOWS-NEXT: rex64 jmpq *%rax # TAILCALL ; WINDOWS-NEXT: .LBB2_2: # %else -; WINDOWS-NEXT: movq 16(%rcx), %r10 +; WINDOWS-NEXT: movq 16(%rcx), %rax ; WINDOWS-NEXT: movl $42, {{.*}}(%rip) ; WINDOWS-NEXT: addq $40, %rsp -; WINDOWS-NEXT: rex64 jmpq *%r10 # TAILCALL +; WINDOWS-NEXT: rex64 jmpq *%rax # TAILCALL ; WINDOWS-NEXT: .seh_handlerdata ; WINDOWS-NEXT: .text ; WINDOWS-NEXT: .seh_endproc From 16dc9959a464f1dd9d2a115807a3222d2ff7752c Mon Sep 17 00:00:00 2001 From: Andrew Paverd Date: Fri, 10 Jan 2020 11:08:18 +0000 Subject: [PATCH 4/4] Add support for __declspec(guard(nocf)) Summary: Avoid using the `nocf_check` attribute with Control Flow Guard. Instead, use a new `"guard_nocf"` function attribute to indicate that checks should not be added on indirect calls within that function. Add support for `__declspec(guard(nocf))` following the same syntax as MSVC. Reviewers: rnk, dmajor, pcc, hans, aaron.ballman Reviewed By: aaron.ballman Subscribers: aaron.ballman, tomrittervg, hiraditya, cfe-commits, llvm-commits Tags: #clang, #llvm Differential Revision: https://reviews.llvm.org/D72167 --- clang/include/clang/Basic/Attr.td | 9 +++ clang/include/clang/Basic/AttrDocs.td | 20 +++++ clang/lib/CodeGen/CGCall.cpp | 11 +++ clang/lib/Sema/SemaDeclAttr.cpp | 22 ++++++ clang/test/CodeGen/guard_nocf.c | 53 +++++++++++++ clang/test/CodeGenCXX/guard_nocf.cpp | 84 +++++++++++++++++++++ clang/test/Sema/attr-guard_nocf.c | 27 +++++++ llvm/lib/Transforms/CFGuard/CFGuard.cpp | 10 +-- llvm/test/CodeGen/AArch64/cfguard-checks.ll | 10 +-- llvm/test/CodeGen/ARM/cfguard-checks.ll | 22 +++--- llvm/test/CodeGen/X86/cfguard-checks.ll | 12 +-- 11 files changed, 252 insertions(+), 28 deletions(-) create mode 100644 clang/test/CodeGen/guard_nocf.c create mode 100644 clang/test/CodeGenCXX/guard_nocf.cpp create mode 100644 clang/test/Sema/attr-guard_nocf.c diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index d39b16e62b7fa..fc599911d62a3 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2806,6 +2806,15 @@ def MSAllocator : InheritableAttr { let Documentation = [MSAllocatorDocs]; } +def CFGuard : InheritableAttr { + // Currently only the __declspec(guard(nocf)) modifier is supported. In future + // we might also want to support __declspec(guard(suppress)). + let Spellings = [Declspec<"guard">]; + let Subjects = SubjectList<[Function]>; + let Args = [EnumArgument<"Guard", "GuardArg", ["nocf"], ["nocf"]>]; + let Documentation = [CFGuardDocs]; +} + def MSStruct : InheritableAttr { let Spellings = [GCC<"ms_struct">]; let Subjects = SubjectList<[Record]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index fac6116057dc2..10b5da98036f1 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4184,6 +4184,26 @@ This attribute does not affect optimizations in any way, unlike GCC's }]; } +def CFGuardDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Code can indicate CFG checks are not wanted with the ``__declspec(guard(nocf))`` +attribute. This directs the compiler to not insert any CFG checks for the entire +function. This approach is typically used only sparingly in specific situations +where the programmer has manually inserted "CFG-equivalent" protection. The +programmer knows that they are calling through some read-only function table +whose address is obtained through read-only memory references and for which the +index is masked to the function table limit. This approach may also be applied +to small wrapper functions that are not inlined and that do nothing more than +make a call through a function pointer. Since incorrect usage of this directive +can compromise the security of CFG, the programmer must be very careful using +the directive. Typically, this usage is limited to very small functions that +only call one function. + +`Control Flow Guard documentation ` +}]; +} + def HIPPinnedShadowDocs : Documentation { let Category = DocCatType; let Content = [{ diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index cf8024550eeec..a037907454030 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4346,6 +4346,17 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (callOrInvoke) *callOrInvoke = CI; + // If this is within a function that has the guard(nocf) attribute and is an + // indirect call, add the "guard_nocf" attribute to this call to indicate that + // Control Flow Guard checks should not be added, even if the call is inlined. + if (const auto *FD = dyn_cast_or_null(CurFuncDecl)) { + if (const auto *A = FD->getAttr()) { + if (A->getGuard() == CFGuardAttr::GuardArg::nocf && !CI->getCalledFunction()) + Attrs = Attrs.addAttribute( + getLLVMContext(), llvm::AttributeList::FunctionIndex, "guard_nocf"); + } + } + // Apply the attributes and calling convention. CI->setAttributes(Attrs); CI->setCallingConv(static_cast(CallingConv)); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index ee06f8ae51147..e0ef8f05e2b63 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6593,6 +6593,25 @@ static void handleMSAllocatorAttr(Sema &S, Decl *D, const ParsedAttr &AL) { handleSimpleAttribute(S, D, AL); } +static void handleCFGuardAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + // The guard attribute takes a single identifier argument. + + if (!AL.isArgIdent(0)) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_type) + << AL << AANT_ArgumentIdentifier; + return; + } + + CFGuardAttr::GuardArg Arg; + IdentifierInfo *II = AL.getArgAsIdent(0)->Ident; + if (!CFGuardAttr::ConvertStrToGuardArg(II->getName(), Arg)) { + S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) << AL << II; + return; + } + + D->addAttr(::new (S.Context) CFGuardAttr(S.Context, AL, Arg)); +} + //===----------------------------------------------------------------------===// // Top Level Sema Entry Points //===----------------------------------------------------------------------===// @@ -7196,6 +7215,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_AbiTag: handleAbiTagAttr(S, D, AL); break; + case ParsedAttr::AT_CFGuard: + handleCFGuardAttr(S, D, AL); + break; // Thread safety attributes: case ParsedAttr::AT_AssertExclusiveLock: diff --git a/clang/test/CodeGen/guard_nocf.c b/clang/test/CodeGen/guard_nocf.c new file mode 100644 index 0000000000000..2fe55736f2379 --- /dev/null +++ b/clang/test/CodeGen/guard_nocf.c @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -triple %ms_abi_triple -fms-extensions -emit-llvm -O2 -o - %s | FileCheck %s + +void target_func(); +void (*func_ptr)() = &target_func; + +// The "guard_nocf" attribute must be added. +__declspec(guard(nocf)) void nocf0() { + (*func_ptr)(); +} +// CHECK-LABEL: nocf0 +// CHECK: call{{.*}}[[NOCF:#[0-9]+]] + +// The "guard_nocf" attribute must *not* be added. +void cf0() { + (*func_ptr)(); +} +// CHECK-LABEL: cf0 +// CHECK: call{{.*}}[[CF:#[0-9]+]] + +// If the modifier is present on either the function declaration or definition, +// the "guard_nocf" attribute must be added. +__declspec(guard(nocf)) void nocf1(); +void nocf1() { + (*func_ptr)(); +} +// CHECK-LABEL: nocf1 +// CHECK: call{{.*}}[[NOCF:#[0-9]+]] + +void nocf2(); +__declspec(guard(nocf)) void nocf2() { + (*func_ptr)(); +} +// CHECK-LABEL: nocf2 +// CHECK: call{{.*}}[[NOCF:#[0-9]+]] + +// When inlining a function, the "guard_nocf" attribute on indirect calls must +// be preserved. +void nocf3() { + nocf0(); +} +// CHECK-LABEL: nocf3 +// CHECK: call{{.*}}[[NOCF:#[0-9]+]] + +// When inlining into a function marked as __declspec(guard(nocf)), the +// "guard_nocf" attribute must *not* be added to the inlined calls. +__declspec(guard(nocf)) void cf1() { + cf0(); +} +// CHECK-LABEL: cf1 +// CHECK: call{{.*}}[[CF:#[0-9]+]] + +// CHECK: attributes [[NOCF]] = { {{.*}}"guard_nocf"{{.*}} } +// CHECK-NOT: attributes [[CF]] = { {{.*}}"guard_nocf"{{.*}} } diff --git a/clang/test/CodeGenCXX/guard_nocf.cpp b/clang/test/CodeGenCXX/guard_nocf.cpp new file mode 100644 index 0000000000000..3dc5c50b6bfd0 --- /dev/null +++ b/clang/test/CodeGenCXX/guard_nocf.cpp @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -triple %ms_abi_triple -fms-extensions -std=c++11 -emit-llvm -O2 -o - %s | FileCheck %s + +void target_func(); +void (*func_ptr)() = &target_func; + +// The "guard_nocf" attribute must be added. +__declspec(guard(nocf)) void nocf0() { + (*func_ptr)(); +} +// CHECK-LABEL: nocf0 +// CHECK: call{{.*}}[[NOCF:#[0-9]+]] + +// The "guard_nocf" attribute must *not* be added. +void cf0() { + (*func_ptr)(); +} +// CHECK-LABEL: cf0 +// CHECK: call{{.*}}[[CF:#[0-9]+]] + +// If the modifier is present on either the function declaration or definition, +// the "guard_nocf" attribute must be added. +__declspec(guard(nocf)) void nocf1(); +void nocf1() { + (*func_ptr)(); +} +// CHECK-LABEL: nocf1 +// CHECK: call{{.*}}[[NOCF:#[0-9]+]] + +void nocf2(); +__declspec(guard(nocf)) void nocf2() { + (*func_ptr)(); +} +// CHECK-LABEL: nocf2 +// CHECK: call{{.*}}[[NOCF:#[0-9]+]] + +// When inlining a function, the "guard_nocf" attribute on indirect calls must +// be preserved. +void nocf3() { + nocf0(); +} +// CHECK-LABEL: nocf3 +// CHECK: call{{.*}}[[NOCF:#[0-9]+]] + +// When inlining into a function marked as __declspec(guard(nocf)), the +// "guard_nocf" attribute must *not* be added to the inlined calls. +__declspec(guard(nocf)) void cf1() { + cf0(); +} +// CHECK-LABEL: cf1 +// CHECK: call{{.*}}[[CF:#[0-9]+]] + +// When the __declspec(guard(nocf)) modifier is present on an override function, +// the "guard_nocf" attribute must be added. +struct Base { + virtual void nocf4(); +}; + +struct Derived : Base { + __declspec(guard(nocf)) void nocf4() override { + (*func_ptr)(); + } +}; +Derived d; +// CHECK-LABEL: nocf4 +// CHECK: call{{.*}}[[NOCF:#[0-9]+]] + +// When the modifier is not present on an override function, the "guard_nocf" +// attribute must *not* be added, even if the modifier is present on the virtual +// function. +struct Base1 { + __declspec(guard(nocf)) virtual void cf2(); +}; + +struct Derived1 : Base1 { + void cf2() override { + (*func_ptr)(); + } +}; +Derived1 d1; +// CHECK-LABEL: cf2 +// CHECK: call{{.*}}[[CF:#[0-9]+]] + +// CHECK: attributes [[NOCF]] = { {{.*}}"guard_nocf"{{.*}} } +// CHECK-NOT: attributes [[CF]] = { {{.*}}"guard_nocf"{{.*}} } diff --git a/clang/test/Sema/attr-guard_nocf.c b/clang/test/Sema/attr-guard_nocf.c new file mode 100644 index 0000000000000..a91640ed98121 --- /dev/null +++ b/clang/test/Sema/attr-guard_nocf.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple %ms_abi_triple -fms-extensions -verify -fsyntax-only %s +// RUN: %clang_cc1 -triple %ms_abi_triple -fms-extensions -verify -std=c++11 -fsyntax-only -x c++ %s + +// Function definition. +__declspec(guard(nocf)) void testGuardNoCF() { // no warning +} + +// Can not be used on variable, parameter, or function pointer declarations. +int __declspec(guard(nocf)) i; // expected-warning {{'guard' attribute only applies to functions}} +void testGuardNoCFFuncParam(double __declspec(guard(nocf)) i) {} // expected-warning {{'guard' attribute only applies to functions}} +__declspec(guard(nocf)) typedef void (*FuncPtrWithGuardNoCF)(void); // expected-warning {{'guard' attribute only applies to functions}} + +// 'guard' Attribute requries an argument. +__declspec(guard) void testGuardNoCFParams() { // expected-error {{'guard' attribute takes one argument}} +} + +// 'guard' Attribute requries an identifier as argument. +__declspec(guard(1)) void testGuardNoCFParamType() { // expected-error {{'guard' attribute requires an identifier}} +} + +// 'guard' Attribute only takes a single argument. +__declspec(guard(nocf, nocf)) void testGuardNoCFTooManyParams() { // expected-error {{use of undeclared identifier 'nocf'}} +} + +// 'guard' Attribute argument must be a supported identifier. +__declspec(guard(cf)) void testGuardNoCFInvalidParam() { // expected-warning {{'guard' attribute argument not supported: 'cf'}} +} diff --git a/llvm/lib/Transforms/CFGuard/CFGuard.cpp b/llvm/lib/Transforms/CFGuard/CFGuard.cpp index 3eca00691e082..7c5e90cb53cd2 100644 --- a/llvm/lib/Transforms/CFGuard/CFGuard.cpp +++ b/llvm/lib/Transforms/CFGuard/CFGuard.cpp @@ -254,8 +254,8 @@ bool CFGuard::doInitialization(Module &M) { bool CFGuard::runOnFunction(Function &F) { - // Skip modules and functions for which CFGuard checks have been disabled. - if (cfguard_module_flag != 2 || F.hasFnAttribute(Attribute::NoCfCheck)) + // Skip modules for which CFGuard checks have been disabled. + if (cfguard_module_flag != 2) return false; SmallVector IndirectCalls; @@ -267,17 +267,15 @@ bool CFGuard::runOnFunction(Function &F) { for (BasicBlock &BB : F.getBasicBlockList()) { for (Instruction &I : BB.getInstList()) { auto *CB = dyn_cast(&I); - if (CB && CB->isIndirectCall()) { + if (CB && CB->isIndirectCall() && !CB->hasFnAttr("guard_nocf")) { IndirectCalls.push_back(CB); CFGuardCounter++; } } } - // If no checks are needed, return early and add this attribute to indicate - // that subsequent CFGuard passes can skip this function. + // If no checks are needed, return early. if (IndirectCalls.empty()) { - F.addFnAttr(Attribute::NoCfCheck); return false; } diff --git a/llvm/test/CodeGen/AArch64/cfguard-checks.ll b/llvm/test/CodeGen/AArch64/cfguard-checks.ll index 627741c4b6fed..5ebe1dd13659d 100644 --- a/llvm/test/CodeGen/AArch64/cfguard-checks.ll +++ b/llvm/test/CodeGen/AArch64/cfguard-checks.ll @@ -7,22 +7,22 @@ declare i32 @target_func() -; Test that Control Flow Guard checks are not added to functions with nocf_checks attribute. -define i32 @func_nocf_checks() #0 { +; Test that Control Flow Guard checks are not added on calls with the "guard_nocf" attribute. +define i32 @func_guard_nocf() { 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() + %1 = call i32 %0() #0 ret i32 %1 - ; CHECK-LABEL: func_nocf_checks + ; CHECK-LABEL: func_guard_nocf ; CHECK: adrp x8, target_func ; CHECK: add x8, x8, target_func ; CHECK-NOT: __guard_check_icall_fptr ; CHECK: blr x8 } -attributes #0 = { nocf_check } +attributes #0 = { "guard_nocf" } ; Test that Control Flow Guard checks are added even at -O0. diff --git a/llvm/test/CodeGen/ARM/cfguard-checks.ll b/llvm/test/CodeGen/ARM/cfguard-checks.ll index c75afc614c7f3..3fab04eb1663f 100644 --- a/llvm/test/CodeGen/ARM/cfguard-checks.ll +++ b/llvm/test/CodeGen/ARM/cfguard-checks.ll @@ -7,26 +7,27 @@ declare i32 @target_func() -; Test that Control Flow Guard checks are not added to functions with nocf_checks attribute. -define i32 @func_nocf_checks() #0 { +; Test that Control Flow Guard checks are not added on calls with the "guard_nocf" attribute. +define i32 @func_guard_nocf() #0 { entry: %func_ptr = alloca i32 ()*, align 8 store i32 ()* @target_func, i32 ()** %func_ptr, align 8 %0 = load i32 ()*, i32 ()** %func_ptr, align 8 - %1 = call arm_aapcs_vfpcc i32 %0() + %1 = call arm_aapcs_vfpcc i32 %0() #1 ret i32 %1 - ; CHECK-LABEL: func_nocf_checks + ; CHECK-LABEL: func_guard_nocf ; CHECK: movw r0, :lower16:target_func ; CHECK: movt r0, :upper16:target_func ; CHECK-NOT: __guard_check_icall_fptr ; CHECK: blx r0 } -attributes #0 = { nocf_check "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} +attributes #0 = { "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} +attributes #1 = { "guard_nocf" } ; Test that Control Flow Guard checks are added even at -O0. -define i32 @func_optnone_cf() #1 { +define i32 @func_optnone_cf() #2 { entry: %func_ptr = alloca i32 ()*, align 8 store i32 ()* @target_func, i32 ()** %func_ptr, align 8 @@ -47,11 +48,11 @@ entry: ; CHECK: blx r1 ; CHECK-NEXT: blx r4 } -attributes #1 = { noinline optnone "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} +attributes #2 = { noinline optnone "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} ; Test that Control Flow Guard checks are correctly added in optimized code (common case). -define i32 @func_cf() #2 { +define i32 @func_cf() #0 { entry: %func_ptr = alloca i32 ()*, align 8 store i32 ()* @target_func, i32 ()** %func_ptr, align 8 @@ -70,11 +71,10 @@ entry: ; CHECK: blx r1 ; CHECK-NEXT: blx r4 } -attributes #2 = { "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"} ; Test that Control Flow Guard checks are correctly added on invoke instructions. -define i32 @func_cf_invoke() #2 personality i8* bitcast (void ()* @h to i8*) { +define i32 @func_cf_invoke() #0 personality i8* bitcast (void ()* @h to i8*) { entry: %0 = alloca i32, align 4 %func_ptr = alloca i32 ()*, align 8 @@ -112,7 +112,7 @@ declare void @h() %struct._SETJMP_FLOAT128 = type { [2 x i64] } @buf1 = internal global [16 x %struct._SETJMP_FLOAT128] zeroinitializer, align 16 -define i32 @func_cf_setjmp() #2 { +define i32 @func_cf_setjmp() #0 { %1 = alloca i32, align 4 %2 = alloca i32, align 4 store i32 0, i32* %1, align 4 diff --git a/llvm/test/CodeGen/X86/cfguard-checks.ll b/llvm/test/CodeGen/X86/cfguard-checks.ll index efd6e7bc11b63..fc5e72b415080 100644 --- a/llvm/test/CodeGen/X86/cfguard-checks.ll +++ b/llvm/test/CodeGen/X86/cfguard-checks.ll @@ -8,26 +8,26 @@ declare i32 @target_func() -; Test that Control Flow Guard checks are not added to functions with nocf_checks attribute. -define i32 @func_nocf_checks() #0 { +; Test that Control Flow Guard checks are not added on calls with the "guard_nocf" attribute. +define i32 @func_guard_nocf() { 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() + %1 = call i32 %0() #0 ret i32 %1 - ; X32-LABEL: func_nocf_checks + ; X32-LABEL: func_guard_nocf ; X32: movl $_target_func, %eax ; X32-NOT: __guard_check_icall_fptr ; X32: calll *%eax - ; X64-LABEL: func_nocf_checks + ; X64-LABEL: func_guard_nocf ; X64: leaq target_func(%rip), %rax ; X64-NOT: __guard_dispatch_icall_fptr ; X64: callq *%rax } -attributes #0 = { nocf_check } +attributes #0 = { "guard_nocf" } ; Test that Control Flow Guard checks are added even at -O0.