usockets: bound basename copy in Linux long-unix-path workaround - #34078
Conversation
bsd_create_unix_socket_address() takes a ptr+len path and, for paths
>= sizeof(sun_path) on Linux, opens the parent directory and rewrites
the address as /proc/self/fd/<dirfd>/<basename>. The basename was
copied via snprintf("%s", path + dirname_len), which reads until a
NUL byte, but the Rust callers pass a bare byte slice with no
terminator. Any pathname unix socket with 108 <= len < dirname_len+107
read past the allocation (ASan heap-buffer-overflow abort) and, on
release builds, assembled sun_path from whatever bytes followed on the
heap. This is reachable from net.createServer().listen(path),
net.connect(path), Bun.listen({unix}) and Bun.connect({unix}).
Switch to %.*s with the known basename length so exactly
path_len - dirname_len bytes are copied.
|
Updated 2:12 PM PT - Jul 13th, 2026
❌ @autofix-ci[bot], your commit 7578548 has 1 failures in
🧪 To try this PR locally: bunx bun-pr 34078That installs a local version of the PR into your bun-34078 --bun |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 37 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughChangesThe Linux Unix socket address workaround now bounds basename reads by the supplied path length. A Linux-only regression suite tests exact 108-byte and 150-byte paths using Node and Bun echo fixtures, checking output, exit status, and ASan diagnostics. Unix socket long-path handling
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test/js/bun/net/unix-socket-long-path.test.ts`:
- Around line 1-109: Move the long Unix socket path tests from the standalone
suite into the existing net/socket test suite. Preserve the
`describe.skipIf(!isLinux)` coverage, helper functions, test cases, and
assertions unchanged while integrating them into `socket.test.ts` and removing
the new test file.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 8659747b-74a7-4213-b94c-1b53dc8aa419
📒 Files selected for processing (2)
packages/bun-usockets/src/bsd.ctest/js/bun/net/unix-socket-long-path.test.ts
There was a problem hiding this comment.
LGTM — one-line %s → %.*s bounding fix for a real ASan heap-buffer-overflow in the Linux long-unix-path workaround.
Checked that (int)(path_len - dirname_len) is safe: the earlier (path_len - dirname_len + 1) >= sizeof(sun_path) guard already caps it below 108, and dirname_len <= path_len so it can't underflow. Verified the sibling macOS branch and the short-path fallback both already use bounded memcpy, so this was the only unbounded read of path. Test constructs exact-length paths, covers both the 108-byte boundary and a longer path, exercises both net.* and Bun.* entry points, and drains subprocess pipes concurrently.
Extended reasoning...
Overview
One-line change in packages/bun-usockets/src/bsd.c inside bsd_create_unix_socket_address()'s Linux-only /proc/self/fd/<dirfd>/<basename> workaround: the snprintf format changes from %s to %.*s with an explicit precision of (int)(path_len - dirname_len). The path argument is a ptr+len pair from Rust and is not NUL-terminated, so the old %s read past the allocation. A new Linux-only test file spawns subprocesses that bind/connect over 108- and 150-byte unix socket paths via both node:net and Bun.listen/Bun.connect, asserting a clean echo round-trip and no ASan header in stderr.
Security risks
The fix removes an out-of-bounds heap read on a user-controlled path buffer. The precision value cannot overflow: dirname_len is derived by walking back from path_len (so path_len - dirname_len >= 0), and the preceding (path_len - dirname_len + 1) >= sizeof(sun_path) early-return already bounds it below 108, well within int. No new attack surface is introduced.
Level of scrutiny
Low-to-medium. The native change is a single format-string precision addition — a textbook fix for passing non-terminated buffers to printf-family functions. I audited the other two branches in the same function that copy from path (the macOS __pthread_fchdir workaround and the short-path fallback) and both already use length-bounded memcpy, so there are no sibling sites with the same bug. The snprintf return-value check on the following line remains valid with %.*s.
Other factors
The test follows harness conventions (tempDir, bunEnv, bunExe, concurrent pipe draining, using for cleanup, test.concurrent) and asserts on a combined {stdout, asan, exitCode} object so failures show all three. The standalone-file placement was raised by CodeRabbit and reasonably justified by the author against the unix-socket-unlink.test.ts precedent (the thread is resolved). The ASAN_OPTIONS symbolize=0 tweak with its comment is a sensible guard against symbolizer-induced timeouts on the pre-fix failure path. No CODEOWNERS cover these paths.
|
CI on build #72492 finished: 285/286 jobs passed. The new test The one hard-failed job is This change is a one-line |
What does this PR do?
bsd_create_unix_socket_address()takes the caller's path as(const char *path, size_t path_len)and, on Linux, works aroundsun_path's 108-byte limit by opening the parent directory and binding to/proc/self/fd/<dirfd>/<basename>instead. The basename was being copied withbut
pathis a ptr+len pair coming from a Rust&[u8]with no NUL terminator.%swalks past the end of the allocation. On ASan builds this aborts withheap-buffer-overflow; on release buildssun_pathis assembled from whatever heap bytes follow the path buffer, so the kernel sees an address built from out-of-bounds memory (sometimes the right one, sometimesEINVAL, sometimes something else).The trigger window is any pathname unix socket with
108 <= path_lenwhose basename still fits inside/proc/self/fd/N/, reachable fromnet.createServer().listen(path),net.connect(path),Bun.listen({unix})andBun.connect({unix}). Node binds a full 108-bytesun_pathhere, so this is also a parity break at exactly length 108.Fix: use
%.*swith(int)(path_len - dirname_len)so the copy is bounded by the known basename length.Repro
Before (debug/ASan):
After:
LISTENING, exit 0.How did you verify your code works?
bun bd test test/js/bun/net/unix-socket-long-path.test.tspasses (4/4). With thepackages/change stashed out, all four cases fail with the ASanheap-buffer-overflowheader in the subprocess stderr.no test proof · iteration 0 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/js/bun/net/unix-socket-long-path.test.ts