diff --git a/include/tdep-x86/libunwind_i.h b/include/tdep-x86/libunwind_i.h index 43c22f1e..e6ee149c 100644 --- a/include/tdep-x86/libunwind_i.h +++ b/include/tdep-x86/libunwind_i.h @@ -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 */ diff --git a/src/x86/Ginit.c b/src/x86/Ginit.c index abc9e613..e1b1dcfc 100644 --- a/src/x86/Ginit.c +++ b/src/x86/Ginit.c @@ -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)) diff --git a/src/x86/Ginit_local.c b/src/x86/Ginit_local.c index 7b86d6e5..55ab7490 100644 --- a/src/x86/Ginit_local.c +++ b/src/x86/Ginit_local.c @@ -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); } diff --git a/src/x86/Ginit_remote.c b/src/x86/Ginit_remote.c index 5d3827d9..6949a73e 100644 --- a/src/x86/Ginit_remote.c +++ b/src/x86/Ginit_remote.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 */ } diff --git a/src/x86/Gresume.c b/src/x86/Gresume.c index 6ea93468..cf914786 100644 --- a/src/x86/Gresume.c +++ b/src/x86/Gresume.c @@ -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, diff --git a/src/x86/Gstep.c b/src/x86/Gstep.c index e0e681d4..266f89f4 100644 --- a/src/x86/Gstep.c +++ b/src/x86/Gstep.c @@ -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))