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