1 // Copyright (c) 2012 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/verifier.h"
6
7 #include <stdint.h>
8 #include <string.h>
9
10 #include "base/macros.h"
11 #include "sandbox/linux/bpf_dsl/seccomp_macros.h"
12 #include "sandbox/linux/bpf_dsl/trap_registry.h"
13 #include "sandbox/linux/system_headers/linux_filter.h"
14 #include "sandbox/linux/system_headers/linux_seccomp.h"
15
16 namespace sandbox {
17 namespace bpf_dsl {
18
19 namespace {
20
21 struct State {
Statesandbox::bpf_dsl::__anonfd4570ca0111::State22 State(const std::vector<struct sock_filter>& p,
23 const struct arch_seccomp_data& d)
24 : program(p), data(d), ip(0), accumulator(0), acc_is_valid(false) {}
25 const std::vector<struct sock_filter>& program;
26 const struct arch_seccomp_data& data;
27 unsigned int ip;
28 uint32_t accumulator;
29 bool acc_is_valid;
30
31 private:
32 DISALLOW_IMPLICIT_CONSTRUCTORS(State);
33 };
34
Ld(State * state,const struct sock_filter & insn,const char ** err)35 void Ld(State* state, const struct sock_filter& insn, const char** err) {
36 if (BPF_SIZE(insn.code) != BPF_W || BPF_MODE(insn.code) != BPF_ABS ||
37 insn.jt != 0 || insn.jf != 0) {
38 *err = "Invalid BPF_LD instruction";
39 return;
40 }
41 if (insn.k < sizeof(struct arch_seccomp_data) && (insn.k & 3) == 0) {
42 // We only allow loading of properly aligned 32bit quantities.
43 memcpy(&state->accumulator,
44 reinterpret_cast<const char*>(&state->data) + insn.k, 4);
45 } else {
46 *err = "Invalid operand in BPF_LD instruction";
47 return;
48 }
49 state->acc_is_valid = true;
50 return;
51 }
52
Jmp(State * state,const struct sock_filter & insn,const char ** err)53 void Jmp(State* state, const struct sock_filter& insn, const char** err) {
54 if (BPF_OP(insn.code) == BPF_JA) {
55 if (state->ip + insn.k + 1 >= state->program.size() ||
56 state->ip + insn.k + 1 <= state->ip) {
57 compilation_failure:
58 *err = "Invalid BPF_JMP instruction";
59 return;
60 }
61 state->ip += insn.k;
62 } else {
63 if (BPF_SRC(insn.code) != BPF_K || !state->acc_is_valid ||
64 state->ip + insn.jt + 1 >= state->program.size() ||
65 state->ip + insn.jf + 1 >= state->program.size()) {
66 goto compilation_failure;
67 }
68 switch (BPF_OP(insn.code)) {
69 case BPF_JEQ:
70 if (state->accumulator == insn.k) {
71 state->ip += insn.jt;
72 } else {
73 state->ip += insn.jf;
74 }
75 break;
76 case BPF_JGT:
77 if (state->accumulator > insn.k) {
78 state->ip += insn.jt;
79 } else {
80 state->ip += insn.jf;
81 }
82 break;
83 case BPF_JGE:
84 if (state->accumulator >= insn.k) {
85 state->ip += insn.jt;
86 } else {
87 state->ip += insn.jf;
88 }
89 break;
90 case BPF_JSET:
91 if (state->accumulator & insn.k) {
92 state->ip += insn.jt;
93 } else {
94 state->ip += insn.jf;
95 }
96 break;
97 default:
98 goto compilation_failure;
99 }
100 }
101 }
102
Ret(State *,const struct sock_filter & insn,const char ** err)103 uint32_t Ret(State*, const struct sock_filter& insn, const char** err) {
104 if (BPF_SRC(insn.code) != BPF_K) {
105 *err = "Invalid BPF_RET instruction";
106 return 0;
107 }
108 return insn.k;
109 }
110
Alu(State * state,const struct sock_filter & insn,const char ** err)111 void Alu(State* state, const struct sock_filter& insn, const char** err) {
112 if (BPF_OP(insn.code) == BPF_NEG) {
113 state->accumulator = -state->accumulator;
114 return;
115 } else {
116 if (BPF_SRC(insn.code) != BPF_K) {
117 *err = "Unexpected source operand in arithmetic operation";
118 return;
119 }
120 switch (BPF_OP(insn.code)) {
121 case BPF_ADD:
122 state->accumulator += insn.k;
123 break;
124 case BPF_SUB:
125 state->accumulator -= insn.k;
126 break;
127 case BPF_MUL:
128 state->accumulator *= insn.k;
129 break;
130 case BPF_DIV:
131 if (!insn.k) {
132 *err = "Illegal division by zero";
133 break;
134 }
135 state->accumulator /= insn.k;
136 break;
137 case BPF_MOD:
138 if (!insn.k) {
139 *err = "Illegal division by zero";
140 break;
141 }
142 state->accumulator %= insn.k;
143 break;
144 case BPF_OR:
145 state->accumulator |= insn.k;
146 break;
147 case BPF_XOR:
148 state->accumulator ^= insn.k;
149 break;
150 case BPF_AND:
151 state->accumulator &= insn.k;
152 break;
153 case BPF_LSH:
154 if (insn.k > 32) {
155 *err = "Illegal shift operation";
156 break;
157 }
158 state->accumulator <<= insn.k;
159 break;
160 case BPF_RSH:
161 if (insn.k > 32) {
162 *err = "Illegal shift operation";
163 break;
164 }
165 state->accumulator >>= insn.k;
166 break;
167 default:
168 *err = "Invalid operator in arithmetic operation";
169 break;
170 }
171 }
172 }
173
174 } // namespace
175
EvaluateBPF(const std::vector<struct sock_filter> & program,const struct arch_seccomp_data & data,const char ** err)176 uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
177 const struct arch_seccomp_data& data,
178 const char** err) {
179 *err = NULL;
180 if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) {
181 *err = "Invalid program length";
182 return 0;
183 }
184 for (State state(program, data); !*err; ++state.ip) {
185 if (state.ip >= program.size()) {
186 *err = "Invalid instruction pointer in BPF program";
187 break;
188 }
189 const struct sock_filter& insn = program[state.ip];
190 switch (BPF_CLASS(insn.code)) {
191 case BPF_LD:
192 Ld(&state, insn, err);
193 break;
194 case BPF_JMP:
195 Jmp(&state, insn, err);
196 break;
197 case BPF_RET: {
198 uint32_t r = Ret(&state, insn, err);
199 switch (r & SECCOMP_RET_ACTION) {
200 case SECCOMP_RET_ALLOW:
201 case SECCOMP_RET_ERRNO:
202 case SECCOMP_RET_KILL:
203 case SECCOMP_RET_TRACE:
204 case SECCOMP_RET_TRAP:
205 break;
206 case SECCOMP_RET_INVALID: // Should never show up in BPF program
207 default:
208 *err = "Unexpected return code found in BPF program";
209 return 0;
210 }
211 return r;
212 }
213 case BPF_ALU:
214 Alu(&state, insn, err);
215 break;
216 default:
217 *err = "Unexpected instruction in BPF program";
218 break;
219 }
220 }
221 return 0;
222 }
223
224 } // namespace bpf_dsl
225 } // namespace sandbox
226