changeset 18406:fa6efc49d71f v8.1.2197

patch 8.1.2197: ExitPre autocommand may cause accessing freed memory Commit: https://github.com/vim/vim/commit/34ba06b6e6f94bb46062e6c85dbfdcbb0d255ada Author: Bram Moolenaar <Bram@vim.org> Date: Sun Oct 20 22:27:10 2019 +0200 patch 8.1.2197: ExitPre autocommand may cause accessing freed memory Problem: ExitPre autocommand may cause accessing freed memory. Solution: Check the window pointer is still valid. (closes https://github.com/vim/vim/issues/5093)
author Bram Moolenaar <Bram@vim.org>
date Sun, 20 Oct 2019 22:30:03 +0200
parents ef35dddb7189
children 3f9986aeb41a
files src/ex_docmd.c src/testdir/test_exit.vim src/version.c
diffstat 3 files changed, 31 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -4817,9 +4817,9 @@ before_quit_autocmds(win_T *wp, int quit
 {
     apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, wp->w_buffer);
 
-    /* Bail out when autocommands closed the window.
-     * Refuse to quit when the buffer in the last window is being closed (can
-     * only happen in autocommands). */
+    // Bail out when autocommands closed the window.
+    // Refuse to quit when the buffer in the last window is being closed (can
+    // only happen in autocommands).
     if (!win_valid(wp)
 	    || curbuf_locked()
 	    || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_locked > 0))
@@ -4828,9 +4828,10 @@ before_quit_autocmds(win_T *wp, int quit
     if (quit_all || (check_more(FALSE, forceit) == OK && only_one_window()))
     {
 	apply_autocmds(EVENT_EXITPRE, NULL, NULL, FALSE, curbuf);
-	/* Refuse to quit when locked or when the buffer in the last window is
-	 * being closed (can only happen in autocommands). */
-	if (curbuf_locked()
+	// Refuse to quit when locked or when the window was closed or the
+	// buffer in the last window is being closed (can only happen in
+	// autocommands).
+	if (!win_valid(wp) || curbuf_locked()
 			  || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0))
 	    return TRUE;
     }
--- a/src/testdir/test_exit.vim
+++ b/src/testdir/test_exit.vim
@@ -40,6 +40,7 @@ func Test_exiting()
   endif
   call delete('Xtestout')
 
+  " ExitPre autocommand splits the window, so that it's no longer the last one.
   let after =<< trim [CODE]
     au QuitPre * call writefile(["QuitPre"], "Xtestout", "a")
     au ExitPre * call writefile(["ExitPre"], "Xtestout", "a")
@@ -58,4 +59,25 @@ func Test_exiting()
 	  \ readfile('Xtestout'))
   endif
   call delete('Xtestout')
+
+  " ExitPre autocommand splits and closes the window, so that there is still
+  " one window but it's a different one.
+  let after =<< trim [CODE]
+    au QuitPre * call writefile(["QuitPre"], "Xtestout", "a")
+    au ExitPre * call writefile(["ExitPre"], "Xtestout", "a")
+    augroup nasty
+      au ExitPre * split | only
+    augroup END
+    quit
+    augroup nasty
+      au! ExitPre
+    augroup END
+    quit
+  [CODE]
+
+  if RunVim([], after, '')
+    call assert_equal(['QuitPre', 'ExitPre', 'QuitPre', 'ExitPre'],
+	  \ readfile('Xtestout'))
+  endif
+  call delete('Xtestout')
 endfunc
--- a/src/version.c
+++ b/src/version.c
@@ -742,6 +742,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2197,
+/**/
     2196,
 /**/
     2195,