Skip to content

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
php:PHP-8.4from
true-async:stream-copy-close-in-callback
Open

EdmondDantes wants to merge 1 commit into
php:PHP-8.4from
true-async:stream-copy-close-in-callback

Conversation

@EdmondDantes

Copy link
Copy Markdown
Contributor

_php_stream_copy_to_stream_ex() reads from src and writes to dest 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 (segfault, or an assertion in php_stream_memory_read() on a debug build).

The fix sets PHP_STREAM_FLAG_NO_FCLOSE on both streams for the duration of the copy, the same protection a stream already gets while its user filter runs, so such an fclose() fails with the existing warning. Each stream's original flag is restored afterwards (both are read before either is set, so src == dest is 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() and closedir() free a stream without checking PHP_STREAM_FLAG_NO_FCLOSE (an opendir() handle can be the source of a copy).

…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.
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.

1 participant