summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-06-05 21:06:10 +0200
committerBram Moolenaar <Bram@vim.org>2020-06-05 21:06:10 +0200
commit3fffa97159a427067b60c80ed4645e168cc5c4bd (patch)
treef0fb9f2fd843478461584c9f6444c8c15d0ae1c5
parent07188fc5ef2366a3b1952e8686a4031b44152d59 (diff)
patch 8.2.0908: crash when changing the function table while listing itv8.2.0908
Problem: Crash when changing the function table while listing it. Solution: Bail out when the function table changes. (closes #6209)
-rw-r--r--src/testdir/test_timers.vim25
-rw-r--r--src/userfunc.c71
-rw-r--r--src/version.c2
3 files changed, 67 insertions, 31 deletions
diff --git a/src/testdir/test_timers.vim b/src/testdir/test_timers.vim
index 639f5c2d19..829e94b5de 100644
--- a/src/testdir/test_timers.vim
+++ b/src/testdir/test_timers.vim
@@ -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
diff --git a/src/userfunc.c b/src/userfunc.c
index 469dcc1a53..b495769a95 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -2373,6 +2373,44 @@ untrans_function_name(char_u *name)
}
/*
+ * 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);
}
}
diff --git a/src/version.c b/src/version.c
index 3fccc702b5..20c0e0dec4 100644
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 908,
+/**/
907,
/**/
906,