From d36c5514d3893578f1c8cce4d0a2256abfcdd1ed Mon Sep 17 00:00:00 2001
From: bruvzg <7645683+bruvzg@users.noreply.github.com>
Date: Wed, 11 May 2022 15:15:58 +0300
Subject: Fix ZipIO crash when reused (and possible leaks).

---
 core/io/zip_io.cpp                                 | 58 ++++++++++++++--------
 core/io/zip_io.h                                   |  6 +--
 editor/editor_asset_installer.cpp                  |  6 ++-
 editor/editor_export.cpp                           |  3 +-
 editor/export_template_manager.cpp                 |  6 ++-
 editor/project_manager.cpp                         |  6 ++-
 platform/android/export/export_plugin.cpp          |  8 +--
 platform/iphone/export/export_plugin.cpp           |  3 +-
 .../api/javascript_tools_editor_plugin.cpp         |  3 +-
 platform/javascript/export/export_plugin.cpp       |  3 +-
 platform/osx/export/export_plugin.cpp              |  6 ++-
 platform/uwp/export/export_plugin.cpp              |  3 +-
 12 files changed, 70 insertions(+), 41 deletions(-)

diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp
index 2cc844b628..e573e8de19 100644
--- a/core/io/zip_io.cpp
+++ b/core/io/zip_io.cpp
@@ -31,18 +31,19 @@
 #include "zip_io.h"
 
 void *zipio_open(voidpf opaque, const char *p_fname, int mode) {
-	ZipIOData *zd = (ZipIOData *)opaque;
+	Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+	ERR_FAIL_COND_V(fa == nullptr, nullptr);
 
 	String fname;
 	fname.parse_utf8(p_fname);
 
 	if (mode & ZLIB_FILEFUNC_MODE_WRITE) {
-		zd->f = FileAccess::open(fname, FileAccess::WRITE);
+		(*fa) = FileAccess::open(fname, FileAccess::WRITE);
 	} else {
-		zd->f = FileAccess::open(fname, FileAccess::READ);
+		(*fa) = FileAccess::open(fname, FileAccess::READ);
 	}
 
-	if (zd->f.is_null()) {
+	if (fa->is_null()) {
 		return nullptr;
 	}
 
@@ -50,49 +51,66 @@ void *zipio_open(voidpf opaque, const char *p_fname, int mode) {
 }
 
 uLong zipio_read(voidpf opaque, voidpf stream, void *buf, uLong size) {
-	ZipIOData *zd = (ZipIOData *)opaque;
-	return zd->f->get_buffer((uint8_t *)buf, size);
+	Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+	ERR_FAIL_COND_V(fa == nullptr, 0);
+	ERR_FAIL_COND_V(fa->is_null(), 0);
+
+	return (*fa)->get_buffer((uint8_t *)buf, size);
 }
 
 uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) {
-	ZipIOData *zd = (ZipIOData *)opaque;
-	zd->f->store_buffer((uint8_t *)buf, size);
+	Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+	ERR_FAIL_COND_V(fa == nullptr, 0);
+	ERR_FAIL_COND_V(fa->is_null(), 0);
+
+	(*fa)->store_buffer((uint8_t *)buf, size);
 	return size;
 }
 
 long zipio_tell(voidpf opaque, voidpf stream) {
-	ZipIOData *zd = (ZipIOData *)opaque;
-	return zd->f->get_position();
+	Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+	ERR_FAIL_COND_V(fa == nullptr, 0);
+	ERR_FAIL_COND_V(fa->is_null(), 0);
+
+	return (*fa)->get_position();
 }
 
 long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) {
-	ZipIOData *zd = (ZipIOData *)opaque;
+	Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+	ERR_FAIL_COND_V(fa == nullptr, 0);
+	ERR_FAIL_COND_V(fa->is_null(), 0);
 
 	uint64_t pos = offset;
 	switch (origin) {
 		case ZLIB_FILEFUNC_SEEK_CUR:
-			pos = zd->f->get_position() + offset;
+			pos = (*fa)->get_position() + offset;
 			break;
 		case ZLIB_FILEFUNC_SEEK_END:
-			pos = zd->f->get_length() + offset;
+			pos = (*fa)->get_length() + offset;
 			break;
 		default:
 			break;
 	}
 
-	zd->f->seek(pos);
+	(*fa)->seek(pos);
 	return 0;
 }
 
 int zipio_close(voidpf opaque, voidpf stream) {
-	ZipIOData *zd = (ZipIOData *)opaque;
-	memdelete(zd);
+	Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+	ERR_FAIL_COND_V(fa == nullptr, 0);
+	ERR_FAIL_COND_V(fa->is_null(), 0);
+
+	fa->unref();
 	return 0;
 }
 
 int zipio_testerror(voidpf opaque, voidpf stream) {
-	ZipIOData *zd = (ZipIOData *)opaque;
-	return (zd->f.is_valid() && zd->f->get_error() != OK) ? 1 : 0;
+	Ref<FileAccess> *fa = reinterpret_cast<Ref<FileAccess> *>(opaque);
+	ERR_FAIL_COND_V(fa == nullptr, 1);
+	ERR_FAIL_COND_V(fa->is_null(), 0);
+
+	return (fa->is_valid() && (*fa)->get_error() != OK) ? 1 : 0;
 }
 
 voidpf zipio_alloc(voidpf opaque, uInt items, uInt size) {
@@ -105,9 +123,9 @@ void zipio_free(voidpf opaque, voidpf address) {
 	memfree(address);
 }
 
-zlib_filefunc_def zipio_create_io() {
+zlib_filefunc_def zipio_create_io(Ref<FileAccess> *p_data) {
 	zlib_filefunc_def io;
-	io.opaque = (void *)memnew(ZipIOData);
+	io.opaque = (void *)p_data;
 	io.zopen_file = zipio_open;
 	io.zread_file = zipio_read;
 	io.zwrite_file = zipio_write;
diff --git a/core/io/zip_io.h b/core/io/zip_io.h
index 3bcd1f830d..f137bd2bbf 100644
--- a/core/io/zip_io.h
+++ b/core/io/zip_io.h
@@ -39,10 +39,6 @@
 #include "thirdparty/minizip/unzip.h"
 #include "thirdparty/minizip/zip.h"
 
-struct ZipIOData {
-	Ref<FileAccess> f;
-};
-
 void *zipio_open(voidpf opaque, const char *p_fname, int mode);
 uLong zipio_read(voidpf opaque, voidpf stream, void *buf, uLong size);
 uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size);
@@ -57,6 +53,6 @@ int zipio_testerror(voidpf opaque, voidpf stream);
 voidpf zipio_alloc(voidpf opaque, uInt items, uInt size);
 void zipio_free(voidpf opaque, voidpf address);
 
-zlib_filefunc_def zipio_create_io();
+zlib_filefunc_def zipio_create_io(Ref<FileAccess> *p_data);
 
 #endif // ZIP_IO_H
diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp
index 7dcb9a4088..f60dcade82 100644
--- a/editor/editor_asset_installer.cpp
+++ b/editor/editor_asset_installer.cpp
@@ -64,7 +64,8 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) {
 	package_path = p_path;
 	Set<String> files_sorted;
 
-	zlib_filefunc_def io = zipio_create_io();
+	Ref<FileAccess> io_fa;
+	zlib_filefunc_def io = zipio_create_io(&io_fa);
 
 	unzFile pkg = unzOpen2(p_path.utf8().get_data(), &io);
 	if (!pkg) {
@@ -237,7 +238,8 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) {
 }
 
 void EditorAssetInstaller::ok_pressed() {
-	zlib_filefunc_def io = zipio_create_io();
+	Ref<FileAccess> io_fa;
+	zlib_filefunc_def io = zipio_create_io(&io_fa);
 
 	unzFile pkg = unzOpen2(package_path.utf8().get_data(), &io);
 	if (!pkg) {
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index a21ee46818..ef99425f68 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -1334,7 +1334,8 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
 Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
 	EditorProgress ep("savezip", TTR("Packing"), 102, true);
 
-	zlib_filefunc_def io = zipio_create_io();
+	Ref<FileAccess> io_fa;
+	zlib_filefunc_def io = zipio_create_io(&io_fa);
 	zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io);
 
 	ZipData zd;
diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp
index 3526b4ce4c..68796683d2 100644
--- a/editor/export_template_manager.cpp
+++ b/editor/export_template_manager.cpp
@@ -374,7 +374,8 @@ void ExportTemplateManager::_install_file() {
 }
 
 bool ExportTemplateManager::_install_file_selected(const String &p_file, bool p_skip_progress) {
-	zlib_filefunc_def io = zipio_create_io();
+	Ref<FileAccess> io_fa;
+	zlib_filefunc_def io = zipio_create_io(&io_fa);
 
 	unzFile pkg = unzOpen2(p_file.utf8().get_data(), &io);
 	if (!pkg) {
@@ -676,7 +677,8 @@ Error ExportTemplateManager::install_android_template_from_file(const String &p_
 
 	// Uncompress source template.
 
-	zlib_filefunc_def io = zipio_create_io();
+	Ref<FileAccess> io_fa;
+	zlib_filefunc_def io = zipio_create_io(&io_fa);
 
 	unzFile pkg = unzOpen2(p_file.utf8().get_data(), &io);
 	ERR_FAIL_COND_V_MSG(!pkg, ERR_CANT_OPEN, "Android sources not in ZIP format.");
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 565bf94708..b4cbb3b7f1 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -186,7 +186,8 @@ private:
 		if (mode == MODE_IMPORT || mode == MODE_RENAME) {
 			if (!valid_path.is_empty() && !d->file_exists("project.godot")) {
 				if (valid_path.ends_with(".zip")) {
-					zlib_filefunc_def io = zipio_create_io();
+					Ref<FileAccess> io_fa;
+					zlib_filefunc_def io = zipio_create_io(&io_fa);
 
 					unzFile pkg = unzOpen2(valid_path.utf8().get_data(), &io);
 					if (!pkg) {
@@ -499,7 +500,8 @@ private:
 						zip_path = project_path->get_text();
 					}
 
-					zlib_filefunc_def io = zipio_create_io();
+					Ref<FileAccess> io_fa;
+					zlib_filefunc_def io = zipio_create_io(&io_fa);
 
 					unzFile pkg = unzOpen2(zip_path.utf8().get_data(), &io);
 					if (!pkg) {
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index d357cd586e..5aef64943b 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -2775,7 +2775,8 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 		return ERR_FILE_BAD_PATH;
 	}
 
-	zlib_filefunc_def io = zipio_create_io();
+	Ref<FileAccess> io_fa;
+	zlib_filefunc_def io = zipio_create_io(&io_fa);
 
 	if (ep.step(TTR("Creating APK..."), 0)) {
 		return ERR_SKIP;
@@ -2789,7 +2790,8 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 
 	int ret = unzGoToFirstFile(pkg);
 
-	zlib_filefunc_def io2 = zipio_create_io();
+	Ref<FileAccess> io2_fa;
+	zlib_filefunc_def io2 = zipio_create_io(&io2_fa);
 
 	String tmp_unaligned_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
 
@@ -2985,7 +2987,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 
 	ret = unzGoToFirstFile(tmp_unaligned);
 
-	io2 = zipio_create_io();
+	io2 = zipio_create_io(&io2_fa);
 	zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2);
 
 	// Take files from the unaligned APK and write them out to the aligned one
diff --git a/platform/iphone/export/export_plugin.cpp b/platform/iphone/export/export_plugin.cpp
index 57bee59523..71f3b5f59f 100644
--- a/platform/iphone/export/export_plugin.cpp
+++ b/platform/iphone/export/export_plugin.cpp
@@ -1488,7 +1488,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
 	ERR_FAIL_COND_V(tmp_app_path.is_null(), ERR_CANT_CREATE);
 
 	print_line("Unzipping...");
-	zlib_filefunc_def io = zipio_create_io();
+	Ref<FileAccess> io_fa;
+	zlib_filefunc_def io = zipio_create_io(&io_fa);
 	unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io);
 	if (!src_pkg_zip) {
 		EditorNode::add_io_error("Could not open export template (not a zip file?):\n" + src_pkg_name);
diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp
index 198af61eff..1507f32375 100644
--- a/platform/javascript/api/javascript_tools_editor_plugin.cpp
+++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp
@@ -64,7 +64,8 @@ void JavaScriptToolsEditorPlugin::_download_zip(Variant p_v) {
 	}
 	String resource_path = ProjectSettings::get_singleton()->get_resource_path();
 
-	zlib_filefunc_def io = zipio_create_io();
+	Ref<FileAccess> io_fa;
+	zlib_filefunc_def io = zipio_create_io(&io_fa);
 
 	// Name the downloaded ZIP file to contain the project name and download date for easier organization.
 	// Replace characters not allowed (or risky) in Windows file names with safe characters.
diff --git a/platform/javascript/export/export_plugin.cpp b/platform/javascript/export/export_plugin.cpp
index 66d93d7c49..9576256d03 100644
--- a/platform/javascript/export/export_plugin.cpp
+++ b/platform/javascript/export/export_plugin.cpp
@@ -33,7 +33,8 @@
 #include "core/config/project_settings.h"
 
 Error EditorExportPlatformJavaScript::_extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa) {
-	zlib_filefunc_def io = zipio_create_io();
+	Ref<FileAccess> io_fa;
+	zlib_filefunc_def io = zipio_create_io(&io_fa);
 	unzFile pkg = unzOpen2(p_template.utf8().get_data(), &io);
 
 	if (!pkg) {
diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp
index 94ef875072..465925524c 100644
--- a/platform/osx/export/export_plugin.cpp
+++ b/platform/osx/export/export_plugin.cpp
@@ -722,7 +722,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 		return ERR_FILE_BAD_PATH;
 	}
 
-	zlib_filefunc_def io = zipio_create_io();
+	Ref<FileAccess> io_fa;
+	zlib_filefunc_def io = zipio_create_io(&io_fa);
 
 	if (ep.step(TTR("Creating app bundle"), 0)) {
 		return ERR_SKIP;
@@ -1327,7 +1328,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 					OS::get_singleton()->move_to_trash(p_path);
 				}
 
-				zlib_filefunc_def io_dst = zipio_create_io();
+				Ref<FileAccess> io_fa_dst;
+				zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst);
 				zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
 
 				_zip_folder_recursive(zip, tmp_base_path_name, "", pkg_name);
diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp
index 7e06bf01e3..65a5ee7140 100644
--- a/platform/uwp/export/export_plugin.cpp
+++ b/platform/uwp/export/export_plugin.cpp
@@ -301,7 +301,8 @@ Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_p
 	AppxPackager packager;
 	packager.init(fa_pack);
 
-	zlib_filefunc_def io = zipio_create_io();
+	Ref<FileAccess> io_fa;
+	zlib_filefunc_def io = zipio_create_io(&io_fa);
 
 	if (ep.step("Creating package...", 0)) {
 		return ERR_SKIP;
-- 
cgit v1.2.3