Package: g++-4.2
Version: 4.2-20070528-1
Severity: normal
I reported this upstream here:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=32182
I may have found a situation where GCC's optimizations causes a constructor to
be skipped that leads to a crash. This problem first manifested itself in a
program involving well over 100000 lines of code (not including the extra lines
from #include'd files). The initial problem is in code generated by Babel,
http://www.llnl.gov/CASC/compnents/, in the runC2Cxx program part of the objarg
regression test. After many hours of work, I've reproduced the bug with a
program involving only 324 lines.
% wc *.c *.h *.hxx *.cxx
45 108 861 main.c
55 136 1174 RefCount.c
35 59 503 RefCount.h
78 170 1426 Wrapper.hxx
111 239 2052 Wrapper.cxx
324 712 6016 total
I compile these files with the following script:
#!/bin/sh
\rm -f *.o test_aliasing test_noaliasing
gcc-4.2 -g -O2 -c RefCount.c main.c Wrapper.cxx
g++-4.2 -g -O2 -o test_aliasing RefCount.o main.o Wrapper.o
gcc-4.2 -g -O2 -fno-strict-aliasing -c RefCount.c main.c Wrapper.cxx
g++-4.2 -g -O2 -fno-strict-aliasing -o test_noaliasing RefCount.o main.o
Wrapper.o
../test_noaliasing runs without crashing, and ./test_aliasing crashes in this
operator= method:
TestClass &
TestClass::operator =(const TestClass &rhs)
{
if (d_self != rhs.d_self) {
if (d_self) {
/* segfault at next line because d_self wasn't initialized to 0 */
deleteRef(reinterpret_cast< struct RefCount_t * >(d_self));
}
d_self = rhs.d_self;
if (d_self) {
addRef(reinterpret_cast< struct RefCount_t * >(d_self));
}
}
return *this;
}
when called from this extern "C" function:
struct Test *
getItem(struct C_Container *cont,
int ind)
{
struct Test *result = 0;
TestClass _local_result;
try {
_local_result = cont->d_cont->at(ind); /* crash here */
}
catch(...) {
return result;
}
result = _local_result.getIOR();
if (result) {
addRef(reinterpret_cast<struct RefCount_t *>(result));
}
return result;
}
In getItem, it appears to have skipped executing empty constructor for
_local_result that initializes d_self to 0.
Here is the declaration for TestClass and its super classes.
class BaseClass {
protected:
void *d_self;
public:
BaseClass() : d_self(0) {}
BaseClass(void *ior) : d_self(ior) {}
~BaseClass() {
if (d_self) {
struct RefCount_t *ref =
reinterpret_cast<struct RefCount_t *>(d_self);
deleteRef(ref);
d_self = 0;
}
}
};
class NextClass : public virtual BaseClass {
public:
typedef struct Next ior_t;
NextClass() {}
NextClass(ior_t *ior);
};
class TestClass : public virtual NextClass {
public:
typedef struct Test ior_t;
TestClass() {}
TestClass(ior_t *ior);
virtual ~TestClass() { }
TestClass(const TestClass &src);
TestClass& operator= (const TestClass &rhs);
ior_t *getIOR() const { return reinterpret_cast < ior_t *>(d_self); }
long getNum() const { return reinterpret_cast< Test *>(d_self)->num; }
};
My understanding is the _local_result should be initialized by running with the
TestClass::TestClass() constructor which fires after the NextClass::NextClass()
constructor which fires after the BaseClass::BaseClass() constructor where
d_self is initialized to 0. If I add a printf("Hello\n"); call inside the
BaseClass() constructor, it runs and the program doesn't segfault.
The output from running valgrind on the executable supports the idea that
d_self is not being initialized.
% valgrind ./test_aliasing
==30651== Memcheck, a memory error detector.
==30651== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==30651== Using LibVEX rev 1732, a library for dynamic binary translation.
==30651== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==30651== Using valgrind-3.2.3-Debian, a dynamic binary instrumentation
framework.
==30651== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==30651== For more details, rerun with: -v
==30651==
==30651== Conditional jump or move depends on uninitialised value(s)
==30651== at 0x80489E5: TestClass::operator=(TestClass const&)
(Wrapper.cxx:30)
==30651== by 0x8048BE2: getItem (Wrapper.cxx:101)
==30651== by 0x804887B: main (main.c:35)
==30651==
==30651== Conditional jump or move depends on uninitialised value(s)
==30651== at 0x80489E9: TestClass::operator=(TestClass const&)
(Wrapper.cxx:31)
==30651== by 0x8048BE2: getItem (Wrapper.cxx:101)
==30651== by 0x804887B: main (main.c:35)
==30651==
==30651== Use of uninitialised value of size 4
==30651== at 0x8048716: deleteRef (RefCount.c:52)
==30651== by 0x80489F2: TestClass::operator=(TestClass const&)
(Wrapper.cxx:33)
==30651== by 0x8048BE2: getItem (Wrapper.cxx:101)
==30651== by 0x804887B: main (main.c:35)
==30651==
==30651== Process terminating with default action of signal 11 (SIGSEGV)
==30651== Bad permissions for mapped region at address 0x8048EB4
==30651== at 0x804871D: deleteRef (RefCount.c:52)
==30651== by 0x80489F2: TestClass::operator=(TestClass const&)
(Wrapper.cxx:33)
==30651== by 0x8048BE2: getItem (Wrapper.cxx:101)
==30651== by 0x804887B: main (main.c:35)
==30651==
==30651== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 19 from 1)
==30651== malloc/free: in use at exit: 1,008 bytes in 12 blocks.
==30651== malloc/free: 12 allocs, 0 frees, 1,008 bytes allocated.
==30651== For counts of detected errors, rerun with: -v
==30651== searching for pointers to 12 not-freed blocks.
==30651== checked 100,788 bytes.
==30651==
==30651== LEAK SUMMARY:
==30651== definitely lost: 0 bytes in 0 blocks.
==30651== possibly lost: 0 bytes in 0 blocks.
==30651== still reachable: 1,008 bytes in 12 blocks.
==30651== suppressed: 0 bytes in 0 blocks.
==30651== Rerun with --leak-check=full to see details of leaked memory.
Segmentation fault
The program doesn't crash when compiled with Intel's 9.0.21 C++ compiler. It
doesn't crash when compiled with pre-4.2 GCC versions either.
Based on this evidence, it seems possible that this illustrates a case of over
zealous optimization.
Release: gcc-4.2 (GCC) 4.2.1 20070525 (prerelease) (Debian
4.2-20070525-1)
System: Linux driftcreek 2.6.18-4-686 #1 SMP Wed May 9 23:03:12 UTC 2007 i686
GNU/Linux
Architecture: i686
configured with: ../src/configure -v
--enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr
--enable-shared --with-system-zlib --libexecdir=/usr/lib
--without-included-gettext --enable-threads=posix --enable-nls
--with-gxx-include-dir=/usr/include/c++/4.2 --program-suffix=-4.2
--enable-clocale=gnu --enable-libstdcxx-debug --enable-mpfr
--enable-targets=all --disable-werror --enable-checking=release
--build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
-- System Information:
Debian Release: lenny/sid
APT prefers unstable
APT policy: (500, 'unstable')
Architecture: amd64 (x86_64)
Kernel: Linux 2.6.21-1-amd64 (SMP w/2 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/bash
Versions of packages g++-4.2 depends on:
ii gcc-4.2 4.2-20070528-1 The GNU C compiler
ii gcc-4.2-base 4.2-20070528-1 The GNU Compiler Collection (base
ii libc6 2.5-9 GNU C Library: Shared libraries
ii libstdc++6-4.2-dev 4.2-20070528-1 The GNU Standard C++ Library v3 (d
g++-4.2 recommends no packages.
-- no debconf information
Attachment:
test_4.tar.bz2
Description: BZip2 compressed data