Skip to content

Commit ac5c8ab

Browse files
seandewarchrisbra
authored andcommitted
patch 9.1.2058: b_locked_split is not checked for :sbuffer
Problem: b_locked_split is not checked for :sbuffer, which allows autocommands to leave windows open to freed buffers. Solution: In do_buffer_ext, check just before possibly splitting, after handling 'switchbuf'. Leave win_split to handle the check for curbuf. (needed even if curbuf is not the target, as setting the buffer after splitting may fail) (Sean Dewar) closes: #19096 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
1 parent 4157787 commit ac5c8ab

3 files changed

Lines changed: 77 additions & 27 deletions

File tree

src/buffer.c

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,19 +1434,10 @@ do_buffer_ext(
14341434
if ((flags & DOBUF_NOPOPUP) && bt_popup(buf) && !bt_terminal(buf))
14351435
return OK;
14361436
#endif
1437-
if (action == DOBUF_GOTO && buf != curbuf)
1438-
{
1439-
if (!check_can_set_curbuf_forceit((flags & DOBUF_FORCEIT) != 0))
1440-
// disallow navigating to another buffer when 'winfixbuf' is applied
1441-
return FAIL;
1442-
if (buf->b_locked_split)
1443-
{
1444-
// disallow navigating to a closing buffer, which like splitting,
1445-
// can result in more windows displaying it
1446-
emsg(_(e_cannot_switch_to_a_closing_buffer));
1447-
return FAIL;
1448-
}
1449-
}
1437+
if (action == DOBUF_GOTO && buf != curbuf
1438+
&& !check_can_set_curbuf_forceit((flags & DOBUF_FORCEIT) != 0))
1439+
// disallow navigating to another buffer when 'winfixbuf' is applied
1440+
return FAIL;
14501441

14511442
if ((action == DOBUF_GOTO || action == DOBUF_SPLIT)
14521443
&& (buf->b_flags & BF_DUMMY))
@@ -1654,15 +1645,17 @@ do_buffer_ext(
16541645
/*
16551646
* make "buf" the current buffer
16561647
*/
1657-
if (action == DOBUF_SPLIT) // split window first
1648+
// If 'switchbuf' is set jump to the window containing "buf".
1649+
if (action == DOBUF_SPLIT && swbuf_goto_win_with_buf(buf) != NULL)
1650+
return OK;
1651+
// Whether splitting or not, don't open a closing buffer in more windows.
1652+
if (buf != curbuf && buf->b_locked_split)
16581653
{
1659-
// If 'switchbuf' is set jump to the window containing "buf".
1660-
if (swbuf_goto_win_with_buf(buf) != NULL)
1661-
return OK;
1662-
1663-
if (win_split(0, 0) == FAIL)
1664-
return FAIL;
1654+
emsg(_(e_cannot_switch_to_a_closing_buffer));
1655+
return FAIL;
16651656
}
1657+
if (action == DOBUF_SPLIT && win_split(0, 0) == FAIL) // split window first
1658+
return FAIL;
16661659

16671660
// go to current buffer - nothing to do
16681661
if (buf == curbuf)

src/testdir/test_buffer.vim

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -589,34 +589,89 @@ endfunc
589589
func Test_closed_buffer_still_in_window()
590590
%bw!
591591

592+
let s:fired = 0
592593
let s:w = win_getid()
593594
new
594595
let s:b = bufnr()
595596
setl bufhidden=wipe
596-
597597
augroup ViewClosedBuffer
598598
autocmd!
599-
autocmd BufUnload * ++once call assert_fails(
600-
\ 'call win_execute(s:w, "' .. s:b .. 'b")', 'E1546:')
599+
autocmd BufUnload * ++once let s:fired += 1 | call assert_fails(
600+
\ 'call win_execute(s:w, "' .. s:b .. 'buffer")', 'E1546:')
601601
augroup END
602602
quit!
603603
" Previously resulted in s:b being curbuf while unloaded (no memfile).
604+
call assert_equal(1, s:fired)
604605
call assert_equal(1, bufloaded(bufnr()))
605606
call assert_equal(0, bufexists(s:b))
607+
%bw!
608+
609+
new flobby
610+
let s:w = win_getid()
611+
let s:b = bufnr()
612+
setl bufhidden=wipe
613+
augroup ViewClosedBuffer
614+
autocmd!
615+
autocmd BufUnload * ++once let s:fired += 1
616+
\| wincmd p
617+
\| call assert_notequal(s:w, win_getid())
618+
\| call assert_notequal(s:b, bufnr())
619+
\| execute s:b 'sbuffer'
620+
\| call assert_equal(s:w, win_getid())
621+
\| call assert_equal(s:b, bufnr())
622+
augroup END
623+
" Not a problem if 'switchbuf' switches to an existing window instead.
624+
set switchbuf=useopen
625+
quit!
626+
call assert_equal(2, s:fired)
627+
call assert_equal(0, bufexists(s:b))
628+
set switchbuf&
629+
%bw!
630+
631+
edit floob
632+
let s:b = bufnr()
633+
enew
634+
augroup ViewClosedBuffer
635+
autocmd!
636+
autocmd BufWipeout * ++once let s:fired += 1
637+
\| call assert_fails(s:b .. 'sbuffer | wincmd p', 'E1546:')
638+
\| call assert_equal(1, winnr('$')) " :sbuffer shouldn't have split.
639+
augroup END
640+
" Used to be a heap UAF.
641+
execute s:b 'bwipeout!'
642+
call assert_equal(3, s:fired)
643+
call assert_equal(0, bufexists(s:b))
644+
%bw!
645+
646+
edit flarb
647+
let s:b = bufnr()
648+
enew
649+
let b2 = bufnr()
650+
augroup ViewClosedBuffer
651+
autocmd!
652+
autocmd BufWipeout * ++once let s:fired += 1
653+
\| call assert_fails(s:b .. 'sbuffer', 'E1159:')
654+
augroup END
655+
" :sbuffer still should fail if curbuf is closing, even if it's not the target
656+
" buffer (as switching buffers can fail after the split)
657+
bwipeout!
658+
call assert_equal(4, s:fired)
659+
call assert_equal(0, bufexists(b2))
660+
%bw!
606661

607662
let s:w = win_getid()
608663
split
609664
new
610665
let s:b = bufnr()
611-
612666
augroup ViewClosedBuffer
613667
autocmd!
614-
autocmd BufWipeout * ++once call win_gotoid(s:w)
615-
\| call assert_fails(s:b .. 'b', 'E1546:') | wincmd p
668+
autocmd BufWipeout * ++once let s:fired += 1 | call win_gotoid(s:w)
669+
\| call assert_fails(s:b .. 'buffer', 'E1546:') | wincmd p
616670
augroup END
617671
bw! " Close only this buffer first; used to be a heap UAF.
672+
call assert_equal(5, s:fired)
618673

619-
unlet! s:w s:b
674+
unlet! s:w s:b s:fired
620675
autocmd! ViewClosedBuffer
621676
%bw!
622677
endfunc

src/version.c

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

735735
static int included_patches[] =
736736
{ /* Add new patch number below this line */
737+
/**/
738+
2058,
737739
/**/
738740
2057,
739741
/**/

0 commit comments

Comments
 (0)