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 <sys/prctl.h>
19 #include <linux/prctl.h>
20 #include <linux/seccomp.h>
21 #include <limits.h>
22 #include <stddef.h>
23 #include <stdbool.h>
24 #include <string.h>
25 #include <syscall.h>
26 #define __USE_GNU 1
27 #include <sys/ucontext.h>
28 #include <sys/mman.h>
29 
30 #include "test_harness.h"
31 
32 #ifndef PR_SET_NO_NEW_PRIVS
33 #define PR_SET_NO_NEW_PRIVS 38
34 #define PR_GET_NO_NEW_PRIVS 39
35 #endif
36 
37 #if defined(__i386__)
38 #define REG_IP	REG_EIP
39 #define REG_SP	REG_ESP
40 #define REG_RESULT	REG_EAX
41 #define REG_SYSCALL	REG_EAX
42 #define REG_ARG0	REG_EBX
43 #define REG_ARG1	REG_ECX
44 #define REG_ARG2	REG_EDX
45 #define REG_ARG3	REG_ESI
46 #define REG_ARG4	REG_EDI
47 #define REG_ARG5	REG_EBP
48 #elif defined(__x86_64__)
49 #define REG_IP	REG_RIP
50 #define REG_SP	REG_RSP
51 #define REG_RESULT	REG_RAX
52 #define REG_SYSCALL	REG_RAX
53 #define REG_ARG0	REG_RDI
54 #define REG_ARG1	REG_RSI
55 #define REG_ARG2	REG_RDX
56 #define REG_ARG3	REG_R10
57 #define REG_ARG4	REG_R8
58 #define REG_ARG5	REG_R9
59 #endif
60 
FIXTURE_DATA(TRAP)61 FIXTURE_DATA(TRAP) {
62 	struct sock_fprog prog;
63 };
64 
FIXTURE_SETUP(TRAP)65 FIXTURE_SETUP(TRAP) {
66 	/* instruction after the syscall. Will be arch specific, of course. */
67 	{
68 		struct sock_filter filter[] = {
69 			BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
70 				offsetof(struct seccomp_data, nr)),
71 			/* Whitelist anything you might need in the sigaction */
72 #ifdef __NR_sigreturn
73 			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_sigreturn, 4, 0),
74 #endif
75 			/* TODO: only allow PROT_NONE */
76 			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_mprotect, 3, 0),
77 			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit, 2, 0),
78 			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_rt_sigreturn, 1, 0),
79 			/* Allow __NR_write so easy logging. */
80 			BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_write, 0, 1),
81 			BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
82 			BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP),
83 		};
84 		memset(&self->prog, 0, sizeof(self->prog));
85 		self->prog.filter = malloc(sizeof(filter));
86 		ASSERT_NE(NULL, self->prog.filter);
87 		memcpy(self->prog.filter, filter, sizeof(filter));
88 		self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
89 	}
90 }
91 
FIXTURE_TEARDOWN(TRAP)92 FIXTURE_TEARDOWN(TRAP) {
93 	if (self->prog.filter)
94 		free(self->prog.filter);
95 };
96 
97 struct arch_sigsys {
98 		void *_call_addr; /* calling user insn */
99 		int _syscall;	/* triggering system call number */
100 		unsigned int _arch;	/* AUDIT_ARCH_* of syscall */
101 };
102 
103 #define _ALIGN(x,sz) (((x + ((sz)-1)) & ~((sz)-1)) - (sz))
104 #define ALIGN(x,sz) ((typeof(x))_ALIGN((unsigned long)(x),(unsigned long)(sz)))
local_mprotect(void * target,unsigned long sz)105 static long local_mprotect(void *target, unsigned long sz)
106 {
107 	register unsigned long res asm ("rax") = __NR_mprotect;
108 	__attribute__((unused)) register void *addr asm ("rdi") = ALIGN(target, sz);
109 	__attribute__((unused)) register long len asm ("rsi") = sz;
110 	__attribute__((unused)) register long num asm ("rdx") = PROT_NONE;
111 	__asm__("syscall\n");
112 	return res;
113 }
114 
TRAP_action(int nr,siginfo_t * info,void * void_context)115 static void TRAP_action(int nr, siginfo_t *info, void *void_context)
116 {
117 	ucontext_t *ctx = (ucontext_t *)void_context;
118 	char buf[256];
119 	int len;
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 			(unsigned long)ctx->uc_mcontext.gregs[REG_ARG0],
137 			(unsigned long)ctx->uc_mcontext.gregs[REG_ARG1],
138 			(unsigned long)ctx->uc_mcontext.gregs[REG_ARG2],
139 			(unsigned long)ctx->uc_mcontext.gregs[REG_ARG3],
140 			(unsigned long)ctx->uc_mcontext.gregs[REG_ARG4],
141 			(unsigned long)ctx->uc_mcontext.gregs[REG_ARG5],
142 			(unsigned long)ALIGN(ctx->uc_mcontext.gregs[REG_IP],
143 			4096));
144 	/* Emit some useful logs or whatever. */
145 	syscall(__NR_write, STDOUT_FILENO, buf, len);
146 	/* Make the calling page non-exec */
147 	/* Careful on how it is called since it may make the syscall() instructions non-exec. */
148 	local_mprotect((void *)ctx->uc_mcontext.gregs[REG_IP], sysconf(_SC_PAGE_SIZE));
149 }
150 
TEST_F_SIGNAL(TRAP,sigsegv,SIGSEGV)151 TEST_F_SIGNAL(TRAP, sigsegv, SIGSEGV) {
152 	int ret;
153 	struct sigaction act;
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 	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
171 	ASSERT_EQ(0, ret);
172 	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
173 	ASSERT_EQ(0, ret);
174 
175 	/* Call anything! */
176 	ret = syscall(__NR_getpid);
177 }
178 
179 TEST_HARNESS_MAIN
180