projects/linux·blob uaccess-cleanups · refs

194 lines · 4.9 KiB

1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2020 Collabora Ltd.
4 */
5#include <linux/elf.h>
6#include <linux/entry-common.h>
7#include <linux/prctl.h>
8#include <linux/ptrace.h>
9#include <linux/sched.h>
10#include <linux/sched/signal.h>
11#include <linux/sched/task_stack.h>
12#include <linux/signal.h>
13#include <linux/syscall_user_dispatch.h>
14#include <linux/sysctl.h>
15#include <linux/uaccess.h>
16
17#include <asm/syscall.h>
18
19static bool syscall_user_dispatch_allowed __read_mostly = true;
20
21static void trigger_sigsys(struct pt_regs *regs)
22{
23 struct kernel_siginfo info;
24
25 clear_siginfo(&info);
26 info.si_signo = SIGSYS;
27 info.si_code = SYS_USER_DISPATCH;
28 info.si_call_addr = (void __user *)KSTK_EIP(current);
29 info.si_errno = 0;
30 info.si_arch = syscall_get_arch(current);
31 info.si_syscall = syscall_get_nr(current, regs);
32
33 force_sig_info(&info);
34}
35
36bool syscall_user_dispatch(struct pt_regs *regs)
37{
38 struct syscall_user_dispatch *sd = &current->syscall_dispatch;
39 char state;
40
41 if (likely(instruction_pointer(regs) - sd->offset < sd->len))
42 return false;
43
44 if (unlikely(arch_syscall_is_vdso_sigreturn(regs)))
45 return false;
46
47 if (likely(sd->selector)) {
48 if (unlikely(get_user(state, untagged_addr(sd->selector)))) {
49 force_exit_sig(SIGSEGV);
50 return true;
51 }
52
53 if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW))
54 return false;
55
56 if (state != SYSCALL_DISPATCH_FILTER_BLOCK) {
57 force_exit_sig(SIGSYS);
58 return true;
59 }
60 }
61
62 sd->on_dispatch = true;
63 syscall_rollback(current, regs);
64 trigger_sigsys(regs);
65
66 return true;
67}
68
69static int task_set_syscall_user_dispatch(struct task_struct *task, unsigned long mode,
70 unsigned long offset, unsigned long len,
71 char __user *selector)
72{
73 switch (mode) {
74 case PR_SYS_DISPATCH_OFF:
75 if (offset || len || selector)
76 return -EINVAL;
77 break;
78 case PR_SYS_DISPATCH_EXCLUSIVE_ON:
79 /*
80 * Validate the direct dispatcher region just for basic
81 * sanity against overflow and a 0-sized dispatcher
82 * region. If the user is able to submit a syscall from
83 * an address, that address is obviously valid.
84 */
85 if (offset && offset + len <= offset)
86 return -EINVAL;
87 break;
88 case PR_SYS_DISPATCH_INCLUSIVE_ON:
89 if (len == 0 || offset + len <= offset)
90 return -EINVAL;
91 /*
92 * Invert the range, the check in syscall_user_dispatch()
93 * supports wrap-around.
94 */
95 offset = offset + len;
96 len = -len;
97 break;
98 default:
99 return -EINVAL;
100 }
101
102 /* Arming can be denied at runtime via sysctl, disarming is allowed */
103 if (mode != PR_SYS_DISPATCH_OFF && !syscall_user_dispatch_allowed)
104 return -EPERM;
105
106 /*
107 * access_ok() will clear memory tags for tagged addresses
108 * if current has memory tagging enabled.
109 *
110 * To enable a tracer to set a tracees selector the
111 * selector address must be untagged for access_ok(),
112 * otherwise an untagged tracer will always fail to set a
113 * tagged tracees selector.
114 */
115 if (mode != PR_SYS_DISPATCH_OFF && selector &&
116 !access_ok(untagged_addr(selector), sizeof(*selector)))
117 return -EFAULT;
118
119 task->syscall_dispatch.selector = selector;
120 task->syscall_dispatch.offset = offset;
121 task->syscall_dispatch.len = len;
122 task->syscall_dispatch.on_dispatch = false;
123
124 if (mode != PR_SYS_DISPATCH_OFF)
125 set_task_syscall_work(task, SYSCALL_USER_DISPATCH);
126 else
127 clear_task_syscall_work(task, SYSCALL_USER_DISPATCH);
128
129 return 0;
130}
131
132int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
133 unsigned long len, char __user *selector)
134{
135 return task_set_syscall_user_dispatch(current, mode, offset, len, selector);
136}
137
138int syscall_user_dispatch_get_config(struct task_struct *task, unsigned long size,
139 void __user *data)
140{
141 struct syscall_user_dispatch *sd = &task->syscall_dispatch;
142 struct ptrace_sud_config cfg;
143
144 if (size != sizeof(cfg))
145 return -EINVAL;
146
147 if (test_task_syscall_work(task, SYSCALL_USER_DISPATCH))
148 cfg.mode = PR_SYS_DISPATCH_ON;
149 else
150 cfg.mode = PR_SYS_DISPATCH_OFF;
151
152 cfg.offset = sd->offset;
153 cfg.len = sd->len;
154 cfg.selector = (__u64)(uintptr_t)sd->selector;
155
156 if (copy_to_user(data, &cfg, sizeof(cfg)))
157 return -EFAULT;
158
159 return 0;
160}
161
162int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long size,
163 void __user *data)
164{
165 struct ptrace_sud_config cfg;
166
167 if (size != sizeof(cfg))
168 return -EINVAL;
169
170 if (copy_from_user(&cfg, data, sizeof(cfg)))
171 return -EFAULT;
172
173 return task_set_syscall_user_dispatch(task, cfg.mode, cfg.offset, cfg.len,
174 (char __user *)(uintptr_t)cfg.selector);
175}
176
177#ifdef CONFIG_SYSCTL
178static const struct ctl_table syscall_user_dispatch_sysctls[] = {
179 {
180 .procname = "syscall_user_dispatch",
181 .data = &syscall_user_dispatch_allowed,
182 .maxlen = sizeof(syscall_user_dispatch_allowed),
183 .mode = 0644,
184 .proc_handler = proc_dobool,
185 },
186};
187
188static int __init syscall_user_dispatch_sysctl_init(void)
189{
190 register_sysctl_init("kernel", syscall_user_dispatch_sysctls);
191 return 0;
192}
193late_initcall(syscall_user_dispatch_sysctl_init);
194#endif /* CONFIG_SYSCTL */