Commit 30645401341 for php.net
commit 3064540134148ca469c4757ec658d690d8cd90b3
Author: “LamentXU123” <108666168+LamentXU123@users.noreply.github.com>
Date: Mon May 25 16:59:28 2026 +0800
ext/phar: improve .phar madic directory preservation logic in phar::addEmptyDir()
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
Closes GH-22146.
diff --git a/NEWS b/NEWS
index dbf83143dce..49b61d23696 100644
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,11 @@ PHP NEWS
IntlCalendar::equals(), ::before(), ::after(), and ::isEquivalentTo().
(Weilin Du)
+- Phar:
+ . Fixed a bypass of the magic ".phar" directory protection in
+ Phar::addEmptyDir() for paths starting with "/.phar", while allowing
+ non-magic directory names that merely share the ".phar" prefix. (Weilin Du)
+
- Zlib:
. Fixed memory leak if deflate initialization fails and there is a dict.
(ndossche)
diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c
index bb96c783e93..a2d70a1a000 100644
--- a/ext/phar/phar_object.c
+++ b/ext/phar/phar_object.c
@@ -3863,9 +3863,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)