From 398a26f7fcd58fbc6e2329f892edbb7479a971bb Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 13 Nov 2022 22:13:33 +0000 Subject: patch 9.0.0875: using freed memory when executing delfunc at more prompt Problem: Using freed memory when executing delfunc at the more prompt. Solution: Check function list not changed in another place. (closes #11437) --- src/testdir/test_functions.vim | 27 +++++++++++++ src/userfunc.c | 88 ++++++++++++++++++++++++++++-------------- src/version.c | 2 + 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\") + call TermWait(buf, 50) + call term_sendkeys(buf, "j") + call TermWait(buf, 50) + call term_sendkeys(buf, "\") + + 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 @@ -3792,15 +3792,36 @@ printable_func_name(ufunc_T *fp) return fp->uf_name_exp != NULL ? fp->uf_name_exp : fp->uf_name; } +/* + * 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 @@ -695,6 +695,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 875, /**/ 874, /**/ -- cgit v1.2.3