[libxml2] Make xmlDumpElementContent non-recursive



commit 24e3973bc03c15d534b2eac6e70fc2b2bef2b6c0
Author: Nick Wellnhofer <wellnhofer aevum de>
Date:   Fri Oct 4 14:42:59 2019 +0200

    Make xmlDumpElementContent non-recursive
    
    Avoid call stack overflow when dumping deeply nested element
    declarations.
    
    Found by OSS-Fuzz.

 valid.c | 157 ++++++++++++++++++++++++++++++++++++----------------------------
 1 file changed, 89 insertions(+), 68 deletions(-)
---
diff --git a/valid.c b/valid.c
index fca3cc06..eaccb11e 100644
--- a/valid.c
+++ b/valid.c
@@ -1148,83 +1148,104 @@ xmlFreeElementContent(xmlElementContentPtr cur) {
 
 #ifdef LIBXML_OUTPUT_ENABLED
 /**
- * xmlDumpElementContent:
+ * xmlDumpElementOccur:
  * @buf:  An XML buffer
- * @content:  An element table
- * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
+ * @cur:  An element table
  *
- * This will dump the content of the element table as an XML DTD definition
+ * Dump the occurence operator of an element.
  */
 static void
-xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
-    if (content == NULL) return;
-
-    if (glob) xmlBufferWriteChar(buf, "(");
-    switch (content->type) {
-        case XML_ELEMENT_CONTENT_PCDATA:
-            xmlBufferWriteChar(buf, "#PCDATA");
-           break;
-       case XML_ELEMENT_CONTENT_ELEMENT:
-           if (content->prefix != NULL) {
-               xmlBufferWriteCHAR(buf, content->prefix);
-               xmlBufferWriteChar(buf, ":");
-           }
-           xmlBufferWriteCHAR(buf, content->name);
-           break;
-       case XML_ELEMENT_CONTENT_SEQ:
-           if ((content->c1 != NULL) &&
-               ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
-                (content->c1->type == XML_ELEMENT_CONTENT_SEQ)))
-               xmlDumpElementContent(buf, content->c1, 1);
-           else
-               xmlDumpElementContent(buf, content->c1, 0);
-            xmlBufferWriteChar(buf, " , ");
-           if ((content->c2 != NULL) &&
-               ((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
-                ((content->c2->type == XML_ELEMENT_CONTENT_SEQ) &&
-                 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE))))
-               xmlDumpElementContent(buf, content->c2, 1);
-           else
-               xmlDumpElementContent(buf, content->c2, 0);
-           break;
-       case XML_ELEMENT_CONTENT_OR:
-           if ((content->c1 != NULL) &&
-               ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
-                (content->c1->type == XML_ELEMENT_CONTENT_SEQ)))
-               xmlDumpElementContent(buf, content->c1, 1);
-           else
-               xmlDumpElementContent(buf, content->c1, 0);
-            xmlBufferWriteChar(buf, " | ");
-           if ((content->c2 != NULL) &&
-               ((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
-                ((content->c2->type == XML_ELEMENT_CONTENT_OR) &&
-                 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE))))
-               xmlDumpElementContent(buf, content->c2, 1);
-           else
-               xmlDumpElementContent(buf, content->c2, 0);
-           break;
-       default:
-           xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
-                   "Internal: ELEMENT content corrupted invalid type\n",
-                   NULL);
-    }
-    if (glob)
-        xmlBufferWriteChar(buf, ")");
-    switch (content->ocur) {
+xmlDumpElementOccur(xmlBufferPtr buf, xmlElementContentPtr cur) {
+    switch (cur->ocur) {
         case XML_ELEMENT_CONTENT_ONCE:
-           break;
+            break;
         case XML_ELEMENT_CONTENT_OPT:
-           xmlBufferWriteChar(buf, "?");
-           break;
+            xmlBufferWriteChar(buf, "?");
+            break;
         case XML_ELEMENT_CONTENT_MULT:
-           xmlBufferWriteChar(buf, "*");
-           break;
+            xmlBufferWriteChar(buf, "*");
+            break;
         case XML_ELEMENT_CONTENT_PLUS:
-           xmlBufferWriteChar(buf, "+");
-           break;
+            xmlBufferWriteChar(buf, "+");
+            break;
     }
 }
 
+/**
+ * xmlDumpElementContent:
+ * @buf:  An XML buffer
+ * @content:  An element table
+ *
+ * This will dump the content of the element table as an XML DTD definition
+ */
+static void
+xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content) {
+    xmlElementContentPtr cur;
+
+    if (content == NULL) return;
+
+    xmlBufferWriteChar(buf, "(");
+    cur = content;
+
+    do {
+        if (cur == NULL) return;
+
+        switch (cur->type) {
+            case XML_ELEMENT_CONTENT_PCDATA:
+                xmlBufferWriteChar(buf, "#PCDATA");
+                break;
+            case XML_ELEMENT_CONTENT_ELEMENT:
+                if (cur->prefix != NULL) {
+                    xmlBufferWriteCHAR(buf, cur->prefix);
+                    xmlBufferWriteChar(buf, ":");
+                }
+                xmlBufferWriteCHAR(buf, cur->name);
+                break;
+            case XML_ELEMENT_CONTENT_SEQ:
+            case XML_ELEMENT_CONTENT_OR:
+                if ((cur != content) &&
+                    (cur->parent != NULL) &&
+                    ((cur->type != cur->parent->type) ||
+                     (cur->ocur != XML_ELEMENT_CONTENT_ONCE)))
+                    xmlBufferWriteChar(buf, "(");
+                cur = cur->c1;
+                continue;
+            default:
+                xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
+                        "Internal: ELEMENT cur corrupted invalid type\n",
+                        NULL);
+        }
+
+        while (cur != content) {
+            xmlElementContentPtr parent = cur->parent;
+
+            if (parent == NULL) return;
+
+            if (((cur->type == XML_ELEMENT_CONTENT_OR) ||
+                 (cur->type == XML_ELEMENT_CONTENT_SEQ)) &&
+                ((cur->type != parent->type) ||
+                 (cur->ocur != XML_ELEMENT_CONTENT_ONCE)))
+                xmlBufferWriteChar(buf, ")");
+            xmlDumpElementOccur(buf, cur);
+
+            if (cur == parent->c1) {
+                if (parent->type == XML_ELEMENT_CONTENT_SEQ)
+                    xmlBufferWriteChar(buf, " , ");
+                else if (parent->type == XML_ELEMENT_CONTENT_OR)
+                    xmlBufferWriteChar(buf, " | ");
+
+                cur = parent->c2;
+                break;
+            }
+
+            cur = parent;
+        }
+    } while (cur != content);
+
+    xmlBufferWriteChar(buf, ")");
+    xmlDumpElementOccur(buf, content);
+}
+
 /**
  * xmlSprintfElementContent:
  * @buf:  an output buffer
@@ -1703,7 +1724,7 @@ xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
            }
            xmlBufferWriteCHAR(buf, elem->name);
            xmlBufferWriteChar(buf, " ");
-           xmlDumpElementContent(buf, elem->content, 1);
+           xmlDumpElementContent(buf, elem->content);
            xmlBufferWriteChar(buf, ">\n");
            break;
        case XML_ELEMENT_TYPE_ELEMENT:
@@ -1714,7 +1735,7 @@ xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
            }
            xmlBufferWriteCHAR(buf, elem->name);
            xmlBufferWriteChar(buf, " ");
-           xmlDumpElementContent(buf, elem->content, 1);
+           xmlDumpElementContent(buf, elem->content);
            xmlBufferWriteChar(buf, ">\n");
            break;
        default:


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]