Commit: patch 9.1.2087: Crash when using :tabonly in BufUnload

1 view
Skip to first unread message

Christian Brabandt

unread,
1:31 PM (10 hours ago) 1:31 PM
to vim...@googlegroups.com
patch 9.1.2087: Crash when using :tabonly in BufUnload

Commit: https://github.com/vim/vim/commit/fa64f92f6ab8b8080bdba77155e7bb3530fa21f6
Author: zeertzjq <zeer...@outlook.com>
Date: Fri Jan 16 18:25:29 2026 +0000

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 <zeer...@outlook.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/buffer.c b/src/buffer.c
index 7f64bdaab..bc57a4ea2 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -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;
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index 12203cbe0..a798355cb 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -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'
diff --git a/src/version.c b/src/version.c
index b4e8e8a6d..517bffab1 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 2087,
/**/
2086,
/**/
diff --git a/src/window.c b/src/window.c
index f4909b7f6..500ec88ca 100644
--- a/src/window.c
+++ b/src/window.c
@@ -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;
Reply all
Reply to author
Forward
0 new messages