Merge from Chromium at DEPS revision 237746
This commit was generated by merge_to_master.py.
Change-Id: I8997af4cddfeb09a7c26f7e8e672c712cab461ea
diff --git a/sandbox/OWNERS b/sandbox/OWNERS
index 018422f..5d15856 100644
--- a/sandbox/OWNERS
+++ b/sandbox/OWNERS
@@ -1,3 +1,4 @@
+# For Windows:
cpu@chromium.org
jschuh@chromium.org
nsylvain@chromium.org
@@ -6,3 +7,4 @@
markus@chromium.org
jln@chromium.org
cevans@chromium.org
+jorgelo@chromium.org
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi
index 3b2df8b..abbf32e 100644
--- a/sandbox/linux/sandbox_linux.gypi
+++ b/sandbox/linux/sandbox_linux.gypi
@@ -19,6 +19,11 @@
}, {
'compile_seccomp_bpf': 0,
}],
+ ['OS=="linux" and (target_arch=="ia32" or target_arch=="x64")', {
+ 'compile_seccomp_bpf_demo': 1,
+ }, {
+ 'compile_seccomp_bpf_demo': 0,
+ }],
],
},
'target_defaults': {
@@ -100,9 +105,9 @@
'seccomp-bpf/errorcode.h',
'seccomp-bpf/instruction.h',
'seccomp-bpf/linux_seccomp.h',
- 'seccomp-bpf/port.h',
'seccomp-bpf/sandbox_bpf.cc',
'seccomp-bpf/sandbox_bpf.h',
+ 'seccomp-bpf/sandbox_bpf_policy.h',
'seccomp-bpf/sandbox_bpf_policy_forward.h',
'seccomp-bpf/syscall.cc',
'seccomp-bpf/syscall.h',
@@ -122,6 +127,26 @@
],
},
{
+ # A demonstration program for the seccomp-bpf sandbox.
+ 'target_name': 'seccomp_bpf_demo',
+ 'conditions': [
+ ['compile_seccomp_bpf_demo==1', {
+ 'type': 'executable',
+ 'sources': [
+ 'seccomp-bpf/demo.cc',
+ ],
+ 'dependencies': [
+ 'seccomp_bpf',
+ ],
+ }, {
+ 'type': 'none',
+ }],
+ ],
+ 'include_dirs': [
+ '../../',
+ ],
+ },
+ {
# The setuid sandbox, for Linux
'target_name': 'chrome_sandbox',
'type': 'executable',
@@ -147,6 +172,8 @@
'sources': [
'services/broker_process.cc',
'services/broker_process.h',
+ 'services/init_process_reaper.cc',
+ 'services/init_process_reaper.h',
],
'dependencies': [
'../base/base.gyp:base',
@@ -209,6 +236,7 @@
],
'dependencies': [
'../base/base.gyp:base',
+ 'sandbox_services',
],
'include_dirs': [
'..',
diff --git a/sandbox/linux/seccomp-bpf/Makefile b/sandbox/linux/seccomp-bpf/Makefile
deleted file mode 100644
index 6b35580..0000000
--- a/sandbox/linux/seccomp-bpf/Makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-DEF_CFLAGS = -g -O3 -Wall -Werror -Wextra -Wno-missing-field-initializers -fPIC -I.
-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 trap verifier
-OBJS64 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o64/')
-OBJS32 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o32/')
-ALL_OBJS = $(OBJS32) $(OBJS64)
-DEP_FILES = $(wildcard $(foreach f,$(ALL_OBJS),.$(f).d))
-
-.SUFFIXES: .o64 .o32
-
-all: demo32 demo64
-
-clean:
- $(RM) demo32 demo64
- $(RM) *.o *.o32 *.o64 .*.d
- $(RM) core core.* vgcore vgcore.* strace.log*
-
--include $(DEP_FILES)
-
-demo32: ${OBJS32}
- ${CXX} -m32 -o $@ $+ ${DEF_LDFLAGS} ${LDFLAGS}
-demo64: ${OBJS64}
- ${CXX} -m64 -o $@ $+ ${DEF_LDFLAGS} ${LDFLAGS}
-
-.cc.o32:
- ${CXX} -m32 ${DEF_CFLAGS} ${DEF_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} ${DEPFLAGS} -c -o $@ $<
-.cc.o64:
- ${CXX} -m64 ${DEF_CFLAGS} ${DEF_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} ${DEPFLAGS} -c -o $@ $<
diff --git a/sandbox/linux/seccomp-bpf/basicblock.cc b/sandbox/linux/seccomp-bpf/basicblock.cc
index bf27c58..58d27b2 100644
--- a/sandbox/linux/seccomp-bpf/basicblock.cc
+++ b/sandbox/linux/seccomp-bpf/basicblock.cc
@@ -4,13 +4,10 @@
#include "sandbox/linux/seccomp-bpf/basicblock.h"
-
namespace playground2 {
-BasicBlock::BasicBlock() {
-}
+BasicBlock::BasicBlock() {}
-BasicBlock::~BasicBlock() {
-}
+BasicBlock::~BasicBlock() {}
} // namespace
diff --git a/sandbox/linux/seccomp-bpf/basicblock.h b/sandbox/linux/seccomp-bpf/basicblock.h
index 1782a80..a116f41 100644
--- a/sandbox/linux/seccomp-bpf/basicblock.h
+++ b/sandbox/linux/seccomp-bpf/basicblock.h
@@ -9,7 +9,6 @@
#include "sandbox/linux/seccomp-bpf/instruction.h"
-
namespace playground2 {
struct BasicBlock {
@@ -20,25 +19,24 @@
// identify common sequences of basic blocks. This would normally be
// really easy to do, but STL requires us to wrap the comparator into
// a class. We begrudgingly add some code here that provides this wrapping.
- template<class T> class Less {
+ template <class T>
+ class Less {
public:
- Less(const T& data, int (*cmp)(const BasicBlock *, const BasicBlock *,
- const T& data))
- : data_(data),
- cmp_(cmp) {
- }
+ Less(const T& data,
+ int (*cmp)(const BasicBlock*, const BasicBlock*, const T& data))
+ : data_(data), cmp_(cmp) {}
- bool operator() (const BasicBlock *a, const BasicBlock *b) const {
+ bool operator()(const BasicBlock* a, const BasicBlock* b) const {
return cmp_(a, b, data_) < 0;
}
private:
const T& data_;
- int (*cmp_)(const BasicBlock *, const BasicBlock *, const T&);
+ int (*cmp_)(const BasicBlock*, const BasicBlock*, const T&);
};
// Basic blocks are essentially nothing more than a set of instructions.
- std::vector<Instruction *> instructions;
+ std::vector<Instruction*> instructions;
// In order to compute relative branch offsets we need to keep track of
// how far our block is away from the very last basic block. The "offset_"
diff --git a/sandbox/linux/seccomp-bpf/bpf_tests.h b/sandbox/linux/seccomp-bpf/bpf_tests.h
index 83fc10a..13ccf7d 100644
--- a/sandbox/linux/seccomp-bpf/bpf_tests.h
+++ b/sandbox/linux/seccomp-bpf/bpf_tests.h
@@ -20,14 +20,13 @@
// macros from unit_tests.h to specify the expected error condition.
// A BPF_DEATH_TEST is always disabled under ThreadSanitizer, see
// crbug.com/243968.
-#define BPF_DEATH_TEST(test_case_name, test_name, death, policy, aux...) \
- void BPF_TEST_##test_name(sandbox::BpfTests<aux>::AuxType& BPF_AUX); \
- TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
- sandbox::BpfTests<aux>::TestArgs arg(BPF_TEST_##test_name, policy); \
- sandbox::BpfTests<aux>::RunTestInProcess( \
- sandbox::BpfTests<aux>::TestWrapper, &arg, \
- death); \
- } \
+#define BPF_DEATH_TEST(test_case_name, test_name, death, policy, aux...) \
+ void BPF_TEST_##test_name(sandbox::BpfTests<aux>::AuxType& BPF_AUX); \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::BpfTests<aux>::TestArgs arg(BPF_TEST_##test_name, policy); \
+ sandbox::BpfTests<aux>::RunTestInProcess( \
+ sandbox::BpfTests<aux>::TestWrapper, &arg, death); \
+ } \
void BPF_TEST_##test_name(sandbox::BpfTests<aux>::AuxType& BPF_AUX)
// BPF_TEST() is a special version of SANDBOX_TEST(). It turns into a no-op,
@@ -40,18 +39,16 @@
// variable will be passed as an argument to the "policy" function. Policies
// would typically use it as an argument to Sandbox::Trap(), if they want to
// communicate data between the BPF_TEST() and a Trap() function.
-#define BPF_TEST(test_case_name, test_name, policy, aux...) \
+#define BPF_TEST(test_case_name, test_name, policy, aux...) \
BPF_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS(), policy, aux)
-
// Assertions are handled exactly the same as with a normal SANDBOX_TEST()
#define BPF_ASSERT SANDBOX_ASSERT
-
// The "Aux" type is optional. We use an "empty" type by default, so that if
// the caller doesn't provide any type, all the BPF_AUX related data compiles
// to nothing.
-template<class Aux = int[0]>
+template <class Aux = int[0]>
class BpfTests : public UnitTests {
public:
typedef Aux AuxType;
@@ -59,10 +56,7 @@
class TestArgs {
public:
TestArgs(void (*t)(AuxType&), playground2::Sandbox::EvaluateSyscall p)
- : test_(t),
- policy_(p),
- aux_() {
- }
+ : test_(t), policy_(p), aux_() {}
void (*test() const)(AuxType&) { return test_; }
playground2::Sandbox::EvaluateSyscall policy() const { return policy_; }
@@ -75,21 +69,21 @@
AuxType aux_;
};
- static void TestWrapper(void *void_arg) {
- TestArgs *arg = reinterpret_cast<TestArgs *>(void_arg);
+ static void TestWrapper(void* void_arg) {
+ TestArgs* arg = reinterpret_cast<TestArgs*>(void_arg);
playground2::Die::EnableSimpleExit();
if (playground2::Sandbox::SupportsSeccompSandbox(-1) ==
playground2::Sandbox::STATUS_AVAILABLE) {
// Ensure the the sandbox is actually available at this time
int proc_fd;
- BPF_ASSERT((proc_fd = open("/proc", O_RDONLY|O_DIRECTORY)) >= 0);
+ BPF_ASSERT((proc_fd = open("/proc", O_RDONLY | O_DIRECTORY)) >= 0);
BPF_ASSERT(playground2::Sandbox::SupportsSeccompSandbox(proc_fd) ==
playground2::Sandbox::STATUS_AVAILABLE);
// Initialize and then start the sandbox with our custom policy
playground2::Sandbox sandbox;
sandbox.set_proc_fd(proc_fd);
- sandbox.SetSandboxPolicy(arg->policy(), &arg->aux_);
+ sandbox.SetSandboxPolicyDeprecated(arg->policy(), &arg->aux_);
sandbox.Sandbox::StartSandbox();
arg->test()(arg->aux_);
@@ -105,8 +99,8 @@
// Call the compiler and verify the policy. That's the least we can do,
// if we don't have kernel support.
playground2::Sandbox sandbox;
- sandbox.SetSandboxPolicy(arg->policy(), &arg->aux_);
- playground2::Sandbox::Program *program =
+ sandbox.SetSandboxPolicyDeprecated(arg->policy(), &arg->aux_);
+ playground2::Sandbox::Program* program =
sandbox.AssembleFilter(true /* force_verification */);
delete program;
sandbox::UnitTests::IgnoreThisTest();
diff --git a/sandbox/linux/seccomp-bpf/codegen.cc b/sandbox/linux/seccomp-bpf/codegen.cc
index 17b5d84..77df612 100644
--- a/sandbox/linux/seccomp-bpf/codegen.cc
+++ b/sandbox/linux/seccomp-bpf/codegen.cc
@@ -6,26 +6,25 @@
#include "sandbox/linux/seccomp-bpf/codegen.h"
-
namespace {
// Helper function for Traverse().
-void TraverseRecursively(std::set<playground2::Instruction *> *visited,
- playground2::Instruction *instruction) {
+void TraverseRecursively(std::set<playground2::Instruction*>* visited,
+ playground2::Instruction* instruction) {
if (visited->find(instruction) == visited->end()) {
visited->insert(instruction);
switch (BPF_CLASS(instruction->code)) {
- case BPF_JMP:
- if (BPF_OP(instruction->code) != BPF_JA) {
- TraverseRecursively(visited, instruction->jf_ptr);
- }
- TraverseRecursively(visited, instruction->jt_ptr);
- break;
- case BPF_RET:
- break;
- default:
- TraverseRecursively(visited, instruction->next);
- break;
+ case BPF_JMP:
+ if (BPF_OP(instruction->code) != BPF_JA) {
+ TraverseRecursively(visited, instruction->jf_ptr);
+ }
+ TraverseRecursively(visited, instruction->jt_ptr);
+ break;
+ case BPF_RET:
+ break;
+ default:
+ TraverseRecursively(visited, instruction->next);
+ break;
}
}
}
@@ -34,9 +33,7 @@
namespace playground2 {
-CodeGen::CodeGen()
- : compiled_(false) {
-}
+CodeGen::CodeGen() : compiled_(false) {}
CodeGen::~CodeGen() {
for (Instructions::iterator iter = instructions_.begin();
@@ -58,108 +55,114 @@
int ip = (int)(iter - program.begin());
fprintf(stderr, "%3d) ", ip);
switch (BPF_CLASS(iter->code)) {
- case BPF_LD:
- if (iter->code == BPF_LD+BPF_W+BPF_ABS) {
- fprintf(stderr, "LOAD %d // ", (int)iter->k);
- if (iter->k == offsetof(struct arch_seccomp_data, nr)) {
- fprintf(stderr, "System call number\n");
- } else if (iter->k == offsetof(struct arch_seccomp_data, arch)) {
- fprintf(stderr, "Architecture\n");
- } else if (iter->k == offsetof(struct arch_seccomp_data,
- instruction_pointer)) {
- fprintf(stderr, "Instruction pointer (LSB)\n");
- } else if (iter->k == offsetof(struct arch_seccomp_data,
- instruction_pointer) + 4) {
- fprintf(stderr, "Instruction pointer (MSB)\n");
- } else if (iter->k >= offsetof(struct arch_seccomp_data, args) &&
- iter->k < offsetof(struct arch_seccomp_data, args)+48 &&
- (iter->k-offsetof(struct arch_seccomp_data, args))%4 == 0) {
- fprintf(stderr, "Argument %d (%cSB)\n",
- (int)(iter->k-offsetof(struct arch_seccomp_data, args))/8,
- (iter->k-offsetof(struct arch_seccomp_data,
- args))%8 ? 'M' : 'L');
+ case BPF_LD:
+ if (iter->code == BPF_LD + BPF_W + BPF_ABS) {
+ fprintf(stderr, "LOAD %d // ", (int)iter->k);
+ if (iter->k == offsetof(struct arch_seccomp_data, nr)) {
+ fprintf(stderr, "System call number\n");
+ } else if (iter->k == offsetof(struct arch_seccomp_data, arch)) {
+ fprintf(stderr, "Architecture\n");
+ } else if (iter->k ==
+ offsetof(struct arch_seccomp_data, instruction_pointer)) {
+ fprintf(stderr, "Instruction pointer (LSB)\n");
+ } else if (iter->k ==
+ offsetof(struct arch_seccomp_data, instruction_pointer) +
+ 4) {
+ fprintf(stderr, "Instruction pointer (MSB)\n");
+ } else if (iter->k >= offsetof(struct arch_seccomp_data, args) &&
+ iter->k < offsetof(struct arch_seccomp_data, args) + 48 &&
+ (iter->k - offsetof(struct arch_seccomp_data, args)) % 4 ==
+ 0) {
+ fprintf(
+ stderr,
+ "Argument %d (%cSB)\n",
+ (int)(iter->k - offsetof(struct arch_seccomp_data, args)) / 8,
+ (iter->k - offsetof(struct arch_seccomp_data, args)) % 8 ? 'M'
+ : 'L');
+ } else {
+ fprintf(stderr, "???\n");
+ }
+ } else {
+ fprintf(stderr, "LOAD ???\n");
+ }
+ break;
+ case BPF_JMP:
+ if (BPF_OP(iter->code) == BPF_JA) {
+ fprintf(stderr, "JMP %d\n", ip + iter->k + 1);
+ } else {
+ fprintf(stderr, "if A %s 0x%x; then JMP %d else JMP %d\n",
+ BPF_OP(iter->code) == BPF_JSET ? "&" :
+ BPF_OP(iter->code) == BPF_JEQ ? "==" :
+ BPF_OP(iter->code) == BPF_JGE ? ">=" :
+ BPF_OP(iter->code) == BPF_JGT ? ">" : "???",
+ (int)iter->k,
+ ip + iter->jt + 1, ip + iter->jf + 1);
+ }
+ break;
+ case BPF_RET:
+ fprintf(stderr, "RET 0x%x // ", iter->k);
+ if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP) {
+ fprintf(stderr, "Trap #%d\n", iter->k & SECCOMP_RET_DATA);
+ } else if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
+ fprintf(stderr, "errno = %d\n", iter->k & SECCOMP_RET_DATA);
+ } else if (iter->k == SECCOMP_RET_ALLOW) {
+ fprintf(stderr, "Allowed\n");
} else {
fprintf(stderr, "???\n");
}
- } else {
- fprintf(stderr, "LOAD ???\n");
- }
- break;
- case BPF_JMP:
- if (BPF_OP(iter->code) == BPF_JA) {
- fprintf(stderr, "JMP %d\n", ip + iter->k + 1);
- } else {
- fprintf(stderr, "if A %s 0x%x; then JMP %d else JMP %d\n",
- BPF_OP(iter->code) == BPF_JSET ? "&" :
- BPF_OP(iter->code) == BPF_JEQ ? "==" :
- BPF_OP(iter->code) == BPF_JGE ? ">=" :
- BPF_OP(iter->code) == BPF_JGT ? ">" : "???",
- (int)iter->k,
- ip + iter->jt + 1, ip + iter->jf + 1);
- }
- break;
- case BPF_RET:
- fprintf(stderr, "RET 0x%x // ", iter->k);
- if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP) {
- fprintf(stderr, "Trap #%d\n", iter->k & SECCOMP_RET_DATA);
- } else if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
- fprintf(stderr, "errno = %d\n", iter->k & SECCOMP_RET_DATA);
- } else if (iter->k == SECCOMP_RET_ALLOW) {
- fprintf(stderr, "Allowed\n");
- } else {
+ break;
+ case BPF_ALU:
+ fprintf(stderr, BPF_OP(iter->code) == BPF_NEG
+ ? "A := -A\n" : "A := A %s 0x%x\n",
+ BPF_OP(iter->code) == BPF_ADD ? "+" :
+ BPF_OP(iter->code) == BPF_SUB ? "-" :
+ BPF_OP(iter->code) == BPF_MUL ? "*" :
+ BPF_OP(iter->code) == BPF_DIV ? "/" :
+ BPF_OP(iter->code) == BPF_MOD ? "%" :
+ BPF_OP(iter->code) == BPF_OR ? "|" :
+ BPF_OP(iter->code) == BPF_XOR ? "^" :
+ BPF_OP(iter->code) == BPF_AND ? "&" :
+ BPF_OP(iter->code) == BPF_LSH ? "<<" :
+ BPF_OP(iter->code) == BPF_RSH ? ">>" : "???",
+ (int)iter->k);
+ break;
+ default:
fprintf(stderr, "???\n");
- }
- break;
- case BPF_ALU:
- fprintf(stderr, BPF_OP(iter->code) == BPF_NEG
- ? "A := -A\n" : "A := A %s 0x%x\n",
- BPF_OP(iter->code) == BPF_ADD ? "+" :
- BPF_OP(iter->code) == BPF_SUB ? "-" :
- BPF_OP(iter->code) == BPF_MUL ? "*" :
- BPF_OP(iter->code) == BPF_DIV ? "/" :
- BPF_OP(iter->code) == BPF_MOD ? "%" :
- BPF_OP(iter->code) == BPF_OR ? "|" :
- BPF_OP(iter->code) == BPF_XOR ? "^" :
- BPF_OP(iter->code) == BPF_AND ? "&" :
- BPF_OP(iter->code) == BPF_LSH ? "<<" :
- BPF_OP(iter->code) == BPF_RSH ? ">>" : "???",
- (int)iter->k);
- break;
- default:
- fprintf(stderr, "???\n");
- break;
+ break;
}
}
return;
}
-Instruction *CodeGen::MakeInstruction(uint16_t code, uint32_t k,
- Instruction *next) {
+Instruction* CodeGen::MakeInstruction(uint16_t code,
+ uint32_t k,
+ Instruction* next) {
// We can handle non-jumping instructions and "always" jumps. Both of
// them are followed by exactly one "next" instruction.
// We allow callers to defer specifying "next", but then they must call
// "joinInstructions" later.
if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_JA) {
- SANDBOX_DIE("Must provide both \"true\" and \"false\" branch "
- "for a BPF_JMP");
+ SANDBOX_DIE(
+ "Must provide both \"true\" and \"false\" branch "
+ "for a BPF_JMP");
}
if (next && BPF_CLASS(code) == BPF_RET) {
SANDBOX_DIE("Cannot append instructions after a return statement");
}
if (BPF_CLASS(code) == BPF_JMP) {
// "Always" jumps use the "true" branch target, only.
- Instruction *insn = new Instruction(code, 0, next, NULL);
+ Instruction* insn = new Instruction(code, 0, next, NULL);
instructions_.push_back(insn);
return insn;
} else {
// Non-jumping instructions do not use any of the branch targets.
- Instruction *insn = new Instruction(code, k, next);
+ Instruction* insn = new Instruction(code, k, next);
instructions_.push_back(insn);
return insn;
}
}
-Instruction *CodeGen::MakeInstruction(uint16_t code, const ErrorCode& err) {
+Instruction* CodeGen::MakeInstruction(uint16_t code, const ErrorCode& err) {
if (BPF_CLASS(code) != BPF_RET) {
SANDBOX_DIE("ErrorCodes can only be used in return expressions");
}
@@ -170,8 +173,10 @@
return MakeInstruction(code, err.err_);
}
-Instruction *CodeGen::MakeInstruction(uint16_t code, uint32_t k,
- Instruction *jt, Instruction *jf) {
+Instruction* CodeGen::MakeInstruction(uint16_t code,
+ uint32_t k,
+ Instruction* jt,
+ Instruction* jf) {
// We can handle all conditional jumps. They are followed by both a
// "true" and a "false" branch.
if (BPF_CLASS(code) != BPF_JMP || BPF_OP(code) == BPF_JA) {
@@ -182,12 +187,12 @@
// targets. It must then be set later by calling "JoinInstructions".
SANDBOX_DIE("Branches must jump to a valid instruction");
}
- Instruction *insn = new Instruction(code, k, jt, jf);
+ Instruction* insn = new Instruction(code, k, jt, jf);
instructions_.push_back(insn);
return insn;
}
-void CodeGen::JoinInstructions(Instruction *head, Instruction *tail) {
+void CodeGen::JoinInstructions(Instruction* head, Instruction* tail) {
// Merge two instructions, or set the branch target for an "always" jump.
// This function should be called, if the caller didn't initially provide
// a value for "next" when creating the instruction.
@@ -216,11 +221,12 @@
return;
}
-void CodeGen::Traverse(Instruction *instruction,
- void (*fnc)(Instruction *, void *), void *aux) {
- std::set<Instruction *> visited;
+void CodeGen::Traverse(Instruction* instruction,
+ void (*fnc)(Instruction*, void*),
+ void* aux) {
+ std::set<Instruction*> visited;
TraverseRecursively(&visited, instruction);
- for (std::set<Instruction *>::const_iterator iter = visited.begin();
+ for (std::set<Instruction*>::const_iterator iter = visited.begin();
iter != visited.end();
++iter) {
fnc(*iter, aux);
@@ -228,15 +234,15 @@
}
void CodeGen::FindBranchTargets(const Instruction& instructions,
- BranchTargets *branch_targets) {
+ BranchTargets* branch_targets) {
// Follow all possible paths through the "instructions" graph and compute
// a list of branch targets. This will later be needed to compute the
// boundaries of basic blocks.
// We maintain a set of all instructions that we have previously seen. This
// set ultimately converges on all instructions in the program.
- std::set<const Instruction *> seen_instructions;
+ std::set<const Instruction*> seen_instructions;
Instructions stack;
- for (const Instruction *insn = &instructions; insn; ) {
+ for (const Instruction* insn = &instructions; insn;) {
seen_instructions.insert(insn);
if (BPF_CLASS(insn->code) == BPF_JMP) {
// Found a jump. Increase count of incoming edges for each of the jump
@@ -244,7 +250,7 @@
++(*branch_targets)[insn->jt_ptr];
if (BPF_OP(insn->code) != BPF_JA) {
++(*branch_targets)[insn->jf_ptr];
- stack.push_back(const_cast<Instruction *>(insn));
+ stack.push_back(const_cast<Instruction*>(insn));
}
// Start a recursive decent for depth-first traversal.
if (seen_instructions.find(insn->jt_ptr) == seen_instructions.end()) {
@@ -262,8 +268,9 @@
// (if any). It's OK if "insn" becomes NULL when reaching a return
// instruction.
if (!insn->next != (BPF_CLASS(insn->code) == BPF_RET)) {
- SANDBOX_DIE("Internal compiler error; return instruction must be at "
- "the end of the BPF program");
+ SANDBOX_DIE(
+ "Internal compiler error; return instruction must be at "
+ "the end of the BPF program");
}
if (seen_instructions.find(insn->next) == seen_instructions.end()) {
insn = insn->next;
@@ -288,8 +295,9 @@
// We have seen both the "true" and the "false" branch, continue
// up the stack.
if (seen_instructions.find(insn->jt_ptr) == seen_instructions.end()) {
- SANDBOX_DIE("Internal compiler error; cannot find all "
- "branch targets");
+ SANDBOX_DIE(
+ "Internal compiler error; cannot find all "
+ "branch targets");
}
insn = NULL;
}
@@ -298,11 +306,10 @@
return;
}
-BasicBlock *CodeGen::MakeBasicBlock(Instruction *head,
- Instruction *tail) {
+BasicBlock* CodeGen::MakeBasicBlock(Instruction* head, Instruction* tail) {
// Iterate over all the instructions between "head" and "tail" and
// insert them into a new basic block.
- BasicBlock *bb = new BasicBlock;
+ BasicBlock* bb = new BasicBlock;
for (;; head = head->next) {
bb->instructions.push_back(head);
if (head == tail) {
@@ -316,20 +323,21 @@
return bb;
}
-void CodeGen::AddBasicBlock(Instruction *head,
- Instruction *tail,
+void CodeGen::AddBasicBlock(Instruction* head,
+ Instruction* tail,
const BranchTargets& branch_targets,
- TargetsToBlocks *basic_blocks,
- BasicBlock **firstBlock) {
+ TargetsToBlocks* basic_blocks,
+ BasicBlock** firstBlock) {
// Add a new basic block to "basic_blocks". Also set "firstBlock", if it
// has not been set before.
BranchTargets::const_iterator iter = branch_targets.find(head);
if ((iter == branch_targets.end()) != !*firstBlock ||
!*firstBlock != basic_blocks->empty()) {
- SANDBOX_DIE("Only the very first basic block should have no "
- "incoming jumps");
+ SANDBOX_DIE(
+ "Only the very first basic block should have no "
+ "incoming jumps");
}
- BasicBlock *bb = MakeBasicBlock(head, tail);
+ BasicBlock* bb = MakeBasicBlock(head, tail);
if (!*firstBlock) {
*firstBlock = bb;
}
@@ -337,19 +345,20 @@
return;
}
-BasicBlock *CodeGen::CutGraphIntoBasicBlocks(
- Instruction *instructions, const BranchTargets& branch_targets,
- TargetsToBlocks *basic_blocks) {
+BasicBlock* CodeGen::CutGraphIntoBasicBlocks(
+ Instruction* instructions,
+ const BranchTargets& branch_targets,
+ TargetsToBlocks* basic_blocks) {
// Textbook implementation of a basic block generator. All basic blocks
// start with a branch target and end with either a return statement or
// a jump (or are followed by an instruction that forms the beginning of a
// new block). Both conditional and "always" jumps are supported.
- BasicBlock *first_block = NULL;
- std::set<const Instruction *> seen_instructions;
+ BasicBlock* first_block = NULL;
+ std::set<const Instruction*> seen_instructions;
Instructions stack;
- Instruction *tail = NULL;
- Instruction *head = instructions;
- for (Instruction *insn = head; insn; ) {
+ Instruction* tail = NULL;
+ Instruction* head = instructions;
+ for (Instruction* insn = head; insn;) {
if (seen_instructions.find(insn) != seen_instructions.end()) {
// We somehow went in a circle. This should never be possible. Not even
// cyclic graphs are supposed to confuse us this much.
@@ -410,7 +419,8 @@
// used in a "less" comparator for the purpose of storing pointers to basic
// blocks in STL containers; this gives an easy option to use STL to find
// shared tail sequences of basic blocks.
-static int PointerCompare(const BasicBlock *block1, const BasicBlock *block2,
+static int PointerCompare(const BasicBlock* block1,
+ const BasicBlock* block2,
const TargetsToBlocks& blocks) {
// Return <0, 0, or >0 depending on the ordering of "block1" and "block2".
// If we are looking at the exact same block, this is trivial and we don't
@@ -486,7 +496,7 @@
}
}
-void CodeGen::MergeTails(TargetsToBlocks *blocks) {
+void CodeGen::MergeTails(TargetsToBlocks* blocks) {
// We enter all of our basic blocks into a set using the BasicBlock::Less()
// comparator. This naturally results in blocks with identical tails of
// instructions to map to the same entry in the set. Whenever we discover
@@ -500,12 +510,11 @@
// the future, we might decide to revisit this decision and attempt to
// merge arbitrary sub-sequences of instructions.
BasicBlock::Less<TargetsToBlocks> less(*blocks, PointerCompare);
- typedef std::set<BasicBlock *, BasicBlock::Less<TargetsToBlocks> > Set;
+ typedef std::set<BasicBlock*, BasicBlock::Less<TargetsToBlocks> > Set;
Set seen_basic_blocks(less);
- for (TargetsToBlocks::iterator iter = blocks->begin();
- iter != blocks->end();
+ for (TargetsToBlocks::iterator iter = blocks->begin(); iter != blocks->end();
++iter) {
- BasicBlock *bb = iter->second;
+ BasicBlock* bb = iter->second;
Set::const_iterator entry = seen_basic_blocks.find(bb);
if (entry == seen_basic_blocks.end()) {
// This is the first time we see this particular sequence of
@@ -521,34 +530,36 @@
}
}
-void CodeGen::ComputeIncomingBranches(BasicBlock *block,
+void CodeGen::ComputeIncomingBranches(BasicBlock* block,
const TargetsToBlocks& targets_to_blocks,
- IncomingBranches *incoming_branches) {
+ IncomingBranches* incoming_branches) {
// We increment the number of incoming branches each time we encounter a
// basic block. But we only traverse recursively the very first time we
// encounter a new block. This is necessary to make topological sorting
// work correctly.
if (++(*incoming_branches)[block] == 1) {
- Instruction *last_insn = block->instructions.back();
+ Instruction* last_insn = block->instructions.back();
if (BPF_CLASS(last_insn->code) == BPF_JMP) {
- ComputeIncomingBranches(
- targets_to_blocks.find(last_insn->jt_ptr)->second,
- targets_to_blocks, incoming_branches);
+ ComputeIncomingBranches(targets_to_blocks.find(last_insn->jt_ptr)->second,
+ targets_to_blocks,
+ incoming_branches);
if (BPF_OP(last_insn->code) != BPF_JA) {
ComputeIncomingBranches(
- targets_to_blocks.find(last_insn->jf_ptr)->second,
- targets_to_blocks, incoming_branches);
+ targets_to_blocks.find(last_insn->jf_ptr)->second,
+ targets_to_blocks,
+ incoming_branches);
}
} else if (BPF_CLASS(last_insn->code) != BPF_RET) {
ComputeIncomingBranches(targets_to_blocks.find(last_insn->next)->second,
- targets_to_blocks, incoming_branches);
+ targets_to_blocks,
+ incoming_branches);
}
}
}
-void CodeGen::TopoSortBasicBlocks(BasicBlock *first_block,
+void CodeGen::TopoSortBasicBlocks(BasicBlock* first_block,
const TargetsToBlocks& blocks,
- BasicBlocks *basic_blocks) {
+ BasicBlocks* basic_blocks) {
// Textbook implementation of a toposort. We keep looking for basic blocks
// that don't have any incoming branches (initially, this is just the
// "first_block") and add them to the topologically sorted list of
@@ -562,7 +573,7 @@
IncomingBranches unordered_blocks;
ComputeIncomingBranches(first_block, blocks, &unordered_blocks);
- std::set<BasicBlock *> heads;
+ std::set<BasicBlock*> heads;
for (;;) {
// Move block from "unordered_blocks" to "basic_blocks".
basic_blocks->push_back(first_block);
@@ -570,7 +581,7 @@
// Inspect last instruction in the basic block. This is typically either a
// jump or a return statement. But it could also be a "normal" instruction
// that is followed by a jump target.
- Instruction *last_insn = first_block->instructions.back();
+ Instruction* last_insn = first_block->instructions.back();
if (BPF_CLASS(last_insn->code) == BPF_JMP) {
// Remove outgoing branches. This might end up moving our descendants
// into set of "head" nodes that no longer have any incoming branches.
@@ -598,7 +609,7 @@
// Our basic block is supposed to be followed by "last_insn->next",
// but dependencies prevent this from happening. Insert a BPF_JA
// instruction to correct the code flow.
- Instruction *ja = MakeInstruction(BPF_JMP+BPF_JA, 0, last_insn->next);
+ Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, last_insn->next);
first_block->instructions.push_back(ja);
last_insn->next = ja;
}
@@ -616,7 +627,7 @@
}
}
-void CodeGen::ComputeRelativeJumps(BasicBlocks *basic_blocks,
+void CodeGen::ComputeRelativeJumps(BasicBlocks* basic_blocks,
const TargetsToBlocks& targets_to_blocks) {
// While we previously used pointers in jt_ptr and jf_ptr to link jump
// instructions to their targets, we now convert these jumps to relative
@@ -626,38 +637,37 @@
// Since we just completed a toposort, all jump targets are guaranteed to
// go forward. This means, iterating over the basic blocks in reverse makes
// it trivial to compute the correct offsets.
- BasicBlock *bb = NULL;
- BasicBlock *last_bb = NULL;
+ BasicBlock* bb = NULL;
+ BasicBlock* last_bb = NULL;
for (BasicBlocks::reverse_iterator iter = basic_blocks->rbegin();
iter != basic_blocks->rend();
++iter) {
last_bb = bb;
bb = *iter;
- Instruction *insn = bb->instructions.back();
+ Instruction* insn = bb->instructions.back();
if (BPF_CLASS(insn->code) == BPF_JMP) {
// Basic block ended in a jump instruction. We can now compute the
// appropriate offsets.
if (BPF_OP(insn->code) == BPF_JA) {
// "Always" jumps use the 32bit "k" field for the offset, instead
// of the 8bit "jt" and "jf" fields.
- int jmp =
- offset - targets_to_blocks.find(insn->jt_ptr)->second->offset;
- insn->k = jmp;
+ int jmp = offset - targets_to_blocks.find(insn->jt_ptr)->second->offset;
+ insn->k = jmp;
insn->jt = insn->jf = 0;
} else {
// The offset computations for conditional jumps are just the same
// as for "always" jumps.
- int jt = offset-targets_to_blocks.find(insn->jt_ptr)->second->offset;
- int jf = offset-targets_to_blocks.find(insn->jf_ptr)->second->offset;
+ int jt = offset - targets_to_blocks.find(insn->jt_ptr)->second->offset;
+ int jf = offset - targets_to_blocks.find(insn->jf_ptr)->second->offset;
// There is an added complication, because conditional relative jumps
// can only jump at most 255 instructions forward. If we have to jump
// further, insert an extra "always" jump.
Instructions::size_type jmp = bb->instructions.size();
if (jt > 255 || (jt == 255 && jf > 255)) {
- Instruction *ja = MakeInstruction(BPF_JMP+BPF_JA, 0, insn->jt_ptr);
+ Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, insn->jt_ptr);
bb->instructions.push_back(ja);
- ja->k = jt;
+ ja->k = jt;
ja->jt = ja->jf = 0;
// The newly inserted "always" jump, of course, requires us to adjust
@@ -666,9 +676,9 @@
++jf;
}
if (jf > 255) {
- Instruction *ja = MakeInstruction(BPF_JMP+BPF_JA, 0, insn->jf_ptr);
+ Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, insn->jf_ptr);
bb->instructions.insert(bb->instructions.begin() + jmp, ja);
- ja->k = jf;
+ ja->k = jf;
ja->jt = ja->jf = 0;
// Again, we have to adjust the jump targets in the original
@@ -696,7 +706,7 @@
}
void CodeGen::ConcatenateBasicBlocks(const BasicBlocks& basic_blocks,
- Sandbox::Program *program) {
+ Sandbox::Program* program) {
// Our basic blocks have been sorted and relative jump offsets have been
// computed. The last remaining step is for all the instructions in our
// basic blocks to be concatenated into a BPF program.
@@ -710,24 +720,25 @@
++insn_iter) {
const Instruction& insn = **insn_iter;
program->push_back(
- (struct sock_filter) { insn.code, insn.jt, insn.jf, insn.k });
+ (struct sock_filter) {insn.code, insn.jt, insn.jf, insn.k});
}
}
return;
}
-void CodeGen::Compile(Instruction *instructions, Sandbox::Program *program) {
+void CodeGen::Compile(Instruction* instructions, Sandbox::Program* program) {
if (compiled_) {
- SANDBOX_DIE("Cannot call Compile() multiple times. Create a new code "
- "generator instead");
+ SANDBOX_DIE(
+ "Cannot call Compile() multiple times. Create a new code "
+ "generator instead");
}
compiled_ = true;
BranchTargets branch_targets;
FindBranchTargets(*instructions, &branch_targets);
TargetsToBlocks all_blocks;
- BasicBlock *first_block =
- CutGraphIntoBasicBlocks(instructions, branch_targets, &all_blocks);
+ BasicBlock* first_block =
+ CutGraphIntoBasicBlocks(instructions, branch_targets, &all_blocks);
MergeTails(&all_blocks);
BasicBlocks basic_blocks;
TopoSortBasicBlocks(first_block, all_blocks, &basic_blocks);
diff --git a/sandbox/linux/seccomp-bpf/codegen.h b/sandbox/linux/seccomp-bpf/codegen.h
index 88521c2..6ef7603 100644
--- a/sandbox/linux/seccomp-bpf/codegen.h
+++ b/sandbox/linux/seccomp-bpf/codegen.h
@@ -13,14 +13,13 @@
#include "sandbox/linux/seccomp-bpf/instruction.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
-
namespace playground2 {
-typedef std::vector<Instruction *> Instructions;
-typedef std::vector<BasicBlock *> BasicBlocks;
-typedef std::map<const Instruction *, int> BranchTargets;
-typedef std::map<const Instruction *, BasicBlock *> TargetsToBlocks;
-typedef std::map<const BasicBlock *, int> IncomingBranches;
+typedef std::vector<Instruction*> Instructions;
+typedef std::vector<BasicBlock*> BasicBlocks;
+typedef std::map<const Instruction*, int> BranchTargets;
+typedef std::map<const Instruction*, BasicBlock*> TargetsToBlocks;
+typedef std::map<const BasicBlock*, int> IncomingBranches;
// The code generator instantiates a basic compiler that can convert a
// graph of BPF instructions into a well-formed stream of BPF instructions.
@@ -66,16 +65,19 @@
// are owned by the CodeGen object. They do not need to be explicitly
// deleted.
// For details on the possible parameters refer to <linux/filter.h>
- Instruction *MakeInstruction(uint16_t code, uint32_t k,
- Instruction *next = NULL);
- Instruction *MakeInstruction(uint16_t code, const ErrorCode& err);
- Instruction *MakeInstruction(uint16_t code, uint32_t k,
- Instruction *jt, Instruction *jf);
+ Instruction* MakeInstruction(uint16_t code,
+ uint32_t k,
+ Instruction* next = NULL);
+ Instruction* MakeInstruction(uint16_t code, const ErrorCode& err);
+ Instruction* MakeInstruction(uint16_t code,
+ uint32_t k,
+ Instruction* jt,
+ Instruction* jf);
// Join two (sequences of) instructions. This is useful, if the "next"
// parameter had not originally been given in the call to MakeInstruction(),
// or if a (conditional) jump still has an unsatisfied target.
- void JoinInstructions(Instruction *head, Instruction *tail);
+ void JoinInstructions(Instruction* head, Instruction* tail);
// Traverse the graph of instructions and visit each instruction once.
// Traversal order is implementation-defined. It is acceptable to make
@@ -83,68 +85,69 @@
// do not affect traversal.
// The "fnc" function gets called with both the instruction and the opaque
// "aux" pointer.
- void Traverse(Instruction *, void (*fnc)(Instruction *, void *aux),
- void *aux);
+ void Traverse(Instruction*, void (*fnc)(Instruction*, void* aux), void* aux);
// Compiles the graph of instructions into a BPF program that can be passed
// to the kernel. Please note that this function modifies the graph in place
// and must therefore only be called once per graph.
- void Compile(Instruction *instructions, Sandbox::Program *program);
+ void Compile(Instruction* instructions, Sandbox::Program* program);
private:
friend class CodeGenUnittestHelper;
// Find all the instructions that are the target of BPF_JMPs.
void FindBranchTargets(const Instruction& instructions,
- BranchTargets *branch_targets);
+ BranchTargets* branch_targets);
// Combine instructions between "head" and "tail" into a new basic block.
// Basic blocks are defined as sequences of instructions whose only branch
// target is the very first instruction; furthermore, any BPF_JMP or BPF_RET
// instruction must be at the very end of the basic block.
- BasicBlock *MakeBasicBlock(Instruction *head, Instruction *tail);
+ BasicBlock* MakeBasicBlock(Instruction* head, Instruction* tail);
// Creates a basic block and adds it to "basic_blocks"; sets "first_block"
// if it is still NULL.
- void AddBasicBlock(Instruction *head, Instruction *tail,
+ void AddBasicBlock(Instruction* head,
+ Instruction* tail,
const BranchTargets& branch_targets,
- TargetsToBlocks *basic_blocks, BasicBlock **first_block);
+ TargetsToBlocks* basic_blocks,
+ BasicBlock** first_block);
// Cuts the DAG of instructions into basic blocks.
- BasicBlock *CutGraphIntoBasicBlocks(Instruction *instructions,
+ BasicBlock* CutGraphIntoBasicBlocks(Instruction* instructions,
const BranchTargets& branch_targets,
- TargetsToBlocks *blocks);
+ TargetsToBlocks* blocks);
// Find common tail sequences of basic blocks and coalesce them.
- void MergeTails(TargetsToBlocks *blocks);
+ void MergeTails(TargetsToBlocks* blocks);
// For each basic block, compute the number of incoming branches.
- void ComputeIncomingBranches(BasicBlock *block,
+ void ComputeIncomingBranches(BasicBlock* block,
const TargetsToBlocks& targets_to_blocks,
- IncomingBranches *incoming_branches);
+ IncomingBranches* incoming_branches);
// Topologically sort the basic blocks so that all jumps are forward jumps.
// This is a requirement for any well-formed BPF program.
- void TopoSortBasicBlocks(BasicBlock *first_block,
+ void TopoSortBasicBlocks(BasicBlock* first_block,
const TargetsToBlocks& blocks,
- BasicBlocks *basic_blocks);
+ BasicBlocks* basic_blocks);
// Convert jt_ptr_ and jf_ptr_ fields in BPF_JMP instructions to valid
// jt_ and jf_ jump offsets. This can result in BPF_JA instructions being
// inserted, if we need to jump over more than 256 instructions.
- void ComputeRelativeJumps(BasicBlocks *basic_blocks,
+ void ComputeRelativeJumps(BasicBlocks* basic_blocks,
const TargetsToBlocks& targets_to_blocks);
// Concatenate instructions from all basic blocks into a BPF program that
// can be passed to the kernel.
- void ConcatenateBasicBlocks(const BasicBlocks&, Sandbox::Program *program);
+ void ConcatenateBasicBlocks(const BasicBlocks&, Sandbox::Program* program);
// We stick all instructions and basic blocks into pools that get destroyed
// when the CodeGen object is destroyed. This way, we neither need to worry
// about explicitly managing ownership, nor do we need to worry about using
// smart pointers in the presence of circular references.
Instructions instructions_;
- BasicBlocks basic_blocks_;
+ BasicBlocks basic_blocks_;
// Compile() must only ever be called once as it makes destructive changes
// to the DAG.
diff --git a/sandbox/linux/seccomp-bpf/codegen_unittest.cc b/sandbox/linux/seccomp-bpf/codegen_unittest.cc
index d24bcf2..ccc5656 100644
--- a/sandbox/linux/seccomp-bpf/codegen_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/codegen_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <errno.h>
+
#include <algorithm>
#include <set>
#include <vector>
diff --git a/sandbox/linux/seccomp-bpf/demo.cc b/sandbox/linux/seccomp-bpf/demo.cc
index b2622ec..48df073 100644
--- a/sandbox/linux/seccomp-bpf/demo.cc
+++ b/sandbox/linux/seccomp-bpf/demo.cc
@@ -26,7 +26,9 @@
#include <time.h>
#include <unistd.h>
+#include "base/posix/eintr_wrapper.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/services/linux_syscalls.h"
using playground2::arch_seccomp_data;
using playground2::ErrorCode;
@@ -221,7 +223,7 @@
char buf[sizeof(msg0) - 1 + 25 + sizeof(msg1)];
*buf = '\000';
- strncat(buf, msg0, sizeof(buf));
+ strncat(buf, msg0, sizeof(buf) - 1);
char *ptr = strrchr(buf, '\000');
itoa_r(data.nr, ptr, sizeof(buf) - (ptr - buf));
@@ -418,7 +420,7 @@
}
Sandbox sandbox;
sandbox.set_proc_fd(proc_fd);
- sandbox.SetSandboxPolicy(Evaluator, NULL);
+ sandbox.SetSandboxPolicyDeprecated(Evaluator, NULL);
sandbox.StartSandbox();
// Check that we can create threads
diff --git a/sandbox/linux/seccomp-bpf/die.cc b/sandbox/linux/seccomp-bpf/die.cc
index dfc59a5..594740c 100644
--- a/sandbox/linux/seccomp-bpf/die.cc
+++ b/sandbox/linux/seccomp-bpf/die.cc
@@ -9,10 +9,11 @@
#include <string>
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
#include "sandbox/linux/seccomp-bpf/syscall.h"
-
namespace playground2 {
void Die::ExitGroup() {
@@ -29,8 +30,9 @@
// succeeded in doing so. Nonetheless, triggering a fatal signal could help
// us terminate.
signal(SIGSEGV, SIG_DFL);
- SandboxSyscall(__NR_prctl, PR_SET_DUMPABLE, (void *)0, (void *)0, (void *)0);
- if (*(volatile char *)0) { }
+ SandboxSyscall(__NR_prctl, PR_SET_DUMPABLE, (void*)0, (void*)0, (void*)0);
+ if (*(volatile char*)0) {
+ }
// If there is no way for us to ask for the program to exit, the next
// best thing we can do is to loop indefinitely. Maybe, somebody will notice
@@ -42,37 +44,29 @@
}
}
-void Die::SandboxDie(const char *msg, const char *file, int line) {
+void Die::SandboxDie(const char* msg, const char* file, int line) {
if (simple_exit_) {
LogToStderr(msg, file, line);
} else {
- #if defined(SECCOMP_BPF_STANDALONE)
- Die::LogToStderr(msg, file, line);
- #else
- logging::LogMessage(file, line, logging::LOG_FATAL).stream() << msg;
- #endif
+ logging::LogMessage(file, line, logging::LOG_FATAL).stream() << msg;
}
ExitGroup();
}
-void Die::RawSandboxDie(const char *msg) {
+void Die::RawSandboxDie(const char* msg) {
if (!msg)
msg = "";
RAW_LOG(FATAL, msg);
ExitGroup();
}
-void Die::SandboxInfo(const char *msg, const char *file, int line) {
+void Die::SandboxInfo(const char* msg, const char* file, int line) {
if (!suppress_info_) {
- #if defined(SECCOMP_BPF_STANDALONE)
- Die::LogToStderr(msg, file, line);
- #else
logging::LogMessage(file, line, logging::LOG_INFO).stream() << msg;
- #endif
}
}
-void Die::LogToStderr(const char *msg, const char *file, int line) {
+void Die::LogToStderr(const char* msg, const char* file, int line) {
if (msg) {
char buf[40];
snprintf(buf, sizeof(buf), "%d", line);
@@ -80,11 +74,12 @@
// No need to loop. Short write()s are unlikely and if they happen we
// probably prefer them over a loop that blocks.
- if (HANDLE_EINTR(SandboxSyscall(__NR_write, 2, s.c_str(), s.length()))) { }
+ ignore_result(
+ HANDLE_EINTR(SandboxSyscall(__NR_write, 2, s.c_str(), s.length())));
}
}
-bool Die::simple_exit_ = false;
+bool Die::simple_exit_ = false;
bool Die::suppress_info_ = false;
} // namespace
diff --git a/sandbox/linux/seccomp-bpf/die.h b/sandbox/linux/seccomp-bpf/die.h
index 7c95997..2ed3f07 100644
--- a/sandbox/linux/seccomp-bpf/die.h
+++ b/sandbox/linux/seccomp-bpf/die.h
@@ -5,24 +5,23 @@
#ifndef SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
#define SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
-#include "sandbox/linux/seccomp-bpf/port.h"
-
+#include "base/basictypes.h"
namespace playground2 {
+// This is the main API for using this file. Prints a error message and
+// exits with a fatal error. This is not async-signal safe.
+#define SANDBOX_DIE(m) playground2::Die::SandboxDie(m, __FILE__, __LINE__)
+
+// An async signal safe version of the same API. Won't print the filename
+// and line numbers.
+#define RAW_SANDBOX_DIE(m) playground2::Die::RawSandboxDie(m)
+
+// Adds an informational message to the log file or stderr as appropriate.
+#define SANDBOX_INFO(m) playground2::Die::SandboxInfo(m, __FILE__, __LINE__)
+
class Die {
public:
- // This is the main API for using this file. Prints a error message and
- // exits with a fatal error. This is not async-signal safe.
- #define SANDBOX_DIE(m) playground2::Die::SandboxDie(m, __FILE__, __LINE__)
-
- // An async signal safe version of the same API. Won't print the filename
- // and line numbers.
- #define RAW_SANDBOX_DIE(m) playground2::Die::RawSandboxDie(m)
-
- // Adds an informational message to the log file or stderr as appropriate.
- #define SANDBOX_INFO(m) playground2::Die::SandboxInfo(m, __FILE__, __LINE__)
-
// Terminate the program, even if the current sandbox policy prevents some
// of the more commonly used functions used for exiting.
// Most users would want to call SANDBOX_DIE() instead, as it logs extra
@@ -32,18 +31,18 @@
// This method gets called by SANDBOX_DIE(). There is normally no reason
// to call it directly unless you are defining your own exiting macro.
- static void SandboxDie(const char *msg, const char *file, int line)
- __attribute__((noreturn));
+ static void SandboxDie(const char* msg, const char* file, int line)
+ __attribute__((noreturn));
- static void RawSandboxDie(const char *msg) __attribute__((noreturn));
+ static void RawSandboxDie(const char* msg) __attribute__((noreturn));
// This method gets called by SANDBOX_INFO(). There is normally no reason
// to call it directly unless you are defining your own logging macro.
- static void SandboxInfo(const char *msg, const char *file, int line);
+ static void SandboxInfo(const char* msg, const char* file, int line);
// Writes a message to stderr. Used as a fall-back choice, if we don't have
// any other way to report an error.
- static void LogToStderr(const char *msg, const char *file, int line);
+ static void LogToStderr(const char* msg, const char* file, int line);
// We generally want to run all exit handlers. This means, on SANDBOX_DIE()
// we should be calling LOG(FATAL). But there are some situations where
diff --git a/sandbox/linux/seccomp-bpf/errorcode.cc b/sandbox/linux/seccomp-bpf/errorcode.cc
index ab89d73..e517d38 100644
--- a/sandbox/linux/seccomp-bpf/errorcode.cc
+++ b/sandbox/linux/seccomp-bpf/errorcode.cc
@@ -5,35 +5,36 @@
#include "sandbox/linux/seccomp-bpf/die.h"
#include "sandbox/linux/seccomp-bpf/errorcode.h"
-
namespace playground2 {
ErrorCode::ErrorCode(int err) {
switch (err) {
- case ERR_ALLOWED:
- err_ = SECCOMP_RET_ALLOW;
- error_type_ = ET_SIMPLE;
- break;
- case ERR_MIN_ERRNO ... ERR_MAX_ERRNO:
- err_ = SECCOMP_RET_ERRNO + err;
- error_type_ = ET_SIMPLE;
- break;
- default:
- SANDBOX_DIE("Invalid use of ErrorCode object");
+ case ERR_ALLOWED:
+ err_ = SECCOMP_RET_ALLOW;
+ error_type_ = ET_SIMPLE;
+ break;
+ case ERR_MIN_ERRNO... ERR_MAX_ERRNO:
+ err_ = SECCOMP_RET_ERRNO + err;
+ error_type_ = ET_SIMPLE;
+ break;
+ default:
+ SANDBOX_DIE("Invalid use of ErrorCode object");
}
}
-ErrorCode::ErrorCode(Trap::TrapFnc fnc, const void *aux, bool safe,
- uint16_t id)
+ErrorCode::ErrorCode(Trap::TrapFnc fnc, const void* aux, bool safe, uint16_t id)
: error_type_(ET_TRAP),
fnc_(fnc),
- aux_(const_cast<void *>(aux)),
+ aux_(const_cast<void*>(aux)),
safe_(safe),
- err_(SECCOMP_RET_TRAP + id) {
-}
+ err_(SECCOMP_RET_TRAP + id) {}
-ErrorCode::ErrorCode(int argno, ArgType width, Operation op, uint64_t value,
- const ErrorCode *passed, const ErrorCode *failed)
+ErrorCode::ErrorCode(int argno,
+ ArgType width,
+ Operation op,
+ uint64_t value,
+ const ErrorCode* passed,
+ const ErrorCode* failed)
: error_type_(ET_COND),
value_(value),
argno_(argno),
@@ -57,12 +58,9 @@
if (error_type_ == ET_SIMPLE || error_type_ == ET_TRAP) {
return err_ == err.err_;
} else if (error_type_ == ET_COND) {
- return value_ == err.value_ &&
- argno_ == err.argno_ &&
- width_ == err.width_ &&
- op_ == err.op_ &&
- passed_->Equals(*err.passed_) &&
- failed_->Equals(*err.failed_);
+ return value_ == err.value_ && argno_ == err.argno_ &&
+ width_ == err.width_ && op_ == err.op_ &&
+ passed_->Equals(*err.passed_) && failed_->Equals(*err.failed_);
} else {
SANDBOX_DIE("Corrupted ErrorCode");
}
diff --git a/sandbox/linux/seccomp-bpf/errorcode.h b/sandbox/linux/seccomp-bpf/errorcode.h
index 61ec110..182fadb 100644
--- a/sandbox/linux/seccomp-bpf/errorcode.h
+++ b/sandbox/linux/seccomp-bpf/errorcode.h
@@ -27,7 +27,7 @@
// completely arbitrary. But we want to pick it so that is is unlikely
// to be passed in accidentally, when the user intended to return an
// "errno" (see below) value instead.
- ERR_ALLOWED = 0x04000000,
+ ERR_ALLOWED = 0x04000000,
// Deny the system call with a particular "errno" value.
// N.B.: It is also possible to return "0" here. That would normally
@@ -85,21 +85,26 @@
// need.
// TODO(markus): Check whether we should automatically emulate signed
// operations.
- OP_GREATER_UNSIGNED, OP_GREATER_EQUAL_UNSIGNED,
+ OP_GREATER_UNSIGNED,
+ OP_GREATER_EQUAL_UNSIGNED,
// Tests a system call argument against a bit mask.
// The "ALL_BITS" variant performs this test: "arg & mask == mask"
// This implies that a mask of zero always results in a passing test.
// The "ANY_BITS" variant performs this test: "arg & mask != 0"
// This implies that a mask of zero always results in a failing test.
- OP_HAS_ALL_BITS, OP_HAS_ANY_BITS,
+ OP_HAS_ALL_BITS,
+ OP_HAS_ANY_BITS,
// Total number of operations.
OP_NUM_OPS,
};
enum ErrorType {
- ET_INVALID, ET_SIMPLE, ET_TRAP, ET_COND,
+ ET_INVALID,
+ ET_SIMPLE,
+ ET_TRAP,
+ ET_COND,
};
// We allow the default constructor, as it makes the ErrorCode class
@@ -107,10 +112,7 @@
// when compiling a BPF filter, we deliberately generate an invalid
// program that will get flagged both by our Verifier class and by
// the Linux kernel.
- ErrorCode() :
- error_type_(ET_INVALID),
- err_(SECCOMP_RET_INVALID) {
- }
+ ErrorCode() : error_type_(ET_INVALID), err_(SECCOMP_RET_INVALID) {}
explicit ErrorCode(int err);
// For all practical purposes, ErrorCodes are treated as if they were
@@ -121,7 +123,7 @@
// callers handle life-cycle management for these objects.
// Destructor
- ~ErrorCode() { }
+ ~ErrorCode() {}
bool Equals(const ErrorCode& err) const;
bool LessThan(const ErrorCode& err) const;
@@ -135,8 +137,8 @@
int argno() const { return argno_; }
ArgType width() const { return width_; }
Operation op() const { return op_; }
- const ErrorCode *passed() const { return passed_; }
- const ErrorCode *failed() const { return failed_; }
+ const ErrorCode* passed() const { return passed_; }
+ const ErrorCode* failed() const { return failed_; }
struct LessThan {
bool operator()(const ErrorCode& a, const ErrorCode& b) const {
@@ -152,31 +154,35 @@
// If we are wrapping a callback, we must assign a unique id. This id is
// how the kernel tells us which one of our different SECCOMP_RET_TRAP
// cases has been triggered.
- ErrorCode(Trap::TrapFnc fnc, const void *aux, bool safe, uint16_t id);
+ ErrorCode(Trap::TrapFnc fnc, const void* aux, bool safe, uint16_t id);
// Some system calls require inspection of arguments. This constructor
// allows us to specify additional constraints.
- ErrorCode(int argno, ArgType width, Operation op, uint64_t value,
- const ErrorCode *passed, const ErrorCode *failed);
+ ErrorCode(int argno,
+ ArgType width,
+ Operation op,
+ uint64_t value,
+ const ErrorCode* passed,
+ const ErrorCode* failed);
ErrorType error_type_;
union {
// Fields needed for SECCOMP_RET_TRAP callbacks
struct {
- Trap::TrapFnc fnc_; // Callback function and arg, if trap was
- void *aux_; // triggered by the kernel's BPF filter.
- bool safe_; // Keep sandbox active while calling fnc_()
+ Trap::TrapFnc fnc_; // Callback function and arg, if trap was
+ void* aux_; // triggered by the kernel's BPF filter.
+ bool safe_; // Keep sandbox active while calling fnc_()
};
// Fields needed when inspecting additional arguments.
struct {
- uint64_t value_; // Value that we are comparing with.
- int argno_; // Syscall arg number that we are inspecting.
- ArgType width_; // Whether we are looking at a 32/64bit value.
+ uint64_t value_; // Value that we are comparing with.
+ int argno_; // Syscall arg number that we are inspecting.
+ ArgType width_; // Whether we are looking at a 32/64bit value.
Operation op_; // Comparison operation.
- const ErrorCode *passed_; // Value to be returned if comparison passed,
- const ErrorCode *failed_; // or if it failed.
+ const ErrorCode* passed_; // Value to be returned if comparison passed,
+ const ErrorCode* failed_; // or if it failed.
};
};
@@ -184,7 +190,6 @@
// the value that uniquely identifies any ErrorCode and it (typically) can
// be emitted directly into a BPF filter program.
uint32_t err_;
-
};
} // namespace
diff --git a/sandbox/linux/seccomp-bpf/errorcode_unittest.cc b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
index 10f2132..3748e51 100644
--- a/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <errno.h>
+
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
#include "sandbox/linux/tests/unit_tests.h"
diff --git a/sandbox/linux/seccomp-bpf/instruction.h b/sandbox/linux/seccomp-bpf/instruction.h
index 0fc8123..8d35187 100644
--- a/sandbox/linux/seccomp-bpf/instruction.h
+++ b/sandbox/linux/seccomp-bpf/instruction.h
@@ -7,7 +7,6 @@
#include <stdint.h>
-
namespace playground2 {
// The fields in this structure have the same meaning as the corresponding
@@ -27,12 +26,12 @@
struct Instruction {
// Constructor for an non-jumping instruction or for an unconditional
// "always" jump.
- Instruction(uint16_t c, uint32_t parm, Instruction *n) :
- code(c), next(n), k(parm) { }
+ Instruction(uint16_t c, uint32_t parm, Instruction* n)
+ : code(c), next(n), k(parm) {}
// Constructor for a conditional jump instruction.
- Instruction(uint16_t c, uint32_t parm, Instruction *jt, Instruction *jf) :
- code(c), jt_ptr(jt), jf_ptr(jf), k(parm) { }
+ Instruction(uint16_t c, uint32_t parm, Instruction* jt, Instruction* jf)
+ : code(c), jt_ptr(jt), jf_ptr(jf), k(parm) {}
uint16_t code;
union {
@@ -47,13 +46,13 @@
// keys in a TargetsToBlocks map and should no longer be dereferenced
// directly.
struct {
- Instruction *jt_ptr, *jf_ptr;
+ Instruction* jt_ptr, *jf_ptr;
};
// While assembling the BPF program, non-jumping instructions are linked
// by the "next_" pointer. This field is no longer needed when we have
// computed basic blocks.
- Instruction *next;
+ Instruction* next;
};
uint32_t k;
};
diff --git a/sandbox/linux/seccomp-bpf/port.h b/sandbox/linux/seccomp-bpf/port.h
deleted file mode 100644
index f10b148..0000000
--- a/sandbox/linux/seccomp-bpf/port.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Commonly used macro definitions to make the code build in different
-// target environments (e.g. as part of Chrome vs. stand-alone)
-
-#ifndef SANDBOX_LINUX_SECCOMP_BPF_PORT_H__
-#define SANDBOX_LINUX_SECCOMP_BPF_PORT_H__
-
-#if !defined(SECCOMP_BPF_STANDALONE)
- #include "base/basictypes.h"
- #include "base/logging.h"
- #include "base/posix/eintr_wrapper.h"
-#else
- #define arraysize(x) (sizeof(x)/sizeof(*(x)))
-
- #define HANDLE_EINTR TEMP_FAILURE_RETRY
-
- #define DISALLOW_COPY_AND_ASSIGN(TypeName) \
- TypeName(const TypeName&); \
- void operator=(const TypeName&)
-
- #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
- TypeName(); \
- DISALLOW_COPY_AND_ASSIGN(TypeName)
-
- template <bool>
- struct CompileAssert {
- };
-
- #define COMPILE_ASSERT(expr, msg) \
- typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
-#endif
-
-#endif // SANDBOX_LINUX_SECCOMP_BPF_PORT_H__
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
index 1dc5eae..3a4b678 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+
// Some headers on Android are missing cdefs: crbug.com/172337.
// (We can't use OS_ANDROID here since build_config.h is not included).
#if defined(ANDROID)
@@ -18,63 +20,54 @@
#include <time.h>
#include <unistd.h>
-#ifndef SECCOMP_BPF_STANDALONE
+#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/posix/eintr_wrapper.h"
-#endif
-
#include "sandbox/linux/seccomp-bpf/codegen.h"
-#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h"
#include "sandbox/linux/seccomp-bpf/syscall.h"
#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
#include "sandbox/linux/seccomp-bpf/verifier.h"
-namespace {
+namespace playground2 {
-using playground2::ErrorCode;
-using playground2::Instruction;
-using playground2::Sandbox;
-using playground2::Trap;
-using playground2::arch_seccomp_data;
+namespace {
const int kExpectedExitCode = 100;
-template<class T> int popcount(T x);
-template<> int popcount<unsigned int>(unsigned int x) {
+int popcount(uint32_t x) {
return __builtin_popcount(x);
}
-template<> int popcount<unsigned long>(unsigned long x) {
- return __builtin_popcountl(x);
-}
-template<> int popcount<unsigned long long>(unsigned long long x) {
- return __builtin_popcountll(x);
-}
+#if !defined(NDEBUG)
void WriteFailedStderrSetupMessage(int out_fd) {
const char* error_string = strerror(errno);
- static const char msg[] = "You have reproduced a puzzling issue.\n"
- "Please, report to crbug.com/152530!\n"
- "Failed to set up stderr: ";
- if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg)-1)) > 0 && error_string &&
+ static const char msg[] =
+ "You have reproduced a puzzling issue.\n"
+ "Please, report to crbug.com/152530!\n"
+ "Failed to set up stderr: ";
+ if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg) - 1)) > 0 && error_string &&
HANDLE_EINTR(write(out_fd, error_string, strlen(error_string))) > 0 &&
HANDLE_EINTR(write(out_fd, "\n", 1))) {
}
}
+#endif // !defined(NDEBUG)
// We define a really simple sandbox policy. It is just good enough for us
// to tell that the sandbox has actually been activated.
-ErrorCode ProbeEvaluator(Sandbox *, int sysnum, void *) __attribute__((const));
-ErrorCode ProbeEvaluator(Sandbox *, int sysnum, void *) {
+ErrorCode ProbeEvaluator(Sandbox*, int sysnum, void*) __attribute__((const));
+ErrorCode ProbeEvaluator(Sandbox*, int sysnum, void*) {
switch (sysnum) {
- case __NR_getpid:
- // Return EPERM so that we can check that the filter actually ran.
- return ErrorCode(EPERM);
- case __NR_exit_group:
- // Allow exit() with a non-default return code.
- return ErrorCode(ErrorCode::ERR_ALLOWED);
- default:
- // Make everything else fail in an easily recognizable way.
- return ErrorCode(EINVAL);
+ case __NR_getpid:
+ // Return EPERM so that we can check that the filter actually ran.
+ return ErrorCode(EPERM);
+ case __NR_exit_group:
+ // Allow exit() with a non-default return code.
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ default:
+ // Make everything else fail in an easily recognizable way.
+ return ErrorCode(EINVAL);
}
}
@@ -84,7 +77,7 @@
}
}
-ErrorCode AllowAllEvaluator(Sandbox *, int sysnum, void *) {
+ErrorCode AllowAllEvaluator(Sandbox*, int sysnum, void*) {
if (!Sandbox::IsValidSyscallNumber(sysnum)) {
return ErrorCode(ENOSYS);
}
@@ -110,12 +103,11 @@
struct stat sb;
int task = -1;
- if ((task = openat(proc_fd, "self/task", O_RDONLY|O_DIRECTORY)) < 0 ||
- fstat(task, &sb) != 0 ||
- sb.st_nlink != 3 ||
- HANDLE_EINTR(close(task))) {
+ if ((task = openat(proc_fd, "self/task", O_RDONLY | O_DIRECTORY)) < 0 ||
+ fstat(task, &sb) != 0 || sb.st_nlink != 3 || HANDLE_EINTR(close(task))) {
if (task >= 0) {
- if (HANDLE_EINTR(close(task))) { }
+ if (HANDLE_EINTR(close(task))) {
+ }
}
return false;
}
@@ -131,14 +123,13 @@
// Function that can be passed as a callback function to CodeGen::Traverse().
// Checks whether the "insn" returns an UnsafeTrap() ErrorCode. If so, it
// sets the "bool" variable pointed to by "aux".
-void CheckForUnsafeErrorCodes(Instruction *insn, void *aux) {
- bool *is_unsafe = static_cast<bool *>(aux);
+void CheckForUnsafeErrorCodes(Instruction* insn, void* aux) {
+ bool* is_unsafe = static_cast<bool*>(aux);
if (!*is_unsafe) {
- if (BPF_CLASS(insn->code) == BPF_RET &&
- insn->k > SECCOMP_RET_TRAP &&
+ if (BPF_CLASS(insn->code) == BPF_RET && insn->k > SECCOMP_RET_TRAP &&
insn->k - SECCOMP_RET_TRAP <= SECCOMP_RET_DATA) {
const ErrorCode& err =
- Trap::ErrorCodeFromTrapId(insn->k & SECCOMP_RET_DATA);
+ Trap::ErrorCodeFromTrapId(insn->k & SECCOMP_RET_DATA);
if (err.error_type() != ErrorCode::ET_INVALID && !err.safe()) {
*is_unsafe = true;
}
@@ -148,7 +139,7 @@
// A Trap() handler that returns an "errno" value. The value is encoded
// in the "aux" parameter.
-intptr_t ReturnErrno(const struct arch_seccomp_data&, void *aux) {
+intptr_t ReturnErrno(const struct arch_seccomp_data&, void* aux) {
// TrapFnc functions report error by following the native kernel convention
// of returning an exit code in the range of -1..-4096. They do not try to
// set errno themselves. The glibc wrapper that triggered the SIGSYS will
@@ -161,7 +152,7 @@
// Checks whether the "insn" returns an errno value from a BPF filter. If so,
// it rewrites the instruction to instead call a Trap() handler that does
// the same thing. "aux" is ignored.
-void RedirectToUserspace(Instruction *insn, void *aux) {
+void RedirectToUserspace(Instruction* insn, void* aux) {
// When inside an UnsafeTrap() callback, we want to allow all system calls.
// This means, we must conditionally disable the sandbox -- and that's not
// something that kernel-side BPF filters can do, as they cannot inspect
@@ -171,52 +162,72 @@
// The performance penalty for this extra round-trip to user-space is not
// actually that bad, as we only ever pay it for denied system calls; and a
// typical program has very few of these.
- Sandbox *sandbox = static_cast<Sandbox *>(aux);
+ Sandbox* sandbox = static_cast<Sandbox*>(aux);
if (BPF_CLASS(insn->code) == BPF_RET &&
(insn->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
insn->k = sandbox->Trap(ReturnErrno,
- reinterpret_cast<void *>(insn->k & SECCOMP_RET_DATA)).err();
+ reinterpret_cast<void*>(insn->k & SECCOMP_RET_DATA)).err();
}
}
-// Stackable wrapper around an Evaluators handler. Changes ErrorCodes
-// returned by a system call evaluator to match the changes made by
-// RedirectToUserspace(). "aux" should be pointer to wrapped system call
-// evaluator.
-ErrorCode RedirectToUserspaceEvalWrapper(Sandbox *sandbox, int sysnum,
- void *aux) {
- // We need to replicate the behavior of RedirectToUserspace(), so that our
- // Verifier can still work correctly.
- Sandbox::Evaluators *evaluators =
- reinterpret_cast<Sandbox::Evaluators *>(aux);
- const std::pair<Sandbox::EvaluateSyscall, void *>& evaluator =
- *evaluators->begin();
-
- ErrorCode err = evaluator.first(sandbox, sysnum, evaluator.second);
- if ((err.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
- return sandbox->Trap(ReturnErrno,
- reinterpret_cast<void *>(err.err() & SECCOMP_RET_DATA));
+// This wraps an existing policy and changes its behavior to match the changes
+// made by RedirectToUserspace(). This is part of the framework that allows BPF
+// evaluation in userland.
+// TODO(markus): document the code inside better.
+class RedirectToUserSpacePolicyWrapper : public SandboxBpfPolicy {
+ public:
+ explicit RedirectToUserSpacePolicyWrapper(
+ const SandboxBpfPolicy* wrapped_policy)
+ : wrapped_policy_(wrapped_policy) {
+ DCHECK(wrapped_policy_);
}
- return err;
+
+ virtual ErrorCode EvaluateSyscall(Sandbox* sandbox_compiler,
+ int system_call_number) const OVERRIDE {
+ ErrorCode err =
+ wrapped_policy_->EvaluateSyscall(sandbox_compiler, system_call_number);
+ if ((err.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
+ return sandbox_compiler->Trap(
+ ReturnErrno, reinterpret_cast<void*>(err.err() & SECCOMP_RET_DATA));
+ }
+ return err;
+ }
+
+ private:
+ const SandboxBpfPolicy* wrapped_policy_;
+ DISALLOW_COPY_AND_ASSIGN(RedirectToUserSpacePolicyWrapper);
+};
+
+intptr_t BpfFailure(const struct arch_seccomp_data&, void* aux) {
+ SANDBOX_DIE(static_cast<char*>(aux));
}
-intptr_t BpfFailure(const struct arch_seccomp_data&, void *aux) {
- SANDBOX_DIE(static_cast<char *>(aux));
-}
+// This class allows compatibility with the old, deprecated SetSandboxPolicy.
+class CompatibilityPolicy : public SandboxBpfPolicy {
+ public:
+ CompatibilityPolicy(Sandbox::EvaluateSyscall syscall_evaluator, void* aux)
+ : syscall_evaluator_(syscall_evaluator), aux_(aux) {
+ DCHECK(syscall_evaluator_);
+ }
+
+ virtual ErrorCode EvaluateSyscall(Sandbox* sandbox_compiler,
+ int system_call_number) const OVERRIDE {
+ return syscall_evaluator_(sandbox_compiler, system_call_number, aux_);
+ }
+
+ private:
+ Sandbox::EvaluateSyscall syscall_evaluator_;
+ void* aux_;
+ DISALLOW_COPY_AND_ASSIGN(CompatibilityPolicy);
+};
} // namespace
-// The kernel gives us a sandbox, we turn it into a playground :-)
-// This is version 2 of the playground; version 1 was built on top of
-// pre-BPF seccomp mode.
-namespace playground2 {
-
Sandbox::Sandbox()
: quiet_(false),
proc_fd_(-1),
- evaluators_(new Evaluators),
- conds_(new Conds) {
-}
+ conds_(new Conds),
+ sandbox_has_started_(false) {}
Sandbox::~Sandbox() {
// It is generally unsafe to call any memory allocator operations or to even
@@ -230,9 +241,6 @@
// The "if ()" statements are technically superfluous. But let's be explicit
// that we really don't want to run any code, when we already destroyed
// objects before setting up the sandbox.
- if (evaluators_) {
- delete evaluators_;
- }
if (conds_) {
delete conds_;
}
@@ -242,19 +250,17 @@
return SyscallIterator::IsValid(sysnum);
}
-
bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(),
Sandbox::EvaluateSyscall syscall_evaluator,
- void *aux) {
+ void* aux) {
// Block all signals before forking a child process. This prevents an
// attacker from manipulating our test by sending us an unexpected signal.
sigset_t old_mask, new_mask;
- if (sigfillset(&new_mask) ||
- sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) {
+ if (sigfillset(&new_mask) || sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) {
SANDBOX_DIE("sigprocmask() failed");
}
int fds[2];
- if (pipe2(fds, O_NONBLOCK|O_CLOEXEC)) {
+ if (pipe2(fds, O_NONBLOCK | O_CLOEXEC)) {
SANDBOX_DIE("pipe() failed");
}
@@ -312,7 +318,7 @@
#endif
}
- SetSandboxPolicy(syscall_evaluator, aux);
+ SetSandboxPolicyDeprecated(syscall_evaluator, aux);
StartSandbox();
// Run our code in the sandbox.
@@ -344,7 +350,7 @@
char buf[4096];
ssize_t len = HANDLE_EINTR(read(fds[0], buf, sizeof(buf) - 1));
if (len > 0) {
- while (len > 1 && buf[len-1] == '\n') {
+ while (len > 1 && buf[len - 1] == '\n') {
--len;
}
buf[len] = '\000';
@@ -359,9 +365,8 @@
}
bool Sandbox::KernelSupportSeccompBPF() {
- return
- RunFunctionInPolicy(ProbeProcess, ProbeEvaluator, 0) &&
- RunFunctionInPolicy(TryVsyscallProcess, AllowAllEvaluator, 0);
+ return RunFunctionInPolicy(ProbeProcess, ProbeEvaluator, 0) &&
+ RunFunctionInPolicy(TryVsyscallProcess, AllowAllEvaluator, 0);
}
Sandbox::SandboxStatus Sandbox::SupportsSeccompSandbox(int proc_fd) {
@@ -405,8 +410,8 @@
// failures (e.g. if the current kernel lacks support for BPF filters).
sandbox.quiet_ = true;
sandbox.set_proc_fd(proc_fd);
- status_ = sandbox.KernelSupportSeccompBPF()
- ? STATUS_AVAILABLE : STATUS_UNSUPPORTED;
+ status_ = sandbox.KernelSupportSeccompBPF() ? STATUS_AVAILABLE
+ : STATUS_UNSUPPORTED;
// As we are performing our tests from a child process, the run-time
// environment that is visible to the sandbox is always guaranteed to be
@@ -419,20 +424,20 @@
return status_;
}
-void Sandbox::set_proc_fd(int proc_fd) {
- proc_fd_ = proc_fd;
-}
+void Sandbox::set_proc_fd(int proc_fd) { proc_fd_ = proc_fd; }
void Sandbox::StartSandbox() {
if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) {
- SANDBOX_DIE("Trying to start sandbox, even though it is known to be "
- "unavailable");
- } else if (!evaluators_ || !conds_) {
- SANDBOX_DIE("Cannot repeatedly start sandbox. Create a separate Sandbox "
- "object instead.");
+ SANDBOX_DIE(
+ "Trying to start sandbox, even though it is known to be "
+ "unavailable");
+ } else if (sandbox_has_started_ || !conds_) {
+ SANDBOX_DIE(
+ "Cannot repeatedly start sandbox. Create a separate Sandbox "
+ "object instead.");
}
if (proc_fd_ < 0) {
- proc_fd_ = open("/proc", O_RDONLY|O_DIRECTORY);
+ proc_fd_ = open("/proc", O_RDONLY | O_DIRECTORY);
}
if (proc_fd_ < 0) {
// For now, continue in degraded mode, if we can't access /proc.
@@ -459,24 +464,35 @@
status_ = STATUS_ENABLED;
}
-void Sandbox::PolicySanityChecks(EvaluateSyscall syscall_evaluator,
- void *aux) {
- for (SyscallIterator iter(true); !iter.Done(); ) {
+void Sandbox::PolicySanityChecks(SandboxBpfPolicy* policy) {
+ for (SyscallIterator iter(true); !iter.Done();) {
uint32_t sysnum = iter.Next();
- if (!IsDenied(syscall_evaluator(this, sysnum, aux))) {
- SANDBOX_DIE("Policies should deny system calls that are outside the "
- "expected range (typically MIN_SYSCALL..MAX_SYSCALL)");
+ if (!IsDenied(policy->EvaluateSyscall(this, sysnum))) {
+ SANDBOX_DIE(
+ "Policies should deny system calls that are outside the "
+ "expected range (typically MIN_SYSCALL..MAX_SYSCALL)");
}
}
return;
}
-void Sandbox::SetSandboxPolicy(EvaluateSyscall syscall_evaluator, void *aux) {
- if (!evaluators_ || !conds_) {
+// Deprecated API, supported with a wrapper to the new API.
+void Sandbox::SetSandboxPolicyDeprecated(EvaluateSyscall syscall_evaluator,
+ void* aux) {
+ if (sandbox_has_started_ || !conds_) {
SANDBOX_DIE("Cannot change policy after sandbox has started");
}
- PolicySanityChecks(syscall_evaluator, aux);
- evaluators_->push_back(std::make_pair(syscall_evaluator, aux));
+ SetSandboxPolicy(new CompatibilityPolicy(syscall_evaluator, aux));
+}
+
+// Don't take a scoped_ptr here, polymorphism make their use awkward.
+void Sandbox::SetSandboxPolicy(SandboxBpfPolicy* policy) {
+ DCHECK(!policy_);
+ if (sandbox_has_started_ || !conds_) {
+ SANDBOX_DIE("Cannot change policy after sandbox has started");
+ }
+ PolicySanityChecks(policy);
+ policy_.reset(policy);
}
void Sandbox::InstallFilter() {
@@ -491,19 +507,20 @@
// 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(false /* force_verification */);
+ Program* program = AssembleFilter(false /* force_verification */);
struct sock_filter bpf[program->size()];
- const struct sock_fprog prog = {
- static_cast<unsigned short>(program->size()), bpf };
+ 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
- delete evaluators_;
+ // Make an attempt to release memory that is no longer needed here, rather
+ // than in the destructor. Try to avoid as much as possible to presume of
+ // what will be possible to do in the new (sandboxed) execution environment.
delete conds_;
- evaluators_ = NULL;
- conds_ = NULL;
+ conds_ = NULL;
+ policy_.reset();
// Install BPF filter program
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
@@ -514,41 +531,38 @@
}
}
+ sandbox_has_started_ = true;
+
return;
}
-Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) {
+Sandbox::Program* Sandbox::AssembleFilter(bool force_verification) {
#if !defined(NDEBUG)
force_verification = true;
#endif
// Verify that the user pushed a policy.
- if (evaluators_->empty()) {
- SANDBOX_DIE("Failed to configure system call filters");
- }
-
- // We can't handle stacked evaluators, yet. We'll get there eventually
- // though. Hang tight.
- if (evaluators_->size() != 1) {
- SANDBOX_DIE("Not implemented");
- }
+ DCHECK(policy_);
// Assemble the BPF filter program.
- CodeGen *gen = new CodeGen();
+ CodeGen* gen = new CodeGen();
if (!gen) {
SANDBOX_DIE("Out of memory");
}
// If the architecture doesn't match SECCOMP_ARCH, disallow the
// system call.
- Instruction *tail;
- Instruction *head =
- gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_ARCH_IDX,
- tail =
- gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_ARCH,
- NULL,
- gen->MakeInstruction(BPF_RET+BPF_K,
- Kill("Invalid audit architecture in BPF filter"))));
+ Instruction* tail;
+ Instruction* head = gen->MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ SECCOMP_ARCH_IDX,
+ tail = gen->MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ SECCOMP_ARCH,
+ NULL,
+ gen->MakeInstruction(
+ BPF_RET + BPF_K,
+ Kill("Invalid audit architecture in BPF filter"))));
bool has_unsafe_traps = false;
{
@@ -558,8 +572,8 @@
FindRanges(&ranges);
// Compile the system call ranges to an optimized BPF jumptable
- Instruction *jumptable =
- AssembleJumpTable(gen, ranges.begin(), ranges.end());
+ Instruction* jumptable =
+ AssembleJumpTable(gen, ranges.begin(), ranges.end());
// If there is at least one UnsafeTrap() in our program, the entire sandbox
// is unsafe. We need to modify the program so that all non-
@@ -569,8 +583,8 @@
gen->Traverse(jumptable, CheckForUnsafeErrorCodes, &has_unsafe_traps);
// Grab the system call number, so that we can implement jump tables.
- Instruction *load_nr =
- gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_NR_IDX);
+ Instruction* load_nr =
+ gen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX);
// If our BPF program has unsafe jumps, enable support for them. This
// test happens very early in the BPF filter program. Even before we
@@ -581,27 +595,29 @@
// is actually requested by the sandbox policy.
if (has_unsafe_traps) {
if (SandboxSyscall(-1) == -1 && errno == ENOSYS) {
- SANDBOX_DIE("Support for UnsafeTrap() has not yet been ported to this "
- "architecture");
+ SANDBOX_DIE(
+ "Support for UnsafeTrap() has not yet been ported to this "
+ "architecture");
}
- EvaluateSyscall evaluateSyscall = evaluators_->begin()->first;
- void *aux = evaluators_->begin()->second;
- if (!evaluateSyscall(this, __NR_rt_sigprocmask, aux).
- Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) ||
- !evaluateSyscall(this, __NR_rt_sigreturn, aux).
- Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
+ if (!policy_->EvaluateSyscall(this, __NR_rt_sigprocmask)
+ .Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) ||
+ !policy_->EvaluateSyscall(this, __NR_rt_sigreturn)
+ .Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
#if defined(__NR_sigprocmask)
- || !evaluateSyscall(this, __NR_sigprocmask, aux).
- Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
+ ||
+ !policy_->EvaluateSyscall(this, __NR_sigprocmask)
+ .Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
#endif
#if defined(__NR_sigreturn)
- || !evaluateSyscall(this, __NR_sigreturn, aux).
- Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
+ ||
+ !policy_->EvaluateSyscall(this, __NR_sigreturn)
+ .Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
#endif
) {
- SANDBOX_DIE("Invalid seccomp policy; if using UnsafeTrap(), you must "
- "unconditionally allow sigreturn() and sigprocmask()");
+ SANDBOX_DIE(
+ "Invalid seccomp policy; if using UnsafeTrap(), you must "
+ "unconditionally allow sigreturn() and sigprocmask()");
}
if (!Trap::EnableUnsafeTrapsInSigSysHandler()) {
@@ -617,49 +633,58 @@
// Allow system calls, if they originate from our magic return address
// (which we can query by calling SandboxSyscall(-1)).
uintptr_t syscall_entry_point =
- static_cast<uintptr_t>(SandboxSyscall(-1));
+ static_cast<uintptr_t>(SandboxSyscall(-1));
uint32_t low = static_cast<uint32_t>(syscall_entry_point);
#if __SIZEOF_POINTER__ > 4
- uint32_t hi = static_cast<uint32_t>(syscall_entry_point >> 32);
+ uint32_t hi = static_cast<uint32_t>(syscall_entry_point >> 32);
#endif
// BPF cannot do native 64bit comparisons. On 64bit architectures, we
// have to compare both 32bit halves of the instruction pointer. If they
// match what we expect, we return ERR_ALLOWED. If either or both don't
// match, we continue evalutating the rest of the sandbox policy.
- Instruction *escape_hatch =
- gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_IP_LSB_IDX,
- gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, low,
+ Instruction* escape_hatch = gen->MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ SECCOMP_IP_LSB_IDX,
+ gen->MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ low,
#if __SIZEOF_POINTER__ > 4
- gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_IP_MSB_IDX,
- gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, hi,
+ gen->MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ SECCOMP_IP_MSB_IDX,
+ gen->MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ hi,
#endif
- gen->MakeInstruction(BPF_RET+BPF_K, ErrorCode(ErrorCode::ERR_ALLOWED)),
+ gen->MakeInstruction(BPF_RET + BPF_K,
+ ErrorCode(ErrorCode::ERR_ALLOWED)),
#if __SIZEOF_POINTER__ > 4
- load_nr)),
+ load_nr)),
#endif
- load_nr));
+ load_nr));
gen->JoinInstructions(tail, escape_hatch);
} else {
gen->JoinInstructions(tail, load_nr);
}
tail = load_nr;
- // On Intel architectures, verify that system call numbers are in the
- // expected number range. The older i386 and x86-64 APIs clear bit 30
- // on all system calls. The newer x32 API always sets bit 30.
+// On Intel architectures, verify that system call numbers are in the
+// expected number range. The older i386 and x86-64 APIs clear bit 30
+// on all system calls. The newer x32 API always sets bit 30.
#if defined(__i386__) || defined(__x86_64__)
- Instruction *invalidX32 =
- gen->MakeInstruction(BPF_RET+BPF_K,
- Kill("Illegal mixing of system call ABIs").err_);
- Instruction *checkX32 =
+ Instruction* invalidX32 = gen->MakeInstruction(
+ BPF_RET + BPF_K, Kill("Illegal mixing of system call ABIs").err_);
+ Instruction* checkX32 =
#if defined(__x86_64__) && defined(__ILP32__)
- gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, 0, invalidX32);
+ gen->MakeInstruction(
+ BPF_JMP + BPF_JSET + BPF_K, 0x40000000, 0, invalidX32);
#else
- gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, invalidX32, 0);
+ gen->MakeInstruction(
+ BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, 0);
#endif
- gen->JoinInstructions(tail, checkX32);
- tail = checkX32;
+ gen->JoinInstructions(tail, checkX32);
+ tail = checkX32;
#endif
// Append jump table to our pre-amble
@@ -667,7 +692,7 @@
}
// Turn the DAG into a vector of instructions.
- Program *program = new Program();
+ Program* program = new Program();
gen->Compile(head, program);
delete gen;
@@ -689,36 +714,32 @@
// 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_));
+ scoped_ptr<const RedirectToUserSpacePolicyWrapper> redirected_policy(
+ new RedirectToUserSpacePolicyWrapper(policy_.get()));
- const char *err = NULL;
- if (!Verifier::VerifyBPF(
- this,
- program,
- has_unsafe_traps ? redirected_evaluators : *evaluators_,
- &err)) {
+ const char* err = NULL;
+ if (!Verifier::VerifyBPF(this,
+ program,
+ has_unsafe_traps ? *redirected_policy : *policy_,
+ &err)) {
CodeGen::PrintProgram(program);
SANDBOX_DIE(err);
}
}
-void Sandbox::FindRanges(Ranges *ranges) {
+void Sandbox::FindRanges(Ranges* ranges) {
// Please note that "struct seccomp_data" defines system calls as a signed
// int32_t, but BPF instructions always operate on unsigned quantities. We
// deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL,
// and then verifying that the rest of the number range (both positive and
// negative) all return the same ErrorCode.
- EvaluateSyscall evaluate_syscall = evaluators_->begin()->first;
- void *aux = evaluators_->begin()->second;
- uint32_t old_sysnum = 0;
- ErrorCode old_err = evaluate_syscall(this, old_sysnum, aux);
- ErrorCode invalid_err = evaluate_syscall(this, MIN_SYSCALL - 1,
- aux);
- for (SyscallIterator iter(false); !iter.Done(); ) {
+ uint32_t old_sysnum = 0;
+ ErrorCode old_err = policy_->EvaluateSyscall(this, old_sysnum);
+ ErrorCode invalid_err = policy_->EvaluateSyscall(this, MIN_SYSCALL - 1);
+
+ for (SyscallIterator iter(false); !iter.Done();) {
uint32_t sysnum = iter.Next();
- ErrorCode err = evaluate_syscall(this, static_cast<int>(sysnum), aux);
+ ErrorCode err = policy_->EvaluateSyscall(this, static_cast<int>(sysnum));
if (!iter.IsValid(sysnum) && !invalid_err.Equals(err)) {
// A proper sandbox policy should always treat system calls outside of
// the range MIN_SYSCALL..MAX_SYSCALL (i.e. anything that returns
@@ -729,12 +750,12 @@
if (!err.Equals(old_err) || iter.Done()) {
ranges->push_back(Range(old_sysnum, sysnum - 1, old_err));
old_sysnum = sysnum;
- old_err = err;
+ old_err = err;
}
}
}
-Instruction *Sandbox::AssembleJumpTable(CodeGen *gen,
+Instruction* Sandbox::AssembleJumpTable(CodeGen* gen,
Ranges::const_iterator start,
Ranges::const_iterator stop) {
// We convert the list of system call ranges into jump table that performs
@@ -753,166 +774,170 @@
// We compare our system call number against the lowest valid system call
// number in this range object. If our number is lower, it is outside of
// this range object. If it is greater or equal, it might be inside.
- Ranges::const_iterator mid = start + (stop - start)/2;
+ Ranges::const_iterator mid = start + (stop - start) / 2;
// Sub-divide the list of ranges and continue recursively.
- Instruction *jf = AssembleJumpTable(gen, start, mid);
- Instruction *jt = AssembleJumpTable(gen, mid, stop);
- return gen->MakeInstruction(BPF_JMP+BPF_JGE+BPF_K, mid->from, jt, jf);
+ Instruction* jf = AssembleJumpTable(gen, start, mid);
+ Instruction* jt = AssembleJumpTable(gen, mid, stop);
+ return gen->MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf);
}
-Instruction *Sandbox::RetExpression(CodeGen *gen, const ErrorCode& err) {
+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, err);
+ return gen->MakeInstruction(BPF_RET + BPF_K, err);
}
}
-Instruction *Sandbox::CondExpression(CodeGen *gen, const ErrorCode& cond) {
+Instruction* Sandbox::CondExpression(CodeGen* gen, const ErrorCode& cond) {
// We can only inspect the six system call arguments that are passed in
// CPU registers.
if (cond.argno_ < 0 || cond.argno_ >= 6) {
- SANDBOX_DIE("Internal compiler error; invalid argument number "
- "encountered");
+ SANDBOX_DIE(
+ "Internal compiler error; invalid argument number "
+ "encountered");
}
// BPF programs operate on 32bit entities. Load both halfs of the 64bit
// system call argument and then generate suitable conditional statements.
- Instruction *msb_head =
- gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
- SECCOMP_ARG_MSB_IDX(cond.argno_));
- Instruction *msb_tail = msb_head;
- Instruction *lsb_head =
- gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
- SECCOMP_ARG_LSB_IDX(cond.argno_));
- Instruction *lsb_tail = lsb_head;
+ Instruction* msb_head = gen->MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARG_MSB_IDX(cond.argno_));
+ Instruction* msb_tail = msb_head;
+ Instruction* lsb_head = gen->MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARG_LSB_IDX(cond.argno_));
+ Instruction* lsb_tail = lsb_head;
// Emit a suitable comparison statement.
switch (cond.op_) {
- case ErrorCode::OP_EQUAL:
- // Compare the least significant bits for equality
- lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
- static_cast<uint32_t>(cond.value_),
- RetExpression(gen, *cond.passed_),
- RetExpression(gen, *cond.failed_));
- gen->JoinInstructions(lsb_head, lsb_tail);
-
- // If we are looking at a 64bit argument, we need to also compare the
- // most significant bits.
- if (cond.width_ == ErrorCode::TP_64BIT) {
- msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
- static_cast<uint32_t>(cond.value_ >> 32),
- lsb_head,
+ case ErrorCode::OP_EQUAL:
+ // Compare the least significant bits for equality
+ lsb_tail = gen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K,
+ static_cast<uint32_t>(cond.value_),
+ RetExpression(gen, *cond.passed_),
RetExpression(gen, *cond.failed_));
- gen->JoinInstructions(msb_head, msb_tail);
- }
- break;
- case ErrorCode::OP_HAS_ALL_BITS:
- // Check the bits in the LSB half of the system call argument. Our
- // OP_HAS_ALL_BITS operator passes, iff all of the bits are set. This is
- // different from the kernel's BPF_JSET operation which passes, if any of
- // the bits are set.
- // Of course, if there is only a single set bit (or none at all), then
- // things get easier.
- {
- uint32_t lsb_bits = static_cast<uint32_t>(cond.value_);
- int lsb_bit_count = popcount(lsb_bits);
- if (lsb_bit_count == 0) {
- // No bits are set in the LSB half. The test will always pass.
- lsb_head = RetExpression(gen, *cond.passed_);
- lsb_tail = NULL;
- } else if (lsb_bit_count == 1) {
- // Exactly one bit is set in the LSB half. We can use the BPF_JSET
- // operator.
- lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
- lsb_bits,
- RetExpression(gen, *cond.passed_),
- RetExpression(gen, *cond.failed_));
- gen->JoinInstructions(lsb_head, lsb_tail);
- } else {
- // More than one bit is set in the LSB half. We need to combine
- // BPF_AND and BPF_JEQ to test whether all of these bits are in fact
- // set in the system call argument.
- gen->JoinInstructions(lsb_head,
- gen->MakeInstruction(BPF_ALU+BPF_AND+BPF_K,
- lsb_bits,
- lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
+ gen->JoinInstructions(lsb_head, lsb_tail);
+
+ // If we are looking at a 64bit argument, we need to also compare the
+ // most significant bits.
+ if (cond.width_ == ErrorCode::TP_64BIT) {
+ msb_tail =
+ gen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K,
+ static_cast<uint32_t>(cond.value_ >> 32),
+ lsb_head,
+ RetExpression(gen, *cond.failed_));
+ gen->JoinInstructions(msb_head, msb_tail);
+ }
+ break;
+ case ErrorCode::OP_HAS_ALL_BITS:
+ // Check the bits in the LSB half of the system call argument. Our
+ // OP_HAS_ALL_BITS operator passes, iff all of the bits are set. This is
+ // different from the kernel's BPF_JSET operation which passes, if any of
+ // the bits are set.
+ // Of course, if there is only a single set bit (or none at all), then
+ // things get easier.
+ {
+ uint32_t lsb_bits = static_cast<uint32_t>(cond.value_);
+ int lsb_bit_count = popcount(lsb_bits);
+ if (lsb_bit_count == 0) {
+ // No bits are set in the LSB half. The test will always pass.
+ lsb_head = RetExpression(gen, *cond.passed_);
+ lsb_tail = NULL;
+ } else if (lsb_bit_count == 1) {
+ // Exactly one bit is set in the LSB half. We can use the BPF_JSET
+ // operator.
+ lsb_tail = gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
lsb_bits,
RetExpression(gen, *cond.passed_),
- RetExpression(gen, *cond.failed_))));
+ RetExpression(gen, *cond.failed_));
+ gen->JoinInstructions(lsb_head, lsb_tail);
+ } else {
+ // More than one bit is set in the LSB half. We need to combine
+ // BPF_AND and BPF_JEQ to test whether all of these bits are in fact
+ // set in the system call argument.
+ gen->JoinInstructions(
+ lsb_head,
+ gen->MakeInstruction(BPF_ALU + BPF_AND + BPF_K,
+ lsb_bits,
+ lsb_tail = gen->MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ lsb_bits,
+ RetExpression(gen, *cond.passed_),
+ RetExpression(gen, *cond.failed_))));
+ }
}
- }
- // If we are looking at a 64bit argument, we need to also check the bits
- // in the MSB half of the system call argument.
- if (cond.width_ == ErrorCode::TP_64BIT) {
- uint32_t msb_bits = static_cast<uint32_t>(cond.value_ >> 32);
- int msb_bit_count = popcount(msb_bits);
- if (msb_bit_count == 0) {
- // No bits are set in the MSB half. The test will always pass.
- msb_head = lsb_head;
- } else if (msb_bit_count == 1) {
- // Exactly one bit is set in the MSB half. We can use the BPF_JSET
- // operator.
- msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
- msb_bits,
- lsb_head,
- RetExpression(gen, *cond.failed_));
- gen->JoinInstructions(msb_head, msb_tail);
- } else {
- // More than one bit is set in the MSB half. We need to combine
- // BPF_AND and BPF_JEQ to test whether all of these bits are in fact
- // set in the system call argument.
- gen->JoinInstructions(msb_head,
- gen->MakeInstruction(BPF_ALU+BPF_AND+BPF_K,
- msb_bits,
- gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
- msb_bits,
- lsb_head,
- RetExpression(gen, *cond.failed_))));
+ // If we are looking at a 64bit argument, we need to also check the bits
+ // in the MSB half of the system call argument.
+ if (cond.width_ == ErrorCode::TP_64BIT) {
+ uint32_t msb_bits = static_cast<uint32_t>(cond.value_ >> 32);
+ int msb_bit_count = popcount(msb_bits);
+ if (msb_bit_count == 0) {
+ // No bits are set in the MSB half. The test will always pass.
+ msb_head = lsb_head;
+ } else if (msb_bit_count == 1) {
+ // Exactly one bit is set in the MSB half. We can use the BPF_JSET
+ // operator.
+ msb_tail = gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
+ msb_bits,
+ lsb_head,
+ RetExpression(gen, *cond.failed_));
+ gen->JoinInstructions(msb_head, msb_tail);
+ } else {
+ // More than one bit is set in the MSB half. We need to combine
+ // BPF_AND and BPF_JEQ to test whether all of these bits are in fact
+ // set in the system call argument.
+ gen->JoinInstructions(
+ msb_head,
+ gen->MakeInstruction(
+ BPF_ALU + BPF_AND + BPF_K,
+ msb_bits,
+ gen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K,
+ msb_bits,
+ lsb_head,
+ RetExpression(gen, *cond.failed_))));
+ }
}
- }
- break;
- case ErrorCode::OP_HAS_ANY_BITS:
- // Check the bits in the LSB half of the system call argument. Our
- // OP_HAS_ANY_BITS operator passes, iff any of the bits are set. This maps
- // nicely to the kernel's BPF_JSET operation.
- {
- uint32_t lsb_bits = static_cast<uint32_t>(cond.value_);
- if (!lsb_bits) {
- // No bits are set in the LSB half. The test will always fail.
- lsb_head = RetExpression(gen, *cond.failed_);
- lsb_tail = NULL;
- } else {
- lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
- lsb_bits,
- RetExpression(gen, *cond.passed_),
- RetExpression(gen, *cond.failed_));
- gen->JoinInstructions(lsb_head, lsb_tail);
+ break;
+ case ErrorCode::OP_HAS_ANY_BITS:
+ // Check the bits in the LSB half of the system call argument. Our
+ // OP_HAS_ANY_BITS operator passes, iff any of the bits are set. This maps
+ // nicely to the kernel's BPF_JSET operation.
+ {
+ uint32_t lsb_bits = static_cast<uint32_t>(cond.value_);
+ if (!lsb_bits) {
+ // No bits are set in the LSB half. The test will always fail.
+ lsb_head = RetExpression(gen, *cond.failed_);
+ lsb_tail = NULL;
+ } else {
+ lsb_tail = gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
+ lsb_bits,
+ RetExpression(gen, *cond.passed_),
+ RetExpression(gen, *cond.failed_));
+ gen->JoinInstructions(lsb_head, lsb_tail);
+ }
}
- }
- // If we are looking at a 64bit argument, we need to also check the bits
- // in the MSB half of the system call argument.
- if (cond.width_ == ErrorCode::TP_64BIT) {
- uint32_t msb_bits = static_cast<uint32_t>(cond.value_ >> 32);
- if (!msb_bits) {
- // No bits are set in the MSB half. The test will always fail.
- msb_head = lsb_head;
- } else {
- msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
- msb_bits,
- RetExpression(gen, *cond.passed_),
- lsb_head);
- gen->JoinInstructions(msb_head, msb_tail);
+ // If we are looking at a 64bit argument, we need to also check the bits
+ // in the MSB half of the system call argument.
+ if (cond.width_ == ErrorCode::TP_64BIT) {
+ uint32_t msb_bits = static_cast<uint32_t>(cond.value_ >> 32);
+ if (!msb_bits) {
+ // No bits are set in the MSB half. The test will always fail.
+ msb_head = lsb_head;
+ } else {
+ msb_tail = gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
+ msb_bits,
+ RetExpression(gen, *cond.passed_),
+ lsb_head);
+ gen->JoinInstructions(msb_head, msb_tail);
+ }
}
- }
- break;
- default:
- // TODO(markus): Need to add support for OP_GREATER
- SANDBOX_DIE("Not implemented");
- break;
+ break;
+ default:
+ // TODO(markus): Need to add support for OP_GREATER
+ SANDBOX_DIE("Not implemented");
+ break;
}
// Ensure that we never pass a 64bit value, when we only expect a 32bit
@@ -921,26 +946,28 @@
// LSB has been sign-extended into the MSB.
if (cond.width_ == ErrorCode::TP_32BIT) {
if (cond.value_ >> 32) {
- SANDBOX_DIE("Invalid comparison of a 32bit system call argument "
- "against a 64bit constant; this test is always false.");
+ SANDBOX_DIE(
+ "Invalid comparison of a 32bit system call argument "
+ "against a 64bit constant; this test is always false.");
}
- Instruction *invalid_64bit = RetExpression(gen, Unexpected64bitArgument());
- #if __SIZEOF_POINTER__ > 4
- invalid_64bit =
- gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 0xFFFFFFFF,
- gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
- SECCOMP_ARG_LSB_IDX(cond.argno_),
- gen->MakeInstruction(BPF_JMP+BPF_JGE+BPF_K, 0x80000000,
- lsb_head,
- invalid_64bit)),
- invalid_64bit);
- #endif
+ Instruction* invalid_64bit = RetExpression(gen, Unexpected64bitArgument());
+#if __SIZEOF_POINTER__ > 4
+ invalid_64bit = gen->MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ 0xFFFFFFFF,
+ gen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS,
+ SECCOMP_ARG_LSB_IDX(cond.argno_),
+ gen->MakeInstruction(BPF_JMP + BPF_JGE + BPF_K,
+ 0x80000000,
+ lsb_head,
+ invalid_64bit)),
+ invalid_64bit);
+#endif
gen->JoinInstructions(
- msb_tail,
- gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 0,
- lsb_head,
- invalid_64bit));
+ msb_tail,
+ gen->MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, 0, lsb_head, invalid_64bit));
}
return msb_head;
@@ -950,11 +977,11 @@
return Kill("Unexpected 64bit argument detected");
}
-ErrorCode Sandbox::Trap(Trap::TrapFnc fnc, const void *aux) {
+ErrorCode Sandbox::Trap(Trap::TrapFnc fnc, const void* aux) {
return Trap::MakeTrap(fnc, aux, true /* Safe Trap */);
}
-ErrorCode Sandbox::UnsafeTrap(Trap::TrapFnc fnc, const void *aux) {
+ErrorCode Sandbox::UnsafeTrap(Trap::TrapFnc fnc, const void* aux) {
return Trap::MakeTrap(fnc, aux, false /* Unsafe Trap */);
}
@@ -968,18 +995,24 @@
static_cast<intptr_t>(args.args[5]));
}
-ErrorCode Sandbox::Cond(int argno, ErrorCode::ArgType width,
- ErrorCode::Operation op, uint64_t value,
- const ErrorCode& passed, const ErrorCode& failed) {
- return ErrorCode(argno, width, op, value,
+ErrorCode Sandbox::Cond(int argno,
+ ErrorCode::ArgType width,
+ ErrorCode::Operation op,
+ uint64_t value,
+ const ErrorCode& passed,
+ const ErrorCode& failed) {
+ return ErrorCode(argno,
+ width,
+ op,
+ value,
&*conds_->insert(passed).first,
&*conds_->insert(failed).first);
}
-ErrorCode Sandbox::Kill(const char *msg) {
- return Trap(BpfFailure, const_cast<char *>(msg));
+ErrorCode Sandbox::Kill(const char* msg) {
+ return Trap(BpfFailure, const_cast<char*>(msg));
}
Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN;
-} // namespace
+} // namespace playground2
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
index f2653b0..dcb65bf 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.h
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -16,29 +16,30 @@
#include <utility>
#include <vector>
+#include "base/memory/scoped_ptr.h"
#include "sandbox/linux/seccomp-bpf/die.h"
#include "sandbox/linux/seccomp-bpf/errorcode.h"
#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
-#include "sandbox/linux/seccomp-bpf/port.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h"
namespace playground2 {
struct arch_seccomp_data {
- int nr;
+ int nr;
uint32_t arch;
uint64_t instruction_pointer;
uint64_t args[6];
};
struct arch_sigsys {
- void *ip;
- int nr;
+ void* ip;
+ int nr;
unsigned int arch;
};
class CodeGen;
class SandboxUnittestHelper;
+class SandboxBpfPolicy;
struct Instruction;
class Sandbox {
@@ -59,7 +60,7 @@
// pointer. One common use case would be to pass the "aux" pointer as an
// argument to Trap() functions.
typedef BpfSandboxPolicy* EvaluateSyscall;
- typedef std::vector<std::pair<EvaluateSyscall, void *> >Evaluators;
+ 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.
@@ -108,7 +109,12 @@
// handler. In this case, of course, the data that is pointed to must remain
// valid for the entire time that Trap() handlers can be called; typically,
// this would be the lifetime of the program.
- void SetSandboxPolicy(EvaluateSyscall syscallEvaluator, void *aux);
+ // DEPRECATED: use the policy interface below.
+ void SetSandboxPolicyDeprecated(EvaluateSyscall syscallEvaluator, void* aux);
+
+ // Set the BPF policy as |policy|. Ownership of |policy| is transfered here
+ // to the sandbox object.
+ void SetSandboxPolicy(SandboxBpfPolicy* policy);
// We can use ErrorCode to request calling of a trap handler. This method
// performs the required wrapping of the callback function into an
@@ -116,7 +122,7 @@
// The "aux" field can carry a pointer to arbitrary data. See EvaluateSyscall
// for a description of how to pass data from SetSandboxPolicy() to a Trap()
// handler.
- ErrorCode Trap(Trap::TrapFnc fnc, const void *aux);
+ ErrorCode Trap(Trap::TrapFnc fnc, const void* aux);
// Calls a user-space trap handler and disables all sandboxing for system
// calls made from this trap handler.
@@ -128,7 +134,7 @@
// very useful to diagnose code that is incompatible with the sandbox.
// If even a single system call returns "UnsafeTrap", the security of
// entire sandbox should be considered compromised.
- ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void *aux);
+ ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void* aux);
// From within an UnsafeTrap() it is often useful to be able to execute
// the system call that triggered the trap. The ForwardSyscall() method
@@ -150,13 +156,15 @@
// If it is outside this range, the sandbox treats the system call just
// the same as any other ABI violation (i.e. it aborts with an error
// message).
- ErrorCode Cond(int argno, ErrorCode::ArgType is_32bit,
+ ErrorCode Cond(int argno,
+ ErrorCode::ArgType is_32bit,
ErrorCode::Operation op,
- uint64_t value, const ErrorCode& passed,
+ uint64_t value,
+ const ErrorCode& passed,
const ErrorCode& failed);
// Kill the program and print an error message.
- ErrorCode Kill(const char *msg);
+ ErrorCode Kill(const char* msg);
// This is the main public entry point. It finds all system calls that
// need rewriting, sets up the resources needed by the sandbox, and
@@ -179,7 +187,7 @@
// through the verifier, iff the program was built in debug mode.
// But by setting "force_verification", the caller can request that the
// verifier is run unconditionally. This is useful for unittests.
- Program *AssembleFilter(bool force_verification);
+ Program* AssembleFilter(bool force_verification);
// Returns the fatal ErrorCode that is used to indicate that somebody
// attempted to pass a 64bit value in a 32bit system call argument.
@@ -193,11 +201,8 @@
struct Range {
Range(uint32_t f, uint32_t t, const ErrorCode& e)
- : from(f),
- to(t),
- err(e) {
- }
- uint32_t from, to;
+ : from(f), to(t), err(e) {}
+ uint32_t from, to;
ErrorCode err;
};
typedef std::vector<Range> Ranges;
@@ -211,7 +216,8 @@
// policy. The caller has to make sure that "this" has not yet been
// initialized with any other policies.
bool RunFunctionInPolicy(void (*code_in_sandbox)(),
- EvaluateSyscall syscall_evaluator, void *aux);
+ EvaluateSyscall syscall_evaluator,
+ void* aux);
// Performs a couple of sanity checks to verify that the kernel supports the
// features that we need for successful sandboxing.
@@ -220,7 +226,7 @@
bool KernelSupportSeccompBPF();
// Verify that the current policy passes some basic sanity checks.
- void PolicySanityChecks(EvaluateSyscall syscall_evaluator, void *aux);
+ void PolicySanityChecks(SandboxBpfPolicy* policy);
// Assembles and installs a filter based on the policy that has previously
// been configured with SetSandboxPolicy().
@@ -235,11 +241,11 @@
// 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.
- void FindRanges(Ranges *ranges);
+ 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.
- Instruction *AssembleJumpTable(CodeGen *gen,
+ Instruction* AssembleJumpTable(CodeGen* gen,
Ranges::const_iterator start,
Ranges::const_iterator stop);
@@ -248,20 +254,21 @@
// conditional expression; if so, this function will recursively call
// CondExpression() and possibly RetExpression() to build a complex set of
// instructions.
- Instruction *RetExpression(CodeGen *gen, const ErrorCode& err);
+ 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().
- Instruction *CondExpression(CodeGen *gen, const ErrorCode& cond);
+ Instruction* CondExpression(CodeGen* gen, const ErrorCode& cond);
static SandboxStatus status_;
- bool quiet_;
- int proc_fd_;
- Evaluators *evaluators_;
- Conds *conds_;
+ bool quiet_;
+ int proc_fd_;
+ scoped_ptr<const SandboxBpfPolicy> policy_;
+ Conds* conds_;
+ bool sandbox_has_started_;
DISALLOW_COPY_AND_ASSIGN(Sandbox);
};
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h b/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h
new file mode 100644
index 0000000..99d9e19
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h
@@ -0,0 +1,35 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_H_
+
+#include "base/basictypes.h"
+
+namespace playground2 {
+
+class ErrorCode;
+class Sandbox;
+
+// This is the interface to implement to define a BPF sandbox policy.
+class SandboxBpfPolicy {
+ public:
+ SandboxBpfPolicy() {}
+ virtual ~SandboxBpfPolicy() {}
+
+ // The EvaluateSyscall method is called with the system call number. It can
+ // decide to allow the system call unconditionally by returning ERR_ALLOWED;
+ // it can deny the system call unconditionally by returning an appropriate
+ // "errno" value; or it can request inspection of system call argument(s) by
+ // returning a suitable ErrorCode.
+ virtual ErrorCode EvaluateSyscall(Sandbox* sandbox_compiler,
+ int system_call_number) const = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SandboxBpfPolicy);
+};
+
+} // namespace playground2
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_H_
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h b/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h
index afc9d87..77d9b53 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h
@@ -11,10 +11,9 @@
class Sandbox;
class ErrorCode;
-typedef ErrorCode BpfSandboxPolicy(
- Sandbox* sandbox_compiler,
- int system_call_number,
- void* aux);
+typedef ErrorCode BpfSandboxPolicy(Sandbox* sandbox_compiler,
+ int system_call_number,
+ void* aux);
typedef base::Callback<BpfSandboxPolicy> BpfSandboxPolicyCallback;
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
index c41f779..9d67db8 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -33,7 +33,7 @@
// Workaround for Android's prctl.h file.
#ifndef PR_GET_ENDIAN
-#define PR_GET_ENDIAN 19
+#define PR_GET_ENDIAN 19
#endif
#ifndef PR_CAPBSET_READ
#define PR_CAPBSET_READ 23
@@ -45,7 +45,7 @@
namespace {
-const int kExpectedReturnValue = 42;
+const int kExpectedReturnValue = 42;
const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING";
// This test should execute no matter whether we have kernel support. So,
@@ -60,8 +60,7 @@
RecordProperty("SeccompBPFSupported",
seccomp_bpf_supported ? "true." : "false.");
std::cout << "Seccomp BPF supported: "
- << (seccomp_bpf_supported ? "true." : "false.")
- << "\n";
+ << (seccomp_bpf_supported ? "true." : "false.") << "\n";
RecordProperty("PointerSize", sizeof(void*));
std::cout << "Pointer size: " << sizeof(void*) << "\n";
}
@@ -78,13 +77,13 @@
// setting up the sandbox. But it wouldn't hurt to have at least one test
// that explicitly walks through all these steps.
-intptr_t FakeGetPid(const struct arch_seccomp_data& args, void *aux) {
+intptr_t FakeGetPid(const struct arch_seccomp_data& args, void* aux) {
BPF_ASSERT(aux);
- pid_t *pid_ptr = static_cast<pid_t *>(aux);
+ pid_t* pid_ptr = static_cast<pid_t*>(aux);
return (*pid_ptr)++;
}
-ErrorCode VerboseAPITestingPolicy(Sandbox *sandbox, int sysno, void *aux) {
+ErrorCode VerboseAPITestingPolicy(Sandbox* sandbox, int sysno, void* aux) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
} else if (sysno == __NR_getpid) {
@@ -99,7 +98,7 @@
playground2::Sandbox::STATUS_AVAILABLE) {
pid_t test_var = 0;
Sandbox sandbox;
- sandbox.SetSandboxPolicy(VerboseAPITestingPolicy, &test_var);
+ sandbox.SetSandboxPolicyDeprecated(VerboseAPITestingPolicy, &test_var);
sandbox.StartSandbox();
BPF_ASSERT(test_var == 0);
@@ -116,7 +115,7 @@
// A simple blacklist test
-ErrorCode BlacklistNanosleepPolicy(Sandbox *, int sysno, void *) {
+ErrorCode BlacklistNanosleepPolicy(Sandbox*, int sysno, void*) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
@@ -140,7 +139,7 @@
// Now do a simple whitelist test
-ErrorCode WhitelistGetpidPolicy(Sandbox *, int sysno, void *) {
+ErrorCode WhitelistGetpidPolicy(Sandbox*, int sysno, void*) {
switch (sysno) {
case __NR_getpid:
case __NR_exit_group:
@@ -163,15 +162,16 @@
// A simple blacklist policy, with a SIGSYS handler
-intptr_t EnomemHandler(const struct arch_seccomp_data& args, void *aux) {
+intptr_t EnomemHandler(const struct arch_seccomp_data& args, void* aux) {
// We also check that the auxiliary data is correct
SANDBOX_ASSERT(aux);
*(static_cast<int*>(aux)) = kExpectedReturnValue;
return -ENOMEM;
}
-ErrorCode BlacklistNanosleepPolicySigsys(Sandbox *sandbox, int sysno,
- void *aux) {
+ErrorCode BlacklistNanosleepPolicySigsys(Sandbox* sandbox,
+ int sysno,
+ void* aux) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
@@ -185,8 +185,10 @@
}
}
-BPF_TEST(SandboxBpf, BasicBlacklistWithSigsys,
- BlacklistNanosleepPolicySigsys, int /* BPF_AUX */) {
+BPF_TEST(SandboxBpf,
+ BasicBlacklistWithSigsys,
+ BlacklistNanosleepPolicySigsys,
+ int /* BPF_AUX */) {
// getpid() should work properly
errno = 0;
BPF_ASSERT(syscall(__NR_getpid) > 0);
@@ -204,33 +206,33 @@
// A simple test that verifies we can return arbitrary errno values.
-ErrorCode ErrnoTestPolicy(Sandbox *, int sysno, void *) {
+ErrorCode ErrnoTestPolicy(Sandbox*, int sysno, void*) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
}
switch (sysno) {
- case __NR_dup2:
- // Pretend that dup2() worked, but don't actually do anything.
- return ErrorCode(0);
- case __NR_setuid:
+ case __NR_dup2:
+ // Pretend that dup2() worked, but don't actually do anything.
+ return ErrorCode(0);
+ case __NR_setuid:
#if defined(__NR_setuid32)
- case __NR_setuid32:
+ case __NR_setuid32:
#endif
- // Return errno = 1.
- return ErrorCode(1);
- case __NR_setgid:
+ // Return errno = 1.
+ return ErrorCode(1);
+ case __NR_setgid:
#if defined(__NR_setgid32)
- case __NR_setgid32:
+ case __NR_setgid32:
#endif
- // Return maximum errno value (typically 4095).
- return ErrorCode(ErrorCode::ERR_MAX_ERRNO);
- case __NR_uname:
- // Return errno = 42;
- return ErrorCode(42);
- default:
- return ErrorCode(ErrorCode::ERR_ALLOWED);
+ // Return maximum errno value (typically 4095).
+ return ErrorCode(ErrorCode::ERR_MAX_ERRNO);
+ case __NR_uname:
+ // Return errno = 42;
+ return ErrorCode(42);
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
@@ -238,9 +240,9 @@
// Verify that dup2() returns success, but doesn't actually run.
int fds[4];
BPF_ASSERT(pipe(fds) == 0);
- BPF_ASSERT(pipe(fds+2) == 0);
+ BPF_ASSERT(pipe(fds + 2) == 0);
BPF_ASSERT(dup2(fds[2], fds[0]) == 0);
- char buf[1] = { };
+ char buf[1] = {};
BPF_ASSERT(write(fds[1], "\x55", 1) == 1);
BPF_ASSERT(write(fds[3], "\xAA", 1) == 1);
BPF_ASSERT(read(fds[0], buf, 1) == 1);
@@ -276,14 +278,17 @@
// Testing the stacking of two sandboxes
-ErrorCode StackingPolicyPartOne(Sandbox *sandbox, int sysno, void *) {
+ErrorCode StackingPolicyPartOne(Sandbox* sandbox, int sysno, void*) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
}
switch (sysno) {
case __NR_getppid:
- return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+ return sandbox->Cond(0,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL,
+ 0,
ErrorCode(ErrorCode::ERR_ALLOWED),
ErrorCode(EPERM));
default:
@@ -291,14 +296,17 @@
}
}
-ErrorCode StackingPolicyPartTwo(Sandbox *sandbox, int sysno, void *) {
+ErrorCode StackingPolicyPartTwo(Sandbox* sandbox, int sysno, void*) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
}
switch (sysno) {
case __NR_getppid:
- return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+ return sandbox->Cond(0,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL,
+ 0,
ErrorCode(EINVAL),
ErrorCode(ErrorCode::ERR_ALLOWED));
default:
@@ -317,7 +325,7 @@
// Stack a second sandbox with its own policy. Verify that we can further
// restrict filters, but we cannot relax existing filters.
Sandbox sandbox;
- sandbox.SetSandboxPolicy(StackingPolicyPartTwo, NULL);
+ sandbox.SetSandboxPolicyDeprecated(StackingPolicyPartTwo, NULL);
sandbox.StartSandbox();
errno = 0;
@@ -343,7 +351,7 @@
return ((sysno & ~3) >> 2) % 29 + 1;
}
-ErrorCode SyntheticPolicy(Sandbox *, int sysno, void *) {
+ErrorCode SyntheticPolicy(Sandbox*, int sysno, void*) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
@@ -368,15 +376,13 @@
BPF_TEST(SandboxBpf, SyntheticPolicy, SyntheticPolicy) {
// Ensure that that kExpectedReturnValue + syscallnumber + 1 does not int
// overflow.
- BPF_ASSERT(
- std::numeric_limits<int>::max() - kExpectedReturnValue - 1 >=
- static_cast<int>(MAX_PUBLIC_SYSCALL));
+ BPF_ASSERT(std::numeric_limits<int>::max() - kExpectedReturnValue - 1 >=
+ static_cast<int>(MAX_PUBLIC_SYSCALL));
- for (int syscall_number = static_cast<int>(MIN_SYSCALL);
- syscall_number <= static_cast<int>(MAX_PUBLIC_SYSCALL);
- ++syscall_number) {
- if (syscall_number == __NR_exit_group ||
- syscall_number == __NR_write) {
+ for (int syscall_number = static_cast<int>(MIN_SYSCALL);
+ syscall_number <= static_cast<int>(MAX_PUBLIC_SYSCALL);
+ ++syscall_number) {
+ if (syscall_number == __NR_exit_group || syscall_number == __NR_write) {
// exit_group() is special
continue;
}
@@ -401,7 +407,7 @@
}
}
-ErrorCode ArmPrivatePolicy(Sandbox *, int sysno, void *) {
+ErrorCode ArmPrivatePolicy(Sandbox*, int sysno, void*) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy.
return ErrorCode(ENOSYS);
@@ -418,9 +424,9 @@
}
BPF_TEST(SandboxBpf, ArmPrivatePolicy, ArmPrivatePolicy) {
- for (int syscall_number = static_cast<int>(__ARM_NR_set_tls + 1);
- syscall_number <= static_cast<int>(MAX_PRIVATE_SYSCALL);
- ++syscall_number) {
+ for (int syscall_number = static_cast<int>(__ARM_NR_set_tls + 1);
+ syscall_number <= static_cast<int>(MAX_PRIVATE_SYSCALL);
+ ++syscall_number) {
errno = 0;
BPF_ASSERT(syscall(syscall_number) == -1);
BPF_ASSERT(errno == ArmPrivateSysnoToErrno(syscall_number));
@@ -428,9 +434,9 @@
}
#endif // defined(__arm__)
-intptr_t CountSyscalls(const struct arch_seccomp_data& args, void *aux) {
+intptr_t CountSyscalls(const struct arch_seccomp_data& args, void* aux) {
// Count all invocations of our callback function.
- ++*reinterpret_cast<int *>(aux);
+ ++*reinterpret_cast<int*>(aux);
// Verify that within the callback function all filtering is temporarily
// disabled.
@@ -441,7 +447,7 @@
return Sandbox::ForwardSyscall(args);
}
-ErrorCode GreyListedPolicy(Sandbox *sandbox, int sysno, void *aux) {
+ErrorCode GreyListedPolicy(Sandbox* sandbox, int sysno, void* aux) {
// The use of UnsafeTrap() causes us to print a warning message. This is
// generally desirable, but it results in the unittest failing, as it doesn't
// expect any messages on "stderr". So, temporarily disable messages. The
@@ -452,13 +458,14 @@
// Some system calls must always be allowed, if our policy wants to make
// use of UnsafeTrap()
- if (sysno == __NR_rt_sigprocmask ||
- sysno == __NR_rt_sigreturn
+ if (sysno == __NR_rt_sigprocmask || sysno == __NR_rt_sigreturn
#if defined(__NR_sigprocmask)
- || sysno == __NR_sigprocmask
+ ||
+ sysno == __NR_sigprocmask
#endif
#if defined(__NR_sigreturn)
- || sysno == __NR_sigreturn
+ ||
+ sysno == __NR_sigreturn
#endif
) {
return ErrorCode(ErrorCode::ERR_ALLOWED);
@@ -467,22 +474,25 @@
return ErrorCode(EPERM);
} else if (Sandbox::IsValidSyscallNumber(sysno)) {
// Allow (and count) all other system calls.
- return sandbox->UnsafeTrap(CountSyscalls, aux);
+ return sandbox->UnsafeTrap(CountSyscalls, aux);
} else {
return ErrorCode(ENOSYS);
}
}
-BPF_TEST(SandboxBpf, GreyListedPolicy,
- GreyListedPolicy, int /* BPF_AUX */) {
+BPF_TEST(SandboxBpf, GreyListedPolicy, GreyListedPolicy, int /* BPF_AUX */) {
BPF_ASSERT(syscall(__NR_getpid) == -1);
BPF_ASSERT(errno == EPERM);
BPF_ASSERT(BPF_AUX == 0);
BPF_ASSERT(syscall(__NR_geteuid) == syscall(__NR_getuid));
BPF_ASSERT(BPF_AUX == 2);
- char name[17] = { };
- BPF_ASSERT(!syscall(__NR_prctl, PR_GET_NAME, name, (void *)NULL,
- (void *)NULL, (void *)NULL));
+ char name[17] = {};
+ BPF_ASSERT(!syscall(__NR_prctl,
+ PR_GET_NAME,
+ name,
+ (void*)NULL,
+ (void*)NULL,
+ (void*)NULL));
BPF_ASSERT(BPF_AUX == 3);
BPF_ASSERT(*name);
}
@@ -500,9 +510,8 @@
SANDBOX_ASSERT(Trap::EnableUnsafeTrapsInSigSysHandler() == true);
}
-intptr_t PrctlHandler(const struct arch_seccomp_data& args, void *) {
- if (args.args[0] == PR_CAPBSET_DROP &&
- static_cast<int>(args.args[1]) == -1) {
+intptr_t PrctlHandler(const struct arch_seccomp_data& args, void*) {
+ if (args.args[0] == PR_CAPBSET_DROP && static_cast<int>(args.args[1]) == -1) {
// prctl(PR_CAPBSET_DROP, -1) is never valid. The kernel will always
// return an error. But our handler allows this call.
return 0;
@@ -511,7 +520,7 @@
}
}
-ErrorCode PrctlPolicy(Sandbox *sandbox, int sysno, void *aux) {
+ErrorCode PrctlPolicy(Sandbox* sandbox, int sysno, void* aux) {
setenv(kSandboxDebuggingEnv, "t", 0);
Die::SuppressInfoMessages(true);
@@ -529,43 +538,48 @@
BPF_TEST(SandboxBpf, ForwardSyscall, PrctlPolicy) {
// This call should never be allowed. But our policy will intercept it and
// let it pass successfully.
- BPF_ASSERT(!prctl(PR_CAPBSET_DROP, -1, (void *)NULL, (void *)NULL,
- (void *)NULL));
+ BPF_ASSERT(
+ !prctl(PR_CAPBSET_DROP, -1, (void*)NULL, (void*)NULL, (void*)NULL));
// Verify that the call will fail, if it makes it all the way to the kernel.
- BPF_ASSERT(prctl(PR_CAPBSET_DROP, -2, (void *)NULL, (void *)NULL,
- (void *)NULL) == -1);
+ BPF_ASSERT(
+ prctl(PR_CAPBSET_DROP, -2, (void*)NULL, (void*)NULL, (void*)NULL) == -1);
// And verify that other uses of prctl() work just fine.
- char name[17] = { };
- BPF_ASSERT(!syscall(__NR_prctl, PR_GET_NAME, name, (void *)NULL,
- (void *)NULL, (void *)NULL));
+ char name[17] = {};
+ BPF_ASSERT(!syscall(__NR_prctl,
+ PR_GET_NAME,
+ name,
+ (void*)NULL,
+ (void*)NULL,
+ (void*)NULL));
BPF_ASSERT(*name);
// Finally, verify that system calls other than prctl() are completely
// unaffected by our policy.
- struct utsname uts = { };
+ struct utsname uts = {};
BPF_ASSERT(!uname(&uts));
BPF_ASSERT(!strcmp(uts.sysname, "Linux"));
}
-intptr_t AllowRedirectedSyscall(const struct arch_seccomp_data& args, void *) {
+intptr_t AllowRedirectedSyscall(const struct arch_seccomp_data& args, void*) {
return Sandbox::ForwardSyscall(args);
}
-ErrorCode RedirectAllSyscallsPolicy(Sandbox *sandbox, int sysno, void *aux) {
+ErrorCode RedirectAllSyscallsPolicy(Sandbox* sandbox, int sysno, void* aux) {
setenv(kSandboxDebuggingEnv, "t", 0);
Die::SuppressInfoMessages(true);
// Some system calls must always be allowed, if our policy wants to make
// use of UnsafeTrap()
- if (sysno == __NR_rt_sigprocmask ||
- sysno == __NR_rt_sigreturn
+ if (sysno == __NR_rt_sigprocmask || sysno == __NR_rt_sigreturn
#if defined(__NR_sigprocmask)
- || sysno == __NR_sigprocmask
+ ||
+ sysno == __NR_sigprocmask
#endif
#if defined(__NR_sigreturn)
- || sysno == __NR_sigreturn
+ ||
+ sysno == __NR_sigreturn
#endif
) {
return ErrorCode(ErrorCode::ERR_ALLOWED);
@@ -578,7 +592,7 @@
int bus_handler_fd_ = -1;
-void SigBusHandler(int, siginfo_t *info, void *void_context) {
+void SigBusHandler(int, siginfo_t* info, void* void_context) {
BPF_ASSERT(write(bus_handler_fd_, "\x55", 1) == 1);
}
@@ -593,7 +607,7 @@
int fds[2];
BPF_ASSERT(pipe(fds) == 0);
bus_handler_fd_ = fds[1];
- struct sigaction sa = { };
+ struct sigaction sa = {};
sa.sa_sigaction = SigBusHandler;
sa.sa_flags = SA_SIGINFO;
BPF_ASSERT(sigaction(SIGBUS, &sa, NULL) == 0);
@@ -629,7 +643,7 @@
sigaddset(&mask0, SIGUSR2);
BPF_ASSERT(!sigprocmask(SIG_BLOCK, &mask0, NULL));
BPF_ASSERT(!sigprocmask(SIG_BLOCK, NULL, &mask2));
- BPF_ASSERT( sigismember(&mask2, SIGUSR2));
+ BPF_ASSERT(sigismember(&mask2, SIGUSR2));
}
BPF_TEST(SandboxBpf, UnsafeTrapWithErrno, RedirectAllSyscallsPolicy) {
@@ -650,8 +664,8 @@
// would make system calls, but it allows us to verify that we don't
// accidentally mess with errno, when we shouldn't.
errno = 0;
- struct arch_seccomp_data args = { };
- args.nr = __NR_close;
+ struct arch_seccomp_data args = {};
+ args.nr = __NR_close;
args.args[0] = -1;
BPF_ASSERT(Sandbox::ForwardSyscall(args) == -EBADF);
BPF_ASSERT(errno == 0);
@@ -666,9 +680,8 @@
allowed_files.push_back("/proc/allowed");
allowed_files.push_back("/proc/cpuinfo");
- broker_process_.reset(new BrokerProcess(EPERM,
- allowed_files,
- std::vector<std::string>()));
+ broker_process_.reset(
+ new BrokerProcess(EPERM, allowed_files, std::vector<std::string>()));
BPF_ASSERT(broker_process() != NULL);
BPF_ASSERT(broker_process_->Init(NULL));
@@ -676,6 +689,7 @@
}
bool initialized() { return initialized_; }
class BrokerProcess* broker_process() { return broker_process_.get(); }
+
private:
bool initialized_;
scoped_ptr<class BrokerProcess> broker_process_;
@@ -683,29 +697,29 @@
};
intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args,
- void *aux) {
+ void* aux) {
BPF_ASSERT(aux);
BrokerProcess* broker_process = static_cast<BrokerProcess*>(aux);
- switch(args.nr) {
+ switch (args.nr) {
case __NR_access:
return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
- static_cast<int>(args.args[1]));
+ static_cast<int>(args.args[1]));
case __NR_open:
return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
- static_cast<int>(args.args[1]));
+ static_cast<int>(args.args[1]));
case __NR_openat:
// We only call open() so if we arrive here, it's because glibc uses
// the openat() system call.
BPF_ASSERT(static_cast<int>(args.args[0]) == AT_FDCWD);
return broker_process->Open(reinterpret_cast<const char*>(args.args[1]),
- static_cast<int>(args.args[2]));
+ static_cast<int>(args.args[2]));
default:
BPF_ASSERT(false);
return -ENOSYS;
}
}
-ErrorCode DenyOpenPolicy(Sandbox *sandbox, int sysno, void *aux) {
+ErrorCode DenyOpenPolicy(Sandbox* sandbox, int sysno, void* aux) {
InitializedOpenBroker* iob = static_cast<InitializedOpenBroker*>(aux);
if (!Sandbox::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
@@ -717,8 +731,8 @@
case __NR_openat:
// We get a InitializedOpenBroker class, but our trap handler wants
// the BrokerProcess object.
- return ErrorCode(sandbox->Trap(BrokerOpenTrapHandler,
- iob->broker_process()));
+ return ErrorCode(
+ sandbox->Trap(BrokerOpenTrapHandler, iob->broker_process()));
default:
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
@@ -726,10 +740,12 @@
// We use a InitializedOpenBroker class, so that we can run unsandboxed
// code in its constructor, which is the only way to do so in a BPF_TEST.
-BPF_TEST(SandboxBpf, UseOpenBroker, DenyOpenPolicy,
+BPF_TEST(SandboxBpf,
+ UseOpenBroker,
+ DenyOpenPolicy,
InitializedOpenBroker /* BPF_AUX */) {
BPF_ASSERT(BPF_AUX.initialized());
- BrokerProcess* broker_process = BPF_AUX.broker_process();
+ BrokerProcess* broker_process = BPF_AUX.broker_process();
BPF_ASSERT(broker_process != NULL);
// First, use the broker "manually"
@@ -771,7 +787,7 @@
// Simple test demonstrating how to use Sandbox::Cond()
-ErrorCode SimpleCondTestPolicy(Sandbox *sandbox, int sysno, void *) {
+ErrorCode SimpleCondTestPolicy(Sandbox* sandbox, int sysno, void*) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
@@ -784,20 +800,26 @@
case __NR_open:
// Allow opening files for reading, but don't allow writing.
COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_be_all_zero_bits);
- return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ return sandbox->Cond(1,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_HAS_ANY_BITS,
O_ACCMODE /* 0x3 */,
ErrorCode(EROFS),
ErrorCode(ErrorCode::ERR_ALLOWED));
case __NR_prctl:
// Allow prctl(PR_SET_DUMPABLE) and prctl(PR_GET_DUMPABLE), but
// disallow everything else.
- return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ return sandbox->Cond(0,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL,
PR_SET_DUMPABLE,
ErrorCode(ErrorCode::ERR_ALLOWED),
- sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
- PR_GET_DUMPABLE,
- ErrorCode(ErrorCode::ERR_ALLOWED),
- ErrorCode(ENOMEM)));
+ sandbox->Cond(0,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL,
+ PR_GET_DUMPABLE,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ ErrorCode(ENOMEM)));
default:
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
@@ -812,7 +834,7 @@
int ret;
BPF_ASSERT((ret = prctl(PR_GET_DUMPABLE)) >= 0);
- BPF_ASSERT(prctl(PR_SET_DUMPABLE, 1-ret) == 0);
+ BPF_ASSERT(prctl(PR_SET_DUMPABLE, 1 - ret) == 0);
BPF_ASSERT(prctl(PR_GET_ENDIAN, &ret) == -1);
BPF_ASSERT(errno == ENOMEM);
}
@@ -832,8 +854,9 @@
// We are actually constructing a graph of ArgValue objects. This
// graph will later be used to a) compute our sandbox policy, and
// b) drive the code that verifies the output from the BPF program.
- COMPILE_ASSERT(kNumTestCases < (int)(MAX_PUBLIC_SYSCALL-MIN_SYSCALL-10),
- num_test_cases_must_be_significantly_smaller_than_num_system_calls);
+ COMPILE_ASSERT(
+ kNumTestCases < (int)(MAX_PUBLIC_SYSCALL - MIN_SYSCALL - 10),
+ num_test_cases_must_be_significantly_smaller_than_num_system_calls);
for (int sysno = MIN_SYSCALL, end = kNumTestCases; sysno < end; ++sysno) {
if (IsReservedSyscall(sysno)) {
// Skip reserved system calls. This ensures that our test frame
@@ -842,21 +865,21 @@
++end;
arg_values_.push_back(NULL);
} else {
- arg_values_.push_back(RandomArgValue(rand() % kMaxArgs, 0,
- rand() % kMaxArgs));
+ arg_values_.push_back(
+ RandomArgValue(rand() % kMaxArgs, 0, rand() % kMaxArgs));
}
}
}
~EqualityStressTest() {
- for (std::vector<ArgValue *>::iterator iter = arg_values_.begin();
+ for (std::vector<ArgValue*>::iterator iter = arg_values_.begin();
iter != arg_values_.end();
++iter) {
DeleteArgValue(*iter);
}
}
- ErrorCode Policy(Sandbox *sandbox, int sysno) {
+ ErrorCode Policy(Sandbox* sandbox, int sysno) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
@@ -888,22 +911,22 @@
// We arbitrarily start by setting all six system call arguments to
// zero. And we then recursive traverse our tree of ArgValues to
// determine the necessary combinations of parameters.
- intptr_t args[6] = { };
+ intptr_t args[6] = {};
Verify(sysno, args, *arg_values_[sysno]);
}
}
private:
struct ArgValue {
- int argno; // Argument number to inspect.
- int size; // Number of test cases (must be > 0).
+ int argno; // Argument number to inspect.
+ int size; // Number of test cases (must be > 0).
struct Tests {
uint32_t k_value; // Value to compare syscall arg against.
- int err; // If non-zero, errno value to return.
- struct ArgValue *arg_value; // Otherwise, more args needs inspecting.
- } *tests;
- int err; // If none of the tests passed, this is what
- struct ArgValue *arg_value; // we'll return (this is the "else" branch).
+ int err; // If non-zero, errno value to return.
+ struct ArgValue* arg_value; // Otherwise, more args needs inspecting.
+ }* tests;
+ int err; // If none of the tests passed, this is what
+ struct ArgValue* arg_value; // we'll return (this is the "else" branch).
};
bool IsReservedSyscall(int sysno) {
@@ -917,41 +940,38 @@
// calls that will be made by this particular test. So, this small list is
// sufficient. But if anybody copy'n'pasted this code for other uses, they
// would have to review that the list.
- return sysno == __NR_read ||
- sysno == __NR_write ||
- sysno == __NR_exit ||
- sysno == __NR_exit_group ||
- sysno == __NR_restart_syscall;
+ return sysno == __NR_read || sysno == __NR_write || sysno == __NR_exit ||
+ sysno == __NR_exit_group || sysno == __NR_restart_syscall;
}
- ArgValue *RandomArgValue(int argno, int args_mask, int remaining_args) {
+ ArgValue* RandomArgValue(int argno, int args_mask, int remaining_args) {
// Create a new ArgValue and fill it with random data. We use as bit mask
// to keep track of the system call parameters that have previously been
// set; this ensures that we won't accidentally define a contradictory
// set of equality tests.
- struct ArgValue *arg_value = new ArgValue();
- args_mask |= 1 << argno;
- arg_value->argno = argno;
+ struct ArgValue* arg_value = new ArgValue();
+ args_mask |= 1 << argno;
+ arg_value->argno = argno;
// Apply some restrictions on just how complex our tests can be.
// Otherwise, we end up with a BPF program that is too complicated for
// the kernel to load.
- int fan_out = kMaxFanOut;
+ int fan_out = kMaxFanOut;
if (remaining_args > 3) {
- fan_out = 1;
+ fan_out = 1;
} else if (remaining_args > 2) {
- fan_out = 2;
+ fan_out = 2;
}
// Create a couple of different test cases with randomized values that
// we want to use when comparing system call parameter number "argno".
- arg_value->size = rand() % fan_out + 1;
- arg_value->tests = new ArgValue::Tests[arg_value->size];
+ arg_value->size = rand() % fan_out + 1;
+ arg_value->tests = new ArgValue::Tests[arg_value->size];
- uint32_t k_value = rand();
+ uint32_t k_value = rand();
for (int n = 0; n < arg_value->size; ++n) {
// Ensure that we have unique values
- k_value += rand() % (RAND_MAX/(kMaxFanOut+1)) + 1;
+ k_value += rand() % (RAND_MAX / (kMaxFanOut + 1)) + 1;
// There are two possible types of nodes. Either this is a leaf node;
// in that case, we have completed all the equality tests that we
@@ -967,7 +987,7 @@
} else {
arg_value->tests[n].err = 0;
arg_value->tests[n].arg_value =
- RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1);
+ RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1);
}
}
// Finally, we have to define what we should return if none of the
@@ -979,7 +999,7 @@
} else {
arg_value->err = 0;
arg_value->arg_value =
- RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1);
+ RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1);
}
// We have now built a new (sub-)tree of ArgValues defining a set of
// boolean expressions for testing random system call arguments against
@@ -1000,7 +1020,7 @@
return argno;
}
- void DeleteArgValue(ArgValue *arg_value) {
+ void DeleteArgValue(ArgValue* arg_value) {
// Delete an ArgValue and all of its child nodes. This requires
// recursively descending into the tree.
if (arg_value) {
@@ -1019,7 +1039,7 @@
}
}
- ErrorCode ToErrorCode(Sandbox *sandbox, ArgValue *arg_value) {
+ ErrorCode ToErrorCode(Sandbox* sandbox, ArgValue* arg_value) {
// Compute the ErrorCode that should be returned, if none of our
// tests succeed (i.e. the system call parameter doesn't match any
// of the values in arg_value->tests[].k_value).
@@ -1038,7 +1058,7 @@
// Now, iterate over all the test cases that we want to compare against.
// This builds a chain of Sandbox::Cond() tests
// (aka "if ... elif ... elif ... elif ... fi")
- for (int n = arg_value->size; n-- > 0; ) {
+ for (int n = arg_value->size; n-- > 0;) {
ErrorCode matched;
// Again, we distinguish between leaf nodes and subtrees.
if (arg_value->tests[n].err) {
@@ -1049,19 +1069,22 @@
// For now, all of our tests are limited to 32bit.
// We have separate tests that check the behavior of 32bit vs. 64bit
// conditional expressions.
- err = sandbox->Cond(arg_value->argno, ErrorCode::TP_32BIT,
- ErrorCode::OP_EQUAL, arg_value->tests[n].k_value,
- matched, err);
+ err = sandbox->Cond(arg_value->argno,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL,
+ arg_value->tests[n].k_value,
+ matched,
+ err);
}
return err;
}
- void Verify(int sysno, intptr_t *args, const ArgValue& arg_value) {
+ void Verify(int sysno, intptr_t* args, const ArgValue& arg_value) {
uint32_t mismatched = 0;
// Iterate over all the k_values in arg_value.tests[] and verify that
// we see the expected return values from system calls, when we pass
// the k_value as a parameter in a system call.
- for (int n = arg_value.size; n-- > 0; ) {
+ for (int n = arg_value.size; n-- > 0;) {
mismatched += arg_value.tests[n].k_value;
args[arg_value.argno] = arg_value.tests[n].k_value;
if (arg_value.tests[n].err) {
@@ -1070,12 +1093,12 @@
Verify(sysno, args, *arg_value.tests[n].arg_value);
}
}
- // Find a k_value that doesn't match any of the k_values in
- // arg_value.tests[]. In most cases, the current value of "mismatched"
- // would fit this requirement. But on the off-chance that it happens
- // to collide, we double-check.
+ // Find a k_value that doesn't match any of the k_values in
+ // arg_value.tests[]. In most cases, the current value of "mismatched"
+ // would fit this requirement. But on the off-chance that it happens
+ // to collide, we double-check.
try_again:
- for (int n = arg_value.size; n-- > 0; ) {
+ for (int n = arg_value.size; n-- > 0;) {
if (mismatched == arg_value.tests[n].k_value) {
++mismatched;
goto try_again;
@@ -1095,18 +1118,19 @@
args[arg_value.argno] = 0;
}
- void VerifyErrno(int sysno, intptr_t *args, int err) {
+ void VerifyErrno(int sysno, intptr_t* args, int err) {
// We installed BPF filters that return different errno values
// based on the system call number and the parameters that we decided
// to pass in. Verify that this condition holds true.
- BPF_ASSERT(SandboxSyscall(sysno,
- args[0], args[1], args[2],
- args[3], args[4], args[5]) == -err);
+ BPF_ASSERT(
+ SandboxSyscall(
+ sysno, args[0], args[1], args[2], args[3], args[4], args[5]) ==
+ -err);
}
// Vector of ArgValue trees. These trees define all the possible boolean
// expressions that we want to turn into a BPF filter program.
- std::vector<ArgValue *> arg_values_;
+ std::vector<ArgValue*> arg_values_;
// Don't increase these values. We are pushing the limits of the maximum
// BPF program that the kernel will allow us to load. If the values are
@@ -1116,33 +1140,47 @@
static const int kMaxArgs = 6;
};
-ErrorCode EqualityStressTestPolicy(Sandbox *sandbox, int sysno, void *aux) {
- return reinterpret_cast<EqualityStressTest *>(aux)->Policy(sandbox, sysno);
+ErrorCode EqualityStressTestPolicy(Sandbox* sandbox, int sysno, void* aux) {
+ return reinterpret_cast<EqualityStressTest*>(aux)->Policy(sandbox, sysno);
}
-BPF_TEST(SandboxBpf, EqualityTests, EqualityStressTestPolicy,
+BPF_TEST(SandboxBpf,
+ EqualityTests,
+ EqualityStressTestPolicy,
EqualityStressTest /* BPF_AUX */) {
BPF_AUX.VerifyFilter();
}
-ErrorCode EqualityArgumentWidthPolicy(Sandbox *sandbox, int sysno, void *) {
+ErrorCode EqualityArgumentWidthPolicy(Sandbox* sandbox, int sysno, void*) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
} else if (sysno == __NR_uname) {
- return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
- sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
- 0x55555555, ErrorCode(1), ErrorCode(2)),
- // The BPF compiler and the BPF interpreter in the kernel are
- // (mostly) agnostic of the host platform's word size. The compiler
- // will happily generate code that tests a 64bit value, and the
- // interpreter will happily perform this test.
- // But unless there is a kernel bug, there is no way for us to pass
- // in a 64bit quantity on a 32bit platform. The upper 32bits should
- // always be zero. So, this test should always evaluate as false on
- // 32bit systems.
- sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_EQUAL,
- 0x55555555AAAAAAAAULL, ErrorCode(1), ErrorCode(2)));
+ return sandbox->Cond(
+ 0,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL,
+ 0,
+ sandbox->Cond(1,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL,
+ 0x55555555,
+ ErrorCode(1),
+ ErrorCode(2)),
+ // The BPF compiler and the BPF interpreter in the kernel are
+ // (mostly) agnostic of the host platform's word size. The compiler
+ // will happily generate code that tests a 64bit value, and the
+ // interpreter will happily perform this test.
+ // But unless there is a kernel bug, there is no way for us to pass
+ // in a 64bit quantity on a 32bit platform. The upper 32bits should
+ // always be zero. So, this test should always evaluate as false on
+ // 32bit systems.
+ sandbox->Cond(1,
+ ErrorCode::TP_64BIT,
+ ErrorCode::OP_EQUAL,
+ 0x55555555AAAAAAAAULL,
+ ErrorCode(1),
+ ErrorCode(2)));
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
@@ -1168,27 +1206,34 @@
// On 32bit machines, there is no way to pass a 64bit argument through the
// syscall interface. So, we have to skip the part of the test that requires
// 64bit arguments.
-BPF_DEATH_TEST(SandboxBpf, EqualityArgumentUnallowed64bit,
+BPF_DEATH_TEST(SandboxBpf,
+ EqualityArgumentUnallowed64bit,
DEATH_MESSAGE("Unexpected 64bit argument detected"),
EqualityArgumentWidthPolicy) {
SandboxSyscall(__NR_uname, 0, 0x5555555555555555ULL);
}
#endif
-ErrorCode EqualityWithNegativeArgumentsPolicy(Sandbox *sandbox, int sysno,
- void *) {
+ErrorCode EqualityWithNegativeArgumentsPolicy(Sandbox* sandbox,
+ int sysno,
+ void*) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
} else if (sysno == __NR_uname) {
- return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
- 0xFFFFFFFF, ErrorCode(1), ErrorCode(2));
+ return sandbox->Cond(0,
+ ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL,
+ 0xFFFFFFFF,
+ ErrorCode(1),
+ ErrorCode(2));
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
-BPF_TEST(SandboxBpf, EqualityWithNegativeArguments,
+BPF_TEST(SandboxBpf,
+ EqualityWithNegativeArguments,
EqualityWithNegativeArgumentsPolicy) {
BPF_ASSERT(SandboxSyscall(__NR_uname, 0xFFFFFFFF) == -1);
BPF_ASSERT(SandboxSyscall(__NR_uname, -1) == -1);
@@ -1196,7 +1241,8 @@
}
#if __SIZEOF_POINTER__ > 4
-BPF_DEATH_TEST(SandboxBpf, EqualityWithNegative64bitArguments,
+BPF_DEATH_TEST(SandboxBpf,
+ EqualityWithNegative64bitArguments,
DEATH_MESSAGE("Unexpected 64bit argument detected"),
EqualityWithNegativeArgumentsPolicy) {
// When expecting a 32bit system call argument, we look at the MSB of the
@@ -1205,7 +1251,6 @@
BPF_ASSERT(SandboxSyscall(__NR_uname, 0xFFFFFFFF00000000LL) == -1);
}
#endif
-
ErrorCode AllBitTestPolicy(Sandbox *sandbox, int sysno, void *) {
// Test the OP_HAS_ALL_BITS conditional test operator with a couple of
// different bitmasks. We try to find bitmasks that could conceivably
@@ -1284,13 +1329,13 @@
// Most notably, "op" and "mask" are unused by the macro. If you want
// to make changes to these values, you will have to edit the
// test policy instead.
-#define BITMASK_TEST(testcase, arg, op, mask, expected_value) \
+#define BITMASK_TEST(testcase, arg, op, mask, expected_value) \
BPF_ASSERT(SandboxSyscall(__NR_uname, (testcase), (arg)) == (expected_value))
// Our uname() system call returns ErrorCode(1) for success and
// ErrorCode(0) for failure. SandboxSyscall() turns this into an
// exit code of -1 or 0.
-#define EXPECT_FAILURE 0
+#define EXPECT_FAILURE 0
#define EXPECT_SUCCESS -1
// A couple of our tests behave differently on 32bit and 64bit systems, as
@@ -1298,9 +1343,7 @@
// argument "arg".
// We expect these tests to succeed on 64bit systems, but to tail on 32bit
// systems.
-#define EXPT64_SUCCESS \
- (sizeof(void *) > 4 ? EXPECT_SUCCESS : EXPECT_FAILURE)
-
+#define EXPT64_SUCCESS (sizeof(void*) > 4 ? EXPECT_SUCCESS : EXPECT_FAILURE)
BPF_TEST(SandboxBpf, AllBitTests, AllBitTestPolicy) {
// 32bit test: all of 0x0 (should always be true)
BITMASK_TEST( 0, 0, ALLBITS32, 0, EXPECT_SUCCESS);
@@ -1404,7 +1447,7 @@
BITMASK_TEST(10, -1L, ALLBITS64,0x100000001, EXPT64_SUCCESS);
}
-ErrorCode AnyBitTestPolicy(Sandbox *sandbox, int sysno, void *) {
+ErrorCode AnyBitTestPolicy(Sandbox* sandbox, int sysno, void*) {
// Test the OP_HAS_ANY_BITS conditional test operator with a couple of
// different bitmasks. We try to find bitmasks that could conceivably
// touch corner cases.
@@ -1581,31 +1624,34 @@
BITMASK_TEST( 10, -1L, ANYBITS64,0x100000001, EXPECT_SUCCESS);
}
-intptr_t PthreadTrapHandler(const struct arch_seccomp_data& args, void *aux) {
- if (args.args[0] != (CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD)) {
+intptr_t PthreadTrapHandler(const struct arch_seccomp_data& args, void* aux) {
+ if (args.args[0] != (CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | SIGCHLD)) {
// We expect to get called for an attempt to fork(). No need to log that
// call. But if we ever get called for anything else, we want to verbosely
// print as much information as possible.
- const char *msg = (const char *)aux;
- printf("Clone() was called with unexpected arguments\n"
- " nr: %d\n"
- " 1: 0x%llX\n"
- " 2: 0x%llX\n"
- " 3: 0x%llX\n"
- " 4: 0x%llX\n"
- " 5: 0x%llX\n"
- " 6: 0x%llX\n"
- "%s\n",
- args.nr,
- (long long)args.args[0], (long long)args.args[1],
- (long long)args.args[2], (long long)args.args[3],
- (long long)args.args[4], (long long)args.args[5],
- msg);
+ const char* msg = (const char*)aux;
+ printf(
+ "Clone() was called with unexpected arguments\n"
+ " nr: %d\n"
+ " 1: 0x%llX\n"
+ " 2: 0x%llX\n"
+ " 3: 0x%llX\n"
+ " 4: 0x%llX\n"
+ " 5: 0x%llX\n"
+ " 6: 0x%llX\n"
+ "%s\n",
+ args.nr,
+ (long long)args.args[0],
+ (long long)args.args[1],
+ (long long)args.args[2],
+ (long long)args.args[3],
+ (long long)args.args[4],
+ (long long)args.args[5],
+ msg);
}
return -EPERM;
}
-
-ErrorCode PthreadPolicyEquality(Sandbox *sandbox, int sysno, void *aux) {
+ErrorCode PthreadPolicyEquality(Sandbox* sandbox, int sysno, void* aux) {
// This policy allows creating threads with pthread_create(). But it
// doesn't allow any other uses of clone(). Most notably, it does not
// allow callers to implement fork() or vfork() by passing suitable flags
@@ -1645,7 +1691,7 @@
}
}
-ErrorCode PthreadPolicyBitMask(Sandbox *sandbox, int sysno, void *aux) {
+ErrorCode PthreadPolicyBitMask(Sandbox* sandbox, int sysno, void* aux) {
// This policy allows creating threads with pthread_create(). But it
// doesn't allow any other uses of clone(). Most notably, it does not
// allow callers to implement fork() or vfork() by passing suitable flags
@@ -1690,8 +1736,8 @@
}
}
-static void *ThreadFnc(void *arg) {
- ++*reinterpret_cast<int *>(arg);
+static void* ThreadFnc(void* arg) {
+ ++*reinterpret_cast<int*>(arg);
SandboxSyscall(__NR_futex, arg, FUTEX_WAKE, 1, 0, 0, 0);
return NULL;
}
@@ -1711,8 +1757,8 @@
BPF_ASSERT(!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
BPF_ASSERT(!pthread_create(&thread, &attr, ThreadFnc, &thread_ran));
BPF_ASSERT(!pthread_attr_destroy(&attr));
- while (SandboxSyscall(__NR_futex, &thread_ran, FUTEX_WAIT,
- 0, 0, 0, 0) == -EINTR) {
+ while (SandboxSyscall(__NR_futex, &thread_ran, FUTEX_WAIT, 0, 0, 0, 0) ==
+ -EINTR) {
}
BPF_ASSERT(thread_ran);
@@ -1723,16 +1769,14 @@
// __NR_clone, and that would introduce a bogus test failure.
int pid;
BPF_ASSERT(SandboxSyscall(__NR_clone,
- CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,
- 0, 0, &pid) == -EPERM);
+ CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | SIGCHLD,
+ 0,
+ 0,
+ &pid) == -EPERM);
}
-BPF_TEST(SandboxBpf, PthreadEquality, PthreadPolicyEquality) {
- PthreadTest();
-}
+BPF_TEST(SandboxBpf, PthreadEquality, PthreadPolicyEquality) { PthreadTest(); }
-BPF_TEST(SandboxBpf, PthreadBitMask, PthreadPolicyBitMask) {
- PthreadTest();
-}
+BPF_TEST(SandboxBpf, PthreadBitMask, PthreadPolicyBitMask) { PthreadTest(); }
} // namespace
diff --git a/sandbox/linux/seccomp-bpf/syscall.cc b/sandbox/linux/seccomp-bpf/syscall.cc
index 8b09a68..a5cbb02 100644
--- a/sandbox/linux/seccomp-bpf/syscall.cc
+++ b/sandbox/linux/seccomp-bpf/syscall.cc
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
#include <asm/unistd.h>
#include <errno.h>
-#include "sandbox/linux/seccomp-bpf/port.h"
-#include "sandbox/linux/seccomp-bpf/syscall.h"
-
+#include "base/basictypes.h"
namespace playground2 {
diff --git a/sandbox/linux/seccomp-bpf/syscall.h b/sandbox/linux/seccomp-bpf/syscall.h
index 39b1bca..f63516b 100644
--- a/sandbox/linux/seccomp-bpf/syscall.h
+++ b/sandbox/linux/seccomp-bpf/syscall.h
@@ -16,9 +16,12 @@
// Passing "nr" as "-1" computes the "magic" return address. Passing any
// other value invokes the appropriate system call.
intptr_t SandboxSyscall(int nr,
- intptr_t p0, intptr_t p1, intptr_t p2,
- intptr_t p3, intptr_t p4, intptr_t p5);
-
+ intptr_t p0,
+ intptr_t p1,
+ intptr_t p2,
+ intptr_t p3,
+ intptr_t p4,
+ intptr_t p5);
// System calls can take up to six parameters. Traditionally, glibc
// implements this property by using variadic argument lists. This works, but
@@ -37,19 +40,30 @@
// easier to read as it hides implementation details.
#if __cplusplus >= 201103 // C++11
-template<class T0 = intptr_t, class T1 = intptr_t, class T2 = intptr_t,
- class T3 = intptr_t, class T4 = intptr_t, class T5 = intptr_t>
+template <class T0 = intptr_t,
+ class T1 = intptr_t,
+ class T2 = intptr_t,
+ class T3 = intptr_t,
+ class T4 = intptr_t,
+ class T5 = intptr_t>
inline intptr_t SandboxSyscall(int nr,
- T0 p0 = 0, T1 p1 = 0, T2 p2 = 0,
- T3 p3 = 0, T4 p4 = 0, T5 p5 = 0)
- __attribute__((always_inline));
+ T0 p0 = 0,
+ T1 p1 = 0,
+ T2 p2 = 0,
+ T3 p3 = 0,
+ T4 p4 = 0,
+ T5 p5 = 0) __attribute__((always_inline));
-template<class T0, class T1, class T2, class T3, class T4, class T5>
-inline intptr_t SandboxSyscall(int nr,
- T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) {
+template <class T0, class T1, class T2, class T3, class T4, class T5>
+inline intptr_t
+SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) {
return SandboxSyscall(nr,
- (intptr_t)p0, (intptr_t)p1, (intptr_t)p2,
- (intptr_t)p3, (intptr_t)p4, (intptr_t)p5);
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5);
}
#else // Pre-C++11
@@ -58,60 +72,61 @@
// expressing what we are doing here. Delete the fall-back code for older
// compilers as soon as we have fully switched to C++11
-template<class T0, class T1, class T2, class T3, class T4, class T5>
-inline intptr_t SandboxSyscall(int nr,
- T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
- __attribute__((always_inline));
-template<class T0, class T1, class T2, class T3, class T4, class T5>
-inline intptr_t SandboxSyscall(int nr,
- T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) {
+template <class T0, class T1, class T2, class T3, class T4, class T5>
+inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
+ __attribute__((always_inline));
+template <class T0, class T1, class T2, class T3, class T4, class T5>
+inline intptr_t
+SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) {
return SandboxSyscall(nr,
- (intptr_t)p0, (intptr_t)p1, (intptr_t)p2,
- (intptr_t)p3, (intptr_t)p4, (intptr_t)p5);
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5);
}
-template<class T0, class T1, class T2, class T3, class T4>
+template <class T0, class T1, class T2, class T3, class T4>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4)
- __attribute__((always_inline));
-template<class T0, class T1, class T2, class T3, class T4>
+ __attribute__((always_inline));
+template <class T0, class T1, class T2, class T3, class T4>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4) {
return SandboxSyscall(nr, p0, p1, p2, p3, p4, 0);
}
-template<class T0, class T1, class T2, class T3>
+template <class T0, class T1, class T2, class T3>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3)
- __attribute__((always_inline));
-template<class T0, class T1, class T2, class T3>
+ __attribute__((always_inline));
+template <class T0, class T1, class T2, class T3>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3) {
return SandboxSyscall(nr, p0, p1, p2, p3, 0, 0);
}
-template<class T0, class T1, class T2>
+template <class T0, class T1, class T2>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2)
- __attribute__((always_inline));
-template<class T0, class T1, class T2>
+ __attribute__((always_inline));
+template <class T0, class T1, class T2>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2) {
return SandboxSyscall(nr, p0, p1, p2, 0, 0, 0);
}
-template<class T0, class T1>
+template <class T0, class T1>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1)
- __attribute__((always_inline));
-template<class T0, class T1>
+ __attribute__((always_inline));
+template <class T0, class T1>
inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1) {
return SandboxSyscall(nr, p0, p1, 0, 0, 0, 0);
}
-template<class T0>
-inline intptr_t SandboxSyscall(int nr, T0 p0)
- __attribute__((always_inline));
-template<class T0>
+template <class T0>
+inline intptr_t SandboxSyscall(int nr, T0 p0) __attribute__((always_inline));
+template <class T0>
inline intptr_t SandboxSyscall(int nr, T0 p0) {
return SandboxSyscall(nr, p0, 0, 0, 0, 0, 0);
}
-inline intptr_t SandboxSyscall(int nr)
- __attribute__((always_inline));
+inline intptr_t SandboxSyscall(int nr) __attribute__((always_inline));
inline intptr_t SandboxSyscall(int nr) {
return SandboxSyscall(nr, 0, 0, 0, 0, 0, 0);
}
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator.cc b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
index 4ea979a..f1f2acf 100644
--- a/sandbox/linux/seccomp-bpf/syscall_iterator.cc
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
@@ -2,10 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
-#include "sandbox/linux/seccomp-bpf/port.h"
#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
+#include "base/basictypes.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+
namespace playground2 {
uint32_t SyscallIterator::Next() {
@@ -17,8 +18,7 @@
do {
// |num_| has been initialized to 0, which we assume is also MIN_SYSCALL.
// This true for supported architectures (Intel and ARM EABI).
- COMPILE_ASSERT(MIN_SYSCALL == 0u,
- min_syscall_should_always_be_zero);
+ COMPILE_ASSERT(MIN_SYSCALL == 0u, min_syscall_should_always_be_zero);
val = num_;
// First we iterate up to MAX_PUBLIC_SYSCALL, which is equal to MAX_SYSCALL
@@ -30,9 +30,9 @@
++num_;
}
#if defined(__arm__)
- // ARM EABI includes "ARM private" system calls starting at
- // MIN_PRIVATE_SYSCALL, and a "ghost syscall private to the kernel" at
- // MIN_GHOST_SYSCALL.
+ // ARM EABI includes "ARM private" system calls starting at
+ // MIN_PRIVATE_SYSCALL, and a "ghost syscall private to the kernel" at
+ // MIN_GHOST_SYSCALL.
} else if (num_ < MIN_PRIVATE_SYSCALL - 1) {
num_ = MIN_PRIVATE_SYSCALL - 1;
} else if (num_ <= MAX_PRIVATE_SYSCALL) {
@@ -50,12 +50,12 @@
++num_;
}
#endif
- // BPF programs only ever operate on unsigned quantities. So, that's how
- // we iterate; we return values from 0..0xFFFFFFFFu. But there are places,
- // where the kernel might interpret system call numbers as signed
- // quantities, so the boundaries between signed and unsigned values are
- // potential problem cases. We want to explicitly return these values from
- // our iterator.
+ // BPF programs only ever operate on unsigned quantities. So, that's how
+ // we iterate; we return values from 0..0xFFFFFFFFu. But there are places,
+ // where the kernel might interpret system call numbers as signed
+ // quantities, so the boundaries between signed and unsigned values are
+ // potential problem cases. We want to explicitly return these values from
+ // our iterator.
} else if (num_ < 0x7FFFFFFFu) {
num_ = 0x7FFFFFFFu;
} else if (num_ < 0x80000000u) {
@@ -86,10 +86,7 @@
(num >= MIN_GHOST_SYSCALL && num <= MAX_SYSCALL);
}
#else
-bool SyscallIterator::IsArmPrivate(uint32_t) {
- return false;
-}
+bool SyscallIterator::IsArmPrivate(uint32_t) { return false; }
#endif
} // namespace
-
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator.h b/sandbox/linux/seccomp-bpf/syscall_iterator.h
index e17593d..3d7f66d 100644
--- a/sandbox/linux/seccomp-bpf/syscall_iterator.h
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator.h
@@ -7,6 +7,8 @@
#include <stdint.h>
+#include "base/basictypes.h"
+
namespace playground2 {
// Iterates over the entire system call range from 0..0xFFFFFFFFu. This
@@ -32,9 +34,7 @@
class SyscallIterator {
public:
explicit SyscallIterator(bool invalid_only)
- : invalid_only_(invalid_only),
- done_(false),
- num_(0) {}
+ : invalid_only_(invalid_only), done_(false), num_(0) {}
bool Done() const { return done_; }
uint32_t Next();
@@ -43,8 +43,8 @@
private:
static bool IsArmPrivate(uint32_t num);
- bool invalid_only_;
- bool done_;
+ bool invalid_only_;
+ bool done_;
uint32_t num_;
DISALLOW_IMPLICIT_CONSTRUCTORS(SyscallIterator);
@@ -53,4 +53,3 @@
} // namespace playground2
#endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__
-
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc b/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
index 26f11ce..61e95d7 100644
--- a/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
@@ -12,7 +12,7 @@
SANDBOX_TEST(SyscallIterator, Monotonous) {
for (int i = 0; i < 2; ++i) {
- bool invalid_only = !i; // Testing both |invalid_only| cases.
+ bool invalid_only = !i; // Testing both |invalid_only| cases.
SyscallIterator iter(invalid_only);
uint32_t next = iter.Next();
@@ -79,7 +79,7 @@
SANDBOX_TEST(SyscallIterator, Invalid) {
for (int i = 0; i < 2; ++i) {
- bool invalid_only = !i; // Testing both |invalid_only| cases.
+ bool invalid_only = !i; // Testing both |invalid_only| cases.
SyscallIterator iter(invalid_only);
uint32_t next = iter.Next();
@@ -132,4 +132,3 @@
}
} // namespace
-
diff --git a/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
index 136deb6..0472448 100644
--- a/sandbox/linux/seccomp-bpf/syscall_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -31,27 +31,27 @@
#endif
TEST(Syscall, WellKnownEntryPoint) {
- // Test that SandboxSyscall(-1) is handled specially. Don't do this on ARM,
- // where syscall(-1) crashes with SIGILL. Not running the test is fine, as we
- // are still testing ARM code in the next set of tests.
+// Test that SandboxSyscall(-1) is handled specially. Don't do this on ARM,
+// where syscall(-1) crashes with SIGILL. Not running the test is fine, as we
+// are still testing ARM code in the next set of tests.
#if !defined(__arm__)
EXPECT_NE(SandboxSyscall(-1), syscall(-1));
#endif
- // If possible, test that SandboxSyscall(-1) returns the address right after
- // a kernel entry point.
+// If possible, test that SandboxSyscall(-1) returns the address right after
+// a kernel entry point.
#if defined(__i386__)
- EXPECT_EQ(0x80CDu, ((uint16_t *)SandboxSyscall(-1))[-1]); // INT 0x80
+ EXPECT_EQ(0x80CDu, ((uint16_t*)SandboxSyscall(-1))[-1]); // INT 0x80
#elif defined(__x86_64__)
- EXPECT_EQ(0x050Fu, ((uint16_t *)SandboxSyscall(-1))[-1]); // SYSCALL
+ EXPECT_EQ(0x050Fu, ((uint16_t*)SandboxSyscall(-1))[-1]); // SYSCALL
#elif defined(__arm__)
#if defined(__thumb__)
- EXPECT_EQ(0xDF00u, ((uint16_t *)SandboxSyscall(-1))[-1]); // SWI 0
+ EXPECT_EQ(0xDF00u, ((uint16_t*)SandboxSyscall(-1))[-1]); // SWI 0
#else
- EXPECT_EQ(0xEF000000u, ((uint32_t *)SandboxSyscall(-1))[-1]); // SVC 0
+ EXPECT_EQ(0xEF000000u, ((uint32_t*)SandboxSyscall(-1))[-1]); // SVC 0
#endif
#else
- #warning Incomplete test case; need port for target platform
+#warning Incomplete test case; need port for target platform
#endif
}
@@ -69,7 +69,7 @@
}
// SIGSYS trap handler that will be called on __NR_uname.
-intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void *aux) {
+intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) {
// |aux| is a pointer to our BPF_AUX.
std::vector<uint64_t>* const seen_syscall_args =
static_cast<std::vector<uint64_t>*>(aux);
@@ -78,7 +78,7 @@
return -ENOMEM;
}
-ErrorCode CopyAllArgsOnUnamePolicy(Sandbox *sandbox, int sysno, void *aux) {
+ErrorCode CopyAllArgsOnUnamePolicy(Sandbox* sandbox, int sysno, void* aux) {
if (!Sandbox::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
}
@@ -91,7 +91,9 @@
// We are testing SandboxSyscall() by making use of a BPF filter that allows us
// to inspect the system call arguments that the kernel saw.
-BPF_TEST(Syscall, SyntheticSixArgs, CopyAllArgsOnUnamePolicy,
+BPF_TEST(Syscall,
+ SyntheticSixArgs,
+ CopyAllArgsOnUnamePolicy,
std::vector<uint64_t> /* BPF_AUX */) {
const int kExpectedValue = 42;
// In this test we only pass integers to the kernel. We might want to make
@@ -105,12 +107,13 @@
// We could use pretty much any system call we don't need here. uname() is
// nice because it doesn't have any dangerous side effects.
- BPF_ASSERT(SandboxSyscall(__NR_uname, syscall_args[0],
- syscall_args[1],
- syscall_args[2],
- syscall_args[3],
- syscall_args[4],
- syscall_args[5]) == -ENOMEM);
+ BPF_ASSERT(SandboxSyscall(__NR_uname,
+ syscall_args[0],
+ syscall_args[1],
+ syscall_args[2],
+ syscall_args[3],
+ syscall_args[4],
+ syscall_args[5]) == -ENOMEM);
// We expect the trap handler to have copied the 6 arguments.
BPF_ASSERT(BPF_AUX.size() == 6);
@@ -131,20 +134,29 @@
ASSERT_LE(0, fd = SandboxSyscall(__NR_open, "/dev/null", O_RDWR, 0L));
// Use mmap() to allocate some read-only memory
- char *addr0;
- ASSERT_NE((char *)NULL,
- addr0 = reinterpret_cast<char *>(
- SandboxSyscall(kMMapNr, (void *)NULL, 4096, PROT_READ,
- MAP_PRIVATE|MAP_ANONYMOUS, fd, 0L)));
+ char* addr0;
+ ASSERT_NE((char*)NULL,
+ addr0 = reinterpret_cast<char*>(
+ SandboxSyscall(kMMapNr,
+ (void*)NULL,
+ 4096,
+ PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ fd,
+ 0L)));
// Try to replace the existing mapping with a read-write mapping
- char *addr1;
+ char* addr1;
ASSERT_EQ(addr0,
- addr1 = reinterpret_cast<char *>(
- SandboxSyscall(kMMapNr, addr0, 4096L, PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED,
- fd, 0L)));
- ++*addr1; // This should not seg fault
+ addr1 = reinterpret_cast<char*>(
+ SandboxSyscall(kMMapNr,
+ addr0,
+ 4096L,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+ fd,
+ 0L)));
+ ++*addr1; // This should not seg fault
// Clean up
EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr1, 4096L));
@@ -153,21 +165,23 @@
// Check that the offset argument (i.e. the sixth argument) is processed
// correctly.
ASSERT_GE(fd = SandboxSyscall(__NR_open, "/proc/self/exe", O_RDONLY, 0L), 0);
- char *addr2, *addr3;
- ASSERT_NE((char *)NULL,
- addr2 = reinterpret_cast<char *>(
- SandboxSyscall(kMMapNr, (void *)NULL, 8192L, PROT_READ,
- MAP_PRIVATE, fd, 0L)));
- ASSERT_NE((char *)NULL,
- addr3 = reinterpret_cast<char *>(
- SandboxSyscall(kMMapNr, (void *)NULL, 4096L, PROT_READ,
- MAP_PRIVATE, fd,
+ char* addr2, *addr3;
+ ASSERT_NE((char*)NULL,
+ addr2 = reinterpret_cast<char*>(SandboxSyscall(
+ kMMapNr, (void*)NULL, 8192L, PROT_READ, MAP_PRIVATE, fd, 0L)));
+ ASSERT_NE((char*)NULL,
+ addr3 = reinterpret_cast<char*>(SandboxSyscall(kMMapNr,
+ (void*)NULL,
+ 4096L,
+ PROT_READ,
+ MAP_PRIVATE,
+ fd,
#if defined(__NR_mmap2)
- 1L
+ 1L
#else
- 4096L
+ 4096L
#endif
- )));
+ )));
EXPECT_EQ(0, memcmp(addr2 + 4096, addr3, 4096));
// Just to be absolutely on the safe side, also verify that the file
@@ -182,4 +196,4 @@
EXPECT_EQ(0, HANDLE_EINTR(SandboxSyscall(__NR_close, fd)));
}
-} // namespace
+} // namespace
diff --git a/sandbox/linux/seccomp-bpf/trap.cc b/sandbox/linux/seccomp-bpf/trap.cc
index 499c81b..de701a7 100644
--- a/sandbox/linux/seccomp-bpf/trap.cc
+++ b/sandbox/linux/seccomp-bpf/trap.cc
@@ -2,30 +2,27 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "sandbox/linux/seccomp-bpf/trap.h"
+
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
-#ifndef SECCOMP_BPF_STANDALONE
+#include <limits>
+
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
-#endif
-
#include "sandbox/linux/seccomp-bpf/codegen.h"
#include "sandbox/linux/seccomp-bpf/die.h"
#include "sandbox/linux/seccomp-bpf/syscall.h"
-#include "sandbox/linux/seccomp-bpf/trap.h"
// Android's signal.h doesn't define ucontext etc.
#if defined(OS_ANDROID)
#include "sandbox/linux/services/android_ucontext.h"
#endif
-#include <limits>
-
-
namespace {
const int kCapacityIncrement = 20;
@@ -47,23 +44,21 @@
// realtime signals. There are plenty of them. Unfortunately, there is no
// way to mark a signal as allocated. So, the potential for collision is
// possibly even worse.
-bool GetIsInSigHandler(const ucontext_t *ctx) {
+bool GetIsInSigHandler(const ucontext_t* ctx) {
// Note: on Android, sigismember does not take a pointer to const.
return sigismember(const_cast<sigset_t*>(&ctx->uc_sigmask), SIGBUS);
}
void SetIsInSigHandler() {
sigset_t mask;
- if (sigemptyset(&mask) ||
- sigaddset(&mask, SIGBUS) ||
+ if (sigemptyset(&mask) || sigaddset(&mask, SIGBUS) ||
sigprocmask(SIG_BLOCK, &mask, NULL)) {
SANDBOX_DIE("Failed to block SIGBUS");
}
}
bool IsDefaultSignalAction(const struct sigaction& sa) {
- if (sa.sa_flags & SA_SIGINFO ||
- sa.sa_handler != SIG_DFL) {
+ if (sa.sa_flags & SA_SIGINFO || sa.sa_handler != SIG_DFL) {
return false;
}
return true;
@@ -79,7 +74,7 @@
trap_array_capacity_(0),
has_unsafe_traps_(false) {
// Set new SIGSYS handler
- struct sigaction sa = { };
+ struct sigaction sa = {};
sa.sa_sigaction = SigSysAction;
sa.sa_flags = SA_SIGINFO | SA_NODEFER;
struct sigaction old_sa;
@@ -94,14 +89,13 @@
// Unmask SIGSYS
sigset_t mask;
- if (sigemptyset(&mask) ||
- sigaddset(&mask, SIGSYS) ||
+ if (sigemptyset(&mask) || sigaddset(&mask, SIGSYS) ||
sigprocmask(SIG_UNBLOCK, &mask, NULL)) {
SANDBOX_DIE("Failed to configure SIGSYS handler");
}
}
-Trap *Trap::GetInstance() {
+Trap* Trap::GetInstance() {
// Note: This class is not thread safe. It is the caller's responsibility
// to avoid race conditions. Normally, this is a non-issue as the sandbox
// can only be initialized if there are no other threads present.
@@ -116,15 +110,16 @@
return global_trap_;
}
-void Trap::SigSysAction(int nr, siginfo_t *info, void *void_context) {
+void Trap::SigSysAction(int nr, siginfo_t* info, void* void_context) {
if (!global_trap_) {
- RAW_SANDBOX_DIE("This can't happen. Found no global singleton instance "
- "for Trap() handling.");
+ RAW_SANDBOX_DIE(
+ "This can't happen. Found no global singleton instance "
+ "for Trap() handling.");
}
global_trap_->SigSys(nr, info, void_context);
}
-void Trap::SigSys(int nr, siginfo_t *info, void *void_context) {
+void Trap::SigSys(int nr, siginfo_t* info, void* void_context) {
// Signal handlers should always preserve "errno". Otherwise, we could
// trigger really subtle bugs.
const int old_errno = errno;
@@ -145,7 +140,7 @@
// Obtain the signal context. This, most notably, gives us access to
// all CPU registers at the time of the signal.
- ucontext_t *ctx = reinterpret_cast<ucontext_t *>(void_context);
+ ucontext_t* ctx = reinterpret_cast<ucontext_t*>(void_context);
// Obtain the siginfo information that is specific to SIGSYS. Unfortunately,
// most versions of glibc don't include this information in siginfo_t. So,
@@ -154,7 +149,7 @@
memcpy(&sigsys, &info->_sifields, sizeof(sigsys));
// Some more sanity checks.
- if (sigsys.ip != reinterpret_cast<void *>(SECCOMP_IP(ctx)) ||
+ if (sigsys.ip != reinterpret_cast<void*>(SECCOMP_IP(ctx)) ||
sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx)) ||
sigsys.arch != SECCOMP_ARCH) {
// TODO(markus):
@@ -172,9 +167,12 @@
RAW_SANDBOX_DIE("Cannot call clone() from an UnsafeTrap() handler.");
}
rc = SandboxSyscall(sigsys.nr,
- SECCOMP_PARM1(ctx), SECCOMP_PARM2(ctx),
- SECCOMP_PARM3(ctx), SECCOMP_PARM4(ctx),
- SECCOMP_PARM5(ctx), SECCOMP_PARM6(ctx));
+ SECCOMP_PARM1(ctx),
+ SECCOMP_PARM2(ctx),
+ SECCOMP_PARM3(ctx),
+ SECCOMP_PARM4(ctx),
+ SECCOMP_PARM5(ctx),
+ SECCOMP_PARM6(ctx));
} else {
const ErrorCode& err = trap_array_[info->si_errno - 1];
if (!err.safe_) {
@@ -185,18 +183,13 @@
// is what we are showing to TrapFnc callbacks that the system call
// evaluator registered with the sandbox.
struct arch_seccomp_data data = {
- sigsys.nr,
- SECCOMP_ARCH,
- reinterpret_cast<uint64_t>(sigsys.ip),
- {
- static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
- static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
- static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
- static_cast<uint64_t>(SECCOMP_PARM4(ctx)),
- static_cast<uint64_t>(SECCOMP_PARM5(ctx)),
- static_cast<uint64_t>(SECCOMP_PARM6(ctx))
- }
- };
+ sigsys.nr, SECCOMP_ARCH, reinterpret_cast<uint64_t>(sigsys.ip),
+ {static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM4(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM5(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM6(ctx))}};
// Now call the TrapFnc callback associated with this particular instance
// of SECCOMP_RET_TRAP.
@@ -207,7 +200,7 @@
// that we just handled, and restore "errno" to the value that it had
// before entering the signal handler.
SECCOMP_RESULT(ctx) = static_cast<greg_t>(rc);
- errno = old_errno;
+ errno = old_errno;
return;
}
@@ -222,11 +215,11 @@
}
}
-ErrorCode Trap::MakeTrap(TrapFnc fnc, const void *aux, bool safe) {
+ErrorCode Trap::MakeTrap(TrapFnc fnc, const void* aux, bool safe) {
return GetInstance()->MakeTrapImpl(fnc, aux, safe);
}
-ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void *aux, bool safe) {
+ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void* aux, bool safe) {
if (!safe && !SandboxDebuggingAllowedByUser()) {
// Unless the user set the CHROME_SANDBOX_DEBUGGING environment variable,
// we never return an ErrorCode that is marked as "unsafe". This also
@@ -239,8 +232,9 @@
// to understand. Removing the SANDBOX_DIE() allows callers to easyly check
// whether unsafe traps are supported (by checking whether the returned
// ErrorCode is ET_INVALID).
- SANDBOX_DIE("Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING "
- "is enabled");
+ SANDBOX_DIE(
+ "Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING "
+ "is enabled");
return ErrorCode();
}
@@ -290,9 +284,9 @@
// against issues with the memory model or with completely asynchronous
// events.
if (trap_array_size_ >= trap_array_capacity_) {
- trap_array_capacity_ += kCapacityIncrement;
- ErrorCode *old_trap_array = trap_array_;
- ErrorCode *new_trap_array = new ErrorCode[trap_array_capacity_];
+ trap_array_capacity_ += kCapacityIncrement;
+ ErrorCode* old_trap_array = trap_array_;
+ ErrorCode* new_trap_array = new ErrorCode[trap_array_capacity_];
// Language specs are unclear on whether the compiler is allowed to move
// the "delete[]" above our preceding assignments and/or memory moves,
@@ -305,7 +299,7 @@
// legitimate worry; but they at least thought that the barrier is
// sufficient to prevent the (so far hypothetical) problem of re-ordering
// of instructions by the compiler.
- memcpy(new_trap_array, trap_array_, trap_array_size_*sizeof(ErrorCode));
+ memcpy(new_trap_array, trap_array_, trap_array_size_ * sizeof(ErrorCode));
asm volatile("" : "=r"(new_trap_array) : "0"(new_trap_array) : "memory");
trap_array_ = new_trap_array;
asm volatile("" : "=r"(trap_array_) : "0"(trap_array_) : "memory");
@@ -321,13 +315,12 @@
}
bool Trap::SandboxDebuggingAllowedByUser() const {
- const char *debug_flag = getenv(kSandboxDebuggingEnv);
+ const char* debug_flag = getenv(kSandboxDebuggingEnv);
return debug_flag && *debug_flag;
}
-
bool Trap::EnableUnsafeTrapsInSigSysHandler() {
- Trap *trap = GetInstance();
+ Trap* trap = GetInstance();
if (!trap->has_unsafe_traps_) {
// Unsafe traps are a one-way fuse. Once enabled, they can never be turned
// off again.
@@ -340,8 +333,9 @@
SANDBOX_INFO("WARNING! Disabling sandbox for debugging purposes");
trap->has_unsafe_traps_ = true;
} else {
- SANDBOX_INFO("Cannot disable sandbox and use unsafe traps unless "
- "CHROME_SANDBOX_DEBUGGING is turned on first");
+ SANDBOX_INFO(
+ "Cannot disable sandbox and use unsafe traps unless "
+ "CHROME_SANDBOX_DEBUGGING is turned on first");
}
}
// Returns the, possibly updated, value of has_unsafe_traps_.
@@ -356,6 +350,6 @@
}
}
-Trap *Trap::global_trap_;
+Trap* Trap::global_trap_;
} // namespace playground2
diff --git a/sandbox/linux/seccomp-bpf/trap.h b/sandbox/linux/seccomp-bpf/trap.h
index 2a4c6ed..edaa023 100644
--- a/sandbox/linux/seccomp-bpf/trap.h
+++ b/sandbox/linux/seccomp-bpf/trap.h
@@ -11,8 +11,7 @@
#include <map>
#include <vector>
-#include "sandbox/linux/seccomp-bpf/port.h"
-
+#include "base/basictypes.h"
namespace playground2 {
@@ -41,13 +40,13 @@
// range -1..-4096. It should not set errno when reporting errors; on the
// other hand, accidentally modifying errno is harmless and the changes will
// be undone afterwards.
- typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void *aux);
+ typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void* aux);
// Registers a new trap handler and sets up the appropriate SIGSYS handler
// as needed.
// N.B.: This makes a permanent state change. Traps cannot be unregistered,
// as that would break existing BPF filters that are still active.
- static ErrorCode MakeTrap(TrapFnc fnc, const void *aux, bool safe);
+ static ErrorCode MakeTrap(TrapFnc fnc, const void* aux, bool safe);
// Enables support for unsafe traps in the SIGSYS signal handler. This is a
// one-way fuse. It works in conjunction with the BPF compiler emitting code
@@ -68,14 +67,10 @@
~Trap();
struct TrapKey {
- TrapKey(TrapFnc f, const void *a, bool s)
- : fnc(f),
- aux(a),
- safe(s) {
- }
- TrapFnc fnc;
- const void *aux;
- bool safe;
+ TrapKey(TrapFnc f, const void* a, bool s) : fnc(f), aux(a), safe(s) {}
+ TrapFnc fnc;
+ const void* aux;
+ bool safe;
bool operator<(const TrapKey&) const;
};
typedef std::map<TrapKey, uint16_t> TrapIds;
@@ -87,29 +82,27 @@
// It also gracefully deals with methods that should check for the singleton,
// but avoid instantiating it, if it doesn't exist yet
// (e.g. ErrorCodeFromTrapId()).
- static Trap *GetInstance();
- static void SigSysAction(int nr, siginfo_t *info, void *void_context);
+ static Trap* GetInstance();
+ static void SigSysAction(int nr, siginfo_t* info, void* void_context);
// Make sure that SigSys is not inlined in order to get slightly better crash
// dumps.
- void SigSys(int nr, siginfo_t *info, void *void_context)
- __attribute__ ((noinline));
- ErrorCode MakeTrapImpl(TrapFnc fnc, const void *aux, bool safe);
+ void SigSys(int nr, siginfo_t* info, void* void_context)
+ __attribute__((noinline));
+ ErrorCode MakeTrapImpl(TrapFnc fnc, const void* aux, bool safe);
bool SandboxDebuggingAllowedByUser() const;
-
-
// We have a global singleton that handles all of our SIGSYS traps. This
// variable must never be deallocated after it has been set up initially, as
// there is no way to reset in-kernel BPF filters that generate SIGSYS
// events.
- static Trap *global_trap_;
+ static Trap* global_trap_;
- TrapIds trap_ids_; // Maps from TrapKeys to numeric ids
- ErrorCode *trap_array_; // Array of ErrorCodes indexed by ids
- size_t trap_array_size_; // Currently used size of array
- size_t trap_array_capacity_; // Currently allocated capacity of array
- bool has_unsafe_traps_; // Whether unsafe traps have been enabled
+ TrapIds trap_ids_; // Maps from TrapKeys to numeric ids
+ ErrorCode* trap_array_; // Array of ErrorCodes indexed by ids
+ size_t trap_array_size_; // Currently used size of array
+ size_t trap_array_capacity_; // Currently allocated capacity of array
+ bool has_unsafe_traps_; // Whether unsafe traps have been enabled
// Our constructor is private. A shared global instance is created
// automatically as needed.
diff --git a/sandbox/linux/seccomp-bpf/verifier.cc b/sandbox/linux/seccomp-bpf/verifier.cc
index 60c0eab..1d6b26d 100644
--- a/sandbox/linux/seccomp-bpf/verifier.cc
+++ b/sandbox/linux/seccomp-bpf/verifier.cc
@@ -5,10 +5,10 @@
#include <string.h>
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h"
#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
#include "sandbox/linux/seccomp-bpf/verifier.h"
-
namespace {
using playground2::ErrorCode;
@@ -18,24 +18,20 @@
struct State {
State(const std::vector<struct sock_filter>& p,
- const struct arch_seccomp_data& d) :
- program(p),
- data(d),
- ip(0),
- accumulator(0),
- acc_is_valid(false) {
- }
+ const struct arch_seccomp_data& d)
+ : program(p), data(d), ip(0), accumulator(0), acc_is_valid(false) {}
const std::vector<struct sock_filter>& program;
- const struct arch_seccomp_data& data;
- unsigned int ip;
- uint32_t accumulator;
- bool acc_is_valid;
+ const struct arch_seccomp_data& data;
+ unsigned int ip;
+ uint32_t accumulator;
+ bool acc_is_valid;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(State);
};
-uint32_t EvaluateErrorCode(Sandbox *sandbox, const ErrorCode& code,
+uint32_t EvaluateErrorCode(Sandbox* sandbox,
+ const ErrorCode& code,
const struct arch_seccomp_data& data) {
if (code.error_type() == ErrorCode::ET_SIMPLE ||
code.error_type() == ErrorCode::ET_TRAP) {
@@ -44,49 +40,50 @@
if (code.width() == ErrorCode::TP_32BIT &&
(data.args[code.argno()] >> 32) &&
(data.args[code.argno()] & 0xFFFFFFFF80000000ull) !=
- 0xFFFFFFFF80000000ull) {
+ 0xFFFFFFFF80000000ull) {
return sandbox->Unexpected64bitArgument().err();
}
switch (code.op()) {
- case ErrorCode::OP_EQUAL:
- return EvaluateErrorCode(sandbox,
- (code.width() == ErrorCode::TP_32BIT
- ? uint32_t(data.args[code.argno()])
- : data.args[code.argno()]) == code.value()
- ? *code.passed()
- : *code.failed(),
- data);
- case ErrorCode::OP_HAS_ALL_BITS:
- return EvaluateErrorCode(sandbox,
- ((code.width() == ErrorCode::TP_32BIT
- ? uint32_t(data.args[code.argno()])
- : data.args[code.argno()]) & code.value())
- == code.value()
- ? *code.passed()
- : *code.failed(),
- data);
- case ErrorCode::OP_HAS_ANY_BITS:
- return EvaluateErrorCode(sandbox,
- (code.width() == ErrorCode::TP_32BIT
- ? uint32_t(data.args[code.argno()])
- : data.args[code.argno()]) & code.value()
- ? *code.passed()
- : *code.failed(),
- data);
- default:
- return SECCOMP_RET_INVALID;
+ case ErrorCode::OP_EQUAL:
+ return EvaluateErrorCode(sandbox,
+ (code.width() == ErrorCode::TP_32BIT
+ ? uint32_t(data.args[code.argno()])
+ : data.args[code.argno()]) == code.value()
+ ? *code.passed()
+ : *code.failed(),
+ data);
+ case ErrorCode::OP_HAS_ALL_BITS:
+ return EvaluateErrorCode(sandbox,
+ ((code.width() == ErrorCode::TP_32BIT
+ ? uint32_t(data.args[code.argno()])
+ : data.args[code.argno()]) &
+ code.value()) == code.value()
+ ? *code.passed()
+ : *code.failed(),
+ data);
+ case ErrorCode::OP_HAS_ANY_BITS:
+ return EvaluateErrorCode(sandbox,
+ (code.width() == ErrorCode::TP_32BIT
+ ? uint32_t(data.args[code.argno()])
+ : data.args[code.argno()]) &
+ code.value()
+ ? *code.passed()
+ : *code.failed(),
+ data);
+ default:
+ return SECCOMP_RET_INVALID;
}
} else {
return SECCOMP_RET_INVALID;
}
}
-bool VerifyErrorCode(Sandbox *sandbox,
+bool VerifyErrorCode(Sandbox* sandbox,
const std::vector<struct sock_filter>& program,
- struct arch_seccomp_data *data,
+ struct arch_seccomp_data* data,
const ErrorCode& root_code,
const ErrorCode& code,
- const char **err) {
+ const char** err) {
if (code.error_type() == ErrorCode::ET_SIMPLE ||
code.error_type() == ErrorCode::ET_TRAP) {
uint32_t computed_ret = Verifier::EvaluateBPF(program, *data, err);
@@ -109,102 +106,113 @@
return false;
}
switch (code.op()) {
- case ErrorCode::OP_EQUAL:
- // Verify that we can check a 32bit value (or the LSB of a 64bit value)
- // for equality.
- data->args[code.argno()] = code.value();
- if (!VerifyErrorCode(sandbox, program, data, root_code,
- *code.passed(), err)) {
+ case ErrorCode::OP_EQUAL:
+ // Verify that we can check a 32bit value (or the LSB of a 64bit value)
+ // for equality.
+ data->args[code.argno()] = code.value();
+ if (!VerifyErrorCode(
+ sandbox, program, data, root_code, *code.passed(), err)) {
+ return false;
+ }
+
+ // Change the value to no longer match and verify that this is detected
+ // as an inequality.
+ data->args[code.argno()] = code.value() ^ 0x55AA55AA;
+ if (!VerifyErrorCode(
+ sandbox, program, data, root_code, *code.failed(), err)) {
+ return false;
+ }
+
+ // BPF programs can only ever operate on 32bit values. So, we have
+ // generated additional BPF instructions that inspect the MSB. Verify
+ // that they behave as intended.
+ if (code.width() == ErrorCode::TP_32BIT) {
+ if (code.value() >> 32) {
+ SANDBOX_DIE(
+ "Invalid comparison of a 32bit system call argument "
+ "against a 64bit constant; this test is always false.");
+ }
+
+ // If the system call argument was intended to be a 32bit parameter,
+ // verify that it is a fatal error if a 64bit value is ever passed
+ // here.
+ data->args[code.argno()] = 0x100000000ull;
+ if (!VerifyErrorCode(sandbox,
+ program,
+ data,
+ root_code,
+ sandbox->Unexpected64bitArgument(),
+ err)) {
+ return false;
+ }
+ } else {
+ // If the system call argument was intended to be a 64bit parameter,
+ // verify that we can handle (in-)equality for the MSB. This is
+ // essentially the same test that we did earlier for the LSB.
+ // We only need to verify the behavior of the inequality test. We
+ // know that the equality test already passed, as unlike the kernel
+ // the Verifier does operate on 64bit quantities.
+ data->args[code.argno()] = code.value() ^ 0x55AA55AA00000000ull;
+ if (!VerifyErrorCode(
+ sandbox, program, data, root_code, *code.failed(), err)) {
+ return false;
+ }
+ }
+ break;
+ case ErrorCode::OP_HAS_ALL_BITS:
+ case ErrorCode::OP_HAS_ANY_BITS:
+ // A comprehensive test of bit values is difficult and potentially
+ // rather
+ // time-expensive. We avoid doing so at run-time and instead rely on the
+ // unittest for full testing. The test that we have here covers just the
+ // common cases. We test against the bitmask itself, all zeros and all
+ // ones.
+ {
+ // Testing "any" bits against a zero mask is always false. So, there
+ // are some cases, where we expect tests to take the "failed()" branch
+ // even though this is a test that normally should take "passed()".
+ const ErrorCode& passed =
+ (!code.value() && code.op() == ErrorCode::OP_HAS_ANY_BITS) ||
+
+ // On a 32bit system, it is impossible to pass a 64bit
+ // value as a
+ // system call argument. So, some additional tests always
+ // evaluate
+ // as false.
+ ((code.value() & ~uint64_t(uintptr_t(-1))) &&
+ code.op() == ErrorCode::OP_HAS_ALL_BITS) ||
+ (code.value() && !(code.value() & uintptr_t(-1)) &&
+ code.op() == ErrorCode::OP_HAS_ANY_BITS)
+ ? *code.failed()
+ : *code.passed();
+
+ // Similary, testing for "all" bits in a zero mask is always true. So,
+ // some cases pass despite them normally failing.
+ const ErrorCode& failed =
+ !code.value() && code.op() == ErrorCode::OP_HAS_ALL_BITS
+ ? *code.passed()
+ : *code.failed();
+
+ data->args[code.argno()] = code.value() & uintptr_t(-1);
+ if (!VerifyErrorCode(
+ sandbox, program, data, root_code, passed, err)) {
+ return false;
+ }
+ data->args[code.argno()] = uintptr_t(-1);
+ if (!VerifyErrorCode(
+ sandbox, program, data, root_code, passed, err)) {
+ return false;
+ }
+ data->args[code.argno()] = 0;
+ if (!VerifyErrorCode(
+ sandbox, program, data, root_code, failed, err)) {
+ return false;
+ }
+ }
+ break;
+ default: // TODO(markus): Need to add support for OP_GREATER
+ *err = "Unsupported operation in conditional error code";
return false;
- }
-
- // Change the value to no longer match and verify that this is detected
- // as an inequality.
- data->args[code.argno()] = code.value() ^ 0x55AA55AA;
- if (!VerifyErrorCode(sandbox, program, data, root_code,
- *code.failed(), err)) {
- return false;
- }
-
- // BPF programs can only ever operate on 32bit values. So, we have
- // generated additional BPF instructions that inspect the MSB. Verify
- // that they behave as intended.
- if (code.width() == ErrorCode::TP_32BIT) {
- if (code.value() >> 32) {
- SANDBOX_DIE("Invalid comparison of a 32bit system call argument "
- "against a 64bit constant; this test is always false.");
- }
-
- // If the system call argument was intended to be a 32bit parameter,
- // verify that it is a fatal error if a 64bit value is ever passed
- // here.
- data->args[code.argno()] = 0x100000000ull;
- if (!VerifyErrorCode(sandbox, program, data, root_code,
- sandbox->Unexpected64bitArgument(),
- err)) {
- return false;
- }
- } else {
- // If the system call argument was intended to be a 64bit parameter,
- // verify that we can handle (in-)equality for the MSB. This is
- // essentially the same test that we did earlier for the LSB.
- // We only need to verify the behavior of the inequality test. We
- // know that the equality test already passed, as unlike the kernel
- // the Verifier does operate on 64bit quantities.
- data->args[code.argno()] = code.value() ^ 0x55AA55AA00000000ull;
- if (!VerifyErrorCode(sandbox, program, data, root_code,
- *code.failed(), err)) {
- return false;
- }
- }
- break;
- case ErrorCode::OP_HAS_ALL_BITS:
- case ErrorCode::OP_HAS_ANY_BITS:
- // A comprehensive test of bit values is difficult and potentially rather
- // time-expensive. We avoid doing so at run-time and instead rely on the
- // unittest for full testing. The test that we have here covers just the
- // common cases. We test against the bitmask itself, all zeros and all
- // ones.
- {
- // Testing "any" bits against a zero mask is always false. So, there
- // are some cases, where we expect tests to take the "failed()" branch
- // even though this is a test that normally should take "passed()".
- const ErrorCode& passed =
- (!code.value() && code.op() == ErrorCode::OP_HAS_ANY_BITS) ||
-
- // On a 32bit system, it is impossible to pass a 64bit value as a
- // system call argument. So, some additional tests always evaluate
- // as false.
- ((code.value() & ~uint64_t(uintptr_t(-1))) &&
- code.op() == ErrorCode::OP_HAS_ALL_BITS) ||
- (code.value() && !(code.value() & uintptr_t(-1)) &&
- code.op() == ErrorCode::OP_HAS_ANY_BITS)
-
- ? *code.failed() : *code.passed();
-
- // Similary, testing for "all" bits in a zero mask is always true. So,
- // some cases pass despite them normally failing.
- const ErrorCode& failed =
- !code.value() && code.op() == ErrorCode::OP_HAS_ALL_BITS
- ? *code.passed() : *code.failed();
-
- data->args[code.argno()] = code.value() & uintptr_t(-1);
- if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) {
- return false;
- }
- data->args[code.argno()] = uintptr_t(-1);
- if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) {
- return false;
- }
- data->args[code.argno()] = 0;
- if (!VerifyErrorCode(sandbox, program, data, root_code, failed, err)) {
- return false;
- }
- }
- break;
- default: // TODO(markus): Need to add support for OP_GREATER
- *err = "Unsupported operation in conditional error code";
- return false;
}
} else {
*err = "Attempting to return invalid error code from BPF program";
@@ -213,16 +221,15 @@
return true;
}
-void Ld(State *state, const struct sock_filter& insn, const char **err) {
- if (BPF_SIZE(insn.code) != BPF_W ||
- BPF_MODE(insn.code) != BPF_ABS) {
+void Ld(State* state, const struct sock_filter& insn, const char** err) {
+ if (BPF_SIZE(insn.code) != BPF_W || BPF_MODE(insn.code) != BPF_ABS) {
*err = "Invalid BPF_LD instruction";
return;
}
if (insn.k < sizeof(struct arch_seccomp_data) && (insn.k & 3) == 0) {
// We only allow loading of properly aligned 32bit quantities.
memcpy(&state->accumulator,
- reinterpret_cast<const char *>(&state->data) + insn.k,
+ reinterpret_cast<const char*>(&state->data) + insn.k,
4);
} else {
*err = "Invalid operand in BPF_LD instruction";
@@ -232,7 +239,7 @@
return;
}
-void Jmp(State *state, const struct sock_filter& insn, const char **err) {
+void Jmp(State* state, const struct sock_filter& insn, const char** err) {
if (BPF_OP(insn.code) == BPF_JA) {
if (state->ip + insn.k + 1 >= state->program.size() ||
state->ip + insn.k + 1 <= state->ip) {
@@ -242,48 +249,47 @@
}
state->ip += insn.k;
} else {
- if (BPF_SRC(insn.code) != BPF_K ||
- !state->acc_is_valid ||
+ if (BPF_SRC(insn.code) != BPF_K || !state->acc_is_valid ||
state->ip + insn.jt + 1 >= state->program.size() ||
state->ip + insn.jf + 1 >= state->program.size()) {
goto compilation_failure;
}
switch (BPF_OP(insn.code)) {
- case BPF_JEQ:
- if (state->accumulator == insn.k) {
- state->ip += insn.jt;
- } else {
- state->ip += insn.jf;
- }
- break;
- case BPF_JGT:
- if (state->accumulator > insn.k) {
- state->ip += insn.jt;
- } else {
- state->ip += insn.jf;
- }
- break;
- case BPF_JGE:
- if (state->accumulator >= insn.k) {
- state->ip += insn.jt;
- } else {
- state->ip += insn.jf;
- }
- break;
- case BPF_JSET:
- if (state->accumulator & insn.k) {
- state->ip += insn.jt;
- } else {
- state->ip += insn.jf;
- }
- break;
- default:
- goto compilation_failure;
+ case BPF_JEQ:
+ if (state->accumulator == insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ case BPF_JGT:
+ if (state->accumulator > insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ case BPF_JGE:
+ if (state->accumulator >= insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ case BPF_JSET:
+ if (state->accumulator & insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ default:
+ goto compilation_failure;
}
}
}
-uint32_t Ret(State *, const struct sock_filter& insn, const char **err) {
+uint32_t Ret(State*, const struct sock_filter& insn, const char** err) {
if (BPF_SRC(insn.code) != BPF_K) {
*err = "Invalid BPF_RET instruction";
return 0;
@@ -291,7 +297,7 @@
return insn.k;
}
-void Alu(State *state, const struct sock_filter& insn, const char **err) {
+void Alu(State* state, const struct sock_filter& insn, const char** err) {
if (BPF_OP(insn.code) == BPF_NEG) {
state->accumulator = -state->accumulator;
return;
@@ -301,55 +307,55 @@
return;
}
switch (BPF_OP(insn.code)) {
- case BPF_ADD:
- state->accumulator += insn.k;
- break;
- case BPF_SUB:
- state->accumulator -= insn.k;
- break;
- case BPF_MUL:
- state->accumulator *= insn.k;
- break;
- case BPF_DIV:
- if (!insn.k) {
- *err = "Illegal division by zero";
+ case BPF_ADD:
+ state->accumulator += insn.k;
break;
- }
- state->accumulator /= insn.k;
- break;
- case BPF_MOD:
- if (!insn.k) {
- *err = "Illegal division by zero";
+ case BPF_SUB:
+ state->accumulator -= insn.k;
break;
- }
- state->accumulator %= insn.k;
- break;
- case BPF_OR:
- state->accumulator |= insn.k;
- break;
- case BPF_XOR:
- state->accumulator ^= insn.k;
- break;
- case BPF_AND:
- state->accumulator &= insn.k;
- break;
- case BPF_LSH:
- if (insn.k > 32) {
- *err = "Illegal shift operation";
+ case BPF_MUL:
+ state->accumulator *= insn.k;
break;
- }
- state->accumulator <<= insn.k;
- break;
- case BPF_RSH:
- if (insn.k > 32) {
- *err = "Illegal shift operation";
+ case BPF_DIV:
+ if (!insn.k) {
+ *err = "Illegal division by zero";
+ break;
+ }
+ state->accumulator /= insn.k;
break;
- }
- state->accumulator >>= insn.k;
- break;
- default:
- *err = "Invalid operator in arithmetic operation";
- break;
+ case BPF_MOD:
+ if (!insn.k) {
+ *err = "Illegal division by zero";
+ break;
+ }
+ state->accumulator %= insn.k;
+ break;
+ case BPF_OR:
+ state->accumulator |= insn.k;
+ break;
+ case BPF_XOR:
+ state->accumulator ^= insn.k;
+ break;
+ case BPF_AND:
+ state->accumulator &= insn.k;
+ break;
+ case BPF_LSH:
+ if (insn.k > 32) {
+ *err = "Illegal shift operation";
+ break;
+ }
+ state->accumulator <<= insn.k;
+ break;
+ case BPF_RSH:
+ if (insn.k > 32) {
+ *err = "Illegal shift operation";
+ break;
+ }
+ state->accumulator >>= insn.k;
+ break;
+ default:
+ *err = "Invalid operator in arithmetic operation";
+ break;
}
}
}
@@ -358,18 +364,12 @@
namespace playground2 {
-bool Verifier::VerifyBPF(Sandbox *sandbox,
+bool Verifier::VerifyBPF(Sandbox* sandbox,
const std::vector<struct sock_filter>& program,
- const Sandbox::Evaluators& evaluators,
- const char **err) {
+ const SandboxBpfPolicy& policy,
+ const char** err) {
*err = NULL;
- if (evaluators.size() != 1) {
- *err = "Not implemented";
- return false;
- }
- Sandbox::EvaluateSyscall evaluate_syscall = evaluators.begin()->first;
- void *aux = evaluators.begin()->second;
- for (SyscallIterator iter(false); !iter.Done(); ) {
+ for (SyscallIterator iter(false); !iter.Done();) {
uint32_t sysnum = iter.Next();
// We ideally want to iterate over the full system call range and values
// just above and just below this range. This gives us the full result set
@@ -378,8 +378,8 @@
// indicates either i386 or x86-64; and a set bit 30 indicates x32. And
// unless we pay attention to setting this bit correctly, an early check in
// our BPF program will make us fail with a misleading error code.
- struct arch_seccomp_data data = { static_cast<int>(sysnum),
- static_cast<uint32_t>(SECCOMP_ARCH) };
+ struct arch_seccomp_data data = {static_cast<int>(sysnum),
+ static_cast<uint32_t>(SECCOMP_ARCH)};
#if defined(__i386__) || defined(__x86_64__)
#if defined(__x86_64__) && defined(__ILP32__)
if (!(sysnum & 0x40000000u)) {
@@ -391,7 +391,7 @@
}
#endif
#endif
- ErrorCode code = evaluate_syscall(sandbox, sysnum, aux);
+ ErrorCode code = policy.EvaluateSyscall(sandbox, sysnum);
if (!VerifyErrorCode(sandbox, program, &data, code, code, err)) {
return false;
}
@@ -401,7 +401,7 @@
uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
const struct arch_seccomp_data& data,
- const char **err) {
+ const char** err) {
*err = NULL;
if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) {
*err = "Invalid program length";
@@ -414,33 +414,34 @@
}
const struct sock_filter& insn = program[state.ip];
switch (BPF_CLASS(insn.code)) {
- case BPF_LD:
- Ld(&state, insn, err);
- break;
- case BPF_JMP:
- Jmp(&state, insn, err);
- break;
- case BPF_RET: {
- uint32_t r = Ret(&state, insn, err);
- switch (r & SECCOMP_RET_ACTION) {
- case SECCOMP_RET_TRAP:
- case SECCOMP_RET_ERRNO:
- case SECCOMP_RET_ALLOW:
+ case BPF_LD:
+ Ld(&state, insn, err);
break;
- case SECCOMP_RET_KILL: // We don't ever generate this
- case SECCOMP_RET_TRACE: // We don't ever generate this
- case SECCOMP_RET_INVALID: // Should never show up in BPF program
- default:
- *err = "Unexpected return code found in BPF program";
- return 0;
+ case BPF_JMP:
+ Jmp(&state, insn, err);
+ break;
+ case BPF_RET: {
+ uint32_t r = Ret(&state, insn, err);
+ switch (r & SECCOMP_RET_ACTION) {
+ case SECCOMP_RET_TRAP:
+ case SECCOMP_RET_ERRNO:
+ case SECCOMP_RET_ALLOW:
+ break;
+ case SECCOMP_RET_KILL: // We don't ever generate this
+ case SECCOMP_RET_TRACE: // We don't ever generate this
+ case SECCOMP_RET_INVALID: // Should never show up in BPF program
+ default:
+ *err = "Unexpected return code found in BPF program";
+ return 0;
+ }
+ return r;
}
- return r; }
- case BPF_ALU:
- Alu(&state, insn, err);
- break;
- default:
- *err = "Unexpected instruction in BPF program";
- break;
+ case BPF_ALU:
+ Alu(&state, insn, err);
+ break;
+ default:
+ *err = "Unexpected instruction in BPF program";
+ break;
}
}
return 0;
diff --git a/sandbox/linux/seccomp-bpf/verifier.h b/sandbox/linux/seccomp-bpf/verifier.h
index 3e99a08..fff5b63 100644
--- a/sandbox/linux/seccomp-bpf/verifier.h
+++ b/sandbox/linux/seccomp-bpf/verifier.h
@@ -10,9 +10,10 @@
#include <utility>
#include <vector>
-
namespace playground2 {
+class SandboxBpfPolicy;
+
class Verifier {
public:
// Evaluate the BPF program for all possible inputs and verify that it
@@ -22,10 +23,10 @@
// set by the "evaluators".
// Upon success, "err" is set to NULL. Upon failure, it contains a static
// error message that does not need to be free()'d.
- static bool VerifyBPF(Sandbox *sandbox,
+ static bool VerifyBPF(Sandbox* sandbox,
const std::vector<struct sock_filter>& program,
- const Sandbox::Evaluators& evaluators,
- const char **err);
+ const SandboxBpfPolicy& policy,
+ const char** err);
// Evaluate a given BPF program for a particular set of system call
// parameters. If evaluation failed for any reason, "err" will be set to
@@ -37,7 +38,7 @@
// BPF compiler, we might have to extend this BPF interpreter.
static uint32_t EvaluateBPF(const std::vector<struct sock_filter>& program,
const struct arch_seccomp_data& data,
- const char **err);
+ const char** err);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Verifier);
diff --git a/sandbox/linux/services/broker_process.cc b/sandbox/linux/services/broker_process.cc
index 81d78b0..0e91c20 100644
--- a/sandbox/linux/services/broker_process.cc
+++ b/sandbox/linux/services/broker_process.cc
@@ -16,6 +16,7 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/pickle.h"
#include "base/posix/eintr_wrapper.h"
@@ -147,13 +148,13 @@
int child_pid = fork();
if (child_pid == -1) {
- (void) HANDLE_EINTR(close(socket_pair[0]));
- (void) HANDLE_EINTR(close(socket_pair[1]));
+ ignore_result(HANDLE_EINTR(close(socket_pair[0])));
+ ignore_result(HANDLE_EINTR(close(socket_pair[1])));
return false;
}
if (child_pid) {
// We are the parent and we have just forked our broker process.
- (void) HANDLE_EINTR(close(socket_pair[0]));
+ ignore_result(HANDLE_EINTR(close(socket_pair[0])));
// We should only be able to write to the IPC channel. We'll always send
// a new file descriptor to receive the reply on.
shutdown(socket_pair[1], SHUT_RD);
@@ -164,7 +165,7 @@
return true;
} else {
// We are the broker.
- (void) HANDLE_EINTR(close(socket_pair[1]));
+ ignore_result(HANDLE_EINTR(close(socket_pair[1])));
// We should only be able to read from this IPC channel. We will send our
// replies on a new file descriptor attached to the requests.
shutdown(socket_pair[0], SHUT_WR);
diff --git a/sandbox/linux/services/broker_process.h b/sandbox/linux/services/broker_process.h
index 00218a0..6b13b33 100644
--- a/sandbox/linux/services/broker_process.h
+++ b/sandbox/linux/services/broker_process.h
@@ -66,26 +66,31 @@
kCommandAccess,
};
int PathAndFlagsSyscall(enum IPCCommands command_type,
- const char* pathname, int flags) const;
+ const char* pathname,
+ int flags) const;
bool HandleRequest() const;
- bool HandleRemoteCommand(IPCCommands command_type, int reply_ipc,
- const Pickle& read_pickle, PickleIterator iter) const;
+ bool HandleRemoteCommand(IPCCommands command_type,
+ int reply_ipc,
+ const Pickle& read_pickle,
+ PickleIterator iter) const;
void AccessFileForIPC(const std::string& requested_filename,
- int mode, Pickle* write_pickle) const;
+ int mode,
+ Pickle* write_pickle) const;
void OpenFileForIPC(const std::string& requested_filename,
- int flags, Pickle* write_pickle,
+ int flags,
+ Pickle* write_pickle,
std::vector<int>* opened_files) const;
bool GetFileNameIfAllowedToAccess(const char*, int, const char**) const;
bool GetFileNameIfAllowedToOpen(const char*, int, const char**) const;
const int denied_errno_;
- bool initialized_; // Whether we've been through Init() yet.
- bool is_child_; // Whether we're the child (broker process).
- bool fast_check_in_client_; // Whether to forward a request that we know
- // will be denied to the broker.
+ bool initialized_; // Whether we've been through Init() yet.
+ bool is_child_; // Whether we're the child (broker process).
+ bool fast_check_in_client_; // Whether to forward a request that we know
+ // will be denied to the broker.
bool quiet_failures_for_tests_; // Disable certain error message when
// testing for failures.
- pid_t broker_pid_; // The PID of the broker (child).
+ pid_t broker_pid_; // The PID of the broker (child).
const std::vector<std::string> allowed_r_files_; // Files allowed for read.
const std::vector<std::string> allowed_w_files_; // Files allowed for write.
int ipc_socketpair_; // Our communication channel to parent or child.
diff --git a/sandbox/linux/services/broker_process_unittest.cc b/sandbox/linux/services/broker_process_unittest.cc
index 6ce294e..4cb9c6f 100644
--- a/sandbox/linux/services/broker_process_unittest.cc
+++ b/sandbox/linux/services/broker_process_unittest.cc
@@ -15,11 +15,15 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/file_util.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/posix/eintr_wrapper.h"
#include "sandbox/linux/tests/unit_tests.h"
#include "testing/gtest/include/gtest/gtest.h"
+using file_util::ScopedFD;
+
namespace sandbox {
namespace {
@@ -68,14 +72,13 @@
std::vector<std::string> read_whitelist;
read_whitelist.push_back("/proc/cpuinfo");
- BrokerProcess* open_broker = new BrokerProcess(EPERM,
- read_whitelist,
- std::vector<std::string>());
+ scoped_ptr<BrokerProcess> open_broker(
+ new BrokerProcess(EPERM, read_whitelist, std::vector<std::string>()));
ASSERT_TRUE(open_broker->Init(NULL));
pid_t broker_pid = open_broker->broker_pid();
- delete(open_broker);
- // Now we check that the broker has exited properly.
+ // Destroy the broker and check it has exited properly.
+ open_broker.reset();
int status = 0;
ASSERT_EQ(waitpid(broker_pid, &status, 0), broker_pid);
ASSERT_TRUE(WIFEXITED(status));
@@ -224,12 +227,12 @@
ASSERT_EQ(ret, -denied_errno);
// We have some extra sanity check for clearly wrong values.
- fd = open_broker.Open(kRW_WhiteListed, O_RDONLY|O_WRONLY|O_RDWR);
+ fd = open_broker.Open(kRW_WhiteListed, O_RDONLY | O_WRONLY | O_RDWR);
ASSERT_EQ(fd, -denied_errno);
// It makes no sense to allow O_CREAT in a 2-parameters open. Ensure this
// is denied.
- fd = open_broker.Open(kRW_WhiteListed, O_RDWR|O_CREAT);
+ fd = open_broker.Open(kRW_WhiteListed, O_RDWR | O_CREAT);
ASSERT_EQ(fd, -denied_errno);
}
@@ -265,15 +268,14 @@
std::vector<std::string> read_whitelist;
read_whitelist.push_back(kFileCpuInfo);
- BrokerProcess* open_broker = new BrokerProcess(EPERM,
- read_whitelist,
- std::vector<std::string>(),
- fast_check_in_client);
+ scoped_ptr<BrokerProcess> open_broker(new BrokerProcess(
+ EPERM, read_whitelist, std::vector<std::string>(), fast_check_in_client));
ASSERT_TRUE(open_broker->Init(NULL));
pid_t broker_pid = open_broker->broker_pid();
int fd = -1;
fd = open_broker->Open(kFileCpuInfo, O_RDWR);
+ ScopedFD fd_closer(&fd);
ASSERT_EQ(fd, -EPERM);
// Check we can read /proc/cpuinfo.
@@ -285,6 +287,7 @@
// Open cpuinfo via the broker.
int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
+ ScopedFD cpuinfo_fd_closer(&cpuinfo_fd);
ASSERT_GE(cpuinfo_fd, 0);
char buf[3];
memset(buf, 0, sizeof(buf));
@@ -293,6 +296,7 @@
// Open cpuinfo directly.
int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY);
+ ScopedFD cpuinfo_fd2_closer(&cpuinfo_fd2);
ASSERT_GE(cpuinfo_fd2, 0);
char buf2[3];
memset(buf2, 1, sizeof(buf2));
@@ -305,14 +309,7 @@
// ourselves.
ASSERT_EQ(memcmp(buf, buf2, read_len1), 0);
- if (fd >= 0)
- close(fd);
- if (cpuinfo_fd >= 0)
- close(cpuinfo_fd);
- if (cpuinfo_fd2 >= 0)
- close(cpuinfo_fd);
-
- delete(open_broker);
+ open_broker.reset();
// Now we check that the broker has exited properly.
int status = 0;
diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc
index a6387d2..cea757c 100644
--- a/sandbox/linux/services/credentials.cc
+++ b/sandbox/linux/services/credentials.cc
@@ -4,11 +4,21 @@
#include "sandbox/linux/services/credentials.h"
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
#include <sys/capability.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "base/basictypes.h"
+#include "base/bind.h"
#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/template_util.h"
+#include "base/threading/thread.h"
namespace {
@@ -32,6 +42,110 @@
// Wrapper to manage the result from libcap2's cap_from_text().
typedef scoped_ptr<char, CapTextFreeDeleter> ScopedCapText;
+struct FILECloser {
+ inline void operator()(FILE* f) const {
+ DCHECK(f);
+ PCHECK(0 == fclose(f));
+ }
+};
+
+// Don't use ScopedFILE in base/file_util.h since it doesn't check fclose().
+// TODO(jln): fix base/.
+typedef scoped_ptr<FILE, FILECloser> ScopedFILE;
+
+struct DIRCloser {
+ void operator()(DIR* d) const {
+ DCHECK(d);
+ PCHECK(0 == closedir(d));
+ }
+};
+
+typedef scoped_ptr<DIR, DIRCloser> ScopedDIR;
+
+COMPILE_ASSERT((base::is_same<uid_t, gid_t>::value), UidAndGidAreSameType);
+// generic_id_t can be used for either uid_t or gid_t.
+typedef uid_t generic_id_t;
+
+// Write a uid or gid mapping from |id| to |id| in |map_file|.
+bool WriteToIdMapFile(const char* map_file, generic_id_t id) {
+ ScopedFILE f(fopen(map_file, "w"));
+ PCHECK(f);
+ const uid_t inside_id = id;
+ const uid_t outside_id = id;
+ int num = fprintf(f.get(), "%d %d 1\n", inside_id, outside_id);
+ if (num < 0) return false;
+ // Manually call fflush() to catch permission failures.
+ int ret = fflush(f.get());
+ if (ret) {
+ VLOG(1) << "Could not write to id map file";
+ return false;
+ }
+ return true;
+}
+
+// Checks that the set of RES-uids and the set of RES-gids have
+// one element each and return that element in |resuid| and |resgid|
+// respectively. It's ok to pass NULL as one or both of the ids.
+bool GetRESIds(uid_t* resuid, gid_t* resgid) {
+ uid_t ruid, euid, suid;
+ gid_t rgid, egid, sgid;
+ PCHECK(getresuid(&ruid, &euid, &suid) == 0);
+ PCHECK(getresgid(&rgid, &egid, &sgid) == 0);
+ const bool uids_are_equal = (ruid == euid) && (ruid == suid);
+ const bool gids_are_equal = (rgid == egid) && (rgid == sgid);
+ if (!uids_are_equal || !gids_are_equal) return false;
+ if (resuid) *resuid = euid;
+ if (resgid) *resgid = egid;
+ return true;
+}
+
+// chroot() and chdir() to /proc/<tid>/fdinfo.
+void ChrootToThreadFdInfo(base::PlatformThreadId tid, bool* result) {
+ DCHECK(result);
+ *result = false;
+
+ COMPILE_ASSERT((base::is_same<base::PlatformThreadId, int>::value),
+ TidIsAnInt);
+ const std::string current_thread_fdinfo = "/proc/" +
+ base::IntToString(tid) + "/fdinfo/";
+
+ // Make extra sure that /proc/<tid>/fdinfo is unique to the thread.
+ CHECK(0 == unshare(CLONE_FILES));
+ int chroot_ret = chroot(current_thread_fdinfo.c_str());
+ if (chroot_ret) {
+ PLOG(ERROR) << "Could not chroot";
+ return;
+ }
+
+ // CWD is essentially an implicit file descriptor, so be careful to not leave
+ // it behind.
+ PCHECK(0 == chdir("/"));
+
+ *result = true;
+ return;
+}
+
+// chroot() to an empty dir that is "safe". To be safe, it must not contain
+// any subdirectory (chroot-ing there would allow a chroot escape) and it must
+// be impossible to create an empty directory there.
+// We achieve this by doing the following:
+// 1. We create a new thread, which will create a new /proc/<tid>/ directory
+// 2. We chroot to /proc/<tid>/fdinfo/
+// This is already "safe", since fdinfo/ does not contain another directory and
+// one cannot create another directory there.
+// 3. The thread dies
+// After (3) happens, the directory is not available anymore in /proc.
+bool ChrootToSafeEmptyDir() {
+ base::Thread chrooter("sandbox_chrooter");
+ if (!chrooter.Start()) return false;
+ bool is_chrooted = false;
+ chrooter.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&ChrootToThreadFdInfo, chrooter.thread_id(), &is_chrooted));
+ // Make sure our task has run before committing the return value.
+ chrooter.Stop();
+ return is_chrooted;
+}
+
} // namespace.
namespace sandbox {
@@ -42,13 +156,60 @@
Credentials::~Credentials() {
}
-void Credentials::DropAllCapabilities() {
+bool Credentials::HasOpenDirectory(int proc_fd) {
+ int proc_self_fd = -1;
+ if (proc_fd >= 0) {
+ proc_self_fd = openat(proc_fd, "self/fd", O_DIRECTORY | O_RDONLY);
+ } else {
+ proc_self_fd = openat(AT_FDCWD, "/proc/self/fd", O_DIRECTORY | O_RDONLY);
+ if (proc_self_fd < 0) {
+ // If not available, guess false.
+ // TODO(mostynb@opera.com): add a CHECK_EQ(ENOENT, errno); Figure out what
+ // other situations are here. http://crbug.com/314985
+ return false;
+ }
+ }
+ CHECK_GE(proc_self_fd, 0);
+
+ // Ownership of proc_self_fd is transferred here, it must not be closed
+ // or modified afterwards except via dir.
+ ScopedDIR dir(fdopendir(proc_self_fd));
+ CHECK(dir);
+
+ struct dirent e;
+ struct dirent* de;
+ while (!readdir_r(dir.get(), &e, &de) && de) {
+ if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) {
+ continue;
+ }
+
+ int fd_num;
+ CHECK(base::StringToInt(e.d_name, &fd_num));
+ if (fd_num == proc_fd || fd_num == proc_self_fd) {
+ continue;
+ }
+
+ struct stat s;
+ // It's OK to use proc_self_fd here, fstatat won't modify it.
+ CHECK(fstatat(proc_self_fd, e.d_name, &s, 0) == 0);
+ if (S_ISDIR(s.st_mode)) {
+ return true;
+ }
+ }
+
+ // No open unmanaged directories found.
+ return false;
+}
+
+bool Credentials::DropAllCapabilities() {
ScopedCap cap(cap_init());
CHECK(cap);
PCHECK(0 == cap_set_proc(cap.get()));
+ // We never let this function fail.
+ return true;
}
-bool Credentials::HasAnyCapability() {
+bool Credentials::HasAnyCapability() const {
ScopedCap current_cap(cap_get_proc());
CHECK(current_cap);
ScopedCap empty_cap(cap_init());
@@ -56,7 +217,7 @@
return cap_compare(current_cap.get(), empty_cap.get()) != 0;
}
-scoped_ptr<std::string> Credentials::GetCurrentCapString() {
+scoped_ptr<std::string> Credentials::GetCurrentCapString() const {
ScopedCap current_cap(cap_get_proc());
CHECK(current_cap);
ScopedCapText cap_text(cap_to_text(current_cap.get(), NULL));
@@ -64,4 +225,42 @@
return scoped_ptr<std::string> (new std::string(cap_text.get()));
}
+bool Credentials::MoveToNewUserNS() {
+ uid_t uid;
+ gid_t gid;
+ if (!GetRESIds(&uid, &gid)) {
+ // If all the uids (or gids) are not equal to each other, the security
+ // model will most likely confuse the caller, abort.
+ DVLOG(1) << "uids or gids differ!";
+ return false;
+ }
+ int ret = unshare(CLONE_NEWUSER);
+ // EPERM can happen if already in a chroot. EUSERS if too many nested
+ // namespaces are used. EINVAL for kernels that don't support the feature.
+ // Valgrind will ENOSYS unshare().
+ PCHECK(!ret || errno == EPERM || errno == EUSERS || errno == EINVAL ||
+ errno == ENOSYS);
+ if (ret) {
+ VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available "
+ << "on this kernel.";
+ return false;
+ }
+ // The current {r,e,s}{u,g}id is now an overflow id (c.f.
+ // /proc/sys/kernel/overflowuid). Setup the uid and gid maps.
+ DCHECK(GetRESIds(NULL, NULL));
+ const char kGidMapFile[] = "/proc/self/gid_map";
+ const char kUidMapFile[] = "/proc/self/uid_map";
+ CHECK(WriteToIdMapFile(kGidMapFile, gid));
+ CHECK(WriteToIdMapFile(kUidMapFile, uid));
+ DCHECK(GetRESIds(NULL, NULL));
+ return true;
+}
+
+bool Credentials::DropFileSystemAccess() {
+ // Chrooting to a safe empty dir will only be safe if no directory file
+ // descriptor is available to the process.
+ DCHECK(!HasOpenDirectory(-1));
+ return ChrootToSafeEmptyDir();
+}
+
} // namespace sandbox.
diff --git a/sandbox/linux/services/credentials.h b/sandbox/linux/services/credentials.h
index 3ea3cfc..c23db93 100644
--- a/sandbox/linux/services/credentials.h
+++ b/sandbox/linux/services/credentials.h
@@ -26,16 +26,49 @@
Credentials();
~Credentials();
+ // Checks whether the current process has any directory file descriptor open.
+ // Directory file descriptors are "capabilities" that would let a process use
+ // system calls such as openat() to bypass restrictions such as
+ // DropFileSystemAccess().
+ // Sometimes it's useful to call HasOpenDirectory() after file system access
+ // has been dropped. In this case, |proc_fd| should be a file descriptor to
+ // /proc. The file descriptor in |proc_fd| will be ignored by
+ // HasOpenDirectory() and remains owned by the caller. It is very important
+ // for the caller to close it.
+ // If /proc is available, |proc_fd| can be passed as -1.
+ // If |proc_fd| is -1 and /proc is not available, this function will return
+ // false.
+ bool HasOpenDirectory(int proc_fd);
+
// Drop all capabilities in the effective, inheritable and permitted sets for
// the current process.
- void DropAllCapabilities();
+ bool DropAllCapabilities();
// Return true iff there is any capability in any of the capabilities sets
// of the current process.
- bool HasAnyCapability();
+ bool HasAnyCapability() const;
// Returns the capabilities of the current process in textual form, as
// documented in libcap2's cap_to_text(3). This is mostly useful for
// debugging and tests.
- scoped_ptr<std::string> GetCurrentCapString();
+ scoped_ptr<std::string> GetCurrentCapString() const;
+
+ // Move the current process to a new "user namespace" as supported by Linux
+ // 3.8+ (CLONE_NEWUSER).
+ // The uid map will be set-up so that the perceived uid and gid will not
+ // change.
+ // If this call succeeds, the current process will be granted a full set of
+ // capabilities in the new namespace.
+ bool MoveToNewUserNS();
+
+ // Remove the ability of the process to access the file system. File
+ // descriptors which are already open prior to calling this API remain
+ // available.
+ // The implementation currently uses chroot(2) and requires CAP_SYS_CHROOT.
+ // CAP_SYS_CHROOT can be acquired by using the MoveToNewUserNS() API.
+ // Make sure to call DropAllCapabilities() after this call to prevent
+ // escapes.
+ // To be secure, it's very important for this API to not be called while the
+ // process has any directory file descriptor open.
+ bool DropFileSystemAccess();
private:
DISALLOW_COPY_AND_ASSIGN(Credentials);
diff --git a/sandbox/linux/services/credentials_unittest.cc b/sandbox/linux/services/credentials_unittest.cc
index 7c705a4..9160bf7 100644
--- a/sandbox/linux/services/credentials_unittest.cc
+++ b/sandbox/linux/services/credentials_unittest.cc
@@ -4,13 +4,51 @@
#include "sandbox/linux/services/credentials.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/file_util.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "sandbox/linux/tests/unit_tests.h"
#include "testing/gtest/include/gtest/gtest.h"
+using file_util::ScopedFD;
+
namespace sandbox {
+namespace {
+
+bool DirectoryExists(const char* path) {
+ struct stat dir;
+ errno = 0;
+ int ret = stat(path, &dir);
+ return -1 != ret || ENOENT != errno;
+}
+
+bool WorkingDirectoryIsRoot() {
+ char current_dir[PATH_MAX];
+ char* cwd = getcwd(current_dir, sizeof(current_dir));
+ PCHECK(cwd);
+ if (strcmp("/", cwd)) return false;
+
+ // The current directory is the root. Add a few paranoid checks.
+ struct stat current;
+ CHECK_EQ(0, stat(".", ¤t));
+ struct stat parrent;
+ CHECK_EQ(0, stat("..", &parrent));
+ CHECK_EQ(current.st_dev, parrent.st_dev);
+ CHECK_EQ(current.st_ino, parrent.st_ino);
+ CHECK_EQ(current.st_mode, parrent.st_mode);
+ CHECK_EQ(current.st_uid, parrent.st_uid);
+ CHECK_EQ(current.st_gid, parrent.st_gid);
+ return true;
+}
+
// Give dynamic tools a simple thing to test.
TEST(Credentials, CreateAndDestroy) {
{
@@ -20,17 +58,158 @@
scoped_ptr<Credentials> cred2(new Credentials);
}
+TEST(Credentials, HasOpenDirectory) {
+ Credentials creds;
+ // No open directory should exist at startup.
+ EXPECT_FALSE(creds.HasOpenDirectory(-1));
+ {
+ // Have a "/dev" file descriptor around.
+ int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
+ ScopedFD dev_fd_closer(&dev_fd);
+ EXPECT_TRUE(creds.HasOpenDirectory(-1));
+ }
+ EXPECT_FALSE(creds.HasOpenDirectory(-1));
+}
+
+TEST(Credentials, HasOpenDirectoryWithFD) {
+ Credentials creds;
+
+ int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
+ ScopedFD proc_fd_closer(&proc_fd);
+ ASSERT_LE(0, proc_fd);
+
+ // Don't pass |proc_fd|, an open directory (proc_fd) should
+ // be detected.
+ EXPECT_TRUE(creds.HasOpenDirectory(-1));
+ // Pass |proc_fd| and no open directory should be detected.
+ EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
+
+ {
+ // Have a "/dev" file descriptor around.
+ int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
+ ScopedFD dev_fd_closer(&dev_fd);
+ EXPECT_TRUE(creds.HasOpenDirectory(proc_fd));
+ }
+
+ // The "/dev" file descriptor should now be closed, |proc_fd| is the only
+ // directory file descriptor open.
+ EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
+}
+
SANDBOX_TEST(Credentials, DropAllCaps) {
Credentials creds;
- creds.DropAllCapabilities();
- SANDBOX_ASSERT(!creds.HasAnyCapability());
+ CHECK(creds.DropAllCapabilities());
+ CHECK(!creds.HasAnyCapability());
}
SANDBOX_TEST(Credentials, GetCurrentCapString) {
Credentials creds;
- creds.DropAllCapabilities();
+ CHECK(creds.DropAllCapabilities());
const char kNoCapabilityText[] = "=";
- SANDBOX_ASSERT(*creds.GetCurrentCapString() == kNoCapabilityText);
+ CHECK(*creds.GetCurrentCapString() == kNoCapabilityText);
}
+SANDBOX_TEST(Credentials, MoveToNewUserNS) {
+ Credentials creds;
+ creds.DropAllCapabilities();
+ bool userns_supported = creds.MoveToNewUserNS();
+ fprintf(stdout, "Unprivileged CLONE_NEWUSER supported: %s\n",
+ userns_supported ? "true." : "false.");
+ fflush(stdout);
+ if (!userns_supported) {
+ fprintf(stdout, "This kernel does not support unprivileged namespaces. "
+ "USERNS tests will succeed without running.\n");
+ fflush(stdout);
+ return;
+ }
+ CHECK(creds.HasAnyCapability());
+ creds.DropAllCapabilities();
+ CHECK(!creds.HasAnyCapability());
+}
+
+SANDBOX_TEST(Credentials, UidIsPreserved) {
+ Credentials creds;
+ creds.DropAllCapabilities();
+ uid_t old_ruid, old_euid, old_suid;
+ gid_t old_rgid, old_egid, old_sgid;
+ PCHECK(0 == getresuid(&old_ruid, &old_euid, &old_suid));
+ PCHECK(0 == getresgid(&old_rgid, &old_egid, &old_sgid));
+ // Probably missing kernel support.
+ if (!creds.MoveToNewUserNS()) return;
+ uid_t new_ruid, new_euid, new_suid;
+ PCHECK(0 == getresuid(&new_ruid, &new_euid, &new_suid));
+ CHECK(old_ruid == new_ruid);
+ CHECK(old_euid == new_euid);
+ CHECK(old_suid == new_suid);
+
+ gid_t new_rgid, new_egid, new_sgid;
+ PCHECK(0 == getresgid(&new_rgid, &new_egid, &new_sgid));
+ CHECK(old_rgid == new_rgid);
+ CHECK(old_egid == new_egid);
+ CHECK(old_sgid == new_sgid);
+}
+
+bool NewUserNSCycle(Credentials* creds) {
+ DCHECK(creds);
+ if (!creds->MoveToNewUserNS() ||
+ !creds->HasAnyCapability() ||
+ !creds->DropAllCapabilities() ||
+ creds->HasAnyCapability()) {
+ return false;
+ }
+ return true;
+}
+
+SANDBOX_TEST(Credentials, NestedUserNS) {
+ Credentials creds;
+ CHECK(creds.DropAllCapabilities());
+ // Probably missing kernel support.
+ if (!creds.MoveToNewUserNS()) return;
+ creds.DropAllCapabilities();
+ // As of 3.12, the kernel has a limit of 32. See create_user_ns().
+ const int kNestLevel = 10;
+ for (int i = 0; i < kNestLevel; ++i) {
+ CHECK(NewUserNSCycle(&creds)) << "Creating new user NS failed at iteration "
+ << i << ".";
+ }
+}
+
+// Test the WorkingDirectoryIsRoot() helper.
+TEST(Credentials, CanDetectRoot) {
+ ASSERT_EQ(0, chdir("/proc/"));
+ ASSERT_FALSE(WorkingDirectoryIsRoot());
+ ASSERT_EQ(0, chdir("/"));
+ ASSERT_TRUE(WorkingDirectoryIsRoot());
+}
+
+SANDBOX_TEST(Credentials, DropFileSystemAccessIsSafe) {
+ Credentials creds;
+ CHECK(creds.DropAllCapabilities());
+ // Probably missing kernel support.
+ if (!creds.MoveToNewUserNS()) return;
+ CHECK(creds.DropFileSystemAccess());
+ CHECK(!DirectoryExists("/proc"));
+ CHECK(WorkingDirectoryIsRoot());
+ // We want the chroot to never have a subdirectory. A subdirectory
+ // could allow a chroot escape.
+ CHECK_NE(0, mkdir("/test", 0700));
+}
+
+// Check that after dropping filesystem access and dropping privileges
+// it is not possible to regain capabilities.
+SANDBOX_TEST(Credentials, CannotRegainPrivileges) {
+ Credentials creds;
+ CHECK(creds.DropAllCapabilities());
+ // Probably missing kernel support.
+ if (!creds.MoveToNewUserNS()) return;
+ CHECK(creds.DropFileSystemAccess());
+ CHECK(creds.DropAllCapabilities());
+
+ // The kernel should now prevent us from regaining capabilities because we
+ // are in a chroot.
+ CHECK(!creds.MoveToNewUserNS());
+}
+
+} // namespace.
+
} // namespace sandbox.
diff --git a/sandbox/linux/services/init_process_reaper.cc b/sandbox/linux/services/init_process_reaper.cc
new file mode 100644
index 0000000..f5473ba
--- /dev/null
+++ b/sandbox/linux/services/init_process_reaper.cc
@@ -0,0 +1,101 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/init_process_reaper.h"
+
+#include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace sandbox {
+
+namespace {
+
+void DoNothingSignalHandler(int signal) {}
+
+} // namespace
+
+bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback) {
+ int sync_fds[2];
+ // We want to use send, so we can't use a pipe
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) {
+ PLOG(ERROR) << "Failed to create socketpair";
+ return false;
+ }
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ int close_ret;
+ close_ret = HANDLE_EINTR(close(sync_fds[0]));
+ DPCHECK(!close_ret);
+ close_ret = HANDLE_EINTR(close(sync_fds[1]));
+ DPCHECK(!close_ret);
+ return false;
+ }
+ if (child_pid) {
+ // In the parent, assuming the role of an init process.
+ // The disposition for SIGCHLD cannot be SIG_IGN or wait() will only return
+ // once all of our childs are dead. Since we're init we need to reap childs
+ // as they come.
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = &DoNothingSignalHandler;
+ CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
+
+ int close_ret;
+ close_ret = HANDLE_EINTR(close(sync_fds[0]));
+ DPCHECK(!close_ret);
+ close_ret = shutdown(sync_fds[1], SHUT_RD);
+ DPCHECK(!close_ret);
+ if (post_fork_parent_callback)
+ post_fork_parent_callback->Run();
+ // Tell the child to continue
+ CHECK(HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) == 1);
+ close_ret = HANDLE_EINTR(close(sync_fds[1]));
+ DPCHECK(!close_ret);
+
+ for (;;) {
+ // Loop until we have reaped our one natural child
+ siginfo_t reaped_child_info;
+ int wait_ret =
+ HANDLE_EINTR(waitid(P_ALL, 0, &reaped_child_info, WEXITED));
+ if (wait_ret)
+ _exit(1);
+ if (reaped_child_info.si_pid == child_pid) {
+ int exit_code = 0;
+ // We're done waiting
+ if (reaped_child_info.si_code == CLD_EXITED) {
+ exit_code = reaped_child_info.si_status;
+ }
+ // Exit with the same exit code as our parent. Exit with 0 if we got
+ // signaled.
+ _exit(exit_code);
+ }
+ }
+ } else {
+ // The child needs to wait for the parent to run the callback to avoid a
+ // race condition.
+ int close_ret;
+ close_ret = HANDLE_EINTR(close(sync_fds[1]));
+ DPCHECK(!close_ret);
+ close_ret = shutdown(sync_fds[0], SHUT_WR);
+ DPCHECK(!close_ret);
+ char should_continue;
+ int read_ret = HANDLE_EINTR(read(sync_fds[0], &should_continue, 1));
+ close_ret = HANDLE_EINTR(close(sync_fds[0]));
+ DPCHECK(!close_ret);
+ if (read_ret == 1)
+ return true;
+ else
+ return false;
+ }
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/init_process_reaper.h b/sandbox/linux/services/init_process_reaper.h
new file mode 100644
index 0000000..531d18c
--- /dev/null
+++ b/sandbox/linux/services/init_process_reaper.h
@@ -0,0 +1,23 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
+#define SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
+
+#include "base/callback_forward.h"
+
+namespace sandbox {
+
+// The current process will fork(). The parent will become a process reaper
+// like init(1). The child will continue normally (after this function
+// returns).
+// If not NULL, |post_fork_parent_callback| will run in the parent almost
+// immediately after fork().
+// Since this function calls fork(), it's very important that the caller has
+// only one thread running.
+bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback);
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.cc b/sandbox/linux/suid/client/setuid_sandbox_client.cc
index 34231d4..740823a 100644
--- a/sandbox/linux/suid/client/setuid_sandbox_client.cc
+++ b/sandbox/linux/suid/client/setuid_sandbox_client.cc
@@ -12,6 +12,7 @@
#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_number_conversions.h"
+#include "sandbox/linux/services/init_process_reaper.h"
#include "sandbox/linux/suid/common/sandbox.h"
#include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h"
#include "setuid_sandbox_client.h"
@@ -150,6 +151,11 @@
return true;
}
+bool SetuidSandboxClient::CreateInitProcessReaper(
+ base::Closure* post_fork_parent_callback) {
+ return sandbox::CreateInitProcessReaper(post_fork_parent_callback);
+}
+
bool SetuidSandboxClient::IsSuidSandboxUpToDate() const {
return GetHelperApi(env_) == kSUIDSandboxApiNumber;
}
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.h b/sandbox/linux/suid/client/setuid_sandbox_client.h
index a9f6536..5a6724d 100644
--- a/sandbox/linux/suid/client/setuid_sandbox_client.h
+++ b/sandbox/linux/suid/client/setuid_sandbox_client.h
@@ -6,6 +6,7 @@
#define SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
#include "base/basictypes.h"
+#include "base/callback_forward.h"
namespace base { class Environment; }
@@ -30,6 +31,11 @@
// to an empty directory.
// Will only work if we have been launched through the setuid helper.
bool ChrootMe();
+ // When a new PID namespace is created, the process with pid == 1 should
+ // assume the role of init.
+ // See sandbox/linux/services/init_process_reaper.h for more information
+ // on this API.
+ bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback);
// Did we get launched through an up to date setuid binary ?
bool IsSuidSandboxUpToDate() const;
diff --git a/sandbox/linux/tests/main.cc b/sandbox/linux/tests/main.cc
index 8142545..8fd85d9 100644
--- a/sandbox/linux/tests/main.cc
+++ b/sandbox/linux/tests/main.cc
@@ -2,9 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/at_exit.h"
#include "testing/gtest/include/gtest/gtest.h"
-int main(int argc, char *argv[]) {
+int main(int argc, char* argv[]) {
+ // The use of Callbacks requires an AtExitManager.
+ base::AtExitManager exit_manager;
testing::InitGoogleTest(&argc, argv);
// Always go through re-execution for death tests.
// This makes gtest only marginally slower for us and has the
diff --git a/sandbox/linux/tests/unit_tests.cc b/sandbox/linux/tests/unit_tests.cc
index 02996b7..320f52b 100644
--- a/sandbox/linux/tests/unit_tests.cc
+++ b/sandbox/linux/tests/unit_tests.cc
@@ -59,9 +59,7 @@
// TODO(jln): figure out why base/.../dynamic_annotations.h's
// RunningOnValgrind() cannot link.
-bool IsRunningOnValgrind() {
- return RUNNING_ON_VALGRIND;
-}
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
static const int kExpectedValue = 42;
static const int kIgnoreThisTest = 43;
@@ -69,13 +67,12 @@
static const int kExitForTimeout = 2;
static void SigAlrmHandler(int) {
- const char failure_message[] = "Timeout reached!\n";
- // Make sure that we never block here.
- if (!fcntl(2, F_SETFL, O_NONBLOCK)) {
- if (write(2, failure_message, sizeof(failure_message) - 1) < 0) {
- }
- }
- _exit(kExitForTimeout);
+ const char failure_message[] = "Timeout reached!\n";
+ // Make sure that we never block here.
+ if (!fcntl(2, F_SETFL, O_NONBLOCK)) {
+ ignore_result(write(2, failure_message, sizeof(failure_message) - 1));
+ }
+ _exit(kExitForTimeout);
}
// Set a timeout with a handler that will automatically fail the
@@ -105,8 +102,10 @@
// in the BPF sandbox, as it potentially makes global state changes and as
// it also tends to raise fatal errors, if the code has been used in an
// insecure manner.
-void UnitTests::RunTestInProcess(UnitTests::Test test, void *arg,
- DeathCheck death, const void *death_aux) {
+void UnitTests::RunTestInProcess(UnitTests::Test test,
+ void* arg,
+ DeathCheck death,
+ const void* death_aux) {
// We need to fork(), so we can't be multi-threaded, as threads could hold
// locks.
int num_threads = CountThreads();
@@ -144,7 +143,7 @@
// Disable core files. They are not very useful for our individual test
// cases.
- struct rlimit no_core = { 0 };
+ struct rlimit no_core = {0};
setrlimit(RLIMIT_CORE, &no_core);
test(arg);
@@ -157,9 +156,9 @@
// Make sure read() will never block as we'll use poll() to
// block with a timeout instead.
- const int fcntl_ret = fcntl(fds[0], F_SETFL, O_NONBLOCK);
+ const int fcntl_ret = fcntl(fds[0], F_SETFL, O_NONBLOCK);
ASSERT_EQ(fcntl_ret, 0);
- struct pollfd poll_fd = { fds[0], POLLIN | POLLRDHUP, 0 };
+ struct pollfd poll_fd = {fds[0], POLLIN | POLLRDHUP, 0};
int poll_ret;
// We prefer the SIGALRM timeout to trigger in the child than this timeout
@@ -198,8 +197,7 @@
}
}
-void UnitTests::DeathSuccess(int status, const std::string& msg,
- const void *) {
+void UnitTests::DeathSuccess(int status, const std::string& msg, const void*) {
std::string details(TestFailedMessage(msg));
bool subprocess_terminated_normally = WIFEXITED(status);
@@ -210,22 +208,24 @@
EXPECT_FALSE(subprocess_exited_but_printed_messages) << details;
}
-void UnitTests::DeathMessage(int status, const std::string& msg,
- const void *aux) {
+void UnitTests::DeathMessage(int status,
+ const std::string& msg,
+ const void* aux) {
std::string details(TestFailedMessage(msg));
- const char *expected_msg = static_cast<const char *>(aux);
+ const char* expected_msg = static_cast<const char*>(aux);
bool subprocess_terminated_normally = WIFEXITED(status);
ASSERT_TRUE(subprocess_terminated_normally) << details;
int subprocess_exit_status = WEXITSTATUS(status);
ASSERT_EQ(kExitWithAssertionFailure, subprocess_exit_status) << details;
bool subprocess_exited_without_matching_message =
- msg.find(expected_msg) == std::string::npos;
+ msg.find(expected_msg) == std::string::npos;
EXPECT_FALSE(subprocess_exited_without_matching_message) << details;
}
-void UnitTests::DeathExitCode(int status, const std::string& msg,
- const void *aux) {
+void UnitTests::DeathExitCode(int status,
+ const std::string& msg,
+ const void* aux) {
int expected_exit_code = static_cast<int>(reinterpret_cast<intptr_t>(aux));
std::string details(TestFailedMessage(msg));
@@ -235,8 +235,9 @@
ASSERT_EQ(subprocess_exit_status, expected_exit_code) << details;
}
-void UnitTests::DeathBySignal(int status, const std::string& msg,
- const void *aux) {
+void UnitTests::DeathBySignal(int status,
+ const std::string& msg,
+ const void* aux) {
int expected_signo = static_cast<int>(reinterpret_cast<intptr_t>(aux));
std::string details(TestFailedMessage(msg));
@@ -246,8 +247,7 @@
ASSERT_EQ(subprocess_signal_number, expected_signo) << details;
}
-void UnitTests::AssertionFailure(const char *expr, const char *file,
- int line) {
+void UnitTests::AssertionFailure(const char* expr, const char* file, int line) {
fprintf(stderr, "%s:%d:%s", file, line, expr);
fflush(stderr);
_exit(kExitWithAssertionFailure);
diff --git a/sandbox/linux/tests/unit_tests.h b/sandbox/linux/tests/unit_tests.h
index c6d1b4d..5480b56 100644
--- a/sandbox/linux/tests/unit_tests.h
+++ b/sandbox/linux/tests/unit_tests.h
@@ -33,59 +33,62 @@
// NOTE: If you do decide to write your own DeathCheck, make sure to use
// gtests's ASSERT_XXX() macros instead of SANDBOX_ASSERT(). See
// unit_tests.cc for examples.
-#define DEATH_SUCCESS() sandbox::UnitTests::DeathSuccess, NULL
-#define DEATH_MESSAGE(msg) sandbox::UnitTests::DeathMessage, \
- static_cast<const void *>( \
- static_cast<const char *>(msg))
-#define DEATH_EXIT_CODE(rc) sandbox::UnitTests::DeathExitCode, \
- reinterpret_cast<void *>(static_cast<intptr_t>(rc))
-#define DEATH_BY_SIGNAL(s) sandbox::UnitTests::DeathExitCode, \
- reinterpret_cast<void *>(static_cast<intptr_t>(s))
+#define DEATH_SUCCESS() sandbox::UnitTests::DeathSuccess, NULL
+#define DEATH_MESSAGE(msg) \
+ sandbox::UnitTests::DeathMessage, \
+ static_cast<const void*>(static_cast<const char*>(msg))
+#define DEATH_EXIT_CODE(rc) \
+ sandbox::UnitTests::DeathExitCode, \
+ reinterpret_cast<void*>(static_cast<intptr_t>(rc))
+#define DEATH_BY_SIGNAL(s) \
+ sandbox::UnitTests::DeathExitCode, \
+ reinterpret_cast<void*>(static_cast<intptr_t>(s))
// A SANDBOX_DEATH_TEST is just like a SANDBOX_TEST (see below), but it assumes
// that the test actually dies. The death test only passes if the death occurs
// in the expected fashion, as specified by "death" and "death_aux". These two
// parameters are typically set to one of the DEATH_XXX() macros.
-#define SANDBOX_DEATH_TEST(test_case_name, test_name, death) \
- void TEST_##test_name(void *); \
- TEST(test_case_name, test_name) { \
- sandbox::UnitTests::RunTestInProcess(TEST_##test_name, NULL, death); \
- } \
- void TEST_##test_name(void *)
+#define SANDBOX_DEATH_TEST(test_case_name, test_name, death) \
+ void TEST_##test_name(void*); \
+ TEST(test_case_name, test_name) { \
+ sandbox::UnitTests::RunTestInProcess(TEST_##test_name, NULL, death); \
+ } \
+ void TEST_##test_name(void*)
// Define a new test case that runs inside of a GTest death test. This is
// necessary, as most of our tests by definition make global and irreversible
// changes to the system (i.e. they install a sandbox). GTest provides death
// tests as a tool to isolate global changes from the rest of the tests.
-#define SANDBOX_TEST(test_case_name, test_name) \
+#define SANDBOX_TEST(test_case_name, test_name) \
SANDBOX_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS())
// Simple assertion macro that is compatible with running inside of a death
// test. We unfortunately cannot use any of the GTest macros.
#define SANDBOX_STR(x) #x
-#define SANDBOX_ASSERT(expr) \
- ((expr) \
- ? static_cast<void>(0) \
- : sandbox::UnitTests::AssertionFailure(SANDBOX_STR(expr), \
- __FILE__, __LINE__))
+#define SANDBOX_ASSERT(expr) \
+ ((expr) ? static_cast<void>(0) : sandbox::UnitTests::AssertionFailure( \
+ SANDBOX_STR(expr), __FILE__, __LINE__))
class UnitTests {
public:
- typedef void (*Test)(void *);
- typedef void (*DeathCheck)(int status, const std::string& msg,
- const void *aux);
+ typedef void (*Test)(void*);
+ typedef void (*DeathCheck)(int status,
+ const std::string& msg,
+ const void* aux);
// Runs a test inside a short-lived process. Do not call this function
// directly. It is automatically invoked by SANDBOX_TEST(). Most sandboxing
// functions make global irreversible changes to the execution environment
// and must therefore execute in their own isolated process.
- static void RunTestInProcess(Test test, void *arg, DeathCheck death,
- const void *death_aux);
+ static void RunTestInProcess(Test test,
+ void* arg,
+ DeathCheck death,
+ const void* death_aux);
// Report a useful error message and terminate the current SANDBOX_TEST().
// Calling this function from outside a SANDBOX_TEST() is unlikely to do
// anything useful.
- static void AssertionFailure(const char *expr, const char *file, int line);
+ static void AssertionFailure(const char* expr, const char* file, int line);
// Sometimes we determine at run-time that a test should be disabled.
// Call this method if we want to return from a test and completely
@@ -98,29 +101,30 @@
// A DeathCheck method that verifies that the test completed succcessfully.
// This is the default test mode for SANDBOX_TEST(). The "aux" parameter
// of this DeathCheck is unused (and thus unnamed)
- static void DeathSuccess(int status, const std::string& msg, const void *);
+ static void DeathSuccess(int status, const std::string& msg, const void*);
// A DeathCheck method that verifies that the test completed with error
// code "1" and printed a message containing a particular substring. The
// "aux" pointer should point to a C-string containing the expected error
// message. This method is useful for checking assertion failures such as
// in SANDBOX_ASSERT() and/or SANDBOX_DIE().
- static void DeathMessage(int status, const std::string& msg,
- const void *aux);
+ static void DeathMessage(int status, const std::string& msg, const void* aux);
// A DeathCheck method that verifies that the test completed with a
// particular exit code. If the test output any messages to stderr, they are
// silently ignored. The expected exit code should be passed in by
// casting the its "int" value to a "void *", which is then used for "aux".
- static void DeathExitCode(int status, const std::string& msg,
- const void *aux);
+ static void DeathExitCode(int status,
+ const std::string& msg,
+ const void* aux);
// A DeathCheck method that verifies that the test was terminated by a
// particular signal. If the test output any messages to stderr, they are
// silently ignore. The expected signal number should be passed in by
// casting the its "int" value to a "void *", which is then used for "aux".
- static void DeathBySignal(int status, const std::string& msg,
- const void *aux);
+ static void DeathBySignal(int status,
+ const std::string& msg,
+ const void* aux);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(UnitTests);
diff --git a/sandbox/sandbox_services.target.darwin-arm.mk b/sandbox/sandbox_services.target.darwin-arm.mk
index 48e606a..8d0d355 100644
--- a/sandbox/sandbox_services.target.darwin-arm.mk
+++ b/sandbox/sandbox_services.target.darwin-arm.mk
@@ -23,7 +23,8 @@
GYP_COPIED_SOURCE_ORIGIN_DIRS :=
LOCAL_SRC_FILES := \
- sandbox/linux/services/broker_process.cc
+ sandbox/linux/services/broker_process.cc \
+ sandbox/linux/services/init_process_reaper.cc
# Flags passed to both C and C++ files.
@@ -73,6 +74,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -154,6 +156,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/sandbox_services.target.darwin-mips.mk b/sandbox/sandbox_services.target.darwin-mips.mk
index 4b72b57..6eaebbb 100644
--- a/sandbox/sandbox_services.target.darwin-mips.mk
+++ b/sandbox/sandbox_services.target.darwin-mips.mk
@@ -23,7 +23,8 @@
GYP_COPIED_SOURCE_ORIGIN_DIRS :=
LOCAL_SRC_FILES := \
- sandbox/linux/services/broker_process.cc
+ sandbox/linux/services/broker_process.cc \
+ sandbox/linux/services/init_process_reaper.cc
# Flags passed to both C and C++ files.
@@ -72,6 +73,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -152,6 +154,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/sandbox_services.target.darwin-x86.mk b/sandbox/sandbox_services.target.darwin-x86.mk
index 5ee4316..4c410bb 100644
--- a/sandbox/sandbox_services.target.darwin-x86.mk
+++ b/sandbox/sandbox_services.target.darwin-x86.mk
@@ -23,7 +23,8 @@
GYP_COPIED_SOURCE_ORIGIN_DIRS :=
LOCAL_SRC_FILES := \
- sandbox/linux/services/broker_process.cc
+ sandbox/linux/services/broker_process.cc \
+ sandbox/linux/services/init_process_reaper.cc
# Flags passed to both C and C++ files.
@@ -75,6 +76,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -159,6 +161,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/sandbox_services.target.linux-arm.mk b/sandbox/sandbox_services.target.linux-arm.mk
index 48e606a..8d0d355 100644
--- a/sandbox/sandbox_services.target.linux-arm.mk
+++ b/sandbox/sandbox_services.target.linux-arm.mk
@@ -23,7 +23,8 @@
GYP_COPIED_SOURCE_ORIGIN_DIRS :=
LOCAL_SRC_FILES := \
- sandbox/linux/services/broker_process.cc
+ sandbox/linux/services/broker_process.cc \
+ sandbox/linux/services/init_process_reaper.cc
# Flags passed to both C and C++ files.
@@ -73,6 +74,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -154,6 +156,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/sandbox_services.target.linux-mips.mk b/sandbox/sandbox_services.target.linux-mips.mk
index 4b72b57..6eaebbb 100644
--- a/sandbox/sandbox_services.target.linux-mips.mk
+++ b/sandbox/sandbox_services.target.linux-mips.mk
@@ -23,7 +23,8 @@
GYP_COPIED_SOURCE_ORIGIN_DIRS :=
LOCAL_SRC_FILES := \
- sandbox/linux/services/broker_process.cc
+ sandbox/linux/services/broker_process.cc \
+ sandbox/linux/services/init_process_reaper.cc
# Flags passed to both C and C++ files.
@@ -72,6 +73,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -152,6 +154,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/sandbox_services.target.linux-x86.mk b/sandbox/sandbox_services.target.linux-x86.mk
index 5ee4316..4c410bb 100644
--- a/sandbox/sandbox_services.target.linux-x86.mk
+++ b/sandbox/sandbox_services.target.linux-x86.mk
@@ -23,7 +23,8 @@
GYP_COPIED_SOURCE_ORIGIN_DIRS :=
LOCAL_SRC_FILES := \
- sandbox/linux/services/broker_process.cc
+ sandbox/linux/services/broker_process.cc \
+ sandbox/linux/services/init_process_reaper.cc
# Flags passed to both C and C++ files.
@@ -75,6 +76,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -159,6 +161,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/sandbox_services_headers.target.darwin-arm.mk b/sandbox/sandbox_services_headers.target.darwin-arm.mk
index c98bec3..f02be39 100644
--- a/sandbox/sandbox_services_headers.target.darwin-arm.mk
+++ b/sandbox/sandbox_services_headers.target.darwin-arm.mk
@@ -72,6 +72,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -151,6 +152,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/sandbox_services_headers.target.darwin-x86.mk b/sandbox/sandbox_services_headers.target.darwin-x86.mk
index 5d24e9e..099b3cb 100644
--- a/sandbox/sandbox_services_headers.target.darwin-x86.mk
+++ b/sandbox/sandbox_services_headers.target.darwin-x86.mk
@@ -74,6 +74,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -156,6 +157,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/sandbox_services_headers.target.linux-arm.mk b/sandbox/sandbox_services_headers.target.linux-arm.mk
index c98bec3..f02be39 100644
--- a/sandbox/sandbox_services_headers.target.linux-arm.mk
+++ b/sandbox/sandbox_services_headers.target.linux-arm.mk
@@ -72,6 +72,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -151,6 +152,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/sandbox_services_headers.target.linux-x86.mk b/sandbox/sandbox_services_headers.target.linux-x86.mk
index 5d24e9e..099b3cb 100644
--- a/sandbox/sandbox_services_headers.target.linux-x86.mk
+++ b/sandbox/sandbox_services_headers.target.linux-x86.mk
@@ -74,6 +74,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -156,6 +157,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/seccomp_bpf.target.darwin-arm.mk b/sandbox/seccomp_bpf.target.darwin-arm.mk
index 3777c9d..c18bcaf 100644
--- a/sandbox/seccomp_bpf.target.darwin-arm.mk
+++ b/sandbox/seccomp_bpf.target.darwin-arm.mk
@@ -82,6 +82,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -162,6 +163,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/seccomp_bpf.target.darwin-x86.mk b/sandbox/seccomp_bpf.target.darwin-x86.mk
index 037dd95..26e021c 100644
--- a/sandbox/seccomp_bpf.target.darwin-x86.mk
+++ b/sandbox/seccomp_bpf.target.darwin-x86.mk
@@ -84,6 +84,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -167,6 +168,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/seccomp_bpf.target.linux-arm.mk b/sandbox/seccomp_bpf.target.linux-arm.mk
index 3777c9d..c18bcaf 100644
--- a/sandbox/seccomp_bpf.target.linux-arm.mk
+++ b/sandbox/seccomp_bpf.target.linux-arm.mk
@@ -82,6 +82,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -162,6 +163,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/seccomp_bpf.target.linux-x86.mk b/sandbox/seccomp_bpf.target.linux-x86.mk
index 037dd95..26e021c 100644
--- a/sandbox/seccomp_bpf.target.linux-x86.mk
+++ b/sandbox/seccomp_bpf.target.linux-x86.mk
@@ -84,6 +84,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
@@ -167,6 +168,7 @@
'-DUSE_OPENSSL=1' \
'-DENABLE_EGLIMAGE=1' \
'-DCLD_VERSION=1' \
+ '-DENABLE_MANAGED_USERS=1' \
'-D__STDC_CONSTANT_MACROS' \
'-D__STDC_FORMAT_MACROS' \
'-DANDROID' \
diff --git a/sandbox/win/src/Wow64.cc b/sandbox/win/src/Wow64.cc
index 39108e5..a710d75 100644
--- a/sandbox/win/src/Wow64.cc
+++ b/sandbox/win/src/Wow64.cc
@@ -157,10 +157,11 @@
STARTUPINFO startup_info = {0};
startup_info.cb = sizeof(startup_info);
- base::win::ScopedProcessInformation process_info;
+ PROCESS_INFORMATION temp_process_info = {};
if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL,
- NULL, &startup_info, process_info.Receive()))
+ NULL, &startup_info, &temp_process_info))
return false;
+ base::win::ScopedProcessInformation process_info(temp_process_info);
DWORD reason = ::WaitForSingleObject(process_info.process_handle(), INFINITE);
diff --git a/sandbox/win/src/job_unittest.cc b/sandbox/win/src/job_unittest.cc
index 597c86c..3b2b331 100644
--- a/sandbox/win/src/job_unittest.cc
+++ b/sandbox/win/src/job_unittest.cc
@@ -164,10 +164,11 @@
wchar_t notepad[] = L"notepad";
STARTUPINFO si = { sizeof(si) };
- base::win::ScopedProcessInformation pi;
+ PROCESS_INFORMATION temp_process_info = {};
result = ::CreateProcess(NULL, notepad, NULL, NULL, FALSE, 0, NULL, NULL, &si,
- pi.Receive());
+ &temp_process_info);
ASSERT_TRUE(result);
+ base::win::ScopedProcessInformation pi(temp_process_info);
ASSERT_EQ(ERROR_SUCCESS, job.AssignProcessToJob(pi.process_handle()));
// Get the job handle.
diff --git a/sandbox/win/src/policy_target_test.cc b/sandbox/win/src/policy_target_test.cc
index ee28260..1e29df2 100644
--- a/sandbox/win/src/policy_target_test.cc
+++ b/sandbox/win/src/policy_target_test.cc
@@ -150,10 +150,11 @@
// Use default values to create a new process.
STARTUPINFO startup_info = {0};
startup_info.cb = sizeof(startup_info);
- base::win::ScopedProcessInformation process_info;
+ PROCESS_INFORMATION temp_process_info = {};
if (!::CreateProcessW(L"foo.exe", L"foo.exe", NULL, NULL, FALSE, 0,
- NULL, NULL, &startup_info, process_info.Receive()))
+ NULL, NULL, &startup_info, &temp_process_info))
return SBOX_TEST_SUCCEEDED;
+ base::win::ScopedProcessInformation process_info(temp_process_info);
return SBOX_TEST_FAILED;
}
@@ -239,11 +240,14 @@
TargetPolicy* policy = broker->CreatePolicy();
policy->SetAlternateDesktop(false);
policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ PROCESS_INFORMATION temp_process_info = {};
result = broker->SpawnTarget(prog_name, arguments.c_str(), policy,
- target.Receive());
+ &temp_process_info);
policy->Release();
EXPECT_EQ(SBOX_ALL_OK, result);
+ if (result == SBOX_ALL_OK)
+ target.Set(temp_process_info);
EXPECT_EQ(1, ::ResumeThread(target.thread_handle()));
@@ -299,11 +303,14 @@
TargetPolicy* policy = broker->CreatePolicy();
policy->SetAlternateDesktop(true);
policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ PROCESS_INFORMATION temp_process_info = {};
result = broker->SpawnTarget(prog_name, arguments.c_str(), policy,
- target.Receive());
+ &temp_process_info);
policy->Release();
EXPECT_EQ(SBOX_ALL_OK, result);
+ if (result == SBOX_ALL_OK)
+ target.Set(temp_process_info);
EXPECT_EQ(1, ::ResumeThread(target.thread_handle()));
diff --git a/sandbox/win/src/process_policy_test.cc b/sandbox/win/src/process_policy_test.cc
index babe321..a03e0be 100644
--- a/sandbox/win/src/process_policy_test.cc
+++ b/sandbox/win/src/process_policy_test.cc
@@ -50,8 +50,12 @@
// Create the process with the unicode version of the API.
sandbox::SboxTestResult ret1 = sandbox::SBOX_TEST_FAILED;
- if (!::CreateProcessW(exe_name, const_cast<wchar_t*>(cmd_line), NULL, NULL,
- FALSE, 0, NULL, NULL, &si, pi.Receive())) {
+ PROCESS_INFORMATION temp_process_info = {};
+ if (::CreateProcessW(exe_name, const_cast<wchar_t*>(cmd_line), NULL, NULL,
+ FALSE, 0, NULL, NULL, &si, &temp_process_info)) {
+ pi.Set(temp_process_info);
+ ret1 = sandbox::SBOX_TEST_SUCCEEDED;
+ } else {
DWORD last_error = GetLastError();
if ((ERROR_NOT_ENOUGH_QUOTA == last_error) ||
(ERROR_ACCESS_DENIED == last_error) ||
@@ -60,8 +64,6 @@
} else {
ret1 = sandbox::SBOX_TEST_FAILED;
}
- } else {
- ret1 = sandbox::SBOX_TEST_SUCCEEDED;
}
pi.Close();
@@ -73,10 +75,13 @@
std::string narrow_cmd_line;
if (cmd_line)
narrow_cmd_line = base::SysWideToMultiByte(cmd_line, CP_UTF8);
- if (!::CreateProcessA(
+ if (::CreateProcessA(
exe_name ? base::SysWideToMultiByte(exe_name, CP_UTF8).c_str() : NULL,
cmd_line ? const_cast<char*>(narrow_cmd_line.c_str()) : NULL,
- NULL, NULL, FALSE, 0, NULL, NULL, &sia, pi.Receive())) {
+ NULL, NULL, FALSE, 0, NULL, NULL, &sia, &temp_process_info)) {
+ pi.Set(temp_process_info);
+ ret2 = sandbox::SBOX_TEST_SUCCEEDED;
+ } else {
DWORD last_error = GetLastError();
if ((ERROR_NOT_ENOUGH_QUOTA == last_error) ||
(ERROR_ACCESS_DENIED == last_error) ||
@@ -85,8 +90,6 @@
} else {
ret2 = sandbox::SBOX_TEST_FAILED;
}
- } else {
- ret2 = sandbox::SBOX_TEST_SUCCEEDED;
}
if (ret1 == ret2)
@@ -215,13 +218,14 @@
string16 path = MakeFullPathToSystem32(argv[0]);
- base::win::ScopedProcessInformation pi;
STARTUPINFOW si = {sizeof(si)};
+ PROCESS_INFORMATION temp_process_info = {};
if (!::CreateProcessW(path.c_str(), NULL, NULL, NULL, FALSE, CREATE_SUSPENDED,
- NULL, NULL, &si, pi.Receive())) {
+ NULL, NULL, &si, &temp_process_info)) {
return SBOX_TEST_FAILED;
}
+ base::win::ScopedProcessInformation pi(temp_process_info);
HANDLE token = NULL;
BOOL result =
diff --git a/sandbox/win/src/restricted_token_utils.cc b/sandbox/win/src/restricted_token_utils.cc
index 5f1a246..f30a8a6 100644
--- a/sandbox/win/src/restricted_token_utils.cc
+++ b/sandbox/win/src/restricted_token_utils.cc
@@ -183,7 +183,7 @@
// Start the process
STARTUPINFO startup_info = {0};
- base::win::ScopedProcessInformation process_info;
+ PROCESS_INFORMATION temp_process_info = {};
DWORD flags = CREATE_SUSPENDED;
if (base::win::GetVersion() < base::win::VERSION_WIN8) {
@@ -202,9 +202,10 @@
NULL, // Use the environment of the caller.
NULL, // Use current directory of the caller.
&startup_info,
- process_info.Receive())) {
+ &temp_process_info)) {
return ::GetLastError();
}
+ base::win::ScopedProcessInformation process_info(temp_process_info);
// Change the token of the main thread of the new process for the
// impersonation token with more rights.
diff --git a/sandbox/win/src/target_process.cc b/sandbox/win/src/target_process.cc
index 9300cce..a2d630c 100644
--- a/sandbox/win/src/target_process.cc
+++ b/sandbox/win/src/target_process.cc
@@ -131,8 +131,7 @@
flags |= CREATE_BREAKAWAY_FROM_JOB;
}
- base::win::ScopedProcessInformation process_info;
-
+ PROCESS_INFORMATION temp_process_info = {};
if (!::CreateProcessAsUserW(lockdown_token_,
exe_path,
cmd_line.get(),
@@ -143,9 +142,10 @@
NULL, // Use the environment of the caller.
NULL, // Use current directory of the caller.
startup_info.startup_info(),
- process_info.Receive())) {
+ &temp_process_info)) {
return ::GetLastError();
}
+ base::win::ScopedProcessInformation process_info(temp_process_info);
lockdown_token_.Close();
DWORD win_result = ERROR_SUCCESS;