Skip to content

ffi: refCallback()/unrefCallback() crash (SIGSEGV) when called on a garbage-collected callback #64880

Description

@trivikr

Version

main

Platform

N/A

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

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