mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-02-01 05:01:59 +01:00
bb552531fd
`MBB.back()` could segfault if `MBB.empty()`. Fixed by checking for `MBB.empty()` in the loop. Differential Revision: https://reviews.llvm.org/D77584
144 lines
4.9 KiB
C++
144 lines
4.9 KiB
C++
//===-- X86LoadValueInjectionRetHardening.cpp - LVI RET hardening for x86 --==//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// Description: Replaces every `ret` instruction with the sequence:
|
|
/// ```
|
|
/// pop <scratch-reg>
|
|
/// lfence
|
|
/// jmp *<scratch-reg>
|
|
/// ```
|
|
/// where `<scratch-reg>` is some available scratch register, according to the
|
|
/// calling convention of the function being mitigated.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "X86.h"
|
|
#include "X86InstrBuilder.h"
|
|
#include "X86Subtarget.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
#include "llvm/CodeGen/MachineBasicBlock.h"
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
|
#include "llvm/CodeGen/MachineFunctionPass.h"
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include <bitset>
|
|
|
|
using namespace llvm;
|
|
|
|
#define PASS_KEY "x86-lvi-ret"
|
|
#define DEBUG_TYPE PASS_KEY
|
|
|
|
STATISTIC(NumFences, "Number of LFENCEs inserted for LVI mitigation");
|
|
STATISTIC(NumFunctionsConsidered, "Number of functions analyzed");
|
|
STATISTIC(NumFunctionsMitigated, "Number of functions for which mitigations "
|
|
"were deployed");
|
|
|
|
namespace {
|
|
|
|
class X86LoadValueInjectionRetHardeningPass : public MachineFunctionPass {
|
|
public:
|
|
X86LoadValueInjectionRetHardeningPass() : MachineFunctionPass(ID) {}
|
|
StringRef getPassName() const override {
|
|
return "X86 Load Value Injection (LVI) Ret-Hardening";
|
|
}
|
|
bool runOnMachineFunction(MachineFunction &MF) override;
|
|
|
|
static char ID;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
char X86LoadValueInjectionRetHardeningPass::ID = 0;
|
|
|
|
bool X86LoadValueInjectionRetHardeningPass::runOnMachineFunction(
|
|
MachineFunction &MF) {
|
|
LLVM_DEBUG(dbgs() << "***** " << getPassName() << " : " << MF.getName()
|
|
<< " *****\n");
|
|
const X86Subtarget *Subtarget = &MF.getSubtarget<X86Subtarget>();
|
|
if (!Subtarget->useLVIControlFlowIntegrity() || !Subtarget->is64Bit())
|
|
return false; // FIXME: support 32-bit
|
|
|
|
// Don't skip functions with the "optnone" attr but participate in opt-bisect.
|
|
const Function &F = MF.getFunction();
|
|
if (!F.hasOptNone() && skipFunction(F))
|
|
return false;
|
|
|
|
++NumFunctionsConsidered;
|
|
const X86RegisterInfo *TRI = Subtarget->getRegisterInfo();
|
|
const X86InstrInfo *TII = Subtarget->getInstrInfo();
|
|
unsigned ClobberReg = X86::NoRegister;
|
|
std::bitset<X86::NUM_TARGET_REGS> UnclobberableGR64s;
|
|
UnclobberableGR64s.set(X86::RSP); // can't clobber stack pointer
|
|
UnclobberableGR64s.set(X86::RIP); // can't clobber instruction pointer
|
|
UnclobberableGR64s.set(X86::RAX); // used for function return
|
|
UnclobberableGR64s.set(X86::RDX); // used for function return
|
|
|
|
// We can clobber any register allowed by the function's calling convention.
|
|
for (const MCPhysReg *PR = TRI->getCalleeSavedRegs(&MF); auto Reg = *PR; ++PR)
|
|
UnclobberableGR64s.set(Reg);
|
|
for (auto &Reg : X86::GR64RegClass) {
|
|
if (!UnclobberableGR64s.test(Reg)) {
|
|
ClobberReg = Reg;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ClobberReg != X86::NoRegister) {
|
|
LLVM_DEBUG(dbgs() << "Selected register "
|
|
<< Subtarget->getRegisterInfo()->getRegAsmName(ClobberReg)
|
|
<< " to clobber\n");
|
|
} else {
|
|
LLVM_DEBUG(dbgs() << "Could not find a register to clobber\n");
|
|
}
|
|
|
|
bool Modified = false;
|
|
for (auto &MBB : MF) {
|
|
if (MBB.empty())
|
|
continue;
|
|
|
|
MachineInstr &MI = MBB.back();
|
|
if (MI.getOpcode() != X86::RETQ)
|
|
continue;
|
|
|
|
if (ClobberReg != X86::NoRegister) {
|
|
MBB.erase_instr(&MI);
|
|
BuildMI(MBB, MBB.end(), DebugLoc(), TII->get(X86::POP64r))
|
|
.addReg(ClobberReg, RegState::Define)
|
|
.setMIFlag(MachineInstr::FrameDestroy);
|
|
BuildMI(MBB, MBB.end(), DebugLoc(), TII->get(X86::LFENCE));
|
|
BuildMI(MBB, MBB.end(), DebugLoc(), TII->get(X86::JMP64r))
|
|
.addReg(ClobberReg);
|
|
} else {
|
|
// In case there is no available scratch register, we can still read from
|
|
// RSP to assert that RSP points to a valid page. The write to RSP is
|
|
// also helpful because it verifies that the stack's write permissions
|
|
// are intact.
|
|
MachineInstr *Fence = BuildMI(MBB, MI, DebugLoc(), TII->get(X86::LFENCE));
|
|
addRegOffset(BuildMI(MBB, Fence, DebugLoc(), TII->get(X86::SHL64mi)),
|
|
X86::RSP, false, 0)
|
|
.addImm(0)
|
|
->addRegisterDead(X86::EFLAGS, TRI);
|
|
}
|
|
|
|
++NumFences;
|
|
Modified = true;
|
|
}
|
|
|
|
if (Modified)
|
|
++NumFunctionsMitigated;
|
|
return Modified;
|
|
}
|
|
|
|
INITIALIZE_PASS(X86LoadValueInjectionRetHardeningPass, PASS_KEY,
|
|
"X86 LVI ret hardener", false, false)
|
|
|
|
FunctionPass *llvm::createX86LoadValueInjectionRetHardeningPass() {
|
|
return new X86LoadValueInjectionRetHardeningPass();
|
|
}
|