Skip to content

Commit f7a92ab

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 4c9375e commit f7a92ab

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
@@ -226,6 +226,28 @@ static void _php_free_envp(php_process_env env)
226226
}
227227
/* }}} */
228228

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

276298
if (wait_pid <= 0) {
@@ -383,7 +405,7 @@ PHP_FUNCTION(proc_get_status)
383405
exitcode = running ? -1 : wstatus;
384406

385407
#elif HAVE_SYS_WAIT_H
386-
wait_pid = waitpid(proc->child, &wstatus, WNOHANG|WUNTRACED);
408+
wait_pid = waitpid_cached(proc, &wstatus, WNOHANG|WUNTRACED);
387409

388410
if (wait_pid == proc->child) {
389411
if (WIFEXITED(wstatus)) {
@@ -1244,6 +1266,9 @@ PHP_FUNCTION(proc_open)
12441266
proc->childHandle = childHandle;
12451267
#endif
12461268
proc->env = env;
1269+
#if HAVE_SYS_WAIT_H
1270+
proc->has_stored_exit_wait_status = false;
1271+
#endif
12471272

12481273
/* Clean up all the child ends and then open streams on the parent
12491274
* 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)