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: &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(to: data, from: &cfg, n: 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(to: &cfg, from: data, n: sizeof(cfg))) |
160 | return -EFAULT; |
161 | |
162 | return task_set_syscall_user_dispatch(task, mode: cfg.mode, offset: cfg.offset, len: cfg.len, |
163 | selector: (char __user *)(uintptr_t)cfg.selector); |
164 | } |
165 | |