I was looking at the crash reported at [1]. Processing the stackdump gives:

0x000000018006ff23    signal_exit                                
exceptions.cc:1234
0x00000001800713ea    _cygtls::call_signal_handler()             
exceptions.cc:1490
0x0000000180121d04    sig_send(_pinfo*, siginfo_t&, _cygtls*)    sigproc.cc:714
0x000000018011ee5e    _pinfo::kill(siginfo_t&)                   signal.cc:252
0x000000018011f32b    kill0                                      signal.cc:303
0x000000018011f4fc    raise                                      signal.cc:289
0x000000018011f7bf    abort                                      signal.cc:376
0x00000001801528c5    dlfree                                     malloc.cc:4762
0x00000001800c52b3    free                                       sync.h:36
0x000000018011ae5b    ??                                         sigfe.s:43

Which is not entirely helpful.

We can see that an abort signal is being raised inside free, but the stacktrace stops at _sigbe, because unwinding that frame requires special measures.

Presumably run has some invalid heap use going on, but annoyingly the stackdump stops short of identifying the problem line.

Attached is a patch which teaches stack_info::walk() how to unwind these frames on x86_64.

Also attached is a small testcase, which currently produces a similar stackdump truncated at _sigbe, or with my patch applied:

0x0000000180070238    signal_exit                                
exceptions.cc:1321
0x000000018007172a    _cygtls::call_signal_handler()             
exceptions.cc:1578
0x0000000180127e2f    sig_send(_pinfo*, siginfo_t&, _cygtls*)    sigproc.cc:714
0x0000000180124fb0    _pinfo::kill(siginfo_t&)                   signal.cc:252
0x0000000180125479    kill0                                      signal.cc:303
0x000000018012564c    raise                                      signal.cc:289
0x000000018012590f    abort                                      signal.cc:376
0x000000018015969a    dlfree                                     malloc.cc:4717
0x00000001800ca103    free                                       sync.h:36
0x0000000180120f5b    ?? (_sigbe)                                sigfe.s:54
0x0000000100401119    foo                                        
malloc-corruption-abort.c:11
0x000000010040113b    sighandler                                 
malloc-corruption-abort.c:17
0x000000018007172a    _cygtls::call_signal_handler()             
exceptions.cc:1578
0x0000000180121078    ?? (sigdelayed)                            sigfe.s:144
0x0000000100401174    main                                       
malloc-corruption-abort.c:32

The x86 unwinder could probably benefit from something similar, but it seems to be able to do a slightly better job of escaping from _sigbe, I guess due to the frame pointer.

[1] https://cygwin.com/ml/cygwin/2015-03/msg00117.html
#include <signal.h>
#include <stdlib.h>
#include <string.h>

static int foo()
{
  void *p = malloc(10);
  memset(p, 0xca, 256);
  free(p);
  free(p);
}

static void
sighandler(int sig)
{
  foo();
}

int main()
{
  signal(SIGALRM, sighandler);

  alarm(1);

  sleep(10);
}
From 94391b7af06f6aec6fc02543b894b0963556118e Mon Sep 17 00:00:00 2001
From: Jon TURNEY <[email protected]>
Date: Mon, 9 Mar 2015 21:55:29 +0000
Subject: [PATCH] Teach stackinfo::walk() how to virtually unwind the tls
 sigstack

This improves how stackinfo::dumpstack() dumps _sigbe and sigdelayed frames

Signed-off-by: Jon TURNEY <[email protected]>
---
 winsup/cygwin/ChangeLog     |  6 ++++++
 winsup/cygwin/exceptions.cc | 13 +++++++++++++
 winsup/cygwin/gendef        |  2 ++
 3 files changed, 21 insertions(+)

diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index d63b359..f0cbeed 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,9 @@
+2015-03-12  Jon TURNEY  <[email protected]>
+
+       * exceptions.cc (stack_info): Add sigstackptr member.
+       (walk): Unwind sigstackptr inside _sigbe and sigdelayed.
+       * gendef (_sigdelayed_end): Add symbol to mark end of sigdelayed.
+
 2015-03-11  Corinna Vinschen  <[email protected]>
 
        * include/cygwin/types.h: Include <sys/_timespec.h>
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index 73de7e7..6035270 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -45,6 +45,8 @@ details. */
 #define CALL_HANDLER_RETRY_INNER 10
 
 char debugger_command[2 * NT_MAX_PATH + 20];
+extern u_char _sigbe;
+extern u_char _sigdelayed_end;
 
 static BOOL WINAPI ctrl_c_handler (DWORD);
 
@@ -277,6 +279,7 @@ class stack_info
 #ifdef __x86_64__
   CONTEXT c;
   UNWIND_HISTORY_TABLE hist;
+  __stack_t *sigstackptr;
 #endif
 public:
   STACKFRAME sf;                /* For storing the stack information */
@@ -305,6 +308,7 @@ stack_info::init (PUINT_PTR framep, bool wantargs, PCONTEXT 
ctx)
       memset (&c, 0, sizeof c);
       c.ContextFlags = CONTEXT_ALL;
     }
+  sigstackptr = _my_tls.stackptr;
 #endif
   memset (&sf, 0, sizeof (sf));
   if (ctx)
@@ -340,6 +344,15 @@ stack_info::walk ()
   sf.AddrStack.Offset = c.Rsp;
   sf.AddrFrame.Offset = c.Rbp;
 
+  if ((c.Rip >= (DWORD64)&_sigbe) && (c.Rip < (DWORD64)&_sigdelayed_end))
+    {
+      /* _sigbe and sigdelayed don't have SEH unwinding data, so virtually
+         unwind the tls sigstack */
+      c.Rip = sigstackptr[-1];
+      sigstackptr--;
+      return 1;
+    }
+
   f = RtlLookupFunctionEntry (c.Rip, &imagebase, &hist);
   if (f)
     RtlVirtualUnwind (0, imagebase, c.Rip, f, &c, &hdl, &establisher, NULL);
diff --git a/winsup/cygwin/gendef b/winsup/cygwin/gendef
index ac411ca..01b8c39 100755
--- a/winsup/cygwin/gendef
+++ b/winsup/cygwin/gendef
@@ -327,6 +327,8 @@ sigdelayed:
        xchgq   %r10,(%rsp)
        ret
        .seh_endproc
+_sigdelayed_end:
+       .global _sigdelayed_end
 
 # _cygtls::pop
        .global _ZN7_cygtls3popEv
-- 
2.1.4

Reply via email to