mirror of
https://github.com/macvim-dev/macvim.git
synced 2026-06-11 15:37:29 +02:00
patch 9.1.2093: heap-use-after-free when wiping buffer in TabClosedPre
Problem: heap-use-after-free when wiping buffer in TabClosedPre.
Solution: Check window_layout_locked() when closing window(s) in another
tabpage (zeertzjq).
closes: #19196
Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
617bf466bb
commit
8fc7042b3d
@@ -6593,6 +6593,9 @@ tabpage_close_other(tabpage_T *tp, int forceit)
|
||||
int done = 0;
|
||||
win_T *wp;
|
||||
|
||||
if (window_layout_locked(CMD_SIZE))
|
||||
return;
|
||||
|
||||
trigger_tabclosedpre(tp, TRUE);
|
||||
|
||||
// Limit to 1000 windows, autocommands may add a window while we close
|
||||
|
||||
+105
-16
@@ -5177,6 +5177,7 @@ func Test_OptionSet_cmdheight()
|
||||
call test_setmouse(&lines - 2, 1)
|
||||
call feedkeys("\<LeftDrag>", 'xt')
|
||||
call assert_equal(2, &l:ch)
|
||||
call feedkeys("\<LeftRelease>", 'xt')
|
||||
|
||||
tabnew | resize +1
|
||||
call assert_equal(1, &l:ch)
|
||||
@@ -5248,7 +5249,7 @@ func Test_WinScrolled_Resized_eiw()
|
||||
endfunc
|
||||
|
||||
" Test that TabClosedPre and TabClosed are triggered when closing a tab.
|
||||
func Test_autocmd_tabclosedpre()
|
||||
func Test_autocmd_TabClosedPre()
|
||||
augroup testing
|
||||
au TabClosedPre * call add(g:tabpagenr_pre, t:testvar)
|
||||
au TabClosed * call add(g:tabpagenr_post, t:testvar)
|
||||
@@ -5314,7 +5315,7 @@ func Test_autocmd_tabclosedpre()
|
||||
call assert_equal([1, 2], g:tabpagenr_pre)
|
||||
call assert_equal([2, 3], g:tabpagenr_post)
|
||||
|
||||
func ClearAutomcdAndCreateTabs()
|
||||
func ClearAutocmdAndCreateTabs()
|
||||
au! TabClosedPre
|
||||
bw!
|
||||
e Z
|
||||
@@ -5337,41 +5338,41 @@ func Test_autocmd_tabclosedpre()
|
||||
call CleanUpTestAuGroup()
|
||||
|
||||
" Close tab in TabClosedPre autocmd
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * tabclose
|
||||
call assert_fails('tabclose', 'E1312:')
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * tabclose
|
||||
call assert_fails('tabclose 2', 'E1312:')
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * tabclose 1
|
||||
call assert_fails('tabclose', 'E1312:')
|
||||
|
||||
" Close other (all) tabs in TabClosedPre autocmd
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * tabonly
|
||||
call assert_fails('tabclose', 'E1312:')
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * tabonly
|
||||
call assert_fails('tabclose 2', 'E1312:')
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * tabclose 4
|
||||
call assert_fails('tabclose 2', 'E1312:')
|
||||
|
||||
" Open new tabs in TabClosedPre autocmd
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * tabnew D
|
||||
call assert_fails('tabclose', 'E1312:')
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * tabnew D
|
||||
call assert_fails('tabclose 1', 'E1312:')
|
||||
|
||||
" Moving the tab page in TabClosedPre autocmd
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * tabmove 0
|
||||
tabclose
|
||||
call assert_equal('1>Z2A3B', GetTabs())
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * tabmove 0
|
||||
tabclose 1
|
||||
call assert_equal('1A2B3>C', GetTabs())
|
||||
@@ -5379,11 +5380,11 @@ func Test_autocmd_tabclosedpre()
|
||||
call assert_equal('1>C', GetTabs())
|
||||
|
||||
" Switching tab page in TabClosedPre autocmd
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * tabnext | e Y
|
||||
tabclose
|
||||
call assert_equal('1Y2A3>B', GetTabs())
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * tabnext | e Y
|
||||
tabclose 1
|
||||
call assert_equal('1Y2B3>C', GetTabs())
|
||||
@@ -5391,10 +5392,10 @@ func Test_autocmd_tabclosedpre()
|
||||
call assert_equal('1>Y', GetTabs())
|
||||
|
||||
" Create new windows in TabClosedPre autocmd
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * split | e X| vsplit | e Y | split | e Z
|
||||
call assert_fails('tabclose', 'E242:')
|
||||
call ClearAutomcdAndCreateTabs()
|
||||
call ClearAutocmdAndCreateTabs()
|
||||
au TabClosedPre * new X | new Y | new Z
|
||||
call assert_fails('tabclose 1', 'E242:')
|
||||
|
||||
@@ -5429,6 +5430,94 @@ func Test_autocmd_tabclosedpre()
|
||||
only
|
||||
tabonly
|
||||
bw!
|
||||
delfunc ClearAutocmdAndCreateTabs
|
||||
delfunc GetTabs
|
||||
endfunc
|
||||
|
||||
" This used to cause heap-use-after-free.
|
||||
func Run_test_TabClosedPre_wipe_buffer(split_cmds)
|
||||
file Xa
|
||||
exe a:split_cmds
|
||||
autocmd TabClosedPre * ++once tabnext | bwipe! Xa
|
||||
" Closing window inside TabClosedPre is not allowed.
|
||||
call assert_fails('tabonly', 'E1312:')
|
||||
|
||||
%bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_TabClosedPre_wipe_buffer()
|
||||
" Test with Xa only in other tab pages.
|
||||
call Run_test_TabClosedPre_wipe_buffer('split | tab split | tabnew Xb')
|
||||
" Test with Xa in both current and other tab pages.
|
||||
call Run_test_TabClosedPre_wipe_buffer('split | tab split | new Xb')
|
||||
endfunc
|
||||
|
||||
func Test_TabClosedPre_mouse()
|
||||
func MyTabline()
|
||||
let cnt = tabpagenr('$')
|
||||
return range(1, cnt)->mapnew({_, n -> $'%{n}X|Close{n}|%X'})->join('')
|
||||
endfunc
|
||||
|
||||
let save_mouse = &mouse
|
||||
if has('gui')
|
||||
set guioptions-=e
|
||||
endif
|
||||
set mouse=a tabline=%!MyTabline()
|
||||
|
||||
func OpenTwoTabPages()
|
||||
%bwipe!
|
||||
file Xa | split | split
|
||||
let g:Xa_bufnr = bufnr()
|
||||
tabnew Xb | split
|
||||
let g:Xb_bufnr = bufnr()
|
||||
redraw!
|
||||
call assert_match('^|Close1||Close2| *$', Screenline(1))
|
||||
call assert_equal(2, tabpagenr('$'))
|
||||
endfunc
|
||||
|
||||
autocmd! TabClosedPre
|
||||
call OpenTwoTabPages()
|
||||
let g:autocmd_bufnrs = []
|
||||
autocmd TabClosedPre * let g:autocmd_bufnrs += [tabpagebuflist()]
|
||||
call test_setmouse(1, 2)
|
||||
call feedkeys("\<LeftMouse>\<LeftRelease>", 'tx')
|
||||
call assert_equal(1, tabpagenr('$'))
|
||||
call assert_equal([[g:Xa_bufnr]->repeat(3)], g:autocmd_bufnrs)
|
||||
call assert_equal([g:Xb_bufnr]->repeat(2), tabpagebuflist())
|
||||
|
||||
call OpenTwoTabPages()
|
||||
let g:autocmd_bufnrs = []
|
||||
autocmd TabClosedPre * call feedkeys("\<LeftRelease>\<LeftMouse>", 'tx')
|
||||
call test_setmouse(1, 2)
|
||||
" Closing tab page inside TabClosedPre is not allowed.
|
||||
call assert_fails('call feedkeys("\<LeftMouse>", "tx")', 'E1312:')
|
||||
call feedkeys("\<LeftRelease>", 'tx')
|
||||
|
||||
autocmd! TabClosedPre
|
||||
call OpenTwoTabPages()
|
||||
let g:autocmd_bufnrs = []
|
||||
autocmd TabClosedPre * let g:autocmd_bufnrs += [tabpagebuflist()]
|
||||
call test_setmouse(1, 10)
|
||||
call feedkeys("\<LeftMouse>\<LeftRelease>", 'tx')
|
||||
call assert_equal(1, tabpagenr('$'))
|
||||
call assert_equal([[g:Xb_bufnr]->repeat(2)], g:autocmd_bufnrs)
|
||||
call assert_equal([g:Xa_bufnr]->repeat(3), tabpagebuflist())
|
||||
|
||||
call OpenTwoTabPages()
|
||||
let g:autocmd_bufnrs = []
|
||||
autocmd TabClosedPre * call feedkeys("\<LeftRelease>\<LeftMouse>", 'tx')
|
||||
call test_setmouse(1, 10)
|
||||
" Closing tab page inside TabClosedPre is not allowed.
|
||||
call assert_fails('call feedkeys("\<LeftMouse>", "tx")', 'E1312:')
|
||||
call feedkeys("\<LeftRelease>", 'tx')
|
||||
|
||||
autocmd! TabClosedPre
|
||||
%bwipe!
|
||||
unlet g:Xa_bufnr g:Xb_bufnr g:autocmd_bufnrs
|
||||
let &mouse = save_mouse
|
||||
set tabline& guioptions&
|
||||
delfunc MyTabline
|
||||
delfunc OpenTwoTabPages
|
||||
endfunc
|
||||
|
||||
func Test_eventignorewin_non_current()
|
||||
|
||||
@@ -734,6 +734,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
2093,
|
||||
/**/
|
||||
2092,
|
||||
/**/
|
||||
|
||||
+11
-1
@@ -124,7 +124,8 @@ frames_locked(void)
|
||||
/*
|
||||
* When the window layout cannot be changed give an error and return TRUE.
|
||||
* "cmd" indicates the action being performed and is used to pick the relevant
|
||||
* error message.
|
||||
* error message. When closing window(s) and the command isn't easy to know,
|
||||
* passing CMD_SIZE will also work.
|
||||
*/
|
||||
int
|
||||
window_layout_locked(enum CMD_index cmd)
|
||||
@@ -2530,6 +2531,8 @@ close_windows(
|
||||
if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
|
||||
&& !(win_locked(wp) || wp->w_buffer->b_locked > 0))
|
||||
{
|
||||
if (window_layout_locked(CMD_SIZE))
|
||||
goto theend; // Only give one error message.
|
||||
if (win_close(wp, FALSE) == FAIL)
|
||||
// If closing the window fails give up, to avoid looping
|
||||
// forever.
|
||||
@@ -2551,6 +2554,8 @@ close_windows(
|
||||
if (wp->w_buffer == buf
|
||||
&& !(win_locked(wp) || wp->w_buffer->b_locked > 0))
|
||||
{
|
||||
if (window_layout_locked(CMD_SIZE))
|
||||
goto theend; // Only give one error message.
|
||||
win_close_othertab(wp, FALSE, tp);
|
||||
|
||||
// Start all over, the tab page may be closed and
|
||||
@@ -2560,6 +2565,7 @@ close_windows(
|
||||
}
|
||||
}
|
||||
|
||||
theend:
|
||||
if (RedrawingDisabled > 0)
|
||||
--RedrawingDisabled;
|
||||
|
||||
@@ -3420,6 +3426,10 @@ win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
|
||||
tabpage_T *ptp = NULL;
|
||||
int free_tp = FALSE;
|
||||
|
||||
// Commands that may call win_close_othertab() already check this, but
|
||||
// check here again just in case.
|
||||
if (window_layout_locked(CMD_SIZE))
|
||||
return;
|
||||
// Get here with win->w_buffer == NULL when win_close() detects the tab
|
||||
// page changed.
|
||||
if (win_locked(win) || (win->w_buffer != NULL
|
||||
|
||||
Reference in New Issue
Block a user