From 5bd5f352e5aeeda3154deffee21caa227e0ce48b Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 30 Jul 2025 18:44:31 +0200 Subject: [PATCH 1/4] Fix GH-19303: Unpacking empty packed array into uninitialized array causes assertion failure Having an empty result array is not a problem, because zend_hash_extend() will initialize it. Except it does not when the number of elements to add equals 0, which leaves the array uninitialized and therefore does not set the packed flag, causing the assertion failure. Technically, removing the assert would also work and save a check. On the other hand, this check could also prevent some real work to be done and should be relatively cheap as we already have to compute the sum anyway. Closes GH-19318. --- NEWS | 2 ++ Zend/tests/array_unpack/gh19303.phpt | 11 +++++++++++ Zend/zend_vm_def.h | 27 ++++++++++++++++----------- Zend/zend_vm_execute.h | 27 ++++++++++++++++----------- 4 files changed, 45 insertions(+), 22 deletions(-) create mode 100644 Zend/tests/array_unpack/gh19303.phpt diff --git a/NEWS b/NEWS index fb6a541bbf665..ced51075fab1e 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,8 @@ PHP NEWS binary const expr). (ilutov) . Fixed bug GH-19305 (Operands may be being released during comparison). (Arnaud) + . Fixed bug GH-19303 (Unpacking empty packed array into uninitialized array + causes assertion failure). (nielsdos) - FTP: . Fix theoretical issues with hrtime() not being available. (nielsdos) diff --git a/Zend/tests/array_unpack/gh19303.phpt b/Zend/tests/array_unpack/gh19303.phpt new file mode 100644 index 0000000000000..af594c3740c2d --- /dev/null +++ b/Zend/tests/array_unpack/gh19303.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-19303 (Unpacking empty packed array into uninitialized array causes assertion failure) +--FILE-- + +--EXPECT-- +array(0) { +} diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7c9f151134fe2..f1d4f7448ce1e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6162,17 +6162,22 @@ ZEND_VM_C_LABEL(add_unpack_again): zval *val; if (HT_IS_PACKED(ht) && (zend_hash_num_elements(result_ht) == 0 || HT_IS_PACKED(result_ht))) { - zend_hash_extend(result_ht, result_ht->nNumUsed + zend_hash_num_elements(ht), 1); - ZEND_HASH_FILL_PACKED(result_ht) { - ZEND_HASH_PACKED_FOREACH_VAL(ht, val) { - if (UNEXPECTED(Z_ISREF_P(val)) && - UNEXPECTED(Z_REFCOUNT_P(val) == 1)) { - val = Z_REFVAL_P(val); - } - Z_TRY_ADDREF_P(val); - ZEND_HASH_FILL_ADD(val); - } ZEND_HASH_FOREACH_END(); - } ZEND_HASH_FILL_END(); + /* zend_hash_extend() skips initialization when the number of elements is 0, + * but the code below expects that result_ht is initialized as packed. + * We can just skip the work in that case. */ + if (result_ht->nNumUsed + zend_hash_num_elements(ht) > 0) { + zend_hash_extend(result_ht, result_ht->nNumUsed + zend_hash_num_elements(ht), 1); + ZEND_HASH_FILL_PACKED(result_ht) { + ZEND_HASH_PACKED_FOREACH_VAL(ht, val) { + if (UNEXPECTED(Z_ISREF_P(val)) && + UNEXPECTED(Z_REFCOUNT_P(val) == 1)) { + val = Z_REFVAL_P(val); + } + Z_TRY_ADDREF_P(val); + ZEND_HASH_FILL_ADD(val); + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FILL_END(); + } } else { zend_string *key; diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 9daa00d97c418..5f7f4997ce8d7 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2657,17 +2657,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_UNPACK_SPEC_HANDLER( zval *val; if (HT_IS_PACKED(ht) && (zend_hash_num_elements(result_ht) == 0 || HT_IS_PACKED(result_ht))) { - zend_hash_extend(result_ht, result_ht->nNumUsed + zend_hash_num_elements(ht), 1); - ZEND_HASH_FILL_PACKED(result_ht) { - ZEND_HASH_PACKED_FOREACH_VAL(ht, val) { - if (UNEXPECTED(Z_ISREF_P(val)) && - UNEXPECTED(Z_REFCOUNT_P(val) == 1)) { - val = Z_REFVAL_P(val); - } - Z_TRY_ADDREF_P(val); - ZEND_HASH_FILL_ADD(val); - } ZEND_HASH_FOREACH_END(); - } ZEND_HASH_FILL_END(); + /* zend_hash_extend() skips initialization when the number of elements is 0, + * but the code below expects that result_ht is initialized as packed. + * We can just skip the work in that case. */ + if (result_ht->nNumUsed + zend_hash_num_elements(ht) > 0) { + zend_hash_extend(result_ht, result_ht->nNumUsed + zend_hash_num_elements(ht), 1); + ZEND_HASH_FILL_PACKED(result_ht) { + ZEND_HASH_PACKED_FOREACH_VAL(ht, val) { + if (UNEXPECTED(Z_ISREF_P(val)) && + UNEXPECTED(Z_REFCOUNT_P(val) == 1)) { + val = Z_REFVAL_P(val); + } + Z_TRY_ADDREF_P(val); + ZEND_HASH_FILL_ADD(val); + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FILL_END(); + } } else { zend_string *key; From 246e8e53ba57328087be9abf4fd9a006abc1cdda Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 30 Jul 2025 13:55:45 -0700 Subject: [PATCH 2/4] [ci skip] Update NEWS to reflect PHP 8.5.0 alpha 4 --- NEWS | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index 6cca730cd9e77..1171faacfcb9e 100644 --- a/NEWS +++ b/NEWS @@ -2,22 +2,15 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.5.0beta1 -- Core: - . Fixed bug GH-19305 (Operands may be being released during comparison). - (Arnaud) -- Intl: - . Fixed bug GH-19307 (PGO builds of shared ext-intl are broken). (cmb) - -- Opcache: - . Fixed bug GH-19301 (opcache build failure). (Remi) - -31 Jul 2025, PHP 8.5.0alpha3 +31 Jul 2025, PHP 8.5.0alpha4 - Core: . Add clone-with support to the clone() function. (timwolla, edorian) . Fix support for non-userland stream notifiers. (timwolla) . Added PHP_BUILD_PROVIDER constant. (timwolla) + . Fixed bug GH-19305 (Operands may be being released during comparison). + (Arnaud) - Curl: . Add support for CURLINFO_CONN_ID in curl_getinfo() (thecaliskan) @@ -32,6 +25,7 @@ PHP NEWS - Intl: . Fix return value on failure for resourcebundle count handler. (Girgias) + . Fixed bug GH-19307 (PGO builds of shared ext-intl are broken). (cmb) - OPcache: . Disallow changing opcache.memory_consumption when SHM is already set up. @@ -41,6 +35,7 @@ PHP NEWS . Make OPcache non-optional (Arnaud, timwolla) . Fixed bug GH-17422 (OPcache bypasses the user-defined error handler for deprecations). (Arnaud, timwolla) + . Fixed bug GH-19301 (opcache build failure). (Remi) - OpenSSL: . Add $digest_algo parameter to openssl_public_encrypt() and From 0fc62310b195533043b1e00c8faa1c86e5f31f25 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 30 Jul 2025 14:50:40 -0700 Subject: [PATCH 3/4] AllowDynamicProperties: use fully qualified name in validation errors (#19296) --- .../attributes/allow_dynamic_properties_on_enum.phpt | 2 +- .../attributes/allow_dynamic_properties_on_interface.phpt | 2 +- .../attributes/allow_dynamic_properties_on_trait.phpt | 2 +- Zend/tests/readonly_classes/gh10377_1.phpt | 2 +- .../readonly_class_dynamic_property_attribute.phpt | 2 +- Zend/zend_attributes.c | 8 ++++---- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Zend/tests/attributes/allow_dynamic_properties_on_enum.phpt b/Zend/tests/attributes/allow_dynamic_properties_on_enum.phpt index b9ab745be9ae7..f1ce8b055b0e0 100644 --- a/Zend/tests/attributes/allow_dynamic_properties_on_enum.phpt +++ b/Zend/tests/attributes/allow_dynamic_properties_on_enum.phpt @@ -8,4 +8,4 @@ enum Test {} ?> --EXPECTF-- -Fatal error: Cannot apply #[AllowDynamicProperties] to enum Test in %s on line %d +Fatal error: Cannot apply #[\AllowDynamicProperties] to enum Test in %s on line %d diff --git a/Zend/tests/attributes/allow_dynamic_properties_on_interface.phpt b/Zend/tests/attributes/allow_dynamic_properties_on_interface.phpt index 0428256a18e40..16440a385db14 100644 --- a/Zend/tests/attributes/allow_dynamic_properties_on_interface.phpt +++ b/Zend/tests/attributes/allow_dynamic_properties_on_interface.phpt @@ -8,4 +8,4 @@ interface Test {} ?> --EXPECTF-- -Fatal error: Cannot apply #[AllowDynamicProperties] to interface Test in %s on line %d +Fatal error: Cannot apply #[\AllowDynamicProperties] to interface Test in %s on line %d diff --git a/Zend/tests/attributes/allow_dynamic_properties_on_trait.phpt b/Zend/tests/attributes/allow_dynamic_properties_on_trait.phpt index 9636dc03141e5..bd18b3e71c8f1 100644 --- a/Zend/tests/attributes/allow_dynamic_properties_on_trait.phpt +++ b/Zend/tests/attributes/allow_dynamic_properties_on_trait.phpt @@ -8,4 +8,4 @@ trait Test {} ?> --EXPECTF-- -Fatal error: Cannot apply #[AllowDynamicProperties] to trait Test in %s on line %d +Fatal error: Cannot apply #[\AllowDynamicProperties] to trait Test in %s on line %d diff --git a/Zend/tests/readonly_classes/gh10377_1.phpt b/Zend/tests/readonly_classes/gh10377_1.phpt index ac421cb8a5171..fba1605ff0849 100644 --- a/Zend/tests/readonly_classes/gh10377_1.phpt +++ b/Zend/tests/readonly_classes/gh10377_1.phpt @@ -12,4 +12,4 @@ $readonly_anon = new #[AllowDynamicProperties] readonly class { ?> --EXPECTF-- -Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class class@anonymous in %s on line %d +Fatal error: Cannot apply #[\AllowDynamicProperties] to readonly class class@anonymous in %s on line %d diff --git a/Zend/tests/readonly_classes/readonly_class_dynamic_property_attribute.phpt b/Zend/tests/readonly_classes/readonly_class_dynamic_property_attribute.phpt index ace50c572aced..fab37dccfbf61 100644 --- a/Zend/tests/readonly_classes/readonly_class_dynamic_property_attribute.phpt +++ b/Zend/tests/readonly_classes/readonly_class_dynamic_property_attribute.phpt @@ -10,4 +10,4 @@ readonly class Foo ?> --EXPECTF-- -Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo in %s on line %d +Fatal error: Cannot apply #[\AllowDynamicProperties] to readonly class Foo in %s on line %d diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index c3633801be83e..01b16d7d205eb 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -73,22 +73,22 @@ static void validate_allow_dynamic_properties( zend_attribute *attr, uint32_t target, zend_class_entry *scope) { if (scope->ce_flags & ZEND_ACC_TRAIT) { - zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to trait %s", + zend_error_noreturn(E_ERROR, "Cannot apply #[\\AllowDynamicProperties] to trait %s", ZSTR_VAL(scope->name) ); } if (scope->ce_flags & ZEND_ACC_INTERFACE) { - zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to interface %s", + zend_error_noreturn(E_ERROR, "Cannot apply #[\\AllowDynamicProperties] to interface %s", ZSTR_VAL(scope->name) ); } if (scope->ce_flags & ZEND_ACC_READONLY_CLASS) { - zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to readonly class %s", + zend_error_noreturn(E_ERROR, "Cannot apply #[\\AllowDynamicProperties] to readonly class %s", ZSTR_VAL(scope->name) ); } if (scope->ce_flags & ZEND_ACC_ENUM) { - zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to enum %s", + zend_error_noreturn(E_ERROR, "Cannot apply #[\\AllowDynamicProperties] to enum %s", ZSTR_VAL(scope->name) ); } From e1cf66b6cbc1ef5b12daa4c3a052810c8a93f5fb Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 30 Jul 2025 22:51:28 +0100 Subject: [PATCH 4/4] Fix release process feature freeze info (#19148) --- docs/release-process.md | 42 ++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/docs/release-process.md b/docs/release-process.md index 41a24481389ed..08b95df53513f 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -878,25 +878,37 @@ If you choose to create a patch-level release, follow these steps: mailinglist. -## Feature freeze +## Soft feature freeze -A major/minor version [feature freeze][] occurs with the first beta release. -Specifically, it occurs when the first beta release is packaged, which means the -feature freeze occurs two days before the first beta release. +A major/minor version soft feature freeze occurs with the first beta release. +This is a soft feature freeze because features can still be merged with RM +approval. -The feature freeze for `php-src` means that we will not accept any new features -after the date of the feature freeze. For any RFCs to be included in the new -version, they should be discussed and have the voting polls closed no later than -the feature freeze date. However, this does not mean the new feature must have a -complete implementation by this date. - -Following the feature freeze, the focus of work for the new version will be on -fixing bugs, writing tests, and completing/polishing all accepted features. +For any RFCs to be included in the new release, they should be discussed and +have their voting polls closed no later than when the first beta is released. +However, this does not mean the new feature must have a complete implementation +by this date. Such implementation can be merged only with RM approval and must +be done before the hard feature freeze. As a courtesy to the community, the release managers should remind others about -the upcoming feature freeze by posting reminders to internals@lists.php.net at -4-weeks, 3-weeks, 2-weeks, and 1-week prior to the feature freeze. This is a -recommendation and the intervals may vary based on work load. +the upcoming soft feature freeze by posting reminders to +internals@lists.php.net at 5 weeks, 4 weeks, 3 weeks, 2 weeks, and 1 week prior +to this feature freeze. This is a recommendation and the intervals may vary +based on workload. The reminder should also contain a note with dates for +the last allowed RFC to start voting. + +## Hard feature freeze + +A major/minor version hard [feature freeze][] occurs with the first RC release. +Specifically, it occurs when the first RC release is packaged, which means the +hard feature freeze occurs two days before the first RC release. + +The hard feature freeze for php-src means that we will not accept any new +features after the date of the hard feature freeze. + +Following the hard feature freeze, the focus of work for the new version will +be on fixing bugs, writing tests, and preparing documentation for all accepted +features. ## Forking a new version branch