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 BPF_TEST(SandboxBpf, CallSupportsTwice, NULL) { |
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 | 41 // Helper function to start a sandbox with a policy specified in |
64 // evaluator | 42 // evaluator |
65 void StartSandboxOrDie(Sandbox::EvaluateSyscall evaluator) { | 43 void StartSandboxOrDie() { |
66 int proc_fd = open("/proc", O_RDONLY|O_DIRECTORY); | 44 // Ensure the the sandbox is actually available at this time |
67 if (proc_fd < 0 || !evaluator) { | 45 int proc_fd; |
68 ExitGroup(1); | 46 BPF_ASSERT((proc_fd = open("/proc", O_RDONLY|O_DIRECTORY)) >= 0); |
69 } | 47 BPF_ASSERT(Sandbox::supportsSeccompSandbox(proc_fd) == |
70 if (Sandbox::supportsSeccompSandbox(proc_fd) != | 48 Sandbox::STATUS_AVAILABLE); |
71 Sandbox::STATUS_AVAILABLE) { | 49 |
72 ExitGroup(1); | 50 // Initialize and then start the sandbox with our custom policy |
73 } | |
74 Sandbox::setProcFd(proc_fd); | 51 Sandbox::setProcFd(proc_fd); |
75 Sandbox::setSandboxPolicy(evaluator, NULL); | |
76 Sandbox::startSandbox(); | 52 Sandbox::startSandbox(); |
77 } | 53 } |
78 | 54 |
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 | 55 |
111 // A simple blacklist test | 56 // A simple blacklist test |
112 | 57 |
113 Sandbox::ErrorCode BlacklistNanosleepPolicy(int sysno) { | 58 Sandbox::ErrorCode BlacklistNanosleepPolicy(int sysno) { |
114 if (sysno < static_cast<int>(MIN_SYSCALL) || | 59 if (sysno < static_cast<int>(MIN_SYSCALL) || |
115 sysno > static_cast<int>(MAX_SYSCALL)) { | 60 sysno > static_cast<int>(MAX_SYSCALL)) { |
116 // FIXME: we should really not have to do that in a trivial policy | 61 // FIXME: we should really not have to do that in a trivial policy |
117 return ENOSYS; | 62 return ENOSYS; |
118 } | 63 } |
119 switch (sysno) { | 64 switch (sysno) { |
120 case __NR_nanosleep: | 65 case __NR_nanosleep: |
121 return EACCES; | 66 return EACCES; |
122 default: | 67 default: |
123 return Sandbox::SB_ALLOWED; | 68 return Sandbox::SB_ALLOWED; |
124 } | 69 } |
125 } | 70 } |
126 | 71 |
127 void NanosleepProcess(void) { | 72 BPF_TEST(SandboxBpf, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) { |
73 StartSandboxOrDie(); | |
74 | |
75 // nanosleep() should be denied | |
128 const struct timespec ts = {0, 0}; | 76 const struct timespec ts = {0, 0}; |
129 errno = 0; | 77 errno = 0; |
130 if(syscall(__NR_nanosleep, &ts, NULL) != -1 || errno != EACCES) { | 78 BPF_ASSERT(syscall(__NR_nanosleep, &ts, NULL) == -1); |
131 ExitGroup(1); | 79 BPF_ASSERT(errno == EACCES); |
132 } | |
133 ExitGroup(kExpectedReturnValue); | |
134 } | |
135 | |
136 TEST(SandboxBpf, ApplyBasicBlacklistPolicy) { | |
137 TryPolicyInProcess(BlacklistNanosleepPolicy, NanosleepProcess); | |
138 } | 80 } |
139 | 81 |
140 // Now do a simple whitelist test | 82 // Now do a simple whitelist test |
141 | 83 |
142 Sandbox::ErrorCode WhitelistGetpidPolicy(int sysno) { | 84 Sandbox::ErrorCode WhitelistGetpidPolicy(int sysno) { |
143 switch (sysno) { | 85 switch (sysno) { |
144 case __NR_getpid: | 86 case __NR_getpid: |
145 case __NR_exit_group: | 87 case __NR_exit_group: |
146 return Sandbox::SB_ALLOWED; | 88 return Sandbox::SB_ALLOWED; |
147 default: | 89 default: |
148 return ENOMEM; | 90 return ENOMEM; |
149 } | 91 } |
150 } | 92 } |
151 | 93 |
152 void GetpidProcess(void) { | 94 BPF_TEST(SandboxBpf, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) { |
95 StartSandboxOrDie(); | |
96 | |
97 // getpid() should be allowed | |
153 errno = 0; | 98 errno = 0; |
154 // getpid() should be allowed | 99 BPF_ASSERT(syscall(__NR_getpid) > 0); |
155 if (syscall(__NR_getpid) < 0 || errno) | 100 BPF_ASSERT(errno == 0); |
156 ExitGroup(1); | 101 |
157 // getpgid() should be denied | 102 // getpgid() should be denied |
158 if (getpgid(0) != -1 || errno != ENOMEM) | 103 BPF_ASSERT(getpgid(0) == -1); |
159 ExitGroup(1); | 104 BPF_ASSERT(errno == ENOMEM); |
160 ExitGroup(kExpectedReturnValue); | |
161 } | |
162 | |
163 TEST(SandboxBpf, ApplyBasicWhitelistPolicy) { | |
164 TryPolicyInProcess(WhitelistGetpidPolicy, GetpidProcess); | |
165 } | 105 } |
166 | 106 |
167 // A simple blacklist policy, with a SIGSYS handler | 107 // A simple blacklist policy, with a SIGSYS handler |
168 | 108 |
169 // TODO: provide an API to provide the auxiliary data pointer | 109 // TODO: provide an API to provide the auxiliary data pointer |
170 // to the evaluator | 110 // to the evaluator |
171 | 111 |
172 static int BlacklistNanosleepPolicySigsysAuxData; | 112 static int BlacklistNanosleepPolicySigsysAuxData; |
173 | 113 |
174 intptr_t EnomemHandler(const struct arch_seccomp_data& args, void *aux) { | 114 intptr_t EnomemHandler(const struct arch_seccomp_data& args, void *aux) { |
175 // We also check that the auxiliary data is correct | 115 // We also check that the auxiliary data is correct |
176 if (!aux) | 116 SANDBOX_ASSERT(aux); |
177 ExitGroup(1); | |
178 *(static_cast<int*>(aux)) = kExpectedReturnValue; | 117 *(static_cast<int*>(aux)) = kExpectedReturnValue; |
179 return -ENOMEM; | 118 return -ENOMEM; |
180 } | 119 } |
181 | 120 |
182 Sandbox::ErrorCode BlacklistNanosleepPolicySigsys(int sysno) { | 121 Sandbox::ErrorCode BlacklistNanosleepPolicySigsys(int sysno) { |
183 if (sysno < static_cast<int>(MIN_SYSCALL) || | 122 if (sysno < static_cast<int>(MIN_SYSCALL) || |
184 sysno > static_cast<int>(MAX_SYSCALL)) { | 123 sysno > static_cast<int>(MAX_SYSCALL)) { |
185 // FIXME: we should really not have to do that in a trivial policy | 124 // FIXME: we should really not have to do that in a trivial policy |
186 return ENOSYS; | 125 return ENOSYS; |
187 } | 126 } |
188 switch (sysno) { | 127 switch (sysno) { |
189 case __NR_nanosleep: | 128 case __NR_nanosleep: |
190 return Sandbox::ErrorCode(EnomemHandler, | 129 return Sandbox::ErrorCode(EnomemHandler, |
191 static_cast<void *>(&BlacklistNanosleepPolicySigsysAuxData)); | 130 static_cast<void *>(&BlacklistNanosleepPolicySigsysAuxData)); |
192 default: | 131 default: |
193 return Sandbox::SB_ALLOWED; | 132 return Sandbox::SB_ALLOWED; |
194 } | 133 } |
195 } | 134 } |
196 | 135 |
197 void NanosleepProcessSigsys(void) { | 136 BPF_TEST(SandboxBpf, BasicBlacklistWithSigsys, |
198 const struct timespec ts = {0, 0}; | 137 BlacklistNanosleepPolicySigsys) { |
138 StartSandboxOrDie(); | |
139 | |
140 // getpid() should work properly | |
199 errno = 0; | 141 errno = 0; |
200 // getpid() should work properly | 142 BPF_ASSERT(syscall(__NR_getpid) > 0); |
201 if (syscall(__NR_getpid) < 0) | 143 BPF_ASSERT(errno == 0); |
202 ExitGroup(1); | 144 |
203 // Our Auxiliary Data, should be reset by the signal handler | 145 // Our Auxiliary Data, should be reset by the signal handler |
204 BlacklistNanosleepPolicySigsysAuxData = -1; | 146 BlacklistNanosleepPolicySigsysAuxData = -1; |
205 errno = 0; | 147 const struct timespec ts = {0, 0}; |
206 if (syscall(__NR_nanosleep, &ts, NULL) != -1 || errno != ENOMEM) | 148 BPF_ASSERT(syscall(__NR_nanosleep, &ts, NULL) == -1); |
207 ExitGroup(1); | 149 BPF_ASSERT(errno == ENOMEM); |
150 | |
208 // We expect the signal handler to modify AuxData | 151 // We expect the signal handler to modify AuxData |
209 if (BlacklistNanosleepPolicySigsysAuxData != kExpectedReturnValue) | 152 BPF_ASSERT( |
210 ExitGroup(1); | 153 BlacklistNanosleepPolicySigsysAuxData == kExpectedReturnValue); |
211 else | |
212 ExitGroup(kExpectedReturnValue); | |
213 } | |
214 | |
215 TEST(SandboxBpf, BasicBlacklistWithSigsys) { | |
216 TryPolicyInProcess(BlacklistNanosleepPolicySigsys, NanosleepProcessSigsys); | |
217 } | 154 } |
218 | 155 |
219 // A more complex, but synthetic policy. This tests the correctness of the BPF | 156 // 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 | 157 // 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 | 158 // depends on the syscall number. Unlike the Verifier, this exercises the BPF |
222 // interpreter in the kernel. | 159 // interpreter in the kernel. |
223 | 160 |
224 // We try to make sure we exercise optimizations in the BPF compiler. We make | 161 // 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 | 162 // 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 | 163 // contiguous numbers and we also make sure that disjoint sets can return the |
(...skipping 18 matching lines...) Expand all Loading... | |
245 #endif | 182 #endif |
246 | 183 |
247 if (sysno == __NR_exit_group) { | 184 if (sysno == __NR_exit_group) { |
248 // exit_group() is special, we really need it to work. | 185 // exit_group() is special, we really need it to work. |
249 return Sandbox::SB_ALLOWED; | 186 return Sandbox::SB_ALLOWED; |
250 } else { | 187 } else { |
251 return SysnoToRandomErrno(sysno); | 188 return SysnoToRandomErrno(sysno); |
252 } | 189 } |
253 } | 190 } |
254 | 191 |
255 void SyntheticProcess(void) { | 192 BPF_TEST(SandboxBpf, SyntheticPolicy, SyntheticPolicy) { |
193 StartSandboxOrDie(); | |
194 | |
256 // Ensure that that kExpectedReturnValue + syscallnumber + 1 does not int | 195 // Ensure that that kExpectedReturnValue + syscallnumber + 1 does not int |
257 // overflow. | 196 // overflow. |
258 if (std::numeric_limits<int>::max() - kExpectedReturnValue - 1 < | 197 BPF_ASSERT( |
259 static_cast<int>(MAX_SYSCALL)) { | 198 std::numeric_limits<int>::max() - kExpectedReturnValue - 1 >= |
260 ExitGroup(1); | 199 static_cast<int>(MAX_SYSCALL)); |
261 } | |
262 | 200 |
263 // TODO(jorgelo): remove this limit once crbug.com/141694 is fixed. | 201 // TODO(jorgelo): remove this limit once crbug.com/141694 is fixed. |
264 #if defined(__arm__) | 202 #if defined(__arm__) |
265 const int sysno_ceiling = kArmPublicSysnoCeiling; | 203 const int sysno_ceiling = kArmPublicSysnoCeiling; |
266 #else | 204 #else |
267 const int sysno_ceiling = static_cast<int>(MAX_SYSCALL); | 205 const int sysno_ceiling = static_cast<int>(MAX_SYSCALL); |
268 #endif | 206 #endif |
269 | 207 |
270 for (int syscall_number = static_cast<int>(MIN_SYSCALL); | 208 for (int syscall_number = static_cast<int>(MIN_SYSCALL); |
271 syscall_number <= sysno_ceiling; | 209 syscall_number <= sysno_ceiling; |
272 ++syscall_number) { | 210 ++syscall_number) { |
273 if (syscall_number == __NR_exit_group) { | 211 if (syscall_number == __NR_exit_group) { |
274 // exit_group() is special | 212 // exit_group() is special |
275 continue; | 213 continue; |
276 } | 214 } |
277 errno = 0; | 215 errno = 0; |
278 if (syscall(syscall_number) != -1 || | 216 BPF_ASSERT(syscall(syscall_number) == -1); |
jln (very slow on Chromium)
2012/08/24 04:00:32
This won't output anything in this test to stderr
| |
279 errno != SysnoToRandomErrno(syscall_number)) { | 217 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 } | 218 } |
286 ExitGroup(kExpectedReturnValue); | |
287 } | |
288 | |
289 TEST(SandboxBpf, SyntheticPolicy) { | |
290 TryPolicyInProcess(SyntheticPolicy, SyntheticProcess); | |
291 } | 219 } |
292 | 220 |
293 } // namespace | 221 } // namespace |
OLD | NEW |