Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: sandbox/linux/bpf_dsl/policy_compiler.cc

Issue 1001833005: Update from https://crrev.com/320343 (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Supress Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sandbox/linux/bpf_dsl/policy_compiler.h ('k') | sandbox/linux/bpf_dsl/seccomp_macros.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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/dump_bpf.h"
18 #include "sandbox/linux/bpf_dsl/policy.h" 19 #include "sandbox/linux/bpf_dsl/policy.h"
19 #include "sandbox/linux/bpf_dsl/seccomp_macros.h" 20 #include "sandbox/linux/bpf_dsl/seccomp_macros.h"
20 #include "sandbox/linux/bpf_dsl/syscall_set.h" 21 #include "sandbox/linux/bpf_dsl/syscall_set.h"
21 #include "sandbox/linux/seccomp-bpf/die.h" 22 #include "sandbox/linux/bpf_dsl/verifier.h"
22 #include "sandbox/linux/seccomp-bpf/errorcode.h" 23 #include "sandbox/linux/seccomp-bpf/errorcode.h"
23 #include "sandbox/linux/seccomp-bpf/syscall.h"
24 #include "sandbox/linux/system_headers/linux_seccomp.h" 24 #include "sandbox/linux/system_headers/linux_seccomp.h"
25 25
26 namespace sandbox { 26 namespace sandbox {
27 namespace bpf_dsl { 27 namespace bpf_dsl {
28 28
29 namespace { 29 namespace {
30 30
31 #if defined(__i386__) || defined(__x86_64__) 31 #if defined(__i386__) || defined(__x86_64__)
32 const bool kIsIntel = true; 32 const bool kIsIntel = true;
33 #else 33 #else
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 } // namespace 79 } // namespace
80 80
81 struct PolicyCompiler::Range { 81 struct PolicyCompiler::Range {
82 uint32_t from; 82 uint32_t from;
83 CodeGen::Node node; 83 CodeGen::Node node;
84 }; 84 };
85 85
86 PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry) 86 PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry)
87 : policy_(policy), 87 : policy_(policy),
88 registry_(registry), 88 registry_(registry),
89 escapepc_(0),
89 conds_(), 90 conds_(),
90 gen_(), 91 gen_(),
91 has_unsafe_traps_(HasUnsafeTraps(policy_)) { 92 has_unsafe_traps_(HasUnsafeTraps(policy_)) {
92 DCHECK(policy); 93 DCHECK(policy);
93 } 94 }
94 95
95 PolicyCompiler::~PolicyCompiler() { 96 PolicyCompiler::~PolicyCompiler() {
96 } 97 }
97 98
98 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile() { 99 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile(bool verify) {
99 if (!policy_->InvalidSyscall()->IsDeny()) { 100 CHECK(policy_->InvalidSyscall()->IsDeny())
100 SANDBOX_DIE("Policies should deny invalid system calls."); 101 << "Policies should deny invalid system calls";
101 }
102 102
103 // If our BPF program has unsafe traps, enable support for them. 103 // If our BPF program has unsafe traps, enable support for them.
104 if (has_unsafe_traps_) { 104 if (has_unsafe_traps_) {
105 // As support for unsafe jumps essentially defeats all the security 105 CHECK_NE(0U, escapepc_) << "UnsafeTrap() requires a valid escape PC";
106 // measures that the sandbox provides, we print a big warning message -- 106
107 // and of course, we make sure to only ever enable this feature if it 107 for (int sysnum : kSyscallsRequiredForUnsafeTraps) {
108 // is actually requested by the sandbox policy. 108 CHECK(policy_->EvaluateSyscall(sysnum)->IsAllow())
109 if (Syscall::Call(-1) == -1 && errno == ENOSYS) { 109 << "Policies that use UnsafeTrap() must unconditionally allow all "
110 SANDBOX_DIE( 110 "required system calls";
111 "Support for UnsafeTrap() has not yet been ported to this "
112 "architecture");
113 } 111 }
114 112
115 for (int sysnum : kSyscallsRequiredForUnsafeTraps) { 113 CHECK(registry_->EnableUnsafeTraps())
116 if (!policy_->EvaluateSyscall(sysnum)->IsAllow()) { 114 << "We'd rather die than enable unsafe traps";
117 SANDBOX_DIE(
118 "Policies that use UnsafeTrap() must unconditionally allow all "
119 "required system calls");
120 }
121 }
122
123 if (!registry_->EnableUnsafeTraps()) {
124 // We should never be able to get here, as UnsafeTrap() should never
125 // actually return a valid ErrorCode object unless the user set the
126 // CHROME_SANDBOX_DEBUGGING environment variable; and therefore,
127 // "has_unsafe_traps" would always be false. But better double-check
128 // than enabling dangerous code.
129 SANDBOX_DIE("We'd rather die than enable unsafe traps");
130 }
131 } 115 }
132 116
133 // Assemble the BPF filter program. 117 // Assemble the BPF filter program.
134 scoped_ptr<CodeGen::Program> program(new CodeGen::Program()); 118 scoped_ptr<CodeGen::Program> program(new CodeGen::Program());
135 gen_.Compile(AssemblePolicy(), program.get()); 119 gen_.Compile(AssemblePolicy(), program.get());
120
121 // Make sure compilation resulted in a BPF program that executes
122 // correctly. Otherwise, there is an internal error in our BPF compiler.
123 // There is really nothing the caller can do until the bug is fixed.
124 if (verify) {
125 const char* err = nullptr;
126 if (!Verifier::VerifyBPF(this, *program, *policy_, &err)) {
127 DumpBPF::PrintProgram(*program);
128 LOG(FATAL) << err;
129 }
130 }
131
136 return program.Pass(); 132 return program.Pass();
137 } 133 }
138 134
135 void PolicyCompiler::DangerousSetEscapePC(uint64_t escapepc) {
136 escapepc_ = escapepc;
137 }
138
139 CodeGen::Node PolicyCompiler::AssemblePolicy() { 139 CodeGen::Node PolicyCompiler::AssemblePolicy() {
140 // A compiled policy consists of three logical parts: 140 // A compiled policy consists of three logical parts:
141 // 1. Check that the "arch" field matches the expected architecture. 141 // 1. Check that the "arch" field matches the expected architecture.
142 // 2. If the policy involves unsafe traps, check if the syscall was 142 // 2. If the policy involves unsafe traps, check if the syscall was
143 // invoked by Syscall::Call, and then allow it unconditionally. 143 // invoked by Syscall::Call, and then allow it unconditionally.
144 // 3. Check the system call number and jump to the appropriate compiled 144 // 3. Check the system call number and jump to the appropriate compiled
145 // system call policy number. 145 // system call policy number.
146 return CheckArch(MaybeAddEscapeHatch(DispatchSyscall())); 146 return CheckArch(MaybeAddEscapeHatch(DispatchSyscall()));
147 } 147 }
148 148
149 CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) { 149 CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) {
150 // If the architecture doesn't match SECCOMP_ARCH, disallow the 150 // If the architecture doesn't match SECCOMP_ARCH, disallow the
151 // system call. 151 // system call.
152 return gen_.MakeInstruction( 152 return gen_.MakeInstruction(
153 BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARCH_IDX, 153 BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARCH_IDX,
154 gen_.MakeInstruction( 154 gen_.MakeInstruction(
155 BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_ARCH, passed, 155 BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_ARCH, passed,
156 CompileResult(Kill("Invalid audit architecture in BPF filter")))); 156 CompileResult(Kill("Invalid audit architecture in BPF filter"))));
157 } 157 }
158 158
159 CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) { 159 CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) {
160 // If no unsafe traps, then simply return |rest|. 160 // If no unsafe traps, then simply return |rest|.
161 if (!has_unsafe_traps_) { 161 if (!has_unsafe_traps_) {
162 return rest; 162 return rest;
163 } 163 }
164 164
165 // Allow system calls, if they originate from our magic return address 165 // We already enabled unsafe traps in Compile, but enable them again to give
166 // (which we can query by calling Syscall::Call(-1)). 166 // the trap registry a second chance to complain before we add the backdoor.
167 uint64_t syscall_entry_point = 167 CHECK(registry_->EnableUnsafeTraps());
168 static_cast<uint64_t>(static_cast<uintptr_t>(Syscall::Call(-1))); 168
169 uint32_t low = static_cast<uint32_t>(syscall_entry_point); 169 // Allow system calls, if they originate from our magic return address.
170 uint32_t hi = static_cast<uint32_t>(syscall_entry_point >> 32); 170 const uint32_t lopc = static_cast<uint32_t>(escapepc_);
171 const uint32_t hipc = static_cast<uint32_t>(escapepc_ >> 32);
171 172
172 // BPF cannot do native 64-bit comparisons, so we have to compare 173 // BPF cannot do native 64-bit comparisons, so we have to compare
173 // both 32-bit halves of the instruction pointer. If they match what 174 // both 32-bit halves of the instruction pointer. If they match what
174 // we expect, we return ERR_ALLOWED. If either or both don't match, 175 // we expect, we return ERR_ALLOWED. If either or both don't match,
175 // we continue evalutating the rest of the sandbox policy. 176 // we continue evalutating the rest of the sandbox policy.
176 // 177 //
177 // For simplicity, we check the full 64-bit instruction pointer even 178 // For simplicity, we check the full 64-bit instruction pointer even
178 // on 32-bit architectures. 179 // on 32-bit architectures.
179 return gen_.MakeInstruction( 180 return gen_.MakeInstruction(
180 BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_LSB_IDX, 181 BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_LSB_IDX,
181 gen_.MakeInstruction( 182 gen_.MakeInstruction(
182 BPF_JMP + BPF_JEQ + BPF_K, low, 183 BPF_JMP + BPF_JEQ + BPF_K, lopc,
183 gen_.MakeInstruction( 184 gen_.MakeInstruction(
184 BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX, 185 BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX,
185 gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hi, 186 gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hipc,
186 CompileResult(Allow()), rest)), 187 CompileResult(Allow()), rest)),
187 rest)); 188 rest));
188 } 189 }
189 190
190 CodeGen::Node PolicyCompiler::DispatchSyscall() { 191 CodeGen::Node PolicyCompiler::DispatchSyscall() {
191 // Evaluate all possible system calls and group their ErrorCodes into 192 // Evaluate all possible system calls and group their ErrorCodes into
192 // ranges of identical codes. 193 // ranges of identical codes.
193 Ranges ranges; 194 Ranges ranges;
194 FindRanges(&ranges); 195 FindRanges(&ranges);
195 196
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
252 } 253 }
253 ranges->push_back(Range{old_sysnum, old_node}); 254 ranges->push_back(Range{old_sysnum, old_node});
254 } 255 }
255 256
256 CodeGen::Node PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start, 257 CodeGen::Node PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start,
257 Ranges::const_iterator stop) { 258 Ranges::const_iterator stop) {
258 // We convert the list of system call ranges into jump table that performs 259 // We convert the list of system call ranges into jump table that performs
259 // a binary search over the ranges. 260 // a binary search over the ranges.
260 // As a sanity check, we need to have at least one distinct ranges for us 261 // As a sanity check, we need to have at least one distinct ranges for us
261 // to be able to build a jump table. 262 // to be able to build a jump table.
262 if (stop - start <= 0) { 263 CHECK(start < stop) << "Invalid iterator range";
263 SANDBOX_DIE("Invalid set of system call ranges"); 264 const auto n = stop - start;
264 } else if (stop - start == 1) { 265 if (n == 1) {
265 // If we have narrowed things down to a single range object, we can 266 // If we have narrowed things down to a single range object, we can
266 // return from the BPF filter program. 267 // return from the BPF filter program.
267 return start->node; 268 return start->node;
268 } 269 }
269 270
270 // Pick the range object that is located at the mid point of our list. 271 // Pick the range object that is located at the mid point of our list.
271 // We compare our system call number against the lowest valid system call 272 // We compare our system call number against the lowest valid system call
272 // number in this range object. If our number is lower, it is outside of 273 // number in this range object. If our number is lower, it is outside of
273 // this range object. If it is greater or equal, it might be inside. 274 // this range object. If it is greater or equal, it might be inside.
274 Ranges::const_iterator mid = start + (stop - start) / 2; 275 Ranges::const_iterator mid = start + n / 2;
275 276
276 // Sub-divide the list of ranges and continue recursively. 277 // Sub-divide the list of ranges and continue recursively.
277 CodeGen::Node jf = AssembleJumpTable(start, mid); 278 CodeGen::Node jf = AssembleJumpTable(start, mid);
278 CodeGen::Node jt = AssembleJumpTable(mid, stop); 279 CodeGen::Node jt = AssembleJumpTable(mid, stop);
279 return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf); 280 return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf);
280 } 281 }
281 282
282 CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) { 283 CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) {
283 return RetExpression(res->Compile(this)); 284 return RetExpression(res->Compile(this));
284 } 285 }
285 286
286 CodeGen::Node PolicyCompiler::RetExpression(const ErrorCode& err) { 287 CodeGen::Node PolicyCompiler::RetExpression(const ErrorCode& err) {
287 switch (err.error_type()) { 288 switch (err.error_type()) {
288 case ErrorCode::ET_COND: 289 case ErrorCode::ET_COND:
289 return CondExpression(err); 290 return CondExpression(err);
290 case ErrorCode::ET_SIMPLE: 291 case ErrorCode::ET_SIMPLE:
291 case ErrorCode::ET_TRAP: 292 case ErrorCode::ET_TRAP:
292 return gen_.MakeInstruction(BPF_RET + BPF_K, err.err()); 293 return gen_.MakeInstruction(BPF_RET + BPF_K, err.err());
293 default: 294 default:
294 SANDBOX_DIE("ErrorCode is not suitable for returning from a BPF program"); 295 LOG(FATAL)
296 << "ErrorCode is not suitable for returning from a BPF program";
297 return CodeGen::kNullNode;
295 } 298 }
296 } 299 }
297 300
298 CodeGen::Node PolicyCompiler::CondExpression(const ErrorCode& cond) { 301 CodeGen::Node PolicyCompiler::CondExpression(const ErrorCode& cond) {
299 // Sanity check that |cond| makes sense. 302 // Sanity check that |cond| makes sense.
300 if (cond.argno_ < 0 || cond.argno_ >= 6) { 303 CHECK(cond.argno_ >= 0 && cond.argno_ < 6) << "Invalid argument number "
301 SANDBOX_DIE("sandbox_bpf: invalid argument number"); 304 << cond.argno_;
305 CHECK(cond.width_ == ErrorCode::TP_32BIT ||
306 cond.width_ == ErrorCode::TP_64BIT)
307 << "Invalid argument width " << cond.width_;
308 CHECK_NE(0U, cond.mask_) << "Zero mask is invalid";
309 CHECK_EQ(cond.value_, cond.value_ & cond.mask_)
310 << "Value contains masked out bits";
311 if (sizeof(void*) == 4) {
312 CHECK_EQ(ErrorCode::TP_32BIT, cond.width_)
313 << "Invalid width on 32-bit platform";
302 } 314 }
303 if (cond.width_ != ErrorCode::TP_32BIT && 315 if (cond.width_ == ErrorCode::TP_32BIT) {
304 cond.width_ != ErrorCode::TP_64BIT) { 316 CHECK_EQ(0U, cond.mask_ >> 32) << "Mask exceeds argument size";
305 SANDBOX_DIE("sandbox_bpf: invalid argument width"); 317 CHECK_EQ(0U, cond.value_ >> 32) << "Value exceeds argument size";
306 } 318 }
307 if (cond.mask_ == 0) {
308 SANDBOX_DIE("sandbox_bpf: zero mask is invalid");
309 }
310 if ((cond.value_ & cond.mask_) != cond.value_) {
311 SANDBOX_DIE("sandbox_bpf: value contains masked out bits");
312 }
313 if (cond.width_ == ErrorCode::TP_32BIT &&
314 ((cond.mask_ >> 32) != 0 || (cond.value_ >> 32) != 0)) {
315 SANDBOX_DIE("sandbox_bpf: test exceeds argument size");
316 }
317 // TODO(mdempsky): Reject TP_64BIT on 32-bit platforms. For now we allow it
318 // because some SandboxBPF unit tests exercise it.
319 319
320 CodeGen::Node passed = RetExpression(*cond.passed_); 320 CodeGen::Node passed = RetExpression(*cond.passed_);
321 CodeGen::Node failed = RetExpression(*cond.failed_); 321 CodeGen::Node failed = RetExpression(*cond.failed_);
322 322
323 // We want to emit code to check "(arg & mask) == value" where arg, mask, and 323 // We want to emit code to check "(arg & mask) == value" where arg, mask, and
324 // value are 64-bit values, but the BPF machine is only 32-bit. We implement 324 // value are 64-bit values, but the BPF machine is only 32-bit. We implement
325 // this by independently testing the upper and lower 32-bits and continuing to 325 // this by independently testing the upper and lower 32-bits and continuing to
326 // |passed| if both evaluate true, or to |failed| if either evaluate false. 326 // |passed| if both evaluate true, or to |failed| if either evaluate false.
327 return CondExpressionHalf(cond, 327 return CondExpressionHalf(cond,
328 UpperHalf, 328 UpperHalf,
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
489 return ErrorCode(argno, 489 return ErrorCode(argno,
490 width, 490 width,
491 mask, 491 mask,
492 value, 492 value,
493 &*conds_.insert(passed).first, 493 &*conds_.insert(passed).first,
494 &*conds_.insert(failed).first); 494 &*conds_.insert(failed).first);
495 } 495 }
496 496
497 } // namespace bpf_dsl 497 } // namespace bpf_dsl
498 } // namespace sandbox 498 } // namespace sandbox
OLDNEW
« no previous file with comments | « sandbox/linux/bpf_dsl/policy_compiler.h ('k') | sandbox/linux/bpf_dsl/seccomp_macros.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698