diff options
author | Alex Orlenko <zxteam@protonmail.com> | 2020-05-10 16:56:19 +0100 |
---|---|---|
committer | Alex Orlenko <zxteam@protonmail.com> | 2020-05-12 02:14:48 +0100 |
commit | 5a9a308790f77d72b192faf5442bd87f695bf3b7 (patch) | |
tree | f341d3c68e772160407aa6cd6d15dea5cef85bdf /src/lua.rs | |
parent | 1b2b94c80899876b5fa6e0882f3437dfb81746a2 (diff) | |
download | mlua-5a9a308790f77d72b192faf5442bd87f695bf3b7.zip |
Provide safe and unsafe Lua modes:
- In safe mode Lua would not have ability to load C code via `require` or `package.loadlib`
- Unsafe mode allows everything.
Diffstat (limited to 'src/lua.rs')
-rw-r--r-- | src/lua.rs | 207 |
1 files changed, 147 insertions, 60 deletions
@@ -43,6 +43,7 @@ pub struct Lua { main_state: *mut ffi::lua_State, extra: Arc<Mutex<ExtraData>>, ephemeral: bool, + safe: bool, // Lua has lots of interior mutability, should not be RefUnwindSafe _no_ref_unwind_safe: PhantomData<UnsafeCell<()>>, } @@ -108,17 +109,63 @@ impl Drop for Lua { } impl Lua { - /// Creates a new Lua state and loads standard library without the `debug` library. + /// Creates a new Lua state and loads the safe subset of the standard libraries. + /// + /// The created Lua state would have safety guarantees and would not allow to load unsafe + /// standard libraries or C modules. pub fn new() -> Lua { - Self::new_with(StdLib::ALL_NO_DEBUG) + mlua_expect!( + Self::new_with(StdLib::ALL_SAFE), + "can't create new safe Lua state" + ) + } + + /// Creates a new Lua state and loads all the standard libraries. + /// + /// The created Lua state would not have safety guarantees and would allow to load C modules. + pub unsafe fn unsafe_new() -> Lua { + Self::unsafe_new_with(StdLib::ALL) + } + + /// Creates a new Lua state and loads the specified safe subset of the standard libraries. + /// + /// Use the [`StdLib`] flags to specifiy the libraries you want to load. + /// + /// The created Lua state would have safety guarantees and would not allow to load unsafe + /// standard libraries or C modules. + /// + /// [`StdLib`]: struct.StdLib.html + pub fn new_with(libs: StdLib) -> Result<Lua> { + if libs.contains(StdLib::DEBUG) { + return Err(Error::SafetyError( + "the unsafe `debug` module can't be loaded using safe `new_with`".to_string(), + )); + } + #[cfg(feature = "luajit")] + { + if libs.contains(StdLib::FFI) { + return Err(Error::SafetyError( + "the unsafe `ffi` module can't be loaded using safe `new_with`".to_string(), + )); + } + } + + let mut lua = unsafe { Self::unsafe_new_with(libs) }; + + mlua_expect!(lua.disable_c_modules(), "Error during disabling C modules"); + lua.safe = true; + + Ok(lua) } - /// Creates a new Lua state and loads the specified set of standard libraries. + /// Creates a new Lua state and loads the specified subset of the standard libraries. /// /// Use the [`StdLib`] flags to specifiy the libraries you want to load. /// + /// The created Lua state would not have safety guarantees and would allow to load C modules. + /// /// [`StdLib`]: struct.StdLib.html - pub fn new_with(libs: StdLib) -> Lua { + pub unsafe fn unsafe_new_with(libs: StdLib) -> Lua { unsafe extern "C" fn allocator( extra_data: *mut c_void, ptr: *mut c_void, @@ -173,67 +220,28 @@ impl Lua { new_ptr } - unsafe { - let mem_info = Box::into_raw(Box::new(MemoryInfo { - used_memory: 0, - memory_limit: 0, - })); - - let state = ffi::lua_newstate(allocator, mem_info as *mut c_void); - - ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1); - ffi::lua_pop(state, 1); - - let mut lua = Lua::init_from_ptr(state); - lua.ephemeral = false; - lua.extra.lock().unwrap().mem_info = mem_info; - - mlua_expect!( - protect_lua_closure(lua.main_state, 0, 0, |state| { - load_from_std_lib(state, libs); - }), - "Error during loading standard libraries" - ); + let mem_info = Box::into_raw(Box::new(MemoryInfo { + used_memory: 0, + memory_limit: 0, + })); - lua - } - } + let state = ffi::lua_newstate(allocator, mem_info as *mut c_void); - /// Consumes and leaks `Lua` object, returning a static reference `&'static Lua`. - /// - /// This function is useful when the `Lua` object is supposed to live for the remainder - /// of the program's life. - /// In particular in asynchronous context this will allow to spawn Lua tasks to execute - /// in background. - /// - /// Dropping the returned reference will cause a memory leak. If this is not acceptable, - /// the reference should first be wrapped with the [`Lua::from_static`] function producing a `Lua`. - /// This `Lua` object can then be dropped which will properly release the allocated memory. - /// - /// [`Lua::from_static`]: #method.from_static - pub fn into_static(self) -> &'static Self { - Box::leak(Box::new(self)) - } + ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1); + ffi::lua_pop(state, 1); - /// Constructs a `Lua` from a static reference to it. - /// - /// # Safety - /// This function is unsafe because improper use may lead to memory problems or undefined behavior. - pub unsafe fn from_static(lua: &'static Lua) -> Self { - *Box::from_raw(lua as *const Lua as *mut Lua) - } + let mut lua = Lua::init_from_ptr(state); + lua.ephemeral = false; + lua.extra.lock().unwrap().mem_info = mem_info; - /// Loads the specified set of standard libraries into an existing Lua state. - /// - /// Use the [`StdLib`] flags to specifiy the libraries you want to load. - /// - /// [`StdLib`]: struct.StdLib.html - pub fn load_from_std_lib(&self, libs: StdLib) -> Result<()> { - unsafe { - protect_lua_closure(self.main_state, 0, 0, |state| { + mlua_expect!( + protect_lua_closure(lua.main_state, 0, 0, |state| { load_from_std_lib(state, libs); - }) - } + }), + "Error during loading standard libraries" + ); + + lua } /// Constructs a new Lua instance from the existing state. @@ -292,10 +300,62 @@ impl Lua { main_state: main_state, extra: extra, ephemeral: true, + safe: false, _no_ref_unwind_safe: PhantomData, } } + /// Loads the specified subset of the standard libraries into an existing Lua state. + /// + /// Use the [`StdLib`] flags to specifiy the libraries you want to load. + /// + /// [`StdLib`]: struct.StdLib.html + pub fn load_from_std_lib(&self, libs: StdLib) -> Result<()> { + if self.safe && libs.contains(StdLib::DEBUG) { + return Err(Error::SafetyError( + "the unsafe `debug` module can't be loaded in safe mode".to_string(), + )); + } + #[cfg(feature = "luajit")] + { + if self.safe && libs.contains(StdLib::FFI) { + return Err(Error::SafetyError( + "the unsafe `ffi` module can't be loaded in safe mode".to_string(), + )); + } + } + + unsafe { + protect_lua_closure(self.main_state, 0, 0, |state| { + load_from_std_lib(state, libs); + }) + } + } + + /// Consumes and leaks `Lua` object, returning a static reference `&'static Lua`. + /// + /// This function is useful when the `Lua` object is supposed to live for the remainder + /// of the program's life. + /// In particular in asynchronous context this will allow to spawn Lua tasks to execute + /// in background. + /// + /// Dropping the returned reference will cause a memory leak. If this is not acceptable, + /// the reference should first be wrapped with the [`Lua::from_static`] function producing a `Lua`. + /// This `Lua` object can then be dropped which will properly release the allocated memory. + /// + /// [`Lua::from_static`]: #method.from_static + pub fn into_static(self) -> &'static Self { + Box::leak(Box::new(self)) + } + + /// Constructs a `Lua` from a static reference to it. + /// + /// # Safety + /// This function is unsafe because improper use may lead to memory problems or undefined behavior. + pub unsafe fn from_static(lua: &'static Lua) -> Self { + *Box::from_raw(lua as *const Lua as *mut Lua) + } + // Executes module entrypoint function, which returns only one Value. // The returned value then pushed to the Lua stack. #[doc(hidden)] @@ -1556,9 +1616,36 @@ impl Lua { main_state: self.main_state, extra: self.extra.clone(), ephemeral: true, + safe: self.safe, _no_ref_unwind_safe: PhantomData, } } + + fn disable_c_modules(&self) -> Result<()> { + let package: Table = self.globals().get("package")?; + + package.set( + "loadlib", + self.create_function(|_, ()| -> Result<()> { + Err(Error::SafetyError( + "package.loadlib is disabled in safe mode".to_string(), + )) + })?, + )?; + + #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] + let searchers: Table = package.get("searchers")?; + #[cfg(any(feature = "lua51", feature = "luajit"))] + let searchers: Table = package.get("loaders")?; + + let loader = self.create_function(|_, ()| Ok("\n\tcan't load C modules in safe mode"))?; + + // The third and fourth searchers looks for a loader as a C library + searchers.raw_set(3, loader.clone())?; + searchers.raw_remove(4)?; + + Ok(()) + } } /// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks. |