Skip to content

Can not construct Buffer after different Buffer was previously transfererd #61362

@lucacasonato

Description

@lucacasonato

Version

24.12.0

Platform

not relevant

Subsystem

lib/buffer

What steps will reproduce the bug?

> const buf = Buffer.from(btoa("hello"), "base64");
undefined
> buf.buffer.transfer();
ArrayBuffer {
  [Uint8Contents]: <2f 00 00 00 00 00 00 00 68 65 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... 8092 more bytes>,
  [byteLength]: 8192
}
> 
> Buffer.from(btoa("hello"), "base64");
Uncaught RangeError: "offset" is outside of buffer bounds
    at Object.write (node:buffer:698:46)
    at fromStringFast (node:buffer:480:22)
    at fromString (node:buffer:504:51)
    at Buffer.from (node:buffer:310:12) {
  code: 'ERR_BUFFER_OUT_OF_BOUNDS'
}

This is the minimal reproduction. In reality this was encountered by two systems that were unaware of each other assuming the following:

  • System 1 assumed that Buffer.from returns Uint8Array (subclasses) that after returning are owned by the caller
  • System 2 assumed that any Uint8Array passed in is owned by it after passing it in

System 2 was detaching the ArrayBuffer because it passed it to the byobRequest.respond() method (which transfers the buffer). In practice this can and will happen more frequently going forward, because there is now a method to detach and transfer arbitrary ArrayBuffers: ArrayBuffer.prototype.transfer().

System 1 was unaware that transfers were going to happen.

How often does it reproduce? Is there a required condition?

Always

What is the expected behavior? Why is that the expected behavior?

The expected behaviour is that Buffer.from should return Uint8Array's that do not break when the underlying buffer of any one other returned Uint8Array is detached (ie, they should not use a pooled AB).

What do you see instead?

An error is thrown on unrelated Buffer.from() calls when the return value of a prior Buffer.from() is detached.

Additional information

A previous mitigation for this was attempted in #32759, but it is not effective anymore as ArrayBuffer.prototype.transfer() does not care about Node internal symbols on the ArrayBuffer pool.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bufferIssues and PRs related to the buffer subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions