// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "sandbox/linux/bpf_dsl/dump_bpf.h" #include <inttypes.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <string> #include "base/strings/stringprintf.h" #include "sandbox/linux/bpf_dsl/codegen.h" #include "sandbox/linux/bpf_dsl/seccomp_macros.h" #include "sandbox/linux/bpf_dsl/trap_registry.h" #include "sandbox/linux/system_headers/linux_filter.h" #include "sandbox/linux/system_headers/linux_seccomp.h" namespace sandbox { namespace bpf_dsl { namespace { const char* AluOpToken(uint32_t code) { switch (BPF_OP(code)) { case BPF_ADD: return "+"; case BPF_SUB: return "-"; case BPF_MUL: return "*"; case BPF_DIV: return "/"; case BPF_MOD: return "%"; case BPF_OR: return "|"; case BPF_XOR: return "^"; case BPF_AND: return "&"; case BPF_LSH: return "<<"; case BPF_RSH: return ">>"; default: return "???"; } } const char* JmpOpToken(uint32_t code) { switch (BPF_OP(code)) { case BPF_JSET: return "&"; case BPF_JEQ: return "=="; case BPF_JGE: return ">="; default: return "???"; } } const char* DataOffsetName(size_t off) { switch (off) { case SECCOMP_NR_IDX: return "System call number"; case SECCOMP_ARCH_IDX: return "Architecture"; case SECCOMP_IP_LSB_IDX: return "Instruction pointer (LSB)"; case SECCOMP_IP_MSB_IDX: return "Instruction pointer (MSB)"; default: return "???"; } } void AppendInstruction(std::string* dst, size_t pc, const sock_filter& insn) { base::StringAppendF(dst, "%3zu) ", pc); switch (BPF_CLASS(insn.code)) { case BPF_LD: if (insn.code == BPF_LD + BPF_W + BPF_ABS) { base::StringAppendF(dst, "LOAD %" PRIu32 " // ", insn.k); size_t maybe_argno = (insn.k - offsetof(struct arch_seccomp_data, args)) / sizeof(uint64_t); if (maybe_argno < 6 && insn.k == SECCOMP_ARG_LSB_IDX(maybe_argno)) { base::StringAppendF(dst, "Argument %zu (LSB)\n", maybe_argno); } else if (maybe_argno < 6 && insn.k == SECCOMP_ARG_MSB_IDX(maybe_argno)) { base::StringAppendF(dst, "Argument %zu (MSB)\n", maybe_argno); } else { base::StringAppendF(dst, "%s\n", DataOffsetName(insn.k)); } } else { base::StringAppendF(dst, "Load ???\n"); } break; case BPF_JMP: if (BPF_OP(insn.code) == BPF_JA) { base::StringAppendF(dst, "JMP %zu\n", pc + insn.k + 1); } else { base::StringAppendF( dst, "if A %s 0x%" PRIx32 "; then JMP %zu else JMP %zu\n", JmpOpToken(insn.code), insn.k, pc + insn.jt + 1, pc + insn.jf + 1); } break; case BPF_RET: base::StringAppendF(dst, "RET 0x%" PRIx32 " // ", insn.k); if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP) { base::StringAppendF(dst, "Trap #%" PRIu32 "\n", insn.k & SECCOMP_RET_DATA); } else if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { base::StringAppendF(dst, "errno = %" PRIu32 "\n", insn.k & SECCOMP_RET_DATA); } else if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRACE) { base::StringAppendF(dst, "Trace #%" PRIu32 "\n", insn.k & SECCOMP_RET_DATA); } else if (insn.k == SECCOMP_RET_ALLOW) { base::StringAppendF(dst, "Allowed\n"); } else if (insn.k == SECCOMP_RET_KILL) { base::StringAppendF(dst, "Kill\n"); } else { base::StringAppendF(dst, "???\n"); } break; case BPF_ALU: if (BPF_OP(insn.code) == BPF_NEG) { base::StringAppendF(dst, "A := -A\n"); } else { base::StringAppendF(dst, "A := A %s 0x%" PRIx32 "\n", AluOpToken(insn.code), insn.k); } break; default: base::StringAppendF(dst, "???\n"); break; } } } // namespace void DumpBPF::PrintProgram(const CodeGen::Program& program) { fputs(StringPrintProgram(program).c_str(), stderr); } std::string DumpBPF::StringPrintProgram(const CodeGen::Program& program) { std::string res; for (size_t i = 0; i < program.size(); i++) { AppendInstruction(&res, i + 1, program[i]); } return res; } } // namespace bpf_dsl } // namespace sandbox