deepclone_from_array: reject building an uninitialized object#22
Merged
Conversation
deepclone_to_array() always emits a class that has __unserialize() as a negative-wakeup state replay. A hand-crafted payload that instead flags such a class for plain creation (wakeup >= 0, no replay) made from_array() build a bare object_init_ex() shell that __unserialize() never initialized — for BcMath\Number that shell has a NULL bc_num, so any operation on it crashed. Reject such a payload with a ValueError before creating the object, so no uninitialized shell is ever built. A php-src guard for the bcmath case (php/php-src#22259) was declined: an uninitialized BcMath\Number cannot be reached from userland, so the extension must not produce one. The test that exercised the crash path now asserts the rejection instead.
nicolas-grekas
added a commit
to symfony/polyfill
that referenced
this pull request
Jun 10, 2026
…ninitialized object (nicolas-grekas) This PR was merged into the 1.x branch. Discussion ---------- [DeepClone] Reject from_array payloads that would build an uninitialized object | Q | A | ------------- | --- | Branch? | 1.x | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | - | License | MIT Brings the polyfill in line with the extension's symfony/php-ext-deepclone#22. `deepclone_to_array()` always emits a class that has `__unserialize()` as a negative-wakeup state replay. A crafted `deepclone_from_array()` payload that flags such a class for plain creation (`wakeup >= 0`, no replay) was reconstructed as an uninitialized object: the polyfill returned `null`, while the extension (before #22) built a bare shell, e.g. a `BcMath\Number` whose `bc_num` stays `NULL` and crashes on use. `reconstruct()` now rejects such a payload with a `\ValueError` before building the object, using the same message as the extension. Well-formed payloads, which always carry the negative-wakeup replay for an `__unserialize` class, are unaffected. Commits ------- 3791449 [DeepClone] Reject from_array payloads that would build an uninitialized object
symfony-splitter
pushed a commit
to symfony/polyfill-deepclone
that referenced
this pull request
Jun 10, 2026
…zed object deepclone_to_array() always emits a class that has __unserialize() as a negative-wakeup state replay. A crafted deepclone_from_array() payload that flags such a class for plain creation (wakeup >= 0, no replay) was reconstructed as an uninitialized object: the polyfill returned null. reconstruct() now rejects such a payload with a \ValueError before building the object, matching the extension (symfony/php-ext-deepclone#22). Well-formed payloads always carry the negative-wakeup replay for an __unserialize class, so they are unaffected.
IonBazan
pushed a commit
to IonBazan/polyfill
that referenced
this pull request
Jun 16, 2026
…zed object deepclone_to_array() always emits a class that has __unserialize() as a negative-wakeup state replay. A crafted deepclone_from_array() payload that flags such a class for plain creation (wakeup >= 0, no replay) was reconstructed as an uninitialized object: the polyfill returned null. reconstruct() now rejects such a payload with a \ValueError before building the object, matching the extension (symfony/php-ext-deepclone#22). Well-formed payloads always carry the negative-wakeup replay for an __unserialize class, so they are unaffected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
deepclone_to_array()always emits a class that has__unserialize()as a negative-wakeup state replay. A hand-craftedfrom_array()payload that instead flags such a class for plain creation (wakeup >= 0, no replay) made the decoder build a bareobject_init_ex()shell that__unserialize()never initialized. ForBcMath\Numberthat shell has aNULLbc_num, so(string),clone, arithmetic, etc. crash — a crafted-payload segfault, surfaced by the v0.6.1BcMath\Numbertest on PHP 8.4+ (where there is no engine guard).from_array()now rejects such a payload with a\ValueErrorbefore the object is created, so no uninitialized shell is ever built. Well-formed payloads (which always carry the negative-wakeup replay for an__unserializeclass) are unaffected.Context: the original test asserted a php-src-level guard message, but that guard (php/php-src#22259) was declined — an uninitialized
BcMath\Numberis unreachable from userland, so php-src won't add code to work around an extension creating one. The fix belongs here: the extension must not produce invalid objects. The test now asserts the\ValueErrorrejection, which holds on every PHP version with no guard dependency.Verified the full suite against a stock (unguarded) PHP build. Companion to #20 / symfony/symfony#64323.