[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)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.