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 Pick-to: 6.4 6.3 6.2 Task-number: QTBUG-104425 Change-Id: I666207fca7ab837ad27a247e504a40757ee8afab Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
e38c7618be
commit
854cb55987
@ -1762,23 +1762,28 @@ bool QHeaderView::restoreState(const QByteArray &state)
|
||||
Q_D(QHeaderView);
|
||||
if (state.isEmpty())
|
||||
return false;
|
||||
|
||||
for (const auto dataStreamVersion : {QDataStream::Qt_5_0, QDataStream::Qt_6_0}) {
|
||||
|
||||
QByteArray data = state;
|
||||
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||
stream.setVersion(QDataStream::Qt_5_0);
|
||||
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
|
||||
|| ver != 0) { // current version is 0
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d->read(stream)) {
|
||||
emit sortIndicatorChanged(d->sortIndicatorSection, d->sortIndicatorOrder );
|
||||
d->viewport->update();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // QT_NO_DATASTREAM
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user