mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2024-12-23 03:53:43 +01:00
Address x86_64 crashes when using sigaltstack
The crashes were tracked down to f->rpb_cfa_offset being incorrect. The problem is that {rsp,rbp}_cfa_offset only have 15 bits, but for SIGRETURN frame they are filled with: // src/x86_64/Gstash_frame.c f->cfa_reg_offset = d->cfa - c->sigcontext_addr; f->rbp_cfa_offset = DWARF_GET_LOC(d->loc[RBP]) - d->cfa; f->rsp_cfa_offset = DWARF_GET_LOC(d->loc[RSP]) - d->cfa; The problem is that the delta here can be arbitrarily large when sigaltstack is used, and can easily overflow the 15 and 30-bit fields. When signal handler starts running, the stack layout is: ... higher addresses ... ucontext CFA-> __restore_rt (== pretcode in rt_sigframe from linux-2.6/arch/x86/include/asm/sigframe.h) SP -> ... sighandler runs on this stack. ... lower addresses ... This makes it very convenient to find ucontext from the CFA. Attached patch re-tested on Linux/x86_64, no new failures. Signed-off-by: Paul Pluzhnikov <ppluzhnikov@google.com> Reviwed-by: Lassi Tuura <lat@cern.ch>
This commit is contained in:
parent
297d9cd07d
commit
1010880548
3 changed files with 19 additions and 28 deletions
|
@ -64,10 +64,9 @@ tdep_reuse_frame (struct dwarf_cursor *dw, struct dwarf_reg_state *rs)
|
|||
c->sigcontext_format = rs->signal_frame;
|
||||
if (c->sigcontext_format == X86_64_SCF_LINUX_RT_SIGFRAME)
|
||||
{
|
||||
/* Rest will be filled by tdep_stash_frame(), save what it needs. */
|
||||
c->frame_info.frame_type = UNW_X86_64_FRAME_SIGRETURN;
|
||||
c->frame_info.cfa_reg_offset = -1;
|
||||
c->frame_info.cfa_reg_rsp = -1;
|
||||
/* Offset from cfa to ucontext_t in signal frame. */
|
||||
c->frame_info.cfa_reg_offset = 0;
|
||||
c->sigcontext_addr = dw->cfa;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -74,19 +74,20 @@ tdep_stash_frame (struct dwarf_cursor *d, struct dwarf_reg_state *rs)
|
|||
Debug (4, " standard frame\n");
|
||||
}
|
||||
|
||||
/* Signal frame was detected via augmentation in tdep_fetch_frame()
|
||||
and partially filled in tdep_reuse_frame(). Now that we have
|
||||
the delta between inner and outer CFAs available to use, fill in
|
||||
the offsets for CFA and stored registers. We don't have space
|
||||
for RIP, it's location is calculated relative to RBP location. */
|
||||
/* Signal frame was detected via augmentation in tdep_fetch_frame() */
|
||||
else if (f->frame_type == UNW_X86_64_FRAME_SIGRETURN)
|
||||
{
|
||||
assert (f->cfa_reg_offset == -1);
|
||||
f->cfa_reg_offset = d->cfa - c->sigcontext_addr;
|
||||
f->rbp_cfa_offset = DWARF_GET_LOC(d->loc[RBP]) - d->cfa;
|
||||
f->rsp_cfa_offset = DWARF_GET_LOC(d->loc[RSP]) - d->cfa;
|
||||
Debug (4, " sigreturn frame rbpoff %d rspoff %d\n",
|
||||
f->rbp_cfa_offset, f->rsp_cfa_offset);
|
||||
/* Later we are going to fish out {RBP,RSP,RIP} from sigcontext via
|
||||
their ucontext_t offsets. Confirm DWARF info agrees with the
|
||||
offsets we expect. */
|
||||
|
||||
const unw_word_t uc = c->sigcontext_addr;
|
||||
|
||||
assert (DWARF_GET_LOC(d->loc[RIP]) - uc == UC_MCONTEXT_GREGS_RIP);
|
||||
assert (DWARF_GET_LOC(d->loc[RBP]) - uc == UC_MCONTEXT_GREGS_RBP);
|
||||
assert (DWARF_GET_LOC(d->loc[RSP]) - uc == UC_MCONTEXT_GREGS_RSP);
|
||||
|
||||
Debug (4, " sigreturn frame\n");
|
||||
}
|
||||
|
||||
/* PLT and guessed RBP-walked frames are handled in unw_step(). */
|
||||
|
|
|
@ -35,12 +35,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|||
/* Initial hash table size. Table expands by 2 bits (times four). */
|
||||
#define HASH_MIN_BITS 14
|
||||
|
||||
/* There's not enough space to store RIP's location in a signal
|
||||
frame, but we can calculate it relative to RBP's (or RSP's)
|
||||
position in mcontext structure. Note we don't want to use
|
||||
the UC_MCONTEXT_GREGS_* directly since we rely on DWARF info. */
|
||||
#define dRIP (UC_MCONTEXT_GREGS_RIP - UC_MCONTEXT_GREGS_RBP)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unw_tdep_frame_t *frames;
|
||||
|
@ -496,16 +490,13 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size)
|
|||
break;
|
||||
|
||||
case UNW_X86_64_FRAME_SIGRETURN:
|
||||
/* Advance standard signal frame, whose CFA points above saved
|
||||
registers (ucontext) among other things. We know the info
|
||||
is stored at some unknown constant offset off inner frame's
|
||||
CFA. We determine the actual offset from DWARF unwind info. */
|
||||
cfa = cfa + f->cfa_reg_offset;
|
||||
ACCESS_MEM_FAST(ret, c->validate, d, cfa + f->rbp_cfa_offset + dRIP, rip);
|
||||
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 (likely(ret >= 0))
|
||||
ACCESS_MEM_FAST(ret, c->validate, d, cfa + f->rbp_cfa_offset, rbp);
|
||||
ACCESS_MEM_FAST(ret, c->validate, d, cfa + UC_MCONTEXT_GREGS_RBP, rbp);
|
||||
if (likely(ret >= 0))
|
||||
ACCESS_MEM_FAST(ret, c->validate, d, cfa + f->rsp_cfa_offset, rsp);
|
||||
ACCESS_MEM_FAST(ret, c->validate, d, cfa + UC_MCONTEXT_GREGS_RSP, rsp);
|
||||
|
||||
/* Resume stack at signal restoration point. The stack is not
|
||||
necessarily continuous here, especially with sigaltstack(). */
|
||||
|
|
Loading…
Reference in a new issue