Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any known side-effects with storing PHP closures in object properties?

I have encountered a very strange problem, and I thought I would record it so as to potentially uncover a possible bug. I have an acceptable workaround for now.

My project is a PHP-based deployment tool that uses phpseclib to make SSH and SFTP connections to a remote server. I have had more luck with this than the SSH2 extension, so I've decided to stick with it for now. I am running PHP 7.0.16 inside a Dockerised Alpine 3.5 environment. The problem surfaces when I am running functional tests inside PHPUnit that connect to a real (local) SSH server.

When I prepare a file for transmission, it goes through a process of creating a temporary copy so I can make modifications on a per-case basis, using the search and replace of strings. For testing though I want something less dynamic, so I thought I would add a class-wide closure to define what a date looks like:

abstract class Base
{
    // (other properties here)
    protected $dateGenerator;

    public function __construct(QueueState $queueState, BaseFetcher $fetcher)
    {
        $this->queueState = $queueState;
        $this->fetcher = $fetcher;

        // Sets a default date generator
        $this->dateGenerator = function() { return date('r'); };
    }
}

So the idea here is the closure is the "real" generator, and I can replace it in test environments only with something static.

So, here is the manifestation of the problem - lots of notices:

~ # ./phpunit test/functional/tests/Sftp --stop-on-error
PHPUnit 6.2.2 by Sebastian Bergmann and contributors.

...........                                                       11 / 11 (100%)

Time: 7.56 seconds, Memory: 6.00MB

OK (11 tests, 17 assertions)
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599
PHP Notice:  Connection closed prematurely in /root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 3599

Note that $this->dateGenerator is not actually used anywhere. Now, here is the oddity, I can replace it with an empty closure, and I get the notices again:

$this->dateGenerator = function() {};

However, if I comment that out, the notices disappear.

#$this->dateGenerator = function() {};

I have tried a lot of things around this, and I am entirely sure I am only changing one thing. I wondered if the problem was storing closures in the ctor, so I tried injecting the empty closure in a setter, and I get the notices again.

I have swapped the closure for a class, and the problem goes away again. That is how I will fix it, but the notices problem makes me nervous about the phpseclib library, or indeed the stability of everything I am building! I appreciate that since I am using PHP, Docker, PHPUnit, phpseclib, ssh and sshd, there are a lot of things that could inject an issue.

On the phpseclib ticket list, this notice is mentioned in #1125 and #985, but both seem to have a specific cause, rather than the apparently unrelated property I am demonstrating here.

Any ideas on how I can research this? Currently I am theorising that closures have a side-effect that I am not aware of. I even tried to null the closure in a destructor, in case it needed to be "closed", but this did not stop the notices.

like image 664
halfer Avatar asked Jul 04 '17 14:07

halfer


1 Answers

After looking up the line number of the error in phpseclib\Net\SSH2, it looks to me like it's coming from send_binary_packet(), which throws that exception when its fsock is either not a valid resource or has reached an EOF marker.

I would look into where any objects are used that inherit from this Base class, and see what is done with them. Your dateGenerator may not be called explicitly, but there could still be something out there seeing it and not liking it for some reason. Might even have to dig through code that's not your own. :/

like image 133
Garrett W. Avatar answered Nov 01 '22 16:11

Garrett W.