Skip to content

Commit ebb66f2

Browse files
committed
ffi: reject detached ArrayBufferViews
Detached ArrayBuffers were rejected with ERR_INVALID_ARG_VALUE, but detached views were not: typed arrays exported zero bytes and DataViews threw a bare TypeError from byteLength. Track detachment in ArrayBufferViewContents::Read() and skip the JS length check for detached buffers, so every detached input is rejected. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: codex:gpt-5.6-sol
1 parent 9caf674 commit ebb66f2

3 files changed

Lines changed: 30 additions & 1 deletion

File tree

lib/ffi.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
'use strict';
22

33
const {
4+
ArrayBufferPrototypeGetDetached,
5+
DataViewPrototypeGetBuffer,
46
FunctionPrototypeCall,
57
ObjectDefineProperty,
68
ObjectFreeze,
79
ObjectGetOwnPropertyDescriptor,
810
ObjectKeys,
911
ObjectPrototypeToString,
1012
SymbolDispose,
13+
TypedArrayPrototypeGetBuffer,
1114
} = primordials;
1215
const { Buffer } = require('buffer');
1316
const { emitExperimentalWarning } = require('internal/util');
1417
const {
18+
isDataView,
1519
isArrayBufferView,
20+
isSharedArrayBuffer,
1621
} = require('internal/util/types');
1722
const {
1823
codes: {
@@ -284,7 +289,13 @@ function exportArrayBufferView(source, data, len) {
284289

285290
validateInteger(len, 'len', 0);
286291

287-
if (len < source.byteLength) {
292+
// Reading byteLength throws for a detached DataView. Let the native binding
293+
// reject detached views consistently with detached ArrayBuffers.
294+
const buffer = isDataView(source) ?
295+
DataViewPrototypeGetBuffer(source) : TypedArrayPrototypeGetBuffer(source);
296+
if ((isSharedArrayBuffer(buffer) ||
297+
!ArrayBufferPrototypeGetDetached(buffer)) &&
298+
len < source.byteLength) {
288299
throw new ERR_OUT_OF_RANGE('len', `>= ${source.byteLength}`, len);
289300
}
290301

src/util-inl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ ArrayBufferViewContents<T, S>::ArrayBufferViewContents(
573573
template <typename T, size_t S>
574574
void ArrayBufferViewContents<T, S>::Read(v8::Local<v8::ArrayBufferView> abv) {
575575
static_assert(sizeof(T) == 1, "Only supports one-byte data at the moment");
576+
was_detached_ = abv->Buffer()->WasDetached();
576577
length_ = abv->ByteLength();
577578
if (length_ > sizeof(stack_storage_) || abv->HasBuffer()) {
578579
auto buf_data = abv->Buffer()->Data();

test/ffi/test-ffi-memory.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,23 @@ test('ffi validates memory access arguments', () => {
251251
assert.throws(() => ffi.exportArrayBufferView('bad', ptr, 4), { code: 'ERR_INVALID_ARG_TYPE' });
252252
assert.throws(() => ffi.exportArrayBufferView(new Uint8Array([1]), ptr, -1), { code: 'ERR_OUT_OF_RANGE' });
253253
assert.throws(() => ffi.exportArrayBufferView(new Uint8Array([1, 2]), ptr, 1), { code: 'ERR_OUT_OF_RANGE' });
254+
ffi.exportArrayBufferView(new Uint8Array(new SharedArrayBuffer(1)), ptr, 1);
255+
256+
const detachedArrayBuffer = new ArrayBuffer(1);
257+
detachedArrayBuffer.transfer();
258+
assert.throws(() => ffi.exportArrayBuffer(detachedArrayBuffer, ptr, 1), {
259+
code: 'ERR_INVALID_ARG_VALUE',
260+
});
261+
262+
for (const View of [Uint8Array, DataView]) {
263+
const arrayBuffer = new ArrayBuffer(1);
264+
const view = new View(arrayBuffer);
265+
arrayBuffer.transfer();
266+
assert.throws(() => ffi.exportArrayBufferView(view, ptr, 1), {
267+
code: 'ERR_INVALID_ARG_VALUE',
268+
});
269+
}
270+
254271
assert.throws(() => ffi.toBuffer(maxPointer, 8), /pointer and length exceed the platform address range/);
255272
assert.throws(() => ffi.toArrayBuffer(maxPointer, 8), /pointer and length exceed the platform address range/);
256273
assert.throws(() => ffi.toBuffer(1n, bufferConstants.MAX_LENGTH + 1), { code: 'ERR_BUFFER_TOO_LARGE' });

0 commit comments

Comments
 (0)