diff options
Diffstat (limited to 'tools/scmp_bpf_sim.c')
-rw-r--r-- | tools/scmp_bpf_sim.c | 370 |
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; +} |