integrated the Out Of Memory test from Havoc Pennington #109368 a lot of
* Makefile.am testOOM.c testOOMlib.[ch] : integrated the Out Of
Memory test from Havoc Pennington #109368
* SAX.c parser.c parserInternals.c tree.c uri.c valid.c
xmlmemory.c xmlreader.c xmlregexp.c include/libxml/tree.h
include/libxml/parser.h: a lot of memory allocation cleanups
based on the results of the OOM testing
* check-relaxng-test-suite2.py: seems I forgot to commit the
script.
Daniel
diff --git a/ChangeLog b/ChangeLog
index 0558bf0..187c801 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+Thu Apr 24 18:01:46 CEST 2003 Daniel Veillard <daniel@veillard.com>
+
+ * Makefile.am testOOM.c testOOMlib.[ch] : integrated the Out Of
+ Memory test from Havoc Pennington #109368
+ * SAX.c parser.c parserInternals.c tree.c uri.c valid.c
+ xmlmemory.c xmlreader.c xmlregexp.c include/libxml/tree.h
+ include/libxml/parser.h: a lot of memory allocation cleanups
+ based on the results of the OOM testing
+ * check-relaxng-test-suite2.py: seems I forgot to commit the
+ script.
+
Wed Apr 23 17:16:41 CEST 2003 Daniel Veillard <daniel@veillard.com>
* xmlschemastypes.c: trivial fix for 109774 removing a warning
diff --git a/Makefile.am b/Makefile.am
index 9d32c31..7242f99 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -116,6 +116,11 @@
testReader_DEPENDENCIES = $(DEPS)
testReader_LDADD= $(LDADDS)
+testOOM_SOURCES=testOOM.c testOOMlib.h testOOMlib.c
+testOOM_LDFLAGS =
+testOOM_DEPENDENCIES = $(DEPS)
+testOOM_LDADD= $(LDADDS)
+
check-local: tests
testall : tests SVGtests SAXtests
diff --git a/SAX.c b/SAX.c
index 6bbb401..df30c62 100644
--- a/SAX.c
+++ b/SAX.c
@@ -232,6 +232,8 @@
ctxt->sax->error(ctxt->userData,
"externalSubset: out of memory\n");
ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
ctxt->input = oldinput;
ctxt->inputNr = oldinputNr;
ctxt->inputMax = oldinputMax;
@@ -745,12 +747,25 @@
"SAX.startDocument()\n");
#endif
if (ctxt->html) {
- if (ctxt->myDoc == NULL)
#ifdef LIBXML_HTML_ENABLED
+ 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.startDocument(): out of memory\n");
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
+ return;
+ }
#else
xmlGenericError(xmlGenericErrorContext,
"libxml2 built without HTML support\n");
+ ctxt->errNo = XML_ERR_INTERNAL_ERROR;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
+ return;
#endif
} else {
doc = ctxt->myDoc = xmlNewDoc(ctxt->version);
@@ -760,6 +775,14 @@
else
doc->encoding = NULL;
doc->standalone = ctxt->standalone;
+ } else {
+ if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
+ ctxt->sax->error(ctxt->userData,
+ "SAX.startDocument(): out of memory\n");
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
+ return;
}
}
if ((ctxt->myDoc != NULL) && (ctxt->myDoc->URL == NULL) &&
@@ -839,6 +862,17 @@
* Split the full name into a namespace prefix and the tag name
*/
name = xmlSplitQName(ctxt, fullname, &ns);
+ if (name == NULL) {
+ if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
+ ctxt->sax->error(ctxt->userData,
+ "SAX.startElement(): out of memory\n");
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
+ if (ns != NULL)
+ xmlFree(ns);
+ return;
+ }
/*
* Do the last stage of the attribute normalization
@@ -921,6 +955,18 @@
val = xmlStringDecodeEntities(ctxt, value, XML_SUBSTITUTE_REF,
0,0,0);
ctxt->depth--;
+ if (val == NULL) {
+ if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
+ ctxt->sax->error(ctxt->userData,
+ "SAX.startElement(): out of memory\n");
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
+ xmlFree(ns);
+ if (name != NULL)
+ xmlFree(name);
+ return;
+ }
} else {
val = (xmlChar *) value;
}
@@ -1200,14 +1246,19 @@
attr->elem, attr->name,
attr->prefix);
if ((tst == attr) || (tst == NULL)) {
+ xmlChar fn[50];
xmlChar *fulln;
- if (attr->prefix != NULL) {
- fulln = xmlStrdup(attr->prefix);
- fulln = xmlStrcat(fulln, BAD_CAST ":");
- fulln = xmlStrcat(fulln, attr->name);
- } else {
- fulln = xmlStrdup(attr->name);
+ 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.startElement(): out of memory\n");
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
+ return;
}
/*
@@ -1229,7 +1280,8 @@
my_attribute(ctxt, fulln, attr->defaultValue,
prefix);
}
- xmlFree(fulln);
+ if ((fulln != fn) && (fulln != attr->name))
+ xmlFree(fulln);
}
}
}
@@ -1301,7 +1353,14 @@
* an attribute at this level.
*/
ret = xmlNewDocNodeEatName(ctxt->myDoc, NULL, name, NULL);
- if (ret == NULL) return;
+ if (ret == NULL) {
+ if (prefix != NULL)
+ xmlFree(prefix);
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
+ return;
+ }
if (ctxt->myDoc->children == NULL) {
#ifdef DEBUG_SAX_TREE
xmlGenericError(xmlGenericErrorContext, "Setting %s as root\n", name);
@@ -1587,6 +1646,9 @@
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"SAX.characters(): out of memory\n");
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
return;
}
ctxt->nodemem = size;
@@ -1596,7 +1658,14 @@
ctxt->nodelen += len;
lastChild->content[ctxt->nodelen] = 0;
} else if (coalesceText) {
- xmlTextConcat(lastChild, ch, len);
+ if (xmlTextConcat(lastChild, ch, len)) {
+ if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
+ ctxt->sax->error(ctxt->userData,
+ "SAX.characters(): out of memory\n");
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
+ }
if (ctxt->node->children != NULL) {
ctxt->nodelen = xmlStrlen(lastChild->content);
ctxt->nodemem = ctxt->nodelen + 1;
@@ -1604,10 +1673,19 @@
} else {
/* Mixed content, first time */
lastChild = xmlNewTextLen(ch, len);
- xmlAddChild(ctxt->node, lastChild);
- if (ctxt->node->children != NULL) {
- ctxt->nodelen = len;
- ctxt->nodemem = len + 1;
+ if (lastChild == NULL) {
+ if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
+ ctxt->sax->error(ctxt->userData,
+ "SAX.characters(): out of memory\n");
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
+ } else {
+ xmlAddChild(ctxt->node, lastChild);
+ if (ctxt->node->children != NULL) {
+ ctxt->nodelen = len;
+ ctxt->nodemem = len + 1;
+ }
}
}
}
diff --git a/check-relaxng-test-suite2.py b/check-relaxng-test-suite2.py
new file mode 100755
index 0000000..6a2b6eb
--- /dev/null
+++ b/check-relaxng-test-suite2.py
@@ -0,0 +1,407 @@
+#!/usr/bin/python
+import sys
+import time
+import os
+import string
+import StringIO
+sys.path.append("python")
+import libxml2
+
+# Memory debug specific
+libxml2.debugMemory(1)
+debug = 0
+
+#
+# the testsuite description
+#
+CONF="test/relaxng/testsuite.xml"
+LOG="check-relaxng-test-suite2.log"
+
+log = open(LOG, "w")
+nb_schemas_tests = 0
+nb_schemas_success = 0
+nb_schemas_failed = 0
+nb_instances_tests = 0
+nb_instances_success = 0
+nb_instances_failed = 0
+
+libxml2.lineNumbersDefault(1)
+#
+# Resolver callback
+#
+resources = {}
+def resolver(URL, ID, ctxt):
+ global resources
+
+ if resources.has_key(URL):
+ return(StringIO.StringIO(resources[URL]))
+ log.write("Resolver failure: asked %s\n" % (URL))
+ log.write("resources: %s\n" % (resources))
+ return None
+
+#
+# Load the previous results
+#
+#results = {}
+#previous = {}
+#
+#try:
+# res = libxml2.parseFile(RES)
+#except:
+# log.write("Could not parse %s" % (RES))
+
+#
+# handle a valid instance
+#
+def handle_valid(node, schema):
+ global log
+ global nb_instances_success
+ global nb_instances_failed
+
+ instance = node.prop("dtd")
+ if instance == None:
+ instance = ""
+ child = node.children
+ while child != None:
+ if child.type != 'text':
+ instance = instance + child.serialize()
+ child = child.next
+
+ mem = libxml2.debugMemory(1);
+ try:
+ doc = libxml2.parseDoc(instance)
+ except:
+ doc = None
+
+ if doc == None:
+ log.write("\nFailed to parse correct instance:\n-----\n")
+ log.write(instance)
+ log.write("\n-----\n")
+ nb_instances_failed = nb_instances_failed + 1
+ return
+
+ if debug:
+ print "instance line %d" % (node.lineNo())
+
+ try:
+ ctxt = schema.relaxNGNewValidCtxt()
+ ret = doc.relaxNGValidateDoc(ctxt)
+ del ctxt
+ except:
+ ret = -1
+
+ doc.freeDoc()
+ if mem != libxml2.debugMemory(1):
+ print "validating instance %d line %d leaks" % (
+ nb_instances_tests, node.lineNo())
+
+ if ret != 0:
+ log.write("\nFailed to validate correct instance:\n-----\n")
+ log.write(instance)
+ log.write("\n-----\n")
+ nb_instances_failed = nb_instances_failed + 1
+ else:
+ nb_instances_success = nb_instances_success + 1
+
+#
+# handle an invalid instance
+#
+def handle_invalid(node, schema):
+ global log
+ global nb_instances_success
+ global nb_instances_failed
+
+ instance = node.prop("dtd")
+ if instance == None:
+ instance = ""
+ child = node.children
+ while child != None:
+ if child.type != 'text':
+ instance = instance + child.serialize()
+ child = child.next
+
+ mem = libxml2.debugMemory(1);
+
+ try:
+ doc = libxml2.parseDoc(instance)
+ except:
+ doc = None
+
+ if doc == None:
+ log.write("\nStrange: failed to parse incorrect instance:\n-----\n")
+ log.write(instance)
+ log.write("\n-----\n")
+ return
+
+ if debug:
+ print "instance line %d" % (node.lineNo())
+
+ try:
+ ctxt = schema.relaxNGNewValidCtxt()
+ ret = doc.relaxNGValidateDoc(ctxt)
+ del ctxt
+
+ except:
+ ret = -1
+
+ doc.freeDoc()
+ if mem != libxml2.debugMemory(1):
+ print "validating instance %d line %d leaks" % (
+ nb_instances_tests, node.lineNo())
+
+ if ret == 0:
+ log.write("\nFailed to detect validation problem in instance:\n-----\n")
+ log.write(instance)
+ log.write("\n-----\n")
+ nb_instances_failed = nb_instances_failed + 1
+ else:
+ nb_instances_success = nb_instances_success + 1
+
+#
+# handle an incorrect test
+#
+def handle_correct(node):
+ global log
+ global nb_schemas_success
+ global nb_schemas_failed
+
+ schema = ""
+ child = node.children
+ while child != None:
+ if child.type != 'text':
+ schema = schema + child.serialize()
+ child = child.next
+
+ try:
+ rngp = libxml2.relaxNGNewMemParserCtxt(schema, len(schema))
+ rngs = rngp.relaxNGParse()
+ except:
+ rngs = None
+ if rngs == None:
+ log.write("\nFailed to compile correct schema:\n-----\n")
+ log.write(schema)
+ log.write("\n-----\n")
+ nb_schemas_failed = nb_schemas_failed + 1
+ else:
+ nb_schemas_success = nb_schemas_success + 1
+ return rngs
+
+def handle_incorrect(node):
+ global log
+ global nb_schemas_success
+ global nb_schemas_failed
+
+ schema = ""
+ child = node.children
+ while child != None:
+ if child.type != 'text':
+ schema = schema + child.serialize()
+ child = child.next
+
+ try:
+ rngp = libxml2.relaxNGNewMemParserCtxt(schema, len(schema))
+ rngs = rngp.relaxNGParse()
+ except:
+ rngs = None
+ if rngs != None:
+ log.write("\nFailed to detect schema error in:\n-----\n")
+ log.write(schema)
+ log.write("\n-----\n")
+ nb_schemas_failed = nb_schemas_failed + 1
+ else:
+# log.write("\nSuccess detecting schema error in:\n-----\n")
+# log.write(schema)
+# log.write("\n-----\n")
+ nb_schemas_success = nb_schemas_success + 1
+ return None
+
+#
+# resource handling: keep a dictionary of URL->string mappings
+#
+def handle_resource(node, dir):
+ global resources
+
+ try:
+ name = node.prop('name')
+ except:
+ name = None
+
+ if name == None or name == '':
+ log.write("resource has no name")
+ return;
+
+ if dir != None:
+# name = libxml2.buildURI(name, dir)
+ name = dir + '/' + name
+
+ res = ""
+ child = node.children
+ while child != None:
+ if child.type != 'text':
+ res = res + child.serialize()
+ child = child.next
+ resources[name] = res
+
+#
+# dir handling: pseudo directory resources
+#
+def handle_dir(node, dir):
+ try:
+ name = node.prop('name')
+ except:
+ name = None
+
+ if name == None or name == '':
+ log.write("resource has no name")
+ return;
+
+ if dir != None:
+# name = libxml2.buildURI(name, dir)
+ name = dir + '/' + name
+
+ dirs = node.xpathEval('dir')
+ for dir in dirs:
+ handle_dir(dir, name)
+ res = node.xpathEval('resource')
+ for r in res:
+ handle_resource(r, name)
+
+#
+# handle a testCase element
+#
+def handle_testCase(node):
+ global nb_schemas_tests
+ global nb_instances_tests
+ global resources
+
+ sections = node.xpathEval('string(section)')
+ log.write("\n ======== test %d line %d section %s ==========\n" % (
+
+ nb_schemas_tests, node.lineNo(), sections))
+ resources = {}
+ if debug:
+ print "test %d line %d" % (nb_schemas_tests, node.lineNo())
+
+ dirs = node.xpathEval('dir')
+ for dir in dirs:
+ handle_dir(dir, None)
+ res = node.xpathEval('resource')
+ for r in res:
+ handle_resource(r, None)
+
+ tsts = node.xpathEval('incorrect')
+ if tsts != []:
+ if len(tsts) != 1:
+ print "warning test line %d has more than one <incorrect> example" %(node.lineNo())
+ schema = handle_incorrect(tsts[0])
+ else:
+ tsts = node.xpathEval('correct')
+ if tsts != []:
+ if len(tsts) != 1:
+ print "warning test line %d has more than one <correct> example"% (node.lineNo())
+ schema = handle_correct(tsts[0])
+ else:
+ print "warning <testCase> line %d has no <correct> nor <incorrect> child" % (node.lineNo())
+
+ nb_schemas_tests = nb_schemas_tests + 1;
+
+ valids = node.xpathEval('valid')
+ invalids = node.xpathEval('invalid')
+ nb_instances_tests = nb_instances_tests + len(valids) + len(invalids)
+ if schema != None:
+ for valid in valids:
+ handle_valid(valid, schema)
+ for invalid in invalids:
+ handle_invalid(invalid, schema)
+
+
+#
+# handle a testSuite element
+#
+def handle_testSuite(node, level = 0):
+ global nb_schemas_tests, nb_schemas_success, nb_schemas_failed
+ global nb_instances_tests, nb_instances_success, nb_instances_failed
+ if level >= 1:
+ old_schemas_tests = nb_schemas_tests
+ old_schemas_success = nb_schemas_success
+ old_schemas_failed = nb_schemas_failed
+ old_instances_tests = nb_instances_tests
+ old_instances_success = nb_instances_success
+ old_instances_failed = nb_instances_failed
+
+ docs = node.xpathEval('documentation')
+ authors = node.xpathEval('author')
+ if docs != []:
+ msg = ""
+ for doc in docs:
+ msg = msg + doc.content + " "
+ if authors != []:
+ msg = msg + "written by "
+ for author in authors:
+ msg = msg + author.content + " "
+ print msg
+ sections = node.xpathEval('section')
+ if sections != [] and level <= 0:
+ msg = ""
+ for section in sections:
+ msg = msg + section.content + " "
+ print "Tests for section %s" % (msg)
+ for test in node.xpathEval('testCase'):
+ handle_testCase(test)
+ for test in node.xpathEval('testSuite'):
+ handle_testSuite(test, level + 1)
+
+
+ if level >= 1 and sections != []:
+ msg = ""
+ for section in sections:
+ msg = msg + section.content + " "
+ print "Result of tests for section %s" % (msg)
+ if nb_schemas_tests != old_schemas_tests:
+ print "found %d test schemas: %d success %d failures" % (
+ nb_schemas_tests - old_schemas_tests,
+ nb_schemas_success - old_schemas_success,
+ nb_schemas_failed - old_schemas_failed)
+ if nb_instances_tests != old_instances_tests:
+ print "found %d test instances: %d success %d failures" % (
+ nb_instances_tests - old_instances_tests,
+ nb_instances_success - old_instances_success,
+ nb_instances_failed - old_instances_failed)
+#
+# Parse the conf file
+#
+libxml2.substituteEntitiesDefault(1);
+testsuite = libxml2.parseFile(CONF)
+
+#
+# Error and warnng callbacks
+#
+def callback(ctx, str):
+ global log
+ log.write("%s%s" % (ctx, str))
+
+libxml2.registerErrorHandler(callback, "")
+
+libxml2.setEntityLoader(resolver)
+root = testsuite.getRootElement()
+if root.name != 'testSuite':
+ print "%s doesn't start with a testSuite element, aborting" % (CONF)
+ sys.exit(1)
+print "Running Relax NG testsuite"
+handle_testSuite(root)
+
+print "\nTOTAL:\nfound %d test schemas: %d success %d failures" % (
+ nb_schemas_tests, nb_schemas_success, nb_schemas_failed)
+print "found %d test instances: %d success %d failures" % (
+ nb_instances_tests, nb_instances_success, nb_instances_failed)
+
+testsuite.freeDoc()
+
+# Memory debug specific
+libxml2.relaxNGCleanupTypes()
+libxml2.cleanupParser()
+if libxml2.debugMemory(1) == 0:
+ print "OK"
+else:
+ print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+ libxml2.dumpMemory()
diff --git a/include/libxml/parser.h b/include/libxml/parser.h
index 4578ad9..0a3597e 100644
--- a/include/libxml/parser.h
+++ b/include/libxml/parser.h
@@ -802,7 +802,7 @@
/*
* Parser contexts handling.
*/
-void xmlInitParserCtxt (xmlParserCtxtPtr ctxt);
+int xmlInitParserCtxt (xmlParserCtxtPtr ctxt);
void xmlClearParserCtxt (xmlParserCtxtPtr ctxt);
void xmlFreeParserCtxt (xmlParserCtxtPtr ctxt);
void xmlSetupParserForBuffer (xmlParserCtxtPtr ctxt,
diff --git a/include/libxml/tree.h b/include/libxml/tree.h
index 60c1d88..c8467e8 100644
--- a/include/libxml/tree.h
+++ b/include/libxml/tree.h
@@ -724,7 +724,7 @@
void xmlUnlinkNode (xmlNodePtr cur);
xmlNodePtr xmlTextMerge (xmlNodePtr first,
xmlNodePtr second);
-void xmlTextConcat (xmlNodePtr node,
+int xmlTextConcat (xmlNodePtr node,
const xmlChar *content,
int len);
void xmlFreeNodeList (xmlNodePtr cur);
diff --git a/parser.c b/parser.c
index c1ce81e..bb6f5cc 100644
--- a/parser.c
+++ b/parser.c
@@ -1720,6 +1720,8 @@
*prefix = NULL;
+ if (cur == NULL) return(NULL);
+
#ifndef XML_XML_NAMESPACE
/* xml: prefix is not really a namespace */
if ((cur[0] == 'x') && (cur[1] == 'm') &&
@@ -1905,6 +1907,14 @@
ctxt->input->cur = in;
ctxt->nbChars += count;
ctxt->input->col += count;
+ if (ret == NULL) {
+ if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
+ ctxt->sax->error(ctxt->userData,
+ "XML parser: out of memory\n");
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
+ }
return(ret);
}
}
@@ -4581,6 +4591,15 @@
return(NULL);
}
cur = ret = xmlNewElementContent(elem, XML_ELEMENT_CONTENT_ELEMENT);
+ if (cur == NULL) {
+ if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
+ ctxt->sax->error(ctxt->userData,
+ "xmlParseElementChildrenContentDecl : out of memory\n");
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ if (ctxt->recovery == 0) ctxt->disableSAX = 1;
+ xmlFree(elem);
+ return(NULL);
+ }
GROW;
if (RAW == '?') {
cur->ocur = XML_ELEMENT_CONTENT_OPT;
@@ -6731,18 +6750,35 @@
xmlGenericError(xmlGenericErrorContext,
"malloc of %ld byte failed\n",
maxatts * (long)sizeof(xmlChar *));
- return(NULL);
+ if (attname != NULL)
+ xmlFree(attname);
+ if (attvalue != NULL)
+ xmlFree(attvalue);
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
+ goto failed;
}
} else if (nbatts + 4 > maxatts) {
+ const xmlChar **n;
+
maxatts *= 2;
- atts = (const xmlChar **) xmlRealloc((void *) atts,
+ n = (const xmlChar **) xmlRealloc((void *) atts,
maxatts * sizeof(xmlChar *));
- if (atts == NULL) {
+ if (n == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc of %ld byte failed\n",
maxatts * (long)sizeof(xmlChar *));
- return(NULL);
+ if (attname != NULL)
+ xmlFree(attname);
+ if (attvalue != NULL)
+ xmlFree(attvalue);
+ ctxt->errNo = XML_ERR_NO_MEMORY;
+ ctxt->instate = XML_PARSER_EOF;
+ ctxt->disableSAX = 1;
+ goto failed;
}
+ atts = n;
}
atts[nbatts++] = attname;
atts[nbatts++] = attvalue;
@@ -6790,7 +6826,9 @@
ctxt->sax->startElement(ctxt->userData, name, atts);
if (atts != NULL) {
- for (i = 0;i < nbatts;i++) xmlFree((xmlChar *) atts[i]);
+ for (i = 0;i < nbatts;i++)
+ if (atts[i] != NULL)
+ xmlFree((xmlChar *) atts[i]);
xmlFree((void *) atts);
}
return(name);
@@ -8317,6 +8355,10 @@
xmlParseGetLasts(ctxt, &lastlt, &lastgt);
while (1) {
+ if ((ctxt->errNo != XML_ERR_OK) && (ctxt->disableSAX == 1))
+ return(0);
+
+
/*
* Pop-up of finished entities.
*/
@@ -9128,6 +9170,8 @@
int
xmlParseChunk(xmlParserCtxtPtr ctxt, const char *chunk, int size,
int terminate) {
+ if ((ctxt->errNo != XML_ERR_OK) && (ctxt->disableSAX == 1))
+ return(ctxt->errNo);
if ((size > 0) && (chunk != NULL) && (ctxt->input != NULL) &&
(ctxt->input->buf != NULL) && (ctxt->instate != XML_PARSER_EOF)) {
int base = ctxt->input->base - ctxt->input->buf->buffer->content;
@@ -9163,6 +9207,8 @@
}
}
xmlParseTryOrFinish(ctxt, terminate);
+ if ((ctxt->errNo != XML_ERR_OK) && (ctxt->disableSAX == 1))
+ return(ctxt->errNo);
if (terminate) {
/*
* Check for termination
@@ -9259,7 +9305,9 @@
ctxt = xmlNewParserCtxt();
if (ctxt == NULL) {
- xmlFree(buf);
+ xmlGenericError(xmlGenericErrorContext,
+ "xml parser: out of memory\n");
+ xmlFreeParserInputBuffer(buf);
return(NULL);
}
if (sax != NULL) {
@@ -9267,8 +9315,10 @@
xmlFree(ctxt->sax);
ctxt->sax = (xmlSAXHandlerPtr) xmlMalloc(sizeof(xmlSAXHandler));
if (ctxt->sax == NULL) {
- xmlFree(buf);
- xmlFree(ctxt);
+ xmlGenericError(xmlGenericErrorContext,
+ "xml parser: out of memory\n");
+ xmlFreeParserInputBuffer(buf);
+ xmlFreeParserCtxt(ctxt);
return(NULL);
}
memcpy(ctxt->sax, sax, sizeof(xmlSAXHandler));
@@ -9284,7 +9334,7 @@
inputStream = xmlNewInputStream(ctxt);
if (inputStream == NULL) {
xmlFreeParserCtxt(ctxt);
- xmlFree(buf);
+ xmlFreeParserInputBuffer(buf);
return(NULL);
}
diff --git a/parserInternals.c b/parserInternals.c
index 8b574f2..1e2777d 100644
--- a/parserInternals.c
+++ b/parserInternals.c
@@ -2166,15 +2166,17 @@
* @ctxt: an XML parser context
*
* Initialize a parser context
+ *
+ * Returns 0 in case of success and -1 in case of error
*/
-void
+int
xmlInitParserCtxt(xmlParserCtxtPtr ctxt)
{
if(ctxt==NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlInitParserCtxt: NULL context given\n");
- return;
+ return(-1);
}
xmlDefaultSAXHandlerInit();
@@ -2183,6 +2185,7 @@
if (ctxt->sax == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlInitParserCtxt: out of memory\n");
+ return(-1);
}
else
memcpy(ctxt->sax, &xmlDefaultSAXHandler, sizeof(xmlSAXHandler));
@@ -2196,7 +2199,7 @@
ctxt->inputNr = 0;
ctxt->inputMax = 0;
ctxt->input = NULL;
- return;
+ return(-1);
}
ctxt->inputNr = 0;
ctxt->inputMax = 5;
@@ -2224,7 +2227,7 @@
ctxt->inputNr = 0;
ctxt->inputMax = 0;
ctxt->input = NULL;
- return;
+ return(-1);
}
ctxt->nodeNr = 0;
ctxt->nodeMax = 10;
@@ -2244,7 +2247,7 @@
ctxt->nameNr = 0;
ctxt->nameMax = 0;
ctxt->name = NULL;
- return;
+ return(-1);
}
ctxt->nameNr = 0;
ctxt->nameMax = 10;
@@ -2267,7 +2270,7 @@
ctxt->spaceNr = 0;
ctxt->spaceMax = 0;
ctxt->space = NULL;
- return;
+ return(-1);
}
ctxt->spaceNr = 1;
ctxt->spaceMax = 10;
@@ -2305,6 +2308,7 @@
ctxt->charset = XML_CHAR_ENCODING_UTF8;
ctxt->catalogs = NULL;
xmlInitNodeInfoSeq(&ctxt->node_seq);
+ return(0);
}
/**
@@ -2370,7 +2374,10 @@
return(NULL);
}
memset(ctxt, 0, sizeof(xmlParserCtxt));
- xmlInitParserCtxt(ctxt);
+ if (xmlInitParserCtxt(ctxt) < 0) {
+ xmlFreeParserCtxt(ctxt);
+ return(NULL);
+ }
return(ctxt);
}
diff --git a/testOOM.c b/testOOM.c
new file mode 100644
index 0000000..8e9a302
--- /dev/null
+++ b/testOOM.c
@@ -0,0 +1,183 @@
+/*
+ * testOOM.c: Test out-of-memory handling
+ *
+ * See Copyright for the status of this software.
+ *
+ * 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>
+#include <stdarg.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <libxml/xmlreader.h>
+
+#include "testOOMlib.h"
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+
+int debug = 0;
+int dump = 0;
+int noent = 0;
+int count = 0;
+int valid = 0;
+
+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");
+ exit(1);
+}
+static int elem, attrs;
+
+static int processNode(xmlTextReaderPtr reader) {
+ int type;
+
+ type = xmlTextReaderNodeType(reader);
+ if (count) {
+ if (type == 1) {
+ elem++;
+ attrs += xmlTextReaderAttributeCount(reader);
+ }
+ }
+
+ return TRUE;
+}
+
+/* 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 int
+check_load_file_memory_func (void *data)
+{
+ const char *filename = data;
+ xmlTextReaderPtr reader;
+ int ret;
+
+ if (count) {
+ elem = 0;
+ attrs = 0;
+ }
+
+ reader = xmlNewTextReaderFilename(filename);
+
+ if (reader != NULL) {
+ if (valid) {
+ if (xmlTextReaderSetParserProp(reader, XML_PARSER_VALIDATE, 1) == -1) {
+ xmlFreeTextReader (reader);
+ return TRUE;
+ }
+ }
+
+ /*
+ * Process all nodes in sequence
+ */
+ ret = xmlTextReaderRead (reader);
+
+ while (TRUE) {
+ if (ret == -1) {
+ xmlFreeTextReader (reader);
+ return TRUE;
+ } else if (ret != 1)
+ break;
+
+ if (!processNode(reader)) {
+ xmlFreeTextReader (reader);
+ return FALSE;
+ }
+
+ ret = xmlTextReaderRead(reader);
+ }
+
+ /*
+ * Done, cleanup and status
+ */
+ xmlFreeTextReader (reader);
+
+ return TRUE;
+ } else {
+ return TRUE;
+ }
+}
+
+int main(int argc, char **argv) {
+ int i;
+ int files = 0;
+
+ if (argc <= 1) {
+ usage(argv[0]);
+ return(1);
+ }
+ LIBXML_TEST_VERSION;
+
+ xmlMemSetup (test_free,
+ test_malloc,
+ test_realloc,
+ test_strdup);
+
+ 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")))
+ count++;
+ else if ((!strcmp(argv[i], "-valid")) || (!strcmp(argv[i], "--valid")))
+ valid++;
+ else if ((!strcmp(argv[i], "-noent")) ||
+ (!strcmp(argv[i], "--noent")))
+ noent++;
+ }
+ if (noent != 0)
+ xmlSubstituteEntitiesDefault(1);
+ for (i = 1; i < argc ; i++) {
+ if (argv[i][0] != '-') {
+ if (!test_oom_handling (check_load_file_memory_func,
+ argv[i])) {
+ fprintf (stderr, "Failed!\n");
+ return (1);
+ }
+
+ xmlCleanupParser();
+
+ if (test_get_malloc_blocks_outstanding () > 0) {
+ fprintf (stderr, "%d blocks leaked\n",
+ test_get_malloc_blocks_outstanding ());
+ xmlMemoryDump();
+ return (1);
+ }
+
+ files ++;
+ }
+ }
+ xmlMemoryDump();
+
+ return(0);
+}
diff --git a/testOOMlib.c b/testOOMlib.c
new file mode 100644
index 0000000..be904b3
--- /dev/null
+++ b/testOOMlib.c
@@ -0,0 +1,260 @@
+/*
+ * testOOM.c: Test out-of-memory handling
+ *
+ * See Copyright for the status of this software.
+ *
+ * Copyright 2003 Red Hat, Inc.
+ * Written by: hp@redhat.com
+ */
+
+#include "testOOMlib.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <string.h>
+
+#define _TEST_INT_MAX 2147483647
+#ifndef TRUE
+#define TRUE (1)
+#endif
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef NULL
+#define NULL ((void*)0)
+#endif
+
+#include <libxml/xmlmemory.h>
+
+static int fail_alloc_counter = _TEST_INT_MAX;
+static int n_failures_per_failure = 1;
+static int n_failures_this_failure = 0;
+static int n_blocks_outstanding = 0;
+
+/**
+ * Sets the number of allocations until we simulate a failed
+ * allocation. If set to 0, the next allocation to run
+ * fails; if set to 1, one succeeds then the next fails; etc.
+ * Set to _TEST_INT_MAX to not fail anything.
+ *
+ * @param until_next_fail number of successful allocs before one fails
+ */
+static void
+set_fail_alloc_counter (int until_next_fail)
+{
+ fail_alloc_counter = until_next_fail;
+}
+
+/**
+ * Gets the number of successful allocs until we'll simulate
+ * a failed alloc.
+ *
+ * @returns current counter value
+ */
+static int
+get_fail_alloc_counter (void)
+{
+ return fail_alloc_counter;
+}
+
+/**
+ * Sets how many mallocs to fail when the fail alloc counter reaches
+ * 0.
+ *
+ * @param number to fail
+ */
+static void
+set_fail_alloc_failures (int failures_per_failure)
+{
+ n_failures_per_failure = failures_per_failure;
+}
+
+/**
+ * Called when about to alloc some memory; if
+ * it returns #TRUE, then the allocation should
+ * fail. If it returns #FALSE, then the allocation
+ * should not fail.
+ *
+ * @returns #TRUE if this alloc should fail
+ */
+static int
+decrement_fail_alloc_counter (void)
+{
+ if (fail_alloc_counter <= 0)
+ {
+ n_failures_this_failure += 1;
+ if (n_failures_this_failure >= n_failures_per_failure)
+ {
+ fail_alloc_counter = _TEST_INT_MAX;
+
+ n_failures_this_failure = 0;
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ fail_alloc_counter -= 1;
+ return FALSE;
+ }
+}
+
+/**
+ * Get the number of outstanding malloc()'d blocks.
+ *
+ * @returns number of blocks
+ */
+int
+test_get_malloc_blocks_outstanding (void)
+{
+ return n_blocks_outstanding;
+}
+
+void*
+test_malloc (size_t bytes)
+{
+ if (decrement_fail_alloc_counter ())
+ {
+ /* FAIL the malloc */
+ return NULL;
+ }
+
+ if (bytes == 0) /* some system mallocs handle this, some don't */
+ return NULL;
+ else
+ {
+ void *mem;
+ mem = xmlMemMalloc (bytes);
+
+ if (mem)
+ n_blocks_outstanding += 1;
+
+ return mem;
+ }
+}
+
+void*
+test_realloc (void *memory,
+ size_t bytes)
+{
+ if (decrement_fail_alloc_counter ())
+ {
+ /* FAIL */
+ return NULL;
+ }
+
+ if (bytes == 0) /* guarantee this is safe */
+ {
+ test_free (memory);
+ return NULL;
+ }
+ else
+ {
+ void *mem;
+ mem = xmlMemRealloc (memory, bytes);
+
+ if (memory == NULL && mem != NULL)
+ n_blocks_outstanding += 1;
+
+ return mem;
+ }
+}
+
+void
+test_free (void *memory)
+{
+ if (memory) /* we guarantee it's safe to free (NULL) */
+ {
+ n_blocks_outstanding -= 1;
+
+ xmlMemFree (memory);
+ }
+}
+
+char*
+test_strdup (const char *str)
+{
+ int len;
+ char *copy;
+
+ if (str == NULL)
+ return NULL;
+
+ len = strlen (str);
+
+ copy = test_malloc (len + 1);
+ if (copy == NULL)
+ return NULL;
+
+ memcpy (copy, str, len + 1);
+
+ return copy;
+}
+
+static int
+run_failing_each_malloc (int n_mallocs,
+ TestMemoryFunction func,
+ void *data)
+{
+ n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
+
+ while (n_mallocs >= 0)
+ {
+ set_fail_alloc_counter (n_mallocs);
+
+ if (!(* func) (data))
+ return FALSE;
+
+ n_mallocs -= 1;
+ }
+
+ set_fail_alloc_counter (_TEST_INT_MAX);
+
+ return TRUE;
+}
+
+/**
+ * Tests how well the given function responds to out-of-memory
+ * situations. Calls the function repeatedly, failing a different
+ * call to malloc() each time. If the function ever returns #FALSE,
+ * the test fails. The function should return #TRUE whenever something
+ * valid (such as returning an error, or succeeding) occurs, and #FALSE
+ * if it gets confused in some way.
+ *
+ * @param func function to call
+ * @param data data to pass to function
+ * @returns #TRUE if the function never returns FALSE
+ */
+int
+test_oom_handling (TestMemoryFunction func,
+ void *data)
+{
+ int approx_mallocs;
+
+ /* Run once to see about how many mallocs are involved */
+
+ set_fail_alloc_counter (_TEST_INT_MAX);
+
+ if (!(* func) (data))
+ return FALSE;
+
+ approx_mallocs = _TEST_INT_MAX - get_fail_alloc_counter ();
+
+ set_fail_alloc_failures (1);
+ if (!run_failing_each_malloc (approx_mallocs, func, data))
+ return FALSE;
+
+ set_fail_alloc_failures (2);
+ if (!run_failing_each_malloc (approx_mallocs, func, data))
+ return FALSE;
+
+ set_fail_alloc_failures (3);
+ if (!run_failing_each_malloc (approx_mallocs, func, data))
+ return FALSE;
+
+ set_fail_alloc_counter (_TEST_INT_MAX);
+
+ return TRUE;
+}
diff --git a/testOOMlib.h b/testOOMlib.h
new file mode 100644
index 0000000..751999d
--- /dev/null
+++ b/testOOMlib.h
@@ -0,0 +1,26 @@
+#ifndef TEST_OOM_LIB_H
+#define TEST_OOM_LIB_H
+
+#include <config.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+void* test_malloc (size_t bytes);
+void* test_realloc (void *memory,
+ size_t bytes);
+void test_free (void *memory);
+char* test_strdup (const char *str);
+
+/* returns true on success */
+typedef int (* TestMemoryFunction) (void *data);
+
+/* returns true on success */
+int test_oom_handling (TestMemoryFunction func,
+ void *data);
+
+/* get number of blocks leaked */
+int test_get_malloc_blocks_outstanding (void);
+
+#endif
diff --git a/tree.c b/tree.c
index 867f074..b2445f0 100644
--- a/tree.c
+++ b/tree.c
@@ -193,6 +193,7 @@
xmlChar *ret = NULL;
*prefix = NULL;
+ if (name == NULL) return(NULL);
#ifndef XML_XML_NAMESPACE
/* xml: prefix is not really a namespace */
@@ -216,7 +217,21 @@
return(NULL);
*prefix = xmlStrndup(name, len);
+ if (*prefix == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlSplitQName2 : out of memory!\n");
+ return(NULL);
+ }
ret = xmlStrdup(&name[len + 1]);
+ if (ret == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlSplitQName2 : out of memory!\n");
+ if (*prefix != NULL) {
+ xmlFree(*prefix);
+ *prefix = NULL;
+ }
+ return(NULL);
+ }
return(ret);
}
@@ -1670,6 +1685,7 @@
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewNsPropEatName : malloc failed\n");
+ xmlFree(name);
return(NULL);
}
memset(cur, 0, sizeof(xmlAttr));
@@ -1988,6 +2004,7 @@
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlNewNode : malloc failed\n");
+ xmlFree(name);
return(NULL);
}
memset(cur, 0, sizeof(xmlNode));
@@ -6047,11 +6064,13 @@
* @len: @content length
*
* Concat the given string at the end of the existing node content
+ *
+ * Returns -1 in case of error, 0 otherwise
*/
-void
+int
xmlTextConcat(xmlNodePtr node, const xmlChar *content, int len) {
- if (node == NULL) return;
+ if (node == NULL) return(-1);
if ((node->type != XML_TEXT_NODE) &&
(node->type != XML_CDATA_SECTION_NODE)) {
@@ -6059,9 +6078,12 @@
xmlGenericError(xmlGenericErrorContext,
"xmlTextConcat: node is not text nor CDATA\n");
#endif
- return;
+ return(-1);
}
node->content = xmlStrncat(node->content, content, len);
+ if (node->content == NULL)
+ return(-1);
+ return(0);
}
/************************************************************************
diff --git a/uri.c b/uri.c
index b5d9360..6b0aa27 100644
--- a/uri.c
+++ b/uri.c
@@ -1992,6 +1992,9 @@
}
uri = xmlCreateURI();
+ if (uri == NULL) {
+ return(NULL);
+ }
#if defined(_WIN32) && !defined(__CYGWIN__)
len = xmlStrlen(path);
diff --git a/valid.c b/valid.c
index 2b5e051..77e7b87 100644
--- a/valid.c
+++ b/valid.c
@@ -59,24 +59,27 @@
static int
vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) {
- if (ctxt->vstateMax == 0) {
+ if ((ctxt->vstateMax == 0) || (ctxt->vstateTab == NULL)) {
ctxt->vstateMax = 10;
ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax *
sizeof(ctxt->vstateTab[0]));
if (ctxt->vstateTab == NULL) {
- VERROR(ctxt->userData, "realloc failed !n");
+ VERROR(ctxt->userData, "malloc failed !n");
return(-1);
}
}
if (ctxt->vstateNr >= ctxt->vstateMax) {
- ctxt->vstateMax *= 2;
- ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
- ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
- if (ctxt->vstateTab == NULL) {
+ xmlValidState *tmp;
+
+ tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
+ 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
+ if (tmp == NULL) {
VERROR(ctxt->userData, "realloc failed !n");
return(-1);
}
+ ctxt->vstateMax *= 2;
+ ctxt->vstateTab = tmp;
}
ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr];
ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl;
@@ -161,15 +164,28 @@
if (ctxt->vstateNr > MAX_RECURSE) {
return(-1);
}
+ if (ctxt->vstateTab == NULL) {
+ ctxt->vstateMax = 8;
+ ctxt->vstateTab = (xmlValidState *) xmlMalloc(
+ ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
+ if (ctxt->vstateTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "malloc failed !n");
+ return(-1);
+ }
+ }
if (ctxt->vstateNr >= ctxt->vstateMax) {
- ctxt->vstateMax *= 2;
- ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
- ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
- if (ctxt->vstateTab == NULL) {
+ xmlValidState *tmp;
+
+ tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
+ 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
+ if (tmp == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc failed !n");
return(-1);
}
+ ctxt->vstateMax *= 2;
+ ctxt->vstateTab = tmp;
ctxt->vstate = &ctxt->vstateTab[0];
}
/*
@@ -219,15 +235,15 @@
}
}
if (ctxt->nodeNr >= ctxt->nodeMax) {
- ctxt->nodeMax *= 2;
- ctxt->nodeTab =
- (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
- ctxt->nodeMax *
- sizeof(ctxt->nodeTab[0]));
- if (ctxt->nodeTab == NULL) {
+ xmlNodePtr *tmp;
+ tmp = (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
+ ctxt->nodeMax * 2 * sizeof(ctxt->nodeTab[0]));
+ if (tmp == NULL) {
xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
return (0);
}
+ ctxt->nodeMax *= 2;
+ ctxt->nodeTab = tmp;
}
ctxt->nodeTab[ctxt->nodeNr] = value;
ctxt->node = value;
@@ -635,7 +651,7 @@
xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
xmlAutomataSetFinalState(ctxt->am, ctxt->state);
elem->contModel = xmlAutomataCompile(ctxt->am);
- if (!xmlRegexpIsDeterminist(elem->contModel)) {
+ if (xmlRegexpIsDeterminist(elem->contModel) != 1) {
char expr[5000];
expr[0] = 0;
xmlSnprintfElementContent(expr, 5000, elem->content, 1);
@@ -901,7 +917,8 @@
strcat(buf, " ...");
return;
}
- strcat(buf, (char *) content->name);
+ if (content->name != NULL)
+ strcat(buf, (char *) content->name);
break;
case XML_ELEMENT_CONTENT_SEQ:
if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
@@ -1086,6 +1103,10 @@
if (table == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlAddElementDecl: Table creation failed!\n");
+ if (uqname != NULL)
+ xmlFree(uqname);
+ if (ns != NULL)
+ xmlFree(ns);
return(NULL);
}
@@ -1116,6 +1137,8 @@
VERROR(ctxt->userData, "Redefinition of element %s\n", name);
if (uqname != NULL)
xmlFree(uqname);
+ if (ns != NULL)
+ xmlFree(ns);
return(NULL);
}
} else {
@@ -1123,6 +1146,10 @@
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlAddElementDecl: out of memory\n");
+ if (uqname != NULL)
+ xmlFree(uqname);
+ if (ns != NULL)
+ xmlFree(ns);
return(NULL);
}
memset(ret, 0, sizeof(xmlElement));
@@ -1132,6 +1159,16 @@
* fill the structure.
*/
ret->name = xmlStrdup(name);
+ if (ret->name == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddElementDecl: out of memory\n");
+ if (uqname != NULL)
+ xmlFree(uqname);
+ if (ns != NULL)
+ xmlFree(ns);
+ xmlFree(ret);
+ return(NULL);
+ }
ret->prefix = ns;
/*
@@ -2667,7 +2704,7 @@
xmlElementPtr cur;
xmlChar *uqname = NULL, *prefix = NULL;
- if (dtd == NULL) return(NULL);
+ if ((dtd == NULL) || (name == NULL)) return(NULL);
if (dtd->elements == NULL)
return(NULL);
table = (xmlElementTablePtr) dtd->elements;
@@ -4641,7 +4678,7 @@
if (elemDecl->contModel == NULL)
ret = xmlValidBuildContentModel(ctxt, elemDecl);
if (elemDecl->contModel == NULL) {
- ret = -1;
+ return(-1);
} else {
xmlRegExecCtxtPtr exec;
@@ -4837,14 +4874,14 @@
char list[5000];
expr[0] = 0;
- xmlSnprintfElementContent(expr, 5000, cont, 1);
+ xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
list[0] = 0;
#ifndef LIBXML_REGEXP_ENABLED
if (repl != NULL)
- xmlSnprintfElements(list, 5000, repl, 1);
+ xmlSnprintfElements(&list[0], 5000, repl, 1);
else
#endif /* LIBXML_REGEXP_ENABLED */
- xmlSnprintfElements(list, 5000, child, 1);
+ xmlSnprintfElements(&list[0], 5000, child, 1);
if (name != NULL) {
if (parent != NULL) VECTXT(ctxt, parent);
diff --git a/xmlmemory.c b/xmlmemory.c
index c8aa793..38cb659 100644
--- a/xmlmemory.c
+++ b/xmlmemory.c
@@ -305,6 +305,8 @@
unsigned long number;
if (!xmlMemInitialized) xmlInitMemory();
+ if (ptr == NULL)
+ return(NULL);
TEST_POINT
p = CLIENT_2_HDR(ptr);
diff --git a/xmlreader.c b/xmlreader.c
index 9a24110..c7b4015 100644
--- a/xmlreader.c
+++ b/xmlreader.c
@@ -83,7 +83,8 @@
XML_TEXTREADER_END= 2,
XML_TEXTREADER_EMPTY= 3,
XML_TEXTREADER_BACKTRACK= 4,
- XML_TEXTREADER_DONE= 5
+ XML_TEXTREADER_DONE= 5,
+ XML_TEXTREADER_ERROR= 6
} xmlTextReaderState;
typedef enum {
@@ -417,6 +418,7 @@
s, 1);
reader->cur = inbuf->use;
reader->mode = XML_TEXTREADER_DONE;
+ if (val != 0) return(-1);
}
}
reader->state = oldstate;
@@ -1151,11 +1153,17 @@
ret->base = 0;
ret->cur = 0;
}
+ if (ret->ctxt == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlNewTextReader : malloc failed\n");
+ xmlFree(ret->sax);
+ xmlFree(ret);
+ return(NULL);
+ }
ret->ctxt->_private = ret;
ret->ctxt->linenumbers = 1;
ret->allocs = XML_TEXTREADER_CTXT;
return(ret);
-
}
/**
diff --git a/xmlregexp.c b/xmlregexp.c
index 8f1e9c1..6ea1bfe 100644
--- a/xmlregexp.c
+++ b/xmlregexp.c
@@ -332,23 +332,19 @@
xmlRegexpPtr ret;
ret = (xmlRegexpPtr) xmlMalloc(sizeof(xmlRegexp));
- if (ret == NULL)
+ if (ret == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "out of memory compiling regexp\n");
return(NULL);
+ }
memset(ret, 0, sizeof(xmlRegexp));
ret->string = ctxt->string;
- ctxt->string = NULL;
ret->nbStates = ctxt->nbStates;
- ctxt->nbStates = 0;
ret->states = ctxt->states;
- ctxt->states = NULL;
ret->nbAtoms = ctxt->nbAtoms;
- ctxt->nbAtoms = 0;
ret->atoms = ctxt->atoms;
- ctxt->atoms = NULL;
ret->nbCounters = ctxt->nbCounters;
- ctxt->nbCounters = 0;
ret->counters = ctxt->counters;
- ctxt->counters = NULL;
ret->determinist = ctxt->determinist;
if ((ret->determinist != 0) &&
@@ -373,6 +369,12 @@
*/
stateRemap = xmlMalloc(ret->nbStates * sizeof(int));
+ if (stateRemap == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "out of memory compiling regexp\n");
+ xmlFree(ret);
+ return(NULL);
+ }
for (i = 0;i < ret->nbStates;i++) {
if (ret->states[i] != NULL) {
stateRemap[i] = nbstates;
@@ -385,7 +387,22 @@
printf("Final: %d states\n", nbstates);
#endif
stringMap = xmlMalloc(ret->nbAtoms * sizeof(char *));
+ if (stringMap == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "out of memory compiling regexp\n");
+ xmlFree(stateRemap);
+ xmlFree(ret);
+ return(NULL);
+ }
stringRemap = xmlMalloc(ret->nbAtoms * sizeof(int));
+ if (stringRemap == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "out of memory compiling regexp\n");
+ xmlFree(stringMap);
+ xmlFree(stateRemap);
+ xmlFree(ret);
+ return(NULL);
+ }
for (i = 0;i < ret->nbAtoms;i++) {
if ((ret->atoms[i]->type == XML_REGEXP_STRING) &&
(ret->atoms[i]->quant == XML_REGEXP_QUANT_ONCE)) {
@@ -399,6 +416,15 @@
if (j >= nbatoms) {
stringRemap[i] = nbatoms;
stringMap[nbatoms] = xmlStrdup(value);
+ if (stringMap[nbatoms] == NULL) {
+ for (i = 0;i < nbatoms;i++)
+ xmlFree(stringMap[i]);
+ xmlFree(stringRemap);
+ xmlFree(stringMap);
+ xmlFree(stateRemap);
+ xmlFree(ret);
+ return(NULL);
+ }
nbatoms++;
}
} else {
@@ -407,20 +433,29 @@
for (i = 0;i < nbatoms;i++)
xmlFree(stringMap[i]);
xmlFree(stringMap);
- goto fail_compact;
+ xmlFree(ret);
+ return(NULL);
}
}
#ifdef DEBUG_COMPACTION
printf("Final: %d atoms\n", nbatoms);
#endif
+ transitions = (int *) xmlMalloc((nbstates + 1) *
+ (nbatoms + 1) * sizeof(int));
+ if (transitions == NULL) {
+ xmlFree(stateRemap);
+ xmlFree(stringRemap);
+ xmlFree(stringMap);
+ xmlFree(ret);
+ return(NULL);
+ }
+ memset(transitions, 0, (nbstates + 1) * (nbatoms + 1) * sizeof(int));
/*
* Allocate the transition table. The first entry for each
* state correspond to the state type.
*/
- transitions = (int *) xmlMalloc(nbstates * (nbatoms + 1) * sizeof(int));
transdata = NULL;
- memset(transitions, 0, nbstates * (nbatoms + 1) * sizeof(int));
for (i = 0;i < ret->nbStates;i++) {
int stateno, atomno, targetno, prev;
@@ -445,6 +480,11 @@
if (transdata != NULL)
memset(transdata, 0,
nbstates * nbatoms * sizeof(void *));
+ else {
+ xmlGenericError(xmlGenericErrorContext,
+ "out of memory compiling regexp\n");
+ break;
+ }
}
targetno = stateRemap[trans->to];
/*
@@ -470,7 +510,7 @@
for (i = 0;i < nbatoms;i++)
xmlFree(stringMap[i]);
xmlFree(stringMap);
- goto fail_compact;
+ goto not_determ;
}
} else {
#if 0
@@ -524,7 +564,14 @@
xmlFree(stateRemap);
xmlFree(stringRemap);
}
-fail_compact:
+not_determ:
+ ctxt->string = NULL;
+ ctxt->nbStates = 0;
+ ctxt->states = NULL;
+ ctxt->nbAtoms = 0;
+ ctxt->atoms = NULL;
+ ctxt->nbCounters = 0;
+ ctxt->counters = NULL;
return(ret);
}
@@ -1048,11 +1095,11 @@
return(ctxt->nbCounters++);
}
-static void
+static int
xmlRegAtomPush(xmlRegParserCtxtPtr ctxt, xmlRegAtomPtr atom) {
if (atom == NULL) {
ERROR("atom push: atom is NULL");
- return;
+ return(-1);
}
if (ctxt->maxAtoms == 0) {
ctxt->maxAtoms = 4;
@@ -1061,7 +1108,7 @@
if (ctxt->atoms == NULL) {
ERROR("atom push: allocation failed");
ctxt->maxAtoms = 0;
- return;
+ return(-1);
}
} else if (ctxt->nbAtoms >= ctxt->maxAtoms) {
xmlRegAtomPtr *tmp;
@@ -1071,12 +1118,13 @@
if (tmp == NULL) {
ERROR("atom push: allocation failed");
ctxt->maxAtoms /= 2;
- return;
+ return(-1);
}
ctxt->atoms = tmp;
}
atom->no = ctxt->nbAtoms;
ctxt->atoms[ctxt->nbAtoms++] = atom;
+ return(0);
}
static void
@@ -1132,8 +1180,9 @@
state->nbTrans++;
}
-static void
+static int
xmlRegStatePush(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr state) {
+ if (state == NULL) return(-1);
if (ctxt->maxStates == 0) {
ctxt->maxStates = 4;
ctxt->states = (xmlRegStatePtr *) xmlMalloc(ctxt->maxStates *
@@ -1141,7 +1190,7 @@
if (ctxt->states == NULL) {
ERROR("add range: allocation failed");
ctxt->maxStates = 0;
- return;
+ return(-1);
}
} else if (ctxt->nbStates >= ctxt->maxStates) {
xmlRegStatePtr *tmp;
@@ -1151,12 +1200,13 @@
if (tmp == NULL) {
ERROR("add range: allocation failed");
ctxt->maxStates /= 2;
- return;
+ return(-1);
}
ctxt->states = tmp;
}
state->no = ctxt->nbStates;
ctxt->states[ctxt->nbStates++] = state;
+ return(0);
}
/**
@@ -1245,20 +1295,23 @@
* @to: the target state or NULL for building a new one
* @atom: the atom generating the transition
*
+ * Returns 0 if succes and -1 in case of error.
*/
-static void
+static int
xmlFAGenerateTransitions(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr from,
xmlRegStatePtr to, xmlRegAtomPtr atom) {
if (atom == NULL) {
ERROR("genrate transition: atom == NULL");
- return;
+ return(-1);
}
if (atom->type == XML_REGEXP_SUBREG) {
/*
* this is a subexpression handling one should not need to
* create a new node excep for XML_REGEXP_QUANT_RANGE.
*/
- xmlRegAtomPush(ctxt, atom);
+ if (xmlRegAtomPush(ctxt, atom) < 0) {
+ return(-1);
+ }
if ((to != NULL) && (atom->stop != to) &&
(atom->quant != XML_REGEXP_QUANT_RANGE)) {
/*
@@ -1314,14 +1367,20 @@
default:
break;
}
- return;
+ return(0);
} else {
if (to == NULL) {
to = xmlRegNewState(ctxt);
- xmlRegStatePush(ctxt, to);
+ if (to != NULL)
+ xmlRegStatePush(ctxt, to);
+ else {
+ return(-1);
+ }
+ }
+ if (xmlRegAtomPush(ctxt, atom) < 0) {
+ return(-1);
}
xmlRegStateAddTrans(ctxt, from, atom, to, -1, -1);
- xmlRegAtomPush(ctxt, atom);
ctxt->state = to;
}
switch (atom->quant) {
@@ -1341,6 +1400,7 @@
default:
break;
}
+ return(0);
}
/**
@@ -1433,6 +1493,9 @@
int statenr, transnr;
xmlRegStatePtr state;
+ if (ctxt->states == NULL) return;
+
+
/*
* build the completed transitions bypassing the epsilons
* Use a marking algorithm to avoid loops
@@ -2276,6 +2339,8 @@
if (comp == NULL)
return(NULL);
+ if ((comp->compact == NULL) && (comp->states == NULL))
+ return(NULL);
exec = (xmlRegExecCtxtPtr) xmlMalloc(sizeof(xmlRegExecCtxt));
if (exec == NULL) {
return(NULL);
@@ -3568,6 +3633,8 @@
* @ctxt: a regexp parser context
*
* [8] QuantExact ::= [0-9]+
+ *
+ * Returns 0 if success or -1 in case of error
*/
static int
xmlFAParseQuantExact(xmlRegParserCtxtPtr ctxt) {
@@ -3723,8 +3790,9 @@
* @first: is taht the first
*
* [2] branch ::= piece*
+ 8
*/
-static void
+static int
xmlFAParseBranch(xmlRegParserCtxtPtr ctxt, int first) {
xmlRegStatePtr previous;
xmlRegAtomPtr prevatom = NULL;
@@ -3734,7 +3802,8 @@
ret = xmlFAParsePiece(ctxt);
if (ret != 0) {
if (first) {
- xmlFAGenerateTransitions(ctxt, previous, NULL, ctxt->atom);
+ if (xmlFAGenerateTransitions(ctxt, previous, NULL, ctxt->atom) < 0)
+ return(-1);
previous = ctxt->state;
} else {
prevatom = ctxt->atom;
@@ -3745,9 +3814,13 @@
ret = xmlFAParsePiece(ctxt);
if (ret != 0) {
if (first) {
- xmlFAGenerateTransitions(ctxt, previous, NULL, ctxt->atom);
+ if (xmlFAGenerateTransitions(ctxt, previous, NULL,
+ ctxt->atom) < 0)
+ return(-1);
} else {
- xmlFAGenerateTransitions(ctxt, previous, NULL, prevatom);
+ if (xmlFAGenerateTransitions(ctxt, previous, NULL,
+ prevatom) < 0)
+ return(-1);
prevatom = ctxt->atom;
}
previous = ctxt->state;
@@ -3755,8 +3828,10 @@
}
}
if (!first) {
- xmlFAGenerateTransitions(ctxt, previous, ctxt->end, prevatom);
+ if (xmlFAGenerateTransitions(ctxt, previous, ctxt->end, prevatom) < 0)
+ return(-1);
}
+ return(0);
}
/**
@@ -3994,7 +4069,15 @@
/* initialize the parser */
ctxt->end = NULL;
ctxt->start = ctxt->state = xmlRegNewState(ctxt);
- xmlRegStatePush(ctxt, ctxt->start);
+ if (ctxt->start == NULL) {
+ xmlFreeAutomata(ctxt);
+ return(NULL);
+ }
+ if (xmlRegStatePush(ctxt, ctxt->start) < 0) {
+ xmlRegFreeState(ctxt->start);
+ xmlFreeAutomata(ctxt);
+ return(NULL);
+ }
return(ctxt);
}
@@ -4067,12 +4150,17 @@
if ((am == NULL) || (from == NULL) || (token == NULL))
return(NULL);
atom = xmlRegNewAtom(am, XML_REGEXP_STRING);
+ if (atom == NULL)
+ return(NULL);
atom->data = data;
if (atom == NULL)
return(NULL);
atom->valuep = xmlStrdup(token);
- xmlFAGenerateTransitions(am, from, to, atom);
+ if (xmlFAGenerateTransitions(am, from, to, atom) < 0) {
+ xmlRegFreeAtom(atom);
+ return(NULL);
+ }
if (to == NULL)
return(am->state);
return(to);
@@ -4127,7 +4215,10 @@
atom->valuep = str;
}
- xmlFAGenerateTransitions(am, from, to, atom);
+ if (xmlFAGenerateTransitions(am, from, to, atom) < 0) {
+ xmlRegFreeAtom(atom);
+ return(NULL);
+ }
if (to == NULL)
return(am->state);
return(to);
@@ -4173,7 +4264,10 @@
atom->min = min;
atom->max = max;
- xmlFAGenerateTransitions(am, from, to, atom);
+ if (xmlFAGenerateTransitions(am, from, to, atom) < 0) {
+ xmlRegFreeAtom(atom);
+ return(NULL);
+ }
if (to == NULL)
to = am->state;
if (to == NULL)
@@ -4400,6 +4494,7 @@
xmlAutomataCompile(xmlAutomataPtr am) {
xmlRegexpPtr ret;
+ if ((am == NULL) || (am->error != 0)) return(NULL);
xmlFAEliminateEpsilonTransitions(am);
/* xmlFAComputesDeterminism(am); */
ret = xmlRegEpxFromParse(am);