diff options
author | Andi Kleen <ak@suse.de> | 2006-07-28 16:44:42 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-07-29 06:28:00 +0400 |
commit | d5a2601734bcc740ee78dc4cb0c56b5687da7bd9 (patch) | |
tree | 42146e7eda57909a475ad69cee5cacbf39033cff | |
parent | 5d2edfe004f5d0f3d805967bd6b4ada95e6aa419 (diff) | |
download | linux-d5a2601734bcc740ee78dc4cb0c56b5687da7bd9.tar.xz |
[PATCH] i386/x86-64: Add user_mode checks to profile_pc for oprofile
Fixes a obscure user space triggerable crash during oprofiling.
Oprofile calls profile_pc from NMIs even when user_mode(regs) is not true and
the program counter is inside the kernel lock section. This opens
a race - when a user program jumps to a kernel lock address and
a NMI happens before the illegal page fault exception is raised
and the program has a unmapped esp or ebp then the kernel could
oops. NMIs have a higher priority than exceptions so that could
happen.
Add user_mode checks to i386/x86-64 profile_pc to prevent that.
Cc: John Levon <levon@movementarian.org>
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | arch/i386/kernel/time.c | 2 | ||||
-rw-r--r-- | arch/x86_64/kernel/time.c | 2 |
2 files changed, 2 insertions, 2 deletions
diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c index 8705c0f05788..edd00f6cee37 100644 --- a/arch/i386/kernel/time.c +++ b/arch/i386/kernel/time.c @@ -135,7 +135,7 @@ unsigned long profile_pc(struct pt_regs *regs) { unsigned long pc = instruction_pointer(regs); - if (in_lock_functions(pc)) + if (!user_mode_vm(regs) && in_lock_functions(pc)) return *(unsigned long *)(regs->ebp + 4); return pc; diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index b9ff75992c16..e0341c6808e5 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c @@ -193,7 +193,7 @@ unsigned long profile_pc(struct pt_regs *regs) is just accounted to the spinlock function. Better would be to write these functions in assembler again and check exactly. */ - if (in_lock_functions(pc)) { + if (!user_mode(regs) && in_lock_functions(pc)) { char *v = *(char **)regs->rsp; if ((v >= _stext && v <= _etext) || (v >= _sinittext && v <= _einittext) || |