Skip to content

Commit 8c752bd

Browse files
committed
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)
1 parent 4520d44 commit 8c752bd

File tree

5 files changed

+129
-2
lines changed

5 files changed

+129
-2
lines changed

src/fileio.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9033,6 +9033,11 @@ aucmd_restbuf(
90339033
win_remove(curwin, NULL);
90349034
aucmd_win_used = FALSE;
90359035
last_status(FALSE); /* may need to remove last status line */
9036+
9037+
if (!valid_tabpage_win(curtab))
9038+
/* no valid window in current tabpage */
9039+
close_tabpage(curtab);
9040+
90369041
restore_snapshot(SNAP_AUCMD_IDX, FALSE);
90379042
(void)win_comp_pos(); /* recompute window positions */
90389043
unblock_autocmds();

src/proto/window.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ int win_new_tabpage(int after);
2626
int may_open_tabpage(void);
2727
int make_tabpages(int maxcount);
2828
int valid_tabpage(tabpage_T *tpc);
29+
int valid_tabpage_win(tabpage_T *tpc);
30+
void close_tabpage(tabpage_T *tpc);
2931
tabpage_T *find_tabpage(int n);
3032
int tabpage_index(tabpage_T *ftp);
3133
void goto_tabpage(int n);

src/testdir/test_autocmd.vim

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,3 +345,66 @@ func Test_BufEnter()
345345
call delete('Xdir', 'd')
346346
au! BufEnter
347347
endfunc
348+
349+
" Closing a window might cause an endless loop
350+
" E814 for older Vims
351+
function Test_autocmd_bufwipe_in_SessLoadPost()
352+
tabnew
353+
set noswapfile
354+
let g:bufnr=bufnr('%')
355+
mksession!
356+
357+
let content=['set nocp noswapfile',
358+
\ 'let v:swapchoice="e"',
359+
\ 'augroup test_autocmd_sessionload',
360+
\ 'autocmd!',
361+
\ 'autocmd SessionLoadPost * 4bw!',
362+
\ 'augroup END'
363+
\ ]
364+
call writefile(content, 'Xvimrc')
365+
let a=system(v:progpath. ' -u Xvimrc --noplugins -S Session.vim')
366+
call assert_match('E814', a)
367+
368+
unlet! g:bufnr
369+
set swapfile
370+
for file in ['Session.vim', 'Xvimrc']
371+
call delete(file)
372+
endfor
373+
endfunc
374+
375+
" SEGV occurs in older versions.
376+
function Test_autocmd_bufwipe_in_SessLoadPost2()
377+
tabnew
378+
set noswapfile
379+
let g:bufnr=bufnr('%')
380+
mksession!
381+
382+
let content = ['set nocp noswapfile',
383+
\ 'function! DeleteInactiveBufs()',
384+
\ ' tabfirst',
385+
\ ' let tabblist = []',
386+
\ ' for i in range(1, tabpagenr(''$''))',
387+
\ ' call extend(tabblist, tabpagebuflist(i))',
388+
\ ' endfor',
389+
\ ' for b in range(1, bufnr(''$''))',
390+
\ ' if bufexists(b) && buflisted(b) && (index(tabblist, b) == -1 || bufname(b) =~# ''^$'')',
391+
\ ' exec ''bwipeout '' . b',
392+
\ ' endif',
393+
\ ' endfor',
394+
\ 'call append("1", "SessionLoadPost DONE")',
395+
\ 'endfunction',
396+
\ 'au SessionLoadPost * call DeleteInactiveBufs()']
397+
call writefile(content, 'Xvimrc')
398+
let a=system(v:progpath. ' -u Xvimrc --noplugins -S Session.vim')
399+
" this probably only matches on unix
400+
if has("unix")
401+
call assert_notmatch('Caught deadly signal SEGV', a)
402+
endif
403+
call assert_match('SessionLoadPost DONE', a)
404+
405+
unlet! g:bufnr
406+
set swapfile
407+
for file in ['Session.vim', 'Xvimrc']
408+
call delete(file)
409+
endfor
410+
endfunc

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,8 @@ static char *(features[]) =
764764

765765
static int included_patches[] =
766766
{ /* Add new patch number below this line */
767+
/**/
768+
486,
767769
/**/
768770
485,
769771
/**/

src/window.c

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,7 +2107,7 @@ win_equal_rec(
21072107
}
21082108

21092109
/*
2110-
* close all windows for buffer 'buf'
2110+
* Close all windows for buffer "buf".
21112111
*/
21122112
void
21132113
close_windows(
@@ -2131,7 +2131,10 @@ close_windows(
21312131
#endif
21322132
)
21332133
{
2134-
win_close(wp, FALSE);
2134+
if (win_close(wp, FALSE) == FAIL)
2135+
/* If closing the window fails give up, to avoid looping
2136+
* forever. */
2137+
break;
21352138

21362139
/* Start all over, autocommands may change the window layout. */
21372140
wp = firstwin;
@@ -3758,6 +3761,58 @@ valid_tabpage(tabpage_T *tpc)
37583761
return FALSE;
37593762
}
37603763

3764+
/*
3765+
* Return TRUE when "tpc" points to a valid tab page and at least one window is
3766+
* valid.
3767+
*/
3768+
int
3769+
valid_tabpage_win(tabpage_T *tpc)
3770+
{
3771+
tabpage_T *tp;
3772+
win_T *wp;
3773+
3774+
FOR_ALL_TABPAGES(tp)
3775+
{
3776+
if (tp == tpc)
3777+
{
3778+
FOR_ALL_WINDOWS_IN_TAB(tp, wp)
3779+
{
3780+
if (win_valid_any_tab(wp))
3781+
return TRUE;
3782+
}
3783+
return FALSE;
3784+
}
3785+
}
3786+
/* shouldn't happen */
3787+
return FALSE;
3788+
}
3789+
3790+
/*
3791+
* Close tabpage "tab", assuming it has no windows in it.
3792+
* There must be another tabpage or this will crash.
3793+
*/
3794+
void
3795+
close_tabpage(tabpage_T *tab)
3796+
{
3797+
tabpage_T *ptp;
3798+
3799+
if (tab == first_tabpage)
3800+
{
3801+
first_tabpage = tab->tp_next;
3802+
ptp = first_tabpage;
3803+
}
3804+
else
3805+
{
3806+
for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tab;
3807+
ptp = ptp->tp_next)
3808+
;
3809+
ptp->tp_next = tab->tp_next;
3810+
}
3811+
3812+
goto_tabpage_tp(ptp, FALSE, FALSE);
3813+
free_tabpage(tab);
3814+
}
3815+
37613816
/*
37623817
* Find tab page "n" (first one is 1). Returns NULL when not found.
37633818
*/

0 commit comments

Comments
 (0)