Chromium Code Reviews| Index: sandbox/linux/seccomp-bpf/verifier.cc |
| diff --git a/sandbox/linux/seccomp-bpf/verifier.cc b/sandbox/linux/seccomp-bpf/verifier.cc |
| index 0863556e0808e7e28d4805b0245dda7fa390bdd3..3e12586549552db84cb038dfc8ced42c116d7996 100644 |
| --- a/sandbox/linux/seccomp-bpf/verifier.cc |
| +++ b/sandbox/linux/seccomp-bpf/verifier.cc |
| @@ -4,16 +4,21 @@ |
| #include <string.h> |
| +#include <limits> |
| + |
| #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 sandbox { |
| namespace { |
| +const uint64_t kLower32Bits = std::numeric_limits<uint32_t>::max(); |
| +const uint64_t kUpper32Bits = static_cast<uint64_t>(kLower32Bits) << 32; |
| +const uint64_t kFull64Bits = std::numeric_limits<uint64_t>::max(); |
| + |
| struct State { |
| State(const std::vector<struct sock_filter>& p, |
| const struct arch_seccomp_data& d) |
| @@ -41,36 +46,9 @@ uint32_t EvaluateErrorCode(SandboxBPF* sandbox, |
| 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; |
| - } |
| + bool equal = (data.args[code.argno()] & code.mask()) == code.value(); |
| + return EvaluateErrorCode( |
| + sandbox, equal ? *code.passed() : *code.failed(), data); |
| } else { |
| return SECCOMP_RET_INVALID; |
| } |
| @@ -103,114 +81,77 @@ bool VerifyErrorCode(SandboxBPF* sandbox, |
| *err = "Invalid argument number in error code"; |
| 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)) { |
| - 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; |
| - } |
| + // Verify that we can check a value for simple equality. |
| + data->args[code.argno()] = code.value(); |
| + if (!VerifyErrorCode( |
| + sandbox, program, data, root_code, *code.passed(), 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 mask ignores any bits, verify that setting those bits is still |
| + // detected as equality. |
| + uint64_t ignored_bits = ~code.mask(); |
|
jln (very slow on Chromium)
2014/09/04 02:08:36
Maybe it would add a little coverage to:
1. Doing
mdempsky
2014/09/04 17:26:48
The unfortunate thing is that this runs recursivel
|
| + if (code.width() == ErrorCode::TP_32BIT) { |
| + ignored_bits = static_cast<uint32_t>(ignored_bits); |
| + } |
| + if ((ignored_bits & kLower32Bits) != 0) { |
| + data->args[code.argno()] = code.value() | (ignored_bits & kLower32Bits); |
| + if (!VerifyErrorCode( |
| + sandbox, program, data, root_code, *code.passed(), err)) { |
| + return false; |
| + } |
| + } |
| + if ((ignored_bits & kUpper32Bits) != 0) { |
| + data->args[code.argno()] = code.value() | (ignored_bits & kUpper32Bits); |
| + if (!VerifyErrorCode( |
| + sandbox, program, data, root_code, *code.passed(), err)) { |
| + return 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) || |
| + // Verify that changing bits included in the mask is detected as inequality. |
| + if ((code.mask() & kLower32Bits) != 0) { |
| + data->args[code.argno()] = code.value() ^ (code.mask() & kLower32Bits); |
| + if (!VerifyErrorCode( |
| + sandbox, program, data, root_code, *code.failed(), err)) { |
| + return false; |
| + } |
| + } |
| + if ((code.mask() & kUpper32Bits) != 0) { |
| + data->args[code.argno()] = code.value() ^ (code.mask() & kUpper32Bits); |
| + if (!VerifyErrorCode( |
| + sandbox, program, data, root_code, *code.failed(), err)) { |
| + return false; |
| + } |
| + } |
| - // 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(); |
| + if (code.width() == ErrorCode::TP_32BIT) { |
| + // For 32-bit system call arguments, we emit additional instructions to |
| + // validate the upper 32-bits. Here we test that validation. |
|
jln (very slow on Chromium)
2014/09/04 02:08:36
Nit: there is an extra space in front of "Here".
mdempsky
2014/09/04 17:26:48
Done. (I generally use two spaces between sentence
|
| - // 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(); |
| + // Arbitrary 64-bit values should be rejected. |
| + data->args[code.argno()] = 1ULL << 32; |
| + if (!VerifyErrorCode(sandbox, |
| + program, |
| + data, |
| + root_code, |
| + sandbox->Unexpected64bitArgument(), |
| + err)) { |
| + return false; |
| + } |
| - 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"; |
| + // Upper 32-bits set without the MSB of the lower 32-bits set should be |
| + // rejected too. |
| + data->args[code.argno()] = kUpper32Bits; |
| + if (!VerifyErrorCode(sandbox, |
| + program, |
| + data, |
| + root_code, |
| + sandbox->Unexpected64bitArgument(), |
| + err)) { |
| return false; |
| + } |
| } |
| } else { |
| *err = "Attempting to return invalid error code from BPF program"; |