//===- AArch64SpeculationHardening.cpp - Harden Against Missspeculation --===// // // 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 // //===----------------------------------------------------------------------===// // // This file contains a pass to insert code to mitigate against side channel // vulnerabilities that may happen under control flow miss-speculation. // // The pass implements tracking of control flow miss-speculation into a "taint" // register. That taint register can then be used to mask off registers with // sensitive data when executing under miss-speculation, a.k.a. "transient // execution". // This pass is aimed at mitigating against SpectreV1-style vulnarabilities. // // It also implements speculative load hardening, i.e. using the taint register // to automatically mask off loaded data. // // As a possible follow-on improvement, also an intrinsics-based approach as // explained at https://lwn.net/Articles/759423/ could be implemented on top of // the current design. // // For AArch64, the following implementation choices are made to implement the // tracking of control flow miss-speculation into a taint register: // Some of these are different than the implementation choices made in // the similar pass implemented in X86SpeculativeLoadHardening.cpp, as // the instruction set characteristics result in different trade-offs. // - The speculation hardening is done after register allocation. With a // relative abundance of registers, one register is reserved (X16) to be // the taint register. X16 is expected to not clash with other register // reservation mechanisms with very high probability because: // . The AArch64 ABI doesn't guarantee X16 to be retained across any call. // . The only way to request X16 to be used as a programmer is through // inline assembly. In the rare case a function explicitly demands to // use X16/W16, this pass falls back to hardening against speculation // by inserting a DSB SYS/ISB barrier pair which will prevent control // flow speculation. // - It is easy to insert mask operations at this late stage as we have // mask operations available that don't set flags. // - The taint variable contains all-ones when no miss-speculation is detected, // and contains all-zeros when miss-speculation is detected. Therefore, when // masking, an AND instruction (which only changes the register to be masked, // no other side effects) can easily be inserted anywhere that's needed. // - The tracking of miss-speculation is done by using a data-flow conditional // select instruction (CSEL) to evaluate the flags that were also used to // make conditional branch direction decisions. Speculation of the CSEL // instruction can be limited with a CSDB instruction - so the combination of // CSEL + a later CSDB gives the guarantee that the flags as used in the CSEL // aren't speculated. When conditional branch direction gets miss-speculated, // the semantics of the inserted CSEL instruction is such that the taint // register will contain all zero bits. // One key requirement for this to work is that the conditional branch is // followed by an execution of the CSEL instruction, where the CSEL // instruction needs to use the same flags status as the conditional branch. // This means that the conditional branches must not be implemented as one // of the AArch64 conditional branches that do not use the flags as input // (CB(N)Z and TB(N)Z). This is implemented by ensuring in the instruction // selectors to not produce these instructions when speculation hardening // is enabled. This pass will assert if it does encounter such an instruction. // - On function call boundaries, the miss-speculation state is transferred from // the taint register X16 to be encoded in the SP register as value 0. // // For the aspect of automatically hardening loads, using the taint register, // (a.k.a. speculative load hardening, see // https://llvm.org/docs/SpeculativeLoadHardening.html), the following // implementation choices are made for AArch64: // - Many of the optimizations described at // https://llvm.org/docs/SpeculativeLoadHardening.html to harden fewer // loads haven't been implemented yet - but for some of them there are // FIXMEs in the code. // - loads that load into general purpose (X or W) registers get hardened by // masking the loaded data. For loads that load into other registers, the // address loaded from gets hardened. It is expected that hardening the // loaded data may be more efficient; but masking data in registers other // than X or W is not easy and may result in being slower than just // hardening the X address register loaded from. // - On AArch64, CSDB instructions are inserted between the masking of the // register and its first use, to ensure there's no non-control-flow // speculation that might undermine the hardening mechanism. // // Future extensions/improvements could be: // - Implement this functionality using full speculation barriers, akin to the // x86-slh-lfence option. This may be more useful for the intrinsics-based // approach than for the SLH approach to masking. // Note that this pass already inserts the full speculation barriers if the // function for some niche reason makes use of X16/W16. // - no indirect branch misprediction gets protected/instrumented; but this // could be done for some indirect branches, such as switch jump tables. //===----------------------------------------------------------------------===// #include "AArch64InstrInfo.h" #include "AArch64Subtarget.h" #include "Utils/AArch64BaseInfo.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/RegisterScavenging.h" #include "llvm/IR/DebugLoc.h" #include "llvm/Pass.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Debug.h" #include "llvm/Target/TargetMachine.h" #include <cassert> usingnamespacellvm; #define DEBUG_TYPE … #define AARCH64_SPECULATION_HARDENING_NAME … static cl::opt<bool> HardenLoads("aarch64-slh-loads", cl::Hidden, cl::desc("Sanitize loads from memory."), cl::init(true)); namespace { class AArch64SpeculationHardening : public MachineFunctionPass { … }; } // end anonymous namespace char AArch64SpeculationHardening::ID = …; INITIALIZE_PASS(…) bool AArch64SpeculationHardening::endsWithCondControlFlow( MachineBasicBlock &MBB, MachineBasicBlock *&TBB, MachineBasicBlock *&FBB, AArch64CC::CondCode &CondCode) const { … } void AArch64SpeculationHardening::insertFullSpeculationBarrier( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, DebugLoc DL) const { … } void AArch64SpeculationHardening::insertTrackingCode( MachineBasicBlock &SplitEdgeBB, AArch64CC::CondCode &CondCode, DebugLoc DL) const { … } bool AArch64SpeculationHardening::instrumentControlFlow( MachineBasicBlock &MBB, bool &UsesFullSpeculationBarrier) { … } void AArch64SpeculationHardening::insertSPToRegTaintPropagation( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) const { … } void AArch64SpeculationHardening::insertRegToSPTaintPropagation( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, unsigned TmpReg) const { … } bool AArch64SpeculationHardening::functionUsesHardeningRegister( MachineFunction &MF) const { … } // Make GPR register Reg speculation-safe by putting it through the // SpeculationSafeValue pseudo instruction, if we can't prove that // the value in the register has already been hardened. bool AArch64SpeculationHardening::makeGPRSpeculationSafe( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, MachineInstr &MI, unsigned Reg) { … } bool AArch64SpeculationHardening::slhLoads(MachineBasicBlock &MBB) { … } /// \brief If MBBI references a pseudo instruction that should be expanded /// here, do the expansion and return true. Otherwise return false. bool AArch64SpeculationHardening::expandSpeculationSafeValue( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, bool UsesFullSpeculationBarrier) { … } bool AArch64SpeculationHardening::insertCSDB(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, DebugLoc DL) { … } bool AArch64SpeculationHardening::lowerSpeculationSafeValuePseudos( MachineBasicBlock &MBB, bool UsesFullSpeculationBarrier) { … } bool AArch64SpeculationHardening::runOnMachineFunction(MachineFunction &MF) { … } /// \brief Returns an instance of the pseudo instruction expansion pass. FunctionPass *llvm::createAArch64SpeculationHardeningPass() { … }