patch 9.1.1204: MS-Windows: crash when passing long string to expand() Commit: https://github.com/vim/vim/commit/00a749bd90e6b84e7e5132691d73fe9aa3fdff05 Author: zeertzjq <zeert...@outlook.com> Date: Sat Mar 15 09:53:32 2025 +0100
patch 9.1.1204: MS-Windows: crash when passing long string to expand() Problem: MS-Windows: crash when passing long string to expand() with 'wildignorecase'. Solution: Use the same buflen as unix_expandpath() in dos_expandpath(). Remove an unnecessary STRLEN() while at it (zeertzjq). closes: #16896 Signed-off-by: zeertzjq <zeert...@outlook.com> Signed-off-by: Christian Brabandt <c...@256bit.org> diff --git a/src/filepath.c b/src/filepath.c index 0671d0f2d..1c15b8d36 100644 --- a/src/filepath.c +++ b/src/filepath.c @@ -3465,7 +3465,7 @@ pstrcmp(const void *a, const void *b) dos_expandpath( garray_T *gap, char_u *path, - int wildoff, + size_t wildoff, int flags, // EW_* flags int didstar) // expanded "**" once already { @@ -3477,7 +3477,8 @@ dos_expandpath( regmatch_T regmatch; int starts_with_dot; int matches; - int len; + size_t buflen; + size_t len; int starstar = FALSE; static int stardepth = 0; // depth for "**" expansion HANDLE hFind = INVALID_HANDLE_VALUE; @@ -3495,9 +3496,9 @@ dos_expandpath( return 0; } - // Make room for file name. When doing encoding conversion the actual - // length may be quite a bit longer, thus use the maximum possible length. - buf = alloc(MAXPATHL); + // Make room for file name (a bit too much to stay on the safe side). + buflen = STRLEN(path) + MAXPATHL; + buf = alloc(buflen); if (buf == NULL) return 0; @@ -3526,10 +3527,11 @@ dos_expandpath( e = p; if (has_mbyte) { - len = (*mb_ptr2len)(path_end); - STRNCPY(p, path_end, len); - p += len; - path_end += len; + int charlen = (*mb_ptr2len)(path_end); + + STRNCPY(p, path_end, (size_t)charlen); + p += charlen; + path_end += charlen; } else *p++ = *path_end++; @@ -3579,19 +3581,20 @@ dos_expandpath( // remember the pattern or file name being looked for matchname = vim_strsave(s); + len = (size_t)(s - buf); // If "**" is by itself, this is the first time we encounter it and more // is following then find matches without any directory. if (!didstar && stardepth < 100 && starstar && e - s == 2 && *path_end == '/') { - STRCPY(s, path_end + 1); + vim_snprintf((char *)s, buflen - len, "%s", path_end + 1); ++stardepth; - (void)dos_expandpath(gap, buf, (int)(s - buf), flags, TRUE); + (void)dos_expandpath(gap, buf, len, flags, TRUE); --stardepth; } // Scan all files in the directory with "dir/ *.*" - STRCPY(s, "*.*"); + vim_snprintf(s, buflen - len, "*.*"); wn = enc_to_utf16(buf, NULL); if (wn != NULL) hFind = FindFirstFileW(wn, &wfb); @@ -3612,6 +3615,7 @@ dos_expandpath( else p_alt = utf16_to_enc(wfb.cAlternateFileName, NULL); + len = (size_t)(s - buf); // Ignore entries starting with a dot, unless when asked for. Accept // all entries found with "matchname". if ((p[0] != '.' || starts_with_dot @@ -3623,42 +3627,43 @@ dos_expandpath( || (p_alt != NULL && vim_regexec(®match, p_alt, (colnr_T)0)))) || ((flags & EW_NOTWILD) - && fnamencmp(path + (s - buf), p, e - s) == 0))) + && fnamencmp(path + len, p, e - s) == 0))) { - STRCPY(s, p); - len = (int)STRLEN(buf); - - if (starstar && stardepth < 100 - && (wfb.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + len += vim_snprintf((char *)s, buflen - len, "%s", p); + if (len + 1 < buflen) { - // For "**" in the pattern first go deeper in the tree to - // find matches. - STRCPY(buf + len, "/**"); - STRCPY(buf + len + 3, path_end); - ++stardepth; - (void)dos_expandpath(gap, buf, len + 1, flags, TRUE); - --stardepth; - } + if (starstar && stardepth < 100 + && (wfb.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + // For "**" in the pattern first go deeper in the tree to + // find matches. + vim_snprintf((char *)buf + len, buflen - len, + "/**%s", path_end); + ++stardepth; + (void)dos_expandpath(gap, buf, len + 1, flags, TRUE); + --stardepth; + } - STRCPY(buf + len, path_end); - if (mch_has_exp_wildcard(path_end)) - { - // need to expand another component of the path - // remove backslashes for the remaining components only - (void)dos_expandpath(gap, buf, len + 1, flags, FALSE); - } - else - { - stat_T sb; - - // no more wildcards, check if there is a match - // remove backslashes for the remaining components only - if (*path_end != 0) - backslash_halve(buf + len + 1); - // add existing file - if ((flags & EW_ALLLINKS) ? mch_lstat((char *)buf, &sb) >= 0 - : mch_getperm(buf) >= 0) - addfile(gap, buf, flags); + vim_snprintf((char *)buf + len, buflen - len, "%s", path_end); + if (mch_has_exp_wildcard(path_end)) + { + // need to expand another component of the path + // remove backslashes for the remaining components only + (void)dos_expandpath(gap, buf, len + 1, flags, FALSE); + } + else + { + stat_T sb; + + // no more wildcards, check if there is a match + // remove backslashes for the remaining components only + if (*path_end != 0) + backslash_halve(buf + len + 1); + // add existing file + if ((flags & EW_ALLLINKS) ? mch_lstat((char *)buf, &sb) >= 0 + : mch_getperm(buf) >= 0) + addfile(gap, buf, flags); + } } } @@ -3714,7 +3719,7 @@ pstrcmp(const void *a, const void *b) unix_expandpath( garray_T *gap, char_u *path, - int wildoff, + size_t wildoff, int flags, // EW_* flags int didstar) // expanded "**" once already { @@ -3726,7 +3731,8 @@ unix_expandpath( regmatch_T regmatch; int starts_with_dot; int matches; - int len; + size_t buflen; + size_t len; int starstar = FALSE; static int stardepth = 0; // depth for "**" expansion @@ -3741,8 +3747,8 @@ unix_expandpath( return 0; } - // make room for file name (a bit too much to stay on the safe side) - size_t buflen = STRLEN(path) + MAXPATHL; + // Make room for file name (a bit too much to stay on the safe side). + buflen = STRLEN(path) + MAXPATHL; buf = alloc(buflen); if (buf == NULL) return 0; @@ -3775,10 +3781,11 @@ unix_expandpath( e = p; if (has_mbyte) { - len = (*mb_ptr2len)(path_end); - STRNCPY(p, path_end, len); - p += len; - path_end += len; + int charlen = (*mb_ptr2len)(path_end); + + STRNCPY(p, path_end, (size_t)charlen); + p += charlen; + path_end += charlen; } else *p++ = *path_end++; @@ -3829,14 +3836,15 @@ unix_expandpath( return 0; } + len = (size_t)(s - buf); // If "**" is by itself, this is the first time we encounter it and more // is following then find matches without any directory. if (!didstar && stardepth < 100 && starstar && e - s == 2 && *path_end == '/') { - STRCPY(s, path_end + 1); + vim_snprintf((char *)s, buflen - len, "%s", path_end + 1); ++stardepth; - (void)unix_expandpath(gap, buf, (int)(s - buf), flags, TRUE); + (void)unix_expandpath(gap, buf, len, flags, TRUE); --stardepth; } @@ -3852,6 +3860,7 @@ unix_expandpath( dp = readdir(dirp); if (dp == NULL) break; + len = (size_t)(s - buf); if ((dp->d_name[0] != '.' || starts_with_dot || ((flags & EW_DODOT) && dp->d_name[1] != NUL @@ -3859,10 +3868,11 @@ unix_expandpath( && ((regmatch.regprog != NULL && vim_regexec(®match, (char_u *)dp->d_name, (colnr_T)0)) || ((flags & EW_NOTWILD) - && fnamencmp(path + (s - buf), dp->d_name, e - s) == 0))) + && fnamencmp(path + len, dp->d_name, e - s) == 0))) { - vim_strncpy(s, (char_u *)dp->d_name, buflen - (s - buf) - 1); - len = STRLEN(buf); + len += vim_snprintf((char *)s, buflen - len, "%s", dp->d_name); + if (len + 1 >= buflen) + continue; if (starstar && stardepth < 100) { diff --git a/src/proto/filepath.pro b/src/proto/filepath.pro index 2979b731f..260f4072d 100644 --- a/src/proto/filepath.pro +++ b/src/proto/filepath.pro @@ -55,7 +55,7 @@ int vim_fexists(char_u *fname); int expand_wildcards_eval(char_u **pat, int *num_file, char_u ***file, int flags); int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, int flags); int match_suffix(char_u *fname); -int unix_expandpath(garray_T *gap, char_u *path, int wildoff, int flags, int didstar); +int unix_expandpath(garray_T *gap, char_u *path, size_t wildoff, int flags, int didstar); int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags); void addfile(garray_T *gap, char_u *f, int flags); void FreeWild(int count, char_u **files); diff --git a/src/testdir/test_expand_func.vim b/src/testdir/test_expand_func.vim index 112809ab2..15700480c 100644 --- a/src/testdir/test_expand_func.vim +++ b/src/testdir/test_expand_func.vim @@ -141,4 +141,11 @@ func Test_expand_wildignore() set wildignore& endfunc +" Passing a long string to expand with 'wildignorecase' should not crash Vim. +func Test_expand_long_str() + set wildignorecase + call expand('a'->repeat(99999)) + set wildignorecase& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 338085488..fe8dceba7 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1204, /**/ 1203, /**/ -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1ttNNE-00Bf2k-Ue%40256bit.org.