Skip to content

Commit 70dfbe0

Browse files
adamjseitzcmb69
authored andcommitted
Fix #80384: limit read buffer size
In the case of a stream with no filters, php_stream_fill_read_buffer only reads stream->chunk_size into the read buffer. If the stream has filters attached, it could unnecessarily buffer a large amount of data. With this change, php_stream_fill_read_buffer only proceeds until either the requested size or stream->chunk_size is available in the read buffer. Co-authored-by: Christoph M. Becker <[email protected]> Closes GH-6444.
1 parent b043759 commit 70dfbe0

File tree

4 files changed

+33
-2
lines changed

4 files changed

+33
-2
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ PHP NEWS
44

55
- Core:
66
. Fixed bug #80523 (bogus parse error on >4GB source code). (Nikita)
7+
. Fixed bug #80384 (filter buffers entire read until file closed). (Adam
8+
Seitz, cmb)
79

810
- Date:
911
. Fixed bug #80376 (last day of the month causes runway cpu usage. (Derick)

ext/standard/tests/streams/bug79984.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@ fclose($f2);
5252
--EXPECT--
5353
filter onCreate
5454
filtered 8192 bytes.
55-
filtered 128 bytes and closing.
55+
filtered 128 bytes and closing. Stream has reached end-of-file.
5656
int(8320)
5757
filter onClose

main/streams/streams.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,14 +542,15 @@ PHPAPI int _php_stream_fill_read_buffer(php_stream *stream, size_t size)
542542
/* allocate/fill the buffer */
543543

544544
if (stream->readfilters.head) {
545+
size_t to_read_now = MIN(size, stream->chunk_size);
545546
char *chunk_buf;
546547
php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
547548
php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap;
548549

549550
/* allocate a buffer for reading chunks */
550551
chunk_buf = emalloc(stream->chunk_size);
551552

552-
while (!stream->eof && (stream->writepos - stream->readpos < (zend_off_t)size)) {
553+
while (!stream->eof && (stream->writepos - stream->readpos < (zend_off_t)to_read_now)) {
553554
ssize_t justread = 0;
554555
int flags;
555556
php_stream_bucket *bucket;

tests/basic/bug80384.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Bug #80384 large reads cause filters to internally buffer large amounts of memory
3+
--FILE--
4+
<?php
5+
/* First, create a file to read */
6+
$tmp_filename = __DIR__ . "/bug80384.tmp";
7+
$fp = fopen($tmp_filename, 'w');
8+
for ($i=0; $i<1024; $i++) {
9+
fwrite($fp, str_repeat('ABCDEFGH', 1024));
10+
}
11+
fclose($fp);
12+
13+
/* Stream the file through a filter */
14+
$fp = fopen($tmp_filename, 'r');
15+
$filter = stream_filter_append($fp, "string.rot13");
16+
17+
$mem_start = memory_get_usage();
18+
fread($fp, 8 * 1024 * 1024);
19+
$mem_final = memory_get_usage();
20+
fclose($fp);
21+
var_dump($mem_final - $mem_start < 32768);
22+
?>
23+
--CLEAN--
24+
<?php
25+
unlink(__DIR__ . "/bug80384.tmp");
26+
?>
27+
--EXPECT--
28+
bool(true)

0 commit comments

Comments
 (0)