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
//! is pending Phase 5 (AUR Packaging) when runtime packages will be available.
use std::mem::ManuallyDrop;
use std::path::{Path, PathBuf};
use std::sync::Arc;
@@ -69,8 +70,11 @@ pub struct ScriptRuntimeVTable {
pub struct LoadedRuntime {
/// Runtime name (for logging)
name: &'static str,
/// Keep library alive
_library: Arc<Library>,
/// Keep library alive — wrapped in ManuallyDrop so we never call dlclose().
/// 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
vtable: &'static ScriptRuntimeVTable,
/// Runtime handle (state)
@@ -138,7 +142,7 @@ impl LoadedRuntime {
Ok(Self {
name,
_library: library,
_library: ManuallyDrop::new(library),
vtable,
handle,
providers,
@@ -166,6 +170,8 @@ impl LoadedRuntime {
impl Drop for LoadedRuntime {
fn drop(&mut self) {
(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) {
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();
if plugins.contains_key(&id) {
eprintln!(