aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um
diff options
context:
space:
mode:
authorRichard Weinberger <richard@nod.at>2013-08-17 12:46:00 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-09-26 20:18:29 -0400
commit4fdaa3d47985338f80d7262de700533a9e630d29 (patch)
treee0b5561831e3336af764f5c5d1472e405a8ab470 /arch/um
parent579db19eca10b594acd9d4516814b599eb4486ec (diff)
um: Implement probe_kernel_read()
commit f75b1b1bedfb498cc43a992ce4d7ed8df3b1e770 upstream. 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. Signed-off-by: Richard Weinberger <richard@nod.at> Cc: <stian@nixia.no> Cc: <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'arch/um')
-rw-r--r--arch/um/include/shared/os.h1
-rw-r--r--arch/um/kernel/Makefile2
-rw-r--r--arch/um/kernel/maccess.c24
-rw-r--r--arch/um/os-Linux/process.c52
4 files changed, 78 insertions, 1 deletions
diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h
index 95feaa47a2fb..c70a234a3f8c 100644
--- a/arch/um/include/shared/os.h
+++ b/arch/um/include/shared/os.h
@@ -200,6 +200,7 @@ extern int os_unmap_memory(void *addr, int len);
200extern int os_drop_memory(void *addr, int length); 200extern int os_drop_memory(void *addr, int length);
201extern int can_drop_memory(void); 201extern int can_drop_memory(void);
202extern void os_flush_stdout(void); 202extern void os_flush_stdout(void);
203extern int os_mincore(void *addr, unsigned long len);
203 204
204/* execvp.c */ 205/* execvp.c */
205extern int execvp_noalloc(char *buf, const char *file, char *const argv[]); 206extern int execvp_noalloc(char *buf, const char *file, char *const argv[]);
diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile
index babe21826e3e..d8b78a03855c 100644
--- a/arch/um/kernel/Makefile
+++ b/arch/um/kernel/Makefile
@@ -13,7 +13,7 @@ clean-files :=
13obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \ 13obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \
14 physmem.o process.o ptrace.o reboot.o sigio.o \ 14 physmem.o process.o ptrace.o reboot.o sigio.o \
15 signal.o smp.o syscall.o sysrq.o time.o tlb.o trap.o \ 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
18obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o 18obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o
19obj-$(CONFIG_GPROF) += gprof_syms.o 19obj-$(CONFIG_GPROF) += gprof_syms.o
diff --git a/arch/um/kernel/maccess.c b/arch/um/kernel/maccess.c
new file mode 100644
index 000000000000..1f3d5c4910d1
--- /dev/null
+++ b/arch/um/kernel/maccess.c
@@ -0,0 +1,24 @@
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
13long 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}
diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c
index b8f34c9e53ae..67b9c8f5a89e 100644
--- a/arch/um/os-Linux/process.c
+++ b/arch/um/os-Linux/process.c
@@ -4,6 +4,7 @@
4 */ 4 */
5 5
6#include <stdio.h> 6#include <stdio.h>
7#include <stdlib.h>
7#include <unistd.h> 8#include <unistd.h>
8#include <errno.h> 9#include <errno.h>
9#include <signal.h> 10#include <signal.h>
@@ -232,6 +233,57 @@ out:
232 return ok; 233 return ok;
233} 234}
234 235
236static int os_page_mincore(void *addr)
237{
238 char vec[2];
239 int ret;
240
241 ret = mincore(addr, UM_KERN_PAGE_SIZE, vec);
242 if (ret < 0) {
243 if (errno == ENOMEM || errno == EINVAL)
244 return 0;
245 else
246 return -errno;
247 }
248
249 return vec[0] & 1;
250}
251
252int os_mincore(void *addr, unsigned long len)
253{
254 char *vec;
255 int ret, i;
256
257 if (len <= UM_KERN_PAGE_SIZE)
258 return os_page_mincore(addr);
259
260 vec = calloc(1, (len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE);
261 if (!vec)
262 return -ENOMEM;
263
264 ret = mincore(addr, UM_KERN_PAGE_SIZE, vec);
265 if (ret < 0) {
266 if (errno == ENOMEM || errno == EINVAL)
267 ret = 0;
268 else
269 ret = -errno;
270
271 goto out;
272 }
273
274 for (i = 0; i < ((len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); i++) {
275 if (!(vec[i] & 1)) {
276 ret = 0;
277 goto out;
278 }
279 }
280
281 ret = 1;
282out:
283 free(vec);
284 return ret;
285}
286
235void init_new_thread_signals(void) 287void init_new_thread_signals(void)
236{ 288{
237 set_handler(SIGSEGV); 289 set_handler(SIGSEGV);