From ff09d9aa87c9ff69b699bf23db4fd30d01caa615 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 22 Sep 2024 20:35:56 +0200 Subject: [PATCH 1/3] Fix GH-15980: Signed integer overflow in main/streams/streams.c We need to avoid signed integer overflows which are undefined behavior. We catch that, and set `offset` to `ZEND_LONG_MAX` (which is also the largest value of `zend_off_t` on all platforms). Of course, after such a seek a stream is no longer readable, but that matches the current behavior for offsets near `ZEND_LONG_MAX`. --- ext/standard/tests/streams/gh15980.phpt | 11 +++++++++++ main/streams/streams.c | 9 +++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 ext/standard/tests/streams/gh15980.phpt diff --git a/ext/standard/tests/streams/gh15980.phpt b/ext/standard/tests/streams/gh15980.phpt new file mode 100644 index 0000000000000..125751648bfa0 --- /dev/null +++ b/ext/standard/tests/streams/gh15980.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-15980 (Signed integer overflow in main/streams/streams.c) +--FILE-- + 1); +?> +--EXPECT-- +bool(true) diff --git a/main/streams/streams.c b/main/streams/streams.c index e22d9e51d594a..4c66d8aadc39b 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1354,8 +1354,13 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) switch(whence) { case SEEK_CUR: - offset = stream->position + offset; - whence = SEEK_SET; + ZEND_ASSERT(stream->position >= 0); + if (UNEXPECTED(offset > ZEND_LONG_MAX - stream->position)) { + offset = ZEND_LONG_MAX; + } else { + offset = stream->position + offset; + } + whence = SEEK_SET; break; } ret = stream->ops->seek(stream, offset, whence, &stream->position); From c5354800fe31ab423d95a8aaf1098a26b593c24c Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 23 Sep 2024 11:54:57 +0200 Subject: [PATCH 2/3] see what actually happens --- ext/standard/tests/streams/gh15980.phpt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/standard/tests/streams/gh15980.phpt b/ext/standard/tests/streams/gh15980.phpt index 125751648bfa0..1601101b827b9 100644 --- a/ext/standard/tests/streams/gh15980.phpt +++ b/ext/standard/tests/streams/gh15980.phpt @@ -4,8 +4,7 @@ GH-15980 (Signed integer overflow in main/streams/streams.c) 1); +var_dump(fseek($s, PHP_INT_MAX, SEEK_CUR)); +var_dump(ftell($s)); ?> --EXPECT-- -bool(true) From e90b6d08336bf6e59477d06f6ff5ac74866239da Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 23 Sep 2024 12:21:33 +0200 Subject: [PATCH 3/3] Adapt test case to our findings For some OS/FS, `fseek()` may fail, so `ftell()` will obviously report the old position. --- ext/standard/tests/streams/gh15980.phpt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/streams/gh15980.phpt b/ext/standard/tests/streams/gh15980.phpt index 1601101b827b9..7a9d8364a90ae 100644 --- a/ext/standard/tests/streams/gh15980.phpt +++ b/ext/standard/tests/streams/gh15980.phpt @@ -4,7 +4,9 @@ GH-15980 (Signed integer overflow in main/streams/streams.c) 1); ?> --EXPECT-- +bool(true)