Home | History | Annotate | Download | only in bpf_dsl
      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 {
     22   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 
     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 
     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 
    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 
    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 
    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