| 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 <linux/filter.h> | 8 #include <linux/filter.h> |
| 9 #include <sys/syscall.h> | 9 #include <sys/syscall.h> |
| 10 | 10 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 47 #if defined(__NR_sigreturn) | 47 #if defined(__NR_sigreturn) |
| 48 __NR_sigreturn, | 48 __NR_sigreturn, |
| 49 #endif | 49 #endif |
| 50 }; | 50 }; |
| 51 | 51 |
| 52 bool HasExactlyOneBit(uint64_t x) { | 52 bool HasExactlyOneBit(uint64_t x) { |
| 53 // Common trick; e.g., see http://stackoverflow.com/a/108329. | 53 // Common trick; e.g., see http://stackoverflow.com/a/108329. |
| 54 return x != 0 && (x & (x - 1)) == 0; | 54 return x != 0 && (x & (x - 1)) == 0; |
| 55 } | 55 } |
| 56 | 56 |
| 57 bool IsDenied(const ErrorCode& code) { | |
| 58 return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP || | |
| 59 (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) && | |
| 60 code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO)); | |
| 61 } | |
| 62 | |
| 63 // A Trap() handler that returns an "errno" value. The value is encoded | 57 // A Trap() handler that returns an "errno" value. The value is encoded |
| 64 // in the "aux" parameter. | 58 // in the "aux" parameter. |
| 65 intptr_t ReturnErrno(const struct arch_seccomp_data&, void* aux) { | 59 intptr_t ReturnErrno(const struct arch_seccomp_data&, void* aux) { |
| 66 // TrapFnc functions report error by following the native kernel convention | 60 // TrapFnc functions report error by following the native kernel convention |
| 67 // of returning an exit code in the range of -1..-4096. They do not try to | 61 // of returning an exit code in the range of -1..-4096. They do not try to |
| 68 // set errno themselves. The glibc wrapper that triggered the SIGSYS will | 62 // set errno themselves. The glibc wrapper that triggered the SIGSYS will |
| 69 // ultimately do so for us. | 63 // ultimately do so for us. |
| 70 int err = reinterpret_cast<intptr_t>(aux) & SECCOMP_RET_DATA; | 64 int err = reinterpret_cast<intptr_t>(aux) & SECCOMP_RET_DATA; |
| 71 return -err; | 65 return -err; |
| 72 } | 66 } |
| 73 | 67 |
| 74 intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) { | |
| 75 SANDBOX_DIE(static_cast<char*>(aux)); | |
| 76 } | |
| 77 | |
| 78 bool HasUnsafeTraps(const Policy* policy) { | 68 bool HasUnsafeTraps(const Policy* policy) { |
| 69 DCHECK(policy); |
| 79 for (uint32_t sysnum : SyscallSet::ValidOnly()) { | 70 for (uint32_t sysnum : SyscallSet::ValidOnly()) { |
| 80 if (policy->EvaluateSyscall(sysnum)->HasUnsafeTraps()) { | 71 if (policy->EvaluateSyscall(sysnum)->HasUnsafeTraps()) { |
| 81 return true; | 72 return true; |
| 82 } | 73 } |
| 83 } | 74 } |
| 84 return policy->InvalidSyscall()->HasUnsafeTraps(); | 75 return policy->InvalidSyscall()->HasUnsafeTraps(); |
| 85 } | 76 } |
| 86 | 77 |
| 87 } // namespace | 78 } // namespace |
| 88 | 79 |
| 89 struct PolicyCompiler::Range { | 80 struct PolicyCompiler::Range { |
| 90 Range(uint32_t f, const ErrorCode& e) : from(f), err(e) {} | |
| 91 uint32_t from; | 81 uint32_t from; |
| 92 ErrorCode err; | 82 CodeGen::Node node; |
| 93 }; | 83 }; |
| 94 | 84 |
| 95 PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry) | 85 PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry) |
| 96 : policy_(policy), | 86 : policy_(policy), |
| 97 registry_(registry), | 87 registry_(registry), |
| 98 conds_(), | 88 conds_(), |
| 99 gen_(), | 89 gen_(), |
| 100 has_unsafe_traps_(HasUnsafeTraps(policy_)) { | 90 has_unsafe_traps_(HasUnsafeTraps(policy_)) { |
| 91 DCHECK(policy); |
| 101 } | 92 } |
| 102 | 93 |
| 103 PolicyCompiler::~PolicyCompiler() { | 94 PolicyCompiler::~PolicyCompiler() { |
| 104 } | 95 } |
| 105 | 96 |
| 106 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile() { | 97 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile() { |
| 107 if (!IsDenied(policy_->InvalidSyscall()->Compile(this))) { | 98 if (!policy_->InvalidSyscall()->IsDeny()) { |
| 108 SANDBOX_DIE("Policies should deny invalid system calls."); | 99 SANDBOX_DIE("Policies should deny invalid system calls."); |
| 109 } | 100 } |
| 110 | 101 |
| 111 // If our BPF program has unsafe traps, enable support for them. | 102 // If our BPF program has unsafe traps, enable support for them. |
| 112 if (has_unsafe_traps_) { | 103 if (has_unsafe_traps_) { |
| 113 // As support for unsafe jumps essentially defeats all the security | 104 // As support for unsafe jumps essentially defeats all the security |
| 114 // measures that the sandbox provides, we print a big warning message -- | 105 // measures that the sandbox provides, we print a big warning message -- |
| 115 // and of course, we make sure to only ever enable this feature if it | 106 // and of course, we make sure to only ever enable this feature if it |
| 116 // is actually requested by the sandbox policy. | 107 // is actually requested by the sandbox policy. |
| 117 if (Syscall::Call(-1) == -1 && errno == ENOSYS) { | 108 if (Syscall::Call(-1) == -1 && errno == ENOSYS) { |
| 118 SANDBOX_DIE( | 109 SANDBOX_DIE( |
| 119 "Support for UnsafeTrap() has not yet been ported to this " | 110 "Support for UnsafeTrap() has not yet been ported to this " |
| 120 "architecture"); | 111 "architecture"); |
| 121 } | 112 } |
| 122 | 113 |
| 123 for (int sysnum : kSyscallsRequiredForUnsafeTraps) { | 114 for (int sysnum : kSyscallsRequiredForUnsafeTraps) { |
| 124 if (!policy_->EvaluateSyscall(sysnum)->Compile(this) | 115 if (!policy_->EvaluateSyscall(sysnum)->IsAllow()) { |
| 125 .Equals(ErrorCode(ErrorCode::ERR_ALLOWED))) { | |
| 126 SANDBOX_DIE( | 116 SANDBOX_DIE( |
| 127 "Policies that use UnsafeTrap() must unconditionally allow all " | 117 "Policies that use UnsafeTrap() must unconditionally allow all " |
| 128 "required system calls"); | 118 "required system calls"); |
| 129 } | 119 } |
| 130 } | 120 } |
| 131 | 121 |
| 132 if (!registry_->EnableUnsafeTraps()) { | 122 if (!registry_->EnableUnsafeTraps()) { |
| 133 // We should never be able to get here, as UnsafeTrap() should never | 123 // We should never be able to get here, as UnsafeTrap() should never |
| 134 // actually return a valid ErrorCode object unless the user set the | 124 // actually return a valid ErrorCode object unless the user set the |
| 135 // CHROME_SANDBOX_DEBUGGING environment variable; and therefore, | 125 // CHROME_SANDBOX_DEBUGGING environment variable; and therefore, |
| (...skipping 16 matching lines...) Expand all Loading... |
| 152 // invoked by Syscall::Call, and then allow it unconditionally. | 142 // invoked by Syscall::Call, and then allow it unconditionally. |
| 153 // 3. Check the system call number and jump to the appropriate compiled | 143 // 3. Check the system call number and jump to the appropriate compiled |
| 154 // system call policy number. | 144 // system call policy number. |
| 155 return CheckArch(MaybeAddEscapeHatch(DispatchSyscall())); | 145 return CheckArch(MaybeAddEscapeHatch(DispatchSyscall())); |
| 156 } | 146 } |
| 157 | 147 |
| 158 CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) { | 148 CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) { |
| 159 // If the architecture doesn't match SECCOMP_ARCH, disallow the | 149 // If the architecture doesn't match SECCOMP_ARCH, disallow the |
| 160 // system call. | 150 // system call. |
| 161 return gen_.MakeInstruction( | 151 return gen_.MakeInstruction( |
| 162 BPF_LD + BPF_W + BPF_ABS, | 152 BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARCH_IDX, |
| 163 SECCOMP_ARCH_IDX, | |
| 164 gen_.MakeInstruction( | 153 gen_.MakeInstruction( |
| 165 BPF_JMP + BPF_JEQ + BPF_K, | 154 BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_ARCH, passed, |
| 166 SECCOMP_ARCH, | 155 CompileResult(Kill("Invalid audit architecture in BPF filter")))); |
| 167 passed, | |
| 168 RetExpression(Kill("Invalid audit architecture in BPF filter")))); | |
| 169 } | 156 } |
| 170 | 157 |
| 171 CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) { | 158 CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) { |
| 172 // If no unsafe traps, then simply return |rest|. | 159 // If no unsafe traps, then simply return |rest|. |
| 173 if (!has_unsafe_traps_) { | 160 if (!has_unsafe_traps_) { |
| 174 return rest; | 161 return rest; |
| 175 } | 162 } |
| 176 | 163 |
| 177 // Allow system calls, if they originate from our magic return address | 164 // Allow system calls, if they originate from our magic return address |
| 178 // (which we can query by calling Syscall::Call(-1)). | 165 // (which we can query by calling Syscall::Call(-1)). |
| 179 uint64_t syscall_entry_point = | 166 uint64_t syscall_entry_point = |
| 180 static_cast<uint64_t>(static_cast<uintptr_t>(Syscall::Call(-1))); | 167 static_cast<uint64_t>(static_cast<uintptr_t>(Syscall::Call(-1))); |
| 181 uint32_t low = static_cast<uint32_t>(syscall_entry_point); | 168 uint32_t low = static_cast<uint32_t>(syscall_entry_point); |
| 182 uint32_t hi = static_cast<uint32_t>(syscall_entry_point >> 32); | 169 uint32_t hi = static_cast<uint32_t>(syscall_entry_point >> 32); |
| 183 | 170 |
| 184 // BPF cannot do native 64-bit comparisons, so we have to compare | 171 // BPF cannot do native 64-bit comparisons, so we have to compare |
| 185 // both 32-bit halves of the instruction pointer. If they match what | 172 // both 32-bit halves of the instruction pointer. If they match what |
| 186 // we expect, we return ERR_ALLOWED. If either or both don't match, | 173 // we expect, we return ERR_ALLOWED. If either or both don't match, |
| 187 // we continue evalutating the rest of the sandbox policy. | 174 // we continue evalutating the rest of the sandbox policy. |
| 188 // | 175 // |
| 189 // For simplicity, we check the full 64-bit instruction pointer even | 176 // For simplicity, we check the full 64-bit instruction pointer even |
| 190 // on 32-bit architectures. | 177 // on 32-bit architectures. |
| 191 return gen_.MakeInstruction( | 178 return gen_.MakeInstruction( |
| 192 BPF_LD + BPF_W + BPF_ABS, | 179 BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_LSB_IDX, |
| 193 SECCOMP_IP_LSB_IDX, | |
| 194 gen_.MakeInstruction( | 180 gen_.MakeInstruction( |
| 195 BPF_JMP + BPF_JEQ + BPF_K, | 181 BPF_JMP + BPF_JEQ + BPF_K, low, |
| 196 low, | |
| 197 gen_.MakeInstruction( | 182 gen_.MakeInstruction( |
| 198 BPF_LD + BPF_W + BPF_ABS, | 183 BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX, |
| 199 SECCOMP_IP_MSB_IDX, | 184 gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hi, |
| 200 gen_.MakeInstruction( | 185 CompileResult(Allow()), rest)), |
| 201 BPF_JMP + BPF_JEQ + BPF_K, | |
| 202 hi, | |
| 203 RetExpression(ErrorCode(ErrorCode::ERR_ALLOWED)), | |
| 204 rest)), | |
| 205 rest)); | 186 rest)); |
| 206 } | 187 } |
| 207 | 188 |
| 208 CodeGen::Node PolicyCompiler::DispatchSyscall() { | 189 CodeGen::Node PolicyCompiler::DispatchSyscall() { |
| 209 // Evaluate all possible system calls and group their ErrorCodes into | 190 // Evaluate all possible system calls and group their ErrorCodes into |
| 210 // ranges of identical codes. | 191 // ranges of identical codes. |
| 211 Ranges ranges; | 192 Ranges ranges; |
| 212 FindRanges(&ranges); | 193 FindRanges(&ranges); |
| 213 | 194 |
| 214 // Compile the system call ranges to an optimized BPF jumptable | 195 // Compile the system call ranges to an optimized BPF jumptable |
| 215 CodeGen::Node jumptable = AssembleJumpTable(ranges.begin(), ranges.end()); | 196 CodeGen::Node jumptable = AssembleJumpTable(ranges.begin(), ranges.end()); |
| 216 | 197 |
| 217 // Grab the system call number, so that we can check it and then | 198 // Grab the system call number, so that we can check it and then |
| 218 // execute the jump table. | 199 // execute the jump table. |
| 219 return gen_.MakeInstruction( | 200 return gen_.MakeInstruction( |
| 220 BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable)); | 201 BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable)); |
| 221 } | 202 } |
| 222 | 203 |
| 223 CodeGen::Node PolicyCompiler::CheckSyscallNumber(CodeGen::Node passed) { | 204 CodeGen::Node PolicyCompiler::CheckSyscallNumber(CodeGen::Node passed) { |
| 224 if (kIsIntel) { | 205 if (kIsIntel) { |
| 225 // On Intel architectures, verify that system call numbers are in the | 206 // On Intel architectures, verify that system call numbers are in the |
| 226 // expected number range. | 207 // expected number range. |
| 227 CodeGen::Node invalidX32 = | 208 CodeGen::Node invalidX32 = |
| 228 RetExpression(Kill("Illegal mixing of system call ABIs")); | 209 CompileResult(Kill("Illegal mixing of system call ABIs")); |
| 229 if (kIsX32) { | 210 if (kIsX32) { |
| 230 // The newer x32 API always sets bit 30. | 211 // The newer x32 API always sets bit 30. |
| 231 return gen_.MakeInstruction( | 212 return gen_.MakeInstruction( |
| 232 BPF_JMP + BPF_JSET + BPF_K, 0x40000000, passed, invalidX32); | 213 BPF_JMP + BPF_JSET + BPF_K, 0x40000000, passed, invalidX32); |
| 233 } else { | 214 } else { |
| 234 // The older i386 and x86-64 APIs clear bit 30 on all system calls. | 215 // The older i386 and x86-64 APIs clear bit 30 on all system calls. |
| 235 return gen_.MakeInstruction( | 216 return gen_.MakeInstruction( |
| 236 BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, passed); | 217 BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, passed); |
| 237 } | 218 } |
| 238 } | 219 } |
| 239 | 220 |
| 240 // TODO(mdempsky): Similar validation for other architectures? | 221 // TODO(mdempsky): Similar validation for other architectures? |
| 241 return passed; | 222 return passed; |
| 242 } | 223 } |
| 243 | 224 |
| 244 void PolicyCompiler::FindRanges(Ranges* ranges) { | 225 void PolicyCompiler::FindRanges(Ranges* ranges) { |
| 245 // Please note that "struct seccomp_data" defines system calls as a signed | 226 // Please note that "struct seccomp_data" defines system calls as a signed |
| 246 // int32_t, but BPF instructions always operate on unsigned quantities. We | 227 // int32_t, but BPF instructions always operate on unsigned quantities. We |
| 247 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, | 228 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, |
| 248 // and then verifying that the rest of the number range (both positive and | 229 // and then verifying that the rest of the number range (both positive and |
| 249 // negative) all return the same ErrorCode. | 230 // negative) all return the same ErrorCode. |
| 250 const ErrorCode invalid_err = policy_->InvalidSyscall()->Compile(this); | 231 const CodeGen::Node invalid_node = CompileResult(policy_->InvalidSyscall()); |
| 251 uint32_t old_sysnum = 0; | 232 uint32_t old_sysnum = 0; |
| 252 ErrorCode old_err = SyscallSet::IsValid(old_sysnum) | 233 CodeGen::Node old_node = |
| 253 ? policy_->EvaluateSyscall(old_sysnum)->Compile(this) | 234 SyscallSet::IsValid(old_sysnum) |
| 254 : invalid_err; | 235 ? CompileResult(policy_->EvaluateSyscall(old_sysnum)) |
| 236 : invalid_node; |
| 255 | 237 |
| 256 for (uint32_t sysnum : SyscallSet::All()) { | 238 for (uint32_t sysnum : SyscallSet::All()) { |
| 257 ErrorCode err = | 239 CodeGen::Node node = |
| 258 SyscallSet::IsValid(sysnum) | 240 SyscallSet::IsValid(sysnum) |
| 259 ? policy_->EvaluateSyscall(static_cast<int>(sysnum))->Compile(this) | 241 ? CompileResult(policy_->EvaluateSyscall(static_cast<int>(sysnum))) |
| 260 : invalid_err; | 242 : invalid_node; |
| 261 if (!err.Equals(old_err)) { | 243 // N.B., here we rely on CodeGen folding (i.e., returning the same |
| 262 ranges->push_back(Range(old_sysnum, old_err)); | 244 // node value for) identical code sequences, otherwise our jump |
| 245 // table will blow up in size. |
| 246 if (node != old_node) { |
| 247 ranges->push_back(Range{old_sysnum, old_node}); |
| 263 old_sysnum = sysnum; | 248 old_sysnum = sysnum; |
| 264 old_err = err; | 249 old_node = node; |
| 265 } | 250 } |
| 266 } | 251 } |
| 267 ranges->push_back(Range(old_sysnum, old_err)); | 252 ranges->push_back(Range{old_sysnum, old_node}); |
| 268 } | 253 } |
| 269 | 254 |
| 270 CodeGen::Node PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start, | 255 CodeGen::Node PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start, |
| 271 Ranges::const_iterator stop) { | 256 Ranges::const_iterator stop) { |
| 272 // We convert the list of system call ranges into jump table that performs | 257 // We convert the list of system call ranges into jump table that performs |
| 273 // a binary search over the ranges. | 258 // a binary search over the ranges. |
| 274 // As a sanity check, we need to have at least one distinct ranges for us | 259 // As a sanity check, we need to have at least one distinct ranges for us |
| 275 // to be able to build a jump table. | 260 // to be able to build a jump table. |
| 276 if (stop - start <= 0) { | 261 if (stop - start <= 0) { |
| 277 SANDBOX_DIE("Invalid set of system call ranges"); | 262 SANDBOX_DIE("Invalid set of system call ranges"); |
| 278 } else if (stop - start == 1) { | 263 } else if (stop - start == 1) { |
| 279 // If we have narrowed things down to a single range object, we can | 264 // If we have narrowed things down to a single range object, we can |
| 280 // return from the BPF filter program. | 265 // return from the BPF filter program. |
| 281 return RetExpression(start->err); | 266 return start->node; |
| 282 } | 267 } |
| 283 | 268 |
| 284 // Pick the range object that is located at the mid point of our list. | 269 // Pick the range object that is located at the mid point of our list. |
| 285 // We compare our system call number against the lowest valid system call | 270 // We compare our system call number against the lowest valid system call |
| 286 // number in this range object. If our number is lower, it is outside of | 271 // number in this range object. If our number is lower, it is outside of |
| 287 // this range object. If it is greater or equal, it might be inside. | 272 // this range object. If it is greater or equal, it might be inside. |
| 288 Ranges::const_iterator mid = start + (stop - start) / 2; | 273 Ranges::const_iterator mid = start + (stop - start) / 2; |
| 289 | 274 |
| 290 // Sub-divide the list of ranges and continue recursively. | 275 // Sub-divide the list of ranges and continue recursively. |
| 291 CodeGen::Node jf = AssembleJumpTable(start, mid); | 276 CodeGen::Node jf = AssembleJumpTable(start, mid); |
| 292 CodeGen::Node jt = AssembleJumpTable(mid, stop); | 277 CodeGen::Node jt = AssembleJumpTable(mid, stop); |
| 293 return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf); | 278 return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf); |
| 294 } | 279 } |
| 295 | 280 |
| 281 CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) { |
| 282 return RetExpression(res->Compile(this)); |
| 283 } |
| 284 |
| 296 CodeGen::Node PolicyCompiler::RetExpression(const ErrorCode& err) { | 285 CodeGen::Node PolicyCompiler::RetExpression(const ErrorCode& err) { |
| 297 switch (err.error_type()) { | 286 switch (err.error_type()) { |
| 298 case ErrorCode::ET_COND: | 287 case ErrorCode::ET_COND: |
| 299 return CondExpression(err); | 288 return CondExpression(err); |
| 300 case ErrorCode::ET_SIMPLE: | 289 case ErrorCode::ET_SIMPLE: |
| 301 case ErrorCode::ET_TRAP: | 290 case ErrorCode::ET_TRAP: |
| 302 return gen_.MakeInstruction(BPF_RET + BPF_K, err.err()); | 291 return gen_.MakeInstruction(BPF_RET + BPF_K, err.err()); |
| 303 default: | 292 default: |
| 304 SANDBOX_DIE("ErrorCode is not suitable for returning from a BPF program"); | 293 SANDBOX_DIE("ErrorCode is not suitable for returning from a BPF program"); |
| 305 } | 294 } |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 447 BPF_LD + BPF_W + BPF_ABS, | 436 BPF_LD + BPF_W + BPF_ABS, |
| 448 idx, | 437 idx, |
| 449 gen_.MakeInstruction( | 438 gen_.MakeInstruction( |
| 450 BPF_ALU + BPF_AND + BPF_K, | 439 BPF_ALU + BPF_AND + BPF_K, |
| 451 mask, | 440 mask, |
| 452 gen_.MakeInstruction( | 441 gen_.MakeInstruction( |
| 453 BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed))); | 442 BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed))); |
| 454 } | 443 } |
| 455 | 444 |
| 456 ErrorCode PolicyCompiler::Unexpected64bitArgument() { | 445 ErrorCode PolicyCompiler::Unexpected64bitArgument() { |
| 457 return Kill("Unexpected 64bit argument detected"); | 446 return Kill("Unexpected 64bit argument detected")->Compile(this); |
| 458 } | 447 } |
| 459 | 448 |
| 460 ErrorCode PolicyCompiler::Error(int err) { | 449 ErrorCode PolicyCompiler::Error(int err) { |
| 461 if (has_unsafe_traps_) { | 450 if (has_unsafe_traps_) { |
| 462 // When inside an UnsafeTrap() callback, we want to allow all system calls. | 451 // When inside an UnsafeTrap() callback, we want to allow all system calls. |
| 463 // This means, we must conditionally disable the sandbox -- and that's not | 452 // This means, we must conditionally disable the sandbox -- and that's not |
| 464 // something that kernel-side BPF filters can do, as they cannot inspect | 453 // something that kernel-side BPF filters can do, as they cannot inspect |
| 465 // any state other than the syscall arguments. | 454 // any state other than the syscall arguments. |
| 466 // But if we redirect all error handlers to user-space, then we can easily | 455 // But if we redirect all error handlers to user-space, then we can easily |
| 467 // make this decision. | 456 // make this decision. |
| 468 // The performance penalty for this extra round-trip to user-space is not | 457 // The performance penalty for this extra round-trip to user-space is not |
| 469 // actually that bad, as we only ever pay it for denied system calls; and a | 458 // actually that bad, as we only ever pay it for denied system calls; and a |
| 470 // typical program has very few of these. | 459 // typical program has very few of these. |
| 471 return Trap(ReturnErrno, reinterpret_cast<void*>(err)); | 460 return Trap(ReturnErrno, reinterpret_cast<void*>(err), true); |
| 472 } | 461 } |
| 473 | 462 |
| 474 return ErrorCode(err); | 463 return ErrorCode(err); |
| 475 } | 464 } |
| 476 | 465 |
| 477 ErrorCode PolicyCompiler::MakeTrap(TrapRegistry::TrapFnc fnc, | 466 ErrorCode PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc, |
| 478 const void* aux, | 467 const void* aux, |
| 479 bool safe) { | 468 bool safe) { |
| 480 uint16_t trap_id = registry_->Add(fnc, aux, safe); | 469 uint16_t trap_id = registry_->Add(fnc, aux, safe); |
| 481 return ErrorCode(trap_id, fnc, aux, safe); | 470 return ErrorCode(trap_id, fnc, aux, safe); |
| 482 } | 471 } |
| 483 | 472 |
| 484 ErrorCode PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc, const void* aux) { | |
| 485 return MakeTrap(fnc, aux, true /* Safe Trap */); | |
| 486 } | |
| 487 | |
| 488 ErrorCode PolicyCompiler::UnsafeTrap(TrapRegistry::TrapFnc fnc, | |
| 489 const void* aux) { | |
| 490 return MakeTrap(fnc, aux, false /* Unsafe Trap */); | |
| 491 } | |
| 492 | |
| 493 bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) { | 473 bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) { |
| 494 for (size_t i = 0; i < arraysize(kSyscallsRequiredForUnsafeTraps); ++i) { | 474 for (size_t i = 0; i < arraysize(kSyscallsRequiredForUnsafeTraps); ++i) { |
| 495 if (sysno == kSyscallsRequiredForUnsafeTraps[i]) { | 475 if (sysno == kSyscallsRequiredForUnsafeTraps[i]) { |
| 496 return true; | 476 return true; |
| 497 } | 477 } |
| 498 } | 478 } |
| 499 return false; | 479 return false; |
| 500 } | 480 } |
| 501 | 481 |
| 502 ErrorCode PolicyCompiler::CondMaskedEqual(int argno, | 482 ErrorCode PolicyCompiler::CondMaskedEqual(int argno, |
| 503 ErrorCode::ArgType width, | 483 ErrorCode::ArgType width, |
| 504 uint64_t mask, | 484 uint64_t mask, |
| 505 uint64_t value, | 485 uint64_t value, |
| 506 const ErrorCode& passed, | 486 const ErrorCode& passed, |
| 507 const ErrorCode& failed) { | 487 const ErrorCode& failed) { |
| 508 return ErrorCode(argno, | 488 return ErrorCode(argno, |
| 509 width, | 489 width, |
| 510 mask, | 490 mask, |
| 511 value, | 491 value, |
| 512 &*conds_.insert(passed).first, | 492 &*conds_.insert(passed).first, |
| 513 &*conds_.insert(failed).first); | 493 &*conds_.insert(failed).first); |
| 514 } | 494 } |
| 515 | 495 |
| 516 ErrorCode PolicyCompiler::Kill(const char* msg) { | |
| 517 return Trap(BPFFailure, const_cast<char*>(msg)); | |
| 518 } | |
| 519 | |
| 520 } // namespace bpf_dsl | 496 } // namespace bpf_dsl |
| 521 } // namespace sandbox | 497 } // namespace sandbox |
| OLD | NEW |