diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/serialization/qxmlstream.cpp | 36 | ||||
-rw-r--r-- | src/corelib/serialization/qxmlstream.g | 14 | ||||
-rw-r--r-- | src/corelib/serialization/qxmlstream.h | 2 | ||||
-rw-r--r-- | src/corelib/serialization/qxmlstream_p.h | 14 |
4 files changed, 64 insertions, 2 deletions
diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp index 7ff87885a5..d7fb0d0d41 100644 --- a/src/corelib/serialization/qxmlstream.cpp +++ b/src/corelib/serialization/qxmlstream.cpp @@ -2041,6 +2041,42 @@ QStringRef QXmlStreamReader::dtdSystemId() const return QStringRef(); } +/*! + \since 5.15 + + Returns the maximum amount of characters a single entity is + allowed to expand into. If a single entity expands past the + given limit, the document is not considered well formed. + + \sa setEntityExpansionLimit +*/ +int QXmlStreamReader::entityExpansionLimit() const +{ + Q_D(const QXmlStreamReader); + return d->entityExpansionLimit; +} + +/*! + \since 5.15 + + Sets the maximum amount of characters a single entity is + allowed to expand into to \a limit. If a single entity expands + past the given limit, the document is not considered well formed. + + The limit is there to prevent DoS attacks when loading unknown + XML documents where recursive entity expansion could otherwise + exhaust all available memory. + + The default value for this property is 4096 characters. + + \sa entityExpansionLimit +*/ +void QXmlStreamReader::setEntityExpansionLimit(int limit) +{ + Q_D(QXmlStreamReader); + d->entityExpansionLimit = limit; +} + /*! If the tokenType() is \l StartElement, this function returns the element's namespace declarations. Otherwise an empty vector is returned. diff --git a/src/corelib/serialization/qxmlstream.g b/src/corelib/serialization/qxmlstream.g index 12ecc9bdb2..b623de9505 100644 --- a/src/corelib/serialization/qxmlstream.g +++ b/src/corelib/serialization/qxmlstream.g @@ -285,9 +285,19 @@ public: QHash<QStringView, Entity> entityHash; QHash<QStringView, Entity> parameterEntityHash; QXmlStreamSimpleStack<Entity *>entityReferenceStack; + int entityExpansionLimit = 4096; + int entityLength = 0; inline bool referenceEntity(Entity &entity) { if (entity.isCurrentlyReferenced) { - raiseWellFormedError(QXmlStream::tr("Recursive entity detected.")); + raiseWellFormedError(QXmlStream::tr("Self-referencing entity detected.")); + return false; + } + // entityLength represents the amount of additional characters the + // entity expands into (can be negative for e.g. &). It's used to + // avoid DoS attacks through recursive entity expansions + entityLength += entity.value.size() - entity.name.size() - 2; + if (entityLength > entityExpansionLimit) { + raiseWellFormedError(QXmlStream::tr("Entity expands to more characters than the entity expansion limit.")); return false; } entity.isCurrentlyReferenced = true; @@ -838,6 +848,8 @@ entity_done ::= ENTITY_DONE; /. case $rule_number: entityReferenceStack.pop()->isCurrentlyReferenced = false; + if (entityReferenceStack.isEmpty()) + entityLength = 0; clearSym(); break; ./ diff --git a/src/corelib/serialization/qxmlstream.h b/src/corelib/serialization/qxmlstream.h index 7d0aa64570..c8647e0465 100644 --- a/src/corelib/serialization/qxmlstream.h +++ b/src/corelib/serialization/qxmlstream.h @@ -426,6 +426,8 @@ public: QStringRef dtdPublicId() const; QStringRef dtdSystemId() const; + int entityExpansionLimit() const; + void setEntityExpansionLimit(int limit); enum Error { NoError, diff --git a/src/corelib/serialization/qxmlstream_p.h b/src/corelib/serialization/qxmlstream_p.h index 9c94e6d434..103b123b10 100644 --- a/src/corelib/serialization/qxmlstream_p.h +++ b/src/corelib/serialization/qxmlstream_p.h @@ -774,9 +774,19 @@ public: QHash<QStringView, Entity> entityHash; QHash<QStringView, Entity> parameterEntityHash; QXmlStreamSimpleStack<Entity *>entityReferenceStack; + int entityExpansionLimit = 4096; + int entityLength = 0; inline bool referenceEntity(Entity &entity) { if (entity.isCurrentlyReferenced) { - raiseWellFormedError(QXmlStream::tr("Recursive entity detected.")); + raiseWellFormedError(QXmlStream::tr("Self-referencing entity detected.")); + return false; + } + // entityLength represents the amount of additional characters the + // entity expands into (can be negative for e.g. &). It's used to + // avoid DoS attacks through recursive entity expansions + entityLength += entity.value.size() - entity.name.size() - 2; + if (entityLength > entityExpansionLimit) { + raiseWellFormedError(QXmlStream::tr("Entity expands to more characters than the entity expansion limit.")); return false; } entity.isCurrentlyReferenced = true; @@ -1308,6 +1318,8 @@ bool QXmlStreamReaderPrivate::parse() case 10: entityReferenceStack.pop()->isCurrentlyReferenced = false; + if (entityReferenceStack.isEmpty()) + entityLength = 0; clearSym(); break; |