Skip to content

Merge train: #9564 #9565 (mysql2 fallback; cross-thread promise pinning) - #9569

Merged
proggeramlug merged 3 commits into
mainfrom
land-train80
Sep 2, 2026
Merged

Merge train: #9564 #9565 (mysql2 fallback; cross-thread promise pinning)#9569
proggeramlug merged 3 commits into
mainfrom
land-train80

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Lands the validated train: #9564 (bundled mysql2 fallback aligned with the ext extension — reflective/dynamic dispatch, query options, rowsAsArray, Error metadata; Drizzle pins one checked-out connection) and #9565 (cross-thread promises pinned in their constructor until settlement — closes the ~110-site class behind #9552's use-after-free; adds the provenance gate, self-tested and wired into lint).

Train fix: three gc test-fixture Promise literals gained the new native_pinned field.

Validation: release build green; RUST_TEST_THREADS=1 perry-runtime 3027/0; perry-stdlib (bundled-mysql2) green single-threaded; #9552 gap fixture byte-identical to node; mysql2 fixtures skip without a live DB (author ran them live end-to-end); new provenance gate green with working self-test; lint gates green.

Rebase-merge preserving authorship.

Ralph Küpper added 3 commits September 2, 2026 21:31
…ey settle (#9552)

A promise minted for a cross-thread settlement — every stdlib fetch/db/ws
request, `spawn`, `Atomics.waitAsync` — leaves the runtime as a bare usize
inside a worker future and is invisible to every root scanner until its
completion is queued back. Nothing on the JS side points AT it either: the
awaiting continuation hangs OFF it (`P.on_fulfilled`) and `P.next` is an
edge out. The pin was the caller's job; `spawn` and `waitAsync` took it,
~110 stdlib sites (fetch among them) never did. A full collection landing
in the in-flight window freed the promise, and the completion then
resolved whatever the allocator had reused the slot for — in the report,
a RegExp header read as the promise's `next` inside the microtask pump.

`js_promise_new_cross_thread` now takes the pin itself (one flag bit on a
malloc-resident object it is already writing; the young-pin latch is not
consulted) and the settlement paths release it (one byte test on the
promise's own cache line). A token dropped without a settlement releases
it too. The caller-side pins in `spawn` and `waitAsync` are gone; the
stdlib bridge helper is now exactly the constructor.

Every place a raw promise address re-enters the runtime from native code
(the stdlib pump, the native-async token pump, the thread-result drain)
classifies the address first and aborts naming the site and the slot's
occupant, so a future rooting hole fails at the boundary instead of as
heap corruption cycles later.

`scripts/check_cross_thread_promise_provenance.py` (lint) finds arena
promises handed to a native settlement sink or captured by a spawn,
self-tested with three planted shapes and three clean ones.

Claude-Session: https://claude.ai/code/session_01Bok4V8wzgNGmBeE4GPf7Up
@proggeramlug
proggeramlug merged commit ef956db into main Sep 2, 2026
@proggeramlug
proggeramlug deleted the land-train80 branch September 2, 2026 20:01
proggeramlug pushed a commit that referenced this pull request Sep 2, 2026
…9565

The #9552 fix landed via merge train #9569 with the first draft of its gap
test. That draft — `await fetch()` in a plain async function plus `gc()` —
passed 6/6 on the UNFIXED runtime: the collection it forces does not free
the in-flight promise, so the gate could not fail. This is the fixture
validated on both arms: three consumer shapes (`.then`, async arrow, async
class method) start a request from a frame that has returned, `Symbol()`
churn trips the malloc-count sweep, `RegExp` headers reuse freed 80-byte
slots. Unfixed it hangs 4/4 (two of the three promises freed by malloc
sweeps, then a stale resolve on a reused slot); fixed it prints node's
`ok,ok,ok 6` 5/5.

Also lands the changelog fragment for #9565, which the train did not carry.

Claude-Session: https://claude.ai/code/session_01Bok4V8wzgNGmBeE4GPf7Up
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant