diff mbox series

[scarthgap] libxml2: fix CVE-2025-49795

Message ID 20250711091527.15224-2-roland.kovacs@est.tech
State Changes Requested
Delegated to: Steve Sakoman
Headers show
Series [scarthgap] libxml2: fix CVE-2025-49795 | expand

Commit Message

Roland Kovács 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

Comments

Steve Sakoman July 16, 2025, 3:04 p.m. UTC | #1
I'm getting ptest failures with this patch:

WARNING: core-image-ptest-libxml2-1.0-r0 do_testimage: There were
failing ptests.
Traceback (most recent call last):
File "/srv/pokybuild/yocto-worker/qemux86-64-ptest/build/meta/lib/oeqa/core/decorator/__init__.py",
line 35, in wrapped_f
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/srv/pokybuild/yocto-worker/qemux86-64-ptest/build/meta/lib/oeqa/core/decorator/__init__.py",
line 35, in wrapped_f
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/srv/pokybuild/yocto-worker/qemux86-64-ptest/build/meta/lib/oeqa/core/decorator/__init__.py",
line 35, in wrapped_f
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/srv/pokybuild/yocto-worker/qemux86-64-ptest/build/meta/lib/oeqa/runtime/cases/ptest.py",
line 27, in test_ptestrunner_expectfail
self.do_ptestrunner()
File "/srv/pokybuild/yocto-worker/qemux86-64-ptest/build/meta/lib/oeqa/runtime/cases/ptest.py",
line 120, in do_ptestrunner
self.fail(failmsg)
AssertionError:
Failed ptests:
{'libxml2': ['runtest']}

I'm seeing it on both x86-64 and arm64:

https://autobuilder.yoctoproject.org/valkyrie/?#/builders/73/builds/1900/steps/12/logs/stdio
https://autobuilder.yoctoproject.org/valkyrie/?#/builders/61/builds/1897/steps/12/logs/stdio

I do not see the same issue with the walnascar version of the patch,
so perhaps that is a clue that will aid your debugging.

Steve

On Fri, Jul 11, 2025 at 2:15 AM roland.kovacs via
lists.openembedded.org <roland.kovacs=est.tech@lists.openembedded.org>
wrote:
>
> 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 --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"
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#220138): https://lists.openembedded.org/g/openembedded-core/message/220138
> Mute This Topic: https://lists.openembedded.org/mt/114097487/3620601
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [steve@sakoman.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
Roland Kovács July 17, 2025, 7:04 a.m. UTC | #2
On Wed, 2025-07-16 at 08:04 -0700, Steve Sakoman wrote:
> I'm getting ptest failures with this patch:
> 
> WARNING: core-image-ptest-libxml2-1.0-r0 do_testimage: There were
> failing ptests.
> Traceback (most recent call last):
> File "/srv/pokybuild/yocto-worker/qemux86-64-
> ptest/build/meta/lib/oeqa/core/decorator/__init__.py",
> line 35, in wrapped_f
> return func(*args, **kwargs)
> ^^^^^^^^^^^^^^^^^^^^^
> File "/srv/pokybuild/yocto-worker/qemux86-64-
> ptest/build/meta/lib/oeqa/core/decorator/__init__.py",
> line 35, in wrapped_f
> return func(*args, **kwargs)
> ^^^^^^^^^^^^^^^^^^^^^
> File "/srv/pokybuild/yocto-worker/qemux86-64-
> ptest/build/meta/lib/oeqa/core/decorator/__init__.py",
> line 35, in wrapped_f
> return func(*args, **kwargs)
> ^^^^^^^^^^^^^^^^^^^^^
> File "/srv/pokybuild/yocto-worker/qemux86-64-ptest/build/meta/lib/oeqa/runtime/cases/ptest.py",
> line 27, in test_ptestrunner_expectfail
> self.do_ptestrunner()
> File "/srv/pokybuild/yocto-worker/qemux86-64-ptest/build/meta/lib/oeqa/runtime/cases/ptest.py",
> line 120, in do_ptestrunner
> self.fail(failmsg)
> AssertionError:
> Failed ptests:
> {'libxml2': ['runtest']}
> 
> I'm seeing it on both x86-64 and arm64:
> 
> https://autobuilder.yoctoproject.org/valkyrie/?#/builders/73/builds/1900/steps/12/logs/stdio
> https://autobuilder.yoctoproject.org/valkyrie/?#/builders/61/builds/1897/steps/12/logs/stdio
> 
> I do not see the same issue with the walnascar version of the patch,
> so perhaps that is a clue that will aid your debugging.
> 
> Steve
> 

I'll look into it, and thanks for the pointers!

Cheers,
Roland

> On Fri, Jul 11, 2025 at 2:15 AM roland.kovacs via
> lists.openembedded.org <roland.kovacs=est.tech@lists.openembedded.org>
> wrote:
> > 
> > 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 --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"
> > 
> > -=-=-=-=-=-=-=-=-=-=-=-
> > Links: You receive all messages sent to this group.
> > View/Reply Online (#220138): https://lists.openembedded.org/g/openembedded-core/message/220138
> > Mute This Topic: https://lists.openembedded.org/mt/114097487/3620601
> > Group Owner: openembedded-core+owner@lists.openembedded.org
> > Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [steve@sakoman.com]
> > -=-=-=-=-=-=-=-=-=-=-=-
> >
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"