mirror of
https://github.com/neovim/neovim.git
synced 2026-01-03 09:56:25 +10:00
fix(events): crash on WinScrolled #35995
Problem: apply_autocmds function can free both buf_T and win_T pointers
Solution: instead retain winids for WinResized and WinScrolled
autocmds and use curbuf pointer, which is consistent with other uses
of apply_autocmds function
(cherry picked from commit 0a0c349b6f)
This commit is contained in:
committed by
github-actions[bot]
parent
5143419e22
commit
656ff4c438
@@ -5719,6 +5719,21 @@ void may_trigger_win_scrolled_resized(void)
|
||||
|
||||
recursive = true;
|
||||
|
||||
// Save window info before autocmds since they can free windows
|
||||
char resize_winid[NUMBUFLEN];
|
||||
bufref_T resize_bufref;
|
||||
if (trigger_resize) {
|
||||
vim_snprintf(resize_winid, sizeof(resize_winid), "%d", first_size_win->handle);
|
||||
set_bufref(&resize_bufref, first_size_win->w_buffer);
|
||||
}
|
||||
|
||||
char scroll_winid[NUMBUFLEN];
|
||||
bufref_T scroll_bufref;
|
||||
if (trigger_scroll) {
|
||||
vim_snprintf(scroll_winid, sizeof(scroll_winid), "%d", first_scroll_win->handle);
|
||||
set_bufref(&scroll_bufref, first_scroll_win->w_buffer);
|
||||
}
|
||||
|
||||
// If both are to be triggered do WinResized first.
|
||||
if (trigger_resize) {
|
||||
save_v_event_T save_v_event;
|
||||
@@ -5726,10 +5741,8 @@ void may_trigger_win_scrolled_resized(void)
|
||||
|
||||
if (tv_dict_add_list(v_event, S_LEN("windows"), windows_list) == OK) {
|
||||
tv_dict_set_keys_readonly(v_event);
|
||||
|
||||
char winid[NUMBUFLEN];
|
||||
vim_snprintf(winid, sizeof(winid), "%d", first_size_win->handle);
|
||||
apply_autocmds(EVENT_WINRESIZED, winid, winid, false, first_size_win->w_buffer);
|
||||
buf_T *buf = bufref_valid(&resize_bufref) ? resize_bufref.br_buf : curbuf;
|
||||
apply_autocmds(EVENT_WINRESIZED, resize_winid, resize_winid, false, buf);
|
||||
}
|
||||
restore_v_event(v_event, &save_v_event);
|
||||
}
|
||||
@@ -5743,9 +5756,8 @@ void may_trigger_win_scrolled_resized(void)
|
||||
tv_dict_set_keys_readonly(v_event);
|
||||
tv_dict_unref(scroll_dict);
|
||||
|
||||
char winid[NUMBUFLEN];
|
||||
vim_snprintf(winid, sizeof(winid), "%d", first_scroll_win->handle);
|
||||
apply_autocmds(EVENT_WINSCROLLED, winid, winid, false, first_scroll_win->w_buffer);
|
||||
buf_T *buf = bufref_valid(&scroll_bufref) ? scroll_bufref.br_buf : curbuf;
|
||||
apply_autocmds(EVENT_WINSCROLLED, scroll_winid, scroll_winid, false, buf);
|
||||
|
||||
restore_v_event(v_event, &save_v_event);
|
||||
}
|
||||
|
||||
@@ -349,4 +349,54 @@ describe('WinScrolled', function()
|
||||
[winid_str] = { leftcol = 0, topline = -3, topfill = 0, width = 0, height = 0, skipcol = 0 },
|
||||
}, eval('g:v_event'))
|
||||
end)
|
||||
|
||||
it('does not crash when WinResized closes popup before WinScrolled #35803', function()
|
||||
exec([[
|
||||
set scrolloff=0
|
||||
call setline(1, range(1, 100))
|
||||
|
||||
" Create first popup window (will be resized and closed)
|
||||
let buf1 = nvim_create_buf(v:false, v:true)
|
||||
call nvim_buf_set_lines(buf1, 0, -1, v:false, map(range(1, 50), 'string(v:val)'))
|
||||
let popup1 = nvim_open_win(buf1, v:false, {
|
||||
\ 'relative': 'editor',
|
||||
\ 'width': 20,
|
||||
\ 'height': 5,
|
||||
\ 'col': 10,
|
||||
\ 'row': 5
|
||||
\ })
|
||||
|
||||
" Create second popup window (will be scrolled)
|
||||
let buf2 = nvim_create_buf(v:false, v:true)
|
||||
call nvim_buf_set_lines(buf2, 0, -1, v:false, map(range(1, 50), 'string(v:val)'))
|
||||
let popup2 = nvim_open_win(buf2, v:false, {
|
||||
\ 'relative': 'editor',
|
||||
\ 'width': 20,
|
||||
\ 'height': 5,
|
||||
\ 'col': 35,
|
||||
\ 'row': 5
|
||||
\ })
|
||||
|
||||
let g:resized = 0
|
||||
let g:scrolled = 0
|
||||
|
||||
" WinResized autocmd resizes and closes the first popup
|
||||
autocmd WinResized * let g:resized += 1 | call nvim_win_set_height(popup1, 10) | call nvim_win_close(popup1, v:true)
|
||||
" WinScrolled autocmd scrolls the second popup
|
||||
autocmd WinScrolled * let g:scrolled += 1 | call nvim_win_call(popup2, {-> execute('normal! \<C-E>')})
|
||||
]])
|
||||
eq(0, eval('g:resized'))
|
||||
eq(0, eval('g:scrolled'))
|
||||
|
||||
-- Trigger a resize on popup1, which will close it
|
||||
-- This should trigger WinResized (which closes popup1) and WinScrolled (which scrolls popup2)
|
||||
-- Before the fix, WinScrolled would use the freed pointer causing a crash
|
||||
api.nvim_win_set_height(eval('popup1'), 8)
|
||||
|
||||
-- The key is that it should not crash when WinResized closes a window
|
||||
-- that WinScrolled might have referenced via a stale buf_T pointer
|
||||
assert_alive()
|
||||
-- Verify autocmds were actually triggered
|
||||
eq(1, eval('g:resized > 0'))
|
||||
end)
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user