php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #51768 Fallback to read_property causes subsequent crash
Submitted: 2010-05-08 04:14 UTC Modified: 2021-06-20 04:22 UTC
Votes:4
Avg. Score:3.5 ± 0.9
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: cataphract@php.net Assigned: cmb (profile)
Status: No Feedback Package: Reproducible crash
PHP Version: 5.3.2 OS: Windows
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2010-05-08 04:14 UTC] cataphract@php.net
Description:
------------
When the get_property_ptr_ptr handler is omitted, zend_fetch_property_address falls back to read_property, but then the behavior in cases such as
$a = &$obj->prop;
deviates from that of
$a = &$obj['prop'];
which properly emits an error.
The final result is a crash.



Test script:
---------------
exttest.h:

#ifndef PHP_EXTTEST_H
# define PHP_EXTTEST_H
# ifdef HAVE_CONFIG_H
#  include<config.h>
# endif
# include<php.h>
extern zend_module_entry exttest_module_entry;
#define phpext_exttest_ptr &exttest_module_entry
#endif

exttest.c:
#include "exttest.h"

static zend_object_handlers object_handlers;

static zend_object_value ce_create_object(zend_class_entry *class_type TSRMLS_DC)
{
    zend_object_value zov;
    zend_object       *zobj;

    zobj = emalloc(sizeof *zobj);
    zend_object_std_init(zobj, class_type TSRMLS_CC);

    zend_hash_copy(zobj->properties, &(class_type->default_properties),
        (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*));
    zov.handle = zend_objects_store_put(zobj,
        (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 = &object_handlers;
    return zov;
}

ZEND_MODULE_STARTUP_D(exttest)
{
    zend_class_entry ce;
    zend_class_entry *ce_ptr;
    zval *property;

    ALLOC_PERMANENT_ZVAL(property);
    INIT_ZVAL(*property);
    Z_TYPE_P(property) = IS_LONG;
    Z_LVAL_P(property) = 20l;

    memcpy(&object_handlers, zend_get_std_object_handlers(),
        sizeof object_handlers);
    object_handlers.get_property_ptr_ptr = NULL;

    INIT_CLASS_ENTRY(ce, "TestClass", NULL);
    ce_ptr = zend_register_internal_class(&ce TSRMLS_CC);
    ce_ptr->create_object = ce_create_object;
    zend_declare_property_ex(ce_ptr, "prop", 4, property, ZEND_ACC_PUBLIC,
        NULL, 0 TSRMLS_CC);
}

zend_module_entry exttest_module_entry = {
    STANDARD_MODULE_HEADER,
    "exttest",
    NULL, /* Functions */
    ZEND_MODULE_STARTUP_N(exttest) , /* MINIT */
    NULL, /* MSHUTDOWN */
    NULL, /* RINIT */
    NULL, /* RSHUTDOWN */
    NULL, /* MINFO */
    NO_VERSION_YET,
    STANDARD_MODULE_PROPERTIES
};

ZEND_GET_MODULE(exttest)

config.m4:
PHP_ARG_ENABLE(exttest,
  [Whether to enable the "exttest" extension],
  [  enable-exttest         Enable "exttest" extension support])

if test $PHP_EXTTEST != "no"; then
  PHP_SUBST(EXTTEST_SHARED_LIBADD)
  PHP_NEW_EXTENSION(exttest, exttest.c, $ext_shared)
fi


test.php:
<?php
$obj = new TestClass();
debug_zval_dump($obj);
$a = &$obj->prop;
debug_zval_dump($obj);
debug_zval_dump(&$a);
$a = 40;
debug_zval_dump($obj);
debug_zval_dump(&$a);
unset($a);
debug_zval_dump($obj);


Expected result:
----------------
Expected an error saying the operation is not permitted (like when attempting $a = &obj['index'] where there's no get_property_ptr_ptr equivalent).

Actual result:
--------------
When thre's get_property_ptr_ptr:

object(TestClass)#1 (1) refcount(2){
  ["prop"]=>
  long(20) refcount(2)
}
object(TestClass)#1 (1) refcount(2){
  ["prop"]=>
  &long(20) refcount(2)
}
&long(20) refcount(3)
object(TestClass)#1 (1) refcount(2){
  ["prop"]=>
  &long(40) refcount(2)
}
&long(40) refcount(3)
object(TestClass)#1 (1) refcount(2){
  ["prop"]=>
  long(40) refcount(1)
}

