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