Skip to content

Commit 81e0367

Browse files
committed
ext/standard: Close owned proc_open descriptors on setup failure
Use the descriptor type to identify owned parent endpoints during failed setup. Descriptor zero is valid for pipes and sockets and must be closed; standard descriptors own only their duplicated child endpoint. Closes GH-24098
1 parent 0c4e3b6 commit 81e0367

4 files changed

Lines changed: 62 additions & 1 deletion

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ PHP NEWS
203203
read failure. (Ilia Alshanetsky)
204204
. Fixed iptcembed() corrupting JPEG headers when called recursively from an
205205
output handler. (Ilia Alshanetsky)
206+
. Fixed proc_open() leaking descriptor zero when descriptor setup fails.
207+
(Ilia Alshanetsky)
206208
. Fixed three Windows-only proc_open() defects: an uninitialized
207209
PROCESS_INFORMATION, an indeterminate comspec pointer after a failed
208210
lookup, and an unchecked CreateFileA() failure. (Ilia Alshanetsky)

‎ext/standard/proc_open.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1189,8 +1189,9 @@ static void close_all_descriptors(descriptorspec_item *descriptors, int ndesc)
11891189
{
11901190
for (int i = 0; i < ndesc; i++) {
11911191
close_descriptor(descriptors[i].childend);
1192-
if (descriptors[i].parentend)
1192+
if (descriptors[i].type != DESCRIPTOR_TYPE_STD) {
11931193
close_descriptor(descriptors[i].parentend);
1194+
}
11941195
}
11951196
}
11961197

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
proc_open() closes descriptor 0 when setup fails after pipe allocation
3+
--SKIPIF--
4+
<?php
5+
if (!function_exists("proc_open")) {
6+
die("skip proc_open() unavailable");
7+
}
8+
if (!@is_dir("/proc/self/fd")) {
9+
die("skip requires /proc/self/fd");
10+
}
11+
?>
12+
--FILE--
13+
<?php
14+
$code = <<<'PHP'
15+
fclose(STDIN);
16+
var_dump(@proc_open("true", [0 => ["pipe", "w"], 1 => ["bogus_type"]], $pipes));
17+
$fd = fopen("/dev/null", "r");
18+
var_dump(readlink("/proc/self/fd/0"));
19+
fclose($fd);
20+
PHP;
21+
$process = proc_open([PHP_BINARY, "-n", "-r", $code], [1 => ["pipe", "w"]], $pipes);
22+
echo stream_get_contents($pipes[1]);
23+
fclose($pipes[1]);
24+
var_dump(proc_close($process));
25+
?>
26+
--EXPECT--
27+
bool(false)
28+
string(9) "/dev/null"
29+
int(0)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
proc_open() closes descriptor 0 when setup fails after socket allocation
3+
--SKIPIF--
4+
<?php
5+
if (!function_exists("proc_open")) {
6+
die("skip proc_open() unavailable");
7+
}
8+
if (!@is_dir("/proc/self/fd")) {
9+
die("skip requires /proc/self/fd");
10+
}
11+
?>
12+
--FILE--
13+
<?php
14+
$code = <<<'PHP'
15+
fclose(STDIN);
16+
var_dump(@proc_open("true", [0 => ["socket"], 1 => ["bogus_type"]], $pipes));
17+
$fd = fopen("/dev/null", "r");
18+
var_dump(readlink("/proc/self/fd/0"));
19+
fclose($fd);
20+
PHP;
21+
$process = proc_open([PHP_BINARY, "-n", "-r", $code], [1 => ["pipe", "w"]], $pipes);
22+
echo stream_get_contents($pipes[1]);
23+
fclose($pipes[1]);
24+
var_dump(proc_close($process));
25+
?>
26+
--EXPECT--
27+
bool(false)
28+
string(9) "/dev/null"
29+
int(0)

0 commit comments

Comments
 (0)