diff --git a/NEWS b/NEWS index 263574eb4b8..48fa98b048b 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.16 +- Date: + . Fix crashes when trying to instantiate uninstantiable classes via date + static constructors. (ndossche) + - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 5228ecbf089..6d0f0428f08 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -1483,7 +1483,8 @@ static void create_date_period_datetime(timelib_time *datetime, zend_class_entry if (datetime) { php_date_obj *date_obj; - object_init_ex(zv, ce); + zend_result result = object_init_ex(zv, ce); + ZEND_ASSERT(result == SUCCESS && "should succeed as it reuses an existing object's ce"); date_obj = Z_PHPDATE_P(zv); date_obj->time = timelib_time_clone(datetime); } else { @@ -2354,6 +2355,7 @@ static void add_common_properties(HashTable *myht, zend_object *zobj) } /* Advanced Interface */ +/* TODO: remove this API because it is unsafe to use as-is, as it does not propagate the failure/success status. */ PHPAPI zval *php_date_instantiate(zend_class_entry *pce, zval *object) /* {{{ */ { object_init_ex(object, pce); @@ -2631,7 +2633,9 @@ PHP_FUNCTION(date_create_from_format) Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone) ZEND_PARSE_PARAMETERS_END(); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, return_value); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date) != SUCCESS) { + RETURN_THROWS(); + } if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, format_str, timezone_object, PHP_DATE_INIT_FORMAT)) { zval_ptr_dtor(return_value); RETURN_FALSE; @@ -2653,7 +2657,9 @@ PHP_FUNCTION(date_create_immutable_from_format) Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone) ZEND_PARSE_PARAMETERS_END(); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, return_value); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable) != SUCCESS) { + RETURN_THROWS(); + } if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, format_str, timezone_object, PHP_DATE_INIT_FORMAT)) { zval_ptr_dtor(return_value); RETURN_FALSE; @@ -2709,7 +2715,9 @@ PHP_METHOD(DateTime, createFromImmutable) old_obj = Z_PHPDATE_P(datetimeimmutable_object); DATE_CHECK_INITIALIZED(old_obj->time, Z_OBJCE_P(datetimeimmutable_object)); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, return_value); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date) != SUCCESS) { + RETURN_THROWS(); + } new_obj = Z_PHPDATE_P(return_value); new_obj->time = timelib_time_clone(old_obj->time); @@ -2730,7 +2738,9 @@ PHP_METHOD(DateTime, createFromInterface) old_obj = Z_PHPDATE_P(datetimeinterface_object); DATE_CHECK_INITIALIZED(old_obj->time, Z_OBJCE_P(datetimeinterface_object)); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, return_value); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date) != SUCCESS) { + RETURN_THROWS(); + } new_obj = Z_PHPDATE_P(return_value); new_obj->time = timelib_time_clone(old_obj->time); @@ -2748,7 +2758,9 @@ PHP_METHOD(DateTime, createFromTimestamp) Z_PARAM_NUMBER(value) ZEND_PARSE_PARAMETERS_END(); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, &new_object); + if (object_init_ex(&new_object, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date) != SUCCESS) { + RETURN_THROWS(); + } new_dateobj = Z_PHPDATE_P(&new_object); switch (Z_TYPE_P(value)) { @@ -2784,7 +2796,9 @@ PHP_METHOD(DateTimeImmutable, createFromMutable) old_obj = Z_PHPDATE_P(datetime_object); DATE_CHECK_INITIALIZED(old_obj->time, Z_OBJCE_P(datetime_object)); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, return_value); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable) != SUCCESS) { + RETURN_THROWS(); + } new_obj = Z_PHPDATE_P(return_value); new_obj->time = timelib_time_clone(old_obj->time); @@ -2805,7 +2819,9 @@ PHP_METHOD(DateTimeImmutable, createFromInterface) old_obj = Z_PHPDATE_P(datetimeinterface_object); DATE_CHECK_INITIALIZED(old_obj->time, Z_OBJCE_P(datetimeinterface_object)); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, return_value); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable) != SUCCESS) { + RETURN_THROWS(); + } new_obj = Z_PHPDATE_P(return_value); new_obj->time = timelib_time_clone(old_obj->time); @@ -2823,7 +2839,9 @@ PHP_METHOD(DateTimeImmutable, createFromTimestamp) Z_PARAM_NUMBER(value) ZEND_PARSE_PARAMETERS_END(); - php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, &new_object); + if (object_init_ex(&new_object, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable) != SUCCESS) { + RETURN_THROWS(); + } new_dateobj = Z_PHPDATE_P(&new_object); switch (Z_TYPE_P(value)) { @@ -5171,7 +5189,9 @@ PHP_METHOD(DatePeriod, createFromISO8601String) Z_PARAM_LONG(options) ZEND_PARSE_PARAMETERS_END(); - object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_period); + if (object_init_ex(return_value, execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_period) != SUCCESS) { + RETURN_THROWS(); + } dpobj = Z_PHPPERIOD_P(return_value); dpobj->current = NULL; diff --git a/ext/date/tests/instantiate_uninstantiable_classes.phpt b/ext/date/tests/instantiate_uninstantiable_classes.phpt new file mode 100644 index 00000000000..634ed635b2d --- /dev/null +++ b/ext/date/tests/instantiate_uninstantiable_classes.phpt @@ -0,0 +1,82 @@ +--TEST-- +Instantiating uninstantiable classes via static constructors +--FILE-- +getMessage(), "\n"; +} + +try { + MyDateTime::createFromFormat('Y-m-d', '2025-01-01'); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTime::createFromImmutable(new DateTimeImmutable()); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTime::createFromInterface(new DateTimeImmutable()); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTime::createFromTimestamp(0); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTimeImmutable::createFromFormat('Y-m-d', '2025-01-01'); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTimeImmutable::createFromMutable(new DateTime()); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTimeImmutable::createFromInterface(new DateTime()); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + MyDateTimeImmutable::createFromTimestamp(0); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Cannot instantiate abstract class MyDatePeriod +Cannot instantiate abstract class MyDateTime +Cannot instantiate abstract class MyDateTime +Cannot instantiate abstract class MyDateTime +Cannot instantiate abstract class MyDateTime +Cannot instantiate abstract class MyDateTimeImmutable +Cannot instantiate abstract class MyDateTimeImmutable +Cannot instantiate abstract class MyDateTimeImmutable +Cannot instantiate abstract class MyDateTimeImmutable