0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

patch 8.0.0486: crash and endless loop when closing windows in autocmd

Problem:    Crash and endless loop when closing windows in a SessionLoadPost
            autocommand.
Solution:   Check for valid tabpage.  (partly neovim #6308)
This commit is contained in:
Bram Moolenaar
2017-03-19 17:09:56 +01:00
parent 4520d440c5
commit 8c752bd6c4
5 changed files with 129 additions and 2 deletions

View File

@@ -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();

View File

@@ -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);

View File

@@ -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

View File

@@ -764,6 +764,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
486,
/**/
485,
/**/

View File

@@ -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;
@@ -3758,6 +3761,58 @@ valid_tabpage(tabpage_T *tpc)
return FALSE;
}
/*
* 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.
*/