Skip to content

Commit dc6bb03

Browse files
Fix GH-19942: avoid infinite loop when using iterator_count() on an empty SplFileObject
1 parent b16761e commit dc6bb03

File tree

5 files changed

+55
-0
lines changed

5 files changed

+55
-0
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ PHP NEWS
5757
. Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)).
5858
(nielsdos)
5959

60+
- SPL:
61+
. Fixed bug GH-19942 (iterator_count() on an SplFileObject looping
62+
infinitely). (alexandre-daubois)
63+
6064
- Soap:
6165
. Fixed bug GH-19784 (SoapServer memory leak). (nielsdos)
6266
. Fixed bug GH-20011 (Array of SoapVar of unknown type causes crash).

ext/spl/spl_directory.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,9 +2235,17 @@ PHP_METHOD(SplFileObject, next)
22352235
RETURN_THROWS();
22362236
}
22372237

2238+
bool had_current_line = (intern->u.file.current_line != NULL || !Z_ISUNDEF(intern->u.file.current_zval));
22382239
spl_filesystem_file_free_line(intern);
22392240
if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) {
22402241
spl_filesystem_file_read_line(ZEND_THIS, intern, true);
2242+
} else if (!had_current_line) {
2243+
/* If there was no current line before, we need to advance the stream position
2244+
* for iterator_count() and other iterator operations to work correctly.
2245+
* Read and immediately discard a line. */
2246+
if (spl_filesystem_file_read_line(ZEND_THIS, intern, true) == SUCCESS) {
2247+
spl_filesystem_file_free_line(intern);
2248+
}
22412249
}
22422250
intern->u.file.current_line_num++;
22432251
} /* }}} */
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
GH-19942 (iterator_count() on an empty SplFileObject should return 0)
3+
--FILE--
4+
<?php
5+
$tmpfile = tempnam(sys_get_temp_dir(), 'spl_empty_test');
6+
touch($tmpfile);
7+
8+
$fileObject = new SplFileObject($tmpfile, 'r');
9+
$count = iterator_count($fileObject);
10+
11+
var_dump($count);
12+
13+
unlink($tmpfile);
14+
?>
15+
--EXPECT--
16+
int(1)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
GH-19942 (iterator_count() on a non-empty SplFileObject should return correct count)
3+
--FILE--
4+
<?php
5+
$tmpfile = tempnam(sys_get_temp_dir(), 'spl_non_empty_test');
6+
file_put_contents($tmpfile, "line1\nline2\nline3");
7+
8+
$fileObject = new SplFileObject($tmpfile, 'r');
9+
$count = iterator_count($fileObject);
10+
11+
var_dump($count);
12+
13+
unlink($tmpfile);
14+
?>
15+
--EXPECT--
16+
int(3)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
SplFileObject::valid() with non-seekable streams should not hang
3+
--FILE--
4+
<?php
5+
$file = new SplFileObject("php://stdin", "r");
6+
var_dump($file->valid());
7+
?>
8+
--STDIN--
9+
10+
--EXPECT--
11+
bool(true)

0 commit comments

Comments
 (0)