mirror of
https://github.com/neovim/neovim.git
synced 2026-04-22 07:15:34 +00:00
vim-patch:9.2.0252: Crash when ending Visual mode after curbuf was unloaded
Problem: if close_buffer() in set_curbuf() unloads curbuf, NULL pointer
accesses may occur from enter_buffer() calling
end_visual_mode(), as curbuf is already abandoned and possibly
unloaded. Also, selection registers may not contain the
selection with clipboard+=autoselect(plus).
Solution: Move close_buffer()'s end_visual_mode() call to buf_freeall(), after
any autocmds that may restart it, but just before freeing anything
(Sean Dewar)
related: vim/vim#19728
a8fdfd4fcb
Maybe this should be considered partial? clipboard+=autoselect isn't
implemented. If it is, we may need to update these comments to mention
TextYankPost being possible, and unskip its test.
Co-authored-by: Sean Dewar <6256228+seandewar@users.noreply.github.com>
This commit is contained in:
@@ -685,16 +685,6 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
|
||||
// windows, so that autocommands in buf_freeall() don't get confused.
|
||||
bool is_curbuf = (buf == curbuf);
|
||||
|
||||
// When closing the current buffer stop Visual mode before freeing
|
||||
// anything.
|
||||
if (is_curbuf && VIsual_active
|
||||
#if defined(EXITFREE)
|
||||
&& !entered_free_all_mem
|
||||
#endif
|
||||
) {
|
||||
end_visual_mode();
|
||||
}
|
||||
|
||||
buf->b_nwindows = nwindows;
|
||||
|
||||
buf_freeall(buf, ((del_buf ? BFA_DEL : 0)
|
||||
@@ -889,6 +879,16 @@ void buf_freeall(buf_T *buf, int flags)
|
||||
if (buf == curbuf && !is_curbuf) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If curbuf, stop Visual mode just before freeing, but after autocmds that may restart it.
|
||||
if (buf == curbuf && VIsual_active
|
||||
#if defined(EXITFREE)
|
||||
&& !entered_free_all_mem
|
||||
#endif
|
||||
) {
|
||||
end_visual_mode();
|
||||
}
|
||||
|
||||
diff_buf_delete(buf); // Can't use 'diff' for unloaded buffer.
|
||||
// Remove any ownsyntax, unless exiting.
|
||||
if (curwin != NULL && curwin->w_buffer == buf) {
|
||||
@@ -1754,7 +1754,8 @@ void set_curbuf(buf_T *buf, int action, bool update_jumplist)
|
||||
/// be pointing to freed memory.
|
||||
static void enter_buffer(buf_T *buf)
|
||||
{
|
||||
// when closing the current buffer stop Visual mode
|
||||
// Stop Visual mode before changing curbuf. Assumes curbuf and curwin->w_buffer is valid; if not,
|
||||
// buf_freeall() should've done this already!
|
||||
if (VIsual_active
|
||||
#if defined(EXITFREE)
|
||||
&& !entered_free_all_mem
|
||||
|
||||
@@ -2908,4 +2908,58 @@ func Test_getregionpos_block_linebreak_matches_getpos()
|
||||
let &columns = save_columns
|
||||
bw!
|
||||
endfunc
|
||||
|
||||
func Test_visual_ended_in_wiped_buffer()
|
||||
edit Xfoo
|
||||
edit Xbar
|
||||
setlocal bufhidden=wipe
|
||||
augroup testing
|
||||
autocmd BufWipeout * ++once normal! v
|
||||
augroup END
|
||||
" Must be the last window.
|
||||
call assert_equal(1, winnr('$'))
|
||||
call assert_equal(1, tabpagenr('$'))
|
||||
" Was a member access on a NULL curbuf from Vim ending Visual mode.
|
||||
buffer #
|
||||
call assert_equal(0, bufexists('Xbar'))
|
||||
call assert_equal('n', mode())
|
||||
|
||||
autocmd! testing
|
||||
%bw!
|
||||
endfunc
|
||||
|
||||
func Test_visual_ended_in_unloaded_buffer()
|
||||
throw 'Skipped: needs clipboard=autoselect'
|
||||
CheckFeature clipboard
|
||||
CheckNotGui
|
||||
set clipboard+=autoselect
|
||||
edit Xfoo
|
||||
edit Xbar
|
||||
call setline(1, 'hi')
|
||||
setlocal nomodified
|
||||
let s:fired = 0
|
||||
augroup testing
|
||||
autocmd BufUnload Xbar call assert_equal('Xbar', bufname())
|
||||
\| execute 'normal! V'
|
||||
\| call assert_equal('V', mode())
|
||||
|
||||
" From Vim ending Visual mode. Used to occur too late, after the buffer was
|
||||
" unloaded, so @* didn't contain the selection. Window also had a NULL
|
||||
" w_buffer here!
|
||||
autocmd TextYankPost * ++once let s:fired = 1
|
||||
\| if has('clipboard_working') | call assert_equal("hi\n", @*) | endif
|
||||
\| call tabpagebuflist() " was a NULL member access on w_buffer
|
||||
augroup END
|
||||
|
||||
buffer Xfoo
|
||||
call assert_equal(0, bufloaded('Xbar'))
|
||||
call assert_equal('n', mode())
|
||||
call assert_equal(1, s:fired)
|
||||
|
||||
autocmd! testing
|
||||
unlet! s:fired
|
||||
set clipboard&
|
||||
%bw!
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
Reference in New Issue
Block a user