patch 9.1.2087: Crash when using :tabonly in BufUnload

Problem:  Crash when using :tabonly in BufUnload.
Solution: Set curbuf when setting curwin->w_buffer. Don't wipe out a
          buffer if there are no other buffers. Don't decrement
          b_nwindows if it was 0 before buf_freeall() (zeertzjq).

fixes:  #19088#issuecomment-3710172769
closes: #19186

Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
zeertzjq
2026-01-16 18:25:29 +00:00
committed by Christian Brabandt
parent a1895b67b7
commit fa64f92f6a
4 changed files with 51 additions and 3 deletions
+7 -3
View File
@@ -776,14 +776,18 @@ aucmd_abort:
// Autocommands may have opened or closed windows for this buffer.
// Decrement the count for the close we do here.
if (buf->b_nwindows > 0)
// Don't decrement b_nwindows if the buffer wasn't displayed in any window
// before calling buf_freeall(),
if (nwindows > 0 && buf->b_nwindows > 0)
--buf->b_nwindows;
/*
* Remove the buffer from the list.
* Do not wipe out the buffer if it is used in a window.
* Do not wipe out the buffer if it is used in a window, or if autocommands
* wiped out all other buffers.
*/
if (wipe_buf && buf->b_nwindows <= 0)
if (wipe_buf && buf->b_nwindows <= 0
&& (buf->b_prev != NULL || buf->b_next != NULL))
{
tabpage_T *tp;
win_T *wp;
+40
View File
@@ -861,6 +861,46 @@ func Test_BufUnload_close_other()
call Run_test_BufUnload_close_other('setlocal bufhidden=wipe')
endfunc
func Run_test_BufUnload_tabonly(first_cmd)
exe a:first_cmd
tabnew Xa
setlocal bufhidden=wipe
tabprevious
autocmd BufWinLeave Xa ++once tabnext
autocmd BufUnload Xa ++once tabonly
tabonly
%bwipe!
endfunc
func Test_BufUnload_tabonly()
" This used to dereference a NULL curbuf.
call Run_test_BufUnload_tabonly('setlocal bufhidden=hide')
" This used to dereference a NULL firstbuf.
call Run_test_BufUnload_tabonly('setlocal bufhidden=wipe')
endfunc
func Run_test_BufUnload_tabonly_nested(second_autocmd)
file Xa
tabnew Xb
setlocal bufhidden=wipe
tabnew Xc
setlocal bufhidden=wipe
autocmd BufUnload Xb ++once ++nested bwipe! Xa
exe $'autocmd BufUnload Xa ++once ++nested {a:second_autocmd}'
autocmd BufWinLeave Xc ++once tabnext
tabfirst
2tabclose
%bwipe!
endfunc
func Test_BufUnload_tabonly_nested()
" These used to cause heap-use-after-free.
call Run_test_BufUnload_tabonly_nested('tabonly')
call Run_test_BufUnload_tabonly_nested('tabonly | tabprevious')
endfunc
func s:AddAnAutocmd()
augroup vimBarTest
au BufReadCmd * echo 'hello'
+2
View File
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2087,
/**/
2086,
/**/
+2
View File
@@ -3462,6 +3462,8 @@ win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
{
win->w_buffer = firstbuf;
++firstbuf->b_nwindows;
if (win == curwin)
curbuf = curwin->w_buffer;
win_init_empty(win);
}
return;