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 "testing/gtest/include/gtest/gtest.h"
31 #define CASES SANDBOX_BPF_DSL_CASES
37 // Helper function to construct fake arch_seccomp_data objects.
38 struct arch_seccomp_data
FakeSyscall(int nr
,
45 // Made up program counter for syscall address.
46 const uint64_t kFakePC
= 0x543210;
48 struct arch_seccomp_data data
= {
53 p0
, p1
, p2
, p3
, p4
, p5
,
60 class FakeTrapRegistry
: public TrapRegistry
{
62 FakeTrapRegistry() : map_() {}
63 virtual ~FakeTrapRegistry() {}
65 uint16_t Add(TrapFnc fnc
, const void* aux
, bool safe
) override
{
68 const uint16_t next_id
= map_
.size() + 1;
69 return map_
.insert(std::make_pair(Key(fnc
, aux
), next_id
)).first
->second
;
72 bool EnableUnsafeTraps() override
{
73 ADD_FAILURE() << "Unimplemented";
78 using Key
= std::pair
<TrapFnc
, const void*>;
80 std::map
<Key
, uint16_t> map_
;
82 DISALLOW_COPY_AND_ASSIGN(FakeTrapRegistry
);
85 intptr_t FakeTrapFuncOne(const arch_seccomp_data
& data
, void* aux
) { return 1; }
86 intptr_t FakeTrapFuncTwo(const arch_seccomp_data
& data
, void* aux
) { return 2; }
88 // Test that FakeTrapRegistry correctly assigns trap IDs to trap handlers.
89 TEST(FakeTrapRegistry
, TrapIDs
) {
91 TrapRegistry::TrapFnc fnc
;
94 {FakeTrapFuncOne
, nullptr},
95 {FakeTrapFuncTwo
, nullptr},
96 {FakeTrapFuncOne
, funcs
},
97 {FakeTrapFuncTwo
, funcs
},
100 FakeTrapRegistry traps
;
102 // Add traps twice to test that IDs are reused correctly.
103 for (int i
= 0; i
< 2; ++i
) {
104 for (size_t j
= 0; j
< arraysize(funcs
); ++j
) {
105 // Trap IDs start at 1.
106 EXPECT_EQ(j
+ 1, traps
.Add(funcs
[j
].fnc
, funcs
[j
].aux
, true));
111 class PolicyEmulator
{
113 explicit PolicyEmulator(const Policy
* policy
) : program_(), traps_() {
114 program_
= *PolicyCompiler(policy
, &traps_
).Compile(true /* verify */);
118 uint32_t Emulate(const struct arch_seccomp_data
& data
) const {
119 const char* err
= nullptr;
120 uint32_t res
= Verifier::EvaluateBPF(program_
, data
, &err
);
122 ADD_FAILURE() << err
;
128 void ExpectAllow(const struct arch_seccomp_data
& data
) const {
129 EXPECT_EQ(SECCOMP_RET_ALLOW
, Emulate(data
));
132 void ExpectErrno(uint16_t err
, const struct arch_seccomp_data
& data
) const {
133 EXPECT_EQ(SECCOMP_RET_ERRNO
| err
, Emulate(data
));
137 CodeGen::Program program_
;
138 FakeTrapRegistry traps_
;
140 DISALLOW_COPY_AND_ASSIGN(PolicyEmulator
);
143 class BasicPolicy
: public Policy
{
146 ~BasicPolicy() override
{}
147 ResultExpr
EvaluateSyscall(int sysno
) const override
{
148 if (sysno
== __NR_getpgid
) {
149 const Arg
<pid_t
> pid(0);
150 return If(pid
== 0, Error(EPERM
)).Else(Error(EINVAL
));
152 if (sysno
== __NR_setuid
) {
153 const Arg
<uid_t
> uid(0);
154 return If(uid
!= 42, Error(ESRCH
)).Else(Error(ENOMEM
));
160 DISALLOW_COPY_AND_ASSIGN(BasicPolicy
);
163 TEST(BPFDSL
, Basic
) {
165 PolicyEmulator
emulator(&policy
);
167 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_getpgid
, 0));
168 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_getpgid
, 1));
170 emulator
.ExpectErrno(ENOMEM
, FakeSyscall(__NR_setuid
, 42));
171 emulator
.ExpectErrno(ESRCH
, FakeSyscall(__NR_setuid
, 43));
174 /* On IA-32, socketpair() is implemented via socketcall(). :-( */
175 #if !defined(ARCH_CPU_X86)
176 class BooleanLogicPolicy
: public Policy
{
178 BooleanLogicPolicy() {}
179 ~BooleanLogicPolicy() override
{}
180 ResultExpr
EvaluateSyscall(int sysno
) const override
{
181 if (sysno
== __NR_socketpair
) {
182 const Arg
<int> domain(0), type(1), protocol(2);
183 return If(domain
== AF_UNIX
&&
184 (type
== SOCK_STREAM
|| type
== SOCK_DGRAM
) &&
186 Error(EPERM
)).Else(Error(EINVAL
));
192 DISALLOW_COPY_AND_ASSIGN(BooleanLogicPolicy
);
195 TEST(BPFDSL
, BooleanLogic
) {
196 BooleanLogicPolicy policy
;
197 PolicyEmulator
emulator(&policy
);
199 const intptr_t kFakeSV
= 0x12345;
201 // Acceptable combinations that should return EPERM.
202 emulator
.ExpectErrno(
203 EPERM
, FakeSyscall(__NR_socketpair
, AF_UNIX
, SOCK_STREAM
, 0, kFakeSV
));
204 emulator
.ExpectErrno(
205 EPERM
, FakeSyscall(__NR_socketpair
, AF_UNIX
, SOCK_DGRAM
, 0, kFakeSV
));
207 // Combinations that are invalid for only one reason; should return EINVAL.
208 emulator
.ExpectErrno(
209 EINVAL
, FakeSyscall(__NR_socketpair
, AF_INET
, SOCK_STREAM
, 0, kFakeSV
));
210 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_socketpair
, AF_UNIX
,
211 SOCK_SEQPACKET
, 0, kFakeSV
));
212 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_socketpair
, AF_UNIX
,
213 SOCK_STREAM
, IPPROTO_TCP
, kFakeSV
));
215 // Completely unacceptable combination; should also return EINVAL.
216 emulator
.ExpectErrno(
217 EINVAL
, FakeSyscall(__NR_socketpair
, AF_INET
, SOCK_SEQPACKET
, IPPROTO_UDP
,
220 #endif // !ARCH_CPU_X86
222 class MoreBooleanLogicPolicy
: public Policy
{
224 MoreBooleanLogicPolicy() {}
225 ~MoreBooleanLogicPolicy() override
{}
226 ResultExpr
EvaluateSyscall(int sysno
) const override
{
227 if (sysno
== __NR_setresuid
) {
228 const Arg
<uid_t
> ruid(0), euid(1), suid(2);
229 return If(ruid
== 0 || euid
== 0 || suid
== 0, Error(EPERM
))
230 .ElseIf(ruid
== 1 && euid
== 1 && suid
== 1, Error(EAGAIN
))
231 .Else(Error(EINVAL
));
237 DISALLOW_COPY_AND_ASSIGN(MoreBooleanLogicPolicy
);
240 TEST(BPFDSL
, MoreBooleanLogic
) {
241 MoreBooleanLogicPolicy policy
;
242 PolicyEmulator
emulator(&policy
);
244 // Expect EPERM if any set to 0.
245 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_setresuid
, 0, 5, 5));
246 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_setresuid
, 5, 0, 5));
247 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_setresuid
, 5, 5, 0));
249 // Expect EAGAIN if all set to 1.
250 emulator
.ExpectErrno(EAGAIN
, FakeSyscall(__NR_setresuid
, 1, 1, 1));
252 // Expect EINVAL for anything else.
253 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setresuid
, 5, 1, 1));
254 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setresuid
, 1, 5, 1));
255 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setresuid
, 1, 1, 5));
256 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setresuid
, 3, 4, 5));
259 static const uintptr_t kDeadBeefAddr
=
260 static_cast<uintptr_t>(0xdeadbeefdeadbeefULL
);
262 class ArgSizePolicy
: public Policy
{
265 ~ArgSizePolicy() override
{}
266 ResultExpr
EvaluateSyscall(int sysno
) const override
{
267 if (sysno
== __NR_uname
) {
268 const Arg
<uintptr_t> addr(0);
269 return If(addr
== kDeadBeefAddr
, Error(EPERM
)).Else(Allow());
275 DISALLOW_COPY_AND_ASSIGN(ArgSizePolicy
);
278 TEST(BPFDSL
, ArgSizeTest
) {
279 ArgSizePolicy policy
;
280 PolicyEmulator
emulator(&policy
);
282 emulator
.ExpectAllow(FakeSyscall(__NR_uname
, 0));
283 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_uname
, kDeadBeefAddr
));
287 // TODO(mdempsky): This is really an integration test.
289 class TrappingPolicy
: public Policy
{
292 ~TrappingPolicy() override
{}
293 ResultExpr
EvaluateSyscall(int sysno
) const override
{
294 if (sysno
== __NR_uname
) {
295 return Trap(UnameTrap
, &count_
);
301 static intptr_t count_
;
303 static intptr_t UnameTrap(const struct arch_seccomp_data
& data
, void* aux
) {
304 BPF_ASSERT_EQ(&count_
, aux
);
308 DISALLOW_COPY_AND_ASSIGN(TrappingPolicy
);
311 intptr_t TrappingPolicy::count_
;
313 BPF_TEST_C(BPFDSL
, TrapTest
, TrappingPolicy
) {
314 ASSERT_SYSCALL_RESULT(1, uname
, NULL
);
315 ASSERT_SYSCALL_RESULT(2, uname
, NULL
);
316 ASSERT_SYSCALL_RESULT(3, uname
, NULL
);
320 class MaskingPolicy
: public Policy
{
323 ~MaskingPolicy() override
{}
324 ResultExpr
EvaluateSyscall(int sysno
) const override
{
325 if (sysno
== __NR_setuid
) {
326 const Arg
<uid_t
> uid(0);
327 return If((uid
& 0xf) == 0, Error(EINVAL
)).Else(Error(EACCES
));
329 if (sysno
== __NR_setgid
) {
330 const Arg
<gid_t
> gid(0);
331 return If((gid
& 0xf0) == 0xf0, Error(EINVAL
)).Else(Error(EACCES
));
333 if (sysno
== __NR_setpgid
) {
334 const Arg
<pid_t
> pid(0);
335 return If((pid
& 0xa5) == 0xa0, Error(EINVAL
)).Else(Error(EACCES
));
341 DISALLOW_COPY_AND_ASSIGN(MaskingPolicy
);
344 TEST(BPFDSL
, MaskTest
) {
345 MaskingPolicy policy
;
346 PolicyEmulator
emulator(&policy
);
348 for (uid_t uid
= 0; uid
< 0x100; ++uid
) {
349 const int expect_errno
= (uid
& 0xf) == 0 ? EINVAL
: EACCES
;
350 emulator
.ExpectErrno(expect_errno
, FakeSyscall(__NR_setuid
, uid
));
353 for (gid_t gid
= 0; gid
< 0x100; ++gid
) {
354 const int expect_errno
= (gid
& 0xf0) == 0xf0 ? EINVAL
: EACCES
;
355 emulator
.ExpectErrno(expect_errno
, FakeSyscall(__NR_setgid
, gid
));
358 for (pid_t pid
= 0; pid
< 0x100; ++pid
) {
359 const int expect_errno
= (pid
& 0xa5) == 0xa0 ? EINVAL
: EACCES
;
360 emulator
.ExpectErrno(expect_errno
, FakeSyscall(__NR_setpgid
, pid
, 0));
364 class ElseIfPolicy
: public Policy
{
367 ~ElseIfPolicy() override
{}
368 ResultExpr
EvaluateSyscall(int sysno
) const override
{
369 if (sysno
== __NR_setuid
) {
370 const Arg
<uid_t
> uid(0);
371 return If((uid
& 0xfff) == 0, Error(0))
372 .ElseIf((uid
& 0xff0) == 0, Error(EINVAL
))
373 .ElseIf((uid
& 0xf00) == 0, Error(EEXIST
))
374 .Else(Error(EACCES
));
380 DISALLOW_COPY_AND_ASSIGN(ElseIfPolicy
);
383 TEST(BPFDSL
, ElseIfTest
) {
385 PolicyEmulator
emulator(&policy
);
387 emulator
.ExpectErrno(0, FakeSyscall(__NR_setuid
, 0));
389 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setuid
, 0x0001));
390 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setuid
, 0x0002));
392 emulator
.ExpectErrno(EEXIST
, FakeSyscall(__NR_setuid
, 0x0011));
393 emulator
.ExpectErrno(EEXIST
, FakeSyscall(__NR_setuid
, 0x0022));
395 emulator
.ExpectErrno(EACCES
, FakeSyscall(__NR_setuid
, 0x0111));
396 emulator
.ExpectErrno(EACCES
, FakeSyscall(__NR_setuid
, 0x0222));
399 class SwitchPolicy
: public Policy
{
402 ~SwitchPolicy() override
{}
403 ResultExpr
EvaluateSyscall(int sysno
) const override
{
404 if (sysno
== __NR_fcntl
) {
405 const Arg
<int> cmd(1);
406 const Arg
<unsigned long> long_arg(2);
408 .CASES((F_GETFL
, F_GETFD
), Error(ENOENT
))
409 .Case(F_SETFD
, If(long_arg
== O_CLOEXEC
, Allow()).Else(Error(EINVAL
)))
410 .Case(F_SETFL
, Error(EPERM
))
411 .Default(Error(EACCES
));
417 DISALLOW_COPY_AND_ASSIGN(SwitchPolicy
);
420 TEST(BPFDSL
, SwitchTest
) {
422 PolicyEmulator
emulator(&policy
);
424 const int kFakeSockFD
= 42;
426 emulator
.ExpectErrno(ENOENT
, FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_GETFD
));
427 emulator
.ExpectErrno(ENOENT
, FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_GETFL
));
429 emulator
.ExpectAllow(
430 FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_SETFD
, O_CLOEXEC
));
431 emulator
.ExpectErrno(EINVAL
,
432 FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_SETFD
, 0));
434 emulator
.ExpectErrno(EPERM
,
435 FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_SETFL
, O_RDONLY
));
437 emulator
.ExpectErrno(EACCES
,
438 FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_DUPFD
, 0));
441 static intptr_t DummyTrap(const struct arch_seccomp_data
& data
, void* aux
) {
445 TEST(BPFDSL
, IsAllowDeny
) {
446 ResultExpr allow
= Allow();
447 EXPECT_TRUE(allow
->IsAllow());
448 EXPECT_FALSE(allow
->IsDeny());
450 ResultExpr error
= Error(ENOENT
);
451 EXPECT_FALSE(error
->IsAllow());
452 EXPECT_TRUE(error
->IsDeny());
454 ResultExpr trace
= Trace(42);
455 EXPECT_FALSE(trace
->IsAllow());
456 EXPECT_FALSE(trace
->IsDeny());
458 ResultExpr trap
= Trap(DummyTrap
, nullptr);
459 EXPECT_FALSE(trap
->IsAllow());
460 EXPECT_TRUE(trap
->IsDeny());
462 const Arg
<int> arg(0);
463 ResultExpr maybe
= If(arg
== 0, Allow()).Else(Error(EPERM
));
464 EXPECT_FALSE(maybe
->IsAllow());
465 EXPECT_FALSE(maybe
->IsDeny());
468 TEST(BPFDSL
, HasUnsafeTraps
) {
469 ResultExpr allow
= Allow();
470 EXPECT_FALSE(allow
->HasUnsafeTraps());
472 ResultExpr safe
= Trap(DummyTrap
, nullptr);
473 EXPECT_FALSE(safe
->HasUnsafeTraps());
475 ResultExpr unsafe
= UnsafeTrap(DummyTrap
, nullptr);
476 EXPECT_TRUE(unsafe
->HasUnsafeTraps());
478 const Arg
<int> arg(0);
479 ResultExpr maybe
= If(arg
== 0, allow
).Else(unsafe
);
480 EXPECT_TRUE(maybe
->HasUnsafeTraps());
484 } // namespace bpf_dsl
485 } // namespace sandbox