Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sandbox/linux/bpf_dsl/policy_compiler.h" | 5 #include "sandbox/linux/bpf_dsl/policy_compiler.h" |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <sys/syscall.h> | 8 #include <sys/syscall.h> |
| 9 | 9 |
| 10 #include <limits> | 10 #include <limits> |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 49 #if defined(__NR_sigreturn) | 49 #if defined(__NR_sigreturn) |
| 50 __NR_sigreturn, | 50 __NR_sigreturn, |
| 51 #endif | 51 #endif |
| 52 }; | 52 }; |
| 53 | 53 |
| 54 bool HasExactlyOneBit(uint64_t x) { | 54 bool HasExactlyOneBit(uint64_t x) { |
| 55 // Common trick; e.g., see http://stackoverflow.com/a/108329. | 55 // Common trick; e.g., see http://stackoverflow.com/a/108329. |
| 56 return x != 0 && (x & (x - 1)) == 0; | 56 return x != 0 && (x & (x - 1)) == 0; |
| 57 } | 57 } |
| 58 | 58 |
| 59 // The default Trap() handler for PolicyCompiler::Panic. | |
| 60 intptr_t DefaultPanic(const struct arch_seccomp_data&, void* aux) { | |
| 61 LOG(FATAL) << "bpf_dsl panic: " << reinterpret_cast<const char*>(aux); | |
|
rickyz (no longer on Chrome)
2015/08/19 23:25:10
Should we default to something async signal safe l
mdempsky
2015/08/19 23:33:31
The problem with defaulting to SANDBOX_DIE is it's
| |
| 62 for (;;) _exit(1); | |
| 63 } | |
| 64 | |
| 59 // A Trap() handler that returns an "errno" value. The value is encoded | 65 // A Trap() handler that returns an "errno" value. The value is encoded |
| 60 // in the "aux" parameter. | 66 // in the "aux" parameter. |
| 61 intptr_t ReturnErrno(const struct arch_seccomp_data&, void* aux) { | 67 intptr_t ReturnErrno(const struct arch_seccomp_data&, void* aux) { |
| 62 // TrapFnc functions report error by following the native kernel convention | 68 // TrapFnc functions report error by following the native kernel convention |
| 63 // of returning an exit code in the range of -1..-4096. They do not try to | 69 // of returning an exit code in the range of -1..-4096. They do not try to |
| 64 // set errno themselves. The glibc wrapper that triggered the SIGSYS will | 70 // set errno themselves. The glibc wrapper that triggered the SIGSYS will |
| 65 // ultimately do so for us. | 71 // ultimately do so for us. |
| 66 int err = reinterpret_cast<intptr_t>(aux) & SECCOMP_RET_DATA; | 72 int err = reinterpret_cast<intptr_t>(aux) & SECCOMP_RET_DATA; |
| 67 return -err; | 73 return -err; |
| 68 } | 74 } |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 81 | 87 |
| 82 struct PolicyCompiler::Range { | 88 struct PolicyCompiler::Range { |
| 83 uint32_t from; | 89 uint32_t from; |
| 84 CodeGen::Node node; | 90 CodeGen::Node node; |
| 85 }; | 91 }; |
| 86 | 92 |
| 87 PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry) | 93 PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry) |
| 88 : policy_(policy), | 94 : policy_(policy), |
| 89 registry_(registry), | 95 registry_(registry), |
| 90 escapepc_(0), | 96 escapepc_(0), |
| 97 panic_func_(DefaultPanic), | |
| 91 conds_(), | 98 conds_(), |
| 92 gen_(), | 99 gen_(), |
| 93 has_unsafe_traps_(HasUnsafeTraps(policy_)) { | 100 has_unsafe_traps_(HasUnsafeTraps(policy_)) { |
| 94 DCHECK(policy); | 101 DCHECK(policy); |
| 95 } | 102 } |
| 96 | 103 |
| 97 PolicyCompiler::~PolicyCompiler() { | 104 PolicyCompiler::~PolicyCompiler() { |
| 98 } | 105 } |
| 99 | 106 |
| 100 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile(bool verify) { | 107 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile(bool verify) { |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 130 } | 137 } |
| 131 } | 138 } |
| 132 | 139 |
| 133 return program.Pass(); | 140 return program.Pass(); |
| 134 } | 141 } |
| 135 | 142 |
| 136 void PolicyCompiler::DangerousSetEscapePC(uint64_t escapepc) { | 143 void PolicyCompiler::DangerousSetEscapePC(uint64_t escapepc) { |
| 137 escapepc_ = escapepc; | 144 escapepc_ = escapepc; |
| 138 } | 145 } |
| 139 | 146 |
| 147 void PolicyCompiler::SetPanicFunc(TrapRegistry::TrapFnc panic_func) { | |
| 148 panic_func_ = panic_func; | |
| 149 } | |
| 150 | |
| 140 CodeGen::Node PolicyCompiler::AssemblePolicy() { | 151 CodeGen::Node PolicyCompiler::AssemblePolicy() { |
| 141 // A compiled policy consists of three logical parts: | 152 // A compiled policy consists of three logical parts: |
| 142 // 1. Check that the "arch" field matches the expected architecture. | 153 // 1. Check that the "arch" field matches the expected architecture. |
| 143 // 2. If the policy involves unsafe traps, check if the syscall was | 154 // 2. If the policy involves unsafe traps, check if the syscall was |
| 144 // invoked by Syscall::Call, and then allow it unconditionally. | 155 // invoked by Syscall::Call, and then allow it unconditionally. |
| 145 // 3. Check the system call number and jump to the appropriate compiled | 156 // 3. Check the system call number and jump to the appropriate compiled |
| 146 // system call policy number. | 157 // system call policy number. |
| 147 return CheckArch(MaybeAddEscapeHatch(DispatchSyscall())); | 158 return CheckArch(MaybeAddEscapeHatch(DispatchSyscall())); |
| 148 } | 159 } |
| 149 | 160 |
| 150 CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) { | 161 CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) { |
| 151 // If the architecture doesn't match SECCOMP_ARCH, disallow the | 162 // If the architecture doesn't match SECCOMP_ARCH, disallow the |
| 152 // system call. | 163 // system call. |
| 153 return gen_.MakeInstruction( | 164 return gen_.MakeInstruction( |
| 154 BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARCH_IDX, | 165 BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARCH_IDX, |
| 155 gen_.MakeInstruction( | 166 gen_.MakeInstruction( |
| 156 BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_ARCH, passed, | 167 BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_ARCH, passed, |
| 157 CompileResult(Kill("Invalid audit architecture in BPF filter")))); | 168 CompileResult(Panic("Invalid audit architecture in BPF filter")))); |
| 158 } | 169 } |
| 159 | 170 |
| 160 CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) { | 171 CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) { |
| 161 // If no unsafe traps, then simply return |rest|. | 172 // If no unsafe traps, then simply return |rest|. |
| 162 if (!has_unsafe_traps_) { | 173 if (!has_unsafe_traps_) { |
| 163 return rest; | 174 return rest; |
| 164 } | 175 } |
| 165 | 176 |
| 166 // We already enabled unsafe traps in Compile, but enable them again to give | 177 // We already enabled unsafe traps in Compile, but enable them again to give |
| 167 // the trap registry a second chance to complain before we add the backdoor. | 178 // the trap registry a second chance to complain before we add the backdoor. |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 202 // execute the jump table. | 213 // execute the jump table. |
| 203 return gen_.MakeInstruction( | 214 return gen_.MakeInstruction( |
| 204 BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable)); | 215 BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable)); |
| 205 } | 216 } |
| 206 | 217 |
| 207 CodeGen::Node PolicyCompiler::CheckSyscallNumber(CodeGen::Node passed) { | 218 CodeGen::Node PolicyCompiler::CheckSyscallNumber(CodeGen::Node passed) { |
| 208 if (kIsIntel) { | 219 if (kIsIntel) { |
| 209 // On Intel architectures, verify that system call numbers are in the | 220 // On Intel architectures, verify that system call numbers are in the |
| 210 // expected number range. | 221 // expected number range. |
| 211 CodeGen::Node invalidX32 = | 222 CodeGen::Node invalidX32 = |
| 212 CompileResult(Kill("Illegal mixing of system call ABIs")); | 223 CompileResult(Panic("Illegal mixing of system call ABIs")); |
| 213 if (kIsX32) { | 224 if (kIsX32) { |
| 214 // The newer x32 API always sets bit 30. | 225 // The newer x32 API always sets bit 30. |
| 215 return gen_.MakeInstruction( | 226 return gen_.MakeInstruction( |
| 216 BPF_JMP + BPF_JSET + BPF_K, 0x40000000, passed, invalidX32); | 227 BPF_JMP + BPF_JSET + BPF_K, 0x40000000, passed, invalidX32); |
| 217 } else { | 228 } else { |
| 218 // The older i386 and x86-64 APIs clear bit 30 on all system calls. | 229 // The older i386 and x86-64 APIs clear bit 30 on all system calls. |
| 219 return gen_.MakeInstruction( | 230 return gen_.MakeInstruction( |
| 220 BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, passed); | 231 BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, passed); |
| 221 } | 232 } |
| 222 } | 233 } |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 438 BPF_LD + BPF_W + BPF_ABS, | 449 BPF_LD + BPF_W + BPF_ABS, |
| 439 idx, | 450 idx, |
| 440 gen_.MakeInstruction( | 451 gen_.MakeInstruction( |
| 441 BPF_ALU + BPF_AND + BPF_K, | 452 BPF_ALU + BPF_AND + BPF_K, |
| 442 mask, | 453 mask, |
| 443 gen_.MakeInstruction( | 454 gen_.MakeInstruction( |
| 444 BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed))); | 455 BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed))); |
| 445 } | 456 } |
| 446 | 457 |
| 447 ErrorCode PolicyCompiler::Unexpected64bitArgument() { | 458 ErrorCode PolicyCompiler::Unexpected64bitArgument() { |
| 448 return Kill("Unexpected 64bit argument detected")->Compile(this); | 459 return Panic("Unexpected 64bit argument detected")->Compile(this); |
| 449 } | 460 } |
| 450 | 461 |
| 451 ErrorCode PolicyCompiler::Error(int err) { | 462 ErrorCode PolicyCompiler::Error(int err) { |
| 452 if (has_unsafe_traps_) { | 463 if (has_unsafe_traps_) { |
| 453 // When inside an UnsafeTrap() callback, we want to allow all system calls. | 464 // When inside an UnsafeTrap() callback, we want to allow all system calls. |
| 454 // This means, we must conditionally disable the sandbox -- and that's not | 465 // This means, we must conditionally disable the sandbox -- and that's not |
| 455 // something that kernel-side BPF filters can do, as they cannot inspect | 466 // something that kernel-side BPF filters can do, as they cannot inspect |
| 456 // any state other than the syscall arguments. | 467 // any state other than the syscall arguments. |
| 457 // But if we redirect all error handlers to user-space, then we can easily | 468 // But if we redirect all error handlers to user-space, then we can easily |
| 458 // make this decision. | 469 // make this decision. |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 488 const ErrorCode& passed, | 499 const ErrorCode& passed, |
| 489 const ErrorCode& failed) { | 500 const ErrorCode& failed) { |
| 490 return ErrorCode(argno, | 501 return ErrorCode(argno, |
| 491 width, | 502 width, |
| 492 mask, | 503 mask, |
| 493 value, | 504 value, |
| 494 &*conds_.insert(passed).first, | 505 &*conds_.insert(passed).first, |
| 495 &*conds_.insert(failed).first); | 506 &*conds_.insert(failed).first); |
| 496 } | 507 } |
| 497 | 508 |
| 509 bpf_dsl::ResultExpr PolicyCompiler::Panic(const char* msg) { | |
| 510 return bpf_dsl::Trap(panic_func_, msg); | |
| 511 } | |
| 512 | |
| 498 } // namespace bpf_dsl | 513 } // namespace bpf_dsl |
| 499 } // namespace sandbox | 514 } // namespace sandbox |
| OLD | NEW |