diff options
author | Liang Qi <liang.qi@qt.io> | 2020-03-31 12:30:18 +0200 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2020-03-31 16:24:30 +0200 |
commit | 7ed097b31f7e15812144c360021872cf8f11821e (patch) | |
tree | 0bc0ac535734fb4513728042b090d15eb86aaa33 | |
parent | 82a39f12fa50424fe792b4ff7e7764d98ebabe3e (diff) | |
parent | 947e1f45c762cf6d26bc8f241833689c6e636627 (diff) |
14 files changed, 386 insertions, 62 deletions
diff --git a/mkspecs/features/win32/opengl.prf b/mkspecs/features/win32/opengl.prf index f21848f941..8568a0c379 100644 --- a/mkspecs/features/win32/opengl.prf +++ b/mkspecs/features/win32/opengl.prf @@ -3,8 +3,9 @@ QT_FOR_CONFIG += gui defineTest(prependOpenGlLib) { path = $$QT.core.libs/$$QMAKE_PREFIX_STATICLIB$$1 ext = .$$QMAKE_EXTENSION_STATICLIB + !mingw|qtConfig(debug_and_release): debug_suffix = "d" QMAKE_LIBS_OPENGL_ES2 = $${path}$${ext} $$QMAKE_LIBS_OPENGL_ES2 - QMAKE_LIBS_OPENGL_ES2_DEBUG = $${path}d$${ext} $$QMAKE_LIBS_OPENGL_ES2_DEBUG + QMAKE_LIBS_OPENGL_ES2_DEBUG = $${path}$${debug_suffix}$${ext} $$QMAKE_LIBS_OPENGL_ES2_DEBUG export(QMAKE_LIBS_OPENGL_ES2) export(QMAKE_LIBS_OPENGL_ES2_DEBUG) } diff --git a/src/3rdparty/tinycbor/tests/parser/data.cpp b/src/3rdparty/tinycbor/tests/parser/data.cpp index 0ab0e47be4..3523c32167 100644 --- a/src/3rdparty/tinycbor/tests/parser/data.cpp +++ b/src/3rdparty/tinycbor/tests/parser/data.cpp @@ -338,7 +338,7 @@ void addValidationColumns() QTest::addColumn<CborError>("expectedError"); } -void addValidationData() +void addValidationData(size_t minInvalid = ~size_t(0)) { // illegal numbers are future extension points QTest::newRow("illegal-number-in-unsigned-1") << raw("\x81\x1c") << 0 << CborErrorIllegalNumber; @@ -488,26 +488,35 @@ void addValidationData() QTest::newRow("map-break-after-value-tag2") << raw("\x81\xbf\0\xd8\x20\xff") << 0 << CborErrorUnexpectedBreak; // check for pointer additions wrapping over the limit of the address space - CborError tooLargeOn32bit = (sizeof(void *) == 4) ? CborErrorDataTooLarge : CborErrorUnexpectedEOF; + auto wraparoundError = [minInvalid](uint64_t encodedSize) { + if (encodedSize > minInvalid) + return CborErrorDataTooLarge; + return CborErrorUnexpectedEOF; + }; + constexpr uint64_t FourGB = UINT32_MAX + UINT64_C(1); // on 32-bit systems, this is a -1 - QTest::newRow("bytearray-wraparound1") << raw("\x81\x5a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-wraparound1") << raw("\x81\x7a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-wraparound1") << raw("\x81\x5a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX); + QTest::newRow("string-wraparound1") << raw("\x81\x7a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX); // on 32-bit systems, a 4GB addition could be dropped - QTest::newRow("bytearray-wraparound2") << raw("\x81\x5b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit; - QTest::newRow("string-wraparound2") << raw("\x81\x7b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit; + QTest::newRow("bytearray-wraparound2") << raw("\x81\x5b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB); + QTest::newRow("string-wraparound2") << raw("\x81\x7b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB); // on 64-bit systems, this could be a -1 - QTest::newRow("bytearray-wraparound3") << raw("\x81\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; - QTest::newRow("string-wraparound3") << raw("\x81\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; + QTest::newRow("bytearray-wraparound3") << raw("\x81\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 + << wraparoundError(UINT64_MAX); + QTest::newRow("string-wraparound3") << raw("\x81\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 + << wraparoundError(UINT64_MAX); // ditto on chunks - QTest::newRow("bytearray-chunk-wraparound1") << raw("\x81\x5f\x5a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunk-wraparound1") << raw("\x81\x7f\x7a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunk-wraparound1") << raw("\x81\x5f\x5a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX); + QTest::newRow("string-chunk-wraparound1") << raw("\x81\x7f\x7a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX); // on 32-bit systems, a 4GB addition could be dropped - QTest::newRow("bytearray-chunk-wraparound2") << raw("\x81\x5f\x5b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit; - QTest::newRow("string-chunk-wraparound2") << raw("\x81\x7f\x7b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit; + QTest::newRow("bytearray-chunk-wraparound2") << raw("\x81\x5f\x5b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB); + QTest::newRow("string-chunk-wraparound2") << raw("\x81\x7f\x7b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB); // on 64-bit systems, this could be a -1 - QTest::newRow("bytearray-chunk-wraparound3") << raw("\x81\x5f\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; - QTest::newRow("string-chunk-wraparound3") << raw("\x81\x7f\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; + QTest::newRow("bytearray-chunk-wraparound3") << raw("\x81\x5f\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 + << wraparoundError(UINT64_MAX); + QTest::newRow("string-chunk-wraparound3") << raw("\x81\x7f\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 + << wraparoundError(UINT64_MAX); QTest::newRow("eof-after-array") << raw("\x81") << 0 << CborErrorUnexpectedEOF; QTest::newRow("eof-after-array2") << raw("\x81\x78\x20") << 0 << CborErrorUnexpectedEOF; diff --git a/src/corelib/serialization/qcborstreamreader.cpp b/src/corelib/serialization/qcborstreamreader.cpp index c983436606..ec385e0629 100644 --- a/src/corelib/serialization/qcborstreamreader.cpp +++ b/src/corelib/serialization/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. @@ -42,6 +42,7 @@ #define CBOR_NO_ENCODER_API #include <private/qcborcommon_p.h> +#include <private/qbytearray_p.h> #include <private/qnumeric_p.h> #include <private/qutfcodec_p.h> #include <qdebug.h> @@ -1055,6 +1056,10 @@ bool QCborStreamReader::next(int maxRecursion) } else if (isString() || isByteArray()) { auto r = _readByteArray_helper(); while (r.status == Ok) { + if (isString() && r.data.size() > MaxStringSize) { + d->handleError(CborErrorDataTooLarge); + break; + } if (isString() && !QUtf8::isValidUtf8(r.data, r.data.size()).isValidUtf8) { d->handleError(CborErrorInvalidUtf8TextString); break; @@ -1337,15 +1342,23 @@ QCborStreamReader::StringResult<QString> QCborStreamReader::_readString_helper() result.status = r.status; if (r.status == Ok) { - QTextCodec::ConverterState cs; - result.data = QUtf8::convertToUnicode(r.data, r.data.size(), &cs); - if (cs.invalidChars == 0 && cs.remainingChars == 0) - return result; + // See QUtf8::convertToUnicode() a detailed explanation of why this + // conversion uses the same number of words or less. + CborError err = CborNoError; + if (r.data.size() > MaxStringSize) { + err = CborErrorDataTooLarge; + } else { + QTextCodec::ConverterState cs; + result.data = QUtf8::convertToUnicode(r.data, r.data.size(), &cs); + if (cs.invalidChars != 0 || cs.remainingChars != 0) + err = CborErrorInvalidUtf8TextString; + } - d->handleError(CborErrorInvalidUtf8TextString); - result.data.clear(); - result.status = Error; - return result; + if (err) { + d->handleError(err); + result.data.clear(); + result.status = Error; + } } return result; } @@ -1373,6 +1386,10 @@ QCborStreamReader::StringResult<QByteArray> QCborStreamReader::_readByteArray_he qsizetype len = _currentStringChunkSize(); if (len < 0) return result; + if (len > MaxByteArraySize) { + d->handleError(CborErrorDataTooLarge); + return result; + } result.data.resize(len); auto r = readStringChunk(result.data.data(), len); diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index 98161094ae..63ee5f3158 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/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. @@ -53,6 +53,7 @@ #include <qendian.h> #include <qlocale.h> +#include <private/qbytearray_p.h> #include <private/qnumeric_p.h> #include <private/qsimd_p.h> @@ -1553,6 +1554,8 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) // and calculate the final size if (add_overflow(offset, increment, &newSize)) return -1; + if (newSize > MaxByteArraySize) + return -1; // since usedData <= data.size(), this can't overflow usedData += increment; @@ -1627,13 +1630,6 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) setErrorInReader(reader, { QCborError::DataTooLarge }); } - if (r.status == QCborStreamReader::Error) { - // There can only be errors if there was data to be read. - Q_ASSERT(e.flags & Element::HasByteData); - data.truncate(e.value); - return; - } - // update size if (e.flags & Element::HasByteData) { auto b = new (dataPtr() + e.value) ByteData; @@ -1645,6 +1641,21 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) Q_ASSERT(e.type == QCborValue::String); e.flags |= Element::StringIsAscii; } + + // check that this UTF-8 text string can be loaded onto a QString + if (e.type == QCborValue::String) { + if (Q_UNLIKELY(b->len > MaxStringSize)) { + setErrorInReader(reader, { QCborError::DataTooLarge }); + r.status = QCborStreamReader::Error; + } + } + } + + if (r.status == QCborStreamReader::Error) { + // There can only be errors if there was data to be read. + Q_ASSERT(e.flags & Element::HasByteData); + data.truncate(e.value); + return; } elements.append(e); diff --git a/src/corelib/text/qbytearray_p.h b/src/corelib/text/qbytearray_p.h index 3c6257f786..ffec6dca22 100644 --- a/src/corelib/text/qbytearray_p.h +++ b/src/corelib/text/qbytearray_p.h @@ -56,10 +56,9 @@ QT_BEGIN_NAMESPACE -enum { - // Define as enum to force inlining. Don't expose MaxAllocSize in a public header. - MaxByteArraySize = MaxAllocSize - sizeof(std::remove_pointer<QByteArray::DataPtr>::type) -}; +// -1 because of the terminating NUL +constexpr qsizetype MaxByteArraySize = MaxAllocSize - sizeof(std::remove_pointer<QByteArray::DataPtr>::type) - 1; +constexpr qsizetype MaxStringSize = (MaxAllocSize - sizeof(std::remove_pointer<QByteArray::DataPtr>::type)) / 2 - 1; QT_END_NAMESPACE diff --git a/src/testlib/qabstractitemmodeltester.cpp b/src/testlib/qabstractitemmodeltester.cpp index 2e05097122..a09d007a82 100644 --- a/src/testlib/qabstractitemmodeltester.cpp +++ b/src/testlib/qabstractitemmodeltester.cpp @@ -288,6 +288,12 @@ QAbstractItemModelTester::FailureReportingMode QAbstractItemModelTester::failure return d->failureReportingMode; } +bool QAbstractItemModelTester::verify(bool statement, const char *statementStr, const char *description, const char *file, int line) +{ + Q_D(QAbstractItemModelTester); + return d->verify(statement, statementStr, description, file, line); +} + QAbstractItemModelTesterPrivate::QAbstractItemModelTesterPrivate(QAbstractItemModel *model, QAbstractItemModelTester::FailureReportingMode failureReportingMode) : model(model), failureReportingMode(failureReportingMode), diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index ebef36d033..f8784f9428 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -1844,10 +1844,16 @@ void QAbstractItemView::mouseMoveEvent(QMouseEvent *event) || edit(index, NoEditTriggers, event)) return; - if (d->selectionMode != SingleSelection) - topLeft = d->pressedPosition - d->offset(); - else + if (d->selectionMode != SingleSelection) { + // Use the current selection start index if it is valid as this will be based on the + // start of the selection and not the last item being pressed which can be different + // when in extended selection + topLeft = d->currentSelectionStartIndex.isValid() + ? visualRect(d->currentSelectionStartIndex).center() + : d->pressedPosition - d->offset(); + } else { topLeft = bottomRight; + } d->checkMouseMove(index); diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 0239856ed9..48e407f917 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -505,7 +505,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ? QEvent::MouseButtonRelease : QEvent::MouseButtonPress; if (QApplicationPrivate::inPopupMode()) { - QWidget *activePopupWidget = QApplication::activePopupWidget(); + QPointer<QWidget> activePopupWidget = QApplication::activePopupWidget(); QPoint mapped = event->pos(); if (activePopupWidget != m_widget) mapped = activePopupWidget->mapFromGlobal(event->globalPos()); @@ -565,9 +565,11 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) #endif if ((event->type() != QEvent::MouseButtonPress) || !(event->flags().testFlag(Qt::MouseEventCreatedDoubleClick))) { - + // if the widget that was pressed is gone, then deliver move events without buttons + const auto buttons = event->type() == QEvent::MouseMove && qt_button_down == nullptr + ? Qt::NoButton : event->buttons(); QMouseEvent e(event->type(), widgetPos, event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers(), event->source()); + event->button(), buttons, event->modifiers(), event->source()); e.setTimestamp(event->timestamp()); QApplicationPrivate::sendMouseEvent(receiver, &e, receiver, receiver->window(), &qt_button_down, qt_last_mouse_receiver); qt_last_mouse_receiver = receiver; 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" |