diff mbox series

[scarthgap] libxml2: fix CVE-2025-49795

Message ID 20250711091527.15224-2-roland.kovacs@est.tech
State New
Headers show
Series [scarthgap] libxml2: fix CVE-2025-49795 | expand

Commit Message

roland.kovacs@est.tech July 11, 2025, 9:15 a.m. UTC
From: Roland Kovacs <roland.kovacs@est.tech>

A NULL pointer dereference vulnerability was found in libxml2 when processing
XPath XML expressions. This flaw allows an attacker to craft a malicious XML
input to libxml2, leading to a denial of service.

Signed-off-by: Roland Kovacs <roland.kovacs@est.tech>
---
 .../libxml/libxml2/CVE-2025-49795.patch       | 238 ++++++++++++++++++
 meta/recipes-core/libxml/libxml2_2.12.10.bb   |   1 +
 2 files changed, 239 insertions(+)
 create mode 100644 meta/recipes-core/libxml/libxml2/CVE-2025-49795.patch
diff mbox series

Patch

diff --git a/meta/recipes-core/libxml/libxml2/CVE-2025-49795.patch b/meta/recipes-core/libxml/libxml2/CVE-2025-49795.patch
new file mode 100644
index 0000000000..fd784ea45b
--- /dev/null
+++ b/meta/recipes-core/libxml/libxml2/CVE-2025-49795.patch
@@ -0,0 +1,238 @@ 
+From 68ce19940f8e08c85f9c8ce65180cdb90bf4b0d2 Mon Sep 17 00:00:00 2001
+From: Michael Mann <mmann78@netscape.net>
+Date: Sat, 21 Jun 2025 12:11:30 -0400
+Subject: [PATCH] Schematron: Fix null pointer dereference leading to DoS
+
+(CVE-2025-49795)
+
+Fixes #932
+
+Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/libxml2/-/commit/c24909ba2601848825b49a60f988222da3019667]
+CVE: CVE-2025-49795
+
+(cherry picked from commit c24909ba2601848825b49a60f988222da3019667)
+Signed-off-by: Roland Kovacs <roland.kovacs@est.tech>
+---
+ result/schematron/zvon16_0.err |   3 +
+ runtest.c                      | 139 +++++++++++++++++++++++++++++++++
+ schematron.c                   |   5 ++
+ test/schematron/zvon16.sct     |   7 ++
+ test/schematron/zvon16_0.xml   |   5 ++
+ 5 files changed, 159 insertions(+)
+ create mode 100644 result/schematron/zvon16_0.err
+ create mode 100644 test/schematron/zvon16.sct
+ create mode 100644 test/schematron/zvon16_0.xml
+
+diff --git a/result/schematron/zvon16_0.err b/result/schematron/zvon16_0.err
+new file mode 100644
+index 00000000..3d052409
+--- /dev/null
++++ b/result/schematron/zvon16_0.err
+@@ -0,0 +1,3 @@
++XPath error : Unregistered function
++./test/schematron/zvon16_0.xml:2: element book: schematron error : /library/book line 2: Book 
++./test/schematron/zvon16_0.xml fails to validate
+diff --git a/runtest.c b/runtest.c
+index c78eec81..736cae5e 100644
+--- a/runtest.c
++++ b/runtest.c
+@@ -52,6 +52,10 @@
+ #include <libxml/xmlschemastypes.h>
+ #endif
+ 
++#ifdef LIBXML_SCHEMATRON_ENABLED
++#include <libxml/schematron.h>
++#endif
++
+ #ifdef LIBXML_PATTERN_ENABLED
+ #include <libxml/pattern.h>
+ #endif
+@@ -3881,6 +3885,136 @@ rngStreamTest(const char *filename,
+ 
+ #endif
+ 
++/************************************************************************
++ *									*
++ *			Schematron tests				*
++ *									*
++ ************************************************************************/
++
++#ifdef LIBXML_SCHEMATRON_ENABLED
++static int
++schematronOneTest(const char *sch, const char *filename, int options,
++                  xmlSchematronPtr schematron) {
++    xmlDocPtr doc;
++    xmlSchematronValidCtxtPtr ctxt;
++    int ret;
++
++    doc = xmlReadFile(filename, NULL, options);
++    if (doc == NULL) {
++        fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch);
++	return(-1);
++    }
++
++    ctxt = xmlSchematronNewValidCtxt(schematron, XML_SCHEMATRON_OUT_ERROR);
++    xmlSchematronSetValidStructuredErrors(ctxt, testStructuredErrorHandler,
++                                          NULL);
++    ret = xmlSchematronValidateDoc(ctxt, doc);
++    if (ret == 0) {
++	testErrorHandler(NULL, "%s validates\n", filename);
++    } else if (ret > 0) {
++	testErrorHandler(NULL, "%s fails to validate\n", filename);
++    } else {
++	testErrorHandler(NULL, "%s validation generated an internal error\n",
++	       filename);
++    }
++
++    xmlSchematronFreeValidCtxt(ctxt);
++    xmlFreeDoc(doc);
++    return(0);
++}
++
++/**
++ * schematronTest:
++ * @filename: the schemas file
++ * @result: the file with expected result
++ * @err: the file with error messages
++ *
++ * Returns 0 in case of success, an error code otherwise
++ */
++static int
++schematronTest(const char *filename,
++               const char *resul ATTRIBUTE_UNUSED,
++               const char *errr ATTRIBUTE_UNUSED,
++               int options) {
++    const char *base = baseFilename(filename);
++    const char *base2;
++    const char *instance;
++    xmlSchematronParserCtxtPtr pctxt;
++    xmlSchematronPtr schematron;
++    int res = 0, len, ret = 0;
++    int parseErrorsSize;
++    char pattern[500];
++    char prefix[500];
++    char err[500];
++    glob_t globbuf;
++    size_t i;
++    char count = 0;
++
++    /* Redirect XPath errors */
++    xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
++
++    pctxt = xmlSchematronNewParserCtxt(filename);
++    schematron = xmlSchematronParse(pctxt);
++    xmlSchematronFreeParserCtxt(pctxt);
++    if (schematron == NULL)
++        testErrorHandler(NULL, "Schematron schema %s failed to compile\n",
++                         filename);
++    parseErrorsSize = testErrorsSize;
++
++    /*
++     * most of the mess is about the output filenames generated by the Makefile
++     */
++    len = strlen(base);
++    if ((len > 499) || (len < 5)) {
++        ret = -1;
++        goto done;
++    }
++    len -= 4; /* remove trailing .sct */
++    memcpy(prefix, base, len);
++    prefix[len] = 0;
++
++    if (snprintf(pattern, 499, "./test/schematron/%s_?.xml", prefix) >= 499)
++        pattern[499] = 0;
++
++    globbuf.gl_offs = 0;
++    glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
++    for (i = 0;i < globbuf.gl_pathc;i++) {
++        testErrorsSize = parseErrorsSize;
++        testErrors[parseErrorsSize] = 0;
++        instance = globbuf.gl_pathv[i];
++	base2 = baseFilename(instance);
++	len = strlen(base2);
++	if ((len > 6) && (base2[len - 6] == '_')) {
++	    count = base2[len - 5];
++	    res = snprintf(err, 499, "result/schematron/%s_%c.err",
++		     prefix, count);
++            if (res >= 499)
++	        err[499] = 0;
++	} else {
++	    fprintf(stderr, "don't know how to process %s\n", instance);
++	    continue;
++	}
++	if (schematron != NULL) {
++	    nb_tests++;
++	    res = schematronOneTest(filename, instance, options, schematron);
++	    if (res != 0)
++		ret = res;
++	}
++        if (compareFileMem(err, testErrors, testErrorsSize)) {
++            fprintf(stderr, "Error for %s on %s failed\n", instance,
++                    filename);
++            ret = 1;
++        }
++    }
++    globfree(&globbuf);
++
++done:
++    xmlSchematronFree(schematron);
++    xmlSetStructuredErrorFunc(NULL, NULL);
++    return(ret);
++}
++#endif /* LIBXML_SCHEMATRON_ENABLED */
++
+ #ifdef LIBXML_PATTERN_ENABLED
+ #ifdef LIBXML_READER_ENABLED
+ /************************************************************************
+@@ -5117,6 +5251,11 @@ testDesc testDescriptions[] = {
+       rngStreamTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
+       XML_PARSE_DTDATTR | XML_PARSE_NOENT },
+ #endif
++#if defined(LIBXML_SCHEMATRON_ENABLED)
++    { "Schematron regression tests" ,
++      schematronTest, "./test/schematron/*.sct", NULL, NULL, NULL,
++      0 },
++#endif
+ #endif
+ #ifdef LIBXML_PATTERN_ENABLED
+ #ifdef LIBXML_READER_ENABLED
+diff --git a/schematron.c b/schematron.c
+index 411a515c..c970d31f 100644
+--- a/schematron.c
++++ b/schematron.c
+@@ -1484,6 +1484,11 @@ xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
+             select = xmlGetNoNsProp(child, BAD_CAST "select");
+             comp = xmlXPathCtxtCompile(ctxt->xctxt, select);
+             eval = xmlXPathCompiledEval(comp, ctxt->xctxt);
++            if (eval == NULL) {
++                xmlXPathFreeCompExpr(comp);
++                xmlFree(select);
++                return ret;
++            }
+ 
+             switch (eval->type) {
+             case XPATH_NODESET: {
+diff --git a/test/schematron/zvon16.sct b/test/schematron/zvon16.sct
+new file mode 100644
+index 00000000..f03848aa
+--- /dev/null
++++ b/test/schematron/zvon16.sct
+@@ -0,0 +1,7 @@
++<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
++	<sch:pattern id="TestPattern">
++		<sch:rule context="book">
++			<sch:report test="not(@available)">Book <sch:value-of select="falae()"/> test</sch:report>
++		</sch:rule>
++	</sch:pattern>
++</sch:schema>
+diff --git a/test/schematron/zvon16_0.xml b/test/schematron/zvon16_0.xml
+new file mode 100644
+index 00000000..551e2d65
+--- /dev/null
++++ b/test/schematron/zvon16_0.xml
+@@ -0,0 +1,5 @@
++<library>
++	<book title="Test Book" id="bk101">
++		<author>Test Author</author>
++	</book>
++</library>
diff --git a/meta/recipes-core/libxml/libxml2_2.12.10.bb b/meta/recipes-core/libxml/libxml2_2.12.10.bb
index 2eea65732b..ea40ca68ed 100644
--- a/meta/recipes-core/libxml/libxml2_2.12.10.bb
+++ b/meta/recipes-core/libxml/libxml2_2.12.10.bb
@@ -20,6 +20,7 @@  SRC_URI += "http://www.w3.org/XML/Test/xmlts20130923.tar;subdir=${BP};name=testt
            file://install-tests.patch \
            file://CVE-2025-32414.patch \
            file://CVE-2025-32415.patch \
+           file://CVE-2025-49795.patch \
            "
 
 SRC_URI[archive.sha256sum] = "c3d8c0c34aa39098f66576fe51969db12a5100b956233dc56506f7a8679be995"