Skip to content

Fix dgram UDP socket creation bugs on macOS#28084

Merged
alii merged 5 commits into
mainfrom
farm/4cd4470b/fix-dgram-implicit-bind
Mar 14, 2026
Merged

Fix dgram UDP socket creation bugs on macOS#28084
alii merged 5 commits into
mainfrom
farm/4cd4470b/fix-dgram-implicit-bind

Conversation

@robobun

@robobun robobun commented Mar 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes three bugs in bsd_create_udp_socket() in uSockets that affect macOS dgram socket behavior:

  • bsd_set_reuseport() was Linux-only: Changed #if defined(__linux__) to #if defined(SO_REUSEPORT) && !defined(_WIN32) so reusePort: true works on macOS
  • Socket fd leak + missing errno on bsd_set_reuse() failure: Added bsd_close_socket() and errno propagation so failures produce diagnostic error messages instead of generic "Failed to bind socket"
  • Hardcoded errno 92 (Linux ENOPROTOOPT): Replaced with ENOPROTOOPT macro so IPv4 fallbacks for IP_PKTINFO and IP_RECVTOS are correctly attempted on macOS (where ENOPROTOOPT is 42, not 92)

Also adds regression tests for the implicit bind-on-send behavior reported in #28083, covering basic send-without-bind, listening event emission, multiple queued sends, short-form send, and bidirectional communication (k-rpc pattern).

Context

The reporter is on macOS (Darwin arm64, M4 Mac) and reports that calling send() on an unbound dgram socket silently fails to bind in Bun 1.3. This cannot be reproduced on Linux — the implicit bind code path works correctly there. The C-layer fixes in this PR address macOS-specific issues in the socket creation path that could cause silent failures or missing error diagnostics on that platform.

Closes #28083

Test plan

  • bun bd test test/regression/issue/28083.test.ts — 5 pass
  • Tests cover: implicit bind delivery, listening event, multiple queued sends, short-form send args, bidirectional k-rpc pattern
  • C-layer fixes verified by code review (errno, fd leak, SO_REUSEPORT guard)

Three bugs in `bsd_create_udp_socket()`:

1. `bsd_set_reuseport()` was restricted to Linux only (`#if defined(__linux__)`),
   causing `reusePort: true` in `node:dgram` to fail on macOS with a generic
   error even though macOS natively supports SO_REUSEPORT. Changed to
   `#if defined(SO_REUSEPORT) && !defined(_WIN32)`.

2. When `bsd_set_reuse()` failed, the socket fd was leaked (not closed) and
   errno was not propagated to the caller, resulting in a generic "Failed to
   bind socket" error with no diagnostic information.

3. Errno checks for IPv4 socket option fallbacks used hardcoded value 92
   (Linux ENOPROTOOPT) instead of the `ENOPROTOOPT` macro. On macOS where
   ENOPROTOOPT is 42, the fallback to IP_PKTINFO and IP_RECVTOS was never
   attempted on IPv4 sockets.

