Skip to content

Assertion failure with ext/date DateInterval property hash construction #20503

Closed

Description

@vi3tL0u1s

Description

The following code:

<?
for ($i = 0; $i <= 2; $j++) {
    $a[] = $$x[ ! 0] = $obj = new DateInterval('P1W');
    $a = false;
    $aniay['abc'] = $obj->prop3 = $y = (array)   $a[] =& $  $u; 
    $$x[$key]-= $aniay['abc'] = $a === $ao = new ArrayObject();
    var_dump(json_decode(json_encode($a), '1000001'));
}

Resulted in this output:

php: /path/to/php-src/Zend/zend_hash.c:912: _zend_hash_str_add_or_update_i: Assertion `(zend_gc_refcount(&(ht)->gc) == 1) || ((ht)->u.flags & (1<<6))' failed.
Aborted

Commit:

035f95cf5e016236cca11bc293dc04d40b40e45c

Build configuration:

CC="clang" CXX="clang++" CFLAGS="-fsanitize=address -g -O0" CXXFLAGS="-fsanitize=address -g -O0" LDFLAGS="-fsanitize=address" ./buildconf --force && ./configure --enable-debug --enable-address-sanitizer --disable-shared --with-pic --enable-mbstring --with-zlib

PHP Version

PHP 8.6.0-dev (cli) (built: Nov 16 2025 19:51:33) (NTS DEBUG)
Copyright (c) The PHP Group
Zend Engine v4.6.0-dev, Copyright (c) Zend Technologies
    with Zend OPcache v8.6.0-dev, Copyright (c), by Zend Technologies

Operating System

Ubuntu 22.04

Activity

  1. devnexen commented on Nov 16, 2025

    @devnexen
    Member
    #0  __pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=0) at ./nptl/pthread_kill.c:44
    #1  __pthread_kill_internal (threadid=<optimized out>, signo=6) at ./nptl/pthread_kill.c:89
    #2  __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:100
    #3  0x00007ffff6845e2e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
    #4  0x00007ffff6828888 in __GI_abort () at ./stdlib/abort.c:77
    #5  0x00007ffff68287f0 in __assert_fail_base (fmt=<optimized out>, assertion=<optimized out>, file=<optimized out>, line=<optimized out>, function=<optimized out>) at ./assert/assert.c:118
    #6  0x000055555e1d3f45 in _zend_hash_str_add_or_update_i (ht=0x7bfff284c8a0, str=0x55555eafc8c0 <str> "y", len=1, h=9223372036854953502, pData=0x7bfff3952520, flag=1)
        at /home/dcarlier/Contribs/php-src/Zend/zend_hash.c:912
    #7  0x000055555e1d3c70 in zend_hash_str_update (ht=0x7bfff284c8a0, str=0x55555eafc8c0 <str> "y", len=1, pData=0x7bfff3952520) at /home/dcarlier/Contribs/php-src/Zend/zend_hash.c:1041
    #8  0x0000555558b73163 in date_interval_object_to_hash (intervalobj=0x7bfff284c780, props=0x7bfff284c8a0) at /home/dcarlier/Contribs/php-src/ext/date/php_date.c:2179
    #9  0x0000555558baf920 in date_object_get_properties_interval (object=0x7bfff284c798) at /home/dcarlier/Contribs/php-src/ext/date/php_date.c:2211
    #10 0x000055555e5137df in zend_std_get_properties_for (obj=0x7bfff284c798, purpose=ZEND_PROP_PURPOSE_JSON) at /home/dcarlier/Contribs/php-src/Zend/zend_object_handlers.c:2508
    #11 0x000055555e514007 in zend_get_properties_for (obj=0x7bfff3952460, purpose=ZEND_PROP_PURPOSE_JSON) at /home/dcarlier/Contribs/php-src/Zend/zend_object_handlers.c:2544
    #12 0x0000555559b1b809 in php_json_encode_array (buf=0x7bfff3b0b660, val=0x7bfff3952460, options=0, encoder=0x7bfff3b0b640) at /home/dcarlier/Contribs/php-src/ext/json/json_encoder.c:209
    #13 0x0000555559b17596 in php_json_encode_zval (buf=0x7bfff3b0b660, val=0x7bfff284d258, options=0, encoder=0x7bfff3b0b640) at /home/dcarlier/Contribs/php-src/ext/json/json_encoder.c:668
    #14 0x0000555559b1dbe1 in php_json_encode_array (buf=0x7bfff3b0b660, val=0x7bfff39523e0, options=0, encoder=0x7bfff3b0b640) at /home/dcarlier/Contribs/php-src/ext/json/json_encoder.c:301
    #15 0x0000555559b17596 in php_json_encode_zval (buf=0x7bfff3b0b660, val=0x7bfff2874080, options=0, encoder=0x7bfff3b0b640) at /home/dcarlier/Contribs/php-src/ext/json/json_encoder.c:668
    #16 0x0000555559b1dbe1 in php_json_encode_array (buf=0x7bfff3b0b660, val=0x7bfff39522e0, options=0, encoder=0x7bfff3b0b640) at /home/dcarlier/Contribs/php-src/ext/json/json_encoder.c:301
    #17 0x0000555559b17596 in php_json_encode_zval (buf=0x7bfff3b0b660, val=0x7bfff28717c0, options=0, encoder=0x7bfff3b0b640) at /home/dcarlier/Contribs/php-src/ext/json/json_encoder.c:668
    #18 0x0000555559b1dbe1 in php_json_encode_array (buf=0x7bfff3b0b660, val=0x7bfff3952260, options=0, encoder=0x7bfff3b0b640) at /home/dcarlier/Contribs/php-src/ext/json/json_encoder.c:301
    #19 0x0000555559b17596 in php_json_encode_zval (buf=0x7bfff3b0b660, val=0x7bfff2801fc8, options=0, encoder=0x7bfff3b0b640) at /home/dcarlier/Contribs/php-src/ext/json/json_encoder.c:668
    #20 0x0000555559b1dbe1 in php_json_encode_array (buf=0x7bfff3b0b660, val=0x7bfff39521e0, options=0, encoder=0x7bfff3b0b640) at /home/dcarlier/Contribs/php-src/ext/json/json_encoder.c:301
    #21 0x0000555559b17596 in php_json_encode_zval (buf=0x7bfff3b0b660, val=0x7bfff28073f0, options=0, encoder=0x7bfff3b0b640) at /home/dcarlier/Contribs/php-src/ext/json/json_encoder.c:668
    #22 0x0000555559b479b9 in zif_json_encode (execute_data=0x7bfff28073a0, return_value=0x7bfff2807280) at /home/dcarlier/Contribs/php-src/ext/json/json.c:237
    #23 0x000055555dea1d62 in ZEND_DO_ICALL_SPEC_RETVAL_USED_TAILCALL_HANDLER (execute_data=0x7bfff2807020, opline=0x7bfff284f3a8) at Zend/zend_vm_execute.h:57205
    #24 0x000055555d43239f in execute_ex (ex=0x7bfff2807020) at Zend/zend_vm_execute.h:116212
    #25 0x000055555d43441f in zend_execute (op_array=0x7bfff2889000, return_value=0x0) at Zend/zend_vm_execute.h:121924
    #26 0x000055555e655620 in zend_execute_script (type=8, retval=0x0, file_handle=0x7fffffffc2c0) at /home/dcarlier/Contribs/php-src/Zend/zend.c:1975
    #27 0x000055555cb0e303 in php_execute_script_ex (primary_file=0x7fffffffc2c0, retval=0x0) at /home/dcarlier/Contribs/php-src/main/main.c:2645
    #28 0x000055555cb0f1e9 in php_execute_script (primary_file=0x7fffffffc2c0) at /home/dcarlier/Contribs/php-src/main/main.c:2685
    #29 0x000055555e666eef in do_cli (argc=2, argv=0x7c2ff4de1ba0) at /home/dcarlier/Contribs/php-src/sapi/cli/php_cli.c:951
    #30 0x000055555e662e92 in main (argc=2, argv=0x7c2ff4de1ba0) at /home/dcarlier/Contribs/php-src/sapi/cli/php_cli.c:1362
  2. ndossche commented on Nov 16, 2025

    @ndossche
    Member

    Reduction:

    <?php
    $obj = new DateInterval('P1W');
    $obj->prop3 = $obj;
    var_dump(json_encode($obj));
  3. changed the title [-]Assertion failure in _zend_hash_str_add_or_update_i triggered by invalid array/dynamic property manipulation during JSON encoding[/-] [+]Assertion failure with ext/date DateInterval property hash construction[/+] on Nov 16, 2025
  4. ndossche commented on Nov 16, 2025

    @ndossche
    Member

    The problem is that by assigning the object to itself we create a cycle and therefore a property table with a refcount>1.
    Then the get_properties handler tries to add the internal keys to it and it fails the RC1 assertion.
    The handler shouldn't modify the original properties table anyway. The following would fix this. However it only works on 8.4+ and not on 8.3 so I still have to figure out why that is:

    diff --git a/ext/date/php_date.c b/ext/date/php_date.c
    index 692136bbad2..25b5e98474d 100644
    --- a/ext/date/php_date.c
    +++ b/ext/date/php_date.c
    @@ -349,7 +349,7 @@ static int date_object_compare_date(zval *d1, zval *d2);
     static HashTable *date_object_get_gc(zend_object *object, zval **table, int *n);
     static HashTable *date_object_get_properties_for(zend_object *object, zend_prop_purpose purpose);
     static HashTable *date_object_get_gc_interval(zend_object *object, zval **table, int *n);
    -static HashTable *date_object_get_properties_interval(zend_object *object);
    +static HashTable *date_object_get_properties_for_interval(zend_object *object, zend_prop_purpose purpose);
     static HashTable *date_object_get_gc_period(zend_object *object, zval **table, int *n);
     static HashTable *date_object_get_properties_for_timezone(zend_object *object, zend_prop_purpose purpose);
     static HashTable *date_object_get_gc_timezone(zend_object *object, zval **table, int *n);
    @@ -1785,7 +1785,7 @@ static void date_register_classes(void) /* {{{ */
     	date_object_handlers_interval.has_property = date_interval_has_property;
     	date_object_handlers_interval.read_property = date_interval_read_property;
     	date_object_handlers_interval.write_property = date_interval_write_property;
    -	date_object_handlers_interval.get_properties = date_object_get_properties_interval;
    +	date_object_handlers_interval.get_properties_for = date_object_get_properties_for_interval;
     	date_object_handlers_interval.get_property_ptr_ptr = date_interval_get_property_ptr_ptr;
     	date_object_handlers_interval.get_gc = date_object_get_gc_interval;
     	date_object_handlers_interval.compare = date_interval_compare_objects;
    @@ -2197,13 +2197,13 @@ static void date_interval_object_to_hash(php_interval_obj *intervalobj, HashTabl
     #undef PHP_DATE_INTERVAL_ADD_PROPERTY
     }
     
    -static HashTable *date_object_get_properties_interval(zend_object *object) /* {{{ */
    +static HashTable *date_object_get_properties_for_interval(zend_object *object, zend_prop_purpose purpose) /* {{{ */
     {
     	HashTable *props;
     	php_interval_obj *intervalobj;
     
     	intervalobj = php_interval_obj_from_obj(object);
    -	props = zend_std_get_properties(object);
    +	props = zend_array_dup(zend_std_get_properties(object));
     	if (!intervalobj->initialized) {
     		return props;
     	}
    

    EDIT: bisect reveals that 780a828 makes it work on 8.4... But that's a lot of code to sift through...

  5. added a commit that references this issue on Oct 5, 2026
    f5bd733
  6. added 3 commits that reference this issue on Oct 15, 2026
  7. added 3 commits that reference this issue on Oct 15, 2026
    5828413
    d4265ca
    8ba5aa7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions