xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/X86/X86LoadValueInjectionRetHardening.cpp (revision e8d8bef961a50d4dc22501cde4fb9fb0be1b2532)
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