]> granicus.if.org Git - php/commitdiff
Fixed bug #72625 realpath() fails on non canonical long path
authorAnatol Belski <ab@php.net>
Sat, 23 Jul 2016 16:07:03 +0000 (18:07 +0200)
committerAnatol Belski <ab@php.net>
Sat, 23 Jul 2016 19:11:27 +0000 (21:11 +0200)
ext/standard/tests/dir/bug72625.phpt [new file with mode: 0644]
win32/dllmain.c
win32/ioutil.c
win32/ioutil.h

diff --git a/ext/standard/tests/dir/bug72625.phpt b/ext/standard/tests/dir/bug72625.phpt
new file mode 100644 (file)
index 0000000..c89f955
--- /dev/null
@@ -0,0 +1,53 @@
+--TEST--
+Bug #72625 realpath() fails on very long argument.
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+  die("skip Valid only on Windows");
+}
+?>
+--FILE--
+<?php
+
+$base = sys_get_temp_dir() . "/" . md5(uniqid());
+while (strlen($base) < 260) {
+       $base = "$base/" . md5(uniqid());
+}
+
+$f0 = "$base/_test/documents/projects/myproject/vendor/name/library/classpath";
+$f1 = "$f0/../../../../../../../../_test/documents/projects/myproject/vendor/name/library/../../../../../../../_test/documents/projects/myproject/vendor/name/library/classpath";
+
+
+mkdir($f0, 0777, true);
+
+
+var_dump(
+       $f0,
+       file_exists($f0),
+       realpath($f0),
+       dirname($f0),
+
+       $f1,
+       file_exists($f1),
+       realpath($f1),
+       dirname($f1)
+);
+
+$tmp = $f0;
+while ($tmp > $base) {
+       rmdir($tmp);
+       $tmp = dirname($tmp);
+}
+
+?>
+===DONE===
+--EXPECTF--
+string(%d) "%s/_test/documents/projects/myproject/vendor/name/library/classpath"
+bool(true)
+string(%d) "%s\_test\documents\projects\myproject\vendor\name\library\classpath"
+string(%d) "%s\_test\documents\projects\myproject\vendor\name\library"
+string(%d) "%s/_test/documents/projects/myproject/vendor/name/library/classpath/../../../../../../../../_test/documents/projects/myproject/vendor/name/library/../../../../../../../_test/documents/projects/myproject/vendor/name/library/classpath"
+bool(true)
+string(%d) "%s\_test\documents\projects\myproject\vendor\name\library\classpath"
+string(%d) "%s\_test\documents\projects\myproject\vendor\name\library"
+===DONE===
index a398b2201d99fedd25e7d983450bc04a1614f275..37408f1e768da93e3322f60df7a160db346060bd 100644 (file)
@@ -19,6 +19,7 @@
 #include <config.w32.h>
 
 #include <win32/time.h>
+#include <win32/ioutil.h>
 #include <php.h>
 
 #ifdef HAVE_LIBXML
@@ -44,6 +45,12 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID dummy)
                                fprintf(stderr, "gettimeofday() initialization failed");
                                return ret;
                        }
+
+                       ret = ret && php_win32_ioutil_init();
+                       if (!ret) {
+                               fprintf(stderr, "ioutil initialization failed");
+                               return ret;
+                       }
                        break;
 #if 0 /* prepared */
                case DLL_PROCESS_DETACH:
index 9d91054d6fa25e77f2f5989d48bbe226c1d52ff3..5deb431b2fd4ecf63f0322f9078269fdc13dd613 100644 (file)
 #include "win32/ioutil.h"
 #include "win32/codepage.h"
 
+#include <pathcch.h>
+
 /*
 #undef NONLS
 #undef _WINNLS_
 #include <winnls.h>
 */
 
+typedef HRESULT (WINAPI *MyPathCchCanonicalizeEx)(_Out_ PWSTR pszPathOut, _In_  size_t cchPathOut, _In_  PCWSTR pszPathIn, _In_  unsigned long dwFlags);
+
+static MyPathCchCanonicalizeEx canonicalize_path_w = NULL;
+
 PW32IO BOOL php_win32_ioutil_posix_to_open_opts(int flags, mode_t mode, php_ioutil_open_opts *opts)
 {/*{{{*/
        int current_umask;
@@ -500,6 +506,62 @@ PW32IO size_t php_win32_ioutil_dirname(char *path, size_t len)
        return ret_len;
 }/*}}}*/
 
