From 3e0a4259a8eabe0c00b12178d0b15c3c00fa59f4 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Tue, 20 May 2025 13:13:34 -0300 Subject: [PATCH 01/54] PHP 8.4 is now for PHP-8.4.9-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 c54c42628e232..8ce2b87f1c8a2 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.4.8 +?? ??? ????, PHP 8.4.9 + + +06 Jun 2025, PHP 8.4.8 - Core: . Fixed GH-18480 (array_splice with large values for offset/length arguments). diff --git a/Zend/zend.h b/Zend/zend.h index 0ce9956c99c99..34a6a0258a261 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.4.8-dev" +#define ZEND_VERSION "4.4.9-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 6dc1e45b34fd6..3662d3e985b02 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.4.8-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.4.9-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 9ac406dfc3208..7bd5e8c37f895 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 4 -#define PHP_RELEASE_VERSION 8 +#define PHP_RELEASE_VERSION 9 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.4.8-dev" -#define PHP_VERSION_ID 80408 +#define PHP_VERSION "8.4.9-dev" +#define PHP_VERSION_ID 80409 From 40e667280bcd5d6618e30ed405000a42f4d4e601 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 20 May 2025 20:36:37 +0200 Subject: [PATCH 02/54] Fix GH-18597: Heap-buffer-overflow in zend_alloc.c when assigning string with UTF-8 bytes xmlSave() also can flush in some cases. When the encoding is not available this can fail for short inputs, resulting in an empty string which is interned but then wrongly tagged by RETURN_NEW_STR. Fix this by checking the error condition and switching to RETURN_STR for defense-in-depth. This issue also exists on 8.3, but does not crash; however, due to the different API usage internally I cannot easily fix it on 8.3. There it gives a partial output. Closes GH-18606. --- NEWS | 3 +++ ext/dom/inner_html_mixin.c | 2 +- ext/dom/xml_document.c | 4 ++-- ext/libxml/libxml.c | 2 +- ext/simplexml/simplexml.c | 3 ++- ext/simplexml/tests/gh18597.phpt | 17 +++++++++++++++++ 6 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 ext/simplexml/tests/gh18597.phpt diff --git a/NEWS b/NEWS index 8ce2b87f1c8a2..2829db92cfc02 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.9 +- SimpleXML: + . Fixed bug GH-18597 (Heap-buffer-overflow in zend_alloc.c when assigning + string with UTF-8 bytes). (nielsdos) 06 Jun 2025, PHP 8.4.8 diff --git a/ext/dom/inner_html_mixin.c b/ext/dom/inner_html_mixin.c index e72b205bf4628..0af47e2cf019f 100644 --- a/ext/dom/inner_html_mixin.c +++ b/ext/dom/inner_html_mixin.c @@ -98,7 +98,7 @@ zend_result dom_element_inner_html_read(dom_object *obj, zval *retval) status |= xmlOutputBufferFlush(out); status |= xmlOutputBufferClose(out); } - (void) xmlSaveClose(ctxt); + status |= xmlSaveClose(ctxt); xmlCharEncCloseFunc(handler); } if (UNEXPECTED(status < 0)) { diff --git a/ext/dom/xml_document.c b/ext/dom/xml_document.c index 2bd3d908d7093..4d941de0f0686 100644 --- a/ext/dom/xml_document.c +++ b/ext/dom/xml_document.c @@ -282,7 +282,7 @@ static zend_string *php_new_dom_dump_node_to_str_ex(xmlNodePtr node, int options } else { xmlCharEncCloseFunc(handler); } - (void) xmlSaveClose(ctxt); + status |= xmlSaveClose(ctxt); } if (UNEXPECTED(status < 0)) { @@ -319,7 +319,7 @@ zend_long php_new_dom_dump_node_to_file(const char *filename, xmlDocPtr doc, xml if (EXPECTED(ctxt != NULL)) { status = dom_xml_serialize(ctxt, out, node, format, false, get_private_data_from_node(node)); status |= xmlOutputBufferFlush(out); - (void) xmlSaveClose(ctxt); + status |= xmlSaveClose(ctxt); } size_t offset = php_stream_tell(stream); diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 5ad67d1244987..c637d2cebf6a4 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -1519,7 +1519,7 @@ static zend_string *php_libxml_default_dump_doc_to_str(xmlDocPtr doc, int option } long status = xmlSaveDoc(ctxt, doc); - (void) xmlSaveClose(ctxt); + status |= xmlSaveClose(ctxt); if (status < 0) { smart_str_free_ex(&str, false); return NULL; diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 28923c4cb3925..619f627e8532a 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -1404,7 +1404,8 @@ PHP_METHOD(SimpleXMLElement, asXML) if (!result) { RETURN_FALSE; } else { - RETURN_NEW_STR(result); + /* Defense-in-depth: don't use the NEW variant in case somehow an empty string gets returned */ + RETURN_STR(result); } } /* }}} */ diff --git a/ext/simplexml/tests/gh18597.phpt b/ext/simplexml/tests/gh18597.phpt new file mode 100644 index 0000000000000..e9176bf7ae041 --- /dev/null +++ b/ext/simplexml/tests/gh18597.phpt @@ -0,0 +1,17 @@ +--TEST-- +GH-18597 (Heap-buffer-overflow in zend_alloc.c when assigning string with UTF-8 bytes) +--EXTENSIONS-- +simplexml +--FILE-- +"); +$sx1->node[0] = 'node1'; +$node = $sx1->node[0]; + +$node[0] = '��c'; + +$sx1->asXML(); // Depends on the available system encodings whether this fails or not, point is, it should not crash +echo "Done\n"; +?> +--EXPECT-- +Done From 8e2c2be7a5476aad61c449993fbca93a7d3de98e Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 21 May 2025 00:39:56 +0200 Subject: [PATCH 03/54] PHP-8.3 is now for PHP 8.3.23-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 aed9bd06181a0..b0188da1c3a61 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.22 +?? ??? ????, PHP 8.3.23 + + +05 Jun 2025, PHP 8.3.22 - Core: . Fixed GH-18480 (array_splice with large values for offset/length arguments). diff --git a/Zend/zend.h b/Zend/zend.h index 99b88992475bf..704df32e9c145 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.22-dev" +#define ZEND_VERSION "4.3.23-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 278dbd110d248..965c0bdd853ee 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.3.22-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.3.23-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 6cec820ba471b..28e4162421ded 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 3 -#define PHP_RELEASE_VERSION 22 +#define PHP_RELEASE_VERSION 23 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.3.22-dev" -#define PHP_VERSION_ID 80322 +#define PHP_VERSION "8.3.23-dev" +#define PHP_VERSION_ID 80323 From 910aeaafc1fbfe82cd51ea461695affdc3590fdc Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 20 May 2025 22:13:48 +0200 Subject: [PATCH 04/54] Add missing filter cleanups on phar failure Closes GH-18609. --- NEWS | 2 ++ ext/phar/phar.c | 5 +++++ ext/phar/zip.c | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/NEWS b/NEWS index b0188da1c3a61..6ae1dfce79730 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.23 +- Phar: + . Add missing filter cleanups on phar failure. (nielsdos) 05 Jun 2025, PHP 8.3.22 diff --git a/ext/phar/phar.c b/ext/phar/phar.c index dfdbbb8fb8390..125fc84703612 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1704,6 +1704,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char php_stream_filter_append(&temp->writefilters, filter); if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) { + php_stream_filter_remove(filter, 1); if (err) { php_stream_close(temp); MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6") @@ -1750,6 +1751,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char php_stream_filter_append(&temp->writefilters, filter); if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) { + php_stream_filter_remove(filter, 1); php_stream_close(temp); MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file") } @@ -2833,6 +2835,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int conv } entry->cfp = shared_cfp; if (!entry->cfp) { + php_stream_filter_free(filter); if (error) { spprintf(error, 0, "unable to create temporary file"); } @@ -2847,6 +2850,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int conv entry->header_offset = php_stream_tell(entry->cfp); php_stream_flush(file); if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) { + php_stream_filter_free(filter); if (closeoldfile) { php_stream_close(oldfile); } @@ -2858,6 +2862,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int conv } php_stream_filter_append((&entry->cfp->writefilters), filter); if (SUCCESS != php_stream_copy_to_stream_ex(file, entry->cfp, entry->uncompressed_filesize, NULL)) { + php_stream_filter_remove(filter, 1); if (closeoldfile) { php_stream_close(oldfile); } diff --git a/ext/phar/zip.c b/ext/phar/zip.c index c1f7d47c761cf..87681c69959a7 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -642,6 +642,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, size_t fname_len, char *alia } if (!entry.uncompressed_filesize || !actual_alias) { + php_stream_filter_remove(filter, 1); pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in alias, truncated"); } @@ -674,6 +675,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, size_t fname_len, char *alia } if (!entry.uncompressed_filesize || !actual_alias) { + php_stream_filter_remove(filter, 1); pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in alias, truncated"); } @@ -968,6 +970,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ entry->cfp = php_stream_fopen_tmpfile(); if (!entry->cfp) { + php_stream_filter_free(filter); spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } @@ -975,6 +978,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ php_stream_flush(efp); if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) { + php_stream_filter_free(filter); spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } @@ -982,6 +986,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ php_stream_filter_append((&entry->cfp->writefilters), filter); if (SUCCESS != php_stream_copy_to_stream_ex(efp, entry->cfp, entry->uncompressed_filesize, NULL)) { + php_stream_filter_remove(filter, 1); spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } From c30bddc18fe91d80947091bf088871760dcb91cb Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 21 May 2025 19:26:15 +0100 Subject: [PATCH 05/54] Fix GH-18617: socket_import_file_descriptor return check. to_zval_read_fd_array() helper when retrieving the socket protocol did not check it. close GH-18617 --- NEWS | 4 ++++ ext/sockets/conversions.c | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 6ae1dfce79730..d712f80818a79 100644 --- a/NEWS +++ b/NEWS @@ -47,6 +47,10 @@ PHP NEWS - PDO_OCI: . Fixed bug GH-18494 (PDO OCI segfault in statement GC). (nielsdos) +- Sockets: + - Fixed bug GH-18617 (socket_import_file_descriptor return value + unchecked). (David Carlier) + - SPL: . Fixed bug GH-18421 (Integer overflow with large numbers in LimitIterator). (nielsdos) diff --git a/ext/sockets/conversions.c b/ext/sockets/conversions.c index 4059758f44719..d03ef8ef68d59 100644 --- a/ext/sockets/conversions.c +++ b/ext/sockets/conversions.c @@ -1457,7 +1457,11 @@ void to_zval_read_fd_array(const char *data, zval *zv, res_context *ctx) object_init_ex(&elem, socket_ce); php_socket *sock = Z_SOCKET_P(&elem); - socket_import_file_descriptor(fd, sock); + if (!socket_import_file_descriptor(fd, sock)) { + do_to_zval_err(ctx, "error getting protocol descriptor %d: getsockopt() call failed with errno %d", fd, errno); + zval_ptr_dtor(&elem); + return; + } } else { php_stream *stream = php_stream_fopen_from_fd(fd, "rw", NULL); php_stream_to_zval(stream, &elem); From d6836fb345977c5d03b47e761220a4c5cbb96fb0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 24 May 2025 13:53:15 +0200 Subject: [PATCH 06/54] Fix memory leak in intl_datetime_decompose() Closes GH-18635. --- NEWS | 3 +++ ext/intl/common/common_date.cpp | 1 + 2 files changed, 4 insertions(+) diff --git a/NEWS b/NEWS index d712f80818a79..a8d6d6ee6fe92 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.23 +- Intl: + . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) + - Phar: . Add missing filter cleanups on phar failure. (nielsdos) diff --git a/ext/intl/common/common_date.cpp b/ext/intl/common/common_date.cpp index 2a52b7e63be5c..a412ddcdffcde 100644 --- a/ext/intl/common/common_date.cpp +++ b/ext/intl/common/common_date.cpp @@ -118,6 +118,7 @@ U_CFUNC int intl_datetime_decompose(zval *z, double *millis, TimeZone **tz, ZVAL_STRING(&zfuncname, "getTimestamp"); if (call_user_function(NULL, z, &zfuncname, &retval, 0, NULL) != SUCCESS || Z_TYPE(retval) != IS_LONG) { + zval_ptr_dtor(&retval); spprintf(&message, 0, "%s: error calling ::getTimeStamp() on the " "object", func); intl_errors_set(err, U_INTERNAL_PROGRAM_ERROR, From 64d511ef29e43539ccd8b3688c7976f52dfea3d8 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 24 May 2025 17:02:49 +0200 Subject: [PATCH 07/54] Fix GH-18641: Accessing a BcMath\Number property by ref crashes The properties are virtual so we need a custom get_property_ptr_ptr handler. Closes GH-18637. --- NEWS | 4 ++++ ext/bcmath/bcmath.c | 7 +++++++ ext/bcmath/tests/number/gh18641.phpt | 13 +++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 ext/bcmath/tests/number/gh18641.phpt diff --git a/NEWS b/NEWS index 624f51a6fbaa0..ca7c8bdb40a4f 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.9 +- BcMath: + . Fixed bug GH-18641 (Accessing a BcMath\Number property by ref crashes). + (nielsdos) + - Intl: . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) diff --git a/ext/bcmath/bcmath.c b/ext/bcmath/bcmath.c index 233045bd7cd7e..3628b95a78e81 100644 --- a/ext/bcmath/bcmath.c +++ b/ext/bcmath/bcmath.c @@ -971,6 +971,12 @@ static zval *bcmath_number_read_property(zend_object *obj, zend_string *name, in return zend_std_read_property(obj, name, type, cache_slot, rv); } +static zval *bcmath_number_get_property_ptr_ptr(zend_object *object, zend_string *member, int type, void **cache_slot) +{ + /* Must always go through read property because all properties are virtual, and no dynamic properties are allowed. */ + return NULL; +} + static int bcmath_number_has_property(zend_object *obj, zend_string *name, int check_empty, void **cache_slot) { if (check_empty == ZEND_PROPERTY_NOT_EMPTY) { @@ -1014,6 +1020,7 @@ static void bcmath_number_register_class(void) bcmath_number_obj_handlers.unset_property = bcmath_number_unset_property; bcmath_number_obj_handlers.has_property = bcmath_number_has_property; bcmath_number_obj_handlers.read_property = bcmath_number_read_property; + bcmath_number_obj_handlers.get_property_ptr_ptr = bcmath_number_get_property_ptr_ptr; bcmath_number_obj_handlers.get_properties_for = bcmath_number_get_properties_for; bcmath_number_obj_handlers.cast_object = bcmath_number_cast_object; } diff --git a/ext/bcmath/tests/number/gh18641.phpt b/ext/bcmath/tests/number/gh18641.phpt new file mode 100644 index 0000000000000..ed8f55e9145a7 --- /dev/null +++ b/ext/bcmath/tests/number/gh18641.phpt @@ -0,0 +1,13 @@ +--TEST-- +GH-18641 (Accessing a BcMath\Number property by ref crashes) +--EXTENSIONS-- +bcmath +--FILE-- +value; +var_dump($x); +?> +--EXPECT-- +string(1) "1" From c9781111e1d22b45ebdee294aa679240b6fd41de Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 24 May 2025 16:17:43 +0200 Subject: [PATCH 08/54] Fix memory leak when calloc() fails in php_readline_completion_cb() Closes GH-18637. --- NEWS | 4 ++++ ext/readline/readline.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index a8d6d6ee6fe92..fc7e534d60432 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,10 @@ PHP NEWS - Phar: . Add missing filter cleanups on phar failure. (nielsdos) +- Readline: + . Fix memory leak when calloc() fails in php_readline_completion_cb(). + (nielsdos) + 05 Jun 2025, PHP 8.3.22 - Core: diff --git a/ext/readline/readline.c b/ext/readline/readline.c index 1bd5e2fd6059a..4da9f35951527 100644 --- a/ext/readline/readline.c +++ b/ext/readline/readline.c @@ -458,13 +458,14 @@ char **php_readline_completion_cb(const char *text, int start, int end) /* libedit will read matches[2] */ matches = calloc(3, sizeof(char *)); if (!matches) { - return NULL; + goto out; } matches[0] = strdup(""); } } } +out: zval_ptr_dtor(¶ms[0]); zval_ptr_dtor(&_readline_array); From a44e3f442f3e52d35e4e8606785b52433fc78f37 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 24 May 2025 16:19:53 +0200 Subject: [PATCH 09/54] Fix memory leaks in php_http.c when call_user_function() fails retval can be refcounted but is not destroyed. Closes GH-18638. --- NEWS | 3 +++ ext/soap/php_http.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/NEWS b/NEWS index fc7e534d60432..c533ad33e490c 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,9 @@ PHP NEWS . Fix memory leak when calloc() fails in php_readline_completion_cb(). (nielsdos) +- Soap: + . Fix memory leaks in php_http.c when call_user_function() fails. (nielsdos) + 05 Jun 2025, PHP 8.3.22 - Core: diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index 00aa54c83efdb..c908bb4d8ff16 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -416,6 +416,7 @@ int make_http_soap_request(zval *this_ptr, } else { zval_ptr_dtor(¶ms[0]); zval_ptr_dtor(&func); + zval_ptr_dtor(&retval); if (request != buf) { zend_string_release_ex(request, 0); } @@ -1314,6 +1315,7 @@ int make_http_soap_request(zval *this_ptr, } else { zval_ptr_dtor(¶ms[0]); zval_ptr_dtor(&func); + zval_ptr_dtor(&retval); efree(content_encoding); zend_string_release_ex(http_headers, 0); zend_string_release_ex(http_body, 0); From 772479ea2ff92115d67b39572e265dee4e91b444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sat, 24 May 2025 22:26:06 +0200 Subject: [PATCH 10/54] Fix build on Apple Clang 17+ (#18629) Fixing "invalid cpu feature string for builtin" errors that started to appear on Apple Clang 17.0.0 --- Zend/zend_cpuinfo.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Zend/zend_cpuinfo.h b/Zend/zend_cpuinfo.h index 9d221c59e541a..5cc161eab9949 100644 --- a/Zend/zend_cpuinfo.h +++ b/Zend/zend_cpuinfo.h @@ -126,58 +126,86 @@ ZEND_API int zend_cpu_supports(zend_cpu_feature feature); * functions */ ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_sse2(void) { +#ifdef __aarch64__ + return 0; +#else #if PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("sse2"); +#endif } ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_sse3(void) { +#ifdef __aarch64__ + return 0; +#else #if PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("sse3"); +#endif } ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_ssse3(void) { +#ifdef __aarch64__ + return 0; +#else #if PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("ssse3"); +#endif } ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_sse41(void) { +#ifdef __aarch64__ + return 0; +#else #if PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("sse4.1"); +#endif } ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_sse42(void) { +#ifdef __aarch64__ + return 0; +#else #if PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("sse4.2"); +#endif } ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_avx(void) { +#ifdef __aarch64__ + return 0; +#else #if PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("avx"); +#endif } ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_avx2(void) { +#ifdef __aarch64__ + return 0; +#else #if PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("avx2"); +#endif } #if PHP_HAVE_AVX512_SUPPORTS From 5d4846b24181881f33ea299697b5392075e5f6a4 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 24 May 2025 21:09:53 +0100 Subject: [PATCH 11/54] ext/ldap: simplify ldap_connect() workflow, fix url leak. delaying the object creation only before ldap initialisation. fix forgotten url freeing on TLS error code path. close GH-18645 --- ext/ldap/ldap.c | 11 +++++++--- ext/ldap/tests/ldap_connect_port_error.phpt | 24 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 ext/ldap/tests/ldap_connect_port_error.phpt diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index a1b7e7322a5de..fecb8846400a6 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -984,8 +984,6 @@ PHP_FUNCTION(ldap_connect) RETURN_FALSE; } - object_init_ex(return_value, ldap_link_ce); - ld = Z_LDAP_LINK_P(return_value); { int rc = LDAP_SUCCESS; @@ -1008,13 +1006,17 @@ PHP_FUNCTION(ldap_connect) /* ensure all pending TLS options are applied in a new context */ if (ldap_set_option(NULL, LDAP_OPT_X_TLS_NEWCTX, &val) != LDAP_OPT_SUCCESS) { - zval_ptr_dtor(return_value); + if (url != host) { + efree(url); + } php_error_docref(NULL, E_WARNING, "Could not create new security context"); RETURN_FALSE; } LDAPG(tls_newctx) = false; } #endif + object_init_ex(return_value, ldap_link_ce); + ld = Z_LDAP_LINK_P(return_value); #ifdef LDAP_API_FEATURE_X_OPENLDAP /* ldap_init() is deprecated, use ldap_initialize() instead. @@ -1027,6 +1029,9 @@ PHP_FUNCTION(ldap_connect) ldap = ldap_init(host, port); if (ldap == NULL) { zval_ptr_dtor(return_value); + if (url != host) { + efree(url); + } php_error_docref(NULL, E_WARNING, "Could not create session handle"); RETURN_FALSE; } diff --git a/ext/ldap/tests/ldap_connect_port_error.phpt b/ext/ldap/tests/ldap_connect_port_error.phpt new file mode 100644 index 0000000000000..55d41d395b13c --- /dev/null +++ b/ext/ldap/tests/ldap_connect_port_error.phpt @@ -0,0 +1,24 @@ +--TEST-- +ldap_connect() - Connection errors +--EXTENSIONS-- +ldap +--INI-- +error_reporting=E_ALL & ~E_DEPRECATED +--FILE-- +getMessage(), PHP_EOL; +} + +try { + ldap_connect("nope://$host", 0); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +ldap_connect(): Argument #2 ($port) must be between 1 and 65535 +ldap_connect(): Argument #2 ($port) must be between 1 and 65535 From cd751f98cb991bae0bdb8de7e9b6970a14e0664a Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Sun, 25 May 2025 16:43:36 -0700 Subject: [PATCH 12/54] Reapply GH-17712 with a fix for internal class constants (#18464) Add recursion protection when emitting deprecation warnings for class constants, since the deprecation message can come from an attribute that is using the same constant for the message, or otherwise result in recursion. But, internal constants are persisted, and thus cannot have recursion protection. Otherwise, if a user error handler triggers bailout before the recursion flag is removed then a subsequent request (e.g. with `--repeat 2`) would start with that flag already applied. Internal constants can presumably be trusted not to use deprecation messages that come from recursive attributes. Fixes GH-18463 Fixes GH-17711 --- NEWS | 5 ++ .../deprecated/class_constants/gh17711.phpt | 28 ++++++++++ .../constants/gh18463-class-constant.phpt | 20 +++++++ Zend/zend_API.c | 2 +- Zend/zend_compile.c | 4 ++ Zend/zend_constants.c | 9 +++- Zend/zend_constants.h | 11 ++++ Zend/zend_vm_def.h | 9 +++- Zend/zend_vm_execute.h | 54 ++++++++++++++++--- ext/opcache/ZendAccelerator.c | 5 ++ 10 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 Zend/tests/attributes/deprecated/class_constants/gh17711.phpt create mode 100644 Zend/tests/constants/gh18463-class-constant.phpt diff --git a/NEWS b/NEWS index e65b61fe6923d..45182d73c65e5 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,11 @@ PHP NEWS . Fixed bug GH-18641 (Accessing a BcMath\Number property by ref crashes). (nielsdos) +- Core: + . Fixed bugs GH-17711 and GH-18022 (Infinite recursion on deprecated attribute + evaluation) and GH-18464 (Recursion protection for deprecation constants not + released on bailout). (DanielEScherzer and ilutov) + - Intl: . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) diff --git a/Zend/tests/attributes/deprecated/class_constants/gh17711.phpt b/Zend/tests/attributes/deprecated/class_constants/gh17711.phpt new file mode 100644 index 0000000000000..abec209343ab3 --- /dev/null +++ b/Zend/tests/attributes/deprecated/class_constants/gh17711.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-17711: Infinite recursion through deprecated class constants self-referencing through deprecation message +--FILE-- + +--EXPECTF-- +Deprecated: Constant C::C is deprecated, Message in %s on line %d +string(7) "Message" + +Deprecated: Constant D::C is deprecated, test in %s on line %d +string(4) "test" diff --git a/Zend/tests/constants/gh18463-class-constant.phpt b/Zend/tests/constants/gh18463-class-constant.phpt new file mode 100644 index 0000000000000..2af977205dc8d --- /dev/null +++ b/Zend/tests/constants/gh18463-class-constant.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-18463: Recursion protection should not be applied to internal class constants +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTF-- +Constant _ZendTestClass::ZEND_TEST_DEPRECATED is deprecated in %s on line %d + +Fatal error: Cannot use "string" as a class name as it is reserved in %s(%d) : eval()'d code on line %d diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5aac3c1f7d77c..6e254561d3745 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1439,7 +1439,7 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_ ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) { if (c->ce == class_type) { - if (Z_TYPE(c->value) == IS_CONSTANT_AST) { + if (Z_TYPE(c->value) == IS_CONSTANT_AST || (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED)) { new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); memcpy(new_c, c, sizeof(zend_class_constant)); c = new_c; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 832dedc421042..195c65d504374 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8822,6 +8822,10 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as if (deprecated) { ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED; + /* For deprecated constants, we need to flag the zval for recursion + * detection. Make sure the zval is separated out of shm. */ + ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; + ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; } } } diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index d453b8bb73717..50bdb3ddad290 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -353,8 +353,15 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * } if (UNEXPECTED(ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED)) { - if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) { + if ((flags & ZEND_FETCH_CLASS_SILENT) == 0 && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { goto failure; } diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 6f0710c0ce63e..bd759c2891500 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -27,6 +27,17 @@ #define CONST_NO_FILE_CACHE (1<<1) /* Can't be saved in file cache */ #define CONST_DEPRECATED (1<<2) /* Deprecated */ #define CONST_OWNED (1<<3) /* constant should be destroyed together with class */ +#define CONST_RECURSIVE (1<<4) /* Recursion protection for constant evaluation */ + +#define CONST_IS_RECURSIVE(c) (Z_CONSTANT_FLAGS((c)->value) & CONST_RECURSIVE) +#define CONST_PROTECT_RECURSION(c) \ + do { \ + Z_CONSTANT_FLAGS((c)->value) |= CONST_RECURSIVE; \ + } while (0) +#define CONST_UNPROTECT_RECURSION(c) \ + do { \ + Z_CONSTANT_FLAGS((c)->value) &= ~CONST_RECURSIVE; \ + } while (0) #define PHP_USER_CONSTANT 0x7fffff /* a constant defined in user space */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 2eecfe035ea2f..01bdcf0ad6a00 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6094,8 +6094,15 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 40cbb4958708f..25e537dc1c07a 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7615,8 +7615,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -8776,8 +8783,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -25882,8 +25896,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -26452,8 +26473,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -35294,8 +35322,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -35654,8 +35689,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 82ae1adce44c5..08abd7048d6dd 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3806,6 +3806,11 @@ static bool preload_try_resolve_constants(zend_class_entry *ce) ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { val = &c->value; if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + /* For deprecated constants, we need to flag the zval for recursion + * detection. Make sure the zval is separated out of shm. */ + if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED) { + ok = false; + } if (EXPECTED(zend_update_class_constant(c, key, c->ce) == SUCCESS)) { was_changed = changed = true; } else { From ff2c7dc0f8ebf3ae811b4b13eb7bc7662051a988 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 22 May 2025 23:06:00 +0200 Subject: [PATCH 13/54] Fix leaks with multiple calls to DatePeriod iterator current() Destroy the old value first. We can't skip recreating the value because the object may have been changed in between calls. Closes GH-18624. --- NEWS | 3 ++ ext/date/php_date.c | 1 + ...le_calls_date_period_iterator_current.phpt | 42 +++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 ext/date/tests/multiple_calls_date_period_iterator_current.phpt diff --git a/NEWS b/NEWS index c533ad33e490c..4dce12d98f00d 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.23 +- Date: + . Fix leaks with multiple calls to DatePeriod iterator current(). (nielsdos) + - Intl: . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 2347fd55706fa..910149efae658 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -1607,6 +1607,7 @@ static zval *date_period_it_current_data(zend_object_iterator *iter) php_date_obj *newdateobj; /* Create new object */ + zval_ptr_dtor(&iterator->current); php_date_instantiate(get_base_date_class(object->start_ce), &iterator->current); newdateobj = Z_PHPDATE_P(&iterator->current); newdateobj->time = timelib_time_ctor(); diff --git a/ext/date/tests/multiple_calls_date_period_iterator_current.phpt b/ext/date/tests/multiple_calls_date_period_iterator_current.phpt new file mode 100644 index 0000000000000..b0e90873e6126 --- /dev/null +++ b/ext/date/tests/multiple_calls_date_period_iterator_current.phpt @@ -0,0 +1,42 @@ +--TEST-- +Multiple calls to DatePeriod iterator current() leak objects +--FILE-- +getIterator(); +var_dump($iter->current()); +var_dump($iter->current()); +$iter->current()->setTimestamp(0); +var_dump($iter->current()); + +?> +--EXPECT-- +object(DateTime)#9 (3) { + ["date"]=> + string(26) "2018-12-31 00:00:00.000000" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +} +object(DateTime)#9 (3) { + ["date"]=> + string(26) "2018-12-31 00:00:00.000000" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +} +object(DateTime)#9 (3) { + ["date"]=> + string(26) "2018-12-31 00:00:00.000000" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +} From b39e17b06c4c33ee95dc5c05456e9f794d5a8629 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 25 May 2025 10:17:19 +0200 Subject: [PATCH 14/54] Fix memory leak in tidy output handler on error Closes GH-18649. --- NEWS | 3 +++ ext/tidy/tidy.c | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 4dce12d98f00d..f790b5164cb36 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,9 @@ PHP NEWS - Soap: . Fix memory leaks in php_http.c when call_user_function() fails. (nielsdos) +- Tidy: + . Fix memory leak in tidy output handler on error. (nielsdos) + 05 Jun 2025, PHP 8.3.22 - Core: diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index 831fcb3815399..46dd637f40e54 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -965,6 +965,11 @@ static int php_tidy_output_handler(void **nothing, php_output_context *output_co TidyBuffer inbuf, outbuf, errbuf; if (TG(clean_output) && (output_context->op & PHP_OUTPUT_HANDLER_START) && (output_context->op & PHP_OUTPUT_HANDLER_FINAL)) { + if (ZEND_SIZE_T_UINT_OVFL(output_context->in.used)) { + php_error_docref(NULL, E_WARNING, "Input string is too long"); + return status; + } + doc = tidyCreate(); tidyBufInit(&errbuf); @@ -972,11 +977,6 @@ static int php_tidy_output_handler(void **nothing, php_output_context *output_co tidyOptSetBool(doc, TidyForceOutput, yes); tidyOptSetBool(doc, TidyMark, no); - if (ZEND_SIZE_T_UINT_OVFL(output_context->in.used)) { - php_error_docref(NULL, E_WARNING, "Input string is too long"); - return status; - } - TIDY_SET_DEFAULT_CONFIG(doc); tidyBufInit(&inbuf); From fab0a6d75cbc5730a4d57cf4ebb8fa45363d0811 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 27 May 2025 20:40:35 +0200 Subject: [PATCH 15/54] Backport fix for GH-17687 Introducing a new attribute macro in lower branches is an ABI break and not allowed. However, we still need to fix the warnings such that -Werror builds don't break. So we copy the macro from the master branch to the C files in the appropriate places. --- ext/fileinfo/data_file.c | 2 +- ext/fileinfo/libmagic/apprentice.c | 6 ++++++ ext/pdo/pdo_sqlstate.c | 8 +++++++- ext/standard/crypt_sha256.c | 8 +++++++- ext/standard/crypt_sha512.c | 8 +++++++- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/ext/fileinfo/data_file.c b/ext/fileinfo/data_file.c index 212addc4d0541..af4c139bed0a0 100644 --- a/ext/fileinfo/data_file.c +++ b/ext/fileinfo/data_file.c @@ -1,6 +1,6 @@ /* This is a generated file, do not modify */ /* Usage: php create_data_file.php /path/to/magic.mgc > data_file.c */ -const unsigned char php_magic_database[7955032] = { +const unsigned char php_magic_database[7955032] ZEND_NONSTRING = { 0x1C, 0x04, 0x1E, 0xF1, 0x12, 0x00, 0x00, 0x00, 0xD2, 0x3B, 0x00, 0x00, 0xD2, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/ext/fileinfo/libmagic/apprentice.c b/ext/fileinfo/libmagic/apprentice.c index 5a5ae197467bb..922adcf55f1cd 100644 --- a/ext/fileinfo/libmagic/apprentice.c +++ b/ext/fileinfo/libmagic/apprentice.c @@ -185,6 +185,12 @@ private struct { { NULL, 0, NULL } }; +#if __has_attribute(nonstring) +# define ZEND_NONSTRING __attribute__((nonstring)) +#else +# define ZEND_NONSTRING +#endif + #include "../data_file.c" #ifdef COMPILE_ONLY diff --git a/ext/pdo/pdo_sqlstate.c b/ext/pdo/pdo_sqlstate.c index 5858566a818eb..a5e23890cee7f 100644 --- a/ext/pdo/pdo_sqlstate.c +++ b/ext/pdo/pdo_sqlstate.c @@ -24,8 +24,14 @@ #include "php_pdo.h" #include "php_pdo_driver.h" +#if __has_attribute(nonstring) +# define ZEND_NONSTRING __attribute__((nonstring)) +#else +# define ZEND_NONSTRING +#endif + struct pdo_sqlstate_info { - const char state[5]; + const char state[5] ZEND_NONSTRING; const char *desc; }; diff --git a/ext/standard/crypt_sha256.c b/ext/standard/crypt_sha256.c index 9e86db6020cdf..3e99bedc54129 100644 --- a/ext/standard/crypt_sha256.c +++ b/ext/standard/crypt_sha256.c @@ -317,8 +317,14 @@ static const char sha256_rounds_prefix[] = "rounds="; /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 +#if __has_attribute(nonstring) +# define ZEND_NONSTRING __attribute__((nonstring)) +#else +# define ZEND_NONSTRING +#endif + /* Table with characters for base64 transformation. */ -static const char b64t[64] = +static const char b64t[64] ZEND_NONSTRING = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; char * php_sha256_crypt_r(const char *key, const char *salt, char *buffer, int buflen) diff --git a/ext/standard/crypt_sha512.c b/ext/standard/crypt_sha512.c index 6ead7f2964a48..ae0eaecdca9a1 100644 --- a/ext/standard/crypt_sha512.c +++ b/ext/standard/crypt_sha512.c @@ -350,8 +350,14 @@ static const char sha512_rounds_prefix[] = "rounds="; /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 +#if __has_attribute(nonstring) +# define ZEND_NONSTRING __attribute__((nonstring)) +#else +# define ZEND_NONSTRING +#endif + /* Table with characters for base64 transformation. */ -static const char b64t[64] = +static const char b64t[64] ZEND_NONSTRING = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; From e4d2cd47c296848c2029d72d7f501317a2f2d746 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 27 May 2025 21:00:07 +0200 Subject: [PATCH 16/54] Fix compile error for attribute --- ext/fileinfo/create_data_file.php | 2 +- ext/fileinfo/data_file.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/fileinfo/create_data_file.php b/ext/fileinfo/create_data_file.php index dbc2abfac5709..dfac5bde134a5 100755 --- a/ext/fileinfo/create_data_file.php +++ b/ext/fileinfo/create_data_file.php @@ -39,7 +39,7 @@ $chunks = str_split($dta, CHUNK_SIZE); $chunks[count($chunks) - 1] = str_pad($chunks[count($chunks) - 1], CHUNK_SIZE, chr(0)); - echo 'const unsigned char php_magic_database[' . count($chunks) . '][' . CHUNK_SIZE . "] = {\n"; + echo 'const unsigned char ZEND_NONSTRING php_magic_database[' . count($chunks) . '][' . CHUNK_SIZE . "] = {\n"; foreach ($chunks as $chunk) { echo '"' . strtr($chunk, $map) . '",' . "\n"; } diff --git a/ext/fileinfo/data_file.c b/ext/fileinfo/data_file.c index db63c4cdc33e9..85a608c76aa4b 100644 --- a/ext/fileinfo/data_file.c +++ b/ext/fileinfo/data_file.c @@ -1,6 +1,6 @@ /* This is a generated file, do not modify */ /* Usage: php create_data_file.php /path/to/magic.mgc > data_file.c */ -const unsigned char php_magic_database[8313][1024] ZEND_NONSTRING = { +const unsigned char ZEND_NONSTRING php_magic_database[8313][1024] = { "\034\004\036\361\022\0\0\0m?\0\0\001\031\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=/\005\0\0\0\0\0\0\0\0\0\0\0\0\0/\0\0\0\0\0\0\0\0\0\0\0Brain Vision Data Exchange Marker File, Version\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/Brainvision Marker file\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0biosig/brainvision\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=.\005\0\0\0\0\0\0\0\0\0\0\0\0\0z\0\0\0\0\0\0\0\0\0\0\0FileId=TMSi PortiLab sample log file\n\015Version=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/TMSiLOG\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0biosig/tmsilog\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=+\005\0\0\0\0\0\004\0\0\0\0\0\0\0}\0\0\0\0\0\0\0\0\0\0\0Synergy\0\0048\0049(&\0048\0048)&\0048\0048\0048&\0048\0048\0048\0\0028\0\0\0\002\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\001\0\0\0=\017\005\0\0\0\0\0?\0\0\0\0\0\0\0~\0\0\0\0\0\0\0\0\0\0\0CRawDataElement\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\002\0\0\0=\016\005\0\0\0\0\0U\0\0\0\0\0\0\0\177\0\0\0\0\0\0\0\0\0\0\0CRawDataBuffer\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/S", "YNERGY\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0biosig/synergy\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=+\005\0\0\0\0\0\0\0\0\0\0\0\0\0.\0\0\0\0\0\0\0\0\0\0\0Brain Vision V-Amp Data Header File Version\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/Brainvision V-Amp file\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=&\005\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0\0\0\0\0\0\0\0\0\0Brain Vision Data Exchange Header File\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/Brainvision data file\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=&\005\0\0\0\0\0\0\0\0\0\0\0\0\0\026\007\0\0\0\0\0\0\0\0\0\0MetaView Service Assurance Expor", From 877663178cc3d0a2a6f127bd5c341c87ce8fc23c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 27 May 2025 21:07:15 +0200 Subject: [PATCH 17/54] Temporarily drop attribute for fileinfo The error in CI doesn't reproduce on my system with GCC 15.1.1... --- ext/fileinfo/create_data_file.php | 2 +- ext/fileinfo/data_file.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/fileinfo/create_data_file.php b/ext/fileinfo/create_data_file.php index dfac5bde134a5..dbc2abfac5709 100755 --- a/ext/fileinfo/create_data_file.php +++ b/ext/fileinfo/create_data_file.php @@ -39,7 +39,7 @@ $chunks = str_split($dta, CHUNK_SIZE); $chunks[count($chunks) - 1] = str_pad($chunks[count($chunks) - 1], CHUNK_SIZE, chr(0)); - echo 'const unsigned char ZEND_NONSTRING php_magic_database[' . count($chunks) . '][' . CHUNK_SIZE . "] = {\n"; + echo 'const unsigned char php_magic_database[' . count($chunks) . '][' . CHUNK_SIZE . "] = {\n"; foreach ($chunks as $chunk) { echo '"' . strtr($chunk, $map) . '",' . "\n"; } diff --git a/ext/fileinfo/data_file.c b/ext/fileinfo/data_file.c index 85a608c76aa4b..8d730ad90be63 100644 --- a/ext/fileinfo/data_file.c +++ b/ext/fileinfo/data_file.c @@ -1,6 +1,6 @@ /* This is a generated file, do not modify */ /* Usage: php create_data_file.php /path/to/magic.mgc > data_file.c */ -const unsigned char ZEND_NONSTRING php_magic_database[8313][1024] = { +const unsigned char php_magic_database[8313][1024] = { "\034\004\036\361\022\0\0\0m?\0\0\001\031\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=/\005\0\0\0\0\0\0\0\0\0\0\0\0\0/\0\0\0\0\0\0\0\0\0\0\0Brain Vision Data Exchange Marker File, Version\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/Brainvision Marker file\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0biosig/brainvision\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=.\005\0\0\0\0\0\0\0\0\0\0\0\0\0z\0\0\0\0\0\0\0\0\0\0\0FileId=TMSi PortiLab sample log file\n\015Version=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/TMSiLOG\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0biosig/tmsilog\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=+\005\0\0\0\0\0\004\0\0\0\0\0\0\0}\0\0\0\0\0\0\0\0\0\0\0Synergy\0\0048\0049(&\0048\0048)&\0048\0048\0048&\0048\0048\0048\0\0028\0\0\0\002\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\001\0\0\0=\017\005\0\0\0\0\0?\0\0\0\0\0\0\0~\0\0\0\0\0\0\0\0\0\0\0CRawDataElement\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\002\0\0\0=\016\005\0\0\0\0\0U\0\0\0\0\0\0\0\177\0\0\0\0\0\0\0\0\0\0\0CRawDataBuffer\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/S", "YNERGY\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0biosig/synergy\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=+\005\0\0\0\0\0\0\0\0\0\0\0\0\0.\0\0\0\0\0\0\0\0\0\0\0Brain Vision V-Amp Data Header File Version\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/Brainvision V-Amp file\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=&\005\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0\0\0\0\0\0\0\0\0\0Brain Vision Data Exchange Header File\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/Brainvision data file\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=&\005\0\0\0\0\0\0\0\0\0\0\0\0\0\026\007\0\0\0\0\0\0\0\0\0\0MetaView Service Assurance Expor", From 61884c3b52012249f571d3ce00827e7a1914b062 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 24 May 2025 17:37:22 +0200 Subject: [PATCH 18/54] Fix GH-18642: Signed integer overflow in ext/phar fseek The overflow checking code already existed, but didn't work because the math was done on signed numbers instead of unsigned numbers. In the process I also discovered a pre-existing issue that needs to be fixed (and seems that other stream wrappers can have this issue too). Closes GH-18644. --- NEWS | 1 + ext/phar/stream.c | 20 +++++++++++--------- ext/phar/tests/gh18642.phpt | 29 +++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 ext/phar/tests/gh18642.phpt diff --git a/NEWS b/NEWS index f790b5164cb36..87c038ebc7ea0 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ PHP NEWS - Phar: . Add missing filter cleanups on phar failure. (nielsdos) + . Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos) - Readline: . Fix memory leak when calloc() fails in php_readline_completion_cb(). diff --git a/ext/phar/stream.c b/ext/phar/stream.c index b53d4297c4227..fee100cc31a10 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -405,7 +405,7 @@ static int phar_stream_seek(php_stream *stream, zend_off_t offset, int whence, z phar_entry_data *data = (phar_entry_data *)stream->abstract; phar_entry_info *entry; int res; - zend_off_t temp; + zend_ulong temp; if (data->internal_file->link) { entry = phar_get_link_source(data->internal_file); @@ -415,26 +415,28 @@ static int phar_stream_seek(php_stream *stream, zend_off_t offset, int whence, z switch (whence) { case SEEK_END : - temp = data->zero + entry->uncompressed_filesize + offset; + temp = (zend_ulong) data->zero + (zend_ulong) entry->uncompressed_filesize + (zend_ulong) offset; break; case SEEK_CUR : - temp = data->zero + data->position + offset; + temp = (zend_ulong) data->zero + (zend_ulong) data->position + (zend_ulong) offset; break; case SEEK_SET : - temp = data->zero + offset; + temp = (zend_ulong) data->zero + (zend_ulong) offset; break; default: temp = 0; } - if (temp > data->zero + (zend_off_t) entry->uncompressed_filesize) { - *newoffset = -1; + + zend_off_t temp_signed = (zend_off_t) temp; + if (temp_signed > data->zero + (zend_off_t) entry->uncompressed_filesize) { + *newoffset = -1; /* FIXME: this will invalidate the ZEND_ASSERT(stream->position >= 0); assertion in streams.c */ return -1; } - if (temp < data->zero) { - *newoffset = -1; + if (temp_signed < data->zero) { + *newoffset = -1; /* FIXME: this will invalidate the ZEND_ASSERT(stream->position >= 0); assertion in streams.c */ return -1; } - res = php_stream_seek(data->fp, temp, SEEK_SET); + res = php_stream_seek(data->fp, temp_signed, SEEK_SET); *newoffset = php_stream_tell(data->fp) - data->zero; data->position = *newoffset; return res; diff --git a/ext/phar/tests/gh18642.phpt b/ext/phar/tests/gh18642.phpt new file mode 100644 index 0000000000000..a6872f7a62d86 --- /dev/null +++ b/ext/phar/tests/gh18642.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-18642 (Signed integer overflow in ext/phar fseek) +--EXTENSIONS-- +phar +--INI-- +phar.require_hash=0 +--FILE-- +setInfoClass('MyFile'); +$f = $phar['a.php']; +var_dump($f->fseek(PHP_INT_MAX)); +var_dump($f->fseek(0)); +var_dump($f->fseek(PHP_INT_MIN, SEEK_END)); +var_dump($f->fseek(0, SEEK_SET)); +var_dump($f->fseek(1, SEEK_CUR)); +var_dump($f->fseek(PHP_INT_MAX, SEEK_CUR)); +?> +--EXPECT-- +int(-1) +int(0) +int(-1) +int(0) +int(0) +int(-1) From b8e734a24ea82a17b07d00e8976bb8e4f25dea15 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 28 May 2025 18:50:43 +0200 Subject: [PATCH 19/54] Fix ZEND_NONSTRING attribute for data_file.c This broke in CI but not on my local machine because of the different compiler version. This is because there was an issue in GCC [1] that caused the attribute to not properly work on multidimensional arrays. This has since been fixed in GCC 15. Therefore, we guard the attribute with a version check. [1] https://gcc.gnu.org/cgit/gcc/commit/?id=afb46540d3921e96c4cd7ba8fa2c8b0901759455 --- ext/fileinfo/create_data_file.php | 2 +- ext/fileinfo/data_file.c | 2 +- ext/fileinfo/libmagic/apprentice.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/fileinfo/create_data_file.php b/ext/fileinfo/create_data_file.php index dbc2abfac5709..a8217d1d33cb0 100755 --- a/ext/fileinfo/create_data_file.php +++ b/ext/fileinfo/create_data_file.php @@ -39,7 +39,7 @@ $chunks = str_split($dta, CHUNK_SIZE); $chunks[count($chunks) - 1] = str_pad($chunks[count($chunks) - 1], CHUNK_SIZE, chr(0)); - echo 'const unsigned char php_magic_database[' . count($chunks) . '][' . CHUNK_SIZE . "] = {\n"; + echo 'const unsigned char php_magic_database[' . count($chunks) . '][' . CHUNK_SIZE . "] ZEND_NONSTRING = {\n"; foreach ($chunks as $chunk) { echo '"' . strtr($chunk, $map) . '",' . "\n"; } diff --git a/ext/fileinfo/data_file.c b/ext/fileinfo/data_file.c index 8d730ad90be63..db63c4cdc33e9 100644 --- a/ext/fileinfo/data_file.c +++ b/ext/fileinfo/data_file.c @@ -1,6 +1,6 @@ /* This is a generated file, do not modify */ /* Usage: php create_data_file.php /path/to/magic.mgc > data_file.c */ -const unsigned char php_magic_database[8313][1024] = { +const unsigned char php_magic_database[8313][1024] ZEND_NONSTRING = { "\034\004\036\361\022\0\0\0m?\0\0\001\031\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=/\005\0\0\0\0\0\0\0\0\0\0\0\0\0/\0\0\0\0\0\0\0\0\0\0\0Brain Vision Data Exchange Marker File, Version\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/Brainvision Marker file\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0biosig/brainvision\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=.\005\0\0\0\0\0\0\0\0\0\0\0\0\0z\0\0\0\0\0\0\0\0\0\0\0FileId=TMSi PortiLab sample log file\n\015Version=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/TMSiLOG\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0biosig/tmsilog\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=+\005\0\0\0\0\0\004\0\0\0\0\0\0\0}\0\0\0\0\0\0\0\0\0\0\0Synergy\0\0048\0049(&\0048\0048)&\0048\0048\0048&\0048\0048\0048\0\0028\0\0\0\002\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\001\0\0\0=\017\005\0\0\0\0\0?\0\0\0\0\0\0\0~\0\0\0\0\0\0\0\0\0\0\0CRawDataElement\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\002\0\0\0=\016\005\0\0\0\0\0U\0\0\0\0\0\0\0\177\0\0\0\0\0\0\0\0\0\0\0CRawDataBuffer\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/S", "YNERGY\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0biosig/synergy\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=+\005\0\0\0\0\0\0\0\0\0\0\0\0\0.\0\0\0\0\0\0\0\0\0\0\0Brain Vision V-Amp Data Header File Version\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/Brainvision V-Amp file\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=&\005\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0\0\0\0\0\0\0\0\0\0Brain Vision Data Exchange Header File\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/Brainvision data file\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=&\005\0\0\0\0\0\0\0\0\0\0\0\0\0\026\007\0\0\0\0\0\0\0\0\0\0MetaView Service Assurance Expor", diff --git a/ext/fileinfo/libmagic/apprentice.c b/ext/fileinfo/libmagic/apprentice.c index 693cf37871637..d7640e74a9dbe 100644 --- a/ext/fileinfo/libmagic/apprentice.c +++ b/ext/fileinfo/libmagic/apprentice.c @@ -184,7 +184,7 @@ file_private struct { { NULL, 0, NULL } }; -#if __has_attribute(nonstring) +#if __has_attribute(nonstring) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 15 # define ZEND_NONSTRING __attribute__((nonstring)) #else # define ZEND_NONSTRING From 9e9db0b22f10375ad0e17b07d4f439740b9d4bf7 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 28 May 2025 20:49:57 +0200 Subject: [PATCH 20/54] Update Lexbor patches for non-string attribute This should fix the final sub-issue of GH-17687. Closes GH-18691. --- ext/dom/lexbor/lexbor/core/sbst.h | 12 ++++++- ...nk-size-of-static-binary-search-tree.patch | 33 +++++++++++++------ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/ext/dom/lexbor/lexbor/core/sbst.h b/ext/dom/lexbor/lexbor/core/sbst.h index 40e0e9148a7ec..03444afdc747d 100644 --- a/ext/dom/lexbor/lexbor/core/sbst.h +++ b/ext/dom/lexbor/lexbor/core/sbst.h @@ -15,11 +15,21 @@ extern "C" { #include "lexbor/core/base.h" +#ifdef __has_attribute +# if __has_attribute(nonstring) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 15 +# define LXB_NONSTRING __attribute__((nonstring)) +# else +# define LXB_NONSTRING +# endif +#else +# define LXB_NONSTRING +#endif + typedef struct { lxb_char_t key; - lxb_char_t value[6]; + lxb_char_t value[6] LXB_NONSTRING; unsigned char value_len; unsigned short left; diff --git a/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch b/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch index dfb0c0a980ebb..24b5fda2bba9f 100644 --- a/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch +++ b/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch @@ -5,24 +5,37 @@ Subject: [PATCH 5/6] Shrink size of static binary search tree This also makes it more efficient on the data cache. --- - source/lexbor/core/sbst.h | 10 +++++----- + source/lexbor/core/sbst.h | 19 ++++++++++++++----- source/lexbor/html/tokenizer/state.c | 2 +- utils/lexbor/html/tmp/tokenizer_res.h | 2 +- utils/lexbor/html/tokenizer_entities_bst.py | 8 ++++---- utils/lexbor/lexbor/LXB.py | 2 +- - 5 files changed, 12 insertions(+), 12 deletions(-) + 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/source/lexbor/core/sbst.h b/source/lexbor/core/sbst.h -index b0fbc54..40e0e91 100644 +index b0fbc54..15a1d40 100644 --- a/source/lexbor/core/sbst.h +++ b/source/lexbor/core/sbst.h -@@ -19,12 +19,12 @@ extern "C" { +@@ -15,16 +15,25 @@ extern "C" { + + #include "lexbor/core/base.h" + ++#ifdef __has_attribute ++# if __has_attribute(nonstring) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 15 ++# define LXB_NONSTRING __attribute__((nonstring)) ++# else ++# define LXB_NONSTRING ++# endif ++#else ++# define LXB_NONSTRING ++#endif + typedef struct { lxb_char_t key; - void *value; - size_t value_len; -+ lxb_char_t value[6]; ++ lxb_char_t value[6] LXB_NONSTRING; + unsigned char value_len; - size_t left; @@ -35,10 +48,10 @@ index b0fbc54..40e0e91 100644 lexbor_sbst_entry_static_t; diff --git a/source/lexbor/html/tokenizer/state.c b/source/lexbor/html/tokenizer/state.c -index 70ca391..2f3414f 100644 +index 158aade..207b909 100644 --- a/source/lexbor/html/tokenizer/state.c +++ b/source/lexbor/html/tokenizer/state.c -@@ -1815,7 +1815,7 @@ lxb_html_tokenizer_state_char_ref_named(lxb_html_tokenizer_t *tkz, +@@ -1820,7 +1820,7 @@ lxb_html_tokenizer_state_char_ref_named(lxb_html_tokenizer_t *tkz, goto done; } @@ -61,7 +74,7 @@ index b3701d5..73ab66e 100644 * Do not change this file! */ diff --git a/utils/lexbor/html/tokenizer_entities_bst.py b/utils/lexbor/html/tokenizer_entities_bst.py -index ee7dcb4..7cd1335 100755 +index 4fa0999..8bd83b2 100755 --- a/utils/lexbor/html/tokenizer_entities_bst.py +++ b/utils/lexbor/html/tokenizer_entities_bst.py @@ -1,6 +1,6 @@ @@ -98,7 +111,7 @@ index ee7dcb4..7cd1335 100755 + "../../../source/lexbor/html/tokenizer/res.h", "data/entities.json"); diff --git a/utils/lexbor/lexbor/LXB.py b/utils/lexbor/lexbor/LXB.py -index 2370c66..c41e645 100755 +index 3e75812..b068ea3 100755 --- a/utils/lexbor/lexbor/LXB.py +++ b/utils/lexbor/lexbor/LXB.py @@ -27,7 +27,7 @@ class Temp: @@ -111,5 +124,5 @@ index 2370c66..c41e645 100755 fh.close() -- -2.44.0 +2.49.0 From 40422e9c55c83e5a453416fe1ae1a1e0c880a6dc Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 29 May 2025 15:41:22 +0100 Subject: [PATCH 21/54] ext/pgsql: Fix warning not being emittedd when failure to cancel a query --- NEWS | 4 ++++ ext/pgsql/pgsql.c | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 87c038ebc7ea0..466ba5d89653e 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,10 @@ PHP NEWS . Add missing filter cleanups on phar failure. (nielsdos) . Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos) +- PGSQL: + . Fix warning not being emitted when failure to cancel a query with + pg_cancel_query(). (Girgias) + - Readline: . Fix memory leak when calloc() fails in php_readline_completion_cb(). (nielsdos) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index e955eed0928c8..63acd26ea01f5 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3576,8 +3576,14 @@ static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type) int rc; c = PQgetCancel(pgsql); + /* PQcancel + * The return value of PQcancel is 1 if the cancel request was successfully dispatched and 0 if not. + * If not, errbuf is filled with an explanatory error message. + * errbuf must be a char array of size errbufsize (the recommended size is 256 bytes). + * https://www.postgresql.org/docs/current/libpq-cancel.html#LIBPQ-PQCANCEL + */ RETVAL_LONG((rc = PQcancel(c, err, sizeof(err)))); - if (rc < 0) { + if (rc == 0) { zend_error(E_WARNING, "cannot cancel the query: %s", err); } while ((pgsql_result = PQgetResult(pgsql))) { From dd856d5ad90c642d82eaf5fe4ab511c8a3971557 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 29 May 2025 16:57:31 +0200 Subject: [PATCH 22/54] Fix potential NULL deref Backported from GH-18697. --- ext/soap/php_schema.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/soap/php_schema.c b/ext/soap/php_schema.c index 423714545ae30..2fc0eacc530f7 100644 --- a/ext/soap/php_schema.c +++ b/ext/soap/php_schema.c @@ -1872,7 +1872,7 @@ static int schema_attribute(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr attrType, sdl } else { xmlNsPtr nsPtr = attr_find_ns(attr); - if (strncmp((char*)nsPtr->href, SCHEMA_NAMESPACE, sizeof(SCHEMA_NAMESPACE))) { + if (nsPtr && strncmp((char*)nsPtr->href, SCHEMA_NAMESPACE, sizeof(SCHEMA_NAMESPACE))) { smart_str key2 = {0}; sdlExtraAttributePtr ext; xmlNsPtr nsptr; From 087f38f3476b2f6c73cf2567ccbe19636c2c975d Mon Sep 17 00:00:00 2001 From: Oleg Efimov Date: Thu, 29 May 2025 21:46:11 +0100 Subject: [PATCH 23/54] Fix GH-18695: float numbers zero fraction is now preserved in zend_ast_export() (#18699) --- NEWS | 4 ++++ Zend/tests/ast/ast_serialize_floats.phpt | 26 ++++++++++++++++++++++++ Zend/zend_ast.c | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/ast/ast_serialize_floats.phpt diff --git a/NEWS b/NEWS index 466ba5d89653e..379718ba2a9ba 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.23 +- Core: + . Fixed GH-18695 (zend_ast_export() - float number is not preserved). + (Oleg Efimov) + - Date: . Fix leaks with multiple calls to DatePeriod iterator current(). (nielsdos) diff --git a/Zend/tests/ast/ast_serialize_floats.phpt b/Zend/tests/ast/ast_serialize_floats.phpt new file mode 100644 index 0000000000000..164b8b03338cf --- /dev/null +++ b/Zend/tests/ast/ast_serialize_floats.phpt @@ -0,0 +1,26 @@ +--TEST-- +Serialization of floats are correct +--INI-- +zend.assertions=1 +--FILE-- +getMessage(), ' failed', PHP_EOL; +} +try { + assert(!is_float(1.1)); +} catch (AssertionError $e) { + echo 'assert(): ', $e->getMessage(), ' failed', PHP_EOL; +} +try { + assert(!is_float(1234.5678)); +} catch (AssertionError $e) { + echo 'assert(): ', $e->getMessage(), ' failed', PHP_EOL; +} +?> +--EXPECT-- +assert(): assert(!is_float(0.0)) failed +assert(): assert(!is_float(1.1)) failed +assert(): assert(!is_float(1234.5678)) failed diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 0fb50e2eae1f5..f8c4ca17a9b95 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1563,7 +1563,7 @@ static ZEND_COLD void zend_ast_export_zval(smart_str *str, zval *zv, int priorit break; case IS_DOUBLE: smart_str_append_double( - str, Z_DVAL_P(zv), (int) EG(precision), /* zero_fraction */ false); + str, Z_DVAL_P(zv), (int) EG(precision), /* zero_fraction */ true); break; case IS_STRING: smart_str_appendc(str, '\''); From 48b492269b9761d0cc606667cf20521c7c1b6449 Mon Sep 17 00:00:00 2001 From: txuna Date: Mon, 26 May 2025 13:25:21 +0000 Subject: [PATCH 24/54] Fix GH-18595: fpm_get_status segfault This fixes null dereference error when calling fpm_get_status() and one of the children is just being created. Closes GH-18662 Co-authored-by: Jakub Zelenka --- NEWS | 3 +++ sapi/fpm/fpm/fpm_request.c | 1 + sapi/fpm/fpm/fpm_request.h | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 379718ba2a9ba..3b3900afff9d8 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,9 @@ PHP NEWS - Date: . Fix leaks with multiple calls to DatePeriod iterator current(). (nielsdos) +- FPM: + . Fixed GH-18662 (fpm_get_status segfault). (txuna) + - Intl: . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) diff --git a/sapi/fpm/fpm/fpm_request.c b/sapi/fpm/fpm/fpm_request.c index 0eb75884d367a..9ea7d8aeaaa35 100644 --- a/sapi/fpm/fpm/fpm_request.c +++ b/sapi/fpm/fpm/fpm_request.c @@ -22,6 +22,7 @@ #include "zlog.h" static const char *requests_stages[] = { + [FPM_REQUEST_CREATING] = "Creating", [FPM_REQUEST_ACCEPTING] = "Idle", [FPM_REQUEST_READING_HEADERS] = "Reading headers", [FPM_REQUEST_INFO] = "Getting request information", diff --git a/sapi/fpm/fpm/fpm_request.h b/sapi/fpm/fpm/fpm_request.h index c1cde0111be47..1dcc7f78902fc 100644 --- a/sapi/fpm/fpm/fpm_request.h +++ b/sapi/fpm/fpm/fpm_request.h @@ -25,7 +25,8 @@ const char *fpm_request_get_stage_name(int stage); int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv); enum fpm_request_stage_e { - FPM_REQUEST_ACCEPTING = 1, + FPM_REQUEST_CREATING, + FPM_REQUEST_ACCEPTING, FPM_REQUEST_READING_HEADERS, FPM_REQUEST_INFO, FPM_REQUEST_EXECUTING, From d9d991928f03c493de524ae19608e7f10373ee6e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 30 May 2025 21:03:41 +0200 Subject: [PATCH 25/54] Fix memory leak when curl_slist_append() fails If curl_slist_append() returns NULL, then the original pointer is lost and not freed. Closes GH-18711. --- NEWS | 3 +++ ext/curl/interface.c | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 3b3900afff9d8..4088849616bc7 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ PHP NEWS . Fixed GH-18695 (zend_ast_export() - float number is not preserved). (Oleg Efimov) +- Curl: + . Fix memory leak when setting a list via curl_setopt fails. (nielsdos) + - Date: . Fix leaks with multiple calls to DatePeriod iterator current(). (nielsdos) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 1a270a1c32cea..61d830e8abfe1 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2220,12 +2220,14 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue ZEND_HASH_FOREACH_VAL(ph, current) { ZVAL_DEREF(current); val = zval_get_tmp_string(current, &tmp_val); - slist = curl_slist_append(slist, ZSTR_VAL(val)); + struct curl_slist *new_slist = curl_slist_append(slist, ZSTR_VAL(val)); zend_tmp_string_release(tmp_val); - if (!slist) { + if (!new_slist) { + curl_slist_free_all(slist); php_error_docref(NULL, E_WARNING, "Could not build curl_slist"); return FAILURE; } + slist = new_slist; } ZEND_HASH_FOREACH_END(); if (slist) { From 75cea65c997a7d580abc41e61db2b1de15ee66c3 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 31 May 2025 12:14:27 +0200 Subject: [PATCH 26/54] Fix reference type confusion and leak in user random engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes GH-18718. Co-authored-by: Tim Düsterhus --- NEWS | 4 +++ ext/random/engine_user.c | 12 ++++++--- .../02_engine/user_reference_return.phpt | 25 +++++++++++++++++++ .../engine_unsafe_empty_string.phpt | 3 ++- 4 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 ext/random/tests/02_engine/user_reference_return.phpt diff --git a/NEWS b/NEWS index 4088849616bc7..02b90ff3dc7f6 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,10 @@ PHP NEWS . Fix warning not being emitted when failure to cancel a query with pg_cancel_query(). (Girgias) +- Random: + . Fix reference type confusion and leak in user random engine. + (nielsdos, timwolla) + - Readline: . Fix memory leak when calloc() fails in php_readline_completion_cb(). (nielsdos) diff --git a/ext/random/engine_user.c b/ext/random/engine_user.c index b45924d3bb7da..ce68521c129a1 100644 --- a/ext/random/engine_user.c +++ b/ext/random/engine_user.c @@ -27,6 +27,7 @@ static uint64_t generate(php_random_status *status) uint64_t result = 0; size_t size; zval retval; + zend_string *zstr; zend_call_known_instance_method_with_0_params(s->generate_method, s->object, &retval); @@ -34,8 +35,14 @@ static uint64_t generate(php_random_status *status) return 0; } + if (UNEXPECTED(Z_ISREF(retval))) { + zstr = Z_STR_P(Z_REFVAL(retval)); + } else { + zstr = Z_STR(retval); + } + /* Store generated size in a state */ - size = Z_STRLEN(retval); + size = ZSTR_LEN(zstr); /* Guard for over 64-bit results */ if (size > sizeof(uint64_t)) { @@ -46,11 +53,10 @@ static uint64_t generate(php_random_status *status) if (size > 0) { /* Endianness safe copy */ for (size_t i = 0; i < size; i++) { - result += ((uint64_t) (unsigned char) Z_STRVAL(retval)[i]) << (8 * i); + result += ((uint64_t) (unsigned char) ZSTR_VAL(zstr)[i]) << (8 * i); } } else { zend_throw_error(random_ce_Random_BrokenRandomEngineError, "A random engine must return a non-empty string"); - return 0; } zval_ptr_dtor(&retval); diff --git a/ext/random/tests/02_engine/user_reference_return.phpt b/ext/random/tests/02_engine/user_reference_return.phpt new file mode 100644 index 0000000000000..f54e65bed7559 --- /dev/null +++ b/ext/random/tests/02_engine/user_reference_return.phpt @@ -0,0 +1,25 @@ +--TEST-- +Random: Engine: User: Returning by reference works +--FILE-- +field; + } +} + +$randomizer = new Randomizer(new ReferenceEngine()); + +var_dump($randomizer->getBytes(64)); + +?> +--EXPECT-- +string(64) "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd" diff --git a/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt index 6aec7180b1ee5..8333df88d6454 100644 --- a/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt +++ b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt @@ -10,7 +10,8 @@ final class EmptyStringEngine implements Engine { public function generate(): string { - return ''; + // Create a non-interned empty string. + return preg_replace('/./', '', random_bytes(4)); } } From 7f2299c8eb2304dbd599fcf98c06624dc8f31a63 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 31 May 2025 14:37:22 +0100 Subject: [PATCH 27/54] tests: Fix expectations Closes GH-18712 --- ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt | 2 +- ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt index 37a47df060a1c..f3302d77c1d63 100644 --- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt +++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt @@ -44,7 +44,7 @@ array(6) { [0]=> string(14) "GET / HTTP/1.1" [1]=> - string(21) "Host: 127.0.0.1:%d" + string(%d) "Host: 127.0.0.1:%d" [2]=> string(17) "Connection: close" [3]=> diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt index 6c84679ff63bd..30d20f855419c 100644 --- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt +++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt @@ -44,7 +44,7 @@ array(7) { [0]=> string(14) "GET / HTTP/1.1" [1]=> - string(21) "Host: 127.0.0.1:%d" + string(%d) "Host: 127.0.0.1:%d" [2]=> string(17) "Connection: close" [3]=> From 08b616e2f5384d03f06feaceb0ea60a773c39949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sat, 31 May 2025 22:31:48 +0200 Subject: [PATCH 28/54] Backport relevant changes of https://github.com/php/php-src/pull/18719 These property writes may now throw exceptions because of property hooks, and this was not handled previously. --- Zend/tests/exception_027.phpt | 37 ++++++++++++++++++++++++++++++ Zend/tests/exception_028.phpt | 43 +++++++++++++++++++++++++++++++++++ Zend/zend_exceptions.c | 30 ++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 Zend/tests/exception_027.phpt create mode 100644 Zend/tests/exception_028.phpt diff --git a/Zend/tests/exception_027.phpt b/Zend/tests/exception_027.phpt new file mode 100644 index 0000000000000..805127420b367 --- /dev/null +++ b/Zend/tests/exception_027.phpt @@ -0,0 +1,37 @@ +--TEST-- +Exception properties are overridden by property hooks +--FILE-- +modified) { + throw new Exception(); + } else { + $this->modified = true; + + $this->code = $value; + } + } + } +} + +$e = new MyException("foo", 1, new Exception()); + +try { + $e->__construct("bar", 2, null); +} catch (Exception) { +} + +var_dump($e->getMessage()); +var_dump($e->getCode()); +var_dump($e->getPrevious()::class); + +?> +--EXPECTF-- +string(3) "bar" +int(1) +string(9) "Exception" \ No newline at end of file diff --git a/Zend/tests/exception_028.phpt b/Zend/tests/exception_028.phpt new file mode 100644 index 0000000000000..233b545fb36bd --- /dev/null +++ b/Zend/tests/exception_028.phpt @@ -0,0 +1,43 @@ +--TEST-- +ErrorException properties are overridden by property hooks +--FILE-- +modified) { + throw new Exception(); + } else { + $this->modified = true; + + $this->code = $value; + } + } + } +} + +$e = new MyException("foo", 1, E_NOTICE, "file1", 1, new Exception()); + +try { + $e->__construct("bar", 2, E_WARNING, "file2", 2, null); +} catch (Exception) { +} + +var_dump($e->getMessage()); +var_dump($e->getCode()); +var_dump($e->getSeverity()); +var_dump($e->getFile()); +var_dump($e->getLine()); +var_dump($e->getPrevious()::class); + +?> +--EXPECTF-- +string(3) "bar" +int(1) +int(8) +string(5) "file1" +int(1) +string(9) "Exception" diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 207b781385452..446aba4ee4a2f 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -320,15 +320,24 @@ ZEND_METHOD(Exception, __construct) if (message) { ZVAL_STR(&tmp, message); zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } if (code) { ZVAL_LONG(&tmp, code); zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_CODE), &tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } if (previous) { zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } } /* }}} */ @@ -370,32 +379,53 @@ ZEND_METHOD(ErrorException, __construct) ZVAL_STR_COPY(&tmp, message); zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); zval_ptr_dtor(&tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } if (code) { ZVAL_LONG(&tmp, code); zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_CODE), &tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } if (previous) { zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } ZVAL_LONG(&tmp, severity); zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } if (filename) { ZVAL_STR_COPY(&tmp, filename); zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_FILE), &tmp); zval_ptr_dtor(&tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } if (!lineno_is_null) { ZVAL_LONG(&tmp, lineno); zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_LINE), &tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } else if (filename) { ZVAL_LONG(&tmp, 0); zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_LINE), &tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } } /* }}} */ From d39d261b7eba3c4ee91e7387713a9e42dde8112f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 31 May 2025 15:21:44 +0200 Subject: [PATCH 29/54] Fix memory leak in lookup_loc_range() Closes GH-18723. --- NEWS | 1 + ext/intl/locale/locale_methods.c | 1 + 2 files changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 02b90ff3dc7f6..40806102246ab 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,7 @@ PHP NEWS - Intl: . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) + . Fix memory leak in locale lookup on failure. (nielsdos) - Phar: . Add missing filter cleanups on phar failure. (nielsdos) diff --git a/ext/intl/locale/locale_methods.c b/ext/intl/locale/locale_methods.c index f810a61b6be12..44ae9a901a56e 100644 --- a/ext/intl/locale/locale_methods.c +++ b/ext/intl/locale/locale_methods.c @@ -1499,6 +1499,7 @@ static zend_string* lookup_loc_range(const char* loc_range, HashTable* hash_arr, zend_string_release_ex(can_loc_range, 0); } if(result == 0) { + efree(cur_loc_range); intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0); LOOKUP_CLEAN_RETURN(NULL); } From d8a17ca7c213242d4156931da496e1522f7286fd Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 1 Jun 2025 15:45:36 +0200 Subject: [PATCH 30/54] Fix test failures in engine_unsafe_empty_string.phpt (#18727) `/./` matches all characters but newlines, so if `random_bytes` generates a string with newlines in it, the resulting string is not empty. Fix this by adding the `s` modifier. --- ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt index 8333df88d6454..fe402b82b57b3 100644 --- a/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt +++ b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt @@ -11,7 +11,7 @@ final class EmptyStringEngine implements Engine public function generate(): string { // Create a non-interned empty string. - return preg_replace('/./', '', random_bytes(4)); + return preg_replace('/./s', '', random_bytes(4)); } } From 9187caeab121b506afb1369a7d05eb8d90c18507 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 1 Jun 2025 17:34:17 +0200 Subject: [PATCH 31/54] Fix ext/enchant test SKIPIFs The resource check makes no sense, and the is_array() check doesn't achieve anything. Drop the former, and replace the latter with a ! check. Discovered while working on GH-18729. Closes GH-18731. --- ext/enchant/tests/broker_dict_exists.phpt | 3 +-- ext/enchant/tests/broker_free.phpt | 4 ---- ext/enchant/tests/broker_free_01.phpt | 4 ---- ext/enchant/tests/broker_free_02.phpt | 3 +-- ext/enchant/tests/broker_free_dict.phpt | 3 +-- ext/enchant/tests/broker_get_error.phpt | 4 ---- ext/enchant/tests/broker_init.phpt | 4 ---- ext/enchant/tests/broker_request_dict.phpt | 3 +-- ext/enchant/tests/broker_request_dict_01.phpt | 3 +-- ext/enchant/tests/broker_request_dict_error_on_empty_tag.phpt | 4 ---- ext/enchant/tests/broker_request_pwl_dict.phpt | 4 ---- ext/enchant/tests/broker_set_ordering.phpt | 3 +-- ext/enchant/tests/bug53070.phpt | 1 - ext/enchant/tests/dict_add_to_personal.phpt | 3 +-- ext/enchant/tests/dict_add_to_session.phpt | 3 +-- ext/enchant/tests/dict_check.phpt | 3 +-- ext/enchant/tests/dict_describe.phpt | 3 +-- ext/enchant/tests/dict_get_error.phpt | 3 +-- ext/enchant/tests/dict_is_in_session.phpt | 3 +-- ext/enchant/tests/dict_quick_check.phpt | 3 +-- ext/enchant/tests/dict_quick_check_01.phpt | 3 +-- ext/enchant/tests/dict_store_replacement.phpt | 3 +-- ext/enchant/tests/dict_suggest.phpt | 3 +-- ext/enchant/tests/enchant_broker_set_dict_path.phpt | 3 +-- ext/enchant/tests/invalidobj.phpt | 4 ---- ext/enchant/tests/null_bytes.phpt | 3 +-- 26 files changed, 18 insertions(+), 65 deletions(-) diff --git a/ext/enchant/tests/broker_dict_exists.phpt b/ext/enchant/tests/broker_dict_exists.phpt index c4ca5d91eb3bd..494d2a539b3cc 100644 --- a/ext/enchant/tests/broker_dict_exists.phpt +++ b/ext/enchant/tests/broker_dict_exists.phpt @@ -6,8 +6,7 @@ marcosptf - enchant --SKIPIF-- --FILE-- --EXTENSIONS-- enchant ---SKIPIF-- - --FILE-- --EXTENSIONS-- enchant ---SKIPIF-- - --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- --EXTENSIONS-- enchant ---SKIPIF-- - --FILE-- --EXTENSIONS-- enchant ---SKIPIF-- - --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- --FILE-- --EXTENSIONS-- enchant ---SKIPIF-- - --FILE-- enchant --SKIPIF-- --FILE-- ")) die('skip libenchant v1 only'); ?> --FILE-- diff --git a/ext/enchant/tests/dict_add_to_personal.phpt b/ext/enchant/tests/dict_add_to_personal.phpt index 1fe6c1ff4a475..54d2be5e5d177 100644 --- a/ext/enchant/tests/dict_add_to_personal.phpt +++ b/ext/enchant/tests/dict_add_to_personal.phpt @@ -6,8 +6,7 @@ marcosptf - enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- ")) die('skip libenchant v1 only'); ?> --FILE-- diff --git a/ext/enchant/tests/invalidobj.phpt b/ext/enchant/tests/invalidobj.phpt index 8dbfc2e1f9a60..d9f60075754d9 100644 --- a/ext/enchant/tests/invalidobj.phpt +++ b/ext/enchant/tests/invalidobj.phpt @@ -2,10 +2,6 @@ invalid object raise exception() function --EXTENSIONS-- enchant ---SKIPIF-- - --FILE-- --FILE-- Date: Sun, 1 Jun 2025 17:58:46 +0200 Subject: [PATCH 32/54] Fix line assignment in zend_ast_create_va() The intent here was to assign the first found line. Instead this always fell back to CG(zend_lineno). Not sure if this line matters for anything in php-src, but the issue was observed in https://github.com/nikic/php-ast/issues/247. --- Zend/zend_ast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 09cc89684f877..f3f03940c62a6 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -279,7 +279,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_va( ast->attr = attr; for (uint32_t i = 0; i < children; i++) { ast->child[i] = va_arg(*va, zend_ast *); - if (lineno != (uint32_t)-1 && ast->child[i]) { + if (lineno == (uint32_t)-1 && ast->child[i]) { lineno = zend_ast_get_lineno(ast->child[i]); } } From 81593cfc6a08e56df836c6382e9873191b9cd6c1 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 2 Jun 2025 09:23:39 +0300 Subject: [PATCH 33/54] Update IR IR commit: e4343be0082897510c40a1b57baff427c6858878 --- ext/opcache/jit/ir/ir.c | 16 ++++++- ext/opcache/jit/ir/ir.h | 38 ++++++++++------ ext/opcache/jit/ir/ir_cfg.c | 50 ++++++++++++++++++++- ext/opcache/jit/ir/ir_emit.c | 4 +- ext/opcache/jit/ir/ir_fold.h | 78 ++++++++++++++++++++++++++++++++- ext/opcache/jit/ir/ir_private.h | 6 +-- ext/opcache/jit/ir/ir_sccp.c | 23 +++++++--- ext/opcache/jit/ir/ir_x86.dasc | 4 +- 8 files changed, 189 insertions(+), 30 deletions(-) diff --git a/ext/opcache/jit/ir/ir.c b/ext/opcache/jit/ir/ir.c index d9f7e3d0f7836..a9f55cc0e466c 100644 --- a/ext/opcache/jit/ir/ir.c +++ b/ext/opcache/jit/ir/ir.c @@ -918,7 +918,7 @@ static ir_ref _ir_fold_cse(ir_ctx *ctx, uint32_t opt, ir_ref op1, ir_ref op2, ir #define IR_FOLD_EMIT goto ir_fold_emit #define IR_FOLD_NEXT break -#include "ir_fold_hash.h" +#include #define IR_FOLD_RULE(x) ((x) >> 21) #define IR_FOLD_KEY(x) ((x) & 0x1fffff) @@ -1485,6 +1485,18 @@ void ir_update_op(ir_ctx *ctx, ir_ref ref, uint32_t idx, ir_ref new_val) void ir_array_grow(ir_array *a, uint32_t size) { IR_ASSERT(size > a->size); + if (size >= 256) { + size = IR_ALIGNED_SIZE(size, 256); + } else { + /* Use big enough power of 2 */ + size -= 1; + size |= (size >> 1); + size |= (size >> 2); + size |= (size >> 4); +// size |= (size >> 8); +// size |= (size >> 16); + size += 1; + } a->refs = ir_mem_realloc(a->refs, size * sizeof(ir_ref)); a->size = size; } @@ -1820,7 +1832,7 @@ int ir_mem_flush(void *ptr, size_t size) #else void *ir_mem_mmap(size_t size) { - int prot_flags = PROT_EXEC; + int prot_flags = PROT_EXEC; #if defined(__NetBSD__) prot_flags |= PROT_MPROTECT(PROT_READ|PROT_WRITE); #endif diff --git a/ext/opcache/jit/ir/ir.h b/ext/opcache/jit/ir/ir.h index 60c501d0bd470..ec5e57129c9ab 100644 --- a/ext/opcache/jit/ir/ir.h +++ b/ext/opcache/jit/ir/ir.h @@ -154,19 +154,27 @@ typedef enum _ir_type { } ir_type; #ifdef IR_64 -# define IR_SIZE_T IR_U64 -# define IR_SSIZE_T IR_I64 -# define IR_UINTPTR_T IR_U64 -# define IR_INTPTR_T IR_I64 -# define IR_C_UINTPTR IR_U64 -# define IR_C_INTPTR IR_I64 +# define IR_SIZE_T IR_U64 +# define IR_SSIZE_T IR_I64 +# define IR_UINTPTR_T IR_U64 +# define IR_INTPTR_T IR_I64 +# define IR_C_UINTPTR IR_U64 +# define IR_C_INTPTR IR_I64 +# define ir_const_size_t ir_const_u64 +# define ir_const_ssize_t ir_const_i64 +# define ir_const_uintptr_t ir_const_u64 +# define ir_const_intptr_t ir_const_i64 #else -# define IR_SIZE_T IR_U32 -# define IR_SSIZE_T IR_I32 -# define IR_UINTPTR_T IR_U32 -# define IR_INTPTR_T IR_I32 -# define IR_C_UINTPTR IR_U32 -# define IR_C_INTPTR IR_I32 +# define IR_SIZE_T IR_U32 +# define IR_SSIZE_T IR_I32 +# define IR_UINTPTR_T IR_U32 +# define IR_INTPTR_T IR_I32 +# define IR_C_UINTPTR IR_U32 +# define IR_C_INTPTR IR_I32 +# define ir_const_size_t ir_const_u32 +# define ir_const_ssize_t ir_const_i32 +# define ir_const_uintptr_t ir_const_u32 +# define ir_const_intptr_t ir_const_i32 #endif /* List of IR opcodes @@ -401,8 +409,10 @@ typedef int32_t ir_ref; #define IR_CONSTS_LIMIT_MIN (-(IR_TRUE - 1)) #define IR_INSNS_LIMIT_MIN (IR_UNUSED + 1) +/* ADDR_MEMBER is neccessary to workaround MSVC C preprocessor bug */ #ifndef IR_64 -# define ADDR_MEMBER uintptr_t addr; +# define ADDR_MEMBER uintptr_t addr; \ + void *ptr; #else # define ADDR_MEMBER #endif @@ -412,6 +422,7 @@ typedef union _ir_val { int64_t i64; #ifdef IR_64 uintptr_t addr; + void *ptr; #endif IR_STRUCT_LOHI( union { @@ -466,6 +477,7 @@ typedef struct _ir_insn { }, union { ir_ref op1; + ir_ref ref; ir_ref prev_const; } ); diff --git a/ext/opcache/jit/ir/ir_cfg.c b/ext/opcache/jit/ir/ir_cfg.c index 16facae51b1f6..01532c8ea3e30 100644 --- a/ext/opcache/jit/ir/ir_cfg.c +++ b/ext/opcache/jit/ir/ir_cfg.c @@ -77,6 +77,51 @@ void ir_reset_cfg(ir_ctx *ctx) } } +static uint32_t IR_NEVER_INLINE ir_cfg_remove_dead_inputs(ir_ctx *ctx, uint32_t *_blocks, ir_block *blocks, uint32_t bb_count) +{ + uint32_t b, count = 0; + ir_block *bb = blocks + 1; + ir_insn *insn; + ir_ref i, j, n, *ops, input; + + for (b = 1; b <= bb_count; b++, bb++) { + bb->successors = count; + count += ctx->use_lists[bb->end].count; + bb->successors_count = 0; + bb->predecessors = count; + insn = &ctx->ir_base[bb->start]; + if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN) { + n = insn->inputs_count; + ops = insn->ops; + for (i = 1, j = 1; i <= n; i++) { + input = ops[i]; + if (_blocks[input]) { + if (i != j) { + ops[j] = ops[i]; + } + j++; + } else if (input > 0) { + ir_use_list_remove_one(ctx, input, bb->start); + } + } + j--; + if (j != n) { + if (j == 1) { + insn->op = IR_BEGIN; + } + insn->inputs_count = j; + bb->predecessors_count = j; + j++; + for (;j <= n; j++) { + ops[j] = IR_UNUSED; + } + } + } + count += bb->predecessors_count; + } + return count; +} + int ir_build_cfg(ir_ctx *ctx) { ir_ref n, *p, ref, start, end; @@ -239,7 +284,10 @@ int ir_build_cfg(ir_ctx *ctx) bb++; } IR_BITSET_FOREACH_END(); bb_count = b - 1; - IR_ASSERT(count == edges_count * 2); + if (UNEXPECTED(count != edges_count * 2)) { + count = ir_cfg_remove_dead_inputs(ctx, _blocks, blocks, bb_count); + IR_ASSERT(count != edges_count * 2); + } ir_mem_free(bb_starts); /* Create an array of successor/predecessors control edges */ diff --git a/ext/opcache/jit/ir/ir_emit.c b/ext/opcache/jit/ir/ir_emit.c index 5cf44a51d0f48..c82655daf48db 100644 --- a/ext/opcache/jit/ir/ir_emit.c +++ b/ext/opcache/jit/ir/ir_emit.c @@ -415,9 +415,9 @@ static int ir_const_label(ir_ctx *ctx, ir_ref ref) } #if defined(IR_TARGET_X86) || defined(IR_TARGET_X64) -# include "ir_emit_x86.h" +# include #elif defined(IR_TARGET_AARCH64) -# include "ir_emit_aarch64.h" +# include #else # error "Unknown IR target" #endif diff --git a/ext/opcache/jit/ir/ir_fold.h b/ext/opcache/jit/ir/ir_fold.h index 78f3ca0c01e5f..88539e52ab085 100644 --- a/ext/opcache/jit/ir/ir_fold.h +++ b/ext/opcache/jit/ir/ir_fold.h @@ -486,10 +486,36 @@ IR_FOLD(MUL(C_FLOAT, C_FLOAT)) } IR_FOLD(DIV(C_U8, C_U8)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.u64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_U(op1_insn->val.u8 / op2_insn->val.u8); +} + IR_FOLD(DIV(C_U16, C_U16)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.u64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_U(op1_insn->val.u16 / op2_insn->val.u16); +} + IR_FOLD(DIV(C_U32, C_U32)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.u64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_U(op1_insn->val.u32 / op2_insn->val.u32); +} + IR_FOLD(DIV(C_U64, C_U64)) -IR_FOLD(DIV(C_ADDR, C_ADDR)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); if (op2_insn->val.u64 == 0) { @@ -499,9 +525,46 @@ IR_FOLD(DIV(C_ADDR, C_ADDR)) IR_FOLD_CONST_U(op1_insn->val.u64 / op2_insn->val.u64); } +IR_FOLD(DIV(C_ADDR, C_ADDR)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.u64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_U(op1_insn->val.addr / op2_insn->val.addr); +} + IR_FOLD(DIV(C_I8, C_I8)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.i64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_I(op1_insn->val.i8 / op2_insn->val.i8); +} + IR_FOLD(DIV(C_I16, C_I16)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.i64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_I(op1_insn->val.i16 / op2_insn->val.i16); +} + IR_FOLD(DIV(C_I32, C_I32)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.i64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_I(op1_insn->val.i32 / op2_insn->val.i32); +} + IR_FOLD(DIV(C_I64, C_I64)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); @@ -1135,6 +1198,7 @@ IR_FOLD(MAX(C_FLOAT, C_FLOAT)) IR_FOLD(SEXT(C_I8)) IR_FOLD(SEXT(C_U8)) IR_FOLD(SEXT(C_BOOL)) +IR_FOLD(SEXT(C_CHAR)) { IR_ASSERT(IR_IS_TYPE_INT(IR_OPT_TYPE(opt))); IR_ASSERT(ir_type_size[IR_OPT_TYPE(opt)] > ir_type_size[op1_insn->type]); @@ -1160,6 +1224,7 @@ IR_FOLD(SEXT(C_U32)) IR_FOLD(ZEXT(C_I8)) IR_FOLD(ZEXT(C_U8)) IR_FOLD(ZEXT(C_BOOL)) +IR_FOLD(ZEXT(C_CHAR)) { IR_ASSERT(IR_IS_TYPE_INT(IR_OPT_TYPE(opt))); IR_ASSERT(ir_type_size[IR_OPT_TYPE(opt)] > ir_type_size[op1_insn->type]); @@ -1195,12 +1260,14 @@ IR_FOLD(TRUNC(C_U64)) default: IR_ASSERT(0); case IR_I8: + case IR_CHAR: IR_FOLD_CONST_I(op1_insn->val.i8); case IR_I16: IR_FOLD_CONST_I(op1_insn->val.i16); case IR_I32: IR_FOLD_CONST_I(op1_insn->val.i32); case IR_U8: + case IR_BOOL: IR_FOLD_CONST_U(op1_insn->val.u8); case IR_U16: IR_FOLD_CONST_U(op1_insn->val.u16); @@ -1474,6 +1541,10 @@ IR_FOLD(EQ(SEXT, C_ADDR)) } else { ir_type type = ctx->ir_base[op1_insn->op1].type; + if (op1_insn->op == IR_ZEXT + && (op2_insn->val.u64 >> (ir_type_size[type] * 8)) != 0) { + IR_FOLD_NEXT; + } if (IR_IS_TYPE_SIGNED(type)) { switch (ir_type_size[type]) { case 1: val.i64 = op2_insn->val.i8; break; @@ -1493,6 +1564,7 @@ IR_FOLD(EQ(SEXT, C_ADDR)) op2 = ir_const(ctx, val, type); IR_FOLD_RESTART; } + IR_FOLD_NEXT; } @@ -1518,6 +1590,10 @@ IR_FOLD(NE(SEXT, C_ADDR)) } else { ir_type type = ctx->ir_base[op1_insn->op1].type; + if (op1_insn->op == IR_ZEXT + && (op2_insn->val.u64 >> (ir_type_size[type] * 8)) != 0) { + IR_FOLD_NEXT; + } if (IR_IS_TYPE_SIGNED(type)) { switch (ir_type_size[type]) { case 1: val.i64 = op2_insn->val.i8; break; diff --git a/ext/opcache/jit/ir/ir_private.h b/ext/opcache/jit/ir/ir_private.h index 9c69d6074defe..69a0101d24ee2 100644 --- a/ext/opcache/jit/ir/ir_private.h +++ b/ext/opcache/jit/ir/ir_private.h @@ -255,9 +255,7 @@ IR_ALWAYS_INLINE void ir_arena_free(ir_arena *arena) IR_ALWAYS_INLINE void* ir_arena_alloc(ir_arena **arena_ptr, size_t size) { ir_arena *arena = *arena_ptr; - char *ptr = arena->ptr; - - size = IR_ALIGNED_SIZE(size, 8); + char *ptr = (char*)IR_ALIGNED_SIZE((uintptr_t)arena->ptr, 8); if (EXPECTED(size <= (size_t)(arena->end - ptr))) { arena->ptr = ptr + size; @@ -283,7 +281,7 @@ IR_ALWAYS_INLINE void* ir_arena_checkpoint(ir_arena *arena) return arena->ptr; } -IR_ALWAYS_INLINE void ir_release(ir_arena **arena_ptr, void *checkpoint) +IR_ALWAYS_INLINE void ir_arena_release(ir_arena **arena_ptr, void *checkpoint) { ir_arena *arena = *arena_ptr; diff --git a/ext/opcache/jit/ir/ir_sccp.c b/ext/opcache/jit/ir/ir_sccp.c index 8480861f91fe7..2e006516df818 100644 --- a/ext/opcache/jit/ir/ir_sccp.c +++ b/ext/opcache/jit/ir/ir_sccp.c @@ -1994,10 +1994,16 @@ static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref if (use_insn->op >= IR_EQ && use_insn->op <= IR_UGT) { if (use_insn->op1 == phi_ref) { + if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op2].type)) { + return 0; + } if (ir_is_cheaper_ext(ctx, use_insn->op2, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } } else if (use_insn->op2 == phi_ref) { + if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op1].type)) { + return 0; + } if (ir_is_cheaper_ext(ctx, use_insn->op1, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } @@ -2027,10 +2033,16 @@ static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref if (use_insn->op >= IR_EQ && use_insn->op <= IR_UGT) { if (use_insn->op1 == phi_ref) { + if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op2].type)) { + return 0; + } if (ir_is_cheaper_ext(ctx, use_insn->op2, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } } else if (use_insn->op2 == phi_ref) { + if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op1].type)) { + return 0; + } if (ir_is_cheaper_ext(ctx, use_insn->op1, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } @@ -3570,11 +3582,12 @@ void ir_iter_opt(ir_ctx *ctx, ir_bitqueue *worklist) if (val_insn->type == insn->type) { ir_iter_replace_insn(ctx, i, val, worklist); } else { - IR_ASSERT(!IR_IS_CONST_REF(insn->op2)); - ir_use_list_remove_one(ctx, insn->op2, i); - if (ir_is_dead(ctx, insn->op2)) { - /* schedule DCE */ - ir_bitqueue_add(worklist, insn->op2); + if (!IR_IS_CONST_REF(insn->op2)) { + ir_use_list_remove_one(ctx, insn->op2, i); + if (ir_is_dead(ctx, insn->op2)) { + /* schedule DCE */ + ir_bitqueue_add(worklist, insn->op2); + } } if (!IR_IS_CONST_REF(val)) { ir_use_list_add(ctx, val, i); diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index d01a8c41359aa..76602c2b4bcf5 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -3236,7 +3236,7 @@ static void ir_emit_store_mem_int_const(ir_ctx *ctx, ir_type type, ir_mem mem, i val = (int64_t)(intptr_t)ir_sym_val(ctx, val_insn); } - if (sizeof(void*) == 4 || IR_IS_SIGNED_32BIT(val)) { + if (ir_type_size[val_insn->type] <= 4 || IR_IS_SIGNED_32BIT(val)) { if (is_arg && ir_type_size[type] < 4) { type = IR_U32; } @@ -3664,7 +3664,7 @@ static int32_t ir_fuse_imm(ir_ctx *ctx, ir_ref ref) IR_ASSERT(IR_IS_SIGNED_32BIT((intptr_t)addr)); return (int32_t)(intptr_t)addr; } else { - IR_ASSERT(IR_IS_SIGNED_32BIT(val_insn->val.i32)); + IR_ASSERT(ir_type_size[val_insn->type] == 4 || IR_IS_SIGNED_32BIT(val_insn->val.i64)); return val_insn->val.i32; } } From 08a95798833963bfb1a33bbacd19045b84d491b9 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 3 Jun 2025 21:19:15 +0200 Subject: [PATCH 34/54] Fix memory leak of X509_STORE in php_openssl_setup_verify() on failure Closes GH-18750. --- NEWS | 4 ++++ ext/openssl/openssl.c | 1 + ext/openssl/tests/memory_leak_x509_store.phpt | 22 +++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 ext/openssl/tests/memory_leak_x509_store.phpt diff --git a/NEWS b/NEWS index 40806102246ab..d6f38f9adceb9 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,10 @@ PHP NEWS . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) . Fix memory leak in locale lookup on failure. (nielsdos) +- OpenSSL: + . Fix memory leak of X509_STORE in php_openssl_setup_verify() on failure. + (nielsdos) + - Phar: . Add missing filter cleanups on phar failure. (nielsdos) . Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index c978859b7ec00..718f946ad176d 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -2421,6 +2421,7 @@ static X509_STORE *php_openssl_setup_verify(zval *calist, uint32_t arg_num) ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(calist), item) { zend_string *str = zval_try_get_string(item); if (UNEXPECTED(!str)) { + X509_STORE_free(store); return NULL; } diff --git a/ext/openssl/tests/memory_leak_x509_store.phpt b/ext/openssl/tests/memory_leak_x509_store.phpt new file mode 100644 index 0000000000000..bc9b113602a33 --- /dev/null +++ b/ext/openssl/tests/memory_leak_x509_store.phpt @@ -0,0 +1,22 @@ +--TEST-- +Memory leak of X509_STORE in php_openssl_setup_verify() on failure +--EXTENSIONS-- +openssl +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +stop From 111072a9f0353e885abb9fd1c3cb15e56fff9703 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 3 Jun 2025 19:08:20 +0200 Subject: [PATCH 35/54] Fix GH-18744: PHP 8.4 classList works not correctly if copy HTMLElement by clone keyword. The $classList property is special in the sense that it's a cached object instance per (HTML)Element instance. The reason for this design is because it has the [[SameObject]] IDL attribute. Cloning in PHP also clones the properties, so it also clones the cached instance. To solve this, we undo this by resetting the backing storage. Closes GH-18749. --- NEWS | 4 +++ ext/dom/element.c | 15 ++++++---- ext/dom/php_dom.c | 23 +++++++++++++-- ext/dom/php_dom.h | 1 + ext/dom/tests/modern/token_list/gh18744.phpt | 30 ++++++++++++++++++++ 5 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 ext/dom/tests/modern/token_list/gh18744.phpt diff --git a/NEWS b/NEWS index 5d10703707a44..b577d8fd948b5 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,10 @@ PHP NEWS - Date: . Fix leaks with multiple calls to DatePeriod iterator current(). (nielsdos) +- DOM: + . Fixed bug GH-18744 (classList works not correctly if copy HTMLElement by + clone keyword). (nielsdos) + - FPM: . Fixed GH-18662 (fpm_get_status segfault). (txuna) diff --git a/ext/dom/element.c b/ext/dom/element.c index 64c53e4a2d8a1..e25805df53eb6 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -177,10 +177,7 @@ zend_result dom_element_class_name_write(dom_object *obj, zval *newval) } /* }}} */ -/* {{{ classList TokenList -URL: https://dom.spec.whatwg.org/#dom-element-classlist -*/ -zend_result dom_element_class_list_read(dom_object *obj, zval *retval) +zval *dom_element_class_list_zval(dom_object *obj) { const uint32_t PROP_INDEX = 0; @@ -191,7 +188,15 @@ zend_result dom_element_class_list_read(dom_object *obj, zval *retval) ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == PROP_INDEX); #endif - zval *cached_token_list = OBJ_PROP_NUM(&obj->std, PROP_INDEX); + return OBJ_PROP_NUM(&obj->std, PROP_INDEX); +} + +/* {{{ classList TokenList +URL: https://dom.spec.whatwg.org/#dom-element-classlist +*/ +zend_result dom_element_class_list_read(dom_object *obj, zval *retval) +{ + zval *cached_token_list = dom_element_class_list_zval(obj); if (Z_ISUNDEF_P(cached_token_list)) { object_init_ex(cached_token_list, dom_token_list_class_entry); dom_token_list_object *intern = php_dom_token_list_from_obj(Z_OBJ_P(cached_token_list)); diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 3c8d6e15cea20..3242529d8842c 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -101,6 +101,7 @@ static zend_object_handlers dom_modern_nodelist_object_handlers; static zend_object_handlers dom_html_collection_object_handlers; static zend_object_handlers dom_object_namespace_node_handlers; static zend_object_handlers dom_modern_domimplementation_object_handlers; +static zend_object_handlers dom_modern_element_object_handlers; static zend_object_handlers dom_token_list_object_handlers; #ifdef LIBXML_XPATH_ENABLED zend_object_handlers dom_xpath_object_handlers; @@ -669,6 +670,21 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */ } /* }}} */ +static zend_object *dom_modern_element_clone_obj(zend_object *zobject) +{ + zend_object *clone = dom_objects_store_clone_obj(zobject); + + /* The $classList property is unique per element, and cached due to its [[SameObject]] requirement. + * Remove it from the clone so the clone will get a fresh instance upon demand. */ + zval *class_list = dom_element_class_list_zval(php_dom_obj_from_obj(clone)); + if (!Z_ISUNDEF_P(class_list)) { + zval_ptr_dtor(class_list); + ZVAL_UNDEF(class_list); + } + + return clone; +} + static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject) { dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(zobject); @@ -778,6 +794,9 @@ PHP_MINIT_FUNCTION(dom) * one instance per parent object. */ dom_modern_domimplementation_object_handlers.clone_obj = NULL; + memcpy(&dom_modern_element_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); + dom_modern_element_object_handlers.clone_obj = dom_modern_element_clone_obj; + memcpy(&dom_nnodemap_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_nnodemap_object_handlers.free_obj = dom_nnodemap_objects_free_storage; dom_nnodemap_object_handlers.read_dimension = dom_nodemap_read_dimension; @@ -1108,7 +1127,7 @@ PHP_MINIT_FUNCTION(dom) dom_modern_element_class_entry = register_class_Dom_Element(dom_modern_node_class_entry, dom_modern_parentnode_class_entry, dom_modern_childnode_class_entry); dom_modern_element_class_entry->create_object = dom_objects_new; - dom_modern_element_class_entry->default_object_handlers = &dom_object_handlers; + dom_modern_element_class_entry->default_object_handlers = &dom_modern_element_object_handlers; zend_hash_init(&dom_modern_element_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL); @@ -1132,7 +1151,7 @@ PHP_MINIT_FUNCTION(dom) dom_html_element_class_entry = register_class_Dom_HTMLElement(dom_modern_element_class_entry); dom_html_element_class_entry->create_object = dom_objects_new; - dom_html_element_class_entry->default_object_handlers = &dom_object_handlers; + dom_html_element_class_entry->default_object_handlers = &dom_modern_element_object_handlers; zend_hash_add_new_ptr(&classes, dom_html_element_class_entry->name, &dom_modern_element_prop_handlers); dom_text_class_entry = register_class_DOMText(dom_characterdata_class_entry); diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index bb0d83676dca1..22c738b20e0f6 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -187,6 +187,7 @@ bool php_dom_create_nullable_object(xmlNodePtr obj, zval *return_value, dom_obje xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node, xmlDocPtr doc, bool recursive); void dom_set_document_ref_pointers(xmlNodePtr node, php_libxml_ref_obj *document); void dom_set_document_ref_pointers_attr(xmlAttrPtr attr, php_libxml_ref_obj *document); +zval *dom_element_class_list_zval(dom_object *obj); typedef enum { DOM_LOAD_STRING = 0, diff --git a/ext/dom/tests/modern/token_list/gh18744.phpt b/ext/dom/tests/modern/token_list/gh18744.phpt new file mode 100644 index 0000000000000..a9109df789d8f --- /dev/null +++ b/ext/dom/tests/modern/token_list/gh18744.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-18744 (classList works not correctly if copy HTMLElement by clone keyword.) +--EXTENSIONS-- +dom +--FILE-- +createElement('div'); +$ele1->classList->add('foo'); +$ele2 = clone $ele1; +$ele2->classList->add('bar'); + +echo "Element1 class: " . $ele1->getAttribute('class'); +echo "\n"; +echo "Element2 class: " . $ele2->getAttribute('class'); +echo "\n"; + +var_dump($ele1->classList !== $ele2->classList); +// These comparisons are not pointless: they're getters and should not create new objects +var_dump($ele1->classList === $ele1->classList); +var_dump($ele2->classList === $ele2->classList); + +?> +--EXPECT-- +Element1 class: foo +Element2 class: foo bar +bool(true) +bool(true) +bool(true) From 2b383848a738eda02bc0f5bf116b021a6e42c24a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 4 Jun 2025 19:52:21 +0200 Subject: [PATCH 36/54] Fix handling of references in zval_try_get_long() This API can't handle references, yet everyone keeps forgetting that it can't and that you should DEREF upfront. Fix every type of this issue once and for all by moving the reference handling to this Zend API. Closes GH-18761. --- NEWS | 1 + Zend/zend_operators.c | 9 +++++++++ ext/intl/dateformat/dateformat_parse.c | 8 +++----- ext/mbstring/mbstring.c | 2 +- .../tests/mb_encode_numericentity_references.phpt | 12 ++++++++++++ ext/pcntl/pcntl.c | 3 +-- 6 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 ext/mbstring/tests/mb_encode_numericentity_references.phpt diff --git a/NEWS b/NEWS index b577d8fd948b5..6c30ac60aa88d 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ PHP NEWS released on bailout). (DanielEScherzer and ilutov) . Fixed GH-18695 (zend_ast_export() - float number is not preserved). (Oleg Efimov) + . Fix handling of references in zval_try_get_long(). (nielsdos) - Curl: . Fix memory leak when setting a list via curl_setopt fails. (nielsdos) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index ae75e95a71c1d..252b8df1ea0fb 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -378,6 +378,7 @@ static zend_always_inline zend_result zendi_try_convert_scalar_to_number(zval *o static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval *op, bool *failed) /* {{{ */ { *failed = 0; +try_again: switch (Z_TYPE_P(op)) { case IS_NULL: case IS_FALSE: @@ -448,6 +449,14 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval * case IS_ARRAY: *failed = 1; return 0; + case IS_REFERENCE: + op = Z_REFVAL_P(op); + if (Z_TYPE_P(op) == IS_LONG) { + return Z_LVAL_P(op); + } else { + goto try_again; + } + break; EMPTY_SWITCH_DEFAULT_CASE() } } diff --git a/ext/intl/dateformat/dateformat_parse.c b/ext/intl/dateformat/dateformat_parse.c index 129e007107db7..2bdde08bcaced 100644 --- a/ext/intl/dateformat/dateformat_parse.c +++ b/ext/intl/dateformat/dateformat_parse.c @@ -185,12 +185,10 @@ PHP_METHOD(IntlDateFormatter, parseToCalendar) DATE_FORMAT_METHOD_FETCH_OBJECT; if (z_parse_pos) { - zval *z_parse_pos_tmp = z_parse_pos; - ZVAL_DEREF(z_parse_pos_tmp); - bool failed = false; - zend_long long_parse_pos = zval_try_get_long(z_parse_pos_tmp, &failed); + bool failed; + zend_long long_parse_pos = zval_try_get_long(z_parse_pos, &failed); if (failed) { - zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos_tmp)); + zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos)); RETURN_THROWS(); } if (ZEND_LONG_INT_OVFL(long_parse_pos)) { diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index e848f21615bca..ca5a8b8ec72e4 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -3930,7 +3930,7 @@ static uint32_t *make_conversion_map(HashTable *target_hash, size_t *conversion_ uint32_t *mapelm = convmap; ZEND_HASH_FOREACH_VAL(target_hash, hash_entry) { - bool failed = true; + bool failed; zend_long tmp = zval_try_get_long(hash_entry, &failed); if (failed) { efree(convmap); diff --git a/ext/mbstring/tests/mb_encode_numericentity_references.phpt b/ext/mbstring/tests/mb_encode_numericentity_references.phpt new file mode 100644 index 0000000000000..682be9b2cd1e2 --- /dev/null +++ b/ext/mbstring/tests/mb_encode_numericentity_references.phpt @@ -0,0 +1,12 @@ +--TEST-- +mb_encode_numericentity() reference handling +--EXTENSIONS-- +mbstring +--FILE-- + +--EXPECT-- +string(0) "" diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 89631960d5677..25a4de9386d44 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -874,8 +874,7 @@ static bool php_pcntl_set_user_signal_infos( zval *user_signal_no; ZEND_HASH_FOREACH_VAL(user_signals, user_signal_no) { - bool failed = true; - ZVAL_DEREF(user_signal_no); + bool failed; zend_long tmp = zval_try_get_long(user_signal_no, &failed); if (failed) { From e13ba36abbce730b552768d13a575799937a6e2d Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 4 Jun 2025 19:22:06 +0100 Subject: [PATCH 37/54] ext/tidy: anticipate tidyOptIsReadOnly retirement. using tidyOptGetCategory when possible. related GH-18751 close GH-18763 --- NEWS | 1 + ext/tidy/config.m4 | 5 +++++ ext/tidy/tidy.c | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/NEWS b/NEWS index d6f38f9adceb9..9b2c97db0b1cf 100644 --- a/NEWS +++ b/NEWS @@ -44,6 +44,7 @@ PHP NEWS - Tidy: . Fix memory leak in tidy output handler on error. (nielsdos) + . Fix tidyOptIsReadonly deprecation, using tidyOptGetCategory. (David Carlier) 05 Jun 2025, PHP 8.3.22 diff --git a/ext/tidy/config.m4 b/ext/tidy/config.m4 index bc0976a1dd9b3..569cb3672944b 100644 --- a/ext/tidy/config.m4 +++ b/ext/tidy/config.m4 @@ -62,6 +62,11 @@ if test "$PHP_TIDY" != "no"; then AC_DEFINE(HAVE_TIDYRELEASEDATE,1,[ ]) ], [], []) + PHP_CHECK_LIBRARY($TIDY_LIB_NAME,tidyOptGetCategory, + [ + AC_DEFINE(HAVE_TIDYOPTGETCATEGORY,1,[ ]) + ], [], []) + PHP_ADD_LIBRARY_WITH_PATH($TIDY_LIB_NAME, $TIDY_LIBDIR, TIDY_SHARED_LIBADD) PHP_ADD_INCLUDE($TIDY_INCDIR) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index 46dd637f40e54..a42e2bc203770 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -232,7 +232,11 @@ static int _php_tidy_set_tidy_opt(TidyDoc doc, char *optname, zval *value) return FAILURE; } +#if defined(HAVE_TIDYOPTGETCATEGORY) + if (tidyOptGetCategory(opt) == TidyInternalCategory) { +#else if (tidyOptIsReadOnly(opt)) { +#endif php_error_docref(NULL, E_WARNING, "Attempting to set read-only option \"%s\"", optname); return FAILURE; } From 42f6c15186b289fa16d71bf0a33cbc5da6098c43 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 21 Feb 2025 15:23:24 +0100 Subject: [PATCH 38/54] Fix bug #74796: Requests through http proxy set peer name This issue happens because http wrapper sets peer_name but then does not remove so it stays in the context. The fix removes the peer name from the context after enabling crypto. In addition to bug #74796, this also fixes bug #76196. In addition it should be a final fix for those SOAP bugs: bug #69783 bug #52913 bug #61463 --- NEWS | 2 + ext/openssl/tests/bug74796.phpt | 175 ++++++++++++++++++++++++++++++ ext/standard/http_fopen_wrapper.c | 6 + main/streams/php_stream_context.h | 3 +- main/streams/streams.c | 14 +++ 5 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 ext/openssl/tests/bug74796.phpt diff --git a/NEWS b/NEWS index 9b2c97db0b1cf..6867a3dbcaeb8 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,8 @@ PHP NEWS - OpenSSL: . Fix memory leak of X509_STORE in php_openssl_setup_verify() on failure. (nielsdos) + . Fixed bug #74796 (Requests through http proxy set peer name). + (Jakub Zelenka) - Phar: . Add missing filter cleanups on phar failure. (nielsdos) diff --git a/ext/openssl/tests/bug74796.phpt b/ext/openssl/tests/bug74796.phpt new file mode 100644 index 0000000000000..a5c99e61697a8 --- /dev/null +++ b/ext/openssl/tests/bug74796.phpt @@ -0,0 +1,175 @@ +--TEST-- +Bug #74796: TLS encryption fails behind HTTP proxy +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'SNI_server_certs' => [ + "cs.php.net" => __DIR__ . "/sni_server_cs.pem", + "uk.php.net" => __DIR__ . "/sni_server_uk.pem", + "us.php.net" => __DIR__ . "/sni_server_us.pem" + ] + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $serverFlags, $ctx); + phpt_notify_server_start($server); + + for ($i=0; $i < 3; $i++) { + $conn = stream_socket_accept($server, 3); + fwrite($conn, "HTTP/1.0 200 OK\r\n\r\nHello from server $i"); + fclose($conn); + } + + phpt_wait(); +CODE; + +$proxyCode = <<<'CODE' + function parse_sni_from_client_hello($data) { + $sni = null; + + if (strlen($data) < 5 || ord($data[0]) != 0x16) return null; + + $session_id_len = ord($data[43]); + $ptr = 44 + $session_id_len; + + // Cipher suites length + $cipher_suites_len = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ptr += 2 + $cipher_suites_len; + + // Compression methods length + $compression_methods_len = ord($data[$ptr]); + $ptr += 1 + $compression_methods_len; + + // Extensions length + if ($ptr + 2 > strlen($data)) return null; + $extensions_len = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ptr += 2; + + $extensions_end = $ptr + $extensions_len; + + while ($ptr + 4 <= $extensions_end) { + $ext_type = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ext_len = (ord($data[$ptr+2]) << 8) | ord($data[$ptr+3]); + $ptr += 4; + + if ($ext_type === 0x00) { // SNI extension + if ($ptr + 2 > strlen($data)) break; + $name_list_len = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ptr += 2; + + if ($ptr + 3 > strlen($data)) break; + $name_type = ord($data[$ptr]); + $name_len = (ord($data[$ptr+1]) << 8) | ord($data[$ptr+2]); + $ptr += 3; + + if ($name_type === 0) { // host_name type + $sni = substr($data, $ptr, $name_len); + break; + } + } + + $ptr += $ext_len; + } + + return $sni; + } + + $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; + $server = stream_socket_server("tcp://127.0.0.1:0", $errornum, $errorstr, $flags); + phpt_notify_server_start($server); + + for ($i=0; $i < 3; $i++) { + $upstream = stream_socket_client("tcp://{{ ADDR }}", $errornum, $errorstr, 30, STREAM_CLIENT_CONNECT); + stream_set_blocking($upstream, false); + + $conn = stream_socket_accept($server); + stream_set_blocking($conn, true); + + // reading CONNECT request headers + while (($line = fgets($conn)) !== false) { + if (rtrim($line) === '') break; // empty line means end of headers + } + + // successful CONNECT response + fwrite($conn, "HTTP/1.0 200 Connection established\r\n\r\n"); + + // tunnel data + stream_set_blocking($conn, false); + $firstRead = true; + while (!feof($conn) && !feof($upstream)) { + $clientData = fread($conn, 8192); + if ($clientData !== false && $clientData !== '') { + if ($firstRead) { + $sni = parse_sni_from_client_hello($clientData); + if ($sni !== null) { + file_put_contents(__DIR__ . "/bug74796_proxy_sni.log", $sni . "\n", FILE_APPEND); + } + $firstRead = false; + } + fwrite($upstream, $clientData); + } + + $serverData = fread($upstream, 8192); + if ($serverData !== false && $serverData !== '') { + fwrite($conn, $serverData); + } + } + fclose($conn); + fclose($upstream); + phpt_wait(); + } +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create([ + 'ssl' => [ + 'cafile' => __DIR__ . '/sni_server_ca.pem', + 'verify_peer' => true, + 'verify_peer_name' => true, + ], + "http" => [ + "proxy" => "tcp://{{ ADDR }}" + ], + ]); + + // servers + $hosts = ["cs.php.net", "uk.php.net", "us.php.net"]; + foreach ($hosts as $host) { + var_dump(file_get_contents("https://$host/", false, $clientCtx)); + var_dump(stream_context_get_options($clientCtx)['ssl']['peer_name'] ?? null); + phpt_notify('proxy'); + } + + echo file_get_contents(__DIR__ . "/bug74796_proxy_sni.log"); + + phpt_notify('server'); +CODE; + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, [ + 'server' => $serverCode, + 'proxy' => $proxyCode, +]); +?> +--CLEAN-- + +--EXPECT-- +string(19) "Hello from server 0" +NULL +string(19) "Hello from server 1" +NULL +string(19) "Hello from server 2" +NULL +cs.php.net +uk.php.net +us.php.net diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index b4d065dd0b625..040ee4eabf78b 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -470,12 +470,14 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (stream && use_proxy && use_ssl) { smart_str header = {0}; + bool reset_ssl_peer_name = false; /* Set peer_name or name verification will try to use the proxy server name */ if (!context || (tmpzval = php_stream_context_get_option(context, "ssl", "peer_name")) == NULL) { ZVAL_STR_COPY(&ssl_proxy_peer_name, resource->host); php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name", &ssl_proxy_peer_name); zval_ptr_dtor(&ssl_proxy_peer_name); + reset_ssl_peer_name = true; } smart_str_appendl(&header, "CONNECT ", sizeof("CONNECT ")-1); @@ -572,6 +574,10 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, stream = NULL; } } + + if (reset_ssl_peer_name) { + php_stream_context_unset_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name"); + } } php_stream_http_response_header_info_init(&header_info); diff --git a/main/streams/php_stream_context.h b/main/streams/php_stream_context.h index d4ebe29bc162e..56a1f53747116 100644 --- a/main/streams/php_stream_context.h +++ b/main/streams/php_stream_context.h @@ -59,7 +59,8 @@ PHPAPI zval *php_stream_context_get_option(php_stream_context *context, const char *wrappername, const char *optionname); PHPAPI void php_stream_context_set_option(php_stream_context *context, const char *wrappername, const char *optionname, zval *optionvalue); - +void php_stream_context_unset_option(php_stream_context *context, + const char *wrappername, const char *optionname); PHPAPI php_stream_notifier *php_stream_notification_alloc(void); PHPAPI void php_stream_notification_free(php_stream_notifier *notifier); END_EXTERN_C() diff --git a/main/streams/streams.c b/main/streams/streams.c index 4e0aaa53b443e..4f9c88e4774c4 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -2432,6 +2432,20 @@ PHPAPI void php_stream_context_set_option(php_stream_context *context, SEPARATE_ARRAY(wrapperhash); zend_hash_str_update(Z_ARRVAL_P(wrapperhash), optionname, strlen(optionname), optionvalue); } + +void php_stream_context_unset_option(php_stream_context *context, + const char *wrappername, const char *optionname) +{ + zval *wrapperhash; + + wrapperhash = zend_hash_str_find(Z_ARRVAL(context->options), wrappername, strlen(wrappername)); + if (NULL == wrapperhash) { + return; + } + SEPARATE_ARRAY(&context->options); + SEPARATE_ARRAY(wrapperhash); + zend_hash_str_del(Z_ARRVAL_P(wrapperhash), optionname, strlen(optionname)); +} /* }}} */ /* {{{ php_stream_dirent_alphasort */ From 444cc78a3e94e90a9424e7200ff9dca410d3f28f Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 5 Jun 2025 16:18:06 +0200 Subject: [PATCH 39/54] Skip OpenSSL proxy test for bug #74796 on Windows --- ext/openssl/tests/bug74796.phpt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/openssl/tests/bug74796.phpt b/ext/openssl/tests/bug74796.phpt index a5c99e61697a8..b3f594d5e60f4 100644 --- a/ext/openssl/tests/bug74796.phpt +++ b/ext/openssl/tests/bug74796.phpt @@ -5,6 +5,9 @@ openssl --SKIPIF-- --FILE-- Date: Thu, 5 Jun 2025 21:51:30 +0200 Subject: [PATCH 40/54] Fix compile without ZEND_MM_STORAGE --- Zend/zend_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index d531270d445ed..573fd5fa26b80 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -3043,7 +3043,7 @@ ZEND_API zend_mm_storage *zend_mm_get_storage(zend_mm_heap *heap) #if ZEND_MM_STORAGE return heap->storage; #else - return NULL + return NULL; #endif } From ae92b85572ac9a5f14103cdedbf6448f9e8db09d Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 14 Feb 2024 13:52:01 +0100 Subject: [PATCH 41/54] Fix linking ext/curl against OpenSSL (#13262) This is backport for 8.3 of b222c020bfa876ae1cea87406beb8af0b53f0fab that originally targeted only 8.4+. This is however a bug fix. Following 68f6ab711323678382d2746e57358d3f57a3446b, the ext/curl doesn't need to be linked against OpenSSL anymore, if curl_version_info_data ssl_version is OpenSSL/1.1 or later. With OpenSSL 3 and later the check for old SSL crypto locking callbacks was detected here. This also uses a common PHP_SETUP_OPENSSL macro for checking OpenSSL and syncs the minimum OpenSSL version (currently 1.0.2 or later) across the PHP build system. --- NEWS | 1 + ext/curl/config.m4 | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 6867a3dbcaeb8..5eb1694b5e816 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ PHP NEWS - Curl: . Fix memory leak when setting a list via curl_setopt fails. (nielsdos) + . Fix incorrect OpenSSL version detection. (Peter Kokot) - Date: . Fix leaks with multiple calls to DatePeriod iterator current(). (nielsdos) diff --git a/ext/curl/config.m4 b/ext/curl/config.m4 index 3b11739654bd6..c0325f990ad11 100644 --- a/ext/curl/config.m4 +++ b/ext/curl/config.m4 @@ -28,6 +28,7 @@ if test "$PHP_CURL" != "no"; then AC_MSG_CHECKING([for libcurl linked against old openssl]) AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include #include #include @@ -39,9 +40,18 @@ int main(int argc, char *argv[]) const char *ptr = data->ssl_version; while(*ptr == ' ') ++ptr; - if (strncasecmp(ptr, "OpenSSL/1.1", sizeof("OpenSSL/1.1")-1) == 0) { - /* New OpenSSL version */ - return 3; + int major, minor; + if (sscanf(ptr, "OpenSSL/%d", &major) == 1) { + if (major >= 3) { + /* OpenSSL version 3 or later */ + return 4; + } + } + if (sscanf(ptr, "OpenSSL/%d.%d", &major, &minor) == 2) { + if (major > 1 || (major == 1 && minor >= 1)) { + /* OpenSSL version 1.1 or later */ + return 3; + } } if (strncasecmp(ptr, "OpenSSL", sizeof("OpenSSL")-1) == 0) { /* Old OpenSSL version */ @@ -56,11 +66,7 @@ int main(int argc, char *argv[]) ]])],[ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_CURL_OLD_OPENSSL], [1], [Have cURL with old OpenSSL]) - PKG_CHECK_MODULES([OPENSSL], [openssl], [ - PHP_EVAL_LIBLINE($OPENSSL_LIBS, CURL_SHARED_LIBADD) - PHP_EVAL_INCLINE($OPENSSL_CFLAGS) - AC_CHECK_HEADERS([openssl/crypto.h]) - ], []) + PHP_SETUP_OPENSSL(CURL_SHARED_LIBADD,[AC_CHECK_HEADERS([openssl/crypto.h])],[]) ], [ AC_MSG_RESULT([no]) ], [ From 1044558b6484d21873039cad04961013f49d08b7 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 7 Jun 2025 13:31:55 +0100 Subject: [PATCH 42/54] ext/pdo_sqlite: createCollation memory leaks fix. coming from callback arguments when its return type is incorrect. close GH-18796 --- NEWS | 4 ++++ ext/pdo_sqlite/pdo_sqlite.c | 6 ++--- ...sqlite_createcollation_wrong_callback.phpt | 24 +++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt diff --git a/NEWS b/NEWS index 4c70733379d5c..8f4ae644a9299 100644 --- a/NEWS +++ b/NEWS @@ -37,6 +37,10 @@ PHP NEWS . Fixed bug #74796 (Requests through http proxy set peer name). (Jakub Zelenka) +- PDO Sqlite: + . Fixed memory leak with Pdo_Sqlite::createCollation when the callback + has an incorrect return type. (David Carlier) + - Phar: . Add missing filter cleanups on phar failure. (nielsdos) . Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos) diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index ff56d04049424..493ba3f36009d 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -346,6 +346,9 @@ static int php_sqlite_collation_callback(void *context, int string1_len, const v zend_call_known_fcc(&collation->callback, &retval, /* argc */ 2, zargs, /* named_params */ NULL); + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + if (!Z_ISUNDEF(retval)) { if (Z_TYPE(retval) != IS_LONG) { zend_string *func_name = get_active_function_or_method_name(); @@ -362,9 +365,6 @@ static int php_sqlite_collation_callback(void *context, int string1_len, const v } } - zval_ptr_dtor(&zargs[0]); - zval_ptr_dtor(&zargs[1]); - return ret; } diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt new file mode 100644 index 0000000000000..a9d17bb230d56 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt @@ -0,0 +1,24 @@ +--TEST-- +Pdo\Sqlite::createCollation() memory leaks on wrong callback return type +--EXTENSIONS-- +pdo_sqlite +--FILE-- +exec("CREATE TABLE test (c string)"); +$db->exec("INSERT INTO test VALUES('youwontseeme')"); +$db->exec("INSERT INTO test VALUES('neither')"); +$db->createCollation('NAT', function($a, $b): string { return $a . $b; }); + +try { + $db->query("SELECT c FROM test ORDER BY c COLLATE NAT"); +} catch (\TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +PDO::query(): Return value of the callback must be of type int, string returned From 9a9d98e02fe19c0f0df5aca9da50da5f74446515 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 4 Jun 2025 12:09:37 +0200 Subject: [PATCH 43/54] Do not delete main chunk in zend_gc Closes GH-18756. Co-authored-by: Arnaud Le Blanc --- NEWS | 1 + Zend/tests/gh18756.phpt | 13 +++++++++++++ Zend/zend_alloc.c | 2 +- ext/zend_test/test.c | 10 ++++++++++ ext/zend_test/test.stub.php | 2 ++ ext/zend_test/test_arginfo.h | 6 +++++- 6 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/gh18756.phpt diff --git a/NEWS b/NEWS index 5eb1694b5e816..4db7d93ff9808 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ PHP NEWS - Core: . Fixed GH-18695 (zend_ast_export() - float number is not preserved). (Oleg Efimov) + . Do not delete main chunk in zend_gc. (danog, Arnaud) - Curl: . Fix memory leak when setting a list via curl_setopt fails. (nielsdos) diff --git a/Zend/tests/gh18756.phpt b/Zend/tests/gh18756.phpt new file mode 100644 index 0000000000000..6e112d9060499 --- /dev/null +++ b/Zend/tests/gh18756.phpt @@ -0,0 +1,13 @@ +--TEST-- +Bug GH-18756: Zend MM may delete the main chunk +--EXTENSIONS-- +zend_test +--FILE-- + +==DONE== +--EXPECT-- +==DONE== diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 573fd5fa26b80..2f80bdae3cfbd 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2047,7 +2047,7 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) i++; } } - if (chunk->free_pages == ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE) { + if (chunk->free_pages == ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE && chunk != heap->main_chunk) { zend_mm_chunk *next_chunk = chunk->next; zend_mm_delete_chunk(heap, chunk); diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index a7dd604d89ef3..04ece8bd2537e 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -1516,3 +1516,13 @@ static PHP_FUNCTION(zend_test_create_throwing_resource) zend_resource *res = zend_register_resource(NULL, le_throwing_resource); ZVAL_RES(return_value, res); } + +static PHP_FUNCTION(zend_test_gh18756) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_mm_heap *heap = zend_mm_startup(); + zend_mm_gc(heap); + zend_mm_gc(heap); + zend_mm_shutdown(heap, true, false); +} diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index c9477eef52712..f9cb93b5a1ccb 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -262,6 +262,8 @@ function zend_test_cast_fread($stream): void {} function zend_test_is_zend_ptr(int $addr): bool {} function zend_test_log_err_debug(string $str): void {} + + function zend_test_gh18756(): void {} } namespace ZendTestNS { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 5947a6587bbed..c7e3df5c58d24 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: 9ddaf4d659226c55d49221c71702fa373d42695e */ + * Stub hash: 2f161861ab09b6b5b594dc2db7c2c9df49d76aa7 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -162,6 +162,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_log_err_debug, 0, 1, I ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) ZEND_END_ARG_INFO() +#define arginfo_zend_test_gh18756 arginfo_zend_test_void_return + #define arginfo_ZendTestNS2_namespaced_func arginfo_zend_test_is_pcre_bundled #define arginfo_ZendTestNS2_namespaced_deprecated_func arginfo_zend_test_void_return @@ -292,6 +294,7 @@ static ZEND_FUNCTION(zend_test_set_fmode); static ZEND_FUNCTION(zend_test_cast_fread); static ZEND_FUNCTION(zend_test_is_zend_ptr); static ZEND_FUNCTION(zend_test_log_err_debug); +static ZEND_FUNCTION(zend_test_gh18756); static ZEND_FUNCTION(ZendTestNS2_namespaced_func); static ZEND_FUNCTION(ZendTestNS2_namespaced_deprecated_func); static ZEND_FUNCTION(ZendTestNS2_ZendSubNS_namespaced_func); @@ -372,6 +375,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_test_cast_fread, arginfo_zend_test_cast_fread) ZEND_FE(zend_test_is_zend_ptr, arginfo_zend_test_is_zend_ptr) ZEND_FE(zend_test_log_err_debug, arginfo_zend_test_log_err_debug) + ZEND_FE(zend_test_gh18756, arginfo_zend_test_gh18756) 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 ef92e06de19b51f5655b80ab41e5abd849ca6ffa Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 7 Jun 2025 00:33:34 +0200 Subject: [PATCH 44/54] Fix memory leak on php_odbc_fetch_hash() failure The array is initialized but not freed. Closes GH-18787. --- NEWS | 3 +++ ext/odbc/php_odbc.c | 1 + 2 files changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 4db7d93ff9808..2956cb8c6af0f 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,9 @@ PHP NEWS . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) . Fix memory leak in locale lookup on failure. (nielsdos) +- ODBC: + . Fix memory leak on php_odbc_fetch_hash() failure. (nielsdos) + - OpenSSL: . Fix memory leak of X509_STORE in php_openssl_setup_verify() on failure. (nielsdos) diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 579b5e989bd3a..77ba85fe12ae8 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -1370,6 +1370,7 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) if (rc == SQL_ERROR) { odbc_sql_error(result->conn_ptr, result->stmt, "SQLGetData"); efree(buf); + zval_ptr_dtor(return_value); RETURN_FALSE; } From 786090b35d2c339912c76588dacf32698f2e2d31 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 7 Jun 2025 00:36:08 +0200 Subject: [PATCH 45/54] pdo_odbc: Fix memory leak if WideCharToMultiByte() fails Closes GH-18788. --- NEWS | 3 +++ ext/pdo_odbc/odbc_stmt.c | 1 + 2 files changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 2956cb8c6af0f..2208cd6e59aad 100644 --- a/NEWS +++ b/NEWS @@ -34,6 +34,9 @@ PHP NEWS . Add missing filter cleanups on phar failure. (nielsdos) . Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos) +- PDO ODBC: + . Fix memory leak if WideCharToMultiByte() fails. (nielsdos) + - PGSQL: . Fix warning not being emitted when failure to cancel a query with pg_cancel_query(). (Girgias) diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c index 4bf7162ea06e6..9f7ab24f8fadc 100644 --- a/ext/pdo_odbc/odbc_stmt.c +++ b/ext/pdo_odbc/odbc_stmt.c @@ -104,6 +104,7 @@ static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, zval *result) zend_string *str = zend_string_alloc(ret, 0); ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result)/sizeof(WCHAR), ZSTR_VAL(str), ZSTR_LEN(str), NULL, NULL); if (ret == 0) { + zend_string_efree(str); return PDO_ODBC_CONV_FAIL; } From b3c8afe272a6919248986c703c2e1defc73ff707 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 5 Jun 2025 19:37:46 +0200 Subject: [PATCH 46/54] Fix GH-18743: Incompatibility in Inline TLS Assembly on Alpine 3.22 GAS started checking the relocation for tlsgd: it must use the %rdi register. However, the inline assembly now uses %rax instead. Fix it by changing the "=a" output register to "=D". Source: https://github.com/bminor/binutils-gdb/blob/ec181e1710e37007a8d95c284609bfaa5868d086/gas/config/tc-i386.c#L6793 gottpoff is unaffected. Closes GH-18779. --- NEWS | 4 ++++ ext/opcache/jit/zend_jit_x86.dasc | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 2208cd6e59aad..bf705c1da0b4d 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,10 @@ PHP NEWS - ODBC: . Fix memory leak on php_odbc_fetch_hash() failure. (nielsdos) +- Opcache: + . Fixed bug GH-18743 (Incompatibility in Inline TLS Assembly on Alpine 3.22). + (nielsdos, Arnaud) + - OpenSSL: . Fix memory leak of X509_STORE in php_openssl_setup_verify() on failure. (nielsdos) diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 1f1abb59a1c24..7061f6b2b73ad 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -2910,7 +2910,7 @@ static int zend_jit_setup(void) __asm__( "leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n" - : "=a" (ti)); + : "=D" (ti)); tsrm_tls_offset = ti[1]; tsrm_tls_index = ti[0] * 8; #elif defined(__FreeBSD__) @@ -2918,7 +2918,7 @@ static int zend_jit_setup(void) __asm__( "leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n" - : "=a" (ti)); + : "=D" (ti)); tsrm_tls_offset = ti[1]; /* Index is offset by 1 on FreeBSD (https://github.com/freebsd/freebsd-src/blob/bf56e8b9c8639ac4447d223b83cdc128107cc3cd/libexec/rtld-elf/rtld.c#L5260) */ tsrm_tls_index = (ti[0] + 1) * 8; @@ -2927,7 +2927,7 @@ static int zend_jit_setup(void) __asm__( "leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n" - : "=a" (ti)); + : "=D" (ti)); tsrm_tls_offset = ti[1]; tsrm_tls_index = ti[0] * 16; #endif From 186a8116beaf1148911032016e539e0ed52524f0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 12:16:57 +0200 Subject: [PATCH 47/54] Fix test conflict between copy_variation2-win32-mb.phpt and copy_variation2-win32.phpt Closes GH-18809. --- .../tests/file/copy_variation2-win32-mb.phpt | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/ext/standard/tests/file/copy_variation2-win32-mb.phpt b/ext/standard/tests/file/copy_variation2-win32-mb.phpt index 4251a24e54cf7..67d84ee3e32c1 100644 --- a/ext/standard/tests/file/copy_variation2-win32-mb.phpt +++ b/ext/standard/tests/file/copy_variation2-win32-mb.phpt @@ -22,24 +22,24 @@ fclose($file_handle); $dest_files = array( /* File names containing special(non-alpha numeric) characters */ - "_copy_variation2.tmp", - "@copy_variation2.tmp", - "#copy_variation2.tmp", - "+copy_variation2.tmp", - "?copy_variation2.tmp", - ">copy_variation2.tmp", - "!copy_variation2.tmp", - "©_variation2.tmp", - "(copy_variation2.tmp", - ":copy_variation2.tmp", - ";copy_variation2.tmp", - "=copy_variation2.tmp", - "[copy_variation2.tmp", - "^copy_variation2.tmp", - "{copy_variation2.tmp", - "|copy_variation2.tmp", - "~copy_variation2.tmp", - "\$copy_variation2.tmp" + "_copy_variation2_mb.tmp", + "@copy_variation2_mb.tmp", + "#copy_variation2_mb.tmp", + "+copy_variation2_mb.tmp", + "?copy_variation2_mb.tmp", + ">copy_variation2_mb.tmp", + "!copy_variation2_mb.tmp", + "©_variation2_mb.tmp", + "(copy_variation2_mb.tmp", + ":copy_variation2_mb.tmp", + ";copy_variation2_mb.tmp", + "=copy_variation2_mb.tmp", + "[copy_variation2_mb.tmp", + "^copy_variation2_mb.tmp", + "{copy_variation2_mb.tmp", + "|copy_variation2_mb.tmp", + "~copy_variation2_mb.tmp", + "\$copy_variation2_mb.tmp" ); echo "Size of the source file before copy operation => "; @@ -90,28 +90,28 @@ Size of the source file before copy operation => int(1500) -- Iteration 1 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/_copy_variation2.tmp +Destination file name => %s/_copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 2 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/@copy_variation2.tmp +Destination file name => %s/@copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 3 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/#copy_variation2.tmp +Destination file name => %s/#copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 4 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/+copy_variation2.tmp +Destination file name => %s/+copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) @@ -130,21 +130,21 @@ Existence of destination file => bool(false) -- Iteration 7 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/!copy_variation2.tmp +Destination file name => %s/!copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 8 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/©_variation2.tmp +Destination file name => %s/©_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 9 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/(copy_variation2.tmp +Destination file name => %s/(copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) @@ -157,35 +157,35 @@ Existence of destination file => bool(false) -- Iteration 11 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/;copy_variation2.tmp +Destination file name => %s/;copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 12 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/=copy_variation2.tmp +Destination file name => %s/=copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 13 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/[copy_variation2.tmp +Destination file name => %s/[copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 14 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/^copy_variation2.tmp +Destination file name => %s/^copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 15 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/{copy_variation2.tmp +Destination file name => %s/{copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) @@ -198,14 +198,14 @@ Existence of destination file => bool(false) -- Iteration 17 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/~copy_variation2.tmp +Destination file name => %s/~copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 18 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/$copy_variation2.tmp +Destination file name => %s/$copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) *** Done *** From d11f9717fdb10bccc4e17bd20508fb7b6d5c9359 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 16:34:22 +0200 Subject: [PATCH 48/54] zend_alloc: Fix compile with ZEND_MM_STAT=0 Closes GH-18811. --- NEWS | 1 + Zend/zend_alloc.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/NEWS b/NEWS index bf705c1da0b4d..11fb787662c4a 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ PHP NEWS . Fixed GH-18695 (zend_ast_export() - float number is not preserved). (Oleg Efimov) . Do not delete main chunk in zend_gc. (danog, Arnaud) + . Fix compile issues with zend_alloc and some non-default options. (nielsdos) - Curl: . Fix memory leak when setting a list via curl_setopt fails. (nielsdos) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 2f80bdae3cfbd..47e9967a1e29f 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2274,7 +2274,9 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) /* Make sure the heap free below does not use tracked_free(). */ heap->custom_heap.std._free = free; } +#if ZEND_MM_STAT heap->size = 0; +#endif } if (full) { @@ -2820,6 +2822,7 @@ static zend_always_inline zval *tracked_get_size_zv(zend_mm_heap *heap, void *pt } static zend_always_inline void tracked_check_limit(zend_mm_heap *heap, size_t add_size) { +#if ZEND_MM_STAT if (add_size > heap->limit - heap->size && !heap->overflow) { #if ZEND_DEBUG zend_mm_safe_error(heap, @@ -2831,6 +2834,7 @@ static zend_always_inline void tracked_check_limit(zend_mm_heap *heap, size_t ad heap->limit, add_size); #endif } +#endif } static void *tracked_malloc(size_t size) @@ -2844,7 +2848,9 @@ static void *tracked_malloc(size_t size) } tracked_add(heap, ptr, size); +#if ZEND_MM_STAT heap->size += size; +#endif return ptr; } @@ -2855,7 +2861,9 @@ static void tracked_free(void *ptr) { zend_mm_heap *heap = AG(mm_heap); zval *size_zv = tracked_get_size_zv(heap, ptr); +#if ZEND_MM_STAT heap->size -= Z_LVAL_P(size_zv); +#endif zend_hash_del_bucket(heap->tracked_allocs, (Bucket *) size_zv); free(ptr); } @@ -2880,7 +2888,9 @@ static void *tracked_realloc(void *ptr, size_t new_size) { ptr = __zend_realloc(ptr, new_size); tracked_add(heap, ptr, new_size); +#if ZEND_MM_STAT heap->size += new_size - old_size; +#endif return ptr; } From fe3bea090e598cc7d543dc6086c7a65f6e6787f1 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 21:17:33 +0200 Subject: [PATCH 49/54] Fix technically incorrect sizeof This doesn't actually matter because both `*sal` and `**sal` are pointer sized, but this makes analysers happy. Fixes bug #68866. Closes GH-18816. --- main/network.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/network.c b/main/network.c index d4938a4a08c1e..8de81a6271a2f 100644 --- a/main/network.c +++ b/main/network.c @@ -227,7 +227,7 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka for (n = 1; (sai = sai->ai_next) != NULL; n++) ; - *sal = safe_emalloc((n + 1), sizeof(*sal), 0); + *sal = safe_emalloc((n + 1), sizeof(**sal), 0); sai = res; sap = *sal; @@ -266,7 +266,7 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka in = *((struct in_addr *) host_info->h_addr); } - *sal = safe_emalloc(2, sizeof(*sal), 0); + *sal = safe_emalloc(2, sizeof(**sal), 0); sap = *sal; *sap = emalloc(sizeof(struct sockaddr_in)); (*sap)->sa_family = AF_INET; From 594221fff2d635528c58bd6273e923e56ff53c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 10 Jun 2025 19:15:35 +0200 Subject: [PATCH 50/54] cli: Fix tests/bug80092.phpt expectation for `PHP_BUILD_PROVIDER` (#18824) see afc5738154b8e0e7f8bcb5d6a521514bb495a0c0 see 40d88cacc1db11787aa2fde6d0ee4b6064746d94 --- sapi/cli/tests/bug80092.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sapi/cli/tests/bug80092.phpt b/sapi/cli/tests/bug80092.phpt index 350b46b3f57a6..1fb2e8664cc1e 100644 --- a/sapi/cli/tests/bug80092.phpt +++ b/sapi/cli/tests/bug80092.phpt @@ -43,5 +43,5 @@ foreach (explode("\n", $output) as $line) { preloaded PHP %s Copyright (c) The PHP Group -Zend Engine %s +%AZend Engine %s %A with Zend OPcache %a From 0cd3ebfc40c6f4aa90fad8bab05b09f274465385 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 17:49:27 +0200 Subject: [PATCH 51/54] Fix 'phpdbg --help' segfault on shutdown with USE_ZEND_ALLOC=0 This hack not only breaks the handling of custom allocators, but also breaks if zend_alloc is compiled with USE_CUSTOM_MM. This hack is just no good, if you want leak information then use ASAN. Closes GH-18813. --- NEWS | 3 +++ sapi/phpdbg/phpdbg.c | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 11fb787662c4a..a665633283d0c 100644 --- a/NEWS +++ b/NEWS @@ -39,6 +39,9 @@ PHP NEWS . Add missing filter cleanups on phar failure. (nielsdos) . Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos) +- PHPDBG: + . Fix 'phpdbg --help' segfault on shutdown with USE_ZEND_ALLOC=0. (nielsdos) + - PDO ODBC: . Fix memory leak if WideCharToMultiByte() fails. (nielsdos) diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index 5a4dd6acdbe2f..f27d87de84187 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -179,12 +179,6 @@ static PHP_MSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ phpdbg_notice("Script ended normally"); } - /* hack to restore mm_heap->use_custom_heap in order to receive memory leak info */ - if (use_mm_wrappers) { - /* ASSUMING that mm_heap->use_custom_heap is the first element of the struct ... */ - *(int *) zend_mm_get_heap() = 0; - } - if (PHPDBG_G(buffer)) { free(PHPDBG_G(buffer)); PHPDBG_G(buffer) = NULL; From afb1c574700e035ba20a3b8ddd203471a31ba6c6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 17:52:21 +0200 Subject: [PATCH 52/54] Fix GH-14551: PGO build fails with xxhash Turns out that the instrumentation added for gcov can change inlining decisions of the compiler, which results in a mismatch between the profile data CFG and the actual generated CFG between compiles. There are two functions that suffer from this issue: 1. _PHP_XXH3_Init: Removing the inline hint fixes this one. In fact, always inlining this makes no sense as there's no real opportunity for specialising. It just bloats the binary and increases I$ pressure. So besides fixing this issue it's beneficial on its own to drop the attribute. 2. PHP_XXH3_128_Final: Sometimes XXH128_canonicalFromHash gets inlined and sometimes not. Make sure it gets always inlined. Closes GH-18814. --- NEWS | 3 +++ ext/hash/hash_xxhash.c | 2 +- ext/hash/xxhash/xxhash.h | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index a665633283d0c..d32c60625ed23 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,9 @@ PHP NEWS - FPM: . Fixed GH-18662 (fpm_get_status segfault). (txuna) +- Hash: + . Fixed bug GH-14551 (PGO build fails with xxhash). (nielsdos) + - Intl: . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) . Fix memory leak in locale lookup on failure. (nielsdos) diff --git a/ext/hash/hash_xxhash.c b/ext/hash/hash_xxhash.c index 24da754d8835a..070bd06bff070 100644 --- a/ext/hash/hash_xxhash.c +++ b/ext/hash/hash_xxhash.c @@ -154,7 +154,7 @@ const php_hash_ops php_hash_xxh3_64_ops = { typedef XXH_errorcode (*xxh3_reset_with_secret_func_t)(XXH3_state_t*, const void*, size_t); typedef XXH_errorcode (*xxh3_reset_with_seed_func_t)(XXH3_state_t*, XXH64_hash_t); -zend_always_inline static void _PHP_XXH3_Init(PHP_XXH3_64_CTX *ctx, HashTable *args, +static void _PHP_XXH3_Init(PHP_XXH3_64_CTX *ctx, HashTable *args, xxh3_reset_with_seed_func_t func_init_seed, xxh3_reset_with_secret_func_t func_init_secret, const char* algo_name) { memset(&ctx->s, 0, sizeof ctx->s); diff --git a/ext/hash/xxhash/xxhash.h b/ext/hash/xxhash/xxhash.h index 8e816c0584ebd..5874c9a1f97b5 100644 --- a/ext/hash/xxhash/xxhash.h +++ b/ext/hash/xxhash/xxhash.h @@ -931,7 +931,7 @@ XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2); /******* Canonical representation *******/ typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t; -XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash); +static zend_always_inline void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash); XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src); @@ -5503,7 +5503,7 @@ XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2) /*====== Canonical representation ======*/ /*! @ingroup xxh3_family */ -XXH_PUBLIC_API void +static zend_always_inline void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); From b4fe4e82fb341f8b1befe63ef608acd87584fcbf Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Tue, 1 Jul 2025 23:14:58 +0900 Subject: [PATCH 53/54] Update NEWS for PHP 8.4.9 --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index c8a8197d1417e..ce2e4dc6c5a90 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.4.9 +03 Jul 2025, PHP 8.4.9 - BcMath: . Fixed bug GH-18641 (Accessing a BcMath\Number property by ref crashes). From 3683f1cd7c9aa18d0f75ade9c25d93ae200188fc Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Tue, 1 Jul 2025 23:15:04 +0900 Subject: [PATCH 54/54] Update versions for PHP 8.4.9 --- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/zend.h b/Zend/zend.h index 34a6a0258a261..fa31986334a4c 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.4.9-dev" +#define ZEND_VERSION "4.4.9" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 3662d3e985b02..3394bccfd848a 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.4.9-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.4.9],[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 7bd5e8c37f895..2a0421bae2ec3 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -3,6 +3,6 @@ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 4 #define PHP_RELEASE_VERSION 9 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.4.9-dev" +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "8.4.9" #define PHP_VERSION_ID 80409