Fixed bug #72625 realpath() fails on non canonical long path

This commit is contained in:
Anatol Belski 2016-07-23 18:07:03 +02:00
parent fc30b8e0f7
commit 0f16c56262
4 changed files with 148 additions and 7 deletions

View file

@ -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===

View file

@ -19,6 +19,7 @@
#include <config.w32.h> #include <config.w32.h>
#include <win32/time.h> #include <win32/time.h>
#include <win32/ioutil.h>
#include <php.h> #include <php.h>
#ifdef HAVE_LIBXML #ifdef HAVE_LIBXML
@ -44,6 +45,12 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID dummy)
fprintf(stderr, "gettimeofday() initialization failed"); fprintf(stderr, "gettimeofday() initialization failed");
return ret; return ret;
} }
ret = ret && php_win32_ioutil_init();
if (!ret) {
fprintf(stderr, "ioutil initialization failed");
return ret;
}
break; break;
#if 0 /* prepared */ #if 0 /* prepared */
case DLL_PROCESS_DETACH: case DLL_PROCESS_DETACH:

View file

@ -58,12 +58,18 @@
#include "win32/ioutil.h" #include "win32/ioutil.h"
#include "win32/codepage.h" #include "win32/codepage.h"
#include <pathcch.h>
/* /*
#undef NONLS #undef NONLS
#undef _WINNLS_ #undef _WINNLS_
#include <winnls.h> #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) PW32IO BOOL php_win32_ioutil_posix_to_open_opts(int flags, mode_t mode, php_ioutil_open_opts *opts)
{/*{{{*/ {/*{{{*/
int current_umask; int current_umask;
@ -500,6 +506,62 @@ PW32IO size_t php_win32_ioutil_dirname(char *path, size_t len)
return ret_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. */ /* an extended version could be implemented, for now direct functions can be used. */
#if 0 #if 0
PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode) PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode)

View file

@ -88,11 +88,15 @@ typedef enum {
} php_win32_ioutil_encoding; } php_win32_ioutil_encoding;
#define PHP_WIN32_IOUTIL_DEFAULT_SLASHW L'\\' #define PHP_WIN32_IOUTIL_FW_SLASHW L'/'
#define PHP_WIN32_IOUTIL_DEFAULT_SLASH '\\' #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_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_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_PREFIXW L"\\??\\"
#define PHP_WIN32_IOUTIL_JUNCTION_PREFIX_LENW 4 #define PHP_WIN32_IOUTIL_JUNCTION_PREFIX_LENW 4
@ -129,6 +133,13 @@ typedef enum {
} \ } \
} while (0); } 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 /* Keep these functions aliased for case some additional handling
is needed later. */ 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) __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); free(mb);
return NULL; 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); free(mb);
} else { } else {