This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug c++/39209] New: Code generated for calling pointer-to-member segfaults when address of p-t-m is not 16-bit aligned


(Please note that in all the following code no standard libaries are linked -
this is for a bare-metal kernel project.)

The following code (full header/assembly attached below):

    Iterator &operator ++ ()
    {
      m_Node = (m_Node->*FunctionNext)();
      return *this;
    }

When instantiated as:

template<typename T>
struct _ListNode_t
{
  /** Get the next data structure in the list
   *\return pointer to the next data structure in the list */
  _ListNode_t *next()
    {return m_Next;}
  /** Get the previous data structure in the list
   *\return pointer to the previous data structure in the list */
  _ListNode_t *previous()
    {return m_Previous;}

  /** Pointer to the next node */
  _ListNode_t *m_Next;
  /** Pointer to the previous node */
  _ListNode_t *m_Previous;
  /** The value of the node */
  T value;
};

** List template specialisation for void*
 *\brief List template specialisation for void* */
template<>
class List<void*>
{
  /** The data structure of the list's nodes */
  typedef _ListNode_t<void*> node_t;
  public:
    /** Type of the bidirectional iterator */
    typedef ::Iterator<void*, node_t>     Iterator;
  ...

Produces the following (dis)assembly:

00000000
<_ZN8IteratorIPv11_ListNode_tIS0_EXadL_ZNS2_8previousEvEEXadL_ZNS2_4nextEvEES0_EppEv>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 08                sub    $0x8,%esp
   6:   b8 00 00 00 00          mov    $0x0,%eax
                        7: R_386_32     _ZN11_ListNode_tIPvE4nextEv
   b:   83 e0 01                and    $0x1,%eax
   e:   84 c0                   test   %al,%al
  10:   74 16                   je     28
<_ZN8IteratorIPv11_ListNode_tIS0_EXadL_ZNS2_8previousEvEEXadL_ZNS2_4nextEvEES0_EppEv+0x28>
  12:   8b 45 08                mov    0x8(%ebp),%eax
  15:   8b 00                   mov    (%eax),%eax
  17:   8b 10                   mov    (%eax),%edx
  19:   b8 ff ff ff ff          mov    $0xffffffff,%eax
                        1a: R_386_32    _ZN11_ListNode_tIPvE4nextEv
  1e:   8d 04 02                lea    (%edx,%eax,1),%eax
  21:   8b 00                   mov    (%eax),%eax
  23:   89 45 fc                mov    %eax,-0x4(%ebp)
  26:   eb 07                   jmp    2f
<_ZN8IteratorIPv11_ListNode_tIS0_EXadL_ZNS2_8previousEvEEXadL_ZNS2_4nextEvEES0_EppEv+0x2f>
  28:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%ebp)
                        2b: R_386_32    _ZN11_ListNode_tIPvE4nextEv
  2f:   8b 45 08                mov    0x8(%ebp),%eax
  32:   8b 00                   mov    (%eax),%eax
  34:   83 ec 0c                sub    $0xc,%esp
  37:   50                      push   %eax
  38:   ff 55 fc                call   *-0x4(%ebp)
  3b:   83 c4 10                add    $0x10,%esp
  3e:   89 c2                   mov    %eax,%edx
  40:   8b 45 08                mov    0x8(%ebp),%eax
  43:   89 10                   mov    %edx,(%eax)
  45:   8b 45 08                mov    0x8(%ebp),%eax
  48:   c9                      leave
  49:   c3                      ret

Notice that the AND instruction tests the least significant bit of the address
of ListNode::next(). The code path changes depending on this - if it is zero,
that is the address is 16-bit aligned, the second code path is taken which
works fine.

If the address is not 16-bit aligned, the first path is taken, and it is on
taking that path that a segfault (well, actually page fault for me as I'm on
bare metal) occurs, on the dereference in the MOV after the LEA.

At the point of the LEA, %eax contains the address of ListNode::next(), and
%edx contains what appears to be a valid address on my kernel heap. This is
seemingly being used as an offset, but with bad consequences. The code fails
here.

I can only get the code to fail when ListNode::next() is not 16-bit aligned.
This code worked perfectly in GCC 4.3.0, and in that version of the compiler
the AND/JE pair was not generated - the only code path is the second one.

Is this deliberate? Is it a regression?

Thanks,

James Molloy

------------------------------------------------------

<Iterator.h>

/*
 * Copyright (c) 2008 James Molloy, Jörg Pfähler, Matthew Iselin
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef KERNEL_UTILITIES_ITERATOR_H
#define KERNEL_UTILITIES_ITERATOR_H

#include <Log.h>

/** @addtogroup kernelutilities
 * @{ */

