changeset 33813:2e92551b2350 v9.0.2122

patch 9.0.2122: [security]: prevent overflow in indenting Commit: https://github.com/vim/vim/commit/3770574e4a70e810add9929973c51f9070c8c851 Author: Christian Brabandt <cb@256bit.org> Date: Wed Nov 22 22:18:35 2023 +0100 patch 9.0.2122: [security]: prevent overflow in indenting Problem: [security]: prevent overflow in indenting Solution: use long long and remove cast to (int) The shiftwidth option values are defined as being long. However, when calculating the actual amount of indent, we cast down to (int), which may cause the shiftwidth value to become negative and later it may even cause Vim to try to allocate a huge amount of memory. We already use long and long long variable types to calculate the indent (and detect possible overflows), so the cast to (int) seems superfluous and can be safely removed. So let's just remove the (int) cast and calculate the indent using longs. Additionally, the 'shiftwidth' option value is also used when determining the actual 'cino' options. There it can again cause another overflow, so make sure it is safe in parse_cino() as well. fixes: #13554 closes: #13555 Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Wed, 22 Nov 2023 22:30:05 +0100
parents c447311b8694
children f20f1226fbc8
files src/cindent.c src/ops.c src/testdir/test_indent.vim src/version.c
diffstat 4 files changed, 69 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/src/cindent.c
+++ b/src/cindent.c
@@ -1730,10 +1730,17 @@ parse_cino(buf_T *buf)
     char_u	*p;
     char_u	*l;
     char_u	*digits;
-    int		n;
+    long long	n;
     int		divider;
     int		fraction = 0;
-    int		sw = (int)get_sw_value(buf);
+    int		sw;
+    long long	t = get_sw_value(buf);
+
+    // needed for cino-(, it will be multiplied by 2 again
+    if (t > INT_MAX / 2)
+	sw = INT_MAX / 2;
+    else
+	sw = (int)t;
 
     // Set the default values.
 
@@ -1902,47 +1909,52 @@ parse_cino(buf_T *buf)
 	if (l[1] == '-')
 	    n = -n;
 
