| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 1 | //======- X86RetpolineThunks.cpp - Construct retpoline thunks for x86  --=====// | 
|  | 2 | // | 
| Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 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 | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 6 | // | 
|  | 7 | //===----------------------------------------------------------------------===// | 
|  | 8 | /// \file | 
|  | 9 | /// | 
|  | 10 | /// Pass that injects an MI thunk implementing a "retpoline". This is | 
|  | 11 | /// a RET-implemented trampoline that is used to lower indirect calls in a way | 
|  | 12 | /// that prevents speculation on some x86 processors and can be used to mitigate | 
|  | 13 | /// security vulnerabilities due to targeted speculative execution and side | 
|  | 14 | /// channels such as CVE-2017-5715. | 
|  | 15 | /// | 
|  | 16 | /// TODO(chandlerc): All of this code could use better comments and | 
|  | 17 | /// documentation. | 
|  | 18 | /// | 
|  | 19 | //===----------------------------------------------------------------------===// | 
|  | 20 |  | 
|  | 21 | #include "X86.h" | 
|  | 22 | #include "X86InstrBuilder.h" | 
|  | 23 | #include "X86Subtarget.h" | 
|  | 24 | #include "llvm/CodeGen/MachineFunction.h" | 
|  | 25 | #include "llvm/CodeGen/MachineInstrBuilder.h" | 
|  | 26 | #include "llvm/CodeGen/MachineModuleInfo.h" | 
|  | 27 | #include "llvm/CodeGen/Passes.h" | 
|  | 28 | #include "llvm/CodeGen/TargetPassConfig.h" | 
|  | 29 | #include "llvm/IR/IRBuilder.h" | 
|  | 30 | #include "llvm/IR/Instructions.h" | 
|  | 31 | #include "llvm/IR/Module.h" | 
|  | 32 | #include "llvm/Support/CommandLine.h" | 
|  | 33 | #include "llvm/Support/Debug.h" | 
|  | 34 | #include "llvm/Support/raw_ostream.h" | 
|  | 35 |  | 
|  | 36 | using namespace llvm; | 
|  | 37 |  | 
|  | 38 | #define DEBUG_TYPE "x86-retpoline-thunks" | 
|  | 39 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 40 | static const char ThunkNamePrefix[] = "__llvm_retpoline_"; | 
|  | 41 | static const char R11ThunkName[]    = "__llvm_retpoline_r11"; | 
|  | 42 | static const char EAXThunkName[]    = "__llvm_retpoline_eax"; | 
|  | 43 | static const char ECXThunkName[]    = "__llvm_retpoline_ecx"; | 
|  | 44 | static const char EDXThunkName[]    = "__llvm_retpoline_edx"; | 
| Reid Kleckner | 91e11a8 | 2018-02-13 20:47:49 +0000 | [diff] [blame] | 45 | static const char EDIThunkName[]    = "__llvm_retpoline_edi"; | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 46 |  | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 47 | namespace { | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 48 | class X86RetpolineThunks : public MachineFunctionPass { | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 49 | public: | 
|  | 50 | static char ID; | 
|  | 51 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 52 | X86RetpolineThunks() : MachineFunctionPass(ID) {} | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 53 |  | 
|  | 54 | StringRef getPassName() const override { return "X86 Retpoline Thunks"; } | 
|  | 55 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 56 | bool doInitialization(Module &M) override; | 
|  | 57 | bool runOnMachineFunction(MachineFunction &F) override; | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 58 |  | 
|  | 59 | void getAnalysisUsage(AnalysisUsage &AU) const override { | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 60 | MachineFunctionPass::getAnalysisUsage(AU); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 61 | AU.addRequired<MachineModuleInfo>(); | 
|  | 62 | AU.addPreserved<MachineModuleInfo>(); | 
|  | 63 | } | 
|  | 64 |  | 
|  | 65 | private: | 
|  | 66 | MachineModuleInfo *MMI; | 
|  | 67 | const TargetMachine *TM; | 
|  | 68 | bool Is64Bit; | 
|  | 69 | const X86Subtarget *STI; | 
|  | 70 | const X86InstrInfo *TII; | 
|  | 71 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 72 | bool InsertedThunks; | 
|  | 73 |  | 
|  | 74 | void createThunkFunction(Module &M, StringRef Name); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 75 | void insertRegReturnAddrClobber(MachineBasicBlock &MBB, unsigned Reg); | 
| Reid Kleckner | 98d880f | 2018-10-26 20:26:36 +0000 | [diff] [blame] | 76 | void populateThunk(MachineFunction &MF, unsigned Reg); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 77 | }; | 
|  | 78 |  | 
|  | 79 | } // end anonymous namespace | 
|  | 80 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 81 | FunctionPass *llvm::createX86RetpolineThunksPass() { | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 82 | return new X86RetpolineThunks(); | 
|  | 83 | } | 
|  | 84 |  | 
|  | 85 | char X86RetpolineThunks::ID = 0; | 
|  | 86 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 87 | bool X86RetpolineThunks::doInitialization(Module &M) { | 
|  | 88 | InsertedThunks = false; | 
|  | 89 | return false; | 
|  | 90 | } | 
|  | 91 |  | 
|  | 92 | bool X86RetpolineThunks::runOnMachineFunction(MachineFunction &MF) { | 
| Nicola Zaghen | d34e60c | 2018-05-14 12:53:11 +0000 | [diff] [blame] | 93 | LLVM_DEBUG(dbgs() << getPassName() << '\n'); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 94 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 95 | TM = &MF.getTarget();; | 
|  | 96 | STI = &MF.getSubtarget<X86Subtarget>(); | 
|  | 97 | TII = STI->getInstrInfo(); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 98 | Is64Bit = TM->getTargetTriple().getArch() == Triple::x86_64; | 
|  | 99 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 100 | MMI = &getAnalysis<MachineModuleInfo>(); | 
|  | 101 | Module &M = const_cast<Module &>(*MMI->getModule()); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 102 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 103 | // If this function is not a thunk, check to see if we need to insert | 
|  | 104 | // a thunk. | 
|  | 105 | if (!MF.getName().startswith(ThunkNamePrefix)) { | 
|  | 106 | // If we've already inserted a thunk, nothing else to do. | 
|  | 107 | if (InsertedThunks) | 
|  | 108 | return false; | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 109 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 110 | // Only add a thunk if one of the functions has the retpoline feature | 
|  | 111 | // enabled in its subtarget, and doesn't enable external thunks. | 
|  | 112 | // FIXME: Conditionalize on indirect calls so we don't emit a thunk when | 
|  | 113 | // nothing will end up calling it. | 
|  | 114 | // FIXME: It's a little silly to look at every function just to enumerate | 
|  | 115 | // the subtargets, but eventually we'll want to look at them for indirect | 
|  | 116 | // calls, so maybe this is OK. | 
| Chandler Carruth | ae0cafe | 2018-08-23 06:06:38 +0000 | [diff] [blame] | 117 | if ((!STI->useRetpolineIndirectCalls() && | 
|  | 118 | !STI->useRetpolineIndirectBranches()) || | 
|  | 119 | STI->useRetpolineExternalThunk()) | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 120 | return false; | 
|  | 121 |  | 
|  | 122 | // Otherwise, we need to insert the thunk. | 
|  | 123 | // WARNING: This is not really a well behaving thing to do in a function | 
|  | 124 | // pass. We extract the module and insert a new function (and machine | 
|  | 125 | // function) directly into the module. | 
|  | 126 | if (Is64Bit) | 
|  | 127 | createThunkFunction(M, R11ThunkName); | 
|  | 128 | else | 
|  | 129 | for (StringRef Name : | 
| Reid Kleckner | 91e11a8 | 2018-02-13 20:47:49 +0000 | [diff] [blame] | 130 | {EAXThunkName, ECXThunkName, EDXThunkName, EDIThunkName}) | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 131 | createThunkFunction(M, Name); | 
|  | 132 | InsertedThunks = true; | 
|  | 133 | return true; | 
|  | 134 | } | 
|  | 135 |  | 
|  | 136 | // If this *is* a thunk function, we need to populate it with the correct MI. | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 137 | if (Is64Bit) { | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 138 | assert(MF.getName() == "__llvm_retpoline_r11" && | 
|  | 139 | "Should only have an r11 thunk on 64-bit targets"); | 
|  | 140 |  | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 141 | // __llvm_retpoline_r11: | 
|  | 142 | //   callq .Lr11_call_target | 
|  | 143 | // .Lr11_capture_spec: | 
|  | 144 | //   pause | 
|  | 145 | //   lfence | 
|  | 146 | //   jmp .Lr11_capture_spec | 
|  | 147 | // .align 16 | 
|  | 148 | // .Lr11_call_target: | 
|  | 149 | //   movq %r11, (%rsp) | 
|  | 150 | //   retq | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 151 | populateThunk(MF, X86::R11); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 152 | } else { | 
|  | 153 | // For 32-bit targets we need to emit a collection of thunks for various | 
| Reid Kleckner | 91e11a8 | 2018-02-13 20:47:49 +0000 | [diff] [blame] | 154 | // possible scratch registers as well as a fallback that uses EDI, which is | 
|  | 155 | // normally callee saved. | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 156 | //   __llvm_retpoline_eax: | 
|  | 157 | //         calll .Leax_call_target | 
|  | 158 | //   .Leax_capture_spec: | 
|  | 159 | //         pause | 
|  | 160 | //         jmp .Leax_capture_spec | 
|  | 161 | //   .align 16 | 
|  | 162 | //   .Leax_call_target: | 
|  | 163 | //         movl %eax, (%esp)  # Clobber return addr | 
|  | 164 | //         retl | 
|  | 165 | // | 
|  | 166 | //   __llvm_retpoline_ecx: | 
|  | 167 | //   ... # Same setup | 
|  | 168 | //         movl %ecx, (%esp) | 
|  | 169 | //         retl | 
|  | 170 | // | 
|  | 171 | //   __llvm_retpoline_edx: | 
|  | 172 | //   ... # Same setup | 
|  | 173 | //         movl %edx, (%esp) | 
|  | 174 | //         retl | 
|  | 175 | // | 
| Reid Kleckner | 91e11a8 | 2018-02-13 20:47:49 +0000 | [diff] [blame] | 176 | //   __llvm_retpoline_edi: | 
|  | 177 | //   ... # Same setup | 
|  | 178 | //         movl %edi, (%esp) | 
|  | 179 | //         retl | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 180 | if (MF.getName() == EAXThunkName) | 
|  | 181 | populateThunk(MF, X86::EAX); | 
|  | 182 | else if (MF.getName() == ECXThunkName) | 
|  | 183 | populateThunk(MF, X86::ECX); | 
|  | 184 | else if (MF.getName() == EDXThunkName) | 
|  | 185 | populateThunk(MF, X86::EDX); | 
| Reid Kleckner | 91e11a8 | 2018-02-13 20:47:49 +0000 | [diff] [blame] | 186 | else if (MF.getName() == EDIThunkName) | 
|  | 187 | populateThunk(MF, X86::EDI); | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 188 | else | 
|  | 189 | llvm_unreachable("Invalid thunk name on x86-32!"); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 190 | } | 
|  | 191 |  | 
|  | 192 | return true; | 
|  | 193 | } | 
|  | 194 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 195 | void X86RetpolineThunks::createThunkFunction(Module &M, StringRef Name) { | 
|  | 196 | assert(Name.startswith(ThunkNamePrefix) && | 
|  | 197 | "Created a thunk with an unexpected prefix!"); | 
|  | 198 |  | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 199 | LLVMContext &Ctx = M.getContext(); | 
|  | 200 | auto Type = FunctionType::get(Type::getVoidTy(Ctx), false); | 
|  | 201 | Function *F = | 
|  | 202 | Function::Create(Type, GlobalValue::LinkOnceODRLinkage, Name, &M); | 
|  | 203 | F->setVisibility(GlobalValue::HiddenVisibility); | 
|  | 204 | F->setComdat(M.getOrInsertComdat(Name)); | 
|  | 205 |  | 
|  | 206 | // Add Attributes so that we don't create a frame, unwind information, or | 
|  | 207 | // inline. | 
|  | 208 | AttrBuilder B; | 
|  | 209 | B.addAttribute(llvm::Attribute::NoUnwind); | 
|  | 210 | B.addAttribute(llvm::Attribute::Naked); | 
|  | 211 | F->addAttributes(llvm::AttributeList::FunctionIndex, B); | 
|  | 212 |  | 
|  | 213 | // Populate our function a bit so that we can verify. | 
|  | 214 | BasicBlock *Entry = BasicBlock::Create(Ctx, "entry", F); | 
|  | 215 | IRBuilder<> Builder(Entry); | 
|  | 216 |  | 
|  | 217 | Builder.CreateRetVoid(); | 
| Jessica Paquette | 7853666 | 2018-05-01 20:49:42 +0000 | [diff] [blame] | 218 |  | 
|  | 219 | // MachineFunctions/MachineBasicBlocks aren't created automatically for the | 
|  | 220 | // IR-level constructs we already made. Create them and insert them into the | 
|  | 221 | // module. | 
|  | 222 | MachineFunction &MF = MMI->getOrCreateMachineFunction(*F); | 
|  | 223 | MachineBasicBlock *EntryMBB = MF.CreateMachineBasicBlock(Entry); | 
|  | 224 |  | 
|  | 225 | // Insert EntryMBB into MF. It's not in the module until we do this. | 
|  | 226 | MF.insert(MF.end(), EntryMBB); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 227 | } | 
|  | 228 |  | 
|  | 229 | void X86RetpolineThunks::insertRegReturnAddrClobber(MachineBasicBlock &MBB, | 
|  | 230 | unsigned Reg) { | 
|  | 231 | const unsigned MovOpc = Is64Bit ? X86::MOV64mr : X86::MOV32mr; | 
|  | 232 | const unsigned SPReg = Is64Bit ? X86::RSP : X86::ESP; | 
|  | 233 | addRegOffset(BuildMI(&MBB, DebugLoc(), TII->get(MovOpc)), SPReg, false, 0) | 
|  | 234 | .addReg(Reg); | 
|  | 235 | } | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 236 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 237 | void X86RetpolineThunks::populateThunk(MachineFunction &MF, | 
| Reid Kleckner | 98d880f | 2018-10-26 20:26:36 +0000 | [diff] [blame] | 238 | unsigned Reg) { | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 239 | // Set MF properties. We never use vregs... | 
|  | 240 | MF.getProperties().set(MachineFunctionProperties::Property::NoVRegs); | 
|  | 241 |  | 
| Reid Kleckner | 98d880f | 2018-10-26 20:26:36 +0000 | [diff] [blame] | 242 | // Grab the entry MBB and erase any other blocks. O0 codegen appears to | 
|  | 243 | // generate two bbs for the entry block. | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 244 | MachineBasicBlock *Entry = &MF.front(); | 
|  | 245 | Entry->clear(); | 
| Reid Kleckner | 98d880f | 2018-10-26 20:26:36 +0000 | [diff] [blame] | 246 | while (MF.size() > 1) | 
|  | 247 | MF.erase(std::next(MF.begin())); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 248 |  | 
| Chandler Carruth | 0dcee4f | 2018-01-31 20:56:37 +0000 | [diff] [blame] | 249 | MachineBasicBlock *CaptureSpec = MF.CreateMachineBasicBlock(Entry->getBasicBlock()); | 
|  | 250 | MachineBasicBlock *CallTarget = MF.CreateMachineBasicBlock(Entry->getBasicBlock()); | 
| Reid Kleckner | 98d880f | 2018-10-26 20:26:36 +0000 | [diff] [blame] | 251 | MCSymbol *TargetSym = MF.getContext().createTempSymbol(); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 252 | MF.push_back(CaptureSpec); | 
|  | 253 | MF.push_back(CallTarget); | 
|  | 254 |  | 
|  | 255 | const unsigned CallOpc = Is64Bit ? X86::CALL64pcrel32 : X86::CALLpcrel32; | 
|  | 256 | const unsigned RetOpc = Is64Bit ? X86::RETQ : X86::RETL; | 
|  | 257 |  | 
| Reid Kleckner | 98d880f | 2018-10-26 20:26:36 +0000 | [diff] [blame] | 258 | Entry->addLiveIn(Reg); | 
|  | 259 | BuildMI(Entry, DebugLoc(), TII->get(CallOpc)).addSym(TargetSym); | 
|  | 260 |  | 
|  | 261 | // The MIR verifier thinks that the CALL in the entry block will fall through | 
|  | 262 | // to CaptureSpec, so mark it as the successor. Technically, CaptureTarget is | 
|  | 263 | // the successor, but the MIR verifier doesn't know how to cope with that. | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 264 | Entry->addSuccessor(CaptureSpec); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 265 |  | 
|  | 266 | // In the capture loop for speculation, we want to stop the processor from | 
|  | 267 | // speculating as fast as possible. On Intel processors, the PAUSE instruction | 
|  | 268 | // will block speculation without consuming any execution resources. On AMD | 
|  | 269 | // processors, the PAUSE instruction is (essentially) a nop, so we also use an | 
|  | 270 | // LFENCE instruction which they have advised will stop speculation as well | 
|  | 271 | // with minimal resource utilization. We still end the capture with a jump to | 
|  | 272 | // form an infinite loop to fully guarantee that no matter what implementation | 
|  | 273 | // of the x86 ISA, speculating this code path never escapes. | 
|  | 274 | BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::PAUSE)); | 
|  | 275 | BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::LFENCE)); | 
|  | 276 | BuildMI(CaptureSpec, DebugLoc(), TII->get(X86::JMP_1)).addMBB(CaptureSpec); | 
|  | 277 | CaptureSpec->setHasAddressTaken(); | 
|  | 278 | CaptureSpec->addSuccessor(CaptureSpec); | 
|  | 279 |  | 
| Reid Kleckner | 98d880f | 2018-10-26 20:26:36 +0000 | [diff] [blame] | 280 | CallTarget->addLiveIn(Reg); | 
|  | 281 | CallTarget->setHasAddressTaken(); | 
| Guillaume Chatelet | d4c4671 | 2019-09-18 15:49:49 +0000 | [diff] [blame^] | 282 | CallTarget->setAlignment(llvm::Align(16)); | 
| Reid Kleckner | 98d880f | 2018-10-26 20:26:36 +0000 | [diff] [blame] | 283 | insertRegReturnAddrClobber(*CallTarget, Reg); | 
|  | 284 | CallTarget->back().setPreInstrSymbol(MF, TargetSym); | 
| Chandler Carruth | c58f216 | 2018-01-22 22:05:25 +0000 | [diff] [blame] | 285 | BuildMI(CallTarget, DebugLoc(), TII->get(RetOpc)); | 
|  | 286 | } |