summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/x86/Ginit.c49
-rw-r--r--src/x86/Ginit_local.c4
-rw-r--r--src/x86/Ginit_remote.c10
-rw-r--r--src/x86/Gresume.c2
-rw-r--r--src/x86/Gstep.c4
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))