Skip to content

Commit 5bc4d47

Browse files
topperctstellar
authored andcommitted
Revert "[X86] Add Support for Load Hardening to Mitigate Load Value Injection (LVI)"
This reverts commit 62c42e2 Reverting to address coding standard issues raised in post-commit review.
1 parent 2530f4e commit 5bc4d47

File tree

2 files changed

+5
-379
lines changed

2 files changed

+5
-379
lines changed

llvm/lib/Target/X86/X86LoadValueInjectionLoadHardening.cpp

Lines changed: 5 additions & 277 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,7 @@
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

@@ -60,7 +37,6 @@
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");
7348
STATISTIC(NumFunctionsConsidered, "Number of functions analyzed");
7449
STATISTIC(NumFunctionsMitigated, "Number of functions for which mitigations "
7550
"were deployed");
7651
STATISTIC(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-
8253
static 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.
300257
int 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

360295
std::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-
736464
bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToAccessMemory(
737465
const MachineInstr &MI, unsigned Reg) const {
738466
if (!MI.mayLoadOrStore() || MI.getOpcode() == X86::MFENCE ||

0 commit comments

Comments
 (0)