From 9129a9142862982bffc7d944db914c878a364ccd Mon Sep 17 00:00:00 2001 From: Samuel K Date: Fri, 3 Jul 2026 10:28:22 -0500 Subject: [PATCH] fix(agent): agent socket exceeds macos sun_path limit Signed-off-by: Samuel K --- pkg/config/pathmanager_darwin.go | 12 +++++++++++- pkg/ssh/server/agent.go | 21 +++++---------------- pkg/ssh/server/agent_test.go | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/pkg/config/pathmanager_darwin.go b/pkg/config/pathmanager_darwin.go index bf04565f1..20cce0a95 100644 --- a/pkg/config/pathmanager_darwin.go +++ b/pkg/config/pathmanager_darwin.go @@ -55,6 +55,16 @@ func (d *darwinPathManager) StateDir() (string, error) { return ensureDir(filepath.Join(home, "."+RepoName, "state")) } +// RuntimeDir returns a directory for runtime state, such as sockets and PID files. func (d *darwinPathManager) RuntimeDir() (string, error) { - return ensureDir(filepath.Join(os.TempDir(), fmt.Sprintf("%s-%d", RepoName, os.Getuid()))) + home, err := os.UserHomeDir() + if err != nil { + return "", fmt.Errorf("runtime dir: %w", err) + } + + dir, err := ensureDir(filepath.Join(home, "."+RepoName, "run")) + if err != nil { + return "", err + } + return dir, nil } diff --git a/pkg/ssh/server/agent.go b/pkg/ssh/server/agent.go index 79905990b..5fb4a590e 100644 --- a/pkg/ssh/server/agent.go +++ b/pkg/ssh/server/agent.go @@ -15,11 +15,8 @@ import ( const ( agentSocketDirPrefix = "devsy-ssh-agent-" - // maxAgentSocketPath is the most restrictive Unix-domain socket path - // length across supported platforms (macOS/BSD: sun_path[104]; - // Linux: sun_path[108]). bind(2) returns EINVAL when exceeded, which - // surfaces as "invalid argument" — a confusing error far from the cause. - // We check eagerly and fail with a clear message instead. + // maxAgentSocketPath is the size of the OS sun_path array. bind(2) returns + // EINVAL when the path length reaches this limit. maxAgentSocketPath = 104 // agentListenFile mirrors the socket filename ssh.NewAgentListener @@ -149,20 +146,12 @@ func SweepStaleAgentSockets() { } // checkAgentSocketPathLen verifies the eventual unix socket path fits within -// the OS sun_path limit. bind(2) returns the unhelpful EINVAL when it does -// not, so we validate up front and emit a clear, actionable error that -// names the offending path and the limit. +// the OS sun_path limit. func checkAgentSocketPathLen(dir string) error { sockPath := filepath.Join(dir, agentListenFile) - if len(sockPath) > maxAgentSocketPath { - log.Warnf( - "agent socket path exceeds max (%d > %d): %s", - len(sockPath), - maxAgentSocketPath, - sockPath, - ) + if len(sockPath) >= maxAgentSocketPath { return fmt.Errorf( - "agent socket path exceeds max (%d > %d): %s", + "agent socket path exceeds max (%d >= %d): %s", len(sockPath), maxAgentSocketPath, sockPath, diff --git a/pkg/ssh/server/agent_test.go b/pkg/ssh/server/agent_test.go index a5c1739bd..37aab5056 100644 --- a/pkg/ssh/server/agent_test.go +++ b/pkg/ssh/server/agent_test.go @@ -88,6 +88,20 @@ func TestCleanupAgentSocketDir(t *testing.T) { }) } +func TestCheckAgentSocketPathLen_Boundary(t *testing.T) { + dirFor := func(pathLen int) string { + suffixLen := len(string(os.PathSeparator)) + len(agentListenFile) + return strings.Repeat("a", pathLen-suffixLen) + } + + assert.NoError(t, checkAgentSocketPathLen(dirFor(maxAgentSocketPath-1)), + "path of maxAgentSocketPath-1 must be accepted") + assert.Error(t, checkAgentSocketPathLen(dirFor(maxAgentSocketPath)), + "path of exactly maxAgentSocketPath must be rejected (kernel returns EINVAL)") + assert.Error(t, checkAgentSocketPathLen(dirFor(maxAgentSocketPath+1)), + "path over maxAgentSocketPath must be rejected") +} + func TestSetupConnectionAgentListener_BadRuntimeDir(t *testing.T) { if runtime.GOOS == osWindows { t.Skip("unix socket based test")