Home | History | Annotate | Download | only in seccomp-bpf
      1 // Copyright (c) 2012 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 <asm/unistd.h>
      6 #include <fcntl.h>
      7 #include <sys/mman.h>
      8 #include <sys/syscall.h>
      9 #include <unistd.h>
     10 
     11 #include <vector>
     12 
     13 #include "base/basictypes.h"
     14 #include "base/posix/eintr_wrapper.h"
     15 #include "sandbox/linux/seccomp-bpf/bpf_tests.h"
     16 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
     17 #include "sandbox/linux/seccomp-bpf/syscall.h"
     18 #include "sandbox/linux/tests/unit_tests.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 
     21 namespace sandbox {
     22 
     23 namespace {
     24 
     25 // Different platforms use different symbols for the six-argument version
     26 // of the mmap() system call. Test for the correct symbol at compile time.
     27 #ifdef __NR_mmap2
     28 const int kMMapNr = __NR_mmap2;
     29 #else
     30 const int kMMapNr = __NR_mmap;
     31 #endif
     32 
     33 TEST(Syscall, WellKnownEntryPoint) {
     34 // Test that SandboxSyscall(-1) is handled specially. Don't do this on ARM,
     35 // where syscall(-1) crashes with SIGILL. Not running the test is fine, as we
     36 // are still testing ARM code in the next set of tests.
     37 #if !defined(__arm__)
     38   EXPECT_NE(SandboxSyscall(-1), syscall(-1));
     39 #endif
     40 
     41 // If possible, test that SandboxSyscall(-1) returns the address right after
     42 // a kernel entry point.
     43 #if defined(__i386__)
     44   EXPECT_EQ(0x80CDu, ((uint16_t*)SandboxSyscall(-1))[-1]);  // INT 0x80
     45 #elif defined(__x86_64__)
     46   EXPECT_EQ(0x050Fu, ((uint16_t*)SandboxSyscall(-1))[-1]);  // SYSCALL
     47 #elif defined(__arm__)
     48 #if defined(__thumb__)
     49   EXPECT_EQ(0xDF00u, ((uint16_t*)SandboxSyscall(-1))[-1]);  // SWI 0
     50 #else
     51   EXPECT_EQ(0xEF000000u, ((uint32_t*)SandboxSyscall(-1))[-1]);  // SVC 0
     52 #endif
     53 #else
     54 #warning Incomplete test case; need port for target platform
     55 #endif
     56 }
     57 
     58 TEST(Syscall, TrivialSyscallNoArgs) {
     59   // Test that we can do basic system calls
     60   EXPECT_EQ(SandboxSyscall(__NR_getpid), syscall(__NR_getpid));
     61 }
     62 
     63 TEST(Syscall, TrivialSyscallOneArg) {
     64   int new_fd;
     65   // Duplicate standard error and close it.
     66   ASSERT_GE(new_fd = SandboxSyscall(__NR_dup, 2), 0);
     67   int close_return_value = IGNORE_EINTR(SandboxSyscall(__NR_close, new_fd));
     68   ASSERT_EQ(close_return_value, 0);
     69 }
     70 
     71 // SIGSYS trap handler that will be called on __NR_uname.
     72 intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) {
     73   // |aux| is a pointer to our BPF_AUX.
     74   std::vector<uint64_t>* const seen_syscall_args =
     75       static_cast<std::vector<uint64_t>*>(aux);
     76   BPF_ASSERT(arraysize(args.args) == 6);
     77   seen_syscall_args->assign(args.args, args.args + arraysize(args.args));
     78   return -ENOMEM;
     79 }
     80 
     81 ErrorCode CopyAllArgsOnUnamePolicy(SandboxBPF* sandbox, int sysno, void* aux) {
     82   if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
     83     return ErrorCode(ENOSYS);
     84   }
     85   if (sysno == __NR_uname) {
     86     return sandbox->Trap(CopySyscallArgsToAux, aux);
     87   } else {
     88     return ErrorCode(ErrorCode::ERR_ALLOWED);
     89   }
     90 }
     91 
     92 // We are testing SandboxSyscall() by making use of a BPF filter that allows us
     93 // to inspect the system call arguments that the kernel saw.
     94 BPF_TEST(Syscall,
     95          SyntheticSixArgs,
     96          CopyAllArgsOnUnamePolicy,
     97          std::vector<uint64_t> /* BPF_AUX */) {
     98   const int kExpectedValue = 42;
     99   // In this test we only pass integers to the kernel. We might want to make
    100   // additional tests to try other types. What we will see depends on
    101   // implementation details of kernel BPF filters and we will need to document
    102   // the expected behavior very clearly.
    103   int syscall_args[6];
    104   for (size_t i = 0; i < arraysize(syscall_args); ++i) {
    105     syscall_args[i] = kExpectedValue + i;
    106   }
    107 
    108   // We could use pretty much any system call we don't need here. uname() is
    109   // nice because it doesn't have any dangerous side effects.
    110   BPF_ASSERT(SandboxSyscall(__NR_uname,
    111                             syscall_args[0],
    112                             syscall_args[1],
    113                             syscall_args[2],
    114                             syscall_args[3],
    115                             syscall_args[4],
    116                             syscall_args[5]) == -ENOMEM);
    117 
    118   // We expect the trap handler to have copied the 6 arguments.
    119   BPF_ASSERT(BPF_AUX.size() == 6);
    120 
    121   // Don't loop here so that we can see which argument does cause the failure
    122   // easily from the failing line.
    123   // uint64_t is the type passed to our SIGSYS handler.
    124   BPF_ASSERT(BPF_AUX[0] == static_cast<uint64_t>(syscall_args[0]));
    125   BPF_ASSERT(BPF_AUX[1] == static_cast<uint64_t>(syscall_args[1]));
    126   BPF_ASSERT(BPF_AUX[2] == static_cast<uint64_t>(syscall_args[2]));
    127   BPF_ASSERT(BPF_AUX[3] == static_cast<uint64_t>(syscall_args[3]));
    128   BPF_ASSERT(BPF_AUX[4] == static_cast<uint64_t>(syscall_args[4]));
    129   BPF_ASSERT(BPF_AUX[5] == static_cast<uint64_t>(syscall_args[5]));
    130 }
    131 
    132 TEST(Syscall, ComplexSyscallSixArgs) {
    133   int fd;
    134   ASSERT_LE(0, fd = SandboxSyscall(__NR_open, "/dev/null", O_RDWR, 0L));
    135 
    136   // Use mmap() to allocate some read-only memory
    137   char* addr0;
    138   ASSERT_NE((char*)NULL,
    139             addr0 = reinterpret_cast<char*>(
    140                 SandboxSyscall(kMMapNr,
    141                                (void*)NULL,
    142                                4096,
    143                                PROT_READ,
    144                                MAP_PRIVATE | MAP_ANONYMOUS,
    145                                fd,
    146                                0L)));
    147 
    148   // Try to replace the existing mapping with a read-write mapping
    149   char* addr1;
    150   ASSERT_EQ(addr0,
    151             addr1 = reinterpret_cast<char*>(
    152                 SandboxSyscall(kMMapNr,
    153                                addr0,
    154                                4096L,
    155                                PROT_READ | PROT_WRITE,
    156                                MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
    157                                fd,
    158                                0L)));
    159   ++*addr1;  // This should not seg fault
    160 
    161   // Clean up
    162   EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr1, 4096L));
    163   EXPECT_EQ(0, IGNORE_EINTR(SandboxSyscall(__NR_close, fd)));
    164 
    165   // Check that the offset argument (i.e. the sixth argument) is processed
    166   // correctly.
    167   ASSERT_GE(fd = SandboxSyscall(__NR_open, "/proc/self/exe", O_RDONLY, 0L), 0);
    168   char* addr2, *addr3;
    169   ASSERT_NE((char*)NULL,
    170             addr2 = reinterpret_cast<char*>(SandboxSyscall(
    171                 kMMapNr, (void*)NULL, 8192L, PROT_READ, MAP_PRIVATE, fd, 0L)));
    172   ASSERT_NE((char*)NULL,
    173             addr3 = reinterpret_cast<char*>(SandboxSyscall(kMMapNr,
    174                                                            (void*)NULL,
    175                                                            4096L,
    176                                                            PROT_READ,
    177                                                            MAP_PRIVATE,
    178                                                            fd,
    179 #if defined(__NR_mmap2)
    180                                                            1L
    181 #else
    182                                                            4096L
    183 #endif
    184                                                            )));
    185   EXPECT_EQ(0, memcmp(addr2 + 4096, addr3, 4096));
    186 
    187   // Just to be absolutely on the safe side, also verify that the file
    188   // contents matches what we are getting from a read() operation.
    189   char buf[8192];
    190   EXPECT_EQ(8192, SandboxSyscall(__NR_read, fd, buf, 8192L));
    191   EXPECT_EQ(0, memcmp(addr2, buf, 8192));
    192 
    193   // Clean up
    194   EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr2, 8192L));
    195   EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr3, 4096L));
    196   EXPECT_EQ(0, IGNORE_EINTR(SandboxSyscall(__NR_close, fd)));
    197 }
    198 
    199 }  // namespace
    200 
    201 }  // namespace sandbox
    202