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