CVE-2023-46246: Integer Overflow or Wraparound (CWE-190)
value: Vim is an improved version of the good old UNIX editor Vi. Heap-use-after-free in memory allocated in the function `ga_grow_inner` in in the file `src/alloc.c` at line 748, which is freed in the file `src/ex_docmd.c` in the function `do_cmdline` at line 1010 and then used again in `src/cmdhist.c` at line 759. When using the `:history` command, it's possible that the provided argument overflows the accepted value. Causing an Integer Overflow and potentially later an use-after-free. This vulnerability has been patched in version 9.0.2068.

CWE description: The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
CVSS v3 base score: 5.5
Published: 2023-10-27T19:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/cmdhist.c, src/errors.h, src/ex_getln.c, src/testdir/test_history.vim, src/version.c
Commit date: 2023-10-26 21:29:32 +0200
Commit message: patch 9.0.2068: [security] overflow in :history

Problem:  [security] overflow in :history
Solution: Check that value fits into int

The get_list_range() function, used to parse numbers for the :history
and :clist command internally uses long variables to store the numbers.
However function arguments are integer pointers, which can then
overflow.

Check that the return value from the vim_str2nr() function is not larger
than INT_MAX and if yes, bail out with an error. I guess nobody uses a
cmdline/clist history that needs so many entries... (famous last words).

It is only a moderate vulnerability, so impact should be low.

Github Advisory:
https://github.com/vim/vim/security/advisories/GHSA-q22m-h7m2-9mgm

Signed-off-by: Christian Brabandt <cb@256bit.org>

Diff: diff --git a/src/cmdhist.c b/src/cmdhist.c
index d398ca7a687b..96a9b3e95b86 100644
--- a/src/cmdhist.c
+++ b/src/cmdhist.c
@@ -740,11 +740,14 @@ ex_history(exarg_T *eap)
     }
     else
 	end = arg;
     if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL)
     {
-	semsg(_(e_trailing_characters_str), end);
+	if (*end != NUL)
+	    semsg(_(e_trailing_characters_str), end);
+	else
+	    semsg(_(e_val_too_large), arg);
 	return;
     }
 
     for (; !got_int && histype1 <= histype2; ++histype1)
     {
diff --git a/src/errors.h b/src/errors.h
index 79a785e1e295..72957d8b93bd 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3558,5 +3558,7 @@ EXTERN char e_aptypes_is_null_nr_str[]
 	INIT(= "E1507: Internal error: ap_types or ap_types[idx] is NULL: %d: %s");
 EXTERN char e_xattr_e2big[]
 	INIT(= N_("E1508: Size of the extended attribute value is larger than the maximum size allowed"));
 EXTERN char e_xattr_other[]
 	INIT(= N_("E1509: Error occurred when reading or writing extended attribute"));
+EXTERN char e_val_too_large[]
+	INIT(= N_("E1510: Value too large: %s"));
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 9683e2ebd5af..8f0be520886b 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -4375,22 +4375,30 @@ get_list_range(char_u **str, int *num1, int *num2)
     *str = skipwhite(*str);
     if (**str == '-' || vim_isdigit(**str))  // parse "from" part of range
     {
 	vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, FALSE, NULL);
 	*str += len;
+	// overflow
+	if (num > INT_MAX)
+	    return FAIL;
+
 	*num1 = (int)num;
 	first = TRUE;
     }
     *str = skipwhite(*str);
     if (**str == ',')			// parse "to" part of range
     {
 	*str = skipwhite(*str + 1);
 	vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, FALSE, NULL);
 	if (len > 0)
 	{
-	    *num2 = (int)num;
 	    *str = skipwhite(*str + len);
+	    // overflow
+	    if (num > INT_MAX)
+		return FAIL;
+
+	    *num2 = (int)num;
 	}
 	else if (!first)		// no number given at all
 	    return FAIL;
     }
     else if (first)			// only one number given
diff --git a/src/testdir/test_history.vim b/src/testdir/test_history.vim
index bb6d67172585..482328ab4aae 100644
--- a/src/testdir/test_history.vim
+++ b/src/testdir/test_history.vim
@@ -252,6 +252,14 @@ func Test_history_crypt_key()
   call assert_equal('set bs=2 key-= ts=8', histget(':'))
 
   set key& bs& ts&
 endfunc
 
+" The following used to overflow and causing an use-after-free
+func Test_history_max_val()
+
+  set history=10
+  call assert_fails(':history 2147483648', 'E1510:')
+  set history&
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 1ae5c98714d1..0284594f7653 100644
--- a/src/version.c
+++ b/src/version.c
@@ -702,10 +702,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2068,
 /**/
     2067,
 /**/
     2066,
 /**/

Vulnerable code: 	semsg(_(e_trailing_characters_str), end);
	    *num2 = (int)num;
Fixed code: 	if (*end != NUL)
	    semsg(_(e_trailing_characters_str), end);
	else
	    semsg(_(e_val_too_large), arg);
EXTERN char e_val_too_large[]
	INIT(= N_("E1510: Value too large: %s"));
	// overflow
	if (num > INT_MAX)
	    return FAIL;

	    // overflow
	    if (num > INT_MAX)
		return FAIL;

	    *num2 = (int)num;
" The following used to overflow and causing an use-after-free
func Test_history_max_val()

  set history=10
  call assert_fails(':history 2147483648', 'E1510:')
  set history&
endfunc

/**/
    2068,