From 3b5321dffe23f4e0d719962c172b820d385b7a04 Mon Sep 17 00:00:00 2001 From: Sergey Panteleev Date: Tue, 23 Apr 2024 17:22:54 +0300 Subject: [PATCH 01/45] PHP-8.2 is now for PHP 8.2.20-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 087409db243ee..4c17d6e6624ab 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.2.19 +?? ??? ????, PHP 8.2.20 + + +09 May 2024, PHP 8.2.19 - Core: . Fixed bug GH-13772 (Invalid execute_data->opline pointers in observer fcall diff --git a/Zend/zend.h b/Zend/zend.h index ad8fde7a8ad1c..5b8accedcc63d 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.2.19-dev" +#define ZEND_VERSION "4.2.20-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 683029f476a94..b8b8d413421aa 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.2.19-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.2.20-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index a17d49197da51..6bdfdc571b872 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 2 -#define PHP_RELEASE_VERSION 19 +#define PHP_RELEASE_VERSION 20 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.2.19-dev" -#define PHP_VERSION_ID 80219 +#define PHP_VERSION "8.2.20-dev" +#define PHP_VERSION_ID 80220 From d670e131df262db13800a026e72f7fe6fe62bcd2 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 29 Apr 2024 16:13:49 +0200 Subject: [PATCH 02/45] Fix __SANITIZE_ADDRESS__ redeclaration warning --- Zend/zend_portability.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index d59fc6d07c1db..6ca9b7266b6e4 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -132,7 +132,7 @@ #if defined(HAVE_LIBDL) && !defined(ZEND_WIN32) -# if __has_feature(address_sanitizer) +# if __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__) # define __SANITIZE_ADDRESS__ # endif From e878b9f39094acaa8da5a782618ab41f5293f30b Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 30 Apr 2024 20:29:37 +0200 Subject: [PATCH 03/45] Fix crashes when entity declaration is removed while still having entity references libxml doesn't do reference counting inside its node types. It's possible to remove an entity declaration out of the document, but then entity references will keep pointing to that stale declaration. This will cause crashes. One idea would be to check when a declaration is removed, to trigger a hook that updates all references. However this means we have to keep track of all references somehow, which would be a high-overhead solution. The solution in this patch makes sure that the fields are always updated before they are read. Closes GH-14089. --- NEWS | 3 + ext/dom/dom_properties.h | 5 ++ ext/dom/entityreference.c | 60 ++++++++++++++++++++ ext/dom/nodelist.c | 14 ++++- ext/dom/php_dom.c | 19 ++++++- ext/dom/php_dom.h | 1 + ext/dom/tests/entity_reference_stale_01.phpt | 41 +++++++++++++ ext/dom/tests/entity_reference_stale_02.phpt | 35 ++++++++++++ 8 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 ext/dom/tests/entity_reference_stale_01.phpt create mode 100644 ext/dom/tests/entity_reference_stale_02.phpt diff --git a/NEWS b/NEWS index 4c17d6e6624ab..580361462504a 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.2.20 +- DOM: + . Fix crashes when entity declaration is removed while still having entity + references. (nielsdos) 09 May 2024, PHP 8.2.19 diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h index 32f2419323833..9c4e2f5ee5785 100644 --- a/ext/dom/dom_properties.h +++ b/ext/dom/dom_properties.h @@ -81,6 +81,11 @@ int dom_entity_actual_encoding_read(dom_object *obj, zval *retval); int dom_entity_encoding_read(dom_object *obj, zval *retval); int dom_entity_version_read(dom_object *obj, zval *retval); +/* entity reference properties */ +int dom_entity_reference_child_read(dom_object *obj, zval *retval); +int dom_entity_reference_text_content_read(dom_object *obj, zval *retval); +int dom_entity_reference_child_nodes_read(dom_object *obj, zval *retval); + /* namednodemap properties */ int dom_namednodemap_length_read(dom_object *obj, zval *retval); diff --git a/ext/dom/entityreference.c b/ext/dom/entityreference.c index 61f0b92eedc22..30928b1c6d1bf 100644 --- a/ext/dom/entityreference.c +++ b/ext/dom/entityreference.c @@ -22,6 +22,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "dom_properties.h" /* * class DOMEntityReference extends DOMNode @@ -65,4 +66,63 @@ PHP_METHOD(DOMEntityReference, __construct) } /* }}} end DOMEntityReference::__construct */ +/* The following property handlers are necessary because of special lifetime management with entities and entity + * references. The issue is that entity references hold a reference to an entity declaration, but don't + * register that reference anywhere. When the entity declaration disappears we have no way of notifying the + * entity references. Override the property handlers for the declaration-accessing properties to fix this problem. */ + +xmlEntityPtr dom_entity_reference_fetch_and_sync_declaration(xmlNodePtr reference) +{ + xmlEntityPtr entity = xmlGetDocEntity(reference->doc, reference->name); + reference->children = (xmlNodePtr) entity; + reference->last = (xmlNodePtr) entity; + reference->content = entity ? entity->content : NULL; + return entity; +} + +int dom_entity_reference_child_read(dom_object *obj, zval *retval) +{ + xmlNodePtr nodep = dom_object_get_node(obj); + + if (nodep == NULL) { + php_dom_throw_error(INVALID_STATE_ERR, true); + return FAILURE; + } + + xmlEntityPtr entity = dom_entity_reference_fetch_and_sync_declaration(nodep); + if (entity == NULL) { + ZVAL_NULL(retval); + return SUCCESS; + } + + php_dom_create_object((xmlNodePtr) entity, retval, obj); + return SUCCESS; +} + +int dom_entity_reference_text_content_read(dom_object *obj, zval *retval) +{ + xmlNodePtr nodep = dom_object_get_node(obj); + + if (nodep == NULL) { + php_dom_throw_error(INVALID_STATE_ERR, true); + return FAILURE; + } + + dom_entity_reference_fetch_and_sync_declaration(nodep); + return dom_node_text_content_read(obj, retval); +} + +int dom_entity_reference_child_nodes_read(dom_object *obj, zval *retval) +{ + xmlNodePtr nodep = dom_object_get_node(obj); + + if (nodep == NULL) { + php_dom_throw_error(INVALID_STATE_ERR, true); + return FAILURE; + } + + dom_entity_reference_fetch_and_sync_declaration(nodep); + return dom_node_child_nodes_read(obj, retval); +} + #endif diff --git a/ext/dom/nodelist.c b/ext/dom/nodelist.c index 20f6320a54607..a8e713f67d03e 100644 --- a/ext/dom/nodelist.c +++ b/ext/dom/nodelist.c @@ -31,6 +31,16 @@ * Since: */ +static xmlNodePtr dom_nodelist_iter_start_first_child(xmlNodePtr nodep) +{ + if (nodep->type == XML_ENTITY_REF_NODE) { + /* See entityreference.c */ + dom_entity_reference_fetch_and_sync_declaration(nodep); + } + + return nodep->children; +} + int php_dom_get_nodelist_length(dom_object *obj) { dom_nnodemap_object *objmap = (dom_nnodemap_object *) obj->ptr; @@ -54,7 +64,7 @@ int php_dom_get_nodelist_length(dom_object *obj) int count = 0; if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) { - xmlNodePtr curnode = nodep->children; + xmlNodePtr curnode = dom_nodelist_iter_start_first_child(nodep); if (curnode) { count++; while (curnode->next != NULL) { @@ -128,7 +138,7 @@ void php_dom_nodelist_get_item_into_zval(dom_nnodemap_object *objmap, zend_long if (nodep) { int count = 0; if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) { - xmlNodePtr curnode = nodep->children; + xmlNodePtr curnode = dom_nodelist_iter_start_first_child(nodep); while (count < index && curnode != NULL) { count++; curnode = curnode->next; diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index c86cab99c6c79..c9123ab14dfcf 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -72,6 +72,7 @@ static HashTable classes; static HashTable dom_document_prop_handlers; static HashTable dom_documentfragment_prop_handlers; static HashTable dom_node_prop_handlers; +static HashTable dom_entity_reference_prop_handlers; static HashTable dom_nodelist_prop_handlers; static HashTable dom_namednodemap_prop_handlers; static HashTable dom_characterdata_prop_handlers; @@ -284,6 +285,14 @@ static void dom_register_prop_handler(HashTable *prop_handler, char *name, size_ zend_string_release_ex(str, 1); } +static void dom_override_prop_handler(HashTable *prop_handler, char *name, size_t name_len, dom_read_t read_func, dom_write_t write_func) +{ + dom_prop_handler hnd; + hnd.read_func = read_func; + hnd.write_func = write_func; + zend_hash_str_update_mem(prop_handler, name, name_len, &hnd, sizeof(dom_prop_handler)); +} + static zval *dom_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot) { dom_object *obj = php_dom_obj_from_obj(object); @@ -807,7 +816,14 @@ PHP_MINIT_FUNCTION(dom) dom_entityreference_class_entry = register_class_DOMEntityReference(dom_node_class_entry); dom_entityreference_class_entry->create_object = dom_objects_new; - zend_hash_add_ptr(&classes, dom_entityreference_class_entry->name, &dom_node_prop_handlers); + + zend_hash_init(&dom_entity_reference_prop_handlers, 0, NULL, dom_dtor_prop_handler, true); + zend_hash_merge(&dom_entity_reference_prop_handlers, &dom_node_prop_handlers, dom_copy_prop_handler, false); + dom_override_prop_handler(&dom_entity_reference_prop_handlers, "firstChild", sizeof("firstChild")-1, dom_entity_reference_child_read, NULL); + dom_override_prop_handler(&dom_entity_reference_prop_handlers, "lastChild", sizeof("lastChild")-1, dom_entity_reference_child_read, NULL); + dom_override_prop_handler(&dom_entity_reference_prop_handlers, "textContent", sizeof("textContent")-1, dom_entity_reference_text_content_read, NULL); + dom_override_prop_handler(&dom_entity_reference_prop_handlers, "childNodes", sizeof("childNodes")-1, dom_entity_reference_child_nodes_read, NULL); + zend_hash_add_ptr(&classes, dom_entityreference_class_entry->name, &dom_entity_reference_prop_handlers); dom_processinginstruction_class_entry = register_class_DOMProcessingInstruction(dom_node_class_entry); dom_processinginstruction_class_entry->create_object = dom_objects_new; @@ -869,6 +885,7 @@ PHP_MSHUTDOWN_FUNCTION(dom) /* {{{ */ zend_hash_destroy(&dom_document_prop_handlers); zend_hash_destroy(&dom_documentfragment_prop_handlers); zend_hash_destroy(&dom_node_prop_handlers); + zend_hash_destroy(&dom_entity_reference_prop_handlers); zend_hash_destroy(&dom_namespace_node_prop_handlers); zend_hash_destroy(&dom_nodelist_prop_handlers); zend_hash_destroy(&dom_namednodemap_prop_handlers); diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index 9a57996729dbf..ccf665c417cd8 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -139,6 +139,7 @@ xmlNode *php_dom_libxml_notation_iter(xmlHashTable *ht, int index); zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref); void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce); xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern); +xmlEntityPtr dom_entity_reference_fetch_and_sync_declaration(xmlNodePtr reference); void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc); void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc); diff --git a/ext/dom/tests/entity_reference_stale_01.phpt b/ext/dom/tests/entity_reference_stale_01.phpt new file mode 100644 index 0000000000000..dc1828c3cd999 --- /dev/null +++ b/ext/dom/tests/entity_reference_stale_01.phpt @@ -0,0 +1,41 @@ +--TEST-- +Entity references with stale entity declaration 01 +--EXTENSIONS-- +dom +--FILE-- +loadXML(<< +]> +&foo; +XML); + +$ref = $dom->documentElement->firstChild; +$decl = $ref->firstChild; + +$nodes = $ref->childNodes; +$dom->removeChild($dom->doctype); +unset($decl); + +var_dump($nodes); +var_dump($ref->firstChild); +var_dump($ref->lastChild); +var_dump($ref->textContent); +var_dump($ref->childNodes); + +?> +--EXPECT-- +object(DOMNodeList)#4 (1) { + ["length"]=> + int(0) +} +NULL +NULL +string(0) "" +object(DOMNodeList)#2 (1) { + ["length"]=> + int(0) +} diff --git a/ext/dom/tests/entity_reference_stale_02.phpt b/ext/dom/tests/entity_reference_stale_02.phpt new file mode 100644 index 0000000000000..ea93b3ca3ef90 --- /dev/null +++ b/ext/dom/tests/entity_reference_stale_02.phpt @@ -0,0 +1,35 @@ +--TEST-- +Entity references with stale entity declaration 02 +--EXTENSIONS-- +dom +--FILE-- +loadXML(<< + + +]> +&foo1; +XML); + +$ref = $dom->documentElement->firstChild; +$decl = $ref->firstChild; + +$nodes = $ref->childNodes; +$iter = $nodes->getIterator(); +$iter->next(); +$dom->removeChild($dom->doctype); +unset($decl); + +try { + $iter->current()->publicId; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Couldn't fetch DOMEntity. Node no longer exists From 30a0b0359ed8338c0e3acd1682de3cf96429e898 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 30 Apr 2024 20:50:30 +0200 Subject: [PATCH 04/45] Fix references not handled correctly in C14N Closes GH-14090. --- NEWS | 1 + ext/dom/node.c | 5 +-- ext/dom/tests/DOMNode_C14N_references.phpt | 41 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 ext/dom/tests/DOMNode_C14N_references.phpt diff --git a/NEWS b/NEWS index 580361462504a..3a95dec835134 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ PHP NEWS - DOM: . Fix crashes when entity declaration is removed while still having entity references. (nielsdos) + . Fix references not handled correctly in C14N. (nielsdos) 09 May 2024, PHP 8.2.19 diff --git a/ext/dom/node.c b/ext/dom/node.c index 973505c5b01a9..82f40860cfd35 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -1614,7 +1614,7 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ zval *tmp; char *xquery; - tmp = zend_hash_str_find(ht, "query", sizeof("query")-1); + tmp = zend_hash_str_find_deref(ht, "query", sizeof("query")-1); if (!tmp) { /* if mode == 0 then $xpath arg is 3, if mode == 1 then $xpath is 4 */ zend_argument_value_error(3 + mode, "must have a \"query\" key"); @@ -1630,12 +1630,13 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ ctxp = xmlXPathNewContext(docp); ctxp->node = nodep; - tmp = zend_hash_str_find(ht, "namespaces", sizeof("namespaces")-1); + tmp = zend_hash_str_find_deref(ht, "namespaces", sizeof("namespaces")-1); if (tmp && Z_TYPE_P(tmp) == IS_ARRAY && !HT_IS_PACKED(Z_ARRVAL_P(tmp))) { zval *tmpns; zend_string *prefix; ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(tmp), prefix, tmpns) { + ZVAL_DEREF(tmpns); if (Z_TYPE_P(tmpns) == IS_STRING) { if (prefix) { xmlXPathRegisterNs(ctxp, (xmlChar *) ZSTR_VAL(prefix), (xmlChar *) Z_STRVAL_P(tmpns)); diff --git a/ext/dom/tests/DOMNode_C14N_references.phpt b/ext/dom/tests/DOMNode_C14N_references.phpt new file mode 100644 index 0000000000000..514e22be6365a --- /dev/null +++ b/ext/dom/tests/DOMNode_C14N_references.phpt @@ -0,0 +1,41 @@ +--TEST-- +Test: Canonicalization - C14N() with references +--EXTENSIONS-- +dom +--FILE-- + + + + + + + + +EOXML; + +$dom = new DOMDocument(); +$dom->loadXML($xml); +$doc = $dom->documentElement->firstChild; + +$xpath = [ + 'query' => '(//a:contain | //a:bar | .//namespace::*)', + 'namespaces' => ['a' => 'http://www.example.com/ns/foo'], +]; +$prefixes = ['test']; + +foreach ($xpath['namespaces'] as $k => &$v); +unset($v); +foreach ($xpath as $k => &$v); +unset($v); +foreach ($prefixes as $k => &$v); +unset($v); + +echo $doc->C14N(true, false, $xpath, $prefixes); +?> +--EXPECT-- + From 2dbe2d62b3785b26516a6450a74137477b4019e6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 30 Apr 2024 21:09:29 +0200 Subject: [PATCH 05/45] Fix crash when calling childNodes next() when iterator is exhausted Closes GH-14091. --- NEWS | 2 ++ ext/dom/dom_iterators.c | 3 +-- ext/dom/tests/childNodes_current_crash.phpt | 25 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 ext/dom/tests/childNodes_current_crash.phpt diff --git a/NEWS b/NEWS index 3a95dec835134..f124c8e4ed544 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,8 @@ PHP NEWS . Fix crashes when entity declaration is removed while still having entity references. (nielsdos) . Fix references not handled correctly in C14N. (nielsdos) + . Fix crash when calling childNodes next() when iterator is exhausted. + (nielsdos) 09 May 2024, PHP 8.2.19 diff --git a/ext/dom/dom_iterators.c b/ext/dom/dom_iterators.c index 72c97104db04d..670f08a679f2e 100644 --- a/ext/dom/dom_iterators.c +++ b/ext/dom/dom_iterators.c @@ -147,8 +147,7 @@ static int php_dom_iterator_valid(zend_object_iterator *iter) /* {{{ */ zval *php_dom_iterator_current_data(zend_object_iterator *iter) /* {{{ */ { php_dom_iterator *iterator = (php_dom_iterator *)iter; - - return &iterator->curobj; + return Z_ISUNDEF(iterator->curobj) ? NULL : &iterator->curobj; } /* }}} */ diff --git a/ext/dom/tests/childNodes_current_crash.phpt b/ext/dom/tests/childNodes_current_crash.phpt new file mode 100644 index 0000000000000..aa93cf33a6481 --- /dev/null +++ b/ext/dom/tests/childNodes_current_crash.phpt @@ -0,0 +1,25 @@ +--TEST-- +Crash in childNodes iterator current() +--EXTENSIONS-- +dom +--FILE-- +loadXML('foo1'); + +$nodes = $dom->documentElement->childNodes; +$iter = $nodes->getIterator(); + +var_dump($iter->valid()); +var_dump($iter->current()?->wholeText); +$iter->next(); +var_dump($iter->valid()); +var_dump($iter->current()?->wholeText); + +?> +--EXPECT-- +bool(true) +string(4) "foo1" +bool(false) +NULL From 74843947f4273977fb7ba9bb1b6ddc7ec8eff706 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 20 Apr 2024 20:03:48 +0100 Subject: [PATCH 06/45] sapi/cgi: fix buffer limit on windows. MSDN recommends dropping the deprecated `read` in favor of `_read`. Also, the buffer size limit is INT_MAX. Close GH-14022 --- NEWS | 4 ++++ main/fastcgi.c | 4 ++-- sapi/cgi/cgi_main.c | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index f124c8e4ed544..570a1b7ad6759 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.2.20 +- CGI: + . Fixed buffer limit on Windows, replacing read call usage by _read. + (David Carlier) + - DOM: . Fix crashes when entity declaration is removed while still having entity references. (nielsdos) diff --git a/main/fastcgi.c b/main/fastcgi.c index df309df9fdc70..85e73f2d8bfe1 100644 --- a/main/fastcgi.c +++ b/main/fastcgi.c @@ -965,9 +965,9 @@ static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count tmp = count - n; if (!req->tcp) { - unsigned int in_len = tmp > UINT_MAX ? UINT_MAX : (unsigned int)tmp; + unsigned int in_len = tmp > INT_MAX ? INT_MAX : (unsigned int)tmp; - ret = read(req->fd, ((char*)buf)+n, in_len); + ret = _read(req->fd, ((char*)buf)+n, in_len); } else { int in_len = tmp > INT_MAX ? INT_MAX : (int)tmp; diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c index b45468031fcd0..1b95afd7acd3c 100644 --- a/sapi/cgi/cgi_main.c +++ b/sapi/cgi/cgi_main.c @@ -486,9 +486,9 @@ static size_t sapi_cgi_read_post(char *buffer, size_t count_bytes) while (read_bytes < count_bytes) { #ifdef PHP_WIN32 size_t diff = count_bytes - read_bytes; - unsigned int to_read = (diff > UINT_MAX) ? UINT_MAX : (unsigned int)diff; + unsigned int to_read = (diff > INT_MAX) ? INT_MAX : (unsigned int)diff; - tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, to_read); + tmp_read_bytes = _read(STDIN_FILENO, buffer + read_bytes, to_read); #else tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes); #endif From b6b9eab68e2b0f7e9bd42ae625a315c87dc6cc93 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 1 May 2024 20:43:00 +0200 Subject: [PATCH 07/45] Fix tests for glibc 2.39 (#14097) 2.39 has fixed inconsistent (mon_)grouping formats in localedata: * https://sourceware.org/git/?p=glibc.git;a=blob;f=NEWS;hb=refs/tags/glibc-2.39#l237 * https://sourceware.org/git/?p=glibc.git;a=blob;f=localedata/locales/en_AU;hb=refs/tags/glibc-2.39#l65 For en_AU locale now it returns array of 1 item instead of 2 for grouping and mon_grouping. Since this is upstream change depending on the system, this only syncs tests for such case. --- .../tests/strings/setlocale_variation3.phpt | 26 +++++++------------ .../tests/strings/setlocale_variation4.phpt | 26 +++++++------------ .../tests/strings/setlocale_variation5.phpt | 26 +++++++------------ 3 files changed, 27 insertions(+), 51 deletions(-) diff --git a/ext/standard/tests/strings/setlocale_variation3.phpt b/ext/standard/tests/strings/setlocale_variation3.phpt index 551eece086fc0..30f281a502006 100644 --- a/ext/standard/tests/strings/setlocale_variation3.phpt +++ b/ext/standard/tests/strings/setlocale_variation3.phpt @@ -47,7 +47,7 @@ if($locale_info_before == $locale_info_after){ echo "\nDone\n"; ?> ---EXPECT-- +--EXPECTF-- *** Testing setlocale() : usage variations - setting system locale = 0 *** Locale info, before setting the locale array(18) { @@ -84,17 +84,13 @@ array(18) { ["n_sign_posn"]=> int(1) ["grouping"]=> - array(2) { - [0]=> - int(3) - [1]=> + array(%d) {%A + [%d]=> int(3) } ["mon_grouping"]=> - array(2) { - [0]=> - int(3) - [1]=> + array(%d) {%A + [%d]=> int(3) } } @@ -134,17 +130,13 @@ array(18) { ["n_sign_posn"]=> int(1) ["grouping"]=> - array(2) { - [0]=> - int(3) - [1]=> + array(%d) {%A + [%d]=> int(3) } ["mon_grouping"]=> - array(2) { - [0]=> - int(3) - [1]=> + array(%d) {%A + [%d]=> int(3) } } diff --git a/ext/standard/tests/strings/setlocale_variation4.phpt b/ext/standard/tests/strings/setlocale_variation4.phpt index 91171543bb694..ca469759dbd15 100644 --- a/ext/standard/tests/strings/setlocale_variation4.phpt +++ b/ext/standard/tests/strings/setlocale_variation4.phpt @@ -45,7 +45,7 @@ if($locale_info_before != $locale_info_after){ echo "\nDone\n"; ?> ---EXPECT-- +--EXPECTF-- *** Testing setlocale() : usage variations - Setting system locale = null *** Locale info, before setting the locale array(18) { @@ -82,17 +82,13 @@ array(18) { ["n_sign_posn"]=> int(1) ["grouping"]=> - array(2) { - [0]=> - int(3) - [1]=> + array(%d) {%A + [%d]=> int(3) } ["mon_grouping"]=> - array(2) { - [0]=> - int(3) - [1]=> + array(%d) {%A + [%d]=> int(3) } } @@ -132,17 +128,13 @@ array(18) { ["n_sign_posn"]=> int(1) ["grouping"]=> - array(2) { - [0]=> - int(3) - [1]=> + array(%d) {%A + [%d]=> int(3) } ["mon_grouping"]=> - array(2) { - [0]=> - int(3) - [1]=> + array(%d) {%A + [%d]=> int(3) } } diff --git a/ext/standard/tests/strings/setlocale_variation5.phpt b/ext/standard/tests/strings/setlocale_variation5.phpt index 15f912d3841ec..50c8a03bb8785 100644 --- a/ext/standard/tests/strings/setlocale_variation5.phpt +++ b/ext/standard/tests/strings/setlocale_variation5.phpt @@ -49,7 +49,7 @@ if($locale_info_before != $locale_info_after){ echo "\nDone\n"; ?> ---EXPECT-- +--EXPECTF-- *** Testing setlocale() : usage variations - setting system locale = "" *** Locale info, before setting the locale array(18) { @@ -86,17 +86,13 @@ array(18) { ["n_sign_posn"]=> int(1) ["grouping"]=> - array(2) { - [0]=> - int(3) - [1]=> + array(%d) {%A + [%d]=> int(3) } ["mon_grouping"]=> - array(2) { - [0]=> - int(3) - [1]=> + array(%d) {%A + [%d]=> int(3) } } @@ -136,17 +132,13 @@ array(18) { ["n_sign_posn"]=> int(1) ["grouping"]=> - array(2) { - [0]=> - int(3) - [1]=> + array(%d) {%A + [%d]=> int(3) } ["mon_grouping"]=> - array(2) { - [0]=> - int(3) - [1]=> + array(%d) {%A + [%d]=> int(3) } } From 6fed9a9a7e503d26f4abc9c44d1b38aeccb3f2d0 Mon Sep 17 00:00:00 2001 From: Matteo Beccati Date: Thu, 2 May 2024 15:29:25 +0200 Subject: [PATCH 08/45] Stick to mysql 8.3 for the time being MySQL 8.4 does not load the 'mysql_native_password' plugin by default and I couldn't figure out how to initialise the container to load it. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 26834a9392e0c..bff38447b8b90 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ jobs: resource_class: arm.medium docker: - image: cimg/base:current-22.04 - - image: mysql:8 + - image: mysql:8.3 environment: MYSQL_ALLOW_EMPTY_PASSWORD: true MYSQL_ROOT_PASSWORD: '' From 3237b8f471f5f3701622c6b08a953311e24fed1f Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Sat, 4 May 2024 09:41:04 +0900 Subject: [PATCH 09/45] CI: fix oci setup (#14123) closes #14123 --- .github/actions/setup-oracle/action.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/setup-oracle/action.yml b/.github/actions/setup-oracle/action.yml index 1208e93a24893..a7afda2e4bcab 100644 --- a/.github/actions/setup-oracle/action.yml +++ b/.github/actions/setup-oracle/action.yml @@ -13,10 +13,10 @@ runs: -d gvenzl/oracle-xe:slim mkdir /opt/oracle - wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip - unzip instantclient-basiclite-linuxx64.zip && rm instantclient-basiclite-linuxx64.zip - wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-sdk-linuxx64.zip - unzip instantclient-sdk-linuxx64.zip && rm instantclient-sdk-linuxx64.zip + wget -nv https://download.oracle.com/otn_software/linux/instantclient/2114000/instantclient-basiclite-linux.x64-21.14.0.0.0dbru.zip + unzip instantclient-basiclite-linux.x64-21.14.0.0.0dbru.zip && rm instantclient-basiclite-linux.x64-21.14.0.0.0dbru.zip + wget -nv https://download.oracle.com/otn_software/linux/instantclient/2114000/instantclient-sdk-linux.x64-21.14.0.0.0dbru.zip + unzip instantclient-sdk-linux.x64-21.14.0.0.0dbru.zip && rm instantclient-sdk-linux.x64-21.14.0.0.0dbru.zip mv instantclient_*_* /opt/oracle/instantclient # interferes with libldap2 headers rm /opt/oracle/instantclient/sdk/include/ldap.h From 427c2441688768e0de51bd71d2ab3fd9df11448d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 4 May 2024 13:57:50 +0200 Subject: [PATCH 10/45] Fix GH-14124: Segmentation fault on unknown address 0x0001ffff8041 with XML extension under certain memory limit (#14126) The ltags were not initialized, so when an OOM happens before the new value is written, uninitialized data is used. --- NEWS | 4 ++++ ext/xml/xml.c | 1 + 2 files changed, 5 insertions(+) diff --git a/NEWS b/NEWS index 570a1b7ad6759..dc16e1fdb7113 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,10 @@ PHP NEWS . Fix crash when calling childNodes next() when iterator is exhausted. (nielsdos) +- XML: + . Fixed bug GH-14124 (Segmentation fault with XML extension under certain + memory limit). (nielsdos) + 09 May 2024, PHP 8.2.19 - Core: diff --git a/ext/xml/xml.c b/ext/xml/xml.c index 1e8a97c9059f4..59d50faed111e 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -1292,6 +1292,7 @@ PHP_FUNCTION(xml_parse_into_struct) parser->level = 0; xml_parser_free_ltags(parser); parser->ltags = safe_emalloc(XML_MAXLEVEL, sizeof(char *), 0); + memset(parser->ltags, 0, XML_MAXLEVEL * sizeof(char *)); XML_SetElementHandler(parser->parser, _xml_startElementHandler, _xml_endElementHandler); XML_SetCharacterDataHandler(parser->parser, _xml_characterDataHandler); From f0356612d923bf31a9760b7e50ded1a6d9f47112 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 6 May 2024 09:49:41 +0300 Subject: [PATCH 11/45] Fix undefined behavior (left shift of negative number) Fixes oss-fuzz #68722 --- ext/standard/image.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/standard/image.c b/ext/standard/image.c index d5455963f3120..d064d9d4fd3b4 100644 --- a/ext/standard/image.c +++ b/ext/standard/image.c @@ -749,10 +749,10 @@ static signed short php_ifd_get16s(void *Short, int motorola_intel) static int php_ifd_get32s(void *Long, int motorola_intel) { if (motorola_intel) { - return ((( char *)Long)[0] << 24) | (((unsigned char *)Long)[1] << 16) - | (((unsigned char *)Long)[2] << 8 ) | (((unsigned char *)Long)[3] << 0 ); + return ((unsigned)((( char *)Long)[0]) << 24) | (((unsigned char *)Long)[1] << 16) + | ((( char *)Long)[2] << 8 ) | (((unsigned char *)Long)[3] << 0 ); } else { - return ((( char *)Long)[3] << 24) | (((unsigned char *)Long)[2] << 16) + return ((unsigned)((( char *)Long)[3]) << 24) | (((unsigned char *)Long)[2] << 16) | (((unsigned char *)Long)[1] << 8 ) | (((unsigned char *)Long)[0] << 0 ); } } From f8d1864bbbfbdc63d1b8e4e58934f8d8668f55bf Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 1 May 2024 18:32:07 +0200 Subject: [PATCH 12/45] Delay #[Attribute] arg validation until runtime Fixes GH-13970 Closes GH-14105 We cannot validate at compile-time for multiple reasons: * Evaluating the argument naively with zend_get_attribute_value can lead to code execution at compile time through the new expression, leading to possible reentrance of the compiler. * Even if the evaluation was possible, it would need to be restricted to the current file, because constant values coming from other files can change without affecting the current compilation unit. For this reason, validation would need to be repeated at runtime anyway. * Enums cannot be instantiated at compile-time (the actual bug report). This could be allowed here, because the value is immediately destroyed. But given the other issues, this won't be needed. Instead, we just move it to runtime entirely. It's only needed for ReflectionAttribute::newInstance(), which is not particularly a hot path. The checks are also simple. --- NEWS | 4 +++ ...021_attribute_flags_type_is_validated.phpt | 13 ++++++++-- ...22_attribute_flags_value_is_validated.phpt | 13 ++++++++-- .../023_ast_node_in_validation.phpt | 13 ++++++++-- .../032_attribute_validation_scope.phpt | 16 +++++++++--- ...gs_type_is_not_validated_at_comp_time.phpt | 12 +++++++++ Zend/zend_attributes.c | 26 ++++++++++++------- Zend/zend_attributes.h | 2 ++ ext/reflection/php_reflection.c | 13 +++------- ext/zend_test/tests/gh13970.phpt | 22 ++++++++++++++++ 10 files changed, 106 insertions(+), 28 deletions(-) create mode 100644 Zend/tests/attributes/033_attribute_flags_type_is_not_validated_at_comp_time.phpt create mode 100644 ext/zend_test/tests/gh13970.phpt diff --git a/NEWS b/NEWS index dc16e1fdb7113..92d93bed27fa7 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,10 @@ PHP NEWS . Fixed buffer limit on Windows, replacing read call usage by _read. (David Carlier) +- Core: + . Fixed bug GH-13970 (Incorrect validation of #[Attribute] flags type for + non-compile-time expressions). (ilutov) + - DOM: . Fix crashes when entity declaration is removed while still having entity references. (nielsdos) diff --git a/Zend/tests/attributes/021_attribute_flags_type_is_validated.phpt b/Zend/tests/attributes/021_attribute_flags_type_is_validated.phpt index 01ab29c5efab9..70e4fdb7f338a 100644 --- a/Zend/tests/attributes/021_attribute_flags_type_is_validated.phpt +++ b/Zend/tests/attributes/021_attribute_flags_type_is_validated.phpt @@ -6,6 +6,15 @@ Attribute flags type is validated. #[Attribute("foo")] class A1 { } +#[A1] +class Foo {} + +try { + (new ReflectionClass(Foo::class))->getAttributes()[0]->newInstance(); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + ?> ---EXPECTF-- -Fatal error: Attribute::__construct(): Argument #1 ($flags) must be of type int, string given in %s +--EXPECT-- +Attribute::__construct(): Argument #1 ($flags) must be of type int, string given diff --git a/Zend/tests/attributes/022_attribute_flags_value_is_validated.phpt b/Zend/tests/attributes/022_attribute_flags_value_is_validated.phpt index 72433a9f13930..efaa969af827e 100644 --- a/Zend/tests/attributes/022_attribute_flags_value_is_validated.phpt +++ b/Zend/tests/attributes/022_attribute_flags_value_is_validated.phpt @@ -6,6 +6,15 @@ Attribute flags value is validated. #[Attribute(-1)] class A1 { } +#[A1] +class Foo { } + +try { + var_dump((new ReflectionClass(Foo::class))->getAttributes()[0]->newInstance()); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + ?> ---EXPECTF-- -Fatal error: Invalid attribute flags specified in %s +--EXPECT-- +Invalid attribute flags specified diff --git a/Zend/tests/attributes/023_ast_node_in_validation.phpt b/Zend/tests/attributes/023_ast_node_in_validation.phpt index 332d83fe86f61..063a6b7e815d2 100644 --- a/Zend/tests/attributes/023_ast_node_in_validation.phpt +++ b/Zend/tests/attributes/023_ast_node_in_validation.phpt @@ -6,6 +6,15 @@ Attribute flags value is validated. #[Attribute(Foo::BAR)] class A1 { } +#[A1] +class Bar { } + +try { + var_dump((new ReflectionClass(Bar::class))->getAttributes()[0]->newInstance()); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + ?> ---EXPECTF-- -Fatal error: Class "Foo" not found in %s on line %d +--EXPECT-- +Class "Foo" not found diff --git a/Zend/tests/attributes/032_attribute_validation_scope.phpt b/Zend/tests/attributes/032_attribute_validation_scope.phpt index 039a427254f4d..d157c35929bf2 100644 --- a/Zend/tests/attributes/032_attribute_validation_scope.phpt +++ b/Zend/tests/attributes/032_attribute_validation_scope.phpt @@ -1,9 +1,19 @@ --TEST-- -Validation for "Attribute" does not use a scope when evaluating constant ASTs +Validation for "Attribute" uses the class scope when evaluating constant ASTs --FILE-- getAttributes()[0]->newInstance()); ?> ---EXPECTF-- -Fatal error: Cannot access "parent" when no class scope is active in %s on line %d +--EXPECT-- +object(x)#1 (0) { +} diff --git a/Zend/tests/attributes/033_attribute_flags_type_is_not_validated_at_comp_time.phpt b/Zend/tests/attributes/033_attribute_flags_type_is_not_validated_at_comp_time.phpt new file mode 100644 index 0000000000000..76b29c65ba87b --- /dev/null +++ b/Zend/tests/attributes/033_attribute_flags_type_is_not_validated_at_comp_time.phpt @@ -0,0 +1,12 @@ +--TEST-- +Attribute flags type is not validated at compile time. +--FILE-- + +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 5a446c8c2859e..2471a6739e3c6 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -35,31 +35,39 @@ static zend_object_handlers attributes_object_handlers_sensitive_parameter_value static HashTable internal_attributes; void validate_attribute(zend_attribute *attr, uint32_t target, zend_class_entry *scope) +{ +} + +uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope) { // TODO: More proper signature validation: Too many args, incorrect arg names. if (attr->argc > 0) { zval flags; - /* As this is run in the middle of compilation, fetch the attribute value without - * specifying a scope. The class is not fully linked yet, and we may seen an - * inconsistent state. */ - if (FAILURE == zend_get_attribute_value(&flags, attr, 0, NULL)) { - return; + if (FAILURE == zend_get_attribute_value(&flags, attr, 0, scope)) { + ZEND_ASSERT(EG(exception)); + return 0; } if (Z_TYPE(flags) != IS_LONG) { - zend_error_noreturn(E_ERROR, + zend_throw_error(NULL, "Attribute::__construct(): Argument #1 ($flags) must be of type int, %s given", zend_zval_type_name(&flags) ); + zval_ptr_dtor(&flags); + return 0; } - if (Z_LVAL(flags) & ~ZEND_ATTRIBUTE_FLAGS) { - zend_error_noreturn(E_ERROR, "Invalid attribute flags specified"); + uint32_t flags_l = Z_LVAL(flags); + if (flags_l & ~ZEND_ATTRIBUTE_FLAGS) { + zend_throw_error(NULL, "Invalid attribute flags specified"); + return 0; } - zval_ptr_dtor(&flags); + return flags_l; } + + return ZEND_ATTRIBUTE_TARGET_ALL; } static void validate_allow_dynamic_properties( diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index fc02a7d656e25..746155f514735 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -85,6 +85,8 @@ ZEND_API zend_attribute *zend_add_attribute( HashTable **attributes, zend_string *name, uint32_t argc, uint32_t flags, uint32_t offset, uint32_t lineno); +uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope); + END_EXTERN_C() static zend_always_inline zend_attribute *zend_add_class_attribute(zend_class_entry *ce, zend_string *name, uint32_t argc) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 136d69b2864f4..f24b00a2857f9 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6739,16 +6739,9 @@ ZEND_METHOD(ReflectionAttribute, newInstance) } if (ce->type == ZEND_USER_CLASS) { - uint32_t flags = ZEND_ATTRIBUTE_TARGET_ALL; - - if (marker->argc > 0) { - zval tmp; - - if (FAILURE == zend_get_attribute_value(&tmp, marker, 0, ce)) { - RETURN_THROWS(); - } - - flags = (uint32_t) Z_LVAL(tmp); + uint32_t flags = zend_attribute_attribute_get_flags(marker, ce); + if (EG(exception)) { + RETURN_THROWS(); } if (!(attr->target & flags)) { diff --git a/ext/zend_test/tests/gh13970.phpt b/ext/zend_test/tests/gh13970.phpt new file mode 100644 index 0000000000000..f6b68f8970234 --- /dev/null +++ b/ext/zend_test/tests/gh13970.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-13970 (Incorrect validation of #[\Attribute]'s first parameter) +--EXTENSIONS-- +zend_test +--FILE-- +getAttributes()[0]->newInstance()); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Attribute::__construct(): Argument #1 ($flags) must be of type int, ZendTestUnitEnum given From 42ede5597e820357685dcefd8c7d9a576a258bf1 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 2 May 2024 21:11:29 +0200 Subject: [PATCH 13/45] Fix persisting of inherited class constants Class constants are inherited to user classes without cloning. Thus, internal class constants should not be persisted at all. Simply keep pointing to the internal class constant. Fixes GH-14109 Closes GH-14114 --- NEWS | 4 ++++ ext/opcache/zend_persist.c | 7 ++++++- ext/opcache/zend_persist_calc.c | 6 ++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 92d93bed27fa7..dedf9db8e2ea0 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,10 @@ PHP NEWS . Fix crash when calling childNodes next() when iterator is exhausted. (nielsdos) +- Opcache: + . Fixed bug GH-14109 (Fix accidental persisting of internal class constant in + shm). (ilutov) + - XML: . Fixed bug GH-14124 (Segmentation fault with XML extension under certain memory limit). (nielsdos) diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 87f1aee1480bc..990ad64ed99bf 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -802,12 +802,17 @@ static zend_property_info *zend_persist_property_info(zend_property_info *prop) static void zend_persist_class_constant(zval *zv) { - zend_class_constant *c = zend_shared_alloc_get_xlat_entry(Z_PTR_P(zv)); + zend_class_constant *orig_c = Z_PTR_P(zv); + zend_class_constant *c = zend_shared_alloc_get_xlat_entry(orig_c); zend_class_entry *ce; if (c) { Z_PTR_P(zv) = c; return; + } else if (((orig_c->ce->ce_flags & ZEND_ACC_IMMUTABLE) && !(Z_CONSTANT_FLAGS(orig_c->value) & CONST_OWNED)) + || orig_c->ce->type == ZEND_INTERNAL_CLASS) { + /* Class constant comes from a different file in shm or internal class, keep existing pointer. */ + return; } else if (!ZCG(current_persistent_script)->corrupted && zend_accel_in_shm(Z_PTR_P(zv))) { return; diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 4e3af0d68c653..abe0a280d9a23 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -26,6 +26,7 @@ #include "zend_shared_alloc.h" #include "zend_operators.h" #include "zend_attributes.h" +#include "zend_constants.h" #define ADD_DUP_SIZE(m,s) ZCG(current_persistent_script)->size += zend_shared_memdup_size((void*)m, s) #define ADD_SIZE(m) ZCG(current_persistent_script)->size += ZEND_ALIGNED_SIZE(m) @@ -386,6 +387,11 @@ static void zend_persist_class_constant_calc(zval *zv) zend_class_constant *c = Z_PTR_P(zv); if (!zend_shared_alloc_get_xlat_entry(c)) { + if (((c->ce->ce_flags & ZEND_ACC_IMMUTABLE) && !(Z_CONSTANT_FLAGS(c->value) & CONST_OWNED)) + || c->ce->type == ZEND_INTERNAL_CLASS) { + /* Class constant comes from a different file in shm or internal class, keep existing pointer. */ + return; + } if (!ZCG(current_persistent_script)->corrupted && zend_accel_in_shm(Z_PTR_P(zv))) { return; From c6b75f93289459b85923b104a071b464e031143f Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 6 May 2024 21:39:30 +0300 Subject: [PATCH 14/45] Fix prototype for trait methods (#14148) * Fix prototype for trait methods Fixes GH-14009 * Clenup do_inheritance_check_on_method() Remove wierd checks and define the behavior by explicit set of flags * Fix naming and indentation --------- Co-authored-by: Ilija Tovilo --- Zend/tests/gh14009_001.phpt | 41 +++++ Zend/tests/gh14009_002.phpt | 22 +++ Zend/tests/gh14009_003.phpt | 19 +++ Zend/tests/gh14009_004.phpt | 37 +++++ .../interface_constructor_prototype_001.phpt | 19 +++ .../interface_constructor_prototype_002.phpt | 26 ++++ Zend/zend_inheritance.c | 145 ++++++++++-------- 7 files changed, 243 insertions(+), 66 deletions(-) create mode 100644 Zend/tests/gh14009_001.phpt create mode 100644 Zend/tests/gh14009_002.phpt create mode 100644 Zend/tests/gh14009_003.phpt create mode 100644 Zend/tests/gh14009_004.phpt create mode 100644 Zend/tests/interface_constructor_prototype_001.phpt create mode 100644 Zend/tests/interface_constructor_prototype_002.phpt diff --git a/Zend/tests/gh14009_001.phpt b/Zend/tests/gh14009_001.phpt new file mode 100644 index 0000000000000..81325e814c271 --- /dev/null +++ b/Zend/tests/gh14009_001.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-14009: Traits inherit prototype +--FILE-- +common(); + } +} + +class B extends P { + protected function common() { + echo __METHOD__, "\n"; + } +} + +trait T { + protected function common() { + echo __METHOD__, "\n"; + } +} + +class C extends P { + use T; +} + +$a = new A(); +$a->test(new B()); +$a->test(new C()); + +?> +--EXPECT-- +B::common +T::common diff --git a/Zend/tests/gh14009_002.phpt b/Zend/tests/gh14009_002.phpt new file mode 100644 index 0000000000000..86047e020205e --- /dev/null +++ b/Zend/tests/gh14009_002.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-14009: Traits inherit prototype +--FILE-- + +--EXPECTF-- +Fatal error: Declaration of P::common() must be compatible with T::common(int $param) in %s on line %d diff --git a/Zend/tests/gh14009_003.phpt b/Zend/tests/gh14009_003.phpt new file mode 100644 index 0000000000000..71ee5baa360a6 --- /dev/null +++ b/Zend/tests/gh14009_003.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-14009: Traits inherit prototype +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/gh14009_004.phpt b/Zend/tests/gh14009_004.phpt new file mode 100644 index 0000000000000..01bad46fedcb7 --- /dev/null +++ b/Zend/tests/gh14009_004.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-14009: Traits inherit prototype +--FILE-- +test(); + } +} + +D::callTest(new C()); + +?> +--EXPECT-- +B::test diff --git a/Zend/tests/interface_constructor_prototype_001.phpt b/Zend/tests/interface_constructor_prototype_001.phpt new file mode 100644 index 0000000000000..67341367ac58f --- /dev/null +++ b/Zend/tests/interface_constructor_prototype_001.phpt @@ -0,0 +1,19 @@ +--TEST-- +Interfaces don't set prototypes to their parent method +--FILE-- + +--EXPECTF-- +Fatal error: Declaration of Test::__construct(int $param) must be compatible with B::__construct(int|float $param) in %s on line %d diff --git a/Zend/tests/interface_constructor_prototype_002.phpt b/Zend/tests/interface_constructor_prototype_002.phpt new file mode 100644 index 0000000000000..76398d2d215b9 --- /dev/null +++ b/Zend/tests/interface_constructor_prototype_002.phpt @@ -0,0 +1,26 @@ +--TEST-- +Interfaces don't set prototypes to their parent method +--XFAIL-- +X::__constructor()'s prototype is set to B::__construct(). Y::__construct() then +uses prototype to verify LSP, but misses A::__construct() which has a stricter +signature. +--FILE-- + +--EXPECTF-- +Fatal error: Declaration of Y::__construct(int $param) must be compatible with A::__construct(int|float $param) in %s on line %d diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index cec0ffdb1853b..695d31be0fbfe 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1075,26 +1075,45 @@ static void perform_delayable_implementation_check( } } -static zend_always_inline inheritance_status do_inheritance_check_on_method_ex( + +#define ZEND_INHERITANCE_LAZY_CHILD_CLONE (1<<0) +#define ZEND_INHERITANCE_CHECK_SILENT (1<<1) /* don't throw errors */ +#define ZEND_INHERITANCE_CHECK_PROTO (1<<2) /* check method prototype (it might be already checked before) */ +#define ZEND_INHERITANCE_CHECK_VISIBILITY (1<<3) +#define ZEND_INHERITANCE_SET_CHILD_CHANGED (1<<4) +#define ZEND_INHERITANCE_SET_CHILD_PROTO (1<<5) + +static inheritance_status do_inheritance_check_on_method( zend_function *child, zend_class_entry *child_scope, zend_function *parent, zend_class_entry *parent_scope, - zend_class_entry *ce, zval *child_zv, - bool check_visibility, bool check_only, bool checked) /* {{{ */ + zend_class_entry *ce, zval *child_zv, uint32_t flags) /* {{{ */ { uint32_t child_flags; uint32_t parent_flags = parent->common.fn_flags; zend_function *proto; - if (UNEXPECTED((parent_flags & ZEND_ACC_PRIVATE) && !(parent_flags & ZEND_ACC_ABSTRACT) && !(parent_flags & ZEND_ACC_CTOR))) { - if (!check_only) { +#define SEPARATE_METHOD() do { \ + if ((flags & ZEND_INHERITANCE_LAZY_CHILD_CLONE) \ + && child_scope != ce && child->type == ZEND_USER_FUNCTION) { \ + /* op_array wasn't duplicated yet */ \ + zend_function *new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); \ + memcpy(new_function, child, sizeof(zend_op_array)); \ + Z_PTR_P(child_zv) = child = new_function; \ + flags &= ~ZEND_INHERITANCE_LAZY_CHILD_CLONE; \ + } \ + } while(0) + + if (UNEXPECTED((parent_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_ABSTRACT|ZEND_ACC_CTOR)) == ZEND_ACC_PRIVATE)) { + if (flags & ZEND_INHERITANCE_SET_CHILD_CHANGED) { + SEPARATE_METHOD(); child->common.fn_flags |= ZEND_ACC_CHANGED; } /* The parent method is private and not an abstract so we don't need to check any inheritance rules */ return INHERITANCE_SUCCESS; } - if (!checked && UNEXPECTED(parent_flags & ZEND_ACC_FINAL)) { - if (check_only) { + if ((flags & ZEND_INHERITANCE_CHECK_PROTO) && UNEXPECTED(parent_flags & ZEND_ACC_FINAL)) { + if (flags & ZEND_INHERITANCE_CHECK_SILENT) { return INHERITANCE_ERROR; } zend_error_at_noreturn(E_COMPILE_ERROR, func_filename(child), func_lineno(child), @@ -1105,8 +1124,9 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex( child_flags = child->common.fn_flags; /* You cannot change from static to non static and vice versa. */ - if (!checked && UNEXPECTED((child_flags & ZEND_ACC_STATIC) != (parent_flags & ZEND_ACC_STATIC))) { - if (check_only) { + if ((flags & ZEND_INHERITANCE_CHECK_PROTO) + && UNEXPECTED((child_flags & ZEND_ACC_STATIC) != (parent_flags & ZEND_ACC_STATIC))) { + if (flags & ZEND_INHERITANCE_CHECK_SILENT) { return INHERITANCE_ERROR; } if (child_flags & ZEND_ACC_STATIC) { @@ -1121,8 +1141,9 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex( } /* Disallow making an inherited method abstract. */ - if (!checked && UNEXPECTED((child_flags & ZEND_ACC_ABSTRACT) > (parent_flags & ZEND_ACC_ABSTRACT))) { - if (check_only) { + if ((flags & ZEND_INHERITANCE_CHECK_PROTO) + && UNEXPECTED((child_flags & ZEND_ACC_ABSTRACT) > (parent_flags & ZEND_ACC_ABSTRACT))) { + if (flags & ZEND_INHERITANCE_CHECK_SILENT) { return INHERITANCE_ERROR; } zend_error_at_noreturn(E_COMPILE_ERROR, func_filename(child), func_lineno(child), @@ -1130,7 +1151,9 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex( ZEND_FN_SCOPE_NAME(parent), ZSTR_VAL(child->common.function_name), ZEND_FN_SCOPE_NAME(child)); } - if (!check_only && (parent_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_CHANGED))) { + if ((flags & ZEND_INHERITANCE_SET_CHILD_CHANGED) + && (parent_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_CHANGED))) { + SEPARATE_METHOD(); child->common.fn_flags |= ZEND_ACC_CHANGED; } @@ -1146,27 +1169,16 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex( parent = proto; } - if (!check_only && child->common.prototype != proto && child_zv) { - do { - if (child->common.scope != ce && child->type == ZEND_USER_FUNCTION) { - if (ce->ce_flags & ZEND_ACC_INTERFACE) { - /* Few parent interfaces contain the same method */ - break; - } else { - /* op_array wasn't duplicated yet */ - zend_function *new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); - memcpy(new_function, child, sizeof(zend_op_array)); - Z_PTR_P(child_zv) = child = new_function; - } - } - child->common.prototype = proto; - } while (0); + if ((flags & ZEND_INHERITANCE_SET_CHILD_PROTO) + && child->common.prototype != proto) { + SEPARATE_METHOD(); + child->common.prototype = proto; } /* Prevent derived classes from restricting access that was available in parent classes (except deriving from non-abstract ctors) */ - if (!checked && check_visibility + if ((flags & ZEND_INHERITANCE_CHECK_VISIBILITY) && (child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK)) { - if (check_only) { + if (flags & ZEND_INHERITANCE_CHECK_SILENT) { return INHERITANCE_ERROR; } zend_error_at_noreturn(E_COMPILE_ERROR, func_filename(child), func_lineno(child), @@ -1174,25 +1186,20 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex( ZEND_FN_SCOPE_NAME(child), ZSTR_VAL(child->common.function_name), zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker"); } - if (!checked) { - if (check_only) { + if (flags & ZEND_INHERITANCE_CHECK_PROTO) { + if (flags & ZEND_INHERITANCE_CHECK_SILENT) { return zend_do_perform_implementation_check(child, child_scope, parent, parent_scope); } perform_delayable_implementation_check(ce, child, child_scope, parent, parent_scope); } + +#undef SEPARATE_METHOD + return INHERITANCE_SUCCESS; } /* }}} */ -static zend_never_inline void do_inheritance_check_on_method( - zend_function *child, zend_class_entry *child_scope, - zend_function *parent, zend_class_entry *parent_scope, - zend_class_entry *ce, zval *child_zv, bool check_visibility) -{ - do_inheritance_check_on_method_ex(child, child_scope, parent, parent_scope, ce, child_zv, check_visibility, 0, 0); -} - -static zend_always_inline void do_inherit_method(zend_string *key, zend_function *parent, zend_class_entry *ce, bool is_interface, bool checked) /* {{{ */ +static void do_inherit_method(zend_string *key, zend_function *parent, zend_class_entry *ce, bool is_interface, uint32_t flags) /* {{{ */ { zval *child = zend_hash_find_known_hash(&ce->function_table, key); @@ -1204,15 +1211,8 @@ static zend_always_inline void do_inherit_method(zend_string *key, zend_function return; } - if (checked) { - do_inheritance_check_on_method_ex( - func, func->common.scope, parent, parent->common.scope, ce, child, - /* check_visibility */ 1, 0, checked); - } else { - do_inheritance_check_on_method( - func, func->common.scope, parent, parent->common.scope, ce, child, - /* check_visibility */ 1); - } + do_inheritance_check_on_method( + func, func->common.scope, parent, parent->common.scope, ce, child, flags); } else { if (is_interface || (parent->common.fn_flags & (ZEND_ACC_ABSTRACT))) { @@ -1618,16 +1618,15 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par zend_hash_extend(&ce->function_table, zend_hash_num_elements(&ce->function_table) + zend_hash_num_elements(&parent_ce->function_table), 0); + uint32_t flags = + ZEND_INHERITANCE_LAZY_CHILD_CLONE | ZEND_INHERITANCE_SET_CHILD_CHANGED | ZEND_INHERITANCE_SET_CHILD_PROTO; - if (checked) { - ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) { - do_inherit_method(key, func, ce, 0, 1); - } ZEND_HASH_FOREACH_END(); - } else { - ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) { - do_inherit_method(key, func, ce, 0, 0); - } ZEND_HASH_FOREACH_END(); + if (!checked) { + flags |= ZEND_INHERITANCE_CHECK_PROTO | ZEND_INHERITANCE_CHECK_VISIBILITY; } + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) { + do_inherit_method(key, func, ce, 0, flags); + } ZEND_HASH_FOREACH_END(); } do_inherit_parent_constructor(ce); @@ -1733,13 +1732,20 @@ static void do_interface_implementation(zend_class_entry *ce, zend_class_entry * zend_function *func; zend_string *key; zend_class_constant *c; + uint32_t flags = ZEND_INHERITANCE_CHECK_PROTO | ZEND_INHERITANCE_CHECK_VISIBILITY; + + if (!(ce->ce_flags & ZEND_ACC_INTERFACE)) { + /* We are not setting the prototype of overridden interface methods because of abstract + * constructors. See Zend/tests/interface_constructor_prototype_001.phpt. */ + flags |= ZEND_INHERITANCE_LAZY_CHILD_CLONE | ZEND_INHERITANCE_SET_CHILD_PROTO; + } ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&iface->constants_table, key, c) { do_inherit_iface_constant(key, c, ce, iface); } ZEND_HASH_FOREACH_END(); ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&iface->function_table, key, func) { - do_inherit_method(key, func, ce, 1, 0); + do_inherit_method(key, func, ce, 1, flags); } ZEND_HASH_FOREACH_END(); do_implement_interface(ce, iface); @@ -1868,6 +1874,7 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ { zend_function *existing_fn = NULL; zend_function *new_fn; + bool check_inheritance = false; if ((existing_fn = zend_hash_find_ptr(&ce->function_table, key)) != NULL) { /* if it is the same function with the same visibility and has not been assigned a class scope yet, regardless @@ -1887,7 +1894,7 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ */ do_inheritance_check_on_method( existing_fn, fixup_trait_scope(existing_fn, ce), fn, fixup_trait_scope(fn, ce), - ce, NULL, /* check_visibility */ 0); + ce, NULL, ZEND_INHERITANCE_CHECK_PROTO); return; } @@ -1902,11 +1909,7 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(existing_fn->common.scope->name), ZSTR_VAL(existing_fn->common.function_name)); } else { - /* Inherited members are overridden by members inserted by traits. - * Check whether the trait method fulfills the inheritance requirements. */ - do_inheritance_check_on_method( - fn, fixup_trait_scope(fn, ce), existing_fn, fixup_trait_scope(existing_fn, ce), - ce, NULL, /* check_visibility */ 1); + check_inheritance = true; } } @@ -1926,6 +1929,15 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ function_add_ref(new_fn); fn = zend_hash_update_ptr(&ce->function_table, key, new_fn); zend_add_magic_method(ce, fn, key); + + if (check_inheritance) { + /* Inherited members are overridden by members inserted by traits. + * Check whether the trait method fulfills the inheritance requirements. */ + do_inheritance_check_on_method( + fn, fixup_trait_scope(fn, ce), existing_fn, fixup_trait_scope(existing_fn, ce), + ce, NULL, + ZEND_INHERITANCE_CHECK_PROTO | ZEND_INHERITANCE_CHECK_VISIBILITY | ZEND_INHERITANCE_SET_CHILD_PROTO); + } } /* }}} */ @@ -3103,10 +3115,11 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e if (zv) { zend_function *child_func = Z_FUNC_P(zv); inheritance_status status = - do_inheritance_check_on_method_ex( + do_inheritance_check_on_method( child_func, child_func->common.scope, parent_func, parent_func->common.scope, - ce, NULL, /* check_visibility */ 1, 1, 0); + ce, NULL, + ZEND_INHERITANCE_CHECK_SILENT | ZEND_INHERITANCE_CHECK_PROTO | ZEND_INHERITANCE_CHECK_VISIBILITY); if (UNEXPECTED(status == INHERITANCE_WARNING)) { overall_status = INHERITANCE_WARNING; } else if (UNEXPECTED(status != INHERITANCE_SUCCESS)) { From 7a9e0fb39b93b6251ed48b852a189e4998431c86 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 7 May 2024 13:16:23 +0300 Subject: [PATCH 15/45] Fix incorrect inheritance of private trait methods (#14163) The bug was introduced in c6b75f93289459b85923b104a071b464e031143f --- Zend/tests/gh14009_005.phpt | 38 +++++++++++++++++++++++++++++++++++++ Zend/zend_inheritance.c | 3 ++- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/gh14009_005.phpt diff --git a/Zend/tests/gh14009_005.phpt b/Zend/tests/gh14009_005.phpt new file mode 100644 index 0000000000000..c079bfb832aa8 --- /dev/null +++ b/Zend/tests/gh14009_005.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-14009: Traits inherit prototype +--FILE-- + ". __CLASS__ . "::" . __METHOD__ . "\n"; + } +} + +class A { + use T; + public function foo() { + $this->test(__METHOD__); + } + public function bar() { + $this->test(__METHOD__); + } +} + +class B extends A { + use T; + public function foo() { + $this->test(__METHOD__); + } +} + +(new A)->foo(); +(new A)->bar(); +(new B)->foo(); +(new B)->bar(); +?> +--EXPECT-- +A::foo -> A::T::test +A::bar -> A::T::test +B::foo -> B::T::test +A::bar -> A::T::test diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 695d31be0fbfe..8c7ea963891bf 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1936,7 +1936,8 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ do_inheritance_check_on_method( fn, fixup_trait_scope(fn, ce), existing_fn, fixup_trait_scope(existing_fn, ce), ce, NULL, - ZEND_INHERITANCE_CHECK_PROTO | ZEND_INHERITANCE_CHECK_VISIBILITY | ZEND_INHERITANCE_SET_CHILD_PROTO); + ZEND_INHERITANCE_CHECK_PROTO | ZEND_INHERITANCE_CHECK_VISIBILITY | + ZEND_INHERITANCE_SET_CHILD_CHANGED| ZEND_INHERITANCE_SET_CHILD_PROTO); } } /* }}} */ From b8aa68bc09c0f4b164293fffc6fc38110bcf3ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 7 May 2024 20:52:10 +0200 Subject: [PATCH 16/45] CI: Do not save the ccache for PRs (#14168) Resolves #14155 --- .github/workflows/push.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index e72cd8f77bfd8..3aeeed92d15f9 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -72,6 +72,7 @@ jobs: # job id, not the job name) key: "LINUX_X64_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}-${{hashFiles('main/php_version.h')}}" append-timestamp: false + save: ${{ github.event_name != 'pull_request' }} - name: ./configure uses: ./.github/actions/configure-x64 with: @@ -111,6 +112,7 @@ jobs: with: key: "${{github.job}}-${{hashFiles('main/php_version.h')}}" append-timestamp: false + save: ${{ github.event_name != 'pull_request' }} - name: ./configure uses: ./.github/actions/configure-macos with: From 12dc51976b13e078ddde75167903077d7f1d380e Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Fri, 10 May 2024 09:37:13 +0900 Subject: [PATCH 17/45] ext/hash: Swap the checking order of __has_builtin and __GNUC__ (#14185) closes #14185 --- NEWS | 4 ++++ ext/hash/murmur/endianness.h | 16 ++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index dedf9db8e2ea0..961dcffcb5de2 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,10 @@ PHP NEWS . Fix crash when calling childNodes next() when iterator is exhausted. (nielsdos) +- Hash: + . ext/hash: Swap the checking order of `__has_builtin` and `__GNUC__` + (Saki Takamachi) + - Opcache: . Fixed bug GH-14109 (Fix accidental persisting of internal class constant in shm). (ilutov) diff --git a/ext/hash/murmur/endianness.h b/ext/hash/murmur/endianness.h index cbad6dc72fb10..11f35a402be3e 100644 --- a/ext/hash/murmur/endianness.h +++ b/ext/hash/murmur/endianness.h @@ -13,14 +13,7 @@ FORCE_INLINE int IsBigEndian(void) # define BSWAP32(u) _byteswap_ulong(u) # define BSWAP64(u) _byteswap_uint64(u) #else -# ifdef __has_builtin -# if __has_builtin(__builtin_bswap32) -# define BSWAP32(u) __builtin_bswap32(u) -# endif // __has_builtin(__builtin_bswap32) -# if __has_builtin(__builtin_bswap64) -# define BSWAP64(u) __builtin_bswap64(u) -# endif // __has_builtin(__builtin_bswap64) -# elif defined(__GNUC__) && ( \ +# if defined(__GNUC__) && ( \ __GNUC__ > 4 || ( \ __GNUC__ == 4 && ( \ __GNUC_MINOR__ >= 3 \ @@ -29,6 +22,13 @@ FORCE_INLINE int IsBigEndian(void) ) # define BSWAP32(u) __builtin_bswap32(u) # define BSWAP64(u) __builtin_bswap64(u) +# elif defined(__has_builtin) +# if __has_builtin(__builtin_bswap32) +# define BSWAP32(u) __builtin_bswap32(u) +# endif // __has_builtin(__builtin_bswap32) +# if __has_builtin(__builtin_bswap64) +# define BSWAP64(u) __builtin_bswap64(u) +# endif // __has_builtin(__builtin_bswap64) # endif // __has_builtin #endif // defined(_MSC_VER) From 15813d69a560b2791ddf82107a54353520d3a885 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 10 May 2024 15:17:29 +0200 Subject: [PATCH 18/45] Fix GH-14183: XMLReader::open() can't be overridden We should only return the override if the internal static method is matched. Closes GH-14194. --- NEWS | 3 +++ ext/xmlreader/php_xmlreader.c | 24 +++++++++++------------- ext/xmlreader/tests/gh14183.phpt | 24 ++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 ext/xmlreader/tests/gh14183.phpt diff --git a/NEWS b/NEWS index 961dcffcb5de2..69231fb8c2c0e 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,9 @@ PHP NEWS . Fixed bug GH-14124 (Segmentation fault with XML extension under certain memory limit). (nielsdos) +- XMLReader: + . Fixed bug GH-14183 (XMLReader::open() can't be overridden). (nielsdos) + 09 May 2024, PHP 8.2.19 - Core: diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c index 3f88f758ba740..1b4bc6bcef4b1 100644 --- a/ext/xmlreader/php_xmlreader.c +++ b/ext/xmlreader/php_xmlreader.c @@ -186,19 +186,17 @@ zval *xmlreader_write_property(zend_object *object, zend_string *name, zval *val /* {{{ */ static zend_function *xmlreader_get_method(zend_object **obj, zend_string *name, const zval *key) { - if (ZSTR_LEN(name) == sizeof("open") - 1 - && (ZSTR_VAL(name)[0] == 'o' || ZSTR_VAL(name)[0] == 'O') - && (ZSTR_VAL(name)[1] == 'p' || ZSTR_VAL(name)[1] == 'P') - && (ZSTR_VAL(name)[2] == 'e' || ZSTR_VAL(name)[2] == 'E') - && (ZSTR_VAL(name)[3] == 'n' || ZSTR_VAL(name)[3] == 'N')) { - return (zend_function*)&xmlreader_open_fn; - } else if (ZSTR_LEN(name) == sizeof("xml") - 1 - && (ZSTR_VAL(name)[0] == 'x' || ZSTR_VAL(name)[0] == 'X') - && (ZSTR_VAL(name)[1] == 'm' || ZSTR_VAL(name)[1] == 'M') - && (ZSTR_VAL(name)[2] == 'l' || ZSTR_VAL(name)[2] == 'L')) { - return (zend_function*)&xmlreader_xml_fn; - } - return zend_std_get_method(obj, name, key);; + zend_function *method = zend_std_get_method(obj, name, key); + if (method && (method->common.fn_flags & ZEND_ACC_STATIC) && method->common.type == ZEND_INTERNAL_FUNCTION) { + /* There are only two static internal methods and they both have overrides. */ + if (ZSTR_LEN(name) == sizeof("xml") - 1) { + return (zend_function *) &xmlreader_xml_fn; + } else { + ZEND_ASSERT(ZSTR_LEN(name) == sizeof("open") - 1); + return (zend_function *) &xmlreader_open_fn; + } + } + return method; } /* }}} */ diff --git a/ext/xmlreader/tests/gh14183.phpt b/ext/xmlreader/tests/gh14183.phpt new file mode 100644 index 0000000000000..2f3f81bd6628e --- /dev/null +++ b/ext/xmlreader/tests/gh14183.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-14183 (XMLReader::open() can't be overridden) +--EXTENSIONS-- +xmlreader +--FILE-- +open('asdf')); +?> +--EXPECT-- +overridden +bool(true) +overridden +bool(true) From 217b753a3dc0c780fa6dfb414f8b995b99b45d2c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 10 May 2024 15:53:04 +0200 Subject: [PATCH 19/45] Fix GH-14189: PHP Interactive shell input state incorrectly handles quoted heredoc literals. Only `'` was handled, no handling case for `"` existed. Simply add it so the heredoc tag is set up correctly. Closes GH-14195. --- NEWS | 4 ++++ ext/readline/readline_cli.c | 1 + sapi/cli/tests/gh14189.phpt | 44 +++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 sapi/cli/tests/gh14189.phpt diff --git a/NEWS b/NEWS index 69231fb8c2c0e..d98f9ac68aa2d 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,10 @@ PHP NEWS . Fixed buffer limit on Windows, replacing read call usage by _read. (David Carlier) +- CLI: + . Fixed bug GH-14189 (PHP Interactive shell input state incorrectly handles + quoted heredoc literals.). (nielsdos) + - Core: . Fixed bug GH-13970 (Incorrect validation of #[Attribute] flags type for non-compile-time expressions). (ilutov) diff --git a/ext/readline/readline_cli.c b/ext/readline/readline_cli.c index 84b261db34283..5ca77bc3c6789 100644 --- a/ext/readline/readline_cli.c +++ b/ext/readline/readline_cli.c @@ -343,6 +343,7 @@ static int cli_is_valid_code(char *code, size_t len, zend_string **prompt) /* {{ case ' ': case '\t': case '\'': + case '"': break; case '\r': case '\n': diff --git a/sapi/cli/tests/gh14189.phpt b/sapi/cli/tests/gh14189.phpt new file mode 100644 index 0000000000000..5c0a8b252bf73 --- /dev/null +++ b/sapi/cli/tests/gh14189.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-14189 (PHP Interactive shell input state incorrectly handles quoted heredoc literals.) +--EXTENSIONS-- +readline +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Interactive shell + +php > $test = <<<"EOF" +<<< > foo +<<< > bar +<<< > baz +<<< > EOF; +php > echo $test; +foo +bar +baz +php > exit From 4e21a26db24cfeb0c8a3ba43dba2d7d5599d2ee5 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 10 May 2024 14:20:17 -0300 Subject: [PATCH 20/45] Fix check for newer versions of ICU (#14186) * Fix check for newer versions of ICU The previous test would always trigger, even if the version of ICU installed didn't require C++17. This was because it incorrectly used the `test` program, which broke the build on systems without a C++17 compiler. Tested with macOS 14 and i 7.2. * Fix broken ICU version check for definition Same as the previous fix for C++17. --------- Co-authored-by: Peter Kokot --- build/php.m4 | 5 ++--- ext/intl/config.m4 | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build/php.m4 b/build/php.m4 index 2bc0733a978a6..8ce9656ad1d89 100644 --- a/build/php.m4 +++ b/build/php.m4 @@ -1929,9 +1929,8 @@ AC_DEFUN([PHP_SETUP_ICU],[ ICU_CFLAGS="$ICU_CFLAGS -DU_NO_DEFAULT_INCLUDE_UTF_HEADERS=1" ICU_CXXFLAGS="$ICU_CXXFLAGS -DUNISTR_FROM_CHAR_EXPLICIT=explicit -DUNISTR_FROM_STRING_EXPLICIT=explicit" - if test "$PKG_CONFIG icu-io --atleast-version=60"; then - ICU_CFLAGS="$ICU_CFLAGS -DU_HIDE_OBSOLETE_UTF_OLD_H=1" - fi + AS_IF([$PKG_CONFIG icu-io --atleast-version=60], + [ICU_CFLAGS="$ICU_CFLAGS -DU_HIDE_OBSOLETE_UTF_OLD_H=1"]) ]) dnl diff --git a/ext/intl/config.m4 b/ext/intl/config.m4 index 48f5147ca7bbf..78396dc416926 100644 --- a/ext/intl/config.m4 +++ b/ext/intl/config.m4 @@ -82,7 +82,7 @@ if test "$PHP_INTL" != "no"; then PHP_REQUIRE_CXX() AC_MSG_CHECKING([if intl requires -std=gnu++17]) - AS_IF([test "$PKG_CONFIG icu-uc --atleast-version=74"],[ + AS_IF([$PKG_CONFIG icu-uc --atleast-version=74],[ AC_MSG_RESULT([yes]) PHP_CXX_COMPILE_STDCXX(17, mandatory, PHP_INTL_STDCXX) ],[ From 239003e01d4e0c68da4b674ab91554ff92ac68a4 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 10 May 2024 15:33:19 -0300 Subject: [PATCH 21/45] Update NEWS for ICU regression fix --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index d98f9ac68aa2d..b4eb80e33989d 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,10 @@ PHP NEWS . ext/hash: Swap the checking order of `__has_builtin` and `__GNUC__` (Saki Takamachi) +- Intl: + . Fixed build regression on systems without C++17 compilers. (Calvin Buckley, + Peter Kokot) + - Opcache: . Fixed bug GH-14109 (Fix accidental persisting of internal class constant in shm). (ilutov) From 1890d47c516d904c61d748ad3648d1a4fc452689 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 11 May 2024 17:12:20 +0200 Subject: [PATCH 22/45] Fix missing deref in C14N (#14203) Follow-up for 30a0b0359ed8338c0e3acd1682de3cf96429e898, which didn't fix all places. This is the last remaining place. --- ext/dom/node.c | 1 + ext/dom/tests/DOMNode_C14N_references.phpt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/dom/node.c b/ext/dom/node.c index 82f40860cfd35..c80f9c3333c6c 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -1667,6 +1667,7 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ inclusive_ns_prefixes = safe_emalloc(zend_hash_num_elements(Z_ARRVAL_P(ns_prefixes)) + 1, sizeof(xmlChar *), 0); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(ns_prefixes), tmpns) { + ZVAL_DEREF(tmpns); if (Z_TYPE_P(tmpns) == IS_STRING) { inclusive_ns_prefixes[nscount++] = (xmlChar *) Z_STRVAL_P(tmpns); } diff --git a/ext/dom/tests/DOMNode_C14N_references.phpt b/ext/dom/tests/DOMNode_C14N_references.phpt index 514e22be6365a..5612874f5f30f 100644 --- a/ext/dom/tests/DOMNode_C14N_references.phpt +++ b/ext/dom/tests/DOMNode_C14N_references.phpt @@ -38,4 +38,4 @@ unset($v); echo $doc->C14N(true, false, $xpath, $prefixes); ?> --EXPECT-- - + From 1e2a2d7df27a0d90ad4c3d39da2b6ea784b45b82 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 11 May 2024 19:44:02 +0200 Subject: [PATCH 23/45] Fix crash in ParentNode::append() when dealing with a fragment containing text nodes Credits for test: https://github.com/PhpGt/Dom/pull/454. Closes GH-14206. --- NEWS | 2 ++ ext/dom/parentnode.c | 22 +++++++++++++++---- ...entNode_append_fragment_text_coalesce.phpt | 21 ++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 ext/dom/tests/ParentNode_append_fragment_text_coalesce.phpt diff --git a/NEWS b/NEWS index b4eb80e33989d..ac8ee18065bad 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,8 @@ PHP NEWS . Fix references not handled correctly in C14N. (nielsdos) . Fix crash when calling childNodes next() when iterator is exhausted. (nielsdos) + . Fix crash in ParentNode::append() when dealing with a fragment + containing text nodes. (nielsdos) - Hash: . ext/hash: Swap the checking order of `__has_builtin` and `__GNUC__` diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index d6b0705545a17..c30db6fcd745f 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -150,6 +150,22 @@ static xmlDocPtr dom_doc_from_context_node(xmlNodePtr contextNode) } } +/* Citing from the docs (https://gnome.pages.gitlab.gnome.org/libxml2/devhelp/libxml2-tree.html#xmlAddChild): + * "Add a new node to @parent, at the end of the child (or property) list merging adjacent TEXT nodes (in which case @cur is freed)". + * So we must use a custom way of adding that does not merge. */ +static void dom_add_child_without_merging(xmlNodePtr parent, xmlNodePtr child) +{ + if (parent->children == NULL) { + parent->children = child; + } else { + xmlNodePtr last = parent->last; + last->next = child; + child->prev = last; + } + parent->last = child; + child->parent = parent; +} + xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNode, zval *nodes, int nodesc) { int i; @@ -183,7 +199,7 @@ xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNod * So we must take a copy if this situation arises to prevent a use-after-free. */ bool will_free = newNode->type == XML_TEXT_NODE && fragment->last && fragment->last->type == XML_TEXT_NODE; if (will_free) { - newNode = xmlCopyNode(newNode, 1); + newNode = xmlCopyNode(newNode, 0); } if (newNode->type == XML_DOCUMENT_FRAG_NODE) { @@ -192,9 +208,7 @@ xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNod while (newNode) { xmlNodePtr next = newNode->next; xmlUnlinkNode(newNode); - if (!xmlAddChild(fragment, newNode)) { - goto err; - } + dom_add_child_without_merging(fragment, newNode); newNode = next; } } else if (!xmlAddChild(fragment, newNode)) { diff --git a/ext/dom/tests/ParentNode_append_fragment_text_coalesce.phpt b/ext/dom/tests/ParentNode_append_fragment_text_coalesce.phpt new file mode 100644 index 0000000000000..601819d611775 --- /dev/null +++ b/ext/dom/tests/ParentNode_append_fragment_text_coalesce.phpt @@ -0,0 +1,21 @@ +--TEST-- +Text coalesce bug when appending fragment with text nodes +--EXTENSIONS-- +dom +--FILE-- +loadXML(''); + +$sut = $document->createDocumentFragment(); +for($i = 0; $i < 10; $i++) { + $textNode = $document->createTextNode("Node$i"); + $sut->append($textNode); +} + +$document->documentElement->append($sut); +echo $document->saveXML(); +?> +--EXPECT-- + +Node0Node1Node2Node3Node4Node5Node6Node7Node8Node9 From 5b6cda6523705c84139808802afc88e27489680f Mon Sep 17 00:00:00 2001 From: Benjamin Cremer Date: Wed, 8 May 2024 12:33:52 +0200 Subject: [PATCH 24/45] Fix GH-14175: Use two digit float specifier for FPM systemd format req rate Close GH-14175 --- NEWS | 4 ++++ sapi/fpm/fpm/fpm_systemd.c | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index ac8ee18065bad..19541227cf610 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,10 @@ PHP NEWS . Fix crash in ParentNode::append() when dealing with a fragment containing text nodes. (nielsdos) +- FPM: + . Fix bug GH-14175 (Show decimal number instead of scientific notation in + systemd status). (Benjamin Cremer) + - Hash: . ext/hash: Swap the checking order of `__has_builtin` and `__GNUC__` (Saki Takamachi) diff --git a/sapi/fpm/fpm/fpm_systemd.c b/sapi/fpm/fpm/fpm_systemd.c index 175312412330f..d5858ac780d64 100644 --- a/sapi/fpm/fpm/fpm_systemd.c +++ b/sapi/fpm/fpm/fpm_systemd.c @@ -29,13 +29,13 @@ static void fpm_systemd(void) } /* - zlog(ZLOG_DEBUG, "systemd %s (Processes active:%d, idle:%d, Requests:%lu, slow:%lu, Traffic:%.3greq/sec)", + zlog(ZLOG_DEBUG, "systemd %s (Processes active:%d, idle:%d, Requests:%lu, slow:%lu, Traffic:%.2freq/sec)", fpm_global_config.systemd_watchdog ? "watchdog" : "heartbeat", active, idle, requests, slow_req, ((float)requests - last) * 1000.0 / fpm_global_config.systemd_interval); */ if (0 > sd_notifyf(0, "READY=1\n%s" - "STATUS=Processes active: %d, idle: %d, Requests: %lu, slow: %lu, Traffic: %.3greq/sec", + "STATUS=Processes active: %d, idle: %d, Requests: %lu, slow: %lu, Traffic: %.2freq/sec", fpm_global_config.systemd_watchdog ? "WATCHDOG=1\n" : "", active, idle, requests, slow_req, ((float)requests - last) * 1000.0 / fpm_global_config.systemd_interval)) { zlog(ZLOG_NOTICE, "failed to notify status to systemd"); From 91c53e43c4350702d5c56795c7f50b83e5d8b213 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 13 May 2024 12:10:40 +0200 Subject: [PATCH 25/45] [skip ci] Adjust port for gh13860.phpt Port 64325 is already used in ext/standard/tests/streams/gh11418.phpt. The test randomly times out, and it's unclear whether it might be related to the conflicting port. --- ext/openssl/tests/gh13860.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/openssl/tests/gh13860.phpt b/ext/openssl/tests/gh13860.phpt index c7c0c40a15760..ed58ebc47697e 100644 --- a/ext/openssl/tests/gh13860.phpt +++ b/ext/openssl/tests/gh13860.phpt @@ -9,7 +9,7 @@ if (!function_exists("proc_open")) die("skip no proc_open"); --FILE-- Date: Mon, 13 May 2024 13:08:28 +0200 Subject: [PATCH 26/45] [skip ci] Fix typo --- ext/openssl/tests/gh13860.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/tests/gh13860.phpt b/ext/openssl/tests/gh13860.phpt index ed58ebc47697e..0b52e0e0583ab 100644 --- a/ext/openssl/tests/gh13860.phpt +++ b/ext/openssl/tests/gh13860.phpt @@ -9,7 +9,7 @@ if (!function_exists("proc_open")) die("skip no proc_open"); --FILE-- Date: Mon, 13 May 2024 22:09:47 +0200 Subject: [PATCH 27/45] Fix Windows SKIPIF (#14219) PHP_OS_FAMILY never has the value WIN, for Windows platforms it is the string "Windows". As such, this test was never executed. Fix this. --- tests/lang/bug38579.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lang/bug38579.phpt b/tests/lang/bug38579.phpt index 0d58eac52969b..1e6e351ee3e30 100644 --- a/tests/lang/bug38579.phpt +++ b/tests/lang/bug38579.phpt @@ -2,7 +2,7 @@ Bug #38579 (include_once() may include the same file twice) --SKIPIF-- From ebd1a36670552ad1aff8dda1b34d3d5da3bc2dc5 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 13 May 2024 20:28:59 +0200 Subject: [PATCH 28/45] Fix GH-14215: Cannot use FFI::load on CRLF header file with apache2handler Some modules may reset _fmode, which causes mangling of line endings. Always be explicit like we do in other places where the native open call is used. Closes GH-14218. --- NEWS | 4 ++++ ext/ffi/ffi.c | 6 +++++- ext/ffi/tests/gh14215.h | 3 +++ ext/ffi/tests/gh14215.phpt | 23 +++++++++++++++++++++++ ext/zend_test/test.c | 12 ++++++++++++ ext/zend_test/test.stub.php | 4 ++++ ext/zend_test/test_arginfo.h | 14 +++++++++++++- 7 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 ext/ffi/tests/gh14215.h create mode 100644 ext/ffi/tests/gh14215.phpt diff --git a/NEWS b/NEWS index 19541227cf610..c6706c2134dee 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,10 @@ PHP NEWS . Fix crash in ParentNode::append() when dealing with a fragment containing text nodes. (nielsdos) +- FFI: + . Fixed bug GH-14215 (Cannot use FFI::load on CRLF header file with + apache2handler). (nielsdos) + - FPM: . Fix bug GH-14175 (Show decimal number instead of scientific notation in systemd status). (Benjamin Cremer) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 5c6ca05f96108..560338e71f335 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -3255,7 +3255,11 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ code_size = buf.st_size; code = emalloc(code_size + 1); - fd = open(filename, O_RDONLY, 0); + int open_flags = O_RDONLY; +#ifdef PHP_WIN32 + open_flags |= _O_BINARY; +#endif + fd = open(filename, open_flags, 0); if (fd < 0 || read(fd, code, code_size) != code_size) { if (preload) { zend_error(E_WARNING, "FFI: Failed pre-loading '%s', cannot read_file", filename); diff --git a/ext/ffi/tests/gh14215.h b/ext/ffi/tests/gh14215.h new file mode 100644 index 0000000000000..9a1245b891297 --- /dev/null +++ b/ext/ffi/tests/gh14215.h @@ -0,0 +1,3 @@ +#define FFI_LIB "Kernel32.dll" +typedef unsigned long DWORD; +DWORD GetLastError(void); diff --git a/ext/ffi/tests/gh14215.phpt b/ext/ffi/tests/gh14215.phpt new file mode 100644 index 0000000000000..e259621e7a596 --- /dev/null +++ b/ext/ffi/tests/gh14215.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-14215 (Cannot use FFI::load on CRLF header file with apache2handler) +--EXTENSIONS-- +ffi +zend_test +--SKIPIF-- + +--INI-- +ffi.enable=1 +--FILE-- +GetLastError()); +zend_test_set_fmode(true); +?> +--EXPECT-- +int(0) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index d27b4543fcc01..8ae98eb106f58 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -584,6 +584,18 @@ static ZEND_FUNCTION(zend_test_is_pcre_bundled) #endif } +#ifdef PHP_WIN32 +static ZEND_FUNCTION(zend_test_set_fmode) +{ + bool binary; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_BOOL(binary) + ZEND_PARSE_PARAMETERS_END(); + + _fmode = binary ? _O_BINARY : _O_TEXT; +} +#endif + static zend_object *zend_test_class_new(zend_class_entry *class_type) { zend_object *obj = zend_objects_new(class_type); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index cf8ac50bd0629..0125b75f970ea 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -182,6 +182,10 @@ function zend_test_override_libxml_global_state(): void {} #endif function zend_test_is_pcre_bundled(): bool {} + +#if defined(PHP_WIN32) + function zend_test_set_fmode(bool $binary): void {} +#endif } namespace ZendTestNS { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 70439ff726164..8f3bcb1bdfb6f 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 420711ec6f040d38bde450a169bf1186f8531191 */ + * Stub hash: b0964f7eabf91dc0fbffdee87257ee4e58dab303 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -112,6 +112,12 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_is_pcre_bundled, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() +#if defined(PHP_WIN32) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_set_fmode, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, binary, _IS_BOOL, 0) +ZEND_END_ARG_INFO() +#endif + #define arginfo_ZendTestNS2_namespaced_func arginfo_zend_test_is_pcre_bundled #define arginfo_ZendTestNS2_namespaced_deprecated_func arginfo_zend_test_void_return @@ -216,6 +222,9 @@ static ZEND_FUNCTION(zend_test_crash); static ZEND_FUNCTION(zend_test_override_libxml_global_state); #endif static ZEND_FUNCTION(zend_test_is_pcre_bundled); +#if defined(PHP_WIN32) +static ZEND_FUNCTION(zend_test_set_fmode); +#endif static ZEND_FUNCTION(ZendTestNS2_namespaced_func); static ZEND_FUNCTION(ZendTestNS2_namespaced_deprecated_func); static ZEND_FUNCTION(ZendTestNS2_ZendSubNS_namespaced_func); @@ -275,6 +284,9 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_test_override_libxml_global_state, arginfo_zend_test_override_libxml_global_state) #endif ZEND_FE(zend_test_is_pcre_bundled, arginfo_zend_test_is_pcre_bundled) +#if defined(PHP_WIN32) + ZEND_FE(zend_test_set_fmode, arginfo_zend_test_set_fmode) +#endif ZEND_NS_FALIAS("ZendTestNS2", namespaced_func, ZendTestNS2_namespaced_func, arginfo_ZendTestNS2_namespaced_func) ZEND_NS_DEP_FALIAS("ZendTestNS2", namespaced_deprecated_func, ZendTestNS2_namespaced_deprecated_func, arginfo_ZendTestNS2_namespaced_deprecated_func) ZEND_NS_FALIAS("ZendTestNS2", namespaced_aliased_func, zend_test_void_return, arginfo_ZendTestNS2_namespaced_aliased_func) From 32d21af296b040cc0a223e8023778d369547f28f Mon Sep 17 00:00:00 2001 From: Marcus Xavier Date: Sat, 4 May 2024 22:05:49 -0300 Subject: [PATCH 29/45] Fix GH-14100: Corrected spelling mistake in php.ini files. Closes GH-14138. --- NEWS | 4 ++++ php.ini-development | 2 +- php.ini-production | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index c6706c2134dee..eecfe0f7c1e09 100644 --- a/NEWS +++ b/NEWS @@ -39,6 +39,10 @@ PHP NEWS . Fixed build regression on systems without C++17 compilers. (Calvin Buckley, Peter Kokot) +- Ini: + . Fixed bug GH-14100 (Corrected spelling mistake in php.ini files). + (Marcus Xavier) + - Opcache: . Fixed bug GH-14109 (Fix accidental persisting of internal class constant in shm). (ilutov) diff --git a/php.ini-development b/php.ini-development index 61cd33e89504c..2bb471298ecb6 100644 --- a/php.ini-development +++ b/php.ini-development @@ -75,7 +75,7 @@ ; php.ini-production contains settings which hold security, performance and ; best practices at its core. But please be aware, these settings may break -; compatibility with older or less security conscience applications. We +; compatibility with older or less security-conscious applications. We ; recommending using the production ini in production and testing environments. ; php.ini-development is very similar to its production variant, except it is diff --git a/php.ini-production b/php.ini-production index 2189660c02c48..4fcd47bff3509 100644 --- a/php.ini-production +++ b/php.ini-production @@ -75,7 +75,7 @@ ; php.ini-production contains settings which hold security, performance and ; best practices at its core. But please be aware, these settings may break -; compatibility with older or less security conscience applications. We +; compatibility with older or less security-conscious applications. We ; recommending using the production ini in production and testing environments. ; php.ini-development is very similar to its production variant, except it is From 02b7d70fa9b17b77d07c6e8c99487026b3aaa652 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 16 May 2024 20:00:46 +0200 Subject: [PATCH 30/45] Fix nightly test failure (#14251) The created files have the same filename, creating conflicts. Fix this by adding a unique suffix. --- .../tests/general_functions/ghsa-pc52-254m-w9w7_1.phpt | 4 ++-- .../tests/general_functions/ghsa-pc52-254m-w9w7_2.phpt | 4 ++-- .../tests/general_functions/ghsa-pc52-254m-w9w7_3.phpt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_1.phpt b/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_1.phpt index 8d0939cdf1bc7..55b6613c1c711 100644 --- a/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_1.phpt +++ b/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_1.phpt @@ -12,7 +12,7 @@ $batch_file_content = << diff --git a/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_2.phpt b/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_2.phpt index a1e39d7ef9ba0..0b186e977bf3f 100644 --- a/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_2.phpt +++ b/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_2.phpt @@ -12,7 +12,7 @@ $batch_file_content = <<^()!.exe --CLEAN-- diff --git a/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_3.phpt b/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_3.phpt index 69f12d7b358d2..e9bcd3bf24b96 100644 --- a/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_3.phpt +++ b/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_3.phpt @@ -12,7 +12,7 @@ $batch_file_content = << From a59868aef8e00ae4eadb752e28166237769e2541 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Thu, 16 May 2024 21:35:03 +0200 Subject: [PATCH 31/45] Clear mysql error in fetch_into Closes GH-14256 --- NEWS | 4 ++++ ext/mysqli/mysqli.c | 2 +- ext/mysqli/tests/gh14255.phpt | 31 +++++++++++++++++++++++++++++++ ext/mysqlnd/mysqlnd_result.c | 7 +++++++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 ext/mysqli/tests/gh14255.phpt diff --git a/NEWS b/NEWS index eecfe0f7c1e09..31e8843282c25 100644 --- a/NEWS +++ b/NEWS @@ -43,6 +43,10 @@ PHP NEWS . Fixed bug GH-14100 (Corrected spelling mistake in php.ini files). (Marcus Xavier) +- MySQLnd: + . Fix bug GH-14255 (mysqli_fetch_assoc reports error from + nested query). (Kamil Tekiela) + - Opcache: . Fixed bug GH-14109 (Fix accidental persisting of internal class constant in shm). (ilutov) diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 9fa46acbb7d29..14c2af6a1a902 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -731,7 +731,7 @@ void php_mysqli_fetch_into_hash_aux(zval *return_value, MYSQL_RES * result, zend /* TODO: We don't have access to the connection object at this point, so we use low-level * mysqlnd APIs to access the error information. We should try to pass through the connection * object instead. */ - if (MyG(report_mode) & MYSQLI_REPORT_ERROR) { + if (MyG(report_mode) & MYSQLI_REPORT_ERROR && result->conn) { MYSQLND_CONN_DATA *conn = result->conn; unsigned error_no = conn->m->get_error_no(conn); if (error_no) { diff --git a/ext/mysqli/tests/gh14255.phpt b/ext/mysqli/tests/gh14255.phpt new file mode 100644 index 0000000000000..375eda0c5b52c --- /dev/null +++ b/ext/mysqli/tests/gh14255.phpt @@ -0,0 +1,31 @@ +--TEST-- +Bug GH-14255 (mysqli_fetch_assoc reports error from nested query) +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--FILE-- +query('SELECT 1 '); +$c = $ca->fetch_assoc(); +try { + $mysqli->query('SELECT non_existent_column'); +} catch (Exception $e) { + echo "Caught exception"."\n"; +} +$c = $ca->fetch_assoc(); + +print "done!"; +?> +--EXPECTF-- +Caught exception +done! diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index 0331518d7d3d6..cf091a802bb66 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -972,6 +972,13 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, const unsigned int bool fetched_anything; zval *row_data; + // We clean the error here because in unbuffered mode we could receive a new error + // and therefore consumers of this method are checking for errors + MYSQLND_CONN_DATA *conn = result->conn; + if (conn) { + SET_EMPTY_ERROR(conn->error_info); + } + DBG_ENTER("mysqlnd_res::fetch_into"); if (FAIL == result->m.fetch_row(result, &row_data, flags, &fetched_anything)) { RETVAL_FALSE; From ad5138a341ff611963122942ff600dcfb667cc5b Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 19 May 2024 22:54:06 +0200 Subject: [PATCH 32/45] Fix undefined behaviour in fastcgi.c (#14269) Arithmetic on NULL pointers is undefined. --- main/fastcgi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/fastcgi.c b/main/fastcgi.c index 85e73f2d8bfe1..18eb4b394bc2a 100644 --- a/main/fastcgi.c +++ b/main/fastcgi.c @@ -744,7 +744,7 @@ int fcgi_listen(const char *path, int backlog) memset(&sa.sa_unix, 0, sizeof(sa.sa_unix)); sa.sa_unix.sun_family = AF_UNIX; memcpy(sa.sa_unix.sun_path, path, path_len + 1); - sock_len = (size_t)(((struct sockaddr_un *)0)->sun_path) + path_len; + sock_len = XtOffsetOf(struct sockaddr_un, sun_path) + path_len; #ifdef HAVE_SOCKADDR_UN_SUN_LEN sa.sa_unix.sun_len = sock_len; #endif From e5cb9d7df9bae8f1e69b04dbe379892b5f499d86 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Tue, 7 May 2024 09:34:50 +0100 Subject: [PATCH 33/45] Force ffp-contract to off --- NEWS | 2 ++ configure.ac | 3 +++ 2 files changed, 5 insertions(+) diff --git a/NEWS b/NEWS index 31e8843282c25..235353a39a103 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,8 @@ PHP NEWS - Core: . Fixed bug GH-13970 (Incorrect validation of #[Attribute] flags type for non-compile-time expressions). (ilutov) + . Fixed bug GH-14140 (Floating point bug in range operation on Apple Silicon + hardware). (Derick, Saki) - DOM: . Fix crashes when entity declaration is removed while still having entity diff --git a/configure.ac b/configure.ac index b8b8d413421aa..ba4a2654a5f25 100644 --- a/configure.ac +++ b/configure.ac @@ -218,6 +218,9 @@ case $host_cpu in ;; esac +dnl See https://github.com/php/php-src/issues/14140 +AX_CHECK_COMPILE_FLAG([-ffp-contract=off], [CFLAGS="$CFLAGS -ffp-contract=off"]) + dnl Mark symbols hidden by default if the compiler (for example, gcc >= 4) dnl supports it. This can help reduce the binary size and startup time. AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], From 0a8fbef996fe2b104c4eb3a978b4decf6292551e Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Sat, 18 May 2024 07:08:39 +0900 Subject: [PATCH 34/45] Added test --- ext/standard/tests/array/gh14140.phpt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 ext/standard/tests/array/gh14140.phpt diff --git a/ext/standard/tests/array/gh14140.phpt b/ext/standard/tests/array/gh14140.phpt new file mode 100644 index 0000000000000..2bc17a236a7a3 --- /dev/null +++ b/ext/standard/tests/array/gh14140.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-14140: Floating point bug in range operation on Apple Silicon hardware +--FILE-- + +--EXPECT-- +Array +( + [0] => -0.03 + [1] => -0.02 + [2] => -0.01 + [3] => 0 + [4] => 0.01 + [5] => 0.02 + [6] => 0.03 +) From 98288a27bcc3c1539213d2c24679745dfd4522ad Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 20 May 2024 23:26:52 +0200 Subject: [PATCH 35/45] Skip .NET tests if mscoree is not available (#14281) --- ext/com_dotnet/tests/bug73679.phpt | 1 + ext/com_dotnet/tests/bug77177.phpt | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/ext/com_dotnet/tests/bug73679.phpt b/ext/com_dotnet/tests/bug73679.phpt index 9815cdcaacd82..56f02dd8f7234 100644 --- a/ext/com_dotnet/tests/bug73679.phpt +++ b/ext/com_dotnet/tests/bug73679.phpt @@ -5,6 +5,7 @@ com_dotnet --SKIPIF-- --FILE-- --FILE-- Date: Mon, 20 May 2024 15:41:43 +0100 Subject: [PATCH 36/45] ext/bcmath: Fix [-Wenum-int-mismatch] compiler warning --- ext/bcmath/libbcmath/src/bcmath.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/bcmath/libbcmath/src/bcmath.h b/ext/bcmath/libbcmath/src/bcmath.h index e6273c18a1101..2d247bd860b2e 100644 --- a/ext/bcmath/libbcmath/src/bcmath.h +++ b/ext/bcmath/libbcmath/src/bcmath.h @@ -128,7 +128,7 @@ int bc_modulo(bc_num num1, bc_num num2, bc_num *resul, int scale); int bc_divmod(bc_num num1, bc_num num2, bc_num *quo, bc_num *rem, int scale); -int bc_raisemod(bc_num base, bc_num expo, bc_num mo, bc_num *result, int scale); +zend_result bc_raisemod(bc_num base, bc_num expo, bc_num mo, bc_num *result, int scale); void bc_raise(bc_num num1, bc_num num2, bc_num *resul, int scale); From 554541c4dbcf74a7f8170ef537ca5e7f347e2da0 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 20 May 2024 15:42:12 +0100 Subject: [PATCH 37/45] ext/ffi: Fix [-Wenum-int-mismatch] compiler warning --- ext/ffi/ffi_parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/ffi/ffi_parser.c b/ext/ffi/ffi_parser.c index eca10c27d195b..b956f885ee001 100644 --- a/ext/ffi/ffi_parser.c +++ b/ext/ffi/ffi_parser.c @@ -3552,7 +3552,7 @@ static void parse(void) { } } -int zend_ffi_parse_decl(const char *str, size_t len) { +zend_result zend_ffi_parse_decl(const char *str, size_t len) { if (SETJMP(FFI_G(bailout))==0) { FFI_G(allow_vla) = 0; FFI_G(attribute_parsing) = 0; @@ -3565,7 +3565,7 @@ int zend_ffi_parse_decl(const char *str, size_t len) { } } -int zend_ffi_parse_type(const char *str, size_t len, zend_ffi_dcl *dcl) { +zend_result zend_ffi_parse_type(const char *str, size_t len, zend_ffi_dcl *dcl) { int sym; if (SETJMP(FFI_G(bailout))==0) { From 3c45152798b1fca856c1318d68401f683a336384 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 20 May 2024 15:43:03 +0100 Subject: [PATCH 38/45] ext/gd: Fix [-Wcalloc-transposed-args] compiler warning --- ext/gd/libgd/gd_topal.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/gd/libgd/gd_topal.c b/ext/gd/libgd/gd_topal.c index a92a7acb17a43..2a9fb3d608dc8 100644 --- a/ext/gd/libgd/gd_topal.c +++ b/ext/gd/libgd/gd_topal.c @@ -1498,7 +1498,7 @@ static int gdImageTrueColorToPaletteBody (gdImagePtr oim, int dither, int colors colorsWanted = maxColors; } if (!cimP) { - nim->pixels = gdCalloc (sizeof (unsigned char *), oim->sy); + nim->pixels = gdCalloc (oim->sy, sizeof (unsigned char *)); if (!nim->pixels) { /* No can do */ @@ -1506,7 +1506,7 @@ static int gdImageTrueColorToPaletteBody (gdImagePtr oim, int dither, int colors } for (i = 0; (i < nim->sy); i++) { - nim->pixels[i] = gdCalloc (sizeof (unsigned char *), oim->sx); + nim->pixels[i] = gdCalloc (oim->sx, sizeof (unsigned char *)); if (!nim->pixels[i]) { goto outOfMemory; @@ -1514,7 +1514,7 @@ static int gdImageTrueColorToPaletteBody (gdImagePtr oim, int dither, int colors } } - cquantize = (my_cquantize_ptr) gdCalloc (sizeof (my_cquantizer), 1); + cquantize = (my_cquantize_ptr) gdCalloc (1, sizeof (my_cquantizer)); if (!cquantize) { /* No can do */ From d4accd8b1253351c9775fc2ce33d471c759eaead Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 20 May 2024 15:43:17 +0100 Subject: [PATCH 39/45] ext/pdo_mysql: Fix [-Wcalloc-transposed-args] compiler warning --- ext/pdo_mysql/mysql_statement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/pdo_mysql/mysql_statement.c b/ext/pdo_mysql/mysql_statement.c index 765e05e1358e3..c4f86b0e7d320 100644 --- a/ext/pdo_mysql/mysql_statement.c +++ b/ext/pdo_mysql/mysql_statement.c @@ -581,7 +581,7 @@ static int pdo_mysql_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori } if (!S->current_row) { - S->current_row = ecalloc(sizeof(zval), stmt->column_count); + S->current_row = ecalloc(stmt->column_count, sizeof(zval)); } for (unsigned i = 0; i < stmt->column_count; i++) { zval_ptr_dtor_nogc(&S->current_row[i]); From 0accfd1fe1de164ca2c87c14a9d1322aafe37c4e Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 20 May 2024 15:43:33 +0100 Subject: [PATCH 40/45] ext/readline: Fix [-Wcalloc-transposed-args] compiler warning Closes GH-14280 --- ext/readline/readline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/readline/readline.c b/ext/readline/readline.c index db2776fb27a9a..1bd5e2fd6059a 100644 --- a/ext/readline/readline.c +++ b/ext/readline/readline.c @@ -456,7 +456,7 @@ char **php_readline_completion_cb(const char *text, int start, int end) matches = rl_completion_matches(text,_readline_command_generator); } else { /* libedit will read matches[2] */ - matches = calloc(sizeof(char *), 3); + matches = calloc(3, sizeof(char *)); if (!matches) { return NULL; } From 39c1a90f4a90c3f0191ba11ed3cd0366a810d2cf Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 20 Apr 2024 17:09:35 +0200 Subject: [PATCH 41/45] Fix GHSA-9fcc-425m-g385: bypass CVE-2024-1874 The old code checked for suffixes but didn't take into account trailing whitespace. Furthermore, there is peculiar behaviour with trailing dots too. This all happens because of the special path-handling code inside CreateProcessW. By studying Wine's code, we can see that CreateProcessInternalW calls get_file_name [1] in our case because we haven't provided an application name. That code gets the first whitespace-delimited string into app_name excluding the quotes. It's then passed to create_process_params [2] where there is the path handling code that transforms the command line argument to an image path [3]. Inside Wine, the extension check if performed after these transformations [4]. By doing the same thing in PHP we match the behaviour and can properly match the extension even in the given edge cases. [1] https://github.com/wine-mirror/wine/blob/166895ae3ad3890ad946a309d0fd85e89ea3630e/dlls/kernelbase/process.c#L542-L543 [2] https://github.com/wine-mirror/wine/blob/166895ae3ad3890ad946a309d0fd85e89ea3630e/dlls/kernelbase/process.c#L565 [3] https://github.com/wine-mirror/wine/blob/166895ae3ad3890ad946a309d0fd85e89ea3630e/dlls/kernelbase/process.c#L150-L151 [4] https://github.com/wine-mirror/wine/blob/166895ae3ad3890ad946a309d0fd85e89ea3630e/dlls/kernelbase/process.c#L647-L654 --- ext/standard/proc_open.c | 59 +- .../ghsa-9fcc-425m-g385_001.phpt | 56 ++ .../ghsa-9fcc-425m-g385_002.phpt | 66 +++ .../ghsa-9fcc-425m-g385_003.phpt | 550 ++++++++++++++++++ 4 files changed, 697 insertions(+), 34 deletions(-) create mode 100644 ext/standard/tests/general_functions/ghsa-9fcc-425m-g385_001.phpt create mode 100644 ext/standard/tests/general_functions/ghsa-9fcc-425m-g385_002.phpt create mode 100644 ext/standard/tests/general_functions/ghsa-9fcc-425m-g385_003.phpt diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index 26bbe0afa3ee0..03c0063a466de 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -546,48 +546,39 @@ static void append_win_escaped_arg(smart_str *str, zend_string *arg, bool is_cmd smart_str_appendc(str, '"'); } -static inline int stricmp_end(const char* suffix, const char* str) { - size_t suffix_len = strlen(suffix); - size_t str_len = strlen(str); +static bool is_executed_by_cmd(const char *prog_name, size_t prog_name_length) +{ + size_t out_len; + WCHAR long_name[MAX_PATH]; + WCHAR full_name[MAX_PATH]; + LPWSTR file_part = NULL; - if (suffix_len > str_len) { - return -1; /* Suffix is longer than string, cannot match. */ - } + wchar_t *prog_name_wide = php_win32_cp_conv_any_to_w(prog_name, prog_name_length, &out_len); - /* Compare the end of the string with the suffix, ignoring case. */ - return _stricmp(str + (str_len - suffix_len), suffix); -} + if (GetLongPathNameW(prog_name_wide, long_name, MAX_PATH) == 0) { + /* This can fail for example with ERROR_FILE_NOT_FOUND (short path resolution only works for existing files) + * in which case we'll pass the path verbatim to the FullPath transformation. */ + lstrcpynW(long_name, prog_name_wide, MAX_PATH); + } -static bool is_executed_by_cmd(const char *prog_name) -{ - /* If program name is cmd.exe, then return true. */ - if (_stricmp("cmd.exe", prog_name) == 0 || _stricmp("cmd", prog_name) == 0 - || stricmp_end("\\cmd.exe", prog_name) == 0 || stricmp_end("\\cmd", prog_name) == 0) { - return true; - } + free(prog_name_wide); + prog_name_wide = NULL; - /* Find the last occurrence of the directory separator (backslash or forward slash). */ - char *last_separator = strrchr(prog_name, '\\'); - char *last_separator_fwd = strrchr(prog_name, '/'); - if (last_separator_fwd && (!last_separator || last_separator < last_separator_fwd)) { - last_separator = last_separator_fwd; + if (GetFullPathNameW(long_name, MAX_PATH, full_name, &file_part) == 0 || file_part == NULL) { + return false; } - /* Find the last dot in the filename after the last directory separator. */ - char *extension = NULL; - if (last_separator != NULL) { - extension = strrchr(last_separator, '.'); + bool uses_cmd = false; + if (_wcsicmp(file_part, L"cmd.exe") == 0 || _wcsicmp(file_part, L"cmd") == 0) { + uses_cmd = true; } else { - extension = strrchr(prog_name, '.'); - } - - if (extension == NULL || extension == prog_name) { - /* No file extension found, it is not batch file. */ - return false; + const WCHAR *extension_dot = wcsrchr(file_part, L'.'); + if (extension_dot && (_wcsicmp(extension_dot, L".bat") == 0 || _wcsicmp(extension_dot, L".cmd") == 0)) { + uses_cmd = true; + } } - /* Check if the file extension is ".bat" or ".cmd" which is always executed by cmd.exe. */ - return _stricmp(extension, ".bat") == 0 || _stricmp(extension, ".cmd") == 0; + return uses_cmd; } static zend_string *create_win_command_from_args(HashTable *args) @@ -606,7 +597,7 @@ static zend_string *create_win_command_from_args(HashTable *args) } if (is_prog_name) { - is_cmd_execution = is_executed_by_cmd(ZSTR_VAL(arg_str)); + is_cmd_execution = is_executed_by_cmd(ZSTR_VAL(arg_str), ZSTR_LEN(arg_str)); } else { smart_str_appendc(&str, ' '); } diff --git a/ext/standard/tests/general_functions/ghsa-9fcc-425m-g385_001.phpt b/ext/standard/tests/general_functions/ghsa-9fcc-425m-g385_001.phpt new file mode 100644 index 0000000000000..2873210608497 --- /dev/null +++ b/ext/standard/tests/general_functions/ghsa-9fcc-425m-g385_001.phpt @@ -0,0 +1,56 @@ +--TEST-- +GHSA-9fcc-425m-g385 - bypass CVE-2024-1874 - batch file variation +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +'"%sghsa-9fcc-425m-g385_001.bat."' is not recognized as an internal or external command, +operable program or batch file. +%sghsa-9fcc-425m-g385_001.bat +"¬epad.exe +%sghsa-9fcc-425m-g385_001.bat. +"¬epad.exe +%sghsa-9fcc-425m-g385_001.bat. ... +"¬epad.exe +%sghsa-9fcc-425m-g385_001.bat. ... . +"¬epad.exe +'"%sghsa-9fcc-425m-g385_001.bat. ... . ."' is not recognized as an internal or external command, +operable program or batch file. + +Warning: proc_open(): CreateProcess failed, error code: 2 in %s on line %d +--CLEAN-- + diff --git a/ext/standard/tests/general_functions/ghsa-9fcc-425m-g385_002.phpt b/ext/standard/tests/general_functions/ghsa-9fcc-425m-g385_002.phpt new file mode 100644 index 0000000000000..714836557af5c --- /dev/null +++ b/ext/standard/tests/general_functions/ghsa-9fcc-425m-g385_002.phpt @@ -0,0 +1,66 @@ +--TEST-- +GHSA-9fcc-425m-g385 - bypass CVE-2024-1874 - cmd.exe variation +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%sghsa-9fcc-425m-g385_002.bat +"¬epad.exe +%sghsa-9fcc-425m-g385_002.bat +"¬epad.exe +%sghsa-9fcc-425m-g385_002.bat +"¬epad.exe +%sghsa-9fcc-425m-g385_002.bat +"¬epad.exe + +Warning: proc_open(): CreateProcess failed, error code: 2 in %s on line %d +%sghsa-9fcc-425m-g385_002.bat +"¬epad.exe +%sghsa-9fcc-425m-g385_002.bat +"¬epad.exe + +Warning: proc_open(): CreateProcess failed, error code: 2 in %s on line %d + +Warning: proc_open(): CreateProcess failed, error code: 2 in %s on line %d + +Warning: proc_open(): CreateProcess failed, error code: 2 in %s on line %d +--CLEAN-- + diff --git a/ext/standard/tests/general_functions/ghsa-9fcc-425m-g385_003.phpt b/ext/standard/tests/general_functions/ghsa-9fcc-425m-g385_003.phpt new file mode 100644 index 0000000000000..a632965eb989a --- /dev/null +++ b/ext/standard/tests/general_functions/ghsa-9fcc-425m-g385_003.phpt @@ -0,0 +1,550 @@ +--TEST-- +GHSA-9fcc-425m-g385 - bypass CVE-2024-1874 - exhaustive suffix test +--SKIPIF-- + +--FILE-- + true)); + var_dump($proc); + proc_close($proc); + } catch (Error) {} +} + +?> +--EXPECTF-- +Testing 1 +bool(false) +Testing 2 +bool(false) +Testing 3 +bool(false) +Testing 4 +bool(false) +Testing 5 +bool(false) +Testing 6 +bool(false) +Testing 7 +bool(false) +Testing 8 +bool(false) +Testing 9 +bool(false) +Testing 10 +bool(false) +Testing 11 +bool(false) +Testing 12 +bool(false) +Testing 13 +bool(false) +Testing 14 +bool(false) +Testing 15 +bool(false) +Testing 16 +bool(false) +Testing 17 +bool(false) +Testing 18 +bool(false) +Testing 19 +bool(false) +Testing 20 +bool(false) +Testing 21 +bool(false) +Testing 22 +bool(false) +Testing 23 +bool(false) +Testing 24 +bool(false) +Testing 25 +bool(false) +Testing 26 +bool(false) +Testing 27 +bool(false) +Testing 28 +bool(false) +Testing 29 +bool(false) +Testing 30 +bool(false) +Testing 31 +bool(false) +Testing 32 +resource(%d) of type (process) +%s.bat +"¬epad.exe +Testing 33 +bool(false) +Testing 34 +bool(false) +Testing 35 +bool(false) +Testing 36 +bool(false) +Testing 37 +bool(false) +Testing 38 +bool(false) +Testing 39 +bool(false) +Testing 40 +bool(false) +Testing 41 +bool(false) +Testing 42 +bool(false) +Testing 43 +bool(false) +Testing 44 +bool(false) +Testing 45 +bool(false) +Testing 46 +resource(%d) of type (process) +'"%s.bat."' is not recognized as an internal or external command, +operable program or batch file. +Testing 47 +bool(false) +Testing 48 +bool(false) +Testing 49 +bool(false) +Testing 50 +bool(false) +Testing 51 +bool(false) +Testing 52 +bool(false) +Testing 53 +bool(false) +Testing 54 +bool(false) +Testing 55 +bool(false) +Testing 56 +bool(false) +Testing 57 +bool(false) +Testing 58 +bool(false) +Testing 59 +bool(false) +Testing 60 +bool(false) +Testing 61 +bool(false) +Testing 62 +bool(false) +Testing 63 +bool(false) +Testing 64 +bool(false) +Testing 65 +bool(false) +Testing 66 +bool(false) +Testing 67 +bool(false) +Testing 68 +bool(false) +Testing 69 +bool(false) +Testing 70 +bool(false) +Testing 71 +bool(false) +Testing 72 +bool(false) +Testing 73 +bool(false) +Testing 74 +bool(false) +Testing 75 +bool(false) +Testing 76 +bool(false) +Testing 77 +bool(false) +Testing 78 +bool(false) +Testing 79 +bool(false) +Testing 80 +bool(false) +Testing 81 +bool(false) +Testing 82 +bool(false) +Testing 83 +bool(false) +Testing 84 +bool(false) +Testing 85 +bool(false) +Testing 86 +bool(false) +Testing 87 +bool(false) +Testing 88 +bool(false) +Testing 89 +bool(false) +Testing 90 +bool(false) +Testing 91 +bool(false) +Testing 92 +bool(false) +Testing 93 +bool(false) +Testing 94 +bool(false) +Testing 95 +bool(false) +Testing 96 +bool(false) +Testing 97 +bool(false) +Testing 98 +bool(false) +Testing 99 +bool(false) +Testing 100 +bool(false) +Testing 101 +bool(false) +Testing 102 +bool(false) +Testing 103 +bool(false) +Testing 104 +bool(false) +Testing 105 +bool(false) +Testing 106 +bool(false) +Testing 107 +bool(false) +Testing 108 +bool(false) +Testing 109 +bool(false) +Testing 110 +bool(false) +Testing 111 +bool(false) +Testing 112 +bool(false) +Testing 113 +bool(false) +Testing 114 +bool(false) +Testing 115 +bool(false) +Testing 116 +bool(false) +Testing 117 +bool(false) +Testing 118 +bool(false) +Testing 119 +bool(false) +Testing 120 +bool(false) +Testing 121 +bool(false) +Testing 122 +bool(false) +Testing 123 +bool(false) +Testing 124 +bool(false) +Testing 125 +bool(false) +Testing 126 +bool(false) +Testing 127 +bool(false) +Testing 128 +bool(false) +Testing 129 +bool(false) +Testing 130 +bool(false) +Testing 131 +bool(false) +Testing 132 +bool(false) +Testing 133 +bool(false) +Testing 134 +bool(false) +Testing 135 +bool(false) +Testing 136 +bool(false) +Testing 137 +bool(false) +Testing 138 +bool(false) +Testing 139 +bool(false) +Testing 140 +bool(false) +Testing 141 +bool(false) +Testing 142 +bool(false) +Testing 143 +bool(false) +Testing 144 +bool(false) +Testing 145 +bool(false) +Testing 146 +bool(false) +Testing 147 +bool(false) +Testing 148 +bool(false) +Testing 149 +bool(false) +Testing 150 +bool(false) +Testing 151 +bool(false) +Testing 152 +bool(false) +Testing 153 +bool(false) +Testing 154 +bool(false) +Testing 155 +bool(false) +Testing 156 +bool(false) +Testing 157 +bool(false) +Testing 158 +bool(false) +Testing 159 +bool(false) +Testing 160 +bool(false) +Testing 161 +bool(false) +Testing 162 +bool(false) +Testing 163 +bool(false) +Testing 164 +bool(false) +Testing 165 +bool(false) +Testing 166 +bool(false) +Testing 167 +bool(false) +Testing 168 +bool(false) +Testing 169 +bool(false) +Testing 170 +bool(false) +Testing 171 +bool(false) +Testing 172 +bool(false) +Testing 173 +bool(false) +Testing 174 +bool(false) +Testing 175 +bool(false) +Testing 176 +bool(false) +Testing 177 +bool(false) +Testing 178 +bool(false) +Testing 179 +bool(false) +Testing 180 +bool(false) +Testing 181 +bool(false) +Testing 182 +bool(false) +Testing 183 +bool(false) +Testing 184 +bool(false) +Testing 185 +bool(false) +Testing 186 +bool(false) +Testing 187 +bool(false) +Testing 188 +bool(false) +Testing 189 +bool(false) +Testing 190 +bool(false) +Testing 191 +bool(false) +Testing 192 +bool(false) +Testing 193 +bool(false) +Testing 194 +bool(false) +Testing 195 +bool(false) +Testing 196 +bool(false) +Testing 197 +bool(false) +Testing 198 +bool(false) +Testing 199 +bool(false) +Testing 200 +bool(false) +Testing 201 +bool(false) +Testing 202 +bool(false) +Testing 203 +bool(false) +Testing 204 +bool(false) +Testing 205 +bool(false) +Testing 206 +bool(false) +Testing 207 +bool(false) +Testing 208 +bool(false) +Testing 209 +bool(false) +Testing 210 +bool(false) +Testing 211 +bool(false) +Testing 212 +bool(false) +Testing 213 +bool(false) +Testing 214 +bool(false) +Testing 215 +bool(false) +Testing 216 +bool(false) +Testing 217 +bool(false) +Testing 218 +bool(false) +Testing 219 +bool(false) +Testing 220 +bool(false) +Testing 221 +bool(false) +Testing 222 +bool(false) +Testing 223 +bool(false) +Testing 224 +bool(false) +Testing 225 +bool(false) +Testing 226 +bool(false) +Testing 227 +bool(false) +Testing 228 +bool(false) +Testing 229 +bool(false) +Testing 230 +bool(false) +Testing 231 +bool(false) +Testing 232 +bool(false) +Testing 233 +bool(false) +Testing 234 +bool(false) +Testing 235 +bool(false) +Testing 236 +bool(false) +Testing 237 +bool(false) +Testing 238 +bool(false) +Testing 239 +bool(false) +Testing 240 +bool(false) +Testing 241 +bool(false) +Testing 242 +bool(false) +Testing 243 +bool(false) +Testing 244 +bool(false) +Testing 245 +bool(false) +Testing 246 +bool(false) +Testing 247 +bool(false) +Testing 248 +bool(false) +Testing 249 +bool(false) +Testing 250 +bool(false) +Testing 251 +bool(false) +Testing 252 +bool(false) +Testing 253 +bool(false) +Testing 254 +bool(false) +Testing 255 +bool(false) +--CLEAN-- + From 88315f263dc4d64855a54e24b71970ad605fbacc Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 17 May 2024 21:51:30 +0200 Subject: [PATCH 42/45] Fix GHSA-3qgc-jrrr-25jv The original code is error-prone due to the "best fit mapping" that happens with the argument parsing but not with the query string. When we get a non-ASCII character, try to remap it and see if it becomes a hyphen. An alternative approach is to create a custom main `wmain` receiving wide-character variations that does the ANSI transformation with the best-fit mapping, but that's more error-prone and could cause unexpected breakage. Another alternative was just don't doing this check altogether and always check for `cgi || fastcgi` instead, but that breaks real-world use-cases. --- sapi/cgi/cgi_main.c | 23 ++++++++++++++- sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt | 38 +++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c index 1b95afd7acd3c..01fdebae0bde1 100644 --- a/sapi/cgi/cgi_main.c +++ b/sapi/cgi/cgi_main.c @@ -1796,8 +1796,13 @@ int main(int argc, char *argv[]) } } + /* Apache CGI will pass the query string to the command line if it doesn't contain a '='. + * This can create an issue where a malicious request can pass command line arguments to + * the executable. Ideally we skip argument parsing when we're in cgi or fastcgi mode, + * but that breaks PHP scripts on Linux with a hashbang: `#!/php-cgi -d option=value`. + * Therefore, this code only prevents passing arguments if the query string starts with a '-'. + * Similarly, scripts spawned in subprocesses on Windows may have the same issue. */ if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) { - /* we've got query string that has no = - apache CGI will pass it to command line */ unsigned char *p; decoded_query_string = strdup(query_string); php_url_decode(decoded_query_string, strlen(decoded_query_string)); @@ -1807,6 +1812,22 @@ int main(int argc, char *argv[]) if(*p == '-') { skip_getopt = 1; } + + /* On Windows we have to take into account the "best fit" mapping behaviour. */ +#ifdef PHP_WIN32 + if (*p >= 0x80) { + wchar_t wide_buf[1]; + wide_buf[0] = *p; + char char_buf[4]; + size_t wide_buf_len = sizeof(wide_buf) / sizeof(wide_buf[0]); + size_t char_buf_len = sizeof(char_buf) / sizeof(char_buf[0]); + if (WideCharToMultiByte(CP_ACP, 0, wide_buf, wide_buf_len, char_buf, char_buf_len, NULL, NULL) == 0 + || char_buf[0] == '-') { + skip_getopt = 1; + } + } +#endif + free(decoded_query_string); } diff --git a/sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt b/sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt new file mode 100644 index 0000000000000..fd2fcdfbf897d --- /dev/null +++ b/sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt @@ -0,0 +1,38 @@ +--TEST-- +GHSA-3qgc-jrrr-25jv +--SKIPIF-- + +--FILE-- +'; +file_put_contents($filename, $script); + +$php = get_cgi_path(); +reset_env_vars(); + +putenv("SERVER_NAME=Test"); +putenv("SCRIPT_FILENAME=$filename"); +putenv("QUERY_STRING=%ads"); +putenv("REDIRECT_STATUS=1"); + +passthru("$php -s"); + +?> +--CLEAN-- + +--EXPECTF-- +X-Powered-By: PHP/%s +Content-type: %s + +hello world From ec803292c2f8e0b9a8c396946df88465f18cfe7a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 22 May 2024 22:25:02 +0200 Subject: [PATCH 43/45] Fix GHSA-w8qr-v226-r27w We should not early-out with success status if we found an ipv6 hostname, we should keep checking the rest of the conditions. Because integrating the if-check of the ipv6 hostname in the "Validate domain" if-check made the code hard to read, I extracted the condition out to a separate function. This also required to make a few pointers const in order to have some clean code. --- ext/filter/logical_filters.c | 35 ++++++++++--------- ext/filter/tests/ghsa-w8qr-v226-r27w.phpt | 41 +++++++++++++++++++++++ 2 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 ext/filter/tests/ghsa-w8qr-v226-r27w.phpt diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 3f58b2a3c4fb1..ca8e65c1f75f6 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -89,7 +89,7 @@ #define FORMAT_IPV4 4 #define FORMAT_IPV6 6 -static int _php_filter_validate_ipv6(char *str, size_t str_len, int ip[8]); +static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]); static int php_filter_parse_int(const char *str, size_t str_len, zend_long *ret) { /* {{{ */ zend_long ctx_value; @@ -580,6 +580,14 @@ static int is_userinfo_valid(zend_string *str) return 1; } +static bool php_filter_is_valid_ipv6_hostname(const char *s, size_t l) +{ + const char *e = s + l; + const char *t = e - 1; + + return *s == '[' && *t == ']' && _php_filter_validate_ipv6(s + 1, l - 2, NULL); +} + void php_filter_validate_url(https://redirect.github.com/PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ { php_url *url; @@ -600,7 +608,7 @@ void php_filter_validate_url(https://redirect.github.com/PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ if (url->scheme != NULL && (zend_string_equals_literal_ci(url->scheme, "http") || zend_string_equals_literal_ci(url->scheme, "https"))) { - char *e, *s, *t; + const char *s; size_t l; if (url->host == NULL) { @@ -609,17 +617,14 @@ void php_filter_validate_url(https://redirect.github.com/PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ s = ZSTR_VAL(url->host); l = ZSTR_LEN(url->host); - e = s + l; - t = e - 1; - - /* An IPv6 enclosed by square brackets is a valid hostname */ - if (*s == '[' && *t == ']' && _php_filter_validate_ipv6((s + 1), l - 2, NULL)) { - php_url_free(url); - return; - } - // Validate domain - if (!_php_filter_validate_domain(ZSTR_VAL(url->host), l, FILTER_FLAG_HOSTNAME)) { + if ( + /* An IPv6 enclosed by square brackets is a valid hostname.*/ + !php_filter_is_valid_ipv6_hostname(s, l) && + /* Validate domain. + * This includes a loose check for an IPv4 address. */ + !_php_filter_validate_domain(ZSTR_VAL(url->host), l, FILTER_FLAG_HOSTNAME) + ) { php_url_free(url); RETURN_VALIDATION_FAILED } @@ -753,15 +758,15 @@ static int _php_filter_validate_ipv4(char *str, size_t str_len, int *ip) /* {{{ } /* }}} */ -static int _php_filter_validate_ipv6(char *str, size_t str_len, int ip[8]) /* {{{ */ +static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]) /* {{{ */ { int compressed_pos = -1; int blocks = 0; int num, n, i; char *ipv4; - char *end; + const char *end; int ip4elm[4]; - char *s = str; + const char *s = str; if (!memchr(str, ':', str_len)) { return 0; diff --git a/ext/filter/tests/ghsa-w8qr-v226-r27w.phpt b/ext/filter/tests/ghsa-w8qr-v226-r27w.phpt new file mode 100644 index 0000000000000..0092408ee5ad6 --- /dev/null +++ b/ext/filter/tests/ghsa-w8qr-v226-r27w.phpt @@ -0,0 +1,41 @@ +--TEST-- +GHSA-w8qr-v226-r27w +--EXTENSIONS-- +filter +--FILE-- + +--EXPECT-- +--- These ones should fail --- +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +--- These ones should work --- +string(21) "http://test@127.0.0.1/" +string(50) "http://test@[2001:db8:3333:4444:5555:6666:102:304]/" +string(17) "http://test@[::1]/" From 0cf08b95ae963a2fffa51d698df0a830fd2c00d3 Mon Sep 17 00:00:00 2001 From: Pierrick Charron Date: Tue, 4 Jun 2024 09:21:22 -0400 Subject: [PATCH 44/45] Update NEWS --- NEWS | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/NEWS b/NEWS index 235353a39a103..9b4bcf66784e8 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ PHP NEWS - CGI: . Fixed buffer limit on Windows, replacing read call usage by _read. (David Carlier) + . Fixed bug GHSA-3qgc-jrrr-25jv (Bypass of CVE-2012-1823, Argument Injection + in PHP-CGI). (CVE-2024-4577) (nielsdos) - CLI: . Fixed bug GH-14189 (PHP Interactive shell input state incorrectly handles @@ -29,6 +31,10 @@ PHP NEWS . Fixed bug GH-14215 (Cannot use FFI::load on CRLF header file with apache2handler). (nielsdos) +- Filter: + . Fixed bug GHSA-w8qr-v226-r27w (Filter bypass in filter_var FILTER_VALIDATE_URL). + (CVE-2024-5458) (nielsdos) + - FPM: . Fix bug GH-14175 (Show decimal number instead of scientific notation in systemd status). (Benjamin Cremer) @@ -53,6 +59,20 @@ PHP NEWS . Fixed bug GH-14109 (Fix accidental persisting of internal class constant in shm). (ilutov) +- OpenSSL: + . The openssl_private_decrypt function in PHP, when using PKCS1 padding + (OPENSSL_PKCS1_PADDING, which is the default), is vulnerable to the Marvin Attack + unless it is used with an OpenSSL version that includes the changes from this pull + request: https://github.com/openssl/openssl/pull/13817 (rsa_pkcs1_implicit_rejection). + These changes are part of OpenSSL 3.2 and have also been backported to stable + versions of various Linux distributions, as well as to the PHP builds provided for + Windows since the previous release. All distributors and builders should ensure that + this version is used to prevent PHP from being vulnerable. (CVE-2024-2408) + +- Standard: + . Fixed bug GHSA-9fcc-425m-g385 (Bypass of CVE-2024-1874). + (CVE-2024-5585) (nielsdos) + - XML: . Fixed bug GH-14124 (Segmentation fault with XML extension under certain memory limit). (nielsdos) From 40298a988fca728ddc47316938da61e0f768c872 Mon Sep 17 00:00:00 2001 From: Pierrick Charron Date: Tue, 4 Jun 2024 09:22:51 -0400 Subject: [PATCH 45/45] Update versions for PHP 8.2.20 --- NEWS | 2 +- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 9b4bcf66784e8..ba10381234f9a 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.2.20 +06 Jun 2024, PHP 8.2.20 - CGI: . Fixed buffer limit on Windows, replacing read call usage by _read. diff --git a/Zend/zend.h b/Zend/zend.h index 5b8accedcc63d..67b8a5291e375 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.2.20-dev" +#define ZEND_VERSION "4.2.20" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index ba4a2654a5f25..effbdb6ec2c9a 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.2.20-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.2.20],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index 6bdfdc571b872..eadeebaab93af 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -3,6 +3,6 @@ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 2 #define PHP_RELEASE_VERSION 20 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.2.20-dev" +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "8.2.20" #define PHP_VERSION_ID 80220