]> granicus.if.org Git - postgresql/commitdiff
Fix crash with old libxml2
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Fri, 8 Mar 2019 22:13:25 +0000 (19:13 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
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.GA2374@alvherre.pgsql

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