From 29483327bebaf6e0141a9bee8bb99552a63f1583 Mon Sep 17 00:00:00 2001 From: Dave Watson Date: Wed, 30 Nov 2016 11:40:20 -0800 Subject: [PATCH] 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 f8a15e9679e59872ca2) 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 --- src/x86_64/Gos-freebsd.c | 18 ++++++++++++++++++ src/x86_64/Gos-linux.c | 6 ++++-- src/x86_64/Gresume.c | 2 +- src/x86_64/setcontext.S | 30 +----------------------------- tests/Gtest-resume-sig.c | 8 ++------ 5 files changed, 26 insertions(+), 38 deletions(-) diff --git a/src/x86_64/Gos-freebsd.c b/src/x86_64/Gos-freebsd.c index e7e8da13..8701aefb 100644 --- a/src/x86_64/Gos-freebsd.c +++ b/src/x86_64/Gos-freebsd.c @@ -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); diff --git a/src/x86_64/Gos-linux.c b/src/x86_64/Gos-linux.c index 9e1acfcf..d1e98ea5 100644 --- a/src/x86_64/Gos-linux.c +++ b/src/x86_64/Gos-linux.c @@ -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); diff --git a/src/x86_64/Gresume.c b/src/x86_64/Gresume.c index b880b01c..9876a549 100644 --- a/src/x86_64/Gresume.c +++ b/src/x86_64/Gresume.c @@ -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(); diff --git a/src/x86_64/setcontext.S b/src/x86_64/setcontext.S index 1af8b673..358217de 100644 --- a/src/x86_64/setcontext.S +++ b/src/x86_64/setcontext.S @@ -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 -#define SIG_SETMASK 2 -#define SIGSET_BYTE_SIZE (64/8) -#elif defined __FreeBSD__ -#include -#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 diff --git a/tests/Gtest-resume-sig.c b/tests/Gtest-resume-sig.c index 147ecd03..846bba9f 100644 --- a/tests/Gtest-resume-sig.c +++ b/tests/Gtest-resume-sig.c @@ -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);