diff options
author | Axel Spoerl <axel.spoerl@qt.io> | 2022-08-05 08:33:56 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2022-08-24 17:11:23 +0000 |
commit | 3638a46b67df366bda6a2f8c1c10f716a6fdc5eb (patch) | |
tree | 832bac38995822c975e332c406100ef2b537c418 | |
parent | 4e656d61421a5a36cfda232980e58aaf3a8937ce (diff) |
Make QHeaderView restore state from different stream versions
If restoring a QHeaderView state from a data stream with version Qt_5_0,
check alignment and resize mode properites for out-of-bound values.
If out of bounds, try QDataStream version Qt_6_0, which is used by KDE
apps compiled with 5.15.2 or 6.2.3.
QFileDialog stores settings in the same settings file across different
Qt versions, using different QDataStream versions. That makes
QFileDialog vulnerable to the issue (QTBUG-104962). A respective auto
test is added with this patch.
Fixes: QTBUG-104962
Task-number: QTBUG-104425
Change-Id: I666207fca7ab837ad27a247e504a40757ee8afab
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
(cherry picked from commit 854cb55987b3de3c8379db0e7e95b4c94d4e6588)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | src/widgets/itemviews/qheaderview.cpp | 42 | ||||
-rw-r--r-- | tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp | 42 |
2 files changed, 70 insertions, 14 deletions
diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp index 413857bf6c..dfb60a91ec 100644 --- a/src/widgets/itemviews/qheaderview.cpp +++ b/src/widgets/itemviews/qheaderview.cpp @@ -1762,22 +1762,27 @@ bool QHeaderView::restoreState(const QByteArray &state) Q_D(QHeaderView); if (state.isEmpty()) return false; - QByteArray data = state; - QDataStream stream(&data, QIODevice::ReadOnly); - stream.setVersion(QDataStream::Qt_5_0); - int marker; - int ver; - stream >> marker; - stream >> ver; - if (stream.status() != QDataStream::Ok + + for (const auto dataStreamVersion : {QDataStream::Qt_5_0, QDataStream::Qt_6_0}) { + + QByteArray data = state; + QDataStream stream(&data, QIODevice::ReadOnly); + stream.setVersion(dataStreamVersion); + int marker; + int ver; + stream >> marker; + stream >> ver; + if (stream.status() != QDataStream::Ok || marker != QHeaderViewPrivate::VersionMarker - || ver != 0) // current version is 0 - return false; + || ver != 0) { // current version is 0 + return false; + } - if (d->read(stream)) { - emit sortIndicatorChanged(d->sortIndicatorSection, d->sortIndicatorOrder ); - d->viewport->update(); - return true; + if (d->read(stream)) { + emit sortIndicatorChanged(d->sortIndicatorSection, d->sortIndicatorOrder ); + d->viewport->update(); + return true; + } } return false; } @@ -4131,6 +4136,15 @@ bool QHeaderViewPrivate::read(QDataStream &in) in >> global; + // Check parameter consistency + // Global orientation out of bounds? + if (global < 0 || global > QHeaderView::ResizeToContents) + return false; + + // Alignment out of bounds? + if (align < 0 || align > Qt::AlignVertical_Mask) + return false; + in >> sectionItemsIn; // In Qt4 we had a vector of spans where one span could hold information on more sections. // Now we have an itemvector where one items contains information about one section diff --git a/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp b/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp index 4ef1a0e36e..0ece2c1156 100644 --- a/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp +++ b/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp @@ -106,6 +106,10 @@ private slots: void dontShowCompleterOnRoot(); void nameFilterParsing_data(); void nameFilterParsing(); +#if QT_CONFIG(settings) + void settingsCompatibility_data(); + void settingsCompatibility(); +#endif private: void cleanupSettingsFile(); @@ -446,6 +450,44 @@ void tst_QFileDialog2::task180459_lastDirectory() delete dlg; } +#if QT_CONFIG(settings) +void tst_QFileDialog2::settingsCompatibility_data() +{ + QTest::addColumn<QString>("qtVersion"); + QTest::addColumn<QDataStream::Version>("dsVersion"); + QTest::newRow("6.2.3") << "6.2.3" << QDataStream::Qt_6_0; + QTest::newRow("6.5") << "6.5" << QDataStream::Qt_5_0; + QTest::newRow("15.5.2") << "5.15.2" << QDataStream::Qt_5_15; + QTest::newRow("15.5.9") << "5.15.9" << QDataStream::Qt_5_15; +} + +void tst_QFileDialog2::settingsCompatibility() +{ + static const QByteArray ba32 = QByteArrayLiteral("\x00\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xF7\x00\x00\x00\x04\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00""d\xFF\xFF\xFF\xFF\x00\x00\x00\x81\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x01\t\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00""B\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00n\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\xE8\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"); + static const QByteArray ba64 = QByteArrayLiteral("\x00\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xF7\x00\x00\x00\x04\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00""d\xFF\xFF\xFF\xFF\x00\x00\x00\x81\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x01\t\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00""B\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00n\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\xE8\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"); + QFETCH(QString, qtVersion); + QFETCH(QDataStream::Version, dsVersion); + // Create a header view, convert template to target format and store it in settings + { + QSettings settings(QSettings::UserScope, "QtProject"); + settings.beginGroup("FileDialog"); + settings.setValue("sidebarWidth", 93); // random value + settings.setValue("shortcuts", QStringList({settings.fileName(), "/tmp"})); + settings.setValue("qtVersion", qtVersion); + settings.setValue("treeViewHeader", dsVersion < QDataStream::Qt_6_0 ? ba32 : ba64); + settings.endGroup(); + } + // Create a file dialog, read settings write them back + { + QFileDialog fd; + } + // Read back settings and compare byte array + QSettings settings(QSettings::UserScope, "QtProject"); + settings.beginGroup("FileDialog"); + const QByteArray savedState = settings.value("treeViewHeader").toByteArray(); + QCOMPARE(savedState, ba32); +} +#endif class FilterDirModel : public QSortFilterProxyModel |