Merge "Merge remote-tracking branch 'origin/5.14' into 5.15"

This commit is contained in:
Liang Qi 2020-03-31 12:30:18 +02:00
commit 7ed097b31f
14 changed files with 385 additions and 61 deletions

View File

@ -3,8 +3,9 @@ QT_FOR_CONFIG += gui
defineTest(prependOpenGlLib) { defineTest(prependOpenGlLib) {
path = $$QT.core.libs/$$QMAKE_PREFIX_STATICLIB$$1 path = $$QT.core.libs/$$QMAKE_PREFIX_STATICLIB$$1
ext = .$$QMAKE_EXTENSION_STATICLIB 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 = $${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)
export(QMAKE_LIBS_OPENGL_ES2_DEBUG) export(QMAKE_LIBS_OPENGL_ES2_DEBUG)
} }

View File

@ -338,7 +338,7 @@ void addValidationColumns()
QTest::addColumn<CborError>("expectedError"); QTest::addColumn<CborError>("expectedError");
} }
void addValidationData() void addValidationData(size_t minInvalid = ~size_t(0))
{ {
// illegal numbers are future extension points // illegal numbers are future extension points
QTest::newRow("illegal-number-in-unsigned-1") << raw("\x81\x1c") << 0 << CborErrorIllegalNumber; 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; 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 // 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 // on 32-bit systems, this is a -1
QTest::newRow("bytearray-wraparound1") << raw("\x81\x5a\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 << CborErrorUnexpectedEOF; 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 // 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("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 << tooLargeOn32bit; 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 // 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("bytearray-wraparound3") << raw("\x81\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0
QTest::newRow("string-wraparound3") << raw("\x81\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; << 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 // ditto on chunks
QTest::newRow("bytearray-chunk-wraparound1") << raw("\x81\x5f\x5a\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 << CborErrorUnexpectedEOF; 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 // 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("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 << tooLargeOn32bit; 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 // 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("bytearray-chunk-wraparound3") << raw("\x81\x5f\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0
QTest::newRow("string-chunk-wraparound3") << raw("\x81\x7f\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; << 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-array") << raw("\x81") << 0 << CborErrorUnexpectedEOF;
QTest::newRow("eof-after-array2") << raw("\x81\x78\x20") << 0 << CborErrorUnexpectedEOF; QTest::newRow("eof-after-array2") << raw("\x81\x78\x20") << 0 << CborErrorUnexpectedEOF;

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2018 Intel Corporation. ** Copyright (C) 2020 Intel Corporation.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the QtCore module of the Qt Toolkit. ** This file is part of the QtCore module of the Qt Toolkit.
@ -42,6 +42,7 @@
#define CBOR_NO_ENCODER_API #define CBOR_NO_ENCODER_API
#include <private/qcborcommon_p.h> #include <private/qcborcommon_p.h>
#include <private/qbytearray_p.h>
#include <private/qnumeric_p.h> #include <private/qnumeric_p.h>
#include <private/qutfcodec_p.h> #include <private/qutfcodec_p.h>
#include <qdebug.h> #include <qdebug.h>
@ -1055,6 +1056,10 @@ bool QCborStreamReader::next(int maxRecursion)
} else if (isString() || isByteArray()) { } else if (isString() || isByteArray()) {
auto r = _readByteArray_helper(); auto r = _readByteArray_helper();
while (r.status == Ok) { while (r.status == Ok) {
if (isString() && r.data.size() > MaxStringSize) {
d->handleError(CborErrorDataTooLarge);
break;
}
if (isString() && !QUtf8::isValidUtf8(r.data, r.data.size()).isValidUtf8) { if (isString() && !QUtf8::isValidUtf8(r.data, r.data.size()).isValidUtf8) {
d->handleError(CborErrorInvalidUtf8TextString); d->handleError(CborErrorInvalidUtf8TextString);
break; break;
@ -1337,15 +1342,23 @@ QCborStreamReader::StringResult<QString> QCborStreamReader::_readString_helper()
result.status = r.status; result.status = r.status;
if (r.status == Ok) { if (r.status == Ok) {
QTextCodec::ConverterState cs; // See QUtf8::convertToUnicode() a detailed explanation of why this
result.data = QUtf8::convertToUnicode(r.data, r.data.size(), &cs); // conversion uses the same number of words or less.
if (cs.invalidChars == 0 && cs.remainingChars == 0) CborError err = CborNoError;
return result; 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); if (err) {
result.data.clear(); d->handleError(err);
result.status = Error; result.data.clear();
return result; result.status = Error;
}
} }
return result; return result;
} }
@ -1373,6 +1386,10 @@ QCborStreamReader::StringResult<QByteArray> QCborStreamReader::_readByteArray_he
qsizetype len = _currentStringChunkSize(); qsizetype len = _currentStringChunkSize();
if (len < 0) if (len < 0)
return result; return result;
if (len > MaxByteArraySize) {
d->handleError(CborErrorDataTooLarge);
return result;
}
result.data.resize(len); result.data.resize(len);
auto r = readStringChunk(result.data.data(), len); auto r = readStringChunk(result.data.data(), len);

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2018 Intel Corporation. ** Copyright (C) 2020 Intel Corporation.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the QtCore module of the Qt Toolkit. ** This file is part of the QtCore module of the Qt Toolkit.
@ -53,6 +53,7 @@
#include <qendian.h> #include <qendian.h>
#include <qlocale.h> #include <qlocale.h>
#include <private/qbytearray_p.h>
#include <private/qnumeric_p.h> #include <private/qnumeric_p.h>
#include <private/qsimd_p.h> #include <private/qsimd_p.h>
@ -1553,6 +1554,8 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader)
// and calculate the final size // and calculate the final size
if (add_overflow(offset, increment, &newSize)) if (add_overflow(offset, increment, &newSize))
return -1; return -1;
if (newSize > MaxByteArraySize)
return -1;
// since usedData <= data.size(), this can't overflow // since usedData <= data.size(), this can't overflow
usedData += increment; usedData += increment;
@ -1627,13 +1630,6 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader)
setErrorInReader(reader, { QCborError::DataTooLarge }); 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 // update size
if (e.flags & Element::HasByteData) { if (e.flags & Element::HasByteData) {
auto b = new (dataPtr() + e.value) ByteData; auto b = new (dataPtr() + e.value) ByteData;
@ -1645,6 +1641,21 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader)
Q_ASSERT(e.type == QCborValue::String); Q_ASSERT(e.type == QCborValue::String);
e.flags |= Element::StringIsAscii; 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); elements.append(e);

View File

@ -56,10 +56,9 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
enum { // -1 because of the terminating NUL
// Define as enum to force inlining. Don't expose MaxAllocSize in a public header. constexpr qsizetype MaxByteArraySize = MaxAllocSize - sizeof(std::remove_pointer<QByteArray::DataPtr>::type) - 1;
MaxByteArraySize = MaxAllocSize - sizeof(std::remove_pointer<QByteArray::DataPtr>::type) constexpr qsizetype MaxStringSize = (MaxAllocSize - sizeof(std::remove_pointer<QByteArray::DataPtr>::type)) / 2 - 1;
};
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -288,6 +288,12 @@ QAbstractItemModelTester::FailureReportingMode QAbstractItemModelTester::failure
return d->failureReportingMode; 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) QAbstractItemModelTesterPrivate::QAbstractItemModelTesterPrivate(QAbstractItemModel *model, QAbstractItemModelTester::FailureReportingMode failureReportingMode)
: model(model), : model(model),
failureReportingMode(failureReportingMode), failureReportingMode(failureReportingMode),

View File

@ -1844,10 +1844,16 @@ void QAbstractItemView::mouseMoveEvent(QMouseEvent *event)
|| edit(index, NoEditTriggers, event)) || edit(index, NoEditTriggers, event))
return; return;
if (d->selectionMode != SingleSelection) if (d->selectionMode != SingleSelection) {
topLeft = d->pressedPosition - d->offset(); // Use the current selection start index if it is valid as this will be based on the
else // 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; topLeft = bottomRight;
}
d->checkMouseMove(index); d->checkMouseMove(index);

View File

@ -505,7 +505,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ? QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ?
QEvent::MouseButtonRelease : QEvent::MouseButtonPress; QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
if (QApplicationPrivate::inPopupMode()) { if (QApplicationPrivate::inPopupMode()) {
QWidget *activePopupWidget = QApplication::activePopupWidget(); QPointer<QWidget> activePopupWidget = QApplication::activePopupWidget();
QPoint mapped = event->pos(); QPoint mapped = event->pos();
if (activePopupWidget != m_widget) if (activePopupWidget != m_widget)
mapped = activePopupWidget->mapFromGlobal(event->globalPos()); mapped = activePopupWidget->mapFromGlobal(event->globalPos());
@ -565,9 +565,11 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
#endif #endif
if ((event->type() != QEvent::MouseButtonPress) if ((event->type() != QEvent::MouseButtonPress)
|| !(event->flags().testFlag(Qt::MouseEventCreatedDoubleClick))) { || !(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(), 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()); e.setTimestamp(event->timestamp());
QApplicationPrivate::sendMouseEvent(receiver, &e, receiver, receiver->window(), &qt_button_down, qt_last_mouse_receiver); QApplicationPrivate::sendMouseEvent(receiver, &e, receiver, receiver->window(), &qt_button_down, qt_last_mouse_receiver);
qt_last_mouse_receiver = receiver; qt_last_mouse_receiver = receiver;

View File

@ -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));
}

View File

@ -1,4 +1,4 @@
QT = core testlib QT = core-private testlib
TARGET = tst_qcborstreamreader TARGET = tst_qcborstreamreader
CONFIG += testcase CONFIG += testcase
SOURCES += \ SOURCES += \

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2018 Intel Corporation. ** Copyright (C) 2020 Intel Corporation.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the QtCore module of the Qt Toolkit. ** This file is part of the QtCore module of the Qt Toolkit.
@ -40,6 +40,8 @@
#include <QtCore/qcborstream.h> #include <QtCore/qcborstream.h>
#include <QtTest> #include <QtTest>
#include <QtCore/private/qbytearray_p.h>
class tst_QCborStreamReader : public QObject class tst_QCborStreamReader : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -73,6 +75,8 @@ private Q_SLOTS:
void next(); void next();
void validation_data(); void validation_data();
void validation(); void validation();
void hugeDeviceValidation_data();
void hugeDeviceValidation();
void recursionLimit_data(); void recursionLimit_data();
void recursionLimit(); void recursionLimit();
@ -902,16 +906,26 @@ void tst_QCborStreamReader::next()
QVERIFY(doit("\xbf\x9f\1\xff\x9f" + data + "\xff\xff")); QVERIFY(doit("\xbf\x9f\1\xff\x9f" + data + "\xff\xff"));
} }
#include "../cborlargedatavalidation.cpp"
void tst_QCborStreamReader::validation_data() 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(); addValidationColumns();
addValidationData(); addValidationData(MinInvalid);
addValidationLargeData(MinInvalid, MaxInvalid);
} }
void tst_QCborStreamReader::validation() void tst_QCborStreamReader::validation()
{ {
QFETCH_GLOBAL(bool, useDevice); QFETCH_GLOBAL(bool, useDevice);
QFETCH(QByteArray, data); QFETCH(QByteArray, data);
QFETCH(CborError, expectedError);
QCborError error = { QCborError::Code(expectedError) };
QBuffer buffer(&data); QBuffer buffer(&data);
QCborStreamReader reader(data); QCborStreamReader reader(data);
@ -920,12 +934,39 @@ void tst_QCborStreamReader::validation()
reader.setDevice(&buffer); reader.setDevice(&buffer);
} }
parse(reader, data); parse(reader, data);
QVERIFY(reader.lastError() != QCborError::NoError); QCOMPARE(reader.lastError(), error);
// next() should fail // next() should fail
reader.reset(); reader.reset();
QVERIFY(!reader.next()); QVERIFY(!reader.next());
QVERIFY(reader.lastError() != QCborError::NoError); 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());
QCOMPARE(reader.lastError(), error);
} }
static const int Recursions = 3; static const int Recursions = 3;

