runtime: abort stack scan in cases that we cannot unwind the stack
authorIan Lance Taylor <ian@gcc.gnu.org>
Tue, 19 Feb 2019 15:32:34 +0000 (15:32 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Tue, 19 Feb 2019 15:32:34 +0000 (15:32 +0000)
    In signal-triggered stack scan, if the signal is delivered at
    certain bad time (e.g. in vdso, or in the middle of setcontext?),
    the unwinder may not be able to unwind the whole stack, while it
    still reports _URC_END_OF_STACK. So we cannot rely on _URC_END_OF_STACK
    to tell if it successfully scanned the stack. Instead, we check
    the last Go frame to see it actually reached the end of the stack.
    For Go-created stack, this is runtime.kickoff. For C-created
    stack, we need to record the outermost Go frame when it enters
    the Go side.

    Also we cannot unwind the stack if the signal is delivered in the
    middle of runtime.gogo, halfway through a goroutine switch, where
    the g and the stack don't match. Give up in this case as well.

    Reviewed-on: https://go-review.googlesource.com/c/159098

From-SVN: r269018

gcc/go/gofrontend/MERGE
libgo/go/runtime/cgo_gccgo.go
libgo/go/runtime/proc.go
libgo/go/runtime/runtime2.go
libgo/go/runtime/stubs.go
libgo/runtime/go-unwind.c

index d9ee0bad8693e1803fbbc46012629581270d99e6..07437d4809c4fb31b9a9bb83b18e1914e1ba9d88 100644 (file)
@@ -1,4 +1,4 @@
-9605c2efd99aa9c744652a9153e208e0653b8596
+672572130ba7a7b277a4c9c8f93576fc42accf63
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index aff8130a27c38b625d2c09b1f6c6d5ea03bd5236..e4d27e8ab67f888df3ea0db70d5133bc3b7f46f7 100644 (file)
@@ -80,6 +80,10 @@ func CgocallBack() {
                gp = getg()
                mp := gp.m
                mp.dropextram = true
+
+               // This is a C-created stack.
+               // Record the outermost Go frame to help stack scan.
+               gp.entrysp = getcallersp()
        }
 
        lockOSThread()
index e9375635b3eeb23383ae3749a0abfa5a0e27e6ac..1c944d64752b9b0f978fe4893086e5c8952d36e5 100644 (file)
@@ -1192,6 +1192,9 @@ func kickoff() {
                gp.param = nil
        }
 
+       // Record the entry SP to help stack scan.
+       gp.entrysp = getsp()
+
        fv(param)
        goexit1()
 }
