• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TEST(Syscall,InvalidCallReturnsENOSYS)45 TEST(Syscall, InvalidCallReturnsENOSYS) {
46   EXPECT_EQ(-ENOSYS, Syscall::InvalidCall());
47 }
48 
TEST(Syscall,WellKnownEntryPoint)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 
TEST(Syscall,TrivialSyscallNoArgs)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 
TEST(Syscall,TrivialSyscallOneArg)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 
TEST(Syscall,TrivialFailingSyscall)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.
CopySyscallArgsToAux(const struct arch_seccomp_data & args,void * aux)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:
CopyAllArgsOnUnamePolicy(std::vector<uint64_t> * aux)113   explicit CopyAllArgsOnUnamePolicy(std::vector<uint64_t>* aux) : aux_(aux) {}
~CopyAllArgsOnUnamePolicy()114   ~CopyAllArgsOnUnamePolicy() override {}
115 
EvaluateSyscall(int sysno) const116   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.
BPF_TEST(Syscall,SyntheticSixArgs,CopyAllArgsOnUnamePolicy,std::vector<uint64_t>)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 
TEST(Syscall,ComplexSyscallSixArgs)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