mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2024-12-23 12:03:41 +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;
|
c->sigcontext_format = rs->signal_frame;
|
||||||
if (c->sigcontext_format == X86_64_SCF_LINUX_RT_SIGFRAME)
|
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.frame_type = UNW_X86_64_FRAME_SIGRETURN;
|
||||||
c->frame_info.cfa_reg_offset = -1;
|
/* Offset from cfa to ucontext_t in signal frame. */
|
||||||
c->frame_info.cfa_reg_rsp = -1;
|
c->frame_info.cfa_reg_offset = 0;
|
||||||
c->sigcontext_addr = dw->cfa;
|
c->sigcontext_addr = dw->cfa;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -74,19 +74,20 @@ tdep_stash_frame (struct dwarf_cursor *d, struct dwarf_reg_state *rs)
|
||||||
Debug (4, " standard frame\n");
|
Debug (4, " standard frame\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Signal frame was detected via augmentation in tdep_fetch_frame()
|
/* 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. */
|
|
||||||
else if (f->frame_type == UNW_X86_64_FRAME_SIGRETURN)
|
else if (f->frame_type == UNW_X86_64_FRAME_SIGRETURN)
|
||||||
{
|
{
|
||||||
assert (f->cfa_reg_offset == -1);
|
/* Later we are going to fish out {RBP,RSP,RIP} from sigcontext via
|
||||||
f->cfa_reg_offset = d->cfa - c->sigcontext_addr;
|
their ucontext_t offsets. Confirm DWARF info agrees with the
|
||||||
f->rbp_cfa_offset = DWARF_GET_LOC(d->loc[RBP]) - d->cfa;
|
offsets we expect. */
|
||||||
f->rsp_cfa_offset = DWARF_GET_LOC(d->loc[RSP]) - d->cfa;
|
|
||||||
Debug (4, " sigreturn frame rbpoff %d rspoff %d\n",
|
const unw_word_t uc = c->sigcontext_addr;
|
||||||
f->rbp_cfa_offset, f->rsp_cfa_offset);
|
|
||||||
|
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(). */
|
/* 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). */
|
/* Initial hash table size. Table expands by 2 bits (times four). */
|
||||||
#define HASH_MIN_BITS 14
|
#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
|
typedef struct
|
||||||
{
|
{
|
||||||
unw_tdep_frame_t *frames;
|
unw_tdep_frame_t *frames;
|
||||||
|
@ -496,16 +490,13 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UNW_X86_64_FRAME_SIGRETURN:
|
case UNW_X86_64_FRAME_SIGRETURN:
|
||||||
/* Advance standard signal frame, whose CFA points above saved
|
cfa = cfa + f->cfa_reg_offset; /* cfa now points to ucontext_t. */
|
||||||
registers (ucontext) among other things. We know the info
|
|
||||||
is stored at some unknown constant offset off inner frame's
|
ACCESS_MEM_FAST(ret, c->validate, d, cfa + UC_MCONTEXT_GREGS_RIP, rip);
|
||||||
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);
|
|
||||||
if (likely(ret >= 0))
|
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))
|
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
|
/* Resume stack at signal restoration point. The stack is not
|
||||||
necessarily continuous here, especially with sigaltstack(). */
|
necessarily continuous here, especially with sigaltstack(). */
|
||||||
|
|
Loading…
Reference in a new issue