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