summaryrefslogtreecommitdiffstats
path: root/tools/scmp_bpf_disasm.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/scmp_bpf_disasm.c')
-rw-r--r--tools/scmp_bpf_disasm.c541
1 files changed, 541 insertions, 0 deletions
diff --git a/tools/scmp_bpf_disasm.c b/tools/scmp_bpf_disasm.c
new file mode 100644
index 0000000..b682de7
--- /dev/null
+++ b/tools/scmp_bpf_disasm.c
@@ -0,0 +1,541 @@
+/**
+ * BPF Disassembler
+ *
+ * Copyright (c) 2012 Red Hat <pmoore@redhat.com>
+ * Author: Paul Moore <paul@paul-moore.com>
+ */
+
+/*
+ * 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 <http://www.gnu.org/licenses>.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/audit.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#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 <arch> [-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_ALU+BPF_MOD+BPF_K:
+ case BPF_ALU+BPF_MOD+BPF_X:
+ return "mod";
+ case BPF_ALU+BPF_XOR+BPF_K:
+ case BPF_ALU+BPF_XOR+BPF_X:
+ return "xor";
+ 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_FULL;
+ uint32_t data = k & SECCOMP_RET_DATA;
+
+ switch (act) {
+ case SECCOMP_RET_KILL_PROCESS:
+ printf("KILL_PROCESS");
+ break;
+ case SECCOMP_RET_KILL_THREAD:
+ 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_LOG:
+ printf("LOG");
+ 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,
+ * non-zero values on failure.
+ *
+ */
+static int bpf_decode(FILE *file)
+{
+ unsigned int line = 0;
+ bpf_instr_raw bpf;
+
+ /* header */
+ printf(" line OP JT JF K\n");
+ printf("=================================\n");
+
+ while (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,
+ * non-zero values on failure.
+ *
+ */
+static int bpf_dot_decode(FILE *file)
+{
+ unsigned int line = 0;
+ bpf_instr_raw bpf;
+ int prev_class = 0;
+
+ /* header */
+ printf("digraph {\n");
+ printf("\tstart[shape=\"box\", style=rounded];\n");
+
+ while (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 if (strcmp(optarg, "riscv64") == 0)
+ arch = AUDIT_ARCH_RISCV64;
+ 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;
+}