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