Details
-
Bug
-
Resolution: Done
-
P3: Somewhat important
-
4.7.1, 5.1.0 Beta 1
-
None
-
Mandriva 2011
-
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::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 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.