Skip to content

jsc-stress: add a fixture for regExp.test(regExp) in the DFG - #42694

Open
robobun wants to merge 2 commits into
mainfrom
robobun/c6613da2/webkit-regexp-test-regexp-is-argument
Open

robobun wants to merge 2 commits into
mainfrom
robobun/c6613da2/webkit-regexp-test-regexp-is-argument

Conversation

@robobun

@robobun robobun commented Sep 14, 2026 •

Copy link
Copy Markdown
Collaborator

Behaviour change: none

Problem

  • A debug or ASAN build of Bun aborted when a hot function called regExp.test(regExp): ASSERTION FAILED: noOverlap(baseGPR, argumentGPR, scratch1GPR, scratch2GPR) in emitRegExpMinimumLengthFilterGuards (dfg/DFGSpeculativeJIT64.cpp).
  • The RegExp and the argument are the same value there, so the DFG gives both operands of RegExpTest the same register. The assertion did not permit that.
  • Release builds have no assertions and the generated code is correct, so they were not affected.
  • main has the fix since Upgrade WebKit to 7b485a76e9 #43882. It is upstream WebKit d22fa241f1 (https://commits.webkit.org/321146@main, https://bugs.webkit.org/show_bug.cgi?id=324211). Bun has no test for it.

Fix

  • This PR adds a test only: the fixture regexp-test-regexp-is-also-the-argument.js in test/js/bun/jsc-stress, and its line in jsc-stress.test.ts.
  • The PR has no change under src/, and it does not change the WebKit pin.
  • Verified on a debug build of this branch (WebKit f20ce7744553, the pin on main): the fixture passes, and all 117 tests of jsc-stress.test.ts pass.
  • Fail-before: a debug build of 37da174d50, the commit on main before Upgrade WebKit to 7b485a76e9 #43882 (WebKit 35e8970dfd92), fails the fixture with exit code 134 and the assertion above.

Background

Notes

Smallest repro for a debug build without the fix:

function test(regExp) { return regExp.test(regExp); }
const regExp = /^abc$/;
for (let i = 0; i < 100000; i++) test(regExp);

How it was found: fuzzer runs of the debug build. The fuzzer reported this assertion six times, with three shapes of script.

The RegExp is a constant of the function. This shape was flaky:

const v0 = /za\S\p{gc=Nd}a|bqja*b|c/g;
function f1() {
    v0.test(v0);
    return f1;
}
v0.valueOf = f1;
function f3() {
    return v0;
}
v0.toString = f3;
const v4 = [-2.2250738585072014e-308,-0.0,-9.32419020495346e+307,1.3436572898162654e+308,1000.0,2.220446049250313e-16];
try { v4.slice(v0); } catch (e) {}

The RegExp is a closure variable, or a literal in the function. These shapes failed on every run in a fresh process:

function F0(...a2) {
    if (!new.target) { throw 'must be called with new'; }
    const v3 = this.constructor;
    try { new v3(); } catch (e) {}
    const v5 = /za\S\p{gc=Nd}a|bqja*b|c/g;
    function f6() {
        return v5.test(v5);
    }
    f6();
}
new F0(F0);
function F0(a2, a3) {
    if (!new.target) { throw 'must be called with new'; }
    const v4 = /P(?:ab|cde)(x)(x)(x)\2/iv;
    v4.test(v4);
    const t7 = this.constructor;
    new t7(F0);
}
new F0();

On the debug build of this branch, all three scripts exit cleanly, 3 of 3 runs each. The first script also passes 3 of 3 runs after the earlier script. The last script ends with the expected RangeError: Maximum call stack size exceeded.

The fixture:

  • Its body is JSTests/stress/regexp-test-regexp-is-also-the-argument.js from the upstream commit, byte for byte. The file adds the // @bun line and a --useConcurrentJIT=0 directive above it.
  • Without compiler threads the DFG compile runs on the main thread, so a build without the fix fails at a fixed point and not only when the compiler thread wins a race with process exit.
  • It covers a RegExp that is not a constant, a constant RegExp, a constant RegExp that starts with ^, and a constant sticky RegExp. The first two fail on a build without the fix.

