vim-patch:8.2.3659: integer overflow with large line number

Problem:    Integer overflow with large line number.
Solution:   Check for overflow. (closes vim/vim#9202)
03725c5795

Put E1247 in globals.h as E1240 is also there.
Do not make getdigits() abort.
This commit is contained in:
zeertzjq
2022-02-18 08:29:55 +08:00
parent 592f4a7c08
commit 3ed800e998
6 changed files with 85 additions and 11 deletions

View File

@@ -4141,7 +4141,11 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
if (!ascii_isdigit(*cmd)) { // '+' is '+1', but '+0' is not '+1'
n = 1;
} else {
n = getdigits(&cmd, true, 0);
n = getdigits(&cmd, false, MAXLNUM);
if (n == MAXLNUM) {
emsg(_(e_line_number_out_of_range));
goto error;
}
}
if (addr_type == ADDR_TABS_RELATIVE) {
@@ -4160,6 +4164,10 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
if (i == '-') {
lnum -= n;
} else {
if (n >= LONG_MAX - lnum) {
emsg(_(e_line_number_out_of_range));
goto error;
}
lnum += n;
}
}

View File

@@ -1002,6 +1002,8 @@ EXTERN char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cann
EXTERN char e_resulting_text_too_long[] INIT(= N_("E1240: Resulting text too long"));
EXTERN char e_line_number_out_of_range[] INIT(= N_("E1247: Line number out of range"));
EXTERN char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long"));
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));

View File

@@ -824,15 +824,12 @@ static bool normal_get_command_count(NormalState *s)
if (s->c == K_DEL || s->c == K_KDEL) {
s->ca.count0 /= 10;
del_from_showcmd(4); // delete the digit and ~@%
} else if (s->ca.count0 >= 999999999L) {
s->ca.count0 = 999999999L;
} else {
s->ca.count0 = s->ca.count0 * 10 + (s->c - '0');
}
if (s->ca.count0 < 0) {
// overflow
s->ca.count0 = 999999999L;
}
// Set v:count here, when called from main() and not a stuffed
// command, so that v:count can be used in an expression mapping
// right after the count. Do set it for redo.
@@ -1046,14 +1043,14 @@ static int normal_execute(VimState *state, int key)
// If you give a count before AND after the operator, they are
// multiplied.
if (s->ca.count0) {
s->ca.count0 = (long)((uint64_t)s->ca.count0 * (uint64_t)s->ca.opcount);
if (s->ca.opcount >= 999999999L / s->ca.count0) {
s->ca.count0 = 999999999L;
} else {
s->ca.count0 *= s->ca.opcount;
}
} else {
s->ca.count0 = s->ca.opcount;
}
if (s->ca.count0 < 0) {
// overflow
s->ca.count0 = 999999999L;
}
}
// Always remember the count. It will be set to zero (on the next call,

View File

@@ -409,4 +409,18 @@ func Test_not_break_expression_register()
call assert_equal('1+1', getreg('=', 1))
endfunc
func Test_address_line_overflow()
throw 'Skipped: v:sizeoflong is N/A' " use legacy/excmd_spec.lua instead
if v:sizeoflong < 8
throw 'Skipped: only works with 64 bit long ints'
endif
new
call setline(1, 'text')
call assert_fails('|.44444444444444444444444', 'E1247:')
call assert_fails('|.9223372036854775806', 'E1247:')
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -2779,4 +2779,25 @@ func Test_normal_gj_on_extra_wide_char()
bw!
endfunc
func Test_normal_count_out_of_range()
new
call setline(1, 'text')
normal 44444444444|
call assert_equal(999999999, v:count)
normal 444444444444|
call assert_equal(999999999, v:count)
normal 4444444444444|
call assert_equal(999999999, v:count)
normal 4444444444444444444|
call assert_equal(999999999, v:count)
normal 9y99999999|
call assert_equal(899999991, v:count)
normal 10y99999999|
call assert_equal(999999999, v:count)
normal 44444444444y44444444444|
call assert_equal(999999999, v:count)
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -0,0 +1,32 @@
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('Ex command', function()
before_each(clear)
after_each(function() eq({}, meths.get_vvar('errors')) end)
it('checks for address line overflow', function()
if sizeoflong() < 8 then
pending('Skipped: only works with 64 bit long ints')
end
source [[
new
call setline(1, 'text')
call assert_fails('|.44444444444444444444444', 'E1247:')
call assert_fails('|.9223372036854775806', 'E1247:')
bwipe!
]]
end)
end)