From c398fe98c044c8e7c23135acdc38d4ef7bedc983 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:25:34 +0200 Subject: [PATCH 1/4] Fix buffer mismanagement in phar_dir_read() Fixes GHSA-jqcx-ccgc-xwhv. (cherry picked from commit 80316123f3e9dcce8ac419bd9dd43546e2ccb5ef) --- ext/phar/dirstream.c | 15 ++++++++------ ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt | 27 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index 4710703c70e..490b14528f1 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -91,25 +91,28 @@ static int phar_dir_seek(php_stream *stream, zend_off_t offset, int whence, zend */ static ssize_t phar_dir_read(php_stream *stream, char *buf, size_t count) /* {{{ */ { - size_t to_read; HashTable *data = (HashTable *)stream->abstract; zend_string *str_key; zend_ulong unused; + if (count != sizeof(php_stream_dirent)) { + return -1; + } + if (HASH_KEY_NON_EXISTENT == zend_hash_get_current_key(data, &str_key, &unused)) { return 0; } zend_hash_move_forward(data); - to_read = MIN(ZSTR_LEN(str_key), count); - if (to_read == 0 || count < ZSTR_LEN(str_key)) { + php_stream_dirent *dirent = (php_stream_dirent *) buf; + + if (sizeof(dirent->d_name) <= ZSTR_LEN(str_key)) { return 0; } - memset(buf, 0, sizeof(php_stream_dirent)); - memcpy(((php_stream_dirent *) buf)->d_name, ZSTR_VAL(str_key), to_read); - ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0'; + memset(dirent, 0, sizeof(php_stream_dirent)); + PHP_STRLCPY(dirent->d_name, ZSTR_VAL(str_key), sizeof(dirent->d_name), ZSTR_LEN(str_key)); return sizeof(php_stream_dirent); } diff --git a/ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt b/ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt new file mode 100644 index 00000000000..4e12f05fb62 --- /dev/null +++ b/ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt @@ -0,0 +1,27 @@ +--TEST-- +GHSA-jqcx-ccgc-xwhv (Buffer overflow and overread in phar_dir_read()) +--SKIPIF-- + +--INI-- +phar.readonly=0 +--FILE-- +startBuffering(); +$phar->addFromString(str_repeat('A', PHP_MAXPATHLEN - 1), 'This is the content of file 1.'); +$phar->addFromString(str_repeat('B', PHP_MAXPATHLEN - 1).'C', 'This is the content of file 2.'); +$phar->stopBuffering(); + +$handle = opendir('phar://' . __DIR__ . '/GHSA-jqcx-ccgc-xwhv.phar'); +var_dump(strlen(readdir($handle))); +// Must not be a string of length PHP_MAXPATHLEN+1 +var_dump(readdir($handle)); +closedir($handle); +?> +--CLEAN-- + +--EXPECTF-- +int(%d) +bool(false) -- 2.41.0