From c63248941708bc1d2e3a4292954593312212f6ca Mon Sep 17 00:00:00 2001 From: yirou_yang Date: Tue, 11 Aug 2026 05:37:03 +0000 Subject: [PATCH] catalog: Fix NULL deref for nextCatalog without 'catalog' attribute --- CMakeLists.txt | 2 +- catalog.c | 2 +- test/catalogs/next-catalog-missing-attr.xml | 4 ++ testcatalog.c | 57 +++++++++++++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 test/catalogs/next-catalog-missing-attr.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 04d373bfd..ec9e73d56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -525,7 +525,7 @@ if(LIBXML2_WITH_TESTS) add_test(NAME runxmlconf COMMAND runxmlconf WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif() add_test(NAME testapi COMMAND testapi) - add_test(NAME testcatalog COMMAND testcatalog) + add_test(NAME testcatalog COMMAND testcatalog WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_test(NAME testchar COMMAND testchar) add_test(NAME testdict COMMAND testdict) add_test(NAME testparser COMMAND testparser WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/catalog.c b/catalog.c index 448c88a59..2a080f877 100644 --- a/catalog.c +++ b/catalog.c @@ -1329,7 +1329,7 @@ xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer, BAD_CAST "nextCatalog", NULL, BAD_CAST "catalog", prefer, cgroup); /* Avoid duplication of nextCatalog */ - while (prev != NULL) { + while ((entry != NULL) && (prev != NULL)) { if ((prev->type == XML_CATA_NEXT_CATALOG) && (xmlStrEqual (prev->URL, entry->URL)) && (prev->prefer == entry->prefer) && diff --git a/test/catalogs/next-catalog-missing-attr.xml b/test/catalogs/next-catalog-missing-attr.xml new file mode 100644 index 000000000..b31a49062 --- /dev/null +++ b/test/catalogs/next-catalog-missing-attr.xml @@ -0,0 +1,4 @@ + + + + diff --git a/testcatalog.c b/testcatalog.c index 56c9bef65..d3384a4e0 100644 --- a/testcatalog.c +++ b/testcatalog.c @@ -79,6 +79,62 @@ testRepeatedNextCatalog(void) { return ret; } +/* + * Test a nextCatalog entry which lacks the mandatory 'catalog' attribute + * See https://gitlab.gnome.org/GNOME/libxml2/-/issues/1143 + */ +static int +testNextCatalogMissingAttr(void) { + int ret = 0; + int i = 0; + const char *cat = "test/catalogs/next-catalog-missing-attr.xml"; + const char *sysId = "http://www.oasis-open.org/docbook/xml/4.1.2/dbpoolx.mod"; + const char *expect = "/usr/share/xml/docbook/xml/4.1.2/dbpoolx.mod"; + xmlChar *resolved = NULL; + xmlDocPtr doc = NULL; + xmlNodePtr node = NULL; + + xmlInitParser(); + + xmlLoadCatalog(cat); + /* + * The malformed nextCatalog must be ignored, the valid one must still + * be followed. This also forces the complete recursive load. + */ + resolved = xmlCatalogResolveSystem(BAD_CAST sysId); + if ((resolved == NULL) || (strcmp((char *) resolved, expect) != 0)) { + fprintf(stderr, "CATALOG-FAILURE: %s resolved to %s, expected %s\n", + sysId, resolved == NULL ? "NULL" : (char *) resolved, expect); + ret = 1; + } + if (resolved != NULL) + xmlFree(resolved); + + /** + * Ensure that only the valid nextCatalog was recorded + */ + 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 single 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; +} + /* * Test repeated call to xmlCatalogResolveURI * See https://gitlab.gnome.org/GNOME/libxml2/-/work_items/1125 @@ -135,6 +191,7 @@ main(void) { err |= testRecursiveDelegateUri(); err |= testRepeatedNextCatalog(); + err |= testNextCatalogMissingAttr(); err |= testRepeatedResolveURI(); return err; -- GitLab