[kernel/f15/master] Restore reliable stack backtraces, and hopefully fix RHBZ #700718

Chuck Ebbert cebbert at fedoraproject.org
Tue May 3 06:38:55 UTC 2011


commit 50ffc6fec51e26b67f3aa57fb26914c04af6f8f9
Author: Chuck Ebbert <cebbert at redhat.com>
Date:   Tue May 3 02:38:59 2011 -0400

    Restore reliable stack backtraces, and hopefully fix RHBZ #700718

 kernel.spec                                        |    8 +
 ...dump-info-when-frame-pointer-is-available.patch |  352 ++++++++++++++++++++
 2 files changed, 360 insertions(+), 0 deletions(-)
---
diff --git a/kernel.spec b/kernel.spec
index a36c747..233f782 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -732,6 +732,10 @@ Patch12401: scsi-mptsas-prevent-heap-overflows-and-unchecked-reads.patch
 # CVE-2011-1581
 Patch12402: bonding-incorrect-tx-queue-offset.patch
 
+# Restore reliable stack backtraces, and hopefully
+# fix RHBZ #700718
+Patch12403: x86-dumpstack-correct-stack-dump-info-when-frame-pointer-is-available.patch
+
 %endif
 
 BuildRoot: %{_tmppath}/kernel-%{KVERREL}-root
@@ -1179,6 +1183,9 @@ ApplyPatch linux-2.6-utrace-ptrace.patch
 
 # Architecture patches
 # x86(-64)
+# Restore reliable stack backtraces, and hopefully
+# fix RHBZ #700718
+ApplyPatch x86-dumpstack-correct-stack-dump-info-when-frame-pointer-is-available.patch
 
 #
 # Intel IOMMU
@@ -1969,6 +1976,7 @@ fi
 - [SCSI] mpt2sas: prevent heap overflows and unchecked reads
   (CVE-2011-1494, CVE-2011-1495)
 - bonding: Incorrect TX queue offset (CVE-2011-1581)
+- Restore reliable stack backtraces, and hopefully fix RHBZ #700718
 
 * Mon May 02 2011 Kyle McMartin <kmcmartin at redhat.com> 2.6.38.5-22
 - And to the released 2.6.38.5
