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