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 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 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 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 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 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 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 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 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 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 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