9
9
// / Description: This pass finds Load Value Injection (LVI) gadgets consisting
10
10
// / of a load from memory (i.e., SOURCE), and any operation that may transmit
11
11
// / 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).
36
13
// /
37
14
// ===----------------------------------------------------------------------===//
38
15
60
37
#include " llvm/Support/CommandLine.h"
61
38
#include " llvm/Support/DOTGraphTraits.h"
62
39
#include " llvm/Support/Debug.h"
63
- #include " llvm/Support/DynamicLibrary.h"
64
40
#include " llvm/Support/GraphWriter.h"
65
41
#include " llvm/Support/raw_ostream.h"
66
42
@@ -69,16 +45,11 @@ using namespace llvm;
69
45
#define PASS_KEY " x86-lvi-load"
70
46
#define DEBUG_TYPE PASS_KEY
71
47
72
- STATISTIC (NumFences, " Number of LFENCEs inserted for LVI mitigation" );
73
48
STATISTIC (NumFunctionsConsidered, " Number of functions analyzed" );
74
49
STATISTIC (NumFunctionsMitigated, " Number of functions for which mitigations "
75
50
" were deployed" );
76
51
STATISTIC (NumGadgets, " Number of LVI gadgets detected during analysis" );
77
52
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
-
82
53
static cl::opt<bool > NoConditionalBranches (
83
54
PASS_KEY " -no-cbranch" ,
84
55
cl::desc (" Don't treat conditional branches as disclosure gadgets. This "
@@ -109,12 +80,6 @@ static cl::opt<bool> NoFixedLoads(
109
80
" may improve performance, at the cost of security." ),
110
81
cl::init(false ), cl::Hidden);
111
82
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
-
118
83
#define ARG_NODE nullptr
119
84
#define GADGET_EDGE ((int )(-1 ))
120
85
#define WEIGHT (EdgeValue ) ((double )(2 * (EdgeValue) + 1 ))
@@ -174,11 +139,6 @@ class X86LoadValueInjectionLoadHardeningPass : public MachineFunctionPass {
174
139
getGadgetGraph (MachineFunction &MF, const MachineLoopInfo &MLI,
175
140
const MachineDominatorTree &MDT,
176
141
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 ;
182
142
183
143
bool instrUsesRegToAccessMemory (const MachineInstr &I, unsigned Reg) const ;
184
144
bool instrUsesRegToBranch (const MachineInstr &I, unsigned Reg) const ;
@@ -281,26 +241,21 @@ bool X86LoadValueInjectionLoadHardeningPass::runOnMachineFunction(
281
241
TII = STI->getInstrInfo ();
282
242
TRI = STI->getRegisterInfo ();
283
243
LLVM_DEBUG (dbgs () << " Hardening data-dependent loads...\n " );
284
- int FencesInserted = hardenLoads (MF, false );
244
+ hardenLoads (MF, false );
285
245
LLVM_DEBUG (dbgs () << " Hardening data-dependent loads... Done\n " );
286
246
if (!NoFixedLoads) {
287
247
LLVM_DEBUG (dbgs () << " Hardening fixed loads...\n " );
288
- FencesInserted += hardenLoads (MF, true );
248
+ hardenLoads (MF, true );
289
249
LLVM_DEBUG (dbgs () << " Hardening fixed loads... Done\n " );
290
250
}
291
- if (FencesInserted > 0 )
292
- ++NumFunctionsMitigated;
293
- NumFences += FencesInserted;
294
- return (FencesInserted > 0 );
251
+ return false ;
295
252
}
296
253
297
254
// Apply the mitigation to `MF`, return the number of fences inserted.
298
255
// If `FixedLoads` is `true`, then the mitigation will be applied to fixed
299
256
// loads; otherwise, mitigation will be applied to non-fixed loads.
300
257
int X86LoadValueInjectionLoadHardeningPass::hardenLoads (MachineFunction &MF,
301
258
bool FixedLoads) const {
302
- int FencesInserted = 0 ;
303
-
304
259
LLVM_DEBUG (dbgs () << " Building gadget graph...\n " );
305
260
const auto &MLI = getAnalysis<MachineLoopInfo>();
306
261
const auto &MDT = getAnalysis<MachineDominatorTree>();
@@ -334,27 +289,7 @@ int X86LoadValueInjectionLoadHardeningPass::hardenLoads(MachineFunction &MF,
334
289
return 0 ;
335
290
}
336
291
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 ;
358
293
}
359
294
360
295
std::unique_ptr<X86LoadValueInjectionLoadHardeningPass::MachineGadgetGraph>
@@ -526,213 +461,6 @@ X86LoadValueInjectionLoadHardeningPass::getGadgetGraph(
526
461
return G;
527
462
}
528
463
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
-
736
464
bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToAccessMemory (
737
465
const MachineInstr &MI, unsigned Reg) const {
738
466
if (!MI.mayLoadOrStore () || MI.getOpcode () == X86::MFENCE ||
0 commit comments