Commit43283237Recorded15 Jul 2026Repositorysigil-ffi

ffi: never dlclose a library from the GC finalizer

Message

Unloading a shared library when its handle object is garbage-collected is unsound: the GC only knows the handle is unreachable, not whether C code still holds raw pointers into the library (c-function fnptrs, callbacks registered with GLib main-context sources or signal handlers, atexit handlers, static data). Collecting a let-bound library handle in a dev-bundle build dlclosed libgtk-4 out from under a live GMainContext and crashed the next gmaincontextiteration. Python ctypes, JNA, and Guile all keep libraries mapped until process exit for the same reason. Explicit c-library-close remains for callers who know nothing references the library.

Bump 0.16.3.

Changed
 native/ffi.c | 17 ++++++++++++++---
 package.sgl  |  2 +-
 2 files changed, 15 insertions(+), 4 deletions(-)
Diff
native/ffi.cmodified
@@ -478,9 +478,20 @@ static void library_finalizer(void *data)
478
{
479
FfiLibrary *lib = (FfiLibrary *)data;
480
if (lib) {
481
if (!lib->closed && lib->handle) {
482
ffi_close_library(lib->handle);
483
}
+481
/* Deliberately do NOT dlclose here. Unloading a shared library
+482
* when its handle object is garbage-collected is unsound: the GC
+483
* only knows the handle is unreachable, not whether the program
+484
* (or C code acting on its behalf) still holds raw pointers into
+485
* the library - c-function fn_ptrs, callbacks registered with C
+486
* event loops (GLib main-context sources, signal handlers),
+487
* atexit handlers, static data. dlclose on collection unmapped
+488
* libgtk-4 out from under a live GMainContext and crashed the
+489
* next g_main_context_iteration (dev-bundle builds collect dead
+490
* locals promptly, so a let-bound handle died while its library
+491
* was still in use). Every mainstream FFI (Python ctypes, JNA,
+492
* Guile) keeps libraries mapped until process exit for the same
+493
* reason. Explicit c-library-close remains available for callers
+494
* who KNOW nothing references the library. */
495
free(lib->name);
496
free(lib);
497
}
package.sglmodified
@@ -6,7 +6,7 @@
6
7
(package
8
name: "sigil-ffi"
9
version: "0.16.2"
+9
version: "0.16.3"
10
sigil: "^0.17"
11
description: "Dynamic foreign function interface for Sigil"
12
url: "https://codeberg.org/sigil/sigil-ffi"