Skip to content

Commit 324b3ba

Browse files
committed
Merge branch 'PHP-8.5'
* PHP-8.5: ext/phar: Fix .phar-prefixed non-magic directory handling (#22372)
2 parents 90e3132 + 403bf75 commit 324b3ba

5 files changed

Lines changed: 122 additions & 31 deletions

File tree

ext/phar/dirstream.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ static php_stream *phar_make_dirstream(const char *dir, size_t dirlen, const Has
154154
ALLOC_HASHTABLE(data);
155155
zend_hash_init(data, 64, NULL, NULL, 0);
156156

157-
if ((*dir == '/' && dirlen == 1 && (manifest->nNumOfElements == 0)) || (dirlen >= sizeof(".phar")-1 && !memcmp(dir, ".phar", sizeof(".phar")-1))) {
157+
if ((*dir == '/' && dirlen == 1 && (manifest->nNumOfElements == 0)) || phar_path_is_magic_phar_ex(dir, dirlen)) {
158158
/* make empty root directory for empty phar */
159159
/* make empty directory for .phar magic directory */
160160
return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
@@ -171,7 +171,7 @@ static php_stream *phar_make_dirstream(const char *dir, size_t dirlen, const Has
171171

172172
if (*dir == '/') {
173173
/* root directory */
174-
if (zend_string_starts_with_literal(str_key, ".phar")) {
174+
if (phar_is_magic_phar(str_key)) {
175175
/* do not add any magic entries to this directory */
176176
continue;
177177
}

ext/phar/phar_internal.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,31 @@ static inline bool phar_validate_alias(const char *alias, size_t alias_len) /* {
376376
}
377377
/* }}} */
378378

379+
static inline bool phar_path_is_magic_phar_ex(const char *path, size_t path_len) /* {{{ */
380+
{
381+
if (path_len > 0 && path[0] == '/') {
382+
path++;
383+
path_len--;
384+
}
385+
386+
if (path_len < sizeof(".phar") - 1 || memcmp(path, ".phar", sizeof(".phar") - 1) != 0) {
387+
return false;
388+
}
389+
390+
if (path_len == sizeof(".phar") - 1) {
391+
return true;
392+
}
393+
394+
return path[sizeof(".phar") - 1] == '/' || path[sizeof(".phar") - 1] == '\\';
395+
}
396+
/* }}} */
397+
398+
static inline bool phar_is_magic_phar(const zend_string *path) /* {{{ */
399+
{
400+
return phar_path_is_magic_phar_ex(ZSTR_VAL(path), ZSTR_LEN(path));
401+
}
402+
/* }}} */
403+
379404
static inline void phar_set_inode(phar_entry_info *entry) /* {{{ */
380405
{
381406
if (entry->phar->fname) {

ext/phar/phar_object.c

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,7 +1612,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
16121612
return ZEND_HASH_APPLY_STOP;
16131613
}
16141614
after_open_fp:
1615-
if (str_key_len >= sizeof(".phar")-1 && !memcmp(str_key, ".phar", sizeof(".phar")-1)) {
1615+
if (phar_path_is_magic_phar_ex(str_key, str_key_len)) {
16161616
/* silently skip any files that would be added to the magic .phar directory */
16171617
if (save) {
16181618
efree(save);
@@ -3401,14 +3401,14 @@ PHP_METHOD(Phar, copy)
34013401
RETURN_THROWS();
34023402
}
34033403

3404-
if (zend_string_starts_with_literal(old_file, ".phar")) {
3404+
if (phar_is_magic_phar(old_file)) {
34053405
/* can't copy a meta file */
34063406
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
34073407
"file \"%s\" cannot be copied to file \"%s\", cannot copy Phar meta-file in %s", ZSTR_VAL(old_file), ZSTR_VAL(new_file), ZSTR_VAL(phar_obj->archive->fname));
34083408
RETURN_THROWS();
34093409
}
34103410

3411-
if (zend_string_starts_with_literal(new_file, ".phar")) {
3411+
if (phar_is_magic_phar(new_file)) {
34123412
/* can't copy a meta file */
34133413
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
34143414
"file \"%s\" cannot be copied to file \"%s\", cannot copy to Phar meta-file in %s", ZSTR_VAL(old_file), ZSTR_VAL(new_file), ZSTR_VAL(phar_obj->archive->fname));
@@ -3495,7 +3495,7 @@ PHP_METHOD(Phar, offsetExists)
34953495
}
34963496

34973497
/* none of these are real files, so they don't exist */
3498-
RETURN_BOOL(!zend_string_starts_with_literal(file_name, ".phar"));
3498+
RETURN_BOOL(!phar_is_magic_phar(file_name));
34993499
} else {
35003500
/* If the info class is not based on PharFileInfo, directories are not directly instantiable */
35013501
if (UNEXPECTED(!instanceof_function(phar_obj->spl.info_class, phar_ce_entry))) {
@@ -3538,7 +3538,7 @@ PHP_METHOD(Phar, offsetGet)
35383538
RETURN_THROWS();
35393539
}
35403540

3541-
if (zend_string_starts_with_literal(file_name, ".phar")) {
3541+
if (phar_is_magic_phar(file_name)) {
35423542
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot directly get any files or directories in magic \".phar\" directory");
35433543
RETURN_THROWS();
35443544
}
@@ -3568,16 +3568,9 @@ static void phar_add_file(phar_archive_data **pphar, zend_string *file_name, con
35683568
ALLOCA_FLAG(filename_use_heap)
35693569
#endif
35703570

3571-
if (
3572-
zend_string_starts_with_literal(file_name, ".phar")
3573-
|| zend_string_starts_with_literal(file_name, "/.phar")
3574-
) {
3575-
size_t prefix_len = (ZSTR_VAL(file_name)[0] == '/') + sizeof(".phar")-1;
3576-
char next_char = ZSTR_VAL(file_name)[prefix_len];
3577-
if (next_char == '/' || next_char == '\\' || next_char == '\0') {
3578-
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create any files in magic \".phar\" directory");
3579-
return;
3580-
}
3571+
if (phar_is_magic_phar(file_name)) {
3572+
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create any files in magic \".phar\" directory");
3573+
return;
35813574
}
35823575

35833576
/* TODO How to handle Windows path normalisation with zend_string ? */
@@ -3722,7 +3715,7 @@ PHP_METHOD(Phar, offsetSet)
37223715
RETURN_THROWS();
37233716
}
37243717

3725-
if (zend_string_starts_with_literal(file_name, ".phar")) {
3718+
if (phar_is_magic_phar(file_name)) {
37263719
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot set any files or directories in magic \".phar\" directory");
37273720
RETURN_THROWS();
37283721
}
@@ -3789,16 +3782,9 @@ PHP_METHOD(Phar, addEmptyDir)
37893782

37903783
PHAR_ARCHIVE_OBJECT();
37913784

3792-
if (
3793-
zend_string_starts_with_literal(dir_name, ".phar")
3794-
|| zend_string_starts_with_literal(dir_name, "/.phar")
3795-
) {
3796-
size_t prefix_len = (ZSTR_VAL(dir_name)[0] == '/') + sizeof(".phar") - 1;
3797-
char next_char = ZSTR_VAL(dir_name)[prefix_len];
3798-
if (next_char == '/' || next_char == '\\' || next_char == '\0') {
3799-
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create a directory in magic \".phar\" directory");
3800-
RETURN_THROWS();
3801-
}
3785+
if (phar_is_magic_phar(dir_name)) {
3786+
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create a directory in magic \".phar\" directory");
3787+
RETURN_THROWS();
38023788
}
38033789

38043790
phar_mkdir(&phar_obj->archive, dir_name);
@@ -4099,7 +4085,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_result phar_extract_file(bool overwrite, phar
40994085
return SUCCESS;
41004086
}
41014087

4102-
if (zend_string_starts_with_literal(entry->filename, ".phar")) {
4088+
if (phar_is_magic_phar(entry->filename)) {
41034089
return SUCCESS;
41044090
}
41054091
/* strip .. from path and restrict it to be under dest directory */
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
--TEST--
2+
Phar: .phar-prefixed non-magic directories are accessible
3+
--EXTENSIONS--
4+
phar
5+
--INI--
6+
phar.readonly=0
7+
phar.require_hash=0
8+
--FILE--
9+
<?php
10+
$fname = __DIR__ . '/' . basename(__FILE__, '.php') . '.phar.php';
11+
$pname = 'phar://' . $fname;
12+
13+
$phar = new Phar($fname);
14+
$phar['.pharx/array.txt'] = 'array';
15+
$phar->addFromString('.pharx/from-string.txt', 'from-string');
16+
$phar->addFromString('/.phary/leading.txt', 'leading');
17+
$phar->copy('.pharx/array.txt', '.pharx/copy.txt');
18+
19+
var_dump(isset($phar['.pharx/array.txt']));
20+
echo $phar['.pharx/array.txt']->getContent(), "\n";
21+
echo file_get_contents($pname . '/.pharx/from-string.txt'), "\n";
22+
echo file_get_contents($pname . '/.phary/leading.txt'), "\n";
23+
echo file_get_contents($pname . '/.pharx/copy.txt'), "\n";
24+
25+
$root = [];
26+
$dh = opendir($pname . '/');
27+
while (false !== ($entry = readdir($dh))) {
28+
$root[] = $entry;
29+
}
30+
closedir($dh);
31+
sort($root);
32+
var_dump($root);
33+
34+
$subdir = [];
35+
$dh = opendir($pname . '/.pharx');
36+
while (false !== ($entry = readdir($dh))) {
37+
$subdir[] = $entry;
38+
}
39+
closedir($dh);
40+
sort($subdir);
41+
var_dump($subdir);
42+
43+
try {
44+
$phar->addFromString('.phar/still-magic.txt', 'no');
45+
} catch (Throwable $e) {
46+
echo $e->getMessage(), "\n";
47+
}
48+
49+
try {
50+
$phar->addEmptyDir('/.phar');
51+
} catch (Throwable $e) {
52+
echo $e->getMessage(), "\n";
53+
}
54+
?>
55+
--CLEAN--
56+
<?php
57+
@unlink(__DIR__ . '/' . basename(__FILE__, '.clean.php') . '.phar.php');
58+
?>
59+
--EXPECT--
60+
bool(true)
61+
array
62+
from-string
63+
leading
64+
array
65+
array(2) {
66+
[0]=>
67+
string(6) ".pharx"
68+
[1]=>
69+
string(6) ".phary"
70+
}
71+
array(3) {
72+
[0]=>
73+
string(9) "array.txt"
74+
[1]=>
75+
string(8) "copy.txt"
76+
[2]=>
77+
string(15) "from-string.txt"
78+
}
79+
Cannot create any files in magic ".phar" directory
80+
Cannot create a directory in magic ".phar" directory

ext/phar/util.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ zend_result phar_mount_entry(phar_archive_data *phar, const char *filename, size
207207
return FAILURE;
208208
}
209209

210-
if (path_len >= sizeof(".phar")-1 && !memcmp(path, ".phar", sizeof(".phar")-1)) {
210+
if (phar_path_is_magic_phar_ex(path, path_len)) {
211211
/* no creating magic phar files by mounting them */
212212
return FAILURE;
213213
}
@@ -1181,7 +1181,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, si
11811181
*error = NULL;
11821182
}
11831183

1184-
if (security && path_len >= sizeof(".phar")-1 && !memcmp(path, ".phar", sizeof(".phar")-1)) {
1184+
if (security && phar_path_is_magic_phar_ex(path, path_len)) {
11851185
if (error) {
11861186
spprintf(error, 4096, "phar error: cannot directly access magic \".phar\" directory or files within it");
11871187
}

0 commit comments

Comments
 (0)