From ac5856b3f515a39f5047f47d9c675a6206ce19bf Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 15 Feb 2022 18:04:22 +0000 Subject: vim-patch:8.2.3492: crash when pasting too many times Problem: Crash when pasting too many times. Solution: Limit the size to what fits in an int. (closes vim/vim#8962) https://github.com/vim/vim/commit/eeed1c7ae090c17f4df51cf97b2a9e4d8b4f4dc7 Note that this overflow check pretty bad. It also doesn't work well on Windows (where sizeof(int) == sizeof(long)). This is all temporary; everything here is rewritten in future patches anyway. e_resulting_text_too_long was already cherry-picked. totlen is size_t in Nvim, but is int in Vim. This means we'll need some casts. We could technically adjust the logic in do_put to use the entire range of size_t in stuff like totlen, but there's not much gain, and it's much easier to just port the patch like Vim as was done before (also allows us to use the same tests). --- src/nvim/ops.c | 9 +++++++-- src/nvim/testdir/test_put.vim | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 18facef13c..e6e617a419 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3431,8 +3431,13 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } do { - totlen = (size_t)(count * yanklen); - if (totlen > 0) { + const long multlen = count * yanklen; + + totlen = (size_t)(int)multlen; + if (totlen != (size_t)multlen) { + emsg(_(e_resulting_text_too_long)); + break; + } else if (totlen > 0) { oldp = ml_get(lnum); if (lnum > start_lnum) { pos_T pos = { diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index ed76709a56..cef2cf0dd7 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -138,6 +138,13 @@ func Test_p_with_count_leaves_mark_at_end() bwipe! endfunc +func Test_very_larg_count() + new + let @" = 'x' + call assert_fails('norm 44444444444444p', 'E1240:') + bwipe! +endfunc + func Test_put_above_first_line() new let @" = 'text' -- cgit From 3fba994de890625eeebf7693a13d4e38cfbea0c7 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 21:21:05 +0000 Subject: vim-patch:8.2.3493: large count test fails on MS-Windows Problem: Large count test fails on MS-Windows. Solution: Skip the test on MS-Windows. https://github.com/vim/vim/commit/cddd5ac911707034ca27f10037c4b1b523188c47 --- src/nvim/testdir/test_put.vim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index cef2cf0dd7..13c00b3b71 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -138,7 +138,10 @@ func Test_p_with_count_leaves_mark_at_end() bwipe! endfunc -func Test_very_larg_count() +func Test_very_large_count() + " FIXME: should actually check if sizeof(int) == sizeof(long) + CheckNotMSWindows + new let @" = 'x' call assert_fails('norm 44444444444444p', 'E1240:') -- cgit From a256b710a26db5c08447eee3e602b86c13c78b06 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 21:38:27 +0000 Subject: vim-patch:8.2.3570: Test_very_large_count fails on 32bit systems Problem: Test_very_large_count fails on 32bit systems. Solution: Bail out when using 32 bit numbers. (closes vim/vim#9072) https://github.com/vim/vim/commit/ec6e63079dde24a1d74b4103775e74d00f9215ec --- src/nvim/testdir/test_put.vim | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index 13c00b3b71..a159687a76 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -142,6 +142,10 @@ func Test_very_large_count() " FIXME: should actually check if sizeof(int) == sizeof(long) CheckNotMSWindows + if v:numbersize != 64 + throw 'Skipped: only works with 64 bit numbers' + endif + new let @" = 'x' call assert_fails('norm 44444444444444p', 'E1240:') -- cgit From b149665689f84ee7297ab5ce8a8eb59b12611af1 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 21:23:08 +0000 Subject: vim-patch:8.2.3573: cannot decide whether to skip test that fails with 64 bit Problem: Cannot decide whether to skip test that fails with 64 bit ints. (closes vim/vim#9072) Solution: Add v:sizeofint, v:sizeoflong and v:sizeofpointer. Improve the check for multiply overflow. https://github.com/vim/vim/commit/69b3072d984480935ec412b32b97fea974d2b689 Omit v:sizeof{int,long,pointer} as they're only really used for tests. --- src/nvim/ops.c | 3 ++- src/nvim/testdir/test_put.vim | 7 ------- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index e6e617a419..2218b079b0 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3434,7 +3434,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) const long multlen = count * yanklen; totlen = (size_t)(int)multlen; - if (totlen != (size_t)multlen) { + if (totlen != (size_t)multlen || (long)totlen / count != yanklen + || (long)totlen / yanklen != count) { emsg(_(e_resulting_text_too_long)); break; } else if (totlen > 0) { diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index a159687a76..bf222477a2 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -139,13 +139,6 @@ func Test_p_with_count_leaves_mark_at_end() endfunc func Test_very_large_count() - " FIXME: should actually check if sizeof(int) == sizeof(long) - CheckNotMSWindows - - if v:numbersize != 64 - throw 'Skipped: only works with 64 bit numbers' - endif - new let @" = 'x' call assert_fails('norm 44444444444444p', 'E1240:') -- cgit From de8e2c61c1b273f57a5a2d85c474b4f12b1b8994 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 22:19:06 +0000 Subject: vim-patch:8.2.3574: divide by zero Problem: Divide by zero. Solution: Don't check for overflow if multiplicand is zero. https://github.com/vim/vim/commit/8a1962d1355096af55e84b1ea2f0baf5f1c5a5bc --- src/nvim/ops.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 2218b079b0..8dc367d572 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3434,8 +3434,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) const long multlen = count * yanklen; totlen = (size_t)(int)multlen; - if (totlen != (size_t)multlen || (long)totlen / count != yanklen - || (long)totlen / yanklen != count) { + if (count != 0 && yanklen != 0 + && (totlen != (size_t)multlen || (long)totlen / count != yanklen + || (long)totlen / yanklen != count)) { emsg(_(e_resulting_text_too_long)); break; } else if (totlen > 0) { -- cgit From 6890f8774bf6ec56b645c9b6389cdd1e4ed823ed Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 22:23:31 +0000 Subject: vim-patch:8.2.3575: overflow check still fails when sizeof(int) == sizeof(long) Problem: Overflow check still fails when sizeof(int) == sizeof(long). Solution: Use a float to check the result. https://github.com/vim/vim/commit/e551ccfb9311eea5252d1c3106ff7a53c762d994 This approach is... interesting... Tests fail. --- src/nvim/ops.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 8dc367d572..ea480c0b31 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3431,12 +3431,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } do { - const long multlen = count * yanklen; + const double multlen = (double)count * (double)yanklen; - totlen = (size_t)(int)multlen; - if (count != 0 && yanklen != 0 - && (totlen != (size_t)multlen || (long)totlen / count != yanklen - || (long)totlen / yanklen != count)) { + totlen = (size_t)(int)(count * yanklen); + if ((double)totlen != multlen) { emsg(_(e_resulting_text_too_long)); break; } else if (totlen > 0) { -- cgit From 308c1952aa55d63a1643dea798f5143eb41e8ed4 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 22:31:40 +0000 Subject: vim-patch:8.2.3577: overflow check fails with 32 ints Problem: Overflow check fails with 32 ints. Solution: Only test with 64 bit ints. https://github.com/vim/vim/commit/0f0044125c2a5dcde2c4605efc39d2e237eed024 --- src/nvim/testdir/test_put.vim | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index bf222477a2..a71224785a 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -139,6 +139,12 @@ func Test_p_with_count_leaves_mark_at_end() endfunc func Test_very_large_count() + throw 'Skipped: v:sizeofint is N/A' + + if v:sizeofint != 8 + throw 'Skipped: only works with 64 bit ints' + endif + new let @" = 'x' call assert_fails('norm 44444444444444p', 'E1240:') -- cgit From 8170260bb35f3761d2008405289832b2620abc53 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 17 Feb 2022 01:43:18 +0000 Subject: fix(ops): str_to_reg passing NULL to memcpy Required for the tests introduced in v8.2.3601 to pass ASAN when running test_alot.vim. Co-authored-by: erw7 --- src/nvim/ops.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index ea480c0b31..95573799e5 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -5696,7 +5696,9 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str // When appending, copy the previous line and free it after. size_t extra = append ? STRLEN(pp[--lnum]) : 0; char_u *s = xmallocz(line_len + extra); - memcpy(s, pp[lnum], extra); + if (extra > 0) { + memcpy(s, pp[lnum], extra); + } memcpy(s + extra, start, line_len); size_t s_len = extra + line_len; -- cgit From 41d0e7af2097e0374ab16fb1567cf22d21aad180 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 22:37:10 +0000 Subject: vim-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 vim/vim#9102) https://github.com/vim/vim/commit/fa53722367c3793fda95dac665af74b8651065e9 Add some casts as Nvim uses size_t variables in some places. We could technically adjust the logic to check for overflow outside of size_t's range, but it's much easier to just port the patch exactly (also means we can use the same tests). v:sizeoflong is N/A, so convert the 64-bit tests to Lua and use the FFI to check long's size. --- src/nvim/ops.c | 51 ++++++++++++++++++++++++++++--------------- src/nvim/testdir/test_put.vim | 37 ++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 95573799e5..b5c7020dee 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3314,18 +3314,28 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } - // 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 = (size_t)(count * (yanklen + spaces) + bd.startspaces + bd.endspaces); int addcount = (int)totlen + lines_appended; newp = (char_u *)xmalloc(totlen + oldlen + 1); + // copy part up to cursor to new line ptr = newp; memmove(ptr, oldp, (size_t)bd.textcol); ptr += bd.textcol; + // may insert some spaces before the new text memset(ptr, ' ', (size_t)bd.startspaces); ptr += bd.startspaces; + // insert the new text for (long j = 0; j < count; j++) { memmove(ptr, y_array[i], (size_t)yanklen); @@ -3339,9 +3349,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) addcount -= spaces; } } + // may insert some spaces after the new text memset(ptr, ' ', (size_t)bd.endspaces); ptr += bd.endspaces; + // move the text after the cursor to the end of the line. int columns = (int)oldlen - bd.textcol - delcount + 1; assert(columns >= 0); @@ -3430,15 +3442,18 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } - do { - const double multlen = (double)count * (double)yanklen; - - totlen = (size_t)(int)(count * yanklen); - if ((double)totlen != multlen) { - emsg(_(e_resulting_text_too_long)); - break; - } else if (totlen > 0) { + 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 = (size_t)(count * yanklen); + do { oldp = ml_get(lnum); + oldlen = STRLEN(oldp); if (lnum > start_lnum) { pos_T pos = { .lnum = lnum, @@ -3449,11 +3464,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) col = MAXCOL; } } - if (VIsual_active && col > (int)STRLEN(oldp)) { + if (VIsual_active && col > (colnr_T)oldlen) { lnum++; continue; } - newp = (char_u *)xmalloc((size_t)(STRLEN(oldp) + totlen + 1)); + newp = (char_u *)xmalloc(totlen + oldlen + 1); memmove(newp, oldp, (size_t)col); ptr = newp + col; for (i = 0; i < (size_t)count; i++) { @@ -3475,14 +3490,14 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) changed_bytes(lnum, col); extmark_splice_cols(curbuf, (int)lnum-1, col, 0, (int)totlen, kExtmarkUndo); - } - 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--; + } } // put '] at the first byte of the last character diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index a71224785a..9f2fc999a7 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -139,10 +139,18 @@ func Test_p_with_count_leaves_mark_at_end() endfunc func Test_very_large_count() - throw 'Skipped: v:sizeofint is N/A' + 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() + throw 'Skipped: v:sizeoflong is N/A' " use legacy/put_spec.lua instead - if v:sizeofint != 8 - throw 'Skipped: only works with 64 bit ints' + if v:sizeoflong < 8 + throw 'Skipped: only works with 64 bit long ints' endif new @@ -151,6 +159,29 @@ 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() + throw 'Skipped: v:sizeoflong is N/A' " use legacy/put_spec.lua instead + + 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' -- cgit