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/dump_bpf.h"
24 #include "sandbox/linux/bpf_dsl/golden/golden_files.h"
25 #include "sandbox/linux/bpf_dsl/policy.h"
26 #include "sandbox/linux/bpf_dsl/policy_compiler.h"
27 #include "sandbox/linux/bpf_dsl/seccomp_macros.h"
28 #include "sandbox/linux/bpf_dsl/test_trap_registry.h"
29 #include "sandbox/linux/bpf_dsl/verifier.h"
30 #include "sandbox/linux/system_headers/linux_filter.h"
31 #include "testing/gtest/include/gtest/gtest.h"
33 #define CASES SANDBOX_BPF_DSL_CASES
39 // Helper function to construct fake arch_seccomp_data objects.
40 struct arch_seccomp_data
FakeSyscall(int nr
,
47 // Made up program counter for syscall address.
48 const uint64_t kFakePC
= 0x543210;
50 struct arch_seccomp_data data
= {
55 p0
, p1
, p2
, p3
, p4
, p5
,
62 class PolicyEmulator
{
64 PolicyEmulator(const golden::Golden
& golden
, const Policy
& policy
)
66 TestTrapRegistry traps
;
67 program_
= *PolicyCompiler(&policy
, &traps
).Compile();
69 // TODO(mdempsky): Generalize to more arches.
70 const char* expected
= nullptr;
71 #if defined(ARCH_CPU_X86)
72 expected
= golden
.i386_dump
;
73 #elif defined(ARCH_CPU_X86_64)
74 expected
= golden
.x86_64_dump
;
77 if (expected
!= nullptr) {
78 const std::string actual
= DumpBPF::StringPrintProgram(program_
);
79 EXPECT_EQ(expected
, actual
);
81 LOG(WARNING
) << "Missing golden file data entry";
87 void ExpectAllow(const struct arch_seccomp_data
& data
) const {
88 EXPECT_EQ(SECCOMP_RET_ALLOW
, Emulate(data
));
91 void ExpectErrno(uint16_t err
, const struct arch_seccomp_data
& data
) const {
92 EXPECT_EQ(SECCOMP_RET_ERRNO
| err
, Emulate(data
));
95 void ExpectKill(const struct arch_seccomp_data
& data
) const {
96 EXPECT_EQ(SECCOMP_RET_KILL
, Emulate(data
));
100 uint32_t Emulate(const struct arch_seccomp_data
& data
) const {
101 const char* err
= nullptr;
102 uint32_t res
= Verifier::EvaluateBPF(program_
, data
, &err
);
104 ADD_FAILURE() << err
;
110 CodeGen::Program program_
;
112 DISALLOW_COPY_AND_ASSIGN(PolicyEmulator
);
115 class BasicPolicy
: public Policy
{
118 ~BasicPolicy() override
{}
119 ResultExpr
EvaluateSyscall(int sysno
) const override
{
120 if (sysno
== __NR_getpgid
) {
121 const Arg
<pid_t
> pid(0);
122 return If(pid
== 0, Error(EPERM
)).Else(Error(EINVAL
));
124 if (sysno
== __NR_setuid
) {
125 const Arg
<uid_t
> uid(0);
126 return If(uid
!= 42, Kill()).Else(Allow());
132 DISALLOW_COPY_AND_ASSIGN(BasicPolicy
);
135 TEST(BPFDSL
, Basic
) {
136 PolicyEmulator
emulator(golden::kBasicPolicy
, BasicPolicy());
138 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_getpgid
, 0));
139 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_getpgid
, 1));
141 emulator
.ExpectAllow(FakeSyscall(__NR_setuid
, 42));
142 emulator
.ExpectKill(FakeSyscall(__NR_setuid
, 43));
145 /* On IA-32, socketpair() is implemented via socketcall(). :-( */
146 #if !defined(ARCH_CPU_X86)
147 class BooleanLogicPolicy
: public Policy
{
149 BooleanLogicPolicy() {}
150 ~BooleanLogicPolicy() override
{}
151 ResultExpr
EvaluateSyscall(int sysno
) const override
{
152 if (sysno
== __NR_socketpair
) {
153 const Arg
<int> domain(0), type(1), protocol(2);
154 return If(domain
== AF_UNIX
&&
155 (type
== SOCK_STREAM
|| type
== SOCK_DGRAM
) &&
157 Error(EPERM
)).Else(Error(EINVAL
));
163 DISALLOW_COPY_AND_ASSIGN(BooleanLogicPolicy
);
166 TEST(BPFDSL
, BooleanLogic
) {
167 PolicyEmulator
emulator(golden::kBooleanLogicPolicy
, BooleanLogicPolicy());
169 const intptr_t kFakeSV
= 0x12345;
171 // Acceptable combinations that should return EPERM.
172 emulator
.ExpectErrno(
173 EPERM
, FakeSyscall(__NR_socketpair
, AF_UNIX
, SOCK_STREAM
, 0, kFakeSV
));
174 emulator
.ExpectErrno(
175 EPERM
, FakeSyscall(__NR_socketpair
, AF_UNIX
, SOCK_DGRAM
, 0, kFakeSV
));
177 // Combinations that are invalid for only one reason; should return EINVAL.
178 emulator
.ExpectErrno(
179 EINVAL
, FakeSyscall(__NR_socketpair
, AF_INET
, SOCK_STREAM
, 0, kFakeSV
));
180 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_socketpair
, AF_UNIX
,
181 SOCK_SEQPACKET
, 0, kFakeSV
));
182 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_socketpair
, AF_UNIX
,
183 SOCK_STREAM
, IPPROTO_TCP
, kFakeSV
));
185 // Completely unacceptable combination; should also return EINVAL.
186 emulator
.ExpectErrno(
187 EINVAL
, FakeSyscall(__NR_socketpair
, AF_INET
, SOCK_SEQPACKET
, IPPROTO_UDP
,
190 #endif // !ARCH_CPU_X86
192 class MoreBooleanLogicPolicy
: public Policy
{
194 MoreBooleanLogicPolicy() {}
195 ~MoreBooleanLogicPolicy() override
{}
196 ResultExpr
EvaluateSyscall(int sysno
) const override
{
197 if (sysno
== __NR_setresuid
) {
198 const Arg
<uid_t
> ruid(0), euid(1), suid(2);
199 return If(ruid
== 0 || euid
== 0 || suid
== 0, Error(EPERM
))
200 .ElseIf(ruid
== 1 && euid
== 1 && suid
== 1, Error(EAGAIN
))
201 .Else(Error(EINVAL
));
207 DISALLOW_COPY_AND_ASSIGN(MoreBooleanLogicPolicy
);
210 TEST(BPFDSL
, MoreBooleanLogic
) {
211 PolicyEmulator
emulator(golden::kMoreBooleanLogicPolicy
,
212 MoreBooleanLogicPolicy());
214 // Expect EPERM if any set to 0.
215 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_setresuid
, 0, 5, 5));
216 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_setresuid
, 5, 0, 5));
217 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_setresuid
, 5, 5, 0));
219 // Expect EAGAIN if all set to 1.
220 emulator
.ExpectErrno(EAGAIN
, FakeSyscall(__NR_setresuid
, 1, 1, 1));
222 // Expect EINVAL for anything else.
223 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setresuid
, 5, 1, 1));
224 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setresuid
, 1, 5, 1));
225 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setresuid
, 1, 1, 5));
226 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setresuid
, 3, 4, 5));
229 static const uintptr_t kDeadBeefAddr
=
230 static_cast<uintptr_t>(0xdeadbeefdeadbeefULL
);
232 class ArgSizePolicy
: public Policy
{
235 ~ArgSizePolicy() override
{}
236 ResultExpr
EvaluateSyscall(int sysno
) const override
{
237 if (sysno
== __NR_uname
) {
238 const Arg
<uintptr_t> addr(0);
239 return If(addr
== kDeadBeefAddr
, Error(EPERM
)).Else(Allow());
245 DISALLOW_COPY_AND_ASSIGN(ArgSizePolicy
);
248 TEST(BPFDSL
, ArgSizeTest
) {
249 PolicyEmulator
emulator(golden::kArgSizePolicy
, ArgSizePolicy());
251 emulator
.ExpectAllow(FakeSyscall(__NR_uname
, 0));
252 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_uname
, kDeadBeefAddr
));
255 class NegativeConstantsPolicy
: public Policy
{
257 NegativeConstantsPolicy() {}
258 ~NegativeConstantsPolicy() override
{}
259 ResultExpr
EvaluateSyscall(int sysno
) const override
{
260 if (sysno
== __NR_fcntl
) {
261 const Arg
<int> fd(0);
262 return If(fd
== -314, Error(EPERM
)).Else(Allow());
268 DISALLOW_COPY_AND_ASSIGN(NegativeConstantsPolicy
);
271 TEST(BPFDSL
, NegativeConstantsTest
) {
272 PolicyEmulator
emulator(golden::kNegativeConstantsPolicy
,
273 NegativeConstantsPolicy());
275 emulator
.ExpectAllow(FakeSyscall(__NR_fcntl
, -5, F_DUPFD
));
276 emulator
.ExpectAllow(FakeSyscall(__NR_fcntl
, 20, F_DUPFD
));
277 emulator
.ExpectErrno(EPERM
, FakeSyscall(__NR_fcntl
, -314, F_DUPFD
));
281 // TODO(mdempsky): This is really an integration test.
283 class TrappingPolicy
: public Policy
{
286 ~TrappingPolicy() override
{}
287 ResultExpr
EvaluateSyscall(int sysno
) const override
{
288 if (sysno
== __NR_uname
) {
289 return Trap(UnameTrap
, &count_
);
295 static intptr_t count_
;
297 static intptr_t UnameTrap(const struct arch_seccomp_data
& data
, void* aux
) {
298 BPF_ASSERT_EQ(&count_
, aux
);
302 DISALLOW_COPY_AND_ASSIGN(TrappingPolicy
);
305 intptr_t TrappingPolicy::count_
;
307 BPF_TEST_C(BPFDSL
, TrapTest
, TrappingPolicy
) {
308 ASSERT_SYSCALL_RESULT(1, uname
, NULL
);
309 ASSERT_SYSCALL_RESULT(2, uname
, NULL
);
310 ASSERT_SYSCALL_RESULT(3, uname
, NULL
);
314 class MaskingPolicy
: public Policy
{
317 ~MaskingPolicy() override
{}
318 ResultExpr
EvaluateSyscall(int sysno
) const override
{
319 if (sysno
== __NR_setuid
) {
320 const Arg
<uid_t
> uid(0);
321 return If((uid
& 0xf) == 0, Error(EINVAL
)).Else(Error(EACCES
));
323 if (sysno
== __NR_setgid
) {
324 const Arg
<gid_t
> gid(0);
325 return If((gid
& 0xf0) == 0xf0, Error(EINVAL
)).Else(Error(EACCES
));
327 if (sysno
== __NR_setpgid
) {
328 const Arg
<pid_t
> pid(0);
329 return If((pid
& 0xa5) == 0xa0, Error(EINVAL
)).Else(Error(EACCES
));
335 DISALLOW_COPY_AND_ASSIGN(MaskingPolicy
);
338 TEST(BPFDSL
, MaskTest
) {
339 PolicyEmulator
emulator(golden::kMaskingPolicy
, MaskingPolicy());
341 for (uid_t uid
= 0; uid
< 0x100; ++uid
) {
342 const int expect_errno
= (uid
& 0xf) == 0 ? EINVAL
: EACCES
;
343 emulator
.ExpectErrno(expect_errno
, FakeSyscall(__NR_setuid
, uid
));
346 for (gid_t gid
= 0; gid
< 0x100; ++gid
) {
347 const int expect_errno
= (gid
& 0xf0) == 0xf0 ? EINVAL
: EACCES
;
348 emulator
.ExpectErrno(expect_errno
, FakeSyscall(__NR_setgid
, gid
));
351 for (pid_t pid
= 0; pid
< 0x100; ++pid
) {
352 const int expect_errno
= (pid
& 0xa5) == 0xa0 ? EINVAL
: EACCES
;
353 emulator
.ExpectErrno(expect_errno
, FakeSyscall(__NR_setpgid
, pid
, 0));
357 class ElseIfPolicy
: public Policy
{
360 ~ElseIfPolicy() override
{}
361 ResultExpr
EvaluateSyscall(int sysno
) const override
{
362 if (sysno
== __NR_setuid
) {
363 const Arg
<uid_t
> uid(0);
364 return If((uid
& 0xfff) == 0, Error(0))
365 .ElseIf((uid
& 0xff0) == 0, Error(EINVAL
))
366 .ElseIf((uid
& 0xf00) == 0, Error(EEXIST
))
367 .Else(Error(EACCES
));
373 DISALLOW_COPY_AND_ASSIGN(ElseIfPolicy
);
376 TEST(BPFDSL
, ElseIfTest
) {
377 PolicyEmulator
emulator(golden::kElseIfPolicy
, ElseIfPolicy());
379 emulator
.ExpectErrno(0, FakeSyscall(__NR_setuid
, 0));
381 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setuid
, 0x0001));
382 emulator
.ExpectErrno(EINVAL
, FakeSyscall(__NR_setuid
, 0x0002));
384 emulator
.ExpectErrno(EEXIST
, FakeSyscall(__NR_setuid
, 0x0011));
385 emulator
.ExpectErrno(EEXIST
, FakeSyscall(__NR_setuid
, 0x0022));
387 emulator
.ExpectErrno(EACCES
, FakeSyscall(__NR_setuid
, 0x0111));
388 emulator
.ExpectErrno(EACCES
, FakeSyscall(__NR_setuid
, 0x0222));
391 class SwitchPolicy
: public Policy
{
394 ~SwitchPolicy() override
{}
395 ResultExpr
EvaluateSyscall(int sysno
) const override
{
396 if (sysno
== __NR_fcntl
) {
397 const Arg
<int> cmd(1);
398 const Arg
<unsigned long> long_arg(2);
400 .CASES((F_GETFL
, F_GETFD
), Error(ENOENT
))
401 .Case(F_SETFD
, If(long_arg
== O_CLOEXEC
, Allow()).Else(Error(EINVAL
)))
402 .Case(F_SETFL
, Error(EPERM
))
403 .Default(Error(EACCES
));
409 DISALLOW_COPY_AND_ASSIGN(SwitchPolicy
);
412 TEST(BPFDSL
, SwitchTest
) {
413 PolicyEmulator
emulator(golden::kSwitchPolicy
, SwitchPolicy());
415 const int kFakeSockFD
= 42;
417 emulator
.ExpectErrno(ENOENT
, FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_GETFD
));
418 emulator
.ExpectErrno(ENOENT
, FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_GETFL
));
420 emulator
.ExpectAllow(
421 FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_SETFD
, O_CLOEXEC
));
422 emulator
.ExpectErrno(EINVAL
,
423 FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_SETFD
, 0));
425 emulator
.ExpectErrno(EPERM
,
426 FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_SETFL
, O_RDONLY
));
428 emulator
.ExpectErrno(EACCES
,
429 FakeSyscall(__NR_fcntl
, kFakeSockFD
, F_DUPFD
, 0));
432 static intptr_t DummyTrap(const struct arch_seccomp_data
& data
, void* aux
) {
436 TEST(BPFDSL
, IsAllowDeny
) {
437 ResultExpr allow
= Allow();
438 EXPECT_TRUE(allow
->IsAllow());
439 EXPECT_FALSE(allow
->IsDeny());
441 ResultExpr error
= Error(ENOENT
);
442 EXPECT_FALSE(error
->IsAllow());
443 EXPECT_TRUE(error
->IsDeny());
445 ResultExpr trace
= Trace(42);
446 EXPECT_FALSE(trace
->IsAllow());
447 EXPECT_FALSE(trace
->IsDeny());
449 ResultExpr trap
= Trap(DummyTrap
, nullptr);
450 EXPECT_FALSE(trap
->IsAllow());
451 EXPECT_TRUE(trap
->IsDeny());
453 const Arg
<int> arg(0);
454 ResultExpr maybe
= If(arg
== 0, Allow()).Else(Error(EPERM
));
455 EXPECT_FALSE(maybe
->IsAllow());
456 EXPECT_FALSE(maybe
->IsDeny());
459 TEST(BPFDSL
, HasUnsafeTraps
) {
460 ResultExpr allow
= Allow();
461 EXPECT_FALSE(allow
->HasUnsafeTraps());
463 ResultExpr safe
= Trap(DummyTrap
, nullptr);
464 EXPECT_FALSE(safe
->HasUnsafeTraps());
466 ResultExpr unsafe
= UnsafeTrap(DummyTrap
, nullptr);
467 EXPECT_TRUE(unsafe
->HasUnsafeTraps());
469 const Arg
<int> arg(0);
470 ResultExpr maybe
= If(arg
== 0, allow
).Else(unsafe
);
471 EXPECT_TRUE(maybe
->HasUnsafeTraps());
475 } // namespace bpf_dsl
476 } // namespace sandbox