Commit | Line | Data |
---|---|---|
8befd5cc MG |
1 | /** |
2 | * BPF Disassembler | |
3 | * | |
4 | * Copyright (c) 2012 Red Hat <pmoore@redhat.com> | |
5 | * Author: Paul Moore <paul@paul-moore.com> | |
6 | */ | |
7 | ||
8 | /* | |
9 | * This library is free software; you can redistribute it and/or modify it | |
10 | * under the terms of version 2.1 of the GNU Lesser General Public License as | |
11 | * published by the Free Software Foundation. | |
12 | * | |
13 | * This library is distributed in the hope that it will be useful, but WITHOUT | |
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License | |
16 | * for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU Lesser General Public License | |
19 | * along with this library; if not, see <http://www.gnu.org/licenses>. | |
20 | */ | |
21 | ||
22 | #include <errno.h> | |
23 | #include <fcntl.h> | |
24 | #include <inttypes.h> | |
25 | #include <limits.h> | |
26 | #include <stdlib.h> | |
27 | #include <stdbool.h> | |
28 | #include <stdio.h> | |
29 | #include <string.h> | |
30 | #include <unistd.h> | |
31 | #include <linux/audit.h> | |
32 | #include <sys/types.h> | |
33 | #include <sys/stat.h> | |
34 | ||
35 | #include "bpf.h" | |
36 | #include "util.h" | |
37 | ||
38 | #define _OP_FMT "%-3s" | |
39 | ||
40 | /** | |
41 | * Print the usage information to stderr and exit | |
42 | * @param program the name of the current program being invoked | |
43 | * | |
44 | * Print the usage information and exit with EINVAL. | |
45 | * | |
46 | */ | |
47 | static void exit_usage(const char *program) | |
48 | { | |
49 | fprintf(stderr, "usage: %s -a <arch> [-d] [-h]\n", program); | |
50 | exit(EINVAL); | |
51 | } | |
52 | ||
53 | /** | |
54 | * Decode the BPF operand | |
55 | * @param bpf the BPF instruction | |
56 | * | |
57 | * Decode the BPF operand and print it to stdout. | |
58 | * | |
59 | */ | |
60 | static const char *bpf_decode_op(const bpf_instr_raw *bpf) | |
61 | { | |
62 | switch (bpf->code) { | |
63 | case BPF_LD+BPF_W+BPF_IMM: | |
64 | case BPF_LD+BPF_W+BPF_ABS: | |
65 | case BPF_LD+BPF_W+BPF_IND: | |
66 | case BPF_LD+BPF_W+BPF_MEM: | |
67 | case BPF_LD+BPF_W+BPF_LEN: | |
68 | case BPF_LD+BPF_W+BPF_MSH: | |
69 | return "ld"; | |
70 | case BPF_LD+BPF_H+BPF_IMM: | |
71 | case BPF_LD+BPF_H+BPF_ABS: | |
72 | case BPF_LD+BPF_H+BPF_IND: | |
73 | case BPF_LD+BPF_H+BPF_MEM: | |
74 | case BPF_LD+BPF_H+BPF_LEN: | |
75 | case BPF_LD+BPF_H+BPF_MSH: | |
76 | return "ldh"; | |
77 | case BPF_LD+BPF_B+BPF_IMM: | |
78 | case BPF_LD+BPF_B+BPF_ABS: | |
79 | case BPF_LD+BPF_B+BPF_IND: | |
80 | case BPF_LD+BPF_B+BPF_MEM: | |
81 | case BPF_LD+BPF_B+BPF_LEN: | |
82 | case BPF_LD+BPF_B+BPF_MSH: | |
83 | return "ldb"; | |
84 | case BPF_LDX+BPF_W+BPF_IMM: | |
85 | case BPF_LDX+BPF_W+BPF_ABS: | |
86 | case BPF_LDX+BPF_W+BPF_IND: | |
87 | case BPF_LDX+BPF_W+BPF_MEM: | |
88 | case BPF_LDX+BPF_W+BPF_LEN: | |
89 | case BPF_LDX+BPF_W+BPF_MSH: | |
90 | case BPF_LDX+BPF_H+BPF_IMM: | |
91 | case BPF_LDX+BPF_H+BPF_ABS: | |
92 | case BPF_LDX+BPF_H+BPF_IND: | |
93 | case BPF_LDX+BPF_H+BPF_MEM: | |
94 | case BPF_LDX+BPF_H+BPF_LEN: | |
95 | case BPF_LDX+BPF_H+BPF_MSH: | |
96 | case BPF_LDX+BPF_B+BPF_IMM: | |
97 | case BPF_LDX+BPF_B+BPF_ABS: | |
98 | case BPF_LDX+BPF_B+BPF_IND: | |
99 | case BPF_LDX+BPF_B+BPF_MEM: | |
100 | case BPF_LDX+BPF_B+BPF_LEN: | |
101 | case BPF_LDX+BPF_B+BPF_MSH: | |
102 | return "ldx"; | |
103 | case BPF_ST: | |
104 | return "st"; | |
105 | case BPF_STX: | |
106 | return "stx"; | |
107 | case BPF_ALU+BPF_ADD+BPF_K: | |
108 | case BPF_ALU+BPF_ADD+BPF_X: | |
109 | return "add"; | |
110 | case BPF_ALU+BPF_SUB+BPF_K: | |
111 | case BPF_ALU+BPF_SUB+BPF_X: | |
112 | return "sub"; | |
113 | case BPF_ALU+BPF_MUL+BPF_K: | |
114 | case BPF_ALU+BPF_MUL+BPF_X: | |
115 | return "mul"; | |
116 | case BPF_ALU+BPF_DIV+BPF_K: | |
117 | case BPF_ALU+BPF_DIV+BPF_X: | |
118 | return "div"; | |
119 | case BPF_ALU+BPF_OR+BPF_K: | |
120 | case BPF_ALU+BPF_OR+BPF_X: | |
121 | return "or"; | |
122 | case BPF_ALU+BPF_AND+BPF_K: | |
123 | case BPF_ALU+BPF_AND+BPF_X: | |
124 | return "and"; | |
125 | case BPF_ALU+BPF_LSH+BPF_K: | |
126 | case BPF_ALU+BPF_LSH+BPF_X: | |
127 | return "lsh"; | |
128 | case BPF_ALU+BPF_RSH+BPF_K: | |
129 | case BPF_ALU+BPF_RSH+BPF_X: | |
130 | return "rsh"; | |
131 | case BPF_ALU+BPF_NEG+BPF_K: | |
132 | case BPF_ALU+BPF_NEG+BPF_X: | |
133 | return "neg"; | |
134 | case BPF_JMP+BPF_JA+BPF_K: | |
135 | case BPF_JMP+BPF_JA+BPF_X: | |
136 | return "jmp"; | |
137 | case BPF_JMP+BPF_JEQ+BPF_K: | |
138 | case BPF_JMP+BPF_JEQ+BPF_X: | |
139 | return "jeq"; | |
140 | case BPF_JMP+BPF_JGT+BPF_K: | |
141 | case BPF_JMP+BPF_JGT+BPF_X: | |
142 | return "jgt"; | |
143 | case BPF_JMP+BPF_JGE+BPF_K: | |
144 | case BPF_JMP+BPF_JGE+BPF_X: | |
145 | return "jge"; | |
146 | case BPF_JMP+BPF_JSET+BPF_K: | |
147 | case BPF_JMP+BPF_JSET+BPF_X: | |
148 | return "jset"; | |
149 | case BPF_RET+BPF_K: | |
150 | case BPF_RET+BPF_X: | |
151 | case BPF_RET+BPF_A: | |
152 | return "ret"; | |
153 | case BPF_MISC+BPF_TAX: | |
154 | return "tax"; | |
155 | case BPF_MISC+BPF_TXA: | |
156 | return "txa"; | |
157 | } | |
158 | return "???"; | |
159 | } | |
160 | ||
161 | /** | |
162 | * Decode a RET action | |
163 | * @param k the return action | |
164 | * | |
165 | * Decode the action and print it to stdout. | |
166 | * | |
167 | */ | |
168 | static void bpf_decode_action(uint32_t k) | |
169 | { | |
170 | uint32_t act = k & SECCOMP_RET_ACTION; | |
171 | uint32_t data = k & SECCOMP_RET_DATA; | |
172 | ||
173 | switch (act) { | |
174 | case SECCOMP_RET_KILL: | |
175 | printf("KILL"); | |
176 | break; | |
177 | case SECCOMP_RET_TRAP: | |
178 | printf("TRAP"); | |
179 | break; | |
180 | case SECCOMP_RET_ERRNO: | |
181 | printf("ERRNO(%u)", data); | |
182 | break; | |
183 | case SECCOMP_RET_TRACE: | |
184 | printf("TRACE(%u)", data); | |
185 | break; | |
186 | case SECCOMP_RET_ALLOW: | |
187 | printf("ALLOW"); | |
188 | break; | |
189 | default: | |
190 | printf("0x%.8x", k); | |
191 | } | |
192 | } | |
193 | ||
194 | /** | |
195 | * Decode the BPF arguments (JT, JF, and K) | |
196 | * @param bpf the BPF instruction | |
197 | * @param line the current line number | |
198 | * | |
199 | * Decode the BPF arguments (JT, JF, and K) and print the relevant information | |
200 | * to stdout based on the operand. | |
201 | * | |
202 | */ | |
203 | static void bpf_decode_args(const bpf_instr_raw *bpf, unsigned int line) | |
204 | { | |
205 | switch (BPF_CLASS(bpf->code)) { | |
206 | case BPF_LD: | |
207 | case BPF_LDX: | |
208 | switch (BPF_MODE(bpf->code)) { | |
209 | case BPF_ABS: | |
210 | printf("$data[%u]", bpf->k); | |
211 | break; | |
212 | case BPF_MEM: | |
213 | printf("$temp[%u]", bpf->k); | |
214 | break; | |
215 | case BPF_IMM: | |
216 | printf("%u", bpf->k); | |
217 | break; | |
218 | case BPF_IND: | |
219 | printf("$data[X + %u]", bpf->k); | |
220 | break; | |
221 | case BPF_LEN: | |
222 | printf("len($data)"); | |
223 | break; | |
224 | case BPF_MSH: | |
225 | printf("4 * $data[%u] & 0x0f", bpf->k); | |
226 | break; | |
227 | } | |
228 | break; | |
229 | case BPF_ST: | |
230 | case BPF_STX: | |
231 | printf("$temp[%u]", bpf->k); | |
232 | break; | |
233 | case BPF_ALU: | |
234 | if (BPF_SRC(bpf->code) == BPF_K) { | |
235 | switch (BPF_OP(bpf->code)) { | |
236 | case BPF_OR: | |
237 | case BPF_AND: | |
238 | printf("0x%.8x", bpf->k); | |
239 | break; | |
240 | default: | |
241 | printf("%u", bpf->k); | |
242 | } | |
243 | } else | |
244 | printf("%u", bpf->k); | |
245 | break; | |
246 | case BPF_JMP: | |
247 | if (BPF_OP(bpf->code) == BPF_JA) { | |
248 | printf("%.4u", (line + 1) + bpf->k); | |
249 | } else { | |
250 | printf("%-4u true:%.4u false:%.4u", | |
251 | bpf->k, | |
252 | (line + 1) + bpf->jt, | |
253 | (line + 1) + bpf->jf); | |
254 | } | |
255 | break; | |
256 | case BPF_RET: | |
257 | if (BPF_RVAL(bpf->code) == BPF_A) { | |
258 | /* XXX - accumulator? */ | |
259 | printf("$acc"); | |
260 | } else if (BPF_SRC(bpf->code) == BPF_K) { | |
261 | bpf_decode_action(bpf->k); | |
262 | } else if (BPF_SRC(bpf->code) == BPF_X) { | |
263 | /* XXX - any idea? */ | |
264 | printf("???"); | |
265 | } | |
266 | break; | |
267 | case BPF_MISC: | |
268 | break; | |
269 | default: | |
270 | printf("???"); | |
271 | } | |
272 | } | |
273 | ||
274 | /** | |
275 | * Perform a simple decoding of the BPF program | |
276 | * @param file the BPF program | |
277 | * | |
278 | * Read the BPF program and display the instructions. Returns zero on success, | |
279 | * negative values on failure. | |
280 | * | |
281 | */ | |
282 | static int bpf_decode(FILE *file) | |
283 | { | |
284 | unsigned int line = 0; | |
285 | size_t len; | |
286 | bpf_instr_raw bpf; | |
287 | ||
288 | /* header */ | |
289 | printf(" line OP JT JF K\n"); | |
290 | printf("=================================\n"); | |
291 | ||
292 | while ((len = fread(&bpf, sizeof(bpf), 1, file))) { | |
293 | /* convert the bpf statement */ | |
294 | bpf.code = ttoh16(arch, bpf.code); | |
295 | bpf.k = ttoh32(arch, bpf.k); | |
296 | ||
297 | /* display a hex dump */ | |
298 | printf(" %.4u: 0x%.2x 0x%.2x 0x%.2x 0x%.8x", | |
299 | line, bpf.code, bpf.jt, bpf.jf, bpf.k); | |
300 | ||
301 | /* display the assembler statements */ | |
302 | printf(" "); | |
303 | printf(_OP_FMT, bpf_decode_op(&bpf)); | |
304 | printf(" "); | |
305 | bpf_decode_args(&bpf, line); | |
306 | printf("\n"); | |
307 | ||
308 | line++; | |
309 | } | |
310 | ||
311 | if (ferror(file)) | |
312 | return errno; | |
313 | return 0; | |
314 | } | |
315 | ||
316 | /** | |
317 | * Decode the BPF arguments (JT, JF, and K) | |
318 | * @param bpf the BPF instruction | |
319 | * @param line the current line number | |
320 | * | |
321 | * Decode the BPF arguments (JT, JF, and K) and print the relevant information | |
322 | * to stdout based on the operand. | |
323 | * | |
324 | */ | |
325 | static void bpf_dot_decode_args(const bpf_instr_raw *bpf, unsigned int line) | |
326 | { | |
327 | const char *op = bpf_decode_op(bpf); | |
328 | ||
329 | printf("\tline%d[label=\"%s", line, op); | |
330 | switch (BPF_CLASS(bpf->code)) { | |
331 | case BPF_LD: | |
332 | case BPF_LDX: | |
333 | switch (BPF_MODE(bpf->code)) { | |
334 | case BPF_ABS: | |
335 | printf(" $data[%u]\",shape=parallelogram]\n", bpf->k); | |
336 | break; | |
337 | case BPF_MEM: | |
338 | printf(" $temp[%u]\",shape=parallelogram]\n", bpf->k); | |
339 | break; | |
340 | case BPF_IMM: | |
341 | printf(" %u\",shape=parallelogram]\n", bpf->k); | |
342 | break; | |
343 | case BPF_IND: | |
344 | printf(" $data[X + %u]\",shape=parallelogram]\n", bpf->k); | |
345 | break; | |
346 | case BPF_LEN: | |
347 | printf(" len($data)\",shape=parallelogram]\n"); | |
348 | break; | |
349 | case BPF_MSH: | |
350 | printf(" 4 * $data[%u] & 0x0f\",shape=parallelogram]\n", bpf->k); | |
351 | break; | |
352 | } | |
353 | break; | |
354 | case BPF_ST: | |
355 | case BPF_STX: | |
356 | printf(" $temp[%u]\",shape=parallelogram]\n", | |
357 | bpf->k); | |
358 | break; | |
359 | case BPF_ALU: | |
360 | if (BPF_SRC(bpf->code) == BPF_K) { | |
361 | switch (BPF_OP(bpf->code)) { | |
362 | case BPF_OR: | |
363 | case BPF_AND: | |
364 | printf(" 0x%.8x\",shape=rectangle]\n", bpf->k); | |
365 | break; | |
366 | default: | |
367 | printf(" %u\",shape=rectangle]\n", bpf->k); | |
368 | } | |
369 | } else | |
370 | printf(" %u\",shape=rectangle]\n", bpf->k); | |
371 | break; | |
372 | case BPF_JMP: | |
373 | if (BPF_OP(bpf->code) == BPF_JA) { | |
374 | printf("\",shape=hexagon]\n"); | |
375 | printf("\tline%d -> line%d\n", | |
376 | line, (line + 1) + bpf->k); | |
377 | } else { | |
378 | printf(" %-4u", bpf->k); | |
379 | /* Heuristic: if k > 256, also emit hex version */ | |
380 | if (bpf->k > 256) | |
381 | printf("\\n(0x%.8x)", bpf->k); | |
382 | printf("\",shape=diamond]\n"); | |
383 | printf("\tline%d -> line%d [label=\"true\"]\n", | |
384 | line, (line + 1) + bpf->jt); | |
385 | printf("\tline%d -> line%d [label=\"false\"]\n", | |
386 | line, (line + 1) + bpf->jf); | |
387 | } | |
388 | break; | |
389 | case BPF_RET: | |
390 | if (BPF_RVAL(bpf->code) == BPF_A) { | |
391 | /* XXX - accumulator? */ | |
392 | printf(" $acc\", shape=\"box\", style=rounded]\n"); | |
393 | } else if (BPF_SRC(bpf->code) == BPF_K) { | |
394 | printf(" "); | |
395 | bpf_decode_action(bpf->k); | |
396 | printf("\", shape=\"box\", style=rounded]\n"); | |
397 | } else if (BPF_SRC(bpf->code) == BPF_X) { | |
398 | /* XXX - any idea? */ | |
399 | printf(" ???\", shape=\"box\", style=rounded]\n"); | |
400 | } | |
401 | break; | |
402 | case BPF_MISC: | |
403 | printf("\"]\n"); | |
404 | break; | |
405 | default: | |
406 | printf(" ???\"]\n"); | |
407 | } | |
408 | } | |
409 | ||
410 | /** | |
411 | * Perform a simple decoding of the BPF program to a dot graph | |
412 | * @param file the BPF program | |
413 | * | |
414 | * Read the BPF program and display the instructions. Returns zero on success, | |
415 | * negative values on failure. | |
416 | * | |
417 | */ | |
418 | static int bpf_dot_decode(FILE *file) | |
419 | { | |
420 | unsigned int line = 0; | |
421 | size_t len; | |
422 | bpf_instr_raw bpf; | |
423 | int prev_class = 0; | |
424 | ||
425 | /* header */ | |
426 | printf("digraph {\n"); | |
427 | printf("\tstart[shape=\"box\", style=rounded];\n"); | |
428 | ||
429 | while ((len = fread(&bpf, sizeof(bpf), 1, file))) { | |
430 | /* convert the bpf statement */ | |
431 | bpf.code = ttoh16(arch, bpf.code); | |
432 | bpf.k = ttoh32(arch, bpf.k); | |
433 | ||
434 | /* display the statement */ | |
435 | bpf_dot_decode_args(&bpf, line); | |
436 | ||
437 | /* if previous line wasn't RET/JMP, link it to this line */ | |
438 | if (line == 0) | |
439 | printf("\tstart -> line%d\n", line); | |
440 | else if ((prev_class != BPF_JMP) && (prev_class != BPF_RET)) | |
441 | printf("\tline%d -> line%d\n", line - 1, line); | |
442 | prev_class = BPF_CLASS(bpf.code); | |
443 | ||
444 | line++; | |
445 | } | |
446 | printf("}\n"); | |
447 | ||
448 | if (ferror(file)) | |
449 | return errno; | |
450 | return 0; | |
451 | } | |
452 | ||
453 | /** | |
454 | * main | |
455 | */ | |
456 | int main(int argc, char *argv[]) | |
457 | { | |
458 | int rc; | |
459 | int opt; | |
460 | bool dot_out = false; | |
461 | FILE *file; | |
462 | ||
463 | /* parse the command line */ | |
464 | while ((opt = getopt(argc, argv, "a:dh")) > 0) { | |
465 | switch (opt) { | |
466 | case 'a': | |
467 | if (strcmp(optarg, "x86") == 0) | |
468 | arch = AUDIT_ARCH_I386; | |
469 | else if (strcmp(optarg, "x86_64") == 0) | |
470 | arch = AUDIT_ARCH_X86_64; | |
471 | else if (strcmp(optarg, "x32") == 0) | |
472 | arch = AUDIT_ARCH_X86_64; | |
473 | else if (strcmp(optarg, "arm") == 0) | |
474 | arch = AUDIT_ARCH_ARM; | |
475 | else if (strcmp(optarg, "aarch64") == 0) | |
476 | arch = AUDIT_ARCH_AARCH64; | |
477 | else if (strcmp(optarg, "mips") == 0) | |
478 | arch = AUDIT_ARCH_MIPS; | |
479 | else if (strcmp(optarg, "mipsel") == 0) | |
480 | arch = AUDIT_ARCH_MIPSEL; | |
481 | else if (strcmp(optarg, "mips64") == 0) | |
482 | arch = AUDIT_ARCH_MIPS64; | |
483 | else if (strcmp(optarg, "mipsel64") == 0) | |
484 | arch = AUDIT_ARCH_MIPSEL64; | |
485 | else if (strcmp(optarg, "mips64n32") == 0) | |
486 | arch = AUDIT_ARCH_MIPS64N32; | |
487 | else if (strcmp(optarg, "mipsel64n32") == 0) | |
488 | arch = AUDIT_ARCH_MIPSEL64N32; | |
489 | else if (strcmp(optarg, "ppc64") == 0) | |
490 | arch = AUDIT_ARCH_PPC64; | |
491 | else if (strcmp(optarg, "ppc64le") == 0) | |
492 | arch = AUDIT_ARCH_PPC64LE; | |
493 | else if (strcmp(optarg, "ppc") == 0) | |
494 | arch = AUDIT_ARCH_PPC; | |
495 | else if (strcmp(optarg, "s390") == 0) | |
496 | arch = AUDIT_ARCH_S390; | |
497 | else if (strcmp(optarg, "s390x") == 0) | |
498 | arch = AUDIT_ARCH_S390X; | |
499 | else | |
500 | exit_usage(argv[0]); | |
501 | break; | |
502 | case 'd': | |
503 | dot_out = true; | |
504 | break; | |
505 | default: | |
506 | /* usage information */ | |
507 | exit_usage(argv[0]); | |
508 | } | |
509 | } | |
510 | ||
511 | if ((optind > 1) && (optind < argc)) { | |
512 | int opt_file = optind - 1 ; | |
513 | file = fopen(argv[opt_file], "r"); | |
514 | if (file == NULL) { | |
515 | fprintf(stderr, "error: unable to open \"%s\" (%s)\n", | |
516 | argv[opt_file], strerror(errno)); | |
517 | return errno; | |
518 | } | |
519 | } else | |
520 | file = stdin; | |
521 | ||
522 | if (dot_out) | |
523 | rc = bpf_dot_decode(file); | |
524 | else | |
525 | rc = bpf_decode(file); | |
526 | fclose(file); | |
527 | ||
528 | return rc; | |
529 | } |