Skip to content

fix(runtime-services): keep native MariaDB socket path within sun_path - #2587

Merged
chubes4 merged 1 commit into
mainfrom
fix/native-mariadb-socket-path
Nov 12, 2026
Merged

chubes4 merged 1 commit into
mainfrom
fix/native-mariadb-socket-path

Conversation

@chubes4

@chubes4 chubes4 commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator

Fixes #2586

Problem

The native MariaDB server socket is created at <mkdtemp root under realpath(tmpdir())>/storage/runtime/server.sock, which is the TMPDIR length plus 54 bytes. Homeboy's per-run TMPDIR is 120 bytes, so the socket path is far past sun_path (108 bytes on Linux, 104 on macOS). mariadbd exits before readiness, and the probe reported only bounded-filesystem-unavailable.

Fix

The root's base directory is now chosen by the length of the socket path it would produce. I moved the whole owned root instead of only the socket dir. Splitting the socket dir out would mean a second private root with its own lifecycle and cleanup, and it would also take the socket out of the bounded FUSE image the docs describe.

  • selectNativeMariaDbRootBase() takes candidates in order: the canonical os.tmpdir() first, then the canonical /tmp. It returns the first one whose projected socket path is at most NATIVE_MARIADB_SOCKET_PATH_LIMIT_BYTES (100 bytes, below both platform limits). Length is measured on the realpath, so a short symlink alias to a long directory can't get through.
  • If no candidate fits, it throws NativeMariaDbSocketPathError (reason: "socket-path-too-long") before any root is allocated.
  • The security checks are the same for either base. Only the parent passed to mkdtemp() changes. chmod 0700, the realpath === resolve canonical check, the lstat directory/non-symlink proof, device/inode capture, assertOwnedNativeRoot, ownedNativePath containment and verified removal all run unchanged. Using /tmp is no weaker than using TMPDIR: mkdtemp creates a random-suffixed directory that only the caller owns, and the existing proofs are what ensure ownership.
  • The FUSE datadir image (<root>/datadir.ext4) and its mount (<root>/storage) move together with the root. The mount source, mountpoint and mountinfo checks are unchanged.
  • As defense in depth, the computed socket path is checked again with assertNativeMariaDbSocketPath() before mariadbd is started.
  • Diagnostics: on failure, evidence now carries diagnostic.cause = { code: "socket-path-too-long", message }, and nativeMariaDbHostReadiness() returns reason: "socket-path-too-long". That covers both the direct-failure path and the retained-cleanup path. The messages give byte counts only, because evidence must not contain private absolute paths.
  • Tests can inject candidate bases through a new optional RuntimeServiceDependencies.nativeRootBases (default [os.tmpdir(), "/tmp"]).
  • docs/native-mariadb-runtime-service.md is updated.

Tests

New unit tests in tests/native-mariadb-runtime-service.test.ts (fake binaries):

  • Path selection: the byte projection; a short TMPDIR is used unchanged; a long TMPDIR falls back to the short base; a short symlink alias to a long dir is rejected on its canonical length; missing candidates are skipped; all-too-long raises NativeMariaDbSocketPathError with byte counts and no paths.
  • Provision with [long, short] bases: it is ready; every --socket= path is under the short base and at most 100 bytes; the fallback root is a canonical, non-symlink 0700 dir; nothing is allocated under the long base; the root is removed on release.
  • Provision with [long] only: it fails as provision-failed with cause.code: "socket-path-too-long"; no paths appear in the evidence; no root is allocated.
  • Readiness: [long] gives { status: "unavailable", reason: "socket-path-too-long" }, and [long, short] gives ready.

Before the fix, the new tests fail against the old source.

tests/native-mariadb-runtime-service.integration.test.ts now accepts socket-path-too-long as a stable fail-closed reason.

Results:

  • npm run test:runtime-services: passes.
  • npm run check (build, types, fast contracts; 329 commands): passes.

End-to-end (real MariaDB 10.11.14 + fuse2fs, uid 1000)

Run after npm run build, with TMPDIR=/var/lib/datamachine/workspace/.homeboy/runtime/tmp/homeboy-run-df70bf63-6e8d-4494-a0e6-27a2706a628d-1794495483671992580 (120 bytes):

  • Before the fix: readiness was bounded-filesystem-unavailable, and provisioning failed with Native MariaDB exited before readiness.
  • After the fix: readiness is ready, and provisionRuntimeServices(...) returns DB_HOST=127.0.0.1/DB_PORT with readiness: "ready" and RSS 58 MiB. Release completes, and no wp-codebox-mariadb-* roots or FUSE mounts are left behind.
  • Full real integration test (two concurrent services, InnoDB transactions, advisory locks, hostile-engine rejection, recipe run over mysqli) passes under both the long TMPDIR and the default one.
    • Under the long TMPDIR it was run with node --import tsx because the tsx CLI's own IPC pipe hits the same sun_path limit.

AI assistance

This PR was written by an AI agent (Claude) via Extra Chill Bot. That covers the investigation, the fix, the tests and this description. I reproduced the bug and ran the end-to-end verification above on a real host.

A long TMPDIR (e.g. Homeboy's per-run directory) pushed
<root>/storage/runtime/server.sock past the 108-byte Unix-domain socket
limit, so mariadbd exited before readiness and discovery reported the
generic bounded-filesystem-unavailable.

Select the owned-root base by projected socket length: prefer the
canonical os.tmpdir(), fall back to the canonical /tmp, and otherwise
fail before allocation with NativeMariaDbSocketPathError. The mkdtemp
0700 root and the realpath/lstat ownership proofs are unchanged for
whichever base is chosen. The final socket path is asserted again, and
the cause is surfaced as diagnostic.cause.code and readiness reason
"socket-path-too-long" without leaking private paths.

Fixes #2586
@chubes4
chubes4 merged commit a145fd3 into main Nov 12, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Native MariaDB runtime service fails under long TMPDIR (socket path exceeds sun_path)

1 participant