Add tests to verify QByteArray's zero termination

For data allocated and maintained by QByteArray, there's a guarantee
that data() is null-terminated. This holds true even for null and empty,
where logically the terminating character should never be dereferenced.

For tests that modify or generate QByteArrays, this ensures the
invariant is kept.

In the toFromHex() text, const-ness of temporary variables was dropped
to enable the test macro to be used, as the qualification didn't add
much to the test otherwise.

Change-Id: I7ee52e79e3a9df7de18c743f3698dab688e6bf0e
Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@nokia.com>
This commit is contained in:
João Abecasis 2012-03-29 00:24:50 +02:00 committed by Qt by Nokia
parent be4554d934
commit 4ef5a6269c

View File

@ -147,8 +147,40 @@ private slots:
void movablity_data();
void movablity();
void literals();
void zeroTermination_data();
void zeroTermination();
};
// Except for QByteArrays initialized with fromRawData(), QByteArray ensures
// that data() is null-terminated. VERIFY_ZERO_TERMINATION checks that
// invariant and goes further by testing that the null-character is in writable
// memory allocated by QByteArray. If the invariant is broken, tools like
// valgrind should be able to detect this.
#define VERIFY_ZERO_TERMINATION(ba) \
do { \
/* Statics could be anything, as can fromRawData() strings */ \
QByteArray::DataPtr baDataPtr = ba.data_ptr(); \
if (!baDataPtr->ref.isStatic() \
&& baDataPtr->offset == QByteArray().data_ptr()->offset) { \
int baSize = ba.size(); \
QCOMPARE(ba.constData()[baSize], '\0'); \
\
QByteArray baCopy(ba.constData(), baSize); \
if (!baDataPtr->ref.isShared()) { \
/* Don't detach, assumes no setSharable support */ \
char *baData = ba.data(); \
baData[baSize] = 'x'; \
\
QCOMPARE(ba.constData()[baSize], 'x'); \
QCOMPARE(ba, baCopy); \
\
baData[baSize] = '\0'; /* Sanity restored */ \
} \
} \
} while (false) \
/**/
struct StaticByteArrays {
struct Standard {
QByteArrayData data;
@ -224,8 +256,13 @@ void tst_QByteArray::qCompress_data()
void tst_QByteArray::qCompress()
{
QFETCH( QByteArray, ba );
QByteArray compressed = ::qCompress( ba );
QTEST( ::qUncompress( compressed ), "ba" );
QByteArray uncompressed = ::qUncompress(compressed);
QCOMPARE(uncompressed, ba);
VERIFY_ZERO_TERMINATION(compressed);
VERIFY_ZERO_TERMINATION(uncompressed);
}
void tst_QByteArray::qUncompressCorruptedData_data()
@ -257,9 +294,11 @@ void tst_QByteArray::qUncompressCorruptedData()
QByteArray res;
res = ::qUncompress(in);
QCOMPARE(res, QByteArray());
VERIFY_ZERO_TERMINATION(res);
res = ::qUncompress(in + "blah");
QCOMPARE(res, QByteArray());
VERIFY_ZERO_TERMINATION(res);
#else
QSKIP("This test freezes on this platform");
#endif
@ -269,7 +308,7 @@ void tst_QByteArray::qCompressionZeroTermination()
{
QString s = "Hello, I'm a string.";
QByteArray ba = ::qUncompress(::qCompress(s.toLocal8Bit()));
QVERIFY((int) *(ba.data() + ba.size()) == 0);
VERIFY_ZERO_TERMINATION(ba);
}
#endif
@ -289,6 +328,7 @@ void tst_QByteArray::constByteArray()
QVERIFY(cba.constData()[1] == 'b');
QVERIFY(cba.constData()[2] == 'c');
QVERIFY(cba.constData()[3] == '\0');
VERIFY_ZERO_TERMINATION(cba);
}
void tst_QByteArray::leftJustified()
@ -506,9 +546,11 @@ void tst_QByteArray::base64()
QByteArray arr = QByteArray::fromBase64(base64);
QCOMPARE(arr, rawdata);
VERIFY_ZERO_TERMINATION(arr);
QByteArray arr64 = rawdata.toBase64();
QCOMPARE(arr64, base64);
VERIFY_ZERO_TERMINATION(arr64);
}
//different from the previous test as the input are invalid
@ -733,17 +775,24 @@ void tst_QByteArray::chop()
src.chop(choplength);
QCOMPARE(src, expected);
VERIFY_ZERO_TERMINATION(src);
}
void tst_QByteArray::prepend()
{
QByteArray ba("foo");
QCOMPARE(ba.prepend((char*)0), QByteArray("foo"));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.prepend(QByteArray()), QByteArray("foo"));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.prepend("1"), QByteArray("1foo"));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.prepend(QByteArray("2")), QByteArray("21foo"));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.prepend('3'), QByteArray("321foo"));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.prepend("\0 ", 2), QByteArray::fromRawData("\0 321foo", 8));
VERIFY_ZERO_TERMINATION(ba);
}
void tst_QByteArray::prependExtended_data()
@ -767,11 +816,17 @@ void tst_QByteArray::prependExtended()
QCOMPARE(QByteArray("").prepend(array), QByteArray("data"));
QCOMPARE(array.prepend((char*)0), QByteArray("data"));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.prepend(QByteArray()), QByteArray("data"));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.prepend("1"), QByteArray("1data"));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.prepend(QByteArray("2")), QByteArray("21data"));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.prepend('3'), QByteArray("321data"));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.prepend("\0 ", 2), QByteArray::fromRawData("\0 321data", 9));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.size(), 9);
}
@ -779,12 +834,19 @@ void tst_QByteArray::append()
{
QByteArray ba("foo");
QCOMPARE(ba.append((char*)0), QByteArray("foo"));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.append(QByteArray()), QByteArray("foo"));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.append("1"), QByteArray("foo1"));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.append(QByteArray("2")), QByteArray("foo12"));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.append('3'), QByteArray("foo123"));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.append("\0"), QByteArray("foo123"));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.append("\0", 1), QByteArray::fromRawData("foo123\0", 7));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.size(), 7);
}
@ -801,12 +863,19 @@ void tst_QByteArray::appendExtended()
QCOMPARE(QByteArray("").append(array), QByteArray("data"));
QCOMPARE(array.append((char*)0), QByteArray("data"));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.append(QByteArray()), QByteArray("data"));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.append("1"), QByteArray("data1"));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.append(QByteArray("2")), QByteArray("data12"));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.append('3'), QByteArray("data123"));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.append("\0"), QByteArray("data123"));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.append("\0", 1), QByteArray::fromRawData("data123\0", 8));
VERIFY_ZERO_TERMINATION(array);
QCOMPARE(array.size(), 8);
}
@ -814,22 +883,28 @@ void tst_QByteArray::insert()
{
QByteArray ba("Meal");
QCOMPARE(ba.insert(1, QByteArray("ontr")), QByteArray("Montreal"));
VERIFY_ZERO_TERMINATION(ba);
QCOMPARE(ba.insert(ba.size(), "foo"), QByteArray("Montrealfoo"));
VERIFY_ZERO_TERMINATION(ba);
ba = QByteArray("13");
QCOMPARE(ba.insert(1, QByteArray("2")), QByteArray("123"));
VERIFY_ZERO_TERMINATION(ba);
ba = "ac";
QCOMPARE(ba.insert(1, 'b'), QByteArray("abc"));
QCOMPARE(ba.size(), 3);
VERIFY_ZERO_TERMINATION(ba);
ba = "ikl";
QCOMPARE(ba.insert(1, "j"), QByteArray("ijkl"));
QCOMPARE(ba.size(), 4);
VERIFY_ZERO_TERMINATION(ba);
ba = "ab";
QCOMPARE(ba.insert(1, "\0X\0", 3), QByteArray::fromRawData("a\0X\0b", 5));
QCOMPARE(ba.size(), 5);
VERIFY_ZERO_TERMINATION(ba);
}
void tst_QByteArray::insertExtended_data()
@ -842,6 +917,7 @@ void tst_QByteArray::insertExtended()
QFETCH(QByteArray, array);
QCOMPARE(array.insert(1, "i"), QByteArray("diata"));
QCOMPARE(array.size(), 5);
VERIFY_ZERO_TERMINATION(array);
}
void tst_QByteArray::remove_data()
@ -872,6 +948,7 @@ void tst_QByteArray::remove()
QFETCH(int, length);
QFETCH(QByteArray, expected);
QCOMPARE(src.remove(position, length), expected);
VERIFY_ZERO_TERMINATION(src);
}
void tst_QByteArray::replace_data()
@ -913,6 +990,8 @@ void tst_QByteArray::replace()
QCOMPARE(str1.replace(pos, len, after).constData(), expected.constData());
QCOMPARE(str2.replace(pos, len, after.data()), expected);
VERIFY_ZERO_TERMINATION(str1);
VERIFY_ZERO_TERMINATION(str2);
}
void tst_QByteArray::replaceWithSpecifiedLength()
@ -925,6 +1004,7 @@ void tst_QByteArray::replaceWithSpecifiedLength()
const char _expected[] = "zxc\0vbcdefghjk";
QByteArray expected(_expected,sizeof(_expected)-1);
QCOMPARE(ba,expected);
VERIFY_ZERO_TERMINATION(ba);
}
void tst_QByteArray::indexOf_data()
@ -1227,6 +1307,7 @@ void tst_QByteArray::appendAfterFromRawData()
data[0] = 'Y';
}
QVERIFY(arr.at(0) == 'X');
VERIFY_ZERO_TERMINATION(arr);
}
void tst_QByteArray::toFromHex_data()
@ -1298,15 +1379,17 @@ void tst_QByteArray::toFromHex()
QFETCH(QByteArray, hex_alt1);
{
const QByteArray th = str.toHex();
QByteArray th = str.toHex();
QCOMPARE(th.size(), hex.size());
QCOMPARE(th, hex);
VERIFY_ZERO_TERMINATION(th);
}
{
const QByteArray fh = QByteArray::fromHex(hex);
QByteArray fh = QByteArray::fromHex(hex);
QCOMPARE(fh.size(), str.size());
QCOMPARE(fh, str);
VERIFY_ZERO_TERMINATION(fh);
}
QCOMPARE(QByteArray::fromHex(hex_alt1), str);
@ -1319,14 +1402,17 @@ void tst_QByteArray::toFromPercentEncoding()
QByteArray data = arr.toPercentEncoding();
QCOMPARE(QString(data), QString("Qt%20is%20great%21"));
QCOMPARE(QByteArray::fromPercentEncoding(data), arr);
VERIFY_ZERO_TERMINATION(data);
data = arr.toPercentEncoding("! ", "Qt");
QCOMPARE(QString(data), QString("%51%74 is grea%74!"));
QCOMPARE(QByteArray::fromPercentEncoding(data), arr);
VERIFY_ZERO_TERMINATION(data);
data = arr.toPercentEncoding(QByteArray(), "abcdefghijklmnopqrstuvwxyz", 'Q');
QCOMPARE(QString(data), QString("Q51Q74Q20Q69Q73Q20Q67Q72Q65Q61Q74Q21"));
QCOMPARE(QByteArray::fromPercentEncoding(data, 'Q'), arr);
VERIFY_ZERO_TERMINATION(data);
// verify that to/from percent encoding preserves nullity
arr = "";
@ -1336,6 +1422,7 @@ void tst_QByteArray::toFromPercentEncoding()
QVERIFY(!arr.toPercentEncoding().isNull());
QVERIFY(QByteArray::fromPercentEncoding("").isEmpty());
QVERIFY(!QByteArray::fromPercentEncoding("").isNull());
VERIFY_ZERO_TERMINATION(arr);
arr = QByteArray();
QVERIFY(arr.isEmpty());
@ -1344,6 +1431,7 @@ void tst_QByteArray::toFromPercentEncoding()
QVERIFY(arr.toPercentEncoding().isNull());
QVERIFY(QByteArray::fromPercentEncoding(QByteArray()).isEmpty());
QVERIFY(QByteArray::fromPercentEncoding(QByteArray()).isNull());
VERIFY_ZERO_TERMINATION(arr);
}
void tst_QByteArray::fromPercentEncoding_data()
@ -1585,6 +1673,9 @@ void tst_QByteArray::repeated() const
QFETCH(int, count);
QCOMPARE(string.repeated(count), expected);
QByteArray repeats = string.repeated(count);
VERIFY_ZERO_TERMINATION(repeats);
}
void tst_QByteArray::repeated_data() const
@ -1676,6 +1767,7 @@ void tst_QByteArray::byteRefDetaching() const
copy[0] = 'S';
QCOMPARE(str, QByteArray("str"));
VERIFY_ZERO_TERMINATION(copy);
}
{
@ -1684,6 +1776,7 @@ void tst_QByteArray::byteRefDetaching() const
str[0] = 'S';
QCOMPARE(buf[0], char('s'));
VERIFY_ZERO_TERMINATION(str);
}
{
@ -1694,6 +1787,7 @@ void tst_QByteArray::byteRefDetaching() const
str[0] = 'S';
QCOMPARE(buf[0], char('s'));
VERIFY_ZERO_TERMINATION(str);
}
}
@ -1703,19 +1797,25 @@ void tst_QByteArray::reserve()
QByteArray qba;
qba.reserve(capacity);
QVERIFY(qba.capacity() == capacity);
char *data = qba.data();
VERIFY_ZERO_TERMINATION(qba);
char *data = qba.data();
for (int i = 0; i < capacity; i++) {
qba.resize(i);
QVERIFY(capacity == qba.capacity());
QVERIFY(data == qba.data());
VERIFY_ZERO_TERMINATION(qba);
}
QByteArray nil1, nil2;
nil1.reserve(0);
VERIFY_ZERO_TERMINATION(nil1);
nil2.squeeze();
VERIFY_ZERO_TERMINATION(nil2);
nil1.squeeze();
VERIFY_ZERO_TERMINATION(nil1);
nil2.reserve(0);
VERIFY_ZERO_TERMINATION(nil2);
}
void tst_QByteArray::reserveExtended_data()
@ -1726,12 +1826,16 @@ void tst_QByteArray::reserveExtended_data()
void tst_QByteArray::reserveExtended()
{
QFETCH(QByteArray, array);
array.reserve(1024);
QVERIFY(array.capacity() == 1024);
QCOMPARE(array, QByteArray("data"));
VERIFY_ZERO_TERMINATION(array);
array.squeeze();
QCOMPARE(array, QByteArray("data"));
QCOMPARE(array.capacity(), array.size());
VERIFY_ZERO_TERMINATION(array);
}
void tst_QByteArray::movablity_data()
@ -1824,6 +1928,7 @@ void tst_QByteArray::literals()
QVERIFY(str == "abcd");
QVERIFY(str.data_ptr()->ref.isStatic());
QVERIFY(str.data_ptr()->offset == sizeof(QByteArrayData));
VERIFY_ZERO_TERMINATION(str);
const char *s = str.constData();
QByteArray str2 = str;
@ -1831,14 +1936,27 @@ void tst_QByteArray::literals()
// detach on non const access
QVERIFY(str.data() != s);
VERIFY_ZERO_TERMINATION(str);
QVERIFY(str2.constData() == s);
QVERIFY(str2.data() != s);
VERIFY_ZERO_TERMINATION(str2);
#else
QSKIP("Only tested on c++0x compliant compiler or gcc");
#endif
}
void tst_QByteArray::zeroTermination_data()
{
movablity_data();
}
void tst_QByteArray::zeroTermination()
{
QFETCH(QByteArray, array);
VERIFY_ZERO_TERMINATION(array);
}
const char globalChar = '1';
QTEST_APPLESS_MAIN(tst_QByteArray)