Merge branch 'PHP-8.4'

* PHP-8.4:
  Fix GH-19303: Unpacking empty packed array into uninitialized array causes assertion failure
This commit is contained in:
Niels Dossche 2025-07-30 22:49:08 +02:00
commit a5219c1ecc
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
3 changed files with 43 additions and 22 deletions

View file

@ -0,0 +1,11 @@
--TEST--
GH-19303 (Unpacking empty packed array into uninitialized array causes assertion failure)
--FILE--
<?php
$a = [0];
unset($a[0]);
var_dump([...$a]);
?>
--EXPECT--
array(0) {
}

View file

@ -6325,17 +6325,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;

27
Zend/zend_vm_execute.h generated
View file

@ -2819,17 +2819,22 @@ 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;