]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.0.0486: crash and endless loop when closing windows in autocmd v8.0.0486
authorBram Moolenaar <Bram@vim.org>
Sun, 19 Mar 2017 16:09:56 +0000 (17:09 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 19 Mar 2017 16:09:56 +0000 (17:09 +0100)
Problem:    Crash and endless loop when closing windows in a SessionLoadPost
            autocommand.
Solution:   Check for valid tabpage.  (partly neovim #6308)

src/fileio.c
src/proto/window.pro
src/testdir/test_autocmd.vim
src/version.c
src/window.c

index 32b2059f0e304a5eb45ea14501b15304ddf337df..e74392075be0b9670d6fca8f6d883cfe44ba274c 100644 (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();
index 8b649dbf15b2d5119f73951d87c14ce72bae353a..e53123c5545d35fc6b13ac023cd3407ef64f9bc8 100644 (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);
index df6a868b29270c53b8c531c5ecddf3a1d1fceba1..b7487522ced534cc1e34e392c76fd5493ccb209e 100644 (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
index 91f795bcc9788a5aab6e22a16f6fd188ba5dfe34..7cd12ebbe73ad393acc9398cc8f90039ad37c95b 100644 (file)
@@ -764,6 +764,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    486,
 /**/
     485,
 /**/
index d0dff69e0a194d2634b23ef117313cea01383c78..867f0636a905a7d7f4e0d6377c1b7201a0480a01 100644 (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.
  */