patch 8.2.4139: using freed memory in expression abbreviation
Problem: Using freed memory if an expression abbreviation deletes the
abbreviation.
Solution: Do not access the pointer after evaluating the expression.
diff --git a/src/map.c b/src/map.c
index 6ad938d..b188e43 100644
--- a/src/map.c
+++ b/src/map.c
@@ -226,7 +226,7 @@
#endif
int simplified)
{
- mapblock_T *mp = ALLOC_ONE(mapblock_T);
+ mapblock_T *mp = ALLOC_CLEAR_ONE(mapblock_T);
if (mp == NULL)
return FAIL;
@@ -1515,6 +1515,12 @@
}
if (mp != NULL)
{
+ int noremap;
+ int silent;
+#ifdef FEAT_EVAL
+ int expr;
+#endif
+
// Found a match:
// Insert the rest of the abbreviation in typebuf.tb_buf[].
// This goes from end to start.
@@ -1567,8 +1573,14 @@
// insert the last typed char
(void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent);
}
+
+ // copy values here, calling eval_map_expr() may make "mp" invalid!
+ noremap = mp->m_noremap;
+ silent = mp->m_silent;
#ifdef FEAT_EVAL
- if (mp->m_expr)
+ expr = mp->m_expr;
+
+ if (expr)
s = eval_map_expr(mp, c);
else
#endif
@@ -1576,11 +1588,11 @@
if (s != NULL)
{
// insert the to string
- (void)ins_typebuf(s, mp->m_noremap, 0, TRUE, mp->m_silent);
+ (void)ins_typebuf(s, noremap, 0, TRUE, silent);
// no abbrev. for these chars
typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1;
#ifdef FEAT_EVAL
- if (mp->m_expr)
+ if (expr)
vim_free(s);
#endif
}
@@ -1590,7 +1602,7 @@
if (has_mbyte)
len = clen; // Delete characters instead of bytes
while (len-- > 0) // delete the from string
- (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent);
+ (void)ins_typebuf(tb, 1, 0, TRUE, silent);
return TRUE;
}
}
@@ -1601,6 +1613,7 @@
/*
* Evaluate the RHS of a mapping or abbreviations and take care of escaping
* special characters.
+ * Careful: after this "mp" will be invalid if the mapping was deleted.
*/
char_u *
eval_map_expr(
diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim
index b170b58..a556362 100644
--- a/src/testdir/test_mapping.vim
+++ b/src/testdir/test_mapping.vim
@@ -704,6 +704,11 @@
mapclear
endfunc
+func GetAbbrText()
+ unabbr hola
+ return 'hello'
+endfunc
+
" Test for <expr> in abbreviation
func Test_expr_abbr()
new
@@ -719,7 +724,14 @@
call assert_equal('', getline(1))
unabbr <expr> hte
- close!
+ " evaluating the expression deletes the abbreviation
+ abbr <expr> hola GetAbbrText()
+ call assert_equal('GetAbbrText()', maparg('hola', 'i', '1'))
+ call feedkeys("ahola \<Esc>", 'xt')
+ call assert_equal('hello ', getline('.'))
+ call assert_equal('', maparg('hola', 'i', '1'))
+
+ bwipe!
endfunc
" Test for storing mappings in different modes in a vimrc file
diff --git a/src/version.c b/src/version.c
index 6198d41..07d5b81 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 4139,
+/**/
4138,
/**/
4137,