Hi Bram!
On Fr, 24 Aug 2012, Bram Moolenaar wrote:
>
> Richard Brown wrote:
>
> > > > Hi, 7.3.449 fixed an issue where this autocommand and the following
> > > > sequence would cause a segfault:
> > > >
> > > > :autocmd BufWinLeave * if empty(&bt) | lclose | endif
> > > > :call setloclist(1, [{'bufnr': 1, 'lnum': 1, 'text': 'testing'}])
> > > > :lopen
> > > > ^wk
> > > > :q
> > > >
> > >
> > > Considering that 7.3.545 fixes a problem with autocommands, it's
> > > unlikely that it introduced the problem, but rather uncovered another
> > > problem.
> > >
> > > Does that sequence of commands segfault also for "vim -u NONE"?
> > >
> >
> > Yes, I should have said that I was doing that, sorry. The backtrace didn't
> > seem to offer much before, but here it is for Included patches: 1-638, 638
> >
> > Program received signal SIGSEGV, Segmentation fault.
> > 0x00000001001553e5 in update_screen (type=40) at screen.c:478
> > 478 if (wp->w_buffer->b_mod_set)
> > (gdb) bt
> > #0 0x00000001001553e5 in update_screen (type=40) at screen.c:478
> > #1 0x00000001001def0f in main_loop (cmdwin=0, noexmode=0) at main.c:1197
> > #2 0x00000001001debce in main (argc=3, argv=0x7fff5fbffbe8) at main.c:998
>
> At some point I decided to set w_buffer to NULL when it is no longer
> valid. Previously there would be a dangling pointer that might still
> work. This turns unpredictable behavior into a reliable crash :-).
> Hopefully this is one such case and we can figure out how to fix it.
> Most likely "wp" is no longer valid (I didn't look at the context yet).
No, wp is perfectly valid.
The problem is, when closing the current window, the autocommand closes
the only other active window and we end up with wp->w_buffer == NULL. So
wp->w_closing doesn't really help here.
I have seen this crash several times already, but have never really had
any idea how to prevent this. But I think, the attached patch fixes it
altogether.
regards,
Christian
--
Beamtenmikado. Wer sich zuerst bewegt hat verloren.
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
diff -r 17b34ac33196 src/window.c
--- a/src/window.c Thu Aug 23 22:52:48 2012 +0200
+++ b/src/window.c Fri Aug 24 20:46:20 2012 +0200
@@ -23,6 +23,7 @@
static void win_totop __ARGS((int size, int flags));
static void win_equal_rec __ARGS((win_T *next_curwin, int current, frame_T *topfr, int dir, int col, int row, int width, int height));
static int last_window __ARGS((void));
+static int close_other_windows __ARGS((win_T * win));
static int close_last_window_tabpage __ARGS((win_T *win, int free_buf, tabpage_T *prev_curtab));
static win_T *win_free_mem __ARGS((win_T *win, int *dirp, tabpage_T *tp));
static frame_T *win_altframe __ARGS((win_T *win, tabpage_T *tp));
@@ -2155,6 +2156,37 @@
return FALSE;
}
+/* returns true, if all other windows are going to be closed,
+ * so closing the current window needs to be aborted, else
+ * Vim will segfault */
+static int
+close_other_windows(win)
+ win_T *win;
+{
+ if (first_tabpage->tp_next != NULL)
+ /* there are other tabpages, so this isn't the last window */
+ return FALSE;
+ else
+#ifdef FEAT_AUTOCMD
+ {
+ win_T *wp;
+
+ FOR_ALL_WINDOWS(wp)
+ {
+ if (wp == aucmd_win || wp == win)
+ continue;
+ else
+ {
+ if (wp->w_closing != OK)
+ return FALSE;
+ }
+ }
+ return OK;
+ }
+#endif
+ return FALSE;
+}
+
/*
* Close window "win". Only works for the current tab page.
* If "free_buf" is TRUE related buffer may be unloaded.
@@ -2180,6 +2212,14 @@
EMSG(_("E444: Cannot close last window"));
return;
}
+ /* Check, if all other windows besides the current window are going to be closed
+ * (because of autocommands), so abort closing this window, or else Vim might
+ * segfault */
+ if (close_other_windows(win))
+ {
+ EMSG(_("E444: Cannot close last window"));
+ return; /* all other windows will be closed, abort closing this one */
+ }
#ifdef FEAT_AUTOCMD
if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing))
@@ -2273,6 +2313,16 @@
#ifdef FEAT_AUTOCMD
if (win_valid(win))
win->w_closing = FALSE;
+ /* close_other_windows return FALSE, cause there is still another tabpage
+ * open, but by the time we are here, an autocommand could still have freed
+ * win->w_buffer, so make sure, close_last_window_tabpage() can successful
+ * close something */
+ if (win->w_buffer == NULL)
+ {
+ /* make sure win->w_buffer is valid */
+ (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE,
+ ECMD_HIDE, curwin);
+ }
#endif
}