Patch 8.1.0905
Problem:    Complicated regexp causes a crash. (Kuang-che Wu)
Solution:   Limit the recursiveness of addstate(). (closes #3941)
Files:      src/regexp_nfa.c, src/testdir/test_regexp_latin.vim


*** ../vim-8.1.0904/src/regexp_nfa.c    2019-01-31 15:34:35.864056935 +0100
--- src/regexp_nfa.c    2019-02-12 23:05:19.968038497 +0100
***************
*** 4284,4289 ****
--- 4284,4290 ----
  /*
   * Add "state" and possibly what follows to state list ".".
   * Returns "subs_arg", possibly copied into temp_subs.
+  * Returns NULL when recursiveness is too deep.
   */
      static regsubs_T *
  addstate(
***************
*** 4310,4315 ****
--- 4311,4325 ----
  #ifdef ENABLE_LOG
      int                       did_print = FALSE;
  #endif
+     static int                depth = 0;
+ 
+     // This function is called recursively.  When the depth is too much we run
+     // out of stack and crash, limit recursiveness here.
+     if (++depth >= 10000 || subs == NULL)
+     {
+       --depth;
+       return NULL;
+     }
  
      if (off_arg <= -ADDSTATE_HERE_OFFSET)
      {
***************
*** 4421,4426 ****
--- 4431,4437 ----
                            abs(state->id), l->id, state->c, code,
                            pim == NULL ? "NULL" : "yes", l->has_pim, found);
  #endif
+                       --depth;
                        return subs;
                    }
                }
***************
*** 4595,4601 ****
            }
  
            subs = addstate(l, state->out, subs, pim, off_arg);
!           /* "subs" may have changed, need to set "sub" again */
  #ifdef FEAT_SYN_HL
            if (state->c >= NFA_ZOPEN && state->c <= NFA_ZOPEN9)
                sub = &subs->synt;
--- 4606,4614 ----
            }
  
            subs = addstate(l, state->out, subs, pim, off_arg);
!           if (subs == NULL)
!               break;
!           // "subs" may have changed, need to set "sub" again
  #ifdef FEAT_SYN_HL
            if (state->c >= NFA_ZOPEN && state->c <= NFA_ZOPEN9)
                sub = &subs->synt;
***************
*** 4619,4625 ****
                        ? subs->norm.list.multi[0].end_lnum >= 0
                        : subs->norm.list.line[0].end != NULL))
            {
!               /* Do not overwrite the position set by \ze. */
                subs = addstate(l, state->out, subs, pim, off_arg);
                break;
            }
--- 4632,4638 ----
                        ? subs->norm.list.multi[0].end_lnum >= 0
                        : subs->norm.list.line[0].end != NULL))
            {
!               // Do not overwrite the position set by \ze.
                subs = addstate(l, state->out, subs, pim, off_arg);
                break;
            }
***************
*** 4695,4700 ****
--- 4708,4715 ----
            }
  
            subs = addstate(l, state->out, subs, pim, off_arg);
+           if (subs == NULL)
+               break;
            /* "subs" may have changed, need to set "sub" again */
  #ifdef FEAT_SYN_HL
            if (state->c >= NFA_ZCLOSE && state->c <= NFA_ZCLOSE9)
***************
*** 4710,4715 ****
--- 4725,4731 ----
            sub->in_use = save_in_use;
            break;
      }
+     --depth;
      return subs;
  }
  
***************
*** 4719,4725 ****
   * This makes sure the order of states to be tried does not change, which
   * matters for alternatives.
   */
!     static void
  addstate_here(
      nfa_list_T                *l,     /* runtime state list */
      nfa_state_T               *state, /* state to update */
--- 4735,4741 ----
   * This makes sure the order of states to be tried does not change, which
   * matters for alternatives.
   */
!     static regsubs_T *
  addstate_here(
      nfa_list_T                *l,     /* runtime state list */
      nfa_state_T               *state, /* state to update */
***************
*** 4730,4752 ****
      int tlen = l->n;
      int count;
      int listidx = *ip;
  
      /* First add the state(s) at the end, so that we know how many there are.
       * Pass the listidx as offset (avoids adding another argument to
       * addstate(). */
!     addstate(l, state, subs, pim, -listidx - ADDSTATE_HERE_OFFSET);
  
!     /* when "*ip" was at the end of the list, nothing to do */
      if (listidx + 1 == tlen)
!       return;
  
!     /* re-order to put the new state at the current position */
      count = l->n - tlen;
      if (count == 0)
!       return; /* no state got added */
      if (count == 1)
      {
!       /* overwrite the current state */
        l->t[listidx] = l->t[l->n - 1];
      }
      else if (count > 1)
--- 4746,4771 ----
      int tlen = l->n;
      int count;
      int listidx = *ip;
+     regsubs_T *r;
  
      /* First add the state(s) at the end, so that we know how many there are.
       * Pass the listidx as offset (avoids adding another argument to
       * addstate(). */
!     r = addstate(l, state, subs, pim, -listidx - ADDSTATE_HERE_OFFSET);
!     if (r == NULL)
!       return r;
  
!     // when "*ip" was at the end of the list, nothing to do
      if (listidx + 1 == tlen)
!       return r;
  
!     // re-order to put the new state at the current position
      count = l->n - tlen;
      if (count == 0)
!       return r; // no state got added
      if (count == 1)
      {
!       // overwrite the current state
        l->t[listidx] = l->t[l->n - 1];
      }
      else if (count > 1)
***************
*** 4760,4766 ****
            l->len = l->len * 3 / 2 + 50;
            newl = (nfa_thread_T *)alloc(l->len * sizeof(nfa_thread_T));
            if (newl == NULL)
!               return;
            mch_memmove(&(newl[0]),
                    &(l->t[0]),
                    sizeof(nfa_thread_T) * listidx);
--- 4779,4785 ----
            l->len = l->len * 3 / 2 + 50;
            newl = (nfa_thread_T *)alloc(l->len * sizeof(nfa_thread_T));
            if (newl == NULL)
!               return r;
            mch_memmove(&(newl[0]),
                    &(l->t[0]),
                    sizeof(nfa_thread_T) * listidx);
***************
*** 4787,4792 ****
--- 4806,4813 ----
      }
      --l->n;
      *ip = listidx - 1;
+ 
+     return r;
  }
  
  /*
***************
*** 5493,5498 ****
--- 5514,5520 ----
      int               add_count;
      int               add_off = 0;
      int               toplevel = start->c == NFA_MOPEN;
+     regsubs_T *r;
  #ifdef NFA_REGEXP_DEBUG_LOG
      FILE      *debug;
  #endif
***************
*** 5567,5576 ****
        else
            m->norm.list.line[0].start = rex.input;
        m->norm.in_use = 1;
!       addstate(thislist, start->out, m, NULL, 0);
      }
      else
!       addstate(thislist, start, m, NULL, 0);
  
  #define       ADD_STATE_IF_MATCH(state)                       \
      if (result) {                                     \
--- 5589,5603 ----
        else
            m->norm.list.line[0].start = rex.input;
        m->norm.in_use = 1;
!       r = addstate(thislist, start->out, m, NULL, 0);
      }
      else
!       r = addstate(thislist, start, m, NULL, 0);
!     if (r == NULL)
!     {
!       nfa_match = NFA_TOO_EXPENSIVE;
!       goto theend;
!     }
  
  #define       ADD_STATE_IF_MATCH(state)                       \
      if (result) {                                     \
***************
*** 5874,5881 ****
                        /* t->state->out1 is the corresponding END_INVISIBLE
                         * node; Add its out to the current list (zero-width
                         * match). */
