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