10946e70aSDimitry Andric //===-- X86LoadValueInjectionRetHardening.cpp - LVI RET hardening for x86 --==// 20946e70aSDimitry Andric // 30946e70aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40946e70aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50946e70aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60946e70aSDimitry Andric // 70946e70aSDimitry Andric //===----------------------------------------------------------------------===// 80946e70aSDimitry Andric /// 90946e70aSDimitry Andric /// Description: Replaces every `ret` instruction with the sequence: 100946e70aSDimitry Andric /// ``` 110946e70aSDimitry Andric /// pop <scratch-reg> 120946e70aSDimitry Andric /// lfence 130946e70aSDimitry Andric /// jmp *<scratch-reg> 140946e70aSDimitry Andric /// ``` 150946e70aSDimitry Andric /// where `<scratch-reg>` is some available scratch register, according to the 160946e70aSDimitry Andric /// calling convention of the function being mitigated. 170946e70aSDimitry Andric /// 180946e70aSDimitry Andric //===----------------------------------------------------------------------===// 190946e70aSDimitry Andric 200946e70aSDimitry Andric #include "X86.h" 210946e70aSDimitry Andric #include "X86InstrBuilder.h" 220946e70aSDimitry Andric #include "X86Subtarget.h" 230946e70aSDimitry Andric #include "llvm/ADT/Statistic.h" 240946e70aSDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h" 250946e70aSDimitry Andric #include "llvm/CodeGen/MachineFunction.h" 260946e70aSDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h" 270946e70aSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 280946e70aSDimitry Andric #include "llvm/IR/Function.h" 290946e70aSDimitry Andric #include "llvm/Support/Debug.h" 300946e70aSDimitry Andric #include <bitset> 310946e70aSDimitry Andric 320946e70aSDimitry Andric using namespace llvm; 330946e70aSDimitry Andric 340946e70aSDimitry Andric #define PASS_KEY "x86-lvi-ret" 350946e70aSDimitry Andric #define DEBUG_TYPE PASS_KEY 360946e70aSDimitry Andric 370946e70aSDimitry Andric STATISTIC(NumFences, "Number of LFENCEs inserted for LVI mitigation"); 380946e70aSDimitry Andric STATISTIC(NumFunctionsConsidered, "Number of functions analyzed"); 390946e70aSDimitry Andric STATISTIC(NumFunctionsMitigated, "Number of functions for which mitigations " 400946e70aSDimitry Andric "were deployed"); 410946e70aSDimitry Andric 420946e70aSDimitry Andric namespace { 430946e70aSDimitry Andric 440946e70aSDimitry Andric class X86LoadValueInjectionRetHardeningPass : public MachineFunctionPass { 450946e70aSDimitry Andric public: 460946e70aSDimitry Andric X86LoadValueInjectionRetHardeningPass() : MachineFunctionPass(ID) {} 470946e70aSDimitry Andric StringRef getPassName() const override { 480946e70aSDimitry Andric return "X86 Load Value Injection (LVI) Ret-Hardening"; 490946e70aSDimitry Andric } 500946e70aSDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 510946e70aSDimitry Andric 520946e70aSDimitry Andric static char ID; 530946e70aSDimitry Andric }; 540946e70aSDimitry Andric 550946e70aSDimitry Andric } // end anonymous namespace 560946e70aSDimitry Andric 570946e70aSDimitry Andric char X86LoadValueInjectionRetHardeningPass::ID = 0; 580946e70aSDimitry Andric 590946e70aSDimitry Andric bool X86LoadValueInjectionRetHardeningPass::runOnMachineFunction( 600946e70aSDimitry Andric MachineFunction &MF) { 610946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "***** " << getPassName() << " : " << MF.getName() 620946e70aSDimitry Andric << " *****\n"); 630946e70aSDimitry Andric const X86Subtarget *Subtarget = &MF.getSubtarget<X86Subtarget>(); 640946e70aSDimitry Andric if (!Subtarget->useLVIControlFlowIntegrity() || !Subtarget->is64Bit()) 650946e70aSDimitry Andric return false; // FIXME: support 32-bit 660946e70aSDimitry Andric 670946e70aSDimitry Andric // Don't skip functions with the "optnone" attr but participate in opt-bisect. 680946e70aSDimitry Andric const Function &F = MF.getFunction(); 690946e70aSDimitry Andric if (!F.hasOptNone() && skipFunction(F)) 700946e70aSDimitry Andric return false; 710946e70aSDimitry Andric 720946e70aSDimitry Andric ++NumFunctionsConsidered; 730946e70aSDimitry Andric const X86RegisterInfo *TRI = Subtarget->getRegisterInfo(); 740946e70aSDimitry Andric const X86InstrInfo *TII = Subtarget->getInstrInfo(); 750946e70aSDimitry Andric 760946e70aSDimitry Andric bool Modified = false; 770946e70aSDimitry Andric for (auto &MBB : MF) { 78*e8d8bef9SDimitry Andric for (auto MBBI = MBB.begin(); MBBI != MBB.end(); ++MBBI) { 79*e8d8bef9SDimitry Andric if (MBBI->getOpcode() != X86::RETQ) 800946e70aSDimitry Andric continue; 810946e70aSDimitry Andric 82*e8d8bef9SDimitry Andric unsigned ClobberReg = TRI->findDeadCallerSavedReg(MBB, MBBI); 830946e70aSDimitry Andric if (ClobberReg != X86::NoRegister) { 84*e8d8bef9SDimitry Andric BuildMI(MBB, MBBI, DebugLoc(), TII->get(X86::POP64r)) 850946e70aSDimitry Andric .addReg(ClobberReg, RegState::Define) 860946e70aSDimitry Andric .setMIFlag(MachineInstr::FrameDestroy); 87*e8d8bef9SDimitry Andric BuildMI(MBB, MBBI, DebugLoc(), TII->get(X86::LFENCE)); 88*e8d8bef9SDimitry Andric BuildMI(MBB, MBBI, DebugLoc(), TII->get(X86::JMP64r)) 890946e70aSDimitry Andric .addReg(ClobberReg); 90*e8d8bef9SDimitry Andric MBB.erase(MBBI); 910946e70aSDimitry Andric } else { 92*e8d8bef9SDimitry Andric // In case there is no available scratch register, we can still read 93*e8d8bef9SDimitry Andric // from RSP to assert that RSP points to a valid page. The write to RSP 94*e8d8bef9SDimitry Andric // is also helpful because it verifies that the stack's write 95*e8d8bef9SDimitry Andric // permissions are intact. 96*e8d8bef9SDimitry Andric MachineInstr *Fence = 97*e8d8bef9SDimitry Andric BuildMI(MBB, MBBI, DebugLoc(), TII->get(X86::LFENCE)); 980946e70aSDimitry Andric addRegOffset(BuildMI(MBB, Fence, DebugLoc(), TII->get(X86::SHL64mi)), 990946e70aSDimitry Andric X86::RSP, false, 0) 1000946e70aSDimitry Andric .addImm(0) 1010946e70aSDimitry Andric ->addRegisterDead(X86::EFLAGS, TRI); 1020946e70aSDimitry Andric } 1030946e70aSDimitry Andric 1040946e70aSDimitry Andric ++NumFences; 1050946e70aSDimitry Andric Modified = true; 106*e8d8bef9SDimitry Andric break; 107*e8d8bef9SDimitry Andric } 1080946e70aSDimitry Andric } 1090946e70aSDimitry Andric 1100946e70aSDimitry Andric if (Modified) 1110946e70aSDimitry Andric ++NumFunctionsMitigated; 1120946e70aSDimitry Andric return Modified; 1130946e70aSDimitry Andric } 1140946e70aSDimitry Andric 1150946e70aSDimitry Andric INITIALIZE_PASS(X86LoadValueInjectionRetHardeningPass, PASS_KEY, 1160946e70aSDimitry Andric "X86 LVI ret hardener", false, false) 1170946e70aSDimitry Andric 1180946e70aSDimitry Andric FunctionPass *llvm::createX86LoadValueInjectionRetHardeningPass() { 1190946e70aSDimitry Andric return new X86LoadValueInjectionRetHardeningPass(); 1200946e70aSDimitry Andric } 121