Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed STDIN returns endless stream of random data #81

Closed
clue opened this issue Mar 14, 2017 · 11 comments
Closed

Closed STDIN returns endless stream of random data #81

clue opened this issue Mar 14, 2017 · 11 comments

Comments

@clue
Copy link
Member

@clue clue commented Mar 14, 2017

While working on #37, I've tested the following simple and quite common code to check possible combinations of readable/writable streams and closing/ending either side:

$stdout = new Stream(STDOUT, $loop);
$stdout->pause();

$stdin = new Stream(STDIN, $loop);
$stdin->pipe($stdout);

Here's what works as expected:

$ php test.php #interactive STDIO
$ echo test | test.php # STDIN ends once receiving input
$ true | php test.php # STDIN ends immediately
$ php test.php < /dev/null # STDIN ends immediately
$ php test.php < /dev/zero # endless STDIN stream
$ php test.php < /dev/urandom #endless STDIN stream
$ php test.php > /dev/null # STDOUT closes when trying to write, thus pausing STDIN

Here's what does not work (on most of my systems, more below):

$ php test.php <&- # no STDIN should error and/or close
$ php test.php >&- # no STDOUT should error and/or close when trying to write

It turns out explicitly passing no handle actually results in a default stream for all STDIO streams (STDIN, STDOUT and STERR). Arguably, this may be a sane default behavior if this default stream would follow expected behavior for closed streams, but unfortunately it does not.

Unfortunately, this default stream behaves just like an endless stream of random data. In fact, it actually is /dev/urandom. This can be verified by checking /proc/{PID}/fd. And it turns out this is not in fact related to ReactPHP at all and can be reproduced with the most simple PHP scripts.

Here's a very simple gist to reproduce this output:

$ LANG=en php -r 'var_dump(!!fstat(STDIN));passthru("ls -o /proc/".getmypid()."/fd");' <&-

This SHOULD show false (because STDIN is not a valid stream) and SHOULD not include file descriptor 0 in the listing.

Note that I could reproduce this in multiple setups, but have seen other setups where this does not occur. As such, I'm opening this ticket to gather more information on why this happens and how this could possibly be avoided.

Unless I'm missing something obvious, it looks like this may boil down to an issue in PHP itself, but I'd rather collect more evidence first.

@clue
Copy link
Member Author

@clue clue commented Mar 14, 2017

PHP 7.1.2 from Docker php:7.1
STDIN defaults to dev/urandom

PHP 7.0.16 from Docker php:7.0
STDIN defaults to dev/urandom

PHP 7.0.15 on Ubuntu 16.04:
STDIN defaults to /dev/urandom

PHP 5.6.8 from Docker php:5.6
STDIN defaults to dev/urandom

PHP 5.5.38 from Docker php:5.5
STDIN defaults to dev/urandom

PHP 5.4.45 from Docker php:5.4
STDIN defaults to dev/urandom

PHP 5.3.29 from Docker php:5.3
STDIN is closed

PHP 5.3.10 on Ubuntu 12.04:
STDIN is closed

@legionth
Copy link

@legionth legionth commented Mar 14, 2017

I executed the gist and got the following:

$ LANG=en php -r 'var_dump(!!fstat(STDIN));passthru("ls -o /proc/".getmypid()."/fd");' <&-
Command line code:1:
bool(false)
total 0
lr-x------ 1 legionth 64 Mar 14 15:40 0 -> pipe:[174916]
lrwx------ 1 legionth 64 Mar 14 15:40 1 -> /dev/pts/17
lrwx------ 1 legionth 64 Mar 14 15:40 2 -> /dev/pts/17

This looks good to me, nothing about dev/urandom. 🤔

Executed this with this version:

$ php --version
PHP 7.0.8-0ubuntu0.16.04.3 (cli) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.0.8-0ubuntu0.16.04.3, Copyright (c) 1999-2016, by Zend Technologies
    with Xdebug v2.4.0, Copyright (c) 2002-2016, by Derick Rethans
@kelunik
Copy link

@kelunik kelunik commented Mar 14, 2017

Just tried this with Amp, it does indeed dump random data with the NativeDriver.

<?php

require "vendor/autoload.php";

use Amp\Loop;

// Loop::set(new Loop\NativeDriver);

Loop::run(function () {
    Loop::onReadable(STDIN, function ($watcherId) {
        var_dump("READ", fread(STDIN, 8192));
        var_dump("EOF", feof(STDIN));

        if (feof(STDIN)) {
            Loop::cancel($watcherId);
        }
    });
});

EvDriver shows the same behavior. However, this doesn't happen with uv nor event.

UvDriver fails with the following error:

Fatal error:  uv_poll_init_socket(): uv_poll_init failed in UvDriver.php on line 182

EventDriver fails with the following error:

