Skip to content

Commit

Permalink
[PATCH] unwinder: more sanity checks in Dwarf2 unwinder
Browse files Browse the repository at this point in the history
Tighten the requirements on both input to and output from the Dwarf2
unwinder.

Signed-off-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Andi Kleen <ak@suse.de>
  • Loading branch information
Jan Beulich authored and Andi Kleen committed Dec 7, 2006
1 parent eef5e0d commit 359ad0d
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 15 deletions.
7 changes: 7 additions & 0 deletions arch/i386/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,19 @@ dump_trace_unwind(struct unwind_frame_info *info, void *data)
{
struct ops_and_data *oad = (struct ops_and_data *)data;
int n = 0;
unsigned long sp = UNW_SP(info);

if (arch_unw_user_mode(info))
return -1;
while (unwind(info) == 0 && UNW_PC(info)) {
n++;
oad->ops->address(oad->data, UNW_PC(info));
if (arch_unw_user_mode(info))
break;
if ((sp & ~(PAGE_SIZE - 1)) == (UNW_SP(info) & ~(PAGE_SIZE - 1))
&& sp > UNW_SP(info))
break;
sp = UNW_SP(info);
}
return n;
}
Expand Down
7 changes: 7 additions & 0 deletions arch/x86_64/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,19 @@ static int dump_trace_unwind(struct unwind_frame_info *info, void *context)
{
struct ops_and_data *oad = (struct ops_and_data *)context;
int n = 0;
unsigned long sp = UNW_SP(info);

if (arch_unw_user_mode(info))
return -1;
while (unwind(info) == 0 && UNW_PC(info)) {
n++;
oad->ops->address(oad->data, UNW_PC(info));
if (arch_unw_user_mode(info))
break;
if ((sp & ~(PAGE_SIZE - 1)) == (UNW_SP(info) & ~(PAGE_SIZE - 1))
&& sp > UNW_SP(info))
break;
sp = UNW_SP(info);
}
return n;
}
Expand Down
12 changes: 4 additions & 8 deletions include/asm-i386/unwind.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,13 @@ extern asmlinkage int arch_unwind_init_running(struct unwind_frame_info *,
void *arg),
void *arg);

static inline int arch_unw_user_mode(const struct unwind_frame_info *info)
static inline int arch_unw_user_mode(/*const*/ struct unwind_frame_info *info)
{
#if 0 /* This can only work when selector register and EFLAGS saves/restores
are properly annotated (and tracked in UNW_REGISTER_INFO). */
return user_mode_vm(&info->regs);
#else
return info->regs.eip < PAGE_OFFSET
return user_mode_vm(&info->regs)
|| info->regs.eip < PAGE_OFFSET
|| (info->regs.eip >= __fix_to_virt(FIX_VDSO)
&& info->regs.eip < __fix_to_virt(FIX_VDSO) + PAGE_SIZE)
&& info->regs.eip < __fix_to_virt(FIX_VDSO) + PAGE_SIZE)
|| info->regs.esp < PAGE_OFFSET;
#endif
}

#else
Expand Down
8 changes: 2 additions & 6 deletions include/asm-x86_64/unwind.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,10 @@ extern int arch_unwind_init_running(struct unwind_frame_info *,

static inline int arch_unw_user_mode(const struct unwind_frame_info *info)
{
#if 0 /* This can only work when selector register saves/restores
are properly annotated (and tracked in UNW_REGISTER_INFO). */
return user_mode(&info->regs);
#else
return (long)info->regs.rip >= 0
return user_mode(&info->regs)
|| (long)info->regs.rip >= 0
|| (info->regs.rip >= VSYSCALL_START && info->regs.rip < VSYSCALL_END)
|| (long)info->regs.rsp >= 0;
#endif
}

#else
Expand Down
16 changes: 15 additions & 1 deletion kernel/unwind.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ static const struct {

typedef unsigned long uleb128_t;
typedef signed long sleb128_t;
#define sleb128abs __builtin_labs

static struct unwind_table {
struct {
Expand Down Expand Up @@ -787,7 +788,7 @@ int unwind(struct unwind_frame_info *frame)
#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs])
const u32 *fde = NULL, *cie = NULL;
const u8 *ptr = NULL, *end = NULL;
unsigned long pc = UNW_PC(frame) - frame->call_frame;
unsigned long pc = UNW_PC(frame) - frame->call_frame, sp;
unsigned long startLoc = 0, endLoc = 0, cfa;
unsigned i;
signed ptrType = -1;
Expand Down Expand Up @@ -936,6 +937,9 @@ int unwind(struct unwind_frame_info *frame)
state.dataAlign = get_sleb128(&ptr, end);
if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end)
cie = NULL;
else if (UNW_PC(frame) % state.codeAlign
|| UNW_SP(frame) % sleb128abs(state.dataAlign))
return -EPERM;
else {
retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end);
/* skip augmentation */
Expand Down Expand Up @@ -968,6 +972,8 @@ int unwind(struct unwind_frame_info *frame)
#ifdef CONFIG_FRAME_POINTER
unsigned long top, bottom;

if ((UNW_SP(frame) | UNW_FP(frame)) % sizeof(unsigned long))
return -EPERM;
top = STACK_TOP(frame->task);
bottom = STACK_BOTTOM(frame->task);
# if FRAME_RETADDR_OFFSET < 0
Expand Down Expand Up @@ -1018,6 +1024,7 @@ int unwind(struct unwind_frame_info *frame)
|| state.regs[retAddrReg].where == Nowhere
|| state.cfa.reg >= ARRAY_SIZE(reg_info)
|| reg_info[state.cfa.reg].width != sizeof(unsigned long)
|| FRAME_REG(state.cfa.reg, unsigned long) % sizeof(unsigned long)
|| state.cfa.offs % sizeof(unsigned long))
return -EIO;
/* update frame */
Expand All @@ -1038,6 +1045,8 @@ int unwind(struct unwind_frame_info *frame)
#else
# define CASES CASE(8); CASE(16); CASE(32); CASE(64)
#endif
pc = UNW_PC(frame);
sp = UNW_SP(frame);
for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
if (REG_INVALID(i)) {
if (state.regs[i].where == Nowhere)
Expand Down Expand Up @@ -1118,6 +1127,11 @@ int unwind(struct unwind_frame_info *frame)
}
}

if (UNW_PC(frame) % state.codeAlign
|| UNW_SP(frame) % sleb128abs(state.dataAlign)
|| (pc == UNW_PC(frame) && sp == UNW_SP(frame)))
return -EIO;

return 0;
#undef CASES
#undef FRAME_REG
Expand Down

0 comments on commit 359ad0d

Please sign in to comment.