From 32ab134df5a647650bb01941dd04f3f965d550ff Mon Sep 17 00:00:00 2001
From: Michael R Sweet <michaelrsweet@users.noreply.github.com>
Date: Sun, 12 Jun 2016 21:12:11 +0000
Subject: [PATCH 2/2] mxmlWrite* used a recursive algorithm which could require
 large amounts of stack space depending on the file (Bug #549, CVE-2016-4571)

---
 CHANGES            |   6 +-
 doc/reference.html |   2 +-
 mxml-file.c        | 372 +++++++++++++++++++++++++++--------------------------
 3 files changed, 198 insertions(+), 182 deletions(-)

Index: mxml-2.5/mxml-file.c
===================================================================
--- mxml-2.5.orig/mxml-file.c	2017-03-10 16:46:09.150490964 +0100
+++ mxml-2.5/mxml-file.c	2017-03-10 16:47:38.558631368 +0100
@@ -2770,29 +2770,35 @@ mxml_write_node(mxml_node_t     *node,	/
 		_mxml_putc_cb_t putc_cb,/* I - Output callback */
 		_mxml_global_t  *global)/* I - Global data */
 {
+  mxml_node_t	*current,		/* Current node */
+		*next;			/* Next node */
   int		i,			/* Looping var */
 		width;			/* Width of attr + value */
   mxml_attr_t	*attr;			/* Current attribute */
   char		s[255];			/* Temporary string */
 
 
-  while (node != NULL)
+ /*
+  * Loop through this node and all of its children...
+  */
+
+  for (current = node; current; current = next)
   {
    /*
     * Print the node value...
     */
 
-    switch (node->type)
+    switch (current->type)
     {
       case MXML_ELEMENT :
-          col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
+	  col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
 
-          if ((*putc_cb)('<', p) < 0)
+	  if ((*putc_cb)('<', p) < 0)
 	    return (-1);
-          if (node->value.element.name[0] == '?' ||
-	      !strncmp(node->value.element.name, "!--", 3) ||
-	      !strncmp(node->value.element.name, "![CDATA[", 8))
-          {
+	  if (current->value.element.name[0] == '?' ||
+	      !strncmp(current->value.element.name, "!--", 3) ||
+	      !strncmp(current->value.element.name, "![CDATA[", 8))
+	  {
 	   /*
 	    * Comments, CDATA, and processing instructions do not
 	    * use character entities.
@@ -2800,24 +2806,16 @@ mxml_write_node(mxml_node_t     *node,	/
 
 	    const char	*ptr;		/* Pointer into name */
 
-
-	    for (ptr = node->value.element.name; *ptr; ptr ++)
+	    for (ptr = current->value.element.name; *ptr; ptr ++)
 	      if ((*putc_cb)(*ptr, p) < 0)
-	        return (-1);
-
-           /*
-	    * Prefer a newline for whitespace after ?xml...
-	    */
-
-            if (!strncmp(node->value.element.name, "?xml", 4))
-              col = global->wrap;
+		return (-1);
 	  }
-	  else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0)
+	  else if (mxml_write_name(current->value.element.name, p, putc_cb) < 0)
 	    return (-1);
 
-          col += strlen(node->value.element.name) + 1;
+	  col += strlen(current->value.element.name) + 1;
 
-	  for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
+	  for (i = current->value.element.num_attrs, attr = current->value.element.attrs;
 	       i > 0;
 	       i --, attr ++)
 	  {
@@ -2826,42 +2824,42 @@ mxml_write_node(mxml_node_t     *node,	/
 	    if (attr->value)
 	      width += strlen(attr->value) + 3;
 
-	    if ((col + width) > global->wrap)
+	    if (global->wrap > 0 && (col + width) > global->wrap)
 	    {
 	      if ((*putc_cb)('\n', p) < 0)
-	        return (-1);
+		return (-1);
 
 	      col = 0;
 	    }
 	    else
 	    {
 	      if ((*putc_cb)(' ', p) < 0)
-	        return (-1);
+		return (-1);
 
 	      col ++;
 	    }
 
-            if (mxml_write_name(attr->name, p, putc_cb) < 0)
+	    if (mxml_write_name(attr->name, p, putc_cb) < 0)
 	      return (-1);
 
 	    if (attr->value)
 	    {
-              if ((*putc_cb)('=', p) < 0)
+	      if ((*putc_cb)('=', p) < 0)
 		return (-1);
-              if ((*putc_cb)('\"', p) < 0)
+	      if ((*putc_cb)('\"', p) < 0)
 		return (-1);
 	      if (mxml_write_string(attr->value, p, putc_cb) < 0)
 		return (-1);
-              if ((*putc_cb)('\"', p) < 0)
+	      if ((*putc_cb)('\"', p) < 0)
 		return (-1);
-            }
+	    }
 
-            col += width;
+	    col += width;
 	  }
 
-	  if (node->child)
+	  if (current->child)
 	  {
-           /*
+	   /*
 	    * Write children...
 	    */
 
@@ -2870,39 +2868,12 @@ mxml_write_node(mxml_node_t     *node,	/
 	    else
 	      col ++;
 
-            col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
-
-	    if ((col = mxml_write_node(node->child, p, cb, col, putc_cb,
-	                               global)) < 0)
-	      return (-1);
-
-           /*
-	    * The ? and ! elements are special-cases and have no end tags...
-	    */
-
-            if (node->value.element.name[0] != '!' &&
-	        node->value.element.name[0] != '?')
-	    {
-              col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb);
-
-              if ((*putc_cb)('<', p) < 0)
-		return (-1);
-              if ((*putc_cb)('/', p) < 0)
-		return (-1);
-              if (mxml_write_string(node->value.element.name, p, putc_cb) < 0)
-		return (-1);
-              if ((*putc_cb)('>', p) < 0)
-		return (-1);
-
-              col += strlen(node->value.element.name) + 3;
-
-              col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb);
-	    }
+	    col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
 	  }
-	  else if (node->value.element.name[0] == '!' ||
-	           node->value.element.name[0] == '?')
+	  else if (current->value.element.name[0] == '!' ||
+		   current->value.element.name[0] == '?')
 	  {
-           /*
+	   /*
 	    * The ? and ! elements are special-cases...
 	    */
 
@@ -2911,30 +2882,30 @@ mxml_write_node(mxml_node_t     *node,	/
 	    else
 	      col ++;
 
-            col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
-          }
+	    col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
+	  }
 	  else
 	  {
-            if ((*putc_cb)(' ', p) < 0)
+	    if ((*putc_cb)(' ', p) < 0)
 	      return (-1);
-            if ((*putc_cb)('/', p) < 0)
+	    if ((*putc_cb)('/', p) < 0)
 	      return (-1);
-            if ((*putc_cb)('>', p) < 0)
+	    if ((*putc_cb)('>', p) < 0)
 	      return (-1);
 
 	    col += 3;
 
-            col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
+	    col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
 	  }
-          break;
+	  break;
 
       case MXML_INTEGER :
-	  if (node->prev)
+	  if (current->prev)
 	  {
-	    if (col > global->wrap)
+	    if (global->wrap > 0 && col > global->wrap)
 	    {
 	      if ((*putc_cb)('\n', p) < 0)
-	        return (-1);
+		return (-1);
 
 	      col = 0;
 	    }
@@ -2942,29 +2913,29 @@ mxml_write_node(mxml_node_t     *node,	/
 	      return (-1);
 	    else
 	      col ++;
-          }
+	  }
 
-          sprintf(s, "%d", node->value.integer);
+	  sprintf(s, "%d", current->value.integer);
 	  if (mxml_write_string(s, p, putc_cb) < 0)
 	    return (-1);
 
 	  col += strlen(s);
-          break;
+	  break;
 
       case MXML_OPAQUE :
-          if (mxml_write_string(node->value.opaque, p, putc_cb) < 0)
+	  if (mxml_write_string(current->value.opaque, p, putc_cb) < 0)
 	    return (-1);
 
-          col += strlen(node->value.opaque);
-          break;
+	  col += strlen(current->value.opaque);
+	  break;
 
       case MXML_REAL :
-	  if (node->prev)
+	  if (current->prev)
 	  {
-	    if (col > global->wrap)
+	    if (global->wrap > 0 && col > global->wrap)
 	    {
 	      if ((*putc_cb)('\n', p) < 0)
-	        return (-1);
+		return (-1);
 
 	      col = 0;
 	    }
@@ -2972,22 +2943,22 @@ mxml_write_node(mxml_node_t     *node,	/
 	      return (-1);
 	    else
 	      col ++;
-          }
+	  }
 
-          sprintf(s, "%f", node->value.real);
+	  sprintf(s, "%f", current->value.real);
 	  if (mxml_write_string(s, p, putc_cb) < 0)
 	    return (-1);
 
 	  col += strlen(s);
-          break;
+	  break;
 
       case MXML_TEXT :
-	  if (node->value.text.whitespace && col > 0)
+	  if (current->value.text.whitespace && col > 0)
 	  {
-	    if (col > global->wrap)
+	    if (global->wrap > 0 && col > global->wrap)
 	    {
 	      if ((*putc_cb)('\n', p) < 0)
-	        return (-1);
+		return (-1);
 
 	      col = 0;
 	    }
@@ -2995,45 +2966,77 @@ mxml_write_node(mxml_node_t     *node,	/
 	      return (-1);
 	    else
 	      col ++;
-          }
+	  }
 
-          if (mxml_write_string(node->value.text.string, p, putc_cb) < 0)
+	  if (mxml_write_string(current->value.text.string, p, putc_cb) < 0)
 	    return (-1);
 
-	  col += strlen(node->value.text.string);
-          break;
+	  col += strlen(current->value.text.string);
+	  break;
 
       case MXML_CUSTOM :
-          if (global->custom_save_cb)
+	  if (global->custom_save_cb)
 	  {
 	    char	*data;		/* Custom data string */
 	    const char	*newline;	/* Last newline in string */
 
 
-            if ((data = (*global->custom_save_cb)(node)) == NULL)
+	    if ((data = (*global->custom_save_cb)(node)) == NULL)
 	      return (-1);
 
-            if (mxml_write_string(data, p, putc_cb) < 0)
+	    if (mxml_write_string(data, p, putc_cb) < 0)
 	      return (-1);
 
-            if ((newline = strrchr(data, '\n')) == NULL)
+	    if ((newline = strrchr(data, '\n')) == NULL)
 	      col += strlen(data);
 	    else
-              col = strlen(newline);
+	      col = strlen(newline);
 
-            free(data);
+	    free(data);
 	    break;
 	  }
 
       default : /* Should never happen */
-          return (-1);
+	  return (-1);
     }
 
    /*
-    * Next node...
+    * Figure out the next node...
     */
 
-    node = node->next;
+    if ((next = current->child) == NULL)
+    {
+      while ((next = current->next) == NULL)
+      {
+        if (current == node)
+          break;
+
+       /*
+	* The ? and ! elements are special-cases and have no end tags...
+	*/
+
+	current = current->parent;
+
+	if (current->value.element.name[0] != '!' &&
+	    current->value.element.name[0] != '?')
+	{
+	  col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb);
+
+	  if ((*putc_cb)('<', p) < 0)
+	    return (-1);
+	  if ((*putc_cb)('/', p) < 0)
+	    return (-1);
+	  if (mxml_write_string(current->value.element.name, p, putc_cb) < 0)
+	    return (-1);
+	  if ((*putc_cb)('>', p) < 0)
+	    return (-1);
+
+	  col += strlen(current->value.element.name) + 3;
+
+	  col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb);
+	}
+      }
+    }
   }
 
   return (col);
