aboutsummaryrefslogtreecommitdiffstats
path: root/xinclude.c
diff options
context:
space:
mode:
Diffstat (limited to 'xinclude.c')
-rw-r--r--xinclude.c185
1 files changed, 100 insertions, 85 deletions
diff --git a/xinclude.c b/xinclude.c
index baeb8dbc..f48e0af5 100644
--- a/xinclude.c
+++ b/xinclude.c
@@ -59,7 +59,7 @@ struct _xmlXIncludeRef {
xmlNodePtr inc; /* the included copy */
int xml; /* xml or txt */
int count; /* how many refs use that specific doc */
- xmlXPathObjectPtr xptr; /* the xpointer if needed */
+ int fallback; /* fallback was loaded */
int emptyFb; /* flag to show fallback empty */
};
@@ -86,10 +86,13 @@ struct _xmlXIncludeCtxt {
xmlChar * base; /* the current xml:base */
void *_private; /* application data */
+
+ unsigned long incTotal; /* total number of processed inclusions */
};
static int
-xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr tree);
+xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr tree,
+ int skipRoot);
/************************************************************************
@@ -207,8 +210,6 @@ xmlXIncludeFreeRef(xmlXIncludeRefPtr ref) {
xmlFree(ref->URI);
if (ref->fragment != NULL)
xmlFree(ref->fragment);
- if (ref->xptr != NULL)
- xmlXPathFreeObject(ref->xptr);
xmlFree(ref);
}
@@ -626,8 +627,8 @@ xmlXIncludeAddNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr cur) {
xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_RECURSION,
"detected a local recursion with no xpointer in %s\n",
URL);
- if (fragment != NULL)
- xmlFree(fragment);
+ xmlFree(URL);
+ xmlFree(fragment);
return(-1);
}
@@ -639,12 +640,15 @@ xmlXIncludeAddNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr cur) {
if (xmlStrEqual(URL, ctxt->urlTab[i])) {
xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_RECURSION,
"detected a recursion in %s\n", URL);
+ xmlFree(URL);
+ xmlFree(fragment);
return(-1);
}
}
}
ref = xmlXIncludeNewRef(ctxt, URL, cur);
+ xmlFree(URL);
if (ref == NULL) {
return(-1);
}
@@ -652,7 +656,6 @@ xmlXIncludeAddNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr cur) {
ref->doc = NULL;
ref->xml = xml;
ref->count = 1;
- xmlFree(URL);
return(0);
}
@@ -729,7 +732,9 @@ xmlXIncludeRecurseDoc(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc,
* (bug 132597)
*/
newctxt->parseFlags = ctxt->parseFlags;
- xmlXIncludeDoProcess(newctxt, doc, xmlDocGetRootElement(doc));
+ newctxt->incTotal = ctxt->incTotal;
+ xmlXIncludeDoProcess(newctxt, doc, xmlDocGetRootElement(doc), 0);
+ ctxt->incTotal = newctxt->incTotal;
for (i = 0;i < ctxt->incNr;i++) {
newctxt->incTab[i]->count--;
newctxt->incTab[i] = NULL;
@@ -1459,7 +1464,7 @@ xmlXIncludeLoadDoc(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, int nr) {
*/
if ((URL[0] == 0) || (URL[0] == '#') ||
((ctxt->doc != NULL) && (xmlStrEqual(URL, ctxt->doc->URL)))) {
- doc = NULL;
+ doc = ctxt->doc;
goto loaded;
}
@@ -1551,15 +1556,8 @@ loaded:
/*
* Add the top children list as the replacement copy.
*/
- if (doc == NULL)
- {
- /* Hopefully a DTD declaration won't be copied from
- * the same document */
- ctxt->incTab[nr]->inc = xmlCopyNodeList(ctxt->doc->children);
- } else {
- ctxt->incTab[nr]->inc = xmlXIncludeCopyNodeList(ctxt, ctxt->doc,
- doc, doc->children);
- }
+ ctxt->incTab[nr]->inc = xmlXIncludeCopyNodeList(ctxt, ctxt->doc,
+ doc, doc->children);
}
#ifdef LIBXML_XPTR_ENABLED
else {
@@ -1571,12 +1569,7 @@ loaded:
xmlXPathContextPtr xptrctxt;
xmlNodeSetPtr set;
- if (doc == NULL) {
- xptrctxt = xmlXPtrNewContext(ctxt->doc, ctxt->incTab[nr]->ref,
- NULL);
- } else {
- xptrctxt = xmlXPtrNewContext(doc, NULL, NULL);
- }
+ xptrctxt = xmlXPtrNewContext(doc, NULL, NULL);
if (xptrctxt == NULL) {
xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref,
XML_XINCLUDE_XPTR_FAILED,
@@ -1680,14 +1673,9 @@ loaded:
}
}
}
- if (doc == NULL) {
- ctxt->incTab[nr]->xptr = xptr;
- ctxt->incTab[nr]->inc = NULL;
- } else {
- ctxt->incTab[nr]->inc =
- xmlXIncludeCopyXPointer(ctxt, ctxt->doc, doc, xptr);
- xmlXPathFreeObject(xptr);
- }
+ ctxt->incTab[nr]->inc =
+ xmlXIncludeCopyXPointer(ctxt, ctxt->doc, doc, xptr);
+ xmlXPathFreeObject(xptr);
xmlXPathFreeContext(xptrctxt);
xmlFree(fragment);
}
@@ -1990,19 +1978,23 @@ xmlXIncludeLoadFallback(xmlXIncludeCtxtPtr ctxt, xmlNodePtr fallback, int nr) {
newctxt->_private = ctxt->_private;
newctxt->base = xmlStrdup(ctxt->base); /* Inherit the base from the existing context */
xmlXIncludeSetFlags(newctxt, ctxt->parseFlags);
- ret = xmlXIncludeDoProcess(newctxt, ctxt->doc, fallback->children);
+ newctxt->incTotal = ctxt->incTotal;
+ if (xmlXIncludeDoProcess(newctxt, ctxt->doc, fallback, 1) < 0)
+ ret = -1;
+ ctxt->incTotal = newctxt->incTotal;
if (ctxt->nbErrors > oldNbErrors)
ret = -1;
- else if (ret > 0)
- ret = 0; /* xmlXIncludeDoProcess can return +ve number */
xmlXIncludeFreeContext(newctxt);
ctxt->incTab[nr]->inc = xmlDocCopyNodeList(ctxt->doc,
fallback->children);
+ if (ctxt->incTab[nr]->inc == NULL)
+ ctxt->incTab[nr]->emptyFb = 1;
} else {
ctxt->incTab[nr]->inc = NULL;
ctxt->incTab[nr]->emptyFb = 1; /* flag empty callback */
}
+ ctxt->incTab[nr]->fallback = 1;
return(ret);
}
@@ -2158,8 +2150,7 @@ xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt, int nr) {
((xmlStrEqual(children->ns->href, XINCLUDE_NS)) ||
(xmlStrEqual(children->ns->href, XINCLUDE_OLD_NS)))) {
ret = xmlXIncludeLoadFallback(ctxt, children, nr);
- if (ret == 0)
- break;
+ break;
}
children = children->next;
}
@@ -2206,19 +2197,9 @@ xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, int nr) {
if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
return(-1);
- /*
- * If we stored an XPointer a late computation may be needed
- */
- if ((ctxt->incTab[nr]->inc == NULL) &&
- (ctxt->incTab[nr]->xptr != NULL)) {
- ctxt->incTab[nr]->inc =
- xmlXIncludeCopyXPointer(ctxt, ctxt->doc, ctxt->doc,
- ctxt->incTab[nr]->xptr);
- xmlXPathFreeObject(ctxt->incTab[nr]->xptr);
- ctxt->incTab[nr]->xptr = NULL;
- }
list = ctxt->incTab[nr]->inc;
ctxt->incTab[nr]->inc = NULL;
+ ctxt->incTab[nr]->emptyFb = 0;
/*
* Check against the risk of generating a multi-rooted document
@@ -2238,6 +2219,7 @@ xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, int nr) {
XML_XINCLUDE_MULTIPLE_ROOT,
"XInclude error: would result in multiple root nodes\n",
NULL);
+ xmlFreeNodeList(list);
return(-1);
}
}
@@ -2255,16 +2237,27 @@ xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, int nr) {
xmlUnlinkNode(cur);
xmlFreeNode(cur);
} else {
+ xmlNodePtr child, next;
+
/*
* Change the current node as an XInclude start one, and add an
* XInclude end one
*/
+ if (ctxt->incTab[nr]->fallback)
+ xmlUnsetProp(cur, BAD_CAST "href");
cur->type = XML_XINCLUDE_START;
+ /* Remove fallback children */
+ for (child = cur->children; child != NULL; child = next) {
+ next = child->next;
+ xmlUnlinkNode(child);
+ xmlFreeNode(child);
+ }
end = xmlNewDocNode(cur->doc, cur->ns, cur->name, NULL);
if (end == NULL) {
xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref,
XML_XINCLUDE_BUILD_FAILED,
"failed to build node\n", NULL);
+ xmlFreeNodeList(list);
return(-1);
}
end->type = XML_XINCLUDE_END;
@@ -2366,6 +2359,7 @@ xmlXIncludeTestNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) {
* @ctxt: the XInclude processing context
* @doc: an XML document
* @tree: the top of the tree to process
+ * @skipRoot: don't process the root node of the tree
*
* Implement the XInclude substitution on the XML document @doc
*
@@ -2373,13 +2367,16 @@ xmlXIncludeTestNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) {
* or the number of substitutions done.
*/
static int
-xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr tree) {
+xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr tree,
+ int skipRoot) {
xmlNodePtr cur;
int ret = 0;
int i, start;
if ((doc == NULL) || (tree == NULL) || (tree->type == XML_NAMESPACE_DECL))
return(-1);
+ if ((skipRoot) && (tree->children == NULL))
+ return(-1);
if (ctxt == NULL)
return(-1);
@@ -2391,40 +2388,59 @@ xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr tree) {
start = ctxt->incNr;
/*
+ * TODO: The phases must run separately for recursive inclusions.
+ *
+ * - Phase 1 should start with top-level XInclude nodes, load documents,
+ * execute XPointer expressions, then process only the result nodes
+ * (not whole document, see bug #324081) and only for phase 1
+ * recursively. We will need a backreference from xmlNodes to
+ * xmlIncludeRefs to detect references that were already visited.
+ * This can also be used for proper cycle detection, see bug #344240.
+ *
+ * - Phase 2 should visit all top-level XInclude nodes and expand
+ * possible subreferences in the replacement recursively.
+ *
+ * - Phase 3 should finally replace the top-level XInclude nodes.
+ * It could also be run together with phase 2.
+ */
+
+ /*
* First phase: lookup the elements in the document
*/
- cur = tree;
- if (xmlXIncludeTestNode(ctxt, cur) == 1)
- xmlXIncludePreProcessNode(ctxt, cur);
- while ((cur != NULL) && (cur != tree->parent)) {
+ if (skipRoot)
+ cur = tree->children;
+ else
+ cur = tree;
+ do {
/* TODO: need to work on entities -> stack */
- if ((cur->children != NULL) &&
- (cur->children->type != XML_ENTITY_DECL) &&
- (cur->children->type != XML_XINCLUDE_START) &&
- (cur->children->type != XML_XINCLUDE_END)) {
- cur = cur->children;
- if (xmlXIncludeTestNode(ctxt, cur))
- xmlXIncludePreProcessNode(ctxt, cur);
- } else if (cur->next != NULL) {
- cur = cur->next;
- if (xmlXIncludeTestNode(ctxt, cur))
- xmlXIncludePreProcessNode(ctxt, cur);
- } else {
- if (cur == tree)
- break;
- do {
- cur = cur->parent;
- if ((cur == NULL) || (cur == tree->parent))
- break; /* do */
- if (cur->next != NULL) {
- cur = cur->next;
- if (xmlXIncludeTestNode(ctxt, cur))
- xmlXIncludePreProcessNode(ctxt, cur);
- break; /* do */
- }
- } while (cur != NULL);
- }
- }
+ if (xmlXIncludeTestNode(ctxt, cur) == 1) {
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ /*
+ * Avoid superlinear expansion by limiting the total number
+ * of replacements.
+ */
+ if (ctxt->incTotal >= 20)
+ return(-1);
+#endif
+ ctxt->incTotal++;
+ xmlXIncludePreProcessNode(ctxt, cur);
+ } else if ((cur->children != NULL) &&
+ (cur->children->type != XML_ENTITY_DECL) &&
+ (cur->children->type != XML_XINCLUDE_START) &&
+ (cur->children->type != XML_XINCLUDE_END)) {
+ cur = cur->children;
+ continue;
+ }
+ do {
+ if (cur == tree)
+ break;
+ if (cur->next != NULL) {
+ cur = cur->next;
+ break;
+ }
+ cur = cur->parent;
+ } while (cur != NULL);
+ } while ((cur != NULL) && (cur != tree));
/*
* Second Phase : collect the infosets fragments
@@ -2447,8 +2463,7 @@ xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr tree) {
*/
for (i = ctxt->incBase;i < ctxt->incNr; i++) {
if ((ctxt->incTab[i]->inc != NULL) ||
- (ctxt->incTab[i]->xptr != NULL) ||
- (ctxt->incTab[i]->emptyFb != 0)) /* (empty fallback) */
+ (ctxt->incTab[i]->emptyFb != 0)) /* (empty fallback) */
xmlXIncludeIncludeNode(ctxt, i);
}
@@ -2502,7 +2517,7 @@ xmlXIncludeProcessTreeFlagsData(xmlNodePtr tree, int flags, void *data) {
ctxt->_private = data;
ctxt->base = xmlStrdup((xmlChar *)tree->doc->URL);
xmlXIncludeSetFlags(ctxt, flags);
- ret = xmlXIncludeDoProcess(ctxt, tree->doc, tree);
+ ret = xmlXIncludeDoProcess(ctxt, tree->doc, tree, 0);
if ((ret >= 0) && (ctxt->nbErrors > 0))
ret = -1;
@@ -2586,7 +2601,7 @@ xmlXIncludeProcessTreeFlags(xmlNodePtr tree, int flags) {
return(-1);
ctxt->base = xmlNodeGetBase(tree->doc, tree);
xmlXIncludeSetFlags(ctxt, flags);
- ret = xmlXIncludeDoProcess(ctxt, tree->doc, tree);
+ ret = xmlXIncludeDoProcess(ctxt, tree->doc, tree, 0);
if ((ret >= 0) && (ctxt->nbErrors > 0))
ret = -1;
@@ -2626,7 +2641,7 @@ xmlXIncludeProcessNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) {
if ((node == NULL) || (node->type == XML_NAMESPACE_DECL) ||
(node->doc == NULL) || (ctxt == NULL))
return(-1);
- ret = xmlXIncludeDoProcess(ctxt, node->doc, node);
+ ret = xmlXIncludeDoProcess(ctxt, node->doc, node, 0);
if ((ret >= 0) && (ctxt->nbErrors > 0))
ret = -1;
return(ret);