diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-11-17 08:40:02 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-17 08:40:02 +0800 |
commit | 6952b1951b6a60df8d477279f4451094fb51c413 (patch) | |
tree | b9ef8b5821c4c263f6d390ffeb4fa15b5f3ee2e4 | |
parent | 133a592d191719023a9151a489d80fcdbed93ed7 (diff) | |
download | rneovim-6952b1951b6a60df8d477279f4451094fb51c413.tar.gz rneovim-6952b1951b6a60df8d477279f4451094fb51c413.tar.bz2 rneovim-6952b1951b6a60df8d477279f4451094fb51c413.zip |
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
https://github.com/vim/vim/commit/cb0b99f0672d8446585d26e998343dceca17d1ce
Co-authored-by: Christian Brabandt <cb@256bit.org>
-rw-r--r-- | src/nvim/change.c | 6 | ||||
-rw-r--r-- | src/nvim/cursor.c | 4 | ||||
-rw-r--r-- | src/nvim/move.c | 13 | ||||
-rw-r--r-- | src/nvim/normal.c | 8 | ||||
-rw-r--r-- | src/nvim/plines.c | 6 | ||||
-rw-r--r-- | src/nvim/regexp.c | 17 | ||||
-rw-r--r-- | test/functional/legacy/scroll_opt_spec.lua | 16 | ||||
-rw-r--r-- | test/old/testdir/test_scroll_opt.vim | 19 |
8 files changed, 61 insertions, 28 deletions
diff --git a/src/nvim/change.c b/src/nvim/change.c index 46c21da384..58718811bc 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -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; } diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 4e8457eb2d..32ee1d6c08 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -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--; } diff --git a/src/nvim/move.c b/src/nvim/move.c index bf5aba2f7b..ca3614ef97 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -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; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index a68d1098e5..38fdff95d7 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -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 { diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 3a168320e4..07c77a5d72 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -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); } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 9e08d2615b..dc7ff30513 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -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; diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index d4e4702f5e..8ac1141c2b 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -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([=[ diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index f428e77579..f2e7bc6b56 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -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 |