[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#427124: g++-4.2: -fstrict-aliasing optimizations cause constructor not to run for object causing segfault



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


Reply to: