fix(runtime): prevent dlclose() to avoid SIGSEGV on runtime teardown

Wrap LoadedRuntime._library in ManuallyDrop so dlclose() is never called.
dlclose() unmaps the library code; thread-local destructors inside liblua.so
then SIGSEGV when they try to run against the unmapped addresses.

Also filter out non-.lua plugins in the Lua runtime's discover_plugins()
so liblua.so does not attempt to load Rune plugins.
This commit is contained in:
2026-04-06 02:26:12 +02:00
parent de74cac67d
commit a6e94deb3c
2 changed files with 13 additions and 3 deletions

View File

@@ -10,6 +10,7 @@
//! Note: This module is infrastructure for the runtime architecture. Full integration //! Note: This module is infrastructure for the runtime architecture. Full integration
//! is pending Phase 5 (AUR Packaging) when runtime packages will be available. //! is pending Phase 5 (AUR Packaging) when runtime packages will be available.
use std::mem::ManuallyDrop;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
@@ -69,8 +70,11 @@ pub struct ScriptRuntimeVTable {
pub struct LoadedRuntime { pub struct LoadedRuntime {
/// Runtime name (for logging) /// Runtime name (for logging)
name: &'static str, name: &'static str,
/// Keep library alive /// Keep library alive — wrapped in ManuallyDrop so we never call dlclose().
_library: Arc<Library>, /// dlclose() unmaps the library code; any thread-local destructors inside the
/// library then SIGSEGV when they try to run against the unmapped addresses.
/// Runtime libraries live for the process lifetime, so leaking the handle is safe.
_library: ManuallyDrop<Arc<Library>>,
/// Runtime vtable /// Runtime vtable
vtable: &'static ScriptRuntimeVTable, vtable: &'static ScriptRuntimeVTable,
/// Runtime handle (state) /// Runtime handle (state)
@@ -138,7 +142,7 @@ impl LoadedRuntime {
Ok(Self { Ok(Self {
name, name,
_library: library, _library: ManuallyDrop::new(library),
vtable, vtable,
handle, handle,
providers, providers,
@@ -166,6 +170,8 @@ impl LoadedRuntime {
impl Drop for LoadedRuntime { impl Drop for LoadedRuntime {
fn drop(&mut self) { fn drop(&mut self) {
(self.vtable.drop)(self.handle); (self.vtable.drop)(self.handle);
// Do NOT drop _library: ManuallyDrop ensures dlclose() is never called.
// See field comment for rationale.
} }
} }

View File

@@ -186,6 +186,10 @@ pub fn discover_plugins(
match PluginManifest::load(&manifest_path) { match PluginManifest::load(&manifest_path) {
Ok(manifest) => { Ok(manifest) => {
// Skip plugins whose entry point is not a Lua file
if !manifest.plugin.entry.ends_with(".lua") {
continue;
}
let id = manifest.plugin.id.clone(); let id = manifest.plugin.id.clone();
if plugins.contains_key(&id) { if plugins.contains_key(&id) {
eprintln!( eprintln!(