Skip to content

date: Fix unserialization of Time\Duration - #23629

Open
TimWolla wants to merge 1 commit into
php:masterfrom
TimWolla:time-duration-unserialize
Open

date: Fix unserialization of Time\Duration#23629
TimWolla wants to merge 1 commit into
php:masterfrom
TimWolla:time-duration-unserialize

Conversation

@TimWolla

@TimWolla TimWolla commented Sep 9, 2026

Copy link
Copy Markdown
Member

No description provided.

Comment thread ext/date/time_duration.c
zval *seconds = OBJ_PROP_NUM(&duration->std, 0);
zval *nanoseconds = OBJ_PROP_NUM(&duration->std, 1);
zval *negative = OBJ_PROP_NUM(&duration->std, 2);
/* object_properties_load() does not type check. We need to do this ourselves. */

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is #9708.

@TimWolla
TimWolla force-pushed the time-duration-unserialize branch from f3c7903 to 170fb17 Compare September 9, 2026 15:50

@derickr derickr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread ext/date/time_duration.c
zval *negative = OBJ_PROP_NUM(&duration->std, 2);
/* object_properties_load() does not type check. We need to do this ourselves. */
if (Z_TYPE_P(seconds) != IS_LONG || Z_TYPE_P(nanoseconds) != IS_LONG || (Z_TYPE_P(negative) != IS_FALSE && Z_TYPE_P(negative) != IS_TRUE)) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(duration->std.ce->name));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you typed check, after the properties have been overwritten, but that leaves the object in an unexpected type-violating state AFAICT?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep in mind people can call ->__unserialize manually BTW.

@TimWolla TimWolla Sep 9, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but that leaves the object in an unexpected type-violating state AFAICT?

That is correct, but I don't think this situation is observable in any way: Duration is a final readonly internal class, thus the engine prevents constructing objects without going through the constructor (or unserialization).

There are thus two cases to reach __unserialize():

  1. Manually calling ->__unserialize() on an existing object: This is guaranteed to fail due to object_properties_load() rejecting the reassignment of the readonly properties.
  2. Unserializing a payload.

In the second case, the types could mismatch after object_properties_load(), but I don't think there is a way this broken object can be observed. It will cleanly be destructed at the end of the failed unserialize() call (which is save since Duration doesn't have a custom destructor and particularly no destructor that accesses the properties).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants