]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.0159: Crash in WinClosed after BufUnload closes other windows v9.1.0159
authorzeertzjq <zeertzjq@outlook.com>
Sat, 9 Mar 2024 14:39:27 +0000 (15:39 +0100)
committerChristian Brabandt <cb@256bit.org>
Sat, 9 Mar 2024 14:39:27 +0000 (15:39 +0100)
Problem:  Crash in WinClosed after BufUnload closes other windows
Solution: Don't trigger WinClosed if the buffer is NULL (zeertzjq)

Now win_close_othertab() doesn't trigger any autocommands if the buffer
is NULL, so remove the autocmd blocking above (which was added not long
ago in patch v9.0.0550) for consistency.

Also remove an unreachable close_last_window_tabpage() above:
- It is only reached if only_one_window() returns TRUE and last_window()
  returns FALSE.
- If only_one_window() returns TRUE, there is only one tabpage.
- If there is only one tabpage and last_window() returns FALSE, the
  one_window() in last_window() must return FALSE, and the ONE_WINDOW
  in close_last_window_tabpage() must also be FALSE.
- So close_last_window_tabpage() doesn't do anything and returns FALSE.

Then the curtab != prev_curtab check also doesn't make much sense, and
the only_one_window() can be replaced with a check for popup and a call
to last_window() since this is a stricter check than only_one_window().

closes: #14166

Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/testdir/test_autocmd.vim
src/version.c
src/window.c

index f4e3c15a61cf35ec565a2a835af5849982640663..564d568c0d6b106c12b2ac0840c0c005de244e8e 100644 (file)
@@ -819,6 +819,27 @@ func Test_WinClosed_switch_tab()
   %bwipe!
 endfunc
 
+" This used to trigger WinClosed twice for the same window, and the window's
+" buffer was NULL in the second autocommand.
+func Test_WinClosed_BufUnload_close_other()
+  tabnew
+  let g:tab = tabpagenr()
+  let g:buf = bufnr()
+  new
+  setlocal bufhidden=wipe
+  augroup test-WinClosed
+    autocmd BufUnload * ++once exe g:buf .. 'bwipe!'
+    autocmd WinClosed * call tabpagebuflist(g:tab)
+  augroup END
+  close
+
+  unlet g:tab
+  unlet g:buf
+  autocmd! test-WinClosed
+  augroup! test-WinClosed
+  %bwipe!
+endfunc
+
 func s:AddAnAutocmd()
   augroup vimBarTest
     au BufReadCmd * echo 'hello'
index 8a1bb161c0ff4f43af148050ae403c9765cd67ac..3d6c36d3e39a5d161b930a2272180902f44244b4 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    159,
 /**/
     158,
 /**/
index 77d9bf772bea4706cca1eb58f47c99959e35103b..f893c12350ebe564338ad4ad758754d215a58378 100644 (file)
@@ -2585,7 +2585,7 @@ one_window(void)
 
 /*
  * Close the possibly last window in a tab page.
- * Returns TRUE when the window was closed already.
+ * Return FALSE if there are other windows and nothing is done, TRUE otherwise.
  */
     static int
 close_last_window_tabpage(
@@ -2723,7 +2723,7 @@ win_close(win_T *win, int free_buf)
     // and then close the window and the tab page to avoid that curwin and
     // curtab are invalid while we are freeing memory.
     if (close_last_window_tabpage(win, free_buf, prev_curtab))
-      return FAIL;
+       return FAIL;
 
     // When closing the help window, try restoring a snapshot after closing
     // the window.  Otherwise clear the snapshot, it's now invalid.
@@ -2796,9 +2796,11 @@ win_close(win_T *win, int free_buf)
 
     win_close_buffer(win, free_buf ? DOBUF_UNLOAD : 0, TRUE);
 
-    if (only_one_window() && win_valid(win) && win->w_buffer == NULL
-           && (last_window() || curtab != prev_curtab
-               || close_last_window_tabpage(win, free_buf, prev_curtab)))
+    if (win_valid(win) && win->w_buffer == NULL
+#if defined(FEAT_PROP_POPUP)
+           && !popup_is_popup(win)
+#endif
+           && last_window())
     {
        // Autocommands have closed all windows, quit now.  Restore
        // curwin->w_buffer, otherwise writing viminfo may fail.
@@ -2812,10 +2814,7 @@ win_close(win_T *win, int free_buf)
                                                      && win->w_buffer == NULL)
     {
        // Need to close the window anyway, since the buffer is NULL.
-       // Don't trigger autocmds with a NULL buffer.
-       block_autocmds();
        win_close_othertab(win, FALSE, prev_curtab);
-       unblock_autocmds();
        return FAIL;
     }
 
@@ -3352,10 +3351,15 @@ win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
        return; // window is already being closed
 
     // Trigger WinClosed just before starting to free window-related resources.
-    trigger_winclosed(win);
-    // autocmd may have freed the window already.
-    if (!win_valid_any_tab(win))
-       return;
+    // If the buffer is NULL, it isn't safe to trigger autocommands,
+    // and win_close() should have already triggered WinClosed.
+    if (win->w_buffer != NULL)
+    {
+       trigger_winclosed(win);
+       // autocmd may have freed the window already.
+       if (!win_valid_any_tab(win))
+           return;
+    }
 
     if (win->w_buffer != NULL)
        // Close the link to the buffer.