Skip to content

Bump WebKit (oven-sh/WebKit#720 preview): a symbolic breakpoint that matches a bun:ffi function name no longer aborts - #43813

Draft
robobun wants to merge 1 commit into
mainfrom
robobun/5f889c11/webkit-720-symbolic-breakpoint-ffi
Draft

robobun wants to merge 1 commit into
mainfrom
robobun/5f889c11/webkit-720-symbolic-breakpoint-ffi

Conversation

@robobun

@robobun robobun commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Blocked on oven-sh/WebKit#720. A draft until the pin is a commit of the fork's main.

Problem

  • A function breakpoint in VS Code, or a symbolic breakpoint in Web Inspector, whose name matches a live bun:ffi function aborts the process: panic(main thread): abort() called. The inspector command is Debugger.addSymbolicBreakpoint.
  • The cause is in JSC. JSFFIFunction::create let two NativeExecutables share one construct JITCode. The debugger swaps a JITCode once per executable, and its RELEASE_ASSERT at InspectorDebuggerAgent.cpp:1695 (or :1729) fails on the second swap.

Fix

Background

  • A symbolic breakpoint pauses when a function with a given name is called. For a native function, JSC replaces the function's entry point with a thunk that calls the debugger first.
  • The debug adapter maps a VS Code function breakpoint to Debugger.addSymbolicBreakpoint (packages/bun-debug-adapter-protocol/src/debugger/adapter.ts:1012).

Downsides

Notes
  • The test links one JSCallback three times under one name with linkSymbols(): two functions before the breakpoint exists and one while it is set. Those are the two orders that failed (:1695 and :1729). It needs no shared library, so it runs on every platform.
  • Without the fix the debuggee dies when the breakpoint is added. The diff of the test then shows signalCode: "SIGABRT", no reply to Debugger.addSymbolicBreakpoint, and only the DebuggerStatement pause.
  • Fail-before on a debug build: bun bd --webkit-version=564ac2a6cad8da6a358f3d331690e1ec65e5d67e test test/cli/inspect/debugger-symbolic-breakpoint-ffi.test.ts. The new test passed 8 of 8 runs with the new pin.
  • The original repro (dlopen("libz.so.1"), keep zlibVersion, add the breakpoint while paused on a debugger statement) with the new pin: the command answers {"result":{}}, the next call pauses with reason FunctionCall and data {"name":"zlibVersion"}, the script prints both values and exits with code 0.
  • test/js/bun/ffi/ffi.test.js on the debug build: 147 pass, 5 skip, 1 fail. The failure is the 5 s timeout of "ptr argument: ArrayBuffer cells through an FTL-compiled call site". It times out the same way with the current pin.
  • testFFI from the preview build (debug, ASAN) passes, with the new testSymbolicBreakpointOnFFIFunction case. CI of [JSC] bun:ffi: a symbolic breakpoint that matches an FFI function name does not abort WebKit#720 ran it on Linux x64 and arm64.

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

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

Debug/ASAN (expected pass):
$ bun bd test 'test/cli/inspect/debugger-symbolic-breakpoint-ffi.test.ts'
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test test/cli/inspect/debugger-symbolic-breakpoint-ffi.test.ts
bun test v1.4.3 (367d939d9)

test/cli/inspect/debugger-symbolic-breakpoint-ffi.test.ts:
(pass) a symbolic breakpoint pauses on a bun:ffi function and the call returns [779.73ms]

 1 pass
 0 fail
 2 expect() calls
Ran 1 test across 1 file. [2.58s]
Exit: 0
diff hotspot
scripts/build/deps/webkit.ts                       |   2 +-
 .../debugger-symbolic-breakpoint-ffi.test.ts       | 114 +++++++++++++++++++++
 2 files changed, 115 insertions(+), 1 deletion(-)

gate history · 1 passed · 0 rejected · iteration 0

evidence per changed file
file                                                      reads  edits  tests
scripts/build/deps/webkit.ts                                  0      0     14
…st/cli/inspect/debugger-symbolic-breakpoint-ffi.test.ts      1      2     13

A symbolic breakpoint (a function breakpoint in VS Code) whose symbol
matches the name of a bun:ffi function aborted the process in JSC.
oven-sh/WebKit#720 gives the executable of an FFI function with an IC
stub its own construct JITCode.

The new test sets the breakpoint on a linkSymbols() function through the
inspector. It checks that each call pauses with reason FunctionCall and
returns, for functions made before and after the breakpoint.
@robobun

robobun commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator Author

Status

Jarred-Sumner added a commit that referenced this pull request Oct 21, 2026
…6270)

### What does this PR do?

Hardening follow-up to #46021. It fixes regressions from #46021 and
#44618 that were never released, includes more of the open pull requests
that fix a crash, and fixes a few crashes that had no pull request.

#### Regressions fixed (none was released)

