Re: [ 06/37] x86-32: Fix invalid stack address while in softirq
From: Herton Ronaldo Krzesinski <hidden>
Date: 2012-12-04 13:43:10
Also in:
lkml
On Fri, Nov 30, 2012 at 10:45:53AM -0800, Greg Kroah-Hartman wrote:
3.0-stable review patch. If anyone has any objections, please let me know. ------------------ From: Robert Richter <redacted> commit 1022623842cb72ee4d0dbf02f6937f38c92c3f41 upstream. In 32 bit the stack address provided by kernel_stack_pointer() may point to an invalid range causing NULL pointer access or page faults while in NMI (see trace below). This happens if called in softirq context and if the stack is empty. The address at ®s->sp is then out of range. Fixing this by checking if regs and ®s->sp are in the same stack context. Otherwise return the previous stack pointer stored in struct thread_info. If that address is invalid too, return address of regs.
[...]
Hi, this makes build fail with oprofile on i386 on 3.0.54:
ERROR: "kernel_stack_pointer" [arch/x86/oprofile/oprofile.ko] undefined!
The following commit should address this failure:
commit cb57a2b4cff7edf2a4e32c0163200e9434807e0a
Author: H. Peter Anvin [off-list ref]
Date: Tue Nov 20 22:21:02 2012 -0800
x86-32: Export kernel_stack_pointer() for modules
Modules, in particular oprofile (and possibly other similar tools)
need kernel_stack_pointer(), so export it using EXPORT_SYMBOL_GPL().
quoted hunk ↗ jump to hunk
--- a/arch/x86/include/asm/ptrace.h +++ b/arch/x86/include/asm/ptrace.h@@ -187,21 +187,14 @@ static inline int v8086_mode(struct pt_r #endif } -/* - * X86_32 CPUs don't save ss and esp if the CPU is already in kernel mode - * when it traps. The previous stack will be directly underneath the saved - * registers, and 'sp/ss' won't even have been saved. Thus the '®s->sp'. - * - * This is valid only for kernel mode traps. - */ -static inline unsigned long kernel_stack_pointer(struct pt_regs *regs) -{ #ifdef CONFIG_X86_32 - return (unsigned long)(®s->sp); +extern unsigned long kernel_stack_pointer(struct pt_regs *regs); #else +static inline unsigned long kernel_stack_pointer(struct pt_regs *regs) +{ return regs->sp; -#endif } +#endif #define GET_IP(regs) ((regs)->ip) #define GET_FP(regs) ((regs)->bp) --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c@@ -164,6 +164,34 @@ static inline bool invalid_selector(u16 #define FLAG_MASK FLAG_MASK_32 +/* + * X86_32 CPUs don't save ss and esp if the CPU is already in kernel mode + * when it traps. The previous stack will be directly underneath the saved + * registers, and 'sp/ss' won't even have been saved. Thus the '®s->sp'. + * + * Now, if the stack is empty, '®s->sp' is out of range. In this + * case we try to take the previous stack. To always return a non-null + * stack pointer we fall back to regs as stack if no previous stack + * exists. + * + * This is valid only for kernel mode traps. + */ +unsigned long kernel_stack_pointer(struct pt_regs *regs) +{ + unsigned long context = (unsigned long)regs & ~(THREAD_SIZE - 1); + unsigned long sp = (unsigned long)®s->sp; + struct thread_info *tinfo; + + if (context == (sp & ~(THREAD_SIZE - 1))) + return sp; + + tinfo = (struct thread_info *)context; + if (tinfo->previous_esp) + return tinfo->previous_esp; + + return (unsigned long)regs; +} + static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long regno) { BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0);
-- []'s Herton