Skip to content

Commit 31749aa

Browse files
committed
Fix #81659: stream_get_contents() may unnecessarily overallocate
Since we're going to read from the current stream position anyway, the `max_len` should be the size of the file minus the current position (still catering to potentially filtered streams). We must, however, make sure to cater to the file position being beyond the actual file size. While we're at, we also fix the step size in the comment, which is 8K. A further optimization could be done for unfiltered streams, thus saving that step size, but 8K might not be worth it. Closes GH-7693.
1 parent c2d6d83 commit 31749aa

File tree

3 files changed

+28
-2
lines changed

3 files changed

+28
-2
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ PHP NEWS
1919
- Standard:
2020
. Fixed bug #81618 (dns_get_record fails on FreeBSD for missing type).
2121
(fsbruva)
22+
. Fixed bug #81659 (stream_get_contents() may unnecessarily overallocate).
23+
(cmb)
2224

2325
18 Nov 2021, PHP 7.4.26
2426

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
Bug #81659 (stream_get_contents() may unnecessarily overallocate)
3+
--FILE--
4+
<?php
5+
$stream = fopen(__DIR__ . "/81659.txt", "w+");
6+
7+
for ($i = 0; $i < 1024; $i++) {
8+
fwrite($stream, str_repeat("*", 1024));
9+
}
10+
11+
fseek($stream, 1023 * 1024);
12+
13+
$m0 = memory_get_peak_usage();
14+
var_dump(strlen(stream_get_contents($stream)));
15+
$m1 = memory_get_peak_usage();
16+
var_dump($m1 < $m0 + 512 * 1024);
17+
?>
18+
--CLEAN--
19+
<?php
20+
@unlink(__DIR__ . "/81659.txt");
21+
?>
22+
--EXPECT--
23+
int(1024)
24+
bool(true)

main/streams/streams.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,9 +1502,9 @@ PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, int
15021502
* result may be inaccurate, as the filter may inflate or deflate the
15031503
* number of bytes that we can read. In order to avoid an upsize followed
15041504
* by a downsize of the buffer, overestimate by the step size (which is
1505-
* 2K). */
1505+
* 8K). */
15061506
if (php_stream_stat(src, &ssbuf) == 0 && ssbuf.sb.st_size > 0) {
1507-
max_len = ssbuf.sb.st_size + step;
1507+
max_len = MAX(ssbuf.sb.st_size - src->position, 0) + step;
15081508
} else {
15091509
max_len = step;
15101510
}

0 commit comments

Comments
 (0)