]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.0908: crash when changing the function table while listing it v8.2.0908
authorBram Moolenaar <Bram@vim.org>
Fri, 5 Jun 2020 19:06:10 +0000 (21:06 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 5 Jun 2020 19:06:10 +0000 (21:06 +0200)
Problem:    Crash when changing the function table while listing it.
Solution:   Bail out when the function table changes. (closes #6209)

src/testdir/test_timers.vim
src/userfunc.c
src/version.c

index 639f5c2d19a200acece839e2430bd4301c00865b..829e94b5de9e28721951f442c84ab418dfabc1cc 100644 (file)
@@ -3,6 +3,7 @@
 source check.vim
 CheckFeature timers
 
+source screendump.vim
 source shared.vim
 source term_util.vim
 
@@ -424,4 +425,28 @@ func Test_timer_invalid_callback()
   call assert_fails('call timer_start(0, "0")', 'E921')
 endfunc
 
+func Test_timer_changing_function_list()
+  CheckRunVimInTerminal
+
+  " Create a large number of functions.  Should get the "more" prompt.
+  " The typing "G" triggers the timer, which changes the function table.
+  let lines =<< trim END
+    for func in map(range(1,99), "'Func' .. v:val")
+      exe "func " .. func .. "()"
+      endfunc
+    endfor
+    au CmdlineLeave : call timer_start(0, {-> 0})
+  END
+  call writefile(lines, 'XTest_timerchange')
+  let buf = RunVimInTerminal('-S XTest_timerchange', #{rows: 10})
+  call term_sendkeys(buf, ":fu\<CR>")
+  call WaitForAssert({-> assert_match('-- More --', term_getline(buf, 10))})
+  call term_sendkeys(buf, "G")
+  call WaitForAssert({-> assert_match('E454', term_getline(buf, 9))})
+  call term_sendkeys(buf, "\<Esc>")
+
+  call StopVimInTerminal(buf)
+  call delete('XTest_timerchange')
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 469dcc1a532ad730758c46d1d844e38cfa9701a0..b495769a95bd0055e8e12a25277893a9bd4e508d 100644 (file)
@@ -2372,6 +2372,44 @@ untrans_function_name(char_u *name)
     return NULL;
 }
 
+/*
+ * List functions.  When "regmatch" is NULL all of then.
+ * Otherwise functions matching "regmatch".
+ */
+    static void
+list_functions(regmatch_T *regmatch)
+{
+    long_u     used = func_hashtab.ht_used;
+    long_u     todo = used;
+    hashitem_T *ht_array = func_hashtab.ht_array;
+    hashitem_T *hi;
+
+    for (hi = ht_array; todo > 0 && !got_int; ++hi)
+    {
+       if (!HASHITEM_EMPTY(hi))
+       {
+           ufunc_T     *fp = HI2UF(hi);
+
+           --todo;
+           if ((fp->uf_flags & FC_DEAD) == 0
+                   && (regmatch == NULL
+                       ? !message_filtered(fp->uf_name)
+                           && !func_name_refcount(fp->uf_name)
+                       : !isdigit(*fp->uf_name)
+                           && vim_regexec(regmatch, fp->uf_name, 0)))
+           {
+               list_func_head(fp, FALSE);
+               if (used != func_hashtab.ht_used
+                       || ht_array != func_hashtab.ht_array)
+               {
+                   emsg(_("E454: function list was modified"));
+                   return;
+               }
+           }
+       }
+    }
+}
+
 /*
  * ":function" also supporting nested ":def".
  * Returns a pointer to the function or NULL if no function defined.
@@ -2407,7 +2445,6 @@ def_function(exarg_T *eap, char_u *name_arg)
     funcdict_T fudi;
     static int func_nr = 0;        // number for nameless function
     int                paren;
-    int                todo;
     hashitem_T *hi;
     int                do_concat = TRUE;
     linenr_T   sourcing_lnum_off;
@@ -2428,22 +2465,7 @@ def_function(exarg_T *eap, char_u *name_arg)
     if (ends_excmd2(eap->cmd, eap->arg))
     {
        if (!eap->skip)
-       {
-           todo = (int)func_hashtab.ht_used;
-           for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi)
-           {
-               if (!HASHITEM_EMPTY(hi))
-               {
-                   --todo;
-                   fp = HI2UF(hi);
-                   if ((fp->uf_flags & FC_DEAD)
-                                             || message_filtered(fp->uf_name))
-                       continue;
-                   if (!func_name_refcount(fp->uf_name))
-                       list_func_head(fp, FALSE);
-               }
-           }
-       }
+           list_functions(NULL);
        eap->nextcmd = check_nextcmd(eap->arg);
        return NULL;
     }
@@ -2465,20 +2487,7 @@ def_function(exarg_T *eap, char_u *name_arg)
            if (regmatch.regprog != NULL)
            {
                regmatch.rm_ic = p_ic;
-
-               todo = (int)func_hashtab.ht_used;
-               for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi)
-               {
-                   if (!HASHITEM_EMPTY(hi))
-                   {
-                       --todo;
-                       fp = HI2UF(hi);
-                       if ((fp->uf_flags & FC_DEAD) == 0
-                               && !isdigit(*fp->uf_name)
-                               && vim_regexec(&regmatch, fp->uf_name, 0))
-                           list_func_head(fp, FALSE);
-                   }
-               }
+               list_functions(&regmatch);
                vim_regfree(regmatch.regprog);
            }
        }
index 3fccc702b5fa624e6c9fee37cdc386db8d30e971..20c0e0dec4f0ca942586499aab7d4576d92747b3 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    908,
 /**/
     907,
 /**/