vim-patch:9.0.1400: find_file_in_path() is not reentrant (#23146)

Problem:    find_file_in_path() is not reentrant.
Solution:   Instead of global variables pass pointers to the functions.
            (closes vim/vim#12093)

5145c9a829

Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
zeertzjq
2023-04-17 14:38:53 +08:00
committed by GitHub
parent 85bc9e8970
commit 9722b3b9f9
4 changed files with 94 additions and 65 deletions

View File

@@ -2146,6 +2146,9 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
} }
if (*fname != NUL && !error) { if (*fname != NUL && !error) {
char *file_to_find = NULL;
char *search_ctx = NULL;
do { do {
if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) { if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) {
xfree(fresult); xfree(fresult);
@@ -2156,13 +2159,17 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
find_what, curbuf->b_ffname, find_what, curbuf->b_ffname,
(find_what == FINDFILE_DIR (find_what == FINDFILE_DIR
? "" ? ""
: curbuf->b_p_sua)); : curbuf->b_p_sua),
&file_to_find, &search_ctx);
first = false; first = false;
if (fresult != NULL && rettv->v_type == VAR_LIST) { if (fresult != NULL && rettv->v_type == VAR_LIST) {
tv_list_append_string(rettv->vval.v_list, fresult, -1); tv_list_append_string(rettv->vval.v_list, fresult, -1);
} }
} while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL); } while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL);
xfree(file_to_find);
vim_findfile_cleanup(search_ctx);
} }
if (rettv->v_type == VAR_STRING) { if (rettv->v_type == VAR_STRING) {

View File

@@ -4976,8 +4976,13 @@ void ex_splitview(exarg_T *eap)
} }
if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) { if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) {
char *file_to_find = NULL;
char *search_ctx = NULL;
fname = find_file_in_path(eap->arg, strlen(eap->arg), fname = find_file_in_path(eap->arg, strlen(eap->arg),
FNAME_MESS, true, curbuf->b_ffname); FNAME_MESS, true, curbuf->b_ffname,
&file_to_find, &search_ctx);
xfree(file_to_find);
vim_findfile_cleanup(search_ctx);
if (fname == NULL) { if (fname == NULL) {
goto theend; goto theend;
} }
@@ -5169,17 +5174,23 @@ static void ex_resize(exarg_T *eap)
/// ":find [+command] <file>" command. /// ":find [+command] <file>" command.
static void ex_find(exarg_T *eap) static void ex_find(exarg_T *eap)
{ {
char *file_to_find = NULL;
char *search_ctx = NULL;
char *fname = find_file_in_path(eap->arg, strlen(eap->arg), char *fname = find_file_in_path(eap->arg, strlen(eap->arg),
FNAME_MESS, true, curbuf->b_ffname); FNAME_MESS, true, curbuf->b_ffname,
&file_to_find, &search_ctx);
if (eap->addr_count > 0) { if (eap->addr_count > 0) {
// Repeat finding the file "count" times. This matters when it // Repeat finding the file "count" times. This matters when it appears
// appears several times in the path. // several times in the path.
linenr_T count = eap->line2; linenr_T count = eap->line2;
while (fname != NULL && --count > 0) { while (fname != NULL && --count > 0) {
xfree(fname); xfree(fname);
fname = find_file_in_path(NULL, 0, FNAME_MESS, false, curbuf->b_ffname); fname = find_file_in_path(NULL, 0, FNAME_MESS, false, curbuf->b_ffname,
&file_to_find, &search_ctx);
} }
} }
xfree(file_to_find);
vim_findfile_cleanup(search_ctx);
if (fname == NULL) { if (fname == NULL) {
return; return;
@@ -5211,18 +5222,17 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
if (*eap->arg == NUL) { if (*eap->arg == NUL) {
// Special case: ":global/pat/visual\NLvi-commands" // Special case: ":global/pat/visual\NLvi-commands"
if (global_busy) { if (global_busy) {
int rd = RedrawingDisabled;
int nwr = no_wait_return;
int ms = msg_scroll;
if (eap->nextcmd != NULL) { if (eap->nextcmd != NULL) {
stuffReadbuff(eap->nextcmd); stuffReadbuff(eap->nextcmd);
eap->nextcmd = NULL; eap->nextcmd = NULL;
} }
const int save_rd = RedrawingDisabled;
RedrawingDisabled = 0; RedrawingDisabled = 0;
const int save_nwr = no_wait_return;
no_wait_return = 0; no_wait_return = 0;
need_wait_return = false; need_wait_return = false;
const int save_ms = msg_scroll;
msg_scroll = 0; msg_scroll = 0;
redraw_all_later(UPD_NOT_VALID); redraw_all_later(UPD_NOT_VALID);
pending_exmode_active = true; pending_exmode_active = true;
@@ -5230,9 +5240,9 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
normal_enter(false, true); normal_enter(false, true);
pending_exmode_active = false; pending_exmode_active = false;
RedrawingDisabled = rd; RedrawingDisabled = save_rd;
no_wait_return = nwr; no_wait_return = save_nwr;
msg_scroll = ms; msg_scroll = save_ms;
} }
return; return;
} }

View File

@@ -1281,28 +1281,26 @@ static int ff_path_in_stoplist(char *path, int path_len, char **stopdirs_v)
/// @param len length of file name /// @param len length of file name
/// @param first use count'th matching file name /// @param first use count'th matching file name
/// @param rel_fname file name searching relative to /// @param rel_fname file name searching relative to
/// @param[in,out] file_to_find modified copy of file name
/// @param[in,out] search_ctx state of the search
/// ///
/// @return an allocated string for the file name. NULL for error. /// @return an allocated string for the file name. NULL for error.
char *find_file_in_path(char *ptr, size_t len, int options, int first, char *rel_fname) char *find_file_in_path(char *ptr, size_t len, int options, int first, char *rel_fname,
char **file_to_find, char **search_ctx)
{ {
return find_file_in_path_option(ptr, len, options, first, return find_file_in_path_option(ptr, len, options, first,
(*curbuf->b_p_path == NUL (*curbuf->b_p_path == NUL
? p_path ? p_path
: curbuf->b_p_path), : curbuf->b_p_path),
FINDFILE_BOTH, rel_fname, curbuf->b_p_sua); FINDFILE_BOTH, rel_fname, curbuf->b_p_sua,
file_to_find, search_ctx);
} }
static char *ff_file_to_find = NULL;
static void *fdip_search_ctx = NULL;
#if defined(EXITFREE) #if defined(EXITFREE)
void free_findfile(void) void free_findfile(void)
{ {
xfree(ff_file_to_find); XFREE_CLEAR(ff_expand_buffer);
vim_findfile_cleanup(fdip_search_ctx);
xfree(ff_expand_buffer);
} }
#endif #endif
/// Find the directory name "ptr[len]" in the path. /// Find the directory name "ptr[len]" in the path.
@@ -1316,12 +1314,16 @@ void free_findfile(void)
/// @param ptr file name /// @param ptr file name
/// @param len length of file name /// @param len length of file name
/// @param rel_fname file name searching relative to /// @param rel_fname file name searching relative to
/// @param[in,out] file_to_find modified copy of file name
/// @param[in,out] search_ctx state of the search
/// ///
/// @return an allocated string for the file name. NULL for error. /// @return an allocated string for the file name. NULL for error.
char *find_directory_in_path(char *ptr, size_t len, int options, char *rel_fname) char *find_directory_in_path(char *ptr, size_t len, int options, char *rel_fname,
char **file_to_find, char **search_ctx)
{ {
return find_file_in_path_option(ptr, len, options, true, p_cdpath, return find_file_in_path_option(ptr, len, options, true, p_cdpath,
FINDFILE_DIR, rel_fname, ""); FINDFILE_DIR, rel_fname, "",
file_to_find, search_ctx);
} }
/// @param ptr file name /// @param ptr file name
@@ -1331,9 +1333,13 @@ char *find_directory_in_path(char *ptr, size_t len, int options, char *rel_fname
/// @param find_what FINDFILE_FILE, _DIR or _BOTH /// @param find_what FINDFILE_FILE, _DIR or _BOTH
/// @param rel_fname file name we are looking relative to. /// @param rel_fname file name we are looking relative to.
/// @param suffixes list of suffixes, 'suffixesadd' option /// @param suffixes list of suffixes, 'suffixesadd' option
/// @param[in,out] file_to_find modified copy of file name
/// @param[in,out] search_ctx_arg state of the search
char *find_file_in_path_option(char *ptr, size_t len, int options, int first, char *path_option, char *find_file_in_path_option(char *ptr, size_t len, int options, int first, char *path_option,
int find_what, char *rel_fname, char *suffixes) int find_what, char *rel_fname, char *suffixes, char **file_to_find,
char **search_ctx_arg)
{ {
ff_search_ctx_T **search_ctx = (ff_search_ctx_T **)search_ctx_arg;
static char *dir; static char *dir;
static int did_findfile_init = false; static int did_findfile_init = false;
char save_char; char save_char;
@@ -1357,11 +1363,11 @@ char *find_file_in_path_option(char *ptr, size_t len, int options, int first, ch
expand_env_esc(ptr, NameBuff, MAXPATHL, false, true, NULL); expand_env_esc(ptr, NameBuff, MAXPATHL, false, true, NULL);
ptr[len] = save_char; ptr[len] = save_char;
xfree(ff_file_to_find); xfree(*file_to_find);
ff_file_to_find = xstrdup(NameBuff); *file_to_find = xstrdup(NameBuff);
if (options & FNAME_UNESC) { if (options & FNAME_UNESC) {
// Change all "\ " to " ". // Change all "\ " to " ".
for (ptr = ff_file_to_find; *ptr != NUL; ptr++) { for (ptr = *file_to_find; *ptr != NUL; ptr++) {
if (ptr[0] == '\\' && ptr[1] == ' ') { if (ptr[0] == '\\' && ptr[1] == ' ') {
memmove(ptr, ptr + 1, strlen(ptr)); memmove(ptr, ptr + 1, strlen(ptr));
} }
@@ -1369,45 +1375,45 @@ char *find_file_in_path_option(char *ptr, size_t len, int options, int first, ch
} }
} }
rel_to_curdir = (ff_file_to_find[0] == '.' rel_to_curdir = ((*file_to_find)[0] == '.'
&& (ff_file_to_find[1] == NUL && ((*file_to_find)[1] == NUL
|| vim_ispathsep(ff_file_to_find[1]) || vim_ispathsep((*file_to_find)[1])
|| (ff_file_to_find[1] == '.' || ((*file_to_find)[1] == '.'
&& (ff_file_to_find[2] == NUL && ((*file_to_find)[2] == NUL
|| vim_ispathsep(ff_file_to_find[2]))))); || vim_ispathsep((*file_to_find)[2])))));
if (vim_isAbsName(ff_file_to_find) if (vim_isAbsName(*file_to_find)
// "..", "../path", "." and "./path": don't use the path_option // "..", "../path", "." and "./path": don't use the path_option
|| rel_to_curdir || rel_to_curdir
#if defined(MSWIN) #if defined(MSWIN)
// handle "\tmp" as absolute path // handle "\tmp" as absolute path
|| vim_ispathsep(ff_file_to_find[0]) || vim_ispathsep((*file_to_find)[0])
// handle "c:name" as absolute path // handle "c:name" as absolute path
|| (ff_file_to_find[0] != NUL && ff_file_to_find[1] == ':') || ((*file_to_find)[0] != NUL && (*file_to_find)[1] == ':')
#endif #endif
) { ) {
// Absolute path, no need to use "path_option". // Absolute path, no need to use "path_option".
// If this is not a first call, return NULL. We already returned a // If this is not a first call, return NULL. We already returned a
// filename on the first call. // filename on the first call.
if (first == true) { if (first == true) {
if (path_with_url(ff_file_to_find)) { if (path_with_url(*file_to_find)) {
file_name = xstrdup(ff_file_to_find); file_name = xstrdup(*file_to_find);
goto theend; goto theend;
} }
// When FNAME_REL flag given first use the directory of the file. // When FNAME_REL flag given first use the directory of the file.
// Otherwise or when this fails use the current directory. // Otherwise or when this fails use the current directory.
for (int run = 1; run <= 2; run++) { for (int run = 1; run <= 2; run++) {
size_t l = strlen(ff_file_to_find); size_t l = strlen(*file_to_find);
if (run == 1 if (run == 1
&& rel_to_curdir && rel_to_curdir
&& (options & FNAME_REL) && (options & FNAME_REL)
&& rel_fname != NULL && rel_fname != NULL
&& strlen(rel_fname) + l < MAXPATHL) { && strlen(rel_fname) + l < MAXPATHL) {
STRCPY(NameBuff, rel_fname); STRCPY(NameBuff, rel_fname);
STRCPY(path_tail(NameBuff), ff_file_to_find); STRCPY(path_tail(NameBuff), *file_to_find);
l = strlen(NameBuff); l = strlen(NameBuff);
} else { } else {
STRCPY(NameBuff, ff_file_to_find); STRCPY(NameBuff, *file_to_find);
run = 2; run = 2;
} }
@@ -1435,14 +1441,14 @@ char *find_file_in_path_option(char *ptr, size_t len, int options, int first, ch
// Otherwise continue to find the next match. // Otherwise continue to find the next match.
if (first == true) { if (first == true) {
// vim_findfile_free_visited can handle a possible NULL pointer // vim_findfile_free_visited can handle a possible NULL pointer
vim_findfile_free_visited(fdip_search_ctx); vim_findfile_free_visited(*search_ctx);
dir = path_option; dir = path_option;
did_findfile_init = false; did_findfile_init = false;
} }
for (;;) { for (;;) {
if (did_findfile_init) { if (did_findfile_init) {
file_name = vim_findfile(fdip_search_ctx); file_name = vim_findfile(*search_ctx);
if (file_name != NULL) { if (file_name != NULL) {
break; break;
} }
@@ -1453,8 +1459,8 @@ char *find_file_in_path_option(char *ptr, size_t len, int options, int first, ch
if (dir == NULL || *dir == NUL) { if (dir == NULL || *dir == NUL) {
// We searched all paths of the option, now we can free the search context. // We searched all paths of the option, now we can free the search context.
vim_findfile_cleanup(fdip_search_ctx); vim_findfile_cleanup(*search_ctx);
fdip_search_ctx = NULL; *search_ctx = NULL;
break; break;
} }
@@ -1466,10 +1472,10 @@ char *find_file_in_path_option(char *ptr, size_t len, int options, int first, ch
// get the stopdir string // get the stopdir string
r_ptr = vim_findfile_stopdir(buf); r_ptr = vim_findfile_stopdir(buf);
fdip_search_ctx = vim_findfile_init(buf, ff_file_to_find, *search_ctx = vim_findfile_init(buf, *file_to_find,
r_ptr, 100, false, find_what, r_ptr, 100, false, find_what,
fdip_search_ctx, false, rel_fname); *search_ctx, false, rel_fname);
if (fdip_search_ctx != NULL) { if (*search_ctx != NULL) {
did_findfile_init = true; did_findfile_init = true;
} }
xfree(buf); xfree(buf);
@@ -1479,19 +1485,15 @@ char *find_file_in_path_option(char *ptr, size_t len, int options, int first, ch
if (file_name == NULL && (options & FNAME_MESS)) { if (file_name == NULL && (options & FNAME_MESS)) {
if (first == true) { if (first == true) {
if (find_what == FINDFILE_DIR) { if (find_what == FINDFILE_DIR) {
semsg(_("E344: Can't find directory \"%s\" in cdpath"), semsg(_("E344: Can't find directory \"%s\" in cdpath"), *file_to_find);
ff_file_to_find);
} else { } else {
semsg(_("E345: Can't find file \"%s\" in path"), semsg(_("E345: Can't find file \"%s\" in path"), *file_to_find);
ff_file_to_find);
} }
} else { } else {
if (find_what == FINDFILE_DIR) { if (find_what == FINDFILE_DIR) {
semsg(_("E346: No more directory \"%s\" found in cdpath"), semsg(_("E346: No more directory \"%s\" found in cdpath"), *file_to_find);
ff_file_to_find);
} else { } else {
semsg(_("E347: No more file \"%s\" found in path"), semsg(_("E347: No more file \"%s\" found in path"), *file_to_find);
ff_file_to_find);
} }
} }
} }
@@ -1606,8 +1608,12 @@ int vim_chdirfile(char *fname, CdCause cause)
/// Change directory to "new_dir". Search 'cdpath' for relative directory names. /// Change directory to "new_dir". Search 'cdpath' for relative directory names.
int vim_chdir(char *new_dir) int vim_chdir(char *new_dir)
{ {
char *dir_name = find_directory_in_path(new_dir, strlen(new_dir), char *file_to_find = NULL;
FNAME_MESS, curbuf->b_ffname); char *search_ctx = NULL;
char *dir_name = find_directory_in_path(new_dir, strlen(new_dir), FNAME_MESS,
curbuf->b_ffname, &file_to_find, &search_ctx);
xfree(file_to_find);
vim_findfile_cleanup(search_ctx);
if (dir_name == NULL) { if (dir_name == NULL) {
return -1; return -1;
} }

View File

@@ -1690,8 +1690,11 @@ char *find_file_name_in_path(char *ptr, size_t len, int options, long count, cha
} }
if (options & FNAME_EXP) { if (options & FNAME_EXP) {
file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, true, char *file_to_find = NULL;
rel_fname); char *search_ctx = NULL;
file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
true, rel_fname, &file_to_find, &search_ctx);
// If the file could not be found in a normal way, try applying // If the file could not be found in a normal way, try applying
// 'includeexpr' (unless done already). // 'includeexpr' (unless done already).
@@ -1702,7 +1705,7 @@ char *find_file_name_in_path(char *ptr, size_t len, int options, long count, cha
ptr = tofree; ptr = tofree;
len = strlen(ptr); len = strlen(ptr);
file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
true, rel_fname); true, rel_fname, &file_to_find, &search_ctx);
} }
} }
if (file_name == NULL && (options & FNAME_MESS)) { if (file_name == NULL && (options & FNAME_MESS)) {
@@ -1716,9 +1719,12 @@ char *find_file_name_in_path(char *ptr, size_t len, int options, long count, cha
// appears several times in the path. // appears several times in the path.
while (file_name != NULL && --count > 0) { while (file_name != NULL && --count > 0) {
xfree(file_name); xfree(file_name);
file_name = file_name = find_file_in_path(ptr, len, options, false, rel_fname,
find_file_in_path(ptr, len, options, false, rel_fname); &file_to_find, &search_ctx);
} }
xfree(file_to_find);
vim_findfile_cleanup(search_ctx);
} else { } else {
file_name = xstrnsave(ptr, len); file_name = xstrnsave(ptr, len);
} }