X-Git-Url: http://git.ieval.ro/?p=linux-seccomp.git;a=blobdiff_plain;f=libseccomp%2Ftools%2Fscmp_bpf_disasm.c;fp=libseccomp%2Ftools%2Fscmp_bpf_disasm.c;h=e8e376b0448ae581ddfed202c1ed8ee46100c9e0;hp=0000000000000000000000000000000000000000;hb=8befd5cc4d2b478c697d81a5ac191083c203d081;hpb=bcf524c10c0ad85fcef711acffc3251bb8472352 diff --git a/libseccomp/tools/scmp_bpf_disasm.c b/libseccomp/tools/scmp_bpf_disasm.c new file mode 100644 index 0000000..e8e376b --- /dev/null +++ b/libseccomp/tools/scmp_bpf_disasm.c @@ -0,0 +1,529 @@ +/** + * BPF Disassembler + * + * Copyright (c) 2012 Red Hat + * Author: Paul Moore + */ + +/* + * This library is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License as + * published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bpf.h" +#include "util.h" + +#define _OP_FMT "%-3s" + +/** + * Print the usage information to stderr and exit + * @param program the name of the current program being invoked + * + * Print the usage information and exit with EINVAL. + * + */ +static void exit_usage(const char *program) +{ + fprintf(stderr, "usage: %s -a [-d] [-h]\n", program); + exit(EINVAL); +} + +/** + * Decode the BPF operand + * @param bpf the BPF instruction + * + * Decode the BPF operand and print it to stdout. + * + */ +static const char *bpf_decode_op(const bpf_instr_raw *bpf) +{ + switch (bpf->code) { + case BPF_LD+BPF_W+BPF_IMM: + case BPF_LD+BPF_W+BPF_ABS: + case BPF_LD+BPF_W+BPF_IND: + case BPF_LD+BPF_W+BPF_MEM: + case BPF_LD+BPF_W+BPF_LEN: + case BPF_LD+BPF_W+BPF_MSH: + return "ld"; + case BPF_LD+BPF_H+BPF_IMM: + case BPF_LD+BPF_H+BPF_ABS: + case BPF_LD+BPF_H+BPF_IND: + case BPF_LD+BPF_H+BPF_MEM: + case BPF_LD+BPF_H+BPF_LEN: + case BPF_LD+BPF_H+BPF_MSH: + return "ldh"; + case BPF_LD+BPF_B+BPF_IMM: + case BPF_LD+BPF_B+BPF_ABS: + case BPF_LD+BPF_B+BPF_IND: + case BPF_LD+BPF_B+BPF_MEM: + case BPF_LD+BPF_B+BPF_LEN: + case BPF_LD+BPF_B+BPF_MSH: + return "ldb"; + case BPF_LDX+BPF_W+BPF_IMM: + case BPF_LDX+BPF_W+BPF_ABS: + case BPF_LDX+BPF_W+BPF_IND: + case BPF_LDX+BPF_W+BPF_MEM: + case BPF_LDX+BPF_W+BPF_LEN: + case BPF_LDX+BPF_W+BPF_MSH: + case BPF_LDX+BPF_H+BPF_IMM: + case BPF_LDX+BPF_H+BPF_ABS: + case BPF_LDX+BPF_H+BPF_IND: + case BPF_LDX+BPF_H+BPF_MEM: + case BPF_LDX+BPF_H+BPF_LEN: + case BPF_LDX+BPF_H+BPF_MSH: + case BPF_LDX+BPF_B+BPF_IMM: + case BPF_LDX+BPF_B+BPF_ABS: + case BPF_LDX+BPF_B+BPF_IND: + case BPF_LDX+BPF_B+BPF_MEM: + case BPF_LDX+BPF_B+BPF_LEN: + case BPF_LDX+BPF_B+BPF_MSH: + return "ldx"; + case BPF_ST: + return "st"; + case BPF_STX: + return "stx"; + case BPF_ALU+BPF_ADD+BPF_K: + case BPF_ALU+BPF_ADD+BPF_X: + return "add"; + case BPF_ALU+BPF_SUB+BPF_K: + case BPF_ALU+BPF_SUB+BPF_X: + return "sub"; + case BPF_ALU+BPF_MUL+BPF_K: + case BPF_ALU+BPF_MUL+BPF_X: + return "mul"; + case BPF_ALU+BPF_DIV+BPF_K: + case BPF_ALU+BPF_DIV+BPF_X: + return "div"; + case BPF_ALU+BPF_OR+BPF_K: + case BPF_ALU+BPF_OR+BPF_X: + return "or"; + case BPF_ALU+BPF_AND+BPF_K: + case BPF_ALU+BPF_AND+BPF_X: + return "and"; + case BPF_ALU+BPF_LSH+BPF_K: + case BPF_ALU+BPF_LSH+BPF_X: + return "lsh"; + case BPF_ALU+BPF_RSH+BPF_K: + case BPF_ALU+BPF_RSH+BPF_X: + return "rsh"; + case BPF_ALU+BPF_NEG+BPF_K: + case BPF_ALU+BPF_NEG+BPF_X: + return "neg"; + case BPF_JMP+BPF_JA+BPF_K: + case BPF_JMP+BPF_JA+BPF_X: + return "jmp"; + case BPF_JMP+BPF_JEQ+BPF_K: + case BPF_JMP+BPF_JEQ+BPF_X: + return "jeq"; + case BPF_JMP+BPF_JGT+BPF_K: + case BPF_JMP+BPF_JGT+BPF_X: + return "jgt"; + case BPF_JMP+BPF_JGE+BPF_K: + case BPF_JMP+BPF_JGE+BPF_X: + return "jge"; + case BPF_JMP+BPF_JSET+BPF_K: + case BPF_JMP+BPF_JSET+BPF_X: + return "jset"; + case BPF_RET+BPF_K: + case BPF_RET+BPF_X: + case BPF_RET+BPF_A: + return "ret"; + case BPF_MISC+BPF_TAX: + return "tax"; + case BPF_MISC+BPF_TXA: + return "txa"; + } + return "???"; +} + +/** + * Decode a RET action + * @param k the return action + * + * Decode the action and print it to stdout. + * + */ +static void bpf_decode_action(uint32_t k) +{ + uint32_t act = k & SECCOMP_RET_ACTION; + uint32_t data = k & SECCOMP_RET_DATA; + + switch (act) { + case SECCOMP_RET_KILL: + printf("KILL"); + break; + case SECCOMP_RET_TRAP: + printf("TRAP"); + break; + case SECCOMP_RET_ERRNO: + printf("ERRNO(%u)", data); + break; + case SECCOMP_RET_TRACE: + printf("TRACE(%u)", data); + break; + case SECCOMP_RET_ALLOW: + printf("ALLOW"); + break; + default: + printf("0x%.8x", k); + } +} + +/** + * Decode the BPF arguments (JT, JF, and K) + * @param bpf the BPF instruction + * @param line the current line number + * + * Decode the BPF arguments (JT, JF, and K) and print the relevant information + * to stdout based on the operand. + * + */ +static void bpf_decode_args(const bpf_instr_raw *bpf, unsigned int line) +{ + switch (BPF_CLASS(bpf->code)) { + case BPF_LD: + case BPF_LDX: + switch (BPF_MODE(bpf->code)) { + case BPF_ABS: + printf("$data[%u]", bpf->k); + break; + case BPF_MEM: + printf("$temp[%u]", bpf->k); + break; + case BPF_IMM: + printf("%u", bpf->k); + break; + case BPF_IND: + printf("$data[X + %u]", bpf->k); + break; + case BPF_LEN: + printf("len($data)"); + break; + case BPF_MSH: + printf("4 * $data[%u] & 0x0f", bpf->k); + break; + } + break; + case BPF_ST: + case BPF_STX: + printf("$temp[%u]", bpf->k); + break; + case BPF_ALU: + if (BPF_SRC(bpf->code) == BPF_K) { + switch (BPF_OP(bpf->code)) { + case BPF_OR: + case BPF_AND: + printf("0x%.8x", bpf->k); + break; + default: + printf("%u", bpf->k); + } + } else + printf("%u", bpf->k); + break; + case BPF_JMP: + if (BPF_OP(bpf->code) == BPF_JA) { + printf("%.4u", (line + 1) + bpf->k); + } else { + printf("%-4u true:%.4u false:%.4u", + bpf->k, + (line + 1) + bpf->jt, + (line + 1) + bpf->jf); + } + break; + case BPF_RET: + if (BPF_RVAL(bpf->code) == BPF_A) { + /* XXX - accumulator? */ + printf("$acc"); + } else if (BPF_SRC(bpf->code) == BPF_K) { + bpf_decode_action(bpf->k); + } else if (BPF_SRC(bpf->code) == BPF_X) { + /* XXX - any idea? */ + printf("???"); + } + break; + case BPF_MISC: + break; + default: + printf("???"); + } +} + +/** + * Perform a simple decoding of the BPF program + * @param file the BPF program + * + * Read the BPF program and display the instructions. Returns zero on success, + * negative values on failure. + * + */ +static int bpf_decode(FILE *file) +{ + unsigned int line = 0; + size_t len; + bpf_instr_raw bpf; + + /* header */ + printf(" line OP JT JF K\n"); + printf("=================================\n"); + + while ((len = fread(&bpf, sizeof(bpf), 1, file))) { + /* convert the bpf statement */ + bpf.code = ttoh16(arch, bpf.code); + bpf.k = ttoh32(arch, bpf.k); + + /* display a hex dump */ + printf(" %.4u: 0x%.2x 0x%.2x 0x%.2x 0x%.8x", + line, bpf.code, bpf.jt, bpf.jf, bpf.k); + + /* display the assembler statements */ + printf(" "); + printf(_OP_FMT, bpf_decode_op(&bpf)); + printf(" "); + bpf_decode_args(&bpf, line); + printf("\n"); + + line++; + } + + if (ferror(file)) + return errno; + return 0; +} + +/** + * Decode the BPF arguments (JT, JF, and K) + * @param bpf the BPF instruction + * @param line the current line number + * + * Decode the BPF arguments (JT, JF, and K) and print the relevant information + * to stdout based on the operand. + * + */ +static void bpf_dot_decode_args(const bpf_instr_raw *bpf, unsigned int line) +{ + const char *op = bpf_decode_op(bpf); + + printf("\tline%d[label=\"%s", line, op); + switch (BPF_CLASS(bpf->code)) { + case BPF_LD: + case BPF_LDX: + switch (BPF_MODE(bpf->code)) { + case BPF_ABS: + printf(" $data[%u]\",shape=parallelogram]\n", bpf->k); + break; + case BPF_MEM: + printf(" $temp[%u]\",shape=parallelogram]\n", bpf->k); + break; + case BPF_IMM: + printf(" %u\",shape=parallelogram]\n", bpf->k); + break; + case BPF_IND: + printf(" $data[X + %u]\",shape=parallelogram]\n", bpf->k); + break; + case BPF_LEN: + printf(" len($data)\",shape=parallelogram]\n"); + break; + case BPF_MSH: + printf(" 4 * $data[%u] & 0x0f\",shape=parallelogram]\n", bpf->k); + break; + } + break; + case BPF_ST: + case BPF_STX: + printf(" $temp[%u]\",shape=parallelogram]\n", + bpf->k); + break; + case BPF_ALU: + if (BPF_SRC(bpf->code) == BPF_K) { + switch (BPF_OP(bpf->code)) { + case BPF_OR: + case BPF_AND: + printf(" 0x%.8x\",shape=rectangle]\n", bpf->k); + break; + default: + printf(" %u\",shape=rectangle]\n", bpf->k); + } + } else + printf(" %u\",shape=rectangle]\n", bpf->k); + break; + case BPF_JMP: + if (BPF_OP(bpf->code) == BPF_JA) { + printf("\",shape=hexagon]\n"); + printf("\tline%d -> line%d\n", + line, (line + 1) + bpf->k); + } else { + printf(" %-4u", bpf->k); + /* Heuristic: if k > 256, also emit hex version */ + if (bpf->k > 256) + printf("\\n(0x%.8x)", bpf->k); + printf("\",shape=diamond]\n"); + printf("\tline%d -> line%d [label=\"true\"]\n", + line, (line + 1) + bpf->jt); + printf("\tline%d -> line%d [label=\"false\"]\n", + line, (line + 1) + bpf->jf); + } + break; + case BPF_RET: + if (BPF_RVAL(bpf->code) == BPF_A) { + /* XXX - accumulator? */ + printf(" $acc\", shape=\"box\", style=rounded]\n"); + } else if (BPF_SRC(bpf->code) == BPF_K) { + printf(" "); + bpf_decode_action(bpf->k); + printf("\", shape=\"box\", style=rounded]\n"); + } else if (BPF_SRC(bpf->code) == BPF_X) { + /* XXX - any idea? */ + printf(" ???\", shape=\"box\", style=rounded]\n"); + } + break; + case BPF_MISC: + printf("\"]\n"); + break; + default: + printf(" ???\"]\n"); + } +} + +/** + * Perform a simple decoding of the BPF program to a dot graph + * @param file the BPF program + * + * Read the BPF program and display the instructions. Returns zero on success, + * negative values on failure. + * + */ +static int bpf_dot_decode(FILE *file) +{ + unsigned int line = 0; + size_t len; + bpf_instr_raw bpf; + int prev_class = 0; + + /* header */ + printf("digraph {\n"); + printf("\tstart[shape=\"box\", style=rounded];\n"); + + while ((len = fread(&bpf, sizeof(bpf), 1, file))) { + /* convert the bpf statement */ + bpf.code = ttoh16(arch, bpf.code); + bpf.k = ttoh32(arch, bpf.k); + + /* display the statement */ + bpf_dot_decode_args(&bpf, line); + + /* if previous line wasn't RET/JMP, link it to this line */ + if (line == 0) + printf("\tstart -> line%d\n", line); + else if ((prev_class != BPF_JMP) && (prev_class != BPF_RET)) + printf("\tline%d -> line%d\n", line - 1, line); + prev_class = BPF_CLASS(bpf.code); + + line++; + } + printf("}\n"); + + if (ferror(file)) + return errno; + return 0; +} + +/** + * main + */ +int main(int argc, char *argv[]) +{ + int rc; + int opt; + bool dot_out = false; + FILE *file; + + /* parse the command line */ + while ((opt = getopt(argc, argv, "a:dh")) > 0) { + switch (opt) { + case 'a': + if (strcmp(optarg, "x86") == 0) + arch = AUDIT_ARCH_I386; + else if (strcmp(optarg, "x86_64") == 0) + arch = AUDIT_ARCH_X86_64; + else if (strcmp(optarg, "x32") == 0) + arch = AUDIT_ARCH_X86_64; + else if (strcmp(optarg, "arm") == 0) + arch = AUDIT_ARCH_ARM; + else if (strcmp(optarg, "aarch64") == 0) + arch = AUDIT_ARCH_AARCH64; + else if (strcmp(optarg, "mips") == 0) + arch = AUDIT_ARCH_MIPS; + else if (strcmp(optarg, "mipsel") == 0) + arch = AUDIT_ARCH_MIPSEL; + else if (strcmp(optarg, "mips64") == 0) + arch = AUDIT_ARCH_MIPS64; + else if (strcmp(optarg, "mipsel64") == 0) + arch = AUDIT_ARCH_MIPSEL64; + else if (strcmp(optarg, "mips64n32") == 0) + arch = AUDIT_ARCH_MIPS64N32; + else if (strcmp(optarg, "mipsel64n32") == 0) + arch = AUDIT_ARCH_MIPSEL64N32; + else if (strcmp(optarg, "ppc64") == 0) + arch = AUDIT_ARCH_PPC64; + else if (strcmp(optarg, "ppc64le") == 0) + arch = AUDIT_ARCH_PPC64LE; + else if (strcmp(optarg, "ppc") == 0) + arch = AUDIT_ARCH_PPC; + else if (strcmp(optarg, "s390") == 0) + arch = AUDIT_ARCH_S390; + else if (strcmp(optarg, "s390x") == 0) + arch = AUDIT_ARCH_S390X; + else + exit_usage(argv[0]); + break; + case 'd': + dot_out = true; + break; + default: + /* usage information */ + exit_usage(argv[0]); + } + } + + if ((optind > 1) && (optind < argc)) { + int opt_file = optind - 1 ; + file = fopen(argv[opt_file], "r"); + if (file == NULL) { + fprintf(stderr, "error: unable to open \"%s\" (%s)\n", + argv[opt_file], strerror(errno)); + return errno; + } + } else + file = stdin; + + if (dot_out) + rc = bpf_dot_decode(file); + else + rc = bpf_decode(file); + fclose(file); + + return rc; +}