From: Catalin Marinas Date: Fri, 19 Jun 2009 15:42:11 +0000 (+0100) Subject: [ARM] 5558/1: Add extra checks to ARM unwinder to avoid tracing corrupt stacks X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=c894ed6956f126d60d888e8efc5fb3a595ba89ae;p=openwrt%2Fstaging%2Fblogic.git [ARM] 5558/1: Add extra checks to ARM unwinder to avoid tracing corrupt stacks There are situations where the unwinder goes beyond stack boundaries and unwinds random data. This patch moves the stack boundaries check after the unwind_exec_insn() call and adds an extra check for possible infinite loops (like "mov pc, lr" with pc == lr). The patch also fixes a bug in the unwind instructions interpreter. The 0xb0 instruction can only set PC to LR if this wasn't already set by a previous instruction (this is used on exceptions taken while in kernel mode where svc_entry is annotated with ".save {r0 - pc}"). Tested-by: Tony Lindgren Signed-off-by: Catalin Marinas Signed-off-by: Russell King --- diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c index 1dedc2c7ff49..dd56e11f339a 100644 --- a/arch/arm/kernel/unwind.c +++ b/arch/arm/kernel/unwind.c @@ -212,7 +212,8 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) ctrl->vrs[14] = *vsp++; ctrl->vrs[SP] = (unsigned long)vsp; } else if (insn == 0xb0) { - ctrl->vrs[PC] = ctrl->vrs[LR]; + if (ctrl->vrs[PC] == 0) + ctrl->vrs[PC] = ctrl->vrs[LR]; /* no further processing */ ctrl->entries = 0; } else if (insn == 0xb1) { @@ -309,18 +310,20 @@ int unwind_frame(struct stackframe *frame) } while (ctrl.entries > 0) { - int urc; - - if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= high) - return -URC_FAILURE; - urc = unwind_exec_insn(&ctrl); + int urc = unwind_exec_insn(&ctrl); if (urc < 0) return urc; + if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= high) + return -URC_FAILURE; } if (ctrl.vrs[PC] == 0) ctrl.vrs[PC] = ctrl.vrs[LR]; + /* check for infinite loop */ + if (frame->pc == ctrl.vrs[PC]) + return -URC_FAILURE; + frame->fp = ctrl.vrs[FP]; frame->sp = ctrl.vrs[SP]; frame->lr = ctrl.vrs[LR]; @@ -332,7 +335,6 @@ int unwind_frame(struct stackframe *frame) void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk) { struct stackframe frame; - unsigned long high, low; register unsigned long current_sp asm ("sp"); pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); @@ -362,9 +364,6 @@ void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk) frame.pc = thread_saved_pc(tsk); } - low = frame.sp & ~(THREAD_SIZE - 1); - high = low + THREAD_SIZE; - while (1) { int urc; unsigned long where = frame.pc;