From 7eaf4063830d6476ee4c429545f34a17c595ba3b Mon Sep 17 00:00:00 2001 From: Patrick Lodder Date: Sun, 29 Oct 2023 07:38:56 -0400 Subject: [PATCH 1/2] trivial: depends: one line per Qt patch --- depends/packages/qt.mk | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 7f7ff0c72..a31ee162a 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -8,7 +8,14 @@ $(package)_dependencies=openssl zlib $(package)_linux_dependencies=freetype fontconfig libxcb libxkbcommon $(package)_build_subdir=qtbase $(package)_qt_libs=corelib network widgets gui plugins testlib printsupport -$(package)_patches=mac-qmake.conf mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch fix_qfontengine_coretext.patch fix_qt_pkgconfig.patch no-xlib.patch + +$(package)_patches=mac-qmake.conf +$(package)_patches+=mingw-uuidof.patch +$(package)_patches+=pidlist_absolute.patch +$(package)_patches+=fix-xcb-include-order.patch +$(package)_patches+=fix_qfontengine_coretext.patch +$(package)_patches+=fix_qt_pkgconfig.patch +$(package)_patches+=no-xlib.patch $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) $(package)_qttranslations_sha256_hash=3a15aebd523c6d89fb97b2d3df866c94149653a26d27a00aac9b6d3020bc5a1d From 92b439f3756cc68a8c5d88698b06fe121886f732 Mon Sep 17 00:00:00 2001 From: Patrick Lodder Date: Sun, 29 Oct 2023 07:47:30 -0400 Subject: [PATCH 2/2] depends: Backport Qt bugfixes Dogecoin-Qt cannot upgrade Qt on a minor release without breaking support for older systems, and thus preventing operators on these to get important security updates. Therefore, it is important to patch the fixed version of Qt we are stuck with when applicable. This backports important bug fixes from later Qt versions that harden Dogecoin-Qt's internals: - Fix possible heap corruption in QXmlStream see: https://codereview.qt-project.org/c/qt/qtbase/+/236691 - do not attempt to load a library relative to $PWD see: https://codereview.qt-project.org/c/qt/qtbase/+/286795 - Fix specific overflow in qtextlayout see: https://codereview.qt-project.org/c/qt/qtbase/+/476125 - QDnsLookup/Unix: Ensure that both the RDATA and the Record header fields before it fall inside the buffer we have see: https://codereview.qt-project.org/c/qt/qtbase/+/477644 - Fix a crash while parsing an XML file with garbage data see: https://codereview.qt-project.org/c/qt/qtbase/+/471756 https://codereview.qt-project.org/c/qt/qtbase/+/455027 - Fix crash when reading corrupt font data see: https://codereview.qt-project.org/c/qt/qtbase/+/503026 --- depends/packages/qt.mk | 2 + depends/patches/qt/backports-1.patch | 483 +++++++++++++++++++++++++++ 2 files changed, 485 insertions(+) create mode 100644 depends/patches/qt/backports-1.patch diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index a31ee162a..ea2988f71 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -16,6 +16,7 @@ $(package)_patches+=fix-xcb-include-order.patch $(package)_patches+=fix_qfontengine_coretext.patch $(package)_patches+=fix_qt_pkgconfig.patch $(package)_patches+=no-xlib.patch +$(package)_patches+=backports-1.patch $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) $(package)_qttranslations_sha256_hash=3a15aebd523c6d89fb97b2d3df866c94149653a26d27a00aac9b6d3020bc5a1d @@ -182,6 +183,7 @@ define $(package)_preprocess_cmds patch -p1 < $($(package)_patch_dir)/fix_qfontengine_coretext.patch && \ patch -p1 < $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \ patch -p1 -i $($(package)_patch_dir)/no-xlib.patch && \ + patch -p1 < $($(package)_patch_dir)/backports-1.patch && \ echo "!host_build: QMAKE_CFLAGS += $($(package)_cflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ diff --git a/depends/patches/qt/backports-1.patch b/depends/patches/qt/backports-1.patch new file mode 100644 index 000000000..a34407352 --- /dev/null +++ b/depends/patches/qt/backports-1.patch @@ -0,0 +1,483 @@ +From: Patrick Lodder +Date: Sat Oct 28 19:43:22 2023 +0200 +Subject: [patch] Backport high severity Qt patches until 5.15 + +Patch qtbase 5.7.1 to include high severity patches that apply +to Dogecoin Core. + +- Fix possible heap corruption in QXmlStream + see: https://codereview.qt-project.org/c/qt/qtbase/+/236691 + +- do not attempt to load a library relative to $PWD + see: https://codereview.qt-project.org/c/qt/qtbase/+/286795 + +- Fix specific overflow in qtextlayout + see: https://codereview.qt-project.org/c/qt/qtbase/+/476125 + +- QDnsLookup/Unix: Ensure that both the RDATA and the Record header fields + before it fall inside the buffer we have + see: https://codereview.qt-project.org/c/qt/qtbase/+/477644 + +- Fix a crash while parsing an XML file with garbage data + see: https://codereview.qt-project.org/c/qt/qtbase/+/471756 + https://codereview.qt-project.org/c/qt/qtbase/+/455027 + +- Fix crash when reading corrupt font data + see: https://codereview.qt-project.org/c/qt/qtbase/+/503026 + +--------- +diff -dur a/qtbase/src/corelib/plugin/qlibrary_unix.cpp b/qtbase/src/corelib/plugin/qlibrary_unix.cpp +--- a/qtbase/src/corelib/plugin/qlibrary_unix.cpp 2016-12-01 03:17:04 ++++ b/qtbase/src/corelib/plugin/qlibrary_unix.cpp 2023-10-28 13:28:11 +@@ -216,6 +216,10 @@ + for(int suffix = 0; retry && !pHnd && suffix < suffixes.size(); suffix++) { + if (!prefixes.at(prefix).isEmpty() && name.startsWith(prefixes.at(prefix))) + continue; ++ ++ if (path.isEmpty() && prefixes.at(prefix).contains(QLatin1Char('/'))) ++ continue; ++ + if (!suffixes.at(suffix).isEmpty() && name.endsWith(suffixes.at(suffix))) + continue; + if (loadHints & QLibrary::LoadArchiveMemberHint) { +diff -dur a/qtbase/src/corelib/xml/qxmlstream.cpp b/qtbase/src/corelib/xml/qxmlstream.cpp +--- a/qtbase/src/corelib/xml/qxmlstream.cpp 2016-12-01 03:17:04 ++++ b/qtbase/src/corelib/xml/qxmlstream.cpp 2023-10-29 08:25:30 +@@ -1289,11 +1289,18 @@ + return n; + } + +-inline int QXmlStreamReaderPrivate::fastScanName(int *prefix) ++// Fast scan an XML attribute name (e.g. "xml:lang"). ++inline QXmlStreamReaderPrivate::FastScanNameResult ++QXmlStreamReaderPrivate::fastScanName(Value *val) + { + int n = 0; + uint c; + while ((c = getChar()) != StreamEOF) { ++ if (n >= 4096) { ++ // can exhaust memory, or the range of decltype(*prefix) ++ raiseNamePrefixTooLongError(); ++ return {}; ++ } + switch (c) { + case '\n': + case ' ': +@@ -1321,23 +1328,23 @@ + case '+': + case '*': + putChar(c); +- if (prefix && *prefix == n+1) { +- *prefix = 0; ++ if (val && val->prefix == n + 1) { ++ val->prefix = 0; + putChar(':'); + --n; + } +- return n; ++ return FastScanNameResult(n); + case ':': +- if (prefix) { +- if (*prefix == 0) { +- *prefix = n+2; ++ if (val) { ++ if (val->prefix == 0) { ++ val->prefix = n + 2; + } else { // only one colon allowed according to the namespace spec. + putChar(c); +- return n; ++ return FastScanNameResult(n); + } + } else { + putChar(c); +- return n; ++ return FastScanNameResult(n); + } + // fall through + default: +@@ -1346,12 +1353,12 @@ + } + } + +- if (prefix) +- *prefix = 0; ++ if (val) ++ val->prefix = 0; + int pos = textBuffer.size() - n; + putString(textBuffer, pos); + textBuffer.resize(pos); +- return 0; ++ return FastScanNameResult(0); + } + + enum NameChar { NameBeginning, NameNotBeginning, NotName }; +@@ -1856,6 +1863,14 @@ + void QXmlStreamReaderPrivate::raiseWellFormedError(const QString &message) + { + raiseError(QXmlStreamReader::NotWellFormedError, message); ++} ++ ++void QXmlStreamReaderPrivate::raiseNamePrefixTooLongError() ++{ ++ // TODO: add a ImplementationLimitsExceededError and use it instead ++ raiseError(QXmlStreamReader::NotWellFormedError, ++ QXmlStream::tr("Length of XML attribute name exceeds implemnetation limits (4KiB " ++ "characters).")); + } + + void QXmlStreamReaderPrivate::parseError() +Only in b/qtbase/src/corelib/xml: qxmlstream.cpp.orig +diff -dur a/qtbase/src/corelib/xml/qxmlstream.g b/qtbase/src/corelib/xml/qxmlstream.g +--- a/qtbase/src/corelib/xml/qxmlstream.g 2016-12-01 03:17:04 ++++ b/qtbase/src/corelib/xml/qxmlstream.g 2023-10-29 08:27:05 +@@ -490,7 +490,17 @@ + int fastScanLiteralContent(); + int fastScanSpace(); + int fastScanContentCharList(); +- int fastScanName(int *prefix = 0); ++ ++ struct FastScanNameResult { ++ FastScanNameResult() : ok(false) {} ++ explicit FastScanNameResult(int len) : addToLen(len), ok(true) { } ++ operator bool() { return ok; } ++ int operator*() { Q_ASSERT(ok); return addToLen; } ++ int addToLen; ++ bool ok; ++ }; ++ FastScanNameResult fastScanName(Value *val = nullptr); ++ + inline int fastScanNMTOKEN(); + + +@@ -499,6 +509,7 @@ + + void raiseError(QXmlStreamReader::Error error, const QString& message = QString()); + void raiseWellFormedError(const QString &message); ++ void raiseNamePrefixTooLongError(); + + QXmlStreamEntityResolver *entityResolver; + +@@ -1782,7 +1793,12 @@ + qname ::= LETTER; + /. + case $rule_number: { +- sym(1).len += fastScanName(&sym(1).prefix); ++ Value &val = sym(1); ++ if (auto res = fastScanName(&val)) ++ val.len += *res; ++ else ++ return false; ++ + if (atEnd) { + resume($rule_number); + return false; +@@ -1793,7 +1809,11 @@ + name ::= LETTER; + /. + case $rule_number: +- sym(1).len += fastScanName(); ++ if (auto res = fastScanName()) ++ sym(1).len += *res; ++ else ++ return false; ++ + if (atEnd) { + resume($rule_number); + return false; +Only in b/qtbase/src/corelib/xml: qxmlstream.g.orig +diff -dur a/qtbase/src/corelib/xml/qxmlstream_p.h b/qtbase/src/corelib/xml/qxmlstream_p.h +--- a/qtbase/src/corelib/xml/qxmlstream_p.h 2016-12-01 03:17:04 ++++ b/qtbase/src/corelib/xml/qxmlstream_p.h 2023-10-29 08:28:31 +@@ -985,7 +985,16 @@ + int fastScanLiteralContent(); + int fastScanSpace(); + int fastScanContentCharList(); +- int fastScanName(int *prefix = 0); ++ ++ struct FastScanNameResult { ++ FastScanNameResult() : ok(false) {} ++ explicit FastScanNameResult(int len) : addToLen(len), ok(true) { } ++ operator bool() { return ok; } ++ int operator*() { Q_ASSERT(ok); return addToLen; } ++ int addToLen; ++ bool ok; ++ }; ++ FastScanNameResult fastScanName(Value *val = nullptr); + inline int fastScanNMTOKEN(); + + +@@ -994,6 +1003,7 @@ + + void raiseError(QXmlStreamReader::Error error, const QString& message = QString()); + void raiseWellFormedError(const QString &message); ++ void raiseNamePrefixTooLongError(); + + QXmlStreamEntityResolver *entityResolver; + +@@ -1239,7 +1249,7 @@ + state_stack[tos] = 0; + return true; + } else if (act > 0) { +- if (++tos == stack_size-1) ++ if (++tos >= stack_size-1) + reallocateStack(); + + Value &val = sym_stack[tos]; +@@ -1914,7 +1924,12 @@ + break; + + case 262: { +- sym(1).len += fastScanName(&sym(1).prefix); ++ Value &val = sym(1); ++ if (auto res = fastScanName(&val)) ++ val.len += *res; ++ else ++ return false; ++ + if (atEnd) { + resume(262); + return false; +@@ -1922,7 +1937,11 @@ + } break; + + case 263: +- sym(1).len += fastScanName(); ++ if (auto res = fastScanName()) ++ sym(1).len += *res; ++ else ++ return false; ++ + if (atEnd) { + resume(263); + return false; +Only in b/qtbase/src/corelib/xml: qxmlstream_p.h.orig +diff -dur a/qtbase/src/gui/painting/qfixed_p.h b/qtbase/src/gui/painting/qfixed_p.h +--- a/qtbase/src/gui/painting/qfixed_p.h 2016-12-01 03:17:04 ++++ b/qtbase/src/gui/painting/qfixed_p.h 2023-10-28 13:28:11 +@@ -53,6 +53,7 @@ + + #include "QtCore/qdebug.h" + #include "QtCore/qpoint.h" ++#include "QtCore/private/qnumeric_p.h" + #include "QtCore/qsize.h" + + QT_BEGIN_NAMESPACE +@@ -167,6 +168,14 @@ + Q_DECL_CONSTEXPR inline QFixed operator+(uint i, const QFixed &d) { return d+i; } + Q_DECL_CONSTEXPR inline QFixed operator-(uint i, const QFixed &d) { return -(d-i); } + // Q_DECL_CONSTEXPR inline QFixed operator*(qreal d, const QFixed &d2) { return d2*d; } ++ ++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; ++} + + 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 f.value() == i * 64; } + +diff -dur a/qtbase/src/gui/text/qtextlayout.cpp b/qtbase/src/gui/text/qtextlayout.cpp +--- a/qtbase/src/gui/text/qtextlayout.cpp 2016-12-01 03:17:04 ++++ b/qtbase/src/gui/text/qtextlayout.cpp 2023-10-28 18:29:14 +@@ -2036,11 +2036,14 @@ + 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) { +diff -dur a/qtbase/src/network/kernel/qdnslookup_unix.cpp b/qtbase/src/network/kernel/qdnslookup_unix.cpp +--- a/qtbase/src/network/kernel/qdnslookup_unix.cpp 2016-12-01 03:17:04 ++++ b/qtbase/src/network/kernel/qdnslookup_unix.cpp 2023-10-28 18:00:41 +@@ -193,7 +193,6 @@ + + // Check the response header. + HEADER *header = (HEADER*)response; +- const int answerCount = ntohs(header->ancount); + switch (header->rcode) { + case NOERROR: + break; +@@ -226,18 +225,31 @@ + return; + } + +- // Skip the query host, type (2 bytes) and class (2 bytes). + char host[PACKETSZ], answer[PACKETSZ]; + unsigned char *p = response + sizeof(HEADER); +- int status = local_dn_expand(response, response + responseLength, p, host, sizeof(host)); +- if (status < 0) { ++ int status; ++ if (ntohs(header->qdcount) == 1) { ++ // Skip the query host, type (2 bytes) and class (2 bytes). ++ status = dn_expand(response, response + responseLength, p, host, sizeof(host)); ++ if (status < 0) { ++ reply->error = QDnsLookup::InvalidReplyError; ++ reply->errorString = tr("Could not expand domain name"); ++ return; ++ } ++ if ((p - response) + status + 4 >= responseLength) ++ header->qdcount = 0xffff; // invalid reply below ++ else ++ p += status + 4; ++ } ++ ++ if (ntohs(header->qdcount) > 1) { + reply->error = QDnsLookup::InvalidReplyError; +- reply->errorString = tr("Could not expand domain name"); ++ reply->errorString = tr("Invalid reply received"); + return; + } +- p += status + 4; + + // Extract results. ++ const int answerCount = ntohs(header->ancount); + int answerIndex = 0; + while ((p < response + responseLength) && (answerIndex < answerCount)) { + status = local_dn_expand(response, response + responseLength, p, host, sizeof(host)); +@@ -249,6 +261,10 @@ + const QString name = QUrl::fromAce(host); + + p += status; ++ if ((p - response) + 10 > responseLength) { ++ // probably just a truncated reply, return what we have ++ return; ++ } + const quint16 type = (p[0] << 8) | p[1]; + p += 2; // RR type + p += 2; // RR class +@@ -256,6 +272,9 @@ + p += 4; + const quint16 size = (p[0] << 8) | p[1]; + p += 2; ++ ++ if ((p - response) + size > responseLength) ++ return; // truncated + + if (type == QDnsLookup::A) { + if (size != 4) { +diff -dur a/qtbase/src/plugins/platforms/windows/qwindowsfontdatabase.cpp b/qtbase/src/plugins/platforms/windows/qwindowsfontdatabase.cpp +--- a/qtbase/src/plugins/platforms/windows/qwindowsfontdatabase.cpp 2016-12-01 03:17:04 ++++ b/qtbase/src/plugins/platforms/windows/qwindowsfontdatabase.cpp 2023-10-29 07:10:06 +@@ -1342,10 +1342,15 @@ + return fontEngine; + } + +-static QList getTrueTypeFontOffsets(const uchar *fontData) ++static QList getTrueTypeFontOffsets(const uchar *fontData, const uchar *fileEndSentinel) + { + QList offsets; +- const quint32 headerTag = *reinterpret_cast(fontData); ++ if (fileEndSentinel - fontData < 12) { ++ qCWarning(lcQpaFonts) << "Corrupted font data detected"; ++ return offsets; ++ } ++ ++ const quint32 headerTag = qFromUnaligned(fontData); + if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) { + if (headerTag != MAKE_TAG(0, 1, 0, 0) + && headerTag != MAKE_TAG('O', 'T', 'T', 'O') +@@ -1355,23 +1360,52 @@ + offsets << 0; + return offsets; + } ++ ++ const quint32 maximumNumFonts = 0xffff; + const quint32 numFonts = qFromBigEndian(fontData + 8); +- for (uint i = 0; i < numFonts; ++i) { +- offsets << qFromBigEndian(fontData + 12 + i * 4); ++ ++ if (numFonts > maximumNumFonts) { ++ qCWarning(lcQpaFonts) << "Font collection of" << numFonts << "fonts is too large. Aborting."; ++ return offsets; + } ++ ++ if (quintptr(fileEndSentinel - fontData) > 12 + (numFonts - 1) * 4) { ++ for (quint32 i = 0; i < numFonts; ++i) ++ offsets << qFromBigEndian(fontData + 12 + i * 4); ++ } else { ++ qCWarning(lcQpaFonts) << "Corrupted font data detected"; ++ } ++ + return offsets; + } + +-static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag, const uchar **table, quint32 *length) ++static void getFontTable(const uchar *fileBegin, const uchar *fileEndSentinel, const uchar *data, quint32 tag, const uchar **table, quint32 *length) + { +- const quint16 numTables = qFromBigEndian(data + 4); +- for (uint i = 0; i < numTables; ++i) { +- const quint32 offset = 12 + 16 * i; +- if (*reinterpret_cast(data + offset) == tag) { +- *table = fileBegin + qFromBigEndian(data + offset + 8); +- *length = qFromBigEndian(data + offset + 12); +- return; ++ if (fileEndSentinel - data >= 6) { ++ const quint16 numTables = qFromBigEndian(data + 4); ++ if (fileEndSentinel - data >= 28 + 16 * (numTables - 1)) { ++ for (quint32 i = 0; i < numTables; ++i) { ++ const quint32 offset = 12 + 16 * i; ++ if (qFromUnaligned(data + offset) == tag) { ++ const quint32 tableOffset = qFromBigEndian(data + offset + 8); ++ if (quintptr(fileEndSentinel - fileBegin) <= tableOffset) { ++ qCWarning(lcQpaFonts) << "Corrupted font data detected"; ++ break; ++ } ++ *table = fileBegin + tableOffset; ++ *length = qFromBigEndian(data + offset + 12); ++ if (quintptr(fileEndSentinel - *table) < *length) { ++ qCWarning(lcQpaFonts) << "Corrupted font data detected"; ++ break; ++ } ++ return; ++ } ++ } ++ } else { ++ qCWarning(lcQpaFonts) << "Corrupted font data detected"; + } ++ } else { ++ qCWarning(lcQpaFonts) << "Corrupted font data detected"; + } + *table = 0; + *length = 0; +@@ -1383,8 +1417,9 @@ + QVector *signatures) + { + const uchar *data = reinterpret_cast(fontData.constData()); ++ const uchar *dataEndSentinel = data + fontData.size(); + +- QList offsets = getTrueTypeFontOffsets(data); ++ QList offsets = getTrueTypeFontOffsets(data, dataEndSentinel); + if (offsets.isEmpty()) + return; + +@@ -1392,7 +1427,7 @@ + const uchar *font = data + offsets.at(i); + const uchar *table; + quint32 length; +- getFontTable(data, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length); ++ getFontTable(data, dataEndSentinel, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length); + if (!table) + continue; + QString name = getEnglishName(table, length); +@@ -1403,7 +1438,7 @@ + + if (signatures) { + FONTSIGNATURE signature; +- getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length); ++ getFontTable(data, dataEndSentinel, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length); + if (table && length >= 86) { + // Offsets taken from OS/2 table in the TrueType spec + signature.fsUsb[0] = qFromBigEndian(table + 42);