Fix low-probability memory leak in XMLSERIALIZE(... INDENT). master github/master
authorTom Lane
Tue, 8 Jul 2025 16:50:19 +0000 (12:50 -0400)
committerTom Lane
Tue, 8 Jul 2025 16:50:33 +0000 (12:50 -0400)
xmltotext_with_options() did not consider the possibility that
pg_xml_init() could fail --- most likely due to OOM.  If that
happened, the already-parsed xmlDoc structure would be leaked.
Oversight in commit 483bdb2af.

Bug: #18981
Author: Dmitry Kovalenko 
Reviewed-by: Tom Lane
Discussion: https://postgr.es/m/18981-9bc3c80f107ae925@postgresql.org
Backpatch-through: 16

src/backend/utils/adt/xml.c

index 2bd39b6ac4b09861f4a17f7a10a7c4bbb822454a..f7b731825fca0d80073d07ae69a5ac3c4fc4d039 100644 (file)
@@ -532,7 +532,7 @@ xmltext(PG_FUNCTION_ARGS)
    volatile xmlChar *xmlbuf = NULL;
    PgXmlErrorContext *xmlerrcxt;
 
-   /* Otherwise, we gotta spin up some error handling. */
+   /* First we gotta spin up some error handling. */
    xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
 
    PG_TRY();
@@ -685,7 +685,7 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
    volatile xmlBufferPtr buf = NULL;
    volatile xmlSaveCtxtPtr ctxt = NULL;
    ErrorSaveContext escontext = {T_ErrorSaveContext};
-   PgXmlErrorContext *xmlerrcxt;
+   PgXmlErrorContext *volatile xmlerrcxt = NULL;
 #endif
 
    if (xmloption_arg != XMLOPTION_DOCUMENT && !indent)
@@ -726,13 +726,18 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
        return (text *) data;
    }
 
-   /* Otherwise, we gotta spin up some error handling. */
-   xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
-
+   /*
+    * Otherwise, we gotta spin up some error handling.  Unlike most other
+    * routines in this module, we already have a libxml "doc" structure to
+    * free, so we need to call pg_xml_init() inside the PG_TRY and be
+    * prepared for it to fail (typically due to palloc OOM).
+    */
    PG_TRY();
    {
        size_t      decl_len = 0;
 
+       xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
+
        /* The serialized data will go into this buffer. */
        buf = xmlBufferCreate();
 
@@ -863,10 +868,10 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
            xmlSaveClose(ctxt);
        if (buf)
            xmlBufferFree(buf);
-       if (doc)
-           xmlFreeDoc(doc);
+       xmlFreeDoc(doc);
 
-       pg_xml_done(xmlerrcxt, true);
+       if (xmlerrcxt)
+           pg_xml_done(xmlerrcxt, true);
 
        PG_RE_THROW();
    }