1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "sandboxed_api/sandbox2/bpfdisassembler.h"
16
17 #include <linux/bpf_common.h>
18 // IWYU pragma: no_include <asm/int-ll64.h>
19 #include <linux/filter.h>
20 #include <linux/seccomp.h>
21 #include <sys/sysinfo.h>
22
23 #include <cstddef>
24 #include <string>
25
26 #include "absl/strings/str_cat.h"
27 #include "absl/types/span.h"
28
29 #define INSIDE_FIELD(what, field) \
30 ((offsetof(seccomp_data, field) == 0 || \
31 (what) >= offsetof(seccomp_data, field)) && \
32 ((what) < (offsetof(seccomp_data, field) + sizeof(seccomp_data::field))))
33
34 #ifndef SECCOMP_RET_USER_NOTIF
35 #define SECCOMP_RET_USER_NOTIF 0x7fc00000U /* notifies userspace */
36 #endif
37
38 #ifndef SECCOMP_RET_LOG
39 #define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */
40 #endif
41
42 #ifndef SECCOMP_RET_KILL_PROCESS
43 #define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the process */
44 #endif
45
46 #ifndef SECCOMP_RET_ACTION_FULL
47 #define SECCOMP_RET_ACTION_FULL 0xffff0000U
48 #endif
49
50 namespace sandbox2 {
51 namespace bpf {
52 namespace {
53
OperandToString(int op)54 std::string OperandToString(int op) {
55 switch (op) {
56 case BPF_ADD:
57 return "+";
58 case BPF_SUB:
59 return "-";
60 case BPF_MUL:
61 return "*";
62 case BPF_DIV:
63 return "/";
64 case BPF_XOR:
65 return "^";
66 case BPF_AND:
67 return "&";
68 case BPF_OR:
69 return "|";
70 case BPF_RSH:
71 return ">>";
72 case BPF_LSH:
73 return "<<";
74 default:
75 return absl::StrCat("[unknown op ", op, "]");
76 }
77 }
78
ComparisonToString(int op)79 std::string ComparisonToString(int op) {
80 switch (op) {
81 case BPF_JGE:
82 return ">=";
83 case BPF_JGT:
84 return ">";
85 case BPF_JEQ:
86 return "==";
87 case BPF_JSET:
88 return "&";
89 default:
90 return absl::StrCat("[unknown cmp ", op, "]");
91 }
92 }
93
NegatedComparisonToString(int op)94 std::string NegatedComparisonToString(int op) {
95 switch (op) {
96 case BPF_JGE:
97 return "<";
98 case BPF_JGT:
99 return "<=";
100 case BPF_JEQ:
101 return "!=";
102 default:
103 return absl::StrCat("[unknown neg cmp ", op, "]");
104 }
105 }
106
107 } // namespace
108
DecodeInstruction(const sock_filter & inst,int pc)109 std::string DecodeInstruction(const sock_filter& inst, int pc) {
110 constexpr auto kArgSize = sizeof(seccomp_data::args[0]);
111 const int op = BPF_OP(inst.code);
112 const int true_target = inst.jt + pc + 1;
113 const int false_target = inst.jf + pc + 1;
114 switch (inst.code) {
115 case BPF_LD | BPF_W | BPF_ABS:
116 if (inst.k & 3) {
117 return absl::StrCat("A := data[0x", absl::Hex(inst.k),
118 "] (misaligned load)");
119 }
120 if (INSIDE_FIELD(inst.k, nr)) {
121 return "A := syscall number";
122 }
123 if (INSIDE_FIELD(inst.k, arch)) {
124 return "A := architecture";
125 }
126 if (INSIDE_FIELD(inst.k, instruction_pointer)) {
127 // TODO(swiecki) handle big-endian.
128 if (inst.k != offsetof(seccomp_data, instruction_pointer)) {
129 return "A := instruction pointer high";
130 }
131 return "A := instruction pointer low";
132 }
133 if (INSIDE_FIELD(inst.k, args)) {
134 const int argno = (inst.k - offsetof(seccomp_data, args)) / kArgSize;
135 // TODO(swiecki) handle big-endian.
136 if (inst.k != (offsetof(seccomp_data, args) + argno * kArgSize)) {
137 return absl::StrCat("A := arg ", argno, " high");
138 }
139 return absl::StrCat("A := arg ", argno, " low");
140 }
141 return absl::StrCat("A := data[0x", absl::Hex(inst.k),
142 "] (invalid load)");
143 case BPF_LD | BPF_W | BPF_LEN:
144 return "A := sizeof(seccomp_data)";
145 case BPF_LDX | BPF_W | BPF_LEN:
146 return "X := sizeof(seccomp_data)";
147 case BPF_LD | BPF_IMM:
148 return absl::StrCat("A := 0x", absl::Hex(inst.k));
149 case BPF_LDX | BPF_IMM:
150 return absl::StrCat("X := 0x", absl::Hex(inst.k));
151 case BPF_MISC | BPF_TAX:
152 return "X := A";
153 case BPF_MISC | BPF_TXA:
154 return "A := X";
155 case BPF_LD | BPF_MEM:
156 return absl::StrCat("A := M[", inst.k, "]");
157 case BPF_LDX | BPF_MEM:
158 return absl::StrCat("X := M[", inst.k, "]");
159 case BPF_ST:
160 return absl::StrCat("M[", inst.k, "] := A");
161 case BPF_STX:
162 return absl::StrCat("M[", inst.k, "] := X");
163 case BPF_RET | BPF_K: {
164 __u32 data = inst.k & SECCOMP_RET_DATA;
165 switch (inst.k & SECCOMP_RET_ACTION_FULL) {
166 case SECCOMP_RET_KILL_PROCESS:
167 return "KILL_PROCESS";
168 case SECCOMP_RET_LOG:
169 return "LOG";
170 case SECCOMP_RET_USER_NOTIF:
171 return "USER_NOTIF";
172 case SECCOMP_RET_KILL:
173 return "KILL";
174 case SECCOMP_RET_ALLOW:
175 return "ALLOW";
176 case SECCOMP_RET_TRAP:
177 return absl::StrCat("TRAP 0x", absl::Hex(data));
178 case SECCOMP_RET_ERRNO:
179 return absl::StrCat("ERRNO 0x", absl::Hex(data));
180 case SECCOMP_RET_TRACE:
181 return absl::StrCat("TRACE 0x", absl::Hex(data));
182 default:
183 return absl::StrCat("return 0x", absl::Hex(inst.k));
184 }
185 }
186 case BPF_RET | BPF_A:
187 return "return A";
188 case BPF_ALU | BPF_ADD | BPF_K:
189 case BPF_ALU | BPF_SUB | BPF_K:
190 case BPF_ALU | BPF_MUL | BPF_K:
191 case BPF_ALU | BPF_DIV | BPF_K:
192 case BPF_ALU | BPF_AND | BPF_K:
193 case BPF_ALU | BPF_OR | BPF_K:
194 case BPF_ALU | BPF_XOR | BPF_K:
195 case BPF_ALU | BPF_LSH | BPF_K:
196 case BPF_ALU | BPF_RSH | BPF_K:
197 return absl::StrCat("A := A ", OperandToString(op), " 0x",
198 absl::Hex(inst.k));
199 case BPF_ALU | BPF_ADD | BPF_X:
200 case BPF_ALU | BPF_SUB | BPF_X:
201 case BPF_ALU | BPF_MUL | BPF_X:
202 case BPF_ALU | BPF_DIV | BPF_X:
203 case BPF_ALU | BPF_AND | BPF_X:
204 case BPF_ALU | BPF_OR | BPF_X:
205 case BPF_ALU | BPF_XOR | BPF_X:
206 case BPF_ALU | BPF_LSH | BPF_X:
207 case BPF_ALU | BPF_RSH | BPF_X:
208 return absl::StrCat("A := A ", OperandToString(op), " X");
209 case BPF_ALU | BPF_NEG:
210 return "A := -A";
211 case BPF_JMP | BPF_JA:
212 return absl::StrCat("jump to ", inst.k + pc + 1);
213 case BPF_JMP | BPF_JEQ | BPF_K:
214 case BPF_JMP | BPF_JGE | BPF_K:
215 case BPF_JMP | BPF_JGT | BPF_K:
216 case BPF_JMP | BPF_JSET | BPF_K:
217 if (inst.jf == 0) {
218 return absl::StrCat("if A ", ComparisonToString(op), " 0x",
219 absl::Hex(inst.k), " goto ", true_target);
220 }
221 if (inst.jt == 0 && op != BPF_JSET) {
222 return absl::StrCat("if A ", NegatedComparisonToString(op), " 0x",
223 absl::Hex(inst.k), " goto ", false_target);
224 }
225 return absl::StrCat("if A ", ComparisonToString(op), " 0x",
226 absl::Hex(inst.k), " then ", true_target, " else ",
227 false_target);
228 case BPF_JMP | BPF_JEQ | BPF_X:
229 case BPF_JMP | BPF_JGE | BPF_X:
230 case BPF_JMP | BPF_JGT | BPF_X:
231 case BPF_JMP | BPF_JSET | BPF_X:
232 if (inst.jf == 0) {
233 return absl::StrCat("if A ", ComparisonToString(op), " X goto ",
234 true_target);
235 }
236 if (inst.jt == 0 && op != BPF_JSET) {
237 return absl::StrCat("if A ", NegatedComparisonToString(op), " X goto ",
238 false_target);
239 }
240 return absl::StrCat("if A ", ComparisonToString(op), " X then ",
241 true_target, " else ", false_target);
242 default:
243 return absl::StrCat("Invalid instruction ", inst.code);
244 }
245 }
246
Disasm(absl::Span<const sock_filter> prog)247 std::string Disasm(absl::Span<const sock_filter> prog) {
248 std::string rv;
249 for (size_t i = 0; i < prog.size(); ++i) {
250 absl::StrAppend(&rv, absl::Dec(i, absl::kZeroPad3), ": ",
251 DecodeInstruction(prog[i], i), "\n");
252 }
253 return rv;
254 }
255
256 } // namespace bpf
257 } // namespace sandbox2
258