Edit report at http://bugs.php.net/bug.php?id=52773&edit=1
ID: 52773 Updated by: fel...@php.net Reported by: cataphr...@php.net Summary: Proxy objects have an inadequate destroy_object store callback -Status: Open +Status: Assigned Type: Bug Package: Scripting Engine problem Operating System: Windows PHP Version: trunk-SVN-2010-09-04 (SVN) -Assigned To: +Assigned To: dmitry Block user comment: N Previous Comments: ------------------------------------------------------------------------ [2010-09-04 03:17:55] cataphr...@php.net 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 this bug report at http://bugs.php.net/bug.php?id=52773&edit=1