Skip to content

Commit c047f3a

Browse files
committed
Fix SplFileObject::fseek() accepting $whence values that do not fit in an int
SplFileObject::fseek() casts $whence to a C int the same way fseek() did, so a value whose low 32 bits alias onto a seek constant is accepted and acted upon while the call reports success. The line cache is now freed only when the seek succeeds, so a failed seek leaves current() describing the unchanged position.
1 parent dd943cc commit c047f3a

4 files changed

Lines changed: 112 additions & 2 deletions

File tree

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ PHP NEWS
2626
. Fixed bug GH-23477 (Memory leak on duplicate native Phar manifest entries).
2727
(Weilin Du)
2828

29+
- SPL:
30+
. Fixed SplFileObject::fseek() accepting $whence values that do not fit in an
31+
int, which were silently truncated onto a valid seek constant. (lacatoire)
32+
2933
- Standard:
3034
. Fixed a segfault when a stream filter callback unsets StreamBucket::$data
3135
before re-attaching the bucket. (iliaal)

ext/spl/spl_directory.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2459,8 +2459,17 @@ PHP_METHOD(SplFileObject, fseek)
24592459

24602460
CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
24612461

2462-
spl_filesystem_file_free_line(intern);
2463-
RETURN_LONG(php_stream_seek(intern->u.file.stream, pos, (int)whence));
2462+
if (ZEND_LONG_EXCEEDS_INT(whence)) {
2463+
RETURN_LONG(-1);
2464+
}
2465+
2466+
int ret = php_stream_seek(intern->u.file.stream, pos, (int)whence);
2467+
2468+
if (ret == 0) {
2469+
spl_filesystem_file_free_line(intern);
2470+
}
2471+
2472+
RETURN_LONG(ret);
24642473
} /* }}} */
24652474

24662475
/* {{{ Get a character from the file */
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
--TEST--
2+
SplFileObject::fseek(): a failed seek must not discard the current line
3+
--FILE--
4+
<?php
5+
$tmp = __DIR__ . '/SplFileObject_fseek_whence_invalid_inrange.tmp';
6+
file_put_contents($tmp, "aaa\nbbb\nccc\n");
7+
8+
foreach ([99, -2147483648, 2147483647] as $whence) {
9+
echo 'whence=', $whence, PHP_EOL;
10+
11+
$file = new SplFileObject($tmp);
12+
var_dump($file->current());
13+
var_dump($file->fseek(3, $whence));
14+
var_dump($file->ftell());
15+
var_dump($file->current());
16+
unset($file);
17+
18+
echo PHP_EOL;
19+
}
20+
?>
21+
--CLEAN--
22+
<?php
23+
@unlink(__DIR__ . '/SplFileObject_fseek_whence_invalid_inrange.tmp');
24+
?>
25+
--EXPECT--
26+
whence=99
27+
string(4) "aaa
28+
"
29+
int(-1)
30+
int(4)
31+
string(4) "aaa
32+
"
33+
34+
whence=-2147483648
35+
string(4) "aaa
36+
"
37+
int(-1)
38+
int(4)
39+
string(4) "aaa
40+
"
41+
42+
whence=2147483647
43+
string(4) "aaa
44+
"
45+
int(-1)
46+
int(4)
47+
string(4) "aaa
48+
"
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
SplFileObject::fseek(): $whence values that overflow int must return -1, not alias onto a valid constant
3+
--SKIPIF--
4+
<?php
5+
if (PHP_INT_SIZE < 8) die("skip 64-bit only");
6+
?>
7+
--FILE--
8+
<?php
9+
$tmp = __DIR__ . '/SplFileObject_fseek_whence_overflow.tmp';
10+
file_put_contents($tmp, "0123456789");
11+
$bias = 2 ** 32;
12+
13+
$file = new SplFileObject($tmp);
14+
15+
// SEEK_CUR + 2**32 must not alias onto SEEK_CUR (1)
16+
$file->fseek(4);
17+
var_dump($file->fseek(3, SEEK_CUR + $bias)); // -1
18+
var_dump($file->ftell()); // 4 (unchanged)
19+
20+
// SEEK_END + 2**32 must not alias onto SEEK_END (2)
21+
$file->fseek(4);
22+
var_dump($file->fseek(3, SEEK_END + $bias)); // -1
23+
var_dump($file->ftell()); // 4 (unchanged)
24+
25+
// PHP_INT_MIN must not alias onto SEEK_SET (0)
26+
$file->fseek(4);
27+
var_dump($file->fseek(3, PHP_INT_MIN)); // -1
28+
var_dump($file->ftell()); // 4 (unchanged)
29+
30+
// Sanity: normal SEEK_CUR still works
31+
$file->fseek(4);
32+
var_dump($file->fseek(3, SEEK_CUR)); // 0
33+
var_dump($file->ftell()); // 7
34+
35+
unset($file);
36+
?>
37+
--CLEAN--
38+
<?php
39+
@unlink(__DIR__ . '/SplFileObject_fseek_whence_overflow.tmp');
40+
?>
41+
--EXPECT--
42+
int(-1)
43+
int(4)
44+
int(-1)
45+
int(4)
46+
int(-1)
47+
int(4)
48+
int(0)
49+
int(7)

0 commit comments

Comments
 (0)