Comments (7)
This shouldn't compile, this is 100% a soundness issue, and I have a fix incoming... I think. I need to do a lot more testing to make sure this fix is right.
As soon as I fix the soundness issue, I'll help you through what you're trying to do, because like I said this SHOULDN'T compile, but it does because of a really bad issue :(
This is a GREAT find, thank you so much for finding this :(
from rlua.
I think a2615a8 fixes this issue, in that the example program you gave no longer compiles and there is now a compiletest_rs test for this specific issue.
The issue allowed parameters passed to callbacks created from Lua::scope
to be stored outside the callback, which should never be allowed. Handles that have a 'lua lifetime are not supposed to be stored "long term" at all, really, since they reference whatever specific instance of Lua
that they come from and can't outlive it. The Lua
instance passed to callbacks is ephemeral, it lives only as long as the callback, so when you convince a handle value to escape the callback, it's referencing a dropped Lua
, which is what caused this crash.
This was only a problem inside Lua::scope
, because normally functions must be 'static anyway so the borrow checker wouldn't allow sneaking a non-'static reference out. The fix is to add a lifetime bound in Scope::create_function
/ Scope::create_function_mut
to make sure that the 'scope outlives 'callback. It's a bit subtle, but since 'scope must outlive 'callback, and the callback itself must outlive 'scope, this means that the callback can't exfiltrate anything with the 'callback lifetime at all, which is what we want.
This fix probably doesn't help your actual problem, since it just makes this example not compile. The good news is you might not need Lua::scope
at all to do what you want to do, that's really only for when you have non-'static types other than Lua handles to deal with. If you simply want to be able to store Lua handles long term, try using the Lua registry with Lua::create_registry_value
.
from rlua.
Thanks so much for the explanation!
I guess I thought that Rust references to Lua values could be carried around as long as the top-level Lua
instance is alive (since they are refcounted), but I'm not familiar enough with the low-level Lua API to see exactly how this would work.
The Lua::scope
was intended to limit the mutability of the program
struct, so I think it still makes sense. Instead of storing LuaFunction
in the Program
struct, I'll try storing a LuaRegistryKey
and also the &Lua
top-level instance to delete from the registry on drop. Something like this:
struct StoredFunction<'a>(&'a Lua, Option<LuaRegistryKey>);
impl<'a> StoredFunction<'a> {
pub fn new(lua :&'a Lua, f :LuaFunction) -> LuaResult<StoredFunction<'a>> {
Ok(StoredFunction(lua, Some(lua.create_registry_value(f)?)))
}
pub fn call<A: rlua::ToLuaMulti<'a>, R: rlua::FromLuaMulti<'a>>(&self, args: A) -> LuaResult<R> {
self.0.registry_value::<LuaFunction>(self.1.as_ref().expect("Stored function error"))
.and_then(|x| x.call(args))
}
}
impl<'a> Drop for StoredFunction<'a> {
fn drop(&mut self) {
self.0.remove_registry_value(self.1.take().unwrap()).unwrap();
}
}
from rlua.
I guess I thought that Rust references to Lua values could be carried around as long as the top-level Lua instance is alive (since they are refcounted), but I'm not familiar enough with the low-level Lua API to see exactly how this would work.
There are actually multiple Lua
instances, when a callback is called by Lua and given lua_State
pointer, a new "ephemeral" Lua
instance is created. The fact that all the Lua
instances are potentially different and the specific 'lua lifetime used only ends up mattering in Scope
methods, because like I said normally callbacks must be 'static anyway.
If you're okay with storing a a &Lua
in a type, you can actually just store normal reference types, it's just generally a huge pain to deal with persistently borrowing &Lua
, and you tend to run head-first into self borrows, which are even more of a pain to deal with. The reason that RegistryKey
exists really is to save you from having to store a &Lua
somewhere in the first place. If you wanted a way to get handles out via a "register" callback with, one way is to create a table in Rust, then make a closure that closes over that table and pass that into Lua as the "register" function, and have that closure populate the table. You can also turn the API around to have Lua just return a table of functions, or use an agreed upon global variable to store them.
I know the handle based API has some limitations and sometimes you could envision the API without those limitations, but generally this is because handles are just not designed to be stored inside Lua itself, so anything involving capturing them in callbacks or getting them out of callbacks is probably going to be painful.
from rlua.
In the meantime, I have found another soundness bug along the same lines with a closure capturing its own arguments into an owned value rather than a captured reference, and have had to attempt another fix (1a9c50f). If anybody is confident in their lifetime knowledge and wants to test this out and try to break it, I would be very appreciative, because I'm not very confident there isn't another issue here. I at least have coverage of all the issues with compiletest_rs, but I would really appreciate another set of eyes here.
from rlua.
I went ahead and released 0.14.2 as well with the second fix, hopefully I'm not missing any other safety issues here.
from rlua.
Great, thanks again for your help.
Just for information, I went ahead with storing the functions in a global table on the Lua side, and then creating a Rc<RefCell<Option<Context>>>
to use as a parameter for the functions, to be able to remove the Context
on the Rust side again after executing the Lua functions from the global table.
from rlua.
Related Issues (20)
- Possible bug with __newindex not called on numeric indexing HOT 4
- Question: How to exec multiple things at once? HOT 4
- Segfault on clone of #[repr(C)] userdata subfield HOT 9
- ios build error HOT 7
- examples for error handling HOT 2
- Example for eval return a struct? HOT 1
- Cannot load DLLs in v0.19 HOT 6
- Missing mutable borrows for Table<'lua> HOT 2
- Context.create_sequence_from should support references HOT 1
- Undefined symbol lua_checkstack HOT 8
- Crash on empty source HOT 2
- custom runtime path HOT 3
- creating a `require`-able module HOT 8
- [Request] UserData::from_lua HOT 1
- Suggestion: proc_macro for LuaUserDataMethods::add_method HOT 3
- No documentation on how to use LuaRocks with rlua HOT 3
- Rlua + Luarocks Modules -> Fail to load HOT 3
- Memory leak when using system-luajit HOT 2
- Merge with mlua HOT 32
- [Typo] Example says laoded(instead of loaded) HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from rlua.