kernelbase: Don't allow the full Unicode character range in path functions.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2020-03-10 12:45:06 +01:00
parent 0eec0eb7e0
commit 5933c2a690
2 changed files with 62 additions and 74 deletions

View File

@ -507,7 +507,7 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO
if (!wcsncmp( filename, L"\\\\.\\", 4 ))
{
if ((iswalpha(filename[4]) && filename[5] == ':' && filename[6] == '\0') ||
if ((filename[4] && filename[5] == ':' && !filename[6]) ||
!wcsnicmp( filename + 4, L"PIPE\\", 5 ) ||
!wcsnicmp( filename + 4, L"MAILSLOT\\", 9 ))
{

View File

@ -36,7 +36,9 @@
WINE_DEFAULT_DEBUG_CHANNEL(path);
#define iswalnum(ch) (iswctype((ch), C1_ALPHA|C1_DIGIT|C1_LOWER|C1_UPPER))
#define isalnum(ch) (((ch) >= '0' && (ch) <= '9') || \
((ch) >= 'A' && (ch) <= 'Z') || \
((ch) >= 'a' && (ch) <= 'z'))
#define isxdigit(ch) (((ch) >= '0' && (ch) <= '9') || \
((ch) >= 'A' && (ch) <= 'F') || \
((ch) >= 'a' && (ch) <= 'f'))
@ -112,6 +114,17 @@ static SIZE_T strnlenW(const WCHAR *string, SIZE_T maxlen)
return i;
}
static BOOL is_drive_spec( const WCHAR *str )
{
return ((str[0] >= 'A' && str[0] <= 'Z') || (str[0] >= 'a' && str[0] <= 'z')) && str[1] == ':';
}
static BOOL is_escaped_drive_spec( const WCHAR *str )
{
return ((str[0] >= 'A' && str[0] <= 'Z') || (str[0] >= 'a' && str[0] <= 'z')) &&
(str[1] == ':' || str[1] == '|');
}
static BOOL is_prefixed_unc(const WCHAR *string)
{
return !wcsnicmp(string, L"\\\\?\\UNC\\", 8 );
@ -119,7 +132,7 @@ static BOOL is_prefixed_unc(const WCHAR *string)
static BOOL is_prefixed_disk(const WCHAR *string)
{
return !wcsncmp(string, L"\\\\?\\", 4) && iswalpha(string[4]) && string[5] == ':';
return !wcsncmp(string, L"\\\\?\\", 4) && is_drive_spec( string + 4 );
}
static BOOL is_prefixed_volume(const WCHAR *string)
@ -148,7 +161,7 @@ static BOOL is_prefixed_volume(const WCHAR *string)
if (guid[i] != '}') return FALSE;
break;
default:
if (!iswxdigit(guid[i])) return FALSE;
if (!isxdigit(guid[i])) return FALSE;
break;
}
i++;
@ -191,7 +204,7 @@ static const WCHAR *get_root_end(const WCHAR *path)
else if (path[0] == '\\')
return path;
/* X:\ */
else if (iswalpha(path[0]) && path[1] == ':')
else if (is_drive_spec( path ))
return path[2] == '\\' ? path + 2 : path + 1;
else
return NULL;
@ -250,7 +263,7 @@ HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **
if(PathCchStripPrefix(dst, length + 6) == S_OK)
{
/* Fill in \ in X:\ if the \ is missing */
if(iswalpha(dst[0]) && dst[1] == ':' && dst[2]!= '\\')
if (is_drive_spec( dst ) && dst[2]!= '\\')
{
dst[2] = '\\';
dst[3] = 0;
@ -324,7 +337,7 @@ HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **
}
/* If X:\ is not complete, then complete it */
if (iswalpha(buffer[0]) && buffer[1] == ':' && buffer[2] != '\\')
if (is_drive_spec( buffer ) && buffer[2] != '\\')
{
root_end = buffer + 2;
dst = buffer + 3;
@ -365,8 +378,8 @@ HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **
/* Extend the path if needed */
length = lstrlenW(buffer);
if (((length + 1 > MAX_PATH && iswalpha(buffer[0]) && buffer[1] == ':')
|| (iswalpha(buffer[0]) && buffer[1] == ':' && flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH))
if (((length + 1 > MAX_PATH && is_drive_spec( buffer ))
|| (is_drive_spec( buffer ) && flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH))
&& !(flags & PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS))
{
memmove(buffer + 4, buffer, (length + 1) * sizeof(WCHAR));
@ -402,7 +415,7 @@ HRESULT WINAPI PathAllocCombine(const WCHAR *path1, const WCHAR *path2, DWORD fl
if (!path1 || !path2) return PathAllocCanonicalize(path1 ? path1 : path2, flags, out);
/* If path2 is fully qualified, use path2 only */
if ((iswalpha(path2[0]) && path2[1] == ':') || (path2[0] == '\\' && path2[1] == '\\'))
if (is_drive_spec( path2 ) || (path2[0] == '\\' && path2[1] == '\\'))
{
path1 = path2;
path2 = NULL;
@ -559,7 +572,7 @@ HRESULT WINAPI PathCchCanonicalize(WCHAR *out, SIZE_T size, const WCHAR *in)
TRACE("%p %lu %s\n", out, size, wine_dbgstr_w(in));
/* Not X:\ and path > MAX_PATH - 4, return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) */
if (lstrlenW(in) > MAX_PATH - 4 && !(iswalpha(in[0]) && in[1] == ':' && in[2] == '\\'))
if (lstrlenW(in) > MAX_PATH - 4 && !(is_drive_spec( in ) && in[2] == '\\'))
return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
return PathCchCanonicalizeEx(out, size, in, PATHCCH_NONE);
@ -582,7 +595,7 @@ HRESULT WINAPI PathCchCanonicalizeEx(WCHAR *out, SIZE_T size, const WCHAR *in, D
if (size < length + 1)
{
/* No root and path > MAX_PATH - 4, return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) */
if (length > MAX_PATH - 4 && !(in[0] == '\\' || (iswalpha(in[0]) && in[1] == ':' && in[2] == '\\')))
if (length > MAX_PATH - 4 && !(in[0] == '\\' || (is_drive_spec( in ) && in[2] == '\\')))
hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
else
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
@ -593,7 +606,7 @@ HRESULT WINAPI PathCchCanonicalizeEx(WCHAR *out, SIZE_T size, const WCHAR *in, D
memcpy(out, buffer, (length + 1) * sizeof(WCHAR));
/* Fill a backslash at the end of X: */
if (iswalpha(out[0]) && out[1] == ':' && !out[2] && size > 3)
if (is_drive_spec( out ) && !out[2] && size > 3)
{
out[2] = '\\';
out[3] = 0;
@ -1779,8 +1792,8 @@ UINT WINAPI PathGetCharTypeW(WCHAR ch)
{
if (ch < 126)
{
if (((ch & 0x1) && ch != ';') || !ch || iswalnum(ch) || ch == '$' || ch == '&' || ch == '(' ||
ch == '.' || ch == '@' || ch == '^' || ch == '\'' || ch == 130 || ch == '`')
if (((ch & 0x1) && ch != ';') || !ch || isalnum(ch) || ch == '$' || ch == '&' || ch == '(' ||
ch == '.' || ch == '@' || ch == '^' || ch == '\'' || ch == '`')
{
flags |= GCT_SHORTCHAR; /* All these are valid for DOS */
}
@ -1803,7 +1816,7 @@ int WINAPI PathGetDriveNumberA(const char *path)
{
TRACE("%s\n", wine_dbgstr_a(path));
if (path && !IsDBCSLeadByte(*path) && path[1] == ':')
if (path && *path && path[1] == ':')
{
if (*path >= 'a' && *path <= 'z') return *path - 'a';
if (*path >= 'A' && *path <= 'Z') return *path - 'A';
@ -2817,7 +2830,7 @@ HRESULT WINAPI ParseURLW(const WCHAR *url, PARSEDURLW *result)
if (result->cbSize != sizeof(*result))
return E_INVALIDARG;
while (*ptr && (iswalnum(*ptr) || *ptr == '-' || *ptr == '+' || *ptr == '.'))
while (*ptr && (isalnum(*ptr) || *ptr == '-' || *ptr == '+' || *ptr == '.'))
ptr++;
if (*ptr != ':' || ptr <= url + 1)
@ -2928,7 +2941,7 @@ HRESULT WINAPI UrlUnescapeW(WCHAR *url, WCHAR *unescaped, DWORD *unescaped_len,
stop_unescaping = TRUE;
next = *src;
}
else if (*src == '%' && iswxdigit(*(src + 1)) && iswxdigit(*(src + 2)) && !stop_unescaping)
else if (*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2)) && !stop_unescaping)
{
INT ih;
WCHAR buf[5] = {'0','x',0};
@ -3047,7 +3060,7 @@ HRESULT WINAPI PathCreateFromUrlW(const WCHAR *url, WCHAR *path, DWORD *pcchPath
/* fall through */
case 3:
/* 'file:///' (implied localhost) + escaped DOS path */
if (!iswalpha(*src) || (src[1] != ':' && src[1] != '|'))
if (!is_escaped_drive_spec( src ))
src -= 1;
break;
case 2:
@ -3056,7 +3069,7 @@ HRESULT WINAPI PathCreateFromUrlW(const WCHAR *url, WCHAR *path, DWORD *pcchPath
/* 'file://localhost/' + escaped DOS path */
src += 10;
}
else if (iswalpha(*src) && (src[1] == ':' || src[1] == '|'))
else if (is_escaped_drive_spec( src ))
{
/* 'file://' + unescaped DOS path */
unescape = 0;
@ -3073,7 +3086,7 @@ HRESULT WINAPI PathCreateFromUrlW(const WCHAR *url, WCHAR *path, DWORD *pcchPath
len = src - url;
StrCpyNW(dst, url, len + 1);
dst += len;
if (*src && iswalpha(src[1]) && (src[2] == ':' || src[2] == '|'))
if (*src && is_escaped_drive_spec( src + 1 ))
{
/* 'Forget' to add a trailing '/', just like Windows */
src++;
@ -3083,7 +3096,7 @@ HRESULT WINAPI PathCreateFromUrlW(const WCHAR *url, WCHAR *path, DWORD *pcchPath
case 4:
/* 'file://' + unescaped UNC path (\\server\share\path) */
unescape = 0;
if (iswalpha(*src) && (src[1] == ':' || src[1] == '|'))
if (is_escaped_drive_spec( src ))
break;
/* fall through */
default:
@ -3098,7 +3111,7 @@ HRESULT WINAPI PathCreateFromUrlW(const WCHAR *url, WCHAR *path, DWORD *pcchPath
/* First do the Windows-specific path conversions */
for (dst = tpath; *dst; dst++)
if (*dst == '/') *dst = '\\';
if (iswalpha(*tpath) && tpath[1] == '|')
if (is_escaped_drive_spec( tpath ))
tpath[1] = ':'; /* c| -> c: */
/* And only then unescape the path (i.e. escaped slashes are left as is) */
@ -3201,7 +3214,7 @@ static BOOL url_needs_escape(WCHAR ch, DWORD flags, DWORD int_flags)
if (ch <= 31 || (ch >= 127 && ch <= 255) )
return TRUE;
if (iswalnum(ch))
if (isalnum(ch))
return FALSE;
switch (ch) {
@ -3596,9 +3609,9 @@ HRESULT WINAPI UrlCanonicalizeW(const WCHAR *src_url, WCHAR *canonicalized, DWOR
switch (state)
{
case 0:
if (!iswalnum(*wk1)) {state = 3; break;}
if (!isalnum(*wk1)) {state = 3; break;}
*wk2++ = *wk1++;
if (!iswalnum(*wk1)) {state = 3; break;}
if (!isalnum(*wk1)) {state = 3; break;}
*wk2++ = *wk1++;
state = 1;
break;
@ -3627,7 +3640,7 @@ HRESULT WINAPI UrlCanonicalizeW(const WCHAR *src_url, WCHAR *canonicalized, DWOR
while (*body == '/')
++body;
if (iswalnum(*body) && *(body+1) == ':')
if (is_drive_spec( body ))
{
if (!(flags & (URL_WININET_COMPATIBILITY | URL_FILE_USE_PATHURL)))
{
@ -3684,12 +3697,12 @@ HRESULT WINAPI UrlCanonicalizeW(const WCHAR *src_url, WCHAR *canonicalized, DWOR
}
break;
case 4:
if (!iswalnum(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
if (!isalnum(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
{
state = 3;
break;
}
while (iswalnum(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
while (isalnum(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
*wk2++ = *wk1++;
state = 5;
if (!*wk1)
@ -3960,8 +3973,7 @@ static HRESULT url_create_from_path(const WCHAR *path, WCHAR *url, DWORD *url_le
new_url = heap_alloc((lstrlenW(path) + 9) * sizeof(WCHAR)); /* "file:///" + path length + 1 */
lstrcpyW(new_url, L"file:");
if (iswalpha(path[0]) && path[1] == ':')
lstrcatW(new_url, L"///");
if (is_drive_spec( path )) lstrcatW(new_url, L"///");
lstrcatW(new_url, path);
hr = UrlEscapeW(new_url, url, url_len, URL_ESCAPE_PERCENT);
heap_free(new_url);
@ -4166,34 +4178,25 @@ HRESULT WINAPI UrlGetPartA(const char *url, char *out, DWORD *out_len, DWORD par
static const WCHAR * scan_url(const WCHAR *start, DWORD *size, enum url_scan_type type)
{
static DWORD alwayszero = 0;
BOOL cont = TRUE;
*size = 0;
switch (type)
{
case SCHEME:
while (cont)
{
if ((iswlower(*start) && iswalpha(*start)) ||
iswdigit(*start) || *start == '+' || *start == '-' || *start == '.')
while ((*start >= 'a' && *start <= 'z') || (*start >= '0' && *start <= '9') ||
*start == '+' || *start == '-' || *start == '.')
{
start++;
(*size)++;
}
else
cont = FALSE;
}
if (*start != ':')
*size = 0;
break;
case USERPASS:
while (cont)
for (;;)
{
if (iswalpha(*start) ||
iswdigit(*start) ||
if (isalnum(*start) ||
/* user/password only characters */
(*start == ';') ||
(*start == '?') ||
@ -4217,49 +4220,34 @@ static const WCHAR * scan_url(const WCHAR *start, DWORD *size, enum url_scan_typ
start++;
(*size)++;
}
else if (*start == '%')
{
if (iswxdigit(*(start + 1)) && iswxdigit(*(start + 2)))
else if (*start == '%' && isxdigit(start[1]) && isxdigit(start[2]))
{
start += 3;
*size += 3;
}
else
cont = FALSE;
} else
cont = FALSE;
else break;
}
break;
case PORT:
while (cont)
{
if (iswdigit(*start))
while (*start >= '0' && *start <= '9')
{
start++;
(*size)++;
}
else
cont = FALSE;
}
break;
case HOST:
while (cont)
{
if (iswalnum(*start) || *start == '-' || *start == '.' || *start == ' ' || *start == '*')
while (isalnum(*start) || *start == '-' || *start == '.' || *start == ' ' || *start == '*')
{
start++;
(*size)++;
}
else
cont = FALSE;
}
break;
default:
FIXME("unknown type %d\n", type);
return (LPWSTR)&alwayszero;
return L"";
}
return start;
@ -4833,7 +4821,7 @@ HRESULT WINAPI UrlCombineW(const WCHAR *baseW, const WCHAR *relativeW, WCHAR *co
process_case = 1;
break;
}
if (iswalnum(*mrelative) && *(mrelative + 1) == ':')
if (is_drive_spec( mrelative ))
{
/* case that becomes "file:///" */
lstrcpyW(preliminary, L"file:///");