diff --git a/backport-Annotate-functions-with-__attribute__-no_sanitize.patch b/backport-Annotate-functions-with-__attribute__-no_sanitize.patch new file mode 100644 index 0000000..1d6ab68 --- /dev/null +++ b/backport-Annotate-functions-with-__attribute__-no_sanitize.patch @@ -0,0 +1,85 @@ +From 44e7a0d5f7a7e2c167a8a4196a5358830f695ab0 Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Thu, 16 May 2019 21:17:28 +0200 +Subject: [PATCH] Annotate functions with __attribute__((no_sanitize)) + +--- + dict.c | 2 ++ + hash.c | 2 ++ + libxml.h | 7 +++++++ + xpath.c | 1 + + 4 files changed, 12 insertions(+) + +diff --git a/dict.c b/dict.c +index 14fe398..13a7b93 100644 +--- a/dict.c ++++ b/dict.c +@@ -372,6 +372,7 @@ found_pool: + * http://burtleburtle.net/bob/hash/doobs.html + */ + ++ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") + static uint32_t + xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) { + uint32_t hash; +@@ -404,6 +405,7 @@ xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) { + * + * Neither of the two strings must be NULL. + */ ++ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") + static unsigned long + xmlDictComputeBigQKey(const xmlChar *prefix, int plen, + const xmlChar *name, int len, int seed) +diff --git a/hash.c b/hash.c +index 1145cb9..f037af6 100644 +--- a/hash.c ++++ b/hash.c +@@ -79,6 +79,7 @@ struct _xmlHashTable { + * xmlHashComputeKey: + * Calculate the hash key + */ ++ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") + static unsigned long + xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *name, + const xmlChar *name2, const xmlChar *name3) { +@@ -109,6 +110,7 @@ xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *name, + return (value % table->size); + } + ++ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") + static unsigned long + xmlHashComputeQKey(xmlHashTablePtr table, + const xmlChar *prefix, const xmlChar *name, +diff --git a/libxml.h b/libxml.h +index 64e30f7..7762331 100644 +--- a/libxml.h ++++ b/libxml.h +@@ -72,6 +72,13 @@ int vfprintf(FILE *, const char *, va_list); + #define XML_POP_WARNINGS + #endif + ++#if defined(__clang__) || \ ++ (defined(__GNUC__) && (__GNUC__ >= 8)) ++#define ATTRIBUTE_NO_SANITIZE(arg) __attribute__((no_sanitize(arg))) ++#else ++#define ATTRIBUTE_NO_SANITIZE(arg) ++#endif ++ + /* + * Internal variable indicating if a callback has been registered for + * node creation/destruction. It avoids spending a lot of time in locking +diff --git a/xpath.c b/xpath.c +index 031772c..e68975e 100644 +--- a/xpath.c ++++ b/xpath.c +@@ -7497,6 +7497,7 @@ xmlXPathMultValues(xmlXPathParserContextPtr ctxt) { + * The numeric operators convert their operands to numbers as if + * by calling the number function. + */ ++ATTRIBUTE_NO_SANITIZE("float-divide-by-zero") + void + xmlXPathDivValues(xmlXPathParserContextPtr ctxt) { + xmlXPathObjectPtr arg; +-- +1.7.12.4 + diff --git a/backport-Another-fix-for-conditional-sections-at-end-of-docum.patch b/backport-Another-fix-for-conditional-sections-at-end-of-docum.patch new file mode 100644 index 0000000..3269950 --- /dev/null +++ b/backport-Another-fix-for-conditional-sections-at-end-of-docum.patch @@ -0,0 +1,27 @@ +From 443fd9665e9471b8083e2b5770f14f200b880381 Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Tue, 29 Oct 2019 16:19:37 +0100 +Subject: [PATCH 3/3] Another fix for conditional sections at end of document + +The previous fix introduced an uninitialized read. + +backport from https://gitlab.gnome.org/GNOME/libxml2/commit/9737ec071786c29788b9aa0971156f9e19a9c6a0 +--- + parser.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/parser.c b/parser.c +index f188c9d..84f1723 100644 +--- a/parser.c ++++ b/parser.c +@@ -6725,6 +6725,7 @@ xmlParseConditionalSections(xmlParserCtxtPtr ctxt) { + + if (RAW == 0) { + xmlFatalErr(ctxt, XML_ERR_CONDSEC_NOT_FINISHED, NULL); ++ goto error; + } + if (ctxt->input->id != id) { + xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY, +-- +2.18.1 + diff --git a/backport-Avoid-ignored-attribute-warnings-under-GCC.patch b/backport-Avoid-ignored-attribute-warnings-under-GCC.patch new file mode 100644 index 0000000..c92a96c --- /dev/null +++ b/backport-Avoid-ignored-attribute-warnings-under-GCC.patch @@ -0,0 +1,62 @@ +From b88ae6d2e1c9f22931f59ff1ec490befe201fb26 Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Mon, 14 Oct 2019 15:38:28 +0200 +Subject: [PATCH] Avoid ignored attribute warnings under GCC + +GCC doesn't support the unsigned-integer-overflow sanitizer. +--- + dict.c | 4 ++++ + hash.c | 4 ++++ + 2 files changed, 8 insertions(+) + +diff --git a/dict.c b/dict.c +index fb0773b..336e046 100644 +--- a/dict.c ++++ b/dict.c +@@ -372,7 +372,9 @@ found_pool: + * http://burtleburtle.net/bob/hash/doobs.html + */ + ++#ifdef __clang__ + ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") ++#endif + static uint32_t + xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) { + uint32_t hash; +@@ -405,7 +407,9 @@ xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) { + * + * Neither of the two strings must be NULL. + */ ++#ifdef __clang__ + ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") ++#endif + static unsigned long + xmlDictComputeBigQKey(const xmlChar *prefix, int plen, + const xmlChar *name, int len, int seed) +diff --git a/hash.c b/hash.c +index f037af6..a83d979 100644 +--- a/hash.c ++++ b/hash.c +@@ -79,7 +79,9 @@ struct _xmlHashTable { + * xmlHashComputeKey: + * Calculate the hash key + */ ++#ifdef __clang__ + ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") ++#endif + static unsigned long + xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *name, + const xmlChar *name2, const xmlChar *name3) { +@@ -110,7 +112,9 @@ xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *name, + return (value % table->size); + } + ++#ifdef __clang__ + ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") ++#endif + static unsigned long + xmlHashComputeQKey(xmlHashTablePtr table, + const xmlChar *prefix, const xmlChar *name, +-- +1.7.12.4 + diff --git a/backport-Fix-for-conditional-sections-at-end-of-document.patch b/backport-Fix-for-conditional-sections-at-end-of-document.patch new file mode 100644 index 0000000..768e5c4 --- /dev/null +++ b/backport-Fix-for-conditional-sections-at-end-of-document.patch @@ -0,0 +1,41 @@ +From 2b9fee2b0baf675b38b5e11756e9339d14c27a3a Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Wed, 23 Oct 2019 11:40:34 +0200 +Subject: [PATCH 2/3] Fix for conditional sections at end of document + +Parsing conditional sections would fail if the final ']]>' was at the +end of the document. Short-lived regression caused by commit c51e38cb. + +backport from https://gitlab.gnome.org/GNOME/libxml2/commit/c1035664f989c2ac7ca31407bc6f0b48396db42c +--- + parser.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/parser.c b/parser.c +index bfa6585..f188c9d 100644 +--- a/parser.c ++++ b/parser.c +@@ -6723,6 +6723,9 @@ xmlParseConditionalSections(xmlParserCtxtPtr ctxt) { + ctxt->disableSAX = state; + ctxt->instate = instate; + ++ if (RAW == 0) { ++ xmlFatalErr(ctxt, XML_ERR_CONDSEC_NOT_FINISHED, NULL); ++ } + if (ctxt->input->id != id) { + xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY, + "All markup of the conditional section is" +@@ -6763,10 +6766,6 @@ xmlParseConditionalSections(xmlParserCtxtPtr ctxt) { + GROW; + } + +- if (RAW == 0) { +- xmlFatalErr(ctxt, XML_ERR_CONDSEC_NOT_FINISHED, NULL); +- } +- + error: + xmlFree(inputIds); + } +-- +2.18.1 + diff --git a/backport-Fix-use-after-free-in-xmlTextReaderFreeNodeList.patch b/backport-Fix-use-after-free-in-xmlTextReaderFreeNodeList.patch new file mode 100644 index 0000000..f5eec27 --- /dev/null +++ b/backport-Fix-use-after-free-in-xmlTextReaderFreeNodeList.patch @@ -0,0 +1,36 @@ +From 664f881008f40356c0502c8cc154e17e3c80e353 Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Thu, 26 Sep 2019 11:01:58 +0200 +Subject: [PATCH] Fix use-after-free in xmlTextReaderFreeNodeList + +Recent commit 1fbcf40 caused a use-after-free read because it didn't +account for the fact that xmlTextReaderFreeDoc frees entities before +freeing entity references via xmlTextReaderFreeNodeList. + +Found by OSS-Fuzz. +--- + xmlreader.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/xmlreader.c b/xmlreader.c +index 9229c18..b505f16 100644 +--- a/xmlreader.c ++++ b/xmlreader.c +@@ -367,10 +367,10 @@ xmlTextReaderFreeNodeList(xmlTextReaderPtr reader, xmlNodePtr cur) { + return; + } + while (1) { +- while ((cur->children != NULL) && +- (cur->children->parent == cur) && +- (cur->type != XML_DTD_NODE) && +- (cur->type != XML_ENTITY_REF_NODE)) { ++ while ((cur->type != XML_DTD_NODE) && ++ (cur->type != XML_ENTITY_REF_NODE) && ++ (cur->children != NULL) && ++ (cur->children->parent == cur)) { + cur = cur->children; + depth += 1; + } +-- +1.7.12.4 + diff --git a/backport-Make-xmlDumpElementContent-non-recursive.patch b/backport-Make-xmlDumpElementContent-non-recursive.patch new file mode 100644 index 0000000..512016d --- /dev/null +++ b/backport-Make-xmlDumpElementContent-non-recursive.patch @@ -0,0 +1,211 @@ +From 3be39335ced799eb1ec312b7b542c84dac4ebd57 Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Fri, 4 Oct 2019 14:42:59 +0200 +Subject: [PATCH] Make xmlDumpElementContent non-recursive + +Avoid call stack overflow when dumping deeply nested element +declarations. + +Found by OSS-Fuzz. + +backport from https://gitlab.gnome.org/GNOME/libxml2/commit/24e3973bc03c15d534b2eac6e70fc2b2bef2b6c0 +--- + valid.c | 157 ++++++++++++++++++++++++++++++++------------------------ + 1 file changed, 89 insertions(+), 68 deletions(-) + +diff --git a/valid.c b/valid.c +index b1cfede..632c5e7 100644 +--- a/valid.c ++++ b/valid.c +@@ -1148,83 +1148,104 @@ xmlFreeElementContent(xmlElementContentPtr cur) { + + #ifdef LIBXML_OUTPUT_ENABLED + /** +- * xmlDumpElementContent: ++ * xmlDumpElementOccur: + * @buf: An XML buffer +- * @content: An element table +- * @glob: 1 if one must print the englobing parenthesis, 0 otherwise ++ * @cur: An element table + * +- * This will dump the content of the element table as an XML DTD definition ++ * Dump the occurence operator of an element. + */ + static void +-xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) { +- if (content == NULL) return; +- +- if (glob) xmlBufferWriteChar(buf, "("); +- switch (content->type) { +- case XML_ELEMENT_CONTENT_PCDATA: +- xmlBufferWriteChar(buf, "#PCDATA"); +- break; +- case XML_ELEMENT_CONTENT_ELEMENT: +- if (content->prefix != NULL) { +- xmlBufferWriteCHAR(buf, content->prefix); +- xmlBufferWriteChar(buf, ":"); +- } +- xmlBufferWriteCHAR(buf, content->name); +- break; +- case XML_ELEMENT_CONTENT_SEQ: +- if ((content->c1 != NULL) && +- ((content->c1->type == XML_ELEMENT_CONTENT_OR) || +- (content->c1->type == XML_ELEMENT_CONTENT_SEQ))) +- xmlDumpElementContent(buf, content->c1, 1); +- else +- xmlDumpElementContent(buf, content->c1, 0); +- xmlBufferWriteChar(buf, " , "); +- if ((content->c2 != NULL) && +- ((content->c2->type == XML_ELEMENT_CONTENT_OR) || +- ((content->c2->type == XML_ELEMENT_CONTENT_SEQ) && +- (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)))) +- xmlDumpElementContent(buf, content->c2, 1); +- else +- xmlDumpElementContent(buf, content->c2, 0); +- break; +- case XML_ELEMENT_CONTENT_OR: +- if ((content->c1 != NULL) && +- ((content->c1->type == XML_ELEMENT_CONTENT_OR) || +- (content->c1->type == XML_ELEMENT_CONTENT_SEQ))) +- xmlDumpElementContent(buf, content->c1, 1); +- else +- xmlDumpElementContent(buf, content->c1, 0); +- xmlBufferWriteChar(buf, " | "); +- if ((content->c2 != NULL) && +- ((content->c2->type == XML_ELEMENT_CONTENT_SEQ) || +- ((content->c2->type == XML_ELEMENT_CONTENT_OR) && +- (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)))) +- xmlDumpElementContent(buf, content->c2, 1); +- else +- xmlDumpElementContent(buf, content->c2, 0); +- break; +- default: +- xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, +- "Internal: ELEMENT content corrupted invalid type\n", +- NULL); +- } +- if (glob) +- xmlBufferWriteChar(buf, ")"); +- switch (content->ocur) { ++xmlDumpElementOccur(xmlBufferPtr buf, xmlElementContentPtr cur) { ++ switch (cur->ocur) { + case XML_ELEMENT_CONTENT_ONCE: +- break; ++ break; + case XML_ELEMENT_CONTENT_OPT: +- xmlBufferWriteChar(buf, "?"); +- break; ++ xmlBufferWriteChar(buf, "?"); ++ break; + case XML_ELEMENT_CONTENT_MULT: +- xmlBufferWriteChar(buf, "*"); +- break; ++ xmlBufferWriteChar(buf, "*"); ++ break; + case XML_ELEMENT_CONTENT_PLUS: +- xmlBufferWriteChar(buf, "+"); +- break; ++ xmlBufferWriteChar(buf, "+"); ++ break; + } + } + ++/** ++ * xmlDumpElementContent: ++ * @buf: An XML buffer ++ * @content: An element table ++ * ++ * This will dump the content of the element table as an XML DTD definition ++ */ ++static void ++xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content) { ++ xmlElementContentPtr cur; ++ ++ if (content == NULL) return; ++ ++ xmlBufferWriteChar(buf, "("); ++ cur = content; ++ ++ do { ++ if (cur == NULL) return; ++ ++ switch (cur->type) { ++ case XML_ELEMENT_CONTENT_PCDATA: ++ xmlBufferWriteChar(buf, "#PCDATA"); ++ break; ++ case XML_ELEMENT_CONTENT_ELEMENT: ++ if (cur->prefix != NULL) { ++ xmlBufferWriteCHAR(buf, cur->prefix); ++ xmlBufferWriteChar(buf, ":"); ++ } ++ xmlBufferWriteCHAR(buf, cur->name); ++ break; ++ case XML_ELEMENT_CONTENT_SEQ: ++ case XML_ELEMENT_CONTENT_OR: ++ if ((cur != content) && ++ (cur->parent != NULL) && ++ ((cur->type != cur->parent->type) || ++ (cur->ocur != XML_ELEMENT_CONTENT_ONCE))) ++ xmlBufferWriteChar(buf, "("); ++ cur = cur->c1; ++ continue; ++ default: ++ xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, ++ "Internal: ELEMENT cur corrupted invalid type\n", ++ NULL); ++ } ++ ++ while (cur != content) { ++ xmlElementContentPtr parent = cur->parent; ++ ++ if (parent == NULL) return; ++ ++ if (((cur->type == XML_ELEMENT_CONTENT_OR) || ++ (cur->type == XML_ELEMENT_CONTENT_SEQ)) && ++ ((cur->type != parent->type) || ++ (cur->ocur != XML_ELEMENT_CONTENT_ONCE))) ++ xmlBufferWriteChar(buf, ")"); ++ xmlDumpElementOccur(buf, cur); ++ ++ if (cur == parent->c1) { ++ if (parent->type == XML_ELEMENT_CONTENT_SEQ) ++ xmlBufferWriteChar(buf, " , "); ++ else if (parent->type == XML_ELEMENT_CONTENT_OR) ++ xmlBufferWriteChar(buf, " | "); ++ ++ cur = parent->c2; ++ break; ++ } ++ ++ cur = parent; ++ } ++ } while (cur != content); ++ ++ xmlBufferWriteChar(buf, ")"); ++ xmlDumpElementOccur(buf, content); ++} ++ + /** + * xmlSprintfElementContent: + * @buf: an output buffer +@@ -1703,7 +1724,7 @@ xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) { + } + xmlBufferWriteCHAR(buf, elem->name); + xmlBufferWriteChar(buf, " "); +- xmlDumpElementContent(buf, elem->content, 1); ++ xmlDumpElementContent(buf, elem->content); + xmlBufferWriteChar(buf, ">\n"); + break; + case XML_ELEMENT_TYPE_ELEMENT: +@@ -1714,7 +1735,7 @@ xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) { + } + xmlBufferWriteCHAR(buf, elem->name); + xmlBufferWriteChar(buf, " "); +- xmlDumpElementContent(buf, elem->content, 1); ++ xmlDumpElementContent(buf, elem->content); + xmlBufferWriteChar(buf, ">\n"); + break; + default: +-- +2.18.1 + diff --git a/backport-Make-xmlFreeNodeList-non-recursive.patch b/backport-Make-xmlFreeNodeList-non-recursive.patch new file mode 100644 index 0000000..c45636e --- /dev/null +++ b/backport-Make-xmlFreeNodeList-non-recursive.patch @@ -0,0 +1,71 @@ +From 0762c9b69ba01628f72eada1c64ff3d361fb5716 Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Mon, 23 Sep 2019 17:07:40 +0200 +Subject: [PATCH 2/3] Make xmlFreeNodeList non-recursive + +Avoid call stack overflow when freeing deeply nested documents. +--- + tree.c | 26 +++++++++++++++++++++----- + 1 file changed, 21 insertions(+), 5 deletions(-) + +diff --git a/tree.c b/tree.c +index bba0614..4781326 100644 +--- a/tree.c ++++ b/tree.c +@@ -3664,7 +3664,9 @@ xmlNextElementSibling(xmlNodePtr node) { + void + xmlFreeNodeList(xmlNodePtr cur) { + xmlNodePtr next; ++ xmlNodePtr parent; + xmlDictPtr dict = NULL; ++ size_t depth = 0; + + if (cur == NULL) return; + if (cur->type == XML_NAMESPACE_DECL) { +@@ -3680,16 +3682,21 @@ xmlFreeNodeList(xmlNodePtr cur) { + return; + } + if (cur->doc != NULL) dict = cur->doc->dict; +- while (cur != NULL) { ++ while (1) { ++ while ((cur->children != NULL) && ++ (cur->type != XML_DTD_NODE) && ++ (cur->type != XML_ENTITY_REF_NODE)) { ++ cur = cur->children; ++ depth += 1; ++ } ++ + next = cur->next; ++ parent = cur->parent; + if (cur->type != XML_DTD_NODE) { + + if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue)) + xmlDeregisterNodeDefaultValue(cur); + +- if ((cur->children != NULL) && +- (cur->type != XML_ENTITY_REF_NODE)) +- xmlFreeNodeList(cur->children); + if (((cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_XINCLUDE_START) || + (cur->type == XML_XINCLUDE_END)) && +@@ -3720,7 +3727,16 @@ xmlFreeNodeList(xmlNodePtr cur) { + DICT_FREE(cur->name) + xmlFree(cur); + } +- cur = next; ++ ++ if (next != NULL) { ++ cur = next; ++ } else { ++ if ((depth == 0) || (parent == NULL)) ++ break; ++ depth -= 1; ++ cur = parent; ++ cur->children = NULL; ++ } + } + } + +-- +1.7.12.4 + diff --git a/backport-Make-xmlParseConditionalSections-non-recursive.patch b/backport-Make-xmlParseConditionalSections-non-recursive.patch new file mode 100644 index 0000000..47f49dc --- /dev/null +++ b/backport-Make-xmlParseConditionalSections-non-recursive.patch @@ -0,0 +1,510 @@ +From 94aa233233dac8d6f497f6584d183e3b5cc2a0f8 Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Mon, 30 Sep 2019 13:50:02 +0200 +Subject: [PATCH] Make xmlParseConditionalSections non-recursive + +Avoid call stack overflow in deeply nested conditional sections. + +Found by OSS-Fuzz. + +backport from https://gitlab.gnome.org/GNOME/libxml2/commit/c51e38cb3a808e315248e03c9e52bce08943c22b +--- + parser.c | 265 ++++++++++++++-------------- + result/errors/759573-2.xml.err | 10 -- + result/errors/759573.xml.err | 13 -- + result/valid/cond_sect1.xml | 8 + + result/valid/cond_sect1.xml.err | 0 + result/valid/cond_sect1.xml.err.rdr | 0 + result/valid/cond_sect2.xml | 0 + result/valid/cond_sect2.xml.err | 9 + + result/valid/cond_sect2.xml.err.rdr | 10 ++ + test/valid/cond_sect1.xml | 7 + + test/valid/cond_sect2.xml | 4 + + test/valid/dtds/cond_sect1.dtd | 20 +++ + test/valid/dtds/cond_sect2.dtd | 16 ++ + 13 files changed, 203 insertions(+), 159 deletions(-) + create mode 100644 result/valid/cond_sect1.xml + create mode 100644 result/valid/cond_sect1.xml.err + create mode 100644 result/valid/cond_sect1.xml.err.rdr + create mode 100644 result/valid/cond_sect2.xml + create mode 100644 result/valid/cond_sect2.xml.err + create mode 100644 result/valid/cond_sect2.xml.err.rdr + create mode 100644 test/valid/cond_sect1.xml + create mode 100644 test/valid/cond_sect2.xml + create mode 100644 test/valid/dtds/cond_sect1.dtd + create mode 100644 test/valid/dtds/cond_sect2.dtd + +diff --git a/parser.c b/parser.c +index 9fb14fe..bfa6585 100644 +--- a/parser.c ++++ b/parser.c +@@ -6632,149 +6632,143 @@ xmlParseElementDecl(xmlParserCtxtPtr ctxt) { + + static void + xmlParseConditionalSections(xmlParserCtxtPtr ctxt) { +- int id = ctxt->input->id; ++ int *inputIds = NULL; ++ size_t inputIdsSize = 0; ++ size_t depth = 0; + +- SKIP(3); +- SKIP_BLANKS; +- if (CMP7(CUR_PTR, 'I', 'N', 'C', 'L', 'U', 'D', 'E')) { +- SKIP(7); +- SKIP_BLANKS; +- if (RAW != '[') { +- xmlFatalErr(ctxt, XML_ERR_CONDSEC_INVALID, NULL); +- xmlHaltParser(ctxt); +- return; +- } else { +- if (ctxt->input->id != id) { +- xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY, +- "All markup of the conditional section is not" +- " in the same entity\n"); +- } +- NEXT; +- } +- if (xmlParserDebugEntities) { +- if ((ctxt->input != NULL) && (ctxt->input->filename)) +- xmlGenericError(xmlGenericErrorContext, +- "%s(%d): ", ctxt->input->filename, +- ctxt->input->line); +- xmlGenericError(xmlGenericErrorContext, +- "Entering INCLUDE Conditional Section\n"); +- } ++ while (ctxt->instate != XML_PARSER_EOF) { ++ if ((RAW == '<') && (NXT(1) == '!') && (NXT(2) == '[')) { ++ int id = ctxt->input->id; + +- SKIP_BLANKS; +- GROW; +- while (((RAW != 0) && ((RAW != ']') || (NXT(1) != ']') || +- (NXT(2) != '>'))) && (ctxt->instate != XML_PARSER_EOF)) { +- const xmlChar *check = CUR_PTR; +- unsigned int cons = ctxt->input->consumed; ++ SKIP(3); ++ SKIP_BLANKS; + +- if ((RAW == '<') && (NXT(1) == '!') && (NXT(2) == '[')) { +- xmlParseConditionalSections(ctxt); +- } else +- xmlParseMarkupDecl(ctxt); ++ if (CMP7(CUR_PTR, 'I', 'N', 'C', 'L', 'U', 'D', 'E')) { ++ SKIP(7); ++ SKIP_BLANKS; ++ if (RAW != '[') { ++ xmlFatalErr(ctxt, XML_ERR_CONDSEC_INVALID, NULL); ++ xmlHaltParser(ctxt); ++ goto error; ++ } ++ if (ctxt->input->id != id) { ++ xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY, ++ "All markup of the conditional section is" ++ " not in the same entity\n"); ++ } ++ NEXT; + +- SKIP_BLANKS; +- GROW; ++ if (inputIdsSize <= depth) { ++ int *tmp; + +- if ((CUR_PTR == check) && (cons == ctxt->input->consumed)) { +- xmlFatalErr(ctxt, XML_ERR_EXT_SUBSET_NOT_FINISHED, NULL); +- xmlHaltParser(ctxt); +- break; +- } +- } +- if (xmlParserDebugEntities) { +- if ((ctxt->input != NULL) && (ctxt->input->filename)) +- xmlGenericError(xmlGenericErrorContext, +- "%s(%d): ", ctxt->input->filename, +- ctxt->input->line); +- xmlGenericError(xmlGenericErrorContext, +- "Leaving INCLUDE Conditional Section\n"); +- } ++ inputIdsSize = (inputIdsSize == 0 ? 4 : inputIdsSize * 2); ++ tmp = (int *) xmlRealloc(inputIds, ++ inputIdsSize * sizeof(int)); ++ if (tmp == NULL) { ++ xmlErrMemory(ctxt, NULL); ++ goto error; ++ } ++ inputIds = tmp; ++ } ++ inputIds[depth] = id; ++ depth++; ++ } else if (CMP6(CUR_PTR, 'I', 'G', 'N', 'O', 'R', 'E')) { ++ int state; ++ xmlParserInputState instate; ++ size_t ignoreDepth = 0; ++ ++ SKIP(6); ++ SKIP_BLANKS; ++ if (RAW != '[') { ++ xmlFatalErr(ctxt, XML_ERR_CONDSEC_INVALID, NULL); ++ xmlHaltParser(ctxt); ++ goto error; ++ } ++ if (ctxt->input->id != id) { ++ xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY, ++ "All markup of the conditional section is" ++ " not in the same entity\n"); ++ } ++ NEXT; + +- } else if (CMP6(CUR_PTR, 'I', 'G', 'N', 'O', 'R', 'E')) { +- int state; +- xmlParserInputState instate; +- int depth = 0; ++ /* ++ * Parse up to the end of the conditional section but disable ++ * SAX event generating DTD building in the meantime ++ */ ++ state = ctxt->disableSAX; ++ instate = ctxt->instate; ++ if (ctxt->recovery == 0) ctxt->disableSAX = 1; ++ ctxt->instate = XML_PARSER_IGNORE; ++ ++ while (RAW != 0) { ++ if ((RAW == '<') && (NXT(1) == '!') && (NXT(2) == '[')) { ++ SKIP(3); ++ ignoreDepth++; ++ /* Check for integer overflow */ ++ if (ignoreDepth == 0) { ++ xmlErrMemory(ctxt, NULL); ++ goto error; ++ } ++ } else if ((RAW == ']') && (NXT(1) == ']') && ++ (NXT(2) == '>')) { ++ if (ignoreDepth == 0) ++ break; ++ SKIP(3); ++ ignoreDepth--; ++ } else { ++ NEXT; ++ } ++ } + +- SKIP(6); +- SKIP_BLANKS; +- if (RAW != '[') { +- xmlFatalErr(ctxt, XML_ERR_CONDSEC_INVALID, NULL); +- xmlHaltParser(ctxt); +- return; +- } else { +- if (ctxt->input->id != id) { +- xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY, +- "All markup of the conditional section is not" +- " in the same entity\n"); +- } +- NEXT; +- } +- if (xmlParserDebugEntities) { +- if ((ctxt->input != NULL) && (ctxt->input->filename)) +- xmlGenericError(xmlGenericErrorContext, +- "%s(%d): ", ctxt->input->filename, +- ctxt->input->line); +- xmlGenericError(xmlGenericErrorContext, +- "Entering IGNORE Conditional Section\n"); +- } ++ ctxt->disableSAX = state; ++ ctxt->instate = instate; + +- /* +- * Parse up to the end of the conditional section +- * But disable SAX event generating DTD building in the meantime +- */ +- state = ctxt->disableSAX; +- instate = ctxt->instate; +- if (ctxt->recovery == 0) ctxt->disableSAX = 1; +- ctxt->instate = XML_PARSER_IGNORE; ++ if (ctxt->input->id != id) { ++ xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY, ++ "All markup of the conditional section is" ++ " not in the same entity\n"); ++ } ++ SKIP(3); ++ } else { ++ xmlFatalErr(ctxt, XML_ERR_CONDSEC_INVALID_KEYWORD, NULL); ++ xmlHaltParser(ctxt); ++ goto error; ++ } ++ } else if ((depth > 0) && ++ (RAW == ']') && (NXT(1) == ']') && (NXT(2) == '>')) { ++ depth--; ++ if (ctxt->input->id != inputIds[depth]) { ++ xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY, ++ "All markup of the conditional section is not" ++ " in the same entity\n"); ++ } ++ SKIP(3); ++ } else { ++ const xmlChar *check = CUR_PTR; ++ unsigned int cons = ctxt->input->consumed; + +- while (((depth >= 0) && (RAW != 0)) && +- (ctxt->instate != XML_PARSER_EOF)) { +- if ((RAW == '<') && (NXT(1) == '!') && (NXT(2) == '[')) { +- depth++; +- SKIP(3); +- continue; +- } +- if ((RAW == ']') && (NXT(1) == ']') && (NXT(2) == '>')) { +- if (--depth >= 0) SKIP(3); +- continue; +- } +- NEXT; +- continue; +- } ++ xmlParseMarkupDecl(ctxt); + +- ctxt->disableSAX = state; +- ctxt->instate = instate; ++ if ((CUR_PTR == check) && (cons == ctxt->input->consumed)) { ++ xmlFatalErr(ctxt, XML_ERR_EXT_SUBSET_NOT_FINISHED, NULL); ++ xmlHaltParser(ctxt); ++ goto error; ++ } ++ } + +- if (xmlParserDebugEntities) { +- if ((ctxt->input != NULL) && (ctxt->input->filename)) +- xmlGenericError(xmlGenericErrorContext, +- "%s(%d): ", ctxt->input->filename, +- ctxt->input->line); +- xmlGenericError(xmlGenericErrorContext, +- "Leaving IGNORE Conditional Section\n"); +- } ++ if (depth == 0) ++ break; + +- } else { +- xmlFatalErr(ctxt, XML_ERR_CONDSEC_INVALID_KEYWORD, NULL); +- xmlHaltParser(ctxt); +- return; ++ SKIP_BLANKS; ++ GROW; + } + +- if (RAW == 0) +- SHRINK; +- + if (RAW == 0) { + xmlFatalErr(ctxt, XML_ERR_CONDSEC_NOT_FINISHED, NULL); +- } else { +- if (ctxt->input->id != id) { +- xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY, +- "All markup of the conditional section is not in" +- " the same entity\n"); +- } +- if ((ctxt-> instate != XML_PARSER_EOF) && +- ((ctxt->input->cur + 3) <= ctxt->input->end)) +- SKIP(3); + } ++ ++error: ++ xmlFree(inputIds); + } + + /** +@@ -6836,16 +6830,6 @@ xmlParseMarkupDecl(xmlParserCtxtPtr ctxt) { + if (ctxt->instate == XML_PARSER_EOF) + return; + +- /* +- * Conditional sections are allowed from entities included +- * by PE References in the internal subset. +- */ +- if ((ctxt->external == 0) && (ctxt->inputNr > 1)) { +- if ((RAW == '<') && (NXT(1) == '!') && (NXT(2) == '[')) { +- xmlParseConditionalSections(ctxt); +- } +- } +- + ctxt->instate = XML_PARSER_DTD; + } + +@@ -8306,6 +8290,15 @@ xmlParseInternalSubset(xmlParserCtxtPtr ctxt) { + xmlParseMarkupDecl(ctxt); + xmlParsePEReference(ctxt); + ++ /* ++ * Conditional sections are allowed from entities included ++ * by PE References in the internal subset. ++ */ ++ if ((ctxt->inputNr > 1) && ++ (RAW == '<') && (NXT(1) == '!') && (NXT(2) == '[')) { ++ xmlParseConditionalSections(ctxt); ++ } ++ + if ((CUR_PTR == check) && (cons == ctxt->input->consumed)) { + xmlFatalErr(ctxt, XML_ERR_INTERNAL_ERROR, + "xmlParseInternalSubset: error detected in Markup declaration\n"); +diff --git a/result/errors/759573-2.xml.err b/result/errors/759573-2.xml.err +index 86d6420..ecaf18f 100644 +--- a/result/errors/759573-2.xml.err ++++ b/result/errors/759573-2.xml.err +@@ -46,13 +46,3 @@ Entity: line 3: + Entity: line 3: + %zz;%xx; + Entity: line 1: + % ++ ++ ++]> ++ ++ text ++ +diff --git a/result/valid/cond_sect1.xml.err b/result/valid/cond_sect1.xml.err +new file mode 100644 +index 0000000..e69de29 +diff --git a/result/valid/cond_sect1.xml.err.rdr b/result/valid/cond_sect1.xml.err.rdr +new file mode 100644 +index 0000000..e69de29 +diff --git a/result/valid/cond_sect2.xml b/result/valid/cond_sect2.xml +new file mode 100644 +index 0000000..e69de29 +diff --git a/result/valid/cond_sect2.xml.err b/result/valid/cond_sect2.xml.err +new file mode 100644 +index 0000000..9a7624b +--- /dev/null ++++ b/result/valid/cond_sect2.xml.err +@@ -0,0 +1,9 @@ ++test/valid/dtds/cond_sect2.dtd:15: parser error : All markup of the conditional section is not in the same entity ++ %ent; ++ ^ ++Entity: line 1: ++]]> ++^ ++test/valid/dtds/cond_sect2.dtd:17: parser error : Content error in the external subset ++ ++^ +diff --git a/result/valid/cond_sect2.xml.err.rdr b/result/valid/cond_sect2.xml.err.rdr +new file mode 100644 +index 0000000..fd3cb75 +--- /dev/null ++++ b/result/valid/cond_sect2.xml.err.rdr +@@ -0,0 +1,10 @@ ++test/valid/dtds/cond_sect2.dtd:15: parser error : All markup of the conditional section is not in the same entity ++ %ent; ++ ^ ++Entity: line 1: ++]]> ++^ ++test/valid/dtds/cond_sect2.dtd:17: parser error : Content error in the external subset ++ ++^ ++./test/valid/cond_sect2.xml : failed to parse +diff --git a/test/valid/cond_sect1.xml b/test/valid/cond_sect1.xml +new file mode 100644 +index 0000000..796faa4 +--- /dev/null ++++ b/test/valid/cond_sect1.xml +@@ -0,0 +1,7 @@ ++ ++ ++]> ++ ++ text ++ +diff --git a/test/valid/cond_sect2.xml b/test/valid/cond_sect2.xml +new file mode 100644 +index 0000000..5153d05 +--- /dev/null ++++ b/test/valid/cond_sect2.xml +@@ -0,0 +1,4 @@ ++ ++ ++ text ++ +diff --git a/test/valid/dtds/cond_sect1.dtd b/test/valid/dtds/cond_sect1.dtd +new file mode 100644 +index 0000000..e327022 +--- /dev/null ++++ b/test/valid/dtds/cond_sect1.dtd +@@ -0,0 +1,20 @@ ++ ++ ++ ]]> ++ ]]> ++ ]]> ++]]> ++ ++ ++ ]]> ++ ]]> ++ ]]> ++]]> +diff --git a/test/valid/dtds/cond_sect2.dtd b/test/valid/dtds/cond_sect2.dtd +new file mode 100644 +index 0000000..29eb4bf +--- /dev/null ++++ b/test/valid/dtds/cond_sect2.dtd +@@ -0,0 +1,16 @@ ++"> ++ ++ ]]> ++ ]]> ++ ]]> ++ ]]> ++ %ent; ++]]> +-- +2.18.1 + diff --git a/backport-Make-xmlParseContent-and-xmlParseElement-non-recursi.patch b/backport-Make-xmlParseContent-and-xmlParseElement-non-recursi.patch new file mode 100644 index 0000000..88136c1 --- /dev/null +++ b/backport-Make-xmlParseContent-and-xmlParseElement-non-recursi.patch @@ -0,0 +1,286 @@ +From 62150ed2ab19a4dd76c15acc62c7d923d9f3b2cc Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Mon, 23 Sep 2019 14:46:41 +0200 +Subject: [PATCH 1/3] Make xmlParseContent and xmlParseElement non-recursive + +Split xmlParseElement into subfunctions. Use nameNsPush to store prefix, +URI and nsNr on the heap, similar to the push parser. + +Closes #84. +--- + parser.c | 121 +++++++++++++++++++++++++------------------ + result/errors/754947.xml.err | 2 +- + result/errors/759398.xml.err | 4 +- + 3 files changed, 74 insertions(+), 53 deletions(-) + +diff --git a/parser.c b/parser.c +index 5b8df8c..cad0a9d 100644 +--- a/parser.c ++++ b/parser.c +@@ -96,6 +96,12 @@ xmlCreateEntityParserCtxtInternal(const xmlChar *URL, const xmlChar *ID, + + static void xmlHaltParser(xmlParserCtxtPtr ctxt); + ++static int ++xmlParseElementStart(xmlParserCtxtPtr ctxt); ++ ++static void ++xmlParseElementEnd(xmlParserCtxtPtr ctxt); ++ + /************************************************************************ + * * + * Arbitrary limits set in the parser. See XML_PARSE_HUGE * +@@ -1822,7 +1828,6 @@ nodePop(xmlParserCtxtPtr ctxt) + return (ret); + } + +-#ifdef LIBXML_PUSH_ENABLED + /** + * nameNsPush: + * @ctxt: an XML parser context +@@ -1858,6 +1863,11 @@ nameNsPush(xmlParserCtxtPtr ctxt, const xmlChar * value, + goto mem_error; + } + ctxt->pushTab = tmp2; ++ } else if (ctxt->pushTab == NULL) { ++ ctxt->pushTab = (void **) xmlMalloc(ctxt->nameMax * 3 * ++ sizeof(ctxt->pushTab[0])); ++ if (ctxt->pushTab == NULL) ++ goto mem_error; + } + ctxt->nameTab[ctxt->nameNr] = value; + ctxt->name = value; +@@ -1869,6 +1879,7 @@ mem_error: + xmlErrMemory(ctxt, NULL); + return (-1); + } ++#ifdef LIBXML_PUSH_ENABLED + /** + * nameNsPop: + * @ctxt: an XML parser context +@@ -9812,9 +9823,10 @@ xmlParseCDSect(xmlParserCtxtPtr ctxt) { + + void + xmlParseContent(xmlParserCtxtPtr ctxt) { ++ int nameNr = ctxt->nameNr; ++ + GROW; + while ((RAW != 0) && +- ((RAW != '<') || (NXT(1) != '/')) && + (ctxt->instate != XML_PARSER_EOF)) { + const xmlChar *test = CUR_PTR; + unsigned int cons = ctxt->input->consumed; +@@ -9848,7 +9860,13 @@ xmlParseContent(xmlParserCtxtPtr ctxt) { + * Fourth case : a sub-element. + */ + else if (*cur == '<') { +- xmlParseElement(ctxt); ++ if (NXT(1) == '/') { ++ if (ctxt->nameNr <= nameNr) ++ break; ++ xmlParseElementEnd(ctxt); ++ } else { ++ xmlParseElementStart(ctxt); ++ } + } + + /* +@@ -9883,7 +9901,7 @@ xmlParseContent(xmlParserCtxtPtr ctxt) { + * xmlParseElement: + * @ctxt: an XML parser context + * +- * parse an XML element, this is highly recursive ++ * parse an XML element + * + * [39] element ::= EmptyElemTag | STag content ETag + * +@@ -9895,6 +9913,23 @@ xmlParseContent(xmlParserCtxtPtr ctxt) { + + void + xmlParseElement(xmlParserCtxtPtr ctxt) { ++ if (xmlParseElementStart(ctxt) != 0) ++ return; ++ xmlParseContent(ctxt); ++ if (ctxt->instate == XML_PARSER_EOF) ++ return; ++ xmlParseElementEnd(ctxt); ++} ++ ++/** ++ * xmlParseElementStart: ++ * @ctxt: an XML parser context ++ * ++ * Parse the start of an XML element. Returns -1 in case of error, 0 if an ++ * opening tag was parsed, 1 if an empty element was parsed. ++ */ ++static int ++xmlParseElementStart(xmlParserCtxtPtr ctxt) { + const xmlChar *name; + const xmlChar *prefix = NULL; + const xmlChar *URI = NULL; +@@ -9909,7 +9944,7 @@ xmlParseElement(xmlParserCtxtPtr ctxt) { + "Excessive depth in document: %d use XML_PARSE_HUGE option\n", + xmlParserMaxDepth); + xmlHaltParser(ctxt); +- return; ++ return(-1); + } + + /* Capture start position */ +@@ -9936,12 +9971,17 @@ xmlParseElement(xmlParserCtxtPtr ctxt) { + name = xmlParseStartTag(ctxt); + #endif /* LIBXML_SAX1_ENABLED */ + if (ctxt->instate == XML_PARSER_EOF) +- return; ++ return(-1); + if (name == NULL) { + spacePop(ctxt); +- return; ++ return(-1); + } +- namePush(ctxt, name); ++ if (ctxt->sax2) ++ nameNsPush(ctxt, name, prefix, URI, ctxt->nsNr - nsNr); ++#ifdef LIBXML_SAX1_ENABLED ++ else ++ namePush(ctxt, name); ++#endif /* LIBXML_SAX1_ENABLED */ + ret = ctxt->node; + + #ifdef LIBXML_VALID_ENABLED +@@ -9982,7 +10022,7 @@ xmlParseElement(xmlParserCtxtPtr ctxt) { + node_info.node = ret; + xmlParserAddNodeInfo(ctxt, &node_info); + } +- return; ++ return(1); + } + if (RAW == '>') { + NEXT1; +@@ -10010,41 +10050,39 @@ xmlParseElement(xmlParserCtxtPtr ctxt) { + node_info.node = ret; + xmlParserAddNodeInfo(ctxt, &node_info); + } +- return; ++ return(-1); + } + +- /* +- * Parse the content of the element: +- */ +- xmlParseContent(ctxt); +- if (ctxt->instate == XML_PARSER_EOF) +- return; +- if (!IS_BYTE_CHAR(RAW)) { +- xmlFatalErrMsgStrIntStr(ctxt, XML_ERR_TAG_NOT_FINISHED, +- "Premature end of data in tag %s line %d\n", +- name, line, NULL); ++ return(0); ++} + +- /* +- * end of parsing of this node. +- */ +- nodePop(ctxt); +- namePop(ctxt); +- spacePop(ctxt); +- if (nsNr != ctxt->nsNr) +- nsPop(ctxt, ctxt->nsNr - nsNr); +- return; +- } ++/** ++ * xmlParseElementEnd: ++ * @ctxt: an XML parser context ++ * ++ * Parse the end of an XML element. ++ */ ++static void ++xmlParseElementEnd(xmlParserCtxtPtr ctxt) { ++ xmlParserNodeInfo node_info; ++ xmlNodePtr ret = ctxt->node; ++ ++ if (ctxt->nameNr <= 0) ++ return; + + /* + * parse the end of tag: 'sax2) { +- xmlParseEndTag2(ctxt, prefix, URI, line, ctxt->nsNr - nsNr, tlen); ++ const xmlChar *prefix = ctxt->pushTab[ctxt->nameNr * 3 - 3]; ++ const xmlChar *URI = ctxt->pushTab[ctxt->nameNr * 3 - 2]; ++ int nsNr = (ptrdiff_t) ctxt->pushTab[ctxt->nameNr * 3 - 1]; ++ xmlParseEndTag2(ctxt, prefix, URI, 0, nsNr, 0); + namePop(ctxt); + } + #ifdef LIBXML_SAX1_ENABLED +- else +- xmlParseEndTag1(ctxt, line); ++ else ++ xmlParseEndTag1(ctxt, 0); + #endif /* LIBXML_SAX1_ENABLED */ + + /* +@@ -12361,13 +12399,6 @@ xmlCreatePushParserCtxt(xmlSAXHandlerPtr sax, void *user_data, + return(NULL); + } + ctxt->dictNames = 1; +- ctxt->pushTab = (void **) xmlMalloc(ctxt->nameMax * 3 * sizeof(xmlChar *)); +- if (ctxt->pushTab == NULL) { +- xmlErrMemory(ctxt, NULL); +- xmlFreeParserInputBuffer(buf); +- xmlFreeParserCtxt(ctxt); +- return(NULL); +- } + if (sax != NULL) { + #ifdef LIBXML_SAX1_ENABLED + if (ctxt->sax != (xmlSAXHandlerPtr) &xmlDefaultSAXHandler) +@@ -14949,16 +14980,6 @@ xmlCtxtResetPush(xmlParserCtxtPtr ctxt, const char *chunk, + + xmlCtxtReset(ctxt); + +- if (ctxt->pushTab == NULL) { +- ctxt->pushTab = (void **) xmlMalloc(ctxt->nameMax * 3 * +- sizeof(xmlChar *)); +- if (ctxt->pushTab == NULL) { +- xmlErrMemory(ctxt, NULL); +- xmlFreeParserInputBuffer(buf); +- return(1); +- } +- } +- + if (filename == NULL) { + ctxt->directory = NULL; + } else { +diff --git a/result/errors/754947.xml.err b/result/errors/754947.xml.err +index f45cb5a..51e9b4e 100644 +--- a/result/errors/754947.xml.err ++++ b/result/errors/754947.xml.err +@@ -2,6 +2,6 @@ + Bytes: 0xEE 0x5D 0x5D 0x3E + + ^ +-./test/errors/754947.xml:1: parser error : Premature end of data in tag d line 1 ++./test/errors/754947.xml:1: parser error : EndTag: ' + ^ +diff --git a/result/errors/759398.xml.err b/result/errors/759398.xml.err +index f6036a3..bc9e5e0 100644 +--- a/result/errors/759398.xml.err ++++ b/result/errors/759398.xml.err +@@ -1,10 +1,10 @@ + ./test/errors/759398.xml:210: parser error : StartTag: invalid element name + need to worry about parsers whi + ^ + ./test/errors/759398.xml:316: parser error : Extra content at the end of the document +-- +1.7.12.4 + diff --git a/backport-Make-xmlTextReaderFreeNodeList-non-recursive.patch b/backport-Make-xmlTextReaderFreeNodeList-non-recursive.patch new file mode 100644 index 0000000..7a68883 --- /dev/null +++ b/backport-Make-xmlTextReaderFreeNodeList-non-recursive.patch @@ -0,0 +1,77 @@ +From 1fbcf4098ba2aefe241de8d7ceb229b995d8daec Mon Sep 17 00:00:00 2001 +From: Nick Wellnhofer +Date: Mon, 23 Sep 2019 17:13:05 +0200 +Subject: [PATCH 3/3] Make xmlTextReaderFreeNodeList non-recursive + +Avoid call stack overflow when freeing deeply nested documents. + +Found by OSS-Fuzz. +--- + xmlreader.c | 32 +++++++++++++++++++++++--------- + 1 file changed, 23 insertions(+), 9 deletions(-) + +diff --git a/xmlreader.c b/xmlreader.c +index d715071..9229c18 100644 +--- a/xmlreader.c ++++ b/xmlreader.c +@@ -348,7 +348,9 @@ xmlTextReaderFreePropList(xmlTextReaderPtr reader, xmlAttrPtr cur) { + static void + xmlTextReaderFreeNodeList(xmlTextReaderPtr reader, xmlNodePtr cur) { + xmlNodePtr next; ++ xmlNodePtr parent; + xmlDictPtr dict; ++ size_t depth = 0; + + if ((reader != NULL) && (reader->ctxt != NULL)) + dict = reader->ctxt->dict; +@@ -364,18 +366,21 @@ xmlTextReaderFreeNodeList(xmlTextReaderPtr reader, xmlNodePtr cur) { + xmlFreeDoc((xmlDocPtr) cur); + return; + } +- while (cur != NULL) { ++ while (1) { ++ while ((cur->children != NULL) && ++ (cur->children->parent == cur) && ++ (cur->type != XML_DTD_NODE) && ++ (cur->type != XML_ENTITY_REF_NODE)) { ++ cur = cur->children; ++ depth += 1; ++ } ++ + next = cur->next; ++ parent = cur->parent; ++ + /* unroll to speed up freeing the document */ + if (cur->type != XML_DTD_NODE) { + +- if ((cur->children != NULL) && +- (cur->type != XML_ENTITY_REF_NODE)) { +- if (cur->children->parent == cur) +- xmlTextReaderFreeNodeList(reader, cur->children); +- cur->children = NULL; +- } +- + if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue)) + xmlDeregisterNodeDefaultValue(cur); + +@@ -414,7 +419,16 @@ xmlTextReaderFreeNodeList(xmlTextReaderPtr reader, xmlNodePtr cur) { + xmlFree(cur); + } + } +- cur = next; ++ ++ if (next != NULL) { ++ cur = next; ++ } else { ++ if ((depth == 0) || (parent == NULL)) ++ break; ++ depth -= 1; ++ cur = parent; ++ cur->children = NULL; ++ } + } + } + +-- +1.7.12.4 + diff --git a/backport-fix-infinite-loop-in-xmlStringLenDecodeEntities.patch b/backport-fix-infinite-loop-in-xmlStringLenDecodeEntities.patch new file mode 100644 index 0000000..a2bebec --- /dev/null +++ b/backport-fix-infinite-loop-in-xmlStringLenDecodeEntities.patch @@ -0,0 +1,32 @@ +From 73060457de03c0dada603bdc6c6e1fc9b08fc94b Mon Sep 17 00:00:00 2001 +From: Zhipeng Xie +Date: Thu, 12 Dec 2019 17:30:55 +0800 +Subject: [PATCH] Fix infinite loop in xmlStringLenDecodeEntities + +When ctxt->instate == XML_PARSER_EOF,xmlParseStringEntityRef +return NULL which cause a infinite loop in xmlStringLenDecodeEntities + +Found with libFuzzer. + +Signed-off-by: Zhipeng Xie +--- + parser.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/parser.c b/parser.c +index 4696f916..3e09b3e9 100644 +--- a/parser.c ++++ b/parser.c +@@ -2786,7 +2786,8 @@ xmlStringLenDecodeEntities(xmlParserCtxtPtr ctxt, const xmlChar *str, int len, + else + c = 0; + while ((c != 0) && (c != end) && /* non input consuming loop */ +- (c != end2) && (c != end3)) { ++ (c != end2) && (c != end3) && ++ (ctxt->instate != XML_PARSER_EOF)) { + + if (c == 0) break; + if ((c == '&') && (str[1] == '#')) { +-- +2.19.1 + diff --git a/libxml2.spec b/libxml2.spec index 9ebb27d..b433706 100644 --- a/libxml2.spec +++ b/libxml2.spec @@ -1,7 +1,7 @@ Summary: Library providing XML and HTML support Name: libxml2 Version: 2.9.8 -Release: 8 +Release: 9 License: MIT Group: Development/Libraries Source: ftp://xmlsoft.org/libxml2/libxml2-%{version}.tar.gz @@ -42,9 +42,20 @@ Patch6015: 0031-Fix-parser-termination-from-Double-hyphen-within-com.patch Patch6016: 0032-Fix-return-value-of-xmlOutputBufferWrite.patch Patch6017: 0034-Fix-unsigned-integer-overflow.patch Patch6018: 0037-Fix-memory-leak-in-xmlAllocOutputBufferInternal-erro.patch +Patch6019: backport-Make-xmlParseContent-and-xmlParseElement-non-recursi.patch +Patch6020: backport-Make-xmlFreeNodeList-non-recursive.patch +Patch6021: backport-Make-xmlTextReaderFreeNodeList-non-recursive.patch +Patch6022: backport-Fix-use-after-free-in-xmlTextReaderFreeNodeList.patch +Patch6023: backport-Make-xmlParseConditionalSections-non-recursive.patch +Patch6024: backport-Fix-for-conditional-sections-at-end-of-document.patch +Patch6025: backport-Another-fix-for-conditional-sections-at-end-of-docum.patch +Patch6026: backport-Make-xmlDumpElementContent-non-recursive.patch Patch9000: Fix-memory-leak-in-xmlParseBalancedChunkMemoryRecove.patch Patch9001: Fix-memory-leak-in-xmlSchemaValidateStream.patch +Patch6027: backport-fix-infinite-loop-in-xmlStringLenDecodeEntities.patch +Patch6028: backport-Annotate-functions-with-__attribute__-no_sanitize.patch +Patch6029: backport-Avoid-ignored-attribute-warnings-under-GCC.patch BuildRoot: %{_tmppath}/%{name}-%{version}-root BuildRequires: python2-devel @@ -234,6 +245,9 @@ rm -fr %{buildroot} %changelog +* Tue Mar 17 2020 Leo Fang - 2.9.8-9 +- Sync some patches from community + * Thu Dec 19 2019 openEuler Buildteam - 2.9.8-8 - Delete unused infomation