ffi: reject non-boolean copy arguments

ffi.toBuffer() and ffi.toArrayBuffer() document copy as a boolean, but
read it with BooleanValue(), which applies JavaScript truthiness. A
falsy non-boolean such as null, 0 or '' therefore selects the
zero-copy mode, which returns a writable view over foreign memory
instead of a copy, while values such as 'false' or 1 select a copy.

Throw ERR_INVALID_ARG_TYPE when copy is neither undefined nor a
boolean. Omitting copy or passing undefined still makes a copy, and
true and false keep their current behavior.

Signed-off-by: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66219
Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
This commit is contained in:
Christian Aurich Zanettini Martins authored and Filip Skokan committed 2026-09-25 21:33:44 +02:00
1 parent 118f2a1d78
commit 63a6ea26bb
2 files changed
+24 -5

No files matched your search

+12 -5
View File
@@ -546,7 +546,6 @@ static bool ZeroCopyUnavailable(Environment* env) {
void ToBuffer(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
@@ -589,8 +588,12 @@ void ToBuffer(const FunctionCallbackInfo<Value>& args) {
return;
}
bool copy = args.Length() < 3 || args[2]->IsUndefined() ||
args[2]->BooleanValue(isolate);
if (!args[2]->IsUndefined() && !args[2]->IsBoolean()) {
THROW_ERR_INVALID_ARG_TYPE(env, "The copy argument must be a boolean");
return;
}
bool copy = !args[2]->IsFalse();
if (!copy && ZeroCopyUnavailable(env)) return;
Local<Object> buf;
@@ -654,8 +657,12 @@ void ToArrayBuffer(const FunctionCallbackInfo<Value>& args) {
return;
}
bool copy = args.Length() < 3 || args[2]->IsUndefined() ||
args[2]->BooleanValue(isolate);
if (!args[2]->IsUndefined() && !args[2]->IsBoolean()) {
THROW_ERR_INVALID_ARG_TYPE(env, "The copy argument must be a boolean");
return;
}
bool copy = !args[2]->IsFalse();
if (!copy && ZeroCopyUnavailable(env)) return;
Local<ArrayBuffer> ab;
+12
View File
@@ -132,6 +132,18 @@ test('ffi zero-copy views throw with the V8 sandbox', { skip: !common.hasV8Sandb
}));
});
test('ffi toBuffer and toArrayBuffer require a boolean copy argument', () => {
withAllocations(common.mustCall((alloc) => {
const ptr = alloc(4);
const type = { code: 'ERR_INVALID_ARG_TYPE' };
for (const copy of [null, 0, '', 'false', 1, {}]) {
assert.throws(() => ffi.toBuffer(ptr, 4, copy), type);
assert.throws(() => ffi.toArrayBuffer(ptr, 4, copy), type);
}
}));
});
test('ffi getRawPointer returns raw addresses for byte sources', () => {
const buffer = Buffer.from([1, 2, 3]);
const arrayBuffer = new Uint8Array([4, 5, 6, 7]).buffer;