Bug 40841

Summary: Application segfaults when throwing an exception that destroys an fstream
Product: gcc Reporter: Jonathan Briggs <zlynx>
Component: libstdc++Assignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED INVALID    
Severity: normal CC: gcc-bugs, hjl.tools, rguenth
Priority: P3    
Version: 4.4.1   
Target Milestone: ---   
Host: ia64-redhat-linux Target:
Build: Known to work: 4.3.0
Known to fail: 4.4.1 4.4.2 Last reconfirmed:
Attachments: reproduce

Description Jonathan Briggs 2009-07-23 20:37:54 UTC
I configure the fstream like this:

    ifstream is; 
    is.exceptions(ifstream::badbit | ifstream::failbit);
    is.open(filename, ifstream::in | ifstream::binary);

When it tries to open a file that does not exist, it throws an exception just as it should. If this is immediately inside a try/catch block it works. If it is inside a function call however, the program segfaults.

I believe that the problem is inside the ifstream class. Further testing showed that declaring a function like this:
void f() {
  ifstream is;
  throw 1;
}
also causes a segfault.

If the exception is not caught in the functions above f() there is an exception abort but no segfault.

I tried this on GCC 4.4.1 and 4.4.2, both from SVN. GCC 4.3 from Fedora does not do this.

I believe that the problem is in the libraries, not the GCC binary, because the Fedora GCC *does* show the problem if I allow it to link to the libraries from GCC 4.4.2. A -static build on GCC 4.3 works, and a -static build on GCC 4.4.2 fails.

Binutils are Fedora 9 default:
$ ld -v
GNU ld version 2.18.50.0.6-2 20080403


I configured my SVN GCCs like this:
$ gcc -v
Using built-in specs.
Target: ia64-redhat-linux
Configured with: ../configure --prefix=/opt/gcc-4.4 --enable-shared --enable-threads=posix --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-languages=c,c++ --build=ia64-redhat-linux
Thread model: posix
gcc version 4.4.2 20090722 (prerelease) (GCC) 

Fedora 9's GCC is configured like this:
$ /usr/bin/gcc -v
Using built-in specs.
Target: ia64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --build=ia64-redhat-linux
Thread model: posix
gcc version 4.3.0 20080428 (Red Hat 4.3.0-8) (GCC)
Comment 1 Paolo Carlini 2009-07-23 20:46:31 UTC
First of all, please provide a complete, small, self contained, snippet showing the problem. Thanks.
Comment 2 Jonathan Briggs 2009-07-23 21:01:57 UTC
Created attachment 18247 [details]
reproduce
Comment 3 Jonathan Briggs 2009-07-23 21:03:36 UTC
Also note that this seems to be a IA64 problem, not x86.
Comment 4 Paolo Carlini 2009-07-23 21:09:15 UTC
I see, I can't reproduce it on x86_64-linux, maybe Richard can help for ia64 testing... If that's really the case, however, probably not a libstdc++-proper issue.
Comment 5 Jonathan Briggs 2009-07-23 22:26:04 UTC
The actual segfault seems to happen in the .plt section. I am not entirely clear on how this is laid out and how to find what symbol is where.

But on entry to the plt code, the r1 register is set to an invalid memory location and it is then read from. It appears that r1 is supposed to be set from r35 much earlier, perhaps it gets overwritten.

It sort of looks like the exception unwinder changed the register value but the code expects the register to still hold the right offset to the PLT.
Comment 6 H.J. Lu 2009-07-24 01:52:52 UTC
It works for me on RHEL 4 with gcc 4.4.1:

[hjl@gnu-14 tmp]$ cat foo.cc
#include <fstream>

using namespace std;

void f(const char * filename)
{
 ifstream is;
 throw 2;
}

int main()
{
 try {
  f("v3");
 } catch(int e) {
 }
}
[hjl@gnu-14 tmp]$ /usr/gcc-4.4/bin/g++ foo.cc -Wl,-rpath,/usr/gcc-4.4/lib
[hjl@gnu-14 tmp]$ ldd a.out
        linux-gate.so.1 =>  (0xa000000000000000)
        libstdc++.so.6 => /usr/gcc-4.4/lib/libstdc++.so.6 (0x2000000000044000)
        libm.so.6.1 => /lib/tls/libm.so.6.1 (0x2000000000240000)
        libgcc_s.so.1 => /usr/gcc-4.4/lib/libgcc_s.so.1 (0x20000000002fc000)
        libunwind.so.7 => /usr/gcc-4.4/lib/libunwind.so.7 (0x2000000000330000)
        libc.so.6.1 => /lib/tls/libc.so.6.1 (0x200000000035c000)
        /lib/ld-linux-ia64.so.2 (0x2000000000000000)
