From:             
Operating system: Mac OS X 10.6.4
PHP version:      5.3.3
Package:          Scripting Engine problem
Bug Type:         Bug
Bug description:Objects unreferenced in __get, __set, __isset or __unset can be 
freed too early

Description:
------------
When a __set(), __get(), __isset() or __unset() method is called on an
object accessed through a reference, the refcount can fall to zero (causing
the object to be destroyed) before the mechanism to avoid recursion has
finished with the object and memory corruption results. These situations
are rare, but possible and useful.



More specifically, with respect to __set(). Consider this php code:



class MyClass {

        private $myRef;

        public function __construct(&$myReferent) {

                $this->myRef=&$myReferent;

        }

        public function __set($property,$value) {

                $this->myRef=null;

        }

}

$myGlobal=new MyClass($myGlobal);

$myGlobal->myNonExistentProperty="triggers __set";



After the object is constructed, $myGlobal and $myGlobal->myRef are both
references to the same variable (and thus both refer to the object). When
__set() is called, myRef, and thus myGlobal, is changed. As the method
exits, $this also goes out of scope. There are no references remaining to
the object, so it is destroyed.



However, this happens slightly too soon in the Zend engine. In
zend_std_write_property, `object' is $myGlobal through which the object is
accessed. Since this is a reference, the call to zend_std_call_setter
results in `object' being changed to a null zval, and the object being
freed, but `guard' is then accessed, which was part of the now-freed
object.



The (a?) solution is to separate `object' so that it is not changed by
zend_std_call_setter, and we therefore retain a reference to the object
which prevents it from being freed until we have finished with the guard.
It is freed when zval_ptr_dtor is called on `object'.



Patch attached. I have debugged and tested fairly carefully for the __set()
case, which was what led me to find the bug. The problem exists in this
case and is fixed by the patch. Larger test cases I have which exhibited
heap corruption and segfaults or bus errors as side-effects of the bug are
also fixed by the patch. I have neither confirmed the same problem exists,
nor that the patch fixes it, for the __get(), isset() and unset() cases,
but see no reason they would be any different. The test suite runs just as
well with the patch as without.



If it helps, I found the easiest way to explore this is to set
ZEND_DEBUG_OBJECTS to 1 and DEBUG_ZEND to 2, break on zend_std_call_setter,
step up a stack frame to zend_std_write_property and print *object, etc.,
step back down again, finish, so you will now be back in
zend_std_write_property but after the call to zend_std_call_setter has
completed, you can see the object deallocation on the console, and can
again examine *object, etc..




-- 
Edit bug report at http://bugs.php.net/bug.php?id=52879&edit=1
-- 
Try a snapshot (PHP 5.2):            
http://bugs.php.net/fix.php?id=52879&r=trysnapshot52
Try a snapshot (PHP 5.3):            
http://bugs.php.net/fix.php?id=52879&r=trysnapshot53
Try a snapshot (trunk):              
http://bugs.php.net/fix.php?id=52879&r=trysnapshottrunk
Fixed in SVN:                        
http://bugs.php.net/fix.php?id=52879&r=fixed
Fixed in SVN and need be documented: 
http://bugs.php.net/fix.php?id=52879&r=needdocs
Fixed in release:                    
http://bugs.php.net/fix.php?id=52879&r=alreadyfixed
Need backtrace:                      
http://bugs.php.net/fix.php?id=52879&r=needtrace
Need Reproduce Script:               
http://bugs.php.net/fix.php?id=52879&r=needscript
Try newer version:                   
http://bugs.php.net/fix.php?id=52879&r=oldversion
Not developer issue:                 
http://bugs.php.net/fix.php?id=52879&r=support
Expected behavior:                   
http://bugs.php.net/fix.php?id=52879&r=notwrong
Not enough info:                     
http://bugs.php.net/fix.php?id=52879&r=notenoughinfo
Submitted twice:                     
http://bugs.php.net/fix.php?id=52879&r=submittedtwice
register_globals:                    
http://bugs.php.net/fix.php?id=52879&r=globals
PHP 4 support discontinued:          http://bugs.php.net/fix.php?id=52879&r=php4
Daylight Savings:                    http://bugs.php.net/fix.php?id=52879&r=dst
IIS Stability:                       
http://bugs.php.net/fix.php?id=52879&r=isapi
Install GNU Sed:                     
http://bugs.php.net/fix.php?id=52879&r=gnused
Floating point limitations:          
http://bugs.php.net/fix.php?id=52879&r=float
No Zend Extensions:                  
http://bugs.php.net/fix.php?id=52879&r=nozend
MySQL Configuration Error:           
http://bugs.php.net/fix.php?id=52879&r=mysqlcfg

Reply via email to