1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "sandbox/linux/bpf_dsl/dump_bpf.h"
6
7#include <inttypes.h>
8#include <stddef.h>
9#include <stdint.h>
10#include <stdio.h>
11
12#include <string>
13
14#include "base/strings/stringprintf.h"
15#include "sandbox/linux/bpf_dsl/codegen.h"
16#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
17#include "sandbox/linux/bpf_dsl/trap_registry.h"
18#include "sandbox/linux/system_headers/linux_filter.h"
19#include "sandbox/linux/system_headers/linux_seccomp.h"
20
21namespace sandbox {
22namespace bpf_dsl {
23
24namespace {
25
26const char* AluOpToken(uint32_t code) {
27  switch (BPF_OP(code)) {
28    case BPF_ADD:
29      return "+";
30    case BPF_SUB:
31      return "-";
32    case BPF_MUL:
33      return "*";
34    case BPF_DIV:
35      return "/";
36    case BPF_MOD:
37      return "%";
38    case BPF_OR:
39      return "|";
40    case BPF_XOR:
41      return "^";
42    case BPF_AND:
43      return "&";
44    case BPF_LSH:
45      return "<<";
46    case BPF_RSH:
47      return ">>";
48    default:
49      return "???";
50  }
51}
52
53const char* JmpOpToken(uint32_t code) {
54  switch (BPF_OP(code)) {
55    case BPF_JSET:
56      return "&";
57    case BPF_JEQ:
58      return "==";
59    case BPF_JGE:
60      return ">=";
61    default:
62      return "???";
63  }
64}
65
66const char* DataOffsetName(size_t off) {
67  switch (off) {
68    case SECCOMP_NR_IDX:
69      return "System call number";
70    case SECCOMP_ARCH_IDX:
71      return "Architecture";
72    case SECCOMP_IP_LSB_IDX:
73      return "Instruction pointer (LSB)";
74    case SECCOMP_IP_MSB_IDX:
75      return "Instruction pointer (MSB)";
76    default:
77      return "???";
78  }
79}
80
81void AppendInstruction(std::string* dst, size_t pc, const sock_filter& insn) {
82  base::StringAppendF(dst, "%3zu) ", pc);
83  switch (BPF_CLASS(insn.code)) {
84    case BPF_LD:
85      if (insn.code == BPF_LD + BPF_W + BPF_ABS) {
86        base::StringAppendF(dst, "LOAD %" PRIu32 "  // ", insn.k);
87        size_t maybe_argno =
88            (insn.k - offsetof(struct arch_seccomp_data, args)) /
89            sizeof(uint64_t);
90        if (maybe_argno < 6 && insn.k == SECCOMP_ARG_LSB_IDX(maybe_argno)) {
91          base::StringAppendF(dst, "Argument %zu (LSB)\n", maybe_argno);
92        } else if (maybe_argno < 6 &&
93                   insn.k == SECCOMP_ARG_MSB_IDX(maybe_argno)) {
94          base::StringAppendF(dst, "Argument %zu (MSB)\n", maybe_argno);
95        } else {
96          base::StringAppendF(dst, "%s\n", DataOffsetName(insn.k));
97        }
98      } else {
99        base::StringAppendF(dst, "Load ???\n");
100      }
101      break;
102    case BPF_JMP:
103      if (BPF_OP(insn.code) == BPF_JA) {
104        base::StringAppendF(dst, "JMP %zu\n", pc + insn.k + 1);
105      } else {
106        base::StringAppendF(
107            dst, "if A %s 0x%" PRIx32 "; then JMP %zu else JMP %zu\n",
108            JmpOpToken(insn.code), insn.k, pc + insn.jt + 1, pc + insn.jf + 1);
109      }
110      break;
111    case BPF_RET:
112      base::StringAppendF(dst, "RET 0x%" PRIx32 "  // ", insn.k);
113      if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP) {
114        base::StringAppendF(dst, "Trap #%" PRIu32 "\n",
115                            insn.k & SECCOMP_RET_DATA);
116      } else if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
117        base::StringAppendF(dst, "errno = %" PRIu32 "\n",
118                            insn.k & SECCOMP_RET_DATA);
119      } else if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRACE) {
120        base::StringAppendF(dst, "Trace #%" PRIu32 "\n",
121                            insn.k & SECCOMP_RET_DATA);
122      } else if (insn.k == SECCOMP_RET_ALLOW) {
123        base::StringAppendF(dst, "Allowed\n");
124      } else if (insn.k == SECCOMP_RET_KILL) {
125        base::StringAppendF(dst, "Kill\n");
126      } else {
127        base::StringAppendF(dst, "???\n");
128      }
129      break;
130    case BPF_ALU:
131      if (BPF_OP(insn.code) == BPF_NEG) {
132        base::StringAppendF(dst, "A := -A\n");
133      } else {
134        base::StringAppendF(dst, "A := A %s 0x%" PRIx32 "\n",
135                            AluOpToken(insn.code), insn.k);
136      }
137      break;
138    default:
139      base::StringAppendF(dst, "???\n");
140      break;
141  }
142}
143
144}  // namespace
145
146void DumpBPF::PrintProgram(const CodeGen::Program& program) {
147  fputs(StringPrintProgram(program).c_str(), stderr);
148}
149
150std::string DumpBPF::StringPrintProgram(const CodeGen::Program& program) {
151  std::string res;
152  for (size_t i = 0; i < program.size(); i++) {
153    AppendInstruction(&res, i + 1, program[i]);
154  }
155  return res;
156}
157
158}  // namespace bpf_dsl
159}  // namespace sandbox
160