From a4018bef7341168c4ce761b7ebf1349650d44ee9 Mon Sep 17 00:00:00 2001 From: "Dr.-Ing. Carsten Grimm" <16454511+carsten-grimm@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:36:45 +0100 Subject: [PATCH] [qt5-base] appy offical patch for CVE-2024-25580 (#36820) Fixes #36819. - [x] Changes comply with the [maintainer guide](https://github.com/microsoft/vcpkg-docs/blob/main/vcpkg/contributing/maintainer-guide.md). - [x] SHA512s are updated for each updated download. - [x] The "supports" clause reflects platforms that may be fixed by this new version. - [x] Any fixed [CI baseline](https://github.com/microsoft/vcpkg/blob/master/scripts/ci.baseline.txt) entries are removed from that file. - [x] Any patches that are no longer applied are deleted from the port's directory. - [x] The version database is fixed by rerunning `./vcpkg x-add-version --all` and committing the result. - [x] Only one version is added to each modified port's versions file. This pull request applies the official patch for CVE-2024-25580 [from Qt](https://www.qt.io/blog/security-advisory-potential-buffer-overflow-when-reading-ktx-images). --- .../patches/CVE-2024-25580-qtbase-5.15.diff | 197 ++++++++++++++++++ ports/qt5-base/portfile.cmake | 1 + ports/qt5-base/vcpkg.json | 2 +- versions/baseline.json | 2 +- versions/q-/qt5-base.json | 5 + 5 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 ports/qt5-base/patches/CVE-2024-25580-qtbase-5.15.diff diff --git a/ports/qt5-base/patches/CVE-2024-25580-qtbase-5.15.diff b/ports/qt5-base/patches/CVE-2024-25580-qtbase-5.15.diff new file mode 100644 index 0000000000..1628961b1f --- /dev/null +++ b/ports/qt5-base/patches/CVE-2024-25580-qtbase-5.15.diff @@ -0,0 +1,197 @@ +diff --git a/src/gui/util/qktxhandler.cpp b/src/gui/util/qktxhandler.cpp +index 0d98e97453..6a79e55109 100644 +--- a/src/gui/util/qktxhandler.cpp ++++ b/src/gui/util/qktxhandler.cpp +@@ -73,7 +73,7 @@ struct KTXHeader { + quint32 bytesOfKeyValueData; + }; + +-static const quint32 headerSize = sizeof(KTXHeader); ++static constexpr quint32 qktxh_headerSize = sizeof(KTXHeader); + + // Currently unused, declared for future reference + struct KTXKeyValuePairItem { +@@ -103,11 +103,36 @@ struct KTXMipmapLevel { + */ + }; + +-bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block) ++static bool qAddOverflow(quint32 v1, quint32 v2, quint32 *r) { ++ // unsigned additions are well-defined ++ *r = v1 + v2; ++ return v1 > quint32(v1 + v2); ++} ++ ++// Returns the nearest multiple of 4 greater than or equal to 'value' ++static bool nearestMultipleOf4(quint32 value, quint32 *result) ++{ ++ constexpr quint32 rounding = 4; ++ *result = 0; ++ if (qAddOverflow(value, rounding - 1, result)) ++ return true; ++ *result &= ~(rounding - 1); ++ return false; ++} ++ ++// Returns a slice with prechecked bounds ++static QByteArray safeSlice(const QByteArray& array, quint32 start, quint32 length) + { +- Q_UNUSED(suffix) ++ quint32 end = 0; ++ if (qAddOverflow(start, length, &end) || end > quint32(array.length())) ++ return {}; ++ return QByteArray(array.data() + start, length); ++} + +- return (qstrncmp(block.constData(), ktxIdentifier, KTX_IDENTIFIER_LENGTH) == 0); ++bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block) ++{ ++ Q_UNUSED(suffix); ++ return block.startsWith(QByteArray::fromRawData(ktxIdentifier, KTX_IDENTIFIER_LENGTH)); + } + + QTextureFileData QKtxHandler::read() +@@ -115,42 +140,97 @@ QTextureFileData QKtxHandler::read() + if (!device()) + return QTextureFileData(); + +- QByteArray buf = device()->readAll(); +- const quint32 dataSize = quint32(buf.size()); +- if (dataSize < headerSize || !canRead(QByteArray(), buf)) { +- qCDebug(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData()); ++ const QByteArray buf = device()->readAll(); ++ if (size_t(buf.size()) > std::numeric_limits::max()) { ++ qWarning(lcQtGuiTextureIO, "Too big KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ if (!canRead(QByteArray(), buf)) { ++ qWarning(lcQtGuiTextureIO, "Invalid KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ if (buf.size() < qsizetype(qktxh_headerSize)) { ++ qWarning(lcQtGuiTextureIO, "Invalid KTX header size in %s", logName().constData()); + return QTextureFileData(); + } + +- const KTXHeader *header = reinterpret_cast(buf.constData()); +- if (!checkHeader(*header)) { +- qCDebug(lcQtGuiTextureIO, "Unsupported KTX file format in %s", logName().constData()); ++ KTXHeader header; ++ memcpy(&header, buf.data(), qktxh_headerSize); ++ if (!checkHeader(header)) { ++ qWarning(lcQtGuiTextureIO, "Unsupported KTX file format in %s", logName().constData()); + return QTextureFileData(); + } + + QTextureFileData texData; + texData.setData(buf); + +- texData.setSize(QSize(decode(header->pixelWidth), decode(header->pixelHeight))); +- texData.setGLFormat(decode(header->glFormat)); +- texData.setGLInternalFormat(decode(header->glInternalFormat)); +- texData.setGLBaseInternalFormat(decode(header->glBaseInternalFormat)); +- +- texData.setNumLevels(decode(header->numberOfMipmapLevels)); +- quint32 offset = headerSize + decode(header->bytesOfKeyValueData); +- const int maxLevels = qMin(texData.numLevels(), 32); // Cap iterations in case of corrupt file. +- for (int i = 0; i < maxLevels; i++) { +- if (offset + sizeof(KTXMipmapLevel) > dataSize) // Corrupt file; avoid oob read +- break; +- const KTXMipmapLevel *level = reinterpret_cast(buf.constData() + offset); +- quint32 levelLen = decode(level->imageSize); +- texData.setDataOffset(offset + sizeof(KTXMipmapLevel::imageSize), i); +- texData.setDataLength(levelLen, i); +- offset += sizeof(KTXMipmapLevel::imageSize) + levelLen + (3 - ((levelLen + 3) % 4)); ++ texData.setSize(QSize(decode(header.pixelWidth), decode(header.pixelHeight))); ++ texData.setGLFormat(decode(header.glFormat)); ++ texData.setGLInternalFormat(decode(header.glInternalFormat)); ++ texData.setGLBaseInternalFormat(decode(header.glBaseInternalFormat)); ++ ++ texData.setNumLevels(decode(header.numberOfMipmapLevels)); ++ ++ const quint32 bytesOfKeyValueData = decode(header.bytesOfKeyValueData); ++ quint32 headerKeyValueSize; ++ if (qAddOverflow(qktxh_headerSize, bytesOfKeyValueData, &headerKeyValueSize)) { ++ qWarning(lcQtGuiTextureIO, "Overflow in size of key value data in header of KTX file %s", ++ logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ if (headerKeyValueSize >= quint32(buf.size())) { ++ qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ // Technically, any number of levels is allowed but if the value is bigger than ++ // what is possible in KTX V2 (and what makes sense) we return an error. ++ // maxLevels = log2(max(width, height, depth)) ++ const int maxLevels = (sizeof(quint32) * 8) ++ - qCountLeadingZeroBits(std::max( ++ { header.pixelWidth, header.pixelHeight, header.pixelDepth })); ++ ++ if (texData.numLevels() > maxLevels) { ++ qWarning(lcQtGuiTextureIO, "Too many levels in KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ quint32 offset = headerKeyValueSize; ++ for (int level = 0; level < texData.numLevels(); level++) { ++ const auto imageSizeSlice = safeSlice(buf, offset, sizeof(quint32)); ++ if (imageSizeSlice.isEmpty()) { ++ qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ const quint32 imageSize = decode(qFromUnaligned(imageSizeSlice.data())); ++ offset += sizeof(quint32); // overflow checked indirectly above ++ ++ texData.setDataOffset(offset, level); ++ texData.setDataLength(imageSize, level); ++ ++ // Add image data and padding to offset ++ quint32 padded = 0; ++ if (nearestMultipleOf4(imageSize, &padded)) { ++ qWarning(lcQtGuiTextureIO, "Overflow in KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ quint32 offsetNext; ++ if (qAddOverflow(offset, padded, &offsetNext)) { ++ qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s", logName().constData()); ++ return QTextureFileData(); ++ } ++ ++ offset = offsetNext; + } + + if (!texData.isValid()) { +- qCDebug(lcQtGuiTextureIO, "Invalid values in header of KTX file %s", logName().constData()); ++ qWarning(lcQtGuiTextureIO, "Invalid values in header of KTX file %s", ++ logName().constData()); + return QTextureFileData(); + } + +@@ -191,7 +271,7 @@ bool QKtxHandler::checkHeader(const KTXHeader &header) + (decode(header.numberOfFaces) == 1)); + } + +-quint32 QKtxHandler::decode(quint32 val) ++quint32 QKtxHandler::decode(quint32 val) const + { + return inverseEndian ? qbswap(val) : val; + } +diff --git a/src/gui/util/qktxhandler_p.h b/src/gui/util/qktxhandler_p.h +index f831e59d95..cdf1b2eaf8 100644 +--- a/src/gui/util/qktxhandler_p.h ++++ b/src/gui/util/qktxhandler_p.h +@@ -68,7 +68,7 @@ public: + + private: + bool checkHeader(const KTXHeader &header); +- quint32 decode(quint32 val); ++ quint32 decode(quint32 val) const; + + bool inverseEndian = false; + }; diff --git a/ports/qt5-base/portfile.cmake b/ports/qt5-base/portfile.cmake index 37d3150ea9..18f9675674 100644 --- a/ports/qt5-base/portfile.cmake +++ b/ports/qt5-base/portfile.cmake @@ -57,6 +57,7 @@ qt_download_submodule( OUT_SOURCE_PATH SOURCE_PATH patches/CVE-2023-43114-5.15.patch patches/0001-CVE-2023-51714-qtbase-5.15.diff patches/0002-CVE-2023-51714-qtbase-5.15.diff + patches/CVE-2024-25580-qtbase-5.15.diff patches/winmain_pro.patch #Moves qtmain to manual-link patches/windows_prf.patch #fixes the qtmain dependency due to the above move diff --git a/ports/qt5-base/vcpkg.json b/ports/qt5-base/vcpkg.json index f81e8b0edf..b08f6a97d3 100644 --- a/ports/qt5-base/vcpkg.json +++ b/ports/qt5-base/vcpkg.json @@ -1,7 +1,7 @@ { "name": "qt5-base", "version": "5.15.12", - "port-version": 1, + "port-version": 2, "description": "Qt5 Application Framework Base Module. Includes Core, GUI, Widgets, Networking, SQL, Concurrent and other essential qt components.", "homepage": "https://www.qt.io/", "license": null, diff --git a/versions/baseline.json b/versions/baseline.json index 8a4771cd47..724423c94f 100644 --- a/versions/baseline.json +++ b/versions/baseline.json @@ -7030,7 +7030,7 @@ }, "qt5-base": { "baseline": "5.15.12", - "port-version": 1 + "port-version": 2 }, "qt5-canvas3d": { "baseline": "0", diff --git a/versions/q-/qt5-base.json b/versions/q-/qt5-base.json index 359d37620c..6ac21b5f95 100644 --- a/versions/q-/qt5-base.json +++ b/versions/q-/qt5-base.json @@ -1,5 +1,10 @@ { "versions": [ + { + "git-tree": "1ff980ad44dfab95d50dfe163ff3ef2de05d41d7", + "version": "5.15.12", + "port-version": 2 + }, { "git-tree": "2932bd39b46da13edde2cf5715b0308c53b1d426", "version": "5.15.12",