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
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
)
14 #undef __BPF_FUNC_STR_FN
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
)
20 BUILD_BUG_ON(ARRAY_SIZE(func_id_str
) != __BPF_FUNC_MAX_ID
);
23 insn
->imm
>= 0 && insn
->imm
< __BPF_FUNC_MAX_ID
&&
24 func_id_str
[insn
->imm
])
25 return func_id_str
[insn
->imm
];
27 if (cbs
&& cbs
->cb_call
) {
30 res
= cbs
->cb_call(cbs
->private_data
, insn
);
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");
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
)
47 if (cbs
&& cbs
->cb_imm
)
48 return cbs
->cb_imm(cbs
->private_data
, insn
, full_imm
);
50 snprintf(buff
, len
, "0x%llx", (unsigned long long)full_imm
);
54 const char *func_id_name(int id
)
56 if (id
>= 0 && id
< __BPF_FUNC_MAX_ID
&& func_id_str
[id
])
57 return func_id_str
[id
];
62 const char *const bpf_class_string
[8] = {
69 [BPF_JMP32
] = "jmp32",
70 [BPF_ALU64
] = "alu64",
73 const char *const bpf_alu_string
[16] = {
74 [BPF_ADD
>> 4] = "+=",
75 [BPF_SUB
>> 4] = "-=",
76 [BPF_MUL
>> 4] = "*=",
77 [BPF_DIV
>> 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] = "^=",
86 [BPF_ARSH
>> 4] = "s>>=",
87 [BPF_END
>> 4] = "endian",
90 static const char *const bpf_alu_sign_string
[16] = {
91 [BPF_DIV
>> 4] = "s/=",
92 [BPF_MOD
>> 4] = "s%=",
95 static const char *const bpf_movsx_string
[4] = {
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",
108 static const char *const bpf_ldst_string
[] = {
109 [BPF_W
>> 3] = "u32",
110 [BPF_H
>> 3] = "u16",
112 [BPF_DW
>> 3] = "u64",
115 static const char *const bpf_ldsx_string
[] = {
116 [BPF_W
>> 3] = "s32",
117 [BPF_H
>> 3] = "s16",
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",
138 static void print_bpf_end_insn(bpf_insn_print_t verbose
,
140 const struct bpf_insn
*insn
)
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
);
148 static void print_bpf_bswap_insn(bpf_insn_print_t verbose
,
150 const struct bpf_insn
*insn
)
152 verbose(private_data
, "(%02x) r%d = bswap%d r%d\n",
153 insn
->code
, insn
->dst_reg
,
154 insn
->imm
, insn
->dst_reg
);
157 static bool is_sdiv_smod(const struct bpf_insn
*insn
)
159 return (BPF_OP(insn
->code
) == BPF_DIV
|| BPF_OP(insn
->code
) == BPF_MOD
) &&
163 static bool is_movsx(const struct bpf_insn
*insn
)
165 return BPF_OP(insn
->code
) == BPF_MOV
&&
166 (insn
->off
== 8 || insn
->off
== 16 || insn
->off
== 32);
169 static bool is_addr_space_cast(const struct bpf_insn
*insn
)
171 return insn
->code
== (BPF_ALU64
| BPF_MOV
| BPF_X
) &&
172 insn
->off
== BPF_ADDR_SPACE_CAST
;
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.
179 #define BPF_ADDR_PERCPU (-1)
181 static inline bool is_mov_percpu_addr(const struct bpf_insn
*insn
)
183 return insn
->code
== (BPF_ALU64
| BPF_MOV
| BPF_X
) && insn
->off
== BPF_ADDR_PERCPU
;
186 void print_bpf_insn(const struct bpf_insn_cbs
*cbs
,
187 const struct bpf_insn
*insn
,
188 bool allow_ptr_leaks
)
190 const bpf_insn_print_t verbose
= cbs
->cb_print
;
191 u8
class = BPF_CLASS(insn
->code
);
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
);
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',
204 } else if (is_addr_space_cast(insn
)) {
205 verbose(cbs
->private_data
, "(%02x) r%d = addr_space_cast(r%d, %d, %d)\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',
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',
221 verbose(cbs
->private_data
, "(%02x) %c%d %s %d\n",
222 insn
->code
, class == BPF_ALU
? 'w' : 'r',
224 is_sdiv_smod(insn
) ? bpf_alu_sign_string
[BPF_OP(insn
->code
) >> 4]
225 : bpf_alu_string
[BPF_OP(insn
->code
) >> 4],
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",
232 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
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",
240 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
241 insn
->dst_reg
, insn
->off
,
242 bpf_alu_string
[BPF_OP(insn
->imm
) >> 4],
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",
259 BPF_SIZE(insn
->code
) == BPF_DW
? "64" : "",
260 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
261 insn
->dst_reg
, insn
->off
,
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
);
271 verbose(cbs
->private_data
, "BUG_%02x\n", insn
->code
);
273 } else if (class == BPF_ST
) {
274 if (BPF_MODE(insn
->code
) == BPF_MEM
) {
275 verbose(cbs
->private_data
, "(%02x) *(%s *)(r%d %+d) = %d\n",
277 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
279 insn
->off
, insn
->imm
);
280 } else if (BPF_MODE(insn
->code
) == 0xc0 /* BPF_NOSPEC, no UAPI */) {
281 verbose(cbs
->private_data
, "(%02x) nospec\n", insn
->code
);
283 verbose(cbs
->private_data
, "BUG_st_%02x\n", insn
->code
);
285 } else if (class == BPF_LDX
) {
286 if (BPF_MODE(insn
->code
) != BPF_MEM
&& BPF_MODE(insn
->code
) != BPF_MEMSX
) {
287 verbose(cbs
->private_data
, "BUG_ldx_%02x\n", insn
->code
);
290 verbose(cbs
->private_data
, "(%02x) r%d = *(%s *)(r%d %+d)\n",
291 insn
->code
, insn
->dst_reg
,
292 BPF_MODE(insn
->code
) == BPF_MEM
?
293 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3] :
294 bpf_ldsx_string
[BPF_SIZE(insn
->code
) >> 3],
295 insn
->src_reg
, insn
->off
);
296 } else if (class == BPF_LD
) {
297 if (BPF_MODE(insn
->code
) == BPF_ABS
) {
298 verbose(cbs
->private_data
, "(%02x) r0 = *(%s *)skb[%d]\n",
300 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
302 } else if (BPF_MODE(insn
->code
) == BPF_IND
) {
303 verbose(cbs
->private_data
, "(%02x) r0 = *(%s *)skb[r%d + %d]\n",
305 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
306 insn
->src_reg
, insn
->imm
);
307 } else if (BPF_MODE(insn
->code
) == BPF_IMM
&&
308 BPF_SIZE(insn
->code
) == BPF_DW
) {
309 /* At this point, we already made sure that the second
310 * part of the ldimm64 insn is accessible.
312 u64 imm
= ((u64
)(insn
+ 1)->imm
<< 32) | (u32
)insn
->imm
;
313 bool is_ptr
= insn
->src_reg
== BPF_PSEUDO_MAP_FD
||
314 insn
->src_reg
== BPF_PSEUDO_MAP_VALUE
;
317 if (is_ptr
&& !allow_ptr_leaks
)
320 verbose(cbs
->private_data
, "(%02x) r%d = %s\n",
321 insn
->code
, insn
->dst_reg
,
322 __func_imm_name(cbs
, insn
, imm
,
325 verbose(cbs
->private_data
, "BUG_ld_%02x\n", insn
->code
);
328 } else if (class == BPF_JMP32
|| class == BPF_JMP
) {
329 u8 opcode
= BPF_OP(insn
->code
);
331 if (opcode
== BPF_CALL
) {
334 if (insn
->src_reg
== BPF_PSEUDO_CALL
) {
335 verbose(cbs
->private_data
, "(%02x) call pc%s\n",
337 __func_get_name(cbs
, insn
,
340 strcpy(tmp
, "unknown");
341 verbose(cbs
->private_data
, "(%02x) call %s#%d\n", insn
->code
,
342 __func_get_name(cbs
, insn
,
346 } else if (insn
->code
== (BPF_JMP
| BPF_JA
)) {
347 verbose(cbs
->private_data
, "(%02x) goto pc%+d\n",
348 insn
->code
, insn
->off
);
349 } else if (insn
->code
== (BPF_JMP
| BPF_JCOND
) &&
350 insn
->src_reg
== BPF_MAY_GOTO
) {
351 verbose(cbs
->private_data
, "(%02x) may_goto pc%+d\n",
352 insn
->code
, insn
->off
);
353 } else if (insn
->code
== (BPF_JMP32
| BPF_JA
)) {
354 verbose(cbs
->private_data
, "(%02x) gotol pc%+d\n",
355 insn
->code
, insn
->imm
);
356 } else if (insn
->code
== (BPF_JMP
| BPF_EXIT
)) {
357 verbose(cbs
->private_data
, "(%02x) exit\n", insn
->code
);
358 } else if (BPF_SRC(insn
->code
) == BPF_X
) {
359 verbose(cbs
->private_data
,
360 "(%02x) if %c%d %s %c%d goto pc%+d\n",
361 insn
->code
, class == BPF_JMP32
? 'w' : 'r',
363 bpf_jmp_string
[BPF_OP(insn
->code
) >> 4],
364 class == BPF_JMP32
? 'w' : 'r',
365 insn
->src_reg
, insn
->off
);
367 verbose(cbs
->private_data
,
368 "(%02x) if %c%d %s 0x%x goto pc%+d\n",
369 insn
->code
, class == BPF_JMP32
? 'w' : 'r',
371 bpf_jmp_string
[BPF_OP(insn
->code
) >> 4],
372 insn
->imm
, insn
->off
);
375 verbose(cbs
->private_data
, "(%02x) %s\n",
376 insn
->code
, bpf_class_string
[class]);