Skip to content

Commit 0e557c4

Browse files
committed
stream: prohibit setting error mode in default context
1 parent 44deea5 commit 0e557c4

File tree

6 files changed

+105
-34
lines changed

6 files changed

+105
-34
lines changed

ext/standard/basic_functions.stub.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3376,7 +3376,10 @@ function soundex(string $string): string {}
33763376

33773377
/* streamsfuncs.c */
33783378

3379-
function stream_select(?array &$read, ?array &$write, ?array &$except, ?int $seconds, ?int $microseconds = null): int|false {}
3379+
/**
3380+
* @param resource|null $context
3381+
*/
3382+
function stream_select(?array &$read, ?array &$write, ?array &$except, ?int $seconds, ?int $microseconds = null, $context = null): int|false {}
33803383

33813384
/**
33823385
* @return resource
@@ -3479,17 +3482,19 @@ function stream_socket_shutdown($stream, int $mode): bool {}
34793482

34803483
#ifdef HAVE_SOCKETPAIR
34813484
/**
3485+
* @param resource|null $context
34823486
* @return array<int, resource>|false
34833487
* @refcount 1
34843488
*/
3485-
function stream_socket_pair(int $domain, int $type, int $protocol): array|false {}
3489+
function stream_socket_pair(int $domain, int $type, int $protocol, $context = null): array|false {}
34863490
#endif
34873491

34883492
/**
34893493
* @param resource $from
34903494
* @param resource $to
3495+
* @param resource|null $context
34913496
*/
3492-
function stream_copy_to_stream($from, $to, ?int $length = null, int $offset = 0): int|false {}
3497+
function stream_copy_to_stream($from, $to, ?int $length = null, int $offset = 0, $context = null): int|false {}
34933498

34943499
/**
34953500
* @param resource $stream
@@ -3560,8 +3565,11 @@ function stream_get_last_error(): ?StreamError {}
35603565
*/
35613566
function stream_get_transports(): array {}
35623567

3563-
/** @param resource|string $stream */
3564-
function stream_is_local($stream): bool {}
3568+
/**
3569+
* @param resource|string $stream
3570+
* @param resource|null $context
3571+
*/
3572+
function stream_is_local($stream, $context = null): bool {}
35653573

35663574
/** @param resource $stream */
35673575
function stream_isatty($stream): bool {}

ext/standard/basic_functions_arginfo.h

Lines changed: 8 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/standard/basic_functions_decl.h

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/standard/streamsfuncs.c

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,15 @@ PHP_FUNCTION(stream_socket_pair)
5050
zend_long domain, type, protocol;
5151
php_stream *s1, *s2;
5252
php_socket_t pair[2];
53+
zval *zcontext = NULL;
54+
php_stream_context *context = NULL;
5355

54-
ZEND_PARSE_PARAMETERS_START(3, 3)
56+
ZEND_PARSE_PARAMETERS_START(3, 4)
5557
Z_PARAM_LONG(domain)
5658
Z_PARAM_LONG(type)
5759
Z_PARAM_LONG(protocol)
60+
Z_PARAM_OPTIONAL
61+
Z_PARAM_RESOURCE_OR_NULL(zcontext)
5862
ZEND_PARSE_PARAMETERS_END();
5963

6064
if (0 != socketpair((int)domain, (int)type, (int)protocol, pair)) {
@@ -65,20 +69,21 @@ PHP_FUNCTION(stream_socket_pair)
6569
}
6670

6771
php_stream_error_operation_begin();
72+
context = php_stream_context_from_zval(zcontext, 0);
6873

6974
s1 = php_stream_sock_open_from_socket(pair[0], 0);
7075
if (s1 == NULL) {
7176
close(pair[0]);
7277
close(pair[1]);
73-
php_stream_error_operation_end(NULL);
78+
php_stream_error_operation_end(context);
7479
php_error_docref(NULL, E_WARNING, "Failed to open stream from socketpair");
7580
RETURN_FALSE;
7681
}
7782
s2 = php_stream_sock_open_from_socket(pair[1], 0);
7883
if (s2 == NULL) {
7984
php_stream_free(s1, PHP_STREAM_FREE_CLOSE);
8085
close(pair[1]);
81-
php_stream_error_operation_end(NULL);
86+
php_stream_error_operation_end(context);
8287
php_error_docref(NULL, E_WARNING, "Failed to open stream from socketpair");
8388
RETURN_FALSE;
8489
}
@@ -93,7 +98,7 @@ PHP_FUNCTION(stream_socket_pair)
9398
add_next_index_resource(return_value, s1->res);
9499
add_next_index_resource(return_value, s2->res);
95100

96-
php_stream_error_operation_end(NULL);
101+
php_stream_error_operation_end(context);
97102
}
98103
/* }}} */
99104
#endif
@@ -506,23 +511,27 @@ PHP_FUNCTION(stream_copy_to_stream)
506511
zend_long maxlen, pos = 0;
507512
bool maxlen_is_null = 1;
508513
size_t len;
514+
zval *zcontext = NULL;
515+
php_stream_context *context = NULL;
509516

