X86LoadValueInjectionRetHardening.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. //===-- X86LoadValueInjectionRetHardening.cpp - LVI RET hardening for x86 --==//
  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-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. ///
  9. /// Description: Replaces every `ret` instruction with the sequence:
  10. /// ```
  11. /// pop <scratch-reg>
  12. /// lfence
  13. /// jmp *<scratch-reg>
  14. /// ```
  15. /// where `<scratch-reg>` is some available scratch register, according to the
  16. /// calling convention of the function being mitigated.
  17. ///
  18. //===----------------------------------------------------------------------===//
  19. #include "X86.h"
  20. #include "X86InstrBuilder.h"
  21. #include "X86Subtarget.h"
  22. #include "llvm/ADT/Statistic.h"
  23. #include "llvm/CodeGen/MachineBasicBlock.h"
  24. #include "llvm/CodeGen/MachineFunction.h"
  25. #include "llvm/CodeGen/MachineFunctionPass.h"
  26. #include "llvm/CodeGen/MachineInstrBuilder.h"
  27. #include "llvm/IR/Function.h"
  28. #include "llvm/Support/Debug.h"
  29. #include <bitset>
  30. using namespace llvm;
  31. #define PASS_KEY "x86-lvi-ret"
  32. #define DEBUG_TYPE PASS_KEY
  33. STATISTIC(NumFences, "Number of LFENCEs inserted for LVI mitigation");
  34. STATISTIC(NumFunctionsConsidered, "Number of functions analyzed");
  35. STATISTIC(NumFunctionsMitigated, "Number of functions for which mitigations "
  36. "were deployed");
  37. namespace {
  38. class X86LoadValueInjectionRetHardeningPass : public MachineFunctionPass {
  39. public:
  40. X86LoadValueInjectionRetHardeningPass() : MachineFunctionPass(ID) {}
  41. StringRef getPassName() const override {
  42. return "X86 Load Value Injection (LVI) Ret-Hardening";
  43. }
  44. bool runOnMachineFunction(MachineFunction &MF) override;
  45. static char ID;
  46. };
  47. } // end anonymous namespace
  48. char X86LoadValueInjectionRetHardeningPass::ID = 0;
  49. bool X86LoadValueInjectionRetHardeningPass::runOnMachineFunction(
  50. MachineFunction &MF) {
  51. LLVM_DEBUG(dbgs() << "***** " << getPassName() << " : " << MF.getName()
  52. << " *****\n");
  53. const X86Subtarget *Subtarget = &MF.getSubtarget<X86Subtarget>();
  54. if (!Subtarget->useLVIControlFlowIntegrity() || !Subtarget->is64Bit())
  55. return false; // FIXME: support 32-bit
  56. // Don't skip functions with the "optnone" attr but participate in opt-bisect.
  57. const Function &F = MF.getFunction();
  58. if (!F.hasOptNone() && skipFunction(F))
  59. return false;
  60. ++NumFunctionsConsidered;
  61. const X86RegisterInfo *TRI = Subtarget->getRegisterInfo();
  62. const X86InstrInfo *TII = Subtarget->getInstrInfo();
  63. bool Modified = false;
  64. for (auto &MBB : MF) {
  65. for (auto MBBI = MBB.begin(); MBBI != MBB.end(); ++MBBI) {
  66. if (MBBI->getOpcode() != X86::RET64)
  67. continue;
  68. unsigned ClobberReg = TRI->findDeadCallerSavedReg(MBB, MBBI);
  69. if (ClobberReg != X86::NoRegister) {
  70. BuildMI(MBB, MBBI, DebugLoc(), TII->get(X86::POP64r))
  71. .addReg(ClobberReg, RegState::Define)
  72. .setMIFlag(MachineInstr::FrameDestroy);
  73. BuildMI(MBB, MBBI, DebugLoc(), TII->get(X86::LFENCE));
  74. BuildMI(MBB, MBBI, DebugLoc(), TII->get(X86::JMP64r))
  75. .addReg(ClobberReg);
  76. MBB.erase(MBBI);
  77. } else {
  78. // In case there is no available scratch register, we can still read
  79. // from RSP to assert that RSP points to a valid page. The write to RSP
  80. // is also helpful because it verifies that the stack's write
  81. // permissions are intact.
  82. MachineInstr *Fence =
  83. BuildMI(MBB, MBBI, DebugLoc(), TII->get(X86::LFENCE));
  84. addRegOffset(BuildMI(MBB, Fence, DebugLoc(), TII->get(X86::SHL64mi)),
  85. X86::RSP, false, 0)
  86. .addImm(0)
  87. ->addRegisterDead(X86::EFLAGS, TRI);
  88. }
  89. ++NumFences;
  90. Modified = true;
  91. break;
  92. }
  93. }
  94. if (Modified)
  95. ++NumFunctionsMitigated;
  96. return Modified;
  97. }
  98. INITIALIZE_PASS(X86LoadValueInjectionRetHardeningPass, PASS_KEY,
  99. "X86 LVI ret hardener", false, false)
  100. FunctionPass *llvm::createX86LoadValueInjectionRetHardeningPass() {
  101. return new X86LoadValueInjectionRetHardeningPass();
  102. }