X86IndirectThunks.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. //==- X86IndirectThunks.cpp - Construct indirect call/jump thunks 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. /// \file
  9. ///
  10. /// Pass that injects an MI thunk that is used to lower indirect calls in a way
  11. /// that prevents speculation on some x86 processors and can be used to mitigate
  12. /// security vulnerabilities due to targeted speculative execution and side
  13. /// channels such as CVE-2017-5715.
  14. ///
  15. /// Currently supported thunks include:
  16. /// - Retpoline -- A RET-implemented trampoline that lowers indirect calls
  17. /// - LVI Thunk -- A CALL/JMP-implemented thunk that forces load serialization
  18. /// before making an indirect call/jump
  19. ///
  20. /// Note that the reason that this is implemented as a MachineFunctionPass and
  21. /// not a ModulePass is that ModulePasses at this point in the LLVM X86 pipeline
  22. /// serialize all transformations, which can consume lots of memory.
  23. ///
  24. /// TODO(chandlerc): All of this code could use better comments and
  25. /// documentation.
  26. ///
  27. //===----------------------------------------------------------------------===//
  28. #include "X86.h"
  29. #include "X86InstrBuilder.h"
  30. #include "X86Subtarget.h"
  31. #include "llvm/CodeGen/IndirectThunks.h"
  32. #include "llvm/CodeGen/MachineFunction.h"
  33. #include "llvm/CodeGen/MachineInstrBuilder.h"
  34. #include "llvm/CodeGen/MachineModuleInfo.h"
  35. #include "llvm/CodeGen/Passes.h"
  36. #include "llvm/CodeGen/TargetPassConfig.h"
  37. #include "llvm/IR/IRBuilder.h"
  38. #include "llvm/IR/Instructions.h"
  39. #include "llvm/IR/Module.h"
  40. #include "llvm/Support/CommandLine.h"
  41. #include "llvm/Support/Debug.h"
  42. #include "llvm/Support/raw_ostream.h"
  43. #include "llvm/Target/TargetMachine.h"
  44. using namespace llvm;
  45. #define DEBUG_TYPE "x86-retpoline-thunks"
  46. static const char RetpolineNamePrefix[] = "__llvm_retpoline_";
  47. static const char R11RetpolineName[] = "__llvm_retpoline_r11";
  48. static const char EAXRetpolineName[] = "__llvm_retpoline_eax";
  49. static const char ECXRetpolineName[] = "__llvm_retpoline_ecx";
  50. static const char EDXRetpolineName[] = "__llvm_retpoline_edx";
  51. static const char EDIRetpolineName[] = "__llvm_retpoline_edi";
  52. static const char LVIThunkNamePrefix[] = "__llvm_lvi_thunk_";
  53. static const char R11LVIThunkName[] = "__llvm_lvi_thunk_r11";
  54. namespace {
  55. struct RetpolineThunkInserter : ThunkInserter<RetpolineThunkInserter> {
  56. const char *getThunkPrefix() { return RetpolineNamePrefix; }
  57. bool mayUseThunk(const MachineFunction &MF) {
  58. const auto &STI = MF.getSubtarget<X86Subtarget>();
  59. return (STI.useRetpolineIndirectCalls() ||
  60. STI.useRetpolineIndirectBranches()) &&
  61. !STI.useRetpolineExternalThunk();
  62. }
  63. void insertThunks(MachineModuleInfo &MMI);
  64. void populateThunk(MachineFunction &MF);
  65. };
  66. struct LVIThunkInserter : ThunkInserter<LVIThunkInserter> {
  67. const char *getThunkPrefix() { return LVIThunkNamePrefix; }
  68. bool mayUseThunk(const MachineFunction &MF) {
  69. return MF.getSubtarget<X86Subtarget>().useLVIControlFlowIntegrity();
  70. }
  71. void insertThunks(MachineModuleInfo &MMI) {
  72. createThunkFunction(MMI, R11LVIThunkName);
  73. }
  74. void populateThunk(MachineFunction &MF) {
  75. assert (MF.size() == 1);
  76. MachineBasicBlock *Entry = &MF.front();
  77. Entry->clear();
  78. // This code mitigates LVI by replacing each indirect call/jump with a
  79. // direct call/jump to a thunk that looks like:
  80. // ```
  81. // lfence
  82. // jmpq *%r11
  83. // ```
  84. // This ensures that if the value in register %r11 was loaded from memory,
  85. // then the value in %r11 is (architecturally) correct prior to the jump.
  86. const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
  87. BuildMI(&MF.front(), DebugLoc(), TII->get(X86::LFENCE));
  88. BuildMI(&MF.front(), DebugLoc(), TII->get(X86::JMP64r)).addReg(X86::R11);
  89. MF.front().addLiveIn(X86::R11);
  90. }
  91. };
  92. class X86IndirectThunks : public MachineFunctionPass {
  93. public:
  94. static char ID;
  95. X86IndirectThunks() : MachineFunctionPass(ID) {}
  96. StringRef getPassName() const override { return "X86 Indirect Thunks"; }
  97. bool doInitialization(Module &M) override;
  98. bool runOnMachineFunction(MachineFunction &MF) override;
  99. private:
  100. std::tuple<RetpolineThunkInserter, LVIThunkInserter> TIs;
  101. // FIXME: When LLVM moves to C++17, these can become folds
  102. template <typename... ThunkInserterT>
  103. static void initTIs(Module &M,
  104. std::tuple<ThunkInserterT...> &ThunkInserters) {
  105. (void)std::initializer_list<int>{
  106. (std::get<ThunkInserterT>(ThunkInserters).init(M), 0)...};
  107. }
  108. template <typename... ThunkInserterT>
  109. static bool runTIs(MachineModuleInfo &MMI, MachineFunction &MF,
  110. std::tuple<ThunkInserterT...> &ThunkInserters) {
  111. bool Modified = false;
  112. (void)std::initializer_list<int>{
  113. Modified |= std::get<ThunkInserterT>(ThunkInserters).run(MMI, MF)...};
  114. return Modified;
  115. }
  116. };
  117. } // end anonymous namespace
  118. void RetpolineThunkInserter::insertThunks(MachineModuleInfo &MMI) {
  119. if (MMI.getTarget().getTargetTriple().getArch() == Triple::x86_64)
  120. createThunkFunction(MMI, R11RetpolineName);
  121. else
  122. for (StringRef Name : {EAXRetpolineName, ECXRetpolineName, EDXRetpolineName,
  123. EDIRetpolineName})
  124. createThunkFunction(MMI, Name);
  125. }
  126. void RetpolineThunkInserter::populateThunk(MachineFunction &MF) {
  127. bool Is64Bit = MF.getTarget().getTargetTriple().getArch() == Triple::x86_64;
  128. Register ThunkReg;
  129. if (Is64Bit) {
  130. assert(MF.getName() == "__llvm_retpoline_r11" &&
  131. "Should only have an r11 thunk on 64-bit targets");
  132. // __llvm_retpoline_r11:
  133. // callq .Lr11_call_target
  134. // .Lr11_capture_spec:
  135. // pause
  136. // lfence
  137. // jmp .Lr11_capture_spec
  138. // .align 16
  139. // .Lr11_call_target:
  140. // movq %r11, (%rsp)
  141. // retq
  142. ThunkReg = X86::R11;
  143. } else {
  144. // For 32-bit targets we need to emit a collection of thunks for various
  145. // possible scratch registers as well as a fallback that uses EDI, which is
  146. // normally callee saved.
  147. // __llvm_retpoline_eax:
  148. // calll .Leax_call_target
  149. // .Leax_capture_spec:
  150. // pause
  151. // jmp .Leax_capture_spec
  152. // .align 16
  153. // .Leax_call_target:
  154. // movl %eax, (%esp) # Clobber return addr
  155. // retl
  156. //
  157. // __llvm_retpoline_ecx:
  158. // ... # Same setup
  159. // movl %ecx, (%esp)
  160. // retl
  161. //
  162. // __llvm_retpoline_edx:
  163. // ... # Same setup
  164. // movl %edx, (%esp)
  165. // retl
  166. //
  167. // __llvm_retpoline_edi:
  168. // ... # Same setup
  169. // movl %edi, (%esp)
  170. // retl
  171. if (MF.getName() == EAXRetpolineName)
  172. ThunkReg = X86::EAX;
  173. else if (MF.getName() == ECXRetpolineName)
  174. ThunkReg = X86::ECX;
  175. else if (MF.getName() == EDXRetpolineName)
  176. ThunkReg = X86::EDX;
  177. else if (MF.getName() == EDIRetpolineName)
  178. ThunkReg = X86::EDI;
  179. else
  180. llvm_unreachable("Invalid thunk name on x86-32!");
  181. }
  182. const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
  183. assert (MF.size() == 1);
  184. MachineBasicBlock *Entry = &MF.front();
  185. Entry->clear();
  186. MachineBasicBlock *CaptureSpec =
  187. MF.CreateMachineBasicBlock(Entry->getBasicBlock());
  188. MachineBasicBlock *CallTarget =
  189. MF.CreateMachineBasicBlock(Entry->getBasicBlock());
  190. MCSymbol *TargetSym = MF.getContext().createTempSymbol();
  191. MF.push_back(CaptureSpec);
  192. MF.push_back(CallTarget);
  193. const unsigned CallOpc = Is64Bit ? X86::CALL64pcrel32 : X86::CALLpcrel32;
  194. const unsigned RetOpc = Is64Bit ? X86::RET64 : X86::RET32;
  195. Entry->addLiveIn(ThunkReg);
  196. BuildMI(Entry, DebugLoc(), TII->get(CallOpc)).addSym(TargetSym);
  197. // The MIR verifier thinks that the CALL in the entry block will fall through
  198. // to CaptureSpec, so mark it as the successor. Technically, CaptureTarget is
  199. // the successor, but the MIR verifier doesn't know how to cope with that.
  200. Entry->addSuccessor(CaptureSpec);
  201. // In the capture loop for speculation, we want to stop the processor from
  202. // speculating as fast as possible. On Intel processors, the PAUSE instruction
  203. // will block speculation without consuming any execution resources. On AMD
  204. // processors, the PAUSE instruction is (essentially) a nop, so we also use an
  205. // LFENCE instruction which they have advised will stop speculation as well
  206. // with minimal resource utilization. We still end the capture with a jump to
  207. // form an infinite loop to fully guarantee that no matter what implementation
  208. // of the x86 ISA, speculating this code path never escapes.
  209. BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::PAUSE));
  210. BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::LFENCE));
  211. BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::JMP_1)).addMBB(CaptureSpec);
  212. CaptureSpec->setHasAddressTaken();
  213. CaptureSpec->addSuccessor(CaptureSpec);
  214. CallTarget->addLiveIn(ThunkReg);
  215. CallTarget->setHasAddressTaken();
  216. CallTarget->setAlignment(Align(16));
  217. // Insert return address clobber
  218. const unsigned MovOpc = Is64Bit ? X86::MOV64mr : X86::MOV32mr;
  219. const Register SPReg = Is64Bit ? X86::RSP : X86::ESP;
  220. addRegOffset(BuildMI(CallTarget, DebugLoc(), TII->get(MovOpc)), SPReg, false,
  221. 0)
  222. .addReg(ThunkReg);
  223. CallTarget->back().setPreInstrSymbol(MF, TargetSym);
  224. BuildMI(CallTarget, DebugLoc(), TII->get(RetOpc));
  225. }
  226. FunctionPass *llvm::createX86IndirectThunksPass() {
  227. return new X86IndirectThunks();
  228. }
  229. char X86IndirectThunks::ID = 0;
  230. bool X86IndirectThunks::doInitialization(Module &M) {
  231. initTIs(M, TIs);
  232. return false;
  233. }
  234. bool X86IndirectThunks::runOnMachineFunction(MachineFunction &MF) {
  235. LLVM_DEBUG(dbgs() << getPassName() << '\n');
  236. auto &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
  237. return runTIs(MMI, MF, TIs);
  238. }