#define REG_ZPAREN 2 // \z(\)
#define REG_NPAREN 3 // \%(\)
+// Limit recursive parsing of nested regexp atoms to avoid using up the C stack.
+#define REG_MAX_PAREN_DEPTH 1000
+
typedef struct
{
char_u *regparse;
static char_u *regcode; // Code-emit pointer, or JUST_CALC_SIZE
static long regsize; // Code size.
static int reg_toolong; // TRUE when offset out of range
+static int bt_reg_parse_depth; // nesting depth in reg()
static char_u had_endbrace[NSUBEXP]; // flags, TRUE if end of () found
static long brace_min[10]; // Minimums for complex brace repeats
static long brace_max[10]; // Maximums for complex brace repeats
#endif
regsize = 0L;
reg_toolong = FALSE;
+ bt_reg_parse_depth = 0;
regflags = 0;
#if defined(FEAT_SYN_HL)
had_eol = FALSE;
else
ret = NULL;
+ if (bt_reg_parse_depth >= REG_MAX_PAREN_DEPTH)
+ EMSG_RET_NULL(_(e_command_too_complex));
+ ++bt_reg_parse_depth;
+
// Pick up the branches, linking them together.
br = regbranch(&flags);
if (br == NULL)
- return NULL;
+ {
+ ret = NULL;
+ goto theend;
+ }
if (ret != NULL)
regtail(ret, br); // [MZ]OPEN -> first.
else
skipchr();
br = regbranch(&flags);
if (br == NULL || reg_toolong)
- return NULL;
+ {
+ ret = NULL;
+ goto theend;
+ }
regtail(ret, br); // BRANCH -> BRANCH.
if (!(flags & HASWIDTH))
*flagp &= ~HASWIDTH;
{
#ifdef FEAT_SYN_HL
if (paren == REG_ZPAREN)
- EMSG_RET_NULL(_(e_unmatched_z));
+ {
+ emsg(_(e_unmatched_z));
+ rc_did_emsg = TRUE;
+ ret = NULL;
+ goto theend;
+ }
else
#endif
- if (paren == REG_NPAREN)
- EMSG2_RET_NULL(_(e_unmatched_str_percent_open), reg_magic == MAGIC_ALL);
+ if (paren == REG_NPAREN)
+ {
+ semsg(_(e_unmatched_str_percent_open),
+ reg_magic == MAGIC_ALL ? "" : "\\");
+ rc_did_emsg = TRUE;
+ ret = NULL;
+ goto theend;
+ }
else
- EMSG2_RET_NULL(_(e_unmatched_str_open), reg_magic == MAGIC_ALL);
+ {
+ semsg(_(e_unmatched_str_open),
+ reg_magic == MAGIC_ALL ? "" : "\\");
+ rc_did_emsg = TRUE;
+ ret = NULL;
+ goto theend;
+ }
}
else if (paren == REG_NOPAREN && peekchr() != NUL)
{
if (curchr == Magic(')'))
- EMSG2_RET_NULL(_(e_unmatched_str_close), reg_magic == MAGIC_ALL);
+ {
+ semsg(_(e_unmatched_str_close),
+ reg_magic == MAGIC_ALL ? "" : "\\");
+ rc_did_emsg = TRUE;
+ ret = NULL;
+ goto theend;
+ }
else
- EMSG_RET_NULL(_(e_trailing_characters)); // "Can't happen".
- // NOTREACHED
+ {
+ emsg(_(e_trailing_characters)); // "Can't happen".
+ rc_did_emsg = TRUE;
+ ret = NULL;
+ goto theend;
+ }
}
// Here we set the flag allowing back references to this set of
// parentheses.
if (paren == REG_PAREN)
had_endbrace[parno] = TRUE; // have seen the close paren
+
+theend:
+ --bt_reg_parse_depth;
return ret;
}
static int *post_start; // holds the postfix form of r.e.
static int *post_end;
static int *post_ptr;
+static int nfa_reg_parse_depth; // nesting depth in nfa_reg()
// Set when the pattern should use the NFA engine.
// E.g. [[:upper:]] only allows 8bit characters for BT engine,
wants_nfa = FALSE;
rex.nfa_has_zend = FALSE;
rex.nfa_has_backref = FALSE;
+ nfa_reg_parse_depth = 0;
// shared with BT engine
regcomp_start(expr, re_flags);
int paren) // REG_NOPAREN, REG_PAREN, REG_NPAREN or REG_ZPAREN
{
int parno = 0;
+ int status = FAIL;
if (paren == REG_PAREN)
{
}
#endif
+ if (nfa_reg_parse_depth >= REG_MAX_PAREN_DEPTH)
+ EMSG_RET_FAIL(_(e_command_too_complex));
+ ++nfa_reg_parse_depth;
+
if (nfa_regbranch() == FAIL)
- return FAIL; // cascaded error
+ goto theend; // cascaded error
while (peekchr() == Magic('|'))
{
skipchr();
if (nfa_regbranch() == FAIL)
- return FAIL; // cascaded error
+ goto theend; // cascaded error
EMIT(NFA_OR);
}
if (paren != REG_NOPAREN && getchr() != Magic(')'))
{
if (paren == REG_NPAREN)
- EMSG2_RET_FAIL(_(e_unmatched_str_percent_open),
- reg_magic == MAGIC_ALL);
+ semsg(_(e_unmatched_str_percent_open),
+ reg_magic == MAGIC_ALL ? "" : "\\");
else
- EMSG2_RET_FAIL(_(e_unmatched_str_open), reg_magic == MAGIC_ALL);
+ semsg(_(e_unmatched_str_open),
+ reg_magic == MAGIC_ALL ? "" : "\\");
+ rc_did_emsg = TRUE;
+ goto theend;
}
else if (paren == REG_NOPAREN && peekchr() != NUL)
{
if (peekchr() == Magic(')'))
- EMSG2_RET_FAIL(_(e_unmatched_str_close), reg_magic == MAGIC_ALL);
+ semsg(_(e_unmatched_str_close),
+ reg_magic == MAGIC_ALL ? "" : "\\");
else
- EMSG_RET_FAIL(_(e_nfa_regexp_proper_termination_error));
+ emsg(_(e_nfa_regexp_proper_termination_error));
+ rc_did_emsg = TRUE;
+ goto theend;
}
/*
* Here we set the flag allowing back references to this set of
EMIT(NFA_ZOPEN + parno);
#endif
- return OK;
+ status = OK;
+
+theend:
+ --nfa_reg_parse_depth;
+ return status;
}
#ifdef DEBUG
call assert_equal('', matchstr('abcd', '\%o181\%o142'))
endfunc
+func Test_regexp_recursion_limit()
+ let nested = repeat('\%(', 20000) .. 'x' .. repeat('\)', 20000)
+ call assert_fails("call matchstr('x', '\%#=2' .. nested)", 'E74:')
+ call assert_fails("call matchstr('x', '\%#=1' .. nested)", 'E74:')
+endfunc
+
" Test for using the last substitute string pattern (~)
func Test_regexp_last_subst_string()
new
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 815,
/**/
814,
/**/