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 // Verify that we can check a value for simple equality. |
117 // as an inequality. | 86 data->args[code.argno()] = code.value(); |
118 data->args[code.argno()] = code.value() ^ 0x55AA55AA; | 87 if (!VerifyErrorCode( |
119 if (!VerifyErrorCode( | 88 sandbox, program, data, root_code, *code.passed(), err)) { |
120 sandbox, program, data, root_code, *code.failed(), err)) { | 89 return false; |
121 return false; | 90 } |
122 } | |
123 | 91 |
124 // BPF programs can only ever operate on 32bit values. So, we have | 92 // If mask ignores any bits, verify that setting those bits is still |
125 // generated additional BPF instructions that inspect the MSB. Verify | 93 // detected as equality. |
126 // that they behave as intended. | 94 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
| |
127 if (code.width() == ErrorCode::TP_32BIT) { | 95 if (code.width() == ErrorCode::TP_32BIT) { |
128 if (code.value() >> 32) { | 96 ignored_bits = static_cast<uint32_t>(ignored_bits); |
129 SANDBOX_DIE( | 97 } |
130 "Invalid comparison of a 32bit system call argument " | 98 if ((ignored_bits & kLower32Bits) != 0) { |
131 "against a 64bit constant; this test is always false."); | 99 data->args[code.argno()] = code.value() | (ignored_bits & kLower32Bits); |
132 } | 100 if (!VerifyErrorCode( |
101 sandbox, program, data, root_code, *code.passed(), err)) { | |
102 return false; | |
103 } | |
104 } | |
105 if ((ignored_bits & kUpper32Bits) != 0) { | |
106 data->args[code.argno()] = code.value() | (ignored_bits & kUpper32Bits); | |
107 if (!VerifyErrorCode( | |
108 sandbox, program, data, root_code, *code.passed(), err)) { | |
109 return false; | |
110 } | |
111 } | |
133 | 112 |
134 // If the system call argument was intended to be a 32bit parameter, | 113 // Verify that changing bits included in the mask is detected as inequality. |
135 // verify that it is a fatal error if a 64bit value is ever passed | 114 if ((code.mask() & kLower32Bits) != 0) { |
136 // here. | 115 data->args[code.argno()] = code.value() ^ (code.mask() & kLower32Bits); |
137 data->args[code.argno()] = 0x100000000ull; | 116 if (!VerifyErrorCode( |
138 if (!VerifyErrorCode(sandbox, | 117 sandbox, program, data, root_code, *code.failed(), err)) { |
139 program, | 118 return false; |
140 data, | 119 } |
141 root_code, | 120 } |
142 sandbox->Unexpected64bitArgument(), | 121 if ((code.mask() & kUpper32Bits) != 0) { |
143 err)) { | 122 data->args[code.argno()] = code.value() ^ (code.mask() & kUpper32Bits); |
144 return false; | 123 if (!VerifyErrorCode( |
145 } | 124 sandbox, program, data, root_code, *code.failed(), err)) { |
146 } else { | 125 return false; |
147 // If the system call argument was intended to be a 64bit parameter, | 126 } |
148 // verify that we can handle (in-)equality for the MSB. This is | 127 } |
149 // essentially the same test that we did earlier for the LSB. | |
150 // We only need to verify the behavior of the inequality test. We | |
151 // know that the equality test already passed, as unlike the kernel | |
152 // the Verifier does operate on 64bit quantities. | |
153 data->args[code.argno()] = code.value() ^ 0x55AA55AA00000000ull; | |
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 | 128 |
175 // On a 32bit system, it is impossible to pass a 64bit | 129 if (code.width() == ErrorCode::TP_32BIT) { |
176 // value as a | 130 // For 32-bit system call arguments, we emit additional instructions to |
177 // system call argument. So, some additional tests always | 131 // 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
| |
178 // evaluate | |
179 // as false. | |
180 ((code.value() & ~uint64_t(uintptr_t(-1))) && | |
181 code.op() == ErrorCode::OP_HAS_ALL_BITS) || | |
182 (code.value() && !(code.value() & uintptr_t(-1)) && | |
183 code.op() == ErrorCode::OP_HAS_ANY_BITS) | |
184 ? *code.failed() | |
185 : *code.passed(); | |
186 | 132 |
187 // Similary, testing for "all" bits in a zero mask is always true. So, | 133 // Arbitrary 64-bit values should be rejected. |
188 // some cases pass despite them normally failing. | 134 data->args[code.argno()] = 1ULL << 32; |
189 const ErrorCode& failed = | 135 if (!VerifyErrorCode(sandbox, |
190 !code.value() && code.op() == ErrorCode::OP_HAS_ALL_BITS | 136 program, |
191 ? *code.passed() | 137 data, |
192 : *code.failed(); | 138 root_code, |
139 sandbox->Unexpected64bitArgument(), | |
140 err)) { | |
141 return false; | |
142 } | |
193 | 143 |
194 data->args[code.argno()] = code.value() & uintptr_t(-1); | 144 // Upper 32-bits set without the MSB of the lower 32-bits set should be |
195 if (!VerifyErrorCode( | 145 // rejected too. |
196 sandbox, program, data, root_code, passed, err)) { | 146 data->args[code.argno()] = kUpper32Bits; |
197 return false; | 147 if (!VerifyErrorCode(sandbox, |
198 } | 148 program, |
199 data->args[code.argno()] = uintptr_t(-1); | 149 data, |
200 if (!VerifyErrorCode( | 150 root_code, |
201 sandbox, program, data, root_code, passed, err)) { | 151 sandbox->Unexpected64bitArgument(), |
202 return false; | 152 err)) { |
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; | 153 return false; |
154 } | |
214 } | 155 } |
215 } else { | 156 } else { |
216 *err = "Attempting to return invalid error code from BPF program"; | 157 *err = "Attempting to return invalid error code from BPF program"; |
217 return false; | 158 return false; |
218 } | 159 } |
219 return true; | 160 return true; |
220 } | 161 } |
221 | 162 |
222 void Ld(State* state, const struct sock_filter& insn, const char** err) { | 163 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) { | 164 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; | 380 break; |
440 default: | 381 default: |
441 *err = "Unexpected instruction in BPF program"; | 382 *err = "Unexpected instruction in BPF program"; |
442 break; | 383 break; |
443 } | 384 } |
444 } | 385 } |
445 return 0; | 386 return 0; |
446 } | 387 } |
447 | 388 |
448 } // namespace sandbox | 389 } // namespace sandbox |
OLD | NEW |