leaksan: follow the Bun::generateModule rename to Bun::generateInternalModule - #40614
Conversation
…alModule #40597 renamed Bun::generateModule to Bun::generateInternalModule. The LSan suppression in test/leaksan.supp kept the old name, so it matched nothing. The node:fs Binding box that a macro VM creates inside `bun build` is owned by a JSC cell in libpas memory, which LSan does not scan. The macro VM is never torn down, so LSan reports the box as a direct leak and test/bundler/transpiler/macro-test.test.ts fails on the x64-asan lane. Rename the entry and document what it covers.
|
Status: reproduced locally with CI: |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 5 included reviews per hour; 0 remain after this review. WalkthroughChangesLSAN suppression update
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Full details: Description checkExplanation The description explains the problem, the suppression change, the technical cause, and detailed verification results. It does not use the template headings exactly, but it provides the required information and is sufficiently complete. Comment |
There was a problem hiding this comment.
LGTM — mechanical rename of an existing LSAN suppression to track the Bun::generateModule → Bun::generateInternalModule rename from #40597.
What was reviewed:
- Verified
Bun::generateInternalModuleexists atsrc/jsc/bindings/InternalModuleRegistry.cpp:103insidenamespace Bun, and that #40597 (595f97b) is where the old name disappeared. - Confirmed this restores an existing suppression rather than adding new coverage — not a weakening of the LSAN safety net.
- The added comment matches the file's established convention (see the
TemporalCore::withTimeZone/RunLoop::dispatchAfterentries) and explains the libpas-heap-invisible-to-LSAN mechanism plus the rename dependency.
Extended reasoning...
Overview
This PR touches a single file, test/leaksan.supp, replacing one line (leak:Bun::generateModule → leak:Bun::generateInternalModule) and adding a seven-line explanatory comment. The suppression file is the allowlist for LeakSanitizer false positives on the ASAN CI lane; entries substring-match frames in the recorded allocation stack. PR #40597 renamed the C++ function this entry targeted, so the old entry stopped matching and test/bundler/transpiler/macro-test.test.ts went red on the ASAN lane.
Security risks
None. This is a test-infrastructure config file that only affects whether LSAN reports are suppressed during CI test runs. It has no bearing on shipped runtime behavior, auth, crypto, or user-facing surface.
Level of scrutiny
Low. This is a one-line config rename plus a comment. I verified the factual claims: generateInternalModule is defined at InternalModuleRegistry.cpp:103 inside namespace Bun {, and git log -S confirms 595f97b (#40597) is where the generateModule symbol changed in that file. The old substring Bun::generateModule is not a substring of Bun::generateInternalModule (the "Internal" infix breaks it), so the PR's premise that the rename silently orphaned the suppression checks out. REVIEW.md's "never silently weaken a safety net" rule applies here in the change's favor: this restores a pre-existing suppression, it does not introduce new leak-hiding.
Other factors
The added comment satisfies CLAUDE.md's comment-quality bar (rules 13/14): it explains why the suppression exists (libpas heap invisible to LSAN, macro VM not torn down before exit) and warns future readers that the entry must track the function's name — both are things a future maintainer would otherwise spend real effort rediscovering. The style and depth match the other commented entries lower in the same file. No CODEOWNERS entry covers test/leaksan.supp. No prior review comments exist on the PR. Exit reason was dry_streak with zero findings.
…lete it `^Bun::generateInternalModule(` matches the function only. A plain substring would also match the lambda inside Bun::generateInternalModuleSourceCode, which the old `Bun::generateModule` entry never covered. The comment now names the allocation the entry exists for and the condition to remove it.
There was a problem hiding this comment.
LGTM — the follow-up commit anchors the entry and states its removal condition, which addresses the earlier feedback.
What was reviewed:
- Confirmed
Bun::generateInternalModuleexists atsrc/jsc/bindings/InternalModuleRegistry.cpp:103and thatgenerateInternalModuleSourceCodeis a distinct sibling the anchor now excludes. - Checked that
^is a real start-of-frame anchor in compiler-rt'sTemplateMatch(sanitizer suppression matcher) and(is matched literally, so^Bun::generateInternalModule(narrows rather than breaks the match. - Comment style and removal-condition phrasing match the existing entries in this file (e.g. the
TemporalCoreandRunLoop::dispatchAfterblocks).
Extended reasoning...
Overview
This PR updates a single line in test/leaksan.supp, the LeakSanitizer suppressions allowlist. PR #40597 renamed Bun::generateModule to Bun::generateInternalModule in InternalModuleRegistry.cpp, which made the existing leak:Bun::generateModule entry match nothing and turned test/bundler/transpiler/macro-test.test.ts red on the ASAN lane. The first commit renamed the entry; the second (pushed after the earlier review) tightened it to leak:^Bun::generateInternalModule( and added a comment naming what it covers, why LSAN reports it (JSC cells live in libpas memory that LSAN does not scan as a root region), and the concrete condition under which the entry should be deleted.
Security risks
None. This is a test-infrastructure change to a sanitizer suppressions file; no runtime code, no user-facing surface, no data handling.
Level of scrutiny
Low-to-moderate. REVIEW.md flags "never disable sanitizers or weaken CI verification to get green" and requires a stated reason for suppression edits. This change goes the right direction: it narrows an existing suppression (anchored with ^ and a trailing ( so Bun::generateInternalModuleSourceCode and any future generateInternalModule* siblings are not swept in), documents the exact allocation it covers (the node:fs Binding box owned by a NodeJSFS cell in the never-torn-down macro VM), and states a removal condition. I verified against the codebase that both Bun::generateInternalModule (InternalModuleRegistry.cpp:103) and generateInternalModuleSourceCode (ModuleLoader.cpp:71) exist, so the anchoring is meaningful. I also confirmed compiler-rt's suppression matcher (TemplateMatch) treats a leading ^ as a start-of-string anchor and other characters literally, so the "anchored" claim in the comment is accurate — this was the one point worth checking since the file has no other ^-anchored entries.
Other factors
The comment format mirrors the well-documented entries already in this file (WTF::RunLoop::dispatchAfter, TemporalCore::withTimeZone, TemporalCore::buildCalendarTemplate), which each explain the ownership chain, why LSAN can't see the root, and when to delete. The PR description includes verification (bun bd test with the CI env: 21 pass / 1 fail on the old file, 22 pass on the new) and a culprit bisection across build numbers. The earlier COMMENTED review on this PR preceded the anchoring commit, and that commit's changes (anchor + removal condition) are the plausible response to it; there are no outstanding CHANGES_REQUESTED reviews.
Problem
test/bundler/transpiler/macro-test.test.tsis red on the debian 13 x64-asan lane.bun buildwith a macro that importsnode:fsexits 134:LeakSanitizer: detected memory leaks,Direct leak of 4104 byte(s)fromBox::newinbun_runtime::node::node_fs_binding::create_binding(src/runtime/node/node_fs_binding.rs:329).Bun::generateModuletoBun::generateInternalModule(src/jsc/bindings/InternalModuleRegistry.cpp:103).test/leaksan.supp:26kept the old name, so the entry matched nothing. Every build with compile: builtin module sources in a dedicated section so --bytecode covers non-host targets #40597 fails, including its own. Builds without it pass.Fix
leak:^Bun::generateInternalModule(. The anchor matches the function only, not the lambda inBun::generateInternalModuleSourceCode, which the old entry never covered.NodeJSFSJSC cell owns the box. The macro VM insidebun buildis never torn down, so the finalizer never runs. Before compile: builtin module sources in a dedicated section so --bytecode covers non-host targets #40597 the entry suppressed exactly this allocation (print_suppressions=1:1 4104 Bun::generateModule).bun bd test test/bundler/transpiler/macro-test.test.tswith the CI LSan environment. Old file: 21 pass, 1 fail (the CI output). New file: 22 pass, suppression used:1 4104 ^Bun::generateInternalModule(. CI build 106730: the asan lane passes.Background
__asan_initis exported. bun does not export it, so JSC allocates through libpas, which LSan does not scan.test/leaksan.suppis the allowlist for that class of report. An entry matches a frame of the recorded allocation stack (30 frames in CI).^anchors the pattern at the start of the function name.Notes
macro-test.test.tson the asan lane (106531 job log:test/bundler/transpiler/macro-test.test.ts (5.81s)). The asan lane runs on PR builds only, so main never showed it.malloc_context_size=30reachesJSC::profiledCall), so locally this test leaks under LSan with any suppressions file unlessmalloc_context_sizeis raised. The release-asan build in CI inlines enough that the frame is recorded at position 12. The local runs above usedmalloc_context_size=80withBUN_DESTRUCT_VM_ON_EXIT=1,ASAN_OPTIONS=detect_leaks=1andLSAN_OPTIONS=suppressions=test/leaksan.supp.Bun::generateModulewas the only definition the old entry matched.bun buildcould tear down its macro VM on exit underBUN_DESTRUCT_VM_ON_EXIT, orcreate_bindingcould mark the box LSan-ignored (node:fs: mark the per-VM Binding box as LSan-ignored (fixes worker-terminate-lifetime.test.ts on main) #35159 proposes that). Either makes this entry deletable. Other entries in the file use Zig-era names (runtime.node.node_fs_binding.Bindings(.mkdtemp).runSync, ...) and are dead since the Rust port.no test proof · iteration 0 · no src or test change; test-proof not applicable