63#define PASS_KEY "x86-slh"
64#define DEBUG_TYPE PASS_KEY
66STATISTIC(NumCondBranchesTraced,
"Number of conditional branches traced");
67STATISTIC(NumBranchesUntraced,
"Number of branches unable to trace");
69 "Number of address mode used registers hardaned");
71 "Number of post-load register values hardened");
73 "Number of calls or jumps requiring extra hardening");
74STATISTIC(NumInstsInserted,
"Number of instructions inserted");
75STATISTIC(NumLFENCEsInserted,
"Number of lfence instructions inserted");
78 "x86-speculative-load-hardening",
85 "Use LFENCE along each conditional edge to harden against speculative "
86 "loads rather than conditional movs and poisoned pointers."),
91 cl::desc(
"Harden the value loaded *after* it is loaded by "
92 "flushing the loaded bits to 1. This is hard to do "
93 "in general but can be done easily for GPRs."),
98 cl::desc(
"Use a full speculation fence to harden both call and ret edges "
99 "rather than a lighter weight mitigation."),
104 cl::desc(
"Harden interprocedurally by passing our state in and out of "
105 "functions in the high bits of the stack pointer."),
110 cl::desc(
"Sanitize loads from memory. When disable, no "
111 "significant security is provided."),
116 cl::desc(
"Harden indirect calls and jumps against using speculatively "
117 "stored attacker controlled addresses. This is designed to "
118 "mitigate Spectre v1.2 style attacks."),
128 return "X86 speculative load hardening";
139 struct BlockCondInfo {
151 unsigned InitialReg = 0;
152 unsigned PoisonReg = 0;
166 std::optional<PredState> PS;
191 const DebugLoc &Loc,
unsigned PredStateReg);
203 bool canHardenRegister(
Register Reg);
210 void hardenIndirectCallOrJumpInstr(
217char X86SpeculativeLoadHardeningPass::ID = 0;
219void X86SpeculativeLoadHardeningPass::getAnalysisUsage(
228 assert(!Succ.
isEHPad() &&
"Shouldn't get edges to EH pads!");
242 "Didn't start with the right target!");
252 "Without an unconditional branch, the old layout successor should "
253 "be an actual successor!");
257 UncondBr = &*BrBuilder;
268 "Cannot have a branchless successor and an unconditional branch!");
270 "A non-branch successor must have been a layout successor before "
271 "and now is a layout successor of the new block.");
277 if (SuccCount == 1) {
290 for (
int OpIdx = 1, NumOps =
MI.getNumOperands(); OpIdx < NumOps;
294 assert(OpMBB.
isMBB() &&
"Block operand to a PHI is not a block!");
299 if (SuccCount == 1) {
305 MI.addOperand(MF, OpV);
312 for (
auto &LI : Succ.
liveins())
332 for (
auto &
MI :
MBB) {
339 for (
int OpIdx = 1, NumOps =
MI.getNumOperands(); OpIdx < NumOps;
341 if (!Preds.
insert(
MI.getOperand(OpIdx + 1).getMBB()).second)
355 while (!DupIndices.
empty()) {
359 MI.removeOperand(OpIdx + 1);
360 MI.removeOperand(OpIdx);
378 if (
MI.getOpcode() == X86::LFENCE)
386 if (
MI.getOpcode() == X86::MFENCE)
398bool X86SpeculativeLoadHardeningPass::runOnMachineFunction(
411 TII = Subtarget->getInstrInfo();
412 TRI = Subtarget->getRegisterInfo();
415 PS.emplace(MF, &X86::GR64_NOSPRegClass);
423 hardenEdgesWithLFENCE(MF);
431 auto EntryInsertPt =
Entry.SkipPHIsLabelsAndDebug(
Entry.begin());
441 if (!HasVulnerableLoad && Infos.
empty())
446 const int PoisonVal = -1;
447 PS->PoisonReg =
MRI->createVirtualRegister(PS->RC);
448 BuildMI(Entry, EntryInsertPt, Loc,
TII->get(X86::MOV64ri32), PS->PoisonReg)
461 BuildMI(Entry, EntryInsertPt, Loc,
TII->get(X86::LFENCE));
463 ++NumLFENCEsInserted;
476 PS->InitialReg = extractPredStateFromSP(Entry, EntryInsertPt, Loc);
480 PS->InitialReg =
MRI->createVirtualRegister(PS->RC);
481 Register PredStateSubReg =
MRI->createVirtualRegister(&X86::GR32RegClass);
482 auto ZeroI =
BuildMI(Entry, EntryInsertPt, Loc,
TII->get(X86::MOV32r0),
486 ZeroI->findRegisterDefOperand(X86::EFLAGS,
nullptr);
488 "Must have an implicit def of EFLAGS!");
490 BuildMI(Entry, EntryInsertPt, Loc,
TII->get(X86::SUBREG_TO_REG),
504 PS->SSA.Initialize(PS->InitialReg);
505 PS->SSA.AddAvailableValue(&Entry, PS->InitialReg);
508 auto CMovs = tracePredStateThroughCFG(MF, Infos);
524 PS->SSA.AddAvailableValue(
533 unfoldCallAndJumpLoads(MF);
536 auto IndirectBrCMovs = tracePredStateThroughIndirectBranches(MF);
537 CMovs.append(IndirectBrCMovs.begin(), IndirectBrCMovs.end());
543 tracePredStateThroughBlocksAndHarden(MF);
549 if (!
Op.isReg() ||
Op.getReg() != PS->InitialReg)
552 PS->SSA.RewriteUse(
Op);
555 LLVM_DEBUG(
dbgs() <<
"Final speculative load hardened function:\n"; MF.dump();
556 dbgs() <<
"\n"; MF.verify(
this));
566void X86SpeculativeLoadHardeningPass::hardenEdgesWithLFENCE(
579 if (TermIt ==
MBB.
end() || !TermIt->isBranch())
586 if (!SuccMBB->isEHPad())
594 ++NumLFENCEsInserted;
599X86SpeculativeLoadHardeningPass::collectBlockCondInfo(
MachineFunction &MF) {
629 BlockCondInfo
Info = {&
MBB, {},
nullptr};
635 if (!
MI.isTerminator())
639 if (!
MI.isBranch()) {
640 Info.CondBrs.clear();
646 if (
MI.getOpcode() == X86::JMP_1) {
647 Info.CondBrs.clear();
662 Info.CondBrs.clear();
668 Info.CondBrs.push_back(&
MI);
670 if (
Info.CondBrs.empty()) {
671 ++NumBranchesUntraced;
672 LLVM_DEBUG(
dbgs() <<
"WARNING: unable to secure successors of block:\n";
691X86SpeculativeLoadHardeningPass::tracePredStateThroughCFG(
699 for (
const BlockCondInfo &Info : Infos) {
706 ++NumCondBranchesTraced;
711 UncondBr ? (UncondBr->
getOpcode() == X86::JMP_1
719 ++SuccCounts[UncondSucc];
720 for (
auto *CondBr : CondBrs)
721 ++SuccCounts[CondBr->getOperand(0).getMBB()];
725 auto BuildCheckingBlockForSuccAndConds =
732 (SuccCount == 1 && Succ.pred_size() == 1)
736 bool LiveEFLAGS = Succ.
isLiveIn(X86::EFLAGS);
738 CheckingMBB.addLiveIn(X86::EFLAGS);
741 auto InsertPt = CheckingMBB.begin();
742 assert((InsertPt == CheckingMBB.end() || !InsertPt->isPHI()) &&
743 "Should never have a PHI in the initial checking block as it "
744 "always has a single predecessor!");
748 unsigned CurStateReg = PS->InitialReg;
751 int PredStateSizeInBytes =
TRI->getRegSizeInBits(*PS->RC) / 8;
754 Register UpdatedStateReg =
MRI->createVirtualRegister(PS->RC);
758 TII->get(CMovOp), UpdatedStateReg)
764 if (!LiveEFLAGS &&
Cond == Conds.back())
774 if (CurStateReg == PS->InitialReg)
778 CurStateReg = UpdatedStateReg;
783 PS->SSA.AddAvailableValue(&CheckingMBB, CurStateReg);
786 std::vector<X86::CondCode> UncondCodeSeq;
787 for (
auto *CondBr : CondBrs) {
789 int &SuccCount = SuccCounts[&Succ];
793 UncondCodeSeq.push_back(
Cond);
795 BuildCheckingBlockForSuccAndConds(
MBB, Succ, SuccCount, CondBr, UncondBr,
818 assert(SuccCounts[UncondSucc] == 1 &&
819 "We should never have more than one edge to the unconditional "
820 "successor at this point because every other edge must have been "
825 UncondCodeSeq.erase(
llvm::unique(UncondCodeSeq), UncondCodeSeq.end());
828 BuildCheckingBlockForSuccAndConds(
MBB, *UncondSucc, 1,
829 UncondBr, UncondBr, UncondCodeSeq);
844 unsigned UnfoldedOpc =
TII.getOpcodeAfterMemoryUnfold(
845 Opcode,
true,
false, &
Index);
847 return TII.getRegClass(MCID,
Index, &
TII.getRegisterInfo(), MF);
850void X86SpeculativeLoadHardeningPass::unfoldCallAndJumpLoads(
857 if (!
MI.isCall() && !
MI.isBranch())
863 switch (
MI.getOpcode()) {
866 dbgs() <<
"ERROR: Found an unexpected loading branch or call "
872 case X86::FARCALL16m:
873 case X86::FARCALL32m:
874 case X86::FARCALL64m:
883 case X86::CALL16m_NT:
885 case X86::CALL32m_NT:
887 case X86::CALL64m_NT:
894 case X86::TAILJMPm64:
895 case X86::TAILJMPm64_REX:
897 case X86::TCRETURNmi64:
898 case X86::TCRETURNmi: {
905 <<
"ERROR: Unable to unfold load from instruction:\n";
914 TII->unfoldMemoryOperand(MF,
MI, Reg,
true,
918 "Computed unfolded register class but failed to unfold");
920 for (
auto *NewMI : NewMIs)
924 if (
MI.isCandidateForCallSiteEntry())
925 MF.eraseCallSiteInfo(&
MI);
927 MI.eraseFromParent();
929 dbgs() <<
"Unfolded load successfully into:\n";
930 for (
auto *NewMI : NewMIs) {
960X86SpeculativeLoadHardeningPass::tracePredStateThroughIndirectBranches(
967 TargetAddrSSA.Initialize(
MRI->createVirtualRegister(&X86::GR64RegClass));
1000 case X86::FARJMP64m:
1006 case X86::JMP16m_NT:
1008 case X86::JMP32m_NT:
1010 case X86::JMP64m_NT:
1016 "Support for 16-bit indirect branches is not implemented.");
1019 "Support for 32-bit indirect branches is not implemented.");
1028 return !OtherTI.isDebugInstr() && &OtherTI != &TI;
1031 dbgs() <<
"ERROR: Found other terminators in a block with an indirect "
1032 "branch! This is not yet supported! Terminator sequence:\n";
1042 TargetAddrSSA.AddAvailableValue(&
MBB, TargetReg);
1047 IndirectTargetMBBs.
insert(Succ);
1054 if (IndirectTargetMBBs.
empty())
1062 if (!IndirectTargetMBBs.
count(&
MBB))
1069 "Unexpected EH pad as target of an indirect branch!");
1077 "Cannot check within a block that already has live-in EFLAGS!");
1084 if (IndirectTerminatedMBBs.
count(Pred))
1093 return Succ->isEHPad() || Succ == &MBB;
1096 dbgs() <<
"ERROR: Found conditional entry to target of indirect "
1102 "an indirect branch!");
1108 auto InsertPt = Pred->getFirstTerminator();
1109 Register TargetReg =
MRI->createVirtualRegister(&X86::GR64RegClass);
1111 !Subtarget->isPositionIndependent()) {
1114 TII->get(X86::MOV64ri32), TargetReg)
1134 TargetAddrSSA.AddAvailableValue(Pred, TargetReg);
1142 Register TargetReg = TargetAddrSSA.GetValueInMiddleOfBlock(&
MBB);
1150 !Subtarget->isPositionIndependent()) {
1160 Register AddrReg =
MRI->createVirtualRegister(&X86::GR64RegClass);
1180 int PredStateSizeInBytes =
TRI->getRegSizeInBits(*PS->RC) / 8;
1182 Register UpdatedStateReg =
MRI->createVirtualRegister(PS->RC);
1196 PS->SSA.AddAvailableValue(&
MBB, UpdatedStateReg);
1207 MI.findRegisterDefOperand(X86::EFLAGS,
nullptr)) {
1208 return !DefOp->isDead();
1219 MI.findRegisterDefOperand(X86::EFLAGS,
nullptr)) {
1221 if (DefOp->isDead())
1229 if (
MI.killsRegister(X86::EFLAGS, &
TRI))
1265void X86SpeculativeLoadHardeningPass::tracePredStateThroughBlocksAndHarden(
1302 return Op.isReg() && LoadDepRegs.test(Op.getReg());
1306 LoadDepRegs.
set(
Def.getReg());
1311 if (
MI.getOpcode() == X86::LFENCE)
1319 if (
MI.getOpcode() == X86::MFENCE)
1324 if (MemRefBeginIdx < 0) {
1326 <<
"WARNING: unable to harden loading instruction: ";
1338 unsigned BaseReg = 0, IndexReg = 0;
1339 if (!BaseMO.
isFI() && BaseMO.
getReg() != X86::RIP &&
1340 BaseMO.
getReg() != X86::NoRegister)
1341 BaseReg = BaseMO.
getReg();
1342 if (IndexMO.
getReg() != X86::NoRegister)
1343 IndexReg = IndexMO.
getReg();
1345 if (!BaseReg && !IndexReg)
1353 if ((BaseReg && LoadDepRegs.
test(BaseReg)) ||
1354 (IndexReg && LoadDepRegs.
test(IndexReg)))
1364 MI.getOperand(0).isReg() &&
1365 canHardenRegister(
MI.getOperand(0).getReg()) &&
1366 !HardenedAddrRegs.
count(BaseReg) &&
1367 !HardenedAddrRegs.
count(IndexReg)) {
1369 HardenedAddrRegs.
insert(
MI.getOperand(0).getReg());
1377 HardenedAddrRegs.
insert(BaseReg);
1379 HardenedAddrRegs.
insert(IndexReg);
1383 LoadDepRegs.
set(
Def.getReg());
1395 "Requested to harden both the address and def of a load!");
1398 if (HardenLoadAddr.
erase(&
MI)) {
1400 assert(MemRefBeginIdx >= 0 &&
"Cannot have an invalid index here!");
1406 hardenLoadAddr(
MI, BaseMO, IndexMO, AddrRegToHardenedReg);
1412 if (HardenPostLoad.
erase(&
MI)) {
1413 assert(!
MI.isCall() &&
"Must not try to post-load harden a call!");
1421 MachineInstr *SunkMI = sinkPostLoadHardenedInst(
MI, HardenPostLoad);
1426 if (SunkMI != &
MI) {
1433 HardenPostLoad.
insert(SunkMI);
1438 unsigned HardenedReg = hardenPostLoad(
MI);
1441 AddrRegToHardenedReg[HardenedReg] = HardenedReg;
1452 hardenIndirectCallOrJumpInstr(
MI, AddrRegToHardenedReg);
1459 if (!
MI.isCall() && !
MI.isReturn())
1464 if (
MI.isReturn() && !
MI.isCall()) {
1465 hardenReturnInstr(
MI);
1472 assert(
MI.isCall() &&
"Should only reach here for calls!");
1473 tracePredStateThroughCall(
MI);
1476 HardenPostLoad.
clear();
1477 HardenLoadAddr.
clear();
1478 HardenedAddrRegs.
clear();
1479 AddrRegToHardenedReg.
clear();
1484 LoadDepRegs.
clear();
1494unsigned X86SpeculativeLoadHardeningPass::saveEFLAGS(
1512void X86SpeculativeLoadHardeningPass::restoreEFLAGS(
1523void X86SpeculativeLoadHardeningPass::mergePredStateIntoSP(
1525 const DebugLoc &Loc,
unsigned PredStateReg) {
1526 Register TmpReg =
MRI->createVirtualRegister(PS->RC);
1530 auto ShiftI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::SHL64ri), TmpReg)
1535 auto OrI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::OR64rr), X86::RSP)
1543unsigned X86SpeculativeLoadHardeningPass::extractPredStateFromSP(
1546 Register PredStateReg =
MRI->createVirtualRegister(PS->RC);
1547 Register TmpReg =
MRI->createVirtualRegister(PS->RC);
1552 BuildMI(
MBB, InsertPt, Loc,
TII->get(TargetOpcode::COPY), TmpReg)
1555 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::SAR64ri), PredStateReg)
1557 .
addImm(
TRI->getRegSizeInBits(*PS->RC) - 1);
1561 return PredStateReg;
1564void X86SpeculativeLoadHardeningPass::hardenLoadAddr(
1576 if (BaseMO.
isFI()) {
1580 dbgs() <<
" Skipping hardening base of explicit stack frame load: ";
1582 }
else if (BaseMO.
getReg() == X86::RSP) {
1587 "Explicit RSP access with dynamic index!");
1589 dbgs() <<
" Cannot harden base of explicit RSP offset in a load!");
1590 }
else if (BaseMO.
getReg() == X86::RIP ||
1591 BaseMO.
getReg() == X86::NoRegister) {
1601 dbgs() <<
" Cannot harden base of "
1602 << (BaseMO.
getReg() == X86::RIP ?
"RIP-relative" :
"no-base")
1603 <<
" address in a load!");
1606 "Only allowed to have a frame index or register base.");
1610 if (IndexMO.
getReg() != X86::NoRegister &&
1611 (HardenOpRegs.
empty() ||
1612 HardenOpRegs.
front()->getReg() != IndexMO.
getReg()))
1616 "Should have exactly one or two registers to harden!");
1618 HardenOpRegs[0]->getReg() != HardenOpRegs[1]->getReg()) &&
1619 "Should not have two of the same registers!");
1624 auto It = AddrRegToHardenedReg.
find(
Op->getReg());
1625 if (It == AddrRegToHardenedReg.
end())
1630 Op->setReg(It->second);
1634 if (HardenOpRegs.
empty())
1638 Register StateReg = PS->SSA.GetValueAtEndOfBlock(&
MBB);
1640 auto InsertPt =
MI.getIterator();
1645 unsigned FlagsReg = 0;
1646 if (EFLAGSLive && !Subtarget->hasBMI2()) {
1648 FlagsReg = saveEFLAGS(
MBB, InsertPt, Loc);
1653 auto *OpRC =
MRI->getRegClass(OpReg);
1654 Register TmpReg =
MRI->createVirtualRegister(OpRC);
1658 if (!Subtarget->hasVLX() && (OpRC->hasSuperClassEq(&X86::VR128RegClass) ||
1659 OpRC->hasSuperClassEq(&X86::VR256RegClass))) {
1660 assert(Subtarget->hasAVX2() &&
"AVX2-specific register classes!");
1661 bool Is128Bit = OpRC->hasSuperClassEq(&X86::VR128RegClass);
1666 Register VStateReg =
MRI->createVirtualRegister(&X86::VR128RegClass);
1668 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::VMOV64toPQIrr), VStateReg)
1675 Register VBStateReg =
MRI->createVirtualRegister(OpRC);
1676 auto BroadcastI =
BuildMI(
MBB, InsertPt, Loc,
1677 TII->get(Is128Bit ? X86::VPBROADCASTQrr
1678 : X86::VPBROADCASTQYrr),
1683 LLVM_DEBUG(
dbgs() <<
" Inserting broadcast: "; BroadcastI->dump();
1689 TII->get(Is128Bit ? X86::VPORrr : X86::VPORYrr), TmpReg)
1695 }
else if (OpRC->hasSuperClassEq(&X86::VR128XRegClass) ||
1696 OpRC->hasSuperClassEq(&X86::VR256XRegClass) ||
1697 OpRC->hasSuperClassEq(&X86::VR512RegClass)) {
1698 assert(Subtarget->hasAVX512() &&
"AVX512-specific register classes!");
1699 bool Is128Bit = OpRC->hasSuperClassEq(&X86::VR128XRegClass);
1700 bool Is256Bit = OpRC->hasSuperClassEq(&X86::VR256XRegClass);
1701 if (Is128Bit || Is256Bit)
1702 assert(Subtarget->hasVLX() &&
"AVX512VL-specific register classes!");
1705 Register VStateReg =
MRI->createVirtualRegister(OpRC);
1706 unsigned BroadcastOp =
Is128Bit ? X86::VPBROADCASTQrZ128rr
1707 : Is256Bit ? X86::VPBROADCASTQrZ256rr
1708 : X86::VPBROADCASTQrZrr;
1710 BuildMI(
MBB, InsertPt, Loc,
TII->get(BroadcastOp), VStateReg)
1714 LLVM_DEBUG(
dbgs() <<
" Inserting broadcast: "; BroadcastI->dump();
1718 unsigned OrOp =
Is128Bit ? X86::VPORQZ128rr
1719 : Is256Bit ? X86::VPORQZ256rr : X86::VPORQZrr;
1720 auto OrI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(OrOp), TmpReg)
1728 assert(OpRC->hasSuperClassEq(&X86::GR64RegClass) &&
1729 "Not a supported register class for address hardening!");
1733 auto OrI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::OR64rr), TmpReg)
1743 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::SHRX64rr), TmpReg)
1755 "Should not have checked this register yet!");
1756 AddrRegToHardenedReg[
Op->getReg()] = TmpReg;
1758 ++NumAddrRegsHardened;
1763 restoreEFLAGS(
MBB, InsertPt, Loc, FlagsReg);
1766MachineInstr *X86SpeculativeLoadHardeningPass::sinkPostLoadHardenedInst(
1769 "Cannot get here with a non-invariant load!");
1771 "Cannot get here with a data invariant load "
1772 "that interferes with EFLAGS!");
1775 auto SinkCheckToSingleUse =
1792 "Data variant instruction being hardened!");
1799 assert(MemRefBeginIdx >= 0 &&
1800 "Should always have mem references here!");
1806 if ((BaseMO.
isReg() && BaseMO.
getReg() == DefReg) ||
1827 if (
UseMI.getDesc().getNumDefs() > 1)
1834 if (!canHardenRegister(UseDefReg))
1837 SingleUseMI = &
UseMI;
1842 return {SingleUseMI};
1846 while (std::optional<MachineInstr *> SingleUse = SinkCheckToSingleUse(*
MI)) {
1856bool X86SpeculativeLoadHardeningPass::canHardenRegister(
Register Reg) {
1858 if (!
Reg.isVirtual())
1861 auto *RC =
MRI->getRegClass(Reg);
1862 int RegBytes =
TRI->getRegSizeInBits(*RC) / 8;
1867 unsigned RegIdx =
Log2_32(RegBytes);
1868 assert(RegIdx < 4 &&
"Unsupported register size");
1878 &X86::GR8_NOREXRegClass, &X86::GR16_NOREXRegClass,
1879 &X86::GR32_NOREXRegClass, &X86::GR64_NOREXRegClass};
1880 if (RC == NOREXRegClasses[RegIdx])
1884 &X86::GR8RegClass, &X86::GR16RegClass, &X86::GR32RegClass,
1885 &X86::GR64RegClass};
1903unsigned X86SpeculativeLoadHardeningPass::hardenValueInRegister(
1906 assert(canHardenRegister(Reg) &&
"Cannot harden this register!");
1908 auto *RC =
MRI->getRegClass(Reg);
1909 int Bytes =
TRI->getRegSizeInBits(*RC) / 8;
1910 Register StateReg = PS->SSA.GetValueAtEndOfBlock(&
MBB);
1911 assert((Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8) &&
1912 "Unknown register size");
1916 unsigned SubRegImms[] = {X86::sub_8bit, X86::sub_16bit, X86::sub_32bit};
1917 unsigned SubRegImm = SubRegImms[
Log2_32(Bytes)];
1918 Register NarrowStateReg =
MRI->createVirtualRegister(RC);
1919 BuildMI(
MBB, InsertPt, Loc,
TII->get(TargetOpcode::COPY), NarrowStateReg)
1920 .
addReg(StateReg, 0, SubRegImm);
1921 StateReg = NarrowStateReg;
1924 unsigned FlagsReg = 0;
1926 FlagsReg = saveEFLAGS(
MBB, InsertPt, Loc);
1929 unsigned OrOpCodes[] = {X86::OR8rr, X86::OR16rr, X86::OR32rr, X86::OR64rr};
1930 unsigned OrOpCode = OrOpCodes[
Log2_32(Bytes)];
1931 auto OrI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(OrOpCode), NewReg)
1939 restoreEFLAGS(
MBB, InsertPt, Loc, FlagsReg);
1953unsigned X86SpeculativeLoadHardeningPass::hardenPostLoad(
MachineInstr &
MI) {
1957 auto &DefOp =
MI.getOperand(0);
1958 Register OldDefReg = DefOp.getReg();
1959 auto *DefRC =
MRI->getRegClass(OldDefReg);
1964 Register UnhardenedReg =
MRI->createVirtualRegister(DefRC);
1965 DefOp.setReg(UnhardenedReg);
1970 unsigned HardenedReg = hardenValueInRegister(
1971 UnhardenedReg,
MBB, std::next(
MI.getIterator()), Loc);
1975 MRI->replaceRegWith( OldDefReg, HardenedReg);
1977 ++NumPostLoadRegsHardened;
2004void X86SpeculativeLoadHardeningPass::hardenReturnInstr(
MachineInstr &
MI) {
2007 auto InsertPt =
MI.getIterator();
2017 mergePredStateIntoSP(
MBB, InsertPt, Loc, PS->SSA.GetValueAtEndOfBlock(&
MBB));
2050void X86SpeculativeLoadHardeningPass::tracePredStateThroughCall(
2054 auto InsertPt =
MI.getIterator();
2067 BuildMI(
MBB, std::next(InsertPt), Loc,
TII->get(X86::LFENCE));
2069 ++NumLFENCEsInserted;
2075 Register StateReg = PS->SSA.GetValueAtEndOfBlock(&
MBB);
2076 mergePredStateIntoSP(
MBB, InsertPt, Loc, StateReg);
2091 MI.setPostInstrSymbol(MF, RetSymbol);
2094 unsigned ExpectedRetAddrReg = 0;
2099 if (!Subtarget->getFrameLowering()->has128ByteRedZone(MF) ||
2119 ExpectedRetAddrReg =
MRI->createVirtualRegister(AddrRC);
2121 !Subtarget->isPositionIndependent()) {
2122 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::MOV64ri32), ExpectedRetAddrReg)
2125 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::LEA64r), ExpectedRetAddrReg)
2141 if (!ExpectedRetAddrReg) {
2142 ExpectedRetAddrReg =
MRI->createVirtualRegister(AddrRC);
2143 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::MOV64rm), ExpectedRetAddrReg)
2153 unsigned NewStateReg = extractPredStateFromSP(
MBB, InsertPt, Loc);
2159 !Subtarget->isPositionIndependent()) {
2166 Register ActualRetAddrReg =
MRI->createVirtualRegister(AddrRC);
2167 BuildMI(
MBB, InsertPt, Loc,
TII->get(X86::LEA64r), ActualRetAddrReg)
2180 int PredStateSizeInBytes =
TRI->getRegSizeInBits(*PS->RC) / 8;
2183 Register UpdatedStateReg =
MRI->createVirtualRegister(PS->RC);
2184 auto CMovI =
BuildMI(
MBB, InsertPt, Loc,
TII->get(CMovOp), UpdatedStateReg)
2192 PS->SSA.AddAvailableValue(&
MBB, UpdatedStateReg);
2210void X86SpeculativeLoadHardeningPass::hardenIndirectCallOrJumpInstr(
2213 switch (
MI.getOpcode()) {
2214 case X86::FARCALL16m:
2215 case X86::FARCALL32m:
2216 case X86::FARCALL64m:
2217 case X86::FARJMP16m:
2218 case X86::FARJMP32m:
2219 case X86::FARJMP64m:
2230 assert(!
MI.mayLoad() &&
"Found a lingering loading instruction!");
2234 if (!
MI.getOperand(0).isReg())
2239 auto &TargetOp =
MI.getOperand(0);
2240 Register OldTargetReg = TargetOp.getReg();
2245 unsigned &HardenedTargetReg = AddrRegToHardenedReg[OldTargetReg];
2254 if (!HardenedTargetReg)
2255 HardenedTargetReg = hardenValueInRegister(
2256 OldTargetReg, *
MI.getParent(),
MI.getIterator(),
MI.getDebugLoc());
2259 TargetOp.setReg(HardenedTargetReg);
2261 ++NumCallsOrJumpsHardened;
2265 "X86 speculative load hardener",
false,
false)
2270 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 isLiveIn(MCPhysReg Reg, LaneBitmask LaneMask=LaneBitmask::getAll()) const
Return true if the specified register is in the live in set.
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 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.
const LLVMTargetMachine & getTarget() const
getTarget - Return the target machine this machine code is compiled with
MachineBasicBlock * CreateMachineBasicBlock(const BasicBlock *BB=nullptr, std::optional< UniqueBBID > BBID=std::nullopt)
CreateMachineBasicBlock - Allocate a new MachineBasicBlock.
void insert(iterator MBBI, MachineBasicBlock *MBB)
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...