diff options
author | Alex Orlenko <zxteam@protonmail.com> | 2023-02-22 20:15:40 +0000 |
---|---|---|
committer | Alex Orlenko <zxteam@protonmail.com> | 2023-02-22 20:15:40 +0000 |
commit | 949906f9f7b8e68ac898ad64c8102b3469b59d12 (patch) | |
tree | 19291b931d4c8a8e6fd5386da0857bb59c0bc8ef | |
parent | 94f01e597c33d6d3c0a19d673c3d4b55b2553e9f (diff) | |
download | mlua-949906f9f7b8e68ac898ad64c8102b3469b59d12.zip |
Fix potential deadlock when trying to reuse dropped RegistryKey.
If no free registry id found, we call protect_lua! macro while keeping mutex guard to the unref list.
Protected calls can trigger garbage collection and if RegistryKey is placed in userdata being collected, this can lead to deadlock.
The solution is drop mutex guard as soon as possible.
Also this commit includes optimization in creating reference in Lua registry.
-rw-r--r-- | src/lua.rs | 17 |
1 files changed, 11 insertions, 6 deletions
@@ -2065,22 +2065,27 @@ impl Lua { let _sg = StackGuard::new(state); check_stack(state, 4)?; - let unref_list = (*self.extra.get()).registry_unref_list.clone(); self.push_value(t)?; // Try to reuse previously allocated slot - let unref_list2 = unref_list.clone(); - let mut unref_list2 = mlua_expect!(unref_list2.lock(), "unref list poisoned"); - if let Some(registry_id) = unref_list2.as_mut().and_then(|x| x.pop()) { + let unref_list = (*self.extra.get()).registry_unref_list.clone(); + let free_registry_id = mlua_expect!(unref_list.lock(), "unref list poisoned") + .as_mut() + .and_then(|x| x.pop()); + if let Some(registry_id) = free_registry_id { // It must be safe to replace the value without triggering memory error ffi::lua_rawseti(state, ffi::LUA_REGISTRYINDEX, registry_id as Integer); return Ok(RegistryKey::new(registry_id, unref_list)); } // Allocate a new RegistryKey - let registry_id = protect_lua!(state, 1, 0, |state| { + let registry_id = if self.unlikely_memory_error() { ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX) - })?; + } else { + protect_lua!(state, 1, 0, |state| { + ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX) + })? + }; Ok(RegistryKey::new(registry_id, unref_list)) } } |