php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Your vote has been updated.
Bug #71966 PHP Phar issue with leading ./ in tar archives
Submitted: 2016-04-05 13:09 UTC Modified: 2021-01-29 15:51 UTC
Votes:6
Avg. Score:3.8 ± 0.9
Reproduced:5 of 5 (100.0%)
Same Version:1 (20.0%)
Same OS:4 (80.0%)
From: pandrade at redhat dot com Assigned:
Status: Verified Package: PHAR related
PHP Version: Irrelevant OS: Linux
Private report: No CVE-ID: None
 [2016-04-05 13:09 UTC] pandrade at redhat dot com
Description:
------------
To test the below script, run first:

$ touch file1 file2 file3
$ tar zcf dotslash.tar.gz ./file1 ./file2 ./file3
$ tar zcf nodotslash.tar.gz file1 file2 file3

I made an initial experiment, that "almost" works,
will work for "./file" but fail for "./dir/file".

---8<---
diff -up php-5.4.16/ext/phar/tar.c.orig php-5.4.16/ext/phar/tar.c
--- php-5.4.16/ext/phar/tar.c.orig	2016-03-29 11:39:57.020599910 -0300
+++ php-5.4.16/ext/phar/tar.c	2016-03-29 11:42:25.582624697 -0300
@@ -481,6 +481,9 @@ bail:
 			entry.link = estrdup(hdr->linkname);
 		}
 		phar_set_inode(&entry TSRMLS_CC);
+		if (entry.filename_len > 2 && entry.filename[0] == '.' && entry.filename[1] == '/')
+			/* Also copy trailing nul */
+			memmove(entry.filename, entry.filename + 2, (entry.filename_len -= 2) + 1);
 		zend_hash_add(&myphar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), (void **) &newentry);
 
 		if (entry.is_persistent) {
---8<---

If erroring out on "." or ".." on tar pathnames is the
expected result, please let me know.


Test script:
---------------
#!/usr/bin/php
<?php
echo "\nRunning for the broken file\n";
$path = realpath('./dotslash.tar.gz');
$pharpath = "phar://" . $path;
$phardata = new PharData($path);
$phariter = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($pharpath));

echo "The phar has " . $phardata->count() . " entries\n";
$i = 0;
foreach($phariter as $file){
    echo $file . "\n";
    $i++;
}
echo "There were $i entries listed.\n";


echo "\nNow running for the working file\n";
$path = realpath('./nodotslash.tar.gz');
$pharpath = "phar://" . $path;
$phardata = new PharData($path);
$phariter = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($pharpath));

echo "The phar has " . $phardata->count() . " entries\n";
$i = 0;
foreach($phariter as $file){
    echo $file . "\n";
    $i++;
}
echo "There were $i entries listed.\n";

?>

Expected result:
----------------
$ ./pharbug.php 

Running for the broken file
The phar has 3 entries
phar:///home/testuser/dotslash.tar.gz/file1
phar:///home/testuser/dotslash.tar.gz/file2
phar:///home/testuser/dotslash.tar.gz/file3
There were 3 entries listed.

Now running for the working file
The phar has 3 entries
phar:///home/testuser/nodotslash.tar.gz/file1
phar:///home/testuser/nodotslash.tar.gz/file2
phar:///home/testuser/nodotslash.tar.gz/file3
There were 3 entries listed.


Actual result:
--------------
$ ./pharbug.php 

Running for the broken file
The phar has 3 entries
phar:///home/testuser/dotslash.tar.gz/.
There were 1 entries listed.

Now running for the working file
The phar has 3 entries
phar:///home/testuser/nodotslash.tar.gz/file1
phar:///home/testuser/nodotslash.tar.gz/file2
phar:///home/testuser/nodotslash.tar.gz/file3
There were 3 entries listed.


Patches

php-dotslash.patch (last revision 2016-04-05 13:10 UTC by pandrade at redhat dot com)

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-04-05 13:13 UTC] pandrade at redhat dot com
Note that the attached patch is not fully functional,
It works for the described problem: "./file", but
fails for "./dir/file".
I added it to manage to get the bug reported.
 [2017-02-22 02:59 UTC] mike at mbaynton dot com
In addition to iteration of the archive contents not occurring, accessing files with through the offsetGet() array-index interface is also broken, and certain flags to the PharData constructor result in attempts to iterate the contents failing with a RuntimeException.

Here's a fully self-sustaining bash script (nothing to manually run first) that shows all the issues I've found when the archive's files lead with ./:
http://pastebin.com/Neu0cWTx
 [2019-01-16 13:08 UTC] someone dot who dot want dot to dot be dot unknown at gmail dot com
I'm experiencing this problem too (linux, php 7.2.10, but this is not relevant really).

How to reproduce:
1. Create archive with paths starting with "./": 
> $ tar -cvf myfile.tar .
2. Check that files paths start with "./":
> $ tar -tvf myfile.tar
3. Try to iterate over archive:

> $this->tar = new PharData('myfile.tar', FilesystemIterator::UNIX_PATHS);
> foreach (new RecursiveIteratorIterator($this->tar) as $i => $file) {
>     // ...
> }

Got an exception:
PHP Fatal error:  Uncaught RuntimeException: Cannot access phar file entry '/' in archive '...' in ...
Stack trace:
#0 [internal function]: PharFileInfo->__construct('phar:///home/wa...')
#1 ...(...): FilesystemIterator->current()

---
This problem is really annoying, making impossible work with these types of achives.
 [2021-01-29 15:51 UTC] cmb@php.net
-Status: Open +Status: Verified -Type: Feature/Change Request +Type: Bug
 [2021-01-29 15:51 UTC] cmb@php.net
Indeed, the handling of the current (.) and parent (..) directory
is very inconsistent in Phar.  The basic problem is that the
dotslash.tar entries are stored in the manifest as is (i.e. as
./file1 etc.), but the lookup normalizes the paths (i.e. to file1
etc.), so the entries cannot be found.

A solution as suggested would solve the problem, but would yield
exactly the same structure for dotslash.tar as for nodotslash.tar,
although popular tools such as GNU tar and 7-zip would show a
different representation.  The same applies for ext/zip which
basically makes the same distinction.  And further fixes would be
required to the Phar routines which allow to add entries to the
tarball.

The alternative solution, namely to not normalize the path on
lookup would yield yet other strange results wrt. phar wrapper
URLs.

Only slightly related, but still interesting in this context, is
that the dotslash.tar could have been created by Phar, but trying
to add a entry like dir/./file would be rejected with the error
message 'invalid path "dir/./local" contains current directory
reference'.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Nov 22 14:01:30 2024 UTC