Warning:  Event::add(): Epoll ADD(1) on fd 0 failed.  Old events were 0; read change was 1 (add); write change was 0 (none): Operation not permitted in EventDriver.php on line 238
Warning:  Event::add(): Failed adding event in EventDriver.php on line 238
@clue
Copy link
Member Author

@clue clue commented Mar 14, 2017

@kelunik This looks like you're affected by the same issue. Can you report the PHP version and the output of the gist as given above?

It looks like this may in fact be a PHP issue, but we have yet to dive deeper. Some systems seem to be not affected (see @legionth's output).

In case you're affected, this really affects the most simple scripts, you can really confuse any interactive CLI tool, like psysh, boris, those building on top of Symfony console etc. etc.

@kelunik
Copy link

@kelunik kelunik commented Mar 14, 2017

LANG=en php -r 'var_dump(!!fstat(STDIN));passthru("ls -o /proc/".getmypid()."/fd");' <&-
bool(true)
total 0
lr-x------ 1 kelunik 64 Mar 14 15:54 0 -> /dev/urandom
lrwx------ 1 kelunik 64 Mar 14 15:54 1 -> /dev/pts/1
lrwx------ 1 kelunik 64 Mar 14 15:54 2 -> /dev/pts/1
lr-x------ 1 kelunik 64 Mar 14 15:54 3 -> pipe:[5172278]

Running on PHP 7.1.3RC1. I don't think this is related to PHP, but probably rather the underlying system. I'm on Ubuntu 16.04.2 LTS.

@kelunik
Copy link

@kelunik kelunik commented Mar 14, 2017

Ok, that's not true, it's not done by the system, (1) because Uv and Event wouldn't fail in that case and (2) because head -c 8 <&- errors out like it should.

@kelunik
Copy link

@kelunik kelunik commented Mar 14, 2017

@clue FD 0 just gets reused. As 0 isn't used yet, that's taken.

┌[kelunik@kelunik] - [~/GitHub/php/php-src] - [16:11:04] - [master]
└[5415] $ LANG=en php -r 'var_dump(!!fstat(STDIN));passthru("ls -o /proc/".getmypid()."/fd");' <&-
bool(true)
total 0
lr-x------ 1 kelunik 64 Mar 14 16:13 0 -> /dev/urandom
lrwx------ 1 kelunik 64 Mar 14 16:13 1 -> /dev/pts/1
lrwx------ 1 kelunik 64 Mar 14 16:13 2 -> /dev/pts/1
lr-x------ 1 kelunik 64 Mar 14 16:13 3 -> pipe:[5183812]

┌[kelunik@kelunik] - [~/GitHub/php/php-src] - [16:13:07] - [master]
└[5416] $ LANG=en php -r 'var_dump(!!fstat(STDIN));passthru("ls -o /proc/".getmypid()."/fd");'
bool(true)
total 0
lrwx------ 1 kelunik 64 Mar 14 16:13 0 -> /dev/pts/1
lrwx------ 1 kelunik 64 Mar 14 16:13 1 -> /dev/pts/1
lrwx------ 1 kelunik 64 Mar 14 16:13 2 -> /dev/pts/1
lr-x------ 1 kelunik 64 Mar 14 16:13 3 -> /dev/urandom
lr-x------ 1 kelunik 64 Mar 14 16:13 4 -> pipe:[5183821]
@kelunik
Copy link

@kelunik kelunik commented Mar 14, 2017

This is due to https://github.com/php/php-src/blob/16ae9f82e82e2aea5d7deaf8f9a9c825a56dfcc1/ext/standard/php_fopen_wrapper.c#L265 just duplicating instead of registering STDIN as an already closed stream. Thanks to @bwoebi for finding the right place.

@clue
Copy link
Member Author

@clue clue commented Mar 26, 2017

Thanks to @kelunik for reporting this upstream: https://bugs.php.net/bug.php?id=74252

It looks like there's little we can do about this on our end, so we may as well document and close this? 👍

@clue
Copy link
Member Author

@clue clue commented Nov 1, 2017

For the reference only: I'm no longer able to reproduce this issue with a default PHP installation on Ubuntu (PHP 7.0.22-0ubuntu0.17.04.1):

$ LANG=en php -r 'var_dump(!!fstat(STDIN));passthru("ls -o /proc/".getmypid()."/fd");' <&-
Command line code:1:
bool(false)
total 0
lr-x------ 1 me 64 Nov  1 13:29 0 -> pipe:[58524]
lrwx------ 1 me 64 Nov  1 13:29 1 -> /dev/pts/1
lrwx------ 1 me 64 Nov  1 13:29 2 -> /dev/pts/1
@mvillalba2016
Copy link

@mvillalba2016 mvillalba2016 commented Jul 13, 2018

I am having the same problem with 7.2-fpm-alpine on docker.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

4 participants