summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-11-13 22:13:33 +0000
committerBram Moolenaar <Bram@vim.org>2022-11-13 22:13:33 +0000
commit398a26f7fcd58fbc6e2329f892edbb7479a971bb (patch)
tree4ddec1eaef1a6ad926ad32fe93b2a1d28bfc5cac
parent920d311480114274e4d73156edf4b49ba0b712dd (diff)
patch 9.0.0875: using freed memory when executing delfunc at more promptv9.0.0875
Problem: Using freed memory when executing delfunc at the more prompt. Solution: Check function list not changed in another place. (closes #11437)
-rw-r--r--src/testdir/test_functions.vim27
-rw-r--r--src/userfunc.c88
-rw-r--r--src/version.c2
3 files changed, 89 insertions, 28 deletions
diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim
index d0508ce7d1..cb072215d9 100644
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -3026,4 +3026,31 @@ func Test_virtcol()
bwipe!
endfunc
+func Test_delfunc_while_listing()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set nocompatible
+ for i in range(1, 999)
+ exe 'func ' .. 'MyFunc' .. i .. '()'
+ endfunc
+ endfor
+ au CmdlineLeave : call timer_start(0, {-> execute('delfunc MyFunc622')})
+ END
+ call writefile(lines, 'Xfunctionclear', 'D')
+ let buf = RunVimInTerminal('-S Xfunctionclear', {'rows': 12})
+
+ " This was using freed memory. The height of the terminal must be so that
+ " the next function to be listed with "j" is the one that is deleted in the
+ " timer callback, tricky!
+ call term_sendkeys(buf, ":func /MyFunc\<CR>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "j")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "\<CR>")
+
+ call StopVimInTerminal(buf)
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/userfunc.c b/src/userfunc.c
index 64bc1f6fd0..ff4cae7322 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -3793,14 +3793,35 @@ printable_func_name(ufunc_T *fp)
}
/*
+ * When "prev_ht_changed" does not equal "ht_changed" give an error and return
+ * TRUE. Otherwise return FALSE.
+ */
+ static int
+function_list_modified(int prev_ht_changed)
+{
+ if (prev_ht_changed != func_hashtab.ht_changed)
+ {
+ emsg(_(e_function_list_was_modified));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
* List the head of the function: "function name(arg1, arg2)".
*/
- static void
+ static int
list_func_head(ufunc_T *fp, int indent)
{
+ int prev_ht_changed = func_hashtab.ht_changed;
int j;
msg_start();
+
+ // a timer at the more prompt may have deleted the function
+ if (function_list_modified(prev_ht_changed))
+ return FAIL;
+
if (indent)
msg_puts(" ");
if (fp->uf_def_status != UF_NOT_COMPILED)
@@ -3877,6 +3898,8 @@ list_func_head(ufunc_T *fp, int indent)
msg_clr_eos();
if (p_verbose > 0)
last_set_msg(fp->uf_script_ctx);
+
+ return OK;
}
/*
@@ -4315,7 +4338,7 @@ save_function_name(
void
list_functions(regmatch_T *regmatch)
{
- int changed = func_hashtab.ht_changed;
+ int prev_ht_changed = func_hashtab.ht_changed;
long_u todo = func_hashtab.ht_used;
hashitem_T *hi;
@@ -4333,12 +4356,10 @@ list_functions(regmatch_T *regmatch)
: !isdigit(*fp->uf_name)
&& vim_regexec(regmatch, fp->uf_name, 0)))
{
- list_func_head(fp, FALSE);
- if (changed != func_hashtab.ht_changed)
- {
- emsg(_(e_function_list_was_modified));
+ if (list_func_head(fp, FALSE) == FAIL)
+ return;
+ if (function_list_modified(prev_ht_changed))
return;
- }
}
}
}
@@ -4542,28 +4563,39 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
if (fp != NULL)
{
- list_func_head(fp, TRUE);
- for (j = 0; j < fp->uf_lines.ga_len && !got_int; ++j)
- {
- if (FUNCLINE(fp, j) == NULL)
- continue;
- msg_putchar('\n');
- msg_outnum((long)(j + 1));
- if (j < 9)
- msg_putchar(' ');
- if (j < 99)
- msg_putchar(' ');
- msg_prt_line(FUNCLINE(fp, j), FALSE);
- out_flush(); // show a line at a time
- ui_breakcheck();
- }
- if (!got_int)
+ // Check no function was added or removed from a timer, e.g. at
+ // the more prompt. "fp" may then be invalid.
+ int prev_ht_changed = func_hashtab.ht_changed;
+
+ if (list_func_head(fp, TRUE) == OK)
{
- msg_putchar('\n');
- if (fp->uf_def_status != UF_NOT_COMPILED)
- msg_puts(" enddef");
- else
- msg_puts(" endfunction");
+ for (j = 0; j < fp->uf_lines.ga_len && !got_int; ++j)
+ {
+ if (FUNCLINE(fp, j) == NULL)
+ continue;
+ msg_putchar('\n');
+ msg_outnum((long)(j + 1));
+ if (j < 9)
+ msg_putchar(' ');
+ if (j < 99)
+ msg_putchar(' ');
+ if (function_list_modified(prev_ht_changed))
+ break;
+ msg_prt_line(FUNCLINE(fp, j), FALSE);
+ out_flush(); // show a line at a time
+ ui_breakcheck();
+ }
+ if (!got_int)
+ {
+ msg_putchar('\n');
+ if (!function_list_modified(prev_ht_changed))
+ {
+ if (fp->uf_def_status != UF_NOT_COMPILED)
+ msg_puts(" enddef");
+ else
+ msg_puts(" endfunction");
+ }
+ }
}
}
else
diff --git a/src/version.c b/src/version.c
index c6e074a631..9a459eac46 100644
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 875,
+/**/
874,
/**/
873,