Skip to content

Commit 4896eb3

Browse files
committed
ffi: validate pointer ranges in optimized calls
On x64 SysV and arm64, a Fast API signature with two or more arguments that uses only pointer and float types got no JS wrapper. V8 then truncated pointer BigInts silently. With `-1n` or `2n ** 64n + 5n`, cold calls threw ERR_INVALID_ARG_VALUE, while optimized calls passed 0xffffffffffffffff or 5 to native code. Use the argument wrapper for any signature with a pointer argument. Signed-off-by: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Assisted-by: claude:opus-5.5
1 parent 75e4bbe commit 4896eb3

2 files changed

Lines changed: 12 additions & 1 deletion

File tree

‎src/node_ffi.cc‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,12 @@ MaybeLocal<Function> DynamicLibrary::CreateFunction(
294294
bool has_ptr_args = use_sb && SignatureHasPointerArgs(*fn);
295295
// Signatures that need JS-side conversion or validation use a wrapper, as
296296
// do all fast signatures on platforms without a native library guard.
297+
// Pointer arguments need the wrapper's range check because V8 truncates
298+
// BigInts passed to Fast API uint64 parameters.
297299
bool needs_fast_argument_wrapper =
298300
use_fast_api && (SignatureNeedsRawPointerConversions(*fn) ||
299301
SignatureNeedsFastIntegerValidation(*fn) ||
302+
SignatureHasPointerArgs(*fn) ||
300303
!info->fast_metadata->guards_library);
301304
// A single pointer-like parameter can get a separate Buffer-aware Fast API
302305
// entrypoint so Buffer calls avoid JS pointer extraction.

‎test/ffi/test-ffi-fast-integer-validation.js‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,23 @@ test('fast FFI validates pointer BigInt ranges', () => {
8383
arguments: [type, 'u64'],
8484
return: 'u64',
8585
});
86+
// Only pointer-like arguments, so no integer type forces the wrapper.
87+
const stringConcat = lib.getFunction('string_concat', {
88+
arguments: [type, type],
89+
return: 'pointer',
90+
});
8691
function callSingle(value) { return identityPointer(value); }
8792

8893
function callMultiple(value) { return sumBuffer(value, 0n); }
8994

95+
function callPointers(value) { return stringConcat(value, 0n); }
96+
9097
optimize(callSingle, 0n);
9198
optimize(callMultiple, 0n);
99+
optimize(callPointers, 0n);
92100

93101
const expect = { code: 'ERR_INVALID_ARG_VALUE' };
94-
for (const call of [callSingle, callMultiple]) {
102+
for (const call of [callSingle, callMultiple, callPointers]) {
95103
assert.throws(() => call(-1n), expect);
96104
assert.throws(() => call((2n ** 64n) + 5n), expect);
97105
}

0 commit comments

Comments
 (0)