Skip to content

Commit 983d83f

Browse files
committed
patch 8.2.2476: using freed memory when splitting window while closing buffer
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.
1 parent dfc3db7 commit 983d83f

7 files changed

Lines changed: 26 additions & 11 deletions

File tree

src/buffer.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ close_buffer(
595595
if (buf->b_nwindows == 1)
596596
{
597597
++buf->b_locked;
598+
++buf->b_locked_split;
598599
if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname,
599600
FALSE, buf)
600601
&& !bufref_valid(&bufref))
@@ -605,6 +606,7 @@ close_buffer(
605606
return FALSE;
606607
}
607608
--buf->b_locked;
609+
--buf->b_locked_split;
608610
if (abort_if_last && one_window())
609611
// Autocommands made this the only window.
610612
goto aucmd_abort;
@@ -614,12 +616,14 @@ close_buffer(
614616
if (!unload_buf)
615617
{
616618
++buf->b_locked;
619+
++buf->b_locked_split;
617620
if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname,
618621
FALSE, buf)
619622
&& !bufref_valid(&bufref))
620623
// Autocommands deleted the buffer.
621624
goto aucmd_abort;
622625
--buf->b_locked;
626+
--buf->b_locked_split;
623627
if (abort_if_last && one_window())
624628
// Autocommands made this the only window.
625629
goto aucmd_abort;
@@ -800,6 +804,7 @@ buf_freeall(buf_T *buf, int flags)
800804

801805
// Make sure the buffer isn't closed by autocommands.
802806
++buf->b_locked;
807+
++buf->b_locked_split;
803808
set_bufref(&bufref, buf);
804809
if (buf->b_ml.ml_mfp != NULL)
805810
{
@@ -826,6 +831,7 @@ buf_freeall(buf_T *buf, int flags)
826831
return;
827832
}
828833
--buf->b_locked;
834+
--buf->b_locked_split;
829835

830836
// If the buffer was in curwin and the window has changed, go back to that
831837
// window, if it still exists. This avoids that ":edit x" triggering a
@@ -1718,8 +1724,8 @@ set_curbuf(buf_T *buf, int action)
17181724
set_bufref(&prevbufref, prevbuf);
17191725
set_bufref(&newbufref, buf);
17201726

1721-
// Autocommands may delete the current buffer and/or the buffer we want to go
1722-
// to. In those cases don't close the buffer.
1727+
// Autocommands may delete the current buffer and/or the buffer we want to
1728+
// go to. In those cases don't close the buffer.
17231729
if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf)
17241730
|| (bufref_valid(&prevbufref)
17251731
&& bufref_valid(&newbufref)

src/errors.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,5 @@ EXTERN char e_missing_return_type[]
353353
INIT(= N_("E1157: Missing return type"));
354354
EXTERN char e_cannot_use_flatten_in_vim9_script[]
355355
INIT(= N_("E1158: Cannot use flatten() in Vim9 script"));
356+
EXTERN char e_cannot_split_window_when_closing_buffer[]
357+
INIT(= N_("E1159: Cannot split a window when closing the buffer"));

src/popupwin.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1941,7 +1941,7 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
19411941
buf->b_p_ul = -1; // no undo
19421942
buf->b_p_swf = FALSE; // no swap file
19431943
buf->b_p_bl = FALSE; // unlisted buffer
1944-
buf->b_locked = TRUE;
1944+
buf->b_locked = TRUE; // prevent deleting the buffer
19451945

19461946
// Avoid that 'buftype' is reset when this buffer is entered.
19471947
buf->b_p_initialized = TRUE;

src/structs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2633,6 +2633,8 @@ struct file_buffer
26332633
int b_flags; // various BF_ flags
26342634
int b_locked; // Buffer is being closed or referenced, don't
26352635
// let autocommands wipe it out.
2636+
int b_locked_split; // Buffer is being closed, don't allow opening
2637+
// a new window with it.
26362638

26372639
/*
26382640
* b_ffname has the full path of the file (NULL for no name).

src/testdir/test_autocmd.vim

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2761,15 +2761,12 @@ endfunc
27612761

27622762
" Fuzzer found some strange combination that caused a crash.
27632763
func Test_autocmd_normal_mess()
2764-
" TODO: why does this hang on Windows?
2765-
CheckNotMSWindows
2766-
27672764
augroup aucmd_normal_test
27682765
au BufLeave,BufWinLeave,BufHidden,BufUnload,BufDelete,BufWipeout * norm 7q/qc
27692766
augroup END
2770-
o4
2767+
call assert_fails('o4', 'E1159')
27712768
silent! H
2772-
e xx
2769+
call assert_fails('e xx', 'E1159')
27732770
normal G
27742771

27752772
augroup aucmd_normal_test
@@ -2791,7 +2788,7 @@ func Test_autocmd_vimgrep()
27912788
au QuickfixCmdPre,BufNew,BufDelete,BufReadCmd * sb
27922789
au QuickfixCmdPre,BufNew,BufDelete,BufReadCmd * q9
27932790
augroup END
2794-
" TODO: if this is executed directly valgrind reports errors
2791+
%bwipe!
27952792
call assert_fails('lv?a?', 'E926:')
27962793

27972794
augroup aucmd_vimgrep

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static char *(features[]) =
750750

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2476,
753755
/**/
754756
2475,
755757
/**/

src/window.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,11 @@ check_split_disallowed()
769769
emsg(_("E242: Can't split a window while closing another"));
770770
return FAIL;
771771
}
772+
if (curwin->w_buffer->b_locked_split)
773+
{
774+
emsg(_(e_cannot_split_window_when_closing_buffer));
775+
return FAIL;
776+
}
772777
return OK;
773778
}
774779

@@ -793,6 +798,9 @@ win_split(int size, int flags)
793798
if (ERROR_IF_ANY_POPUP_WINDOW)
794799
return FAIL;
795800

801+
if (check_split_disallowed() == FAIL)
802+
return FAIL;
803+
796804
// When the ":tab" modifier was used open a new tab page instead.
797805
if (may_open_tabpage() == OK)
798806
return OK;
@@ -804,8 +812,6 @@ win_split(int size, int flags)
804812
emsg(_("E442: Can't split topleft and botright at the same time"));
805813
return FAIL;
806814
}
807-
if (check_split_disallowed() == FAIL)
808-
return FAIL;
809815

810816
// When creating the help window make a snapshot of the window layout.
811817
// Otherwise clear the snapshot, it's now invalid.

0 commit comments

Comments
 (0)