diff --git a/CVE-2023-32762-pre.patch b/CVE-2023-32762-pre.patch new file mode 100644 index 0000000000000000000000000000000000000000..7ab1cd92fa7336fecaa800c828f4b8e21b9cb12c --- /dev/null +++ b/CVE-2023-32762-pre.patch @@ -0,0 +1,836 @@ +From cad7100fda1b27ba56c4d9efc6bceba62859dfbc Mon Sep 17 00:00:00 2001 +From: Thiago Macieira +Date: Sun, 13 May 2018 21:53:07 -0700 +Subject: [PATCH] QByteArray: add compare() with case sensitivity options +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Need to do the same for startsWith() and endsWith(). indexOf() is a lot +harder. + +[ChangeLog][QtCore][QByteArray] Added compare(), which takes +Qt::CaseSensitivity as one of the parameters. This function is more +efficient than using toLower() or toUpper() and then comparing. + +Change-Id: Ib48364abee9f464c96c6fffd152e69bde4194df7 +Reviewed-by: MÃ¥rten Nordheim +Reviewed-by: Timur Pocheptsov +--- + src/corelib/io/qsettings.cpp | 6 +- + src/corelib/tools/qbytearray.cpp | 123 +++++++++++++++++- + src/corelib/tools/qbytearray.h | 14 ++ + src/corelib/tools/qstring.cpp | 8 +- + src/gui/kernel/qhighdpiscaling.cpp | 3 +- + src/network/access/http2/http2protocol.cpp | 3 +- + src/network/access/qhsts.cpp | 5 +- + src/network/access/qhttp2protocolhandler.cpp | 15 ++- + src/network/access/qhttpnetworkconnection.cpp | 2 +- + src/network/access/qhttpnetworkheader.cpp | 6 +- + src/network/access/qhttpnetworkreply.cpp | 5 +- + src/network/access/qnetworkdiskcache.cpp | 6 +- + src/network/access/qnetworkreplyhttpimpl.cpp | 4 +- + src/network/access/qnetworkrequest.cpp | 20 +-- + src/network/kernel/qauthenticator.cpp | 8 +- + src/network/socket/qhttpsocketengine.cpp | 5 +- + .../platforms/eglfs/api/qeglfsintegration.cpp | 3 +- + .../tools/qbytearray/tst_qbytearray.cpp | 32 ++++- + 18 files changed, 212 insertions(+), 56 deletions(-) + +diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp +index 4b1b9888d81..1134c6bb85e 100644 +--- a/src/corelib/io/qsettings.cpp ++++ b/src/corelib/io/qsettings.cpp +@@ -1694,10 +1694,10 @@ bool QConfFileSettingsPrivate::readIniFile(const QByteArray &data, + + iniSection = iniSection.trimmed(); + +- if (qstricmp(iniSection.constData(), "general") == 0) { ++ if (iniSection.compare("general", Qt::CaseInsensitive) == 0) { + currentSection.clear(); + } else { +- if (qstricmp(iniSection.constData(), "%general") == 0) { ++ if (iniSection.compare("%general", Qt::CaseInsensitive) == 0) { + currentSection = QLatin1String(iniSection.constData() + 1); + } else { + currentSection.clear(); +@@ -1857,7 +1857,7 @@ bool QConfFileSettingsPrivate::writeIniFile(QIODevice &device, const ParsedSetti + + if (realSection.isEmpty()) { + realSection = "[General]"; +- } else if (qstricmp(realSection.constData(), "general") == 0) { ++ } else if (realSection.compare("general", Qt::CaseInsensitive) == 0) { + realSection = "[%General]"; + } else { + realSection.prepend('['); +diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp +index 3468580bf14..4d2003334c0 100644 +--- a/src/corelib/tools/qbytearray.cpp ++++ b/src/corelib/tools/qbytearray.cpp +@@ -356,7 +356,8 @@ char *qstrncpy(char *dst, const char *src, uint len) + Special case 2: Returns an arbitrary non-zero value if \a str1 is + nullptr or \a str2 is nullptr (but not both). + +- \sa qstrncmp(), qstricmp(), qstrnicmp(), {8-bit Character Comparisons} ++ \sa qstrncmp(), qstricmp(), qstrnicmp(), {8-bit Character Comparisons}, ++ QByteArray::compare() + */ + int qstrcmp(const char *str1, const char *str2) + { +@@ -381,7 +382,8 @@ int qstrcmp(const char *str1, const char *str2) + Special case 2: Returns a random non-zero value if \a str1 is nullptr + or \a str2 is nullptr (but not both). + +- \sa qstrcmp(), qstricmp(), qstrnicmp(), {8-bit Character Comparisons} ++ \sa qstrcmp(), qstricmp(), qstrnicmp(), {8-bit Character Comparisons}, ++ QByteArray::compare() + */ + + /*! \relates QByteArray +@@ -400,7 +402,8 @@ int qstrcmp(const char *str1, const char *str2) + Special case 2: Returns a random non-zero value if \a str1 is nullptr + or \a str2 is nullptr (but not both). + +- \sa qstrcmp(), qstrncmp(), qstrnicmp(), {8-bit Character Comparisons} ++ \sa qstrcmp(), qstrncmp(), qstrnicmp(), {8-bit Character Comparisons}, ++ QByteArray::compare() + */ + + int qstricmp(const char *str1, const char *str2) +@@ -434,7 +437,8 @@ int qstricmp(const char *str1, const char *str2) + Special case 2: Returns a random non-zero value if \a str1 is nullptr + or \a str2 is nullptr (but not both). + +- \sa qstrcmp(), qstrncmp(), qstricmp(), {8-bit Character Comparisons} ++ \sa qstrcmp(), qstrncmp(), qstricmp(), {8-bit Character Comparisons}, ++ QByteArray::compare() + */ + + int qstrnicmp(const char *str1, const char *str2, uint len) +@@ -456,6 +460,55 @@ int qstrnicmp(const char *str1, const char *str2, uint len) + + /*! + \internal ++ \since 5.12 ++ ++ A helper for QByteArray::compare. Compares \a len1 bytes from \a str1 to \a ++ len2 bytes from \a str2. If \a len2 is -1, then \a str2 is expected to be ++ null-terminated. ++ */ ++int qstrnicmp(const char *str1, qsizetype len1, const char *str2, qsizetype len2) ++{ ++ Q_ASSERT(str1); ++ Q_ASSERT(len1 >= 0); ++ Q_ASSERT(len2 >= -1); ++ const uchar *s1 = reinterpret_cast(str1); ++ const uchar *s2 = reinterpret_cast(str2); ++ if (!s2) ++ return len1 == 0 ? 0 : 1; ++ ++ int res; ++ uchar c; ++ if (len2 == -1) { ++ // null-terminated str2 ++ qsizetype i; ++ for (i = 0; i < len1; ++i) { ++ c = latin1_lowercased[s2[i]]; ++ if (!c) ++ return 1; ++ ++ res = latin1_lowercased[s1[i]] - c; ++ if (res) ++ return res; ++ } ++ c = latin1_lowercased[s2[i]]; ++ return c ? -1 : 0; ++ } else { ++ // not null-terminated ++ for (qsizetype i = 0; i < qMin(len1, len2); ++i) { ++ c = latin1_lowercased[s2[i]]; ++ res = latin1_lowercased[s1[i]] - c; ++ if (res) ++ return res; ++ } ++ if (len1 == len2) ++ return 0; ++ return len1 < len2 ? -1 : 1; ++ } ++} ++ ++/*! ++ \internal ++ ### Qt6: replace the QByteArray parameter with [pointer,len] pair + */ + int qstrcmp(const QByteArray &str1, const char *str2) + { +@@ -483,6 +536,7 @@ int qstrcmp(const QByteArray &str1, const char *str2) + + /*! + \internal ++ ### Qt6: replace the QByteArray parameter with [pointer,len] pair + */ + int qstrcmp(const QByteArray &str1, const QByteArray &str2) + { +@@ -2863,6 +2917,31 @@ int QByteArray::count(char ch) const + Same as size(). + */ + ++/*! ++ \fn int QByteArray::compare(const char *c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const ++ \since 5.12 ++ ++ Returns an integer less than, equal to, or greater than zero depending on ++ whether this QByteArray sorts before, at the same position, or after the ++ string pointed to by \a c. The comparison is performed according to case ++ sensitivity \a cs. ++ ++ \sa operator== ++*/ ++ ++/*! ++ \fn int QByteArray::compare(const QByteArray &a, Qt::CaseSensitivity cs = Qt::CaseSensitive) const ++ \overload ++ \since 5.12 ++ ++ Returns an integer less than, equal to, or greater than zero depending on ++ whether this QByteArray sorts before, at the same position, or after the ++ QByteArray \a a. The comparison is performed according to case sensitivity ++ \a cs. ++ ++ \sa operator== ++*/ ++ + /*! + Returns \c true if this byte array starts with byte array \a ba; + otherwise returns \c false. +@@ -3367,6 +3446,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if byte array \a a1 is equal to byte array \a a2; + otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator==(const QByteArray &a1, const char *a2) +@@ -3376,6 +3457,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if byte array \a a1 is equal to string \a a2; + otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator==(const char *a1, const QByteArray &a2) +@@ -3385,6 +3468,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if string \a a1 is equal to byte array \a a2; + otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator!=(const QByteArray &a1, const QByteArray &a2) +@@ -3394,6 +3479,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if byte array \a a1 is not equal to byte array \a a2; + otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator!=(const QByteArray &a1, const char *a2) +@@ -3403,6 +3490,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if byte array \a a1 is not equal to string \a a2; + otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator!=(const char *a1, const QByteArray &a2) +@@ -3412,6 +3501,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if string \a a1 is not equal to byte array \a a2; + otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator<(const QByteArray &a1, const QByteArray &a2) +@@ -3421,6 +3512,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if byte array \a a1 is lexically less than byte array + \a a2; otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn inline bool operator<(const QByteArray &a1, const char *a2) +@@ -3430,6 +3523,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if byte array \a a1 is lexically less than string + \a a2; otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator<(const char *a1, const QByteArray &a2) +@@ -3439,6 +3534,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if string \a a1 is lexically less than byte array + \a a2; otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator<=(const QByteArray &a1, const QByteArray &a2) +@@ -3448,6 +3545,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if byte array \a a1 is lexically less than or equal + to byte array \a a2; otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator<=(const QByteArray &a1, const char *a2) +@@ -3457,6 +3556,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if byte array \a a1 is lexically less than or equal + to string \a a2; otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator<=(const char *a1, const QByteArray &a2) +@@ -3466,6 +3567,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if string \a a1 is lexically less than or equal + to byte array \a a2; otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator>(const QByteArray &a1, const QByteArray &a2) +@@ -3475,6 +3578,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if byte array \a a1 is lexically greater than byte + array \a a2; otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator>(const QByteArray &a1, const char *a2) +@@ -3484,6 +3589,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if byte array \a a1 is lexically greater than string + \a a2; otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator>(const char *a1, const QByteArray &a2) +@@ -3493,6 +3600,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if string \a a1 is lexically greater than byte array + \a a2; otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator>=(const QByteArray &a1, const QByteArray &a2) +@@ -3502,6 +3611,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if byte array \a a1 is lexically greater than or + equal to byte array \a a2; otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator>=(const QByteArray &a1, const char *a2) +@@ -3511,6 +3622,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if byte array \a a1 is lexically greater than or + equal to string \a a2; otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn bool operator>=(const char *a1, const QByteArray &a2) +@@ -3520,6 +3633,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) + + Returns \c true if string \a a1 is lexically greater than or + equal to byte array \a a2; otherwise returns \c false. ++ ++ \sa QByteArray::compare() + */ + + /*! \fn const QByteArray operator+(const QByteArray &a1, const QByteArray &a2) +diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h +index bed710c5976..8ee3a29ecc8 100644 +--- a/src/corelib/tools/qbytearray.h ++++ b/src/corelib/tools/qbytearray.h +@@ -99,6 +99,7 @@ inline int qstrncmp(const char *str1, const char *str2, uint len) + } + Q_CORE_EXPORT int qstricmp(const char *, const char *); + Q_CORE_EXPORT int qstrnicmp(const char *, const char *, uint len); ++Q_CORE_EXPORT int qstrnicmp(const char *, qsizetype, const char *, qsizetype = -1); + + // implemented in qvsnprintf.cpp + Q_CORE_EXPORT int qvsnprintf(char *str, size_t n, const char *fmt, va_list ap); +@@ -231,6 +232,9 @@ class Q_CORE_EXPORT QByteArray + int count(const char *a) const; + int count(const QByteArray &a) const; + ++ inline int compare(const char *c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; ++ inline int compare(const QByteArray &a, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; ++ + Q_REQUIRED_RESULT QByteArray left(int len) const; + Q_REQUIRED_RESULT QByteArray right(int len) const; + Q_REQUIRED_RESULT QByteArray mid(int index, int len = -1) const; +@@ -603,6 +607,16 @@ inline bool QByteArray::contains(const QByteArray &a) const + { return indexOf(a) != -1; } + inline bool QByteArray::contains(char c) const + { return indexOf(c) != -1; } ++inline int QByteArray::compare(const char *c, Qt::CaseSensitivity cs) const ++{ ++ return cs == Qt::CaseSensitive ? qstrcmp(*this, c) : ++ qstrnicmp(data(), size(), c, -1); ++} ++inline int QByteArray::compare(const QByteArray &a, Qt::CaseSensitivity cs) const ++{ ++ return cs == Qt::CaseSensitive ? qstrcmp(*this, a) : ++ qstrnicmp(data(), size(), a.data(), a.size()); ++} + inline bool operator==(const QByteArray &a1, const QByteArray &a2) Q_DECL_NOTHROW + { return (a1.size() == a2.size()) && (memcmp(a1.constData(), a2.constData(), a1.size())==0); } + inline bool operator==(const QByteArray &a1, const char *a2) Q_DECL_NOTHROW +diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp +index 712e058c9c5..c2e62a47f75 100644 +--- a/src/corelib/tools/qstring.cpp ++++ b/src/corelib/tools/qstring.cpp +@@ -1076,14 +1076,12 @@ static int qt_compare_strings(QLatin1String lhs, QStringView rhs, Qt::CaseSensit + + static int qt_compare_strings(QLatin1String lhs, QLatin1String rhs, Qt::CaseSensitivity cs) Q_DECL_NOTHROW + { ++ if (cs == Qt::CaseInsensitive) ++ return qstrnicmp(lhs.data(), lhs.size(), rhs.data(), rhs.size()); + if (lhs.isEmpty()) + return lencmp(0, rhs.size()); + const auto l = std::min(lhs.size(), rhs.size()); +- int r; +- if (cs == Qt::CaseSensitive) +- r = qstrncmp(lhs.data(), rhs.data(), l); +- else +- r = qstrnicmp(lhs.data(), rhs.data(), l); ++ int r = qstrncmp(lhs.data(), rhs.data(), l); + return r ? r : lencmp(lhs.size(), rhs.size()); + } + +diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp +index 8689f9f3b18..a455aa639d5 100644 +--- a/src/gui/kernel/qhighdpiscaling.cpp ++++ b/src/gui/kernel/qhighdpiscaling.cpp +@@ -244,7 +244,8 @@ static inline bool usePixelDensity() + return false; + return QCoreApplication::testAttribute(Qt::AA_EnableHighDpiScaling) + || (screenEnvValueOk && screenEnvValue > 0) +- || (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar) && qgetenv(legacyDevicePixelEnvVar).toLower() == "auto"); ++ || (qEnvironmentVariableIsSet(legacyDevicePixelEnvVar) && ++ qgetenv(legacyDevicePixelEnvVar).compare("auto", Qt::CaseInsensitive) == 0); + } + + void QHighDpiScaling::initHighDpiScaling() +diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp +index f51af4be5c5..0be72042c6a 100644 +--- a/src/network/access/http2/http2protocol.cpp ++++ b/src/network/access/http2/http2protocol.cpp +@@ -287,7 +287,8 @@ bool is_protocol_upgraded(const QHttpNetworkReply &reply) + // Do some minimal checks here - we expect 'Upgrade: h2c' to be found. + const auto &header = reply.header(); + for (const QPair &field : header) { +- if (field.first.toLower() == "upgrade" && field.second.toLower() == "h2c") ++ if (field.first.compare("upgrade", Qt::CaseInsensitive) == 0 && ++ field.second.compare("h2c", Qt::CaseInsensitive) == 0) + return true; + } + } +diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp +index 43a8a3663ec..a015feb0444 100644 +--- a/src/network/access/qhsts.cpp ++++ b/src/network/access/qhsts.cpp +@@ -453,8 +453,7 @@ bool QHstsHeaderParser::processDirective(const QByteArray &name, const QByteArra + { + Q_ASSERT(name.size()); + // RFC6797 6.1/3 Directive names are case-insensitive +- const auto lcName = name.toLower(); +- if (lcName == "max-age") { ++ if (name.compare("max-age", Qt::CaseInsensitive) == 0) { + // RFC 6797, 6.1.1 + // The syntax of the max-age directive's REQUIRED value (after + // quoted-string unescaping, if necessary) is defined as: +@@ -477,7 +476,7 @@ bool QHstsHeaderParser::processDirective(const QByteArray &name, const QByteArra + + maxAge = age; + maxAgeFound = true; +- } else if (lcName == "includesubdomains") { ++ } else if (name.compare("includesubdomains", Qt::CaseInsensitive) == 0) { + // RFC 6797, 6.1.2. The includeSubDomains Directive. + // The OPTIONAL "includeSubDomains" directive is a valueless directive. + +diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp +index c207d6e2404..df7f87efd4c 100644 +--- a/src/network/access/qhttp2protocolhandler.cpp ++++ b/src/network/access/qhttp2protocolhandler.cpp +@@ -97,16 +97,18 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH + if (size.second > maxHeaderListSize) + break; + +- QByteArray key(field.first.toLower()); +- if (key == "connection" || key == "host" || key == "keep-alive" +- || key == "proxy-connection" || key == "transfer-encoding") ++ if (field.first.compare("connection", Qt::CaseInsensitive) == 0 || ++ field.first.compare("host", Qt::CaseInsensitive) == 0 || ++ field.first.compare("keep-alive", Qt::CaseInsensitive) == 0 || ++ field.first.compare("proxy-connection", Qt::CaseInsensitive) == 0 || ++ field.first.compare("transfer-encoding", Qt::CaseInsensitive) == 0) + continue; // Those headers are not valid (section 3.2.1) - from QSpdyProtocolHandler + // TODO: verify with specs, which fields are valid to send .... + // toLower - 8.1.2 .... "header field names MUST be converted to lowercase prior + // to their encoding in HTTP/2. + // A request or response containing uppercase header field names + // MUST be treated as malformed (Section 8.1.2.6)". +- header.push_back(HeaderField(key, field.second)); ++ header.push_back(HeaderField(field.first.toLower(), field.second)); + } + + return header; +@@ -1404,8 +1406,9 @@ bool QHttp2ProtocolHandler::tryReserveStream(const Http2::Frame &pushPromiseFram + return false; + } + +- const auto method = pseudoHeaders[":method"].toLower(); +- if (method != "get" && method != "head") ++ const QByteArray method = pseudoHeaders[":method"]; ++ if (method.compare("get", Qt::CaseInsensitive) != 0 && ++ method.compare("head", Qt::CaseInsensitive) != 0) + return false; + + QUrl url; +diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp +index 0e2c257952b..c58fd24a443 100644 +--- a/src/network/access/qhttpnetworkconnection.cpp ++++ b/src/network/access/qhttpnetworkconnection.cpp +@@ -528,7 +528,7 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke + QUrl redirectUrl; + const QList > fields = reply->header(); + for (const QNetworkReply::RawHeaderPair &header : fields) { +- if (header.first.toLower() == "location") { ++ if (header.first.compare("location", Qt::CaseInsensitive) == 0) { + redirectUrl = QUrl::fromEncoded(header.second); + break; + } +diff --git a/src/network/access/qhttpnetworkheader.cpp b/src/network/access/qhttpnetworkheader.cpp +index 3326f89d2fa..8ad01174b48 100644 +--- a/src/network/access/qhttpnetworkheader.cpp ++++ b/src/network/access/qhttpnetworkheader.cpp +@@ -64,7 +64,7 @@ qint64 QHttpNetworkHeaderPrivate::contentLength() const + QList >::ConstIterator it = fields.constBegin(), + end = fields.constEnd(); + for ( ; it != end; ++it) +- if (qstricmp("content-length", it->first) == 0) { ++ if (it->first.compare("content-length", Qt::CaseInsensitive) == 0) { + value = it->second; + break; + } +@@ -95,7 +95,7 @@ QList QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray + QList >::ConstIterator it = fields.constBegin(), + end = fields.constEnd(); + for ( ; it != end; ++it) +- if (qstricmp(name.constData(), it->first) == 0) ++ if (name.compare(it->first, Qt::CaseInsensitive) == 0) + result += it->second; + + return result; +@@ -104,7 +104,7 @@ QList QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray + void QHttpNetworkHeaderPrivate::setHeaderField(const QByteArray &name, const QByteArray &data) + { + auto firstEqualsName = [&name](const QPair &header) { +- return qstricmp(name.constData(), header.first) == 0; ++ return name.compare(header.first, Qt::CaseInsensitive) == 0; + }; + fields.erase(std::remove_if(fields.begin(), fields.end(), + firstEqualsName), +diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp +index a6573469581..c9c31723045 100644 +--- a/src/network/access/qhttpnetworkreply.cpp ++++ b/src/network/access/qhttpnetworkreply.cpp +@@ -390,7 +390,8 @@ qint64 QHttpNetworkReplyPrivate::bytesAvailable() const + bool QHttpNetworkReplyPrivate::isCompressed() + { + QByteArray encoding = headerField("content-encoding"); +- return qstricmp(encoding.constData(), "gzip") == 0 || qstricmp(encoding.constData(), "deflate") == 0; ++ return encoding.compare("gzip", Qt::CaseInsensitive) == 0 || ++ encoding.compare("deflate", Qt::CaseInsensitive) == 0; + } + + void QHttpNetworkReplyPrivate::removeAutoDecompressHeader() +@@ -401,7 +402,7 @@ void QHttpNetworkReplyPrivate::removeAutoDecompressHeader() + QList >::Iterator it = fields.begin(), + end = fields.end(); + while (it != end) { +- if (qstricmp(name.constData(), it->first.constData()) == 0) { ++ if (name.compare(it->first, Qt::CaseInsensitive) == 0) { + removedContentLength = strtoull(it->second.constData(), nullptr, 0); + fields.erase(it); + break; +diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp +index c9d658225e5..df2e4902a4b 100644 +--- a/src/network/access/qnetworkdiskcache.cpp ++++ b/src/network/access/qnetworkdiskcache.cpp +@@ -189,7 +189,7 @@ QIODevice *QNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData) + + const auto headers = metaData.rawHeaders(); + for (const auto &header : headers) { +- if (header.first.toLower() == "content-length") { ++ if (header.first.compare("content-length", Qt::CaseInsensitive) == 0) { + const qint64 size = header.second.toLongLong(); + if (size > (maximumCacheSize() * 3)/4) + return 0; +@@ -642,7 +642,7 @@ bool QCacheItem::canCompress() const + bool typeOk = false; + const auto headers = metaData.rawHeaders(); + for (const auto &header : headers) { +- if (header.first.toLower() == "content-length") { ++ if (header.first.compare("content-length", Qt::CaseInsensitive) == 0) { + qint64 size = header.second.toLongLong(); + if (size > MAX_COMPRESSION_SIZE) + return false; +@@ -650,7 +650,7 @@ bool QCacheItem::canCompress() const + sizeOk = true; + } + +- if (header.first.toLower() == "content-type") { ++ if (header.first.compare("content-type", Qt::CaseInsensitive) == 0) { + QByteArray type = header.second; + if (type.startsWith("text/") + || (type.startsWith("application/") +diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp +index 96016453c24..3c6f4aadfa8 100644 +--- a/src/network/access/qnetworkreplyhttpimpl.cpp ++++ b/src/network/access/qnetworkreplyhttpimpl.cpp +@@ -1318,7 +1318,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QListfirst.constData(), "set-cookie") == 0) ++ if (it->first.compare("set-cookie", Qt::CaseInsensitive) == 0) + value += '\n'; + else + value += ", "; +@@ -1584,7 +1584,7 @@ bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData + QUrl redirectUrl; + for ( ; it != end; ++it) { + if (httpRequest.isFollowRedirects() && +- !qstricmp(it->first.toLower().constData(), "location")) ++ !it->first.compare("location", Qt::CaseInsensitive)) + redirectUrl = QUrl::fromEncoded(it->second); + setRawHeader(it->first, it->second); + } +diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp +index 70a7f4e8bdc..c2b388bbce6 100644 +--- a/src/network/access/qnetworkrequest.cpp ++++ b/src/network/access/qnetworkrequest.cpp +@@ -918,11 +918,11 @@ static int parseHeaderName(const QByteArray &headerName) + + switch (tolower(headerName.at(0))) { + case 'c': +- if (qstricmp(headerName.constData(), "content-type") == 0) ++ if (headerName.compare("content-type", Qt::CaseInsensitive) == 0) + return QNetworkRequest::ContentTypeHeader; +- else if (qstricmp(headerName.constData(), "content-length") == 0) ++ else if (headerName.compare("content-length", Qt::CaseInsensitive) == 0) + return QNetworkRequest::ContentLengthHeader; +- else if (qstricmp(headerName.constData(), "cookie") == 0) ++ else if (headerName.compare("cookie", Qt::CaseInsensitive) == 0) + return QNetworkRequest::CookieHeader; + else if (qstricmp(headerName.constData(), "content-disposition") == 0) + return QNetworkRequest::ContentDispositionHeader; +@@ -943,21 +943,21 @@ static int parseHeaderName(const QByteArray &headerName) + break; + + case 'l': +- if (qstricmp(headerName.constData(), "location") == 0) ++ if (headerName.compare("location", Qt::CaseInsensitive) == 0) + return QNetworkRequest::LocationHeader; +- else if (qstricmp(headerName.constData(), "last-modified") == 0) ++ else if (headerName.compare("last-modified", Qt::CaseInsensitive) == 0) + return QNetworkRequest::LastModifiedHeader; + break; + + case 's': +- if (qstricmp(headerName.constData(), "set-cookie") == 0) ++ if (headerName.compare("set-cookie", Qt::CaseInsensitive) == 0) + return QNetworkRequest::SetCookieHeader; +- else if (qstricmp(headerName.constData(), "server") == 0) ++ else if (headerName.compare("server", Qt::CaseInsensitive) == 0) + return QNetworkRequest::ServerHeader; + break; + + case 'u': +- if (qstricmp(headerName.constData(), "user-agent") == 0) ++ if (headerName.compare("user-agent", Qt::CaseInsensitive) == 0) + return QNetworkRequest::UserAgentHeader; + break; + } +@@ -1100,7 +1100,7 @@ QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const + RawHeadersList::ConstIterator it = rawHeaders.constBegin(); + RawHeadersList::ConstIterator end = rawHeaders.constEnd(); + for ( ; it != end; ++it) +- if (qstricmp(it->first.constData(), key.constData()) == 0) ++ if (it->first.compare(key, Qt::CaseInsensitive) == 0) + return it; + + return end; // not found +@@ -1181,7 +1181,7 @@ void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders heade + void QNetworkHeadersPrivate::setRawHeaderInternal(const QByteArray &key, const QByteArray &value) + { + auto firstEqualsKey = [&key](const RawHeaderPair &header) { +- return qstricmp(header.first.constData(), key.constData()) == 0; ++ return header.first.compare(key, Qt::CaseInsensitive) == 0; + }; + rawHeaders.erase(std::remove_if(rawHeaders.begin(), rawHeaders.end(), + firstEqualsKey), +diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp +index a3ccf13e82f..11ea40dbce6 100644 +--- a/src/network/kernel/qauthenticator.cpp ++++ b/src/network/kernel/qauthenticator.cpp +@@ -412,7 +412,7 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList ¤t = values.at(i); +- if (current.first.toLower() != search) ++ if (current.first.compare(search, Qt::CaseInsensitive) != 0) + continue; + QByteArray str = current.second.toLower(); + if (method < Basic && str.startsWith("basic")) { +@@ -443,7 +443,7 @@ void QAuthenticatorPrivate::parseHttpResponse(const QListoptions[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm")); +- if (options.value("stale").toLower() == "true") ++ if (options.value("stale").compare("true", Qt::CaseInsensitive) == 0) + phase = Start; + if (user.isEmpty() && password.isEmpty()) + phase = Done; +@@ -630,7 +630,7 @@ static QByteArray digestMd5ResponseHelper( + hash.addData(":", 1); + hash.addData(password); + QByteArray ha1 = hash.result(); +- if (alg.toLower() == "md5-sess") { ++ if (alg.compare("md5-sess", Qt::CaseInsensitive) == 0) { + hash.reset(); + // RFC 2617 contains an error, it was: + // hash.addData(ha1); +@@ -650,7 +650,7 @@ static QByteArray digestMd5ResponseHelper( + hash.addData(method); + hash.addData(":", 1); + hash.addData(digestUri); +- if (qop.toLower() == "auth-int") { ++ if (qop.compare("auth-int", Qt::CaseInsensitive) == 0) { + hash.addData(":", 1); + hash.addData(hEntity); + } +diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp +index b543ea79810..9427c3b00d8 100644 +--- a/src/network/socket/qhttpsocketengine.cpp ++++ b/src/network/socket/qhttpsocketengine.cpp +@@ -627,10 +627,9 @@ void QHttpSocketEngine::slotSocketReadNotification() + // from http spec is also allowed. + if (proxyConnectionHeader.isEmpty()) + proxyConnectionHeader = d->reply->headerField("Connection"); +- proxyConnectionHeader = proxyConnectionHeader.toLower(); +- if (proxyConnectionHeader == "close") { ++ if (proxyConnectionHeader.compare("close", Qt::CaseSensitive) == 0) { + willClose = true; +- } else if (proxyConnectionHeader == "keep-alive") { ++ } else if (proxyConnectionHeader.compare("keep-alive", Qt::CaseInsensitive) == 0) { + willClose = false; + } else { + // no Proxy-Connection header, so use the default +diff --git a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp +index 33878a5f50f..43f2e31a49e 100644 +--- a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp ++++ b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp +@@ -415,8 +415,7 @@ static void *eglContextForContext(QOpenGLContext *context) + QPlatformNativeInterface::NativeResourceForContextFunction QEglFSIntegration::nativeResourceFunctionForContext(const QByteArray &resource) + { + #ifndef QT_NO_OPENGL +- QByteArray lowerCaseResource = resource.toLower(); +- if (lowerCaseResource == "get_egl_context") ++ if (resource.compare("get_egl_context", Qt::CaseInsensitive) == 0) + return NativeResourceForContextFunction(eglContextForContext); + #else + Q_UNUSED(resource); +diff --git a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp +index ee01a632d0f..a6ab49a004a 100644 +--- a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp ++++ b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp +@@ -858,15 +858,32 @@ void tst_QByteArray::qstricmp() + if ( actual != 0 ) { + actual = (actual < 0 ? -1 : 1); + } +- QCOMPARE(expected, actual); ++ QCOMPARE(actual, expected); ++ ++ actual = str1.toLatin1().compare(str2.toLatin1(), Qt::CaseInsensitive); ++ if ( actual != 0 ) { ++ actual = (actual < 0 ? -1 : 1); ++ } ++ QCOMPARE(actual, expected); ++ ++ actual = str1.toLatin1().compare(str2.toLatin1().constData(), Qt::CaseInsensitive); ++ if ( actual != 0 ) { ++ actual = (actual < 0 ? -1 : 1); ++ } ++ QCOMPARE(actual, expected); + } + + void tst_QByteArray::qstricmp_singularities() + { + QCOMPARE(::qstricmp(0, 0), 0); +- QVERIFY(::qstricmp(0, "a") != 0); +- QVERIFY(::qstricmp("a", 0) != 0); ++ QVERIFY(::qstricmp(0, "a") < 0); ++ QVERIFY(::qstricmp("a", 0) > 0); + QCOMPARE(::qstricmp("", ""), 0); ++ QCOMPARE(QByteArray().compare(nullptr, Qt::CaseInsensitive), 0); ++ QCOMPARE(QByteArray().compare("", Qt::CaseInsensitive), 0); ++ QVERIFY(QByteArray("a").compare(nullptr, Qt::CaseInsensitive) > 0); ++ QVERIFY(QByteArray("a").compare("", Qt::CaseInsensitive) > 0); ++ QVERIFY(QByteArray().compare("a", Qt::CaseInsensitive) < 0); + } + + void tst_QByteArray::qstrnicmp_singularities() +@@ -876,6 +893,9 @@ void tst_QByteArray::qstrnicmp_singularities() + QVERIFY(::qstrnicmp("a", 0, 123) != 0); + QCOMPARE(::qstrnicmp("", "", 123), 0); + QCOMPARE(::qstrnicmp("a", "B", 0), 0); ++ QCOMPARE(QByteArray().compare(QByteArray(), Qt::CaseInsensitive), 0); ++ QVERIFY(QByteArray().compare(QByteArray("a"), Qt::CaseInsensitive) < 0); ++ QVERIFY(QByteArray("a").compare(QByteArray(), Qt::CaseInsensitive) > 0); + } + + void tst_QByteArray::chop_data() +@@ -1759,6 +1779,12 @@ void tst_QByteArray::compare() + const bool isLess = result < 0; + const bool isGreater = result > 0; + ++ int cmp = str1.compare(str2); ++ if (cmp) ++ cmp = (cmp < 0 ? -1 : 1); ++ ++ QCOMPARE(cmp, result); ++ + // basic tests: + QCOMPARE(str1 == str2, isEqual); + QCOMPARE(str1 < str2, isLess); diff --git a/CVE-2023-32762.patch b/CVE-2023-32762.patch new file mode 100644 index 0000000000000000000000000000000000000000..33686d952ddf271a008de4ea8b38d0166b2922e3 --- /dev/null +++ b/CVE-2023-32762.patch @@ -0,0 +1,50 @@ +From 1b736a815be0222f4b24289cf17575fc15707305 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= +Date: Fri, 5 May 2023 11:07:26 +0200 +Subject: [PATCH] Hsts: match header names case insensitively + +Header field names are always considered to be case-insensitive. + +Pick-to: 6.5 6.5.1 6.2 5.15 +Fixes: QTBUG-113392 +Change-Id: Ifb4def4bb7f2ac070416cdc76581a769f1e52b43 +Reviewed-by: Qt CI Bot +Reviewed-by: Edward Welbourne +Reviewed-by: Volker Hilsheimer +--- + src/network/access/qhsts.cpp | 4 ++-- + tests/auto/network/access/hsts/tst_qhsts.cpp | 6 ++++++ + 2 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp +index 39905f354807..82deede17298 100644 +--- a/src/network/access/qhsts.cpp ++++ b/src/network/access/qhsts.cpp +@@ -327,8 +327,8 @@ quoted-pair = "\" CHAR + bool QHstsHeaderParser::parse(const QList> &headers) + { + for (const auto &h : headers) { +- // We use '==' since header name was already 'trimmed' for us: +- if (h.first == "Strict-Transport-Security") { ++ // We compare directly because header name was already 'trimmed' for us: ++ if (h.first.compare("Strict-Transport-Security", Qt::CaseInsensitive) == 0) { + header = h.second; + // RFC6797, 8.1: + // +diff --git a/tests/auto/network/access/hsts/tst_qhsts.cpp b/tests/auto/network/access/hsts/tst_qhsts.cpp +index 252f5e8f5792..97a2d2889e57 100644 +--- a/tests/auto/network/access/hsts/tst_qhsts.cpp ++++ b/tests/auto/network/access/hsts/tst_qhsts.cpp +@@ -216,6 +216,12 @@ void tst_QHsts::testSTSHeaderParser() + QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc()); + QVERIFY(parser.includeSubDomains()); + ++ list.pop_back(); ++ list << Header("strict-transport-security", "includeSubDomains;max-age=1000"); ++ QVERIFY(parser.parse(list)); ++ QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc()); ++ QVERIFY(parser.includeSubDomains()); ++ + list.pop_back(); + // Invalid (includeSubDomains twice): + list << Header("Strict-Transport-Security", "max-age = 1000 ; includeSubDomains;includeSubDomains"); diff --git a/CVE-2023-32763.patch b/CVE-2023-32763.patch new file mode 100644 index 0000000000000000000000000000000000000000..f3ef013c7e72610286998b5e960ad6714a59ba27 --- /dev/null +++ b/CVE-2023-32763.patch @@ -0,0 +1,65 @@ +From 16a18de0e297a21b87808270f7a06953be519d62 Mon Sep 17 00:00:00 2001 +From: starlet-dx <15929766099@163.com> +Date: Wed, 28 Jun 2023 14:11:54 +0800 +Subject: [PATCH 1/1] Fix CVE-2023-32763 + +Origin: +https://download.qt.io/official_releases/qt/5.15/CVE-2023-32763-qtbase-5.15.diff + +--- + src/gui/painting/qfixed_p.h | 9 +++++++++ + src/gui/text/qtextlayout.cpp | 9 ++++++--- + 2 files changed, 15 insertions(+), 3 deletions(-) + +diff --git a/src/gui/painting/qfixed_p.h b/src/gui/painting/qfixed_p.h +index 84659288..57d750a4 100644 +--- a/src/gui/painting/qfixed_p.h ++++ b/src/gui/painting/qfixed_p.h +@@ -54,6 +54,7 @@ + #include + #include "QtCore/qdebug.h" + #include "QtCore/qpoint.h" ++#include + #include "QtCore/qsize.h" + + QT_BEGIN_NAMESPACE +@@ -182,6 +183,14 @@ Q_DECL_CONSTEXPR inline bool operator<(int i, const QFixed &f) { return i * 64 < + Q_DECL_CONSTEXPR inline bool operator>(const QFixed &f, int i) { return f.value() > i * 64; } + Q_DECL_CONSTEXPR inline bool operator>(int i, const QFixed &f) { return i * 64 > f.value(); } + ++inline bool qAddOverflow(QFixed v1, QFixed v2, QFixed *r) ++{ ++ int val; ++ bool result = add_overflow(v1.value(), v2.value(), &val); ++ r->setValue(val); ++ return result; ++} ++ + #ifndef QT_NO_DEBUG_STREAM + inline QDebug &operator<<(QDebug &dbg, const QFixed &f) + { return dbg << f.toReal(); } +diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp +index 6e824b2e..b4231a8a 100644 +--- a/src/gui/text/qtextlayout.cpp ++++ b/src/gui/text/qtextlayout.cpp +@@ -2051,11 +2051,14 @@ found: + eng->maxWidth = qMax(eng->maxWidth, line.textWidth); + } else { + eng->minWidth = qMax(eng->minWidth, lbh.minw); +- eng->maxWidth += line.textWidth; ++ if (qAddOverflow(eng->maxWidth, line.textWidth, &eng->maxWidth)) ++ eng->maxWidth = QFIXED_MAX; + } + +- if (line.textWidth > 0 && item < eng->layoutData->items.size()) +- eng->maxWidth += lbh.spaceData.textWidth; ++ if (line.textWidth > 0 && item < eng->layoutData->items.size()) { ++ if (qAddOverflow(eng->maxWidth, lbh.spaceData.textWidth, &eng->maxWidth)) ++ eng->maxWidth = QFIXED_MAX; ++ } + if (eng->option.flags() & QTextOption::IncludeTrailingSpaces) + line.textWidth += lbh.spaceData.textWidth; + if (lbh.spaceData.length) { +-- +2.30.0 + diff --git a/qt5-qtbase.spec b/qt5-qtbase.spec index 4235c74371159b5ce859fd15b54647b260ee98ec..857387565f238a226a636b912abf9d311366fc23 100644 --- a/qt5-qtbase.spec +++ b/qt5-qtbase.spec @@ -13,7 +13,7 @@ Name: qt5-qtbase Summary: Core component of Qt toolkit Version: 5.11.1 -Release: 14 +Release: 15 License: LGPLv2 with exceptions or GPLv3 with exceptions Url: http://qt-project.org/ Source0: https://download.qt.io/new_archive/qt/5.11/%{version}/submodules/qtbase-everywhere-src-%{version}.tar.xz @@ -43,6 +43,9 @@ Patch6003: CVE-2021-38593.patch Patch6004: CVE-2022-25255.patch # https://download.qt.io/official_releases/qt/5.15/CVE-2023-24607-qtbase-5.15.diff Patch6005: CVE-2023-24607.patch +Patch6006: CVE-2023-32762-pre.patch +Patch6007: CVE-2023-32762.patch +Patch6008: CVE-2023-32763.patch BuildRequires: pkgconfig(libsystemd) cups-devel desktop-file-utils findutils BuildRequires: libjpeg-devel libmng-devel libtiff-devel pkgconfig(alsa) @@ -409,6 +412,9 @@ fi %changelog +* Wed Jun 28 2023 yaoxin - 5.11.1-15 +- Fix CVE-2023-32762 and CVE-2023-32763 + * Mon May 22 2023 douyan - 5.11.1-14 - Fix CVE-2023-24607