• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1From 71e1e8af5ee46dad1b57bb96cfbf1c3ad21fbd7b Mon Sep 17 00:00:00 2001
2From: Nick Wellnhofer <wellnhofer@aevum.de>
3Date: Fri, 4 Jul 2025 14:28:26 +0200
4Subject: [PATCH] schematron: Fix memory safety issues in
5 xmlSchematronReportOutput
6
7Fix use-after-free (CVE-2025-49794) and type confusion (CVE-2025-49796)
8in xmlSchematronReportOutput.
9
10Fixes #931.
11Fixes #933.
12---
13 result/schematron/cve-2025-49794_0.err |  2 ++
14 result/schematron/cve-2025-49796_0.err |  2 ++
15 schematron.c                           | 49 ++++++++++++++------------
16 test/schematron/cve-2025-49794.sct     | 10 ++++++
17 test/schematron/cve-2025-49794_0.xml   |  6 ++++
18 test/schematron/cve-2025-49796.sct     |  9 +++++
19 test/schematron/cve-2025-49796_0.xml   |  3 ++
20 7 files changed, 58 insertions(+), 23 deletions(-)
21 create mode 100644 result/schematron/cve-2025-49794_0.err
22 create mode 100644 result/schematron/cve-2025-49796_0.err
23 create mode 100644 test/schematron/cve-2025-49794.sct
24 create mode 100644 test/schematron/cve-2025-49794_0.xml
25 create mode 100644 test/schematron/cve-2025-49796.sct
26 create mode 100644 test/schematron/cve-2025-49796_0.xml
27
28diff --git a/result/schematron/cve-2025-49794_0.err b/result/schematron/cve-2025-49794_0.err
29new file mode 100644
30index 000000000..57752310e
31--- /dev/null
32+++ b/result/schematron/cve-2025-49794_0.err
33@@ -0,0 +1,2 @@
34+./test/schematron/cve-2025-49794_0.xml:2: element boo0: schematron error : /librar0/boo0 line 2:
35+./test/schematron/cve-2025-49794_0.xml fails to validate
36diff --git a/result/schematron/cve-2025-49796_0.err b/result/schematron/cve-2025-49796_0.err
37new file mode 100644
38index 000000000..bf875ee0c
39--- /dev/null
40+++ b/result/schematron/cve-2025-49796_0.err
41@@ -0,0 +1,2 @@
42+./test/schematron/cve-2025-49796_0.xml:2: element boo0: schematron error : /librar0/boo0 line 2:
43+./test/schematron/cve-2025-49796_0.xml fails to validate
44diff --git a/schematron.c b/schematron.c
45index 85b462827..0fd374617 100644
46--- a/schematron.c
47+++ b/schematron.c
48@@ -1364,27 +1364,15 @@ exit:
49  *                                                                      *
50  ************************************************************************/
51
52-static xmlNodePtr
53+static xmlXPathObjectPtr
54 xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,
55                      xmlNodePtr cur, const xmlChar *xpath) {
56-    xmlNodePtr node = NULL;
57-    xmlXPathObjectPtr ret;
58-
59     if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))
60         return(NULL);
61
62     ctxt->xctxt->doc = cur->doc;
63     ctxt->xctxt->node = cur;
64-    ret = xmlXPathEval(xpath, ctxt->xctxt);
65-    if (ret == NULL)
66-        return(NULL);
67-
68-    if ((ret->type == XPATH_NODESET) &&
69-        (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))
70-        node = ret->nodesetval->nodeTab[0];
71-
72-    xmlXPathFreeObject(ret);
73-    return(node);
74+    return(xmlXPathEval(xpath, ctxt->xctxt));
75 }
76
77 /**
78@@ -1427,25 +1415,40 @@ xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
79             (child->type == XML_CDATA_SECTION_NODE))
80             ret = xmlStrcat(ret, child->content);
81         else if (IS_SCHEMATRON(child, "name")) {
82+            xmlXPathObject *obj = NULL;
83             xmlChar *path;
84
85             path = xmlGetNoNsProp(child, BAD_CAST "path");
86
87             node = cur;
88             if (path != NULL) {
89-                node = xmlSchematronGetNode(ctxt, cur, path);
90-                if (node == NULL)
91-                    node = cur;
92+                obj = xmlSchematronGetNode(ctxt, cur, path);
93+                if ((obj != NULL) &&
94+                    (obj->type == XPATH_NODESET) &&
95+                    (obj->nodesetval != NULL) &&
96+                    (obj->nodesetval->nodeNr > 0))
97+                    node = obj->nodesetval->nodeTab[0];
98                 xmlFree(path);
99             }
100
101-            if ((node->ns == NULL) || (node->ns->prefix == NULL))
102-                ret = xmlStrcat(ret, node->name);
103-            else {
104-                ret = xmlStrcat(ret, node->ns->prefix);
105-                ret = xmlStrcat(ret, BAD_CAST ":");
106-                ret = xmlStrcat(ret, node->name);
107+            switch (node->type) {
108+                case XML_ELEMENT_NODE:
109+                case XML_ATTRIBUTE_NODE:
110+                    if ((node->ns == NULL) || (node->ns->prefix == NULL))
111+                        ret = xmlStrcat(ret, node->name);
112+                    else {
113+                        ret = xmlStrcat(ret, node->ns->prefix);
114+                        ret = xmlStrcat(ret, BAD_CAST ":");
115+                        ret = xmlStrcat(ret, node->name);
116+                    }
117+                    break;
118+
119+                /* TODO: handle other node types */
120+                default:
121+                    break;
122             }
123+
124+            xmlXPathFreeObject(obj);
125         } else if (IS_SCHEMATRON(child, "value-of")) {
126             xmlChar *select;
127             xmlXPathObjectPtr eval;
128diff --git a/test/schematron/cve-2025-49794.sct b/test/schematron/cve-2025-49794.sct
129new file mode 100644
130index 000000000..7fc9ee3db
131--- /dev/null
132+++ b/test/schematron/cve-2025-49794.sct
133@@ -0,0 +1,10 @@
134+<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
135+    <sch:pattern id="">
136+        <sch:rule context="boo0">
137+            <sch:report test="not(0)">
138+                <sch:name path="&#9;e|namespace::*|e"/>
139+            </sch:report>
140+            <sch:report test="0"></sch:report>
141+        </sch:rule>
142+    </sch:pattern>
143+</sch:schema>
144diff --git a/test/schematron/cve-2025-49794_0.xml b/test/schematron/cve-2025-49794_0.xml
145new file mode 100644
146index 000000000..debc64ba6
147--- /dev/null
148+++ b/test/schematron/cve-2025-49794_0.xml
149@@ -0,0 +1,6 @@
150+<librar0>
151+    <boo0 t="">
152+        <author></author>
153+    </boo0>
154+    <ins></ins>
155+</librar0>
156diff --git a/test/schematron/cve-2025-49796.sct b/test/schematron/cve-2025-49796.sct
157new file mode 100644
158index 000000000..e9702d752
159--- /dev/null
160+++ b/test/schematron/cve-2025-49796.sct
161@@ -0,0 +1,9 @@
162+<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
163+    <sch:pattern id="">
164+        <sch:rule context="boo0">
165+            <sch:report test="not(0)">
166+                <sch:name path="/"/>
167+            </sch:report>
168+        </sch:rule>
169+    </sch:pattern>
170+</sch:schema>
171diff --git a/test/schematron/cve-2025-49796_0.xml b/test/schematron/cve-2025-49796_0.xml
172new file mode 100644
173index 000000000..be33c4ec5
174--- /dev/null
175+++ b/test/schematron/cve-2025-49796_0.xml
176@@ -0,0 +1,3 @@
177+<librar0>
178+    <boo0/>
179+</librar0>
180--
181GitLab
182
183