fix(bundler): avoid aliased mutable borrows in chunk codegen#31267
fix(bundler): avoid aliased mutable borrows in chunk codegen#31267Dicklesworthstone wants to merge 1 commit into
Conversation
3003d75 to
c766026
Compare
|
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 (15)
WalkthroughConvert exclusive ChangesImmutability refactor for parallel chunk codegen
Possibly related PRs
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 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 `@src/bundler/LinkerContext.rs`:
- Around line 1785-1825: prepare_pretty_paths_for_isolated_hash currently calls
path_with_pretty_initialized(...).expect("OOM") and can abort; change the
function signature to return Result<(), ErrorType> (propagate the appropriate
bundler error type), replace the expect calls with ? so failures from
path_with_pretty_initialized are returned, and update callers (including
generate_isolated_hash) to propagate or handle that Result rather than
panicking; specifically modify prepare_pretty_paths_for_isolated_hash, any loop
branches that call path_with_pretty_initialized and the fallback in
generate_isolated_hash to thread the error via ? through
parse_graph_mut().input_files.items_source_mut() updates instead of calling
expect.
🪄 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: 2c90c1d6-e8a0-4efe-a922-d7d99a47aa21
📒 Files selected for processing (13)
src/ast/symbol.rssrc/bundler/Chunk.rssrc/bundler/LinkerContext.rssrc/bundler/linker_context/convertStmtsForChunk.rssrc/bundler/linker_context/convertStmtsForChunkForDevServer.rssrc/bundler/linker_context/generateChunksInParallel.rssrc/bundler/linker_context/generateCodeForFileInChunkJS.rssrc/bundler/linker_context/generateCompileResultForCssChunk.rssrc/bundler/linker_context/generateCompileResultForJSChunk.rssrc/bundler/linker_context/postProcessJSChunk.rssrc/bundler/ungate_support.rssrc/js_printer/lib.rssrc/js_printer/renamer.rs
1d8fa5b to
a70197b
Compare
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 `@src/js_printer/lib.rs`:
- Around line 1479-1495: The init function for RequireOrImportMetaCallback
should require T: Sync to ensure the stored context pointer is safe to share
across threads; update the generic bound on RequireOrImportMetaCallback::init
from T: RequireOrImportMetaSource to T: RequireOrImportMetaSource + Sync and
adjust the inner thunk signature bounds if necessary so the NonNull::from(ctx)
cast and subsequent cross-thread use are covered by Sync; make the same T: Sync
addition wherever init is invoked (e.g., RequireOrImportMetaCallback::init(self)
with self: &LinkerContext) to keep bounds consistent.
🪄 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: dbc80e1c-16f4-47bd-9534-ca8e4e8e30ee
📒 Files selected for processing (13)
src/ast/symbol.rssrc/bundler/Chunk.rssrc/bundler/LinkerContext.rssrc/bundler/linker_context/convertStmtsForChunk.rssrc/bundler/linker_context/convertStmtsForChunkForDevServer.rssrc/bundler/linker_context/generateChunksInParallel.rssrc/bundler/linker_context/generateCodeForFileInChunkJS.rssrc/bundler/linker_context/generateCompileResultForCssChunk.rssrc/bundler/linker_context/generateCompileResultForJSChunk.rssrc/bundler/linker_context/postProcessJSChunk.rssrc/bundler/ungate_support.rssrc/js_printer/lib.rssrc/js_printer/renamer.rs
The UB audit flagged EXP-111 / Bundler B-1..B-5 as a structural aliasing hazard in the parallel chunk-codegen fan-out. JS/CSS worker callbacks were rebuilding &mut LinkerContext and &mut Chunk from shared raw pointers even though the worker path only needs shared graph/chunk reads plus disjoint compile-result slot writes. Route JS/CSS part-range codegen through shared LinkerContext/Chunk borrows. Per-task output still goes through Chunk::write_compile_result_slot, and per-source byte counters remain atomic. Keep the truly per-chunk postprocess callbacks as &mut Chunk because each worker owns a distinct chunk pointer. Make the printer-side renamer view read-only so parallel part-range printers can borrow the per-chunk ChunkRenamer through &Chunk. SymbolMap::follow() is now read-only as well; the single-threaded follow_all(&mut self) pass remains the path-compression point instead of racing on Symbol::link Cell writes. Preserve the old serial side effects by initializing the browser transpiler and Source.path.pretty values before worker fan-out. generate_isolated_hash() now only takes &self during parallel post-processing, with a local fallback for any future non-prepared call path. Pretty-path initialization failures are returned through the existing bundler Result path instead of panicking. Keep the disjoint slot writer on ordinary shared access to the boxed UnsafeCell slice instead of raw-reading the Box representation. That preserves the no-&mut-Chunk invariant without relying on a stronger layout assumption than needed. Audit witness: - .ub-exorcism/2026-05-15-exhaustive/phase5_experiment_results/EXP-111-sb.log - Miri stacked-borrows report: data race detected between retag writes while reconstructing &mut Chunk on parallel worker threads. Verification: - cargo fmt -p bun_ast -p bun_js_printer -p bun_bundler - cargo fmt --check -p bun_ast -p bun_js_printer -p bun_bundler - env CARGO_TARGET_DIR=/tmp/bun-ub-fix-bundler-alias-v2-target cargo check -p bun_bundler - env CARGO_TARGET_DIR=/tmp/bun-ub-fix-bundler-alias-v2-target cargo check --workspace - bun bd test test/bundler/transpiler/transpiler.test.js - bun bd test test/bundler/bundler_barrel.test.ts - bun bd test test/bundler/bundler_html.test.ts - git diff --check origin/main...HEAD Co-Authored-By: Gemini <bot@gemini.local>
a70197b to
c47a469
Compare
Summary
Fixes the audit's EXP-111 / Bundler B-1..B-5 aliasing finding in the parallel chunk-codegen path.
The JS/CSS part-range worker callbacks used to rebuild
&mut LinkerContextand&mut Chunkfrom shared raw pointers even though that phase only needs shared graph/chunk reads plus disjoint compile-result slot writes. Under Miri stacked borrows, the EXP-111 witness reports a data race between retag writes while reconstructing&mut Chunkon parallel worker threads.This PR:
&LinkerContext/&ChunkborrowsChunk::write_compile_result_slot, which writes only the task's disjointcompile_results_for_chunk[i]slotUnsafeCellslice instead of raw-reading theBoxrepresentation&mut ChunkRenamerborrowsRequireOrImportMetaCallback::initcontexts to beSync, matching the shared worker-side callback useSymbolMap::follow()read-only;follow_all(&mut self)remains the serial path-compression passSource.path.prettybefore worker fan-outResultpath instead of panickingAudit Evidence
.ub-exorcism/2026-05-15-exhaustive/phase5_experiment_results/EXP-111-sb.log&mut Chunkfrom shared worker raw pointersVerification
cargo fmt -p bun_ast -p bun_js_printer -p bun_bundlercargo fmt --check -p bun_ast -p bun_js_printer -p bun_bundlerenv CARGO_TARGET_DIR=/tmp/bun-ub-fix-bundler-alias-v2-target cargo check -p bun_bundlerenv CARGO_TARGET_DIR=/tmp/bun-ub-fix-bundler-alias-v2-target cargo check --workspacebun bd test test/bundler/transpiler/transpiler.test.jsbun bd test test/bundler/bundler_barrel.test.tsbun bd test test/bundler/bundler_html.test.tsgit diff --check origin/main...HEAD