]>
git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - kernel/bpf/disasm.c
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_atomic_alu_string
[16] = {
91 [BPF_ADD
>> 4] = "add",
92 [BPF_AND
>> 4] = "and",
94 [BPF_XOR
>> 4] = "xor",
97 static const char *const bpf_ldst_string
[] = {
101 [BPF_DW
>> 3] = "u64",
104 static const char *const bpf_jmp_string
[16] = {
105 [BPF_JA
>> 4] = "jmp",
106 [BPF_JEQ
>> 4] = "==",
107 [BPF_JGT
>> 4] = ">",
108 [BPF_JLT
>> 4] = "<",
109 [BPF_JGE
>> 4] = ">=",
110 [BPF_JLE
>> 4] = "<=",
111 [BPF_JSET
>> 4] = "&",
112 [BPF_JNE
>> 4] = "!=",
113 [BPF_JSGT
>> 4] = "s>",
114 [BPF_JSLT
>> 4] = "s<",
115 [BPF_JSGE
>> 4] = "s>=",
116 [BPF_JSLE
>> 4] = "s<=",
117 [BPF_CALL
>> 4] = "call",
118 [BPF_EXIT
>> 4] = "exit",
121 static void print_bpf_end_insn(bpf_insn_print_t verbose
,
123 const struct bpf_insn
*insn
)
125 verbose(private_data
, "(%02x) r%d = %s%d r%d\n",
126 insn
->code
, insn
->dst_reg
,
127 BPF_SRC(insn
->code
) == BPF_TO_BE
? "be" : "le",
128 insn
->imm
, insn
->dst_reg
);
131 void print_bpf_insn(const struct bpf_insn_cbs
*cbs
,
132 const struct bpf_insn
*insn
,
133 bool allow_ptr_leaks
)
135 const bpf_insn_print_t verbose
= cbs
->cb_print
;
136 u8
class = BPF_CLASS(insn
->code
);
138 if (class == BPF_ALU
|| class == BPF_ALU64
) {
139 if (BPF_OP(insn
->code
) == BPF_END
) {
140 if (class == BPF_ALU64
)
141 verbose(cbs
->private_data
, "BUG_alu64_%02x\n", insn
->code
);
143 print_bpf_end_insn(verbose
, cbs
->private_data
, insn
);
144 } else if (BPF_OP(insn
->code
) == BPF_NEG
) {
145 verbose(cbs
->private_data
, "(%02x) %c%d = -%c%d\n",
146 insn
->code
, class == BPF_ALU
? 'w' : 'r',
147 insn
->dst_reg
, class == BPF_ALU
? 'w' : 'r',
149 } else if (BPF_SRC(insn
->code
) == BPF_X
) {
150 verbose(cbs
->private_data
, "(%02x) %c%d %s %c%d\n",
151 insn
->code
, class == BPF_ALU
? 'w' : 'r',
153 bpf_alu_string
[BPF_OP(insn
->code
) >> 4],
154 class == BPF_ALU
? 'w' : 'r',
157 verbose(cbs
->private_data
, "(%02x) %c%d %s %d\n",
158 insn
->code
, class == BPF_ALU
? 'w' : 'r',
160 bpf_alu_string
[BPF_OP(insn
->code
) >> 4],
163 } else if (class == BPF_STX
) {
164 if (BPF_MODE(insn
->code
) == BPF_MEM
)
165 verbose(cbs
->private_data
, "(%02x) *(%s *)(r%d %+d) = r%d\n",
167 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
169 insn
->off
, insn
->src_reg
);
170 else if (BPF_MODE(insn
->code
) == BPF_ATOMIC
&&
171 (insn
->imm
== BPF_ADD
|| insn
->imm
== BPF_AND
||
172 insn
->imm
== BPF_OR
|| insn
->imm
== BPF_XOR
)) {
173 verbose(cbs
->private_data
, "(%02x) lock *(%s *)(r%d %+d) %s r%d\n",
175 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
176 insn
->dst_reg
, insn
->off
,
177 bpf_alu_string
[BPF_OP(insn
->imm
) >> 4],
179 } else if (BPF_MODE(insn
->code
) == BPF_ATOMIC
&&
180 (insn
->imm
== (BPF_ADD
| BPF_FETCH
) ||
181 insn
->imm
== (BPF_AND
| BPF_FETCH
) ||
182 insn
->imm
== (BPF_OR
| BPF_FETCH
) ||
183 insn
->imm
== (BPF_XOR
| BPF_FETCH
))) {
184 verbose(cbs
->private_data
, "(%02x) r%d = atomic%s_fetch_%s((%s *)(r%d %+d), r%d)\n",
185 insn
->code
, insn
->src_reg
,
186 BPF_SIZE(insn
->code
) == BPF_DW
? "64" : "",
187 bpf_atomic_alu_string
[BPF_OP(insn
->imm
) >> 4],
188 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
189 insn
->dst_reg
, insn
->off
, insn
->src_reg
);
190 } else if (BPF_MODE(insn
->code
) == BPF_ATOMIC
&&
191 insn
->imm
== BPF_CMPXCHG
) {
192 verbose(cbs
->private_data
, "(%02x) r0 = atomic%s_cmpxchg((%s *)(r%d %+d), r0, r%d)\n",
194 BPF_SIZE(insn
->code
) == BPF_DW
? "64" : "",
195 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
196 insn
->dst_reg
, insn
->off
,
198 } else if (BPF_MODE(insn
->code
) == BPF_ATOMIC
&&
199 insn
->imm
== BPF_XCHG
) {
200 verbose(cbs
->private_data
, "(%02x) r%d = atomic%s_xchg((%s *)(r%d %+d), r%d)\n",
201 insn
->code
, insn
->src_reg
,
202 BPF_SIZE(insn
->code
) == BPF_DW
? "64" : "",
203 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
204 insn
->dst_reg
, insn
->off
, insn
->src_reg
);
206 verbose(cbs
->private_data
, "BUG_%02x\n", insn
->code
);
208 } else if (class == BPF_ST
) {
209 if (BPF_MODE(insn
->code
) == BPF_MEM
) {
210 verbose(cbs
->private_data
, "(%02x) *(%s *)(r%d %+d) = %d\n",
212 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
214 insn
->off
, insn
->imm
);
215 } else if (BPF_MODE(insn
->code
) == 0xc0 /* BPF_NOSPEC, no UAPI */) {
216 verbose(cbs
->private_data
, "(%02x) nospec\n", insn
->code
);
218 verbose(cbs
->private_data
, "BUG_st_%02x\n", insn
->code
);
220 } else if (class == BPF_LDX
) {
221 if (BPF_MODE(insn
->code
) != BPF_MEM
) {
222 verbose(cbs
->private_data
, "BUG_ldx_%02x\n", insn
->code
);
225 verbose(cbs
->private_data
, "(%02x) r%d = *(%s *)(r%d %+d)\n",
226 insn
->code
, insn
->dst_reg
,
227 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
228 insn
->src_reg
, insn
->off
);
229 } else if (class == BPF_LD
) {
230 if (BPF_MODE(insn
->code
) == BPF_ABS
) {
231 verbose(cbs
->private_data
, "(%02x) r0 = *(%s *)skb[%d]\n",
233 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
235 } else if (BPF_MODE(insn
->code
) == BPF_IND
) {
236 verbose(cbs
->private_data
, "(%02x) r0 = *(%s *)skb[r%d + %d]\n",
238 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
239 insn
->src_reg
, insn
->imm
);
240 } else if (BPF_MODE(insn
->code
) == BPF_IMM
&&
241 BPF_SIZE(insn
->code
) == BPF_DW
) {
242 /* At this point, we already made sure that the second
243 * part of the ldimm64 insn is accessible.
245 u64 imm
= ((u64
)(insn
+ 1)->imm
<< 32) | (u32
)insn
->imm
;
246 bool is_ptr
= insn
->src_reg
== BPF_PSEUDO_MAP_FD
||
247 insn
->src_reg
== BPF_PSEUDO_MAP_VALUE
;
250 if (is_ptr
&& !allow_ptr_leaks
)
253 verbose(cbs
->private_data
, "(%02x) r%d = %s\n",
254 insn
->code
, insn
->dst_reg
,
255 __func_imm_name(cbs
, insn
, imm
,
258 verbose(cbs
->private_data
, "BUG_ld_%02x\n", insn
->code
);
261 } else if (class == BPF_JMP32
|| class == BPF_JMP
) {
262 u8 opcode
= BPF_OP(insn
->code
);
264 if (opcode
== BPF_CALL
) {
267 if (insn
->src_reg
== BPF_PSEUDO_CALL
) {
268 verbose(cbs
->private_data
, "(%02x) call pc%s\n",
270 __func_get_name(cbs
, insn
,
273 strcpy(tmp
, "unknown");
274 verbose(cbs
->private_data
, "(%02x) call %s#%d\n", insn
->code
,
275 __func_get_name(cbs
, insn
,
279 } else if (insn
->code
== (BPF_JMP
| BPF_JA
)) {
280 verbose(cbs
->private_data
, "(%02x) goto pc%+d\n",
281 insn
->code
, insn
->off
);
282 } else if (insn
->code
== (BPF_JMP
| BPF_EXIT
)) {
283 verbose(cbs
->private_data
, "(%02x) exit\n", insn
->code
);
284 } else if (BPF_SRC(insn
->code
) == BPF_X
) {
285 verbose(cbs
->private_data
,
286 "(%02x) if %c%d %s %c%d goto pc%+d\n",
287 insn
->code
, class == BPF_JMP32
? 'w' : 'r',
289 bpf_jmp_string
[BPF_OP(insn
->code
) >> 4],
290 class == BPF_JMP32
? 'w' : 'r',
291 insn
->src_reg
, insn
->off
);
293 verbose(cbs
->private_data
,
294 "(%02x) if %c%d %s 0x%x goto pc%+d\n",
295 insn
->code
, class == BPF_JMP32
? 'w' : 'r',
297 bpf_jmp_string
[BPF_OP(insn
->code
) >> 4],
298 insn
->imm
, insn
->off
);
301 verbose(cbs
->private_data
, "(%02x) %s\n",
302 insn
->code
, bpf_class_string
[class]);