[PATCH] ARM: backtrace: avoid crash on large invalid fp value
Colin Cross
ccross at google.com
Fri Nov 2 19:47:38 EDT 2012
On Wed, Oct 10, 2012 at 4:15 AM, Dave Martin <dave.martin at linaro.org> wrote:
> On Tue, Oct 09, 2012 at 11:46:12PM -0700, Todd Poynor wrote:
>> Invalid frame pointer (signed) -4 <= fp <= -1 defeats check for too high
>> on overflow.
>>
>> Signed-off-by: Todd Poynor <toddpoynor at google.com>
>> ---
>> arch/arm/kernel/stacktrace.c | 2 +-
>> 1 files changed, 1 insertions(+), 1 deletions(-)
>>
>> diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c
>> index 00f79e5..6315162 100644
>> --- a/arch/arm/kernel/stacktrace.c
>> +++ b/arch/arm/kernel/stacktrace.c
>> @@ -31,7 +31,7 @@ int notrace unwind_frame(struct stackframe *frame)
>> high = ALIGN(low, THREAD_SIZE);
>>
>> /* check current frame pointer is within bounds */
>> - if (fp < (low + 12) || fp + 4 >= high)
>> + if (fp < (low + 12) || fp >= high - 4)
>> return -EINVAL;
>>
>> /* restore the registers from the stack frame */
>
> sp and fp can still be complete garbage in the case of a corrupted frame,
> so low + 12 can still overflow and cause us to read beyond the stack base.
>
> A more robust patch might be as follows. This also checks for misaligned
> fp and sp values, since those indicate corruption and there can be no
> sensible way to interpret the resulting frame in that case.
>
> Also, according to the definition of current_thread_info(),
> IS_ALIGNED(sp, THREAD_SIZE) indicates a full stack extending from sp
> to sp + THREAD_SIZE, and not an empty stack extending from sp -
> THREAD_SIZE to sp. We cannot backtrace this situation anyway, since
> that would imply that the frame record extends beyond the stack...
> but this patch tidies it up in the interest of clarity.
>
> Cheers
> ---Dave
>
> (untested)
>
> diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c
> index 00f79e5..fec82be 100644
> --- a/arch/arm/kernel/stacktrace.c
> +++ b/arch/arm/kernel/stacktrace.c
> @@ -28,10 +28,20 @@ int notrace unwind_frame(struct stackframe *frame)
>
> /* only go to a higher address on the stack */
> low = frame->sp;
> - high = ALIGN(low, THREAD_SIZE);
> + if (!IS_ALIGNED(fp, 4))
> + return -EINVAL;
> +
> + /*
> + * low + 1 here ensures that high > sp, consistent with the
> + * definition of current_thread_info().
> + * We subtract 1 to compute the highest allowable byte address.
> + * Otherwise, we might get high == 0 which would confuse our
> + * comparisons.
> + */
> + high = ALIGN(low + 1, THREAD_SIZE) - 1;
>
> /* check current frame pointer is within bounds */
> - if (fp < (low + 12) || fp + 4 >= high)
> + if (fp < 12 || fp - 12 < low || fp > high)
> return -EINVAL;
>
> /* restore the registers from the stack frame */
> @@ -39,6 +49,10 @@ int notrace unwind_frame(struct stackframe *frame)
> frame->sp = *(unsigned long *)(fp - 8);
> frame->pc = *(unsigned long *)(fp - 4);
>
> + /* Do not claim the frame is valid if if is obviously corrupt: */
> + if (!IS_ALIGNED(frame->fp, 4))
> + return -EINVAL;
> +
> return 0;
> }
> #endif
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Dave or Todd, mind reposting this, or should I squash it into my
CONFIG_SMP stacktrace series?
More information about the linux-arm-kernel
mailing list