| 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/verifier.h" | |
| 6 | |
| 7 #include <string.h> | |
| 8 | |
| 9 #include <limits> | |
| 10 | |
| 11 #include "sandbox/linux/bpf_dsl/bpf_dsl.h" | |
| 12 #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" | |
| 13 #include "sandbox/linux/bpf_dsl/policy.h" | |
| 14 #include "sandbox/linux/bpf_dsl/policy_compiler.h" | |
| 15 #include "sandbox/linux/bpf_dsl/seccomp_macros.h" | |
| 16 #include "sandbox/linux/bpf_dsl/syscall_set.h" | |
| 17 #include "sandbox/linux/seccomp-bpf/errorcode.h" | |
| 18 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" | |
| 19 #include "sandbox/linux/system_headers/linux_seccomp.h" | |
| 20 | |
| 21 namespace sandbox { | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 const uint64_t kLower32Bits = std::numeric_limits<uint32_t>::max(); | |
| 26 const uint64_t kUpper32Bits = static_cast<uint64_t>(kLower32Bits) << 32; | |
| 27 const uint64_t kFull64Bits = std::numeric_limits<uint64_t>::max(); | |
| 28 | |
| 29 struct State { | |
| 30 State(const std::vector<struct sock_filter>& p, | |
| 31 const struct arch_seccomp_data& d) | |
| 32 : program(p), data(d), ip(0), accumulator(0), acc_is_valid(false) {} | |
| 33 const std::vector<struct sock_filter>& program; | |
| 34 const struct arch_seccomp_data& data; | |
| 35 unsigned int ip; | |
| 36 uint32_t accumulator; | |
| 37 bool acc_is_valid; | |
| 38 | |
| 39 private: | |
| 40 DISALLOW_IMPLICIT_CONSTRUCTORS(State); | |
| 41 }; | |
| 42 | |
| 43 uint32_t EvaluateErrorCode(bpf_dsl::PolicyCompiler* compiler, | |
| 44 const ErrorCode& code, | |
| 45 const struct arch_seccomp_data& data) { | |
| 46 if (code.error_type() == ErrorCode::ET_SIMPLE || | |
| 47 code.error_type() == ErrorCode::ET_TRAP) { | |
| 48 return code.err(); | |
| 49 } else if (code.error_type() == ErrorCode::ET_COND) { | |
| 50 if (code.width() == ErrorCode::TP_32BIT && | |
| 51 (data.args[code.argno()] >> 32) && | |
| 52 (data.args[code.argno()] & 0xFFFFFFFF80000000ull) != | |
| 53 0xFFFFFFFF80000000ull) { | |
| 54 return compiler->Unexpected64bitArgument().err(); | |
| 55 } | |
| 56 bool equal = (data.args[code.argno()] & code.mask()) == code.value(); | |
| 57 return EvaluateErrorCode( | |
| 58 compiler, equal ? *code.passed() : *code.failed(), data); | |
| 59 } else { | |
| 60 return SECCOMP_RET_INVALID; | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 bool VerifyErrorCode(bpf_dsl::PolicyCompiler* compiler, | |
| 65 const std::vector<struct sock_filter>& program, | |
| 66 struct arch_seccomp_data* data, | |
| 67 const ErrorCode& root_code, | |
| 68 const ErrorCode& code, | |
| 69 const char** err) { | |
| 70 if (code.error_type() == ErrorCode::ET_SIMPLE || | |
| 71 code.error_type() == ErrorCode::ET_TRAP) { | |
| 72 uint32_t computed_ret = Verifier::EvaluateBPF(program, *data, err); | |
| 73 if (*err) { | |
| 74 return false; | |
| 75 } else if (computed_ret != EvaluateErrorCode(compiler, root_code, *data)) { | |
| 76 // For efficiency's sake, we'd much rather compare "computed_ret" | |
| 77 // against "code.err()". This works most of the time, but it doesn't | |
| 78 // always work for nested conditional expressions. The test values | |
| 79 // that we generate on the fly to probe expressions can trigger | |
| 80 // code flow decisions in multiple nodes of the decision tree, and the | |
| 81 // only way to compute the correct error code in that situation is by | |
| 82 // calling EvaluateErrorCode(). | |
| 83 *err = "Exit code from BPF program doesn't match"; | |
| 84 return false; | |
| 85 } | |
| 86 } else if (code.error_type() == ErrorCode::ET_COND) { | |
| 87 if (code.argno() < 0 || code.argno() >= 6) { | |
| 88 *err = "Invalid argument number in error code"; | |
| 89 return false; | |
| 90 } | |
| 91 | |
| 92 // TODO(mdempsky): The test values generated here try to provide good | |
| 93 // coverage for generated BPF instructions while avoiding combinatorial | |
| 94 // explosion on large policies. Ideally we would instead take a fuzzing-like | |
| 95 // approach and generate a bounded number of test cases regardless of policy | |
| 96 // size. | |
| 97 | |
| 98 // Verify that we can check a value for simple equality. | |
| 99 data->args[code.argno()] = code.value(); | |
| 100 if (!VerifyErrorCode( | |
| 101 compiler, program, data, root_code, *code.passed(), err)) { | |
| 102 return false; | |
| 103 } | |
| 104 | |
| 105 // If mask ignores any bits, verify that setting those bits is still | |
| 106 // detected as equality. | |
| 107 uint64_t ignored_bits = ~code.mask(); | |
| 108 if (code.width() == ErrorCode::TP_32BIT) { | |
| 109 ignored_bits = static_cast<uint32_t>(ignored_bits); | |
| 110 } | |
| 111 if ((ignored_bits & kLower32Bits) != 0) { | |
| 112 data->args[code.argno()] = code.value() | (ignored_bits & kLower32Bits); | |
| 113 if (!VerifyErrorCode( | |
| 114 compiler, program, data, root_code, *code.passed(), err)) { | |
| 115 return false; | |
| 116 } | |
| 117 } | |
| 118 if ((ignored_bits & kUpper32Bits) != 0) { | |
| 119 data->args[code.argno()] = code.value() | (ignored_bits & kUpper32Bits); | |
| 120 if (!VerifyErrorCode( | |
| 121 compiler, program, data, root_code, *code.passed(), err)) { | |
| 122 return false; | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 // Verify that changing bits included in the mask is detected as inequality. | |
| 127 if ((code.mask() & kLower32Bits) != 0) { | |
| 128 data->args[code.argno()] = code.value() ^ (code.mask() & kLower32Bits); | |
| 129 if (!VerifyErrorCode( | |
| 130 compiler, program, data, root_code, *code.failed(), err)) { | |
| 131 return false; | |
| 132 } | |
| 133 } | |
| 134 if ((code.mask() & kUpper32Bits) != 0) { | |
| 135 data->args[code.argno()] = code.value() ^ (code.mask() & kUpper32Bits); | |
| 136 if (!VerifyErrorCode( | |
| 137 compiler, program, data, root_code, *code.failed(), err)) { | |
| 138 return false; | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 if (code.width() == ErrorCode::TP_32BIT) { | |
| 143 // For 32-bit system call arguments, we emit additional instructions to | |
| 144 // validate the upper 32-bits. Here we test that validation. | |
| 145 | |
| 146 // Arbitrary 64-bit values should be rejected. | |
| 147 data->args[code.argno()] = 1ULL << 32; | |
| 148 if (!VerifyErrorCode(compiler, | |
| 149 program, | |
| 150 data, | |
| 151 root_code, | |
| 152 compiler->Unexpected64bitArgument(), | |
| 153 err)) { | |
| 154 return false; | |
| 155 } | |
| 156 | |
| 157 // Upper 32-bits set without the MSB of the lower 32-bits set should be | |
| 158 // rejected too. | |
| 159 data->args[code.argno()] = kUpper32Bits; | |
| 160 if (!VerifyErrorCode(compiler, | |
| 161 program, | |
| 162 data, | |
| 163 root_code, | |
| 164 compiler->Unexpected64bitArgument(), | |
| 165 err)) { | |
| 166 return false; | |
| 167 } | |
| 168 } | |
| 169 } else { | |
| 170 *err = "Attempting to return invalid error code from BPF program"; | |
| 171 return false; | |
| 172 } | |
| 173 return true; | |
| 174 } | |
| 175 | |
| 176 void Ld(State* state, const struct sock_filter& insn, const char** err) { | |
| 177 if (BPF_SIZE(insn.code) != BPF_W || BPF_MODE(insn.code) != BPF_ABS || | |
| 178 insn.jt != 0 || insn.jf != 0) { | |
| 179 *err = "Invalid BPF_LD instruction"; | |
| 180 return; | |
| 181 } | |
| 182 if (insn.k < sizeof(struct arch_seccomp_data) && (insn.k & 3) == 0) { | |
| 183 // We only allow loading of properly aligned 32bit quantities. | |
| 184 memcpy(&state->accumulator, | |
| 185 reinterpret_cast<const char*>(&state->data) + insn.k, | |
| 186 4); | |
| 187 } else { | |
| 188 *err = "Invalid operand in BPF_LD instruction"; | |
| 189 return; | |
| 190 } | |
| 191 state->acc_is_valid = true; | |
| 192 return; | |
| 193 } | |
| 194 | |
| 195 void Jmp(State* state, const struct sock_filter& insn, const char** err) { | |
| 196 if (BPF_OP(insn.code) == BPF_JA) { | |
| 197 if (state->ip + insn.k + 1 >= state->program.size() || | |
| 198 state->ip + insn.k + 1 <= state->ip) { | |
| 199 compilation_failure: | |
| 200 *err = "Invalid BPF_JMP instruction"; | |
| 201 return; | |
| 202 } | |
| 203 state->ip += insn.k; | |
| 204 } else { | |
| 205 if (BPF_SRC(insn.code) != BPF_K || !state->acc_is_valid || | |
| 206 state->ip + insn.jt + 1 >= state->program.size() || | |
| 207 state->ip + insn.jf + 1 >= state->program.size()) { | |
| 208 goto compilation_failure; | |
| 209 } | |
| 210 switch (BPF_OP(insn.code)) { | |
| 211 case BPF_JEQ: | |
| 212 if (state->accumulator == insn.k) { | |
| 213 state->ip += insn.jt; | |
| 214 } else { | |
| 215 state->ip += insn.jf; | |
| 216 } | |
| 217 break; | |
| 218 case BPF_JGT: | |
| 219 if (state->accumulator > insn.k) { | |
| 220 state->ip += insn.jt; | |
| 221 } else { | |
| 222 state->ip += insn.jf; | |
| 223 } | |
| 224 break; | |
| 225 case BPF_JGE: | |
| 226 if (state->accumulator >= insn.k) { | |
| 227 state->ip += insn.jt; | |
| 228 } else { | |
| 229 state->ip += insn.jf; | |
| 230 } | |
| 231 break; | |
| 232 case BPF_JSET: | |
| 233 if (state->accumulator & insn.k) { | |
| 234 state->ip += insn.jt; | |
| 235 } else { | |
| 236 state->ip += insn.jf; | |
| 237 } | |
| 238 break; | |
| 239 default: | |
| 240 goto compilation_failure; | |
| 241 } | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 uint32_t Ret(State*, const struct sock_filter& insn, const char** err) { | |
| 246 if (BPF_SRC(insn.code) != BPF_K) { | |
| 247 *err = "Invalid BPF_RET instruction"; | |
| 248 return 0; | |
| 249 } | |
| 250 return insn.k; | |
| 251 } | |
| 252 | |
| 253 void Alu(State* state, const struct sock_filter& insn, const char** err) { | |
| 254 if (BPF_OP(insn.code) == BPF_NEG) { | |
| 255 state->accumulator = -state->accumulator; | |
| 256 return; | |
| 257 } else { | |
| 258 if (BPF_SRC(insn.code) != BPF_K) { | |
| 259 *err = "Unexpected source operand in arithmetic operation"; | |
| 260 return; | |
| 261 } | |
| 262 switch (BPF_OP(insn.code)) { | |
| 263 case BPF_ADD: | |
| 264 state->accumulator += insn.k; | |
| 265 break; | |
| 266 case BPF_SUB: | |
| 267 state->accumulator -= insn.k; | |
| 268 break; | |
| 269 case BPF_MUL: | |
| 270 state->accumulator *= insn.k; | |
| 271 break; | |
| 272 case BPF_DIV: | |
| 273 if (!insn.k) { | |
| 274 *err = "Illegal division by zero"; | |
| 275 break; | |
| 276 } | |
| 277 state->accumulator /= insn.k; | |
| 278 break; | |
| 279 case BPF_MOD: | |
| 280 if (!insn.k) { | |
| 281 *err = "Illegal division by zero"; | |
| 282 break; | |
| 283 } | |
| 284 state->accumulator %= insn.k; | |
| 285 break; | |
| 286 case BPF_OR: | |
| 287 state->accumulator |= insn.k; | |
| 288 break; | |
| 289 case BPF_XOR: | |
| 290 state->accumulator ^= insn.k; | |
| 291 break; | |
| 292 case BPF_AND: | |
| 293 state->accumulator &= insn.k; | |
| 294 break; | |
| 295 case BPF_LSH: | |
| 296 if (insn.k > 32) { | |
| 297 *err = "Illegal shift operation"; | |
| 298 break; | |
| 299 } | |
| 300 state->accumulator <<= insn.k; | |
| 301 break; | |
| 302 case BPF_RSH: | |
| 303 if (insn.k > 32) { | |
| 304 *err = "Illegal shift operation"; | |
| 305 break; | |
| 306 } | |
| 307 state->accumulator >>= insn.k; | |
| 308 break; | |
| 309 default: | |
| 310 *err = "Invalid operator in arithmetic operation"; | |
| 311 break; | |
| 312 } | |
| 313 } | |
| 314 } | |
| 315 | |
| 316 } // namespace | |
| 317 | |
| 318 bool Verifier::VerifyBPF(bpf_dsl::PolicyCompiler* compiler, | |
| 319 const std::vector<struct sock_filter>& program, | |
| 320 const bpf_dsl::Policy& policy, | |
| 321 const char** err) { | |
| 322 *err = NULL; | |
| 323 for (uint32_t sysnum : SyscallSet::All()) { | |
| 324 // We ideally want to iterate over the full system call range and values | |
| 325 // just above and just below this range. This gives us the full result set | |
| 326 // of the "evaluators". | |
| 327 // On Intel systems, this can fail in a surprising way, as a cleared bit 30 | |
| 328 // indicates either i386 or x86-64; and a set bit 30 indicates x32. And | |
| 329 // unless we pay attention to setting this bit correctly, an early check in | |
| 330 // our BPF program will make us fail with a misleading error code. | |
| 331 struct arch_seccomp_data data = {static_cast<int>(sysnum), | |
| 332 static_cast<uint32_t>(SECCOMP_ARCH)}; | |
| 333 #if defined(__i386__) || defined(__x86_64__) | |
| 334 #if defined(__x86_64__) && defined(__ILP32__) | |
| 335 if (!(sysnum & 0x40000000u)) { | |
| 336 continue; | |
| 337 } | |
| 338 #else | |
| 339 if (sysnum & 0x40000000u) { | |
| 340 continue; | |
| 341 } | |
| 342 #endif | |
| 343 #endif | |
| 344 ErrorCode code = SyscallSet::IsValid(sysnum) | |
| 345 ? policy.EvaluateSyscall(sysnum)->Compile(compiler) | |
| 346 : policy.InvalidSyscall()->Compile(compiler); | |
| 347 if (!VerifyErrorCode(compiler, program, &data, code, code, err)) { | |
| 348 return false; | |
| 349 } | |
| 350 } | |
| 351 return true; | |
| 352 } | |
| 353 | |
| 354 uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program, | |
| 355 const struct arch_seccomp_data& data, | |
| 356 const char** err) { | |
| 357 *err = NULL; | |
| 358 if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) { | |
| 359 *err = "Invalid program length"; | |
| 360 return 0; | |
| 361 } | |
| 362 for (State state(program, data); !*err; ++state.ip) { | |
| 363 if (state.ip >= program.size()) { | |
| 364 *err = "Invalid instruction pointer in BPF program"; | |
| 365 break; | |
| 366 } | |
| 367 const struct sock_filter& insn = program[state.ip]; | |
| 368 switch (BPF_CLASS(insn.code)) { | |
| 369 case BPF_LD: | |
| 370 Ld(&state, insn, err); | |
| 371 break; | |
| 372 case BPF_JMP: | |
| 373 Jmp(&state, insn, err); | |
| 374 break; | |
| 375 case BPF_RET: { | |
| 376 uint32_t r = Ret(&state, insn, err); | |
| 377 switch (r & SECCOMP_RET_ACTION) { | |
| 378 case SECCOMP_RET_TRAP: | |
| 379 case SECCOMP_RET_ERRNO: | |
| 380 case SECCOMP_RET_TRACE: | |
| 381 case SECCOMP_RET_ALLOW: | |
| 382 break; | |
| 383 case SECCOMP_RET_KILL: // We don't ever generate this | |
| 384 case SECCOMP_RET_INVALID: // Should never show up in BPF program | |
| 385 default: | |
| 386 *err = "Unexpected return code found in BPF program"; | |
| 387 return 0; | |
| 388 } | |
| 389 return r; | |
| 390 } | |
| 391 case BPF_ALU: | |
| 392 Alu(&state, insn, err); | |
| 393 break; | |
| 394 default: | |
| 395 *err = "Unexpected instruction in BPF program"; | |
| 396 break; | |
| 397 } | |
| 398 } | |
| 399 return 0; | |
| 400 } | |
| 401 | |
| 402 } // namespace sandbox | |
| OLD | NEW |