1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2021 Benjamin Berg <benjamin@sipsolutions.net>
4 * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
5 */
6
7 #include <stddef.h>
8 #include <unistd.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <sys/mman.h>
12 #include <init.h>
13 #include <as-layout.h>
14 #include <mm_id.h>
15 #include <os.h>
16 #include <ptrace_user.h>
17 #include <registers.h>
18 #include <skas.h>
19 #include <sysdep/ptrace.h>
20 #include <sysdep/stub.h>
21 #include "../internal.h"
22
23 extern char __syscall_stub_start[];
24
syscall_stub_dump_error(struct mm_id * mm_idp)25 void syscall_stub_dump_error(struct mm_id *mm_idp)
26 {
27 struct stub_data *proc_data = (void *)mm_idp->stack;
28 struct stub_syscall *sc;
29
30 if (proc_data->syscall_data_len < 0 ||
31 proc_data->syscall_data_len >= ARRAY_SIZE(proc_data->syscall_data))
32 panic("Syscall data was corrupted by stub (len is: %d, expected maximum: %d)!",
33 proc_data->syscall_data_len,
34 mm_idp->syscall_data_len);
35
36 sc = &proc_data->syscall_data[proc_data->syscall_data_len];
37
38 printk(UM_KERN_ERR "%s : length = %d, last offset = %d",
39 __func__, mm_idp->syscall_data_len,
40 proc_data->syscall_data_len);
41 printk(UM_KERN_ERR "%s : stub syscall type %d failed, return value = 0x%lx\n",
42 __func__, sc->syscall, proc_data->err);
43
44 print_hex_dump(UM_KERN_ERR, " syscall data: ", 0,
45 16, 4, sc, sizeof(*sc), 0);
46
47 if (using_seccomp) {
48 printk(UM_KERN_ERR "%s: FD map num: %d", __func__,
49 mm_idp->syscall_fd_num);
50 print_hex_dump(UM_KERN_ERR,
51 " FD map: ", 0, 16,
52 sizeof(mm_idp->syscall_fd_map[0]),
53 mm_idp->syscall_fd_map,
54 sizeof(mm_idp->syscall_fd_map), 0);
55 }
56 }
57
check_init_stack(struct mm_id * mm_idp,unsigned long * stack)58 static inline unsigned long *check_init_stack(struct mm_id * mm_idp,
59 unsigned long *stack)
60 {
61 if (stack == NULL) {
62 stack = (unsigned long *) mm_idp->stack + 2;
63 *stack = 0;
64 }
65 return stack;
66 }
67
68 static unsigned long syscall_regs[MAX_REG_NR];
69
init_syscall_regs(void)70 static int __init init_syscall_regs(void)
71 {
72 get_safe_registers(syscall_regs, NULL);
73
74 syscall_regs[REGS_IP_INDEX] = STUB_CODE +
75 ((unsigned long) stub_syscall_handler -
76 (unsigned long) __syscall_stub_start);
77 syscall_regs[REGS_SP_INDEX] = STUB_DATA +
78 offsetof(struct stub_data, sigstack) +
79 sizeof(((struct stub_data *) 0)->sigstack) -
80 sizeof(void *);
81
82 return 0;
83 }
84
85 __initcall(init_syscall_regs);
86
do_syscall_stub(struct mm_id * mm_idp)87 static inline long do_syscall_stub(struct mm_id *mm_idp)
88 {
89 struct stub_data *proc_data = (void *)mm_idp->stack;
90 int n, i;
91 int err, pid = mm_idp->pid;
92
93 /* Inform process how much we have filled in. */
94 proc_data->syscall_data_len = mm_idp->syscall_data_len;
95
96 if (using_seccomp) {
97 proc_data->restart_wait = 1;
98 wait_stub_done_seccomp(mm_idp, 0, 1);
99 } else {
100 n = ptrace_setregs(pid, syscall_regs);
101 if (n < 0) {
102 printk(UM_KERN_ERR "Registers -\n");
103 for (i = 0; i < MAX_REG_NR; i++)
104 printk(UM_KERN_ERR "\t%d\t0x%lx\n", i, syscall_regs[i]);
105 panic("%s : PTRACE_SETREGS failed, errno = %d\n",
106 __func__, -n);
107 }
108
109 err = ptrace(PTRACE_CONT, pid, 0, 0);
110 if (err)
111 panic("Failed to continue stub, pid = %d, errno = %d\n",
112 pid, errno);
113
114 wait_stub_done(pid);
115 }
116
117 /*
118 * proc_data->err will be negative if there was an (unexpected) error.
119 * In that case, syscall_data_len points to the last executed syscall,
120 * otherwise it will be zero (but we do not need to rely on that).
121 */
122 if (proc_data->err < 0) {
123 syscall_stub_dump_error(mm_idp);
124
125 /* Store error code in case someone tries to add more syscalls */
126 mm_idp->syscall_data_len = proc_data->err;
127 } else {
128 mm_idp->syscall_data_len = 0;
129 }
130
131 if (using_seccomp)
132 mm_idp->syscall_fd_num = 0;
133
134 return mm_idp->syscall_data_len;
135 }
136
syscall_stub_flush(struct mm_id * mm_idp)137 int syscall_stub_flush(struct mm_id *mm_idp)
138 {
139 int res;
140
141 if (mm_idp->syscall_data_len == 0)
142 return 0;
143
144 /* If an error happened already, report it and reset the state. */
145 if (mm_idp->syscall_data_len < 0) {
146 res = mm_idp->syscall_data_len;
147 mm_idp->syscall_data_len = 0;
148 return res;
149 }
150
151 res = do_syscall_stub(mm_idp);
152 mm_idp->syscall_data_len = 0;
153
154 return res;
155 }
156
syscall_stub_alloc(struct mm_id * mm_idp)157 struct stub_syscall *syscall_stub_alloc(struct mm_id *mm_idp)
158 {
159 struct stub_syscall *sc;
160 struct stub_data *proc_data = (struct stub_data *) mm_idp->stack;
161
162 if (mm_idp->syscall_data_len > 0 &&
163 mm_idp->syscall_data_len == ARRAY_SIZE(proc_data->syscall_data))
164 do_syscall_stub(mm_idp);
165
166 if (mm_idp->syscall_data_len < 0) {
167 /* Return dummy to retain error state. */
168 sc = &proc_data->syscall_data[0];
169 } else {
170 sc = &proc_data->syscall_data[mm_idp->syscall_data_len];
171 mm_idp->syscall_data_len += 1;
172 }
173 memset(sc, 0, sizeof(*sc));
174
175 return sc;
176 }
177
syscall_stub_get_previous(struct mm_id * mm_idp,int syscall_type,unsigned long virt)178 static struct stub_syscall *syscall_stub_get_previous(struct mm_id *mm_idp,
179 int syscall_type,
180 unsigned long virt)
181 {
182 if (mm_idp->syscall_data_len > 0) {
183 struct stub_data *proc_data = (void *) mm_idp->stack;
184 struct stub_syscall *sc;
185
186 sc = &proc_data->syscall_data[mm_idp->syscall_data_len - 1];
187
188 if (sc->syscall == syscall_type &&
189 sc->mem.addr + sc->mem.length == virt)
190 return sc;
191 }
192
193 return NULL;
194 }
195
get_stub_fd(struct mm_id * mm_idp,int fd)196 static int get_stub_fd(struct mm_id *mm_idp, int fd)
197 {
198 int i;
199
200 /* Find an FD slot (or flush and use first) */
201 if (!using_seccomp)
202 return fd;
203
204 /* Already crashed, value does not matter */
205 if (mm_idp->syscall_data_len < 0)
206 return 0;
207
208 /* Find existing FD in map if we can allocate another syscall */
209 if (mm_idp->syscall_data_len <
210 ARRAY_SIZE(((struct stub_data *)NULL)->syscall_data)) {
211 for (i = 0; i < mm_idp->syscall_fd_num; i++) {
212 if (mm_idp->syscall_fd_map[i] == fd)
213 return i;
214 }
215
216 if (mm_idp->syscall_fd_num < STUB_MAX_FDS) {
217 i = mm_idp->syscall_fd_num;
218 mm_idp->syscall_fd_map[i] = fd;
219
220 mm_idp->syscall_fd_num++;
221
222 return i;
223 }
224 }
225
226 /* FD map full or no syscall space available, continue after flush */
227 do_syscall_stub(mm_idp);
228 mm_idp->syscall_fd_map[0] = fd;
229 mm_idp->syscall_fd_num = 1;
230
231 return 0;
232 }
233
map(struct mm_id * mm_idp,unsigned long virt,unsigned long len,int prot,int phys_fd,unsigned long long offset)234 int map(struct mm_id *mm_idp, unsigned long virt, unsigned long len, int prot,
235 int phys_fd, unsigned long long offset)
236 {
237 struct stub_syscall *sc;
238
239 /* Compress with previous syscall if that is possible */
240 sc = syscall_stub_get_previous(mm_idp, STUB_SYSCALL_MMAP, virt);
241 if (sc && sc->mem.prot == prot &&
242 sc->mem.offset == MMAP_OFFSET(offset - sc->mem.length)) {
243 int prev_fd = sc->mem.fd;
244
245 if (using_seccomp)
246 prev_fd = mm_idp->syscall_fd_map[sc->mem.fd];
247
248 if (phys_fd == prev_fd) {
249 sc->mem.length += len;
250 return 0;
251 }
252 }
253
254 phys_fd = get_stub_fd(mm_idp, phys_fd);
255
256 sc = syscall_stub_alloc(mm_idp);
257 sc->syscall = STUB_SYSCALL_MMAP;
258 sc->mem.addr = virt;
259 sc->mem.length = len;
260 sc->mem.prot = prot;
261 sc->mem.fd = phys_fd;
262 sc->mem.offset = MMAP_OFFSET(offset);
263
264 return 0;
265 }
266
unmap(struct mm_id * mm_idp,unsigned long addr,unsigned long len)267 int unmap(struct mm_id *mm_idp, unsigned long addr, unsigned long len)
268 {
269 struct stub_syscall *sc;
270
271 /* Compress with previous syscall if that is possible */
272 sc = syscall_stub_get_previous(mm_idp, STUB_SYSCALL_MUNMAP, addr);
273 if (sc) {
274 sc->mem.length += len;
275 return 0;
276 }
277
278 sc = syscall_stub_alloc(mm_idp);
279 sc->syscall = STUB_SYSCALL_MUNMAP;
280 sc->mem.addr = addr;
281 sc->mem.length = len;
282
283 return 0;
284 }
285