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 = typename GraphT::Node;
119 using Edge = typename GraphT::Edge;
120 using size_type = typename 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;
194 using NodeRef = typename Traits::NodeRef;
195 using EdgeRef = typename Traits::EdgeRef;
196 using ChildIteratorType = typename Traits::ChildIteratorType;
197 using ChildEdgeIteratorType = typename 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
230constexpr MachineInstr *MachineGadgetGraph::ArgNodeSentinel;
231constexpr int MachineGadgetGraph::GadgetEdgeSentinel;
232
233char X86LoadValueInjectionLoadHardeningPass::ID = 0;
234
235void X86LoadValueInjectionLoadHardeningPass::getAnalysisUsage(
236 AnalysisUsage &AU) const {
238 AU.addRequired<MachineLoopInfoWrapperPass>();
239 AU.addRequired<MachineDominatorTreeWrapperPass>();
240 AU.addRequired<MachineDominanceFrontier>();
241 AU.setPreservesCFG();
242}
243
245 MachineGadgetGraph *G) {
246 WriteGraph(OS, G, /*ShortNames*/ false,
247 "Speculative gadgets for \"" + MF.getName() + "\" function");
248}
249
250bool X86LoadValueInjectionLoadHardeningPass::runOnMachineFunction(
251 MachineFunction &MF) {
252 LLVM_DEBUG(dbgs() << "***** " << getPassName() << " : " << MF.getName()
253 << " *****\n");
254 STI = &MF.getSubtarget<X86Subtarget>();
255 if (!STI->useLVILoadHardening())
256 return false;
257
258 // FIXME: support 32-bit
259 if (!STI->is64Bit())
260 report_fatal_error("LVI load hardening is only supported on 64-bit", false);
261
262 // Don't skip functions with the "optnone" attr but participate in opt-bisect.
263 const Function &F = MF.getFunction();
264 if (!F.hasOptNone() && skipFunction(F))
265 return false;
266
267 ++NumFunctionsConsidered;
268 TII = STI->getInstrInfo();
269 TRI = STI->getRegisterInfo();
270 LLVM_DEBUG(dbgs() << "Building gadget graph...\n");
271 const auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
272 const auto &MDT = getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
273 const auto &MDF = getAnalysis<MachineDominanceFrontier>();
274 std::unique_ptr<MachineGadgetGraph> Graph = getGadgetGraph(MF, MLI, MDT, MDF);
275 LLVM_DEBUG(dbgs() << "Building gadget graph... Done\n");
276 if (Graph == nullptr)
277 return false; // didn't find any gadgets
278
279 if (EmitDotVerify) {
280 writeGadgetGraph(outs(), MF, Graph.get());
281 return false;
282 }
283
284 if (EmitDot || EmitDotOnly) {
285 LLVM_DEBUG(dbgs() << "Emitting gadget graph...\n");
286 std::error_code FileError;
287 std::string FileName = "lvi.";
288 FileName += MF.getName();
289 FileName += ".dot";
290 raw_fd_ostream FileOut(FileName, FileError);
291 if (FileError)
292 errs() << FileError.message();
293 writeGadgetGraph(FileOut, MF, Graph.get());
294 FileOut.close();
295 LLVM_DEBUG(dbgs() << "Emitting gadget graph... Done\n");
296 if (EmitDotOnly)
297 return false;
298 }
299
300 int FencesInserted;
301 if (!OptimizePluginPath.empty()) {
302 if (!OptimizeDL.isValid()) {
303 std::string ErrorMsg;
305 OptimizePluginPath.c_str(), &ErrorMsg);
306 if (!ErrorMsg.empty())
307 report_fatal_error(Twine("Failed to load opt plugin: \"") + ErrorMsg +
308 "\"");
310 if (!OptimizeCut)
311 report_fatal_error("Invalid optimization plugin");
312 }
313 FencesInserted = hardenLoadsWithPlugin(MF, std::move(Graph));
314 } else { // Use the default greedy heuristic
315 FencesInserted = hardenLoadsWithHeuristic(MF, std::move(Graph));
316 }
317
318 if (FencesInserted > 0)
319 ++NumFunctionsMitigated;
320 NumFences += FencesInserted;
321 return (FencesInserted > 0);
322}
323
324std::unique_ptr<MachineGadgetGraph>
325X86LoadValueInjectionLoadHardeningPass::getGadgetGraph(
326 MachineFunction &MF, const MachineLoopInfo &MLI,
327 const MachineDominatorTree &MDT,
328 const MachineDominanceFrontier &MDF) const {
329 using namespace rdf;
330
331 // Build the Register Dataflow Graph using the RDF framework
332 DataFlowGraph DFG{MF, *TII, *TRI, MDT, MDF};
333 DFG.build();
334 Liveness L{MF.getRegInfo(), DFG};
335 L.computePhiInfo();
336
337 GraphBuilder Builder;
338 using GraphIter = typename GraphBuilder::BuilderNodeRef;
339 DenseMap<MachineInstr *, GraphIter> NodeMap;
340 int FenceCount = 0, GadgetCount = 0;
341 auto MaybeAddNode = [&NodeMap, &Builder](MachineInstr *MI) {
342 auto [Ref, Inserted] = NodeMap.try_emplace(MI);
343 if (Inserted) {
344 auto I = Builder.addVertex(MI);
345 Ref->second = I;
346 return std::pair<GraphIter, bool>{I, true};
347 }
348 return std::pair<GraphIter, bool>{Ref->getSecond(), false};
349 };
350
351 // The `Transmitters` map memoizes transmitters found for each def. If a def
352 // has not yet been analyzed, then it will not appear in the map. If a def
353 // has been analyzed and was determined not to have any transmitters, then
354 // its list of transmitters will be empty.
355 DenseMap<NodeId, std::vector<NodeId>> Transmitters;
356
357 // Analyze all machine instructions to find gadgets and LFENCEs, adding
358 // each interesting value to `Nodes`
359 auto AnalyzeDef = [&](NodeAddr<DefNode *> SourceDef) {
360 SmallSet<NodeId, 8> UsesVisited, DefsVisited;
361 std::function<void(NodeAddr<DefNode *>)> AnalyzeDefUseChain =
362 [&](NodeAddr<DefNode *> Def) {
363 if (Transmitters.contains(Def.Id))
364 return; // Already analyzed `Def`
365
366 // Use RDF to find all the uses of `Def`
368 RegisterRef DefReg = Def.Addr->getRegRef(DFG);
369 for (auto UseID : L.getAllReachedUses(DefReg, Def)) {
370 auto Use = DFG.addr<UseNode *>(UseID);
371 if (Use.Addr->getFlags() & NodeAttrs::PhiRef) { // phi node
372 NodeAddr<PhiNode *> Phi = Use.Addr->getOwner(DFG);
373 for (const auto& I : L.getRealUses(Phi.Id)) {
374 if (DFG.getPRI().alias(RegisterRef(I.first), DefReg)) {
375 for (const auto &UA : I.second)
376 Uses.emplace(UA.first);
377 }
378 }
379 } else { // not a phi node
380 Uses.emplace(UseID);
381 }
382 }
383
384 // For each use of `Def`, we want to know whether:
385 // (1) The use can leak the Def'ed value,
386 // (2) The use can further propagate the Def'ed value to more defs
387 for (auto UseID : Uses) {
388 if (!UsesVisited.insert(UseID).second)
389 continue; // Already visited this use of `Def`
390
391 auto Use = DFG.addr<UseNode *>(UseID);
392 assert(!(Use.Addr->getFlags() & NodeAttrs::PhiRef));
393 MachineOperand &UseMO = Use.Addr->getOp();
394 MachineInstr &UseMI = *UseMO.getParent();
395 assert(UseMO.isReg());
396
397 // We naively assume that an instruction propagates any loaded
398 // uses to all defs unless the instruction is a call, in which
399 // case all arguments will be treated as gadget sources during
400 // analysis of the callee function.
401 if (UseMI.isCall())
402 continue;
403
404 // Check whether this use can transmit (leak) its value.
405 if (instrUsesRegToAccessMemory(UseMI, UseMO.getReg()) ||
407 instrUsesRegToBranch(UseMI, UseMO.getReg()))) {
408 Transmitters[Def.Id].push_back(Use.Addr->getOwner(DFG).Id);
409 if (UseMI.mayLoad())
410 continue; // Found a transmitting load -- no need to continue
411 // traversing its defs (i.e., this load will become
412 // a new gadget source anyways).
413 }
414
415 // Check whether the use propagates to more defs.
416 NodeAddr<InstrNode *> Owner{Use.Addr->getOwner(DFG)};
417 for (const auto &ChildDef :
418 Owner.Addr->members_if(DataFlowGraph::IsDef, DFG)) {
419 if (!DefsVisited.insert(ChildDef.Id).second)
420 continue; // Already visited this def
421 if (Def.Addr->getAttrs() & NodeAttrs::Dead)
422 continue;
423 if (Def.Id == ChildDef.Id)
424 continue; // `Def` uses itself (e.g., increment loop counter)
425
426 AnalyzeDefUseChain(ChildDef);
427
428 // `Def` inherits all of its child defs' transmitters.
429 for (auto TransmitterId : Transmitters[ChildDef.Id])
430 Transmitters[Def.Id].push_back(TransmitterId);
431 }
432 }
433
434 // Note that this statement adds `Def.Id` to the map if no
435 // transmitters were found for `Def`.
436 auto &DefTransmitters = Transmitters[Def.Id];
437
438 // Remove duplicate transmitters
439 llvm::sort(DefTransmitters);
440 DefTransmitters.erase(llvm::unique(DefTransmitters),
441 DefTransmitters.end());
442 };
443
444 // Find all of the transmitters
445 AnalyzeDefUseChain(SourceDef);
446 auto &SourceDefTransmitters = Transmitters[SourceDef.Id];
447 if (SourceDefTransmitters.empty())
448 return; // No transmitters for `SourceDef`
449
450 MachineInstr *Source = SourceDef.Addr->getFlags() & NodeAttrs::PhiRef
451 ? MachineGadgetGraph::ArgNodeSentinel
452 : SourceDef.Addr->getOp().getParent();
453 auto GadgetSource = MaybeAddNode(Source);
454 // Each transmitter is a sink for `SourceDef`.
455 for (auto TransmitterId : SourceDefTransmitters) {
456 MachineInstr *Sink = DFG.addr<StmtNode *>(TransmitterId).Addr->getCode();
457 auto GadgetSink = MaybeAddNode(Sink);
458 // Add the gadget edge to the graph.
459 Builder.addEdge(MachineGadgetGraph::GadgetEdgeSentinel,
460 GadgetSource.first, GadgetSink.first);
461 ++GadgetCount;
462 }
463 };
464
465 LLVM_DEBUG(dbgs() << "Analyzing def-use chains to find gadgets\n");
466 // Analyze function arguments
467 NodeAddr<BlockNode *> EntryBlock = DFG.getFunc().Addr->getEntryBlock(DFG);
468 for (NodeAddr<PhiNode *> ArgPhi :
469 EntryBlock.Addr->members_if(DataFlowGraph::IsPhi, DFG)) {
470 NodeList Defs = ArgPhi.Addr->members_if(DataFlowGraph::IsDef, DFG);
471 llvm::for_each(Defs, AnalyzeDef);
472 }
473 // Analyze every instruction in MF
474 for (NodeAddr<BlockNode *> BA : DFG.getFunc().Addr->members(DFG)) {
475 for (NodeAddr<StmtNode *> SA :
476 BA.Addr->members_if(DataFlowGraph::IsCode<NodeAttrs::Stmt>, DFG)) {
477 MachineInstr *MI = SA.Addr->getCode();
478 if (isFence(MI)) {
479 MaybeAddNode(MI);
480 ++FenceCount;
481 } else if (MI->mayLoad()) {
482 NodeList Defs = SA.Addr->members_if(DataFlowGraph::IsDef, DFG);
483 llvm::for_each(Defs, AnalyzeDef);
484 }
485 }
486 }
487 LLVM_DEBUG(dbgs() << "Found " << FenceCount << " fences\n");
488 LLVM_DEBUG(dbgs() << "Found " << GadgetCount << " gadgets\n");
489 if (GadgetCount == 0)
490 return nullptr;
491 NumGadgets += GadgetCount;
492
493 // Traverse CFG to build the rest of the graph
494 SmallPtrSet<MachineBasicBlock *, 8> BlocksVisited;
495 std::function<void(MachineBasicBlock *, GraphIter, unsigned)> TraverseCFG =
496 [&](MachineBasicBlock *MBB, GraphIter GI, unsigned ParentDepth) {
497 unsigned LoopDepth = MLI.getLoopDepth(MBB);
498 if (!MBB->empty()) {
499 // Always add the first instruction in each block
500 auto NI = MBB->begin();
501 auto BeginBB = MaybeAddNode(&*NI);
502 Builder.addEdge(ParentDepth, GI, BeginBB.first);
503 if (!BlocksVisited.insert(MBB).second)
504 return;
505
506 // Add any instructions within the block that are gadget components
507 GI = BeginBB.first;
508 while (++NI != MBB->end()) {
509 auto Ref = NodeMap.find(&*NI);
510 if (Ref != NodeMap.end()) {
511 Builder.addEdge(LoopDepth, GI, Ref->getSecond());
512 GI = Ref->getSecond();
513 }
514 }
515
516 // Always add the terminator instruction, if one exists
517 auto T = MBB->getFirstTerminator();
518 if (T != MBB->end()) {
519 auto EndBB = MaybeAddNode(&*T);
520 if (EndBB.second)
521 Builder.addEdge(LoopDepth, GI, EndBB.first);
522 GI = EndBB.first;
523 }
524 }
525 for (MachineBasicBlock *Succ : MBB->successors())
526 TraverseCFG(Succ, GI, LoopDepth);
527 };
528 // ArgNodeSentinel is a pseudo-instruction that represents MF args in the
529 // GadgetGraph
530 GraphIter ArgNode = MaybeAddNode(MachineGadgetGraph::ArgNodeSentinel).first;
531 TraverseCFG(&MF.front(), ArgNode, 0);
532 std::unique_ptr<MachineGadgetGraph> G{Builder.get(FenceCount, GadgetCount)};
533 LLVM_DEBUG(dbgs() << "Found " << G->nodes_size() << " nodes\n");
534 return G;
535}
536
537// Returns the number of remaining gadget edges that could not be eliminated
538int X86LoadValueInjectionLoadHardeningPass::elimMitigatedEdgesAndNodes(
539 MachineGadgetGraph &G, EdgeSet &ElimEdges /* in, out */,
540 NodeSet &ElimNodes /* in, out */) const {
541 if (G.NumFences > 0) {
542 // Eliminate fences and CFG edges that ingress and egress the fence, as
543 // they are trivially mitigated.
544 for (const Edge &E : G.edges()) {
545 const Node *Dest = E.getDest();
546 if (isFence(Dest->getValue())) {
547 ElimNodes.insert(*Dest);
548 ElimEdges.insert(E);
549 for (const Edge &DE : Dest->edges())
550 ElimEdges.insert(DE);
551 }
552 }
553 }
554
555 // Find and eliminate gadget edges that have been mitigated.
556 int RemainingGadgets = 0;
557 NodeSet ReachableNodes{G};
558 for (const Node &RootN : G.nodes()) {
559 if (llvm::none_of(RootN.edges(), MachineGadgetGraph::isGadgetEdge))
560 continue; // skip this node if it isn't a gadget source
561
562 // Find all of the nodes that are CFG-reachable from RootN using DFS
563 ReachableNodes.clear();
564 std::function<void(const Node *, bool)> FindReachableNodes =
565 [&](const Node *N, bool FirstNode) {
566 if (!FirstNode)
567 ReachableNodes.insert(*N);
568 for (const Edge &E : N->edges()) {
569 const Node *Dest = E.getDest();
570 if (MachineGadgetGraph::isCFGEdge(E) && !ElimEdges.contains(E) &&
571 !ReachableNodes.contains(*Dest))
572 FindReachableNodes(Dest, false);
573 }
574 };
575 FindReachableNodes(&RootN, true);
576
577 // Any gadget whose sink is unreachable has been mitigated
578 for (const Edge &E : RootN.edges()) {
579 if (MachineGadgetGraph::isGadgetEdge(E)) {
580 if (ReachableNodes.contains(*E.getDest())) {
581 // This gadget's sink is reachable
582 ++RemainingGadgets;
583 } else { // This gadget's sink is unreachable, and therefore mitigated
584 ElimEdges.insert(E);
585 }
586 }
587 }
588 }
589 return RemainingGadgets;
590}
591
592std::unique_ptr<MachineGadgetGraph>
593X86LoadValueInjectionLoadHardeningPass::trimMitigatedEdges(
594 std::unique_ptr<MachineGadgetGraph> Graph) const {
595 NodeSet ElimNodes{*Graph};
596 EdgeSet ElimEdges{*Graph};
597 int RemainingGadgets =
598 elimMitigatedEdgesAndNodes(*Graph, ElimEdges, ElimNodes);
599 if (ElimEdges.empty() && ElimNodes.empty()) {
600 Graph->NumFences = 0;
601 Graph->NumGadgets = RemainingGadgets;
602 } else {
603 Graph = GraphBuilder::trim(*Graph, ElimNodes, ElimEdges, 0 /* NumFences */,
604 RemainingGadgets);
605 }
606 return Graph;
607}
608
609int X86LoadValueInjectionLoadHardeningPass::hardenLoadsWithPlugin(
610 MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph) const {
611 int FencesInserted = 0;
612
613 do {
614 LLVM_DEBUG(dbgs() << "Eliminating mitigated paths...\n");
615 Graph = trimMitigatedEdges(std::move(Graph));
616 LLVM_DEBUG(dbgs() << "Eliminating mitigated paths... Done\n");
617 if (Graph->NumGadgets == 0)
618 break;
619
620 LLVM_DEBUG(dbgs() << "Cutting edges...\n");
621 EdgeSet CutEdges{*Graph};
622 auto Nodes = std::make_unique<unsigned int[]>(Graph->nodes_size() +
623 1 /* terminator node */);
624 auto Edges = std::make_unique<unsigned int[]>(Graph->edges_size());
625 auto EdgeCuts = std::make_unique<int[]>(Graph->edges_size());
626 auto EdgeValues = std::make_unique<int[]>(Graph->edges_size());
627 for (const Node &N : Graph->nodes()) {
628 Nodes[Graph->getNodeIndex(N)] = Graph->getEdgeIndex(*N.edges_begin());
629 }
630 Nodes[Graph->nodes_size()] = Graph->edges_size(); // terminator node
631 for (const Edge &E : Graph->edges()) {
632 Edges[Graph->getEdgeIndex(E)] = Graph->getNodeIndex(*E.getDest());
633 EdgeValues[Graph->getEdgeIndex(E)] = E.getValue();
634 }
635 OptimizeCut(Nodes.get(), Graph->nodes_size(), Edges.get(), EdgeValues.get(),
636 EdgeCuts.get(), Graph->edges_size());
637 for (int I = 0; I < Graph->edges_size(); ++I)
638 if (EdgeCuts[I])
639 CutEdges.set(I);
640 LLVM_DEBUG(dbgs() << "Cutting edges... Done\n");
641 LLVM_DEBUG(dbgs() << "Cut " << CutEdges.count() << " edges\n");
642
643 LLVM_DEBUG(dbgs() << "Inserting LFENCEs...\n");
644 FencesInserted += insertFences(MF, *Graph, CutEdges);
645 LLVM_DEBUG(dbgs() << "Inserting LFENCEs... Done\n");
646 LLVM_DEBUG(dbgs() << "Inserted " << FencesInserted << " fences\n");
647
648 Graph = GraphBuilder::trim(*Graph, NodeSet{*Graph}, CutEdges);
649 } while (true);
650
651 return FencesInserted;
652}
653
654int X86LoadValueInjectionLoadHardeningPass::hardenLoadsWithHeuristic(
655 MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph) const {
656 // If `MF` does not have any fences, then no gadgets would have been
657 // mitigated at this point.
658 if (Graph->NumFences > 0) {
659 LLVM_DEBUG(dbgs() << "Eliminating mitigated paths...\n");
660 Graph = trimMitigatedEdges(std::move(Graph));
661 LLVM_DEBUG(dbgs() << "Eliminating mitigated paths... Done\n");
662 }
663
664 if (Graph->NumGadgets == 0)
665 return 0;
666
667 LLVM_DEBUG(dbgs() << "Cutting edges...\n");
668 EdgeSet CutEdges{*Graph};
669
670 // Begin by collecting all ingress CFG edges for each node
671 DenseMap<const Node *, SmallVector<const Edge *, 2>> IngressEdgeMap;
672 for (const Edge &E : Graph->edges())
673 if (MachineGadgetGraph::isCFGEdge(E))
674 IngressEdgeMap[E.getDest()].push_back(&E);
675
676 // For each gadget edge, make cuts that guarantee the gadget will be
677 // mitigated. A computationally efficient way to achieve this is to either:
678 // (a) cut all egress CFG edges from the gadget source, or
679 // (b) cut all ingress CFG edges to the gadget sink.
680 //
681 // Moreover, the algorithm tries not to make a cut into a loop by preferring
682 // to make a (b)-type cut if the gadget source resides at a greater loop depth
683 // than the gadget sink, or an (a)-type cut otherwise.
684 for (const Node &N : Graph->nodes()) {
685 for (const Edge &E : N.edges()) {
686 if (!MachineGadgetGraph::isGadgetEdge(E))
687 continue;
688
690 SmallVector<const Edge *, 2> &IngressEdges = IngressEdgeMap[E.getDest()];
691 for (const Edge &EgressEdge : N.edges())
692 if (MachineGadgetGraph::isCFGEdge(EgressEdge))
693 EgressEdges.push_back(&EgressEdge);
694
695 int EgressCutCost = 0, IngressCutCost = 0;
696 for (const Edge *EgressEdge : EgressEdges)
697 if (!CutEdges.contains(*EgressEdge))
698 EgressCutCost += EgressEdge->getValue();
699 for (const Edge *IngressEdge : IngressEdges)
700 if (!CutEdges.contains(*IngressEdge))
701 IngressCutCost += IngressEdge->getValue();
702
703 auto &EdgesToCut =
704 IngressCutCost < EgressCutCost ? IngressEdges : EgressEdges;
705 for (const Edge *E : EdgesToCut)
706 CutEdges.insert(*E);
707 }
708 }
709 LLVM_DEBUG(dbgs() << "Cutting edges... Done\n");
710 LLVM_DEBUG(dbgs() << "Cut " << CutEdges.count() << " edges\n");
711
712 LLVM_DEBUG(dbgs() << "Inserting LFENCEs...\n");
713 int FencesInserted = insertFences(MF, *Graph, CutEdges);
714 LLVM_DEBUG(dbgs() << "Inserting LFENCEs... Done\n");
715 LLVM_DEBUG(dbgs() << "Inserted " << FencesInserted << " fences\n");
716
717 return FencesInserted;
718}
719
720int X86LoadValueInjectionLoadHardeningPass::insertFences(
721 MachineFunction &MF, MachineGadgetGraph &G,
722 EdgeSet &CutEdges /* in, out */) const {
723 int FencesInserted = 0;
724 for (const Node &N : G.nodes()) {
725 for (const Edge &E : N.edges()) {
726 if (CutEdges.contains(E)) {
727 MachineInstr *MI = N.getValue(), *Prev;
728 MachineBasicBlock *MBB; // Insert an LFENCE in this MBB
729 MachineBasicBlock::iterator InsertionPt; // ...at this point
730 if (MI == MachineGadgetGraph::ArgNodeSentinel) {
731 // insert LFENCE at beginning of entry block
732 MBB = &MF.front();
733 InsertionPt = MBB->begin();
734 Prev = nullptr;
735 } else if (MI->isBranch()) { // insert the LFENCE before the branch
736 MBB = MI->getParent();
737 InsertionPt = MI;
738 Prev = MI->getPrevNode();
739 // Remove all egress CFG edges from this branch because the inserted
740 // LFENCE prevents gadgets from crossing the branch.
741 for (const Edge &E : N.edges()) {
742 if (MachineGadgetGraph::isCFGEdge(E))
743 CutEdges.insert(E);
744 }
745 } else { // insert the LFENCE after the instruction
746 MBB = MI->getParent();
747 InsertionPt = MI->getNextNode() ? MI->getNextNode() : MBB->end();
748 Prev = InsertionPt == MBB->end()
749 ? (MBB->empty() ? nullptr : &MBB->back())
750 : InsertionPt->getPrevNode();
751 }
752 // Ensure this insertion is not redundant (two LFENCEs in sequence).
753 if ((InsertionPt == MBB->end() || !isFence(&*InsertionPt)) &&
754 (!Prev || !isFence(Prev))) {
755 BuildMI(*MBB, InsertionPt, DebugLoc(), TII->get(X86::LFENCE));
756 ++FencesInserted;
757 }
758 }
759 }
760 }
761 return FencesInserted;
762}
763
764bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToAccessMemory(
765 const MachineInstr &MI, Register Reg) const {
766 if (!MI.mayLoadOrStore() || MI.getOpcode() == X86::MFENCE ||
767 MI.getOpcode() == X86::SFENCE || MI.getOpcode() == X86::LFENCE)
768 return false;
769
770 const int MemRefBeginIdx = X86::getFirstAddrOperandIdx(MI);
771 if (MemRefBeginIdx < 0) {
772 LLVM_DEBUG(dbgs() << "Warning: unable to obtain memory operand for loading "
773 "instruction:\n";
774 MI.print(dbgs()); dbgs() << '\n';);
775 return false;
776 }
777
778 const MachineOperand &BaseMO =
779 MI.getOperand(MemRefBeginIdx + X86::AddrBaseReg);
780 const MachineOperand &IndexMO =
781 MI.getOperand(MemRefBeginIdx + X86::AddrIndexReg);
782 return (BaseMO.isReg() && BaseMO.getReg().isValid() &&
783 TRI->regsOverlap(BaseMO.getReg(), Reg)) ||
784 (IndexMO.isReg() && IndexMO.getReg().isValid() &&
785 TRI->regsOverlap(IndexMO.getReg(), Reg));
786}
787
788bool X86LoadValueInjectionLoadHardeningPass::instrUsesRegToBranch(
789 const MachineInstr &MI, Register Reg) const {
790 if (!MI.isConditionalBranch())
791 return false;
792 for (const MachineOperand &Use : MI.uses())
793 if (Use.isReg() && Use.getReg() == Reg)
794 return true;
795 return false;
796}
797
798INITIALIZE_PASS_BEGIN(X86LoadValueInjectionLoadHardeningPass, PASS_KEY,
799 "X86 LVI load hardening", false, false)
803INITIALIZE_PASS_END(X86LoadValueInjectionLoadHardeningPass, PASS_KEY,
804 "X86 LVI load hardening", false, false)
805
807 return new X86LoadValueInjectionLoadHardeningPass();
808}
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:165
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
Definition DenseMap.h:229
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:156
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.
const MachineBasicBlock * getParent() const
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:181
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:1698
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:2056
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1624
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:1719
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 *)
DefaultDOTGraphTraits(bool simple=false)
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