fix(runtime): do not allow breakcheck inside runtime path calculation

problem: breakcheck might run arbitrary lua code, which might require
modules and thus invoke runtime path calculation recursively.
solution: Block the use of breakcheck when expanding glob patterns
inside 'runtimepath'

fixes #23012
This commit is contained in:
bfredl
2023-04-17 13:08:53 +02:00
parent 7bf1a917b7
commit aee6f08ce1
7 changed files with 26 additions and 10 deletions

View File

@ -620,7 +620,7 @@ static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, in
static int stardepth = 0; // depth for "**" expansion static int stardepth = 0; // depth for "**" expansion
// Expanding "**" may take a long time, check for CTRL-C. // Expanding "**" may take a long time, check for CTRL-C.
if (stardepth > 0) { if (stardepth > 0 && !(flags & EW_NOBREAK)) {
os_breakcheck(); os_breakcheck();
if (got_int) { if (got_int) {
return 0; return 0;
@ -701,7 +701,8 @@ static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, in
if (flags & (EW_NOERROR | EW_NOTWILD)) { if (flags & (EW_NOERROR | EW_NOTWILD)) {
emsg_silent++; emsg_silent++;
} }
regmatch.regprog = vim_regcomp(pat, RE_MAGIC); bool nobreak = (flags & EW_NOBREAK);
regmatch.regprog = vim_regcomp(pat, RE_MAGIC | (nobreak ? RE_NOBREAK : 0));
if (flags & (EW_NOERROR | EW_NOTWILD)) { if (flags & (EW_NOERROR | EW_NOTWILD)) {
emsg_silent--; emsg_silent--;
} }

View File

@ -26,6 +26,7 @@
#define EW_DODOT 0x4000 // also files starting with a dot #define EW_DODOT 0x4000 // also files starting with a dot
#define EW_EMPTYOK 0x8000 // no matches is not an error #define EW_EMPTYOK 0x8000 // no matches is not an error
#define EW_NOTENV 0x10000 // do not expand environment variables #define EW_NOTENV 0x10000 // do not expand environment variables
#define EW_NOBREAK 0x20000 // do not invoke breakcheck
/// Return value for the comparison of two files. Also @see path_full_compare. /// Return value for the comparison of two files. Also @see path_full_compare.
typedef enum file_comparison { typedef enum file_comparison {

View File

@ -989,6 +989,8 @@ typedef struct {
// flag in the regexp. Defaults to false, always. // flag in the regexp. Defaults to false, always.
bool reg_icombine; bool reg_icombine;
bool reg_nobreak;
// Copy of "rmm_maxcol": maximum column to search for a match. Zero when // Copy of "rmm_maxcol": maximum column to search for a match. Zero when
// there is no maximum. // there is no maximum.
colnr_T reg_maxcol; colnr_T reg_maxcol;
@ -1011,6 +1013,13 @@ typedef struct {
static regexec_T rex; static regexec_T rex;
static bool rex_in_use = false; static bool rex_in_use = false;
static void reg_breakcheck(void)
{
if (!rex.reg_nobreak) {
fast_breakcheck();
}
}
// Return true if character 'c' is included in 'iskeyword' option for // Return true if character 'c' is included in 'iskeyword' option for
// "reg_buf" buffer. // "reg_buf" buffer.
static bool reg_iswordc(int c) static bool reg_iswordc(int c)
@ -1221,7 +1230,7 @@ static void reg_nextline(void)
{ {
rex.line = (uint8_t *)reg_getline(++rex.lnum); rex.line = (uint8_t *)reg_getline(++rex.lnum);
rex.input = rex.line; rex.input = rex.line;
fast_breakcheck(); reg_breakcheck();
} }
// Check whether a backreference matches. // Check whether a backreference matches.
@ -2265,6 +2274,7 @@ static void init_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_
rex.reg_line_lbr = false; rex.reg_line_lbr = false;
rex.reg_ic = rmp->rmm_ic; rex.reg_ic = rmp->rmm_ic;
rex.reg_icombine = false; rex.reg_icombine = false;
rex.reg_nobreak = rmp->regprog->re_flags & RE_NOBREAK;
rex.reg_maxcol = rmp->rmm_maxcol; rex.reg_maxcol = rmp->rmm_maxcol;
} }

View File

@ -10,6 +10,7 @@
#define RE_STRING 2 ///< match in string instead of buffer text #define RE_STRING 2 ///< match in string instead of buffer text
#define RE_STRICT 4 ///< don't allow [abc] without ] #define RE_STRICT 4 ///< don't allow [abc] without ]
#define RE_AUTO 8 ///< automatic engine selection #define RE_AUTO 8 ///< automatic engine selection
#define RE_NOBREAK 16 ///< don't use breakcheck functions
// values for reg_do_extmatch // values for reg_do_extmatch
#define REX_SET 1 ///< to allow \z\(...\), #define REX_SET 1 ///< to allow \z\(...\),

View File

@ -3539,7 +3539,7 @@ static bool regmatch(uint8_t *scan, proftime_T *tm, int *timed_out)
for (;;) { for (;;) {
// Some patterns may take a long time to match, e.g., "\([a-z]\+\)\+Q". // Some patterns may take a long time to match, e.g., "\([a-z]\+\)\+Q".
// Allow interrupting them with CTRL-C. // Allow interrupting them with CTRL-C.
fast_breakcheck(); reg_breakcheck();
#ifdef REGEXP_DEBUG #ifdef REGEXP_DEBUG
if (scan != NULL && regnarrate) { if (scan != NULL && regnarrate) {
@ -4792,7 +4792,7 @@ static bool regmatch(uint8_t *scan, proftime_T *tm, int *timed_out)
break; break;
} }
rex.input = rex.line + strlen((char *)rex.line); rex.input = rex.line + strlen((char *)rex.line);
fast_breakcheck(); reg_breakcheck();
} else { } else {
MB_PTR_BACK(rex.line, rex.input); MB_PTR_BACK(rex.line, rex.input);
} }
@ -5155,6 +5155,7 @@ static int bt_regexec_nl(regmatch_T *rmp, uint8_t *line, colnr_T col, bool line_
rex.reg_win = NULL; rex.reg_win = NULL;
rex.reg_ic = rmp->rm_ic; rex.reg_ic = rmp->rm_ic;
rex.reg_icombine = false; rex.reg_icombine = false;
rex.reg_nobreak = rmp->regprog->re_flags & RE_NOBREAK;
rex.reg_maxcol = 0; rex.reg_maxcol = 0;
long r = bt_regexec_both(line, col, NULL, NULL); long r = bt_regexec_both(line, col, NULL, NULL);

View File

@ -5890,7 +5890,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
regsubs_T *r; regsubs_T *r;
// Some patterns may take a long time to match, especially when using // Some patterns may take a long time to match, especially when using
// recursive_regmatch(). Allow interrupting them with CTRL-C. // recursive_regmatch(). Allow interrupting them with CTRL-C.
fast_breakcheck(); reg_breakcheck();
if (got_int) { if (got_int) {
return false; return false;
} }
@ -6020,7 +6020,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
for (listidx = 0; listidx < thislist->n; listidx++) { for (listidx = 0; listidx < thislist->n; listidx++) {
// If the list gets very long there probably is something wrong. // If the list gets very long there probably is something wrong.
// At least allow interrupting with CTRL-C. // At least allow interrupting with CTRL-C.
fast_breakcheck(); reg_breakcheck();
if (got_int) { if (got_int) {
break; break;
} }
@ -7168,7 +7168,7 @@ nextchar:
} }
// Allow interrupting with CTRL-C. // Allow interrupting with CTRL-C.
line_breakcheck(); reg_breakcheck();
if (got_int) { if (got_int) {
break; break;
} }
@ -7591,6 +7591,7 @@ static int nfa_regexec_nl(regmatch_T *rmp, uint8_t *line, colnr_T col, bool line
rex.reg_win = NULL; rex.reg_win = NULL;
rex.reg_ic = rmp->rm_ic; rex.reg_ic = rmp->rm_ic;
rex.reg_icombine = false; rex.reg_icombine = false;
rex.reg_nobreak = rmp->regprog->re_flags & RE_NOBREAK;
rex.reg_maxcol = 0; rex.reg_maxcol = 0;
return (int)nfa_regexec_both(line, col, NULL, NULL); return (int)nfa_regexec_both(line, col, NULL, NULL);
} }

View File

@ -470,7 +470,8 @@ int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback, void *c
} }
int ew_flags = ((flags & DIP_DIR) ? EW_DIR : EW_FILE) int ew_flags = ((flags & DIP_DIR) ? EW_DIR : EW_FILE)
| (flags & DIP_DIRFILE) ? (EW_DIR|EW_FILE) : 0; | ((flags & DIP_DIRFILE) ? (EW_DIR|EW_FILE) : 0)
| EW_NOBREAK;
// Expand wildcards, invoke the callback for each match. // Expand wildcards, invoke the callback for each match.
char *(pat[]) = { buf }; char *(pat[]) = { buf };
@ -670,7 +671,7 @@ static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_
int num_files; int num_files;
char **files; char **files;
char *(pat[]) = { entry }; char *(pat[]) = { entry };
if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) { if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR | EW_NOBREAK) == OK) {
for (int i = 0; i < num_files; i++) { for (int i = 0; i < num_files; i++) {
push_path(search_path, rtp_used, files[i], after); push_path(search_path, rtp_used, files[i], after);
} }