About the fail-before proof:

  • USE_SYSTEM_BUN=1 cannot show it. A release build has no assertions, so the fixture passes there before and after the fix.
  • bun bd --webkit-version=35e8970dfd92 on this branch does not compile, because the bindings on main use a WebKit API that the older commit does not have. So the fail-before build is the commit before Upgrade WebKit to 7b485a76e9 #43882 with this PR's two files on top.
  • The debug jsc shells of the two WebKit releases agree: the one of 35e8970dfd92 aborts on the upstream test file with the assertion, and the one of f20ce7744553 passes it.

[auto-merge] gate passed · iteration 2 · 2 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.3 (367d939d9)

test/js/bun/jsc-stress/jsc-stress.test.ts:
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-arithsin.js [2010.89ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-arithcos.js [2330.36ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-arithsqrt.js [2370.60ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-arithtan.js [2457.59ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-string-equality.js [2254.64ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-string-ident-equality.js [1978.75ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-string-strict-equality.js [3460.96ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-regexp-exec.js [2483.19ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-regexp-test.js [2663.33ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-library-substring.js [3181.28ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-getmyargumentslength-inline.js [1479.43ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-getmyargumentslength.js [2469.55ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-get-my-argument-by-val.js [1744.47ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-get-my-argument-by-val-inlined.js [1640.49ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-get-my-argument-by-val-inlined-and-not-inlined.js [1602.71ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-call-exception-no-catch.js [1452.63ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-try-catch-arith-sub-exception.js [1293.08ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-call-exception.js [1881.50ms]
(pass) JSC JIT Stress Tests > JS (Baseline/DFG/FTL) > ftl-call-varargs-exception.js [2035.72ms]
(pass) JSC JIT Stress Test
... (truncated)
Exit: 0
diff hotspot
.../regexp-test-regexp-is-also-the-argument.js     | 57 ++++++++++++++++++++++
 test/js/bun/jsc-stress/jsc-stress.test.ts          |  1 +
 2 files changed, 58 insertions(+)

gate history · 5 passed · 0 rejected · iteration 2

evidence per changed file
file                                                      reads  edits  tests
…ess/fixtures/regexp-test-regexp-is-also-the-argument.js      0      1     20
test/js/bun/jsc-stress/jsc-stress.test.ts                     1      1     17

@coderabbitai

coderabbitai Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview 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: Essentials

Run ID: 7ae40320-c7ff-4861-94be-37f06fe371b7

📥 Commits

Reviewing files that changed from the base of the PR and between 07303bb and 616336c.

📒 Files selected for processing (1)
  • scripts/build/deps/webkit.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.


Walkthrough

The change pins Bun to a WebKit preview build and adds a JSC stress fixture for RegExp values passed as both the receiver and argument to test(). The fixture is registered in the DFG stress-test suite.

Changes

RegExp validation

Layer / File(s) Summary
WebKit preview build pin
scripts/build/deps/webkit.ts
WEBKIT_VERSION now selects autobuild-preview-pr-654-17b2731a.
RegExp self-argument stress coverage
test/js/bun/jsc-stress/fixtures/regexp-test-regexp-is-also-the-argument.js, test/js/bun/jsc-stress/jsc-stress.test.ts
The fixture checks matching, anchoring, length filtering, global lastIndex, and sticky behavior. The JSC stress suite includes the fixture.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 61633

No actionable issue was established from the available evidence.

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description provides detailed problem, fix, background, and verification information. However, it states that the PR does not change the WebKit pin, while the changeset updates WEBKIT_VERSION. I… Update the description to document the WEBKIT_VERSION change and explain why the preview build is used. Remove the inaccurate statement that the PR has no WebKit pin change. Add or map the content to the required What does this PR do? a…
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy the coding requirements in #654. WEBKIT_VERSION now selects an autobuild-preview-pr-654-* build that contains the upstream DFG register-overlap fix. The new fixture covers non-…
Out of Scope Changes check ✅ Passed The changes stay within #654. The dependency pin provides the required preview WebKit fix. The fixture and suite registration directly exercise the reported RegExp#test assertion. No unrelated chang…
Title check ✅ Passed The title clearly identifies the added JSC stress-test fixture for the DFG regExp.test(regExp) case. It does not mention the WebKit dependency pin update, but it accurately describes the primary tes…
Full details: Description check

Explanation

The description provides detailed problem, fix, background, and verification information. However, it states that the PR does not change the WebKit pin, while the changeset updates WEBKIT_VERSION. It also does not use the template headings exactly, although the required information is mostly present.

Resolution

Update the description to document the WEBKIT_VERSION change and explain why the preview build is used. Remove the inaccurate statement that the PR has no WebKit pin change. Add or map the content to the required What does this PR do? and How did you verify your code works? sections.

  • Fix all pre-merge checks with AI

Warning

Git: CodeRabbit could not clone the repository, so clone-backed analysis was skipped and this review may be incomplete. Verify repository clone access, such as SSH credentials, before requesting another full review. If clone access is intentionally unavailable, use path_filters to narrow the review scope.


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

@robobun

robobun commented Sep 14, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 4:27 AM PT - Sep 27th, 2026

✅ @robobun, your commit 67a0d50ac4ee1f1ff7f5aa5af3e69f496fb21c68 passed in Build #121209! 🎉


🧪   To try this PR locally:

bunx bun-pr 42694

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

bun-42694 --bun

Comment thread scripts/build/deps/webkit.ts Outdated
@robobun

robobun commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator Author

The finding on scripts/build/deps/webkit.ts:6 is correct. This PR must not merge with the preview pin.

The preview pin is there on purpose for now. oven-sh/WebKit#654 is still open, and CI needs a build of that branch to prove the fix.

After oven-sh/WebKit#654 merges, I will move WEBKIT_VERSION to the merge commit on oven-sh/WebKit main. Before I push that change, I will check that the autobuild-<merge sha> release has a package for each platform and flavor that prebuiltSuffix produces, and run test/js/bun/jsc-stress/jsc-stress.test.ts again with it.

@robobun

robobun commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator Author

The fuzzer reported this assertion a second time, about 8 hours after the first report, with a different script. The core of the script is the same:

const v10 = /za\S\p{gc=Nd}a|bqja*b|c/g;
function f11() {
    // ... object setup that does not matter here ...
    v10.test(v10);
    return f11;
}
v10.valueOf = f11;
function f31() { return v10; }
v10.toString = f31;
try { v32.slice(v10); } catch (e) {}

It behaves like the first script. On a debug build with WebKit cf1b36ec8703 it passes in a fresh process, and it fails 3 of 3 runs after the earlier script from oven-sh/WebKit#654. On the build that this PR pins, it passes in a fresh process and passes 3 of 3 runs after that earlier script.

So the fuzzer will keep reporting DFGSpeculativeJIT64.cpp(10399) until the WebKit change lands.

@robobun
robobun force-pushed the robobun/c6613da2/webkit-regexp-test-regexp-is-argument branch from 71b86df to 07303bb Compare September 15, 2026 07:01
@robobun

robobun commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator Author

main moved its WebKit pin to 9b02218df662, so this PR had a conflict on that line, and the old preview was based on the older pin.

I put the commit of oven-sh/WebKit#654 on top of 9b02218df662 (same two-file diff, new head ede7410c) and rebased this branch on main. The pin is now autobuild-preview-pr-654-ede7410c, which is the pin on main plus that one commit.

Checked with a debug build of the rebased branch: the new fixture passes, all 117 tests of jsc-stress.test.ts pass, and the fuzzer scripts exit cleanly. A debug build of main with its own pin still aborts on the fixture with exit code 134.

The earlier review finding still applies. This PR must not merge with a preview pin. I will move the pin to the autobuild-<merge sha> release after oven-sh/WebKit#654 merges.

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

Code review found no new issues

No new issues were found in this update; 1 finding from earlier reviews is still open above.

@robobun
robobun force-pushed the robobun/c6613da2/webkit-regexp-test-regexp-is-argument branch from 07303bb to 616336c Compare September 16, 2026 07:51
@robobun

robobun commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator Author

main moved its WebKit pin again, to d3720d515e14, so this PR had a conflict on that line once more.

I put the commit of oven-sh/WebKit#654 on top of d3720d515e14 (same two-file diff, new head 17b2731a) and rebased this branch on main. The pin is now autobuild-preview-pr-654-17b2731a. The CI of oven-sh/WebKit passed on that head, including every JavaScriptCore test lane.

Checked with a debug build of the rebased branch: the new fixture passes, all 117 tests of jsc-stress.test.ts pass, and the fuzzer scripts exit cleanly (3 of 3 runs each). A debug build of main with its own pin still aborts on the fixture with exit code 134. The assertion is at DFGSpeculativeJIT64.cpp:10401 in that WebKit revision.

Each pin move on main causes this rebase again. A merge of oven-sh/WebKit#654 ends that: the next pin then contains the fix, and this PR can move its pin to a merged commit.

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

Code review found no new issues

No new issues were found in this update; 1 finding from earlier reviews is still open above.

Still open from earlier reviews (1):

  • Unresolved: 1 blocking on lines changed since (possibly already fixed).

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

@robobun

robobun commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator Author

main moved its WebKit pin a third time, to c28156899e5f (with the LLVM 23 upgrade in #42851), so this PR conflicts on the pin line again. That WebKit commit still has the combined assertion.

I will not rebuild the preview for each pin move any more. Each round needs a new WebKit preview build and a full CI run here, and this PR cannot merge on a preview pin anyway. So this PR stays as it is until oven-sh/WebKit#654 merges.

After that merge, the next WebKit upgrade on main contains the fix. I will then rebase this PR. It then only adds the fixture, or it moves the pin to the merged commit if main has not picked it up yet.

oven-sh/WebKit#654 is not affected. It is mergeable and its CI is green.

The fuzzer reported this assertion a sixth time today. The new script has the RegExp literal inside a recursive constructor:

function F0(a2, a3) {
    if (!new.target) { throw 'must be called with new'; }
    const v4 = /P(?:ab|cde)(x)(x)(x)\2/iv;
    v4.test(v4);
    const t7 = this.constructor;
    new t7(F0);
}
new F0();

With WebKit cf1b36ec8703 it aborts 3 of 3 runs in a fresh process. The build that this PR pinned (autobuild-preview-pr-654-17b2731a) ends 3 of 3 runs with the expected RangeError: Maximum call stack size exceeded.

@robobun

robobun commented Sep 20, 2026

Copy link
Copy Markdown
Collaborator Author

Upstream WebKit landed the same change on 2026-09-15: WebKit/WebKit#74030 (https://commits.webkit.org/321146@main). oven-sh/WebKit#654 is now a cherry-pick of that commit on the main of oven-sh/WebKit, with the upstream test file. A later upstream merge does not conflict with it.

Nothing changes for this PR. It still waits for the fix to reach the WebKit pin on main, through oven-sh/WebKit#654 or through an upstream merge that includes 321146@main. Then I rebase it, and it adds only the fixture.

The DFG gives the RegExp and the argument of RegExpTest one register when
both are the same value. Before WebKit 321146@main the minimum-length
filter of RegExp#test asserted that the two registers differ, so a debug
or ASAN build aborted in the DFG compiler.

The fixture is JSTests/stress/regexp-test-regexp-is-also-the-argument.js
from that WebKit commit.
@robobun
robobun force-pushed the robobun/c6613da2/webkit-regexp-test-regexp-is-argument branch from 616336c to 3637080 Compare September 27, 2026 10:13
@robobun robobun changed the title Bump WebKit (oven-sh/WebKit#654 preview): regExp.test(regExp) passes the DFG register assertion in debug and ASAN builds jsc-stress: add a fixture for regExp.test(regExp) in the DFG Sep 27, 2026
@robobun

robobun commented Sep 27, 2026

Copy link
Copy Markdown
Collaborator Author

main has the fix since #43882 ("Upgrade WebKit to 7b485a76e9"), which includes upstream d22fa241f1. So this PR no longer changes the WebKit pin. It now adds only the test: the fixture and its line in jsc-stress.test.ts. I changed the title and the description to match.

The fixture body is now the upstream test file, byte for byte.

What I ran on the rebased branch (3637080d42):

  • Debug build with the pin on main (WebKit f20ce7744553): the fixture passes, and all 117 tests of jsc-stress.test.ts pass.
  • Debug build of 37da174d50, the commit before Upgrade WebKit to 7b485a76e9 #43882 (WebKit 35e8970dfd92), with the same two files: the fixture fails with exit code 134 and the noOverlap assertion.
  • The three fuzzer scripts exit cleanly on the first build, 3 of 3 runs each.

This closes the review finding about the preview pin. I close oven-sh/WebKit#654, because the upstream merge brought the same commit to the fork.

@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 — the diff is now test-only (the preview-tag pin in scripts/build/deps/webkit.ts is gone), and the new jsc-stress fixture is a faithful port that the harness runs as-is.

What was reviewed:

  • Traced every shouldBe in the fixture by hand against regExp.test("/source/flags") semantics (non-anchored, ^...$, .{20} longer than its own source, /g lastIndex 0→4→0, /\/ab/y lastIndex 0→0 and 2→5): all expected values are right, so no assertion is vacuous.
  • Checked the // @ bun + //@ runDefault("--useConcurrentJIT=0") header against parseJSCFlags in jsc-stress.test.ts and noInline/testLoopCount against preload.js — all supported; entry sits in the DFG group of jsFixtures.
  • Could not confirm from this offline checkout that the base pin f20ce774… already contains oven-sh/WebKit#654; if it does not, the debug/ASAN jsc-stress lanes fail loudly rather than pass silently, so CI settles it.
Extended reasoning...

The change adds one 57-line JSC stress fixture under test/js/bun/jsc-stress/fixtures and a one-line registration in the jsFixtures array; no src/, native, or build-config files are touched anymore, so it touches no security-sensitive surface. The earlier blocking finding about pinning WEBKIT_VERSION to an ephemeral preview tag is addressed by the pin change being dropped from the diff. Every assertion in the fixture was verified by hand and the harness directive parsing and preload globals it relies on exist in the checked-in harness. No CODEOWNERS entry covers the changed paths and no third-party change request is visible in the timeline. The only residual uncertainty, whether the base WebKit pin already includes the register-overlap fix, is one that CI's debug and ASAN lanes would surface as a hard failure rather than something a human reviewer could settle by reading the diff.

@robobun

robobun commented Sep 27, 2026

Copy link
Copy Markdown
Collaborator Author

A note on the automated summary above: its description warning refers to the previous head (616336c6e6), which changed the WebKit pin.

The current head, 3637080d42, changes two files, both under test/:

  • test/js/bun/jsc-stress/fixtures/regexp-test-regexp-is-also-the-argument.js (added)
  • test/js/bun/jsc-stress/jsc-stress.test.ts (one line)

scripts/build/deps/webkit.ts is not in the diff. WEBKIT_VERSION is f20ce7744553c910bcf16a33faf976af208de091 on this branch and on main. So the description is correct when it says that this PR does not change the pin.

This branch has not been deployed

No deployments
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