LLVM 22.0.0git
X86LoadValueInjectionLoadHardening.cpp
Go to the documentation of this file.
1//==-- X86LoadValueInjectionLoadHardening.cpp - LVI load hardening for x86 --=//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// Description: This pass finds Load Value Injection (LVI) gadgets consisting
10/// of a load from memory (i.e., SOURCE), and any operation that may transmit
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 runOnMachineFunction()
35/// to tell LLVM that the function was modified.
36///
37//===----------------------------------------------------------------------===//
38
39#include "ImmutableGraph.h"
40#include "X86.h"
41#include "X86Subtarget.h"
42#include "X86TargetMachine.h"
43#include "llvm/ADT/DenseMap.h"
44#include "llvm/ADT/STLExtras.h"
45#include "llvm/ADT/SmallSet.h"
46#include "llvm/ADT/Statistic.h"
47#include "llvm/ADT/StringRef.h"
61#include "llvm/Support/Debug.h"
65
66using namespace llvm;
67
68#define PASS_KEY "x86-lvi-load"
69#define DEBUG_TYPE PASS_KEY
70
71STATISTIC(NumFences, "Number of LFENCEs inserted for LVI mitigation");
72STATISTIC(NumFunctionsConsidered, "Number of functions analyzed");
73STATISTIC(NumFunctionsMitigated, "Number of functions for which mitigations "
74 "were deployed");
75STATISTIC(NumGadgets, "Number of LVI gadgets detected during analysis");
76
78 PASS_KEY "-opt-plugin",
79 cl::desc("Specify a plugin to optimize LFENCE insertion"), cl::Hidden);
80
82 PASS_KEY "-no-cbranch",
83 cl::desc("Don't treat conditional branches as disclosure gadgets. This "
84 "may improve performance, at the cost of security."),
85 cl::init(false), cl::Hidden);
86
88 PASS_KEY "-dot",
90 "For each function, emit a dot graph depicting potential LVI gadgets"),
91 cl::init(false), cl::Hidden);
92
94 PASS_KEY "-dot-only",
95 cl::desc("For each function, emit a dot graph depicting potential LVI "
96 "gadgets, and do not insert any fences"),
97 cl::init(false), cl::Hidden);
98
100 PASS_KEY "-dot-verify",
101 cl::desc("For each function, emit a dot graph to stdout depicting "
102 "potential LVI gadgets, used for testing purposes only"),
103 cl::init(false), cl::Hidden);
104
106typedef int (*OptimizeCutT)(unsigned int *Nodes, unsigned int NodesSize,
107 unsigned int *Edges, int *EdgeValues,
108 int *CutEdges /* out */, unsigned int EdgesSize);
109static OptimizeCutT OptimizeCut = nullptr;
110
111namespace {
112
113struct MachineGadgetGraph : ImmutableGraph<MachineInstr *, int> {
114 static constexpr int GadgetEdgeSentinel = -1;
115 static constexpr MachineInstr *const ArgNodeSentinel = nullptr;
116
118 using Node = GraphT::Node;
119 using Edge = GraphT::Edge;
120 using size_type = GraphT::size_type;
121 MachineGadgetGraph(std::unique_ptr<Node[]> Nodes,
122 std::unique_ptr<Edge[]> Edges, size_type NodesSize,
123 size_type EdgesSize, int NumFences = 0, int NumGadgets = 0)
124 : GraphT(std::move(Nodes), std::move(Edges), NodesSize, EdgesSize),
125 NumFences(NumFences), NumGadgets(NumGadgets) {}
126 static inline bool isCFGEdge(const Edge &E) {
127 return E.getValue() != GadgetEdgeSentinel;
128 }
129 static inline bool isGadgetEdge(const Edge &E) {
130 return E.getValue() == GadgetEdgeSentinel;
131 }
132 int NumFences;
133 int NumGadgets;
134};
135
136class X86LoadValueInjectionLoadHardeningPass : public MachineFunctionPass {
137public:
138 X86LoadValueInjectionLoadHardeningPass() : MachineFunctionPass(ID) {}
139
140 StringRef getPassName() const override {
141 return "X86 Load Value Injection (LVI) Load Hardening";
142 }
143 void getAnalysisUsage(AnalysisUsage &AU) const override;
144 bool runOnMachineFunction(MachineFunction &MF) override;
145
146 static char ID;
147
148private:
150 using Edge = MachineGadgetGraph::Edge;
151 using Node = MachineGadgetGraph::Node;
152 using EdgeSet = MachineGadgetGraph::EdgeSet;
153 using NodeSet = MachineGadgetGraph::NodeSet;
154
155 const X86Subtarget *STI = nullptr;
156 const TargetInstrInfo *TII = nullptr;
157 const TargetRegisterInfo *TRI = nullptr;
158
159 std::unique_ptr<MachineGadgetGraph>
160 getGadgetGraph(MachineFunction &MF, const MachineLoopInfo &MLI,
161 const MachineDominatorTree &MDT,
162 const MachineDominanceFrontier &MDF) const;
163 int hardenLoadsWithPlugin(MachineFunction &MF,
164 std::unique_ptr<MachineGadgetGraph> Graph) const;
165 int hardenLoadsWithHeuristic(MachineFunction &MF,
166 std::unique_ptr<MachineGadgetGraph> Graph) const;
167 int elimMitigatedEdgesAndNodes(MachineGadgetGraph &G,
168 EdgeSet &ElimEdges /* in, out */,
169 NodeSet &ElimNodes /* in, out */) const;
170 std::unique_ptr<MachineGadgetGraph>
171 trimMitigatedEdges(std::unique_ptr<MachineGadgetGraph> Graph) const;
172 int insertFences(MachineFunction &MF, MachineGadgetGraph &G,
173 EdgeSet &CutEdges /* in, out */) const;
174 bool instrUsesRegToAccessMemory(const MachineInstr &I, Register Reg) const;
175 bool instrUsesRegToBranch(const MachineInstr &I, Register Reg) const;
176 inline bool isFence(const MachineInstr *MI) const {
177 return MI && (MI->getOpcode() == X86::LFENCE ||
178 (STI->useLVIControlFlowIntegrity() && MI->isCall()));
179 }
180};
181
182} // end anonymous namespace
183
184namespace llvm {
185
186template <>
187struct GraphTraits<MachineGadgetGraph *>
189
190template <>
191struct DOTGraphTraits<MachineGadgetGraph *> : DefaultDOTGraphTraits {
192 using GraphType = MachineGadgetGraph;
195 using EdgeRef = Traits::EdgeRef;
196 using ChildIteratorType = Traits::ChildIteratorType;
197 using ChildEdgeIteratorType = Traits::ChildEdgeIteratorType;
198
199 DOTGraphTraits(bool IsSimple = false) : DefaultDOTGraphTraits(IsSimple) {}
200
202 if (Node->getValue() == MachineGadgetGraph::ArgNodeSentinel)
203 return "ARGS";
204
205 std::string Str;
206 raw_string_ostream OS(Str);
207 OS << *Node->getValue();
208 return OS.str();
209 }
210
211 static std::string getNodeAttributes(NodeRef Node, GraphType *) {
212 MachineInstr *MI = Node->getValue();
213 if (MI == MachineGadgetGraph::ArgNodeSentinel)
214 return "color = blue";
215 if (MI->getOpcode() == X86::LFENCE)
216 return "color = green";
217 return "";
218 }
219
221 GraphType *) {
222 int EdgeVal = (*E.getCurrent()).getValue();
223 return EdgeVal >= 0 ? "label = " + std::to_string(EdgeVal)
224 : "color = red, style = \"dashed\"";
225 }
226};
227
228} // end namespace llvm
229
230char X86LoadValueInjectionLoadHardeningPass::ID = 0;
231
232void X86LoadValueInjectionLoadHardeningPass::getAnalysisUsage(
233 AnalysisUsage &AU) const {
235 AU.addRequired<MachineLoopInfoWrapperPass>();
236 AU.addRequired<MachineDominatorTreeWrapperPass>();
237 AU.addRequired<MachineDominanceFrontier>();
238 AU.setPreservesCFG();
239}
240
242 MachineGadgetGraph *G) {
243 WriteGraph(OS, G, /*ShortNames*/ false,
244 "Speculative gadgets for \"" + MF.getName() + "\" function");
245}
246
247bool X86LoadValueInjectionLoadHardeningPass::runOnMachineFunction(
248 MachineFunction &MF) {
249 LLVM_DEBUG(dbgs() << "***** " << getPassName() << " : " << MF.getName()
250 << " *****\n");
251 STI = &MF.getSubtarget<X86Subtarget>();
252 if (!STI->useLVILoadHardening())
253 return false;
254
255 // FIXME: support 32-bit
256 if (!STI->is64Bit())
257 report_fatal_error("LVI load hardening is only supported on 64-bit", false);
258
259 // Don't skip functions with the "optnone" attr but participate in opt-bisect.
260 const Function &F = MF.getFunction();
261 if (!F.hasOptNone() && skipFunction(F))
262 return false;
263
264 ++NumFunctionsConsidered;
265 TII = STI->getInstrInfo();
266 TRI = STI->getRegisterInfo();
267 LLVM_DEBUG(dbgs() << "Building gadget graph...\n");
268 const auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
269 const auto &MDT = getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
270 const auto &MDF = getAnalysis<MachineDominanceFrontier>();
271 std::unique_ptr<MachineGadgetGraph> Graph = getGadgetGraph(MF, MLI, MDT, MDF);
272 LLVM_DEBUG(dbgs() << "Building gadget graph... Done\n");
273 if (Graph == nullptr)
274 return false; // didn't find any gadgets
275
276 if (EmitDotVerify) {
277 writeGadgetGraph(outs(), MF, Graph.get());
278 return false;
279 }
280
281 if (EmitDot || EmitDotOnly) {
282 LLVM_DEBUG(dbgs() << "Emitting gadget graph...\n");
283 std::error_code FileError;
284 std::string FileName = "lvi.";
285 FileName += MF.getName();
286 FileName += ".dot";
287 raw_fd_ostream FileOut(FileName, FileError);
288 if (FileError)
289 errs() << FileError.message();
290 writeGadgetGraph(FileOut, MF, Graph.get());
291 FileOut.close();
292 LLVM_DEBUG(dbgs() << "Emitting gadget graph... Done\n");
293 if (EmitDotOnly)
294 return false;
295 }
296
297 int FencesInserted;
298 if (!OptimizePluginPath.empty()) {
299 if (!OptimizeDL.isValid()) {
300 std::string ErrorMsg;
302 OptimizePluginPath.c_str(), &ErrorMsg);
303 if (!ErrorMsg.empty())
304 report_fatal_error(Twine("Failed to load opt plugin: \"") + ErrorMsg +
305 "\"");
307 if (!OptimizeCut)
308 report_fatal_error("Invalid optimization plugin");
309 }
310 FencesInserted = hardenLoadsWithPlugin(MF, std::move(Graph));
311 } else { // Use the default greedy heuristic
312 FencesInserted = hardenLoadsWithHeuristic(MF, std::move(Graph));
313 }
314
315 if (FencesInserted > 0)
316 ++NumFunctionsMitigated;
317 NumFences += FencesInserted;
318 return (FencesInserted > 0);
319}
320
321std::unique_ptr<MachineGadgetGraph>
322X86LoadValueInjectionLoadHardeningPass::getGadgetGraph(
323 MachineFunction &MF, const MachineLoopInfo &MLI,
324 const MachineDominatorTree &MDT,
325 const MachineDominanceFrontier &MDF) const {
326 using namespace rdf;
327
328 // Build the Register Dataflow Graph using the RDF framework
329 DataFlowGraph DFG{MF, *TII, *TRI, MDT, MDF};
330 DFG.build();
331 Liveness L{MF.getRegInfo(), DFG};
332 L.computePhiInfo();
333
334 GraphBuilder Builder;
335 using GraphIter = GraphBuilder::BuilderNodeRef;
336 DenseMap<MachineInstr *, GraphIter> NodeMap;
337 int FenceCount = 0, GadgetCount = 0;
338 auto MaybeAddNode = [&NodeMap, &Builder](MachineInstr *MI) {
339 auto [Ref, Inserted] = NodeMap.try_emplace(MI);
340 if (Inserted) {
341 auto I = Builder.addVertex(MI);
342 Ref->second = I;
343 return std::pair<GraphIter, bool>{I, true};
344 }
345 return std::pair<GraphIter, bool>{Ref->getSecond(), false};
346 };
347
348 // The `Transmitters` map memoizes transmitters found for each def. If a def
349 // has not yet been analyzed, then it will not appear in the map. If a def
350 // has been analyzed and was determined not to have any transmitters, then
351 // its list of transmitters will be empty.
352 DenseMap<NodeId, std::vector<NodeId>> Transmitters;
353
354 // Analyze all machine instructions to find gadgets and LFENCEs, adding
355 // each interesting value to `Nodes`
356 auto AnalyzeDef = [&](NodeAddr<DefNode *> SourceDef) {
357 SmallSet<NodeId, 8> UsesVisited, DefsVisited;
358 std::function<void(NodeAddr<DefNode *>)> AnalyzeDefUseChain =
359 [&](NodeAddr<DefNode *> Def) {
360 if (Transmitters.contains(Def.Id))
361 return; // Already analyzed `Def`
362
363 // Use RDF to find all the uses of `Def`
365 RegisterRef DefReg = Def.Addr->getRegRef(DFG);
366 for (auto UseID : L.getAllReachedUses(DefReg, Def)) {
367 auto Use = DFG.addr<UseNode *>(UseID);
368 if (Use.Addr->getFlags() & NodeAttrs::PhiRef) { // phi node
369 NodeAddr<PhiNode *> Phi = Use.Addr->getOwner(DFG);
370 for (const auto& I : L.getRealUses(Phi.Id)) {
371 if (DFG.getPRI().alias(RegisterRef(I.first), DefReg)) {
372 for (const auto &UA : I.second)
373 Uses.emplace(UA.first);
374 }
375 }
376 } else { // not a phi node
377 Uses.emplace(UseID);
378 }
379 }
380
381 // For each use of `Def`, we want to know whether:
382 // (1) The use can leak the Def'ed value,
383 // (2) The use can further propagate the Def'ed value to more defs
384 for (auto UseID : Uses) {
385 if (!UsesVisited.insert(UseID).second)
386 continue; // Already visited this use of `Def`
387
388 auto Use = DFG.addr<UseNode *>(UseID);
389 assert(!(Use.Addr->getFlags() & NodeAttrs::PhiRef));
390 MachineOperand &UseMO = Use.Addr->getOp();
391 MachineInstr &UseMI = *UseMO.getParent();
392 assert(UseMO.isReg());
393
394 // We naively assume that an instruction propagates any loaded
395 // uses to all defs unless the instruction is a call, in which
396 // case all arguments will be treated as gadget sources during
397 // analysis of the callee function.
398 if (UseMI.isCall())
399 continue;
400
401 // Check whether this use can transmit (leak) its value.
402 if (instrUsesRegToAccessMemory(UseMI, UseMO.getReg()) ||
404 instrUsesRegToBranch(UseMI, UseMO.getReg()))) {
405 Transmitters[Def.Id].push_back(Use.Addr->getOwner(DFG).Id);
406 if (UseMI.mayLoad())
407 continue; // Found a transmitting load -- no need to continue
408 // traversing its defs (i.e., this load will become
409 // a new gadget source anyways).
410 }
411
412 // Check whether the use propagates to more defs.
413 NodeAddr<InstrNode *> Owner{Use.Addr->getOwner(DFG)};
414 for (const auto &ChildDef :
415 Owner.Addr->members_if(DataFlowGraph::IsDef, DFG)) {
416 if (!DefsVisited.insert(ChildDef.Id).second)
417 continue; // Already visited this def
418 if (Def.Addr->getAttrs() & NodeAttrs::Dead)
419 continue;
420 if (Def.Id == ChildDef.Id)
421 continue; // `Def` uses itself (e.g., increment loop counter)
422
423 AnalyzeDefUseChain(ChildDef);
424
425 // `Def` inherits all of its child defs' transmitters.
426 for (auto TransmitterId : Transmitters[ChildDef.Id])
427 Transmitters[Def.Id].push_back(TransmitterId);
428 }
429 }
430
431 // Note that this statement adds `Def.Id` to the map if no
432 // transmitters were found for `Def`.
433 auto &DefTransmitters = Transmitters[Def.Id];
434
435 // Remove duplicate transmitters
436 llvm::sort(DefTransmitters);
437 DefTransmitters.erase(llvm::unique(DefTransmitters),
438 DefTransmitters.end());
439 };
440
441 // Find all of the transmitters
442 AnalyzeDefUseChain(SourceDef);
443 auto &SourceDefTransmitters = Transmitters[SourceDef.Id];
444 if (SourceDefTransmitters.empty())
445 return; // No transmitters for `SourceDef`
446
447 MachineInstr *Source = SourceDef.Addr->getFlags() & NodeAttrs::PhiRef
448 ? MachineGadgetGraph::ArgNodeSentinel
449 : SourceDef.Addr->getOp().getParent();
450 auto GadgetSource = MaybeAddNode(Source);
451 // Each transmitter is a sink for `SourceDef`.
452 for (auto TransmitterId : SourceDefTransmitters) {
453 MachineInstr *Sink = DFG.addr<StmtNode *>(TransmitterId).Addr->getCode();
454 auto GadgetSink = MaybeAddNode(Sink);
455 // Add the gadget edge to the graph.
456 Builder.addEdge(MachineGadgetGraph::GadgetEdgeSentinel,
457 GadgetSource.first, GadgetSink.first);
458 ++GadgetCount;
459 }
460 };
461
462 LLVM_DEBUG(dbgs() << "Analyzing def-use chains to find gadgets\n");
463 // Analyze function arguments
464 NodeAddr<BlockNode *> EntryBlock = DFG.getFunc().Addr->getEntryBlock(DFG);
465 for (NodeAddr<PhiNode *> ArgPhi :
466 EntryBlock.Addr->members_if(DataFlowGraph::IsPhi, DFG)) {
467 NodeList Defs = ArgPhi.Addr->members_if(DataFlowGraph::IsDef, DFG);
468 llvm::for_each(Defs, AnalyzeDef);
469 }
470 // Analyze every instruction in MF
471 for (NodeAddr<BlockNode *> BA : DFG.getFunc().Addr->members(DFG)) {
472 for (NodeAddr<StmtNode *> SA :
473 BA.Addr->members_if(DataFlowGraph::IsCode<NodeAttrs::Stmt>, DFG)) {
474 MachineInstr *MI = SA.Addr->getCode();
475 if (isFence(MI)) {
476 MaybeAddNode(MI);
477 ++FenceCount;
478 } else if (MI->mayLoad()) {
479 NodeList Defs = SA.Addr->members_if(DataFlowGraph::IsDef, DFG);
480 llvm::for_each(Defs, AnalyzeDef);
481 }
482 }
483 }
484 LLVM_DEBUG(dbgs() << "Found " << FenceCount << " fences\n");
485 LLVM_DEBUG(dbgs() << "Found " << GadgetCount << " gadgets\n");
486 if (GadgetCount == 0)
487 return nullptr;
488 NumGadgets += GadgetCount;
489
490 // Traverse CFG to build the rest of the graph
491 SmallPtrSet<MachineBasicBlock *, 8> BlocksVisited;
492 std::function<void(MachineBasicBlock *, GraphIter, unsigned)> TraverseCFG =
493 [&](MachineBasicBlock *MBB, GraphIter GI, unsigned ParentDepth) {
494 unsigned LoopDepth = MLI.getLoopDepth(MBB);
495 if (!MBB->empty()) {
496 // Always add the first instruction in each block
497 auto NI = MBB->begin();
498 auto BeginBB = MaybeAddNode(&*NI);
499 Builder.addEdge(ParentDepth, GI, BeginBB.first);
500 if (!BlocksVisited.insert(MBB).second)
501 return;
502
503 // Add any instructions within the block that are gadget components
504 GI = BeginBB.first;
505 while (++NI != MBB->end()) {
506 auto Ref = NodeMap.find(&*NI);
507 if (Ref != NodeMap.end()) {
508 Builder.addEdge(LoopDepth, GI, Ref->getSecond());
509 GI = Ref->getSecond();
510 }
511 }
512
513 // Always add the terminator instruction, if one exists
514 auto T = MBB->getFirstTerminator();
515 if (T != MBB->end()) {
516 auto EndBB = MaybeAddNode(&*T);
517 if (EndBB.second)
518 Builder.addEdge(LoopDepth, GI, EndBB.first);
519 GI = EndBB.first;
520 }
521 }
522 for (MachineBasicBlock *Succ : MBB->successors())
523 TraverseCFG(Succ, GI, LoopDepth);
524 };
525 // ArgNodeSentinel is a pseudo-instruction that represents MF args in the
526 // GadgetGraph
527 GraphIter ArgNode = MaybeAddNode(MachineGadgetGraph::ArgNodeSentinel).first;
528 TraverseCFG(&MF.front(), ArgNode, 0);
529 std::unique_ptr<MachineGadgetGraph> G{Builder.get(FenceCount, GadgetCount)};
530 LLVM_DEBUG(dbgs() << "Found " << G->nodes_size() << " nodes\n");
531 return G;
532}
533
534// Returns the number of remaining gadget edges that could not be eliminated
535int X86LoadValueInjectionLoadHardeningPass::elimMitigatedEdgesAndNodes(
536 MachineGadgetGraph &G, EdgeSet &ElimEdges /* in, out */,
537 NodeSet &ElimNodes /* in, out */) const {
538 if (G.NumFences > 0) {
539 // Eliminate fences and CFG edges that ingress and egress the fence, as
540 // they are trivially mitigated.
541 for (const Edge &E : G.edges()) {
542 const Node *Dest = E.getDest();
543 if (isFence(Dest->getValue())) {
544 ElimNodes.insert(*Dest);
545 ElimEdges.insert(E);
546 for (const Edge &DE : Dest->edges())
547 ElimEdges.insert(DE);
548 }
549 }
550 }
551
552 // Find and eliminate gadget edges that have been mitigated.
553 int RemainingGadgets = 0;
554 NodeSet ReachableNodes{G};
555 for (const Node &RootN : G.nodes()) {
556 if (llvm::none_of(RootN.edges(), MachineGadgetGraph::isGadgetEdge))
557 continue; // skip this node if it isn't a gadget source
558
559 // Find all of the nodes that are CFG-reachable from RootN using DFS
560 ReachableNodes.clear();
561 std::function<void(const Node *, bool)> FindReachableNodes =
562 [&](const Node *N, bool FirstNode) {
563 if (!FirstNode)
564 ReachableNodes.insert(*N);
565 for (const Edge &E : N->edges()) {
566 const Node *Dest = E.getDest();
567 if (MachineGadgetGraph::isCFGEdge(E) && !ElimEdges.contains(E) &&
568 !ReachableNodes.contains(*Dest))
569 FindReachableNodes(Dest, false);
570 }
571 };
572 FindReachableNodes(&RootN, true);
573
574 // Any gadget whose sink is unreachable has been mitigated
575 for (const Edge &E : RootN.edges()) {
576 if (MachineGadgetGraph::isGadgetEdge(E)) {
577 if (ReachableNodes.contains(*E.getDest())) {
578 // This gadget's sink is reachable
579 ++RemainingGadgets;
580 } else { // This gadget's sink is unreachable, and therefore mitigated
581 ElimEdges.insert(E);
582 }
583 }
584 }
585 }
586 return RemainingGadgets;
587}
588
589std::unique_ptr<MachineGadgetGraph>
590X86LoadValueInjectionLoadHardeningPass::trimMitigatedEdges(
591 std::unique_ptr<MachineGadgetGraph> Graph) const {
592 NodeSet ElimNodes{*Graph};
593 EdgeSet ElimEdges{*Graph};
594 int RemainingGadgets =
595 elimMitigatedEdgesAndNodes(*Graph, ElimEdges, ElimNodes);
596 if (ElimEdges.empty() && ElimNodes.empty()) {
597 Graph->NumFences = 0;
598 Graph->NumGadgets = RemainingGadgets;
599 } else {
600 Graph = GraphBuilder::trim(*Graph, ElimNodes, ElimEdges, 0 /* NumFences */,
601 RemainingGadgets);
602 }
603 return Graph;
604}
605
606int X86LoadValueInjectionLoadHardeningPass::hardenLoadsWithPlugin(
607 MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph) const {
608 int FencesInserted = 0;
609
610 do {
611 LLVM_DEBUG(dbgs() << "Eliminating mitigated paths...\n");
612 Graph = trimMitigatedEdges(std::move(Graph));
613 LLVM_DEBUG(dbgs() << "Eliminating mitigated paths... Done\n");
614 if (Graph->NumGadgets == 0)
615 break;
616
617 LLVM_DEBUG(dbgs() << "Cutting edges...\n");
618 EdgeSet CutEdges{*Graph};
619 auto Nodes = std::make_unique<unsigned int[]>(Graph->nodes_size() +
620 1 /* terminator node */);
621 auto Edges = std::make_unique<unsigned int[]>(Graph->edges_size());
622 auto EdgeCuts = std::make_unique<int[]>(Graph->edges_size());
623 auto EdgeValues = std::make_unique<int[]>(Graph->edges_size());
624 for (const Node &N : Graph->nodes()) {
625 Nodes[Graph->getNodeIndex(N)] = Graph->getEdgeIndex(*N.edges_begin());
626 }
627 Nodes[Graph->nodes_size()] = Graph->edges_size(); // terminator node
628 for (const Edge &E : Graph->edges()) {
629 Edges[Graph->getEdgeIndex(E)] = Graph->getNodeIndex(*E.getDest());
630 EdgeValues[Graph->getEdgeIndex(E)] = E.getValue();
631 }
632 OptimizeCut(Nodes.get(), Graph->nodes_size(), Edges.get(), EdgeValues.get(),
633 EdgeCuts.get(), Graph->edges_size());
634 for (int I = 0; I < Graph->edges_size(); ++I)
635 if (EdgeCuts[I])
636 CutEdges.set(I);
637 LLVM_DEBUG(dbgs() << "Cutting edges... Done\n");
638 LLVM_DEBUG(dbgs() << "Cut " << CutEdges.count() << " edges\n");
639
640 LLVM_DEBUG(dbgs() << "Inserting LFENCEs...\n");
641 FencesInserted += insertFences(MF, *Graph, CutEdges);
642 LLVM_DEBUG(dbgs() << "Inserting LFENCEs... Done\n");
643 LLVM_DEBUG(dbgs() << "Inserted " << FencesInserted << " fences\n");
644
645 Graph = GraphBuilder::trim(*Graph, NodeSet{*Graph}, CutEdges);
646 } while (true);
647
648 return FencesInserted;
649}
650
651int X86LoadValueInjectionLoadHardeningPass::hardenLoadsWithHeuristic(
652 MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph) const {
653 // If `MF` does not have any fences, then no gadgets would have been
654 // mitigated at this point.
655 if (Graph->NumFences > 0) {
656 LLVM_DEBUG(dbgs() << "Eliminating mitigated paths...\n");
657 Graph = trimMitigatedEdges(std::move(Graph));
658 LLVM_DEBUG(dbgs() << "Eliminating mitigated paths... Done\n");
659 }
660
661 if (Graph->NumGadgets == 0)
662 return 0;
663
664 LLVM_DEBUG(dbgs() << "Cutting edges...\n");
665 EdgeSet CutEdges{*Graph};
666
667 // Begin by collecting all ingress CFG edges for each node
668 DenseMap<const Node *, SmallVector<const Edge *, 2>> IngressEdgeMap;
669 for (const Edge &E : Graph->edges())
670 if (MachineGadgetGraph::isCFGEdge(E))
671 IngressEdgeMap[E.getDest()].push_back(&E);
672
673 // For each gadget edge, make cuts that guarantee the gadget will be
674 // mitigated. A computationally efficient way to achieve this is to either:
675 // (a) cut all egress CFG edges from the gadget source, or
676 // (b) cut all ingress CFG edges to the gadget sink.
677 //
678 // Moreover, the algorithm tries not to make a cut into a loop by preferring
679 // to make a (b)-type cut if the gadget source resides at a greater loop depth
680 // than the gadget sink, or an (a)-type cut otherwise.
681 for (const Node &N : Graph->nodes()) {
682 for (const Edge &E : N.edges()) {
683 if (!MachineGadgetGraph::isGadgetEdge(E))
684 continue;
685
687 SmallVector<const Edge *, 2> &IngressEdges = IngressEdgeMap[E.getDest()];
688 for (const Edge &EgressEdge : N.edges())
689 if (MachineGadgetGraph::isCFGEdge(EgressEdge))
690 EgressEdges.push_back(&EgressEdge);
691
692 int EgressCutCost = 0, IngressCutCost = 0;
693 for (const Edge *EgressEdge : EgressEdges)
694 if (!CutEdges.contains(*EgressEdge))
695 EgressCutCost += EgressEdge->getValue();
696 for (const Edge *IngressEdge : IngressEdges)
697 if (!CutEdges.contains(*IngressEdge))
698 IngressCutCost += IngressEdge->getValue();
699
700 auto &EdgesToCut =
701 IngressCutCost < EgressCutCost ? IngressEdges : EgressEdges;
702 for (const Edge *E : EdgesToCut)
703 CutEdges.insert(*E);
704 }
705 }
706 LLVM_DEBUG(dbgs() << "Cutting edges... Done\n");
707 LLVM_DEBUG(dbgs() << "Cut " << CutEdges.count() << " edges\n");
708
709 LLVM_DEBUG(dbgs() << "Inserting LFENCEs...\n");
710 int FencesInserted = insertFences(MF, *Graph, CutEdges);
711 LLVM_DEBUG(dbgs() << "Inserting LFENCEs... Done\n");
712 LLVM_DEBUG(dbgs() << "Inserted " << FencesInserted << " fences\n");
713
714 return FencesInserted;
715}
716
717int X86LoadValueInjectionLoadHardeningPass::insertFences(
718 MachineFunction &MF, MachineGadgetGraph &G,
719 EdgeSet &CutEdges /* in, out */) const {
720 int FencesInserted = 0;
721 for (const Node &N : G.nodes()) {
722 for (const Edge &E : N.edges()) {
723 if (CutEdges.contains(E)) {
724 MachineInstr *MI = N.getValue(), *Prev;
725 MachineBasicBlock *MBB; // Insert an LFENCE in this MBB
726 MachineBasicBlock::iterator InsertionPt; // ...at this point
727 if (MI == MachineGadgetGraph::ArgNodeSentinel) {
728 // insert LFENCE at beginning of entry block
729 MBB = &MF.front();
730 InsertionPt = MBB->begin();
731 Prev = nullptr;
732 } else if (MI->isBranch()) { // insert the LFENCE before the branch
733 MBB = MI->getParent();
734 InsertionPt = MI;
735 Prev = MI->getPrevNode();
736 // Remove all egress CFG edges from this branch because the inserted
737 // LFENCE prevents gadgets from crossing the branch.
738 for (const Edge &E : N.edges()) {
739 if (MachineGadgetGraph::isCFGEdge(E))
740 CutEdges.insert(E);
741 }
742 } else { // insert the LFENCE after the instruction
743 MBB = MI->getParent();
744 InsertionPt = MI->getNextNode() ? MI->getNextNode() : MBB->end();
745 Prev = InsertionPt == MBB->end()
746 ? (MBB->empty() ? nullptr : &MBB->back())
747 : InsertionPt->getPrevNode();
748 }
749 // Ensure this insertion is not redundant (two LFENCEs in sequence).
750 if ((InsertionPt == MBB->end() || !isFence(&*InsertionPt)) &&
751 (!Prev || !isFence(Prev))) {
752 BuildMI(*MBB, InsertionPt, DebugLoc(), TII->get(X86::LFENCE));
753 ++FencesInserted;
754 }
755 }
756 }
757 }
758 return FencesInserted;
759}
760
761bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToAccessMemory(
762 const MachineInstr &MI, Register Reg) const {
763 if (!MI.mayLoadOrStore() || MI.getOpcode() == X86::MFENCE ||
764 MI.getOpcode() == X86::SFENCE || MI.getOpcode() == X86::LFENCE)
765 return false;
766
767 const int MemRefBeginIdx = X86::getFirstAddrOperandIdx(MI);
768 if (MemRefBeginIdx < 0) {
769 LLVM_DEBUG(dbgs() << "Warning: unable to obtain memory operand for loading "
770 "instruction:\n";
771 MI.print(dbgs()); dbgs() << '\n';);
772 return false;
773 }
774
775 const MachineOperand &BaseMO =
776 MI.getOperand(MemRefBeginIdx + X86::AddrBaseReg);
777 const MachineOperand &IndexMO =
778 MI.getOperand(MemRefBeginIdx + X86::AddrIndexReg);
779 return (BaseMO.isReg() && BaseMO.getReg().isValid() &&
780 TRI->regsOverlap(BaseMO.getReg(), Reg)) ||
781 (IndexMO.isReg() && IndexMO.getReg().isValid() &&
782 TRI->regsOverlap(IndexMO.getReg(), Reg));
783}
784
785bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToBranch(
786 const MachineInstr &MI, Register Reg) const {
787 if (!MI.isConditionalBranch())
788 return false;
789 for (const MachineOperand &Use : MI.uses())
790 if (Use.isReg() && Use.getReg() == Reg)
791 return true;
792 return false;
793}
794
795INITIALIZE_PASS_BEGIN(X86LoadValueInjectionLoadHardeningPass, PASS_KEY,
796 "X86 LVI load hardening", false, false)
800INITIALIZE_PASS_END(X86LoadValueInjectionLoadHardeningPass, PASS_KEY,
801 "X86 LVI load hardening", false, false)
802
804 return new X86LoadValueInjectionLoadHardeningPass();
805}
MachineInstrBuilder & UseMI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock & MBB
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file defines the DenseMap class.
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
Description: ImmutableGraph is a fast DAG implementation that cannot be modified, except by creating ...
#define F(x, y, z)
Definition MD5.cpp:55
#define I(x, y, z)
Definition MD5.cpp:58
#define G(x, y, z)
Definition MD5.cpp:56
Register Reg
Register const TargetRegisterInfo * TRI
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define T
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
Remove Loads Into Fake Uses
std::pair< BasicBlock *, BasicBlock * > Edge
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallSet class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
Definition Statistic.h:171
#define LLVM_DEBUG(...)
Definition Debug.h:114
#define PASS_KEY
int(* OptimizeCutT)(unsigned int *Nodes, unsigned int NodesSize, unsigned int *Edges, int *EdgeValues, int *CutEdges, unsigned int EdgesSize)
static cl::opt< bool > EmitDot(PASS_KEY "-dot", cl::desc("For each function, emit a dot graph depicting potential LVI gadgets"), cl::init(false), cl::Hidden)
static cl::opt< std::string > OptimizePluginPath(PASS_KEY "-opt-plugin", cl::desc("Specify a plugin to optimize LFENCE insertion"), cl::Hidden)
static void writeGadgetGraph(raw_ostream &OS, MachineFunction &MF, MachineGadgetGraph *G)
static cl::opt< bool > EmitDotVerify(PASS_KEY "-dot-verify", cl::desc("For each function, emit a dot graph to stdout depicting " "potential LVI gadgets, used for testing purposes only"), cl::init(false), cl::Hidden)
static llvm::sys::DynamicLibrary OptimizeDL
static OptimizeCutT OptimizeCut
static cl::opt< bool > EmitDotOnly(PASS_KEY "-dot-only", cl::desc("For each function, emit a dot graph depicting potential LVI " "gadgets, and do not insert any fences"), cl::init(false), cl::Hidden)
static cl::opt< bool > NoConditionalBranches(PASS_KEY "-no-cbranch", cl::desc("Don't treat conditional branches as disclosure gadgets. This " "may improve performance, at the cost of security."), cl::init(false), cl::Hidden)
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:270
iterator find(const_arg_type_t< KeyT > Val)
Definition DenseMap.h:178
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
Definition DenseMap.h:248
iterator end()
Definition DenseMap.h:81
bool contains(const_arg_type_t< KeyT > Val) const
Return true if the specified key is in the map, false otherwise.
Definition DenseMap.h:169
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
unsigned getLoopDepth(const BlockT *BB) const
Return the loop nesting level of the specified block.
LLVM_ABI iterator getFirstTerminator()
Returns an iterator to the first terminator instruction of this basic block.
iterator_range< succ_iterator > successors()
MachineInstrBundleIterator< MachineInstr > iterator
Analysis pass which computes a MachineDominatorTree.
DominatorTree Class - Concrete subclass of DominatorTreeBase that is used to compute a normal dominat...
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
StringRef getName() const
getName - Return the name of the corresponding LLVM function.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
const MachineBasicBlock & front() const
Representation of each machine instruction.
bool isReg() const
isReg - Tests if this is a MO_Register operand.
MachineInstr * getParent()
getParent - Return the instruction that this operand belongs to.
Register getReg() const
getReg - Returns the register number.
A NodeSet contains a set of SUnit DAG nodes with additional information that assigns a priority to th...
bool insert(SUnit *SU)
bool empty() const
virtual void print(raw_ostream &OS, const Module *M) const
print - Print out the internal state of the pass.
Definition Pass.cpp:140
Wrapper class representing virtual and physical registers.
Definition Register.h:19
constexpr bool isValid() const
Definition Register.h:107
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
Definition SmallSet.h:183
void push_back(const T &Elt)
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
TargetInstrInfo - Interface to description of machine instruction set.
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
const X86InstrInfo * getInstrInfo() const override
const X86RegisterInfo * getRegisterInfo() const override
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
A raw_ostream that writes to an std::string.
std::string & str()
Returns the string's reference.
This class provides a portable interface to dynamic libraries which also might be known as shared lib...
static LLVM_ABI DynamicLibrary getPermanentLibrary(const char *filename, std::string *errMsg=nullptr)
This function permanently loads the dynamic library at the given path using the library load operatio...
LLVM_ABI void * getAddressOfSymbol(const char *symbolName)
Searches through the library for the symbol symbolName.
bool isValid() const
Returns true if the object refers to a valid library.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
int getFirstAddrOperandIdx(const MachineInstr &MI)
Return the index of the instruction's first address operand, if it has a memory reference,...
initializer< Ty > init(const Ty &Val)
NodeAddr< DefNode * > Def
Definition RDFGraph.h:384
NodeAddr< PhiNode * > Phi
Definition RDFGraph.h:390
NodeAddr< UseNode * > Use
Definition RDFGraph.h:385
std::set< NodeId > NodeSet
Definition RDFGraph.h:551
SmallVector< Node, 4 > NodeList
Definition RDFGraph.h:550
This is an optimization pass for GlobalISel generic memory operations.
UnaryFunction for_each(R &&Range, UnaryFunction F)
Provide wrappers to std::for_each which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1718
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
FunctionPass * createX86LoadValueInjectionLoadHardeningPass()
LLVM_ABI raw_fd_ostream & outs()
This returns a reference to a raw_fd_ostream for standard output.
raw_ostream & WriteGraph(raw_ostream &O, const GraphType &G, bool ShortNames=false, const Twine &Title="")
auto unique(Range &&R, Predicate P)
Definition STLExtras.h:2076
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1622
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:207
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1739
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:167
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
@ Ref
The access may reference the value stored in memory.
Definition ModRef.h:32
#define N
static std::string getNodeAttributes(NodeRef Node, GraphType *)
static std::string getEdgeAttributes(NodeRef, ChildIteratorType E, GraphType *)
typename GraphType::UnknownGraphTypeError NodeRef
Definition GraphTraits.h:95
uint16_t getAttrs() const
Definition RDFGraph.h:497
uint16_t getFlags() const
Definition RDFGraph.h:494
MachineOperand & getOp()
Definition RDFGraph.h:558
RegisterRef getRegRef(const DataFlowGraph &G) const
Definition RDFGraph.cpp:401
Node getOwner(const DataFlowGraph &G)
Definition RDFGraph.cpp:427