Skip to content

runtime: new Uint8Array(arrayBuffer) does not produce a live view; second view of same ArrayBuffer reads zeros #579

Closed

Description

@proggeramlug

Repro

const ab = new ArrayBuffer(4);
const v1 = new Uint8Array(ab);
v1[0] = 10; v1[1] = 20; v1[2] = 30; v1[3] = 40;

const v2 = new Uint8Array(ab);   // second view of the SAME buffer
console.log(v2[0], v2[1], v2[2], v2[3]);

bun main.ts → 10 20 30 40 (v2 reads what v1 wrote, since both share ab)
perry main.ts → 0 0 0 0

Per spec, every new Uint8Array(arrayBuffer) aliases the underlying ArrayBuffer — multiple views see each other's writes. In Perry, each call seems to allocate its own backing storage and forget the ArrayBuffer.

Why this matters

Discovered while running @bradenmacdonald/s3-lite-client post #575 + #576 fix (#551). The package's sha256hmac helper does:

const signature = await crypto.subtle.sign("HMAC", keyObject, data);
return new Uint8Array(signature);   // signature is an ArrayBuffer per Web Crypto spec

The returned bytes are then hex-encoded for the AWS signature. Since the second view doesn't see the original buffer's contents, the hex signature is empty/wrong — every AWS-signed URL fails authentication.

Beyond s3-lite, this breaks every Web Crypto consumer: AWS SigV4, JWT (HS256/RS256), OAuth, web push (VAPID), Google Cloud signed URLs. The Web Crypto contract returns ArrayBuffer and consumers wrap with new Uint8Array(buf) — that's the standard idiom and it has to work.

Also breaks fetch body handling, file I/O via Blob, anywhere data crosses an ArrayBuffer boundary.

Suggested implementation

new Uint8Array(arrayBuffer) needs to produce a view that aliases the same backing storage as arrayBuffer. Currently it appears to either copy or reset to zeros. Search crates/perry-runtime/src/typedarray.rs for the constructor — the ArrayBuffer-input arm should not allocate fresh storage; it should record a pointer + offset + length into the shared buffer.

If ArrayBuffer itself isn't currently a real heap-allocated shared object in Perry, that's the underlying gap to fix first.

Acceptance

The repro prints 10 20 30 40. Plus regressions:

  • new Uint8Array(ab, byteOffset, length) — partial view
  • new DataView(ab) reads/writes through the same buffer
  • Uint16Array, Float32Array, etc. all alias
  • crypto.subtle.sign(...) → new Uint8Array(result) produces correct HMAC bytes (verified against AWS SigV4 test vectors at https://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html)
  • await response.arrayBuffer() then new Uint8Array(buf) reads body bytes

Refs #551

Activity

  1. added a commit that references this issue on May 8, 2026
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