CVE-2021-4192: 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.8
Published: 2021-12-31T15:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/regexp.c, src/testdir/test_regexp_latin.vim, src/version.c
Commit date: 2021-12-30 14:49:43 +0000
Commit message: patch 8.2.3949: using freed memory with /\%V

Problem:    Using freed memory with /\%V.
Solution:   Get the line again after getvvcol().

Diff: diff --git a/src/regexp.c b/src/regexp.c
index 7fbad451b6a..9e3d218fb9d 100644
--- a/src/regexp.c
+++ b/src/regexp.c
@@ -1308,13 +1308,13 @@ reg_match_visual(void)
     }
     lnum = rex.lnum + rex.reg_firstlnum;
     if (lnum < top.lnum || lnum > bot.lnum)
 	return FALSE;
 
+    col = (colnr_T)(rex.input - rex.line);
     if (mode == 'v')
     {
-	col = (colnr_T)(rex.input - rex.line);
 	if ((lnum == top.lnum && col < top.col)
 		|| (lnum == bot.lnum && col >= bot.col + (*p_sel != 'e')))
 	    return FALSE;
     }
     else if (mode == Ctrl_V)
@@ -1325,11 +1325,16 @@ reg_match_visual(void)
 	    start = start2;
 	if (end2 > end)
 	    end = end2;
 	if (top.col == MAXCOL || bot.col == MAXCOL || curswant == MAXCOL)
 	    end = MAXCOL;
-	cols = win_linetabsize(wp, rex.line, (colnr_T)(rex.input - rex.line));
+
+	// getvvcol() flushes rex.line, need to get it again
+	rex.line = reg_getline(rex.lnum);
+	rex.input = rex.line + col;
+
+	cols = win_linetabsize(wp, rex.line, col);
 	if (cols < start || cols > end - (*p_sel == 'e'))
 	    return FALSE;
     }
     return TRUE;
 }
diff --git a/src/testdir/test_regexp_latin.vim b/src/testdir/test_regexp_latin.vim
index 96c317736df..03efdbea30e 100644
--- a/src/testdir/test_regexp_latin.vim
+++ b/src/testdir/test_regexp_latin.vim
@@ -1043,6 +1043,14 @@ func Test_using_mark_position()
   norm O0
   call assert_fails("s/\\%')", 'E486:')
   bwipe!
 endfunc
 
+func Test_using_visual_position()
+  " this was using freed memory
+  new
+  exe "norm 0o\<Esc>\<C-V>k\<C-X>o0"
+  /\%V
+  bwipe!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index a362dca9a7d..9f5ed308a1f 100644
--- a/src/version.c
+++ b/src/version.c
@@ -747,10 +747,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3949,
 /**/
     3948,
 /**/
     3947,
 /**/

Vulnerable code: 	col = (colnr_T)(rex.input - rex.line);
	cols = win_linetabsize(wp, rex.line, (colnr_T)(rex.input - rex.line));
Fixed code:     col = (colnr_T)(rex.input - rex.line);

	// getvvcol() flushes rex.line, need to get it again
	rex.line = reg_getline(rex.lnum);
	rex.input = rex.line + col;

	cols = win_linetabsize(wp, rex.line, col);
func Test_using_visual_position()
  " this was using freed memory
  new
  exe "norm 0o\<Esc>\<C-V>k\<C-X>o0"
  /\%V
  bwipe!
endfunc

/**/
    3949,