Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ PHP NEWS
(David Carlier)
. Fixed bug GH-23747 (ZipArchive::close() use-after-free from a progress or
cancel callback). (David Carlier)
. Fixed a use-after-free when a ZipArchive method that modifies the archive
is called from a progress or cancel callback during close().
(Ilia Alshanetsky)


24 Sep 2026, PHP 8.4.26
Expand Down
127 changes: 123 additions & 4 deletions ext/zip/php_zip.c
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,15 @@ static char * php_zipobj_get_zip_comment(ze_zip_object *obj, int *len) /* {{{ */
}
/* }}} */

static bool php_zipobj_closing(ze_zip_object *obj)
{
if (obj->archive && obj->archive->close) {
zend_throw_error(NULL, "Already being closed");
return true;
}
return false;
}

#ifdef HAVE_GLOB /* {{{ */
#ifndef GLOB_ONLYDIR
#define GLOB_ONLYDIR (1<<30)
Expand Down Expand Up @@ -1582,9 +1591,8 @@ PHP_METHOD(ZipArchive, open)

if (ze_obj->archive) {
/* we already have an opened zip, free it */
if (ze_obj->archive->close) {
if (php_zipobj_closing(ze_obj)) {
efree(resolved_path);
zend_throw_error(NULL, "Already being closed");
RETURN_THROWS();
}
intern = ze_obj->archive->za;
Expand Down Expand Up @@ -1647,6 +1655,10 @@ PHP_METHOD(ZipArchive, setPassword)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (password_len < 1) {
RETURN_FALSE;
}
Expand Down Expand Up @@ -1676,8 +1688,7 @@ PHP_METHOD(ZipArchive, close)

ze_obj = Z_ZIP_P(self);

if (ze_obj->archive->close) {
zend_throw_error(NULL, "Already being closed");
if (php_zipobj_closing(ze_obj)) {
RETURN_THROWS();
}

Expand Down Expand Up @@ -1823,6 +1834,10 @@ PHP_METHOD(ZipArchive, addEmptyDir)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (dirname_len<1) {
RETURN_FALSE;
}
Expand Down Expand Up @@ -1881,6 +1896,10 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /*
RETURN_THROWS();
}

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (type == 1) {
found = php_zip_glob(ZSTR_VAL(pattern), ZSTR_LEN(pattern), glob_flags, return_value);
} else {
Expand Down Expand Up @@ -2015,6 +2034,10 @@ PHP_METHOD(ZipArchive, addFile)
entry_name_len = ZSTR_LEN(filename);
}

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (php_zip_add_file(Z_ZIP_P(self), ZSTR_VAL(filename), ZSTR_LEN(filename),
entry_name, entry_name_len, offset_start, offset_len, -1, flags) < 0) {
RETURN_FALSE;
Expand Down Expand Up @@ -2048,6 +2071,10 @@ PHP_METHOD(ZipArchive, replaceFile)
RETURN_THROWS();
}

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (php_zip_add_file(Z_ZIP_P(self), ZSTR_VAL(filename), ZSTR_LEN(filename),
NULL, 0, offset_start, offset_len, index, flags) < 0) {
RETURN_FALSE;
Expand Down Expand Up @@ -2078,6 +2105,10 @@ PHP_METHOD(ZipArchive, addFromString)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

ze_obj = Z_ZIP_P(self);
archive = ze_obj->archive;
if (archive->buffers_cnt) {
Expand Down Expand Up @@ -2220,6 +2251,10 @@ PHP_METHOD(ZipArchive, setArchiveComment)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (comment_len > 0xffff) {
zend_argument_value_error(1, "must be less than 65535 bytes");
RETURN_THROWS();
Expand Down Expand Up @@ -2268,6 +2303,10 @@ PHP_METHOD(ZipArchive, setArchiveFlag)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (zip_set_archive_flag(intern, flag, (int)value)) {
RETURN_FALSE;
} else {
Expand Down Expand Up @@ -2311,6 +2350,10 @@ PHP_METHOD(ZipArchive, setCommentName)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (comment_len > 0xffff) {
zend_argument_value_error(2, "must be less than 65535 bytes");
RETURN_THROWS();
Expand Down Expand Up @@ -2342,6 +2385,10 @@ PHP_METHOD(ZipArchive, setCommentIndex)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (comment_len > 0xffff) {
zend_argument_value_error(2, "must be less than 65535 bytes");
RETURN_THROWS();
Expand Down Expand Up @@ -2374,6 +2421,10 @@ PHP_METHOD(ZipArchive, setExternalAttributesName)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (name_len == 0) {
zend_argument_must_not_be_empty_error(1);
RETURN_THROWS();
Expand Down Expand Up @@ -2407,6 +2458,10 @@ PHP_METHOD(ZipArchive, setExternalAttributesIndex)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

PHP_ZIP_STAT_INDEX(intern, index, 0, sb);
if (zip_file_set_external_attributes(intern, (zip_uint64_t)index,
(zip_flags_t)flags, (zip_uint8_t)(opsys&0xff), (zip_uint32_t)attr) < 0) {
Expand Down Expand Up @@ -2502,6 +2557,10 @@ PHP_METHOD(ZipArchive, setEncryptionName)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (name_len == 0) {
zend_argument_must_not_be_empty_error(1);
RETURN_THROWS();
Expand Down Expand Up @@ -2541,6 +2600,10 @@ PHP_METHOD(ZipArchive, setEncryptionIndex)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (UNEXPECTED(zip_file_set_encryption(intern, index, ZIP_EM_NONE, NULL) < 0)) {
php_error_docref(NULL, E_WARNING, "password reset failed");
RETURN_FALSE;
Expand Down Expand Up @@ -2629,6 +2692,10 @@ PHP_METHOD(ZipArchive, setCompressionName)

ZIP_FROM_OBJECT(intern, this);

if (php_zipobj_closing(Z_ZIP_P(this))) {
RETURN_THROWS();
}

if (name_len == 0) {
zend_argument_must_not_be_empty_error(1);
RETURN_THROWS();
Expand Down Expand Up @@ -2663,6 +2730,10 @@ PHP_METHOD(ZipArchive, setCompressionIndex)

ZIP_FROM_OBJECT(intern, this);

if (php_zipobj_closing(Z_ZIP_P(this))) {
RETURN_THROWS();
}

if (zip_set_file_compression(intern, (zip_uint64_t)index,
(zip_int32_t)comp_method, (zip_uint32_t)comp_flags) != 0) {
RETURN_FALSE;
Expand All @@ -2689,6 +2760,10 @@ PHP_METHOD(ZipArchive, setMtimeName)

ZIP_FROM_OBJECT(intern, this);

if (php_zipobj_closing(Z_ZIP_P(this))) {
RETURN_THROWS();
}

if (name_len == 0) {
zend_argument_must_not_be_empty_error(1);
RETURN_THROWS();
Expand Down Expand Up @@ -2723,6 +2798,10 @@ PHP_METHOD(ZipArchive, setMtimeIndex)

ZIP_FROM_OBJECT(intern, this);

if (php_zipobj_closing(Z_ZIP_P(this))) {
RETURN_THROWS();
}

if (zip_file_set_mtime(intern, (zip_uint64_t)index,
(time_t)mtime, (zip_uint32_t)flags) != 0) {
RETURN_FALSE;
Expand All @@ -2745,6 +2824,10 @@ PHP_METHOD(ZipArchive, deleteIndex)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (index < 0) {
RETURN_FALSE;
}
Expand Down Expand Up @@ -2772,6 +2855,10 @@ PHP_METHOD(ZipArchive, deleteName)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (name_len < 1) {
RETURN_FALSE;
}
Expand Down Expand Up @@ -2803,6 +2890,10 @@ PHP_METHOD(ZipArchive, renameIndex)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (new_name_len == 0) {
zend_argument_must_not_be_empty_error(2);
RETURN_THROWS();
Expand Down Expand Up @@ -2831,6 +2922,10 @@ PHP_METHOD(ZipArchive, renameName)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (new_name_len == 0) {
zend_argument_must_not_be_empty_error(2);
RETURN_THROWS();
Expand Down Expand Up @@ -2859,6 +2954,10 @@ PHP_METHOD(ZipArchive, unchangeIndex)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (index < 0) {
RETURN_FALSE;
}
Expand Down Expand Up @@ -2886,6 +2985,10 @@ PHP_METHOD(ZipArchive, unchangeName)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (name_len < 1) {
RETURN_FALSE;
}
Expand All @@ -2912,6 +3015,10 @@ PHP_METHOD(ZipArchive, unchangeAll)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (zip_unchange_all(intern) != 0) {
RETURN_FALSE;
} else {
Expand All @@ -2932,6 +3039,10 @@ PHP_METHOD(ZipArchive, unchangeArchive)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

if (zip_unchange_archive(intern) != 0) {
RETURN_FALSE;
} else {
Expand Down Expand Up @@ -3222,6 +3333,10 @@ PHP_METHOD(ZipArchive, registerProgressCallback)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

archive = Z_ZIP_P(self)->archive;

/* register */
Expand Down Expand Up @@ -3264,6 +3379,10 @@ PHP_METHOD(ZipArchive, registerCancelCallback)

ZIP_FROM_OBJECT(intern, self);

if (php_zipobj_closing(Z_ZIP_P(self))) {
RETURN_THROWS();
}

archive = Z_ZIP_P(self)->archive;

/* register */
Expand Down
Loading
Loading