Skip to content

Commit e027c56

Browse files
committed
Merge branch 'PHP-8.5'
* PHP-8.5: Fix file descriptor leak when proc_open() descriptor setup fails
2 parents fec9e88 + d8a5347 commit e027c56

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
@@ -1377,7 +1377,6 @@ PHP_FUNCTION(proc_open)
13771377

13781378
if (newprocok == FALSE) {
13791379
DWORD dw = GetLastError();
1380-
close_all_descriptors(descriptors, ndesc);
13811380
char *msg = php_win32_error_to_msg(dw);
13821381
php_error_docref(NULL, E_WARNING, "CreateProcess failed: %s", msg);
13831382
php_win32_error_msg_free(msg);
@@ -1394,7 +1393,6 @@ PHP_FUNCTION(proc_open)
13941393

13951394
if (close_parentends_of_pipes(&factions, descriptors, ndesc) == FAILURE) {
13961395
posix_spawn_file_actions_destroy(&factions);
1397-
close_all_descriptors(descriptors, ndesc);
13981396
goto exit_fail;
13991397
}
14001398

@@ -1414,7 +1412,6 @@ PHP_FUNCTION(proc_open)
14141412
}
14151413
posix_spawn_file_actions_destroy(&factions);
14161414
if (r != 0) {
1417-
close_all_descriptors(descriptors, ndesc);
14181415
php_error_docref(NULL, E_WARNING, "posix_spawn() failed: %s", strerror(r));
14191416
goto exit_fail;
14201417
}
@@ -1456,7 +1453,6 @@ PHP_FUNCTION(proc_open)
14561453
_exit(127);
14571454
} else if (child < 0) {
14581455
/* Failed to fork() */
1459-
close_all_descriptors(descriptors, ndesc);
14601456
php_error_docref(NULL, E_WARNING, "Fork failed: %s", strerror(errno));
14611457
goto exit_fail;
14621458
}
@@ -1546,6 +1542,9 @@ PHP_FUNCTION(proc_open)
15461542
} else {
15471543
exit_fail:
15481544
_php_free_envp(env);
1545+
if (descriptors) {
1546+
close_all_descriptors(descriptors, ndesc);
1547+
}
15491548
RETVAL_FALSE;
15501549
}
15511550

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)