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