Bug 111864

Summary: bad codegen with -O1
Product: Red Hat Enterprise Linux 3 Reporter: Martin Sebor <sebor>
Component: gcc3Assignee: Jakub Jelinek <jakub>
Status: CLOSED WONTFIX QA Contact:
Severity: high Docs Contact:
Priority: medium    
Version: 3.0   
Target Milestone: ---   
Target Release: ---   
Hardware: ia64   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2007-10-19 19:32:13 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Martin Sebor 2003-12-11 00:14:02 UTC
From Bugzilla Helper:
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.4)
Gecko/20030624 Netscape/7.1 (ax)

Description of problem:
The attached program, reduced from our implementation of
std::basic_string, produces an error at runtime when optimized at -O.
It runs with no errors at -O0 or -O2 or -O3. The output shows that the
compiler generates code that corrupts the program data (in the member
function free_a()). Outlining the member function also eliminates the bug.

Version-Release number of selected component (if applicable):
gcc-3.2.3 20030502 (Red Hat Linux 3.2.3-20)

How reproducible:
Always

Steps to Reproduce:
Compile and run the program t.C below with gcc -O t.C -lsupc++ &&
./a.out. Observe the incorrect output.

extern "C" {
  int printf (const char*, ...);

  void* malloc (unsigned long);
  void free (void*);
}

struct rw_mutex
{
  void acquire () { }
  void release () { }
};

struct rw_guard
{
  rw_mutex *mutex_;

  rw_guard (rw_mutex &mutex): mutex_ (&mutex) {
    mutex_->acquire ();
  }

  ~rw_guard () { mutex_->release (); }
};

template <class T>
inline T dec (T &t, rw_mutex &mutex)
{
  rw_guard guard (mutex);
  return --t;
}

template <class T>
inline T dec (T &t, bool)
{
  static rw_mutex mutex;
  return dec (t, mutex);
}

template <class T> class S;

template <class T>
struct A
{
  int ref () const { return refs_ + 1; }

  void inc () {
    if (this != S<T>::a1 ())
      ++this->refs_;
  }

  long dec () {
    return this == S<T>::a1 () ? 1L : 1 + ::dec (refs_, mutex_);
  }

  T* data () { return (T*)(this + 1); }

  rw_mutex mutex_;

  int refs_;
  int cap_;
  int size_;
};

template <class T>
struct A1: A<T>
{
  T t;
};

template <class T>
struct S
{
  S (): data_ (a1 ()->data ()) { }

  S (const S&);

  S (const T*, int);

  ~S () {
    free_a (0);
  }

  S& foobar (const S&) { return *this; }

  S& append (const S&);

  A<T>* a0 () const {
    return (A<T>*)data_ - 1;
  }

  void free_a (T*);

  static A1<T>* a1 () {
    static A1<T> a;

    return &a;
  }

  A<T>* make_a0 (int, int);

  T* data_;
};


template <class T>
inline void S<T>::free_a (T* ptr)
{
  void* const a = a0 ();

  const long ref = a0 ()->dec ();

  if (0 >= ref) {

    printf ("free_a(): deallocating %p %c= %p)\n",
            a0 (), a0 () == a ? '=' : '!', a);

    free (a0 ());
  }

  data_ = ptr;
}

template <class T>
S<T>::S (const S<T> &s)
{
  if (s.a0 ()->ref () > 0) {
    data_ = s.data_;
    a0 ()->inc ();
  }
  else {
    const int n = s.a0 ()->size_;
    data_ = make_a0 (n, n)->data ();
  }
}

template <class T>
S<T>& S<T>::append (const S &str)
{
  const int len = a0 ()->size_ + str.a0 ()->size_;

  if (len > a0 ()->cap_ || a0 ()->ref () > 1)
    return foobar (str);

  return *this;
}

template <class T>
A<T>* S<T>::make_a0 (int cap, int len)
{
  if (!cap)
    return a1 ();

  A<T>* a = (A<T>*)malloc (cap + sizeof (A<T>) + 2);

  a->cap_  = cap;
  a->size_ = len;
  a->refs_ = 0;

  return a;
}

template <class T>
S<T>::S (const T* s, int n)
{
  data_ = make_a0 (128 <  n ? n : 128, n)->data ();
}

template <class T>
inline S<T> operator+ (const S<T> &lhs, const S<T> &rhs)
{
  return S<T>(lhs.data_, lhs.a0 ()->size_).append (rhs);
}

const S<char> s1 ("1", 1);

int main ()
{
  S<char>() + s1;
}


Actual Results:  When compiled at -O1, the output of the program is:
free_a(): deallocating 0x600000000002c840 == 0x600000000002c840)
free_a(): deallocating 0x600000000002c820 != 0x600000000002c830)
free(): invalid pointer 0x600000000002c820!
free_a(): deallocating 0x600000000002c7a0 == 0x600000000002c7a0)


Expected Results:  When compiled at -O0, the output of the program is:
free_a(): deallocating 0x600000000001d540 == 0x600000000001d540)
free_a(): deallocating 0x600000000001d4a0 == 0x600000000001d4a0)


Additional info:

Comment 1 Martin Sebor 2004-06-09 20:59:42 UTC
The problem was originally reported against 3.2.3-20. We continue to
experience it with gcc 3.2.3-24 (on Red Hat Advanced Server 3.0/IA64).
Has there been any progress at all on this issue?

Comment 2 Jakub Jelinek 2004-06-09 21:45:48 UTC
Verified this in g++ 3.2.3-35 and g++ 3.3.3-7, while it seems to work
with g++ 3.4.0-2 (as well as with -O0, -Os, -O2 and -O3).

Comment 3 RHEL Program Management 2007-10-19 19:32:13 UTC
This bug is filed against RHEL 3, which is in maintenance phase.
During the maintenance phase, only security errata and select mission
critical bug fixes will be released for enterprise products. Since
this bug does not meet that criteria, it is now being closed.
 
For more information of the RHEL errata support policy, please visit:
http://www.redhat.com/security/updates/errata/
 
If you feel this bug is indeed mission critical, please contact your
support representative. You may be asked to provide detailed
information on how this bug is affecting you.