• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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