Skip to content

Reject classes that keep their state internally in deepclone_from_array() - #53

Merged
nicolas-grekas merged 1 commit into
mainfrom
from-array-internal-state
Sep 29, 2026
Merged

nicolas-grekas merged 1 commit into
mainfrom
from-array-internal-state

Conversation

@nicolas-grekas

@nicolas-grekas nicolas-grekas commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

Builds on #50 and #52.

deepclone_to_array() and deepclone_hydrate() reject the internal classes that keep their state out of their properties and declare no serialization API, like Redis, Imagick or the AMQP and Relay classes, but deepclone_from_array() created them when a payload named them, eg one produced by the polyfill. unserialize() creates them that way too, and some crash PHP then: RedisCluster::acl() or ImagickDraw::affine() segfault on such objects, and a Relay\Table does when destroyed.

The class lookup of deepclone_from_array() now applies the same rule, which the three functions share in one helper, and throws NotInstantiableException. User classes that extend such a class without declaring a serialization API are rejected too, in all three functions: a subclass of RedisCluster crashed the same way. For user classes, that's one more flag test per class and call. IteratorIterator and the other internal iterators wrapping another one are rejected too, where #50 created them like unserialize() does, and so are their user subclasses. Payloads that carry serialized strings still create whatever unserialize() creates from them, unless $allowed_classes excludes it.

The polyfill gets the same change in symfony/polyfill#708, for the classes of the extensions it knows, as it can't tell which classes keep their state internally.

@stof

stof commented Sep 29, 2026

Copy link
Copy Markdown
Member

unserialize() creates them that way too, and some crash PHP then: RedisCluster::acl() or ImagickDraw::affine() segfault on such objects, and a Relay\Table does when destroyed.

have you reported this to php-src so that a fix is implemented ?

@nicolas-grekas

Copy link
Copy Markdown
Member Author

That's for the extensions to fix, likely with a @not-serializable flag

@stof

stof commented Sep 29, 2026

Copy link
Copy Markdown
Member

ah, I missed that those are about non-core extensions. So adapted question: have you reported it to the extension maintainers ?

…ay()

deepclone_to_array() and deepclone_hydrate() reject the internal classes
that keep their state out of their properties and declare no
serialization API, like Redis, Imagick or the AMQP and Relay classes,
but deepclone_from_array() created them when a payload named them, eg
one produced by the polyfill. unserialize() creates them that way too,
without their state, and some crash PHP then: RedisCluster::acl() or
ImagickDraw::affine() segfault on such objects, and a Relay\Table does
when destroyed.

The class lookup of deepclone_from_array() now applies the same rule,
which the three functions share in one helper, and throws
NotInstantiableException. User classes that extend such a class without
declaring a serialization API are rejected too, eg a subclass of
RedisCluster, which crashed the same way, in all three functions. That's
one more flag test per class and call for user classes. IteratorIterator
and the other internal iterators wrapping another one are rejected too,
instead of being created like unserialize() does, and so are their user
subclasses.
@nicolas-grekas
nicolas-grekas force-pushed the from-array-internal-state branch from a9cce4c to f7b2c3c Compare September 29, 2026 11:07
@nicolas-grekas
nicolas-grekas merged commit d36742f into main Sep 29, 2026
24 checks passed
@nicolas-grekas
nicolas-grekas deleted the from-array-internal-state branch September 29, 2026 11:29
@nicolas-grekas

Copy link
Copy Markdown
Member Author

Done: phpredis/phpredis#2938, Imagick/imagick#800, php-amqp/php-amqp#640, krakjoe/apcu#633, msgpack/msgpack-php#190, php-memcached-dev/php-memcached#583, and cachewerk/relay#226 since Relay is closed source.
Once released, deepclone rejects these classes through the flag, subclasses included. The rule of this PR stays for older versions and other extensions.

nicolas-grekas added a commit to symfony/polyfill that referenced this pull request Sep 29, 2026
…te out of their properties (nicolas-grekas)

This PR was merged into the 1.x branch.

Discussion
----------

[DeepClone] Reject more internal classes that keep their state out of their properties

| Q             | A
| ------------- | ---
| Branch?       | 1.x
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        | -
| License       | MIT

Builds on #703.

The extension rejects the internal classes that keep their state out of their properties and declare no serialization API, in `deepclone_to_array()`, `deepclone_hydrate()` and, with symfony/php-ext-deepclone#53, `deepclone_from_array()`. The polyfill can't see which classes do so and knew only the ones of zip, xmlreader, xmlwriter, snmp and tidy: it exported a `Redis` or an `Imagick` without its state, and `deepclone_from_array()` created them, which can crash PHP, eg when a bare `Relay\Table` is destroyed or a clone of a bare `AMQPConnection` is dumped. It now also knows the classes of the redis, relay, imagick, amqp, apcu and msgpack extensions, and rejects them before creating a prototype, together with their user subclasses that declare no serialization API. Classes of other extensions stay accepted.

Like the extension, `deepclone_from_array()` now also rejects the internal classes whose state `serialize()` loses, like `IteratorIterator`, as `deepclone_hydrate()` already did, and both reject their user subclasses too, unless they declare a serialization API.

Commits
-------

14a31d4 [DeepClone] Reject more internal classes that keep their state out of their properties
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants