From 71e1e8af5ee46dad1b57bb96cfbf1c3ad21fbd7b Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Fri, 4 Jul 2025 14:28:26 +0200
Subject: [PATCH] schematron: Fix memory safety issues in
 xmlSchematronReportOutput

Fix use-after-free (CVE-2025-49794) and type confusion (CVE-2025-49796)
in xmlSchematronReportOutput.

Fixes #931.
Fixes #933.

Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/libxml2/-/commit/71e1e8af5ee46dad1b57bb96cfbf1c3ad21fbd7b]
CVE: CVE-2025-49794 CVE-2025-49796
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
 result/schematron/cve-2025-49794_0.err |  2 ++
 result/schematron/cve-2025-49796_0.err |  2 ++
 schematron.c                           | 37 +++++++++++++++-----------
 test/schematron/cve-2025-49794.sct     | 10 +++++++
 test/schematron/cve-2025-49794_0.xml   |  6 +++++
 test/schematron/cve-2025-49796.sct     |  9 +++++++
 test/schematron/cve-2025-49796_0.xml   |  3 +++
 7 files changed, 53 insertions(+), 16 deletions(-)
 create mode 100644 result/schematron/cve-2025-49794_0.err
 create mode 100644 result/schematron/cve-2025-49796_0.err
 create mode 100644 test/schematron/cve-2025-49794.sct
 create mode 100644 test/schematron/cve-2025-49794_0.xml
 create mode 100644 test/schematron/cve-2025-49796.sct
 create mode 100644 test/schematron/cve-2025-49796_0.xml

diff --git a/result/schematron/cve-2025-49794_0.err b/result/schematron/cve-2025-49794_0.err
new file mode 100644
index 0000000..5775231
--- /dev/null
+++ b/result/schematron/cve-2025-49794_0.err
@@ -0,0 +1,2 @@
+./test/schematron/cve-2025-49794_0.xml:2: element boo0: schematron error : /librar0/boo0 line 2:  
+./test/schematron/cve-2025-49794_0.xml fails to validate
diff --git a/result/schematron/cve-2025-49796_0.err b/result/schematron/cve-2025-49796_0.err
new file mode 100644
index 0000000..bf875ee
--- /dev/null
+++ b/result/schematron/cve-2025-49796_0.err
@@ -0,0 +1,2 @@
+./test/schematron/cve-2025-49796_0.xml:2: element boo0: schematron error : /librar0/boo0 line 2:  
+./test/schematron/cve-2025-49796_0.xml fails to validate
diff --git a/schematron.c b/schematron.c
index ddbb069..5ebca64 100644
--- a/schematron.c
+++ b/schematron.c
@@ -1239,27 +1239,16 @@ exit:
  *									*
  ************************************************************************/
 
-static xmlNodePtr
+static xmlXPathObjectPtr
 xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,
                      xmlNodePtr cur, const xmlChar *xpath) {
-    xmlNodePtr node = NULL;
-    xmlXPathObjectPtr ret;
 
     if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))
         return(NULL);
 
     ctxt->xctxt->doc = cur->doc;
     ctxt->xctxt->node = cur;
-    ret = xmlXPathEval(xpath, ctxt->xctxt);
-    if (ret == NULL)
-        return(NULL);
-
-    if ((ret->type == XPATH_NODESET) &&
-        (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))
-	node = ret->nodesetval->nodeTab[0];
-
-    xmlXPathFreeObject(ret);
-    return(node);
+    return(xmlXPathEval(xpath, ctxt->xctxt));
 }
 
 /**
@@ -1304,18 +1293,26 @@ xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
 	    (child->type == XML_CDATA_SECTION_NODE))
 	    ret = xmlStrcat(ret, child->content);
 	else if (IS_SCHEMATRON(child, "name")) {
+            xmlXPathObject *obj = NULL;
 	    xmlChar *path;
 
 	    path = xmlGetNoNsProp(child, BAD_CAST "path");
 
             node = cur;
 	    if (path != NULL) {
-	        node = xmlSchematronGetNode(ctxt, cur, path);
-		if (node == NULL)
-		    node = cur;
+                obj = xmlSchematronGetNode(ctxt, cur, path);
+                if ((obj != NULL) &&
+                    (obj->type == XPATH_NODESET) &&
+                    (obj->nodesetval != NULL) &&
+                    (obj->nodesetval->nodeNr > 0))
+                    node = obj->nodesetval->nodeTab[0];
 		xmlFree(path);
 	    }
 
+	     switch (node->type) {
+                case XML_ELEMENT_NODE:
+                case XML_ATTRIBUTE_NODE:
+
 	    if ((node->ns == NULL) || (node->ns->prefix == NULL))
 	        ret = xmlStrcat(ret, node->name);
 	    else {
@@ -1323,6 +1320,14 @@ xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
 	        ret = xmlStrcat(ret, BAD_CAST ":");
 	        ret = xmlStrcat(ret, node->name);
 	    }
+	    break;
+
+		/* TODO: handle other node types */
+		default:
+	    break;
+            }
+
+            xmlXPathFreeObject(obj);
 	} else {
 	    child = child->next;
 	    continue;
diff --git a/test/schematron/cve-2025-49794.sct b/test/schematron/cve-2025-49794.sct
new file mode 100644
index 0000000..7fc9ee3
--- /dev/null
+++ b/test/schematron/cve-2025-49794.sct
@@ -0,0 +1,10 @@
+<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
+    <sch:pattern id="">
+        <sch:rule context="boo0">
+            <sch:report test="not(0)">
+                <sch:name path="&#9;e|namespace::*|e"/>
+            </sch:report>
+            <sch:report test="0"></sch:report>
+        </sch:rule>
+    </sch:pattern>
+</sch:schema>
diff --git a/test/schematron/cve-2025-49794_0.xml b/test/schematron/cve-2025-49794_0.xml
new file mode 100644
index 0000000..debc64b
--- /dev/null
+++ b/test/schematron/cve-2025-49794_0.xml
@@ -0,0 +1,6 @@
+<librar0>
+    <boo0 t="">
+        <author></author>
+    </boo0>
+    <ins></ins>
+</librar0>
diff --git a/test/schematron/cve-2025-49796.sct b/test/schematron/cve-2025-49796.sct
new file mode 100644
index 0000000..e9702d7
--- /dev/null
+++ b/test/schematron/cve-2025-49796.sct
@@ -0,0 +1,9 @@
+<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
+    <sch:pattern id="">
+        <sch:rule context="boo0">
+            <sch:report test="not(0)">
+                <sch:name path="/"/>
+            </sch:report>
+        </sch:rule>
+    </sch:pattern>
+</sch:schema>
diff --git a/test/schematron/cve-2025-49796_0.xml b/test/schematron/cve-2025-49796_0.xml
new file mode 100644
index 0000000..be33c4e
--- /dev/null
+++ b/test/schematron/cve-2025-49796_0.xml
@@ -0,0 +1,3 @@
+<librar0>
+    <boo0/>
+</librar0>
-- 
2.49.0

