diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 7e74de782ccb..204740f2b7b8 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -886,6 +886,14 @@ static zend_result phar_parse_pharfile(php_stream *fp, char *fname, size_t fname sig_ptr = sig_buf; PHAR_GET_32(sig_ptr, signature_len); + if (signature_len > 1024 * 1024) { + efree(savebuf); + php_stream_close(fp); + if (error) { + spprintf(error, 0, "phar \"%s\" openssl signature length is too large", fname); + } + return FAILURE; + } sig = (char *) emalloc(signature_len); whence = signature_len + 4; whence = -whence; diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 18db3190bb03..7b675e2c900a 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -3591,6 +3591,11 @@ PHP_METHOD(Phar, offsetGet) if (!(entry = phar_get_entry_info_dir(phar_obj->archive, ZSTR_VAL(file_name), ZSTR_LEN(file_name), 1, &error, 0))) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s does not exist%s%s", ZSTR_VAL(file_name), error?", ":"", error?error:""); } else { + if (entry->is_temp_dir) { + efree(entry->filename); + efree(entry); + } + if (zend_string_equals_literal(file_name, ".phar/stub.php")) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot get stub \".phar/stub.php\" directly in phar \"%s\", use getStub", phar_obj->archive->fname); RETURN_THROWS(); @@ -3606,11 +3611,6 @@ PHP_METHOD(Phar, offsetGet) RETURN_THROWS(); } - if (entry->is_temp_dir) { - efree(entry->filename); - efree(entry); - } - zend_string *sfname = strpprintf(0, "phar://%s/%s", phar_obj->archive->fname, ZSTR_VAL(file_name)); zval zfname; ZVAL_NEW_STR(&zfname, sfname); @@ -3681,11 +3681,13 @@ static void phar_add_file(phar_archive_data **pphar, zend_string *file_name, con size_t written_len = php_stream_write(data->fp, ZSTR_VAL(content), ZSTR_LEN(content)); if (written_len != contents_len) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s could not be written to", filename); + phar_entry_delref(data); goto finish; } } else { if (!(php_stream_from_zval_no_verify(contents_file, zresource))) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s could not be written to", filename); + phar_entry_delref(data); goto finish; } php_stream_copy_to_stream_ex(contents_file, data->fp, PHP_STREAM_COPY_ALL, &contents_len); diff --git a/ext/phar/tests/gh21798-add-file-delref.phpt b/ext/phar/tests/gh21798-add-file-delref.phpt new file mode 100644 index 000000000000..fa893a1975e3 --- /dev/null +++ b/ext/phar/tests/gh21798-add-file-delref.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-21798: phar_add_file must call phar_entry_delref on write error paths +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--FILE-- +addFromString('hello.txt', 'hello world'); +$phar->addFromString('empty.txt', ''); +unset($phar); + +$phar = new Phar($fname); +echo $phar['hello.txt']->getContent() . "\n"; +echo ($phar->offsetExists('empty.txt') ? 'empty exists' : 'missing') . "\n"; +echo "no crash\n"; +?> +--CLEAN-- + +--EXPECT-- +hello world +empty exists +no crash diff --git a/ext/phar/tests/gh21798-offsetget-temp-entry.phpt b/ext/phar/tests/gh21798-offsetget-temp-entry.phpt new file mode 100644 index 000000000000..b5b11ad43634 --- /dev/null +++ b/ext/phar/tests/gh21798-offsetget-temp-entry.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-21798: Phar::offsetGet() must free is_temp_dir entry before rejecting .phar/* paths +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--FILE-- +addFromString('index.php', ''); +unset($phar); + +$phar = new Phar($fname); +try { + $phar->offsetGet('.phar/stub.php'); +} catch (BadMethodCallException $e) { + echo $e->getMessage() . "\n"; +} +try { + $phar->offsetGet('.phar/alias.txt'); +} catch (BadMethodCallException $e) { + echo $e->getMessage() . "\n"; +} +try { + $phar->offsetGet('.phar/internal'); +} catch (BadMethodCallException $e) { + echo $e->getMessage() . "\n"; +} +echo "no crash\n"; +?> +--CLEAN-- + +--EXPECT-- +Entry .phar/stub.php does not exist +Entry .phar/alias.txt does not exist +Entry .phar/internal does not exist +no crash diff --git a/ext/phar/tests/gh21798-sig-len-cap.phpt b/ext/phar/tests/gh21798-sig-len-cap.phpt new file mode 100644 index 000000000000..1746b7974d1d --- /dev/null +++ b/ext/phar/tests/gh21798-sig-len-cap.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-21798: phar rejects oversized OpenSSL signature length field +--EXTENSIONS-- +phar +--SKIPIF-- + +--INI-- +phar.require_hash=0 +phar.readonly=0 +--FILE-- +getMessage() . "\n"; +} +echo "done\n"; +?> +--CLEAN-- + +--EXPECTF-- +phar "%s" openssl signature length is too large +done