Mercurial > vim
changeset 31085:8c10a0b22015 v9.0.0877
patch 9.0.0877: using freed memory with :comclear while listing commands
Commit: https://github.com/vim/vim/commit/cf2594fbf34d9a6776bd9d33f845cb8ceb1e1cd0
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Nov 13 23:30:06 2022 +0000
patch 9.0.0877: using freed memory with :comclear while listing commands
Problem: Using freed memory with :comclear while listing commands.
Solution: Bail out when the command list has changed. (closes https://github.com/vim/vim/issues/11440)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Mon, 14 Nov 2022 00:45:03 +0100 |
parents | cb82e8d640a8 |
children | 47d825028c38 |
files | src/errors.h src/testdir/test_usercommands.vim src/usercmd.c src/version.c |
diffstat | 4 files changed, 72 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/src/errors.h +++ b/src/errors.h @@ -3339,3 +3339,5 @@ EXTERN char e_cannot_change_mappings_whi EXTERN char e_cannot_change_menus_while_listing[] INIT(= N_("E1310: Cannot change menus while listing")); #endif +EXTERN char e_cannot_change_user_commands_while_listing[] + INIT(= N_("E1311: Cannot change user commands while listing"));
--- a/src/testdir/test_usercommands.vim +++ b/src/testdir/test_usercommands.vim @@ -2,6 +2,9 @@ import './vim9.vim' as v9 +source check.vim +source screendump.vim + " Test for <mods> in user defined commands function Test_cmdmods() let g:mods = '' @@ -373,6 +376,14 @@ func Test_CmdCompletion() call feedkeys(":com MyCmd chist\<Tab>\<C-B>\"\<CR>", 'tx') call assert_equal("\"com MyCmd chistory", @:) + " delete the Check commands to avoid them showing up + call feedkeys(":com Check\<C-A>\<C-B>\"\<CR>", 'tx') + let cmds = substitute(@:, '"com ', '', '')->split() + for cmd in cmds + exe 'delcommand ' .. cmd + endfor + delcommand MissingFeature + command! DoCmd1 : command! DoCmd2 : call feedkeys(":com \<C-A>\<C-B>\"\<CR>", 'tx') @@ -716,6 +727,7 @@ func Test_usercmd_with_block() echo 'hello' END call v9.CheckScriptFailure(lines, 'E1026:') + delcommand DoesNotEnd let lines =<< trim END command HelloThere { @@ -754,6 +766,7 @@ func Test_usercmd_with_block() BadCommand END call v9.CheckScriptFailure(lines, 'E1128:') + delcommand BadCommand endfunc func Test_delcommand_buffer() @@ -817,7 +830,7 @@ func Test_recursive_define() call DefCmd('Command') let name = 'Command' - while len(name) < 30 + while len(name) <= 30 exe 'delcommand ' .. name let name ..= 'x' endwhile @@ -882,5 +895,30 @@ func Test_block_declaration_legacy_scrip delcommand Rename endfunc +func Test_comclear_while_listing() + call CheckRunVimInTerminal() + + let lines =<< trim END + set nocompatible + comclear + for i in range(1, 999) + exe 'command ' .. 'Foo' .. i .. ' bar' + endfor + au CmdlineLeave : call timer_start(0, {-> execute('comclear')}) + END + call writefile(lines, 'Xcommandclear', 'D') + let buf = RunVimInTerminal('-S Xcommandclear', {'rows': 10}) + + " this was using freed memory + call term_sendkeys(buf, ":command\<CR>") + call TermWait(buf, 50) + call term_sendkeys(buf, "j") + call TermWait(buf, 50) + call term_sendkeys(buf, "G") + call term_sendkeys(buf, "\<CR>") + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab
--- a/src/usercmd.c +++ b/src/usercmd.c @@ -31,6 +31,9 @@ typedef struct ucmd // List of all user commands. static garray_T ucmds = {0, 0, sizeof(ucmd_T), 4, NULL}; +// When non-zero it is not allowed to add or remove user commands +static int ucmd_locked = 0; + #define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i]) #define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i]) @@ -499,6 +502,9 @@ uc_list(char_u *name, size_t name_len) long a; garray_T *gap; + // don't allow for adding or removing user commands here + ++ucmd_locked; + // In cmdwin, the alternative buffer should be used. gap = &prevwin_curwin()->w_buffer->b_ucmds; for (;;) @@ -656,6 +662,8 @@ uc_list(char_u *name, size_t name_len) if (!found) msg(_("No user-defined commands found")); + + --ucmd_locked; } char * @@ -1223,6 +1231,21 @@ ex_comclear(exarg_T *eap UNUSED) } /* + * If ucmd_locked is set give an error and return TRUE. + * Otherwise return FALSE. + */ + static int +is_ucmd_locked(void) +{ + if (ucmd_locked > 0) + { + emsg(_(e_cannot_change_user_commands_while_listing)); + return TRUE; + } + return FALSE; +} + +/* * Clear all user commands for "gap". */ void @@ -1231,6 +1254,9 @@ uc_clear(garray_T *gap) int i; ucmd_T *cmd; + if (is_ucmd_locked()) + return; + for (i = 0; i < gap->ga_len; ++i) { cmd = USER_CMD_GA(gap, i); @@ -1285,6 +1311,9 @@ ex_delcommand(exarg_T *eap) return; } + if (is_ucmd_locked()) + return; + vim_free(cmd->uc_name); vim_free(cmd->uc_rep); # if defined(FEAT_EVAL)