Skip to content

Commit e0ab94e

Browse files
committed
patch 7.4.2324
Problem: Crash when editing a new buffer and BufUnload autocommand wipes out the new buffer. (Norio Takagi) Solution: Don't allow wiping out this buffer. (partly by Hirohito Higashi) Move old style test13 into test_autocmd. Avoid ml_get error when editing a file.
1 parent d77f9d5 commit e0ab94e

11 files changed

Lines changed: 133 additions & 122 deletions

File tree

src/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2042,7 +2042,7 @@ test1 \
20422042
test_utf8 \
20432043
test_wordcount \
20442044
test2 test3 test4 test5 test6 test7 test8 test9 \
2045-
test11 test12 test13 test14 test15 test17 test18 test19 \
2045+
test11 test12 test14 test15 test17 test18 test19 \
20462046
test20 test21 test22 test23 test24 test25 test26 test27 test28 test29 \
20472047
test30 test31 test32 test33 test34 test36 test37 test38 test39 \
20482048
test40 test41 test42 test43 test44 test45 test48 test49 \

src/buffer.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,14 @@ close_buffer(
476476
unload_buf = TRUE;
477477
#endif
478478

479+
/* Disallow deleting the buffer when it is locked (already being closed or
480+
* halfway a command that relies on it). Unloading is allowed. */
481+
if (buf->b_locked > 0 && (del_buf || wipe_buf))
482+
{
483+
EMSG(_("E937: Attempt to delete a buffer that is in use"));
484+
return;
485+
}
486+
479487
if (win != NULL
480488
#ifdef FEAT_WINDOWS
481489
&& win_valid_any_tab(win) /* in case autocommands closed the window */
@@ -499,7 +507,7 @@ close_buffer(
499507
/* When the buffer is no longer in a window, trigger BufWinLeave */
500508
if (buf->b_nwindows == 1)
501509
{
502-
buf->b_closing = TRUE;
510+
++buf->b_locked;
503511
if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname,
504512
FALSE, buf)
505513
&& !bufref_valid(&bufref))
@@ -509,7 +517,7 @@ close_buffer(
509517
EMSG(_(e_auabort));
510518
return;
511519
}
512-
buf->b_closing = FALSE;
520+
--buf->b_locked;
513521
if (abort_if_last && one_window())
514522
/* Autocommands made this the only window. */
515523
goto aucmd_abort;
@@ -518,13 +526,13 @@ close_buffer(
518526
* BufHidden */
519527
if (!unload_buf)
520528
{
521-
buf->b_closing = TRUE;
529+
++buf->b_locked;
522530
if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname,
523531
FALSE, buf)
524532
&& !bufref_valid(&bufref))
525533
/* Autocommands deleted the buffer. */
526534
goto aucmd_abort;
527-
buf->b_closing = FALSE;
535+
--buf->b_locked;
528536
if (abort_if_last && one_window())
529537
/* Autocommands made this the only window. */
530538
goto aucmd_abort;
@@ -685,7 +693,7 @@ buf_freeall(buf_T *buf, int flags)
685693
# endif
686694

687695
/* Make sure the buffer isn't closed by autocommands. */
688-
buf->b_closing = TRUE;
696+
++buf->b_locked;
689697
set_bufref(&bufref, buf);
690698
if (buf->b_ml.ml_mfp != NULL)
691699
{
@@ -711,7 +719,7 @@ buf_freeall(buf_T *buf, int flags)
711719
/* autocommands deleted the buffer */
712720
return;
713721
}
714-
buf->b_closing = FALSE;
722+
--buf->b_locked;
715723

716724
# ifdef FEAT_WINDOWS
717725
/* If the buffer was in curwin and the window has changed, go back to that
@@ -1369,7 +1377,7 @@ do_buffer(
13691377
*/
13701378
while (buf == curbuf
13711379
# ifdef FEAT_AUTOCMD
1372-
&& !(curwin->w_closing || curwin->w_buffer->b_closing)
1380+
&& !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
13731381
# endif
13741382
&& (firstwin != lastwin || first_tabpage->tp_next != NULL))
13751383
{
@@ -5100,7 +5108,7 @@ ex_buffer_all(exarg_T *eap)
51005108
#endif
51015109
) && firstwin != lastwin
51025110
#ifdef FEAT_AUTOCMD
5103-
&& !(wp->w_closing || wp->w_buffer->b_closing)
5111+
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)
51045112
#endif
51055113
)
51065114
{

src/ex_cmds.c

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3872,8 +3872,8 @@ do_ecmd(
38723872
oldbuf = TRUE;
38733873
set_bufref(&bufref, buf);
38743874
(void)buf_check_timestamp(buf, FALSE);
3875-
/* Check if autocommands made buffer invalid or changed the current
3876-
* buffer. */
3875+
/* Check if autocommands made the buffer invalid or changed the
3876+
* current buffer. */
38773877
if (!bufref_valid(&bufref)
38783878
#ifdef FEAT_AUTOCMD
38793879
|| curbuf != old_curbuf.br_buf
@@ -3938,8 +3938,9 @@ do_ecmd(
39383938
win_T *the_curwin = curwin;
39393939

39403940
/* Set the w_closing flag to avoid that autocommands close the
3941-
* window. */
3941+
* window. And set b_locked for the same reason. */
39423942
the_curwin->w_closing = TRUE;
3943+
++buf->b_locked;
39433944

39443945
if (curbuf == old_curbuf.br_buf)
39453946
#endif
@@ -3953,6 +3954,7 @@ do_ecmd(
39533954

39543955
#ifdef FEAT_AUTOCMD
39553956
the_curwin->w_closing = FALSE;
3957+
--buf->b_locked;
39563958

39573959
# ifdef FEAT_EVAL
39583960
/* autocmds may abort script processing */
@@ -4139,11 +4141,6 @@ do_ecmd(
41394141
/* Assume success now */
41404142
retval = OK;
41414143

4142-
/*
4143-
* Reset cursor position, could be used by autocommands.
4144-
*/
4145-
check_cursor();
4146-
41474144
/*
41484145
* Check if we are editing the w_arg_idx file in the argument list.
41494146
*/

src/ex_docmd.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7201,7 +7201,7 @@ ex_quit(exarg_T *eap)
72017201
/* Refuse to quit when locked or when the buffer in the last window is
72027202
* being closed (can only happen in autocommands). */
72037203
if (curbuf_locked() || (wp->w_buffer->b_nwindows == 1
7204-
&& wp->w_buffer->b_closing))
7204+
&& wp->w_buffer->b_locked > 0))
72057205
return;
72067206
#endif
72077207

@@ -7283,7 +7283,7 @@ ex_quit_all(exarg_T *eap)
72837283
apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf);
72847284
/* Refuse to quit when locked or when the buffer in the last window is
72857285
* being closed (can only happen in autocommands). */
7286-
if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing))
7286+
if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0))
72877287
return;
72887288
#endif
72897289

