fix(socket): --listen silently truncates long paths #40625

Problem:
A `--listen` path longer than the system socket path limit (~104 bytes on
macOS, 108 on Linux) is silently truncated by `uv_pipe_bind()`. Nvim either
serves a socket at a different path than `v:servername` reports, or fails with
an error that blames the full untruncated path (confusing):

    nvim: Failed to --listen: address already in use: "<full path>"

Steps to reproduce:

    $ nvim --listen /var/folders/g7/9y_ydbnj2fs_fvp8xf44p8gc0000gn/T//nvim/-Users-rpatterson-Projects-src-github.com-neovide-neovide --embed -p
    nvim: Failed to --listen: address already in use: "/var/folders/g7/9y_ydbnj2fs_fvp8xf44p8gc0000gn/T//nvim/-Users-rpatterson-Projects-src-github.com-neovide-neovide"

    $ ls /var/folders/g7/9y_ydbnj2fs_fvp8xf44p8gc0000gn/T//nvim/-Users-rpatterson-Projects-src-github.com-neovide-neovide
    "/var/folders/g7/9y_ydbnj2fs_fvp8xf44p8gc0000gn/T//nvim/-Users-rpatterson-Projects-src-github.com-neovide-neovide": No such file or directory (os error 2)

    $ ls -l /var/folders/g7/9y_ydbnj2fs_fvp8xf44p8gc0000gn/T//nvim/
    srwxr-xr-x@ - rpatterson 31 Mar 10:24 -Users-rpatterson-Projects-src-github.com-neovid

Solution:
Bind with `uv_pipe_bind2()` and `UV_PIPE_NO_TRUNCATE` (libuv 1.46+), so
a too-long path fails up front with the actual reason:

    nvim: Failed to --listen: invalid argument: "<full path>"
This commit is contained in:
MAAZIZ Adel Ayoub
2026-07-09 21:07:17 +01:00
committed by GitHub
parent a54a7f6a78
commit de141b4679
3 changed files with 23 additions and 4 deletions

View File

@@ -169,7 +169,8 @@ int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb)
}
uv_freeaddrinfo(watcher->uv.tcp.addrinfo);
} else {
result = uv_pipe_bind(&watcher->uv.pipe.handle, watcher->addr);
result = uv_pipe_bind2(&watcher->uv.pipe.handle, watcher->addr, strlen(watcher->addr),
UV_PIPE_NO_TRUNCATE);
// If bind failed with EACCES/EADDRINUSE, check if socket is stale
if (result == UV_EACCES || result == UV_EADDRINUSE) {
@@ -196,7 +197,8 @@ int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb)
watcher->stream->data = watcher;
// Retry bind with fresh handle
result = uv_pipe_bind(&watcher->uv.pipe.handle, watcher->addr);
result = uv_pipe_bind2(&watcher->uv.pipe.handle, watcher->addr, strlen(watcher->addr),
UV_PIPE_NO_TRUNCATE);
}
} else {
// Socket is alive - this is a real error

View File

@@ -364,6 +364,15 @@ describe('startup --listen', function()
('nvim.*: Failed to %%-%%-listen: [^:]+ already [^:]+: "%s"'):format(vim.pesc(in_use))
)
_test({ '--listen', '/' }, nil, 'nvim.*: Failed to %-%-listen: [^:]+: "/"')
if not is_os('win') then
-- Too-long path is rejected, not silently truncated. #38623
local too_long = './Xtest-listen-' .. ('x'):rep(192)
_test(
{ '--listen', too_long },
nil,
('nvim.*: Failed to %%-%%-listen: invalid argument: "%s"'):format(vim.pesc(too_long))
)
end
_test(
{ '--listen', 'https://example.com' },
nil,

View File

@@ -376,8 +376,8 @@ describe('XDG defaults', function()
if not is_os('win') then
-- Broken XDG vars cause serverstart() to fail (except on Windows, where servernames are not
-- informed by $XDG_STATE_HOME).
t.matches('Failed to start server: no such file or directory', t.pcall_err(fn.serverstart))
assert_log('Failed to start server: no such file or directory: /X/X/X', testlog, 10)
t.matches('Failed to start server: invalid argument', t.pcall_err(fn.serverstart))
assert_log('Failed to start server: invalid argument: /X/X/X', testlog, 10)
end
local vimruntime, libdir = vimruntime_and_libdir()
@@ -962,6 +962,13 @@ describe('stdpath()', function()
it('$NVIM_APPNAME relative path', function()
local tmpdir = t.tmpname(false)
t.mkdir(tmpdir)
-- Short runtime dir so the autogenerated server socket fits in sun_path even
-- when $TMPDIR is deep (otherwise the server fails to bind, see #38623).
local xdg_run = 'Xtest-appname-run'
mkdir(xdg_run)
finally(function()
rmdir(xdg_run)
end)
clear({
args_rm = { '--listen' },
@@ -969,6 +976,7 @@ describe('stdpath()', function()
NVIM_APPNAME = 'relative/appname',
NVIM_LOG_FILE = testlog,
TMPDIR = tmpdir,
XDG_RUNTIME_DIR = xdg_run,
},
})