From: zeertzjq Date: Fri, 16 Jan 2026 18:25:29 +0000 (+0000) Subject: patch 9.1.2087: Crash when using :tabonly in BufUnload X-Git-Tag: v9.1.2087^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fa64f92f6ab8b8080bdba77155e7bb3530fa21f6;p=thirdparty%2Fvim.git 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 Signed-off-by: Christian Brabandt --- diff --git a/src/buffer.c b/src/buffer.c index 7f64bdaab5..bc57a4ea2e 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 12203cbe0e..a798355cbc 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 b4e8e8a6dd..517bffab12 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 f4909b7f68..500ec88caf 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;