Bug #79930 [NEW]: array_merge_recursive() crashes when called with array with single reference
| From: | tandre@php.net | Date: | Wed, 05 Aug 2020 00:22:29 +0000 |
| Subject: | Bug #79930 [NEW]: array_merge_recursive() crashes when called with array with single reference | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-228405@lists.php.net to get a copy of this message | ||
From: tandre
Operating system: Linux(any?)
PHP version: 7.4.9RC1
Package: Reproducible crash
Bug Type: Bug
Bug description:array_merge_recursive() crashes when called with array with single reference
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)
--
Edit bug report at https://bugs.php.net/bug.php?id=79930&edit=1
--
Fix committed: https://bugs.php.net/fix.php?id=79930&r=fixed
Fixed in release: https://bugs.php.net/fix.php?id=79930&r=alreadyfixed
Need backtrace: https://bugs.php.net/fix.php?id=79930&r=needtrace
Need Reproduce Script: https://bugs.php.net/fix.php?id=79930&r=needscript
Try newer version: https://bugs.php.net/fix.php?id=79930&r=oldversion
Not developer issue: https://bugs.php.net/fix.php?id=79930&r=support
Expected behavior: https://bugs.php.net/fix.php?id=79930&r=notwrong
Not enough info: https://bugs.php.net/fix.php?id=79930&r=notenoughinfo
Submitted twice: https://bugs.php.net/fix.php?id=79930&r=submittedtwice
register_globals: https://bugs.php.net/fix.php?id=79930&r=globals
PHP version support discontinued: https://bugs.php.net/fix.php?id=79930&r=phptooold
Daylight Savings: https://bugs.php.net/fix.php?id=79930&r=dst
IIS Stability: https://bugs.php.net/fix.php?id=79930&r=isapi
Install GNU Sed: https://bugs.php.net/fix.php?id=79930&r=gnused
Floating point limitations: https://bugs.php.net/fix.php?id=79930&r=float
No Zend Extensions: https://bugs.php.net/fix.php?id=79930&r=nozend
MySQL Configuration Error: https://bugs.php.net/fix.php?id=79930&r=mysqlcfg