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 310946e70aSDimitry Andric using namespace llvm; 320946e70aSDimitry Andric 330946e70aSDimitry Andric #define PASS_KEY "x86-lvi-ret" 340946e70aSDimitry Andric #define DEBUG_TYPE PASS_KEY 350946e70aSDimitry Andric 360946e70aSDimitry Andric STATISTIC(NumFences, "Number of LFENCEs inserted for LVI mitigation"); 370946e70aSDimitry Andric STATISTIC(NumFunctionsConsidered, "Number of functions analyzed"); 380946e70aSDimitry Andric STATISTIC(NumFunctionsMitigated, "Number of functions for which mitigations " 390946e70aSDimitry Andric "were deployed"); 400946e70aSDimitry Andric 410946e70aSDimitry Andric namespace { 420946e70aSDimitry Andric 430946e70aSDimitry Andric class X86LoadValueInjectionRetHardeningPass : public MachineFunctionPass { 440946e70aSDimitry Andric public: 450946e70aSDimitry Andric X86LoadValueInjectionRetHardeningPass() : MachineFunctionPass(ID) {} 460946e70aSDimitry Andric StringRef getPassName() const override { 470946e70aSDimitry Andric return "X86 Load Value Injection (LVI) Ret-Hardening"; 480946e70aSDimitry Andric } 490946e70aSDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 500946e70aSDimitry Andric 510946e70aSDimitry Andric static char ID; 520946e70aSDimitry Andric }; 530946e70aSDimitry Andric 540946e70aSDimitry Andric } // end anonymous namespace 550946e70aSDimitry Andric 560946e70aSDimitry Andric char X86LoadValueInjectionRetHardeningPass::ID = 0; 570946e70aSDimitry Andric 580946e70aSDimitry Andric bool X86LoadValueInjectionRetHardeningPass::runOnMachineFunction( 590946e70aSDimitry Andric MachineFunction &MF) { 600946e70aSDimitry Andric LLVM_DEBUG(dbgs() << "***** " << getPassName() << " : " << MF.getName() 610946e70aSDimitry Andric << " *****\n"); 620946e70aSDimitry Andric const X86Subtarget *Subtarget = &MF.getSubtarget<X86Subtarget>(); 630946e70aSDimitry Andric if (!Subtarget->useLVIControlFlowIntegrity() || !Subtarget->is64Bit()) 640946e70aSDimitry Andric return false; // FIXME: support 32-bit 650946e70aSDimitry Andric 660946e70aSDimitry Andric // Don't skip functions with the "optnone" attr but participate in opt-bisect. 670946e70aSDimitry Andric const Function &F = MF.getFunction(); 680946e70aSDimitry Andric if (!F.hasOptNone() && skipFunction(F)) 690946e70aSDimitry Andric return false; 700946e70aSDimitry Andric 710946e70aSDimitry Andric ++NumFunctionsConsidered; 720946e70aSDimitry Andric const X86RegisterInfo *TRI = Subtarget->getRegisterInfo(); 730946e70aSDimitry Andric const X86InstrInfo *TII = Subtarget->getInstrInfo(); 740946e70aSDimitry Andric 750946e70aSDimitry Andric bool Modified = false; 760946e70aSDimitry Andric for (auto &MBB : MF) { 77e8d8bef9SDimitry Andric for (auto MBBI = MBB.begin(); MBBI != MBB.end(); ++MBBI) { 78*349cc55cSDimitry Andric if (MBBI->getOpcode() != X86::RET64) 790946e70aSDimitry Andric continue; 800946e70aSDimitry Andric 81e8d8bef9SDimitry Andric unsigned ClobberReg = TRI->findDeadCallerSavedReg(MBB, MBBI); 820946e70aSDimitry Andric if (ClobberReg != X86::NoRegister) { 83e8d8bef9SDimitry Andric BuildMI(MBB, MBBI, DebugLoc(), TII->get(X86::POP64r)) 840946e70aSDimitry Andric .addReg(ClobberReg, RegState::Define) 850946e70aSDimitry Andric .setMIFlag(MachineInstr::FrameDestroy); 86e8d8bef9SDimitry Andric BuildMI(MBB, MBBI, DebugLoc(), TII->get(X86::LFENCE)); 87e8d8bef9SDimitry Andric BuildMI(MBB, MBBI, DebugLoc(), TII->get(X86::JMP64r)) 880946e70aSDimitry Andric .addReg(ClobberReg); 89e8d8bef9SDimitry Andric MBB.erase(MBBI); 900946e70aSDimitry Andric } else { 91e8d8bef9SDimitry Andric // In case there is no available scratch register, we can still read 92e8d8bef9SDimitry Andric // from RSP to assert that RSP points to a valid page. The write to RSP 93e8d8bef9SDimitry Andric // is also helpful because it verifies that the stack's write 94e8d8bef9SDimitry Andric // permissions are intact. 95e8d8bef9SDimitry Andric MachineInstr *Fence = 96e8d8bef9SDimitry Andric BuildMI(MBB, MBBI, DebugLoc(), TII->get(X86::LFENCE)); 970946e70aSDimitry Andric addRegOffset(BuildMI(MBB, Fence, DebugLoc(), TII->get(X86::SHL64mi)), 980946e70aSDimitry Andric X86::RSP, false, 0) 990946e70aSDimitry Andric .addImm(0) 1000946e70aSDimitry Andric ->addRegisterDead(X86::EFLAGS, TRI); 1010946e70aSDimitry Andric } 1020946e70aSDimitry Andric 1030946e70aSDimitry Andric ++NumFences; 1040946e70aSDimitry Andric Modified = true; 105e8d8bef9SDimitry Andric break; 106e8d8bef9SDimitry Andric } 1070946e70aSDimitry Andric } 1080946e70aSDimitry Andric 1090946e70aSDimitry Andric if (Modified) 1100946e70aSDimitry Andric ++NumFunctionsMitigated; 1110946e70aSDimitry Andric return Modified; 1120946e70aSDimitry Andric } 1130946e70aSDimitry Andric 1140946e70aSDimitry Andric INITIALIZE_PASS(X86LoadValueInjectionRetHardeningPass, PASS_KEY, 1150946e70aSDimitry Andric "X86 LVI ret hardener", false, false) 1160946e70aSDimitry Andric 1170946e70aSDimitry Andric FunctionPass *llvm::createX86LoadValueInjectionRetHardeningPass() { 1180946e70aSDimitry Andric return new X86LoadValueInjectionRetHardeningPass(); 1190946e70aSDimitry Andric } 120