Commit 546fe1cb authored by Paolo 'Blaisorblade' Giarrusso's avatar Paolo 'Blaisorblade' Giarrusso Committed by Linus Torvalds

[PATCH] uml: fix hang in TT mode on fault

The current code doesn't handle well general protection faults on the host -
it thinks that cr2 is always the address of a page fault.  While actually, on
general protection faults, that address is not accessible, so we'd better
assume we couldn't satisfy the fault.  Currently instead we think we've fixed
it, so we go back, retry the instruction and fault again endlessly.

This leads to the kernel hanging when doing copy_from_user(dest, -1, ...) in
TT mode, since reading *(-1) causes a GFP, and we don't support kernel
preemption.

Thanks to Luo Xin for testing UML with LTP and reporting the failures he got.

Cc: Luo Xin <luothing@sina.com>
Signed-off-by: default avatarPaolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Cc: Jeff Dike <jdike@addtoit.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 69e1e688
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "asm/a.out.h" #include "asm/a.out.h"
#include "asm/current.h" #include "asm/current.h"
#include "asm/irq.h" #include "asm/irq.h"
#include "sysdep/sigcontext.h"
#include "user_util.h" #include "user_util.h"
#include "kern_util.h" #include "kern_util.h"
#include "kern.h" #include "kern.h"
...@@ -125,7 +126,15 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, void *sc) ...@@ -125,7 +126,15 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, void *sc)
} }
else if(current->mm == NULL) else if(current->mm == NULL)
panic("Segfault with no mm"); panic("Segfault with no mm");
if (SEGV_IS_FIXABLE(&fi))
err = handle_page_fault(address, ip, is_write, is_user, &si.si_code); err = handle_page_fault(address, ip, is_write, is_user, &si.si_code);
else {
err = -EFAULT;
/* A thread accessed NULL, we get a fault, but CR2 is invalid.
* This code is used in __do_copy_from_user() of TT mode. */
address = 0;
}
catcher = current->thread.fault_catcher; catcher = current->thread.fault_catcher;
if(!err) if(!err)
......
...@@ -22,8 +22,15 @@ int __do_copy_from_user(void *to, const void *from, int n, ...@@ -22,8 +22,15 @@ int __do_copy_from_user(void *to, const void *from, int n,
__do_copy, &faulted); __do_copy, &faulted);
TASK_REGS(get_current())->tt = save; TASK_REGS(get_current())->tt = save;
if(!faulted) return(0); if(!faulted)
else return(n - (fault - (unsigned long) from)); return 0;
else if (fault)
return n - (fault - (unsigned long) from);
else
/* In case of a general protection fault, we don't have the
* fault address, so NULL is used instead. Pretend we didn't
* copy anything. */
return n;
} }
static void __do_strncpy(void *dst, const void *src, int count) static void __do_strncpy(void *dst, const void *src, int count)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment