summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-02-07 12:12:43 +0100
committerBram Moolenaar <Bram@vim.org>2021-02-07 12:12:43 +0100
commit983d83ff1cd796ff321074335fa53fbe7ac45a46 (patch)
tree04500912b576c2c91010495406d7e7332053c78f
parentdfc3db76b9de217542cc9258301c1b4818a51cd0 (diff)
downloadvim-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.c10
-rw-r--r--src/errors.h2
-rw-r--r--src/popupwin.c2
-rw-r--r--src/structs.h2
-rw-r--r--src/testdir/test_autocmd.vim9
-rw-r--r--src/version.c2
-rw-r--r--src/window.c10
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.