-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Fix missing exception check on Bun.plugin target coercion #31956
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 nit: now that
RETURN_IF_EXCEPTIONfollowstoStringOrNull(),targetJSStringcan never be null on line 311 —toStringOrNullreturns null iff an exception is pending (see the[[ZIG_EXPORT(null_is_throw)]]annotation at bindings.cpp:4697). Per CLAUDE.md ("guards a new validator makes redundant — a leftover null-check misleads readers into thinking the value can be absent"), drop theif (targetJSString)wrapper and unindent the body.Extended reasoning...
What & why
JSC's
JSValue::toStringOrNull(globalObject)is implemented astoString()followed by an internalRETURN_IF_EXCEPTION(scope, nullptr).toString()itself never returns null (it returns an emptyJSStringon failure), so the only waytoStringOrNullyieldsnullptris when an exception is pending. Bun's own FFI export documents this contract explicitly:This PR adds
RETURN_IF_EXCEPTION(throwScope, {})immediately after thetoStringOrNullcall (line 310). Once that macro returns on a pending exception, control reaching line 311 implies no exception is pending, which in turn impliestargetJSString != nullptr. Theif (targetJSString)guard is therefore unreachable-false — dead code introduced by this very change.Step-by-step proof
Consider the two possible outcomes of line 309:
toString/@@toPrimitivethrows (the fuzzer case this PR fixes).toStringOrNullreturnsnullptrand sets a pending exception. Line 310'sRETURN_IF_EXCEPTIONobserves the exception and returns{}. Line 311 is never reached.toString()returns a non-nullJSString*; the internal scope sees no exception, sotoStringOrNullforwards that non-null pointer. Line 310 sees no exception and falls through. Line 311 evaluatesif (non-null)— always true.There is no third path where line 311 is reached with
targetJSString == nullptr.Why existing code doesn't justify keeping it
One reviewer might note that
NodeModuleModule.cpp:220-224has the same belt-and-suspenders shape (toStringOrNull→RETURN_IF_EXCEPTION→if (!code)). However, the codebase is not uniform here —JSBuffer.cpp:279-281andErrorCode.cpp:376-378both dereference the result directly afterRETURN_IF_EXCEPTIONwith no null guard:So "prevailing convention" cuts both ways. The tiebreaker is the project's own contributor guidance — CLAUDE.md:323 is unambiguous and calls out this exact scenario:
This PR added the validator (
RETURN_IF_EXCEPTION); per the documented rule, removing the now-redundant guard is in-scope for the same PR rather than a drive-by style preference.Impact
None at runtime — the branch is always taken. The cost is purely readability: a future reader seeing
if (targetJSString)after the exception check will reasonably (but wrongly) infer there's a non-exception path wheretoStringOrNullreturns null, and may cargo-cult that defensive shape elsewhere.Fix
(Equivalently, switch to
targetValue.toString(globalObject)— which never returns null — and keep theRETURN_IF_EXCEPTION; either form removes the dead branch.)