mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2025-03-31 03:32:16 +02:00
[X86-64] For local unwinding, we have a defence mechanism against
bad/missing unwind information, which could result in libunwind dereferencing bad pointers. This mechanism is based on msync(2) system call and significantly reduces the chances of a bad pointer dereference in libunwind. The original idea was to turn this mechanism on only when necessary i.e. libunwind didn't find proper unwind information for a IP. There are a couple of problems in the current implementation. * The flag is global and is modified without locking * The flag isn't reset when starting a new unwind The attached patch makes ->validate a per-thread setting by moving it into struct cursor from unw_local_addr_space and resets it to false when starting a new unwind. As a result, cursor->as_arg points to the cursor itself instead of the ucontext (for the local case). This was found to reduce the number of msync() system calls from an application using libunwind significantly. Signed-off-by: Paul Pluzhnikov <ppluzhnikov@google.com> Signed-off-by: Arun Sharma <arun.sharma@google.com>
This commit is contained in:
parent
183d28a066
commit
649f1fb344
7 changed files with 33 additions and 11 deletions
include/tdep-x86_64
src/x86_64
|
@ -51,7 +51,6 @@ struct unw_addr_space
|
|||
unw_word_t dyn_info_list_addr; /* (cached) dyn_info_list_addr */
|
||||
struct dwarf_rs_cache global_cache;
|
||||
struct unw_debug_frame_list *debug_frames;
|
||||
int validate;
|
||||
};
|
||||
|
||||
struct cursor
|
||||
|
@ -67,8 +66,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
|
||||
|
@ -77,10 +85,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))
|
||||
#else /* !UNW_LOCAL_ONLY */
|
||||
|
||||
# define DWARF_LOC_TYPE_FP (1 << 0)
|
||||
|
|
|
@ -158,7 +158,8 @@ access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
|
|||
else
|
||||
{
|
||||
/* validate address */
|
||||
if (as->validate && validate_mem(addr))
|
||||
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[%016lx] -> %lx\n", addr, *val);
|
||||
|
@ -171,7 +172,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;
|
||||
|
@ -200,7 +201,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))
|
||||
|
@ -252,7 +253,6 @@ x86_64_local_addr_space_init (void)
|
|||
local_addr_space.acc.get_proc_name = get_static_proc_name;
|
||||
unw_flush_cache (&local_addr_space, 0, 0);
|
||||
|
||||
local_addr_space.validate = 0;
|
||||
memset (last_good_addr, 0, sizeof (unw_word_t) * NLGA);
|
||||
lga_victim = 0;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,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);
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,16 @@ unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg)
|
|||
Debug (1, "(cursor=%p)\n", c);
|
||||
|
||||
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 */
|
||||
}
|
||||
|
|
|
@ -40,7 +40,6 @@ unw_is_signal_frame (unw_cursor_t *cursor)
|
|||
|
||||
as = c->dwarf.as;
|
||||
a = unw_get_accessors (as);
|
||||
as->validate = 1; /* Don't trust the ip */
|
||||
arg = c->dwarf.as_arg;
|
||||
|
||||
/* Check if RIP points at sigreturn sequence.
|
||||
|
|
|
@ -51,7 +51,7 @@ x86_64_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-64, it's relatively common to
|
||||
be missing DWARF unwind info. We don't want to fail in that
|
||||
|
|
|
@ -71,6 +71,10 @@ unw_step (unw_cursor_t *cursor)
|
|||
unw_word_t prev_ip = c->dwarf.ip, prev_cfa = c->dwarf.cfa;
|
||||
struct dwarf_loc rbp_loc, rsp_loc, rip_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…
Add table
Reference in a new issue