@@ -7665,7 +7665,7 @@ ex_exit(exarg_T *eap)
76657665
apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf);
76667666
/* Refuse to quit when locked or when the buffer in the last window is
76677667
* being closed (can only happen in autocommands). */
7668-
if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing))
7668+
if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0))
76697669
return;
76707670
#endif
76717671

src/structs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1845,8 +1845,8 @@ struct file_buffer
18451845

18461846
int b_flags; /* various BF_ flags */
18471847
#ifdef FEAT_AUTOCMD
1848-
int b_closing; /* buffer is being closed, don't let
1849-
autocommands close it too. */
1848+
int b_locked; /* Buffer is being closed or referenced, don't
1849+
let autocommands wipe it out. */
18501850
#endif
18511851

18521852
/*

src/testdir/Make_all.mak

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ SCRIPTS_MORE1 = \
111111
SCRIPTS_MORE2 = \
112112
test2.out \
113113
test12.out \
114-
test13.out \
115114
test25.out \
116115
test49.out \
117116
test97.out \

src/testdir/test13.in

Lines changed: 0 additions & 64 deletions
This file was deleted.

src/testdir/test13.ok

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/testdir/test_autocmd.vim

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,49 @@ function Test_autocmd_bufunload_with_tabnext()
7777
quit
7878
call assert_equal(2, tabpagenr('$'))
7979

80+
autocmd! test_autocmd_bufunload_with_tabnext_group
8081
augroup! test_autocmd_bufunload_with_tabnext_group
8182
tablast
8283
quit
8384
endfunc
8485

86+
" SEGV occurs in older versions. (At least 7.4.2321 or older)
87+
function Test_autocmd_bufunload_avoiding_SEGV_01()
88+
split aa.txt
89+
let lastbuf = bufnr('$')
90+
91+
augroup test_autocmd_bufunload
92+
autocmd!
93+
exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
94+
augroup END
95+
96+
call assert_fails('edit bb.txt', 'E937:')
97+
98+
autocmd! test_autocmd_bufunload
99+
augroup! test_autocmd_bufunload
100+
bwipe! aa.txt
101+
bwipe! bb.txt
102+
endfunc
103+
104+
" SEGV occurs in older versions. (At least 7.4.2321 or older)
105+
function Test_autocmd_bufunload_avoiding_SEGV_02()
106+
setlocal buftype=nowrite
107+
let lastbuf = bufnr('$')
108+
109+
augroup test_autocmd_bufunload
110+
autocmd!
111+
exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
112+
augroup END
113+
114+
normal! i1
115+
call assert_fails('edit a.txt', 'E517:')
116+
call feedkeys("\<CR>")
117+
118+
autocmd! test_autocmd_bufunload
119+
augroup! test_autocmd_bufunload
120+
bwipe! a.txt
121+
endfunc
122+
85123
func Test_win_tab_autocmd()
86124
let g:record = []
87125

@@ -196,3 +234,63 @@ func Test_augroup_deleted()
196234
au! VimEnter
197235
endfunc
198236

237+
" Tests for autocommands on :close command.
238+
" This used to be in test13.
239+
func Test_three_windows()
240+
" Write three files and open them, each in a window.
241+
" Then go to next window, with autocommand that deletes the previous one.
242+
" Do this twice, writing the file.
243+
e! Xtestje1
244+
call setline(1, 'testje1')
245+
w
246+
sp Xtestje2
247+
call setline(1, 'testje2')
248+
w
249+
sp Xtestje3
250+
call setline(1, 'testje3')
251+
w
252+
wincmd w
253+
au WinLeave Xtestje2 bwipe
254+
wincmd w
255+
call assert_equal('Xtestje1', expand('%'))
256+
257+
au WinLeave Xtestje1 bwipe Xtestje3
258+
close
259+
call assert_equal('Xtestje1', expand('%'))
260+
261+
" Test deleting the buffer on a Unload event. If this goes wrong there
262+
" will be the ATTENTION prompt.
263+
e Xtestje1
264+
au!
265+
au! BufUnload Xtestje1 bwipe
266+
call assert_fails('e Xtestje3', 'E937:')
267+
call assert_equal('Xtestje3', expand('%'))
268+
269+
e Xtestje2
270+
sp Xtestje1
271+
call assert_fails('e', 'E937:')
272+
call assert_equal('Xtestje2', expand('%'))
273+
274+
" Test changing buffers in a BufWipeout autocommand. If this goes wrong
275+
" there are ml_line errors and/or a Crash.
276+
au!
277+
only
278+
e Xanother
279+
e Xtestje1
280+
bwipe Xtestje2
281+
bwipe Xtestje3
282+
au BufWipeout Xtestje1 buf Xtestje1
283+
bwipe
284+
call assert_equal('Xanother', expand('%'))
285+
286+
only
287+
help
288+
wincmd w
289+
1quit
290+
call assert_equal('Xanother', expand('%'))
291+
292+
au!
293+
call delete('Xtestje1')
294+
call delete('Xtestje2')
295+
call delete('Xtestje3')
296+
endfunc

0 commit comments

Comments
 (0)