Commit: patch 9.1.0159: Crash in WinClosed after BufUnload closes other windows

7 views
Skip to first unread message

Christian Brabandt

unread,
Mar 9, 2024, 9:45:13 AM3/9/24
to vim...@googlegroups.com
patch 9.1.0159: Crash in WinClosed after BufUnload closes other windows

Commit: https://github.com/vim/vim/commit/b2ec0da080fb24f12a8d6f54bd7318a078ca4e6c
Author: zeertzjq <zeer...@outlook.com>
Date: Sat Mar 9 15:39:27 2024 +0100

patch 9.1.0159: Crash in WinClosed after BufUnload closes other windows

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

diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index f4e3c15a6..564d568c0 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -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'
diff --git a/src/version.c b/src/version.c
index 8a1bb161c..3d6c36d3e 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =

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