commit 7ca3a56258b9d2fa950c24767c5ead1d208bfbe0
parent 1e6c4ea896b784754cb0ba18ea510a9c407ab54c
Author: zeertzjq <zeertzjq@outlook.com>
Date: Sat, 31 Jan 2026 21:09:06 +0800
fix(ui): don't crash if maximum UI count reached (#37636)
Diffstat:
4 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
@@ -162,10 +162,13 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dict opt
"UI already attached to channel: %" PRId64, channel_id);
return;
}
+ if (!ui_can_attach_more()) {
+ api_set_error(err, kErrorTypeException, "Maximum UI count reached");
+ return;
+ }
if (width <= 0 || height <= 0) {
- api_set_error(err, kErrorTypeValidation,
- "Expected width > 0 and height > 0");
+ api_set_error(err, kErrorTypeValidation, "Expected width > 0 and height > 0");
return;
}
RemoteUI *ui = xcalloc(1, sizeof(RemoteUI));
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
@@ -371,9 +371,14 @@ void do_autocmd_uienter_all(void)
}
}
+bool ui_can_attach_more(void)
+{
+ return ui_count < MAX_UI_COUNT;
+}
+
void ui_attach_impl(RemoteUI *ui, uint64_t chanid)
{
- if (ui_count == MAX_UI_COUNT) {
+ if (ui_count >= MAX_UI_COUNT) {
abort();
}
if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug]
@@ -419,7 +424,7 @@ void ui_detach_impl(RemoteUI *ui, uint64_t chanid)
}
}
- if (shift_index == MAX_UI_COUNT) {
+ if (shift_index >= MAX_UI_COUNT) {
abort();
}
diff --git a/test/functional/api/ui_spec.lua b/test/functional/api/ui_spec.lua
@@ -71,6 +71,23 @@ describe('nvim_ui_attach()', function()
pcall_err(request, 'nvim_ui_attach', 40, 10, { rgb = false })
)
end)
+
+ it('does not crash if maximum UI count is reached', function()
+ t.skip(t.is_os('win'), 'n.connect() hangs on Windows')
+ local server = api.nvim_get_vvar('servername')
+ local screens = {} --- @type test.functional.ui.screen[]
+ for i = 1, 16 do
+ screens[i] = Screen.new(nil, nil, nil, n.connect(server))
+ end
+ eq(
+ -- 0 is kErrorTypeException
+ { false, { 0, 'Maximum UI count reached' } },
+ { n.connect(server):request('nvim_ui_attach', 80, 24, {}) }
+ )
+ for i = 1, 16 do
+ screens[i]:detach()
+ end
+ end)
end)
describe('nvim_ui_send', function()
@@ -100,9 +117,9 @@ describe('nvim_ui_send', function()
poke_eventloop()
screen:expect([[
- ^ |
- {1:~ }|*8
- |
+ ^ |
+ {1:~ }|*8
+ |
]])
eq('Hello world', table.concat(read_data))
@@ -130,9 +147,9 @@ describe('nvim_ui_send', function()
poke_eventloop()
screen:expect([[
- ^ |
- {1:~ }|*8
- |
+ ^ |
+ {1:~ }|*8
+ |
]])
eq('', table.concat(read_data))
diff --git a/test/functional/ui/embed_spec.lua b/test/functional/ui/embed_spec.lua
@@ -299,7 +299,7 @@ end)
describe('--embed --listen UI', function()
it('waits for connection on listening address', function()
- t.skip(t.is_os('win'))
+ t.skip(t.is_os('win'), 'n.connect() hangs on Windows')
clear()
local child_server = assert(n.new_pipename())
fn.jobstart({