Skip to content

Commit 92128ac

Browse files
Fix stream_context_set_option() mutating the default context (#22235)
Since GH-20524, _php_stream_open_wrapper_ex() attaches the context to a stream that has none, including the implicitly substituted default context. Sharing the default context by reference let a later stream_context_set_option() on the stream mutate the global default context, leaking options into every other context-less stream. Only attach explicitly provided contexts. Stream errors already fall back to the default context when the stream has none, so error handling is unaffected.
1 parent 0d6f941 commit 92128ac

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
stream_context_set_option() on a context-less stream must not leak into the default context
3+
--FILE--
4+
<?php
5+
$a = fopen('php://memory', 'r+');
6+
stream_context_set_option($a, 'http', 'filename', 'test.txt');
7+
8+
// The default context must stay untouched.
9+
var_dump(stream_context_get_options(stream_context_get_default()));
10+
11+
// A later context-less stream must not inherit the option.
12+
$b = fopen('php://memory', 'r+');
13+
var_dump(stream_context_get_options($b));
14+
15+
// The stream the option was set on keeps it for itself.
16+
var_dump(stream_context_get_options($a));
17+
?>
18+
--EXPECT--
19+
array(0) {
20+
}
21+
array(0) {
22+
}
23+
array(1) {
24+
["http"]=>
25+
array(1) {
26+
["filename"]=>
27+
string(8) "test.txt"
28+
}
29+
}

main/streams/streams.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2248,7 +2248,12 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod
22482248
stream->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename;
22492249
stream->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno;
22502250
#endif
2251-
if (stream->ctx == NULL && context != NULL && !persistent) {
2251+
/* Attach an explicitly provided context to the stream, but never the
2252+
* default context: sharing it by reference would let a later
2253+
* stream_context_set_option() on the stream mutate the global default
2254+
* context, leaking options into every other stream. Stream errors fall
2255+
* back to the default context on their own when the stream has none. */
2256+
if (stream->ctx == NULL && context != NULL && context != FG(default_context) && !persistent) {
22522257
php_stream_context_set(stream, context);
22532258
}
22542259
}

0 commit comments

Comments
 (0)