From c50b37d2312ec22c68b66b54cc10e52804633ede Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 7 Sep 2025 23:33:29 +0200 Subject: [PATCH 1/2] Fix metadata leak when phar convert logic fails Closes GH-19756. --- NEWS | 1 + ext/phar/phar_object.c | 2 ++ .../tests/phar_convert_metadata_leak.phpt | 24 +++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 ext/phar/tests/phar_convert_metadata_leak.phpt diff --git a/NEWS b/NEWS index 0cc3a4c0e83..34061a9e19f 100644 --- a/NEWS +++ b/NEWS @@ -48,6 +48,7 @@ PHP NEWS - Phar: . Fixed memory leaks when verifying OpenSSL signature. (Girgias) . Fix memory leak in phar tar temporary file error handling code. (nielsdos) + . Fix metadata leak when phar convert logic fails. (nielsdos) - Standard: . Fixed bug GH-16649 (UAF during array_splice). (alexandre-daubois) diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index cb032095c39..17d6c3c64fd 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -2281,6 +2281,7 @@ static zend_object *phar_convert_to_other(phar_archive_data *source, int convert } if (FAILURE == phar_copy_file_contents(&newentry, phar->fp)) { + phar_metadata_tracker_free(&phar->metadata_tracker, phar->is_persistent); zend_hash_destroy(&(phar->manifest)); php_stream_close(phar->fp); efree(phar); @@ -2318,6 +2319,7 @@ no_copy: return ret; } else { if(phar != NULL) { + phar_metadata_tracker_free(&phar->metadata_tracker, phar->is_persistent); zend_hash_destroy(&(phar->manifest)); zend_hash_destroy(&(phar->mounted_dirs)); zend_hash_destroy(&(phar->virtual_dirs)); diff --git a/ext/phar/tests/phar_convert_metadata_leak.phpt b/ext/phar/tests/phar_convert_metadata_leak.phpt new file mode 100644 index 00000000000..61a240c8888 --- /dev/null +++ b/ext/phar/tests/phar_convert_metadata_leak.phpt @@ -0,0 +1,24 @@ +--TEST-- +Phar convert logic leaks metadata +--EXTENSIONS-- +phar +--INI-- +phar.require_hash=0 +phar.readonly=0 +--FILE-- +setMetadata("foobar"); +$phar['x'] = 'hi'; +try { + $phar->convertToData(Phar::ZIP, Phar::NONE, 'phar.zip'); +} catch (BadMethodCallException $e) { + echo $e->getMessage(),"\n"; +} +?> +--CLEAN-- + +--EXPECTF-- +data phar "%s" has invalid extension phar.zip From 98bb9346850d7000130016935c36bb99606c9f07 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 7 Sep 2025 23:18:03 +0200 Subject: [PATCH 2/2] Fix memory leak on failure in phar_convert_to_other() Closes GH-19755. --- NEWS | 1 + ext/phar/phar_object.c | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 34061a9e19f..151c3df9613 100644 --- a/NEWS +++ b/NEWS @@ -49,6 +49,7 @@ PHP NEWS . Fixed memory leaks when verifying OpenSSL signature. (Girgias) . Fix memory leak in phar tar temporary file error handling code. (nielsdos) . Fix metadata leak when phar convert logic fails. (nielsdos) + . Fix memory leak on failure in phar_convert_to_other(). (nielsdos) - Standard: . Fixed bug GH-16649 (UAF during array_splice). (alexandre-daubois) diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 17d6c3c64fd..e91ebd0735e 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -2229,6 +2229,12 @@ static zend_object *phar_convert_to_other(phar_archive_data *source, int convert PHAR_G(last_phar) = NULL; PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + php_stream *tmp_fp = php_stream_fopen_tmpfile(); + if (tmp_fp == NULL) { + zend_throw_exception_ex(phar_ce_PharException, 0, "unable to create temporary file"); + return NULL; + } + phar = (phar_archive_data *) ecalloc(1, sizeof(phar_archive_data)); /* set whole-archive compression and type from parameter */ phar->flags = flags; @@ -2253,11 +2259,7 @@ static zend_object *phar_convert_to_other(phar_archive_data *source, int convert zend_hash_init(&phar->virtual_dirs, sizeof(char *), zend_get_hash_value, NULL, 0); - phar->fp = php_stream_fopen_tmpfile(); - if (phar->fp == NULL) { - zend_throw_exception_ex(phar_ce_PharException, 0, "unable to create temporary file"); - return NULL; - } + phar->fp = tmp_fp; phar->fname = source->fname; phar->fname_len = source->fname_len; phar->is_temporary_alias = source->is_temporary_alias;