LLVM 23.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
136constexpr StringRef X86LVILHPassName =
137 "X86 Load Value Injection (LVI) Load Hardening";
138
139class X86LoadValueInjectionLoadHardeningLegacy : public MachineFunctionPass {
140public:
141 X86LoadValueInjectionLoadHardeningLegacy() : MachineFunctionPass(ID) {}
142
143 StringRef getPassName() const override { return X86LVILHPassName; }
144 void getAnalysisUsage(AnalysisUsage &AU) const override;
145 bool runOnMachineFunction(MachineFunction &MF) override;
146
147 static char ID;
148};
149
150class X86LoadValueInjectionLoadHardeningImpl {
151public:
152 X86LoadValueInjectionLoadHardeningImpl() = default;
153
154 bool run(MachineFunction &MF, const MachineLoopInfo &MLI,
155 const MachineDominatorTree &MDT,
156 const MachineDominanceFrontier &MDF);
157
158private:
160 using Edge = MachineGadgetGraph::Edge;
161 using Node = MachineGadgetGraph::Node;
162 using EdgeSet = MachineGadgetGraph::EdgeSet;
163 using NodeSet = MachineGadgetGraph::NodeSet;
164
165 const X86Subtarget *STI = nullptr;
166 const TargetInstrInfo *TII = nullptr;
167 const TargetRegisterInfo *TRI = nullptr;
168
169 std::unique_ptr<MachineGadgetGraph>
170 getGadgetGraph(MachineFunction &MF, const MachineLoopInfo &MLI,
171 const MachineDominatorTree &MDT,
172 const MachineDominanceFrontier &MDF) const;
173 int hardenLoadsWithPlugin(MachineFunction &MF,
174 std::unique_ptr<MachineGadgetGraph> Graph) const;
175 int hardenLoadsWithHeuristic(MachineFunction &MF,
176 std::unique_ptr<MachineGadgetGraph> Graph) const;
177 int elimMitigatedEdgesAndNodes(MachineGadgetGraph &G,
178 EdgeSet &ElimEdges /* in, out */,
179 NodeSet &ElimNodes /* in, out */) const;
180 std::unique_ptr<MachineGadgetGraph>
181 trimMitigatedEdges(std::unique_ptr<MachineGadgetGraph> Graph) const;
182 int insertFences(MachineFunction &MF, MachineGadgetGraph &G,
183 EdgeSet &CutEdges /* in, out */) const;
184 bool instrUsesRegToAccessMemory(const MachineInstr &I, Register Reg) const;
185 bool instrUsesRegToBranch(const MachineInstr &I, Register Reg) const;
186 inline bool isFence(const MachineInstr *MI) const {
187 return MI && (MI->getOpcode() == X86::LFENCE ||
188 (STI->useLVIControlFlowIntegrity() && MI->isCall()));
189 }
190};
191
192} // end anonymous namespace
193
194namespace llvm {
195
196template <>
197struct GraphTraits<MachineGadgetGraph *>
199
200template <>
201struct DOTGraphTraits<MachineGadgetGraph *> : DefaultDOTGraphTraits {
202 using GraphType = MachineGadgetGraph;
205 using EdgeRef = Traits::EdgeRef;
206 using ChildIteratorType = Traits::ChildIteratorType;
207 using ChildEdgeIteratorType = Traits::ChildEdgeIteratorType;
208
209 DOTGraphTraits(bool IsSimple = false) : DefaultDOTGraphTraits(IsSimple) {}
210
212 if (Node->getValue() == MachineGadgetGraph::ArgNodeSentinel)
213 return "ARGS";
214
215 std::string Str;
216 raw_string_ostream OS(Str);
217 OS << *Node->getValue();
218 return OS.str();
219 }
220
221 static std::string getNodeAttributes(NodeRef Node, GraphType *) {
222 MachineInstr *MI = Node->getValue();
223 if (MI == MachineGadgetGraph::ArgNodeSentinel)
224 return "color = blue";
225 if (MI->getOpcode() == X86::LFENCE)
226 return "color = green";
227 return "";
228 }
229
231 GraphType *) {
232 int EdgeVal = (*E.getCurrent()).getValue();
233 return EdgeVal >= 0 ? "label = " + std::to_string(EdgeVal)
234 : "color = red, style = \"dashed\"";
235 }
236};
237
238} // end namespace llvm
239
240char X86LoadValueInjectionLoadHardeningLegacy::ID = 0;
241
242void X86LoadValueInjectionLoadHardeningLegacy::getAnalysisUsage(
243 AnalysisUsage &AU) const {
245 AU.addRequired<MachineLoopInfoWrapperPass>();
246 AU.addRequired<MachineDominatorTreeWrapperPass>();
247 AU.addRequired<MachineDominanceFrontierWrapperPass>();
248 AU.setPreservesCFG();
249}
250
252 MachineGadgetGraph *G) {
253 WriteGraph(OS, G, /*ShortNames*/ false,
254 "Speculative gadgets for \"" + MF.getName() + "\" function");
255}
256
257bool X86LoadValueInjectionLoadHardeningImpl::run(
258 MachineFunction &MF, const MachineLoopInfo &MLI,
259 const MachineDominatorTree &MDT, const MachineDominanceFrontier &MDF) {
260 LLVM_DEBUG(dbgs() << "***** " << X86LVILHPassName << " : " << MF.getName()
261 << " *****\n");
262 STI = &MF.getSubtarget<X86Subtarget>();
263
264 // FIXME: support 32-bit
265 if (!STI->is64Bit())
266 report_fatal_error("LVI load hardening is only supported on 64-bit", false);
267
268 ++NumFunctionsConsidered;
269 TII = STI->getInstrInfo();
270 TRI = STI->getRegisterInfo();
271 LLVM_DEBUG(dbgs() << "Building gadget graph...\n");
272 std::unique_ptr<MachineGadgetGraph> Graph = getGadgetGraph(MF, MLI, MDT, MDF);
273 LLVM_DEBUG(dbgs() << "Building gadget graph... Done\n");
274 if (Graph == nullptr)
275 return false; // didn't find any gadgets
276
277 if (EmitDotVerify) {
278 writeGadgetGraph(outs(), MF, Graph.get());
279 return false;
280 }
281
282 if (EmitDot || EmitDotOnly) {
283 LLVM_DEBUG(dbgs() << "Emitting gadget graph...\n");
284 std::error_code FileError;
285 std::string FileName = "lvi.";
286 FileName += MF.getName();
287 FileName += ".dot";
288 raw_fd_ostream FileOut(FileName, FileError);
289 if (FileError)
290 errs() << FileError.message();
291 writeGadgetGraph(FileOut, MF, Graph.get());
292 FileOut.close();
293 LLVM_DEBUG(dbgs() << "Emitting gadget graph... Done\n");
294 if (EmitDotOnly)
295 return false;
296 }
297
298 int FencesInserted;
299 if (!OptimizePluginPath.empty()) {
300 if (!OptimizeDL.isValid()) {
301 std::string ErrorMsg;
303 OptimizePluginPath.c_str(), &ErrorMsg);
304 if (!ErrorMsg.empty())
305 report_fatal_error(Twine("Failed to load opt plugin: \"") + ErrorMsg +
306 "\"");
308 if (!OptimizeCut)
309 report_fatal_error("Invalid optimization plugin");
310 }
311 FencesInserted = hardenLoadsWithPlugin(MF, std::move(Graph));
312 } else { // Use the default greedy heuristic
313 FencesInserted = hardenLoadsWithHeuristic(MF, std::move(Graph));
314 }
315
316 if (FencesInserted > 0)
317 ++NumFunctionsMitigated;
318 NumFences += FencesInserted;
319 return (FencesInserted > 0);
320}
321
322std::unique_ptr<MachineGadgetGraph>
323X86LoadValueInjectionLoadHardeningImpl::getGadgetGraph(
324 MachineFunction &MF, const MachineLoopInfo &MLI,
325 const MachineDominatorTree &MDT,
326 const MachineDominanceFrontier &MDF) const {
327 using namespace rdf;
328
329 // Build the Register Dataflow Graph using the RDF framework
330 DataFlowGraph DFG{MF, *TII, *TRI, MDT, MDF};
331 DFG.build();
332 Liveness L{MF.getRegInfo(), DFG};
333 L.computePhiInfo();
334
335 GraphBuilder Builder;
336 using GraphIter = GraphBuilder::BuilderNodeRef;
337 DenseMap<MachineInstr *, GraphIter> NodeMap;
338 int FenceCount = 0, GadgetCount = 0;
339 auto MaybeAddNode = [&NodeMap, &Builder](MachineInstr *MI) {
340 auto [Ref, Inserted] = NodeMap.try_emplace(MI);
341 if (Inserted) {
342 auto I = Builder.addVertex(MI);
343 Ref->second = I;
344 return std::pair<GraphIter, bool>{I, true};
345 }
346 return std::pair<GraphIter, bool>{Ref->getSecond(), false};
347 };
348
349 // The `Transmitters` map memoizes transmitters found for each def. If a def
350 // has not yet been analyzed, then it will not appear in the map. If a def
351 // has been analyzed and was determined not to have any transmitters, then
352 // its list of transmitters will be empty.
353 DenseMap<NodeId, std::vector<NodeId>> Transmitters;
354
355 // Analyze all machine instructions to find gadgets and LFENCEs, adding
356 // each interesting value to `Nodes`
357 auto AnalyzeDef = [&](NodeAddr<DefNode *> SourceDef) {
358 SmallSet<NodeId, 8> UsesVisited, DefsVisited;
359 std::function<void(NodeAddr<DefNode *>)> AnalyzeDefUseChain =
360 [&](NodeAddr<DefNode *> Def) {
361 if (Transmitters.contains(Def.Id))
362 return; // Already analyzed `Def`
363
364 // Use RDF to find all the uses of `Def`
366 RegisterRef DefReg = Def.Addr->getRegRef(DFG);
367 for (auto UseID : L.getAllReachedUses(DefReg, Def)) {
368 auto Use = DFG.addr<UseNode *>(UseID);
369 if (Use.Addr->getFlags() & NodeAttrs::PhiRef) { // phi node
370 NodeAddr<PhiNode *> Phi = Use.Addr->getOwner(DFG);
371 for (const auto& I : L.getRealUses(Phi.Id)) {
372 if (DFG.getPRI().alias(RegisterRef(I.first), DefReg)) {
373 for (const auto &UA : I.second)
374 Uses.emplace(UA.first);
375 }
376 }
377 } else { // not a phi node
378 Uses.emplace(UseID);
379 }
380 }
381
382 // For each use of `Def`, we want to know whether:
383 // (1) The use can leak the Def'ed value,
384 // (2) The use can further propagate the Def'ed value to more defs
385 for (auto UseID : Uses) {
386 if (!UsesVisited.insert(UseID).second)
387 continue; // Already visited this use of `Def`
388
389 auto Use = DFG.addr<UseNode *>(UseID);
390 assert(!(Use.Addr->getFlags() & NodeAttrs::PhiRef));
391 MachineOperand &UseMO = Use.Addr->getOp();
392 MachineInstr &UseMI = *UseMO.getParent();
393 assert(UseMO.isReg());
394
395 // We naively assume that an instruction propagates any loaded
396 // uses to all defs unless the instruction is a call, in which
397 // case all arguments will be treated as gadget sources during
398 // analysis of the callee function.
399 if (UseMI.isCall())
400 continue;
401
402 // Check whether this use can transmit (leak) its value.
403 if (instrUsesRegToAccessMemory(UseMI, UseMO.getReg()) ||
405 instrUsesRegToBranch(UseMI, UseMO.getReg()))) {
406 Transmitters[Def.Id].push_back(Use.Addr->getOwner(DFG).Id);
407 if (UseMI.mayLoad())
408 continue; // Found a transmitting load -- no need to continue
409 // traversing its defs (i.e., this load will become
410 // a new gadget source anyways).
411 }
412
413 // Check whether the use propagates to more defs.
414 NodeAddr<InstrNode *> Owner{Use.Addr->getOwner(DFG)};
415 for (const auto &ChildDef :
416 Owner.Addr->members_if(DataFlowGraph::IsDef, DFG)) {
417 if (!DefsVisited.insert(ChildDef.Id).second)
418 continue; // Already visited this def
419 if (Def.Addr->getAttrs() & NodeAttrs::Dead)
420 continue;
421 if (Def.Id == ChildDef.Id)
422 continue; // `Def` uses itself (e.g., increment loop counter)
423
424 AnalyzeDefUseChain(ChildDef);
425
426 // `Def` inherits all of its child defs' transmitters.
427 for (auto TransmitterId : Transmitters[ChildDef.Id])
428 Transmitters[Def.Id].push_back(TransmitterId);
429 }
430 }
431
432 // Note that this statement adds `Def.Id` to the map if no
433 // transmitters were found for `Def`.
434 auto &DefTransmitters = Transmitters[Def.Id];
435
436 // Remove duplicate transmitters
437 llvm::sort(DefTransmitters);
438 DefTransmitters.erase(llvm::unique(DefTransmitters),
439 DefTransmitters.end());
440 };
441
442 // Find all of the transmitters
443 AnalyzeDefUseChain(SourceDef);
444 auto &SourceDefTransmitters = Transmitters[SourceDef.Id];
445 if (SourceDefTransmitters.empty())
446 return; // No transmitters for `SourceDef`
447
448 MachineInstr *Source = SourceDef.Addr->getFlags() & NodeAttrs::PhiRef
449 ? MachineGadgetGraph::ArgNodeSentinel
450 : SourceDef.Addr->getOp().getParent();
451 auto GadgetSource = MaybeAddNode(Source);
452 // Each transmitter is a sink for `SourceDef`.
453 for (auto TransmitterId : SourceDefTransmitters) {
454 MachineInstr *Sink = DFG.addr<StmtNode *>(TransmitterId).Addr->getCode();
455 auto GadgetSink = MaybeAddNode(Sink);
456 // Add the gadget edge to the graph.
457 Builder.addEdge(MachineGadgetGraph::GadgetEdgeSentinel,
458 GadgetSource.first, GadgetSink.first);
459 ++GadgetCount;
460 }
461 };
462
463 LLVM_DEBUG(dbgs() << "Analyzing def-use chains to find gadgets\n");
464 // Analyze function arguments
465 NodeAddr<BlockNode *> EntryBlock = DFG.getFunc().Addr->getEntryBlock(DFG);
466 for (NodeAddr<PhiNode *> ArgPhi :
467 EntryBlock.Addr->members_if(DataFlowGraph::IsPhi, DFG)) {
468 NodeList Defs = ArgPhi.Addr->members_if(DataFlowGraph::IsDef, DFG);
469 llvm::for_each(Defs, AnalyzeDef);
470 }
471 // Analyze every instruction in MF
472 for (NodeAddr<BlockNode *> BA : DFG.getFunc().Addr->members(DFG)) {
473 for (NodeAddr<StmtNode *> SA :
474 BA.Addr->members_if(DataFlowGraph::IsCode<NodeAttrs::Stmt>, DFG)) {
475 MachineInstr *MI = SA.Addr->getCode();
476 if (isFence(MI)) {
477 MaybeAddNode(MI);
478 ++FenceCount;
479 } else if (MI->mayLoad()) {
480 NodeList Defs = SA.Addr->members_if(DataFlowGraph::IsDef, DFG);
481 llvm::for_each(Defs, AnalyzeDef);
482 }
483 }
484 }
485 LLVM_DEBUG(dbgs() << "Found " << FenceCount << " fences\n");
486 LLVM_DEBUG(dbgs() << "Found " << GadgetCount << " gadgets\n");
487 if (GadgetCount == 0)
488 return nullptr;
489 NumGadgets += GadgetCount;
490
491 // Traverse CFG to build the rest of the graph
492 SmallPtrSet<MachineBasicBlock *, 8> BlocksVisited;
493 std::function<void(MachineBasicBlock *, GraphIter, unsigned)> TraverseCFG =
494 [&](MachineBasicBlock *MBB, GraphIter GI, unsigned ParentDepth) {
495 unsigned LoopDepth = MLI.getLoopDepth(MBB);
496 if (!MBB->empty()) {
497 // Always add the first instruction in each block
498 auto NI = MBB->begin();
499 auto BeginBB = MaybeAddNode(&*NI);
500 Builder.addEdge(ParentDepth, GI, BeginBB.first);
501 if (!BlocksVisited.insert(MBB).second)
502 return;
503
504 // Add any instructions within the block that are gadget components
505 GI = BeginBB.first;
506 while (++NI != MBB->end()) {
507 auto Ref = NodeMap.find(&*NI);
508 if (Ref != NodeMap.end()) {
509 Builder.addEdge(LoopDepth, GI, Ref->getSecond());
510 GI = Ref->getSecond();
511 }
512 }
513
514 // Always add the terminator instruction, if one exists
515 auto T = MBB->getFirstTerminator();
516 if (T != MBB->end()) {
517 auto EndBB = MaybeAddNode(&*T);
518 if (EndBB.second)
519 Builder.addEdge(LoopDepth, GI, EndBB.first);
520 GI = EndBB.first;
521 }
522 }
523 for (MachineBasicBlock *Succ : MBB->successors())
524 TraverseCFG(Succ, GI, LoopDepth);
525 };
526 // ArgNodeSentinel is a pseudo-instruction that represents MF args in the
527 // GadgetGraph
528 GraphIter ArgNode = MaybeAddNode(MachineGadgetGraph::ArgNodeSentinel).first;
529 TraverseCFG(&MF.front(), ArgNode, 0);
530 std::unique_ptr<MachineGadgetGraph> G{Builder.get(FenceCount, GadgetCount)};
531 LLVM_DEBUG(dbgs() << "Found " << G->nodes_size() << " nodes\n");
532 return G;
533}
534
535// Returns the number of remaining gadget edges that could not be eliminated
536int X86LoadValueInjectionLoadHardeningImpl::elimMitigatedEdgesAndNodes(
537 MachineGadgetGraph &G, EdgeSet &ElimEdges /* in, out */,
538 NodeSet &ElimNodes /* in, out */) const {
539 if (G.NumFences > 0) {
540 // Eliminate fences and CFG edges that ingress and egress the fence, as
541 // they are trivially mitigated.
542 for (const Edge &E : G.edges()) {
543 const Node *Dest = E.getDest();
544 if (isFence(Dest->getValue())) {
545 ElimNodes.insert(*Dest);
546 ElimEdges.insert(E);
547 for (const Edge &DE : Dest->edges())
548 ElimEdges.insert(DE);
549 }
550 }
551 }
552
553 // Find and eliminate gadget edges that have been mitigated.
554 int RemainingGadgets = 0;
555 NodeSet ReachableNodes{G};
556 for (const Node &RootN : G.nodes()) {
557 if (llvm::none_of(RootN.edges(), MachineGadgetGraph::isGadgetEdge))
558 continue; // skip this node if it isn't a gadget source
559
560 // Find all of the nodes that are CFG-reachable from RootN using DFS
561 ReachableNodes.clear();
562 std::function<void(const Node *, bool)> FindReachableNodes =
563 [&](const Node *N, bool FirstNode) {
564 if (!FirstNode)
565 ReachableNodes.insert(*N);
566 for (const Edge &E : N->edges()) {
567 const Node *Dest = E.getDest();
568 if (MachineGadgetGraph::isCFGEdge(E) && !ElimEdges.contains(E) &&
569 !ReachableNodes.contains(*Dest))
570 FindReachableNodes(Dest, false);
571 }
572 };
573 FindReachableNodes(&RootN, true);
574
575 // Any gadget whose sink is unreachable has been mitigated
576 for (const Edge &E : RootN.edges()) {
577 if (MachineGadgetGraph::isGadgetEdge(E)) {
578 if (ReachableNodes.contains(*E.getDest())) {
579 // This gadget's sink is reachable
580 ++RemainingGadgets;
581 } else { // This gadget's sink is unreachable, and therefore mitigated
582 ElimEdges.insert(E);
583 }
584 }
585 }
586 }
587 return RemainingGadgets;
588}
589
590std::unique_ptr<MachineGadgetGraph>
591X86LoadValueInjectionLoadHardeningImpl::trimMitigatedEdges(
592 std::unique_ptr<MachineGadgetGraph> Graph) const {
593 NodeSet ElimNodes{*Graph};
594 EdgeSet ElimEdges{*Graph};
595 int RemainingGadgets =
596 elimMitigatedEdgesAndNodes(*Graph, ElimEdges, ElimNodes);
597 if (ElimEdges.empty() && ElimNodes.empty()) {
598 Graph->NumFences = 0;
599 Graph->NumGadgets = RemainingGadgets;
600 } else {
601 Graph = GraphBuilder::trim(*Graph, ElimNodes, ElimEdges, 0 /* NumFences */,
602 RemainingGadgets);
603 }
604 return Graph;
605}
606
607int X86LoadValueInjectionLoadHardeningImpl::hardenLoadsWithPlugin(
608 MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph) const {
609 int FencesInserted = 0;
610
611 do {
612 LLVM_DEBUG(dbgs() << "Eliminating mitigated paths...\n");
613 Graph = trimMitigatedEdges(std::move(Graph));
614 LLVM_DEBUG(dbgs() << "Eliminating mitigated paths... Done\n");
615 if (Graph->NumGadgets == 0)
616 break;
617
618 LLVM_DEBUG(dbgs() << "Cutting edges...\n");
619 EdgeSet CutEdges{*Graph};
620 auto Nodes = std::make_unique<unsigned int[]>(Graph->nodes_size() +
621 1 /* terminator node */);
622 auto Edges = std::make_unique<unsigned int[]>(Graph->edges_size());
623 auto EdgeCuts = std::make_unique<int[]>(Graph->edges_size());
624 auto EdgeValues = std::make_unique<int[]>(Graph->edges_size());
625 for (const Node &N : Graph->nodes()) {
626 Nodes[Graph->getNodeIndex(N)] = Graph->getEdgeIndex(*N.edges_begin());
627 }
628 Nodes[Graph->nodes_size()] = Graph->edges_size(); // terminator node
629 for (const Edge &E : Graph->edges()) {
630 Edges[Graph->getEdgeIndex(E)] = Graph->getNodeIndex(*E.getDest());
631 EdgeValues[Graph->getEdgeIndex(E)] = E.getValue();
632 }
633 OptimizeCut(Nodes.get(), Graph->nodes_size(), Edges.get(), EdgeValues.get(),
634 EdgeCuts.get(), Graph->edges_size());
635 for (int I = 0; I < Graph->edges_size(); ++I)
636 if (EdgeCuts[I])
637 CutEdges.set(I);
638 LLVM_DEBUG(dbgs() << "Cutting edges... Done\n");
639 LLVM_DEBUG(dbgs() << "Cut " << CutEdges.count() << " edges\n");
640
641 LLVM_DEBUG(dbgs() << "Inserting LFENCEs...\n");
642 FencesInserted += insertFences(MF, *Graph, CutEdges);
643 LLVM_DEBUG(dbgs() << "Inserting LFENCEs... Done\n");
644 LLVM_DEBUG(dbgs() << "Inserted " << FencesInserted << " fences\n");
645
646 Graph = GraphBuilder::trim(*Graph, NodeSet{*Graph}, CutEdges);
647 } while (true);
648
649 return FencesInserted;
650}
651
652int X86LoadValueInjectionLoadHardeningImpl::hardenLoadsWithHeuristic(
653 MachineFunction &MF, std::unique_ptr<MachineGadgetGraph> Graph) const {
654 // If `MF` does not have any fences, then no gadgets would have been
655 // mitigated at this point.
656 if (Graph->NumFences > 0) {
657 LLVM_DEBUG(dbgs() << "Eliminating mitigated paths...\n");
658 Graph = trimMitigatedEdges(std::move(Graph));
659 LLVM_DEBUG(dbgs() << "Eliminating mitigated paths... Done\n");
660 }
661
662 if (Graph->NumGadgets == 0)
663 return 0;
664
665 LLVM_DEBUG(dbgs() << "Cutting edges...\n");
666 EdgeSet CutEdges{*Graph};
667
668 // Begin by collecting all ingress CFG edges for each node
669 DenseMap<const Node *, SmallVector<const Edge *, 2>> IngressEdgeMap;
670 for (const Edge &E : Graph->edges())
671 if (MachineGadgetGraph::isCFGEdge(E))
672 IngressEdgeMap[E.getDest()].push_back(&E);
673
674 // For each gadget edge, make cuts that guarantee the gadget will be
675 // mitigated. A computationally efficient way to achieve this is to either:
676 // (a) cut all egress CFG edges from the gadget source, or
677 // (b) cut all ingress CFG edges to the gadget sink.
678 //
679 // Moreover, the algorithm tries not to make a cut into a loop by preferring
680 // to make a (b)-type cut if the gadget source resides at a greater loop depth
681 // than the gadget sink, or an (a)-type cut otherwise.
682 for (const Node &N : Graph->nodes()) {
683 for (const Edge &E : N.edges()) {
684 if (!MachineGadgetGraph::isGadgetEdge(E))
685 continue;
686
688 SmallVector<const Edge *, 2> &IngressEdges = IngressEdgeMap[E.getDest()];
689 for (const Edge &EgressEdge : N.edges())
690 if (MachineGadgetGraph::isCFGEdge(EgressEdge))
691 EgressEdges.push_back(&EgressEdge);
692
693 int EgressCutCost = 0, IngressCutCost = 0;
694 for (const Edge *EgressEdge : EgressEdges)
695 if (!CutEdges.contains(*EgressEdge))
696 EgressCutCost += EgressEdge->getValue();
697 for (const Edge *IngressEdge : IngressEdges)
698 if (!CutEdges.contains(*IngressEdge))
699 IngressCutCost += IngressEdge->getValue();
700
701 auto &EdgesToCut =
702 IngressCutCost < EgressCutCost ? IngressEdges : EgressEdges;
703 for (const Edge *E : EdgesToCut)
704 CutEdges.insert(*E);
705 }
706 }
707 LLVM_DEBUG(dbgs() << "Cutting edges... Done\n");
708 LLVM_DEBUG(dbgs() << "Cut " << CutEdges.count() << " edges\n");
709
710 LLVM_DEBUG(dbgs() << "Inserting LFENCEs...\n");
711 int FencesInserted = insertFences(MF, *Graph, CutEdges);
712 LLVM_DEBUG(dbgs() << "Inserting LFENCEs... Done\n");
713 LLVM_DEBUG(dbgs() << "Inserted " << FencesInserted << " fences\n");
714
715 return FencesInserted;
716}
717
718int X86LoadValueInjectionLoadHardeningImpl::insertFences(
719 MachineFunction &MF, MachineGadgetGraph &G,
720 EdgeSet &CutEdges /* in, out */) const {
721 int FencesInserted = 0;
722 for (const Node &N : G.nodes()) {
723 for (const Edge &E : N.edges()) {
724 if (CutEdges.contains(E)) {
725 MachineInstr *MI = N.getValue(), *Prev;
726 MachineBasicBlock *MBB; // Insert an LFENCE in this MBB
727 MachineBasicBlock::iterator InsertionPt; // ...at this point
728 if (MI == MachineGadgetGraph::ArgNodeSentinel) {
729 // insert LFENCE at beginning of entry block
730 MBB = &MF.front();
731 InsertionPt = MBB->begin();
732 Prev = nullptr;
733 } else if (MI->isBranch()) { // insert the LFENCE before the branch
734 MBB = MI->getParent();
735 InsertionPt = MI;
736 Prev = MI->getPrevNode();
737 // Remove all egress CFG edges from this branch because the inserted
738 // LFENCE prevents gadgets from crossing the branch.
739 for (const Edge &E : N.edges()) {
740 if (MachineGadgetGraph::isCFGEdge(E))
741 CutEdges.insert(E);
742 }
743 } else { // insert the LFENCE after the instruction
744 MBB = MI->getParent();
745 InsertionPt = MI->getNextNode() ? MI->getNextNode() : MBB->end();
746 Prev = InsertionPt == MBB->end()
747 ? (MBB->empty() ? nullptr : &MBB->back())
748 : InsertionPt->getPrevNode();
749 }
750 // Ensure this insertion is not redundant (two LFENCEs in sequence).
751 if ((InsertionPt == MBB->end() || !isFence(&*InsertionPt)) &&
752 (!Prev || !isFence(Prev))) {
753 BuildMI(*MBB, InsertionPt, DebugLoc(), TII->get(X86::LFENCE));
754 ++FencesInserted;
755 }
756 }
757 }
758 }
759 return FencesInserted;
760}
761
762bool X86LoadValueInjectionLoadHardeningImpl::instrUsesRegToAccessMemory(
763 const MachineInstr &MI, Register Reg) const {
764 if (!MI.mayLoadOrStore() || MI.getOpcode() == X86::MFENCE ||
765 MI.getOpcode() == X86::SFENCE || MI.getOpcode() == X86::LFENCE)
766 return false;
767
768 const int MemRefBeginIdx = X86::getFirstAddrOperandIdx(MI);
769 if (MemRefBeginIdx < 0) {
770 LLVM_DEBUG(dbgs() << "Warning: unable to obtain memory operand for loading "
771 "instruction:\n";
772 MI.print(dbgs()); dbgs() << '\n';);
773 return false;
774 }
775
776 const MachineOperand &BaseMO =
777 MI.getOperand(MemRefBeginIdx + X86::AddrBaseReg);
778 const MachineOperand &IndexMO =
779 MI.getOperand(MemRefBeginIdx + X86::AddrIndexReg);
780 return (BaseMO.isReg() && BaseMO.getReg().isValid() &&
781 TRI->regsOverlap(BaseMO.getReg(), Reg)) ||
782 (IndexMO.isReg() && IndexMO.getReg().isValid() &&
783 TRI->regsOverlap(IndexMO.getReg(), Reg));
784}
785
786bool X86LoadValueInjectionLoadHardeningImpl::instrUsesRegToBranch(
787 const MachineInstr &MI, Register Reg) const {
788 if (!MI.isConditionalBranch())
789 return false;
790 for (const MachineOperand &Use : MI.uses())
791 if (Use.isReg() && Use.getReg() == Reg)
792 return true;
793 return false;
794}
795
796bool X86LoadValueInjectionLoadHardeningLegacy::runOnMachineFunction(
797 MachineFunction &MF) {
798 // Don't skip functions with the "optnone" attr but participate in opt-bisect.
799 // Note: Not needed for new PM impl, where it is handled at the PM level.
800 const Function &F = MF.getFunction();
801 if (!F.hasOptNone() && skipFunction(F))
802 return false;
803
804 // Bail early (without computing analyses) if LVI load hardening is disabled.
805 if (!MF.getSubtarget<X86Subtarget>().useLVILoadHardening()) {
806 return false;
807 }
808
809 const auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
810 const auto &MDT = getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
811 const auto &MDF = getAnalysis<MachineDominanceFrontierWrapperPass>().getMDF();
812
813 X86LoadValueInjectionLoadHardeningImpl Impl;
814 return Impl.run(MF, MLI, MDT, MDF);
815}
816
819 // Bail early (without computing analyses) if LVI load hardening is disabled.
820 if (!MF.getSubtarget<X86Subtarget>().useLVILoadHardening()) {
821 return PreservedAnalyses::all();
822 }
823
824 const auto &MLI = MFAM.getResult<MachineLoopAnalysis>(MF);
825 const auto &MDT = MFAM.getResult<MachineDominatorTreeAnalysis>(MF);
826 const auto &MDF = MFAM.getResult<MachineDominanceFrontierAnalysis>(MF);
827
828 X86LoadValueInjectionLoadHardeningImpl Impl;
829 const bool Modified = Impl.run(MF, MLI, MDT, MDF);
833}
834
835INITIALIZE_PASS_BEGIN(X86LoadValueInjectionLoadHardeningLegacy, PASS_KEY,
836 "X86 LVI load hardening", false, false)
840INITIALIZE_PASS_END(X86LoadValueInjectionLoadHardeningLegacy, PASS_KEY,
841 "X86 LVI load hardening", false, false)
842
844 return new X86LoadValueInjectionLoadHardeningLegacy();
845}
MachineInstrBuilder & UseMI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock & MBB
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static ManagedStatic< DebugCounterOwner > Owner
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:54
#define I(x, y, z)
Definition MD5.cpp:57
#define G(x, y, z)
Definition MD5.cpp:55
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)
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
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
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
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:256
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.
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.
Analysis pass that exposes the MachineLoopInfo for a machine function.
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
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
Definition Analysis.h:151
Wrapper class representing virtual and physical registers.
Definition Register.h:20
constexpr bool isValid() const
Definition Register.h:112
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:184
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...
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
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.
Definition Types.h:26
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:1732
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
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="")
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
auto unique(Range &&R, Predicate P)
Definition STLExtras.h:2134
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
FunctionPass * createX86LoadValueInjectionLoadHardeningLegacyPass()
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1636
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:1753
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
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