Skip to content

Commit 797e63b

Browse files
committed
patch 8.2.2354: crash with a weird combination of autocommands
Problem: Crash with a weird combination of autocommands. Solution: Increment b_nwindows when needed. (closes #7674)
1 parent 17d015b commit 797e63b

5 files changed

Lines changed: 46 additions & 11 deletions

File tree

src/buffer.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,10 @@ can_unload_buffer(buf_T *buf)
492492
* supposed to close the window but autocommands close all other windows.
493493
*
494494
* When "ignore_abort" is TRUE don't abort even when aborting() returns TRUE.
495+
*
496+
* Return TRUE when we got to the end and b_nwindows was decremented.
495497
*/
496-
void
498+
int
497499
close_buffer(
498500
win_T *win, // if not NULL, set b_last_cursor
499501
buf_T *buf,
@@ -540,7 +542,7 @@ close_buffer(
540542
if (wipe_buf || unload_buf)
541543
{
542544
if (!can_unload_buffer(buf))
543-
return;
545+
return FALSE;
544546

545547
// Wiping out or unloading a terminal buffer kills the job.
546548
free_terminal(buf);
@@ -571,7 +573,7 @@ close_buffer(
571573
// Disallow deleting the buffer when it is locked (already being closed or
572574
// halfway a command that relies on it). Unloading is allowed.
573575
if ((del_buf || wipe_buf) && !can_unload_buffer(buf))
574-
return;
576+
return FALSE;
575577

576578
// check no autocommands closed the window
577579
if (win != NULL && win_valid_any_tab(win))
@@ -600,7 +602,7 @@ close_buffer(
600602
// Autocommands deleted the buffer.
601603
aucmd_abort:
602604
emsg(_(e_auabort));
603-
return;
605+
return FALSE;
604606
}
605607
--buf->b_locked;
606608
if (abort_if_last && one_window())
@@ -625,7 +627,7 @@ close_buffer(
625627
#ifdef FEAT_EVAL
626628
// autocmds may abort script processing
627629
if (!ignore_abort && aborting())
628-
return;
630+
return FALSE;
629631
#endif
630632
}
631633

@@ -653,7 +655,7 @@ close_buffer(
653655
// Return when a window is displaying the buffer or when it's not
654656
// unloaded.
655657
if (buf->b_nwindows > 0 || !unload_buf)
656-
return;
658+
return FALSE;
657659

658660
// Always remove the buffer when there is no file name.
659661
if (buf->b_ffname == NULL)
@@ -683,11 +685,11 @@ close_buffer(
683685

684686
// Autocommands may have deleted the buffer.
685687
if (!bufref_valid(&bufref))
686-
return;
688+
return FALSE;
687689
#ifdef FEAT_EVAL
688690
// autocmds may abort script processing
689691
if (!ignore_abort && aborting())
690-
return;
692+
return FALSE;
691693
#endif
692694

693695
/*
@@ -698,7 +700,7 @@ close_buffer(
698700
* deleted buffer.
699701
*/
700702
if (buf == curbuf && !is_curbuf)
701-
return;
703+
return FALSE;
702704

703705
if (win_valid_any_tab(win) && win->w_buffer == buf)
704706
win->w_buffer = NULL; // make sure we don't use the buffer now
@@ -755,6 +757,7 @@ close_buffer(
755757
buf->b_p_bl = FALSE;
756758
}
757759
// NOTE: at this point "curbuf" may be invalid!
760+
return TRUE;
758761
}
759762

760763
/*

src/ex_cmds.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2742,6 +2742,8 @@ do_ecmd(
27422742
else
27432743
{
27442744
win_T *the_curwin = curwin;
2745+
int did_decrement;
2746+
buf_T *was_curbuf = curbuf;
27452747

27462748
// Set the w_closing flag to avoid that autocommands close the
27472749
// window. And set b_locked for the same reason.
@@ -2754,7 +2756,7 @@ do_ecmd(
27542756
// Close the link to the current buffer. This will set
27552757
// oldwin->w_buffer to NULL.
27562758
u_sync(FALSE);
2757-
close_buffer(oldwin, curbuf,
2759+
did_decrement = close_buffer(oldwin, curbuf,
27582760
(flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE, FALSE);
27592761

27602762
the_curwin->w_closing = FALSE;
@@ -2776,7 +2778,15 @@ do_ecmd(
27762778
goto theend;
27772779
}
27782780
if (buf == curbuf) // already in new buffer
2781+
{
2782+
// close_buffer() has decremented the window count,
2783+
// increment it again here and restore w_buffer.
2784+
if (did_decrement && buf_valid(was_curbuf))
2785+
++was_curbuf->b_nwindows;
2786+
if (win_valid_any_tab(oldwin) && oldwin->w_buffer == NULL)
2787+
oldwin->w_buffer = was_curbuf;
27792788
auto_buf = TRUE;
2789+
}
27802790
else
27812791
{
27822792
#ifdef FEAT_SYN_HL

src/proto/buffer.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags);
55
void set_bufref(bufref_T *bufref, buf_T *buf);
66
int bufref_valid(bufref_T *bufref);
77
int buf_valid(buf_T *buf);
8-
void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last, int ignore_abort);
8+
int close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last, int ignore_abort);
99
void buf_clear_file(buf_T *buf);
1010
void buf_freeall(buf_T *buf, int flags);
1111
void free_wininfo(wininfo_T *wip);

src/testdir/test_autocmd.vim

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,26 @@ func Test_autocmd_bufwipe_in_SessLoadPost()
500500
endfor
501501
endfunc
502502

503+
" Using :blast and :ball for many events caused a crash, because b_nwindows was
504+
" not incremented correctly.
505+
func Test_autocmd_blast_badd()
506+
let content =<< trim [CODE]
507+
au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* blast
508+
edit foo1
509+
au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* ball
510+
edit foo2
511+
call writefile(['OK'], 'Xerrors')
512+
qall
513+
[CODE]
514+
515+
call writefile(content, 'XblastBall')
516+
call system(GetVimCommand() .. ' --clean -S XblastBall')
517+
call assert_match('OK', readfile('Xerrors')->join())
518+
519+
call delete('XblastBall')
520+
call delete('Xerrors')
521+
endfunc
522+
503523
" SEGV occurs in older versions.
504524
func Test_autocmd_bufwipe_in_SessLoadPost2()
505525
tabnew

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static char *(features[]) =
750750

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2354,
753755
/**/
754756
2353,
755757
/**/

0 commit comments

Comments
 (0)