Skip to content

Fix stale-structure abort when a lazy Bun property builder throws mid-lookup - #37001

Open
robobun wants to merge 7 commits into
mainfrom
farm/f76ebd86/fix-stale-structure-getpropertyslot
Open

robobun wants to merge 7 commits into
mainfrom
farm/f76ebd86/fix-stale-structure-getpropertyslot

Conversation

@robobun

@robobun robobun commented Aug 5, 2026 •

Copy link
Copy Markdown
Collaborator

What

Fuzzilli kept hitting a flaky abort on debug builds (fingerprint StructureInlinesLight.h(56)):

ASSERTION FAILED: isCompilationThread() || Thread::mayBeGCThread() || object->structure() == this
JSValue JSC::Structure::storedPrototype(const JSObject *) const

The fuzzer sample tripped it while reading every property on the Bun object, which first-touches all of its lazy static-table properties.

Root cause

The inlined prototype-chain loop in JSObject::getPropertySlot (JSObject.h) reads the object's Structure* once, calls getOwnNonIndexPropertySlot, and on a miss uses that same pointer for structure->storedPrototype(object). A miss is not side-effect free for static-table objects: reifying a lazy PropertyCallback property transitions the object, and with our fork's throwing builders, setUpStaticFunctionSlot can return false after the transition happened (the builder threw, so the slot is reported as not found and the caller observes the exception). The prototype step then runs on the stale pre-transition structure. Release builds read the prototype off the old structure, which happens to be harmless for mono-proto objects, so only asserts builds crash.

Deterministic trigger: a first read of Bun.sql evaluates the sql internal modules, whose class SQLError extends Error reads Error.prototype at module scope. Hooking that with a proxy that first touches another lazy Bun property (transitioning the Bun object) and then throws reproduces the abort every time, on current main included:

let phase = 0;
globalThis.Error = new Proxy(function () {}, {
  get(target, key, receiver) {
    if (key === "prototype" && phase === 0) {
      phase = 1;
      Bun.semver;
      throw "boom";
    }
    return Reflect.get(target, key, receiver);
  },
});
try { Bun.sql; } catch (e) {}

Most of the fuzzer's later samples reached the same assert by a second route that did not involve a transition of the object being read: the sql builders in BunObject.cpp had a debug-only reportUncaughtExceptionAtEventLoop call that ran while the builder's exception was still pending (stack overflow next to the Bun.sql read, or a broken global Symbol), and that report's read of process._fatalException reified a property that was then counted as a miss, so the prototype step asserted on the process object. This PR originally removed that report as well; #29642 has since landed the same removal on main with its own test, so that hunk dropped out of this branch on rebase and those sample shapes no longer abort on main. What is left here is the engine half, which main still needs: any builder that re-enters the object being read and then throws (the proxy above; #29642's test also notes a Windows-only case of it, where building process.env inside a throwing read reifies another Bun property) still aborts with main's current pin.

Fix

