|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2006-10-20 16:07 UTC] tstarling at wikimedia dot org
Description:
------------
The stream created by fopen('php://stdin','r') has inappropriate ownership semantics. It closes the underlying FD when it is destroyed, despite the fact that it didn't open it. If you create two distinct streams which refer to the same FD, as demonstrated below, you can cause a double-close, which causes a segfault on Windows XP.
This may well be a regression caused by the fix of bug #38199
Reproduce code:
---------------
<?php
function foo() {
static $stdin;
$stdin = fopen( 'php://stdin', 'r' );
return fgets( $stdin );
}
print foo();
?>
Expected result:
----------------
FD 0 should not be closed.
Actual result:
--------------
You can see that FD 0 is closed using strace. In fact it is closed twice, once by the static variable destructor and once by the destructor of the STDIN constant.
[1546][tstarling@zwinger:~]$ strace -e trace=close php -n stdin-test.php
close(3) = 0
close(3) = 0
...
hello
hello
close(0) = 0
close(0) = -1 EBADF (Bad file descriptor)
Process 28429 detached
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Fri Oct 24 19:00:01 2025 UTC |
.. and the "double close" is actually much easier to reproduce with just: <?php $s = fopen("php://stdin", "r"); ?> On shutdown both $s and STDIN constant are destroyed, but they both point to the same resource. It's reproducible only with CLI, though.This patch is scheduled for PHP 5.2.1. Suggested workaround is to use the STDIN constant instead of explicitly opening php://stdin. Index: ext/standard/php_fopen_wrapper.c =================================================================== RCS file: /repository/php-src/ext/standard/php_fopen_wrapper.c,v retrieving revision 1.45.2.4.2.2 diff -u -p -r1.45.2.4.2.2 php_fopen_wrapper.c --- ext/standard/php_fopen_wrapper.c 5 Jul 2006 17:38:14 -0000 1.45.2.4.2.2 +++ ext/standard/php_fopen_wrapper.c 21 Oct 2006 22:13:22 -0000 @@ -191,11 +191,41 @@ php_stream * php_stream_url_wrap_php(php } if (!strcasecmp(path, "stdin")) { - fd = !strcmp(sapi_module.name, "cli") ? STDIN_FILENO : dup(STDIN_FILENO); + if (!strcmp(sapi_module.name, "cli")) { + static int cli_in = 0; + fd = STDIN_FILENO; + if (cli_in) { + fd = dup(fd); + } else { + cli_in = 1; + } + } else { + fd = dup(STDIN_FILENO); + } } else if (!strcasecmp(path, "stdout")) { - fd = !strcmp(sapi_module.name, "cli") ? STDOUT_FILENO : dup(STDOUT_FILENO); + if (!strcmp(sapi_module.name, "cli")) { + static int cli_out = 0; + fd = STDOUT_FILENO; + if (cli_out++) { + fd = dup(fd); + } else { + cli_out = 1; + } + } else { + fd = dup(STDOUT_FILENO); + } } else if (!strcasecmp(path, "stderr")) { - fd = !strcmp(sapi_module.name, "cli") ? STDERR_FILENO : dup(STDERR_FILENO); + if (!strcmp(sapi_module.name, "cli")) { + static int cli_err = 0; + fd = STDERR_FILENO; + if (cli_err++) { + fd = dup(fd); + } else { + cli_err = 1; + } + } else { + fd = dup(STDERR_FILENO); + } } else if (!strncasecmp(path, "filter/", 7)) { /* Save time/memory when chain isn't specified */ if (strchr(mode, 'r') || strchr(mode, '+')) {