From:             cataphract
Operating system: Windows
PHP version:      trunk-SVN-2010-09-04 (SVN)
Package:          Scripting Engine problem
Bug Type:         Bug
Bug description:Proxy objects have an inadequate destroy_object store callback

Description:
------------
zend_object_create_proxy calls zend_objects_store_put with the second
argument NULL:



See the definition of zend_object_create-proxy:



http://lxr.php.net/search?q=zend_object_create_proxy&project=PHP_TRUNK&defs=&refs=&path=&hist=



This results in the _store_object.dtor store callback being set to
zend_objects_destroy_object. See the definition of zend_objects_store_put:



http://lxr.php.net/opengrok/xref/PHP_TRUNK/Zend/zend_objects_API.c#zend_objects_store_put



This callback is defined here:



http://lxr.php.net/xref/PHP_TRUNK/Zend/zend_objects.c#zend_objects_destroy_object



It is inappropriate because it starts with:



ZEND_API void zend_objects_destroy_object(zend_object *object,
zend_object_handle handle TSRMLS_DC)

{

        zend_function *destructor = object ? object->ce->destructor : NULL;



The first parameter won't actually be a zend_object; it'll be a
zend_proxy_object, whose definition is not compatible with that of
zend_object:



typedef struct _zend_object {

        zend_class_entry *ce;

        HashTable *properties;

        zval **properties_table;

        HashTable *guards; /* protects from __get/__set ... recursion */

} zend_object;



typedef struct _zend_proxy_object {

        zval *object;

        zval *property;

} zend_proxy_object;

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;

}



/*static zend_function_entry proxy_test_methods[] = {

        {NULL, NULL, NULL, 0, 0}

};*/



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;

unset($h);

Expected result:
----------------
zend_objects_destroy_object should not be the proxy object's destructor.

A no-op destructor should be passed to the store_put function.

Actual result:
--------------
I didn't get a crash by change (the memory in object->ce->destructor turned
out to be zeroed, which makes zend_objects_destroy_object skip the
implementation), but we can see this doesn't make sense:



Breakpoint with this stack:



>       php5ts_debug.dll!zend_objects_destroy_object(_zend_object *
object=0x011bf680, unsigned int handle=2, void * * * tsrm_ls=0x001d15f8) 
Line 62 C

        php5ts_debug.dll!zend_objects_store_del_ref_by_handle_ex(unsigned int
handle=2, const _zend_object_handlers * handlers=0x5d6fb848, void * * *
tsrm_ls=0x001d15f8)  Line 206 + 0x18 bytes      C

        php5ts_debug.dll!zend_objects_store_del_ref(_zval_struct *
zobject=0x011bdae8, void * * * tsrm_ls=0x001d15f8)  Line 172 + 0x14
bytes   C

        php5ts_debug.dll!_zval_dtor_func(_zval_struct * zvalue=0x011bdae8, const
char * __zend_filename=0x5d633388, const unsigned int __zend_lineno=435) 
Line 53 + 0x15 bytes    C

        php5ts_debug.dll!_zval_dtor(_zval_struct * zvalue=0x011bdae8, const char
* __zend_filename=0x5d633388, const unsigned int __zend_lineno=435)  Line
35 + 0x11 bytes C

        php5ts_debug.dll!_zval_ptr_dtor(_zval_struct * * zval_ptr=0x011bfaac,
const char * __zend_filename=0x5d63828c, const unsigned int
__zend_lineno=181)  Line 435 + 0x19 bytes       C

        php5ts_debug.dll!_zval_ptr_dtor_wrapper(_zval_struct * *
zval_ptr=0x011bfaac)  Line 181 + 0x17 bytes     C

        php5ts_debug.dll!zend_hash_del_key_or_index(_hashtable * ht=0x001d8c98,
const char * arKey=0x011c05c8, unsigned int nKeyLength=2, unsigned long
h=5863341, int flag=2)  Line 513 + 0x11 bytes   C

        php5ts_debug.dll!zend_delete_variable(_zend_execute_data * 
ex=0x00000000,
_hashtable * ht=0x001d8c98, char * name=0x011c05c8, int name_len=2,
unsigned long hash_value=5863341, void * * * tsrm_ls=0x001d15f8)  Line 1678
+ 0x17 bytes    C

        
php5ts_debug.dll!ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDLER(_zend_execute_data
* execute_data=0x011a20d8, void * * * tsrm_ls=0x001d15f8)  Line 33645 +
0x3c bytes      C

        php5ts_debug.dll!execute(_zend_op_array * op_array=0x011bf470, void * * 
*
tsrm_ls=0x001d15f8)  Line 410 + 0x11 bytes      C

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

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

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



object

0x011bf680 {ce=0x011bdb38 properties=0x011c0350 properties_table=0xa4693fd0
...}

    ce: 0x011bdb38 {type='' name=0x62afc3c8
"õ°&]â´&]qÐ&]      ¯b6¯bcÊ&]ð©=]å¯b" name_length=2 ...}

    properties: 0x011c0350 {nTableSize=18614848 nTableMask=8
nNumOfElements=3 ...}

    properties_table: 0xa4693fd0

    guards: 0x3fd05a5a {nTableSize=??? nTableMask=??? nNumOfElements=???
...}



((zend_proxy_object*) object)

0x011bf680 {object=0x011bdb38 property=0x011c0350 }

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

    property: 0x011c0350 {value={...} refcount__gc=3 type='' ...}



We were lucky though:



object->ce->destructor

0x00000000 {type=??? common={...} op_array={...} ...}

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

Reply via email to