From:             cataphract
Operating system: 
PHP version:      trunk-SVN-2010-09-04 (SVN)
Package:          Scripting Engine problem
Bug Type:         Bug
Bug description:Proxy object's store free callback calls zval_ptor_dtor on 
already freed data

Description:
------------
Proxy objects have this structure:



typedef struct _zend_proxy_object {

        zval *object;

        zval *property;

} zend_proxy_object;



zend_object_create_proxy does this:



ZEND_API zval *zend_object_create_proxy(zval *object, zval *member
TSRMLS_DC)

{

        zend_proxy_object *pobj = emalloc(sizeof(zend_proxy_object));

        /* ... */

        pobj->property = member;

        zval_add_ref(&pobj->property);

        /* ... */

}



The property field is used to store a zval that, in the get and set
handlers can the passed to zend_proxy_object.object's read_property and
write_property callbacks, so that the reads and writes in the proxy object
can be proxied.



The store free callback for proxy objects calls zval_ptr_tor on
&pobj->property:



ZEND_API void zend_objects_proxy_free_storage(zend_proxy_object *object
TSRMLS_DC)

{

        zval_ptr_dtor(&object->object);

        zval_ptr_dtor(&object->property);

        efree(object);

}



However, on script cleanup, the destruction order is wrong. The zval stored
in object->property is destroyed *before* the proxy object is.

Test script:
---------------
/* Extension */





typedef struct _proxy_test {

        zend_object std;

        long value;

} proxy_test;

static zend_class_entry *pt_ce_ptr;

static zend_object_handlers p_obj_handlers;

static zend_object_value p_ce_create_object(zend_class_entry *class_type
TSRMLS_DC)

{

    zend_object_value zov;

    proxy_test       *pobj;



    pobj = emalloc(sizeof *pobj);

    zend_object_std_init((zend_object *) pobj, class_type TSRMLS_CC);

        pobj->value = 7;



    object_properties_init(&pobj->std, class_type);

    zov.handle = zend_objects_store_put(pobj,

        (zend_objects_store_dtor_t) zend_objects_destroy_object,

        (zend_objects_free_object_storage_t)
zend_objects_free_object_storage,

        NULL TSRMLS_CC);

        zov.handlers = &p_obj_handlers;

    return zov;

}

zval *p_read_property(zval *object, zval *member, int type, const struct
_zend_literal *key TSRMLS_DC)

{

        proxy_test *iobj = zend_object_store_get_object(object TSRMLS_CC);

        if (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) {

                zval *ret = zend_object_create_proxy(object, member TSRMLS_CC);

                Z_DELREF_P(ret);

                return ret;

        } else {

                zval *ret;

                MAKE_STD_ZVAL(ret);

                ZVAL_LONG(ret, iobj->value);

                Z_DELREF_P(ret);

                return ret;

        }

}



void p_write_property(zval *object, zval *member, zval *value, const struct
_zend_literal *key TSRMLS_DC)

{

        proxy_test *iobj = zend_object_store_get_object(object TSRMLS_CC);

        if (Z_TYPE_P(value) == IS_LONG) {

                iobj->value = Z_LVAL_P(value);

        }

}

zval **p_get_property_ptr_ptr(zval *object, zval *member, const struct
_zend_literal *key TSRMLS_DC)

{

        return NULL;

}



ZEND_MODULE_STARTUP_D(testext)

{

        zend_class_entry ce;



        INIT_CLASS_ENTRY(ce, "ProxyTestClass", NULL);

        pt_ce_ptr = zend_register_internal_class(&ce TSRMLS_CC);

        pt_ce_ptr->create_object = p_ce_create_object;

    memcpy(&p_obj_handlers, zend_get_std_object_handlers(), sizeof
p_obj_handlers);

        /* could be NULL, but an empty impl is better (see bug #51768) */

        p_obj_handlers.get_property_ptr_ptr = p_get_property_ptr_ptr;

        p_obj_handlers.read_property = p_read_property;

        p_obj_handlers.write_property = p_write_property;

}



/* Script */



<?php



$n = new ProxyTestClass();

$h =& $n->whatever;

Expected result:
----------------
The proxy object would be destroyed before the property zval encapsulated
in it.

Actual result:
--------------
The property zval encapsulated in the proxy object is destroyed
prematurely.



Breakpoint on zend_object_create_proxy. Set data breakpoint on
&member->refcount__gc



Continue. Data breakpoint is hit (zval_add_ref(&pobj->property);). The
refcount is now 3.



Continue. Data breakpoint is hit. Call stack:



>       msvcr100d.dll!memset(unsigned char * dst=0x0000005a, unsigned char
value='`', unsigned long count=11071032)  Line 127      Asm

        php5ts_debug.dll!_zend_mm_free_int(_zend_mm_heap * heap=0x02426d50, void
* p=0x028101a0, const char * __zend_filename=0x5d637b70, const unsigned int
__zend_lineno=397, const char * __zend_orig_filename=0x00000000, const
unsigned int __zend_orig_lineno=0)  Line 2019 + 0x15 bytes      C

        php5ts_debug.dll!_efree(void * ptr=0x028101a0, const char *
__zend_filename=0x5d637b70, const unsigned int __zend_lineno=397, const
char * __zend_orig_filename=0x00000000, const unsigned int
__zend_orig_lineno=0)  Line 2378 + 0x2b bytes   C

        php5ts_debug.dll!destroy_op_array(_zend_op_array * op_array=0x0280f470,
void * * * tsrm_ls=0x024115f8)  Line 397 + 0x21 bytes   C

        php5ts_debug.dll!zend_execute_scripts(int type=8, void * * *
tsrm_ls=0x024115f8, _zval_struct * * retval=0x00000000, int file_count=3,
...)  Line 1220 + 0x1e bytes    C

        php5ts_debug.dll!php_execute_script(_zend_file_handle *
primary_file=0x00a8f72c, void * * * tsrm_ls=0x024115f8)  Line 2330 + 0x1b
bytes   C

        php.exe!main(int argc=2, char * * argv=0x024114c0)  Line 1252 + 0x13
bytes   C

        php.exe!__tmainCRTStartup()  Line 555 + 0x19 bytes      C

        php.exe!mainCRTStartup()  Line 371      C





efree() is being called with the refcount still being 3.



Breakpoint on zend_objects_proxy_free_storage. The call
zval_ptr_dtor(&object->property); will operate on already freed data:



object

0x028102c0 {object=0x0280db38 property=0x028101d0 }

    object: 0x0280db38 {value={...} refcount__gc=2 type='' ...}

    property: 0x028101d0 {value={...} refcount__gc=1515870810 type='Z'
...}













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

Reply via email to