aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/edit.c2
-rw-r--r--src/nvim/normal.c2
-rw-r--r--src/nvim/testdir/test_autocmd.vim46
-rw-r--r--src/nvim/window.c16
-rw-r--r--test/functional/autocmd/winscrolled_spec.lua13
5 files changed, 59 insertions, 20 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 8b25378531..313e23fd3b 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1545,7 +1545,7 @@ static void ins_redraw(bool ready)
if (ready) {
// Trigger Scroll if viewport changed.
- may_trigger_winscrolled(curwin);
+ may_trigger_winscrolled();
}
// Trigger BufModified if b_changed_invalid is set.
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 5b3f27529c..a8769c4c80 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1225,7 +1225,7 @@ static void normal_check_window_scrolled(NormalState *s)
{
if (!finish_op) {
// Trigger Scroll if the viewport changed.
- may_trigger_winscrolled(curwin);
+ may_trigger_winscrolled();
}
}
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 4b4f6ad3d3..228145ec4d 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -265,17 +265,17 @@ func Test_WinScrolled()
CheckRunVimInTerminal
let lines =<< trim END
- set nowrap scrolloff=0
- for ii in range(1, 18)
- call setline(ii, repeat(nr2char(96 + ii), ii * 2))
- endfor
- let win_id = win_getid()
- let g:matched = v:false
- execute 'au WinScrolled' win_id 'let g:matched = v:true'
- let g:scrolled = 0
- au WinScrolled * let g:scrolled += 1
- au WinScrolled * let g:amatch = str2nr(expand('<amatch>'))
- au WinScrolled * let g:afile = str2nr(expand('<afile>'))
+ set nowrap scrolloff=0
+ for ii in range(1, 18)
+ call setline(ii, repeat(nr2char(96 + ii), ii * 2))
+ endfor
+ let win_id = win_getid()
+ let g:matched = v:false
+ execute 'au WinScrolled' win_id 'let g:matched = v:true'
+ let g:scrolled = 0
+ au WinScrolled * let g:scrolled += 1
+ au WinScrolled * let g:amatch = str2nr(expand('<amatch>'))
+ au WinScrolled * let g:afile = str2nr(expand('<afile>'))
END
call writefile(lines, 'Xtest_winscrolled')
let buf = RunVimInTerminal('-S Xtest_winscrolled', {'rows': 6})
@@ -315,6 +315,30 @@ func Test_WinScrolled()
call delete('Xtest_winscrolled')
endfunc
+func Test_WinScrolled_close_curwin()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set nowrap scrolloff=0
+ call setline(1, ['aaa', 'bbb'])
+ vsplit
+ au WinScrolled * close
+ au VimLeave * call writefile(['123456'], 'Xtestout')
+ END
+ call writefile(lines, 'Xtest_winscrolled_close_curwin')
+ let buf = RunVimInTerminal('-S Xtest_winscrolled_close_curwin', {'rows': 6})
+
+ " This was using freed memory
+ call term_sendkeys(buf, "\<C-E>")
+ call TermWait(buf)
+ call StopVimInTerminal(buf)
+
+ call assert_equal(['123456'], readfile('Xtestout'))
+
+ call delete('Xtest_winscrolled_close_curwin')
+ call delete('Xtestout')
+endfunc
+
func Test_WinClosed()
" Test that the pattern is matched against the closed window's ID, and both
" <amatch> and <afile> are set to it.
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 08cdfa4ebe..2ca5128445 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -5246,8 +5246,8 @@ void shell_new_columns(void)
win_reconfig_floats(); // The size of floats might change
}
-/// Trigger WinScrolled autocmd if window has scrolled.
-void may_trigger_winscrolled(win_T *wp)
+/// Trigger WinScrolled for "curwin" if needed.
+void may_trigger_winscrolled(void)
{
static bool recursive = false;
@@ -5255,6 +5255,7 @@ void may_trigger_winscrolled(win_T *wp)
return;
}
+ win_T *wp = curwin;
if (wp->w_last_topline != wp->w_topline
|| wp->w_last_leftcol != wp->w_leftcol
|| wp->w_last_width != wp->w_width
@@ -5266,10 +5267,13 @@ void may_trigger_winscrolled(win_T *wp)
apply_autocmds(EVENT_WINSCROLLED, winid, winid, false, wp->w_buffer);
recursive = false;
- wp->w_last_topline = wp->w_topline;
- wp->w_last_leftcol = wp->w_leftcol;
- wp->w_last_width = wp->w_width;
- wp->w_last_height = wp->w_height;
+ // an autocmd may close the window, "wp" may be invalid now
+ if (win_valid_any_tab(wp)) {
+ wp->w_last_topline = wp->w_topline;
+ wp->w_last_leftcol = wp->w_leftcol;
+ wp->w_last_width = wp->w_width;
+ wp->w_last_height = wp->w_height;
+ }
}
}
diff --git a/test/functional/autocmd/winscrolled_spec.lua b/test/functional/autocmd/winscrolled_spec.lua
index 01c2631b62..5c1b758961 100644
--- a/test/functional/autocmd/winscrolled_spec.lua
+++ b/test/functional/autocmd/winscrolled_spec.lua
@@ -6,12 +6,14 @@ local eval = helpers.eval
local command = helpers.command
local feed = helpers.feed
local meths = helpers.meths
+local assert_alive = helpers.assert_alive
+
+before_each(clear)
describe('WinScrolled', function()
local win_id
before_each(function()
- clear()
win_id = meths.get_current_win().id
command(string.format('autocmd WinScrolled %d let g:matched = v:true', win_id))
command('let g:scrolled = 0')
@@ -72,3 +74,12 @@ describe('WinScrolled', function()
eq(1, eval('g:scrolled'))
end)
end)
+
+it('closing window in WinScrolled does not cause use-after-free #13265', function()
+ local lines = {'aaa', 'bbb'}
+ meths.buf_set_lines(0, 0, -1, true, lines)
+ command('vsplit')
+ command('autocmd WinScrolled * close')
+ feed('<C-E>')
+ assert_alive()
+end)