Bug #52774 [Asn->Csd]: Proxy object's store free callback calls zval_ptor_dtor on already freed data

From: Date: Mon, 04 Oct 2010 07:16:16 +0000
Subject: Bug #52774 [Asn->Csd]: Proxy object's store free callback calls zval_ptor_dtor on already freed data
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-154877@lists.php.net to get a copy of this message
Edit report at http://bugs.php.net/bug.php?id=52774&edit=1

 ID:                 52774
 Updated by:         dmitry@php.net
 Reported by:        cataphract@php.net
 Summary:            Proxy object's store free callback calls
                     zval_ptor_dtor on already freed data
-Status:             Assigned
+Status:             Closed
 Type:               Bug
 Package:            Scripting Engine problem
 PHP Version:        trunk-SVN-2010-09-04 (SVN)
 Assigned To:        dmitry
 Block user comment: N

 New Comment:

This bug has been fixed in SVN.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.




Previous Comments:
------------------------------------------------------------------------
[2010-10-04 09:15:34] dmitry@php.net

Automatic comment from SVN on behalf of dmitry
Revision: http://svn.php.net/viewvc/?view=revision&revision=303971
Log: - Fixed bug #52773 (Proxy objects have an inadequate destroy_object
store callback)
- Fixed bug #52774 (Proxy object's store free callback calls
zval_ptor_dtor on already freed data)

------------------------------------------------------------------------
[2010-09-04 03:43:10] cataphract@php.net

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 this bug report at http://bugs.php.net/bug.php?id=52774&edit=1


Thread (3 messages)

« previous php.bugs (#154877) next »