Fix Lua assertion when inspecting another thread stack.
The thread can be dead and it's not safe to call __tostring metamethod (if present) on error object. Fixes #195
This commit is contained in:
parent
5330b900fd
commit
0cd724f63b
|
@ -4,7 +4,7 @@ use std::os::raw::c_int;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
use crate::types::LuaRef;
|
use crate::types::LuaRef;
|
||||||
use crate::util::{check_stack, error_traceback, pop_error, StackGuard};
|
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
|
||||||
use crate::value::{FromLuaMulti, ToLuaMulti};
|
use crate::value::{FromLuaMulti, ToLuaMulti};
|
||||||
|
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
|
@ -136,8 +136,12 @@ impl<'lua> Thread<'lua> {
|
||||||
|
|
||||||
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
|
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
|
||||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||||
protect_lua!(lua.state, 0, 0, |_| error_traceback(thread_state))?;
|
check_stack(lua.state, 3)?;
|
||||||
return Err(pop_error(thread_state, ret));
|
protect_lua!(lua.state, 0, 1, |state| error_traceback_thread(
|
||||||
|
state,
|
||||||
|
thread_state
|
||||||
|
))?;
|
||||||
|
return Err(pop_error(lua.state, ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut results = args; // Reuse MultiValue container
|
let mut results = args; // Reuse MultiValue container
|
||||||
|
|
14
src/util.rs
14
src/util.rs
|
@ -696,6 +696,20 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A variant of `error_traceback` that can safely inspect another (yielded) thread stack
|
||||||
|
pub unsafe fn error_traceback_thread(state: *mut ffi::lua_State, thread: *mut ffi::lua_State) {
|
||||||
|
// Move error object to the main thread to safely call `__tostring` metamethod if present
|
||||||
|
ffi::lua_xmove(thread, state, 1);
|
||||||
|
|
||||||
|
if get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
|
||||||
|
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||||
|
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||||
|
ffi::luaL_traceback(state, thread, s, 0);
|
||||||
|
ffi::lua_remove(state, -2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A variant of `pcall` that does not allow Lua to catch Rust panics from `callback_error`.
|
// A variant of `pcall` that does not allow Lua to catch Rust panics from `callback_error`.
|
||||||
pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||||
|
|
|
@ -426,6 +426,30 @@ async fn test_async_userdata() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_thread_error() -> Result<()> {
|
||||||
|
struct MyUserData;
|
||||||
|
|
||||||
|
impl UserData for MyUserData {
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_meta_method("__tostring", |_, _this, ()| Ok("myuserdata error"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
let result = lua
|
||||||
|
.load("function x(...) error(...) end x(...)")
|
||||||
|
.set_name("chunk")?
|
||||||
|
.call_async::<_, ()>(MyUserData)
|
||||||
|
.await;
|
||||||
|
assert!(
|
||||||
|
matches!(result, Err(Error::RuntimeError(cause)) if cause.contains("myuserdata error")),
|
||||||
|
"improper error traceback from dead thread"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_async_scope() -> Result<()> {
|
async fn test_async_scope() -> Result<()> {
|
||||||
let ref lua = Lua::new();
|
let ref lua = Lua::new();
|
||||||
|
|
Loading…
Reference in a new issue