mirror of
https://github.com/neovim/neovim.git
synced 2026-04-22 07:15:34 +00:00
fix(terminal): heap UAF if buffer deleted during TermRequest (#37612)
Problem: Heap UAF if a terminal buffer is deleted during TermRequest in
Normal mode.
Solution: Increment terminal refcount before triggering TermRequest, and
destroy the terminal if the buffer is closed during that.
(cherry picked from commit b40880f88f)
This commit is contained in:
committed by
github-actions[bot]
parent
4792c29969
commit
3eef5752b3
@@ -264,8 +264,10 @@ static void emit_termrequest(void **argv)
|
|||||||
PUT_C(data, "sequence", STRING_OBJ(termrequest));
|
PUT_C(data, "sequence", STRING_OBJ(termrequest));
|
||||||
PUT_C(data, "cursor", ARRAY_OBJ(cursor));
|
PUT_C(data, "cursor", ARRAY_OBJ(cursor));
|
||||||
|
|
||||||
|
term->refcount++;
|
||||||
apply_autocmds_group(EVENT_TERMREQUEST, NULL, NULL, true, AUGROUP_ALL, buf, NULL,
|
apply_autocmds_group(EVENT_TERMREQUEST, NULL, NULL, true, AUGROUP_ALL, buf, NULL,
|
||||||
&DICT_OBJ(data));
|
&DICT_OBJ(data));
|
||||||
|
term->refcount--;
|
||||||
xfree(sequence);
|
xfree(sequence);
|
||||||
|
|
||||||
StringBuilder *term_pending_send = term->pending.send;
|
StringBuilder *term_pending_send = term->pending.send;
|
||||||
@@ -278,6 +280,13 @@ static void emit_termrequest(void **argv)
|
|||||||
term->pending.send = term_pending_send;
|
term->pending.send = term_pending_send;
|
||||||
}
|
}
|
||||||
xfree(pending_send);
|
xfree(pending_send);
|
||||||
|
|
||||||
|
// Terminal buffer closed during TermRequest in Normal mode: destroy the terminal.
|
||||||
|
// In Terminal mode term->refcount should still be non-zero here.
|
||||||
|
if (term->buf_handle == 0 && !term->refcount) {
|
||||||
|
term->destroy = true;
|
||||||
|
term->opts.close_cb(term->opts.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void schedule_termrequest(Terminal *term)
|
static void schedule_termrequest(Terminal *term)
|
||||||
|
|||||||
@@ -351,3 +351,64 @@ describe('autocmd TextChangedT,WinResized', function()
|
|||||||
eq({}, api.nvim_get_chan_info(term2)) -- Channel should've been cleaned up.
|
eq({}, api.nvim_get_chan_info(term2)) -- Channel should've been cleaned up.
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('no crash if :bwipe from TermClose is processed by', function()
|
||||||
|
local oldwin --- @type integer
|
||||||
|
local chan --- @type integer
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
command('autocmd! nvim.terminal')
|
||||||
|
oldwin = api.nvim_get_current_win()
|
||||||
|
command('new')
|
||||||
|
local buf = api.nvim_get_current_buf()
|
||||||
|
chan = api.nvim_open_term(buf, {})
|
||||||
|
api.nvim_set_var('chan', chan)
|
||||||
|
command(('autocmd TermClose <buffer> bwipe! %d'):format(buf))
|
||||||
|
command('let g:done = 0')
|
||||||
|
feed('i')
|
||||||
|
eq({ mode = 't', blocking = false }, api.nvim_get_mode())
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- @param event string Event name.
|
||||||
|
--- @param trigger_cmd string The Ex command to trigger the event.
|
||||||
|
local function test_case(event, trigger_cmd)
|
||||||
|
api.nvim_create_autocmd(
|
||||||
|
event,
|
||||||
|
{ nested = true, once = true, command = 'sleep 40m | let g:done = 1' }
|
||||||
|
)
|
||||||
|
exec_lua(function()
|
||||||
|
vim.cmd(trigger_cmd)
|
||||||
|
vim.defer_fn(function()
|
||||||
|
vim.fn.chanclose(chan)
|
||||||
|
end, 25)
|
||||||
|
end)
|
||||||
|
retry(nil, 1000, function()
|
||||||
|
eq(1, api.nvim_get_var('done'))
|
||||||
|
end)
|
||||||
|
assert_alive()
|
||||||
|
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
|
||||||
|
eq({ oldwin }, api.nvim_list_wins())
|
||||||
|
feed('<Ignore>') -- Add input to separate two RPC requests.
|
||||||
|
-- Channel should have been released.
|
||||||
|
eq({}, api.nvim_get_chan_info(chan))
|
||||||
|
end
|
||||||
|
|
||||||
|
it('WinResized autocommand in Terminal mode', function()
|
||||||
|
test_case('WinResized', 'vsplit')
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('TextChangedT autocommand in Terminal mode', function()
|
||||||
|
test_case('TextChangedT', [[call chansend(g:chan, "foo\r\nbar")]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('TermRequest autocommand in Terminal mode', function()
|
||||||
|
test_case('TermRequest', [[call chansend(g:chan, "\x1b]11;?\x1b\\")]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('TermRequest autocommand in Normal mode', function()
|
||||||
|
feed([[<C-\><C-N>]])
|
||||||
|
eq({ mode = 'nt', blocking = false }, api.nvim_get_mode())
|
||||||
|
test_case('TermRequest', [[call chansend(g:chan, "\x1b]11;?\x1b\\")]])
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|||||||
Reference in New Issue
Block a user