mirror of
https://github.com/vim/vim.git
synced 2025-09-26 04:04:07 -04:00
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 #11440)
This commit is contained in:
@@ -3339,3 +3339,5 @@ EXTERN char e_cannot_change_mappings_while_listing[]
|
|||||||
EXTERN char e_cannot_change_menus_while_listing[]
|
EXTERN char e_cannot_change_menus_while_listing[]
|
||||||
INIT(= N_("E1310: Cannot change menus while listing"));
|
INIT(= N_("E1310: Cannot change menus while listing"));
|
||||||
#endif
|
#endif
|
||||||
|
EXTERN char e_cannot_change_user_commands_while_listing[]
|
||||||
|
INIT(= N_("E1311: Cannot change user commands while listing"));
|
||||||
|
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
import './vim9.vim' as v9
|
import './vim9.vim' as v9
|
||||||
|
|
||||||
|
source check.vim
|
||||||
|
source screendump.vim
|
||||||
|
|
||||||
" Test for <mods> in user defined commands
|
" Test for <mods> in user defined commands
|
||||||
function Test_cmdmods()
|
function Test_cmdmods()
|
||||||
let g:mods = ''
|
let g:mods = ''
|
||||||
@@ -373,6 +376,14 @@ func Test_CmdCompletion()
|
|||||||
call feedkeys(":com MyCmd chist\<Tab>\<C-B>\"\<CR>", 'tx')
|
call feedkeys(":com MyCmd chist\<Tab>\<C-B>\"\<CR>", 'tx')
|
||||||
call assert_equal("\"com MyCmd chistory", @:)
|
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! DoCmd1 :
|
||||||
command! DoCmd2 :
|
command! DoCmd2 :
|
||||||
call feedkeys(":com \<C-A>\<C-B>\"\<CR>", 'tx')
|
call feedkeys(":com \<C-A>\<C-B>\"\<CR>", 'tx')
|
||||||
@@ -716,6 +727,7 @@ func Test_usercmd_with_block()
|
|||||||
echo 'hello'
|
echo 'hello'
|
||||||
END
|
END
|
||||||
call v9.CheckScriptFailure(lines, 'E1026:')
|
call v9.CheckScriptFailure(lines, 'E1026:')
|
||||||
|
delcommand DoesNotEnd
|
||||||
|
|
||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
command HelloThere {
|
command HelloThere {
|
||||||
@@ -754,6 +766,7 @@ func Test_usercmd_with_block()
|
|||||||
BadCommand
|
BadCommand
|
||||||
END
|
END
|
||||||
call v9.CheckScriptFailure(lines, 'E1128:')
|
call v9.CheckScriptFailure(lines, 'E1128:')
|
||||||
|
delcommand BadCommand
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_delcommand_buffer()
|
func Test_delcommand_buffer()
|
||||||
@@ -817,7 +830,7 @@ func Test_recursive_define()
|
|||||||
call DefCmd('Command')
|
call DefCmd('Command')
|
||||||
|
|
||||||
let name = 'Command'
|
let name = 'Command'
|
||||||
while len(name) < 30
|
while len(name) <= 30
|
||||||
exe 'delcommand ' .. name
|
exe 'delcommand ' .. name
|
||||||
let name ..= 'x'
|
let name ..= 'x'
|
||||||
endwhile
|
endwhile
|
||||||
@@ -882,5 +895,30 @@ func Test_block_declaration_legacy_script()
|
|||||||
delcommand Rename
|
delcommand Rename
|
||||||
endfunc
|
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
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
@@ -31,6 +31,9 @@ typedef struct ucmd
|
|||||||
// List of all user commands.
|
// List of all user commands.
|
||||||
static garray_T ucmds = {0, 0, sizeof(ucmd_T), 4, NULL};
|
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(i) (&((ucmd_T *)(ucmds.ga_data))[i])
|
||||||
#define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->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;
|
long a;
|
||||||
garray_T *gap;
|
garray_T *gap;
|
||||||
|
|
||||||
|
// don't allow for adding or removing user commands here
|
||||||
|
++ucmd_locked;
|
||||||
|
|
||||||
// In cmdwin, the alternative buffer should be used.
|
// In cmdwin, the alternative buffer should be used.
|
||||||
gap = &prevwin_curwin()->w_buffer->b_ucmds;
|
gap = &prevwin_curwin()->w_buffer->b_ucmds;
|
||||||
for (;;)
|
for (;;)
|
||||||
@@ -656,6 +662,8 @@ uc_list(char_u *name, size_t name_len)
|
|||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
msg(_("No user-defined commands found"));
|
msg(_("No user-defined commands found"));
|
||||||
|
|
||||||
|
--ucmd_locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
@@ -1222,6 +1230,21 @@ ex_comclear(exarg_T *eap UNUSED)
|
|||||||
uc_clear(&curbuf->b_ucmds);
|
uc_clear(&curbuf->b_ucmds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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".
|
* Clear all user commands for "gap".
|
||||||
*/
|
*/
|
||||||
@@ -1231,6 +1254,9 @@ uc_clear(garray_T *gap)
|
|||||||
int i;
|
int i;
|
||||||
ucmd_T *cmd;
|
ucmd_T *cmd;
|
||||||
|
|
||||||
|
if (is_ucmd_locked())
|
||||||
|
return;
|
||||||
|
|
||||||
for (i = 0; i < gap->ga_len; ++i)
|
for (i = 0; i < gap->ga_len; ++i)
|
||||||
{
|
{
|
||||||
cmd = USER_CMD_GA(gap, i);
|
cmd = USER_CMD_GA(gap, i);
|
||||||
@@ -1285,6 +1311,9 @@ ex_delcommand(exarg_T *eap)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_ucmd_locked())
|
||||||
|
return;
|
||||||
|
|
||||||
vim_free(cmd->uc_name);
|
vim_free(cmd->uc_name);
|
||||||
vim_free(cmd->uc_rep);
|
vim_free(cmd->uc_rep);
|
||||||
# if defined(FEAT_EVAL)
|
# if defined(FEAT_EVAL)
|
||||||
|
@@ -695,6 +695,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
877,
|
||||||
/**/
|
/**/
|
||||||
876,
|
876,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user