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 |
11 #include <limits> | 11 #include <limits> |
12 | 12 |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "base/macros.h" | 14 #include "base/macros.h" |
15 #include "sandbox/linux/bpf_dsl/bpf_dsl.h" | 15 #include "sandbox/linux/bpf_dsl/bpf_dsl.h" |
16 #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" | 16 #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" |
17 #include "sandbox/linux/bpf_dsl/codegen.h" | 17 #include "sandbox/linux/bpf_dsl/codegen.h" |
18 #include "sandbox/linux/bpf_dsl/policy.h" | 18 #include "sandbox/linux/bpf_dsl/policy.h" |
19 #include "sandbox/linux/bpf_dsl/seccomp_macros.h" | 19 #include "sandbox/linux/bpf_dsl/seccomp_macros.h" |
20 #include "sandbox/linux/bpf_dsl/syscall_set.h" | 20 #include "sandbox/linux/bpf_dsl/syscall_set.h" |
21 #include "sandbox/linux/seccomp-bpf/die.h" | |
22 #include "sandbox/linux/seccomp-bpf/errorcode.h" | 21 #include "sandbox/linux/seccomp-bpf/errorcode.h" |
23 #include "sandbox/linux/system_headers/linux_seccomp.h" | 22 #include "sandbox/linux/system_headers/linux_seccomp.h" |
24 | 23 |
25 namespace sandbox { | 24 namespace sandbox { |
26 namespace bpf_dsl { | 25 namespace bpf_dsl { |
27 | 26 |
28 namespace { | 27 namespace { |
29 | 28 |
30 #if defined(__i386__) || defined(__x86_64__) | 29 #if defined(__i386__) || defined(__x86_64__) |
31 const bool kIsIntel = true; | 30 const bool kIsIntel = true; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
89 conds_(), | 88 conds_(), |
90 gen_(), | 89 gen_(), |
91 has_unsafe_traps_(HasUnsafeTraps(policy_)) { | 90 has_unsafe_traps_(HasUnsafeTraps(policy_)) { |
92 DCHECK(policy); | 91 DCHECK(policy); |
93 } | 92 } |
94 | 93 |
95 PolicyCompiler::~PolicyCompiler() { | 94 PolicyCompiler::~PolicyCompiler() { |
96 } | 95 } |
97 | 96 |
98 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile() { | 97 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile() { |
99 if (!policy_->InvalidSyscall()->IsDeny()) { | 98 CHECK(policy_->InvalidSyscall()->IsDeny()) |
100 SANDBOX_DIE("Policies should deny invalid system calls."); | 99 << "Policies should deny invalid system calls"; |
101 } | |
102 | 100 |
103 // If our BPF program has unsafe traps, enable support for them. | 101 // If our BPF program has unsafe traps, enable support for them. |
104 if (has_unsafe_traps_) { | 102 if (has_unsafe_traps_) { |
105 // As support for unsafe jumps essentially defeats all the security | |
106 // measures that the sandbox provides, we print a big warning message -- | |
107 // and of course, we make sure to only ever enable this feature if it | |
108 // is actually requested by the sandbox policy. | |
109 | |
110 CHECK_NE(0U, escapepc_) << "UnsafeTrap() requires a valid escape PC"; | 103 CHECK_NE(0U, escapepc_) << "UnsafeTrap() requires a valid escape PC"; |
111 | 104 |
112 for (int sysnum : kSyscallsRequiredForUnsafeTraps) { | 105 for (int sysnum : kSyscallsRequiredForUnsafeTraps) { |
113 if (!policy_->EvaluateSyscall(sysnum)->IsAllow()) { | 106 CHECK(policy_->EvaluateSyscall(sysnum)->IsAllow()) |
114 SANDBOX_DIE( | 107 << "Policies that use UnsafeTrap() must unconditionally allow all " |
115 "Policies that use UnsafeTrap() must unconditionally allow all " | 108 "required system calls"; |
116 "required system calls"); | |
117 } | |
118 } | 109 } |
119 | 110 |
120 if (!registry_->EnableUnsafeTraps()) { | 111 CHECK(registry_->EnableUnsafeTraps()) |
121 // We should never be able to get here, as UnsafeTrap() should never | 112 << "We'd rather die than enable unsafe traps"; |
122 // actually return a valid ErrorCode object unless the user set the | |
123 // CHROME_SANDBOX_DEBUGGING environment variable; and therefore, | |
124 // "has_unsafe_traps" would always be false. But better double-check | |
125 // than enabling dangerous code. | |
126 SANDBOX_DIE("We'd rather die than enable unsafe traps"); | |
127 } | |
128 } | 113 } |
129 | 114 |
130 // Assemble the BPF filter program. | 115 // Assemble the BPF filter program. |
131 scoped_ptr<CodeGen::Program> program(new CodeGen::Program()); | 116 scoped_ptr<CodeGen::Program> program(new CodeGen::Program()); |
132 gen_.Compile(AssemblePolicy(), program.get()); | 117 gen_.Compile(AssemblePolicy(), program.get()); |
133 return program.Pass(); | 118 return program.Pass(); |
134 } | 119 } |
135 | 120 |
136 void PolicyCompiler::DangerousSetEscapePC(uint64_t escapepc) { | 121 void PolicyCompiler::DangerousSetEscapePC(uint64_t escapepc) { |
137 escapepc_ = escapepc; | 122 escapepc_ = escapepc; |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
254 } | 239 } |
255 ranges->push_back(Range{old_sysnum, old_node}); | 240 ranges->push_back(Range{old_sysnum, old_node}); |
256 } | 241 } |
257 | 242 |
258 CodeGen::Node PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start, | 243 CodeGen::Node PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start, |
259 Ranges::const_iterator stop) { | 244 Ranges::const_iterator stop) { |
260 // We convert the list of system call ranges into jump table that performs | 245 // We convert the list of system call ranges into jump table that performs |
261 // a binary search over the ranges. | 246 // a binary search over the ranges. |
262 // As a sanity check, we need to have at least one distinct ranges for us | 247 // As a sanity check, we need to have at least one distinct ranges for us |
263 // to be able to build a jump table. | 248 // to be able to build a jump table. |
264 if (stop - start <= 0) { | 249 CHECK(start < stop) << "Invalid iterator range"; |
265 SANDBOX_DIE("Invalid set of system call ranges"); | 250 const auto n = stop - start; |
266 } else if (stop - start == 1) { | 251 if (n == 1) { |
267 // If we have narrowed things down to a single range object, we can | 252 // If we have narrowed things down to a single range object, we can |
268 // return from the BPF filter program. | 253 // return from the BPF filter program. |
269 return start->node; | 254 return start->node; |
270 } | 255 } |
271 | 256 |
272 // Pick the range object that is located at the mid point of our list. | 257 // Pick the range object that is located at the mid point of our list. |
273 // We compare our system call number against the lowest valid system call | 258 // We compare our system call number against the lowest valid system call |
274 // number in this range object. If our number is lower, it is outside of | 259 // number in this range object. If our number is lower, it is outside of |
275 // this range object. If it is greater or equal, it might be inside. | 260 // this range object. If it is greater or equal, it might be inside. |
276 Ranges::const_iterator mid = start + (stop - start) / 2; | 261 Ranges::const_iterator mid = start + n / 2; |
277 | 262 |
278 // Sub-divide the list of ranges and continue recursively. | 263 // Sub-divide the list of ranges and continue recursively. |
279 CodeGen::Node jf = AssembleJumpTable(start, mid); | 264 CodeGen::Node jf = AssembleJumpTable(start, mid); |
280 CodeGen::Node jt = AssembleJumpTable(mid, stop); | 265 CodeGen::Node jt = AssembleJumpTable(mid, stop); |
281 return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf); | 266 return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf); |
282 } | 267 } |
283 | 268 |
284 CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) { | 269 CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) { |
285 return RetExpression(res->Compile(this)); | 270 return RetExpression(res->Compile(this)); |
286 } | 271 } |
287 | 272 |
288 CodeGen::Node PolicyCompiler::RetExpression(const ErrorCode& err) { | 273 CodeGen::Node PolicyCompiler::RetExpression(const ErrorCode& err) { |
289 switch (err.error_type()) { | 274 switch (err.error_type()) { |
290 case ErrorCode::ET_COND: | 275 case ErrorCode::ET_COND: |
291 return CondExpression(err); | 276 return CondExpression(err); |
292 case ErrorCode::ET_SIMPLE: | 277 case ErrorCode::ET_SIMPLE: |
293 case ErrorCode::ET_TRAP: | 278 case ErrorCode::ET_TRAP: |
294 return gen_.MakeInstruction(BPF_RET + BPF_K, err.err()); | 279 return gen_.MakeInstruction(BPF_RET + BPF_K, err.err()); |
295 default: | 280 default: |
296 SANDBOX_DIE("ErrorCode is not suitable for returning from a BPF program"); | 281 LOG(FATAL) |
282 << "ErrorCode is not suitable for returning from a BPF program"; | |
283 return CodeGen::kNullNode; | |
297 } | 284 } |
298 } | 285 } |
299 | 286 |
300 CodeGen::Node PolicyCompiler::CondExpression(const ErrorCode& cond) { | 287 CodeGen::Node PolicyCompiler::CondExpression(const ErrorCode& cond) { |
301 // Sanity check that |cond| makes sense. | 288 // Sanity check that |cond| makes sense. |
302 if (cond.argno_ < 0 || cond.argno_ >= 6) { | 289 CHECK(cond.argno_ >= 0 && cond.argno_ < 6) << "Invalid argument number " |
303 SANDBOX_DIE("sandbox_bpf: invalid argument number"); | 290 << cond.argno_; |
291 CHECK(cond.width_ == ErrorCode::TP_32BIT || | |
292 cond.width_ == ErrorCode::TP_64BIT) | |
293 << "Invalid argument width " << cond.width_; | |
294 CHECK_NE(0U, cond.mask_) << "Zero mask is invalid"; | |
295 CHECK_EQ(cond.value_, cond.value_ & cond.mask_) | |
296 << "Value contains masked out bits"; | |
297 if (sizeof(void*) == 4) { | |
298 CHECK_EQ(ErrorCode::TP_32BIT, cond.width_) | |
299 << "Invalid width on 32-bit platform"; | |
304 } | 300 } |
305 if (cond.width_ != ErrorCode::TP_32BIT && | 301 if (cond.width_ == ErrorCode::TP_32BIT) { |
306 cond.width_ != ErrorCode::TP_64BIT) { | 302 CHECK_EQ(0U, cond.mask_ >> 32) << "Mask exceeds argument size"; |
307 SANDBOX_DIE("sandbox_bpf: invalid argument width"); | 303 CHECK_EQ(0U, cond.value_ >> 32) << "Value exceeds argument size"; |
308 } | 304 } |
309 if (cond.mask_ == 0) { | |
310 SANDBOX_DIE("sandbox_bpf: zero mask is invalid"); | |
311 } | |
312 if ((cond.value_ & cond.mask_) != cond.value_) { | |
313 SANDBOX_DIE("sandbox_bpf: value contains masked out bits"); | |
314 } | |
315 if (cond.width_ == ErrorCode::TP_32BIT && | |
316 ((cond.mask_ >> 32) != 0 || (cond.value_ >> 32) != 0)) { | |
317 SANDBOX_DIE("sandbox_bpf: test exceeds argument size"); | |
318 } | |
319 // TODO(mdempsky): Reject TP_64BIT on 32-bit platforms. For now we allow it | |
320 // because some SandboxBPF unit tests exercise it. | |
jln (very slow on Chromium)
2015/02/20 07:00:44
So these disappeared? Cool, but I don't remember :
| |
321 | 305 |
322 CodeGen::Node passed = RetExpression(*cond.passed_); | 306 CodeGen::Node passed = RetExpression(*cond.passed_); |
323 CodeGen::Node failed = RetExpression(*cond.failed_); | 307 CodeGen::Node failed = RetExpression(*cond.failed_); |
324 | 308 |
325 // We want to emit code to check "(arg & mask) == value" where arg, mask, and | 309 // We want to emit code to check "(arg & mask) == value" where arg, mask, and |
326 // value are 64-bit values, but the BPF machine is only 32-bit. We implement | 310 // value are 64-bit values, but the BPF machine is only 32-bit. We implement |
327 // this by independently testing the upper and lower 32-bits and continuing to | 311 // this by independently testing the upper and lower 32-bits and continuing to |
328 // |passed| if both evaluate true, or to |failed| if either evaluate false. | 312 // |passed| if both evaluate true, or to |failed| if either evaluate false. |
329 return CondExpressionHalf(cond, | 313 return CondExpressionHalf(cond, |
330 UpperHalf, | 314 UpperHalf, |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
491 return ErrorCode(argno, | 475 return ErrorCode(argno, |
492 width, | 476 width, |
493 mask, | 477 mask, |
494 value, | 478 value, |
495 &*conds_.insert(passed).first, | 479 &*conds_.insert(passed).first, |
496 &*conds_.insert(failed).first); | 480 &*conds_.insert(failed).first); |
497 } | 481 } |
498 | 482 |
499 } // namespace bpf_dsl | 483 } // namespace bpf_dsl |
500 } // namespace sandbox | 484 } // namespace sandbox |
OLD | NEW |