• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FIXTURE_DATA(TRAP)60 FIXTURE_DATA(TRAP) {
61 	struct sock_fprog prog;
62 };
63 
FIXTURE_SETUP(TRAP)64 FIXTURE_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 
FIXTURE_TEARDOWN(TRAP)91 FIXTURE_TEARDOWN(TRAP) {
92 	if (self->prog.filter)
93 		free(self->prog.filter);
94 };
95 
96 struct 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)))
local_mprotect(void * target,unsigned long sz)104 static 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 
TRAP_action(int nr,siginfo_t * info,void * void_context)114 static 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 
TEST_F_SIGNAL(TRAP,sigsegv,SIGSEGV)150 TEST_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 
182 TEST_HARNESS_MAIN
183