diff --git a/ChangeLog b/ChangeLog index 0558bf05..187c801c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +Thu Apr 24 18:01:46 CEST 2003 Daniel Veillard + + * 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 * xmlschemastypes.c: trivial fix for 109774 removing a warning diff --git a/Makefile.am b/Makefile.am index 9d32c31b..7242f992 100644 --- a/Makefile.am +++ b/Makefile.am @@ -116,6 +116,11 @@ testReader_LDFLAGS = 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 6bbb4013..df30c625 100644 --- a/SAX.c +++ b/SAX.c @@ -232,6 +232,8 @@ externalSubset(void *ctx, const xmlChar *name, 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 @@ startDocument(void *ctx) "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 @@ startDocument(void *ctx) 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 @@ my_attribute(void *ctx, const xmlChar *fullname, const xmlChar *value, * 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 @@ my_attribute(void *ctx, const xmlChar *fullname, const xmlChar *value, 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 @@ xmlCheckDefaultedAttributes(xmlParserCtxtPtr ctxt, const xmlChar *name, 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 @@ xmlCheckDefaultedAttributes(xmlParserCtxtPtr ctxt, const xmlChar *name, my_attribute(ctxt, fulln, attr->defaultValue, prefix); } - xmlFree(fulln); + if ((fulln != fn) && (fulln != attr->name)) + xmlFree(fulln); } } } @@ -1301,7 +1353,14 @@ startElement(void *ctx, const xmlChar *fullname, const xmlChar **atts) * 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 @@ characters(void *ctx, const xmlChar *ch, int 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; return; } ctxt->nodemem = size; @@ -1596,7 +1658,14 @@ characters(void *ctx, const xmlChar *ch, int len) 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 @@ characters(void *ctx, const xmlChar *ch, int len) } 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 00000000..6a2b6eb9 --- /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 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 example"% (node.lineNo()) + schema = handle_correct(tsts[0]) + else: + print "warning line %d has no nor 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 4578ad91..0a3597e5 100644 --- a/include/libxml/parser.h +++ b/include/libxml/parser.h @@ -802,7 +802,7 @@ int xmlParseCtxtExternalEntity(xmlParserCtxtPtr ctx, /* * 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 60c1d88f..c8467e8e 100644 --- a/include/libxml/tree.h +++ b/include/libxml/tree.h @@ -724,7 +724,7 @@ xmlNodePtr xmlAddNextSibling (xmlNodePtr cur, 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 c1ce81e7..bb6f5cc6 100644 --- a/parser.c +++ b/parser.c @@ -1720,6 +1720,8 @@ xmlSplitQName(xmlParserCtxtPtr ctxt, const xmlChar *name, xmlChar **prefix) { *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 @@ xmlParseName(xmlParserCtxtPtr ctxt) { 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 @@ xmlParseElementChildrenContentDecl 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 @@ xmlParseStartTag(xmlParserCtxtPtr ctxt) { 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 @@ xmlParseStartTag(xmlParserCtxtPtr ctxt) { 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 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { 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 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { 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 @@ xmlParseChunk(xmlParserCtxtPtr ctxt, const char *chunk, int size, } } xmlParseTryOrFinish(ctxt, terminate); + if ((ctxt->errNo != XML_ERR_OK) && (ctxt->disableSAX == 1)) + return(ctxt->errNo); if (terminate) { /* * Check for termination @@ -9259,7 +9305,9 @@ xmlCreatePushParserCtxt(xmlSAXHandlerPtr sax, void *user_data, 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 @@ xmlCreatePushParserCtxt(xmlSAXHandlerPtr sax, void *user_data, 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 @@ xmlCreatePushParserCtxt(xmlSAXHandlerPtr sax, void *user_data, inputStream = xmlNewInputStream(ctxt); if (inputStream == NULL) { xmlFreeParserCtxt(ctxt); - xmlFree(buf); + xmlFreeParserInputBuffer(buf); return(NULL); } diff --git a/parserInternals.c b/parserInternals.c index 8b574f25..1e2777d7 100644 --- a/parserInternals.c +++ b/parserInternals.c @@ -2166,15 +2166,17 @@ xmlNewInputFromFile(xmlParserCtxtPtr ctxt, const char *filename) { * @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 @@ xmlInitParserCtxt(xmlParserCtxtPtr ctxt) if (ctxt->sax == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlInitParserCtxt: out of memory\n"); + return(-1); } else memcpy(ctxt->sax, &xmlDefaultSAXHandler, sizeof(xmlSAXHandler)); @@ -2196,7 +2199,7 @@ xmlInitParserCtxt(xmlParserCtxtPtr ctxt) ctxt->inputNr = 0; ctxt->inputMax = 0; ctxt->input = NULL; - return; + return(-1); } ctxt->inputNr = 0; ctxt->inputMax = 5; @@ -2224,7 +2227,7 @@ xmlInitParserCtxt(xmlParserCtxtPtr ctxt) ctxt->inputNr = 0; ctxt->inputMax = 0; ctxt->input = NULL; - return; + return(-1); } ctxt->nodeNr = 0; ctxt->nodeMax = 10; @@ -2244,7 +2247,7 @@ xmlInitParserCtxt(xmlParserCtxtPtr ctxt) ctxt->nameNr = 0; ctxt->nameMax = 0; ctxt->name = NULL; - return; + return(-1); } ctxt->nameNr = 0; ctxt->nameMax = 10; @@ -2267,7 +2270,7 @@ xmlInitParserCtxt(xmlParserCtxtPtr ctxt) ctxt->spaceNr = 0; ctxt->spaceMax = 0; ctxt->space = NULL; - return; + return(-1); } ctxt->spaceNr = 1; ctxt->spaceMax = 10; @@ -2305,6 +2308,7 @@ xmlInitParserCtxt(xmlParserCtxtPtr ctxt) ctxt->charset = XML_CHAR_ENCODING_UTF8; ctxt->catalogs = NULL; xmlInitNodeInfoSeq(&ctxt->node_seq); + return(0); } /** @@ -2370,7 +2374,10 @@ xmlNewParserCtxt() 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 00000000..8e9a3024 --- /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 +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include + +#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 00000000..be904b32 --- /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 +#endif + +#include + +#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 + +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 00000000..751999da --- /dev/null +++ b/testOOMlib.h @@ -0,0 +1,26 @@ +#ifndef TEST_OOM_LIB_H +#define TEST_OOM_LIB_H + +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#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 867f074a..b2445f00 100644 --- a/tree.c +++ b/tree.c @@ -193,6 +193,7 @@ xmlSplitQName2(const xmlChar *name, xmlChar **prefix) { 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 @@ xmlSplitQName2(const xmlChar *name, xmlChar **prefix) { 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 @@ xmlNewNsPropEatName(xmlNodePtr node, xmlNsPtr ns, xmlChar *name, if (cur == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlNewNsPropEatName : malloc failed\n"); + xmlFree(name); return(NULL); } memset(cur, 0, sizeof(xmlAttr)); @@ -1988,6 +2004,7 @@ xmlNewNodeEatName(xmlNsPtr ns, xmlChar *name) { if (cur == NULL) { xmlGenericError(xmlGenericErrorContext, "xmlNewNode : malloc failed\n"); + xmlFree(name); return(NULL); } memset(cur, 0, sizeof(xmlNode)); @@ -6047,11 +6064,13 @@ xmlIsBlankNode(xmlNodePtr node) { * @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 @@ xmlTextConcat(xmlNodePtr node, const xmlChar *content, int len) { 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 b5d93602..6b0aa27b 100644 --- a/uri.c +++ b/uri.c @@ -1992,6 +1992,9 @@ xmlCanonicPath(const xmlChar *path) } uri = xmlCreateURI(); + if (uri == NULL) { + return(NULL); + } #if defined(_WIN32) && !defined(__CYGWIN__) len = xmlStrlen(path); diff --git a/valid.c b/valid.c index 2b5e0517..77e7b874 100644 --- a/valid.c +++ b/valid.c @@ -59,24 +59,27 @@ typedef struct _xmlValidState { 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 @@ vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont, 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 @@ nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value) } } 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 @@ xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) { 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 @@ xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int 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 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name, 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 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name, 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 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name, 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 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name, * 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 @@ xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) { 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 @@ xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child, if (elemDecl->contModel == NULL) ret = xmlValidBuildContentModel(ctxt, elemDecl); if (elemDecl->contModel == NULL) { - ret = -1; + return(-1); } else { xmlRegExecCtxtPtr exec; @@ -4837,14 +4874,14 @@ xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child, 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 c8aa793e..38cb6599 100644 --- a/xmlmemory.c +++ b/xmlmemory.c @@ -305,6 +305,8 @@ xmlReallocLoc(void *ptr,size_t size, const char * file, int line) 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 9a24110c..c7b4015f 100644 --- a/xmlreader.c +++ b/xmlreader.c @@ -83,7 +83,8 @@ typedef enum { 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 @@ xmlTextReaderPushData(xmlTextReaderPtr reader) { s, 1); reader->cur = inbuf->use; reader->mode = XML_TEXTREADER_DONE; + if (val != 0) return(-1); } } reader->state = oldstate; @@ -1151,11 +1153,17 @@ xmlNewTextReader(xmlParserInputBufferPtr input, const char *URI) { 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 8f1e9c1d..6ea1bfe4 100644 --- a/xmlregexp.c +++ b/xmlregexp.c @@ -332,23 +332,19 @@ xmlRegEpxFromParse(xmlRegParserCtxtPtr ctxt) { 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 @@ xmlRegEpxFromParse(xmlRegParserCtxtPtr ctxt) { */ 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 @@ xmlRegEpxFromParse(xmlRegParserCtxtPtr ctxt) { 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 @@ xmlRegEpxFromParse(xmlRegParserCtxtPtr ctxt) { 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 @@ xmlRegEpxFromParse(xmlRegParserCtxtPtr ctxt) { 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 @@ xmlRegEpxFromParse(xmlRegParserCtxtPtr ctxt) { 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 @@ xmlRegEpxFromParse(xmlRegParserCtxtPtr ctxt) { for (i = 0;i < nbatoms;i++) xmlFree(stringMap[i]); xmlFree(stringMap); - goto fail_compact; + goto not_determ; } } else { #if 0 @@ -524,7 +564,14 @@ xmlRegEpxFromParse(xmlRegParserCtxtPtr ctxt) { 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 @@ xmlRegGetCounter(xmlRegParserCtxtPtr ctxt) { 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 @@ xmlRegAtomPush(xmlRegParserCtxtPtr ctxt, xmlRegAtomPtr atom) { 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 @@ xmlRegAtomPush(xmlRegParserCtxtPtr ctxt, xmlRegAtomPtr atom) { 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 @@ xmlRegStateAddTrans(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr state, 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 @@ xmlRegStatePush(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr state) { 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 @@ xmlRegStatePush(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr state) { 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 @@ xmlFAGenerateCountedTransition(xmlRegParserCtxtPtr ctxt, * @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 @@ xmlFAGenerateTransitions(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr from, 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 @@ xmlFAGenerateTransitions(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr from, default: break; } + return(0); } /** @@ -1433,6 +1493,9 @@ xmlFAEliminateEpsilonTransitions(xmlRegParserCtxtPtr ctxt) { 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 @@ xmlRegNewExecCtxt(xmlRegexpPtr comp, xmlRegExecCallbacks callback, void *data) { 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 @@ xmlFAParseCharClass(xmlRegParserCtxtPtr ctxt) { * @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 @@ xmlFAParsePiece(xmlRegParserCtxtPtr ctxt) { * @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 @@ xmlFAParseBranch(xmlRegParserCtxtPtr ctxt, int first) { 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 @@ xmlFAParseBranch(xmlRegParserCtxtPtr ctxt, int first) { 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 @@ xmlFAParseBranch(xmlRegParserCtxtPtr ctxt, int first) { } } if (!first) { - xmlFAGenerateTransitions(ctxt, previous, ctxt->end, prevatom); + if (xmlFAGenerateTransitions(ctxt, previous, ctxt->end, prevatom) < 0) + return(-1); } + return(0); } /** @@ -3994,7 +4069,15 @@ xmlNewAutomata(void) { /* 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 @@ xmlAutomataNewTransition(xmlAutomataPtr am, xmlAutomataStatePtr from, 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 @@ xmlAutomataNewTransition2(xmlAutomataPtr am, xmlAutomataStatePtr from, 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 @@ xmlAutomataNewCountTrans(xmlAutomataPtr am, xmlAutomataStatePtr from, 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 @@ xmlRegexpPtr xmlAutomataCompile(xmlAutomataPtr am) { xmlRegexpPtr ret; + if ((am == NULL) || (am->error != 0)) return(NULL); xmlFAEliminateEpsilonTransitions(am); /* xmlFAComputesDeterminism(am); */ ret = xmlRegEpxFromParse(am);