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