[Mono-list] catching stack overflow signals

Dietmar Maurer dietmar@ximian.com
19 Jun 2002 18:44:23 +0200


I just detected another more generic problem. Stack overflow can occur
within system/library calls, so system state is not consistent. I cant
imagine how to avoid that, so all we can do is to terminate the program?

- Dietmar

On Wed, 2002-06-19 at 17:42, Dietmar Maurer wrote:
> Hi all,
> 
> today I tried to find a solution to catch stack overflow signals
> (segmentation fault) in order to throw a StackOverflow exception, but
> unfortunately its not so easy.
> 
> On stack overflow the system raises a SIGSEGV signal and transfers
> control to the handler. But that action needs the stack, so it raises
> another SIGSEGV which then terminates the process immediately.
> 
> On solution is to use a separate stack for signal handler. We can use
> sigaltstack () to setup such extra stack. I have attached a small test
> program I found on the net to show how this works - simply compile the
> program with:
> 
> gcc sigtest.c -o sigtest
> 
> The problem with that solution is that i does not work with pthread. you
> can test that if you compile the program with:
> 
> gcc sigtest.c -o sigtest -lpthread
> 
> Pthread tries to determine the current thread using the stack pointer,
> so this fails if we change the stack using sigaltstack.
> 
> On workaround is to use a stack allocated memory for sigaltstack, for
> example we can define a local variable:
> 
> char alttack [8192] __attribute__ (( aligned (32) )); 
> 
> That way we solve the above problem, but i am quite sure that this will
> confuse pthread somehow, because its thread detection always points to
> the same thread in signal handler.
> 
> So maybe someone knows a better solution?
> 
> - Dietmar
> 
> 
> 
> 
> 
> 
> 
> ----
> 

> /*
>  * Test program to test sigaltstack() system call.
>  * Does a stack overflow, which normally linux/i386 will not be able 
>  * to deliver. There are other ways of demonstrating but this is
>  * the easiest. Of course apply the sigaltstack() patch first or
>  * you wont see diddly.
>  */
> 
> #include <sys/signal.h>
> #include <asm/unistd.h>
> #include <stdio.h>
> #include <errno.h>
> #include <sys/time.h>
> #include <sys/resource.h>
> #include <unistd.h>
> #include <sys/syscall.h>
> 
> struct sigaction act;
> 
> #define GET_STACK( x ) \
> 	__asm__ __volatile("movl %%esp, %0\n\t" : "=q" (x) : :"%0" )
> 
> void handler()
> {
> 	unsigned long ss_sp;
> 
> 	printf( "Caught SIGSEGV and handled stack overflow correctly.\n" );
> 	GET_STACK( ss_sp );
> 	printf( "signal handler stack pointer = %p\n", ss_sp );
> 	exit(0);
> }
> void handler1()
> {
> 	printf("handler1\n");
> 	exit(0);
> }
> 
> void recurse()
> {
> 	char buf[ 4096 ];
> 	recurse();
> }
> 
> int main()
> {
> 	long ret;
> 	struct sigaltstack sa, sa_old;
> 	unsigned long ss_sp;
> 
> 	/* Step 1 - setup your alternate sig-stack */
> 	sa.ss_sp = malloc( MINSIGSTKSZ );
> 	if( !sa.ss_sp )
> 		printf( "malloc failed\n" );
> 
> 	sa.ss_size = MINSIGSTKSZ;
> 	sa.ss_flags = SS_ONSTACK;
> 
> 	if( sigaltstack( &sa, &sa_old ) < 0 ) {
> 		printf( "failed to install alt-stack!\n" );
> 		exit(0);
> }
> 
> /* Step 2 - setup a sighandler and specify we want it delivered
> 	 * on the alternate stack */
> 	act.sa_handler = handler;
> 	act.sa_flags = SA_STACK;
> 
> #if 0
> 	syscall (SYS_sigaction, SIGSEGV, &act, NULL);
> #else
> 	sigaction( SIGSEGV, &act, 0);
> #endif	
> 
> /* Step 3 - Generate a stack-overflow with recursion.
>      * Without the sigaltstack you will not handle the SIGSEGV
> 	 * because there will be no more space left on the processes
> 	 * main stack for the call.
>      * With the patch, you will catch it correctly! Wheee!! 
> 	 */
> 
> 	GET_STACK( ss_sp );
> 	printf( "main stack pointer = %p\n", ss_sp );
> 
> 	recurse();
> }