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/ptrace.h> 8 #include <linux/syscall_user_dispatch.h> 9 #include <linux/uaccess.h> 10 #include <linux/signal.h> 11 #include <linux/elf.h> 12 13 #include <linux/sched/signal.h> 14 #include <linux/sched/task_stack.h> 15 16 #include <asm/syscall.h> 17 18 #include "common.h" 19 20 static void trigger_sigsys(struct pt_regs *regs) 21 { 22 struct kernel_siginfo info; 23 24 clear_siginfo(&info); 25 info.si_signo = SIGSYS; 26 info.si_code = SYS_USER_DISPATCH; 27 info.si_call_addr = (void __user *)KSTK_EIP(current); 28 info.si_errno = 0; 29 info.si_arch = syscall_get_arch(current); 30 info.si_syscall = syscall_get_nr(current, regs); 31 32 force_sig_info(&info); 33 } 34 35 bool syscall_user_dispatch(struct pt_regs *regs) 36 { 37 struct syscall_user_dispatch *sd = ¤t->syscall_dispatch; 38 char state; 39 40 if (likely(instruction_pointer(regs) - sd->offset < sd->len)) 41 return false; 42 43 if (unlikely(arch_syscall_is_vdso_sigreturn(regs))) 44 return false; 45 46 if (likely(sd->selector)) { 47 /* 48 * access_ok() is performed once, at prctl time, when 49 * the selector is loaded by userspace. 50 */ 51 if (unlikely(__get_user(state, sd->selector))) { 52 force_exit_sig(SIGSEGV); 53 return true; 54 } 55 56 if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW)) 57 return false; 58 59 if (state != SYSCALL_DISPATCH_FILTER_BLOCK) { 60 force_exit_sig(SIGSYS); 61 return true; 62 } 63 } 64 65 sd->on_dispatch = true; 66 syscall_rollback(current, regs); 67 trigger_sigsys(regs); 68 69 return true; 70 } 71 72 static int task_set_syscall_user_dispatch(struct task_struct *task, unsigned long mode, 73 unsigned long offset, unsigned long len, 74 char __user *selector) 75 { 76 switch (mode) { 77 case PR_SYS_DISPATCH_OFF: 78 if (offset || len || selector) 79 return -EINVAL; 80 break; 81 case PR_SYS_DISPATCH_ON: 82 /* 83 * Validate the direct dispatcher region just for basic 84 * sanity against overflow and a 0-sized dispatcher 85 * region. If the user is able to submit a syscall from 86 * an address, that address is obviously valid. 87 */ 88 if (offset && offset + len <= offset) 89 return -EINVAL; 90 91 /* 92 * access_ok() will clear memory tags for tagged addresses 93 * if current has memory tagging enabled. 94 95 * To enable a tracer to set a tracees selector the 96 * selector address must be untagged for access_ok(), 97 * otherwise an untagged tracer will always fail to set a 98 * tagged tracees selector. 99 */ 100 if (selector && !access_ok(untagged_addr(selector), sizeof(*selector))) 101 return -EFAULT; 102 103 break; 104 default: 105 return -EINVAL; 106 } 107 108 task->syscall_dispatch.selector = selector; 109 task->syscall_dispatch.offset = offset; 110 task->syscall_dispatch.len = len; 111 task->syscall_dispatch.on_dispatch = false; 112 113 if (mode == PR_SYS_DISPATCH_ON) 114 set_task_syscall_work(task, SYSCALL_USER_DISPATCH); 115 else 116 clear_task_syscall_work(task, SYSCALL_USER_DISPATCH); 117 118 return 0; 119 } 120 121 int set_syscall_user_dispatch(unsigned long mode, unsigned long offset, 122 unsigned long len, char __user *selector) 123 { 124 return task_set_syscall_user_dispatch(current, mode, offset, len, selector); 125 } 126 127 int syscall_user_dispatch_get_config(struct task_struct *task, unsigned long size, 128 void __user *data) 129 { 130 struct syscall_user_dispatch *sd = &task->syscall_dispatch; 131 struct ptrace_sud_config cfg; 132 133 if (size != sizeof(cfg)) 134 return -EINVAL; 135 136 if (test_task_syscall_work(task, SYSCALL_USER_DISPATCH)) 137 cfg.mode = PR_SYS_DISPATCH_ON; 138 else 139 cfg.mode = PR_SYS_DISPATCH_OFF; 140 141 cfg.offset = sd->offset; 142 cfg.len = sd->len; 143 cfg.selector = (__u64)(uintptr_t)sd->selector; 144 145 if (copy_to_user(data, &cfg, sizeof(cfg))) 146 return -EFAULT; 147 148 return 0; 149 } 150 151 int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long size, 152 void __user *data) 153 { 154 struct ptrace_sud_config cfg; 155 156 if (size != sizeof(cfg)) 157 return -EINVAL; 158 159 if (copy_from_user(&cfg, data, sizeof(cfg))) 160 return -EFAULT; 161 162 return task_set_syscall_user_dispatch(task, cfg.mode, cfg.offset, cfg.len, 163 (char __user *)(uintptr_t)cfg.selector); 164 } 165