amphp / ext-uv

Other
191 stars 28 forks source link

Segfault when running under PHPUnit and forcing a spawn error #79

Closed ghost closed 4 years ago

ghost commented 4 years ago

I'm trying to force a process spawn error (i.e. spawning fails with an error < 0) by trying to spawn a binary that doesn't exist, however under PHPUnit, PHP will segfault with the following backtrace:

#0  destruct_uv_loop_walk_cb (handle=<optimized out>, arg=0x0) at /tmp/pear/temp/uv/php_uv.c:1206
#1  0x00007fffec4fa70f in uv_walk () from /usr/lib/x86_64-linux-gnu/libuv.so.1
#2  0x00007fffec72073f in zm_deactivate_uv (type=<optimized out>, module_number=<optimized out>) at /tmp/pear/temp/uv/php_uv.c:2700
#3  0x0000555555b7fc4c in zend_deactivate_modules () at /root/php-src/Zend/zend_API.c:2637
#4  0x0000555555b08062 in php_request_shutdown (dummy=<optimized out>) at /root/php-src/main/main.c:1878
#5  0x0000555555c35350 in do_cli (argc=3, argv=0x555556787090) at /root/php-src/sapi/cli/php_cli.c:1178
#6  0x00005555556e3463 in main (argc=3, argv=0x555556787090) at /root/php-src/sapi/cli/php_cli.c:1404

This is the PHPUnit test:

use PHPUnit\Framework\TestCase;

class UvProcessTest extends TestCase {
    function testProcess() {
        $rt = uv_spawn(
            uv_default_loop(),
            '',
            array(),
            array(),
            __DIR__,
            array(),
            static function () {
                echo 'Child Process exited'.PHP_EOL;
            }
        );

        $this->assertNotNull($rt);
        var_dump($rt);

        uv_run();
    }
}

The segfault occurs on Windows and Linux, unrelated to the PHP version and ZTS (I've tried with PHP 7.3.3 NTS, 7.4.1 NTS, 7.2.26 ZTS and 7.4.1 ZTS).

However on a freshly compiled PHP 7.4.1 ZTS DEBUG it does not segfault and runs fine for some reason.

TheTechsTech commented 4 years ago

Looking your test, it should not run at all.

I copied and ran your code, and it displayed int(-4058) meaning and the call returned false, and not a UVProcess object.

I'm using my stubs with VSCODE, and it show the block in red, needing 8 you have 7. and parameters wrong, or not setup right before hand.

Take a look at an added commit example https://github.com/bwoebi/php-uv/blob/c75b2d75ad7eeb26f7265ba5ec349ff7a8c84b7f/examples/process_kill.php

ghost commented 4 years ago

@techno-express The test is specifically meant to assert that no process has been spawned (spawning fails!). uv_spawn has 7 mandatory parameters, parameter 8 and 9 (flags and options) are optional.

https://github.com/bwoebi/php-uv/blob/4b69d0fa2e4446bfa9343172227e8351da421dff/php_uv.c#L5031-L5042

TheTechsTech commented 4 years ago

I haven't looked at the extension c code.

But there seems this will display zend_mm_heap corrupted:

<?php
$loop = uv_default_loop();

$async = uv_async_init($loop, function ($async) {
    echo "[async]";
});

$queue = uv_queue_work($loop, function () use ($async) {
    uv_async_send($async);
    echo "[queue]";
    uv_close($async);
}, function () {
    echo "[finished]";
});

uv_fs_lstat($loop, __FILE__, function ($result, $da) {
    var_dump($da);
});

uv_run();

[finished] is never display, moving uv_close($async); to uv_fs_lstat() callback will produce error.

ghost commented 4 years ago

@techno-express You need to close $async in the [finished] callback.

Output:

array(11) {
  'dev' =>
  int(1074201867)
  'ino' =>
  int(31243722414900747)
  'mode' =>
  int(33206)
  'nlink' =>
  int(1)
  'uid' =>
  int(0)
  'gid' =>
  int(0)
  'rdev' =>
  int(0)
  'size' =>
  int(381)
  'atime' =>
  int(1579437665)
  'mtime' =>
  int(1579437661)
  'ctime' =>
  int(1579437661)
}
[queue][async][finished]

Not that it's related to my segfault issue...

TheTechsTech commented 4 years ago

It's not related, just wanted to see if anyone getting [finished] displayed on windows, which I'm not.

And the behavior of uv_async_send($async) is actually closing after use. I guess you have not played around with it usage. Which is what wanted to explore.

TheTechsTech commented 4 years ago

@techno-express The test is specifically meant to assert that no process has been spawned (spawning fails!). uv_spawn has 7 mandatory parameters, parameter 8 and 9 (flags and options) are optional.

It didn't run, nothing spawned, I got and negative number returned. Maybe because of the execution under PHPunit, which is controlling the environment, and might work different with PHP 7.4.1 ZTS DEBUG .

ghost commented 4 years ago

A ZTS build segfaulted when trying to spawn with invalid parameters (i.e. no executable defined) on CI. A non-ZTS build did not segfault and returned the proper -2 error code (POSIX 2 = ENOENT) on CI.

The initial post execution segfaults occurred on Linux Debian Stretch and Windows 10 Pro 1909. The CI uses Ubuntu Bionic (Travis, which always segfaulted due always ZTS) and Debian Buster (Circle).

Check these two CI builds (only difference between these two is ZTS and non-ZTS): https://circleci.com/gh/AndromedaGalaxy/reactphp-libuv-process/127 https://circleci.com/gh/AndromedaGalaxy/reactphp-libuv-process/128