diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/x86/Ginit.c | 49 | ||||
-rw-r--r-- | src/x86/Ginit_local.c | 4 | ||||
-rw-r--r-- | src/x86/Ginit_remote.c | 10 | ||||
-rw-r--r-- | src/x86/Gresume.c | 2 | ||||
-rw-r--r-- | src/x86/Gstep.c | 4 |
5 files changed, 65 insertions, 4 deletions
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)) |