]> granicus.if.org Git - vim/commitdiff
patch 8.2.2476: using freed memory when splitting window while closing buffer v8.2.2476
authorBram Moolenaar <Bram@vim.org>
Sun, 7 Feb 2021 11:12:43 +0000 (12:12 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 7 Feb 2021 11:12:43 +0000 (12:12 +0100)
Problem:    Using freed memory when using an autocommand to split a window
            while a buffer is being closed.
Solution:   Disallow splitting when the buffer has b_locked_split set.

src/buffer.c
src/errors.h
src/popupwin.c
src/structs.h
src/testdir/test_autocmd.vim
src/version.c
src/window.c

index f9bffbf3d8b9a6a2e27727144e5995c96be5df6b..ca1fd36efe0ab9058345febcd256765e2535ad2f 100644 (file)
@@ -595,6 +595,7 @@ close_buffer(
     if (buf->b_nwindows == 1)
     {
        ++buf->b_locked;
+       ++buf->b_locked_split;
        if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname,
                                                                  FALSE, buf)
                && !bufref_valid(&bufref))
@@ -605,6 +606,7 @@ aucmd_abort:
            return FALSE;
        }
        --buf->b_locked;
+       --buf->b_locked_split;
        if (abort_if_last && one_window())
            // Autocommands made this the only window.
            goto aucmd_abort;
@@ -614,12 +616,14 @@ aucmd_abort:
        if (!unload_buf)
        {
            ++buf->b_locked;
+           ++buf->b_locked_split;
            if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname,
                                                                  FALSE, buf)
                    && !bufref_valid(&bufref))
                // Autocommands deleted the buffer.
                goto aucmd_abort;
            --buf->b_locked;
+           --buf->b_locked_split;
            if (abort_if_last && one_window())
                // Autocommands made this the only window.
                goto aucmd_abort;
@@ -800,6 +804,7 @@ buf_freeall(buf_T *buf, int flags)
 
     // Make sure the buffer isn't closed by autocommands.
     ++buf->b_locked;
+    ++buf->b_locked_split;
     set_bufref(&bufref, buf);
     if (buf->b_ml.ml_mfp != NULL)
     {
@@ -826,6 +831,7 @@ buf_freeall(buf_T *buf, int flags)
            return;
     }
     --buf->b_locked;
+    --buf->b_locked_split;
 
     // If the buffer was in curwin and the window has changed, go back to that
     // window, if it still exists.  This avoids that ":edit x" triggering a
@@ -1718,8 +1724,8 @@ set_curbuf(buf_T *buf, int action)
     set_bufref(&prevbufref, prevbuf);
     set_bufref(&newbufref, buf);
 
-    // Autocommands may delete the current buffer and/or the buffer we want to go
-    // to.  In those cases don't close the buffer.
+    // Autocommands may delete the current buffer and/or the buffer we want to
+    // go to.  In those cases don't close the buffer.
     if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf)
            || (bufref_valid(&prevbufref)
                && bufref_valid(&newbufref)
index e6d7ab26c8b63a30be59510dd5b51d38092e5516..ec038ec24e56636c692b4a15ae569d0526164955 100644 (file)
@@ -353,3 +353,5 @@ EXTERN char e_missing_return_type[]
        INIT(= N_("E1157: Missing return type"));
 EXTERN char e_cannot_use_flatten_in_vim9_script[]
        INIT(= N_("E1158: Cannot use flatten() in Vim9 script"));
+EXTERN char e_cannot_split_window_when_closing_buffer[]
+       INIT(= N_("E1159: Cannot split a window when closing the buffer"));
index 436238f9ba897ba5a8fe06fbbfb079a5c06e7373..47e7338b74f4ed7746ac3906767948ad321a94fb 100644 (file)
@@ -1941,7 +1941,7 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
        buf->b_p_ul = -1;       // no undo
        buf->b_p_swf = FALSE;   // no swap file
        buf->b_p_bl = FALSE;    // unlisted buffer
-       buf->b_locked = TRUE;
+       buf->b_locked = TRUE;   // prevent deleting the buffer
 
        // Avoid that 'buftype' is reset when this buffer is entered.
        buf->b_p_initialized = TRUE;
index 6cdfb4fc5c6ea6037c676dae7d805b8165cf3041..8d86d11b18b3a28b8d2f5c96224be92f582d42c2 100644 (file)
@@ -2633,6 +2633,8 @@ struct file_buffer
     int                b_flags;        // various BF_ flags
     int                b_locked;       // Buffer is being closed or referenced, don't
                                // let autocommands wipe it out.
+    int                b_locked_split; // Buffer is being closed, don't allow opening
+                               // a new window with it.
 
     /*
      * b_ffname has the full path of the file (NULL for no name).
index c1d3639538c08fc3564c6e3a0f1cf1f6322c6b58..3109f88bbd673a7f60829a5f2f3bbf5fc66de7ee 100644 (file)
@@ -2761,15 +2761,12 @@ endfunc
 
 " Fuzzer found some strange combination that caused a crash.
 func Test_autocmd_normal_mess()
-  " TODO: why does this hang on Windows?
-  CheckNotMSWindows
-
   augroup aucmd_normal_test
     au BufLeave,BufWinLeave,BufHidden,BufUnload,BufDelete,BufWipeout * norm 7q/qc
   augroup END
-  o4
+  call assert_fails('o4', 'E1159')
   silent! H
-  e xx
+  call assert_fails('e xx', 'E1159')
   normal G
 
   augroup aucmd_normal_test
@@ -2791,7 +2788,7 @@ func Test_autocmd_vimgrep()
     au QuickfixCmdPre,BufNew,BufDelete,BufReadCmd * sb
     au QuickfixCmdPre,BufNew,BufDelete,BufReadCmd * q9\0
   augroup END
-  " TODO: if this is executed directly valgrind reports errors
+  %bwipe!
   call assert_fails('lv?a?\1f', 'E926:')
 
   augroup aucmd_vimgrep
index 01c9efb0ac1be203597e85db1cbe113e083bcc1c..c21856080fc63bdde23950bce9a53384134e5185 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2476,
 /**/
     2475,
 /**/
index bea4f391a890200e93cdb9112ed8bdf4d538de53..58a216daf7886db01802cd2319a1cdcc851cd6ad 100644 (file)
@@ -769,6 +769,11 @@ check_split_disallowed()
        emsg(_("E242: Can't split a window while closing another"));
        return FAIL;
     }
+    if (curwin->w_buffer->b_locked_split)
+    {
+       emsg(_(e_cannot_split_window_when_closing_buffer));
+       return FAIL;
+    }
     return OK;
 }
 
@@ -793,6 +798,9 @@ win_split(int size, int flags)
     if (ERROR_IF_ANY_POPUP_WINDOW)
        return FAIL;
 
+    if (check_split_disallowed() == FAIL)
+       return FAIL;
+
     // When the ":tab" modifier was used open a new tab page instead.
     if (may_open_tabpage() == OK)
        return OK;
@@ -804,8 +812,6 @@ win_split(int size, int flags)
        emsg(_("E442: Can't split topleft and botright at the same time"));
        return FAIL;
     }
-    if (check_split_disallowed() == FAIL)
-       return FAIL;
 
     // When creating the help window make a snapshot of the window layout.
     // Otherwise clear the snapshot, it's now invalid.