510-
ZEND_PARSE_PARAMETERS_START(2, 4)
517+
ZEND_PARSE_PARAMETERS_START(2, 5)
511518
PHP_Z_PARAM_STREAM(src)
512519
PHP_Z_PARAM_STREAM(dest)
513520
Z_PARAM_OPTIONAL
514521
Z_PARAM_LONG_OR_NULL(maxlen, maxlen_is_null)
515522
Z_PARAM_LONG(pos)
523+
Z_PARAM_RESOURCE_OR_NULL(zcontext)
516524
ZEND_PARSE_PARAMETERS_END();
517525

518526
if (maxlen_is_null) {
519527
maxlen = PHP_STREAM_COPY_ALL;
520528
}
521529

522530
php_stream_error_operation_begin();
531+
context = php_stream_context_from_zval(zcontext, 0);
523532

524533
if (pos > 0 && php_stream_seek(src, pos, SEEK_SET) < 0) {
525-
php_stream_error_operation_end(NULL);
534+
php_stream_error_operation_end(context);
526535
php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", pos);
527536
RETURN_FALSE;
528537
}
@@ -532,7 +541,7 @@ PHP_FUNCTION(stream_copy_to_stream)
532541
} else {
533542
RETVAL_LONG(len);
534543
}
535-
php_stream_error_operation_end(NULL);
544+
php_stream_error_operation_end(context);
536545
}
537546
/* }}} */
538547

@@ -778,7 +787,7 @@ static int stream_array_emulate_read_fd_set(zval *stream_array)
778787
/* {{{ Runs the select() system call on the sets of streams with a timeout specified by tv_sec and tv_usec */
779788
PHP_FUNCTION(stream_select)
780789
{
781-
zval *r_array, *w_array, *e_array;
790+
zval *r_array, *w_array, *e_array, *zcontext = NULL;
782791
struct timeval tv, *tv_p = NULL;
783792
fd_set rfds, wfds, efds;
784793
php_socket_t max_fd = 0;
@@ -787,21 +796,24 @@ PHP_FUNCTION(stream_select)
787796
bool secnull;
788797
bool usecnull = 1;
789798
int set_count, max_set_count = 0;
799+
php_stream_context *context = NULL;
790800

791-
ZEND_PARSE_PARAMETERS_START(4, 5)
801+
ZEND_PARSE_PARAMETERS_START(4, 6)
792802
Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0)
793803
Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0)
794804
Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0)
795805
Z_PARAM_LONG_OR_NULL(sec, secnull)
796806
Z_PARAM_OPTIONAL
797807
Z_PARAM_LONG_OR_NULL(usec, usecnull)
808+
Z_PARAM_RESOURCE_OR_NULL(zcontext)
798809
ZEND_PARSE_PARAMETERS_END();
799810

800811
FD_ZERO(&rfds);
801812
FD_ZERO(&wfds);
802813
FD_ZERO(&efds);
803814

804815
php_stream_error_operation_begin();
816+
context = php_stream_context_from_zval(zcontext, 0);
805817

806818
if (r_array != NULL) {
807819
set_count = stream_array_to_fd_set(Z_ARR_P(r_array), &rfds, &max_fd);
@@ -825,7 +837,7 @@ PHP_FUNCTION(stream_select)
825837
}
826838

827839
if (!sets) {
828-
php_stream_error_operation_end(NULL);
840+
php_stream_error_operation_end(context);
829841
zend_value_error("No stream arrays were passed");
830842
RETURN_THROWS();
831843
}
@@ -836,7 +848,7 @@ PHP_FUNCTION(stream_select)
836848

