summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnatol Belski <ab@php.net>2016-07-23 18:07:03 +0200
committerAnatol Belski <ab@php.net>2016-07-23 21:11:27 +0200
commit0f16c56262a4d1f91bc299bb47d82df1e09700f8 (patch)
treec09a54706b8c39e5639242851a635ca393da687f
parentfc30b8e0f70319cda3e76c54a5bba81d19956176 (diff)
downloadphp-git-0f16c56262a4d1f91bc299bb47d82df1e09700f8.tar.gz
Fixed bug #72625 realpath() fails on non canonical long path
-rw-r--r--ext/standard/tests/dir/bug72625.phpt53
-rw-r--r--win32/dllmain.c7
-rw-r--r--win32/ioutil.c62
-rw-r--r--win32/ioutil.h33
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 {