Bug 12301 - [3.3/3.4 regression] corruption in exception path, exception in returned expression
Summary: [3.3/3.4 regression] corruption in exception path, exception in returned expr...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 3.3.1
: P1 critical
Target Milestone: 3.3.2
Assignee: Eric Botcazou
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2003-09-16 13:57 UTC by Colin Hirsch
Modified: 2004-01-17 04:22 UTC (History)
3 users (show)

See Also:
Host: sparc-sun-solaris2.8
Target: sparc-sun-solaris2.8
Build: sparc-sun-solaris2.8
Known to work:
Known to fail:
Last reconfirmed: 2003-09-17 15:52:36


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Colin Hirsch 2003-09-16 13:57:53 UTC
SYMPTOM:

If evaluating the expression in a return statement in a try-block throws an
exception, the program crashes with a segfault or uses random data at the point
mentioned in the code snippet below.

Compiled with g++ -W -Wall -O.


CODE SNIPPET:
(full example code below)

template<> std::string real_cast ( const std::string & value )
{
   try {
      return mk( value );  // bug triggered by mk throwing a std::string
   }
   catch ( std::string & s ) {
      throw s + value;  // segfaults in std::string::operator+
   }
}


WORKAROUND:

The problem goes away by either of
(1) not compiling with -O
(2) using an explicit temporary for the return value:

std::string nrv;
try {
  nrv = ...
}
catch ... {
}
return nrv;


ADDITIONAL ANALYSIS:

While debugging with gdb, it seemed that the stack got corrupted, i.e. while the
value of "note" was ok before the return statement, it was mangled when
execution reached the catch block; similar for the "this" pointer in case of
"foo" being a member function.

The full code below shows that the corruption cannot be a side-effect of "somefunc".


OS VERSION:

uname -a
SunOS xxxxxx 5.8 Generic_108528-20 sun4u sparc SUNW,Sun-Fire-880 Solaris


COMPILER VERSIONS:

Reading specs from /opt/local.source/lib/gcc-lib/sparc-sun-solaris2.8/3.3.2/specs
Configured with: ./configure --prefix=/opt/local.source --enable-shared
--enable-threads --with-cpu=v9 --enable-languages=c,c++ --disable-libgcj
--disable-multilib --with-gnu-as
--with-as=/opt/global/gcc-3.3-32-solaris8-108528-20/bin/as --with-gnu-ld
--with-ld=/opt/global/gcc-3.3-32-solaris8-108528-20/bin/ld
Thread model: posix
gcc version 3.3.2 20030908 (prerelease)

Reading specs from
/opt/global/gcc-3.3-32-solaris8-108528-20/lib/gcc-lib/sparc-sun-solaris2.8/3.3/specs
Configured with: ../gcc-3.3/configure
--prefix=/opt/global/gcc-3.3-32-solaris8-108528-20 --enable-shared
--enable-threads --with-cpu=v9 --with-gnu-as
--with-as=/opt/global/gcc-3.3-32-solaris8-108528-20/bin/as --with-gnu-ld
--with-ld=/opt/global/gcc-3.3-32-solaris8-108528-20/bin/ld --disable-multilib
Thread model: posix
gcc version 3.3


SOURCE CODE:

#include <string>
#include <iostream>

// Program to trigger a bug in GCC 3.3* / Sparc / Solaris 2.8
// compile with "g++ -O -W -Wall" to trigger bug
// compile without optimisation produces non-crashing version

template< class T > T real_cast( const std::string & );

template< class T > T cast_helper( const std::string & value, const std::string
& debug )
{
   try {
      return real_cast< T >( value );
   }
   catch ( std::string & e ) {
      throw e + debug;
   }
}

struct Value
{
   Value( const std::string & value ) : m_value( value ) {}

   template< class T > T get( const std::string & debug ) const
   {
      if ( m_value.empty() )
         throw std::string( "empty" );
      else
         return cast_helper< T >( m_value, debug );
   }

   const std::string m_value;
};

std::string mk( const std::string & value )
{
   if ( value.length() < 4 )
      return value + value;
   else
      throw value + value;
}

template<> std::string real_cast ( const std::string & value )
{
   try {
      return mk( value );
   }
   catch ( std::string & s ) {
      throw s + value;  // segfaults in std::string::operator+
   }
}

int main( int, char ** )
{
   try {
      Value error( "error" );
      const std::string error_s = error.get< std::string >( "error string" );
      std::cerr << error_s << '\n';
   }                                        
   catch ( std::string & e ) {
      std::cerr << "exception [ " << e << " ]\n";
   }
   return 0;
}
Comment 1 Wolfgang Bangerth 2003-09-16 14:35:33 UTC
I can't reproduce this on a linux box, so we need someone with a sparc. Christian? 
 
W. 
Comment 2 Christian Ehrhardt 2003-09-17 15:52:34 UTC
I can confirm the crash on sparc with recent 3.4. Apparently
the value of debug in function cast_helper is not preserved
accross the try block and the catch block tries to throw chunk.

Here's a reduced self contained testcase that crashes on sparc if
compiled with -O. Note that adding debug output sometimes makes the
bug go away.

// #include <iostream>

struct S{
	char * c;
	char data[100];
	S () : c (data) {};
	S (const S & s) {
		c = data;
		data[0] = s.c[0];
	}
};

S real_cast ()
{
      throw 3;  
}

S cast_helper( S & debug )
{
//   std::cerr << &debug << std::endl;      /*   AAA   */
   try {
      return real_cast();
   }
   catch ( int e) {
//      std::cerr << "HO " << &e << " "<< &debug << std::endl;
      throw debug;
   }
}

int main( )
{
   S tmp;
   try {
      cast_helper ( tmp );
   }                                        
   catch ( S & e ) { }
   return 0;
}
Comment 3 Eric Botcazou 2003-09-17 16:59:07 UTC
I can confirm the crash on GCC 3.2.2, 3.2.3, 3.3 and 3.3.2pre with Christian's
testcase, but GCC 2.95.3 doesn't crash so we have a regression.
Comment 4 Eric Botcazou 2003-09-17 21:06:37 UTC
Fixing.
Comment 5 CVS Commits 2003-09-21 08:17:53 UTC
Subject: Bug 12301

CVSROOT:	/cvs/gcc
Module name:	gcc
Changes by:	ebotcazou@gcc.gnu.org	2003-09-21 08:17:48

Modified files:
	gcc            : ChangeLog reorg.c 
	gcc/testsuite  : ChangeLog 
Added files:
	gcc/testsuite/g++.dg/eh: delayslot1.C 

Log message:
	PR target/12301
	* reorg.c (stop_search_p): Return 1 for insns that can
	throw internally.

Patches:
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/ChangeLog.diff?cvsroot=gcc&r1=2.1109&r2=2.1110
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/reorg.c.diff?cvsroot=gcc&r1=1.87&r2=1.88
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/ChangeLog.diff?cvsroot=gcc&r1=1.3074&r2=1.3075
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/eh/delayslot1.C.diff?cvsroot=gcc&r1=NONE&r2=1.1

Comment 7 Eric Botcazou 2003-09-21 08:24:35 UTC
See http://gcc.gnu.org/ml/gcc-patches/2003-09/msg01313.html