Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -889,15 +889,15 @@ releases on a rotation basis as outlined in the
* [bengl](https://github.com/bengl) -
**Bryan English** <<[email protected]>> (he/him)
* [HeroDevs](https://www.herodevs.com/)
* [juanarbol](https://github.com/juanarbol) - OpenJSF handle: `juanarbol`
* [juanarbol](https://github.com/juanarbol) - OpenJS Slack handle: `juanarbol`
**Juan José Arboleda** <<[email protected]>> (he/him)
* [marco-ippolito](https://github.com/marco-ippolito) - OpenJSF handle: `Marco Ippolito`
* [marco-ippolito](https://github.com/marco-ippolito) - OpenJS Slack handle: `Marco Ippolito`
**Marco Ippolito** <<[email protected]>> (he/him)
* [NodeSource](https://nodesource.com/)
* [RafaelGSS](https://github.com/RafaelGSS) - OpenJSF handle: `RafaelGSS`
* [RafaelGSS](https://github.com/RafaelGSS) - OpenJS Slack handle: `RafaelGSS`
**Rafael Gonzaga** <<[email protected]>> (he/him)
* [Platformatic](https://platformatic.dev/)
* [mcollina](https://github.com/mcollina) - OpenJSF handle: `mcollina`
* [mcollina](https://github.com/mcollina) - OpenJS Slack handle: `mcollina`
**Matteo Collina** <<[email protected]>> (he/him)
* [Red Hat](https://redhat.com) / [IBM](https://ibm.com)
* [BethGriggs](https://github.com/BethGriggs) -
Expand Down
4 changes: 2 additions & 2 deletions lib/internal/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const {
hexWrite,
ucs2Write,
utf8WriteStatic,
createUnsafeArrayBuffer,
createUnsafeBuffer: createUnsafeBufferSlow,
setDetachKey,
} = internalBinding('buffer');

Expand Down Expand Up @@ -1101,7 +1101,7 @@ function createUnsafeBuffer(size) {
return new FastBuffer(size);
}

return new FastBuffer(createUnsafeArrayBuffer(size));
return createUnsafeBufferSlow(size);
}

module.exports = {
Expand Down
69 changes: 51 additions & 18 deletions src/node_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,30 @@ inline size_t CheckNumberToSize(Local<Value> number) {
return size;
}

MaybeLocal<ArrayBuffer> CreateUnsafeArrayBufferFromSize(Environment* env,
size_t size) {
Isolate* isolate = env->isolate();

// 0-length, or zero-fill flag is set, or building snapshot
if (size == 0 || per_process::cli_options->zero_fill_all_buffers ||
env->isolate_data()->is_building_snapshot()) {
return ArrayBuffer::New(isolate, size);
}

std::unique_ptr<BackingStore> store = ArrayBuffer::NewBackingStore(
isolate,
size,
BackingStoreInitializationMode::kUninitialized,
BackingStoreOnFailureMode::kReturnNull);

if (!store) [[unlikely]] {
THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
return MaybeLocal<ArrayBuffer>();
}

return ArrayBuffer::New(isolate, std::move(store));
}

void CreateUnsafeArrayBuffer(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (args.Length() != 1) {
Expand All @@ -1466,30 +1490,36 @@ void CreateUnsafeArrayBuffer(const FunctionCallbackInfo<Value>& args) {

size_t size = CheckNumberToSize(args[0]);

Isolate* isolate = env->isolate();

Local<ArrayBuffer> buf;
if (!CreateUnsafeArrayBufferFromSize(env, size).ToLocal(&buf))
return;

// 0-length, or zero-fill flag is set, or building snapshot
if (size == 0 || per_process::cli_options->zero_fill_all_buffers ||
env->isolate_data()->is_building_snapshot()) {
buf = ArrayBuffer::New(isolate, size);
} else {
std::unique_ptr<BackingStore> store = ArrayBuffer::NewBackingStore(
isolate,
size,
BackingStoreInitializationMode::kUninitialized,
v8::BackingStoreOnFailureMode::kReturnNull);
args.GetReturnValue().Set(buf);
}

if (!store) [[unlikely]] {
THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
return;
}
void CreateUnsafeBuffer(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (args.Length() != 1) {
env->ThrowRangeError("Invalid array buffer length");
return;
}

size_t size = CheckNumberToSize(args[0]);

Local<ArrayBuffer> array_buffer;
if (!CreateUnsafeArrayBufferFromSize(env, size).ToLocal(&array_buffer))
return;

buf = ArrayBuffer::New(isolate, std::move(store));
if (env->buffer_prototype_object().IsEmpty()) {
args.GetReturnValue().Set(Uint8Array::New(array_buffer, 0, size));
return;
}

args.GetReturnValue().Set(buf);
Local<Uint8Array> buffer;
if (!Buffer::New(env, array_buffer, 0, size).ToLocal(&buffer))
return;

args.GetReturnValue().Set(buffer);
}

template <encoding encoding>
Expand Down Expand Up @@ -1621,6 +1651,8 @@ void Initialize(Local<Object> target,
SetMethod(context, target, "copyArrayBuffer", CopyArrayBuffer);
SetMethodNoSideEffect(
context, target, "createUnsafeArrayBuffer", CreateUnsafeArrayBuffer);
SetMethodNoSideEffect(
context, target, "createUnsafeBuffer", CreateUnsafeBuffer);

SetMethod(context, target, "swap16", Swap16);
SetMethod(context, target, "swap32", Swap32);
Expand Down Expand Up @@ -1723,6 +1755,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {

registry->Register(CopyArrayBuffer);
registry->Register(CreateUnsafeArrayBuffer);
registry->Register(CreateUnsafeBuffer);

registry->Register(Atob);
registry->Register(Btoa);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Flags: --expose-internals
'use strict';

const assert = require('assert');
const { internalBinding } = require('internal/test/binding');
const { loadBuiltinModule } = require('internal/modules/helpers');

const binding = internalBinding('buffer');
const internalBufferModule = loadBuiltinModule('internal/buffer');

function reloadInternalBuffer() {
internalBufferModule.loaded = false;
internalBufferModule.loading = false;
internalBufferModule.exports = {};
internalBufferModule.exportKeys = undefined;
internalBufferModule.module = undefined;
internalBufferModule.compileForPublicLoader();
return internalBufferModule.exports;
}

const originalCreateUnsafeArrayBuffer = binding.createUnsafeArrayBuffer;
const originalCreateUnsafeBuffer = binding.createUnsafeBuffer;

// Regression: Large allocations should not route through createUnsafeArrayBuffer.
binding.createUnsafeArrayBuffer = () => {
throw new Error('createUnsafeArrayBuffer should not be called for large buffers');
};

let internalBuffer = reloadInternalBuffer();
const large = internalBuffer.createUnsafeBuffer(65);
assert(large instanceof Uint8Array);
assert.strictEqual(large.length, 65);

// Edge case: Small sizes should remain on the in-heap path.
binding.createUnsafeBuffer = () => {
throw new Error('createUnsafeBuffer should not be called for small buffers');
};

internalBuffer = reloadInternalBuffer();
const small = internalBuffer.createUnsafeBuffer(64);
assert(small instanceof Uint8Array);
assert.strictEqual(small.length, 64);

// Nearby-path safety check: Public API behavior is unchanged for large sizes.
const userVisible = Buffer.allocUnsafeSlow(65);
assert(Buffer.isBuffer(userVisible));
assert.strictEqual(userVisible.length, 65);

binding.createUnsafeArrayBuffer = originalCreateUnsafeArrayBuffer;
if (originalCreateUnsafeBuffer === undefined) {
delete binding.createUnsafeBuffer;
} else {
binding.createUnsafeBuffer = originalCreateUnsafeBuffer;
}
reloadInternalBuffer();