Patch 9.0.0875
Problem: Using freed memory when executing delfunc at the more prompt.
Solution: Check function list not changed in another place. (closes #11437)
Files: src/userfunc.c, src/testdir/test_functions.vim
*** ../vim-9.0.0874/src/userfunc.c 2022-11-02 13:30:37.542314565 +0000
--- src/userfunc.c 2022-11-13 22:10:33.952835694 +0000
***************
*** 3793,3806 ****
}
/*
* List the head of the function: "function name(arg1, arg2)".
*/
! static void
list_func_head(ufunc_T *fp, int indent)
{
int j;
msg_start();
if (indent)
msg_puts(" ");
if (fp->uf_def_status != UF_NOT_COMPILED)
--- 3793,3827 ----
}
/*
+ * 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 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,3882 ****
--- 3898,3905 ----
msg_clr_eos();
if (p_verbose > 0)
last_set_msg(fp->uf_script_ctx);
+
+ return OK;
}
/*
***************
*** 4315,4321 ****
void
list_functions(regmatch_T *regmatch)
{
! int changed = func_hashtab.ht_changed;
long_u todo = func_hashtab.ht_used;
hashitem_T *hi;
--- 4338,4344 ----
void
list_functions(regmatch_T *regmatch)
{
! int prev_ht_changed = func_hashtab.ht_changed;
long_u todo = func_hashtab.ht_used;
hashitem_T *hi;
***************
*** 4333,4344 ****
: !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));
return;
- }
}
}
}
--- 4356,4365 ----
: !isdigit(*fp->uf_name)
&& vim_regexec(regmatch, fp->uf_name, 0)))
{
! if (list_func_head(fp, FALSE) == FAIL)
! return;
! if (function_list_modified(prev_ht_changed))
return;
}
}
}
***************
*** 4542,4569 ****
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)
{
! msg_putchar('\n');
! if (fp->uf_def_status != UF_NOT_COMPILED)
! msg_puts(" enddef");
! else
! msg_puts(" endfunction");
}
}
else
--- 4563,4601 ----
if (fp != NULL)
{
! // 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)
{
! 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
*** ../vim-9.0.0874/src/testdir/test_functions.vim 2022-11-12 16:07:01.777944369 +0000
--- src/testdir/test_functions.vim 2022-11-13 22:12:41.904938913 +0000
***************
*** 3026,3029 ****
--- 3026,3056 ----
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
*** ../vim-9.0.0874/src/version.c 2022-11-13 21:09:58.598211225 +0000
--- src/version.c 2022-11-13 21:48:32.404033363 +0000
***************
*** 697,698 ****
--- 697,700 ----
{ /* Add new patch number below this line */
+ /**/
+ 875,
/**/
--
Be thankful to be in a traffic jam, because it means you own a car.
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///