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"
34 #define PASS_KEY "x86-lvi-ret"
35 #define DEBUG_TYPE PASS_KEY
37 STATISTIC(NumFences
, "Number of LFENCEs inserted for LVI mitigation");
38 STATISTIC(NumFunctionsConsidered
, "Number of functions analyzed");
39 STATISTIC(NumFunctionsMitigated
, "Number of functions for which mitigations "
44 class X86LoadValueInjectionRetHardeningPass
: public MachineFunctionPass
{
46 X86LoadValueInjectionRetHardeningPass() : MachineFunctionPass(ID
) {}
47 StringRef
getPassName() const override
{
48 return "X86 Load Value Injection (LVI) Ret-Hardening";
50 bool runOnMachineFunction(MachineFunction
&MF
) override
;
55 } // end anonymous namespace
57 char X86LoadValueInjectionRetHardeningPass::ID
= 0;
59 bool X86LoadValueInjectionRetHardeningPass::runOnMachineFunction(
60 MachineFunction
&MF
) {
61 LLVM_DEBUG(dbgs() << "***** " << getPassName() << " : " << MF
.getName()
63 const X86Subtarget
*Subtarget
= &MF
.getSubtarget
<X86Subtarget
>();
64 if (!Subtarget
->useLVIControlFlowIntegrity() || !Subtarget
->is64Bit())
65 return false; // FIXME: support 32-bit
67 // Don't skip functions with the "optnone" attr but participate in opt-bisect.
68 const Function
&F
= MF
.getFunction();
69 if (!F
.hasOptNone() && skipFunction(F
))
72 ++NumFunctionsConsidered
;
73 const X86RegisterInfo
*TRI
= Subtarget
->getRegisterInfo();
74 const X86InstrInfo
*TII
= Subtarget
->getInstrInfo();
76 bool Modified
= false;
77 for (auto &MBB
: MF
) {
78 for (auto MBBI
= MBB
.begin(); MBBI
!= MBB
.end(); ++MBBI
) {
79 if (MBBI
->getOpcode() != X86::RET64
)
82 unsigned ClobberReg
= TRI
->findDeadCallerSavedReg(MBB
, MBBI
);
83 if (ClobberReg
!= X86::NoRegister
) {
84 BuildMI(MBB
, MBBI
, DebugLoc(), TII
->get(X86::POP64r
))
85 .addReg(ClobberReg
, RegState::Define
)
86 .setMIFlag(MachineInstr::FrameDestroy
);
87 BuildMI(MBB
, MBBI
, DebugLoc(), TII
->get(X86::LFENCE
));
88 BuildMI(MBB
, MBBI
, DebugLoc(), TII
->get(X86::JMP64r
))
92 // In case there is no available scratch register, we can still read
93 // from RSP to assert that RSP points to a valid page. The write to RSP
94 // is also helpful because it verifies that the stack's write
95 // permissions are intact.
97 BuildMI(MBB
, MBBI
, DebugLoc(), TII
->get(X86::LFENCE
));
98 addRegOffset(BuildMI(MBB
, Fence
, DebugLoc(), TII
->get(X86::SHL64mi
)),
101 ->addRegisterDead(X86::EFLAGS
, TRI
);
111 ++NumFunctionsMitigated
;
115 INITIALIZE_PASS(X86LoadValueInjectionRetHardeningPass
, PASS_KEY
,
116 "X86 LVI ret hardener", false, false)
118 FunctionPass
*llvm::createX86LoadValueInjectionRetHardeningPass() {
119 return new X86LoadValueInjectionRetHardeningPass();