summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArun Sharma <arun.sharma@google.com>2008-06-16 14:35:53 -0600
committerDavid Mosberger-Tang <davidm@koala.mostang.com>2008-06-16 14:35:53 -0600
commit649f1fb3449a65dd0626a709432d8b02a7c56bbc (patch)
tree61b412e9938a8ebfa870a4cf202e1697cff22383
parent183d28a0668f100a181cc665ea702985d2ee717e (diff)
downloadlibunwind-649f1fb3449a65dd0626a709432d8b02a7c56bbc.tar.gz
[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>
-rw-r--r--include/tdep-x86_64/libunwind_i.h14
-rw-r--r--src/x86_64/Ginit.c8
-rw-r--r--src/x86_64/Ginit_local.c4
-rw-r--r--src/x86_64/Ginit_remote.c11
-rw-r--r--src/x86_64/Gis_signal_frame.c1
-rw-r--r--src/x86_64/Gresume.c2
-rw-r--r--src/x86_64/Gstep.c4
7 files changed, 33 insertions, 11 deletions
diff --git a/include/tdep-x86_64/libunwind_i.h b/include/tdep-x86_64/libunwind_i.h
index ad4987b8..a949a4cb 100644
--- a/include/tdep-x86_64/libunwind_i.h
+++ b/include/tdep-x86_64/libunwind_i.h
@@ -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)
diff --git a/src/x86_64/Ginit.c b/src/x86_64/Ginit.c
index 026e8d25..031deaa2 100644
--- a/src/x86_64/Ginit.c
+++ b/src/x86_64/Ginit.c
@@ -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;
}
diff --git a/src/x86_64/Ginit_local.c b/src/x86_64/Ginit_local.c
index 9bb25137..1e80cb85 100644
--- a/src/x86_64/Ginit_local.c
+++ b/src/x86_64/Ginit_local.c
@@ -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);
}
diff --git a/src/x86_64/Ginit_remote.c b/src/x86_64/Ginit_remote.c
index 9b2fd233..8aa644d3 100644
--- a/src/x86_64/Ginit_remote.c
+++ b/src/x86_64/Ginit_remote.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 */
}
diff --git a/src/x86_64/Gis_signal_frame.c b/src/x86_64/Gis_signal_frame.c
index b795827c..72edf46e 100644
--- a/src/x86_64/Gis_signal_frame.c
+++ b/src/x86_64/Gis_signal_frame.c
@@ -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.
diff --git a/src/x86_64/Gresume.c b/src/x86_64/Gresume.c
index 4edc4da6..b4000401 100644
--- a/src/x86_64/Gresume.c
+++ b/src/x86_64/Gresume.c
@@ -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
diff --git a/src/x86_64/Gstep.c b/src/x86_64/Gstep.c
index 75f796f5..2da1c25d 100644
--- a/src/x86_64/Gstep.c
+++ b/src/x86_64/Gstep.c
@@ -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))