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