Fix for a soundness bug around scope, don't allow callback parameters to escape

Also includes other fixes for compiletest_rs failures, and a small reorg of tests
This commit is contained in:
kyren 2018-08-05 11:54:33 -04:00
parent 8366960368
commit a2615a8cbb
8 changed files with 152 additions and 104 deletions

View File

@ -61,6 +61,7 @@ impl<'scope> Scope<'scope> {
A: FromLuaMulti<'callback>,
R: ToLuaMulti<'callback>,
F: 'scope + Fn(&'callback Lua, A) -> Result<R>,
'scope: 'callback,
{
unsafe {
let f = Box::new(move |lua, args| {
@ -106,6 +107,7 @@ impl<'scope> Scope<'scope> {
A: FromLuaMulti<'callback>,
R: ToLuaMulti<'callback>,
F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
'scope: 'callback,
{
let func = RefCell::new(func);
self.create_function(move |lua, args| {

View File

@ -1,21 +1,17 @@
mod function;
mod scope;
mod string;
mod table;
mod thread;
mod types;
mod userdata;
use std::cell::Cell;
use std::iter::FromIterator;
use std::panic::catch_unwind;
use std::rc::Rc;
use std::sync::Arc;
use std::{error, fmt};
use {
Error, ExternalError, Function, Lua, Nil, Result, String, Table, UserData, UserDataMethods,
Value, Variadic,
};
use {Error, ExternalError, Function, Lua, Nil, Result, String, Table, UserData, Value, Variadic};
#[test]
fn test_load() {
@ -620,102 +616,6 @@ fn test_mismatched_registry_key() {
};
}
#[test]
fn scope_func() {
let lua = Lua::new();
let rc = Rc::new(Cell::new(0));
lua.scope(|scope| {
let r = rc.clone();
let f = scope
.create_function(move |_, ()| {
r.set(42);
Ok(())
})
.unwrap();
lua.globals().set("bad", f.clone()).unwrap();
f.call::<_, ()>(()).unwrap();
assert_eq!(Rc::strong_count(&rc), 2);
});
assert_eq!(rc.get(), 42);
assert_eq!(Rc::strong_count(&rc), 1);
match lua
.globals()
.get::<_, Function>("bad")
.unwrap()
.call::<_, ()>(())
{
Err(Error::CallbackError { .. }) => {}
r => panic!("improper return for destructed function: {:?}", r),
};
}
#[test]
fn scope_drop() {
let lua = Lua::new();
struct MyUserdata(Rc<()>);
impl UserData for MyUserdata {
fn add_methods(methods: &mut UserDataMethods<Self>) {
methods.add_method("method", |_, _, ()| Ok(()));
}
}
let rc = Rc::new(());
lua.scope(|scope| {
lua.globals()
.set(
"test",
scope.create_userdata(MyUserdata(rc.clone())).unwrap(),
)
.unwrap();
assert_eq!(Rc::strong_count(&rc), 2);
});
assert_eq!(Rc::strong_count(&rc), 1);
match lua.exec::<()>("test:method()", None) {
Err(Error::CallbackError { .. }) => {}
r => panic!("improper return for destructed userdata: {:?}", r),
};
}
#[test]
fn scope_capture() {
let lua = Lua::new();
let mut i = 0;
lua.scope(|scope| {
scope
.create_function_mut(|_, ()| {
i = 42;
Ok(())
})
.unwrap()
.call::<_, ()>(())
.unwrap();
});
assert_eq!(i, 42);
}
#[test]
fn outer_lua_access() {
let lua = Lua::new();
let table = lua.create_table().unwrap();
lua.scope(|scope| {
scope
.create_function_mut(|_, ()| {
table.set("a", "b").unwrap();
Ok(())
})
.unwrap()
.call::<_, ()>(())
.unwrap();
});
assert_eq!(table.get::<_, String>("a").unwrap(), "b");
}
#[test]
fn too_many_returns() {
let lua = Lua::new();

100
src/tests/scope.rs Normal file
View File

@ -0,0 +1,100 @@
use std::cell::Cell;
use std::rc::Rc;
use {Error, Function, Lua, String, UserData, UserDataMethods};
#[test]
fn scope_func() {
let lua = Lua::new();
let rc = Rc::new(Cell::new(0));
lua.scope(|scope| {
let r = rc.clone();
let f = scope
.create_function(move |_, ()| {
r.set(42);
Ok(())
})
.unwrap();
lua.globals().set("bad", f.clone()).unwrap();
f.call::<_, ()>(()).unwrap();
assert_eq!(Rc::strong_count(&rc), 2);
});
assert_eq!(rc.get(), 42);
assert_eq!(Rc::strong_count(&rc), 1);
match lua
.globals()
.get::<_, Function>("bad")
.unwrap()
.call::<_, ()>(())
{
Err(Error::CallbackError { .. }) => {}
r => panic!("improper return for destructed function: {:?}", r),
};
}
#[test]
fn scope_drop() {
let lua = Lua::new();
struct MyUserdata(Rc<()>);
impl UserData for MyUserdata {
fn add_methods(methods: &mut UserDataMethods<Self>) {
methods.add_method("method", |_, _, ()| Ok(()));
}
}
let rc = Rc::new(());
lua.scope(|scope| {
lua.globals()
.set(
"test",
scope.create_userdata(MyUserdata(rc.clone())).unwrap(),
)
.unwrap();
assert_eq!(Rc::strong_count(&rc), 2);
});
assert_eq!(Rc::strong_count(&rc), 1);
match lua.exec::<()>("test:method()", None) {
Err(Error::CallbackError { .. }) => {}
r => panic!("improper return for destructed userdata: {:?}", r),
};
}
#[test]
fn scope_capture() {
let lua = Lua::new();
let mut i = 0;
lua.scope(|scope| {
scope
.create_function_mut(|_, ()| {
i = 42;
Ok(())
})
.unwrap()
.call::<_, ()>(())
.unwrap();
});
assert_eq!(i, 42);
}
#[test]
fn outer_lua_access() {
let lua = Lua::new();
let table = lua.create_table().unwrap();
lua.scope(|scope| {
scope
.create_function_mut(|_, ()| {
table.set("a", "b").unwrap();
Ok(())
})
.unwrap()
.call::<_, ()>(())
.unwrap();
});
assert_eq!(table.get::<_, String>("a").unwrap(), "b");
}

View File

@ -7,5 +7,6 @@ use rlua::*;
fn main() {
let lua = Lua::new();
let _ = catch_unwind(|| lua.create_table().unwrap());
//~^ error: the trait bound `std::cell::UnsafeCell<()>: std::panic::RefUnwindSafe` is not satisfied in `rlua::Lua`
//~^ error: the type `std::cell::UnsafeCell<()>` may contain interior mutability and a reference
// may not be safely transferrable across a catch_unwind boundary
}

View File

@ -8,5 +8,6 @@ fn main() {
let lua = Lua::new();
let table = lua.create_table().unwrap();
let _ = catch_unwind(move || table.set("a", "b").unwrap());
//~^ error: the trait bound `std::cell::UnsafeCell<()>: std::panic::RefUnwindSafe` is not satisfied in `rlua::Lua`
//~^ error: the type `std::cell::UnsafeCell<()>` may contain interior mutability and a reference
// may not be safely transferrable across a catch_unwind boundary
}

View File

@ -0,0 +1,22 @@
extern crate rlua;
use rlua::*;
fn main() {
struct Test {
field: i32,
}
let lua = Lua::new();
let mut outer: Option<Table> = None;
lua.scope(|scope| {
let f = scope
.create_function_mut(|_, t: Table| {
outer = Some(t);
//~^ error: `*outer` does not live long enough
Ok(())
})
.unwrap();
f.call::<_, ()>(lua.create_table()).unwrap();
});
}

View File

@ -0,0 +1,22 @@
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(|_, t: Table| {
inner = Some(t);
//~^ error: `inner` does not live long enough
Ok(())
})
.unwrap();
f.call::<_, ()>(lua.create_table()).unwrap();
});
}