[Bug c++/31289] New: gcc412 elides code in operator delete() override method

gcc-bugzilla at gcc dot gnu dot org gcc-bugzilla@gcc.gnu.org
Wed Mar 21 00:31:00 GMT 2007


// BUG DEMO for gcc 4.1.2 (and 4.1.1) for 32-bit x86
//
// If operator delete() is over-ridden in a base class, then
// if the implementation of delete() stores into the storage used
// by the object, the store seems to be removed by the compiler
// (maybe because the compiler thinks stores to a deleted object 
// are dead code?  Or may be the casts of void * cause trouble
// with the C99 aliasing rules??)
//
//   Only occurs with g++ -O3
//   Occurs using native gcc on 32-bit x86.  
//   Does NOT occur with 64-bit x86.
//   Does NOT occur with gcc 3.2.3
//
// The following program is a skeletal memory manager which maintains
// a separate free-list for each size object.  Deleted objects are
// kept in a singly-linked list.  operator delete() casts the void *
// pointer to be a pointer to an internal struct in order to store
// a "next" pointer in the first word of the storage block.   The
// store through this cast pointer seems to be lost.
//
// TO DEMO THE PROBLEM:
//    g++ -O3 thisfile.cpp && ./a.out
//    (prints "ERROR" and then segfaults if the bug is there)
//
// -Jim Avera (jima@cadence.com)
// Please copy steele@cadence.com rking@cadence.com on all correspondence
//
#include <stdio.h>
#include <stdlib.h>

const size_t MaxSize = 100;
class MyHeap {
public:
  MyHeap();
  ~MyHeap() {}
  void * Malloc(const size_t bytes_needed);
  void   Free(void * ptr_to_free, size_t how_big_it_is);
  void   Dump(void);

  struct Node {
    Node *next;
  };
private:
  Node *Heads[MaxSize];
};

MyHeap::MyHeap() {
  printf("MyHeap constructed\n");
  for (int i=0; i < MaxSize; ++i) {
    Heads[i] = 0;
  }
}
void MyHeap::Dump(void)
{
  printf("Dump of free-list data:\n");
  int num_empties = 0;
  for (int i=0; i < MaxSize; ++i) {
    if (Heads[i] == 0) {
      ++num_empties;
    } else {
      printf("  Heads[%d] = %p\n", i, Heads[i]);
      Node * next_node = Heads[i]->next;
      if (next_node != 0) {
        // For this test program, there should be exactly one item
        printf("  ERROR: Expecting only one free item on list\n");
        while (next_node != 0) {
          printf("    next = %p\n", next_node);
          next_node = next_node->next;
        }
        exit(1); // usually segfaults before getting here
      }
    }
  }
  printf("(%d lists are empty)\n", num_empties);
}
void * MyHeap::Malloc(const size_t bytes_needed) 
{
  if (bytes_needed < sizeof(Node) || bytes_needed > MaxSize) {
    printf("MyHeap::Malloc - object too small or too large\n");
    exit(1);
  }
  if (Heads[bytes_needed] == 0) {
    // Pre-charge the free list with more storage
    // (just one object for this test)
    Node * newnode = static_cast<Node *>( malloc(bytes_needed) );
    newnode->next = 0;
    Heads[bytes_needed] = newnode;
  }
  // Detach the first from the list
  Node * node = Heads[bytes_needed];
  Heads[bytes_needed] = node->next;
  return node;
}
void MyHeap::Free(void * ptr_to_free, size_t how_big_it_is)
{
  // Put object on the free list
  Node * node = static_cast<Node *>(ptr_to_free);
  node->next = Heads[how_big_it_is];
  Heads[how_big_it_is] = node;
}


// The memory-manager object
MyHeap my_pool;

class base {
public:
  void * operator new(size_t bytes_needed)
  {
    void * raw_mem = my_pool.Malloc(bytes_needed);
    return(raw_mem);
  }

  void operator delete(void * ptr_to_free,  size_t how_big_it_is) 
  {
    my_pool.Free(ptr_to_free, how_big_it_is);
  }
};

class middle : public base {
public:
  virtual ~middle() { }
};

class derived : public middle {
public:
  derived() {
    i = 11111111;
    j = 22222222;
    k = 33333333;
  }
  virtual ~derived() {
  }
  int i,j,k;
};

int main() {
  setbuf(stdout,NULL); setbuf(stderr,NULL); // disable buffering
  my_pool.Dump();

  printf("new...\n");
  derived *d = new derived;

  printf("Got %p, sizeof=%u, raw dump follows:\n", d, (unsigned)sizeof(*d));
  unsigned * up = reinterpret_cast<unsigned *>(d);
  const size_t size_in_unsigneds = sizeof(*d)/sizeof(unsigned);
  for (int ix=0; ix < size_in_unsigneds; ++ix) {
    printf("  0x%08x = %d\n", up[ix], up[ix]);
  }
  my_pool.Dump();

  printf("delete...\n");
  delete d;

  my_pool.Dump();

  printf("The bug was not detected\n");
  return 0;
}

Environment:
System: Linux xba24 2.4.21-37.ELsmp #1 SMP Wed Sep 7 13:28:55 EDT 2005 i686
i686 i386 GNU/Linux
Architecture: i686


host: i686-pc-linux-gnu
build: i686-pc-linux-gnu
target: i686-pc-linux-gnu
configured with:
/grid/sfi/ct_src/gcc-v4.1.2/platforms/linux86/matrix_bootstrap_000/gcc-4.1.2/configure
--prefix=/grid/common/pkgs/gcc/v4.1.2 --mandir=/grid/common/pkgs/gcc/v4.1.2/man
--infodir=/grid/common/pkgs/gcc/v4.1.2/info --enable-shared
--enable-threads=posix --disable-checking --with-system-zlib
--enable-__cxa_atexit --with-gnu-as
--with-as=/grid/common/pkgs/binutils/v2.17/bin/as --with-gnu-ld
--with-ld=/grid/common/pkgs/binutils/v2.17/bin/ld --disable-libgcj
--with-local-prefix=/grid/common/pkgs/gcc/v4.1.2 --enable-clocale=gnu
--with-gmp=/grid/common/pkgs/gmp/v4.1.4 --enable-languages=c,c++,objc,fortran

How-To-Repeat:
        Compile the supplied demo program with g++ -O3 and run it


-- 
           Summary: gcc412 elides code in operator delete() override method
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
        AssignedTo: unassigned at gcc dot gnu dot org
        ReportedBy: jima at cadence dot com
 GCC build triplet: i686-pc-linux-gnu
  GCC host triplet: i686-pc-linux-gnu
GCC target triplet: i686-pc-linux-gnu


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31289



More information about the Gcc-bugs mailing list