summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2020-03-31 12:30:18 +0200
committerLiang Qi <liang.qi@qt.io>2020-03-31 16:24:30 +0200
commit7ed097b31f7e15812144c360021872cf8f11821e (patch)
tree0bc0ac535734fb4513728042b090d15eb86aaa33
parent82a39f12fa50424fe792b4ff7e7764d98ebabe3e (diff)
parent947e1f45c762cf6d26bc8f241833689c6e636627 (diff)
Merge "Merge remote-tracking branch 'origin/5.14' into 5.15"v5.15.0-beta3
-rw-r--r--mkspecs/features/win32/opengl.prf3
-rw-r--r--src/3rdparty/tinycbor/tests/parser/data.cpp37
-rw-r--r--src/corelib/serialization/qcborstreamreader.cpp35
-rw-r--r--src/corelib/serialization/qcborvalue.cpp27
-rw-r--r--src/corelib/text/qbytearray_p.h7
-rw-r--r--src/testlib/qabstractitemmodeltester.cpp6
-rw-r--r--src/widgets/itemviews/qabstractitemview.cpp12
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp8
-rw-r--r--tests/auto/corelib/serialization/cborlargedatavalidation.cpp134
-rw-r--r--tests/auto/corelib/serialization/qcborstreamreader/qcborstreamreader.pro2
-rw-r--r--tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp49
-rw-r--r--tests/auto/corelib/serialization/qcborvalue/qcborvalue.pro2
-rw-r--r--tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp68
-rw-r--r--tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp58
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"