diff --git a/0147-Disallow-deep-or-widely-nested-entity-references.patch b/0147-Disallow-deep-or-widely-nested-entity-references.patch new file mode 100644 index 0000000..4e609d9 --- /dev/null +++ b/0147-Disallow-deep-or-widely-nested-entity-references.patch @@ -0,0 +1,124 @@ +From 512a1ce0698d370c313bb561bbf078935fa0342e Mon Sep 17 00:00:00 2001 +From: Mitch Curtis +Date: Thu, 7 Nov 2013 09:36:29 +0100 +Subject: [PATCH 147/192] Disallow deep or widely nested entity references. + +Nested references with a depth of 2 or greater will fail. References +that partially expand to greater than 1024 characters will also fail. + +This is a backport of 46a8885ae486e238a39efa5119c2714f328b08e4. + +Change-Id: I0c2e1fa13d6ccb5f88641dae2ed3f28bfdeaf609 +Reviewed-by: Richard J. Moore +Reviewed-by: Lars Knoll +--- + src/xml/sax/qxml.cpp | 51 +++++++++++++++++++ + .../auto/qxmlsimplereader/tst_qxmlsimplereader.cpp | 58 ++++++++++++++++++++++ + .../xmldocs/1-levels-nested-dtd.xml | 12 +++++ + .../xmldocs/2-levels-nested-dtd.xml | 13 +++++ + .../internal-entity-polynomial-attribute.xml | 13 +++++ + 5 files changed, 147 insertions(+) + create mode 100644 tests/auto/qxmlsimplereader/xmldocs/1-levels-nested-dtd.xml + create mode 100644 tests/auto/qxmlsimplereader/xmldocs/2-levels-nested-dtd.xml + create mode 100644 tests/auto/qxmlsimplereader/xmldocs/internal-entity-polynomial-attribute.xml + +diff --git a/src/xml/sax/qxml.cpp b/src/xml/sax/qxml.cpp +index a1777c5..3904632 100644 +--- a/src/xml/sax/qxml.cpp ++++ b/src/xml/sax/qxml.cpp +@@ -424,6 +424,10 @@ private: + int stringValueLen; + QString emptyStr; + ++ // The limit to the amount of times the DTD parsing functions can be called ++ // for the DTD currently being parsed. ++ int dtdRecursionLimit; ++ + const QString &string(); + void stringClear(); + void stringAddC(QChar); +@@ -492,6 +496,7 @@ private: + void unexpectedEof(ParseFunction where, int state); + void parseFailed(ParseFunction where, int state); + void pushParseState(ParseFunction function, int state); ++ bool isPartiallyExpandedEntityValueTooLarge(QString *errorMessage); + + Q_DECLARE_PUBLIC(QXmlSimpleReader) + QXmlSimpleReader *q_ptr; +@@ -2759,6 +2764,7 @@ QXmlSimpleReaderPrivate::QXmlSimpleReaderPrivate(QXmlSimpleReader *reader) + useNamespacePrefixes = false; + reportWhitespaceCharData = true; + reportEntities = false; ++ dtdRecursionLimit = 2; + } + + QXmlSimpleReaderPrivate::~QXmlSimpleReaderPrivate() +@@ -5018,6 +5024,11 @@ bool QXmlSimpleReaderPrivate::parseDoctype() + } + break; + case Mup: ++ if (dtdRecursionLimit > 0 && parameterEntities.size() > dtdRecursionLimit) { ++ reportParseError(QString::fromLatin1( ++ "DTD parsing exceeded recursion limit of %1.").arg(dtdRecursionLimit)); ++ return false; ++ } + if (!parseMarkupdecl()) { + parseFailed(&QXmlSimpleReaderPrivate::parseDoctype, state); + return false; +@@ -6627,6 +6638,37 @@ bool QXmlSimpleReaderPrivate::parseChoiceSeq() + return false; + } + ++bool QXmlSimpleReaderPrivate::isPartiallyExpandedEntityValueTooLarge(QString *errorMessage) ++{ ++ const QString value = string(); ++ QMap referencedEntityCounts; ++ foreach (QString entityName, entities.keys()) { ++ for (int i = 0; i < value.size() && i != -1; ) { ++ i = value.indexOf(entityName, i); ++ if (i != -1) { ++ // The entityName we're currently trying to find ++ // was matched in this string; increase our count. ++ ++referencedEntityCounts[entityName]; ++ i += entityName.size(); ++ } ++ } ++ } ++ ++ foreach (QString entityName, referencedEntityCounts.keys()) { ++ const int timesReferenced = referencedEntityCounts[entityName]; ++ const QString entityValue = entities[entityName]; ++ if (entityValue.size() * timesReferenced > 1024) { ++ if (errorMessage) { ++ *errorMessage = QString::fromLatin1("The XML entity \"%1\"" ++ "expands too a string that is too large to process when " ++ "referencing \"%2\" %3 times.").arg(entityName).arg(entityName).arg(timesReferenced); ++ } ++ return true; ++ } ++ } ++ return false; ++} ++ + /* + Parse a EntityDecl [70]. + +@@ -6721,6 +6763,15 @@ bool QXmlSimpleReaderPrivate::parseEntityDecl() + switch (state) { + case EValue: + if ( !entityExist(name())) { ++ QString errorMessage; ++ if (isPartiallyExpandedEntityValueTooLarge(&errorMessage)) { ++ // The entity at entityName is entityValue.size() characters ++ // long in its unexpanded form, and was mentioned timesReferenced times, ++ // resulting in a string that would be greater than 1024 characters. ++ reportParseError(errorMessage); ++ return false; ++ } ++ + entities.insert(name(), string()); + if (declHnd) { + if (!declHnd->internalEntityDecl(name(), string())) { +-- +1.8.4.2 + diff --git a/0162-Fully-expand-entities-to-ensure-deep-or-widely-neste.patch b/0162-Fully-expand-entities-to-ensure-deep-or-widely-neste.patch new file mode 100644 index 0000000..89fb81e --- /dev/null +++ b/0162-Fully-expand-entities-to-ensure-deep-or-widely-neste.patch @@ -0,0 +1,128 @@ +From cecceb0cdd87482124a73ecf537f3445d68be13e Mon Sep 17 00:00:00 2001 +From: Mitch Curtis +Date: Tue, 12 Nov 2013 13:44:56 +0100 +Subject: [PATCH 162/192] Fully expand entities to ensure deep or widely nested + ones fail parsing + +With 512a1ce0698d370c313bb561bbf078935fa0342e, we failed when parsing +entities whose partially expanded size was greater than 1024 +characters. That was not enough, so now we fully expand all entities. + +This is a backport of f1053d94f59f053ce4acad9320df14f1fbe4faac. + +Change-Id: I41dd6f4525c63e82fd320a22d19248169627f7e0 +Reviewed-by: Richard J. Moore +--- + src/xml/sax/qxml.cpp | 61 +++++++++++++--------- + .../auto/qxmlsimplereader/tst_qxmlsimplereader.cpp | 2 +- + 2 files changed, 37 insertions(+), 26 deletions(-) + +diff --git a/src/xml/sax/qxml.cpp b/src/xml/sax/qxml.cpp +index 3904632..befa801 100644 +--- a/src/xml/sax/qxml.cpp ++++ b/src/xml/sax/qxml.cpp +@@ -426,7 +426,9 @@ private: + + // The limit to the amount of times the DTD parsing functions can be called + // for the DTD currently being parsed. +- int dtdRecursionLimit; ++ static const int dtdRecursionLimit = 2; ++ // The maximum amount of characters an entity value may contain, after expansion. ++ static const int entityCharacterLimit = 1024; + + const QString &string(); + void stringClear(); +@@ -496,7 +498,7 @@ private: + void unexpectedEof(ParseFunction where, int state); + void parseFailed(ParseFunction where, int state); + void pushParseState(ParseFunction function, int state); +- bool isPartiallyExpandedEntityValueTooLarge(QString *errorMessage); ++ bool isExpandedEntityValueTooLarge(QString *errorMessage); + + Q_DECLARE_PUBLIC(QXmlSimpleReader) + QXmlSimpleReader *q_ptr; +@@ -2764,7 +2766,6 @@ QXmlSimpleReaderPrivate::QXmlSimpleReaderPrivate(QXmlSimpleReader *reader) + useNamespacePrefixes = false; + reportWhitespaceCharData = true; + reportEntities = false; +- dtdRecursionLimit = 2; + } + + QXmlSimpleReaderPrivate::~QXmlSimpleReaderPrivate() +@@ -6638,30 +6639,43 @@ bool QXmlSimpleReaderPrivate::parseChoiceSeq() + return false; + } + +-bool QXmlSimpleReaderPrivate::isPartiallyExpandedEntityValueTooLarge(QString *errorMessage) ++bool QXmlSimpleReaderPrivate::isExpandedEntityValueTooLarge(QString *errorMessage) + { +- const QString value = string(); +- QMap referencedEntityCounts; +- foreach (QString entityName, entities.keys()) { +- for (int i = 0; i < value.size() && i != -1; ) { +- i = value.indexOf(entityName, i); +- if (i != -1) { +- // The entityName we're currently trying to find +- // was matched in this string; increase our count. +- ++referencedEntityCounts[entityName]; +- i += entityName.size(); ++ QMap literalEntitySizes; ++ // The entity at (QMap) times. ++ QMap > referencesToOtherEntities; ++ QMap expandedSizes; ++ ++ // For every entity, check how many times all entity names were referenced in its value. ++ foreach (QString toSearch, entities.keys()) { ++ // The amount of characters that weren't entity names, but literals, like 'X'. ++ QString leftOvers = entities.value(toSearch); ++ // How many times was entityName referenced by toSearch? ++ foreach (QString entityName, entities.keys()) { ++ for (int i = 0; i < leftOvers.size() && i != -1; ) { ++ i = leftOvers.indexOf(QString::fromLatin1("&%1;").arg(entityName), i); ++ if (i != -1) { ++ leftOvers.remove(i, entityName.size() + 2); ++ // The entityName we're currently trying to find was matched in this string; increase our count. ++ ++referencesToOtherEntities[toSearch][entityName]; ++ } + } + } ++ literalEntitySizes[toSearch] = leftOvers.size(); + } + +- foreach (QString entityName, referencedEntityCounts.keys()) { +- const int timesReferenced = referencedEntityCounts[entityName]; +- const QString entityValue = entities[entityName]; +- if (entityValue.size() * timesReferenced > 1024) { ++ foreach (QString entity, referencesToOtherEntities.keys()) { ++ expandedSizes[entity] = literalEntitySizes[entity]; ++ foreach (QString referenceTo, referencesToOtherEntities.value(entity).keys()) { ++ const int references = referencesToOtherEntities.value(entity).value(referenceTo); ++ // The total size of an entity's value is the expanded size of all of its referenced entities, plus its literal size. ++ expandedSizes[entity] += expandedSizes[referenceTo] * references + literalEntitySizes[referenceTo] * references; ++ } ++ ++ if (expandedSizes[entity] > entityCharacterLimit) { + if (errorMessage) { +- *errorMessage = QString::fromLatin1("The XML entity \"%1\"" +- "expands too a string that is too large to process when " +- "referencing \"%2\" %3 times.").arg(entityName).arg(entityName).arg(timesReferenced); ++ *errorMessage = QString::fromLatin1("The XML entity \"%1\" expands too a string that is too large to process (%2 characters > %3)."); ++ *errorMessage = (*errorMessage).arg(entity).arg(expandedSizes[entity]).arg(entityCharacterLimit); + } + return true; + } +@@ -6764,10 +6778,7 @@ bool QXmlSimpleReaderPrivate::parseEntityDecl() + case EValue: + if ( !entityExist(name())) { + QString errorMessage; +- if (isPartiallyExpandedEntityValueTooLarge(&errorMessage)) { +- // The entity at entityName is entityValue.size() characters +- // long in its unexpanded form, and was mentioned timesReferenced times, +- // resulting in a string that would be greater than 1024 characters. ++ if (isExpandedEntityValueTooLarge(&errorMessage)) { + reportParseError(errorMessage); + return false; + } +-- +1.8.4.2 + diff --git a/qt.spec b/qt.spec index d389b10..6196995 100644 --- a/qt.spec +++ b/qt.spec @@ -29,7 +29,7 @@ Summary: Qt toolkit Name: qt Epoch: 1 Version: 4.8.5 -Release: 11%{?dist} +Release: 12%{?dist} # See LGPL_EXCEPTIONS.txt, LICENSE.GPL3, respectively, for exception details License: (LGPLv2 with exceptions or GPLv3 with exceptions) and ASL 2.0 and BSD and FTL and MIT @@ -161,8 +161,14 @@ Patch113: qt-everywhere-opensource-src-4.8.5-QTBUG-22829.patch #Patch155: qt-everywhere-opensource-src-4.8-QTBUG-27809.patch ## upstream git +# related prereq patch to 0162 below +Patch1147: 0147-Disallow-deep-or-widely-nested-entity-references.patch +# CVE-2013-4549 +# http://lists.qt-project.org/pipermail/announce/2013-December/000036.html +# https://codereview.qt-project.org/#change,71010 +Patch1162: 0162-Fully-expand-entities-to-ensure-deep-or-widely-neste.patch -# security patches +## security patches # desktop files Source20: assistant.desktop @@ -529,6 +535,8 @@ rm -fv mkspecs/linux-g++*/qmake.conf.multilib-optflags #patch155 -p1 -b .QTBUG-27809 # security fixes +%patch1147 -p1 -b .0147 +%patch1162 -p1 -b .0162 # drop -fexceptions from $RPM_OPT_FLAGS RPM_OPT_FLAGS=`echo $RPM_OPT_FLAGS | sed 's|-fexceptions||g'` @@ -1220,6 +1228,9 @@ fi %changelog +* Thu Dec 05 2013 Rex Dieter 4.8.5-12 +- XML Entity Expansion Denial of Service (CVE-2013-4549) + * Wed Oct 09 2013 Rex Dieter 4.8.5-11 - Discover printers shared by CUPS 1.6 (#980952)