diff options
author | Bram Moolenaar <Bram@vim.org> | 2021-02-07 12:12:43 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2021-02-07 12:12:43 +0100 |
commit | 983d83ff1cd796ff321074335fa53fbe7ac45a46 (patch) | |
tree | 04500912b576c2c91010495406d7e7332053c78f | |
parent | dfc3db76b9de217542cc9258301c1b4818a51cd0 (diff) | |
download | vim-git-983d83ff1cd796ff321074335fa53fbe7ac45a46.tar.gz |
patch 8.2.2476: using freed memory when splitting window while closing bufferv8.2.2476
Problem: Using freed memory when using an autocommand to split a window
while a buffer is being closed.
Solution: Disallow splitting when the buffer has b_locked_split set.
-rw-r--r-- | src/buffer.c | 10 | ||||
-rw-r--r-- | src/errors.h | 2 | ||||
-rw-r--r-- | src/popupwin.c | 2 | ||||
-rw-r--r-- | src/structs.h | 2 | ||||
-rw-r--r-- | src/testdir/test_autocmd.vim | 9 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/window.c | 10 |
7 files changed, 26 insertions, 11 deletions
diff --git a/src/buffer.c b/src/buffer.c index f9bffbf3d..ca1fd36ef 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -595,6 +595,7 @@ close_buffer( if (buf->b_nwindows == 1) { ++buf->b_locked; + ++buf->b_locked_split; if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, FALSE, buf) && !bufref_valid(&bufref)) @@ -605,6 +606,7 @@ aucmd_abort: return FALSE; } --buf->b_locked; + --buf->b_locked_split; if (abort_if_last && one_window()) // Autocommands made this the only window. goto aucmd_abort; @@ -614,12 +616,14 @@ aucmd_abort: if (!unload_buf) { ++buf->b_locked; + ++buf->b_locked_split; if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, FALSE, buf) && !bufref_valid(&bufref)) // Autocommands deleted the buffer. goto aucmd_abort; --buf->b_locked; + --buf->b_locked_split; if (abort_if_last && one_window()) // Autocommands made this the only window. goto aucmd_abort; @@ -800,6 +804,7 @@ buf_freeall(buf_T *buf, int flags) // Make sure the buffer isn't closed by autocommands. ++buf->b_locked; + ++buf->b_locked_split; set_bufref(&bufref, buf); if (buf->b_ml.ml_mfp != NULL) { @@ -826,6 +831,7 @@ buf_freeall(buf_T *buf, int flags) return; } --buf->b_locked; + --buf->b_locked_split; // If the buffer was in curwin and the window has changed, go back to that // window, if it still exists. This avoids that ":edit x" triggering a @@ -1718,8 +1724,8 @@ set_curbuf(buf_T *buf, int action) set_bufref(&prevbufref, prevbuf); set_bufref(&newbufref, buf); - // Autocommands may delete the current buffer and/or the buffer we want to go - // to. In those cases don't close the buffer. + // Autocommands may delete the current buffer and/or the buffer we want to + // go to. In those cases don't close the buffer. if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf) || (bufref_valid(&prevbufref) && bufref_valid(&newbufref) diff --git a/src/errors.h b/src/errors.h index e6d7ab26c..ec038ec24 100644 --- a/src/errors.h +++ b/src/errors.h @@ -353,3 +353,5 @@ EXTERN char e_missing_return_type[] INIT(= N_("E1157: Missing return type")); EXTERN char e_cannot_use_flatten_in_vim9_script[] INIT(= N_("E1158: Cannot use flatten() in Vim9 script")); +EXTERN char e_cannot_split_window_when_closing_buffer[] + INIT(= N_("E1159: Cannot split a window when closing the buffer")); diff --git a/src/popupwin.c b/src/popupwin.c index 436238f9b..47e7338b7 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -1941,7 +1941,7 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type) buf->b_p_ul = -1; // no undo buf->b_p_swf = FALSE; // no swap file buf->b_p_bl = FALSE; // unlisted buffer - buf->b_locked = TRUE; + buf->b_locked = TRUE; // prevent deleting the buffer // Avoid that 'buftype' is reset when this buffer is entered. buf->b_p_initialized = TRUE; diff --git a/src/structs.h b/src/structs.h index 6cdfb4fc5..8d86d11b1 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2633,6 +2633,8 @@ struct file_buffer int b_flags; // various BF_ flags int b_locked; // Buffer is being closed or referenced, don't // let autocommands wipe it out. + int b_locked_split; // Buffer is being closed, don't allow opening + // a new window with it. /* * b_ffname has the full path of the file (NULL for no name). diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index c1d363953..3109f88bb 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -2761,15 +2761,12 @@ endfunc " Fuzzer found some strange combination that caused a crash. func Test_autocmd_normal_mess() - " TODO: why does this hang on Windows? - CheckNotMSWindows - augroup aucmd_normal_test au BufLeave,BufWinLeave,BufHidden,BufUnload,BufDelete,BufWipeout * norm 7q/qc augroup END - o4 + call assert_fails('o4', 'E1159') silent! H - e xx + call assert_fails('e xx', 'E1159') normal G augroup aucmd_normal_test @@ -2791,7 +2788,7 @@ func Test_autocmd_vimgrep() au QuickfixCmdPre,BufNew,BufDelete,BufReadCmd * sb au QuickfixCmdPre,BufNew,BufDelete,BufReadCmd * q9 augroup END - " TODO: if this is executed directly valgrind reports errors + %bwipe! call assert_fails('lv?a?', 'E926:') augroup aucmd_vimgrep diff --git a/src/version.c b/src/version.c index 01c9efb0a..c21856080 100644 --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2476, +/**/ 2475, /**/ 2474, diff --git a/src/window.c b/src/window.c index bea4f391a..58a216daf 100644 --- a/src/window.c +++ b/src/window.c @@ -769,6 +769,11 @@ check_split_disallowed() emsg(_("E242: Can't split a window while closing another")); return FAIL; } + if (curwin->w_buffer->b_locked_split) + { + emsg(_(e_cannot_split_window_when_closing_buffer)); + return FAIL; + } return OK; } @@ -793,6 +798,9 @@ win_split(int size, int flags) if (ERROR_IF_ANY_POPUP_WINDOW) return FAIL; + if (check_split_disallowed() == FAIL) + return FAIL; + // When the ":tab" modifier was used open a new tab page instead. if (may_open_tabpage() == OK) return OK; @@ -804,8 +812,6 @@ win_split(int size, int flags) emsg(_("E442: Can't split topleft and botright at the same time")); return FAIL; } - if (check_split_disallowed() == FAIL) - return FAIL; // When creating the help window make a snapshot of the window layout. // Otherwise clear the snapshot, it's now invalid. |