Commit 2e58094a authored by mdempsky's avatar mdempsky Committed by Commit bot

SyscallIterator: support C++11 range-based for loops

Also remove the superfluous "to" field in PolicyCompiler::Range,
and fix the next-syscall-number calculation so we can remove the
loop in SyscallIterator::operator++() (nee SyscallIterator::Next()).

BUG=414363

Review URL: https://codereview.chromium.org/659723002

Cr-Commit-Position: refs/heads/master@{#300633}
parent 558a1c9d
...@@ -76,9 +76,8 @@ intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) { ...@@ -76,9 +76,8 @@ intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) {
} }
bool HasUnsafeTraps(const SandboxBPFDSLPolicy* policy) { bool HasUnsafeTraps(const SandboxBPFDSLPolicy* policy) {
for (SyscallIterator iter(false); !iter.Done();) { for (uint32_t sysnum : SyscallSet::All()) {
uint32_t sysnum = iter.Next(); if (SyscallSet::IsValid(sysnum) &&
if (SyscallIterator::IsValid(sysnum) &&
policy->EvaluateSyscall(sysnum)->HasUnsafeTraps()) { policy->EvaluateSyscall(sysnum)->HasUnsafeTraps()) {
return true; return true;
} }
...@@ -89,8 +88,8 @@ bool HasUnsafeTraps(const SandboxBPFDSLPolicy* policy) { ...@@ -89,8 +88,8 @@ bool HasUnsafeTraps(const SandboxBPFDSLPolicy* policy) {
} // namespace } // namespace
struct PolicyCompiler::Range { struct PolicyCompiler::Range {
Range(uint32_t f, uint32_t t, const ErrorCode& e) : from(f), to(t), err(e) {} Range(uint32_t f, const ErrorCode& e) : from(f), err(e) {}
uint32_t from, to; uint32_t from;
ErrorCode err; ErrorCode err;
}; };
...@@ -252,22 +251,22 @@ void PolicyCompiler::FindRanges(Ranges* ranges) { ...@@ -252,22 +251,22 @@ void PolicyCompiler::FindRanges(Ranges* ranges) {
// negative) all return the same ErrorCode. // negative) all return the same ErrorCode.
const ErrorCode invalid_err = policy_->InvalidSyscall()->Compile(this); const ErrorCode invalid_err = policy_->InvalidSyscall()->Compile(this);
uint32_t old_sysnum = 0; uint32_t old_sysnum = 0;
ErrorCode old_err = SyscallIterator::IsValid(old_sysnum) ErrorCode old_err = SyscallSet::IsValid(old_sysnum)
? policy_->EvaluateSyscall(old_sysnum)->Compile(this) ? policy_->EvaluateSyscall(old_sysnum)->Compile(this)
: invalid_err; : invalid_err;
for (SyscallIterator iter(false); !iter.Done();) { for (uint32_t sysnum : SyscallSet::All()) {
uint32_t sysnum = iter.Next();
ErrorCode err = ErrorCode err =
SyscallIterator::IsValid(sysnum) SyscallSet::IsValid(sysnum)
? policy_->EvaluateSyscall(static_cast<int>(sysnum))->Compile(this) ? policy_->EvaluateSyscall(static_cast<int>(sysnum))->Compile(this)
: invalid_err; : invalid_err;
if (!err.Equals(old_err) || iter.Done()) { if (!err.Equals(old_err)) {
ranges->push_back(Range(old_sysnum, sysnum - 1, old_err)); ranges->push_back(Range(old_sysnum, old_err));
old_sysnum = sysnum; old_sysnum = sysnum;
old_err = err; old_err = err;
} }
} }
ranges->push_back(Range(old_sysnum, old_err));
} }
Instruction* PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start, Instruction* PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start,
......
...@@ -150,7 +150,7 @@ SandboxBPF::~SandboxBPF() { ...@@ -150,7 +150,7 @@ SandboxBPF::~SandboxBPF() {
} }
bool SandboxBPF::IsValidSyscallNumber(int sysnum) { bool SandboxBPF::IsValidSyscallNumber(int sysnum) {
return SyscallIterator::IsValid(sysnum); return SyscallSet::IsValid(sysnum);
} }
bool SandboxBPF::RunFunctionInPolicy( bool SandboxBPF::RunFunctionInPolicy(
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "sandbox/linux/seccomp-bpf/syscall_iterator.h" #include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
#include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" #include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
...@@ -38,14 +39,16 @@ const SyscallRange kValidSyscallRanges[] = { ...@@ -38,14 +39,16 @@ const SyscallRange kValidSyscallRanges[] = {
#endif #endif
}; };
// NextSyscall returns the next system call in the specified system
// call set after |cur|, or 0 if no such system call exists.
uint32_t NextSyscall(uint32_t cur, bool invalid_only) { uint32_t NextSyscall(uint32_t cur, bool invalid_only) {
for (const SyscallRange& range : kValidSyscallRanges) { for (const SyscallRange& range : kValidSyscallRanges) {
if (range.first > 0 && cur < range.first - 1) { if (range.first > 0 && cur < range.first - 1) {
return range.first - 1; return range.first - 1;
} }
if (cur <= range.last) { if (cur <= range.last) {
if (invalid_only && cur < range.last) { if (invalid_only) {
return range.last; return range.last + 1;
} }
return cur + 1; return cur + 1;
} }
...@@ -62,27 +65,22 @@ uint32_t NextSyscall(uint32_t cur, bool invalid_only) { ...@@ -62,27 +65,22 @@ uint32_t NextSyscall(uint32_t cur, bool invalid_only) {
if (cur < 0x80000000u) if (cur < 0x80000000u)
return 0x80000000u; return 0x80000000u;
return 0xFFFFFFFFu; if (cur < 0xFFFFFFFFu)
return 0xFFFFFFFFu;
return 0;
} }
} // namespace } // namespace
uint32_t SyscallIterator::Next() { SyscallSet::Iterator SyscallSet::begin() const {
if (done_) { return Iterator(set_, false);
return num_; }
}
uint32_t val;
do {
val = num_;
num_ = NextSyscall(num_, invalid_only_);
} while (invalid_only_ && IsValid(val));
done_ |= val == 0xFFFFFFFFu; SyscallSet::Iterator SyscallSet::end() const {
return val; return Iterator(set_, true);
} }
bool SyscallIterator::IsValid(uint32_t num) { bool SyscallSet::IsValid(uint32_t num) {
for (const SyscallRange& range : kValidSyscallRanges) { for (const SyscallRange& range : kValidSyscallRanges) {
if (num >= range.first && num <= range.last) { if (num >= range.first && num <= range.last) {
return true; return true;
...@@ -91,4 +89,42 @@ bool SyscallIterator::IsValid(uint32_t num) { ...@@ -91,4 +89,42 @@ bool SyscallIterator::IsValid(uint32_t num) {
return false; return false;
} }
bool operator==(const SyscallSet& lhs, const SyscallSet& rhs) {
return (lhs.set_ == rhs.set_);
}
SyscallSet::Iterator::Iterator(Set set, bool done)
: set_(set), done_(done), num_(0) {
if (set_ == Set::INVALID_ONLY && !done_ && IsValid(num_)) {
++*this;
}
}
uint32_t SyscallSet::Iterator::operator*() const {
DCHECK(!done_);
return num_;
}
SyscallSet::Iterator& SyscallSet::Iterator::operator++() {
DCHECK(!done_);
num_ = NextSyscall(num_, set_ == Set::INVALID_ONLY);
if (num_ == 0) {
done_ = true;
}
return *this;
}
bool operator==(const SyscallSet::Iterator& lhs,
const SyscallSet::Iterator& rhs) {
DCHECK(lhs.set_ == rhs.set_);
return (lhs.done_ == rhs.done_) && (lhs.num_ == rhs.num_);
}
bool operator!=(const SyscallSet::Iterator& lhs,
const SyscallSet::Iterator& rhs) {
return !(lhs == rhs);
}
} // namespace sandbox } // namespace sandbox
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
namespace sandbox { namespace sandbox {
// TODO(mdempsky): Rename this header to syscall_set.h.
// Iterates over the entire system call range from 0..0xFFFFFFFFu. This // Iterates over the entire system call range from 0..0xFFFFFFFFu. This
// iterator is aware of how system calls look like and will skip quickly // iterator is aware of how system calls look like and will skip quickly
// over ranges that can't contain system calls. It iterates more slowly // over ranges that can't contain system calls. It iterates more slowly
...@@ -20,35 +22,75 @@ namespace sandbox { ...@@ -20,35 +22,75 @@ namespace sandbox {
// first invalid value after a valid range of syscalls. It iterates over // first invalid value after a valid range of syscalls. It iterates over
// individual values whenever it is in the normal range for system calls // individual values whenever it is in the normal range for system calls
// (typically MIN_SYSCALL..MAX_SYSCALL). // (typically MIN_SYSCALL..MAX_SYSCALL).
// If |invalid_only| is true, this iterator will only return invalid
// syscall numbers, but will still skip quickly over invalid ranges,
// returning the first invalid value in the range and then skipping
// to the last invalid value in the range.
// //
// Example usage: // Example usage:
// for (SyscallIterator iter(false); !iter.Done(); ) { // for (uint32_t sysnum : SyscallSet::All()) {
// uint32_t sysnum = iter.Next();
// // Do something with sysnum. // // Do something with sysnum.
// } // }
// class SANDBOX_EXPORT SyscallSet {
// TODO(markus): Make this a classic C++ iterator.
class SANDBOX_EXPORT SyscallIterator {
public: public:
explicit SyscallIterator(bool invalid_only) class Iterator;
: invalid_only_(invalid_only), done_(false), num_(0) {}
SyscallSet(const SyscallSet& ss) : set_(ss.set_) {}
~SyscallSet() {}
Iterator begin() const;
Iterator end() const;
// All returns a SyscallSet that contains both valid and invalid
// system call numbers.
static SyscallSet All() { return SyscallSet(Set::ALL); }
bool Done() const { return done_; } // InvalidOnly returns a SyscallSet that contains only invalid
uint32_t Next(); // system call numbers, but still omits numbers in the middle of a
// range of invalid system call numbers.
static SyscallSet InvalidOnly() { return SyscallSet(Set::INVALID_ONLY); }
// IsValid returns whether |num| specifies a valid system call
// number.
static bool IsValid(uint32_t num); static bool IsValid(uint32_t num);
private: private:
bool invalid_only_; enum class Set { ALL, INVALID_ONLY };
explicit SyscallSet(Set set) : set_(set) {}
Set set_;
friend bool operator==(const SyscallSet&, const SyscallSet&);
DISALLOW_ASSIGN(SyscallSet);
};
SANDBOX_EXPORT bool operator==(const SyscallSet& lhs, const SyscallSet& rhs);
// Iterator provides C++ input iterator semantics for traversing a
// SyscallSet.
class SyscallSet::Iterator {
public:
Iterator(const Iterator& it)
: set_(it.set_), done_(it.done_), num_(it.num_) {}
~Iterator() {}
uint32_t operator*() const;
Iterator& operator++();
private:
Iterator(Set set, bool done);
Set set_;
bool done_; bool done_;
uint32_t num_; uint32_t num_;
DISALLOW_IMPLICIT_CONSTRUCTORS(SyscallIterator); friend SyscallSet;
friend bool operator==(const Iterator&, const Iterator&);
DISALLOW_ASSIGN(Iterator);
}; };
SANDBOX_EXPORT bool operator==(const SyscallSet::Iterator& lhs,
const SyscallSet::Iterator& rhs);
SANDBOX_EXPORT bool operator!=(const SyscallSet::Iterator& lhs,
const SyscallSet::Iterator& rhs);
} // namespace sandbox } // namespace sandbox
#endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__ #endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__
...@@ -13,18 +13,19 @@ namespace sandbox { ...@@ -13,18 +13,19 @@ namespace sandbox {
namespace { namespace {
const bool kFalseTrue[] = {false, true}; const SyscallSet kSyscallSets[] = {
SyscallSet::All(),
SyscallSet::InvalidOnly(),
};
SANDBOX_TEST(SyscallIterator, Monotonous) { SANDBOX_TEST(SyscallIterator, Monotonous) {
for (bool invalid_only : kFalseTrue) { for (const SyscallSet& set : kSyscallSets) {
uint32_t prev = 0; uint32_t prev = 0;
bool have_prev = false; bool have_prev = false;
for (SyscallIterator iter(invalid_only); !iter.Done();) { for (uint32_t sysnum : set) {
uint32_t sysnum = iter.Next();
if (have_prev) { if (have_prev) {
SANDBOX_ASSERT(sysnum > prev); SANDBOX_ASSERT(sysnum > prev);
} else if (!invalid_only) { } else if (set == SyscallSet::All()) {
// The iterator should start at 0. // The iterator should start at 0.
SANDBOX_ASSERT(sysnum == 0); SANDBOX_ASSERT(sysnum == 0);
} }
...@@ -44,8 +45,7 @@ SANDBOX_TEST(SyscallIterator, Monotonous) { ...@@ -44,8 +45,7 @@ SANDBOX_TEST(SyscallIterator, Monotonous) {
void AssertRange(uint32_t min, uint32_t max) { void AssertRange(uint32_t min, uint32_t max) {
SANDBOX_ASSERT(min < max); SANDBOX_ASSERT(min < max);
uint32_t prev = min - 1; uint32_t prev = min - 1;
for (SyscallIterator iter(false); !iter.Done();) { for (uint32_t sysnum : SyscallSet::All()) {
uint32_t sysnum = iter.Next();
if (sysnum >= min && sysnum <= max) { if (sysnum >= min && sysnum <= max) {
SANDBOX_ASSERT(prev == sysnum - 1); SANDBOX_ASSERT(prev == sysnum - 1);
prev = sysnum; prev = sysnum;
...@@ -80,11 +80,10 @@ SANDBOX_TEST(SyscallIterator, InvalidSyscalls) { ...@@ -80,11 +80,10 @@ SANDBOX_TEST(SyscallIterator, InvalidSyscalls) {
0xFFFFFFFFu, 0xFFFFFFFFu,
}; };
for (bool invalid_only : kFalseTrue) { for (const SyscallSet& set : kSyscallSets) {
size_t i = 0; size_t i = 0;
for (SyscallIterator iter(invalid_only); !iter.Done();) { for (uint32_t sysnum : set) {
uint32_t sysnum = iter.Next(); if (!SyscallSet::IsValid(sysnum)) {
if (!SyscallIterator::IsValid(sysnum)) {
SANDBOX_ASSERT(i < arraysize(kExpected)); SANDBOX_ASSERT(i < arraysize(kExpected));
SANDBOX_ASSERT(kExpected[i] == sysnum); SANDBOX_ASSERT(kExpected[i] == sysnum);
++i; ++i;
...@@ -94,6 +93,12 @@ SANDBOX_TEST(SyscallIterator, InvalidSyscalls) { ...@@ -94,6 +93,12 @@ SANDBOX_TEST(SyscallIterator, InvalidSyscalls) {
} }
} }
SANDBOX_TEST(SyscallIterator, InvalidOnlyIsOnlyInvalid) {
for (uint32_t sysnum : SyscallSet::InvalidOnly()) {
SANDBOX_ASSERT(!SyscallSet::IsValid(sysnum));
}
}
} // namespace } // namespace
} // namespace sandbox } // namespace sandbox
...@@ -318,8 +318,7 @@ bool Verifier::VerifyBPF(bpf_dsl::PolicyCompiler* compiler, ...@@ -318,8 +318,7 @@ bool Verifier::VerifyBPF(bpf_dsl::PolicyCompiler* compiler,
const bpf_dsl::SandboxBPFDSLPolicy& policy, const bpf_dsl::SandboxBPFDSLPolicy& policy,
const char** err) { const char** err) {
*err = NULL; *err = NULL;
for (SyscallIterator iter(false); !iter.Done();) { for (uint32_t sysnum : SyscallSet::All()) {
uint32_t sysnum = iter.Next();
// We ideally want to iterate over the full system call range and values // 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 // just above and just below this range. This gives us the full result set
// of the "evaluators". // of the "evaluators".
...@@ -340,7 +339,7 @@ bool Verifier::VerifyBPF(bpf_dsl::PolicyCompiler* compiler, ...@@ -340,7 +339,7 @@ bool Verifier::VerifyBPF(bpf_dsl::PolicyCompiler* compiler,
} }
#endif #endif
#endif #endif
ErrorCode code = iter.IsValid(sysnum) ErrorCode code = SyscallSet::IsValid(sysnum)
? policy.EvaluateSyscall(sysnum)->Compile(compiler) ? policy.EvaluateSyscall(sysnum)->Compile(compiler)
: policy.InvalidSyscall()->Compile(compiler); : policy.InvalidSyscall()->Compile(compiler);
if (!VerifyErrorCode(compiler, program, &data, code, code, err)) { if (!VerifyErrorCode(compiler, program, &data, code, code, err)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment