diff options
author | Elia Geretto <elia.f.geretto@gmail.com> | 2021-02-11 11:27:33 +0100 |
---|---|---|
committer | Dave Watson <dade.watson@gmail.com> | 2021-03-15 10:42:44 -0700 |
commit | debb6128d17b782552d53efa8869a392d1f40a83 (patch) | |
tree | 8f5a8f1c561aed863f7d4bd59e3b684e195b5f02 /src | |
parent | d16908ca9cf7b793cae566eb0f2e5d6c6df5820a (diff) | |
download | libunwind-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.c | 8 | ||||
-rw-r--r-- | src/x86_64/Ginit_local.c | 5 | ||||
-rw-r--r-- | src/x86_64/Ginit_remote.c | 5 | ||||
-rw-r--r-- | src/x86_64/Gos-linux.c | 3 | ||||
-rw-r--r-- | src/x86_64/Gresume.c | 3 | ||||
-rw-r--r-- | src/x86_64/Gstep.c | 16 | ||||
-rw-r--r-- | src/x86_64/Gtrace.c | 27 | ||||
-rw-r--r-- | src/x86_64/init.h | 4 |
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) \ |