patch 8.2.2476: using freed memory when splitting window while closing buffer

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.
diff --git a/src/buffer.c b/src/buffer.c
index f9bffbf..ca1fd36 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -595,6 +595,7 @@
     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 @@
 	    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 @@
 	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 @@
 
     // 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 @@
 	    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_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)
diff --git a/src/errors.h b/src/errors.h
index e6d7ab2..ec038ec 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -353,3 +353,5 @@
 	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"));
diff --git a/src/popupwin.c b/src/popupwin.c
index 436238f..47e7338 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -1941,7 +1941,7 @@
 	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;
diff --git a/src/structs.h b/src/structs.h
index 6cdfb4f..8d86d11 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -2633,6 +2633,8 @@
     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).
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index c1d3639..3109f88 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
Binary files differ
diff --git a/src/version.c b/src/version.c
index 01c9efb..c218560 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2476,
+/**/
     2475,
 /**/
     2474,
diff --git a/src/window.c b/src/window.c
index bea4f39..58a216d 100644
--- a/src/window.c
+++ b/src/window.c
@@ -769,6 +769,11 @@
 	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 @@
     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 @@
 	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.