diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-03-04 14:19:53 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-04 14:19:53 +0800 |
commit | de14f2c928f913d4fb617d693024eec5cf2223ec (patch) | |
tree | 34e2b7c7324382eb0058e1dffa2d1baa1ab2ae0d /src | |
parent | 446c353a507834a3cbe9007b06e7e0c2c46b5ac7 (diff) | |
parent | b7d59649acf43c76cc72b25c04bcae926a40b4fe (diff) | |
download | rneovim-de14f2c928f913d4fb617d693024eec5cf2223ec.tar.gz rneovim-de14f2c928f913d4fb617d693024eec5cf2223ec.tar.bz2 rneovim-de14f2c928f913d4fb617d693024eec5cf2223ec.zip |
Merge pull request #22506 from zeertzjq/vim-9.0.0013
vim-patch:9.0.{partial:0013,0016}: fix memory access errors
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/change.c | 6 | ||||
-rw-r--r-- | src/nvim/drawline.c | 9 | ||||
-rw-r--r-- | src/nvim/edit.c | 2 | ||||
-rw-r--r-- | src/nvim/indent.c | 27 | ||||
-rw-r--r-- | src/nvim/indent_c.c | 21 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 6 | ||||
-rw-r--r-- | src/nvim/memline.c | 48 | ||||
-rw-r--r-- | src/nvim/memline_defs.h | 9 | ||||
-rw-r--r-- | src/nvim/testdir/test_breakindent.vim | 10 | ||||
-rw-r--r-- | src/nvim/testdir/test_edit.vim | 3 |
10 files changed, 97 insertions, 44 deletions
diff --git a/src/nvim/change.c b/src/nvim/change.c index 1bd7ea3a5a..7c8f62015d 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -1162,12 +1162,16 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) if (p[0] == '/' && p[-1] == '*') { // End of C comment, indent should line up // with the line containing the start of - // the comment + // the comment. curwin->w_cursor.col = (colnr_T)(p - ptr); if ((pos = findmatch(NULL, NUL)) != NULL) { curwin->w_cursor.lnum = pos->lnum; newindent = get_indent(); + break; } + // this may make "ptr" invalid, get it again + ptr = ml_get(curwin->w_cursor.lnum); + p = ptr + curwin->w_cursor.col; } } } diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index a2ae828f7e..a46f383f64 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -1507,6 +1507,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, if (statuscol.draw) { if (statuscol.textp == NULL) { get_statuscol_str(wp, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol); + if (!end_fill) { + // Get the line again as evaluating 'statuscolumn' may free it. + line = ml_get_buf(wp->w_buffer, lnum, false); + ptr = line + v; + } if (wp->w_redr_statuscol) { break; } @@ -1585,6 +1590,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_extra = NUL; wlv.c_final = NUL; wlv.p_extra[wlv.n_extra] = NUL; + + // Get the line again as evaluating 'foldtext' may free it. + line = ml_get_buf(wp->w_buffer, lnum, false); + ptr = line + v; } if (wlv.draw_state == WL_LINE diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 7522cc202e..2849268da2 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -4653,9 +4653,9 @@ int ins_copychar(linenr_T lnum) } // try to advance to the cursor column + validate_virtcol(); line = ml_get(lnum); prev_ptr = line; - validate_virtcol(); chartabsize_T cts; init_chartabsize_arg(&cts, curwin, lnum, 0, line, line); diff --git a/src/nvim/indent.c b/src/nvim/indent.c index ee9bc48460..c57d26dbe0 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -803,11 +803,12 @@ bool briopt_check(win_T *wp) int get_breakindent_win(win_T *wp, char *line) FUNC_ATTR_NONNULL_ALL { - static int prev_indent = 0; // Cached indent value. - static long prev_ts = 0L; // Cached tabstop value. - static const char *prev_line = NULL; // cached pointer to line. - static varnumber_T prev_tick = 0; // Changedtick of cached value. - static long *prev_vts = NULL; // Cached vartabs values. + static int prev_indent = 0; // cached indent value + static long prev_ts = 0L; // cached tabstop value + static int prev_fnum = 0; // cached buffer number + static char *prev_line = NULL; // cached copy of "line" + static varnumber_T prev_tick = 0; // changedtick of cached value + static long *prev_vts = NULL; // cached vartabs values static int prev_list = 0; // cached list value static int prev_listopt = 0; // cached w_p_briopt_list value static char *prev_flp = NULL; // cached formatlistpat value @@ -818,16 +819,24 @@ int get_breakindent_win(win_T *wp, char *line) && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) ? number_width(wp) + 1 : 0); // used cached indent, unless - // - line pointer changed + // - buffer changed // - 'tabstop' changed + // - buffer was changed // - 'briopt_list changed' changed or // - 'formatlistpattern' changed - if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts + // - line changed + // - 'vartabs' changed + if (prev_fnum != wp->w_buffer->b_fnum + || prev_ts != wp->w_buffer->b_p_ts || prev_tick != buf_get_changedtick(wp->w_buffer) || prev_listopt != wp->w_briopt_list - || (prev_flp == NULL || (strcmp(prev_flp, get_flp_value(wp->w_buffer)) != 0)) + || prev_flp == NULL + || strcmp(prev_flp, get_flp_value(wp->w_buffer)) != 0 + || prev_line == NULL || strcmp(prev_line, line) != 0 || prev_vts != wp->w_buffer->b_p_vts_array) { - prev_line = line; + prev_fnum = wp->w_buffer->b_fnum; + xfree(prev_line); + prev_line = xstrdup(line); prev_ts = wp->w_buffer->b_p_ts; prev_tick = buf_get_changedtick(wp->w_buffer); prev_vts = wp->w_buffer->b_p_vts_array; diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 1c771073b2..3e7f640326 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -2529,8 +2529,6 @@ int get_c_indent(void) break; } - l = get_cursor_line_ptr(); - // If we're in a comment or raw string now, skip to // the start of it. trypos = ind_find_start_CORS(NULL); @@ -2540,9 +2538,9 @@ int get_c_indent(void) continue; } - // + l = get_cursor_line_ptr(); + // Skip preprocessor directives and blank lines. - // if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) { continue; } @@ -2640,8 +2638,6 @@ int get_c_indent(void) break; } - l = get_cursor_line_ptr(); - // If we're in a comment or raw string now, skip // to the start of it. trypos = ind_find_start_CORS(NULL); @@ -2651,6 +2647,8 @@ int get_c_indent(void) continue; } + l = get_cursor_line_ptr(); + // Skip preprocessor directives and blank lines. if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) { continue; @@ -2916,11 +2914,15 @@ int get_c_indent(void) trypos = NULL; } + l = get_cursor_line_ptr(); + // If we are looking for ',', we also look for matching // braces. - if (trypos == NULL && terminated == ',' - && find_last_paren(l, '{', '}')) { - trypos = find_start_brace(); + if (trypos == NULL && terminated == ',') { + if (find_last_paren(l, '{', '}')) { + trypos = find_start_brace(); + } + l = get_cursor_line_ptr(); } if (trypos != NULL) { @@ -2951,6 +2953,7 @@ int get_c_indent(void) curwin->w_cursor.lnum--; curwin->w_cursor.col = 0; } + l = get_cursor_line_ptr(); } // Get indent and pointer to text for current line, diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index c8fc76e20d..bb461a7f13 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1706,7 +1706,9 @@ void ex_luado(exarg_T *const eap) break; } lua_pushvalue(lstate, -1); - const char *old_line = (const char *)ml_get_buf(curbuf, l, false); + const char *const old_line = (const char *)ml_get_buf(curbuf, l, false); + // Get length of old_line here as calling Lua code may free it. + const size_t old_line_len = strlen(old_line); lua_pushstring(lstate, old_line); lua_pushnumber(lstate, (lua_Number)l); if (nlua_pcall(lstate, 2, 1)) { @@ -1714,8 +1716,6 @@ void ex_luado(exarg_T *const eap) break; } if (lua_isstring(lstate, -1)) { - size_t old_line_len = strlen(old_line); - size_t new_line_len; const char *const new_line = lua_tolstring(lstate, -1, &new_line_len); char *const new_line_transformed = xmemdupz(new_line, new_line_len); diff --git a/src/nvim/memline.c b/src/nvim/memline.c index ff0a6d7627..e9988eb844 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -244,6 +244,10 @@ typedef enum { # include "memline.c.generated.h" #endif +#if __has_feature(address_sanitizer) +# define ML_GET_ALLOC_LINES +#endif + /// Open a new memline for "buf". /// /// @return FAIL for failure, OK otherwise. @@ -535,7 +539,8 @@ void ml_close(buf_T *buf, int del_file) return; } mf_close(buf->b_ml.ml_mfp, del_file); // close the .swp file - if (buf->b_ml.ml_line_lnum != 0 && (buf->b_ml.ml_flags & ML_LINE_DIRTY)) { + if (buf->b_ml.ml_line_lnum != 0 + && (buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED))) { xfree(buf->b_ml.ml_line_ptr); } xfree(buf->b_ml.ml_stack); @@ -1780,7 +1785,6 @@ char *ml_get_buf(buf_T *buf, linenr_T lnum, bool will_change) recursive--; } ml_flush_line(buf); - buf->b_ml.ml_flags &= ~ML_LINE_DIRTY; errorret: STRCPY(questions, "???"); buf->b_ml.ml_line_lnum = lnum; @@ -1824,18 +1828,36 @@ errorret: char *ptr = (char *)dp + (dp->db_index[lnum - buf->b_ml.ml_locked_low] & DB_INDEX_MASK); buf->b_ml.ml_line_ptr = ptr; buf->b_ml.ml_line_lnum = lnum; - buf->b_ml.ml_flags &= ~ML_LINE_DIRTY; + buf->b_ml.ml_flags &= ~(ML_LINE_DIRTY | ML_ALLOCATED); } if (will_change) { buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS); +#ifdef ML_GET_ALLOC_LINES + if (buf->b_ml.ml_flags & ML_ALLOCATED) { + // can't make the change in the data block + buf->b_ml.ml_flags |= ML_LINE_DIRTY; + } +#endif ml_add_deleted_len_buf(buf, buf->b_ml.ml_line_ptr, -1); } +#ifdef ML_GET_ALLOC_LINES + if ((buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) == 0) { + // make sure the text is in allocated memory + buf->b_ml.ml_line_ptr = xstrdup(buf->b_ml.ml_line_ptr); + buf->b_ml.ml_flags |= ML_ALLOCATED; + if (will_change) { + // can't make the change in the data block + buf->b_ml.ml_flags |= ML_LINE_DIRTY; + } + } +#endif return buf->b_ml.ml_line_ptr; } /// Check if a line that was just obtained by a call to ml_get /// is in allocated memory. +/// This ignores ML_ALLOCATED to get the same behavior as without ML_GET_ALLOC_LINES. int ml_line_alloced(void) { return curbuf->b_ml.ml_flags & ML_LINE_DIRTY; @@ -2352,24 +2374,23 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy) return FAIL; } - bool readlen = true; - if (copy) { line = xstrdup(line); } - if (buf->b_ml.ml_line_lnum != lnum) { // other line buffered - ml_flush_line(buf); // flush it - } else if (buf->b_ml.ml_flags & ML_LINE_DIRTY) { // same line allocated - ml_add_deleted_len_buf(buf, buf->b_ml.ml_line_ptr, -1); - readlen = false; // already added the length - xfree(buf->b_ml.ml_line_ptr); // free it + if (buf->b_ml.ml_line_lnum != lnum) { + // another line is buffered, flush it + ml_flush_line(buf); } - if (readlen && kv_size(buf->update_callbacks)) { + if (kv_size(buf->update_callbacks)) { ml_add_deleted_len_buf(buf, ml_get_buf(buf, lnum, false), -1); } + if (buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) { + xfree(buf->b_ml.ml_line_ptr); // free allocated line + } + buf->b_ml.ml_line_ptr = line; buf->b_ml.ml_line_lnum = lnum; buf->b_ml.ml_flags = (buf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY; @@ -2692,8 +2713,11 @@ static void ml_flush_line(buf_T *buf) xfree(new_line); entered = false; + } else if (buf->b_ml.ml_flags & ML_ALLOCATED) { + xfree(buf->b_ml.ml_line_ptr); } + buf->b_ml.ml_flags &= ~(ML_LINE_DIRTY | ML_ALLOCATED); buf->b_ml.ml_line_lnum = 0; buf->b_ml.ml_line_offset = 0; } diff --git a/src/nvim/memline_defs.h b/src/nvim/memline_defs.h index 6bb9255909..fd50a73f8c 100644 --- a/src/nvim/memline_defs.h +++ b/src/nvim/memline_defs.h @@ -49,10 +49,11 @@ typedef struct memline { int ml_stack_top; // current top of ml_stack int ml_stack_size; // total number of entries in ml_stack -#define ML_EMPTY 1 // empty buffer -#define ML_LINE_DIRTY 2 // cached line was changed and allocated -#define ML_LOCKED_DIRTY 4 // ml_locked was changed -#define ML_LOCKED_POS 8 // ml_locked needs positive block number +#define ML_EMPTY 0x01 // empty buffer +#define ML_LINE_DIRTY 0x02 // cached line was changed and allocated +#define ML_LOCKED_DIRTY 0x04 // ml_locked was changed +#define ML_LOCKED_POS 0x08 // ml_locked needs positive block number +#define ML_ALLOCATED 0x10 // ml_line_ptr is an allocated copy int ml_flags; linenr_T ml_line_lnum; // line number of cached line, 0 if not valid diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index b61c9a570d..0d1753182e 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -10,7 +10,9 @@ CheckOption breakindent source view_util.vim source screendump.vim -let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" +func SetUp() + let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" +endfunc func s:screen_lines(lnum, width) abort return ScreenLines([a:lnum, a:lnum + 2], a:width) @@ -837,12 +839,12 @@ func Test_breakindent20_list() call s:compare_lines(expect, lines) " check formatlistpat indent with different list levels let &l:flp = '^\s*\*\+\s\+' - redraw! %delete _ call setline(1, ['* Congress shall make no law', \ '*** Congress shall make no law', \ '**** Congress shall make no law']) norm! 1gg + redraw! let expect = [ \ "* Congress shall ", \ " make no law ", @@ -956,9 +958,9 @@ func Test_no_spurious_match() let @/ = '\%>3v[y]' redraw! call searchcount().total->assert_equal(1) + " cleanup set hls&vim - let s:input = "\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" bwipeout! endfunc @@ -1023,8 +1025,6 @@ func Test_no_extra_indent() endfunc func Test_breakindent_column() - " restore original - let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" call s:test_windows('setl breakindent breakindentopt=column:10') redraw! " 1) default: does not indent, too wide :( diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index 075f78ec76..e4c21d9932 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -1890,6 +1890,9 @@ func Test_edit_insertmode_ex_edit() call writefile(lines, 'Xtest_edit_insertmode_ex_edit') let buf = RunVimInTerminal('-S Xtest_edit_insertmode_ex_edit', #{rows: 6}) + " Somehow this can be very slow with valgrind. A separate TermWait() works + " better than a longer time with WaitForAssert() (why?) + call TermWait(buf, 1000) call WaitForAssert({-> assert_match('^-- INSERT --\s*$', term_getline(buf, 6))}) call term_sendkeys(buf, "\<C-B>\<C-L>") call WaitForAssert({-> assert_notmatch('^-- INSERT --\s*$', term_getline(buf, 6))}) |