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 <ostream> | 5 #include <ostream> |
6 | 6 |
7 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" | 7 #include "sandbox/linux/seccomp-bpf/bpf_tests.h" |
8 #include "sandbox/linux/seccomp-bpf/verifier.h" | 8 #include "sandbox/linux/seccomp-bpf/verifier.h" |
9 #include "testing/gtest/include/gtest/gtest.h" | 9 #include "testing/gtest/include/gtest/gtest.h" |
10 | 10 |
11 using namespace playground2; | 11 using namespace playground2; |
12 | 12 |
13 namespace { | 13 namespace { |
14 | 14 |
15 const int kExpectedReturnValue = 42; | 15 const int kExpectedReturnValue = 42; |
16 #if defined(__arm__) | 16 #if defined(__arm__) |
17 const int kArmPublicSysnoCeiling = __NR_SYSCALL_BASE + 1024; | 17 const int kArmPublicSysnoCeiling = __NR_SYSCALL_BASE + 1024; |
18 #endif | 18 #endif |
19 | 19 |
| 20 // This test should execute no matter whether we have kernel support. So, |
| 21 // we make it a TEST() instead of a BPF_TEST(). |
20 TEST(SandboxBpf, CallSupports) { | 22 TEST(SandboxBpf, CallSupports) { |
21 // We check that we don't crash, but it's ok if the kernel doesn't | 23 // We check that we don't crash, but it's ok if the kernel doesn't |
22 // support it. | 24 // support it. |
23 bool seccomp_bpf_supported = | 25 bool seccomp_bpf_supported = |
24 Sandbox::supportsSeccompSandbox(-1) == Sandbox::STATUS_AVAILABLE; | 26 Sandbox::supportsSeccompSandbox(-1) == Sandbox::STATUS_AVAILABLE; |
25 // We want to log whether or not seccomp BPF is actually supported | 27 // We want to log whether or not seccomp BPF is actually supported |
26 // since actual test coverage depends on it. | 28 // since actual test coverage depends on it. |
27 RecordProperty("SeccompBPFSupported", | 29 RecordProperty("SeccompBPFSupported", |
28 seccomp_bpf_supported ? "true." : "false."); | 30 seccomp_bpf_supported ? "true." : "false."); |
29 std::cout << "Seccomp BPF supported: " | 31 std::cout << "Seccomp BPF supported: " |
30 << (seccomp_bpf_supported ? "true." : "false.") | 32 << (seccomp_bpf_supported ? "true." : "false.") |
31 << "\n"; | 33 << "\n"; |
32 } | 34 } |
33 | 35 |
34 TEST(SandboxBpf, CallSupportsTwice) { | 36 SANDBOX_TEST(SandboxBpf, CallSupportsTwice) { |
35 Sandbox::supportsSeccompSandbox(-1); | 37 Sandbox::supportsSeccompSandbox(-1); |
36 Sandbox::supportsSeccompSandbox(-1); | 38 Sandbox::supportsSeccompSandbox(-1); |
37 } | 39 } |
38 | 40 |
39 __attribute__((noreturn)) void DoCrash() { | |
40 // Cause a #PF. This only works if we assume that we have the default | |
41 // SIGSEGV handler. | |
42 *(reinterpret_cast<volatile char*>(NULL)) = '\0'; | |
43 for (;;) { | |
44 // If we didn't manage to crash there is really nothing we can do reliably | |
45 // but spin. | |
46 } | |
47 } | |
48 | |
49 __attribute__((noreturn)) void ExitGroup(int status) { | |
50 syscall(__NR_exit_group, status); | |
51 // If exit_group() failed, there is a high likelihood this | |
52 // happened due to a bug in the sandbox. We therefore cannot | |
53 // blindly assume that the failure happened because we are | |
54 // running on an old kernel that supports exit(), but not | |
55 // exit_group(). We cannot even trust "errno" returning | |
56 // "ENOSYS". So, calling exit() as the fallback for | |
57 // exit_group() would at this point almost certainly be a | |
58 // bug. Instead, try some more aggressive methods to make | |
59 // the program stop. | |
60 DoCrash(); | |
61 } | |
62 | |
63 // Helper function to start a sandbox with a policy specified in | |
64 // evaluator | |
65 void StartSandboxOrDie(Sandbox::EvaluateSyscall evaluator) { | |
66 int proc_fd = open("/proc", O_RDONLY|O_DIRECTORY); | |
67 if (proc_fd < 0 || !evaluator) { | |
68 ExitGroup(1); | |
69 } | |
70 if (Sandbox::supportsSeccompSandbox(proc_fd) != | |
71 Sandbox::STATUS_AVAILABLE) { | |
72 ExitGroup(1); | |
73 } | |
74 Sandbox::setProcFd(proc_fd); | |
75 Sandbox::setSandboxPolicy(evaluator, NULL); | |
76 Sandbox::startSandbox(); | |
77 } | |
78 | |
79 void RunInSandbox(Sandbox::EvaluateSyscall evaluator, | |
80 void (*SandboxedCode)()) { | |
81 // TODO(markus): Implement IsEqual for ErrorCode | |
82 // IsEqual(evaluator(__NR_exit_group), Sandbox::SB_ALLOWED) << | |
83 // "You need to always allow exit_group() in your test policy"; | |
84 StartSandboxOrDie(evaluator); | |
85 // TODO(jln): find a way to use the testing framework inside | |
86 // SandboxedCode() or at the very least to surface errors | |
87 SandboxedCode(); | |
88 // SandboxedCode() should have exited, this is a failure | |
89 ExitGroup(1); | |
90 } | |
91 | |
92 // evaluator should always allow ExitGroup | |
93 // SandboxedCode should ExitGroup(kExpectedReturnValue) if and only if | |
94 // things go as expected. | |
95 void TryPolicyInProcess(Sandbox::EvaluateSyscall evaluator, | |
96 void (*SandboxedCode)()) { | |
97 // TODO(jln) figure out a way to surface whether we're actually testing | |
98 // something or not. | |
99 if (Sandbox::supportsSeccompSandbox(-1) == Sandbox::STATUS_AVAILABLE) { | |
100 EXPECT_EXIT(RunInSandbox(evaluator, SandboxedCode), | |
101 ::testing::ExitedWithCode(kExpectedReturnValue), | |
102 ""); | |
103 } else { | |
104 // The sandbox is not available. We should still try to exercise what we | |
105 // can. | |
106 // TODO(markus): (crbug.com/141545) let us call the compiler from here. | |
107 Sandbox::setSandboxPolicy(evaluator, NULL); | |
108 } | |
109 } | |
110 | |
111 // A simple blacklist test | 41 // A simple blacklist test |
112 | 42 |
113 Sandbox::ErrorCode BlacklistNanosleepPolicy(int sysno) { | 43 Sandbox::ErrorCode BlacklistNanosleepPolicy(int sysno) { |
114 if (sysno < static_cast<int>(MIN_SYSCALL) || | 44 if (sysno < static_cast<int>(MIN_SYSCALL) || |
115 sysno > static_cast<int>(MAX_SYSCALL)) { | 45 sysno > static_cast<int>(MAX_SYSCALL)) { |
116 // FIXME: we should really not have to do that in a trivial policy | 46 // FIXME: we should really not have to do that in a trivial policy |
117 return ENOSYS; | 47 return ENOSYS; |
118 } | 48 } |
119 switch (sysno) { | 49 switch (sysno) { |
120 case __NR_nanosleep: | 50 case __NR_nanosleep: |
121 return EACCES; | 51 return EACCES; |
122 default: | 52 default: |
123 return Sandbox::SB_ALLOWED; | 53 return Sandbox::SB_ALLOWED; |
124 } | 54 } |
125 } | 55 } |
126 | 56 |
127 void NanosleepProcess(void) { | 57 BPF_TEST(SandboxBpf, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) { |
| 58 // nanosleep() should be denied |
128 const struct timespec ts = {0, 0}; | 59 const struct timespec ts = {0, 0}; |
129 errno = 0; | 60 errno = 0; |
130 if(syscall(__NR_nanosleep, &ts, NULL) != -1 || errno != EACCES) { | 61 BPF_ASSERT(syscall(__NR_nanosleep, &ts, NULL) == -1); |
131 ExitGroup(1); | 62 BPF_ASSERT(errno == EACCES); |
132 } | |
133 ExitGroup(kExpectedReturnValue); | |
134 } | |
135 | |
136 TEST(SandboxBpf, ApplyBasicBlacklistPolicy) { | |
137 TryPolicyInProcess(BlacklistNanosleepPolicy, NanosleepProcess); | |
138 } | 63 } |
139 | 64 |
140 // Now do a simple whitelist test | 65 // Now do a simple whitelist test |
141 | 66 |
142 Sandbox::ErrorCode WhitelistGetpidPolicy(int sysno) { | 67 Sandbox::ErrorCode WhitelistGetpidPolicy(int sysno) { |
143 switch (sysno) { | 68 switch (sysno) { |
144 case __NR_getpid: | 69 case __NR_getpid: |
145 case __NR_exit_group: | 70 case __NR_exit_group: |
146 return Sandbox::SB_ALLOWED; | 71 return Sandbox::SB_ALLOWED; |
147 default: | 72 default: |
148 return ENOMEM; | 73 return ENOMEM; |
149 } | 74 } |
150 } | 75 } |
151 | 76 |
152 void GetpidProcess(void) { | 77 BPF_TEST(SandboxBpf, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) { |
| 78 // getpid() should be allowed |
153 errno = 0; | 79 errno = 0; |
154 // getpid() should be allowed | 80 BPF_ASSERT(syscall(__NR_getpid) > 0); |
155 if (syscall(__NR_getpid) < 0 || errno) | 81 BPF_ASSERT(errno == 0); |
156 ExitGroup(1); | 82 |
157 // getpgid() should be denied | 83 // getpgid() should be denied |
158 if (getpgid(0) != -1 || errno != ENOMEM) | 84 BPF_ASSERT(getpgid(0) == -1); |
159 ExitGroup(1); | 85 BPF_ASSERT(errno == ENOMEM); |
160 ExitGroup(kExpectedReturnValue); | |
161 } | |
162 | |
163 TEST(SandboxBpf, ApplyBasicWhitelistPolicy) { | |
164 TryPolicyInProcess(WhitelistGetpidPolicy, GetpidProcess); | |
165 } | 86 } |
166 | 87 |
167 // A simple blacklist policy, with a SIGSYS handler | 88 // A simple blacklist policy, with a SIGSYS handler |
168 | 89 |
169 // TODO: provide an API to provide the auxiliary data pointer | 90 // TODO: provide an API to provide the auxiliary data pointer |
170 // to the evaluator | 91 // to the evaluator |
171 | 92 |
172 static int BlacklistNanosleepPolicySigsysAuxData; | 93 static int BlacklistNanosleepPolicySigsysAuxData; |
173 | 94 |
174 intptr_t EnomemHandler(const struct arch_seccomp_data& args, void *aux) { | 95 intptr_t EnomemHandler(const struct arch_seccomp_data& args, void *aux) { |
175 // We also check that the auxiliary data is correct | 96 // We also check that the auxiliary data is correct |
176 if (!aux) | 97 SANDBOX_ASSERT(aux); |
177 ExitGroup(1); | |
178 *(static_cast<int*>(aux)) = kExpectedReturnValue; | 98 *(static_cast<int*>(aux)) = kExpectedReturnValue; |
179 return -ENOMEM; | 99 return -ENOMEM; |
180 } | 100 } |
181 | 101 |
182 Sandbox::ErrorCode BlacklistNanosleepPolicySigsys(int sysno) { | 102 Sandbox::ErrorCode BlacklistNanosleepPolicySigsys(int sysno) { |
183 if (sysno < static_cast<int>(MIN_SYSCALL) || | 103 if (sysno < static_cast<int>(MIN_SYSCALL) || |
184 sysno > static_cast<int>(MAX_SYSCALL)) { | 104 sysno > static_cast<int>(MAX_SYSCALL)) { |
185 // FIXME: we should really not have to do that in a trivial policy | 105 // FIXME: we should really not have to do that in a trivial policy |
186 return ENOSYS; | 106 return ENOSYS; |
187 } | 107 } |
188 switch (sysno) { | 108 switch (sysno) { |
189 case __NR_nanosleep: | 109 case __NR_nanosleep: |
190 return Sandbox::ErrorCode(EnomemHandler, | 110 return Sandbox::ErrorCode(EnomemHandler, |
191 static_cast<void *>(&BlacklistNanosleepPolicySigsysAuxData)); | 111 static_cast<void *>(&BlacklistNanosleepPolicySigsysAuxData)); |
192 default: | 112 default: |
193 return Sandbox::SB_ALLOWED; | 113 return Sandbox::SB_ALLOWED; |
194 } | 114 } |
195 } | 115 } |
196 | 116 |
197 void NanosleepProcessSigsys(void) { | 117 BPF_TEST(SandboxBpf, BasicBlacklistWithSigsys, |
198 const struct timespec ts = {0, 0}; | 118 BlacklistNanosleepPolicySigsys) { |
| 119 // getpid() should work properly |
199 errno = 0; | 120 errno = 0; |
200 // getpid() should work properly | 121 BPF_ASSERT(syscall(__NR_getpid) > 0); |
201 if (syscall(__NR_getpid) < 0) | 122 BPF_ASSERT(errno == 0); |
202 ExitGroup(1); | 123 |
203 // Our Auxiliary Data, should be reset by the signal handler | 124 // Our Auxiliary Data, should be reset by the signal handler |
204 BlacklistNanosleepPolicySigsysAuxData = -1; | 125 BlacklistNanosleepPolicySigsysAuxData = -1; |
205 errno = 0; | 126 const struct timespec ts = {0, 0}; |
206 if (syscall(__NR_nanosleep, &ts, NULL) != -1 || errno != ENOMEM) | 127 BPF_ASSERT(syscall(__NR_nanosleep, &ts, NULL) == -1); |
207 ExitGroup(1); | 128 BPF_ASSERT(errno == ENOMEM); |
| 129 |
208 // We expect the signal handler to modify AuxData | 130 // We expect the signal handler to modify AuxData |
209 if (BlacklistNanosleepPolicySigsysAuxData != kExpectedReturnValue) | 131 BPF_ASSERT( |
210 ExitGroup(1); | 132 BlacklistNanosleepPolicySigsysAuxData == kExpectedReturnValue); |
211 else | |
212 ExitGroup(kExpectedReturnValue); | |
213 } | |
214 | |
215 TEST(SandboxBpf, BasicBlacklistWithSigsys) { | |
216 TryPolicyInProcess(BlacklistNanosleepPolicySigsys, NanosleepProcessSigsys); | |
217 } | 133 } |
218 | 134 |
219 // A more complex, but synthetic policy. This tests the correctness of the BPF | 135 // A more complex, but synthetic policy. This tests the correctness of the BPF |
220 // program by iterating through all syscalls and checking for an errno that | 136 // program by iterating through all syscalls and checking for an errno that |
221 // depends on the syscall number. Unlike the Verifier, this exercises the BPF | 137 // depends on the syscall number. Unlike the Verifier, this exercises the BPF |
222 // interpreter in the kernel. | 138 // interpreter in the kernel. |
223 | 139 |
224 // We try to make sure we exercise optimizations in the BPF compiler. We make | 140 // We try to make sure we exercise optimizations in the BPF compiler. We make |
225 // sure that the compiler can have an opportunity to coalesce syscalls with | 141 // sure that the compiler can have an opportunity to coalesce syscalls with |
226 // contiguous numbers and we also make sure that disjoint sets can return the | 142 // contiguous numbers and we also make sure that disjoint sets can return the |
(...skipping 10 matching lines...) Expand all Loading... |
237 // FIXME: we should really not have to do that in a trivial policy. | 153 // FIXME: we should really not have to do that in a trivial policy. |
238 return ENOSYS; | 154 return ENOSYS; |
239 } | 155 } |
240 | 156 |
241 // TODO(jorgelo): remove this restriction once crbug.com/141694 is fixed. | 157 // TODO(jorgelo): remove this restriction once crbug.com/141694 is fixed. |
242 #if defined(__arm__) | 158 #if defined(__arm__) |
243 if (sysno > kArmPublicSysnoCeiling) | 159 if (sysno > kArmPublicSysnoCeiling) |
244 return ENOSYS; | 160 return ENOSYS; |
245 #endif | 161 #endif |
246 | 162 |
247 if (sysno == __NR_exit_group) { | 163 // TODO(markus): allow calls to write(). This should start working as soon |
| 164 // as we switch to the new code generator. Currently we are blowing up, |
| 165 // because our jumptable is getting too big. |
| 166 if (sysno == __NR_exit_group /* || sysno == __NR_write */) { |
248 // exit_group() is special, we really need it to work. | 167 // exit_group() is special, we really need it to work. |
| 168 // write() is needed for BPF_ASSERT() to report a useful error message. |
249 return Sandbox::SB_ALLOWED; | 169 return Sandbox::SB_ALLOWED; |
250 } else { | 170 } else { |
251 return SysnoToRandomErrno(sysno); | 171 return SysnoToRandomErrno(sysno); |
252 } | 172 } |
253 } | 173 } |
254 | 174 |
255 void SyntheticProcess(void) { | 175 BPF_TEST(SandboxBpf, SyntheticPolicy, SyntheticPolicy) { |
256 // Ensure that that kExpectedReturnValue + syscallnumber + 1 does not int | 176 // Ensure that that kExpectedReturnValue + syscallnumber + 1 does not int |
257 // overflow. | 177 // overflow. |
258 if (std::numeric_limits<int>::max() - kExpectedReturnValue - 1 < | 178 BPF_ASSERT( |
259 static_cast<int>(MAX_SYSCALL)) { | 179 std::numeric_limits<int>::max() - kExpectedReturnValue - 1 >= |
260 ExitGroup(1); | 180 static_cast<int>(MAX_SYSCALL)); |
261 } | |
262 | 181 |
263 // TODO(jorgelo): remove this limit once crbug.com/141694 is fixed. | 182 // TODO(jorgelo): remove this limit once crbug.com/141694 is fixed. |
264 #if defined(__arm__) | 183 #if defined(__arm__) |
265 const int sysno_ceiling = kArmPublicSysnoCeiling; | 184 const int sysno_ceiling = kArmPublicSysnoCeiling; |
266 #else | 185 #else |
267 const int sysno_ceiling = static_cast<int>(MAX_SYSCALL); | 186 const int sysno_ceiling = static_cast<int>(MAX_SYSCALL); |
268 #endif | 187 #endif |
269 | 188 |
270 for (int syscall_number = static_cast<int>(MIN_SYSCALL); | 189 for (int syscall_number = static_cast<int>(MIN_SYSCALL); |
271 syscall_number <= sysno_ceiling; | 190 syscall_number <= sysno_ceiling; |
272 ++syscall_number) { | 191 ++syscall_number) { |
273 if (syscall_number == __NR_exit_group) { | 192 if (syscall_number == __NR_exit_group || |
| 193 syscall_number == __NR_write) { |
274 // exit_group() is special | 194 // exit_group() is special |
275 continue; | 195 continue; |
276 } | 196 } |
277 errno = 0; | 197 errno = 0; |
278 if (syscall(syscall_number) != -1 || | 198 BPF_ASSERT(syscall(syscall_number) == -1); |
279 errno != SysnoToRandomErrno(syscall_number)) { | 199 BPF_ASSERT(errno == SysnoToRandomErrno(syscall_number)); |
280 // Exit with a return value that is different than kExpectedReturnValue | |
281 // to signal an error. Make it easy to see what syscall_number failed in | |
282 // the test report. | |
283 ExitGroup(kExpectedReturnValue + syscall_number + 1); | |
284 } | |
285 } | 200 } |
286 ExitGroup(kExpectedReturnValue); | |
287 } | |
288 | |
289 TEST(SandboxBpf, SyntheticPolicy) { | |
290 TryPolicyInProcess(SyntheticPolicy, SyntheticProcess); | |
291 } | 201 } |
292 | 202 |
293 } // namespace | 203 } // namespace |
OLD | NEW |