+PW32IO BOOL php_win32_ioutil_normalize_path_w(wchar_t **buf, size_t len, size_t *new_len)
+{/*{{{*/
+       wchar_t *pos, *idx = *buf, canonicalw[MAXPATHLEN];
+       size_t ret_len = len, canonicalw_len, shift;
+
+       if (len >= MAXPATHLEN) {
+               SET_ERRNO_FROM_WIN32_CODE(ERROR_BUFFER_OVERFLOW);
+               return FALSE;
+       }
+
+       while (NULL != (pos = wcschr(idx, PHP_WIN32_IOUTIL_FW_SLASHW)) && idx - *buf <= len) {
+               *pos = PHP_WIN32_IOUTIL_DEFAULT_SLASHW;
+               idx = pos++;
+       }
+
+       shift = PHP_WIN32_IOUTIL_IS_LONG_PATHW(*buf, len) ? PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW : 0;
+       if (S_OK != canonicalize_path_w(canonicalw, MAXPATHLEN, *buf + shift, PATHCCH_ALLOW_LONG_PATHS)) {
+               *new_len = ret_len;
+               return FALSE;
+       }
+       canonicalw_len = wcslen(canonicalw);
+       if (canonicalw_len + shift != len) {
+               if (canonicalw_len > len) {
+                       *buf = realloc(*buf, (canonicalw_len + 1) * sizeof(wchar_t));
+               }
+               memmove(*buf + shift, canonicalw, (canonicalw_len + 1) * sizeof(wchar_t));
+               ret_len = canonicalw_len + shift;
+       }
+       *new_len = ret_len;
+
+       return TRUE;
+}/*}}}*/
+
+static HRESULT MyPathCchCanonicalizeExFallback(_Out_ PWSTR pszPathOut, _In_  size_t cchPathOut, _In_  PCWSTR pszPathIn, _In_  unsigned long dwFlags)
+{
+       pszPathOut = pszPathIn;
+       cchPathOut = wcslen(pszPathOut);
+
+       return S_OK;
+}
+
+BOOL php_win32_ioutil_init(void)
+{
+       HMODULE hMod = GetModuleHandle("api-ms-win-core-path-l1-1-0");
+
+       if (hMod) {
+               canonicalize_path_w = (MyPathCchCanonicalizeEx)GetProcAddress(hMod, "PathCchCanonicalizeEx");
+               if (!canonicalize_path_w) {
+                       canonicalize_path_w = MyPathCchCanonicalizeExFallback;
+               }
+       } else {
+               canonicalize_path_w = MyPathCchCanonicalizeExFallback;
+       }
+
+       return TRUE;
+}
 /* an extended version could be implemented, for now direct functions can be used. */
 #if 0
 PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode)
index 990f07b34bf2e61f92a86dd649d2c9020e0d23f0..a11c64912e412e9ac051e291617444a604059763 100644 (file)
@@ -88,11 +88,15 @@ typedef enum {
 } php_win32_ioutil_encoding;
 
 
-#define PHP_WIN32_IOUTIL_DEFAULT_SLASHW L'\\'
-#define PHP_WIN32_IOUTIL_DEFAULT_SLASH '\\'
+#define PHP_WIN32_IOUTIL_FW_SLASHW L'/'
+#define PHP_WIN32_IOUTIL_FW_SLASH '/'
+#define PHP_WIN32_IOUTIL_BW_SLASHW L'\\'
+#define PHP_WIN32_IOUTIL_BW_SLASH '\\'
+#define PHP_WIN32_IOUTIL_DEFAULT_SLASHW PHP_WIN32_IOUTIL_BW_SLASHW
+#define PHP_WIN32_IOUTIL_DEFAULT_SLASH PHP_WIN32_IOUTIL_BW_SLASH
 
 #define PHP_WIN32_IOUTIL_DEFAULT_DIR_SEPARATORW        L';'
-#define PHP_WIN32_IOUTIL_IS_SLASHW(c) ((c) == L'\\' || (c) == L'/')
+#define PHP_WIN32_IOUTIL_IS_SLASHW(c) ((c) == PHP_WIN32_IOUTIL_BW_SLASHW || (c) == PHP_WIN32_IOUTIL_FW_SLASHW)
 #define PHP_WIN32_IOUTIL_IS_LETTERW(c) (((c) >= L'a' && (c) <= L'z') || ((c) >= L'A' && (c) <= L'Z'))
 #define PHP_WIN32_IOUTIL_JUNCTION_PREFIXW L"\\??\\"
 #define PHP_WIN32_IOUTIL_JUNCTION_PREFIX_LENW 4
@@ -129,6 +133,13 @@ typedef enum {
                } \
 } while (0);
 
+PW32IO BOOL php_win32_ioutil_normalize_path_w(wchar_t **buf, size_t len, size_t *new_len);
+#ifdef PHP_EXPORTS
+/* This symbols are needed only for the DllMain, but should not be exported 
+       or be available when used with PHP binaries. */
+BOOL php_win32_ioutil_init(void);
+#endif
+
 /* Keep these functions aliased for case some additional handling
    is needed later. */
 __forceinline static wchar_t *php_win32_ioutil_conv_any_to_w(const char* in, size_t in_len, size_t *out_len)
@@ -148,11 +159,19 @@ __forceinline static wchar_t *php_win32_ioutil_conv_any_to_w(const char* in, siz
                        free(mb);
                        return NULL;
                }
-               memmove(ret, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW * sizeof(wchar_t));
-               memmove(ret+PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, mb, mb_len * sizeof(wchar_t));
-               ret[mb_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW] = L'\0';
 
-               mb_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
+               (void)php_win32_ioutil_normalize_path_w(&mb, mb_len, &mb_len);
+
+               if (PHP_WIN32_IOUTIL_IS_LONG_PATHW(mb, mb_len)) {
+                       memmove(ret, mb, mb_len * sizeof(wchar_t));
+                       ret[mb_len] = L'\0';
+               } else {
+                       memmove(ret, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW * sizeof(wchar_t));
+                       memmove(ret+PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, mb, mb_len * sizeof(wchar_t));
+                       ret[mb_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW] = L'\0';
+
+                       mb_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
+               }
 
                free(mb);
        } else {