AArch64SLSHardening.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. //===- AArch64SLSHardening.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 "AArch64InstrInfo.h"
  14. #include "AArch64Subtarget.h"
  15. #include "Utils/AArch64BaseInfo.h"
  16. #include "llvm/ADT/BitVector.h"
  17. #include "llvm/ADT/SmallVector.h"
  18. #include "llvm/CodeGen/IndirectThunks.h"
  19. #include "llvm/CodeGen/MachineBasicBlock.h"
  20. #include "llvm/CodeGen/MachineFunction.h"
  21. #include "llvm/CodeGen/MachineFunctionPass.h"
  22. #include "llvm/CodeGen/MachineInstr.h"
  23. #include "llvm/CodeGen/MachineInstrBuilder.h"
  24. #include "llvm/CodeGen/MachineOperand.h"
  25. #include "llvm/CodeGen/MachineRegisterInfo.h"
  26. #include "llvm/CodeGen/RegisterScavenging.h"
  27. #include "llvm/IR/DebugLoc.h"
  28. #include "llvm/Pass.h"
  29. #include "llvm/Support/CodeGen.h"
  30. #include "llvm/Support/Debug.h"
  31. #include "llvm/Target/TargetMachine.h"
  32. #include <cassert>
  33. using namespace llvm;
  34. #define DEBUG_TYPE "aarch64-sls-hardening"
  35. #define AARCH64_SLS_HARDENING_NAME "AArch64 sls hardening pass"
  36. namespace {
  37. class AArch64SLSHardening : public MachineFunctionPass {
  38. public:
  39. const TargetInstrInfo *TII;
  40. const TargetRegisterInfo *TRI;
  41. const AArch64Subtarget *ST;
  42. static char ID;
  43. AArch64SLSHardening() : MachineFunctionPass(ID) {
  44. initializeAArch64SLSHardeningPass(*PassRegistry::getPassRegistry());
  45. }
  46. bool runOnMachineFunction(MachineFunction &Fn) override;
  47. StringRef getPassName() const override { return AARCH64_SLS_HARDENING_NAME; }
  48. private:
  49. bool hardenReturnsAndBRs(MachineBasicBlock &MBB) const;
  50. bool hardenBLRs(MachineBasicBlock &MBB) const;
  51. MachineBasicBlock &ConvertBLRToBL(MachineBasicBlock &MBB,
  52. MachineBasicBlock::iterator) const;
  53. };
  54. } // end anonymous namespace
  55. char AArch64SLSHardening::ID = 0;
  56. INITIALIZE_PASS(AArch64SLSHardening, "aarch64-sls-hardening",
  57. AARCH64_SLS_HARDENING_NAME, false, false)
  58. static void insertSpeculationBarrier(const AArch64Subtarget *ST,
  59. MachineBasicBlock &MBB,
  60. MachineBasicBlock::iterator MBBI,
  61. DebugLoc DL,
  62. bool AlwaysUseISBDSB = false) {
  63. assert(MBBI != MBB.begin() &&
  64. "Must not insert SpeculationBarrierEndBB as only instruction in MBB.");
  65. assert(std::prev(MBBI)->isBarrier() &&
  66. "SpeculationBarrierEndBB must only follow unconditional control flow "
  67. "instructions.");
  68. assert(std::prev(MBBI)->isTerminator() &&
  69. "SpeculationBarrierEndBB must only follow terminators.");
  70. const TargetInstrInfo *TII = ST->getInstrInfo();
  71. unsigned BarrierOpc = ST->hasSB() && !AlwaysUseISBDSB
  72. ? AArch64::SpeculationBarrierSBEndBB
  73. : AArch64::SpeculationBarrierISBDSBEndBB;
  74. if (MBBI == MBB.end() ||
  75. (MBBI->getOpcode() != AArch64::SpeculationBarrierSBEndBB &&
  76. MBBI->getOpcode() != AArch64::SpeculationBarrierISBDSBEndBB))
  77. BuildMI(MBB, MBBI, DL, TII->get(BarrierOpc));
  78. }
  79. bool AArch64SLSHardening::runOnMachineFunction(MachineFunction &MF) {
  80. ST = &MF.getSubtarget<AArch64Subtarget>();
  81. TII = MF.getSubtarget().getInstrInfo();
  82. TRI = MF.getSubtarget().getRegisterInfo();
  83. bool Modified = false;
  84. for (auto &MBB : MF) {
  85. Modified |= hardenReturnsAndBRs(MBB);
  86. Modified |= hardenBLRs(MBB);
  87. }
  88. return Modified;
  89. }
  90. static bool isBLR(const MachineInstr &MI) {
  91. switch (MI.getOpcode()) {
  92. case AArch64::BLR:
  93. case AArch64::BLRNoIP:
  94. return true;
  95. case AArch64::BLRAA:
  96. case AArch64::BLRAB:
  97. case AArch64::BLRAAZ:
  98. case AArch64::BLRABZ:
  99. llvm_unreachable("Currently, LLVM's code generator does not support "
  100. "producing BLRA* instructions. Therefore, there's no "
  101. "support in this pass for those instructions.");
  102. }
  103. return false;
  104. }
  105. bool AArch64SLSHardening::hardenReturnsAndBRs(MachineBasicBlock &MBB) const {
  106. if (!ST->hardenSlsRetBr())
  107. return false;
  108. bool Modified = false;
  109. MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(), E = MBB.end();
  110. MachineBasicBlock::iterator NextMBBI;
  111. for (; MBBI != E; MBBI = NextMBBI) {
  112. MachineInstr &MI = *MBBI;
  113. NextMBBI = std::next(MBBI);
  114. if (MI.isReturn() || isIndirectBranchOpcode(MI.getOpcode())) {
  115. assert(MI.isTerminator());
  116. insertSpeculationBarrier(ST, MBB, std::next(MBBI), MI.getDebugLoc());
  117. Modified = true;
  118. }
  119. }
  120. return Modified;
  121. }
  122. static const char SLSBLRNamePrefix[] = "__llvm_slsblr_thunk_";
  123. static const struct ThunkNameAndReg {
  124. const char* Name;
  125. Register Reg;
  126. } SLSBLRThunks[] = {
  127. { "__llvm_slsblr_thunk_x0", AArch64::X0},
  128. { "__llvm_slsblr_thunk_x1", AArch64::X1},
  129. { "__llvm_slsblr_thunk_x2", AArch64::X2},
  130. { "__llvm_slsblr_thunk_x3", AArch64::X3},
  131. { "__llvm_slsblr_thunk_x4", AArch64::X4},
  132. { "__llvm_slsblr_thunk_x5", AArch64::X5},
  133. { "__llvm_slsblr_thunk_x6", AArch64::X6},
  134. { "__llvm_slsblr_thunk_x7", AArch64::X7},
  135. { "__llvm_slsblr_thunk_x8", AArch64::X8},
  136. { "__llvm_slsblr_thunk_x9", AArch64::X9},
  137. { "__llvm_slsblr_thunk_x10", AArch64::X10},
  138. { "__llvm_slsblr_thunk_x11", AArch64::X11},
  139. { "__llvm_slsblr_thunk_x12", AArch64::X12},
  140. { "__llvm_slsblr_thunk_x13", AArch64::X13},
  141. { "__llvm_slsblr_thunk_x14", AArch64::X14},
  142. { "__llvm_slsblr_thunk_x15", AArch64::X15},
  143. // X16 and X17 are deliberately missing, as the mitigation requires those
  144. // register to not be used in BLR. See comment in ConvertBLRToBL for more
  145. // details.
  146. { "__llvm_slsblr_thunk_x18", AArch64::X18},
  147. { "__llvm_slsblr_thunk_x19", AArch64::X19},
  148. { "__llvm_slsblr_thunk_x20", AArch64::X20},
  149. { "__llvm_slsblr_thunk_x21", AArch64::X21},
  150. { "__llvm_slsblr_thunk_x22", AArch64::X22},
  151. { "__llvm_slsblr_thunk_x23", AArch64::X23},
  152. { "__llvm_slsblr_thunk_x24", AArch64::X24},
  153. { "__llvm_slsblr_thunk_x25", AArch64::X25},
  154. { "__llvm_slsblr_thunk_x26", AArch64::X26},
  155. { "__llvm_slsblr_thunk_x27", AArch64::X27},
  156. { "__llvm_slsblr_thunk_x28", AArch64::X28},
  157. { "__llvm_slsblr_thunk_x29", AArch64::FP},
  158. // X30 is deliberately missing, for similar reasons as X16 and X17 are
  159. // missing.
  160. { "__llvm_slsblr_thunk_x31", AArch64::XZR},
  161. };
  162. namespace {
  163. struct SLSBLRThunkInserter : ThunkInserter<SLSBLRThunkInserter> {
  164. const char *getThunkPrefix() { return SLSBLRNamePrefix; }
  165. bool mayUseThunk(const MachineFunction &MF, bool InsertedThunks) {
  166. if (InsertedThunks)
  167. return false;
  168. ComdatThunks &= !MF.getSubtarget<AArch64Subtarget>().hardenSlsNoComdat();
  169. // FIXME: This could also check if there are any BLRs in the function
  170. // to more accurately reflect if a thunk will be needed.
  171. return MF.getSubtarget<AArch64Subtarget>().hardenSlsBlr();
  172. }
  173. bool insertThunks(MachineModuleInfo &MMI, MachineFunction &MF);
  174. void populateThunk(MachineFunction &MF);
  175. private:
  176. bool ComdatThunks = true;
  177. };
  178. } // namespace
  179. bool SLSBLRThunkInserter::insertThunks(MachineModuleInfo &MMI,
  180. MachineFunction &MF) {
  181. // FIXME: It probably would be possible to filter which thunks to produce
  182. // based on which registers are actually used in BLR instructions in this
  183. // function. But would that be a worthwhile optimization?
  184. for (auto T : SLSBLRThunks)
  185. createThunkFunction(MMI, T.Name, ComdatThunks);
  186. return true;
  187. }
  188. void SLSBLRThunkInserter::populateThunk(MachineFunction &MF) {
  189. // FIXME: How to better communicate Register number, rather than through
  190. // name and lookup table?
  191. assert(MF.getName().startswith(getThunkPrefix()));
  192. auto ThunkIt = llvm::find_if(
  193. SLSBLRThunks, [&MF](auto T) { return T.Name == MF.getName(); });
  194. assert(ThunkIt != std::end(SLSBLRThunks));
  195. Register ThunkReg = ThunkIt->Reg;
  196. const TargetInstrInfo *TII =
  197. MF.getSubtarget<AArch64Subtarget>().getInstrInfo();
  198. assert (MF.size() == 1);
  199. MachineBasicBlock *Entry = &MF.front();
  200. Entry->clear();
  201. // These thunks need to consist of the following instructions:
  202. // __llvm_slsblr_thunk_xN:
  203. // BR xN
  204. // barrierInsts
  205. Entry->addLiveIn(ThunkReg);
  206. // MOV X16, ThunkReg == ORR X16, XZR, ThunkReg, LSL #0
  207. BuildMI(Entry, DebugLoc(), TII->get(AArch64::ORRXrs), AArch64::X16)
  208. .addReg(AArch64::XZR)
  209. .addReg(ThunkReg)
  210. .addImm(0);
  211. BuildMI(Entry, DebugLoc(), TII->get(AArch64::BR)).addReg(AArch64::X16);
  212. // Make sure the thunks do not make use of the SB extension in case there is
  213. // a function somewhere that will call to it that for some reason disabled
  214. // the SB extension locally on that function, even though it's enabled for
  215. // the module otherwise. Therefore set AlwaysUseISBSDB to true.
  216. insertSpeculationBarrier(&MF.getSubtarget<AArch64Subtarget>(), *Entry,
  217. Entry->end(), DebugLoc(), true /*AlwaysUseISBDSB*/);
  218. }
  219. MachineBasicBlock &
  220. AArch64SLSHardening::ConvertBLRToBL(MachineBasicBlock &MBB,
  221. MachineBasicBlock::iterator MBBI) const {
  222. // Transform a BLR to a BL as follows:
  223. // Before:
  224. // |-----------------------------|
  225. // | ... |
  226. // | instI |
  227. // | BLR xN |
  228. // | instJ |
  229. // | ... |
  230. // |-----------------------------|
  231. //
  232. // After:
  233. // |-----------------------------|
  234. // | ... |
  235. // | instI |
  236. // | BL __llvm_slsblr_thunk_xN |
  237. // | instJ |
  238. // | ... |
  239. // |-----------------------------|
  240. //
  241. // __llvm_slsblr_thunk_xN:
  242. // |-----------------------------|
  243. // | BR xN |
  244. // | barrierInsts |
  245. // |-----------------------------|
  246. //
  247. // The __llvm_slsblr_thunk_xN thunks are created by the SLSBLRThunkInserter.
  248. // This function merely needs to transform BLR xN into BL
  249. // __llvm_slsblr_thunk_xN.
  250. //
  251. // Since linkers are allowed to clobber X16 and X17 on function calls, the
  252. // above mitigation only works if the original BLR instruction was not
  253. // BLR X16 nor BLR X17. Code generation before must make sure that no BLR
  254. // X16|X17 was produced if the mitigation is enabled.
  255. MachineInstr &BLR = *MBBI;
  256. assert(isBLR(BLR));
  257. unsigned BLOpcode;
  258. Register Reg;
  259. bool RegIsKilled;
  260. switch (BLR.getOpcode()) {
  261. case AArch64::BLR:
  262. case AArch64::BLRNoIP:
  263. BLOpcode = AArch64::BL;
  264. Reg = BLR.getOperand(0).getReg();
  265. assert(Reg != AArch64::X16 && Reg != AArch64::X17 && Reg != AArch64::LR);
  266. RegIsKilled = BLR.getOperand(0).isKill();
  267. break;
  268. case AArch64::BLRAA:
  269. case AArch64::BLRAB:
  270. case AArch64::BLRAAZ:
  271. case AArch64::BLRABZ:
  272. llvm_unreachable("BLRA instructions cannot yet be produced by LLVM, "
  273. "therefore there is no need to support them for now.");
  274. default:
  275. llvm_unreachable("unhandled BLR");
  276. }
  277. DebugLoc DL = BLR.getDebugLoc();
  278. // If we'd like to support also BLRAA and BLRAB instructions, we'd need
  279. // a lot more different kind of thunks.
  280. // For example, a
  281. //
  282. // BLRAA xN, xM
  283. //
  284. // instruction probably would need to be transformed to something like:
  285. //
  286. // BL __llvm_slsblraa_thunk_x<N>_x<M>
  287. //
  288. // __llvm_slsblraa_thunk_x<N>_x<M>:
  289. // BRAA x<N>, x<M>
  290. // barrierInsts
  291. //
  292. // Given that about 30 different values of N are possible and about 30
  293. // different values of M are possible in the above, with the current way
  294. // of producing indirect thunks, we'd be producing about 30 times 30, i.e.
  295. // about 900 thunks (where most might not be actually called). This would
  296. // multiply further by two to support both BLRAA and BLRAB variants of those
  297. // instructions.
  298. // If we'd want to support this, we'd probably need to look into a different
  299. // way to produce thunk functions, based on which variants are actually
  300. // needed, rather than producing all possible variants.
  301. // So far, LLVM does never produce BLRA* instructions, so let's leave this
  302. // for the future when LLVM can start producing BLRA* instructions.
  303. MachineFunction &MF = *MBBI->getMF();
  304. MCContext &Context = MBB.getParent()->getContext();
  305. auto ThunkIt =
  306. llvm::find_if(SLSBLRThunks, [Reg](auto T) { return T.Reg == Reg; });
  307. assert (ThunkIt != std::end(SLSBLRThunks));
  308. MCSymbol *Sym = Context.getOrCreateSymbol(ThunkIt->Name);
  309. MachineInstr *BL = BuildMI(MBB, MBBI, DL, TII->get(BLOpcode)).addSym(Sym);
  310. // Now copy the implicit operands from BLR to BL and copy other necessary
  311. // info.
  312. // However, both BLR and BL instructions implictly use SP and implicitly
  313. // define LR. Blindly copying implicit operands would result in SP and LR
  314. // operands to be present multiple times. While this may not be too much of
  315. // an issue, let's avoid that for cleanliness, by removing those implicit
  316. // operands from the BL created above before we copy over all implicit
  317. // operands from the BLR.
  318. int ImpLROpIdx = -1;
  319. int ImpSPOpIdx = -1;
  320. for (unsigned OpIdx = BL->getNumExplicitOperands();
  321. OpIdx < BL->getNumOperands(); OpIdx++) {
  322. MachineOperand Op = BL->getOperand(OpIdx);
  323. if (!Op.isReg())
  324. continue;
  325. if (Op.getReg() == AArch64::LR && Op.isDef())
  326. ImpLROpIdx = OpIdx;
  327. if (Op.getReg() == AArch64::SP && !Op.isDef())
  328. ImpSPOpIdx = OpIdx;
  329. }
  330. assert(ImpLROpIdx != -1);
  331. assert(ImpSPOpIdx != -1);
  332. int FirstOpIdxToRemove = std::max(ImpLROpIdx, ImpSPOpIdx);
  333. int SecondOpIdxToRemove = std::min(ImpLROpIdx, ImpSPOpIdx);
  334. BL->removeOperand(FirstOpIdxToRemove);
  335. BL->removeOperand(SecondOpIdxToRemove);
  336. // Now copy over the implicit operands from the original BLR
  337. BL->copyImplicitOps(MF, BLR);
  338. MF.moveCallSiteInfo(&BLR, BL);
  339. // Also add the register called in the BLR as being used in the called thunk.
  340. BL->addOperand(MachineOperand::CreateReg(Reg, false /*isDef*/, true /*isImp*/,
  341. RegIsKilled /*isKill*/));
  342. // Remove BLR instruction
  343. MBB.erase(MBBI);
  344. return MBB;
  345. }
  346. bool AArch64SLSHardening::hardenBLRs(MachineBasicBlock &MBB) const {
  347. if (!ST->hardenSlsBlr())
  348. return false;
  349. bool Modified = false;
  350. MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
  351. MachineBasicBlock::iterator NextMBBI;
  352. for (; MBBI != E; MBBI = NextMBBI) {
  353. MachineInstr &MI = *MBBI;
  354. NextMBBI = std::next(MBBI);
  355. if (isBLR(MI)) {
  356. ConvertBLRToBL(MBB, MBBI);
  357. Modified = true;
  358. }
  359. }
  360. return Modified;
  361. }
  362. FunctionPass *llvm::createAArch64SLSHardeningPass() {
  363. return new AArch64SLSHardening();
  364. }
  365. namespace {
  366. class AArch64IndirectThunks : public MachineFunctionPass {
  367. public:
  368. static char ID;
  369. AArch64IndirectThunks() : MachineFunctionPass(ID) {}
  370. StringRef getPassName() const override { return "AArch64 Indirect Thunks"; }
  371. bool doInitialization(Module &M) override;
  372. bool runOnMachineFunction(MachineFunction &MF) override;
  373. private:
  374. std::tuple<SLSBLRThunkInserter> TIs;
  375. // FIXME: When LLVM moves to C++17, these can become folds
  376. template <typename... ThunkInserterT>
  377. static void initTIs(Module &M,
  378. std::tuple<ThunkInserterT...> &ThunkInserters) {
  379. (void)std::initializer_list<int>{
  380. (std::get<ThunkInserterT>(ThunkInserters).init(M), 0)...};
  381. }
  382. template <typename... ThunkInserterT>
  383. static bool runTIs(MachineModuleInfo &MMI, MachineFunction &MF,
  384. std::tuple<ThunkInserterT...> &ThunkInserters) {
  385. bool Modified = false;
  386. (void)std::initializer_list<int>{
  387. Modified |= std::get<ThunkInserterT>(ThunkInserters).run(MMI, MF)...};
  388. return Modified;
  389. }
  390. };
  391. } // end anonymous namespace
  392. char AArch64IndirectThunks::ID = 0;
  393. FunctionPass *llvm::createAArch64IndirectThunks() {
  394. return new AArch64IndirectThunks();
  395. }
  396. bool AArch64IndirectThunks::doInitialization(Module &M) {
  397. initTIs(M, TIs);
  398. return false;
  399. }
  400. bool AArch64IndirectThunks::runOnMachineFunction(MachineFunction &MF) {
  401. LLVM_DEBUG(dbgs() << getPassName() << '\n');
  402. auto &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
  403. return runTIs(MMI, MF, TIs);
  404. }