/** General iterator for structures that provide functions for the next and
previous structure
 *  in the datastructure and a "value" member. This template provides a
bidirectional, a constant
 *  bidirectional, a reverse bidirectional and a constant reverse bidirectional
iterator.
 *\brief An iterator applicable for many data structures
 *\param[in] originalT the original element type of the iterator
 *\param[in] Struct the datastructure that provides functions for the
next/previous datastructure
 *                  and a "value" member
 *\param[in] previous pointer to the member function used to iterate forward
 *\param[in] next pointer to the member function used to iterate backwards
 *\param[in] T the real element type of the iterator */
template<typename originalT,
         class Struct,
         Struct *(Struct::*FunctionPrev)() = &Struct::previous,
         Struct *(Struct::*FunctionNext)() = &Struct::next,
         typename T = originalT>
class Iterator
{
  /** All iterators must be friend in order to allow casts between some
iterator types */
  template<typename _originalT,
           class _Struct,
           _Struct *(_Struct::*_FunctionPrev)(),
           _Struct *(_Struct::*_FunctionNext)(),
           typename _T>
  friend class Iterator;

  /** The assignment operator is extern */
  template<typename _originalT,
          class _Struct,
          _Struct *(_Struct::*_FunctionPrev)(),
          _Struct *(_Struct::*_FunctionNext)(),
          typename _T1,
          typename _T2>
  friend bool operator == (const Iterator<_originalT, _Struct, _FunctionPrev,
_FunctionNext, _T1> &x1,
                           const Iterator<_originalT, _Struct, _FunctionPrev,
_FunctionNext, _T2> &x2);

  public:
    /** Type of the constant bidirectional iterator */
    typedef Iterator<originalT, Struct, FunctionPrev, FunctionNext, T const>   
 Const;
    /** Type of the reverse iterator */
    typedef Iterator<originalT, Struct, FunctionNext, FunctionPrev, T>         
 Reverse;
    /** Type of the constant reverse iterator */
    typedef Iterator<originalT, Struct, FunctionNext, FunctionPrev, T const>   
 ConstReverse;

    /** The default constructor constructs an invalid/unusable iterator */
    Iterator()
      : m_Node(){}
    /** The copy-constructor
     *\param[in] Iterator the reference object */
    Iterator(const Iterator &x)
      : m_Node(x.m_Node){}
    /** The constructor
     *\param[in] Iterator the reference object */
    template<typename T2>
    Iterator(const Iterator<originalT, Struct, FunctionPrev, FunctionNext, T2>
&x)
      : m_Node(x.m_Node){}
    /** Constructor from a pointer to an instance of the data structure
     *\param[in] Node pointer to an instance of the data structure */
    Iterator(Struct *Node)
      : m_Node(Node){}
    /** The destructor does nothing */
    ~Iterator(){}

    /** The assignment operator
     *\param[in] Iterator the reference object */
    Iterator &operator = (const Iterator &x)
    {
      m_Node = x.m_Node;
      return *this;
    }
    /** Preincrement operator */
    Iterator &operator ++ ()
    {
      m_Node = (m_Node->*FunctionNext)();
      return *this;
    }
    /** Predecrement operator */
    Iterator &operator -- ()
    {
      m_Node = (m_Node->*FunctionPrev)();
      return *this;
    }
    /** Dereference operator yields the element value */
    T &operator *()
    {
      return m_Node->value;
    }
    /** Dereference operator yields the element value */
    T &operator ->()
    {
      return m_Node->value;
    }

    /** Conversion Operator to a constant iterator */
    operator Const ()
    {
      return Const(m_Node);
    }

    /** Get the Node */
    Struct *__getNode()
    {
      return m_Node;
    }

  private:
    /** Pointer to the instance of the data structure or 0 */
    Struct *m_Node;
};

/** Comparison operator for the Iterator class
 *\param[in] x1 the first operand
 *\param[in] x2 the second operand
 *\return true, if the two iterator point to the same object, false otherwise
*/
template<typename originalT,
         class Struct,
         Struct *(Struct::*FunctionPrev)(),
         Struct *(Struct::*FunctionNext)(),
         typename T1,
         typename T2>
bool operator == (const Iterator<originalT, Struct, FunctionPrev, FunctionNext,
T1> &x1,
                  const Iterator<originalT, Struct, FunctionPrev, FunctionNext,
T2> &x2)
{
  if (x1.m_Node != x2.m_Node)return false;
  return true;
}

/** @} */

#endif


------------------------------------

<List.h>

/*
 * Copyright (c) 2008 James Molloy, Jörg Pfähler, Matthew Iselin
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef KERNEL_UTILITIES_LIST_H
#define KERNEL_UTILITIES_LIST_H

#include <utilities/template.h>
#include <processor/types.h>
#include <utilities/IteratorAdapter.h>
#include <utilities/Iterator.h>
#include <Log.h>
/** @addtogroup kernelutilities
 * @{ */

