parser: Fix infinite loop in xmlCtxtParseContent
The loop to find an element/document ancestor uses "cur = node->parent"
as the iterator instead of "cur = cur->parent". This causes an infinite
loop when the immediate parent is not an element/document/html node
(e.g., when the node's parent is an entity reference).
Fix by iterating with cur->parent to properly walk up the ancestor chain.
Add regression test to testparser.c.
Fixes: 4f329dc5 ("parser: Implement xmlCtxtParseContent")
diff --git a/parser.c b/parser.c
index c7f46a3..6e7621a 100644
--- a/parser.c
+++ b/parser.c
@@ -12033,7 +12033,7 @@
case XML_ENTITY_REF_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
- for (cur = node->parent; cur != NULL; cur = node->parent) {
+ for (cur = node->parent; cur != NULL; cur = cur->parent) {
if ((cur->type == XML_ELEMENT_NODE) ||
(cur->type == XML_DOCUMENT_NODE) ||
(cur->type == XML_HTML_DOCUMENT_NODE)) {
diff --git a/testparser.c b/testparser.c
index 36b950e..39b2bd4 100644
--- a/testparser.c
+++ b/testparser.c
@@ -399,6 +399,49 @@
return err;
}
+/*
+ * Test that xmlParseInNodeContext doesn't hang when called on a node
+ * whose parent is an entity reference (not an element).
+ * Regression test for infinite loop bug in xmlCtxtParseContent.
+ */
+static int
+testParseInNodeContextEntityParent(void) {
+ xmlDocPtr doc;
+ xmlNodePtr root, entRef, textNode, result = NULL;
+ int err = 0;
+
+ doc = xmlNewDoc(BAD_CAST "1.0");
+ root = xmlNewNode(NULL, BAD_CAST "root");
+ xmlDocSetRootElement(doc, root);
+
+ /* Create an entity reference node */
+ entRef = xmlNewReference(doc, BAD_CAST "testentity");
+ xmlAddChild(root, entRef);
+
+ /* Create a text node as child of the entity reference */
+ textNode = xmlNewText(BAD_CAST "content");
+ xmlAddChild(entRef, textNode);
+
+ /*
+ * This used to hang in an infinite loop because the code walked
+ * up parents with "cur = node->parent" instead of "cur = cur->parent".
+ */
+ xmlParseInNodeContext(textNode, "<x/>", 4, 0, &result);
+
+ if (result != NULL)
+ xmlFreeNodeList(result);
+
+ /*
+ * Entity reference children aren't freed automatically by xmlFreeDoc,
+ * so we need to unlink and free the text node manually.
+ */
+ xmlUnlinkNode(textNode);
+ xmlFreeNode(textNode);
+ xmlFreeDoc(doc);
+
+ return err;
+}
+
static int
testNoBlanks(void) {
const xmlChar xml[] =
@@ -1504,6 +1547,7 @@
#endif
#ifdef LIBXML_OUTPUT_ENABLED
err |= testCtxtParseContent();
+ err |= testParseInNodeContextEntityParent();
err |= testNoBlanks();
err |= testSaveNullEnc();
err |= testDocDumpFormatMemoryEnc();