GNOME Bugzilla – Bug 350085
xsl:variable changes value when using node-set for multipass processing
Last modified: 2006-08-09 18:35:34 UTC
The following transform provides a minimal test case for a problem that arose in the XSLT-based C code generator for the XML-XCB project. This transform performs multi-pass processing using the EXSLT node-set extension. The bug arose when I attempted to define a variable in the stylesheet containing some fixed data and traverse it as part of the definition of one of the variables used for multi-pass processing; both variables consist of a result-tree-fragment subsequently passed to the node-set extension. If you run this transform, the first access to the request-suffixes variable via xsl:for-each successfully iterates over the two elements it contains, but the second access sees no content in the variable. test.xsl: <?xml version="1.0" encoding="utf-8"?> <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:e="http://exslt.org/common" exclude-result-prefixes="e"> <xsl:variable name="request-suffixes-rtf"> <checked /> <checked /> </xsl:variable> <xsl:variable name="request-suffixes" select="e:node-set($request-suffixes-rtf)" /> <xsl:template match="request" mode="pass1"> <thing1> <xsl:for-each select="$request-suffixes/checked"> <thing2 /> </xsl:for-each> </thing1> </xsl:template> <xsl:variable name="pass1-rtf"> <xsl:apply-templates select="/" mode="pass1" /> </xsl:variable> <xsl:variable name="pass1" select="e:node-set($pass1-rtf)" /> <xsl:template match="/"> <root> <xsl:copy-of select="$pass1/*" /> </root> </xsl:template> </xsl:transform> test.xml: <?xml version="1.0" encoding="utf-8"?> <xcb> <request /> <request /> </xcb> Run this example with: xsltproc test.xsl test.xml | xmllint --format - Expected result: <?xml version="1.0"?> <root> <thing1> <thing2/> <thing2/> </thing1> <thing1> <thing2/> <thing2/> </thing1> </root> Actual result: <?xml version="1.0"?> <root> <thing1> <thing2/> <thing2/> </thing1> <thing1/> </root> Excerpts from the xsltproc -v debugging output: [...] xsltForEach: select $request-suffixes/checked Lookup variable request-suffixes Evaluating global variable request-suffixes Lookup variable request-suffixes-rtf Evaluating global variable request-suffixes-rtf reusing transformation dict for RVT xsltApplyOneTemplate: copy node checked xsltApplyOneTemplate: copy node checked Object is an XSLT value tree : 1 ELEMENT checked ELEMENT checked found variable request-suffixes-rtf Lookup function {http://exslt.org/common}node-set found function node-set Object is a Node Set : Set contains 1 nodes: 1 / found variable request-suffixes xsltForEach: select evaluates to 2 nodes xsltForEach: Changing document - context doc temp.xml, xpathdoc (null) xsltApplyOneTemplate: copy node thing2 xsltApplyOneTemplate: copy node thing2 [...] xsltForEach: select $request-suffixes/checked Lookup variable request-suffixes found variable request-suffixes xsltForEach: select evaluates to 0 nodes
xsltproc --version: Using libxml 20626, libxslt 10117 and libexslt 813 xsltproc was compiled against libxml 20626, libxslt 10117 and libexslt 813 libxslt 10117 was compiled against libxml 20626 libexslt 813 was compiled against libxml 20626
"valgrind xsltproc temp.xsl temp.xml" also seems rather informative; it indicates repeated accesses to freed memory: ==31926== Invalid read of size 4 ==31926== at 0x41415AA: xmlXPathNodeSetMerge (xpath.c:3708) ==31926== by 0x4147653: xmlXPathObjectCopy (xpath.c:5062) ==31926== by 0x40A5AED: xsltVariableLookup (variables.c:1401) ==31926== by 0x40A5CA1: xsltXPathVariableLookup (variables.c:1727) ==31926== by 0x414B19B: xmlXPathVariableLookup (xpath.c:4679) ==31926== by 0x414C3CE: xmlXPathCompOpEval (xpath.c:12960) ==31926== by 0x414BC3C: xmlXPathCompOpEval (xpath.c:12918) ==31926== by 0x414B5AE: xmlXPathCompOpEval (xpath.c:13423) ==31926== by 0x415149C: xmlXPathCompiledEval (xpath.c:14203) ==31926== by 0x40B521E: xsltForEach (transform.c:4854) ==31926== by 0x40B466E: xsltApplyOneTemplateInt (transform.c:2525) ==31926== by 0x40B6707: xsltProcessOneNode (transform.c:1513) ==31926== Address 0x43A89EC is 4 bytes inside a block of size 88 free'd ==31926== at 0x401D139: free (vg_replace_malloc.c:233) ==31926== by 0x4115DA6: xmlFreeDoc (tree.c:1179) ==31926== by 0x40B4486: xsltApplyOneTemplateInt (transform.c:2790) ==31926== by 0x40B6707: xsltProcessOneNode (transform.c:1513) ==31926== by 0x40B6707: xsltProcessOneNode (transform.c:1513) ==31926== by 0x40B6F2E: xsltApplyTemplates (transform.c:4501) ==31926== by 0x40B466E: xsltApplyOneTemplateInt (transform.c:2525) ==31926== by 0x40A5671: xsltEvalGlobalVariable (variables.c:723) ==31926== by 0x40A5BED: xsltVariableLookup (variables.c:1363) ==31926== by 0x40A5CA1: xsltXPathVariableLookup (variables.c:1727) ==31926== by 0x414B19B: xmlXPathVariableLookup (xpath.c:4679) ==31926== by 0x414C3CE: xmlXPathCompOpEval (xpath.c:12960) The first backtrace (the invalid access) occurs during processing of the for-each, and the second (the free that has already occurred) looks like it occurred during the processing of the pass1/pass1-rtf variable. Furthermore, when run under valgrind, the bug does not reproduce. I also cannot reproduce the problem on an x86-64 machine running identical versions of xsltproc, libxml, libxslt, and libexslt.
The problem with the handling of node-set globals and the valgrind detected memory problems should now be fixed in CVS. I have checked your above test files on both x86 and x64. Please try the CVS code on your original data and confirm that all is now ok. If there is any further problem, please re-open this bug. Thanks for the informative and helpful report!