verifier.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 <string.h>
6
7#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
8#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
9#include "sandbox/linux/seccomp-bpf/verifier.h"
10
11
12namespace {
13
14using playground2::ErrorCode;
15using playground2::Sandbox;
16using playground2::Verifier;
17using playground2::arch_seccomp_data;
18
19struct State {
20  State(const std::vector<struct sock_filter>& p,
21        const struct arch_seccomp_data& d) :
22    program(p),
23    data(d),
24    ip(0),
25    accumulator(0),
26    acc_is_valid(false) {
27  }
28  const std::vector<struct sock_filter>& program;
29  const struct arch_seccomp_data&        data;
30  unsigned int                           ip;
31  uint32_t                               accumulator;
32  bool                                   acc_is_valid;
33
34 private:
35  DISALLOW_IMPLICIT_CONSTRUCTORS(State);
36};
37
38uint32_t EvaluateErrorCode(Sandbox *sandbox, const ErrorCode& code,
39                           const struct arch_seccomp_data& data) {
40  if (code.error_type() == ErrorCode::ET_SIMPLE ||
41      code.error_type() == ErrorCode::ET_TRAP) {
42    return code.err();
43  } else if (code.error_type() == ErrorCode::ET_COND) {
44    if (code.width() == ErrorCode::TP_32BIT &&
45        (data.args[code.argno()] >> 32) &&
46        (data.args[code.argno()] & 0xFFFFFFFF80000000ull) !=
47        0xFFFFFFFF80000000ull) {
48      return sandbox->Unexpected64bitArgument().err();
49    }
50    switch (code.op()) {
51    case ErrorCode::OP_EQUAL:
52      return EvaluateErrorCode(sandbox,
53                               (code.width() == ErrorCode::TP_32BIT
54                                ? uint32_t(data.args[code.argno()])
55                                : data.args[code.argno()]) == code.value()
56                               ? *code.passed()
57                               : *code.failed(),
58                               data);
59    case ErrorCode::OP_HAS_ALL_BITS:
60      return EvaluateErrorCode(sandbox,
61                               ((code.width() == ErrorCode::TP_32BIT
62                                 ? uint32_t(data.args[code.argno()])
63                                 : data.args[code.argno()]) & code.value())
64                               == code.value()
65                               ? *code.passed()
66                               : *code.failed(),
67                               data);
68    case ErrorCode::OP_HAS_ANY_BITS:
69      return EvaluateErrorCode(sandbox,
70                               (code.width() == ErrorCode::TP_32BIT
71                                ? uint32_t(data.args[code.argno()])
72                                : data.args[code.argno()]) & code.value()
73                               ? *code.passed()
74                               : *code.failed(),
75                               data);
76    default:
77      return SECCOMP_RET_INVALID;
78    }
79  } else {
80    return SECCOMP_RET_INVALID;
81  }
82}
83
84bool VerifyErrorCode(Sandbox *sandbox,
85                     const std::vector<struct sock_filter>& program,
86                     struct arch_seccomp_data *data,
87                     const ErrorCode& root_code,
88                     const ErrorCode& code,
89                     const char **err) {
90  if (code.error_type() == ErrorCode::ET_SIMPLE ||
91      code.error_type() == ErrorCode::ET_TRAP) {
92    uint32_t computed_ret = Verifier::EvaluateBPF(program, *data, err);
93    if (*err) {
94      return false;
95    } else if (computed_ret != EvaluateErrorCode(sandbox, root_code, *data)) {
96      // For efficiency's sake, we'd much rather compare "computed_ret"
97      // against "code.err()". This works most of the time, but it doesn't
98      // always work for nested conditional expressions. The test values
99      // that we generate on the fly to probe expressions can trigger
100      // code flow decisions in multiple nodes of the decision tree, and the
101      // only way to compute the correct error code in that situation is by
102      // calling EvaluateErrorCode().
103      *err = "Exit code from BPF program doesn't match";
104      return false;
105    }
106  } else if (code.error_type() == ErrorCode::ET_COND) {
107    if (code.argno() < 0 || code.argno() >= 6) {
108      *err = "Invalid argument number in error code";
109      return false;
110    }
111    switch (code.op()) {
112    case ErrorCode::OP_EQUAL:
113      // Verify that we can check a 32bit value (or the LSB of a 64bit value)
114      // for equality.
115      data->args[code.argno()] = code.value();
116      if (!VerifyErrorCode(sandbox, program, data, root_code,
117                           *code.passed(), err)) {
118        return false;
119      }
120
121      // Change the value to no longer match and verify that this is detected
122      // as an inequality.
123      data->args[code.argno()] = code.value() ^ 0x55AA55AA;
124      if (!VerifyErrorCode(sandbox, program, data, root_code,
125                           *code.failed(), err)) {
126        return false;
127      }
128
129      // BPF programs can only ever operate on 32bit values. So, we have
130      // generated additional BPF instructions that inspect the MSB. Verify
131      // that they behave as intended.
132      if (code.width() == ErrorCode::TP_32BIT) {
133        if (code.value() >> 32) {
134          SANDBOX_DIE("Invalid comparison of a 32bit system call argument "
135                      "against a 64bit constant; this test is always false.");
136        }
137
138        // If the system call argument was intended to be a 32bit parameter,
139        // verify that it is a fatal error if a 64bit value is ever passed
140        // here.
141        data->args[code.argno()] = 0x100000000ull;
142        if (!VerifyErrorCode(sandbox, program, data, root_code,
143                             sandbox->Unexpected64bitArgument(),
144                             err)) {
145          return false;
146        }
147      } else {
148        // If the system call argument was intended to be a 64bit parameter,
149        // verify that we can handle (in-)equality for the MSB. This is
150        // essentially the same test that we did earlier for the LSB.
151        // We only need to verify the behavior of the inequality test. We
152        // know that the equality test already passed, as unlike the kernel
153        // the Verifier does operate on 64bit quantities.
154        data->args[code.argno()] = code.value() ^ 0x55AA55AA00000000ull;
155        if (!VerifyErrorCode(sandbox, program, data, root_code,
156                             *code.failed(), err)) {
157          return false;
158        }
159      }
160      break;
161    case ErrorCode::OP_HAS_ALL_BITS:
162    case ErrorCode::OP_HAS_ANY_BITS:
163      // A comprehensive test of bit values is difficult and potentially rather
164      // time-expensive. We avoid doing so at run-time and instead rely on the
165      // unittest for full testing. The test that we have here covers just the
166      // common cases. We test against the bitmask itself, all zeros and all
167      // ones.
168      {
169        // Testing "any" bits against a zero mask is always false. So, there
170        // are some cases, where we expect tests to take the "failed()" branch
171        // even though this is a test that normally should take "passed()".
172        const ErrorCode& passed =
173          (!code.value() && code.op() == ErrorCode::OP_HAS_ANY_BITS) ||
174
175          // On a 32bit system, it is impossible to pass a 64bit value as a
176          // system call argument. So, some additional tests always evaluate
177          // as false.
178          ((code.value() & ~uint64_t(uintptr_t(-1))) &&
179           code.op() == ErrorCode::OP_HAS_ALL_BITS) ||
180          (code.value() && !(code.value() & uintptr_t(-1)) &&
181           code.op() == ErrorCode::OP_HAS_ANY_BITS)
182
183          ? *code.failed() : *code.passed();
184
185        // Similary, testing for "all" bits in a zero mask is always true. So,
186        // some cases pass despite them normally failing.
187        const ErrorCode& failed =
188          !code.value() && code.op() == ErrorCode::OP_HAS_ALL_BITS
189          ? *code.passed() : *code.failed();
190
191        data->args[code.argno()] = code.value() & uintptr_t(-1);
192        if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) {
193          return false;
194        }
195        data->args[code.argno()] = uintptr_t(-1);
196        if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) {
197          return false;
198        }
199        data->args[code.argno()] = 0;
200        if (!VerifyErrorCode(sandbox, program, data, root_code, failed, err)) {
201          return false;
202        }
203      }
204      break;
205    default: // TODO(markus): Need to add support for OP_GREATER
206      *err = "Unsupported operation in conditional error code";
207      return false;
208    }
209  } else {
210    *err = "Attempting to return invalid error code from BPF program";
211    return false;
212  }
213  return true;
214}
215
216void Ld(State *state, const struct sock_filter& insn, const char **err) {
217  if (BPF_SIZE(insn.code) != BPF_W ||
218      BPF_MODE(insn.code) != BPF_ABS) {
219    *err = "Invalid BPF_LD instruction";
220    return;
221  }
222  if (insn.k < sizeof(struct arch_seccomp_data) && (insn.k & 3) == 0) {
223    // We only allow loading of properly aligned 32bit quantities.
224    memcpy(&state->accumulator,
225           reinterpret_cast<const char *>(&state->data) + insn.k,
226           4);
227  } else {
228    *err = "Invalid operand in BPF_LD instruction";
229    return;
230  }
231  state->acc_is_valid = true;
232  return;
233}
234
235void Jmp(State *state, const struct sock_filter& insn, const char **err) {
236  if (BPF_OP(insn.code) == BPF_JA) {
237    if (state->ip + insn.k + 1 >= state->program.size() ||
238        state->ip + insn.k + 1 <= state->ip) {
239    compilation_failure:
240      *err = "Invalid BPF_JMP instruction";
241      return;
242    }
243    state->ip += insn.k;
244  } else {
245    if (BPF_SRC(insn.code) != BPF_K ||
246        !state->acc_is_valid ||
247        state->ip + insn.jt + 1 >= state->program.size() ||
248        state->ip + insn.jf + 1 >= state->program.size()) {
249      goto compilation_failure;
250    }
251    switch (BPF_OP(insn.code)) {
252    case BPF_JEQ:
253      if (state->accumulator == insn.k) {
254        state->ip += insn.jt;
255      } else {
256        state->ip += insn.jf;
257      }
258      break;
259    case BPF_JGT:
260      if (state->accumulator > insn.k) {
261        state->ip += insn.jt;
262      } else {
263        state->ip += insn.jf;
264      }
265      break;
266    case BPF_JGE:
267      if (state->accumulator >= insn.k) {
268        state->ip += insn.jt;
269      } else {
270        state->ip += insn.jf;
271      }
272      break;
273    case BPF_JSET:
274      if (state->accumulator & insn.k) {
275        state->ip += insn.jt;
276      } else {
277        state->ip += insn.jf;
278      }
279      break;
280    default:
281      goto compilation_failure;
282    }
283  }
284}
285
286uint32_t Ret(State *, const struct sock_filter& insn, const char **err) {
287  if (BPF_SRC(insn.code) != BPF_K) {
288    *err = "Invalid BPF_RET instruction";
289    return 0;
290  }
291  return insn.k;
292}
293
294void Alu(State *state, const struct sock_filter& insn, const char **err) {
295  if (BPF_OP(insn.code) == BPF_NEG) {
296    state->accumulator = -state->accumulator;
297    return;
298  } else {
299    if (BPF_SRC(insn.code) != BPF_K) {
300      *err = "Unexpected source operand in arithmetic operation";
301      return;
302    }
303    switch (BPF_OP(insn.code)) {
304    case BPF_ADD:
305      state->accumulator += insn.k;
306      break;
307    case BPF_SUB:
308      state->accumulator -= insn.k;
309      break;
310    case BPF_MUL:
311      state->accumulator *= insn.k;
312      break;
313    case BPF_DIV:
314      if (!insn.k) {
315        *err = "Illegal division by zero";
316        break;
317      }
318      state->accumulator /= insn.k;
319      break;
320    case BPF_MOD:
321      if (!insn.k) {
322        *err = "Illegal division by zero";
323        break;
324      }
325      state->accumulator %= insn.k;
326      break;
327    case BPF_OR:
328      state->accumulator |= insn.k;
329      break;
330    case BPF_XOR:
331      state->accumulator ^= insn.k;
332      break;
333    case BPF_AND:
334      state->accumulator &= insn.k;
335      break;
336    case BPF_LSH:
337      if (insn.k > 32) {
338        *err = "Illegal shift operation";
339        break;
340      }
341      state->accumulator <<= insn.k;
342      break;
343    case BPF_RSH:
344      if (insn.k > 32) {
345        *err = "Illegal shift operation";
346        break;
347      }
348      state->accumulator >>= insn.k;
349      break;
350    default:
351      *err = "Invalid operator in arithmetic operation";
352      break;
353    }
354  }
355}
356
357}  // namespace
358
359namespace playground2 {
360
361bool Verifier::VerifyBPF(Sandbox *sandbox,
362                         const std::vector<struct sock_filter>& program,
363                         const Sandbox::Evaluators& evaluators,
364                         const char **err) {
365  *err = NULL;
366  if (evaluators.size() != 1) {
367    *err = "Not implemented";
368    return false;
369  }
370  Sandbox::EvaluateSyscall evaluate_syscall = evaluators.begin()->first;
371  void *aux                                 = evaluators.begin()->second;
372  for (SyscallIterator iter(false); !iter.Done(); ) {
373    uint32_t sysnum = iter.Next();
374    // We ideally want to iterate over the full system call range and values
375    // just above and just below this range. This gives us the full result set
376    // of the "evaluators".
377    // On Intel systems, this can fail in a surprising way, as a cleared bit 30
378    // indicates either i386 or x86-64; and a set bit 30 indicates x32. And
379    // unless we pay attention to setting this bit correctly, an early check in
380    // our BPF program will make us fail with a misleading error code.
381    struct arch_seccomp_data data = { static_cast<int>(sysnum),
382                                      static_cast<uint32_t>(SECCOMP_ARCH) };
383#if defined(__i386__) || defined(__x86_64__)
384#if defined(__x86_64__) && defined(__ILP32__)
385    if (!(sysnum & 0x40000000u)) {
386      continue;
387    }
388#else
389    if (sysnum & 0x40000000u) {
390      continue;
391    }
392#endif
393#endif
394    ErrorCode code = evaluate_syscall(sandbox, sysnum, aux);
395    if (!VerifyErrorCode(sandbox, program, &data, code, code, err)) {
396      return false;
397    }
398  }
399  return true;
400}
401
402uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
403                               const struct arch_seccomp_data& data,
404                               const char **err) {
405  *err = NULL;
406  if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) {
407    *err = "Invalid program length";
408    return 0;
409  }
410  for (State state(program, data); !*err; ++state.ip) {
411    if (state.ip >= program.size()) {
412      *err = "Invalid instruction pointer in BPF program";
413      break;
414    }
415    const struct sock_filter& insn = program[state.ip];
416    switch (BPF_CLASS(insn.code)) {
417    case BPF_LD:
418      Ld(&state, insn, err);
419      break;
420    case BPF_JMP:
421      Jmp(&state, insn, err);
422      break;
423    case BPF_RET: {
424      uint32_t r = Ret(&state, insn, err);
425      switch (r & SECCOMP_RET_ACTION) {
426      case SECCOMP_RET_TRAP:
427      case SECCOMP_RET_ERRNO:
428      case SECCOMP_RET_ALLOW:
429        break;
430      case SECCOMP_RET_KILL:     // We don't ever generate this
431      case SECCOMP_RET_TRACE:    // We don't ever generate this
432      case SECCOMP_RET_INVALID:  // Should never show up in BPF program
433      default:
434        *err = "Unexpected return code found in BPF program";
435        return 0;
436      }
437      return r; }
438    case BPF_ALU:
439      Alu(&state, insn, err);
440      break;
441    default:
442      *err = "Unexpected instruction in BPF program";
443      break;
444    }
445  }
446  return 0;
447}
448
449}  // namespace
450