diff options
Diffstat (limited to 'tests')
6 files changed, 293 insertions, 20 deletions
diff --git a/tests/auto/corelib/serialization/cborlargedatavalidation.cpp b/tests/auto/corelib/serialization/cborlargedatavalidation.cpp new file mode 100644 index 0000000000..9abfe0f575 --- /dev/null +++ b/tests/auto/corelib/serialization/cborlargedatavalidation.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <cbor.h> + +namespace { +// A QIODevice that supplies a fixed header followed by a large sequence of +// null bytes up until a pre-determined size. +class LargeIODevice final : public QIODevice +{ +public: + qint64 realSize; + QByteArray start; + + LargeIODevice(const QByteArray &start, qint64 size, QObject *parent = nullptr) + : QIODevice(parent), realSize(size), start(start) + {} + + qint64 size() const override { return realSize; } + bool isSequential() const override { return false; } + +protected: + qint64 readData(char *data, qint64 maxlen) override; + qint64 writeData(const char *, qint64) override { return -1; } +}; +}; + +qint64 LargeIODevice::readData(char *data, qint64 maxlen) +{ + qint64 p = pos(); + if (maxlen > realSize - p) + maxlen = realSize - p; + memset(data, '\0', maxlen); + + qint64 fromstart = start.size() - p; + if (fromstart > maxlen) + fromstart = maxlen; + else if (fromstart < 0) + fromstart = 0; + if (fromstart) + memcpy(data, start.constData() + p, fromstart); + return maxlen; +} + +void addValidationLargeData(qsizetype minInvalid, qsizetype maxInvalid) +{ + char toolong[2 + sizeof(qsizetype)] = { char(0x81) }; + for (qsizetype v = maxInvalid; v >= minInvalid; --v) { + // 0x5a for 32-bit, 0x5b for 64-bit + toolong[1] = sizeof(v) > 4 ? 0x5b : 0x5a; + qToBigEndian(v, toolong + 2); + + QTest::addRow("bytearray-too-big-for-qbytearray-%llx", v) + << QByteArray(toolong, sizeof(toolong)) << 0 << CborErrorDataTooLarge; + toolong[1] |= 0x20; + + // QCborStreamReader::readString copies to a QByteArray first + QTest::addRow("string-too-big-for-qbytearray-%llx", v) + << QByteArray(toolong, sizeof(toolong)) << 0 << CborErrorDataTooLarge; + } +} + +void addValidationHugeDevice(qsizetype byteArrayInvalid, qsizetype stringInvalid) +{ + qRegisterMetaType<QSharedPointer<QIODevice>>(); + QTest::addColumn<QSharedPointer<QIODevice>>("device"); + QTest::addColumn<CborError>("expectedError"); + + char buf[1 + sizeof(quint64)]; + auto device = [&buf](QCborStreamReader::Type t, quint64 size) { + buf[0] = quint8(t) | 0x1b; + qToBigEndian(size, buf + 1); + size += sizeof(buf); + QSharedPointer<QIODevice> p = + QSharedPointer<LargeIODevice>::create(QByteArray(buf, sizeof(buf)), size); + return p; + }; + + // do the exact limits + QTest::newRow("bytearray-just-too-big") + << device(QCborStreamReader::ByteArray, byteArrayInvalid) << CborErrorDataTooLarge; + QTest::newRow("string-just-too-big") + << device(QCborStreamReader::String, stringInvalid) << CborErrorDataTooLarge; + + auto addSize = [=](const char *sizename, qint64 size) { + if (byteArrayInvalid < size) + QTest::addRow("bytearray-%s", sizename) + << device(QCborStreamReader::ByteArray, size) << CborErrorDataTooLarge; + if (stringInvalid < size) + QTest::addRow("string-%s", sizename) + << device(QCborStreamReader::String, size) << CborErrorDataTooLarge; + }; + addSize("1GB", quint64(1) << 30); + addSize("2GB", quint64(1) << 31); + addSize("4GB", quint64(1) << 32); + addSize("max", std::numeric_limits<qint64>::max() - sizeof(buf)); +} diff --git a/tests/auto/corelib/serialization/qcborstreamreader/qcborstreamreader.pro b/tests/auto/corelib/serialization/qcborstreamreader/qcborstreamreader.pro index 5df331314a..b758de1a9e 100644 --- a/tests/auto/corelib/serialization/qcborstreamreader/qcborstreamreader.pro +++ b/tests/auto/corelib/serialization/qcborstreamreader/qcborstreamreader.pro @@ -1,4 +1,4 @@ -QT = core testlib +QT = core-private testlib TARGET = tst_qcborstreamreader CONFIG += testcase SOURCES += \ diff --git a/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp b/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp index 28d29168fb..f969bb9074 100644 --- a/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp +++ b/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 Intel Corporation. +** Copyright (C) 2020 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -40,6 +40,8 @@ #include <QtCore/qcborstream.h> #include <QtTest> +#include <QtCore/private/qbytearray_p.h> + class tst_QCborStreamReader : public QObject { Q_OBJECT @@ -73,6 +75,8 @@ private Q_SLOTS: void next(); void validation_data(); void validation(); + void hugeDeviceValidation_data(); + void hugeDeviceValidation(); void recursionLimit_data(); void recursionLimit(); @@ -902,16 +906,26 @@ void tst_QCborStreamReader::next() QVERIFY(doit("\xbf\x9f\1\xff\x9f" + data + "\xff\xff")); } +#include "../cborlargedatavalidation.cpp" + void tst_QCborStreamReader::validation_data() { + // Add QCborStreamReader-specific limitations due to use of QByteArray and + // QString, which are allocated by QArrayData::allocate(). + const qsizetype MaxInvalid = std::numeric_limits<QByteArray::size_type>::max(); + const qsizetype MinInvalid = MaxByteArraySize + 1; + addValidationColumns(); - addValidationData(); + addValidationData(MinInvalid); + addValidationLargeData(MinInvalid, MaxInvalid); } void tst_QCborStreamReader::validation() { QFETCH_GLOBAL(bool, useDevice); QFETCH(QByteArray, data); + QFETCH(CborError, expectedError); + QCborError error = { QCborError::Code(expectedError) }; QBuffer buffer(&data); QCborStreamReader reader(data); @@ -920,12 +934,39 @@ void tst_QCborStreamReader::validation() reader.setDevice(&buffer); } parse(reader, data); - QVERIFY(reader.lastError() != QCborError::NoError); + QCOMPARE(reader.lastError(), error); + + // next() should fail + reader.reset(); + QVERIFY(!reader.next()); + QCOMPARE(reader.lastError(), error); +} + +void tst_QCborStreamReader::hugeDeviceValidation_data() +{ + addValidationHugeDevice(MaxByteArraySize + 1, MaxStringSize + 1); +} + +void tst_QCborStreamReader::hugeDeviceValidation() +{ + QFETCH_GLOBAL(bool, useDevice); + if (!useDevice) + return; + + QFETCH(QSharedPointer<QIODevice>, device); + QFETCH(CborError, expectedError); + QCborError error = { QCborError::Code(expectedError) }; + + device->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + QCborStreamReader reader(device.data()); + + QVERIFY(parseOne(reader).isEmpty()); + QCOMPARE(reader.lastError(), error); // next() should fail reader.reset(); QVERIFY(!reader.next()); - QVERIFY(reader.lastError() != QCborError::NoError); + QCOMPARE(reader.lastError(), error); } static const int Recursions = 3; diff --git a/tests/auto/corelib/serialization/qcborvalue/qcborvalue.pro b/tests/auto/corelib/serialization/qcborvalue/qcborvalue.pro index 9dd67da1f0..4d01b290f5 100644 --- a/tests/auto/corelib/serialization/qcborvalue/qcborvalue.pro +++ b/tests/auto/corelib/serialization/qcborvalue/qcborvalue.pro @@ -1,4 +1,4 @@ -QT = core testlib +QT = core-private testlib TARGET = tst_qcborvalue CONFIG += testcase SOURCES += \ diff --git a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp index 49bb9cc144..6d8161c1f9 100644 --- a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp +++ b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 Intel Corporation. +** Copyright (C) 2020 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -40,6 +40,8 @@ #include <QtCore/qcborvalue.h> #include <QtTest> +#include <QtCore/private/qbytearray_p.h> + Q_DECLARE_METATYPE(QCborKnownTags) Q_DECLARE_METATYPE(QCborValue) Q_DECLARE_METATYPE(QCborValue::EncodingOptions) @@ -102,6 +104,8 @@ private slots: void fromCborStreamReaderIODevice(); void validation_data(); void validation(); + void hugeDeviceValidation_data(); + void hugeDeviceValidation(); void recursionLimit_data(); void recursionLimit(); void toDiagnosticNotation_data(); @@ -1689,39 +1693,75 @@ void tst_QCborValue::fromCborStreamReaderIODevice() fromCbor_common(doCheck); } +#include "../cborlargedatavalidation.cpp" + void tst_QCborValue::validation_data() { + // Add QCborStreamReader-specific limitations due to use of QByteArray and + // QString, which are allocated by QArrayData::allocate(). + const qsizetype MaxInvalid = std::numeric_limits<QByteArray::size_type>::max(); + const qsizetype MinInvalid = MaxByteArraySize + 1; addValidationColumns(); - addValidationData(); + addValidationData(MinInvalid); + addValidationLargeData(MinInvalid, MaxInvalid); // These tests say we have arrays and maps with very large item counts. // They are meant to ensure we don't pre-allocate a lot of memory // unnecessarily and possibly crash the application. The actual number of // elements in the stream is only 2, so we should get an unexpected EOF - // error. QCborValue internally uses 16 bytes per element, so we get to - // 2 GB at 2^27 elements. - QTest::addRow("very-large-array-no-overflow") << raw("\x9a\x07\xff\xff\xff" "\0\0"); - QTest::addRow("very-large-array-overflow1") << raw("\x9a\x40\0\0\0" "\0\0"); - - // this makes sure we don't accidentally clip to 32-bit: sending 2^32+2 elements - QTest::addRow("very-large-array-overflow2") << raw("\x9b\0\0\0\1""\0\0\0\2" "\0\0"); + // error. QCborValue internally uses 16 bytes per element, so we get to 2 + // GB at 2^27 elements (32-bit) or, theoretically, 2^63 bytes at 2^59 + // elements (64-bit). + if (sizeof(QVector<int>::size_type) == sizeof(int)) { + // 32-bit sizes (Qt 5 and 32-bit platforms) + QTest::addRow("very-large-array-no-overflow") << raw("\x9a\x07\xff\xff\xff" "\0\0") << 0 << CborErrorUnexpectedEOF; + QTest::addRow("very-large-array-overflow1") << raw("\x9a\x40\0\0\0" "\0\0") << 0 << CborErrorUnexpectedEOF; + + // this makes sure we don't accidentally clip to 32-bit: sending 2^32+2 elements + QTest::addRow("very-large-array-overflow2") << raw("\x9b\0\0\0\1""\0\0\0\2" "\0\0") << 0 << CborErrorDataTooLarge; + } else { + // 64-bit Qt 6 + QTest::addRow("very-large-array-no-overflow") << raw("\x9b\x07\xff\xff\xff" "\xff\xff\xff\xff" "\0\0"); + QTest::addRow("very-large-array-overflow") << raw("\x9b\x40\0\0\0" "\0\0\0\0" "\0\0"); + } } void tst_QCborValue::validation() { QFETCH(QByteArray, data); + QFETCH(CborError, expectedError); + QCborError error = { QCborError::Code(expectedError) }; - QCborParserError error; - QCborValue decoded = QCborValue::fromCbor(data, &error); - QVERIFY(error.error != QCborError{}); + QCborParserError parserError; + QCborValue decoded = QCborValue::fromCbor(data, &parserError); + QCOMPARE(parserError.error, error); if (data.startsWith('\x81')) { // decode without the array prefix - decoded = QCborValue::fromCbor(data.mid(1), &error); - QVERIFY(error.error != QCborError{}); + char *ptr = const_cast<char *>(data.constData()); + QByteArray mid = QByteArray::fromRawData(ptr + 1, data.size() - 1); + decoded = QCborValue::fromCbor(mid, &parserError); + QCOMPARE(parserError.error, error); } } +void tst_QCborValue::hugeDeviceValidation_data() +{ + addValidationHugeDevice(MaxByteArraySize + 1, MaxStringSize + 1); +} + +void tst_QCborValue::hugeDeviceValidation() +{ + QFETCH(QSharedPointer<QIODevice>, device); + QFETCH(CborError, expectedError); + QCborError error = { QCborError::Code(expectedError) }; + + device->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + QCborStreamReader reader(device.data()); + QCborValue decoded = QCborValue::fromCbor(reader); + QCOMPARE(reader.lastError(), error); +} + void tst_QCborValue::recursionLimit_data() { constexpr int RecursionAttempts = 4096; diff --git a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp index 0834d989e0..0bdf61e1e1 100644 --- a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp +++ b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp @@ -149,6 +149,7 @@ private slots: void currentFollowsIndexWidget(); void checkFocusAfterActivationChanges_data(); void checkFocusAfterActivationChanges(); + void dragSelectAfterNewPress(); private: static QAbstractItemView *viewFromString(const QByteArray &viewType, QWidget *parent = nullptr) { @@ -2453,5 +2454,62 @@ void tst_QAbstractItemView::checkFocusAfterActivationChanges() QVERIFY(view->hasFocus()); } +void tst_QAbstractItemView::dragSelectAfterNewPress() +{ + QStandardItemModel model; + for (int i = 0; i < 10; ++i) { + QStandardItem *item = new QStandardItem(QString::number(i)); + model.setItem(i, 0, item); + } + + QListView view; + view.setFixedSize(160, 650); // Minimum width for windows with frame on Windows 8 + view.setSelectionMode(QListView::ExtendedSelection); + view.setModel(&model); + centerOnScreen(&view); + moveCursorAway(&view); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QModelIndex index0 = model.index(0, 0); + QModelIndex index2 = model.index(2, 0); + + view.setCurrentIndex(index0); + QCOMPARE(view.currentIndex(), index0); + + // Select item 0 using a single click + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, + view.visualRect(index0).center()); + QCOMPARE(view.currentIndex(), index0); + + // Press to select item 2 + QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, + view.visualRect(index2).center()); + QCOMPARE(view.currentIndex(), index2); + + // Verify that the selection worked OK + QModelIndexList selected = view.selectionModel()->selectedIndexes(); + QCOMPARE(selected.count(), 3); + for (int i = 0; i < 2; ++i) + QVERIFY(selected.contains(model.index(i, 0))); + + QModelIndex index5 = model.index(5, 0); + const QPoint releasePos = view.visualRect(index5).center(); + // The mouse move event has to be created manually because the QTest framework does not + // contain a function for mouse moves with buttons pressed + QMouseEvent moveEvent2(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, + Qt::ShiftModifier); + const bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent2); + QVERIFY(moveEventReceived); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, releasePos); + QCOMPARE(view.currentIndex(), index5); + + // Verify that the selection worked OK + selected = view.selectionModel()->selectedIndexes(); + QCOMPARE(selected.count(), 6); + for (int i = 0; i < 5; ++i) + QVERIFY(selected.contains(model.index(i, 0))); +} + QTEST_MAIN(tst_QAbstractItemView) #include "tst_qabstractitemview.moc" |