Blame SOURCES/libxml2-CVE-2016-9597.patch

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