From 4a5d13e20562a4af0bca8fe2ebfe93649715b110 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 7 Jul 2023 15:33:00 +0100 Subject: [PATCH] Fix GH-11242: Use dynamic buffer for large length in stream mem copy --- NEWS | 4 +++ .../file_get_contents_with_large_length.phpt | 27 ++++++++++++++++++ main/streams/streams.c | 28 +++++++++++++------ 3 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 ext/standard/tests/file/file_get_contents_with_large_length.phpt diff --git a/NEWS b/NEWS index eb7a991c9d9..d60787f0c5e 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,10 @@ PHP NEWS . Fixed GH-11573 (RecursiveDirectoryIterator::hasChildren is slow). (nielsdos) +- Streams: + . Implemented GH-11242 (_php_stream_copy_to_mem: Allow specifying a maximum + length without allocating a buffer of that size). (Jakub Zelenka) + 06 Jul 2023, PHP 8.3.0alpha3 - Core: diff --git a/ext/standard/tests/file/file_get_contents_with_large_length.phpt b/ext/standard/tests/file/file_get_contents_with_large_length.phpt new file mode 100644 index 00000000000..09929d25ecd --- /dev/null +++ b/ext/standard/tests/file/file_get_contents_with_large_length.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test file_get_contents() function with large length parameter +--INI-- +memory_limit=128M +--FILE-- + +--CLEAN-- + +--EXPECT-- +bool(true) +bool(true) diff --git a/main/streams/streams.c b/main/streams/streams.c index 2a5178e2942..06589e649c2 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1489,7 +1489,7 @@ PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, int { ssize_t ret = 0; char *ptr; - size_t len = 0, max_len; + size_t len = 0, buflen; int step = CHUNK_SIZE; int min_room = CHUNK_SIZE / 4; php_stream_statbuf ssbuf; @@ -1503,7 +1503,7 @@ PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, int maxlen = 0; } - if (maxlen > 0) { + if (maxlen > 0 && maxlen < 4 * CHUNK_SIZE) { result = zend_string_alloc(maxlen, persistent); ptr = ZSTR_VAL(result); while ((len < maxlen) && !php_stream_eof(src)) { @@ -1537,20 +1537,30 @@ PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, int * by a downsize of the buffer, overestimate by the step size (which is * 8K). */ if (php_stream_stat(src, &ssbuf) == 0 && ssbuf.sb.st_size > 0) { - max_len = MAX(ssbuf.sb.st_size - src->position, 0) + step; + buflen = MAX(ssbuf.sb.st_size - src->position, 0) + step; + if (maxlen > 0 && buflen > maxlen) { + buflen = maxlen; + } } else { - max_len = step; + buflen = step; } - result = zend_string_alloc(max_len, persistent); + result = zend_string_alloc(buflen, persistent); ptr = ZSTR_VAL(result); // TODO: Propagate error? - while ((ret = php_stream_read(src, ptr, max_len - len)) > 0){ + while ((ret = php_stream_read(src, ptr, buflen - len)) > 0) { len += ret; - if (len + min_room >= max_len) { - result = zend_string_extend(result, max_len + step, persistent); - max_len += step; + if (len + min_room >= buflen) { + if (maxlen == len) { + break; + } + if (maxlen > 0 && buflen + step > maxlen) { + buflen = maxlen; + } else { + buflen += step; + } + result = zend_string_extend(result, buflen, persistent); ptr = ZSTR_VAL(result) + len; } else { ptr += ret;