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