CVE-2023-48237: Integer Overflow or Wraparound (CWE-190)
value: Vim is an open source command line text editor. In affected versions when shifting lines in operator pending mode and using a very large value, it may be possible to overflow the size of integer. Impact is low, user interaction is required and a crash may not even happen in all situations. This issue has been addressed in commit `6bf131888` which has been included in version 9.0.2112. Users are advised to upgrade. There are no known workarounds for this vulnerability.
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: 4.3
Published: 2023-11-16T23:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/ops.c, src/testdir/test_indent.vim, src/version.c
Commit date: 2023-11-16 22:04:38 +0100
Commit message: patch 9.0.2112: [security]: overflow in shift_line

Problem:  [security]: overflow in shift_line
Solution: allow a max indent of INT_MAX

[security]: overflow in shift_line

When shifting lines in operator pending mode and using a very large
value, we may overflow the size of integer. Fix this by using a long
variable, testing if the result would be larger than INT_MAX and if so,
indent by INT_MAX value.

Special case: We cannot use long here, since on 32bit architectures (or
on Windows?), it typically cannot take larger values than a plain int,
so we have to use long long count, decide whether the resulting
multiplication of the shiftwidth value * amount is larger than INT_MAX
and if so, we will store INT_MAX as possible larges value in the long
long count variable.

Then we can safely cast it back to int when calling the functions to set
the indent (set_indent() or change_indent()). So this should be safe.

Add a test that when using a huge value in operator pending mode for
shifting, we will shift by INT_MAX

closes: #13535

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

Diff: diff --git a/src/ops.c b/src/ops.c
index c0a2981d6877..ecd7fc2170c5 100644
--- a/src/ops.c
+++ b/src/ops.c
@@ -227,15 +227,15 @@ shift_line(
     int	left,
     int	round,
     int	amount,
     int call_changed_bytes)	// call changed_bytes()
 {
-    int		count;
+    long long	count;
     int		i, j;
     int		sw_val = (int)get_sw_value_indent(curbuf);
 
-    count = get_indent();	// get current indent
+    count = (long long)get_indent();	// get current indent
 
     if (round)			// round off indent
     {
 	i = count / sw_val;	// number of 'shiftwidth' rounded down
 	j = count % sw_val;	// extra spaces
@@ -258,18 +258,23 @@ shift_line(
 	    count -= sw_val * amount;
 	    if (count < 0)
 		count = 0;
 	}
 	else
-	    count += sw_val * amount;
+	{
+	    if ((long long)sw_val * (long long)amount > INT_MAX - count)
+		count = INT_MAX;
+	    else
+		count += (long long)sw_val * (long long)amount;
+	}
     }
 
     // Set new indent
     if (State & VREPLACE_FLAG)
-	change_indent(INDENT_SET, count, FALSE, NUL, call_changed_bytes);
+	change_indent(INDENT_SET, (int)count, FALSE, NUL, call_changed_bytes);
     else
-	(void)set_indent(count, call_changed_bytes ? SIN_CHANGED : 0);
+	(void)set_indent((int)count, call_changed_bytes ? SIN_CHANGED : 0);
 }
 
 /*
  * Shift one line of the current block one shiftwidth right or left.
  * Leaves cursor on first character in block.
diff --git a/src/testdir/test_indent.vim b/src/testdir/test_indent.vim
index 96e9d2300883..217a7ae62507 100644
--- a/src/testdir/test_indent.vim
+++ b/src/testdir/test_indent.vim
@@ -273,6 +273,17 @@ func Test_formatting_keeps_first_line_indent()
   END
   call assert_equal(expected, getline(1, '$'))
   bwipe!
 endfunc
 
+" Test for indenting with large amount, causes overflow
+func Test_indent_overflow_count()
+  new
+  setl sw=8
+  call setline(1, "abc")
+  norm! V2147483647>
+  " indents by INT_MAX
+  call assert_equal(2147483647, indent(1))
+  close!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 66aff800bd38..249cd11d208b 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 */
+/**/
+    2112,
 /**/
     2111,
 /**/
     2110,
 /**/

Vulnerable code:     int		count;
    count = get_indent();	// get current indent
	    count += sw_val * amount;
	change_indent(INDENT_SET, count, FALSE, NUL, call_changed_bytes);
	(void)set_indent(count, call_changed_bytes ? SIN_CHANGED : 0);
Fixed code:     long long	count;
    count = (long long)get_indent();	// get current indent
	{
	    if ((long long)sw_val * (long long)amount > INT_MAX - count)
		count = INT_MAX;
	    else
		count += (long long)sw_val * (long long)amount;
	}
	change_indent(INDENT_SET, (int)count, FALSE, NUL, call_changed_bytes);
	(void)set_indent((int)count, call_changed_bytes ? SIN_CHANGED : 0);
" Test for indenting with large amount, causes overflow
func Test_indent_overflow_count()
  new
  setl sw=8
  call setline(1, "abc")
  norm! V2147483647>
  " indents by INT_MAX
  call assert_equal(2147483647, indent(1))
  close!
endfunc

/**/
    2112,