Skip to content

Object.freeze throws for a typed array whose buffer is detached #1765

Open

Description

@d01c2

Reproduction

const b = new ArrayBuffer(8);
const v = new Float32Array(b);
b.transfer();
print(Object.freeze(v) === v);

Expected

v is returned. The buffer is not resizable, and a detached view has no elements to freeze.

V8 (15.6.20):

$ v8 -e 'const b = new ArrayBuffer(8); const v = new Float32Array(b); b.transfer(); print(Object.freeze(v) === v);'
true

Actual (QuickJS-ng)

QuickJS-ng reports the view as resizable:

$ qjs -e 'const b = new ArrayBuffer(8); const v = new Float32Array(b); b.transfer(); print(Object.freeze(v) === v);'
TypeError: cannot freeze resizable typed array

Spec

Object.freeze calls SetIntegrityLevel with frozen. Its first step is O.[[PreventExtensions]](); for a TypedArray, [[PreventExtensions]] returns false only when IsTypedArrayFixedLength is false. A fixed-length view on a non-resizable buffer is fixed-length whether or not the buffer is detached, so this succeeds.

SetIntegrityLevel then makes every own key non-configurable and non-writable. A detached view has no integer-indexed keys, so nothing can fail and Object.freeze returns v.

QuickJS-ng agrees with this everywhere except Object.freeze: Object.preventExtensions(v) succeeds on the same view, after which Object.isFrozen(v) is true. The freeze path additionally rejects any view that is out of bounds, and a detached view counts as out of bounds even though its buffer is not resizable.

For context: #1691 (fixed by PR #1695) added the resizable-buffer check to Object.preventExtensions, where Object.freeze already had it; this report is about the extra out-of-bounds condition that only Object.freeze applies.

JavaScriptCore, SpiderMonkey, GraalJS, XS and V8 all return the typed array.

Versions

  • QuickJS-ng: 0.17.0
  • V8: 15.6.20

Activity

  1. mvanhorn commented on Oct 12, 2026

    @mvanhorn
    Contributor

    In js_object_seal, the freeze path threw when the backing buffer was resizable or when typed_array_is_oob was true. Detachment makes the view out of bounds even if the buffer was never resizable, so that second check rejected a view that is still fixed-length.

    The freeze guard now throws only when array_buffer_is_resizable is true, and a detached fixed-length view continues through the normal freeze path and is returned.

    #1824

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