1//===- X86MCLFIRewriter.cpp -------------------------------------*- C++ -*-===//
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 implements the X86MCLFIRewriter class, which rewrites X86-64
10// instructions for LFI (Lightweight Fault Isolation) sandboxing.
11//
12//===----------------------------------------------------------------------===//
13
14#include "X86MCLFIRewriter.h"
15#include "X86BaseInfo.h"
16#include "X86MCTargetDesc.h"
17#include "llvm/MC/MCContext.h"
18#include "llvm/MC/MCExpr.h"
19#include "llvm/MC/MCInst.h"
20#include "llvm/MC/MCStreamer.h"
21#include "llvm/MC/MCSubtargetInfo.h"
22
23using namespace llvm;
24
25// LFI reserved registers.
26static constexpr MCRegister LFIBaseReg = X86::R14;
27static constexpr MCRegister LFIScratchReg = X86::R11;
28static constexpr MCRegister LFITPReg = X86::R15;
29
30// Byte offset into the context register file (pointed to by R15) where the
31// thread pointer is stored.
32static constexpr int TPOffset = 16;
33
34static bool isSyscall(const MCInst &Inst) {
35 return Inst.getOpcode() == X86::SYSCALL;
36}
37
38// Find the index of the memory operand if it has an %fs segment override.
39// Returns -1 if there is no memory operand or no %fs override.
40static int findFSMemOperand(const MCInst &Inst, const MCInstrInfo &InstInfo) {
41 const MCInstrDesc &Desc = InstInfo.get(Opcode: Inst.getOpcode());
42 int MemRefIdx = X86II::getMemoryOperandNo(TSFlags: Desc.TSFlags);
43 if (MemRefIdx < 0)
44 return -1;
45 int MemIdx = MemRefIdx + X86II::getOperandBias(Desc);
46 const MCOperand &Seg = Inst.getOperand(i: MemIdx + X86::AddrSegmentReg);
47 if (Seg.isReg() && Seg.getReg() == X86::FS)
48 return MemIdx;
49 return -1;
50}
51
52// Return true if the instruction reads from Reg.
53static bool readsRegister(const MCInst &Inst, const MCInstrDesc &Desc,
54 MCRegister Reg, const MCRegisterInfo &RI) {
55 for (unsigned I = Desc.getNumDefs(), E = Inst.getNumOperands(); I < E; ++I) {
56 const MCOperand &Op = Inst.getOperand(i: I);
57 if (Op.isReg() && Op.getReg() && RI.regsOverlap(RegA: Op.getReg(), RegB: Reg))
58 return true;
59 }
60 for (MCPhysReg Use : Desc.implicit_uses())
61 if (RI.regsOverlap(RegA: Use, RegB: Reg))
62 return true;
63 return false;
64}
65
66// Return true if Reg is absent or a 64-bit general-purpose register.
67static bool isGR64OrNone(MCRegister Reg) {
68 return Reg == X86::NoRegister ||
69 getX86MCRegisterClass(RC: X86::GR64RegClassID).contains(Reg);
70}
71
72// syscall
73// ->
74// leaq .Ltmp(%rip), %r11
75// jmpq *(%r14)
76// .Ltmp:
77void X86::X86MCLFIRewriter::rewriteSyscall(const MCInst &Inst, MCStreamer &Out,
78 const MCSubtargetInfo &STI) {
79 MCSymbol *Symbol = Out.getContext().createTempSymbol();
80
81 // leaq .Ltmp(%rip), %r11
82 MCInst Lea;
83 Lea.setOpcode(X86::LEA64r);
84 Lea.addOperand(Op: MCOperand::createReg(Reg: LFIScratchReg));
85 Lea.addOperand(Op: MCOperand::createReg(Reg: X86::RIP));
86 Lea.addOperand(Op: MCOperand::createImm(Val: 1));
87 Lea.addOperand(Op: MCOperand::createReg(Reg: X86::NoRegister));
88 Lea.addOperand(
89 Op: MCOperand::createExpr(Val: MCSymbolRefExpr::create(Symbol, Ctx&: Out.getContext())));
90 Lea.addOperand(Op: MCOperand::createReg(Reg: X86::NoRegister));
91 Out.emitInstruction(Inst: Lea, STI);
92
93 // jmpq *(%r14)
94 MCInst Jmp;
95 Jmp.setOpcode(X86::JMP64m);
96 Jmp.addOperand(Op: MCOperand::createReg(Reg: LFIBaseReg));
97 Jmp.addOperand(Op: MCOperand::createImm(Val: 1));
98 Jmp.addOperand(Op: MCOperand::createReg(Reg: X86::NoRegister));
99 Jmp.addOperand(Op: MCOperand::createImm(Val: -8));
100 Jmp.addOperand(Op: MCOperand::createReg(Reg: X86::NoRegister));
101 Out.emitInstruction(Inst: Jmp, STI);
102
103 Out.emitLabel(Symbol);
104}
105
106// Emit: movq TPOffset(%r15), %Reg
107static void emitTPLoad(MCRegister Reg, MCStreamer &Out,
108 const MCSubtargetInfo &STI) {
109 MCInst Mov;
110 Mov.setOpcode(X86::MOV64rm);
111 Mov.addOperand(Op: MCOperand::createReg(Reg));
112 Mov.addOperand(Op: MCOperand::createReg(Reg: LFITPReg));
113 Mov.addOperand(Op: MCOperand::createImm(Val: 1));
114 Mov.addOperand(Op: MCOperand::createReg(Reg: X86::NoRegister));
115 Mov.addOperand(Op: MCOperand::createImm(Val: TPOffset));
116 Mov.addOperand(Op: MCOperand::createReg(Reg: X86::NoRegister));
117 Out.emitInstruction(Inst: Mov, STI);
118}
119
120bool X86::X86MCLFIRewriter::isFSAccess(const MCInst &Inst) {
121 return (mayLoad(Inst) || mayStore(Inst)) &&
122 findFSMemOperand(Inst, InstInfo: *InstInfo) >= 0;
123}
124
125// Rewrite %fs-segment memory accesses to use the virtual thread pointer stored
126// at TPOffset(%r15). The actual memory access is currently unsandboxed because
127// load/store sandboxing is not yet supported. Example rewrites:
128//
129// movq %fs:0, %rax
130// ->
131// movq 16(%r15), %rax
132//
133// movq %fs:(%rdi), %rax
134// ->
135// movq 16(%r15), %rax
136// movq (%rax, %rdi), %rax
137//
138// movq %fs:8(%rdi, %rsi, 2), %rax
139// ->
140// movq 16(%r15), %rax
141// leaq (%rax, %rdi), %rax
142// movq 8(%rax, %rsi, 2), %rax
143void X86::X86MCLFIRewriter::rewriteFSAccess(const MCInst &Inst, MCStreamer &Out,
144 const MCSubtargetInfo &STI) {
145 int MemIdx = findFSMemOperand(Inst, InstInfo: *InstInfo);
146 assert(MemIdx >= 0);
147
148 MCRegister BaseReg = Inst.getOperand(i: MemIdx + X86::AddrBaseReg).getReg();
149 MCRegister IndexReg = Inst.getOperand(i: MemIdx + X86::AddrIndexReg).getReg();
150 bool HasBase = BaseReg != X86::NoRegister;
151 bool HasIndex = IndexReg != X86::NoRegister;
152 bool HasDisp = !Inst.getOperand(i: MemIdx + X86::AddrDisp).isImm() ||
153 Inst.getOperand(i: MemIdx + X86::AddrDisp).getImm() != 0;
154
155 // %fs:0 -> TPOffset(%r15)
156 if (!HasBase && !HasIndex && !HasDisp) {
157 MCInst Modified(Inst);
158 Modified.getOperand(i: MemIdx + X86::AddrBaseReg).setReg(LFITPReg);
159 Modified.getOperand(i: MemIdx + X86::AddrDisp).setImm(TPOffset);
160 Modified.getOperand(i: MemIdx + X86::AddrSegmentReg).setReg(X86::NoRegister);
161 return Out.emitInstruction(Inst: Modified, STI);
162 }
163
164 if (!isGR64OrNone(Reg: BaseReg) || !isGR64OrNone(Reg: IndexReg) ||
165 BaseReg == X86::RSP || BaseReg == X86::RIP)
166 return error(Inst, Msg: "unsupported addressing mode for %fs access");
167
168 const MCInstrDesc &Desc = InstInfo->get(Opcode: Inst.getOpcode());
169
170 // Reuse operand 0 as the TP temporary when the instruction writes it without
171 // also reading it, otherwise use %r11.
172 MCRegister TPDest = LFIScratchReg;
173 if (MemIdx > 0 && Inst.getOperand(i: 0).isReg()) {
174 MCRegister DestReg = Inst.getOperand(i: 0).getReg();
175 if (Desc.getNumDefs() > 0 &&
176 getX86MCRegisterClass(RC: X86::GR64RegClassID).contains(Reg: DestReg) &&
177 !readsRegister(Inst, Desc, Reg: DestReg, RI: *RegInfo))
178 TPDest = DestReg;
179 }
180
181 if (TPDest == LFIScratchReg &&
182 readsRegister(Inst, Desc, Reg: LFIScratchReg, RI: *RegInfo))
183 return error(Inst, Msg: "%fs access reads reserved register %r11");
184
185 emitTPLoad(Reg: TPDest, Out, STI);
186
187 // Both slots occupied: the compute base via lea. For example:
188 //
189 // movq %fs:8(%rdi,%rsi,2), %rax
190 // ->
191 // movq 16(%r15), %rax
192 // leaq (%rax,%rdi), %rax
193 // movq 8(%rax,%rsi,2), %rax
194 if (HasBase && HasIndex) {
195 MCInst Lea;
196 Lea.setOpcode(X86::LEA64r);
197 Lea.addOperand(Op: MCOperand::createReg(Reg: TPDest));
198 Lea.addOperand(Op: MCOperand::createReg(Reg: TPDest));
199 Lea.addOperand(Op: MCOperand::createImm(Val: 1));
200 Lea.addOperand(Op: MCOperand::createReg(Reg: BaseReg));
201 Lea.addOperand(Op: MCOperand::createImm(Val: 0));
202 Lea.addOperand(Op: MCOperand::createReg(Reg: X86::NoRegister));
203 Out.emitInstruction(Inst: Lea, STI);
204 }
205
206 // Emit the access with TPDest as the new base, and the original base
207 // (offset from %fs) as the new index. For example:
208 //
209 // movq %fs:(%rdi), %rax
210 // ->
211 // movq 16(%r15), %rax
212 // movq (%rax,%rdi), %rax
213 MCInst Modified(Inst);
214 Modified.getOperand(i: MemIdx + X86::AddrBaseReg).setReg(TPDest);
215 if (HasBase && !HasIndex)
216 Modified.getOperand(i: MemIdx + X86::AddrIndexReg).setReg(BaseReg);
217 Modified.getOperand(i: MemIdx + X86::AddrSegmentReg).setReg(X86::NoRegister);
218 Out.emitInstruction(Inst: Modified, STI);
219}
220
221void X86::X86MCLFIRewriter::doRewriteInst(const MCInst &Inst, MCStreamer &Out,
222 const MCSubtargetInfo &STI) {
223 if (mayModifyRegister(Inst, Reg: LFIBaseReg) || mayModifyRegister(Inst, Reg: LFITPReg))
224 return error(Inst, Msg: "illegal modification of reserved LFI register");
225
226 if (isSyscall(Inst))
227 return rewriteSyscall(Inst, Out, STI);
228
229 if (isFSAccess(Inst))
230 return rewriteFSAccess(Inst, Out, STI);
231
232 // Pass through all other instructions unchanged.
233 Out.emitInstruction(Inst, STI);
234}
235
236bool X86::X86MCLFIRewriter::rewriteInst(const MCInst &Inst, MCStreamer &Out,
237 const MCSubtargetInfo &STI) {
238 // The guard prevents rewrite-recursion when we emit instructions from inside
239 // the rewriter (such instructions should not be rewritten).
240 if (!Enabled || Guard)
241 return false;
242 Guard = true;
243
244 doRewriteInst(Inst, Out, STI);
245
246 Guard = false;
247 return true;
248}
249