1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <sys/prctl.h>
#include <sys/utsname.h>
#include <ostream>
#include "base/memory/scoped_ptr.h"
#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
#include "sandbox/linux/seccomp-bpf/verifier.h"
#include "sandbox/linux/services/broker_process.h"
#include "testing/gtest/include/gtest/gtest.h"
using namespace playground2;
using sandbox::BrokerProcess;
namespace {
const int kExpectedReturnValue = 42;
// This test should execute no matter whether we have kernel support. So,
// we make it a TEST() instead of a BPF_TEST().
TEST(SandboxBpf, CallSupports) {
// We check that we don't crash, but it's ok if the kernel doesn't
// support it.
bool seccomp_bpf_supported =
Sandbox::supportsSeccompSandbox(-1) == Sandbox::STATUS_AVAILABLE;
// We want to log whether or not seccomp BPF is actually supported
// since actual test coverage depends on it.
RecordProperty("SeccompBPFSupported",
seccomp_bpf_supported ? "true." : "false.");
std::cout << "Seccomp BPF supported: "
<< (seccomp_bpf_supported ? "true." : "false.")
<< "\n";
RecordProperty("PointerSize", sizeof(void*));
std::cout << "Pointer size: " << sizeof(void*) << "\n";
}
SANDBOX_TEST(SandboxBpf, CallSupportsTwice) {
Sandbox::supportsSeccompSandbox(-1);
Sandbox::supportsSeccompSandbox(-1);
}
// BPF_TEST does a lot of the boiler-plate code around setting up a
// policy and optional passing data between the caller, the policy and
// any Trap() handlers. This is great for writing short and concise tests,
// and it helps us accidentally forgetting any of the crucial steps in
// setting up the sandbox. But it wouldn't hurt to have at least one test
// that explicitly walks through all these steps.
intptr_t FakeGetPid(const struct arch_seccomp_data& args, void *aux) {
BPF_ASSERT(aux);
pid_t *pid_ptr = static_cast<pid_t *>(aux);
return (*pid_ptr)++;
}
ErrorCode VerboseAPITestingPolicy(int sysno, void *aux) {
if (!Sandbox::isValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
} else if (sysno == __NR_getpid) {
return Sandbox::Trap(FakeGetPid, aux);
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
SANDBOX_TEST(SandboxBpf, VerboseAPITesting) {
if (Sandbox::supportsSeccompSandbox(-1) ==
playground2::Sandbox::STATUS_AVAILABLE) {
pid_t test_var = 0;
playground2::Sandbox::setSandboxPolicy(VerboseAPITestingPolicy, &test_var);
playground2::Sandbox::startSandbox();
BPF_ASSERT(test_var == 0);
BPF_ASSERT(syscall(__NR_getpid) == 0);
BPF_ASSERT(test_var == 1);
BPF_ASSERT(syscall(__NR_getpid) == 1);
BPF_ASSERT(test_var == 2);
// N.B.: Any future call to getpid() would corrupt the stack.
// This is OK. The SANDBOX_TEST() macro is guaranteed to
// only ever call _exit() after the test completes.
}
}
// A simple blacklist test
ErrorCode BlacklistNanosleepPolicy(int sysno, void *) {
if (!Sandbox::isValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
}
switch (sysno) {
case __NR_nanosleep:
return ErrorCode(EACCES);
default:
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
BPF_TEST(SandboxBpf, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) {
// nanosleep() should be denied
const struct timespec ts = {0, 0};
errno = 0;
BPF_ASSERT(syscall(__NR_nanosleep, &ts, NULL) == -1);
BPF_ASSERT(errno == EACCES);
}
// Now do a simple whitelist test
ErrorCode WhitelistGetpidPolicy(int sysno, void *) {
switch (sysno) {
case __NR_getpid:
case __NR_exit_group:
return ErrorCode(ErrorCode::ERR_ALLOWED);
default:
return ErrorCode(ENOMEM);
}
}
BPF_TEST(SandboxBpf, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) {
// getpid() should be allowed
errno = 0;
BPF_ASSERT(syscall(__NR_getpid) > 0);
BPF_ASSERT(errno == 0);
// getpgid() should be denied
BPF_ASSERT(getpgid(0) == -1);
BPF_ASSERT(errno == ENOMEM);
}
// A simple blacklist policy, with a SIGSYS handler
intptr_t EnomemHandler(const struct arch_seccomp_data& args, void *aux) {
// We also check that the auxiliary data is correct
SANDBOX_ASSERT(aux);
*(static_cast<int*>(aux)) = kExpectedReturnValue;
return -ENOMEM;
}
ErrorCode BlacklistNanosleepPolicySigsys(int sysno, void *aux) {
if (!Sandbox::isValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
}
switch (sysno) {
case __NR_nanosleep:
return Sandbox::Trap(EnomemHandler, aux);
default:
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
BPF_TEST(SandboxBpf, BasicBlacklistWithSigsys,
BlacklistNanosleepPolicySigsys, int /* BPF_AUX */) {
// getpid() should work properly
errno = 0;
BPF_ASSERT(syscall(__NR_getpid) > 0);
BPF_ASSERT(errno == 0);
// Our Auxiliary Data, should be reset by the signal handler
BPF_AUX = -1;
const struct timespec ts = {0, 0};
BPF_ASSERT(syscall(__NR_nanosleep, &ts, NULL) == -1);
BPF_ASSERT(errno == ENOMEM);
// We expect the signal handler to modify AuxData
BPF_ASSERT(BPF_AUX == kExpectedReturnValue);
}
// A more complex, but synthetic policy. This tests the correctness of the BPF
// program by iterating through all syscalls and checking for an errno that
// depends on the syscall number. Unlike the Verifier, this exercises the BPF
// interpreter in the kernel.
// We try to make sure we exercise optimizations in the BPF compiler. We make
// sure that the compiler can have an opportunity to coalesce syscalls with
// contiguous numbers and we also make sure that disjoint sets can return the
// same errno.
int SysnoToRandomErrno(int sysno) {
// Small contiguous sets of 3 system calls return an errno equal to the
// index of that set + 1 (so that we never return a NUL errno).
return ((sysno & ~3) >> 2) % 29 + 1;
}
ErrorCode SyntheticPolicy(int sysno, void *) {
if (!Sandbox::isValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
}
// TODO(jorgelo): remove this once the new code generator lands.
#if defined(__arm__)
if (sysno > static_cast<int>(MAX_PUBLIC_SYSCALL)) {
return ErrorCode(ENOSYS);
}
#endif
if (sysno == __NR_exit_group || sysno == __NR_write) {
// exit_group() is special, we really need it to work.
// write() is needed for BPF_ASSERT() to report a useful error message.
return ErrorCode(ErrorCode::ERR_ALLOWED);
} else {
return ErrorCode(SysnoToRandomErrno(sysno));
}
}
BPF_TEST(SandboxBpf, SyntheticPolicy, SyntheticPolicy) {
// Ensure that that kExpectedReturnValue + syscallnumber + 1 does not int
// overflow.
BPF_ASSERT(
std::numeric_limits<int>::max() - kExpectedReturnValue - 1 >=
static_cast<int>(MAX_PUBLIC_SYSCALL));
for (int syscall_number = static_cast<int>(MIN_SYSCALL);
syscall_number <= static_cast<int>(MAX_PUBLIC_SYSCALL);
++syscall_number) {
if (syscall_number == __NR_exit_group ||
syscall_number == __NR_write) {
// exit_group() is special
continue;
}
errno = 0;
BPF_ASSERT(syscall(syscall_number) == -1);
BPF_ASSERT(errno == SysnoToRandomErrno(syscall_number));
}
}
#if defined(__arm__)
// A simple policy that tests whether ARM private system calls are supported
// by our BPF compiler and by the BPF interpreter in the kernel.
// For ARM private system calls, return an errno equal to their offset from
// MIN_PRIVATE_SYSCALL plus 1 (to avoid NUL errno).
int ArmPrivateSysnoToErrno(int sysno) {
if (sysno >= static_cast<int>(MIN_PRIVATE_SYSCALL) &&
sysno <= static_cast<int>(MAX_PRIVATE_SYSCALL)) {
return (sysno - MIN_PRIVATE_SYSCALL) + 1;
} else {
return ENOSYS;
}
}
ErrorCode ArmPrivatePolicy(int sysno, void *) {
if (!Sandbox::isValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy.
return ErrorCode(ENOSYS);
}
// Start from |__ARM_NR_set_tls + 1| so as not to mess with actual
// ARM private system calls.
if (sysno >= static_cast<int>(__ARM_NR_set_tls + 1) &&
sysno <= static_cast<int>(MAX_PRIVATE_SYSCALL)) {
return ErrorCode(ArmPrivateSysnoToErrno(sysno));
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
BPF_TEST(SandboxBpf, ArmPrivatePolicy, ArmPrivatePolicy) {
for (int syscall_number = static_cast<int>(__ARM_NR_set_tls + 1);
syscall_number <= static_cast<int>(MAX_PRIVATE_SYSCALL);
++syscall_number) {
errno = 0;
BPF_ASSERT(syscall(syscall_number) == -1);
BPF_ASSERT(errno == ArmPrivateSysnoToErrno(syscall_number));
}
}
#endif // defined(__arm__)
intptr_t CountSyscalls(const struct arch_seccomp_data& args, void *aux) {
// Count all invocations of our callback function.
++*reinterpret_cast<int *>(aux);
// Verify that within the callback function all filtering is temporarily
// disabled.
BPF_ASSERT(syscall(__NR_getpid) > 1);
// Verify that we can now call the underlying system call without causing
// infinite recursion.
return Sandbox::ForwardSyscall(args);
}
ErrorCode GreyListedPolicy(int sysno, void *aux) {
// The use of UnsafeTrap() causes us to print a warning message. This is
// generally desirable, but it results in the unittest failing, as it doesn't
// expect any messages on "stderr". So, temporarily disable messages. The
// BPF_TEST() is guaranteed to turn messages back on, after the policy
// function has completed.
Die::SuppressInfoMessages(true);
// Some system calls must always be allowed, if our policy wants to make
// use of UnsafeTrap()
if (sysno == __NR_rt_sigprocmask ||
sysno == __NR_rt_sigreturn
#if defined(__NR_sigprocmask)
|| sysno == __NR_sigprocmask
#endif
#if defined(__NR_sigreturn)
|| sysno == __NR_sigreturn
#endif
) {
return ErrorCode(ErrorCode::ERR_ALLOWED);
} else if (sysno == __NR_getpid) {
// Disallow getpid()
return ErrorCode(EPERM);
} else if (Sandbox::isValidSyscallNumber(sysno)) {
// Allow (and count) all other system calls.
return Sandbox::UnsafeTrap(CountSyscalls, aux);
} else {
return ErrorCode(ENOSYS);
}
}
BPF_TEST(SandboxBpf, GreyListedPolicy,
GreyListedPolicy, int /* BPF_AUX */) {
BPF_ASSERT(syscall(__NR_getpid) == -1);
BPF_ASSERT(errno == EPERM);
BPF_ASSERT(BPF_AUX == 0);
BPF_ASSERT(syscall(__NR_geteuid) == syscall(__NR_getuid));
BPF_ASSERT(BPF_AUX == 2);
char name[17] = { };
BPF_ASSERT(!syscall(__NR_prctl, PR_GET_NAME, name, (void *)NULL,
(void *)NULL, (void *)NULL));
BPF_ASSERT(BPF_AUX == 3);
BPF_ASSERT(*name);
}
intptr_t PrctlHandler(const struct arch_seccomp_data& args, void *) {
if (args.args[0] == PR_CAPBSET_DROP &&
static_cast<int>(args.args[1]) == -1) {
// prctl(PR_CAPBSET_DROP, -1) is never valid. The kernel will always
// return an error. But our handler allows this call.
return 0;
} else {
return Sandbox::ForwardSyscall(args);
}
}
ErrorCode PrctlPolicy(int sysno, void *aux) {
Die::SuppressInfoMessages(true);
if (sysno == __NR_prctl) {
// Handle prctl() inside an UnsafeTrap()
return Sandbox::UnsafeTrap(PrctlHandler, NULL);
} else if (Sandbox::isValidSyscallNumber(sysno)) {
// Allow all other system calls.
return ErrorCode(ErrorCode::ERR_ALLOWED);
} else {
return ErrorCode(ENOSYS);
}
}
BPF_TEST(SandboxBpf, ForwardSyscall, PrctlPolicy) {
// This call should never be allowed. But our policy will intercept it and
// let it pass successfully.
BPF_ASSERT(!prctl(PR_CAPBSET_DROP, -1, (void *)NULL, (void *)NULL,
(void *)NULL));
// Verify that the call will fail, if it makes it all the way to the kernel.
BPF_ASSERT(prctl(PR_CAPBSET_DROP, -2, (void *)NULL, (void *)NULL,
(void *)NULL) == -1);
// And verify that other uses of prctl() work just fine.
char name[17] = { };
BPF_ASSERT(!syscall(__NR_prctl, PR_GET_NAME, name, (void *)NULL,
(void *)NULL, (void *)NULL));
BPF_ASSERT(*name);
// Finally, verify that system calls other than prctl() are completely
// unaffected by our policy.
struct utsname uts = { };
BPF_ASSERT(!uname(&uts));
BPF_ASSERT(!strcmp(uts.sysname, "Linux"));
}
intptr_t AllowRedirectedSyscall(const struct arch_seccomp_data& args, void *) {
return Sandbox::ForwardSyscall(args);
}
ErrorCode RedirectAllSyscallsPolicy(int sysno, void *aux) {
Die::SuppressInfoMessages(true);
// Some system calls must always be allowed, if our policy wants to make
// use of UnsafeTrap()
if (sysno == __NR_rt_sigprocmask ||
sysno == __NR_rt_sigreturn
#if defined(__NR_sigprocmask)
|| sysno == __NR_sigprocmask
#endif
#if defined(__NR_sigreturn)
|| sysno == __NR_sigreturn
#endif
) {
return ErrorCode(ErrorCode::ERR_ALLOWED);
} else if (Sandbox::isValidSyscallNumber(sysno)) {
return Sandbox::UnsafeTrap(AllowRedirectedSyscall, aux);
} else {
return ErrorCode(ENOSYS);
}
}
int bus_handler_fd_ = -1;
void SigBusHandler(int, siginfo_t *info, void *void_context) {
BPF_ASSERT(write(bus_handler_fd_, "\x55", 1) == 1);
}
BPF_TEST(SandboxBpf, SigBus, RedirectAllSyscallsPolicy) {
// We use the SIGBUS bit in the signal mask as a thread-local boolean
// value in the implementation of UnsafeTrap(). This is obviously a bit
// of a hack that could conceivably interfere with code that uses SIGBUS
// in more traditional ways. This test verifies that basic functionality
// of SIGBUS is not impacted, but it is certainly possibly to construe
// more complex uses of signals where our use of the SIGBUS mask is not
// 100% transparent. This is expected behavior.
int fds[2];
BPF_ASSERT(pipe(fds) == 0);
bus_handler_fd_ = fds[1];
struct sigaction sa = { };
sa.sa_sigaction = SigBusHandler;
sa.sa_flags = SA_SIGINFO;
BPF_ASSERT(sigaction(SIGBUS, &sa, NULL) == 0);
raise(SIGBUS);
char c = '\000';
BPF_ASSERT(read(fds[0], &c, 1) == 1);
BPF_ASSERT(close(fds[0]) == 0);
BPF_ASSERT(close(fds[1]) == 0);
BPF_ASSERT(c == 0x55);
}
BPF_TEST(SandboxBpf, SigMask, RedirectAllSyscallsPolicy) {
// Signal masks are potentially tricky to handle. For instance, if we
// ever tried to update them from inside a Trap() or UnsafeTrap() handler,
// the call to sigreturn() at the end of the signal handler would undo
// all of our efforts. So, it makes sense to test that sigprocmask()
// works, even if we have a policy in place that makes use of UnsafeTrap().
// In practice, this works because we force sigprocmask() to be handled
// entirely in the kernel.
sigset_t mask0, mask1, mask2;
// Call sigprocmask() to verify that SIGUSR1 wasn't blocked, if we didn't
// change the mask (it shouldn't have been, as it isn't blocked by default
// in POSIX).
sigemptyset(&mask0);
BPF_ASSERT(!sigprocmask(SIG_BLOCK, &mask0, &mask1));
BPF_ASSERT(!sigismember(&mask1, SIGUSR1));
// Try again, and this time we verify that we can block it. This
// requires a second call to sigprocmask().
sigaddset(&mask0, SIGUSR1);
BPF_ASSERT(!sigprocmask(SIG_BLOCK, &mask0, NULL));
BPF_ASSERT(!sigprocmask(SIG_BLOCK, NULL, &mask2));
BPF_ASSERT( sigismember(&mask2, SIGUSR1));
}
BPF_TEST(SandboxBpf, UnsafeTrapWithErrno, RedirectAllSyscallsPolicy) {
// An UnsafeTrap() (or for that matter, a Trap()) has to report error
// conditions by returning an exit code in the range -1..-4096. This
// should happen automatically if using ForwardSyscall(). If the TrapFnc()
// uses some other method to make system calls, then it is responsible
// for computing the correct return code.
// This test verifies that ForwardSyscall() does the correct thing.
// The glibc system wrapper will ultimately set errno for us. So, from normal
// userspace, all of this should be completely transparent.
errno = 0;
BPF_ASSERT(close(-1) == -1);
BPF_ASSERT(errno == EBADF);
// Explicitly avoid the glibc wrapper. This is not normally the way anybody
// would make system calls, but it allows us to verify that we don't
// accidentally mess with errno, when we shouldn't.
errno = 0;
struct arch_seccomp_data args = { };
args.nr = __NR_close;
args.args[0] = -1;
BPF_ASSERT(Sandbox::ForwardSyscall(args) == -EBADF);
BPF_ASSERT(errno == 0);
}
// Test a trap handler that makes use of a broker process to open().
class InitializedOpenBroker {
public:
InitializedOpenBroker() : initialized_(false) {
std::vector<std::string> allowed_files;
allowed_files.push_back("/proc/allowed");
allowed_files.push_back("/proc/cpuinfo");
broker_process_.reset(new BrokerProcess(allowed_files,
std::vector<std::string>()));
BPF_ASSERT(broker_process() != NULL);
BPF_ASSERT(broker_process_->Init(NULL));
initialized_ = true;
}
bool initialized() { return initialized_; }
class BrokerProcess* broker_process() { return broker_process_.get(); }
private:
bool initialized_;
scoped_ptr<class BrokerProcess> broker_process_;
DISALLOW_COPY_AND_ASSIGN(InitializedOpenBroker);
};
intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args,
void *aux) {
BPF_ASSERT(aux);
BrokerProcess* broker_process = static_cast<BrokerProcess*>(aux);
switch(args.nr) {
case __NR_open:
return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
static_cast<int>(args.args[1]));
case __NR_openat:
// We only call open() so if we arrive here, it's because glibc uses
// the openat() system call.
BPF_ASSERT(static_cast<int>(args.args[0]) == AT_FDCWD);
return broker_process->Open(reinterpret_cast<const char*>(args.args[1]),
static_cast<int>(args.args[2]));
default:
BPF_ASSERT(false);
return -ENOSYS;
}
}
ErrorCode DenyOpenPolicy(int sysno, void *aux) {
InitializedOpenBroker* iob = static_cast<InitializedOpenBroker*>(aux);
if (!Sandbox::isValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
}
switch (sysno) {
case __NR_open:
case __NR_openat:
// We get a InitializedOpenBroker class, but our trap handler wants
// the BrokerProcess object.
return ErrorCode(Sandbox::Trap(BrokerOpenTrapHandler,
iob->broker_process()));
default:
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
// We use a InitializedOpenBroker class, so that we can run unsandboxed
// code in its constructor, which is the only way to do so in a BPF_TEST.
BPF_TEST(SandboxBpf, UseOpenBroker, DenyOpenPolicy,
InitializedOpenBroker /* BPF_AUX */) {
BPF_ASSERT(BPF_AUX.initialized());
BrokerProcess* broker_process = BPF_AUX.broker_process();
BPF_ASSERT(broker_process != NULL);
// First, use the broker "manually"
BPF_ASSERT(broker_process->Open("/proc/denied", O_RDONLY) == -EPERM);
BPF_ASSERT(broker_process->Open("/proc/allowed", O_RDONLY) == -ENOENT);
// Now use glibc's open() as an external library would.
BPF_ASSERT(open("/proc/denied", O_RDONLY) == -1);
BPF_ASSERT(errno == EPERM);
BPF_ASSERT(open("/proc/allowed", O_RDONLY) == -1);
BPF_ASSERT(errno == ENOENT);
// Also test glibc's openat(), some versions of libc use it transparently
// instead of open().
BPF_ASSERT(openat(AT_FDCWD, "/proc/denied", O_RDONLY) == -1);
BPF_ASSERT(errno == EPERM);
BPF_ASSERT(openat(AT_FDCWD, "/proc/allowed", O_RDONLY) == -1);
BPF_ASSERT(errno == ENOENT);
// This is also white listed and does exist.
int cpu_info_fd = open("/proc/cpuinfo", O_RDONLY);
BPF_ASSERT(cpu_info_fd >= 0);
char buf[1024];
BPF_ASSERT(read(cpu_info_fd, buf, sizeof(buf)) > 0);
}
} // namespace
|