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="	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