lkml.org 
[lkml]   [2013]   [Nov]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH] ARM : unwinder : Prevent data abort due to stack overflow in unwind_exec_insn Signed-off-by: Anurag Aggarwal <>
Date
While executing some unwind instructions stack overflow can cause a data abort
when area beyond stack is not mapped to physical memory.

To prevent the data abort check whether it is possible to execute
these instructions before unwinding the stack
---
arch/arm/kernel/unwind.c | 59 +++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 58 insertions(+), 1 deletions(-)

diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c
index 00df012..3777cd7 100644
--- a/arch/arm/kernel/unwind.c
+++ b/arch/arm/kernel/unwind.c
@@ -49,6 +49,8 @@
#include <asm/traps.h>
#include <asm/unwind.h>

+#define TOTAL_REGISTERS 16
+
/* Dummy functions to avoid linker complaints */
void __aeabi_unwind_cpp_pr0(void)
{
@@ -66,7 +68,7 @@ void __aeabi_unwind_cpp_pr2(void)
EXPORT_SYMBOL(__aeabi_unwind_cpp_pr2);

struct unwind_ctrl_block {
- unsigned long vrs[16]; /* virtual register set */
+ unsigned long vrs[TOTAL_REGISTERS]; /* virtual register set */
const unsigned long *insn; /* pointer to the current instructions word */
int entries; /* number of entries left to interpret */
int byte; /* current byte number in the instructions word */
@@ -235,6 +237,58 @@ static unsigned long unwind_get_byte(struct unwind_ctrl_block *ctrl)
return ret;
}

+/* check whether there is enough space on stack to execute instructions
+ that can cause a data abort*/
+static int unwind_check_insn(struct unwind_ctrl_block *ctrl, unsigned long insn)
+{
+ unsigned long high, low;
+ int required_stack = 0;
+
+ low = ctrl->vrs[SP];
+ high = ALIGN(low, THREAD_SIZE);
+
+ /* check whether we have enough space to extract
+ atleast one set of registers*/
+ if ((high - low) > TOTAL_REGISTERS)
+ return URC_OK;
+
+ if ((insn & 0xf0) == 0x80) {
+ unsigned long mask;
+ insn = (insn << 8) | unwind_get_byte(ctrl);
+ mask = insn & 0x0fff;
+ if (mask == 0) {
+ pr_warning("unwind: 'Refuse to unwind' instruction %04lx\n",
+ insn);
+ return -URC_FAILURE;
+ }
+ while (mask) {
+ if (mask & 1)
+ required_stack++;
+ mask >>= 1;
+ }
+ } else if ((insn & 0xf0) == 0xa0) {
+ required_stack += insn & 7;
+ required_stack += (insn & 0x80) ? 1 : 0;
+ } else if (insn == 0xb1) {
+ unsigned long mask = unwind_get_byte(ctrl);
+ if (mask == 0 || mask & 0xf0) {
+ pr_warning("unwind: Spare encoding %04lx\n",
+ (insn << 8) | mask);
+ return -URC_FAILURE;
+ }
+ while (mask) {
+ if (mask & 1)
+ required_stack++;
+ mask >>= 1;
+ }
+ }
+
+ if ((high - low) < required_stack)
+ return -URC_FAILURE;
+
+ return URC_OK;
+}
+
/*
* Execute the current unwind instruction.
*/
@@ -244,6 +298,9 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)

pr_debug("%s: insn = %08lx\n", __func__, insn);

+ if (unwind_check_insn(ctrl, insn) < 0)
+ return -URC_FAILURE;
+
if ((insn & 0xc0) == 0x00)
ctrl->vrs[SP] += ((insn & 0x3f) << 2) + 4;
else if ((insn & 0xc0) == 0x40)
--
1.7.0.4


\
 
 \ /
  Last update: 2013-11-28 11:41    [W:0.072 / U:0.020 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site