syscall_user_dispatch.c (2500B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2020 Collabora Ltd. 4 */ 5#include <linux/sched.h> 6#include <linux/prctl.h> 7#include <linux/syscall_user_dispatch.h> 8#include <linux/uaccess.h> 9#include <linux/signal.h> 10#include <linux/elf.h> 11 12#include <linux/sched/signal.h> 13#include <linux/sched/task_stack.h> 14 15#include <asm/syscall.h> 16 17#include "common.h" 18 19static void trigger_sigsys(struct pt_regs *regs) 20{ 21 struct kernel_siginfo info; 22 23 clear_siginfo(&info); 24 info.si_signo = SIGSYS; 25 info.si_code = SYS_USER_DISPATCH; 26 info.si_call_addr = (void __user *)KSTK_EIP(current); 27 info.si_errno = 0; 28 info.si_arch = syscall_get_arch(current); 29 info.si_syscall = syscall_get_nr(current, regs); 30 31 force_sig_info(&info); 32} 33 34bool syscall_user_dispatch(struct pt_regs *regs) 35{ 36 struct syscall_user_dispatch *sd = ¤t->syscall_dispatch; 37 char state; 38 39 if (likely(instruction_pointer(regs) - sd->offset < sd->len)) 40 return false; 41 42 if (unlikely(arch_syscall_is_vdso_sigreturn(regs))) 43 return false; 44 45 if (likely(sd->selector)) { 46 /* 47 * access_ok() is performed once, at prctl time, when 48 * the selector is loaded by userspace. 49 */ 50 if (unlikely(__get_user(state, sd->selector))) { 51 force_exit_sig(SIGSEGV); 52 return true; 53 } 54 55 if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW)) 56 return false; 57 58 if (state != SYSCALL_DISPATCH_FILTER_BLOCK) { 59 force_exit_sig(SIGSYS); 60 return true; 61 } 62 } 63 64 sd->on_dispatch = true; 65 syscall_rollback(current, regs); 66 trigger_sigsys(regs); 67 68 return true; 69} 70 71int set_syscall_user_dispatch(unsigned long mode, unsigned long offset, 72 unsigned long len, char __user *selector) 73{ 74 switch (mode) { 75 case PR_SYS_DISPATCH_OFF: 76 if (offset || len || selector) 77 return -EINVAL; 78 break; 79 case PR_SYS_DISPATCH_ON: 80 /* 81 * Validate the direct dispatcher region just for basic 82 * sanity against overflow and a 0-sized dispatcher 83 * region. If the user is able to submit a syscall from 84 * an address, that address is obviously valid. 85 */ 86 if (offset && offset + len <= offset) 87 return -EINVAL; 88 89 if (selector && !access_ok(selector, sizeof(*selector))) 90 return -EFAULT; 91 92 break; 93 default: 94 return -EINVAL; 95 } 96 97 current->syscall_dispatch.selector = selector; 98 current->syscall_dispatch.offset = offset; 99 current->syscall_dispatch.len = len; 100 current->syscall_dispatch.on_dispatch = false; 101 102 if (mode == PR_SYS_DISPATCH_ON) 103 set_syscall_work(SYSCALL_USER_DISPATCH); 104 else 105 clear_syscall_work(SYSCALL_USER_DISPATCH); 106 107 return 0; 108}