diff options
author | Richard Weinberger <richard@nod.at> | 2013-08-17 12:46:00 -0400 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2013-09-07 04:38:34 -0400 |
commit | f75b1b1bedfb498cc43a992ce4d7ed8df3b1e770 (patch) | |
tree | e7afc9647b9cc80fb95ee9dae7d9532b67efdc01 /arch/um/os-Linux/process.c | |
parent | 65984ff9d2179a97e5a11aaef1e86fdb276cfad5 (diff) |
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>
Diffstat (limited to 'arch/um/os-Linux/process.c')
-rw-r--r-- | arch/um/os-Linux/process.c | 52 |
1 files changed, 52 insertions, 0 deletions
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); |