|
|
6f3f5b |
Make the XML entity recursion check more precise.
|
|
|
6f3f5b |
|
|
|
6f3f5b |
libxml doesn't detect entity recursion specifically but has a variety
|
|
|
6f3f5b |
of related checks, such as entities not expanding too deeply or
|
|
|
6f3f5b |
producing exponential blow-ups in content.
|
|
|
6f3f5b |
|
|
|
6f3f5b |
Because entity declarations are parsed in a separate context with
|
|
|
6f3f5b |
their own element recursion budget, a recursive entity can overflow
|
|
|
6f3f5b |
the stack using a lot of open elements (but within the per-context
|
|
|
6f3f5b |
limit) as it slowly consumes (but does not exhaust) the entity depth
|
|
|
6f3f5b |
budget.
|
|
|
6f3f5b |
|
|
|
6f3f5b |
This adds a specific, precise check for recursive entities that
|
|
|
6f3f5b |
detects entity recursion specifically and fails immediately.
|
|
|
6f3f5b |
|
|
|
6f3f5b |
The existing entity expansion depth checks are still relevant for long
|
|
|
6f3f5b |
chains of different entities.
|
|
|
6f3f5b |
|
|
|
6f3f5b |
BUG=628581
|
|
|
6f3f5b |
|
|
|
6f3f5b |
Review-Url: https://codereview.chromium.org/2539003002
|
|
|
6f3f5b |
Cr-Commit-Position: refs/heads/master@{#436899}
|
|
|
6f3f5b |
|
|
|
6f3f5b |
|
|
|
6f3f5b |
Index: libxml2-2.9.4/entities.c
|
|
|
6f3f5b |
===================================================================
|
|
|
6f3f5b |
--- libxml2-2.9.4.orig/entities.c
|
|
|
6f3f5b |
+++ libxml2-2.9.4/entities.c
|
|
|
6f3f5b |
@@ -159,6 +159,7 @@ xmlCreateEntity(xmlDictPtr dict, const x
|
|
|
6f3f5b |
memset(ret, 0, sizeof(xmlEntity));
|
|
|
6f3f5b |
ret->type = XML_ENTITY_DECL;
|
|
|
6f3f5b |
ret->checked = 0;
|
|
|
6f3f5b |
+ ret->guard = XML_ENTITY_NOT_BEING_CHECKED;
|
|
|
6f3f5b |
|
|
|
6f3f5b |
/*
|
|
|
6f3f5b |
* fill the structure.
|
|
|
6f3f5b |
@@ -931,6 +932,7 @@ xmlCopyEntity(xmlEntityPtr ent) {
|
|
|
6f3f5b |
cur->orig = xmlStrdup(ent->orig);
|
|
|
6f3f5b |
if (ent->URI != NULL)
|
|
|
6f3f5b |
cur->URI = xmlStrdup(ent->URI);
|
|
|
6f3f5b |
+ cur->guard = 0;
|
|
|
6f3f5b |
return(cur);
|
|
|
6f3f5b |
}
|
|
|
6f3f5b |
|
|
|
6f3f5b |
Index: libxml2-2.9.4/include/libxml/entities.h
|
|
|
6f3f5b |
===================================================================
|
|
|
6f3f5b |
--- libxml2-2.9.4.orig/include/libxml/entities.h
|
|
|
6f3f5b |
+++ libxml2-2.9.4/include/libxml/entities.h
|
|
|
6f3f5b |
@@ -30,6 +30,11 @@ typedef enum {
|
|
|
6f3f5b |
XML_INTERNAL_PREDEFINED_ENTITY = 6
|
|
|
6f3f5b |
} xmlEntityType;
|
|
|
6f3f5b |
|
|
|
6f3f5b |
+typedef enum {
|
|
|
6f3f5b |
+ XML_ENTITY_NOT_BEING_CHECKED,
|
|
|
6f3f5b |
+ XML_ENTITY_BEING_CHECKED /* entity check is in progress */
|
|
|
6f3f5b |
+} xmlEntityRecursionGuard;
|
|
|
6f3f5b |
+
|
|
|
6f3f5b |
/*
|
|
|
6f3f5b |
* An unit of storage for an entity, contains the string, the value
|
|
|
6f3f5b |
* and the linkind data needed for the linking in the hash table.
|
|
|
6f3f5b |
@@ -60,6 +65,7 @@ struct _xmlEntity {
|
|
|
6f3f5b |
/* this is also used to count entities
|
|
|
6f3f5b |
* references done from that entity
|
|
|
6f3f5b |
* and if it contains '<' */
|
|
|
6f3f5b |
+ xmlEntityRecursionGuard guard;
|
|
|
6f3f5b |
};
|
|
|
6f3f5b |
|
|
|
6f3f5b |
/*
|
|
|
6f3f5b |
Index: libxml2-2.9.4/parser.c
|
|
|
6f3f5b |
===================================================================
|
|
|
6f3f5b |
--- libxml2-2.9.4.orig/parser.c
|
|
|
6f3f5b |
+++ libxml2-2.9.4/parser.c
|
|
|
6f3f5b |
@@ -133,6 +133,10 @@ xmlParserEntityCheck(xmlParserCtxtPtr ct
|
|
|
6f3f5b |
if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP)
|
|
|
6f3f5b |
return (1);
|
|
|
6f3f5b |
|
|
|
6f3f5b |
+ if ((ent != NULL) && (ent->guard == XML_ENTITY_BEING_CHECKED)) {
|
|
|
6f3f5b |
+ xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
|
|
|
6f3f5b |
+ return (1);
|
|
|
6f3f5b |
+ }
|
|
|
6f3f5b |
/*
|
|
|
6f3f5b |
* This may look absurd but is needed to detect
|
|
|
6f3f5b |
* entities problems
|
|
|
6f3f5b |
@@ -143,12 +147,14 @@ xmlParserEntityCheck(xmlParserCtxtPtr ct
|
|
|
6f3f5b |
unsigned long oldnbent = ctxt->nbentities;
|
|
|
6f3f5b |
xmlChar *rep;
|
|
|
6f3f5b |
|
|
|
6f3f5b |
+ ent->guard = XML_ENTITY_BEING_CHECKED;
|
|
|
6f3f5b |
ent->checked = 1;
|
|
|
6f3f5b |
|
|
|
6f3f5b |
++ctxt->depth;
|
|
|
6f3f5b |
rep = xmlStringDecodeEntities(ctxt, ent->content,
|
|
|
6f3f5b |
XML_SUBSTITUTE_REF, 0, 0, 0);
|
|
|
6f3f5b |
--ctxt->depth;
|
|
|
6f3f5b |
+ ent->guard = XML_ENTITY_NOT_BEING_CHECKED;
|
|
|
6f3f5b |
if (ctxt->errNo == XML_ERR_ENTITY_LOOP) {
|
|
|
6f3f5b |
ent->content[0] = 0;
|
|
|
6f3f5b |
}
|
|
|
6f3f5b |
@@ -7337,23 +7343,28 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
|
|
|
6f3f5b |
* if its replacement text matches the production labeled
|
|
|
6f3f5b |
* content.
|
|
|
6f3f5b |
*/
|
|
|
6f3f5b |
- if (ent->etype == XML_INTERNAL_GENERAL_ENTITY) {
|
|
|
6f3f5b |
- ctxt->depth++;
|
|
|
6f3f5b |
- ret = xmlParseBalancedChunkMemoryInternal(ctxt, ent->content,
|
|
|
6f3f5b |
- user_data, &list);
|
|
|
6f3f5b |
- ctxt->depth--;
|
|
|
6f3f5b |
-
|
|
|
6f3f5b |
- } else if (ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
|
|
|
6f3f5b |
- ctxt->depth++;
|
|
|
6f3f5b |
- ret = xmlParseExternalEntityPrivate(ctxt->myDoc, ctxt, ctxt->sax,
|
|
|
6f3f5b |
- user_data, ctxt->depth, ent->URI,
|
|
|
6f3f5b |
- ent->ExternalID, &list);
|
|
|
6f3f5b |
- ctxt->depth--;
|
|
|
6f3f5b |
- } else {
|
|
|
6f3f5b |
- ret = XML_ERR_ENTITY_PE_INTERNAL;
|
|
|
6f3f5b |
- xmlErrMsgStr(ctxt, XML_ERR_INTERNAL_ERROR,
|
|
|
6f3f5b |
- "invalid entity type found\n", NULL);
|
|
|
6f3f5b |
- }
|
|
|
6f3f5b |
+ if (ent->guard == XML_ENTITY_BEING_CHECKED) {
|
|
|
6f3f5b |
+ ret = XML_ERR_ENTITY_LOOP;
|
|
|
6f3f5b |
+ } else {
|
|
|
6f3f5b |
+ ent->guard = XML_ENTITY_BEING_CHECKED;
|
|
|
6f3f5b |
+ if (ent->etype == XML_INTERNAL_GENERAL_ENTITY) {
|
|
|
6f3f5b |
+ ctxt->depth++;
|
|
|
6f3f5b |
+ ret = xmlParseBalancedChunkMemoryInternal(ctxt, ent->content,
|
|
|
6f3f5b |
+ user_data, &list);
|
|
|
6f3f5b |
+ ctxt->depth--;
|
|
|
6f3f5b |
+ } else if (ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
|
|
|
6f3f5b |
+ ctxt->depth++;
|
|
|
6f3f5b |
+ ret = xmlParseExternalEntityPrivate(ctxt->myDoc, ctxt, ctxt->sax,
|
|
|
6f3f5b |
+ user_data, ctxt->depth, ent->URI,
|
|
|
6f3f5b |
+ ent->ExternalID, &list);
|
|
|
6f3f5b |
+ ctxt->depth--;
|
|
|
6f3f5b |
+ } else {
|
|
|
6f3f5b |
+ ret = XML_ERR_ENTITY_PE_INTERNAL;
|
|
|
6f3f5b |
+ xmlErrMsgStr(ctxt, XML_ERR_INTERNAL_ERROR,
|
|
|
6f3f5b |
+ "invalid entity type found\n", NULL);
|
|
|
6f3f5b |
+ }
|
|
|
6f3f5b |
+ ent->guard = XML_ENTITY_NOT_BEING_CHECKED;
|
|
|
6f3f5b |
+ }
|
|
|
6f3f5b |
|
|
|
6f3f5b |
/*
|
|
|
6f3f5b |
* Store the number of entities needing parsing for this entity
|
|
|
6f3f5b |
@@ -7456,23 +7467,29 @@ xmlParseReference(xmlParserCtxtPtr ctxt)
|
|
|
6f3f5b |
else
|
|
|
6f3f5b |
user_data = ctxt->userData;
|
|
|
6f3f5b |
|
|
|
6f3f5b |
- if (ent->etype == XML_INTERNAL_GENERAL_ENTITY) {
|
|
|
6f3f5b |
- ctxt->depth++;
|
|
|
6f3f5b |
- ret = xmlParseBalancedChunkMemoryInternal(ctxt,
|
|
|
6f3f5b |
- ent->content, user_data, NULL);
|
|
|
6f3f5b |
- ctxt->depth--;
|
|
|
6f3f5b |
- } else if (ent->etype ==
|
|
|
6f3f5b |
- XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
|
|
|
6f3f5b |
- ctxt->depth++;
|
|
|
6f3f5b |
- ret = xmlParseExternalEntityPrivate(ctxt->myDoc, ctxt,
|
|
|
6f3f5b |
- ctxt->sax, user_data, ctxt->depth,
|
|
|
6f3f5b |
- ent->URI, ent->ExternalID, NULL);
|
|
|
6f3f5b |
- ctxt->depth--;
|
|
|
6f3f5b |
- } else {
|
|
|
6f3f5b |
- ret = XML_ERR_ENTITY_PE_INTERNAL;
|
|
|
6f3f5b |
- xmlErrMsgStr(ctxt, XML_ERR_INTERNAL_ERROR,
|
|
|
6f3f5b |
- "invalid entity type found\n", NULL);
|
|
|
6f3f5b |
- }
|
|
|
6f3f5b |
+ if (ent->guard == XML_ENTITY_BEING_CHECKED) {
|
|
|
6f3f5b |
+ ret = XML_ERR_ENTITY_LOOP;
|
|
|
6f3f5b |
+ } else {
|
|
|
6f3f5b |
+ ent->guard = XML_ENTITY_BEING_CHECKED;
|
|
|
6f3f5b |
+ if (ent->etype == XML_INTERNAL_GENERAL_ENTITY) {
|
|
|
6f3f5b |
+ ctxt->depth++;
|
|
|
6f3f5b |
+ ret = xmlParseBalancedChunkMemoryInternal(ctxt,
|
|
|
6f3f5b |
+ ent->content, user_data, NULL);
|
|
|
6f3f5b |
+ ctxt->depth--;
|
|
|
6f3f5b |
+ } else if (ent->etype ==
|
|
|
6f3f5b |
+ XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
|
|
|
6f3f5b |
+ ctxt->depth++;
|
|
|
6f3f5b |
+ ret = xmlParseExternalEntityPrivate(ctxt->myDoc, ctxt,
|
|
|
6f3f5b |
+ ctxt->sax, user_data, ctxt->depth,
|
|
|
6f3f5b |
+ ent->URI, ent->ExternalID, NULL);
|
|
|
6f3f5b |
+ ctxt->depth--;
|
|
|
6f3f5b |
+ } else {
|
|
|
6f3f5b |
+ ret = XML_ERR_ENTITY_PE_INTERNAL;
|
|
|
6f3f5b |
+ xmlErrMsgStr(ctxt, XML_ERR_INTERNAL_ERROR,
|
|
|
6f3f5b |
+ "invalid entity type found\n", NULL);
|
|
|
6f3f5b |
+ }
|
|
|
6f3f5b |
+ ent->guard = XML_ENTITY_NOT_BEING_CHECKED;
|
|
|
6f3f5b |
+ }
|
|
|
6f3f5b |
if (ret == XML_ERR_ENTITY_LOOP) {
|
|
|
6f3f5b |
xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
|
|
|
6f3f5b |
return;
|