]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.1.0342: crash when a callback deletes a window that is being used v8.1.0342
authorBram Moolenaar <Bram@vim.org>
Sat, 1 Sep 2018 13:30:03 +0000 (15:30 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 1 Sep 2018 13:30:03 +0000 (15:30 +0200)
Problem:    Crash when a callback deletes a window that is being used.
Solution:   Do not unload a buffer that is being displayed while redrawing the
            screen. Also avoid invoking callbacks while redrawing.
            (closes #2107)

src/buffer.c
src/misc2.c
src/version.c

index 2ca131d23e11ee5fd19e4d3a40cc816128fca827..dcff94ba8b6f1f2f82d1f2243d3b690289dfcfc8 100644 (file)
@@ -412,7 +412,28 @@ buf_hashtab_remove(buf_T *buf)
        hash_remove(&buf_hashtab, hi);
 }
 
-static char *e_buflocked = N_("E937: Attempt to delete a buffer that is in use");
+/*
+ * Return TRUE when buffer "buf" can be unloaded.
+ * Give an error message and return FALSE when the buffer is locked or the
+ * screen is being redrawn and the buffer is in a window.
+ */
+    static int
+can_unload_buffer(buf_T *buf)
+{
+    int            can_unload = !buf->b_locked;
+
+    if (can_unload && updating_screen)
+    {
+       win_T   *wp;
+
+       FOR_ALL_WINDOWS(wp)
+           if (wp->w_buffer == buf)
+               can_unload = FALSE;
+    }
+    if (!can_unload)
+       EMSG(_("E937: Attempt to delete a buffer that is in use"));
+    return can_unload;
+}
 
 /*
  * Close the link to a buffer.
@@ -474,11 +495,9 @@ close_buffer(
        {
            if (wipe_buf || unload_buf)
            {
-               if (buf->b_locked)
-               {
-                   EMSG(_(e_buflocked));
+               if (!can_unload_buffer(buf))
                    return;
-               }
+
                /* Wiping out or unloading a terminal buffer kills the job. */
                free_terminal(buf);
            }
@@ -501,11 +520,8 @@ 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 (buf->b_locked > 0 && (del_buf || wipe_buf))
-    {
-       EMSG(_(e_buflocked));
+    if ((del_buf || wipe_buf) && !can_unload_buffer(buf))
        return;
-    }
 
     /* check no autocommands closed the window */
     if (win != NULL && win_valid_any_tab(win))
@@ -1196,8 +1212,6 @@ do_bufdel(
     return errormsg;
 }
 
-static int     empty_curbuf(int close_others, int forceit, int action);
-
 /*
  * Make the current buffer empty.
  * Used when it is wiped out and it's the last buffer.
@@ -1238,6 +1252,7 @@ empty_curbuf(
        need_fileinfo = FALSE;
     return retval;
 }
+
 /*
  * Implementation of the commands for the buffer list.
  *
@@ -1359,11 +1374,8 @@ do_buffer(
        int     forward;
        bufref_T bufref;
 
-       if (buf->b_locked)
-       {
-           EMSG(_(e_buflocked));
+       if (!can_unload_buffer(buf))
            return FAIL;
-       }
 
        set_bufref(&bufref, buf);
 
index b7716cea1341ebffc3229739c4f690995da86d38..7c5a45883420925218fe38c3ce1c7bf3c0dbba4c 100644 (file)
@@ -6366,33 +6366,38 @@ parse_queued_messages(void)
 {
     win_T *old_curwin = curwin;
 
-    /* For Win32 mch_breakcheck() does not check for input, do it here. */
+    // Do not handle messages while redrawing, because it may cause buffers to
+    // change or be wiped while they are being redrawn.
+    if (updating_screen)
+       return;
+
+    // For Win32 mch_breakcheck() does not check for input, do it here.
 # if defined(WIN32) && defined(FEAT_JOB_CHANNEL)
     channel_handle_events(FALSE);
 # endif
 
 # ifdef FEAT_NETBEANS_INTG
-    /* Process the queued netbeans messages. */
+    // Process the queued netbeans messages.
     netbeans_parse_messages();
 # endif
 # ifdef FEAT_JOB_CHANNEL
-    /* Write any buffer lines still to be written. */
+    // Write any buffer lines still to be written.
     channel_write_any_lines();
 
-    /* Process the messages queued on channels. */
+    // Process the messages queued on channels.
     channel_parse_messages();
 # endif
 # if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11)
-    /* Process the queued clientserver messages. */
+    // Process the queued clientserver messages.
     server_parse_messages();
 # endif
 # ifdef FEAT_JOB_CHANNEL
-    /* Check if any jobs have ended. */
+    // Check if any jobs have ended.
     job_check_ended();
 # endif
 
-    /* If the current window changed we need to bail out of the waiting loop.
-     * E.g. when a job exit callback closes the terminal window. */
+    // If the current window changed we need to bail out of the waiting loop.
+    // E.g. when a job exit callback closes the terminal window.
     if (curwin != old_curwin)
        ins_char_typebuf(K_IGNORE);
 }
index c7787e752887e8606d85516abb60d196e9c3d930..b88258e8bbfbd738dda6fc478b6b48587516b90f 100644 (file)
@@ -794,6 +794,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    342,
 /**/
     341,
 /**/