CVE-2021-3973: Heap-based Buffer Overflow (CWE-122)
value: vim is vulnerable to Heap-based Buffer Overflow
CWE description: A heap overflow condition is a buffer overflow, where the buffer that can be overwritten is allocated in the heap portion of memory, generally meaning that the buffer was allocated using a routine such as malloc().
CVSS v3 base score: 7.8
Published: 2021-11-19T12:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/findfile.c, src/normal.c, src/testdir/test_visual.vim, src/version.c
Commit date: 2021-11-17 18:00:31 +0000
Commit message: patch 8.2.3611: crash when using CTRL-W f without finding a file name

Problem:    Crash when using CTRL-W f without finding a file name.
Solution:   Bail out when the file name length is zero.

Diff: diff --git a/src/findfile.c b/src/findfile.c
index b79b1d1dbf..b4aed048ce 100644
--- a/src/findfile.c
+++ b/src/findfile.c
@@ -1733,10 +1733,13 @@ find_file_in_path_option(
 
     // Avoid a requester here for a volume that doesn't exist.
     proc->pr_WindowPtr = (APTR)-1L;
 # endif
 
+    if (len == 0)
+	return NULL;
+
     if (first == TRUE)
     {
 	// copy file name into NameBuff, expanding environment variables
 	save_char = ptr[len];
 	ptr[len] = NUL;
@@ -2116,11 +2119,16 @@ find_file_name_in_path(
 {
     char_u	*file_name;
     int		c;
 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
     char_u	*tofree = NULL;
+# endif
 
+    if (len == 0)
+	return NULL;
+
+# if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
     if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL)
     {
 	tofree = eval_includeexpr(ptr, len);
 	if (tofree != NULL)
 	{
diff --git a/src/normal.c b/src/normal.c
index 059baee90f..aab336a5fe 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -3879,12 +3879,14 @@ get_visual_text(
 	else
 	{
 	    *pp = ml_get_pos(&VIsual);
 	    *lenp = curwin->w_cursor.col - VIsual.col + 1;
 	}
-	if (has_mbyte)
-	    // Correct the length to include the whole last character.
+	if (**pp == NUL)
+	    *lenp = 0;
+	if (has_mbyte && *lenp > 0)
+	    // Correct the length to include all bytes of the last character.
 	    *lenp += (*mb_ptr2len)(*pp + (*lenp - 1)) - 1;
     }
     reset_VIsual_and_resel();
     return OK;
 }
diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim
index 20eb4b43e8..0c89dbb010 100644
--- a/src/testdir/test_visual.vim
+++ b/src/testdir/test_visual.vim
@@ -1263,10 +1263,18 @@ func Test_visual_block_with_virtualedit()
   call term_sendkeys(buf, "\<Esc>")
   call StopVimInTerminal(buf)
   call delete('XTest_block')
 endfunc
 
+func Test_visual_block_ctrl_w_f()
+  " Emtpy block selected in new buffer should not result in an error.
+  au! BufNew foo sil norm f
+  edit foo
+
+  au! BufNew
+endfunc
+
 func Test_visual_reselect_with_count()
   " this was causing an illegal memory access
   let lines =<< trim END
 
 
diff --git a/src/version.c b/src/version.c
index d301120079..4251b03ea5 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,10 +755,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3611,
 /**/
     3610,
 /**/
     3609,
 /**/

Vulnerable code: 	if (has_mbyte)
	    // Correct the length to include the whole last character.
Fixed code:     if (len == 0)
	return NULL;

# endif
    if (len == 0)
	return NULL;

# if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
	if (**pp == NUL)
	    *lenp = 0;
	if (has_mbyte && *lenp > 0)
	    // Correct the length to include all bytes of the last character.
func Test_visual_block_ctrl_w_f()
  " Emtpy block selected in new buffer should not result in an error.
  au! BufNew foo sil norm f
  edit foo

  au! BufNew
endfunc

/**/
    3611,