[PATCH] fix crash with CONFIG_LOCKDEP and CONFIG_DEBUG_PAGEALLOC

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi

This patch fixes crash on boot when both CONFIG_LOCKDEP and 
CONFIG_DEBUG_PAGEALLOC are enabled.

The problem:
config_lockdep calls save_stack_trace extensively to collect stack traces 
at points where locks are taken. save_stack_trace stops reading when the 
frame pointer points out of current page --- unfortunatelly there is a 
small window when frame pointer can point just before the end of the page 
and save_stack_trace will read some data from the next page --- crashing 
if CONFIG_DEBUG_PAGEALLOC is enabled.

The patch fixes it to make sure that save_stack_trace won't read 
out-of-page data.

Furthermore, if the frame pointer of the last function is uninitialized, 
it can (theoretically) produce loop (pointing at some already processed 
entry) or unaligned access --- this patch adds a check that the frame 
address is increasing and aligned.

Mikulas

Signed-off-by: Mikulas Patocka <mpatocka@xxxxxxxxxx>

---
 arch/sparc64/kernel/stacktrace.c |   17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

Index: linux-2.6.26-devel/arch/sparc64/kernel/stacktrace.c
===================================================================
--- linux-2.6.26-devel.orig/arch/sparc64/kernel/stacktrace.c	2008-08-12 17:32:01.000000000 +0200
+++ linux-2.6.26-devel/arch/sparc64/kernel/stacktrace.c	2008-08-12 23:20:01.000000000 +0200
@@ -6,7 +6,7 @@
 
 void save_stack_trace(struct stack_trace *trace)
 {
-	unsigned long ksp, fp, thread_base;
+	unsigned long ksp, fp, new_fp, thread_base;
 	struct thread_info *tp = task_thread_info(current);
 
 	stack_trace_flush();
@@ -24,26 +24,31 @@ void save_stack_trace(struct stack_trace
 		unsigned long pc;
 
 		/* Bogus frame pointer? */
-		if (fp < (thread_base + sizeof(struct thread_info)) ||
-		    fp >= (thread_base + THREAD_SIZE))
+		if (fp & 15 ||
+		    fp < (thread_base + sizeof(struct thread_info)) ||
+		    fp > (thread_base + THREAD_SIZE - sizeof(struct sparc_stackf)))
 			break;
 
 		sf = (struct sparc_stackf *) fp;
 		regs = (struct pt_regs *) (sf + 1);
 
-		if ((regs->magic & ~0x1ff) == PT_REGS_MAGIC) {
+		if ((unsigned long)regs <= (thread_base + THREAD_SIZE - sizeof(struct pt_regs)) && (regs->magic & ~0x1ff) == PT_REGS_MAGIC) {
 			if (!(regs->tstate & TSTATE_PRIV))
 				break;
 			pc = regs->tpc;
-			fp = regs->u_regs[UREG_I6] + STACK_BIAS;
+			new_fp = regs->u_regs[UREG_I6] + STACK_BIAS;
 		} else {
 			pc = sf->callers_pc;
-			fp = (unsigned long)sf->fp + STACK_BIAS;
+			new_fp = (unsigned long)sf->fp + STACK_BIAS;
 		}
 
 		if (trace->skip > 0)
 			trace->skip--;
 		else
 			trace->entries[trace->nr_entries++] = pc;
+
+		if (new_fp <= fp)
+			break;
+		fp = new_fp;
 	} while (trace->nr_entries < trace->max_entries);
 }
--
To unsubscribe from this list: send the line "unsubscribe sparclinux" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Kernel Development]     [DCCP]     [Linux ARM Development]     [Linux]     [Photo]     [Yosemite Help]     [Linux ARM Kernel]     [Linux SCSI]     [Linux x86_64]     [Linux Hams]

  Powered by Linux