From 8fe20cefce17dc95abf0265dcd28509a13b4fbd7 Mon Sep 17 00:00:00 2001 From: l0gic Date: Sat, 6 Jun 2026 00:12:18 +0500 Subject: [PATCH] ext/zip: Make ZipArchive non-serializable A ZipArchive wraps a libzip resource that cannot survive serialization, so unserialize() silently produced a broken object (numFiles = 0) instead of erroring. Mark the class as not-serializable, consistent with Socket, CurlHandle and the Dba classes. Fixes GH-21682 --- NEWS | 2 ++ ext/standard/tests/strings/bug72434.phpt | 2 +- ext/zip/php_zip.stub.php | 3 +++ ext/zip/php_zip_arginfo.h | 4 ++-- ext/zip/tests/gh21682.phpt | 14 ++++++++++++++ 5 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 ext/zip/tests/gh21682.phpt diff --git a/NEWS b/NEWS index 496b61ef81b1..abeb8021fd01 100644 --- a/NEWS +++ b/NEWS @@ -266,6 +266,8 @@ PHP NEWS (kocsismate) - Zip: + . Fixed bug GH-21682 (ZipArchive must not be serializable). + (Nickolay Harin) . Fixed ZipArchive callback being called after executor has shut down. (ilutov) . Support minimum version for libzip dependency updated to 1.0.0. diff --git a/ext/standard/tests/strings/bug72434.phpt b/ext/standard/tests/strings/bug72434.phpt index 6d64baa26fa7..fb1336a4758b 100644 --- a/ext/standard/tests/strings/bug72434.phpt +++ b/ext/standard/tests/strings/bug72434.phpt @@ -5,7 +5,7 @@ Bug #72434: ZipArchive class Use After Free Vulnerability in PHP's GC algorithm // The following array will be serialized and this representation will be freed later on. $free_me = array(new StdClass()); // Create our payload and unserialize it. -$serialized_payload = 'a:3:{i:1;N;i:2;O:10:"ZipArchive":1:{s:8:"filename";'.serialize($free_me).'}i:1;R:4;}'; +$serialized_payload = 'a:3:{i:1;N;i:2;O:8:"stdClass":1:{s:8:"filename";'.serialize($free_me).'}i:1;R:4;}'; $unserialized_payload = unserialize($serialized_payload); gc_collect_cycles(); // The reference counter for $free_me is at -1 for PHP 7 right now. diff --git a/ext/zip/php_zip.stub.php b/ext/zip/php_zip.stub.php index 49dd19e53553..2fa8cdfe1e23 100644 --- a/ext/zip/php_zip.stub.php +++ b/ext/zip/php_zip.stub.php @@ -64,6 +64,9 @@ function zip_entry_filesize($zip_entry): int|false {} #[\Deprecated(since: '8.0', message: 'use ZipArchive::statIndex() instead')] function zip_entry_compressionmethod($zip_entry): string|false {} +/** + * @not-serializable + */ class ZipArchive implements Countable { /** diff --git a/ext/zip/php_zip_arginfo.h b/ext/zip/php_zip_arginfo.h index faa6feb1cb1e..ed35782cbddd 100644 --- a/ext/zip/php_zip_arginfo.h +++ b/ext/zip/php_zip_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit php_zip.stub.php instead. - * Stub hash: d623efdfe5ac46f07aebf8fb120050c818f3d793 */ + * Stub hash: 0b3a1bc6477555f00cb1fdf88f896d9ebc012c4f */ ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_open, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) @@ -536,7 +536,7 @@ static zend_class_entry *register_class_ZipArchive(zend_class_entry *class_entry zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "ZipArchive", class_ZipArchive_methods); - class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_NOT_SERIALIZABLE); zend_class_implements(class_entry, 1, class_entry_Countable); zval const_CREATE_value; diff --git a/ext/zip/tests/gh21682.phpt b/ext/zip/tests/gh21682.phpt new file mode 100644 index 000000000000..28bea1b9ffcf --- /dev/null +++ b/ext/zip/tests/gh21682.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-21682 (ZipArchive must not be serializable) +--EXTENSIONS-- +zip +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECT-- +Serialization of 'ZipArchive' is not allowed