Skip to content

Commit 562f05f

Browse files
trivikraduh95
authored andcommitted
ffi: validate fast pointer BigInt argument ranges
Optimized V8 fast API calls truncate out-of-range pointer BigInts. Validate them against uintptrMax before invoking the raw function so optimized calls match the generic and shared-buffer paths. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: codex:gpt-5.6-sol PR-URL: #65032 Fixes: #65031 Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 1fa1ea8 commit 562f05f

3 files changed

Lines changed: 45 additions & 3 deletions

File tree

lib/internal/ffi/fast-api.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const {
2424
getRawPointer,
2525
kFastArguments,
2626
kFastBufferInvoke,
27+
uintptrMax,
2728
} = internalBinding('ffi');
2829

2930
const {
@@ -110,6 +111,14 @@ function needsPointerConversion(type) {
110111
needsNullPointerConversion(type) || needsStringPointerConversion(type);
111112
}
112113

114+
function validateFastPointerArg(type, value, index) {
115+
if (needsPointerConversion(type) && typeof value === 'bigint' &&
116+
(value < 0n || value > uintptrMax)) {
117+
throwFFIArgError(
118+
`Argument ${index} must be a non-negative pointer bigint`);
119+
}
120+
}
121+
113122
function hasStringPointerArg(type, value) {
114123
return typeof value === 'string' && needsStringPointerConversion(type);
115124
}
@@ -159,6 +168,7 @@ function getStringConversionPointer(state, value, index) {
159168
}
160169

161170
function convertPointerArg(type, value, stringState, index) {
171+
validateFastPointerArg(type, value, index);
162172
if (needsNullPointerConversion(type) &&
163173
(value === null || value === undefined)) {
164174
return 0n;
@@ -261,6 +271,7 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, owner) {
261271
throwFFIArgCountError(1, arguments.length);
262272
}
263273
validateFastIntegerArg(t0, a0, 0);
274+
validateFastPointerArg(t0, a0, 0);
264275
let arg = a0;
265276
if (needsNullPointerConversion(t0) &&
266277
(arg === null || arg === undefined)) {

src/node_ffi.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,9 +1341,9 @@ static void Initialize(Local<Object> target,
13411341
Boolean::New(isolate, CHAR_MIN < 0))
13421342
.Check();
13431343

1344-
// The shared-buffer fast path uses `uintptrMax` to reject pointer BigInts
1345-
// that would otherwise be silently truncated by `ReadFFIArgFromBuffer`'s
1346-
// `memcpy(..., type->size, ...)` on 32-bit platforms. The slow path
1344+
// The JavaScript fast paths use `uintptrMax` to reject pointer BigInts that
1345+
// would otherwise be silently truncated by V8 or, on 32-bit platforms, by
1346+
// `ReadFFIArgFromBuffer`'s `memcpy(..., type->size, ...)`. The slow path
13471347
// rejects the same values through `ToFFIArgument`.
13481348
target
13491349
->Set(context,

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,34 @@ test('fast FFI validates integer argument ranges', () => {
6868
lib.close();
6969
}
7070
});
71+
72+
test('fast FFI validates pointer BigInt ranges', () => {
73+
const lib = new ffi.DynamicLibrary(libraryPath);
74+
try {
75+
for (const type of ['pointer', 'ptr', 'string', 'str',
76+
'buffer', 'arraybuffer']) {
77+
const identityPointer = lib.getFunction('identity_pointer', {
78+
arguments: [type],
79+
return: 'pointer',
80+
});
81+
const sumBuffer = lib.getFunction('sum_buffer', {
82+
arguments: [type, 'u64'],
83+
return: 'u64',
84+
});
85+
function callSingle(value) { return identityPointer(value); }
86+
87+
function callMultiple(value) { return sumBuffer(value, 0n); }
88+
89+
optimize(callSingle, 0n);
90+
optimize(callMultiple, 0n);
91+
92+
const expect = { code: 'ERR_INVALID_ARG_VALUE' };
93+
for (const call of [callSingle, callMultiple]) {
94+
assert.throws(() => call(-1n), expect);
95+
assert.throws(() => call((2n ** 64n) + 5n), expect);
96+
}
97+
}
98+
} finally {
99+
lib.close();
100+
}
101+
});

0 commit comments

Comments
 (0)