99// / Description: This pass finds Load Value Injection (LVI) gadgets consisting
1010// / of a load from memory (i.e., SOURCE), and any operation that may transmit
1111// / the value loaded from memory over a covert channel, or use the value loaded
12- // / from memory to determine a branch/call target (i.e., SINK). After finding
13- // / all such gadgets in a given function, the pass minimally inserts LFENCE
14- // / instructions in such a manner that the following property is satisfied: for
15- // / all SOURCE+SINK pairs, all paths in the CFG from SOURCE to SINK contain at
16- // / least one LFENCE instruction. The algorithm that implements this minimal
17- // / insertion is influenced by an academic paper that minimally inserts memory
18- // / fences for high-performance concurrent programs:
19- // / http://www.cs.ucr.edu/~lesani/companion/oopsla15/OOPSLA15.pdf
20- // / The algorithm implemented in this pass is as follows:
21- // / 1. Build a condensed CFG (i.e., a GadgetGraph) consisting only of the
22- // / following components:
23- // / - SOURCE instructions (also includes function arguments)
24- // / - SINK instructions
25- // / - Basic block entry points
26- // / - Basic block terminators
27- // / - LFENCE instructions
28- // / 2. Analyze the GadgetGraph to determine which SOURCE+SINK pairs (i.e.,
29- // / gadgets) are already mitigated by existing LFENCEs. If all gadgets have been
30- // / mitigated, go to step 6.
31- // / 3. Use a heuristic or plugin to approximate minimal LFENCE insertion.
32- // / 4. Insert one LFENCE along each CFG edge that was cut in step 3.
33- // / 5. Go to step 2.
34- // / 6. If any LFENCEs were inserted, return `true` from runOnFunction() to tell
35- // / LLVM that the function was modified.
12+ // / from memory to determine a branch/call target (i.e., SINK).
3613// /
3714// ===----------------------------------------------------------------------===//
3815
6037#include " llvm/Support/CommandLine.h"
6138#include " llvm/Support/DOTGraphTraits.h"
6239#include " llvm/Support/Debug.h"
63- #include " llvm/Support/DynamicLibrary.h"
6440#include " llvm/Support/GraphWriter.h"
6541#include " llvm/Support/raw_ostream.h"
6642
@@ -69,16 +45,11 @@ using namespace llvm;
6945#define PASS_KEY " x86-lvi-load"
7046#define DEBUG_TYPE PASS_KEY
7147
72- STATISTIC (NumFences, " Number of LFENCEs inserted for LVI mitigation" );
7348STATISTIC (NumFunctionsConsidered, " Number of functions analyzed" );
7449STATISTIC (NumFunctionsMitigated, " Number of functions for which mitigations "
7550 " were deployed" );
7651STATISTIC (NumGadgets, " Number of LVI gadgets detected during analysis" );
7752
78- static cl::opt<std::string> OptimizePluginPath (
79- PASS_KEY " -opt-plugin" ,
80- cl::desc (" Specify a plugin to optimize LFENCE insertion" ), cl::Hidden);
81-
8253static cl::opt<bool > NoConditionalBranches (
8354 PASS_KEY " -no-cbranch" ,
8455 cl::desc (" Don't treat conditional branches as disclosure gadgets. This "
@@ -109,12 +80,6 @@ static cl::opt<bool> NoFixedLoads(
10980 " may improve performance, at the cost of security." ),
11081 cl::init(false ), cl::Hidden);
11182
112- static llvm::sys::DynamicLibrary OptimizeDL{};
113- typedef int (*OptimizeCutT)(unsigned int *nodes, unsigned int nodes_size,
114- unsigned int *edges, int *edge_values,
115- int *cut_edges /* out */ , unsigned int edges_size);
116- static OptimizeCutT OptimizeCut = nullptr ;
117-
11883#define ARG_NODE nullptr
11984#define GADGET_EDGE ((int )(-1 ))
12085#define WEIGHT (EdgeValue ) ((double )(2 * (EdgeValue) + 1 ))
@@ -174,11 +139,6 @@ class X86LoadValueInjectionLoadHardeningPass : public MachineFunctionPass {
174139 getGadgetGraph (MachineFunction &MF, const MachineLoopInfo &MLI,
175140 const MachineDominatorTree &MDT,
176141 const MachineDominanceFrontier &MDF, bool FixedLoads) const ;
177- std::unique_ptr<MachineGadgetGraph>
178- elimEdges (std::unique_ptr<MachineGadgetGraph> Graph) const ;
179- void cutEdges (MachineGadgetGraph &G, EdgeSet &CutEdges /* out */ ) const ;
180- int insertFences (MachineGadgetGraph &G,
181- EdgeSet &CutEdges /* in, out */ ) const ;
182142
183143 bool instrUsesRegToAccessMemory (const MachineInstr &I, unsigned Reg) const ;
184144 bool instrUsesRegToBranch (const MachineInstr &I, unsigned Reg) const ;
@@ -281,26 +241,21 @@ bool X86LoadValueInjectionLoadHardeningPass::runOnMachineFunction(
281241 TII = STI->getInstrInfo ();
282242 TRI = STI->getRegisterInfo ();
283243 LLVM_DEBUG (dbgs () << " Hardening data-dependent loads...\n " );
284- int FencesInserted = hardenLoads (MF, false );
244+ hardenLoads (MF, false );
285245 LLVM_DEBUG (dbgs () << " Hardening data-dependent loads... Done\n " );
286246 if (!NoFixedLoads) {
287247 LLVM_DEBUG (dbgs () << " Hardening fixed loads...\n " );
288- FencesInserted += hardenLoads (MF, true );
248+ hardenLoads (MF, true );
289249 LLVM_DEBUG (dbgs () << " Hardening fixed loads... Done\n " );
290250 }
291- if (FencesInserted > 0 )
292- ++NumFunctionsMitigated;
293- NumFences += FencesInserted;
294- return (FencesInserted > 0 );
251+ return false ;
295252}
296253
297254// Apply the mitigation to `MF`, return the number of fences inserted.
298255// If `FixedLoads` is `true`, then the mitigation will be applied to fixed
299256// loads; otherwise, mitigation will be applied to non-fixed loads.
300257int X86LoadValueInjectionLoadHardeningPass::hardenLoads (MachineFunction &MF,
301258 bool FixedLoads) const {
302- int FencesInserted = 0 ;
303-
304259 LLVM_DEBUG (dbgs () << " Building gadget graph...\n " );
305260 const auto &MLI = getAnalysis<MachineLoopInfo>();
306261 const auto &MDT = getAnalysis<MachineDominatorTree>();
@@ -334,27 +289,7 @@ int X86LoadValueInjectionLoadHardeningPass::hardenLoads(MachineFunction &MF,
334289 return 0 ;
335290 }
336291
337- do {
338- LLVM_DEBUG (dbgs () << " Eliminating mitigated paths...\n " );
339- std::unique_ptr<MachineGadgetGraph> ElimGraph = elimEdges (std::move (Graph));
340- LLVM_DEBUG (dbgs () << " Eliminating mitigated paths... Done\n " );
341- if (ElimGraph->NumGadgets == 0 )
342- break ;
343-
344- EdgeSet CutEdges{*ElimGraph};
345- LLVM_DEBUG (dbgs () << " Cutting edges...\n " );
346- cutEdges (*ElimGraph, CutEdges);
347- LLVM_DEBUG (dbgs () << " Cutting edges... Done\n " );
348-
349- LLVM_DEBUG (dbgs () << " Inserting LFENCEs...\n " );
350- FencesInserted += insertFences (*ElimGraph, CutEdges);
351- LLVM_DEBUG (dbgs () << " Inserting LFENCEs... Done\n " );
352-
353- Graph.reset (GraphBuilder::trim (
354- *ElimGraph, MachineGadgetGraph::NodeSet{*ElimGraph}, CutEdges));
355- } while (true );
356-
357- return FencesInserted;
292+ return 0 ;
358293}
359294
360295std::unique_ptr<X86LoadValueInjectionLoadHardeningPass::MachineGadgetGraph>
@@ -526,213 +461,6 @@ X86LoadValueInjectionLoadHardeningPass::getGadgetGraph(
526461 return G;
527462}
528463
529- std::unique_ptr<X86LoadValueInjectionLoadHardeningPass::MachineGadgetGraph>
530- X86LoadValueInjectionLoadHardeningPass::elimEdges (
531- std::unique_ptr<MachineGadgetGraph> Graph) const {
532- MachineGadgetGraph::NodeSet ElimNodes{*Graph};
533- MachineGadgetGraph::EdgeSet ElimEdges{*Graph};
534-
535- if (Graph->NumFences > 0 ) { // eliminate fences
536- for (auto EI = Graph->edges_begin (), EE = Graph->edges_end (); EI != EE;
537- ++EI) {
538- GTraits::NodeRef Dest = GTraits::edge_dest (*EI);
539- if (isFence (Dest->value ())) {
540- ElimNodes.insert (Dest);
541- ElimEdges.insert (EI);
542- std::for_each (
543- GTraits::child_edge_begin (Dest), GTraits::child_edge_end (Dest),
544- [&ElimEdges](GTraits::EdgeRef E) { ElimEdges.insert (&E); });
545- }
546- }
547- LLVM_DEBUG (dbgs () << " Eliminated " << ElimNodes.count ()
548- << " fence nodes\n " );
549- }
550-
551- // eliminate gadget edges that are mitigated
552- int NumGadgets = 0 ;
553- MachineGadgetGraph::NodeSet Visited{*Graph}, GadgetSinks{*Graph};
554- MachineGadgetGraph::EdgeSet ElimGadgets{*Graph};
555- for (auto NI = GTraits::nodes_begin (Graph.get ()),
556- NE = GTraits::nodes_end (Graph.get ());
557- NI != NE; ++NI) {
558- // collect the gadgets for this node
559- for (auto EI = GTraits::child_edge_begin (*NI),
560- EE = GTraits::child_edge_end (*NI);
561- EI != EE; ++EI) {
562- if (MachineGadgetGraph::isGadgetEdge (*EI)) {
563- ++NumGadgets;
564- ElimGadgets.insert (EI);
565- GadgetSinks.insert (GTraits::edge_dest (*EI));
566- }
567- }
568- if (GadgetSinks.empty ())
569- continue ;
570- std::function<void (GTraits::NodeRef, bool )> TraverseDFS =
571- [&](GTraits::NodeRef N, bool FirstNode) {
572- if (!FirstNode) {
573- Visited.insert (N);
574- if (GadgetSinks.contains (N)) {
575- for (auto CEI = GTraits::child_edge_begin (*NI),
576- CEE = GTraits::child_edge_end (*NI);
577- CEI != CEE; ++CEI) {
578- if (MachineGadgetGraph::isGadgetEdge (*CEI) &&
579- GTraits::edge_dest (*CEI) == N)
580- ElimGadgets.erase (CEI);
581- }
582- }
583- }
584- for (auto CEI = GTraits::child_edge_begin (N),
585- CEE = GTraits::child_edge_end (N);
586- CEI != CEE; ++CEI) {
587- GTraits::NodeRef Dest = GTraits::edge_dest (*CEI);
588- if (MachineGadgetGraph::isCFGEdge (*CEI) &&
589- !Visited.contains (Dest) && !ElimEdges.contains (CEI))
590- TraverseDFS (Dest, false );
591- }
592- };
593- TraverseDFS (*NI, true );
594- Visited.clear ();
595- GadgetSinks.clear ();
596- }
597- LLVM_DEBUG (dbgs () << " Eliminated " << ElimGadgets.count ()
598- << " gadget edges\n " );
599- ElimEdges |= ElimGadgets;
600-
601- if (!(ElimEdges.empty () && ElimNodes.empty ())) {
602- int NumRemainingGadgets = NumGadgets - ElimGadgets.count ();
603- Graph.reset (GraphBuilder::trim (*Graph, ElimNodes, ElimEdges,
604- 0 /* NumFences */ , NumRemainingGadgets));
605- } else {
606- Graph->NumFences = 0 ;
607- Graph->NumGadgets = NumGadgets;
608- }
609- return Graph;
610- }
611-
612- void X86LoadValueInjectionLoadHardeningPass::cutEdges (
613- MachineGadgetGraph &G,
614- MachineGadgetGraph::EdgeSet &CutEdges /* out */ ) const {
615- if (!OptimizePluginPath.empty ()) {
616- if (!OptimizeDL.isValid ()) {
617- std::string ErrorMsg{};
618- OptimizeDL = llvm::sys::DynamicLibrary::getPermanentLibrary (
619- OptimizePluginPath.c_str (), &ErrorMsg);
620- if (!ErrorMsg.empty ())
621- report_fatal_error (" Failed to load opt plugin: \" " + ErrorMsg + ' \" ' );
622- OptimizeCut = (OptimizeCutT)OptimizeDL.getAddressOfSymbol (" optimize_cut" );
623- if (!OptimizeCut)
624- report_fatal_error (" Invalid optimization plugin" );
625- }
626- auto *Nodes = new unsigned int [G.nodes_size () + 1 /* terminator node */ ];
627- auto *Edges = new unsigned int [G.edges_size ()];
628- auto *EdgeCuts = new int [G.edges_size ()];
629- auto *EdgeValues = new int [G.edges_size ()];
630- for (auto *NI = G.nodes_begin (), *NE = G.nodes_end (); NI != NE; ++NI) {
631- Nodes[std::distance (G.nodes_begin (), NI)] =
632- std::distance (G.edges_begin (), GTraits::child_edge_begin (NI));
633- }
634- Nodes[G.nodes_size ()] = G.edges_size (); // terminator node
635- for (auto *EI = G.edges_begin (), *EE = G.edges_end (); EI != EE; ++EI) {
636- Edges[std::distance (G.edges_begin (), EI)] =
637- std::distance (G.nodes_begin (), GTraits::edge_dest (*EI));
638- EdgeValues[std::distance (G.edges_begin (), EI)] = EI->value ();
639- }
640- OptimizeCut (Nodes, G.nodes_size (), Edges, EdgeValues, EdgeCuts,
641- G.edges_size ());
642- for (int I = 0 ; I < G.edges_size (); ++I) {
643- if (EdgeCuts[I])
644- CutEdges.set (I);
645- }
646- delete[] Nodes;
647- delete[] Edges;
648- delete[] EdgeCuts;
649- delete[] EdgeValues;
650- } else { // Use the default greedy heuristic
651- // Find the cheapest CFG edge that will eliminate a gadget (by being egress
652- // from a SOURCE node or ingress to a SINK node), and cut it.
653- MachineGadgetGraph::NodeSet GadgetSinks{G};
654- MachineGadgetGraph::Edge *CheapestSoFar = nullptr ;
655- for (auto NI = GTraits::nodes_begin (&G), NE = GTraits::nodes_end (&G);
656- NI != NE; ++NI) {
657- for (auto EI = GTraits::child_edge_begin (*NI),
658- EE = GTraits::child_edge_end (*NI);
659- EI != EE; ++EI) {
660- if (MachineGadgetGraph::isGadgetEdge (*EI)) {
661- // NI is a SOURCE node. Look for a cheap egress edge
662- for (auto EEI = GTraits::child_edge_begin (*NI); EEI != EE; ++EEI) {
663- if (MachineGadgetGraph::isCFGEdge (*EEI)) {
664- if (!CheapestSoFar || EEI->value () < CheapestSoFar->value ())
665- CheapestSoFar = EEI;
666- }
667- }
668- GadgetSinks.insert (GTraits::edge_dest (*EI));
669- } else { // EI is a CFG edge
670- if (GadgetSinks.contains (GTraits::edge_dest (*EI))) {
671- // The dest is a SINK node. Hence EI is an ingress edge
672- if (!CheapestSoFar || EI->value () < CheapestSoFar->value ())
673- CheapestSoFar = EI;
674- }
675- }
676- }
677- }
678- assert (CheapestSoFar && " Failed to cut an edge" );
679- CutEdges.insert (CheapestSoFar);
680- }
681- LLVM_DEBUG (dbgs () << " Cut " << CutEdges.count () << " edges\n " );
682- }
683-
684- int X86LoadValueInjectionLoadHardeningPass::insertFences (
685- MachineGadgetGraph &G, EdgeSet &CutEdges /* in, out */ ) const {
686- int FencesInserted = 0 , AdditionalEdgesCut = 0 ;
687- auto CutAllCFGEdges = [&CutEdges, &AdditionalEdgesCut](GTraits::NodeRef N) {
688- for (auto CEI = GTraits::child_edge_begin (N),
689- CEE = GTraits::child_edge_end (N);
690- CEI != CEE; ++CEI) {
691- if (MachineGadgetGraph::isCFGEdge (*CEI) && !CutEdges.contains (CEI)) {
692- CutEdges.insert (CEI);
693- ++AdditionalEdgesCut;
694- }
695- }
696- };
697- for (auto NI = GTraits::nodes_begin (&G), NE = GTraits::nodes_end (&G);
698- NI != NE; ++NI) {
699- for (auto CEI = GTraits::child_edge_begin (*NI),
700- CEE = GTraits::child_edge_end (*NI);
701- CEI != CEE; ++CEI) {
702- if (CutEdges.contains (CEI)) {
703- MachineInstr *MI = (*NI)->value (), *Prev;
704- MachineBasicBlock *MBB;
705- MachineBasicBlock::iterator InsertionPt;
706- if (MI == ARG_NODE) { // insert LFENCE at beginning of entry block
707- MBB = &G.getMF ().front ();
708- InsertionPt = MBB->begin ();
709- Prev = nullptr ;
710- } else if (MI->isBranch ()) { // insert the LFENCE before the branch
711- MBB = MI->getParent ();
712- InsertionPt = MI;
713- Prev = MI->getPrevNode ();
714- CutAllCFGEdges (*NI);
715- } else { // insert the LFENCE after the instruction
716- MBB = MI->getParent ();
717- InsertionPt = MI->getNextNode () ? MI->getNextNode () : MBB->end ();
718- Prev = InsertionPt == MBB->end ()
719- ? (MBB->empty () ? nullptr : &MBB->back ())
720- : InsertionPt->getPrevNode ();
721- }
722- if ((InsertionPt == MBB->end () || !isFence (&*InsertionPt)) &&
723- (!Prev || !isFence (Prev))) {
724- BuildMI (*MBB, InsertionPt, DebugLoc (), TII->get (X86::LFENCE));
725- ++FencesInserted;
726- }
727- }
728- }
729- }
730- LLVM_DEBUG (dbgs () << " Inserted " << FencesInserted << " fences\n " );
731- LLVM_DEBUG (dbgs () << " Cut an additional " << AdditionalEdgesCut
732- << " edges during fence insertion\n " );
733- return FencesInserted;
734- }
735-
736464bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToAccessMemory (
737465 const MachineInstr &MI, unsigned Reg) const {
738466 if (!MI.mayLoadOrStore () || MI.getOpcode () == X86::MFENCE ||
0 commit comments