Skip to content

Bump WebKit: keep the locals of a using block's synthesized catch alive in optimized code - #37941

Draft
robobun wants to merge 2 commits into
mainfrom
farm/a3a8c180/dfg-live-catch-nested-handler
Draft

Bump WebKit: keep the locals of a using block's synthesized catch alive in optimized code#37941
robobun wants to merge 2 commits into
mainfrom
farm/a3a8c180/dfg-live-catch-nested-handler

Conversation

@robobun

@robobun robobun commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Found by fuzzing. Fingerprint: DFGOSRAvailabilityAnalysisPhase.cpp(198).

Problem

Reduced fuzzer input, run through indirect eval (that is how the REPRL harness evaluates scripts; a plain function with a using block hits the same thing once it reaches the FTL, see the second fixture):

const resource = { [Symbol.dispose]() {} };
for (using r of [resource]) {
  try { r(); } catch (e) {}
}
for (let i = 0; i < 2000000; ++i) {}

Debug/ASAN builds abort when the FTL compiles it:

DFG ASSERTION FAILED: Live bytecode local not available: operand = loc21, availabilityMap = {...}, origin = bc#188
vendor/WebKit/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp(198) : ...::validateNode(Node *, LocalOSRAvailabilityCalculator &)

loc21 is the hasError ("the body threw") flag of the disposal code the bytecode generator emits for using. Release builds do not abort, they miscompile: once a function containing a using block is optimized (the DFG tier is enough; the dispose call just has to remain a real call instead of being inlined), a dispose method that throws after the body also threw comes out as the dispose method's plain Error instead of a SuppressedError holding both errors, so the body's exception is silently dropped. On a release build of the unfixed engine (the phase in question is identical from the build I used through main's current pin and WebKit main):

$ bun test/js/bun/jsc-stress/fixtures/using-dispose-throw-after-body-throw-in-jit.js
error: Expected true but got false          # error instanceof SuppressedError
$ BUN_JSC_useJIT=0 bun <same file>           # interpreter: passes

Cause

DFG's LiveCatchVariablePreservationPhase keeps the locals a catch handler reads alive by inserting Flushes: in front of SetLocals inside a try range, and whenever a block's nodes cross from one handler's range into a different one (plus at the end of the block). The handler lookup it uses to detect "the handler changed" also overwrote its cached live-at-catch set with the liveness of the handler it had just found, before the flush for the handler being left was emitted, so a transition straight from one handler into another flushed the wrong handler's locals. Plain try/catch never crosses handlers inside a block because TryNode emits its jump over the catch block inside the try range. The using disposal code ends the synthesized catch's range right after the dispose call, at a label, so the block ends there as well and its terminal carries the origin of the next bytecode, which belongs to the enclosing handler (the for-of's synthesized finally here, the user's try/catch in the function case). hasError is only read by that synthesized catch, so with the flush missing nothing keeps it alive across the merge where it is either false or true; it is unavailable at the dispose call's exception exit, the exit restores it as undefined, and the catch treats that as "no pending error".

Fix

oven-sh/WebKit#417: flush for the handler being left first, then compute the liveness of the handler being entered. That PR is rebased onto WebKit eeab0404, which is exactly what main pins today (the upstream merge in #39371 did not touch this phase), so its preview build (autobuild-preview-pr-417-ece09c13, the pin in this PR) is main's engine plus that one change. The pin should move to the final autobuild-<sha> once #417 lands.

Fixtures in test/js/bun/jsc-stress: for-using-dispose-call-live-catch-locals-ftl-validation.js is the first fuzzer shape (eval, dispose call not inlined) and using-inlined-dispose-live-catch-locals-ftl-validation.js is a small deterministic version of a second fuzzer input that hit the same assertion with the dispose method inlined into the call and the function reached through recursion; both run with --validateGraph=1, so they fail on release builds of the unfixed engine too. using-dispose-throw-after-body-throw-in-jit.js checks the SuppressedError behaviour once the function is optimized. The first and third of these are also in the WebKit PR as stress tests.

Verification

  • Fail before: all three fixtures hit the assertion above on the jsc shell shipped in the 0cbb4a19 debug-asan prebuilt (main pinned that when the evidence was gathered) and on the shell from today's pin eeab0404 (loc12 in the function shapes, loc21 in the eval shape), the two validation fixtures also fail through the harness with USE_SYSTEM_BUN=1, and the behaviour fixture returns the wrong result on a release build of the unfixed engine. The second fuzzer input itself asserts on the unfixed engine within its first FTL compile.
  • Pass after: bun bd against the preview tarball, on this branch rebased onto current main: all three fixtures pass, jsc-stress.test.ts is 117/117 before the third fixture was added, and both fuzzer inputs no longer assert (the second one completes the FTL compile that used to fail; the input itself then runs for minutes even on a release build because it recurses to stack overflow from every level of another recursion). The preview's own jsc shell passes the fixtures as well. (Before the rebase, the earlier preview also passed test/js/web/explicit-resource-management.test.ts, test/js/bun/test/mock-disposable.test.ts, test/js/bun/resolve/lower-using-bun-target.test.ts, test/js/bun/jsc/bun-jsc.test.ts and test/js/node/async_hooks.)
  • The SuppressedError scenario matches the interpreter at both the DFG and FTL tiers with the fix.

Rebased onto main three times as the WebKit pin moved underneath, most recently for the upstream merge in #39371; each time the only conflict was the WEBKIT_VERSION line, resolved to the preview built on top of the new pin. Latest: preview ece09c13 on eeab0404, jsc-stress 118/118 with it, and the three fixtures still fail on the eeab0404 shell.


[decide:webkit] gate passed · iteration 1 · 5 files touched

passes on PR (with fix)
Test-only change.

Debug/ASAN (expected pass):
$ bun bd test 'test/js/bun/jsc-stress/jsc-stress.test.ts'
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test test/js/bun/jsc-stress/jsc-stress.test.ts
bun test v1.4.0 (8326d1bd3)

test/js/bun/jsc-stress/jsc-stress.test.ts:
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-arithsin.js [435.90ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-arithcos.js [419.13ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-arithtan.js [418.74ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-string-equality.js [444.79ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-arithsqrt.js [468.31ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-string-strict-equality.js [432.78ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-library-substring.js [412.80ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-string-ident-equality.js [437.84ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-regexp-exec.js [526.62ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-regexp-test.js [526.68ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-getmyargumentslength-inline.js [345.01ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-getmyargumentslength.js [430.20ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-get-my-argument-by-val.js [468.61ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-get-my-argument-by-val-inlined.js [474.12ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-get-my-argument-by-val-inlined-and-not-inlined.js [521.30ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-call-exception.js [478.99ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-call-varargs-exception.js [358.27ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-call-exception-no-catch.js [469.19ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-try-catch-arith-sub-exception.js [446.99ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DF
... (truncated)
Exit: 0
diff hotspot
scripts/build/deps/webkit.ts                       |  2 +-
 ...ispose-call-live-catch-locals-ftl-validation.js | 18 ++++++++
 .../using-dispose-throw-after-body-throw-in-jit.js | 54 ++++++++++++++++++++++
 ...ned-dispose-live-catch-locals-ftl-validation.js | 28 +++++++++++
 test/js/bun/jsc-stress/jsc-stress.test.ts          |  4 ++
 5 files changed, 105 insertions(+), 1 deletion(-)

gate history · 5 passed · 0 rejected · iteration 1

evidence per changed file
file                                                      reads  edits  tests
scripts/build/deps/webkit.ts                                  5      4      0
…-using-dispose-call-live-catch-locals-ftl-validation.js      0      1      0
…fixtures/using-dispose-throw-after-body-throw-in-jit.js      0      1      0
…ing-inlined-dispose-live-catch-locals-ftl-validation.js      1      1      0
test/js/bun/jsc-stress/jsc-stress.test.ts                     2      3      0

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 56 seconds

Limit details: You’ve used all 5 included reviews currently available under your plan.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f7c5a61b-bf29-4b7a-9980-7f9171bd0122

📥 Commits

Reviewing files that changed from the base of the PR and between 8326d1b and 61a54bd.

📒 Files selected for processing (4)
  • scripts/build/deps/webkit.ts
  • test/js/bun/jsc-stress/fixtures/for-using-dispose-call-live-catch-locals-ftl-validation.js
  • test/js/bun/jsc-stress/fixtures/using-dispose-throw-after-body-throw-in-jit.js
  • test/js/bun/jsc-stress/jsc-stress.test.ts

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

@robobun

robobun commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 12:07 AM PT - Aug 18th, 2026

@robobun, your commit db1e4fe60f79bb220a14d93e5112e1e19b8066a9 passed in Build #100437! 🎉


🧪   To try this PR locally:

bunx bun-pr 37941

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

bun-37941 --bun

@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.

I reviewed this PR and didn't find any bugs. The Bun-side diff is small and the two new stress fixtures look correct (the requireOptions directive is parsed by the existing harness, and the SuppressedError assertions are exact). That said, this pins WebKit to a preview build of an unmerged PR and pulls in everything between the old pin and current WebKit main (including oven-sh/WebKit#410) — a maintainer should sign off on shipping against a preview tag and on the wider engine bump.

Extended reasoning...

Overview

This PR bumps WEBKIT_VERSION in scripts/build/deps/webkit.ts from a commit sha to autobuild-preview-pr-417-fbb76610, a preview build of oven-sh/WebKit#417 that fixes a DFG LiveCatchVariablePreservationPhase bug affecting using disposal in optimized code. It also adds two JSC stress fixtures (the fuzzer-shaped validation case and a behavioural SuppressedError check) and wires them into jsc-stress.test.ts.

Security risks

None identified. The change is a dependency version bump plus test fixtures; no auth, crypto, or input-handling code is touched in this repo.

Level of scrutiny

High. Although the local diff is tiny, the effective change is a new JavaScriptCore binary for every platform. The preview build is cut from current WebKit main, so it carries not only the targeted fix but everything between the previous pin (7b763944…) and main — the description calls out oven-sh/WebKit#410 explicitly. Pinning to a preview tag of an unmerged upstream PR (with the description noting the pin "should move to the final autobuild-<sha> once it lands") is a release-process decision that a maintainer should confirm rather than an automated reviewer.

Other factors

  • The fixtures follow the existing jsc-stress conventions: // @bun header, //@ requireOptions(...) directives that parseJSCFlags already handles, and self-checking via shouldBe / non-zero exit on failure. --validateGraph=1 on the FTL fixture makes it fail on release builds of the unfixed engine, and the behavioural fixture asserts the exact SuppressedError shape.
  • Verified that prebuiltUrl/prebuiltDestDir in webkit.ts already handle autobuild--prefixed version strings, so the tag form of the pin resolves to the correct release URL and cache key without further changes.
  • The description's verification (fixtures fail under USE_SYSTEM_BUN=1, pass on the preview build, related ERM/async_hooks suites pass) is thorough, but CI on the actual preview tarball is what proves the wider bump is safe across platforms — that's the part a human should look at before merging.

@robobun

robobun commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator Author

On the pin: the preview tag is only there so CI can exercise the fix before oven-sh/WebKit#417 lands; once it merges this should be re-pointed at the merged main sha, as with previous bumps of this kind. The engine content between the current pin (7b763944) and the preview is #417 itself plus oven-sh/WebKit#410 (InternalFieldTuple cast by JSType); the remaining commits in that range (#411 to #414) only change how the prebuilts are built (line tables only for the -lto variants, extra freebsd/android variants). Any bump that picks up #417 necessarily carries those too.

What the preview tarball has shown so far: locally the debug-asan tarball builds and passes jsc-stress (117/117), the explicit resource management, mock-disposable, bun-jsc and async_hooks suites (the last one covers the #410 cast path). The one failure in build 93631 was the x64-musl build step getting a 404 for bun-webkit-linux-amd64-musl-lto.tar.gz: the preview release's last asset upload had stalled, so that lane started before the asset existed. The WebKit release job has been rerun and is re-uploading the assets; I will retrigger CI here once the release is complete.

@robobun

robobun commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator Author

The preview release is complete now (all 42 tarballs uploaded, including the musl-lto one that 404'd in build 93631), so CI has been retriggered: https://buildkite.com/bun/bun/builds/93640. No code changes since the review above; the new commit is empty.

@robobun

robobun commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator Author

Status: build 93640's only failure is the linux x64 build-bun step, and it failed fetching the lolhtml source tarball (github.com archive download, "fetch failed" after 5 retries), not anything in this change; the preview WebKit tarball itself downloaded and extracted fine on that agent, and the other platforms' lanes are still running. That step just needs a retry. I am not going to push another empty commit for it; whoever picks this up can retry the step in Buildkite, and I will follow up on anything it turns up.

Unchanged from the description: with the preview tarball locally, both new fixtures pass, jsc-stress is 117/117, and the original fuzzer input no longer asserts; both fixtures fail on the engine without oven-sh/WebKit#417.

@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.

I reviewed this PR and didn't find any bugs in the diff itself. Because it bumps the WebKit/JavaScriptCore pin — and to a preview tag that the description says should be re-pointed at the merged autobuild-<sha> once oven-sh/WebKit#417 lands — a human should sign off on the engine bump and confirm the final pin before merge.

What was reviewed:

  • prebuiltUrl/prebuiltDestDir handle the autobuild--prefixed version string correctly (no double prefix; cache key uses the full tag).
  • The two new fixtures use //@ requireOptions(...), which parseJSCFlags already handles, and are wired into the jsFixtures list.
  • The behavioural fixture asserts SuppressedError with both .error and .suppressed messages after warming the JIT; the validation fixture relies on --validateGraph=1 so it fails on release builds too.
Extended reasoning...

Overview

This PR changes the WEBKIT_VERSION constant in scripts/build/deps/webkit.ts from the SHA 7b763944… to a preview tag autobuild-preview-pr-417-fbb76610, and adds two JSC stress-test fixtures (plus registers them in jsc-stress.test.ts). The actual code fix lives in oven-sh/WebKit#417; this PR is the Bun-side pin bump and regression coverage.

Security risks

None introduced by the diff. The bump pulls a prebuilt tarball from oven-sh/WebKit's own GitHub releases, same mechanism as before. No new user-facing surface, parsing, or auth paths are touched.

Level of scrutiny

High. Although the textual diff is tiny, changing the JavaScriptCore pin swaps out the JS engine for every code path in Bun. The bump also carries oven-sh/WebKit#410 and build-only changes #411#414 in addition to the targeted #417 fix. Engine bumps are exactly the kind of change where a maintainer should confirm CI is fully green across all lanes and decide whether to land on the preview tag or wait for the merged SHA.

Other factors

  • The PR description and the follow-up comment both state the preview pin is temporary and should move to the final autobuild-<sha> once #417 merges — that's a pending action a human needs to track.
  • The prior CI run (#93631) had a lane fail on a 404 for a not-yet-uploaded preview asset; the current build (#93640) was retriggered after re-uploading. A human should verify it went green.
  • The test additions themselves look correct and follow the existing jsc-stress fixture conventions (//@ requireOptions, shouldBe helper, megamorphic call-site trick to prevent inlining). No issues found there.
  • No prior review comments from me on this PR.

@robobun

robobun commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator Author

Nothing new to change from this review; the pin handling and the CI state are covered in the two comments above. For the record on the "verify it went green" point: in build 93640 the only failure is still the linux x64 build step (lolhtml source tarball download), which blocks the 41 x64 test lanes behind it; everything else is proceeding against the preview tarball, 113 jobs passed so far, including test lanes on the x64-asan (assert-enabled engine), alpine and windows builds. The handful of yellow entries so far are timing and GC sensitive tests that passed on retry or alone (test-fs-read-stream-pos, inspect-error-leak, sourcetextmodule-leak, napi GC condition, test-error-code-done-callback), none of them near exception handling or disposal. The x64 build step still just needs a retry in Buildkite.

@robobun
robobun force-pushed the farm/a3a8c180/dfg-live-catch-nested-handler branch from ae6e00a to 61a54bd Compare August 16, 2026 19:48
@robobun

robobun commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto current main. The WebKit pin had moved five times underneath this branch, so the only conflict was the WEBKIT_VERSION line. oven-sh/WebKit#417 is now rebased onto 0cbb4a19 (main's current pin) and this branch pins its new preview, autobuild-preview-pr-417-bc08a6e1, so the engine delta against main is just that one change; the earlier note about the preview also carrying #410 no longer applies. The empty retrigger commit is gone, the fixtures are unchanged.

Re-verified on the rebased branch: both fixtures still assert on the jsc shell from main's current 0cbb4a19 debug-asan prebuilt, and with the new preview bun bd passes both of them and the rest of jsc-stress (117/117), and the original fuzzer input no longer asserts. Description updated accordingly.

Comment thread scripts/build/deps/webkit.ts Outdated
* From https://github.com/oven-sh/WebKit releases.
*/
export const WEBKIT_VERSION = "0cbb4a194653231955187f9d8a2990d4b4a55266";
export const WEBKIT_VERSION = "autobuild-preview-pr-417-bc08a6e1";

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.

🔴 WEBKIT_VERSION is pinned to autobuild-preview-pr-417-bc08a6e1, a preview tag for the unmerged oven-sh/WebKit#417. Per the repo's Dependencies & vendoring rules ("Never merge a pin to an ephemeral artifact (preview tags, unmerged-PR builds)"), this needs to be re-pointed at the merged main commit's autobuild-<sha> — with all 42 platform×flavor tarballs verified present — before this PR lands. The PR description already notes this; flagging so it isn't merged as-is.

Extended reasoning...

What the bug is

scripts/build/deps/webkit.ts:6 sets WEBKIT_VERSION = "autobuild-preview-pr-417-bc08a6e1". This is a preview-build tag published by oven-sh/WebKit's CI for the not-yet-merged PR oven-sh/WebKit#417, not a release tag for a commit on WebKit main.

The repo's own dependency rules in .claude/docs/landing-prs.md:47 (Dependencies & vendoring, referenced from REVIEW.md) state explicitly:

Version bumps are repo-wide, verified operations. Never merge a pin to an ephemeral artifact (preview tags, unmerged-PR builds) — swap to the merged upstream SHA and verify prebuilt artifacts exist for every platform × flavor before merge.

An autobuild-preview-pr-* tag is precisely the "preview tags, unmerged-PR builds" case that rule prohibits merging.

The code path that triggers it

prebuiltUrl() in the same file constructs the download URL from cfg.webkitVersion:

const tag = version.startsWith("autobuild-") ? version : `autobuild-${version}`;
return `https://github.com/oven-sh/WebKit/releases/download/${tag}/${name}.tar.gz`;

So every fresh build with cfg.webkit === "prebuilt" (the default for CI and most local builds) fetches from https://github.com/oven-sh/WebKit/releases/download/autobuild-preview-pr-417-bc08a6e1/bun-webkit-<os>-<arch><suffix>.tar.gz. Preview releases are transient by convention — once oven-sh/WebKit#417 merges and gets its own autobuild-<merged-sha> release, the preview release can be (and typically is) cleaned up. At that point every fresh build 404s on the WebKit tarball, and bun bd / CI's build-bun step fails before compilation even starts.

Why nothing prevents it

There is no fallback in source.ts's prebuilt fetch — a 404 on the tarball is a hard build failure (the timeline already shows one lane 404ing on a not-yet-uploaded asset in build 93631). prebuiltDestDir() caches by version string, so machines that already have webkit-preview-pr-417-bc08a6e1<suffix> extracted keep working, which masks the problem locally but leaves every fresh checkout, new CI runner, and cache-cleared machine broken.

Step-by-step proof

  1. This PR merges with WEBKIT_VERSION = "autobuild-preview-pr-417-bc08a6e1".
  2. DFG: LiveCatchVariablePreservationPhase flushed the wrong handler's locals when one try range leads directly into another WebKit#417 merges to main; CI publishes autobuild-<merged-sha> with the same 42 tarballs.
  3. The now-redundant autobuild-preview-pr-417-bc08a6e1 GitHub release is deleted (standard practice for preview tags).
  4. A developer clones bun fresh and runs bun bd, or a CI runner starts with an empty cache.
  5. prebuiltUrl() returns .../autobuild-preview-pr-417-bc08a6e1/bun-webkit-linux-amd64-lto.tar.gz → 404.
  6. Build fails at the "fetch WebKit" step. Every platform × flavor is affected.

Impact

Merging as-is puts main one preview-release deletion away from being unbuildable from a clean checkout. The author has already acknowledged this in both the PR description ("the pin should move to the final autobuild-<sha> once it lands on WebKit main") and the timeline ("once it merges this should be re-pointed at the merged main sha"), so this is not a disagreement about the fix — it is the repo-mandated gate that must be satisfied before merge.

How to fix

Wait for oven-sh/WebKit#417 to land on main, then replace this line with the merged commit's 40-hex SHA (e.g. export const WEBKIT_VERSION = "<merged-sha>";) and confirm all 42 tarballs (os × arch × {musl,android,freebsd} × {debug,lto,asan}) exist under the corresponding autobuild-<sha> release before merging this PR.

@robobun
robobun marked this pull request as draft August 16, 2026 20:04
@robobun

robobun commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Agreed, and that matches the plan in the description: the preview pin exists so CI can exercise the fix, it must not land. To make that explicit I have converted this PR to a draft. Once oven-sh/WebKit#417 merges I will re-point WEBKIT_VERSION at the merged commit's autobuild- release, check that all 42 tarballs are present in it (the same list the preview release has), and mark the PR ready. The version only lives in scripts/build/deps/webkit.ts on this branch (git grep finds no other copy of the current pin), so that swap is the one line.

@robobun

robobun commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

A second fuzzer input landed on the same fingerprint (same phase, same hasError local, loc20 this time). Its shape differs in two ways worth recording: the dispose method is inlined into the dispose call (it is trivial, so inlining does not split the block the way it did in the case I had looked at earlier), and the function gets hot through recursion rather than a loop. It asserts on the unfixed engine during the first FTL compile of the function; with the preview engine that compile succeeds and nothing asserts (the input itself then keeps running for minutes because it recurses to stack overflow from every level of a second recursion, so it is not usable as a test as is).

Pushed c507330 with a small deterministic version of that shape as a third fixture, using-inlined-dispose-live-catch-locals-ftl-validation.js (2s on a debug build; the dump confirms the dispose closure is inlined at the call). It asserts on the jsc shell from main's current 0cbb4a19 prebuilt, fails through the harness with USE_SYSTEM_BUN=1, and passes with bun bd test. No engine change needed, so oven-sh/WebKit#417 and the pin are untouched; description updated.

@robobun
robobun force-pushed the farm/a3a8c180/dfg-live-catch-nested-handler branch from c507330 to 298e421 Compare August 17, 2026 00:52
@robobun

robobun commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased again: main moved its pin to c6cfe90c (#39368), so the only conflict was the WEBKIT_VERSION line once more. oven-sh/WebKit#417 is now rebased onto c6cfe90c and this branch pins its new preview, autobuild-preview-pr-417-9984a386 (all 42 tarballs present), so the engine delta against main is still just that one change. With it, bun bd passes jsc-stress 118/118 (including the three fixtures here) plus the explicit resource management, mock-disposable and bun-jsc suites. Still a draft until #417 lands and the pin can move to the merged sha.

@robobun

robobun commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

A third fuzzer input came in on the same fingerprint. It is the eval shape again:

const v3 = { [Symbol.dispose]() {} };
for (using v5 of [v3]) {
    try { SharedArrayBuffer.call(); } catch (e) {}
}
let v12 = new Int8Array(51)[44];
v12++;
while (v12) {}   // never ends; this is what tiers the eval code up to the FTL

That is the shape for-using-dispose-call-live-catch-locals-ftl-validation.js already covers (the first input had r() in the try block and a bounded loop after it). The local that goes missing is the same synthesized hasError temporary of the dispose handler (loc24 with a plain indirect-eval wrapper, loc74 under the fuzzer's own prelude). Checked it against the debug-asan prebuilts: on main's pin (c6cfe90c) the jsc shell asserts in the FTL compile of <eval> exactly as reported, and on the preview pinned here that compile completes (Optimized <eval> ... using FTL with FTL) and the script then just sits in its loop until killed. The fuzzing build of main still asserts on it as well. So there is nothing to add on this branch for it.

CI for the rebased head (298e421) passed: build 99684, 179/179. Still a draft until oven-sh/WebKit#417 lands and the pin can move to the merged sha.

…ized code

DFG's LiveCatchVariablePreservationPhase flushed the locals of the handler
being entered instead of the handler being left when one try range led
directly into another. The dispose call emitted for a `using` block ends its
synthesized catch range right at the call, inside the enclosing handler, so
the "body threw" flag that only the synthesized catch reads was never flushed.
FTL compiles of such code failed OSR availability validation, and optimized
code restored the flag as undefined when a dispose method threw after the
body threw, dropping the body's error instead of reporting a SuppressedError.

Pins oven-sh/WebKit#417's preview build and adds both shapes as jsc-stress
fixtures.
…fixture

A second fuzzer input hit the same assertion with the dispose method inlined
into the dispose call (the method is trivial, so inlining does not split the
block) and the function reached through recursion. The original input does
quadratic work, so this is a small deterministic version of that shape; it
fails OSR availability validation on the unfixed engine and passes with the
fix.
@robobun

robobun commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

Main moved its pin to eeab0404 (#39371, the upstream merge), so the WEBKIT_VERSION line conflicts again. The bug is still there in that engine: all three fixtures assert on the jsc shell from the eeab0404 debug-asan prebuilt, and the phase itself did not change in the upstream range (the file is byte-identical between c6cfe90c and eeab0404), so oven-sh/WebKit#417 is still needed. It is now rebased onto eeab0404 (same patch, head ece09c13) and its preview build is running. This branch is rebased locally onto current main with the pin pointing at that preview; I will push it once the preview release has all of its tarballs, so CI does not run against a half-uploaded release like it did in build 93631.

@robobun
robobun force-pushed the farm/a3a8c180/dfg-live-catch-nested-handler branch from 298e421 to db1e4fe Compare August 18, 2026 06:48
@robobun

robobun commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed the rebase (db1e4fe). The preview for the rebased oven-sh/WebKit#417 is autobuild-preview-pr-417-ece09c13, built on eeab0404, and all 42 tarballs are in the release (one of its runner-side builds and then the last asset upload needed a rerun, which is why this took a while). This branch is still the same two commits, only the pin line changed in the conflict resolution. With that preview, bun bd passes jsc-stress 118/118 (the three fixtures included) plus the explicit resource management, mock-disposable and bun-jsc suites; the same three fixtures assert on the eeab0404 shell and fail through the harness with the system bun. Description updated. Still a draft until #417 merges and the pin can move to the merged sha.

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