1/* sigsegv.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 * Forces a denied system call to trigger a SIGSEGV at the instruction after
7 * the call using a SIGSYS handler.. This can be useful when debugging
8 * frameworks have trouble tracing through the SIGSYS handler.
9 * Proof of concept using amd64 registers and 'syscall'.
10 */
11
12#include <asm/siginfo.h>
13#define __have_siginfo_t 1
14#define __have_sigval_t 1
15#define __have_sigevent_t 1
16
17#include <linux/filter.h>
18#include <linux/prctl.h>
19#include <linux/seccomp.h>
20#include <limits.h>
21#include <stddef.h>
22#include <stdbool.h>
23#include <string.h>
24#include <syscall.h>
25#define __USE_GNU 1
26#include <sys/ucontext.h>
27#include <sys/mman.h>
28
29#include "test_harness.h"
30
31#ifndef PR_SET_NO_NEW_PRIVS
32#define PR_SET_NO_NEW_PRIVS 38
33#define PR_GET_NO_NEW_PRIVS 39
34#endif
35
36#if defined(__i386__)
37#define REG_IP	REG_EIP
38#define REG_SP	REG_ESP
39#define REG_RESULT	REG_EAX
40#define REG_SYSCALL	REG_EAX
41#define REG_ARG0	REG_EBX
42#define REG_ARG1	REG_ECX
43#define REG_ARG2	REG_EDX
44#define REG_ARG3	REG_ESI
45#define REG_ARG4	REG_EDI
46#define REG_ARG5	REG_EBP
47#elif defined(__x86_64__)
48#define REG_IP	REG_RIP
49#define REG_SP	REG_RSP
50#define REG_RESULT	REG_RAX
51#define REG_SYSCALL	REG_RAX
52#define REG_ARG0	REG_RDI
53#define REG_ARG1	REG_RSI
54#define REG_ARG2	REG_RDX
55#define REG_ARG3	REG_R10
56#define REG_ARG4	REG_R8
57#define REG_ARG5	REG_R9
58#endif
59
60FIXTURE_DATA(TRAP) {
61	struct sock_fprog prog;
62};
63
64FIXTURE_SETUP(TRAP) {
65	/* instruction after the syscall. Will be arch specific, of course. */
66	{
67		struct sock_filter filter[] = {
68			BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
69				offsetof(struct seccomp_data, nr)),
70			/* Whitelist anything you might need in the sigaction */
71#ifdef __NR_sigreturn
72			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_sigreturn, 4, 0),
73#endif
74			/* TODO: only allow PROT_NONE */
75			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_mprotect, 3, 0),
76			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit, 2, 0),
77			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_rt_sigreturn, 1, 0),
78			/* Allow __NR_write so easy logging. */
79			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_write, 0, 1),
80			BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
81			BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP),
82		};
83		memset(&self->prog, 0, sizeof(self->prog));
84		self->prog.filter = malloc(sizeof(filter));
85		ASSERT_NE(NULL, self->prog.filter);
86		memcpy(self->prog.filter, filter, sizeof(filter));
87		self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
88	}
89}
90
91FIXTURE_TEARDOWN(TRAP) {
92	if (self->prog.filter)
93		free(self->prog.filter);
94};
95
96struct arch_sigsys {
97		void *_call_addr; /* calling user insn */
98		int _syscall;	/* triggering system call number */
99		unsigned int _arch;	/* AUDIT_ARCH_* of syscall */
100};
101
102#define _ALIGN(x,sz) (((x + ((sz)-1)) & ~((sz)-1)) - (sz))
103#define ALIGN(x,sz) ((typeof(x))_ALIGN((unsigned long)(x),(unsigned long)(sz)))
104static long local_mprotect(void *target, unsigned long sz)
105{
106	register unsigned long res asm ("rax") = __NR_mprotect;
107	register void *addr asm ("rdi") = ALIGN(target, sz);
108	register long len asm ("rsi") = sz;
109	register long num asm ("rdx") = PROT_NONE;
110	__asm__("syscall\n");
111	return res;
112}
113
114static void TRAP_action(int nr, siginfo_t *info, void *void_context)
115{
116	ucontext_t *ctx = (ucontext_t *)void_context;
117	char buf[256];
118	int len;
119	int do_ret = 1;
120	struct arch_sigsys *sys = (struct arch_sigsys *)
121#ifdef si_syscall
122		&(info->si_call_addr);
123#else
124		&(info->si_pid);
125#endif
126
127	if (info->si_code != SYS_SECCOMP)
128		return;
129	if (!ctx)
130		return;
131	len = snprintf(buf, sizeof(buf),
132			"@0x%lX:%X:%d:0x%lX:0x%lX:0x%lX:0x%lX:0x%lX:0x%lX [0x%lX]\n",
133			(unsigned long)sys->_call_addr,
134			sys->_arch,
135			sys->_syscall,
136			ctx->uc_mcontext.gregs[REG_ARG0],
137			ctx->uc_mcontext.gregs[REG_ARG1],
138			ctx->uc_mcontext.gregs[REG_ARG2],
139			ctx->uc_mcontext.gregs[REG_ARG3],
140			ctx->uc_mcontext.gregs[REG_ARG4],
141			ctx->uc_mcontext.gregs[REG_ARG5],
142			ALIGN(ctx->uc_mcontext.gregs[REG_IP], 4096));
143	/* Emit some useful logs or whatever. */
144	syscall(__NR_write, STDOUT_FILENO, buf, len);
145	/* Make the calling page non-exec */
146	/* Careful on how it is called since it may make the syscall() instructions non-exec. */
147	local_mprotect((void *)ctx->uc_mcontext.gregs[REG_IP], sysconf(_SC_PAGE_SIZE));
148}
149
150TEST_F_SIGNAL(TRAP, sigsegv, SIGSEGV) {
151	int ret;
152	struct sigaction act;
153	pid_t pid;
154	sigset_t mask;
155	memset(&act, 0, sizeof(act));
156	sigemptyset(&mask);
157	sigaddset(&mask, SIGSYS);
158
159	act.sa_sigaction = &TRAP_action;
160	act.sa_flags = SA_SIGINFO;
161	ret = sigaction(SIGSYS, &act, NULL);
162	ASSERT_EQ(0, ret) {
163		TH_LOG("sigaction failed");
164	}
165	ret = sigprocmask(SIG_UNBLOCK, &mask, NULL);
166	ASSERT_EQ(0, ret) {
167		TH_LOG("sigprocmask failed");
168	}
169
170	/* Get the pid to compare against. */
171	pid = getpid();
172
173	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
174	ASSERT_EQ(0, ret);
175	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
176	ASSERT_EQ(0, ret);
177
178	/* Call anything! */
179	ret = syscall(__NR_getpid);
180}
181
182TEST_HARNESS_MAIN
183