Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/kernel/entry/syscall_user_dispatch.c
29267 views
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 = &current->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_EXCLUSIVE_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
break;
91
case PR_SYS_DISPATCH_INCLUSIVE_ON:
92
if (len == 0 || offset + len <= offset)
93
return -EINVAL;
94
/*
95
* Invert the range, the check in syscall_user_dispatch()
96
* supports wrap-around.
97
*/
98
offset = offset + len;
99
len = -len;
100
break;
101
default:
102
return -EINVAL;
103
}
104
105
/*
106
* access_ok() will clear memory tags for tagged addresses
107
* if current has memory tagging enabled.
108
*
109
* To enable a tracer to set a tracees selector the
110
* selector address must be untagged for access_ok(),
111
* otherwise an untagged tracer will always fail to set a
112
* tagged tracees selector.
113
*/
114
if (mode != PR_SYS_DISPATCH_OFF && selector &&
115
!access_ok(untagged_addr(selector), sizeof(*selector)))
116
return -EFAULT;
117
118
task->syscall_dispatch.selector = selector;
119
task->syscall_dispatch.offset = offset;
120
task->syscall_dispatch.len = len;
121
task->syscall_dispatch.on_dispatch = false;
122
123
if (mode != PR_SYS_DISPATCH_OFF)
124
set_task_syscall_work(task, SYSCALL_USER_DISPATCH);
125
else
126
clear_task_syscall_work(task, SYSCALL_USER_DISPATCH);
127
128
return 0;
129
}
130
131
int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
132
unsigned long len, char __user *selector)
133
{
134
return task_set_syscall_user_dispatch(current, mode, offset, len, selector);
135
}
136
137
int syscall_user_dispatch_get_config(struct task_struct *task, unsigned long size,
138
void __user *data)
139
{
140
struct syscall_user_dispatch *sd = &task->syscall_dispatch;
141
struct ptrace_sud_config cfg;
142
143
if (size != sizeof(cfg))
144
return -EINVAL;
145
146
if (test_task_syscall_work(task, SYSCALL_USER_DISPATCH))
147
cfg.mode = PR_SYS_DISPATCH_ON;
148
else
149
cfg.mode = PR_SYS_DISPATCH_OFF;
150
151
cfg.offset = sd->offset;
152
cfg.len = sd->len;
153
cfg.selector = (__u64)(uintptr_t)sd->selector;
154
155
if (copy_to_user(data, &cfg, sizeof(cfg)))
156
return -EFAULT;
157
158
return 0;
159
}
160
161
int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long size,
162
void __user *data)
163
{
164
struct ptrace_sud_config cfg;
165
166
if (size != sizeof(cfg))
167
return -EINVAL;
168
169
if (copy_from_user(&cfg, data, sizeof(cfg)))
170
return -EFAULT;
171
172
return task_set_syscall_user_dispatch(task, cfg.mode, cfg.offset, cfg.len,
173
(char __user *)(uintptr_t)cfg.selector);
174
}
175
176