From 6338d2d54b14a1570722f3b02509f9b94c562f92 Mon Sep 17 00:00:00 2001 From: Sean Dewar <6256228+seandewar@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:31:05 +0000 Subject: [PATCH] fix(window): win_move_after UAF from naughty autocmds (#37065) Problem: use-after-free in win_move_after if win_enter autocommands free win1/2. Solution: set w_pos_changed before calling win_enter. (cherry picked from commit d1189ea508c888ebf7468be3872c975e4ea264bb, also adding an import of "exec" in the test) --- src/nvim/window.c | 4 ++-- test/functional/autocmd/autocmd_spec.lua | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/nvim/window.c b/src/nvim/window.c index 0c0c00335a..7ff4a06e6d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2071,10 +2071,10 @@ void win_move_after(win_T *win1, win_T *win2) win_comp_pos(); // recompute w_winrow for all windows redraw_later(curwin, UPD_NOT_VALID); } - win_enter(win1, false); - win1->w_pos_changed = true; win2->w_pos_changed = true; + + win_enter(win1, false); } /// Compute maximum number of windows that can fit within "height" in frame "fr". diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua index 1b7275ebf6..9b5c1b5250 100644 --- a/test/functional/autocmd/autocmd_spec.lua +++ b/test/functional/autocmd/autocmd_spec.lua @@ -8,6 +8,7 @@ local dedent = t.dedent local eq = t.eq local neq = t.neq local eval = n.eval +local exec = n.exec local feed = n.feed local clear = n.clear local matches = t.matches @@ -695,4 +696,20 @@ describe('autocmd', function() vim.cmd "tabnew" ]] end) + + it('no use-after-free from win_enter autocommands in win_move_after', function() + exec [[ + split foo + split bar + lcd .. + wincmd b + ]] + eq(fn.winnr('$'), fn.winnr()) + -- Using DirChanged as Enter/Leave autocmds are blocked by :ball here. + exec [[ + autocmd DirChanged * ++once split flarb | only! + ball + ]] + eq('flarb', fn.bufname()) + end) end)