diff --git a/ext/standard/tests/streams/stream_context_set_option_no_default_leak.phpt b/ext/standard/tests/streams/stream_context_set_option_no_default_leak.phpt new file mode 100644 index 000000000000..788b6d8a4676 --- /dev/null +++ b/ext/standard/tests/streams/stream_context_set_option_no_default_leak.phpt @@ -0,0 +1,29 @@ +--TEST-- +stream_context_set_option() on a context-less stream must not leak into the default context +--FILE-- + +--EXPECT-- +array(0) { +} +array(0) { +} +array(1) { + ["http"]=> + array(1) { + ["filename"]=> + string(8) "test.txt" + } +} diff --git a/main/streams/streams.c b/main/streams/streams.c index 715bbcfe0371..171748e6a08a 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -2248,7 +2248,12 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod stream->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename; stream->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno; #endif - if (stream->ctx == NULL && context != NULL && !persistent) { + /* Attach an explicitly provided context to the stream, but never the + * default context: sharing it by reference would let a later + * stream_context_set_option() on the stream mutate the global default + * context, leaking options into every other stream. Stream errors fall + * back to the default context on their own when the stream has none. */ + if (stream->ctx == NULL && context != NULL && context != FG(default_context) && !persistent) { php_stream_context_set(stream, context); } }