The function __splitstack_find is used by the Go garbage collector to
find all stacks used by all threads.  The goal was to find all pointers
on the stack.  Unfortunately, it turned out to miss some saved registers
in some cases, because they were saved on the split stack below the
location where the stack was restored.  That meant that if a signal
happened at just the wrong time, specifically just when signals were
unblocked, those register values would not necessarily be found on any
stack returned by __splitstack_find.  That could in turn mean that some
newly allocated memory, not yet stored anywhere, might be immediately
freed.

This patch fixes the problem by doing a target dependent adjustment of
the stack pointer to include any saved registers.

Bootstrapped and ran split-stack and Go tests on
x86_64-unknown-linux-gnu.  Committed to mainline.

Ian


2011-03-07  Ian Lance Taylor  <i...@google.com>

        * generic-morestack.c (__splitstack_find): Adjust returned stack
        pointer to include all registers pushed by __morestack.


Index: generic-morestack.c
===================================================================
--- generic-morestack.c	(revision 170756)
+++ generic-morestack.c	(working copy)
@@ -1,5 +1,5 @@
 /* Library support for -fsplit-stack.  */
-/* Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+/* Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
    Contributed by Ian Lance Taylor <i...@google.com>.
 
 This file is part of GCC.
@@ -846,20 +846,24 @@ __splitstack_find (void *segment_arg, vo
          parameters       <- old_stack
          return in f1
 	 return in f2
-	 data pushed by __morestack
+	 registers pushed by __morestack
 
-     On x86, the data pushed by __morestack includes the saved value
-     of the ebp/rbp register.  We want our caller to be able to see
-     that value, which can not be found on any other stack.  So we
-     adjust accordingly.  This may need to be tweaked for other
-     targets.  */
+     The registers pushed by __morestack may not be visible on any
+     other stack, if we are being called by a signal handler
+     immediately after the call to __morestack_unblock_signals.  We
+     want to adjust our return value to include those registers.  This
+     is target dependent.  */
 
   nsp = (char *) segment->old_stack;
-#ifdef STACK_GROWS_DOWNWARD
-  nsp -= 3 * sizeof (void *);
+
+#if defined (__x86_64__)
+  nsp -= 12 * sizeof (void *);
+#elif defined (__i386__)
+  nsp -= 6 * sizeof (void *);
 #else
-  nsp += 3 * sizeof (void *);
+#error "unrecognized target"
 #endif
+
   *next_sp = (void *) nsp;
 
 #ifdef STACK_GROWS_DOWNWARD

Reply via email to