Repository navigation
[8.6 regression] Fix UAF of the default stream context in the stream error code - #25321
Conversation
FG(default_context) is a request resource that zend_close_rsrc_list() frees at request shutdown, but the pointer is only reset in the RINIT of ext/standard. php_stream_error_operation_end() falls back to FG(default_context) when no context is given, and sapi_read_standard_form_data() buffers the POST body before RINIT, so when the temporary file for a body over 16 KiB cannot be created, the error path reads the previous request's freed context. Clear FG(default_context) when its resource is destroyed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LUfkv6dbRLGQe2UL9SsR2y
|
@DanielEScherzer @mbeccati @svpernova09 Could this be pulled into PHP-8.6.0 for GA? It fixes a use-after-free introduced in 8.6 by the stream error API (f2b371e). A POST body over 16 KiB that fails to spill to a temp file reports an error before RINIT, which falls back to As 8.6 has no stable release yet, this is low severity under the security classification policy, so I filed it publicly. I'd like it to land before GA so it never ships in a stable release. |
|
(For |
mbeccati
left a comment
There was a problem hiding this comment.
The changes are minor, and if there are no issues, I will merge them into PHP-8.6.0 and PHP-8.6, master.
Is it necessary to include a note in the NEWS file for RC users?
Yes please, go ahead with the merge and the appropriate NEWS entry, as soon as possible.
For PHP-8.6.0, we may need to re-tag some items
We will take care of cherry-picking into the PHP-8.6.0 branch, if that's what you mean.
|
@mbeccati I've merged it up to master in 3cb920a, leaving the entry out of master's NEWS. As was done then, could you move it to the 8.6.0 section when cherry-picking it into PHP-8.6.0? |
…25321) * Fix use of the freed default stream context by the stream error code FG(default_context) is a request resource that zend_close_rsrc_list() frees at request shutdown, but the pointer is only reset in the RINIT of ext/standard. php_stream_error_operation_end() falls back to FG(default_context) when no context is given, and sapi_read_standard_form_data() buffers the POST body before RINIT, so when the temporary file for a body over 16 KiB cannot be created, the error path reads the previous request's freed context. Clear FG(default_context) when its resource is destroyed.
Found while reworking #22730.
FG(default_context)is freed with the request's resources, but the pointer is only reset in ext/standard's RINIT. The new stream error code (8.6+) falls back to it when no context is given, so an error raised before RINIT, for example a POST body over 16 KiB that cannot be spooled, reads the previous request's freed context: ASan reports a heap-use-after-free, a release build dies with SIGSEGV.Clear
FG(default_context)when its resource is destroyed. OnlyPHP-8.6andmasterhave the reader, 8.5 and earlier are not affected.Test:
sapi/cgi/tests/stream_error_default_context.phptfails before the change and passes after it.PoC:
gdb:
Debug + ASan build of the same head (
USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1):