When there's no get_property_ptr_ptr, zend_fetch_property_address falls back to read_propert. Something happens afterwards that provokes the crash.

object(TestClass)#1 (1) refcount(2){
  ["prop"]=>
  long(20) refcount(2)
}
object(TestClass)#1 (1) refcount(2){
  ["prop"]=>
  long(20) refcount(1)
}
&long(20) refcount(3)
object(TestClass)#1 (1) refcount(2){
  ["prop"]=>
  long(20) refcount(1)
}
&long(40) refcount(3)
object(TestClass)#1 (1) refcount(2){
  ["prop"]=>
  long(20) refcount(1)
}
Segmentation fault

#0  0x081d60ae in zend_mm_check_ptr (heap=0x83881b8, ptr=0x840ca90, silent=1,
    __zend_filename=0x83633a0 "/home/glopes/php/php-5.3.2/Zend/zend_variables.c", __zend_lineno=178,
    __zend_orig_filename=0x83621b8 "/home/glopes/php/php-5.3.2/Zend/zend_execute_API.c",
    __zend_orig_lineno=440) at /home/glopes/php/php-5.3.2/Zend/zend_alloc.c:1347
#1  0x081d768d in _zend_mm_free_int (heap=0x83881b8, p=0x840ca90,
    __zend_filename=0x83633a0 "/home/glopes/php/php-5.3.2/Zend/zend_variables.c", __zend_lineno=178,
    __zend_orig_filename=0x83621b8 "/home/glopes/php/php-5.3.2/Zend/zend_execute_API.c",
    __zend_orig_lineno=440) at /home/glopes/php/php-5.3.2/Zend/zend_alloc.c:1983
#2  0x081d86bb in _efree (ptr=0x840ca90,
    __zend_filename=0x83633a0 "/home/glopes/php/php-5.3.2/Zend/zend_variables.c", __zend_lineno=178,
    __zend_orig_filename=0x83621b8 "/home/glopes/php/php-5.3.2/Zend/zend_execute_API.c",
    __zend_orig_lineno=440) at /home/glopes/php/php-5.3.2/Zend/zend_alloc.c:2351
#3  0x081e9944 in _zval_ptr_dtor (zval_ptr=0x841d584,
    __zend_filename=0x83633a0 "/home/glopes/php/php-5.3.2/Zend/zend_variables.c", __zend_lineno=178)
    at /home/glopes/php/php-5.3.2/Zend/zend_execute_API.c:440
#4  0x081f7ca8 in _zval_ptr_dtor_wrapper (zval_ptr=0x841d584)
    at /home/glopes/php/php-5.3.2/Zend/zend_variables.c:178
#5  0x08207cf7 in zend_hash_destroy (ht=0x841d520) at /home/glopes/php/php-5.3.2/Zend/zend_hash.c:526
#6  0x0821d64d in zend_object_std_dtor (object=0x841ccf8)
    at /home/glopes/php/php-5.3.2/Zend/zend_objects.c:45
#7  0x0821d9a9 in zend_objects_free_object_storage (object=0x841ccf8)
    at /home/glopes/php/php-5.3.2/Zend/zend_objects.c:114
#8  0x0822252b in zend_objects_store_del_ref_by_handle_ex (handle=1, handlers=0xb7f1bd60)
    at /home/glopes/php/php-5.3.2/Zend/zend_objects_API.c:220
