summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorBarrett Ruth <62671086+barrettruth@users.noreply.github.com>2026-04-23 06:31:24 -0400
committergithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>2026-04-23 23:05:20 +0000
commit39e8c584d5b51681e0bcc98dce6d205a6acf285d (patch)
tree75cc4370943365ff940ac6a9c04e3e009fd45cd4
parentbc288ee3e9a81b6e63ac5ab0116110cf5a6e56b0 (diff)
fix(terminal): memory leak in pending TermRequest StringBuilder #39333
Problem: Destroying a terminal with pending `TermRequest` events leaks memory. Solution: Make `emit_termrequest` the sole owner of its `pending_send` allocation. (cherry picked from commit 19ef632decf7e5d3d9bb70f347c88b92c138b45e)
-rw-r--r--src/nvim/terminal.c3
-rw-r--r--test/functional/terminal/parser_spec.lua13
2 files changed, 15 insertions, 1 deletions
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 96b2d8e215..004a00265a 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -247,6 +247,8 @@ static void emit_termrequest(void **argv)
buf_T *buf = handle_get_buffer(buf_handle);
if (!buf || buf->terminal == NULL) { // Terminal already closed.
xfree(sequence);
+ kv_destroy(*pending_send);
+ xfree(pending_send);
return;
}
Terminal *term = buf->terminal;
@@ -1232,7 +1234,6 @@ void terminal_destroy(Terminal **termpp)
kv_destroy(term->selection);
kv_destroy(term->termrequest_buffer);
vterm_free(term->vt);
- xfree(term->pending.send);
multiqueue_free(term->pending.events);
xfree(term);
*termpp = NULL; // coverity[dead-store]
diff --git a/test/functional/terminal/parser_spec.lua b/test/functional/terminal/parser_spec.lua
index 64aa3ea134..962ca24793 100644
--- a/test/functional/terminal/parser_spec.lua
+++ b/test/functional/terminal/parser_spec.lua
@@ -92,4 +92,17 @@ describe(':terminal', function()
exec_lua([[return _G.osc10_response]])
)
end)
+
+ it('does not leak pending TermRequest on buffer destroy #39332', function()
+ -- Send all OSC sequences in one exec_lua so that the event loop does not drain between the sends.
+ exec_lua(function(prefix, st)
+ local buf = vim.api.nvim_create_buf(false, true)
+ local chan = vim.api.nvim_open_term(buf, {})
+ vim.api.nvim_create_autocmd('TermRequest', { buffer = buf, callback = function() end })
+ vim.api.nvim_chan_send(chan, prefix .. '7;file:///a' .. st)
+ vim.api.nvim_chan_send(chan, prefix .. '7;file:///b' .. st)
+ vim.api.nvim_buf_delete(buf, { force = true })
+ end, OSC_PREFIX, ST)
+ assert_alive()
+ end)
end)