1*0946e70aSDimitry Andric //===-- X86LoadValueInjectionRetHardening.cpp - LVI RET hardening for x86 --==// 2*0946e70aSDimitry Andric // 3*0946e70aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0946e70aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0946e70aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0946e70aSDimitry Andric // 7*0946e70aSDimitry Andric //===----------------------------------------------------------------------===// 8*0946e70aSDimitry Andric /// 9*0946e70aSDimitry Andric /// Description: Replaces every `ret` instruction with the sequence: 10*0946e70aSDimitry Andric /// ``` 11*0946e70aSDimitry Andric /// pop <scratch-reg> 12*0946e70aSDimitry Andric /// lfence 13*0946e70aSDimitry Andric /// jmp *<scratch-reg> 14*0946e70aSDimitry Andric /// ``` 15*0946e70aSDimitry Andric /// where `<scratch-reg>` is some available scratch register, according to the 16*0946e70aSDimitry Andric /// calling convention of the function being mitigated. 17*0946e70aSDimitry Andric /// 18*0946e70aSDimitry Andric //===----------------------------------------------------------------------===// 19*0946e70aSDimitry Andric 20*0946e70aSDimitry Andric #include "X86.h" 21*0946e70aSDimitry Andric #include "X86InstrBuilder.h" 22*0946e70aSDimitry Andric #include "X86Subtarget.h" 23*0946e70aSDimitry Andric #include "llvm/ADT/Statistic.h" 24*0946e70aSDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h" 25*0946e70aSDimitry Andric #include "llvm/CodeGen/MachineFunction.h" 26*0946e70aSDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h" 27*0946e70aSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 28*0946e70aSDimitry Andric #include "llvm/IR/Function.h" 29*0946e70aSDimitry Andric #include "llvm/Support/Debug.h" 30*0946e70aSDimitry Andric #include <bitset> 31*0946e70aSDimitry Andric 32*0946e70aSDimitry Andric using namespace llvm; 33*0946e70aSDimitry Andric 34*0946e70aSDimitry Andric #define PASS_KEY "x86-lvi-ret" 35*0946e70aSDimitry Andric #define DEBUG_TYPE PASS_KEY 36*0946e70aSDimitry Andric 37*0946e70aSDimitry Andric STATISTIC(NumFences, "Number of LFENCEs inserted for LVI mitigation"); 38*0946e70aSDimitry Andric STATISTIC(NumFunctionsConsidered, "Number of functions analyzed"); 39*0946e70aSDimitry Andric STATISTIC(NumFunctionsMitigated, "Number of functions for which mitigations " 40*0946e70aSDimitry Andric "were deployed"); 41*0946e70aSDimitry Andric 42*0946e70aSDimitry Andric namespace { 43*0946e70aSDimitry Andric 44*0946e70aSDimitry Andric class X86LoadValueInjectionRetHardeningPass : public MachineFunctionPass { 45*0946e70aSDimitry Andric public: 46*0946e70aSDimitry Andric X86LoadValueInjectionRetHardeningPass() : MachineFunctionPass(ID) {} 47*0946e70aSDimitry Andric StringRef getPassName() const override { 48*0946e70aSDimitry Andric return "X86 Load Value Injection (LVI) Ret-Hardening"; 49*0946e70aSDimitry Andric } 50*0946e70aSDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 51*0946e70aSDimitry Andric 52*0946e70aSDimitry Andric static char ID; 53*0946e70aSDimitry Andric }; 54*0946e70aSDimitry Andric 55*0946e70aSDimitry Andric } // end anonymous namespace 56*0946e70aSDimitry Andric 57*0946e70aSDimitry Andric char X86LoadValueInjectionRetHardeningPass::ID = 0; 58*0946e70aSDimitry Andric 59*0946e70aSDimitry Andric bool X86LoadValueInjectionRetHardeningPass::runOnMachineFunction( 60*0946e70aSDimitry Andric MachineFunction &MF) { 61*0946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "***** " << getPassName() << " : " << MF.getName() 62*0946e70aSDimitry Andric << " *****\n"); 63*0946e70aSDimitry Andric const X86Subtarget *Subtarget = &MF.getSubtarget<X86Subtarget>(); 64*0946e70aSDimitry Andric if (!Subtarget->useLVIControlFlowIntegrity() || !Subtarget->is64Bit()) 65*0946e70aSDimitry Andric return false; // FIXME: support 32-bit 66*0946e70aSDimitry Andric 67*0946e70aSDimitry Andric // Don't skip functions with the "optnone" attr but participate in opt-bisect. 68*0946e70aSDimitry Andric const Function &F = MF.getFunction(); 69*0946e70aSDimitry Andric if (!F.hasOptNone() && skipFunction(F)) 70*0946e70aSDimitry Andric return false; 71*0946e70aSDimitry Andric 72*0946e70aSDimitry Andric ++NumFunctionsConsidered; 73*0946e70aSDimitry Andric const X86RegisterInfo *TRI = Subtarget->getRegisterInfo(); 74*0946e70aSDimitry Andric const X86InstrInfo *TII = Subtarget->getInstrInfo(); 75*0946e70aSDimitry Andric unsigned ClobberReg = X86::NoRegister; 76*0946e70aSDimitry Andric std::bitset<X86::NUM_TARGET_REGS> UnclobberableGR64s; 77*0946e70aSDimitry Andric UnclobberableGR64s.set(X86::RSP); // can't clobber stack pointer 78*0946e70aSDimitry Andric UnclobberableGR64s.set(X86::RIP); // can't clobber instruction pointer 79*0946e70aSDimitry Andric UnclobberableGR64s.set(X86::RAX); // used for function return 80*0946e70aSDimitry Andric UnclobberableGR64s.set(X86::RDX); // used for function return 81*0946e70aSDimitry Andric 82*0946e70aSDimitry Andric // We can clobber any register allowed by the function's calling convention. 83*0946e70aSDimitry Andric for (const MCPhysReg *PR = TRI->getCalleeSavedRegs(&MF); auto Reg = *PR; ++PR) 84*0946e70aSDimitry Andric UnclobberableGR64s.set(Reg); 85*0946e70aSDimitry Andric for (auto &Reg : X86::GR64RegClass) { 86*0946e70aSDimitry Andric if (!UnclobberableGR64s.test(Reg)) { 87*0946e70aSDimitry Andric ClobberReg = Reg; 88*0946e70aSDimitry Andric break; 89*0946e70aSDimitry Andric } 90*0946e70aSDimitry Andric } 91*0946e70aSDimitry Andric 92*0946e70aSDimitry Andric if (ClobberReg != X86::NoRegister) { 93*0946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Selected register " 94*0946e70aSDimitry Andric << Subtarget->getRegisterInfo()->getRegAsmName(ClobberReg) 95*0946e70aSDimitry Andric << " to clobber\n"); 96*0946e70aSDimitry Andric } else { 97*0946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "Could not find a register to clobber\n"); 98*0946e70aSDimitry Andric } 99*0946e70aSDimitry Andric 100*0946e70aSDimitry Andric bool Modified = false; 101*0946e70aSDimitry Andric for (auto &MBB : MF) { 102*0946e70aSDimitry Andric if (MBB.empty()) 103*0946e70aSDimitry Andric continue; 104*0946e70aSDimitry Andric 105*0946e70aSDimitry Andric MachineInstr &MI = MBB.back(); 106*0946e70aSDimitry Andric if (MI.getOpcode() != X86::RETQ) 107*0946e70aSDimitry Andric continue; 108*0946e70aSDimitry Andric 109*0946e70aSDimitry Andric if (ClobberReg != X86::NoRegister) { 110*0946e70aSDimitry Andric MBB.erase_instr(&MI); 111*0946e70aSDimitry Andric BuildMI(MBB, MBB.end(), DebugLoc(), TII->get(X86::POP64r)) 112*0946e70aSDimitry Andric .addReg(ClobberReg, RegState::Define) 113*0946e70aSDimitry Andric .setMIFlag(MachineInstr::FrameDestroy); 114*0946e70aSDimitry Andric BuildMI(MBB, MBB.end(), DebugLoc(), TII->get(X86::LFENCE)); 115*0946e70aSDimitry Andric BuildMI(MBB, MBB.end(), DebugLoc(), TII->get(X86::JMP64r)) 116*0946e70aSDimitry Andric .addReg(ClobberReg); 117*0946e70aSDimitry Andric } else { 118*0946e70aSDimitry Andric // In case there is no available scratch register, we can still read from 119*0946e70aSDimitry Andric // RSP to assert that RSP points to a valid page. The write to RSP is 120*0946e70aSDimitry Andric // also helpful because it verifies that the stack's write permissions 121*0946e70aSDimitry Andric // are intact. 122*0946e70aSDimitry Andric MachineInstr *Fence = BuildMI(MBB, MI, DebugLoc(), TII->get(X86::LFENCE)); 123*0946e70aSDimitry Andric addRegOffset(BuildMI(MBB, Fence, DebugLoc(), TII->get(X86::SHL64mi)), 124*0946e70aSDimitry Andric X86::RSP, false, 0) 125*0946e70aSDimitry Andric .addImm(0) 126*0946e70aSDimitry Andric ->addRegisterDead(X86::EFLAGS, TRI); 127*0946e70aSDimitry Andric } 128*0946e70aSDimitry Andric 129*0946e70aSDimitry Andric ++NumFences; 130*0946e70aSDimitry Andric Modified = true; 131*0946e70aSDimitry Andric } 132*0946e70aSDimitry Andric 133*0946e70aSDimitry Andric if (Modified) 134*0946e70aSDimitry Andric ++NumFunctionsMitigated; 135*0946e70aSDimitry Andric return Modified; 136*0946e70aSDimitry Andric } 137*0946e70aSDimitry Andric 138*0946e70aSDimitry Andric INITIALIZE_PASS(X86LoadValueInjectionRetHardeningPass, PASS_KEY, 139*0946e70aSDimitry Andric "X86 LVI ret hardener", false, false) 140*0946e70aSDimitry Andric 141*0946e70aSDimitry Andric FunctionPass *llvm::createX86LoadValueInjectionRetHardeningPass() { 142*0946e70aSDimitry Andric return new X86LoadValueInjectionRetHardeningPass(); 143*0946e70aSDimitry Andric } 144