summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2017-03-19 17:09:56 +0100
committerBram Moolenaar <Bram@vim.org>2017-03-19 17:09:56 +0100
commit8c752bd6c4af54c0b7bac35a39acc2bf16015f85 (patch)
treed3d5fed20e6d7bb27a955eb129c8cfec0fc04bcb
parent4520d440c59034452d1450b27fcd56825c090687 (diff)
downloadvim-git-8c752bd6c4af54c0b7bac35a39acc2bf16015f85.tar.gz
patch 8.0.0486: crash and endless loop when closing windows in autocmdv8.0.0486
Problem: Crash and endless loop when closing windows in a SessionLoadPost autocommand. Solution: Check for valid tabpage. (partly neovim #6308)
-rw-r--r--src/fileio.c5
-rw-r--r--src/proto/window.pro2
-rw-r--r--src/testdir/test_autocmd.vim63
-rw-r--r--src/version.c2
-rw-r--r--src/window.c59
5 files changed, 129 insertions, 2 deletions
diff --git a/src/fileio.c b/src/fileio.c
index 32b2059f0..e74392075 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -9033,6 +9033,11 @@ win_found:
win_remove(curwin, NULL);
aucmd_win_used = FALSE;
last_status(FALSE); /* may need to remove last status line */
+
+ if (!valid_tabpage_win(curtab))
+ /* no valid window in current tabpage */
+ close_tabpage(curtab);
+
restore_snapshot(SNAP_AUCMD_IDX, FALSE);
(void)win_comp_pos(); /* recompute window positions */
unblock_autocmds();
diff --git a/src/proto/window.pro b/src/proto/window.pro
index 8b649dbf1..e53123c55 100644
--- a/src/proto/window.pro
+++ b/src/proto/window.pro
@@ -26,6 +26,8 @@ int win_new_tabpage(int after);
int may_open_tabpage(void);
int make_tabpages(int maxcount);
int valid_tabpage(tabpage_T *tpc);
+int valid_tabpage_win(tabpage_T *tpc);
+void close_tabpage(tabpage_T *tpc);
tabpage_T *find_tabpage(int n);
int tabpage_index(tabpage_T *ftp);
void goto_tabpage(int n);
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index df6a868b2..b7487522c 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -345,3 +345,66 @@ func Test_BufEnter()
call delete('Xdir', 'd')
au! BufEnter
endfunc
+
+" Closing a window might cause an endless loop
+" E814 for older Vims
+function Test_autocmd_bufwipe_in_SessLoadPost()
+ tabnew
+ set noswapfile
+ let g:bufnr=bufnr('%')
+ mksession!
+
+ let content=['set nocp noswapfile',
+ \ 'let v:swapchoice="e"',
+ \ 'augroup test_autocmd_sessionload',
+ \ 'autocmd!',
+ \ 'autocmd SessionLoadPost * 4bw!',
+ \ 'augroup END'
+ \ ]
+ call writefile(content, 'Xvimrc')
+ let a=system(v:progpath. ' -u Xvimrc --noplugins -S Session.vim')
+ call assert_match('E814', a)
+
+ unlet! g:bufnr
+ set swapfile
+ for file in ['Session.vim', 'Xvimrc']
+ call delete(file)
+ endfor
+endfunc
+
+" SEGV occurs in older versions.
+function Test_autocmd_bufwipe_in_SessLoadPost2()
+ tabnew
+ set noswapfile
+ let g:bufnr=bufnr('%')
+ mksession!
+
+ let content = ['set nocp noswapfile',
+ \ 'function! DeleteInactiveBufs()',
+ \ ' tabfirst',
+ \ ' let tabblist = []',
+ \ ' for i in range(1, tabpagenr(''$''))',
+ \ ' call extend(tabblist, tabpagebuflist(i))',
+ \ ' endfor',
+ \ ' for b in range(1, bufnr(''$''))',
+ \ ' if bufexists(b) && buflisted(b) && (index(tabblist, b) == -1 || bufname(b) =~# ''^$'')',
+ \ ' exec ''bwipeout '' . b',
+ \ ' endif',
+ \ ' endfor',
+ \ 'call append("1", "SessionLoadPost DONE")',
+ \ 'endfunction',
+ \ 'au SessionLoadPost * call DeleteInactiveBufs()']
+ call writefile(content, 'Xvimrc')
+ let a=system(v:progpath. ' -u Xvimrc --noplugins -S Session.vim')
+ " this probably only matches on unix
+ if has("unix")
+ call assert_notmatch('Caught deadly signal SEGV', a)
+ endif
+ call assert_match('SessionLoadPost DONE', a)
+
+ unlet! g:bufnr
+ set swapfile
+ for file in ['Session.vim', 'Xvimrc']
+ call delete(file)
+ endfor
+endfunc
diff --git a/src/version.c b/src/version.c
index 91f795bcc..7cd12ebbe 100644
--- a/src/version.c
+++ b/src/version.c
@@ -765,6 +765,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 486,
+/**/
485,
/**/
484,
diff --git a/src/window.c b/src/window.c
index d0dff69e0..867f0636a 100644
--- a/src/window.c
+++ b/src/window.c
@@ -2107,7 +2107,7 @@ win_equal_rec(
}
/*
- * close all windows for buffer 'buf'
+ * Close all windows for buffer "buf".
*/
void
close_windows(
@@ -2131,7 +2131,10 @@ close_windows(
#endif
)
{
- win_close(wp, FALSE);
+ if (win_close(wp, FALSE) == FAIL)
+ /* If closing the window fails give up, to avoid looping
+ * forever. */
+ break;
/* Start all over, autocommands may change the window layout. */
wp = firstwin;
@@ -3759,6 +3762,58 @@ valid_tabpage(tabpage_T *tpc)
}
/*
+ * Return TRUE when "tpc" points to a valid tab page and at least one window is
+ * valid.
+ */
+ int
+valid_tabpage_win(tabpage_T *tpc)
+{
+ tabpage_T *tp;
+ win_T *wp;
+
+ FOR_ALL_TABPAGES(tp)
+ {
+ if (tp == tpc)
+ {
+ FOR_ALL_WINDOWS_IN_TAB(tp, wp)
+ {
+ if (win_valid_any_tab(wp))
+ return TRUE;
+ }
+ return FALSE;
+ }
+ }
+ /* shouldn't happen */
+ return FALSE;
+}
+
+/*
+ * Close tabpage "tab", assuming it has no windows in it.
+ * There must be another tabpage or this will crash.
+ */
+ void
+close_tabpage(tabpage_T *tab)
+{
+ tabpage_T *ptp;
+
+ if (tab == first_tabpage)
+ {
+ first_tabpage = tab->tp_next;
+ ptp = first_tabpage;
+ }
+ else
+ {
+ for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tab;
+ ptp = ptp->tp_next)
+ ;
+ ptp->tp_next = tab->tp_next;
+ }
+
+ goto_tabpage_tp(ptp, FALSE, FALSE);
+ free_tabpage(tab);
+}
+
+/*
* Find tab page "n" (first one is 1). Returns NULL when not found.
*/
tabpage_T *