Bundle libseccomp 2.3.1
[linux-seccomp.git] / libseccomp / tools / scmp_bpf_disasm.c
CommitLineData
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 */
47static 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 */
60static 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 */
168static 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 */
203static 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 */
282static 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 */
325static 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 */
418static 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 */
456int 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}
This page took 0.040177 seconds and 4 git commands to generate.