+	if (n > INT_MAX)
+	    n = INT_MAX;
+	else if (n < INT_MIN)
+	    n = INT_MIN;
+
 	// When adding an entry here, also update the default 'cinoptions' in
 	// doc/indent.txt, and add explanation for it!
 	switch (*l)
 	{
-	    case '>': buf->b_ind_level = n; break;
-	    case 'e': buf->b_ind_open_imag = n; break;
-	    case 'n': buf->b_ind_no_brace = n; break;
-	    case 'f': buf->b_ind_first_open = n; break;
-	    case '{': buf->b_ind_open_extra = n; break;
-	    case '}': buf->b_ind_close_extra = n; break;
-	    case '^': buf->b_ind_open_left_imag = n; break;
-	    case 'L': buf->b_ind_jump_label = n; break;
-	    case ':': buf->b_ind_case = n; break;
-	    case '=': buf->b_ind_case_code = n; break;
-	    case 'b': buf->b_ind_case_break = n; break;
-	    case 'p': buf->b_ind_param = n; break;
-	    case 't': buf->b_ind_func_type = n; break;
-	    case '/': buf->b_ind_comment = n; break;
-	    case 'c': buf->b_ind_in_comment = n; break;
-	    case 'C': buf->b_ind_in_comment2 = n; break;
-	    case 'i': buf->b_ind_cpp_baseclass = n; break;
-	    case '+': buf->b_ind_continuation = n; break;
-	    case '(': buf->b_ind_unclosed = n; break;
-	    case 'u': buf->b_ind_unclosed2 = n; break;
-	    case 'U': buf->b_ind_unclosed_noignore = n; break;
-	    case 'W': buf->b_ind_unclosed_wrapped = n; break;
-	    case 'w': buf->b_ind_unclosed_whiteok = n; break;
-	    case 'm': buf->b_ind_matching_paren = n; break;
-	    case 'M': buf->b_ind_paren_prev = n; break;
-	    case ')': buf->b_ind_maxparen = n; break;
-	    case '*': buf->b_ind_maxcomment = n; break;
-	    case 'g': buf->b_ind_scopedecl = n; break;
-	    case 'h': buf->b_ind_scopedecl_code = n; break;
-	    case 'j': buf->b_ind_java = n; break;
-	    case 'J': buf->b_ind_js = n; break;
-	    case 'l': buf->b_ind_keep_case_label = n; break;
-	    case '#': buf->b_ind_hash_comment = n; break;
-	    case 'N': buf->b_ind_cpp_namespace = n; break;
-	    case 'k': buf->b_ind_if_for_while = n; break;
-	    case 'E': buf->b_ind_cpp_extern_c = n; break;
-	    case 'P': buf->b_ind_pragma = n; break;
+	    case '>': buf->b_ind_level = (int)n; break;
+	    case 'e': buf->b_ind_open_imag = (int)n; break;
+	    case 'n': buf->b_ind_no_brace = (int)n; break;
+	    case 'f': buf->b_ind_first_open = (int)n; break;
+	    case '{': buf->b_ind_open_extra = (int)n; break;
+	    case '}': buf->b_ind_close_extra = (int)n; break;
+	    case '^': buf->b_ind_open_left_imag = (int)n; break;
+	    case 'L': buf->b_ind_jump_label = (int)n; break;
+	    case ':': buf->b_ind_case = (int)n; break;
+	    case '=': buf->b_ind_case_code = (int)n; break;
+	    case 'b': buf->b_ind_case_break = (int)n; break;
+	    case 'p': buf->b_ind_param = (int)n; break;
+	    case 't': buf->b_ind_func_type = (int)n; break;
+	    case '/': buf->b_ind_comment = (int)n; break;
+	    case 'c': buf->b_ind_in_comment = (int)n; break;
+	    case 'C': buf->b_ind_in_comment2 = (int)n; break;
+	    case 'i': buf->b_ind_cpp_baseclass = (int)n; break;
+	    case '+': buf->b_ind_continuation = (int)n; break;
+	    case '(': buf->b_ind_unclosed = (int)n; break;
+	    case 'u': buf->b_ind_unclosed2 = (int)n; break;
+	    case 'U': buf->b_ind_unclosed_noignore = (int)n; break;
+	    case 'W': buf->b_ind_unclosed_wrapped = (int)n; break;
+	    case 'w': buf->b_ind_unclosed_whiteok = (int)n; break;
+	    case 'm': buf->b_ind_matching_paren = (int)n; break;
+	    case 'M': buf->b_ind_paren_prev = (int)n; break;
+	    case ')': buf->b_ind_maxparen = (int)n; break;
+	    case '*': buf->b_ind_maxcomment = (int)n; break;
+	    case 'g': buf->b_ind_scopedecl = (int)n; break;
+	    case 'h': buf->b_ind_scopedecl_code = (int)n; break;
+	    case 'j': buf->b_ind_java = (int)n; break;
+	    case 'J': buf->b_ind_js = (int)n; break;
+	    case 'l': buf->b_ind_keep_case_label = (int)n; break;
+	    case '#': buf->b_ind_hash_comment = (int)n; break;
+	    case 'N': buf->b_ind_cpp_namespace = (int)n; break;
+	    case 'k': buf->b_ind_if_for_while = (int)n; break;
+	    case 'E': buf->b_ind_cpp_extern_c = (int)n; break;
+	    case 'P': buf->b_ind_pragma = (int)n; break;
 	}
 	if (*p == ',')
 	    ++p;
--- a/src/ops.c
+++ b/src/ops.c
@@ -230,8 +230,8 @@ shift_line(
     int call_changed_bytes)	// call changed_bytes()
 {
     long long	count;
-    int		i, j;
-    int		sw_val = (int)get_sw_value_indent(curbuf);
+    long	i, j;
+    long	sw_val = get_sw_value_indent(curbuf);
 
     count = (long long)get_indent();	// get current indent
 
--- a/src/testdir/test_indent.vim
+++ b/src/testdir/test_indent.vim
@@ -286,4 +286,18 @@ func Test_indent_overflow_count()
   close!
 endfunc
 
+func Test_indent_overflow_count2()
+  new
+  " this only works, when long is 64bits
+  try
+    setl sw=0x180000000
+  catch /^Vim\%((\a\+)\)\=:E487:/
+  throw 'Skipped: value negative on this platform'
+  endtry
+  call setline(1, "\tabc")
+  norm! <<
+  call assert_equal(0, indent(1))
+  close!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2122,
+/**/
     2121,
 /**/
     2120,