[php-src] Issue #21292: proc_close() returns raw signal number, indistinguishable from normal exit code
[email protected] (alies-dev)
| Newsgroups | php.bugs |
|---|---|
| Message-ID | <[email protected]> |
Issue: https://github.com/php/php-src/issues/21292
Author: alies-dev
### Description
On non-Windows systems, when a child process opened via `proc_open()` is killed by a signal, `proc_close()` returns the raw signal number (e.g. `11` for `SIGSEGV`) rather than following the Unix convention of `128 + signal` (e.g. `139`).
This makes it impossible for PHP userland code to distinguish between "process exited normally with code 11" and "process was killed by SIGSEGV".
### Underlying cause
Internally, `proc_close()` calls `waitpid()` which returns an encoded status. The C macros `WIFEXITED()`/`WEXITSTATUS()` and `WIFSIGNALED()`/`WTERMSIG()` exist to decode this, but `proc_close()` does not use them to differentiate the two cases. It effectively passes back a value that loses the signal/exit distinction.
PHP's own `pcntl_waitpid()` + `pcntl_wifexited()` + `pcntl_wtermsig()` handle this correctly, but `pcntl` is not always available, and `proc_close()` is the standard way to get the exit status of a `proc_open()` process.
### Reproduction
```php
<?php
// Child script that triggers SIGSEGV
$child = '<?php posix_kill(posix_getpid(), 11);';
$process = proc_open(
[PHP_BINARY, '-r', $child],
[],
$pipes
);
$exitCode = proc_close($process);
// Expected: 139 (128 + 11, following bash/Unix convention)
// Actual: 11 (raw signal number, same as a normal exit code)
echo "Exit code: $exitCode\n";
echo "Is this SIGSEGV or a normal exit(11)? Impossible to tell.\n";
```
### Expected behavior
`proc_close()` should return `128 + signal_number` when the child was killed by a signal, consistent with how bash and other Unix shells report signal termination via `$?`. This would allow userland code to detect crashes.
Alternatively, expose the signal information through a separate mechanism (e.g. an optional by-reference parameter, or a new `proc_exit_status()` function).
### Real-world impact
This affects tools that use `proc_open()` to spawn child processes, notably [`composer/xdebug-handler`](https://github.com/composer/xdebug-handler) which restarts PHP processes. When the child crashes (e.g. due to a PHP JIT bug triggering SIGSEGV), the parent receives exit code `11` with no way to detect it was a signal — leading to silent, misleading failures in tools like Psalm, PHPStan, and Composer.
- Related upstream fix: https://github.com/composer/xdebug-handler/pull/162
- Downstream report: https://github.com/vimeo/psalm/issues/11679
### PHP Version
Tested on PHP 8.3 and 8.4, but the behavior has existed since `proc_open` was introduced.
### Operating System
Linux / macOS (any non-Windows POSIX system)