CVE-2022-0128: Out-of-bounds Read (CWE-125)
value: vim is vulnerable to Out-of-bounds Read
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.8
Published: 2022-01-06T17:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/ex_docmd.c, src/testdir/test_vim9_func.vim, src/version.c, src/vim9compile.c
Commit date: 2022-01-05 16:50:40 +0000
Commit message: patch 8.2.4009: reading one byte beyond the end of the line

Problem:    Reading one byte beyond the end of the line.
Solution:   Check for NUL byte first.

Diff: diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 41cbd3090f8..1f15bc8544d 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -3630,11 +3630,12 @@ find_ex_command(
 	    eap->cmdidx = CMD_eval;
 	    return eap->cmd;
 	}
 
 	// Check for "++nr" and "--nr".
-	if (p == eap->cmd && p[0] == p[1] && (*p == '+' || *p == '-'))
+	if (p == eap->cmd && p[0] != NUL && p[0] == p[1]
+						   && (*p == '+' || *p == '-'))
 	{
 	    eap->cmdidx = *p == '+' ? CMD_increment : CMD_decrement;
 	    return eap->cmd + 2;
 	}
     }
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index 0ec1700dc62..025edd5dafa 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -3535,10 +3535,21 @@ def Test_numbered_function_reference()
   # check that the function still exists
   assert_equal(output, execute('legacy func g:mydict.afunc'))
   unlet g:mydict
 enddef
 
+def Test_go_beyond_end_of_cmd()
+  # this was reading the byte after the end of the line
+  var lines =<< trim END
+    def F()
+      cal
+    enddef
+    defcompile
+  END
+  CheckScriptFailure(lines, 'E476:')
+enddef
+
 if has('python3')
   def Test_python3_heredoc()
     py3 << trim EOF
       import vim
       vim.vars['didit'] = 'yes'
diff --git a/src/version.c b/src/version.c
index 48a1c9820e1..6b7a915c4fc 100644
--- a/src/version.c
+++ b/src/version.c
@@ -748,10 +748,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4009,
 /**/
     4008,
 /**/
     4007,
 /**/
diff --git a/src/vim9compile.c b/src/vim9compile.c
index cccf02c2c52..f1a6a031163 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -2779,11 +2779,12 @@ compile_def_function(
 	 * in "$ENV->func()" the "$" is not a range
 	 */
 	cmd = ea.cmd;
 	if ((*cmd != '$' || starts_with_colon)
 		&& (starts_with_colon || !(*cmd == '\''
-		       || (cmd[0] == cmd[1] && (*cmd == '+' || *cmd == '-')))))
+		       || (cmd[0] != NUL && cmd[0] == cmd[1]
+					    && (*cmd == '+' || *cmd == '-')))))
 	{
 	    ea.cmd = skip_range(ea.cmd, TRUE, NULL);
 	    if (ea.cmd > cmd)
 	    {
 		if (!starts_with_colon

Vulnerable code: 	if (p == eap->cmd && p[0] == p[1] && (*p == '+' || *p == '-'))
		       || (cmd[0] == cmd[1] && (*cmd == '+' || *cmd == '-')))))
Fixed code: 	if (p == eap->cmd && p[0] != NUL && p[0] == p[1]
						   && (*p == '+' || *p == '-'))
def Test_go_beyond_end_of_cmd()
  # this was reading the byte after the end of the line
  var lines =<< trim END
    def F()
      cal
    enddef
    defcompile
  END
  CheckScriptFailure(lines, 'E476:')
enddef

/**/
    4009,
		       || (cmd[0] != NUL && cmd[0] == cmd[1]
					    && (*cmd == '+' || *cmd == '-')))))