diff --git a/dev-qt/qt6-base/patches/CVE-2025-5455-qtbase-6.9.patch b/dev-qt/qt6-base/patches/CVE-2025-5455-qtbase-6.9.patch new file mode 100644 index 000000000..c1d5a1f3e --- /dev/null +++ b/dev-qt/qt6-base/patches/CVE-2025-5455-qtbase-6.9.patch @@ -0,0 +1,19 @@ +diff --git a/src/corelib/io/qdataurl.cpp b/src/corelib/io/qdataurl.cpp +index 65b934b3f67..c5ecca8fb82 100644 +--- a/src/corelib/io/qdataurl.cpp ++++ b/src/corelib/io/qdataurl.cpp +@@ -47,10 +47,10 @@ Q_CORE_EXPORT bool qDecodeDataUrl(const QUrl &uri, QString &mimeType, QByteArray + QLatin1StringView textPlain; + constexpr auto charset = "charset"_L1; + if (QLatin1StringView{data}.startsWith(charset, Qt::CaseInsensitive)) { +- qsizetype i = charset.size(); +- while (data.at(i) == ' ') +- ++i; +- if (data.at(i) == '=') ++ QByteArrayView copy = data.sliced(charset.size()); ++ while (copy.startsWith(' ')) ++ copy.slice(1); ++ if (copy.startsWith('=')) + textPlain = "text/plain;"_L1; + } + diff --git a/dev-qt/qt6-base/patches/CVE-2025-5991-qtbase-6.9.patch b/dev-qt/qt6-base/patches/CVE-2025-5991-qtbase-6.9.patch new file mode 100644 index 000000000..6be5fc50c --- /dev/null +++ b/dev-qt/qt6-base/patches/CVE-2025-5991-qtbase-6.9.patch @@ -0,0 +1,120 @@ +diff --git a/src/network/access/qabstractprotocolhandler_p.h b/src/network/access/qabstractprotocolhandler_p.h +index da5eaeeb74c4..42925a169d34 100644 +--- a/src/network/access/qabstractprotocolhandler_p.h ++++ b/src/network/access/qabstractprotocolhandler_p.h +@@ -17,6 +17,8 @@ + + #include + ++#include ++ + QT_REQUIRE_CONFIG(http); + + QT_BEGIN_NAMESPACE +@@ -34,6 +36,13 @@ public: + virtual void _q_receiveReply() = 0; + virtual void _q_readyRead() = 0; + virtual bool sendRequest() = 0; ++ // Called when the reply is being destroyed and removing itself from any other internals ++ virtual bool tryRemoveReply(QHttpNetworkReply *reply) ++ { ++ Q_UNUSED(reply); ++ // base implementation is a noop ++ return false; ++ } + void setReply(QHttpNetworkReply *reply); + + protected: +diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp +index 87dc504ee126..a99921f52881 100644 +--- a/src/network/access/qhttp2protocolhandler.cpp ++++ b/src/network/access/qhttp2protocolhandler.cpp +@@ -151,15 +151,6 @@ void QHttp2ProtocolHandler::handleConnectionClosure() + h2Connection->handleConnectionClosure(); + } + +-void QHttp2ProtocolHandler::_q_replyDestroyed(QObject *reply) +-{ +- QPointer stream = streamIDs.take(reply); +- requestReplyPairs.remove(stream); +- QObject::disconnect(stream, nullptr, this, nullptr); +- if (stream && stream->isActive()) +- stream->sendRST_STREAM(CANCEL); +-} +- + void QHttp2ProtocolHandler::_q_uploadDataDestroyed(QObject *uploadData) + { + QPointer stream = streamIDs.take(uploadData); +@@ -262,6 +253,25 @@ bool QHttp2ProtocolHandler::sendRequest() + return true; + } + ++/*! ++ \internal ++ This gets called during destruction of \a reply, so do not call any functions ++ on \a reply. We check if there is a stream associated with the reply and, ++ if there is, we remove the request-reply pair associated with this stream, ++ delete the stream and return \c{true}. Otherwise nothing happens and we ++ return \c{false}. ++*/ ++bool QHttp2ProtocolHandler::tryRemoveReply(QHttpNetworkReply *reply) ++{ ++ QHttp2Stream *stream = streamIDs.take(reply); ++ if (stream) { ++ requestReplyPairs.remove(stream); ++ stream->deleteLater(); ++ return true; ++ } ++ return false; ++} ++ + bool QHttp2ProtocolHandler::sendHEADERS(QHttp2Stream *stream, QHttpNetworkRequest &request) + { + using namespace HPack; +@@ -623,8 +633,6 @@ void QHttp2ProtocolHandler::connectStream(const HttpMessagePair &message, QHttp2 + auto *replyPrivate = reply->d_func(); + replyPrivate->connection = m_connection; + replyPrivate->connectionChannel = m_channel; +- connect(reply, &QObject::destroyed, this, &QHttp2ProtocolHandler::_q_replyDestroyed, +- Qt::UniqueConnection); + + reply->setHttp2WasUsed(true); + QPointer &oldStream = streamIDs[reply]; +diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h +index ecbc6823dcfd..aca8a0b6f66b 100644 +--- a/src/network/access/qhttp2protocolhandler_p.h ++++ b/src/network/access/qhttp2protocolhandler_p.h +@@ -61,7 +61,6 @@ public: + Q_INVOKABLE void handleConnectionClosure(); + + private slots: +- void _q_replyDestroyed(QObject *reply); + void _q_uploadDataDestroyed(QObject *uploadData); + + private: +@@ -70,6 +69,7 @@ private: + void _q_readyRead() override; + Q_INVOKABLE void _q_receiveReply() override; + Q_INVOKABLE bool sendRequest() override; ++ bool tryRemoveReply(QHttpNetworkReply *reply) override; + + bool sendSETTINGS_ACK(); + bool sendHEADERS(QHttp2Stream *stream, QHttpNetworkRequest &request); +diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp +index cf7aad1930de..4f105af084a8 100644 +--- a/src/network/access/qhttpnetworkconnection.cpp ++++ b/src/network/access/qhttpnetworkconnection.cpp +@@ -1002,6 +1002,13 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply) + QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); + return; + } ++ // Check if the h2 protocol handler already started processing it ++ if ((connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct ++ || channels[i].switchedToHttp2) ++ && channels[i].protocolHandler) { ++ if (channels[i].protocolHandler->tryRemoveReply(reply)) ++ return; ++ } + } + // remove from the high priority queue + if (!highPriorityQueue.isEmpty()) { diff --git a/dev-qt/qt6-base/patches/CVE-2025-5992-qtbase-6.9.patch b/dev-qt/qt6-base/patches/CVE-2025-5992-qtbase-6.9.patch new file mode 100644 index 000000000..e3c301004 --- /dev/null +++ b/dev-qt/qt6-base/patches/CVE-2025-5992-qtbase-6.9.patch @@ -0,0 +1,36 @@ +diff --git a/src/gui/painting/qcolortransfergeneric_p.h b/src/gui/painting/qcolortransfergeneric_p.h +index 6caebceb1a4c..c2ebd937a445 100644 +--- a/src/gui/painting/qcolortransfergeneric_p.h ++++ b/src/gui/painting/qcolortransfergeneric_p.h +@@ -65,6 +65,7 @@ private: + // HLG from linear [0-12] -> [0-1] + static float hlgFromLinear(float x) + { ++ x = std::clamp(x, 0.f, 12.f); + if (x > 1.f) + return m_hlg_a * std::log(x - m_hlg_b) + m_hlg_c; + return std::sqrt(x * 0.25f); +@@ -73,6 +74,7 @@ private: + // HLG to linear [0-1] -> [0-12] + static float hlgToLinear(float x) + { ++ x = std::clamp(x, 0.f, 1.f); + if (x < 0.5f) + return (x * x) * 4.f; + return std::exp((x - m_hlg_c) / m_hlg_a) + m_hlg_b; +@@ -86,6 +88,7 @@ private: + // PQ to linear [0-1] -> [0-64] + static float pqToLinear(float e) + { ++ e = std::clamp(e, 0.f, 1.f); + // m2-th root of E' + const float eRoot = std::pow(e, 1.f / m_pq_m2); + // rational transform +@@ -99,6 +102,7 @@ private: + // PQ from linear [0-64] -> [0-1] + static float pqFromLinear(float fd) + { ++ fd = std::clamp(fd, 0.f, 64.f); + // scale Fd to Y + const float y = fd * (1.f / m_pq_f); + // yRoot = Y^m1 -- "root" because m1 is <1 diff --git a/dev-qt/qt6-base/patches/CVE-2025-6338-qtbase-6.9.patch b/dev-qt/qt6-base/patches/CVE-2025-6338-qtbase-6.9.patch new file mode 100644 index 000000000..e538f2c3f --- /dev/null +++ b/dev-qt/qt6-base/patches/CVE-2025-6338-qtbase-6.9.patch @@ -0,0 +1,263 @@ +diff --git a/src/plugins/tls/schannel/qtls_schannel.cpp b/src/plugins/tls/schannel/qtls_schannel.cpp +index 617e070025e6..7e6a372347e0 100644 +--- a/src/plugins/tls/schannel/qtls_schannel.cpp ++++ b/src/plugins/tls/schannel/qtls_schannel.cpp +@@ -7,6 +7,7 @@ + #include "qtlskey_schannel_p.h" + #include "qx509_schannel_p.h" + #include "qtls_schannel_p.h" ++#include "../shared/qasn1element_p.h" + + #include + #include +@@ -126,8 +127,9 @@ using namespace Qt::StringLiterals; + Q_LOGGING_CATEGORY(lcTlsBackendSchannel, "qt.tlsbackend.schannel"); + + // Defined in qsslsocket_qt.cpp. +-QByteArray _q_makePkcs12(const QList &certs, const QSslKey &key, ++extern QByteArray _q_makePkcs12(const QList &certs, const QSslKey &key, + const QString &passPhrase); ++extern QAsn1Element _q_PKCS12_key(const QSslKey &key); + + namespace { + bool supportsTls13(); +@@ -1008,7 +1010,6 @@ TlsCryptographSchannel::~TlsCryptographSchannel() + closeCertificateStores(); + deallocateContext(); + freeCredentialsHandle(); +- CertFreeCertificateContext(localCertContext); + } + + void TlsCryptographSchannel::init(QSslSocket *qObj, QSslSocketPrivate *dObj) +@@ -1103,12 +1104,6 @@ bool TlsCryptographSchannel::acquireCredentialsHandle() + return false; + } + +- const CERT_CHAIN_CONTEXT *chainContext = nullptr; +- auto freeCertChain = qScopeGuard([&chainContext]() { +- if (chainContext) +- CertFreeCertificateChain(chainContext); +- }); +- + DWORD certsCount = 0; + // Set up our certificate stores before trying to use one... + initializeCertificateStores(); +@@ -1119,36 +1114,11 @@ bool TlsCryptographSchannel::acquireCredentialsHandle() + if (!configuration.localCertificateChain().isEmpty() && !localCertificateStore) + return true; // 'true' because "tst_QSslSocket::setEmptyKey" expects us to not disconnect + ++ PCCERT_CONTEXT localCertificate = nullptr; + if (localCertificateStore != nullptr) { +- CERT_CHAIN_FIND_BY_ISSUER_PARA findParam; +- ZeroMemory(&findParam, sizeof(findParam)); +- findParam.cbSize = sizeof(findParam); +- findParam.pszUsageIdentifier = isClient ? szOID_PKIX_KP_CLIENT_AUTH : szOID_PKIX_KP_SERVER_AUTH; +- +- // There should only be one chain in our store, so.. we grab that one. +- chainContext = CertFindChainInStore(localCertificateStore.get(), +- X509_ASN_ENCODING, +- 0, +- CERT_CHAIN_FIND_BY_ISSUER, +- &findParam, +- nullptr); +- if (!chainContext) { +- const QString message = isClient +- ? QSslSocket::tr("The certificate provided cannot be used for a client.") +- : QSslSocket::tr("The certificate provided cannot be used for a server."); +- setErrorAndEmit(d, QAbstractSocket::SocketError::SslInvalidUserDataError, message); +- return false; +- } +- Q_ASSERT(chainContext->cChain == 1); +- Q_ASSERT(chainContext->rgpChain[0]); +- Q_ASSERT(chainContext->rgpChain[0]->cbSize >= 1); +- Q_ASSERT(chainContext->rgpChain[0]->rgpElement[0]); +- Q_ASSERT(!localCertContext); +- localCertContext = CertDuplicateCertificateContext(chainContext->rgpChain[0] +- ->rgpElement[0] +- ->pCertContext); + certsCount = 1; +- Q_ASSERT(localCertContext); ++ localCertificate = static_cast(configuration.localCertificate().handle()); ++ Q_ASSERT(localCertificate); + } + + const QList ciphers = configuration.ciphers(); +@@ -1172,7 +1142,7 @@ bool TlsCryptographSchannel::acquireCredentialsHandle() + SCH_CREDENTIALS_VERSION, + 0, + certsCount, +- &localCertContext, ++ &localCertificate, + nullptr, + 0, + nullptr, +@@ -1729,9 +1699,6 @@ void TlsCryptographSchannel::reset() + connectionInfo = {}; + streamSizes = {}; + +- CertFreeCertificateContext(localCertContext); +- localCertContext = nullptr; +- + contextAttributes = 0; + intermediateBuffer.clear(); + schannelState = SchannelState::InitializeHandshake; +@@ -2282,6 +2249,70 @@ bool TlsCryptographSchannel::checkSslErrors() + return true; + } + ++static void attachPrivateKeyToCertificate(const QSslCertificate &certificate, ++ const QSslKey &privateKey) ++{ ++ QAsn1Element elem = _q_PKCS12_key(privateKey); ++ QByteArray buffer; ++ QDataStream stream(&buffer, QDataStream::WriteOnly); ++ elem.write(stream); ++ NCRYPT_PROV_HANDLE provider = 0; ++ SECURITY_STATUS status = NCryptOpenStorageProvider(&provider, MS_KEY_STORAGE_PROVIDER, 0); ++ if (status != SEC_E_OK) { ++ qCWarning(lcTlsBackendSchannel()) ++ << "Failed to open ncrypt storage provider:" << schannelErrorToString(status); ++ return; ++ } ++ const auto freeProvider = qScopeGuard([provider]() { NCryptFreeObject(provider); }); ++ ++ const QString certName = certificate.subjectInfo(QSslCertificate::CommonName).front(); ++ QSpan nameSpan(certName); ++ NCryptBuffer nbuffer{ ULONG(nameSpan.size_bytes() + sizeof(char16_t)), ++ NCRYPTBUFFER_PKCS_KEY_NAME, ++ const_reinterpret_cast(nameSpan.data()) }; ++ NCryptBufferDesc bufferDesc{ NCRYPTBUFFER_VERSION, 1, &nbuffer }; ++ NCRYPT_KEY_HANDLE ncryptKey = 0; ++ status = NCryptImportKey(provider, 0, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, &bufferDesc, &ncryptKey, ++ PBYTE(buffer.data()), buffer.size(), 0); ++ if (status != SEC_E_OK) { ++ qCWarning(lcTlsBackendSchannel()) ++ << "Failed to import private key:" << schannelErrorToString(status); ++ return; ++ } ++ const auto freeKey = qScopeGuard([ncryptKey]() { NCryptFreeObject(ncryptKey); }); ++ ++ CERT_CONTEXT *context = PCERT_CONTEXT(certificate.handle()); ++ Q_ASSERT(context); ++ ++ CRYPT_DATA_BLOB keyBlob = { sizeof(ncryptKey), PBYTE(&ncryptKey) }; ++ BOOL ok = ++ CertSetCertificateContextProperty(context, CERT_NCRYPT_KEY_HANDLE_PROP_ID, 0, &keyBlob); ++ if (!ok) { ++ auto error = GetLastError(); ++ if (lcTlsBackendSchannel().isWarningEnabled()) ++ qErrnoWarning(int(error), "Failed to set ncrypt handle property."); ++ return; ++ } ++ ++ CRYPT_KEY_PROV_INFO provInfo{ ++ const_reinterpret_cast(certName.constData()), ++ const_cast(MS_KEY_STORAGE_PROVIDER), ++ 0, ++ CERT_SET_KEY_PROV_HANDLE_PROP_ID | CERT_SET_KEY_CONTEXT_PROP_ID, ++ 0, ++ nullptr, ++ 0, ++ }; ++ ok = CertSetCertificateContextProperty(context, CERT_KEY_PROV_INFO_PROP_ID, ++ CERT_SET_PROPERTY_INHIBIT_PERSIST_FLAG, &provInfo); ++ if (!ok) { ++ auto error = GetLastError(); ++ if (lcTlsBackendSchannel().isWarningEnabled()) ++ qErrnoWarning(int(error), "Failed to set key provider info property."); ++ return; ++ } ++} ++ + void TlsCryptographSchannel::initializeCertificateStores() + { + //// helper function which turns a chain into a certificate store +@@ -2298,7 +2329,10 @@ void TlsCryptographSchannel::initializeCertificateStores() + CRYPT_DATA_BLOB pfxBlob; + pfxBlob.cbData = DWORD(pkcs12.length()); + pfxBlob.pbData = reinterpret_cast(pkcs12.data()); +- return QHCertStorePointer(PFXImportCertStore(&pfxBlob, passphrase, 0)); ++ // ALWAYS_CNG to import using "Cryptography API: Next Generation (CNG)" ++ // NO_PERSIST_KEY to request not persisting anything imported to disk ++ constexpr DWORD flags = PKCS12_ALWAYS_CNG_KSP | PKCS12_NO_PERSIST_KEY; ++ return QHCertStorePointer(PFXImportCertStore(&pfxBlob, passphrase, flags)); + }; + + if (!configuration.localCertificateChain().isEmpty()) { +@@ -2308,10 +2342,34 @@ void TlsCryptographSchannel::initializeCertificateStores() + return; + } + if (localCertificateStore == nullptr) { +- localCertificateStore = createStoreFromCertificateChain(configuration.localCertificateChain(), +- configuration.privateKey()); +- if (localCertificateStore == nullptr) ++ localCertificateStore = ++ createStoreFromCertificateChain(configuration.localCertificateChain(), {}); ++ if (localCertificateStore) { ++ const CERT_CONTEXT *certificateContext = CertFindCertificateInStore( ++ localCertificateStore.get(), X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, ++ CERT_FIND_ANY, nullptr, nullptr); ++ if (certificateContext) { ++ auto *backend = QTlsBackend::backend( ++ configuration.localCertificate()); ++ backend->certificateContext.reset( ++ CertDuplicateCertificateContext(certificateContext)); ++ ++ DWORD keySpec = 0; ++ BOOL mustFree = FALSE; ++ NCRYPT_KEY_HANDLE testKey = 0; ++ BOOL ok = CryptAcquireCertificatePrivateKey( ++ certificateContext, CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, nullptr, ++ &testKey, &keySpec, &mustFree); ++ if (mustFree) ++ NCryptFreeObject(testKey); ++ if (!ok) { ++ attachPrivateKeyToCertificate(configuration.localCertificate(), ++ configuration.privateKey()); ++ } ++ } ++ } else { + qCWarning(lcTlsBackendSchannel, "Failed to load certificate chain!"); ++ } + } + } + +diff --git a/src/plugins/tls/schannel/qtls_schannel_p.h b/src/plugins/tls/schannel/qtls_schannel_p.h +index 64c3b21d45e8..5d5297954b2d 100644 +--- a/src/plugins/tls/schannel/qtls_schannel_p.h ++++ b/src/plugins/tls/schannel/qtls_schannel_p.h +@@ -114,8 +114,6 @@ private: + QHCertStorePointer peerCertificateStore = nullptr; + QHCertStorePointer caCertificateStore = nullptr; + +- const CERT_CONTEXT *localCertContext = nullptr; +- + ULONG contextAttributes = 0; + qint64 missingData = 0; + +diff --git a/src/plugins/tls/schannel/qx509_schannel_p.h b/src/plugins/tls/schannel/qx509_schannel_p.h +index caf2a914f65d..4f856ba019d6 100644 +--- a/src/plugins/tls/schannel/qx509_schannel_p.h ++++ b/src/plugins/tls/schannel/qx509_schannel_p.h +@@ -40,7 +40,7 @@ public: + static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert, + QList *caCertificates, + const QByteArray &passPhrase); +-private: ++ + QPCCertContextPointer certificateContext; + + Q_DISABLE_COPY_MOVE(X509CertificateSchannel); +diff --git a/src/plugins/tls/shared/qsslsocket_qt.cpp b/src/plugins/tls/shared/qsslsocket_qt.cpp +index f55b3e3ded7a..7e108dceca45 100644 +--- a/src/plugins/tls/shared/qsslsocket_qt.cpp ++++ b/src/plugins/tls/shared/qsslsocket_qt.cpp +@@ -134,7 +134,7 @@ static QByteArray _q_PKCS12_certBag(const QSslCertificate &cert) + return ba; + } + +-static QAsn1Element _q_PKCS12_key(const QSslKey &key) ++QAsn1Element _q_PKCS12_key(const QSslKey &key) + { + Q_ASSERT(key.algorithm() == QSsl::Rsa || key.algorithm() == QSsl::Dsa); + diff --git a/dev-qt/qt6-base/qt6_base-6.9.0.recipe b/dev-qt/qt6-base/qt6_base-6.9.0.recipe index b218df485..9e49cab4b 100644 --- a/dev-qt/qt6-base/qt6_base-6.9.0.recipe +++ b/dev-qt/qt6-base/qt6_base-6.9.0.recipe @@ -8,12 +8,16 @@ COPYRIGHT="2015-2025 The Qt Company Ltd." LICENSE="GNU LGPL v3 GNU GPL v3 GNU FDL v1" -REVISION="1" +REVISION="2" QT_MIRROR_URI="https://qt-mirror.dannhauer.de/archive" SOURCE_URI="$QT_MIRROR_URI/qt/${portVersion%.*}/$portVersion/submodules/qtbase-everywhere-src-$portVersion.tar.xz" CHECKSUM_SHA256="c1800c2ea835801af04a05d4a32321d79a93954ee3ae2172bbeacf13d1f0598c" SOURCE_DIR="qtbase-everywhere-src-$portVersion" -PATCHES="qt6_base-$portVersion.patchset" +PATCHES="qt6_base-$portVersion.patchset + CVE-2025-5455-qtbase-6.9.patch + CVE-2025-5991-qtbase-6.9.patch + CVE-2025-5992-qtbase-6.9.patch + CVE-2025-6338-qtbase-6.9.patch" srcGitRev2="4341730a0b93fc5564d9a20688f6360526148a7a" SOURCE_URI_2="https://github.com/threedeyes/qt6-haikuplugins/archive/$srcGitRev2.tar.gz" CHECKSUM_SHA256_2="2e46855a2f215c5b19bf9961cff6303993b26f31bba120c90d1babfbf659db1a"