mirror of
https://github.com/neovim/neovim.git
synced 2025-09-28 05:58:33 +00:00
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)
fa53722367
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.
This commit is contained in:
@@ -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)
|
totlen = (size_t)(count * (yanklen + spaces)
|
||||||
+ bd.startspaces + bd.endspaces);
|
+ bd.startspaces + bd.endspaces);
|
||||||
int addcount = (int)totlen + lines_appended;
|
int addcount = (int)totlen + lines_appended;
|
||||||
newp = (char_u *)xmalloc(totlen + oldlen + 1);
|
newp = (char_u *)xmalloc(totlen + oldlen + 1);
|
||||||
|
|
||||||
// copy part up to cursor to new line
|
// copy part up to cursor to new line
|
||||||
ptr = newp;
|
ptr = newp;
|
||||||
memmove(ptr, oldp, (size_t)bd.textcol);
|
memmove(ptr, oldp, (size_t)bd.textcol);
|
||||||
ptr += bd.textcol;
|
ptr += bd.textcol;
|
||||||
|
|
||||||
// may insert some spaces before the new text
|
// may insert some spaces before the new text
|
||||||
memset(ptr, ' ', (size_t)bd.startspaces);
|
memset(ptr, ' ', (size_t)bd.startspaces);
|
||||||
ptr += bd.startspaces;
|
ptr += bd.startspaces;
|
||||||
|
|
||||||
// insert the new text
|
// insert the new text
|
||||||
for (long j = 0; j < count; j++) {
|
for (long j = 0; j < count; j++) {
|
||||||
memmove(ptr, y_array[i], (size_t)yanklen);
|
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;
|
addcount -= spaces;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// may insert some spaces after the new text
|
// may insert some spaces after the new text
|
||||||
memset(ptr, ' ', (size_t)bd.endspaces);
|
memset(ptr, ' ', (size_t)bd.endspaces);
|
||||||
ptr += bd.endspaces;
|
ptr += bd.endspaces;
|
||||||
|
|
||||||
// move the text after the cursor to the end of the line.
|
// move the text after the cursor to the end of the line.
|
||||||
int columns = (int)oldlen - bd.textcol - delcount + 1;
|
int columns = (int)oldlen - bd.textcol - delcount + 1;
|
||||||
assert(columns >= 0);
|
assert(columns >= 0);
|
||||||
@@ -3430,15 +3442,18 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
if (count == 0 || yanklen == 0) {
|
||||||
const double multlen = (double)count * (double)yanklen;
|
if (VIsual_active) {
|
||||||
|
lnum = end_lnum;
|
||||||
totlen = (size_t)(int)(count * yanklen);
|
}
|
||||||
if ((double)totlen != multlen) {
|
} else if (count > INT_MAX / yanklen) {
|
||||||
emsg(_(e_resulting_text_too_long));
|
// multiplication overflow
|
||||||
break;
|
emsg(_(e_resulting_text_too_long));
|
||||||
} else if (totlen > 0) {
|
} else {
|
||||||
|
totlen = (size_t)(count * yanklen);
|
||||||
|
do {
|
||||||
oldp = ml_get(lnum);
|
oldp = ml_get(lnum);
|
||||||
|
oldlen = STRLEN(oldp);
|
||||||
if (lnum > start_lnum) {
|
if (lnum > start_lnum) {
|
||||||
pos_T pos = {
|
pos_T pos = {
|
||||||
.lnum = lnum,
|
.lnum = lnum,
|
||||||
@@ -3449,11 +3464,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
|
|||||||
col = MAXCOL;
|
col = MAXCOL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (VIsual_active && col > (int)STRLEN(oldp)) {
|
if (VIsual_active && col > (colnr_T)oldlen) {
|
||||||
lnum++;
|
lnum++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
newp = (char_u *)xmalloc((size_t)(STRLEN(oldp) + totlen + 1));
|
newp = (char_u *)xmalloc(totlen + oldlen + 1);
|
||||||
memmove(newp, oldp, (size_t)col);
|
memmove(newp, oldp, (size_t)col);
|
||||||
ptr = newp + col;
|
ptr = newp + col;
|
||||||
for (i = 0; i < (size_t)count; i++) {
|
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);
|
changed_bytes(lnum, col);
|
||||||
extmark_splice_cols(curbuf, (int)lnum-1, col,
|
extmark_splice_cols(curbuf, (int)lnum-1, col,
|
||||||
0, (int)totlen, kExtmarkUndo);
|
0, (int)totlen, kExtmarkUndo);
|
||||||
}
|
if (VIsual_active) {
|
||||||
if (VIsual_active) {
|
lnum++;
|
||||||
lnum++;
|
}
|
||||||
}
|
} while (VIsual_active && lnum <= end_lnum);
|
||||||
} while (VIsual_active && lnum <= end_lnum);
|
|
||||||
|
|
||||||
if (VIsual_active) { // reset lnum to the last visual line
|
if (VIsual_active) { // reset lnum to the last visual line
|
||||||
lnum--;
|
lnum--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// put '] at the first byte of the last character
|
// put '] at the first byte of the last character
|
||||||
|
@@ -139,10 +139,18 @@ func Test_p_with_count_leaves_mark_at_end()
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_very_large_count()
|
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
|
||||||
|
|
||||||
if v:sizeofint != 8
|
func Test_very_large_count_64bit()
|
||||||
throw 'Skipped: only works with 64 bit ints'
|
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
|
endif
|
||||||
|
|
||||||
new
|
new
|
||||||
@@ -151,6 +159,29 @@ func Test_very_large_count()
|
|||||||
bwipe!
|
bwipe!
|
||||||
endfunc
|
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()
|
||||||
|
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 \<C-V>y"
|
||||||
|
call assert_fails('norm 44444444444444p', 'E1240:')
|
||||||
|
bwipe!
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_put_above_first_line()
|
func Test_put_above_first_line()
|
||||||
new
|
new
|
||||||
let @" = 'text'
|
let @" = 'text'
|
||||||
|
45
test/functional/legacy/put_spec.lua
Normal file
45
test/functional/legacy/put_spec.lua
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
local clear = helpers.clear
|
||||||
|
local exec_lua = helpers.exec_lua
|
||||||
|
local meths = helpers.meths
|
||||||
|
local source = helpers.source
|
||||||
|
local eq = helpers.eq
|
||||||
|
|
||||||
|
local function sizeoflong()
|
||||||
|
if not exec_lua('return pcall(require, "ffi")') then
|
||||||
|
pending('missing LuaJIT FFI')
|
||||||
|
end
|
||||||
|
return exec_lua('return require("ffi").sizeof(require("ffi").typeof("long"))')
|
||||||
|
end
|
||||||
|
|
||||||
|
describe('put', function()
|
||||||
|
before_each(clear)
|
||||||
|
after_each(function() eq({}, meths.get_vvar('errors')) end)
|
||||||
|
|
||||||
|
it('very large count 64-bit', function()
|
||||||
|
if sizeoflong() < 8 then
|
||||||
|
pending('Skipped: only works with 64 bit long ints')
|
||||||
|
end
|
||||||
|
|
||||||
|
source [[
|
||||||
|
new
|
||||||
|
let @" = 'x'
|
||||||
|
call assert_fails('norm 44444444444444p', 'E1240:')
|
||||||
|
bwipe!
|
||||||
|
]]
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('very large count (visual block) 64-bit', function()
|
||||||
|
if sizeoflong() < 8 then
|
||||||
|
pending('Skipped: only works with 64 bit long ints')
|
||||||
|
end
|
||||||
|
|
||||||
|
source [[
|
||||||
|
new
|
||||||
|
call setline(1, 'x')
|
||||||
|
exe "norm \<C-V>y"
|
||||||
|
call assert_fails('norm 44444444444444p', 'E1240:')
|
||||||
|
bwipe!
|
||||||
|
]]
|
||||||
|
end)
|
||||||
|
end)
|
Reference in New Issue
Block a user