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