fix(tui): heap-use-after-free when resuming (#36387)

Discovered when writing more tests for suspend/resume.
It seems that this isn't always reproducible with ASAN due to the arena.
This commit is contained in:
zeertzjq
2025-10-30 13:26:49 +08:00
committed by GitHub
parent 69bddc089f
commit 50a38d9698
2 changed files with 70 additions and 3 deletions

View File

@@ -368,6 +368,7 @@ static void terminfo_start(TUIData *tui)
tui->input.tui_data = tui;
tui->ti_arena = (Arena)ARENA_EMPTY;
assert(tui->term == NULL);
char *term = os_getenv("TERM");
#ifdef MSWIN
@@ -384,9 +385,7 @@ static void terminfo_start(TUIData *tui)
bool found_in_db = false;
if (term) {
if (terminfo_from_unibilium(&tui->ti, term, &tui->ti_arena)) {
if (!tui->term) {
tui->term = arena_strdup(&tui->ti_arena, term);
}
tui->term = arena_strdup(&tui->ti_arena, term);
found_in_db = true;
}
}
@@ -590,6 +589,9 @@ static void terminfo_stop(TUIData *tui)
abort();
}
arena_mem_free(arena_finish(&tui->ti_arena));
// Avoid using freed memory.
memset(&tui->ti, 0, sizeof(tui->ti));
tui->term = NULL;
}
static void tui_terminal_start(TUIData *tui)

View File

@@ -4141,4 +4141,69 @@ describe('TUI client', function()
test_remote_tui_quit(42)
end)
end)
it('suspend/resume works with multiple clients', function()
local server_super, screen_server, screen_client = start_tui_and_remote_client()
local server_super_exec_lua = tt.make_lua_executor(server_super)
local screen_normal = [[
Hello, Worl^d |
{100:~ }|*3
{3:[No Name] [+] }|
|
{5:-- TERMINAL --} |
]]
local screen_suspended = [[
^ |
|*5
{5:-- TERMINAL --} |
]]
screen_client:expect({ grid = screen_normal, unchanged = true })
screen_server:expect({ grid = screen_normal, unchanged = true })
-- Suspend both clients.
feed_data(':suspend\r')
screen_client:expect({ grid = screen_suspended })
screen_server:expect({ grid = screen_suspended })
-- Resume the remote client.
exec_lua([[vim.uv.kill(vim.fn.jobpid(vim.bo.channel), 'sigcont')]])
screen_client:expect({ grid = screen_normal })
screen_server:expect({ grid = screen_suspended, unchanged = true })
-- Resume the embedding client.
server_super_exec_lua([[vim.uv.kill(vim.fn.jobpid(vim.bo.channel), 'sigcont')]])
screen_server:expect({ grid = screen_normal })
screen_client:expect({ grid = screen_normal, unchanged = true })
-- Suspend both clients again.
feed_data(':suspend\r')
screen_client:expect({ grid = screen_suspended })
screen_server:expect({ grid = screen_suspended })
-- Resume the remote client.
exec_lua([[vim.uv.kill(vim.fn.jobpid(vim.bo.channel), 'sigcont')]])
screen_client:expect({ grid = screen_normal })
screen_server:expect({ grid = screen_suspended, unchanged = true })
-- Suspend the remote client again.
feed_data(':suspend\r')
screen_client:expect({ grid = screen_suspended })
screen_server:expect({ grid = screen_suspended, unchanged = true })
-- Resume the embedding client.
server_super_exec_lua([[vim.uv.kill(vim.fn.jobpid(vim.bo.channel), 'sigcont')]])
screen_server:expect({ grid = screen_normal })
screen_client:expect({ grid = screen_suspended, unchanged = true })
-- Resume the remote client.
exec_lua([[vim.uv.kill(vim.fn.jobpid(vim.bo.channel), 'sigcont')]])
screen_client:expect({ grid = screen_normal })
screen_server:expect({ grid = screen_normal, unchanged = true })
feed_data(':quit!\r')
screen_server:expect({ any = vim.pesc('[Process exited 0]') })
screen_client:expect({ any = vim.pesc('[Process exited 0]') })
end)
end)