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 static inline unsigned long *check_init_stack(struct mm_id * mm_idp, 26 unsigned long *stack) 27 { 28 if (stack == NULL) { 29 stack = (unsigned long *) mm_idp->stack + 2; 30 *stack = 0; 31 } 32 return stack; 33 } 34 35 static unsigned long syscall_regs[MAX_REG_NR]; 36 37 static int __init init_syscall_regs(void) 38 { 39 get_safe_registers(syscall_regs, NULL); 40 41 syscall_regs[REGS_IP_INDEX] = STUB_CODE + 42 ((unsigned long) stub_syscall_handler - 43 (unsigned long) __syscall_stub_start); 44 syscall_regs[REGS_SP_INDEX] = STUB_DATA + 45 offsetof(struct stub_data, sigstack) + 46 sizeof(((struct stub_data *) 0)->sigstack) - 47 sizeof(void *); 48 49 return 0; 50 } 51 52 __initcall(init_syscall_regs); 53 54 static inline long do_syscall_stub(struct mm_id *mm_idp) 55 { 56 struct stub_data *proc_data = (void *)mm_idp->stack; 57 int n, i; 58 int err, pid = mm_idp->u.pid; 59 60 n = ptrace_setregs(pid, syscall_regs); 61 if (n < 0) { 62 printk(UM_KERN_ERR "Registers - \n"); 63 for (i = 0; i < MAX_REG_NR; i++) 64 printk(UM_KERN_ERR "\t%d\t0x%lx\n", i, syscall_regs[i]); 65 panic("%s : PTRACE_SETREGS failed, errno = %d\n", 66 __func__, -n); 67 } 68 69 /* Inform process how much we have filled in. */ 70 proc_data->syscall_data_len = mm_idp->syscall_data_len; 71 72 err = ptrace(PTRACE_CONT, pid, 0, 0); 73 if (err) 74 panic("Failed to continue stub, pid = %d, errno = %d\n", pid, 75 errno); 76 77 wait_stub_done(pid); 78 79 /* 80 * proc_data->err will be non-zero if there was an (unexpected) error. 81 * In that case, syscall_data_len points to the last executed syscall, 82 * otherwise it will be zero (but we do not need to rely on that). 83 */ 84 if (proc_data->err < 0) { 85 struct stub_syscall *sc; 86 87 if (proc_data->syscall_data_len < 0 || 88 proc_data->syscall_data_len >= ARRAY_SIZE(proc_data->syscall_data)) 89 panic("Syscall data was corrupted by stub (len is: %d, expected maximum: %d)!", 90 proc_data->syscall_data_len, 91 mm_idp->syscall_data_len); 92 93 sc = &proc_data->syscall_data[proc_data->syscall_data_len]; 94 95 printk(UM_KERN_ERR "%s : length = %d, last offset = %d", 96 __func__, mm_idp->syscall_data_len, 97 proc_data->syscall_data_len); 98 printk(UM_KERN_ERR "%s : stub syscall type %d failed, return value = 0x%lx\n", 99 __func__, sc->syscall, proc_data->err); 100 101 print_hex_dump(UM_KERN_ERR, 102 " syscall data: ", 0, 103 16, 4, sc, sizeof(*sc), 0); 104 105 mm_idp->syscall_data_len = proc_data->err; 106 } else { 107 mm_idp->syscall_data_len = 0; 108 } 109 110 return mm_idp->syscall_data_len; 111 } 112 113 int syscall_stub_flush(struct mm_id *mm_idp) 114 { 115 int res; 116 117 if (mm_idp->syscall_data_len == 0) 118 return 0; 119 120 /* If an error happened already, report it and reset the state. */ 121 if (mm_idp->syscall_data_len < 0) { 122 res = mm_idp->syscall_data_len; 123 mm_idp->syscall_data_len = 0; 124 return res; 125 } 126 127 res = do_syscall_stub(mm_idp); 128 mm_idp->syscall_data_len = 0; 129 130 return res; 131 } 132 133 struct stub_syscall *syscall_stub_alloc(struct mm_id *mm_idp) 134 { 135 struct stub_syscall *sc; 136 struct stub_data *proc_data = (struct stub_data *) mm_idp->stack; 137 138 if (mm_idp->syscall_data_len > 0 && 139 mm_idp->syscall_data_len == ARRAY_SIZE(proc_data->syscall_data)) 140 do_syscall_stub(mm_idp); 141 142 if (mm_idp->syscall_data_len < 0) { 143 /* Return dummy to retain error state. */ 144 sc = &proc_data->syscall_data[0]; 145 } else { 146 sc = &proc_data->syscall_data[mm_idp->syscall_data_len]; 147 mm_idp->syscall_data_len += 1; 148 } 149 memset(sc, 0, sizeof(*sc)); 150 151 return sc; 152 } 153 154 static struct stub_syscall *syscall_stub_get_previous(struct mm_id *mm_idp, 155 int syscall_type, 156 unsigned long virt) 157 { 158 if (mm_idp->syscall_data_len > 0) { 159 struct stub_data *proc_data = (void *) mm_idp->stack; 160 struct stub_syscall *sc; 161 162 sc = &proc_data->syscall_data[mm_idp->syscall_data_len - 1]; 163 164 if (sc->syscall == syscall_type && 165 sc->mem.addr + sc->mem.length == virt) 166 return sc; 167 } 168 169 return NULL; 170 } 171 172 void map(struct mm_id *mm_idp, unsigned long virt, unsigned long len, int prot, 173 int phys_fd, unsigned long long offset) 174 { 175 struct stub_syscall *sc; 176 177 /* Compress with previous syscall if that is possible */ 178 sc = syscall_stub_get_previous(mm_idp, STUB_SYSCALL_MMAP, virt); 179 if (sc && sc->mem.prot == prot && sc->mem.fd == phys_fd && 180 sc->mem.offset == MMAP_OFFSET(offset - sc->mem.length)) { 181 sc->mem.length += len; 182 return; 183 } 184 185 sc = syscall_stub_alloc(mm_idp); 186 sc->syscall = STUB_SYSCALL_MMAP; 187 sc->mem.addr = virt; 188 sc->mem.length = len; 189 sc->mem.prot = prot; 190 sc->mem.fd = phys_fd; 191 sc->mem.offset = MMAP_OFFSET(offset); 192 } 193 194 void unmap(struct mm_id *mm_idp, unsigned long addr, unsigned long len) 195 { 196 struct stub_syscall *sc; 197 198 /* Compress with previous syscall if that is possible */ 199 sc = syscall_stub_get_previous(mm_idp, STUB_SYSCALL_MUNMAP, addr); 200 if (sc) { 201 sc->mem.length += len; 202 return; 203 } 204 205 sc = syscall_stub_alloc(mm_idp); 206 sc->syscall = STUB_SYSCALL_MUNMAP; 207 sc->mem.addr = addr; 208 sc->mem.length = len; 209 } 210 211 void protect(struct mm_id *mm_idp, unsigned long addr, unsigned long len, 212 unsigned int prot) 213 { 214 struct stub_syscall *sc; 215 216 /* Compress with previous syscall if that is possible */ 217 sc = syscall_stub_get_previous(mm_idp, STUB_SYSCALL_MPROTECT, addr); 218 if (sc && sc->mem.prot == prot) { 219 sc->mem.length += len; 220 return; 221 } 222 223 sc = syscall_stub_alloc(mm_idp); 224 sc->syscall = STUB_SYSCALL_MPROTECT; 225 sc->mem.addr = addr; 226 sc->mem.length = len; 227 sc->mem.prot = prot; 228 } 229