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 19 static 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 34 bool 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 do_exit(SIGSEGV); 52 53 if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW)) 54 return false; 55 56 if (state != SYSCALL_DISPATCH_FILTER_BLOCK) 57 do_exit(SIGSYS); 58 } 59 60 sd->on_dispatch = true; 61 syscall_rollback(current, regs); 62 trigger_sigsys(regs); 63 64 return true; 65 } 66 67 int set_syscall_user_dispatch(unsigned long mode, unsigned long offset, 68 unsigned long len, char __user *selector) 69 { 70 switch (mode) { 71 case PR_SYS_DISPATCH_OFF: 72 if (offset || len || selector) 73 return -EINVAL; 74 break; 75 case PR_SYS_DISPATCH_ON: 76 /* 77 * Validate the direct dispatcher region just for basic 78 * sanity against overflow and a 0-sized dispatcher 79 * region. If the user is able to submit a syscall from 80 * an address, that address is obviously valid. 81 */ 82 if (offset && offset + len <= offset) 83 return -EINVAL; 84 85 if (selector && !access_ok(selector, sizeof(*selector))) 86 return -EFAULT; 87 88 break; 89 default: 90 return -EINVAL; 91 } 92 93 current->syscall_dispatch.selector = selector; 94 current->syscall_dispatch.offset = offset; 95 current->syscall_dispatch.len = len; 96 current->syscall_dispatch.on_dispatch = false; 97 98 if (mode == PR_SYS_DISPATCH_ON) 99 set_syscall_work(SYSCALL_USER_DISPATCH); 100 else 101 clear_syscall_work(SYSCALL_USER_DISPATCH); 102 103 return 0; 104 } 105