Skip to content

Commit c80b777

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

File tree

4 files changed

+54
-0
lines changed

4 files changed

+54
-0
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ PHP NEWS
2727
of the curl_copy_handle() function to clone a CurlHandle. (timwolla)
2828
. Fix curl build failure on macOS+curl 8.16. (nielsdos)
2929

30+
- SPL:
31+
. Fixed bug GH-19942 (iterator_count() on an empty SplFileObject looping
32+
infinitely). (alexandre-daubois)
33+
3034
- Soap:
3135
. Fixed bug GH-19784 (SoapServer memory leak). (nielsdos)
3236

ext/spl/spl_directory.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2166,6 +2166,29 @@ PHP_METHOD(SplFileObject, valid)
21662166
if (!intern->u.file.stream) {
21672167
RETURN_FALSE;
21682168
}
2169+
2170+
/* For empty files, php_stream_eof() might not return TRUE until after a read attempt.
2171+
* If we're at the beginning and haven't read anything, check EOF directly after ensuring
2172+
* a read has been attempted (only on seekable streams). */
2173+
if (!intern->u.file.current_line && Z_ISUNDEF(intern->u.file.current_zval) &&
2174+
intern->u.file.current_line_num == 0) {
2175+
2176+
if (intern->u.file.stream->ops->seek &&
2177+
(intern->u.file.stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
2178+
2179+
if (php_stream_eof(intern->u.file.stream)) {
2180+
RETURN_FALSE;
2181+
}
2182+
2183+
if (php_stream_tell(intern->u.file.stream) == 0) {
2184+
php_stream_statbuf ssb;
2185+
if (php_stream_stat(intern->u.file.stream, &ssb) == 0 && ssb.sb.st_size == 0) {
2186+
RETURN_FALSE;
2187+
}
2188+
}
2189+
}
2190+
}
2191+
21692192
RETURN_BOOL(!php_stream_eof(intern->u.file.stream));
21702193
} /* }}} */
21712194

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(0)
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)