ad6da4ad66
Resolves: RHEL-66589
645 lines
23 KiB
Diff
645 lines
23 KiB
Diff
From b3758bd21223b97c042cae7bd26a66cde081ea98 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)
|
|
---
|
|
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 e683eb8f701..989b5b3dd24 100644
|
|
--- a/ext/dom/document.c
|
|
+++ b/ext/dom/document.c
|
|
@@ -1459,6 +1459,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;
|
|
@@ -1759,7 +1760,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
|
|
@@ -1799,6 +1802,7 @@ PHP_FUNCTION(dom_document_validate)
|
|
|
|
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
|
|
|
|
+ PHP_LIBXML_SANITIZE_GLOBALS(validate);
|
|
cvp = xmlNewValidCtxt();
|
|
|
|
cvp->userData = NULL;
|
|
@@ -1810,6 +1814,7 @@ PHP_FUNCTION(dom_document_validate)
|
|
} else {
|
|
RETVAL_FALSE;
|
|
}
|
|
+ PHP_LIBXML_RESTORE_GLOBALS(validate);
|
|
|
|
xmlFreeValidCtxt(cvp);
|
|
|
|
@@ -1844,14 +1849,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;
|
|
}
|
|
@@ -1872,6 +1881,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;
|
|
@@ -1890,11 +1900,13 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type
|
|
valid_opts |= XML_SCHEMA_VAL_VC_I_CREATE;
|
|
}
|
|
|
|
+ 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;
|
|
@@ -1965,12 +1977,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;
|
|
@@ -2069,6 +2083,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 9b222586ac5..711c42f939d 100644
|
|
--- a/ext/dom/documentfragment.c
|
|
+++ b/ext/dom/documentfragment.c
|
|
@@ -131,7 +131,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 00000000000..b28afd4694e
|
|
--- /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--
|
|
+<?php
|
|
+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');
|
|
+?>
|
|
+--FILE--
|
|
+<?php
|
|
+
|
|
+$xml = "<?xml version='1.0'?><!DOCTYPE root [<!ENTITY % bork SYSTEM \"php://nope\"> %bork;]><nothing/>";
|
|
+
|
|
+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 cf936e95de1..92028d5703e 100644
|
|
--- a/ext/libxml/php_libxml.h
|
|
+++ b/ext/libxml/php_libxml.h
|
|
@@ -121,6 +121,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 2cdff0e648d..101a9d8fd8c 100644
|
|
--- a/ext/simplexml/simplexml.c
|
|
+++ b/ext/simplexml/simplexml.c
|
|
@@ -2194,7 +2194,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;
|
|
@@ -2248,7 +2250,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;
|
|
@@ -2298,7 +2302,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 00000000000..2152e012328
|
|
--- /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--
|
|
+<?php
|
|
+if (!extension_loaded('libxml')) die('skip libxml extension not available');
|
|
+if (!extension_loaded('simplexml')) die('skip simplexml extension not available');
|
|
+if (!extension_loaded('zend-test')) die('skip zend-test extension not available');
|
|
+?>
|
|
+--FILE--
|
|
+<?php
|
|
+
|
|
+$xml = "<?xml version='1.0'?><!DOCTYPE root [<!ENTITY % bork SYSTEM \"php://nope\"> %bork;]><nothing/>";
|
|
+
|
|
+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--
|
|
+<?php
|
|
+@unlink("libxml_global_state_entity_loader_bypass.tmp");
|
|
+?>
|
|
+--EXPECT--
|
|
+--- String test ---
|
|
+--- Constructor test ---
|
|
+--- File test ---
|
|
+Done
|
|
diff --git a/ext/soap/php_xml.c b/ext/soap/php_xml.c
|
|
index 18a266179b7..1bb7fa00a37 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;
|
|
@@ -141,6 +142,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 fc4525650fc..57eb00dd429 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;
|
|
@@ -471,6 +472,7 @@ XML_ParserCreate_MM(const XML_Char *encoding, const XML_Memory_Handling_Suite *m
|
|
return NULL;
|
|
}
|
|
|
|
+ php_libxml_sanitize_parse_ctxt_options(parser->parser);
|
|
xmlCtxtUseOptions(parser->parser, XML_PARSE_OLDSAX);
|
|
|
|
parser->parser->replaceEntities = 1;
|
|
diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c
|
|
index ecc81ad1470..51d6bb9c9f2 100644
|
|
--- a/ext/xmlreader/php_xmlreader.c
|
|
+++ b/ext/xmlreader/php_xmlreader.c
|
|
@@ -304,6 +304,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,
|
|
@@ -312,6 +313,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;
|
|
}
|
|
@@ -881,7 +883,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) {
|
|
@@ -958,7 +962,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;
|
|
@@ -1082,6 +1088,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) {
|
|
@@ -1100,9 +1107,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 00000000000..e9ffb04c2bb
|
|
--- /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--
|
|
+<?php
|
|
+if (!extension_loaded('libxml')) die('skip libxml extension not available');
|
|
+if (!extension_loaded('xmlreader')) die('skip xmlreader extension not available');
|
|
+if (!extension_loaded('zend-test')) die('skip zend-test extension not available');
|
|
+?>
|
|
+--FILE--
|
|
+<?php
|
|
+
|
|
+$xml = "<?xml version='1.0'?><!DOCTYPE root [<!ENTITY % bork SYSTEM \"php://nope\"> %bork;]><nothing/>";
|
|
+
|
|
+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--
|
|
+<?php
|
|
+@unlink("libxml_global_state_entity_loader_bypass.tmp");
|
|
+?>
|
|
+--EXPECT--
|
|
+--- String test ---
|
|
+--- File test ---
|
|
+Done
|
|
diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c
|
|
index 079920d0ffa..2d95b2ff4bb 100644
|
|
--- a/ext/xsl/xsltprocessor.c
|
|
+++ b/ext/xsl/xsltprocessor.c
|
|
@@ -398,7 +398,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;
|
|
|
|
@@ -421,13 +421,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 ef1d507acf7be23d7624dc3c891683b2218feb51 Mon Sep 17 00:00:00 2001
|
|
From: Remi Collet <remi@remirepo.net>
|
|
Date: Tue, 1 Aug 2023 07:22:33 +0200
|
|
Subject: [PATCH 3/4] NEWS
|
|
|
|
---
|
|
NEWS | 10 ++++++++++
|
|
1 file changed, 10 insertions(+)
|
|
|
|
diff --git a/NEWS b/NEWS
|
|
index 899644b3d63..4f88029a7d6 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
|
|
|
|
From 24e669e790e6aebd219c9a9fa19017455c8646b4 Mon Sep 17 00:00:00 2001
|
|
From: Remi Collet <remi@remirepo.net>
|
|
Date: Tue, 1 Aug 2023 07:37:25 +0200
|
|
Subject: [PATCH 4/4] backport zend_test changes
|
|
(zend_test_override_libxml_global_state)
|
|
|
|
---
|
|
...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 b28afd4694e..7fc2a249ac7 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--
|
|
<?php
|
|
diff --git a/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt
|
|
index 2152e012328..54f9d4941eb 100644
|
|
--- a/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt
|
|
+++ b/ext/simplexml/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('simplexml')) die('skip simplexml 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--
|
|
<?php
|
|
diff --git a/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt
|
|
index e9ffb04c2bb..b0120b325ef 100644
|
|
--- a/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt
|
|
+++ b/ext/xmlreader/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('xmlreader')) die('skip xmlreader 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--
|
|
<?php
|
|
@@ -15,11 +16,11 @@ libxml_use_internal_errors(true);
|
|
zend_test_override_libxml_global_state();
|
|
|
|
echo "--- String test ---\n";
|
|
-$reader = XMLReader::xml($xml);
|
|
+$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 = @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 4f81adc6ac1..cdfc15571c0 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 <libxml/globals.h>
|
|
+# include <libxml/parser.h>
|
|
+#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 */
|
|
@@ -297,6 +316,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
|
|
|