changeset 5816:29eb4c2a33ac v7.4.251

updated for version 7.4.251 Problem: Crash when BufAdd autocommand wipes out the buffer. Solution: Check for buffer to still be valid. Postpone freeing the buffer structure. (Hirohito Higashi)
author Bram Moolenaar <bram@vim.org>
date Sun, 06 Apr 2014 20:45:43 +0200
parents d735e62f5925
children ee674aaf65a4
files src/buffer.c src/ex_cmds.c src/fileio.c src/globals.h src/version.c
diffstat 5 files changed, 40 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -676,8 +676,16 @@ free_buffer(buf)
 #endif
 #ifdef FEAT_AUTOCMD
     aubuflocal_remove(buf);
-#endif
-    vim_free(buf);
+    if (autocmd_busy)
+    {
+	/* Do not free the buffer structure while autocommands are executing,
+	 * it's still needed. Free it when autocmd_busy is reset. */
+	buf->b_next = au_pending_free_buf;
+	au_pending_free_buf = buf;
+    }
+    else
+#endif
+	vim_free(buf);
 }
 
 /*
@@ -1681,7 +1689,11 @@ buflist_new(ffname, sfname, lnum, flags)
 	    buf->b_p_bl = TRUE;
 #ifdef FEAT_AUTOCMD
 	    if (!(flags & BLN_DUMMY))
+	    {
 		apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf);
+		if (!buf_valid(buf))
+		    return NULL;
+	    }
 #endif
 	}
 	return buf;
@@ -1857,8 +1869,14 @@ buflist_new(ffname, sfname, lnum, flags)
     if (!(flags & BLN_DUMMY))
     {
 	apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf);
+	if (!buf_valid(buf))
+	    return NULL;
 	if (flags & BLN_LISTED)
+	{
 	    apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf);
+	    if (!buf_valid(buf))
+		return NULL;
+	}
 # ifdef FEAT_EVAL
 	if (aborting())		/* autocmds may abort script processing */
 	    return NULL;
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -3343,6 +3343,12 @@ do_ecmd(fnum, ffname, sfname, eap, newln
 #endif
 	    buf = buflist_new(ffname, sfname, 0L,
 		    BLN_CURBUF | ((flags & ECMD_SET_HELP) ? 0 : BLN_LISTED));
+#ifdef FEAT_AUTOCMD
+	    /* autocommands may change curwin and curbuf */
+	    if (oldwin != NULL)
+		oldwin = curwin;
+	    old_curbuf = curbuf;
+#endif
 	}
 	if (buf == NULL)
 	    goto theend;
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -9548,13 +9548,19 @@ apply_autocmds_group(event, fname, fname
 
     /*
      * When stopping to execute autocommands, restore the search patterns and
-     * the redo buffer.
+     * the redo buffer.  Free buffers in the au_pending_free_buf list.
      */
     if (!autocmd_busy)
     {
 	restore_search_patterns();
 	restoreRedobuff();
 	did_filetype = FALSE;
+	while (au_pending_free_buf != NULL)
+	{
+	    buf_T *b = au_pending_free_buf->b_next;
+	    vim_free(au_pending_free_buf);
+	    au_pending_free_buf = b;
+	}
     }
 
     /*
--- a/src/globals.h
+++ b/src/globals.h
@@ -386,6 +386,11 @@ EXTERN int	keep_filetype INIT(= FALSE);	
 /* When deleting the current buffer, another one must be loaded.  If we know
  * which one is preferred, au_new_curbuf is set to it */
 EXTERN buf_T	*au_new_curbuf INIT(= NULL);
+
+/* When deleting the buffer and autocmd_busy is TRUE, do not free the buffer
+ * but link it in the list starting with au_pending_free_buf, using b_next.
+ * Free the buffer when autocmd_busy is set to FALSE. */
+EXTERN buf_T	*au_pending_free_buf INIT(= NULL);
 #endif
 
 #ifdef FEAT_MOUSE
--- a/src/version.c
+++ b/src/version.c
@@ -735,6 +735,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    251,
+/**/
     250,
 /**/
     249,