Skip to content

Commit 8fc8584

Browse files
committed
Fix GH-20503: Assertion failure with ext/date DateInterval property hash
When a DateInterval object has a circular reference (e.g., $obj->prop = $obj), calling json_encode() triggered an assertion failure because the get_properties handler modified a HashTable with refcount > 1. Added a get_properties_for handler for DateInterval that duplicates the properties array before modification, matching the pattern used by DateTime and DateTimeZone.
1 parent fb1ec9a commit 8fc8584

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

ext/date/php_date.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ static HashTable *date_object_get_gc(zend_object *object, zval **table, int *n);
352352
static HashTable *date_object_get_properties_for(zend_object *object, zend_prop_purpose purpose);
353353
static HashTable *date_object_get_gc_interval(zend_object *object, zval **table, int *n);
354354
static HashTable *date_object_get_properties_interval(zend_object *object);
355+
static HashTable *date_object_get_properties_for_interval(zend_object *object, zend_prop_purpose purpose);
355356
static HashTable *date_object_get_gc_period(zend_object *object, zval **table, int *n);
356357
static HashTable *date_object_get_properties_for_timezone(zend_object *object, zend_prop_purpose purpose);
357358
static HashTable *date_object_get_gc_timezone(zend_object *object, zval **table, int *n);
@@ -1816,6 +1817,7 @@ static void date_register_classes(void) /* {{{ */
18161817
date_object_handlers_interval.read_property = date_interval_read_property;
18171818
date_object_handlers_interval.write_property = date_interval_write_property;
18181819
date_object_handlers_interval.get_properties = date_object_get_properties_interval;
1820+
date_object_handlers_interval.get_properties_for = date_object_get_properties_for_interval;
18191821
date_object_handlers_interval.get_property_ptr_ptr = date_interval_get_property_ptr_ptr;
18201822
date_object_handlers_interval.get_gc = date_object_get_gc_interval;
18211823
date_object_handlers_interval.compare = date_interval_compare_objects;
@@ -2240,6 +2242,33 @@ static HashTable *date_object_get_properties_interval(zend_object *object) /* {{
22402242
return props;
22412243
} /* }}} */
22422244

2245+
static HashTable *date_object_get_properties_for_interval(zend_object *object, zend_prop_purpose purpose) /* {{{ */
2246+
{
2247+
HashTable *props;
2248+
php_interval_obj *intervalobj;
2249+
2250+
switch (purpose) {
2251+
case ZEND_PROP_PURPOSE_DEBUG:
2252+
case ZEND_PROP_PURPOSE_SERIALIZE:
2253+
case ZEND_PROP_PURPOSE_VAR_EXPORT:
2254+
case ZEND_PROP_PURPOSE_JSON:
2255+
case ZEND_PROP_PURPOSE_ARRAY_CAST:
2256+
break;
2257+
default:
2258+
return zend_std_get_properties_for(object, purpose);
2259+
}
2260+
2261+
intervalobj = php_interval_obj_from_obj(object);
2262+
props = zend_array_dup(zend_std_get_properties(object));
2263+
if (!intervalobj->initialized) {
2264+
return props;
2265+
}
2266+
2267+
date_interval_object_to_hash(intervalobj, props);
2268+
2269+
return props;
2270+
} /* }}} */
2271+
22432272
static zend_object *date_object_new_period(zend_class_entry *class_type) /* {{{ */
22442273
{
22452274
php_period_obj *intern = zend_object_alloc(sizeof(php_period_obj), class_type);

ext/date/tests/bug-gh20503.phpt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
GH-20503 (Assertion failure with ext/date DateInterval property hash construction)
3+
--FILE--
4+
<?php
5+
$obj = new DateInterval('P1W');
6+
$obj->prop3 = $obj;
7+
8+
// Array cast triggers get_properties_for with ARRAY_CAST purpose
9+
// This would previously cause an assertion failure when modifying
10+
// a HashTable with refcount > 1
11+
$props = (array) $obj;
12+
var_dump(count($props));
13+
var_dump(isset($props['prop3']));
14+
var_dump($props['y']);
15+
?>
16+
--EXPECTF--
17+
Deprecated: Creation of dynamic property DateInterval::$prop3 is deprecated in %s on line %d
18+
int(11)
19+
bool(true)
20+
int(0)

0 commit comments

Comments
 (0)