php / pecl-networking-ssh2

Bindings for the libssh2 library
http://pecl.php.net/package/ssh2
Other
51 stars 58 forks source link

Fixed debug and disconnected callback, fixed crash when disconnected #37

Closed Szpadel closed 3 years ago

Cirrusware commented 4 years ago

Nice. When will the new release ?

Thanks to Szpadel

rposky commented 4 years ago

Thank you for your changes to php_ssh2_set_callback(). It helped me out of a bind.

Can you provide more context in support of the changes proposed to ssh2_disconnect()? They may not be necessary, and I think them undesirable, as I explain below.

Presently, if the libssh2 session resource is closed, libssh2_session_disconnect() will necessarily be invoked during resource destruction within php_ssh2_session_dtor(), the mechanics of which ensure libssh2_session_disconnect() is only invoked once, i.e. because a resource cannot be destructed twice.

By adding a call to libssh2_session_disconnect() from within ssh2_disconnect() and foregoing closing the session resource, the proposed changes allow libssh2_session_disconnect() to be invoked one or more times against any libssh2 session; once upon destruction and once anytime ssh2_disconnect() is called.

It may be the case that libssh2_session_disconnect() can tolerate multiple calls for the same libssh2 session. In either regard, I find the process less elegant than it exists today, particularly in the loss of guarantee that libssh2_session_disconnect() be invoked only once and also that resource destruction occur separately from ssh2_disconnect().

I've found that your proposed changes with the exception of those made within ssh2_disconnect() still resolves the segmentation fault potential that initially prompted my adoption of them. Perhaps there is another error being addressed that I am yet unaware of?

I've been able to use the following script to reproduce a segmentation fault issue about 50% of the time in PHP 7.2.9, and which is addressed by this change. The fault seems to arise when destructing the disconnect callback reference within php_ssh2_session_dtor() for the first libssh2 session resource and after key exchange activities overwrite the memory address allocated to the local $callbacks variable (or its 'disconnect' member) during the second loop iteration.

$onDisconnect = function ($reason, $message, $language) {
    echo "In onDisconnect callback...\n";
};
for ($i = 0; $i < 2; $i++) {
    $callbacks = ['disconnect' => $onDisconnect];
    $connection = ssh2_connect('127.0.0.1', 22222, null, $callbacks);
    if (!is_resource($connection)) {
        die("Failed to connect");
    }
    if (!ssh2_auth_password($connection, 'user', 'pass')) {
        die("Failed to authenticate with password");
    }
}

It would be great to see any scripts you have to recreate the problems you're addressing here. Thank you again for this pull request. Cheers.

Szpadel commented 4 years ago

I'm not remember exact issues that was there, but there 2 cases when if segfaults

  1. explicit disconnecting from code
  2. after resource was garbage collected

I guess this could be issue when I wanted to reconnect to host, those calls should have no effect on already disconnected resource

Whissi commented 4 years ago

I hope I am not hijacking this issue.

Today I also run into a segfault issue. My reproducer looks like

<?php

$file = 'ssh2.sftp://USERNAME:SECRETPASSPHRASE@sftp.example.com/home/USERNAME/test/foo.csv';

$i = 1;
while ($i <= 3) {
        echo "Calling file_get_contents for the #$i time ..." . PHP_EOL;
        $data = file_get_contents($file);
        if (!$data) {
                echo "Survived but no data received!" . PHP_EOL;
        }
        else {
                echo "Survived, all good!" . PHP_EOL;
        }

        $i++;
}

echo "Reached end!" . PHP_EOL;

I tested this on Debian and Gentoo Linux. Against PHP 7.3.15 and PHP 7.4.3. I am getting two types of crashes. With a minimal PHP and debug enabled it looks like:

