| OLD | NEW |
| (Empty) | |
| 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/seccomp-bpf/sandbox_bpf.h" |
| 6 #include "sandbox/linux/seccomp-bpf/verifier.h" |
| 7 |
| 8 |
| 9 namespace playground2 { |
| 10 |
| 11 bool Verifier::verifyBPF(const std::vector<struct sock_filter>& program, |
| 12 const Sandbox::Evaluators& evaluators, |
| 13 const char **err) { |
| 14 *err = NULL; |
| 15 if (evaluators.size() != 1) { |
| 16 *err = "Not implemented"; |
| 17 return false; |
| 18 } |
| 19 Sandbox::EvaluateSyscall evaluateSyscall = evaluators.begin()->first; |
| 20 for (int nr = MIN_SYSCALL-1; nr <= MAX_SYSCALL+1; ++nr) { |
| 21 // We ideally want to iterate over the full system call range and values |
| 22 // just above and just below this range. This gives us the full result set |
| 23 // of the "evaluators". |
| 24 // On Intel systems, this can fail in a surprising way, as a cleared bit 30 |
| 25 // indicates either i386 or x86-64; and a set bit 30 indicates x32. And |
| 26 // unless we pay attention to setting this bit correctly, an early check in |
| 27 // our BPF program will make us fail with a misleading error code. |
| 28 #if defined(__i386__) || defined(__x86_64__) |
| 29 #if defined(__x86_64__) && defined(__ILP32__) |
| 30 int sysnum = nr | 0x40000000; |
| 31 #else |
| 32 int sysnum = nr & ~0x40000000; |
| 33 #endif |
| 34 #else |
| 35 int sysnum = nr; |
| 36 #endif |
| 37 |
| 38 struct arch_seccomp_data data = { sysnum, SECCOMP_ARCH }; |
| 39 uint32_t expectedRet; |
| 40 Sandbox::ErrorCode code = evaluateSyscall(sysnum); |
| 41 switch (code) { |
| 42 case Sandbox::SB_TRAP: |
| 43 expectedRet = SECCOMP_RET_TRAP; |
| 44 break; |
| 45 case Sandbox::SB_ALLOWED: |
| 46 expectedRet = SECCOMP_RET_ALLOW; |
| 47 break; |
| 48 case Sandbox::SB_INSPECT_ARG_1...Sandbox::SB_INSPECT_ARG_6: |
| 49 *err = "Not implemented"; |
| 50 return false; |
| 51 default: |
| 52 if (code >= 1 && code < 4096) { |
| 53 expectedRet = SECCOMP_RET_ERRNO + static_cast<int>(code); |
| 54 } else { |
| 55 *err = "Invalid errno value"; |
| 56 return false; |
| 57 } |
| 58 break; |
| 59 } |
| 60 uint32_t computedRet = evaluateBPF(program, data, err); |
| 61 if (*err) { |
| 62 return false; |
| 63 } else if (computedRet != expectedRet) { |
| 64 *err = "Exit code from BPF program doesn't match"; |
| 65 return false; |
| 66 } |
| 67 } |
| 68 return true; |
| 69 } |
| 70 |
| 71 uint32_t Verifier::evaluateBPF(const std::vector<struct sock_filter>& program, |
| 72 const struct arch_seccomp_data& data, |
| 73 const char **err) { |
| 74 *err = NULL; |
| 75 for (State state(program, data); !*err; ++state.ip) { |
| 76 if (state.ip >= program.size()) { |
| 77 *err = "Invalid instruction pointer in BPF program"; |
| 78 break; |
| 79 } |
| 80 const struct sock_filter& insn = program[state.ip]; |
| 81 switch (BPF_CLASS(insn.code)) { |
| 82 case BPF_LD: |
| 83 ld(&state, insn, err); |
| 84 break; |
| 85 case BPF_JMP: |
| 86 jmp(&state, insn, err); |
| 87 break; |
| 88 case BPF_RET: |
| 89 return ret(&state, insn, err); |
| 90 default: |
| 91 *err = "Unexpected instruction in BPF program"; |
| 92 break; |
| 93 } |
| 94 } |
| 95 return 0; |
| 96 } |
| 97 |
| 98 void Verifier::ld(State *state, const struct sock_filter& insn, |
| 99 const char **err) { |
| 100 if (BPF_SIZE(insn.code) != BPF_W || |
| 101 BPF_MODE(insn.code) != BPF_ABS) { |
| 102 *err = "Invalid BPF_LD instruction"; |
| 103 return; |
| 104 } |
| 105 if (insn.k < sizeof(struct arch_seccomp_data) && (insn.k & 3) == 0) { |
| 106 // We only allow loading of properly aligned 32bit quantities. |
| 107 memcpy(&state->accumulator, |
| 108 reinterpret_cast<const char *>(&state->data) + insn.k, |
| 109 4); |
| 110 } else { |
| 111 *err = "Invalid operand in BPF_LD instruction"; |
| 112 return; |
| 113 } |
| 114 state->accIsValid = true; |
| 115 return; |
| 116 } |
| 117 |
| 118 void Verifier::jmp(State *state, const struct sock_filter& insn, |
| 119 const char **err) { |
| 120 if (BPF_OP(insn.code) == BPF_JA) { |
| 121 if (state->ip + insn.k + 1 >= state->program.size() || |
| 122 state->ip + insn.k + 1 <= state->ip) { |
| 123 compilation_failure: |
| 124 *err = "Invalid BPF_JMP instruction"; |
| 125 return; |
| 126 } |
| 127 state->ip += insn.k; |
| 128 } else { |
| 129 if (BPF_SRC(insn.code) != BPF_K || |
| 130 !state->accIsValid || |
| 131 state->ip + insn.jt + 1 >= state->program.size() || |
| 132 state->ip + insn.jf + 1 >= state->program.size()) { |
| 133 goto compilation_failure; |
| 134 } |
| 135 switch (BPF_OP(insn.code)) { |
| 136 case BPF_JEQ: |
| 137 if (state->accumulator == insn.k) { |
| 138 state->ip += insn.jt; |
| 139 } else { |
| 140 state->ip += insn.jf; |
| 141 } |
| 142 break; |
| 143 case BPF_JGT: |
| 144 if (state->accumulator > insn.k) { |
| 145 state->ip += insn.jt; |
| 146 } else { |
| 147 state->ip += insn.jf; |
| 148 } |
| 149 break; |
| 150 case BPF_JGE: |
| 151 if (state->accumulator >= insn.k) { |
| 152 state->ip += insn.jt; |
| 153 } else { |
| 154 state->ip += insn.jf; |
| 155 } |
| 156 break; |
| 157 case BPF_JSET: |
| 158 if (state->accumulator & insn.k) { |
| 159 state->ip += insn.jt; |
| 160 } else { |
| 161 state->ip += insn.jf; |
| 162 } |
| 163 break; |
| 164 default: |
| 165 goto compilation_failure; |
| 166 } |
| 167 } |
| 168 } |
| 169 |
| 170 uint32_t Verifier::ret(State *state, const struct sock_filter& insn, |
| 171 const char **err) { |
| 172 if (BPF_SRC(insn.code) != BPF_K) { |
| 173 *err = "Invalid BPF_RET instruction"; |
| 174 return 0; |
| 175 } |
| 176 return insn.k; |
| 177 } |
| 178 |
| 179 } // namespace |
| OLD | NEW |