1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
9 #include <netinet/in.h>
10 #include <sys/socket.h>
11 #include <sys/syscall.h>
12 #include <sys/utsname.h>
18 #include "base/files/scoped_file.h"
19 #include "base/macros.h"
20 #include "build/build_config.h"
21 #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
22 #include "sandbox/linux/bpf_dsl/codegen.h"
23 #include "sandbox/linux/bpf_dsl/policy.h"
24 #include "sandbox/linux/bpf_dsl/policy_compiler.h"
25 #include "sandbox/linux/bpf_dsl/seccomp_macros.h"
26 #include "sandbox/linux/bpf_dsl/trap_registry.h"
27 #include "sandbox/linux/bpf_dsl/verifier.h"
28 #include "sandbox/linux/seccomp-bpf/errorcode.h"
29 #include "sandbox/linux/system_headers/linux_filter.h"
30 #include "testing/gtest/include/gtest/gtest.h"
32 #define CASES SANDBOX_BPF_DSL_CASES
38 // Helper function to construct fake arch_seccomp_data objects.
39 struct arch_seccomp_data
FakeSyscall(int nr
,
46 // Made up program counter for syscall address.
47 const uint64_t kFakePC
= 0x543210;
49 struct arch_seccomp_data data
= {
54 p0
, p1
, p2
, p3
, p4
, p5
,
61 class FakeTrapRegistry
: public TrapRegistry
{
63 FakeTrapRegistry() : map_() {}
64 virtual ~FakeTrapRegistry() {}
66 uint16_t Add(TrapFnc fnc
, const void* aux
, bool safe
) override
{
69 const uint16_t next_id
= map_
.size() + 1;
70 return map_
.insert(std::make_pair(Key(fnc
, aux
), next_id
)).first
->second
;
73 bool EnableUnsafeTraps() override
{
74 ADD_FAILURE() << "Unimplemented";
79 using Key
= std::pair
<TrapFnc
, const void*>;
81 std::map
<Key
, uint16_t> map_
;
83 DISALLOW_COPY_AND_ASSIGN(FakeTrapRegistry
);
86 intptr_t FakeTrapFuncOne(const arch_seccomp_data
& data
, void* aux
) { return 1; }
87 intptr_t FakeTrapFuncTwo(const arch_seccomp_data
& data
, void* aux
) { return 2; }
89 // Test that FakeTrapRegistry correctly assigns trap IDs to trap handlers.
90 TEST(FakeTrapRegistry
, TrapIDs
) {
92 TrapRegistry::TrapFnc fnc
;
95 {FakeTrapFuncOne
, nullptr},
96 {FakeTrapFuncTwo
, nullptr},
97 {FakeTrapFuncOne
, funcs
},
98 {FakeTrapFuncTwo
, funcs
},
101 FakeTrapRegistry traps
;
103 // Add traps twice to test that IDs are reused correctly.
104 for (int i
= 0; i
< 2; ++i
) {
105 for (size_t j
= 0; j
< arraysize(funcs
); ++j
) {
106 // Trap IDs start at 1.
107 EXPECT_EQ(j
+ 1, traps
.Add(funcs
[j
].fnc
, funcs
[j
].aux
, true));
112 class PolicyEmulator
{
114 explicit PolicyEmulator(const Policy
* policy
) : program_(), traps_() {
115 program_
= *PolicyCompiler(policy
, &traps_
).Compile(true /* verify */);
119 uint32_t Emulate(const struct arch_seccomp_data
& data
) const {
120 const char* err
= nullptr;
121 uint32_t res
= Verifier::EvaluateBPF(program_
, data
, &err
);
123 ADD_FAILURE() << err
;
129 void ExpectAllow(const struct arch_seccomp_data
& data
) const {
130 EXPECT_EQ(SECCOMP_RET_ALLOW
, Emulate(data
));
133 void ExpectErrno(uint16_t err
, const struct arch_seccomp_data
& data
) const {
134 EXPECT_EQ(SECCOMP_RET_ERRNO
| err
, Emulate(data
));
138 CodeGen::Program program_
;
139 FakeTrapRegistry traps_
;
141 DISALLOW_COPY_AND_ASSIGN(PolicyEmulator
);
144 class BasicPolicy
: public Policy
{
147 ~BasicPolicy() override
{}
148 ResultExpr
EvaluateSyscall(int sysno
) const override
{
149 if (sysno
== __NR_getpgid
) {
150 const Arg
<pid_t
> pid(0);
151 return If(pid
== 0, Error(EPERM
)).Else(Error(EINVAL
));
153 if (sysno
== __NR_setuid
) {
154 const Arg
<uid_t
> uid(0);
155 return If(uid
!= 42, Error(ESRCH
)).Else(Error(ENOMEM
));
161 DISALLOW_COPY_AND_ASSIGN(BasicPolicy
);
164 TEST(BPFDSL
, Basic
) {
166 PolicyEmulator
emulator(&policy
);
168 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_getpgid
, 0));
169 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_getpgid
, 1));
171 emulator
.ExpectErrno(ENOMEM
, FakeSyscall(__NR_setuid
, 42));
172 emulator
.ExpectErrno(ESRCH
, FakeSyscall(__NR_setuid
, 43));
175 /* On IA-32, socketpair() is implemented via socketcall(). :-( */
176 #if !defined(ARCH_CPU_X86)
177 class BooleanLogicPolicy
: public Policy
{
179 BooleanLogicPolicy() {}
180 ~BooleanLogicPolicy() override
{}
181 ResultExpr
EvaluateSyscall(int sysno
) const override
{
182 if (sysno
== __NR_socketpair
) {
183 const Arg
<int> domain(0), type(1), protocol(2);
184 return If(domain
== AF_UNIX
&&
185 (type
== SOCK_STREAM
|| type
== SOCK_DGRAM
) &&
187 Error(EPERM
)).Else(Error(EINVAL
));
193 DISALLOW_COPY_AND_ASSIGN(BooleanLogicPolicy
);
196 TEST(BPFDSL
, BooleanLogic
) {
197 BooleanLogicPolicy policy
;
198 PolicyEmulator
emulator(&policy
);
200 const intptr_t kFakeSV
= 0x12345;
202 // Acceptable combinations that should return EPERM.
203 emulator
.ExpectErrno(
204 EPERM
, FakeSyscall(__NR_socketpair
, AF_UNIX
, SOCK_STREAM
, 0, kFakeSV
));
205 emulator
.ExpectErrno(
206 EPERM
, FakeSyscall(__NR_socketpair
, AF_UNIX
, SOCK_DGRAM
, 0, kFakeSV
));
208 // Combinations that are invalid for only one reason; should return EINVAL.
209 emulator
.ExpectErrno(
210 EINVAL
, FakeSyscall(__NR_socketpair
, AF_INET
, SOCK_STREAM
, 0, kFakeSV
));
211 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_socketpair
, AF_UNIX
,
212 SOCK_SEQPACKET
, 0, kFakeSV
));
213 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_socketpair
, AF_UNIX
,
214 SOCK_STREAM
, IPPROTO_TCP
, kFakeSV
));
216 // Completely unacceptable combination; should also return EINVAL.
217 emulator
.ExpectErrno(
218 EINVAL
, FakeSyscall(__NR_socketpair
, AF_INET
, SOCK_SEQPACKET
, IPPROTO_UDP
,
221 #endif // !ARCH_CPU_X86
223 class MoreBooleanLogicPolicy
: public Policy
{
225 MoreBooleanLogicPolicy() {}
226 ~MoreBooleanLogicPolicy() override
{}
227 ResultExpr
EvaluateSyscall(int sysno
) const override
{
228 if (sysno
== __NR_setresuid
) {
229 const Arg
<uid_t
> ruid(0), euid(1), suid(2);
230 return If(ruid
== 0 || euid
== 0 || suid
== 0, Error(EPERM
))
231 .ElseIf(ruid
== 1 && euid
== 1 && suid
== 1, Error(EAGAIN
))
232 .Else(Error(EINVAL
));
238 DISALLOW_COPY_AND_ASSIGN(MoreBooleanLogicPolicy
);
241 TEST(BPFDSL
, MoreBooleanLogic
) {
242 MoreBooleanLogicPolicy policy
;
243 PolicyEmulator
emulator(&policy
);
245 // Expect EPERM if any set to 0.
246 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_setresuid
, 0, 5, 5));
247 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_setresuid
, 5, 0, 5));
248 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_setresuid
, 5, 5, 0));
250 // Expect EAGAIN if all set to 1.
251 emulator
.ExpectErrno(EAGAIN
, FakeSyscall(__NR_setresuid
, 1, 1, 1));
253 // Expect EINVAL for anything else.
254 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setresuid
, 5, 1, 1));
255 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setresuid
, 1, 5, 1));
256 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setresuid
, 1, 1, 5));
257 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setresuid
, 3, 4, 5));
260 static const uintptr_t kDeadBeefAddr
=
261 static_cast<uintptr_t>(0xdeadbeefdeadbeefULL
);
263 class ArgSizePolicy
: public Policy
{
266 ~ArgSizePolicy() override
{}
267 ResultExpr
EvaluateSyscall(int sysno
) const override
{
268 if (sysno
== __NR_uname
) {
269 const Arg
<uintptr_t> addr(0);
270 return If(addr
== kDeadBeefAddr
, Error(EPERM
)).Else(Allow());
276 DISALLOW_COPY_AND_ASSIGN(ArgSizePolicy
);
279 TEST(BPFDSL
, ArgSizeTest
) {
280 ArgSizePolicy policy
;
281 PolicyEmulator
emulator(&policy
);
283 emulator
.ExpectAllow(FakeSyscall(__NR_uname
, 0));
284 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_uname
, kDeadBeefAddr
));
288 // TODO(mdempsky): This is really an integration test.
290 class TrappingPolicy
: public Policy
{
293 ~TrappingPolicy() override
{}
294 ResultExpr
EvaluateSyscall(int sysno
) const override
{
295 if (sysno
== __NR_uname
) {
296 return Trap(UnameTrap
, &count_
);
302 static intptr_t count_
;
304 static intptr_t UnameTrap(const struct arch_seccomp_data
& data
, void* aux
) {
305 BPF_ASSERT_EQ(&count_
, aux
);
309 DISALLOW_COPY_AND_ASSIGN(TrappingPolicy
);
312 intptr_t TrappingPolicy::count_
;
314 BPF_TEST_C(BPFDSL
, TrapTest
, TrappingPolicy
) {
315 ASSERT_SYSCALL_RESULT(1, uname
, NULL
);
316 ASSERT_SYSCALL_RESULT(2, uname
, NULL
);
317 ASSERT_SYSCALL_RESULT(3, uname
, NULL
);
321 class MaskingPolicy
: public Policy
{
324 ~MaskingPolicy() override
{}
325 ResultExpr
EvaluateSyscall(int sysno
) const override
{
326 if (sysno
== __NR_setuid
) {
327 const Arg
<uid_t
> uid(0);
328 return If((uid
& 0xf) == 0, Error(EINVAL
)).Else(Error(EACCES
));
330 if (sysno
== __NR_setgid
) {
331 const Arg
<gid_t
> gid(0);
332 return If((gid
& 0xf0) == 0xf0, Error(EINVAL
)).Else(Error(EACCES
));
334 if (sysno
== __NR_setpgid
) {
335 const Arg
<pid_t
> pid(0);
336 return If((pid
& 0xa5) == 0xa0, Error(EINVAL
)).Else(Error(EACCES
));
342 DISALLOW_COPY_AND_ASSIGN(MaskingPolicy
);
345 TEST(BPFDSL
, MaskTest
) {
346 MaskingPolicy policy
;
347 PolicyEmulator
emulator(&policy
);
349 for (uid_t uid
= 0; uid
< 0x100; ++uid
) {
350 const int expect_errno
= (uid
& 0xf) == 0 ? EINVAL
: EACCES
;
351 emulator
.ExpectErrno(expect_errno
, FakeSyscall(__NR_setuid
, uid
));
354 for (gid_t gid
= 0; gid
< 0x100; ++gid
) {
355 const int expect_errno
= (gid
& 0xf0) == 0xf0 ? EINVAL
: EACCES
;
356 emulator
.ExpectErrno(expect_errno
, FakeSyscall(__NR_setgid
, gid
));
359 for (pid_t pid
= 0; pid
< 0x100; ++pid
) {
360 const int expect_errno
= (pid
& 0xa5) == 0xa0 ? EINVAL
: EACCES
;
361 emulator
.ExpectErrno(expect_errno
, FakeSyscall(__NR_setpgid
, pid
, 0));
365 class ElseIfPolicy
: public Policy
{
368 ~ElseIfPolicy() override
{}
369 ResultExpr
EvaluateSyscall(int sysno
) const override
{
370 if (sysno
== __NR_setuid
) {
371 const Arg
<uid_t
> uid(0);
372 return If((uid
& 0xfff) == 0, Error(0))
373 .ElseIf((uid
& 0xff0) == 0, Error(EINVAL
))
374 .ElseIf((uid
& 0xf00) == 0, Error(EEXIST
))
375 .Else(Error(EACCES
));
381 DISALLOW_COPY_AND_ASSIGN(ElseIfPolicy
);
384 TEST(BPFDSL
, ElseIfTest
) {
386 PolicyEmulator
emulator(&policy
);
388 emulator
.ExpectErrno(0, FakeSyscall(__NR_setuid
, 0));
390 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setuid
, 0x0001));
391 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setuid
, 0x0002));
393 emulator
.ExpectErrno(EEXIST
, FakeSyscall(__NR_setuid
, 0x0011));
394 emulator
.ExpectErrno(EEXIST
, FakeSyscall(__NR_setuid
, 0x0022));
396 emulator
.ExpectErrno(EACCES
, FakeSyscall(__NR_setuid
, 0x0111));
397 emulator
.ExpectErrno(EACCES
, FakeSyscall(__NR_setuid
, 0x0222));
400 class SwitchPolicy
: public Policy
{
403 ~SwitchPolicy() override
{}
404 ResultExpr
EvaluateSyscall(int sysno
) const override
{
405 if (sysno
== __NR_fcntl
) {
406 const Arg
<int> cmd(1);
407 const Arg
<unsigned long> long_arg(2);
409 .CASES((F_GETFL
, F_GETFD
), Error(ENOENT
))
410 .Case(F_SETFD
, If(long_arg
== O_CLOEXEC
, Allow()).Else(Error(EINVAL
)))
411 .Case(F_SETFL
, Error(EPERM
))
412 .Default(Error(EACCES
));
418 DISALLOW_COPY_AND_ASSIGN(SwitchPolicy
);
421 TEST(BPFDSL
, SwitchTest
) {
423 PolicyEmulator
emulator(&policy
);
425 const int kFakeSockFD
= 42;
427 emulator
.ExpectErrno(ENOENT
, FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_GETFD
));
428 emulator
.ExpectErrno(ENOENT
, FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_GETFL
));
430 emulator
.ExpectAllow(
431 FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_SETFD
, O_CLOEXEC
));
432 emulator
.ExpectErrno(EINVAL
,
433 FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_SETFD
, 0));
435 emulator
.ExpectErrno(EPERM
,
436 FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_SETFL
, O_RDONLY
));
438 emulator
.ExpectErrno(EACCES
,
439 FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_DUPFD
, 0));
442 static intptr_t DummyTrap(const struct arch_seccomp_data
& data
, void* aux
) {
446 TEST(BPFDSL
, IsAllowDeny
) {
447 ResultExpr allow
= Allow();
448 EXPECT_TRUE(allow
->IsAllow());
449 EXPECT_FALSE(allow
->IsDeny());
451 ResultExpr error
= Error(ENOENT
);
452 EXPECT_FALSE(error
->IsAllow());
453 EXPECT_TRUE(error
->IsDeny());
455 ResultExpr trace
= Trace(42);
456 EXPECT_FALSE(trace
->IsAllow());
457 EXPECT_FALSE(trace
->IsDeny());
459 ResultExpr trap
= Trap(DummyTrap
, nullptr);
460 EXPECT_FALSE(trap
->IsAllow());
461 EXPECT_TRUE(trap
->IsDeny());
463 const Arg
<int> arg(0);
464 ResultExpr maybe
= If(arg
== 0, Allow()).Else(Error(EPERM
));
465 EXPECT_FALSE(maybe
->IsAllow());
466 EXPECT_FALSE(maybe
->IsDeny());
469 TEST(BPFDSL
, HasUnsafeTraps
) {
470 ResultExpr allow
= Allow();
471 EXPECT_FALSE(allow
->HasUnsafeTraps());
473 ResultExpr safe
= Trap(DummyTrap
, nullptr);
474 EXPECT_FALSE(safe
->HasUnsafeTraps());
476 ResultExpr unsafe
= UnsafeTrap(DummyTrap
, nullptr);
477 EXPECT_TRUE(unsafe
->HasUnsafeTraps());
479 const Arg
<int> arg(0);
480 ResultExpr maybe
= If(arg
== 0, allow
).Else(unsafe
);
481 EXPECT_TRUE(maybe
->HasUnsafeTraps());
485 } // namespace bpf_dsl
486 } // namespace sandbox