From d157a9bc8ba1085cc4808c6941412322a7fd884e Mon Sep 17 00:00:00 2001 From: Andrew Paverd Date: Mon, 28 Oct 2019 13:22:19 +0000 Subject: 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 --- 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 + .../CodeGen/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 + llvm/lib/Target/AArch64/AArch64CallingConvention.h | 3 + .../lib/Target/AArch64/AArch64CallingConvention.td | 13 + llvm/lib/Target/AArch64/AArch64FastISel.cpp | 2 + llvm/lib/Target/AArch64/AArch64ISelLowering.cpp | 6 +- llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp | 4 + llvm/lib/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 +- 41 files changed, 657 insertions(+), 24 deletions(-) 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 (limited to 'llvm/lib') diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index 5292b0e6274..819f8a0712d 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 664ef8d4844..41e1d0bd889 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1921,6 +1921,7 @@ void LLParser::ParseOptionalDLLStorageClass(unsigned &Res) { /// ::= 'fastcc' /// ::= 'intel_ocl_bicc' /// ::= 'coldcc' +/// ::= 'cfguard_checkcc' /// ::= 'x86_stdcallcc' /// ::= 'x86_fastcallcc' /// ::= 'x86_thiscallcc' @@ -1965,6 +1966,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 f49feb2dc14..2ebfb9da257 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 73c53d6c4af..3692a03c268 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -139,7 +139,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"; @@ -381,12 +381,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(std::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 290be81c6ba..f071a2583e5 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 def0a59ab00..494a153b05b 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 @@ public: 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 @@ public: /// 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 00000000000..42ad22b6cfa --- /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 50b469d6d93..1d561c3c3e8 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 ad9525f927e..85696ccc482 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 45cef4aca88..6e2d22057c1 100644 --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -1590,6 +1590,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); @@ -1683,6 +1687,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 6d7260d7aee..4586a20562e 100644 --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -1190,6 +1190,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 51f5b1998bb..e8e2bb49c9e 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -2746,8 +2746,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()); @@ -7145,6 +7146,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())) @@ -7686,8 +7699,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()); @@ -9030,6 +9045,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; @@ -9142,6 +9158,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 b0c26e0ecaf..f811c842cf5 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -353,6 +353,7 @@ static void PrintCallingConv(unsigned cc, raw_ostream &Out) { case CallingConv::CXX_FAST_TLS: Out << "cxx_fast_tlscc"; break; case CallingConv::GHC: Out << "ghccc"; break; case CallingConv::Tail: Out << "tailcc"; 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 5e8772186a2..cb13b27aa50 100644 --- a/llvm/lib/IR/LLVMContext.cpp +++ b/llvm/lib/IR/LLVMContext.cpp @@ -62,6 +62,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 b17fc433ed7..497dc394d96 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -2975,10 +2975,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(); @@ -2997,6 +2997,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 70c0409ece7..1a9ceb07dd5 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -715,6 +715,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 5a55d090d7c..59939e0684e 100644 --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.h +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.h @@ -31,6 +31,9 @@ bool CC_AArch64_DarwinPCS_ILP32_VarArg(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 bccbbd4591e..3c4121b1185 100644 --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.td +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.td @@ -170,6 +170,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: @@ -384,6 +391,12 @@ def CSR_Win_AArch64_AAPCS : CalleeSavedRegs<(add X19, X20, X21, X22, X23, X24, 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 X19, X20, X21, X22, X23, X24, diff --git a/llvm/lib/Target/AArch64/AArch64FastISel.cpp b/llvm/lib/Target/AArch64/AArch64FastISel.cpp index 277a3052f1e..98410c2e747 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 aa9e26c879a..8e3a524ed2c 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -3118,8 +3118,10 @@ CCAssignFn *AArch64TargetLowering::CCAssignFnForCall(CallingConv::ID CC, : CC_AArch64_DarwinPCS_VarArg; 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 de176088595..918e89fd886 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -43,6 +43,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) @@ -124,6 +126,8 @@ AArch64RegisterInfo::getCallPreservedMask(const MachineFunction &MF, return SCS ? CSR_AArch64_AAVPCS_SCS_RegMask : CSR_AArch64_AAVPCS_RegMask; if (CC == CallingConv::AArch64_SVE_VectorCall) return CSR_AArch64_SVE_AAPCS_RegMask; + if (CC == CallingConv::CFGuard_Check) + return CSR_Win_AArch64_CFGuard_Check_RegMask; if (MF.getSubtarget().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 b3ed96e815b..0ec1e667d69 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 @@ -459,6 +460,10 @@ void AArch64PassConfig::addIRPasses() { addPass(createAArch64StackTaggingPass(/* MergeInit = */ TM->getOptLevel() != CodeGenOpt::None)); + + // Add Control Flow Guard checks. + if (TM->getTargetTriple().isOSWindows()) + addPass(createCFGuardCheckPass()); } // Pass Pipeline Configuration @@ -617,6 +622,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 620d3a857e8..08ff4645451 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 1eaf871867e..0276ff140ac 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 615634551d9..7c692f03b44 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 61d2d83ddc4..55111f544ff 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 1fc5ff6921c..6c204bc0ed5 100644 --- a/llvm/lib/Target/ARM/ARMFastISel.cpp +++ b/llvm/lib/Target/ARM/ARMFastISel.cpp @@ -1879,6 +1879,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 bb6bca51efd..6e511e68d7a 100644 --- a/llvm/lib/Target/ARM/ARMISelLowering.cpp +++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -1855,6 +1855,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; @@ -1914,6 +1915,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 5c8007f101d..7f85b93beac 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 @@ -420,6 +421,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() { @@ -534,4 +539,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 7a6b3a8401e..40f8c2c93b0 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 bfc0eda21fa..5d09ced3d94 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 8d27be30a27..cc1e79d7a29 100644 --- a/llvm/lib/Target/X86/X86AsmPrinter.cpp +++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp @@ -614,7 +614,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 4c49d68bec9..30d05c63814 100644 --- a/llvm/lib/Target/X86/X86CallingConv.td +++ b/llvm/lib/Target/X86/X86CallingConv.td @@ -434,6 +434,7 @@ def RetCC_X86_32 : CallingConv<[ // If FastCC, use RetCC_X86_32_Fast. CCIfCC<"CallingConv::Fast", CCDelegateTo>, CCIfCC<"CallingConv::Tail", 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>, @@ -606,6 +607,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>, @@ -936,6 +940,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>, @@ -1000,6 +1010,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::Tail", CCDelegateTo>, CCIfCC<"CallingConv::GHC", CCDelegateTo>, @@ -1136,7 +1147,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 e5e089d07d5..e5e84bb5f19 100644 --- a/llvm/lib/Target/X86/X86FastISel.cpp +++ b/llvm/lib/Target/X86/X86FastISel.cpp @@ -3218,6 +3218,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 ff625325b4c..9362c60ae49 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 c15297134e4..21f1ef9cb06 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 @@ -414,6 +415,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() { @@ -530,6 +541,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 00000000000..9517e21ffde --- /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 00000000000..65d3a0f0b6f --- /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 00000000000..d20cdb443eb --- /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 74db9e53304..dda5f6de11e 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 75c614fd2c4..5fb5efcc068 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 -- cgit v1.2.3