Fix GH-8952: std streams can not be deliberately closed (#8953)

This commit is contained in:
Arnaud Le Blanc 2022-07-09 22:58:02 +02:00 committed by GitHub
parent 5e0b2e537d
commit 2dbde18b29
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 148 additions and 8 deletions

View file

@ -538,14 +538,6 @@ static void cli_register_file_handles(bool no_close) /* {{{ */
s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out); s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out);
s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err); s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err);
/* Release stream resources, but don't free the underlying handles. Othewrise,
* extensions which write to stderr or company during mshutdown/gshutdown
* won't have the expected functionality.
*/
if (s_in) s_in->flags |= PHP_STREAM_FLAG_NO_CLOSE;
if (s_out) s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
if (s_err) s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
if (s_in==NULL || s_out==NULL || s_err==NULL) { if (s_in==NULL || s_out==NULL || s_err==NULL) {
if (s_in) php_stream_close(s_in); if (s_in) php_stream_close(s_in);
if (s_out) php_stream_close(s_out); if (s_out) php_stream_close(s_out);
@ -553,6 +545,12 @@ static void cli_register_file_handles(bool no_close) /* {{{ */
return; return;
} }
if (no_close) {
s_in->flags |= PHP_STREAM_FLAG_NO_CLOSE;
s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
}
s_in_process = s_in; s_in_process = s_in;
php_stream_to_zval(s_in, &ic.value); php_stream_to_zval(s_in, &ic.value);

View file

@ -0,0 +1,38 @@
--TEST--
std handles can be deliberately closed 001
--SKIPIF--
<?php
if (php_sapi_name() != "cli") {
die("skip CLI only");
}
if (PHP_OS_FAMILY == 'Windows') {
die("skip not for Windows");
}
if (PHP_DEBUG) {
die("skip std streams are not closeable in debug builds");
}
if (getenv('SKIP_REPEAT')) {
die("skip cannot be repeated");
}
?>
--FILE--
<?php
print "STDIN:\n";
fclose(STDIN);
var_dump(@fopen('php://stdin', 'r'));
print "STDERR:\n";
fclose(STDERR);
var_dump(@fopen('php://stderr', 'a'));
print "STDOUT:\n";
fclose(STDOUT);
// not printed if stdout is closed
var_dump(@fopen('php://stdout', 'a'));
?>
--EXPECT--
STDIN:
bool(false)
STDERR:
bool(false)
STDOUT:

View file

@ -0,0 +1,47 @@
--TEST--
std handles can be deliberately closed 002
--SKIPIF--
<?php
if (php_sapi_name() != "cli") {
die("skip CLI only");
}
if (PHP_OS_FAMILY == 'Windows') {
die("skip not for Windows");
}
if (PHP_DEBUG) {
die("skip std streams are not closeable in debug builds");
}
if (getenv('SKIP_REPEAT')) {
die("skip cannot be repeated");
}
?>
--FILE--
<?php
$stdin = fopen('php://stdin', 'r');
$stdout = fopen('php://stdout', 'r');
$stderr = fopen('php://stderr', 'r');
ob_start(function ($buffer) use ($stdout) {
fwrite($stdout, $buffer);
}, 1);
print "STDIN:\n";
fclose(STDIN);
var_dump(@fopen('php://stdin', 'r'));
print "STDERR:\n";
fclose(STDERR);
var_dump(@fopen('php://stderr', 'a'));
print "STDOUT:\n";
fclose(STDOUT);
var_dump(@fopen('php://stdout', 'a'));
?>
--EXPECT--
STDIN:
bool(false)
STDERR:
bool(false)
STDOUT:
bool(false)

View file

@ -0,0 +1,57 @@
--TEST--
std handles can be deliberately closed 003
--SKIPIF--
<?php
if (php_sapi_name() != "cli") {
die("skip CLI only");
}
if (PHP_OS_FAMILY == 'Windows') {
die("skip not for Windows");
}
if (PHP_DEBUG) {
die("skip std streams are not closeable in debug builds");
}
if (getenv('SKIP_REPEAT')) {
die("skip cannot be repeated");
}
?>
--FILE--
<?php
$stdoutStream = fopen('php://stdout', 'r');
$stdoutFile = tempnam(sys_get_temp_dir(), 'gh8827');
$stderrFile = tempnam(sys_get_temp_dir(), 'gh8827');
register_shutdown_function(function () use ($stdoutFile, $stderrFile) {
unlink($stdoutFile);
unlink($stderrFile);
});
fclose(STDOUT);
fclose(STDERR);
$stdoutFileStream = fopen($stdoutFile, 'a');
$stderrFileStream = fopen($stderrFile, 'a');
print "Goes to stdoutFile\n";
file_put_contents('php://fd/1', "Also goes to stdoutFile\n");
file_put_contents('php://fd/2', "Goes to stderrFile\n");
ob_start(function ($buffer) use ($stdoutStream) {
fwrite($stdoutStream, $buffer);
}, 1);
print "stdoutFile:\n";
readfile($stdoutFile);
print "stderrFile:\n";
readfile($stderrFile);
?>
--EXPECT--
stdoutFile:
Goes to stdoutFile
Also goes to stdoutFile
stderrFile:
Goes to stderrFile