| 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 |