Skip to content

Commit 6ae73d8

Browse files
committed
Fix GH-10239: proc_close after proc_get_status always returns -1
The waitpid function only works once when a process is exited. Cache the result so subsequent status reads succeed.
1 parent e7c0f4e commit 6ae73d8

File tree

2 files changed

+33
-2
lines changed

2 files changed

+33
-2
lines changed

ext/standard/proc_open.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,28 @@ static void _php_free_envp(php_process_env env)
229229
}
230230
/* }}} */
231231

232+
#if HAVE_SYS_WAIT_H
233+
static pid_t waitpid_cached(php_process_handle *proc, int *wait_status, int options)
234+
{
235+
if (proc->has_stored_exit_wait_status) {
236+
*wait_status = proc->stored_exit_wait_status_value;
237+
return proc->child;
238+
}
239+
240+
pid_t wait_pid = waitpid(proc->child, wait_status, options);
241+
242+
/* The "exit" status is the final status of the process.
243+
* If we were to store the status unconditionally,
244+
* we would return stale statuses in the future after the process continues. */
245+
if (wait_pid > 0 && WIFEXITED(*wait_status)) {
246+
proc->has_stored_exit_wait_status = true;
247+
proc->stored_exit_wait_status_value = *wait_status;
248+
}
249+
250+
return wait_pid;
251+
}
252+
#endif
253+
232254
/* {{{ proc_open_rsrc_dtor
233255
* Free `proc` resource, either because all references to it were dropped or because `pclose` or
234256
* `proc_close` were called */
@@ -273,7 +295,7 @@ static void proc_open_rsrc_dtor(zend_resource *rsrc)
273295
waitpid_options = WNOHANG;
274296
}
275297
do {
276-
wait_pid = waitpid(proc->child, &wstatus, waitpid_options);
298+
wait_pid = waitpid_cached(proc, &wstatus, waitpid_options);
277299
} while (wait_pid == -1 && errno == EINTR);
278300

279301
if (wait_pid <= 0) {
@@ -386,7 +408,7 @@ PHP_FUNCTION(proc_get_status)
386408
exitcode = running ? -1 : wstatus;
387409

388410
#elif HAVE_SYS_WAIT_H
389-
wait_pid = waitpid(proc->child, &wstatus, WNOHANG|WUNTRACED);
411+
wait_pid = waitpid_cached(proc, &wstatus, WNOHANG|WUNTRACED);
390412

391413
if (wait_pid == proc->child) {
392414
if (WIFEXITED(wstatus)) {
@@ -1247,6 +1269,9 @@ PHP_FUNCTION(proc_open)
12471269
proc->childHandle = childHandle;
12481270
#endif
12491271
proc->env = env;
1272+
#if HAVE_SYS_WAIT_H
1273+
proc->has_stored_exit_wait_status = false;
1274+
#endif
12501275

12511276
/* Clean up all the child ends and then open streams on the parent
12521277
* ends, where appropriate */

ext/standard/proc_open.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,10 @@ typedef struct _php_process_handle {
4343
zend_resource **pipes;
4444
zend_string *command;
4545
php_process_env env;
46+
#if HAVE_SYS_WAIT_H
47+
/* We can only request the status once before it becomes unavailable.
48+
* Store the result so we can request it multiple times. */
49+
int stored_exit_wait_status_value;
50+
bool has_stored_exit_wait_status;
51+
#endif
4652
} php_process_handle;

0 commit comments

Comments
 (0)