Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

um: Implement probe_kernel_read()

UML needs it's own probe_kernel_read() to handle kernel
mode faults correctly.
The implementation uses mincore() on the host side to detect
whether a page is owned by the UML kernel process.

This fixes also a possible crash when sysrq-t is used.
Starting with 3.10 sysrq-t calls probe_kernel_read() to
read details from the kernel workers. As kernel worker are
completely async pointers may turn NULL while reading them.

Cc: <stian@nixia.no>
Cc: <tj@kernel.org>
Cc: <stable@vger.kernel.org> # 3.10.x
Signed-off-by: Richard Weinberger <richard@nod.at>

+78 -1
+1
arch/um/include/shared/os.h
··· 200 200 extern int os_drop_memory(void *addr, int length); 201 201 extern int can_drop_memory(void); 202 202 extern void os_flush_stdout(void); 203 + extern int os_mincore(void *addr, unsigned long len); 203 204 204 205 /* execvp.c */ 205 206 extern int execvp_noalloc(char *buf, const char *file, char *const argv[]);
+1 -1
arch/um/kernel/Makefile
··· 13 13 obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \ 14 14 physmem.o process.o ptrace.o reboot.o sigio.o \ 15 15 signal.o smp.o syscall.o sysrq.o time.o tlb.o trap.o \ 16 - um_arch.o umid.o skas/ 16 + um_arch.o umid.o maccess.o skas/ 17 17 18 18 obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o 19 19 obj-$(CONFIG_GPROF) += gprof_syms.o
+24
arch/um/kernel/maccess.c
··· 1 + /* 2 + * Copyright (C) 2013 Richard Weinberger <richrd@nod.at> 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 as 6 + * published by the Free Software Foundation. 7 + */ 8 + 9 + #include <linux/uaccess.h> 10 + #include <linux/kernel.h> 11 + #include <os.h> 12 + 13 + long probe_kernel_read(void *dst, const void *src, size_t size) 14 + { 15 + void *psrc = (void *)rounddown((unsigned long)src, PAGE_SIZE); 16 + 17 + if ((unsigned long)src < PAGE_SIZE || size <= 0) 18 + return -EFAULT; 19 + 20 + if (os_mincore(psrc, size + src - psrc) <= 0) 21 + return -EFAULT; 22 + 23 + return __probe_kernel_read(dst, src, size); 24 + }
+52
arch/um/os-Linux/process.c
··· 4 4 */ 5 5 6 6 #include <stdio.h> 7 + #include <stdlib.h> 7 8 #include <unistd.h> 8 9 #include <errno.h> 9 10 #include <signal.h> ··· 231 230 close(fd); 232 231 out: 233 232 return ok; 233 + } 234 + 235 + static int os_page_mincore(void *addr) 236 + { 237 + char vec[2]; 238 + int ret; 239 + 240 + ret = mincore(addr, UM_KERN_PAGE_SIZE, vec); 241 + if (ret < 0) { 242 + if (errno == ENOMEM || errno == EINVAL) 243 + return 0; 244 + else 245 + return -errno; 246 + } 247 + 248 + return vec[0] & 1; 249 + } 250 + 251 + int os_mincore(void *addr, unsigned long len) 252 + { 253 + char *vec; 254 + int ret, i; 255 + 256 + if (len <= UM_KERN_PAGE_SIZE) 257 + return os_page_mincore(addr); 258 + 259 + vec = calloc(1, (len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); 260 + if (!vec) 261 + return -ENOMEM; 262 + 263 + ret = mincore(addr, UM_KERN_PAGE_SIZE, vec); 264 + if (ret < 0) { 265 + if (errno == ENOMEM || errno == EINVAL) 266 + ret = 0; 267 + else 268 + ret = -errno; 269 + 270 + goto out; 271 + } 272 + 273 + for (i = 0; i < ((len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); i++) { 274 + if (!(vec[i] & 1)) { 275 + ret = 0; 276 + goto out; 277 + } 278 + } 279 + 280 + ret = 1; 281 + out: 282 + free(vec); 283 + return ret; 234 284 } 235 285 236 286 void init_new_thread_signals(void)