diff --git a/x86-dumpstack-correct-stack-dump-info-when-frame-pointer-is-available.patch b/x86-dumpstack-correct-stack-dump-info-when-frame-pointer-is-available.patch
new file mode 100644
index 0000000..36eae4b
--- /dev/null
+++ b/x86-dumpstack-correct-stack-dump-info-when-frame-pointer-is-available.patch
@@ -0,0 +1,352 @@
+From: Namhyung Kim <namhyung at gmail.com>
+Date: Fri, 18 Mar 2011 02:40:06 +0000 (+0900)
+Subject: x86, dumpstack: Correct stack dump info when frame pointer is available
+X-Git-Tag: v2.6.39-rc1~64^2~7
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=e8e999cf3cc733482e390b02ff25a64cecdc0b64
+
+x86, dumpstack: Correct stack dump info when frame pointer is available
+
+Current stack dump code scans entire stack and check each entry
+contains a pointer to kernel code. If CONFIG_FRAME_POINTER=y it
+could mark whether the pointer is valid or not based on value of
+the frame pointer. Invalid entries could be preceded by '?' sign.
+
+However this was not going to happen because scan start point
+was always higher than the frame pointer so that they could not
+meet.
+
+Commit 9c0729dc8062 ("x86: Eliminate bp argument from the stack
+tracing routines") delayed bp acquisition point, so the bp was
+read in lower frame, thus all of the entries were marked
+invalid.
+
+This patch fixes this by reverting above commit while retaining
+stack_frame() helper as suggested by Frederic Weisbecker.
+
+End result looks like below:
+
+before:
+
+ [    3.508329] Call Trace:
+ [    3.508551]  [<ffffffff814f35c9>] ? panic+0x91/0x199
+ [    3.508662]  [<ffffffff814f3739>] ? printk+0x68/0x6a
+ [    3.508770]  [<ffffffff81a981b2>] ? mount_block_root+0x257/0x26e
+ [    3.508876]  [<ffffffff81a9821f>] ? mount_root+0x56/0x5a
+ [    3.508975]  [<ffffffff81a98393>] ? prepare_namespace+0x170/0x1a9
+ [    3.509216]  [<ffffffff81a9772b>] ? kernel_init+0x1d2/0x1e2
+ [    3.509335]  [<ffffffff81003894>] ? kernel_thread_helper+0x4/0x10
+ [    3.509442]  [<ffffffff814f6880>] ? restore_args+0x0/0x30
+ [    3.509542]  [<ffffffff81a97559>] ? kernel_init+0x0/0x1e2
+ [    3.509641]  [<ffffffff81003890>] ? kernel_thread_helper+0x0/0x10
+
+after:
+
+ [    3.522991] Call Trace:
+ [    3.523351]  [<ffffffff814f35b9>] panic+0x91/0x199
+ [    3.523468]  [<ffffffff814f3729>] ? printk+0x68/0x6a
+ [    3.523576]  [<ffffffff81a981b2>] mount_block_root+0x257/0x26e
+ [    3.523681]  [<ffffffff81a9821f>] mount_root+0x56/0x5a
+ [    3.523780]  [<ffffffff81a98393>] prepare_namespace+0x170/0x1a9
+ [    3.523885]  [<ffffffff81a9772b>] kernel_init+0x1d2/0x1e2
+ [    3.523987]  [<ffffffff81003894>] kernel_thread_helper+0x4/0x10
+ [    3.524228]  [<ffffffff814f6880>] ? restore_args+0x0/0x30
+ [    3.524345]  [<ffffffff81a97559>] ? kernel_init+0x0/0x1e2
+ [    3.524445]  [<ffffffff81003890>] ? kernel_thread_helper+0x0/0x10
+
+ -v5:
+   * fix build breakage with oprofile
+
+ -v4:
+   * use 0 instead of regs->bp
+   * separate out printk changes
+
+ -v3:
+   * apply comment from Frederic
+   * add a couple of printk fixes
+
+Signed-off-by: Namhyung Kim <namhyung at gmail.com>
+Acked-by: Peter Zijlstra <a.p.zijlstra at chello.nl>
+Acked-by: Frederic Weisbecker <fweisbec at gmail.com>
+Cc: Soren Sandmann <ssp at redhat.com>
+Cc: Paul Mackerras <paulus at samba.org>
+Cc: Arnaldo Carvalho de Melo <acme at redhat.com>
+Cc: Robert Richter <robert.richter at amd.com>
+LKML-Reference: <1300416006-3163-1-git-send-email-namhyung at gmail.com>
+Signed-off-by: Ingo Molnar <mingo at elte.hu>
+---
+
+diff --git a/arch/x86/include/asm/kdebug.h b/arch/x86/include/asm/kdebug.h
+index 518bbbb..fe2cc6e 100644
+--- a/arch/x86/include/asm/kdebug.h
++++ b/arch/x86/include/asm/kdebug.h
+@@ -26,7 +26,7 @@ extern void die(const char *, struct pt_regs *,long);
+ extern int __must_check __die(const char *, struct pt_regs *, long);
+ extern void show_registers(struct pt_regs *regs);
+ extern void show_trace(struct task_struct *t, struct pt_regs *regs,
+-		       unsigned long *sp);
++		       unsigned long *sp, unsigned long bp);
+ extern void __show_regs(struct pt_regs *regs, int all);
+ extern void show_regs(struct pt_regs *regs);
+ extern unsigned long oops_begin(void);
+diff --git a/arch/x86/include/asm/stacktrace.h b/arch/x86/include/asm/stacktrace.h
+index 52b5c7e..d7e89c8 100644
+--- a/arch/x86/include/asm/stacktrace.h
++++ b/arch/x86/include/asm/stacktrace.h
+@@ -47,7 +47,7 @@ struct stacktrace_ops {
+ };
+ 
+ void dump_trace(struct task_struct *tsk, struct pt_regs *regs,
+-		unsigned long *stack,
++		unsigned long *stack, unsigned long bp,
+ 		const struct stacktrace_ops *ops, void *data);
+ 
+ #ifdef CONFIG_X86_32
+@@ -86,11 +86,11 @@ stack_frame(struct task_struct *task, struct pt_regs *regs)
+ 
+ extern void
+ show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
+-		   unsigned long *stack, char *log_lvl);
++		   unsigned long *stack, unsigned long bp, char *log_lvl);
+ 
+ extern void
+ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
+-		   unsigned long *sp, char *log_lvl);
++		   unsigned long *sp, unsigned long bp, char *log_lvl);
+ 
+ extern unsigned int code_bytes;
+ 
+diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
+index 279bc9d..3061276 100644
+--- a/arch/x86/kernel/cpu/perf_event.c
++++ b/arch/x86/kernel/cpu/perf_event.c
+@@ -1792,7 +1792,7 @@ perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs)
+ 
+ 	perf_callchain_store(entry, regs->ip);
+ 
+-	dump_trace(NULL, regs, NULL, &backtrace_ops, entry);
++	dump_trace(NULL, regs, NULL, 0, &backtrace_ops, entry);
+ }
+ 
+ #ifdef CONFIG_COMPAT
+diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c
+index 220a1c1..999e279 100644
+--- a/arch/x86/kernel/dumpstack.c
++++ b/arch/x86/kernel/dumpstack.c
+@@ -175,21 +175,21 @@ static const struct stacktrace_ops print_trace_ops = {
+ 
+ void
+ show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
+-		unsigned long *stack, char *log_lvl)
++		unsigned long *stack, unsigned long bp, char *log_lvl)
+ {
+ 	printk("%sCall Trace:\n", log_lvl);
+-	dump_trace(task, regs, stack, &print_trace_ops, log_lvl);
++	dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl);
+ }
+ 
+ void show_trace(struct task_struct *task, struct pt_regs *regs,
+-		unsigned long *stack)
++		unsigned long *stack, unsigned long bp)
+ {
+-	show_trace_log_lvl(task, regs, stack, "");
++	show_trace_log_lvl(task, regs, stack, bp, "");
+ }
+ 
+ void show_stack(struct task_struct *task, unsigned long *sp)
+ {
+-	show_stack_log_lvl(task, NULL, sp, "");
++	show_stack_log_lvl(task, NULL, sp, 0, "");
+ }
+ 
+ /*
+@@ -197,14 +197,16 @@ void show_stack(struct task_struct *task, unsigned long *sp)
+  */
+ void dump_stack(void)
+ {
++	unsigned long bp;
+ 	unsigned long stack;
+ 
++	bp = stack_frame(current, NULL);
+ 	printk("Pid: %d, comm: %.20s %s %s %.*s\n",
+ 		current->pid, current->comm, print_tainted(),
+ 		init_utsname()->release,
+ 		(int)strcspn(init_utsname()->version, " "),
+ 		init_utsname()->version);
+-	show_trace(NULL, NULL, &stack);
++	show_trace(NULL, NULL, &stack, bp);
+ }
+ EXPORT_SYMBOL(dump_stack);
+ 
+diff --git a/arch/x86/kernel/dumpstack_32.c b/arch/x86/kernel/dumpstack_32.c
+index 74cc1ed..3b97a80 100644
+--- a/arch/x86/kernel/dumpstack_32.c
++++ b/arch/x86/kernel/dumpstack_32.c
+@@ -17,12 +17,11 @@
+ #include <asm/stacktrace.h>
+ 
+ 
+-void dump_trace(struct task_struct *task,
+-		struct pt_regs *regs, unsigned long *stack,
++void dump_trace(struct task_struct *task, struct pt_regs *regs,
++		unsigned long *stack, unsigned long bp,
+ 		const struct stacktrace_ops *ops, void *data)
+ {
+ 	int graph = 0;
+-	unsigned long bp;
+ 
+ 	if (!task)
+ 		task = current;
+@@ -35,7 +34,9 @@ void dump_trace(struct task_struct *task,
+ 			stack = (unsigned long *)task->thread.sp;
+ 	}
+ 
+-	bp = stack_frame(task, regs);
++	if (!bp)
++		bp = stack_frame(task, regs);
++
+ 	for (;;) {
+ 		struct thread_info *context;
+ 
+@@ -55,7 +56,7 @@ EXPORT_SYMBOL(dump_trace);
+ 
+ void
+ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
+-		   unsigned long *sp, char *log_lvl)
++		   unsigned long *sp, unsigned long bp, char *log_lvl)
+ {
+ 	unsigned long *stack;
+ 	int i;
+@@ -77,7 +78,7 @@ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
+ 		touch_nmi_watchdog();
+ 	}
+ 	printk(KERN_CONT "\n");
+-	show_trace_log_lvl(task, regs, sp, log_lvl);
++	show_trace_log_lvl(task, regs, sp, bp, log_lvl);
+ }
+ 
+ 
+@@ -102,7 +103,7 @@ void show_registers(struct pt_regs *regs)
+ 		u8 *ip;
+ 
+ 		printk(KERN_EMERG "Stack:\n");
+-		show_stack_log_lvl(NULL, regs, &regs->sp, KERN_EMERG);
++		show_stack_log_lvl(NULL, regs, &regs->sp, 0, KERN_EMERG);
+ 
+ 		printk(KERN_EMERG "Code: ");
+ 
+diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c
+index a6b6fcf..e71c98d 100644
+--- a/arch/x86/kernel/dumpstack_64.c
++++ b/arch/x86/kernel/dumpstack_64.c
+@@ -139,8 +139,8 @@ fixup_bp_irq_link(unsigned long bp, unsigned long *stack,
+  * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack
+  */
+ 
+-void dump_trace(struct task_struct *task,
+-		struct pt_regs *regs, unsigned long *stack,
++void dump_trace(struct task_struct *task, struct pt_regs *regs,
++		unsigned long *stack, unsigned long bp,
+ 		const struct stacktrace_ops *ops, void *data)
+ {
+ 	const unsigned cpu = get_cpu();
+@@ -150,7 +150,6 @@ void dump_trace(struct task_struct *task,
+ 	struct thread_info *tinfo;
+ 	int graph = 0;
+ 	unsigned long dummy;
+-	unsigned long bp;
+ 
+ 	if (!task)
+ 		task = current;
+@@ -161,7 +160,8 @@ void dump_trace(struct task_struct *task,
+ 			stack = (unsigned long *)task->thread.sp;
+ 	}
+ 
+-	bp = stack_frame(task, regs);
++	if (!bp)
++		bp = stack_frame(task, regs);
+ 	/*
+ 	 * Print function call entries in all stacks, starting at the
+ 	 * current stack address. If the stacks consist of nested
+@@ -225,7 +225,7 @@ EXPORT_SYMBOL(dump_trace);
+ 
+ void
+ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
+-		   unsigned long *sp, char *log_lvl)
++		   unsigned long *sp, unsigned long bp, char *log_lvl)
+ {
+ 	unsigned long *irq_stack_end;
+ 	unsigned long *irq_stack;
+@@ -269,7 +269,7 @@ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
+ 	preempt_enable();
+ 
+ 	printk(KERN_CONT "\n");
+-	show_trace_log_lvl(task, regs, sp, log_lvl);
++	show_trace_log_lvl(task, regs, sp, bp, log_lvl);
+ }
+ 
+ void show_registers(struct pt_regs *regs)
+@@ -298,7 +298,7 @@ void show_registers(struct pt_regs *regs)
+ 
+ 		printk(KERN_EMERG "Stack:\n");
+ 		show_stack_log_lvl(NULL, regs, (unsigned long *)sp,
+-				   KERN_EMERG);
++				   0, KERN_EMERG);
+ 
+ 		printk(KERN_EMERG "Code: ");
+ 
+diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
+index 99fa3ad..d46cbe4 100644
+--- a/arch/x86/kernel/process.c
++++ b/arch/x86/kernel/process.c
+@@ -87,7 +87,7 @@ void exit_thread(void)
+ void show_regs(struct pt_regs *regs)
+ {
+ 	show_registers(regs);
+-	show_trace(NULL, regs, (unsigned long *)kernel_stack_pointer(regs));
++	show_trace(NULL, regs, (unsigned long *)kernel_stack_pointer(regs), 0);
+ }
+ 
+ void show_regs_common(void)
+diff --git a/arch/x86/kernel/stacktrace.c b/arch/x86/kernel/stacktrace.c
+index 938c8e1..6515733 100644
+--- a/arch/x86/kernel/stacktrace.c
++++ b/arch/x86/kernel/stacktrace.c
+@@ -73,7 +73,7 @@ static const struct stacktrace_ops save_stack_ops_nosched = {
+  */
+ void save_stack_trace(struct stack_trace *trace)
+ {
+-	dump_trace(current, NULL, NULL, &save_stack_ops, trace);
++	dump_trace(current, NULL, NULL, 0, &save_stack_ops, trace);
+ 	if (trace->nr_entries < trace->max_entries)
+ 		trace->entries[trace->nr_entries++] = ULONG_MAX;
+ }
+@@ -81,14 +81,14 @@ EXPORT_SYMBOL_GPL(save_stack_trace);
+ 
+ void save_stack_trace_regs(struct stack_trace *trace, struct pt_regs *regs)
+ {
+-	dump_trace(current, regs, NULL, &save_stack_ops, trace);
++	dump_trace(current, regs, NULL, 0, &save_stack_ops, trace);
+ 	if (trace->nr_entries < trace->max_entries)
+ 		trace->entries[trace->nr_entries++] = ULONG_MAX;
+ }
+ 
+ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+ {
+-	dump_trace(tsk, NULL, NULL, &save_stack_ops_nosched, trace);
++	dump_trace(tsk, NULL, NULL, 0, &save_stack_ops_nosched, trace);
+ 	if (trace->nr_entries < trace->max_entries)
+ 		trace->entries[trace->nr_entries++] = ULONG_MAX;
+ }
+diff --git a/arch/x86/oprofile/backtrace.c b/arch/x86/oprofile/backtrace.c
+index 72cbec1..2d49d4e 100644
+--- a/arch/x86/oprofile/backtrace.c
++++ b/arch/x86/oprofile/backtrace.c
+@@ -126,7 +126,7 @@ x86_backtrace(struct pt_regs * const regs, unsigned int depth)
+ 	if (!user_mode_vm(regs)) {
+ 		unsigned long stack = kernel_stack_pointer(regs);
+ 		if (depth)
+-			dump_trace(NULL, regs, (unsigned long *)stack,
++			dump_trace(NULL, regs, (unsigned long *)stack, 0,
+ 				   &backtrace_ops, &depth);
+ 		return;
+ 	}


More information about the scm-commits mailing list