php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #72625 realpath() fails on very long argument.
Submitted: 2016-07-20 03:53 UTC Modified: 2016-07-23 23:06 UTC
From: anrdaemon at freemail dot ru Assigned: ab (profile)
Status: Closed Package: Filesystem function related
PHP Version: 7.0.8 OS: Windows
Private report: No CVE-ID: None
 [2016-07-20 03:53 UTC] anrdaemon at freemail dot ru
Description:
------------
When given a long string (> 260 characters), realpath() and SplFileInfo::getRealPath() both fail to convert path to something sensible and return empty result.

However, using dirname() to shorten the input string will result in the function working again.

Additionally, PHP reporting "invalid path", if you try to use extended access syntax ('\\?\<path>').

(Oh, and before you say something silly, the sting in example is an equivalent of a real path generated by Composer.)

Test script:
---------------
<?php

$fn = "{$_SERVER['TEMP']}/_test/documents/projects/myproject/vendor/name/library/classpath";
$fn1 = "$fn/../../../../../../../../_test/documents/projects/myproject/vendor/name/library/../../../../../../../_test/documents/projects/myproject/vendor/name/library/classpath";
mkdir($fn, 0777, true);
error_log(strlen($fn));
error_log(realpath($fn));
error_log(strlen($fn1));
error_log($fn1);
error_log(realpath($fn1));
error_log(strlen(dirname($fn1)));
error_log(dirname($fn1));
error_log(realpath(dirname($fn1)));



Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-07-20 10:05 UTC] cmb@php.net
-Status: Open +Status: Feedback -Assigned To: +Assigned To: cmb
 [2016-07-20 10:05 UTC] cmb@php.net
That is supposed to be fixed as of PHP 7.1.0alpha2. Please try
with a version from <http://windows.php.net/qa/>.
 [2016-07-21 12:07 UTC] anrdaemon at freemail dot ru
-Status: Feedback +Status: Assigned
 [2016-07-21 12:07 UTC] anrdaemon at freemail dot ru
Didn't change. If not made worse.

Refined script:

<?php

$fn = "{$_SERVER['TEMP']}/_test/documents/projects/myproject/vendor/name/library/classpath";
$fn1 = "$fn/../../../../../../../../_test/documents/projects/myproject/vendor/name/library/../../../../../../../_test/documents/projects/myproject/vendor/name/library/classpath";
mkdir($fn, 0777, true);
error_log(sprintf("(%u)%s", strlen($fn), $fn));
error_log("=>" . realpath($fn));
error_log(sprintf("(%u)%s", strlen($fn1), $fn1));
error_log("=>" . realpath($fn1));
error_log(sprintf("(%u)%s", strlen(dirname($fn1)), dirname($fn1)));
error_log("=>" . realpath(dirname($fn1)));

PHP 5.6.23/64
(101)C:\Users\ANRDAE~1\AppData\Local\Temp/_test/documents/projects/myproject/vendor/name/library/classpath
=>C:\Users\anrdaemon\AppData\Local\Temp\_test\documents\projects\myproject\vendor\name\library\classpath
(266)C:\Users\ANRDAE~1\AppData\Local\Temp/_test/documents/projects/myproject/vendor/name/library/classpath/../../../../../../../../_test/documents/projects/myproject/vendor/name/library/../../../../../../../_test/documents/projects/myproject/vendor/name/library/classpath
=>
(256)C:\Users\ANRDAE~1\AppData\Local\Temp/_test/documents/projects/myproject/vendor/name/library/classpath/../../../../../../../../_test/documents/projects/myproject/vendor/name/library/../../../../../../../_test/documents/projects/myproject/vendor/name/library
=>C:\Users\anrdaemon\AppData\Local\Temp\_test\documents\projects\myproject\vendor\name\library

PHP 7.0.8/64
(101)C:\Users\ANRDAE~1\AppData\Local\Temp/_test/documents/projects/myproject/vendor/name/library/classpath
=>C:\Users\anrdaemon\AppData\Local\Temp\_test\documents\projects\myproject\vendor\name\library\classpath
(266)C:\Users\ANRDAE~1\AppData\Local\Temp/_test/documents/projects/myproject/vendor/name/library/classpath/../../../../../../../../_test/documents/projects/myproject/vendor/name/library/../../../../../../../_test/documents/projects/myproject/vendor/name/library/classpath
=>
(256)C:\Users\ANRDAE~1\AppData\Local\Temp/_test/documents/projects/myproject/vendor/name/library/classpath/../../../../../../../../_test/documents/projects/myproject/vendor/name/library/../../../../../../../_test/documents/projects/myproject/vendor/name/library
=>C:\Users\anrdaemon\AppData\Local\Temp\_test\documents\projects\myproject\vendor\name\library

