183 lines · cpp
1//===-- X86SpeculativeExecutionSideEffectSuppression.cpp ------------------===//2//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-exception6//7//===----------------------------------------------------------------------===//8/// \file9///10/// This file contains the X86 implementation of the speculative execution side11/// effect suppression mitigation.12///13/// This must be used with the -mlvi-cfi flag in order to mitigate indirect14/// branches and returns.15//===----------------------------------------------------------------------===//16 17#include "X86.h"18#include "X86InstrInfo.h"19#include "X86Subtarget.h"20#include "llvm/ADT/Statistic.h"21#include "llvm/CodeGen/MachineFunction.h"22#include "llvm/CodeGen/MachineFunctionPass.h"23#include "llvm/CodeGen/MachineInstrBuilder.h"24#include "llvm/Pass.h"25#include "llvm/Target/TargetMachine.h"26using namespace llvm;27 28#define DEBUG_TYPE "x86-seses"29 30STATISTIC(NumLFENCEsInserted, "Number of lfence instructions inserted");31 32static cl::opt<bool> EnableSpeculativeExecutionSideEffectSuppression(33 "x86-seses-enable-without-lvi-cfi",34 cl::desc("Force enable speculative execution side effect suppression. "35 "(Note: User must pass -mlvi-cfi in order to mitigate indirect "36 "branches and returns.)"),37 cl::init(false), cl::Hidden);38 39static cl::opt<bool> OneLFENCEPerBasicBlock(40 "x86-seses-one-lfence-per-bb",41 cl::desc(42 "Omit all lfences other than the first to be placed in a basic block."),43 cl::init(false), cl::Hidden);44 45static cl::opt<bool> OnlyLFENCENonConst(46 "x86-seses-only-lfence-non-const",47 cl::desc("Only lfence before groups of terminators where at least one "48 "branch instruction has an input to the addressing mode that is a "49 "register other than %rip."),50 cl::init(false), cl::Hidden);51 52static cl::opt<bool>53 OmitBranchLFENCEs("x86-seses-omit-branch-lfences",54 cl::desc("Omit all lfences before branch instructions."),55 cl::init(false), cl::Hidden);56 57namespace {58 59class X86SpeculativeExecutionSideEffectSuppression60 : public MachineFunctionPass {61public:62 X86SpeculativeExecutionSideEffectSuppression() : MachineFunctionPass(ID) {}63 64 static char ID;65 StringRef getPassName() const override {66 return "X86 Speculative Execution Side Effect Suppression";67 }68 69 bool runOnMachineFunction(MachineFunction &MF) override;70};71} // namespace72 73char X86SpeculativeExecutionSideEffectSuppression::ID = 0;74 75// This function returns whether the passed instruction uses a memory addressing76// mode that is constant. We treat all memory addressing modes that read77// from a register that is not %rip as non-constant. Note that the use78// of the EFLAGS register results in an addressing mode being considered79// non-constant, therefore all JCC instructions will return false from this80// function since one of their operands will always be the EFLAGS register.81static bool hasConstantAddressingMode(const MachineInstr &MI) {82 for (const MachineOperand &MO : MI.uses())83 if (MO.isReg() && X86::RIP != MO.getReg())84 return false;85 return true;86}87 88bool X86SpeculativeExecutionSideEffectSuppression::runOnMachineFunction(89 MachineFunction &MF) {90 91 const auto &OptLevel = MF.getTarget().getOptLevel();92 const X86Subtarget &Subtarget = MF.getSubtarget<X86Subtarget>();93 94 // Check whether SESES needs to run as the fallback for LVI at O0, whether the95 // user explicitly passed an SESES flag, or whether the SESES target feature96 // was set.97 if (!EnableSpeculativeExecutionSideEffectSuppression &&98 !(Subtarget.useLVILoadHardening() && OptLevel == CodeGenOptLevel::None) &&99 !Subtarget.useSpeculativeExecutionSideEffectSuppression())100 return false;101 102 LLVM_DEBUG(dbgs() << "********** " << getPassName() << " : " << MF.getName()103 << " **********\n");104 bool Modified = false;105 const X86InstrInfo *TII = Subtarget.getInstrInfo();106 for (MachineBasicBlock &MBB : MF) {107 MachineInstr *FirstTerminator = nullptr;108 // Keep track of whether the previous instruction was an LFENCE to avoid109 // adding redundant LFENCEs.110 bool PrevInstIsLFENCE = false;111 for (auto &MI : MBB) {112 113 if (MI.getOpcode() == X86::LFENCE) {114 PrevInstIsLFENCE = true;115 continue;116 }117 // We want to put an LFENCE before any instruction that118 // may load or store. This LFENCE is intended to avoid leaking any secret119 // data due to a given load or store. This results in closing the cache120 // and memory timing side channels. We will treat terminators that load121 // or store separately.122 if (MI.mayLoadOrStore() && !MI.isTerminator()) {123 if (!PrevInstIsLFENCE) {124 BuildMI(MBB, MI, DebugLoc(), TII->get(X86::LFENCE));125 NumLFENCEsInserted++;126 Modified = true;127 }128 if (OneLFENCEPerBasicBlock)129 break;130 }131 // The following section will be LFENCEing before groups of terminators132 // that include branches. This will close the branch prediction side133 // channels since we will prevent code executing after misspeculation as134 // a result of the LFENCEs placed with this logic.135 136 // Keep track of the first terminator in a basic block since if we need137 // to LFENCE the terminators in this basic block we must add the138 // instruction before the first terminator in the basic block (as139 // opposed to before the terminator that indicates an LFENCE is140 // required). An example of why this is necessary is that the141 // X86InstrInfo::analyzeBranch method assumes all terminators are grouped142 // together and terminates it's analysis once the first non-termintor143 // instruction is found.144 if (MI.isTerminator() && FirstTerminator == nullptr)145 FirstTerminator = &MI;146 147 // Look for branch instructions that will require an LFENCE to be put148 // before this basic block's terminators.149 if (!MI.isBranch() || OmitBranchLFENCEs) {150 // This isn't a branch or we're not putting LFENCEs before branches.151 PrevInstIsLFENCE = false;152 continue;153 }154 155 if (OnlyLFENCENonConst && hasConstantAddressingMode(MI)) {156 // This is a branch, but it only has constant addressing mode and we're157 // not adding LFENCEs before such branches.158 PrevInstIsLFENCE = false;159 continue;160 }161 162 // This branch requires adding an LFENCE.163 if (!PrevInstIsLFENCE) {164 assert(FirstTerminator && "Unknown terminator instruction");165 BuildMI(MBB, FirstTerminator, DebugLoc(), TII->get(X86::LFENCE));166 NumLFENCEsInserted++;167 Modified = true;168 }169 break;170 }171 }172 173 return Modified;174}175 176FunctionPass *llvm::createX86SpeculativeExecutionSideEffectSuppression() {177 return new X86SpeculativeExecutionSideEffectSuppression();178}179 180INITIALIZE_PASS(X86SpeculativeExecutionSideEffectSuppression, "x86-seses",181 "X86 Speculative Execution Side Effect Suppression", false,182 false)183