Commit f99989f5f30 for php.net
commit f99989f5f300572df99ef4c701770e52eb801a15
Author: Weilin Du <108666168+LamentXU123@users.noreply.github.com>
Date: Mon May 18 19:35:54 2026 +0800
ext/phar: improve .phar madic directory preservation logic in phar::addEmptyDir() (#22011)
Now, the .phar directory is a magic dir for phar files, and in phar::addEmptyDir(), users couldn't create a dir naming .phar
The implementation is:
```c
if (zend_string_starts_with_literal(dir_name, ".phar")) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create a directory in magic \".phar\" directory");
RETURN_THROWS();
```
This has two bugs.
Firstly, people can use /.phar to create the .phar dir. The leading / will be ignored. (no need to concern about ../ though, it will be ignored.)
```php
<?php
$phar = new Phar(__DIR__ . '/test.phar', 0, 'test.phar');
$phar->addEmptyDir('/.phar');
var_dump(is_dir('phar://' . __DIR__ . '/test.phar/.phar'));
```
Will return true with the .phar dir created, while if the dir is .phar it will raise an error.
Secondly, it only matches the prefix. That means, /.pharxxx will not be allowed to create, which is not a magic dir.
```php
<?php
$phar = new Phar(__DIR__ . '/test.phar', 0, 'test.phar');
$phar->addEmptyDir('.pharx');
```
This will raise an error.
```
PHP Fatal error: Uncaught BadMethodCallException: Cannot create a directory in magic ".phar" directory in C:\Users\admin\Desktop\bench.php:3
```
This PR fix both by 1. adding a trailing check of the path to make .pharx valid 2. adding a check to /.phar
diff --git a/NEWS b/NEWS
index 65b1f85ba37..f86892d0212 100644
--- a/NEWS
+++ b/NEWS
@@ -129,6 +129,10 @@ PHP NEWS
. Support reference values in Phar::mungServer(). (ndossche)
. Invalid values now throw in Phar::mungServer() instead of being silently
ignored. (ndossche)
+ . Fixed a bypass of the magic ".phar" directory protection in
+ Phar::addEmptyDir() for paths starting with "/.phar". (Weilin Du)
+ . Phar::addEmptyDir() now allows non-magic directory names that merely
+ share the ".phar" prefix. (Weilin Du)
. Support overridden methods in SplFileInfo for getMTime() and getPathname()
when building a phar. (ndossche)
. Mark Phar::buildFromIterator() base directory argument as a path.
diff --git a/UPGRADING b/UPGRADING
index 44d6981acbf..5aa617b99fd 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -55,6 +55,11 @@ PHP 8.6 UPGRADE NOTES
- Phar:
. Phar::mungServer() now raises a ValueError when an invalid
argument value is passed instead of being silently ignored.
+ . Phar::addEmptyDir() now rejects `/.phar` paths in addition to `.phar`
+ paths, and raises the same BadMethodCallException for attempts to create
+ the reserved magic ".phar" directory through that form.
+ . Phar::addEmptyDir() now treats non-magic names that merely share the
+ `.phar` prefix as ordinary directories.
- PGSQL:
. pg_fetch_object() now reports the ValueError for a non-empty
diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c
index 81c4fd14f7b..46db925cfd5 100644
--- a/ext/phar/phar_object.c
+++ b/ext/phar/phar_object.c
@@ -3789,9 +3789,16 @@ PHP_METHOD(Phar, addEmptyDir)
PHAR_ARCHIVE_OBJECT();
- if (zend_string_starts_with_literal(dir_name, ".phar")) {
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create a directory in magic \".phar\" directory");
- RETURN_THROWS();
+ if (
+ zend_string_starts_with_literal(dir_name, ".phar")
+ || zend_string_starts_with_literal(dir_name, "/.phar")
+ ) {
+ size_t prefix_len = (ZSTR_VAL(dir_name)[0] == '/') + sizeof(".phar") - 1;
+ char next_char = ZSTR_VAL(dir_name)[prefix_len];
+ if (next_char == '/' || next_char == '\\' || next_char == '\0') {
+ zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create a directory in magic \".phar\" directory");
+ RETURN_THROWS();
+ }
}
phar_mkdir(&phar_obj->archive, dir_name);
diff --git a/ext/phar/tests/mkdir.phpt b/ext/phar/tests/mkdir.phpt
index 1ffdc7fe252..2c1586b0de5 100644
--- a/ext/phar/tests/mkdir.phpt
+++ b/ext/phar/tests/mkdir.phpt
@@ -24,6 +24,13 @@
} catch (Exception $e) {
echo $e->getMessage(),"\n";
}
+try {
+$a->addEmptyDir('/.phar');
+} catch (Exception $e) {
+echo $e->getMessage(),"\n";
+}
+$a->addEmptyDir('/.pharx');
+var_dump(is_dir($pname . '/.pharx'));
?>
--CLEAN--
<?php
@@ -43,3 +50,5 @@
Warning: rmdir(): phar error: cannot remove directory "a" in phar "%smkdir.phar.php", phar error: path "a" exists and is a not a directory in %smkdir.php on line %d
Cannot create a directory in magic ".phar" directory
+Cannot create a directory in magic ".phar" directory
+bool(true)