# HG changeset patch # User Bram Moolenaar # Date 1668377703 -3600 # Node ID c12069d2871993866c677001b13ad5093d33cfd4 # Parent df91dcf490f23d0ec46047803cfcd02ae18a947c patch 9.0.0875: using freed memory when executing delfunc at more prompt Commit: https://github.com/vim/vim/commit/398a26f7fcd58fbc6e2329f892edbb7479a971bb Author: Bram Moolenaar Date: Sun Nov 13 22:13:33 2022 +0000 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 https://github.com/vim/vim/issues/11437) diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim --- 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 --- 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 *na if (fp != NULL) { - list_func_head(fp, TRUE); - for (j = 0; j < fp->uf_lines.ga_len && !got_int; ++j) + // 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) { - 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"); + 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 --- 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,