mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
Defer unloading box-local extension DLLs on Windows
box_entry_free could run before other T_DATA objects are finalized in rb_objspace_call_finalizer, and FreeLibrary there unmapped the rb_data_type_t and dfree functions that later finalization still dereferences, crashing any boxed process that leaves extension objects alive at exit. Keep the copied DLLs loaded until ruby_vm_destruct, then unload and delete them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3e1ea919f4
commit
1c664d8024
4 files changed
+41
-15
Notes:
git
2026-08-27 01:42:46 +00:00
No files matched your search
@@ -65,7 +65,6 @@ bool ruby_box_crashed = false; // extern, changed only in vm.c
|
||||
|
||||
VALUE rb_resolve_feature_path(VALUE klass, VALUE fname);
|
||||
static VALUE rb_box_inspect(VALUE obj);
|
||||
static void cleanup_all_local_extensions(VALUE libmap);
|
||||
|
||||
void
|
||||
rb_box_set_gem_flags(rb_box_gem_flags_t *flags)
|
||||
@@ -303,8 +302,6 @@ box_entry_free(void *ptr)
|
||||
st_foreach(box->classext_cow_classes, free_classext_for_box, (st_data_t)box);
|
||||
}
|
||||
|
||||
cleanup_all_local_extensions(box->ruby_dln_libmap);
|
||||
|
||||
free_box_st_tables(ptr);
|
||||
SIZED_FREE(box);
|
||||
}
|
||||
@@ -808,24 +805,48 @@ rb_box_cleanup_local_extension(VALUE cleanup)
|
||||
(void)p;
|
||||
}
|
||||
|
||||
static int
|
||||
cleanup_local_extension_i(VALUE key, VALUE value, VALUE arg)
|
||||
#if defined(_WIN32)
|
||||
struct box_local_ext_list {
|
||||
struct box_local_ext_list *next;
|
||||
HMODULE handle;
|
||||
};
|
||||
static struct box_local_ext_list *box_local_exts;
|
||||
#endif
|
||||
|
||||
void
|
||||
rb_box_defer_unload_local_extension(void *handle)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
HMODULE h = (HMODULE)NUM2PTR(value);
|
||||
WCHAR module_path[MAXPATHLEN];
|
||||
DWORD len = GetModuleFileNameW(h, module_path, numberof(module_path));
|
||||
|
||||
FreeLibrary(h);
|
||||
if (len > 0 && len < numberof(module_path)) DeleteFileW(module_path);
|
||||
/* A box-local copy of an extension DLL must stay loaded as long as
|
||||
* objects created by it can be finalized; unloading is deferred to
|
||||
* rb_box_unload_local_extensions after the objspace is destructed.
|
||||
* The loaded copy cannot be deleted on Windows, so the file is also
|
||||
* removed there instead of in box_ext_cleanup_free. */
|
||||
struct box_local_ext_list *ext = malloc(sizeof(struct box_local_ext_list));
|
||||
if (!ext) return;
|
||||
ext->handle = (HMODULE)handle;
|
||||
ext->next = box_local_exts;
|
||||
box_local_exts = ext;
|
||||
#endif
|
||||
return ST_DELETE;
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_all_local_extensions(VALUE libmap)
|
||||
void
|
||||
rb_box_unload_local_extensions(void)
|
||||
{
|
||||
rb_hash_foreach(libmap, cleanup_local_extension_i, 0);
|
||||
#if defined(_WIN32)
|
||||
struct box_local_ext_list *ext = box_local_exts;
|
||||
box_local_exts = NULL;
|
||||
while (ext) {
|
||||
struct box_local_ext_list *next = ext->next;
|
||||
WCHAR module_path[MAXPATHLEN];
|
||||
DWORD len = GetModuleFileNameW(ext->handle, module_path, numberof(module_path));
|
||||
|
||||
FreeLibrary(ext->handle);
|
||||
if (len > 0 && len < numberof(module_path)) DeleteFileW(module_path);
|
||||
free(ext);
|
||||
ext = next;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
VALUE
|
||||
|
||||
@@ -90,6 +90,8 @@ VALUE rb_get_box_object(rb_box_t *ns);
|
||||
|
||||
VALUE rb_box_local_extension(VALUE box, VALUE fname, VALUE path, VALUE *cleanup);
|
||||
void rb_box_cleanup_local_extension(VALUE cleanup);
|
||||
void rb_box_defer_unload_local_extension(void *handle);
|
||||
void rb_box_unload_local_extensions(void);
|
||||
|
||||
void rb_initialize_mandatory_boxes(void);
|
||||
void rb_box_init_done(void);
|
||||
|
||||
@@ -1224,6 +1224,7 @@ load_ext(VALUE path, VALUE fname)
|
||||
void *handle = dln_load_feature(RSTRING_PTR(loaded), RSTRING_PTR(fname));
|
||||
if (cleanup) {
|
||||
rb_box_cleanup_local_extension(cleanup);
|
||||
rb_box_defer_unload_local_extension(handle);
|
||||
}
|
||||
RB_GC_GUARD(loaded);
|
||||
RB_GC_GUARD(fname);
|
||||
|
||||
Reference in new issue
Block a user