[php-src] PHP-8.4: Fix GH-23418: UAF when accessing mounted Phar subdirectories (#23442)

Weilin Du via GitHub <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Weilin Du (LamentXU123)
Committer: GitHub (web-flow)
Pusher: LamentXU123
Date: 2026-08-27T00:09:11+08:00

Commit: https://github.com/php/php-src/commit/12b1f654db632e1420142dd87f190a93a44fb88e
Raw diff: https://github.com/php/php-src/commit/12b1f654db632e1420142dd87f190a93a44fb88e.diff

Fix GH-23418: UAF when accessing mounted Phar subdirectories (#23442)

Here we passes a properly null-terminated copy of the shortened path
to `phar_mount_entry()` instead and keep `test` alive until error
formatting and manifest lookup have completed.

Changed paths:
  A  ext/phar/tests/gh23418.phpt
  M  NEWS
  M  ext/phar/util.c


Diff:

diff --git a/NEWS b/NEWS
index e4c3889e292d..935b31234898 100644
--- a/NEWS
+++ b/NEWS
@@ -52,6 +52,10 @@ PHP                                                                        NEWS
   . Fixed a leak when a persistent connection failed a liveness check
     with no other live PDO handle. (iliaal)
 
+- Phar:
+  . Fixed bug GH-23418 (Use-after-free when looking up mounted directories).
+    (Weilin Du)
+
 - Standard:
   . Fixed a memory leak in array_merge_recursive() when the recursive merge of
     an object converted to an array fails. (David Carlier)
diff --git a/ext/phar/tests/gh23418.phpt b/ext/phar/tests/gh23418.phpt
new file mode 100644
index 000000000000..d7ebebcb9ecb
--- /dev/null
+++ b/ext/phar/tests/gh23418.phpt
@@ -0,0 +1,32 @@
+--TEST--
+GH-23418: Access a subdirectory of a mounted directory with a trailing slash
+--EXTENSIONS--
+phar
+--INI--
+phar.readonly=0
+--FILE--
+<?php
+$phar = __DIR__ . '/gh23418.phar';
+$mount = __DIR__ . '/gh23418';
+
+@mkdir($mount . '/s2', 0777, true);
+
+$p = new Phar($phar);
+$p->addFromString('x.txt', 'x');
+$p->setStub('<?php __HALT_COMPILER(); ?>');
+unset($p);
+
+$p = new Phar($phar);
+Phar::mount('phar://' . $phar . '/m', $mount);
+$info = $p['m/s2/'];
+
+echo get_class($info), ', isDir=', $info->isDir() ? 'true' : 'false', PHP_EOL;
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/gh23418.phar');
+@rmdir(__DIR__ . '/gh23418/s2');
+@rmdir(__DIR__ . '/gh23418');
+?>
+--EXPECT--
+PharFileInfo, isDir=true
diff --git a/ext/phar/util.c b/ext/phar/util.c
index d3bdf3d52a78..f4de9922f899 100644
--- a/ext/phar/util.c
+++ b/ext/phar/util.c
@@ -1382,7 +1382,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, si
 			if (ZSTR_LEN(str_key) >= path_len || strncmp(ZSTR_VAL(str_key), path, ZSTR_LEN(str_key))) {
 				continue;
 			} else {
-				char *test;
+				char *test, *mount_path;
 				size_t test_len;
 				php_stream_statbuf ssb;
 
@@ -1425,22 +1425,25 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, si
 				}
 
 				/* mount the file just in time */
-				if (SUCCESS != phar_mount_entry(phar, test, test_len, path, path_len)) {
-					efree(test);
+				mount_path = estrndup(path, path_len);
+				if (SUCCESS != phar_mount_entry(phar, test, test_len, mount_path, path_len)) {
 					if (error) {
 						spprintf(error, 4096, "phar error: path \"%s\" exists as file \"%s\" and could not be mounted", path, test);
 					}
+					efree(mount_path);
+					efree(test);
 					return NULL;
 				}
-
-				efree(test);
+				efree(mount_path);
 
 				if (NULL == (entry = zend_hash_str_find_ptr(&phar->manifest, path, path_len))) {
 					if (error) {
 						spprintf(error, 4096, "phar error: path \"%s\" exists as file \"%s\" and could not be retrieved after being mounted", path, test);
 					}
+					efree(test);
 					return NULL;
 				}
+				efree(test);
 				return entry;
 			}
 		} ZEND_HASH_FOREACH_END();
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.