From 4fb61f06b1aff89a4d7e548c37ffa5bf573270c3 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 15 Jul 2023 17:33:52 +0200 Subject: [PATCH 2/4] Sanitize libxml2 globals before parsing Fixes GHSA-3qrf-m4j2-pcrr. To parse a document with libxml2, you first need to create a parsing context. The parsing context contains parsing options (e.g. XML_NOENT to substitute entities) that the application (in this case PHP) can set. Unfortunately, libxml2 also supports providing default set options. For example, if you call xmlSubstituteEntitiesDefault(1) then the XML_NOENT option will be added to the parsing options every time you create a parsing context **even if the application never requested XML_NOENT**. Third party extensions can override these globals, in particular the substitute entity global. This causes entity substitution to be unexpectedly active. Fix it by setting the parsing options to a sane known value. For API calls that depend on global state we introduce PHP_LIBXML_SANITIZE_GLOBALS() and PHP_LIBXML_RESTORE_GLOBALS(). For other APIs that work directly with a context we introduce php_libxml_sanitize_parse_ctxt_options(). (cherry picked from commit c283c3ab0ba45d21b2b8745c1f9c7cbfe771c975) (cherry picked from commit b3758bd21223b97c042cae7bd26a66cde081ea98) --- ext/dom/document.c | 15 ++++++++ ext/dom/documentfragment.c | 2 ++ ...xml_global_state_entity_loader_bypass.phpt | 36 +++++++++++++++++++ ext/libxml/php_libxml.h | 36 +++++++++++++++++++ ext/simplexml/simplexml.c | 6 ++++ ...xml_global_state_entity_loader_bypass.phpt | 36 +++++++++++++++++++ ext/soap/php_xml.c | 2 ++ ext/xml/compat.c | 2 ++ ext/xmlreader/php_xmlreader.c | 9 +++++ ...xml_global_state_entity_loader_bypass.phpt | 35 ++++++++++++++++++ ext/xsl/xsltprocessor.c | 9 +++-- 11 files changed, 183 insertions(+), 5 deletions(-) create mode 100644 ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt create mode 100644 ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt create mode 100644 ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt diff --git a/ext/dom/document.c b/ext/dom/document.c index 3f34e5370d..238b1381cc 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -1436,6 +1436,7 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so options |= XML_PARSE_NOBLANKS; } + php_libxml_sanitize_parse_ctxt_options(ctxt); xmlCtxtUseOptions(ctxt, options); ctxt->recovery = recover; @@ -1733,7 +1734,9 @@ PHP_FUNCTION(dom_document_xinclude) DOM_GET_OBJ(docp, id, xmlDocPtr, intern); + PHP_LIBXML_SANITIZE_GLOBALS(xinclude); err = xmlXIncludeProcessFlags(docp, (int)flags); + PHP_LIBXML_RESTORE_GLOBALS(xinclude); /* XML_XINCLUDE_START and XML_XINCLUDE_END nodes need to be removed as these are added via xmlXIncludeProcess to mark beginning and ending of xincluded document @@ -1772,6 +1775,7 @@ PHP_FUNCTION(dom_document_validate) DOM_GET_OBJ(docp, id, xmlDocPtr, intern); + PHP_LIBXML_SANITIZE_GLOBALS(validate); cvp = xmlNewValidCtxt(); cvp->userData = NULL; @@ -1783,6 +1787,7 @@ PHP_FUNCTION(dom_document_validate) } else { RETVAL_FALSE; } + PHP_LIBXML_RESTORE_GLOBALS(validate); xmlFreeValidCtxt(cvp); @@ -1816,14 +1821,18 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type DOM_GET_OBJ(docp, id, xmlDocPtr, intern); + PHP_LIBXML_SANITIZE_GLOBALS(new_parser_ctxt); + switch (type) { case DOM_LOAD_FILE: if (CHECK_NULL_PATH(source, source_len)) { + PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt); php_error_docref(NULL, E_WARNING, "Invalid Schema file source"); RETURN_FALSE; } valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN); if (!valid_file) { + PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt); php_error_docref(NULL, E_WARNING, "Invalid Schema file source"); RETURN_FALSE; } @@ -1844,6 +1853,7 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type parser); sptr = xmlSchemaParse(parser); xmlSchemaFreeParserCtxt(parser); + PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt); if (!sptr) { php_error_docref(NULL, E_WARNING, "Invalid Schema"); RETURN_FALSE; @@ -1864,11 +1874,13 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type } #endif + PHP_LIBXML_SANITIZE_GLOBALS(validate); xmlSchemaSetValidOptions(vptr, valid_opts); xmlSchemaSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr); is_valid = xmlSchemaValidateDoc(vptr, docp); xmlSchemaFree(sptr); xmlSchemaFreeValidCtxt(vptr); + PHP_LIBXML_RESTORE_GLOBALS(validate); if (is_valid == 0) { RETURN_TRUE; @@ -1938,12 +1950,14 @@ static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int typ return; } + PHP_LIBXML_SANITIZE_GLOBALS(parse); xmlRelaxNGSetParserErrors(parser, (xmlRelaxNGValidityErrorFunc) php_libxml_error_handler, (xmlRelaxNGValidityWarningFunc) php_libxml_error_handler, parser); sptr = xmlRelaxNGParse(parser); xmlRelaxNGFreeParserCtxt(parser); + PHP_LIBXML_RESTORE_GLOBALS(parse); if (!sptr) { php_error_docref(NULL, E_WARNING, "Invalid RelaxNG"); RETURN_FALSE; @@ -2042,6 +2056,7 @@ static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */ ctxt->sax->error = php_libxml_ctx_error; ctxt->sax->warning = php_libxml_ctx_warning; } + php_libxml_sanitize_parse_ctxt_options(ctxt); if (options) { htmlCtxtUseOptions(ctxt, (int)options); } diff --git a/ext/dom/documentfragment.c b/ext/dom/documentfragment.c index 0c289565a2..f12d191ddf 100644 --- a/ext/dom/documentfragment.c +++ b/ext/dom/documentfragment.c @@ -132,7 +132,9 @@ PHP_METHOD(domdocumentfragment, appendXML) { } if (data) { + PHP_LIBXML_SANITIZE_GLOBALS(parse); err = xmlParseBalancedChunkMemory(nodep->doc, NULL, NULL, 0, (xmlChar *) data, &lst); + PHP_LIBXML_RESTORE_GLOBALS(parse); if (err != 0) { RETURN_FALSE; } diff --git a/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt new file mode 100644 index 0000000000..b28afd4694 --- /dev/null +++ b/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt @@ -0,0 +1,36 @@ +--TEST-- +GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass) +--SKIPIF-- + +--FILE-- + %bork;]>"; + +libxml_use_internal_errors(true); + +function parseXML($xml) { + $doc = new DOMDocument(); + @$doc->loadXML($xml); + $doc->createDocumentFragment()->appendXML("&bork;"); + foreach (libxml_get_errors() as $error) { + var_dump(trim($error->message)); + } +} + +parseXML($xml); +zend_test_override_libxml_global_state(); +parseXML($xml); + +echo "Done\n"; + +?> +--EXPECT-- +string(25) "Entity 'bork' not defined" +string(25) "Entity 'bork' not defined" +string(25) "Entity 'bork' not defined" +Done diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h index 7cc7271db2..bb2ef2d606 100644 --- a/ext/libxml/php_libxml.h +++ b/ext/libxml/php_libxml.h @@ -120,6 +120,42 @@ PHP_LIBXML_API void php_libxml_shutdown(void); ZEND_TSRMLS_CACHE_EXTERN() #endif +/* Other extension may override the global state options, these global options + * are copied initially to ctxt->options. Set the options to a known good value. + * See libxml2 globals.c and parserInternals.c. + * The unique_name argument allows multiple sanitizes and restores within the + * same function, even nested is necessary. */ +#define PHP_LIBXML_SANITIZE_GLOBALS(unique_name) \ + int xml_old_loadsubset_##unique_name = xmlLoadExtDtdDefaultValue; \ + xmlLoadExtDtdDefaultValue = 0; \ + int xml_old_validate_##unique_name = xmlDoValidityCheckingDefaultValue; \ + xmlDoValidityCheckingDefaultValue = 0; \ + int xml_old_pedantic_##unique_name = xmlPedanticParserDefault(0); \ + int xml_old_substitute_##unique_name = xmlSubstituteEntitiesDefault(0); \ + int xml_old_linenrs_##unique_name = xmlLineNumbersDefault(0); \ + int xml_old_blanks_##unique_name = xmlKeepBlanksDefault(1); + +#define PHP_LIBXML_RESTORE_GLOBALS(unique_name) \ + xmlLoadExtDtdDefaultValue = xml_old_loadsubset_##unique_name; \ + xmlDoValidityCheckingDefaultValue = xml_old_validate_##unique_name; \ + (void) xmlPedanticParserDefault(xml_old_pedantic_##unique_name); \ + (void) xmlSubstituteEntitiesDefault(xml_old_substitute_##unique_name); \ + (void) xmlLineNumbersDefault(xml_old_linenrs_##unique_name); \ + (void) xmlKeepBlanksDefault(xml_old_blanks_##unique_name); + +/* Alternative for above, working directly on the context and not setting globals. + * Generally faster because no locking is involved, and this has the advantage that it sets the options to a known good value. */ +static zend_always_inline void php_libxml_sanitize_parse_ctxt_options(xmlParserCtxtPtr ctxt) +{ + ctxt->loadsubset = 0; + ctxt->validate = 0; + ctxt->pedantic = 0; + ctxt->replaceEntities = 0; + ctxt->linenumbers = 0; + ctxt->keepBlanks = 1; + ctxt->options = 0; +} + #else /* HAVE_LIBXML */ #define libxml_module_ptr NULL #endif diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index ab394b5c83..de718ced93 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -2175,7 +2175,9 @@ PHP_FUNCTION(simplexml_load_file) RETURN_FALSE; } + PHP_LIBXML_SANITIZE_GLOBALS(read_file); docp = xmlReadFile(filename, NULL, (int)options); + PHP_LIBXML_RESTORE_GLOBALS(read_file); if (!docp) { RETURN_FALSE; @@ -2229,7 +2231,9 @@ PHP_FUNCTION(simplexml_load_string) RETURN_FALSE; } + PHP_LIBXML_SANITIZE_GLOBALS(read_memory); docp = xmlReadMemory(data, (int)data_len, NULL, NULL, (int)options); + PHP_LIBXML_RESTORE_GLOBALS(read_memory); if (!docp) { RETURN_FALSE; @@ -2279,7 +2283,9 @@ SXE_METHOD(__construct) return; } + PHP_LIBXML_SANITIZE_GLOBALS(read_file_or_memory); docp = is_url ? xmlReadFile(data, NULL, (int)options) : xmlReadMemory(data, (int)data_len, NULL, NULL, (int)options); + PHP_LIBXML_RESTORE_GLOBALS(read_file_or_memory); if (!docp) { ((php_libxml_node_object *)sxe)->document = NULL; diff --git a/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt new file mode 100644 index 0000000000..2152e01232 --- /dev/null +++ b/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt @@ -0,0 +1,36 @@ +--TEST-- +GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass) +--SKIPIF-- + +--FILE-- + %bork;]>"; + +libxml_use_internal_errors(true); +zend_test_override_libxml_global_state(); + +echo "--- String test ---\n"; +simplexml_load_string($xml); +echo "--- Constructor test ---\n"; +new SimpleXMLElement($xml); +echo "--- File test ---\n"; +file_put_contents("libxml_global_state_entity_loader_bypass.tmp", $xml); +simplexml_load_file("libxml_global_state_entity_loader_bypass.tmp"); + +echo "Done\n"; + +?> +--CLEAN-- + +--EXPECT-- +--- String test --- +--- Constructor test --- +--- File test --- +Done diff --git a/ext/soap/php_xml.c b/ext/soap/php_xml.c index a1ab525de3..7ebfbc2f71 100644 --- a/ext/soap/php_xml.c +++ b/ext/soap/php_xml.c @@ -93,6 +93,7 @@ xmlDocPtr soap_xmlParseFile(const char *filename) if (ctxt) { zend_bool old; + php_libxml_sanitize_parse_ctxt_options(ctxt); ctxt->keepBlanks = 0; ctxt->sax->ignorableWhitespace = soap_ignorableWhitespace; ctxt->sax->comment = soap_Comment; @@ -143,6 +144,7 @@ xmlDocPtr soap_xmlParseMemory(const void *buf, size_t buf_size) if (ctxt) { zend_bool old; + php_libxml_sanitize_parse_ctxt_options(ctxt); ctxt->sax->ignorableWhitespace = soap_ignorableWhitespace; ctxt->sax->comment = soap_Comment; ctxt->sax->warning = NULL; diff --git a/ext/xml/compat.c b/ext/xml/compat.c index 450bb1b52c..82dff1f75a 100644 --- a/ext/xml/compat.c +++ b/ext/xml/compat.c @@ -19,6 +19,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && (defined(HAVE_XML) || defined(HAVE_XMLRPC)) && !defined(HAVE_LIBEXPAT) #include "expat_compat.h" +#include "ext/libxml/php_libxml.h" typedef struct _php_xml_ns { xmlNsPtr nsptr; @@ -476,6 +477,7 @@ XML_ParserCreate_MM(const XML_Char *encoding, const XML_Memory_Handling_Suite *m parser->parser->charset = XML_CHAR_ENCODING_NONE; #endif + php_libxml_sanitize_parse_ctxt_options(parser->parser); #if LIBXML_VERSION >= 20703 xmlCtxtUseOptions(parser->parser, XML_PARSE_OLDSAX); #endif diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c index e03273709f..ee219aea36 100644 --- a/ext/xmlreader/php_xmlreader.c +++ b/ext/xmlreader/php_xmlreader.c @@ -290,6 +290,7 @@ static xmlRelaxNGPtr _xmlreader_get_relaxNG(char *source, size_t source_len, siz return NULL; } + PHP_LIBXML_SANITIZE_GLOBALS(parse); if (error_func || warn_func) { xmlRelaxNGSetParserErrors(parser, (xmlRelaxNGValidityErrorFunc) error_func, @@ -298,6 +299,7 @@ static xmlRelaxNGPtr _xmlreader_get_relaxNG(char *source, size_t source_len, siz } sptr = xmlRelaxNGParse(parser); xmlRelaxNGFreeParserCtxt(parser); + PHP_LIBXML_RESTORE_GLOBALS(parse); return sptr; } @@ -870,7 +872,9 @@ PHP_METHOD(xmlreader, open) valid_file = _xmlreader_get_valid_file_path(source, resolved_path, MAXPATHLEN ); if (valid_file) { + PHP_LIBXML_SANITIZE_GLOBALS(reader_for_file); reader = xmlReaderForFile(valid_file, encoding, options); + PHP_LIBXML_RESTORE_GLOBALS(reader_for_file); } if (reader == NULL) { @@ -948,7 +952,9 @@ PHP_METHOD(xmlreader, setSchema) intern = Z_XMLREADER_P(id); if (intern && intern->ptr) { + PHP_LIBXML_SANITIZE_GLOBALS(schema); retval = xmlTextReaderSchemaValidate(intern->ptr, source); + PHP_LIBXML_RESTORE_GLOBALS(schema); if (retval == 0) { RETURN_TRUE; @@ -1068,6 +1074,7 @@ PHP_METHOD(xmlreader, XML) } uri = (char *) xmlCanonicPath((const xmlChar *) resolved_path); } + PHP_LIBXML_SANITIZE_GLOBALS(text_reader); reader = xmlNewTextReader(inputbfr, uri); if (reader != NULL) { @@ -1088,9 +1095,11 @@ PHP_METHOD(xmlreader, XML) xmlFree(uri); } + PHP_LIBXML_RESTORE_GLOBALS(text_reader); return; } } + PHP_LIBXML_RESTORE_GLOBALS(text_reader); } if (uri) { diff --git a/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt new file mode 100644 index 0000000000..e9ffb04c2b --- /dev/null +++ b/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt @@ -0,0 +1,35 @@ +--TEST-- +GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass) +--SKIPIF-- + +--FILE-- + %bork;]>"; + +libxml_use_internal_errors(true); +zend_test_override_libxml_global_state(); + +echo "--- String test ---\n"; +$reader = XMLReader::xml($xml); +$reader->read(); +echo "--- File test ---\n"; +file_put_contents("libxml_global_state_entity_loader_bypass.tmp", $xml); +$reader = XMLReader::open("libxml_global_state_entity_loader_bypass.tmp"); +$reader->read(); + +echo "Done\n"; + +?> +--CLEAN-- + +--EXPECT-- +--- String test --- +--- File test --- +Done diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c index 182aab68d6..af72dab248 100644 --- a/ext/xsl/xsltprocessor.c +++ b/ext/xsl/xsltprocessor.c @@ -395,7 +395,7 @@ PHP_FUNCTION(xsl_xsltprocessor_import_stylesheet) xmlDoc *doc = NULL, *newdoc = NULL; xsltStylesheetPtr sheetp, oldsheetp; xsl_object *intern; - int prevSubstValue, prevExtDtdValue, clone_docu = 0; + int clone_docu = 0; xmlNode *nodep = NULL; zval *cloneDocu, member, rv; @@ -417,13 +417,12 @@ PHP_FUNCTION(xsl_xsltprocessor_import_stylesheet) stylesheet document otherwise the node proxies will be a mess */ newdoc = xmlCopyDoc(doc, 1); xmlNodeSetBase((xmlNodePtr) newdoc, (xmlChar *)doc->URL); - prevSubstValue = xmlSubstituteEntitiesDefault(1); - prevExtDtdValue = xmlLoadExtDtdDefaultValue; + PHP_LIBXML_SANITIZE_GLOBALS(parse); + xmlSubstituteEntitiesDefault(1); xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS; sheetp = xsltParseStylesheetDoc(newdoc); - xmlSubstituteEntitiesDefault(prevSubstValue); - xmlLoadExtDtdDefaultValue = prevExtDtdValue; + PHP_LIBXML_RESTORE_GLOBALS(parse); if (!sheetp) { xmlFreeDoc(newdoc); -- 2.41.0 From 79a97d0e2b93c40c3728d587046266989c5acc42 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Tue, 1 Aug 2023 07:37:25 +0200 Subject: [PATCH 3/4] backport zend_test changes (zend_test_override_libxml_global_state) (cherry picked from commit 24e669e790e6aebd219c9a9fa19017455c8646b4) --- ...xml_global_state_entity_loader_bypass.phpt | 1 + ...xml_global_state_entity_loader_bypass.phpt | 1 + ...xml_global_state_entity_loader_bypass.phpt | 5 +++-- ext/zend_test/test.c | 22 +++++++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt index b28afd4694..7fc2a249ac 100644 --- a/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt +++ b/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt @@ -5,6 +5,7 @@ GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass) if (!extension_loaded('libxml')) die('skip libxml extension not available'); if (!extension_loaded('dom')) die('skip dom extension not available'); if (!extension_loaded('zend-test')) die('skip zend-test extension not available'); +if (!function_exists('zend_test_override_libxml_global_state')) die('skip not for Windows'); ?> --FILE-- --FILE-- --FILE-- read(); echo "--- File test ---\n"; file_put_contents("libxml_global_state_entity_loader_bypass.tmp", $xml); -$reader = XMLReader::open("libxml_global_state_entity_loader_bypass.tmp"); +$reader = @XMLReader::open("libxml_global_state_entity_loader_bypass.tmp"); $reader->read(); echo "Done\n"; diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 87dcc90220..7f605773b7 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -25,6 +25,11 @@ #include "ext/standard/info.h" #include "php_test.h" +#if defined(HAVE_LIBXML) && !defined(PHP_WIN32) +# include +# include +#endif + static zend_class_entry *zend_test_interface; static zend_class_entry *zend_test_class; static zend_class_entry *zend_test_child_class; @@ -48,6 +53,20 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_leak_variable, 0, 0, 1) ZEND_ARG_INFO(0, variable) ZEND_END_ARG_INFO() +#if defined(HAVE_LIBXML) && !defined(PHP_WIN32) +static ZEND_FUNCTION(zend_test_override_libxml_global_state) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + xmlLoadExtDtdDefaultValue = 1; + xmlDoValidityCheckingDefaultValue = 1; + (void) xmlPedanticParserDefault(1); + (void) xmlSubstituteEntitiesDefault(1); + (void) xmlLineNumbersDefault(1); + (void) xmlKeepBlanksDefault(0); +} +#endif + ZEND_FUNCTION(zend_test_func) { /* dummy */ @@ -266,6 +285,9 @@ static const zend_function_entry zend_test_functions[] = { ZEND_FE(zend_terminate_string, arginfo_zend_terminate_string) ZEND_FE(zend_leak_bytes, NULL) ZEND_FE(zend_leak_variable, arginfo_zend_leak_variable) +#if defined(HAVE_LIBXML) && !defined(PHP_WIN32) + ZEND_FE(zend_test_override_libxml_global_state, NULL) +#endif ZEND_FE_END }; -- 2.41.0 From 3cf7c2b10e577136b267f2d90bfdff6743271c5c Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Tue, 1 Aug 2023 07:22:33 +0200 Subject: [PATCH 4/4] NEWS (cherry picked from commit ef1d507acf7be23d7624dc3c891683b2218feb51) --- NEWS | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/NEWS b/NEWS index 1950d841a5..05d9ca8f4c 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,16 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +Backported from 8.0.30 + +- Libxml: + . Fixed bug GHSA-3qrf-m4j2-pcrr (Security issue with external entity loading + in XML without enabling it). (CVE-2023-3823) (nielsdos, ilutov) + +- Phar: + . Fixed bug GHSA-jqcx-ccgc-xwhv (Buffer mismanagement in phar_dir_read()). + (CVE-2023-3824) (nielsdos) + Backported from 8.0.29 - Soap: -- 2.41.0