Measure the socket path before tmux sees it - #730
Open
tony wants to merge 1 commit into
Open
Conversation
why: A tmux socket is a UNIX domain socket, so its path is capped by sockaddr_un -- 107 bytes on Linux, 103 on macOS. Server accepted any path and the overrun arrived at the first tmux command as a passed- through "File name too long", blamed on new-session rather than on the socket, and without the one number the caller needed: how far over. The length is usually inherited rather than typed -- a deep pytest tmp_path, an XDG runtime dir, a nested worktree, a long $TMUX_TMPDIR -- so the path that failed is one nobody wrote. Closes #725. what: - Add libtmux.exc.SocketPathTooLong, carrying the byte count, the limit, the path, and the socket name when the path was resolved rather than passed in - Add _internal.env.SOCKET_PATH_MAX_BYTES (sun_path size per platform, less the NUL) and check_socket_path_length(), which measures with os.fsencode as the kernel does - Measure in Server.__init__: a given socket_path, and the path a socket_name, a socket_name_factory, or a bare Server() resolves to under $TMUX_TMPDIR - Document the limit and the shorter-socket-dir workaround on Server, in the pytest plugin usage guide, and on the env var page - Test both routes, the boundary, and probe the running kernel to keep the per-platform size honest
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## fix/723-repr-socket-dir #730 +/- ##
===========================================================
+ Coverage 52.28% 52.35% +0.06%
===========================================================
Files 26 26
Lines 3737 3755 +18
Branches 747 749 +2
===========================================================
+ Hits 1954 1966 +12
- Misses 1479 1485 +6
Partials 304 304 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Server(...)accepting a socket path that cannot work. A tmux socket is a UNIX domain socket, so its path is capped bysockaddr_un— 107 bytes on Linux, 103 on macOS. Construction succeeded, and the overrun arrived at the first tmux command as a passed-throughFile name too long, blamed onnew-session.libtmux.exc.SocketPathTooLong, carrying the byte count, the limit, the path, and the socket name when the path was resolved rather than passed in.socket_path, the path asocket_nameorsocket_name_factory()resolves to, and the bareServer()default — whose whole path is inherited from$TMUX_TMPDIR.Server, in the pytest-plugin usage guide, and on the environment-variable page.Why the error needed to move
The path that overruns is almost never one the caller typed. It is a deep pytest
tmp_path, an XDG runtime dir, a nested worktree, or a CI checkout under a long workspace prefix. So the number the user needs — how far over, and over what — is exactly what tmux's message does not give them, and the object that is wrong is the one they still have a stack frame for.Before / After
The case that actually bites is the one where the caller passed something short:
Design decisions
A documented per-platform constant, not a runtime probe.
sun_pathis a fixed-size char array whose size is part of each platform's frozen ABI, and the stdlib publishes no constant for it. So_internal/env.pyspells it out — 104 on the BSD-derived kernels, 108 elsewhere, less one for the NUL — with a comment saying why. To stop that number drifting silently, the test suite probes the running kernel:socket.socket(AF_UNIX).connect()makes CPython measuresun_pathand raise before any syscall, so the probe touches no filesystem and creates no socket file. Fixed number in the library, empirical check in the tests.Bytes, not characters.
os.fsencode, as the kernel counts. A non-ASCII path runs out sooner than its character count suggests.Measured at construction, not on first use. The object is what is wrong. Deferring to first use is what produced the misattributed error this PR removes.
The name travels with the exception.
socket_nameis recorded when the path was resolved rather than given, so the message can say the length was inherited from$TMUX_TMPDIRrather than implying the caller chose it.if/elifbecameif/elsein__init__sosocket_name,socket_name_factory(), and the bare-Server()default share one check instead of three. Precedence is unchanged:socket_namestill wins oversocket_name_factory, andsocket_pathstill leavessocket_nameunset.Behaviour change
A bare
Server()or aServer(socket_name=...)now raises under a$TMUX_TMPDIRlong enough to make the socket unusable, where it previously constructed and failed later. Paths at or under the limit are unaffected.Server.from_env()builds from a$TMUXwritten by a live tmux, so its path is necessarily within the limit.Test plan
test_socket_path_too_long_raises— explicit long path; assertslength,limit,socket_name is None, and the messagetest_socket_name_too_long_via_tmux_tmpdir— long$TMUX_TMPDIRwith a shortsocket_name; the case the issue says actually bitestest_default_socket_too_long_via_tmux_tmpdir— bareServer()under the sametest_socket_path_at_limit_is_accepted— boundary, guarding against an off-by-one that would reject working socketstest_socket_path_max_bytes_matches_platform— probes the running kernel so the per-platform constant cannot driftServer,exc.SocketPathTooLong, andcheck_socket_path_length, all collected and runlibtmux.pytest_plugin, which builds sockets under temp directoriesuv run ruff check ./uv run ruff format ./uv run mypycleanjust build-docsclean, with the new{ref},{exc}and{func}targets resolvingNote for the merger
The
CHANGESheading cites the issue number; swap it for this PR's number on merge. It lands under### What's newrather than### Fixesbecause it ships a new public exception and new construction-time behaviour. It will conflict with the sibling PRs for #723, #724 and #726 — keep both sides.