ARMSLSHardening.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. //===- ARMSLSHardening.cpp - Harden Straight Line Missspeculation ---------===//
  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. // This file contains a pass to insert code to mitigate against side channel
  10. // vulnerabilities that may happen under straight line miss-speculation.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "ARM.h"
  14. #include "ARMInstrInfo.h"
  15. #include "ARMSubtarget.h"
  16. #include "llvm/CodeGen/IndirectThunks.h"
  17. #include "llvm/CodeGen/MachineBasicBlock.h"
  18. #include "llvm/CodeGen/MachineFunction.h"
  19. #include "llvm/CodeGen/MachineFunctionPass.h"
  20. #include "llvm/CodeGen/MachineInstr.h"
  21. #include "llvm/CodeGen/MachineInstrBuilder.h"
  22. #include "llvm/CodeGen/MachineOperand.h"
  23. #include "llvm/IR/DebugLoc.h"
  24. #include <cassert>
  25. using namespace llvm;
  26. #define DEBUG_TYPE "arm-sls-hardening"
  27. #define ARM_SLS_HARDENING_NAME "ARM sls hardening pass"
  28. namespace {
  29. class ARMSLSHardening : public MachineFunctionPass {
  30. public:
  31. const TargetInstrInfo *TII;
  32. const ARMSubtarget *ST;
  33. static char ID;
  34. ARMSLSHardening() : MachineFunctionPass(ID) {
  35. initializeARMSLSHardeningPass(*PassRegistry::getPassRegistry());
  36. }
  37. bool runOnMachineFunction(MachineFunction &Fn) override;
  38. StringRef getPassName() const override { return ARM_SLS_HARDENING_NAME; }
  39. void getAnalysisUsage(AnalysisUsage &AU) const override {
  40. AU.setPreservesCFG();
  41. MachineFunctionPass::getAnalysisUsage(AU);
  42. }
  43. private:
  44. bool hardenReturnsAndBRs(MachineBasicBlock &MBB) const;
  45. bool hardenIndirectCalls(MachineBasicBlock &MBB) const;
  46. MachineBasicBlock &
  47. ConvertIndirectCallToIndirectJump(MachineBasicBlock &MBB,
  48. MachineBasicBlock::iterator) const;
  49. };
  50. } // end anonymous namespace
  51. char ARMSLSHardening::ID = 0;
  52. INITIALIZE_PASS(ARMSLSHardening, "arm-sls-hardening",
  53. ARM_SLS_HARDENING_NAME, false, false)
  54. static void insertSpeculationBarrier(const ARMSubtarget *ST,
  55. MachineBasicBlock &MBB,
  56. MachineBasicBlock::iterator MBBI,
  57. DebugLoc DL,
  58. bool AlwaysUseISBDSB = false) {
  59. assert(MBBI != MBB.begin() &&
  60. "Must not insert SpeculationBarrierEndBB as only instruction in MBB.");
  61. assert(std::prev(MBBI)->isBarrier() &&
  62. "SpeculationBarrierEndBB must only follow unconditional control flow "
  63. "instructions.");
  64. assert(std::prev(MBBI)->isTerminator() &&
  65. "SpeculationBarrierEndBB must only follow terminators.");
  66. const TargetInstrInfo *TII = ST->getInstrInfo();
  67. assert(ST->hasDataBarrier() || ST->hasSB());
  68. bool ProduceSB = ST->hasSB() && !AlwaysUseISBDSB;
  69. unsigned BarrierOpc =
  70. ProduceSB ? (ST->isThumb() ? ARM::t2SpeculationBarrierSBEndBB
  71. : ARM::SpeculationBarrierSBEndBB)
  72. : (ST->isThumb() ? ARM::t2SpeculationBarrierISBDSBEndBB
  73. : ARM::SpeculationBarrierISBDSBEndBB);
  74. if (MBBI == MBB.end() || !isSpeculationBarrierEndBBOpcode(MBBI->getOpcode()))
  75. BuildMI(MBB, MBBI, DL, TII->get(BarrierOpc));
  76. }
  77. bool ARMSLSHardening::runOnMachineFunction(MachineFunction &MF) {
  78. ST = &MF.getSubtarget<ARMSubtarget>();
  79. TII = MF.getSubtarget().getInstrInfo();
  80. bool Modified = false;
  81. for (auto &MBB : MF) {
  82. Modified |= hardenReturnsAndBRs(MBB);
  83. Modified |= hardenIndirectCalls(MBB);
  84. }
  85. return Modified;
  86. }
  87. bool ARMSLSHardening::hardenReturnsAndBRs(MachineBasicBlock &MBB) const {
  88. if (!ST->hardenSlsRetBr())
  89. return false;
  90. assert(!ST->isThumb1Only());
  91. bool Modified = false;
  92. MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(), E = MBB.end();
  93. MachineBasicBlock::iterator NextMBBI;
  94. for (; MBBI != E; MBBI = NextMBBI) {
  95. MachineInstr &MI = *MBBI;
  96. NextMBBI = std::next(MBBI);
  97. if (isIndirectControlFlowNotComingBack(MI)) {
  98. assert(MI.isTerminator());
  99. assert(!TII->isPredicated(MI));
  100. insertSpeculationBarrier(ST, MBB, std::next(MBBI), MI.getDebugLoc());
  101. Modified = true;
  102. }
  103. }
  104. return Modified;
  105. }
  106. static const char SLSBLRNamePrefix[] = "__llvm_slsblr_thunk_";
  107. static const struct ThunkNameRegMode {
  108. const char* Name;
  109. Register Reg;
  110. bool isThumb;
  111. } SLSBLRThunks[] = {
  112. {"__llvm_slsblr_thunk_arm_r0", ARM::R0, false},
  113. {"__llvm_slsblr_thunk_arm_r1", ARM::R1, false},
  114. {"__llvm_slsblr_thunk_arm_r2", ARM::R2, false},
  115. {"__llvm_slsblr_thunk_arm_r3", ARM::R3, false},
  116. {"__llvm_slsblr_thunk_arm_r4", ARM::R4, false},
  117. {"__llvm_slsblr_thunk_arm_r5", ARM::R5, false},
  118. {"__llvm_slsblr_thunk_arm_r6", ARM::R6, false},
  119. {"__llvm_slsblr_thunk_arm_r7", ARM::R7, false},
  120. {"__llvm_slsblr_thunk_arm_r8", ARM::R8, false},
  121. {"__llvm_slsblr_thunk_arm_r9", ARM::R9, false},
  122. {"__llvm_slsblr_thunk_arm_r10", ARM::R10, false},
  123. {"__llvm_slsblr_thunk_arm_r11", ARM::R11, false},
  124. {"__llvm_slsblr_thunk_arm_sp", ARM::SP, false},
  125. {"__llvm_slsblr_thunk_arm_pc", ARM::PC, false},
  126. {"__llvm_slsblr_thunk_thumb_r0", ARM::R0, true},
  127. {"__llvm_slsblr_thunk_thumb_r1", ARM::R1, true},
  128. {"__llvm_slsblr_thunk_thumb_r2", ARM::R2, true},
  129. {"__llvm_slsblr_thunk_thumb_r3", ARM::R3, true},
  130. {"__llvm_slsblr_thunk_thumb_r4", ARM::R4, true},
  131. {"__llvm_slsblr_thunk_thumb_r5", ARM::R5, true},
  132. {"__llvm_slsblr_thunk_thumb_r6", ARM::R6, true},
  133. {"__llvm_slsblr_thunk_thumb_r7", ARM::R7, true},
  134. {"__llvm_slsblr_thunk_thumb_r8", ARM::R8, true},
  135. {"__llvm_slsblr_thunk_thumb_r9", ARM::R9, true},
  136. {"__llvm_slsblr_thunk_thumb_r10", ARM::R10, true},
  137. {"__llvm_slsblr_thunk_thumb_r11", ARM::R11, true},
  138. {"__llvm_slsblr_thunk_thumb_sp", ARM::SP, true},
  139. {"__llvm_slsblr_thunk_thumb_pc", ARM::PC, true},
  140. };
  141. namespace {
  142. struct SLSBLRThunkInserter : ThunkInserter<SLSBLRThunkInserter> {
  143. const char *getThunkPrefix() { return SLSBLRNamePrefix; }
  144. bool mayUseThunk(const MachineFunction &MF) {
  145. ComdatThunks &= !MF.getSubtarget<ARMSubtarget>().hardenSlsNoComdat();
  146. // FIXME: This could also check if there are any indirect calls in the
  147. // function to more accurately reflect if a thunk will be needed.
  148. return MF.getSubtarget<ARMSubtarget>().hardenSlsBlr();
  149. }
  150. void insertThunks(MachineModuleInfo &MMI);
  151. void populateThunk(MachineFunction &MF);
  152. private:
  153. bool ComdatThunks = true;
  154. };
  155. } // namespace
  156. void SLSBLRThunkInserter::insertThunks(MachineModuleInfo &MMI) {
  157. // FIXME: It probably would be possible to filter which thunks to produce
  158. // based on which registers are actually used in indirect calls in this
  159. // function. But would that be a worthwhile optimization?
  160. for (auto T : SLSBLRThunks)
  161. createThunkFunction(MMI, T.Name, ComdatThunks);
  162. }
  163. void SLSBLRThunkInserter::populateThunk(MachineFunction &MF) {
  164. // FIXME: How to better communicate Register number, rather than through
  165. // name and lookup table?
  166. assert(MF.getName().startswith(getThunkPrefix()));
  167. auto ThunkIt = llvm::find_if(
  168. SLSBLRThunks, [&MF](auto T) { return T.Name == MF.getName(); });
  169. assert(ThunkIt != std::end(SLSBLRThunks));
  170. Register ThunkReg = ThunkIt->Reg;
  171. bool isThumb = ThunkIt->isThumb;
  172. const TargetInstrInfo *TII = MF.getSubtarget<ARMSubtarget>().getInstrInfo();
  173. MachineBasicBlock *Entry = &MF.front();
  174. Entry->clear();
  175. // These thunks need to consist of the following instructions:
  176. // __llvm_slsblr_thunk_(arm/thumb)_rN:
  177. // bx rN
  178. // barrierInsts
  179. Entry->addLiveIn(ThunkReg);
  180. if (isThumb)
  181. BuildMI(Entry, DebugLoc(), TII->get(ARM::tBX))
  182. .addReg(ThunkReg)
  183. .add(predOps(ARMCC::AL));
  184. else
  185. BuildMI(Entry, DebugLoc(), TII->get(ARM::BX))
  186. .addReg(ThunkReg);
  187. // Make sure the thunks do not make use of the SB extension in case there is
  188. // a function somewhere that will call to it that for some reason disabled
  189. // the SB extension locally on that function, even though it's enabled for
  190. // the module otherwise. Therefore set AlwaysUseISBSDB to true.
  191. insertSpeculationBarrier(&MF.getSubtarget<ARMSubtarget>(), *Entry,
  192. Entry->end(), DebugLoc(), true /*AlwaysUseISBDSB*/);
  193. }
  194. MachineBasicBlock &ARMSLSHardening::ConvertIndirectCallToIndirectJump(
  195. MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) const {
  196. // Transform an indirect call to an indirect jump as follows:
  197. // Before:
  198. // |-----------------------------|
  199. // | ... |
  200. // | instI |
  201. // | BLX rN |
  202. // | instJ |
  203. // | ... |
  204. // |-----------------------------|
  205. //
  206. // After:
  207. // |---------- -------------------------|
  208. // | ... |
  209. // | instI |
  210. // | *call* __llvm_slsblr_thunk_mode_xN |
  211. // | instJ |
  212. // | ... |
  213. // |--------------------------------------|
  214. //
  215. // __llvm_slsblr_thunk_mode_xN:
  216. // |-----------------------------|
  217. // | BX rN |
  218. // | barrierInsts |
  219. // |-----------------------------|
  220. //
  221. // The __llvm_slsblr_thunk_mode_xN thunks are created by the
  222. // SLSBLRThunkInserter.
  223. // This function merely needs to transform an indirect call to a direct call
  224. // to __llvm_slsblr_thunk_xN.
  225. MachineInstr &IndirectCall = *MBBI;
  226. assert(isIndirectCall(IndirectCall) && !IndirectCall.isReturn());
  227. int RegOpIdxOnIndirectCall = -1;
  228. bool isThumb;
  229. switch (IndirectCall.getOpcode()) {
  230. case ARM::BLX: // !isThumb2
  231. case ARM::BLX_noip: // !isThumb2
  232. isThumb = false;
  233. RegOpIdxOnIndirectCall = 0;
  234. break;
  235. case ARM::tBLXr: // isThumb2
  236. case ARM::tBLXr_noip: // isThumb2
  237. isThumb = true;
  238. RegOpIdxOnIndirectCall = 2;
  239. break;
  240. default:
  241. llvm_unreachable("unhandled Indirect Call");
  242. }
  243. Register Reg = IndirectCall.getOperand(RegOpIdxOnIndirectCall).getReg();
  244. // Since linkers are allowed to clobber R12 on function calls, the above
  245. // mitigation only works if the original indirect call instruction was not
  246. // using R12. Code generation before must make sure that no indirect call
  247. // using R12 was produced if the mitigation is enabled.
  248. // Also, the transformation is incorrect if the indirect call uses LR, so
  249. // also have to avoid that.
  250. assert(Reg != ARM::R12 && Reg != ARM::LR);
  251. bool RegIsKilled = IndirectCall.getOperand(RegOpIdxOnIndirectCall).isKill();
  252. DebugLoc DL = IndirectCall.getDebugLoc();
  253. MachineFunction &MF = *MBBI->getMF();
  254. auto ThunkIt = llvm::find_if(SLSBLRThunks, [Reg, isThumb](auto T) {
  255. return T.Reg == Reg && T.isThumb == isThumb;
  256. });
  257. assert(ThunkIt != std::end(SLSBLRThunks));
  258. Module *M = MF.getFunction().getParent();
  259. const GlobalValue *GV = cast<GlobalValue>(M->getNamedValue(ThunkIt->Name));
  260. MachineInstr *BL =
  261. isThumb ? BuildMI(MBB, MBBI, DL, TII->get(ARM::tBL))
  262. .addImm(IndirectCall.getOperand(0).getImm())
  263. .addReg(IndirectCall.getOperand(1).getReg())
  264. .addGlobalAddress(GV)
  265. : BuildMI(MBB, MBBI, DL, TII->get(ARM::BL)).addGlobalAddress(GV);
  266. // Now copy the implicit operands from IndirectCall to BL and copy other
  267. // necessary info.
  268. // However, both IndirectCall and BL instructions implictly use SP and
  269. // implicitly define LR. Blindly copying implicit operands would result in SP
  270. // and LR operands to be present multiple times. While this may not be too
  271. // much of an issue, let's avoid that for cleanliness, by removing those
  272. // implicit operands from the BL created above before we copy over all
  273. // implicit operands from the IndirectCall.
  274. int ImpLROpIdx = -1;
  275. int ImpSPOpIdx = -1;
  276. for (unsigned OpIdx = BL->getNumExplicitOperands();
  277. OpIdx < BL->getNumOperands(); OpIdx++) {
  278. MachineOperand Op = BL->getOperand(OpIdx);
  279. if (!Op.isReg())
  280. continue;
  281. if (Op.getReg() == ARM::LR && Op.isDef())
  282. ImpLROpIdx = OpIdx;
  283. if (Op.getReg() == ARM::SP && !Op.isDef())
  284. ImpSPOpIdx = OpIdx;
  285. }
  286. assert(ImpLROpIdx != -1);
  287. assert(ImpSPOpIdx != -1);
  288. int FirstOpIdxToRemove = std::max(ImpLROpIdx, ImpSPOpIdx);
  289. int SecondOpIdxToRemove = std::min(ImpLROpIdx, ImpSPOpIdx);
  290. BL->RemoveOperand(FirstOpIdxToRemove);
  291. BL->RemoveOperand(SecondOpIdxToRemove);
  292. // Now copy over the implicit operands from the original IndirectCall
  293. BL->copyImplicitOps(MF, IndirectCall);
  294. MF.moveCallSiteInfo(&IndirectCall, BL);
  295. // Also add the register called in the IndirectCall as being used in the
  296. // called thunk.
  297. BL->addOperand(MachineOperand::CreateReg(Reg, false /*isDef*/, true /*isImp*/,
  298. RegIsKilled /*isKill*/));
  299. // Remove IndirectCallinstruction
  300. MBB.erase(MBBI);
  301. return MBB;
  302. }
  303. bool ARMSLSHardening::hardenIndirectCalls(MachineBasicBlock &MBB) const {
  304. if (!ST->hardenSlsBlr())
  305. return false;
  306. bool Modified = false;
  307. MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
  308. MachineBasicBlock::iterator NextMBBI;
  309. for (; MBBI != E; MBBI = NextMBBI) {
  310. MachineInstr &MI = *MBBI;
  311. NextMBBI = std::next(MBBI);
  312. // Tail calls are both indirect calls and "returns".
  313. // They are also indirect jumps, so should be handled by sls-harden-retbr,
  314. // rather than sls-harden-blr.
  315. if (isIndirectCall(MI) && !MI.isReturn()) {
  316. ConvertIndirectCallToIndirectJump(MBB, MBBI);
  317. Modified = true;
  318. }
  319. }
  320. return Modified;
  321. }
  322. FunctionPass *llvm::createARMSLSHardeningPass() {
  323. return new ARMSLSHardening();
  324. }
  325. namespace {
  326. class ARMIndirectThunks : public MachineFunctionPass {
  327. public:
  328. static char ID;
  329. ARMIndirectThunks() : MachineFunctionPass(ID) {}
  330. StringRef getPassName() const override { return "ARM Indirect Thunks"; }
  331. bool doInitialization(Module &M) override;
  332. bool runOnMachineFunction(MachineFunction &MF) override;
  333. void getAnalysisUsage(AnalysisUsage &AU) const override {
  334. MachineFunctionPass::getAnalysisUsage(AU);
  335. AU.addRequired<MachineModuleInfoWrapperPass>();
  336. AU.addPreserved<MachineModuleInfoWrapperPass>();
  337. }
  338. private:
  339. std::tuple<SLSBLRThunkInserter> TIs;
  340. // FIXME: When LLVM moves to C++17, these can become folds
  341. template <typename... ThunkInserterT>
  342. static void initTIs(Module &M,
  343. std::tuple<ThunkInserterT...> &ThunkInserters) {
  344. (void)std::initializer_list<int>{
  345. (std::get<ThunkInserterT>(ThunkInserters).init(M), 0)...};
  346. }
  347. template <typename... ThunkInserterT>
  348. static bool runTIs(MachineModuleInfo &MMI, MachineFunction &MF,
  349. std::tuple<ThunkInserterT...> &ThunkInserters) {
  350. bool Modified = false;
  351. (void)std::initializer_list<int>{
  352. Modified |= std::get<ThunkInserterT>(ThunkInserters).run(MMI, MF)...};
  353. return Modified;
  354. }
  355. };
  356. } // end anonymous namespace
  357. char ARMIndirectThunks::ID = 0;
  358. FunctionPass *llvm::createARMIndirectThunks() {
  359. return new ARMIndirectThunks();
  360. }
  361. bool ARMIndirectThunks::doInitialization(Module &M) {
  362. initTIs(M, TIs);
  363. return false;
  364. }
  365. bool ARMIndirectThunks::runOnMachineFunction(MachineFunction &MF) {
  366. LLVM_DEBUG(dbgs() << getPassName() << '\n');
  367. auto &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
  368. return runTIs(MMI, MF, TIs);
  369. }