/** One node in the list
 *\brief One node in the list
 *\param[in] T the element type */
template<typename T>
struct _ListNode_t
{
  /** Get the next data structure in the list
   *\return pointer to the next data structure in the list */
  _ListNode_t *next()
    {return m_Next;}
  /** Get the previous data structure in the list
   *\return pointer to the previous data structure in the list */
  _ListNode_t *previous()
    {return m_Previous;}

  /** Pointer to the next node */
  _ListNode_t *m_Next;
  /** Pointer to the previous node */
  _ListNode_t *m_Previous;
  /** The value of the node */
  T value;
};

template<class T>
class List;

/** List template specialisation for void*
 *\brief List template specialisation for void* */
template<>
class List<void*>
{
  /** The data structure of the list's nodes */
  typedef _ListNode_t<void*> node_t;
  public:
    /** Type of the bidirectional iterator */
    typedef ::Iterator<void*, node_t>     Iterator;
    /** Type of the constant bidirectional iterator */
    typedef Iterator::Const               ConstIterator;
    /** Type of the reverse iterator */
    typedef Iterator::Reverse             ReverseIterator;
    /** Type of the constant reverse iterator */
    typedef Iterator::ConstReverse        ConstReverseIterator;

    /** Default constructor, does nothing */
    List();
    /** Copy-constructor
     *\param[in] x reference object */
    List(const List &x);
    /** Destructor, deallocates memory */
    ~List();

    /** Assignment operator
     *\param[in] x the object that should be copied */
    List &operator = (const List &x);

    /** Get the number of elements we reserved space for
     *\return number of elements we reserved space for */
    size_t size() const;
    /** Get the number of elements in the List */
    size_t count() const;
    /** Add a value to the end of the List
     *\param[in] value the value that should be added */
    void pushBack(void *value);
    /** Remove the last element from the List
     *\return the previously last element */
    void *popBack();
    /** Add a value to the front of the List
     *\param[in] value the value that should be added */
    void pushFront(void *value);
    /** Remove the first element in the List
     *\return the previously first element */
    void *popFront();
    /** Erase an element
     *\param[in] iterator the iterator that points to the element */
    Iterator erase(Iterator &Iter);

    /** Get an iterator pointing to the beginning of the List
     *\return iterator pointing to the beginning of the List */
    inline Iterator begin()
    {
      return Iterator(m_First);
    }
    /** Get a constant iterator pointing to the beginning of the List
     *\return constant iterator pointing to the beginning of the List */
    inline ConstIterator begin() const
    {
      return ConstIterator(m_First);
    }
    /** Get an iterator pointing to the end of the List + 1
     *\return iterator pointing to the end of the List + 1 */
    inline Iterator end()
    {
      return Iterator();
    }
    /** Get a constant iterator pointing to the end of the List + 1
     *\return constant iterator pointing to the end of the List + 1 */
    inline ConstIterator end() const
    {
      return ConstIterator();
    }
    /** Get an iterator pointing to the reverse beginning of the List
     *\return iterator pointing to the reverse beginning of the List */
    inline ReverseIterator rbegin()
    {
      return ReverseIterator(m_Last);
    }
    /** Get a constant iterator pointing to the reverse beginning of the List
     *\return constant iterator pointing to the reverse beginning of the List
*/
    inline ConstReverseIterator rbegin() const
    {
      return ConstReverseIterator(m_Last);
    }
    /** Get an iterator pointing to the reverse end of the List + 1
     *\return iterator pointing to the reverse end of the List + 1 */
    inline ReverseIterator rend()
    {
      return ReverseIterator();
    }
    /** Get a constant iterator pointing to the reverse end of the List + 1
     *\return constant iterator pointing to the reverse end of the List + 1 */
    inline ConstReverseIterator rend() const
    {
      return ConstReverseIterator();
    }

    /** Remove all elements from the List */
    void clear();
    /** Copy the content of a List into this List
     *\param[in] x the reference List */
    void assign(const List &x);

  private:
    /** The number of Nodes/Elements in the List */
    size_t m_Count;
    /** Pointer to the first Node in the List */
    node_t *m_First;
    /** Pointer to the last Node in the List */
    node_t *m_Last;
};

/** List template specialisation for pointers. Just forwards to the
 * void* template specialisation of List.
 *\brief List template specialisation for pointers */
