Solve (maybe) *another* soundness issue with Lua::scope

Callbacks should not be able to capture their arguments and hold onto them,
because the `&Lua` used in previous calls will not remain valid across calls.
One could imagine an API where the specific `&Lua` is simply stored inside the
`Scope` itself, but this is harder to do, and would (badly) encourage storing
references inside Lua userdata.

Ideally, the only way it should be possible to store Lua handles inside Lua
itself is through usafety or the `rental` crate or other self-borrowing
techniques to make references into 'static types.  If at all possible this
roadblock should stay, because reference types inside userdata are almost always
going to lead to a a memory leak, and if you accept the risks you should just
use `RegistryKey` with its manual removal.
This commit is contained in:
kyren 2018-08-05 20:03:47 -04:00
parent b35ff5fa12
commit 1a9c50f228
4 changed files with 34 additions and 9 deletions

View file

@ -43,15 +43,15 @@ impl<'scope> Scope<'scope> {
/// [`Lua::scope`]: struct.Lua.html#method.scope
pub fn create_function<'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>>
where
A: FromLuaMulti<'scope>,
R: ToLuaMulti<'scope>,
F: 'scope + Fn(&'scope Lua, A) -> Result<R>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'scope + Fn(&'lua Lua, A) -> Result<R>,
{
unsafe {
let f = Box::new(move |lua, args| {
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
});
let f = mem::transmute::<Callback<'scope, 'scope>, Callback<'scope, 'static>>(f);
let f = mem::transmute::<Callback<'lua, 'scope>, Callback<'lua, 'static>>(f);
let f = self.lua.create_callback(f)?;
let mut destructors = self.destructors.borrow_mut();
@ -85,9 +85,9 @@ impl<'scope> Scope<'scope> {
/// [`Scope::create_function`]: #method.create_function
pub fn create_function_mut<'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>>
where
A: FromLuaMulti<'scope>,
R: ToLuaMulti<'scope>,
F: 'scope + FnMut(&'scope Lua, A) -> Result<R>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'scope + FnMut(&'lua Lua, A) -> Result<R>,
{
let func = RefCell::new(func);
self.create_function(move |lua, args| {

View file

@ -0,0 +1,25 @@
extern crate rlua;
use rlua::*;
fn main() {
struct Test {
field: i32,
}
let lua = Lua::new();
lua.scope(|scope| {
let mut inner: Option<Table> = None;
let f = scope
.create_function_mut(move |lua, t: Table| {
//~^ error: cannot infer an appropriate lifetime for autoref due to conflicting requirements
if let Some(old) = inner.take() {
// Access old callback `Lua`.
}
inner = Some(t);
Ok(())
})
.unwrap();
f.call::<_, ()>(lua.create_table()).unwrap();
});
}

View file

@ -12,8 +12,8 @@ fn main() {
lua.scope(|scope| {
let f = scope
.create_function_mut(|_, t: Table| {
//~^^ error: borrowed data cannot be stored outside of its closure
outer = Some(t);
//~^ error: `*outer` does not live long enough
Ok(())
})
.unwrap();

View file

@ -12,8 +12,8 @@ fn main() {
let mut inner: Option<Table> = None;
let f = scope
.create_function_mut(|_, t: Table| {
//~^ error: cannot infer an appropriate lifetime for autoref due to conflicting requirements
inner = Some(t);
//~^ error: `inner` does not live long enough
Ok(())
})
.unwrap();