summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorElia Geretto <elia.f.geretto@gmail.com>2021-02-11 11:27:33 +0100
committerDave Watson <dade.watson@gmail.com>2021-03-15 10:42:44 -0700
commitdebb6128d17b782552d53efa8869a392d1f40a83 (patch)
tree8f5a8f1c561aed863f7d4bd59e3b684e195b5f02 /src
parentd16908ca9cf7b793cae566eb0f2e5d6c6df5820a (diff)
downloadlibunwind-debb6128d17b782552d53efa8869a392d1f40a83.tar.gz
[x86_64] Remove self-reference from local cursor
When creating a local x86_64 cursor, its `dwarf.as_arg` member is set to point to the cursor itself, making it a self-referential struct. This does not allow to safely keep a copy of a cursor, as stated in the documentation. In addition, the self-reference is used to access just two members: `uc` and `validate`. This commit modifies `dwarf.as_arg` to pack together both `uc` and `validate`, so the x86_64 cursor is not self-referential anymore and no additional memory allocation is performed. Since `uc` points to a `ucontext_t`, which is at least 2-byte aligned, it is safe to store the `validate` bit in the LSB of the `uc` pointer. Additional checks were added to verify that the `validate` bit is not set when the cursor is non-local.
Diffstat (limited to 'src')
-rw-r--r--src/x86_64/Ginit.c8
-rw-r--r--src/x86_64/Ginit_local.c5
-rw-r--r--src/x86_64/Ginit_remote.c5
-rw-r--r--src/x86_64/Gos-linux.c3
-rw-r--r--src/x86_64/Gresume.c3
-rw-r--r--src/x86_64/Gstep.c16
-rw-r--r--src/x86_64/Gtrace.c27
-rw-r--r--src/x86_64/init.h4
8 files changed, 44 insertions, 27 deletions
diff --git a/src/x86_64/Ginit.c b/src/x86_64/Ginit.c
index 785d2e4a..0b121bc9 100644
--- a/src/x86_64/Ginit.c
+++ b/src/x86_64/Ginit.c
@@ -26,6 +26,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+#include "libunwind_i.h"
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -321,8 +322,7 @@ 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 (likely (c != NULL) && unlikely (c->validate)
+ if (unlikely (AS_ARG_GET_VALIDATE(arg))
&& unlikely (validate_mem (addr))) {
Debug (16, "mem[%016lx] -> invalid\n", addr);
return -1;
@@ -338,7 +338,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 = ((struct cursor *)arg)->uc;
+ ucontext_t *uc = AS_ARG_GET_UC_PTR(arg);
if (unw_is_fpreg (reg))
goto badreg;
@@ -367,7 +367,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 = ((struct cursor *)arg)->uc;
+ ucontext_t *uc = AS_ARG_GET_UC_PTR(arg);
unw_fpreg_t *addr;
if (!unw_is_fpreg (reg))
diff --git a/src/x86_64/Ginit_local.c b/src/x86_64/Ginit_local.c
index 06a7c497..bf771de3 100644
--- a/src/x86_64/Ginit_local.c
+++ b/src/x86_64/Ginit_local.c
@@ -25,6 +25,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+#include "libunwind_i.h"
#include "unwind_i.h"
#include "init.h"
@@ -49,9 +50,7 @@ unw_init_local_common (unw_cursor_t *cursor, ucontext_t *uc, unsigned use_prev_i
Debug (1, "(cursor=%p)\n", c);
c->dwarf.as = unw_local_addr_space;
- c->dwarf.as_arg = c;
- c->uc = uc;
- c->validate = 0;
+ c->dwarf.as_arg = dwarf_build_as_arg(uc, /*validate*/ 0);
return common_init (c, use_prev_instr);
}
diff --git a/src/x86_64/Ginit_remote.c b/src/x86_64/Ginit_remote.c
index 9ab1d6df..51761a71 100644
--- a/src/x86_64/Ginit_remote.c
+++ b/src/x86_64/Ginit_remote.c
@@ -26,6 +26,7 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#include "init.h"
+#include "libunwind_i.h"
#include "unwind_i.h"
int
@@ -44,13 +45,11 @@ unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg)
c->dwarf.as = as;
if (as == unw_local_addr_space)
{
- c->dwarf.as_arg = c;
- c->uc = as_arg;
+ c->dwarf.as_arg = dwarf_build_as_arg(as_arg, /*validate*/ 0);
}
else
{
c->dwarf.as_arg = as_arg;
- c->uc = NULL;
}
return common_init (c, 0);
#endif /* !UNW_LOCAL_ONLY */
diff --git a/src/x86_64/Gos-linux.c b/src/x86_64/Gos-linux.c
index bd142345..12755b63 100644
--- a/src/x86_64/Gos-linux.c
+++ b/src/x86_64/Gos-linux.c
@@ -25,6 +25,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+#include "libunwind_i.h"
#include "unwind_i.h"
#include "ucontext_i.h"
@@ -140,7 +141,7 @@ x86_64_sigreturn (unw_cursor_t *cursor)
struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr;
mcontext_t *sc_mcontext = &((ucontext_t*)sc)->uc_mcontext;
/* Copy in saved uc - all preserved regs are at the start of sigcontext */
- memcpy(sc_mcontext, &c->uc->uc_mcontext,
+ memcpy(sc_mcontext, &dwarf_get_uc(&c->dwarf)->uc_mcontext,
DWARF_NUM_PRESERVED_REGS * sizeof(unw_word_t));
Debug (8, "resuming at ip=%llx via sigreturn(%p)\n",
diff --git a/src/x86_64/Gresume.c b/src/x86_64/Gresume.c
index 944cdaae..becb1bd6 100644
--- a/src/x86_64/Gresume.c
+++ b/src/x86_64/Gresume.c
@@ -27,6 +27,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#include <stdlib.h>
+#include "libunwind_i.h"
#include "offsets.h"
#include "unwind_i.h"
@@ -36,7 +37,7 @@ HIDDEN inline int
x86_64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
{
struct cursor *c = (struct cursor *) cursor;
- ucontext_t *uc = c->uc;
+ ucontext_t *uc = dwarf_get_uc(&c->dwarf);
/* 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 d4831197..3c5c3830 100644
--- a/src/x86_64/Gstep.c
+++ b/src/x86_64/Gstep.c
@@ -25,6 +25,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+#include "libunwind_i.h"
#include "unwind_i.h"
#include <signal.h>
@@ -59,8 +60,11 @@ unw_step (unw_cursor_t *cursor)
int ret, i;
#if CONSERVATIVE_CHECKS
- int val = c->validate;
- c->validate = 1;
+ int val = 0;
+ if (c->dwarf.as == unw_local_addr_space) {
+ val = dwarf_get_validate(&c->dwarf);
+ dwarf_set_validate(&c->dwarf, 1);
+ }
#endif
Debug (1, "(cursor=%p, ip=0x%016lx, cfa=0x%016lx)\n",
@@ -71,7 +75,9 @@ unw_step (unw_cursor_t *cursor)
ret = dwarf_step (&c->dwarf);
#if CONSERVATIVE_CHECKS
- c->validate = val;
+ if (c->dwarf.as == unw_local_addr_space) {
+ dwarf_set_validate(&c->dwarf, val);
+ }
#endif
if (ret < 0 && ret != -UNW_ENOINFO)
@@ -110,7 +116,9 @@ unw_step (unw_cursor_t *cursor)
/* We could get here because of missing/bad unwind information.
Validate all addresses before dereferencing. */
- c->validate = 1;
+ if (c->dwarf.as == unw_local_addr_space) {
+ dwarf_set_validate(&c->dwarf, 1);
+ }
Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret);
diff --git a/src/x86_64/Gtrace.c b/src/x86_64/Gtrace.c
index 40be17eb..fa43c6a5 100644
--- a/src/x86_64/Gtrace.c
+++ b/src/x86_64/Gtrace.c
@@ -22,6 +22,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+#include "libunwind_i.h"
#include "unwind_i.h"
#include "ucontext_i.h"
#include <signal.h>
@@ -403,6 +404,7 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size)
int maxdepth = 0;
int depth = 0;
int ret;
+ int validate = 0;
/* Check input parametres. */
if (unlikely(! cursor || ! buffer || ! size || (maxdepth = *size) <= 0))
@@ -473,14 +475,17 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size)
{
case UNW_X86_64_FRAME_GUESSED:
/* Fall thru to standard processing after forcing validation. */
- c->validate = 1;
+ if (d->as == unw_local_addr_space)
+ dwarf_set_validate(d, 1);
case UNW_X86_64_FRAME_STANDARD:
/* Advance standard traceable frame. */
cfa = (f->cfa_reg_rsp ? rsp : rbp) + f->cfa_reg_offset;
- ACCESS_MEM_FAST(ret, c->validate, d, cfa - 8, rip);
+ if (d->as == unw_local_addr_space)
+ validate = dwarf_get_validate(d);
+ ACCESS_MEM_FAST(ret, validate, d, cfa - 8, rip);
if (likely(ret >= 0) && likely(f->rbp_cfa_offset != -1))
- ACCESS_MEM_FAST(ret, c->validate, d, cfa + f->rbp_cfa_offset, rbp);
+ ACCESS_MEM_FAST(ret, validate, d, cfa + f->rbp_cfa_offset, rbp);
/* Don't bother reading RSP from DWARF, CFA becomes new RSP. */
rsp = cfa;
@@ -492,11 +497,13 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size)
case UNW_X86_64_FRAME_SIGRETURN:
cfa = cfa + f->cfa_reg_offset; /* cfa now points to ucontext_t. */
- ACCESS_MEM_FAST(ret, c->validate, d, cfa + UC_MCONTEXT_GREGS_RIP, rip);
+ if (d->as == unw_local_addr_space)
+ validate = dwarf_get_validate(d);
+ ACCESS_MEM_FAST(ret, validate, d, cfa + UC_MCONTEXT_GREGS_RIP, rip);
if (likely(ret >= 0))
- ACCESS_MEM_FAST(ret, c->validate, d, cfa + UC_MCONTEXT_GREGS_RBP, rbp);
+ ACCESS_MEM_FAST(ret, validate, d, cfa + UC_MCONTEXT_GREGS_RBP, rbp);
if (likely(ret >= 0))
- ACCESS_MEM_FAST(ret, c->validate, d, cfa + UC_MCONTEXT_GREGS_RSP, rsp);
+ ACCESS_MEM_FAST(ret, validate, d, cfa + UC_MCONTEXT_GREGS_RSP, rsp);
/* Resume stack at signal restoration point. The stack is not
necessarily continuous here, especially with sigaltstack(). */
@@ -510,11 +517,13 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size)
/* Address of RIP was pushed on the stack via a simple
* def_cfa_expr - result stack offset stored in cfa_reg_offset */
cfa = (f->cfa_reg_rsp ? rsp : rbp) + f->cfa_reg_offset;
- ACCESS_MEM_FAST(ret, c->validate, d, cfa, cfa);
+ if (d->as == unw_local_addr_space)
+ validate = dwarf_get_validate(d);
+ ACCESS_MEM_FAST(ret, validate, d, cfa, cfa);
if (likely(ret >= 0))
- ACCESS_MEM_FAST(ret, c->validate, d, cfa - 8, rip);
+ ACCESS_MEM_FAST(ret, validate, d, cfa - 8, rip);
if (likely(ret >= 0))
- ACCESS_MEM_FAST(ret, c->validate, d, rbp, rbp);
+ ACCESS_MEM_FAST(ret, validate, d, rbp, rbp);
/* Don't bother reading RSP from DWARF, CFA becomes new RSP. */
rsp = cfa;
diff --git a/src/x86_64/init.h b/src/x86_64/init.h
index 09a12b55..f4b7cf5d 100644
--- a/src/x86_64/init.h
+++ b/src/x86_64/init.h
@@ -30,11 +30,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/* Avoid a trip to x86_64_r_uc_addr() for purely local initialisation. */
#if defined UNW_LOCAL_ONLY && defined __linux__
# define REG_INIT_LOC(c, rlc, ruc) \
- DWARF_LOC ((unw_word_t) &c->uc->uc_mcontext.gregs[REG_ ## ruc], 0)
+ DWARF_LOC ((unw_word_t) &dwarf_get_uc(&c->dwarf)->uc_mcontext.gregs[REG_ ## ruc], 0)
#elif defined UNW_LOCAL_ONLY && defined __FreeBSD__
# define REG_INIT_LOC(c, rlc, ruc) \
- DWARF_LOC ((unw_word_t) &c->uc->uc_mcontext.mc_ ## rlc, 0)
+ DWARF_LOC ((unw_word_t) &dwarf_get_uc(&c->dwarf)->uc_mcontext.mc_ ## rlc, 0)
#else
# define REG_INIT_LOC(c, rlc, ruc) \