Commit 93247e9

Chris Rose  ·  2026-07-04 16:41:39 -0400 EDT
parent 444fa72
fix: avoid statx syscall so zmx runs on pre-4.11 kernels (#186)

zmx crashed with `error: Unexpected` on every invocation on a Synology
DiskStation (Linux 4.4.302). `error.Unexpected` is what Zig's std returns
for an unrecognized errno; here the errno is ENOSYS (38) from the `statx`
syscall, which only exists on Linux >= 4.11.

Two code paths issued statx via Zig std, which calls the raw statx syscall
directly and does not fall back like libc does:

  - log.zig: LogSystem.init() called file.getEndPos() -> File.stat() -> statx.
    This runs before command parsing, so it broke *every* subcommand, not
    just attach.
  - socket.zig: sessionExists() called Dir.statFile() -> statx.

Both now use fd/at-based stat instead:

  - LogSystem.init() uses posix.fstat(file.handle).
  - sessionExists() uses posix.fstatatZ() + S.ISSOCK().

Because zmx links musl, these resolve to SYS_fstat / SYS_fstatat (musl on
x86_64 skips statx entirely), which are available on 4.4. Verified by
disassembling the release binary: no reachable statx call remains on the
attach path (the only residual references are the DWARF stack-trace
unwinder and sendFile fast-paths, neither of which zmx exercises).
2 files changed,  +9, -3
+4, -1
 1@@ -1,4 +1,5 @@
 2 const std = @import("std");
 3+const posix = std.posix;
 4 
 5 pub const LogSystem = struct {
 6     file: ?std.fs.File = null,
 7@@ -22,7 +23,9 @@ pub const LogSystem = struct {
 8             else => return err,
 9         };
10 
11-        const end_pos = try file.getEndPos();
12+        // fstat (not getEndPos) to avoid the statx syscall; see #186.
13+        const st = try posix.fstat(file.handle);
14+        const end_pos: u64 = @intCast(st.size);
15         try file.seekTo(end_pos);
16         self.current_size = end_pos;
17         self.file = file;
+5, -2
 1@@ -44,11 +44,14 @@ pub fn cleanupStaleSocket(dir: std.fs.Dir, session_name: []const u8) void {
 2 }
 3 
 4 pub fn sessionExists(dir: std.fs.Dir, name: []const u8) !bool {
 5-    const stat = dir.statFile(name) catch |err| switch (err) {
 6+    // fstatatZ (not statFile) to avoid the statx syscall
 7+    // https://github.com/neurosnap/zmx/issues/186
 8+    const name_c = try posix.toPosixPath(name);
 9+    const stat = posix.fstatatZ(dir.fd, &name_c, posix.AT.SYMLINK_NOFOLLOW) catch |err| switch (err) {
10         error.FileNotFound => return false,
11         else => return err,
12     };
13-    if (stat.kind != .unix_domain_socket) {
14+    if (!posix.S.ISSOCK(stat.mode)) {
15         return error.FileNotUnixSocket;
16     }
17     return true;