changeset 27221:165a799b4129 v8.2.4139

patch 8.2.4139: using freed memory in expression abbreviation Commit: https://github.com/vim/vim/commit/94075b2b0e8e3b75334799d2c082497fbf85ffa1 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Jan 18 20:30:39 2022 +0000 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.
author Bram Moolenaar <Bram@vim.org>
date Tue, 18 Jan 2022 21:45:02 +0100
parents 3a101ad74200
children 20f098f8b3e6
files src/map.c src/testdir/test_mapping.vim src/version.c
diffstat 3 files changed, 33 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/map.c
+++ b/src/map.c
@@ -226,7 +226,7 @@ map_add(
 #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 @@ check_abbr(
 	}
 	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 @@ check_abbr(
 					// 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 @@ check_abbr(
 	    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 @@ check_abbr(
 	    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 @@ check_abbr(
 /*
  * 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(
--- a/src/testdir/test_mapping.vim
+++ b/src/testdir/test_mapping.vim
@@ -704,6 +704,11 @@ func Test_mapcomplete()
   mapclear
 endfunc
 
+func GetAbbrText()
+  unabbr hola
+  return 'hello'
+endfunc
+
 " Test for <expr> in abbreviation
 func Test_expr_abbr()
   new
@@ -719,7 +724,14 @@ func Test_expr_abbr()
   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
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4139,
+/**/
     4138,
 /**/
     4137,