vim-patch:9.0.2107: [security]: FPE in adjust_plines_for_skipcol (#26082)

Problem:  [security]: FPE in adjust_plines_for_skipcol
Solution: don't divide by zero, return zero

Prevent a floating point exception when calculating w_skipcol (which can
happen with a small window when the number option is set and cpo+=n).

Add a test to verify

cb0b99f067

Co-authored-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
zeertzjq 2023-11-17 08:40:02 +08:00 committed by GitHub
parent 133a592d19
commit 6952b1951b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 61 additions and 28 deletions

View File

@ -262,9 +262,9 @@ static void changed_common(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnum
&& (last < wp->w_topline
|| (wp->w_topline >= lnum
&& wp->w_topline < lnume
&& win_linetabsize(wp, wp->w_topline, ml_get(wp->w_topline), (colnr_T)MAXCOL)
<= (unsigned)(wp->w_skipcol + sms_marker_overlap(wp, win_col_off(wp)
- win_col_off2(wp)))))) {
&& win_linetabsize(wp, wp->w_topline, ml_get(wp->w_topline), MAXCOL)
<= (wp->w_skipcol
+ sms_marker_overlap(wp, win_col_off(wp) - win_col_off2(wp)))))) {
wp->w_skipcol = 0;
}

View File

@ -111,7 +111,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
col = wcol;
if ((addspaces || finetune) && !VIsual_active) {
curwin->w_curswant = (int)linetabsize(curwin, pos->lnum) + one_more;
curwin->w_curswant = linetabsize(curwin, pos->lnum) + one_more;
if (curwin->w_curswant > 0) {
curwin->w_curswant--;
}
@ -125,7 +125,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
&& curwin->w_width_inner != 0
&& wcol >= (colnr_T)width
&& width > 0) {
csize = (int)linetabsize(curwin, pos->lnum);
csize = linetabsize(curwin, pos->lnum);
if (csize > 0) {
csize--;
}

View File

@ -65,8 +65,9 @@ int adjust_plines_for_skipcol(win_T *wp)
}
int width = wp->w_width_inner - win_col_off(wp);
if (wp->w_skipcol >= width) {
return (wp->w_skipcol - width) / (width + win_col_off2(wp)) + 1;
int w2 = width + win_col_off2(wp);
if (wp->w_skipcol >= width && w2 > 0) {
return (wp->w_skipcol - width) / w2 + 1;
}
return 0;
@ -1238,8 +1239,8 @@ bool scrolldown(linenr_T line_count, int byfold)
curwin->w_topline = first;
} else {
if (do_sms) {
int size = (int)win_linetabsize(curwin, curwin->w_topline,
ml_get(curwin->w_topline), (colnr_T)MAXCOL);
int size = win_linetabsize(curwin, curwin->w_topline,
ml_get(curwin->w_topline), MAXCOL);
if (size > width1) {
curwin->w_skipcol = width1;
size -= width1;
@ -1335,7 +1336,7 @@ bool scrollup(linenr_T line_count, int byfold)
if (do_sms || (byfold && hasAnyFolding(curwin)) || win_may_fill(curwin)) {
int width1 = curwin->w_width_inner - curwin_col_off();
int width2 = width1 + curwin_col_off2();
unsigned size = 0;
int size = 0;
const colnr_T prev_skipcol = curwin->w_skipcol;
if (do_sms) {
@ -1360,7 +1361,7 @@ bool scrollup(linenr_T line_count, int byfold)
// the end of the line, then advance to the next line.
int add = curwin->w_skipcol > 0 ? width2 : width1;
curwin->w_skipcol += add;
if ((unsigned)curwin->w_skipcol >= size) {
if (curwin->w_skipcol >= size) {
if (lnum == curbuf->b_ml.ml_line_count) {
// at the last screen line, can't scroll further
curwin->w_skipcol -= add;

View File

@ -2466,7 +2466,7 @@ bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_ar
/// @return true if able to move cursor, false otherwise.
static bool nv_screengo(oparg_T *oap, int dir, int dist)
{
int linelen = (int)linetabsize(curwin, curwin->w_cursor.lnum);
int linelen = linetabsize(curwin, curwin->w_cursor.lnum);
bool retval = true;
bool atend = false;
int col_off1; // margin offset for first screen line
@ -2530,7 +2530,7 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist)
}
cursor_up_inner(curwin, 1);
linelen = (int)linetabsize(curwin, curwin->w_cursor.lnum);
linelen = linetabsize(curwin, curwin->w_cursor.lnum);
if (linelen > width1) {
int w = (((linelen - width1 - 1) / width2) + 1) * width2;
assert(curwin->w_curswant <= INT_MAX - w);
@ -2563,7 +2563,7 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist)
if (curwin->w_curswant >= width1) {
curwin->w_curswant -= width2;
}
linelen = (int)linetabsize(curwin, curwin->w_cursor.lnum);
linelen = linetabsize(curwin, curwin->w_cursor.lnum);
}
}
}
@ -5491,7 +5491,7 @@ static void nv_g_cmd(cmdarg_T *cap)
case 'M':
oap->motion_type = kMTCharWise;
oap->inclusive = false;
i = (int)linetabsize(curwin, curwin->w_cursor.lnum);
i = linetabsize(curwin, curwin->w_cursor.lnum);
if (cap->count0 > 0 && cap->count0 <= 100) {
coladvance((colnr_T)(i * cap->count0 / 100));
} else {

View File

@ -83,18 +83,18 @@ int linetabsize_col(int startcol, char *s)
/// @param len
///
/// @return Number of characters the string will take on the screen.
unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
{
chartabsize_T cts;
init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
win_linetabsize_cts(&cts, len);
clear_chartabsize_arg(&cts);
return (unsigned)cts.cts_vcol;
return cts.cts_vcol;
}
/// Return the number of cells line "lnum" of window "wp" will take on the
/// screen, taking into account the size of a tab and inline virtual text.
unsigned linetabsize(win_T *wp, linenr_T lnum)
int linetabsize(win_T *wp, linenr_T lnum)
{
return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum), (colnr_T)MAXCOL);
}

View File

@ -1294,9 +1294,7 @@ static bool reg_match_visual(void)
rex.line = (uint8_t *)reg_getline(rex.lnum);
rex.input = rex.line + col;
unsigned cols_u = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, (char *)rex.line, col);
assert(cols_u <= MAXCOL);
colnr_T cols = (colnr_T)cols_u;
colnr_T cols = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, (char *)rex.line, col);
if (cols < start || cols > end - (*p_sel == 'e')) {
return false;
}
@ -6029,11 +6027,10 @@ static bool regmatch(uint8_t *scan, proftime_T *tm, int *timed_out)
break;
case RE_VCOL:
if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL
? curwin : rex.reg_win,
rex.reg_firstlnum + rex.lnum,
(char *)rex.line,
(colnr_T)(rex.input - rex.line)) + 1,
if (!re_num_cmp((unsigned)win_linetabsize(rex.reg_win == NULL ? curwin : rex.reg_win,
rex.reg_firstlnum + rex.lnum,
(char *)rex.line,
(colnr_T)(rex.input - rex.line)) + 1,
scan)) {
status = RA_NOMATCH;
}
@ -14754,9 +14751,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
result = col > t->state->val * ts;
}
if (!result) {
uintmax_t lts = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, (char *)rex.line, col);
int lts = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, (char *)rex.line, col);
assert(t->state->val >= 0);
result = nfa_re_num_cmp((uintmax_t)t->state->val, op, lts + 1);
result = nfa_re_num_cmp((uintmax_t)t->state->val, op, (uintmax_t)lts + 1);
}
if (result) {
add_here = true;

View File

@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local exec = helpers.exec
local feed = helpers.feed
local assert_alive = helpers.assert_alive
before_each(clear)
@ -1007,6 +1008,21 @@ describe('smoothscroll', function()
]])
end)
-- oldtest: Test_smoothscroll_crash()
it('does not crash with small window and cpo+=n', function()
screen:try_resize(40, 12)
exec([[
20 new
vsp
put =repeat('aaaa', 20)
set nu fdc=1 smoothscroll cpo+=n
vert resize 0
exe "norm! 0\<c-e>"
]])
feed('2<C-E>')
assert_alive()
end)
it("works with virt_lines above and below", function()
screen:try_resize(55, 7)
exec([=[

View File

@ -929,4 +929,23 @@ func Test_smoothscroll_cursor_top()
call StopVimInTerminal(buf)
endfunc
" Division by zero, shouldn't crash
func Test_smoothscroll_crash()
CheckScreendump
let lines =<< trim END
20 new
vsp
put =repeat('aaaa', 20)
set nu fdc=1 smoothscroll cpo+=n
vert resize 0
exe "norm! 0\<c-e>"
END
call writefile(lines, 'XSmoothScrollCrash', 'D')
let buf = RunVimInTerminal('-u NONE -S XSmoothScrollCrash', #{rows: 12, cols:40})
call term_sendkeys(buf, "2\<C-E>\<C-L>")
call StopVimInTerminal(buf)
endfunc
" vim: shiftwidth=2 sts=2 expandtab