Repository navigation
Use-after-free when a user wrapper closes the other stream during stream_copy_to_stream() - #24169
Open
EdmondDantes wants to merge 1 commit into
Open
EdmondDantes wants to merge 1 commit into
EdmondDantes wants to merge 1 commit into
Conversation
…am() _php_stream_copy_to_stream_ex() writes to dest and reads from src in a loop. When either is a user wrapper, its stream_write() or stream_read() may fclose() the other stream, and the next turn of the loop uses the freed stream. Both streams now carry PHP_STREAM_FLAG_NO_FCLOSE for the duration of the copy, as a stream does while its user filter runs, so such an fclose() fails with a warning. The original flag of each stream is restored afterwards.
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.
_php_stream_copy_to_stream_ex()reads fromsrcand writes todestin a loop. When either is a user wrapper, itsstream_write()orstream_read()mayfclose()the other stream, and the next turn of the loop uses the freed stream (segfault, or an assertion inphp_stream_memory_read()on a debug build).The fix sets
PHP_STREAM_FLAG_NO_FCLOSEon both streams for the duration of the copy, the same protection a stream already gets while its user filter runs, so such anfclose()fails with the existing warning. Each stream's original flag is restored afterwards (both are read before either is set, sosrc == destis fine). The body moved into a static helper so that none of its returns needed touching.Test:
ext/standard/tests/streams/stream_copy_to_stream_close_in_callback.phpt, both directions. It segfaults without the fix.Known limit, left for a separate change:
pclose(),proc_close()andclosedir()free a stream without checkingPHP_STREAM_FLAG_NO_FCLOSE(anopendir()handle can be the source of a copy).