CVE-2022-2343 (published 2022-07-08T18:15Z)

Heap-based Buffer Overflow in GitHub repository vim/vim prior to 9.0.0044.

Severity: MEDIUM
CVSS v2 base score: 6.8 (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 7.8, HIGH (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-122
cwe_name: Heap-based Buffer Overflow
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().
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/vim/vim
commit_details: repo_url: https://github.com/vim/vim
author: Bram Moolenaar
author_date: 2022-07-07 19:42:04+01:00
msg: patch 9.0.0045: reading past end of completion with a long line

Problem:    Reading past end of completion with a long line and 'infercase'
            set.
Solution:   Allocate the string if needed.
repository: repo_url: https://github.com/vim/vim
repo_name: vim/vim
description: The official Vim repository
repo_language: Vim Script
owner: vim
file_changes: filename: insexpand.c
change_type: ModificationType.MODIFY
diff: @@ -524,29 +524,32 @@ ins_compl_accept_char(int c)
 
 /*
  * Get the completed text by inferring the case of the originally typed text.
+ * If the result is in allocated memory "tofree" is set to it.
  */
     static char_u *
 ins_compl_infercase_gettext(
 	char_u	*str,
-	int	actual_len,
-	int	actual_compl_length,
-	int	min_len)
+	int	char_len,
+	int	compl_char_len,
+	int	min_len,
+	char_u  **tofree)
 {
     int		*wca;			// Wide character array.
     char_u	*p;
     int		i, c;
     int		has_lower = FALSE;
     int		was_letter = FALSE;
+    garray_T	gap;
 
     IObuff[0] = NUL;
 
     // Allocate wide character array for the completion and fill it.
-    wca = ALLOC_MULT(int, actual_len);
+    wca = ALLOC_MULT(int, char_len);
     if (wca == NULL)
 	return IObuff;
 
     p = str;
-    for (i = 0; i < actual_len; ++i)
+    for (i = 0; i < char_len; ++i)
 	if (has_mbyte)
 	    wca[i] = mb_ptr2char_adv(&p);
 	else
@@ -566,7 +569,7 @@ ins_compl_infercase_gettext(
 	    if (MB_ISUPPER(wca[i]))
 	    {
 		// Rule 1 is satisfied.
-		for (i = actual_compl_length; i < actual_len; ++i)
+		for (i = compl_char_len; i < char_len; ++i)
 		    wca[i] = MB_TOLOWER(wca[i]);
 		break;
 	    }
@@ -587,7 +590,7 @@ ins_compl_infercase_gettext(
 	    if (was_letter && MB_ISUPPER(c) && MB_ISLOWER(wca[i]))
 	    {
 		// Rule 2 is satisfied.
-		for (i = actual_compl_length; i < actual_len; ++i)
+		for (i = compl_char_len; i < char_len; ++i)
 		    wca[i] = MB_TOUPPER(wca[i]);
 		break;
 	    }
@@ -610,20 +613,52 @@ ins_compl_infercase_gettext(
     }
 
     // Generate encoding specific output from wide character array.
-    // Multi-byte characters can occupy up to five bytes more than
-    // ASCII characters, and we also need one byte for NUL, so stay
-    // six bytes away from the edge of IObuff.
     p = IObuff;
     i = 0;
-    while (i < actual_len && (p - IObuff + 6) < IOSIZE)
-	if (has_mbyte)
+    ga_init2(&gap, 1, 500);
+    while (i < char_len)
+    {
+	if (gap.ga_data != NULL)
+	{
+	    if (ga_grow(&gap, 10) == FAIL)
+	    {
+		ga_clear(&gap);
+		return (char_u *)"[failed]";
+	    }
+	    p = (char_u *)gap.ga_data + gap.ga_len;
+	    if (has_mbyte)
+		gap.ga_len += (*mb_char2bytes)(wca[i++], p);
+	    else
+	    {
+		*p = wca[i++];
+		++gap.ga_len;
+	    }
+	}
+	else if ((p - IObuff) + 6 >= IOSIZE)
+	{
+	    // Multi-byte characters can occupy up to five bytes more than
+	    // ASCII characters, and we also need one byte for NUL, so when
+	    // getting to six bytes from the edge of IObuff switch to using a
+	    // growarray.  Add the character in the next round.
+	    if (ga_grow(&gap, IOSIZE) == FAIL)
+		return (char_u *)"[failed]";
+	    STRCPY(gap.ga_data, IObuff);
+	    gap.ga_len = STRLEN(IObuff);
+	}
+	else if (has_mbyte)
 	    p += (*mb_char2bytes)(wca[i++], p);
 	else
 	    *(p++) = wca[i++];
-    *p = NUL;
-
+    }
     vim_free(wca);
 
+    if (gap.ga_data != NULL)
+    {
+	*tofree = gap.ga_data;
+	return gap.ga_data;
+    }
+
+    *p = NUL;
     return IObuff;
 }
 
@@ -644,10 +679,12 @@ ins_compl_add_infercase(
 {
     char_u	*str = str_arg;
     char_u	*p;
-    int		actual_len;		// Take multi-byte characters
-    int		actual_compl_length;	// into account.
+    int		char_len;		// count multi-byte characters
+    int		compl_char_len;
     int		min_len;
     int		flags = 0;
+    int		res;
+    char_u	*tofree = NULL;
 
     if (p_ic && curbuf->b_p_inf && len > 0)
     {
@@ -657,44 +694,45 @@ ins_compl_add_infercase(
 	if (has_mbyte)
 	{
 	    p = str;
-	    actual_len = 0;
+	    char_len = 0;
 	    while (*p != NUL)
 	    {
 		MB_PTR_ADV(p);
-		++actual_len;
+		++char_len;
 	    }
 	}
 	else
-	    actual_len = len;
+	    char_len = len;
 
 	// Find actual length of original text.
 	if (has_mbyte)
 	{
 	    p = compl_orig_text;
-	    actual_compl_length = 0;
+	    compl_char_len = 0;
 	    while (*p != NUL)
 	    {
 		MB_PTR_ADV(p);
-		++actual_compl_length;
+		++compl_char_len;
 	    }
 	}
 	else
-	    actual_compl_length = compl_length;
+	    compl_char_len = compl_length;
 
-	// "actual_len" may be smaller than "actual_compl_length" when using
+	// "char_len" may be smaller than "compl_char_len" when using
 	// thesaurus, only use the minimum when comparing.
-	min_len = actual_len < actual_compl_length
-					   ? actual_len : actual_compl_length;
+	min_len = char_len < compl_char_len ? char_len : compl_char_len;
 
-	str = ins_compl_infercase_gettext(str, actual_len, actual_compl_length,
-								min_len);
+	str = ins_compl_infercase_gettext(str, char_len,
+					  compl_char_len, min_len, &tofree);
     }
     if (cont_s_ipos)
 	flags |= CP_CONT_S_IPOS;
     if (icase)
 	flags |= CP_ICASE;
 
-    return ins_compl_add(str, len, fname, NULL, NULL, dir, flags, FALSE);
+    res = ins_compl_add(str, len, fname, NULL, NULL, dir, flags, FALSE);
+    vim_free(tofree);
+    return res;
 }
 
 /*

programming_language: C
filename: test_ins_complete.vim
change_type: ModificationType.MODIFY
diff: @@ -2097,4 +2097,20 @@ func Test_complete_overrun()
   bwipe!
 endfunc
 
+func Test_infercase_very_long_line()
+  " this was truncating the line when inferring case
+  new
+  let longLine = "blah "->repeat(300)
+  let verylongLine = "blah "->repeat(400)
+  call setline(1, verylongLine)
+  call setline(2, longLine)
+  set ic infercase
+  exe "normal 2Go\<C-X>\<C-L>\<Esc>"
+  call assert_equal(longLine, getline(3))
+
+  bwipe!
+  set noic noinfercase
+endfunc
+
+
 " vim: shiftwidth=2 sts=2 expandtab

programming_language: Lua
filename: version.c
change_type: ModificationType.MODIFY
diff: @@ -735,6 +735,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    45,
 /**/
     44,
 /**/

programming_language: Ruby