Commit 4985676d96e for php

commit 4985676d96ef33acd413b9fe595747676b73cd33
Merge: be420663e32 66bbbe5fd04
Author: Weilin Du <weilindu@php.net>
Date:   Fri Oct 9 00:55:01 2026 +0800

    Merge branch 'PHP-8.5' into PHP-8.6

    * PHP-8.5:
      ext/zip: Reject ZipArchive mutators during close() (#24025)

diff --cc NEWS
index cce4f4a0ee4,7b8426b2b51..c895aa7b1dd
--- a/NEWS
+++ b/NEWS
@@@ -25,8 -17,11 +25,11 @@@ PH
  - Zip:
    . Fixed use-after-free when re-entering ZipArchive during destruction or
      a close warning, and rejected opening streams while closing. (jvoisin)
+   . 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)

 -22 Oct 2026, PHP 8.5.12
 +08 Oct 2026, PHP 8.6.0RC3

  - BCMath:
    . Fixed BcMath\Number results that truncate to zero keeping a negative sign
diff --cc ext/zip/php_zip.c
index fc2998a7ca3,7f95f55a3ca..5baecfc1893
--- a/ext/zip/php_zip.c
+++ b/ext/zip/php_zip.c
@@@ -1945,8 -1864,12 +1953,12 @@@ static void php_zip_add_from_pattern(IN
  		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);
 +		found = php_zip_glob(pattern, glob_flags, return_value);
  	} else {
  		found = php_zip_pcre(pattern, path, path_len, return_value);
  	}
@@@ -2079,8 -2002,16 +2091,12 @@@ 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;
 -	} else {
 -		RETURN_TRUE;
 -	}
 +	RETURN_BOOL(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) == SUCCESS);
  }
  /* }}} */

@@@ -2108,8 -2039,16 +2124,12 @@@ 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;
 -	} else {
 -		RETURN_TRUE;
 -	}
 +	RETURN_BOOL(php_zip_add_file(Z_ZIP_P(self), ZSTR_VAL(filename), ZSTR_LEN(filename),
 +			NULL, 0, offset_start, offset_len, index, flags) == SUCCESS);
  }
  /* }}} */

@@@ -2132,8 -2073,24 +2152,12 @@@ 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) {
 -		archive->buffers = (char **)safe_erealloc(archive->buffers, sizeof(char *), (archive->buffers_cnt+1), 0);
 -		pos = archive->buffers_cnt++;
 -	} else {
 -		archive->buffers = (char **)emalloc(sizeof(char *));
 -		archive->buffers_cnt++;
 -		pos = 0;
 -	}
 -	archive->buffers[pos] = (char *)safe_emalloc(ZSTR_LEN(buffer), 1, 1);
 -	memcpy(archive->buffers[pos], ZSTR_VAL(buffer), ZSTR_LEN(buffer) + 1);
 -
 -	zs = zip_source_buffer(intern, archive->buffers[pos], ZSTR_LEN(buffer), 0);
 +	zs = php_zip_create_string_source(buffer, NULL, NULL);

  	if (zs == NULL) {
  		RETURN_FALSE;
@@@ -2306,7 -2271,15 +2334,11 @@@ 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 {
 -		RETURN_TRUE;
 -	}
 +	RETURN_BOOL(zip_set_archive_flag(intern, flag, (int)value) == 0);
  }

  PHP_METHOD(ZipArchive, getArchiveFlag)
@@@ -2438,9 -2426,16 +2482,13 @@@ PHP_METHOD(ZipArchive, setExternalAttri

  	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) {
 -		RETURN_FALSE;
 -	}
 -	RETURN_TRUE;
 +	RETURN_BOOL(zip_file_set_external_attributes(intern, (zip_uint64_t)index,
 +			(zip_flags_t)flags, (zip_uint8_t)(opsys&0xff), (zip_uint32_t)attr) == 0);
  }
  /* }}} */

@@@ -2558,7 -2568,19 +2610,11 @@@ PHP_METHOD(ZipArchive, setEncryptionInd

  	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;
 -	}
 -
 -	if (zip_file_set_encryption(intern, index, (zip_uint16_t)method, password)) {
 -		RETURN_FALSE;
 -	}
 -	RETURN_TRUE;
 +	RETURN_BOOL(php_zip_file_set_encryption(intern, index, method, password));
  }
  /* }}} */
  #endif
@@@ -2646,13 -2664,11 +2702,18 @@@ PHP_METHOD(ZipArchive, setCompressionNa
  		RETURN_THROWS();
  	}

 -	if (name_len == 0) {
 -		zend_argument_must_not_be_empty_error(1);
 +	if (comp_flags < 0 || comp_flags > USHRT_MAX) {
 +		// comp_flags is cast down accordingly in libzip, zip_entry_t compression_level is of zip_uint16_t
 +		zend_argument_value_error(3, "must be between 0 and %u", USHRT_MAX);
 +		RETURN_THROWS();
 +	}
 +
 +	ZIP_FROM_OBJECT(intern, this);
++
++	if (php_zipobj_closing(Z_ZIP_P(this))) {
+ 		RETURN_THROWS();
+ 	}
+
  	idx = zip_name_locate(intern, name, 0);

  	if (idx < 0) {
@@@ -2677,25 -2696,17 +2738,29 @@@ PHP_METHOD(ZipArchive, setCompressionIn
  		RETURN_THROWS();
  	}

 +	if (index < 0) {
 +		RETURN_FALSE;
 +	}
 +
 +	if (comp_method < -1 || comp_method > INT_MAX) {
 +		zend_argument_value_error(2, "must be between -1 and %d", INT_MAX);
 +		RETURN_THROWS();
 +	}
 +
 +	if (comp_flags < 0 || comp_flags > USHRT_MAX) {
 +		// comp_flags is cast down accordingly in libzip, zip_entry_t compression_level is of zip_uint16_t
 +		zend_argument_value_error(3, "must be between 0 and %u", USHRT_MAX);
 +		RETURN_THROWS();
 +	}
 +
  	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;
 -	}
 -	RETURN_TRUE;
 +	RETURN_BOOL(zip_set_file_compression(intern, (zip_uint64_t)index,
 +			(zip_int32_t)comp_method, (zip_uint32_t)comp_flags) == 0);
  }
  /* }}} */

@@@ -2719,8 -2732,11 +2784,12 @@@ PHP_METHOD(ZipArchive, setMtimeName
  		RETURN_THROWS();
  	}

 -	if (name_len == 0) {
 -		zend_argument_must_not_be_empty_error(1);
 +	ZIP_FROM_OBJECT(intern, this);
 +
++	if (php_zipobj_closing(Z_ZIP_P(this))) {
+ 		RETURN_THROWS();
+ 	}
+
  	idx = zip_name_locate(intern, name, 0);

  	if (idx < 0) {
@@@ -2747,10 -2766,18 +2816,14 @@@ 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;
 -	}
 -	RETURN_TRUE;
 +	RETURN_BOOL(zip_file_set_mtime(intern, (zip_uint64_t)index,
 +			(time_t)mtime, (zip_uint32_t)flags) == 0);
  }
  /* }}} */
 -#endif

  /* {{{ Delete a file using its index */
  PHP_METHOD(ZipArchive, deleteIndex)
@@@ -2786,15 -2821,21 +2863,19 @@@ PHP_METHOD(ZipArchive, deleteName
  		RETURN_THROWS();
  	}

 +	if (name_len < 1) {
 +		RETURN_FALSE;
 +	}
 +
  	ZIP_FROM_OBJECT(intern, self);

+ 	if (php_zipobj_closing(Z_ZIP_P(self))) {
+ 		RETURN_THROWS();
+ 	}
+
 -	if (name_len < 1) {
 -		RETURN_FALSE;
 -	}
 -
  	PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb);
 -	if (zip_delete(intern, sb.index)) {
 -		RETURN_FALSE;
 -	}
 -	RETURN_TRUE;
 +
 +	RETURN_BOOL(zip_delete(intern, sb.index) == 0);
  }
  /* }}} */

@@@ -2820,9 -2867,11 +2901,13 @@@ PHP_METHOD(ZipArchive, renameIndex
  		RETURN_THROWS();
  	}

 -	if (zip_file_rename(intern, index, (const char *)new_name, 0) != 0) {
 -		RETURN_FALSE;
 +	ZIP_FROM_OBJECT(intern, self);
 +
++	if (php_zipobj_closing(Z_ZIP_P(self))) {
++		RETURN_THROWS();
+ 	}
+
 -	RETURN_TRUE;
 +	RETURN_BOOL(zip_file_rename(intern, index, (const char *)new_name, 0) == 0);
  }
  /* }}} */

@@@ -2863,13 -2920,21 +2952,17 @@@ PHP_METHOD(ZipArchive, unchangeIndex
  		RETURN_THROWS();
  	}

 +	if (index < 0) {
 +		RETURN_FALSE;
 +	}
 +
  	ZIP_FROM_OBJECT(intern, self);

+ 	if (php_zipobj_closing(Z_ZIP_P(self))) {
+ 		RETURN_THROWS();
+ 	}
+
 -	if (index < 0) {
 -		RETURN_FALSE;
 -	}
 -
 -	if (zip_unchange(intern, index) != 0) {
 -		RETURN_FALSE;
 -	} else {
 -		RETURN_TRUE;
 -	}
 +	RETURN_BOOL(zip_unchange(intern, index) == 0);
  }
  /* }}} */

@@@ -2886,15 -2951,23 +2979,19 @@@ PHP_METHOD(ZipArchive, unchangeName
  		RETURN_THROWS();
  	}

 +	if (name_len < 1) {
 +		RETURN_FALSE;
 +	}
 +
  	ZIP_FROM_OBJECT(intern, self);

+ 	if (php_zipobj_closing(Z_ZIP_P(self))) {
+ 		RETURN_THROWS();
+ 	}
+
 -	if (name_len < 1) {
 -		RETURN_FALSE;
 -	}
 -
  	PHP_ZIP_STAT_PATH(intern, name, name_len, 0, sb);

 -	if (zip_unchange(intern, sb.index) != 0) {
 -		RETURN_FALSE;
 -	} else {
 -		RETURN_TRUE;
 -	}
 +	RETURN_BOOL(zip_unchange(intern, sb.index) == 0);
  }
  /* }}} */

@@@ -2908,7 -2983,15 +3005,11 @@@ 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 {
 -		RETURN_TRUE;
 -	}
 +	RETURN_BOOL(zip_unchange_all(intern) == 0);
  }
  /* }}} */

@@@ -2922,7 -3007,15 +3023,11 @@@ 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 {
 -		RETURN_TRUE;
 -	}
 +	RETURN_BOOL(zip_unchange_archive(intern) == 0);
  }
  /* }}} */