Also adds regression tests for dgram implicit bind-on-send behavior
(issue #28083) covering: basic send without bind, listening event,
multiple queued sends, short-form send, and bidirectional k-rpc pattern.

Closes #28083
@robobun

robobun commented Mar 14, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 10:04 PM PT - Mar 13th, 2026

@claude, your commit 86b3ac1 has some failures in Build #39529 (All Failures)


🧪   To try this PR locally:

bunx bun-pr 28084

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

bun-28084 --bun

@robobun

robobun commented Mar 14, 2026

Copy link
Copy Markdown
Collaborator Author

Verification complete — PR #28084 (head: 86b3ac1, push 4)

All gates pass. CI green (all test platforms pass; only upload-benchmark.mjs infra failure). All 5 bot review threads resolved. Review posted — ready for human review by @cirospaciari.

@coderabbitai

coderabbitai Bot commented Mar 14, 2026

Copy link
Copy Markdown
Contributor

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: 55795765-b419-4615-bf88-df7bf95f3c69

📥 Commits

Reviewing files that changed from the base of the PR and between 8948907 and 86b3ac1.

📒 Files selected for processing (1)
  • test/regression/issue/28083.test.ts

Walkthrough

Enabled SO_REUSEPORT when available (excluding Windows), propagate reuse-setting failures during UDP socket creation, improved IPv6→IPv4 socket-option fallback checks using ENOPROTOOPT/EINVAL, added a Win32-to-errno mapping for WSAEOPNOTSUPP, and added regression tests for implicit UDP bind/send behavior.

Changes

Cohort / File(s) Summary
Socket reuse & UDP creation
packages/bun-usockets/src/bsd.c
Changed SO_REUSEPORT guard to use availability (#ifdef SO_REUSEPORT) and exclude _WIN32. On reuse option failure, capture platform error (WSAGetLastError()/errno), close/free the socket and return LIBUS_SOCKET_ERROR. Adjusted IPv6 option fallback logic to check ENOPROTOOPT/EINVAL when falling back to IPv4 IP_PKTINFO/IP_RECVTOS.
UDP regression tests
test/regression/issue/28083.test.ts
Added end-to-end tests (spawned Bun processes) covering implicit UDP bind on send(), listening event emission after implicit bind, multiple sends without explicit bind(), short-form send variants, port reuse semantics, and a k-rpc style bidirectional pattern.
Windows errno mapping
src/errno/windows_errno.zig
Added mapping from Win32Error.WSAEOPNOTSUPP to SystemErrno.ENOTSUP.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix dgram UDP socket creation bugs on macOS' accurately and specifically summarizes the main changes in the PR.
Description check ✅ Passed The description comprehensively covers what the PR does and how it was verified, matching the template with detailed explanations of three bug fixes and test plan results.
Linked Issues check ✅ Passed The PR directly addresses all objectives from issue #28083: enables implicit UDP socket binding on macOS via SO_REUSEPORT guard changes, fixes fd leak and errno propagation, corrects errno 92 hardcoding, and adds comprehensive regression tests covering implicit bind-on-send behavior.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing dgram UDP socket creation bugs on macOS and adding related regression tests; the Windows errno mapping addition in windows_errno.zig is necessary support for error handling improvements.

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

📝 Coding Plan
  • Generate coding plan for human review comments

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/regression/issue/28083.test.ts`:
- Around line 48-79: Replace the hardcoded destination port 41234 with a
dynamically assigned port by creating a receiver socket bound to port 0 and
using its assigned port for the send; specifically, before calling
socket.send(...) create a receiver via dgram.createSocket("udp4"), call
receiver.bind(0), read the assigned port from receiver.address().port, then use
that port in the socket.send call and close the receiver when done (keep the
existing variables like socket, listeningFired, and the send callback intact).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0afd9f5e-af25-4868-8fb4-e84eed347b7a

📥 Commits

Reviewing files that changed from the base of the PR and between 636c638 and 0a8e960.

📒 Files selected for processing (2)
  • packages/bun-usockets/src/bsd.c
  • test/regression/issue/28083.test.ts

Comment thread test/regression/issue/28083.test.ts
Use a target socket bound to port 0 to get a random port, matching
project guidelines for no hardcoded ports.
Comment thread test/regression/issue/28083.test.ts
Comment thread packages/bun-usockets/src/bsd.c

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/regression/issue/28083.test.ts`:
- Around line 48-71: The socket.send callback currently ignores its err
parameter which can mask failures; inside the target.bind callback's
socket.send((err) => { ... }) handler check if err is truthy and if so write the
error or a failure marker to stdout/stderr and call process.exit(1) (or throw)
to fail the test immediately, otherwise continue with the existing
process.nextTick that writes String(listeningFired) and closes socket/target;
update the handler around socket.send to bail on err so the test doesn't produce
false-positive passes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 958152fd-833c-4f67-8e59-2904057743d5

📥 Commits

Reviewing files that changed from the base of the PR and between 0a8e960 and 924e6b7.

📒 Files selected for processing (1)
  • test/regression/issue/28083.test.ts

Comment thread test/regression/issue/28083.test.ts
Comment thread packages/bun-usockets/src/bsd.c
claude added 2 commits March 14, 2026 03:07
Without this mapping, SystemErrno.init(10045) returns null on Windows
when SO_REUSEPORT fails with WSAEOPNOTSUPP, causing an unwrap panic
in udp_socket.zig.
Tests that two UDP sockets with reusePort: true can bind to the same
port. This exercises the SO_REUSEPORT fix on macOS where the option
was previously gated behind #if defined(__linux__).

@robobun robobun left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Verification Report — 86b3ac1 (push 4)

Gate 1: CI ✅

Build #39529 — all test steps pass across all platforms (darwin-aarch64, darwin-x64, linux-x64, linux-aarch64, linux-musl, alpine, debian, ubuntu, windows-2019, windows-aarch64, ASAN). Only failure: upload-benchmark.mjs (infra benchmark upload script, not a test). 1 pending: windows-11-aarch64-test-bun.

Gate 2: Classification

Bug fix — test proof required.

Gate 3: Test proof ⚠️ (platform-limited)

  • New test: test/regression/issue/28083.test.ts (6 tests covering reusePort, implicit bind, listening event, multiple sends, short-form send, bidirectional k-rpc pattern)
  • Baked binary (main): All 6 tests pass on Linux — expected, since the bugs are macOS-specific (errno 92 vs 42 for ENOPROTOOPT, SO_REUSEPORT was already enabled on Linux)
  • Darwin CI: darwin-aarch64 build+test passed — this is the platform where the fix matters
  • Windows CI: windows-2019 and windows-aarch64 passed — confirms the WSAEOPNOTSUPP→ENOTSUP errno mapping fix works

Gate 4: Diff ✅

No TODOs/FIXMEs in added lines. Three C-layer fixes all match the described root causes:

  1. SO_REUSEPORT guard: #if defined(__linux__)#if defined(SO_REUSEPORT) && !defined(_WIN32)
  2. fd leak + errno propagation on bsd_set_reuse() failure ✓
  3. Hardcoded 92ENOPROTOOPT/EINVAL macros ✓
  4. WSAEOPNOTSUPPENOTSUP mapping in windows_errno.zig ✓

Gate 5: Bot convergence ✅

5 review threads (coderabbit + claude[bot]) — all resolved. Addressed: hardcoded port fix, describe.concurrent, IPV6_V6ONLY leak (pre-existing, out of scope), error callback handling, WSAEOPNOTSUPP mapping.

Gate 6: Hygiene ✅

Branch farm/4cd4470b/fix-dgram-implicit-bind — correct format. PR body is thorough with context, test plan, and issue link.

Verdict: Ready for human review. Suggesting @cirospaciari as reviewer (most active non-bot contributor to bun-usockets).

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM — the Windows crash bug I found was fixed, all reviewer comments are resolved, and the C-layer fixes are correct and well-scoped.

Extended reasoning...

Overview

This PR fixes three bugs in bsd_create_udp_socket() in packages/bun-usockets/src/bsd.c: (1) SO_REUSEPORT guard changed from Linux-only to availability-based, (2) fd leak + missing errno on bsd_set_reuse() failure path, (3) hardcoded errno 92 replaced with ENOPROTOOPT/EINVAL macros. A Windows errno mapping (WSAEOPNOTSUPP => ENOTSUP) was added in src/errno/windows_errno.zig to fix a crash I found in my previous review. A new test file with 5 regression tests covers the implicit bind-on-send behavior.

Security risks

No security risks. The changes are socket option configuration and error handling improvements. No auth, crypto, or permission-related code is touched.

Level of scrutiny

Medium. While this touches C socket infrastructure code, the changes are mechanical: a preprocessor guard fix, adding cleanup to an error path (matching the existing pattern used elsewhere in the function), replacing magic numbers with named constants, and adding a single line to an errno mapping switch. The crash bug I found in my previous review (WSAEOPNOTSUPP) was promptly fixed and the fix is a one-line addition to an existing mapping table.

Other factors

All reviewer comments (from me and CodeRabbit) have been addressed and resolved. The test file covers 5 scenarios for dgram implicit bind behavior. The PR has a claude label indicating it was generated by an automated system, but the fixes are straightforward and correct. The pre-existing IPV6_V6ONLY fd leak I noted is acknowledged as out of scope.

@alii alii merged commit f4dc498 into main Mar 14, 2026
64 of 66 checks passed
@alii alii deleted the farm/4cd4470b/fix-dgram-implicit-bind branch March 14, 2026 05:07
structwafel pushed a commit to structwafel/bun that referenced this pull request Apr 25, 2026
## Summary

Fixes three bugs in `bsd_create_udp_socket()` in uSockets that affect
macOS dgram socket behavior:

- **`bsd_set_reuseport()` was Linux-only**: Changed `#if
defined(__linux__)` to `#if defined(SO_REUSEPORT) && !defined(_WIN32)`
so `reusePort: true` works on macOS
- **Socket fd leak + missing errno on `bsd_set_reuse()` failure**: Added
`bsd_close_socket()` and errno propagation so failures produce
diagnostic error messages instead of generic "Failed to bind socket"
- **Hardcoded errno 92 (Linux ENOPROTOOPT)**: Replaced with
`ENOPROTOOPT` macro so IPv4 fallbacks for `IP_PKTINFO` and `IP_RECVTOS`
are correctly attempted on macOS (where ENOPROTOOPT is 42, not 92)

Also adds regression tests for the implicit bind-on-send behavior
reported in oven-sh#28083, covering basic send-without-bind, listening event
emission, multiple queued sends, short-form send, and bidirectional
communication (k-rpc pattern).

### Context

The reporter is on macOS (Darwin arm64, M4 Mac) and reports that calling
`send()` on an unbound dgram socket silently fails to bind in Bun 1.3.
This cannot be reproduced on Linux — the implicit bind code path works
correctly there. The C-layer fixes in this PR address macOS-specific
issues in the socket creation path that could cause silent failures or
missing error diagnostics on that platform.

Closes oven-sh#28083

## Test plan

- [x] `bun bd test test/regression/issue/28083.test.ts` — 5 pass
- [x] Tests cover: implicit bind delivery, listening event, multiple
queued sends, short-form send args, bidirectional k-rpc pattern
- [x] C-layer fixes verified by code review (errno, fd leak,
SO_REUSEPORT guard)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
xhjkl pushed a commit to xhjkl/bun that referenced this pull request May 14, 2026
## Summary

Fixes three bugs in `bsd_create_udp_socket()` in uSockets that affect
macOS dgram socket behavior:

- **`bsd_set_reuseport()` was Linux-only**: Changed `#if
defined(__linux__)` to `#if defined(SO_REUSEPORT) && !defined(_WIN32)`
so `reusePort: true` works on macOS
- **Socket fd leak + missing errno on `bsd_set_reuse()` failure**: Added
`bsd_close_socket()` and errno propagation so failures produce
diagnostic error messages instead of generic "Failed to bind socket"
- **Hardcoded errno 92 (Linux ENOPROTOOPT)**: Replaced with
`ENOPROTOOPT` macro so IPv4 fallbacks for `IP_PKTINFO` and `IP_RECVTOS`
are correctly attempted on macOS (where ENOPROTOOPT is 42, not 92)

Also adds regression tests for the implicit bind-on-send behavior
reported in oven-sh#28083, covering basic send-without-bind, listening event
emission, multiple queued sends, short-form send, and bidirectional
communication (k-rpc pattern).

### Context

The reporter is on macOS (Darwin arm64, M4 Mac) and reports that calling
`send()` on an unbound dgram socket silently fails to bind in Bun 1.3.
This cannot be reproduced on Linux — the implicit bind code path works
correctly there. The C-layer fixes in this PR address macOS-specific
issues in the socket creation path that could cause silent failures or
missing error diagnostics on that platform.

Closes oven-sh#28083

## Test plan

- [x] `bun bd test test/regression/issue/28083.test.ts` — 5 pass
- [x] Tests cover: implicit bind delivery, listening event, multiple
queued sends, short-form send args, bidirectional k-rpc pattern
- [x] C-layer fixes verified by code review (errno, fd leak,
SO_REUSEPORT guard)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dgram: Implicit UDP socket binding broken in Bun 1.3 (Node.js compat regression)

3 participants