diff options
author | markus@chromium.org <markus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-11 16:17:58 +0000 |
---|---|---|
committer | markus@chromium.org <markus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-11 16:17:58 +0000 |
commit | c9cd220761b7ed7950b78a7e64e754f43fbb9ffc (patch) | |
tree | 696286d07863b98b2eacc8032faf996ac1750870 /sandbox | |
parent | f8d865534ff053b4c87973968f01755beba9119d (diff) | |
download | chromium_src-c9cd220761b7ed7950b78a7e64e754f43fbb9ffc.zip chromium_src-c9cd220761b7ed7950b78a7e64e754f43fbb9ffc.tar.gz chromium_src-c9cd220761b7ed7950b78a7e64e754f43fbb9ffc.tar.bz2 |
If the kernel lacks support for BPF filtering, we can still perform a couple
of static tests on our filter policy and on the filter program. This extends
the test coverage of our unittests, even if it is still somewhat limited.
TEST=sandbox_linux_unittests
BUG=141545
Review URL: https://chromiumcodereview.appspot.com/11829013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@176361 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r-- | sandbox/linux/seccomp-bpf/Makefile | 2 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/bpf_tests.h | 10 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/sandbox_bpf.cc | 135 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/sandbox_bpf.h | 46 |
4 files changed, 108 insertions, 85 deletions
diff --git a/sandbox/linux/seccomp-bpf/Makefile b/sandbox/linux/seccomp-bpf/Makefile index 6d644b8..78ff704 100644 --- a/sandbox/linux/seccomp-bpf/Makefile +++ b/sandbox/linux/seccomp-bpf/Makefile @@ -1,5 +1,5 @@ DEF_CFLAGS = -g -O3 -Wall -Werror -Wextra -Wno-missing-field-initializers -fPIC -I. -DEF_CPPFLAGS = -D_GNU_SOURCE -DSECCOMP_BPF_STANDALONE -DSECCOMP_BPF_VALGRIND_HACKS -include valgrind/valgrind.h -iquote ../../.. +DEF_CPPFLAGS = -D_GNU_SOURCE -DSECCOMP_BPF_STANDALONE -iquote ../../.. DEF_LDFLAGS = -g -lpthread DEPFLAGS = -MMD -MF .$@.d MODS := demo sandbox_bpf basicblock codegen die errorcode syscall syscall_iterator util verifier diff --git a/sandbox/linux/seccomp-bpf/bpf_tests.h b/sandbox/linux/seccomp-bpf/bpf_tests.h index 3ac631c..931a39e 100644 --- a/sandbox/linux/seccomp-bpf/bpf_tests.h +++ b/sandbox/linux/seccomp-bpf/bpf_tests.h @@ -87,9 +87,13 @@ class BpfTests : public UnitTests { arg->test()(arg->aux_); } else { - // TODO(markus): (crbug.com/141545) Call the compiler and verify the - // policy. That's the least we can do, if we don't have kernel support. - playground2::Sandbox::SetSandboxPolicy(arg->policy(), NULL); + // Call the compiler and verify the policy. That's the least we can do, + // if we don't have kernel support. + playground2::Sandbox::SetSandboxPolicy(arg->policy(), &arg->aux_); + playground2::Sandbox::Program *program = + playground2::Sandbox::AssembleFilter(); + playground2::Sandbox::VerifyProgram(*program); + delete program; sandbox::UnitTests::IgnoreThisTest(); } } diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc index 366706f..cc2ce1e 100644 --- a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc @@ -235,15 +235,6 @@ bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(), } bool Sandbox::KernelSupportSeccompBPF(int proc_fd) { -#if defined(SECCOMP_BPF_VALGRIND_HACKS) - if (RUNNING_ON_VALGRIND) { - // Valgrind doesn't like our run-time test. Disable testing and assume we - // always support sandboxing. This feature should only ever be enabled when - // debugging. - return true; - } -#endif - return RunFunctionInPolicy(ProbeProcess, Sandbox::ProbeEvaluator, 0, proc_fd) && RunFunctionInPolicy(TryVsyscallProcess, Sandbox::AllowAllEvaluator, 0, @@ -426,6 +417,49 @@ void Sandbox::SetSandboxPolicy(EvaluateSyscall syscall_evaluator, void *aux) { } void Sandbox::InstallFilter(bool quiet) { + // We want to be very careful in not imposing any requirements on the + // policies that are set with SetSandboxPolicy(). This means, as soon as + // the sandbox is active, we shouldn't be relying on libraries that could + // be making system calls. This, for example, means we should avoid + // using the heap and we should avoid using STL functions. + // Temporarily copy the contents of the "program" vector into a + // stack-allocated array; and then explicitly destroy that object. + // This makes sure we don't ex- or implicitly call new/delete after we + // installed the BPF filter program in the kernel. Depending on the + // system memory allocator that is in effect, these operators can result + // in system calls to things like munmap() or brk(). + Program *program = AssembleFilter(); + + // Make sure compilation resulted in BPF program that executes + // correctly. Otherwise, there is an internal error in our BPF compiler. + // There is really nothing the caller can do until the bug is fixed. +#ifndef NDEBUG + VerifyProgram(*program); +#endif + + struct sock_filter bpf[program->size()]; + const struct sock_fprog prog = { + static_cast<unsigned short>(program->size()), bpf }; + memcpy(bpf, &(*program)[0], sizeof(bpf)); + delete program; + + // Release memory that is no longer needed + evaluators_.clear(); + conds_.clear(); + + // Install BPF filter program + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + SANDBOX_DIE(quiet ? NULL : "Kernel refuses to enable no-new-privs"); + } else { + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { + SANDBOX_DIE(quiet ? NULL : "Kernel refuses to turn on BPF filters"); + } + } + + return; +} + +Sandbox::Program *Sandbox::AssembleFilter() { // Verify that the user pushed a policy. if (evaluators_.empty()) { filter_failed: @@ -586,68 +620,25 @@ void Sandbox::InstallFilter(bool quiet) { gen->Compile(head, program); delete gen; - // Make sure compilation resulted in BPF program that executes - // correctly. Otherwise, there is an internal error in our BPF compiler. - // There is really nothing the caller can do until the bug is fixed. -#ifndef NDEBUG - { - // If we previously rewrote the BPF program so that it calls user-space - // whenever we return an "errno" value from the filter, then we have to - // wrap our system call evaluator to perform the same operation. Otherwise, - // the verifier would also report a mismatch in return codes. - Evaluators redirected_evaluators; - redirected_evaluators.push_back( - std::make_pair(RedirectToUserspaceEvalWrapper, &evaluators_)); - - const char *err = NULL; - if (!Verifier::VerifyBPF( - *program, - has_unsafe_traps_ ? redirected_evaluators : evaluators_, - &err)) { - SANDBOX_DIE(err); - } - } -#endif - - // We want to be very careful in not imposing any requirements on the - // policies that are set with setSandboxPolicy(). This means, as soon as - // the sandbox is active, we shouldn't be relying on libraries that could - // be making system calls. This, for example, means we should avoid - // using the heap and we should avoid using STL functions. - // Temporarily copy the contents of the "program" vector into a - // stack-allocated array; and then explicitly destroy that object. - // This makes sure we don't ex- or implicitly call new/delete after we - // installed the BPF filter program in the kernel. Depending on the - // system memory allocator that is in effect, these operators can result - // in system calls to things like munmap() or brk(). - struct sock_filter bpf[program->size()]; - const struct sock_fprog prog = { - static_cast<unsigned short>(program->size()), bpf }; - memcpy(bpf, &(*program)[0], sizeof(bpf)); - delete program; + return program; +} - // Release memory that is no longer needed - evaluators_.clear(); - conds_.clear(); +void Sandbox::VerifyProgram(const Program& program) { + // If we previously rewrote the BPF program so that it calls user-space + // whenever we return an "errno" value from the filter, then we have to + // wrap our system call evaluator to perform the same operation. Otherwise, + // the verifier would also report a mismatch in return codes. + Evaluators redirected_evaluators; + redirected_evaluators.push_back( + std::make_pair(RedirectToUserspaceEvalWrapper, &evaluators_)); -#if defined(SECCOMP_BPF_VALGRIND_HACKS) - // Valgrind is really not happy about our sandbox. Disable it when running - // in Valgrind. This feature is dangerous and should never be enabled by - // default. We protect it behind a pre-processor option. - if (!RUNNING_ON_VALGRIND) -#endif - { - // Install BPF filter program - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - SANDBOX_DIE(quiet ? NULL : "Kernel refuses to enable no-new-privs"); - } else { - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { - SANDBOX_DIE(quiet ? NULL : "Kernel refuses to turn on BPF filters"); - } - } + const char *err = NULL; + if (!Verifier::VerifyBPF( + program, + has_unsafe_traps_ ? redirected_evaluators : evaluators_, + &err)) { + SANDBOX_DIE(err); } - - return; } void Sandbox::FindRanges(Ranges *ranges) { @@ -706,11 +697,11 @@ Instruction *Sandbox::AssembleJumpTable(CodeGen *gen, return gen->MakeInstruction(BPF_JMP+BPF_JGE+BPF_K, mid->from, jt, jf); } -Instruction *Sandbox::RetExpression(CodeGen *gen, const ErrorCode& cond) { - if (cond.error_type_ == ErrorCode::ET_COND) { - return CondExpression(gen, cond); +Instruction *Sandbox::RetExpression(CodeGen *gen, const ErrorCode& err) { + if (err.error_type_ == ErrorCode::ET_COND) { + return CondExpression(gen, err); } else { - return gen->MakeInstruction(BPF_RET+BPF_K, cond); + return gen->MakeInstruction(BPF_RET+BPF_K, err); } } diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h index aee03fa..e440840 100644 --- a/sandbox/linux/seccomp-bpf/sandbox_bpf.h +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h @@ -46,12 +46,6 @@ #include "base/posix/eintr_wrapper.h" #endif -#if defined(SECCOMP_BPF_VALGRIND_HACKS) -#if !defined(SECCOMP_BPF_STANDALONE) -#include "base/third_party/valgrind/valgrind.h" -#endif -#endif - // The Seccomp2 kernel ABI is not part of older versions of glibc. // As we can't break compilation with these versions of the library, @@ -298,6 +292,10 @@ class Sandbox { typedef ErrorCode (*EvaluateSyscall)(int sysnum, void *aux); typedef std::vector<std::pair<EvaluateSyscall, void *> >Evaluators; + // A vector of BPF instructions that need to be installed as a filter + // program in the kernel. + typedef std::vector<struct sock_filter> Program; + // Checks whether a particular system call number is valid on the current // architecture. E.g. on ARM there's a non-contiguous range of private // system calls. @@ -380,6 +378,17 @@ class Sandbox { // enters Seccomp mode. static void StartSandbox() { StartSandboxInternal(false); } + // Assembles a BPF filter program from the current policy. After calling this + // function, you must not call any other sandboxing function. + // Typically, AssembleFilter() is only used by unit tests and by sandbox + // internals. It should not be used by production code. + static Program *AssembleFilter(); + + // Verify the correctness of a compiled program by comparing it against the + // current policy. This function should only ever be called by unit tests and + // by the sandbox internals. It should not be used by production code. + static void VerifyProgram(const Program& program); + private: friend class CodeGen; friend class SandboxUnittestHelper; @@ -387,8 +396,6 @@ class Sandbox { friend class Util; friend class Verifier; - typedef std::vector<struct sock_filter> Program; - struct Range { Range(uint32_t f, uint32_t t, const ErrorCode& e) : from(f), @@ -451,12 +458,33 @@ class Sandbox { // evaluator. static ErrorCode RedirectToUserspaceEvalWrapper(int sysnum, void *aux); + // Assembles and installs a filter based on the policy that has previously + // been configured with SetSandboxPolicy(). static void InstallFilter(bool quiet); + + // Finds all the ranges of system calls that need to be handled. Ranges are + // sorted in ascending order of system call numbers. There are no gaps in the + // ranges. System calls with identical ErrorCodes are coalesced into a single + // range. static void FindRanges(Ranges *ranges); + + // Returns a BPF program snippet that implements a jump table for the + // given range of system call numbers. This function runs recursively. static Instruction *AssembleJumpTable(CodeGen *gen, Ranges::const_iterator start, Ranges::const_iterator stop); - static Instruction *RetExpression(CodeGen *gen, const ErrorCode& cond); + + // Returns a BPF program snippet that makes the BPF filter program exit + // with the given ErrorCode "err". N.B. the ErrorCode may very well be a + // conditional expression; if so, this function will recursively call + // CondExpression() and possibly RetExpression() to build a complex set of + // instructions. + static Instruction *RetExpression(CodeGen *gen, const ErrorCode& err); + + // Returns a BPF program that evaluates the conditional expression in + // "cond" and returns the appropriate value from the BPF filter program. + // This function recursively calls RetExpression(); it should only ever be + // called from RetExpression(). static Instruction *CondExpression(CodeGen *gen, const ErrorCode& cond); // Returns the fatal ErrorCode that is used to indicate that somebody |