PHP 7.1.0b1/64
(101)C:\Users\ANRDAE~1\AppData\Local\Temp/_test/documents/projects/myproject/vendor/name/library/classpath
=>C:\Users\anrdaemon\AppData\Local\Temp\_test\documents\projects\myproject\vendor\name\library\classpath
(266)C:\Users\ANRDAE~1\AppData\Local\Temp/_test/documents/projects/myproject/vendor/name/library/classpath/../../../../../../../../_test/documents/projects/myproject/vendor/name/library/../../../../../../../_test/documents/projects/myproject/vendor/name/library/classpath
=>
(260)\\?\C:\Users\ANRDAE~1\AppData\Local\Temp/_test/documents/projects/myproject/vendor/name/library/classpath/../../../../../../../../_test/documents/projects/myproject/vendor/name/library/../../../../../../../_test/documents/projects/myproject/vendor/name/library
=>
 [2016-07-21 18:43 UTC] cmb@php.net
-Status: Assigned +Status: Verified
 [2016-07-21 18:43 UTC] cmb@php.net
Indeed, can also reproduce with current master.
 [2016-07-21 23:02 UTC] cmb@php.net
-Status: Verified +Status: Analyzed -Assigned To: cmb +Assigned To: ab
 [2016-07-21 23:02 UTC] cmb@php.net
The issue is FindFirstFileW(), which isn't as liberal with regard
to the \\?\ prefix as without it. POC:

    #include <windows.h>
    
    int main()
    {
        HANDLE h;
        WIN32_FIND_DATA fd;
    
        h = FindFirstFileW(L"C:\\Windows", &fd);
        printf("%i\n", h != INVALID_HANDLE_VALUE);
        h = FindFirstFileW(L"C:/Windows", &fd);
        printf("%i\n", h != INVALID_HANDLE_VALUE);
        h = FindFirstFileW(L"\\\\?\\C:\\Windows", &fd);
        printf("%i\n", h != INVALID_HANDLE_VALUE);
        h = FindFirstFileW(L"\\\\?\\C:/Windows", &fd);
        printf("%i\n", h != INVALID_HANDLE_VALUE);
        return 0;
    }

outputs:

    1
    1
    1
    0

Neither slashes (nor dots nor double-dots) are converted, as noted
on MSDN in the section about "Maximum Path Length Limitation"[1].

It occurs to me that this conversion would have to be done by PHP
in the first place. Anatol, what do you think?

[1] <https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath>
 [2016-07-23 16:17 UTC] ab@php.net
@cmb, yeah, Windows API won't do any magic with \\?\ prefixed paths. I intentionally left this part, because there is yet no API for all windows versions (starting with 8.1 there is one). Also, that saves some memory operations. While replacing slashes wouldn't cost any memory overhead, so probably could be done. Since this issue is related to composer, probably worth it at least. I'll look to maybe come up with some custom PathCchCanonicalizeEx for lower Windows versions.

Thanks.
 [2016-07-23 19:48 UTC] ab@php.net
Automatic comment on behalf of ab
Revision: http://git.php.net/?p=php-src.git;a=commit;h=0f16c56262a4d1f91bc299bb47d82df1e09700f8
Log: Fixed bug #72625 realpath() fails on non canonical long path
 [2016-07-23 19:48 UTC] ab@php.net
-Status: Analyzed +Status: Closed
 [2016-07-23 20:05 UTC] ab@php.net
Christoph, i've fixed this exact case, and also partially the general case using teh new APIs when available. Reimplementing the path canonicalization might take some time. The clean case is however worky under any circumstances.

@anrdaemon just one note to your code - realpath(dirname($fn1)) seems wrong. If the path ends with some periods, it might deliver usexpected results.

Thanks.
 [2016-07-23 22:55 UTC] cmb@php.net
Thanks, Anatol.

> Reimplementing the path canonicalization might take some time.

In my opinion, a fallback for Windows < 8.1 is not absolutely
necessary; this might simply be documented as limitation.
 [2016-07-23 23:06 UTC] anrdaemon at freemail dot ru
Yes, the "dirname(realpath(...))" isn't completely foolproof.
I've used it as a means to show that the string length is an issue, and to not use any questionable and hard to understand code in its place.
dirname() would cut the last element in the path, and if the original path actually exists, the result of dirname()'ing would guarantee to exist as well, as in this specific case.
In regard to Composer, the origin of its issue is https://bugs.php.net/bug.php?id=72642
But it seems the person replying to that bug has his head way above his shoulders.
 [2016-10-17 10:10 UTC] bwoebi@php.net
Automatic comment on behalf of ab
Revision: http://git.php.net/?p=php-src.git;a=commit;h=0f16c56262a4d1f91bc299bb47d82df1e09700f8
Log: Fixed bug #72625 realpath() fails on non canonical long path
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Mon Dec 09 01:01:27 2024 UTC