changeset 26137:14a55d1520f2 v8.2.3601

patch 8.2.3601: check for overflow in put count does not work well Commit: https://github.com/vim/vim/commit/fa53722367c3793fda95dac665af74b8651065e9 Author: ichizok <gclient.gaap@gmail.com> Date: Tue Nov 16 12:50:46 2021 +0000 patch 8.2.3601: check for overflow in put count does not work well Problem: Check for overflow in put count does not work well. Solution: Improve the overflow check. (Ozaki Kiichi, closes https://github.com/vim/vim/issues/9102)
author Bram Moolenaar <Bram@vim.org>
date Tue, 16 Nov 2021 14:00:04 +0100
parents 8492808608d5
children b19a063a7a74
files src/register.c src/testdir/test_put.vim src/version.c
diffstat 3 files changed, 68 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/src/register.c
+++ b/src/register.c
@@ -1884,18 +1884,30 @@ do_put(
 		    spaces = 0;
 	    }
 
-	    // insert the new text
+	    // Insert the new text.
+	    // First check for multiplication overflow.
+	    if (yanklen + spaces != 0
+		     && count > ((INT_MAX - (bd.startspaces + bd.endspaces))
+							/ (yanklen + spaces)))
+	    {
+		emsg(_(e_resulting_text_too_long));
+		break;
+	    }
+
 	    totlen = count * (yanklen + spaces) + bd.startspaces + bd.endspaces;
 	    newp = alloc(totlen + oldlen + 1);
 	    if (newp == NULL)
 		break;
+
 	    // copy part up to cursor to new line
 	    ptr = newp;
 	    mch_memmove(ptr, oldp, (size_t)bd.textcol);
 	    ptr += bd.textcol;
+
 	    // may insert some spaces before the new text
 	    vim_memset(ptr, ' ', (size_t)bd.startspaces);
 	    ptr += bd.startspaces;
+
 	    // insert the new text
 	    for (j = 0; j < count; ++j)
 	    {
@@ -1909,9 +1921,11 @@ do_put(
 		    ptr += spaces;
 		}
 	    }
+
 	    // may insert some spaces after the new text
 	    vim_memset(ptr, ' ', (size_t)bd.endspaces);
 	    ptr += bd.endspaces;
+
 	    // move the text after the cursor to the end of the line.
 	    mch_memmove(ptr, oldp + bd.textcol + delcount,
 				(size_t)(oldlen - bd.textcol - delcount + 1));
@@ -2010,26 +2024,20 @@ do_put(
 		}
 	    }
 
-	    do {
-#ifdef FEAT_FLOAT
-		double multlen = (double)count * (double)yanklen;
-
+	    if (count == 0 || yanklen == 0)
+	    {
+		if (VIsual_active)
+		    lnum = end_lnum;
+	    }
+	    else if (count > INT_MAX / yanklen)
+		// multiplication overflow
+		emsg(_(e_resulting_text_too_long));
+	    else
+	    {
 		totlen = count * yanklen;
-		if ((double)totlen != multlen)
-#else
-		long multlen = count * yanklen;
-
-		// this only works when sizeof(int) != sizeof(long)
-		totlen = multlen;
-		if (totlen != multlen)
-#endif
-		{
-		    emsg(_(e_resulting_text_too_long));
-		    break;
-		}
-		else if (totlen > 0)
-		{
+		do {
 		    oldp = ml_get(lnum);
+		    oldlen = (int)STRLEN(oldp);
 		    if (lnum > start_lnum)
 		    {
 			pos_T   pos;
@@ -2040,12 +2048,12 @@ do_put(
 			else
 			    col = MAXCOL;
 		    }
-		    if (VIsual_active && col > (int)STRLEN(oldp))
+		    if (VIsual_active && col > oldlen)
 		    {
 			lnum++;
 			continue;
 		    }
-		    newp = alloc(STRLEN(oldp) + totlen + 1);
+		    newp = alloc(totlen + oldlen + 1);
 		    if (newp == NULL)
 			goto end;	// alloc() gave an error message
 		    mch_memmove(newp, oldp, (size_t)col);
@@ -2064,13 +2072,13 @@ do_put(
 			changed_cline_bef_curs();
 			curwin->w_cursor.col += (colnr_T)(totlen - 1);
 		    }
-		}
-		if (VIsual_active)
-		    lnum++;
-	    } while (VIsual_active && lnum <= end_lnum);
+		    if (VIsual_active)
+			lnum++;
+		} while (VIsual_active && lnum <= end_lnum);
 
-	    if (VIsual_active) // reset lnum to the last visual line
-		lnum--;
+		if (VIsual_active) // reset lnum to the last visual line
+		    lnum--;
+	    }
 
 	    curbuf->b_op_end = curwin->w_cursor;
 	    // For "CTRL-O p" in Insert mode, put cursor after last char
--- a/src/testdir/test_put.vim
+++ b/src/testdir/test_put.vim
@@ -149,8 +149,16 @@ func Test_p_with_count_leaves_mark_at_en
 endfunc
 
 func Test_very_large_count()
-  if v:sizeofint != 8
-    throw 'Skipped: only works with 64 bit ints'
+  new
+  " total put-length (21474837 * 100) brings 32 bit int overflow
+  let @" = repeat('x', 100)
+  call assert_fails('norm 21474837p', 'E1240:')
+  bwipe!
+endfunc
+
+func Test_very_large_count_64bit()
+  if v:sizeoflong < 8
+    throw 'Skipped: only works with 64 bit long ints'
   endif
 
   new
@@ -159,6 +167,27 @@ func Test_very_large_count()
   bwipe!
 endfunc
 
+func Test_very_large_count_block()
+  new
+  " total put-length (21474837 * 100) brings 32 bit int overflow
+  call setline(1, repeat('x', 100))
+  exe "norm \<C-V>99ly"
+  call assert_fails('norm 21474837p', 'E1240:')
+  bwipe!
+endfunc
+
+func Test_very_large_count_block_64bit()
+  if v:sizeoflong < 8
+    throw 'Skipped: only works with 64 bit long ints'
+  endif
+
+  new
+  call setline(1, 'x')
+  exe "norm \<C-V>y"
+  call assert_fails('norm 44444444444444p', 'E1240:')
+  bwipe!
+endfunc
+
 func Test_put_above_first_line()
   new
   let @" = 'text'
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3601,
+/**/
     3600,
 /**/
     3599,