Skip to content

ffi: registerCallback(callback, signature) silently creates a void () callback #66996

Open

Description

@trivikr

Version

main

Platform

macOS

Subsystem

ffi

What steps will reproduce the bug?

import { dlopen, toBuffer } from 'node:ffi';

const { lib, functions: { qsort } } = dlopen(null, {
  qsort: { arguments: ['buffer', 'u64', 'u64', 'pointer'], return: 'void' },
});
const signature = { arguments: ['pointer', 'pointer'], return: 'i32' };

let received;
function compare(a, b) {
  received = [typeof a, typeof b];
  if (typeof a !== 'bigint') return -1; // never reaches native code
  return toBuffer(a, 4, false).readInt32LE() - toBuffer(b, 4, false).readInt32LE();
}

function sort(callback) {
  const data = new Int32Array([5, 3, 9, 1, 7]);
  qsort(data, 5n, 4n, callback);
  return `[${data}], compare received ${received}`;
}

console.log('(signature, fn):', sort(lib.registerCallback(signature, compare)));
console.log('(fn, signature):', sort(lib.registerCallback(compare, signature)));
lib.close();

How often does it reproduce? Is there a required condition?

Always

What is the expected behavior? Why is that the expected behavior?

library.registerCallback(compare, signature) should throw ERR_INVALID_ARG_TYPE. The documented order is registerCallback([signature,] callback), and the signature argument is validated when it comes first.

What do you see instead?

(signature, fn): [1,3,5,7,9], compare received bigint,bigint
(fn, signature): [5,3,9,1,7], compare received undefined,undefined

The reversed call returns a valid pointer for a void () closure. Native code calls it with its real signature. The JS function receives no arguments, its return value is dropped, and the caller reads a return register that libffi never wrote. That was 0 on macOS arm64, but the ABI doesn't define it.

Additional information

No response

Activity

  1. self-assigned this
    on Oct 25, 2026
  2. added
    ffiIssues and PRs related to experimental Foreign Function Interface support.
    on Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

ffiIssues and PRs related to experimental Foreign Function Interface support.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions