summaryrefslogtreecommitdiffstats
path: root/tools/scmp_bpf_sim.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/scmp_bpf_sim.c')
-rw-r--r--tools/scmp_bpf_sim.c370
1 files changed, 370 insertions, 0 deletions
diff --git a/tools/scmp_bpf_sim.c b/tools/scmp_bpf_sim.c
new file mode 100644
index 0000000..a381314
--- /dev/null
+++ b/tools/scmp_bpf_sim.c
@@ -0,0 +1,370 @@
+/**
+ * BPF Simulator
+ *
+ * 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 <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdlib.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 BPF_PRG_MAX_LEN 4096
+
+/**
+ * BPF simulator machine state
+ */
+struct sim_state {
+ uint32_t acc;
+ uint32_t temp[BPF_SCRATCH_SIZE];
+};
+
+struct bpf_program {
+ size_t i_cnt;
+ bpf_instr_raw *i;
+};
+
+static unsigned int opt_verbose = 0;
+
+/**
+ * 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 -f <bpf_file> [-v] [-h]"
+ " -a <arch> -s <syscall_num> [-0 <a0>] ... [-5 <a5>]\n",
+ program);
+ exit(EINVAL);
+}
+
+/**
+ * Handle a simulator fault
+ * @param rc the error or return code
+ *
+ * Print a "FAULT" to stderr to indicate a simulator fault, and an errno value
+ * if the simulator is running in verbose mode, then exit with EFAULT.
+ *
+ */
+static void exit_fault(unsigned int rc)
+{
+ if (opt_verbose)
+ fprintf(stderr, "FAULT: errno = %d\n", rc);
+ else
+ fprintf(stderr, "FAULT\n");
+ exit(EFAULT);
+}
+
+/**
+ * Handle a BPF program error
+ * @param rc the error or return code
+ * @param line the line number
+ *
+ * Print an "ERROR" to stderr to indicate a program error, and an errno value
+ * if the simulator is running in verbose mode, then exit with ENOEXEC.
+ *
+ */
+static void exit_error(unsigned int rc, unsigned int line)
+{
+ if (opt_verbose)
+ fprintf(stderr, "ERROR: errno = %d, line = %d\n", rc, line);
+ else
+ fprintf(stderr, "ERROR\n");
+ exit(ENOEXEC);
+}
+
+/**
+ * Handle a simulator return/action
+ * @param action the return value
+ * @param line the line number
+ *
+ * Display the action to stdout and exit with 0.
+ *
+ */
+static void end_action(uint32_t action, unsigned int line)
+{
+ uint32_t act = action & SECCOMP_RET_ACTION_FULL;
+ uint32_t data = action & SECCOMP_RET_DATA;
+
+ switch (act) {
+ case SECCOMP_RET_KILL_PROCESS:
+ fprintf(stdout, "KILL_PROCESS\n");
+ break;
+ case SECCOMP_RET_KILL_THREAD:
+ fprintf(stdout, "KILL\n");
+ break;
+ case SECCOMP_RET_TRAP:
+ fprintf(stdout, "TRAP\n");
+ break;
+ case SECCOMP_RET_ERRNO:
+ fprintf(stdout, "ERRNO(%u)\n", data);
+ break;
+ case SECCOMP_RET_TRACE:
+ fprintf(stdout, "TRACE(%u)\n", data);
+ break;
+ case SECCOMP_RET_LOG:
+ fprintf(stdout, "LOG\n");
+ break;
+ case SECCOMP_RET_ALLOW:
+ fprintf(stdout, "ALLOW\n");
+ break;
+ default:
+ exit_error(EDOM, line);
+ }
+
+ exit(0);
+}
+
+/**
+ * Execute a BPF program
+ * @param prg the loaded BPF program
+ * @param sys_data the syscall record being tested
+ *
+ * Simulate the BPF program with the given syscall record.
+ *
+ */
+static void bpf_execute(const struct bpf_program *prg,
+ const struct seccomp_data *sys_data)
+{
+ unsigned int ip, ip_c;
+ struct sim_state state;
+ bpf_instr_raw *bpf;
+ unsigned char *sys_data_b = (unsigned char *)sys_data;
+ uint16_t code;
+ uint8_t jt;
+ uint8_t jf;
+ uint32_t k;
+
+ /* initialize the machine state */
+ ip_c = 0;
+ ip = 0;
+ memset(&state, 0, sizeof(state));
+
+ while (ip < prg->i_cnt) {
+ /* get the instruction and bump the ip */
+ ip_c = ip;
+ bpf = &prg->i[ip++];
+
+ code = ttoh16(arch, bpf->code);
+ jt = bpf->jt;
+ jf = bpf->jf;
+ k = ttoh32(arch, bpf->k);
+
+ switch (code) {
+ case BPF_LD+BPF_W+BPF_ABS:
+ if (k < BPF_SYSCALL_MAX) {
+ uint32_t val = *((uint32_t *)&sys_data_b[k]);
+ state.acc = ttoh32(arch, val);
+ } else
+ exit_error(ERANGE, ip_c);
+ break;
+ case BPF_ALU+BPF_OR+BPF_K:
+ state.acc |= k;
+ break;
+ case BPF_ALU+BPF_AND+BPF_K:
+ state.acc &= k;
+ break;
+ case BPF_JMP+BPF_JA:
+ ip += k;
+ break;
+ case BPF_JMP+BPF_JEQ+BPF_K:
+ if (state.acc == k)
+ ip += jt;
+ else
+ ip += jf;
+ break;
+ case BPF_JMP+BPF_JGT+BPF_K:
+ if (state.acc > k)
+ ip += jt;
+ else
+ ip += jf;
+ break;
+ case BPF_JMP+BPF_JGE+BPF_K:
+ if (state.acc >= k)
+ ip += jt;
+ else
+ ip += jf;
+ break;
+ case BPF_RET+BPF_K:
+ end_action(k, ip_c);
+ break;
+ default:
+ /* since we don't support the full bpf language just
+ * yet, this could be either a fault or an error, we'll
+ * treat it as a fault until we provide full support */
+ exit_fault(EOPNOTSUPP);
+ }
+ }
+
+ /* if we've reached here there is a problem with the program */
+ exit_error(ERANGE, ip_c);
+}
+
+/**
+ * main
+ */
+int main(int argc, char *argv[])
+{
+ int opt;
+ int iter;
+ char *opt_file = NULL;
+ FILE *file;
+ size_t file_read_len;
+ struct seccomp_data sys_data;
+ struct bpf_program bpf_prg;
+
+ /* initialize the syscall record */
+ memset(&sys_data, 0, sizeof(sys_data));
+
+ /* parse the command line */
+ while ((opt = getopt(argc, argv, "a:f:hs:v0:1:2:3:4:5:")) > 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, "parisc") == 0)
+ arch = AUDIT_ARCH_PARISC;
+ else if (strcmp(optarg, "parisc64") == 0)
+ arch = AUDIT_ARCH_PARISC64;
+ else if (strcmp(optarg, "ppc") == 0)
+ arch = AUDIT_ARCH_PPC;
+ else if (strcmp(optarg, "ppc64") == 0)
+ arch = AUDIT_ARCH_PPC64;
+ else if (strcmp(optarg, "ppc64le") == 0)
+ arch = AUDIT_ARCH_PPC64LE;
+ 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_fault(EINVAL);
+ break;
+ case 'f':
+ if (opt_file)
+ exit_fault(EINVAL);
+ opt_file = strdup(optarg);
+ if (opt_file == NULL)
+ exit_fault(ENOMEM);
+ break;
+ case 's':
+ sys_data.nr = strtol(optarg, NULL, 0);
+ break;
+ case 'v':
+ opt_verbose = 1;
+ break;
+ case '0':
+ sys_data.args[0] = strtoull(optarg, NULL, 0);
+ break;
+ case '1':
+ sys_data.args[1] = strtoull(optarg, NULL, 0);
+ break;
+ case '2':
+ sys_data.args[2] = strtoull(optarg, NULL, 0);
+ break;
+ case '3':
+ sys_data.args[3] = strtoull(optarg, NULL, 0);
+ break;
+ case '4':
+ sys_data.args[4] = strtoull(optarg, NULL, 0);
+ break;
+ case '5':
+ sys_data.args[5] = strtoull(optarg, NULL, 0);
+ break;
+ case 'h':
+ default:
+ /* usage information */
+ exit_usage(argv[0]);
+ }
+ }
+
+ /* adjust the endianess of sys_data to match the target */
+ sys_data.nr = htot32(arch, sys_data.nr);
+ sys_data.arch = htot32(arch, arch);
+ sys_data.instruction_pointer = htot64(arch,
+ sys_data.instruction_pointer);
+ for (iter = 0; iter < BPF_SYS_ARG_MAX; iter++)
+ sys_data.args[iter] = htot64(arch, sys_data.args[iter]);
+
+ /* allocate space for the bpf program */
+ /* XXX - we should make this dynamic */
+ bpf_prg.i_cnt = 0;
+ bpf_prg.i = calloc(BPF_PRG_MAX_LEN, sizeof(*bpf_prg.i));
+ if (bpf_prg.i == NULL)
+ exit_fault(ENOMEM);
+
+ /* load the bpf program */
+ if (opt_file == NULL)
+ exit_usage(argv[0]);
+ file = fopen(opt_file, "r");
+ if (file == NULL)
+ exit_fault(errno);
+ do {
+ file_read_len = fread(&(bpf_prg.i[bpf_prg.i_cnt]),
+ sizeof(*bpf_prg.i), 1, file);
+ if (file_read_len == 1)
+ bpf_prg.i_cnt++;
+
+ /* check the size */
+ if (bpf_prg.i_cnt == BPF_PRG_MAX_LEN)
+ exit_fault(E2BIG);
+ } while (file_read_len > 0);
+ fclose(file);
+
+ /* execute the bpf program */
+ bpf_execute(&bpf_prg, &sys_data);
+
+ /* we should never reach here */
+ exit_fault(EFAULT);
+ return 0;
+}