Skip to content

Commit 4a5d13e

Browse files
committed
Fix GH-11242: Use dynamic buffer for large length in stream mem copy
1 parent c6b9db2 commit 4a5d13e

File tree

3 files changed

+50
-9
lines changed

3 files changed

+50
-9
lines changed

NEWS

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ PHP NEWS
2222
. Fixed GH-11573 (RecursiveDirectoryIterator::hasChildren is slow).
2323
(nielsdos)
2424

25+
- Streams:
26+
. Implemented GH-11242 (_php_stream_copy_to_mem: Allow specifying a maximum
27+
length without allocating a buffer of that size). (Jakub Zelenka)
28+
2529
06 Jul 2023, PHP 8.3.0alpha3
2630

2731
- Core:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Test file_get_contents() function with large length parameter
3+
--INI--
4+
memory_limit=128M
5+
--FILE--
6+
<?php
7+
8+
$file_path = __DIR__ . '/file_get_contents_with_large_length_content.txt';
9+
$file_content = str_repeat('a', 50000);
10+
file_put_contents($file_path, $file_content);
11+
12+
// test lenght limiting
13+
$result = file_get_contents($file_path, length: 500000000);
14+
var_dump($result === $file_content);
15+
16+
// test lenght limiting
17+
$result = file_get_contents($file_path, length: 40000);
18+
var_dump($result === str_repeat('a', 40000));
19+
20+
?>
21+
--CLEAN--
22+
<?php
23+
unlink(__DIR__ . '/file_get_contents_with_large_length_content.txt');
24+
?>
25+
--EXPECT--
26+
bool(true)
27+
bool(true)

main/streams/streams.c

+19-9
Original file line numberDiff line numberDiff line change
@@ -1489,7 +1489,7 @@ PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, int
14891489
{
14901490
ssize_t ret = 0;
14911491
char *ptr;
1492-
size_t len = 0, max_len;
1492+
size_t len = 0, buflen;
14931493
int step = CHUNK_SIZE;
14941494
int min_room = CHUNK_SIZE / 4;
14951495
php_stream_statbuf ssbuf;
@@ -1503,7 +1503,7 @@ PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, int
15031503
maxlen = 0;
15041504
}
15051505

1506-
if (maxlen > 0) {
1506+
if (maxlen > 0 && maxlen < 4 * CHUNK_SIZE) {
15071507
result = zend_string_alloc(maxlen, persistent);
15081508
ptr = ZSTR_VAL(result);
15091509
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
15371537
* by a downsize of the buffer, overestimate by the step size (which is
15381538
* 8K). */
15391539
if (php_stream_stat(src, &ssbuf) == 0 && ssbuf.sb.st_size > 0) {
1540-
max_len = MAX(ssbuf.sb.st_size - src->position, 0) + step;
1540+
buflen = MAX(ssbuf.sb.st_size - src->position, 0) + step;
1541+
if (maxlen > 0 && buflen > maxlen) {
1542+
buflen = maxlen;
1543+
}
15411544
} else {
1542-
max_len = step;
1545+
buflen = step;
15431546
}
15441547

1545-
result = zend_string_alloc(max_len, persistent);
1548+
result = zend_string_alloc(buflen, persistent);
15461549
ptr = ZSTR_VAL(result);
15471550

15481551
// TODO: Propagate error?
1549-
while ((ret = php_stream_read(src, ptr, max_len - len)) > 0){
1552+
while ((ret = php_stream_read(src, ptr, buflen - len)) > 0) {
15501553
len += ret;
1551-
if (len + min_room >= max_len) {
1552-
result = zend_string_extend(result, max_len + step, persistent);
1553-
max_len += step;
1554+
if (len + min_room >= buflen) {
1555+
if (maxlen == len) {
1556+
break;
1557+
}
1558+
if (maxlen > 0 && buflen + step > maxlen) {
1559+
buflen = maxlen;
1560+
} else {
1561+
buflen += step;
1562+
}
1563+
result = zend_string_extend(result, buflen, persistent);
15541564
ptr = ZSTR_VAL(result) + len;
15551565
} else {
15561566
ptr += ret;

0 commit comments

Comments
 (0)