Commit | Line | Data |
---|---|---|
1446e1df GKB |
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> | |
3f67987c | 7 | #include <linux/ptrace.h> |
1446e1df GKB |
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 | */ | |
941edc5b | 51 | if (unlikely(__get_user(state, sd->selector))) { |
fcb116bc | 52 | force_exit_sig(SIGSEGV); |
941edc5b EB |
53 | return true; |
54 | } | |
1446e1df | 55 | |
36a6c843 | 56 | if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW)) |
1446e1df GKB |
57 | return false; |
58 | ||
941edc5b | 59 | if (state != SYSCALL_DISPATCH_FILTER_BLOCK) { |
fcb116bc | 60 | force_exit_sig(SIGSYS); |
941edc5b EB |
61 | return true; |
62 | } | |
1446e1df GKB |
63 | } |
64 | ||
65 | sd->on_dispatch = true; | |
66 | syscall_rollback(current, regs); | |
67 | trigger_sigsys(regs); | |
68 | ||
69 | return true; | |
70 | } | |
71 | ||
43360686 GP |
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) | |
1446e1df GKB |
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 | ||
463b7715 GP |
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))) | |
1446e1df GKB |
101 | return -EFAULT; |
102 | ||
103 | break; | |
104 | default: | |
105 | return -EINVAL; | |
106 | } | |
107 | ||
43360686 GP |
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; | |
1446e1df GKB |
112 | |
113 | if (mode == PR_SYS_DISPATCH_ON) | |
43360686 | 114 | set_task_syscall_work(task, SYSCALL_USER_DISPATCH); |
1446e1df | 115 | else |
43360686 | 116 | clear_task_syscall_work(task, SYSCALL_USER_DISPATCH); |
1446e1df GKB |
117 | |
118 | return 0; | |
119 | } | |
43360686 GP |
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 | } | |
3f67987c GP |
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 | } |