mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2024-12-23 03:53:43 +01:00
Bad pointer validation for 32 bit x86.
This corresponds to commit 649f1fb344
.
Signed-off-by: Paul Pluzhnikov <ppluzhnikov@google.com>
This commit is contained in:
parent
5822fca27a
commit
ff0ae70cc3
6 changed files with 80 additions and 10 deletions
|
@ -65,8 +65,17 @@ struct cursor
|
|||
}
|
||||
sigcontext_format;
|
||||
unw_word_t sigcontext_addr;
|
||||
int validate;
|
||||
ucontext_t *uc;
|
||||
};
|
||||
|
||||
static inline ucontext_t *
|
||||
dwarf_get_uc(const struct dwarf_cursor *cursor)
|
||||
{
|
||||
const struct cursor *c = (struct cursor *) cursor->as_arg;
|
||||
return c->uc;
|
||||
}
|
||||
|
||||
#define DWARF_GET_LOC(l) ((l).val)
|
||||
|
||||
#ifdef UNW_LOCAL_ONLY
|
||||
|
@ -75,10 +84,10 @@ struct cursor
|
|||
# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r) })
|
||||
# define DWARF_IS_REG_LOC(l) 0
|
||||
# define DWARF_REG_LOC(c,r) (DWARF_LOC((unw_word_t) \
|
||||
tdep_uc_addr((c)->as_arg, (r)), 0))
|
||||
tdep_uc_addr(dwarf_get_uc(c), (r)), 0))
|
||||
# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0)
|
||||
# define DWARF_FPREG_LOC(c,r) (DWARF_LOC((unw_word_t) \
|
||||
tdep_uc_addr((c)->as_arg, (r)), 0))
|
||||
tdep_uc_addr(dwarf_get_uc(c), (r)), 0))
|
||||
|
||||
static inline int
|
||||
dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val)
|
||||
|
@ -103,8 +112,8 @@ dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val)
|
|||
{
|
||||
if (!DWARF_GET_LOC (loc))
|
||||
return -1;
|
||||
*val = *(unw_word_t *) DWARF_GET_LOC (loc);
|
||||
return 0;
|
||||
return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), val,
|
||||
0, c->as_arg);
|
||||
}
|
||||
|
||||
static inline int
|
||||
|
@ -112,8 +121,8 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
|
|||
{
|
||||
if (!DWARF_GET_LOC (loc))
|
||||
return -1;
|
||||
*(unw_word_t *) DWARF_GET_LOC (loc) = val;
|
||||
return 0;
|
||||
return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), &val,
|
||||
1, c->as_arg);
|
||||
}
|
||||
|
||||
#else /* !UNW_LOCAL_ONLY */
|
||||
|
|
|
@ -102,6 +102,47 @@ get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
#define PAGE_START(a) ((a) & ~(PAGE_SIZE-1))
|
||||
|
||||
/* Cache of already validated addresses */
|
||||
#define NLGA 4
|
||||
static unw_word_t last_good_addr[NLGA];
|
||||
static int lga_victim;
|
||||
|
||||
static int
|
||||
validate_mem (unw_word_t addr)
|
||||
{
|
||||
int i, victim;
|
||||
|
||||
addr = PAGE_START(addr);
|
||||
|
||||
for (i = 0; i < NLGA; i++)
|
||||
{
|
||||
if (last_good_addr[i] && (addr == last_good_addr[i]))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (msync ((void *) addr, 1, MS_SYNC) == -1)
|
||||
return -1;
|
||||
|
||||
victim = lga_victim;
|
||||
for (i = 0; i < NLGA; i++) {
|
||||
if (!last_good_addr[victim]) {
|
||||
last_good_addr[victim++] = addr;
|
||||
return 0;
|
||||
}
|
||||
victim = (victim + 1) % NLGA;
|
||||
}
|
||||
|
||||
/* All slots full. Evict the victim. */
|
||||
last_good_addr[victim] = addr;
|
||||
victim = (victim + 1) % NLGA;
|
||||
lga_victim = victim;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
|
||||
void *arg)
|
||||
|
@ -113,6 +154,10 @@ access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
|
|||
}
|
||||
else
|
||||
{
|
||||
/* validate address */
|
||||
const struct cursor *c = (const struct cursor *)arg;
|
||||
if (c && c->validate && validate_mem(addr))
|
||||
return -1;
|
||||
*val = *(unw_word_t *) addr;
|
||||
Debug (16, "mem[%x] -> %x\n", addr, *val);
|
||||
}
|
||||
|
@ -124,7 +169,7 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
|
|||
void *arg)
|
||||
{
|
||||
unw_word_t *addr;
|
||||
ucontext_t *uc = arg;
|
||||
ucontext_t *uc = ((struct cursor *)arg)->uc;
|
||||
|
||||
if (unw_is_fpreg (reg))
|
||||
goto badreg;
|
||||
|
@ -153,7 +198,7 @@ static int
|
|||
access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
|
||||
int write, void *arg)
|
||||
{
|
||||
ucontext_t *uc = arg;
|
||||
ucontext_t *uc = ((struct cursor *)arg)->uc;
|
||||
unw_fpreg_t *addr;
|
||||
|
||||
if (!unw_is_fpreg (reg))
|
||||
|
|
|
@ -47,7 +47,9 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
|
|||
Debug (1, "(cursor=%p)\n", c);
|
||||
|
||||
c->dwarf.as = unw_local_addr_space;
|
||||
c->dwarf.as_arg = uc;
|
||||
c->dwarf.as_arg = c;
|
||||
c->uc = uc;
|
||||
c->validate = 0;
|
||||
return common_init (c);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,16 @@ unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg)
|
|||
|
||||
c->dwarf.as = as;
|
||||
c->dwarf.as_arg = as_arg;
|
||||
if (as == unw_local_addr_space)
|
||||
{
|
||||
c->dwarf.as_arg = c;
|
||||
c->uc = as_arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
c->dwarf.as_arg = as_arg;
|
||||
c->uc = 0;
|
||||
}
|
||||
return common_init (c);
|
||||
#endif /* !UNW_LOCAL_ONLY */
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
|
|||
{
|
||||
#if defined(__linux)
|
||||
struct cursor *c = (struct cursor *) cursor;
|
||||
ucontext_t *uc = c->dwarf.as_arg;
|
||||
ucontext_t *uc = c->uc;
|
||||
|
||||
/* Ensure c->pi is up-to-date. On x86, it's relatively common to be
|
||||
missing DWARF unwind info. We don't want to fail in that case,
|
||||
|
|
|
@ -49,6 +49,10 @@ unw_step (unw_cursor_t *cursor)
|
|||
or skip over the signal trampoline. */
|
||||
struct dwarf_loc ebp_loc, eip_loc;
|
||||
|
||||
/* We could get here because of missing/bad unwind information.
|
||||
Validate all addresses before dereferencing. */
|
||||
c->validate = 1;
|
||||
|
||||
Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret);
|
||||
|
||||
if (unw_is_signal_frame (cursor))
|
||||
|
|
Loading…
Reference in a new issue