!                       addstate_here(thislist, t->state->out1->out, &t->subs,
!                                                              &pim, &listidx);
                    }
                }
                break;
--- 5901,5912 ----
                        /* t->state->out1 is the corresponding END_INVISIBLE
                         * node; Add its out to the current list (zero-width
                         * match). */
!                       if (addstate_here(thislist, t->state->out1->out,
!                                            &t->subs, &pim, &listidx) == NULL)
!                       {
!                           nfa_match = NFA_TOO_EXPENSIVE;
!                           goto theend;
!                       }
                    }
                }
                break;
***************
*** 6749,6761 ****
                }
  
                if (add_here)
!                   addstate_here(thislist, add_state, &t->subs, pim, &listidx);
                else
                {
!                   addstate(nextlist, add_state, &t->subs, pim, add_off);
                    if (add_count > 0)
                        nextlist->t[nextlist->n - 1].count = add_count;
                }
            }
  
        } /* for (thislist = thislist; thislist->state; thislist++) */
--- 6780,6798 ----
                }
  
                if (add_here)
!                   r = addstate_here(thislist, add_state, &t->subs,
!                                                               pim, &listidx);
                else
                {
!                   r = addstate(nextlist, add_state, &t->subs, pim, add_off);
                    if (add_count > 0)
                        nextlist->t[nextlist->n - 1].count = add_count;
                }
+               if (r == NULL)
+               {
+                   nfa_match = NFA_TOO_EXPENSIVE;
+                   goto theend;
+               }
            }
  
        } /* for (thislist = thislist; thislist->state; thislist++) */
***************
*** 6831,6841 ****
                                         (colnr_T)(rex.input - rex.line) + clen;
                    else
                        m->norm.list.line[0].start = rex.input + clen;
!                   addstate(nextlist, start->out, m, NULL, clen);
                }
            }
            else
!               addstate(nextlist, start, m, NULL, clen);
        }
  
  #ifdef ENABLE_LOG
--- 6868,6888 ----
                                         (colnr_T)(rex.input - rex.line) + clen;
                    else
                        m->norm.list.line[0].start = rex.input + clen;
!                   if (addstate(nextlist, start->out, m, NULL, clen) == NULL)
!                   {
!                       nfa_match = NFA_TOO_EXPENSIVE;
!                       goto theend;
!                   }
                }
            }
            else
!           {
!               if (addstate(nextlist, start, m, NULL, clen) == NULL)
!               {
!                   nfa_match = NFA_TOO_EXPENSIVE;
!                   goto theend;
!               }
!           }
        }
  
  #ifdef ENABLE_LOG
*** ../vim-8.1.0904/src/testdir/test_regexp_latin.vim   2019-01-14 
23:19:26.244853406 +0100
--- src/testdir/test_regexp_latin.vim   2019-02-12 23:04:40.336347263 +0100
***************
*** 84,86 ****
--- 84,92 ----
    call assert_fails('/a\{a}', 'E870:')
    set re=0
  endfunc
+ 
+ func Test_recursive_addstate()
+   " This will call addstate() recursively until it runs into the limit.
+   let lnum = search('\v((){328}){389}')
+   call assert_equal(0, lnum)
+ endfunc
*** ../vim-8.1.0904/src/version.c       2019-02-12 22:37:24.181961482 +0100
--- src/version.c       2019-02-12 22:58:41.059230984 +0100
***************
*** 785,786 ****
--- 785,788 ----
  {   /* Add new patch number below this line */
+ /**/
+     905,
  /**/

-- 
To keep milk from turning sour: Keep it in the cow.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui