Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(584)

Side by Side Diff: sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc

Issue 10878033: Simplified unit testing of sandboxing code. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add missing parameter that suppresses benign error messages Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sandbox/linux/seccomp-bpf/sandbox_bpf.cc ('k') | sandbox/linux/seccomp-bpf/util.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
OLDNEW
« no previous file with comments | « sandbox/linux/seccomp-bpf/sandbox_bpf.cc ('k') | sandbox/linux/seccomp-bpf/util.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698