Skip to content

fix(usockets): snapshot loop before context unref in us_connecting_socket_free#29064

Open
cirospaciari wants to merge 1 commit intomainfrom
claude/usockets-connecting-free-snapshot-loop
Open

fix(usockets): snapshot loop before context unref in us_connecting_socket_free#29064
cirospaciari wants to merge 1 commit intomainfrom
claude/usockets-connecting-free-snapshot-loop

Conversation

@cirospaciari
Copy link
Copy Markdown
Member

us_internal_socket_context_unlink_connecting_socket() calls us_socket_context_unref(), which can drop the last reference and queue the context for free. The two lines that follow then dereference c->context->loop. Snapshot the loop pointer before unlink runs.

Matches the existing snapshot idiom in start_connections (context.c:596-597).

Part of defensive hardening for the v1.3.11 crash signature (segfault at 0x0 inside us_loop_run_bun_tick at loop.c:238, the inlined us_internal_socket_after_resolve / DNS drain path). No deterministic repro; ASAN stress test (WebSocket + cancel + spawnSync, 50 iters) runs clean on the debug build. Existing socket / node:net / DNS / proxy tests pass (proxy redirect timeouts are pre-existing on main).

Companion PR: ref/unref the context across us_internal_socket_after_resolve.

…cket_free

us_internal_socket_context_unlink_connecting_socket() calls
us_socket_context_unref(), which can drop the last reference to the
context. The next two lines then dereference c->context to reach the
loop. Snapshot the loop pointer before unlink, matching the existing
idiom in start_connections (context.c:596-597).

Part of hardening for the v1.3.11 loop.c:238 crash signature
(null deref in the DNS drain path). No behavioral change on the
happy path.
@robobun
Copy link
Copy Markdown
Collaborator

robobun commented Apr 9, 2026

Updated 9:45 PM PT - Apr 8th, 2026

@cirospaciari, your commit 52b45bd has some failures in Build #44665 (All Failures)


🧪   To try this PR locally:

bunx bun-pr 29064

That installs a local version of the PR into your bun-29064 executable, so you can run:

bun-29064 --bun

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b84c4467-0ec9-4e27-94bb-0d6666d8c03a

📥 Commits

Reviewing files that changed from the base of the PR and between 94d1253 and 52b45bd.

📒 Files selected for processing (1)
  • packages/bun-usockets/src/socket.c

Walkthrough

Modified us_connecting_socket_free function to cache c->context->loop into a local variable. The function now uses this cached loop variable for list-head operations instead of repeatedly accessing the context pointer. No functional changes to the list-head update logic.

Changes

Cohort / File(s) Summary
Socket Connection Cleanup
packages/bun-usockets/src/socket.c
Caches c->context->loop into local variable in us_connecting_socket_free function to avoid re-reading through context pointer during list-head update.
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: snapshot of loop pointer before context unref in us_connecting_socket_free function.
Description check ✅ Passed The PR description provides detailed context about the fix, rationale, defensive hardening motivation, testing, and references to existing patterns, but the description template requires 'What does this PR do?' and 'How did you verify your code works?' sections which are not explicitly structured.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — snapshotting the loop pointer before the unref is the correct fix, matching the existing idiom in context.c.

Extended reasoning...

Overview

Single-line change in packages/bun-usockets/src/socket.c: snapshots c->context->loop into a local loop variable before calling us_internal_socket_context_unlink_connecting_socket(), then uses the snapshot for the two subsequent pointer writes.

Security Risks

None. This is a defensive fix against a use-after-free/dangling-pointer dereference; it does not change any security boundary or authentication path.

Level of Scrutiny

Low. The fix is mechanical and confined to four lines. The root cause is clear: us_internal_socket_context_unlink_connecting_socket() unconditionally calls us_socket_context_unref() (context.c:152), which can drop the last reference and queue the context for free. The two subsequent accesses via c->context->loop would then dereference a potentially-freed pointer. Snapshotting the loop pointer before the unlink call is the correct and minimal remedy, and it exactly mirrors the snapshot idiom already used in context.c:596-597.

Other Factors

No bugs were identified by the automated review. The change is single-file, has no side-effects on callers, and the description provides adequate rationale and testing notes (ASAN stress test, existing test suite passes).

dylan-conway pushed a commit that referenced this pull request Apr 9, 2026
…_callback (#29067)

## What

Take an explicit \`us_socket_context_ref\` in
\`us_socket_context_connect\` immediately after setting
\`c->pending_resolve_callback = 1\` (before \`Bun__addrinfo_set\`
exposes \`c\` to the DNS worker), and drop it at every exit of
\`us_internal_socket_after_resolve\`. Snapshot \`context\`/\`ssl\` into
locals at the top of \`after_resolve\` so the unref uses a stable
pointer even after \`us_connecting_socket_free\`/\`close\` has unlinked
\`c\`.

## Why

v1.3.11 crash signature, linux x86_64_baseline:

\`\`\`
Segmentation fault at address 0x00000000
- loop.c:238: us_loop_run_bun_tick
\`\`\`

\`loop\` is the first field of \`us_socket_context_t\`, so address 0x0
means \`c->context\` was NULL when the inlined \`after_resolve\` ran
\`c->context->loop->num_polls--\`. The existing invariant relies on the
link-ref taken by \`us_internal_socket_context_link_connecting_socket\`;
this PR makes the ref independent of link/unlink so the context is
guaranteed live for the entire pending-resolve window regardless of
which list \`c\` is on.

Features in the report: spawn, fetch, http_client_proxy, WebSocket,
abort_signal. \`Bun.spawnSync\` (whose tick changed in v1.3.11 from
\`tickWithoutJS\` to \`tickTasksOnly\`) lets \`dns_ready_head\`
accumulate while the JS thread is blocked, widening the window.

## Testing

No deterministic repro on macOS — the production crash is on linux with
the work-pool getaddrinfo path; an ASAN stress test (concurrent multi-IP
WebSocket + spawnSync + abort, 400 iters) runs clean on the debug build.
Existing suites pass on the debug build: socket.test.ts (29/29),
node-net.test.ts (31/31+1 skip), resolve-dns.test.ts (71/71).

## Related

- #29064 snapshots \`loop\` before unref in
\`us_connecting_socket_free\` (independent ordering fix)
- #29065 takes the ref inside \`after_resolve\` instead — this PR
supersedes it by acquiring the ref before the DNS thread can observe
\`c\`
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.

2 participants