diff --git a/crates/bevy_dynamic_plugin/src/lib.rs b/crates/bevy_dynamic_plugin/src/lib.rs index 0a86b83b8a..961ae5a19f 100644 --- a/crates/bevy_dynamic_plugin/src/lib.rs +++ b/crates/bevy_dynamic_plugin/src/lib.rs @@ -1,5 +1,3 @@ -// FIXME(11590): remove this once the lint is fixed -#![allow(unsafe_op_in_unsafe_fn)] // FIXME(3492): remove once docs are ready #![allow(missing_docs)] diff --git a/crates/bevy_dynamic_plugin/src/loader.rs b/crates/bevy_dynamic_plugin/src/loader.rs index 9e57daaced..656e377849 100644 --- a/crates/bevy_dynamic_plugin/src/loader.rs +++ b/crates/bevy_dynamic_plugin/src/loader.rs @@ -21,14 +21,28 @@ pub enum DynamicPluginLoadError { /// The specified plugin must be linked against the exact same libbevy.so as this program. /// In addition the `_bevy_create_plugin` symbol must not be manually created, but instead created /// by deriving `DynamicPlugin` on a unit struct implementing [`Plugin`]. +/// +/// Dynamically loading plugins is orchestrated through dynamic linking. When linking against foreign +/// code, initialization routines may be run (as well as termination routines when the program exits). +/// The caller of this function is responsible for ensuring these routines are sound. For more +/// information, please see the safety section of [`libloading::Library::new`]. pub unsafe fn dynamically_load_plugin>( path: P, ) -> Result<(Library, Box), DynamicPluginLoadError> { - let lib = Library::new(path).map_err(DynamicPluginLoadError::Library)?; - let func: Symbol = lib - .get(b"_bevy_create_plugin") - .map_err(DynamicPluginLoadError::Plugin)?; - let plugin = Box::from_raw(func()); + // SAFETY: Caller must follow the safety requirements of Library::new. + let lib = unsafe { Library::new(path).map_err(DynamicPluginLoadError::Library)? }; + + // SAFETY: Loaded plugins are not allowed to specify `_bevy_create_plugin` symbol manually, but must + // instead automatically generate it through `DynamicPlugin`. + let func: Symbol = unsafe { + lib.get(b"_bevy_create_plugin") + .map_err(DynamicPluginLoadError::Plugin)? + }; + + // SAFETY: `func` is automatically generated and is guaranteed to return a pointer created using + // `Box::into_raw`. + let plugin = unsafe { Box::from_raw(func()) }; + Ok((lib, plugin)) } @@ -41,7 +55,8 @@ pub trait DynamicPluginExt { impl DynamicPluginExt for App { unsafe fn load_plugin>(&mut self, path: P) -> &mut Self { - let (lib, plugin) = dynamically_load_plugin(path).unwrap(); + // SAFETY: Follows the same safety requirements as `dynamically_load_plugin`. + let (lib, plugin) = unsafe { dynamically_load_plugin(path).unwrap() }; std::mem::forget(lib); // Ensure that the library is not automatically unloaded plugin.build(self); self