mirror of
https://github.com/zoriya/vim.git
synced 2025-12-20 22:25:20 +00:00
patch 8.1.0098: segfault when pattern with \z() is very slow
Problem: Segfault when pattern with \z() is very slow.
Solution: Check for NULL regprog. Add "nfa_fail" to test_override() to be
able to test this. Fix that 'searchhl' resets called_emsg.
This commit is contained in:
@@ -8694,6 +8694,8 @@ test_override({name}, {val}) *test_override()*
|
|||||||
redraw disable the redrawing() function
|
redraw disable the redrawing() function
|
||||||
char_avail disable the char_avail() function
|
char_avail disable the char_avail() function
|
||||||
starting reset the "starting" variable, see below
|
starting reset the "starting" variable, see below
|
||||||
|
nfa_fail makes the NFA regexp engine fail to force a
|
||||||
|
fallback to the old engine
|
||||||
ALL clear all overrides ({val} is not used)
|
ALL clear all overrides ({val} is not used)
|
||||||
|
|
||||||
"starting" is to be used when a test should behave like
|
"starting" is to be used when a test should behave like
|
||||||
|
|||||||
@@ -13090,10 +13090,13 @@ f_test_override(typval_T *argvars, typval_T *rettv UNUSED)
|
|||||||
save_starting = -1;
|
save_starting = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (STRCMP(name, (char_u *)"nfa_fail") == 0)
|
||||||
|
nfa_fail_for_testing = val;
|
||||||
else if (STRCMP(name, (char_u *)"ALL") == 0)
|
else if (STRCMP(name, (char_u *)"ALL") == 0)
|
||||||
{
|
{
|
||||||
disable_char_avail_for_testing = FALSE;
|
disable_char_avail_for_testing = FALSE;
|
||||||
disable_redraw_for_testing = FALSE;
|
disable_redraw_for_testing = FALSE;
|
||||||
|
nfa_fail_for_testing = FALSE;
|
||||||
if (save_starting >= 0)
|
if (save_starting >= 0)
|
||||||
{
|
{
|
||||||
starting = save_starting;
|
starting = save_starting;
|
||||||
|
|||||||
@@ -1634,6 +1634,7 @@ EXTERN int alloc_fail_repeat INIT(= 0);
|
|||||||
/* flags set by test_override() */
|
/* flags set by test_override() */
|
||||||
EXTERN int disable_char_avail_for_testing INIT(= 0);
|
EXTERN int disable_char_avail_for_testing INIT(= 0);
|
||||||
EXTERN int disable_redraw_for_testing INIT(= 0);
|
EXTERN int disable_redraw_for_testing INIT(= 0);
|
||||||
|
EXTERN int nfa_fail_for_testing INIT(= 0);
|
||||||
|
|
||||||
EXTERN int in_free_unref_items INIT(= FALSE);
|
EXTERN int in_free_unref_items INIT(= FALSE);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
15
src/regexp.c
15
src/regexp.c
@@ -367,7 +367,7 @@ static char_u e_unmatchedp[] = N_("E54: Unmatched %s(");
|
|||||||
static char_u e_unmatchedpar[] = N_("E55: Unmatched %s)");
|
static char_u e_unmatchedpar[] = N_("E55: Unmatched %s)");
|
||||||
#ifdef FEAT_SYN_HL
|
#ifdef FEAT_SYN_HL
|
||||||
static char_u e_z_not_allowed[] = N_("E66: \\z( not allowed here");
|
static char_u e_z_not_allowed[] = N_("E66: \\z( not allowed here");
|
||||||
static char_u e_z1_not_allowed[] = N_("E67: \\z1 et al. not allowed here");
|
static char_u e_z1_not_allowed[] = N_("E67: \\z1 - \\z9 not allowed here");
|
||||||
#endif
|
#endif
|
||||||
static char_u e_missing_sb[] = N_("E69: Missing ] after %s%%[");
|
static char_u e_missing_sb[] = N_("E69: Missing ] after %s%%[");
|
||||||
static char_u e_empty_sb[] = N_("E70: Empty %s%%[]");
|
static char_u e_empty_sb[] = N_("E70: Empty %s%%[]");
|
||||||
@@ -2139,7 +2139,7 @@ regatom(int *flagp)
|
|||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
#ifdef FEAT_SYN_HL
|
#ifdef FEAT_SYN_HL
|
||||||
case '(': if (reg_do_extmatch != REX_SET)
|
case '(': if ((reg_do_extmatch & REX_SET) == 0)
|
||||||
EMSG_RET_NULL(_(e_z_not_allowed));
|
EMSG_RET_NULL(_(e_z_not_allowed));
|
||||||
if (one_exactly)
|
if (one_exactly)
|
||||||
EMSG_ONE_RET_NULL;
|
EMSG_ONE_RET_NULL;
|
||||||
@@ -2158,7 +2158,7 @@ regatom(int *flagp)
|
|||||||
case '6':
|
case '6':
|
||||||
case '7':
|
case '7':
|
||||||
case '8':
|
case '8':
|
||||||
case '9': if (reg_do_extmatch != REX_USE)
|
case '9': if ((reg_do_extmatch & REX_USE) == 0)
|
||||||
EMSG_RET_NULL(_(e_z1_not_allowed));
|
EMSG_RET_NULL(_(e_z1_not_allowed));
|
||||||
ret = regnode(ZREF + c - '0');
|
ret = regnode(ZREF + c - '0');
|
||||||
re_has_z = REX_USE;
|
re_has_z = REX_USE;
|
||||||
@@ -8332,8 +8332,8 @@ vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Match a regexp against multiple lines.
|
* Match a regexp against multiple lines.
|
||||||
* "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
|
* "rmp->regprog" must be a compiled regexp as returned by vim_regcomp().
|
||||||
* Note: "rmp->regprog" may be freed and changed.
|
* Note: "rmp->regprog" may be freed and changed, even set to NULL.
|
||||||
* Uses curbuf for line count and 'iskeyword'.
|
* Uses curbuf for line count and 'iskeyword'.
|
||||||
*
|
*
|
||||||
* Return zero if there is no match. Return number of lines contained in the
|
* Return zero if there is no match. Return number of lines contained in the
|
||||||
@@ -8376,7 +8376,12 @@ vim_regexec_multi(
|
|||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
report_re_switch(pat);
|
report_re_switch(pat);
|
||||||
#endif
|
#endif
|
||||||
|
// checking for \z misuse was already done when compiling for NFA,
|
||||||
|
// allow all here
|
||||||
|
reg_do_extmatch = REX_ALL;
|
||||||
rmp->regprog = vim_regcomp(pat, re_flags);
|
rmp->regprog = vim_regcomp(pat, re_flags);
|
||||||
|
reg_do_extmatch = 0;
|
||||||
|
|
||||||
if (rmp->regprog != NULL)
|
if (rmp->regprog != NULL)
|
||||||
result = rmp->regprog->engine->regexec_multi(
|
result = rmp->regprog->engine->regexec_multi(
|
||||||
rmp, win, buf, lnum, col, tm, timed_out);
|
rmp, win, buf, lnum, col, tm, timed_out);
|
||||||
|
|||||||
@@ -1482,7 +1482,7 @@ nfa_regatom(void)
|
|||||||
case '8':
|
case '8':
|
||||||
case '9':
|
case '9':
|
||||||
/* \z1...\z9 */
|
/* \z1...\z9 */
|
||||||
if (reg_do_extmatch != REX_USE)
|
if ((reg_do_extmatch & REX_USE) == 0)
|
||||||
EMSG_RET_FAIL(_(e_z1_not_allowed));
|
EMSG_RET_FAIL(_(e_z1_not_allowed));
|
||||||
EMIT(NFA_ZREF1 + (no_Magic(c) - '1'));
|
EMIT(NFA_ZREF1 + (no_Magic(c) - '1'));
|
||||||
/* No need to set nfa_has_backref, the sub-matches don't
|
/* No need to set nfa_has_backref, the sub-matches don't
|
||||||
@@ -1491,7 +1491,7 @@ nfa_regatom(void)
|
|||||||
break;
|
break;
|
||||||
case '(':
|
case '(':
|
||||||
/* \z( */
|
/* \z( */
|
||||||
if (reg_do_extmatch != REX_SET)
|
if ((reg_do_extmatch & REX_SET) == 0)
|
||||||
EMSG_RET_FAIL(_(e_z_not_allowed));
|
EMSG_RET_FAIL(_(e_z_not_allowed));
|
||||||
if (nfa_reg(REG_ZPAREN) == FAIL)
|
if (nfa_reg(REG_ZPAREN) == FAIL)
|
||||||
return FAIL; /* cascaded error */
|
return FAIL; /* cascaded error */
|
||||||
@@ -5692,7 +5692,8 @@ nfa_regmatch(
|
|||||||
nextlist->n = 0; /* clear nextlist */
|
nextlist->n = 0; /* clear nextlist */
|
||||||
nextlist->has_pim = FALSE;
|
nextlist->has_pim = FALSE;
|
||||||
++nfa_listid;
|
++nfa_listid;
|
||||||
if (prog->re_engine == AUTOMATIC_ENGINE && nfa_listid >= NFA_MAX_STATES)
|
if (prog->re_engine == AUTOMATIC_ENGINE
|
||||||
|
&& (nfa_listid >= NFA_MAX_STATES || nfa_fail_for_testing))
|
||||||
{
|
{
|
||||||
/* too many states, retry with old engine */
|
/* too many states, retry with old engine */
|
||||||
nfa_match = NFA_TOO_EXPENSIVE;
|
nfa_match = NFA_TOO_EXPENSIVE;
|
||||||
|
|||||||
@@ -7868,6 +7868,7 @@ next_search_hl(
|
|||||||
linenr_T l;
|
linenr_T l;
|
||||||
colnr_T matchcol;
|
colnr_T matchcol;
|
||||||
long nmatched;
|
long nmatched;
|
||||||
|
int save_called_emsg = called_emsg;
|
||||||
|
|
||||||
if (shl->lnum != 0)
|
if (shl->lnum != 0)
|
||||||
{
|
{
|
||||||
@@ -7986,6 +7987,9 @@ next_search_hl(
|
|||||||
break; /* useful match found */
|
break; /* useful match found */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore called_emsg for assert_fails().
|
||||||
|
called_emsg = save_called_emsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -3327,6 +3327,12 @@ syn_regexec(
|
|||||||
profile_start(&pt);
|
profile_start(&pt);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (rmp->regprog == NULL)
|
||||||
|
// This can happen if a previous call to vim_regexec_multi() tried to
|
||||||
|
// use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
|
||||||
|
// compiling the pattern with the other engine fails.
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
rmp->rmm_maxcol = syn_buf->b_p_smc;
|
rmp->rmm_maxcol = syn_buf->b_p_smc;
|
||||||
r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
|
r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
|
||||||
#ifdef FEAT_RELTIME
|
#ifdef FEAT_RELTIME
|
||||||
|
|||||||
@@ -562,3 +562,15 @@ func Test_syntax_c()
|
|||||||
let $COLORFGBG = ''
|
let $COLORFGBG = ''
|
||||||
call delete('Xtest.c')
|
call delete('Xtest.c')
|
||||||
endfun
|
endfun
|
||||||
|
|
||||||
|
" Using \z() in a region with NFA failing should not crash.
|
||||||
|
func Test_syn_wrong_z_one()
|
||||||
|
new
|
||||||
|
call setline(1, ['just some text', 'with foo and bar to match with'])
|
||||||
|
syn region FooBar start="foo\z(.*\)bar" end="\z1"
|
||||||
|
call test_override("nfa_fail", 1)
|
||||||
|
redraw!
|
||||||
|
redraw!
|
||||||
|
call test_override("ALL", 0)
|
||||||
|
bwipe!
|
||||||
|
endfunc
|
||||||
|
|||||||
@@ -761,6 +761,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
98,
|
||||||
/**/
|
/**/
|
||||||
97,
|
97,
|
||||||
/**/
|
/**/
|
||||||
|
|||||||
@@ -1013,6 +1013,7 @@ extern int (*dyn_libintl_putenv)(const char *envstring);
|
|||||||
/* values for reg_do_extmatch */
|
/* values for reg_do_extmatch */
|
||||||
# define REX_SET 1 /* to allow \z\(...\), */
|
# define REX_SET 1 /* to allow \z\(...\), */
|
||||||
# define REX_USE 2 /* to allow \z\1 et al. */
|
# define REX_USE 2 /* to allow \z\1 et al. */
|
||||||
|
# define REX_ALL (REX_SET | REX_USE)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Return values for fullpathcmp() */
|
/* Return values for fullpathcmp() */
|
||||||
|
|||||||
Reference in New Issue
Block a user