xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/sandbox2/bpfdisassembler.cc (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
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