mirror of
https://github.com/neovim/neovim.git
synced 2026-07-12 12:29:44 +00:00
vim-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: vim/vim#19096
ac5c8ab6cc
Co-authored-by: Sean Dewar <6256228+seandewar@users.noreply.github.com>
This commit is contained in:
@@ -1323,17 +1323,10 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action == DOBUF_GOTO && buf != curbuf) {
|
if (action == DOBUF_GOTO && buf != curbuf
|
||||||
if (!check_can_set_curbuf_forceit((flags & DOBUF_FORCEIT) != 0)) {
|
&& !check_can_set_curbuf_forceit((flags & DOBUF_FORCEIT) != 0)) {
|
||||||
// disallow navigating to another buffer when 'winfixbuf' is applied
|
// disallow navigating to another buffer when 'winfixbuf' is applied
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
|
||||||
if (buf->b_locked_split) {
|
|
||||||
// disallow navigating to a closing buffer, which like splitting,
|
|
||||||
// can result in more windows displaying it
|
|
||||||
emsg(_(e_cannot_switch_to_a_closing_buffer));
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((action == DOBUF_GOTO || action == DOBUF_SPLIT) && (buf->b_flags & BF_DUMMY)) {
|
if ((action == DOBUF_GOTO || action == DOBUF_SPLIT) && (buf->b_flags & BF_DUMMY)) {
|
||||||
@@ -1558,15 +1551,17 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// make "buf" the current buffer
|
// make "buf" the current buffer
|
||||||
if (action == DOBUF_SPLIT) { // split window first
|
// If 'switchbuf' is set jump to the window containing "buf".
|
||||||
// If 'switchbuf' is set jump to the window containing "buf".
|
if (action == DOBUF_SPLIT && swbuf_goto_win_with_buf(buf) != NULL) {
|
||||||
if (swbuf_goto_win_with_buf(buf) != NULL) {
|
return OK;
|
||||||
return OK;
|
}
|
||||||
}
|
// Whether splitting or not, don't open a closing buffer in more windows.
|
||||||
|
if (buf != curbuf && buf->b_locked_split) {
|
||||||
if (win_split(0, 0) == FAIL) {
|
emsg(_(e_cannot_switch_to_a_closing_buffer));
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
if (action == DOBUF_SPLIT && win_split(0, 0) == FAIL) { // split window first
|
||||||
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// go to current buffer - nothing to do
|
// go to current buffer - nothing to do
|
||||||
|
|||||||
@@ -591,34 +591,89 @@ endfunc
|
|||||||
func Test_closed_buffer_still_in_window()
|
func Test_closed_buffer_still_in_window()
|
||||||
%bw!
|
%bw!
|
||||||
|
|
||||||
|
let s:fired = 0
|
||||||
let s:w = win_getid()
|
let s:w = win_getid()
|
||||||
new
|
new
|
||||||
let s:b = bufnr()
|
let s:b = bufnr()
|
||||||
setl bufhidden=wipe
|
setl bufhidden=wipe
|
||||||
|
|
||||||
augroup ViewClosedBuffer
|
augroup ViewClosedBuffer
|
||||||
autocmd!
|
autocmd!
|
||||||
autocmd BufUnload * ++once call assert_fails(
|
autocmd BufUnload * ++once let s:fired += 1 | call assert_fails(
|
||||||
\ 'call win_execute(s:w, "' .. s:b .. 'b")', 'E1546:')
|
\ 'call win_execute(s:w, "' .. s:b .. 'buffer")', 'E1546:')
|
||||||
augroup END
|
augroup END
|
||||||
quit!
|
quit!
|
||||||
" Previously resulted in s:b being curbuf while unloaded (no memfile).
|
" Previously resulted in s:b being curbuf while unloaded (no memfile).
|
||||||
|
call assert_equal(1, s:fired)
|
||||||
call assert_equal(1, bufloaded(bufnr()))
|
call assert_equal(1, bufloaded(bufnr()))
|
||||||
call assert_equal(0, bufexists(s:b))
|
call assert_equal(0, bufexists(s:b))
|
||||||
|
%bw!
|
||||||
|
|
||||||
|
new flobby
|
||||||
|
let s:w = win_getid()
|
||||||
|
let s:b = bufnr()
|
||||||
|
setl bufhidden=wipe
|
||||||
|
augroup ViewClosedBuffer
|
||||||
|
autocmd!
|
||||||
|
autocmd BufUnload * ++once let s:fired += 1
|
||||||
|
\| wincmd p
|
||||||
|
\| call assert_notequal(s:w, win_getid())
|
||||||
|
\| call assert_notequal(s:b, bufnr())
|
||||||
|
\| execute s:b 'sbuffer'
|
||||||
|
\| call assert_equal(s:w, win_getid())
|
||||||
|
\| call assert_equal(s:b, bufnr())
|
||||||
|
augroup END
|
||||||
|
" Not a problem if 'switchbuf' switches to an existing window instead.
|
||||||
|
set switchbuf=useopen
|
||||||
|
quit!
|
||||||
|
call assert_equal(2, s:fired)
|
||||||
|
call assert_equal(0, bufexists(s:b))
|
||||||
|
set switchbuf&
|
||||||
|
%bw!
|
||||||
|
|
||||||
|
edit floob
|
||||||
|
let s:b = bufnr()
|
||||||
|
enew
|
||||||
|
augroup ViewClosedBuffer
|
||||||
|
autocmd!
|
||||||
|
autocmd BufWipeout * ++once let s:fired += 1
|
||||||
|
\| call assert_fails(s:b .. 'sbuffer | wincmd p', 'E1546:')
|
||||||
|
\| call assert_equal(1, winnr('$')) " :sbuffer shouldn't have split.
|
||||||
|
augroup END
|
||||||
|
" Used to be a heap UAF.
|
||||||
|
execute s:b 'bwipeout!'
|
||||||
|
call assert_equal(3, s:fired)
|
||||||
|
call assert_equal(0, bufexists(s:b))
|
||||||
|
%bw!
|
||||||
|
|
||||||
|
edit flarb
|
||||||
|
let s:b = bufnr()
|
||||||
|
enew
|
||||||
|
let b2 = bufnr()
|
||||||
|
augroup ViewClosedBuffer
|
||||||
|
autocmd!
|
||||||
|
autocmd BufWipeout * ++once let s:fired += 1
|
||||||
|
\| call assert_fails(s:b .. 'sbuffer', 'E1159:')
|
||||||
|
augroup END
|
||||||
|
" :sbuffer still should fail if curbuf is closing, even if it's not the target
|
||||||
|
" buffer (as switching buffers can fail after the split)
|
||||||
|
bwipeout!
|
||||||
|
call assert_equal(4, s:fired)
|
||||||
|
call assert_equal(0, bufexists(b2))
|
||||||
|
%bw!
|
||||||
|
|
||||||
let s:w = win_getid()
|
let s:w = win_getid()
|
||||||
split
|
split
|
||||||
new
|
new
|
||||||
let s:b = bufnr()
|
let s:b = bufnr()
|
||||||
|
|
||||||
augroup ViewClosedBuffer
|
augroup ViewClosedBuffer
|
||||||
autocmd!
|
autocmd!
|
||||||
autocmd BufWipeout * ++once call win_gotoid(s:w)
|
autocmd BufWipeout * ++once let s:fired += 1 | call win_gotoid(s:w)
|
||||||
\| call assert_fails(s:b .. 'b', 'E1546:') | wincmd p
|
\| call assert_fails(s:b .. 'buffer', 'E1546:') | wincmd p
|
||||||
augroup END
|
augroup END
|
||||||
bw! " Close only this buffer first; used to be a heap UAF.
|
bw! " Close only this buffer first; used to be a heap UAF.
|
||||||
|
call assert_equal(5, s:fired)
|
||||||
|
|
||||||
unlet! s:w s:b
|
unlet! s:w s:b s:fired
|
||||||
autocmd! ViewClosedBuffer
|
autocmd! ViewClosedBuffer
|
||||||
%bw!
|
%bw!
|
||||||
endfunc
|
endfunc
|
||||||
|
|||||||
Reference in New Issue
Block a user