# HG changeset patch # User Bram Moolenaar # Date 1637067604 -3600 # Node ID 14a55d1520f29e120be37953add0c12826618eb3 # Parent 8492808608d5bb42eb54e01955faef6059002bbe patch 8.2.3601: check for overflow in put count does not work well Commit: https://github.com/vim/vim/commit/fa53722367c3793fda95dac665af74b8651065e9 Author: ichizok 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) diff --git a/src/register.c b/src/register.c --- 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 diff --git a/src/testdir/test_put.vim b/src/testdir/test_put.vim --- 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 \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 \y" + call assert_fails('norm 44444444444444p', 'E1240:') + bwipe! +endfunc + func Test_put_above_first_line() new let @" = 'text' diff --git a/src/version.c b/src/version.c --- 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,