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" 440946e70aSDimitry Andric #include "llvm/ADT/DenseSet.h" 45*e8d8bef9SDimitry Andric #include "llvm/ADT/STLExtras.h" 460946e70aSDimitry Andric #include "llvm/ADT/SmallSet.h" 470946e70aSDimitry Andric #include "llvm/ADT/Statistic.h" 480946e70aSDimitry Andric #include "llvm/ADT/StringRef.h" 490946e70aSDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h" 500946e70aSDimitry Andric #include "llvm/CodeGen/MachineDominanceFrontier.h" 510946e70aSDimitry Andric #include "llvm/CodeGen/MachineDominators.h" 520946e70aSDimitry Andric #include "llvm/CodeGen/MachineFunction.h" 530946e70aSDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h" 540946e70aSDimitry Andric #include "llvm/CodeGen/MachineInstr.h" 550946e70aSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 560946e70aSDimitry Andric #include "llvm/CodeGen/MachineLoopInfo.h" 570946e70aSDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 580946e70aSDimitry Andric #include "llvm/CodeGen/RDFGraph.h" 590946e70aSDimitry Andric #include "llvm/CodeGen/RDFLiveness.h" 600946e70aSDimitry Andric #include "llvm/InitializePasses.h" 610946e70aSDimitry Andric #include "llvm/Support/CommandLine.h" 620946e70aSDimitry Andric #include "llvm/Support/DOTGraphTraits.h" 630946e70aSDimitry Andric #include "llvm/Support/Debug.h" 640946e70aSDimitry Andric #include "llvm/Support/DynamicLibrary.h" 650946e70aSDimitry Andric #include "llvm/Support/GraphWriter.h" 660946e70aSDimitry Andric #include "llvm/Support/raw_ostream.h" 670946e70aSDimitry Andric 680946e70aSDimitry Andric using namespace llvm; 690946e70aSDimitry Andric 700946e70aSDimitry Andric #define PASS_KEY "x86-lvi-load" 710946e70aSDimitry Andric #define DEBUG_TYPE PASS_KEY 720946e70aSDimitry Andric 730946e70aSDimitry Andric STATISTIC(NumFences, "Number of LFENCEs inserted for LVI mitigation"); 740946e70aSDimitry Andric STATISTIC(NumFunctionsConsidered, "Number of functions analyzed"); 750946e70aSDimitry Andric STATISTIC(NumFunctionsMitigated, "Number of functions for which mitigations " 760946e70aSDimitry Andric "were deployed"); 770946e70aSDimitry Andric STATISTIC(NumGadgets, "Number of LVI gadgets detected during analysis"); 780946e70aSDimitry Andric 790946e70aSDimitry Andric static cl::opt<std::string> OptimizePluginPath( 800946e70aSDimitry Andric PASS_KEY "-opt-plugin", 810946e70aSDimitry Andric cl::desc("Specify a plugin to optimize LFENCE insertion"), cl::Hidden); 820946e70aSDimitry Andric 830946e70aSDimitry Andric static cl::opt<bool> NoConditionalBranches( 840946e70aSDimitry Andric PASS_KEY "-no-cbranch", 850946e70aSDimitry Andric cl::desc("Don't treat conditional branches as disclosure gadgets. This " 860946e70aSDimitry Andric "may improve performance, at the cost of security."), 870946e70aSDimitry Andric cl::init(false), cl::Hidden); 880946e70aSDimitry Andric 890946e70aSDimitry Andric static cl::opt<bool> EmitDot( 900946e70aSDimitry Andric PASS_KEY "-dot", 910946e70aSDimitry Andric cl::desc( 920946e70aSDimitry Andric "For each function, emit a dot graph depicting potential LVI gadgets"), 930946e70aSDimitry Andric cl::init(false), cl::Hidden); 940946e70aSDimitry Andric 950946e70aSDimitry Andric static cl::opt<bool> EmitDotOnly( 960946e70aSDimitry Andric PASS_KEY "-dot-only", 970946e70aSDimitry Andric cl::desc("For each function, emit a dot graph depicting potential LVI " 980946e70aSDimitry Andric "gadgets, and do not insert any fences"), 990946e70aSDimitry Andric cl::init(false), cl::Hidden); 1000946e70aSDimitry Andric 1010946e70aSDimitry Andric static cl::opt<bool> EmitDotVerify( 1020946e70aSDimitry Andric PASS_KEY "-dot-verify", 1030946e70aSDimitry Andric cl::desc("For each function, emit a dot graph to stdout depicting " 1040946e70aSDimitry Andric "potential LVI gadgets, used for testing purposes only"), 1050946e70aSDimitry Andric cl::init(false), cl::Hidden); 1060946e70aSDimitry Andric 1070946e70aSDimitry Andric static llvm::sys::DynamicLibrary OptimizeDL; 108*e8d8bef9SDimitry Andric typedef int (*OptimizeCutT)(unsigned int *Nodes, unsigned int NodesSize, 109*e8d8bef9SDimitry Andric unsigned int *Edges, int *EdgeValues, 110*e8d8bef9SDimitry Andric int *CutEdges /* out */, unsigned int EdgesSize); 1110946e70aSDimitry Andric static OptimizeCutT OptimizeCut = nullptr; 1120946e70aSDimitry Andric 1130946e70aSDimitry Andric namespace { 1140946e70aSDimitry Andric 1150946e70aSDimitry Andric struct MachineGadgetGraph : ImmutableGraph<MachineInstr *, int> { 1160946e70aSDimitry Andric static constexpr int GadgetEdgeSentinel = -1; 1170946e70aSDimitry Andric static constexpr MachineInstr *const ArgNodeSentinel = nullptr; 1180946e70aSDimitry Andric 1190946e70aSDimitry Andric using GraphT = ImmutableGraph<MachineInstr *, int>; 1200946e70aSDimitry Andric using Node = typename GraphT::Node; 1210946e70aSDimitry Andric using Edge = typename GraphT::Edge; 1220946e70aSDimitry Andric using size_type = typename GraphT::size_type; 1230946e70aSDimitry Andric MachineGadgetGraph(std::unique_ptr<Node[]> Nodes, 1240946e70aSDimitry Andric std::unique_ptr<Edge[]> Edges, size_type NodesSize, 1250946e70aSDimitry Andric size_type EdgesSize, int NumFences = 0, int NumGadgets = 0) 1260946e70aSDimitry Andric : GraphT(std::move(Nodes), std::move(Edges), NodesSize, EdgesSize), 1270946e70aSDimitry Andric NumFences(NumFences), NumGadgets(NumGadgets) {} 1280946e70aSDimitry Andric static inline bool isCFGEdge(const Edge &E) { 1290946e70aSDimitry Andric return E.getValue() != GadgetEdgeSentinel; 1300946e70aSDimitry Andric } 1310946e70aSDimitry Andric static inline bool isGadgetEdge(const Edge &E) { 1320946e70aSDimitry Andric return E.getValue() == GadgetEdgeSentinel; 1330946e70aSDimitry Andric } 1340946e70aSDimitry Andric int NumFences; 1350946e70aSDimitry Andric int NumGadgets; 1360946e70aSDimitry Andric }; 1370946e70aSDimitry Andric 1380946e70aSDimitry Andric class X86LoadValueInjectionLoadHardeningPass : public MachineFunctionPass { 1390946e70aSDimitry Andric public: 1400946e70aSDimitry Andric X86LoadValueInjectionLoadHardeningPass() : MachineFunctionPass(ID) {} 1410946e70aSDimitry Andric 1420946e70aSDimitry Andric StringRef getPassName() const override { 1430946e70aSDimitry Andric return "X86 Load Value Injection (LVI) Load Hardening"; 1440946e70aSDimitry Andric } 1450946e70aSDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override; 1460946e70aSDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 1470946e70aSDimitry Andric 1480946e70aSDimitry Andric static char ID; 1490946e70aSDimitry Andric 1500946e70aSDimitry Andric private: 1510946e70aSDimitry Andric using GraphBuilder = ImmutableGraphBuilder<MachineGadgetGraph>; 152*e8d8bef9SDimitry Andric using Edge = MachineGadgetGraph::Edge; 153*e8d8bef9SDimitry Andric using Node = MachineGadgetGraph::Node; 1540946e70aSDimitry Andric using EdgeSet = MachineGadgetGraph::EdgeSet; 1550946e70aSDimitry Andric using NodeSet = MachineGadgetGraph::NodeSet; 1560946e70aSDimitry Andric 1570946e70aSDimitry Andric const X86Subtarget *STI; 1580946e70aSDimitry Andric const TargetInstrInfo *TII; 1590946e70aSDimitry Andric const TargetRegisterInfo *TRI; 1600946e70aSDimitry Andric 1610946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> 1620946e70aSDimitry Andric getGadgetGraph(MachineFunction &MF, const MachineLoopInfo &MLI, 1630946e70aSDimitry Andric const MachineDominatorTree &MDT, 1640946e70aSDimitry Andric const MachineDominanceFrontier &MDF) const; 1650946e70aSDimitry Andric int hardenLoadsWithPlugin(MachineFunction &MF, 1660946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> Graph) const; 167*e8d8bef9SDimitry Andric int hardenLoadsWithHeuristic(MachineFunction &MF, 168*e8d8bef9SDimitry Andric std::unique_ptr<MachineGadgetGraph> Graph) const; 1690946e70aSDimitry Andric int elimMitigatedEdgesAndNodes(MachineGadgetGraph &G, 1700946e70aSDimitry Andric EdgeSet &ElimEdges /* in, out */, 1710946e70aSDimitry Andric NodeSet &ElimNodes /* in, out */) const; 1720946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> 1730946e70aSDimitry Andric trimMitigatedEdges(std::unique_ptr<MachineGadgetGraph> Graph) const; 1740946e70aSDimitry Andric int insertFences(MachineFunction &MF, MachineGadgetGraph &G, 1750946e70aSDimitry Andric EdgeSet &CutEdges /* in, out */) const; 1760946e70aSDimitry Andric bool instrUsesRegToAccessMemory(const MachineInstr &I, unsigned Reg) const; 1770946e70aSDimitry Andric bool instrUsesRegToBranch(const MachineInstr &I, unsigned Reg) const; 1780946e70aSDimitry Andric inline bool isFence(const MachineInstr *MI) const { 1790946e70aSDimitry Andric return MI && (MI->getOpcode() == X86::LFENCE || 1800946e70aSDimitry Andric (STI->useLVIControlFlowIntegrity() && MI->isCall())); 1810946e70aSDimitry Andric } 1820946e70aSDimitry Andric }; 1830946e70aSDimitry Andric 1840946e70aSDimitry Andric } // end anonymous namespace 1850946e70aSDimitry Andric 1860946e70aSDimitry Andric namespace llvm { 1870946e70aSDimitry Andric 1880946e70aSDimitry Andric template <> 1890946e70aSDimitry Andric struct GraphTraits<MachineGadgetGraph *> 1900946e70aSDimitry Andric : GraphTraits<ImmutableGraph<MachineInstr *, int> *> {}; 1910946e70aSDimitry Andric 1920946e70aSDimitry Andric template <> 1930946e70aSDimitry Andric struct DOTGraphTraits<MachineGadgetGraph *> : DefaultDOTGraphTraits { 1940946e70aSDimitry Andric using GraphType = MachineGadgetGraph; 1950946e70aSDimitry Andric using Traits = llvm::GraphTraits<GraphType *>; 1960946e70aSDimitry Andric using NodeRef = typename Traits::NodeRef; 1970946e70aSDimitry Andric using EdgeRef = typename Traits::EdgeRef; 1980946e70aSDimitry Andric using ChildIteratorType = typename Traits::ChildIteratorType; 1990946e70aSDimitry Andric using ChildEdgeIteratorType = typename Traits::ChildEdgeIteratorType; 2000946e70aSDimitry Andric 201*e8d8bef9SDimitry Andric DOTGraphTraits(bool IsSimple = false) : DefaultDOTGraphTraits(IsSimple) {} 2020946e70aSDimitry Andric 2030946e70aSDimitry Andric std::string getNodeLabel(NodeRef Node, GraphType *) { 2040946e70aSDimitry Andric if (Node->getValue() == MachineGadgetGraph::ArgNodeSentinel) 2050946e70aSDimitry Andric return "ARGS"; 2060946e70aSDimitry Andric 2070946e70aSDimitry Andric std::string Str; 2080946e70aSDimitry Andric raw_string_ostream OS(Str); 2090946e70aSDimitry Andric OS << *Node->getValue(); 2100946e70aSDimitry Andric return OS.str(); 2110946e70aSDimitry Andric } 2120946e70aSDimitry Andric 2130946e70aSDimitry Andric static std::string getNodeAttributes(NodeRef Node, GraphType *) { 2140946e70aSDimitry Andric MachineInstr *MI = Node->getValue(); 2150946e70aSDimitry Andric if (MI == MachineGadgetGraph::ArgNodeSentinel) 2160946e70aSDimitry Andric return "color = blue"; 2170946e70aSDimitry Andric if (MI->getOpcode() == X86::LFENCE) 2180946e70aSDimitry Andric return "color = green"; 2190946e70aSDimitry Andric return ""; 2200946e70aSDimitry Andric } 2210946e70aSDimitry Andric 2220946e70aSDimitry Andric static std::string getEdgeAttributes(NodeRef, ChildIteratorType E, 2230946e70aSDimitry Andric GraphType *) { 2240946e70aSDimitry Andric int EdgeVal = (*E.getCurrent()).getValue(); 2250946e70aSDimitry Andric return EdgeVal >= 0 ? "label = " + std::to_string(EdgeVal) 2260946e70aSDimitry Andric : "color = red, style = \"dashed\""; 2270946e70aSDimitry Andric } 2280946e70aSDimitry Andric }; 2290946e70aSDimitry Andric 2300946e70aSDimitry Andric } // end namespace llvm 2310946e70aSDimitry Andric 2320946e70aSDimitry Andric constexpr MachineInstr *MachineGadgetGraph::ArgNodeSentinel; 2330946e70aSDimitry Andric constexpr int MachineGadgetGraph::GadgetEdgeSentinel; 2340946e70aSDimitry Andric 2350946e70aSDimitry Andric char X86LoadValueInjectionLoadHardeningPass::ID = 0; 2360946e70aSDimitry Andric 2370946e70aSDimitry Andric void X86LoadValueInjectionLoadHardeningPass::getAnalysisUsage( 2380946e70aSDimitry Andric AnalysisUsage &AU) const { 2390946e70aSDimitry Andric MachineFunctionPass::getAnalysisUsage(AU); 2400946e70aSDimitry Andric AU.addRequired<MachineLoopInfo>(); 2410946e70aSDimitry Andric AU.addRequired<MachineDominatorTree>(); 2420946e70aSDimitry Andric AU.addRequired<MachineDominanceFrontier>(); 2430946e70aSDimitry Andric AU.setPreservesCFG(); 2440946e70aSDimitry Andric } 2450946e70aSDimitry Andric 246*e8d8bef9SDimitry Andric static void writeGadgetGraph(raw_ostream &OS, MachineFunction &MF, 2470946e70aSDimitry Andric MachineGadgetGraph *G) { 2480946e70aSDimitry Andric WriteGraph(OS, G, /*ShortNames*/ false, 2490946e70aSDimitry Andric "Speculative gadgets for \"" + MF.getName() + "\" function"); 2500946e70aSDimitry Andric } 2510946e70aSDimitry Andric 2520946e70aSDimitry Andric bool X86LoadValueInjectionLoadHardeningPass::runOnMachineFunction( 2530946e70aSDimitry Andric MachineFunction &MF) { 2540946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "***** " << getPassName() << " : " << MF.getName() 2550946e70aSDimitry Andric << " *****\n"); 2560946e70aSDimitry Andric STI = &MF.getSubtarget<X86Subtarget>(); 2570946e70aSDimitry Andric if (!STI->useLVILoadHardening()) 2580946e70aSDimitry Andric return false; 2590946e70aSDimitry Andric 2600946e70aSDimitry Andric // FIXME: support 32-bit 2610946e70aSDimitry Andric if (!STI->is64Bit()) 2620946e70aSDimitry Andric report_fatal_error("LVI load hardening is only supported on 64-bit", false); 2630946e70aSDimitry Andric 2640946e70aSDimitry Andric // Don't skip functions with the "optnone" attr but participate in opt-bisect. 2650946e70aSDimitry Andric const Function &F = MF.getFunction(); 2660946e70aSDimitry Andric if (!F.hasOptNone() && skipFunction(F)) 2670946e70aSDimitry Andric return false; 2680946e70aSDimitry Andric 2690946e70aSDimitry Andric ++NumFunctionsConsidered; 2700946e70aSDimitry Andric TII = STI->getInstrInfo(); 2710946e70aSDimitry Andric TRI = STI->getRegisterInfo(); 2720946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Building gadget graph...\n"); 2730946e70aSDimitry Andric const auto &MLI = getAnalysis<MachineLoopInfo>(); 2740946e70aSDimitry Andric const auto &MDT = getAnalysis<MachineDominatorTree>(); 2750946e70aSDimitry Andric const auto &MDF = getAnalysis<MachineDominanceFrontier>(); 2760946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> Graph = getGadgetGraph(MF, MLI, MDT, MDF); 2770946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Building gadget graph... Done\n"); 2780946e70aSDimitry Andric if (Graph == nullptr) 2790946e70aSDimitry Andric return false; // didn't find any gadgets 2800946e70aSDimitry Andric 2810946e70aSDimitry Andric if (EmitDotVerify) { 282*e8d8bef9SDimitry Andric writeGadgetGraph(outs(), MF, Graph.get()); 2830946e70aSDimitry Andric return false; 2840946e70aSDimitry Andric } 2850946e70aSDimitry Andric 2860946e70aSDimitry Andric if (EmitDot || EmitDotOnly) { 2870946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Emitting gadget graph...\n"); 2880946e70aSDimitry Andric std::error_code FileError; 2890946e70aSDimitry Andric std::string FileName = "lvi."; 2900946e70aSDimitry Andric FileName += MF.getName(); 2910946e70aSDimitry Andric FileName += ".dot"; 2920946e70aSDimitry Andric raw_fd_ostream FileOut(FileName, FileError); 2930946e70aSDimitry Andric if (FileError) 2940946e70aSDimitry Andric errs() << FileError.message(); 295*e8d8bef9SDimitry Andric writeGadgetGraph(FileOut, MF, Graph.get()); 2960946e70aSDimitry Andric FileOut.close(); 2970946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Emitting gadget graph... Done\n"); 2980946e70aSDimitry Andric if (EmitDotOnly) 2990946e70aSDimitry Andric return false; 3000946e70aSDimitry Andric } 3010946e70aSDimitry Andric 3020946e70aSDimitry Andric int FencesInserted; 3030946e70aSDimitry Andric if (!OptimizePluginPath.empty()) { 3040946e70aSDimitry Andric if (!OptimizeDL.isValid()) { 3050946e70aSDimitry Andric std::string ErrorMsg; 3060946e70aSDimitry Andric OptimizeDL = llvm::sys::DynamicLibrary::getPermanentLibrary( 3070946e70aSDimitry Andric OptimizePluginPath.c_str(), &ErrorMsg); 3080946e70aSDimitry Andric if (!ErrorMsg.empty()) 3090946e70aSDimitry Andric report_fatal_error("Failed to load opt plugin: \"" + ErrorMsg + '\"'); 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 316*e8d8bef9SDimitry 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 3330946e70aSDimitry Andric TargetOperandInfo TOI{*TII}; 3340946e70aSDimitry Andric DataFlowGraph DFG{MF, *TII, *TRI, MDT, MDF, TOI}; 3350946e70aSDimitry Andric DFG.build(); 3360946e70aSDimitry Andric Liveness L{MF.getRegInfo(), DFG}; 3370946e70aSDimitry Andric L.computePhiInfo(); 3380946e70aSDimitry Andric 3390946e70aSDimitry Andric GraphBuilder Builder; 3400946e70aSDimitry Andric using GraphIter = typename GraphBuilder::BuilderNodeRef; 3410946e70aSDimitry Andric DenseMap<MachineInstr *, GraphIter> NodeMap; 3420946e70aSDimitry Andric int FenceCount = 0, GadgetCount = 0; 3430946e70aSDimitry Andric auto MaybeAddNode = [&NodeMap, &Builder](MachineInstr *MI) { 3440946e70aSDimitry Andric auto Ref = NodeMap.find(MI); 3450946e70aSDimitry Andric if (Ref == NodeMap.end()) { 3460946e70aSDimitry Andric auto I = Builder.addVertex(MI); 3470946e70aSDimitry Andric NodeMap[MI] = I; 3480946e70aSDimitry Andric return std::pair<GraphIter, bool>{I, true}; 3490946e70aSDimitry Andric } 3500946e70aSDimitry Andric return std::pair<GraphIter, bool>{Ref->getSecond(), false}; 3510946e70aSDimitry Andric }; 3520946e70aSDimitry Andric 3530946e70aSDimitry Andric // The `Transmitters` map memoizes transmitters found for each def. If a def 3540946e70aSDimitry Andric // has not yet been analyzed, then it will not appear in the map. If a def 3550946e70aSDimitry Andric // has been analyzed and was determined not to have any transmitters, then 3560946e70aSDimitry Andric // its list of transmitters will be empty. 3570946e70aSDimitry Andric DenseMap<NodeId, std::vector<NodeId>> Transmitters; 3580946e70aSDimitry Andric 3590946e70aSDimitry Andric // Analyze all machine instructions to find gadgets and LFENCEs, adding 3600946e70aSDimitry Andric // each interesting value to `Nodes` 3610946e70aSDimitry Andric auto AnalyzeDef = [&](NodeAddr<DefNode *> SourceDef) { 3620946e70aSDimitry Andric SmallSet<NodeId, 8> UsesVisited, DefsVisited; 3630946e70aSDimitry Andric std::function<void(NodeAddr<DefNode *>)> AnalyzeDefUseChain = 3640946e70aSDimitry Andric [&](NodeAddr<DefNode *> Def) { 3650946e70aSDimitry Andric if (Transmitters.find(Def.Id) != Transmitters.end()) 3660946e70aSDimitry Andric return; // Already analyzed `Def` 3670946e70aSDimitry Andric 3680946e70aSDimitry Andric // Use RDF to find all the uses of `Def` 3690946e70aSDimitry Andric rdf::NodeSet Uses; 370*e8d8bef9SDimitry Andric RegisterRef DefReg = Def.Addr->getRegRef(DFG); 3710946e70aSDimitry Andric for (auto UseID : L.getAllReachedUses(DefReg, Def)) { 3720946e70aSDimitry Andric auto Use = DFG.addr<UseNode *>(UseID); 3730946e70aSDimitry Andric if (Use.Addr->getFlags() & NodeAttrs::PhiRef) { // phi node 3740946e70aSDimitry Andric NodeAddr<PhiNode *> Phi = Use.Addr->getOwner(DFG); 3750946e70aSDimitry Andric for (auto I : L.getRealUses(Phi.Id)) { 3760946e70aSDimitry Andric if (DFG.getPRI().alias(RegisterRef(I.first), DefReg)) { 3770946e70aSDimitry Andric for (auto UA : I.second) 3780946e70aSDimitry Andric Uses.emplace(UA.first); 3790946e70aSDimitry Andric } 3800946e70aSDimitry Andric } 3810946e70aSDimitry Andric } else { // not a phi node 3820946e70aSDimitry Andric Uses.emplace(UseID); 3830946e70aSDimitry Andric } 3840946e70aSDimitry Andric } 3850946e70aSDimitry Andric 3860946e70aSDimitry Andric // For each use of `Def`, we want to know whether: 3870946e70aSDimitry Andric // (1) The use can leak the Def'ed value, 3880946e70aSDimitry Andric // (2) The use can further propagate the Def'ed value to more defs 3890946e70aSDimitry Andric for (auto UseID : Uses) { 3900946e70aSDimitry Andric if (!UsesVisited.insert(UseID).second) 3910946e70aSDimitry Andric continue; // Already visited this use of `Def` 3920946e70aSDimitry Andric 3930946e70aSDimitry Andric auto Use = DFG.addr<UseNode *>(UseID); 3940946e70aSDimitry Andric assert(!(Use.Addr->getFlags() & NodeAttrs::PhiRef)); 3950946e70aSDimitry Andric MachineOperand &UseMO = Use.Addr->getOp(); 3960946e70aSDimitry Andric MachineInstr &UseMI = *UseMO.getParent(); 3970946e70aSDimitry Andric assert(UseMO.isReg()); 3980946e70aSDimitry Andric 3990946e70aSDimitry Andric // We naively assume that an instruction propagates any loaded 4000946e70aSDimitry Andric // uses to all defs unless the instruction is a call, in which 4010946e70aSDimitry Andric // case all arguments will be treated as gadget sources during 4020946e70aSDimitry Andric // analysis of the callee function. 4030946e70aSDimitry Andric if (UseMI.isCall()) 4040946e70aSDimitry Andric continue; 4050946e70aSDimitry Andric 4060946e70aSDimitry Andric // Check whether this use can transmit (leak) its value. 4070946e70aSDimitry Andric if (instrUsesRegToAccessMemory(UseMI, UseMO.getReg()) || 4080946e70aSDimitry Andric (!NoConditionalBranches && 4090946e70aSDimitry Andric instrUsesRegToBranch(UseMI, UseMO.getReg()))) { 4100946e70aSDimitry Andric Transmitters[Def.Id].push_back(Use.Addr->getOwner(DFG).Id); 4110946e70aSDimitry Andric if (UseMI.mayLoad()) 4120946e70aSDimitry Andric continue; // Found a transmitting load -- no need to continue 4130946e70aSDimitry Andric // traversing its defs (i.e., this load will become 4140946e70aSDimitry Andric // a new gadget source anyways). 4150946e70aSDimitry Andric } 4160946e70aSDimitry Andric 4170946e70aSDimitry Andric // Check whether the use propagates to more defs. 4180946e70aSDimitry Andric NodeAddr<InstrNode *> Owner{Use.Addr->getOwner(DFG)}; 4190946e70aSDimitry Andric rdf::NodeList AnalyzedChildDefs; 4200946e70aSDimitry Andric for (auto &ChildDef : 4210946e70aSDimitry Andric Owner.Addr->members_if(DataFlowGraph::IsDef, DFG)) { 4220946e70aSDimitry Andric if (!DefsVisited.insert(ChildDef.Id).second) 4230946e70aSDimitry Andric continue; // Already visited this def 4240946e70aSDimitry Andric if (Def.Addr->getAttrs() & NodeAttrs::Dead) 4250946e70aSDimitry Andric continue; 4260946e70aSDimitry Andric if (Def.Id == ChildDef.Id) 4270946e70aSDimitry Andric continue; // `Def` uses itself (e.g., increment loop counter) 4280946e70aSDimitry Andric 4290946e70aSDimitry Andric AnalyzeDefUseChain(ChildDef); 4300946e70aSDimitry Andric 4310946e70aSDimitry Andric // `Def` inherits all of its child defs' transmitters. 4320946e70aSDimitry Andric for (auto TransmitterId : Transmitters[ChildDef.Id]) 4330946e70aSDimitry Andric Transmitters[Def.Id].push_back(TransmitterId); 4340946e70aSDimitry Andric } 4350946e70aSDimitry Andric } 4360946e70aSDimitry Andric 4370946e70aSDimitry Andric // Note that this statement adds `Def.Id` to the map if no 4380946e70aSDimitry Andric // transmitters were found for `Def`. 4390946e70aSDimitry Andric auto &DefTransmitters = Transmitters[Def.Id]; 4400946e70aSDimitry Andric 4410946e70aSDimitry Andric // Remove duplicate transmitters 4420946e70aSDimitry Andric llvm::sort(DefTransmitters); 4430946e70aSDimitry Andric DefTransmitters.erase( 4440946e70aSDimitry Andric std::unique(DefTransmitters.begin(), DefTransmitters.end()), 4450946e70aSDimitry Andric DefTransmitters.end()); 4460946e70aSDimitry Andric }; 4470946e70aSDimitry Andric 4480946e70aSDimitry Andric // Find all of the transmitters 4490946e70aSDimitry Andric AnalyzeDefUseChain(SourceDef); 4500946e70aSDimitry Andric auto &SourceDefTransmitters = Transmitters[SourceDef.Id]; 4510946e70aSDimitry Andric if (SourceDefTransmitters.empty()) 4520946e70aSDimitry Andric return; // No transmitters for `SourceDef` 4530946e70aSDimitry Andric 4540946e70aSDimitry Andric MachineInstr *Source = SourceDef.Addr->getFlags() & NodeAttrs::PhiRef 4550946e70aSDimitry Andric ? MachineGadgetGraph::ArgNodeSentinel 4560946e70aSDimitry Andric : SourceDef.Addr->getOp().getParent(); 4570946e70aSDimitry Andric auto GadgetSource = MaybeAddNode(Source); 4580946e70aSDimitry Andric // Each transmitter is a sink for `SourceDef`. 4590946e70aSDimitry Andric for (auto TransmitterId : SourceDefTransmitters) { 4600946e70aSDimitry Andric MachineInstr *Sink = DFG.addr<StmtNode *>(TransmitterId).Addr->getCode(); 4610946e70aSDimitry Andric auto GadgetSink = MaybeAddNode(Sink); 4620946e70aSDimitry Andric // Add the gadget edge to the graph. 4630946e70aSDimitry Andric Builder.addEdge(MachineGadgetGraph::GadgetEdgeSentinel, 4640946e70aSDimitry Andric GadgetSource.first, GadgetSink.first); 4650946e70aSDimitry Andric ++GadgetCount; 4660946e70aSDimitry Andric } 4670946e70aSDimitry Andric }; 4680946e70aSDimitry Andric 4690946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Analyzing def-use chains to find gadgets\n"); 4700946e70aSDimitry Andric // Analyze function arguments 4710946e70aSDimitry Andric NodeAddr<BlockNode *> EntryBlock = DFG.getFunc().Addr->getEntryBlock(DFG); 4720946e70aSDimitry Andric for (NodeAddr<PhiNode *> ArgPhi : 4730946e70aSDimitry Andric EntryBlock.Addr->members_if(DataFlowGraph::IsPhi, DFG)) { 4740946e70aSDimitry Andric NodeList Defs = ArgPhi.Addr->members_if(DataFlowGraph::IsDef, DFG); 4750946e70aSDimitry Andric llvm::for_each(Defs, AnalyzeDef); 4760946e70aSDimitry Andric } 4770946e70aSDimitry Andric // Analyze every instruction in MF 4780946e70aSDimitry Andric for (NodeAddr<BlockNode *> BA : DFG.getFunc().Addr->members(DFG)) { 4790946e70aSDimitry Andric for (NodeAddr<StmtNode *> SA : 4800946e70aSDimitry Andric BA.Addr->members_if(DataFlowGraph::IsCode<NodeAttrs::Stmt>, DFG)) { 4810946e70aSDimitry Andric MachineInstr *MI = SA.Addr->getCode(); 4820946e70aSDimitry Andric if (isFence(MI)) { 4830946e70aSDimitry Andric MaybeAddNode(MI); 4840946e70aSDimitry Andric ++FenceCount; 4850946e70aSDimitry Andric } else if (MI->mayLoad()) { 4860946e70aSDimitry Andric NodeList Defs = SA.Addr->members_if(DataFlowGraph::IsDef, DFG); 4870946e70aSDimitry Andric llvm::for_each(Defs, AnalyzeDef); 4880946e70aSDimitry Andric } 4890946e70aSDimitry Andric } 4900946e70aSDimitry Andric } 4910946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Found " << FenceCount << " fences\n"); 4920946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Found " << GadgetCount << " gadgets\n"); 4930946e70aSDimitry Andric if (GadgetCount == 0) 4940946e70aSDimitry Andric return nullptr; 4950946e70aSDimitry Andric NumGadgets += GadgetCount; 4960946e70aSDimitry Andric 4970946e70aSDimitry Andric // Traverse CFG to build the rest of the graph 4980946e70aSDimitry Andric SmallSet<MachineBasicBlock *, 8> BlocksVisited; 4990946e70aSDimitry Andric std::function<void(MachineBasicBlock *, GraphIter, unsigned)> TraverseCFG = 5000946e70aSDimitry Andric [&](MachineBasicBlock *MBB, GraphIter GI, unsigned ParentDepth) { 5010946e70aSDimitry Andric unsigned LoopDepth = MLI.getLoopDepth(MBB); 5020946e70aSDimitry Andric if (!MBB->empty()) { 5030946e70aSDimitry Andric // Always add the first instruction in each block 5040946e70aSDimitry Andric auto NI = MBB->begin(); 5050946e70aSDimitry Andric auto BeginBB = MaybeAddNode(&*NI); 5060946e70aSDimitry Andric Builder.addEdge(ParentDepth, GI, BeginBB.first); 5070946e70aSDimitry Andric if (!BlocksVisited.insert(MBB).second) 5080946e70aSDimitry Andric return; 5090946e70aSDimitry Andric 5100946e70aSDimitry Andric // Add any instructions within the block that are gadget components 5110946e70aSDimitry Andric GI = BeginBB.first; 5120946e70aSDimitry Andric while (++NI != MBB->end()) { 5130946e70aSDimitry Andric auto Ref = NodeMap.find(&*NI); 5140946e70aSDimitry Andric if (Ref != NodeMap.end()) { 5150946e70aSDimitry Andric Builder.addEdge(LoopDepth, GI, Ref->getSecond()); 5160946e70aSDimitry Andric GI = Ref->getSecond(); 5170946e70aSDimitry Andric } 5180946e70aSDimitry Andric } 5190946e70aSDimitry Andric 5200946e70aSDimitry Andric // Always add the terminator instruction, if one exists 5210946e70aSDimitry Andric auto T = MBB->getFirstTerminator(); 5220946e70aSDimitry Andric if (T != MBB->end()) { 5230946e70aSDimitry Andric auto EndBB = MaybeAddNode(&*T); 5240946e70aSDimitry Andric if (EndBB.second) 5250946e70aSDimitry Andric Builder.addEdge(LoopDepth, GI, EndBB.first); 5260946e70aSDimitry Andric GI = EndBB.first; 5270946e70aSDimitry Andric } 5280946e70aSDimitry Andric } 5290946e70aSDimitry Andric for (MachineBasicBlock *Succ : MBB->successors()) 5300946e70aSDimitry Andric TraverseCFG(Succ, GI, LoopDepth); 5310946e70aSDimitry Andric }; 5320946e70aSDimitry Andric // ArgNodeSentinel is a pseudo-instruction that represents MF args in the 5330946e70aSDimitry Andric // GadgetGraph 5340946e70aSDimitry Andric GraphIter ArgNode = MaybeAddNode(MachineGadgetGraph::ArgNodeSentinel).first; 5350946e70aSDimitry Andric TraverseCFG(&MF.front(), ArgNode, 0); 5360946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> G{Builder.get(FenceCount, GadgetCount)}; 5370946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Found " << G->nodes_size() << " nodes\n"); 5380946e70aSDimitry Andric return G; 5390946e70aSDimitry Andric } 5400946e70aSDimitry Andric 5410946e70aSDimitry Andric // Returns the number of remaining gadget edges that could not be eliminated 5420946e70aSDimitry Andric int X86LoadValueInjectionLoadHardeningPass::elimMitigatedEdgesAndNodes( 543*e8d8bef9SDimitry Andric MachineGadgetGraph &G, EdgeSet &ElimEdges /* in, out */, 544*e8d8bef9SDimitry Andric NodeSet &ElimNodes /* in, out */) const { 5450946e70aSDimitry Andric if (G.NumFences > 0) { 5460946e70aSDimitry Andric // Eliminate fences and CFG edges that ingress and egress the fence, as 5470946e70aSDimitry Andric // they are trivially mitigated. 548*e8d8bef9SDimitry Andric for (const Edge &E : G.edges()) { 549*e8d8bef9SDimitry Andric const Node *Dest = E.getDest(); 5500946e70aSDimitry Andric if (isFence(Dest->getValue())) { 5510946e70aSDimitry Andric ElimNodes.insert(*Dest); 5520946e70aSDimitry Andric ElimEdges.insert(E); 553*e8d8bef9SDimitry Andric for (const Edge &DE : Dest->edges()) 5540946e70aSDimitry Andric ElimEdges.insert(DE); 5550946e70aSDimitry Andric } 5560946e70aSDimitry Andric } 5570946e70aSDimitry Andric } 5580946e70aSDimitry Andric 5590946e70aSDimitry Andric // Find and eliminate gadget edges that have been mitigated. 5600946e70aSDimitry Andric int MitigatedGadgets = 0, RemainingGadgets = 0; 561*e8d8bef9SDimitry Andric NodeSet ReachableNodes{G}; 562*e8d8bef9SDimitry Andric for (const Node &RootN : G.nodes()) { 5630946e70aSDimitry Andric if (llvm::none_of(RootN.edges(), MachineGadgetGraph::isGadgetEdge)) 5640946e70aSDimitry Andric continue; // skip this node if it isn't a gadget source 5650946e70aSDimitry Andric 5660946e70aSDimitry Andric // Find all of the nodes that are CFG-reachable from RootN using DFS 5670946e70aSDimitry Andric ReachableNodes.clear(); 568*e8d8bef9SDimitry Andric std::function<void(const Node *, bool)> FindReachableNodes = 569*e8d8bef9SDimitry Andric [&](const Node *N, bool FirstNode) { 5700946e70aSDimitry Andric if (!FirstNode) 5710946e70aSDimitry Andric ReachableNodes.insert(*N); 572*e8d8bef9SDimitry Andric for (const Edge &E : N->edges()) { 573*e8d8bef9SDimitry Andric const Node *Dest = E.getDest(); 574*e8d8bef9SDimitry Andric if (MachineGadgetGraph::isCFGEdge(E) && !ElimEdges.contains(E) && 575*e8d8bef9SDimitry Andric !ReachableNodes.contains(*Dest)) 5760946e70aSDimitry Andric FindReachableNodes(Dest, false); 5770946e70aSDimitry Andric } 5780946e70aSDimitry Andric }; 5790946e70aSDimitry Andric FindReachableNodes(&RootN, true); 5800946e70aSDimitry Andric 5810946e70aSDimitry Andric // Any gadget whose sink is unreachable has been mitigated 582*e8d8bef9SDimitry Andric for (const Edge &E : RootN.edges()) { 5830946e70aSDimitry Andric if (MachineGadgetGraph::isGadgetEdge(E)) { 5840946e70aSDimitry Andric if (ReachableNodes.contains(*E.getDest())) { 5850946e70aSDimitry Andric // This gadget's sink is reachable 5860946e70aSDimitry Andric ++RemainingGadgets; 5870946e70aSDimitry Andric } else { // This gadget's sink is unreachable, and therefore mitigated 5880946e70aSDimitry Andric ++MitigatedGadgets; 5890946e70aSDimitry Andric ElimEdges.insert(E); 5900946e70aSDimitry Andric } 5910946e70aSDimitry Andric } 5920946e70aSDimitry Andric } 5930946e70aSDimitry Andric } 5940946e70aSDimitry Andric return RemainingGadgets; 5950946e70aSDimitry Andric } 5960946e70aSDimitry Andric 5970946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> 5980946e70aSDimitry Andric X86LoadValueInjectionLoadHardeningPass::trimMitigatedEdges( 5990946e70aSDimitry Andric std::unique_ptr<MachineGadgetGraph> Graph) const { 600*e8d8bef9SDimitry Andric NodeSet ElimNodes{*Graph}; 601*e8d8bef9SDimitry Andric EdgeSet ElimEdges{*Graph}; 6020946e70aSDimitry Andric int RemainingGadgets = 6030946e70aSDimitry Andric elimMitigatedEdgesAndNodes(*Graph, ElimEdges, ElimNodes); 6040946e70aSDimitry Andric if (ElimEdges.empty() && ElimNodes.empty()) { 6050946e70aSDimitry Andric Graph->NumFences = 0; 6060946e70aSDimitry Andric Graph->NumGadgets = RemainingGadgets; 6070946e70aSDimitry Andric } else { 6080946e70aSDimitry Andric Graph = GraphBuilder::trim(*Graph, ElimNodes, ElimEdges, 0 /* NumFences */, 6090946e70aSDimitry Andric RemainingGadgets); 6100946e70aSDimitry Andric } 6110946e70aSDimitry Andric return Graph; 6120946e70aSDimitry Andric } 6130946e70aSDimitry Andric 6140946e70aSDimitry Andric int X86LoadValueInjectionLoadHardeningPass::hardenLoadsWithPlugin( 6150946e70aSDimitry Andric MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph) const { 6160946e70aSDimitry Andric int FencesInserted = 0; 6170946e70aSDimitry Andric 6180946e70aSDimitry Andric do { 6190946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Eliminating mitigated paths...\n"); 6200946e70aSDimitry Andric Graph = trimMitigatedEdges(std::move(Graph)); 6210946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Eliminating mitigated paths... Done\n"); 6220946e70aSDimitry Andric if (Graph->NumGadgets == 0) 6230946e70aSDimitry Andric break; 6240946e70aSDimitry Andric 6250946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Cutting edges...\n"); 6260946e70aSDimitry Andric EdgeSet CutEdges{*Graph}; 6270946e70aSDimitry Andric auto Nodes = std::make_unique<unsigned int[]>(Graph->nodes_size() + 6280946e70aSDimitry Andric 1 /* terminator node */); 6290946e70aSDimitry Andric auto Edges = std::make_unique<unsigned int[]>(Graph->edges_size()); 6300946e70aSDimitry Andric auto EdgeCuts = std::make_unique<int[]>(Graph->edges_size()); 6310946e70aSDimitry Andric auto EdgeValues = std::make_unique<int[]>(Graph->edges_size()); 632*e8d8bef9SDimitry Andric for (const Node &N : Graph->nodes()) { 6330946e70aSDimitry Andric Nodes[Graph->getNodeIndex(N)] = Graph->getEdgeIndex(*N.edges_begin()); 6340946e70aSDimitry Andric } 6350946e70aSDimitry Andric Nodes[Graph->nodes_size()] = Graph->edges_size(); // terminator node 636*e8d8bef9SDimitry Andric for (const Edge &E : Graph->edges()) { 6370946e70aSDimitry Andric Edges[Graph->getEdgeIndex(E)] = Graph->getNodeIndex(*E.getDest()); 6380946e70aSDimitry Andric EdgeValues[Graph->getEdgeIndex(E)] = E.getValue(); 6390946e70aSDimitry Andric } 6400946e70aSDimitry Andric OptimizeCut(Nodes.get(), Graph->nodes_size(), Edges.get(), EdgeValues.get(), 6410946e70aSDimitry Andric EdgeCuts.get(), Graph->edges_size()); 6420946e70aSDimitry Andric for (int I = 0; I < Graph->edges_size(); ++I) 6430946e70aSDimitry Andric if (EdgeCuts[I]) 6440946e70aSDimitry Andric CutEdges.set(I); 6450946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Cutting edges... Done\n"); 6460946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Cut " << CutEdges.count() << " edges\n"); 6470946e70aSDimitry Andric 6480946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Inserting LFENCEs...\n"); 6490946e70aSDimitry Andric FencesInserted += insertFences(MF, *Graph, CutEdges); 6500946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Inserting LFENCEs... Done\n"); 6510946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Inserted " << FencesInserted << " fences\n"); 6520946e70aSDimitry Andric 653*e8d8bef9SDimitry Andric Graph = GraphBuilder::trim(*Graph, NodeSet{*Graph}, CutEdges); 6540946e70aSDimitry Andric } while (true); 6550946e70aSDimitry Andric 6560946e70aSDimitry Andric return FencesInserted; 6570946e70aSDimitry Andric } 6580946e70aSDimitry Andric 659*e8d8bef9SDimitry Andric int X86LoadValueInjectionLoadHardeningPass::hardenLoadsWithHeuristic( 6600946e70aSDimitry Andric MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph) const { 661*e8d8bef9SDimitry Andric // If `MF` does not have any fences, then no gadgets would have been 662*e8d8bef9SDimitry Andric // mitigated at this point. 663*e8d8bef9SDimitry Andric if (Graph->NumFences > 0) { 6640946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Eliminating mitigated paths...\n"); 6650946e70aSDimitry Andric Graph = trimMitigatedEdges(std::move(Graph)); 6660946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Eliminating mitigated paths... Done\n"); 667*e8d8bef9SDimitry Andric } 668*e8d8bef9SDimitry Andric 6690946e70aSDimitry Andric if (Graph->NumGadgets == 0) 6700946e70aSDimitry Andric return 0; 6710946e70aSDimitry Andric 6720946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Cutting edges...\n"); 673*e8d8bef9SDimitry Andric EdgeSet CutEdges{*Graph}; 6740946e70aSDimitry Andric 675*e8d8bef9SDimitry Andric // Begin by collecting all ingress CFG edges for each node 676*e8d8bef9SDimitry Andric DenseMap<const Node *, SmallVector<const Edge *, 2>> IngressEdgeMap; 677*e8d8bef9SDimitry Andric for (const Edge &E : Graph->edges()) 678*e8d8bef9SDimitry Andric if (MachineGadgetGraph::isCFGEdge(E)) 679*e8d8bef9SDimitry Andric IngressEdgeMap[E.getDest()].push_back(&E); 6800946e70aSDimitry Andric 681*e8d8bef9SDimitry Andric // For each gadget edge, make cuts that guarantee the gadget will be 682*e8d8bef9SDimitry Andric // mitigated. A computationally efficient way to achieve this is to either: 683*e8d8bef9SDimitry Andric // (a) cut all egress CFG edges from the gadget source, or 684*e8d8bef9SDimitry Andric // (b) cut all ingress CFG edges to the gadget sink. 685*e8d8bef9SDimitry Andric // 686*e8d8bef9SDimitry Andric // Moreover, the algorithm tries not to make a cut into a loop by preferring 687*e8d8bef9SDimitry Andric // to make a (b)-type cut if the gadget source resides at a greater loop depth 688*e8d8bef9SDimitry Andric // than the gadget sink, or an (a)-type cut otherwise. 689*e8d8bef9SDimitry Andric for (const Node &N : Graph->nodes()) { 690*e8d8bef9SDimitry Andric for (const Edge &E : N.edges()) { 691*e8d8bef9SDimitry Andric if (!MachineGadgetGraph::isGadgetEdge(E)) 6920946e70aSDimitry Andric continue; 6930946e70aSDimitry Andric 694*e8d8bef9SDimitry Andric SmallVector<const Edge *, 2> EgressEdges; 695*e8d8bef9SDimitry Andric SmallVector<const Edge *, 2> &IngressEdges = IngressEdgeMap[E.getDest()]; 696*e8d8bef9SDimitry Andric for (const Edge &EgressEdge : N.edges()) 697*e8d8bef9SDimitry Andric if (MachineGadgetGraph::isCFGEdge(EgressEdge)) 698*e8d8bef9SDimitry Andric EgressEdges.push_back(&EgressEdge); 6990946e70aSDimitry Andric 700*e8d8bef9SDimitry Andric int EgressCutCost = 0, IngressCutCost = 0; 701*e8d8bef9SDimitry Andric for (const Edge *EgressEdge : EgressEdges) 702*e8d8bef9SDimitry Andric if (!CutEdges.contains(*EgressEdge)) 703*e8d8bef9SDimitry Andric EgressCutCost += EgressEdge->getValue(); 704*e8d8bef9SDimitry Andric for (const Edge *IngressEdge : IngressEdges) 705*e8d8bef9SDimitry Andric if (!CutEdges.contains(*IngressEdge)) 706*e8d8bef9SDimitry Andric IngressCutCost += IngressEdge->getValue(); 707*e8d8bef9SDimitry Andric 708*e8d8bef9SDimitry Andric auto &EdgesToCut = 709*e8d8bef9SDimitry Andric IngressCutCost < EgressCutCost ? IngressEdges : EgressEdges; 710*e8d8bef9SDimitry Andric for (const Edge *E : EdgesToCut) 711*e8d8bef9SDimitry Andric CutEdges.insert(*E); 712*e8d8bef9SDimitry Andric } 713*e8d8bef9SDimitry Andric } 7140946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Cutting edges... Done\n"); 7150946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Cut " << CutEdges.count() << " edges\n"); 7160946e70aSDimitry Andric 7170946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Inserting LFENCEs...\n"); 7180946e70aSDimitry Andric int FencesInserted = insertFences(MF, *Graph, CutEdges); 7190946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Inserting LFENCEs... Done\n"); 7200946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Inserted " << FencesInserted << " fences\n"); 7210946e70aSDimitry Andric 7220946e70aSDimitry Andric return FencesInserted; 7230946e70aSDimitry Andric } 7240946e70aSDimitry Andric 7250946e70aSDimitry Andric int X86LoadValueInjectionLoadHardeningPass::insertFences( 7260946e70aSDimitry Andric MachineFunction &MF, MachineGadgetGraph &G, 7270946e70aSDimitry Andric EdgeSet &CutEdges /* in, out */) const { 7280946e70aSDimitry Andric int FencesInserted = 0; 729*e8d8bef9SDimitry Andric for (const Node &N : G.nodes()) { 730*e8d8bef9SDimitry Andric for (const Edge &E : N.edges()) { 7310946e70aSDimitry Andric if (CutEdges.contains(E)) { 7320946e70aSDimitry Andric MachineInstr *MI = N.getValue(), *Prev; 7330946e70aSDimitry Andric MachineBasicBlock *MBB; // Insert an LFENCE in this MBB 7340946e70aSDimitry Andric MachineBasicBlock::iterator InsertionPt; // ...at this point 7350946e70aSDimitry Andric if (MI == MachineGadgetGraph::ArgNodeSentinel) { 7360946e70aSDimitry Andric // insert LFENCE at beginning of entry block 7370946e70aSDimitry Andric MBB = &MF.front(); 7380946e70aSDimitry Andric InsertionPt = MBB->begin(); 7390946e70aSDimitry Andric Prev = nullptr; 7400946e70aSDimitry Andric } else if (MI->isBranch()) { // insert the LFENCE before the branch 7410946e70aSDimitry Andric MBB = MI->getParent(); 7420946e70aSDimitry Andric InsertionPt = MI; 7430946e70aSDimitry Andric Prev = MI->getPrevNode(); 7440946e70aSDimitry Andric // Remove all egress CFG edges from this branch because the inserted 7450946e70aSDimitry Andric // LFENCE prevents gadgets from crossing the branch. 746*e8d8bef9SDimitry Andric for (const Edge &E : N.edges()) { 7470946e70aSDimitry Andric if (MachineGadgetGraph::isCFGEdge(E)) 7480946e70aSDimitry Andric CutEdges.insert(E); 7490946e70aSDimitry Andric } 7500946e70aSDimitry Andric } else { // insert the LFENCE after the instruction 7510946e70aSDimitry Andric MBB = MI->getParent(); 7520946e70aSDimitry Andric InsertionPt = MI->getNextNode() ? MI->getNextNode() : MBB->end(); 7530946e70aSDimitry Andric Prev = InsertionPt == MBB->end() 7540946e70aSDimitry Andric ? (MBB->empty() ? nullptr : &MBB->back()) 7550946e70aSDimitry Andric : InsertionPt->getPrevNode(); 7560946e70aSDimitry Andric } 7570946e70aSDimitry Andric // Ensure this insertion is not redundant (two LFENCEs in sequence). 7580946e70aSDimitry Andric if ((InsertionPt == MBB->end() || !isFence(&*InsertionPt)) && 7590946e70aSDimitry Andric (!Prev || !isFence(Prev))) { 7600946e70aSDimitry Andric BuildMI(*MBB, InsertionPt, DebugLoc(), TII->get(X86::LFENCE)); 7610946e70aSDimitry Andric ++FencesInserted; 7620946e70aSDimitry Andric } 7630946e70aSDimitry Andric } 7640946e70aSDimitry Andric } 7650946e70aSDimitry Andric } 7660946e70aSDimitry Andric return FencesInserted; 7670946e70aSDimitry Andric } 7680946e70aSDimitry Andric 7690946e70aSDimitry Andric bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToAccessMemory( 7700946e70aSDimitry Andric const MachineInstr &MI, unsigned Reg) const { 7710946e70aSDimitry Andric if (!MI.mayLoadOrStore() || MI.getOpcode() == X86::MFENCE || 7720946e70aSDimitry Andric MI.getOpcode() == X86::SFENCE || MI.getOpcode() == X86::LFENCE) 7730946e70aSDimitry Andric return false; 7740946e70aSDimitry Andric 7750946e70aSDimitry Andric // FIXME: This does not handle pseudo loading instruction like TCRETURN* 7760946e70aSDimitry Andric const MCInstrDesc &Desc = MI.getDesc(); 7770946e70aSDimitry Andric int MemRefBeginIdx = X86II::getMemoryOperandNo(Desc.TSFlags); 7780946e70aSDimitry Andric if (MemRefBeginIdx < 0) { 7790946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Warning: unable to obtain memory operand for loading " 7800946e70aSDimitry Andric "instruction:\n"; 7810946e70aSDimitry Andric MI.print(dbgs()); dbgs() << '\n';); 7820946e70aSDimitry Andric return false; 7830946e70aSDimitry Andric } 7840946e70aSDimitry Andric MemRefBeginIdx += X86II::getOperandBias(Desc); 7850946e70aSDimitry Andric 7860946e70aSDimitry Andric const MachineOperand &BaseMO = 7870946e70aSDimitry Andric MI.getOperand(MemRefBeginIdx + X86::AddrBaseReg); 7880946e70aSDimitry Andric const MachineOperand &IndexMO = 7890946e70aSDimitry Andric MI.getOperand(MemRefBeginIdx + X86::AddrIndexReg); 7900946e70aSDimitry Andric return (BaseMO.isReg() && BaseMO.getReg() != X86::NoRegister && 7910946e70aSDimitry Andric TRI->regsOverlap(BaseMO.getReg(), Reg)) || 7920946e70aSDimitry Andric (IndexMO.isReg() && IndexMO.getReg() != X86::NoRegister && 7930946e70aSDimitry Andric TRI->regsOverlap(IndexMO.getReg(), Reg)); 7940946e70aSDimitry Andric } 7950946e70aSDimitry Andric 7960946e70aSDimitry Andric bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToBranch( 7970946e70aSDimitry Andric const MachineInstr &MI, unsigned Reg) const { 7980946e70aSDimitry Andric if (!MI.isConditionalBranch()) 7990946e70aSDimitry Andric return false; 8000946e70aSDimitry Andric for (const MachineOperand &Use : MI.uses()) 8010946e70aSDimitry Andric if (Use.isReg() && Use.getReg() == Reg) 8020946e70aSDimitry Andric return true; 8030946e70aSDimitry Andric return false; 8040946e70aSDimitry Andric } 8050946e70aSDimitry Andric 8060946e70aSDimitry Andric INITIALIZE_PASS_BEGIN(X86LoadValueInjectionLoadHardeningPass, PASS_KEY, 8070946e70aSDimitry Andric "X86 LVI load hardening", false, false) 8080946e70aSDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineLoopInfo) 8090946e70aSDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree) 8100946e70aSDimitry Andric INITIALIZE_PASS_DEPENDENCY(MachineDominanceFrontier) 8110946e70aSDimitry Andric INITIALIZE_PASS_END(X86LoadValueInjectionLoadHardeningPass, PASS_KEY, 8120946e70aSDimitry Andric "X86 LVI load hardening", false, false) 8130946e70aSDimitry Andric 8140946e70aSDimitry Andric FunctionPass *llvm::createX86LoadValueInjectionLoadHardeningPass() { 8150946e70aSDimitry Andric return new X86LoadValueInjectionLoadHardeningPass(); 8160946e70aSDimitry Andric } 817