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