CVE-2021-3903: 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-10-27T21:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/move.c, src/testdir/test_normal.vim, src/version.c
Commit date: 2021-10-25 17:07:04 +0100
Commit message: patch 8.2.3564: invalid memory access when scrolling without valid screen

Problem:    Invalid memory access when scrolling without a valid screen.
Solution:   Do not set VALID_BOTLINE in w_valid.

Diff: diff --git a/src/move.c b/src/move.c
index 8fe00bb678..64b12963c6 100644
--- a/src/move.c
+++ b/src/move.c
@@ -197,11 +197,10 @@ update_topline(void)
     if (!screen_valid(TRUE) || curwin->w_height == 0)
     {
 	check_cursor_lnum();
 	curwin->w_topline = curwin->w_cursor.lnum;
 	curwin->w_botline = curwin->w_topline;
-	curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
 	curwin->w_scbind_pos = 1;
 	return;
     }
 
     check_cursor_moved(curwin);
diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim
index f8dbf8aeda..f994f970ab 100644
--- a/src/testdir/test_normal.vim
+++ b/src/testdir/test_normal.vim
@@ -32,29 +32,29 @@ func CountSpaces(type, ...)
   elseif a:type == 'line'
     silent exe "normal! '[V']y"
   else
     silent exe "normal! `[v`]y"
   endif
-  let g:a=strlen(substitute(@@, '[^ ]', '', 'g'))
+  let g:a = strlen(substitute(@@, '[^ ]', '', 'g'))
   let &selection = sel_save
   let @@ = reg_save
 endfunc
 
 func OpfuncDummy(type, ...)
   " for testing operatorfunc
-  let g:opt=&linebreak
+  let g:opt = &linebreak
 
   if a:0  " Invoked from Visual mode, use gv command.
     silent exe "normal! gvy"
   elseif a:type == 'line'
     silent exe "normal! '[V']y"
   else
     silent exe "normal! `[v`]y"
   endif
   " Create a new dummy window
   new
-  let g:bufnr=bufnr('%')
+  let g:bufnr = bufnr('%')
 endfunc
 
 func Test_normal00_optrans()
   new
   call append(0, ['1 This is a simple test: abcd', '2 This is the second line', '3 this is the third line'])
@@ -985,10 +985,26 @@ func Test_vert_scroll_cmds()
 
   set foldenable&
   close!
 endfunc
 
+func Test_scroll_in_ex_mode()
+  " This was using invalid memory because w_botline was invalid.
+  let lines =<< trim END
+      diffsplit
+      norm os00(
+      call writefile(['done'], 'Xdone')
+      qa!
+  END
+  call writefile(lines, 'Xscript')
+  call assert_equal(1, RunVim([], [], '--clean -X -Z -e -s -S Xscript'))
+  call assert_equal(['done'], readfile('Xdone'))
+
+  call delete('Xscript')
+  call delete('Xdone')
+endfunc
+
 " Test for the 'sidescroll' option
 func Test_sidescroll_opt()
   new
   20vnew
 
diff --git a/src/version.c b/src/version.c
index 57585dc5de..2c25238a0d 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 */
+/**/
+    3564,
 /**/
     3563,
 /**/
     3562,
 /**/

Vulnerable code: 	curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
  let g:a=strlen(substitute(@@, '[^ ]', '', 'g'))
  let g:opt=&linebreak
  let g:bufnr=bufnr('%')
Fixed code:   let g:a = strlen(substitute(@@, '[^ ]', '', 'g'))
  let g:opt = &linebreak
  let g:bufnr = bufnr('%')
func Test_scroll_in_ex_mode()
  " This was using invalid memory because w_botline was invalid.
  let lines =<< trim END
      diffsplit
      norm os00(
      call writefile(['done'], 'Xdone')
      qa!
  END
  call writefile(lines, 'Xscript')
  call assert_equal(1, RunVim([], [], '--clean -X -Z -e -s -S Xscript'))
  call assert_equal(['done'], readfile('Xdone'))

  call delete('Xscript')
  call delete('Xdone')
endfunc

/**/
    3564,