diff options
author | Richard Weinberger <richard@nod.at> | 2013-08-17 12:46:00 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-09-26 20:18:29 -0400 |
commit | 4fdaa3d47985338f80d7262de700533a9e630d29 (patch) | |
tree | e0b5561831e3336af764f5c5d1472e405a8ab470 /arch/um | |
parent | 579db19eca10b594acd9d4516814b599eb4486ec (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.h | 1 | ||||
-rw-r--r-- | arch/um/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/um/kernel/maccess.c | 24 | ||||
-rw-r--r-- | arch/um/os-Linux/process.c | 52 |
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); | |||
200 | extern int os_drop_memory(void *addr, int length); | 200 | extern int os_drop_memory(void *addr, int length); |
201 | extern int can_drop_memory(void); | 201 | extern int can_drop_memory(void); |
202 | extern void os_flush_stdout(void); | 202 | extern void os_flush_stdout(void); |
203 | extern int os_mincore(void *addr, unsigned long len); | ||
203 | 204 | ||
204 | /* execvp.c */ | 205 | /* execvp.c */ |
205 | extern int execvp_noalloc(char *buf, const char *file, char *const argv[]); | 206 | extern 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 := | |||
13 | obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \ | 13 | obj-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 | ||
18 | obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o | 18 | obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o |
19 | obj-$(CONFIG_GPROF) += gprof_syms.o | 19 | obj-$(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 | |||
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 | } | ||
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 | ||
236 | static 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 | |||
252 | int 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; | ||
282 | out: | ||
283 | free(vec); | ||
284 | return ret; | ||
285 | } | ||
286 | |||
235 | void init_new_thread_signals(void) | 287 | void init_new_thread_signals(void) |
236 | { | 288 | { |
237 | set_handler(SIGSEGV); | 289 | set_handler(SIGSEGV); |