]> granicus.if.org Git - vim/commitdiff
patch 8.2.2354: crash with a weird combination of autocommands v8.2.2354
authorBram Moolenaar <Bram@vim.org>
Fri, 15 Jan 2021 15:22:52 +0000 (16:22 +0100)
committerBram Moolenaar <Bram@vim.org>
Fri, 15 Jan 2021 15:22:52 +0000 (16:22 +0100)
Problem:    Crash with a weird combination of autocommands.
Solution:   Increment b_nwindows when needed. (closes #7674)

src/buffer.c
src/ex_cmds.c
src/proto/buffer.pro
src/testdir/test_autocmd.vim
src/version.c

index c14aefb0e3cf137aab9cd4e65470121e7f43abf6..c347ef9a64714370b30f0877624622d78542edae 100644 (file)
@@ -492,8 +492,10 @@ can_unload_buffer(buf_T *buf)
  * supposed to close the window but autocommands close all other windows.
  *
  * When "ignore_abort" is TRUE don't abort even when aborting() returns TRUE.
+ *
+ * Return TRUE when we got to the end and b_nwindows was decremented.
  */
-    void
+    int
 close_buffer(
     win_T      *win,           // if not NULL, set b_last_cursor
     buf_T      *buf,
@@ -540,7 +542,7 @@ close_buffer(
            if (wipe_buf || unload_buf)
            {
                if (!can_unload_buffer(buf))
-                   return;
+                   return FALSE;
 
                // Wiping out or unloading a terminal buffer kills the job.
                free_terminal(buf);
@@ -571,7 +573,7 @@ close_buffer(
     // Disallow deleting the buffer when it is locked (already being closed or
     // halfway a command that relies on it). Unloading is allowed.
     if ((del_buf || wipe_buf) && !can_unload_buffer(buf))
-       return;
+       return FALSE;
 
     // check no autocommands closed the window
     if (win != NULL && win_valid_any_tab(win))
@@ -600,7 +602,7 @@ close_buffer(
            // Autocommands deleted the buffer.
 aucmd_abort:
            emsg(_(e_auabort));
-           return;
+           return FALSE;
        }
        --buf->b_locked;
        if (abort_if_last && one_window())
@@ -625,7 +627,7 @@ aucmd_abort:
 #ifdef FEAT_EVAL
        // autocmds may abort script processing
        if (!ignore_abort && aborting())
-           return;
+           return FALSE;
 #endif
     }
 
@@ -653,7 +655,7 @@ aucmd_abort:
     // Return when a window is displaying the buffer or when it's not
     // unloaded.
     if (buf->b_nwindows > 0 || !unload_buf)
-       return;
+       return FALSE;
 
     // Always remove the buffer when there is no file name.
     if (buf->b_ffname == NULL)
@@ -683,11 +685,11 @@ aucmd_abort:
 
     // Autocommands may have deleted the buffer.
     if (!bufref_valid(&bufref))
-       return;
+       return FALSE;
 #ifdef FEAT_EVAL
     // autocmds may abort script processing
     if (!ignore_abort && aborting())
-       return;
+       return FALSE;
 #endif
 
     /*
@@ -698,7 +700,7 @@ aucmd_abort:
      * deleted buffer.
      */
     if (buf == curbuf && !is_curbuf)
-       return;
+       return FALSE;
 
     if (win_valid_any_tab(win) && win->w_buffer == buf)
        win->w_buffer = NULL;  // make sure we don't use the buffer now
@@ -755,6 +757,7 @@ aucmd_abort:
            buf->b_p_bl = FALSE;
     }
     // NOTE: at this point "curbuf" may be invalid!
+    return TRUE;
 }
 
 /*
index 24763d94d30852fc2979311ee6577d6fc7060ff8..a3226d25cf5131d5bfe600dff702c22e38e76609 100644 (file)
@@ -2742,6 +2742,8 @@ do_ecmd(
            else
            {
                win_T       *the_curwin = curwin;
+               int         did_decrement;
+               buf_T       *was_curbuf = curbuf;
 
                // Set the w_closing flag to avoid that autocommands close the
                // window.  And set b_locked for the same reason.
@@ -2754,7 +2756,7 @@ do_ecmd(
                // Close the link to the current buffer. This will set
                // oldwin->w_buffer to NULL.
                u_sync(FALSE);
-               close_buffer(oldwin, curbuf,
+               did_decrement = close_buffer(oldwin, curbuf,
                         (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE, FALSE);
 
                the_curwin->w_closing = FALSE;
@@ -2776,7 +2778,15 @@ do_ecmd(
                    goto theend;
                }
                if (buf == curbuf)              // already in new buffer
+               {
+                   // close_buffer() has decremented the window count,
+                   // increment it again here and restore w_buffer.
+                   if (did_decrement && buf_valid(was_curbuf))
+                       ++was_curbuf->b_nwindows;
+                   if (win_valid_any_tab(oldwin) && oldwin->w_buffer == NULL)
+                       oldwin->w_buffer = was_curbuf;
                    auto_buf = TRUE;
+               }
                else
                {
 #ifdef FEAT_SYN_HL
index 82bcaf3c1fa7e581ec6ff867912b6a3edc695cf6..564b655402a9865f4d49c2b796ea4178ccabf07a 100644 (file)
@@ -5,7 +5,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags);
 void set_bufref(bufref_T *bufref, buf_T *buf);
 int bufref_valid(bufref_T *bufref);
 int buf_valid(buf_T *buf);
-void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last, int ignore_abort);
+int close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last, int ignore_abort);
 void buf_clear_file(buf_T *buf);
 void buf_freeall(buf_T *buf, int flags);
 void free_wininfo(wininfo_T *wip);
index 97b091466f9a9921b136e653a30518b7a6df0cbe..0d6b414e687553e6bff46c3f9c1d6ee6610dd345 100644 (file)
@@ -500,6 +500,26 @@ func Test_autocmd_bufwipe_in_SessLoadPost()
   endfor
 endfunc
 
+" Using :blast and :ball for many events caused a crash, because b_nwindows was
+" not incremented correctly.
+func Test_autocmd_blast_badd()
+  let content =<< trim [CODE]
+      au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* blast
+      edit foo1
+      au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* ball
+      edit foo2
+      call writefile(['OK'], 'Xerrors')
+      qall
+  [CODE]
+
+  call writefile(content, 'XblastBall')
+  call system(GetVimCommand() .. ' --clean -S XblastBall')
+  call assert_match('OK', readfile('Xerrors')->join())
+
+  call delete('XblastBall')
+  call delete('Xerrors')
+endfunc
+
 " SEGV occurs in older versions.
 func Test_autocmd_bufwipe_in_SessLoadPost2()
   tabnew
index 8372920dfd6a73b59e2ae719525978a450b9248a..e6113c91b02c42cdf8d9dd7763656491a40241e1 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2354,
 /**/
     2353,
 /**/