To: vim_dev@googlegroups.com Subject: Patch 9.0.0875 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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\") + 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 *** ../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 -- Bram@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 ///