summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArun Sharma <aruns@google.com>2009-03-16 11:06:26 -0700
committerArun Sharma <aruns@google.com>2009-03-16 21:34:49 -0700
commitff0ae70cc3c3c1498724deed519cd18dde6c1e80 (patch)
treef953cdc360269e9239e0a201a0081ca974b08b57
parent5822fca27a6861e07c3f74c3abd90cb020304d01 (diff)
downloadlibunwind-ff0ae70cc3c3c1498724deed519cd18dde6c1e80.tar.gz
Bad pointer validation for 32 bit x86.
This corresponds to commit 649f1fb3449a65dd0626a709432d8b02a7c56bbc. Signed-off-by: Paul Pluzhnikov <ppluzhnikov@google.com>
-rw-r--r--include/tdep-x86/libunwind_i.h21
-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
6 files changed, 80 insertions, 10 deletions
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))