Fix crash with old libxml2
authorAlvaro Herrera <[email protected]>
Fri, 8 Mar 2019 22:13:25 +0000 (19:13 -0300)
committerAlvaro Herrera <[email protected]>
Fri, 8 Mar 2019 22:13:25 +0000 (19:13 -0300)
Certain libxml2 versions (such as the 2.7.6 commonly seen in older
distributions, but apparently only on x86_64) contain a bug that causes
xmlCopyNode, when called on a XML_DOCUMENT_NODE, to return a node that
xmlFreeNode crashes on.  Arrange to call xmlFreeDoc instead of
xmlFreeNode for those nodes.

Per buildfarm members lapwing and grison.

Author: Pavel Stehule, light editing by Álvaro.
Discussion: https://postgr.es/m/20190308024436[email protected]

src/backend/utils/adt/xml.c

index 28b3eaaa2010aa7a0afe4b80385b57c6bae1ed34..1116b773427c59a05555e6e5d51006008693cbc0 100644 (file)
@@ -3720,35 +3720,57 @@ xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
 
    if (cur->type != XML_ATTRIBUTE_NODE && cur->type != XML_TEXT_NODE)
    {
-       xmlBufferPtr buf;
-       xmlNodePtr  cur_copy;
+       void        (*nodefree) (xmlNodePtr) = NULL;
+       volatile xmlBufferPtr buf = NULL;
+       volatile xmlNodePtr cur_copy = NULL;
 
-       buf = xmlBufferCreate();
+       PG_TRY();
+       {
+           int         bytes;
 
-       /*
-        * The result of xmlNodeDump() won't contain namespace definitions
-        * from parent nodes, but xmlCopyNode() duplicates a node along with
-        * its required namespace definitions.
-        */
-       cur_copy = xmlCopyNode(cur, 1);
+           buf = xmlBufferCreate();
+           if (buf == NULL || xmlerrcxt->err_occurred)
+               xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+                           "could not allocate xmlBuffer");
 
-       if (cur_copy == NULL)
-           xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
-                       "could not copy node");
+           /*
+            * Produce a dump of the node that we can serialize.  xmlNodeDump
+            * does that, but the result of that function won't contain
+            * namespace definitions from ancestor nodes, so we first do a
+            * xmlCopyNode() which duplicates the node along with its required
+            * namespace definitions.
+            *
+            * Some old libxml2 versions such as 2.7.6 produce partially
+            * broken XML_DOCUMENT_NODE nodes (unset content field) when
+            * copying them.  xmlNodeDump of such a node works fine, but
+            * xmlFreeNode crashes; set us up to call xmlFreeDoc instead.
+            */
+           cur_copy = xmlCopyNode(cur, 1);
+           if (cur_copy == NULL || xmlerrcxt->err_occurred)
+               xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+                           "could not copy node");
+           nodefree = (cur_copy->type == XML_DOCUMENT_NODE) ?
+               (void (*) (xmlNodePtr)) xmlFreeDoc : xmlFreeNode;
+
+           bytes = xmlNodeDump(buf, NULL, cur_copy, 0, 1);
+           if (bytes == -1 || xmlerrcxt->err_occurred)
+               xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+                           "could not dump node");
 
-       PG_TRY();
-       {
-           xmlNodeDump(buf, NULL, cur_copy, 0, 1);
            result = xmlBuffer_to_xmltype(buf);
        }
        PG_CATCH();
        {
-           xmlFreeNode(cur_copy);
-           xmlBufferFree(buf);
+           if (nodefree)
+               nodefree(cur_copy);
+           if (buf)
+               xmlBufferFree(buf);
            PG_RE_THROW();
        }
        PG_END_TRY();
-       xmlFreeNode(cur_copy);
+
+       if (nodefree)
+           nodefree(cur_copy);
        xmlBufferFree(buf);
    }
    else