CVE-2022-1616: Use After Free (CWE-416)
value: Use after free in append_command in GitHub repository vim/vim prior to 8.2.4895. This vulnerability is capable of crashing software, Bypass Protection Mechanism, Modify Memory, and possible remote execution
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: 2022-05-07T19:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/ex_docmd.c, src/testdir/test_cmdline.vim, src/version.c
Commit date: 2022-05-06 20:38:47 +0100
Commit message: patch 8.2.4895: buffer overflow with invalid command with composing chars

Problem:    Buffer overflow with invalid command with composing chars.
Solution:   Check that the whole character fits in the buffer.

Diff: diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 26acc07e0fa7..46f2b221b384 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -3433,18 +3433,20 @@ append_command(char_u *cmd)
     char_u *s = cmd;
     char_u *d;
 
     STRCAT(IObuff, ": ");
     d = IObuff + STRLEN(IObuff);
-    while (*s != NUL && d - IObuff < IOSIZE - 7)
+    while (*s != NUL && d - IObuff + 5 < IOSIZE)
     {
 	if (enc_utf8 ? (s[0] == 0xc2 && s[1] == 0xa0) : *s == 0xa0)
 	{
 	    s += enc_utf8 ? 2 : 1;
 	    STRCPY(d, "<a0>");
 	    d += 4;
 	}
+	else if (d - IObuff + (*mb_ptr2len)(s) + 1 >= IOSIZE)
+	    break;
 	else
 	    MB_COPY_CHAR(s, d);
     }
     *d = NUL;
 }
diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim
index 8d556faf0a73..474638fb00d6 100644
--- a/src/testdir/test_cmdline.vim
+++ b/src/testdir/test_cmdline.vim
@@ -3351,10 +3351,21 @@ func Test_cmdline_complete_scriptnames()
   bw!
   call delete('Xa1b2c3.vim')
   set wildmenu&
 endfunc
 
+" this was going over the end of IObuff
+func Test_report_error_with_composing()
+  let caught = 'no'
+  try
+    exe repeat('0', 987) .. "0\xdd\x80\xdd\x80\xdd\x80\xdd\x80"
+  catch /E492:/
+    let caught = 'yes'
+  endtry
+  call assert_equal('yes', caught)
+endfunc
+
 " Test for expanding 2-letter and 3-letter :substitute command arguments.
 " These commands don't accept an argument.
 func Test_cmdline_complete_substitute_short()
   for cmd in ['sc', 'sce', 'scg', 'sci', 'scI', 'scn', 'scp', 'scl',
         \ 'sgc', 'sge', 'sg', 'sgi', 'sgI', 'sgn', 'sgp', 'sgl', 'sgr',
diff --git a/src/version.c b/src/version.c
index 0857bcb7c0ab..df306852f57b 100644
--- a/src/version.c
+++ b/src/version.c
@@ -744,10 +744,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4895,
 /**/
     4894,
 /**/
     4893,
 /**/

Vulnerable code:     while (*s != NUL && d - IObuff < IOSIZE - 7)
Fixed code:     while (*s != NUL && d - IObuff + 5 < IOSIZE)
	else if (d - IObuff + (*mb_ptr2len)(s) + 1 >= IOSIZE)
	    break;
" this was going over the end of IObuff
func Test_report_error_with_composing()
  let caught = 'no'
  try
    exe repeat('0', 987) .. "0\xdd\x80\xdd\x80\xdd\x80\xdd\x80"
  catch /E492:/
    let caught = 'yes'
  endtry
  call assert_equal('yes', caught)
endfunc

/**/
    4895,