#9  0x08222320 in zend_objects_store_del_ref (zobject=0x841c45c)
    at /home/glopes/php/php-5.3.2/Zend/zend_objects_API.c:172
#10 0x081f7917 in _zval_dtor_func (zvalue=0x841c45c,
    __zend_filename=0x83621b8 "/home/glopes/php/php-5.3.2/Zend/zend_execute_API.c", __zend_lineno=439)
    at /home/glopes/php/php-5.3.2/Zend/zend_variables.c:52
#11 0x081e96cf in _zval_dtor (zvalue=0x841c45c,
    __zend_filename=0x83621b8 "/home/glopes/php/php-5.3.2/Zend/zend_execute_API.c", __zend_lineno=439)
    at /home/glopes/php/php-5.3.2/Zend/zend_variables.h:35
#12 0x081e9919 in _zval_ptr_dtor (zval_ptr=0x841d5d8,
    __zend_filename=0x83633a0 "/home/glopes/php/php-5.3.2/Zend/zend_variables.c", __zend_lineno=178)
    at /home/glopes/php/php-5.3.2/Zend/zend_execute_API.c:439
#13 0x081f7ca8 in _zval_ptr_dtor_wrapper (zval_ptr=0x841d5d8)
    at /home/glopes/php/php-5.3.2/Zend/zend_variables.c:178
#14 0x0820806e in zend_hash_apply_deleter (ht=0x83879d0, p=0x841d5cc)
    at /home/glopes/php/php-5.3.2/Zend/zend_hash.c:611
#15 0x08208596 in zend_hash_reverse_apply (ht=0x83879d0, apply_func=0x81e91bd <zval_call_destructor>)
    at /home/glopes/php/php-5.3.2/Zend/zend_hash.c:760
#16 0x081e924a in shutdown_destructors () at /home/glopes/php/php-5.3.2/Zend/zend_execute_API.c:226
#17 0x081f943c in zend_call_destructors () at /home/glopes/php/php-5.3.2/Zend/zend.c:874
#18 0x08190427 in php_request_shutdown (dummy=0x0) at /home/glopes/php/php-5.3.2/main/main.c:1587
#19 0x082c0086 in main (argc=2, argv=0xbfffe674) at /home/glopes/php/php-5.3.2/sapi/cli/php_cli.c:1373


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-05-08 16:01 UTC] cataphract@php.net
I got it backwards in the expected behavior and a bit in the description.
What should give a fatal error is something like:
$obj->prop = &$a;
I said that
$a = &$obj['prop']
emits an error. It doesn't. It emits a notice if the zval* returned by the read_dimension handler does not return a reference (Indirect modification of overloaded element).
In fact both read_dimension and read_property without get_property_ptr_ptr behave correctly in the $obj->prop = &$a by emitting a fatal error: (Cannot assign by reference to overloaded object)

The expected behavior should then be either mimic read_dimension (accept only references) or just throw an error.
 [2010-05-09 17:21 UTC] cataphract@php.net
Apparently, not to break backwards compatibility, 
$a = &$obj->prop
should not accept only references, like read_dimension. The current behavior is to make a reference if the refcount is one (SEPARATE_ZVAL_TO_MAKE_IS_REF).
Anyway, in the test script the refcount is 2.
 [2021-06-09 14:28 UTC] cmb@php.net
-Status: Open +Status: Feedback -Assigned To: +Assigned To: cmb
 [2021-06-09 14:28 UTC] cmb@php.net
Is this still an issue with any of the actively supported PHP
versions[1]?

[1] <https://www.php.net/supported-versions.php>
 [2021-06-20 04:22 UTC] php-bugs at lists dot php dot net
No feedback was provided. The bug is being suspended because
we assume that you are no longer experiencing the problem.
If this is not the case and you are able to provide the
information that was requested earlier, please do so and
change the status of the bug back to "Re-Opened". Thank you.
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Wed Jun 23 22:01:24 2021 UTC