Commit 42331a90 authored by William M. Brack's avatar William M. Brack

further fixes for out of memory condition, mostly from Olivier Andrieu.

* SAX2.c, tree.c, uri.c, xmlIO.c, xmlreader.c: further
  fixes for out of memory condition, mostly from Olivier
  Andrieu.
* testOOM.c: some further improvement by Olivier, with
  a further small enhancement for easier debugging.
parent 9f797abd
Thu Jul 29 00:05:58 PDT 2004 William Brack <wbrack@mmm.com.hk>
* SAX2.c, tree.c, uri.c, xmlIO.c, xmlreader.c: further
fixes for out of memory condition, mostly from Olivier
Andrieu.
* testOOM.c: some further improvement by Olivier, with
a further small enhancement for easier debugging.
Tue Jul 27 00:34:07 PDT 2004 William Brack <wbrack@mmm.com.hk>
* SAX2.c, error.c, parser.c, tree.c, xmlreader.c:
......
......@@ -44,6 +44,20 @@
"Unimplemented block at %s:%d\n", \
__FILE__, __LINE__);
/*
* xmlSAX2ErrMemory:
* @ctxt: an XML validation parser context
* @msg: a string to accompany the error message
*/
static void
xmlSAX2ErrMemory(xmlParserCtxtPtr ctxt, char *msg) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "%s: out of memory\n", msg);
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
}
/**
* xmlValidError:
* @ctxt: an XML validation parser context
......@@ -214,6 +228,8 @@ xmlSAX2InternalSubset(void *ctx, const xmlChar *name,
}
ctxt->myDoc->intSubset =
xmlCreateIntSubset(ctxt->myDoc, name, ExternalID, SystemID);
if (ctxt->myDoc->intSubset == NULL)
xmlSAX2ErrMemory(ctxt, "xmlSAX2InternalSubset");
}
/**
......@@ -273,13 +289,7 @@ xmlSAX2ExternalSubset(void *ctx, const xmlChar *name,
ctxt->inputTab = (xmlParserInputPtr *)
xmlMalloc(5 * sizeof(xmlParserInputPtr));
if (ctxt->inputTab == NULL) {
ctxt->errNo = XML_ERR_NO_MEMORY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlSAX2ExternalSubset: out of memory\n");
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2ExternalSubset");
ctxt->input = oldinput;
ctxt->inputNr = oldinputNr;
ctxt->inputMax = oldinputMax;
......@@ -816,12 +826,7 @@ xmlSAX2StartDocument(void *ctx)
if (ctxt->myDoc == NULL)
ctxt->myDoc = htmlNewDocNoDtD(NULL, NULL);
if (ctxt->myDoc == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"SAX.xmlSAX2StartDocument(): out of memory\n");
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2StartDocument");
return;
}
#else
......@@ -841,12 +846,7 @@ xmlSAX2StartDocument(void *ctx)
doc->encoding = NULL;
doc->standalone = ctxt->standalone;
} else {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"SAX.xmlSAX2StartDocument(): out of memory\n");
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2StartDocument");
return;
}
if ((ctxt->dictNames) && (doc != NULL)) {
......@@ -945,12 +945,7 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname,
name = xmlStrdup(fullname);
}
if (name == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"SAX.xmlSAX2StartElement(): out of memory\n");
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElement");
if (ns != NULL)
xmlFree(ns);
return;
......@@ -1044,12 +1039,7 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname,
0,0,0);
ctxt->depth--;
if (val == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"SAX.xmlSAX2StartElement(): out of memory\n");
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElement");
xmlFree(ns);
if (name != NULL)
xmlFree(name);
......@@ -1345,13 +1335,7 @@ process_external_subset:
fulln = xmlBuildQName(attr->name, attr->prefix, fn, 50);
if (fulln == NULL) {
if ((ctxt->sax != NULL) &&
(ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"SAX.xmlSAX2StartElement(): out of memory\n");
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElement");
return;
}
......@@ -1446,9 +1430,7 @@ xmlSAX2StartElement(void *ctx, const xmlChar *fullname, const xmlChar **atts)
if (ret == NULL) {
if (prefix != NULL)
xmlFree(prefix);
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElement");
return;
}
if (ctxt->myDoc->children == NULL) {
......@@ -1672,12 +1654,7 @@ xmlSAX2TextNode(xmlParserCtxtPtr ctxt, const xmlChar *str, int len) {
ret = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
}
if (ret == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"SAX.xmlSAX2Characters(): out of memory\n");
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlErrMemory(ctxt, "xmlSAX2Characters");
return(NULL);
}
/*
......@@ -1708,9 +1685,8 @@ skip:
if (intern == NULL) {
ret->content = xmlStrndup(str, len);
if (ret->content == NULL) {
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2TextNode");
xmlFree(ret);
return(NULL);
}
} else
......@@ -1822,9 +1798,7 @@ xmlSAX2AttributeNs(xmlParserCtxtPtr ctxt,
else
ret = xmlNewNsProp(ctxt->node, namespace, localname, NULL);
if (ret == NULL) {
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlErrMemory(ctxt, "xmlSAX2AttributeNs");
return;
}
}
......@@ -2042,9 +2016,7 @@ xmlSAX2StartElementNs(void *ctx,
else {
ret->name = xmlStrdup(localname);
if (ret->name == NULL) {
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElementNs");
return;
}
}
......@@ -2057,9 +2029,7 @@ xmlSAX2StartElementNs(void *ctx,
else
ret = xmlNewDocNode(ctxt->myDoc, NULL, localname, NULL);
if (ret == NULL) {
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElementNs");
return;
}
}
......@@ -2094,9 +2064,7 @@ xmlSAX2StartElementNs(void *ctx,
if ((URI != NULL) && (prefix == pref))
ret->ns = ns;
} else {
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElementNs");
return;
}
#ifdef LIBXML_VALID_ENABLED
......@@ -2143,9 +2111,7 @@ xmlSAX2StartElementNs(void *ctx,
if (ret->ns == NULL) {
ns = xmlNewNs(ret, NULL, prefix);
if (ns == NULL) {
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElementNs");
return;
}
if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
......@@ -2327,12 +2293,7 @@ xmlSAX2Characters(void *ctx, const xmlChar *ch, int len)
size *= 2;
newbuf = (xmlChar *) xmlRealloc(lastChild->content,size);
if (newbuf == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"SAX.xmlSAX2Characters(): out of memory\n");
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2Characters");
return;
}
ctxt->nodemem = size;
......@@ -2343,12 +2304,7 @@ xmlSAX2Characters(void *ctx, const xmlChar *ch, int len)
lastChild->content[ctxt->nodelen] = 0;
} else if (coalesceText) {
if (xmlTextConcat(lastChild, ch, len)) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"SAX.xmlSAX2Characters(): out of memory\n");
ctxt->errNo = XML_ERR_NO_MEMORY;
ctxt->instate = XML_PARSER_EOF;
ctxt->disableSAX = 1;
xmlSAX2ErrMemory(ctxt, "xmlSAX2Characters");
}
if (ctxt->node->children != NULL) {
ctxt->nodelen = xmlStrlen(lastChild->content);
......
......@@ -6,11 +6,6 @@
* hp@redhat.com
*/
/* FIXME this test would be much better if instead of just checking
* for debug spew or crashes on OOM, it also validated the expected
* results of parsing a particular file vs. the actual results
*/
#include "libxml.h"
#include <string.h>
......@@ -40,23 +35,140 @@
#define FALSE (0)
#endif
#define EXIT_OOM 2
int debug = 0;
int dump = 0;
int noent = 0;
int count = 0;
int valid = 0;
int showErrs = 0;
/*
* Since we are using the xmlTextReader functions, we set up
* strings for the element types to help in debugging any error
* output
*/
const char *elementNames[] = {
"XML_READER_TYPE_NONE",
"XML_READER_TYPE_ELEMENT",
"XML_READER_TYPE_ATTRIBUTE",
"XML_READER_TYPE_TEXT",
"XML_READER_TYPE_CDATA",
"XML_READER_TYPE_ENTITY_REFERENCE",
"XML_READER_TYPE_ENTITY",
"XML_READER_TYPE_PROCESSING_INSTRUCTION",
"XML_READER_TYPE_COMMENT",
"XML_READER_TYPE_DOCUMENT",
"XML_READER_TYPE_DOCUMENT_TYPE",
"XML_READER_TYPE_DOCUMENT_FRAGMENT",
"XML_READER_TYPE_NOTATION",
"XML_READER_TYPE_WHITESPACE",
"XML_READER_TYPE_SIGNIFICANT_WHITESPACE",
"XML_READER_TYPE_END_ELEMENT",
"XML_READER_TYPE_END_ENTITY",
"XML_READER_TYPE_XML_DECLARATION"};
/* not using xmlBuff here because I don't want those
* mallocs to interfere */
struct buffer {
char *str;
size_t len;
size_t max;
};
static struct buffer *buffer_create (size_t init_len)
{
struct buffer *b;
b = malloc (sizeof *b);
if (b == NULL)
exit (EXIT_OOM);
if (init_len) {
b->str = malloc (init_len);
if (b->str == NULL)
exit (EXIT_OOM);
}
else
b->str = NULL;
b->len = 0;
b->max = init_len;
return b;
}
static void buffer_free (struct buffer *b)
{
free (b->str);
free (b);
}
static size_t buffer_get_length (struct buffer *b)
{
return b->len;
}
static void buffer_expand (struct buffer *b, size_t min)
{
void *new_str;
size_t new_size = b->max ? b->max : 512;
while (new_size < b->len + min)
new_size *= 2;
if (new_size > b->max) {
new_str = realloc (b->str, new_size);
if (new_str == NULL)
exit (EXIT_OOM);
b->str = new_str;
b->max = new_size;
}
}
static void buffer_add_char (struct buffer *b, char c)
{
buffer_expand (b, 1);
b->str[b->len] = c;
b->len += 1;
}
static void buffer_add_string (struct buffer *b, const char *s)
{
size_t size = strlen(s) + 1;
int ix;
for (ix=0; ix<size-1; ix++) {
if (s[ix] < 0x20)
printf ("binary data [0x%02x]?\n", (unsigned char)s[ix]);
}
buffer_expand (b, size);
strcpy (b->str + b->len, s);
b->str[b->len+size-1] = '\n'; /* replace string term with newline */
b->len += size;
}
static int buffer_equal (struct buffer *b1, struct buffer *b2)
{
return (b1->len == b2->len &&
(b1->len == 0 || (memcmp (b1->str, b2->str, b1->len) == 0)));
}
static void buffer_dump (struct buffer *b, const char *fname)
{
FILE *f = fopen (fname, "wb");
if (f != NULL) {
fwrite (b->str, 1, b->len, f);
fclose (f);
}
}
static void usage(const char *progname) {
printf("Usage : %s [options] XMLfiles ...\n", progname);
printf("\tParse the XML files using the xmlTextReader API\n");
printf("\t --count: count the number of attribute and elements\n");
printf("\t --valid: validate the document\n");
printf("\t --show: display the error messages encountered\n");
exit(1);
}
static unsigned int elem, attrs, chars;
static int processNode(xmlTextReaderPtr reader) {
static int processNode (xmlTextReaderPtr reader, void *data)
{
struct buffer *buff = data;
int type;
type = xmlTextReaderNodeType(reader);
......@@ -74,72 +186,128 @@ static int processNode(xmlTextReaderPtr reader) {
}
}
if (buff != NULL) {
int ret;
const char *s;
buffer_add_string (buff, elementNames[type]);
if (type == 1) {
s = xmlTextReaderConstName (reader);
if (s == NULL) return FALSE;
buffer_add_string (buff, s);
while ((ret = xmlTextReaderMoveToNextAttribute (reader)) == 1) {
s = xmlTextReaderConstName (reader);
if (s == NULL) return FALSE;
buffer_add_string (buff, s);
buffer_add_char (buff, '=');
s = xmlTextReaderConstValue (reader);
if (s == NULL) return FALSE;
buffer_add_string (buff, s);
}
if (ret == -1) return FALSE;
}
else if (type == 3) {
s = xmlTextReaderConstValue (reader);
if (s == NULL) return FALSE;
buffer_add_string (buff, s);
}
}
return TRUE;
}
struct file_params {
const char *filename;
unsigned int elem, attrs, chars;
const char *filename;
struct buffer *verif_buff;
};
/* This always returns TRUE since we don't validate the results of
* parsing a particular document vs. the expected results of parsing
* that document. The idea is that such a failure would return FALSE.
*/
static void
error_func (void *data, xmlErrorPtr err)
{
int *e = data;
if (err->level == XML_ERR_ERROR ||
err->level == XML_ERR_FATAL)
*e = TRUE;
if (showErrs) {
printf("line %d: %s\n", err->line, err->message);
}
}
static int
check_load_file_memory_func (void *data)
{
struct file_params *p = data;
struct buffer *b;
xmlTextReaderPtr reader;
int ret, status;
int ret, status, first_run, error;
if (count) {
elem = 0;
attrs = 0;
chars = 0;
elem = 0;
attrs = 0;
chars = 0;
}
first_run = p->verif_buff == NULL;
status = TRUE;
reader = xmlNewTextReaderFilename(p->filename);
error = FALSE;
if (first_run)
b = buffer_create (0);
else
b = buffer_create (buffer_get_length (p->verif_buff));
reader = xmlNewTextReaderFilename (p->filename);
if (reader == NULL)
goto out;
if (reader != NULL) {
if (valid) {
if (xmlTextReaderSetParserProp(reader, XML_PARSER_VALIDATE, 1) == -1)
goto out;
}
xmlTextReaderSetStructuredErrorHandler (reader, error_func, &error);
if (valid) {
if (xmlTextReaderSetParserProp(reader, XML_PARSER_VALIDATE, 1) == -1)
goto out;
}
/*
* Process all nodes in sequence
*/
while ((ret = xmlTextReaderRead(reader)) == 1) {
if (!processNode(reader))
goto out;
}
if (ret == -1)
goto out;
/*
* Done, cleanup and status
*/
if (count)
{
if (p->elem == (unsigned int)-1) {
p->elem = elem;
p->attrs = attrs;
p->chars = chars;
}
else {
status = (elem == p->elem && attrs == p->attrs && chars == p->chars);
fprintf (stderr, "# %s: %u elems, %u attrs, %u chars %s\n",
p->filename, elem, attrs, chars,
status ? "ok" : "wrong");
}
}
/*
* Process all nodes in sequence
*/
while ((ret = xmlTextReaderRead(reader)) == 1) {
if (!processNode(reader, b))
goto out;
}
if (ret == -1)
goto out;
if (error) {
fprintf (stdout, "error handler was called but parse completed successfully\n");
return FALSE;
}
/*
* Done, cleanup and status
*/
if (! first_run) {
status = buffer_equal (p->verif_buff, b);
if (! status) {
buffer_dump (p->verif_buff, ".OOM.verif_buff");
buffer_dump (b, ".OOM.buff");
}
}
if (count)
{
fprintf (stdout, "# %s: %u elems, %u attrs, %u chars %s\n",
p->filename, elem, attrs, chars,
status ? "ok" : "wrong");
}
out:
if (first_run)
p->verif_buff = b;
else
buffer_free (b);
if (reader)
xmlFreeTextReader (reader);
xmlFreeTextReader (reader);
return status;
}
......@@ -157,19 +325,20 @@ int main(int argc, char **argv) {
test_malloc,
test_realloc,
test_strdup);
xmlInitParser();
for (i = 1; i < argc ; i++) {
if ((!strcmp(argv[i], "-debug")) || (!strcmp(argv[i], "--debug")))
debug++;
else if ((!strcmp(argv[i], "-dump")) || (!strcmp(argv[i], "--dump")))
dump++;
else if ((!strcmp(argv[i], "-count")) || (!strcmp(argv[i], "--count")))
if ((!strcmp(argv[i], "-count")) || (!strcmp(argv[i], "--count")))
count++;
else if ((!strcmp(argv[i], "-valid")) || (!strcmp(argv[i], "--valid")))
valid++;
else if ((!strcmp(argv[i], "-noent")) ||
(!strcmp(argv[i], "--noent")))
noent++;
else if ((!strcmp(argv[i], "-show")) ||
(!strcmp(argv[i], "--show")))
showErrs++;
}
if (noent != 0)
xmlSubstituteEntitiesDefault(1);
......@@ -177,27 +346,22 @@ int main(int argc, char **argv) {
if (argv[i][0] != '-') {
struct file_params p;
p.filename = argv[i];
p.elem = -1;
/* Run once to count */
check_load_file_memory_func (&p);
if (count) {
fprintf (stderr, "# %s: %u elems, %u attrs, %u chars\n",
p.filename, p.elem, p.attrs, p.chars);
}
p.verif_buff = NULL;
if (!test_oom_handling (check_load_file_memory_func,
&p)) {
fprintf (stderr, "Failed!\n");
return (1);
fprintf (stdout, "Failed!\n");
return 1;
}
buffer_free (p.verif_buff);
xmlCleanupParser();
if (test_get_malloc_blocks_outstanding () > 0) {
fprintf (stderr, "%d blocks leaked\n",
fprintf (stdout, "%d blocks leaked\n",
test_get_malloc_blocks_outstanding ());
xmlMemoryDump();
return (1);
return 1;
}
files ++;
......@@ -205,5 +369,5 @@ int main(int argc, char **argv) {
}
xmlMemoryDump();