ID: 46222 Updated by: [EMAIL PROTECTED] Reported By: [EMAIL PROTECTED] -Status: Critical +Status: Closed Bug Type: Scripting Engine problem Operating System: Any PHP Version: 5.2.6 Assigned To: colder New Comment:
This bug has been fixed in CVS. Snapshots of the sources are packaged every three hours; this change will be in the next snapshot. You can grab the snapshot at http://snaps.php.net/. Thank you for the report, and for helping us make PHP better. Fixed by initializing zvals in dimension_get in case: 1) it doesn't exist yet 2) it is a write operation. Thanks for the suggestions/patches Previous Comments: ------------------------------------------------------------------------ [2008-10-05 06:05:19] mark at hell dot ne dot jp Adding the following code in spl_array.c near line 350 seems to fix the problem (however it's a bit dirty, I think). After line : ret = spl_array_get_dimension_ptr_ptr(check_inherited, object, offset, type TSRMLS_CC); Add : // Dirty test patch if ((type == BP_VAR_W || type == BP_VAR_RW) && (ret == &EG(uninitialized_zval_ptr))) { // write a NULL value in the missing part of the array spl_array_write_dimension_ex(check_inherited, object, offset, EG(uninitialized_zval_ptr) TSRMLS_CC); // And reload it ret = spl_array_get_dimension_ptr_ptr(check_inherited, object, offset, type TSRMLS_CC); } This fixes this bug, but I don't think this is the best way to fix it. I just make things how they should be at a later point in code, I don't know the Zend engine internals enough (and the documentation's empty skeleton isn't helping much) to make a better fix than that. ------------------------------------------------------------------------ [2008-10-05 05:38:58] mark at hell dot ne dot jp The patch by crrodriguez doesn't fix the main problem: If you print_r (or var_dump in my case) $test, it's empty. This fixes the overwritting of EG(uninitialized_zval_ptr), but not the behaviour problem. I suggest updating the reproduce code as following: Reproduce code: --------------- $test = new ArrayObject(); $test['d1']['d2'] = 'hello'; var_dump($test, $test3['mmmmm']); Expected result (php5.3): ------------------------- object(ArrayObject)#1 (1) { ["storage":"ArrayObject":private]=> array(1) { ["d1"]=> array(1) { ["d2"]=> string(5) "hello" } } } NULL Actual result (php5.3): ----------------------- object(ArrayObject)#1 (1) { ["storage":"ArrayObject":private]=> array(0) { } } array(1) { ["d2"]=> string(5) "hello" } Actual result with crrodriguez's patch: object(ArrayObject)#1 (1) { ["storage":"ArrayObject":private]=> array(0) { } } NULL ------------------------------------------------------------------------ [2008-10-05 03:46:30] crrodriguez at opensuse dot org Not sure, but this patch works for me Index: ext/spl/spl_array.c =================================================================== RCS file: /repository/php-src/ext/spl/spl_array.c,v retrieving revision 1.71.2.17.2.13.2.26 diff -u -p -r1.71.2.17.2.13.2.26 spl_array.c --- ext/spl/spl_array.c 29 Sep 2008 22:45:27 -0000 1.71.2.17.2.13.2.26 +++ ext/spl/spl_array.c 5 Oct 2008 03:44:42 -0000 @@ -346,7 +346,7 @@ static zval *spl_array_read_dimension_ex /* When in a write context, * ZE has to be fooled into thinking this is in a reference set * by separating (if necessary) and returning as an is_ref=1 zval (even if refcount == 1) */ - if ((type == BP_VAR_W || type == BP_VAR_RW) && !Z_ISREF_PP(ret)) { + if (ret != &EG(uninitialized_zval_ptr) && (type == BP_VAR_W || type == BP_VAR_RW) && !Z_ISREF_PP(ret)) { if (Z_REFCOUNT_PP(ret) > 1) { zval *newval; ------------------------------------------------------------------------ [2008-10-04 20:57:34] mark at hell dot ne dot jp Changing the previously mentionned code to add one line : if (Z_REFCOUNT_PP(ret) > 1) { zval *newval; /* Separate */ MAKE_STD_ZVAL(newval); *newval = **ret; zval_copy_ctor(newval); Z_SET_REFCOUNT_P(newval, 1); /* Replace */ Z_DELREF_PP(ret); ret = emalloc(sizeof(void*)); // Avoid overwritting stuff?? *ret = newval; } Seems to fix the initial problem (dumping anything else no longer breaks stuff; I didn't care about memleaks in this "fix", it's just to demonstrate that this part of code has problems), however I see something else coming: dumping $test in initial code shows that it stays empty even after assigning $test['d1']['d2']. I guess the real problem is probably something like the fact $test['d1'] didn't get created, so d2 gets lost in "outer space". ------------------------------------------------------------------------ [2008-10-04 20:29:34] mark at hell dot ne dot jp I searched a bit too and found that we overwrite the memory space for: EG(uninitialized_zval_ptr) In spl/spl_array.c near line 375 (probably wrong as I added lots of debug to find out where this came from). Basically this code : if (Z_REFCOUNT_PP(ret) > 1) { zval *newval; /* Separate */ MAKE_STD_ZVAL(newval); *newval = **ret; zval_copy_ctor(newval); Z_SET_REFCOUNT_P(newval, 1); /* Replace */ Z_DELREF_PP(ret); *ret = newval; } Will be run in some cases with ret == &EG(uninitialized_zval_ptr), which means writing to *ret is the same as writing to EG(uninitialized_zval_ptr). I checked how new (unexisting) array entries are managed in Zend and will try to replace some code here to see if it helps. ------------------------------------------------------------------------ The remainder of the comments for this report are too long. To view the rest of the comments, please view the bug report online at http://bugs.php.net/46222 -- Edit this bug report at http://bugs.php.net/?id=46222&edit=1