Version
main
Platform
Subsystem
ffi
What steps will reproduce the bug?
import { DynamicLibrary, suffix } from 'node:ffi';
// Use Node.js's prebuilt FFI test fixture, which exports sum_8_i32().
const libraryPath =
`../node/test/ffi/fixture_library/build/Release/ffi_test_library.${suffix}`;
const lib = new DynamicLibrary(libraryPath);
let reads = 0;
const signature = {
return: 'i32',
get arguments() {
const args = ++reads === 1 ?
Array(8).fill('i32') :
['i32'];
console.log(`read ${reads}: returning ${args.length} arguments`);
console.log(
new Error().stack.split('\n').slice(2, 4).join('\n'),
);
return args;
},
};
const fn = lib.getFunction('sum_8_i32', signature);
console.log(`JavaScript wrapper arity: ${fn.length}`);
console.log(`native sum_8_i32 result: ${fn(1)}`);
lib.close();
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
read 1: returning 8 arguments
at DynamicLibrary.getFunction (node:ffi:105:15)
at file:///Users/trivikram/workspace/test-repro/repro.js:27:16
JavaScript wrapper arity: 8
node:internal/ffi-shared-buffer:110
const err = new TypeError(msg);
^
TypeError: Invalid argument count: expected 8, got 1
at throwFFIArgError (node:internal/ffi-shared-buffer:110:15)
at throwFFIArgCountError (node:internal/ffi-shared-buffer:116:3)
at sum_8_i32 (node:internal/ffi-shared-buffer:515:7)
at file:///Users/trivikram/workspace/test-repro/repro.js:30:41
at ModuleJob.run (node:internal/modules/esm/module_job:569:25)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async node:internal/modules/esm/loader:650:26
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5) {
code: 'ERR_INVALID_ARG_VALUE'
}
Node should snapshot the signature once, producing an eight-argument wrapper that rejects the one-argument call.
For clean expected output, call the function with eight arguments:
console.log(
`native sum_8_i32 result: ${fn(1, 2, 3, 4, 5, 6, 7, 8)}`,
);
which will output the following instead of error
native sum_8_i32 result: 36
What do you see instead?
read 1: returning 8 arguments
at DynamicLibrary.getFunction (node:ffi:105:15)
at file:///Users/trivikram/workspace/test-repro/repro.js:27:16
read 2: returning 1 arguments
at DynamicLibrary.getFunction (node:ffi:106:41)
at file:///Users/trivikram/workspace/test-repro/repro.js:27:16
JavaScript wrapper arity: 1
native sum_8_i32 result: 1
The signature getter is evaluated twice, giving the native call interface eight arguments but the JavaScript wrapper only one, so an unsafe native call succeeds.
Additional information
No response
Version
main
Platform
Subsystem
ffi
What steps will reproduce the bug?
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
Node should snapshot the signature once, producing an eight-argument wrapper that rejects the one-argument call.
For clean expected output, call the function with eight arguments:
which will output the following instead of error
native sum_8_i32 result: 36What do you see instead?
The signature getter is evaluated twice, giving the native call interface eight arguments but the JavaScript wrapper only one, so an unsafe native call succeeds.
Additional information
No response