After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 777432 - SEGV when extensions add text nodes with xmlAddChild
SEGV when extensions add text nodes with xmlAddChild
Status: RESOLVED FIXED
Product: libxslt
Classification: Platform
Component: general
1.1.28
Other Linux
: Normal normal
: ---
Assigned To: Daniel Veillard
libxml QA maintainers
Depends on:
Blocks:
 
 
Reported: 2017-01-18 08:56 UTC by manfred.benesch
Modified: 2017-02-10 13:19 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description manfred.benesch 2017-01-18 08:56:54 UTC
Reproducable on different machines (debian-based) with version 1.1.28, 1.1.19 and git master.

  • #0 __GI___libc_free
    at malloc.c line 2945
  • #1 xmlXPathFreeNodeSet__internal_alias
    at ../../xpath.c line 4188
  • #2 xmlXPathFreeObject__internal_alias
    at ../../xpath.c line 5492
  • #3 ??
    from /usr/lib/x86_64-linux-gnu/libxslt.so.1
  • #4 xsltForEach
    from /usr/lib/x86_64-linux-gnu/libxslt.so.1
  • #5 ??
    from /usr/lib/x86_64-linux-gnu/libxslt.so.1
  • #6 ??
    from /usr/lib/x86_64-linux-gnu/libxslt.so.1
  • #7 xsltProcessOneNode
    from /usr/lib/x86_64-linux-gnu/libxslt.so.1
  • #8 ??
    from /usr/lib/x86_64-linux-gnu/libxslt.so.1
  • #9 xsltProcessOneNode
    from /usr/lib/x86_64-linux-gnu/libxslt.so.1
  • #10 ??
    from /usr/lib/x86_64-linux-gnu/libxslt.so.1
  • #11 main
    at test.c line 69

Reproducable with following minimal example:

#include <string.h>
#include <libintl.h>

#include <libxslt/xslt.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>
#include <libxslt/extensions.h>

#define UNUSED __attribute__((__unused__))

#define XSLT_MODUL_URI "http://test.uri"

static void XsltGettext(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst, UNUSED xsltStylePreCompPtr comp)
{
    xmlChar *text;

    if(ctxt == NULL||node==NULL||inst==NULL||ctxt->insert==NULL) {
        xsltGenericError(xsltGenericErrorContext, "error\n");
        return;
    }

    text = BAD_CAST gettext("text");
    node = xmlNewText(text);
    xmlAddChild(ctxt->insert, node);
}

static void* xsltInitFct(xsltTransformContextPtr ctxt, UNUSED const xmlChar *URI)
{
    xsltRegisterExtElement(ctxt, (const xmlChar*)"gettext", (const xmlChar*)XSLT_MODUL_URI, (xsltTransformFunction)XsltGettext);

    return NULL;
}

static void xsltShutdownFct(UNUSED xsltTransformContextPtr ctxt, UNUSED const xmlChar *URI, UNUSED void *data)
{}

int main(UNUSED int argc, UNUSED char **argv)
{
    xsltStylesheetPtr cur;
    const char *params[1] = {NULL};
    xmlDocPtr xslDoc = NULL, xmlDoc = NULL, htmlDoc = NULL;

    const char *xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
<test>\
<sub><s>startdate starttime</s><e>enddate endtime</e></sub>\
</test>";
    const char *xsl = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\" xmlns:test=\"http://test.uri\" extension-element-prefixes=\"test\">\
  <xsl:template match=\"test\">\
    <xsl:for-each select=\"sub\">\
      <a><xsl:value-of select=\"position()\"/>. <test:gettext>sub</test:gettext> : <xsl:value-of select=\"s\"/><test:gettext> till </test:gettext><xsl:value-of select=\"e\"/></a>\
    </xsl:for-each>\
  </xsl:template>\
</xsl:stylesheet>";

    xmlSubstituteEntitiesDefault(1);
    xmlLoadExtDtdDefaultValue = 1;

    xsltRegisterExtModule((const xmlChar*)XSLT_MODUL_URI, xsltInitFct, xsltShutdownFct);

    xmlDoc = xmlReadMemory(xml, strlen(xml), "test", NULL, XML_PARSE_NOBLANKS);
    if(xmlDoc == NULL)
        return 1;
    xslDoc = xmlReadMemory(xsl, strlen(xsl), "stylesheet", NULL, XML_PARSE_NOBLANKS);
    if(xslDoc == NULL)
        return 1;

    cur = xsltParseStylesheetDoc(xslDoc);
    htmlDoc = xsltApplyStylesheet(cur, xmlDoc, params);
    unsigned char *buf = NULL;
    int len = 0;
    xmlDocDumpFormatMemory(htmlDoc, &buf, &len, 1);
    printf("%s\n", buf);

    xsltFreeStylesheet(cur);
    xmlFreeDoc(xmlDoc);
    xmlFreeDoc(htmlDoc);
    xsltCleanupGlobals();
    xmlCleanupParser();

    xmlMemoryDump();
    return 0;
}
Comment 1 Nick Wellnhofer 2017-01-18 13:07:52 UTC
libxslt uses an internal optimization when adding text to result elements that breaks if xmlAddChild is used. Extensions should use xsltCopyTextString instead:

http://xmlsoft.org/XSLT/html/libxslt-transform.html#xsltCopyTextString

This behavior is completely unexpected and libxslt really should allow to append text nodes with xmlAddChild, so I'm leaving this bug open.
Comment 2 manfred.benesch 2017-01-19 06:36:25 UTC
(In reply to Nick Wellnhofer from comment #1)
> libxslt uses an internal optimization when adding text to result elements
> that breaks if xmlAddChild is used. Extensions should use xsltCopyTextString
> instead:
> 
> http://xmlsoft.org/XSLT/html/libxslt-transform.html#xsltCopyTextString
> 
> This behavior is completely unexpected and libxslt really should allow to
> append text nodes with xmlAddChild, so I'm leaving this bug open.

Thanks a lot!
Using xsltCopyTextString eliminate all problems.
It would be useful to add that hint also on
http://xmlsoft.org/XSLT/extensions.html#Example

That page suggests to use AddChild for extensions.
Comment 3 Nick Wellnhofer 2017-02-10 13:19:27 UTC
Fixed with the following commit:

https://git.gnome.org/browse/libxslt/commit/?id=ec547b2c12dc12e1277bc527fdc6b37a2feb1b43

Extensions can now append text with xmlAddChild safely. The xsltCopyTextString optimization is disabled for extension elements. I can't see how to make it work safely for code that might call xmlAddChild.