$ php7.4 test.php
Calling file_get_contents for the #1 time ...
Survived, all good!
Calling file_get_contents for the #2 time ...
PHP Warning:  file_get_contents(): Unable to find the wrapper "rsh2.sftp" - did you forget to enable it when you configured PHP? in /tmp/test.php on line 10
PHP Warning:  file_get_contents(rsh2.sftp://...@sftp.example.com/home/USERNAME/test/foo.csv): failed to open stream: No such file or directory in /tmp/test.php on line 10
Survived but no data received!
Reached end!
[Tue Mar 17 17:01:26 2020]  Script:  '/tmp/test.php'
/var/tmp/portage/dev-php/pecl-ssh2-1.2/work/php7.4/ssh2.c(61) :  Freeing 0x00007f70a5601050 (7 bytes), script=/tmp/test.php
Last leak repeated 11 times
[Tue Mar 17 17:01:26 2020]  Script:  '/tmp/test.php'
/var/tmp/portage/dev-php/pecl-ssh2-1.2/work/php7.4/ssh2_fopen_wrappers.c(476) :  Freeing 0x00007f70a5658268 (24 bytes), script=/tmp/test.php
[Tue Mar 17 17:01:26 2020]  Script:  '/tmp/test.php'
/var/tmp/portage/dev-php/pecl-ssh2-1.2/work/php7.4/ssh2.c(311) :  Freeing 0x00007f70a5688500 (40 bytes), script=/tmp/test.php
=== Total 14 memory leaks detected ===
$ php7.4 test.php
$ gdb /usr/bin/php7.4 /var/tmp/coredumps/php7.4.117769.1584460888
GNU gdb (Gentoo 9.1 vanilla) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/php7.4...
Reading symbols from /usr/lib/debug//usr/lib64/php7.4/bin/php.debug...

warning: core file may not match specified executable file.
[New LWP 117769]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `php7.4 test.php'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00005622e77f7c11 in zend_gc_delref (p=0x883c0)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_types.h:1039
1039    /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_types.h: No such file or directory.
(gdb) bt
#0  0x00005622e77f7c11 in zend_gc_delref (p=0x883c0)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_types.h:1039
#1  0x00005622e77f7f85 in zend_list_delete (res=0x883c0)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_list.c:47
#2  0x00007fa0dfd348ab in php_ssh2_fopen_wraper_parse_path (
    path=0x7fa0dfa8b1f8 "ssh2.sftp://USERNAME:SECRETPASSPHRASE@sftp.example.com/home/USERNAME/test/foo.csv",
    type=0x7fa0dfd3b77e "sftp", context=0x7fa0dfa6e800, psession=0x7ffd5819e7e8, presource=0x7ffd5819e7f8,
    psftp=0x7ffd5819e7f0, psftp_rsrc=0x7ffd5819e800)
    at /var/tmp/portage/dev-php/pecl-ssh2-1.2/work/php7.4/ssh2_fopen_wrappers.c:458
#3  0x00007fa0dfd37cb1 in php_ssh2_sftp_stream_opener (wrapper=0x7fa0dfd3fd80 <php_ssh2_sftp_wrapper>,
    filename=0x7fa0dfa8b1f8 "ssh2.sftp://USERNAME:SECRETPASSPHRASE@sftp.example.com/home/USERNAME/test/foo.csv", mode=0x5622e7a0a3d0 "rb", options=0, opened_path=0x0, context=0x7fa0dfa6e800, __php_stream_call_depth=1,
    __zend_filename=0x5622e7a30038 "/var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/main/streams/streams.c", __zend_lineno=2107,
    __zend_orig_filename=0x5622e7a0a2c8 "/var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/ext/standard/file.c", __zend_orig_lineno=554) at /var/tmp/portage/dev-php/pecl-ssh2-1.2/work/php7.4/ssh2_sftp.c:230
#4  0x00005622e775f154 in _php_stream_open_wrapper_ex (
    path=0x7fa0dfa8b1f8 "ssh2.sftp://USERNAME:SECRETPASSPHRASE@sftp.example.com/home/USERNAME/test/foo.csv",
    mode=0x5622e7a0a3d0 "rb", options=8, opened_path=0x0, context=0x7fa0dfa6e800, __php_stream_call_depth=0,
    __zend_filename=0x5622e7a0a2c8 "/var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/ext/standard/file.c",
    __zend_lineno=554, __zend_orig_filename=0x0, __zend_orig_lineno=0)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/main/streams/streams.c:2105
#5  0x00005622e76838ce in zif_file_get_contents (execute_data=0x7fa0dfa14150, return_value=0x7fa0dfa14100)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/ext/standard/file.c:554
#6  0x00005622e784efa7 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER ()
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_vm_execute.h:1314
#7  0x00005622e78b4bf5 in execute_ex (ex=0x7fa0dfa14020)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_vm_execute.h:53797
#8  0x00005622e78b8d34 in zend_execute (op_array=0x7fa0dfa8a300, return_value=0x0)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_vm_execute.h:57913
#9  0x00005622e77dc168 in zend_execute_scripts (type=8, retval=0x0, file_count=3)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend.c:1665
#10 0x00005622e773c17a in php_execute_script (primary_file=0x7ffd581a11e0)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/main/main.c:2617
#11 0x00005622e78bb868 in do_cli (argc=2, argv=0x5622e878e740)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/sapi/cli/php_cli.c:961
#12 0x00005622e78bca33 in main (argc=2, argv=0x5622e878e740)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/sapi/cli/php_cli.c:1356

With your PR applied, most of the times it's now crashing like

Core was generated by `php7.4 test.php'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000055c565a89c11 in zend_gc_delref (p=0x0)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_types.h:1039
1039    /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_types.h: No such file or directory.
(gdb) bt
#0  0x000055c565a89c11 in zend_gc_delref (p=0x0)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_types.h:1039
#1  0x000055c565a89f85 in zend_list_delete (res=0x0)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_list.c:47
#2  0x00007fbf35912b4e in php_ssh2_sftp_stream_close (stream=0x7fbf3568a400, close_handle=1)
    at /var/tmp/portage/dev-php/pecl-ssh2-1.2/work/php7.4/ssh2_sftp.c:139
#3  0x000055c5659ece5b in _php_stream_free (stream=0x7fbf3568a400, close_options=3)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/main/streams/streams.c:477
#4  0x000055c565915a6e in zif_file_get_contents (execute_data=0x7fbf35614150, return_value=0x7fbf35614100)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/ext/standard/file.c:577
#5  0x000055c565ae0fa7 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER ()
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_vm_execute.h:1314
#6  0x000055c565b46bf5 in execute_ex (ex=0x7fbf35614020)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_vm_execute.h:53797
#7  0x000055c565b4ad34 in zend_execute (op_array=0x7fbf3568a300, return_value=0x0)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend_vm_execute.h:57913
#8  0x000055c565a6e168 in zend_execute_scripts (type=8, retval=0x0, file_count=3)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/Zend/zend.c:1665
#9  0x000055c5659ce17a in php_execute_script (primary_file=0x7ffdf1847230)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/main/main.c:2617
#10 0x000055c565b4d868 in do_cli (argc=2, argv=0x55c567c5b740)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/sapi/cli/php_cli.c:961
#11 0x000055c565b4ea33 in main (argc=2, argv=0x55c567c5b740)
    at /var/tmp/portage/dev-lang/php-7.4.3-r1/work/sapis-build/cli/sapi/cli/php_cli.c:1356

But please note: It was crashing in php_ssh2_sftp_stream_close sometimes before I applied your patch, too.

rposky commented 4 years ago

Both errors shared appear to be from areas unrelated to this issue: php_ssh2_sftp_stream_close and php_ssh2_fopen_wraper_parse_path. It seems like you've encountered some novel segmentation faults that no open PRs will yet address.

cmb69 commented 3 years ago

Indeed, the fix to ssh2_disconnect() in this PR doesn't look right. libssh2_session_disconnect() is called in the resource destructor, and that needs to be triggered to avoid memory leaks anyway. I think PR #46 is the more appropriate fix for this issue.

I should also point out that the additional zend_fetch_resource() is correct, but would break code that calls that function to close SSH2 SFTP (or other) resources. It may make sense to deprecate passing anything else than a SSH2 SFTP resource to this function, and actually prohibit this in a later version only.

langemeijer commented 3 years ago

@Szpadel I have used your proposed changes on the callbacks to create PR #52. If you agree with what has been discussed here please close this PR.

@Whissi Could you run your test again against the latest master branch?