]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.2087: Crash when using :tabonly in BufUnload v9.1.2087
authorzeertzjq <zeertzjq@outlook.com>
Fri, 16 Jan 2026 18:25:29 +0000 (18:25 +0000)
committerChristian Brabandt <cb@256bit.org>
Fri, 16 Jan 2026 18:25:29 +0000 (18:25 +0000)
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>
src/buffer.c
src/testdir/test_autocmd.vim
src/version.c
src/window.c

index 7f64bdaab57dcfc19bf4887e1c5ce571850eea68..bc57a4ea2e00809c555d437b4597f0461da9c8b9 100644 (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;
index 12203cbe0e2b7f33d98037c6cc8284a3c03ef72a..a798355cbcaa7119b6aad508ed6e9c2b3db0f735 100644 (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'
index b4e8e8a6dd4fec921e0b70cf44f3a0ad9fca9071..517bffab127ac91331aba7ddb267db667b42eefb 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2087,
 /**/
     2086,
 /**/
index f4909b7f683bdd1b2e764b2f6466ed6daab56271..500ec88caf66ff4dc96bf87680a82c9b2cd30e6b 100644 (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;