View File

@ -1,4 +1,4 @@
QT = core testlib QT = core-private testlib
TARGET = tst_qcborvalue TARGET = tst_qcborvalue
CONFIG += testcase CONFIG += testcase
SOURCES += \ SOURCES += \

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2018 Intel Corporation. ** Copyright (C) 2020 Intel Corporation.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the QtCore module of the Qt Toolkit. ** This file is part of the QtCore module of the Qt Toolkit.
@ -40,6 +40,8 @@
#include <QtCore/qcborvalue.h> #include <QtCore/qcborvalue.h>
#include <QtTest> #include <QtTest>
#include <QtCore/private/qbytearray_p.h>
Q_DECLARE_METATYPE(QCborKnownTags) Q_DECLARE_METATYPE(QCborKnownTags)
Q_DECLARE_METATYPE(QCborValue) Q_DECLARE_METATYPE(QCborValue)
Q_DECLARE_METATYPE(QCborValue::EncodingOptions) Q_DECLARE_METATYPE(QCborValue::EncodingOptions)
@ -102,6 +104,8 @@ private slots:
void fromCborStreamReaderIODevice(); void fromCborStreamReaderIODevice();
void validation_data(); void validation_data();
void validation(); void validation();
void hugeDeviceValidation_data();
void hugeDeviceValidation();
void recursionLimit_data(); void recursionLimit_data();
void recursionLimit(); void recursionLimit();
void toDiagnosticNotation_data(); void toDiagnosticNotation_data();
@ -1689,39 +1693,75 @@ void tst_QCborValue::fromCborStreamReaderIODevice()
fromCbor_common(doCheck); fromCbor_common(doCheck);
} }
#include "../cborlargedatavalidation.cpp"
void tst_QCborValue::validation_data() 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(); addValidationColumns();
addValidationData(); addValidationData(MinInvalid);
addValidationLargeData(MinInvalid, MaxInvalid);
// These tests say we have arrays and maps with very large item counts. // 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 // They are meant to ensure we don't pre-allocate a lot of memory
// unnecessarily and possibly crash the application. The actual number of // unnecessarily and possibly crash the application. The actual number of
// elements in the stream is only 2, so we should get an unexpected EOF // 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 // error. QCborValue internally uses 16 bytes per element, so we get to 2
// 2 GB at 2^27 elements. // GB at 2^27 elements (32-bit) or, theoretically, 2^63 bytes at 2^59
QTest::addRow("very-large-array-no-overflow") << raw("\x9a\x07\xff\xff\xff" "\0\0"); // elements (64-bit).
QTest::addRow("very-large-array-overflow1") << raw("\x9a\x40\0\0\0" "\0\0"); 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 // 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"); 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() void tst_QCborValue::validation()
{ {
QFETCH(QByteArray, data); QFETCH(QByteArray, data);
QFETCH(CborError, expectedError);
QCborError error = { QCborError::Code(expectedError) };
QCborParserError error; QCborParserError parserError;
QCborValue decoded = QCborValue::fromCbor(data, &error); QCborValue decoded = QCborValue::fromCbor(data, &parserError);
QVERIFY(error.error != QCborError{}); QCOMPARE(parserError.error, error);
if (data.startsWith('\x81')) { if (data.startsWith('\x81')) {
// decode without the array prefix // decode without the array prefix
decoded = QCborValue::fromCbor(data.mid(1), &error); char *ptr = const_cast<char *>(data.constData());
QVERIFY(error.error != QCborError{}); 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() void tst_QCborValue::recursionLimit_data()
{ {
constexpr int RecursionAttempts = 4096; constexpr int RecursionAttempts = 4096;

View File

@ -149,6 +149,7 @@ private slots:
void currentFollowsIndexWidget(); void currentFollowsIndexWidget();
void checkFocusAfterActivationChanges_data(); void checkFocusAfterActivationChanges_data();
void checkFocusAfterActivationChanges(); void checkFocusAfterActivationChanges();
void dragSelectAfterNewPress();
private: private:
static QAbstractItemView *viewFromString(const QByteArray &viewType, QWidget *parent = nullptr) static QAbstractItemView *viewFromString(const QByteArray &viewType, QWidget *parent = nullptr)
{ {
@ -2453,5 +2454,62 @@ void tst_QAbstractItemView::checkFocusAfterActivationChanges()
QVERIFY(view->hasFocus()); 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) QTEST_MAIN(tst_QAbstractItemView)
#include "tst_qabstractitemview.moc" #include "tst_qabstractitemview.moc"