Skip to content

Commit d8a5347

Browse files
committed
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4: Fix file descriptor leak when proc_open() descriptor setup fails
2 parents 91ea928 + 100938b commit d8a5347

2 files changed

Lines changed: 23 additions & 4 deletions

File tree

ext/standard/proc_open.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,7 +1372,6 @@ PHP_FUNCTION(proc_open)
13721372

13731373
if (newprocok == FALSE) {
13741374
DWORD dw = GetLastError();
1375-
close_all_descriptors(descriptors, ndesc);
13761375
char *msg = php_win32_error_to_msg(dw);
13771376
php_error_docref(NULL, E_WARNING, "CreateProcess failed: %s", msg);
13781377
php_win32_error_msg_free(msg);
@@ -1389,7 +1388,6 @@ PHP_FUNCTION(proc_open)
13891388

13901389
if (close_parentends_of_pipes(&factions, descriptors, ndesc) == FAILURE) {
13911390
posix_spawn_file_actions_destroy(&factions);
1392-
close_all_descriptors(descriptors, ndesc);
13931391
goto exit_fail;
13941392
}
13951393

@@ -1409,7 +1407,6 @@ PHP_FUNCTION(proc_open)
14091407
}
14101408
posix_spawn_file_actions_destroy(&factions);
14111409
if (r != 0) {
1412-
close_all_descriptors(descriptors, ndesc);
14131410
php_error_docref(NULL, E_WARNING, "posix_spawn() failed: %s", strerror(r));
14141411
goto exit_fail;
14151412
}
@@ -1451,7 +1448,6 @@ PHP_FUNCTION(proc_open)
14511448
_exit(127);
14521449
} else if (child < 0) {
14531450
/* Failed to fork() */
1454-
close_all_descriptors(descriptors, ndesc);
14551451
php_error_docref(NULL, E_WARNING, "Fork failed: %s", strerror(errno));
14561452
goto exit_fail;
14571453
}
@@ -1541,6 +1537,9 @@ PHP_FUNCTION(proc_open)
15411537
} else {
15421538
exit_fail:
15431539
_php_free_envp(env);
1540+
if (descriptors) {
1541+
close_all_descriptors(descriptors, ndesc);
1542+
}
15441543
RETVAL_FALSE;
15451544
}
15461545

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
proc_open() does not leak file descriptors when descriptor setup fails mid-spec
3+
--SKIPIF--
4+
<?php
5+
if (!function_exists("proc_open")) die("skip proc_open() unavailable");
6+
if (!@is_dir("/proc/self/fd")) die("skip requires /proc/self/fd");
7+
?>
8+
--FILE--
9+
<?php
10+
$before = count(scandir("/proc/self/fd"));
11+
for ($i = 0; $i < 100; $i++) {
12+
// Index 0 opens a real pipe; index 1 is invalid, so setup fails after the
13+
// pipe is already open. The aborted call must release the pipe fds.
14+
@proc_open("true", [0 => ["pipe", "r"], 1 => ["bogus_type"]], $pipes);
15+
}
16+
$after = count(scandir("/proc/self/fd"));
17+
var_dump($after <= $before + 2);
18+
?>
19+
--EXPECT--
20+
bool(true)

0 commit comments

Comments
 (0)