ffi: validate fast integer argument ranges · nodejs/node@0fb1d2b · GitHub
Skip to content

Commit 0fb1d2b

Browse files
trivikraduh95
authored andcommitted
ffi: validate fast integer argument ranges
Validate narrow integer and 64-bit BigInt arguments before entering the Fast API trampoline. This prevents out-of-range values from being silently truncated or wrapped and matches the generic FFI path. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.6-sol PR-URL: #64614 Fixes: #64613 Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent bbd6fc5 commit 0fb1d2b

5 files changed

Lines changed: 135 additions & 15 deletions

File tree

lib/internal/ffi/fast-api.js

Lines changed: 58 additions & 10 deletions

src/ffi/fast.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,20 @@ bool SignatureNeedsRawPointerConversions(const FFIFunction& fn) {
160160
return false;
161161
}
162162

163+
bool SignatureNeedsFastIntegerValidation(const FFIFunction& fn) {
164+
// V8 widens narrow integers to 32 bits and truncates BigInts to 64 bits for
165+
// Fast API calls. These types need a JS range check before the trampoline.
166+
for (const std::string& name : fn.arg_type_names) {
167+
if (name == "bool" || name == "char" || name == "i8" || name == "int8" ||
168+
name == "u8" || name == "uint8" || name == "i16" || name == "int16" ||
169+
name == "u16" || name == "uint16" || name == "i64" || name == "int64" ||
170+
name == "u64" || name == "uint64") {
171+
return true;
172+
}
173+
}
174+
return false;
175+
}
176+
163177
bool IsPointerTypeName(const std::string& name) {
164178
// `pointer`, `ptr`, and `function` all use the same uintptr ABI slot; only
165179
// the public type spelling differs.

src/ffi/fast.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ struct FastFFIMetadata {
5959
bool IsFastCallSupported();
6060

6161
bool SignatureNeedsRawPointerConversions(const FFIFunction& fn);
62+
bool SignatureNeedsFastIntegerValidation(const FFIFunction& fn);
6263
bool IsPointerTypeName(const std::string& name);
6364
bool SignatureNeedsFastBufferInvoke(const FFIFunction& fn);
6465
std::shared_ptr<FFIFunction> CloneWithFastBufferArgNames(

src/node_ffi.cc

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,11 @@ MaybeLocal<Function> DynamicLibrary::CreateFunction(
253253
bool use_fast_api = info->fast_metadata != nullptr;
254254
bool use_sb = !use_fast_api && IsSBEligibleSignature(*fn);
255255
bool has_ptr_args = use_sb && SignatureHasPointerArgs(*fn);
256-
// Fast API signatures that still accept JS pointer-like values need a JS
257-
// wrapper with the native type names attached as hidden metadata.
258-
bool needs_raw_pointer_conversions =
259-
use_fast_api && SignatureNeedsRawPointerConversions(*fn);
256+
// Fast API signatures that need JS-side argument conversion or range checks
257+
// use a wrapper with the native type names attached as hidden metadata.
258+
bool needs_fast_argument_wrapper =
259+
use_fast_api && (SignatureNeedsRawPointerConversions(*fn) ||
260+
SignatureNeedsFastIntegerValidation(*fn));
260261
// A single pointer-like parameter can get a separate Buffer-aware Fast API
261262
// entrypoint so Buffer calls avoid JS pointer extraction.
262263
bool needs_fast_buffer_invoke =
@@ -381,7 +382,7 @@ MaybeLocal<Function> DynamicLibrary::CreateFunction(
381382
}
382383
}
383384

384-
if (needs_raw_pointer_conversions || needs_fast_buffer_invoke) {
385+
if (needs_fast_argument_wrapper || needs_fast_buffer_invoke) {
385386
// Fast API wrappers need only the parameter type names. Result conversion
386387
// is still handled by V8's CFunction metadata, unlike the SharedBuffer path
387388
// which must also know how to read slot 0.
Lines changed: 56 additions & 0 deletions

0 commit comments

Comments
 (0)