php / php-src

The PHP Interpreter
https://www.php.net
Other
38.23k stars 7.75k forks source link

Assertion failure at Zend/zend_hash.c:3076 #16792

Open YuanchengJiang opened 3 days ago

YuanchengJiang commented 3 days ago

Description

The following code:

<?php
set_error_handler(function() {
$GLOBALS['x'] = $GLOBALS['y'];
});
function x(&$s) {
$s[0] = 1;
};
x($y);
arsort($y, $fusion);

Resulted in this output:

/php-src/Zend/zend_hash.c:3076: void zend_array_sort_ex(HashTable *, sort_func_t, bucket_compare_func_t, _Bool): Assertion `(zend_gc_refcount(&(ht)->gc) == 1) || ((ht)->u.flags & (1<<6))' failed.
Aborted (core dumped)

PHP Version

nightly

Operating System

ubuntu 22.04

nielsdos commented 2 days ago

Oh this happens during ZPP parsing, the separation happens earlier than the Deprecated: arsort(): Passing null to parameter #2 ($flags) of type int is deprecated deprecation is emitted, so the function assumes the array is separated already while in reality the error handler caused the RC to be 2, which means it now needs to be separated while previously it did not.

nielsdos commented 2 days ago

This would work, and would need to be done for the other functions too, but it's a general issue when ZPP can indirectly invoke user code after the array should've been separated:

diff --git a/ext/standard/array.c b/ext/standard/array.c
index ba1424f677f..fea5086a1f3 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -757,11 +757,13 @@ PHP_FUNCTION(arsort)
    bucket_compare_func_t cmp;

    ZEND_PARSE_PARAMETERS_START(1, 2)
-       Z_PARAM_ARRAY_EX(array, 0, 1)
+       Z_PARAM_ARRAY_EX2(array, 0, 1, 0)
        Z_PARAM_OPTIONAL
        Z_PARAM_LONG(sort_type)
    ZEND_PARSE_PARAMETERS_END();

+   SEPARATE_ARRAY(array);
+
    cmp = php_get_data_compare_func(sort_type, 1);

    zend_array_sort(Z_ARRVAL_P(array), cmp, 0);
iluuu1994 commented 1 day ago

I'm not sure we want to go down that rabbit hole...

Passing null to parameter #2 ($flags) of type int is deprecated

Should go away in PHP 9.