From 1edcfccdca8197b17efd0d87ee29358c8401a6ce Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 30 Dec 2023 23:49:28 +0100 Subject: [PATCH] Fix #77432: Segmentation fault on including phar file phar_get_pharfp() can return NULL. In this case this is because the stream gets closed by the include code in the engine. However, the phar entry is still cached, so when the next include happens the engine tries to read from a closed (and nullified) stream. Use the same fix as in phar_open_entry_fp(): take into account that the phar_get_pharfp() can return NULL and in that case reopen the phar archive. Closes GH-13056. --- NEWS | 3 +++ ext/phar/stream.c | 13 +++++++++- ext/phar/tests/bug77432.phpt | 46 ++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 ext/phar/tests/bug77432.phpt diff --git a/NEWS b/NEWS index 3dbd6980e74..aa5e4fe7188 100644 --- a/NEWS +++ b/NEWS @@ -51,6 +51,9 @@ PHP NEWS . Fixed bug GH-12974 (Apache crashes on shutdown when using pg_pconnect()). (nielsdos) +- Phar: + . Fixed bug #77432 (Segmentation fault on including phar file). (nielsdos) + - PHPDBG: . Fixed bug GH-12962 (Double free of init_file in phpdbg_prompt.c). (nielsdos) diff --git a/ext/phar/stream.c b/ext/phar/stream.c index b45b662398c..adebe5ab9bd 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -254,6 +254,17 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha php_url_free(resource); goto phar_stub; } else { + php_stream *stream = phar_get_pharfp(phar); + if (stream == NULL) { + if (UNEXPECTED(FAILURE == phar_open_archive_fp(phar))) { + php_stream_wrapper_log_error(wrapper, options, "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); + efree(internal_file); + php_url_free(resource); + return NULL; + } + stream = phar_get_pharfp(phar); + } + phar_entry_info *entry; entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info)); @@ -266,7 +277,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha entry->is_crc_checked = 1; idata = (phar_entry_data *) ecalloc(1, sizeof(phar_entry_data)); - idata->fp = phar_get_pharfp(phar); + idata->fp = stream; idata->phar = phar; idata->internal_file = entry; if (!phar->is_persistent) { diff --git a/ext/phar/tests/bug77432.phpt b/ext/phar/tests/bug77432.phpt new file mode 100644 index 00000000000..5e4d79c4a3d --- /dev/null +++ b/ext/phar/tests/bug77432.phpt @@ -0,0 +1,46 @@ +--TEST-- +Bug #77432 (Segmentation fault on including phar file) +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +--FILE-- +startBuffering(); +$phar->addFromString('test.txt', 'text'); +$phar->setStub(''); +$phar->stopBuffering(); +unset($phar); + +echo "--- Include 1 ---\n"; +include("phar://" . $filename); +echo "--- Include 2 ---\n"; +// Note: will warn because the halting offset is redefined, but won't display the name because "zend_mangle_property_name" starts the name with \0 +// However, this is just the easiest way to reproduce it, so go with this test. +include("phar://" . $filename); +echo "--- After unlink ---\n"; +unlink($filename); +// This will just fail, as it should, but it is here to test the reopen error-handling path. +include("phar://" . $filename); + +?> +--CLEAN-- + +--EXPECTF-- +--- Include 1 --- +hello world +--- Include 2 --- + +Warning: Constant already defined in %s on line %d +hello world +--- After unlink --- + +Warning: include(%sbug77432.phar): Failed to open stream: phar error: could not reopen phar "%sbug77432.phar" in %s on line %d + +Warning: include(): Failed opening '%sbug77432.phar' for inclusion (include_path='.:') in %s on line %d