Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-24345

QBitArray initialized with negative size.

    XMLWordPrintable

Details

    • 9aa24645eb09be9e1d05060b2d976327726cb747

    Description

      The problem came from the QTableView, where a QBitArray is used to mark the drawn visible cells. Somehow (which is a different story) the number of visible cells was evaluated as negative, and QBitArray was initialized with a negative value. An expected behavior would be an assertion failure, but the code proceeded and a while later failed. Now, here is a small test:

      QBitArray ba (-208);
      qDebug () << ba.size ();
      for (int i = 0; i < ba.size (); i++)
      {
         qDebug () << ba.testBit (i);
         ba.setBit (i);
         qDebug () << ba.testBit (i);
      }
      

      Try running this, preferably within the valgrind. Here is what I get:

       
      48 
      false 
      true 
      false 
      true 
      ......
      true 
      false 
      true 
      *** glibc detected *** ./q8.sq: munmap_chunk(): invalid pointer: 0x0943b2b0 ***
      ======= Backtrace: =========
      /lib/i686/libc.so.6(+0x6add1)[0xb72d9dd1]
      /usr/lib/libstdc++.so.6(_ZdlPv+0x21)[0xb74bafa1]
      ./q8.sq[0x8052ccc]
      ./q8.sq[0x8050969]
      /lib/i686/libc.so.6(__libc_start_main+0xe6)[0xb7285b96]
      ./q8.sq[0x8050491]
      ======= Memory map: ========
      08048000-08db7000 r-xp 00000000 08:06 9969923    /home/brugeman/upg/p8/src/q8.sq/q8.sq
      08db7000-08dde000 rw-p 00d6f000 08:06 9969923    /home/brugeman/upg/p8/src/q8.sq/q8.sq
      08dde000-08de3000 rw-p 00000000 00:00 0 
      0943b000-0945c000 rw-p 00000000 00:00 0          [heap]
      b7101000-b7103000 rw-p 00000000 00:00 0 
      .........
      

      If you change the size to some other value, say -192, valgrind might say:

       
      ==5205== Conditional jump or move depends on uninitialised value(s)
      ==5205==    at 0x8052A7E: QDebug::operator<<(bool) (qdebug.h:97)
      ==5205==    by 0x8050857: main (main.cpp:515)
      ==5205== 
      false 
      true 
      false 
      true 
      .......
      false 
      true 
      ==5205== Invalid read of size 1
      ==5205==    at 0x80527DC: QBitArray::testBit(int) const (qbitarray.h:120)
      ==5205==    by 0x8050825: main (main.cpp:515)
      ==5205==  Address 0x469c544 is 0 bytes after a block of size 20 alloc'd
      ==5205==    at 0x4023D7C: malloc (vg_replace_malloc.c:195)
      ==5205==    by 0x8928522: qMalloc(unsigned int) (qmalloc.cpp:55)
      ==5205==    by 0x8932EC6: QByteArray::realloc(int) (qbytearray.cpp:1429)
      ==5205==    by 0x80528B9: QByteArray::detach() (qbytearray.h:418)
      ==5205==    by 0x80528CC: QByteArray::data() (qbytearray.h:412)
      ==5205==    by 0x89306D4: QBitArray::QBitArray(int, bool) (qbitarray.cpp:130)
      ==5205==    by 0x8050804: main (main.cpp:512)
      ==5205== 
      false 
      ==5205== Invalid read of size 1
      ==5205==    at 0x8052941: QBitArray::setBit(int) (qbitarray.h:124)
      ==5205==    by 0x80508A1: main (main.cpp:516)
      ==5205==  Address 0x469c544 is 0 bytes after a block of size 20 alloc'd
      ==5205==    at 0x4023D7C: malloc (vg_replace_malloc.c:195)
      ==5205==    by 0x8928522: qMalloc(unsigned int) (qmalloc.cpp:55)
      ==5205==    by 0x8932EC6: QByteArray::realloc(int) (qbytearray.cpp:1429)
      ==5205==    by 0x80528B9: QByteArray::detach() (qbytearray.h:418)
      ==5205==    by 0x80528CC: QByteArray::data() (qbytearray.h:412)
      ==5205==    by 0x89306D4: QBitArray::QBitArray(int, bool) (qbitarray.cpp:130)
      ==5205==    by 0x8050804: main (main.cpp:512)
      ==5205== 
      ==5205== Invalid write of size 1
      ==5205==    at 0x8052953: QBitArray::setBit(int) (qbitarray.h:124)
      ==5205==    by 0x80508A1: main (main.cpp:516)
      ==5205==  Address 0x469c544 is 0 bytes after a block of size 20 alloc'd
      ==5205==    at 0x4023D7C: malloc (vg_replace_malloc.c:195)
      ==5205==    by 0x8928522: qMalloc(unsigned int) (qmalloc.cpp:55)
      ==5205==    by 0x8932EC6: QByteArray::realloc(int) (qbytearray.cpp:1429)
      ==5205==    by 0x80528B9: QByteArray::detach() (qbytearray.h:418)
      ==5205==    by 0x80528CC: QByteArray::data() (qbytearray.h:412)
      ==5205==    by 0x89306D4: QBitArray::QBitArray(int, bool) (qbitarray.cpp:130)
      ==5205==    by 0x8050804: main (main.cpp:512)
      ....
      

      Now, here is the cause (Qt sources with my comments):

      qbitarray.cpp:123
      QBitArray::QBitArray(int size, bool value)
      {
          if (!size) {
              d.resize(0);
              return;
          }
          d.resize(1 + (size+7)/8); // Here a negative value is given to QByteArray's resize
          // A negative size makes an empty QByteArray d.
      
          // Now, we take pointer to QByteArray's data, which points to a single allocated byte - the terminating zero
          uchar* c = reinterpret_cast<uchar*>(d.data());
      
          // This line does nothing as d.size() == 0
          memset(c, value ? 0xff : 0, d.size());
      
          // Here we write some positive value to the first byte of the buffer (which is the terminating zero)
          // So, remember that FIRST BYTE OF BUFFER CONTAINS SOME VALUE (<=0xFF)
          *c = d.size()*8 - size;
          if (value && size && size % 8)
              *(c+1+size/8) &= (1 << (size%8)) - 1;
      }
      
      

      Now, look at the size of QBitArray:

      qbitarray.h:67
      // QBitArray member function to evaluate the number of stored bits.
      // Here, d.size () is zero, so the returned size will depend on the FIRST BYTE of buffer.
      // Remember that we've written some value there, treating is as a uchar?
      // Here, d.constData () is pointer to char, so if we've written there, say, 0x7F,
      // -*d.constData() will evaluate to some >0 number.
      inline int size() const { return (d.size() << 3) - *d.constData(); }
      

      Finally, look at setBit and testBit. They both check whether provided index of the bit is less than
      the size, but we know that size is non-zero. And, we also know that there is only a single byte allocated
      in the buffer, so here goes the access out of range of allocated memory.

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            mitch_curtis Mitch Curtis
            cerber Artur Brugeman
            Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes