xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/X86/X86LoadValueInjectionRetHardening.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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:
X86LoadValueInjectionRetHardeningPass()450946e70aSDimitry Andric   X86LoadValueInjectionRetHardeningPass() : MachineFunctionPass(ID) {}
getPassName() const460946e70aSDimitry 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 
runOnMachineFunction(MachineFunction & MF)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 
createX86LoadValueInjectionRetHardeningPass()1170946e70aSDimitry Andric FunctionPass *llvm::createX86LoadValueInjectionRetHardeningPass() {
1180946e70aSDimitry Andric   return new X86LoadValueInjectionRetHardeningPass();
1190946e70aSDimitry Andric }
120