Fix use-after-free serializing an array grown by an element's hook#22714
Open
iliaal wants to merge 1 commit into
Open
Fix use-after-free serializing an array grown by an element's hook#22714iliaal wants to merge 1 commit into
iliaal wants to merge 1 commit into
Conversation
The IS_ARRAY case of php_var_serialize_intern() walked the array's HashTable without holding a reference across php_var_serialize_nested_data(), which recurses into user hooks (__serialize, __sleep, Serializable::serialize). A hook that grows the same array through a by-reference alias reallocs the backing store mid-walk, so the iterator reads freed memory. Hold a ref across the walk, as the object path and var_dump/var_export already do, so the append separates a copy instead of reallocating in place.
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.
The IS_ARRAY case of php_var_serialize_intern() walks the array's HashTable without holding a reference while php_var_serialize_nested_data() recurses into user hooks, so a __serialize() that grows the same array through a by-reference alias reallocs the backing store mid-walk and the iterator reads freed memory. The object case, var_dump, and var_export already hold a ref across the walk; this adds the same GC_ADDREF/GC_DTOR_NO_REF for arrays, so the append separates a copy instead of reallocating in place. __sleep() and Serializable::serialize() reach the same walk, so one test covers all three.
Reproducer (invalid read in php_var_serialize_nested_data under ASan/valgrind; a normal build usually reads the freed memory and passes, so the test is an ASan canary):