61#define PASS_KEY "x86-slh"
62#define DEBUG_TYPE PASS_KEY
64STATISTIC(NumCondBranchesTraced,
"Number of conditional branches traced");
65STATISTIC(NumBranchesUntraced,
"Number of branches unable to trace");
67 "Number of address mode used registers hardaned");
69 "Number of post-load register values hardened");
71 "Number of calls or jumps requiring extra hardening");
72STATISTIC(NumInstsInserted,
"Number of instructions inserted");
73STATISTIC(NumLFENCEsInserted,
"Number of lfence instructions inserted");
76 "x86-speculative-load-hardening",
83 "Use LFENCE along each conditional edge to harden against speculative "
84 "loads rather than conditional movs and poisoned pointers."),
89 cl::desc(
"Harden the value loaded *after* it is loaded by "
90 "flushing the loaded bits to 1. This is hard to do "
91 "in general but can be done easily for GPRs."),
96 cl::desc(
"Use a full speculation fence to harden both call and ret edges "
97 "rather than a lighter weight mitigation."),
102 cl::desc(
"Harden interprocedurally by passing our state in and out of "
103 "functions in the high bits of the stack pointer."),
108 cl::desc(
"Sanitize loads from memory. When disable, no "
109 "significant security is provided."),
114 cl::desc(
"Harden indirect calls and jumps against using speculatively "
115 "stored attacker controlled addresses. This is designed to "
116 "mitigate Spectre v1.2 style attacks."),
126 return "X86 speculative load hardening";
137 struct BlockCondInfo {
149 unsigned InitialReg = 0;
150 unsigned PoisonReg = 0;
164 std::optional<PredState> PS;
189 const DebugLoc &Loc,
unsigned PredStateReg);
201 bool canHardenRegister(
Register Reg);
208 void hardenIndirectCallOrJumpInstr(
215char X86SpeculativeLoadHardeningPass::ID = 0;
217void X86SpeculativeLoadHardeningPass::getAnalysisUsage(
226 assert(!Succ.
isEHPad() &&
"Shouldn't get edges to EH pads!");
240 "Didn't start with the right target!");
250 "Without an unconditional branch, the old layout successor should "
251 "be an actual successor!");
255 UncondBr = &*BrBuilder;
266 "Cannot have a branchless successor and an unconditional branch!");
268 "A non-branch successor must have been a layout successor before "
269 "and now is a layout successor of the new block.");
275 if (SuccCount == 1) {
288 for (
int OpIdx = 1, NumOps =
MI.getNumOperands(); OpIdx < NumOps;
292 assert(OpMBB.
isMBB() &&
"Block operand to a PHI is not a block!");
297 if (SuccCount == 1) {
303 MI.addOperand(MF, OpV);
310 for (
auto &LI : Succ.
liveins())
330 for (
auto &
MI :
MBB) {
337 for (
int OpIdx = 1, NumOps =
MI.getNumOperands(); OpIdx < NumOps;
339 if (!Preds.
insert(
MI.getOperand(OpIdx + 1).getMBB()).second)
353 while (!DupIndices.
empty()) {
357 MI.removeOperand(OpIdx + 1);
358 MI.removeOperand(OpIdx);
376 if (
MI.getOpcode() == X86::LFENCE)
384 if (
MI.getOpcode() == X86::MFENCE)
396bool X86SpeculativeLoadHardeningPass::runOnMachineFunction(
409 TII = Subtarget->getInstrInfo();
410 TRI = Subtarget->getRegisterInfo();
413 PS.emplace(MF, &X86::GR64_NOSPRegClass);
421 hardenEdgesWithLFENCE(MF);
429 auto EntryInsertPt =
Entry.SkipPHIsLabelsAndDebug(
Entry.begin());
439 if (!HasVulnerableLoad && Infos.
empty())
444 const int PoisonVal = -1;
445 PS->PoisonReg =
MRI->createVirtualRegister(PS->RC);
446 BuildMI(Entry, EntryInsertPt, Loc,
TII->get(X86::MOV64ri32), PS->PoisonReg)
459 BuildMI(Entry, EntryInsertPt, Loc,
TII->get(X86::LFENCE));
461 ++NumLFENCEsInserted;
474 PS->InitialReg = extractPredStateFromSP(Entry, EntryInsertPt, Loc);
478 PS->InitialReg =
MRI->createVirtualRegister(PS->RC);
479 Register PredStateSubReg =
MRI->createVirtualRegister(&X86::GR32RegClass);
480 auto ZeroI =
BuildMI(Entry, EntryInsertPt, Loc,
TII->get(X86::MOV32r0),
484 ZeroI->findRegisterDefOperand(X86::EFLAGS,
nullptr);
486 "Must have an implicit def of EFLAGS!");
488 BuildMI(Entry, EntryInsertPt, Loc,
TII->get(X86::SUBREG_TO_REG),
502 PS->SSA.Initialize(PS->InitialReg);
503 PS->SSA.AddAvailableValue(&Entry, PS->InitialReg);
506 auto CMovs = tracePredStateThroughCFG(MF, Infos);
522 PS->SSA.AddAvailableValue(
531 unfoldCallAndJumpLoads(MF);
534 auto IndirectBrCMovs = tracePredStateThroughIndirectBranches(MF);
535 CMovs.append(IndirectBrCMovs.begin(), IndirectBrCMovs.end());
541 tracePredStateThroughBlocksAndHarden(MF);
547 if (!
Op.isReg() ||
Op.getReg() != PS->InitialReg)
550 PS->SSA.RewriteUse(
Op);
553 LLVM_DEBUG(
dbgs() <<
"Final speculative load hardened function:\n"; MF.dump();
554 dbgs() <<
"\n"; MF.verify(
this));
564void X86SpeculativeLoadHardeningPass::hardenEdgesWithLFENCE(
577 if (TermIt ==
MBB.
end() || !TermIt->isBranch())
584 if (!SuccMBB->isEHPad())
592 ++NumLFENCEsInserted;
597X86SpeculativeLoadHardeningPass::collectBlockCondInfo(
MachineFunction &MF) {
627 BlockCondInfo
Info = {&
MBB, {},
nullptr};
633 if (!
MI.isTerminator())
637 if (!
MI.isBranch()) {
638 Info.CondBrs.clear();
644 if (
MI.getOpcode() == X86::JMP_1) {
645 Info.CondBrs.clear();
660 Info.CondBrs.clear();
666 Info.CondBrs.push_back(&
MI);
668 if (
Info.CondBrs.empty()) {
669 ++NumBranchesUntraced;
670 LLVM_DEBUG(
dbgs() <<
"WARNING: unable to secure successors of block:\n";
689X86SpeculativeLoadHardeningPass::tracePredStateThroughCFG(
697 for (
const BlockCondInfo &Info : Infos) {
704 ++NumCondBranchesTraced;
709 UncondBr ? (UncondBr->
getOpcode() == X86::JMP_1
717 ++SuccCounts[UncondSucc];
718 for (
auto *CondBr : CondBrs)
719 ++SuccCounts[CondBr->getOperand(0).getMBB()];
723 auto BuildCheckingBlockForSuccAndConds =
730 (SuccCount == 1 && Succ.pred_size() == 1)
734 bool LiveEFLAGS = Succ.
isLiveIn(X86::EFLAGS);
736 CheckingMBB.addLiveIn(X86::EFLAGS);
739 auto InsertPt = CheckingMBB.begin();
740 assert((InsertPt == CheckingMBB.end() || !InsertPt->isPHI()) &&
741 "Should never have a PHI in the initial checking block as it "
742 "always has a single predecessor!");
746 unsigned CurStateReg = PS->InitialReg;
749 int PredStateSizeInBytes =
TRI->getRegSizeInBits(*PS->RC) / 8;
752 Register UpdatedStateReg =
MRI->createVirtualRegister(PS->RC);
756 TII->get(CMovOp), UpdatedStateReg)
762 if (!LiveEFLAGS &&
Cond == Conds.back())
772 if (CurStateReg == PS->InitialReg)
776 CurStateReg = UpdatedStateReg;
781 PS->SSA.AddAvailableValue(&CheckingMBB, CurStateReg);
784 std::vector<X86::CondCode> UncondCodeSeq;
785 for (
auto *CondBr : CondBrs) {
787 int &SuccCount = SuccCounts[&Succ];
791 UncondCodeSeq.push_back(
Cond);
793 BuildCheckingBlockForSuccAndConds(
MBB, Succ, SuccCount, CondBr, UncondBr,
816 assert(SuccCounts[UncondSucc] == 1 &&
817 "We should never have more than one edge to the unconditional "
818 "successor at this point because every other edge must have been "
823 UncondCodeSeq.erase(
llvm::unique(UncondCodeSeq), UncondCodeSeq.end());
826 BuildCheckingBlockForSuccAndConds(
MBB, *UncondSucc, 1,
827 UncondBr, UncondBr, UncondCodeSeq);
842 unsigned UnfoldedOpc =
TII.getOpcodeAfterMemoryUnfold(
843 Opcode,
true,
false, &Index);
845 return TII.getRegClass(MCID, Index, &
TII.getRegisterInfo(), MF);
848void X86SpeculativeLoadHardeningPass::unfoldCallAndJumpLoads(
855 if (!
MI.isCall() && !
MI.isBranch())
861 switch (
MI.getOpcode()) {
864 dbgs() <<
"ERROR: Found an unexpected loading branch or call "
870 case X86::FARCALL16m:
871 case X86::FARCALL32m:
872 case X86::FARCALL64m:
881 case X86::CALL16m_NT:
883 case X86::CALL32m_NT:
885 case X86::CALL64m_NT:
892 case X86::TAILJMPm64:
893 case X86::TAILJMPm64_REX:
895 case X86::TCRETURNmi64:
896 case X86::TCRETURNmi: {
903 <<
"ERROR: Unable to unfold load from instruction:\n";
912 TII->unfoldMemoryOperand(MF,
MI, Reg,
true,
916 "Computed unfolded register class but failed to unfold");
918 for (
auto *NewMI : NewMIs)
922 if (
MI.isCandidateForCallSiteEntry())
923 MF.eraseCallSiteInfo(&
MI);
925 MI.eraseFromParent();
927 dbgs() <<
"Unfolded load successfully into:\n";
928 for (
auto *NewMI : NewMIs) {
958X86SpeculativeLoadHardeningPass::tracePredStateThroughIndirectBranches(
965 TargetAddrSSA.Initialize(
MRI->createVirtualRegister(&X86::GR64RegClass));
1004 case X86::JMP16m_NT:
1006 case X86::JMP32m_NT:
1008 case X86::JMP64m_NT:
1014 "Support for 16-bit indirect branches is not implemented.");
1017 "Support for 32-bit indirect branches is not implemented.");
1026 return !OtherTI.isDebugInstr() && &OtherTI != &TI;
1029 dbgs() <<
"ERROR: Found other terminators in a block with an indirect "
1030 "branch! This is not yet supported! Terminator sequence:\n";
1040 TargetAddrSSA.AddAvailableValue(&
MBB, TargetReg);
1045 IndirectTargetMBBs.
insert(Succ);
1052 if (IndirectTargetMBBs.
empty())
1060 if (!IndirectTargetMBBs.
count(&
MBB))
1067 "Unexpected EH pad as target of an indirect branch!");
1075 "Cannot check within a block that already has live-in EFLAGS!");
1082 if (IndirectTerminatedMBBs.
count(Pred))
1091 return Succ->isEHPad() || Succ == &MBB;
1094 dbgs() <<
"ERROR: Found conditional entry to target of indirect "
1100 "an indirect branch!");
1106 auto InsertPt = Pred->getFirstTerminator();
1107 Register TargetReg =
MRI->createVirtualRegister(&X86::GR64RegClass);
1109 !Subtarget->isPositionIndependent()) {
1112 TII->get(X86::MOV64ri32), TargetReg)
1132 TargetAddrSSA.AddAvailableValue(Pred, TargetReg);
1140 Register TargetReg = TargetAddrSSA.GetValueInMiddleOfBlock(&
MBB);
1148 !Subtarget->isPositionIndependent()) {
1158 Register AddrReg =
MRI->createVirtualRegister(&X86::GR64RegClass);
1178 int PredStateSizeInBytes =
TRI->getRegSizeInBits(*PS->RC) / 8;
1180 Register UpdatedStateReg =
MRI->createVirtualRegister(PS->RC);
1194 PS->SSA.AddAvailableValue(&
MBB, UpdatedStateReg);
1205 MI.findRegisterDefOperand(X86::EFLAGS,
nullptr)) {
1206 return !DefOp->isDead();
1217 MI.findRegisterDefOperand(X86::EFLAGS,
nullptr)) {
1219 if (DefOp->isDead())
1227 if (
MI.killsRegister(X86::EFLAGS, &
TRI))
1263void X86SpeculativeLoadHardeningPass::tracePredStateThroughBlocksAndHarden(
1300 return Op.isReg() && LoadDepRegs.test(Op.getReg());
1304 LoadDepRegs.
set(
Def.getReg());
1309 if (
MI.getOpcode() == X86::LFENCE)
1317 if (
MI.getOpcode() == X86::MFENCE)
1322 if (MemRefBeginIdx < 0) {
1324 <<
"WARNING: unable to harden loading instruction: ";
1336 unsigned BaseReg = 0, IndexReg = 0;
1337 if (!BaseMO.
isFI() && BaseMO.
getReg() != X86::RIP &&
1338 BaseMO.
getReg() != X86::NoRegister)
1339 BaseReg = BaseMO.
getReg();
1340 if (IndexMO.
getReg() != X86::NoRegister)
1341 IndexReg = IndexMO.
getReg();
1343 if (!BaseReg && !IndexReg)
1351 if ((BaseReg && LoadDepRegs.
test(BaseReg)) ||
1352 (IndexReg && LoadDepRegs.
test(IndexReg)))
1362 MI.getOperand(0).isReg() &&
1363 canHardenRegister(
MI.getOperand(0).getReg()) &&
1364 !HardenedAddrRegs.
count(BaseReg) &&
1365 !HardenedAddrRegs.
count(IndexReg)) {
1367 HardenedAddrRegs.
insert(
MI.getOperand(0).getReg());
1375 HardenedAddrRegs.
insert(BaseReg);
1377 HardenedAddrRegs.
insert(IndexReg);
1381 LoadDepRegs.
set(
Def.getReg());
1393 "Requested to harden both the address and def of a load!");
1396 if (HardenLoadAddr.
erase(&
MI)) {
1398 assert(MemRefBeginIdx >= 0 &&
"Cannot have an invalid index here!");
1404 hardenLoadAddr(
MI, BaseMO, IndexMO, AddrRegToHardenedReg);
1410 if (HardenPostLoad.
erase(&
MI)) {
1411 assert(!
MI.isCall() &&
"Must not try to post-load harden a call!");
1419 MachineInstr *SunkMI = sinkPostLoadHardenedInst(
MI, HardenPostLoad);
1424 if (SunkMI != &
MI) {
1431 HardenPostLoad.
insert(SunkMI);
1436 unsigned HardenedReg = hardenPostLoad(
MI);
1439 AddrRegToHardenedReg[HardenedReg] = HardenedReg;
1450 hardenIndirectCallOrJumpInstr(
MI, AddrRegToHardenedReg);
1457 if (!
MI.isCall() && !
MI.isReturn())
1462 if (
MI.isReturn() && !
MI.isCall()) {
1463 hardenReturnInstr(
MI);
1470 assert(
MI.isCall() &&
"Should only reach here for calls!");
1471 tracePredStateThroughCall(
MI);
1474 HardenPostLoad.
clear();
1475 HardenLoadAddr.
clear();
1476 HardenedAddrRegs.
clear();
1477 AddrRegToHardenedReg.
clear();
1482 LoadDepRegs.
clear();
1492unsigned X86SpeculativeLoadHardeningPass::saveEFLAGS(
1510void X86SpeculativeLoadHardeningPass::restoreEFLAGS(
1521void X86SpeculativeLoadHardeningPass::mergePredStateIntoSP(
1523 const DebugLoc &Loc,
unsigned PredStateReg) {
1524 Register TmpReg =
MRI->createVirtualRegister(PS->RC);
1528 auto ShiftI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::SHL64ri), TmpReg)
1533 auto OrI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::OR64rr), X86::RSP)
1541unsigned X86SpeculativeLoadHardeningPass::extractPredStateFromSP(
1544 Register PredStateReg =
MRI->createVirtualRegister(PS->RC);
1545 Register TmpReg =
MRI->createVirtualRegister(PS->RC);
1550 BuildMI(
MBB, InsertPt, Loc,
TII->get(TargetOpcode::COPY), TmpReg)
1553 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::SAR64ri), PredStateReg)
1555 .
addImm(
TRI->getRegSizeInBits(*PS->RC) - 1);
1559 return PredStateReg;
1562void X86SpeculativeLoadHardeningPass::hardenLoadAddr(
1574 if (BaseMO.
isFI()) {
1578 dbgs() <<
" Skipping hardening base of explicit stack frame load: ";
1580 }
else if (BaseMO.
getReg() == X86::RSP) {
1585 "Explicit RSP access with dynamic index!");
1587 dbgs() <<
" Cannot harden base of explicit RSP offset in a load!");
1588 }
else if (BaseMO.
getReg() == X86::RIP ||
1589 BaseMO.
getReg() == X86::NoRegister) {
1599 dbgs() <<
" Cannot harden base of "
1600 << (BaseMO.
getReg() == X86::RIP ?
"RIP-relative" :
"no-base")
1601 <<
" address in a load!");
1604 "Only allowed to have a frame index or register base.");
1608 if (IndexMO.
getReg() != X86::NoRegister &&
1609 (HardenOpRegs.
empty() ||
1610 HardenOpRegs.
front()->getReg() != IndexMO.
getReg()))
1614 "Should have exactly one or two registers to harden!");
1616 HardenOpRegs[0]->getReg() != HardenOpRegs[1]->getReg()) &&
1617 "Should not have two of the same registers!");
1622 auto It = AddrRegToHardenedReg.
find(
Op->getReg());
1623 if (It == AddrRegToHardenedReg.
end())
1628 Op->setReg(It->second);
1632 if (HardenOpRegs.
empty())
1636 Register StateReg = PS->SSA.GetValueAtEndOfBlock(&
MBB);
1638 auto InsertPt =
MI.getIterator();
1643 unsigned FlagsReg = 0;
1644 if (EFLAGSLive && !Subtarget->hasBMI2()) {
1646 FlagsReg = saveEFLAGS(
MBB, InsertPt, Loc);
1651 auto *OpRC =
MRI->getRegClass(OpReg);
1652 Register TmpReg =
MRI->createVirtualRegister(OpRC);
1656 if (!Subtarget->hasVLX() && (OpRC->hasSuperClassEq(&X86::VR128RegClass) ||
1657 OpRC->hasSuperClassEq(&X86::VR256RegClass))) {
1658 assert(Subtarget->hasAVX2() &&
"AVX2-specific register classes!");
1659 bool Is128Bit = OpRC->hasSuperClassEq(&X86::VR128RegClass);
1664 Register VStateReg =
MRI->createVirtualRegister(&X86::VR128RegClass);
1666 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::VMOV64toPQIrr), VStateReg)
1673 Register VBStateReg =
MRI->createVirtualRegister(OpRC);
1674 auto BroadcastI =
BuildMI(
MBB, InsertPt, Loc,
1675 TII->get(Is128Bit ? X86::VPBROADCASTQrr
1676 : X86::VPBROADCASTQYrr),
1681 LLVM_DEBUG(
dbgs() <<
" Inserting broadcast: "; BroadcastI->dump();
1687 TII->get(Is128Bit ? X86::VPORrr : X86::VPORYrr), TmpReg)
1693 }
else if (OpRC->hasSuperClassEq(&X86::VR128XRegClass) ||
1694 OpRC->hasSuperClassEq(&X86::VR256XRegClass) ||
1695 OpRC->hasSuperClassEq(&X86::VR512RegClass)) {
1696 assert(Subtarget->hasAVX512() &&
"AVX512-specific register classes!");
1697 bool Is128Bit = OpRC->hasSuperClassEq(&X86::VR128XRegClass);
1698 bool Is256Bit = OpRC->hasSuperClassEq(&X86::VR256XRegClass);
1699 if (Is128Bit || Is256Bit)
1700 assert(Subtarget->hasVLX() &&
"AVX512VL-specific register classes!");
1703 Register VStateReg =
MRI->createVirtualRegister(OpRC);
1704 unsigned BroadcastOp =
Is128Bit ? X86::VPBROADCASTQrZ128rr
1705 : Is256Bit ? X86::VPBROADCASTQrZ256rr
1706 : X86::VPBROADCASTQrZrr;
1708 BuildMI(
MBB, InsertPt, Loc,
TII->get(BroadcastOp), VStateReg)
1712 LLVM_DEBUG(
dbgs() <<
" Inserting broadcast: "; BroadcastI->dump();
1716 unsigned OrOp =
Is128Bit ? X86::VPORQZ128rr
1717 : Is256Bit ? X86::VPORQZ256rr : X86::VPORQZrr;
1718 auto OrI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(OrOp), TmpReg)
1726 assert(OpRC->hasSuperClassEq(&X86::GR64RegClass) &&
1727 "Not a supported register class for address hardening!");
1731 auto OrI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::OR64rr), TmpReg)
1741 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::SHRX64rr), TmpReg)
1753 "Should not have checked this register yet!");
1754 AddrRegToHardenedReg[
Op->getReg()] = TmpReg;
1756 ++NumAddrRegsHardened;
1761 restoreEFLAGS(
MBB, InsertPt, Loc, FlagsReg);
1764MachineInstr *X86SpeculativeLoadHardeningPass::sinkPostLoadHardenedInst(
1767 "Cannot get here with a non-invariant load!");
1769 "Cannot get here with a data invariant load "
1770 "that interferes with EFLAGS!");
1773 auto SinkCheckToSingleUse =
1790 "Data variant instruction being hardened!");
1797 assert(MemRefBeginIdx >= 0 &&
1798 "Should always have mem references here!");
1804 if ((BaseMO.
isReg() && BaseMO.
getReg() == DefReg) ||
1825 if (
UseMI.getDesc().getNumDefs() > 1)
1832 if (!canHardenRegister(UseDefReg))
1835 SingleUseMI = &
UseMI;
1840 return {SingleUseMI};
1844 while (std::optional<MachineInstr *> SingleUse = SinkCheckToSingleUse(*
MI)) {
1854bool X86SpeculativeLoadHardeningPass::canHardenRegister(
Register Reg) {
1856 if (!
Reg.isVirtual())
1859 auto *RC =
MRI->getRegClass(Reg);
1860 int RegBytes =
TRI->getRegSizeInBits(*RC) / 8;
1865 unsigned RegIdx =
Log2_32(RegBytes);
1866 assert(RegIdx < 4 &&
"Unsupported register size");
1876 &X86::GR8_NOREXRegClass, &X86::GR16_NOREXRegClass,
1877 &X86::GR32_NOREXRegClass, &X86::GR64_NOREXRegClass};
1878 if (RC == NOREXRegClasses[RegIdx])
1882 &X86::GR8RegClass, &X86::GR16RegClass, &X86::GR32RegClass,
1883 &X86::GR64RegClass};
1901unsigned X86SpeculativeLoadHardeningPass::hardenValueInRegister(
1904 assert(canHardenRegister(Reg) &&
"Cannot harden this register!");
1906 auto *RC =
MRI->getRegClass(Reg);
1907 int Bytes =
TRI->getRegSizeInBits(*RC) / 8;
1908 Register StateReg = PS->SSA.GetValueAtEndOfBlock(&
MBB);
1909 assert((Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8) &&
1910 "Unknown register size");
1914 unsigned SubRegImms[] = {X86::sub_8bit, X86::sub_16bit, X86::sub_32bit};
1915 unsigned SubRegImm = SubRegImms[
Log2_32(Bytes)];
1916 Register NarrowStateReg =
MRI->createVirtualRegister(RC);
1917 BuildMI(
MBB, InsertPt, Loc,
TII->get(TargetOpcode::COPY), NarrowStateReg)
1918 .
addReg(StateReg, 0, SubRegImm);
1919 StateReg = NarrowStateReg;
1922 unsigned FlagsReg = 0;
1924 FlagsReg = saveEFLAGS(
MBB, InsertPt, Loc);
1927 unsigned OrOpCodes[] = {X86::OR8rr, X86::OR16rr, X86::OR32rr, X86::OR64rr};
1928 unsigned OrOpCode = OrOpCodes[
Log2_32(Bytes)];
1929 auto OrI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(OrOpCode), NewReg)
1937 restoreEFLAGS(
MBB, InsertPt, Loc, FlagsReg);
1951unsigned X86SpeculativeLoadHardeningPass::hardenPostLoad(
MachineInstr &
MI) {
1955 auto &DefOp =
MI.getOperand(0);
1956 Register OldDefReg = DefOp.getReg();
1957 auto *DefRC =
MRI->getRegClass(OldDefReg);
1962 Register UnhardenedReg =
MRI->createVirtualRegister(DefRC);
1963 DefOp.setReg(UnhardenedReg);
1968 unsigned HardenedReg = hardenValueInRegister(
1969 UnhardenedReg,
MBB, std::next(
MI.getIterator()), Loc);
1973 MRI->replaceRegWith( OldDefReg, HardenedReg);
1975 ++NumPostLoadRegsHardened;
2002void X86SpeculativeLoadHardeningPass::hardenReturnInstr(
MachineInstr &
MI) {
2005 auto InsertPt =
MI.getIterator();
2015 mergePredStateIntoSP(
MBB, InsertPt, Loc, PS->SSA.GetValueAtEndOfBlock(&
MBB));
2048void X86SpeculativeLoadHardeningPass::tracePredStateThroughCall(
2052 auto InsertPt =
MI.getIterator();
2065 BuildMI(
MBB, std::next(InsertPt), Loc,
TII->get(X86::LFENCE));
2067 ++NumLFENCEsInserted;
2073 Register StateReg = PS->SSA.GetValueAtEndOfBlock(&
MBB);
2074 mergePredStateIntoSP(
MBB, InsertPt, Loc, StateReg);
2089 MI.setPostInstrSymbol(MF, RetSymbol);
2092 unsigned ExpectedRetAddrReg = 0;
2097 if (!Subtarget->getFrameLowering()->has128ByteRedZone(MF) ||
2117 ExpectedRetAddrReg =
MRI->createVirtualRegister(AddrRC);
2119 !Subtarget->isPositionIndependent()) {
2120 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::MOV64ri32), ExpectedRetAddrReg)
2123 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::LEA64r), ExpectedRetAddrReg)
2139 if (!ExpectedRetAddrReg) {
2140 ExpectedRetAddrReg =
MRI->createVirtualRegister(AddrRC);
2141 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::MOV64rm), ExpectedRetAddrReg)
2151 unsigned NewStateReg = extractPredStateFromSP(
MBB, InsertPt, Loc);
2157 !Subtarget->isPositionIndependent()) {
2164 Register ActualRetAddrReg =
MRI->createVirtualRegister(AddrRC);
2165 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::LEA64r), ActualRetAddrReg)
2178 int PredStateSizeInBytes =
TRI->getRegSizeInBits(*PS->RC) / 8;
2181 Register UpdatedStateReg =
MRI->createVirtualRegister(PS->RC);
2182 auto CMovI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(CMovOp), UpdatedStateReg)
2190 PS->SSA.AddAvailableValue(&
MBB, UpdatedStateReg);
2208void X86SpeculativeLoadHardeningPass::hardenIndirectCallOrJumpInstr(
2211 switch (
MI.getOpcode()) {
2212 case X86::FARCALL16m:
2213 case X86::FARCALL32m:
2214 case X86::FARCALL64m:
2215 case X86::FARJMP16m:
2216 case X86::FARJMP32m:
2217 case X86::FARJMP64m:
2228 assert(!
MI.mayLoad() &&
"Found a lingering loading instruction!");
2232 if (!
MI.getOperand(0).isReg())
2237 auto &TargetOp =
MI.getOperand(0);
2238 Register OldTargetReg = TargetOp.getReg();
2243 unsigned &HardenedTargetReg = AddrRegToHardenedReg[OldTargetReg];
2252 if (!HardenedTargetReg)
2253 HardenedTargetReg = hardenValueInRegister(
2254 OldTargetReg, *
MI.getParent(),
MI.getIterator(),
MI.getDebugLoc());
2257 TargetOp.setReg(HardenedTargetReg);
2259 ++NumCallsOrJumpsHardened;
2263 "X86 speculative load hardener",
false,
false)
2268 return new X86SpeculativeLoadHardeningPass();
unsigned const MachineRegisterInfo * MRI
MachineInstrBuilder & UseMI
AMDGPU Mark last scratch load
Analysis containing CSE Info
This file defines the DenseMap class.
DenseMap< Block *, BlockRelaxAux > Blocks
const HexagonInstrInfo * TII
This file declares the MachineConstantPool class which is an abstract constant pool to keep track of ...
unsigned const TargetRegisterInfo * TRI
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
const SmallVectorImpl< MachineOperand > & Cond
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
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(MachineFunction &MF, 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)
X86 speculative load hardener
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.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
This class represents an Operation in the Expression.
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.
unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, ArrayRef< MachineOperand > Cond, const DebugLoc &DL, int *BytesAdded=nullptr) const override
Insert branch code into the end of the specified MachineBasicBlock.
MCSymbol * createTempSymbol()
Create a temporary symbol with a unique name.
Describe properties that are true of each instruction in the target description file.
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
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.
void replaceSuccessor(MachineBasicBlock *Old, MachineBasicBlock *New)
Replace successor OLD with NEW and update probability info.
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()
iterator SkipPHIsAndLabels(iterator I)
Return the first instruction in MBB after I that is not a PHI or a label.
void splitSuccessor(MachineBasicBlock *Old, MachineBasicBlock *New, bool NormalizeSuccProbs=false)
Split the old successor into old plus new and updates the probability info.
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.
iterator getFirstTerminator()
Returns an iterator to the first terminator instruction of this basic block.
unsigned succ_size() 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 ...
void addSuccessor(MachineBasicBlock *Succ, BranchProbability Prob=BranchProbability::getUnknown())
Add Succ as a successor of this MachineBasicBlock.
reverse_instr_iterator instr_rend()
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()
bool isSuccessor(const MachineBasicBlock *MBB) const
Return true if the specified MBB is a successor of this block.
iterator_range< pred_iterator > predecessors()
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...
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.
virtual bool runOnMachineFunction(MachineFunction &MF)=0
runOnMachineFunction - This method must be overloaded to perform the desired machine code transformat...
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.
MachineBasicBlock * CreateMachineBasicBlock(const BasicBlock *BB=nullptr, std::optional< UniqueBBID > BBID=std::nullopt)
CreateMachineBasicBlock - Allocate a new MachineBasicBlock.
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
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.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
MachineSSAUpdater - This class updates SSA form for a set of virtual registers defined in multiple bl...
virtual StringRef getPassName() const
getPassName - Return a nice clean name for a pass.
Wrapper class representing virtual and physical registers.
A templated base class for SmallPtrSet which provides the typesafe interface that is common across al...
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.
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.
A SetVector that performs no allocations if smaller than a certain size.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
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.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
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
StringRef - Represent a constant reference to a string, i.e.
CodeModel::Model getCodeModel() const
Returns the code model.
bool hasSuperClassEq(const TargetRegisterClass *RC) const
Returns true if RC is a super-class of or equal to this class.
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
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...
#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.
Reg
All possible values of the reg field in the ModR/M byte.
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
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)
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
void erase_if(Container &C, UnaryPredicate P)
Provide a container algorithm similar to C++ Library Fundamentals v2's erase_if which is equivalent t...