Skip to content

Add a leak test for SocketAddress validation-error paths - #40296

Open
robobun wants to merge 3 commits into
mainfrom
farm/fa3c242e/close-superseded-string-leak-prs
Open

robobun wants to merge 3 commits into
mainfrom
farm/fa3c242e/close-superseded-string-leak-prs

Conversation

@robobun

@robobun robobun commented Aug 24, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Fix

Background

  • bun_core::String is the Rust mirror of the C++ BunString. When it is WTF-backed it holds one ref on a WTF::StringImpl. Before Make bun_core::String own its WTF ref #40238 it was a Copy POD with no destructor, so every +1 producer relied on the caller to call deref() by hand. Early returns through ? skipped that call.
  • expectRssDeltaBelow spawns bun with the ASAN quarantine disabled and reads a deltaMiB JSON line from stdout. The quarantine pins freed blocks, which would hide a fixed leak behind a large RSS.
  • Add leak tests for Bun.serve unix option and Transpiler reject path #40297 is the sibling test-only PR for two other sites from the same Fixes list. The two PRs touch different files.
Notes

no test proof · iteration 1 · no src or test change; test-proof not applicable

#40238 made bun_core::String own its WTF ref and release it on Drop. That
closed three leaks that had open fix PRs with tests but no test on main:

