php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #52773 Proxy objects have an inadequate destroy_object store callback
Submitted: 2010-09-04 03:17 UTC Modified: 2010-10-04 09:16 UTC
From: cataphract@php.net Assigned: dmitry (profile)
Status: Closed Package: Scripting Engine problem
PHP Version: trunk-SVN-2010-09-04 (SVN) OS: Windows
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: cataphract@php.net
New email:
PHP Version: OS:

 

 [2010-09-04 03:17 UTC] cataphract@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={...} ...}

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-09-05 23:37 UTC] felipe@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: dmitry
 [2010-10-04 09:15 UTC] dmitry@php.net
Automatic comment from SVN on behalf of dmitry
Revision: http://svn.php.net/viewvc/?view=revision&amp;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-10-04 09:16 UTC] dmitry@php.net
-Status: Assigned +Status: Closed
 [2010-10-04 09:16 UTC] dmitry@php.net
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.


 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Dec 27 17:01:30 2024 UTC