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