From 1c57e4cb4f1748fc1813e741cb03bbc4acbad252 Mon Sep 17 00:00:00 2001 From: Sean Dewar <6256228+seandewar@users.noreply.github.com> Date: Sun, 15 Mar 2026 21:38:25 +0000 Subject: [PATCH] fix(terminal): heap UAF from autocmds when entering #38316 Problem: heap-use-after-free possible when entering Terminal mode if autocommands close the terminal. Solution: set the refcount. Skip to the end if we must close the terminal. --- src/nvim/terminal.c | 14 ++++++++++---- test/functional/terminal/buffer_spec.lua | 10 ++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index d07e74a966..4b168e3596 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -943,12 +943,18 @@ bool terminal_enter(void) // Don't fire TextChangedT from changes in Normal mode. curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); + // Don't let autocommands free the terminal now! + s->term->refcount++; apply_autocmds(EVENT_TERMENTER, NULL, NULL, false, curbuf); may_trigger_modechanged(); - - s->state.execute = terminal_execute; - s->state.check = terminal_check; - state_enter(&s->state); + s->term->refcount--; + if (s->term->buf_handle == 0) { + s->close = true; // skip entering and close + } else { + s->state.execute = terminal_execute; + s->state.check = terminal_check; + state_enter(&s->state); + } if (!s->got_bsl_o) { restart_edit = 0; diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index f868cfe539..9907e1e66b 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -1146,6 +1146,16 @@ describe(':terminal buffer', function() eq(false, api.nvim_buf_is_valid(term_buf)) end) + it('no heap-use-after-free from autocmds when entering terminal mode', function() + local chans = api.nvim_list_chans() + local buf = api.nvim_get_current_buf() + api.nvim_open_term(0, {}) + command('autocmd TermEnter,ModeChanged * ++once bwipeout!') + feed('i') + eq(false, api.nvim_buf_is_valid(buf)) + eq(chans, api.nvim_list_chans()) + end) + local enew_screen = [[ ^ | {1:~ }|*5