837849
if (secnull && !usecnull) {
838850
if (usec != 0) {
839-
php_stream_error_operation_end(NULL);
851+
php_stream_error_operation_end(context);
840852
zend_argument_value_error(5, "must be null when argument #4 ($seconds) is null");
841853
RETURN_THROWS();
842854
}
@@ -845,11 +857,11 @@ PHP_FUNCTION(stream_select)
845857
/* If seconds is not set to null, build the timeval, else we wait indefinitely */
846858
if (!secnull) {
847859
if (sec < 0) {
848-
php_stream_error_operation_end(NULL);
860+
php_stream_error_operation_end(context);
849861
zend_argument_value_error(4, "must be greater than or equal to 0");
850862
RETURN_THROWS();
851863
} else if (usec < 0) {
852-
php_stream_error_operation_end(NULL);
864+
php_stream_error_operation_end(context);
853865
zend_argument_value_error(5, "must be greater than or equal to 0");
854866
RETURN_THROWS();
855867
}
@@ -866,7 +878,7 @@ PHP_FUNCTION(stream_select)
866878
if (r_array != NULL) {
867879
retval = stream_array_emulate_read_fd_set(r_array);
868880
if (retval > 0) {
869-
php_stream_error_operation_end(NULL);
881+
php_stream_error_operation_end(context);
870882
if (w_array != NULL) {
871883
zval_ptr_dtor(w_array);
872884
ZVAL_EMPTY_ARRAY(w_array);
@@ -880,7 +892,7 @@ PHP_FUNCTION(stream_select)
880892
}
881893

882894
retval = php_select(max_fd+1, &rfds, &wfds, &efds, tv_p);
883-
php_stream_error_operation_end(NULL);
895+
php_stream_error_operation_end(context);
884896

885897
if (retval == -1) {
886898
php_error_docref(NULL, E_WARNING, "Unable to select [%d]: %s (max_fd=" PHP_SOCKET_FMT ")",
@@ -1207,6 +1219,24 @@ PHP_FUNCTION(stream_context_get_default)
12071219
}
12081220
/* }}} */
12091221

1222+
/* Check if options contain stream error handling settings */
1223+
static bool php_stream_context_options_has_error_settings(const HashTable *options)
1224+
{
1225+
zval *stream_options = zend_hash_str_find(options, ZEND_STRL("stream"));
1226+
if (!stream_options) {
1227+
return false;
1228+
}
1229+
1230+
ZVAL_DEREF(stream_options);
1231+
if (Z_TYPE_P(stream_options) != IS_ARRAY) {
1232+
return false;
1233+
}
1234+
1235+
return zend_hash_str_exists(Z_ARRVAL_P(stream_options), ZEND_STRL("error_mode"))
1236+
|| zend_hash_str_exists(Z_ARRVAL_P(stream_options), ZEND_STRL("error_store"))
1237+
|| zend_hash_str_exists(Z_ARRVAL_P(stream_options), ZEND_STRL("error_handler"));
1238+
}
1239+
12101240
/* {{{ Set default file/stream context, returns the context as a resource */
12111241
PHP_FUNCTION(stream_context_set_default)
12121242
{
@@ -1222,6 +1252,11 @@ PHP_FUNCTION(stream_context_set_default)
12221252
}
12231253
context = FG(default_context);
12241254

1255+
if (php_stream_context_options_has_error_settings(options)) {
1256+
zend_value_error("Stream error handling options cannot be set on the default context");
1257+
RETURN_THROWS();
1258+
}
1259+
12251260
if (parse_context_options(context, options) == FAILURE) {
12261261
RETURN_THROWS();
12271262
}
@@ -1636,12 +1671,15 @@ PHP_FUNCTION(stream_resolve_include_path)
16361671
/* {{{ */
16371672
PHP_FUNCTION(stream_is_local)
16381673
{
1639-
zval *zstream;
1674+
zval *zstream, *zcontext = NULL;
16401675
php_stream *stream = NULL;
16411676
php_stream_wrapper *wrapper = NULL;
1677+
php_stream_context *context = NULL;
16421678

1643-
ZEND_PARSE_PARAMETERS_START(1, 1)
1679+
ZEND_PARSE_PARAMETERS_START(1, 2)
16441680
Z_PARAM_ZVAL(zstream)
1681+
Z_PARAM_OPTIONAL
1682+
Z_PARAM_RESOURCE_OR_NULL(zcontext)
16451683
ZEND_PARSE_PARAMETERS_END();
16461684

16471685
if (Z_TYPE_P(zstream) == IS_RESOURCE) {
@@ -1653,8 +1691,9 @@ PHP_FUNCTION(stream_is_local)
16531691
}
16541692

16551693
php_stream_error_operation_begin();
1694+
context = php_stream_context_from_zval(zcontext, 0);
16561695
wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(zstream), NULL, 0);
1657-
php_stream_error_operation_end(NULL);
1696+
php_stream_error_operation_end(context);
16581697
}
16591698

16601699
RETURN_BOOL(wrapper && wrapper->is_url == 0);

ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@ class TestStream {
2727

2828
stream_wrapper_register('test', 'TestStream');
2929

30-
function stream_test_errors($title, $context) {
31-
stream_context_set_default($context);
32-
$stream = fopen('test://foo', 'r', false);
30+
function stream_test_errors($title, $contextOptions) {
31+
$context = stream_context_create($contextOptions);
32+
$stream = fopen('test://foo', 'r', false, $context);
3333
try {
3434
echo $title . "\n";
35-
$readin = fopen('php://stdin', 'r');
35+
$readin = fopen('php://stdin', 'r', false, $context);
3636
$data = fread($stream, 10);
3737

3838
$read = [$readin, $stream];
3939
$write = NULL;
4040
$except = NULL;
41-
stream_select($read, $write, $except, 0);
41+
stream_select($read, $write, $except, 0, 0, $context);
4242
} catch (StreamException $e) {
4343
echo 'EXCEPTION: ' . $e->getMessage() . "\n";
4444
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Stream errors - prohibit setting error mode in default context
3+
--FILE--
4+
<?php
5+
6+
try {
7+
stream_context_set_default([
8+
'stream' => [
9+
'error_mode' => StreamErrorMode::Exception,
10+
]
11+
]);
12+
} catch (\ValueError $e) {
13+
echo $e->getMessage() . "\n";
14+
}
15+
16+
?>
17+
--EXPECT--
18+
Stream error handling options cannot be set on the default context

0 commit comments

Comments
 (0)