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/policy.h" | 17 #include "sandbox/linux/bpf_dsl/policy.h" |
18 #include "sandbox/linux/seccomp-bpf/codegen.h" | 18 #include "sandbox/linux/seccomp-bpf/codegen.h" |
19 #include "sandbox/linux/seccomp-bpf/die.h" | 19 #include "sandbox/linux/seccomp-bpf/die.h" |
20 #include "sandbox/linux/seccomp-bpf/errorcode.h" | 20 #include "sandbox/linux/seccomp-bpf/errorcode.h" |
21 #include "sandbox/linux/seccomp-bpf/instruction.h" | |
22 #include "sandbox/linux/seccomp-bpf/linux_seccomp.h" | 21 #include "sandbox/linux/seccomp-bpf/linux_seccomp.h" |
23 #include "sandbox/linux/seccomp-bpf/syscall.h" | 22 #include "sandbox/linux/seccomp-bpf/syscall.h" |
24 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" | 23 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" |
25 | 24 |
26 namespace sandbox { | 25 namespace sandbox { |
27 namespace bpf_dsl { | 26 namespace bpf_dsl { |
28 | 27 |
29 namespace { | 28 namespace { |
30 | 29 |
31 #if defined(__i386__) || defined(__x86_64__) | 30 #if defined(__i386__) || defined(__x86_64__) |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
139 SANDBOX_DIE("We'd rather die than enable unsafe traps"); | 138 SANDBOX_DIE("We'd rather die than enable unsafe traps"); |
140 } | 139 } |
141 } | 140 } |
142 | 141 |
143 // Assemble the BPF filter program. | 142 // Assemble the BPF filter program. |
144 scoped_ptr<CodeGen::Program> program(new CodeGen::Program()); | 143 scoped_ptr<CodeGen::Program> program(new CodeGen::Program()); |
145 gen_.Compile(AssemblePolicy(), program.get()); | 144 gen_.Compile(AssemblePolicy(), program.get()); |
146 return program.Pass(); | 145 return program.Pass(); |
147 } | 146 } |
148 | 147 |
149 Instruction* PolicyCompiler::AssemblePolicy() { | 148 CodeGen::Node PolicyCompiler::AssemblePolicy() { |
150 // A compiled policy consists of three logical parts: | 149 // A compiled policy consists of three logical parts: |
151 // 1. Check that the "arch" field matches the expected architecture. | 150 // 1. Check that the "arch" field matches the expected architecture. |
152 // 2. If the policy involves unsafe traps, check if the syscall was | 151 // 2. If the policy involves unsafe traps, check if the syscall was |
153 // invoked by Syscall::Call, and then allow it unconditionally. | 152 // invoked by Syscall::Call, and then allow it unconditionally. |
154 // 3. Check the system call number and jump to the appropriate compiled | 153 // 3. Check the system call number and jump to the appropriate compiled |
155 // system call policy number. | 154 // system call policy number. |
156 return CheckArch(MaybeAddEscapeHatch(DispatchSyscall())); | 155 return CheckArch(MaybeAddEscapeHatch(DispatchSyscall())); |
157 } | 156 } |
158 | 157 |
159 Instruction* PolicyCompiler::CheckArch(Instruction* passed) { | 158 CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) { |
160 // If the architecture doesn't match SECCOMP_ARCH, disallow the | 159 // If the architecture doesn't match SECCOMP_ARCH, disallow the |
161 // system call. | 160 // system call. |
162 return gen_.MakeInstruction( | 161 return gen_.MakeInstruction( |
163 BPF_LD + BPF_W + BPF_ABS, | 162 BPF_LD + BPF_W + BPF_ABS, |
164 SECCOMP_ARCH_IDX, | 163 SECCOMP_ARCH_IDX, |
165 gen_.MakeInstruction( | 164 gen_.MakeInstruction( |
166 BPF_JMP + BPF_JEQ + BPF_K, | 165 BPF_JMP + BPF_JEQ + BPF_K, |
167 SECCOMP_ARCH, | 166 SECCOMP_ARCH, |
168 passed, | 167 passed, |
169 RetExpression(Kill("Invalid audit architecture in BPF filter")))); | 168 RetExpression(Kill("Invalid audit architecture in BPF filter")))); |
170 } | 169 } |
171 | 170 |
172 Instruction* PolicyCompiler::MaybeAddEscapeHatch(Instruction* rest) { | 171 CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) { |
173 // If no unsafe traps, then simply return |rest|. | 172 // If no unsafe traps, then simply return |rest|. |
174 if (!has_unsafe_traps_) { | 173 if (!has_unsafe_traps_) { |
175 return rest; | 174 return rest; |
176 } | 175 } |
177 | 176 |
178 // Allow system calls, if they originate from our magic return address | 177 // Allow system calls, if they originate from our magic return address |
179 // (which we can query by calling Syscall::Call(-1)). | 178 // (which we can query by calling Syscall::Call(-1)). |
180 uint64_t syscall_entry_point = | 179 uint64_t syscall_entry_point = |
181 static_cast<uint64_t>(static_cast<uintptr_t>(Syscall::Call(-1))); | 180 static_cast<uint64_t>(static_cast<uintptr_t>(Syscall::Call(-1))); |
182 uint32_t low = static_cast<uint32_t>(syscall_entry_point); | 181 uint32_t low = static_cast<uint32_t>(syscall_entry_point); |
(...skipping 16 matching lines...) Expand all Loading... |
199 BPF_LD + BPF_W + BPF_ABS, | 198 BPF_LD + BPF_W + BPF_ABS, |
200 SECCOMP_IP_MSB_IDX, | 199 SECCOMP_IP_MSB_IDX, |
201 gen_.MakeInstruction( | 200 gen_.MakeInstruction( |
202 BPF_JMP + BPF_JEQ + BPF_K, | 201 BPF_JMP + BPF_JEQ + BPF_K, |
203 hi, | 202 hi, |
204 RetExpression(ErrorCode(ErrorCode::ERR_ALLOWED)), | 203 RetExpression(ErrorCode(ErrorCode::ERR_ALLOWED)), |
205 rest)), | 204 rest)), |
206 rest)); | 205 rest)); |
207 } | 206 } |
208 | 207 |
209 Instruction* PolicyCompiler::DispatchSyscall() { | 208 CodeGen::Node PolicyCompiler::DispatchSyscall() { |
210 // Evaluate all possible system calls and group their ErrorCodes into | 209 // Evaluate all possible system calls and group their ErrorCodes into |
211 // ranges of identical codes. | 210 // ranges of identical codes. |
212 Ranges ranges; | 211 Ranges ranges; |
213 FindRanges(&ranges); | 212 FindRanges(&ranges); |
214 | 213 |
215 // Compile the system call ranges to an optimized BPF jumptable | 214 // Compile the system call ranges to an optimized BPF jumptable |
216 Instruction* jumptable = AssembleJumpTable(ranges.begin(), ranges.end()); | 215 CodeGen::Node jumptable = AssembleJumpTable(ranges.begin(), ranges.end()); |
217 | 216 |
218 // Grab the system call number, so that we can check it and then | 217 // Grab the system call number, so that we can check it and then |
219 // execute the jump table. | 218 // execute the jump table. |
220 return gen_.MakeInstruction( | 219 return gen_.MakeInstruction( |
221 BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable)); | 220 BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable)); |
222 } | 221 } |
223 | 222 |
224 Instruction* PolicyCompiler::CheckSyscallNumber(Instruction* passed) { | 223 CodeGen::Node PolicyCompiler::CheckSyscallNumber(CodeGen::Node passed) { |
225 if (kIsIntel) { | 224 if (kIsIntel) { |
226 // On Intel architectures, verify that system call numbers are in the | 225 // On Intel architectures, verify that system call numbers are in the |
227 // expected number range. | 226 // expected number range. |
228 Instruction* invalidX32 = | 227 CodeGen::Node invalidX32 = |
229 RetExpression(Kill("Illegal mixing of system call ABIs")); | 228 RetExpression(Kill("Illegal mixing of system call ABIs")); |
230 if (kIsX32) { | 229 if (kIsX32) { |
231 // The newer x32 API always sets bit 30. | 230 // The newer x32 API always sets bit 30. |
232 return gen_.MakeInstruction( | 231 return gen_.MakeInstruction( |
233 BPF_JMP + BPF_JSET + BPF_K, 0x40000000, passed, invalidX32); | 232 BPF_JMP + BPF_JSET + BPF_K, 0x40000000, passed, invalidX32); |
234 } else { | 233 } else { |
235 // The older i386 and x86-64 APIs clear bit 30 on all system calls. | 234 // The older i386 and x86-64 APIs clear bit 30 on all system calls. |
236 return gen_.MakeInstruction( | 235 return gen_.MakeInstruction( |
237 BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, passed); | 236 BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, passed); |
238 } | 237 } |
(...skipping 22 matching lines...) Expand all Loading... |
261 : invalid_err; | 260 : invalid_err; |
262 if (!err.Equals(old_err)) { | 261 if (!err.Equals(old_err)) { |
263 ranges->push_back(Range(old_sysnum, old_err)); | 262 ranges->push_back(Range(old_sysnum, old_err)); |
264 old_sysnum = sysnum; | 263 old_sysnum = sysnum; |
265 old_err = err; | 264 old_err = err; |
266 } | 265 } |
267 } | 266 } |
268 ranges->push_back(Range(old_sysnum, old_err)); | 267 ranges->push_back(Range(old_sysnum, old_err)); |
269 } | 268 } |
270 | 269 |
271 Instruction* PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start, | 270 CodeGen::Node PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start, |
272 Ranges::const_iterator stop) { | 271 Ranges::const_iterator stop) { |
273 // We convert the list of system call ranges into jump table that performs | 272 // We convert the list of system call ranges into jump table that performs |
274 // a binary search over the ranges. | 273 // a binary search over the ranges. |
275 // As a sanity check, we need to have at least one distinct ranges for us | 274 // As a sanity check, we need to have at least one distinct ranges for us |
276 // to be able to build a jump table. | 275 // to be able to build a jump table. |
277 if (stop - start <= 0) { | 276 if (stop - start <= 0) { |
278 SANDBOX_DIE("Invalid set of system call ranges"); | 277 SANDBOX_DIE("Invalid set of system call ranges"); |
279 } else if (stop - start == 1) { | 278 } else if (stop - start == 1) { |
280 // If we have narrowed things down to a single range object, we can | 279 // If we have narrowed things down to a single range object, we can |
281 // return from the BPF filter program. | 280 // return from the BPF filter program. |
282 return RetExpression(start->err); | 281 return RetExpression(start->err); |
283 } | 282 } |
284 | 283 |
285 // Pick the range object that is located at the mid point of our list. | 284 // Pick the range object that is located at the mid point of our list. |
286 // We compare our system call number against the lowest valid system call | 285 // We compare our system call number against the lowest valid system call |
287 // number in this range object. If our number is lower, it is outside of | 286 // number in this range object. If our number is lower, it is outside of |
288 // this range object. If it is greater or equal, it might be inside. | 287 // this range object. If it is greater or equal, it might be inside. |
289 Ranges::const_iterator mid = start + (stop - start) / 2; | 288 Ranges::const_iterator mid = start + (stop - start) / 2; |
290 | 289 |
291 // Sub-divide the list of ranges and continue recursively. | 290 // Sub-divide the list of ranges and continue recursively. |
292 Instruction* jf = AssembleJumpTable(start, mid); | 291 CodeGen::Node jf = AssembleJumpTable(start, mid); |
293 Instruction* jt = AssembleJumpTable(mid, stop); | 292 CodeGen::Node jt = AssembleJumpTable(mid, stop); |
294 return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf); | 293 return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf); |
295 } | 294 } |
296 | 295 |
297 Instruction* PolicyCompiler::RetExpression(const ErrorCode& err) { | 296 CodeGen::Node PolicyCompiler::RetExpression(const ErrorCode& err) { |
298 switch (err.error_type()) { | 297 switch (err.error_type()) { |
299 case ErrorCode::ET_COND: | 298 case ErrorCode::ET_COND: |
300 return CondExpression(err); | 299 return CondExpression(err); |
301 case ErrorCode::ET_SIMPLE: | 300 case ErrorCode::ET_SIMPLE: |
302 case ErrorCode::ET_TRAP: | 301 case ErrorCode::ET_TRAP: |
303 return gen_.MakeInstruction(BPF_RET + BPF_K, err.err()); | 302 return gen_.MakeInstruction(BPF_RET + BPF_K, err.err()); |
304 default: | 303 default: |
305 SANDBOX_DIE("ErrorCode is not suitable for returning from a BPF program"); | 304 SANDBOX_DIE("ErrorCode is not suitable for returning from a BPF program"); |
306 } | 305 } |
307 } | 306 } |
308 | 307 |
309 Instruction* PolicyCompiler::CondExpression(const ErrorCode& cond) { | 308 CodeGen::Node PolicyCompiler::CondExpression(const ErrorCode& cond) { |
310 // Sanity check that |cond| makes sense. | 309 // Sanity check that |cond| makes sense. |
311 if (cond.argno_ < 0 || cond.argno_ >= 6) { | 310 if (cond.argno_ < 0 || cond.argno_ >= 6) { |
312 SANDBOX_DIE("sandbox_bpf: invalid argument number"); | 311 SANDBOX_DIE("sandbox_bpf: invalid argument number"); |
313 } | 312 } |
314 if (cond.width_ != ErrorCode::TP_32BIT && | 313 if (cond.width_ != ErrorCode::TP_32BIT && |
315 cond.width_ != ErrorCode::TP_64BIT) { | 314 cond.width_ != ErrorCode::TP_64BIT) { |
316 SANDBOX_DIE("sandbox_bpf: invalid argument width"); | 315 SANDBOX_DIE("sandbox_bpf: invalid argument width"); |
317 } | 316 } |
318 if (cond.mask_ == 0) { | 317 if (cond.mask_ == 0) { |
319 SANDBOX_DIE("sandbox_bpf: zero mask is invalid"); | 318 SANDBOX_DIE("sandbox_bpf: zero mask is invalid"); |
320 } | 319 } |
321 if ((cond.value_ & cond.mask_) != cond.value_) { | 320 if ((cond.value_ & cond.mask_) != cond.value_) { |
322 SANDBOX_DIE("sandbox_bpf: value contains masked out bits"); | 321 SANDBOX_DIE("sandbox_bpf: value contains masked out bits"); |
323 } | 322 } |
324 if (cond.width_ == ErrorCode::TP_32BIT && | 323 if (cond.width_ == ErrorCode::TP_32BIT && |
325 ((cond.mask_ >> 32) != 0 || (cond.value_ >> 32) != 0)) { | 324 ((cond.mask_ >> 32) != 0 || (cond.value_ >> 32) != 0)) { |
326 SANDBOX_DIE("sandbox_bpf: test exceeds argument size"); | 325 SANDBOX_DIE("sandbox_bpf: test exceeds argument size"); |
327 } | 326 } |
328 // TODO(mdempsky): Reject TP_64BIT on 32-bit platforms. For now we allow it | 327 // TODO(mdempsky): Reject TP_64BIT on 32-bit platforms. For now we allow it |
329 // because some SandboxBPF unit tests exercise it. | 328 // because some SandboxBPF unit tests exercise it. |
330 | 329 |
331 Instruction* passed = RetExpression(*cond.passed_); | 330 CodeGen::Node passed = RetExpression(*cond.passed_); |
332 Instruction* failed = RetExpression(*cond.failed_); | 331 CodeGen::Node failed = RetExpression(*cond.failed_); |
333 | 332 |
334 // We want to emit code to check "(arg & mask) == value" where arg, mask, and | 333 // We want to emit code to check "(arg & mask) == value" where arg, mask, and |
335 // value are 64-bit values, but the BPF machine is only 32-bit. We implement | 334 // value are 64-bit values, but the BPF machine is only 32-bit. We implement |
336 // this by independently testing the upper and lower 32-bits and continuing to | 335 // this by independently testing the upper and lower 32-bits and continuing to |
337 // |passed| if both evaluate true, or to |failed| if either evaluate false. | 336 // |passed| if both evaluate true, or to |failed| if either evaluate false. |
338 return CondExpressionHalf(cond, | 337 return CondExpressionHalf(cond, |
339 UpperHalf, | 338 UpperHalf, |
340 CondExpressionHalf(cond, LowerHalf, passed, failed), | 339 CondExpressionHalf(cond, LowerHalf, passed, failed), |
341 failed); | 340 failed); |
342 } | 341 } |
343 | 342 |
344 Instruction* PolicyCompiler::CondExpressionHalf(const ErrorCode& cond, | 343 CodeGen::Node PolicyCompiler::CondExpressionHalf(const ErrorCode& cond, |
345 ArgHalf half, | 344 ArgHalf half, |
346 Instruction* passed, | 345 CodeGen::Node passed, |
347 Instruction* failed) { | 346 CodeGen::Node failed) { |
348 if (cond.width_ == ErrorCode::TP_32BIT && half == UpperHalf) { | 347 if (cond.width_ == ErrorCode::TP_32BIT && half == UpperHalf) { |
349 // Special logic for sanity checking the upper 32-bits of 32-bit system | 348 // Special logic for sanity checking the upper 32-bits of 32-bit system |
350 // call arguments. | 349 // call arguments. |
351 | 350 |
352 // TODO(mdempsky): Compile Unexpected64bitArgument() just per program. | 351 // TODO(mdempsky): Compile Unexpected64bitArgument() just per program. |
353 Instruction* invalid_64bit = RetExpression(Unexpected64bitArgument()); | 352 CodeGen::Node invalid_64bit = RetExpression(Unexpected64bitArgument()); |
354 | 353 |
355 const uint32_t upper = SECCOMP_ARG_MSB_IDX(cond.argno_); | 354 const uint32_t upper = SECCOMP_ARG_MSB_IDX(cond.argno_); |
356 const uint32_t lower = SECCOMP_ARG_LSB_IDX(cond.argno_); | 355 const uint32_t lower = SECCOMP_ARG_LSB_IDX(cond.argno_); |
357 | 356 |
358 if (sizeof(void*) == 4) { | 357 if (sizeof(void*) == 4) { |
359 // On 32-bit platforms, the upper 32-bits should always be 0: | 358 // On 32-bit platforms, the upper 32-bits should always be 0: |
360 // LDW [upper] | 359 // LDW [upper] |
361 // JEQ 0, passed, invalid | 360 // JEQ 0, passed, invalid |
362 return gen_.MakeInstruction( | 361 return gen_.MakeInstruction( |
363 BPF_LD + BPF_W + BPF_ABS, | 362 BPF_LD + BPF_W + BPF_ABS, |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
513 &*conds_.insert(passed).first, | 512 &*conds_.insert(passed).first, |
514 &*conds_.insert(failed).first); | 513 &*conds_.insert(failed).first); |
515 } | 514 } |
516 | 515 |
517 ErrorCode PolicyCompiler::Kill(const char* msg) { | 516 ErrorCode PolicyCompiler::Kill(const char* msg) { |
518 return Trap(BPFFailure, const_cast<char*>(msg)); | 517 return Trap(BPFFailure, const_cast<char*>(msg)); |
519 } | 518 } |
520 | 519 |
521 } // namespace bpf_dsl | 520 } // namespace bpf_dsl |
522 } // namespace sandbox | 521 } // namespace sandbox |
OLD | NEW |