x86_64: Use sigprocmask from signal frames

Currently setcontext for x86_64 restores the signal mask, even
though it is never saved anywhere.  This means the signal mask
is often garbage after an unw_resume.

(changed in commit f8a15e9679)

It looks like this was a fix for the Gtest-resume-sig function -
testing if signal masks are restored across signal frames.  The
root issue looks like that x86_64 only uses sigreturn for the exact
signal frame, and not for any decedant frames as well (as i64 does).

Instead, modify Gresume to use sigreturn if *any* frame on the stack
is a signal frame, so that we correct fixup the signal mask and any
sigaltstacks.  The sigreturn os-specific functions are changed slightly
to copy in the saved ucontext structure if we are jumping farther
up the stack.

This should fix sigprocmask reported issues such as
https://github.com/dropbox/pyston/blob/master/libunwind_patches/0002-pyston-stop-x86_64-setcontext-restoring-uninitialize.patch

Tests pass on freebsd, linux
This commit is contained in:
Dave Watson 2016-11-30 11:40:20 -08:00
parent b707e13ca0
commit 29483327be
5 changed files with 26 additions and 38 deletions

View File

@ -192,6 +192,24 @@ x86_64_sigreturn (unw_cursor_t *cursor)
ucontext_t *uc = (ucontext_t *)(c->sigcontext_addr +
offsetof(struct sigframe, sf_uc));
uc->uc_mcontext.mc_r8 = c->uc->uc_mcontext.mc_r8;
uc->uc_mcontext.mc_r9 = c->uc->uc_mcontext.mc_r9;
uc->uc_mcontext.mc_r10 = c->uc->uc_mcontext.mc_r10;
uc->uc_mcontext.mc_r11 = c->uc->uc_mcontext.mc_r11;
uc->uc_mcontext.mc_r12 = c->uc->uc_mcontext.mc_r12;
uc->uc_mcontext.mc_r13 = c->uc->uc_mcontext.mc_r13;
uc->uc_mcontext.mc_r14 = c->uc->uc_mcontext.mc_r14;
uc->uc_mcontext.mc_r15 = c->uc->uc_mcontext.mc_r15;
uc->uc_mcontext.mc_rdi = c->uc->uc_mcontext.mc_rdi;
uc->uc_mcontext.mc_rsi = c->uc->uc_mcontext.mc_rsi;
uc->uc_mcontext.mc_rbp = c->uc->uc_mcontext.mc_rbp;
uc->uc_mcontext.mc_rbx = c->uc->uc_mcontext.mc_rbx;
uc->uc_mcontext.mc_rdx = c->uc->uc_mcontext.mc_rdx;
uc->uc_mcontext.mc_rax = c->uc->uc_mcontext.mc_rax;
uc->uc_mcontext.mc_rcx = c->uc->uc_mcontext.mc_rcx;
uc->uc_mcontext.mc_rsp = c->uc->uc_mcontext.mc_rsp;
uc->uc_mcontext.mc_rip = c->uc->uc_mcontext.mc_rip;
Debug (8, "resuming at ip=%llx via sigreturn(%p)\n",
(unsigned long long) c->dwarf.ip, uc);
sigreturn(uc);

View File

@ -69,8 +69,6 @@ tdep_reuse_frame (struct dwarf_cursor *dw, struct dwarf_reg_state *rs)
c->frame_info.cfa_reg_offset = 0;
c->sigcontext_addr = dw->cfa;
}
else
c->sigcontext_addr = 0;
Debug(5, "reuse frame ip=0x%lx cfa=0x%lx format=%d addr=0x%lx offset=%+d\n",
dw->ip, dw->cfa, c->sigcontext_format, c->sigcontext_addr,
@ -140,6 +138,10 @@ x86_64_sigreturn (unw_cursor_t *cursor)
{
struct cursor *c = (struct cursor *) cursor;
struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr;
struct mcontext_t *sc_mcontext = &((struct ucontext*)sc)->uc_mcontext;
/* Copy in saved uc - all preserved regs are at the start of sigcontext */
memcpy(sc_mcontext, &c->uc->uc_mcontext,
DWARF_NUM_PRESERVED_REGS * sizeof(unw_word_t));
Debug (8, "resuming at ip=%llx via sigreturn(%p)\n",
(unsigned long long) c->dwarf.ip, sc);

View File

@ -44,7 +44,7 @@ x86_64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
at least. */
dwarf_make_proc_info (&c->dwarf);
if (unlikely (c->sigcontext_format != X86_64_SCF_NONE))
if (unlikely (c->sigcontext_addr != X86_64_SCF_NONE))
{
x86_64_sigreturn(cursor);
abort();

View File

@ -25,19 +25,12 @@ 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 "ucontext_i.h"
#if defined __linux__
#include <asm/unistd.h>
#define SIG_SETMASK 2
#define SIGSET_BYTE_SIZE (64/8)
#elif defined __FreeBSD__
#include <sys/syscall.h>
#endif
/* int _Ux86_64_setcontext (const ucontext_t *ucp)
Restores the machine context provided.
Unlike the libc implementation, doesn't clobber %rax
*/
.global _Ux86_64_setcontext
.type _Ux86_64_setcontext, @function
@ -45,32 +38,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
_Ux86_64_setcontext:
#if defined __linux__
/* restore signal mask
sigprocmask(SIG_SETMASK, ucp->uc_sigmask, NULL, sizeof(sigset_t)) */
push %rdi
mov $__NR_rt_sigprocmask, %rax
lea UC_SIGMASK(%rdi), %rsi
mov $SIG_SETMASK, %rdi
xor %rdx, %rdx
mov $SIGSET_BYTE_SIZE, %r10
syscall
pop %rdi
/* restore fp state */
mov UC_MCONTEXT_FPREGS_PTR(%rdi),%r8
fldenv (%r8)
ldmxcsr FPREGS_OFFSET_MXCSR(%r8)
#elif defined __FreeBSD__
/* restore signal mask */
pushq %rdi
xorl %edx,%edx
leaq UC_SIGMASK(%rdi),%rsi
movl $3,%edi/* SIG_SETMASK */
movl $SYS_sigprocmask,%eax
movq %rcx,%r10
syscall
popq %rdi
/* restore fp state */
cmpq $UC_MCONTEXT_FPOWNED_FPU,UC_MCONTEXT_OWNEDFP(%rdi)
jne 1f

View File

@ -74,7 +74,7 @@ handler (int sig)
#endif
{
unw_word_t ip;
sigset_t mask, oldmask;
sigset_t mask;
unw_context_t uc;
unw_cursor_t c;
char foo;
@ -95,17 +95,13 @@ handler (int sig)
sigemptyset (&mask);
sigaddset (&mask, SIGUSR2);
sigprocmask (SIG_BLOCK, &mask, &oldmask);
sigprocmask (SIG_BLOCK, &mask, NULL);
kill (getpid (), SIGUSR2); /* pend SIGUSR2 */
signal (SIGUSR1, SIG_IGN);
if ((ret = unw_getcontext (&uc)) < 0)
panic ("unw_getcontext() failed: ret=%d\n", ret);
#if UNW_TARGET_X86_64
/* unw_getcontext() doesn't save signal mask to avoid a syscall */
uc.uc_sigmask = oldmask;
#endif
if ((ret = unw_init_local (&c, &uc)) < 0)
panic ("unw_init_local() failed: ret=%d\n", ret);