summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-11-13 21:10:02 +0000
committerBram Moolenaar <Bram@vim.org>2022-11-13 21:10:02 +0000
commit920d311480114274e4d73156edf4b49ba0b712dd (patch)
tree9a1026423d747cf21904a03a6adde768ca8e1ed4
parentbf533e4e88ebac8b8fec6d3e12dadc476ce9a1df (diff)
patch 9.0.0874: using freed memory when executing unmenu at more promptv9.0.0874
Problem: Using freed memory when executing unmenu at the more prompt. Solution: Do not clear menus while listing them. (closes #11439)
-rw-r--r--src/errors.h4
-rw-r--r--src/menu.c34
-rw-r--r--src/testdir/test_menu.vim26
-rw-r--r--src/version.c2
4 files changed, 63 insertions, 3 deletions
diff --git a/src/errors.h b/src/errors.h
index 88db28510f..99247b6d19 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3335,3 +3335,7 @@ EXTERN char e_cannot_resize_window_in_another_tab_page[]
#endif
EXTERN char e_cannot_change_mappings_while_listing[]
INIT(= N_("E1309: Cannot change mappings while listing"));
+#if defined(FEAT_MENU)
+EXTERN char e_cannot_change_menus_while_listing[]
+ INIT(= N_("E1310: Cannot change menus while listing"));
+#endif
diff --git a/src/menu.c b/src/menu.c
index 432fbe9727..9209bf735c 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -46,6 +46,10 @@ static int s_tearoffs = FALSE;
static int menu_is_hidden(char_u *name);
static int menu_is_tearoff(char_u *name);
+// When non-zero no menu must be added or cleared. Prevents the list of menus
+// changing while listing them.
+static int menus_locked = 0;
+
#if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR)
static char_u *menu_skip_part(char_u *p);
#endif
@@ -99,6 +103,21 @@ get_root_menu(char_u *name)
}
/*
+ * If "menus_locked" is set then give an error and return TRUE.
+ * Otherwise return FALSE.
+ */
+ static int
+is_menus_locked(void)
+{
+ if (menus_locked > 0)
+ {
+ emsg(_(e_cannot_change_menus_while_listing));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
* Do the :menu command and relatives.
*/
void
@@ -329,6 +348,9 @@ ex_menu(
}
else if (unmenu)
{
+ if (is_menus_locked())
+ goto theend;
+
/*
* Delete menu(s).
*/
@@ -357,6 +379,9 @@ ex_menu(
}
else
{
+ if (is_menus_locked())
+ goto theend;
+
/*
* Add menu(s).
* Replace special key codes.
@@ -1147,11 +1172,14 @@ show_menus(char_u *path_name, int modes)
}
vim_free(path_name);
- // Now we have found the matching menu, and we list the mappings
- // Highlight title
- msg_puts_title(_("\n--- Menus ---"));
+ // make sure the list of menus doesn't change while listing them
+ ++menus_locked;
+ // list the matching menu mappings
+ msg_puts_title(_("\n--- Menus ---"));
show_menus_recursive(parent, modes, 0);
+
+ --menus_locked;
return OK;
}
diff --git a/src/testdir/test_menu.vim b/src/testdir/test_menu.vim
index 95d2b01a5c..65bdac2373 100644
--- a/src/testdir/test_menu.vim
+++ b/src/testdir/test_menu.vim
@@ -3,6 +3,8 @@
source check.vim
CheckFeature menu
+source screendump.vim
+
func Test_load_menu()
try
source $VIMRUNTIME/menu.vim
@@ -568,4 +570,28 @@ func Test_only_modifier()
tunmenu a.b
endfunc
+func Test_mapclear_while_listing()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set nocompatible
+ unmenu *
+ for i in range(1, 999)
+ exe 'menu ' .. 'foo.' .. i .. ' bar'
+ endfor
+ au CmdlineLeave : call timer_start(0, {-> execute('unmenu *')})
+ END
+ call writefile(lines, 'Xmenuclear', 'D')
+ let buf = RunVimInTerminal('-S Xmenuclear', {'rows': 10})
+
+ " this was using freed memory
+ call term_sendkeys(buf, ":menu\<CR>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "G")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "\<CR>")
+
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 5b4e34ee46..c6e074a631 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 */
/**/
+ 874,
+/**/
873,
/**/
872,