60#define PASS_KEY "x86-slh"
61#define DEBUG_TYPE PASS_KEY
63STATISTIC(NumCondBranchesTraced,
"Number of conditional branches traced");
64STATISTIC(NumBranchesUntraced,
"Number of branches unable to trace");
66 "Number of address mode used registers hardaned");
68 "Number of post-load register values hardened");
70 "Number of calls or jumps requiring extra hardening");
71STATISTIC(NumInstsInserted,
"Number of instructions inserted");
72STATISTIC(NumLFENCEsInserted,
"Number of lfence instructions inserted");
75 "x86-speculative-load-hardening",
82 "Use LFENCE along each conditional edge to harden against speculative "
83 "loads rather than conditional movs and poisoned pointers."),
88 cl::desc(
"Harden the value loaded *after* it is loaded by "
89 "flushing the loaded bits to 1. This is hard to do "
90 "in general but can be done easily for GPRs."),
95 cl::desc(
"Use a full speculation fence to harden both call and ret edges "
96 "rather than a lighter weight mitigation."),
101 cl::desc(
"Harden interprocedurally by passing our state in and out of "
102 "functions in the high bits of the stack pointer."),
107 cl::desc(
"Sanitize loads from memory. When disable, no "
108 "significant security is provided."),
113 cl::desc(
"Harden indirect calls and jumps against using speculatively "
114 "stored attacker controlled addresses. This is designed to "
115 "mitigate Spectre v1.2 style attacks."),
124 StringRef getPassName()
const override {
125 return "X86 speculative load hardening";
127 bool runOnMachineFunction(MachineFunction &MF)
override;
128 void getAnalysisUsage(AnalysisUsage &AU)
const override;
136 struct BlockCondInfo {
137 MachineBasicBlock *MBB;
141 SmallVector<MachineInstr *, 2> CondBrs;
143 MachineInstr *UncondBr;
151 const TargetRegisterClass *RC;
152 MachineSSAUpdater SSA;
154 PredState(MachineFunction &MF,
const TargetRegisterClass *RC)
158 const X86Subtarget *Subtarget =
nullptr;
159 MachineRegisterInfo *MRI =
nullptr;
160 const X86InstrInfo *TII =
nullptr;
161 const TargetRegisterInfo *TRI =
nullptr;
163 std::optional<PredState> PS;
165 void hardenEdgesWithLFENCE(MachineFunction &MF);
172 void unfoldCallAndJumpLoads(MachineFunction &MF);
175 tracePredStateThroughIndirectBranches(MachineFunction &MF);
177 void tracePredStateThroughBlocksAndHarden(MachineFunction &MF);
182 void restoreEFLAGS(MachineBasicBlock &
MBB,
186 void mergePredStateIntoSP(MachineBasicBlock &
MBB,
189 Register extractPredStateFromSP(MachineBasicBlock &
MBB,
194 hardenLoadAddr(MachineInstr &
MI, MachineOperand &BaseMO,
195 MachineOperand &IndexMO,
196 SmallDenseMap<Register, Register, 32> &AddrRegToHardenedReg);
198 sinkPostLoadHardenedInst(MachineInstr &
MI,
199 SmallPtrSetImpl<MachineInstr *> &HardenedInstrs);
205 void hardenReturnInstr(MachineInstr &
MI);
206 void tracePredStateThroughCall(MachineInstr &
MI);
207 void hardenIndirectCallOrJumpInstr(
209 SmallDenseMap<Register, Register, 32> &AddrRegToHardenedReg);
214char X86SpeculativeLoadHardeningPass::ID = 0;
216void X86SpeculativeLoadHardeningPass::getAnalysisUsage(
225 assert(!Succ.
isEHPad() &&
"Shouldn't get edges to EH pads!");
239 "Didn't start with the right target!");
248 assert(
MBB.isSuccessor(&OldLayoutSucc) &&
249 "Without an unconditional branch, the old layout successor should "
250 "be an actual successor!");
254 UncondBr = &*BrBuilder;
265 "Cannot have a branchless successor and an unconditional branch!");
267 "A non-branch successor must have been a layout successor before "
268 "and now is a layout successor of the new block.");
274 if (SuccCount == 1) {
275 MBB.replaceSuccessor(&Succ, &NewMBB);
277 MBB.splitSuccessor(&Succ, &NewMBB);
291 assert(OpMBB.
isMBB() &&
"Block operand to a PHI is not a block!");
296 if (SuccCount == 1) {
302 MI.addOperand(MF, OpV);
309 for (
auto &LI : Succ.
liveins())
329 for (
auto &
MI :
MBB) {
352 while (!DupIndices.
empty()) {
375 if (
MI.getOpcode() == X86::LFENCE)
383 if (
MI.getOpcode() == X86::MFENCE)
395bool X86SpeculativeLoadHardeningPass::runOnMachineFunction(
396 MachineFunction &MF) {
412 PS.emplace(MF, &X86::GR64_NOSPRegClass);
420 hardenEdgesWithLFENCE(MF);
428 auto EntryInsertPt =
Entry.SkipPHIsLabelsAndDebug(
Entry.begin());
438 if (!HasVulnerableLoad && Infos.
empty())
443 const int PoisonVal = -1;
444 PS->PoisonReg =
MRI->createVirtualRegister(PS->RC);
445 BuildMI(Entry, EntryInsertPt, Loc,
TII->get(X86::MOV64ri32), PS->PoisonReg)
458 BuildMI(Entry, EntryInsertPt, Loc,
TII->get(X86::LFENCE));
460 ++NumLFENCEsInserted;
473 PS->InitialReg = extractPredStateFromSP(Entry, EntryInsertPt, Loc);
477 PS->InitialReg =
MRI->createVirtualRegister(PS->RC);
478 Register PredStateSubReg =
MRI->createVirtualRegister(&X86::GR32RegClass);
479 auto ZeroI =
BuildMI(Entry, EntryInsertPt, Loc,
TII->get(X86::MOV32r0),
482 MachineOperand *ZeroEFLAGSDefOp =
483 ZeroI->findRegisterDefOperand(X86::EFLAGS,
nullptr);
485 "Must have an implicit def of EFLAGS!");
487 BuildMI(Entry, EntryInsertPt, Loc,
TII->get(X86::SUBREG_TO_REG),
501 PS->SSA.Initialize(PS->InitialReg);
502 PS->SSA.AddAvailableValue(&Entry, PS->InitialReg);
505 auto CMovs = tracePredStateThroughCFG(MF, Infos);
515 for (MachineBasicBlock &
MBB : MF) {
521 PS->SSA.AddAvailableValue(
530 unfoldCallAndJumpLoads(MF);
533 auto IndirectBrCMovs = tracePredStateThroughIndirectBranches(MF);
534 CMovs.append(IndirectBrCMovs.begin(), IndirectBrCMovs.end());
540 tracePredStateThroughBlocksAndHarden(MF);
544 for (MachineInstr *CMovI : CMovs)
545 for (MachineOperand &
Op : CMovI->operands()) {
546 if (!
Op.isReg() ||
Op.getReg() != PS->InitialReg)
549 PS->SSA.RewriteUse(
Op);
552 LLVM_DEBUG(
dbgs() <<
"Final speculative load hardened function:\n"; MF.dump();
553 dbgs() <<
"\n"; MF.verify(
this));
563void X86SpeculativeLoadHardeningPass::hardenEdgesWithLFENCE(
564 MachineFunction &MF) {
567 SmallSetVector<MachineBasicBlock *, 8> Blocks;
568 for (MachineBasicBlock &
MBB : MF) {
576 if (TermIt ==
MBB.
end() || !TermIt->isBranch())
583 if (!SuccMBB->isEHPad())
587 for (MachineBasicBlock *
MBB : Blocks) {
591 ++NumLFENCEsInserted;
596X86SpeculativeLoadHardeningPass::collectBlockCondInfo(MachineFunction &MF) {
601 for (MachineBasicBlock &
MBB : MF) {
626 BlockCondInfo
Info = {&
MBB, {},
nullptr};
632 if (!
MI.isTerminator())
636 if (!
MI.isBranch()) {
637 Info.CondBrs.clear();
643 if (
MI.getOpcode() == X86::JMP_1) {
644 Info.CondBrs.clear();
659 Info.CondBrs.clear();
665 Info.CondBrs.push_back(&
MI);
667 if (
Info.CondBrs.empty()) {
668 ++NumBranchesUntraced;
669 LLVM_DEBUG(
dbgs() <<
"WARNING: unable to secure successors of block:\n";
688X86SpeculativeLoadHardeningPass::tracePredStateThroughCFG(
696 for (
const BlockCondInfo &
Info : Infos) {
697 MachineBasicBlock &
MBB = *
Info.MBB;
698 const SmallVectorImpl<MachineInstr *> &CondBrs =
Info.CondBrs;
699 MachineInstr *UncondBr =
Info.UncondBr;
703 ++NumCondBranchesTraced;
707 MachineBasicBlock *UncondSucc =
708 UncondBr ? (UncondBr->
getOpcode() == X86::JMP_1
714 SmallDenseMap<MachineBasicBlock *, int> SuccCounts;
716 ++SuccCounts[UncondSucc];
717 for (
auto *CondBr : CondBrs)
718 ++SuccCounts[CondBr->getOperand(0).getMBB()];
722 auto BuildCheckingBlockForSuccAndConds =
723 [&](MachineBasicBlock &
MBB, MachineBasicBlock &Succ,
int SuccCount,
724 MachineInstr *Br, MachineInstr *&UncondBr,
729 (SuccCount == 1 && Succ.pred_size() == 1)
731 : splitEdge(MBB, Succ, SuccCount, Br, UncondBr, *TII);
733 bool LiveEFLAGS = Succ.isLiveIn(X86::EFLAGS);
735 CheckingMBB.addLiveIn(X86::EFLAGS);
738 auto InsertPt = CheckingMBB.begin();
739 assert((InsertPt == CheckingMBB.end() || !InsertPt->isPHI()) &&
740 "Should never have a PHI in the initial checking block as it "
741 "always has a single predecessor!");
745 Register CurStateReg = PS->InitialReg;
747 for (X86::CondCode Cond : Conds) {
748 int PredStateSizeInBytes = TRI->getRegSizeInBits(*PS->RC) / 8;
749 auto CMovOp = X86::getCMovOpcode(PredStateSizeInBytes);
751 Register UpdatedStateReg = MRI->createVirtualRegister(PS->RC);
754 auto CMovI = BuildMI(CheckingMBB, InsertPt, DebugLoc(),
755 TII->get(CMovOp), UpdatedStateReg)
757 .addReg(PS->PoisonReg)
761 if (!LiveEFLAGS && Cond == Conds.back())
762 CMovI->findRegisterUseOperand(X86::EFLAGS, nullptr)
766 LLVM_DEBUG(dbgs() <<
" Inserting cmov: "; CMovI->dump();
771 if (CurStateReg == PS->InitialReg)
772 CMovs.push_back(&*CMovI);
775 CurStateReg = UpdatedStateReg;
780 PS->SSA.AddAvailableValue(&CheckingMBB, CurStateReg);
783 std::vector<X86::CondCode> UncondCodeSeq;
784 for (
auto *CondBr : CondBrs) {
785 MachineBasicBlock &Succ = *CondBr->getOperand(0).getMBB();
786 int &SuccCount = SuccCounts[&Succ];
790 UncondCodeSeq.push_back(
Cond);
792 BuildCheckingBlockForSuccAndConds(
MBB, Succ, SuccCount, CondBr, UncondBr,
815 assert(SuccCounts[UncondSucc] == 1 &&
816 "We should never have more than one edge to the unconditional "
817 "successor at this point because every other edge must have been "
822 UncondCodeSeq.erase(
llvm::unique(UncondCodeSeq), UncondCodeSeq.end());
825 BuildCheckingBlockForSuccAndConds(
MBB, *UncondSucc, 1,
826 UncondBr, UncondBr, UncondCodeSeq);
837static const TargetRegisterClass *
840 unsigned UnfoldedOpc =
TII.getOpcodeAfterMemoryUnfold(
841 Opcode,
true,
false, &Index);
843 return TII.getRegClass(
MCID, Index);
846void X86SpeculativeLoadHardeningPass::unfoldCallAndJumpLoads(
847 MachineFunction &MF) {
848 for (MachineBasicBlock &
MBB : MF)
853 if (!
MI.isCall() && !
MI.isBranch())
859 switch (
MI.getOpcode()) {
862 dbgs() <<
"ERROR: Found an unexpected loading branch or call "
868 case X86::FARCALL16m:
869 case X86::FARCALL32m:
870 case X86::FARCALL64m:
879 case X86::CALL16m_NT:
881 case X86::CALL32m_NT:
883 case X86::CALL64m_NT:
890 case X86::TAILJMPm64:
891 case X86::TAILJMPm64_REX:
893 case X86::TCRETURNmi64:
894 case X86::TCRETURN_WINmi64:
895 case X86::TCRETURNmi: {
902 <<
"ERROR: Unable to unfold load from instruction:\n";
907 SmallVector<MachineInstr *, 2> NewMIs;
911 TII->unfoldMemoryOperand(MF,
MI,
Reg,
true,
915 "Computed unfolded register class but failed to unfold");
917 for (
auto *NewMI : NewMIs)
921 if (
MI.isCandidateForAdditionalCallInfo())
922 MF.eraseAdditionalCallInfo(&
MI);
924 MI.eraseFromParent();
926 dbgs() <<
"Unfolded load successfully into:\n";
927 for (
auto *NewMI : NewMIs) {
957X86SpeculativeLoadHardeningPass::tracePredStateThroughIndirectBranches(
958 MachineFunction &MF) {
963 MachineSSAUpdater TargetAddrSSA(MF);
964 TargetAddrSSA.Initialize(
MRI->createVirtualRegister(&X86::GR64RegClass));
967 SmallPtrSet<MachineBasicBlock *, 4> IndirectTerminatedMBBs;
972 SmallPtrSet<MachineBasicBlock *, 4> IndirectTargetMBBs;
976 for (MachineBasicBlock &
MBB : MF) {
983 MachineInstr &TI = *MII;
1003 case X86::JMP16m_NT:
1005 case X86::JMP32m_NT:
1007 case X86::JMP64m_NT:
1013 "Support for 16-bit indirect branches is not implemented.");
1016 "Support for 32-bit indirect branches is not implemented.");
1025 return !OtherTI.isDebugInstr() && &OtherTI != &TI;
1028 dbgs() <<
"ERROR: Found other terminators in a block with an indirect "
1029 "branch! This is not yet supported! Terminator sequence:\n";
1039 TargetAddrSSA.AddAvailableValue(&
MBB, TargetReg);
1050 if (IndirectTargetMBBs.
empty())
1056 for (MachineBasicBlock &
MBB : MF) {
1058 if (!IndirectTargetMBBs.
count(&
MBB))
1065 "Unexpected EH pad as target of an indirect branch!");
1073 "Cannot check within a block that already has live-in EFLAGS!");
1080 if (IndirectTerminatedMBBs.
count(Pred))
1088 if (!
llvm::all_of(Pred->successors(), [&](MachineBasicBlock *Succ) {
1089 return Succ->isEHPad() || Succ == &MBB;
1092 dbgs() <<
"ERROR: Found conditional entry to target of indirect "
1098 "an indirect branch!");
1104 auto InsertPt = Pred->getFirstTerminator();
1105 Register TargetReg =
MRI->createVirtualRegister(&X86::GR64RegClass);
1110 TII->get(X86::MOV64ri32), TargetReg)
1130 TargetAddrSSA.AddAvailableValue(Pred, TargetReg);
1138 Register TargetReg = TargetAddrSSA.GetValueInMiddleOfBlock(&
MBB);
1156 Register AddrReg =
MRI->createVirtualRegister(&X86::GR64RegClass);
1176 int PredStateSizeInBytes =
TRI->getRegSizeInBits(*PS->RC) / 8;
1178 Register UpdatedStateReg =
MRI->createVirtualRegister(PS->RC);
1192 PS->SSA.AddAvailableValue(&
MBB, UpdatedStateReg);
1203 MI.findRegisterDefOperand(X86::EFLAGS,
nullptr)) {
1204 return !DefOp->isDead();
1215 MI.findRegisterDefOperand(X86::EFLAGS,
nullptr)) {
1217 if (DefOp->isDead())
1225 if (
MI.killsRegister(X86::EFLAGS, &
TRI))
1231 return MBB.isLiveIn(X86::EFLAGS);
1261void X86SpeculativeLoadHardeningPass::tracePredStateThroughBlocksAndHarden(
1262 MachineFunction &MF) {
1263 SmallPtrSet<MachineInstr *, 16> HardenPostLoad;
1264 SmallPtrSet<MachineInstr *, 16> HardenLoadAddr;
1266 SmallSet<Register, 16> HardenedAddrRegs;
1268 SmallDenseMap<Register, Register, 32> AddrRegToHardenedReg;
1273 SparseBitVector<> LoadDepRegs;
1275 for (MachineBasicBlock &
MBB : MF) {
1292 for (MachineInstr &
MI :
MBB) {
1298 return Op.isReg() && LoadDepRegs.test(Op.getReg().id());
1300 for (MachineOperand &Def :
MI.defs())
1302 LoadDepRegs.
set(
Def.getReg().id());
1307 if (
MI.getOpcode() == X86::LFENCE)
1315 if (
MI.getOpcode() == X86::MFENCE)
1320 if (MemRefBeginIdx < 0) {
1322 <<
"WARNING: unable to harden loading instruction: ";
1327 MachineOperand &BaseMO =
1329 MachineOperand &IndexMO =
1335 if (!BaseMO.
isFI() && BaseMO.
getReg() != X86::RIP &&
1339 IndexReg = IndexMO.
getReg();
1341 if (!BaseReg && !IndexReg)
1349 if ((BaseReg && LoadDepRegs.
test(
BaseReg.id())) ||
1350 (IndexReg && LoadDepRegs.
test(IndexReg.
id())))
1360 MI.getOperand(0).isReg() &&
1361 canHardenRegister(
MI.getOperand(0).getReg()) &&
1362 !HardenedAddrRegs.
count(BaseReg) &&
1363 !HardenedAddrRegs.
count(IndexReg)) {
1365 HardenedAddrRegs.
insert(
MI.getOperand(0).getReg());
1373 HardenedAddrRegs.
insert(BaseReg);
1375 HardenedAddrRegs.
insert(IndexReg);
1377 for (MachineOperand &Def :
MI.defs())
1379 LoadDepRegs.
set(
Def.getReg().id());
1387 for (MachineInstr &
MI :
MBB) {
1391 "Requested to harden both the address and def of a load!");
1394 if (HardenLoadAddr.
erase(&
MI)) {
1396 assert(MemRefBeginIdx >= 0 &&
"Cannot have an invalid index here!");
1398 MachineOperand &BaseMO =
1400 MachineOperand &IndexMO =
1402 hardenLoadAddr(
MI, BaseMO, IndexMO, AddrRegToHardenedReg);
1408 if (HardenPostLoad.
erase(&
MI)) {
1409 assert(!
MI.isCall() &&
"Must not try to post-load harden a call!");
1417 MachineInstr *SunkMI = sinkPostLoadHardenedInst(
MI, HardenPostLoad);
1422 if (SunkMI != &
MI) {
1429 HardenPostLoad.
insert(SunkMI);
1437 AddrRegToHardenedReg[HardenedReg] = HardenedReg;
1448 hardenIndirectCallOrJumpInstr(
MI, AddrRegToHardenedReg);
1455 if (!
MI.isCall() && !
MI.isReturn())
1460 if (
MI.isReturn() && !
MI.isCall()) {
1461 hardenReturnInstr(
MI);
1468 assert(
MI.isCall() &&
"Should only reach here for calls!");
1469 tracePredStateThroughCall(
MI);
1472 HardenPostLoad.
clear();
1473 HardenLoadAddr.
clear();
1474 HardenedAddrRegs.
clear();
1475 AddrRegToHardenedReg.
clear();
1480 LoadDepRegs.
clear();
1490Register X86SpeculativeLoadHardeningPass::saveEFLAGS(
1508void X86SpeculativeLoadHardeningPass::restoreEFLAGS(
1519void X86SpeculativeLoadHardeningPass::mergePredStateIntoSP(
1522 Register TmpReg =
MRI->createVirtualRegister(PS->RC);
1526 auto ShiftI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::SHL64ri), TmpReg)
1531 auto OrI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::OR64rr), X86::RSP)
1539Register X86SpeculativeLoadHardeningPass::extractPredStateFromSP(
1542 Register PredStateReg =
MRI->createVirtualRegister(PS->RC);
1543 Register TmpReg =
MRI->createVirtualRegister(PS->RC);
1548 BuildMI(
MBB, InsertPt, Loc,
TII->get(TargetOpcode::COPY), TmpReg)
1551 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::SAR64ri), PredStateReg)
1553 .
addImm(
TRI->getRegSizeInBits(*PS->RC) - 1);
1557 return PredStateReg;
1560void X86SpeculativeLoadHardeningPass::hardenLoadAddr(
1561 MachineInstr &
MI, MachineOperand &BaseMO, MachineOperand &IndexMO,
1562 SmallDenseMap<Register, Register, 32> &AddrRegToHardenedReg) {
1563 MachineBasicBlock &
MBB = *
MI.getParent();
1572 if (BaseMO.
isFI()) {
1576 dbgs() <<
" Skipping hardening base of explicit stack frame load: ";
1578 }
else if (BaseMO.
getReg() == X86::RSP) {
1583 "Explicit RSP access with dynamic index!");
1585 dbgs() <<
" Cannot harden base of explicit RSP offset in a load!");
1586 }
else if (BaseMO.
getReg() == X86::RIP ||
1587 BaseMO.
getReg() == X86::NoRegister) {
1597 dbgs() <<
" Cannot harden base of "
1598 << (BaseMO.
getReg() == X86::RIP ?
"RIP-relative" :
"no-base")
1599 <<
" address in a load!");
1602 "Only allowed to have a frame index or register base.");
1606 if (IndexMO.
getReg() != X86::NoRegister &&
1607 (HardenOpRegs.
empty() ||
1608 HardenOpRegs.
front()->getReg() != IndexMO.
getReg()))
1612 "Should have exactly one or two registers to harden!");
1614 HardenOpRegs[0]->getReg() != HardenOpRegs[1]->getReg()) &&
1615 "Should not have two of the same registers!");
1620 auto It = AddrRegToHardenedReg.
find(
Op->getReg());
1621 if (It == AddrRegToHardenedReg.
end())
1626 Op->setReg(It->second);
1630 if (HardenOpRegs.
empty())
1634 Register StateReg = PS->SSA.GetValueAtEndOfBlock(&
MBB);
1636 auto InsertPt =
MI.getIterator();
1642 if (EFLAGSLive && !Subtarget->hasBMI2()) {
1644 FlagsReg = saveEFLAGS(
MBB, InsertPt, Loc);
1647 for (MachineOperand *
Op : HardenOpRegs) {
1649 auto *OpRC =
MRI->getRegClass(OpReg);
1650 Register TmpReg =
MRI->createVirtualRegister(OpRC);
1654 if (!Subtarget->hasVLX() && (OpRC->hasSuperClassEq(&X86::VR128RegClass) ||
1655 OpRC->hasSuperClassEq(&X86::VR256RegClass))) {
1656 assert(Subtarget->
hasAVX2() &&
"AVX2-specific register classes!");
1657 bool Is128Bit = OpRC->hasSuperClassEq(&X86::VR128RegClass);
1662 Register VStateReg =
MRI->createVirtualRegister(&X86::VR128RegClass);
1664 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::VMOV64toPQIrr), VStateReg)
1671 Register VBStateReg =
MRI->createVirtualRegister(OpRC);
1672 auto BroadcastI =
BuildMI(
MBB, InsertPt, Loc,
1673 TII->get(Is128Bit ? X86::VPBROADCASTQrr
1674 : X86::VPBROADCASTQYrr),
1679 LLVM_DEBUG(
dbgs() <<
" Inserting broadcast: "; BroadcastI->dump();
1685 TII->get(Is128Bit ? X86::VPORrr : X86::VPORYrr), TmpReg)
1691 }
else if (OpRC->hasSuperClassEq(&X86::VR128XRegClass) ||
1692 OpRC->hasSuperClassEq(&X86::VR256XRegClass) ||
1693 OpRC->hasSuperClassEq(&X86::VR512RegClass)) {
1694 assert(Subtarget->
hasAVX512() &&
"AVX512-specific register classes!");
1695 bool Is128Bit = OpRC->hasSuperClassEq(&X86::VR128XRegClass);
1696 bool Is256Bit = OpRC->hasSuperClassEq(&X86::VR256XRegClass);
1697 if (Is128Bit || Is256Bit)
1698 assert(Subtarget->hasVLX() &&
"AVX512VL-specific register classes!");
1701 Register VStateReg =
MRI->createVirtualRegister(OpRC);
1702 unsigned BroadcastOp =
Is128Bit ? X86::VPBROADCASTQrZ128rr
1703 : Is256Bit ? X86::VPBROADCASTQrZ256rr
1704 : X86::VPBROADCASTQrZrr;
1706 BuildMI(
MBB, InsertPt, Loc,
TII->get(BroadcastOp), VStateReg)
1710 LLVM_DEBUG(
dbgs() <<
" Inserting broadcast: "; BroadcastI->dump();
1714 unsigned OrOp =
Is128Bit ? X86::VPORQZ128rr
1715 : Is256Bit ? X86::VPORQZ256rr : X86::VPORQZrr;
1716 auto OrI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(OrOp), TmpReg)
1724 assert(OpRC->hasSuperClassEq(&X86::GR64RegClass) &&
1725 "Not a supported register class for address hardening!");
1729 auto OrI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::OR64rr), TmpReg)
1739 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::SHRX64rr), TmpReg)
1751 "Should not have checked this register yet!");
1752 AddrRegToHardenedReg[
Op->getReg()] = TmpReg;
1754 ++NumAddrRegsHardened;
1759 restoreEFLAGS(
MBB, InsertPt, Loc, FlagsReg);
1762MachineInstr *X86SpeculativeLoadHardeningPass::sinkPostLoadHardenedInst(
1763 MachineInstr &InitialMI, SmallPtrSetImpl<MachineInstr *> &HardenedInstrs) {
1765 "Cannot get here with a non-invariant load!");
1767 "Cannot get here with a data invariant load "
1768 "that interferes with EFLAGS!");
1771 auto SinkCheckToSingleUse =
1772 [&](MachineInstr &
MI) -> std::optional<MachineInstr *> {
1778 MachineInstr *SingleUseMI =
nullptr;
1779 for (MachineInstr &
UseMI :
MRI->use_instructions(DefReg)) {
1788 "Data variant instruction being hardened!");
1795 assert(MemRefBeginIdx >= 0 &&
1796 "Should always have mem references here!");
1798 MachineOperand &BaseMO =
1800 MachineOperand &IndexMO =
1802 if ((BaseMO.
isReg() && BaseMO.
getReg() == DefReg) ||
1823 if (
UseMI.getDesc().getNumDefs() > 1)
1830 if (!canHardenRegister(UseDefReg))
1833 SingleUseMI = &
UseMI;
1838 return {SingleUseMI};
1841 MachineInstr *
MI = &InitialMI;
1842 while (std::optional<MachineInstr *> SingleUse = SinkCheckToSingleUse(*
MI)) {
1852bool X86SpeculativeLoadHardeningPass::canHardenRegister(
Register Reg) {
1857 auto *RC =
MRI->getRegClass(
Reg);
1858 int RegBytes =
TRI->getRegSizeInBits(*RC) / 8;
1863 unsigned RegIdx =
Log2_32(RegBytes);
1864 assert(RegIdx < 4 &&
"Unsupported register size");
1873 const TargetRegisterClass *NOREXRegClasses[] = {
1874 &X86::GR8_NOREXRegClass, &X86::GR16_NOREXRegClass,
1875 &X86::GR32_NOREXRegClass, &X86::GR64_NOREXRegClass};
1876 if (RC == NOREXRegClasses[RegIdx])
1879 const TargetRegisterClass *GPRRegClasses[] = {
1880 &X86::GR8RegClass, &X86::GR16RegClass, &X86::GR32RegClass,
1881 &X86::GR64RegClass};
1882 return RC->hasSuperClassEq(GPRRegClasses[RegIdx]);
1899Register X86SpeculativeLoadHardeningPass::hardenValueInRegister(
1902 assert(canHardenRegister(
Reg) &&
"Cannot harden this register!");
1904 auto *RC =
MRI->getRegClass(
Reg);
1905 int Bytes =
TRI->getRegSizeInBits(*RC) / 8;
1906 Register StateReg = PS->SSA.GetValueAtEndOfBlock(&
MBB);
1907 assert((Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8) &&
1908 "Unknown register size");
1912 unsigned SubRegImms[] = {X86::sub_8bit, X86::sub_16bit, X86::sub_32bit};
1913 unsigned SubRegImm = SubRegImms[
Log2_32(Bytes)];
1914 Register NarrowStateReg =
MRI->createVirtualRegister(RC);
1915 BuildMI(
MBB, InsertPt, Loc,
TII->get(TargetOpcode::COPY), NarrowStateReg)
1916 .
addReg(StateReg, 0, SubRegImm);
1917 StateReg = NarrowStateReg;
1922 FlagsReg = saveEFLAGS(
MBB, InsertPt, Loc);
1925 unsigned OrOpCodes[] = {X86::OR8rr, X86::OR16rr, X86::OR32rr, X86::OR64rr};
1926 unsigned OrOpCode = OrOpCodes[
Log2_32(Bytes)];
1927 auto OrI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(OrOpCode), NewReg)
1935 restoreEFLAGS(
MBB, InsertPt, Loc, FlagsReg);
1949Register X86SpeculativeLoadHardeningPass::hardenPostLoad(MachineInstr &
MI) {
1950 MachineBasicBlock &
MBB = *
MI.getParent();
1953 auto &DefOp =
MI.getOperand(0);
1954 Register OldDefReg = DefOp.getReg();
1955 auto *DefRC =
MRI->getRegClass(OldDefReg);
1960 Register UnhardenedReg =
MRI->createVirtualRegister(DefRC);
1961 DefOp.setReg(UnhardenedReg);
1966 Register HardenedReg = hardenValueInRegister(
1967 UnhardenedReg,
MBB, std::next(
MI.getIterator()), Loc);
1971 MRI->replaceRegWith( OldDefReg, HardenedReg);
1973 ++NumPostLoadRegsHardened;
2000void X86SpeculativeLoadHardeningPass::hardenReturnInstr(MachineInstr &
MI) {
2001 MachineBasicBlock &
MBB = *
MI.getParent();
2003 auto InsertPt =
MI.getIterator();
2013 mergePredStateIntoSP(
MBB, InsertPt, Loc, PS->SSA.GetValueAtEndOfBlock(&
MBB));
2046void X86SpeculativeLoadHardeningPass::tracePredStateThroughCall(
2048 MachineBasicBlock &
MBB = *
MI.getParent();
2050 auto InsertPt =
MI.getIterator();
2063 BuildMI(
MBB, std::next(InsertPt), Loc,
TII->get(X86::LFENCE));
2065 ++NumLFENCEsInserted;
2071 Register StateReg = PS->SSA.GetValueAtEndOfBlock(&
MBB);
2072 mergePredStateIntoSP(
MBB, InsertPt, Loc, StateReg);
2087 MI.setPostInstrSymbol(MF, RetSymbol);
2089 const TargetRegisterClass *AddrRC = &X86::GR64RegClass;
2115 ExpectedRetAddrReg =
MRI->createVirtualRegister(AddrRC);
2118 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::MOV64ri32), ExpectedRetAddrReg)
2121 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::LEA64r), ExpectedRetAddrReg)
2137 if (!ExpectedRetAddrReg) {
2138 ExpectedRetAddrReg =
MRI->createVirtualRegister(AddrRC);
2139 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::MOV64rm), ExpectedRetAddrReg)
2149 Register NewStateReg = extractPredStateFromSP(
MBB, InsertPt, Loc);
2162 Register ActualRetAddrReg =
MRI->createVirtualRegister(AddrRC);
2163 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::LEA64r), ActualRetAddrReg)
2176 int PredStateSizeInBytes =
TRI->getRegSizeInBits(*PS->RC) / 8;
2179 Register UpdatedStateReg =
MRI->createVirtualRegister(PS->RC);
2180 auto CMovI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(CMovOp), UpdatedStateReg)
2188 PS->SSA.AddAvailableValue(&
MBB, UpdatedStateReg);
2206void X86SpeculativeLoadHardeningPass::hardenIndirectCallOrJumpInstr(
2208 SmallDenseMap<Register, Register, 32> &AddrRegToHardenedReg) {
2209 switch (
MI.getOpcode()) {
2210 case X86::FARCALL16m:
2211 case X86::FARCALL32m:
2212 case X86::FARCALL64m:
2213 case X86::FARJMP16m:
2214 case X86::FARJMP32m:
2215 case X86::FARJMP64m:
2226 assert(!
MI.mayLoad() &&
"Found a lingering loading instruction!");
2230 if (!
MI.getOperand(0).isReg())
2235 auto &TargetOp =
MI.getOperand(0);
2236 Register OldTargetReg = TargetOp.getReg();
2241 Register &HardenedTargetReg = AddrRegToHardenedReg[OldTargetReg];
2250 if (!HardenedTargetReg)
2251 HardenedTargetReg = hardenValueInRegister(
2252 OldTargetReg, *
MI.getParent(),
MI.getIterator(),
MI.getDebugLoc());
2255 TargetOp.setReg(HardenedTargetReg);
2257 ++NumCallsOrJumpsHardened;
2261 "X86 speculative load hardener",
false,
false)
2266 return new X86SpeculativeLoadHardeningPass();
unsigned const MachineRegisterInfo * MRI
MachineInstrBuilder & UseMI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static cl::opt< bool > HardenLoads("aarch64-slh-loads", cl::Hidden, cl::desc("Sanitize loads from memory."), cl::init(true))
Analysis containing CSE Info
This file defines the DenseMap class.
const HexagonInstrInfo * TII
const size_t AbstractManglingParser< Derived, Alloc >::NumOps
This file declares the MachineConstantPool class which is an abstract constant pool to keep track of ...
Register const TargetRegisterInfo * TRI
Promote Memory to Register
MachineInstr unsigned OpIdx
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
const SmallVectorImpl< MachineOperand > & Cond
This file defines the SmallPtrSet class.
This file defines the SmallSet class.
This file defines the SmallVector class.
This file defines the SparseBitVector class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
static MachineBasicBlock & splitEdge(MachineBasicBlock &MBB, MachineBasicBlock &Succ, int SuccCount, MachineInstr *Br, MachineInstr *&UncondBr, const X86InstrInfo &TII)
static cl::opt< bool > HardenLoads(PASS_KEY "-loads", cl::desc("Sanitize loads from memory. When disable, no " "significant security is provided."), cl::init(true), cl::Hidden)
static void canonicalizePHIOperands(MachineFunction &MF)
Removing duplicate PHI operands to leave the PHI in a canonical and predictable form.
static cl::opt< bool > HardenInterprocedurally(PASS_KEY "-ip", cl::desc("Harden interprocedurally by passing our state in and out of " "functions in the high bits of the stack pointer."), cl::init(true), cl::Hidden)
static cl::opt< bool > FenceCallAndRet(PASS_KEY "-fence-call-and-ret", cl::desc("Use a full speculation fence to harden both call and ret edges " "rather than a lighter weight mitigation."), cl::init(false), cl::Hidden)
static cl::opt< bool > EnablePostLoadHardening(PASS_KEY "-post-load", cl::desc("Harden the value loaded *after* it is loaded by " "flushing the loaded bits to 1. This is hard to do " "in general but can be done easily for GPRs."), cl::init(true), cl::Hidden)
static cl::opt< bool > HardenEdgesWithLFENCE(PASS_KEY "-lfence", cl::desc("Use LFENCE along each conditional edge to harden against speculative " "loads rather than conditional movs and poisoned pointers."), cl::init(false), cl::Hidden)
static bool isEFLAGSLive(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const TargetRegisterInfo &TRI)
static cl::opt< bool > EnableSpeculativeLoadHardening("x86-speculative-load-hardening", cl::desc("Force enable speculative load hardening"), cl::init(false), cl::Hidden)
static const TargetRegisterClass * getRegClassForUnfoldedLoad(const X86InstrInfo &TII, unsigned Opcode)
Compute the register class for the unfolded load.
static bool hasVulnerableLoad(MachineFunction &MF)
Helper to scan a function for loads vulnerable to misspeculation that we want to harden.
static bool isEFLAGSDefLive(const MachineInstr &MI)
static cl::opt< bool > HardenIndirectCallsAndJumps(PASS_KEY "-indirect", cl::desc("Harden indirect calls and jumps against using speculatively " "stored attacker controlled addresses. This is designed to " "mitigate Spectre v1.2 style attacks."), cl::init(true), cl::Hidden)
Represent the analysis usage information of a pass.
iterator find(const_arg_type_t< KeyT > Val)
size_type count(const_arg_type_t< KeyT > Val) const
Return 1 if the specified key is in the map, 0 otherwise.
FunctionPass class - This class is used to implement most global optimizations.
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
LLVM_ABI MCSymbol * createTempSymbol()
Create a temporary symbol with a unique name.
Describe properties that are true of each instruction in the target description file.
void normalizeSuccProbs()
Normalize probabilities of all successors so that the sum of them becomes one.
bool isEHPad() const
Returns true if the block is a landing pad.
LLVM_ABI instr_iterator insert(instr_iterator I, MachineInstr *M)
Insert MI into the instruction list before I, possibly inside a bundle.
iterator_range< livein_iterator > liveins() const
reverse_instr_iterator instr_rbegin()
LLVM_ABI iterator SkipPHIsAndLabels(iterator I)
Return the first instruction in MBB after I that is not a PHI or a label.
LLVM_ABI iterator SkipPHIsLabelsAndDebug(iterator I, Register Reg=Register(), bool SkipPseudoOp=true)
Return the first instruction in MBB after I that is not a PHI, label or debug.
bool isEHFuncletEntry() const
Returns true if this is the entry block of an EH funclet.
LLVM_ABI iterator getFirstTerminator()
Returns an iterator to the first terminator instruction of this basic block.
unsigned succ_size() const
LLVM_ABI void dump() const
bool isEHScopeEntry() const
Returns true if this is the entry block of an EH scope, i.e., the block that used to have a catchpad ...
LLVM_ABI void addSuccessor(MachineBasicBlock *Succ, BranchProbability Prob=BranchProbability::getUnknown())
Add Succ as a successor of this MachineBasicBlock.
reverse_instr_iterator instr_rend()
LLVM_ABI bool isLayoutSuccessor(const MachineBasicBlock *MBB) const
Return true if the specified MBB will be emitted immediately after this block, such that if this bloc...
void addLiveIn(MCRegister PhysReg, LaneBitmask LaneMask=LaneBitmask::getAll())
Adds the specified register as a live in.
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
iterator_range< iterator > terminators()
iterator_range< succ_iterator > successors()
iterator_range< pred_iterator > predecessors()
MachineInstrBundleIterator< MachineInstr > iterator
LLVM_ABI StringRef getName() const
Return the name of the corresponding LLVM basic block, or an empty string.
void setMachineBlockAddressTaken()
Set this block to indicate that its address is used as something other than the target of a terminato...
LLVM_ABI bool isLiveIn(MCRegister Reg, LaneBitmask LaneMask=LaneBitmask::getAll()) const
Return true if the specified register is in the live in set.
bool isCleanupFuncletEntry() const
Returns true if this is the entry block of a cleanup funclet.
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.
bool exposesReturnsTwice() const
exposesReturnsTwice - Returns true if the function calls setjmp or any other similar functions with a...
MCContext & getContext() const
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
BasicBlockListType::iterator iterator
MachineBasicBlock * CreateMachineBasicBlock(const BasicBlock *BB=nullptr, std::optional< UniqueBBID > BBID=std::nullopt)
CreateMachineInstr - Allocate a new MachineInstr.
void insert(iterator MBBI, MachineBasicBlock *MBB)
const TargetMachine & getTarget() const
getTarget - Return the target machine this machine code is compiled with
Register getReg(unsigned Idx) const
Get the register for the operand index.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & addSym(MCSymbol *Sym, unsigned char TargetFlags=0) const
const MachineInstrBuilder & addReg(Register RegNo, unsigned flags=0, unsigned SubReg=0) const
Add a new virtual register operand.
const MachineInstrBuilder & addMBB(MachineBasicBlock *MBB, unsigned TargetFlags=0) const
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
bool isTerminator(QueryType Type=AnyInBundle) const
Returns true if this instruction part of the terminator for a basic block.
bool isBranch(QueryType Type=AnyInBundle) const
Returns true if this is a conditional, unconditional, or indirect branch.
MachineOperand * findRegisterUseOperand(Register Reg, const TargetRegisterInfo *TRI, bool isKill=false)
Wrapper for findRegisterUseOperandIdx, it returns a pointer to the MachineOperand rather than an inde...
const DebugLoc & getDebugLoc() const
Returns the debug location id of this MachineInstr.
const MachineOperand & getOperand(unsigned i) const
LLVM_ABI bool addRegisterDead(Register Reg, const TargetRegisterInfo *RegInfo, bool AddIfNotFound=false)
We have determined MI defined a register without a use.
MachineOperand class - Representation of each machine instruction operand.
bool isReg() const
isReg - Tests if this is a MO_Register operand.
MachineBasicBlock * getMBB() const
void setIsDead(bool Val=true)
void setIsKill(bool Val=true)
void setMBB(MachineBasicBlock *MBB)
Register getReg() const
getReg - Returns the register number.
bool isFI() const
isFI - Tests if this is a MO_FrameIndex operand.
static MachineOperand CreateMBB(MachineBasicBlock *MBB, unsigned TargetFlags=0)
bool isMBB() const
isMBB - Tests if this is a MO_MachineBasicBlock operand.
constexpr bool isValid() const
constexpr bool isVirtual() const
Return true if the specified register number is in the virtual register namespace.
constexpr unsigned id() const
bool insert(const value_type &X)
Insert a new element into the SetVector.
bool erase(PtrType Ptr)
Remove pointer from the set.
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
void insert_range(Range &&R)
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
size_type count(const T &V) const
count - Return 1 if the element is in the set, 0 otherwise.
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
bool test(unsigned Idx) const
CodeModel::Model getCodeModel() const
Returns the code model.
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
bool has128ByteRedZone(const MachineFunction &MF) const
Return true if the function has a redzone (accessible bytes past the frame of the top of stack functi...
static bool isDataInvariantLoad(MachineInstr &MI)
Returns true if the instruction has no behavior (specified or otherwise) that is based on the value l...
static bool isDataInvariant(MachineInstr &MI)
Returns true if the instruction has no behavior (specified or otherwise) that is based on the value o...
const X86InstrInfo * getInstrInfo() const override
bool isPositionIndependent() const
const X86RegisterInfo * getRegisterInfo() const override
const X86FrameLowering * getFrameLowering() const override
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
@ Kill
The last use of a register.
CondCode getCondFromBranch(const MachineInstr &MI)
int getFirstAddrOperandIdx(const MachineInstr &MI)
Return the index of the instruction's first address operand, if it has a memory reference,...
CondCode GetOppositeBranchCondition(CondCode CC)
GetOppositeBranchCondition - Return the inverse of the specified cond, e.g.
unsigned getCMovOpcode(unsigned RegBytes, bool HasMemoryOperand=false, bool HasNDD=false)
Return a cmov opcode for the given register size in bytes, and operand type.
initializer< Ty > init(const Ty &Val)
NodeAddr< DefNode * > Def
BaseReg
Stack frame base register. Bit 0 of FREInfo.Info.
This is an optimization pass for GlobalISel generic memory operations.
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
auto unique(Range &&R, Predicate P)
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
unsigned Log2_32(uint32_t Value)
Return the floor log base 2 of the specified value, -1 if the value is zero.
FunctionPass * createX86SpeculativeLoadHardeningPass()
auto reverse(ContainerTy &&C)
void sort(IteratorTy Start, IteratorTy End)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >
void erase_if(Container &C, UnaryPredicate P)
Provide a container algorithm similar to C++ Library Fundamentals v2's erase_if which is equivalent t...