Repository navigation
fix(shell): prevent double-free of ShellArgs in error path - #26277
Conversation
|
Updated 6:27 PM PT - Jan 19th, 2026
❌ @Jarred-Sumner, your commit 46a3984 has 3 failures in
🧪 To try this PR locally: bunx bun-pr 26277That installs a local version of the PR into your bun-26277 --bun |
|
Caution Review failedThe pull request is closed. WalkthroughThe change removes an explicit Changes
Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/shell/interpreter.zig (1)
747-754: Finalize here skips root IO/root shell cleanup before destroying the interpreter.At Line 747–754,
interpreter.finalize()runsdeinitFromFinalizer(), butthis_jsvalueis still.zero, so#derefRootShellAndIOIfNeeded()won’t callroot_io.deref()orroot_shell.deinitImpl(). That leaks the dup’d stdin/cwd fd and env maps on this error path. Prefer a cleanup path that releases IO/root_shell plus args before destroying the interpreter.💡 Proposed fix
- // Note: Don't call shargs.deinit() here - interpreter.finalize() will do it - // since interpreter.args points to shargs after init() succeeds. - interpreter.finalize(); + // Fully release args + IO/root_shell before destroying the interpreter. + interpreter.args.deinit(); + interpreter.#deinitFromExec();
🤖 Fix all issues with AI agents
In `@test/regression/issue/24368.test.ts`:
- Around line 9-18: The test holds ShellPromise objects in the results array
when Bun.gc(true) is called, preventing finalizers from running reliably; after
awaiting Promise.all(results) clear references by emptying or nulling the
results array (e.g., set results.length = 0 or assign null) so
ShellPromise/ShellInterpreter objects can become unreachable before calling
Bun.gc(true) in the test "shell interpreter GC finalization does not crash".
When interpreter.init() succeeds but globalThis.hasException() is true, the code was calling shargs.deinit() before interpreter.finalize(). However, interpreter.args points to shargs after init() succeeds, so interpreter.finalize() -> deinitFromFinalizer() -> this.args.deinit() would then try to deinit an already-freed ShellArgs, causing a double-free. This is a related issue to #24368, which reported crashes during GC finalization of ShellInterpreter objects. While the main fix for #24368 was added in v1.3.6 (commit 367eeb3), this fixes an additional double-free bug in the error handling path. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
f5af9d2 to
4849a06
Compare
|
Addressed the review feedback: Updated the test to clear the |
Summary
What Changed
When
interpreter.init()succeeds butglobalThis.hasException()is true, the code was callingshargs.deinit()beforeinterpreter.finalize(). However,interpreter.argspoints toshargsafterinit()succeeds, so callinginterpreter.finalize()->deinitFromFinalizer()->this.args.deinit()would then try to deinit an already-freedShellArgs, causing a double-free.This is a related issue to #24368, which reported crashes during GC finalization of ShellInterpreter objects. While the main fix for #24368 was added in v1.3.6 (commit 367eeb3), this fixes an additional double-free bug in the error handling path.
Test plan
test/regression/issue/24368.test.tsbun bd test test/regression/issue/24368.test.tsFixes #24368
🤖 Generated with Claude Code