1 //===-- X86LoadValueInjectionRetHardening.cpp - LVI RET hardening for x86 --==//
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
7 //===----------------------------------------------------------------------===//
9 /// Description: Replaces every `ret` instruction with the sequence:
13 /// jmp *<scratch-reg>
15 /// where `<scratch-reg>` is some available scratch register, according to the
16 /// calling convention of the function being mitigated.
18 //===----------------------------------------------------------------------===//
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"
33 #define PASS_KEY "x86-lvi-ret"
34 #define DEBUG_TYPE PASS_KEY
36 STATISTIC(NumFences
, "Number of LFENCEs inserted for LVI mitigation");
37 STATISTIC(NumFunctionsConsidered
, "Number of functions analyzed");
38 STATISTIC(NumFunctionsMitigated
, "Number of functions for which mitigations "
43 class X86LoadValueInjectionRetHardeningPass
: public MachineFunctionPass
{
45 X86LoadValueInjectionRetHardeningPass() : MachineFunctionPass(ID
) {}
46 StringRef
getPassName() const override
{
47 return "X86 Load Value Injection (LVI) Ret-Hardening";
49 bool runOnMachineFunction(MachineFunction
&MF
) override
;
54 } // end anonymous namespace
56 char X86LoadValueInjectionRetHardeningPass::ID
= 0;
58 bool X86LoadValueInjectionRetHardeningPass::runOnMachineFunction(
59 MachineFunction
&MF
) {
60 LLVM_DEBUG(dbgs() << "***** " << getPassName() << " : " << MF
.getName()
62 const X86Subtarget
*Subtarget
= &MF
.getSubtarget
<X86Subtarget
>();
63 if (!Subtarget
->useLVIControlFlowIntegrity() || !Subtarget
->is64Bit())
64 return false; // FIXME: support 32-bit
66 // Don't skip functions with the "optnone" attr but participate in opt-bisect.
67 const Function
&F
= MF
.getFunction();
68 if (!F
.hasOptNone() && skipFunction(F
))
71 ++NumFunctionsConsidered
;
72 const X86RegisterInfo
*TRI
= Subtarget
->getRegisterInfo();
73 const X86InstrInfo
*TII
= Subtarget
->getInstrInfo();
75 bool Modified
= false;
76 for (auto &MBB
: MF
) {
77 for (auto MBBI
= MBB
.begin(); MBBI
!= MBB
.end(); ++MBBI
) {
78 if (MBBI
->getOpcode() != X86::RET64
)
81 unsigned ClobberReg
= TRI
->findDeadCallerSavedReg(MBB
, MBBI
);
82 if (ClobberReg
!= X86::NoRegister
) {
83 BuildMI(MBB
, MBBI
, DebugLoc(), TII
->get(X86::POP64r
))
84 .addReg(ClobberReg
, RegState::Define
)
85 .setMIFlag(MachineInstr::FrameDestroy
);
86 BuildMI(MBB
, MBBI
, DebugLoc(), TII
->get(X86::LFENCE
));
87 BuildMI(MBB
, MBBI
, DebugLoc(), TII
->get(X86::JMP64r
))
91 // In case there is no available scratch register, we can still read
92 // from RSP to assert that RSP points to a valid page. The write to RSP
93 // is also helpful because it verifies that the stack's write
94 // permissions are intact.
96 BuildMI(MBB
, MBBI
, DebugLoc(), TII
->get(X86::LFENCE
));
97 addRegOffset(BuildMI(MBB
, Fence
, DebugLoc(), TII
->get(X86::SHL64mi
)),
100 ->addRegisterDead(X86::EFLAGS
, TRI
);
110 ++NumFunctionsMitigated
;
114 INITIALIZE_PASS(X86LoadValueInjectionRetHardeningPass
, PASS_KEY
,
115 "X86 LVI ret hardener", false, false)
117 FunctionPass
*llvm::createX86LoadValueInjectionRetHardeningPass() {
118 return new X86LoadValueInjectionRetHardeningPass();