Skip to content

fix(runtime): #5625 — Temporal brand probe must skip header-less typed arrays/buffers#5650

Merged
proggeramlug merged 1 commit into
mainfrom
worktree-fix-5625-typedarray-species
Jun 24, 2026
Merged

fix(runtime): #5625 — Temporal brand probe must skip header-less typed arrays/buffers#5650
proggeramlug merged 1 commit into
mainfrom
worktree-fix-5625-typedarray-species

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

What

Fixes the TypedArray species-constructor crashes — the net-new #5579-batch test262 regressions tracked in #5625 (the other 17 from that issue were already resolved by #5628).

Root cause

temporal::is_temporal_cell_addr brand-probed a candidate receiver by reading GcHeader.obj_type at addr - GC_HEADER_SIZE without first excluding off-GC-heap, header-less allocations. Small typed arrays and Buffers are raw-alloc'd with no 8-byte GcHeader prefix, so that back-read lands on unrelated preceding heap bytes — which can coincidentally equal GC_TYPE_TEMPORAL (18).

When it did, a method call on such a typed array (<TA>.slice() / .subarray() / .every()) was mis-routed through temporal::dispatch::call_method, which then dereferenced the "cell"'s value box (actually the typed array's {length, capacity} words, e.g. 0x0000000400000004 for a 4-element array) as a Box<TemporalValue> and segfaulted. This surfaced as the silent (no output) crashes in the report.

Because the trigger is a coincidental byte value in adjacent heap memory, the crash was allocation-layout dependent (deterministic under the test262 harness, masked by extra allocations from print/auto-optimize — which is why it only reproduced under PERRY_NO_AUTO_OPTIMIZE).

Fix

Reject header-less side-table allocations via crate::typedarray::is_offheap_sidetable_alloc before the deref — exactly as crate::date::is_date_cell_addr already does for the identical reason (the Map/Set brand probes are safe because they consult their registries first). A real TemporalCell is never registered as a typed array or buffer, so the guard never rejects a genuine Temporal value.

Verification

The 4 affected cases now pass deterministically (each 5×), and the broader built-ins/TypedArray/prototype/{slice,map,subarray,every} sweep is 160/161 (the 1 remaining — every/callbackfn-not-callable-throws — is a pre-existing, unrelated argument-validation gap):

built-ins/TypedArray/prototype/slice/speciesctor-get-ctor-returns-throws.js    => exit=0
built-ins/TypedArray/prototype/map/speciesctor-get-ctor-returns-throws.js      => exit=0
built-ins/TypedArray/prototype/subarray/speciesctor-get-ctor-returns-throws.js => exit=0
built-ins/TypedArray/prototype/every/returns-true-if-every-cb-returns-true.js  => exit=0

Temporal is unaffected — built-ins/Temporal/{Duration,PlainDate,Instant} self-validated sweep stays at 97.8% (1620/1657), and a Temporal smoke test (Duration/PlainDate/Instant arithmetic + formatting) passes.

Out of scope

The remaining built-ins/Array/prototype/copyWithin/coerced-values-start-change-start case in #5625 is a separate, pre-existing, flaky generational-GC bug: an array's custom [[Prototype]] (set via Object.setPrototypeOf and stored as an old→young edge in an external side-table) is not rewritten when the prototype is evacuated by a minor GC, so an inherited index read intermittently sees a stale/forwarded pointer. It is independent of this regression and needs dedicated remembered-set work; #5625 can stay open for it.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of typed arrays and buffers so off-heap allocations are no longer mistaken for temporal cells.
    • Prevented incorrect routing caused by reading unrelated heap data during temporal address checks.

…d arrays/buffers

`is_temporal_cell_addr` read `GcHeader.obj_type` at `addr - GC_HEADER_SIZE`
without first excluding off-GC-heap, header-less allocations. Small typed
arrays and `Buffer`s are raw-`alloc`'d with NO 8-byte `GcHeader` prefix, so
that back-read lands on unrelated preceding heap bytes — which can
coincidentally equal `GC_TYPE_TEMPORAL` (18). When it did, a method call on
such a typed array (e.g. `<TA>.slice()` / `.subarray()` / `.every()`) was
mis-routed through `temporal::dispatch::call_method`, which then dereferenced
the "cell"'s value box (actually the typed array's `{length,capacity}` words)
as a `Box<TemporalValue>` and segfaulted — surfacing as silent `(no output)`
crashes in the test262 `%TypedArray%.prototype` species-constructor cases.

Reject header-less side-table allocations via `is_offheap_sidetable_alloc`
before the deref, exactly as `crate::date::is_date_cell_addr` already does for
the same reason. A real `TemporalCell` is never registered as a typed array or
buffer, so the guard never rejects a genuine Temporal value.

Fixes the 4 net-new #5579-batch regressions:
  built-ins/TypedArray/prototype/{slice,map,subarray}/speciesctor-get-ctor-returns-throws
  built-ins/TypedArray/prototype/every/returns-true-if-every-cb-returns-true

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b77356f5-84b7-4a74-b403-f13db266bcf6

📥 Commits

Reviewing files that changed from the base of the PR and between 3b7271c and 9f686ee.

📒 Files selected for processing (1)
  • crates/perry-runtime/src/temporal/mod.rs

📝 Walkthrough

Walkthrough

is_temporal_cell_addr in crates/perry-runtime/src/temporal/mod.rs gains a 10-line early-return that calls crate::typedarray::is_offheap_sidetable_alloc(addr) and returns false before the GC header word is read, preventing off-heap typed-array/Buffer addresses from being misidentified as TemporalCells.

Changes

Off-heap guard in temporal cell address probe

Layer / File(s) Summary
Early-return guard for off-heap typed-array addresses
crates/perry-runtime/src/temporal/mod.rs
Adds a pre-check in is_temporal_cell_addr that calls is_offheap_sidetable_alloc(addr) and returns false for off-heap allocations, along with comments explaining the misrouting failure mode being prevented.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related PRs

  • PerryTS/perry#5243: Applies the same typedarray::is_offheap_sidetable_alloc guard pattern to other type-probe paths in the runtime for the same class of GC-header misread.

Poem

A rabbit hopped past a typed array's edge,
"Off-heap!" it cried, from its memory ledge.
No GC header shall fool me today,
I'll return false and just hop away.
🐇 Safe cells only, that's what I say!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the runtime fix and the Temporal brand-probe issue affecting typed arrays/buffers.
Description check ✅ Passed The description covers the bug, root cause, fix, related issue, and verification, though it doesn't follow the template exactly.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-fix-5625-typedarray-species

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

@proggeramlug proggeramlug merged commit 40406c1 into main Jun 24, 2026
15 checks passed
@proggeramlug proggeramlug deleted the worktree-fix-5625-typedarray-species branch June 24, 2026 21:16
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