CVE-2021-3796: Use After Free (CWE-416)
value: vim is vulnerable to Use After Free
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 7.3
Published: 2021-09-15T13:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/normal.c, src/testdir/test_edit.vim, src/version.c
Commit date: 2021-09-11 21:14:20 +0200
Commit message: patch 8.2.3428: using freed memory when replacing

Problem:    Using freed memory when replacing. (Dhiraj Mishra)
Solution:   Get the line pointer after calling ins_copychar().

Diff: diff --git a/src/normal.c b/src/normal.c
index 0e1e11801c..6620af9245 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -5097,23 +5097,27 @@ nv_replace(cmdarg_T *cap)
 	     */
 	    for (n = cap->count1; n > 0; --n)
 	    {
 		/*
 		 * Get ptr again, because u_save and/or showmatch() will have
-		 * released the line.  At the same time we let know that the
-		 * line will be changed.
+		 * released the line.  This may also happen in ins_copychar().
+		 * At the same time we let know that the line will be changed.
 		 */
-		ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
 		if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
 		{
 		  int c = ins_copychar(curwin->w_cursor.lnum
 					   + (cap->nchar == Ctrl_Y ? -1 : 1));
+
+		  ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
 		  if (c != NUL)
 		    ptr[curwin->w_cursor.col] = c;
 		}
 		else
+		{
+		    ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
 		    ptr[curwin->w_cursor.col] = cap->nchar;
+		}
 		if (p_sm && msg_silent == 0)
 		    showmatch(cap->nchar);
 		++curwin->w_cursor.col;
 	    }
 #ifdef FEAT_NETBEANS_INTG
diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim
index 3fcc11f2d8..518c21ea84 100644
--- a/src/testdir/test_edit.vim
+++ b/src/testdir/test_edit.vim
@@ -1893,6 +1893,18 @@ func Test_edit_revins()
   call assert_equal(['one two three', 'ruof'], getline(1, '$'))
   set revins&
   bw!
 endfunc
 
+" Test for getting the character of the line below after "p"
+func Test_edit_put_CTRL_E()
+  set encoding=latin1
+  new
+  let @" = ''
+  sil! norm orggRx
+  sil! norm pr
+  call assert_equal(['r', 'r'], getline(1, 2))
+  bwipe!
+  set encoding=utf-8
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index f82881fc06..2d6f6c7bfd 100644
--- a/src/version.c
+++ b/src/version.c
@@ -753,10 +753,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3428,
 /**/
     3427,
 /**/
     3426,
 /**/

Vulnerable code: 		 * released the line.  At the same time we let know that the
		 * line will be changed.
		ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
Fixed code: 		 * released the line.  This may also happen in ins_copychar().
		 * At the same time we let know that the line will be changed.

		  ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
		{
		    ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
		}
" Test for getting the character of the line below after "p"
func Test_edit_put_CTRL_E()
  set encoding=latin1
  new
  let @" = ''
  sil! norm orggRx
  sil! norm pr
  call assert_equal(['r', 'r'], getline(1, 2))
  bwipe!
  set encoding=utf-8
endfunc

/**/
    3428,