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. 4 5 #include "sandbox/linux/bpf_dsl/bpf_dsl.h" 6 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <netinet/in.h> 10 #include <stdint.h> 11 #include <sys/socket.h> 12 #include <sys/syscall.h> 13 #include <sys/utsname.h> 14 #include <unistd.h> 15 16 #include <map> 17 #include <utility> 18 19 #include "base/files/scoped_file.h" 20 #include "base/macros.h" 21 #include "build/build_config.h" 22 #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" 23 #include "sandbox/linux/bpf_dsl/codegen.h" 24 #include "sandbox/linux/bpf_dsl/dump_bpf.h" 25 #include "sandbox/linux/bpf_dsl/golden/golden_files.h" 26 #include "sandbox/linux/bpf_dsl/policy.h" 27 #include "sandbox/linux/bpf_dsl/policy_compiler.h" 28 #include "sandbox/linux/bpf_dsl/seccomp_macros.h" 29 #include "sandbox/linux/bpf_dsl/test_trap_registry.h" 30 #include "sandbox/linux/bpf_dsl/verifier.h" 31 #include "sandbox/linux/system_headers/linux_filter.h" 32 #include "testing/gtest/include/gtest/gtest.h" 33 34 #define CASES SANDBOX_BPF_DSL_CASES 35 36 namespace sandbox { 37 namespace bpf_dsl { 38 namespace { 39 40 // Helper function to construct fake arch_seccomp_data objects. 41 struct arch_seccomp_data FakeSyscall(int nr, 42 uintptr_t p0 = 0, 43 uintptr_t p1 = 0, 44 uintptr_t p2 = 0, 45 uintptr_t p3 = 0, 46 uintptr_t p4 = 0, 47 uintptr_t p5 = 0) { 48 // Made up program counter for syscall address. 49 const uint64_t kFakePC = 0x543210; 50 51 struct arch_seccomp_data data = { 52 nr, 53 SECCOMP_ARCH, 54 kFakePC, 55 { 56 p0, p1, p2, p3, p4, p5, 57 }, 58 }; 59 60 return data; 61 } 62 63 class PolicyEmulator { 64 public: 65 PolicyEmulator(const golden::Golden& golden, const Policy& policy) 66 : program_() { 67 TestTrapRegistry traps; 68 program_ = PolicyCompiler(&policy, &traps).Compile(); 69 70 // TODO(mdempsky): Generalize to more arches. 71 const char* expected = nullptr; 72 #if defined(ARCH_CPU_X86) 73 expected = golden.i386_dump; 74 #elif defined(ARCH_CPU_X86_64) 75 expected = golden.x86_64_dump; 76 #endif 77 78 if (expected != nullptr) { 79 const std::string actual = DumpBPF::StringPrintProgram(program_); 80 EXPECT_EQ(expected, actual); 81 } else { 82 LOG(WARNING) << "Missing golden file data entry"; 83 } 84 } 85 86 ~PolicyEmulator() {} 87 88 void ExpectAllow(const struct arch_seccomp_data& data) const { 89 EXPECT_EQ(SECCOMP_RET_ALLOW, Emulate(data)); 90 } 91 92 void ExpectErrno(uint16_t err, const struct arch_seccomp_data& data) const { 93 EXPECT_EQ(SECCOMP_RET_ERRNO | err, Emulate(data)); 94 } 95 96 void ExpectKill(const struct arch_seccomp_data& data) const { 97 EXPECT_EQ(SECCOMP_RET_KILL, Emulate(data)); 98 } 99 100 private: 101 uint32_t Emulate(const struct arch_seccomp_data& data) const { 102 const char* err = nullptr; 103 uint32_t res = Verifier::EvaluateBPF(program_, data, &err); 104 if (err) { 105 ADD_FAILURE() << err; 106 return 0; 107 } 108 return res; 109 } 110 111 CodeGen::Program program_; 112 113 DISALLOW_COPY_AND_ASSIGN(PolicyEmulator); 114 }; 115 116 class BasicPolicy : public Policy { 117 public: 118 BasicPolicy() {} 119 ~BasicPolicy() override {} 120 ResultExpr EvaluateSyscall(int sysno) const override { 121 if (sysno == __NR_getpgid) { 122 const Arg<pid_t> pid(0); 123 return If(pid == 0, Error(EPERM)).Else(Error(EINVAL)); 124 } 125 if (sysno == __NR_setuid) { 126 const Arg<uid_t> uid(0); 127 return If(uid != 42, Kill()).Else(Allow()); 128 } 129 return Allow(); 130 } 131 132 private: 133 DISALLOW_COPY_AND_ASSIGN(BasicPolicy); 134 }; 135 136 TEST(BPFDSL, Basic) { 137 PolicyEmulator emulator(golden::kBasicPolicy, BasicPolicy()); 138 139 emulator.ExpectErrno(EPERM, FakeSyscall(__NR_getpgid, 0)); 140 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_getpgid, 1)); 141 142 emulator.ExpectAllow(FakeSyscall(__NR_setuid, 42)); 143 emulator.ExpectKill(FakeSyscall(__NR_setuid, 43)); 144 } 145 146 /* On IA-32, socketpair() is implemented via socketcall(). :-( */ 147 #if !defined(ARCH_CPU_X86) 148 class BooleanLogicPolicy : public Policy { 149 public: 150 BooleanLogicPolicy() {} 151 ~BooleanLogicPolicy() override {} 152 ResultExpr EvaluateSyscall(int sysno) const override { 153 if (sysno == __NR_socketpair) { 154 const Arg<int> domain(0), type(1), protocol(2); 155 return If(AllOf(domain == AF_UNIX, 156 AnyOf(type == SOCK_STREAM, type == SOCK_DGRAM), 157 protocol == 0), 158 Error(EPERM)) 159 .Else(Error(EINVAL)); 160 } 161 return Allow(); 162 } 163 164 private: 165 DISALLOW_COPY_AND_ASSIGN(BooleanLogicPolicy); 166 }; 167 168 TEST(BPFDSL, BooleanLogic) { 169 PolicyEmulator emulator(golden::kBooleanLogicPolicy, BooleanLogicPolicy()); 170 171 const intptr_t kFakeSV = 0x12345; 172 173 // Acceptable combinations that should return EPERM. 174 emulator.ExpectErrno( 175 EPERM, FakeSyscall(__NR_socketpair, AF_UNIX, SOCK_STREAM, 0, kFakeSV)); 176 emulator.ExpectErrno( 177 EPERM, FakeSyscall(__NR_socketpair, AF_UNIX, SOCK_DGRAM, 0, kFakeSV)); 178 179 // Combinations that are invalid for only one reason; should return EINVAL. 180 emulator.ExpectErrno( 181 EINVAL, FakeSyscall(__NR_socketpair, AF_INET, SOCK_STREAM, 0, kFakeSV)); 182 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_socketpair, AF_UNIX, 183 SOCK_SEQPACKET, 0, kFakeSV)); 184 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_socketpair, AF_UNIX, 185 SOCK_STREAM, IPPROTO_TCP, kFakeSV)); 186 187 // Completely unacceptable combination; should also return EINVAL. 188 emulator.ExpectErrno( 189 EINVAL, FakeSyscall(__NR_socketpair, AF_INET, SOCK_SEQPACKET, IPPROTO_UDP, 190 kFakeSV)); 191 } 192 #endif // !ARCH_CPU_X86 193 194 class MoreBooleanLogicPolicy : public Policy { 195 public: 196 MoreBooleanLogicPolicy() {} 197 ~MoreBooleanLogicPolicy() override {} 198 ResultExpr EvaluateSyscall(int sysno) const override { 199 if (sysno == __NR_setresuid) { 200 const Arg<uid_t> ruid(0), euid(1), suid(2); 201 return If(AnyOf(ruid == 0, euid == 0, suid == 0), Error(EPERM)) 202 .ElseIf(AllOf(ruid == 1, euid == 1, suid == 1), Error(EAGAIN)) 203 .Else(Error(EINVAL)); 204 } 205 return Allow(); 206 } 207 208 private: 209 DISALLOW_COPY_AND_ASSIGN(MoreBooleanLogicPolicy); 210 }; 211 212 TEST(BPFDSL, MoreBooleanLogic) { 213 PolicyEmulator emulator(golden::kMoreBooleanLogicPolicy, 214 MoreBooleanLogicPolicy()); 215 216 // Expect EPERM if any set to 0. 217 emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 0, 5, 5)); 218 emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 5, 0, 5)); 219 emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 5, 5, 0)); 220 221 // Expect EAGAIN if all set to 1. 222 emulator.ExpectErrno(EAGAIN, FakeSyscall(__NR_setresuid, 1, 1, 1)); 223 224 // Expect EINVAL for anything else. 225 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 5, 1, 1)); 226 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 1, 5, 1)); 227 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 1, 1, 5)); 228 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 3, 4, 5)); 229 } 230 231 static const uintptr_t kDeadBeefAddr = 232 static_cast<uintptr_t>(0xdeadbeefdeadbeefULL); 233 234 class ArgSizePolicy : public Policy { 235 public: 236 ArgSizePolicy() {} 237 ~ArgSizePolicy() override {} 238 ResultExpr EvaluateSyscall(int sysno) const override { 239 if (sysno == __NR_uname) { 240 const Arg<uintptr_t> addr(0); 241 return If(addr == kDeadBeefAddr, Error(EPERM)).Else(Allow()); 242 } 243 return Allow(); 244 } 245 246 private: 247 DISALLOW_COPY_AND_ASSIGN(ArgSizePolicy); 248 }; 249 250 TEST(BPFDSL, ArgSizeTest) { 251 PolicyEmulator emulator(golden::kArgSizePolicy, ArgSizePolicy()); 252 253 emulator.ExpectAllow(FakeSyscall(__NR_uname, 0)); 254 emulator.ExpectErrno(EPERM, FakeSyscall(__NR_uname, kDeadBeefAddr)); 255 } 256 257 class NegativeConstantsPolicy : public Policy { 258 public: 259 NegativeConstantsPolicy() {} 260 ~NegativeConstantsPolicy() override {} 261 ResultExpr EvaluateSyscall(int sysno) const override { 262 if (sysno == __NR_fcntl) { 263 const Arg<int> fd(0); 264 return If(fd == -314, Error(EPERM)).Else(Allow()); 265 } 266 return Allow(); 267 } 268 269 private: 270 DISALLOW_COPY_AND_ASSIGN(NegativeConstantsPolicy); 271 }; 272 273 TEST(BPFDSL, NegativeConstantsTest) { 274 PolicyEmulator emulator(golden::kNegativeConstantsPolicy, 275 NegativeConstantsPolicy()); 276 277 emulator.ExpectAllow(FakeSyscall(__NR_fcntl, -5, F_DUPFD)); 278 emulator.ExpectAllow(FakeSyscall(__NR_fcntl, 20, F_DUPFD)); 279 emulator.ExpectErrno(EPERM, FakeSyscall(__NR_fcntl, -314, F_DUPFD)); 280 } 281 282 #if 0 283 // TODO(mdempsky): This is really an integration test. 284 285 class TrappingPolicy : public Policy { 286 public: 287 TrappingPolicy() {} 288 ~TrappingPolicy() override {} 289 ResultExpr EvaluateSyscall(int sysno) const override { 290 if (sysno == __NR_uname) { 291 return Trap(UnameTrap, &count_); 292 } 293 return Allow(); 294 } 295 296 private: 297 static intptr_t count_; 298 299 static intptr_t UnameTrap(const struct arch_seccomp_data& data, void* aux) { 300 BPF_ASSERT_EQ(&count_, aux); 301 return ++count_; 302 } 303 304 DISALLOW_COPY_AND_ASSIGN(TrappingPolicy); 305 }; 306 307 intptr_t TrappingPolicy::count_; 308 309 BPF_TEST_C(BPFDSL, TrapTest, TrappingPolicy) { 310 ASSERT_SYSCALL_RESULT(1, uname, NULL); 311 ASSERT_SYSCALL_RESULT(2, uname, NULL); 312 ASSERT_SYSCALL_RESULT(3, uname, NULL); 313 } 314 #endif 315 316 class MaskingPolicy : public Policy { 317 public: 318 MaskingPolicy() {} 319 ~MaskingPolicy() override {} 320 ResultExpr EvaluateSyscall(int sysno) const override { 321 if (sysno == __NR_setuid) { 322 const Arg<uid_t> uid(0); 323 return If((uid & 0xf) == 0, Error(EINVAL)).Else(Error(EACCES)); 324 } 325 if (sysno == __NR_setgid) { 326 const Arg<gid_t> gid(0); 327 return If((gid & 0xf0) == 0xf0, Error(EINVAL)).Else(Error(EACCES)); 328 } 329 if (sysno == __NR_setpgid) { 330 const Arg<pid_t> pid(0); 331 return If((pid & 0xa5) == 0xa0, Error(EINVAL)).Else(Error(EACCES)); 332 } 333 return Allow(); 334 } 335 336 private: 337 DISALLOW_COPY_AND_ASSIGN(MaskingPolicy); 338 }; 339 340 TEST(BPFDSL, MaskTest) { 341 PolicyEmulator emulator(golden::kMaskingPolicy, MaskingPolicy()); 342 343 for (uid_t uid = 0; uid < 0x100; ++uid) { 344 const int expect_errno = (uid & 0xf) == 0 ? EINVAL : EACCES; 345 emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setuid, uid)); 346 } 347 348 for (gid_t gid = 0; gid < 0x100; ++gid) { 349 const int expect_errno = (gid & 0xf0) == 0xf0 ? EINVAL : EACCES; 350 emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setgid, gid)); 351 } 352 353 for (pid_t pid = 0; pid < 0x100; ++pid) { 354 const int expect_errno = (pid & 0xa5) == 0xa0 ? EINVAL : EACCES; 355 emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setpgid, pid, 0)); 356 } 357 } 358 359 class ElseIfPolicy : public Policy { 360 public: 361 ElseIfPolicy() {} 362 ~ElseIfPolicy() override {} 363 ResultExpr EvaluateSyscall(int sysno) const override { 364 if (sysno == __NR_setuid) { 365 const Arg<uid_t> uid(0); 366 return If((uid & 0xfff) == 0, Error(0)) 367 .ElseIf((uid & 0xff0) == 0, Error(EINVAL)) 368 .ElseIf((uid & 0xf00) == 0, Error(EEXIST)) 369 .Else(Error(EACCES)); 370 } 371 return Allow(); 372 } 373 374 private: 375 DISALLOW_COPY_AND_ASSIGN(ElseIfPolicy); 376 }; 377 378 TEST(BPFDSL, ElseIfTest) { 379 PolicyEmulator emulator(golden::kElseIfPolicy, ElseIfPolicy()); 380 381 emulator.ExpectErrno(0, FakeSyscall(__NR_setuid, 0)); 382 383 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setuid, 0x0001)); 384 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setuid, 0x0002)); 385 386 emulator.ExpectErrno(EEXIST, FakeSyscall(__NR_setuid, 0x0011)); 387 emulator.ExpectErrno(EEXIST, FakeSyscall(__NR_setuid, 0x0022)); 388 389 emulator.ExpectErrno(EACCES, FakeSyscall(__NR_setuid, 0x0111)); 390 emulator.ExpectErrno(EACCES, FakeSyscall(__NR_setuid, 0x0222)); 391 } 392 393 class SwitchPolicy : public Policy { 394 public: 395 SwitchPolicy() {} 396 ~SwitchPolicy() override {} 397 ResultExpr EvaluateSyscall(int sysno) const override { 398 if (sysno == __NR_fcntl) { 399 const Arg<int> cmd(1); 400 const Arg<unsigned long> long_arg(2); 401 return Switch(cmd) 402 .CASES((F_GETFL, F_GETFD), Error(ENOENT)) 403 .Case(F_SETFD, If(long_arg == O_CLOEXEC, Allow()).Else(Error(EINVAL))) 404 .Case(F_SETFL, Error(EPERM)) 405 .Default(Error(EACCES)); 406 } 407 return Allow(); 408 } 409 410 private: 411 DISALLOW_COPY_AND_ASSIGN(SwitchPolicy); 412 }; 413 414 TEST(BPFDSL, SwitchTest) { 415 PolicyEmulator emulator(golden::kSwitchPolicy, SwitchPolicy()); 416 417 const int kFakeSockFD = 42; 418 419 emulator.ExpectErrno(ENOENT, FakeSyscall(__NR_fcntl, kFakeSockFD, F_GETFD)); 420 emulator.ExpectErrno(ENOENT, FakeSyscall(__NR_fcntl, kFakeSockFD, F_GETFL)); 421 422 emulator.ExpectAllow( 423 FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFD, O_CLOEXEC)); 424 emulator.ExpectErrno(EINVAL, 425 FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFD, 0)); 426 427 emulator.ExpectErrno(EPERM, 428 FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFL, O_RDONLY)); 429 430 emulator.ExpectErrno(EACCES, 431 FakeSyscall(__NR_fcntl, kFakeSockFD, F_DUPFD, 0)); 432 } 433 434 static intptr_t DummyTrap(const struct arch_seccomp_data& data, void* aux) { 435 return 0; 436 } 437 438 TEST(BPFDSL, IsAllowDeny) { 439 ResultExpr allow = Allow(); 440 EXPECT_TRUE(allow->IsAllow()); 441 EXPECT_FALSE(allow->IsDeny()); 442 443 ResultExpr error = Error(ENOENT); 444 EXPECT_FALSE(error->IsAllow()); 445 EXPECT_TRUE(error->IsDeny()); 446 447 ResultExpr trace = Trace(42); 448 EXPECT_FALSE(trace->IsAllow()); 449 EXPECT_FALSE(trace->IsDeny()); 450 451 ResultExpr trap = Trap(DummyTrap, nullptr); 452 EXPECT_FALSE(trap->IsAllow()); 453 EXPECT_TRUE(trap->IsDeny()); 454 455 const Arg<int> arg(0); 456 ResultExpr maybe = If(arg == 0, Allow()).Else(Error(EPERM)); 457 EXPECT_FALSE(maybe->IsAllow()); 458 EXPECT_FALSE(maybe->IsDeny()); 459 } 460 461 TEST(BPFDSL, HasUnsafeTraps) { 462 ResultExpr allow = Allow(); 463 EXPECT_FALSE(allow->HasUnsafeTraps()); 464 465 ResultExpr safe = Trap(DummyTrap, nullptr); 466 EXPECT_FALSE(safe->HasUnsafeTraps()); 467 468 ResultExpr unsafe = UnsafeTrap(DummyTrap, nullptr); 469 EXPECT_TRUE(unsafe->HasUnsafeTraps()); 470 471 const Arg<int> arg(0); 472 ResultExpr maybe = If(arg == 0, allow).Else(unsafe); 473 EXPECT_TRUE(maybe->HasUnsafeTraps()); 474 } 475 476 } // namespace 477 } // namespace bpf_dsl 478 } // namespace sandbox 479