CVE-2021-4069: 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-06T12:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/ex_docmd.c, src/testdir/test_ex_mode.vim, src/version.c
Commit date: 2021-12-05 12:06:24 +0000
Commit message: patch 8.2.3741: using freed memory in open command

Problem:    Using freed memory in open command.
Solution:   Make a copy of the current line.

Diff: diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 439a78cedc5..1fc538b85c5 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -6875,17 +6875,21 @@ ex_open(exarg_T *eap)
 	p = skip_regexp(eap->arg, '/', magic_isset());
 	*p = NUL;
 	regmatch.regprog = vim_regcomp(eap->arg, magic_isset() ? RE_MAGIC : 0);
 	if (regmatch.regprog != NULL)
 	{
+	    // make a copy of the line, when searching for a mark it might be
+	    // flushed
+	    char_u *line = vim_strsave(ml_get_curline());
+
 	    regmatch.rm_ic = p_ic;
-	    p = ml_get_curline();
-	    if (vim_regexec(&regmatch, p, (colnr_T)0))
-		curwin->w_cursor.col = (colnr_T)(regmatch.startp[0] - p);
+	    if (vim_regexec(&regmatch, line, (colnr_T)0))
+		curwin->w_cursor.col = (colnr_T)(regmatch.startp[0] - line);
 	    else
 		emsg(_(e_nomatch));
 	    vim_regfree(regmatch.regprog);
+	    vim_free(line);
 	}
 	// Move to the NUL, ignore any other arguments.
 	eap->arg += STRLEN(eap->arg);
     }
     check_cursor();
diff --git a/src/testdir/test_ex_mode.vim b/src/testdir/test_ex_mode.vim
index 1ca50623286..7031115fcba 100644
--- a/src/testdir/test_ex_mode.vim
+++ b/src/testdir/test_ex_mode.vim
@@ -119,10 +119,23 @@ func Test_open_command()
   call assert_equal(5, col('.'))
   call assert_fails('call feedkeys("Qopen /baz/\<CR>", "xt")', 'E479:')
   close!
 endfunc
 
+func Test_open_command_flush_line()
+  " this was accessing freed memory: the regexp match uses a pointer to the
+  " current line which becomes invalid when searching for the ') mark.
+  new
+  call setline(1, ['one', 'two. three'])
+  s/one/ONE
+  try
+    open /\%')/
+  catch /E479/
+  endtry
+  bwipe!
+endfunc
+
 " Test for :g/pat/visual to run vi commands in Ex mode
 " This used to hang Vim before 8.2.0274.
 func Test_Ex_global()
   new
   call setline(1, ['', 'foo', 'bar', 'foo', 'bar', 'foo'])
diff --git a/src/version.c b/src/version.c
index 6e1e30aa987..83326fef65e 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,10 +751,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3741,
 /**/
     3740,
 /**/
     3739,
 /**/

Vulnerable code: 	    p = ml_get_curline();
	    if (vim_regexec(&regmatch, p, (colnr_T)0))
		curwin->w_cursor.col = (colnr_T)(regmatch.startp[0] - p);
Fixed code: 	    // make a copy of the line, when searching for a mark it might be
	    // flushed
	    char_u *line = vim_strsave(ml_get_curline());

	    if (vim_regexec(&regmatch, line, (colnr_T)0))
		curwin->w_cursor.col = (colnr_T)(regmatch.startp[0] - line);
	    vim_free(line);
func Test_open_command_flush_line()
  " this was accessing freed memory: the regexp match uses a pointer to the
  " current line which becomes invalid when searching for the ') mark.
  new
  call setline(1, ['one', 'two. three'])
  s/one/ONE
  try
    open /\%')/
  catch /E479/
  endtry
  bwipe!
endfunc

/**/
    3741,