OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <string.h> | 5 #include <string.h> |
6 | 6 |
| 7 #include <limits> |
| 8 |
7 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" | 9 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" |
8 #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" | 10 #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" |
9 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" | 11 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" |
10 #include "sandbox/linux/seccomp-bpf/verifier.h" | 12 #include "sandbox/linux/seccomp-bpf/verifier.h" |
11 | 13 |
12 | |
13 namespace sandbox { | 14 namespace sandbox { |
14 | 15 |
15 namespace { | 16 namespace { |
16 | 17 |
| 18 const uint64_t kLower32Bits = std::numeric_limits<uint32_t>::max(); |
| 19 const uint64_t kUpper32Bits = static_cast<uint64_t>(kLower32Bits) << 32; |
| 20 const uint64_t kFull64Bits = std::numeric_limits<uint64_t>::max(); |
| 21 |
17 struct State { | 22 struct State { |
18 State(const std::vector<struct sock_filter>& p, | 23 State(const std::vector<struct sock_filter>& p, |
19 const struct arch_seccomp_data& d) | 24 const struct arch_seccomp_data& d) |
20 : program(p), data(d), ip(0), accumulator(0), acc_is_valid(false) {} | 25 : program(p), data(d), ip(0), accumulator(0), acc_is_valid(false) {} |
21 const std::vector<struct sock_filter>& program; | 26 const std::vector<struct sock_filter>& program; |
22 const struct arch_seccomp_data& data; | 27 const struct arch_seccomp_data& data; |
23 unsigned int ip; | 28 unsigned int ip; |
24 uint32_t accumulator; | 29 uint32_t accumulator; |
25 bool acc_is_valid; | 30 bool acc_is_valid; |
26 | 31 |
27 private: | 32 private: |
28 DISALLOW_IMPLICIT_CONSTRUCTORS(State); | 33 DISALLOW_IMPLICIT_CONSTRUCTORS(State); |
29 }; | 34 }; |
30 | 35 |
31 uint32_t EvaluateErrorCode(SandboxBPF* sandbox, | 36 uint32_t EvaluateErrorCode(SandboxBPF* sandbox, |
32 const ErrorCode& code, | 37 const ErrorCode& code, |
33 const struct arch_seccomp_data& data) { | 38 const struct arch_seccomp_data& data) { |
34 if (code.error_type() == ErrorCode::ET_SIMPLE || | 39 if (code.error_type() == ErrorCode::ET_SIMPLE || |
35 code.error_type() == ErrorCode::ET_TRAP) { | 40 code.error_type() == ErrorCode::ET_TRAP) { |
36 return code.err(); | 41 return code.err(); |
37 } else if (code.error_type() == ErrorCode::ET_COND) { | 42 } else if (code.error_type() == ErrorCode::ET_COND) { |
38 if (code.width() == ErrorCode::TP_32BIT && | 43 if (code.width() == ErrorCode::TP_32BIT && |
39 (data.args[code.argno()] >> 32) && | 44 (data.args[code.argno()] >> 32) && |
40 (data.args[code.argno()] & 0xFFFFFFFF80000000ull) != | 45 (data.args[code.argno()] & 0xFFFFFFFF80000000ull) != |
41 0xFFFFFFFF80000000ull) { | 46 0xFFFFFFFF80000000ull) { |
42 return sandbox->Unexpected64bitArgument().err(); | 47 return sandbox->Unexpected64bitArgument().err(); |
43 } | 48 } |
44 switch (code.op()) { | 49 bool equal = (data.args[code.argno()] & code.mask()) == code.value(); |
45 case ErrorCode::OP_EQUAL: | 50 return EvaluateErrorCode( |
46 return EvaluateErrorCode(sandbox, | 51 sandbox, equal ? *code.passed() : *code.failed(), data); |
47 (code.width() == ErrorCode::TP_32BIT | |
48 ? uint32_t(data.args[code.argno()]) | |
49 : data.args[code.argno()]) == code.value() | |
50 ? *code.passed() | |
51 : *code.failed(), | |
52 data); | |
53 case ErrorCode::OP_HAS_ALL_BITS: | |
54 return EvaluateErrorCode(sandbox, | |
55 ((code.width() == ErrorCode::TP_32BIT | |
56 ? uint32_t(data.args[code.argno()]) | |
57 : data.args[code.argno()]) & | |
58 code.value()) == code.value() | |
59 ? *code.passed() | |
60 : *code.failed(), | |
61 data); | |
62 case ErrorCode::OP_HAS_ANY_BITS: | |
63 return EvaluateErrorCode(sandbox, | |
64 (code.width() == ErrorCode::TP_32BIT | |
65 ? uint32_t(data.args[code.argno()]) | |
66 : data.args[code.argno()]) & | |
67 code.value() | |
68 ? *code.passed() | |
69 : *code.failed(), | |
70 data); | |
71 default: | |
72 return SECCOMP_RET_INVALID; | |
73 } | |
74 } else { | 52 } else { |
75 return SECCOMP_RET_INVALID; | 53 return SECCOMP_RET_INVALID; |
76 } | 54 } |
77 } | 55 } |
78 | 56 |
79 bool VerifyErrorCode(SandboxBPF* sandbox, | 57 bool VerifyErrorCode(SandboxBPF* sandbox, |
80 const std::vector<struct sock_filter>& program, | 58 const std::vector<struct sock_filter>& program, |
81 struct arch_seccomp_data* data, | 59 struct arch_seccomp_data* data, |
82 const ErrorCode& root_code, | 60 const ErrorCode& root_code, |
83 const ErrorCode& code, | 61 const ErrorCode& code, |
(...skipping 12 matching lines...) Expand all Loading... |
96 // only way to compute the correct error code in that situation is by | 74 // only way to compute the correct error code in that situation is by |
97 // calling EvaluateErrorCode(). | 75 // calling EvaluateErrorCode(). |
98 *err = "Exit code from BPF program doesn't match"; | 76 *err = "Exit code from BPF program doesn't match"; |
99 return false; | 77 return false; |
100 } | 78 } |
101 } else if (code.error_type() == ErrorCode::ET_COND) { | 79 } else if (code.error_type() == ErrorCode::ET_COND) { |
102 if (code.argno() < 0 || code.argno() >= 6) { | 80 if (code.argno() < 0 || code.argno() >= 6) { |
103 *err = "Invalid argument number in error code"; | 81 *err = "Invalid argument number in error code"; |
104 return false; | 82 return false; |
105 } | 83 } |
106 switch (code.op()) { | |
107 case ErrorCode::OP_EQUAL: | |
108 // Verify that we can check a 32bit value (or the LSB of a 64bit value) | |
109 // for equality. | |
110 data->args[code.argno()] = code.value(); | |
111 if (!VerifyErrorCode( | |
112 sandbox, program, data, root_code, *code.passed(), err)) { | |
113 return false; | |
114 } | |
115 | 84 |
116 // Change the value to no longer match and verify that this is detected | 85 // TODO(mdempsky): The test values generated here try to provide good |
117 // as an inequality. | 86 // coverage for generated BPF instructions while avoiding combinatorial |
118 data->args[code.argno()] = code.value() ^ 0x55AA55AA; | 87 // explosion on large policies. Ideally we would instead take a fuzzing-like |
119 if (!VerifyErrorCode( | 88 // approach and generate a bounded number of test cases regardless of policy |
120 sandbox, program, data, root_code, *code.failed(), err)) { | 89 // size. |
121 return false; | |
122 } | |
123 | 90 |
124 // BPF programs can only ever operate on 32bit values. So, we have | 91 // Verify that we can check a value for simple equality. |
125 // generated additional BPF instructions that inspect the MSB. Verify | 92 data->args[code.argno()] = code.value(); |
126 // that they behave as intended. | 93 if (!VerifyErrorCode( |
127 if (code.width() == ErrorCode::TP_32BIT) { | 94 sandbox, program, data, root_code, *code.passed(), err)) { |
128 if (code.value() >> 32) { | 95 return false; |
129 SANDBOX_DIE( | 96 } |
130 "Invalid comparison of a 32bit system call argument " | |
131 "against a 64bit constant; this test is always false."); | |
132 } | |
133 | 97 |
134 // If the system call argument was intended to be a 32bit parameter, | 98 // If mask ignores any bits, verify that setting those bits is still |
135 // verify that it is a fatal error if a 64bit value is ever passed | 99 // detected as equality. |
136 // here. | 100 uint64_t ignored_bits = ~code.mask(); |
137 data->args[code.argno()] = 0x100000000ull; | 101 if (code.width() == ErrorCode::TP_32BIT) { |
138 if (!VerifyErrorCode(sandbox, | 102 ignored_bits = static_cast<uint32_t>(ignored_bits); |
139 program, | 103 } |
140 data, | 104 if ((ignored_bits & kLower32Bits) != 0) { |
141 root_code, | 105 data->args[code.argno()] = code.value() | (ignored_bits & kLower32Bits); |
142 sandbox->Unexpected64bitArgument(), | 106 if (!VerifyErrorCode( |
143 err)) { | 107 sandbox, program, data, root_code, *code.passed(), err)) { |
144 return false; | 108 return false; |
145 } | 109 } |
146 } else { | 110 } |
147 // If the system call argument was intended to be a 64bit parameter, | 111 if ((ignored_bits & kUpper32Bits) != 0) { |
148 // verify that we can handle (in-)equality for the MSB. This is | 112 data->args[code.argno()] = code.value() | (ignored_bits & kUpper32Bits); |
149 // essentially the same test that we did earlier for the LSB. | 113 if (!VerifyErrorCode( |
150 // We only need to verify the behavior of the inequality test. We | 114 sandbox, program, data, root_code, *code.passed(), err)) { |
151 // know that the equality test already passed, as unlike the kernel | 115 return false; |
152 // the Verifier does operate on 64bit quantities. | 116 } |
153 data->args[code.argno()] = code.value() ^ 0x55AA55AA00000000ull; | 117 } |
154 if (!VerifyErrorCode( | |
155 sandbox, program, data, root_code, *code.failed(), err)) { | |
156 return false; | |
157 } | |
158 } | |
159 break; | |
160 case ErrorCode::OP_HAS_ALL_BITS: | |
161 case ErrorCode::OP_HAS_ANY_BITS: | |
162 // A comprehensive test of bit values is difficult and potentially | |
163 // rather | |
164 // time-expensive. We avoid doing so at run-time and instead rely on the | |
165 // unittest for full testing. The test that we have here covers just the | |
166 // common cases. We test against the bitmask itself, all zeros and all | |
167 // ones. | |
168 { | |
169 // Testing "any" bits against a zero mask is always false. So, there | |
170 // are some cases, where we expect tests to take the "failed()" branch | |
171 // even though this is a test that normally should take "passed()". | |
172 const ErrorCode& passed = | |
173 (!code.value() && code.op() == ErrorCode::OP_HAS_ANY_BITS) || | |
174 | 118 |
175 // On a 32bit system, it is impossible to pass a 64bit | 119 // Verify that changing bits included in the mask is detected as inequality. |
176 // value as a | 120 if ((code.mask() & kLower32Bits) != 0) { |
177 // system call argument. So, some additional tests always | 121 data->args[code.argno()] = code.value() ^ (code.mask() & kLower32Bits); |
178 // evaluate | 122 if (!VerifyErrorCode( |
179 // as false. | 123 sandbox, program, data, root_code, *code.failed(), err)) { |
180 ((code.value() & ~uint64_t(uintptr_t(-1))) && | 124 return false; |
181 code.op() == ErrorCode::OP_HAS_ALL_BITS) || | 125 } |
182 (code.value() && !(code.value() & uintptr_t(-1)) && | 126 } |
183 code.op() == ErrorCode::OP_HAS_ANY_BITS) | 127 if ((code.mask() & kUpper32Bits) != 0) { |
184 ? *code.failed() | 128 data->args[code.argno()] = code.value() ^ (code.mask() & kUpper32Bits); |
185 : *code.passed(); | 129 if (!VerifyErrorCode( |
| 130 sandbox, program, data, root_code, *code.failed(), err)) { |
| 131 return false; |
| 132 } |
| 133 } |
186 | 134 |
187 // Similary, testing for "all" bits in a zero mask is always true. So, | 135 if (code.width() == ErrorCode::TP_32BIT) { |
188 // some cases pass despite them normally failing. | 136 // For 32-bit system call arguments, we emit additional instructions to |
189 const ErrorCode& failed = | 137 // validate the upper 32-bits. Here we test that validation. |
190 !code.value() && code.op() == ErrorCode::OP_HAS_ALL_BITS | |
191 ? *code.passed() | |
192 : *code.failed(); | |
193 | 138 |
194 data->args[code.argno()] = code.value() & uintptr_t(-1); | 139 // Arbitrary 64-bit values should be rejected. |
195 if (!VerifyErrorCode( | 140 data->args[code.argno()] = 1ULL << 32; |
196 sandbox, program, data, root_code, passed, err)) { | 141 if (!VerifyErrorCode(sandbox, |
197 return false; | 142 program, |
198 } | 143 data, |
199 data->args[code.argno()] = uintptr_t(-1); | 144 root_code, |
200 if (!VerifyErrorCode( | 145 sandbox->Unexpected64bitArgument(), |
201 sandbox, program, data, root_code, passed, err)) { | 146 err)) { |
202 return false; | |
203 } | |
204 data->args[code.argno()] = 0; | |
205 if (!VerifyErrorCode( | |
206 sandbox, program, data, root_code, failed, err)) { | |
207 return false; | |
208 } | |
209 } | |
210 break; | |
211 default: // TODO(markus): Need to add support for OP_GREATER | |
212 *err = "Unsupported operation in conditional error code"; | |
213 return false; | 147 return false; |
| 148 } |
| 149 |
| 150 // Upper 32-bits set without the MSB of the lower 32-bits set should be |
| 151 // rejected too. |
| 152 data->args[code.argno()] = kUpper32Bits; |
| 153 if (!VerifyErrorCode(sandbox, |
| 154 program, |
| 155 data, |
| 156 root_code, |
| 157 sandbox->Unexpected64bitArgument(), |
| 158 err)) { |
| 159 return false; |
| 160 } |
214 } | 161 } |
215 } else { | 162 } else { |
216 *err = "Attempting to return invalid error code from BPF program"; | 163 *err = "Attempting to return invalid error code from BPF program"; |
217 return false; | 164 return false; |
218 } | 165 } |
219 return true; | 166 return true; |
220 } | 167 } |
221 | 168 |
222 void Ld(State* state, const struct sock_filter& insn, const char** err) { | 169 void Ld(State* state, const struct sock_filter& insn, const char** err) { |
223 if (BPF_SIZE(insn.code) != BPF_W || BPF_MODE(insn.code) != BPF_ABS) { | 170 if (BPF_SIZE(insn.code) != BPF_W || BPF_MODE(insn.code) != BPF_ABS) { |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
439 break; | 386 break; |
440 default: | 387 default: |
441 *err = "Unexpected instruction in BPF program"; | 388 *err = "Unexpected instruction in BPF program"; |
442 break; | 389 break; |
443 } | 390 } |
444 } | 391 } |
445 return 0; | 392 return 0; |
446 } | 393 } |
447 | 394 |
448 } // namespace sandbox | 395 } // namespace sandbox |
OLD | NEW |