diff --git a/src/ffi/data.cc b/src/ffi/data.cc index dbeba94cd1b9..8cf2e025f303 100644 --- a/src/ffi/data.cc +++ b/src/ffi/data.cc @@ -50,7 +50,10 @@ Maybe GetValidatedSize(Environment* env, return Nothing(); } - if (length > static_cast(std::numeric_limits::max())) { + // Values beyond Number.MAX_SAFE_INTEGER may already have been rounded + // by the caller. On 32-bit platforms SIZE_MAX is the tighter bound. + if (length > kMaxSafeJsInteger || + length > static_cast(std::numeric_limits::max())) { THROW_ERR_OUT_OF_RANGE(env, "The %s is too large", label); return Nothing(); } diff --git a/test/ffi/test-ffi-memory.js b/test/ffi/test-ffi-memory.js index 53fe3928029b..8c42b677cbf9 100644 --- a/test/ffi/test-ffi-memory.js +++ b/test/ffi/test-ffi-memory.js @@ -322,8 +322,13 @@ test('ffi validates memory access arguments', () => { assert.throws(() => ffi.toBuffer(maxPointer, 8), /pointer and length exceed the platform address range/); assert.throws(() => ffi.toArrayBuffer(maxPointer, 8), /pointer and length exceed the platform address range/); - assert.throws(() => ffi.toBuffer(1n, bufferConstants.MAX_LENGTH + 1), { code: 'ERR_BUFFER_TOO_LARGE' }); - assert.throws(() => ffi.toArrayBuffer(1n, bufferConstants.MAX_LENGTH + 1), { code: 'ERR_BUFFER_TOO_LARGE' }); + + // If MAX_LENGTH is Number.MAX_SAFE_INTEGER, MAX_LENGTH + 1 is an unsafe + // integer and is rejected before the buffer length is checked. + if (bufferConstants.MAX_LENGTH < Number.MAX_SAFE_INTEGER) { + assert.throws(() => ffi.toBuffer(1n, bufferConstants.MAX_LENGTH + 1), { code: 'ERR_BUFFER_TOO_LARGE' }); + assert.throws(() => ffi.toArrayBuffer(1n, bufferConstants.MAX_LENGTH + 1), { code: 'ERR_BUFFER_TOO_LARGE' }); + } if (process.arch === 'ia32' || process.arch === 'arm') { assert.throws(() => ffi.toBuffer(2n ** 32n, 0), /platform pointer range/); @@ -331,6 +336,21 @@ test('ffi validates memory access arguments', () => { })); }); +test('ffi rejects unsafe integers as an offset or length', () => { + withAllocations(common.mustCall((alloc) => { + const ptr = alloc(8); + const range = { code: 'ERR_OUT_OF_RANGE' }; + + // On 64-bit platforms SIZE_MAX rounds up to 2 ** 64 as a double. + for (const value of [Number.MAX_SAFE_INTEGER + 1, 2 ** 64]) { + assert.throws(() => ffi.getUint8(ptr, value), range); + assert.throws(() => ffi.setUint8(ptr, value, 42), range); + assert.throws(() => ffi.toBuffer(ptr, value), range); + assert.throws(() => ffi.toArrayBuffer(ptr, value), range); + } + })); +}); + test('ffi memory helpers reject missing required arguments', () => { const widths = ['Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Float32', 'Float64'];