diff --git a/NEWS b/NEWS index 84bbf47ee39..ac76176680c 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,10 @@ PHP NEWS . Fixed GH-11982 (str_getcsv returns null byte for unterminated enclosure). (Jakub Zelenka) +- Streams: + . Fixed bug #52335 (fseek() on memory stream behavior different than file). + (Jakub Zelenka) + 17 Aug 2023, PHP 8.3.0beta3 - Core: diff --git a/UPGRADING b/UPGRADING index 20ff98b3095..eec2b433219 100644 --- a/UPGRADING +++ b/UPGRADING @@ -648,6 +648,9 @@ PHP 8.3 UPGRADE NOTES - Streams: . Blocking fread() on socket connection returns immediately if there are any buffered data instead of waiting for more data. + . Memory stream no longer fails if seek offset is past the end. Instead + the memory is increase on the next write and date between the old end and + offset is filled with zero bytes in the same way how it works for files. ======================================== 14. Performance Improvements diff --git a/ext/standard/tests/file/bug52335.phpt b/ext/standard/tests/file/bug52335.phpt new file mode 100644 index 00000000000..8a7cbb53fcf --- /dev/null +++ b/ext/standard/tests/file/bug52335.phpt @@ -0,0 +1,67 @@ +--TEST-- +Bug #52335 (fseek() on memory stream behavior different then file) +--FILE-- + +--EXPECT-- +Read mode +int(0) +bool(false) +int(20) +bool(false) +string(0) "" +bool(true) +int(0) +bool(false) +int(24) +Read write mode +int(4) +int(0) +bool(false) +int(24) +bool(false) +string(0) "" +bool(true) +int(0) +int(14) +bool(false) +int(34) +string(0) "" +int(0) +int(50) +int(0) +string(68) "646174610000000000000000000000000000000020616e64206d6f72652064617461" diff --git a/ext/standard/tests/file/gh9441.phpt b/ext/standard/tests/file/gh9441.phpt new file mode 100644 index 00000000000..0c32e2b1c69 --- /dev/null +++ b/ext/standard/tests/file/gh9441.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug GH-9441 (fseek does not work with php://input when data is not preread) +--INI-- +enable_post_data_reading=0 +--POST_RAW-- +Content-Type: application/unknown +a=123456789&b=ZYX +--FILE-- + +--EXPECT-- +int(0) +int(10) +string(7) "9&b=ZYX" +string(17) "a=123456789&b=ZYX" diff --git a/ext/standard/tests/file/stream_rfc2397_007.phpt b/ext/standard/tests/file/stream_rfc2397_007.phpt index 56d91621575..dcbe5beeb3d 100644 --- a/ext/standard/tests/file/stream_rfc2397_007.phpt +++ b/ext/standard/tests/file/stream_rfc2397_007.phpt @@ -125,8 +125,8 @@ int(0) int(3) bool(false) ===S:10,C=== -int(-1) -bool(false) +int(0) +int(13) bool(false) ===S:-1,E=== int(0) @@ -137,6 +137,6 @@ int(0) int(6) bool(false) ===S:1,E=== -int(-1) -bool(false) +int(0) +int(7) bool(false) diff --git a/main/streams/memory.c b/main/streams/memory.c index 444f9637617..62d36906635 100644 --- a/main/streams/memory.c +++ b/main/streams/memory.c @@ -49,11 +49,17 @@ static ssize_t php_stream_memory_write(php_stream *stream, const char *buf, size if (ms->mode & TEMP_STREAM_READONLY) { return (ssize_t) -1; - } else if (ms->mode & TEMP_STREAM_APPEND) { - ms->fpos = ZSTR_LEN(ms->data); } - if (ms->fpos + count > ZSTR_LEN(ms->data)) { + size_t data_len = ZSTR_LEN(ms->data); + if (ms->mode & TEMP_STREAM_APPEND) { + ms->fpos = data_len; + } + if (ms->fpos + count > data_len) { ms->data = zend_string_realloc(ms->data, ms->fpos + count, 0); + if (ms->fpos > data_len) { + /* zero the bytes added due to seek past end position */ + memset(ZSTR_VAL(ms->data) + data_len, 0, ms->fpos - data_len); + } } else { ms->data = zend_string_separate(ms->data, 0); } @@ -73,7 +79,7 @@ static ssize_t php_stream_memory_read(php_stream *stream, char *buf, size_t coun php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract; assert(ms != NULL); - if (ms->fpos == ZSTR_LEN(ms->data)) { + if (ms->fpos >= ZSTR_LEN(ms->data)) { stream->eof = 1; count = 0; } else { @@ -132,20 +138,14 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe return 0; } } else { - if (ms->fpos + (size_t)(offset) > ZSTR_LEN(ms->data)) { - ms->fpos = ZSTR_LEN(ms->data); - *newoffs = -1; - return -1; - } else { - ms->fpos = ms->fpos + offset; - *newoffs = ms->fpos; - stream->eof = 0; - return 0; - } + stream->eof = 0; + ms->fpos = ms->fpos + offset; + *newoffs = ms->fpos; + return 0; } case SEEK_SET: - if (ZSTR_LEN(ms->data) < (size_t)(offset)) { - ms->fpos = ZSTR_LEN(ms->data); + if (offset < 0) { + ms->fpos = 0; *newoffs = -1; return -1; } else { @@ -156,9 +156,10 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe } case SEEK_END: if (offset > 0) { - ms->fpos = ZSTR_LEN(ms->data); - *newoffs = -1; - return -1; + ms->fpos = ZSTR_LEN(ms->data) + offset; + *newoffs = ms->fpos; + stream->eof = 0; + return 0; } else if (ZSTR_LEN(ms->data) < (size_t)(-offset)) { ms->fpos = 0; *newoffs = -1;