diff options
author | Andrew Paverd <andrew.paverd@microsoft.com> | 2019-10-28 13:22:19 +0000 |
---|---|---|
committer | David Chisnall <David.Chisnall@microsoft.com> | 2019-10-28 15:19:39 +0000 |
commit | d157a9bc8ba1085cc4808c6941412322a7fd884e (patch) | |
tree | c675a8e26a4bf08d0cd8abcde693780dd5bd3859 /llvm | |
parent | a233e7d7cb642ada49985426c23aa3c6a4c98690 (diff) | |
download | bcm5719-llvm-d157a9bc8ba1085cc4808c6941412322a7fd884e.tar.gz bcm5719-llvm-d157a9bc8ba1085cc4808c6941412322a7fd884e.zip |
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
Diffstat (limited to 'llvm')
66 files changed, 1447 insertions, 29 deletions
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index c3f401126e1..7c3db50425d 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -444,6 +444,17 @@ added in the future: the GHC or the HiPE convention is used. <CodeGenerator.html#id80>`_ This calling convention does not support varargs and requires the prototype of all callees to exactly match the prototype of the function definition. +"``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 <n>``" - 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 796b0524e0f..131a08a4d4d 100644 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -83,6 +83,11 @@ Non-comprehensive list of changes in this release ``bcmp`` pattern, and convert it into a call to ``bcmp`` (or ``memcmp``) function. +* 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 3a3176e51c5..6456e927734 100644 --- a/llvm/include/llvm/CodeGen/MachineFunction.h +++ b/llvm/include/llvm/CodeGen/MachineFunction.h @@ -304,6 +304,10 @@ class MachineFunction { /// by debug and exception handling consumers. std::vector<MCCFIInstruction> FrameInstructions; + /// List of basic blocks immediately following calls to _setjmp. Used to + /// construct a table of valid longjmp targets for Windows Control Flow Guard. + std::vector<MCSymbol *> LongjmpTargets; + /// \name Exception Handling /// \{ @@ -830,6 +834,17 @@ public: LLVM_NODISCARD unsigned addFrameInst(const MCCFIInstruction &Inst); + /// Returns a reference to a list of symbols immediately following calls to + /// _setjmp in the function. Used to construct the longjmp target table used + /// by Windows Control Flow Guard. + const std::vector<MCSymbol *> &getLongjmpTargets() const { + return LongjmpTargets; + } + + /// Add the specified symbol to the list of valid longjmp targets for Windows + /// Control Flow Guard. + void addLongjmpTarget(MCSymbol *Target) { LongjmpTargets.push_back(Target); } + /// \name Exception Handling /// \{ diff --git a/llvm/include/llvm/CodeGen/Passes.h b/llvm/include/llvm/CodeGen/Passes.h index 1e765ce51e4..5915cb3b2c7 100644 --- a/llvm/include/llvm/CodeGen/Passes.h +++ b/llvm/include/llvm/CodeGen/Passes.h @@ -451,6 +451,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 db3d1175afe..f515050efad 100644 --- a/llvm/include/llvm/CodeGen/TargetCallingConv.h +++ b/llvm/include/llvm/CodeGen/TargetCallingConv.h @@ -38,6 +38,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 @@ -56,8 +57,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) { @@ -88,6 +89,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 a58fca7e73f..b9bf19474ca 100644 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -188,13 +188,14 @@ public: 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 c1c979c2e2a..d0906de3ea4 100644 --- a/llvm/include/llvm/IR/CallingConv.h +++ b/llvm/include/llvm/IR/CallingConv.h @@ -80,6 +80,12 @@ namespace CallingConv { /// be performed. Tail = 18, + /// 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, + // Target - This is the start of the target-specific calling conventions, // 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 7fb94e9d8c2..faf58cf1901 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<uint32_t> *Tag; diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h index 91bd57dc5ac..ea272740ba9 100644 --- a/llvm/include/llvm/IR/LLVMContext.h +++ b/llvm/include/llvm/IR/LLVMContext.h @@ -85,6 +85,7 @@ public: 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 49f69340c82..b8108339abb 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 abc87bf2774..a28ddd7fa45 100644 --- a/llvm/include/llvm/MC/MCObjectFileInfo.h +++ b/llvm/include/llvm/MC/MCObjectFileInfo.h @@ -211,6 +211,7 @@ protected: MCSection *XDataSection; MCSection *SXDataSection; MCSection *GFIDsSection; + MCSection *GLJMPSection; public: void InitMCObjectFileInfo(const Triple &TT, bool PIC, MCContext &ctx, @@ -379,6 +380,7 @@ public: 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 7b1973cc382..d5f3931c3d5 100644 --- a/llvm/include/llvm/Target/TargetCallingConv.td +++ b/llvm/include/llvm/Target/TargetCallingConv.td @@ -51,6 +51,11 @@ class CCIfSwiftSelf<CCAction A> : CCIf<"ArgFlags.isSwiftSelf()", A> { class CCIfSwiftError<CCAction A> : CCIf<"ArgFlags.isSwiftError()", A> { } +/// CCIfCFGuardTarget - If the current argument has cfguardtarget parameter +/// attribute, apply Action A. +class CCIfCFGuardTarget<CCAction A> : CCIf<"ArgFlags.isCFGuardTarget()", A> { +} + /// CCIfConsecutiveRegs - If the current argument has InConsecutiveRegs /// parameter attribute, apply Action A. class CCIfConsecutiveRegs<CCAction A> : 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 00000000000..86fcbc3c13e --- /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 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<ConstantInt>( - MMI->getModule()->getModuleFlag("cfguardtable"))) + MMI->getModule()->getModuleFlag("cfguard"))) Handlers.emplace_back(std::make_unique<WinCFGuard>(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<const Function *> Functions; for (const Function &F : *M) if (F.hasAddressTaken()) Functions.push_back(&F); - if (Functions.empty()) + if (Functions.empty() && LongjmpTargets.empty()) return; auto &OS = *Asm->OutStreamer; OS.SwitchSection(Asm->OutContext.getObjectFileInfo()->getGFIDsSection()); for (const Function *F : Functions) OS.EmitCOFFSymbolIndex(Asm->getSymbol(F)); + + // Emit the symbol index of each longjmp target. + OS.SwitchSection(Asm->OutContext.getObjectFileInfo()->getGLJMPSection()); + for (const MCSymbol *S : LongjmpTargets) { + OS.EmitCOFFSymbolIndex(S); + } } 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 <vector> namespace llvm { class LLVM_LIBRARY_VISIBILITY WinCFGuard : public AsmPrinterHandler { /// Target of directive emission. AsmPrinter *Asm; + std::vector<const MCSymbol *> LongjmpTargets; public: WinCFGuard(AsmPrinter *A); @@ -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<MachineInstr *, 8> 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<Function>(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<LandingPadInst>(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<FuncletPadInst>(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<CC_AArch64_AAPCS> ]>; +// Windows Control Flow Guard checks take a single argument (the target function +// address) and have no return value. +let Entry = 1 in +def CC_AArch64_Win64_CFGuard_Check : CallingConv<[ + CCIfType<[i64], CCAssignToReg<[X15]>> +]>; + // Darwin uses a calling convention which differs in only two ways // from the standard one at this level: @@ -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<AArch64Subtarget>().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<AArch64Subtarget>().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 <memory> #include <string> @@ -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<RetCC_ARM_AAPCS_Common> ]>; + +// Windows Control Flow Guard checks take a single argument (the target function +// address) and have no return value. +let Entry = 1 in +def CC_ARM_Win32_CFGuard_Check : CallingConv<[ + CCIfType<[i32], CCAssignToReg<[R0]>> +]>; + + + //===----------------------------------------------------------------------===// // Callee-saved register lists. //===----------------------------------------------------------------------===// @@ -256,6 +266,11 @@ def CSR_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 <cassert> #include <memory> @@ -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<RetCC_X86_32_Fast>>, CCIfCC<"CallingConv::Tail", CCDelegateTo<RetCC_X86_32_Fast>>, + // CFGuard_Check never returns a value so does not need a RetCC. // If HiPE, use RetCC_X86_32_HiPE. CCIfCC<"CallingConv::HiPE", CCDelegateTo<RetCC_X86_32_HiPE>>, CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<RetCC_X86_32_VectorCall>>, @@ -606,6 +607,9 @@ def CC_X86_Win64_C : CallingConv<[ // A SwiftError is passed in R12. CCIfSwiftError<CCIfType<[i64], CCAssignToReg<[R12]>>>, + // The 'CFGuardTarget' parameter, if any, is passed in RAX. + CCIfCFGuardTarget<CCAssignToReg<[RAX]>>, + // 128 bit vectors are passed by pointer CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64], CCPassIndirect<i64>>, @@ -936,6 +940,12 @@ def CC_X86_32_FastCC : CallingConv<[ CCDelegateTo<CC_X86_32_Common> ]>; +def CC_X86_Win32_CFGuard_Check : CallingConv<[ + // The CFGuard check call takes exactly one integer argument + // (i.e. the target function address), which is passed in ECX. + CCIfType<[i32], CCAssignToReg<[ECX]>> +]>; + def CC_X86_32_GHC : CallingConv<[ // Promote i8/i16 arguments to i32. CCIfType<[i8, i16], CCPromoteToType<i32>>, @@ -1000,6 +1010,7 @@ def CC_X86_32 : CallingConv<[ CCIfCC<"CallingConv::X86_FastCall", CCDelegateTo<CC_X86_32_FastCall>>, CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<CC_X86_Win32_VectorCall>>, CCIfCC<"CallingConv::X86_ThisCall", CCDelegateTo<CC_X86_32_ThisCall>>, + CCIfCC<"CallingConv::CFGuard_Check", CCDelegateTo<CC_X86_Win32_CFGuard_Check>>, CCIfCC<"CallingConv::Fast", CCDelegateTo<CC_X86_32_FastCC>>, CCIfCC<"CallingConv::Tail", CCDelegateTo<CC_X86_32_FastCC>>, CCIfCC<"CallingConv::GHC", CCDelegateTo<CC_X86_32_GHC>>, @@ -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 <memory> #include <string> @@ -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<CSEConfigBase> 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<Value *>;
+
+#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<llvm::OperandBundleDef, 1> 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<CallInst>(CB)) {
+ NewCB = CallInst::Create(CI, Bundles, CB);
+ } else {
+ assert(isa<InvokeInst>(CB) && "Unknown indirect call type");
+ InvokeInst *II = cast<InvokeInst>(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<ConstantInt>(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<CallBase *, 8> 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<CallBase>(&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 diff --git a/llvm/test/Bitcode/calling-conventions.3.2.ll b/llvm/test/Bitcode/calling-conventions.3.2.ll index b60f1d70ca0..d968d802b9c 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 Binary files differindex b3fad967db0..b7ac769a82b 100644 --- a/llvm/test/Bitcode/calling-conventions.3.2.ll.bc +++ b/llvm/test/Bitcode/calling-conventions.3.2.ll.bc diff --git a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll index 8f7aa37cbed..ac7feec77aa 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: <OPERAND_BUNDLE_TAG ; CHECK-NEXT: <OPERAND_BUNDLE_TAG ; CHECK-NEXT: <OPERAND_BUNDLE_TAG +; CHECK-NEXT: <OPERAND_BUNDLE_TAG ; CHECK-NEXT: </OPERAND_BUNDLE_TAGS_BLOCK ; CHECK: <FUNCTION_BLOCK diff --git a/llvm/test/CodeGen/AArch64/cfguard-checks.ll b/llvm/test/CodeGen/AArch64/cfguard-checks.ll new file mode 100644 index 00000000000..4b6a7ebc243 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/cfguard-checks.ll @@ -0,0 +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}
diff --git a/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll new file mode 100644 index 00000000000..25c53019d93 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll @@ -0,0 +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}
diff --git a/llvm/test/CodeGen/ARM/cfguard-checks.ll b/llvm/test/CodeGen/ARM/cfguard-checks.ll new file mode 100644 index 00000000000..1835bcfc1b5 --- /dev/null +++ b/llvm/test/CodeGen/ARM/cfguard-checks.ll @@ -0,0 +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}
diff --git a/llvm/test/CodeGen/ARM/cfguard-module-flag.ll b/llvm/test/CodeGen/ARM/cfguard-module-flag.ll new file mode 100644 index 00000000000..87878a084dc --- /dev/null +++ b/llvm/test/CodeGen/ARM/cfguard-module-flag.ll @@ -0,0 +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}
diff --git a/llvm/test/CodeGen/WinCFGuard/cfguard.ll b/llvm/test/CodeGen/WinCFGuard/cfguard.ll index 2ddd3463219..1018c1fa62c 100644 --- a/llvm/test/CodeGen/WinCFGuard/cfguard.ll +++ b/llvm/test/CodeGen/WinCFGuard/cfguard.ll @@ -1,4 +1,5 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s +; Control Flow Guard is currently only available on Windows ; CHECK: .set @feat.00, 2048 @@ -159,6 +160,6 @@ attributes #2 = { nounwind } !llvm.module.flags = !{!0, !1} !llvm.ident = !{!2} -!0 = !{i32 2, !"cfguardtable", i32 1} +!0 = !{i32 2, !"cfguard", i32 1} !1 = !{i32 1, !"wchar_size", i32 2} !2 = !{!"clang version 6.0.0 "} diff --git a/llvm/test/CodeGen/X86/cfguard-checks.ll b/llvm/test/CodeGen/X86/cfguard-checks.ll new file mode 100644 index 00000000000..5a930afef13 --- /dev/null +++ b/llvm/test/CodeGen/X86/cfguard-checks.ll @@ -0,0 +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}
diff --git a/llvm/test/CodeGen/X86/cfguard-module-flag.ll b/llvm/test/CodeGen/X86/cfguard-module-flag.ll new file mode 100644 index 00000000000..1b2e71c1a45 --- /dev/null +++ b/llvm/test/CodeGen/X86/cfguard-module-flag.ll @@ -0,0 +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}
diff --git a/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll b/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll new file mode 100644 index 00000000000..a554ebeeb90 --- /dev/null +++ b/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll @@ -0,0 +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}
diff --git a/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll b/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll new file mode 100644 index 00000000000..0f31f5ba4b6 --- /dev/null +++ b/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll @@ -0,0 +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}
|