Skip to content

Commit bcf9442

Browse files
committed
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.
1 parent 5efa010 commit bcf9442

File tree

10 files changed

+45
-8
lines changed

10 files changed

+45
-8
lines changed

runtime/doc/eval.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8694,6 +8694,8 @@ test_override({name}, {val}) *test_override()*
86948694
redraw disable the redrawing() function
86958695
char_avail disable the char_avail() function
86968696
starting reset the "starting" variable, see below
8697+
nfa_fail makes the NFA regexp engine fail to force a
8698+
fallback to the old engine
86978699
ALL clear all overrides ({val} is not used)
86988700

86998701
"starting" is to be used when a test should behave like

src/evalfunc.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13090,10 +13090,13 @@ f_test_override(typval_T *argvars, typval_T *rettv UNUSED)
1309013090
save_starting = -1;
1309113091
}
1309213092
}
13093+
else if (STRCMP(name, (char_u *)"nfa_fail") == 0)
13094+
nfa_fail_for_testing = val;
1309313095
else if (STRCMP(name, (char_u *)"ALL") == 0)
1309413096
{
1309513097
disable_char_avail_for_testing = FALSE;
1309613098
disable_redraw_for_testing = FALSE;
13099+
nfa_fail_for_testing = FALSE;
1309713100
if (save_starting >= 0)
1309813101
{
1309913102
starting = save_starting;

src/globals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,6 +1634,7 @@ EXTERN int alloc_fail_repeat INIT(= 0);
16341634
/* flags set by test_override() */
16351635
EXTERN int disable_char_avail_for_testing INIT(= 0);
16361636
EXTERN int disable_redraw_for_testing INIT(= 0);
1637+
EXTERN int nfa_fail_for_testing INIT(= 0);
16371638

16381639
EXTERN int in_free_unref_items INIT(= FALSE);
16391640
#endif

src/regexp.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ static char_u e_unmatchedp[] = N_("E54: Unmatched %s(");
367367
static char_u e_unmatchedpar[] = N_("E55: Unmatched %s)");
368368
#ifdef FEAT_SYN_HL
369369
static char_u e_z_not_allowed[] = N_("E66: \\z( not allowed here");
370-
static char_u e_z1_not_allowed[] = N_("E67: \\z1 et al. not allowed here");
370+
static char_u e_z1_not_allowed[] = N_("E67: \\z1 - \\z9 not allowed here");
371371
#endif
372372
static char_u e_missing_sb[] = N_("E69: Missing ] after %s%%[");
373373
static char_u e_empty_sb[] = N_("E70: Empty %s%%[]");
@@ -2139,7 +2139,7 @@ regatom(int *flagp)
21392139
switch (c)
21402140
{
21412141
#ifdef FEAT_SYN_HL
2142-
case '(': if (reg_do_extmatch != REX_SET)
2142+
case '(': if ((reg_do_extmatch & REX_SET) == 0)
21432143
EMSG_RET_NULL(_(e_z_not_allowed));
21442144
if (one_exactly)
21452145
EMSG_ONE_RET_NULL;
@@ -2158,7 +2158,7 @@ regatom(int *flagp)
21582158
case '6':
21592159
case '7':
21602160
case '8':
2161-
case '9': if (reg_do_extmatch != REX_USE)
2161+
case '9': if ((reg_do_extmatch & REX_USE) == 0)
21622162
EMSG_RET_NULL(_(e_z1_not_allowed));
21632163
ret = regnode(ZREF + c - '0');
21642164
re_has_z = REX_USE;
@@ -8332,8 +8332,8 @@ vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col)
83328332

83338333
/*
83348334
* Match a regexp against multiple lines.
8335-
* "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
8336-
* Note: "rmp->regprog" may be freed and changed.
8335+
* "rmp->regprog" must be a compiled regexp as returned by vim_regcomp().
8336+
* Note: "rmp->regprog" may be freed and changed, even set to NULL.
83378337
* Uses curbuf for line count and 'iskeyword'.
83388338
*
83398339
* Return zero if there is no match. Return number of lines contained in the
@@ -8376,7 +8376,12 @@ vim_regexec_multi(
83768376
#ifdef FEAT_EVAL
83778377
report_re_switch(pat);
83788378
#endif
8379+
// checking for \z misuse was already done when compiling for NFA,
8380+
// allow all here
8381+
reg_do_extmatch = REX_ALL;
83798382
rmp->regprog = vim_regcomp(pat, re_flags);
8383+
reg_do_extmatch = 0;
8384+
83808385
if (rmp->regprog != NULL)
83818386
result = rmp->regprog->engine->regexec_multi(
83828387
rmp, win, buf, lnum, col, tm, timed_out);

src/regexp_nfa.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,7 +1482,7 @@ nfa_regatom(void)
14821482
case '8':
14831483
case '9':
14841484
/* \z1...\z9 */
1485-
if (reg_do_extmatch != REX_USE)
1485+
if ((reg_do_extmatch & REX_USE) == 0)
14861486
EMSG_RET_FAIL(_(e_z1_not_allowed));
14871487
EMIT(NFA_ZREF1 + (no_Magic(c) - '1'));
14881488
/* No need to set nfa_has_backref, the sub-matches don't
@@ -1491,7 +1491,7 @@ nfa_regatom(void)
14911491
break;
14921492
case '(':
14931493
/* \z( */
1494-
if (reg_do_extmatch != REX_SET)
1494+
if ((reg_do_extmatch & REX_SET) == 0)
14951495
EMSG_RET_FAIL(_(e_z_not_allowed));
14961496
if (nfa_reg(REG_ZPAREN) == FAIL)
14971497
return FAIL; /* cascaded error */
@@ -5692,7 +5692,8 @@ nfa_regmatch(
56925692
nextlist->n = 0; /* clear nextlist */
56935693
nextlist->has_pim = FALSE;
56945694
++nfa_listid;
5695-
if (prog->re_engine == AUTOMATIC_ENGINE && nfa_listid >= NFA_MAX_STATES)
5695+
if (prog->re_engine == AUTOMATIC_ENGINE
5696+
&& (nfa_listid >= NFA_MAX_STATES || nfa_fail_for_testing))
56965697
{
56975698
/* too many states, retry with old engine */
56985699
nfa_match = NFA_TOO_EXPENSIVE;

src/screen.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7868,6 +7868,7 @@ next_search_hl(
78687868
linenr_T l;
78697869
colnr_T matchcol;
78707870
long nmatched;
7871+
int save_called_emsg = called_emsg;
78717872

78727873
if (shl->lnum != 0)
78737874
{
@@ -7986,6 +7987,9 @@ next_search_hl(
79867987
break; /* useful match found */
79877988
}
79887989
}
7990+
7991+
// Restore called_emsg for assert_fails().
7992+
called_emsg = save_called_emsg;
79897993
}
79907994

79917995
/*

src/syntax.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3327,6 +3327,12 @@ syn_regexec(
33273327
profile_start(&pt);
33283328
#endif
33293329

3330+
if (rmp->regprog == NULL)
3331+
// This can happen if a previous call to vim_regexec_multi() tried to
3332+
// use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
3333+
// compiling the pattern with the other engine fails.
3334+
return FALSE;
3335+
33303336
rmp->rmm_maxcol = syn_buf->b_p_smc;
33313337
r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
33323338
#ifdef FEAT_RELTIME

src/testdir/test_syntax.vim

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,3 +562,15 @@ func Test_syntax_c()
562562
let $COLORFGBG = ''
563563
call delete('Xtest.c')
564564
endfun
565+
566+
" Using \z() in a region with NFA failing should not crash.
567+
func Test_syn_wrong_z_one()
568+
new
569+
call setline(1, ['just some text', 'with foo and bar to match with'])
570+
syn region FooBar start="foo\z(.*\)bar" end="\z1"
571+
call test_override("nfa_fail", 1)
572+
redraw!
573+
redraw!
574+
call test_override("ALL", 0)
575+
bwipe!
576+
endfunc

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,8 @@ static char *(features[]) =
761761

762762
static int included_patches[] =
763763
{ /* Add new patch number below this line */
764+
/**/
765+
98,
764766
/**/
765767
97,
766768
/**/

src/vim.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,7 @@ extern int (*dyn_libintl_putenv)(const char *envstring);
10131013
/* values for reg_do_extmatch */
10141014
# define REX_SET 1 /* to allow \z\(...\), */
10151015
# define REX_USE 2 /* to allow \z\1 et al. */
1016+
# define REX_ALL (REX_SET | REX_USE)
10161017
#endif
10171018

10181019
/* Return values for fullpathcmp() */

0 commit comments

Comments
 (0)