Skip to content

correctness: a Float64Array element whose bits land in the NaN-box window reads back as its payload integer, and Number.isNaN then reports false #10779

Description

@proggeramlug

Summary

A Float64Array element whose bit pattern falls inside perry's NaN-box tag window reads back as its payload integer instead of the double it was written as. No error; the value is silently wrong.

const a = new Float64Array(1);
const view = new BigUint64Array(a.buffer);
view[0] = 0x7FF8000012345678n;          // a NaN whose payload is 0x12345678
console.log(typeof a[0], Number.isNaN(a[0]), a[0]);
// node : number  true  NaN
// perry: number  false 305419896

305419896 is 0x12345678 — the NaN payload, surfaced as an integer. perry also reports Number.isNaN(a[0]) as false for a value that is a NaN by IEEE-754 definition.

A typed array is raw memory with a defined bit-level interpretation: any 64-bit pattern may legitimately be stored and must read back as the Float64 it denotes. Perry's NaN-boxing uses the NaN space for tagged values, and a genuine f64 NaN with a payload collides with it.

How this arises in ordinary code

It does not need a BigUint64Array to construct. Any of these can put an arbitrary bit pattern into a Float64Array:

  • reading binary data — a file, a socket, a WASM memory, a GPU buffer
  • new Float64Array(someArrayBuffer) over bytes from anywhere
  • deserialising a struct written by C, Rust or another language
  • signalling NaNs produced by numeric code that encodes information in the payload

In each case the value is silently replaced by its payload integer, and Number.isNaN then agrees that it is not a NaN — so a downstream isNaN check cannot catch it either.

Two related divergences found alongside

Number.isNaN returns false for a NaN in the case above. That is the same root cause surfacing through a different API, but worth stating separately because Number.isNaN is the standard defensive check and it is the one thing a program would use to defend itself here.

A module-global BigInt64Array read throws a TypeError where node returns the value. Minimal case: declare a BigInt64Array at module scope and read an element from inside a function.

Provenance

Found while investigating #10777, on base — no change of ours is involved, and all three reproduce on origin/main plus only unrelated open PRs. Compared against node v26.8.1 on Linux x86-64.

I am filing the first two together because they share a root cause; the BigInt64Array one is separate and may well be unrelated, but it was found in the same sweep and would otherwise go unrecorded.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions