Commit 614afe7b09f for php.net
commit 614afe7b09fd37c57f71a02fecfac33191ea141a
Author: David Carlier <devnexen@gmail.com>
Date: Sat Aug 29 20:58:17 2026 +0100
ext/zip: php_zip_ops_stat() succeeds when the archive cannot be opened.
When zip_open() failed the whole stat block was skipped, yet the function
still returned 0. fstat() on a zip:// stream therefore succeeded with the
zeroed statbuf it was given, reporting a zero size and no file type bits,
instead of failing. Return -1 on that path.
Close GH-23511
diff --git a/NEWS b/NEWS
index aa7862f63bb..db1aba15a8e 100644
--- a/NEWS
+++ b/NEWS
@@ -82,6 +82,8 @@ PHP NEWS
on corrupted entries. (David Carlier)
. Fixed ZipArchive::getNameIndex() truncating the entry index to int.
(David Carlier)
+ . Fixed fstat() on a zip:// stream reporting success when the archive cannot
+ be opened. (David Carlier)
- SAPI:
. Fixed fuzzer targets failing to build in isolation. (Mrmaxmeier)
diff --git a/ext/zip/tests/stream_fstat_unreadable_archive.phpt b/ext/zip/tests/stream_fstat_unreadable_archive.phpt
new file mode 100644
index 00000000000..a81fc8fc3cf
--- /dev/null
+++ b/ext/zip/tests/stream_fstat_unreadable_archive.phpt
@@ -0,0 +1,38 @@
+--TEST--
+fstat() on a zip:// stream whose archive can no longer be opened
+--EXTENSIONS--
+zip
+--SKIPIF--
+<?php
+if (PHP_OS_FAMILY === 'Windows') die('skip the archive cannot be rewritten while it is open');
+?>
+--FILE--
+<?php
+$file = __DIR__ . '/stream_fstat_unreadable_archive.zip';
+
+@unlink($file);
+
+$zip = new ZipArchive;
+if (!$zip->open($file, ZipArchive::CREATE)) {
+ exit('failed');
+}
+
+$zip->addFromString('entry.txt', 'entry');
+$zip->close();
+
+$fp = fopen('zip://' . $file . '#entry.txt', 'rb');
+var_dump($fp !== false);
+
+file_put_contents($file, 'this is not a zip archive');
+
+var_dump(fstat($fp));
+
+fclose($fp);
+?>
+--EXPECT--
+bool(true)
+bool(false)
+--CLEAN--
+<?php
+unlink(__DIR__ . '/stream_fstat_unreadable_archive.zip');
+?>
diff --git a/ext/zip/zip_stream.c b/ext/zip/zip_stream.c
index 0356863ef7c..b70b82a415e 100644
--- a/ext/zip/zip_stream.c
+++ b/ext/zip/zip_stream.c
@@ -195,6 +195,9 @@ static int php_zip_ops_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{
ssb->sb.st_blocks = -1;
#endif
ssb->sb.st_ino = -1;
+ } else {
+ zend_string_release_ex(file_basename, 0);
+ return -1;
}
zend_string_release_ex(file_basename, 0);
return 0;