commit 64ce5382bd53de0500d9bcfba4e8d2a7f3421af6
parent 3e843a2891258c1a297daa97e9704d9bf110b760
Author: zeertzjq <zeertzjq@outlook.com>
Date: Wed, 11 Feb 2026 09:34:38 +0800
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.
Diffstat:
2 files changed, 26 insertions(+), 2 deletions(-)
diff --git 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
@@ -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)