Skip to content
29 changes: 29 additions & 0 deletions ext/standard/tests/streams/memory_stream_seek_overflow.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
Memory stream seek overflow detection and position preservation on failure
--FILE--
<?php
$f = fopen('php://memory', 'r+');
fwrite($f, "hello world");
var_dump(ftell($f));

var_dump(fseek($f, 5, SEEK_SET));
var_dump(ftell($f));

// Seeking past ZEND_LONG_MAX with SEEK_END fails and preserves position
var_dump(fseek($f, PHP_INT_MAX, SEEK_END));
var_dump(ftell($f));

// Seeking with positive offset from SEEK_END succeeds
var_dump(fseek($f, 10, SEEK_END));
var_dump(ftell($f));

fclose($f);
?>
--EXPECT--
int(11)
int(0)
int(5)
int(-1)
int(5)
int(0)
int(21)
23 changes: 16 additions & 7 deletions main/streams/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,12 @@ static ssize_t php_stream_memory_write(php_stream *stream, const char *buf, size
if (ms->mode & TEMP_STREAM_APPEND) {
ms->fpos = data_len;
}
if (ms->fpos + count > data_len) {
zend_string *grown = zend_string_frealloc(ms->data, ms->fpos + count, 0, /* fallible */ true);
if (UNEXPECTED(count > SIZE_MAX - ms->fpos)) {
return (ssize_t) -1;
}
size_t new_pos = ms->fpos + count;
if (new_pos > data_len) {
zend_string *grown = zend_string_frealloc(ms->data, new_pos, 0, /* fallible */ true);
if (UNEXPECTED(!grown)) {
return (ssize_t) -1;
}
Expand All @@ -69,7 +73,7 @@ static ssize_t php_stream_memory_write(php_stream *stream, const char *buf, size
ZEND_ASSERT(buf != NULL);
memcpy(ZSTR_VAL(ms->data) + ms->fpos, (char*) buf, count);
ZSTR_VAL(ms->data)[ZSTR_LEN(ms->data)] = '\0';
ms->fpos += count;
ms->fpos = new_pos;
}
return count;
}
Expand Down Expand Up @@ -142,9 +146,12 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe
return 0;
}
} else {
if (UNEXPECTED((size_t)offset > ZEND_LONG_MAX - ms->fpos)) {
return -1;
}
stream->eof = 0;
stream->fatal_error = 0;
ms->fpos = ms->fpos + offset;
ms->fpos += (size_t)offset;
*newoffs = ms->fpos;
return 0;
}
Expand All @@ -154,15 +161,18 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe
*newoffs = -1;
return -1;
} else {
ms->fpos = offset;
ms->fpos = (size_t)offset;
*newoffs = ms->fpos;
stream->eof = 0;
stream->fatal_error = 0;
return 0;
}
case SEEK_END:
if (offset > 0) {
ms->fpos = ZSTR_LEN(ms->data) + offset;
if (UNEXPECTED((size_t)offset > ZEND_LONG_MAX - ZSTR_LEN(ms->data))) {
return -1;
}
ms->fpos = ZSTR_LEN(ms->data) + (size_t)offset;
*newoffs = ms->fpos;
stream->eof = 0;
stream->fatal_error = 0;
Expand Down Expand Up @@ -459,7 +469,6 @@ static int php_stream_temp_seek(php_stream *stream, zend_off_t offset, int whenc
assert(ts != NULL);

if (!ts->innerstream) {
*newoffs = -1;
return -1;
}
ret = php_stream_seek(ts->innerstream, offset, whence);
Expand Down
Loading