vim-patch:9.1.0671: Problem: crash with WinNewPre autocommand

Problem:  crash with WinNewPre autocommand, because window
          structures are not yet safe to use
Solution: Don't trigger WinNewPre on :tabnew

fb3f969936

Cherry-pick doc updates from latest Vim runtime.

Co-authored-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
zeertzjq
2026-01-16 12:31:28 +08:00
parent 4544cec168
commit 786c5fbdec
5 changed files with 56 additions and 20 deletions

View File

@@ -1233,10 +1233,12 @@ WinLeave Before leaving a window. If the window to be
Not used for ":qa" or ":q" when exiting Vim.
Before WinClosed.
*WinNewPre*
WinNewPre Before creating a new window. Triggered
WinNewPre Before creating a new window. Triggered
before commands that modify window layout by
creating a split or new tab page. Not done for
the first window, when Vim has just started.
creating a split.
Not done when creating tab pages and for the
first window, as the window structure is not
initialized yet and so is generally not safe.
It is not allowed to modify window layout
while executing commands for the WinNewPre
event.

View File

@@ -4362,6 +4362,10 @@ void free_tabpage(tabpage_T *tp)
///
/// It will edit the current buffer, like after :split.
///
/// Does not trigger WinNewPre, since the window structures
/// are not completely setup yet and could cause dereferencing
/// NULL pointers
///
/// @param after Put new tabpage after tabpage "after", or after the current
/// tabpage in case of 0.
/// @param filename Will be passed to apply_autocmds().
@@ -4391,8 +4395,6 @@ int win_new_tabpage(int after, char *filename)
curtab = newtp;
trigger_winnewpre();
// Create a new empty window.
if (win_alloc_firstwin(old_curtab->tp_curwin) == OK) {
// Make the new Tab page the new topframe.

Binary file not shown.

View File

@@ -276,6 +276,7 @@ endfunc
func Test_win_tab_autocmd()
let g:record = []
defer CleanUpTestAuGroup()
augroup testing
au WinNewPre * call add(g:record, 'WinNewPre')
au WinNew * call add(g:record, 'WinNew')
@@ -295,7 +296,7 @@ func Test_win_tab_autocmd()
call assert_equal([
\ 'WinNewPre', 'WinLeave', 'WinNew', 'WinEnter',
\ 'WinLeave', 'TabLeave', 'WinNewPre', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter',
\ 'WinLeave', 'TabLeave', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter',
\ 'WinLeave', 'TabLeave', 'WinClosed', 'TabClosed', 'WinEnter', 'TabEnter',
\ 'WinLeave', 'WinClosed', 'WinEnter'
\ ], g:record)
@@ -306,7 +307,7 @@ func Test_win_tab_autocmd()
bwipe somefile
call assert_equal([
\ 'WinLeave', 'TabLeave', 'WinNewPre', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter',
\ 'WinLeave', 'TabLeave', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter',
\ 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter',
\ 'WinClosed', 'TabClosed'
\ ], g:record)
@@ -323,9 +324,6 @@ func Test_win_tab_autocmd()
\ 'WinNewPre', 'WinLeave', 'WinNew', 'WinEnter'
\ ], g:record)
augroup testing
au!
augroup END
unlet g:record
endfunc
@@ -337,17 +335,15 @@ func Test_WinNewPre()
au WinNewPre * call add(g:layouts_pre, winlayout())
au WinNew * call add(g:layouts_post, winlayout())
augroup END
defer CleanUpTestAuGroup()
split
call assert_notequal(g:layouts_pre[0], g:layouts_post[0])
split
call assert_equal(g:layouts_pre[1], g:layouts_post[0])
call assert_notequal(g:layouts_pre[1], g:layouts_post[1])
" not triggered for tabnew
tabnew
call assert_notequal(g:layouts_pre[2], g:layouts_post[1])
call assert_notequal(g:layouts_pre[2], g:layouts_post[2])
augroup testing
au!
augroup END
call assert_equal(2, len(g:layouts_pre))
unlet g:layouts_pre
unlet g:layouts_post
@@ -390,9 +386,6 @@ func Test_WinNewPre()
let g:caught += 1
endtry
call assert_equal(4, g:caught)
augroup testing
au!
augroup END
unlet g:caught
endfunc
@@ -2900,7 +2893,8 @@ endfunc
func Test_autocmd_nested()
let g:did_nested = 0
augroup Testing
defer CleanUpTestAuGroup()
augroup testing
au WinNew * edit somefile
au BufNew * let g:did_nested = 1
augroup END
@@ -2910,7 +2904,7 @@ func Test_autocmd_nested()
bwipe! somefile
" old nested argument still works
augroup Testing
augroup testing
au!
au WinNew * nested edit somefile
au BufNew * let g:did_nested = 1
@@ -4458,6 +4452,38 @@ func Test_BufEnter_botline()
set hidden&vim
endfunc
" those commands caused null pointer access, see #15464
func Test_WinNewPre_crash()
defer CleanUpTestAuGroup()
let _cmdheight=&cmdheight
augroup testing
au!
autocmd WinNewPre * redraw
augroup END
tabnew
tabclose
augroup testing
au!
autocmd WinNewPre * wincmd t
augroup END
tabnew
tabclose
augroup testing
au!
autocmd WinNewPre * wincmd b
augroup END
tabnew
tabclose
augroup testing
au!
autocmd WinNewPre * set cmdheight+=1
augroup END
tabnew
tabclose
let &cmdheight=_cmdheight
endfunc
" This was using freed memory
func Test_autocmd_BufWinLeave_with_vsp()
new

View File

@@ -220,6 +220,12 @@ func Test_crash1_3()
call term_sendkeys(buf, args)
call TermWait(buf, 150)
let file = 'crash/nullpointer'
let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'\<cr>"
let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args)
call TermWait(buf, 50)
let file = 'crash/heap_overflow3'
let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file)