1
0
Fork 0
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:
Arun Sharma 2011-11-27 18:34:38 -08:00
parent 297d9cd07d
commit 1010880548
3 changed files with 19 additions and 28 deletions

View file

@ -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

View file

@ -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(). */

View file

@ -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(). */