|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
[2020-08-05 12:04 UTC] imbolk at gmail dot com
[2020-08-05 13:23 UTC] nikic@php.net
-Assigned To:
+Assigned To: nikic
[2020-08-05 13:44 UTC] nikic@php.net
[2020-08-05 13:44 UTC] nikic@php.net
-Status: Assigned
+Status: Closed
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Thu Nov 06 14:00:01 2025 UTC |
Description: ------------ When array_merge_recursive is called where the second array contains values which are PHP reference groups of size 1 to values that can be freed, it can free values prematurely. For example, the below snippet creates an array with a reference group of size 1 to a temporary mutable string 'a'. $b = ['value' => strtolower('A')]; array_walk($b, static function (){}); I noticed this when investigating https://github.com/igbinary/igbinary/issues/278 PHP 7.3 is also affected - it can produce invalid output or a segmentation fault Test script: --------------- <?php call_user_func(function() { // The strtolower is one way to create a value that isn't a constant literal generated // by the compiler. $b = [ 'value' => strtolower('A'), ]; array_walk($b, static function (){}); for ($i = 0; $i < 30; $i++) { echo "i=$i\n"; $m = array_merge_recursive(['value' => 'a'], $b); } var_dump($a, $b, $m); }); Expected result: ---------------- This produces valid output and does not crash Actual result: -------------- This crashes in NTS non-debug, prints memory leaks in NTS debug for 7.4.10-dev When running in valgrind with php 7.4.10-dev NTS debug with `USE_ZEND_ALLOC=0 valgrind php error.php`, I see ยป USE_ZEND_ALLOC=0 valgrind php error.php ==4839== Memcheck, a memory error detector ==4839== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. ==4839== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info ==4839== Command: php error.php ==4839== i=0 i=1 i=2 ==4839== Invalid read of size 4 ==4839== at 0xAE68B2: zend_gc_delref (zend_types.h:1039) ==4839== by 0xAE70A9: i_zval_ptr_dtor (zend_variables.h:43) ==4839== by 0xAEC3C4: zend_array_destroy (zend_hash.c:1611) ==4839== by 0xAD00C4: rc_dtor_func (zend_variables.c:57) ==4839== by 0xAE70B9: i_zval_ptr_dtor (zend_variables.h:44) ==4839== by 0xAEC3F6: zend_array_destroy (zend_hash.c:1615) ==4839== by 0xAD00C4: rc_dtor_func (zend_variables.c:57) ==4839== by 0xB383F6: zend_assign_to_variable (zend_execute.h:131) ==4839== by 0xBA36D1: ZEND_ASSIGN_SPEC_CV_VAR_RETVAL_UNUSED_HANDLER (zend_vm_execute.h:45260) ==4839== by 0xBB1684: execute_ex (zend_vm_execute.h:57440) ==4839== by 0xBB1D4B: zend_execute (zend_vm_execute.h:57856) ==4839== by 0xAD4770: zend_execute_scripts (zend.c:1677) ==4839== Address 0x11f85590 is 0 bytes inside a block of size 32 free'd ==4839== at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==4839== by 0xA99066: _efree_custom (zend_alloc.c:2426) ==4839== by 0xA991A7: _efree (zend_alloc.c:2546) ==4839== by 0xAD01C5: zend_string_destroy (zend_variables.c:67) ==4839== by 0xAD00C4: rc_dtor_func (zend_variables.c:57) ==4839== by 0xAE70B9: i_zval_ptr_dtor (zend_variables.h:44) ==4839== by 0xAEC3C4: zend_array_destroy (zend_hash.c:1611) ==4839== by 0xAD00C4: rc_dtor_func (zend_variables.c:57) ==4839== by 0xAE70B9: i_zval_ptr_dtor (zend_variables.h:44) ==4839== by 0xAEC3F6: zend_array_destroy (zend_hash.c:1615) ==4839== by 0xAD00C4: rc_dtor_func (zend_variables.c:57) ==4839== by 0xB383F6: zend_assign_to_variable (zend_execute.h:131) ==4839== Block was alloc'd at ==4839== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==4839== by 0xA9A0A7: __zend_malloc (zend_alloc.c:2976) ==4839== by 0xA98FFF: _malloc_custom (zend_alloc.c:2417) ==4839== by 0xA9912D: _emalloc (zend_alloc.c:2536) ==4839== by 0x922D61: zend_string_alloc (zend_string.h:133) ==4839== by 0x929DFE: php_string_tolower (string.c:1486) ==4839== by 0x92A146: zif_strtolower (string.c:1516) ==4839== by 0xB47BEC: ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:1314) ==4839== by 0xBADC0D: execute_ex (zend_vm_execute.h:53740) ==4839== by 0xBB1D4B: zend_execute (zend_vm_execute.h:57856) ==4839== by 0xAD4770: zend_execute_scripts (zend.c:1677) ==4839== by 0xA355BB: php_execute_script (main.c:2621) ==4839== php: /path/to/php-src/Zend/zend_types.h:1039: zend_gc_delref: Assertion `p->refcount > 0' failed. ==4839== ==4839== Process terminating with default action of signal 6 (SIGABRT) ==4839== at 0x9D49438: raise (raise.c:54) ==4839== by 0x9D4B039: abort (abort.c:89) ==4839== by 0x9D41BE6: __assert_fail_base (assert.c:92) ==4839== by 0x9D41C91: __assert_fail (assert.c:101) ==4839== by 0xAE68D6: zend_gc_delref (zend_types.h:1039) ==4839== by 0xAE70A9: i_zval_ptr_dtor (zend_variables.h:43) ==4839== by 0xAEC3C4: zend_array_destroy (zend_hash.c:1611) ==4839== by 0xAD00C4: rc_dtor_func (zend_variables.c:57) ==4839== by 0xAE70B9: i_zval_ptr_dtor (zend_variables.h:44) ==4839== by 0xAEC3F6: zend_array_destroy (zend_hash.c:1615) ==4839== by 0xAD00C4: rc_dtor_func (zend_variables.c:57) ==4839== by 0xB383F6: zend_assign_to_variable (zend_execute.h:131) ==4839== ==4839== HEAP SUMMARY: ==4839== in use at exit: 3,329,941 bytes in 24,367 blocks ==4839== total heap usage: 28,842 allocs, 4,475 frees, 4,574,768 bytes allocated ==4839== ==4839== LEAK SUMMARY: ==4839== definitely lost: 24 bytes in 1 blocks ==4839== indirectly lost: 0 bytes in 0 blocks ==4839== possibly lost: 2,389,714 bytes in 18,644 blocks ==4839== still reachable: 940,203 bytes in 5,722 blocks ==4839== suppressed: 0 bytes in 0 blocks ==4839== Rerun with --leak-check=full to see details of leaked memory ==4839== ==4839== For counts of detected and suppressed errors, rerun with: -v ==4839== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)