10946e70aSDimitry Andric //==-- X86LoadValueInjectionLoadHardening.cpp - LVI load hardening for x86 --=// 20946e70aSDimitry Andric // 30946e70aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40946e70aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50946e70aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60946e70aSDimitry Andric // 70946e70aSDimitry Andric //===----------------------------------------------------------------------===// 80946e70aSDimitry Andric /// 90946e70aSDimitry Andric /// Description: This pass finds Load Value Injection (LVI) gadgets consisting 100946e70aSDimitry Andric /// of a load from memory (i.e., SOURCE), and any operation that may transmit 110946e70aSDimitry Andric /// the value loaded from memory over a covert channel, or use the value loaded 120946e70aSDimitry Andric /// from memory to determine a branch/call target (i.e., SINK). After finding 130946e70aSDimitry Andric /// all such gadgets in a given function, the pass minimally inserts LFENCE 140946e70aSDimitry Andric /// instructions in such a manner that the following property is satisfied: for 150946e70aSDimitry Andric /// all SOURCE+SINK pairs, all paths in the CFG from SOURCE to SINK contain at 160946e70aSDimitry Andric /// least one LFENCE instruction. The algorithm that implements this minimal 170946e70aSDimitry Andric /// insertion is influenced by an academic paper that minimally inserts memory 180946e70aSDimitry Andric /// fences for high-performance concurrent programs: 190946e70aSDimitry Andric /// http://www.cs.ucr.edu/~lesani/companion/oopsla15/OOPSLA15.pdf 200946e70aSDimitry Andric /// The algorithm implemented in this pass is as follows: 210946e70aSDimitry Andric /// 1. Build a condensed CFG (i.e., a GadgetGraph) consisting only of the 220946e70aSDimitry Andric /// following components: 230946e70aSDimitry Andric /// - SOURCE instructions (also includes function arguments) 240946e70aSDimitry Andric /// - SINK instructions 250946e70aSDimitry Andric /// - Basic block entry points 260946e70aSDimitry Andric /// - Basic block terminators 270946e70aSDimitry Andric /// - LFENCE instructions 280946e70aSDimitry Andric /// 2. Analyze the GadgetGraph to determine which SOURCE+SINK pairs (i.e., 290946e70aSDimitry Andric /// gadgets) are already mitigated by existing LFENCEs. If all gadgets have been 300946e70aSDimitry Andric /// mitigated, go to step 6. 310946e70aSDimitry Andric /// 3. Use a heuristic or plugin to approximate minimal LFENCE insertion. 320946e70aSDimitry Andric /// 4. Insert one LFENCE along each CFG edge that was cut in step 3. 330946e70aSDimitry Andric /// 5. Go to step 2. 340946e70aSDimitry Andric /// 6. If any LFENCEs were inserted, return `true` from runOnMachineFunction() 350946e70aSDimitry Andric /// to tell LLVM that the function was modified. 360946e70aSDimitry Andric /// 370946e70aSDimitry Andric //===----------------------------------------------------------------------===// 380946e70aSDimitry Andric 390946e70aSDimitry Andric #include "ImmutableGraph.h" 400946e70aSDimitry Andric #include "X86.h" 410946e70aSDimitry Andric #include "X86Subtarget.h" 420946e70aSDimitry Andric #include "X86TargetMachine.h" 430946e70aSDimitry Andric #include "llvm/ADT/DenseMap.h" 44e8d8bef9SDimitry Andric #include "llvm/ADT/STLExtras.h" 450946e70aSDimitry Andric #include "llvm/ADT/SmallSet.h" 460946e70aSDimitry Andric #include "llvm/ADT/Statistic.h" 470946e70aSDimitry Andric #include "llvm/ADT/StringRef.h" 480946e70aSDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h" 490946e70aSDimitry Andric #include "llvm/CodeGen/MachineDominanceFrontier.h" 500946e70aSDimitry Andric #include "llvm/CodeGen/MachineDominators.h" 510946e70aSDimitry Andric #include "llvm/CodeGen/MachineFunction.h" 520946e70aSDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h" 530946e70aSDimitry Andric #include "llvm/CodeGen/MachineInstr.h" 540946e70aSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 550946e70aSDimitry Andric #include "llvm/CodeGen/MachineLoopInfo.h" 560946e70aSDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 570946e70aSDimitry Andric #include "llvm/CodeGen/RDFGraph.h" 580946e70aSDimitry Andric #include "llvm/CodeGen/RDFLiveness.h" 590946e70aSDimitry Andric #include "llvm/InitializePasses.h" 600946e70aSDimitry Andric #include "llvm/Support/CommandLine.h" 610946e70aSDimitry Andric #include "llvm/Support/DOTGraphTraits.h" 620946e70aSDimitry Andric #include "llvm/Support/Debug.h" 630946e70aSDimitry Andric #include "llvm/Support/DynamicLibrary.h" 640946e70aSDimitry Andric #include "llvm/Support/GraphWriter.h" 650946e70aSDimitry Andric #include "llvm/Support/raw_ostream.h" 660946e70aSDimitry Andric 670946e70aSDimitry Andric using namespace llvm; 680946e70aSDimitry Andric 690946e70aSDimitry Andric #define PASS_KEY "x86-lvi-load" 700946e70aSDimitry Andric #define DEBUG_TYPE PASS_KEY 710946e70aSDimitry Andric 720946e70aSDimitry Andric STATISTIC(NumFences, "Number of LFENCEs inserted for LVI mitigation"); 730946e70aSDimitry Andric STATISTIC(NumFunctionsConsidered, "Number of functions analyzed"); 740946e70aSDimitry Andric STATISTIC(NumFunctionsMitigated, "Number of functions for which mitigations " 750946e70aSDimitry Andric "were deployed"); 760946e70aSDimitry Andric STATISTIC(NumGadgets, "Number of LVI gadgets detected during analysis"); 770946e70aSDimitry Andric 780946e70aSDimitry Andric static cl::opt<std::string> OptimizePluginPath( 790946e70aSDimitry Andric PASS_KEY "-opt-plugin", 800946e70aSDimitry Andric cl::desc("Specify a plugin to optimize LFENCE insertion"), cl::Hidden); 810946e70aSDimitry Andric 820946e70aSDimitry Andric static cl::opt<bool> NoConditionalBranches( 830946e70aSDimitry Andric PASS_KEY "-no-cbranch", 840946e70aSDimitry Andric cl::desc("Don't treat conditional branches as disclosure gadgets. This " 850946e70aSDimitry Andric "may improve performance, at the cost of security."), 860946e70aSDimitry Andric cl::init(false), cl::Hidden); 870946e70aSDimitry Andric 880946e70aSDimitry Andric static cl::opt<bool> EmitDot( 890946e70aSDimitry Andric PASS_KEY "-dot", 900946e70aSDimitry Andric cl::desc( 910946e70aSDimitry Andric "For each function, emit a dot graph depicting potential LVI gadgets"), 920946e70aSDimitry Andric cl::init(false), cl::Hidden); 930946e70aSDimitry Andric 940946e70aSDimitry Andric static cl::opt<bool> EmitDotOnly( 950946e70aSDimitry Andric PASS_KEY "-dot-only", 960946e70aSDimitry Andric cl::desc("For each function, emit a dot graph depicting potential LVI " 970946e70aSDimitry Andric "gadgets, and do not insert any fences"), 980946e70aSDimitry Andric cl::init(false), cl::Hidden); 990946e70aSDimitry Andric 1000946e70aSDimitry Andric static cl::opt<bool> EmitDotVerify( 1010946e70aSDimitry Andric PASS_KEY "-dot-verify", 1020946e70aSDimitry Andric cl::desc("For each function, emit a dot graph to stdout depicting " 1030946e70aSDimitry Andric "potential LVI gadgets, used for testing purposes only"), 1040946e70aSDimitry Andric cl::init(false), cl::Hidden); 1050946e70aSDimitry Andric 1060946e70aSDimitry Andric static llvm::sys::DynamicLibrary OptimizeDL; 107e8d8bef9SDimitry Andric typedef int (*OptimizeCutT)(unsigned int *Nodes, unsigned int NodesSize, 108e8d8bef9SDimitry Andric unsigned int *Edges, int *EdgeValues, 109e8d8bef9SDimitry Andric int *CutEdges /* out */, unsigned int EdgesSize); 1100946e70aSDimitry Andric static OptimizeCutT OptimizeCut = nullptr; 1110946e70aSDimitry Andric 1120946e70aSDimitry Andric namespace { 1130946e70aSDimitry Andric 1140946e70aSDimitry Andric struct MachineGadgetGraph : ImmutableGraph<MachineInstr *, int> { 1150946e70aSDimitry Andric static constexpr int GadgetEdgeSentinel = -1; 1160946e70aSDimitry Andric static constexpr MachineInstr *const ArgNodeSentinel = nullptr; 1170946e70aSDimitry Andric 1180946e70aSDimitry Andric using GraphT = ImmutableGraph<MachineInstr *, int>; 1190946e70aSDimitry Andric using Node = typename GraphT::Node; 1200946e70aSDimitry Andric using Edge = typename GraphT::Edge; 1210946e70aSDimitry Andric using size_type = typename GraphT::size_type; 1220946e70aSDimitry Andric MachineGadgetGraph(std::unique_ptr<Node[]> Nodes, 1230946e70aSDimitry Andric std::unique_ptr<Edge[]> Edges, size_type NodesSize, 1240946e70aSDimitry Andric size_type EdgesSize, int NumFences = 0, int NumGadgets = 0) 1250946e70aSDimitry Andric : GraphT(std::move(Nodes), std::move(Edges), NodesSize, EdgesSize), 1260946e70aSDimitry Andric NumFences(NumFences), NumGadgets(NumGadgets) {} 1270946e70aSDimitry Andric static inline bool isCFGEdge(const Edge &E) { 1280946e70aSDimitry Andric return E.getValue() != GadgetEdgeSentinel; 1290946e70aSDimitry Andric } 1300946e70aSDimitry Andric static inline bool isGadgetEdge(const Edge &E) { 1310946e70aSDimitry Andric return E.getValue() == GadgetEdgeSentinel; 1320946e70aSDimitry Andric } 1330946e70aSDimitry Andric int NumFences; 1340946e70aSDimitry Andric int NumGadgets; 1350946e70aSDimitry Andric }; 1360946e70aSDimitry Andric 1370946e70aSDimitry Andric class X86LoadValueInjectionLoadHardeningPass : public MachineFunctionPass { 1380946e70aSDimitry Andric public: 1390946e70aSDimitry Andric X86LoadValueInjectionLoadHardeningPass() : MachineFunctionPass(ID) {} 1400946e70aSDimitry Andric 1410946e70aSDimitry Andric StringRef getPassName() const override { 1420946e70aSDimitry Andric return "X86 Load Value Injection (LVI) Load Hardening"; 1430946e70aSDimitry Andric } 1440946e70aSDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override; 1450946e70aSDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 1460946e70aSDimitry Andric 1470946e70aSDimitry Andric static char ID; 1480946e70aSDimitry Andric 1490946e70aSDimitry Andric private: 1500946e70aSDimitry Andric using GraphBuilder = ImmutableGraphBuilder<MachineGadgetGraph>; 151e8d8bef9SDimitry Andric using Edge = MachineGadgetGraph::Edge; 152e8d8bef9SDimitry Andric using Node = MachineGadgetGraph::Node; 1530946e70aSDimitry Andric using EdgeSet = MachineGadgetGraph::EdgeSet; 1540946e70aSDimitry Andric using NodeSet = MachineGadgetGraph::NodeSet; 1550946e70aSDimitry Andric 15606c3fb27SDimitry Andric const X86Subtarget *STI = nullptr; 15706c3fb27SDimitry Andric const TargetInstrInfo *TII = nullptr; 15806c3fb27SDimitry Andric const TargetRegisterInfo *TRI = nullptr; 1590946e70aSDimitry Andric 1600946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> 1610946e70aSDimitry Andric getGadgetGraph(MachineFunction &MF, const MachineLoopInfo &MLI, 1620946e70aSDimitry Andric const MachineDominatorTree &MDT, 1630946e70aSDimitry Andric const MachineDominanceFrontier &MDF) const; 1640946e70aSDimitry Andric int hardenLoadsWithPlugin(MachineFunction &MF, 1650946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> Graph) const; 166e8d8bef9SDimitry Andric int hardenLoadsWithHeuristic(MachineFunction &MF, 167e8d8bef9SDimitry Andric std::unique_ptr<MachineGadgetGraph> Graph) const; 1680946e70aSDimitry Andric int elimMitigatedEdgesAndNodes(MachineGadgetGraph &G, 1690946e70aSDimitry Andric EdgeSet &ElimEdges /* in, out */, 1700946e70aSDimitry Andric NodeSet &ElimNodes /* in, out */) const; 1710946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> 1720946e70aSDimitry Andric trimMitigatedEdges(std::unique_ptr<MachineGadgetGraph> Graph) const; 1730946e70aSDimitry Andric int insertFences(MachineFunction &MF, MachineGadgetGraph &G, 1740946e70aSDimitry Andric EdgeSet &CutEdges /* in, out */) const; 1750946e70aSDimitry Andric bool instrUsesRegToAccessMemory(const MachineInstr &I, unsigned Reg) const; 1760946e70aSDimitry Andric bool instrUsesRegToBranch(const MachineInstr &I, unsigned Reg) const; 1770946e70aSDimitry Andric inline bool isFence(const MachineInstr *MI) const { 1780946e70aSDimitry Andric return MI && (MI->getOpcode() == X86::LFENCE || 1790946e70aSDimitry Andric (STI->useLVIControlFlowIntegrity() && MI->isCall())); 1800946e70aSDimitry Andric } 1810946e70aSDimitry Andric }; 1820946e70aSDimitry Andric 1830946e70aSDimitry Andric } // end anonymous namespace 1840946e70aSDimitry Andric 1850946e70aSDimitry Andric namespace llvm { 1860946e70aSDimitry Andric 1870946e70aSDimitry Andric template <> 1880946e70aSDimitry Andric struct GraphTraits<MachineGadgetGraph *> 1890946e70aSDimitry Andric : GraphTraits<ImmutableGraph<MachineInstr *, int> *> {}; 1900946e70aSDimitry Andric 1910946e70aSDimitry Andric template <> 1920946e70aSDimitry Andric struct DOTGraphTraits<MachineGadgetGraph *> : DefaultDOTGraphTraits { 1930946e70aSDimitry Andric using GraphType = MachineGadgetGraph; 1940946e70aSDimitry Andric using Traits = llvm::GraphTraits<GraphType *>; 1950946e70aSDimitry Andric using NodeRef = typename Traits::NodeRef; 1960946e70aSDimitry Andric using EdgeRef = typename Traits::EdgeRef; 1970946e70aSDimitry Andric using ChildIteratorType = typename Traits::ChildIteratorType; 1980946e70aSDimitry Andric using ChildEdgeIteratorType = typename Traits::ChildEdgeIteratorType; 1990946e70aSDimitry Andric 200e8d8bef9SDimitry Andric DOTGraphTraits(bool IsSimple = false) : DefaultDOTGraphTraits(IsSimple) {} 2010946e70aSDimitry Andric 2020946e70aSDimitry Andric std::string getNodeLabel(NodeRef Node, GraphType *) { 2030946e70aSDimitry Andric if (Node->getValue() == MachineGadgetGraph::ArgNodeSentinel) 2040946e70aSDimitry Andric return "ARGS"; 2050946e70aSDimitry Andric 2060946e70aSDimitry Andric std::string Str; 2070946e70aSDimitry Andric raw_string_ostream OS(Str); 2080946e70aSDimitry Andric OS << *Node->getValue(); 2090946e70aSDimitry Andric return OS.str(); 2100946e70aSDimitry Andric } 2110946e70aSDimitry Andric 2120946e70aSDimitry Andric static std::string getNodeAttributes(NodeRef Node, GraphType *) { 2130946e70aSDimitry Andric MachineInstr *MI = Node->getValue(); 2140946e70aSDimitry Andric if (MI == MachineGadgetGraph::ArgNodeSentinel) 2150946e70aSDimitry Andric return "color = blue"; 2160946e70aSDimitry Andric if (MI->getOpcode() == X86::LFENCE) 2170946e70aSDimitry Andric return "color = green"; 2180946e70aSDimitry Andric return ""; 2190946e70aSDimitry Andric } 2200946e70aSDimitry Andric 2210946e70aSDimitry Andric static std::string getEdgeAttributes(NodeRef, ChildIteratorType E, 2220946e70aSDimitry Andric GraphType *) { 2230946e70aSDimitry Andric int EdgeVal = (*E.getCurrent()).getValue(); 2240946e70aSDimitry Andric return EdgeVal >= 0 ? "label = " + std::to_string(EdgeVal) 2250946e70aSDimitry Andric : "color = red, style = \"dashed\""; 2260946e70aSDimitry Andric } 2270946e70aSDimitry Andric }; 2280946e70aSDimitry Andric 2290946e70aSDimitry Andric } // end namespace llvm 2300946e70aSDimitry Andric 2310946e70aSDimitry Andric constexpr MachineInstr *MachineGadgetGraph::ArgNodeSentinel; 2320946e70aSDimitry Andric constexpr int MachineGadgetGraph::GadgetEdgeSentinel; 2330946e70aSDimitry Andric 2340946e70aSDimitry Andric char X86LoadValueInjectionLoadHardeningPass::ID = 0; 2350946e70aSDimitry Andric 2360946e70aSDimitry Andric void X86LoadValueInjectionLoadHardeningPass::getAnalysisUsage( 2370946e70aSDimitry Andric AnalysisUsage &AU) const { 2380946e70aSDimitry Andric MachineFunctionPass::getAnalysisUsage(AU); 239*0fca6ea1SDimitry Andric AU.addRequired<MachineLoopInfoWrapperPass>(); 240*0fca6ea1SDimitry Andric AU.addRequired<MachineDominatorTreeWrapperPass>(); 2410946e70aSDimitry Andric AU.addRequired<MachineDominanceFrontier>(); 2420946e70aSDimitry Andric AU.setPreservesCFG(); 2430946e70aSDimitry Andric } 2440946e70aSDimitry Andric 245e8d8bef9SDimitry Andric static void writeGadgetGraph(raw_ostream &OS, MachineFunction &MF, 2460946e70aSDimitry Andric MachineGadgetGraph *G) { 2470946e70aSDimitry Andric WriteGraph(OS, G, /*ShortNames*/ false, 2480946e70aSDimitry Andric "Speculative gadgets for \"" + MF.getName() + "\" function"); 2490946e70aSDimitry Andric } 2500946e70aSDimitry Andric 2510946e70aSDimitry Andric bool X86LoadValueInjectionLoadHardeningPass::runOnMachineFunction( 2520946e70aSDimitry Andric MachineFunction &MF) { 2530946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "***** " << getPassName() << " : " << MF.getName() 2540946e70aSDimitry Andric << " *****\n"); 2550946e70aSDimitry Andric STI = &MF.getSubtarget<X86Subtarget>(); 2560946e70aSDimitry Andric if (!STI->useLVILoadHardening()) 2570946e70aSDimitry Andric return false; 2580946e70aSDimitry Andric 2590946e70aSDimitry Andric // FIXME: support 32-bit 2600946e70aSDimitry Andric if (!STI->is64Bit()) 2610946e70aSDimitry Andric report_fatal_error("LVI load hardening is only supported on 64-bit", false); 2620946e70aSDimitry Andric 2630946e70aSDimitry Andric // Don't skip functions with the "optnone" attr but participate in opt-bisect. 2640946e70aSDimitry Andric const Function &F = MF.getFunction(); 2650946e70aSDimitry Andric if (!F.hasOptNone() && skipFunction(F)) 2660946e70aSDimitry Andric return false; 2670946e70aSDimitry Andric 2680946e70aSDimitry Andric ++NumFunctionsConsidered; 2690946e70aSDimitry Andric TII = STI->getInstrInfo(); 2700946e70aSDimitry Andric TRI = STI->getRegisterInfo(); 2710946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Building gadget graph...\n"); 272*0fca6ea1SDimitry Andric const auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI(); 273*0fca6ea1SDimitry Andric const auto &MDT = getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree(); 2740946e70aSDimitry Andric const auto &MDF = getAnalysis<MachineDominanceFrontier>(); 2750946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> Graph = getGadgetGraph(MF, MLI, MDT, MDF); 2760946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Building gadget graph... Done\n"); 2770946e70aSDimitry Andric if (Graph == nullptr) 2780946e70aSDimitry Andric return false; // didn't find any gadgets 2790946e70aSDimitry Andric 2800946e70aSDimitry Andric if (EmitDotVerify) { 281e8d8bef9SDimitry Andric writeGadgetGraph(outs(), MF, Graph.get()); 2820946e70aSDimitry Andric return false; 2830946e70aSDimitry Andric } 2840946e70aSDimitry Andric 2850946e70aSDimitry Andric if (EmitDot || EmitDotOnly) { 2860946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Emitting gadget graph...\n"); 2870946e70aSDimitry Andric std::error_code FileError; 2880946e70aSDimitry Andric std::string FileName = "lvi."; 2890946e70aSDimitry Andric FileName += MF.getName(); 2900946e70aSDimitry Andric FileName += ".dot"; 2910946e70aSDimitry Andric raw_fd_ostream FileOut(FileName, FileError); 2920946e70aSDimitry Andric if (FileError) 2930946e70aSDimitry Andric errs() << FileError.message(); 294e8d8bef9SDimitry Andric writeGadgetGraph(FileOut, MF, Graph.get()); 2950946e70aSDimitry Andric FileOut.close(); 2960946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Emitting gadget graph... Done\n"); 2970946e70aSDimitry Andric if (EmitDotOnly) 2980946e70aSDimitry Andric return false; 2990946e70aSDimitry Andric } 3000946e70aSDimitry Andric 3010946e70aSDimitry Andric int FencesInserted; 3020946e70aSDimitry Andric if (!OptimizePluginPath.empty()) { 3030946e70aSDimitry Andric if (!OptimizeDL.isValid()) { 3040946e70aSDimitry Andric std::string ErrorMsg; 3050946e70aSDimitry Andric OptimizeDL = llvm::sys::DynamicLibrary::getPermanentLibrary( 3060946e70aSDimitry Andric OptimizePluginPath.c_str(), &ErrorMsg); 3070946e70aSDimitry Andric if (!ErrorMsg.empty()) 308349cc55cSDimitry Andric report_fatal_error(Twine("Failed to load opt plugin: \"") + ErrorMsg + 309349cc55cSDimitry Andric "\""); 3100946e70aSDimitry Andric OptimizeCut = (OptimizeCutT)OptimizeDL.getAddressOfSymbol("optimize_cut"); 3110946e70aSDimitry Andric if (!OptimizeCut) 3120946e70aSDimitry Andric report_fatal_error("Invalid optimization plugin"); 3130946e70aSDimitry Andric } 3140946e70aSDimitry Andric FencesInserted = hardenLoadsWithPlugin(MF, std::move(Graph)); 3150946e70aSDimitry Andric } else { // Use the default greedy heuristic 316e8d8bef9SDimitry Andric FencesInserted = hardenLoadsWithHeuristic(MF, std::move(Graph)); 3170946e70aSDimitry Andric } 3180946e70aSDimitry Andric 3190946e70aSDimitry Andric if (FencesInserted > 0) 3200946e70aSDimitry Andric ++NumFunctionsMitigated; 3210946e70aSDimitry Andric NumFences += FencesInserted; 3220946e70aSDimitry Andric return (FencesInserted > 0); 3230946e70aSDimitry Andric } 3240946e70aSDimitry Andric 3250946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> 3260946e70aSDimitry Andric X86LoadValueInjectionLoadHardeningPass::getGadgetGraph( 3270946e70aSDimitry Andric MachineFunction &MF, const MachineLoopInfo &MLI, 3280946e70aSDimitry Andric const MachineDominatorTree &MDT, 3290946e70aSDimitry Andric const MachineDominanceFrontier &MDF) const { 3300946e70aSDimitry Andric using namespace rdf; 3310946e70aSDimitry Andric 3320946e70aSDimitry Andric // Build the Register Dataflow Graph using the RDF framework 333bdd1243dSDimitry Andric DataFlowGraph DFG{MF, *TII, *TRI, MDT, MDF}; 3340946e70aSDimitry Andric DFG.build(); 3350946e70aSDimitry Andric Liveness L{MF.getRegInfo(), DFG}; 3360946e70aSDimitry Andric L.computePhiInfo(); 3370946e70aSDimitry Andric 3380946e70aSDimitry Andric GraphBuilder Builder; 3390946e70aSDimitry Andric using GraphIter = typename GraphBuilder::BuilderNodeRef; 3400946e70aSDimitry Andric DenseMap<MachineInstr *, GraphIter> NodeMap; 3410946e70aSDimitry Andric int FenceCount = 0, GadgetCount = 0; 3420946e70aSDimitry Andric auto MaybeAddNode = [&NodeMap, &Builder](MachineInstr *MI) { 3430946e70aSDimitry Andric auto Ref = NodeMap.find(MI); 3440946e70aSDimitry Andric if (Ref == NodeMap.end()) { 3450946e70aSDimitry Andric auto I = Builder.addVertex(MI); 3460946e70aSDimitry Andric NodeMap[MI] = I; 3470946e70aSDimitry Andric return std::pair<GraphIter, bool>{I, true}; 3480946e70aSDimitry Andric } 3490946e70aSDimitry Andric return std::pair<GraphIter, bool>{Ref->getSecond(), false}; 3500946e70aSDimitry Andric }; 3510946e70aSDimitry Andric 3520946e70aSDimitry Andric // The `Transmitters` map memoizes transmitters found for each def. If a def 3530946e70aSDimitry Andric // has not yet been analyzed, then it will not appear in the map. If a def 3540946e70aSDimitry Andric // has been analyzed and was determined not to have any transmitters, then 3550946e70aSDimitry Andric // its list of transmitters will be empty. 3560946e70aSDimitry Andric DenseMap<NodeId, std::vector<NodeId>> Transmitters; 3570946e70aSDimitry Andric 3580946e70aSDimitry Andric // Analyze all machine instructions to find gadgets and LFENCEs, adding 3590946e70aSDimitry Andric // each interesting value to `Nodes` 3600946e70aSDimitry Andric auto AnalyzeDef = [&](NodeAddr<DefNode *> SourceDef) { 3610946e70aSDimitry Andric SmallSet<NodeId, 8> UsesVisited, DefsVisited; 3620946e70aSDimitry Andric std::function<void(NodeAddr<DefNode *>)> AnalyzeDefUseChain = 3630946e70aSDimitry Andric [&](NodeAddr<DefNode *> Def) { 36406c3fb27SDimitry Andric if (Transmitters.contains(Def.Id)) 3650946e70aSDimitry Andric return; // Already analyzed `Def` 3660946e70aSDimitry Andric 3670946e70aSDimitry Andric // Use RDF to find all the uses of `Def` 3680946e70aSDimitry Andric rdf::NodeSet Uses; 369e8d8bef9SDimitry Andric RegisterRef DefReg = Def.Addr->getRegRef(DFG); 3700946e70aSDimitry Andric for (auto UseID : L.getAllReachedUses(DefReg, Def)) { 3710946e70aSDimitry Andric auto Use = DFG.addr<UseNode *>(UseID); 3720946e70aSDimitry Andric if (Use.Addr->getFlags() & NodeAttrs::PhiRef) { // phi node 3730946e70aSDimitry Andric NodeAddr<PhiNode *> Phi = Use.Addr->getOwner(DFG); 374fe6060f1SDimitry Andric for (const auto& I : L.getRealUses(Phi.Id)) { 3750946e70aSDimitry Andric if (DFG.getPRI().alias(RegisterRef(I.first), DefReg)) { 376fe6060f1SDimitry Andric for (const auto &UA : I.second) 3770946e70aSDimitry Andric Uses.emplace(UA.first); 3780946e70aSDimitry Andric } 3790946e70aSDimitry Andric } 3800946e70aSDimitry Andric } else { // not a phi node 3810946e70aSDimitry Andric Uses.emplace(UseID); 3820946e70aSDimitry Andric } 3830946e70aSDimitry Andric } 3840946e70aSDimitry Andric 3850946e70aSDimitry Andric // For each use of `Def`, we want to know whether: 3860946e70aSDimitry Andric // (1) The use can leak the Def'ed value, 3870946e70aSDimitry Andric // (2) The use can further propagate the Def'ed value to more defs 3880946e70aSDimitry Andric for (auto UseID : Uses) { 3890946e70aSDimitry Andric if (!UsesVisited.insert(UseID).second) 3900946e70aSDimitry Andric continue; // Already visited this use of `Def` 3910946e70aSDimitry Andric 3920946e70aSDimitry Andric auto Use = DFG.addr<UseNode *>(UseID); 3930946e70aSDimitry Andric assert(!(Use.Addr->getFlags() & NodeAttrs::PhiRef)); 3940946e70aSDimitry Andric MachineOperand &UseMO = Use.Addr->getOp(); 3950946e70aSDimitry Andric MachineInstr &UseMI = *UseMO.getParent(); 3960946e70aSDimitry Andric assert(UseMO.isReg()); 3970946e70aSDimitry Andric 3980946e70aSDimitry Andric // We naively assume that an instruction propagates any loaded 3990946e70aSDimitry Andric // uses to all defs unless the instruction is a call, in which 4000946e70aSDimitry Andric // case all arguments will be treated as gadget sources during 4010946e70aSDimitry Andric // analysis of the callee function. 4020946e70aSDimitry Andric if (UseMI.isCall()) 4030946e70aSDimitry Andric continue; 4040946e70aSDimitry Andric 4050946e70aSDimitry Andric // Check whether this use can transmit (leak) its value. 4060946e70aSDimitry Andric if (instrUsesRegToAccessMemory(UseMI, UseMO.getReg()) || 4070946e70aSDimitry Andric (!NoConditionalBranches && 4080946e70aSDimitry Andric instrUsesRegToBranch(UseMI, UseMO.getReg()))) { 4090946e70aSDimitry Andric Transmitters[Def.Id].push_back(Use.Addr->getOwner(DFG).Id); 4100946e70aSDimitry Andric if (UseMI.mayLoad()) 4110946e70aSDimitry Andric continue; // Found a transmitting load -- no need to continue 4120946e70aSDimitry Andric // traversing its defs (i.e., this load will become 4130946e70aSDimitry Andric // a new gadget source anyways). 4140946e70aSDimitry Andric } 4150946e70aSDimitry Andric 4160946e70aSDimitry Andric // Check whether the use propagates to more defs. 4170946e70aSDimitry Andric NodeAddr<InstrNode *> Owner{Use.Addr->getOwner(DFG)}; 4180946e70aSDimitry Andric rdf::NodeList AnalyzedChildDefs; 419fe6060f1SDimitry Andric for (const auto &ChildDef : 4200946e70aSDimitry Andric Owner.Addr->members_if(DataFlowGraph::IsDef, DFG)) { 4210946e70aSDimitry Andric if (!DefsVisited.insert(ChildDef.Id).second) 4220946e70aSDimitry Andric continue; // Already visited this def 4230946e70aSDimitry Andric if (Def.Addr->getAttrs() & NodeAttrs::Dead) 4240946e70aSDimitry Andric continue; 4250946e70aSDimitry Andric if (Def.Id == ChildDef.Id) 4260946e70aSDimitry Andric continue; // `Def` uses itself (e.g., increment loop counter) 4270946e70aSDimitry Andric 4280946e70aSDimitry Andric AnalyzeDefUseChain(ChildDef); 4290946e70aSDimitry Andric 4300946e70aSDimitry Andric // `Def` inherits all of its child defs' transmitters. 4310946e70aSDimitry Andric for (auto TransmitterId : Transmitters[ChildDef.Id]) 4320946e70aSDimitry Andric Transmitters[Def.Id].push_back(TransmitterId); 4330946e70aSDimitry Andric } 4340946e70aSDimitry Andric } 4350946e70aSDimitry Andric 4360946e70aSDimitry Andric // Note that this statement adds `Def.Id` to the map if no 4370946e70aSDimitry Andric // transmitters were found for `Def`. 4380946e70aSDimitry Andric auto &DefTransmitters = Transmitters[Def.Id]; 4390946e70aSDimitry Andric 4400946e70aSDimitry Andric // Remove duplicate transmitters 4410946e70aSDimitry Andric llvm::sort(DefTransmitters); 442*0fca6ea1SDimitry Andric DefTransmitters.erase(llvm::unique(DefTransmitters), 4430946e70aSDimitry Andric DefTransmitters.end()); 4440946e70aSDimitry Andric }; 4450946e70aSDimitry Andric 4460946e70aSDimitry Andric // Find all of the transmitters 4470946e70aSDimitry Andric AnalyzeDefUseChain(SourceDef); 4480946e70aSDimitry Andric auto &SourceDefTransmitters = Transmitters[SourceDef.Id]; 4490946e70aSDimitry Andric if (SourceDefTransmitters.empty()) 4500946e70aSDimitry Andric return; // No transmitters for `SourceDef` 4510946e70aSDimitry Andric 4520946e70aSDimitry Andric MachineInstr *Source = SourceDef.Addr->getFlags() & NodeAttrs::PhiRef 4530946e70aSDimitry Andric ? MachineGadgetGraph::ArgNodeSentinel 4540946e70aSDimitry Andric : SourceDef.Addr->getOp().getParent(); 4550946e70aSDimitry Andric auto GadgetSource = MaybeAddNode(Source); 4560946e70aSDimitry Andric // Each transmitter is a sink for `SourceDef`. 4570946e70aSDimitry Andric for (auto TransmitterId : SourceDefTransmitters) { 4580946e70aSDimitry Andric MachineInstr *Sink = DFG.addr<StmtNode *>(TransmitterId).Addr->getCode(); 4590946e70aSDimitry Andric auto GadgetSink = MaybeAddNode(Sink); 4600946e70aSDimitry Andric // Add the gadget edge to the graph. 4610946e70aSDimitry Andric Builder.addEdge(MachineGadgetGraph::GadgetEdgeSentinel, 4620946e70aSDimitry Andric GadgetSource.first, GadgetSink.first); 4630946e70aSDimitry Andric ++GadgetCount; 4640946e70aSDimitry Andric } 4650946e70aSDimitry Andric }; 4660946e70aSDimitry Andric 4670946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Analyzing def-use chains to find gadgets\n"); 4680946e70aSDimitry Andric // Analyze function arguments 4690946e70aSDimitry Andric NodeAddr<BlockNode *> EntryBlock = DFG.getFunc().Addr->getEntryBlock(DFG); 4700946e70aSDimitry Andric for (NodeAddr<PhiNode *> ArgPhi : 4710946e70aSDimitry Andric EntryBlock.Addr->members_if(DataFlowGraph::IsPhi, DFG)) { 4720946e70aSDimitry Andric NodeList Defs = ArgPhi.Addr->members_if(DataFlowGraph::IsDef, DFG); 4730946e70aSDimitry Andric llvm::for_each(Defs, AnalyzeDef); 4740946e70aSDimitry Andric } 4750946e70aSDimitry Andric // Analyze every instruction in MF 4760946e70aSDimitry Andric for (NodeAddr<BlockNode *> BA : DFG.getFunc().Addr->members(DFG)) { 4770946e70aSDimitry Andric for (NodeAddr<StmtNode *> SA : 4780946e70aSDimitry Andric BA.Addr->members_if(DataFlowGraph::IsCode<NodeAttrs::Stmt>, DFG)) { 4790946e70aSDimitry Andric MachineInstr *MI = SA.Addr->getCode(); 4800946e70aSDimitry Andric if (isFence(MI)) { 4810946e70aSDimitry Andric MaybeAddNode(MI); 4820946e70aSDimitry Andric ++FenceCount; 4830946e70aSDimitry Andric } else if (MI->mayLoad()) { 4840946e70aSDimitry Andric NodeList Defs = SA.Addr->members_if(DataFlowGraph::IsDef, DFG); 4850946e70aSDimitry Andric llvm::for_each(Defs, AnalyzeDef); 4860946e70aSDimitry Andric } 4870946e70aSDimitry Andric } 4880946e70aSDimitry Andric } 4890946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Found " << FenceCount << " fences\n"); 4900946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Found " << GadgetCount << " gadgets\n"); 4910946e70aSDimitry Andric if (GadgetCount == 0) 4920946e70aSDimitry Andric return nullptr; 4930946e70aSDimitry Andric NumGadgets += GadgetCount; 4940946e70aSDimitry Andric 4950946e70aSDimitry Andric // Traverse CFG to build the rest of the graph 4960946e70aSDimitry Andric SmallSet<MachineBasicBlock *, 8> BlocksVisited; 4970946e70aSDimitry Andric std::function<void(MachineBasicBlock *, GraphIter, unsigned)> TraverseCFG = 4980946e70aSDimitry Andric [&](MachineBasicBlock *MBB, GraphIter GI, unsigned ParentDepth) { 4990946e70aSDimitry Andric unsigned LoopDepth = MLI.getLoopDepth(MBB); 5000946e70aSDimitry Andric if (!MBB->empty()) { 5010946e70aSDimitry Andric // Always add the first instruction in each block 5020946e70aSDimitry Andric auto NI = MBB->begin(); 5030946e70aSDimitry Andric auto BeginBB = MaybeAddNode(&*NI); 5040946e70aSDimitry Andric Builder.addEdge(ParentDepth, GI, BeginBB.first); 5050946e70aSDimitry Andric if (!BlocksVisited.insert(MBB).second) 5060946e70aSDimitry Andric return; 5070946e70aSDimitry Andric 5080946e70aSDimitry Andric // Add any instructions within the block that are gadget components 5090946e70aSDimitry Andric GI = BeginBB.first; 5100946e70aSDimitry Andric while (++NI != MBB->end()) { 5110946e70aSDimitry Andric auto Ref = NodeMap.find(&*NI); 5120946e70aSDimitry Andric if (Ref != NodeMap.end()) { 5130946e70aSDimitry Andric Builder.addEdge(LoopDepth, GI, Ref->getSecond()); 5140946e70aSDimitry Andric GI = Ref->getSecond(); 5150946e70aSDimitry Andric } 5160946e70aSDimitry Andric } 5170946e70aSDimitry Andric 5180946e70aSDimitry Andric // Always add the terminator instruction, if one exists 5190946e70aSDimitry Andric auto T = MBB->getFirstTerminator(); 5200946e70aSDimitry Andric if (T != MBB->end()) { 5210946e70aSDimitry Andric auto EndBB = MaybeAddNode(&*T); 5220946e70aSDimitry Andric if (EndBB.second) 5230946e70aSDimitry Andric Builder.addEdge(LoopDepth, GI, EndBB.first); 5240946e70aSDimitry Andric GI = EndBB.first; 5250946e70aSDimitry Andric } 5260946e70aSDimitry Andric } 5270946e70aSDimitry Andric for (MachineBasicBlock *Succ : MBB->successors()) 5280946e70aSDimitry Andric TraverseCFG(Succ, GI, LoopDepth); 5290946e70aSDimitry Andric }; 5300946e70aSDimitry Andric // ArgNodeSentinel is a pseudo-instruction that represents MF args in the 5310946e70aSDimitry Andric // GadgetGraph 5320946e70aSDimitry Andric GraphIter ArgNode = MaybeAddNode(MachineGadgetGraph::ArgNodeSentinel).first; 5330946e70aSDimitry Andric TraverseCFG(&MF.front(), ArgNode, 0); 5340946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> G{Builder.get(FenceCount, GadgetCount)}; 5350946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Found " << G->nodes_size() << " nodes\n"); 5360946e70aSDimitry Andric return G; 5370946e70aSDimitry Andric } 5380946e70aSDimitry Andric 5390946e70aSDimitry Andric // Returns the number of remaining gadget edges that could not be eliminated 5400946e70aSDimitry Andric int X86LoadValueInjectionLoadHardeningPass::elimMitigatedEdgesAndNodes( 541e8d8bef9SDimitry Andric MachineGadgetGraph &G, EdgeSet &ElimEdges /* in, out */, 542e8d8bef9SDimitry Andric NodeSet &ElimNodes /* in, out */) const { 5430946e70aSDimitry Andric if (G.NumFences > 0) { 5440946e70aSDimitry Andric // Eliminate fences and CFG edges that ingress and egress the fence, as 5450946e70aSDimitry Andric // they are trivially mitigated. 546e8d8bef9SDimitry Andric for (const Edge &E : G.edges()) { 547e8d8bef9SDimitry Andric const Node *Dest = E.getDest(); 5480946e70aSDimitry Andric if (isFence(Dest->getValue())) { 5490946e70aSDimitry Andric ElimNodes.insert(*Dest); 5500946e70aSDimitry Andric ElimEdges.insert(E); 551e8d8bef9SDimitry Andric for (const Edge &DE : Dest->edges()) 5520946e70aSDimitry Andric ElimEdges.insert(DE); 5530946e70aSDimitry Andric } 5540946e70aSDimitry Andric } 5550946e70aSDimitry Andric } 5560946e70aSDimitry Andric 5570946e70aSDimitry Andric // Find and eliminate gadget edges that have been mitigated. 55881ad6265SDimitry Andric int RemainingGadgets = 0; 559e8d8bef9SDimitry Andric NodeSet ReachableNodes{G}; 560e8d8bef9SDimitry Andric for (const Node &RootN : G.nodes()) { 5610946e70aSDimitry Andric if (llvm::none_of(RootN.edges(), MachineGadgetGraph::isGadgetEdge)) 5620946e70aSDimitry Andric continue; // skip this node if it isn't a gadget source 5630946e70aSDimitry Andric 5640946e70aSDimitry Andric // Find all of the nodes that are CFG-reachable from RootN using DFS 5650946e70aSDimitry Andric ReachableNodes.clear(); 566e8d8bef9SDimitry Andric std::function<void(const Node *, bool)> FindReachableNodes = 567e8d8bef9SDimitry Andric [&](const Node *N, bool FirstNode) { 5680946e70aSDimitry Andric if (!FirstNode) 5690946e70aSDimitry Andric ReachableNodes.insert(*N); 570e8d8bef9SDimitry Andric for (const Edge &E : N->edges()) { 571e8d8bef9SDimitry Andric const Node *Dest = E.getDest(); 572e8d8bef9SDimitry Andric if (MachineGadgetGraph::isCFGEdge(E) && !ElimEdges.contains(E) && 573e8d8bef9SDimitry Andric !ReachableNodes.contains(*Dest)) 5740946e70aSDimitry Andric FindReachableNodes(Dest, false); 5750946e70aSDimitry Andric } 5760946e70aSDimitry Andric }; 5770946e70aSDimitry Andric FindReachableNodes(&RootN, true); 5780946e70aSDimitry Andric 5790946e70aSDimitry Andric // Any gadget whose sink is unreachable has been mitigated 580e8d8bef9SDimitry Andric for (const Edge &E : RootN.edges()) { 5810946e70aSDimitry Andric if (MachineGadgetGraph::isGadgetEdge(E)) { 5820946e70aSDimitry Andric if (ReachableNodes.contains(*E.getDest())) { 5830946e70aSDimitry Andric // This gadget's sink is reachable 5840946e70aSDimitry Andric ++RemainingGadgets; 5850946e70aSDimitry Andric } else { // This gadget's sink is unreachable, and therefore mitigated 5860946e70aSDimitry Andric ElimEdges.insert(E); 5870946e70aSDimitry Andric } 5880946e70aSDimitry Andric } 5890946e70aSDimitry Andric } 5900946e70aSDimitry Andric } 5910946e70aSDimitry Andric return RemainingGadgets; 5920946e70aSDimitry Andric } 5930946e70aSDimitry Andric 5940946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> 5950946e70aSDimitry Andric X86LoadValueInjectionLoadHardeningPass::trimMitigatedEdges( 5960946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> Graph) const { 597e8d8bef9SDimitry Andric NodeSet ElimNodes{*Graph}; 598e8d8bef9SDimitry Andric EdgeSet ElimEdges{*Graph}; 5990946e70aSDimitry Andric int RemainingGadgets = 6000946e70aSDimitry Andric elimMitigatedEdgesAndNodes(*Graph, ElimEdges, ElimNodes); 6010946e70aSDimitry Andric if (ElimEdges.empty() && ElimNodes.empty()) { 6020946e70aSDimitry Andric Graph->NumFences = 0; 6030946e70aSDimitry Andric Graph->NumGadgets = RemainingGadgets; 6040946e70aSDimitry Andric } else { 6050946e70aSDimitry Andric Graph = GraphBuilder::trim(*Graph, ElimNodes, ElimEdges, 0 /* NumFences */, 6060946e70aSDimitry Andric RemainingGadgets); 6070946e70aSDimitry Andric } 6080946e70aSDimitry Andric return Graph; 6090946e70aSDimitry Andric } 6100946e70aSDimitry Andric 6110946e70aSDimitry Andric int X86LoadValueInjectionLoadHardeningPass::hardenLoadsWithPlugin( 6120946e70aSDimitry Andric MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph) const { 6130946e70aSDimitry Andric int FencesInserted = 0; 6140946e70aSDimitry Andric 6150946e70aSDimitry Andric do { 6160946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Eliminating mitigated paths...\n"); 6170946e70aSDimitry Andric Graph = trimMitigatedEdges(std::move(Graph)); 6180946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Eliminating mitigated paths... Done\n"); 6190946e70aSDimitry Andric if (Graph->NumGadgets == 0) 6200946e70aSDimitry Andric break; 6210946e70aSDimitry Andric 6220946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Cutting edges...\n"); 6230946e70aSDimitry Andric EdgeSet CutEdges{*Graph}; 6240946e70aSDimitry Andric auto Nodes = std::make_unique<unsigned int[]>(Graph->nodes_size() + 6250946e70aSDimitry Andric 1 /* terminator node */); 6260946e70aSDimitry Andric auto Edges = std::make_unique<unsigned int[]>(Graph->edges_size()); 6270946e70aSDimitry Andric auto EdgeCuts = std::make_unique<int[]>(Graph->edges_size()); 6280946e70aSDimitry Andric auto EdgeValues = std::make_unique<int[]>(Graph->edges_size()); 629e8d8bef9SDimitry Andric for (const Node &N : Graph->nodes()) { 6300946e70aSDimitry Andric Nodes[Graph->getNodeIndex(N)] = Graph->getEdgeIndex(*N.edges_begin()); 6310946e70aSDimitry Andric } 6320946e70aSDimitry Andric Nodes[Graph->nodes_size()] = Graph->edges_size(); // terminator node 633e8d8bef9SDimitry Andric for (const Edge &E : Graph->edges()) { 6340946e70aSDimitry Andric Edges[Graph->getEdgeIndex(E)] = Graph->getNodeIndex(*E.getDest()); 6350946e70aSDimitry Andric EdgeValues[Graph->getEdgeIndex(E)] = E.getValue(); 6360946e70aSDimitry Andric } 6370946e70aSDimitry Andric OptimizeCut(Nodes.get(), Graph->nodes_size(), Edges.get(), EdgeValues.get(), 6380946e70aSDimitry Andric EdgeCuts.get(), Graph->edges_size()); 6390946e70aSDimitry Andric for (int I = 0; I < Graph->edges_size(); ++I) 6400946e70aSDimitry Andric if (EdgeCuts[I]) 6410946e70aSDimitry Andric CutEdges.set(I); 6420946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Cutting edges... Done\n"); 6430946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Cut " << CutEdges.count() << " edges\n"); 6440946e70aSDimitry Andric 6450946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Inserting LFENCEs...\n"); 6460946e70aSDimitry Andric FencesInserted += insertFences(MF, *Graph, CutEdges); 6470946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Inserting LFENCEs... Done\n"); 6480946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Inserted " << FencesInserted << " fences\n"); 6490946e70aSDimitry Andric 650e8d8bef9SDimitry Andric Graph = GraphBuilder::trim(*Graph, NodeSet{*Graph}, CutEdges); 6510946e70aSDimitry Andric } while (true); 6520946e70aSDimitry Andric 6530946e70aSDimitry Andric return FencesInserted; 6540946e70aSDimitry Andric } 6550946e70aSDimitry Andric 656e8d8bef9SDimitry Andric int X86LoadValueInjectionLoadHardeningPass::hardenLoadsWithHeuristic( 6570946e70aSDimitry Andric MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph) const { 658e8d8bef9SDimitry Andric // If `MF` does not have any fences, then no gadgets would have been 659e8d8bef9SDimitry Andric // mitigated at this point. 660e8d8bef9SDimitry Andric if (Graph->NumFences > 0) { 6610946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Eliminating mitigated paths...\n"); 6620946e70aSDimitry Andric Graph = trimMitigatedEdges(std::move(Graph)); 6630946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Eliminating mitigated paths... Done\n"); 664e8d8bef9SDimitry Andric } 665e8d8bef9SDimitry Andric 6660946e70aSDimitry Andric if (Graph->NumGadgets == 0) 6670946e70aSDimitry Andric return 0; 6680946e70aSDimitry Andric 6690946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Cutting edges...\n"); 670e8d8bef9SDimitry Andric EdgeSet CutEdges{*Graph}; 6710946e70aSDimitry Andric 672e8d8bef9SDimitry Andric // Begin by collecting all ingress CFG edges for each node 673e8d8bef9SDimitry Andric DenseMap<const Node *, SmallVector<const Edge *, 2>> IngressEdgeMap; 674e8d8bef9SDimitry Andric for (const Edge &E : Graph->edges()) 675e8d8bef9SDimitry Andric if (MachineGadgetGraph::isCFGEdge(E)) 676e8d8bef9SDimitry Andric IngressEdgeMap[E.getDest()].push_back(&E); 6770946e70aSDimitry Andric 678e8d8bef9SDimitry Andric // For each gadget edge, make cuts that guarantee the gadget will be 679e8d8bef9SDimitry Andric // mitigated. A computationally efficient way to achieve this is to either: 680e8d8bef9SDimitry Andric // (a) cut all egress CFG edges from the gadget source, or 681e8d8bef9SDimitry Andric // (b) cut all ingress CFG edges to the gadget sink. 682e8d8bef9SDimitry Andric // 683e8d8bef9SDimitry Andric // Moreover, the algorithm tries not to make a cut into a loop by preferring 684e8d8bef9SDimitry Andric // to make a (b)-type cut if the gadget source resides at a greater loop depth 685e8d8bef9SDimitry Andric // than the gadget sink, or an (a)-type cut otherwise. 686e8d8bef9SDimitry Andric for (const Node &N : Graph->nodes()) { 687e8d8bef9SDimitry Andric for (const Edge &E : N.edges()) { 688e8d8bef9SDimitry Andric if (!MachineGadgetGraph::isGadgetEdge(E)) 6890946e70aSDimitry Andric continue; 6900946e70aSDimitry Andric 691e8d8bef9SDimitry Andric SmallVector<const Edge *, 2> EgressEdges; 692e8d8bef9SDimitry Andric SmallVector<const Edge *, 2> &IngressEdges = IngressEdgeMap[E.getDest()]; 693e8d8bef9SDimitry Andric for (const Edge &EgressEdge : N.edges()) 694e8d8bef9SDimitry Andric if (MachineGadgetGraph::isCFGEdge(EgressEdge)) 695e8d8bef9SDimitry Andric EgressEdges.push_back(&EgressEdge); 6960946e70aSDimitry Andric 697e8d8bef9SDimitry Andric int EgressCutCost = 0, IngressCutCost = 0; 698e8d8bef9SDimitry Andric for (const Edge *EgressEdge : EgressEdges) 699e8d8bef9SDimitry Andric if (!CutEdges.contains(*EgressEdge)) 700e8d8bef9SDimitry Andric EgressCutCost += EgressEdge->getValue(); 701e8d8bef9SDimitry Andric for (const Edge *IngressEdge : IngressEdges) 702e8d8bef9SDimitry Andric if (!CutEdges.contains(*IngressEdge)) 703e8d8bef9SDimitry Andric IngressCutCost += IngressEdge->getValue(); 704e8d8bef9SDimitry Andric 705e8d8bef9SDimitry Andric auto &EdgesToCut = 706e8d8bef9SDimitry Andric IngressCutCost < EgressCutCost ? IngressEdges : EgressEdges; 707e8d8bef9SDimitry Andric for (const Edge *E : EdgesToCut) 708e8d8bef9SDimitry Andric CutEdges.insert(*E); 709e8d8bef9SDimitry Andric } 710e8d8bef9SDimitry Andric } 7110946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Cutting edges... Done\n"); 7120946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Cut " << CutEdges.count() << " edges\n"); 7130946e70aSDimitry Andric 7140946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Inserting LFENCEs...\n"); 7150946e70aSDimitry Andric int FencesInserted = insertFences(MF, *Graph, CutEdges); 7160946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Inserting LFENCEs... Done\n"); 7170946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Inserted " << FencesInserted << " fences\n"); 7180946e70aSDimitry Andric 7190946e70aSDimitry Andric return FencesInserted; 7200946e70aSDimitry Andric } 7210946e70aSDimitry Andric 7220946e70aSDimitry Andric int X86LoadValueInjectionLoadHardeningPass::insertFences( 7230946e70aSDimitry Andric MachineFunction &MF, MachineGadgetGraph &G, 7240946e70aSDimitry Andric EdgeSet &CutEdges /* in, out */) const { 7250946e70aSDimitry Andric int FencesInserted = 0; 726e8d8bef9SDimitry Andric for (const Node &N : G.nodes()) { 727e8d8bef9SDimitry Andric for (const Edge &E : N.edges()) { 7280946e70aSDimitry Andric if (CutEdges.contains(E)) { 7290946e70aSDimitry Andric MachineInstr *MI = N.getValue(), *Prev; 7300946e70aSDimitry Andric MachineBasicBlock *MBB; // Insert an LFENCE in this MBB 7310946e70aSDimitry Andric MachineBasicBlock::iterator InsertionPt; // ...at this point 7320946e70aSDimitry Andric if (MI == MachineGadgetGraph::ArgNodeSentinel) { 7330946e70aSDimitry Andric // insert LFENCE at beginning of entry block 7340946e70aSDimitry Andric MBB = &MF.front(); 7350946e70aSDimitry Andric InsertionPt = MBB->begin(); 7360946e70aSDimitry Andric Prev = nullptr; 7370946e70aSDimitry Andric } else if (MI->isBranch()) { // insert the LFENCE before the branch 7380946e70aSDimitry Andric MBB = MI->getParent(); 7390946e70aSDimitry Andric InsertionPt = MI; 7400946e70aSDimitry Andric Prev = MI->getPrevNode(); 7410946e70aSDimitry Andric // Remove all egress CFG edges from this branch because the inserted 7420946e70aSDimitry Andric // LFENCE prevents gadgets from crossing the branch. 743e8d8bef9SDimitry Andric for (const Edge &E : N.edges()) { 7440946e70aSDimitry Andric if (MachineGadgetGraph::isCFGEdge(E)) 7450946e70aSDimitry Andric CutEdges.insert(E); 7460946e70aSDimitry Andric } 7470946e70aSDimitry Andric } else { // insert the LFENCE after the instruction 7480946e70aSDimitry Andric MBB = MI->getParent(); 7490946e70aSDimitry Andric InsertionPt = MI->getNextNode() ? MI->getNextNode() : MBB->end(); 7500946e70aSDimitry Andric Prev = InsertionPt == MBB->end() 7510946e70aSDimitry Andric ? (MBB->empty() ? nullptr : &MBB->back()) 7520946e70aSDimitry Andric : InsertionPt->getPrevNode(); 7530946e70aSDimitry Andric } 7540946e70aSDimitry Andric // Ensure this insertion is not redundant (two LFENCEs in sequence). 7550946e70aSDimitry Andric if ((InsertionPt == MBB->end() || !isFence(&*InsertionPt)) && 7560946e70aSDimitry Andric (!Prev || !isFence(Prev))) { 7570946e70aSDimitry Andric BuildMI(*MBB, InsertionPt, DebugLoc(), TII->get(X86::LFENCE)); 7580946e70aSDimitry Andric ++FencesInserted; 7590946e70aSDimitry Andric } 7600946e70aSDimitry Andric } 7610946e70aSDimitry Andric } 7620946e70aSDimitry Andric } 7630946e70aSDimitry Andric return FencesInserted; 7640946e70aSDimitry Andric } 7650946e70aSDimitry Andric 7660946e70aSDimitry Andric bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToAccessMemory( 7670946e70aSDimitry Andric const MachineInstr &MI, unsigned Reg) const { 7680946e70aSDimitry Andric if (!MI.mayLoadOrStore() || MI.getOpcode() == X86::MFENCE || 7690946e70aSDimitry Andric MI.getOpcode() == X86::SFENCE || MI.getOpcode() == X86::LFENCE) 7700946e70aSDimitry Andric return false; 7710946e70aSDimitry Andric 7727a6dacacSDimitry Andric const int MemRefBeginIdx = X86::getFirstAddrOperandIdx(MI); 7730946e70aSDimitry Andric if (MemRefBeginIdx < 0) { 7740946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Warning: unable to obtain memory operand for loading " 7750946e70aSDimitry Andric "instruction:\n"; 7760946e70aSDimitry Andric MI.print(dbgs()); dbgs() << '\n';); 7770946e70aSDimitry Andric return false; 7780946e70aSDimitry Andric } 7790946e70aSDimitry Andric 7800946e70aSDimitry Andric const MachineOperand &BaseMO = 7810946e70aSDimitry Andric MI.getOperand(MemRefBeginIdx + X86::AddrBaseReg); 7820946e70aSDimitry Andric const MachineOperand &IndexMO = 7830946e70aSDimitry Andric MI.getOperand(MemRefBeginIdx + X86::AddrIndexReg); 7840946e70aSDimitry Andric return (BaseMO.isReg() && BaseMO.getReg() != X86::NoRegister && 7850946e70aSDimitry Andric TRI->regsOverlap(BaseMO.getReg(), Reg)) || 7860946e70aSDimitry Andric (IndexMO.isReg() && IndexMO.getReg() != X86::NoRegister && 7870946e70aSDimitry Andric TRI->regsOverlap(IndexMO.getReg(), Reg)); 7880946e70aSDimitry Andric } 7890946e70aSDimitry Andric 7900946e70aSDimitry Andric bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToBranch( 7910946e70aSDimitry Andric const MachineInstr &MI, unsigned Reg) const { 7920946e70aSDimitry Andric if (!MI.isConditionalBranch()) 7930946e70aSDimitry Andric return false; 7940946e70aSDimitry Andric for (const MachineOperand &Use : MI.uses()) 7950946e70aSDimitry Andric if (Use.isReg() && Use.getReg() == Reg) 7960946e70aSDimitry Andric return true; 7970946e70aSDimitry Andric return false; 7980946e70aSDimitry Andric } 7990946e70aSDimitry Andric 8000946e70aSDimitry Andric INITIALIZE_PASS_BEGIN(X86LoadValueInjectionLoadHardeningPass, PASS_KEY, 8010946e70aSDimitry Andric "X86 LVI load hardening", false, false) 802*0fca6ea1SDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineLoopInfoWrapperPass) 803*0fca6ea1SDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineDominatorTreeWrapperPass) 8040946e70aSDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineDominanceFrontier) 8050946e70aSDimitry Andric INITIALIZE_PASS_END(X86LoadValueInjectionLoadHardeningPass, PASS_KEY, 8060946e70aSDimitry Andric "X86 LVI load hardening", false, false) 8070946e70aSDimitry Andric 8080946e70aSDimitry Andric FunctionPass *llvm::createX86LoadValueInjectionLoadHardeningPass() { 8090946e70aSDimitry Andric return new X86LoadValueInjectionLoadHardeningPass(); 8100946e70aSDimitry Andric } 811