Skip to content

Fix unchecked exception in Bun.plugin target conversion#31086

Closed
robobun wants to merge 1 commit into
mainfrom
farm/bc0d04a5/fix-plugin-target-exception
Closed

Fix unchecked exception in Bun.plugin target conversion#31086
robobun wants to merge 1 commit into
mainfrom
farm/bc0d04a5/fix-plugin-target-exception

Conversation

@robobun

@robobun robobun commented May 19, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Fixes an ExceptionScope::assertNoException() assertion failure in debug builds when Bun.plugin() is called with a target option that throws during string conversion (e.g. an object whose toString/valueOf return non-primitives).

toStringOrNull() returns nullptr and leaves the exception pending. The previous code only checked for a non-null return and fell through to construct the builder object and call setup() with the exception still pending.

Repro

const badTarget = { toString: () => ({}), valueOf: () => ({}) };
Bun.plugin({ name: "x", target: badTarget, setup() {} });

Before:

ASSERTION FAILED: Unexpected exception observed
!exception()
ExceptionScope.h(61) : void JSC::ExceptionScope::assertNoException()

After: the TypeError: No default value exception is propagated to JS and can be caught.

Found by Fuzzilli. Fingerprint: 2fddca8bb30d77a3

When the target option passed to Bun.plugin() was an object whose
toString/valueOf returned a non-primitive, toStringOrNull() would throw
and return null, but the pending exception was not checked before
continuing to construct the builder object and invoke setup(), tripping
an ExceptionScope assertion.
@robobun

robobun commented May 19, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 11:32 AM PT - May 19th, 2026

@robobun, your commit 989e6aa has 9 failures in Build #56171 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 31086

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

bun-31086 --bun

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: bd72f52b-6eeb-43d6-bc69-9dc6b084614d

📥 Commits

Reviewing files that changed from the base of the PR and between 184d037 and 989e6aa.

📒 Files selected for processing (2)
  • src/jsc/bindings/BunPlugin.cpp
  • test/js/bun/plugin/plugins.test.ts

Walkthrough

This PR hardens plugin target validation by refactoring the C++ string extraction code to check for exceptions immediately after converting the target value to a string, and adds a test case verifying the plugin handles malicious toString/valueOf implementations without crashing.

Changes

Plugin target robustness

Layer / File(s) Summary
Plugin target exception handling
src/jsc/bindings/BunPlugin.cpp, test/js/bun/plugin/plugins.test.ts
In setupBunPlugin, the target string extraction is refactored from an inline conditional initializer to a separate assignment with explicit RETURN_IF_EXCEPTION checking immediately after conversion. A new test case verifies the plugin throws (but does not crash) when target is an object whose toString() and valueOf() throw non-string values.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: fixing an unchecked exception in Bun.plugin's target conversion, which is the primary focus of the changeset.
Description check ✅ Passed The description includes both required template sections: 'What does this PR do?' is detailed with context and code examples, and 'How did you verify your code works?' is covered through the repro case and Fuzzilli discovery reference.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@github-actions

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. Fix unchecked exception when Bun.plugin target coercion throws #31017 - Fix unchecked exception when Bun.plugin target coercion throws
  2. Handle exceptions from toStringOrNull in Bun.plugin target parsing #28369 - Handle exceptions from toStringOrNull in Bun.plugin target parsing

🤖 Generated with Claude Code

@robobun

robobun commented May 19, 2026

Copy link
Copy Markdown
Collaborator Author

Duplicate of #31017 (and #28369). Closing in favor of the earlier PR.

@robobun robobun closed this May 19, 2026
@robobun robobun deleted the farm/bc0d04a5/fix-plugin-target-exception branch May 19, 2026 18:31

@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 — straightforward RETURN_IF_EXCEPTION additions following the established JSC pattern, with a regression test.

Extended reasoning...

Overview

This PR adds two RETURN_IF_EXCEPTION(throwScope, {}) checks in setupBunPlugin() (src/jsc/bindings/BunPlugin.cpp) after targetValue.toStringOrNull(globalObject) and after targetJSString->value(globalObject). Previously, if toStringOrNull threw (e.g. an object whose toString/valueOf both return non-primitives), it returned nullptr, the if body was skipped, and execution continued with a pending exception — tripping ExceptionScope::assertNoException() in debug builds. The fix restructures the if to check the exception immediately and bail out. A regression test in test/js/bun/plugin/plugins.test.ts verifies the call now throws cleanly instead of asserting.

Security risks

None. This is defensive exception-propagation hardening on a user-supplied options object. The change strictly narrows behavior: where the old code would proceed with a pending exception (assertion in debug, undefined-ish behavior in release), the new code returns early and surfaces the JS TypeError to the caller. No new code paths, no new inputs accepted, no auth/crypto/permissions involved.

Level of scrutiny

Low. The diff is 4 effective lines of C++ that apply the same RETURN_IF_EXCEPTION macro already used a dozen+ times in this very function and file (e.g. after getIfPropertyExists, toWTFString, etc.). It is a mechanical, idiomatic JSC fix for a fuzzer-found edge case, not a logic or design change.

Other factors

  • The added test directly exercises the repro from the PR description and asserts .toThrow(), so the fix is covered.
  • No CODEOWNERS entry matches src/jsc/bindings/.
  • No prior reviewer comments to address; the bug-hunting system found no issues.
  • The second RETURN_IF_EXCEPTION after ->value(globalObject) is belt-and-suspenders (rope resolution can in principle throw OOM) and is harmless/correct.

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