From 64ce5382bd53de0500d9bcfba4e8d2a7f3421af6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 11 Feb 2026 09:34:38 +0800 Subject: [PATCH] fix(channel): crash on failed sockconnect() (#37811) Problem: Crash on failed sockconnect() if a new connection is accepted while polling for uv events. Solution: Don't use channel_destroy_early(). Also test "tcp" mode failure properly. --- src/nvim/channel.c | 4 +++- test/functional/core/channels_spec.lua | 24 +++++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 3ffefd1298..1f9aebc941 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -474,7 +474,9 @@ uint64_t channel_connect(bool tcp, const char *address, bool rpc, CallbackReader channel = channel_alloc(kChannelStreamSocket); if (!socket_connect(&main_loop, &channel->stream.socket, tcp, address, timeout, error)) { - channel_destroy_early(channel); + // Don't use channel_destroy_early() as new channels may have been allocated + // by channel_from_connection() while polling for uv events. + channel_decref(channel); return 0; } diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua index 44342e05c1..7473b47b13 100644 --- a/test/functional/core/channels_spec.lua +++ b/test/functional/core/channels_spec.lua @@ -478,9 +478,31 @@ describe('channels', function() end) it('in "tcp" mode', function() + skip(not is_os('linux'), 'FIXME: hangs on non-Linux') eq( 'Vim:connection failed: connection refused', - pcall_err(fn.sockconnect, 'pipe', '127.0.0.1:0') + pcall_err(fn.sockconnect, 'tcp', '127.0.0.1:0') + ) + end) + + it('with another connection accepted while polling #37807', function() + local server = api.nvim_get_vvar('servername') + local invalid_pipe = n.new_pipename() + exec_lua(function() + vim.defer_fn(function() + vim.uv.sleep(50) -- Block the uv event loop. + vim.fn.sockconnect('pipe', invalid_pipe) + end, 10) + end) + vim.uv.sleep(20) + -- The server uv event loop is currently blocked, so the connection will + -- be accepted when sockconnect() polls. + local other_session = n.connect(server) + eq({ true, { 1000 } }, { other_session:request('nvim_list_wins') }) + other_session:close() + matches( + '^vim.schedule callback: Vim:connection failed: connection refused\n', + api.nvim_get_vvar('errmsg') ) end) end)