[hjl@gnu-14 tmp]$ ./a.out
[hjl@gnu-14 tmp]$ /usr/gcc-4.4/bin/g++ -v
Using built-in specs.
Target: ia64-unknown-linux-gnu
Configured with: /net/gnu-13/export/gnu/src/gcc-4.4/gcc/configure --enable-clocale=gnu --with-system-zlib --enable-checking=assert --with-demangler-in-ld --enable-shared --enable-threads=posix --enable-haifa --prefix=/usr/gcc-4.4 --with-local-prefix=/usr/local
Thread model: posix
gcc version 4.4.1 (GCC)
[hjl@gnu-14 tmp]$ ld -V
GNU ld (Linux/GNU Binutils) 2.19.51.0.14.20090722
  Supported emulations:
   elf64_ia64
[hjl@gnu-14 tmp]$
Comment 7 Richard Biener 2009-07-24 12:21:09 UTC
It works on SLES11 as well.

rguenther@scandium:/tmp> g++ -v
Using built-in specs.
Target: ia64-suse-linux
Configured with: ../configure --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man --libdir=/usr/lib --libexecdir=/usr/lib --enable-languages=c,c++,objc,fortran,obj-c++,java,ada --enable-checking=release --with-gxx-include-dir=/usr/include/c++/4.3 --enable-ssp --disable-libssp --with-bugurl=http://bugs.opensuse.org/ --with-pkgversion='SUSE Linux' --disable-libgcj --disable-libmudflap --with-slibdir=/lib --with-system-zlib --enable-__cxa_atexit --enable-libstdcxx-allocator=new --disable-libstdcxx-pch --enable-version-specific-runtime-libs --program-suffix=-4.3 --enable-linux-futex --with-system-libunwind --build=ia64-suse-linux
Thread model: posix
gcc version 4.3.2 [gcc-4_3-branch revision 141291] (SUSE Linux) 

rguenther@scandium:/tmp> ld --version
GNU ld (GNU Binutils; SUSE Linux Enterprise 11) 2.19
Copyright 2007 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.

rguenther@scandium:/tmp> g++ -o t t.cpp
rguenther@scandium:/tmp> ldd t
        linux-gate.so.1 =>  (0xa000000000000000)
        libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x2000000000090000)
        libm.so.6.1 => /lib/libm.so.6.1 (0x2000000000270000)
        libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x2000000000350000)
        libunwind.so.7 => /lib/libunwind.so.7 (0x2000000000380000)
        libc.so.6.1 => /lib/libc.so.6.1 (0x20000000003d0000)
        /lib/ld-linux-ia64.so.2 (0x2000000000000000)
rguenther@scandium:/tmp> ./t

One difference seems to be that Fedora builds with --disable-libunwind-exceptions.
Comment 8 Jonathan Briggs 2009-07-24 15:47:29 UTC
I will work on trying it with libunwind and then with binutils 2.19. Those seem to be the differences so far.
Comment 9 Jonathan Briggs 2009-07-27 21:26:10 UTC
I built GCC using libunwind and binutils 2.19.1 and there is the same problem.

The registers are *not* being restored to the right values after the exception return.

In the following assembly code you can clearly see that it expects the value in r37 to remain useful after the call to f. It doesn't.

mov r37=r1
addl r38=112,r1;;
ld8 r38=[r38]
nop.i 0x0
nop.m 0x0
nop.i 0x0
br.call.sptk.many b0=4000000000000f80 <f(char const*)>
mov r1=r37
nop.i 0x0
br.few 4000000000001210 <main+0x190>;;

(Well, ok, it actually jumps to the exception catch block, but that block also does mov r1=r37 to get a PLT offset.)
Comment 10 Jonathan Briggs 2009-08-15 00:02:12 UTC
I erased every trace of experimental GCC versions and support libraries. Then I rebuilt GCC 4.4.2 (from SVN) using the system provided GCC 4.3.

The problem went away.

It still worries me some because it seems to indicate that the GCC bootstrap process was not pure and was somehow using a bad version of GCC or libraries.