1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
3 * Copyright (c) 2016 Facebook
4 */
5
6 #include <linux/bpf.h>
7
8 #include "disasm.h"
9
10 /* Only defined by the non-UAPI linux/filter.h, which this file cannot use. */
11 #define BPF_PROBE_ATOMIC 0xe0
12
13 #define __BPF_FUNC_STR_FN(x) [BPF_FUNC_ ## x] = __stringify(bpf_ ## x)
14 static const char * const func_id_str[] = {
15 __BPF_FUNC_MAPPER(__BPF_FUNC_STR_FN)
16 };
17 #undef __BPF_FUNC_STR_FN
18
__func_get_name(const struct bpf_insn_cbs * cbs,const struct bpf_insn * insn,char * buff,size_t len)19 static const char *__func_get_name(const struct bpf_insn_cbs *cbs,
20 const struct bpf_insn *insn,
21 char *buff, size_t len)
22 {
23 BUILD_BUG_ON(ARRAY_SIZE(func_id_str) != __BPF_FUNC_MAX_ID);
24
25 if (!insn->src_reg &&
26 insn->imm >= 0 && insn->imm < __BPF_FUNC_MAX_ID &&
27 func_id_str[insn->imm])
28 return func_id_str[insn->imm];
29
30 if (cbs && cbs->cb_call) {
31 const char *res;
32
33 res = cbs->cb_call(cbs->private_data, insn);
34 if (res)
35 return res;
36 }
37
38 if (insn->src_reg == BPF_PSEUDO_CALL)
39 snprintf(buff, len, "%+d", insn->imm);
40 else if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL)
41 snprintf(buff, len, "kernel-function");
42
43 return buff;
44 }
45
__func_imm_name(const struct bpf_insn_cbs * cbs,const struct bpf_insn * insn,u64 full_imm,char * buff,size_t len)46 static const char *__func_imm_name(const struct bpf_insn_cbs *cbs,
47 const struct bpf_insn *insn,
48 u64 full_imm, char *buff, size_t len)
49 {
50 if (cbs && cbs->cb_imm)
51 return cbs->cb_imm(cbs->private_data, insn, full_imm);
52
53 snprintf(buff, len, "0x%llx", (unsigned long long)full_imm);
54 return buff;
55 }
56
func_id_name(int id)57 const char *func_id_name(int id)
58 {
59 if (id >= 0 && id < __BPF_FUNC_MAX_ID && func_id_str[id])
60 return func_id_str[id];
61 else
62 return "unknown";
63 }
64
65 const char *const bpf_class_string[8] = {
66 [BPF_LD] = "ld",
67 [BPF_LDX] = "ldx",
68 [BPF_ST] = "st",
69 [BPF_STX] = "stx",
70 [BPF_ALU] = "alu",
71 [BPF_JMP] = "jmp",
72 [BPF_JMP32] = "jmp32",
73 [BPF_ALU64] = "alu64",
74 };
75
76 const char *const bpf_alu_string[16] = {
77 [BPF_ADD >> 4] = "+=",
78 [BPF_SUB >> 4] = "-=",
79 [BPF_MUL >> 4] = "*=",
80 [BPF_DIV >> 4] = "/=",
81 [BPF_OR >> 4] = "|=",
82 [BPF_AND >> 4] = "&=",
83 [BPF_LSH >> 4] = "<<=",
84 [BPF_RSH >> 4] = ">>=",
85 [BPF_NEG >> 4] = "neg",
86 [BPF_MOD >> 4] = "%=",
87 [BPF_XOR >> 4] = "^=",
88 [BPF_MOV >> 4] = "=",
89 [BPF_ARSH >> 4] = "s>>=",
90 [BPF_END >> 4] = "endian",
91 };
92
93 static const char *const bpf_alu_sign_string[16] = {
94 [BPF_DIV >> 4] = "s/=",
95 [BPF_MOD >> 4] = "s%=",
96 };
97
98 static const char *const bpf_movsx_string[4] = {
99 [0] = "(s8)",
100 [1] = "(s16)",
101 [3] = "(s32)",
102 };
103
104 static const char *const bpf_atomic_alu_string[16] = {
105 [BPF_ADD >> 4] = "add",
106 [BPF_AND >> 4] = "and",
107 [BPF_OR >> 4] = "or",
108 [BPF_XOR >> 4] = "xor",
109 };
110
111 static const char *const bpf_ldst_string[] = {
112 [BPF_W >> 3] = "u32",
113 [BPF_H >> 3] = "u16",
114 [BPF_B >> 3] = "u8",
115 [BPF_DW >> 3] = "u64",
116 };
117
118 static const char *const bpf_ldsx_string[] = {
119 [BPF_W >> 3] = "s32",
120 [BPF_H >> 3] = "s16",
121 [BPF_B >> 3] = "s8",
122 };
123
124 static const char *const bpf_jmp_string[16] = {
125 [BPF_JA >> 4] = "jmp",
126 [BPF_JEQ >> 4] = "==",
127 [BPF_JGT >> 4] = ">",
128 [BPF_JLT >> 4] = "<",
129 [BPF_JGE >> 4] = ">=",
130 [BPF_JLE >> 4] = "<=",
131 [BPF_JSET >> 4] = "&",
132 [BPF_JNE >> 4] = "!=",
133 [BPF_JSGT >> 4] = "s>",
134 [BPF_JSLT >> 4] = "s<",
135 [BPF_JSGE >> 4] = "s>=",
136 [BPF_JSLE >> 4] = "s<=",
137 [BPF_CALL >> 4] = "call",
138 [BPF_EXIT >> 4] = "exit",
139 };
140
print_bpf_end_insn(bpf_insn_print_t verbose,void * private_data,const struct bpf_insn * insn)141 static void print_bpf_end_insn(bpf_insn_print_t verbose,
142 void *private_data,
143 const struct bpf_insn *insn)
144 {
145 verbose(private_data, "(%02x) r%d = %s%d r%d",
146 insn->code, insn->dst_reg,
147 BPF_SRC(insn->code) == BPF_TO_BE ? "be" : "le",
148 insn->imm, insn->dst_reg);
149 }
150
print_bpf_bswap_insn(bpf_insn_print_t verbose,void * private_data,const struct bpf_insn * insn)151 static void print_bpf_bswap_insn(bpf_insn_print_t verbose,
152 void *private_data,
153 const struct bpf_insn *insn)
154 {
155 verbose(private_data, "(%02x) r%d = bswap%d r%d",
156 insn->code, insn->dst_reg,
157 insn->imm, insn->dst_reg);
158 }
159
is_sdiv_smod(const struct bpf_insn * insn)160 static bool is_sdiv_smod(const struct bpf_insn *insn)
161 {
162 return (BPF_OP(insn->code) == BPF_DIV || BPF_OP(insn->code) == BPF_MOD) &&
163 insn->off == 1;
164 }
165
is_movsx(const struct bpf_insn * insn)166 static bool is_movsx(const struct bpf_insn *insn)
167 {
168 return BPF_OP(insn->code) == BPF_MOV &&
169 (insn->off == 8 || insn->off == 16 || insn->off == 32);
170 }
171
is_addr_space_cast(const struct bpf_insn * insn)172 static bool is_addr_space_cast(const struct bpf_insn *insn)
173 {
174 return insn->code == (BPF_ALU64 | BPF_MOV | BPF_X) &&
175 insn->off == BPF_ADDR_SPACE_CAST;
176 }
177
178 /* Special (internal-only) form of mov, used to resolve per-CPU addrs:
179 * dst_reg = src_reg + <percpu_base_off>
180 * BPF_ADDR_PERCPU is used as a special insn->off value.
181 */
182 #define BPF_ADDR_PERCPU (-1)
183
is_mov_percpu_addr(const struct bpf_insn * insn)184 static inline bool is_mov_percpu_addr(const struct bpf_insn *insn)
185 {
186 return insn->code == (BPF_ALU64 | BPF_MOV | BPF_X) && insn->off == BPF_ADDR_PERCPU;
187 }
188
print_bpf_insn(const struct bpf_insn_cbs * cbs,const struct bpf_insn * insn,bool allow_ptr_leaks)189 void print_bpf_insn(const struct bpf_insn_cbs *cbs,
190 const struct bpf_insn *insn,
191 bool allow_ptr_leaks)
192 {
193 const bpf_insn_print_t verbose = cbs->cb_print;
194 u8 class = BPF_CLASS(insn->code);
195
196 if (class == BPF_ALU || class == BPF_ALU64) {
197 if (BPF_OP(insn->code) == BPF_END) {
198 if (class == BPF_ALU64)
199 print_bpf_bswap_insn(verbose, cbs->private_data, insn);
200 else
201 print_bpf_end_insn(verbose, cbs->private_data, insn);
202 } else if (BPF_OP(insn->code) == BPF_NEG) {
203 verbose(cbs->private_data, "(%02x) %c%d = -%c%d",
204 insn->code, class == BPF_ALU ? 'w' : 'r',
205 insn->dst_reg, class == BPF_ALU ? 'w' : 'r',
206 insn->dst_reg);
207 } else if (is_addr_space_cast(insn)) {
208 verbose(cbs->private_data, "(%02x) r%d = addr_space_cast(r%d, %u, %u)",
209 insn->code, insn->dst_reg,
210 insn->src_reg, ((u32)insn->imm) >> 16, (u16)insn->imm);
211 } else if (is_mov_percpu_addr(insn)) {
212 verbose(cbs->private_data, "(%02x) r%d = &(void __percpu *)(r%d)",
213 insn->code, insn->dst_reg, insn->src_reg);
214 } else if (BPF_SRC(insn->code) == BPF_X) {
215 verbose(cbs->private_data, "(%02x) %c%d %s %s%c%d",
216 insn->code, class == BPF_ALU ? 'w' : 'r',
217 insn->dst_reg,
218 is_sdiv_smod(insn) ? bpf_alu_sign_string[BPF_OP(insn->code) >> 4]
219 : bpf_alu_string[BPF_OP(insn->code) >> 4],
220 is_movsx(insn) ? bpf_movsx_string[(insn->off >> 3) - 1] : "",
221 class == BPF_ALU ? 'w' : 'r',
222 insn->src_reg);
223 } else {
224 verbose(cbs->private_data, "(%02x) %c%d %s %d",
225 insn->code, class == BPF_ALU ? 'w' : 'r',
226 insn->dst_reg,
227 is_sdiv_smod(insn) ? bpf_alu_sign_string[BPF_OP(insn->code) >> 4]
228 : bpf_alu_string[BPF_OP(insn->code) >> 4],
229 insn->imm);
230 }
231 } else if (class == BPF_STX) {
232 const char *probe_pfx = BPF_MODE(insn->code) == BPF_PROBE_ATOMIC ? "probe " : "";
233 bool atomic = BPF_MODE(insn->code) == BPF_ATOMIC ||
234 BPF_MODE(insn->code) == BPF_PROBE_ATOMIC;
235
236 if (BPF_MODE(insn->code) == BPF_MEM)
237 verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = r%d",
238 insn->code,
239 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
240 insn->dst_reg,
241 insn->off, insn->src_reg);
242 else if (atomic &&
243 (insn->imm == BPF_ADD || insn->imm == BPF_AND ||
244 insn->imm == BPF_OR || insn->imm == BPF_XOR)) {
245 verbose(cbs->private_data, "(%02x) %slock *(%s *)(r%d %+d) %s r%d",
246 insn->code, probe_pfx,
247 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
248 insn->dst_reg, insn->off,
249 bpf_alu_string[BPF_OP(insn->imm) >> 4],
250 insn->src_reg);
251 } else if (atomic &&
252 (insn->imm == (BPF_ADD | BPF_FETCH) ||
253 insn->imm == (BPF_AND | BPF_FETCH) ||
254 insn->imm == (BPF_OR | BPF_FETCH) ||
255 insn->imm == (BPF_XOR | BPF_FETCH))) {
256 verbose(cbs->private_data, "(%02x) %sr%d = atomic%s_fetch_%s((%s *)(r%d %+d), r%d)",
257 insn->code, probe_pfx, insn->src_reg,
258 BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
259 bpf_atomic_alu_string[BPF_OP(insn->imm) >> 4],
260 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
261 insn->dst_reg, insn->off, insn->src_reg);
262 } else if (atomic && insn->imm == BPF_CMPXCHG) {
263 verbose(cbs->private_data, "(%02x) %sr0 = atomic%s_cmpxchg((%s *)(r%d %+d), r0, r%d)",
264 insn->code, probe_pfx,
265 BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
266 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
267 insn->dst_reg, insn->off,
268 insn->src_reg);
269 } else if (atomic && insn->imm == BPF_XCHG) {
270 verbose(cbs->private_data, "(%02x) %sr%d = atomic%s_xchg((%s *)(r%d %+d), r%d)",
271 insn->code, probe_pfx, insn->src_reg,
272 BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
273 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
274 insn->dst_reg, insn->off, insn->src_reg);
275 } else if (atomic && insn->imm == BPF_LOAD_ACQ) {
276 verbose(cbs->private_data, "(%02x) %sr%d = load_acquire((%s *)(r%d %+d))",
277 insn->code, probe_pfx, insn->dst_reg,
278 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
279 insn->src_reg, insn->off);
280 } else if (atomic && insn->imm == BPF_STORE_REL) {
281 verbose(cbs->private_data, "(%02x) %sstore_release((%s *)(r%d %+d), r%d)",
282 insn->code, probe_pfx,
283 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
284 insn->dst_reg, insn->off, insn->src_reg);
285 } else {
286 verbose(cbs->private_data, "BUG_%02x", insn->code);
287 }
288 } else if (class == BPF_ST) {
289 if (BPF_MODE(insn->code) == BPF_MEM) {
290 verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = %d",
291 insn->code,
292 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
293 insn->dst_reg,
294 insn->off, insn->imm);
295 } else if (BPF_MODE(insn->code) == 0xc0 /* BPF_NOSPEC, no UAPI */) {
296 verbose(cbs->private_data, "(%02x) nospec", insn->code);
297 } else {
298 verbose(cbs->private_data, "BUG_st_%02x", insn->code);
299 }
300 } else if (class == BPF_LDX) {
301 if ((BPF_MODE(insn->code) != BPF_MEM && BPF_MODE(insn->code) != BPF_MEMSX) ||
302 (BPF_MODE(insn->code) == BPF_MEMSX && BPF_SIZE(insn->code) == BPF_DW)) {
303 verbose(cbs->private_data, "BUG_ldx_%02x", insn->code);
304 return;
305 }
306 verbose(cbs->private_data, "(%02x) r%d = *(%s *)(r%d %+d)",
307 insn->code, insn->dst_reg,
308 BPF_MODE(insn->code) == BPF_MEM ?
309 bpf_ldst_string[BPF_SIZE(insn->code) >> 3] :
310 bpf_ldsx_string[BPF_SIZE(insn->code) >> 3],
311 insn->src_reg, insn->off);
312 } else if (class == BPF_LD) {
313 if (BPF_MODE(insn->code) == BPF_ABS) {
314 verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[%d]",
315 insn->code,
316 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
317 insn->imm);
318 } else if (BPF_MODE(insn->code) == BPF_IND) {
319 verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[r%d + %d]",
320 insn->code,
321 bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
322 insn->src_reg, insn->imm);
323 } else if (BPF_MODE(insn->code) == BPF_IMM &&
324 BPF_SIZE(insn->code) == BPF_DW) {
325 /* At this point, we already made sure that the second
326 * part of the ldimm64 insn is accessible.
327 */
328 u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm;
329 bool is_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD ||
330 insn->src_reg == BPF_PSEUDO_MAP_VALUE ||
331 insn->src_reg == BPF_PSEUDO_MAP_IDX ||
332 insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE ||
333 insn->src_reg == BPF_PSEUDO_BTF_ID;
334 char tmp[64];
335
336 if (is_ptr && !allow_ptr_leaks)
337 imm = 0;
338
339 verbose(cbs->private_data, "(%02x) r%d = %s",
340 insn->code, insn->dst_reg,
341 __func_imm_name(cbs, insn, imm,
342 tmp, sizeof(tmp)));
343 } else {
344 verbose(cbs->private_data, "BUG_ld_%02x", insn->code);
345 return;
346 }
347 } else if (class == BPF_JMP32 || class == BPF_JMP) {
348 u8 opcode = BPF_OP(insn->code);
349
350 if (opcode == BPF_CALL) {
351 char tmp[64];
352
353 if (insn->src_reg == BPF_PSEUDO_CALL) {
354 verbose(cbs->private_data, "(%02x) call pc%s",
355 insn->code,
356 __func_get_name(cbs, insn,
357 tmp, sizeof(tmp)));
358 } else {
359 strcpy(tmp, "unknown");
360 verbose(cbs->private_data, "(%02x) call %s#%d", insn->code,
361 __func_get_name(cbs, insn,
362 tmp, sizeof(tmp)),
363 insn->imm);
364 }
365 } else if (insn->code == (BPF_JMP | BPF_JA)) {
366 verbose(cbs->private_data, "(%02x) goto pc%+d",
367 insn->code, insn->off);
368 } else if (insn->code == (BPF_JMP | BPF_JA | BPF_X)) {
369 verbose(cbs->private_data, "(%02x) gotox r%d",
370 insn->code, insn->dst_reg);
371 } else if (insn->code == (BPF_JMP | BPF_JCOND) &&
372 insn->src_reg == BPF_MAY_GOTO) {
373 verbose(cbs->private_data, "(%02x) may_goto pc%+d",
374 insn->code, insn->off);
375 } else if (insn->code == (BPF_JMP32 | BPF_JA)) {
376 verbose(cbs->private_data, "(%02x) gotol pc%+d",
377 insn->code, insn->imm);
378 } else if (insn->code == (BPF_JMP | BPF_EXIT)) {
379 verbose(cbs->private_data, "(%02x) exit", insn->code);
380 } else if (BPF_SRC(insn->code) == BPF_X) {
381 verbose(cbs->private_data,
382 "(%02x) if %c%d %s %c%d goto pc%+d",
383 insn->code, class == BPF_JMP32 ? 'w' : 'r',
384 insn->dst_reg,
385 bpf_jmp_string[BPF_OP(insn->code) >> 4],
386 class == BPF_JMP32 ? 'w' : 'r',
387 insn->src_reg, insn->off);
388 } else {
389 verbose(cbs->private_data,
390 "(%02x) if %c%d %s 0x%x goto pc%+d",
391 insn->code, class == BPF_JMP32 ? 'w' : 'r',
392 insn->dst_reg,
393 bpf_jmp_string[BPF_OP(insn->code) >> 4],
394 (u32)insn->imm, insn->off);
395 }
396 } else {
397 verbose(cbs->private_data, "(%02x) %s",
398 insn->code, bpf_class_string[class]);
399 }
400 }
401