[php-src] PHP-8.5: ext/zip: php_zip_ops_stat() succeeds when the archive cannot be opened.

David Carlier <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: David Carlier (devnexen)
Date: 2026-08-30T15:26:29+01:00

Commit: https://github.com/php/php-src/commit/614afe7b09fd37c57f71a02fecfac33191ea141a
Raw diff: https://github.com/php/php-src/commit/614afe7b09fd37c57f71a02fecfac33191ea141a.diff

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

Changed paths:
  A  ext/zip/tests/stream_fstat_unreadable_archive.phpt
  M  NEWS
  M  ext/zip/zip_stream.c


Diff:

diff --git a/NEWS b/NEWS
index aa7862f63bb4..db1aba15a8e6 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 000000000000..a81fc8fc3cfc
--- /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 0356863ef7ce..b70b82a415ec 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;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.