| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sandbox/linux/bpf_dsl/bpf_dsl.h" | 5 #include "sandbox/linux/bpf_dsl/bpf_dsl.h" |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <fcntl.h> | 8 #include <fcntl.h> |
| 9 #include <netinet/in.h> | 9 #include <netinet/in.h> |
| 10 #include <sys/socket.h> | 10 #include <sys/socket.h> |
| 11 #include <sys/syscall.h> | 11 #include <sys/syscall.h> |
| 12 #include <sys/utsname.h> | 12 #include <sys/utsname.h> |
| 13 #include <unistd.h> | 13 #include <unistd.h> |
| 14 | 14 |
| 15 #include "base/files/scoped_file.h" | 15 #include "base/files/scoped_file.h" |
| 16 #include "base/macros.h" | 16 #include "base/macros.h" |
| 17 #include "build/build_config.h" | 17 #include "build/build_config.h" |
| 18 #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" | 18 #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" |
| 19 #include "sandbox/linux/bpf_dsl/codegen.h" | |
| 20 #include "sandbox/linux/bpf_dsl/policy.h" | 19 #include "sandbox/linux/bpf_dsl/policy.h" |
| 21 #include "sandbox/linux/bpf_dsl/policy_compiler.h" | 20 #include "sandbox/linux/seccomp-bpf/bpf_tests.h" |
| 22 #include "sandbox/linux/bpf_dsl/seccomp_macros.h" | |
| 23 #include "sandbox/linux/bpf_dsl/trap_registry.h" | |
| 24 #include "sandbox/linux/seccomp-bpf/errorcode.h" | 21 #include "sandbox/linux/seccomp-bpf/errorcode.h" |
| 25 #include "sandbox/linux/seccomp-bpf/verifier.h" | 22 #include "sandbox/linux/seccomp-bpf/syscall.h" |
| 26 #include "testing/gtest/include/gtest/gtest.h" | 23 #include "testing/gtest/include/gtest/gtest.h" |
| 27 | 24 |
| 28 #define CASES SANDBOX_BPF_DSL_CASES | 25 #define CASES SANDBOX_BPF_DSL_CASES |
| 29 | 26 |
| 27 // Helper macro to assert that invoking system call |sys| directly via |
| 28 // Syscall::Call with arguments |...| returns |res|. |
| 29 // Errors can be asserted by specifying a value like "-EINVAL". |
| 30 #define ASSERT_SYSCALL_RESULT(res, sys, ...) \ |
| 31 BPF_ASSERT_EQ(res, Stubs::sys(__VA_ARGS__)) |
| 32 |
| 30 namespace sandbox { | 33 namespace sandbox { |
| 31 namespace bpf_dsl { | 34 namespace bpf_dsl { |
| 32 namespace { | 35 namespace { |
| 33 | 36 |
| 34 // Helper function to construct fake arch_seccomp_data objects. | 37 // Type safe stubs for tested system calls. |
| 35 struct arch_seccomp_data FakeSyscall(int nr, | 38 class Stubs { |
| 36 uint64_t p0 = 0, | |
| 37 uint64_t p1 = 0, | |
| 38 uint64_t p2 = 0, | |
| 39 uint64_t p3 = 0, | |
| 40 uint64_t p4 = 0, | |
| 41 uint64_t p5 = 0) { | |
| 42 // Made up program counter for syscall address. | |
| 43 const uint64_t kFakePC = 0x543210; | |
| 44 | |
| 45 struct arch_seccomp_data data = { | |
| 46 nr, | |
| 47 SECCOMP_ARCH, | |
| 48 kFakePC, | |
| 49 { | |
| 50 p0, p1, p2, p3, p4, p5, | |
| 51 }, | |
| 52 }; | |
| 53 | |
| 54 return data; | |
| 55 } | |
| 56 | |
| 57 class FakeTrapRegistry : public TrapRegistry { | |
| 58 public: | 39 public: |
| 59 FakeTrapRegistry() : next_id_(1) {} | 40 static int getpgid(pid_t pid) { return Syscall::Call(__NR_getpgid, pid); } |
| 60 virtual ~FakeTrapRegistry() {} | 41 static int setuid(uid_t uid) { return Syscall::Call(__NR_setuid, uid); } |
| 61 | 42 static int setgid(gid_t gid) { return Syscall::Call(__NR_setgid, gid); } |
| 62 uint16_t Add(TrapFnc fnc, const void* aux, bool safe) override { | 43 static int setpgid(pid_t pid, pid_t pgid) { |
| 63 EXPECT_NE(0U, next_id_); | 44 return Syscall::Call(__NR_setpgid, pid, pgid); |
| 64 return next_id_++; | |
| 65 } | 45 } |
| 66 | 46 |
| 67 bool EnableUnsafeTraps() override { | 47 static int fcntl(int fd, int cmd, unsigned long arg = 0) { |
| 68 ADD_FAILURE() << "Unimplemented"; | 48 return Syscall::Call(__NR_fcntl, fd, cmd, arg); |
| 69 return false; | |
| 70 } | 49 } |
| 71 | 50 |
| 72 private: | 51 static int uname(struct utsname* buf) { |
| 73 uint16_t next_id_; | 52 return Syscall::Call(__NR_uname, buf); |
| 74 | |
| 75 DISALLOW_COPY_AND_ASSIGN(FakeTrapRegistry); | |
| 76 }; | |
| 77 | |
| 78 class PolicyEmulator { | |
| 79 public: | |
| 80 explicit PolicyEmulator(const Policy* policy) : program_(), traps_() { | |
| 81 program_ = *PolicyCompiler(policy, &traps_).Compile(); | |
| 82 } | |
| 83 ~PolicyEmulator() {} | |
| 84 | |
| 85 uint32_t Emulate(const struct arch_seccomp_data& data) const { | |
| 86 const char* err = nullptr; | |
| 87 uint32_t res = Verifier::EvaluateBPF(program_, data, &err); | |
| 88 if (err) { | |
| 89 ADD_FAILURE() << err; | |
| 90 return 0; | |
| 91 } | |
| 92 return res; | |
| 93 } | 53 } |
| 94 | 54 |
| 95 void ExpectAllow(const struct arch_seccomp_data& data) const { | 55 static int setresuid(uid_t ruid, uid_t euid, uid_t suid) { |
| 96 EXPECT_EQ(SECCOMP_RET_ALLOW, Emulate(data)); | 56 return Syscall::Call(__NR_setresuid, ruid, euid, suid); |
| 97 } | 57 } |
| 98 | 58 |
| 99 void ExpectErrno(uint16_t err, const struct arch_seccomp_data& data) const { | 59 #if !defined(ARCH_CPU_X86) |
| 100 EXPECT_EQ(SECCOMP_RET_ERRNO | err, Emulate(data)); | 60 static int socketpair(int domain, int type, int protocol, int sv[2]) { |
| 61 return Syscall::Call(__NR_socketpair, domain, type, protocol, sv); |
| 101 } | 62 } |
| 102 | 63 #endif |
| 103 private: | |
| 104 CodeGen::Program program_; | |
| 105 FakeTrapRegistry traps_; | |
| 106 | |
| 107 DISALLOW_COPY_AND_ASSIGN(PolicyEmulator); | |
| 108 }; | 64 }; |
| 109 | 65 |
| 110 class BasicPolicy : public Policy { | 66 class BasicPolicy : public Policy { |
| 111 public: | 67 public: |
| 112 BasicPolicy() {} | 68 BasicPolicy() {} |
| 113 ~BasicPolicy() override {} | 69 ~BasicPolicy() override {} |
| 114 ResultExpr EvaluateSyscall(int sysno) const override { | 70 ResultExpr EvaluateSyscall(int sysno) const override { |
| 115 if (sysno == __NR_getpgid) { | 71 if (sysno == __NR_getpgid) { |
| 116 const Arg<pid_t> pid(0); | 72 const Arg<pid_t> pid(0); |
| 117 return If(pid == 0, Error(EPERM)).Else(Error(EINVAL)); | 73 return If(pid == 0, Error(EPERM)).Else(Error(EINVAL)); |
| 118 } | 74 } |
| 119 if (sysno == __NR_setuid) { | 75 if (sysno == __NR_setuid) { |
| 120 const Arg<uid_t> uid(0); | 76 const Arg<uid_t> uid(0); |
| 121 return If(uid != 42, Error(ESRCH)).Else(Error(ENOMEM)); | 77 return If(uid != 42, Error(ESRCH)).Else(Error(ENOMEM)); |
| 122 } | 78 } |
| 123 return Allow(); | 79 return Allow(); |
| 124 } | 80 } |
| 125 | 81 |
| 126 private: | 82 private: |
| 127 DISALLOW_COPY_AND_ASSIGN(BasicPolicy); | 83 DISALLOW_COPY_AND_ASSIGN(BasicPolicy); |
| 128 }; | 84 }; |
| 129 | 85 |
| 130 TEST(BPFDSL, Basic) { | 86 BPF_TEST_C(BPFDSL, Basic, BasicPolicy) { |
| 131 BasicPolicy policy; | 87 ASSERT_SYSCALL_RESULT(-EPERM, getpgid, 0); |
| 132 PolicyEmulator emulator(&policy); | 88 ASSERT_SYSCALL_RESULT(-EINVAL, getpgid, 1); |
| 133 | 89 |
| 134 emulator.ExpectErrno(EPERM, FakeSyscall(__NR_getpgid, 0)); | 90 ASSERT_SYSCALL_RESULT(-ENOMEM, setuid, 42); |
| 135 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_getpgid, 1)); | 91 ASSERT_SYSCALL_RESULT(-ESRCH, setuid, 43); |
| 136 | |
| 137 emulator.ExpectErrno(ENOMEM, FakeSyscall(__NR_setuid, 42)); | |
| 138 emulator.ExpectErrno(ESRCH, FakeSyscall(__NR_setuid, 43)); | |
| 139 } | 92 } |
| 140 | 93 |
| 141 /* On IA-32, socketpair() is implemented via socketcall(). :-( */ | 94 /* On IA-32, socketpair() is implemented via socketcall(). :-( */ |
| 142 #if !defined(ARCH_CPU_X86) | 95 #if !defined(ARCH_CPU_X86) |
| 143 class BooleanLogicPolicy : public Policy { | 96 class BooleanLogicPolicy : public Policy { |
| 144 public: | 97 public: |
| 145 BooleanLogicPolicy() {} | 98 BooleanLogicPolicy() {} |
| 146 ~BooleanLogicPolicy() override {} | 99 ~BooleanLogicPolicy() override {} |
| 147 ResultExpr EvaluateSyscall(int sysno) const override { | 100 ResultExpr EvaluateSyscall(int sysno) const override { |
| 148 if (sysno == __NR_socketpair) { | 101 if (sysno == __NR_socketpair) { |
| 149 const Arg<int> domain(0), type(1), protocol(2); | 102 const Arg<int> domain(0), type(1), protocol(2); |
| 150 return If(domain == AF_UNIX && | 103 return If(domain == AF_UNIX && |
| 151 (type == SOCK_STREAM || type == SOCK_DGRAM) && | 104 (type == SOCK_STREAM || type == SOCK_DGRAM) && |
| 152 protocol == 0, | 105 protocol == 0, |
| 153 Error(EPERM)).Else(Error(EINVAL)); | 106 Error(EPERM)).Else(Error(EINVAL)); |
| 154 } | 107 } |
| 155 return Allow(); | 108 return Allow(); |
| 156 } | 109 } |
| 157 | 110 |
| 158 private: | 111 private: |
| 159 DISALLOW_COPY_AND_ASSIGN(BooleanLogicPolicy); | 112 DISALLOW_COPY_AND_ASSIGN(BooleanLogicPolicy); |
| 160 }; | 113 }; |
| 161 | 114 |
| 162 TEST(BPFDSL, BooleanLogic) { | 115 BPF_TEST_C(BPFDSL, BooleanLogic, BooleanLogicPolicy) { |
| 163 BooleanLogicPolicy policy; | 116 int sv[2]; |
| 164 PolicyEmulator emulator(&policy); | |
| 165 | |
| 166 const intptr_t kFakeSV = 0x12345; | |
| 167 | 117 |
| 168 // Acceptable combinations that should return EPERM. | 118 // Acceptable combinations that should return EPERM. |
| 169 emulator.ExpectErrno( | 119 ASSERT_SYSCALL_RESULT(-EPERM, socketpair, AF_UNIX, SOCK_STREAM, 0, sv); |
| 170 EPERM, FakeSyscall(__NR_socketpair, AF_UNIX, SOCK_STREAM, 0, kFakeSV)); | 120 ASSERT_SYSCALL_RESULT(-EPERM, socketpair, AF_UNIX, SOCK_DGRAM, 0, sv); |
| 171 emulator.ExpectErrno( | |
| 172 EPERM, FakeSyscall(__NR_socketpair, AF_UNIX, SOCK_DGRAM, 0, kFakeSV)); | |
| 173 | 121 |
| 174 // Combinations that are invalid for only one reason; should return EINVAL. | 122 // Combinations that are invalid for only one reason; should return EINVAL. |
| 175 emulator.ExpectErrno( | 123 ASSERT_SYSCALL_RESULT(-EINVAL, socketpair, AF_INET, SOCK_STREAM, 0, sv); |
| 176 EINVAL, FakeSyscall(__NR_socketpair, AF_INET, SOCK_STREAM, 0, kFakeSV)); | 124 ASSERT_SYSCALL_RESULT(-EINVAL, socketpair, AF_UNIX, SOCK_SEQPACKET, 0, sv); |
| 177 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_socketpair, AF_UNIX, | 125 ASSERT_SYSCALL_RESULT( |
| 178 SOCK_SEQPACKET, 0, kFakeSV)); | 126 -EINVAL, socketpair, AF_UNIX, SOCK_STREAM, IPPROTO_TCP, sv); |
| 179 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_socketpair, AF_UNIX, | |
| 180 SOCK_STREAM, IPPROTO_TCP, kFakeSV)); | |
| 181 | 127 |
| 182 // Completely unacceptable combination; should also return EINVAL. | 128 // Completely unacceptable combination; should also return EINVAL. |
| 183 emulator.ExpectErrno( | 129 ASSERT_SYSCALL_RESULT( |
| 184 EINVAL, FakeSyscall(__NR_socketpair, AF_INET, SOCK_SEQPACKET, IPPROTO_UDP, | 130 -EINVAL, socketpair, AF_INET, SOCK_SEQPACKET, IPPROTO_UDP, sv); |
| 185 kFakeSV)); | |
| 186 } | 131 } |
| 187 #endif // !ARCH_CPU_X86 | 132 #endif // !ARCH_CPU_X86 |
| 188 | 133 |
| 189 class MoreBooleanLogicPolicy : public Policy { | 134 class MoreBooleanLogicPolicy : public Policy { |
| 190 public: | 135 public: |
| 191 MoreBooleanLogicPolicy() {} | 136 MoreBooleanLogicPolicy() {} |
| 192 ~MoreBooleanLogicPolicy() override {} | 137 ~MoreBooleanLogicPolicy() override {} |
| 193 ResultExpr EvaluateSyscall(int sysno) const override { | 138 ResultExpr EvaluateSyscall(int sysno) const override { |
| 194 if (sysno == __NR_setresuid) { | 139 if (sysno == __NR_setresuid) { |
| 195 const Arg<uid_t> ruid(0), euid(1), suid(2); | 140 const Arg<uid_t> ruid(0), euid(1), suid(2); |
| 196 return If(ruid == 0 || euid == 0 || suid == 0, Error(EPERM)) | 141 return If(ruid == 0 || euid == 0 || suid == 0, Error(EPERM)) |
| 197 .ElseIf(ruid == 1 && euid == 1 && suid == 1, Error(EAGAIN)) | 142 .ElseIf(ruid == 1 && euid == 1 && suid == 1, Error(EAGAIN)) |
| 198 .Else(Error(EINVAL)); | 143 .Else(Error(EINVAL)); |
| 199 } | 144 } |
| 200 return Allow(); | 145 return Allow(); |
| 201 } | 146 } |
| 202 | 147 |
| 203 private: | 148 private: |
| 204 DISALLOW_COPY_AND_ASSIGN(MoreBooleanLogicPolicy); | 149 DISALLOW_COPY_AND_ASSIGN(MoreBooleanLogicPolicy); |
| 205 }; | 150 }; |
| 206 | 151 |
| 207 TEST(BPFDSL, MoreBooleanLogic) { | 152 BPF_TEST_C(BPFDSL, MoreBooleanLogic, MoreBooleanLogicPolicy) { |
| 208 MoreBooleanLogicPolicy policy; | |
| 209 PolicyEmulator emulator(&policy); | |
| 210 | |
| 211 // Expect EPERM if any set to 0. | 153 // Expect EPERM if any set to 0. |
| 212 emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 0, 5, 5)); | 154 ASSERT_SYSCALL_RESULT(-EPERM, setresuid, 0, 5, 5); |
| 213 emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 5, 0, 5)); | 155 ASSERT_SYSCALL_RESULT(-EPERM, setresuid, 5, 0, 5); |
| 214 emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 5, 5, 0)); | 156 ASSERT_SYSCALL_RESULT(-EPERM, setresuid, 5, 5, 0); |
| 215 | 157 |
| 216 // Expect EAGAIN if all set to 1. | 158 // Expect EAGAIN if all set to 1. |
| 217 emulator.ExpectErrno(EAGAIN, FakeSyscall(__NR_setresuid, 1, 1, 1)); | 159 ASSERT_SYSCALL_RESULT(-EAGAIN, setresuid, 1, 1, 1); |
| 218 | 160 |
| 219 // Expect EINVAL for anything else. | 161 // Expect EINVAL for anything else. |
| 220 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 5, 1, 1)); | 162 ASSERT_SYSCALL_RESULT(-EINVAL, setresuid, 5, 1, 1); |
| 221 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 1, 5, 1)); | 163 ASSERT_SYSCALL_RESULT(-EINVAL, setresuid, 1, 5, 1); |
| 222 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 1, 1, 5)); | 164 ASSERT_SYSCALL_RESULT(-EINVAL, setresuid, 1, 1, 5); |
| 223 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 3, 4, 5)); | 165 ASSERT_SYSCALL_RESULT(-EINVAL, setresuid, 3, 4, 5); |
| 224 } | 166 } |
| 225 | 167 |
| 226 static const uintptr_t kDeadBeefAddr = | 168 static const uintptr_t kDeadBeefAddr = |
| 227 static_cast<uintptr_t>(0xdeadbeefdeadbeefULL); | 169 static_cast<uintptr_t>(0xdeadbeefdeadbeefULL); |
| 228 | 170 |
| 229 class ArgSizePolicy : public Policy { | 171 class ArgSizePolicy : public Policy { |
| 230 public: | 172 public: |
| 231 ArgSizePolicy() {} | 173 ArgSizePolicy() {} |
| 232 ~ArgSizePolicy() override {} | 174 ~ArgSizePolicy() override {} |
| 233 ResultExpr EvaluateSyscall(int sysno) const override { | 175 ResultExpr EvaluateSyscall(int sysno) const override { |
| 234 if (sysno == __NR_uname) { | 176 if (sysno == __NR_uname) { |
| 235 const Arg<uintptr_t> addr(0); | 177 const Arg<uintptr_t> addr(0); |
| 236 return If(addr == kDeadBeefAddr, Error(EPERM)).Else(Allow()); | 178 return If(addr == kDeadBeefAddr, Error(EPERM)).Else(Allow()); |
| 237 } | 179 } |
| 238 return Allow(); | 180 return Allow(); |
| 239 } | 181 } |
| 240 | 182 |
| 241 private: | 183 private: |
| 242 DISALLOW_COPY_AND_ASSIGN(ArgSizePolicy); | 184 DISALLOW_COPY_AND_ASSIGN(ArgSizePolicy); |
| 243 }; | 185 }; |
| 244 | 186 |
| 245 TEST(BPFDSL, ArgSizeTest) { | 187 BPF_TEST_C(BPFDSL, ArgSizeTest, ArgSizePolicy) { |
| 246 ArgSizePolicy policy; | 188 struct utsname buf; |
| 247 PolicyEmulator emulator(&policy); | 189 ASSERT_SYSCALL_RESULT(0, uname, &buf); |
| 248 | 190 ASSERT_SYSCALL_RESULT( |
| 249 emulator.ExpectAllow(FakeSyscall(__NR_uname, 0)); | 191 -EPERM, uname, reinterpret_cast<struct utsname*>(kDeadBeefAddr)); |
| 250 emulator.ExpectErrno(EPERM, FakeSyscall(__NR_uname, kDeadBeefAddr)); | |
| 251 } | 192 } |
| 252 | 193 |
| 253 #if 0 | |
| 254 // TODO(mdempsky): This is really an integration test. | |
| 255 | |
| 256 class TrappingPolicy : public Policy { | 194 class TrappingPolicy : public Policy { |
| 257 public: | 195 public: |
| 258 TrappingPolicy() {} | 196 TrappingPolicy() {} |
| 259 ~TrappingPolicy() override {} | 197 ~TrappingPolicy() override {} |
| 260 ResultExpr EvaluateSyscall(int sysno) const override { | 198 ResultExpr EvaluateSyscall(int sysno) const override { |
| 261 if (sysno == __NR_uname) { | 199 if (sysno == __NR_uname) { |
| 262 return Trap(UnameTrap, &count_); | 200 return Trap(UnameTrap, &count_); |
| 263 } | 201 } |
| 264 return Allow(); | 202 return Allow(); |
| 265 } | 203 } |
| 266 | 204 |
| 267 private: | 205 private: |
| 268 static intptr_t count_; | 206 static intptr_t count_; |
| 269 | 207 |
| 270 static intptr_t UnameTrap(const struct arch_seccomp_data& data, void* aux) { | 208 static intptr_t UnameTrap(const struct arch_seccomp_data& data, void* aux) { |
| 271 BPF_ASSERT_EQ(&count_, aux); | 209 BPF_ASSERT_EQ(&count_, aux); |
| 272 return ++count_; | 210 return ++count_; |
| 273 } | 211 } |
| 274 | 212 |
| 275 DISALLOW_COPY_AND_ASSIGN(TrappingPolicy); | 213 DISALLOW_COPY_AND_ASSIGN(TrappingPolicy); |
| 276 }; | 214 }; |
| 277 | 215 |
| 278 intptr_t TrappingPolicy::count_; | 216 intptr_t TrappingPolicy::count_; |
| 279 | 217 |
| 280 BPF_TEST_C(BPFDSL, TrapTest, TrappingPolicy) { | 218 BPF_TEST_C(BPFDSL, TrapTest, TrappingPolicy) { |
| 281 ASSERT_SYSCALL_RESULT(1, uname, NULL); | 219 ASSERT_SYSCALL_RESULT(1, uname, NULL); |
| 282 ASSERT_SYSCALL_RESULT(2, uname, NULL); | 220 ASSERT_SYSCALL_RESULT(2, uname, NULL); |
| 283 ASSERT_SYSCALL_RESULT(3, uname, NULL); | 221 ASSERT_SYSCALL_RESULT(3, uname, NULL); |
| 284 } | 222 } |
| 285 #endif | |
| 286 | 223 |
| 287 class MaskingPolicy : public Policy { | 224 class MaskingPolicy : public Policy { |
| 288 public: | 225 public: |
| 289 MaskingPolicy() {} | 226 MaskingPolicy() {} |
| 290 ~MaskingPolicy() override {} | 227 ~MaskingPolicy() override {} |
| 291 ResultExpr EvaluateSyscall(int sysno) const override { | 228 ResultExpr EvaluateSyscall(int sysno) const override { |
| 292 if (sysno == __NR_setuid) { | 229 if (sysno == __NR_setuid) { |
| 293 const Arg<uid_t> uid(0); | 230 const Arg<uid_t> uid(0); |
| 294 return If((uid & 0xf) == 0, Error(EINVAL)).Else(Error(EACCES)); | 231 return If((uid & 0xf) == 0, Error(EINVAL)).Else(Error(EACCES)); |
| 295 } | 232 } |
| 296 if (sysno == __NR_setgid) { | 233 if (sysno == __NR_setgid) { |
| 297 const Arg<gid_t> gid(0); | 234 const Arg<gid_t> gid(0); |
| 298 return If((gid & 0xf0) == 0xf0, Error(EINVAL)).Else(Error(EACCES)); | 235 return If((gid & 0xf0) == 0xf0, Error(EINVAL)).Else(Error(EACCES)); |
| 299 } | 236 } |
| 300 if (sysno == __NR_setpgid) { | 237 if (sysno == __NR_setpgid) { |
| 301 const Arg<pid_t> pid(0); | 238 const Arg<pid_t> pid(0); |
| 302 return If((pid & 0xa5) == 0xa0, Error(EINVAL)).Else(Error(EACCES)); | 239 return If((pid & 0xa5) == 0xa0, Error(EINVAL)).Else(Error(EACCES)); |
| 303 } | 240 } |
| 304 return Allow(); | 241 return Allow(); |
| 305 } | 242 } |
| 306 | 243 |
| 307 private: | 244 private: |
| 308 DISALLOW_COPY_AND_ASSIGN(MaskingPolicy); | 245 DISALLOW_COPY_AND_ASSIGN(MaskingPolicy); |
| 309 }; | 246 }; |
| 310 | 247 |
| 311 TEST(BPFDSL, MaskTest) { | 248 BPF_TEST_C(BPFDSL, MaskTest, MaskingPolicy) { |
| 312 MaskingPolicy policy; | |
| 313 PolicyEmulator emulator(&policy); | |
| 314 | |
| 315 for (uid_t uid = 0; uid < 0x100; ++uid) { | 249 for (uid_t uid = 0; uid < 0x100; ++uid) { |
| 316 const int expect_errno = (uid & 0xf) == 0 ? EINVAL : EACCES; | 250 const int expect_errno = (uid & 0xf) == 0 ? EINVAL : EACCES; |
| 317 emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setuid, uid)); | 251 ASSERT_SYSCALL_RESULT(-expect_errno, setuid, uid); |
| 318 } | 252 } |
| 319 | 253 |
| 320 for (gid_t gid = 0; gid < 0x100; ++gid) { | 254 for (gid_t gid = 0; gid < 0x100; ++gid) { |
| 321 const int expect_errno = (gid & 0xf0) == 0xf0 ? EINVAL : EACCES; | 255 const int expect_errno = (gid & 0xf0) == 0xf0 ? EINVAL : EACCES; |
| 322 emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setgid, gid)); | 256 ASSERT_SYSCALL_RESULT(-expect_errno, setgid, gid); |
| 323 } | 257 } |
| 324 | 258 |
| 325 for (pid_t pid = 0; pid < 0x100; ++pid) { | 259 for (pid_t pid = 0; pid < 0x100; ++pid) { |
| 326 const int expect_errno = (pid & 0xa5) == 0xa0 ? EINVAL : EACCES; | 260 const int expect_errno = (pid & 0xa5) == 0xa0 ? EINVAL : EACCES; |
| 327 emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setpgid, pid, 0)); | 261 ASSERT_SYSCALL_RESULT(-expect_errno, setpgid, pid, 0); |
| 328 } | 262 } |
| 329 } | 263 } |
| 330 | 264 |
| 331 class ElseIfPolicy : public Policy { | 265 class ElseIfPolicy : public Policy { |
| 332 public: | 266 public: |
| 333 ElseIfPolicy() {} | 267 ElseIfPolicy() {} |
| 334 ~ElseIfPolicy() override {} | 268 ~ElseIfPolicy() override {} |
| 335 ResultExpr EvaluateSyscall(int sysno) const override { | 269 ResultExpr EvaluateSyscall(int sysno) const override { |
| 336 if (sysno == __NR_setuid) { | 270 if (sysno == __NR_setuid) { |
| 337 const Arg<uid_t> uid(0); | 271 const Arg<uid_t> uid(0); |
| 338 return If((uid & 0xfff) == 0, Error(0)) | 272 return If((uid & 0xfff) == 0, Error(0)) |
| 339 .ElseIf((uid & 0xff0) == 0, Error(EINVAL)) | 273 .ElseIf((uid & 0xff0) == 0, Error(EINVAL)) |
| 340 .ElseIf((uid & 0xf00) == 0, Error(EEXIST)) | 274 .ElseIf((uid & 0xf00) == 0, Error(EEXIST)) |
| 341 .Else(Error(EACCES)); | 275 .Else(Error(EACCES)); |
| 342 } | 276 } |
| 343 return Allow(); | 277 return Allow(); |
| 344 } | 278 } |
| 345 | 279 |
| 346 private: | 280 private: |
| 347 DISALLOW_COPY_AND_ASSIGN(ElseIfPolicy); | 281 DISALLOW_COPY_AND_ASSIGN(ElseIfPolicy); |
| 348 }; | 282 }; |
| 349 | 283 |
| 350 TEST(BPFDSL, ElseIfTest) { | 284 BPF_TEST_C(BPFDSL, ElseIfTest, ElseIfPolicy) { |
| 351 ElseIfPolicy policy; | 285 ASSERT_SYSCALL_RESULT(0, setuid, 0); |
| 352 PolicyEmulator emulator(&policy); | |
| 353 | 286 |
| 354 emulator.ExpectErrno(0, FakeSyscall(__NR_setuid, 0)); | 287 ASSERT_SYSCALL_RESULT(-EINVAL, setuid, 0x0001); |
| 288 ASSERT_SYSCALL_RESULT(-EINVAL, setuid, 0x0002); |
| 355 | 289 |
| 356 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setuid, 0x0001)); | 290 ASSERT_SYSCALL_RESULT(-EEXIST, setuid, 0x0011); |
| 357 emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setuid, 0x0002)); | 291 ASSERT_SYSCALL_RESULT(-EEXIST, setuid, 0x0022); |
| 358 | 292 |
| 359 emulator.ExpectErrno(EEXIST, FakeSyscall(__NR_setuid, 0x0011)); | 293 ASSERT_SYSCALL_RESULT(-EACCES, setuid, 0x0111); |
| 360 emulator.ExpectErrno(EEXIST, FakeSyscall(__NR_setuid, 0x0022)); | 294 ASSERT_SYSCALL_RESULT(-EACCES, setuid, 0x0222); |
| 361 | |
| 362 emulator.ExpectErrno(EACCES, FakeSyscall(__NR_setuid, 0x0111)); | |
| 363 emulator.ExpectErrno(EACCES, FakeSyscall(__NR_setuid, 0x0222)); | |
| 364 } | 295 } |
| 365 | 296 |
| 366 class SwitchPolicy : public Policy { | 297 class SwitchPolicy : public Policy { |
| 367 public: | 298 public: |
| 368 SwitchPolicy() {} | 299 SwitchPolicy() {} |
| 369 ~SwitchPolicy() override {} | 300 ~SwitchPolicy() override {} |
| 370 ResultExpr EvaluateSyscall(int sysno) const override { | 301 ResultExpr EvaluateSyscall(int sysno) const override { |
| 371 if (sysno == __NR_fcntl) { | 302 if (sysno == __NR_fcntl) { |
| 372 const Arg<int> cmd(1); | 303 const Arg<int> cmd(1); |
| 373 const Arg<unsigned long> long_arg(2); | 304 const Arg<unsigned long> long_arg(2); |
| 374 return Switch(cmd) | 305 return Switch(cmd) |
| 375 .CASES((F_GETFL, F_GETFD), Error(ENOENT)) | 306 .CASES((F_GETFL, F_GETFD), Error(ENOENT)) |
| 376 .Case(F_SETFD, If(long_arg == O_CLOEXEC, Allow()).Else(Error(EINVAL))) | 307 .Case(F_SETFD, If(long_arg == O_CLOEXEC, Allow()).Else(Error(EINVAL))) |
| 377 .Case(F_SETFL, Error(EPERM)) | 308 .Case(F_SETFL, Error(EPERM)) |
| 378 .Default(Error(EACCES)); | 309 .Default(Error(EACCES)); |
| 379 } | 310 } |
| 380 return Allow(); | 311 return Allow(); |
| 381 } | 312 } |
| 382 | 313 |
| 383 private: | 314 private: |
| 384 DISALLOW_COPY_AND_ASSIGN(SwitchPolicy); | 315 DISALLOW_COPY_AND_ASSIGN(SwitchPolicy); |
| 385 }; | 316 }; |
| 386 | 317 |
| 387 TEST(BPFDSL, SwitchTest) { | 318 BPF_TEST_C(BPFDSL, SwitchTest, SwitchPolicy) { |
| 388 SwitchPolicy policy; | 319 base::ScopedFD sock_fd(socket(AF_UNIX, SOCK_STREAM, 0)); |
| 389 PolicyEmulator emulator(&policy); | 320 BPF_ASSERT(sock_fd.is_valid()); |
| 390 | 321 |
| 391 const int kFakeSockFD = 42; | 322 ASSERT_SYSCALL_RESULT(-ENOENT, fcntl, sock_fd.get(), F_GETFD); |
| 323 ASSERT_SYSCALL_RESULT(-ENOENT, fcntl, sock_fd.get(), F_GETFL); |
| 392 | 324 |
| 393 emulator.ExpectErrno(ENOENT, FakeSyscall(__NR_fcntl, kFakeSockFD, F_GETFD)); | 325 ASSERT_SYSCALL_RESULT(0, fcntl, sock_fd.get(), F_SETFD, O_CLOEXEC); |
| 394 emulator.ExpectErrno(ENOENT, FakeSyscall(__NR_fcntl, kFakeSockFD, F_GETFL)); | 326 ASSERT_SYSCALL_RESULT(-EINVAL, fcntl, sock_fd.get(), F_SETFD, 0); |
| 395 | 327 |
| 396 emulator.ExpectAllow( | 328 ASSERT_SYSCALL_RESULT(-EPERM, fcntl, sock_fd.get(), F_SETFL, O_RDONLY); |
| 397 FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFD, O_CLOEXEC)); | |
| 398 emulator.ExpectErrno(EINVAL, | |
| 399 FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFD, 0)); | |
| 400 | 329 |
| 401 emulator.ExpectErrno(EPERM, | 330 ASSERT_SYSCALL_RESULT(-EACCES, fcntl, sock_fd.get(), F_DUPFD, 0); |
| 402 FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFL, O_RDONLY)); | |
| 403 | |
| 404 emulator.ExpectErrno(EACCES, | |
| 405 FakeSyscall(__NR_fcntl, kFakeSockFD, F_DUPFD, 0)); | |
| 406 } | 331 } |
| 407 | 332 |
| 408 static intptr_t DummyTrap(const struct arch_seccomp_data& data, void* aux) { | 333 static intptr_t DummyTrap(const struct arch_seccomp_data& data, void* aux) { |
| 409 return 0; | 334 return 0; |
| 410 } | 335 } |
| 411 | 336 |
| 412 TEST(BPFDSL, IsAllowDeny) { | 337 TEST(BPFDSL, IsAllowDeny) { |
| 413 ResultExpr allow = Allow(); | 338 ResultExpr allow = Allow(); |
| 414 EXPECT_TRUE(allow->IsAllow()); | 339 EXPECT_TRUE(allow->IsAllow()); |
| 415 EXPECT_FALSE(allow->IsDeny()); | 340 EXPECT_FALSE(allow->IsDeny()); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 443 EXPECT_TRUE(unsafe->HasUnsafeTraps()); | 368 EXPECT_TRUE(unsafe->HasUnsafeTraps()); |
| 444 | 369 |
| 445 const Arg<int> arg(0); | 370 const Arg<int> arg(0); |
| 446 ResultExpr maybe = If(arg == 0, allow).Else(unsafe); | 371 ResultExpr maybe = If(arg == 0, allow).Else(unsafe); |
| 447 EXPECT_TRUE(maybe->HasUnsafeTraps()); | 372 EXPECT_TRUE(maybe->HasUnsafeTraps()); |
| 448 } | 373 } |
| 449 | 374 |
| 450 } // namespace | 375 } // namespace |
| 451 } // namespace bpf_dsl | 376 } // namespace bpf_dsl |
| 452 } // namespace sandbox | 377 } // namespace sandbox |
| OLD | NEW |