Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wToDy-000lV7-15 for pgsql-hackers@arkaria.postgresql.org; Sun, 31 May 2026 22:01:39 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wToDv-0084D9-1o for pgsql-hackers@arkaria.postgresql.org; Sun, 31 May 2026 22:01:35 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wToDv-0084D1-01 for pgsql-hackers@lists.postgresql.org; Sun, 31 May 2026 22:01:35 +0000 Received: from forward100a.mail.yandex.net ([178.154.239.83]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wToDq-00000000Tjr-0QFG for pgsql-hackers@lists.postgresql.org; Sun, 31 May 2026 22:01:34 +0000 Received: from mail-nwsmtp-smtp-production-main-54.vla.yp-c.yandex.net (mail-nwsmtp-smtp-production-main-54.vla.yp-c.yandex.net [IPv6:2a02:6b8:c17:2010:0:640:2180:0]) by forward100a.mail.yandex.net (Yandex) with ESMTPS id B0820C0165 for ; Mon, 01 Jun 2026 01:01:26 +0300 (MSK) Received: by mail-nwsmtp-smtp-production-main-54.vla.yp-c.yandex.net (smtp) with ESMTPSA id P1msvexg2qM0-iAplf9hV; Mon, 01 Jun 2026 01:01:26 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tantorlabs.com; s=mail; t=1780264886; bh=eYPE8XI4Zs4C9XDUcSfwgmk7rfiFofTzKlC+FzWB9lY=; h=Message-ID:Subject:To:From:Date; b=oakqnF2tcmFR2jtgUTOWuTj03fj+rzUDtDT0514uUjqa7HttNwRsJN+Ai/VX/qMvd izesu82JOTPDIyz9SlQbgR3h9TSgSu2y/VWlOTKHgueZ8S6M/r6rjQlWa1mfDRTrgg 74Eb3SHE+5JKcoM9kH5yVUH6t7GwbjSxsn4H3hKI= Authentication-Results: mail-nwsmtp-smtp-production-main-54.vla.yp-c.yandex.net; dkim=pass header.i=@tantorlabs.com Date: Mon, 1 Jun 2026 01:01:24 +0300 From: Andrey Chernyy To: pgsql-hackers@lists.postgresql.org Subject: [PATCH] Fix libxml leaks in contrib/xml2 XPath functions Message-ID: <20260601010124.5edf9a20@andrnote> X-Mailer: Claws Mail 4.4.0 (GTK 3.24.52; x86_64-redhat-linux-gnu) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="MP_/T7YUrG7W2jPQOBsjEYwqj9B" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --MP_/T7YUrG7W2jPQOBsjEYwqj9B Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Content-Disposition: inline Hi, While reviewing contrib/xml2 I found two successful-path libxml leaks in XPath functions. The attached series is: 0001 Fix libxml string leak in contrib/xml2 xpath_list 0002 Fix libxml leaks in contrib/xml2 xpath_table In xpath_list(), the plain separator path passes the result of xmlXPathCastNodeToString() directly to xmlBufferWriteCHAR(). xmlXPathCastNodeToString() returns a libxml-allocated xmlChar * that has to be released with xmlFree(), while xmlBufferWriteCHAR() copies the string rather than taking ownership. In xpath_table(), xmlXPathCompiledEval() returns an xmlXPathObjectPtr that has to be released with xmlXPathFreeObject(). The function also stores libxml-allocated xmlChar strings in the values array before calling BuildTupleFromCStrings(). BuildTupleFromCStrings() consumes those strings by converting/copying them into the result tuple, so the temporary libxml strings can be released after the tuple is built. The second patch frees the XPath result object after each evaluation, frees per-column string values after BuildTupleFromCStrings() has consumed them, and tracks the current libxml allocations across the existing PG_TRY block so they are also released on error. The attached manual repro scripts exercise the two paths separately. They are Linux-specific because they sample VmRSS from /proc//status using pg_read_file(), so they should be run as a superuser or a role allowed to read server files. On unpatched origin/master, selected NOTICE lines from the repro scripts show steady backend RSS growth: postgres=# \i ./xml2-xpath-list-leak-repro.sql psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=1, total_kb=38616, diff_kb=17220 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=2, total_kb=40732, diff_kb=2104 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=3, total_kb=42720, diff_kb=1988 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=4, total_kb=44644, diff_kb=1924 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=5, total_kb=46440, diff_kb=1796 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=6, total_kb=47968, diff_kb=1528 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=7, total_kb=50892, diff_kb=2924 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=8, total_kb=52712, diff_kb=1820 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=9, total_kb=54276, diff_kb=1564 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=10, total_kb=55328, diff_kb=1052 postgres=# \i ./xml2-xpath-table-leak-repro.sql psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=1, total_kb=26452, diff_kb=5136 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=2, total_kb=27772, diff_kb=1312 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=3, total_kb=29068, diff_kb=1296 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=4, total_kb=30368, diff_kb=1300 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=5, total_kb=31668, diff_kb=1300 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=6, total_kb=32968, diff_kb=1300 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=7, total_kb=34260, diff_kb=1292 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=8, total_kb=35568, diff_kb=1308 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=9, total_kb=36876, diff_kb=1308 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=10, total_kb=38168, diff_kb=1292 With both patches applied, the same scripts plateau after the initial allocations: postgres=# \i ./xml2-xpath-list-leak-repro.sql psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=1, total_kb=24480, diff_kb=3020 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=2, total_kb=24532, diff_kb=44 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=3, total_kb=24580, diff_kb=48 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=4, total_kb=24580, diff_kb=0 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=5, total_kb=24624, diff_kb=44 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=6, total_kb=24580, diff_kb=-44 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=7, total_kb=24580, diff_kb=0 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=8, total_kb=24580, diff_kb=0 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=9, total_kb=24580, diff_kb=0 psql:xml2-xpath-list-leak-repro.sql:38: NOTICE: xpath_list i=10, total_kb=24580, diff_kb=0 postgres=# \i ./xml2-xpath-table-leak-repro.sql psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=1, total_kb=26572, diff_kb=188 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=2, total_kb=26600, diff_kb=28 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=3, total_kb=26600, diff_kb=0 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=4, total_kb=26600, diff_kb=0 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=5, total_kb=26600, diff_kb=0 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=6, total_kb=26600, diff_kb=0 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=7, total_kb=26600, diff_kb=0 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=8, total_kb=26600, diff_kb=0 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=9, total_kb=26600, diff_kb=0 psql:xml2-xpath-table-leak-repro.sql:40: NOTICE: xpath_table i=10, total_kb=26600, diff_kb=0 contrib/xml2 regression tests still pass: make -C contrib/xml2 check # All 1 tests passed. I also checked that the two patches apply cleanly to current origin/master with git am. This is related to, but not fixed by, the recent xml2/libxml error-handling work from BUG #18943 / commit 732061150b0. That patch improved cleanup and OOM handling around libxml calls, while these cases are successful execution path leaks. These are long-standing leaks and look like candidates for back-patching to all supported branches. -- Andrey Chernyy --MP_/T7YUrG7W2jPQOBsjEYwqj9B Content-Type: application/sql Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename=xml2-xpath-list-leak-repro.sql XHNldCBPTl9FUlJPUl9TVE9QIG9uCgpDUkVBVEUgRVhURU5TSU9OIElGIE5PVCBFWElTVFMgeG1s MjsKCkNSRUFURSBPUiBSRVBMQUNFIEZVTkNUSU9OIHBnX3RlbXAudm1yc3Nfa2IoKQpSRVRVUk5T IGludGVnZXIKTEFOR1VBR0Ugc3FsCkFTICQkCiAgU0VMRUNUICgocmVnZXhwX21hdGNoKAogICAg ICAgICAgICAgcGdfcmVhZF9maWxlKCcvcHJvYy8nIHx8IHBnX2JhY2tlbmRfcGlkKCkgfHwgJy9z dGF0dXMnKSwKICAgICAgICAgICAgICdWbVJTUzpbWzpzcGFjZTpdXSsoWzAtOV0rKVtbOnNwYWNl Ol1dK2tCJwogICAgICAgICApKVsxXSk6OmludGVnZXI7CiQkOwoKRFJPUCBUQUJMRSBJRiBFWElT VFMgcGdfdGVtcC54bWwyX2xlYWtfbGlzdF9kb2M7CgpDUkVBVEUgVEVNUCBUQUJMRSB4bWwyX2xl YWtfbGlzdF9kb2MgQVMKU0VMRUNUICc8ZG9jPicgfHwKICAgICAgIHN0cmluZ19hZ2coJzxuPicg fHwgcmVwZWF0KG1kNShnOjp0ZXh0KSwgMTI4KSB8fCAnPC9uPicsICcnKSB8fAogICAgICAgJzwv ZG9jPicgQVMgZG9jCkZST00gZ2VuZXJhdGVfc2VyaWVzKDEsIDUxMikgQVMgZzsKCkRPICQkCkRF Q0xBUkUKICAgIGJlZm9yZV9yc3MgYmlnaW50OwogICAgYWZ0ZXJfcnNzICBiaWdpbnQ7CiAgICBk aWZmICAgICAgIGJpZ2ludDsKQkVHSU4KICAgIEZPUiBpIElOIDEuLjEwIExPT1AKICAgICAgICBi ZWZvcmVfcnNzIDo9IHBnX3RlbXAudm1yc3Nfa2IoKTsKICAgICAgICBQRVJGT1JNIGxlbmd0aCh4 cGF0aF9saXN0KGRvYywgJy8vbi90ZXh0KCknLCAnLCcpKQogICAgICAgIEZST00geG1sMl9sZWFr X2xpc3RfZG9jOwogICAgICAgIGFmdGVyX3JzcyA6PSBwZ190ZW1wLnZtcnNzX2tiKCk7CiAgICAg ICAgZGlmZiA6PSBhZnRlcl9yc3MgLSBiZWZvcmVfcnNzOwogICAgICAgIFJBSVNFIE5PVElDRSAn eHBhdGhfbGlzdCBpPSUsIHRvdGFsX2tiPSUsIGRpZmZfa2I9JScsCiAgICAgICAgICAgICAgICAg ICAgIGksIGFmdGVyX3JzcywgZGlmZjsKICAgIEVORCBMT09QOwpFTkQgJCQ7Cg== --MP_/T7YUrG7W2jPQOBsjEYwqj9B Content-Type: application/sql Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename=xml2-xpath-table-leak-repro.sql XHNldCBPTl9FUlJPUl9TVE9QIG9uCgpDUkVBVEUgRVhURU5TSU9OIElGIE5PVCBFWElTVFMgeG1s MjsKCkNSRUFURSBPUiBSRVBMQUNFIEZVTkNUSU9OIHBnX3RlbXAudm1yc3Nfa2IoKQpSRVRVUk5T IGludGVnZXIKTEFOR1VBR0Ugc3FsCkFTICQkCiAgU0VMRUNUICgocmVnZXhwX21hdGNoKAogICAg ICAgICAgICAgcGdfcmVhZF9maWxlKCcvcHJvYy8nIHx8IHBnX2JhY2tlbmRfcGlkKCkgfHwgJy9z dGF0dXMnKSwKICAgICAgICAgICAgICdWbVJTUzpbWzpzcGFjZTpdXSsoWzAtOV0rKVtbOnNwYWNl Ol1dK2tCJwogICAgICAgICApKVsxXSk6OmludGVnZXI7CiQkOwoKRFJPUCBUQUJMRSBJRiBFWElT VFMgcGdfdGVtcC54bWwyX2xlYWtfdGFibGVfZG9jOwoKQ1JFQVRFIFRFTVAgVEFCTEUgeG1sMl9s ZWFrX3RhYmxlX2RvYyBBUwpTRUxFQ1QgMSBBUyBpZCwKICAgICAgICc8ZG9jPicgfHwKICAgICAg IHN0cmluZ19hZ2coJzxuPicgfHwgcmVwZWF0KG1kNShnOjp0ZXh0KSwgNjQpIHx8ICc8L24+Jywg JycpIHx8CiAgICAgICAnPC9kb2M+JyBBUyBkb2MKRlJPTSBnZW5lcmF0ZV9zZXJpZXMoMSwgMjU2 KSBBUyBnOwoKRE8gJCQKREVDTEFSRQogICAgYmVmb3JlX3JzcyBiaWdpbnQ7CiAgICBhZnRlcl9y c3MgIGJpZ2ludDsKICAgIGRpZmYgICAgICAgYmlnaW50OwpCRUdJTgogICAgRk9SIGkgSU4gMS4u MTAgTE9PUAogICAgICAgIGJlZm9yZV9yc3MgOj0gcGdfdGVtcC52bXJzc19rYigpOwogICAgICAg IFBFUkZPUk0gY291bnQoKikKICAgICAgICBGUk9NIHhwYXRoX3RhYmxlKCdpZCcsICdkb2MnLCAn eG1sMl9sZWFrX3RhYmxlX2RvYycsCiAgICAgICAgICAgICAgICAgICAgICAgICAnL2RvYy9uL3Rl eHQoKScsICd0cnVlJykgQVMgdChpZCBpbnQsIHZhbHVlIHRleHQpOwogICAgICAgIGFmdGVyX3Jz cyA6PSBwZ190ZW1wLnZtcnNzX2tiKCk7CiAgICAgICAgZGlmZiA6PSBhZnRlcl9yc3MgLSBiZWZv cmVfcnNzOwogICAgICAgIFJBSVNFIE5PVElDRSAneHBhdGhfdGFibGUgaT0lLCB0b3RhbF9rYj0l LCBkaWZmX2tiPSUnLAogICAgICAgICAgICAgICAgICAgICBpLCBhZnRlcl9yc3MsIGRpZmY7CiAg ICBFTkQgTE9PUDsKRU5EICQkOwo= --MP_/T7YUrG7W2jPQOBsjEYwqj9B Content-Type: text/x-patch Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=0001-Fix-libxml-string-leak-in-contrib-xml2-xpath_list.patch From fadb35b4b9f24aa00d3604d2885ad009791b3f3b Mon Sep 17 00:00:00 2001 From: Andrey Chernyy Date: Mon, 25 May 2026 22:12:02 +0300 Subject: [PATCH 1/2] Fix libxml string leak in contrib/xml2 xpath_list xmlXPathCastNodeToString() returns a libxml-allocated xmlChar *, but pgxmlNodeSetToText() passed it directly to xmlBufferWriteCHAR() in the plain separator path. Since xmlBufferWriteCHAR() copies the string rather than taking ownership, successful xpath_list() calls leaked one string per emitted node. Store the cast result locally and free it with xmlFree() after writing it to the buffer. --- contrib/xml2/xpath.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c index 7bf477e0c3f..94819961787 100644 --- a/contrib/xml2/xpath.c +++ b/contrib/xml2/xpath.c @@ -147,6 +147,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, { volatile xmlBufferPtr buf = NULL; xmlChar *volatile result = NULL; + xmlChar *volatile str = NULL; PgXmlErrorContext *xmlerrcxt; /* spin up some error handling */ @@ -172,8 +173,14 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, { if (plainsep != NULL) { - xmlBufferWriteCHAR(buf, - xmlXPathCastNodeToString(nodeset->nodeTab[i])); + str = xmlXPathCastNodeToString(nodeset->nodeTab[i]); + if (str == NULL || pg_xml_error_occurred(xmlerrcxt)) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, + "could not allocate node text"); + + xmlBufferWriteCHAR(buf, str); + xmlFree(str); + str = NULL; /* If this isn't the last entry, write the plain sep. */ if (i < (nodeset->nodeNr) - 1) @@ -216,6 +223,8 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, } PG_CATCH(); { + if (str) + xmlFree(str); if (buf) xmlBufferFree(buf); -- 2.54.0 --MP_/T7YUrG7W2jPQOBsjEYwqj9B Content-Type: text/x-patch Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=0002-Fix-libxml-leaks-in-contrib-xml2-xpath_table.patch From 3430cf722811dd256f205c3fbf3a8e16431913a0 Mon Sep 17 00:00:00 2001 From: Andrey Chernyy Date: Tue, 26 May 2026 00:11:06 +0300 Subject: [PATCH 2/2] Fix libxml leaks in contrib/xml2 xpath_table xpath_table() did not release libxml objects allocated while evaluating XPath expressions. xmlXPathCompiledEval() returns an xmlXPathObjectPtr that must be freed, and string results copied into the tuple input array must be freed after BuildTupleFromCStrings() has consumed them. Track the current libxml objects across the existing PG_TRY block so they are also released on error. --- contrib/xml2/xpath.c | 47 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c index 94819961787..ac140a640e0 100644 --- a/contrib/xml2/xpath.c +++ b/contrib/xml2/xpath.c @@ -643,6 +643,10 @@ xpath_table(PG_FUNCTION_ARGS) StringInfoData query_buf; PgXmlErrorContext *xmlerrcxt; volatile xmlDocPtr doctree = NULL; + xmlXPathContextPtr volatile ctxt = NULL; + xmlXPathObjectPtr volatile res = NULL; + xmlXPathCompExprPtr volatile comppath = NULL; + xmlChar *volatile resstr = NULL; InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC); @@ -662,7 +666,7 @@ xpath_table(PG_FUNCTION_ARGS) attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc); - values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *)); + values = (char **) palloc0(rsinfo->setDesc->natts * sizeof(char *)); xpaths = (xmlChar **) palloc(rsinfo->setDesc->natts * sizeof(xmlChar *)); /* @@ -732,10 +736,6 @@ xpath_table(PG_FUNCTION_ARGS) { char *pkey; char *xmldoc; - xmlXPathContextPtr ctxt; - xmlXPathObjectPtr res; - xmlChar *resstr; - xmlXPathCompExprPtr comppath; HeapTuple ret_tuple; /* Extract the row data as C Strings */ @@ -780,6 +780,11 @@ xpath_table(PG_FUNCTION_ARGS) had_values = false; for (j = 0; j < numpaths; j++) { + ctxt = NULL; + res = NULL; + comppath = NULL; + resstr = NULL; + ctxt = xmlXPathNewContext(doctree); if (ctxt == NULL || pg_xml_error_occurred(xmlerrcxt)) xml_ereport(xmlerrcxt, @@ -798,6 +803,7 @@ xpath_table(PG_FUNCTION_ARGS) /* Now evaluate the path expression. */ res = xmlXPathCompiledEval(comppath, ctxt); xmlXPathFreeCompExpr(comppath); + comppath = NULL; if (res != NULL) { @@ -842,8 +848,16 @@ xpath_table(PG_FUNCTION_ARGS) * result tuple. */ values[j + 1] = (char *) resstr; + resstr = NULL; + } + + if (res != NULL) + { + xmlXPathFreeObject(res); + res = NULL; } xmlXPathFreeContext(ctxt); + ctxt = NULL; } /* Now add the tuple to the output, if there is one. */ @@ -854,6 +868,16 @@ xpath_table(PG_FUNCTION_ARGS) heap_freetuple(ret_tuple); } + /* BuildTupleFromCStrings() has copied the values. */ + for (j = 1; j < rsinfo->setDesc->natts; j++) + { + if (values[j] != NULL) + { + xmlFree((xmlChar *) values[j]); + values[j] = NULL; + } + } + rownr++; } while (had_values); } @@ -870,6 +894,19 @@ xpath_table(PG_FUNCTION_ARGS) } PG_CATCH(); { + if (resstr != NULL) + xmlFree(resstr); + for (j = 1; j < rsinfo->setDesc->natts; j++) + { + if (values[j] != NULL) + xmlFree((xmlChar *) values[j]); + } + if (res != NULL) + xmlXPathFreeObject(res); + if (comppath != NULL) + xmlXPathFreeCompExpr(comppath); + if (ctxt != NULL) + xmlXPathFreeContext(ctxt); if (doctree != NULL) xmlFreeDoc(doctree); -- 2.54.0 --MP_/T7YUrG7W2jPQOBsjEYwqj9B--