From: Bram Moolenaar Date: Sun, 13 Nov 2022 20:43:19 +0000 (+0000) Subject: patch 9.0.0873: using freed memory when executing mapclear at more prompt X-Git-Tag: v9.0.0873 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bf533e4e88ebac8b8fec6d3e12dadc476ce9a1df;p=vim patch 9.0.0873: using freed memory when executing mapclear at more prompt Problem: Using freed memory when executing mapclear at the more prompt. Solution: Do not clear mappings while listing them. (closes #11438) --- diff --git a/src/errors.h b/src/errors.h index ea33b0475..88db28510 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3333,3 +3333,5 @@ EXTERN char e_argument_nr_trying_to_modify_const_str[] EXTERN char e_cannot_resize_window_in_another_tab_page[] INIT(= N_("E1308: Cannot resize a window in another tab page")); #endif +EXTERN char e_cannot_change_mappings_while_listing[] + INIT(= N_("E1309: Cannot change mappings while listing")); diff --git a/src/map.c b/src/map.c index fbbf9dcaf..4dec9e76e 100644 --- a/src/map.c +++ b/src/map.c @@ -24,6 +24,10 @@ static mapblock_T *first_abbr = NULL; // first entry in abbrlist static mapblock_T *(maphash[256]); static int maphash_valid = FALSE; +// When non-zero then no mappings can be added or removed. Prevents mappings +// to change while listing them. +static int map_locked = 0; + /* * Make a hash value for a mapping. * "mode" is the lower 4 bits of the State for the mapping. @@ -150,11 +154,15 @@ showmap( if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)) return; + // Prevent mappings to be cleared while at the more prompt. + // Must jump to "theend" instead of returning. + ++map_locked; + if (msg_didout || msg_silent != 0) { msg_putchar('\n'); if (got_int) // 'q' typed at MORE prompt - return; + goto theend; } mapchars = map_mode_to_chars(mp->m_mode); @@ -200,6 +208,9 @@ showmap( #endif msg_clr_eos(); out_flush(); // show one line at a time + +theend: + --map_locked; } static int @@ -298,6 +309,9 @@ list_mappings( int mode, int *did_local) { + // Prevent mappings to be cleared while at the more prompt. + ++map_locked; + if (p_verbose > 0 && keyround == 1 && seenModifyOtherKeys) msg_puts(_("Seen modifyOtherKeys: true")); @@ -337,6 +351,8 @@ list_mappings( } } } + + --map_locked; } /* @@ -954,6 +970,21 @@ map_clear( map_clear_mode(curbuf, mode, local, abbr); } +/* + * If "map_locked" is set then give an error and return TRUE. + * Otherwise return FALSE. + */ + static int +is_map_locked(void) +{ + if (map_locked > 0) + { + emsg(_(e_cannot_change_mappings_while_listing)); + return TRUE; + } + return FALSE; +} + /* * Clear all mappings in "mode". */ @@ -968,6 +999,9 @@ map_clear_mode( int hash; int new_hash; + if (is_map_locked()) + return; + validate_maphash(); for (hash = 0; hash < 256; ++hash) diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim index f8e5a317f..4d5b202d4 100644 --- a/src/testdir/test_mapping.vim +++ b/src/testdir/test_mapping.vim @@ -1774,5 +1774,29 @@ func Test_using_past_typeahead() nunmap :00 endfunc +func Test_mapclear_while_listing() + CheckRunVimInTerminal + + let lines =<< trim END + set nocompatible + mapclear + for i in range(1, 999) + exe 'map ' .. 'foo' .. i .. ' bar' + endfor + au CmdlineLeave : call timer_start(0, {-> execute('mapclear')}) + END + call writefile(lines, 'Xmapclear', 'D') + let buf = RunVimInTerminal('-S Xmapclear', {'rows': 10}) + + " this was using freed memory + call term_sendkeys(buf, ":map\") + call TermWait(buf, 50) + call term_sendkeys(buf, "G") + call TermWait(buf, 50) + call term_sendkeys(buf, "\") + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index e8a1fa823..5b4e34ee4 100644 --- a/src/version.c +++ b/src/version.c @@ -695,6 +695,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 873, /**/ 872, /**/