File libxml2-CVE-2026-0992.patch of Package libxml2
From f8399e62a31095bf1ced01827c33f9b29494046f Mon Sep 17 00:00:00 2001
From: Daniel Garcia Moreno <daniel.garcia@suse.com>
Date: Fri, 19 Dec 2025 12:27:54 +0100
Subject: [PATCH 1/2] testcatalog: Add new tests for catalog.c
Adds a new test program to run specific tests related to catalog
parsing.
This initial version includes a couple of tests, the first one to check
the infinite recursion detection related to:
https://gitlab.gnome.org/GNOME/libxml2/-/issues/1018.
The second one tests the nextCatalog element repeated parsing, related
to:
https://gitlab.gnome.org/GNOME/libxml2/-/issues/1019
https://gitlab.gnome.org/GNOME/libxml2/-/issues/1040
---
CMakeLists.txt | 2 +
Makefile.am | 6 ++
catalog.c | 63 +++++++++++-----
include/libxml/catalog.h | 2 +
meson.build | 1 +
test/catalogs/catalog-recursive.xml | 3 +
test/catalogs/repeated-next-catalog.xml | 10 +++
testcatalog.c | 96 +++++++++++++++++++++++++
8 files changed, 164 insertions(+), 19 deletions(-)
create mode 100644 test/catalogs/catalog-recursive.xml
create mode 100644 test/catalogs/repeated-next-catalog.xml
create mode 100644 testcatalog.c
Index: libxml2-2.14.5/CMakeLists.txt
===================================================================
--- libxml2-2.14.5.orig/CMakeLists.txt
+++ libxml2-2.14.5/CMakeLists.txt
@@ -488,6 +488,7 @@ if(LIBXML2_WITH_TESTS)
runxmlconf
runsuite
testapi
+ testcatalog
testchar
testdict
testModule
@@ -512,6 +513,7 @@ if(LIBXML2_WITH_TESTS)
if(NOT WIN32)
add_test(NAME testapi COMMAND testapi)
endif()
+ add_test(NAME testcatalog COMMAND testcatalog)
add_test(NAME testchar COMMAND testchar)
add_test(NAME testdict COMMAND testdict)
add_test(NAME testparser COMMAND testparser WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
Index: libxml2-2.14.5/Makefile.am
===================================================================
--- libxml2-2.14.5.orig/Makefile.am
+++ libxml2-2.14.5/Makefile.am
@@ -20,6 +20,7 @@ check_PROGRAMS = \
runxmlconf \
testModule \
testapi \
+ testcatalog \
testchar \
testdict \
testlimits \
@@ -120,6 +121,10 @@ testlimits_SOURCES=testlimits.c
testlimits_DEPENDENCIES = $(DEPS)
testlimits_LDADD= $(LDADDS)
+testcatalog_SOURCES=testcatalog.c
+testcatalog_DEPENDENCIES = $(DEPS)
+testcatalog_LDADD= $(LDADDS)
+
testchar_SOURCES=testchar.c
testchar_DEPENDENCIES = $(DEPS)
testchar_LDADD= $(LDADDS)
@@ -169,6 +174,7 @@ check-local:
$(CHECKER) ./runtest$(EXEEXT)
$(CHECKER) ./testrecurse$(EXEEXT)
$(CHECKER) ./testapi$(EXEEXT)
+ $(CHECKER) ./testcatalog$(EXEEXT)
$(CHECKER) ./testchar$(EXEEXT)
$(CHECKER) ./testdict$(EXEEXT)
$(CHECKER) ./testparser$(EXEEXT)
Index: libxml2-2.14.5/catalog.c
===================================================================
--- libxml2-2.14.5.orig/catalog.c
+++ libxml2-2.14.5/catalog.c
@@ -637,43 +637,54 @@ static void xmlDumpXMLCatalogNode(xmlCat
}
}
-static int
-xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
- int ret;
- xmlDocPtr doc;
+static xmlDocPtr
+xmlDumpXMLCatalogToDoc(xmlCatalogEntryPtr catal) {
xmlNsPtr ns;
xmlDtdPtr dtd;
xmlNodePtr catalog;
- xmlOutputBufferPtr buf;
+ xmlDocPtr doc = xmlNewDoc(NULL);
+ if (doc == NULL) {
+ return(NULL);
+ }
- /*
- * Rebuild a catalog
- */
- doc = xmlNewDoc(NULL);
- if (doc == NULL)
- return(-1);
dtd = xmlNewDtd(doc, BAD_CAST "catalog",
- BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
-BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");
+ BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
+ BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");
xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
if (ns == NULL) {
- xmlFreeDoc(doc);
- return(-1);
+ xmlFreeDoc(doc);
+ return(NULL);
}
catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
if (catalog == NULL) {
- xmlFreeNs(ns);
- xmlFreeDoc(doc);
- return(-1);
+ xmlFreeDoc(doc);
+ xmlFreeNs(ns);
+ return(NULL);
}
catalog->nsDef = ns;
xmlAddChild((xmlNodePtr) doc, catalog);
-
xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL);
+ return(doc);
+}
+
+static int
+xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
+ int ret;
+ xmlDocPtr doc;
+ xmlOutputBufferPtr buf;
+
+ /*
+ * Rebuild a catalog
+ */
+ doc = xmlDumpXMLCatalogToDoc(catal);
+ if (doc == NULL) {
+ return(-1);
+ }
+
/*
* reserialize it
*/
@@ -1236,7 +1247,6 @@ xmlParseXMLCatalogNode(xmlNodePtr cur, x
while (prev != NULL) {
if ((prev->type == XML_CATA_NEXT_CATALOG) &&
(xmlStrEqual (prev->URL, entry->URL)) &&
- (xmlStrEqual (prev->value, entry->value)) &&
(prev->prefer == entry->prefer) &&
(prev->group == entry->group)) {
if (xmlDebugCatalogs)
@@ -3369,6 +3379,20 @@ xmlCatalogDump(FILE *out) {
xmlACatalogDump(xmlDefaultCatalog, out);
}
+
+/**
+ * Dump all the global catalog content as a xmlDoc
+ * This function is just for testing/debugging purposes
+ *
+ * @returns The catalog as xmlDoc or NULL if failed, it must be freed by the caller.
+ */
+xmlDocPtr
+xmlCatalogDumpDoc(void) {
+ if (!xmlCatalogInitialized)
+ xmlInitializeCatalog();
+
+ return xmlDumpXMLCatalogToDoc(xmlDefaultCatalog->xml);
+}
#endif /* LIBXML_OUTPUT_ENABLED */
/**
Index: libxml2-2.14.5/include/libxml/catalog.h
===================================================================
--- libxml2-2.14.5.orig/include/libxml/catalog.h
+++ libxml2-2.14.5/include/libxml/catalog.h
@@ -119,6 +119,8 @@ XMLPUBFUN void
#ifdef LIBXML_OUTPUT_ENABLED
XMLPUBFUN void
xmlCatalogDump (FILE *out);
+XMLPUBFUN xmlDocPtr
+ xmlCatalogDumpDoc (void);
#endif /* LIBXML_OUTPUT_ENABLED */
XMLPUBFUN xmlChar *
xmlCatalogResolve (const xmlChar *pubID,
Index: libxml2-2.14.5/meson.build
===================================================================
--- libxml2-2.14.5.orig/meson.build
+++ libxml2-2.14.5/meson.build
@@ -539,6 +539,7 @@ checks = {
# Disabled for now, see #694
# 'testModule': [],
'testapi': [],
+ 'testcatalog': [],
'testchar': [],
'testdict': [],
'testlimits': [],
Index: libxml2-2.14.5/test/catalogs/catalog-recursive.xml
===================================================================
--- /dev/null
+++ libxml2-2.14.5/test/catalogs/catalog-recursive.xml
@@ -0,0 +1,3 @@
+<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
+ <delegateURI uriStartString="/foo" catalog="catalog-recursive.xml"/>
+</catalog>
Index: libxml2-2.14.5/test/catalogs/repeated-next-catalog.xml
===================================================================
--- /dev/null
+++ libxml2-2.14.5/test/catalogs/repeated-next-catalog.xml
@@ -0,0 +1,10 @@
+<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
+ <nextCatalog catalog="registry.xml"/>
+ <nextCatalog catalog="registry.xml"/>
+ <nextCatalog catalog="./registry.xml"/>
+ <nextCatalog catalog="././registry.xml"/>
+ <nextCatalog catalog="./././registry.xml"/>
+ <nextCatalog catalog="./../catalogs/registry.xml"/>
+ <nextCatalog catalog="./../catalogs/./registry.xml"/>
+</catalog>
+
Index: libxml2-2.14.5/testcatalog.c
===================================================================
--- /dev/null
+++ libxml2-2.14.5/testcatalog.c
@@ -0,0 +1,96 @@
+/*
+ * testcatalog.c: C program to run libxml2 catalog.c unit tests
+ *
+ * To compile on Unixes:
+ * cc -o testcatalog `xml2-config --cflags` testcatalog.c `xml2-config --libs` -lpthread
+ *
+ * See Copyright for the status of this software.
+ *
+ * Author: Daniel Garcia <dani@danigm.net>
+ */
+
+
+#include "libxml.h"
+#include <stdio.h>
+
+#ifdef LIBXML_CATALOG_ENABLED
+#include <libxml/catalog.h>
+
+/* Test catalog resolve uri with recursive catalog */
+static int
+testRecursiveDelegateUri(void) {
+ int ret = 0;
+ const char *cat = "test/catalogs/catalog-recursive.xml";
+ const char *entity = "/foo.ent";
+ xmlChar *resolved = NULL;
+
+ xmlInitParser();
+ xmlLoadCatalog(cat);
+
+ /* This should trigger recursive error */
+ resolved = xmlCatalogResolveURI(BAD_CAST entity);
+ if (resolved != NULL) {
+ fprintf(stderr, "CATALOG-FAILURE: Catalog %s entity should fail to resolve\n", entity);
+ ret = 1;
+ }
+ xmlCatalogCleanup();
+
+ return ret;
+}
+
+/* Test parsing repeated NextCatalog */
+static int
+testRepeatedNextCatalog(void) {
+ int ret = 0;
+ int i = 0;
+ const char *cat = "test/catalogs/repeated-next-catalog.xml";
+ const char *entity = "/foo.ent";
+ xmlDocPtr doc = NULL;
+ xmlNodePtr node = NULL;
+
+ xmlInitParser();
+
+ xmlLoadCatalog(cat);
+ /* To force the complete recursive load */
+ xmlCatalogResolveURI(BAD_CAST entity);
+ /**
+ * Ensure that the doc doesn't contain the same nextCatalog
+ */
+ doc = xmlCatalogDumpDoc();
+ xmlCatalogCleanup();
+
+ if (doc == NULL) {
+ fprintf(stderr, "CATALOG-FAILURE: Failed to dump the catalog\n");
+ return 1;
+ }
+
+ /* Just the root "catalog" node with a series of nextCatalog */
+ node = xmlDocGetRootElement(doc);
+ node = node->children;
+ for (i=0; node != NULL; node=node->next, i++) {}
+ if (i > 1) {
+ fprintf(stderr, "CATALOG-FAILURE: Found %d nextCatalog entries and should be 1\n", i);
+ ret = 1;
+ }
+
+ xmlFreeDoc(doc);
+
+ return ret;
+}
+
+int
+main(void) {
+ int err = 0;
+
+ err |= testRecursiveDelegateUri();
+ err |= testRepeatedNextCatalog();
+
+ return err;
+}
+#else
+/* No catalog, so everything okay */
+int
+main(void) {
+ return 0;
+}
+#endif
Index: libxml2-2.14.5/configure.ac
===================================================================
--- libxml2-2.14.5.orig/configure.ac
+++ libxml2-2.14.5/configure.ac
@@ -41,7 +41,7 @@ AC_SUBST(LIBXML_VERSION_INFO)
AC_SUBST(LIBXML_VERSION_NUMBER)
AC_SUBST(LIBXML_VERSION_EXTRA)
-AM_INIT_AUTOMAKE([1.16.3 foreign subdir-objects no-dist-gzip dist-xz])
+AM_INIT_AUTOMAKE([1.15.1 foreign subdir-objects no-dist-gzip dist-xz])
AM_MAINTAINER_MODE([enable])
AM_SILENT_RULES([yes])