1 /* seccomp_bpf_tests.c
2 * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 *
6 * Test code for seccomp bpf.
7 */
8
9 #include <asm/siginfo.h>
10 #define __have_siginfo_t 1
11 #define __have_sigval_t 1
12 #define __have_sigevent_t 1
13
14 #include <linux/filter.h>
15 #include <sys/prctl.h>
16 #include <linux/prctl.h>
17 #include <linux/seccomp.h>
18 #include <stddef.h>
19 #include <stdbool.h>
20 #include <string.h>
21 #include <syscall.h>
22 #define __USE_GNU 1
23 #include <sys/ucontext.h>
24 #include <sys/mman.h>
25
26 #include "test_harness.h"
27
28 #ifndef PR_SET_NO_NEW_PRIVS
29 #define PR_SET_NO_NEW_PRIVS 38
30 #define PR_GET_NO_NEW_PRIVS 39
31 #endif
32
33 #if defined(__i386__)
34 #define REG_IP REG_EIP
35 #define REG_SP REG_ESP
36 #define REG_RESULT REG_EAX
37 #define REG_SYSCALL REG_EAX
38 #define REG_ARG0 REG_EBX
39 #define REG_ARG1 REG_ECX
40 #define REG_ARG2 REG_EDX
41 #define REG_ARG3 REG_ESI
42 #define REG_ARG4 REG_EDI
43 #define REG_ARG5 REG_EBP
44 #elif defined(__x86_64__)
45 #define REG_IP REG_RIP
46 #define REG_SP REG_RSP
47 #define REG_RESULT REG_RAX
48 #define REG_SYSCALL REG_RAX
49 #define REG_ARG0 REG_RDI
50 #define REG_ARG1 REG_RSI
51 #define REG_ARG2 REG_RDX
52 #define REG_ARG3 REG_R10
53 #define REG_ARG4 REG_R8
54 #define REG_ARG5 REG_R9
55 #endif
56
FIXTURE_DATA(TRAP)57 FIXTURE_DATA(TRAP) {
58 struct sock_fprog prog;
59 };
60
61 /* XXX: will need one per arch, etc.
62 * thankfully _arch can tell us the calling convention!
63 */
64 extern void *thunk_ip; /* label for the instruction _after_ syscall */
syscall_thunk(void)65 static void syscall_thunk(void)
66 {
67 asm("syscall; thunk_ip:");
68 }
69
vsyscall_time(time_t * p)70 static time_t vsyscall_time(time_t *p)
71 {
72 register time_t t asm ("rax");
73 __attribute__((unused)) register time_t *p1 asm ("rdi") = p;
74 __asm__("call 0xffffffffff600400 \n");
75 return t;
76 }
77
78
79 #if 0
80 /* For instance, we could jump here instead. */
81 static void compat_thunk(void)
82 {
83 asm("int 0x80");
84 }
85 #endif
86
FIXTURE_SETUP(TRAP)87 FIXTURE_SETUP(TRAP) {
88 /* instruction after the syscall. Will be arch specific, of course. */
89 unsigned long thunk_addr = (unsigned long)&thunk_ip;
90 TH_LOG("Thunk: 0x%lX\n", thunk_addr);
91 {
92 struct sock_filter filter[] = {
93 BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
94 offsetof(struct seccomp_data, nr)),
95 /* Whitelist anything you might need in the sigaction */
96 #ifdef __NR_sigreturn
97 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_sigreturn, 3, 0),
98 #endif
99 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit, 2, 0),
100 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_rt_sigreturn, 1, 0),
101 /* Allow __NR_write so easy logging. */
102 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_write, 0, 1),
103 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
104 /* Check if we're within the thunk. */
105 BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
106 offsetof(struct seccomp_data, instruction_pointer)),
107 /* XXX: make this 32-bit friendly. */
108 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ((__u32*)&thunk_addr)[0], 0, 3),
109 BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
110 offsetof(struct seccomp_data, instruction_pointer)+sizeof(int)),
111 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ((__u32*)&thunk_addr)[1], 0, 1),
112 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
113 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP),
114 };
115 memset(&self->prog, 0, sizeof(self->prog));
116 self->prog.filter = malloc(sizeof(filter));
117 ASSERT_NE(NULL, self->prog.filter);
118 memcpy(self->prog.filter, filter, sizeof(filter));
119 self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
120 }
121 }
122
FIXTURE_TEARDOWN(TRAP)123 FIXTURE_TEARDOWN(TRAP) {
124 if (self->prog.filter)
125 free(self->prog.filter);
126 };
127
128 struct arch_sigsys {
129 void *_call_addr; /* calling user insn */
130 int _syscall; /* triggering system call number */
131 unsigned int _arch; /* AUDIT_ARCH_* of syscall */
132 };
133
TRAP_action(int nr,siginfo_t * info,void * void_context)134 static void TRAP_action(int nr, siginfo_t *info, void *void_context)
135 {
136 ucontext_t *ctx = (ucontext_t *)void_context;
137 char buf[256];
138 int len;
139 int do_ret = 1;
140 struct arch_sigsys *sys = (struct arch_sigsys *)
141 #ifdef si_syscall
142 &(info->si_call_addr);
143 #else
144 &(info->si_pid);
145 #endif
146
147 if (info->si_code != SYS_SECCOMP)
148 return;
149 if (!ctx)
150 return;
151 len = snprintf(buf, sizeof(buf),
152 "@0x%lX:%X:%d:0x%lX:0x%lX:0x%lX:0x%lX:0x%lX:0x%lX\n",
153 (unsigned long)sys->_call_addr,
154 sys->_arch,
155 sys->_syscall,
156 (unsigned long)ctx->uc_mcontext.gregs[REG_ARG0],
157 (unsigned long)ctx->uc_mcontext.gregs[REG_ARG1],
158 (unsigned long)ctx->uc_mcontext.gregs[REG_ARG2],
159 (unsigned long)ctx->uc_mcontext.gregs[REG_ARG3],
160 (unsigned long)ctx->uc_mcontext.gregs[REG_ARG4],
161 (unsigned long)ctx->uc_mcontext.gregs[REG_ARG5]);
162 /* Send the soft-fail to our "listener" */
163 syscall(__NR_write, STDOUT_FILENO, buf, len);
164 if (ctx->uc_mcontext.gregs[REG_IP] >= 0xffffffffff600000ULL &&
165 ctx->uc_mcontext.gregs[REG_IP] < 0xffffffffff601000ULL)
166 do_ret = 0;
167 if (do_ret) {
168 /* push [REG_IP] */
169 ctx->uc_mcontext.gregs[REG_SP] -= sizeof(unsigned long);
170 *((unsigned long *)ctx->uc_mcontext.gregs[REG_SP]) =
171 ctx->uc_mcontext.gregs[REG_IP];
172 }
173 /* jmp syscall_thunk */
174 ctx->uc_mcontext.gregs[REG_IP] = (unsigned long)syscall_thunk;
175 return;
176 }
177
TEST_F(TRAP,handler)178 TEST_F(TRAP, handler) {
179 int ret;
180 struct sigaction act;
181 pid_t pid;
182 sigset_t mask;
183 memset(&act, 0, sizeof(act));
184 sigemptyset(&mask);
185 sigaddset(&mask, SIGSYS);
186
187 act.sa_sigaction = &TRAP_action;
188 act.sa_flags = SA_SIGINFO;
189 ret = sigaction(SIGSYS, &act, NULL);
190 ASSERT_EQ(0, ret) {
191 TH_LOG("sigaction failed");
192 }
193 ret = sigprocmask(SIG_UNBLOCK, &mask, NULL);
194 ASSERT_EQ(0, ret) {
195 TH_LOG("sigprocmask failed");
196 }
197
198 /* Get the pid to compare against. */
199 pid = getpid();
200
201 ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
202 ASSERT_EQ(0, ret);
203 ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
204 ASSERT_EQ(0, ret);
205
206 /* Call anything! */
207 ret = syscall(__NR_getpid);
208 ASSERT_EQ(pid, ret);
209 ret = syscall(__NR_close, 0);
210 ASSERT_EQ(0, ret);
211 ret = syscall(__NR_close, 0);
212 ASSERT_EQ(-1, ret);
213 printf("The time is %ld\n", vsyscall_time(NULL));
214 ASSERT_LT(0, vsyscall_time(NULL));
215 }
216
217 TEST_HARNESS_MAIN
218