mxmlWrite* used a recursive algorithm which could require large amounts of

stack space depending on the file (Bug #549, CVE-2016-4571)
pull/193/head
Michael R Sweet 8 years ago
parent d8c0ba9007
commit 5f74dc2124
  1. 6
      CHANGES
  2. 2
      doc/reference.html
  3. 372
      mxml-file.c

@ -1,10 +1,12 @@
CHANGES - 2016-06-11 CHANGES - 2016-06-12
-------------------- --------------------
CHANGES IN Mini-XML 2.10 CHANGES IN Mini-XML 2.10
- mxmlDelete used a recursive algorithm which could require large - mxmlDelete used a recursive algorithm which could require large
amounts of stack space depending on the file (Bug #549) amounts of stack space depending on the file (Bug #549, CVE-2016-4570)
- mxmlWrite* used a recursive algorithm which could require large
amounts of stack space depending on the file (Bug #549, CVE-2016-4571)
CHANGES IN Mini-XML 2.9 CHANGES IN Mini-XML 2.9

@ -3,7 +3,7 @@
<head> <head>
<title>Documentation </title> <title>Documentation </title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta name="creator" content="Mini-XML v2.9"> <meta name="creator" content="Mini-XML v2.10">
<style type="text/css"><!-- <style type="text/css"><!--
body, p, h1, h2, h3, h4 { body, p, h1, h2, h3, h4 {
font-family: "lucida grande", geneva, helvetica, arial, sans-serif; font-family: "lucida grande", geneva, helvetica, arial, sans-serif;

@ -3,7 +3,7 @@
* *
* File loading code for Mini-XML, a small XML-like file parsing library. * File loading code for Mini-XML, a small XML-like file parsing library.
* *
* Copyright 2003-2014 by Michael R Sweet. * Copyright 2003-2016 by Michael R Sweet.
* *
* These coded instructions, statements, and computer programs are the * These coded instructions, statements, and computer programs are the
* property of Michael R Sweet and are protected by Federal copyright * property of Michael R Sweet and are protected by Federal copyright
@ -2710,6 +2710,8 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */
_mxml_putc_cb_t putc_cb,/* I - Output callback */ _mxml_putc_cb_t putc_cb,/* I - Output callback */
_mxml_global_t *global)/* I - Global data */ _mxml_global_t *global)/* I - Global data */
{ {
mxml_node_t *current, /* Current node */
*next; /* Next node */
int i, /* Looping var */ int i, /* Looping var */
width; /* Width of attr + value */ width; /* Width of attr + value */
mxml_attr_t *attr; /* Current attribute */ mxml_attr_t *attr; /* Current attribute */
@ -2717,252 +2719,264 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */
/* /*
* Print the node value... * Loop through this node and all of its children...
*/ */
switch (node->type) for (current = node; current; current = next)
{ {
case MXML_ELEMENT : /*
col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb); * Print the node value...
*/
if ((*putc_cb)('<', p) < 0) switch (current->type)
return (-1); {
if (node->value.element.name[0] == '?' || case MXML_ELEMENT :
!strncmp(node->value.element.name, "!--", 3) || col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
!strncmp(node->value.element.name, "![CDATA[", 8))
{
/*
* Comments, CDATA, and processing instructions do not
* use character entities.
*/
const char *ptr; /* Pointer into name */ if ((*putc_cb)('<', p) < 0)
return (-1);
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.
*/
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) if ((*putc_cb)(*ptr, p) < 0)
return (-1); 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); 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 > 0;
i --, attr ++) i --, attr ++)
{ {
width = strlen(attr->name); width = strlen(attr->name);
if (attr->value) if (attr->value)
width += strlen(attr->value) + 3; width += strlen(attr->value) + 3;
if (global->wrap > 0 && (col + width) > global->wrap) if (global->wrap > 0 && (col + width) > global->wrap)
{ {
if ((*putc_cb)('\n', p) < 0) if ((*putc_cb)('\n', p) < 0)
return (-1);
col = 0;
}
else
{
if ((*putc_cb)(' ', p) < 0)
return (-1);
col ++;
}
if (mxml_write_name(attr->name, p, putc_cb) < 0)
return (-1); return (-1);
col = 0; if (attr->value)
{
if ((*putc_cb)('=', p) < 0)
return (-1);
if ((*putc_cb)('\"', p) < 0)
return (-1);
if (mxml_write_string(attr->value, p, putc_cb) < 0)
return (-1);
if ((*putc_cb)('\"', p) < 0)
return (-1);
}
col += width;
} }
else
if (current->child)
{ {
if ((*putc_cb)(' ', p) < 0) /*
* Write children...
*/
if ((*putc_cb)('>', p) < 0)
return (-1); return (-1);
else
col ++;
col ++; col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
} }
else if (current->value.element.name[0] == '!' ||
current->value.element.name[0] == '?')
{
/*
* The ? and ! elements are special-cases...
*/
if (mxml_write_name(attr->name, p, putc_cb) < 0) if ((*putc_cb)('>', p) < 0)
return (-1); return (-1);
else
col ++;
if (attr->value) 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)
return (-1); return (-1);
if (mxml_write_string(attr->value, p, putc_cb) < 0) if ((*putc_cb)('/', p) < 0)
return (-1); return (-1);
if ((*putc_cb)('\"', p) < 0) if ((*putc_cb)('>', p) < 0)
return (-1); return (-1);
}
col += width; col += 3;
}
if (node->child) col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
{ }
/* break;
* Write children...
*/
mxml_node_t *child; /* Current child */ case MXML_INTEGER :
if (current->prev)
{
if (global->wrap > 0 && col > global->wrap)
{
if ((*putc_cb)('\n', p) < 0)
return (-1);
col = 0;
}
else if ((*putc_cb)(' ', p) < 0)
return (-1);
else
col ++;
}
if ((*putc_cb)('>', p) < 0) sprintf(s, "%d", current->value.integer);
if (mxml_write_string(s, p, putc_cb) < 0)
return (-1); return (-1);
else
col ++;
col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); col += strlen(s);
break;
for (child = node->child; child; child = child->next) case MXML_OPAQUE :
{ if (mxml_write_string(current->value.opaque, p, putc_cb) < 0)
if ((col = mxml_write_node(child, p, cb, col, putc_cb, global)) < 0) return (-1);
return (-1);
}
/* col += strlen(current->value.opaque);
* The ? and ! elements are special-cases and have no end tags... break;
*/
if (node->value.element.name[0] != '!' && case MXML_REAL :
node->value.element.name[0] != '?') if (current->prev)
{ {
col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb); if (global->wrap > 0 && col > global->wrap)
{
if ((*putc_cb)('\n', p) < 0)
return (-1);
if ((*putc_cb)('<', p) < 0) col = 0;
return (-1); }
if ((*putc_cb)('/', p) < 0) else 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); return (-1);
else
col += strlen(node->value.element.name) + 3; col ++;
col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb);
} }
}
else if (node->value.element.name[0] == '!' ||
node->value.element.name[0] == '?')
{
/*
* The ? and ! elements are special-cases...
*/
if ((*putc_cb)('>', p) < 0)
return (-1);
else
col ++;
col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); sprintf(s, "%f", current->value.real);
} if (mxml_write_string(s, p, putc_cb) < 0)
else
{
if ((*putc_cb)(' ', p) < 0)
return (-1);
if ((*putc_cb)('/', p) < 0)
return (-1);
if ((*putc_cb)('>', p) < 0)
return (-1); return (-1);
col += 3; col += strlen(s);
break;
col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
}
break;
case MXML_INTEGER : case MXML_TEXT :
if (node->prev) if (current->value.text.whitespace && col > 0)
{
if (global->wrap > 0 && col > global->wrap)
{ {
if ((*putc_cb)('\n', p) < 0) if (global->wrap > 0 && col > global->wrap)
return (-1); {
if ((*putc_cb)('\n', p) < 0)
return (-1);
col = 0; col = 0;
}
else if ((*putc_cb)(' ', p) < 0)
return (-1);
else
col ++;
} }
else if ((*putc_cb)(' ', p) < 0)
if (mxml_write_string(current->value.text.string, p, putc_cb) < 0)
return (-1); return (-1);
else
col ++;
}
sprintf(s, "%d", node->value.integer); col += strlen(current->value.text.string);
if (mxml_write_string(s, p, putc_cb) < 0) break;
return (-1);
col += strlen(s); case MXML_CUSTOM :
break; if (global->custom_save_cb)
{
char *data; /* Custom data string */
const char *newline; /* Last newline in string */
case MXML_OPAQUE :
if (mxml_write_string(node->value.opaque, p, putc_cb) < 0)
return (-1);
col += strlen(node->value.opaque); if ((data = (*global->custom_save_cb)(node)) == NULL)
break; return (-1);
case MXML_REAL : if (mxml_write_string(data, p, putc_cb) < 0)
if (node->prev)
{
if (global->wrap > 0 && col > global->wrap)
{
if ((*putc_cb)('\n', p) < 0)
return (-1); return (-1);
col = 0; if ((newline = strrchr(data, '\n')) == NULL)
col += strlen(data);
else
col = strlen(newline);
free(data);
break;
} }
else if ((*putc_cb)(' ', p) < 0)
return (-1);
else
col ++;
}
sprintf(s, "%f", node->value.real); default : /* Should never happen */
if (mxml_write_string(s, p, putc_cb) < 0)
return (-1); return (-1);
}
col += strlen(s); /*
break; * Figure out the next node...
*/
case MXML_TEXT :
if (node->value.text.whitespace && col > 0)
{
if (global->wrap > 0 && col > global->wrap)
{
if ((*putc_cb)('\n', p) < 0)
return (-1);
col = 0; if ((next = current->child) == NULL)
} {
else if ((*putc_cb)(' ', p) < 0) while ((next = current->next) == NULL)
return (-1); {
else if (current == node)
col ++; break;
}
if (mxml_write_string(node->value.text.string, p, putc_cb) < 0) /*
return (-1); * The ? and ! elements are special-cases and have no end tags...
*/
col += strlen(node->value.text.string); current = current->parent;
break;
case MXML_CUSTOM : if (current->value.element.name[0] != '!' &&
if (global->custom_save_cb) current->value.element.name[0] != '?')
{ {
char *data; /* Custom data string */ col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb);
const char *newline; /* Last newline in string */
if ((data = (*global->custom_save_cb)(node)) == NULL) if ((*putc_cb)('<', p) < 0)
return (-1); return (-1);
if ((*putc_cb)('/', p) < 0)
if (mxml_write_string(data, p, putc_cb) < 0) return (-1);
if (mxml_write_string(current->value.element.name, p, putc_cb) < 0)
return (-1);
if ((*putc_cb)('>', p) < 0)
return (-1); return (-1);
if ((newline = strrchr(data, '\n')) == NULL) col += strlen(current->value.element.name) + 3;
col += strlen(data);
else
col = strlen(newline);
free(data); col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb);
break;
} }
}
default : /* Should never happen */ }
return (-1);
} }
return (col); return (col);

Loading…
Cancel
Save