diff options
author | Anatol Belski <ab@php.net> | 2016-07-23 18:07:03 +0200 |
---|---|---|
committer | Anatol Belski <ab@php.net> | 2016-07-23 21:11:27 +0200 |
commit | 0f16c56262a4d1f91bc299bb47d82df1e09700f8 (patch) | |
tree | c09a54706b8c39e5639242851a635ca393da687f | |
parent | fc30b8e0f70319cda3e76c54a5bba81d19956176 (diff) | |
download | php-git-0f16c56262a4d1f91bc299bb47d82df1e09700f8.tar.gz |
Fixed bug #72625 realpath() fails on non canonical long path
-rw-r--r-- | ext/standard/tests/dir/bug72625.phpt | 53 | ||||
-rw-r--r-- | win32/dllmain.c | 7 | ||||
-rw-r--r-- | win32/ioutil.c | 62 | ||||
-rw-r--r-- | win32/ioutil.h | 33 |
4 files changed, 148 insertions, 7 deletions
diff --git a/ext/standard/tests/dir/bug72625.phpt b/ext/standard/tests/dir/bug72625.phpt new file mode 100644 index 0000000000..c89f955c3d --- /dev/null +++ b/ext/standard/tests/dir/bug72625.phpt @@ -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=== diff --git a/win32/dllmain.c b/win32/dllmain.c index a398b2201d..37408f1e76 100644 --- a/win32/dllmain.c +++ b/win32/dllmain.c @@ -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: diff --git a/win32/ioutil.c b/win32/ioutil.c index 9d91054d6f..5deb431b2f 100644 --- a/win32/ioutil.c +++ b/win32/ioutil.c @@ -58,12 +58,18 @@ #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) diff --git a/win32/ioutil.h b/win32/ioutil.h index 990f07b34b..a11c64912e 100644 --- a/win32/ioutil.h +++ b/win32/ioutil.h @@ -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 { |