Version
main
Platform
Subsystem
ffi
What steps will reproduce the bug?
repro.c
#include <stdint.h>
int32_t noop(int32_t x) { return x; }
Compile it by running
$ cc -shared -o repro.so repro.c # or .dylib on macOS
repro.js
import { DynamicLibrary, suffix } from 'node:ffi';
const lib = new DynamicLibrary(`./repro.${suffix}`);
let cb = (x) => x + 1;
const ptr = lib.registerCallback({ arguments: ['i32'], return: 'i32' }, cb);
// Allow GC to collect the callback backing function
lib.unrefCallback(ptr);
cb = null;
globalThis.gc();
// Second unref (or refCallback) on the now-empty weak handle → SIGSEGV
lib.unrefCallback(ptr);
lib.close();
Run it using
$ node --experimental-ffi --expose-gc --no-warnings repro.js
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
unrefCallback/refCallback on a collected callback should throw ERR_INVALID_ARG_VALUE("Callback not found").
What do you see instead?
segmentation fault (core dumped)
The process crashes with SIGSEGV inside v8::PersistentBase::SetWeak() (or ClearWeak() for refCallback) because the weak handle slot is already cleared after GC.
Additional information
No response
Version
main
Platform
Subsystem
ffi
What steps will reproduce the bug?
repro.cCompile it by running
$ cc -shared -o repro.so repro.c # or .dylib on macOSrepro.jsRun it using
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
unrefCallback/refCallbackon a collected callback should throwERR_INVALID_ARG_VALUE("Callback not found").What do you see instead?
segmentation fault (core dumped)The process crashes with
SIGSEGVinsidev8::PersistentBase::SetWeak()(orClearWeak()for refCallback) because the weak handle slot is already cleared after GC.Additional information
No response