From e43198ffd9e469b8bcf1c86e7c741688951ee23c Mon Sep 17 00:00:00 2001 From: lacatoire Date: Fri, 6 Mar 2026 09:35:02 +0100 Subject: [PATCH] Fix undefined behavior in php_stream_memory_seek() When offset is ZEND_LONG_MIN, (size_t)(-offset) triggers signed integer overflow which is undefined behavior. Replace with (size_t)0 - (size_t)offset to perform the negation in unsigned arithmetic, which is well-defined. Also adds a test to verify that seeking with PHP_INT_MIN does not crash and that the stream remains usable afterwards. --- .../streams/memory_stream_seek_int_min.phpt | 58 +++++++++++++++++++ main/streams/memory.c | 4 +- 2 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 ext/standard/tests/streams/memory_stream_seek_int_min.phpt diff --git a/ext/standard/tests/streams/memory_stream_seek_int_min.phpt b/ext/standard/tests/streams/memory_stream_seek_int_min.phpt new file mode 100644 index 0000000000000..55528e1a3c322 --- /dev/null +++ b/ext/standard/tests/streams/memory_stream_seek_int_min.phpt @@ -0,0 +1,58 @@ +--TEST-- +php://memory and php://temp stream seek with PHP_INT_MIN does not trigger undefined behavior +--FILE-- + +--EXPECT-- +=== php://memory === +int(-1) +int(-1) +int(-1) +int(0) +int(2) +int(0) +int(2) +string(5) "hello" +=== php://temp === +int(-1) +int(-1) +int(-1) +int(0) +int(2) +int(0) +int(2) +string(5) "hello" +Done diff --git a/main/streams/memory.c b/main/streams/memory.c index 9cb94542df6f5..bf0667415d850 100644 --- a/main/streams/memory.c +++ b/main/streams/memory.c @@ -128,7 +128,7 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe switch(whence) { case SEEK_CUR: if (offset < 0) { - if (ms->fpos < (size_t)(-offset)) { + if (ms->fpos < (size_t)0 - (size_t)offset) { ms->fpos = 0; *newoffs = -1; return -1; @@ -165,7 +165,7 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe stream->eof = 0; stream->fatal_error = 0; return 0; - } else if (ZSTR_LEN(ms->data) < (size_t)(-offset)) { + } else if (ZSTR_LEN(ms->data) < (size_t)0 - (size_t)offset) { ms->fpos = 0; *newoffs = -1; return -1;