WebKit (oven-sh/WebKit#390, pinned here as its preview autobuild): reload the structure before the prototype step in JSObject::getPropertySlot. The megamorphic slow paths in JITOperations.cpp already do exactly this reload, with the comment "Reload it again since static-class-table can cause transition", and getNonIndexPropertySlot uses getPrototypeDirect() which re-reads it. This was the one caller left reusing the stale pointer. The change is 4 lines and has been carried unchanged across every pin bump since it was written.

Verification

Landing sequence

The autobuild-preview-pr-390-* pin exists so CI on this branch can fetch the patched WebKit. It must not reach main: GitHub deletes the preview release once oven-sh/WebKit#390 merges or closes (scripts/build/download.ts documents exactly this failure mode), which would leave main commits that 404 on vendor fetch. So the order is: land oven-sh/WebKit#390 first, then swap WEBKIT_VERSION here to the merged sha (the durable autobuild-<sha> release, after its prebuilt assets exist for every platform and flavor), then merge this PR. The swap should also delete the comment block above WEBKIT_VERSION in scripts/build/deps/webkit.ts, which only describes the preview pin.


[policy-decision:webkit] gate passed · iteration 25 · 2 files touched

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

Debug/ASAN (expected pass):
$ bun bd test 'test/js/bun/util/BunObject.test.ts'
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test test/js/bun/util/BunObject.test.ts
bun test v1.4.3 (ebea65e6c)

test/js/bun/util/BunObject.test.ts:
(pass) hasNonReifiedStatic [391.94ms]
(pass) lazy property builder that transitions Bun and throws does not abort [707.01ms]
(pass) require('bun') [7.23ms]
Module {
  $: [Function: BunShell],
  Archive: [class Archive],
  ArrayBufferSink: [class ArrayBufferSink],
  CSRF: {
    generate: [Function: generate],
    verify: [Function: verify],
  },
  Cookie: [class Cookie],
  CookieMap: [class CookieMap],
  CryptoHasher: [class CryptoHasher],
  FFI: {
    viewSource: [Function: viewSource],
    dlopen: [Function: dlopen],
    callback: [Function: callback],
    linkSymbols: [Function: linkSymbols],
    toBuffer: [Function: toBuffer],
    toArrayBuffer: [Function: toArrayBuffer],
    closeCallback: [Function: closeCallback],
    cfunction: [Function: cfunction],
    CString: [class CString],
    ptr: [Function: ptr],
    read: {
      u8: [Function: u8],
      u16: [Function: u16],
      u32: [Function: u32],
      ptr: [Function: ptr],
      i8: [Function: i8],
      i16: [Function: i16],
      i32: [Function: i32],
      i64: [Function: i64],
      u64: [Function: u64],
      intptr: [Function: intptr],
      f32: [Function: f32],
      f64: [Function: f64],
    },
  },
  FetchSession: [class FetchSession],
  FileSystemRouter: [class FileSystemRouter],
  Glob: [class Glob],
  Image: [class Image],
  JSON5: {
    parse: [Function: parse],
    stringify: [Function: stringify],
  },
  JSONC: {
    parse: [Function: parse],
  },
  JSONL: JSONL {
    parse: [Function: parse],
    parseChunk: [Function: parseChunk],
  },
  MD4: [class MD4],
  MD5: [class MD5],
  ModuleGraph: [class ModuleGraph],
  RedisClient: [class RedisClient],
  S3Client: [class S3Client],
  SHA1: [class SHA1],
  SHA224: [class SHA224],
  SHA256: [class SHA256],
  SHA384: [class SHA384],
  SHA512: [class SHA512],
  S
... (truncated)
Exit: 0
diff hotspot
scripts/build/deps/webkit.ts       |  7 ++++-
 test/js/bun/util/BunObject.test.ts | 58 +++++++++++++++++++++++++++++++++-----
 2 files changed, 57 insertions(+), 8 deletions(-)

gate history · 28 passed · 3 rejected · iteration 25

evidence per changed file
file                                reads  edits  tests
scripts/build/deps/webkit.ts           12     12    112
test/js/bun/util/BunObject.test.ts      7      9     92

@github-actions github-actions Bot added the claude label Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review.

Walkthrough

Changes

The PR updates the WebKit preview version and adds subprocess coverage for lazy Bun.sql evaluation during Bun re-entry.

Bun SQL lazy property exception handling

Layer / File(s) Summary
WebKit assertion fix
scripts/build/deps/webkit.ts
WEBKIT_VERSION now uses the WebKit PR 390 preview build. Comments document the related assertion fix.
Bun SQL exception handling and regression test
test/js/bun/util/BunObject.test.ts
The test harness imports bunEnv and bunExe. A subprocess test covers lazy Bun.sql evaluation, Error.prototype lookup, captured output, empty stderr, and successful exit. The existing lazy builtin-failure test now starts directly by replacing Symbol.

Suggested reviewers: jarred-sumner

Merge Risk: Low · up to 8b5d2

The runtime change is localized and covered by regression tests, but the subprocess regression test should assert its output before checking the child exit so failures cannot be masked; the PR is mergeable with owner follow-up.

Pre-merge checks: Passed 4

Check name Status Explanation
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.
Title check Passed The title clearly and concisely describes the main fix: preventing stale-structure aborts when a lazy Bun property builder throws during lookup.
Description check Passed The description explains the problem, root cause, fix, verification results, scope, and landing sequence. Although it uses "What" and "Verification" instead of the exact template headings, it provides…
Full details: Description check

Explanation

The description explains the problem, root cause, fix, verification results, scope, and landing sequence. Although it uses "What" and "Verification" instead of the exact template headings, it provides the required information and is substantially complete.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

ℹ️ Recent review info

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: aa6a4a29-f7fa-4695-976c-256b753d63c8

📥 Commits

Reviewing files that changed from the base of the PR and between 2ca8cf7 and 8b5d2fc.

📒 Files selected for processing (1)

  • scripts/build/deps/webkit.ts

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

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. Fix segfault when a lazy Bun.* getter throws during reification #33211 - Deletes the exact same #if BUN_DEBUG reportUncaughtExceptionAtEventLoop blocks in defaultBunSQLObject/constructBunSQLObject and adds a regression test to the same test/js/bun/util/BunObject.test.ts, for the same crash when a lazy Bun.* property builder throws.
  2. Fix Bun.inspect null deref with Proxy prototypes; stop reporting from inside the sql lazy-property builders #30245 - Rewrites the same two SQL lazy-property builders and removes the identical #if BUN_DEBUG blocks to stop a throwing lazy Bun initializer from causing a null-JSCell deref/abort.
  3. worker_threads: don't abort when terminate() interrupts a lazy property builder #33418 - Edits the same defaultBunSQLObject/constructBunSQLObject builders so an exception cannot escape into reifyStaticProperty — same functions and failure mode, triggered by worker.terminate() instead of a user throw.

🤖 Generated with Claude Code

@robobun

robobun commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Those three are earlier passes at the same crash family, each working around it on the Bun side only: #33211 wraps the lazy builders so a throw is cleared and reified as undefined, #30245 rewrites the SQL builders (plus an unrelated Bun.inspect fix) to avoid the null JSCell deref, and #33418 covers the worker.terminate() interruption variant.

This PR fixes the engine-level gap those were steering around. JSObject::getPropertySlot kept using the structure it read before getOwnNonIndexPropertySlot, and a static-table reification that transitions the object and then misses (the builder threw) leaves that pointer stale, which is the assert the fuzzer keeps hitting. That fix is oven-sh/WebKit#390, pinned here as its preview autobuild. With it in place a throwing builder is a legal state: the exception propagates to the caller like any other failed property read, so no swallowing is needed. The only textual overlap with #33211 is the two deleted BUN_DEBUG lines in the SQL builders; the rest is disjoint.

Comment thread test/js/bun/util/BunObject.test.ts Resolved
@robobun

robobun commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Good catch on the undrained stderr pipe, fixed in 2564ffa: the test now drains both pipes and asserts the combined { stdout, stderr, exitCode } object, so a regression surfaces the assertion text instead of just a wrong exit code.

On CI: the red builds so far are all the vendor fetch step 404ing on the pinned WebKit preview tag, which publishes when oven-sh/WebKit#390's autobuild finishes. The next push or retry after that lands should build normally.

@robobun

robobun commented Aug 5, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 10:29 PM PT - Nov 10th, 2026

❌ @robobun, your commit f453ac3 has 1 failures in Build #139971 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 37001

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

bun-37001 --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.

Thanks for addressing the stderr-drain nit — the test now asserts the combined { stdout, stderr, exitCode } object. I didn't find any further issues in this revision, but this needs a human reviewer: the load-bearing fix is a WebKit engine change (oven-sh/WebKit#390) pinned here via a preview autobuild tag, and there are three overlapping open PRs (#33211, #30245, #33418) targeting the same crash family that need reconciling.

What was reviewed

  • defaultBunSQLObject / constructBunSQLObject: removing the BUN_DEBUG-only reportUncaughtExceptionAtEventLoop is correct — it consumed the pending exception so RETURN_IF_EXCEPTION fell through to a null-JSValue .getObject().
  • prebuiltDestDir / prebuiltUrl in webkit.ts handle the autobuild- prefixed tag correctly (no double-prefix, cache dir keyed on the full tag).
  • New test: subprocess pipes drained, phase: 1 assertion guards that the trigger path is still exercised.
Extended reasoning...

Overview

This PR fixes a debug-build assertion abort (ASSERT(object->structure() == this) in Structure::storedPrototype) that the fuzzer hits when a lazy Bun.* static-table property builder transitions the Bun object and then throws mid-getPropertySlot. Three files touched:

  • scripts/build/deps/webkit.ts: bumps WEBKIT_VERSION from a commit sha to autobuild-preview-pr-390-c3a156ea, a preview build of oven-sh/WebKit#390. That WebKit PR is the actual engine fix — it reloads the object's structure before the prototype step in JSObject::getPropertySlot.
  • src/jsc/bindings/BunObject.cpp: deletes two #if BUN_DEBUG blocks in defaultBunSQLObject / constructBunSQLObject that called reportUncaughtExceptionAtEventLoop after requireId. That call consumed the pending exception, so RETURN_IF_EXCEPTION fell through and sqlValue.getObject() ran on an empty JSValue.
  • test/js/bun/util/BunObject.test.ts: adds a spawned-subprocess regression test using a Proxy on globalThis.Error to make the Bun.sql builder re-enter the Bun object (via Bun.semver) and throw.

Security risks

None identified. The BunObject.cpp change removes debug-only diagnostic code; release builds are unchanged there. The test is hermetic (subprocess with bunEnv, no network). The WebKit change itself is out of scope for this diff.

Level of scrutiny

High, and beyond what I can sign off on:

  1. The core fix is a WebKit engine change in a separate repository. This diff only pins a preview autobuild tag; the actual JSObject::getPropertySlot modification isn't reviewable here. A JSC change to the property-lookup fast path affects every property read in the runtime and warrants maintainer review of the upstream PR.
  2. The pin is to a preview build of an unmerged WebKit PR, not a merged commit. A human should confirm this is the intended workflow (vs. waiting for oven-sh/WebKit#390 to merge and pinning the resulting sha).
  3. Three open PRs (#33211, #30245, #33418) attack the same crash family with Bun-side workarounds. The author's response explains why this engine-level fix supersedes them, but a maintainer needs to decide which to close.

Other factors

  • My prior review (the undrained-stderr nit) was addressed in 2564ffa; the test now drains both pipes and asserts the combined object.
  • I checked prebuiltUrl / prebuiltDestDir in webkit.ts for the new tag format: prebuiltUrl already handles the autobuild- prefix (no double-prefixing), and prebuiltDestDir uses the full tag when it starts with autobuild-, so the cache-dir key is correct.
  • The BunObject.cpp deletion is straightforward and matches the release-build code path exactly — no behavioral change outside BUN_DEBUG.
  • The PR description explicitly notes the test passes under USE_SYSTEM_BUN=1 by design (release builds never fired the assert). That's an acknowledged exception to the usual rule since the guarded failure is a debug-only assert.
  • CI was still building at the time of this review (initial runs 404'd on the not-yet-published preview tag).

@robobun

robobun commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

On the preview-pin question: the plan is the usual two-step. This PR pins the preview tag so CI exercises the exact JSC change; once oven-sh/WebKit#390 merges, the pin moves to the merge commit sha (either here before landing or as the routine WebKit upgrade that follows). The preview tag stays downloadable either way.

CI at 0a3a39f: 187 of 196 jobs passed so far. The only failures are the two debian x64-asan test lanes, both from tests unrelated to this diff: worker-transfer-terminate-stress.test.ts (a known intermittent terminate-during-transfer abort, documented in that test's own header and the subject of #33418) and AsyncLocalStorage-tracking.test.ts (pre-existing RSA keygen leak, also failing on main). Both are reported for main-side triage.

@robobun

robobun commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Fuzzing hit the same assert through a different trigger, minimized to:

globalThis.Symbol = undefined;
try { Bun.sql } catch {}

Clobbering Symbol makes the BunSql module's setup throw (Symbol("resolve") evaluates with Symbol undefined) while the builder runs, and the Bun object's structure transitions before the exception unwinds, so the lookup aborts with the identical storedPrototype assert at StructureInlinesLight.h(56) on a main debug build (reproduced at 0ffabf6).

Verified against this branch with the pinned WebKit preview: the one-liner exits 0 with the TypeError caught. Unlike the Error.prototype hook in the regression test, this trigger does not depend on the sql module reading Error.prototype at evaluation time, so it could serve as a second case if that ever changes.

@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 found no bugs; the earlier stderr-drain nit was addressed. Because it pins the entire runtime to an unmerged WebKit preview build (oven-sh/WebKit#390) and overlaps with three earlier PRs on the same crash family, a human should sign off on the landing sequence.

What was reviewed:

  • BunObject.cpp: verified removing reportUncaughtExceptionAtEventLoop leaves RETURN_IF_EXCEPTION as the exception path — no other callers of scope between requireId and the check.
  • webkit.ts: the autobuild- prefix handling in prebuiltUrl/prebuiltDestDir already accommodates the new tag format.
  • Test: subprocess drains all pipes, asserts combined object, and phase: 1 guards the trigger staying live.
Extended reasoning...

Overview

Three files: scripts/build/deps/webkit.ts swaps the WebKit pin from commit sha ddea7131… to preview tag autobuild-preview-pr-390-01c983d8; src/jsc/bindings/BunObject.cpp deletes two #if BUN_DEBUG blocks that called reportUncaughtExceptionAtEventLoop (which consumed the pending exception, letting RETURN_IF_EXCEPTION fall through to a null-deref on sqlValue.getObject()); test/js/bun/util/BunObject.test.ts adds a spawned regression test using an Error.prototype proxy trap to transition the Bun object mid-lookup and throw.

Security risks

None identified. The C++ change deletes debug-only code and makes the debug exception path match release. The test is a spawned subprocess with fixed input. The WebKit pin points to a build produced by oven-sh's own release workflow.

Level of scrutiny

High. The load-bearing fix is in a separate repository (oven-sh/WebKit#390, a change to JSObject::getPropertySlot — one of the hottest paths in the engine) and cannot be reviewed from this diff. Pinning WEBKIT_VERSION to a preview of an unmerged PR is a dependency-bump decision the repo's landing-prs guidance calls out for maintainer attention, and the author explicitly describes a two-step plan (preview pin now, move to merge sha later) that a human should confirm. Per REVIEW.md, the test also passes under USE_SYSTEM_BUN=1 by the author's own account — the justification (guards debug/ASAN lanes only, since release never fired the assert) is reasonable but is a maintainer call.

Other factors

Three open PRs (#33211, #30245, #33418) target the same crash family with Bun-side workarounds; this PR supersedes them at the engine layer, so someone should decide which lands and close the rest. The BunObject.cpp deletion is small and clearly correct (reportUncaughtExceptionAtEventLoop clears the exception, so the following RETURN_IF_EXCEPTION becomes a no-op and control reaches .getObject() on an empty JSValue). The regression test now follows harness conventions after the earlier feedback. CI at 0a3a39f passed 187/196 with the two failures documented as pre-existing on main.

@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 earlier stderr-drain nit was addressed in b8619cc). Because the load-bearing fix is a WebKit version bump to an unmerged preview build of oven-sh/WebKit#390 — an engine-level change to JSObject::getPropertySlot that isn't visible in this diff — a maintainer sign-off on the JSC change and the preview-pin landing plan is still warranted.

What was reviewed:

  • BunObject.cpp: the deleted BUN_DEBUG blocks — confirmed reportUncaughtExceptionAtEventLoop consumes the pending exception, so removal restores correct RETURN_IF_EXCEPTION propagation and matches release-build behavior.
  • Regression test: subprocess drains both pipes and asserts the combined { stdout, stderr, exitCode }; phase: 1 guard makes the test self-invalidating if the sql module stops reading Error.prototype.
  • webkit.ts: the autobuild- tag prefix is already handled by prebuiltUrl/prebuiltDestDir, so the pin format is compatible with the existing download/cache-key logic.
Extended reasoning...

Overview

Three files touched: scripts/build/deps/webkit.ts bumps WEBKIT_VERSION from a merged commit sha to an unmerged preview tag autobuild-preview-pr-390-1394aa19; src/jsc/bindings/BunObject.cpp deletes two #if BUN_DEBUG blocks in defaultBunSQLObject/constructBunSQLObject that called reportUncaughtExceptionAtEventLoop before RETURN_IF_EXCEPTION; test/js/bun/util/BunObject.test.ts adds a spawned-subprocess regression test. The substantive fix — reloading the structure before the prototype step in JSObject::getPropertySlot — lives entirely in oven-sh/WebKit#390 and is not part of this diff.

Security risks

None identified. The deleted code was debug-only diagnostic reporting; the WebKit change is a correctness fix on the property-lookup slow path that affects debug-assert behavior for mono-proto objects. No auth, crypto, or untrusted-input handling is touched.

Level of scrutiny

High, because the real change is a JavaScriptCore engine modification to one of its hottest inline paths (JSObject::getPropertySlot). That change is only referenced here as a version pin — reviewing this PR alone cannot validate the JSC diff, its perf impact, or whether it interacts correctly with other prototype-chain callers. Per the repo's own guidance, dependency/vendoring bumps and WebKit upgrades warrant maintainer review. The Bun-side changes (6-line deletion, new test) are simple and would be approvable in isolation.

Other factors

  • The pin targets a preview autobuild of an unmerged WebKit PR. The author's stated plan is the usual two-step (land on preview, then re-pin to the merge sha), but that is a process decision a maintainer should confirm before this hits main.
  • Three overlapping open PRs (#33211, #30245, #33418) work around the same crash family on the Bun side; landing this one implies closing/rebasing those, which a maintainer should coordinate.
  • The bug-hunting system found no issues this run; my only prior comment (undrained stderr pipe) was fixed and the thread is resolved.
  • CI at 0a3a39f was mostly green with two ASAN-lane failures the author attributes to pre-existing flakes on main; the latest commit 80b355b is a CI retrigger for the now-published preview build.

@robobun
robobun force-pushed the farm/f76ebd86/fix-stale-structure-getpropertyslot branch from 80b355b to 9da6a46 Compare August 10, 2026 10:20

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/build/deps/webkit.ts`:
- Around line 6-11: Update WEBKIT_VERSION to the commit SHA produced when WebKit
PR `#390` merges, replacing the preview tag so local and prebuilt builds resolve
the same revision; do not add a process.versions assertion.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 38d53589-06a1-401a-a579-55a9821c4fbb

📥 Commits

Reviewing files that changed from the base of the PR and between 80b355b and 9da6a46.

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

Comment thread scripts/build/deps/webkit.ts Outdated
Comment on lines +6 to +11
// Preview of oven-sh/WebKit#390 (rebased on 447082ab, the previous pin here):
// JSObject::getPropertySlot reloads the structure before the prototype step, so
// a static-table lazy property builder that transitions the object and then
// throws no longer trips ASSERT(object->structure() == this) in
// Structure::storedPrototype.
export const WEBKIT_VERSION = "autobuild-preview-pr-390-0c51423a";

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.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- dependency file ---'
cat -n scripts/build/deps/webkit.ts

printf '%s\n' '--- WebKit version usage ---'
rg -n -C 3 'WEBKIT_VERSION|process\.versions' --glob '*.{ts,tsx,js,jsx}' .

printf '%s\n' '--- repository guidance ---'
if [ -f REVIEW.md ]; then
  cat -n REVIEW.md
fi

Repository: oven-sh/bun

Length of output: 50368


🌐 Web query:

GitHub oven-sh/WebKit pull request 390 current status merged commit

💡 Result:

There is no publicly available information indicating that pull request #390 exists in the oven-sh/WebKit repository as of August 10, 2026. Review of the repository's activity, merged pull requests, and associated release tags shows recent pull requests such as #370 and others, but no PR #390 [1][2][3]. Results related to "PR 390" in other contexts refer to unrelated items, such as automotive engine parts, consumer products, or pull requests in different repositories [4][5][6][7][8][9][10][11].

Citations:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- dependency-version assertion ---'
sed -n '45,75p' scripts/build/depVersionsHeader.ts
sed -n '565,600p' test/js/node/process/process.test.js

printf '%s\n' '--- preview-tag handling ---'
sed -n '275,300p' scripts/build/download.ts
sed -n '1,50p' scripts/sync-webkit-source.ts

printf '%s\n' '--- upstream PR status ---'
curl -fsSL -H 'Accept: application/vnd.github+json' \
  https://api.github.com/repos/oven-sh/WebKit/pulls/390 |
  jq '{number,state,merged_at,merge_commit_sha,head:.head.sha,base:.base.ref,html_url}'

printf '%s\n' '--- preview release and tag ---'
curl -sS -o /tmp/webkit-release.json -w 'release_http=%{http_code}\n' \
  https://api.github.com/repos/oven-sh/WebKit/releases/tags/autobuild-preview-pr-390-0c51423a
if [ -s /tmp/webkit-release.json ]; then
  jq '{tag_name,name,draft,prerelease,published_at,target_commitish,html_url}' /tmp/webkit-release.json
fi

curl -sS -o /tmp/webkit-ref.json -w 'ref_http=%{http_code}\n' \
  https://api.github.com/repos/oven-sh/WebKit/git/ref/tags/autobuild-preview-pr-390-0c51423a
if [ -s /tmp/webkit-ref.json ]; then
  jq '{ref,object}' /tmp/webkit-ref.json
fi

Repository: oven-sh/bun

Length of output: 6565


🏁 Script executed:

#!/usr/bin/env bash
set -u

repo=https://github.com/oven-sh/WebKit.git
tag=autobuild-preview-pr-390-0c51423a

printf '%s\n' '--- remote preview tag ---'
git ls-remote "$repo" "refs/tags/$tag" "refs/tags/$tag^{}" 2>&1
printf 'tag_status=%s\n' "$?"

printf '%s\n' '--- remote PR refs ---'
git ls-remote "$repo" \
  "refs/pull/390/head" \
  "refs/pull/390/merge" 2>&1
printf 'pr_ref_status=%s\n' "$?"

printf '%s\n' '--- WebKit-specific version assertions and macros ---'
rg -n -i -C 3 'webkit|WEBKIT' \
  test/js/node/process/process.test.js \
  src \
  scripts/build/depVersionsHeader.ts \
  scripts/build/deps/webkit.ts \
  scripts/build/bun.ts \
  2>/dev/null | head -n 240

Repository: oven-sh/bun

Length of output: 17974


🏁 Script executed:

#!/usr/bin/env bash
set -u

printf '%s\n' '--- pull request page markers ---'
curl -L -sS -A 'Mozilla/5.0' https://github.com/oven-sh/WebKit/pull/390 |
  tr '\n' ' ' |
  grep -oE '.{0,100}(Open|Closed|Merged|pull request|PR `#390`).{0,160}' |
  head -n 20
printf 'page_status=%s\n' "$?"

printf '%s\n' '--- preview release asset status ---'
for asset in \
  bun-webkit-linux-amd64.tar.gz \
  bun-webkit-linux-amd64-debug.tar.gz \
  bun-webkit-macos-amd64.tar.gz; do
  url="https://github.com/oven-sh/WebKit/releases/download/autobuild-preview-pr-390-0c51423a/$asset"
  status="$(curl -L -sS -o /dev/null -w '%{http_code}' "$url")"
  printf '%s %s\n' "$status" "$asset"
done

printf '%s\n' '--- immutable commit existence ---'
for sha in 447082ab6897278727b44e1ba3c326ae6e1504c3 0c51423a042b9845a221bce1a6fb3a67a6b2afac eff835bf690f7a114669a4321c8a9a1323e5bbd2; do
  git ls-remote https://github.com/oven-sh/WebKit.git "$sha^{commit}" 2>&1
done

Repository: oven-sh/bun

Length of output: 4872


Replace the preview pin before merging. The preview tag resolves to the previous commit 447082ab..., while prebuilt assets use PR head 0c51423a...; local and prebuilt modes therefore use different revisions. After PR #390 merges, set WEBKIT_VERSION to its merged commit SHA. No matching hard-coded process.versions assertion exists.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/build/deps/webkit.ts` around lines 6 - 11, Update WEBKIT_VERSION to
the commit SHA produced when WebKit PR `#390` merges, replacing the preview tag so
local and prebuilt builds resolve the same revision; do not add a
process.versions assertion.

Sources: Coding guidelines, Learnings, MCP tools

@robobun

robobun commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

On the webkit.ts line comment: the preview tag is deliberate while oven-sh/WebKit#390 is still open, there is no merge sha to pin yet. Once that PR lands, the pin moves to the resulting commit on WebKit main, as described in the landing plan in the PR description. In the meantime the tag resolves identically in both modes: it is a release tag on the PR head (0c51423a, a rebase onto the current pin 447082ab), which is the exact revision the prebuilt archives were built from.

Comment thread scripts/build/deps/webkit.ts Outdated
Comment on lines +6 to +11
// Preview of oven-sh/WebKit#390 (rebased on 447082ab, the previous pin here):
// JSObject::getPropertySlot reloads the structure before the prototype step, so
// a static-table lazy property builder that transitions the object and then
// throws no longer trips ASSERT(object->structure() == this) in
// Structure::storedPrototype.
export const WEBKIT_VERSION = "autobuild-preview-pr-390-0c51423a";

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-390-0c51423a, an ephemeral preview tag for an unmerged oven-sh/WebKit PR. Per the repo's Dependencies & vendoring rule (.claude/docs/landing-prs.md:47, "Never merge a pin to an ephemeral artifact … swap to the merged upstream SHA … before merge"), and per scripts/build/download.ts's own error handling ("GitHub deletes the preview release when the PR merges or closes, so every build 404s at once"), this must be swapped to the merged oven-sh/WebKit#390 commit SHA before this PR lands — deferring to "the routine WebKit upgrade that follows" leaves a range of main commits that fail vendor-fetch on any cold cache or git bisect once the preview release is pruned.

Extended reasoning...

What the issue is. scripts/build/deps/webkit.ts:11 sets WEBKIT_VERSION = "autobuild-preview-pr-390-0c51423a". This is an autobuild-preview-pr-* tag — a preview build for an as-yet-unmerged oven-sh/WebKit pull request — not a merged upstream commit SHA. prebuiltUrl() in the same file (line ~85) constructs the download URL directly from this constant: https://github.com/oven-sh/WebKit/releases/download/autobuild-preview-pr-390-0c51423a/bun-webkit-<os>-<arch><suffix>.tar.gz, and prebuiltDestDir() keys the extraction cache on it.

Why this is a problem. The repo's own review guidance is explicit. .claude/docs/landing-prs.md:47 (Dependencies & vendoring), which REVIEW.md designates as the situational rule for dependency bumps, states verbatim:

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 tag / unmerged-PR build" the rule prohibits. The codebase itself documents the concrete failure mode — scripts/build/download.ts:277-295 has a dedicated error handler for exactly this case:

A missing prebuilt tarball is a bad pin, not a network blip. The autobuild-preview-pr-* WebKit tags are the sharp edge: GitHub deletes the preview release when the PR merges or closes, so every build 404s at once.

and the accompanying hint tells the user to "Set WEBKIT_VERSION in scripts/build/deps/webkit.ts to the merged main sha".

Step-by-step failure path.

  1. This PR merges to main with WEBKIT_VERSION = "autobuild-preview-pr-390-0c51423a".
  2. JSObject::getPropertySlot: reload the structure before the prototype step WebKit#390 later merges (or is closed / force-pushed). Per the download.ts comment, GitHub deletes the autobuild-preview-pr-390-0c51423a release.
  3. A developer does a fresh clone, or a CI runner starts with a cold cache, or someone runs git bisect and lands on any main commit in the range [this PR's merge, the follow-up WebKit bump).
  4. The build's vendor-fetch step calls prebuiltUrl(cfg) → GET .../releases/download/autobuild-preview-pr-390-0c51423a/bun-webkit-linux-amd64-debug.tar.gz → HTTP 404.
  5. prebuiltDownloadError() throws BuildError: WebKit preview release is gone. The build fails before compilation begins. There is no fallback — the only remedy is manually editing WEBKIT_VERSION on that historical commit, which defeats bisect.

Why the timeline comment doesn't resolve this. The author's comment on 2026-08-06 says the pin will move to the merge commit SHA "either here before landing or as the routine WebKit upgrade that follows; the preview tag stays downloadable either way". The repo rule does not permit the second option: it requires the swap before merge, and the download.ts comment directly contradicts "stays downloadable either way" — preview releases are deleted when the PR merges or closes. Pinning the preview during CI iteration is fine; landing it on main is what the rule forbids.

Fix. Wait for oven-sh/WebKit#390 to merge, then before landing this PR update WEBKIT_VERSION to the merged 40-hex commit SHA on oven-sh/WebKit main (the durable autobuild-<sha> release), verify the prebuilt tarball exists for every platform × flavor combination prebuiltSuffix() produces, and drop the now-obsolete "Preview of oven-sh/WebKit#390" comment above the constant.

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.

The pin was bumped but is still ephemeral: WEBKIT_VERSION is now "autobuild-preview-pr-390-5242a091", another autobuild-preview-pr-* tag for the same unmerged oven-sh/WebKit#390. The concern stands unchanged — per .claude/docs/landing-prs.md:47 and the prebuiltDownloadError() handling in scripts/build/download.ts, the preview release is deleted when #390 merges or closes, so any main commit carrying this pin will 404 on a cold cache or during bisect. Before landing, swap WEBKIT_VERSION to the merged 40-hex commit SHA on oven-sh/WebKit main (the durable autobuild-<sha> release) and drop the "Preview of oven-sh/WebKit#390" 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.

The pin was bumped to autobuild-preview-pr-390-168fe18a, but that is still an autobuild-preview-pr-* preview tag for the unmerged oven-sh/WebKit#390, so the original concern stands unchanged: once #390 merges or closes, GitHub deletes this release and every cold-cache build or git bisect onto this range 404s in prebuiltDownloadError(). Before landing, WEBKIT_VERSION needs to be the 40-hex merged main SHA from oven-sh/WebKit (the durable autobuild-<sha> release), not another preview suffix.

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.

The pin at scripts/build/deps/webkit.ts:11 moved to autobuild-preview-pr-390-70ecb3b8, but that is still an autobuild-preview-pr-* tag for the unmerged oven-sh/WebKit#390, so the same failure applies: once the PR merges or closes, GitHub drops the preview release and every cold-cache build or bisect landing on this commit 404s in vendor-fetch. Rebasing the preview onto a newer base does not change its lifetime. To land this, wait for oven-sh/WebKit#390 to merge and set WEBKIT_VERSION to the resulting 40-hex commit SHA on oven-sh/WebKit main (the durable autobuild-<sha> release), then confirm the prebuilt tarballs exist for every os/arch/flavor before merging.

@robobun

robobun commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

The review comment on the webkit.ts pin is right, and it corrects my earlier reply: the preview tag does not stay downloadable after the WebKit PR merges. scripts/build/download.ts documents that GitHub deletes autobuild-preview-pr-* releases when the PR merges or closes, and the vendoring rule requires the swap to the merged sha to happen before this PR lands, not in a follow-up bump. I updated the landing sequence in the PR description to match: land oven-sh/WebKit#390 first, swap WEBKIT_VERSION here to the merged sha once its prebuilt assets exist for every platform and flavor, then merge this PR. Until that merge happens there is no durable sha to pin, so the preview tag stays only for CI on this branch.

@robobun

robobun commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 54a79e0, which adds a second regression test. Recent fuzzer samples hit the same assert without touching any global: they recurse until the stack overflows and then first-touch Bun.sql from the frame just above the overflow, so the builder's module evaluation throws a RangeError after the Bun object has already transitioned. The minimal form (now in the PR description) aborts an unfixed debug build every run and exits 0 printing RangeError with the fix, in about 0.6s under debug ASAN. It does not depend on the sql module reading Error.prototype, so it stays valid if that ever changes. Checked both tests fail on an unfixed debug binary (exit 134 with this assertion) and pass with the pinned preview.

Comment thread test/js/bun/util/BunObject.test.ts Resolved
@robobun

robobun commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

On the stack overflow test comment: the second half of it was right, and checking it turned up something worth recording. I traced the script on an unfixed debug binary with breakpoints on structure transitions. The Bun object never transitions in that scenario. What aborts is the debug-only report that the builder used to make while the RangeError was still pending: Bun__handleUncaughtException reads process._fatalException, the static table reifies it (transition), setUpStaticFunctionSlot then reports a miss because of the pending exception, and the prototype step asserts on the process object's stale structure. So that test covers the BunObject.cpp change (it is the same lines that also consumed the exception), while the Error proxy test is the one that transitions the Bun object inside the builder and needs the engine reload on its own. f1ce747 rewrites the test comment to say exactly that, and the PR description and the WebKit PR now describe both routes.

On the exact stdout assertion, I kept it. The frame that runs the lookup is the one whose own call to f() just failed the stack check, and the builder cannot complete without entering JS functions that perform the same check, so it throws there on every configuration; the test passed on all 190 jobs of build 91796 (glibc, musl, darwin, windows, release and ASAN), and 8 of 8 local debug and 5 of 5 release runs print exactly RangeError. Accepting "no throw" would make the test pass in precisely the case where it stopped exercising anything, which is the failure mode the exact string is there to catch. The process-stdio precedent is different in kind: its getter is native and can legitimately finish in the remaining headroom.

@robobun

robobun commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto main (f5deafe0 pin from #40674). Head is now 8b5d2fcb09.

  • JSObject::getPropertySlot: reload the structure before the prototype step WebKit#390 is 28e2d2a8 on f5deafe0, the same 4-line patch. The two WebKit commits between the pins only touch the module loader, so the patched JSObject.h is byte for byte the previous one.
  • The only conflict here was the WEBKIT_VERSION line. It now points at autobuild-preview-pr-390-28e2d2a8, which has all 42 assets (the same asset set as main's autobuild-f5deafe0... release). The diff against main is still that line, its comment, and BunObject.test.ts.
  • On a debug ASAN build of the rebased branch (8b5d2fcb09, process.versions.webkit = preview-pr-390-28e2d2a8), test/js/bun/util/BunObject.test.ts passes (5 tests), the original fuzzer sample runs to its end, and the small Proxy prototype repro still prints caught: boom.
  • CI build 107205 is running on 8b5d2fcb09. The pin history in the description has the new entry.
  • The fuzzer reported the same Structure::storedPrototype assertion again today on main commit 43fad9bc61 with the stock cb61607f WebKit build. That is expected while the engine half of the fix is unmerged: main only carries the Bun-side half (Fix crash and dropped properties in Bun.inspect when a property lookup or getPrototypeOf throws during the property walk #29642).

Landing sequence is unchanged: merge oven-sh/WebKit#390, swap WEBKIT_VERSION here to the merged commit and delete the preview comment block, then merge this PR.

@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 commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

Main now pins WebKit 1817c3c3 (#40681, the upstream merge oven-sh/WebKit#528), which conflicts with the preview pin on this branch again.

  • JSObject::getPropertySlot: reload the structure before the prototype step WebKit#390 is rebased onto 1817c3c3 (now commit ebee9eee). The patch is unchanged. The 345 commits in that range do not touch JSObject.h, Lookup.cpp, Lookup.h, JSObjectInlines.h or StructureInlinesLight.h, so the patched file is byte for byte the same file the previous preview patched, and the fail-before analysis in the description still describes this pin.
  • This branch is rebased locally onto main. The only conflict was the WEBKIT_VERSION line, which now points at autobuild-preview-pr-390-ebee9eee. The diff against main is still that line, its comment, and BunObject.test.ts.
  • The push follows as soon as the preview release for ebee9eee has all of its assets, so that CI can download them.
  • For the record, CI build 107205 on the previous head 8b5d2fcb09 passed 181 of 182 jobs; the one red test was main's url.test.ts IDNA case on macOS x64.

@robobun robobun closed this Aug 28, 2026
@robobun
robobun force-pushed the farm/f76ebd86/fix-stale-structure-getpropertyslot branch from 8b5d2fc to 5ad6455 Compare August 28, 2026 08:42
@robobun robobun reopened this Aug 28, 2026

@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 commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto main twice in a row. Head is now e1aee47412, pinned to autobuild-preview-pr-390-41813b80.

  • Main moved its WebKit pin to 1817c3c3 (Upgrade WebKit to 6b879687ee #40681, the Upgrade to upstream WebKit 6b879687ee WebKit#528 upstream merge) and then to c4ddc0cf (compile: resolve module record names through the bytecode string table #40677) while the first preview was building. JSObject::getPropertySlot: reload the structure before the prototype step WebKit#390 is now 41813b80 on c4ddc0cf, the same 4-line patch. Neither range touches JSObject.h, Lookup.cpp, Lookup.h, JSObjectInlines.h or StructureInlinesLight.h, so the patched file is byte for byte the previous one and the fail-before analysis in the description still applies.
  • The only conflict here was the WEBKIT_VERSION line each time. The preview release for 41813b80 has all 42 assets (the same asset set as main's autobuild-c4ddc0cf... release). The diff against main is still that line, its comment, and BunObject.test.ts.
  • On a debug ASAN build of the rebased branch (e1aee47412, process.versions.webkit = preview-pr-390-41813b80), test/js/bun/util/BunObject.test.ts passes (5 tests) and the small Proxy prototype repro still prints caught: boom.
  • This PR was closed for about an hour this morning by my mistake. While rebasing onto the second pin bump, my push script ran in the middle of a conflicted rebase and pushed main's own commit 5ad6455989 to this branch, which left the PR with no diff and GitHub closed it. I restored the branch and reopened the PR once the rebased head existed. Nothing from the earlier rounds was lost. The script now refuses to push unless the rebase has finished, the branch is based on main, and the diff is exactly the two files.
  • The pin history in the description has both new entries, and the previous entry records build 107205 (181 of 182 green, the red being main's url.test.ts IDNA failure).
  • There is no CI build for e1aee47412 yet: the push landed while the PR was closed, so Buildkite skipped it, and my token cannot start builds. The next rebase push, once main moves again, starts one.

Landing sequence is unchanged: merge oven-sh/WebKit#390, swap WEBKIT_VERSION here to the merged commit and delete the preview comment block, then merge this PR.

@robobun

robobun commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

Main now pins WebKit ceb9f90f (#40767), which conflicts with the preview pin on this branch again.

@robobun
robobun force-pushed the farm/f76ebd86/fix-stale-structure-getpropertyslot branch from e1aee47 to 33d0c02 Compare August 28, 2026 14:43
@robobun

robobun commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto main (ceb9f90f pin from #40767). Head is now 33d0c02b6f.

  • JSObject::getPropertySlot: reload the structure before the prototype step WebKit#390 is 22905e76 on ceb9f90f, the same 4-line patch. The four WebKit commits between the pins touch the DFG integer range optimization, the bytecode cache constant decode, RegExp and JSString.h, so the patched JSObject.h is byte for byte the previous one.
  • The only conflict here was the WEBKIT_VERSION line. It now points at autobuild-preview-pr-390-22905e76, which has all 42 assets (the same asset set as main's autobuild-ceb9f90f... release). The diff against main is still that line, its comment, and BunObject.test.ts.
  • On a debug ASAN build of the rebased branch (33d0c02b6f, process.versions.webkit = preview-pr-390-22905e76), test/js/bun/util/BunObject.test.ts passes (5 tests) and the small Proxy prototype repro still prints caught: boom.
  • CI build 107725 is running on 33d0c02b6f, the first build since the accidental close. The pin history in the description has the new entry.

Landing sequence is unchanged: merge oven-sh/WebKit#390, swap WEBKIT_VERSION here to the merged commit and delete the preview comment block, then merge this PR.

@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 commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator Author

The fuzzer hit this assertion again on main (367d939d9a, stock WebKit 000c4899), and main has moved its WebKit pin many times since the last rebase here (23 bumps, now 299c5323). Status:

  • The new sample is small. Its trigger is a bare Bun.sql() call, the first touch of the lazy Bun.sql property. Alone it does not abort (the report is marked flaky). With the same precondition as this PR's test (an Error proxy whose prototype read reifies Bun.semver and throws), it aborts on that main build at the Bun.sql() line with exactly this Structure::storedPrototype assertion. So it is the bug this PR fixes, and main still has it because the engine half is unmerged.
  • JSObject::getPropertySlot: reload the structure before the prototype step WebKit#390 is rebased onto 299c5323 (now commit 70ecb3b8). This time JSObject.h did change upstream (fillCustomGetterPropertySlot takes a PropertyOffset now), so I re-applied the patch to the new file instead of reusing the old blob. It is the same 4 lines at the same place, and the WebKit PR still shows +4/-0 in one file. Lookup.cpp, Lookup.h, JSObjectInlines.h and StructureInlinesLight.h are unchanged.
  • This branch is rebased locally onto main. The only conflict was the WEBKIT_VERSION line, which now points at autobuild-preview-pr-390-70ecb3b8. Main did not touch BunObject.test.ts. The push follows once the preview release for 70ecb3b8 is published, so that CI can download it.

@robobun
robobun force-pushed the farm/f76ebd86/fix-stale-structure-getpropertyslot branch from 33d0c02 to 63c45cf Compare September 24, 2026 02:37
@robobun

robobun commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto main (299c5323 pin from #43724). Head is now 63c45cf62a.

  • JSObject::getPropertySlot: reload the structure before the prototype step WebKit#390 is 70ecb3b8 on 299c5323. JSObject.h changed upstream this time (fillCustomGetterPropertySlot takes a PropertyOffset), so I re-applied the patch to the new file. It is the same 4 lines at the same place, and that PR still shows +4/-0 in one file.
  • The only conflict here was the WEBKIT_VERSION line. It now points at autobuild-preview-pr-390-70ecb3b8, which is published with all 42 assets (the same asset set as main's autobuild-299c5323... release). Main did not touch BunObject.test.ts. The diff against main is still that line, its comment, and the test file.
  • On a debug ASAN build of the rebased branch (63c45cf62a, process.versions.webkit = preview-pr-390-70ecb3b8), test/js/bun/util/BunObject.test.ts passes (5 tests).
  • The fuzzer's new sample from today, checked both ways:
    • Unfixed: a main debug ASAN build (367d939d9a, stock WebKit 000c4899) aborts with the Structure::storedPrototype assertion at the sample's Bun.sql() line when the sample runs after the Error proxy from this PR's test (2 of 2 runs). Alone the sample does not abort, which matches its flaky label: in the fuzzer the precondition comes from an earlier sample in the same process.
    • Fixed: the same script on this branch's build does not abort (3 of 3 runs). The builder's exception surfaces as a normal uncaught error. The sample alone runs to its end.
  • The pin history in the description has the new entry with this fail-before.

Landing sequence is unchanged: merge oven-sh/WebKit#390, swap WEBKIT_VERSION here to the merged commit and delete the preview comment block, then merge this PR.

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

Nothing new to post: everything this review found is already covered by existing comments on this pull request or didn't merit a separate one.

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 force-pushed the farm/f76ebd86/fix-stale-structure-getpropertyslot branch 2 times, most recently from e00ff78 to ebea65e Compare September 26, 2026 03:06

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

Nothing new to post: everything this review found is already covered by existing comments on this pull request or didn't merit a separate one.

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 26, 2026

Copy link
Copy Markdown
Collaborator Author

The fuzzer hit this assertion again on main (367d939d9a, stock WebKit 000c4899), and main moved its WebKit pin to 35e8970d (#43811). Both are handled. Head is now ebea65e6cd.

  • The new sample calls Bun.sql() 8 times in a loop. The first call is the first touch of the lazy Bun.sql property. Checked both ways, with the Error proxy from this PR's test in front of the sample:
    • Unfixed main debug ASAN build: aborts with the Structure::storedPrototype assertion at the first Bun.sql() (2 of 2 runs). The proxy alone, with no Bun.sql touch, does not abort.
    • This branch's build: no abort (3 of 3 runs). The builder's exception surfaces as a normal uncaught error.
    • Alone, the sample does not abort on either build, which matches its flaky label. It ends at its own TypeError (a BigInt passed to new Float32Array), after the loop.
  • JSObject::getPropertySlot: reload the structure before the prototype step WebKit#390 is rebased onto 35e8970d (now commit 3a71b639). The one WebKit commit between 299c5323 and 35e8970d only touches the bytecode cache, so the patched JSObject.h is the same file as before. That PR still shows +4/-0 in one file.
  • The only conflict here was the WEBKIT_VERSION line. It now points at autobuild-preview-pr-390-3a71b639, which is published with all 42 assets (the same asset set as main's autobuild-35e8970d... release). The diff against main is still that line, its comment, and BunObject.test.ts.
  • On a debug ASAN build of the rebased branch (ebea65e6cd, process.versions.webkit = preview-pr-390-3a71b639), test/js/bun/util/BunObject.test.ts passes (5 tests).
  • CI: build 120848 on ebea65e6cd passed on every lane (181 jobs). Build 120218 on the previous head e00ff78139 did too.

Landing sequence is unchanged: merge oven-sh/WebKit#390, swap WEBKIT_VERSION here to the merged commit and delete the preview comment block, then merge this PR. Until the engine half lands, the fuzzer keeps finding this abort on main.

…-lookup

Fuzzilli kept hitting ASSERT(object->structure() == this) in
JSC::Structure::storedPrototype on debug builds (fingerprint
StructureInlinesLight.h(56)). The inlined JSObject::getPropertySlot loop
caches the structure, and reifying a static-table lazy property can
transition the object inside getOwnNonIndexPropertySlot and still return
false when the PropertyCallback builder throws, so the prototype step ran
on the pre-transition structure. Trigger: a first read of Bun.sql whose
module evaluation touches another lazy Bun property and then throws.

oven-sh/WebKit#390 reloads the structure before the prototype step, the
same reload the megamorphic slow paths in JITOperations.cpp already do.
This bumps the WebKit pin to its preview build.

Fixing that surfaced a second bug in the same path: defaultBunSQLObject
and constructBunSQLObject had a BUN_DEBUG-only
reportUncaughtExceptionAtEventLoop call that consumed the pending
exception, so RETURN_IF_EXCEPTION fell through and getObject() ran on an
empty JSValue (null deref), and an empty builder result with no pending
exception would trip RELEASE_ASSERT_NOT_REACHED in
setUpStaticFunctionSlot. Drop the debug reporting; the exception now
propagates to the caller like in release builds.
@robobun
robobun force-pushed the farm/f76ebd86/fix-stale-structure-getpropertyslot branch from ebea65e to f453ac3 Compare November 11, 2026 01:59

@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 Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

The fuzzer hit this assertion again (on main 367d939d9a with stock WebKit 000c4899), and main moved its WebKit pin 28 times since the last rebase here. The pin is now 873ab3a4 (#48436). Both are handled. Head is now f453ac3f98.

  • Current main still needs this. I built this branch twice as debug ASAN: once with main's pin (--webkit-version=873ab3a4...) and once with the preview pin.
    • Main's pin: the Error proxy test in BunObject.test.ts fails. Its script aborts with exit code 134 and the Structure::storedPrototype assertion. The backtrace goes from llint_slow_path_get_by_id through JSValue::get to JSObject::getPropertySlot (JSObject.h:1284). The other five tests pass.
    • Preview pin: all six tests pass.
  • The new fuzzer sample calls Bun.sql() and then runs a query. With the Error proxy from this PR's test in front of it, it aborts with main's pin (2 of 2 runs) and does not abort with the preview pin (3 of 3 runs). Alone it runs to its end on both builds, which matches its flaky label.
  • JSObject::getPropertySlot: reload the structure before the prototype step WebKit#390 is rebased onto 873ab3a4 (now commit 619f4d3e). The lookup path changed upstream in this range: JSObject.h, Lookup.cpp, Lookup.h and JSObjectInlines.h all differ, mostly for the ahead-of-time compiler. The loop in getPropertySlot is the same, and setUpStaticFunctionSlot still reports the slot as not found when the builder threw. So the patch is the same 4 lines at the same place, +4/-0 in one file. That PR's CI run passed.
  • The only conflict here was the WEBKIT_VERSION line. It now points at autobuild-preview-pr-390-619f4d3e, which is published with 77 assets (the same asset set as main's autobuild-873ab3a4... release). Main added a test to BunObject.test.ts in Hardening: fix crashes across the runtime, bundler, dev server, shell and CLI #46021, and the rebase kept it. The diff against main is still the pin line, its comment, and the two test changes.
  • CI build 139971 has one red test with 184 of 189 jobs finished: test/aot/resolver/exports-conditions.test.ts on the macOS 27 aarch64 lane. A child process trapped 1 ms after start, at a system library address (0x18EC0EC5C). Build 139935 of another branch has the identical trap at the same address in a different test, so it does not come from this diff.

Landing sequence is unchanged: merge oven-sh/WebKit#390, swap WEBKIT_VERSION here to the merged commit and delete the preview comment block, then merge this PR. Until the engine half lands, the fuzzer keeps finding this abort on main.

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