Repository navigation
Conversation
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.
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Blocked on oven-sh/WebKit#720. A draft until the pin is a commit of the fork's main.
Problem
bun:ffifunction aborts the process:panic(main thread): abort() called. The inspector command isDebugger.addSymbolicBreakpoint.JSFFIFunction::createlet twoNativeExecutables share one constructJITCode. The debugger swaps aJITCodeonce per executable, and itsRELEASE_ASSERTatInspectorDebuggerAgent.cpp:1695(or:1729) fails on the second swap.Fix
autobuild-preview-pr-720-d997e370: the current pin564ac2a6cad8plus [JSC] bun:ffi: a symbolic breakpoint that matches an FFI function name does not abort WebKit#720, which gives that executable its own constructJITCode.autobuild-<sha>release of the merge commit then.test/cli/inspect/debugger-symbolic-breakpoint-ffi.test.tsfails withUSE_SYSTEM_BUN=1and on a debug build with the current pin (signalCode: "SIGABRT"). It passes with the new pin. Alsotest/cli/inspect/inspect.test.ts,debugger-buntranspiledmodule.test.ts,test/js/bun/ffi/ffi.test.js,webkit-prebuilt-url.test.tsand the shippedtestFFI.Background
Debugger.addSymbolicBreakpoint(packages/bun-debug-adapter-protocol/src/debugger/adapter.ts:1012).Downsides
NativeJITCode(40 bytes).CallFFI. The same abort still exists for two live WebAssembly functions named"0"or"start". Both are in the notes of [JSC] bun:ffi: a symbolic breakpoint that matches an FFI function name does not abort WebKit#720.Notes
JSCallbackthree times under one name withlinkSymbols(): two functions before the breakpoint exists and one while it is set. Those are the two orders that failed (:1695and:1729). It needs no shared library, so it runs on every platform.signalCode: "SIGABRT", no reply toDebugger.addSymbolicBreakpoint, and only theDebuggerStatementpause.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.dlopen("libz.so.1"), keepzlibVersion, add the breakpoint while paused on adebuggerstatement) with the new pin: the command answers{"result":{}}, the next call pauses with reasonFunctionCalland data{"name":"zlibVersion"}, the script prints both values and exits with code 0.test/js/bun/ffi/ffi.test.json 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.testFFIfrom the preview build (debug, ASAN) passes, with the newtestSymbolicBreakpointOnFFIFunctioncase. 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)
diff hotspot
gate history · 1 passed · 0 rejected · iteration 0
evidence per changed file