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> |
11 | 11 |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/macros.h" | 13 #include "base/macros.h" |
14 #include "sandbox/linux/bpf_dsl/bpf_dsl.h" | 14 #include "sandbox/linux/bpf_dsl/bpf_dsl.h" |
15 #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" | 15 #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" |
16 #include "sandbox/linux/bpf_dsl/codegen.h" | 16 #include "sandbox/linux/bpf_dsl/codegen.h" |
17 #include "sandbox/linux/bpf_dsl/errorcode.h" | |
18 #include "sandbox/linux/bpf_dsl/policy.h" | 17 #include "sandbox/linux/bpf_dsl/policy.h" |
19 #include "sandbox/linux/bpf_dsl/seccomp_macros.h" | 18 #include "sandbox/linux/bpf_dsl/seccomp_macros.h" |
20 #include "sandbox/linux/bpf_dsl/syscall_set.h" | 19 #include "sandbox/linux/bpf_dsl/syscall_set.h" |
21 #include "sandbox/linux/system_headers/linux_filter.h" | 20 #include "sandbox/linux/system_headers/linux_filter.h" |
22 #include "sandbox/linux/system_headers/linux_seccomp.h" | 21 #include "sandbox/linux/system_headers/linux_seccomp.h" |
23 #include "sandbox/linux/system_headers/linux_syscalls.h" | 22 #include "sandbox/linux/system_headers/linux_syscalls.h" |
24 | 23 |
25 namespace sandbox { | 24 namespace sandbox { |
26 namespace bpf_dsl { | 25 namespace bpf_dsl { |
27 | 26 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
84 struct PolicyCompiler::Range { | 83 struct PolicyCompiler::Range { |
85 uint32_t from; | 84 uint32_t from; |
86 CodeGen::Node node; | 85 CodeGen::Node node; |
87 }; | 86 }; |
88 | 87 |
89 PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry) | 88 PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry) |
90 : policy_(policy), | 89 : policy_(policy), |
91 registry_(registry), | 90 registry_(registry), |
92 escapepc_(0), | 91 escapepc_(0), |
93 panic_func_(DefaultPanic), | 92 panic_func_(DefaultPanic), |
94 conds_(), | |
95 gen_(), | 93 gen_(), |
96 has_unsafe_traps_(HasUnsafeTraps(policy_)) { | 94 has_unsafe_traps_(HasUnsafeTraps(policy_)) { |
97 DCHECK(policy); | 95 DCHECK(policy); |
98 } | 96 } |
99 | 97 |
100 PolicyCompiler::~PolicyCompiler() { | 98 PolicyCompiler::~PolicyCompiler() { |
101 } | 99 } |
102 | 100 |
103 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile() { | 101 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile() { |
104 CHECK(policy_->InvalidSyscall()->IsDeny()) | 102 CHECK(policy_->InvalidSyscall()->IsDeny()) |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
178 gen_.MakeInstruction( | 176 gen_.MakeInstruction( |
179 BPF_JMP + BPF_JEQ + BPF_K, lopc, | 177 BPF_JMP + BPF_JEQ + BPF_K, lopc, |
180 gen_.MakeInstruction( | 178 gen_.MakeInstruction( |
181 BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX, | 179 BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX, |
182 gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hipc, | 180 gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hipc, |
183 CompileResult(Allow()), rest)), | 181 CompileResult(Allow()), rest)), |
184 rest)); | 182 rest)); |
185 } | 183 } |
186 | 184 |
187 CodeGen::Node PolicyCompiler::DispatchSyscall() { | 185 CodeGen::Node PolicyCompiler::DispatchSyscall() { |
188 // Evaluate all possible system calls and group their ErrorCodes into | 186 // Evaluate all possible system calls and group their Nodes into |
189 // ranges of identical codes. | 187 // ranges of identical codes. |
190 Ranges ranges; | 188 Ranges ranges; |
191 FindRanges(&ranges); | 189 FindRanges(&ranges); |
192 | 190 |
193 // Compile the system call ranges to an optimized BPF jumptable | 191 // Compile the system call ranges to an optimized BPF jumptable |
194 CodeGen::Node jumptable = AssembleJumpTable(ranges.begin(), ranges.end()); | 192 CodeGen::Node jumptable = AssembleJumpTable(ranges.begin(), ranges.end()); |
195 | 193 |
196 // Grab the system call number, so that we can check it and then | 194 // Grab the system call number, so that we can check it and then |
197 // execute the jump table. | 195 // execute the jump table. |
198 return gen_.MakeInstruction( | 196 return gen_.MakeInstruction( |
(...skipping 19 matching lines...) Expand all Loading... | |
218 | 216 |
219 // TODO(mdempsky): Similar validation for other architectures? | 217 // TODO(mdempsky): Similar validation for other architectures? |
220 return passed; | 218 return passed; |
221 } | 219 } |
222 | 220 |
223 void PolicyCompiler::FindRanges(Ranges* ranges) { | 221 void PolicyCompiler::FindRanges(Ranges* ranges) { |
224 // Please note that "struct seccomp_data" defines system calls as a signed | 222 // Please note that "struct seccomp_data" defines system calls as a signed |
225 // int32_t, but BPF instructions always operate on unsigned quantities. We | 223 // int32_t, but BPF instructions always operate on unsigned quantities. We |
226 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, | 224 // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, |
227 // and then verifying that the rest of the number range (both positive and | 225 // and then verifying that the rest of the number range (both positive and |
228 // negative) all return the same ErrorCode. | 226 // negative) all return the same Node. |
229 const CodeGen::Node invalid_node = CompileResult(policy_->InvalidSyscall()); | 227 const CodeGen::Node invalid_node = CompileResult(policy_->InvalidSyscall()); |
230 uint32_t old_sysnum = 0; | 228 uint32_t old_sysnum = 0; |
231 CodeGen::Node old_node = | 229 CodeGen::Node old_node = |
232 SyscallSet::IsValid(old_sysnum) | 230 SyscallSet::IsValid(old_sysnum) |
233 ? CompileResult(policy_->EvaluateSyscall(old_sysnum)) | 231 ? CompileResult(policy_->EvaluateSyscall(old_sysnum)) |
234 : invalid_node; | 232 : invalid_node; |
235 | 233 |
236 for (uint32_t sysnum : SyscallSet::All()) { | 234 for (uint32_t sysnum : SyscallSet::All()) { |
237 CodeGen::Node node = | 235 CodeGen::Node node = |
238 SyscallSet::IsValid(sysnum) | 236 SyscallSet::IsValid(sysnum) |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
270 // this range object. If it is greater or equal, it might be inside. | 268 // this range object. If it is greater or equal, it might be inside. |
271 Ranges::const_iterator mid = start + n / 2; | 269 Ranges::const_iterator mid = start + n / 2; |
272 | 270 |
273 // Sub-divide the list of ranges and continue recursively. | 271 // Sub-divide the list of ranges and continue recursively. |
274 CodeGen::Node jf = AssembleJumpTable(start, mid); | 272 CodeGen::Node jf = AssembleJumpTable(start, mid); |
275 CodeGen::Node jt = AssembleJumpTable(mid, stop); | 273 CodeGen::Node jt = AssembleJumpTable(mid, stop); |
276 return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf); | 274 return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf); |
277 } | 275 } |
278 | 276 |
279 CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) { | 277 CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) { |
280 return RetExpression(res->Compile(this)); | 278 return res->Compile(this); |
281 } | 279 } |
282 | 280 |
283 CodeGen::Node PolicyCompiler::RetExpression(const ErrorCode& err) { | 281 CodeGen::Node PolicyCompiler::MaskedEqual(int argno, |
284 switch (err.error_type()) { | 282 size_t width, |
285 case ErrorCode::ET_COND: | 283 uint64_t mask, |
286 return CondExpression(err); | 284 uint64_t value, |
287 case ErrorCode::ET_SIMPLE: | 285 CodeGen::Node passed, |
288 case ErrorCode::ET_TRAP: | 286 CodeGen::Node failed) { |
289 return gen_.MakeInstruction(BPF_RET + BPF_K, err.err()); | 287 // Sanity check that arguments make sense. |
290 default: | 288 CHECK(argno >= 0 && argno < 6) << "Invalid argument number " << argno; |
291 LOG(FATAL) | 289 CHECK(width == 4 || width == 8) << "Invalid argument width " << width; |
292 << "ErrorCode is not suitable for returning from a BPF program"; | 290 CHECK_NE(0U, mask) << "Zero mask is invalid"; |
293 return CodeGen::kNullNode; | 291 CHECK_EQ(value, value & mask) << "Value contains masked out bits"; |
292 if (sizeof(void*) == 4) { | |
rickyz (no longer on Chrome)
2015/09/03 23:34:11
(Question unrelated to your change): Is there ever
mdempsky
2015/09/04 18:32:09
Agreed that Clang's cross-compiler model is nicer,
| |
293 CHECK_EQ(4U, width) << "Invalid width on 32-bit platform"; | |
294 } | 294 } |
295 } | 295 if (width == 4) { |
296 | 296 CHECK_EQ(0U, mask >> 32) << "Mask exceeds argument size"; |
297 CodeGen::Node PolicyCompiler::CondExpression(const ErrorCode& cond) { | 297 CHECK_EQ(0U, value >> 32) << "Value exceeds argument size"; |
298 // Sanity check that |cond| makes sense. | |
299 CHECK(cond.argno_ >= 0 && cond.argno_ < 6) << "Invalid argument number " | |
300 << cond.argno_; | |
301 CHECK(cond.width_ == ErrorCode::TP_32BIT || | |
302 cond.width_ == ErrorCode::TP_64BIT) | |
303 << "Invalid argument width " << cond.width_; | |
304 CHECK_NE(0U, cond.mask_) << "Zero mask is invalid"; | |
305 CHECK_EQ(cond.value_, cond.value_ & cond.mask_) | |
306 << "Value contains masked out bits"; | |
307 if (sizeof(void*) == 4) { | |
308 CHECK_EQ(ErrorCode::TP_32BIT, cond.width_) | |
309 << "Invalid width on 32-bit platform"; | |
310 } | 298 } |
311 if (cond.width_ == ErrorCode::TP_32BIT) { | |
312 CHECK_EQ(0U, cond.mask_ >> 32) << "Mask exceeds argument size"; | |
313 CHECK_EQ(0U, cond.value_ >> 32) << "Value exceeds argument size"; | |
314 } | |
315 | |
316 CodeGen::Node passed = RetExpression(*cond.passed_); | |
317 CodeGen::Node failed = RetExpression(*cond.failed_); | |
318 | 299 |
319 // We want to emit code to check "(arg & mask) == value" where arg, mask, and | 300 // We want to emit code to check "(arg & mask) == value" where arg, mask, and |
320 // value are 64-bit values, but the BPF machine is only 32-bit. We implement | 301 // value are 64-bit values, but the BPF machine is only 32-bit. We implement |
321 // this by independently testing the upper and lower 32-bits and continuing to | 302 // this by independently testing the upper and lower 32-bits and continuing to |
322 // |passed| if both evaluate true, or to |failed| if either evaluate false. | 303 // |passed| if both evaluate true, or to |failed| if either evaluate false. |
323 return CondExpressionHalf(cond, | 304 return MaskedEqualHalf(argno, width, mask, value, ArgHalf::UPPER, |
324 UpperHalf, | 305 MaskedEqualHalf(argno, width, mask, value, |
325 CondExpressionHalf(cond, LowerHalf, passed, failed), | 306 ArgHalf::LOWER, passed, failed), |
326 failed); | 307 failed); |
327 } | 308 } |
328 | 309 |
329 CodeGen::Node PolicyCompiler::CondExpressionHalf(const ErrorCode& cond, | 310 CodeGen::Node PolicyCompiler::MaskedEqualHalf(int argno, |
330 ArgHalf half, | 311 size_t width, |
331 CodeGen::Node passed, | 312 uint64_t full_mask, |
332 CodeGen::Node failed) { | 313 uint64_t full_value, |
333 if (cond.width_ == ErrorCode::TP_32BIT && half == UpperHalf) { | 314 ArgHalf half, |
315 CodeGen::Node passed, | |
316 CodeGen::Node failed) { | |
317 if (width == 4 && half == ArgHalf::UPPER) { | |
334 // Special logic for sanity checking the upper 32-bits of 32-bit system | 318 // Special logic for sanity checking the upper 32-bits of 32-bit system |
335 // call arguments. | 319 // call arguments. |
336 | 320 |
337 // TODO(mdempsky): Compile Unexpected64bitArgument() just per program. | 321 // TODO(mdempsky): Compile Unexpected64bitArgument() just per program. |
338 CodeGen::Node invalid_64bit = RetExpression(Unexpected64bitArgument()); | 322 CodeGen::Node invalid_64bit = Unexpected64bitArgument(); |
339 | 323 |
340 const uint32_t upper = SECCOMP_ARG_MSB_IDX(cond.argno_); | 324 const uint32_t upper = SECCOMP_ARG_MSB_IDX(argno); |
341 const uint32_t lower = SECCOMP_ARG_LSB_IDX(cond.argno_); | 325 const uint32_t lower = SECCOMP_ARG_LSB_IDX(argno); |
342 | 326 |
343 if (sizeof(void*) == 4) { | 327 if (sizeof(void*) == 4) { |
344 // On 32-bit platforms, the upper 32-bits should always be 0: | 328 // On 32-bit platforms, the upper 32-bits should always be 0: |
345 // LDW [upper] | 329 // LDW [upper] |
346 // JEQ 0, passed, invalid | 330 // JEQ 0, passed, invalid |
347 return gen_.MakeInstruction( | 331 return gen_.MakeInstruction( |
348 BPF_LD + BPF_W + BPF_ABS, | 332 BPF_LD + BPF_W + BPF_ABS, |
349 upper, | 333 upper, |
350 gen_.MakeInstruction( | 334 gen_.MakeInstruction( |
351 BPF_JMP + BPF_JEQ + BPF_K, 0, passed, invalid_64bit)); | 335 BPF_JMP + BPF_JEQ + BPF_K, 0, passed, invalid_64bit)); |
(...skipping 22 matching lines...) Expand all Loading... | |
374 gen_.MakeInstruction( | 358 gen_.MakeInstruction( |
375 BPF_LD + BPF_W + BPF_ABS, | 359 BPF_LD + BPF_W + BPF_ABS, |
376 lower, | 360 lower, |
377 gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, | 361 gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, |
378 1U << 31, | 362 1U << 31, |
379 passed, | 363 passed, |
380 invalid_64bit)), | 364 invalid_64bit)), |
381 invalid_64bit))); | 365 invalid_64bit))); |
382 } | 366 } |
383 | 367 |
384 const uint32_t idx = (half == UpperHalf) ? SECCOMP_ARG_MSB_IDX(cond.argno_) | 368 const uint32_t idx = (half == ArgHalf::UPPER) ? SECCOMP_ARG_MSB_IDX(argno) |
385 : SECCOMP_ARG_LSB_IDX(cond.argno_); | 369 : SECCOMP_ARG_LSB_IDX(argno); |
386 const uint32_t mask = (half == UpperHalf) ? cond.mask_ >> 32 : cond.mask_; | 370 const uint32_t mask = (half == ArgHalf::UPPER) ? full_mask >> 32 : full_mask; |
387 const uint32_t value = (half == UpperHalf) ? cond.value_ >> 32 : cond.value_; | 371 const uint32_t value = |
372 (half == ArgHalf::UPPER) ? full_value >> 32 : full_value; | |
388 | 373 |
389 // Emit a suitable instruction sequence for (arg & mask) == value. | 374 // Emit a suitable instruction sequence for (arg & mask) == value. |
390 | 375 |
391 // For (arg & 0) == 0, just return passed. | 376 // For (arg & 0) == 0, just return passed. |
392 if (mask == 0) { | 377 if (mask == 0) { |
393 CHECK_EQ(0U, value); | 378 CHECK_EQ(0U, value); |
394 return passed; | 379 return passed; |
395 } | 380 } |
396 | 381 |
397 // For (arg & ~0) == value, emit: | 382 // For (arg & ~0) == value, emit: |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
432 return gen_.MakeInstruction( | 417 return gen_.MakeInstruction( |
433 BPF_LD + BPF_W + BPF_ABS, | 418 BPF_LD + BPF_W + BPF_ABS, |
434 idx, | 419 idx, |
435 gen_.MakeInstruction( | 420 gen_.MakeInstruction( |
436 BPF_ALU + BPF_AND + BPF_K, | 421 BPF_ALU + BPF_AND + BPF_K, |
437 mask, | 422 mask, |
438 gen_.MakeInstruction( | 423 gen_.MakeInstruction( |
439 BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed))); | 424 BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed))); |
440 } | 425 } |
441 | 426 |
442 ErrorCode PolicyCompiler::Unexpected64bitArgument() { | 427 CodeGen::Node PolicyCompiler::Unexpected64bitArgument() { |
443 return panic_func_("Unexpected 64bit argument detected")->Compile(this); | 428 return CompileResult(panic_func_("Unexpected 64bit argument detected")); |
444 } | 429 } |
445 | 430 |
446 ErrorCode PolicyCompiler::Error(int err) { | 431 CodeGen::Node PolicyCompiler::Return(uint32_t ret) { |
447 if (has_unsafe_traps_) { | 432 if (has_unsafe_traps_ && (ret & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { |
448 // When inside an UnsafeTrap() callback, we want to allow all system calls. | 433 // When inside an UnsafeTrap() callback, we want to allow all system calls. |
449 // This means, we must conditionally disable the sandbox -- and that's not | 434 // This means, we must conditionally disable the sandbox -- and that's not |
450 // something that kernel-side BPF filters can do, as they cannot inspect | 435 // something that kernel-side BPF filters can do, as they cannot inspect |
451 // any state other than the syscall arguments. | 436 // any state other than the syscall arguments. |
452 // But if we redirect all error handlers to user-space, then we can easily | 437 // But if we redirect all error handlers to user-space, then we can easily |
453 // make this decision. | 438 // make this decision. |
454 // The performance penalty for this extra round-trip to user-space is not | 439 // The performance penalty for this extra round-trip to user-space is not |
455 // actually that bad, as we only ever pay it for denied system calls; and a | 440 // actually that bad, as we only ever pay it for denied system calls; and a |
456 // typical program has very few of these. | 441 // typical program has very few of these. |
457 return Trap(ReturnErrno, reinterpret_cast<void*>(err), true); | 442 return Trap(ReturnErrno, reinterpret_cast<void*>(ret & SECCOMP_RET_DATA), |
443 true); | |
458 } | 444 } |
459 | 445 |
460 return ErrorCode(err); | 446 return gen_.MakeInstruction(BPF_RET + BPF_K, ret); |
461 } | 447 } |
462 | 448 |
463 ErrorCode PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc, | 449 CodeGen::Node PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc, |
464 const void* aux, | 450 const void* aux, |
465 bool safe) { | 451 bool safe) { |
466 uint16_t trap_id = registry_->Add(fnc, aux, safe); | 452 uint16_t trap_id = registry_->Add(fnc, aux, safe); |
467 return ErrorCode(trap_id, fnc, aux, safe); | 453 return gen_.MakeInstruction(BPF_RET + BPF_K, SECCOMP_RET_TRAP + trap_id); |
468 } | 454 } |
469 | 455 |
470 bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) { | 456 bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) { |
471 for (size_t i = 0; i < arraysize(kSyscallsRequiredForUnsafeTraps); ++i) { | 457 for (size_t i = 0; i < arraysize(kSyscallsRequiredForUnsafeTraps); ++i) { |
472 if (sysno == kSyscallsRequiredForUnsafeTraps[i]) { | 458 if (sysno == kSyscallsRequiredForUnsafeTraps[i]) { |
473 return true; | 459 return true; |
474 } | 460 } |
475 } | 461 } |
476 return false; | 462 return false; |
477 } | 463 } |
478 | 464 |
479 ErrorCode PolicyCompiler::CondMaskedEqual(int argno, | |
480 ErrorCode::ArgType width, | |
481 uint64_t mask, | |
482 uint64_t value, | |
483 const ErrorCode& passed, | |
484 const ErrorCode& failed) { | |
485 return ErrorCode(argno, | |
486 width, | |
487 mask, | |
488 value, | |
489 &*conds_.insert(passed).first, | |
490 &*conds_.insert(failed).first); | |
491 } | |
492 | |
493 } // namespace bpf_dsl | 465 } // namespace bpf_dsl |
494 } // namespace sandbox | 466 } // namespace sandbox |
OLD | NEW |