forked from pool/libxml2
Compare commits
2 Commits
| Author | SHA256 | Date | |
|---|---|---|---|
| faf6e62617 | |||
| 4cd91fe2f1 |
301
libxml2-CVE-2026-0989.patch
Normal file
301
libxml2-CVE-2026-0989.patch
Normal file
@@ -0,0 +1,301 @@
|
||||
From 19549c61590c1873468c53e0026a2fbffae428ef Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Garcia Moreno <daniel.garcia@suse.com>
|
||||
Date: Fri, 10 Oct 2025 09:38:31 +0200
|
||||
Subject: [PATCH] Add RelaxNG include limit
|
||||
|
||||
This patch adds a default xmlRelaxNGIncludeLimit of 1.000, and that
|
||||
limit can be modified at runtime with the env variable
|
||||
RNG_INCLUDE_LIMIT.
|
||||
|
||||
Fix https://gitlab.gnome.org/GNOME/libxml2/-/issues/998
|
||||
---
|
||||
include/libxml/relaxng.h | 4 ++
|
||||
relaxng.c | 63 ++++++++++++++++++++--
|
||||
runtest.c | 67 ++++++++++++++++++++++++
|
||||
test/relaxng/include/include-limit.rng | 4 ++
|
||||
test/relaxng/include/include-limit_1.rng | 4 ++
|
||||
test/relaxng/include/include-limit_2.rng | 4 ++
|
||||
test/relaxng/include/include-limit_3.rng | 8 +++
|
||||
7 files changed, 150 insertions(+), 4 deletions(-)
|
||||
create mode 100644 test/relaxng/include/include-limit.rng
|
||||
create mode 100644 test/relaxng/include/include-limit_1.rng
|
||||
create mode 100644 test/relaxng/include/include-limit_2.rng
|
||||
create mode 100644 test/relaxng/include/include-limit_3.rng
|
||||
|
||||
Index: libxml2-2.13.8/include/libxml/relaxng.h
|
||||
===================================================================
|
||||
--- libxml2-2.13.8.orig/include/libxml/relaxng.h
|
||||
+++ libxml2-2.13.8/include/libxml/relaxng.h
|
||||
@@ -138,6 +138,10 @@ XMLPUBFUN int
|
||||
xmlRelaxParserSetFlag (xmlRelaxNGParserCtxtPtr ctxt,
|
||||
int flag);
|
||||
|
||||
+XMLPUBFUN int
|
||||
+ xmlRelaxParserSetIncLImit (xmlRelaxNGParserCtxt *ctxt,
|
||||
+ int limit);
|
||||
+
|
||||
XMLPUBFUN void
|
||||
xmlRelaxNGFreeParserCtxt (xmlRelaxNGParserCtxtPtr ctxt);
|
||||
XMLPUBFUN void
|
||||
Index: libxml2-2.13.8/relaxng.c
|
||||
===================================================================
|
||||
--- libxml2-2.13.8.orig/relaxng.c
|
||||
+++ libxml2-2.13.8/relaxng.c
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#ifdef LIBXML_SCHEMAS_ENABLED
|
||||
|
||||
+#include <errno.h>
|
||||
+#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
@@ -44,6 +46,12 @@
|
||||
static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
|
||||
"http://relaxng.org/ns/structure/1.0";
|
||||
|
||||
+/*
|
||||
+ * Default include limit, this can be override with RNG_INCLUDE_LIMIT
|
||||
+ * env variable
|
||||
+ */
|
||||
+static const int _xmlRelaxNGIncludeLimit = 1000;
|
||||
+
|
||||
#define IS_RELAXNG(node, typ) \
|
||||
((node != NULL) && (node->ns != NULL) && \
|
||||
(node->type == XML_ELEMENT_NODE) && \
|
||||
@@ -220,6 +228,7 @@ struct _xmlRelaxNGParserCtxt {
|
||||
int incNr; /* Depth of the include parsing stack */
|
||||
int incMax; /* Max depth of the parsing stack */
|
||||
xmlRelaxNGIncludePtr *incTab; /* array of incs */
|
||||
+ int incLimit; /* Include limit, to avoid stack-overflow on parse */
|
||||
|
||||
int idref; /* requires idref checking */
|
||||
|
||||
@@ -1403,6 +1412,23 @@ xmlRelaxParserSetFlag(xmlRelaxNGParserCt
|
||||
return(0);
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * Semi private function used to set the include recursion limit to a
|
||||
+ * parser context. Set to 0 to use the default value.
|
||||
+ *
|
||||
+ * @param ctxt a RelaxNG parser context
|
||||
+ * @param limit the new include depth limit
|
||||
+ * @returns 0 if success and -1 in case of error
|
||||
+ */
|
||||
+int
|
||||
+xmlRelaxParserSetIncLImit(xmlRelaxNGParserCtxt *ctxt, int limit)
|
||||
+{
|
||||
+ if (ctxt == NULL) return(-1);
|
||||
+ if (limit < 0) return(-1);
|
||||
+ ctxt->incLimit = limit;
|
||||
+ return(0);
|
||||
+}
|
||||
+
|
||||
/************************************************************************
|
||||
* *
|
||||
* Document functions *
|
||||
@@ -1454,7 +1480,7 @@ xmlRelaxReadMemory(xmlRelaxNGParserCtxtP
|
||||
*
|
||||
* Pushes a new include on top of the include stack
|
||||
*
|
||||
- * Returns 0 in case of error, the index in the stack otherwise
|
||||
+ * @returns -1 in case of error, the index in the stack otherwise
|
||||
*/
|
||||
static int
|
||||
xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
|
||||
@@ -1468,9 +1494,15 @@ xmlRelaxNGIncludePush(xmlRelaxNGParserCt
|
||||
sizeof(ctxt->incTab[0]));
|
||||
if (ctxt->incTab == NULL) {
|
||||
xmlRngPErrMemory(ctxt);
|
||||
- return (0);
|
||||
+ return (-1);
|
||||
}
|
||||
}
|
||||
+ if (ctxt->incNr >= ctxt->incLimit) {
|
||||
+ xmlRngPErr(ctxt, (xmlNodePtr)value->doc, XML_RNGP_PARSE_ERROR,
|
||||
+ "xmlRelaxNG: inclusion recursion limit reached\n", NULL, NULL);
|
||||
+ return(-1);
|
||||
+ }
|
||||
+
|
||||
if (ctxt->incNr >= ctxt->incMax) {
|
||||
ctxt->incMax *= 2;
|
||||
ctxt->incTab =
|
||||
@@ -1479,7 +1511,7 @@ xmlRelaxNGIncludePush(xmlRelaxNGParserCt
|
||||
sizeof(ctxt->incTab[0]));
|
||||
if (ctxt->incTab == NULL) {
|
||||
xmlRngPErrMemory(ctxt);
|
||||
- return (0);
|
||||
+ return (-1);
|
||||
}
|
||||
}
|
||||
ctxt->incTab[ctxt->incNr] = value;
|
||||
@@ -1649,7 +1681,9 @@ xmlRelaxNGLoadInclude(xmlRelaxNGParserCt
|
||||
/*
|
||||
* push it on the stack
|
||||
*/
|
||||
- xmlRelaxNGIncludePush(ctxt, ret);
|
||||
+ if (xmlRelaxNGIncludePush(ctxt, ret) < 0) {
|
||||
+ return (NULL);
|
||||
+ }
|
||||
|
||||
/*
|
||||
* Some preprocessing of the document content, this include recursing
|
||||
@@ -7373,11 +7407,32 @@ xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr
|
||||
xmlDocPtr doc;
|
||||
xmlNodePtr root;
|
||||
|
||||
+ const char *include_limit_env = getenv("RNG_INCLUDE_LIMIT");
|
||||
+
|
||||
xmlRelaxNGInitTypes();
|
||||
|
||||
if (ctxt == NULL)
|
||||
return (NULL);
|
||||
|
||||
+ if (ctxt->incLimit == 0) {
|
||||
+ ctxt->incLimit = _xmlRelaxNGIncludeLimit;
|
||||
+ if (include_limit_env != NULL) {
|
||||
+ char *strEnd;
|
||||
+ unsigned long val = 0;
|
||||
+ errno = 0;
|
||||
+ val = strtoul(include_limit_env, &strEnd, 10);
|
||||
+ if (errno != 0 || *strEnd != 0 || val > INT_MAX) {
|
||||
+ xmlRngPErr(ctxt, NULL, XML_RNGP_PARSE_ERROR,
|
||||
+ "xmlRelaxNGParse: invalid RNG_INCLUDE_LIMIT %s\n",
|
||||
+ (const xmlChar*)include_limit_env,
|
||||
+ NULL);
|
||||
+ return(NULL);
|
||||
+ }
|
||||
+ if (val)
|
||||
+ ctxt->incLimit = val;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/*
|
||||
* First step is to parse the input document into an DOM/Infoset
|
||||
*/
|
||||
Index: libxml2-2.13.8/runtest.c
|
||||
===================================================================
|
||||
--- libxml2-2.13.8.orig/runtest.c
|
||||
+++ libxml2-2.13.8/runtest.c
|
||||
@@ -3529,6 +3529,70 @@ rngTest(const char *filename,
|
||||
return(ret);
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * Parse an RNG schemas with a custom RNG_INCLUDE_LIMIT
|
||||
+ *
|
||||
+ * @param filename the schemas file
|
||||
+ * @param result the file with expected result
|
||||
+ * @param err the file with error messages
|
||||
+ * @returns 0 in case of success, an error code otherwise
|
||||
+ */
|
||||
+static int
|
||||
+rngIncludeTest(const char *filename,
|
||||
+ const char *resul ATTRIBUTE_UNUSED,
|
||||
+ const char *errr ATTRIBUTE_UNUSED,
|
||||
+ int options ATTRIBUTE_UNUSED) {
|
||||
+ xmlRelaxNGParserCtxtPtr ctxt;
|
||||
+ xmlRelaxNGPtr schemas;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ /* first compile the schemas if possible */
|
||||
+ ctxt = xmlRelaxNGNewParserCtxt(filename);
|
||||
+ xmlRelaxNGSetParserStructuredErrors(ctxt, testStructuredErrorHandler,
|
||||
+ NULL);
|
||||
+
|
||||
+ /* Should work */
|
||||
+ schemas = xmlRelaxNGParse(ctxt);
|
||||
+ if (schemas == NULL) {
|
||||
+ testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n",
|
||||
+ filename);
|
||||
+ ret = -1;
|
||||
+ goto done;
|
||||
+ }
|
||||
+ xmlRelaxNGFree(schemas);
|
||||
+ xmlRelaxNGFreeParserCtxt(ctxt);
|
||||
+
|
||||
+ ctxt = xmlRelaxNGNewParserCtxt(filename);
|
||||
+ /* Should fail */
|
||||
+ xmlRelaxParserSetIncLImit(ctxt, 2);
|
||||
+ xmlRelaxNGSetParserStructuredErrors(ctxt, testStructuredErrorHandler,
|
||||
+ NULL);
|
||||
+ schemas = xmlRelaxNGParse(ctxt);
|
||||
+ if (schemas != NULL) {
|
||||
+ ret = -1;
|
||||
+ xmlRelaxNGFree(schemas);
|
||||
+ }
|
||||
+ xmlRelaxNGFreeParserCtxt(ctxt);
|
||||
+
|
||||
+ ctxt = xmlRelaxNGNewParserCtxt(filename);
|
||||
+ /* Should work */
|
||||
+ xmlRelaxParserSetIncLImit(ctxt, 3);
|
||||
+ xmlRelaxNGSetParserStructuredErrors(ctxt, testStructuredErrorHandler,
|
||||
+ NULL);
|
||||
+ schemas = xmlRelaxNGParse(ctxt);
|
||||
+ if (schemas == NULL) {
|
||||
+ testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n",
|
||||
+ filename);
|
||||
+ ret = -1;
|
||||
+ goto done;
|
||||
+ }
|
||||
+ xmlRelaxNGFree(schemas);
|
||||
+
|
||||
+done:
|
||||
+ xmlRelaxNGFreeParserCtxt(ctxt);
|
||||
+ return(ret);
|
||||
+}
|
||||
+
|
||||
#ifdef LIBXML_READER_ENABLED
|
||||
/**
|
||||
* rngStreamTest:
|
||||
@@ -4873,6 +4937,9 @@ testDesc testDescriptions[] = {
|
||||
{ "Relax-NG regression tests" ,
|
||||
rngTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
|
||||
XML_PARSE_DTDATTR | XML_PARSE_NOENT },
|
||||
+ { "Relax-NG include limit tests" ,
|
||||
+ rngIncludeTest, "./test/relaxng/include/include-limit.rng", NULL, NULL, NULL,
|
||||
+ 0 },
|
||||
#ifdef LIBXML_READER_ENABLED
|
||||
{ "Relax-NG streaming regression tests" ,
|
||||
rngStreamTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
|
||||
Index: libxml2-2.13.8/test/relaxng/include/include-limit.rng
|
||||
===================================================================
|
||||
--- /dev/null
|
||||
+++ libxml2-2.13.8/test/relaxng/include/include-limit.rng
|
||||
@@ -0,0 +1,4 @@
|
||||
+<?xml version="1.0" encoding="UTF-8"?>
|
||||
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
||||
+ <include href="include-limit_1.rng"/>
|
||||
+</grammar>
|
||||
Index: libxml2-2.13.8/test/relaxng/include/include-limit_1.rng
|
||||
===================================================================
|
||||
--- /dev/null
|
||||
+++ libxml2-2.13.8/test/relaxng/include/include-limit_1.rng
|
||||
@@ -0,0 +1,4 @@
|
||||
+<?xml version="1.0" encoding="UTF-8"?>
|
||||
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
||||
+ <include href="include-limit_2.rng"/>
|
||||
+</grammar>
|
||||
Index: libxml2-2.13.8/test/relaxng/include/include-limit_2.rng
|
||||
===================================================================
|
||||
--- /dev/null
|
||||
+++ libxml2-2.13.8/test/relaxng/include/include-limit_2.rng
|
||||
@@ -0,0 +1,4 @@
|
||||
+<?xml version="1.0" encoding="UTF-8"?>
|
||||
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
||||
+ <include href="include-limit_3.rng"/>
|
||||
+</grammar>
|
||||
Index: libxml2-2.13.8/test/relaxng/include/include-limit_3.rng
|
||||
===================================================================
|
||||
--- /dev/null
|
||||
+++ libxml2-2.13.8/test/relaxng/include/include-limit_3.rng
|
||||
@@ -0,0 +1,8 @@
|
||||
+<?xml version="1.0" encoding="UTF-8"?>
|
||||
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
||||
+ <start>
|
||||
+ <element name="root">
|
||||
+ <empty/>
|
||||
+ </element>
|
||||
+ </start>
|
||||
+</grammar>
|
||||
@@ -1,3 +1,11 @@
|
||||
-------------------------------------------------------------------
|
||||
Thu Jan 22 13:13:34 UTC 2026 - Daniel Garcia <daniel.garcia@suse.com>
|
||||
|
||||
- Add patch libxml2-CVE-2026-0989.patch, to fix call stack exhaustion
|
||||
leading to application crash due to RelaxNG parser not limiting the
|
||||
recursion depth when resolving `<include>` directives
|
||||
CVE-2026-0989, bsc#1256805, https://gitlab.gnome.org/GNOME/libxml2/-/merge_requests/374
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Fri Jul 18 11:12:01 UTC 2025 - pgajdos@suse.com
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@ Patch0: libxml2-python3-unicode-errors.patch
|
||||
# PATCH-FIX-UPSTREAM libxml2-python3-string-null-check.patch bsc#1065270 mgorse@suse.com
|
||||
# https://gitlab.gnome.org/GNOME/libxml2/-/merge_requests/15
|
||||
Patch1: libxml2-python3-string-null-check.patch
|
||||
# PATCH-FIX-UPSTREAM libxml2-CVE-2026-0989.patch bsc#1256805 daniel.garcia@suse.com
|
||||
# https://gitlab.gnome.org/GNOME/libxml2/-/merge_requests/374
|
||||
Patch2: libxml2-CVE-2026-0989.patch
|
||||
|
||||
#
|
||||
### -- openSUSE patches range from 1000 to 1999 -- ###
|
||||
|
||||
Reference in New Issue
Block a user