index 4cd68da2ea24c482fb969e88e2eb38fc11340c52..4f823e09b4be04b221cad522a2c2a128c79535d9 100644 (file)
@@ -433,6 +433,7 @@ type g struct {
 
        entry    func(unsafe.Pointer) // goroutine function to run
        entryfn  uintptr              // function address passed to __go_go
+       entrysp  uintptr              // the stack pointer of the outermost Go frame
        fromgogo bool                 // whether entered from gogo function
 
        scanningself bool // whether goroutine is scanning its own stack
index 9f5191bc661542d21b5621f8f32c61a6cde01260..dfdb38e8010620d9ed47bb40c6b3ea6708f7d53d 100644 (file)
@@ -231,6 +231,10 @@ func getcallerpc() uintptr
 //go:noescape
 func getcallersp() uintptr // implemented as an intrinsic on all platforms
 
+// getsp returns the stack pointer (SP) of the caller of getsp.
+//go:noinline
+func getsp() uintptr { return getcallersp() }
+
 func asmcgocall(fn, arg unsafe.Pointer) int32 {
        throw("asmcgocall")
        return 0
index 158cbd07968efa01d6504b1c2fa96226421dba23..9fd9596926ab70587c3635d1c04aa91540fd4703 100644 (file)
@@ -649,6 +649,19 @@ findstackmaps (struct _Unwind_Context *context, _Unwind_Ptr *ip, _Unwind_Ptr *sp
   _sleb128_t index;
   int size;
 
+#ifdef HAVE_GETIPINFO
+  ip1 = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+  ip1 = _Unwind_GetIP (context);
+#endif
+  if (! ip_before_insn)
+    --ip1;
+
+  if (ip != NULL)
+    *ip = ip1;
+  if (sp != NULL)
+    *sp = _Unwind_GetCFA (context);
+
 #ifdef __ARM_EABI_UNWINDER__
   {
     _Unwind_Control_Block *ucbp;
@@ -672,14 +685,6 @@ findstackmaps (struct _Unwind_Context *context, _Unwind_Ptr *ip, _Unwind_Ptr *sp
   if (info.TType == NULL)
     return NOTFOUND_OK;
 
-#ifdef HAVE_GETIPINFO
-  ip1 = _Unwind_GetIPInfo (context, &ip_before_insn);
-#else
-  ip1 = _Unwind_GetIP (context);
-#endif
-  if (! ip_before_insn)
-    --ip1;
-
   size = value_size (info.ttype_encoding);
 
   action_record = NULL;
@@ -738,15 +743,16 @@ findstackmaps (struct _Unwind_Context *context, _Unwind_Ptr *ip, _Unwind_Ptr *sp
   if (stackmap1 == NULL)
     return NOTFOUND_BAD;
 
-  if (ip != NULL)
-    *ip = ip1;
-  if (sp != NULL)
-    *sp = _Unwind_GetCFA (context);
   if (stackmap != NULL)
     *stackmap = stackmap1;
   return FOUND;
 }
 
+struct scanstate {
+  void* gcw;      // the GC worker, passed into scanstackwithmap_callback
+  uintptr lastsp; // the last (outermost) SP of Go function seen in a traceback, set by the callback
+};
+
 // Callback function to scan a stack frame with stack maps.
 // It skips non-Go functions.
 static _Unwind_Reason_Code
@@ -755,7 +761,11 @@ scanstackwithmap_callback (struct _Unwind_Context *context, void *arg)
   struct _stackmap *stackmap;
   _Unwind_Ptr ip, sp;
   G* gp;
-  void *gcw = arg;
+  struct scanstate* state = (struct scanstate*) arg;
+  void *gcw;
+
+  gp = runtime_g ();
+  gcw = state->gcw;
 
   switch (findstackmaps (context, &ip, &sp, &stackmap))
     {
@@ -767,7 +777,6 @@ scanstackwithmap_callback (struct _Unwind_Context *context, void *arg)
           // No stack map found.
           // If we're scanning from the signal stack, the goroutine
           // may be not stopped at a safepoint. Allow this case.
-          gp = runtime_g ();
           if (gp != gp->m->gsignal)
             {
               // TODO: print gp, pc, sp
@@ -781,6 +790,7 @@ scanstackwithmap_callback (struct _Unwind_Context *context, void *arg)
         abort ();
     }
 
+  state->lastsp = sp;
   runtime_scanstackblockwithmap (ip, sp, (uintptr)(stackmap->len) * sizeof(uintptr), stackmap->data, gcw);
 
   return _URC_NO_REASON;
@@ -792,10 +802,30 @@ bool
 scanstackwithmap (void *gcw)
 {
   _Unwind_Reason_Code code;
+  bool ret;
+  struct scanstate state;
+  G* gp;
+  G* curg;
+
+  state.gcw = gcw;
+  state.lastsp = 0;
+  gp = runtime_g ();
+  curg = gp->m->curg;
+
   runtime_xadd (&__go_runtime_in_callers, 1);
-  code = _Unwind_Backtrace (scanstackwithmap_callback, gcw);
+  code = _Unwind_Backtrace (scanstackwithmap_callback, (void*)&state);
   runtime_xadd (&__go_runtime_in_callers, -1);
-  return code == _URC_END_OF_STACK;
+  ret = (code == _URC_END_OF_STACK);
+  if (ret && gp == gp->m->gsignal)
+    {
+      // For signal-triggered scan, the unwinder may not be able to unwind
+      // the whole stack while it still reports _URC_END_OF_STACK (e.g.
+      // signal is delivered in vdso). Check that we actually reached the
+      // the end of the stack, that is, the SP on entry.
+      if (state.lastsp != curg->entrysp)
+        ret = false;
+    }
+  return ret;
 }
 
 // Returns whether stack map is enabled.