|
@@ -253,22 +253,15 @@ unsigned long *unwind_get_return_address_ptr(struct unwind_state *state)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-static bool stack_access_ok(struct unwind_state *state, unsigned long addr,
|
|
|
+static bool stack_access_ok(struct unwind_state *state, unsigned long _addr,
|
|
|
size_t len)
|
|
|
{
|
|
|
struct stack_info *info = &state->stack_info;
|
|
|
+ void *addr = (void *)_addr;
|
|
|
|
|
|
- /*
|
|
|
- * If the address isn't on the current stack, switch to the next one.
|
|
|
- *
|
|
|
- * We may have to traverse multiple stacks to deal with the possibility
|
|
|
- * that info->next_sp could point to an empty stack and the address
|
|
|
- * could be on a subsequent stack.
|
|
|
- */
|
|
|
- while (!on_stack(info, (void *)addr, len))
|
|
|
- if (get_stack_info(info->next_sp, state->task, info,
|
|
|
- &state->stack_mask))
|
|
|
- return false;
|
|
|
+ if (!on_stack(info, addr, len) &&
|
|
|
+ (get_stack_info(addr, state->task, info, &state->stack_mask)))
|
|
|
+ return false;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
@@ -283,42 +276,32 @@ static bool deref_stack_reg(struct unwind_state *state, unsigned long addr,
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-#define REGS_SIZE (sizeof(struct pt_regs))
|
|
|
-#define SP_OFFSET (offsetof(struct pt_regs, sp))
|
|
|
-#define IRET_REGS_SIZE (REGS_SIZE - offsetof(struct pt_regs, ip))
|
|
|
-#define IRET_SP_OFFSET (SP_OFFSET - offsetof(struct pt_regs, ip))
|
|
|
-
|
|
|
static bool deref_stack_regs(struct unwind_state *state, unsigned long addr,
|
|
|
- unsigned long *ip, unsigned long *sp, bool full)
|
|
|
+ unsigned long *ip, unsigned long *sp)
|
|
|
{
|
|
|
- size_t regs_size = full ? REGS_SIZE : IRET_REGS_SIZE;
|
|
|
- size_t sp_offset = full ? SP_OFFSET : IRET_SP_OFFSET;
|
|
|
- struct pt_regs *regs = (struct pt_regs *)(addr + regs_size - REGS_SIZE);
|
|
|
-
|
|
|
- if (IS_ENABLED(CONFIG_X86_64)) {
|
|
|
- if (!stack_access_ok(state, addr, regs_size))
|
|
|
- return false;
|
|
|
-
|
|
|
- *ip = regs->ip;
|
|
|
- *sp = regs->sp;
|
|
|
+ struct pt_regs *regs = (struct pt_regs *)addr;
|
|
|
|
|
|
- return true;
|
|
|
- }
|
|
|
+ /* x86-32 support will be more complicated due to the ®s->sp hack */
|
|
|
+ BUILD_BUG_ON(IS_ENABLED(CONFIG_X86_32));
|
|
|
|
|
|
- if (!stack_access_ok(state, addr, sp_offset))
|
|
|
+ if (!stack_access_ok(state, addr, sizeof(struct pt_regs)))
|
|
|
return false;
|
|
|
|
|
|
*ip = regs->ip;
|
|
|
+ *sp = regs->sp;
|
|
|
+ return true;
|
|
|
+}
|
|
|
|
|
|
- if (user_mode(regs)) {
|
|
|
- if (!stack_access_ok(state, addr + sp_offset,
|
|
|
- REGS_SIZE - SP_OFFSET))
|
|
|
- return false;
|
|
|
+static bool deref_stack_iret_regs(struct unwind_state *state, unsigned long addr,
|
|
|
+ unsigned long *ip, unsigned long *sp)
|
|
|
+{
|
|
|
+ struct pt_regs *regs = (void *)addr - IRET_FRAME_OFFSET;
|
|
|
|
|
|
- *sp = regs->sp;
|
|
|
- } else
|
|
|
- *sp = (unsigned long)®s->sp;
|
|
|
+ if (!stack_access_ok(state, addr, IRET_FRAME_SIZE))
|
|
|
+ return false;
|
|
|
|
|
|
+ *ip = regs->ip;
|
|
|
+ *sp = regs->sp;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
@@ -327,7 +310,6 @@ bool unwind_next_frame(struct unwind_state *state)
|
|
|
unsigned long ip_p, sp, orig_ip, prev_sp = state->sp;
|
|
|
enum stack_type prev_type = state->stack_info.type;
|
|
|
struct orc_entry *orc;
|
|
|
- struct pt_regs *ptregs;
|
|
|
bool indirect = false;
|
|
|
|
|
|
if (unwind_done(state))
|
|
@@ -435,7 +417,7 @@ bool unwind_next_frame(struct unwind_state *state)
|
|
|
break;
|
|
|
|
|
|
case ORC_TYPE_REGS:
|
|
|
- if (!deref_stack_regs(state, sp, &state->ip, &state->sp, true)) {
|
|
|
+ if (!deref_stack_regs(state, sp, &state->ip, &state->sp)) {
|
|
|
orc_warn("can't dereference registers at %p for ip %pB\n",
|
|
|
(void *)sp, (void *)orig_ip);
|
|
|
goto done;
|
|
@@ -447,20 +429,14 @@ bool unwind_next_frame(struct unwind_state *state)
|
|
|
break;
|
|
|
|
|
|
case ORC_TYPE_REGS_IRET:
|
|
|
- if (!deref_stack_regs(state, sp, &state->ip, &state->sp, false)) {
|
|
|
+ if (!deref_stack_iret_regs(state, sp, &state->ip, &state->sp)) {
|
|
|
orc_warn("can't dereference iret registers at %p for ip %pB\n",
|
|
|
(void *)sp, (void *)orig_ip);
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
- ptregs = container_of((void *)sp, struct pt_regs, ip);
|
|
|
- if ((unsigned long)ptregs >= prev_sp &&
|
|
|
- on_stack(&state->stack_info, ptregs, REGS_SIZE)) {
|
|
|
- state->regs = ptregs;
|
|
|
- state->full_regs = false;
|
|
|
- } else
|
|
|
- state->regs = NULL;
|
|
|
-
|
|
|
+ state->regs = (void *)sp - IRET_FRAME_OFFSET;
|
|
|
+ state->full_regs = false;
|
|
|
state->signal = true;
|
|
|
break;
|
|
|
|