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;