diff options
author | Bram Moolenaar <Bram@vim.org> | 2022-11-13 23:30:06 +0000 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2022-11-13 23:30:06 +0000 |
commit | cf2594fbf34d9a6776bd9d33f845cb8ceb1e1cd0 (patch) | |
tree | ba645afa4188118a25f5d183a60740a710f96b1d /src | |
parent | 68353e5270fca45daffee5b5f882853cdfa40f76 (diff) |
patch 9.0.0877: using freed memory with :comclear while listing commandsv9.0.0877
Problem: Using freed memory with :comclear while listing commands.
Solution: Bail out when the command list has changed. (closes #11440)
Diffstat (limited to 'src')
-rw-r--r-- | src/errors.h | 2 | ||||
-rw-r--r-- | src/testdir/test_usercommands.vim | 40 | ||||
-rw-r--r-- | src/usercmd.c | 29 | ||||
-rw-r--r-- | src/version.c | 2 |
4 files changed, 72 insertions, 1 deletions
diff --git a/src/errors.h b/src/errors.h index 99247b6d19..0f54eba265 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3339,3 +3339,5 @@ EXTERN char e_cannot_change_mappings_while_listing[] 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")); diff --git a/src/testdir/test_usercommands.vim b/src/testdir/test_usercommands.vim index f8cc1f53a1..57953ced8a 100644 --- 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_script() 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 diff --git a/src/usercmd.c b/src/usercmd.c index 9f16680f58..d8783321d8 100644 --- 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) diff --git a/src/version.c b/src/version.c index 868118d0c7..a764712188 100644 --- 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 */ /**/ + 877, +/**/ 876, /**/ 875, |