- `spyOn(Bun, "$")`, and the same for `"sql"`, `"postgres"` and `"SQL"`,
threw "does not support accessor properties yet".
- Async `fs.read`, `fs.write` and `zlib.brotliCompress` on a view over a
`WebAssembly.Memory` could lose data or copy far more than they needed.
- `node:vm` with `vm.constants.DONT_CONTEXTIFY`: `this` in host
accessors, prototype handling, `util.types.isProxy`, `Bun.deepEquals`,
`assert.deepEqual` and `console.log` of a context.
- `zlib` `stream.flush()` threw for arguments it used to accept, and
streams had a new enumerable property.
- `Bun.Transpiler` `exports.replace` with the name `"default"`.
- `process.env.X = 3000` could store the number once the function was
JIT-compiled.
- `node:sqlite` and `bun:sqlite` could lose rows at exit.
- Dev server: stylesheets shared between pages after a build error.
- Shell `cp` of a file onto itself exited 1.
- `error.stack`: the frame of `require()` reads `at anonymous (unknown)`
again.
- `import * as process from "process"` followed by `process.on(...)`
threw at load. The EventEmitter methods that the module exports now act
on `process`.
- `delete res.statusCode; res.end()` in a `node:http` handler threw. It
sends 200, as in Node. (from #46128)
- `node:tls`, `node:https` and `node:http2`: connection teardown and
write callbacks behave as in 1.4.3 and Node again.

#### Crash fixes

Bundler, parser and CSS: #42286, #46092, #46097, #46177, #46239, #36334,
#43024, #40769.
`bun test`, `--hot` and `--watch`: #30776, #38206, #38974.
Shell: #43183, #37743, #46168, #46207, #34900.
`node:http`, `node:http2`, `Bun.serve`, streams: #43449, #45380, #44457,
#42197, #42485, #38082, #38124, #46074, #45999, #46045.
`node:vm`, native addons, workers: #46129, #46198, #45615, #33412,
#38031.
Dev server: #42790, #42850, #39488.
Other: #35991, #38030, #38586, #40526, #41830, #46029, #46078, #46114,
and the bug of #42215.

#### Behavior changes

- `crypto.randomFill(view, cb)` reports `ERR_CRYPTO_OPERATION_FAILED` if
it could not fill the view. It used to report success.
- A function registered twice on `process` runs twice, as in Node.
(#41830)
- Under `--hot`, a reload after an uncaught exception cancels the timers
that the failed run created. The new run's timers fire, and so do timers
and `fs.watchFile` watchers that earlier runs kept on `globalThis`. The
reload also resets the exit code to 0. (#38206)
- The parent of a Worker no longer gets an `'error'` event for a failed
`Bun.serve` request in that Worker. The error is printed, as on the main
thread.
- `hash.write()` after `digest()` drops the chunk, as in Node. (#35991)
- `http2` over TLS with `server.emit("connection", socket)`:
`session.socket` is a `TLSSocket` and a `'connection'` listener fires,
as in Node. (#38124)
- A `slice()` with no type of a body `Blob` has `type === ""`, as in
Node. (#46114)
- `util.types.isProxy(globalThis)` is false, as in Node.
- A write on a `node:net` or `node:tls` socket that is still pending
when the connection closes calls back with `ECANCELED`, as in Node.
- `HTMLRewriter.on()` throws for a selector that is nested too deeply.
- `Bun.Image`: `maxPixels` also bounds PNG metadata, `metadata()` of a
GIF reports the size that gets decoded, and a path with a NUL byte
throws. (#40526)
- `bun build --no-bundle` with the `base64` or `dataurl` loader prints
an error. (#36334)
- Bundled output for a default-exported function or class expression
changes to `var file_default = function name() {}`, as in esbuild.
`.name` is unchanged. (#46097)
- A Proxy in the prototype chain of a `node:vm` global is allowed. jsdom
30 with `runScripts` needs this.
- `bun:sqlite` commits a write that `iterate()` left open when the main
thread exits. An explicit `BEGIN` is still rolled back.
- When a Worker stops while a native addon's async work is still
running, the addon's finalizers run after that work is done, as in Node.
- Shell: `mv -n` and `cp -n` don't overwrite, and `cp` reads `R`, `v`
and `n` anywhere in a flag group.

#### Performance

- Ordinary `node:vm` contexts run at the same speed. With
`DONT_CONTEXTIFY`, stores to globals and reads of inherited names in a
hot loop cost 10 to 20 % more than on `main`.
- A `process.env` write costs 20 to 35 % less than after #46021.
- An async read into a `WebAssembly.Memory` view looks through the live
memories: about 1.5 µs more with one memory.

#### Not included

- Taken in part, so these should stay open for the rest: #36334, #38974,
#39488, #40526, #40769, #42485, #45615, #45999, #46029, #46092, #46114,
#46168, #46207, #46239.
- Needs a WebKit change: #34655 #39204 #40170 #40253 #41587 #41900
#42699 #42897 #42903 #43084 #43813 #44518 #44949 #45770 #45804 #45827.
- #44723 (covered by #46097), #41675, #42753, #38762, #41475, #46016.

### How did you verify your code works?

- Every item was reproduced first on release builds of `main`, of its
parent and of 1.4.3, and compared with Node v26 where Node has the API.
- Every fix has a test that fails on `main` and passes on a debug + ASAN
build of this branch, except where that isn't practical (huge inputs,
code that only runs on Windows or macOS).
- The regressions were checked on grids of calls against the build
before #46021 and against 1.4.3.
- Bun's own test suites and Node's `test-vm-*`, `test-http2-*` and
`test-zlib-*` files for the areas touched.

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