- SocketAddress: options.address on validation-error paths (#35230)
- worker_threads: the cached Bun.main path at VM teardown (#38178)
- bun:test: the bound test/describe function name (#38208)

This adds only the tests from those branches. The LSan tests run on the
ASAN lanes. The SocketAddress test measures RSS growth.
@coderabbitai

coderabbitai Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

On-demand reviews are free for the next 27 days. After that, they cost $0.25 per reviewed file.

Or wait 13 minutes for your next included review.

View limit details

Limit details: You’ve used all 5 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c8165340-81dc-47e1-9d03-cea34d771458

📥 Commits

Reviewing files that changed from the base of the PR and between f2fe7d3 and fd4a62c.

📒 Files selected for processing (1)
  • test/js/node/net/socketaddress.spec.ts

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

@robobun

robobun commented Aug 24, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 7:42 AM PT - Aug 24th, 2026

✅ @robobun, your commit fd4a62cf7fd9d432cc99db164ad6f0bbc204836c passed in Build #104919! 🎉


🧪   To try this PR locally:

bunx bun-pr 40296

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

bun-40296 --bun

@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 — test-only PR adding three leak regression tests salvaged from superseded fix PRs.

What was reviewed:

  • leaksan.supp path resolution from both test dirs (../../../leaksan.supp → test/leaksan.supp), and that the worker test correctly composes it with the extra WebCore::eventNames suppression.
  • The pass/skip/todo/fail counts in the bun-test.test.ts fixture — hand-counted 26/8/3/0 across all modifier variants; matches the assertion.
  • SocketAddress RSS test: warmup phase, per-path measurement, non-vacuous loop guard (Object.keys(growth).length === 6), 20 MB threshold vs. ~64 MB expected on regression, ASAN quarantine disabled so RSS reflects live allocations.
  • Harness conventions: bunEnv spread, Buffer.alloc().toString(), using/await using, concurrent pipe drain, combined-object assertions, skipIf(!isASAN || isWindows) gate, per-test timeouts with justifying comments.
Extended reasoning...

Overview

This PR adds three regression tests to existing test files, pinning string-leak fixes that landed as a side effect of #40238 (bun_core::String gaining a Drop impl). The tests were lifted from three now-closed fix PRs (#35230, #38178, #38208). No source code is touched.

  • test/js/node/net/socketaddress.spec.ts: RSS-growth test over six SocketAddress/BlockList validation-error paths that populate an address string before throwing. Runs on all platforms in a spawned --smol subprocess.
  • test/js/node/worker_threads/worker-shutdown-post-leak.test.ts: LSan check (ASAN lanes only) that a worker's cached Bun.main path is released at VM teardown, for both ASCII and non-ASCII entry paths.
  • test/js/bun/test/bun-test.test.ts: LSan check (ASAN lanes only) that ScopeFunctions::bind no longer leaks the bound function name, exercising every test/describe modifier.

Security risks

None. Test-only additions that spawn the debug binary against local temp dirs; no network, no external inputs, no auth/crypto surface.

Level of scrutiny

Low-to-moderate. Test-only with no runtime behavior change — the risk is limited to CI flakiness or a vacuous test. I checked the specific things reviewers catch on leak tests per REVIEW.md:

  • Fails for the right reason: PR description documents USE_SYSTEM_BUN=1 failure against a pre-#40238 canary (67 MB growth) and pass on current main. The two LSan tests were shown to fail on the unfixed tree in their original PRs.
  • Non-vacuous assertions: the SocketAddress test asserts Object.keys(growth).length === 6 so an empty result object can't pass; the bun-test fixture asserts exact result counts (I hand-verified 26 pass / 8 skip / 3 todo / 0 fail against the fixture body); the worker test asserts exact {stdout, stderr, exitCode}.
  • Threshold well below the unfixed leak: 20 MB vs ~64 MB, with ASAN quarantine disabled and a warmup phase to stabilize RSS.
  • Harness conventions: {...bunEnv, ...}, Buffer.alloc(128*1024, "a").toString(), using/await using, Promise.all on stdout/stderr/exited, tempDir from harness, test.concurrent for the independent worker subprocess pair, per-test timeouts with comments explaining LSan symbolization cost.

Other factors

  • The ../../../leaksan.supp relative paths resolve correctly to test/leaksan.supp from both test/js/bun/test/ and test/js/node/worker_threads/.
  • The Malloc=1 + detect_leaks=1 + suppressions-file pattern matches the existing test in the same file and several others across test/.
  • The exact-count assertion in bun-test.test.ts is somewhat coupled to the reporter output format, but that's an intentional pin — a format change would break it visibly, not silently.
  • No prior review comments to address; CI is building.

The worker test was vacuous on current main: #39536 removed the
bootstrap read of Bun.main, so a worker with an empty body never
populates main_resolved_path. The bun:test modifier test measured a
path that creates no WTF string since bind() takes a &'static str.

The SocketAddress test now goes through the shared RSS helper and
branches its bound on debug/ASAN builds. Fewer iterations with a
larger string keep the leaked signal at about 50 MiB per path while
the test runs in about 2.5 s under a debug ASAN build.
@robobun robobun changed the title Add regression tests for three string leaks fixed by #40238 Add a leak test for SocketAddress validation-error paths Aug 24, 2026

@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 — test-only addition using the existing expectRssDeltaBelow harness helper. The one inline note is just PR-metadata hygiene.

Extended reasoning...

Overview

This PR adds a single leak-regression test to test/js/node/net/socketaddress.spec.ts. It spawns bun --smol -e <code> via the existing expectRssDeltaBelow harness helper and measures per-case RSS growth across six error paths where options.address is read into a native string before a later validator throws (four in SocketAddress's Options::from_js, one in init_js via pton reject, one in BlockList.addAddress). The child prints per-case deltas plus a final {deltaMiB: max} line, which the helper parses and asserts against {release: 20, debug: 30}.

Security risks

None. Test-only change; no source, no new dependencies, no network access (spawns the local bunExe() with bunEnv).

Level of scrutiny

Low-to-medium. It's a test addition to an existing spec file. I checked that: the expectRssDeltaBelow signature matches the call (cmd: string[], {release, debug}); the child's last stdout line is the {deltaMiB} object the helper's JSON.parse(...at(-1)) expects; the helper already branches on isASAN || isDebug and disables ASAN quarantine so the 30 MiB debug bound is realistic; the test uses Buffer.alloc(n, fill).toString() per repo convention; there's a 20-iteration warmup and Bun.gc(true) before each measurement window; and the 20/30 MiB thresholds sit well below the ~50 MiB an unfixed leak would produce per path.

Other factors

The PR originally added three tests but commit 3cd36e6 dropped two as vacuous — hence the inline nit about the stale title/description. That's a squash-commit-message concern only and doesn't affect the code being merged. No prior human reviewer comments to address.

Comment thread test/js/node/net/socketaddress.spec.ts

@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 — thanks for retitling and syncing the description; the earlier concern is resolved.

What was reviewed:

  • Traced each of the six cases against Options::from_js / init_js / BlockList.addAddress in src/runtime/socket/SocketAddress.rs — every path reads options.address into a BunString before the named validator throws.
  • expectRssDeltaBelow contract: last stdout line is the {deltaMiB} JSON, ASAN quarantine is disabled by the helper, and the 20/30 MiB bounds sit well below the ~50 MiB unfixed leak and above the reported 7 MiB fixed value.
  • Confirmed the helper is already used by ~9 other leak tests, so this follows the established pattern; Buffer.alloc(n, fill).toString() and big + i per-iteration match harness conventions.
Extended reasoning...

Overview

Test-only PR adding one leak-regression test to test/js/node/net/socketaddress.spec.ts. It spawns a child bun via the shared expectRssDeltaBelow harness helper and drives six SocketAddress/BlockList validation-error paths with a 512 KiB address string, asserting the max per-path RSS delta stays under 20 MiB (release) / 30 MiB (debug/ASAN). No source changes.

Security risks

None. The change is a test that spawns bun -e <code> with bunEnv and reads stdout; no network, no external data, no new API surface.

Level of scrutiny

Low-to-moderate. It is test-only and reuses an existing harness helper already in use by ~9 other leak tests. The main risks for leak tests are flakiness and vacuity, both of which the PR body addresses with concrete numbers: 7 MiB observed on a fixed debug+ASAN build vs 49–57 MiB on pre-#40238 builds, giving wide margin on both sides of the 20/30 MiB thresholds. The helper disables ASAN quarantine and branches the bound on isASAN || isDebug, which is exactly what REVIEW.md asks of leak tests.

I cross-checked the source ordering in SocketAddress.rs: Options::from_js reads address into a BunString at line ~72 before validating family, port, and flowlabel, so bad_family/bad_port/bad_flow_type/bad_flow_range all throw after the string ref exists. pton_reject and blocklist_bad_family cover the two remaining post-read throw sites named in the description. The child prints a per-case diagnostic line and then the {deltaMiB: max} line that expectRssDeltaBelow parses via .split("\n").at(-1), so the extra diagnostic line is harmless.

Other factors

My prior review comment (stale title/description) was addressed and the thread is resolved — the title and body now describe only this test, with a Notes block explaining why the two dropped tests were vacuous. The PR body records both bun bd test (67 pass) and USE_SYSTEM_BUN=1 on bun 1.4.0 (fails with 57 MiB), satisfying the fails-without-fix requirement. Harness idioms are followed: Buffer.alloc(n, fill).toString() instead of .repeat(), per-iteration big + i to defeat string dedup, warmup before measurement, Bun.gc(true) between windows, --smol.

This branch has not been deployed

No deployments
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.

1 participant