template<class T>
class List<T*>
{
  public:
    /** Iterator */
    typedef IteratorAdapter<T*, List<void*>::Iterator>                   
Iterator;
    /** ConstIterator */
    typedef IteratorAdapter<T* const, List<void*>::ConstIterator>        
ConstIterator;
    /** ReverseIterator */
    typedef IteratorAdapter<T*, List<void*>::ReverseIterator>            
ReverseIterator;
    /** ConstReverseIterator */
    typedef IteratorAdapter<T* const, List<void*>::ConstReverseIterator> 
ConstReverseIterator;

    /** Default constructor, does nothing */
    inline List()
      : m_VoidList(){}
    /** Copy-constructor
     *\param[in] x reference object */
    inline List(const List &x)
      : m_VoidList(x.m_VoidList){}
    /** Destructor, deallocates memory */
    inline ~List()
      {}

    /** Assignment operator
     *\param[in] x the object that should be copied */
    inline List &operator = (const List &x)
    {
      m_VoidList = x.m_VoidList;
      return *this;
    }

    /** Get the number of elements we reserved space for
     *\return number of elements we reserved space for */
    inline size_t size() const
    {
      return m_VoidList.size();
    }
    /** Get the number of elements in the List */
    inline size_t count() const
    {
      return m_VoidList.count();
    }
    /** Add a value to the end of the List
     *\param[in] value the value that should be added */
    inline void pushBack(T *value)
    {
      m_VoidList.pushBack(reinterpret_cast<void*>(const_cast<typename
nonconst_type<T>::type*>(value)));
    }
    /** Remove the last element from the List
     *\return the previously last element */
    inline T *popBack()
    {
      return reinterpret_cast<T*>(m_VoidList.popBack());
    }
    /** Add a value to the front of the List
     *\param[in] value the value that should be added */
    inline void pushFront(T *value)
    {
      m_VoidList.pushFront(reinterpret_cast<void*>(value));
    }
    /** Remove the first element in the List
     *\return the previously first element */
    inline T *popFront()
    {
      return reinterpret_cast<T*>(m_VoidList.popFront());
    }
    /** Erase an element
     *\param[in] iterator the iterator that points to the element */
    inline Iterator erase(Iterator &Iter)
    {
      return Iterator(m_VoidList.erase(Iter.__getIterator()));
    }

    /** Get an iterator pointing to the beginning of the List
     *\return iterator pointing to the beginning of the List */
    inline Iterator begin()
    {
      return Iterator(m_VoidList.begin());
    }
    /** Get a constant iterator pointing to the beginning of the List
     *\return constant iterator pointing to the beginning of the List */
    inline ConstIterator begin() const
    {
      return ConstIterator(m_VoidList.begin());
    }
    /** Get an iterator pointing to the end of the List + 1
     *\return iterator pointing to the end of the List + 1 */
    inline Iterator end()
    {
      return Iterator(m_VoidList.end());
    }
    /** Get a constant iterator pointing to the end of the List + 1
     *\return constant iterator pointing to the end of the List + 1 */
    inline ConstIterator end() const
    {
      return ConstIterator(m_VoidList.end());
    }
    /** Get an iterator pointing to the reverse beginning of the List
     *\return iterator pointing to the reverse beginning of the List */
    inline ReverseIterator rbegin()
    {
      return ReverseIterator(m_VoidList.rbegin());
    }
    /** Get a constant iterator pointing to the reverse beginning of the List
     *\return constant iterator pointing to the reverse beginning of the List
*/
    inline ConstReverseIterator rbegin() const
    {
      return ConstReverseIterator(m_VoidList.rbegin());
    }
    /** Get an iterator pointing to the reverse end of the List + 1
     *\return iterator pointing to the reverse end of the List + 1 */
    inline ReverseIterator rend()
    {
      return ReverseIterator(m_VoidList.rend());
    }
    /** Get a constant iterator pointing to the reverse end of the List + 1
     *\return constant iterator pointing to the reverse end of the List + 1 */
    inline ConstReverseIterator rend() const
    {
      return ConstReverseIterator(m_VoidList.rend());
    }

    /** Remove all elements from the List */
    inline void clear()
    {
      m_VoidList.clear();
    }
    /** Copy the content of a List into this List
     *\param[in] x the reference List */
    inline void assign(const List &x)
    {
      m_VoidList.assign(x.m_VoidList);
    }

  private:
    /** The actual container */
    List<void*> m_VoidList;
};

/** @} */

#endif


-- 
           Summary: Code generated for calling pointer-to-member segfaults
                    when address of p-t-m is not 16-bit aligned
           Product: gcc
           Version: 4.3.2
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
        AssignedTo: unassigned at gcc dot gnu dot org
        ReportedBy: james at jamesmolloy dot co dot uk
 GCC build triplet: i686-elf
  GCC host triplet: x86_64-linux-gnu
GCC target triplet: i686-elf


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]