Projects
home:rottame:vhosts-ng:php74
php7
php7-CVE-2026-91765.patch
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File php7-CVE-2026-91765.patch of Package php7
From 3655b79c7bfa90db004d75a3b282ed0f538f4836 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <alex.daubois@gmail.com> Date: Thu, 17 Sep 2026 08:09:08 +0200 Subject: [PATCH] Fix GHSA-rgrp-mwpx-f6rm: unbounded recursion in ext/soap XML parsing and decoding --- ext/soap/php_encoding.c | 7 ++ ext/soap/php_soap.h | 3 + ext/soap/php_xml.c | 102 ++++++++++++++---- ext/soap/soap.c | 1 + .../tests/GHSA-rgrp-mwpx-f6rm-href-chain.phpt | 47 ++++++++ .../tests/GHSA-rgrp-mwpx-f6rm-href-cycle.phpt | 43 ++++++++ ext/soap/tests/GHSA-rgrp-mwpx-f6rm.phpt | 89 +++++++++++++++ 7 files changed, 272 insertions(+), 20 deletions(-) create mode 100644 ext/soap/tests/GHSA-rgrp-mwpx-f6rm-href-chain.phpt create mode 100644 ext/soap/tests/GHSA-rgrp-mwpx-f6rm-href-cycle.phpt create mode 100644 ext/soap/tests/GHSA-rgrp-mwpx-f6rm.phpt Index: php-7.4.33/ext/soap/php_encoding.c =================================================================== --- php-7.4.33.orig/ext/soap/php_encoding.c +++ php-7.4.33/ext/soap/php_encoding.c @@ -558,7 +558,12 @@ static zval *master_to_zval_int(zval *re } } if (encode->to_zval) { + if (SOAP_GLOBAL(decode_depth) >= SOAP_MAX_DECODE_DEPTH) { + soap_error0(E_ERROR, "Encoding: Nesting level too deep"); + } + SOAP_GLOBAL(decode_depth)++; ret = encode->to_zval(ret, &encode->details, data); + SOAP_GLOBAL(decode_depth)--; } return ret; } @@ -3424,6 +3429,7 @@ void encode_reset_ns() { SOAP_GLOBAL(cur_uniq_ns) = 0; SOAP_GLOBAL(cur_uniq_ref) = 0; + SOAP_GLOBAL(decode_depth) = 0; if (SOAP_GLOBAL(ref_map)) { zend_hash_destroy(SOAP_GLOBAL(ref_map)); } else { @@ -3436,6 +3442,7 @@ void encode_finish() { SOAP_GLOBAL(cur_uniq_ns) = 0; SOAP_GLOBAL(cur_uniq_ref) = 0; + SOAP_GLOBAL(decode_depth) = 0; if (SOAP_GLOBAL(ref_map)) { zend_hash_destroy(SOAP_GLOBAL(ref_map)); efree(SOAP_GLOBAL(ref_map)); Index: php-7.4.33/ext/soap/php_soap.h =================================================================== --- php-7.4.33.orig/ext/soap/php_soap.h +++ php-7.4.33/ext/soap/php_soap.h @@ -152,6 +152,8 @@ struct _soapService { #define SOAP_SSL_METHOD_SSLv3 2 #define SOAP_SSL_METHOD_SSLv23 3 +#define SOAP_MAX_XML_DEPTH 2048 +#define SOAP_MAX_DECODE_DEPTH (SOAP_MAX_XML_DEPTH * 2) ZEND_BEGIN_MODULE_GLOBALS(soap) HashTable defEncNs; /* mapping of default namespaces to prefixes */ @@ -177,6 +179,7 @@ ZEND_BEGIN_MODULE_GLOBALS(soap) HashTable wsdl_cache; int cur_uniq_ref; HashTable *ref_map; + unsigned int decode_depth; ZEND_END_MODULE_GLOBALS(soap) #ifdef ZTS Index: php-7.4.33/ext/soap/php_xml.c =================================================================== --- php-7.4.33.orig/ext/soap/php_xml.c +++ php-7.4.33/ext/soap/php_xml.c @@ -37,36 +37,72 @@ static int is_blank(const xmlChar* str) return 1; } -/* removes all empty text, comments and other insignoficant nodes */ +/* removes all empty text, comments and other insignoficant nodes. + * Iterative because recursion overflows the stack on a deep document. */ static void cleanup_xml_node(xmlNodePtr node) { - xmlNodePtr trav; - xmlNodePtr del = NULL; + xmlNodePtr parent = node; + xmlNodePtr trav = node->children; - trav = node->children; while (trav != NULL) { - if (del != NULL) { - xmlUnlinkNode(del); - xmlFreeNode(del); - del = NULL; - } + xmlNodePtr next = trav->next; + if (trav->type == XML_TEXT_NODE) { if (is_blank(trav->content)) { - del = trav; + xmlUnlinkNode(trav); + xmlFreeNode(trav); } } else if ((trav->type != XML_ELEMENT_NODE) && (trav->type != XML_CDATA_SECTION_NODE)) { - del = trav; + xmlUnlinkNode(trav); + xmlFreeNode(trav); } else if (trav->children != NULL) { - cleanup_xml_node(trav); + parent = trav; + trav = trav->children; + continue; } - trav = trav->next; + + while (next == NULL) { + if (parent == node) { + return; + } + next = parent->next; + parent = parent->parent; + } + trav = next; } - if (del != NULL) { - xmlUnlinkNode(del); - xmlFreeNode(del); +} + +#if LIBXML_VERSION < 21300 +static int is_nesting_too_deep(xmlNodePtr node) +{ + xmlNodePtr trav = node->children; + unsigned int depth = 0; + + while (trav != NULL) { + /* An entity reference borrows its declaration as child list, and that + * declaration hangs off the DTD, so descending leaves the document. */ + if (trav->children != NULL && + trav->type != XML_ENTITY_REF_NODE && + trav->type != XML_DTD_NODE) { + if (++depth > SOAP_MAX_XML_DEPTH) { + return TRUE; + } + trav = trav->children; + continue; + } + while (trav->next == NULL) { + trav = trav->parent; + if (trav == node) { + return FALSE; + } + depth--; + } + trav = trav->next; } + return FALSE; } +#endif static void soap_ignorableWhitespace(void *ctx, const xmlChar *ch, int len) { @@ -124,6 +160,15 @@ xmlDocPtr soap_xmlParseFile(const char * */ if (ret) { +#if LIBXML_VERSION < 21300 + if (is_nesting_too_deep((xmlNodePtr)ret)) { + /* php_sdl.c reports xmlGetLastError() as the reason, and libxml2 did + * not fail here, so drop the error an earlier parse left behind. */ + xmlResetLastError(); + xmlFreeDoc(ret); + return NULL; + } +#endif cleanup_xml_node((xmlNodePtr)ret); } return ret; @@ -171,6 +216,13 @@ xmlDocPtr soap_xmlParseMemory(const void xmlCleanupParser(); */ +#if LIBXML_VERSION < 21300 + if (ret && is_nesting_too_deep((xmlNodePtr)ret)) { + xmlFreeDoc(ret); + ret = NULL; + } +#endif + /* if (ret) { cleanup_xml_node((xmlNodePtr)ret); @@ -293,6 +345,8 @@ xmlNodePtr get_node_with_attribute_ex(xm xmlNodePtr get_node_with_attribute_recursive_ex(xmlNodePtr node, char *name, char *name_ns, char *attribute, char *value, char *attr_ns) { + unsigned int depth = 0; + while (node != NULL) { if (node_is_equal_ex(node, name, name_ns)) { xmlAttrPtr attr = get_attribute_ex(node->properties, attribute, attr_ns); @@ -300,11 +354,19 @@ xmlNodePtr get_node_with_attribute_recur return node; } } - if (node->children != NULL) { - xmlNodePtr tmp = get_node_with_attribute_recursive_ex(node->children, name, name_ns, attribute, value, attr_ns); - if (tmp) { - return tmp; + if (node->children != NULL && + node->type != XML_ENTITY_REF_NODE && + node->type != XML_DTD_NODE) { + node = node->children; + depth++; + continue; + } + while (node->next == NULL) { + if (depth == 0) { + return NULL; } + node = node->parent; + depth--; } node = node->next; } Index: php-7.4.33/ext/soap/soap.c =================================================================== --- php-7.4.33.orig/ext/soap/soap.c +++ php-7.4.33/ext/soap/soap.c @@ -579,6 +579,7 @@ static void php_soap_init_globals(zend_s soap_globals->soap_version = SOAP_1_1; soap_globals->mem_cache = NULL; soap_globals->ref_map = NULL; + soap_globals->decode_depth = 0; } PHP_MSHUTDOWN_FUNCTION(soap)
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.